Published
Edited
Jun 14, 2022
6 forks
8 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof chart = {
const engine = Matter.Engine.create({
positionIterations: 10,
constraintIterations: 10
});

const world = engine.world;
world.gravity.y = 0;
Matter.World.add(world, initialBodies);

const pathFunc = d3
.line()
.x((d) => d.x)
.y((d) => d.y);

const destination = {
x: width / 2,
y: height / 2
};
const strength = 0.03;
const velocityDecay = 0.2;

Matter.Events.on(engine, "beforeUpdate", () => {
Matter.Composite.allBodies(world).forEach((body) => {
const { position } = body;
const distance = Matter.Vector.sub(destination, position);
body._vx = ((body._vx || 0) + distance.x * xStrength) * velocityDecay;
body._vy = ((body._vY || 0) + distance.y * yStrength) * velocityDecay;
Matter.Body.setVelocity(body, {
x: body._vx,
y: body._vy
});
let angle = radToDeg(body.angle);
angle = Math.max(-maxAngle, angle);
angle = Math.min(maxAngle, angle);
Matter.Body.setAngle(body, degToRad(angle));
});
});

let tick = 0;
let running = true;
window.stop = () => {
running = false;
};
while (running) {
tick++;
Matter.Engine.update(engine, 1000 / 60);
const bodies = Matter.Composite.allBodies(world);
const paths = bodies.map((body, index) => {
const { vertices, position, angle } = body;
const pathData = `${pathFunc(vertices)}z`;
const style = `fill: black; fill-opacity: 0.03; stroke: black; stroke-width: 1px; stroke-opacity: 0.7`;
const degrees = radToDeg(angle);
const transform = `translate(${position.x}, ${position.y}) rotate(${degrees})`;
const textStyle = `font-size: 0.8rem; font-family: sans-serif; alignment-baseline: middle; text-anchor: middle;`;
let path = null;
if (showOvals) {
path = `<path d="${pathData}" style="${style}"></path>`;
}
return `
<g>
${path}
<text transform="${transform}" style="${textStyle}">${body.name}</text>
</g>
`;
});
const style = `height: ${height}px; width: ${width}px; shape-rendering: geometricPrecision;`;
yield html`<svg style="${style}">${paths}</svg>`;
}
}
Insert cell
initialBodies = {
const randScale = d3.scaleLinear().range([-200, 200]);
const initialBodies = names.map((name) => {
const x = width / 2 + randScale(Math.random());
const y = height / 2 + randScale(Math.random());
const options = {
isSensor: false,
friction: 0
};
const body = Matter.Bodies.circle(x, y, 20, options);
Matter.Body.scale(body, 4, 0.7);
body.name = name;
return body;
});
return initialBodies;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more