{
replay;
const width = 600,
height = 200,
centerX = width / 2,
centerY = height / 2,
attractor = object({
mass: 20,
G: 1,
location: cm.vec(centerX, centerY)
}),
mover = object({
location: cm.vec(centerX, centerY - 50),
velocity: cm.vec(1, 0),
acceleration: cm.vec(),
mass: 5
}),
applyAttraction = attraction(attractor);
function update(app) {
app.append(cm.clear, { fill: cm.rgb(255) });
app
.datum(mover)
.process(cm.each, applyAttraction)
.process(cm.each, move)
.append(cm.circle, {
x: (d) => d.location.x,
y: (d) => d.location.y,
r: (d) => d.mass,
fill: cm.rgb(175),
stroke: "#000",
strokeWidth: 2
});
app
.datum(attractor)
.append(cm.circle, {
x: (d) => d.location.x,
y: (d) => d.location.y,
r: (d) => d.mass,
fill: cm.rgb(175),
stroke: "#000",
strokeWidth: 5
});
}
function dispose(app) {
invalidation.then(() => app.dispose());
}
function frame(app) {
app.node().style.border = "solid #000 1px";
}
return cm
.app({ width, height })
.on("update", update)
.call(dispose)
.call(frame)
.start()
.node();
}