0
0
Fork 0
mirror of https://github.com/liabru/matter-js.git synced 2025-01-12 16:08:50 -05:00

added angle constraints

This commit is contained in:
liabru 2020-03-11 22:11:28 +00:00
parent 06f8023fb7
commit 0764c8d002
2 changed files with 215 additions and 14 deletions

View file

@ -23,6 +23,7 @@ var Common = require('../core/Common');
Constraint._warming = 0.4; Constraint._warming = 0.4;
Constraint._torqueDampen = 1; Constraint._torqueDampen = 1;
Constraint._angleLimitDampen = 0.5;
Constraint._minLength = 0.000001; Constraint._minLength = 0.000001;
/** /**
@ -48,8 +49,9 @@ var Common = require('../core/Common');
// calculate static length using initial world space points // calculate static length using initial world space points
var initialPointA = constraint.bodyA ? Vector.add(constraint.bodyA.position, constraint.pointA) : constraint.pointA, 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, initialPointB = constraint.bodyB ? Vector.add(constraint.bodyB.position, constraint.pointB) : constraint.pointB,
length = Vector.magnitude(Vector.sub(initialPointA, initialPointB)); length = Vector.magnitude(Vector.sub(initialPointA, initialPointB)),
angle = Vector.angle(initialPointA, initialPointB);
constraint.length = typeof constraint.length !== 'undefined' ? constraint.length : length; constraint.length = typeof constraint.length !== 'undefined' ? constraint.length : length;
// option defaults // option defaults
@ -59,17 +61,32 @@ var Common = require('../core/Common');
constraint.stiffness = constraint.stiffness || (constraint.length > 0 ? 1 : 0.7); constraint.stiffness = constraint.stiffness || (constraint.length > 0 ? 1 : 0.7);
constraint.damping = constraint.damping || 0; constraint.damping = constraint.damping || 0;
constraint.angularStiffness = constraint.angularStiffness || 0; constraint.angularStiffness = constraint.angularStiffness || 0;
constraint.angleA = constraint.bodyA ? constraint.bodyA.angle : constraint.angleA; constraint.angleAPrev = constraint.bodyA ? constraint.bodyA.angle : constraint.angleAPrev;
constraint.angleB = constraint.bodyB ? constraint.bodyB.angle : constraint.angleB; constraint.angleBPrev = constraint.bodyB ? constraint.bodyB.angle : constraint.angleBPrev;
constraint.plugin = {}; constraint.plugin = {};
constraint.angleA = typeof constraint.angleA !== 'undefined' ?
constraint.angleA : (constraint.bodyA ? 0 : angle);
constraint.angleAStiffness = constraint.angleAStiffness || 0;
constraint.angleAMin = constraint.angleAMin || 0;
constraint.angleAMax = constraint.angleAMax || 0;
constraint.angleB = typeof constraint.angleB !== 'undefined' ?
constraint.angleB : (constraint.bodyB ? 0 : angle);
constraint.angleBStiffness = constraint.angleBStiffness || 0;
constraint.angleBMin = constraint.angleBMin || 0;
constraint.angleBMax = constraint.angleBMax || 0;
// render // render
var render = { var render = {
visible: true, visible: true,
lineWidth: 2, lineWidth: 2,
strokeStyle: '#ffffff', strokeStyle: '#ffffff',
type: 'line', type: 'line',
anchors: true anchors: true,
angles: true
}; };
if (constraint.length === 0 && constraint.stiffness > 0.1) { if (constraint.length === 0 && constraint.stiffness > 0.1) {
@ -154,14 +171,14 @@ var Common = require('../core/Common');
// update reference angle // update reference angle
if (bodyA && !bodyA.isStatic) { if (bodyA && !bodyA.isStatic) {
Vector.rotate(pointA, bodyA.angle - constraint.angleA, pointA); Vector.rotate(pointA, bodyA.angle - constraint.angleAPrev, pointA);
constraint.angleA = bodyA.angle; constraint.angleAPrev = bodyA.angle;
} }
// update reference angle // update reference angle
if (bodyB && !bodyB.isStatic) { if (bodyB && !bodyB.isStatic) {
Vector.rotate(pointB, bodyB.angle - constraint.angleB, pointB); Vector.rotate(pointB, bodyB.angle - constraint.angleBPrev, pointB);
constraint.angleB = bodyB.angle; constraint.angleBPrev = bodyB.angle;
} }
var pointAWorld = pointA, var pointAWorld = pointA,
@ -188,11 +205,29 @@ var Common = require('../core/Common');
massTotal = (bodyA ? bodyA.inverseMass : 0) + (bodyB ? bodyB.inverseMass : 0), massTotal = (bodyA ? bodyA.inverseMass : 0) + (bodyB ? bodyB.inverseMass : 0),
inertiaTotal = (bodyA ? bodyA.inverseInertia : 0) + (bodyB ? bodyB.inverseInertia : 0), inertiaTotal = (bodyA ? bodyA.inverseInertia : 0) + (bodyB ? bodyB.inverseInertia : 0),
resistanceTotal = massTotal + inertiaTotal, resistanceTotal = massTotal + inertiaTotal,
angleAStiffness = (constraint.angleAStiffness < 1 ? constraint.angleAStiffness * timeScale : constraint.angleAStiffness) * Constraint._angleLimitDampen,
angleBStiffness = (constraint.angleBStiffness < 1 ? constraint.angleBStiffness * timeScale : constraint.angleBStiffness) * Constraint._angleLimitDampen,
torque, torque,
share, share,
normal, normal,
normalVelocity, normalVelocity,
relativeVelocity; relativeVelocity,
angleLimitPointA,
angleLimitPointB;
if (constraint.angleAStiffness) {
angleLimitPointA = Constraint.solveAngleLimits(
constraint, bodyA, constraint.angleA, constraint.angleAMin,
constraint.angleAMax, delta, -1, currentLength, pointAWorld, pointBWorld
);
}
if (constraint.angleBStiffness) {
angleLimitPointB = Constraint.solveAngleLimits(
constraint, bodyB, constraint.angleB, constraint.angleBMin,
constraint.angleBMax, delta, 1, currentLength, pointBWorld, pointAWorld
);
}
if (constraint.damping) { if (constraint.damping) {
var zero = Vector.create(); var zero = Vector.create();
@ -209,6 +244,12 @@ var Common = require('../core/Common');
if (bodyA && !bodyA.isStatic) { if (bodyA && !bodyA.isStatic) {
share = bodyA.inverseMass / massTotal; share = bodyA.inverseMass / massTotal;
// temporarily add angular limit force from pointB if pinned
if (angleLimitPointB && (!bodyB || bodyB.isStatic)) {
force.x -= angleLimitPointB.x * angleBStiffness;
force.y -= angleLimitPointB.y * angleBStiffness;
}
// keep track of applied impulses for post solving // keep track of applied impulses for post solving
bodyA.constraintImpulse.x -= force.x * share; bodyA.constraintImpulse.x -= force.x * share;
bodyA.constraintImpulse.y -= force.y * share; bodyA.constraintImpulse.y -= force.y * share;
@ -223,15 +264,34 @@ var Common = require('../core/Common');
bodyA.positionPrev.y -= constraint.damping * normal.y * normalVelocity * share; bodyA.positionPrev.y -= constraint.damping * normal.y * normalVelocity * share;
} }
// apply torque // find torque to apply
torque = (Vector.cross(pointA, force) / resistanceTotal) * Constraint._torqueDampen * bodyA.inverseInertia * (1 - constraint.angularStiffness); torque = (Vector.cross(pointA, force) / resistanceTotal) * Constraint._torqueDampen * bodyA.inverseInertia * (1 - constraint.angularStiffness);
// add any torque from angular limit at pointA
if (angleLimitPointA) {
torque -= angleLimitPointA.angle * angleAStiffness;
}
// apply torque
bodyA.constraintImpulse.angle -= torque; bodyA.constraintImpulse.angle -= torque;
bodyA.angle -= torque; bodyA.angle -= torque;
// remove angular limit from pointB
if (angleLimitPointB && (!bodyB || bodyB.isStatic)) {
force.x += angleLimitPointB.x * angleBStiffness;
force.y += angleLimitPointB.y * angleBStiffness;
}
} }
if (bodyB && !bodyB.isStatic) { if (bodyB && !bodyB.isStatic) {
share = bodyB.inverseMass / massTotal; share = bodyB.inverseMass / massTotal;
// add angular limit force from pointA if pinned
if (angleLimitPointA && (!bodyA || bodyA.isStatic)) {
force.x += angleLimitPointA.x * angleAStiffness;
force.y += angleLimitPointA.y * angleAStiffness;
}
// keep track of applied impulses for post solving // keep track of applied impulses for post solving
bodyB.constraintImpulse.x += force.x * share; bodyB.constraintImpulse.x += force.x * share;
bodyB.constraintImpulse.y += force.y * share; bodyB.constraintImpulse.y += force.y * share;
@ -246,8 +306,15 @@ var Common = require('../core/Common');
bodyB.positionPrev.y += constraint.damping * normal.y * normalVelocity * share; bodyB.positionPrev.y += constraint.damping * normal.y * normalVelocity * share;
} }
// apply torque // find torque to apply
torque = (Vector.cross(pointB, force) / resistanceTotal) * Constraint._torqueDampen * bodyB.inverseInertia * (1 - constraint.angularStiffness); torque = (Vector.cross(pointB, force) / resistanceTotal) * Constraint._torqueDampen * bodyB.inverseInertia * (1 - constraint.angularStiffness);
// add any torque from angular limit at pointB
if (angleLimitPointB) {
torque += angleLimitPointB.angle * angleBStiffness;
}
// apply torque
bodyB.constraintImpulse.angle += torque; bodyB.constraintImpulse.angle += torque;
bodyB.angle += torque; bodyB.angle += torque;
} }
@ -325,6 +392,46 @@ var Common = require('../core/Common');
y: (constraint.bodyB ? constraint.bodyB.position.y : 0) + constraint.pointB.y y: (constraint.bodyB ? constraint.bodyB.position.y : 0) + constraint.pointB.y
}; };
}; };
/**
* Solves angle limits on the constraint.
* @private
* @method solveAngleLimits
*/
Constraint.solveAngleLimits = function(constraint, body, angle, angleMin, angleMax, delta, deltaScale, currentLength, pointAWorld, pointBWorld) {
var currentAngle = (body ? body.angle : 0) + (constraint.length > 0 ? angle : 0),
min = angleMin < angleMax ? angleMin : angleMax,
max = angleMax > angleMin ? angleMax : angleMin,
angleNormal = { x: Math.cos(currentAngle), y: Math.sin(currentAngle) },
angleDelta;
if (constraint.length === 0) {
// use absolute angle for pin constraints
angleDelta = Common.angleDiff(angle, currentAngle);
} else {
// otherwise use relative angle
angleDelta = Math.atan2(
angleNormal.x * delta.y * deltaScale - angleNormal.y * delta.x * deltaScale,
angleNormal.x * delta.x * deltaScale + angleNormal.y * delta.y * deltaScale
);
}
// no impulse required if angle within limits
if (angleDelta > min && angleDelta < max) {
return null;
}
// find the clamped angle and clamp the normal
var angleClamped = Common.clampAngle(angleDelta, min, max),
normalLimited = Vector.rotate(angleNormal, angleClamped);
// return the impulses required to correct the angle
return {
x: pointAWorld.x + normalLimited.x * currentLength - pointBWorld.x,
y: pointAWorld.y + normalLimited.y * currentLength - pointBWorld.y,
angle: Common.angleDiff(angleDelta, angleClamped)
};
};
/* /*
* *
@ -333,7 +440,7 @@ var Common = require('../core/Common');
*/ */
/** /**
* An integer `Number` uniquely identifying number generated in `Composite.create` by `Common.nextId`. * An integer `Number` uniquely identifying number generated in `Constraint.create` by `Common.nextId`.
* *
* @property id * @property id
* @type number * @type number
@ -407,6 +514,14 @@ var Common = require('../core/Common');
* @default true * @default true
*/ */
/**
* **ALPHA**: A `Boolean` that defines if the constraint's anglular limits should be rendered.
*
* @property render.angles
* @type boolean
* @default true
*/
/** /**
* The first possible `Body` that this constraint is attached to. * The first possible `Body` that this constraint is attached to.
* *
@ -469,6 +584,92 @@ var Common = require('../core/Common');
* @type number * @type number
*/ */
/**
* **ALPHA**: A `Number` in radians that specifies the limiting angle of the constraint about `constraint.pointA`.
* It is relative to `constraint.bodyA.angle` if `constraint.bodyA` is set, otherwise is absolute.
* Defaults to the initial angle of the constraint or `0`.
* Only applies if `constraint.angleAStiffness > 0`.
* The constraint angle is measured between the vector `pointA - pointB` and the x-axis.
*
* @property angleA
* @type number
* @default the initial relative constraint angle
*/
/**
* **ALPHA**: A `Number` that specifies the stiffness of angular limits about `constraint.pointA`.
* A value of `0` (default) means the constraint will not limit the angle.
* A value of `0.01` means the constraint will softly limit the angle.
* A value of `1` means the constraint will rigidly limit the angle.
*
* @property angleAStiffness
* @type number
* @default 0
*/
/**
* **ALPHA**: A `Number` in radians that specifies the lower angular limit
* about `constraint.pointA` relative to `constraint.angleA`.
* A value of `-0.5` means the constraint is limited to `0.5` radians
* anti-clockwise of `constraint.angleA`, or clockwise if the value is positive.
*
* @property angleAMin
* @type number
* @default 0
*/
/**
* **ALPHA**: A `Number` in radians that specifies the upper angular limit
* about `constraint.pointA` relative to `constraint.angleA`. See `constraint.angleAMin` for more.
*
* @property angleAMax
* @type number
* @default 0
*/
/**
* **ALPHA**: A `Number` in radians that specifies the limiting angle of the constraint about `constraint.pointB`.
* It is relative to `constraint.bodyB.angle` if `constraint.bodyB` is set, otherwise is absolute.
* Defaults to the initial angle of the constraint or `0`.
* Only applies if `constraint.angleBStiffness > 0`.
* The constraint angle is measured between the vector `pointA - pointB` and the x-axis.
*
* @property angleB
* @type number
* @default the initial relative constraint angle
*/
/**
* **ALPHA**: A `Number` that specifies the stiffness of angular limits about `constraint.pointB`.
* A value of `0` (default) means the constraint will not limit the angle.
* A value of `0.01` means the constraint will softly limit the angle.
* A value of `1` means the constraint will rigidly limit the angle.
*
* @property angleBStiffness
* @type number
* @default 0
*/
/**
* **ALPHA**: A `Number` in radians that specifies the lower angular limit
* about `constraint.pointB` relative to `constraint.angleB`.
* A value of `-0.5` means the constraint is limited to `0.5` radians
* anti-clockwise of `constraint.angleB`, or clockwise if the value is positive.
*
* @property angleBMin
* @type number
* @default 0
*/
/**
* **ALPHA**: A `Number` in radians that specifies the upper angular limit
* about `constraint.pointB` relative to `constraint.angleB`. See `constraint.angleBMin` for more.
*
* @property angleBMax
* @type number
* @default 0
*/
/** /**
* An object reserved for storing plugin-specific properties. * An object reserved for storing plugin-specific properties.
* *

View file

@ -107,7 +107,7 @@ var Bounds = require('../geometry/Bounds');
constraint.pointA = mouse.position; constraint.pointA = mouse.position;
constraint.bodyB = mouseConstraint.body = body; constraint.bodyB = mouseConstraint.body = body;
constraint.pointB = { x: mouse.position.x - body.position.x, y: mouse.position.y - body.position.y }; constraint.pointB = { x: mouse.position.x - body.position.x, y: mouse.position.y - body.position.y };
constraint.angleB = body.angle; constraint.angleBPrev = body.angle;
Sleeping.set(body, false); Sleeping.set(body, false);
Events.trigger(mouseConstraint, 'startdrag', { mouse: mouse, body: body }); Events.trigger(mouseConstraint, 'startdrag', { mouse: mouse, body: body });