Physics simulation – a “squishy” ball bouncing

A short while ago a computer science student asked me how one could simulate the physics of a “squishy” ball. The idea was to imitate the ball in the following video: http://vimeo.com/27907918

After playing around with it for a bit, I finally came up with something that at least looked realistic. Very briefly, for people unfamiliar with the physics, here is how I did it. I divided the simulation into x- and y- velocity components to simplify the math. The dynamics of a squishy ball include the following:

  1. Gravity causes an acceleration in the downward direction.
  2. Friction. Every time the ball moves parallel to a surface, frictional forces act to reduce the speed of the ball.
  3. Loss of momentum due to inelastic collisions. Every time the ball hits a surface, it bounces back with slightly lower speed.
  4. Deformation or “squishing.” As the ball hits a surface, it flattens against that surface.

Here is the code. It is implemented on Processing 2.1.


/*********************************************************
SquishyBallV04
Created June 18, 2014
Created on Processing 2.1
**********************************************************

Demonstrates the dynamics of a flexible ball bouncing
off the borders of the screen and slowing down with
friction and loss of momentum.

**********************************************************/

float framesPerSecond = 60.0;                   // [per second]
float dt = 1.0/60.0;                            // time between two frames [seconds]
float currTime;                                 // [seconds]
int WIDTH = 640, HEIGHT = 360;                  // graphics window width and height [pixels]
float currXPos, currYPos;                       // current position of the center of the ball [pixels]
float initXVel = 10.0/dt, initYVel = 20.0/dt;   // initial velocities in x and y directions [pixels/frame]
float currXVel = initXVel, currYVel = initYVel; // current velocities [pixels/frame]
float minXVelMu = 10.0, minYVelMu = 50.0;       /* velocities below which friction effects become so large
                                                   that the ball comes to a stop [pixels/frame]              */
float minXVelSquish = 10.0, minYVelSquish = 30.0;/* minimum velocities to cause squishing [pixels/frame]     */
float maxDia = 100, minDia = 50;
float ballWidth = maxDia, ballHeight = maxDia;  /* ball defined as an ellipse [pixels]                 */
float gravity = 980*3;                          /* Acceleration due to gravity [pixels/frame/frame]    */
float muX = 150.0, muY = 150.0;                 /* Coefficent of friction along x and y directions
                                                   Affects loss of from velocity rolling friction
                                                   along a wall [dimensionless]                        */
float cRX = 0.8, cRY = 0.8;                     /* Coefficient of restitution along x and y directions
                                                   Effects loss of momentum after a collision.
                                                   [dimensionless]                                      */

/*
This is run once
*/
void setup()
{
  //frameRate(framesPerSecond);
  size(WIDTH, HEIGHT); // drawing window
  // initial position at center of window
  currXPos = WIDTH/2;
  currYPos = HEIGHT/2;
  currTime = (float)millis()/1000.0; // [seconds]
}

/*
This is run repeatedly
*/
void draw()
{
  /*
  Redraw and recompute only once every time interval dt. This ensures
  the animation takes place at a rate of 1/dt regardless of which plaform
  is used.
  */
  if ( ((float(millis())/1000.0) - currTime) > dt /* seconds */)
  {
    currTime = float(millis())/1000.0;  // [seconds]
    background(0); // draw background
    ellipse(currXPos, currYPos, ballWidth, ballHeight); // draw ball
    moveBall();
  }
 
   
}

/*
Apply the equations of motion and move the ball
*/
void moveBall ()
{  
  updatePositions();
  applyEquationsOfMotion();  
 
}

/*
Updates positions based on velocities
*/
void updatePositions ()
{
  currXPos += currXVel * dt;
  currYPos += currYVel * dt;
 
}

/*
Updates velocities based on the equations of motion.
*/
void applyEquationsOfMotion ()
{
  applyCollisionEffects();
  applyGravityEffects();  
  applyFrictionEffects();  
 
}

/*
 Equations of motion for collision with wall. It has three effects.
 The travel direction is reversed, some momentum is lost due to
 the collision being inelastic, and deformation (squihing) occurs.
 Magnitude of loss of momentum depends on the coefficient of restitution
 with that wall. To simulate real world deformation, deformation occurs
 only above a certain velocity.
*/
void applyCollisionEffects ()
{
  // if ball touches right/left wall
 
  if ((currXPos + minDia/2) > WIDTH)  {
    
     currXVel *= -cRX;  
     currXPos = WIDTH - minDia/2;  
     
  }  else if ((currXPos - minDia/2) < 0)  {
    
     currXVel *= -cRX;    
     currXPos = minDia/2;
     
  }  else if ((currXPos + maxDia/2) > WIDTH){
    
    // partial squishing, right wall
    ballWidth = maxDia - (currXPos + ballWidth/2 - WIDTH);
    ballHeight = maxDia + (currXPos + ballWidth/2 - WIDTH);
    
  }   else if ((currXPos - maxDia/2) < 0)  {
    
    // partial squishing, left wall
    ballWidth = maxDia + (currXPos - ballWidth/2);
    ballHeight = maxDia - (currXPos - ballWidth/2);
  }
 
 
  // if ball touches top/bottom wall
  if ( (currYVel > minYVelSquish) && (currYVel < (-1.0*minYVelSquish)) ){
    
    // high speed collision effects, with squishing
    if ((currYPos + minDia/2) > HEIGHT)  { // maximum squishing
      
       currYVel *= -cRY;
       currYPos = HEIGHT - minDia/2;  
       
       
    } else if  ((currYPos - minDia/2) < 0) {
      
       currYVel *= -cRY;    
       currYPos = minDia/2;    
       
    } else if ((currYPos + maxDia/2) > HEIGHT) {
      
      // partial squishing, bottom wall    
      ballHeight = maxDia - (currYPos + ballHeight/2 - HEIGHT);
      ballWidth = maxDia + (currYPos + ballHeight/2 - HEIGHT);
      
      
    }  else if  ((currYPos - maxDia/2) < 0) {
      
      // partial squishing, top wall
      ballHeight = maxDia + (currYPos - ballHeight/2);
      ballWidth = maxDia - (currYPos - ballHeight/2);
      
    }
  } else {
    
    // low speed collision effects, no squishing
    if ((currYPos + maxDia/2) > HEIGHT) {
      
      // no squishing, bottom wall    
      currYVel *= -cRY;
      currYPos = HEIGHT - maxDia/2;
      
      
    }  else if  ((currYPos - maxDia/2) < 0) {
      
      // no squishing, top wall
      currYVel *= -cRY;    
      currYPos = maxDia/2;  
    }
    
  }
 
}

/*
  Equations of motion for effects of gravity. Gravity is positive
  in the positive y direction.
*/
void applyGravityEffects ()
{
  currYVel += gravity * dt;
}

/*
  Equations of motion for the friction effects with walls. Assuming only friction
  along horizontal walls is significant. Assuming friction with vertical walls is
  negligible for our animation, since the ball spends only a negligible amount of
  time sliding down on the vertical walls. At very low speeds the ball comes to a
  stop, simulating the real world effect.
*/
void applyFrictionEffects () {
  // if touching any of the horizontal walls
  if (((currYPos + maxDia/2) >= HEIGHT) || ((currYPos - maxDia/2) <= 0))  {
    if(currXVel > minXVelMu) {
      currXVel -= muX*dt;
    } else if (currXVel < (-1.0*minXVelMu) ) {
      currXVel += muX*dt;
    } else {
      currXVel = 0.0; // stop if too slow
    }
  }
 
}

</pre>
<pre>