![](https://static.wixstatic.com/media/e41812_8a029429003d4a64bbe39e46c29902a6~mv2.png/v1/fill/w_291,h_94,al_c,q_85,enc_avif,quality_auto/e41812_8a029429003d4a64bbe39e46c29902a6~mv2.png)
The line constraint is a constraint that restricts the movement of a pair of bodies to a prescribed "line". Dyn4j sites a car on a roller coaster track as being an example.
It is similar to the Prismatic constraint but the bodies are free to rotate about the anchor point.
![](https://static.wixstatic.com/media/e41812_e905dd553359411081eab0095a5a7b07~mv2.png/v1/fill/w_200,h_150,al_c,q_85,enc_avif,quality_auto/e41812_e905dd553359411081eab0095a5a7b07~mv2.png)
The initial vector u will be supplied in the construction of the constraint.
From u, we will obtain the tangent of u, the t vector.
Each simulation step we will recompute u from the anchor points and use it along with the saved t vector to determine if the constraint has been violated.
class LineJoint extends Joint {
constructor(b1, b2, anchor1 = b1.position, anchor2 = b2.position, dir, frequency = 30, dampingRatio = 1.0, jointMass = -1) {
super(b1, b2, frequency, dampingRatio, jointMass);
this.impulseSum = 0.0;
if (body1.type == Type.Static && body2.type == Type.Static)
throw "Can't make line constraint between static bodies";
if (body1.type == Type.Dynamic && body2.type == Type.Dynamic)
throw "Can't make line constraint between dynamic bodies";
if (body2.type == Type.Static)
throw "Please make line constraint by using the body1 as a static body";
this.localAnchor1 = Vector2.InverseRotateAndTranslate(this.body1.Rot, this.body1.position, anchor1);
this.localAnchor2 = Vector2.InverseRotateAndTranslate(this.body2.Rot, this.body2.position, anchor2);
let u = anchor2.sub(anchor1);
this.t = new Vector2(-u.y, u.x).normalized();
if (dir == undefined) {
let u = anchor2.sub(anchor1);
this.t = new Vector2(-u.y, u.x).normalized();
}
else {
this.t = new Vector2(-dir.y, dir.x).normalized();
}
Util.assert(this.t.squaredLength > 0);
}
Position Constraint Function
The position constraint functino is defined as follows:
![](https://static.wixstatic.com/media/e41812_da7cc72fd676463f8f13db992548b87b~mv2.png/v1/fill/w_123,h_39,al_c,q_85,enc_avif,quality_auto/e41812_da7cc72fd676463f8f13db992548b87b~mv2.png)
where
![](https://static.wixstatic.com/media/e41812_0c8e8d0ca7d349bc9b8e110a01fca7b4~mv2.png/v1/fill/w_358,h_78,al_c,q_85,enc_avif,quality_auto/e41812_0c8e8d0ca7d349bc9b8e110a01fca7b4~mv2.png)
This equation states that any motion that is not along the vector is invalid, because the tangent of that motion projected onto (via the dot product) the t vector will no longer yield 0.
Velocity Constraint Function
![](https://static.wixstatic.com/media/e41812_c9874fa980924b5db362dfba9688cc59~mv2.png/v1/fill/w_147,h_50,al_c,q_85,enc_avif,quality_auto/e41812_c9874fa980924b5db362dfba9688cc59~mv2.png)
By the chain rule:
![](https://static.wixstatic.com/media/e41812_59e4b2d1724348bc92a53480f5fc0584~mv2.png/v1/fill/w_241,h_50,al_c,q_85,enc_avif,quality_auto/e41812_59e4b2d1724348bc92a53480f5fc0584~mv2.png)
Where the derivative of u:
![](https://static.wixstatic.com/media/e41812_a77478a0f2b941df8e4d74540eec9386~mv2.png/v1/fill/w_324,h_126,al_c,q_85,enc_avif,quality_auto/e41812_a77478a0f2b941df8e4d74540eec9386~mv2.png)
And the derivative of t:
![](https://static.wixstatic.com/media/e41812_af6ce57afb824ecdb1910d08c353a400~mv2.png/v1/fill/w_124,h_50,al_c,q_85,enc_avif,quality_auto/e41812_af6ce57afb824ecdb1910d08c353a400~mv2.png)
which gives us the following constraint function
![](https://static.wixstatic.com/media/e41812_1a53bc3120374a588dbbdd83c1f2fcae~mv2.png/v1/fill/w_480,h_44,al_c,q_85,enc_avif,quality_auto/e41812_1a53bc3120374a588dbbdd83c1f2fcae~mv2.png)
if we multiply this out, we get
![](https://static.wixstatic.com/media/e41812_d5a82981b8a04248830e53af167429eb~mv2.png/v1/fill/w_515,h_44,al_c,q_85,enc_avif,quality_auto/e41812_d5a82981b8a04248830e53af167429eb~mv2.png)
If we rely on the triple product identity, we can rearrange the above as
![](https://static.wixstatic.com/media/e41812_221ded6059ba47988e9c53b89e930e74~mv2.png/v1/fill/w_515,h_108,al_c,q_85,enc_avif,quality_auto/e41812_221ded6059ba47988e9c53b89e930e74~mv2.png)
Isolate The Velocities
![](https://static.wixstatic.com/media/e41812_6c21308667ef46839095bc44e75dbaa0~mv2.png/v1/fill/w_432,h_110,al_c,q_85,enc_avif,quality_auto/e41812_6c21308667ef46839095bc44e75dbaa0~mv2.png)
by inspection the jacobian is
![](https://static.wixstatic.com/media/e41812_d7b5947bcc1d4ac7a6a7883902a6edec~mv2.png/v1/fill/w_340,h_26,al_c,q_85,enc_avif,quality_auto/e41812_d7b5947bcc1d4ac7a6a7883902a6edec~mv2.png)
K-matrix
![](https://static.wixstatic.com/media/e41812_63e811ebc771478f8d55c51e66d03da4~mv2.png/v1/fill/w_748,h_77,al_c,q_85,enc_avif,quality_auto/e41812_63e811ebc771478f8d55c51e66d03da4~mv2.png)
PreStep()
preStep(inv_dt) {
// Calculate Jacobian J and effective mass M
// J = [-t^t, -(ra + u)×t, t^t, rb×t]
// M = (J · M^-1 · J^t)^-1
// Pre-compute anchors, mass matrix, and bias.
Vector2.Rotate(this.body1.Rot, this.localAnchor1, this.r1);
Vector2.Rotate(this.body2.Rot, this.localAnchor2, this.r2);
let p1 = this.body1.position.add(this.r1);
let p2 = this.body2.position.add(this.r2);
this.u = p2.sub(p1);
let k = this.body2.invMass + this.r2.cross(this.t) * this.body2.invI
- (this.body1.invMass + this.r1.add(this.u).cross(this.t) * this.body1.invI)
+ this.gamma;
this.m = 1.0 / k;
let error = this.u.dot(this.t);
if (Settings.positionCorrection)
this.bias = error * this.beta * inv_dt;
else
this.bias = 0.0;
if (Settings.warmStarting)
this.applyImpulse(this.impulseSum);
}
ApplyImpulse
solve() {
// Calculate corrective impulse: Pc
// Pc = J^t · λ (λ: lagrangian multiplier)
// λ = (J · M^-1 · J^t)^-1 ⋅ -(J·v+b)
let jv = this.t.dot(this.body2.linearVelocity) + this.r2.cross(this.t) * this.body2.angularVelocity
- (this.t.dot(this.body1.linearVelocity) + this.r2.add(this.u).cross(this.t) * this.body1.angularVelocity);
let lambda = this.m * -(jv + this.bias + this.impulseSum * this.gamma);
this.applyImpulse(lambda);
if (Settings.warmStarting)
this.impulseSum += lambda;
}
Comments