Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this ball inside a circle not bouncing properly?

Please see this Fiddle: https://jsfiddle.net/sfarbota/wd5aa1wv/2/

I am trying to make the ball bounce inside the circle at the correct angles without losing speed. I think I have the collision detection down, but I am facing 2 issues:

  1. The ball slows down with each bounce, until eventually stopping.
  2. The angles at which it bounces appear to be incorrect.

This is partially based off of the answer given here: https://stackoverflow.com/a/12053397/170309 but I had to translate from Java and also skipped a few lines from their example that seemed irrelevant.

Here is the code:

JavaScript:

function getBall(xVal, yVal, dxVal, dyVal, rVal, colorVal) {
  var ball = {
    x: xVal,
    lastX: xVal,
    y: yVal,
    lastY: yVal,
    dx: dxVal,
    dy: dyVal,
    r: rVal,
    color: colorVal,
    normX: 0,
    normY: 0
  };

  return ball;
}

var canvas = document.getElementById("myCanvas");
var xLabel = document.getElementById("x");
var yLabel = document.getElementById("y");
var dxLabel = document.getElementById("dx");
var dyLabel = document.getElementById("dy");
var vLabel = document.getElementById("v");
var normXLabel = document.getElementById("normX");
var normYLabel = document.getElementById("normY");

var ctx = canvas.getContext("2d");

var containerR = 200;
canvas.width = containerR * 2;
canvas.height = containerR * 2;
canvas.style["border-radius"] = containerR + "px";

var balls = [
  //getBall(canvas.width / 2, canvas.height - 30, 2, -2, 20, "#0095DD"),
  //getBall(canvas.width / 3, canvas.height - 50, 3, -3, 30, "#DD9500"),
  //getBall(canvas.width / 4, canvas.height - 60, -3, 4, 10, "#00DD95"),
  getBall(canvas.width / 2, canvas.height / 5, -1.5, 3, 40, "#DD0095")
];

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  for (var i = 0; i < balls.length; i++) {
    var curBall = balls[i];
    ctx.beginPath();
    ctx.arc(curBall.x, curBall.y, curBall.r, 0, Math.PI * 2);
    ctx.fillStyle = curBall.color;
    ctx.fill();
    ctx.closePath();
    curBall.lastX = curBall.x;
    curBall.lastY = curBall.y;
    curBall.x += curBall.dx;
    curBall.y += curBall.dy;
    if (containerR <= curBall.r + Math.sqrt(Math.pow(curBall.x - containerR, 2) + Math.pow(curBall.y - containerR, 2))) {
      curBall.normX = (curBall.x + curBall.r) - (containerR);
      curBall.normY = (curBall.y + curBall.r) - (containerR);
      var normD = Math.sqrt(Math.pow(curBall.x, 2) + Math.pow(curBall.y, 2));
      if (normD == 0)
        normD = 1;
      curBall.normX /= normD;
      curBall.normY /= normD;
      var dotProduct = (curBall.dx * curBall.normX) + (curBall.dy * curBall.normY);
      curBall.dx = -2 * dotProduct * curBall.normX;
      curBall.dy = -2 * dotProduct * curBall.normY;
    }

    xLabel.innerText = "x: " + curBall.x;
    yLabel.innerText = "y: " + curBall.y;
    dxLabel.innerText = "dx: " + curBall.dx;
    dyLabel.innerText = "dy: " + curBall.dy;
    vLabel.innerText = "v: " + curBall.dy / curBall.dx;
    normXLabel.innerText = "normX: " + curBall.normX;
    normYLabel.innerText = "normY: " + curBall.normY;
  }
}

setInterval(draw, 10);

HTML:

<canvas id="myCanvas"></canvas>
<div id="x"></div>
<div id="y"></div>
<div id="dx"></div>
<div id="dy"></div>
<div id="v"></div>
<div id="normX"></div>
<div id="normY"></div>

CSS:

canvas { background: #eee; }
like image 281
sfarbota Avatar asked Feb 07 '16 22:02

sfarbota


People also ask

What makes a ball not bounce?

The ball that doesn't bounce is made of a special kind of rubber, called butyl rubber. Butyl rubber is synthetic, or manmade, rubber that absorbs the kinetic energy from the ball falling. Butyl rubber used in athletic shoes can help absorb the impact of your feet as they hit the ground.

Why do balls bounce differently on different surfaces?

A hard surface, like concrete or hardwood, hardly absorbs any, so most of the kinetic energy of the fall goes into bouncing the ball back up. A soft surface, like grass or carpet, absorbs more energy from the fall, so there's less left to push the ball back up, and it bounces pretty badly.

Why does the bouncy ball not bounce all the way up?

During a collision, some of the ball's energy is converted into heat. As no energy is added to the ball, the ball bounces back with less kinetic energy and cannot reach quite the same height.

Why does a ball change shape when it bounces?

The Physics of a Bouncing Ball While the ball is on the move, kinetic energy--the energy of motion--is at work. As the energy-driven ball hits the floor, the physical forces in play flatten and deform the shape of the ball, dispersing and compressing the molecules that make up the ball.


1 Answers

My math is rusty, so I'm not quite sure how you could compute the new trajectory of the ball using just a dot product, but I'm sure you can compute it with the relevant trig functions: use atan2 to compute the angle to the collision point and the current trajectory angle, use those two to compute the new angle, and a pair of sin and cos multiplied by the speed to get the new x/y speeds.

jsFiddle: https://jsfiddle.net/jacquesc/wd5aa1wv/6/

The important part is:

    var dx = curBall.x - containerR;
    var dy = curBall.y - containerR;
    if (Math.sqrt(dx * dx + dy * dy) >= containerR - curBall.r) {
      // current speed
      var v = Math.sqrt(curBall.dx * curBall.dx + curBall.dy * curBall.dy);
      // Angle from center of large circle to center of small circle,
      // which is the same as angle from center of large cercle
      // to the collision point
      var angleToCollisionPoint = Math.atan2(-dy, dx);
      // Angle of the current movement
      var oldAngle = Math.atan2(-curBall.dy, curBall.dx);
      // New angle
      var newAngle = 2 * angleToCollisionPoint - oldAngle;
      // new x/y speeds, using current speed and new angle
      curBall.dx = -v * Math.cos(newAngle);
      curBall.dy = v * Math.sin(newAngle);
    }

Also note I switched from setInterval to requestAnimationFrame, which will make sure there's no more than one update per frame. Ideally you would want to compute the movement based on the actual time elapsed since the last update rather than rely on it being always the same.

Update

Using dot products:

jsFiddle: https://jsfiddle.net/jacquesc/wd5aa1wv/9/

    var dx = curBall.x - containerR;
    var dy = curBall.y - containerR;
    var distanceFromCenter = Math.sqrt(dx * dx + dy * dy);

    if (distanceFromCenter >= containerR - curBall.r) {
      var normalMagnitude = distanceFromCenter;
      var normalX = dx / normalMagnitude;
      var normalY = dy / normalMagnitude;
      var tangentX = -normalY;
      var tangentY = normalX;
      var normalSpeed = -(normalX * curBall.dx + normalY * curBall.dy);
      var tangentSpeed = tangentX * curBall.dx + tangentY * curBall.dy;
      curBall.dx = normalSpeed * normalX + tangentSpeed * tangentX;
      curBall.dy = normalSpeed * normalY + tangentSpeed * tangentY;
    }
like image 155
jcaron Avatar answered Sep 23 '22 10:09

jcaron