Matter-js: Angle constraints

Created on 18 Mar 2015  路  18Comments  路  Source: liabru/matter-js

Distance constraints are really nice, how about angle constraints?

That would make it possible to remove those two "diagonal constraints" from this example: http://jsbin.com/risahi/4/edit

feature-request

Most helpful comment

This is definitely on my list, I'll keep this thread updated, thanks!

All 18 comments

This is definitely on my list, I'll keep this thread updated, thanks!

+1

+1

+1

+1

What are/were your thoughts on the best way to implement this @liabru? I really need this and would be willing to help.

+1

+1111111111

+1

+1

@liabru Is there any progress on this one? I need this for my project so much that I'm looking into hacking it in myself. But, I'm concerned I might not figure out how to do it in a way that conserves momentum, etc.

FWIW, here's how I implemented angular constraints for another verlet lib: https://github.com/softfab/tshirt/blob/master/src/verlet-constraint-angle-2d.js

I will bury a timecapsule with instructions for my great grandchildren on how to complete my project when angle constraints are finally added to this library in 2050

Anyone wanna help @liabru out? It's an open source library, not a paid service ;)

+1

I've come up with a solution for this that seems like a good workaround until this is implemented. For my project I wanted to limit the extended and contracted angle between the upper arm and lower arm of a character to mimic normal elbow behavior. You can check it out below in action...

revolute

How it works

  • Create a constraint between the two bodies that are linked with a revolute constraint

    • Ie.. a 0 distance .7 stiffness regular constraint

    • Set the constraint stiffness to 0

    • The length doesn't matter yet

  • It seems like it works well if you offset the constraint from the bodies as seen in the gif
  • Every frame, measure the distance between the two attachment points of the constraint

    • Set some some minimum and & maximum rules around this distance

  • For my case, I am checking to see if the the distance is greater than 120
  • If it's greater than 120,

    • I set the stiffness of the constraint to 1

    • I set the length of the constraint to 120

  • For my case, this does enough to limit the angle for both an extended and contracted arm but you could layer a bunch of these together to get the constraints you want.

In general, I think this might be a good approach and can be adapted for other scenarios.

Some Code

This is the constraint setup

let testConstraint = Constraint.create({
  bodyA: upperArm,
  pointA: { x: 120, y: 0 },
  bodyB: lowerArm,
  pointB: { x: 120, y: 0 },
  stiffness : 0,
  length: 100
});

This gets the distance between the attachment points

let uX = 120; // This is the x offset of the constraint for the upper arm
let uY = 0;   // This is the y offset of the constraint for the upper arm
let uA = upperArm.angle;
let upperX = upperArm.position.x + uX * Math.cos(uA) - uY * Math.sin(uA);
let upperY = upperArm.position.y + uX * Math.sin(uA) + uY * Math.cos(uA);

let lX = 120; // This is the x offset of the constraint for the lower arm
let lY = 0;   // This is the x offset of the constraint for the lower arm
let lA = lowerArm.angle;
let lowerX = lowerArm.position.x + lX * Math.cos(lA) - lY * Math.sin(lA);
let lowerY = lowerArm.position.y + lX * Math.sin(lA) + lY * Math.cos(lA);

var xDelta = upperX - lowerX;
var yDelta = upperY - lowerY;
var distance = Math.sqrt( xDelta * xDelta + yDelta * yDelta );

if (distance > 120){
  testConstraint.length = 120;
  testConstraint.stiffness = 1;
} else {
  testConstraint.stiffness = 0;
}

cc @AndrewBrownK @Twosies @joel-simon @davearel in case this still matter to you haha.

My use case was to implement a servo arm. What I ended up doing was to add two revolute constraints; one for the pivot point (like flukeout) and one farther out (unlike in flukeout's example, this is also a revolute constraint!). The constraint can be modified at runtime to change to which angle the dependent element should go.

const anchor = Matter.Bodies.rectangle(200, 350, 50, 50, {
  density: 1,
  frictionAir: 0.4,
});
const arm = Matter.Bodies.rectangle(200, 200, 10, 200, {
  // in this example I don't want the anchor to rotate too much when the arm moves
  density: 0.01,
  frictionAir: 0.1,
});
// this constraint remains constant
const pivot = Matter.Constraint.create({
  bodyA: anchor,
  pointA: { x: 0, y: -50 },
  bodyB: arm,
  pointB: { x: 0, y: 100 },
});
const control = Matter.Constraint.create({
  bodyA: anchor,
  // the pointA is modified to change the point to which the arm should be fixed
  pointA: { x: 0, y: -100 },
  bodyB: arm,
  pointB: { x: 0, y: 50 },
});

Matter.World.add(this.simulation.world, [anchor, arm, pivot, control]);

setTimeout(() => {
  // change the control point later
  // should use sine and cosine, this is fine for the demo
  control.pointA = { x: 20, y: -100 };
}, 1000);

I think by making the pivot constraint stiffer than the control constraint, you can ensure that the rotation point is at/closer to the pivot, and by adjusting the distance between pivot and control, you can control the "angular stiffness".

I've opened PR #837 which adds angle constraints, those interested please take a look, try them out and let me know if you spot any issues or just if they work nicely for your use case.

Thanks for holding on here everyone!

(@AndrewBrownK get ready to dig up that time capsule)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

liabru picture liabru  路  3Comments

christianmalek picture christianmalek  路  4Comments

ShadewEnder picture ShadewEnder  路  3Comments

probityrules picture probityrules  路  4Comments

BlueInt32 picture BlueInt32  路  4Comments