Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Balls bouncing in a 2D frustum</title>
</head>
<body onload="start();">
<canvas id=canvas width=500 height=500 style="border:1px solid #000000;">
</body>
</html>
 
function start() {
  var canvas = document.querySelector("#canvas");
  var context = canvas.getContext("2d");
  
  var frustum = new Frustum(250, 250, 150, 200, 100);
  
  var balls = [];
  var N = 1;
  for (var i = 0; i < N; i++) {
    var theta = 2*i*Math.PI/N;  // for the direction of speed
    var ball = new Ball(
      frustum.x + 1/2* frustum.L2*(0.5 - Math.random()),
      frustum.y + 1/2* frustum.height*(0.5 - Math.random()),
      Math.cos(theta),
      Math.sin(theta)
    );
    balls.push(ball);
  }
  
  (function animate() {
    requestAnimationFrame(animate);
    context.clearRect(0, 0, canvas.width, canvas.height);
    frustum.draw(context);
    var dt = 1;
    for (var i = 0; balls[i]; i++) {
      balls[i].draw(context);
      balls[i].move(dt);
      frustum.move(dt);
      frustum.makeBallBounce(balls[i]);
    }
  })();
  
}
function Ball(x, y, vx, vy) {
  this.x = x;
  this.y = y;
  this.vx = vx;
  this.vy = vy;
  this.color = [
    '#3A5BCD', '#EF2B36', '#FFC636', '#02A817'
  ][Math.floor(Math.random()*4)]; 
}
Ball.prototype.mass = 1;
Ball.prototype.radius = 3;
Ball.prototype.move = function (dt) {
  this.x += this.vx * dt;
  this.y += this.vy * dt;
};
Ball.prototype.draw = function (ctx) {
  ctx.save();
  ctx.fillStyle = this.color;
  ctx.beginPath();
  ctx.arc(this.x, this.y, this.radius, 0, 2*Math.PI);
  ctx.fill();
  ctx.restore();
};
function Frustum(x, y, height, L1, L2) {
  this.mass = 1.5;
  this.x = x;
  this.y = y;
  this.vx = 0;
  this.vy = 0;
  this.height = height;
  this.L1 = L1;
  this.L2 = L2;
  this.move = function ( dt ) {
    this.x += this.vx * dt;
    this.y += this.vy * dt;
  };
  this.draw = function (ctx) {
    var x = this.x, y = this.y,
        height = this.height,
        L1 = this.L1, L2 = this.L2;
    ctx.save();
    ctx.strokeStyle = "black";
    ctx.translate(x, y);
    ctx.beginPath();
    ctx.moveTo(-L1/2,-height/2);
    ctx.lineTo(+L1/2,-height/2);
    ctx.lineTo(+L2/2,+height/2);
    ctx.lineTo(-L2/2,+height/2);
    ctx.lineTo(-L1/2,-height/2);
    ctx.stroke();
    ctx.restore();
  };
  this.makeBallBounce = function (ball) {
    var totalMass = this.mass + ball.mass;
    var theta = Math.atan((this.L1 - this.L2) / 2 / this.height);
    var c = Math.cos(theta), s = Math.sin(theta);
    
    var x, y, X, Y,
        ballVx, ballVy, thisVx, thisVy,
        ballVX, ballVY, thisVX, thisVY;
    
    x = ball.x - this.x + this.L1/2;
    y = ball.y - this.y + this.height/2;
    
    // See https://en.wikipedia.org/wiki/Elastic_collision
    if (y < 0 || y > this.height) {
      ballVy = ball.vy;
      thisVy = this.vy;
      ball.vy =
        (ball.vy*(ball.mass - this.mass) + 2*this.mass*thisVy)/totalMass;
      this.vy =
        (thisVy*(this.mass - ball.mass) + 2*ball.mass*ballVy)/totalMass;
      if (y < 0) { ball.y = this.y - this.height/2; }
      if (y > this.height) { ball.y = this.y + this.height/2; }
    }
    
    X = c*x - s*y; Y = c*y + s*x;
    ballVX = c*ball.vx - s*ball.vy;
    ballVY = c*ball.vy + s*ball.vx;
    thisVX = c*this.vx - s*this.vy;
    thisVY = c*this.vy + s*this.vx;
    
    if (X < 0) {
      ball.vX =
        (ballVX*(ball.mass - this.mass) + 2*this.mass*thisVX)/totalMass;
      this.vX =
        (thisVX*(this.mass - ball.mass) + 2*ball.mass*ballVX)/totalMass;
      ball.vx = c*ball.vX + s*ballVY;
      ball.vy = c*ballVY - s*ball.vX;
      this.vx = c*this.vX + s*thisVY;
      this.vy = c*thisVY - s*this.vX;
      X = 0;
      x = c*X + s*Y;  y = c*Y - s*X;
      ball.x = x + this.x - this.L1/2;
      ball.y = y + this.y - this.height/2;
    }
    x = ball.x - this.x - this.L1/2;
    y = ball.y - this.y + this.height/2; 
    c = Math.cos(-theta); s = Math.sin(-theta);
    X = c*x - s*y; Y = c*y + s*x;
    ballVX = c*ball.vx - s*ball.vy;
    ballVY = c*ball.vy + s*ball.vx;
    thisVX = c*this.vx - s*this.vy;
    thisVY = c*this.vy + s*this.vx;
    if (X > 0) {
      ball.vX =
        (ballVX*(ball.mass - this.mass) + 2*this.mass*thisVX)/totalMass;
      this.vX =
        (thisVX*(this.mass - ball.mass) + 2*ball.mass*ballVX)/totalMass;
      ball.vx = c*ball.vX + s*ballVY;
      ball.vy = c*ballVY - s*ball.vX;
      this.vx = c*this.vX + s*thisVY;
      this.vy = c*thisVY - s*this.vX;
      X = 0;
      x = c*X + s*Y;  y = c*Y - s*X;
      ball.x = x + this.x + this.L1/2;
      ball.y = y + this.y - this.height/2;
    }
  };
}
Output

This bin was created anonymously and its free preview time has expired (learn why). — Get a free unrestricted account

Dismiss x
public
Bin info
anonymouspro
0viewers