Skip to content

Falling Polygons, Matter.js ​

Polygons falling on two ledges. Matter.js simulates the rigid bodies, and Charming draws their vertices as polygons and the ledges as rotated rects.

js
const replay = Inputs.button("Replay");
js
replay;

const width = 640;
const height = 240;
const engine = Matter.Engine.create();
const polygons = [];
const ledges = [
  createLedge(width / 4, height - 5, width / 2 - 50, 10),
  createLedge((3 * width) / 4, height - 50, width / 2 - 50, 10),
];
const renderer = cm.canvas({width, height});
const root = cm.flow().renderer(renderer);

function createLedge(x, y, width, height) {
  const body = Matter.Bodies.rectangle(x, y, width, height, {isStatic: true});
  Matter.Composite.add(engine.world, body);
  return {body, width, height};
}

function createPolygon(x, y) {
  const vertices = [
    {x: -10, y: -10},
    {x: 20, y: -15},
    {x: 15, y: 0},
    {x: 0, y: 10},
    {x: -20, y: 15},
  ];
  const body = Matter.Bodies.fromVertices(x, y, vertices, {restitution: 0.2});
  Matter.Body.setVelocity(body, {x: Math.random() * 10 - 5, y: 0});
  Matter.Body.setAngularVelocity(body, 0.1);
  Matter.Composite.add(engine.world, body);
  return {body};
}

// Remove the polygons that fell off the canvas.
function removeFallen(d, i, data) {
  if (d.body.bounds.min.y < height) return;
  data.splice(i, 1);
  Matter.Composite.remove(engine.world, d.body);
}

requestAnimationFrame(function frame() {
  if (Math.random() < 0.1) polygons.push(createPolygon(width / 2, 50));
  Matter.Engine.update(engine);

  root.append(cm.clear, {fill: "white"});
  root
    .data(polygons)
    .process(cm.eachRight, removeFallen)
    .append(cm.polygon, {
      x: (d) => d.body.vertices.map((v) => v.x),
      y: (d) => d.body.vertices.map((v) => v.y),
      fill: "rgb(127, 127, 127)",
      stroke: "black",
      strokeWidth: 2,
    });
  root.data(ledges).append(cm.rect, {
    x: (d) => d.body.position.x,
    y: (d) => d.body.position.y,
    width: (d) => d.width,
    height: (d) => d.height,
    rotate: (d) => d.body.angle,
    anchor: "center",
    fill: "rgb(127, 127, 127)",
    stroke: "black",
    strokeWidth: 2,
  });

  root.render();
  requestAnimationFrame(frame);
});

document.body.append(renderer.node());