File indexing completed on 2025-08-03 03:49:59

0001 /*
0002 * Copyright (c) 2007 Erin Catto http://www.gphysics.com
0003 *
0004 * This software is provided 'as-is', without any express or implied
0005 * warranty.  In no event will the authors be held liable for any damages
0006 * arising from the use of this software.
0007 * Permission is granted to anyone to use this software for any purpose,
0008 * including commercial applications, and to alter it and redistribute it
0009 * freely, subject to the following restrictions:
0010 * 1. The origin of this software must not be misrepresented; you must not
0011 * claim that you wrote the original software. If you use this software
0012 * in a product, an acknowledgment in the product documentation would be
0013 * appreciated but is not required.
0014 * 2. Altered source versions must be plainly marked as such, and must not be
0015 * misrepresented as being the original software.
0016 * 3. This notice may not be removed or altered from any source distribution.
0017 */
0018 
0019 #include <Box2D/Dynamics/Joints/b2PulleyJoint.h>
0020 #include <Box2D/Dynamics/b2Body.h>
0021 #include <Box2D/Dynamics/b2TimeStep.h>
0022 
0023 // Pulley:
0024 // length1 = norm(p1 - s1)
0025 // length2 = norm(p2 - s2)
0026 // C0 = (length1 + ratio * length2)_initial
0027 // C = C0 - (length1 + ratio * length2) >= 0
0028 // u1 = (p1 - s1) / norm(p1 - s1)
0029 // u2 = (p2 - s2) / norm(p2 - s2)
0030 // Cdot = -dot(u1, v1 + cross(w1, r1)) - ratio * dot(u2, v2 + cross(w2, r2))
0031 // J = -[u1 cross(r1, u1) ratio * u2  ratio * cross(r2, u2)]
0032 // K = J * invM * JT
0033 //   = invMass1 + invI1 * cross(r1, u1)^2 + ratio^2 * (invMass2 + invI2 * cross(r2, u2)^2)
0034 //
0035 // Limit:
0036 // C = maxLength - length
0037 // u = (p - s) / norm(p - s)
0038 // Cdot = -dot(u, v + cross(w, r))
0039 // K = invMass + invI * cross(r, u)^2
0040 // 0 <= impulse
0041 
0042 void b2PulleyJointDef::Initialize(b2Body* b1, b2Body* b2,
0043                 const b2Vec2& ga1, const b2Vec2& ga2,
0044                 const b2Vec2& anchor1, const b2Vec2& anchor2,
0045                 qreal r)
0046 {
0047     bodyA = b1;
0048     bodyB = b2;
0049     groundAnchorA = ga1;
0050     groundAnchorB = ga2;
0051     localAnchorA = bodyA->GetLocalPoint(anchor1);
0052     localAnchorB = bodyB->GetLocalPoint(anchor2);
0053     b2Vec2 d1 = anchor1 - ga1;
0054     lengthA = d1.Length();
0055     b2Vec2 d2 = anchor2 - ga2;
0056     lengthB = d2.Length();
0057     ratio = r;
0058     b2Assert(ratio > b2_epsilon);
0059     qreal C = lengthA + ratio * lengthB;
0060     maxLengthA = C - ratio * b2_minPulleyLength;
0061     maxLengthB = (C - b2_minPulleyLength) / ratio;
0062 }
0063 
0064 b2PulleyJoint::b2PulleyJoint(const b2PulleyJointDef* def)
0065 : b2Joint(def)
0066 {
0067     m_groundAnchor1 = def->groundAnchorA;
0068     m_groundAnchor2 = def->groundAnchorB;
0069     m_localAnchor1 = def->localAnchorA;
0070     m_localAnchor2 = def->localAnchorB;
0071 
0072     b2Assert(def->ratio != 0.0f);
0073     m_ratio = def->ratio;
0074 
0075     m_constant = def->lengthA + m_ratio * def->lengthB;
0076 
0077     m_maxLength1 = b2Min(def->maxLengthA, m_constant - m_ratio * b2_minPulleyLength);
0078     m_maxLength2 = b2Min(def->maxLengthB, (m_constant - b2_minPulleyLength) / m_ratio);
0079 
0080     m_impulse = 0.0f;
0081     m_limitImpulse1 = 0.0f;
0082     m_limitImpulse2 = 0.0f;
0083 }
0084 
0085 void b2PulleyJoint::InitVelocityConstraints(const b2TimeStep& step)
0086 {
0087     b2Body* b1 = m_bodyA;
0088     b2Body* b2 = m_bodyB;
0089 
0090     b2Vec2 r1 = b2Mul(b1->GetTransform().R, m_localAnchor1 - b1->GetLocalCenter());
0091     b2Vec2 r2 = b2Mul(b2->GetTransform().R, m_localAnchor2 - b2->GetLocalCenter());
0092 
0093     b2Vec2 p1 = b1->m_sweep.c + r1;
0094     b2Vec2 p2 = b2->m_sweep.c + r2;
0095 
0096     b2Vec2 s1 = m_groundAnchor1;
0097     b2Vec2 s2 = m_groundAnchor2;
0098 
0099     // Get the pulley axes.
0100     m_u1 = p1 - s1;
0101     m_u2 = p2 - s2;
0102 
0103     qreal length1 = m_u1.Length();
0104     qreal length2 = m_u2.Length();
0105 
0106     if (length1 > b2_linearSlop)
0107     {
0108         m_u1 *= 1.0f / length1;
0109     }
0110     else
0111     {
0112         m_u1.SetZero();
0113     }
0114 
0115     if (length2 > b2_linearSlop)
0116     {
0117         m_u2 *= 1.0f / length2;
0118     }
0119     else
0120     {
0121         m_u2.SetZero();
0122     }
0123 
0124     qreal C = m_constant - length1 - m_ratio * length2;
0125     if (C > 0.0f)
0126     {
0127         m_state = e_inactiveLimit;
0128         m_impulse = 0.0f;
0129     }
0130     else
0131     {
0132         m_state = e_atUpperLimit;
0133     }
0134 
0135     if (length1 < m_maxLength1)
0136     {
0137         m_limitState1 = e_inactiveLimit;
0138         m_limitImpulse1 = 0.0f;
0139     }
0140     else
0141     {
0142         m_limitState1 = e_atUpperLimit;
0143     }
0144 
0145     if (length2 < m_maxLength2)
0146     {
0147         m_limitState2 = e_inactiveLimit;
0148         m_limitImpulse2 = 0.0f;
0149     }
0150     else
0151     {
0152         m_limitState2 = e_atUpperLimit;
0153     }
0154 
0155     // Compute effective mass.
0156     qreal cr1u1 = b2Cross(r1, m_u1);
0157     qreal cr2u2 = b2Cross(r2, m_u2);
0158 
0159     m_limitMass1 = b1->m_invMass + b1->m_invI * cr1u1 * cr1u1;
0160     m_limitMass2 = b2->m_invMass + b2->m_invI * cr2u2 * cr2u2;
0161     m_pulleyMass = m_limitMass1 + m_ratio * m_ratio * m_limitMass2;
0162     b2Assert(m_limitMass1 > b2_epsilon);
0163     b2Assert(m_limitMass2 > b2_epsilon);
0164     b2Assert(m_pulleyMass > b2_epsilon);
0165     m_limitMass1 = 1.0f / m_limitMass1;
0166     m_limitMass2 = 1.0f / m_limitMass2;
0167     m_pulleyMass = 1.0f / m_pulleyMass;
0168 
0169     if (step.warmStarting)
0170     {
0171         // Scale impulses to support variable time steps.
0172         m_impulse *= step.dtRatio;
0173         m_limitImpulse1 *= step.dtRatio;
0174         m_limitImpulse2 *= step.dtRatio;
0175 
0176         // Warm starting.
0177         b2Vec2 P1 = -(m_impulse + m_limitImpulse1) * m_u1;
0178         b2Vec2 P2 = (-m_ratio * m_impulse - m_limitImpulse2) * m_u2;
0179         b1->m_linearVelocity += b1->m_invMass * P1;
0180         b1->m_angularVelocity += b1->m_invI * b2Cross(r1, P1);
0181         b2->m_linearVelocity += b2->m_invMass * P2;
0182         b2->m_angularVelocity += b2->m_invI * b2Cross(r2, P2);
0183     }
0184     else
0185     {
0186         m_impulse = 0.0f;
0187         m_limitImpulse1 = 0.0f;
0188         m_limitImpulse2 = 0.0f;
0189     }
0190 }
0191 
0192 void b2PulleyJoint::SolveVelocityConstraints(const b2TimeStep& step)
0193 {
0194     B2_NOT_USED(step);
0195 
0196     b2Body* b1 = m_bodyA;
0197     b2Body* b2 = m_bodyB;
0198 
0199     b2Vec2 r1 = b2Mul(b1->GetTransform().R, m_localAnchor1 - b1->GetLocalCenter());
0200     b2Vec2 r2 = b2Mul(b2->GetTransform().R, m_localAnchor2 - b2->GetLocalCenter());
0201 
0202     if (m_state == e_atUpperLimit)
0203     {
0204         b2Vec2 v1 = b1->m_linearVelocity + b2Cross(b1->m_angularVelocity, r1);
0205         b2Vec2 v2 = b2->m_linearVelocity + b2Cross(b2->m_angularVelocity, r2);
0206 
0207         qreal Cdot = -b2Dot(m_u1, v1) - m_ratio * b2Dot(m_u2, v2);
0208         qreal impulse = m_pulleyMass * (-Cdot);
0209         qreal oldImpulse = m_impulse;
0210         m_impulse = b2Max(0.0f, m_impulse + impulse);
0211         impulse = m_impulse - oldImpulse;
0212 
0213         b2Vec2 P1 = -impulse * m_u1;
0214         b2Vec2 P2 = -m_ratio * impulse * m_u2;
0215         b1->m_linearVelocity += b1->m_invMass * P1;
0216         b1->m_angularVelocity += b1->m_invI * b2Cross(r1, P1);
0217         b2->m_linearVelocity += b2->m_invMass * P2;
0218         b2->m_angularVelocity += b2->m_invI * b2Cross(r2, P2);
0219     }
0220 
0221     if (m_limitState1 == e_atUpperLimit)
0222     {
0223         b2Vec2 v1 = b1->m_linearVelocity + b2Cross(b1->m_angularVelocity, r1);
0224 
0225         qreal Cdot = -b2Dot(m_u1, v1);
0226         qreal impulse = -m_limitMass1 * Cdot;
0227         qreal oldImpulse = m_limitImpulse1;
0228         m_limitImpulse1 = b2Max(0.0f, m_limitImpulse1 + impulse);
0229         impulse = m_limitImpulse1 - oldImpulse;
0230 
0231         b2Vec2 P1 = -impulse * m_u1;
0232         b1->m_linearVelocity += b1->m_invMass * P1;
0233         b1->m_angularVelocity += b1->m_invI * b2Cross(r1, P1);
0234     }
0235 
0236     if (m_limitState2 == e_atUpperLimit)
0237     {
0238         b2Vec2 v2 = b2->m_linearVelocity + b2Cross(b2->m_angularVelocity, r2);
0239 
0240         qreal Cdot = -b2Dot(m_u2, v2);
0241         qreal impulse = -m_limitMass2 * Cdot;
0242         qreal oldImpulse = m_limitImpulse2;
0243         m_limitImpulse2 = b2Max(0.0f, m_limitImpulse2 + impulse);
0244         impulse = m_limitImpulse2 - oldImpulse;
0245 
0246         b2Vec2 P2 = -impulse * m_u2;
0247         b2->m_linearVelocity += b2->m_invMass * P2;
0248         b2->m_angularVelocity += b2->m_invI * b2Cross(r2, P2);
0249     }
0250 }
0251 
0252 bool b2PulleyJoint::SolvePositionConstraints(qreal baumgarte)
0253 {
0254     B2_NOT_USED(baumgarte);
0255 
0256     b2Body* b1 = m_bodyA;
0257     b2Body* b2 = m_bodyB;
0258 
0259     b2Vec2 s1 = m_groundAnchor1;
0260     b2Vec2 s2 = m_groundAnchor2;
0261 
0262     qreal linearError = 0.0f;
0263 
0264     if (m_state == e_atUpperLimit)
0265     {
0266         b2Vec2 r1 = b2Mul(b1->GetTransform().R, m_localAnchor1 - b1->GetLocalCenter());
0267         b2Vec2 r2 = b2Mul(b2->GetTransform().R, m_localAnchor2 - b2->GetLocalCenter());
0268 
0269         b2Vec2 p1 = b1->m_sweep.c + r1;
0270         b2Vec2 p2 = b2->m_sweep.c + r2;
0271 
0272         // Get the pulley axes.
0273         m_u1 = p1 - s1;
0274         m_u2 = p2 - s2;
0275 
0276         qreal length1 = m_u1.Length();
0277         qreal length2 = m_u2.Length();
0278 
0279         if (length1 > b2_linearSlop)
0280         {
0281             m_u1 *= 1.0f / length1;
0282         }
0283         else
0284         {
0285             m_u1.SetZero();
0286         }
0287 
0288         if (length2 > b2_linearSlop)
0289         {
0290             m_u2 *= 1.0f / length2;
0291         }
0292         else
0293         {
0294             m_u2.SetZero();
0295         }
0296 
0297         qreal C = m_constant - length1 - m_ratio * length2;
0298         linearError = b2Max(linearError, -C);
0299 
0300         C = b2Clamp(C + b2_linearSlop, -b2_maxLinearCorrection, 0.0f);
0301         qreal impulse = -m_pulleyMass * C;
0302 
0303         b2Vec2 P1 = -impulse * m_u1;
0304         b2Vec2 P2 = -m_ratio * impulse * m_u2;
0305 
0306         b1->m_sweep.c += b1->m_invMass * P1;
0307         b1->m_sweep.a += b1->m_invI * b2Cross(r1, P1);
0308         b2->m_sweep.c += b2->m_invMass * P2;
0309         b2->m_sweep.a += b2->m_invI * b2Cross(r2, P2);
0310 
0311         b1->SynchronizeTransform();
0312         b2->SynchronizeTransform();
0313     }
0314 
0315     if (m_limitState1 == e_atUpperLimit)
0316     {
0317         b2Vec2 r1 = b2Mul(b1->GetTransform().R, m_localAnchor1 - b1->GetLocalCenter());
0318         b2Vec2 p1 = b1->m_sweep.c + r1;
0319 
0320         m_u1 = p1 - s1;
0321         qreal length1 = m_u1.Length();
0322 
0323         if (length1 > b2_linearSlop)
0324         {
0325             m_u1 *= 1.0f / length1;
0326         }
0327         else
0328         {
0329             m_u1.SetZero();
0330         }
0331 
0332         qreal C = m_maxLength1 - length1;
0333         linearError = b2Max(linearError, -C);
0334         C = b2Clamp(C + b2_linearSlop, -b2_maxLinearCorrection, 0.0f);
0335         qreal impulse = -m_limitMass1 * C;
0336 
0337         b2Vec2 P1 = -impulse * m_u1;
0338         b1->m_sweep.c += b1->m_invMass * P1;
0339         b1->m_sweep.a += b1->m_invI * b2Cross(r1, P1);
0340 
0341         b1->SynchronizeTransform();
0342     }
0343 
0344     if (m_limitState2 == e_atUpperLimit)
0345     {
0346         b2Vec2 r2 = b2Mul(b2->GetTransform().R, m_localAnchor2 - b2->GetLocalCenter());
0347         b2Vec2 p2 = b2->m_sweep.c + r2;
0348 
0349         m_u2 = p2 - s2;
0350         qreal length2 = m_u2.Length();
0351 
0352         if (length2 > b2_linearSlop)
0353         {
0354             m_u2 *= 1.0f / length2;
0355         }
0356         else
0357         {
0358             m_u2.SetZero();
0359         }
0360 
0361         qreal C = m_maxLength2 - length2;
0362         linearError = b2Max(linearError, -C);
0363         C = b2Clamp(C + b2_linearSlop, -b2_maxLinearCorrection, 0.0f);
0364         qreal impulse = -m_limitMass2 * C;
0365 
0366         b2Vec2 P2 = -impulse * m_u2;
0367         b2->m_sweep.c += b2->m_invMass * P2;
0368         b2->m_sweep.a += b2->m_invI * b2Cross(r2, P2);
0369 
0370         b2->SynchronizeTransform();
0371     }
0372 
0373     return linearError < b2_linearSlop;
0374 }
0375 
0376 b2Vec2 b2PulleyJoint::GetAnchorA() const
0377 {
0378     return m_bodyA->GetWorldPoint(m_localAnchor1);
0379 }
0380 
0381 b2Vec2 b2PulleyJoint::GetAnchorB() const
0382 {
0383     return m_bodyB->GetWorldPoint(m_localAnchor2);
0384 }
0385 
0386 b2Vec2 b2PulleyJoint::GetReactionForce(qreal inv_dt) const
0387 {
0388     b2Vec2 P = m_impulse * m_u2;
0389     return inv_dt * P;
0390 }
0391 
0392 qreal b2PulleyJoint::GetReactionTorque(qreal inv_dt) const
0393 {
0394     B2_NOT_USED(inv_dt);
0395     return 0.0f;
0396 }
0397 
0398 b2Vec2 b2PulleyJoint::GetGroundAnchorA() const
0399 {
0400     return m_groundAnchor1;
0401 }
0402 
0403 b2Vec2 b2PulleyJoint::GetGroundAnchorB() const
0404 {
0405     return m_groundAnchor2;
0406 }
0407 
0408 qreal b2PulleyJoint::GetLength1() const
0409 {
0410     b2Vec2 p = m_bodyA->GetWorldPoint(m_localAnchor1);
0411     b2Vec2 s = m_groundAnchor1;
0412     b2Vec2 d = p - s;
0413     return d.Length();
0414 }
0415 
0416 qreal b2PulleyJoint::GetLength2() const
0417 {
0418     b2Vec2 p = m_bodyB->GetWorldPoint(m_localAnchor2);
0419     b2Vec2 s = m_groundAnchor2;
0420     b2Vec2 d = p - s;
0421     return d.Length();
0422 }
0423 
0424 qreal b2PulleyJoint::GetRatio() const
0425 {
0426     return m_ratio;
0427 }