{
replay;
const width = 600,
height = 200,
applyGravity = force(cm.vec(0, 0.2)),
applyFriction = force(cm.vec(0.02, 0)),
movers = cm.range(25).map(() =>
object({
location: cm.vec(),
velocity: cm.vec(),
acceleration: cm.vec(),
mass: cm.random(0.1, 5)
})
);
function update(app) {
app.append(cm.clear, { fill: cm.rgb(255) });
app
.data(movers)
.process(cm.each, applyFriction)
.process(cm.each, applyGravity)
.process(cm.each, move)
.process(cm.each, collision)
.append(cm.circle, {
x: (d) => d.location.x,
y: (d) => d.location.y,
fill: "rgba(175, 175, 175, 0.5)",
stroke: cm.rgb(0),
r: (d) => d.mass
})
.transform(cm.mapAttrs, {
r: { scale: cm.scaleSqrt, range: [2, 20] }
});
}
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();
}