Skip to content

Instantly share code, notes, and snippets.

@gigamonkey
Last active March 19, 2024 23:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gigamonkey/3837a7af6787d3e4e07b4e2ba1647f56 to your computer and use it in GitHub Desktop.
Save gigamonkey/3837a7af6787d3e4e07b4e2ba1647f56 to your computer and use it in GitHub Desktop.
Bouncing ball utility functions
// Some utility functions
const distance = (a, b) => Math.abs(a - b);
const distance2d = (p1, p2) => Math.hypot(p1.x - p2.x, p1.y - p2.y);
const clamp = (n, min, max) => (n < min ? min : n > max ? max : n);
const nextPos = (b) => ({ x: b.x + b.dx, y: b.y + b.dy });
const mass = (b) => Math.PI * b.size ** 2;
/*
* Update the velocity of two colliding balls.
*/
const collide = (b1, b2, elasticity) => {
// Get the vector between the centers of the two balls
const axis = { x: b1.x - b2.x, y: b1.y - b2.y };
// Get the distance
const dist = Math.hypot(axis.x, axis.y);
// This vector is normalized, i.e. has magnitude 1
const collisionNormal = { x: axis.x / dist, y: axis.y / dist };
// Relative velocity between the two balls as a vector
const rv = { x: b1.dx - b2.dx, y: b1.dy - b2.dy };
// The dot product of the relative velocity and the collisionNormal. This is
// basically the first half of the secret sauce.
const dot = rv.x * collisionNormal.x + rv.y * collisionNormal.y;
// How much to scale up both the x and y components of the collisionNormal
// vector to get the magnitude of the acceleration in each direction. Takes
// into account the elasticity of the collision. (1 is perfect elasticity.)
const j = (-(elasticity + 1) * dot) / (1 / mass(b1) + 1 / mass(b2));
// Collision normal vector scaled by j
const scaled = { x: collisionNormal.x * j, y: collisionNormal.y * j };
// Accelerate both balls in opposite directions. Because we got the axis by
// substracting b2 from b1 we add to b1 and subtract from b2
b1.dx += scaled.x / mass(b1);
b1.dy += scaled.y / mass(b1);
b2.dx -= scaled.x / mass(b2);
b2.dy -= scaled.y / mass(b2);
};
/*
* Are two moving points approaching each other. (Points must have x, y, dx and
* dy properties.
*/
const approaching = (p1, p2) => {
// Get the displacement vector from p1 to p2
const displacement = { x: p2.x - p1.x, y: p2.y - p1.y };
// Relative velocity of p1 toward p2 (if p2 was stationary this would just be
// p1's velocity)
const rv = { x: p1.dx - p2.dx, y: p1.dy - p2.dy };
// The dot product gives us a measure of how aligned the two vectors are.
const dot = rv.x * displacement.x + rv.y * displacement.y;
// If the dot product is positive the vectors are pointing in roughly the same
// direction; when it is negative they are pointing in away from each other;
// and when it is 0 they are parallel.
return dot > 0;
};
/*
* From a plain point (just x and y) return a fixed point, that can be used with collide.
*/
const fixedPoint = (p) => {
return { ...p, dx: 0, dy: 0, size: Infinity }
};
/*
* Find the point on a line that is closest to the the center of the ball.
* Ball's center should be in its x and y properties and the line should have
* two properties p1 and p2 representing the points (each with an x and y) at
* the ends of the line.
*/
const closestPointOnLine = (ball, line) => {
const { p1, p2 } = line;
const n = (ball.x - p1.x) * (p2.x - p1.x) + (ball.y - p1.y) * (p2.y - p1.y);
const d = (p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2;
const s = n / d;
if (s < 0) {
return p1;
} else if (s > 1) {
return p2;
} else {
return {
x: p1.x + (p2.x - p1.x) * s,
y: p1.y + (p2.y - p1.y) * s,
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment