Skip to content

Instantly share code, notes, and snippets.

@ds604
Created November 13, 2018 12:02
Show Gist options
  • Save ds604/9c57fd37aa15700e9b7963880d7e8f60 to your computer and use it in GitHub Desktop.
Save ds604/9c57fd37aa15700e9b7963880d7e8f60 to your computer and use it in GitHub Desktop.
Bouncing Ball
interface Numeric<T> {
plus(other: T): T
times(scalar: number): T
}
function runSimulation<T extends Numeric<T>>(
y0: T,
f: (t: number, y: T) => T,
applyConstraints: (y: T) => T,
iterationsPerFrame: number,
render: (y: T) => void
) {
const frameTime = 1 / 60.0
const h = frameTime / iterationsPerFrame
function simulationStep(yi: T, ti: number) {
render(yi)
requestAnimationFrame(function () {
for (let i = 0; i < iterationsPerFrame; i++) {
yi = yi.plus(f(ti, yi).times(h))
yi = applyConstraints(yi)
ti = ti + h
}
simulationStep(yi, ti)
})
}
simulationStep(y0, 0.0)
}
const canvas = document.createElement("canvas")
canvas.width = 400;
canvas.height = 400;
const ctx = canvas.getContext("2d")!;
document.body.appendChild(canvas);
ctx.fillStyle = "rgba(0, 0, 0, 0, 1)"
ctx.fillRect(0, 0, 400, 400);
const g = -9.8; // m / s^2
const r = 0.2; // m
class Ball implements Numeric<Ball> {
constructor(readonly x: number, readonly v: number) { }
plus(other: Ball) { return new Ball(this.x + other.x, this.v + other.v) }
times(scalar: number) { return new Ball(this.x * scalar, this.v * scalar) }
}
function f(t: number, y: Ball) {
const { x, v } = y
return new Ball(v, g)
}
function applyConstraints(y: Ball): Ball {
const { x, v } = y
if (x <= 0 && v < 0) {
return new Ball(x, -v)
}
return y
}
const y0 = new Ball(
/* x */ 0.8,
/* v */ 0
)
function render(y: Ball) {
ctx.clearRect(0, 0, 400, 400)
ctx.fillStyle = '#EB5757'
ctx.beginPath()
ctx.ellipse(200, 400 - ((y.x + r) * 300), r * 300, r * 300, 0, 0, 2 * Math.PI)
ctx.fill()
}
runSimulation(y0, f, applyConstraints, 30, render)
canvas {
border: 1px solid black;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment