Back to index

wims  3.65+svn20090927
PlinkoBoard.java
Go to the documentation of this file.
00001 import java.util.*;
00002 import java.awt.*;
00003 import java.awt.event.*;
00004 import java.awt.image.*;
00005 import java.awt.geom.*;
00006 import javax.swing.*;
00007 
00008 public class PlinkoBoard extends JPanel implements Runnable, KeyListener, MouseListener, MouseMotionListener{
00009 
00010        double COUNT;                             // number of balls that have been dropped
00011        double TOTAL;                             // total of bins
00012        double TOTAL_SQUARES;                     // total of bins
00013        
00014        long[] HIST;                              // number of balls in each bin;
00015        float MAX;                                       // maximum number of balls in a single bin
00016        double[][][] PINS;                               // coordinates of pins (including one at the base of each bin)
00017        double DIST;                              // vertical distance between pins
00018        double BALL_RAD;                          // radius of ball
00019        int PIN_RAD;                              // radius of pin
00020        int BINS;                                        // total number of bins
00021        int W;                                           // width of board
00022        int H;                                           // height of board
00023        PlinkoBall FIRST_BALL;                    // represents beginning of doubly linked list of plinko balls
00024        int BALL_COUNT;
00025        int BOTTOM_MARGIN = 5;
00026        
00027        // used in calculating confidence intervals
00028        int LEFT;
00029        int RIGHT;
00030        double PERCENT;
00031        int CURRENT_BIN = 0;
00032        int showstats=0;
00033        static double[][] DYS;
00034        // jm.evers: defining a few things
00035        int maximum_balls;String start_number="0";int incr=1;
00036        
00037        static Color[] COLORS = { Color.red, Color.magenta, Color.orange, Color.yellow, Color.green, Color.blue, Color.cyan };
00038        /*  new Color(.45f,.40f,.86f), new Color(.36f,.53f,.95f),
00039            new Color(.38f,.84f,.67f), new Color(.37f,.74f,.44f), new Color(.49f,.51f,.36f), new Color(.90f,.90f,.35f),
00040            new Color(.99f,.75f,.34f), new Color(.85f,.27f,.42f), new Color(.73f,.34f,.76f), new Color(.51f,.33f,.82f),
00041            new Color(.41f,.46f,.91f), new Color(.36f,.73f,.79f), new Color(.38f,.79f,.56f), new Color(.44f,.60f,.37f),
00042            new Color(.70f,.71f,.35f), new Color(.99f,.91f,.34f), new Color(.99f,.58f,.34f), new Color(.94f,.38f,.34f), 
00043            new Color(.86f,.33f,.68f), new Color(.35f,.09f,.73f), new Color(.09f,.13f,.80f), new Color(.09f,.38f,.35f), 
00044            new Color(.18f,.55f,.13f), new Color(.55f,.74f,.11f), new Color(.99f,.92f,.09f), new Color(.99f,.69f,.09f), 
00045            new Color(.99f,.46f,.09f), new Color(.96f,.25f,.11f), new Color(.93f,.09f,.12f), new Color(.42f,.09f,.69f), 
00046            new Color(.27f,.09f,.78f), new Color(.09f,.29f,.51f), new Color(.09f,.46f,.20f), new Color(.36f,.64f,.12f), 
00047            new Color(.73f,.83f,.10f), new Color(.99f,.80f,.09f), new Color(.99f,.57f,.09f), new Color(.98f,.31f,.10f), 
00048            new Color(.94f,.12f,.11f), new Color(.16f,.10f,.06f), new Color(.36f,.24f,.14f), new Color(.55f,.38f,.21f), 
00049            new Color(.75f,.52f,.29f), new Color(.80f,.78f,.76f), new Color(.55f,.50f,.45f),  new Color(.24f,.22f,.20f), 
00050            new Color(.76f,.76f,.80f), new Color(.45f,.45f,.55f), new Color(.20f,.20f,.24f)};*/
00051        
00052        //BufferedImage[] IMAGES;
00053     Image[] IMAGES;
00054     Thread thread;
00055     boolean active;  
00056     Image background;
00057     Image image;
00058     Graphics2D graphics;
00059     Plinko plinko;
00060     
00061     public PlinkoBoard( Plinko plinko ){
00062        super();
00063        this.plinko = plinko;
00064        setup();
00065        newHist();
00066 
00067        FIRST_BALL = null;
00068        BALL_COUNT = 0;
00069        active = false;
00070 
00071        DYS = new double[12][];
00072 
00073        for ( double i=0.0; i<12.0; i++ ){
00074            DYS[(int)i] = new double[ (int)i ];
00075            for ( double j=0.0; j<i; j++ ){
00076               DYS[(int)i][(int)j] = PlinkoBall.A*j*j/(i*i) + PlinkoBall.B*j/i;
00077            }
00078        }
00079 
00080        addKeyListener( this );
00081        addMouseListener( this );
00082        // jm.evers: if the applet is in an wims exercise...read appletparam and start buckets_number with 1 instead of 0
00083        maximum_balls=(int)plinko.total_balls - 1;
00084        if(plinko.wims_exercise == false){ showstats = 1;}else{ start_number="1"; incr=2; }
00085     }
00086 
00087     public void setup(){
00088               W = getWidth();
00089               H = getHeight();
00090 
00091               BINS = ((Integer)plinko.bins.getValue()).intValue();
00092               CURRENT_BIN = BINS/2;
00093               LEFT = BINS+1;
00094               RIGHT = BINS;
00095               PERCENT = 0;
00096 
00097               if ( H-100-BOTTOM_MARGIN<W/2 ){
00098                      DIST = (double)(H-100-BOTTOM_MARGIN)/BINS;
00099               } else {
00100                      DIST = (double)(W-10)/(2*BINS);
00101               }
00102 
00103               PIN_RAD = (int)DIST/9 + 1;
00104               BALL_RAD = Math.max(2*DIST/7,2.0)+1;
00105               
00106               // create images of colored balls
00107               IMAGES = new Image[ COLORS.length ];
00108               Graphics2D g;
00109               int red;
00110               int green;
00111               int blue;
00112               for ( int i=0; i<COLORS.length; i++ ){
00113                      IMAGES[i] = getBall( BALL_RAD, COLORS[i] );
00114                      /*
00115                      IMAGES[i] = new BufferedImage( (int)(2*BALL_RAD+2), (int)(2*BALL_RAD+2), BufferedImage.TYPE_INT_ARGB);
00116                      g = (Graphics2D)(IMAGES[i].getGraphics());
00117                      g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
00118                      g.setBackground( new Color(0,0,0,255) );
00119 
00120                      red = COLORS[i].getRed();
00121                      green = COLORS[i].getGreen();
00122                      blue = COLORS[i].getBlue();
00123                      
00124                      for ( float k=0; k<1; k=k+(float)(1/BALL_RAD) ){
00125                             g.setColor( new Color(red + (int)(k*k*(255-red)),green + (int)(k*k*(255-green)),blue + (int)(k*k*(255-blue))) );
00126                             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) ) );
00127                      }
00128                      */
00129               }
00130               
00131 
00132               PINS = new double[BINS][][];
00133               for (int i=0; i<BINS; i++){
00134                      PINS[i] = new double[i+1][2];
00135                      for (int j=0; j<=i; j++){
00136                             //PINS[i][j] = new Point((int)(DIST*(2*j-i)+W/2),(int)(DIST*(i+1)));
00137                             PINS[i][j][0] = DIST*(2*j-i)+W/2;
00138                             PINS[i][j][1] = DIST*(i+1);
00139                      }
00140               }             
00141               
00142               // pins at the base of each bin
00143               for (int i=0; i<BINS; i++){
00144                      //PINS[BINS-1][i] = new Point((int)(DIST*(2*i-BINS+1)+W/2),(int)(H-30-BALL_RAD));
00145                      PINS[BINS-1][i][0] = DIST*(2*i-BINS+1)+W/2;
00146                      PINS[BINS-1][i][1] = H-30-BALL_RAD;
00147               }
00148        }
00149        
00150        
00151        public static Image getBall( double R, Color color ){
00152               BufferedImage image = new BufferedImage( (int)(2*R+2), (int)(2*R+2), BufferedImage.TYPE_INT_ARGB);
00153                                           
00154               int red;
00155               int grn;
00156               int blu;
00157               double[] n = new double[3];
00158               double[] e = new double[3];
00159               double[] l = {-5,5,10};
00160               double ll = Math.sqrt(150);
00161               double F;
00162               double G;
00163 
00164               for ( int i=0; i<(int)(2*R+2); i++ ){
00165                      for ( int j=0; j<(int)(2*R+2); j++ ){
00166                             if ( (R-i)*(R-i) + (R-j)*(R-j) <= (R-1)*(R-1) ){        
00167                                    // vector normal to sphere
00168                                    n[0] = (i - R)/R;
00169                                    n[1] = (R - j)/R;
00170                                    n[2] = Math.sqrt(1 - n[0]*n[0] - n[1]*n[1]);
00171                                    
00172                                    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]);
00173                                    F = (1+F)/2;
00174                                    G = Math.pow(F,20.0);
00175 
00176                                    red = (int)(color.getRed()*(F-G) + G*255);
00177                                    grn = (int)(color.getGreen()*(F-G) + G*255);
00178                                    blu = (int)(color.getBlue()*(F-G) + G*255);
00179 
00180                                    image.setRGB(i,j, (255<<24) + (red<<16) + (grn<<8) + blu );
00181                             } else if ( (R-i)*(R-i) + (R-j)*(R-j) <= (R+1)*(R+1) ){
00182                                    red = 0;
00183                                    grn = 0;
00184                                    blu = 0;
00185                                    
00186                                    for ( double di = -0.33; di<0.5; di+=0.33 ){
00187                                           for ( double dj = -0.33; dj<0.5; dj+=0.33 ){
00188               
00189                                                  if ( (R-(i+di))*(R-(i+di)) + (R-(j+dj))*(R-(j+dj)) <= R*R ){
00190                                                         n[0] = (i + dj - R)/R;
00191                                                         n[1] = (R - (j+dj))/R;
00192                                                         n[2] = Math.sqrt(1 - n[0]*n[0] - n[1]*n[1]);
00193 
00194                                                         F = (n[0]*l[0] + n[1]*l[1] + n[2]*l[2])/ll;
00195                                                         F = (1+F)/2;
00196                                                         G = Math.pow(F,20.0);
00197                                                  } else {
00198                                                         F = 1;
00199                                                         G = 1;
00200                                                  }
00201                                                  red += (int)(color.getRed()*(F-G) + G*255);
00202                                                  grn += (int)(color.getGreen()*(F-G) + G*255);
00203                                                  blu += (int)(color.getBlue()*(F-G) + G*255);
00204 
00205                                           }
00206                                    }
00207        
00208                                    red /= 9;
00209                                    grn /= 9;
00210                                    blu /= 9;
00211                                    
00212                                    image.setRGB(i,j, ((255-(red+grn+blu)/3)<<24) + (red<<16) + (grn<<8) + blu );
00213                             }
00214                      }
00215               }
00216               return image;
00217        }
00218        
00219        
00220     public void newHist(){
00221               COUNT = 0;
00222               TOTAL = 0;
00223               TOTAL_SQUARES = 0;
00224               MAX = 0;
00225               PERCENT = 0;
00226 
00227               HIST = new long[ BINS ];
00228               for (int i=0; i<BINS; i++){
00229                      HIST[i] = 0;
00230               }
00231     }
00232 
00233     // jm.evers :preparing a data string for javascript: must be of type string: arrays always give trouble on IE...
00234     public String ReadData(){
00235        String reply="";
00236        for(int i=0;i<HIST.length;i++){
00237            if(i != 0){reply=reply+","+HIST[i];}else{reply=""+HIST[0];}
00238        }
00239        return reply;
00240     }
00241     
00242     public void LimitReached(){
00243            plinko.active = true;
00244            FIRST_BALL=null;
00245            plinko.toggleStart();
00246     }
00247 
00248     public void run(){
00249        while ( BALL_COUNT>0 ){
00250            repaint();
00251            try {Thread.sleep(50);} catch (InterruptedException e){}
00252        }
00253        repaint();
00254        active = false;
00255        plinko.active = false;
00256        plinko.bins.setEnabled( true );
00257     }
00258 
00259 
00260     // under construction
00261     public void kill(){
00262               FIRST_BALL = null;
00263               if ( plinko.start.getText().equals(plinko.start_text) ){
00264                      active = false; 
00265                      plinko.active = false;
00266                      plinko.bins.setEnabled( true );
00267               }
00268     }
00269 
00270 
00271     public void dropBall( boolean sound ){
00272        BALL_COUNT++;
00273        // jm.evers : I don't know of another/better way to get the system to stop at a given [param] number of balls...
00274        if(COUNT == maximum_balls ){LimitReached();}
00275        if ( FIRST_BALL == null ){
00276            FIRST_BALL = new PlinkoBall();
00277            FIRST_BALL.sound = sound;
00278        } else {
00279            FIRST_BALL.previousBall = new PlinkoBall();
00280            FIRST_BALL.previousBall.sound = sound;
00281            FIRST_BALL.previousBall.nextBall = FIRST_BALL;
00282            FIRST_BALL = FIRST_BALL.previousBall;
00283        }
00284 
00285        try { Thread.sleep(0);} catch (InterruptedException e){}
00286            
00287        if ( !active ){
00288            active = true;
00289            thread = new Thread(this);
00290            thread.start();
00291        }
00292     }
00293 
00294     public void paintComponent( Graphics g ){
00295               // have a copy of the background on which to draw
00296               W = getWidth();
00297               H = getHeight();
00298 
00299               if ( background == null || background.getWidth(this) != W || background.getHeight(this) != H || image == null || image.getWidth(this) != W || image.getHeight(this) != H ){
00300                      setup();
00301                      background = createImage( W, H );
00302                      drawBackground(W,H);
00303                      image = createImage( W, H );
00304                      graphics = (Graphics2D) image.getGraphics();
00305                      graphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
00306                      graphics.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
00307               }
00308 
00309               graphics.drawImage( background, 0, 0, this );
00310 
00311               // run through all active balls and draw them on the image
00312               PlinkoBall ball = FIRST_BALL;
00313               while ( ball != null ){
00314                      graphics.drawImage( IMAGES[ball.spaz], (int)(ball.X - BALL_RAD),  (int)(ball.Y - 2*BALL_RAD - PIN_RAD + 1), this );
00315                      //if ( ball.t == ball.C && ball.ROW < BINS-2 ) Plinko.click.play();
00316                      //try {
00317                             increment( ball );
00318                      //} catch ( NullPointerException npe ) {
00319                      //}
00320                      ball = ball.nextBall;
00321               }
00322               drawHist( graphics );
00323        //     if(showstats==1){
00324                   drawStats( graphics );
00325        //     }
00326               g.drawImage(image,0,0,this);
00327     }
00328        
00329     // under construction
00330     public void drawNormal(){
00331     }
00332        
00333        
00334     public void increment( PlinkoBall ball ){
00335               // if ball has landed on pin, reset t to 0 and pick a direction
00336               if ( ball.t == ball.C && ball.ROW < BINS-2 ){
00337                      ball.ROW++;
00338                      ball.COL += ball.DIR;
00339                      ball.t = 0;
00340                      ball.DIR = 0;
00341                      
00342                      // pick a new direction
00343                      
00344                      // jm.evers: reading controls or applet param...plinko.chance is parameter
00345                      if(plinko.wims_exercise == false){
00346                          if ( Math.random() < ((Double)plinko.prob.getValue()).doubleValue() ) ball.DIR = 1;
00347                      }
00348                      else
00349                      {
00350                          if ( Math.random() <  plinko.chance ){ ball.DIR = 1;}
00351                      }
00352 
00353                      // update speed
00354                      ball.C = 11-((Integer)plinko.rate.getValue()).intValue();
00355                      
00356                      if ( ball.sound ) Plinko.ping.play();
00357               }
00358 
00359               double dx = (double)(DIST*ball.t*(2*ball.DIR - 1))/(double)(ball.C);
00360 
00361               if ( ball.ROW < 0 ){ 
00362                      // ball falling onto top pin
00363                      ball.X = PINS[0][0][0];
00364                      dx = Math.abs(dx);
00365                      ball.Y = PINS[0][0][1] - DIST + DIST*ball.t*ball.t/(ball.C*ball.C);
00366               } else if ( ball.ROW < BINS-2 ) {
00367                      ball.X = PINS[ball.ROW][ball.COL][0] + dx;
00368                      dx = Math.abs(dx);
00369                      ball.Y = PINS[ball.ROW][ball.COL][1] - DIST*DYS[ball.C][ball.t];
00370               } else { 
00371                      // ball falling into bin
00372                      // dx = DIST*ball.t*(2*ball.DIR - 1)/10.0;
00373                      ball.X = PINS[ball.ROW][ball.COL][0] + dx;
00374                      if ( dx>0 ){ 
00375                             ball.X = Math.min(ball.X,PINS[ball.ROW][ball.COL][0] + 2*DIST - BALL_RAD );
00376                      } else {
00377                             ball.X = Math.max(ball.X,PINS[ball.ROW][ball.COL][0] - 2*DIST + BALL_RAD + 1);
00378                      }
00379                      dx = Math.abs(dx);
00380                      ball.Y = PINS[ball.ROW][ball.COL][1]  - dx*(ball.A*dx/DIST+ball.B);
00381               }
00382 
00383               ball.t++;
00384 
00385               if ( ball.Y > H - BOTTOM_MARGIN - PIN_RAD ){
00386                      if ( ball.previousBall != null && ball.nextBall != null){
00387                             ball.previousBall.nextBall = ball.nextBall;
00388                             ball.nextBall.previousBall = ball.previousBall;
00389                      } else if ( ball.previousBall != null && ball.nextBall == null ) {
00390                             ball.previousBall.nextBall = null;
00391                      } else if ( ball.previousBall == null && ball.nextBall != null ) {
00392                             ball.nextBall.previousBall = null;
00393                             FIRST_BALL = ball.nextBall;
00394                      } else  {
00395                             FIRST_BALL = null;
00396                             plinko.bins.setEnabled( true );
00397                      }
00398 
00399                      BALL_COUNT--;
00400                      updateHist(ball.COL+ball.DIR);
00401                      if ( ball.sound ) Plinko.click.play();
00402               }
00403     }
00404        
00405               
00406     public void updateHist( int i ){
00407               HIST[i]++;
00408               COUNT++;
00409               TOTAL += i;
00410               TOTAL_SQUARES += i*i;
00411               if ( HIST[i] > MAX) MAX = HIST[i];
00412               if ( i>= LEFT && i <= RIGHT ){
00413                      PERCENT++;
00414               }
00415     }
00416        
00417 
00418     public void drawStats(Graphics g){
00419     //jm.evers : if in a wims_exercise we show a limited amount of statistical data...
00420        if(showstats == 1){
00421            plinko.count.setText(plinko.label_count+ (int)COUNT);
00422            plinko.current_bin.setText(plinko.label_bin + CURRENT_BIN);
00423            plinko.current_bin_count.setText(plinko.label_bin_count + HIST[CURRENT_BIN]);
00424            if(COUNT > 0.0D){
00425               double d = TOTAL / COUNT;
00426                plinko.mean.setText(plinko.label_mean + (float)d);
00427                plinko.variance.setText(plinko.label_variance + (float)(TOTAL_SQUARES / COUNT - d * d));
00428               plinko.current_bin_prob.setText(plinko.label_bin_probability + (float)((double)(int)((double)(0x186a0L * HIST[CURRENT_BIN]) / COUNT) / 1000D) + "%");
00429               if(LEFT < BINS){
00430                   if(LEFT == RIGHT){
00431                      plinko.confidence.setText(plinko.label_confidence + (float)((double)(int)((100000D * PERCENT) / COUNT) / 1000D) + plinko.some_text + LEFT + ".");
00432                   } 
00433                   else 
00434                   {
00435                      plinko.confidence.setText(plinko.label_confidence + (float)((double)(int)((100000D * PERCENT) / COUNT) / 1000D) + plinko.some_text + LEFT + plinko.through + RIGHT + ".");
00436                   }
00437               } 
00438               else
00439               {
00440                   plinko.confidence.setText(plinko.label_confidence );
00441               }
00442            } 
00443            else
00444            {
00445               plinko.mean.setText(plinko.label_mean );
00446                plinko.variance.setText(plinko.label_variance);
00447                plinko.current_bin_prob.setText(plinko.label_bin_probability);
00448                plinko.confidence.setText(plinko.label_confidence);
00449            }
00450        }
00451        else
00452        {
00453        // just the total COUNT and "balls per active bin" 
00454            plinko.count.setText(plinko.label_count+ (int)COUNT);
00455            plinko.current_bin.setText(plinko.label_bin + (CURRENT_BIN+1));
00456            plinko.current_bin_count.setText(plinko.label_bin_count + HIST[CURRENT_BIN]);
00457        }
00458     }
00459        
00460 
00461     public void drawHist( Graphics2D g ){
00462               long h;
00463               double x0;
00464               double x1;
00465               String str;
00466               W = getWidth();
00467               H = getHeight();
00468               Rectangle2D rect;
00469 
00470               for (int i=0; i<BINS; i++){
00471                      if ( i==0 ){
00472                             x0 = PINS[BINS-1][0][0]-DIST;
00473                             x1 = PINS[BINS-2][0][0];
00474                      } else if ( i==BINS-1 ){
00475                             x0 = PINS[BINS-2][BINS-2][0];
00476                             x1 = PINS[BINS-1][BINS-1][0]+DIST;
00477                      } else {
00478                             x0 = PINS[BINS-2][i-1][0];
00479                             x1 = PINS[BINS-2][i][0];
00480                      }
00481 
00482                      //Draw bar
00483                      h = HIST[i];
00484                      if (MAX>100) h=(int)(100.0*h/MAX);
00485                      g.setColor( new Color(255,0,0,175) );
00486                      if ( i >= LEFT && i <= RIGHT ) g.setColor( new Color(0,255,0,175) );
00487                      rect = new Rectangle2D.Double(x0,H-h-BOTTOM_MARGIN,x1-x0,h);
00488                      g.fill( rect );
00489                      g.setColor(Color.black);
00490                      g.setStroke( new BasicStroke(1.0f) );
00491                      g.draw( rect );
00492               }
00493        
00494               // draw line under current bin
00495               g.setColor( Color.black );
00496               g.setStroke( new BasicStroke(3.0f) );
00497               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) );
00498     }
00499        
00500        
00501     public void drawBackground(){
00502               drawBackground( getWidth(), getHeight() );
00503     }
00504 
00505 
00506     public void drawBackground( int W, int H ){
00507               double[] p;
00508 
00509               Graphics2D backgroundgraphics = (Graphics2D) background.getGraphics();
00510               backgroundgraphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
00511               backgroundgraphics.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
00512               backgroundgraphics.setColor(Color.white);
00513               backgroundgraphics.fillRect( 0, 0, W, H );
00514               backgroundgraphics.setColor(Color.black);
00515 
00516               Image img = getBall( PIN_RAD, Color.black );
00517 
00518               // draw pins
00519               for (int i=0; i<BINS-1; i++){
00520                      for (int j=0;j<=i;j++){
00521                      p = PINS[i][j];
00522                      //backgroundgraphics.fill( new Ellipse2D.Double(p[0]-PIN_RAD,p[1]-PIN_RAD,2*PIN_RAD,2*PIN_RAD) );
00523                      backgroundgraphics.drawImage(img, (int)(p[0]-PIN_RAD),(int)(p[1]-PIN_RAD),this );
00524                      }
00525               }
00526 
00527               String s;
00528               backgroundgraphics.setFont( new Font("Helvetica",Font.BOLD,Math.min((int)(4*DIST)/3,50)) );
00529               FontMetrics fm = backgroundgraphics.getFontMetrics();
00530               // draw lines and numbers
00531               p = PINS[BINS-1][0];
00532               backgroundgraphics.draw( new Line2D.Double( p[0]-DIST,H-100-BOTTOM_MARGIN,p[0]-DIST,H-BOTTOM_MARGIN) );
00533               backgroundgraphics.setColor( Color.darkGray );
00534               backgroundgraphics.drawString(start_number,(int)(p[0] - fm.stringWidth("0")/2), H-100-BOTTOM_MARGIN+Math.min((int)(4*DIST)/3, 50) );
00535               for (int i=0; i<BINS-1; i++){
00536                      p = PINS[BINS-2][i];
00537                      s = ""+(i+incr);
00538                      backgroundgraphics.setColor( Color.darkGray );
00539                      backgroundgraphics.drawString(s,(int)(p[0] + DIST - fm.stringWidth(s)/2),H-100-BOTTOM_MARGIN+Math.min((int)(4*DIST)/3,50));
00540                      backgroundgraphics.setColor( Color.black );
00541                      backgroundgraphics.draw( new Line2D.Double( p[0],H-100-BOTTOM_MARGIN,p[0],H-BOTTOM_MARGIN) );
00542               }
00543               p = PINS[BINS-1][BINS-1];
00544               backgroundgraphics.draw( new Line2D.Double(p[0]+DIST,H-100-BOTTOM_MARGIN,p[0]+DIST,H-BOTTOM_MARGIN) );
00545               backgroundgraphics.draw( new Line2D.Double(0,H-BOTTOM_MARGIN,W,H-BOTTOM_MARGIN) );
00546               repaint();
00547     }
00548 
00549 
00550     public void keyTyped( KeyEvent ke ){
00551     }
00552        
00553        
00554     public void keyPressed( KeyEvent ke ){
00555               int code = ke.getKeyCode();
00556               char key = ke.getKeyChar();
00557 
00558               if ( key >= '0' && key <= '4' ){
00559                      int x = key - '0';
00560                      int n = 1;
00561                      for (int i=0; i<x; i++ ){
00562                             n *= 10;
00563                      }/*
00564                      plinko.countdown = n;
00565                      if ( !plinko.active ){
00566                             plinko.start();
00567                             plinko.bins.setEnabled( false );
00568                             plinko.thread = new Thread(plinko);
00569                             plinko.thread.start();
00570                             //start.setText("Stop");
00571                      }*/
00572                      for (int i=0; i<n; i++){
00573                             dropBall( n == 1 );
00574                      }
00575               } else if ( code == 37 ){  // left arrow
00576                      CURRENT_BIN--;
00577                      if ( CURRENT_BIN<0 ) CURRENT_BIN = BINS - 1;
00578                      updatePercent();
00579                      repaint();
00580               } else if ( code == 39 ){  // right arrow
00581                      CURRENT_BIN++;
00582                      if ( CURRENT_BIN>BINS-1 ) CURRENT_BIN = 0;
00583                      updatePercent();
00584                      repaint();
00585               } else if ( code == KeyEvent.VK_ENTER ){ // return or enter
00586               } else if ( key == ' ' ){
00587                      plinko.toggleStart();
00588               } else if (key==20){ //control t - toggle erasing
00589               } else if (key==3){ //control c - clear screen
00590               } else if (key==24){ //control x - kill all threads
00591               }
00592     }
00593 
00594 
00595     public void keyReleased( KeyEvent ke ){}
00596 
00597     public void mouseReleased( MouseEvent me ){}
00598     
00599     public void mouseEntered( MouseEvent me ){}
00600 
00601     public void mouseExited( MouseEvent me ){}
00602               
00603     public void updatePercent(){
00604        double mean = (BINS-1)*((Double)plinko.prob.getValue()).doubleValue();
00605        if ( CURRENT_BIN <= mean ){
00606            LEFT = CURRENT_BIN;
00607            RIGHT = (int)(2*mean) - LEFT;
00608        } else {
00609            RIGHT = CURRENT_BIN;
00610            LEFT = (int)(2*mean) - RIGHT;
00611        }
00612        LEFT = Math.max( LEFT, 0 );
00613        RIGHT = Math.min( RIGHT, BINS-1 );
00614        PERCENT = 0;
00615        for ( int i=LEFT; i<=RIGHT; i++ ){
00616            PERCENT += HIST[i];
00617        }
00618     }
00619        
00620        
00621     public void mouseClicked( MouseEvent me ){
00622        requestFocus();
00623        Point p = me.getPoint();
00624        int bin;
00625        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){
00626            bin = 0; 
00627            while ( bin<BINS-1 ){
00628               if ( p.x + DIST < PINS[BINS-1][bin+1][0] ) break;
00629                bin++;
00630            }
00631            CURRENT_BIN = bin;
00632            updatePercent();
00633            repaint();
00634        }                    
00635     }
00636 
00637 
00638     public void mousePressed( MouseEvent me ){}
00639 
00640     public void mouseDragged( MouseEvent me ){}
00641     
00642     public void mouseMoved( MouseEvent me ){}
00643 }
00644