The program is a variation based on lesson 3 of "10 minutes physics" by Matthias Müller.
That lesson is a simple billard where balls interact and bounce off one another. See demo here of the original (unmodified) code.
The modifications include using SVG, and the possibility to add balls (by clicking on the page) and change them into fixed obstacles (by clicking on them); it's also possible to pause the simulation, which makes it easier to catch a moving ball.
It works generally well, but in some cases a moving ball becomes "trapped" by a fixed one, and can't escape.
See the JSFiddle here; when one clicks "run" on the fiddle the red ball moves a little and then becomes immediately trapped by the black ball. (It happens naturally; the fiddle is just a setup to reproduce the bug systematically. On the fiddle it's also possible to add more balls by clicking anywhere.)
The collision code is as follows (line 157 in the fiddle):
function handleBallCollision(ball1, ball2, restitution) {
const pushVel = 2.0;
const dir = new Vector2();
dir.subtractVectors(ball2.pos, ball1.pos);
const d = dir.length();
const rsum = ball1.radius ball2.radius;
if (d == 0.0 || d > rsum) return;
dir.scale(1.0 / d);
let corr = (ball1.radius ball2.radius - d) / 2.0;
if (ball1.isMoving == 0 || ball2.isMoving == 0) {
const ball = ball1.isMoving == 0 ? ball2 : ball1;
ball.pos.add(dir, corr);
var v = ball.vel.dot(dir);
ball.vel.add(dir, pushVel - v);
}
// else handle case of two balls moving...
The general principle is that we test the proximity of two balls; if their distance (d
) is smaller than the sum of their radii, they are in contact and should move apart.
Of course if one of the balls is fixed then only the other moves.
Once two balls have been in contact, their distance should start growing again; in the case of the bug, the distance keeps getting smaller.
This has something to do with the dir
and corr
vectors but I don't understand why it works in the general case, and sometimes fail.
CodePudding user response:
There are some issues with the signs in line 177 and 180. Hence the above portion should read as follows. A working fiddle can be found here: https://jsfiddle.net/x47fv9te/
function handleBallCollision(ball1, ball2, restitution) {
const pushVel = 2.0;
const dir = new Vector2();
dir.subtractVectors(ball2.pos, ball1.pos);
const d = dir.length();
const rsum = ball1.radius ball2.radius;
if (d == 0.0 || d > rsum) return;
dir.scale(1.0 / d);
let corr = (ball1.radius ball2.radius - d) / 2.0;
if (ball1.isMoving == 0 || ball2.isMoving == 0) {
const ball = ball1.isMoving == 0 ? ball2 : ball1;
ball.pos.add(dir, -corr); // minus added
var v = ball.vel.dot(dir);
ball.vel.add(dir, -pushVel - v); // minus added
}
// else handle case of two balls moving...