Last active
March 19, 2024 23:28
-
-
Save gigamonkey/3837a7af6787d3e4e07b4e2ba1647f56 to your computer and use it in GitHub Desktop.
Bouncing ball utility functions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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