Add a constraint from a point A to another constraint in Matter.js

66 Views Asked by At

let Engine = Matter.Engine,
  Render = Matter.Render,
  Runner = Matter.Runner,
  World = Matter.World,
  Bodies = Matter.Bodies,
  Constraint = Matter.Constraint;

let engine = Engine.create(),
  world = engine.world;

let render = Render.create({
  element: document.body,
  engine: engine,
  options: {
    width: 800,
    height: 600,
    wireframes: false,
    background: "#fafafa",
  },
});

let base = Bodies.rectangle(400, 100, 350, 20, {
  isStatic: true,
  render: { fillStyle: "#2e3276" },
});

let side_base = Bodies.rectangle(280, 280, 20, 350, {
  isStatic: true,
  render: { fillStyle: "#2e3276" },
});

let ball = Bodies.circle(800, 300, 20, {
  density: 0.04,
  frictionAir: 0.005,
  restitution: 0.8,
  friction: 0.01,
  render: { fillStyle: "#4CAF50" },
});

let pendulum = Constraint.create({
  pointA: { x: 400, y: 100 },
  bodyB: ball,
  render: { strokeStyle: "#000000" },
  length: 170,
});

let spring = Constraint.create({
  pointA: { x: 280, y: 300 },
  bodyB: ball,
  stiffness: 0.0001,
  render: { strokeStyle: "#000000" },
});

World.add(world, [base, side_base, ball, pendulum, spring]);
Runner.run(engine);
Render.run(render);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/matter.min.js"></script>

Example, first, I have a rectangle and a circle. I created a contraint from rectangle to circle and it worked and it's like a Pendulum. Now I need to create a new constraint from another rectangle to the constraint before created, but it doesn't work. Do you have any idea about the problem? Thanks. This problem was made in matter.js

pendulum with a spring

Example

It's like that, but I need the spring in the line, not in the circle.

1

There are 1 best solutions below

1
ggorlen On BEST ANSWER

If I understand correctly, it sounds like the goal is for the constraint K to remain horizontal and move up and down line L, connecting at whatever x point K and L intersect.

One approach is to compute the x-intercept of L, then position an invisible, non-colliding body there on every frame. The invisible body will be constraint K's endpoint.

const engine = Matter.Engine.create();

const render = Matter.Render.create({
  engine,
  element: document.body,
  options: {
    width: 800,
    height: 600,
    wireframes: false,
    background: "#fafafa",
  },
});

const base = Matter.Bodies.rectangle(400, 100, 350, 20, {
  isStatic: true,
  render: {fillStyle: "#2e3276"},
});

const sideBase = Matter.Bodies.rectangle(280, 280, 20, 350, {
  isStatic: true,
  render: {fillStyle: "#2e3276"},
});

const ball = Matter.Bodies.circle(300, 300, 20, {
  density: 0.04,
  frictionAir: 0.005,
  restitution: 0.8,
  friction: 0.01,
  render: {fillStyle: "#4CAF50"},
});

const pendulum = Matter.Constraint.create({
  pointA: {x: 400, y: 100},
  bodyB: ball,
  render: {strokeStyle: "#000000"},
  length: 170,
});

const lineAnchor = Matter.Bodies.circle(0, 0, 0, {
  isStatic: true,
  isSensor: true,
});

const spring = Matter.Constraint.create({
  pointA: {x: 280, y: 200},
  bodyB: lineAnchor,
  stiffness: 0.0001,
  render: {strokeStyle: "#000000"},
});

Matter.Composite.add(engine.world, [
  base,
  sideBase,
  ball,
  pendulum,
  spring,
  lineAnchor,
]);
Matter.Runner.run(engine);
Matter.Render.run(render);

const findXCoordinate = (x1, y1, x2, y2, y) => {
  // slope
  const m = (y2 - y1) / (x2 - x1);

  // y-intercept (b)
  const b = y1 - m * x1;

  // x-coordinate when the line passes through y
  const x = (y - b) / m;
  return x;
};

Matter.Events.on(engine, "afterUpdate", () => {
  const {x: x1, y: y1} = ball.position;
  const {x: x2, y: y2} = pendulum.pointA;
  const {y} = spring.pointA;
  const x = findXCoordinate(x1, y1, x2, y2, y);
  Matter.Body.setPosition(lineAnchor, {x, y});
});
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/matter.min.js"></script>

The (possible) problem here is that the constraint K doesn't exert any force on the pendulum line L because setPosition overrides physics. This seems to be the desired behavior, given that the constraint's stiffness is so low.

If this isn't the intent, it seems a bit contrary to the constraint being purely horizontal--I would pick a fixed point on the line and tie the constraint to it, probably splitting the line into two parts joined by a body. This way, if the ball is pulled hard by the horizontal constraint, the bottom part below the connecting point will droop naturally (or, make the bottom body attached on a rigid constraint if that's not desired).

Another option is to keep your original design with a constraint linked to the ball, but make this constraint invisible. Then use my horizontal constraint above to add the desired visible effect. Ultimately, the behavior won't be physically perfect, since the L constraint will be pulling on the ball rather than the chain, but may be close enough to create the necessary illusion for your needs.