mirror of
https://github.com/liabru/matter-js.git
synced 2024-11-30 10:20:52 -05:00
217 lines
No EOL
7.6 KiB
JavaScript
217 lines
No EOL
7.6 KiB
JavaScript
/**
|
|
* See [Demo.js](https://github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
|
|
* and [DemoMobile.js](https://github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples.
|
|
*
|
|
* @class Constraint
|
|
*/
|
|
|
|
// TODO: fix instabillity issues with torque
|
|
// TODO: linked constraints
|
|
// TODO: breakable constraints
|
|
// TODO: collidable constraints
|
|
// TODO: allow constrained bodies to sleep
|
|
// TODO: handle 0 length constraints properly
|
|
// TODO: impulse caching and warming
|
|
|
|
var Constraint = {};
|
|
|
|
(function() {
|
|
|
|
var _minLength = 0.000001;
|
|
|
|
/**
|
|
* Description
|
|
* @method create
|
|
* @param {} options
|
|
* @return {constraint} constraint
|
|
*/
|
|
Constraint.create = function(options) {
|
|
var constraint = options;
|
|
|
|
// if bodies defined but no points, use body centre
|
|
if (constraint.bodyA && !constraint.pointA)
|
|
constraint.pointA = { x: 0, y: 0 };
|
|
if (constraint.bodyB && !constraint.pointB)
|
|
constraint.pointB = { x: 0, y: 0 };
|
|
|
|
// calculate static length using initial world space points
|
|
var initialPointA = constraint.bodyA ? Vector.add(constraint.bodyA.position, constraint.pointA) : constraint.pointA,
|
|
initialPointB = constraint.bodyB ? Vector.add(constraint.bodyB.position, constraint.pointB) : constraint.pointB,
|
|
length = Vector.magnitude(Vector.sub(initialPointA, initialPointB));
|
|
|
|
constraint.length = constraint.length || length || _minLength;
|
|
|
|
// option defaults
|
|
constraint.lineWidth = constraint.lineWidth || 2;
|
|
constraint.strokeStyle = constraint.strokeStyle || '#666';
|
|
constraint.stiffness = constraint.stiffness || 1;
|
|
constraint.angularStiffness = constraint.angularStiffness || 0;
|
|
constraint.angleA = constraint.bodyA ? constraint.bodyA.angle : constraint.angleA;
|
|
constraint.angleB = constraint.bodyB ? constraint.bodyB.angle : constraint.angleB;
|
|
|
|
return constraint;
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method updateAll
|
|
* @param {constraint[]} constraints
|
|
*/
|
|
Constraint.updateAll = function(constraints) {
|
|
for (var i = 0; i < constraints.length; i++) {
|
|
Constraint.update(constraints[i]);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Description
|
|
* @method update
|
|
* @param {constraint} constraint
|
|
*/
|
|
Constraint.update = function(constraint) {
|
|
var bodyA = constraint.bodyA,
|
|
bodyB = constraint.bodyB,
|
|
pointA = constraint.pointA,
|
|
pointB = constraint.pointB;
|
|
|
|
// update reference angle
|
|
if (bodyA && !bodyA.isStatic) {
|
|
constraint.pointA = Vector.rotate(pointA, bodyA.angle - constraint.angleA);
|
|
constraint.angleA = bodyA.angle;
|
|
}
|
|
|
|
// update reference angle
|
|
if (bodyB && !bodyB.isStatic) {
|
|
constraint.pointB = Vector.rotate(pointB, bodyB.angle - constraint.angleB);
|
|
constraint.angleB = bodyB.angle;
|
|
}
|
|
|
|
var pointAWorld = pointA,
|
|
pointBWorld = pointB;
|
|
|
|
if (bodyA) pointAWorld = Vector.add(bodyA.position, pointA);
|
|
if (bodyB) pointBWorld = Vector.add(bodyB.position, pointB);
|
|
|
|
if (!pointAWorld || !pointBWorld)
|
|
return;
|
|
|
|
var delta = Vector.sub(pointAWorld, pointBWorld),
|
|
currentLength = Vector.magnitude(delta);
|
|
|
|
// prevent singularity
|
|
if (currentLength === 0)
|
|
currentLength = _minLength;
|
|
|
|
// solve distance constraint with Gauss-Siedel method
|
|
var difference = (currentLength - constraint.length) / currentLength,
|
|
normal = Vector.div(delta, currentLength),
|
|
force = Vector.mult(delta, difference * 0.5 * constraint.stiffness);
|
|
|
|
var velocityPointA,
|
|
velocityPointB,
|
|
offsetA,
|
|
offsetB,
|
|
oAn,
|
|
oBn,
|
|
bodyADenom,
|
|
bodyBDenom;
|
|
|
|
if (bodyA && !bodyA.isStatic) {
|
|
// point body offset
|
|
offsetA = {
|
|
x: pointAWorld.x - bodyA.position.x + force.x,
|
|
y: pointAWorld.y - bodyA.position.y + force.y
|
|
};
|
|
|
|
// update velocity
|
|
bodyA.velocity.x = bodyA.position.x - bodyA.positionPrev.x;
|
|
bodyA.velocity.y = bodyA.position.y - bodyA.positionPrev.y;
|
|
bodyA.angularVelocity = bodyA.angle - bodyA.anglePrev;
|
|
|
|
// find point velocity and body mass
|
|
velocityPointA = Vector.add(bodyA.velocity, Vector.mult(Vector.perp(offsetA), bodyA.angularVelocity));
|
|
oAn = Vector.dot(offsetA, normal);
|
|
bodyADenom = bodyA.inverseMass + bodyA.inverseInertia * oAn * oAn;
|
|
} else {
|
|
velocityPointA = { x: 0, y: 0 };
|
|
bodyADenom = bodyA ? bodyA.inverseMass : 0;
|
|
}
|
|
|
|
if (bodyB && !bodyB.isStatic) {
|
|
// point body offset
|
|
offsetB = {
|
|
x: pointBWorld.x - bodyB.position.x - force.x,
|
|
y: pointBWorld.y - bodyB.position.y - force.y
|
|
};
|
|
|
|
// update velocity
|
|
bodyB.velocity.x = bodyB.position.x - bodyB.positionPrev.x;
|
|
bodyB.velocity.y = bodyB.position.y - bodyB.positionPrev.y;
|
|
bodyB.angularVelocity = bodyB.angle - bodyB.anglePrev;
|
|
|
|
// find point velocity and body mass
|
|
velocityPointB = Vector.add(bodyB.velocity, Vector.mult(Vector.perp(offsetB), bodyB.angularVelocity));
|
|
oBn = Vector.dot(offsetB, normal);
|
|
bodyBDenom = bodyB.inverseMass + bodyB.inverseInertia * oBn * oBn;
|
|
} else {
|
|
velocityPointB = { x: 0, y: 0 };
|
|
bodyBDenom = bodyB ? bodyB.inverseMass : 0;
|
|
}
|
|
|
|
var relativeVelocity = Vector.sub(velocityPointB, velocityPointA),
|
|
normalImpulse = Vector.dot(normal, relativeVelocity) / (bodyADenom + bodyBDenom);
|
|
|
|
if (normalImpulse > 0) normalImpulse = 0;
|
|
|
|
var normalVelocity = {
|
|
x: normal.x * normalImpulse,
|
|
y: normal.y * normalImpulse
|
|
};
|
|
|
|
var torque;
|
|
|
|
if (bodyA && !bodyA.isStatic) {
|
|
torque = Vector.cross(offsetA, normalVelocity) * bodyA.inverseInertia * (1 - constraint.angularStiffness);
|
|
|
|
Sleeping.set(bodyA, false);
|
|
|
|
// clamp to prevent instabillity
|
|
// TODO: solve this properlly
|
|
torque = Common.clamp(torque, -0.01, 0.01);
|
|
|
|
// apply forces
|
|
bodyA.position.x -= force.x;
|
|
bodyA.position.y -= force.y;
|
|
bodyA.angle += torque;
|
|
|
|
// update geometry
|
|
Vertices.translate(bodyA.vertices, force, -1);
|
|
Vertices.rotate(bodyA.vertices, torque, bodyA.position);
|
|
Axes.rotate(bodyA.axes, torque);
|
|
Bounds.update(bodyA.bounds, bodyA.vertices, bodyA.velocity);
|
|
}
|
|
|
|
if (bodyB && !bodyB.isStatic) {
|
|
torque = Vector.cross(offsetB, normalVelocity) * bodyB.inverseInertia * (1 - constraint.angularStiffness);
|
|
|
|
Sleeping.set(bodyB, false);
|
|
|
|
// clamp to prevent instabillity
|
|
// TODO: solve this properlly
|
|
torque = Common.clamp(torque, -0.01, 0.01);
|
|
|
|
// apply forces
|
|
bodyB.position.x += force.x;
|
|
bodyB.position.y += force.y;
|
|
bodyB.angle -= torque;
|
|
|
|
// update geometry
|
|
Vertices.translate(bodyB.vertices, force);
|
|
Vertices.rotate(bodyB.vertices, -torque, bodyB.position);
|
|
Axes.rotate(bodyB.axes, -torque);
|
|
Bounds.update(bodyB.bounds, bodyB.vertices, bodyB.velocity);
|
|
}
|
|
|
|
};
|
|
|
|
})(); |