As a follow up post to the Distance Constraint post, we can also create a maximum distance constraint using the same solution we found in the Distance Constraint post.
The previous solution created a fixed length distance constraint which forced a pair of bodies to be a given length apart. We can simply add an if statement to make this constraint a max length constraint.
A max length constraint only specifies that the distance between the joined bodies does not exceed the given maximum. So before applying the fixed length distance constraint just check whether the bodies have separated past the maximum distance. If not, then do nothing. Simple!
class MaxDistanceJoint extends Joint {
constructor(b1, b2, maxDistance = -1, anchor1 = b1.position, anchor2 = b2.position, frequency = 15, dampingRatio = 1.0, jointMass = -1) {
super(b1, b2, frequency, dampingRatio, jointMass);
this.impulseSum = 0.0;
this.localAnchor1 = this.body1.globalToLocal.mulVector2(anchor1, 1);
this.localAnchor2 = this.body2.globalToLocal.mulVector2(anchor2, 1);
this._maxDistance = maxDistance <= 0 ? anchor2.sub(anchor1).length : maxDistance;
}
preStep()
preStep(inv_dt) {
// Calculate Jacobian J and effective mass M
// J = [-n, -n·cross(ra), n, n·cross(rb)] ( n = (anchor2-anchor1) / ||anchor2-anchor1|| )
// M = (J · M^-1 · J^t)^-1
this.r1 = this.body1.localToGlobal.mulVector2(this.localAnchor1, 0);
this.r2 = this.body2.localToGlobal.mulVector2(this.localAnchor2, 0);
let p1 = this.body1.position.add(this.r1);
let p2 = this.body2.position.add(this.r2);
let u = p2.sub(p1);
let error = (u.length - this._maxDistance);
// Inequality constraint needs to check if the constraint is violated. If not, then do nothing.
if (error < 0) {
this.bias = -1;
return;
};
this.n = u.normalized();
let k = this.body1.invMass + this.body2.invMass
+ this.body1.invI * this.n.cross(this.r1) * this.n.cross(this.r1)
+ this.body2.invI * this.n.cross(this.r2) * this.n.cross(this.r2)
+ this.gamma;
this.m = 1.0 / k;
if (Settings.positionCorrection)
this.bias = error * this.beta * inv_dt;
else
this.bias = 0.0;
if (Settings.warmStarting)
this.applyImpulse(this.impulseSum);
}
solve()
solve() {
// Calculate corrective impulse: Pc
// Pc = J^t · λ (λ: lagrangian multiplier)
// λ = (J · M^-1 · J^t)^-1 ⋅ -(J·v+b)
if (this.bias < 0)
return;
let jv = this.body2.linearVelocity.add(Util.cross(this.body2.angularVelocity, this.r2))
.sub(this.body1.linearVelocity.add(Util.cross(this.body1.angularVelocity, this.r1))).dot(this.n);
let lambda = this.m * -(jv + this.bias + this.impulseSum * this.gamma);
this.applyImpulse(lambda);
if (Settings.warmStarting) this.impulseSum += lambda;
}
Comments