Simple Physics Engine :: Bouncing Ball Inside Circle

Uses Minkovski sum technique to properly bounce a 2D ball off a circular wall. For more information, see: http://gamedev.stackexchange.com/q/29650

HTML

<div>state: <span id="state" style="font-weight:bold;">init</span></div>
<div>
  <button id="step_button">step</button>
</div>
<canvas id="the_canvas" width="250" height="250"></canvas>

JavaScript

/*
 * Simple Physics Engine :: Bouncing Ball Inside Circle
 * http://jsfiddle.net/user/klenwell/fiddles/
 */
var Physics = {

  bounce_ball_off_world: function(ball, world) {
    /*
     * translate ball back inside world
     * http://gamedev.stackexchange.com/a/29658
     */
    var contact_pt = this.find_contact_point(world, ball);
    ball.set_point(contact_pt.x, contact_pt.y);

    /*
     * reflect velocity of ball
     * http://stackoverflow.com/questions/573084/bounce-angle
     */
    var new_ball_vel = this.reflect_ball(world, ball);
    ball.set_velocity(new_ball_vel.x, new_ball_vel.y);
  },

  find_contact_point: function(world, ball) {
    // see http://gamedev.stacmedev.stackkexchange.com/a/29658
    var A = world.point();
    var B = ball.point().subtract(ball.velocity());
    var C = ball.point();
    var R = world.r;
    var r = ball.r;

    var AB = B.subtract(A);
    var BC = C.subtract(B);
    var AB_len = AB.get_length();
    var BC_len = BC.get_length();

    // if BC_len == 0, then B and C are equal, meaning D is also equal.
    // Continuing will produce divide-by-zero error
    if (!BC_len) {
      return C;
    }

    var b = AB.dot(BC) / Math.pow(BC_len, 2) * -1;
    var c = (Math.pow(AB_len, 2) - Math.pow(R - r, 2)) / Math.pow(BC_len, 2);
    var d = b * b - c;
    var k = b - Math.sqrt(d);

    if (k < 0) {
      k = b + Math.sqrt(d);
    }

    var BD = C.subtract(B);
    var BD_len = BC_len * k;
    BD.set_length(BD_len);

    var D = B.add(BD);
    return D;
  },

  reflect_ball: function(world, ball) {
    // see http://stackoverflow.com/questions/573084/bounce-angle
    var world_pt = world.point();
    var ball_pt = ball.point();
    var v = ball.velocity();
    var n = ball_pt.subtract(world_pt).normalized();

    // assume perfect elasticity for now
    // TODO: add elasticity attr to balls
    var ball_elasticity = 1.0;
    var world_elasticity = 1.0;
    var restitution = ball_elasticity * world_elasticity;

    // solve...