Animation - Threads
Previous    Next    Home    Source    Package

Two Balls in a Confined Area
  1. The two dots in motion look like they are bouncing off each other and the rectangular boundary as if they are two balls of equal mass rolling on a frictionless billiard table. How is this done? Consider easy things first. There is a thread that recomputes ball positions based on ball velocities and redraws the balls on the table every 25 milliseconds. Suppose the balls are in an array created by
     
       Ball balls[] = new Ball[2]; // Array of Balls 
    
    Each ball is initialized (by the constructor) with a random horizontal velocity component, a random vertical velocity component, and x and y position. These are accessed via
       balls[a].vx  // horizontal velocity, ball 'a'
       balls[a].vy  // vertical velocity, ball 'a'
       balls[a].x   // horizontal position, ball 'a'
       balls[a].y   // vertical position, ball 'a'
    
    respectively. Both balls have the same diameter which is given by variable ballDia. It is easy to compute the next position of a ball given its current velocity and position if the ball does not hit anything. Just do this:
       balls[a].x += balls[a].vx*time;
       balls[a].y += balls[a].vy*time;
    
    where time is some constant factor that exists to make adjustments easy. It is also fairly easy to bounce a ball off a boundary: if it bounces off a vertical boundary its horizontal velocity is reversed and if it bounces off a horizontal boundary its vertical velocity is reversed. But first it has to be determined whether it hits a boundary. This is done by computing the distance from ball center to the boundary and, if the distance is less than ballDia/2, a velocity is reversed. The test, and change if needed, looks like this for the left vertical boundary:
       if (balls[n].x <= ballDia/2 ) {
          balls[n].x = ballDia/2;
          balls[n].vx = -balls[n].vx;
       }
    
    which reverses the horizontal velocity and 'cheats' by repositioning the ball flush against the boundary. This turns out not to present a problem for the viewer because the change from what the actual position would be is quite small.
  2. Now we get to the hard part: how to deal with balls hitting each other. This actually would be quite simple if the direction of the balls is exactly the same and is also the same as the direction of an imaginary line connecting the centers of the balls. In that case the velocities would switch! Seem strange? Try it! Roll a ball at a stationary ball so that it hits dead center. The stationary balls takes the velocity of the hitting ball and the hitting ball comes to a stop (I am sure you have seen this demonstration many times). Similarly, if the target ball is moving away or toward the throw - the velocities switch. Generally, the balls do not hit dead center. But we can transform the horizontal and vertical velocities of the balls to velocity components that are perpendicular to a line between centers and parallel to that line. The perpendicular components will not change. The parallel components change as stated about. The change is a simple switch. Then it is necessary to transform back to horizontal and vertical velocity components. The following code does all this between balls 'a' and 'b'.
       // find cos and sin of angle between centers line and horiz
       double dx = (balls[a].x-balls[b].x);
       double dy = (balls[a].y-balls[b].y);
       double dist = Math.sqrt(dx*dx+dy*dy);
       double sinalpha = dy/dist; // sin of angle of centers line
       double cosalpha = dx/dist; // cos of angle of centers line
    
       // find velocity components parallel to and perpendicular
       // to the centers line
       double vapara = balls[a].vx*cosalpha + balls[a].vy*sinalpha;
       double vaperp = balls[a].vx*sinalpha - balls[a].vy*cosalpha;
       double vbpara = balls[b].vx*cosalpha + balls[b].vy*sinalpha;
       double vbperp = balls[b].vx*sinalpha - balls[b].vy*cosalpha;
    
       // Switch
       double temp = vapara;
       vapara = vbpara;
       vbpara = temp;            
    
       // Reestablish x and y velocity directions
       balls[a].vx = vapara*cosalpha + vaperp*sinalpha;
       balls[a].vy = vapara*sinalpha - vaperp*cosalpha;
       balls[b].vx = vbpara*cosalpha + vbperp*sinalpha;
       balls[b].vy = vbpara*sinalpha - vbperp*cosalpha;