A Pen By
Jamie Wong

` ````
canvas {
border: 1px solid black;
}
```

` ````
class Vec2 {
constructor(readonly x: number = 0, readonly y: number = 0) { }
plus(other: Vec2): Vec2 { return new Vec2(this.x + other.x, this.y + other.y); }
times(scalar: number): Vec2 { return new Vec2(this.x * scalar, this.y * scalar); }
minus(other: Vec2): Vec2 { return new Vec2(this.x - other.x, this.y - other.y); }
length2(): number { return (this.x * this.x + this.y * this.y); }
length(): number { return Math.sqrt(this.length2()) }
}
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,
render: (y: T) => void
) {
const h = (24 * 60 * 60) * 1 / 60.0
function simulationStep(yi: T, ti: number) {
render(yi)
requestAnimationFrame(function() {
// t_{i+1} = t_i + h
const tNext = ti + h
// y_{i+1} = y_i + h f(t_i, y_i)
const yNext = yi.plus(f(ti, yi).times(h))
simulationStep(yNext, tNext)
})
}
simulationStep(y0, 0.0)
}
class TwoParticles implements Numeric<TwoParticles> {
constructor(
readonly x1: Vec2,
readonly v1: Vec2,
readonly x2: Vec2,
readonly v2: Vec2
) { }
plus(other: TwoParticles) {
return new TwoParticles(
this.x1.plus(other.x1),
this.v1.plus(other.v1),
this.x2.plus(other.x2),
this.v2.plus(other.v2)
);
}
times(scalar: number) {
return new TwoParticles(
this.x1.times(scalar),
this.v1.times(scalar),
this.x2.times(scalar),
this.v2.times(scalar)
)
}
}
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);
function render(y: TwoParticles) {
const { x1, x2 } = y;
ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
ctx.fillRect(0, 0, 400, 400);
const rEarth = 6.371e6/1e9;
const rMoon = 1.73e6/1e9;
ctx.fillStyle = "rgba(45, 66, 143, 1)";
ctx.beginPath();
ctx.ellipse((x1.x/1e9)*400 + 200, (x1.y/1e9)*400 + 200, rEarth*400*10, rEarth*400*10, 0, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle = "rgba(189, 189, 189, 1)";
ctx.beginPath();
ctx.ellipse((x2.x/1e9)*400 + 200, (x2.y/1e9)*400 + 200, rMoon*400*10, rMoon*400*10, 0, 0, 2 * Math.PI);
ctx.fill();
}
const G = 6.67e-11;
const m1 = 5.972e24;
const m2 = 7.34e22;
function f(t: number, y: TwoParticles) {
const { x1, v1, x2, v2 } = y;
return new TwoParticles(
// dx1/dt = v1
v1,
// dv1/dt = G*m2*(x2-x1)/|x2-x1|^3
x2.minus(x1).times(G * m2 / Math.pow(x2.minus(x1).length(), 3)),
// dx2/dt = v2
v2,
// dv2/dt = G*m1*(x1-x1)/|x1-x2|^3
x1.minus(x2).times(G * m1 / Math.pow(x1.minus(x2).length(), 3))
)
}
const y0 = new TwoParticles(
/* x1 */ new Vec2(0, 0),
/* v1 */ new Vec2(0, -13.22),
/* x2 */ new Vec2(3.6e8, 0),
/* v2 */ new Vec2(0, 1.076e3)
)
runSimulation(y0, f, render)
```

999px

Loading
..................

Alt F
Opt F
Find & Replace

Also see: Tab Triggers