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 |