xxxxxxxxxx
// This is a simplification of a real encounter of a bug in p5's reflect()
// https://github.com/processing/p5.js/issues/7088
// In it, the calculation of the repositioning for ball bounce is
// thrown off by the alteration of the passed surface normal by p5's reflect function.
let balls;
function setup() {
createCanvas(windowWidth, windowHeight);
createBalls()
}
function draw() {
background(100);
for (const ball of balls) {
drawBall(ball)
}
for (const ball of balls) {
updateBall(ball);
}
}
function createBalls() {
balls = [{
pos: createVector(200, 200),
radius: 200,
vel: createVector(10, 2)
},
{
pos: createVector(850, 250),
radius: 100,
vel: createVector(-4, 0)
}
];
}
function drawBall(ball) {
push()
translate(ball.pos)
circle(0, 0, ball.radius * 2);
pop()
}
function updateBall(ball) {
ball.pos.add(ball.vel);
for (const otherBall of balls) {
if (otherBall === ball) {
continue;
}
if (otherBall.pos.dist(ball.pos) < ball.radius + otherBall.radius) {
bounceOff(ball, otherBall)
}
}
}
/**
* Bounces ballA off ballB, changing ballA's course and repositioning ballA to stop intersection.
* Doesn't modify ballB.
*/
function bounceOff(ballA, ballB) {
//Adjust course
const normalizedBToA = p5.Vector.sub(ballA.pos, ballB.pos).normalize();
ballA.vel.reflect(normalizedBToA);
// The behaviour can be fixed by replacing the above as follows:
// ballA.vel.reflect(normalizedBToA.copy());
// This will work around the bug in reflect(), which mutates this normal.
//Reposition out to touching distance, ceasing intersection
const touchingDistance = ballA.radius + ballB.radius;
const offsetPosition = normalizedBToA.mult(touchingDistance);
const myNewPos = p5.Vector.add(ballB.pos, offsetPosition);
ballA.pos.set(myNewPos);
}