Elastic collisions of multiple balls is not a trivial effect. Apparently this subject seems only related to physics but collision between objects is simulated in gaming. To run collision of balls we need to understand physics laws.

Consider two spherical rigid objects, denoted by subscripts 1 and 2, one of mass m1 and the other of mass m2. Let's set things up so these two balls are approaching each other along the line joining their centers, a recipe for a head on collision. Let these balls not be rotating or vibrating; the motion is purely translation. Under these conditions, we may choose a reference frame, which is one dimensional, with the objects on the x-axis. We consider an isolated system, in other words a system where external forces are absent (nothing affects spheres in the stage). A perfectly elastic collision is defined as one in which there is no loss of kinetic energy in the collision; the conservation laws say in an elastic collision total kinetic energy is the same before and after the collision and total momentum remains constant throughout the collision. Applying the conservation of linear momentum to this situation, we have:

m1v1 + m2v2 = m1u1 + m2u2

where v is the initial velocity of each object before the collision and u is the final velocity of each object after the collision and m is the mass of the object. The conservation of kinetic energy gives us the equation

m1v12 /2 + m2v22 /2 = m1u12 /2 + m2u22 /2

These two equations can rewritten as:

  • momentum equation:           m1 (v1 - u1) = m2 (u2 - v2)  
  • conservation of kinetic energy: m1 (v12 - u12) = m2 (u22 - v22)

As long as the difference between final and initial velocities is not zero for either object (meaning a collision actually happens), we may divide the second equation by the first one, which yields:

v1 + u1 = u2 + v2 or v1 - v2 = u2 - u1

In other words in a one-dimensional elastic collision, the relative velocity of approach before the collision equals the relative velocity of separation after collision. To get the final velocities in terms of the initial velocities and the masses, we would solve the last equation above for u2, plug that into the momentum equation, and solve to get:

u1 = v1 (m1-m2)/(m1+m2) + v2 (2 m2)/(m1 + m2)
u2 = v1 (2 m1) /(m1+m2) + v2 (m2 - m1)/ (m2 + m1)

Let start out with two spheres moving in the plane to have a collision

 

Figure 1: angles in collision

Also in bi-dimensional collision, we ignore every energy losses due to friction and rotation. The physical laws (the conservation of momentum and conservation of kinetic energy) are invariant for changing of Cartesian coordinate system, so we can apply a transformation and consider a new standard x’-y’ coordinate system (Figure 2), where the x-axis lies along the collision line, and the y-axis is perpendicular to that.  In this new Cartesian coordinate system is easier to understand what happens.

Figure 2: new Cartesian coordinate system

The Figure 3 shows the new vector components of velocities, in the new Cartesian coordinate system where the x-axis lies along the collision line, and the y-axis is perpendicular to that.

Figure 3: x and y velocity components in the new coordinate system

In the new Cartesian coordinate system the velocities components of spheres along x,y’-axis are:

v’1x = v1*cos(q1-F)

v’1y = v1*sin(q1- F)

v’2x = v2*cos(q2- F)

v’2y = v2*sin(q2- F)

In the case of the two spheres, the velocity components involved are the components resolved along the line of centers during the contact. Consequently, the components of velocity perpendicular to the line of centers will be unchanged during the impact. The new x velocities in our new coordinate system follows the same outcome of collision 1D, and the y-components do not change.

u’1x = ((m1-m2)*v’1x+(m2+m2)*v’2x)/(m1+m2)
u’2x = ((m1+m1)*v’1x+(m2-m1)*v’2x)/(m1+m2)
u’1y = v’1y      (the y velocity of sphere 1 will not be changed)
u’2y = v’2y      (the y velocity of sphere 2 will not be changed)

The Figure 4 shows the x-velocities inversion, after the collision

Figure 4: velocities components in new Cartesian coordinate system

We have determined the 'after collision' velocities, but we have to transform the components back to the initial x-y reference frame.

 

Figure 5: changing back to new initial Coordinate system (Ref) 

Analysis of velocity components in the original Cartesian coordinate system can be done through graphical approach; in Figure 6, Figure 7, Figure 8 and Figure 9 are shown the x, y components of velocities after the collision and the related magnitudes.

Figure 6: x-component of velocity after collision of sphere 1 in initial coordinate system

 

 

Figure 7: y-component of velocity after collision of sphere 1 in initial coordinate system

 

 

Figure 8: x-component of velocity after collision of sphere 2 in initial coordinate system

 

 

Figure 9: y-component of velocity after collision of sphere 2 in initial coordinate system

u1x = u’1x * cos(F)+ u’1y * cos(p/2+ F)
u1y = u’1x * sin(F)+ u’1y * sin(p/2+ F)
u2x = u’2x * cos(F)+ u’2y * cos(p/2+ F)
u2y = u’2x * sin(F)+ u’2y * sin(p/2+ F)

Considering the trigonometric properties of sin() and cos() functions, we have:

u1x = u’1x * cos(F)- u’1y * sin(F)
u1y = u’1x * sin(F)+ u’1y * cos(F)
u2x = u’2x * cos(F)- u’2y * sin(F)
u2y = u’2x * sin(F)+ u’2y * cos(F)

Same outcome can be achieved by mathematical approach. In fact, translation from Cartesian coordinate system Ref’ to Ref can be execute by counterclockwise rotation matrix M(F) :

 

Executing matrix multiplication produce the result:

 

In other words, to do a perfectly elastic collision between the balls we only need to consider the component of the velocity that is in the direction of the collision. The other component (tangent to the collision) will stay the same for both balls. We can get the collision components by creating a unit vector pointing in the direction from one ball to the other, then taking the dot product with the velocity vectors of the balls. We can then plug these components into a 1D perfectly elastic collision equation. In our project we introduce a class named Ball containing all elementary information needed to identify the ellipse in canvas control and physical parameters (position, mass, velocity) for describing motion: 

public class Ball
{
  public Ellipse ellip = new Ellipse(); // Ellipse object 
  public double r; // circle radius
  public double m; // mass of the ball 
  public Vector p; // position vector
  public Vector v; // velocity vector
}

Starting animation is triggered from on-click button named “Start”:

private void StartButton_Click(object sender, RoutedEventArgs e)
{
   // remove EventHandler from Rendering event, if exists
   CompositionTarget.Rendering -= new EventHandler(RenderFrame);
   // change the label of button from "Start" to "Stop"
   StopContinueButton.Content = "Stop";
   // clean all Ellipse object from canvas, if it exists
   CleanBalls();
   // Assign the correct number of circles to be controlled
   TotNumCircles = tmpTotalNumCircles;
   // Resizing of correct number of array of Ball class
   // Number of array elements is defined from variable TotNumCircles
   Array.Resize(ref balls, TotNumCircles);
   // it draws all ellipses in canvas, with circle distribution
   StartDraw();
   // set an EventHandler on rendering event, with RenderFrame as callback function
   CompositionTarget.Rendering += new EventHandler(RenderFrame);
   StopContinueButton.IsEnabled = true;
   flagStopContinue = true;
}

In  Figure 10 is reported a flow diagram for on-click on Start button

 

Figure 10: flow executed by click-on Start button event

 Code and diagram are mostly self-explain; we just give some insight on how works the logic of project.

At beginning of running a method StartDraw() draws circles in the canvas;  StartDraw() uses a basic Mathematical function to dispose all circles around the center of canvas, avoiding overlaps:

// R: radius to position the balls in circles from center of canvas control
double R;
// R0: initial value of radius to position circles from center of canvas to the edges
double R0 = 80d;
// theta: angle to distribute the balls in canvas, avoiding overlap
double theta = 0;
// initial value of angle of radius R
double theta_old = -1 * Math.PI;
double m = 0d;
R = R0;
theta = (k == 0) ? Math.Atan2(3 * balls[k].r, R) : Math.Atan2(2 * balls[k].r + 2 * balls[k - 1].r, R);
theta = theta + theta_old;
m = Convert.ToInt32(theta / (2 * Math.PI));
R = R0 + (R0 / 2) * m;
theta_old = theta;
balls[k].v = rnd1.Next(minSpeed, maxSpeed) * new Vector(Math.Cos(theta), Math.Sin(theta));
balls[k].p = startVector + new Vector(R * Math.Cos(theta), R * Math.Sin(theta));
balls[k].ellip.SetValue(Canvas.LeftProperty, balls[k].p.X - balls[k].r);
balls[k].ellip.SetValue(Canvas.TopProperty, balls[k].p.Y - balls[k].r);
canv.Children.Add(balls[k].ellip);

 

On completion of StartDraw() all balls[k] (k=0,1,2,…. TotNumCircles) are drawn in the canvas in fix position (Figure 11).

Figure 11: initial position of all ellipses in the canvas

 The System.Windows.Media.CompositionTarget static class is the engine of balls animation and it represents the display surface on which our application is drawn. Every time the CompositionTarget class raises the Rendering event, notifies any event handlers that a frame has been rendered. To create the  animation we attach to the  Rendering event an event handler:

CompositionTarget.Rendering += new EventHandler(RenderFrame);

The callback function RenderFrame() is called every time the Rendering event is fired from CompositionTarget; in  Figure 11 is shown the flow for RenderFrame() method.

  

Figure 12: main flow of the program

 RenderFrame() has a main loop for scanning all elements of array of Ball class for two actions: 

  • increase the position of the ellipse a step forward with magnitude specified in Vector velocity (v), to make an effect of animation:

balls[j].p = balls[j].p + balls[j].v;

  • verify that the ellipse does not intersect the edges of canvas. If the ellipse oversteps the boundary of canvas, it is applied an inversion of the velocity component orthogonal to the edge (see Figure 12) 

 

 

Figure 13: inversion of velocity component orthogonal to the edge of canvas

Inside the main loop of the RenderFrame() method exists a further loop which makes two cascade  checks:

  • First check runs through the colliding() method, to establish if the specific ball doesn’t intercept with any others.  colliding() method accepts as input parameters two Balls class and return a boolean equal true in case of interception between two balls (i.e. balls[i] and  balls[j]). To detect collision between two balls, is enough verify the distance between their centers is less than the sum of their radii. Given two circles (x0, y0, R0) and (x1,y1,R1), condition of interception follows the formula:

ABS(R0-R1) <= SQRT((x0-x1)^2+(y0-y1)^2) <= (R0+R1)

  • Second check runs through resolveCollision(); this last methods runs only if the colliding() methods detect an interception between balls. resolveCollision() method contains physics laws to control velocities of balls after collision and one algorithm based on Minimum Translation Distance (mtd) technique, to push balls apart in case of balls intersecting. Checking for collisions between balls happens through internal loop, created with start value that skips redundant checks:
    • if you have to check if ball 1 collides with ball 2 then you don't need to check if ball 2 collides with ball 1.
    • it skips checking for collisions with itself

Below the colliding() routine.

public bool colliding(Ball _ball1, Ball _ball2)
{
   double r1 = _ball1.r;
   double r2 = _ball2.r;
   double dist = (_ball1.p - _ball2.p).Length;
   if (dist > (r1 + r2))
   {
       // No solutions, the circles are too far apart. 
       return false;
    }
    else if (dist <= r1 + r2)
    {
       // One circle contains the other.
       return true;
    }
    else if ((dist == 0) && (r1 == r2))
    {
        // the circles coincide.
        return true;
    }
    else return true;
}

 

public void resolveCollision(Ball _ball1, Ball _ball2)
{
   double collisionision_angle = Math.Atan2((_ball2.p.Y - _ball1.p.Y), (_ball2.p.X - _ball1.p.X));        
   double speed1 = _ball1.v.Length;
   double speed2 = _ball2.v.Length;

   double direction_1 = Math.Atan2(_ball1.v.Y, _ball1.v.X);
   double direction_2 = Math.Atan2(_ball2.v.Y, _ball2.v.X);
   double new_xspeed_1 = speed1 * Math.Cos(direction_1 - collisionision_angle);
   double new_yspeed_1 = speed1 * Math.Sin(direction_1 - collisionision_angle);
   double new_xspeed_2 = speed2 * Math.Cos(direction_2 - collisionision_angle);
   double new_yspeed_2 = speed2 * Math.Sin(direction_2 - collisionision_angle);

   double final_xspeed_1 = ((_ball1.m - _ball2.m) * new_xspeed_1 + (_ball2.m + _ball2.m) * new_xspeed_2) / (_ball1.m + _ball2.m);
   double final_xspeed_2 = ((_ball1.m + _ball1.m) * new_xspeed_1 + (_ball2.m - _ball1.m) * new_xspeed_2) / (_ball1.m + _ball2.m);
   double final_yspeed_1 = new_yspeed_1;
   double final_yspeed_2 = new_yspeed_2;

   double cosAngle = Math.Cos(collisionision_angle);
   double sinAngle = Math.Sin(collisionision_angle);
   _ball1.v.X = cosAngle * final_xspeed_1 - sinAngle * final_yspeed_1;
   _ball1.v.Y = sinAngle * final_xspeed_1 + cosAngle * final_yspeed_1;
   _ball2.v.X = cosAngle * final_xspeed_2 - sinAngle * final_yspeed_2;
   _ball2.v.Y = sinAngle * final_xspeed_2 + cosAngle * final_yspeed_2;

   Vector pos1 = new Vector(_ball1.p.X, _ball1.p.Y);
   Vector pos2 = new Vector(_ball2.p.X, _ball2.p.Y);

   // get the mtd
   Vector posDiff = pos1 - pos2;
   double d = posDiff.Length;

   // minimum translation distance to push balls apart after intersecting
   Vector mtd = posDiff * (((_ball1.r + _ball2.r) - d) / d);

   // resolve intersection --
   // computing inverse mass quantities
   double im1 = 1 / _ball1.m;
   double im2 = 1 / _ball2.m;

   // push-pull them apart based off their mass
   pos1 = pos1 + mtd * (im1 / (im1 + im2));
   pos2 = pos2 - mtd * (im2 / (im1 + im2));
   _ball1.p = pos1;
   _ball2.p = pos2;

   if (((_ball1.p.X + _ball1.r) >= canv.Width) | ((_ball1.p.X - _ball1.r) <= 0))
      _ball1.v.X = -1 * _ball1.v.X;

   if (((_ball1.p.Y + _ball1.r) >= canv.Height) | ((_ball1.p.Y - _ball1.r) <= 0))
      _ball1.v.Y = -1 * _ball1.v.Y;

   if (((_ball2.p.X + _ball2.r) >= canv.Width) | ((_ball2.p.X - _ball2.r) <= 0))
       _ball2.v.X = -1 * _ball2.v.X;

    if (((_ball2.p.Y + _ball2.r) >= canv.Height) | ((_ball2.p.Y - _ball2.r) <= 0))
       _ball2.v.Y = -1 * _ball2.v.Y;

 }

 Here the code for RenderFrameFrame() method.

public void RenderFrame(object sender, System.EventArgs e)
{
   for (int j = 0; j < TotNumCircles; j++)
   {
      //  balls[j].p.X = balls[j].p.X + balls[j].v.X;
      //  balls[j].p.Y = balls[j].p.Y + +balls[j].v.Y;
      balls[j].p = balls[j].p + balls[j].v;

      if ((balls[j].p.X + balls[j].r) >= canv.Width)
      {
         balls[j].v.X = -1 * balls[j].v.X;
         balls[j].p.X = canv.Width - balls[j].r;
       }
       if ((balls[j].p.X - balls[j].r) <= 0)
       {
          balls[j].v.X = -1 * balls[j].v.X;
          balls[j].p.X = balls[j].r;
       }

       if ((balls[j].p.Y + balls[j].r) >= canv.Height)
       {
            balls[j].v.Y = -1 * balls[j].v.Y;
            balls[j].p.Y = canv.Height - balls[j].r;
        }
        if ((balls[j].p.Y - balls[j].r) <= 0)
        {
            balls[j].v.Y = -1 * balls[j].v.Y;
            balls[j].p.Y = balls[j].r;
         }

         Canvas.SetLeft(balls[j].ellip, balls[j].p.X - balls[j].r);
         Canvas.SetTop(balls[j].ellip, balls[j].p.Y - balls[j].r);

         for (int k = j + 1; k < TotNumCircles; k++)
         {
            if (colliding(balls[j], balls[k]))
            {
                TextBoxCollision.Text = "Collision pair: " + j.ToString() + "-" + k.ToString();
                resolveCollision(balls[k], balls[j]);
             }
          }
   }
}

 Porting to Windows Store Apps

You might be interest to evaluate porting of the project to Windows Store Apps; effort is actually minimal, and the structure of project require few minor changes. The System.Windows.Media.CompositionTarget class needs to be replaced in Windows StoreApps with Windows.UI.Xaml.Media.CompositionTarget   class that represents the composited display surface of the application. The Rendering event occurs when the core rendering process renders a frame. Missing  System.Windows.Vector class in Windows StoreApps can be overcome creating our own Vector class:

public class Vector
{
   private double _x, _y;
   public Vector(double x, double y) { _x = x; _y = y; }

   public double X
   {
       get { return _x; }
       set { _x = value; }
   }

   public double Y
   {
       get { return _y; }
       set { _y = value; }
   }

   public static Vector operator +(Vector v1, Vector v2)
   {
       return new Vector(v1.X + v2.X, v1.Y + v2.Y);
   }
   public static Vector operator -(Vector v1, Vector v2)
   {
       return new Vector(v1.X - v2.X, v1.Y - v2.Y);
   }
   public static Vector operator *(Vector v1, double scale)
   {
       return new Vector(v1.X * scale, v1.Y * scale);
   }
   public static Vector operator *( double scale, Vector v1)
   {
       return new Vector(v1.X * scale, v1.Y * scale);
   }
   public double Length
   {
       get { return ((double)Math.Sqrt(X * X + Y * Y)); }
       set
       {
          if (value < 0)
          {
              throw new ArgumentOutOfRangeException("value", value,
              "The magnitude of a Vector must be a positive value greater than 0");
          }
        }
    }
}

In the Vector class the operator * should accept interchanging in the order of operands; this properties can be achieve through two methods: one method for the sequence (vector, double) and the other for the sequence (double, vector). In our project the variable TotNumCircles controls the total number of ellipses to be animated in canvas control. At runtime the user can change the variable TotNumCircles through  a slider. Simultaneously the value of variable has to be shown to the UI through a Textbox. A correct implementation requires that Textbox control always shows the current value of the variable  TotNumCircles, or in other words TextBox value has to be always alignment with slider’s value. Data binding can be used either between UI-elements or to bind CLR-objects and UI-elements together. To achieve the right constrain we bind the TextBox’s Text-property to Slider’s value. Using as data binding TwoWay, when we change the sliders value the TextBox’s Text-property will be updated as well. If we type a value into the TextBox the slider will be updated as well. Below the relative fragment of MainPage.xaml:

.....

 <Slider Name="SliderTotalBalls" Minimum="10" Maximum="100"  TickFrequency="1"  ValueChanged="Slider_ValueChanged" />

 <TextBox Name="TextBoxTotNumBalls" Text="{Binding ElementName=SliderTotalBalls, Path=Value, Mode=TwoWay}" />

.....

To complete our porting in Windows Store apps, we apply two last changes:

  • System.Windows.RoutedPropertyChangedEventArgs  class needs to be replaced with Windows.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs
  • System.Windows.Media.Brushes needs to be replaced with Windows.UI.Colors

 i.e.   

balls[k].ellip.Fill =System.Windows.Media.Brushes.Blue;

is replaced in Windows Store App with:  balls[k].ellip.Fill = SolidColorBrush(Windows.UI.Colors.Blue);

 

You can download the Visual Studio 2012 projects here:

BallsCollisionFrameBased.zip 
Frame-based project.

AnimationSpinningCircles_FrameBased.zip 
Timer-based project.

SpinningCircles_FrameBasedWin8.zip;
Windows Store App Frame-based project.

Reference

 Ball to Ball Collision - Detection and Handling:  http://stackoverflow.com/questions/345838/ball-to-ball-collision-detection-and-handling

The physics of an elastic collision: Part I   http://www.director-online.com/buildArticle.php?id=460

The Physics of an Elastic Collision (Part 2) http://www.director-online.com/buildArticle.php?id=532

Central and Eccentric Collisions of Balls http://physics-animations.com/Physics/English/par_txt.htm