Skip to content

๐Ÿš€ Getting started

Any simulation is structured as follows:

A physics model with properties that have physical meaning only:
Section titled โ€œA physics model with properties that have physical meaning only:โ€
Body
โ”œโ”€โ”€ position
โ”œโ”€โ”€ velocity
โ”œโ”€โ”€ acceleration
โ”œโ”€โ”€ mass
โ”œโ”€โ”€ charge
โ”œโ”€โ”€ force
โ””โ”€โ”€ fixed
Block
โ”œโ”€โ”€ size
โ””โ”€โ”€ orientation
RadialSymmetricBody
โ””โ”€โ”€ radius
Sphere / Box / Cylinder / Ring / Arrow โ†’ position + radius/size/axis (Body, RadialSymmetricBody, AxialSymmetricBody, Block)
VectorView / Trail / Label โ†’ willekeurige Vec3 + body.position (vector, trail, label)
SwitchableBondView / DiatomicMolecule โ†’ BodyPair (breedte switchbaar via SwitchableBondView.Type.Spring/Cylinder)
ParticleView2D / PointCloudView โ†’ 2D/3D deeltjes (position + radius/size + colorFunction/hasBorder voor 2D)
TiledPlane / DiscreteFieldSurfaceView โ†’ DiscreteScalarField (via colorMapper/normalizer)
Zichtbaarheid โ†’ model.visible (bijv. get visible(){return index < n}) โ€” houdt Binding.synchronize actief, i.p.v. view.visible
simulation.bind(ball.alwaysWith(new Sphere(...)));
simulation.bind(ball.alwaysWith(new VectorView({
vector: body => body.velocity,
color: "cyan"
})));
simulation.bind(ball.alwaysWith(new VectorView({
vector: body => body.force,
color: "red"
})));

As an example, letโ€™s code the three-body problem. First, we start by coding the physics involved:

const astronomical_unit = 1.49e11;
const mass = 1e30;
const radiusA = 0.1 * astronomical_unit;
const radiusB = radiusA / 0.8;
const velocityA = Math.sqrt(G * 0.8 * mass * radiusA) / (radiusA + radiusB);
const radius = 1.9e9;
const bodyA = new RadialSymmetricBody({
position: new Vec3(radiusA, 0, 0),
velocity: new Vec3(0, velocityA, 0),
radius,
mass
});
const bodyB = new RadialSymmetricBody({
position: new Vec3(-radiusB, 0, 0),
velocity: new Vec3(0, -velocityA / 0.8, 0),
radius,
mass: mass * 0.8
});
const bodyC = new RadialSymmetricBody({
position: new Vec3(0, 0, radiusA),
velocity: new Vec3(0, 0, 0),
radius,
mass: mass * 0.5
});
function updateForces(dt) {
bodyA.and(bodyB).apply(gravitationalForce);
bodyA.and(bodyC).apply(gravitationalForce);
bodyB.and(bodyC).apply(gravitationalForce);
bodyA.integrate(10, Integrators.symplecticEulerStep);
bodyB.integrate(10, Integrators.symplecticEulerStep);
bodyC.integrate(10, Integrators.symplecticEulerStep);
}

In the simulation, we synchronize the bodies with the view: spheres that leave a trail behind:

const dt = 5000;
const subSteps = 50;
Simulation
.with({
htmlDivId: "threeBodyContainer",
cameraPosition: new Vec3(30, 30, 30),
scale: 1e-9,
headUpDisplay: true
})
.bind(bodyA.alwaysWith(new Sphere({color: "yellow"})))
.bind(bodyA.alwaysWith(new Trail({maxPoints: 500, color: "yellow"})))
.bind(bodyB.alwaysWith(new Sphere({color: "cyan"})))
.bind(bodyB.alwaysWith(new Trail({maxPoints: 500, color: "cyan"})))
.bind(bodyC.alwaysWith(new Sphere({color: "magenta"})))
.bind(bodyC.alwaysWith(new Trail({maxPoints: 500, color: "magenta"})))
.runsEvery(dt / subSteps)
.onClockTick((clockTime, simulatedTime) => updateForces(dt), subSteps)
.withMouseClickEventListener();