Subversion Repositories wimsdev

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3653 schaersvoo 1
/*                                                                                                            
2
Copyright 2004-2006 David P. Little                                                                            
3
license:                                                                                                      
4
Unless otherwise stated, the above applets were written by David Little.                                      
5
They may be used without permission from the author for home and/or educational (non-profit) purposes only.    
6
Any other use must be approved by the author.                                                                  
7
 
8
Modified for wims interactive usage with permission of the author.                                            
9
 
10
J.M. Evers                                                                                                    
11
*/
12
import java.util.*;
13
import java.awt.*;
14
import java.awt.event.*;
15
import java.awt.image.*;
16
import java.awt.geom.*;
17
import javax.swing.*;
18
 
19
public class PlinkoBoard extends JPanel implements Runnable, KeyListener, MouseListener, MouseMotionListener{
20
 
21
        double COUNT;                                   // number of balls that have been dropped
22
        double TOTAL;                                   // total of bins
23
        double TOTAL_SQUARES;                   // total of bins
24
 
25
        long[] HIST;                                    // number of balls in each bin;
26
        float MAX;                                              // maximum number of balls in a single bin
27
        double[][][] PINS;                                      // coordinates of pins (including one at the base of each bin)
28
        double DIST;                                    // vertical distance between pins
29
        double BALL_RAD;                                // radius of ball
30
        int PIN_RAD;                                    // radius of pin
31
        int BINS;                                               // total number of bins
32
        int W;                                                  // width of board
33
        int H;                                                  // height of board
34
        PlinkoBall FIRST_BALL;                  // represents beginning of doubly linked list of plinko balls
35
        int BALL_COUNT;
36
        int BOTTOM_MARGIN = 5;
37
 
38
        // used in calculating confidence intervals
39
        int LEFT;
40
        int RIGHT;
41
        double PERCENT;
42
        int CURRENT_BIN = 0;
43
        int showstats=0;
44
        static double[][] DYS;
45
        // jm.evers: defining a few things
46
        int maximum_balls;String start_number="0";int incr=1;
47
 
48
        static Color[] COLORS = { Color.red, Color.magenta, Color.orange, Color.yellow, Color.green, Color.blue, Color.cyan };
49
        /*  new Color(.45f,.40f,.86f), new Color(.36f,.53f,.95f),
50
            new Color(.38f,.84f,.67f), new Color(.37f,.74f,.44f), new Color(.49f,.51f,.36f), new Color(.90f,.90f,.35f),
51
            new Color(.99f,.75f,.34f), new Color(.85f,.27f,.42f), new Color(.73f,.34f,.76f), new Color(.51f,.33f,.82f),
52
            new Color(.41f,.46f,.91f), new Color(.36f,.73f,.79f), new Color(.38f,.79f,.56f), new Color(.44f,.60f,.37f),
53
            new Color(.70f,.71f,.35f), new Color(.99f,.91f,.34f), new Color(.99f,.58f,.34f), new Color(.94f,.38f,.34f),
54
            new Color(.86f,.33f,.68f), new Color(.35f,.09f,.73f), new Color(.09f,.13f,.80f), new Color(.09f,.38f,.35f),
55
            new Color(.18f,.55f,.13f), new Color(.55f,.74f,.11f), new Color(.99f,.92f,.09f), new Color(.99f,.69f,.09f),
56
            new Color(.99f,.46f,.09f), new Color(.96f,.25f,.11f), new Color(.93f,.09f,.12f), new Color(.42f,.09f,.69f),
57
            new Color(.27f,.09f,.78f), new Color(.09f,.29f,.51f), new Color(.09f,.46f,.20f), new Color(.36f,.64f,.12f),
58
            new Color(.73f,.83f,.10f), new Color(.99f,.80f,.09f), new Color(.99f,.57f,.09f), new Color(.98f,.31f,.10f),
59
            new Color(.94f,.12f,.11f), new Color(.16f,.10f,.06f), new Color(.36f,.24f,.14f), new Color(.55f,.38f,.21f),
60
            new Color(.75f,.52f,.29f), new Color(.80f,.78f,.76f), new Color(.55f,.50f,.45f),  new Color(.24f,.22f,.20f),
61
            new Color(.76f,.76f,.80f), new Color(.45f,.45f,.55f), new Color(.20f,.20f,.24f)};*/
62
 
63
        //BufferedImage[] IMAGES;
64
    Image[] IMAGES;
65
    Thread thread;
66
    boolean active;    
67
    Image background;
68
    Image image;
69
    Graphics2D graphics;
70
    Plinko plinko;
71
 
72
    public PlinkoBoard( Plinko plinko ){
73
        super();
74
        this.plinko = plinko;
75
        setup();
76
        newHist();
77
 
78
        FIRST_BALL = null;
79
        BALL_COUNT = 0;
80
        active = false;
81
 
82
        DYS = new double[12][];
83
 
84
        for ( double i=0.0; i<12.0; i++ ){
85
            DYS[(int)i] = new double[ (int)i ];
86
            for ( double j=0.0; j<i; j++ ){
87
                DYS[(int)i][(int)j] = PlinkoBall.A*j*j/(i*i) + PlinkoBall.B*j/i;
88
            }
89
        }
90
 
91
        addKeyListener( this );
92
        addMouseListener( this );
93
        // jm.evers: if the applet is in an wims exercise...read appletparam and start buckets_number with 1 instead of 0
94
        maximum_balls=(int)plinko.total_balls - 1;
95
        if(plinko.wims_exercise == false){ showstats = 1;}else{ start_number="1"; incr=2; }
96
    }
97
 
98
    public void setup(){
99
                W = getWidth();
100
                H = getHeight();
101
 
102
                BINS = ((Integer)plinko.bins.getValue()).intValue();
103
                CURRENT_BIN = BINS/2;
104
                LEFT = BINS+1;
105
                RIGHT = BINS;
106
                PERCENT = 0;
107
 
108
                if ( H-100-BOTTOM_MARGIN<W/2 ){
109
                        DIST = (double)(H-100-BOTTOM_MARGIN)/BINS;
110
                } else {
111
                        DIST = (double)(W-10)/(2*BINS);
112
                }
113
 
114
                PIN_RAD = (int)DIST/9 + 1;
115
                BALL_RAD = Math.max(2*DIST/7,2.0)+1;
116
 
117
                // create images of colored balls
118
                IMAGES = new Image[ COLORS.length ];
119
                Graphics2D g;
120
                int red;
121
                int green;
122
                int blue;
123
                for ( int i=0; i<COLORS.length; i++ ){
124
                        IMAGES[i] = getBall( BALL_RAD, COLORS[i] );
125
                        /*
126
                        IMAGES[i] = new BufferedImage( (int)(2*BALL_RAD+2), (int)(2*BALL_RAD+2), BufferedImage.TYPE_INT_ARGB);
127
                        g = (Graphics2D)(IMAGES[i].getGraphics());
128
                        g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
129
                        g.setBackground( new Color(0,0,0,255) );
130
 
131
                        red = COLORS[i].getRed();
132
                        green = COLORS[i].getGreen();
133
                        blue = COLORS[i].getBlue();
134
 
135
                        for ( float k=0; k<1; k=k+(float)(1/BALL_RAD) ){
136
                                g.setColor( new Color(red + (int)(k*k*(255-red)),green + (int)(k*k*(255-green)),blue + (int)(k*k*(255-blue))) );
137
                                g.fill( new Ellipse2D.Double( k*k*BALL_RAD/2, k*k*BALL_RAD/2, 2*(BALL_RAD-k*k*BALL_RAD), 2*(BALL_RAD-k*k*BALL_RAD) ) );
138
                        }
139
                        */
140
                }
141
 
142
 
143
                PINS = new double[BINS][][];
144
                for (int i=0; i<BINS; i++){
145
                        PINS[i] = new double[i+1][2];
146
                        for (int j=0; j<=i; j++){
147
                                //PINS[i][j] = new Point((int)(DIST*(2*j-i)+W/2),(int)(DIST*(i+1)));
148
                                PINS[i][j][0] = DIST*(2*j-i)+W/2;
149
                                PINS[i][j][1] = DIST*(i+1);
150
                        }
151
                }              
152
 
153
                // pins at the base of each bin
154
                for (int i=0; i<BINS; i++){
155
                        //PINS[BINS-1][i] = new Point((int)(DIST*(2*i-BINS+1)+W/2),(int)(H-30-BALL_RAD));
156
                        PINS[BINS-1][i][0] = DIST*(2*i-BINS+1)+W/2;
157
                        PINS[BINS-1][i][1] = H-30-BALL_RAD;
158
                }
159
        }
160
 
161
 
162
        public static Image getBall( double R, Color color ){
163
                BufferedImage image = new BufferedImage( (int)(2*R+2), (int)(2*R+2), BufferedImage.TYPE_INT_ARGB);
164
 
165
                int red;
166
                int grn;
167
                int blu;
168
                double[] n = new double[3];
169
                double[] e = new double[3];
170
                double[] l = {-5,5,10};
171
                double ll = Math.sqrt(150);
172
                double F;
173
                double G;
174
 
175
                for ( int i=0; i<(int)(2*R+2); i++ ){
176
                        for ( int j=0; j<(int)(2*R+2); j++ ){
177
                                if ( (R-i)*(R-i) + (R-j)*(R-j) <= (R-1)*(R-1) ){               
178
                                        // vector normal to sphere
179
                                        n[0] = (i - R)/R;
180
                                        n[1] = (R - j)/R;
181
                                        n[2] = Math.sqrt(1 - n[0]*n[0] - n[1]*n[1]);
182
 
183
                                        F = (n[0]*l[0] + n[1]*l[1] + n[2]*l[2])/Math.sqrt(l[0]*l[0] + l[1]*l[1] + l[2]*l[2]);
184
                                        F = (1+F)/2;
185
                                        G = Math.pow(F,20.0);
186
 
187
                                        red = (int)(color.getRed()*(F-G) + G*255);
188
                                        grn = (int)(color.getGreen()*(F-G) + G*255);
189
                                        blu = (int)(color.getBlue()*(F-G) + G*255);
190
 
191
                                        image.setRGB(i,j, (255<<24) + (red<<16) + (grn<<8) + blu );
192
                                } else if ( (R-i)*(R-i) + (R-j)*(R-j) <= (R+1)*(R+1) ){
193
                                        red = 0;
194
                                        grn = 0;
195
                                        blu = 0;
196
 
197
                                        for ( double di = -0.33; di<0.5; di+=0.33 ){
198
                                                for ( double dj = -0.33; dj<0.5; dj+=0.33 ){
199
 
200
                                                        if ( (R-(i+di))*(R-(i+di)) + (R-(j+dj))*(R-(j+dj)) <= R*R ){
201
                                                                n[0] = (i + dj - R)/R;
202
                                                                n[1] = (R - (j+dj))/R;
203
                                                                n[2] = Math.sqrt(1 - n[0]*n[0] - n[1]*n[1]);
204
 
205
                                                                F = (n[0]*l[0] + n[1]*l[1] + n[2]*l[2])/ll;
206
                                                                F = (1+F)/2;
207
                                                                G = Math.pow(F,20.0);
208
                                                        } else {
209
                                                                F = 1;
210
                                                                G = 1;
211
                                                        }
212
                                                        red += (int)(color.getRed()*(F-G) + G*255);
213
                                                        grn += (int)(color.getGreen()*(F-G) + G*255);
214
                                                        blu += (int)(color.getBlue()*(F-G) + G*255);
215
 
216
                                                }
217
                                        }
218
 
219
                                        red /= 9;
220
                                        grn /= 9;
221
                                        blu /= 9;
222
 
223
                                        image.setRGB(i,j, ((255-(red+grn+blu)/3)<<24) + (red<<16) + (grn<<8) + blu );
224
                                }
225
                        }
226
                }
227
                return image;
228
        }
229
 
230
 
231
    public void newHist(){
232
                COUNT = 0;
233
                TOTAL = 0;
234
                TOTAL_SQUARES = 0;
235
                MAX = 0;
236
                PERCENT = 0;
237
 
238
                HIST = new long[ BINS ];
239
                for (int i=0; i<BINS; i++){
240
                        HIST[i] = 0;
241
                }
242
    }
243
 
244
    // jm.evers :preparing a data string for javascript: must be of type string: arrays always give trouble on IE...
245
    public String ReadData(){
246
        String reply="";
247
        for(int i=0;i<HIST.length;i++){
248
            if(i != 0){reply=reply+","+HIST[i];}else{reply=""+HIST[0];}
249
        }
250
        return reply;
251
    }
252
 
253
    public void LimitReached(){
254
            plinko.active = true;
255
            FIRST_BALL=null;
256
            plinko.toggleStart();
257
    }
258
 
259
    public void run(){
260
        while ( BALL_COUNT>0 ){
261
            repaint();
262
            try {Thread.sleep(50);} catch (InterruptedException e){}
263
        }
264
        repaint();
265
        active = false;
266
        plinko.active = false;
267
        plinko.bins.setEnabled( true );
268
    }
269
 
270
 
271
    // under construction
272
    public void kill(){
273
                FIRST_BALL = null;
274
                if ( plinko.start.getText().equals(plinko.start_text) ){
275
                        active = false;
276
                        plinko.active = false;
277
                        plinko.bins.setEnabled( true );
278
                }
279
    }
280
 
281
 
282
    public void dropBall( boolean sound ){
283
        BALL_COUNT++;
284
        // jm.evers : I don't know of another/better way to get the system to stop at a given [param] number of balls...
285
        if(COUNT == maximum_balls ){LimitReached();}
286
        if ( FIRST_BALL == null ){
287
            FIRST_BALL = new PlinkoBall();
288
            FIRST_BALL.sound = sound;
289
        } else {
290
            FIRST_BALL.previousBall = new PlinkoBall();
291
            FIRST_BALL.previousBall.sound = sound;
292
            FIRST_BALL.previousBall.nextBall = FIRST_BALL;
293
            FIRST_BALL = FIRST_BALL.previousBall;
294
        }
295
 
296
        try { Thread.sleep(0);} catch (InterruptedException e){}
297
 
298
        if ( !active ){
299
            active = true;
300
            thread = new Thread(this);
301
            thread.start();
302
        }
303
    }
304
 
305
    public void paintComponent( Graphics g ){
306
                // have a copy of the background on which to draw
307
                W = getWidth();
308
                H = getHeight();
309
 
310
                if ( background == null || background.getWidth(this) != W || background.getHeight(this) != H || image == null || image.getWidth(this) != W || image.getHeight(this) != H ){
311
                        setup();
312
                        background = createImage( W, H );
313
                        drawBackground(W,H);
314
                        image = createImage( W, H );
315
                        graphics = (Graphics2D) image.getGraphics();
316
                        graphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
317
                        graphics.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
318
                }
319
 
320
                graphics.drawImage( background, 0, 0, this );
321
 
322
                // run through all active balls and draw them on the image
323
                PlinkoBall ball = FIRST_BALL;
324
                while ( ball != null ){
325
                        graphics.drawImage( IMAGES[ball.spaz], (int)(ball.X - BALL_RAD),  (int)(ball.Y - 2*BALL_RAD - PIN_RAD + 1), this );
326
                        //if ( ball.t == ball.C && ball.ROW < BINS-2 ) Plinko.click.play();
327
                        //try {
328
                                increment( ball );
329
                        //} catch ( NullPointerException npe ) {
330
                        //}
331
                        ball = ball.nextBall;
332
                }
333
                drawHist( graphics );
334
        //      if(showstats==1){
335
                    drawStats( graphics );
336
        //      }
337
                g.drawImage(image,0,0,this);
338
    }
339
 
340
    // under construction
341
    public void drawNormal(){
342
    }
343
 
344
 
345
    public void increment( PlinkoBall ball ){
346
                // if ball has landed on pin, reset t to 0 and pick a direction
347
                if ( ball.t == ball.C && ball.ROW < BINS-2 ){
348
                        ball.ROW++;
349
                        ball.COL += ball.DIR;
350
                        ball.t = 0;
351
                        ball.DIR = 0;
352
 
353
                        // pick a new direction
354
 
355
                        // jm.evers: reading controls or applet param...plinko.chance is parameter
356
                        if(plinko.wims_exercise == false){
357
                            if ( Math.random() < ((Double)plinko.prob.getValue()).doubleValue() ) ball.DIR = 1;
358
                        }
359
                        else
360
                        {
361
                            if ( Math.random() <  plinko.chance ){ ball.DIR = 1;}
362
                        }
363
 
364
                        // update speed
365
                        ball.C = 11-((Integer)plinko.rate.getValue()).intValue();
366
 
367
                        if ( ball.sound ) Plinko.ping.play();
368
                }
369
 
370
                double dx = (double)(DIST*ball.t*(2*ball.DIR - 1))/(double)(ball.C);
371
 
372
                if ( ball.ROW < 0 ){
373
                        // ball falling onto top pin
374
                        ball.X = PINS[0][0][0];
375
                        dx = Math.abs(dx);
376
                        ball.Y = PINS[0][0][1] - DIST + DIST*ball.t*ball.t/(ball.C*ball.C);
377
                } else if ( ball.ROW < BINS-2 ) {
378
                        ball.X = PINS[ball.ROW][ball.COL][0] + dx;
379
                        dx = Math.abs(dx);
380
                        ball.Y = PINS[ball.ROW][ball.COL][1] - DIST*DYS[ball.C][ball.t];
381
                } else {
382
                        // ball falling into bin
383
                        // dx = DIST*ball.t*(2*ball.DIR - 1)/10.0;
384
                        ball.X = PINS[ball.ROW][ball.COL][0] + dx;
385
                        if ( dx>0 ){
386
                                ball.X = Math.min(ball.X,PINS[ball.ROW][ball.COL][0] + 2*DIST - BALL_RAD );
387
                        } else {
388
                                ball.X = Math.max(ball.X,PINS[ball.ROW][ball.COL][0] - 2*DIST + BALL_RAD + 1);
389
                        }
390
                        dx = Math.abs(dx);
391
                        ball.Y = PINS[ball.ROW][ball.COL][1]  - dx*(ball.A*dx/DIST+ball.B);
392
                }
393
 
394
                ball.t++;
395
 
396
                if ( ball.Y > H - BOTTOM_MARGIN - PIN_RAD ){
397
                        if ( ball.previousBall != null && ball.nextBall != null){
398
                                ball.previousBall.nextBall = ball.nextBall;
399
                                ball.nextBall.previousBall = ball.previousBall;
400
                        } else if ( ball.previousBall != null && ball.nextBall == null ) {
401
                                ball.previousBall.nextBall = null;
402
                        } else if ( ball.previousBall == null && ball.nextBall != null ) {
403
                                ball.nextBall.previousBall = null;
404
                                FIRST_BALL = ball.nextBall;
405
                        } else  {
406
                                FIRST_BALL = null;
407
                                plinko.bins.setEnabled( true );
408
                        }
409
 
410
                        BALL_COUNT--;
411
                        updateHist(ball.COL+ball.DIR);
412
                        if ( ball.sound ) Plinko.click.play();
413
                }
414
    }
415
 
416
 
417
    public void updateHist( int i ){
418
                HIST[i]++;
419
                COUNT++;
420
                TOTAL += i;
421
                TOTAL_SQUARES += i*i;
422
                if ( HIST[i] > MAX) MAX = HIST[i];
423
                if ( i>= LEFT && i <= RIGHT ){
424
                        PERCENT++;
425
                }
426
    }
427
 
428
 
429
    public void drawStats(Graphics g){
430
    //jm.evers : if in a wims_exercise we show a limited amount of statistical data...
431
        if(showstats == 1){
432
            plinko.count.setText(plinko.label_count+ (int)COUNT);
433
            plinko.current_bin.setText(plinko.label_bin + CURRENT_BIN);
434
            plinko.current_bin_count.setText(plinko.label_bin_count + HIST[CURRENT_BIN]);
435
            if(COUNT > 0.0D){
436
                double d = TOTAL / COUNT;
437
                plinko.mean.setText(plinko.label_mean + (float)d);
438
                plinko.variance.setText(plinko.label_variance + (float)(TOTAL_SQUARES / COUNT - d * d));
439
                plinko.current_bin_prob.setText(plinko.label_bin_probability + (float)((double)(int)((double)(0x186a0L * HIST[CURRENT_BIN]) / COUNT) / 1000D) + "%");
440
                if(LEFT < BINS){
441
                    if(LEFT == RIGHT){
442
                        plinko.confidence.setText(plinko.label_confidence + (float)((double)(int)((100000D * PERCENT) / COUNT) / 1000D) + plinko.some_text + LEFT + ".");
443
                    }
444
                    else
445
                    {
446
                        plinko.confidence.setText(plinko.label_confidence + (float)((double)(int)((100000D * PERCENT) / COUNT) / 1000D) + plinko.some_text + LEFT + plinko.through + RIGHT + ".");
447
                    }
448
                }
449
                else
450
                {
451
                    plinko.confidence.setText(plinko.label_confidence );
452
                }
453
            }
454
            else
455
            {
456
                plinko.mean.setText(plinko.label_mean );
457
                plinko.variance.setText(plinko.label_variance);
458
                plinko.current_bin_prob.setText(plinko.label_bin_probability);
459
                plinko.confidence.setText(plinko.label_confidence);
460
            }
461
        }
462
        else
463
        {
464
        // just the total COUNT and "balls per active bin" 
465
            plinko.count.setText(plinko.label_count+ (int)COUNT);
466
            plinko.current_bin.setText(plinko.label_bin + (CURRENT_BIN+1));
467
            plinko.current_bin_count.setText(plinko.label_bin_count + HIST[CURRENT_BIN]);
468
        }
469
    }
470
 
471
 
472
    public void drawHist( Graphics2D g ){
473
                long h;
474
                double x0;
475
                double x1;
476
                String str;
477
                W = getWidth();
478
                H = getHeight();
479
                Rectangle2D rect;
480
 
481
                for (int i=0; i<BINS; i++){
482
                        if ( i==0 ){
483
                                x0 = PINS[BINS-1][0][0]-DIST;
484
                                x1 = PINS[BINS-2][0][0];
485
                        } else if ( i==BINS-1 ){
486
                                x0 = PINS[BINS-2][BINS-2][0];
487
                                x1 = PINS[BINS-1][BINS-1][0]+DIST;
488
                        } else {
489
                                x0 = PINS[BINS-2][i-1][0];
490
                                x1 = PINS[BINS-2][i][0];
491
                        }
492
 
493
                        //Draw bar
494
                        h = HIST[i];
495
                        if (MAX>100) h=(int)(100.0*h/MAX);
496
                        g.setColor( new Color(255,0,0,175) );
497
                        if ( i >= LEFT && i <= RIGHT ) g.setColor( new Color(0,255,0,175) );
498
                        rect = new Rectangle2D.Double(x0,H-h-BOTTOM_MARGIN,x1-x0,h);
499
                        g.fill( rect );
500
                        g.setColor(Color.black);
501
                        g.setStroke( new BasicStroke(1.0f) );
502
                        g.draw( rect );
503
                }
504
 
505
                // draw line under current bin
506
                g.setColor( Color.black );
507
                g.setStroke( new BasicStroke(3.0f) );
508
                g.draw( new Line2D.Double(PINS[BINS-1][CURRENT_BIN][0]-DIST,H-BOTTOM_MARGIN+3,PINS[BINS-1][CURRENT_BIN][0]+DIST,H-BOTTOM_MARGIN+3) );
509
    }
510
 
511
 
512
    public void drawBackground(){
513
                drawBackground( getWidth(), getHeight() );
514
    }
515
 
516
 
517
    public void drawBackground( int W, int H ){
518
                double[] p;
519
 
520
                Graphics2D backgroundgraphics = (Graphics2D) background.getGraphics();
521
                backgroundgraphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
522
                backgroundgraphics.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
523
                backgroundgraphics.setColor(Color.white);
524
                backgroundgraphics.fillRect( 0, 0, W, H );
525
                backgroundgraphics.setColor(Color.black);
526
 
527
                Image img = getBall( PIN_RAD, Color.black );
528
 
529
                // draw pins
530
                for (int i=0; i<BINS-1; i++){
531
                        for (int j=0;j<=i;j++){
532
                        p = PINS[i][j];
533
                        //backgroundgraphics.fill( new Ellipse2D.Double(p[0]-PIN_RAD,p[1]-PIN_RAD,2*PIN_RAD,2*PIN_RAD) );
534
                        backgroundgraphics.drawImage(img, (int)(p[0]-PIN_RAD),(int)(p[1]-PIN_RAD),this );
535
                        }
536
                }
537
 
538
                String s;
539
                backgroundgraphics.setFont( new Font("Helvetica",Font.BOLD,Math.min((int)(4*DIST)/3,50)) );
540
                FontMetrics fm = backgroundgraphics.getFontMetrics();
541
                // draw lines and numbers
542
                p = PINS[BINS-1][0];
543
                backgroundgraphics.draw( new Line2D.Double( p[0]-DIST,H-100-BOTTOM_MARGIN,p[0]-DIST,H-BOTTOM_MARGIN) );
544
                backgroundgraphics.setColor( Color.darkGray );
545
                backgroundgraphics.drawString(start_number,(int)(p[0] - fm.stringWidth("0")/2), H-100-BOTTOM_MARGIN+Math.min((int)(4*DIST)/3, 50) );
546
                for (int i=0; i<BINS-1; i++){
547
                        p = PINS[BINS-2][i];
548
                        s = ""+(i+incr);
549
                        backgroundgraphics.setColor( Color.darkGray );
550
                        backgroundgraphics.drawString(s,(int)(p[0] + DIST - fm.stringWidth(s)/2),H-100-BOTTOM_MARGIN+Math.min((int)(4*DIST)/3,50));
551
                        backgroundgraphics.setColor( Color.black );
552
                        backgroundgraphics.draw( new Line2D.Double( p[0],H-100-BOTTOM_MARGIN,p[0],H-BOTTOM_MARGIN) );
553
                }
554
                p = PINS[BINS-1][BINS-1];
555
                backgroundgraphics.draw( new Line2D.Double(p[0]+DIST,H-100-BOTTOM_MARGIN,p[0]+DIST,H-BOTTOM_MARGIN) );
556
                backgroundgraphics.draw( new Line2D.Double(0,H-BOTTOM_MARGIN,W,H-BOTTOM_MARGIN) );
557
                repaint();
558
    }
559
 
560
 
561
    public void keyTyped( KeyEvent ke ){
562
    }
563
 
564
 
565
    public void keyPressed( KeyEvent ke ){
566
                int code = ke.getKeyCode();
567
                char key = ke.getKeyChar();
568
 
569
                if ( key >= '0' && key <= '4' ){
570
                        int x = key - '0';
571
                        int n = 1;
572
                        for (int i=0; i<x; i++ ){
573
                                n *= 10;
574
                        }/*
575
                        plinko.countdown = n;
576
                        if ( !plinko.active ){
577
                                plinko.start();
578
                                plinko.bins.setEnabled( false );
579
                                plinko.thread = new Thread(plinko);
580
                                plinko.thread.start();
581
                                //start.setText("Stop");
582
                        }*/
583
                        for (int i=0; i<n; i++){
584
                                dropBall( n == 1 );
585
                        }
586
                } else if ( code == 37 ){  // left arrow
587
                        CURRENT_BIN--;
588
                        if ( CURRENT_BIN<0 ) CURRENT_BIN = BINS - 1;
589
                        updatePercent();
590
                        repaint();
591
                } else if ( code == 39 ){  // right arrow
592
                        CURRENT_BIN++;
593
                        if ( CURRENT_BIN>BINS-1 ) CURRENT_BIN = 0;
594
                        updatePercent();
595
                        repaint();
596
                } else if ( code == KeyEvent.VK_ENTER ){ // return or enter
597
                } else if ( key == ' ' ){
598
                        plinko.toggleStart();
599
                } else if (key==20){ //control t - toggle erasing
600
                } else if (key==3){ //control c - clear screen
601
                } else if (key==24){ //control x - kill all threads
602
                }
603
    }
604
 
605
 
606
    public void keyReleased( KeyEvent ke ){}
607
 
608
    public void mouseReleased( MouseEvent me ){}
609
 
610
    public void mouseEntered( MouseEvent me ){}
611
 
612
    public void mouseExited( MouseEvent me ){}
613
 
614
    public void updatePercent(){
615
        double mean = (BINS-1)*((Double)plinko.prob.getValue()).doubleValue();
616
        if ( CURRENT_BIN <= mean ){
617
            LEFT = CURRENT_BIN;
618
            RIGHT = (int)(2*mean) - LEFT;
619
        } else {
620
            RIGHT = CURRENT_BIN;
621
            LEFT = (int)(2*mean) - RIGHT;
622
        }
623
        LEFT = Math.max( LEFT, 0 );
624
        RIGHT = Math.min( RIGHT, BINS-1 );
625
        PERCENT = 0;
626
        for ( int i=LEFT; i<=RIGHT; i++ ){
627
            PERCENT += HIST[i];
628
        }
629
    }
630
 
631
 
632
    public void mouseClicked( MouseEvent me ){
633
        requestFocus();
634
        Point p = me.getPoint();
635
        int bin;
636
        if ( p.x + DIST> PINS[BINS-1][0][0] && p.x-DIST < PINS[BINS-1][BINS-1][0] && p.y> H-100-BOTTOM_MARGIN && p.y<H-BOTTOM_MARGIN){
637
            bin = 0;
638
            while ( bin<BINS-1 ){
639
                if ( p.x + DIST < PINS[BINS-1][bin+1][0] ) break;
640
                bin++;
641
            }
642
            CURRENT_BIN = bin;
643
            updatePercent();
644
            repaint();
645
        }                      
646
    }
647
 
648
 
649
    public void mousePressed( MouseEvent me ){}
650
 
651
    public void mouseDragged( MouseEvent me ){}
652
 
653
    public void mouseMoved( MouseEvent me ){}
654
}
655