File indexing completed on 2025-08-03 03:50:00

0001 /*
0002 * Copyright (c) 2006-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/b2RevoluteJoint.h>
0020 #include <Box2D/Dynamics/b2Body.h>
0021 #include <Box2D/Dynamics/b2TimeStep.h>
0022 
0023 // Point-to-point constraint
0024 // C = p2 - p1
0025 // Cdot = v2 - v1
0026 //      = v2 + cross(w2, r2) - v1 - cross(w1, r1)
0027 // J = [-I -r1_skew I r2_skew ]
0028 // Identity used:
0029 // w k % (rx i + ry j) = w * (-ry i + rx j)
0030 
0031 // Motor constraint
0032 // Cdot = w2 - w1
0033 // J = [0 0 -1 0 0 1]
0034 // K = invI1 + invI2
0035 
0036 void b2RevoluteJointDef::Initialize(b2Body* b1, b2Body* b2, const b2Vec2& anchor)
0037 {
0038     bodyA = b1;
0039     bodyB = b2;
0040     localAnchorA = bodyA->GetLocalPoint(anchor);
0041     localAnchorB = bodyB->GetLocalPoint(anchor);
0042     referenceAngle = bodyB->GetAngle() - bodyA->GetAngle();
0043 }
0044 
0045 b2RevoluteJoint::b2RevoluteJoint(const b2RevoluteJointDef* def)
0046 : b2Joint(def)
0047 {
0048     m_localAnchor1 = def->localAnchorA;
0049     m_localAnchor2 = def->localAnchorB;
0050     m_referenceAngle = def->referenceAngle;
0051 
0052     m_impulse.SetZero();
0053     m_motorImpulse = 0.0f;
0054 
0055     m_lowerAngle = def->lowerAngle;
0056     m_upperAngle = def->upperAngle;
0057     m_maxMotorTorque = def->maxMotorTorque;
0058     m_motorSpeed = def->motorSpeed;
0059     m_enableLimit = def->enableLimit;
0060     m_enableMotor = def->enableMotor;
0061     m_limitState = e_inactiveLimit;
0062 }
0063 
0064 void b2RevoluteJoint::InitVelocityConstraints(const b2TimeStep& step)
0065 {
0066     b2Body* b1 = m_bodyA;
0067     b2Body* b2 = m_bodyB;
0068 
0069     if (m_enableMotor || m_enableLimit)
0070     {
0071         // You cannot create a rotation limit between bodies that
0072         // both have fixed rotation.
0073         b2Assert(b1->m_invI > 0.0f || b2->m_invI > 0.0f);
0074     }
0075 
0076     // Compute the effective mass matrix.
0077     b2Vec2 r1 = b2Mul(b1->GetTransform().R, m_localAnchor1 - b1->GetLocalCenter());
0078     b2Vec2 r2 = b2Mul(b2->GetTransform().R, m_localAnchor2 - b2->GetLocalCenter());
0079 
0080     // J = [-I -r1_skew I r2_skew]
0081     //     [ 0       -1 0       1]
0082     // r_skew = [-ry; rx]
0083 
0084     // Matlab
0085     // K = [ m1+r1y^2*i1+m2+r2y^2*i2,  -r1y*i1*r1x-r2y*i2*r2x,          -r1y*i1-r2y*i2]
0086     //     [  -r1y*i1*r1x-r2y*i2*r2x, m1+r1x^2*i1+m2+r2x^2*i2,           r1x*i1+r2x*i2]
0087     //     [          -r1y*i1-r2y*i2,           r1x*i1+r2x*i2,                   i1+i2]
0088 
0089     qreal m1 = b1->m_invMass, m2 = b2->m_invMass;
0090     qreal i1 = b1->m_invI, i2 = b2->m_invI;
0091 
0092     m_mass.col1.x = m1 + m2 + r1.y * r1.y * i1 + r2.y * r2.y * i2;
0093     m_mass.col2.x = -r1.y * r1.x * i1 - r2.y * r2.x * i2;
0094     m_mass.col3.x = -r1.y * i1 - r2.y * i2;
0095     m_mass.col1.y = m_mass.col2.x;
0096     m_mass.col2.y = m1 + m2 + r1.x * r1.x * i1 + r2.x * r2.x * i2;
0097     m_mass.col3.y = r1.x * i1 + r2.x * i2;
0098     m_mass.col1.z = m_mass.col3.x;
0099     m_mass.col2.z = m_mass.col3.y;
0100     m_mass.col3.z = i1 + i2;
0101 
0102     m_motorMass = i1 + i2;
0103     if (m_motorMass > 0.0f)
0104     {
0105         m_motorMass = 1.0f / m_motorMass;
0106     }
0107 
0108     if (m_enableMotor == false)
0109     {
0110         m_motorImpulse = 0.0f;
0111     }
0112 
0113     if (m_enableLimit)
0114     {
0115         qreal jointAngle = b2->m_sweep.a - b1->m_sweep.a - m_referenceAngle;
0116         if (b2Abs(m_upperAngle - m_lowerAngle) < 2.0f * b2_angularSlop)
0117         {
0118             m_limitState = e_equalLimits;
0119         }
0120         else if (jointAngle <= m_lowerAngle)
0121         {
0122             if (m_limitState != e_atLowerLimit)
0123             {
0124                 m_impulse.z = 0.0f;
0125             }
0126             m_limitState = e_atLowerLimit;
0127         }
0128         else if (jointAngle >= m_upperAngle)
0129         {
0130             if (m_limitState != e_atUpperLimit)
0131             {
0132                 m_impulse.z = 0.0f;
0133             }
0134             m_limitState = e_atUpperLimit;
0135         }
0136         else
0137         {
0138             m_limitState = e_inactiveLimit;
0139             m_impulse.z = 0.0f;
0140         }
0141     }
0142     else
0143     {
0144         m_limitState = e_inactiveLimit;
0145     }
0146 
0147     if (step.warmStarting)
0148     {
0149         // Scale impulses to support a variable time step.
0150         m_impulse *= step.dtRatio;
0151         m_motorImpulse *= step.dtRatio;
0152 
0153         b2Vec2 P(m_impulse.x, m_impulse.y);
0154 
0155         b1->m_linearVelocity -= m1 * P;
0156         b1->m_angularVelocity -= i1 * (b2Cross(r1, P) + m_motorImpulse + m_impulse.z);
0157 
0158         b2->m_linearVelocity += m2 * P;
0159         b2->m_angularVelocity += i2 * (b2Cross(r2, P) + m_motorImpulse + m_impulse.z);
0160     }
0161     else
0162     {
0163         m_impulse.SetZero();
0164         m_motorImpulse = 0.0f;
0165     }
0166 }
0167 
0168 void b2RevoluteJoint::SolveVelocityConstraints(const b2TimeStep& step)
0169 {
0170     b2Body* b1 = m_bodyA;
0171     b2Body* b2 = m_bodyB;
0172 
0173     b2Vec2 v1 = b1->m_linearVelocity;
0174     qreal w1 = b1->m_angularVelocity;
0175     b2Vec2 v2 = b2->m_linearVelocity;
0176     qreal w2 = b2->m_angularVelocity;
0177 
0178     qreal m1 = b1->m_invMass, m2 = b2->m_invMass;
0179     qreal i1 = b1->m_invI, i2 = b2->m_invI;
0180 
0181     // Solve motor constraint.
0182     if (m_enableMotor && m_limitState != e_equalLimits)
0183     {
0184         qreal Cdot = w2 - w1 - m_motorSpeed;
0185         qreal impulse = m_motorMass * (-Cdot);
0186         qreal oldImpulse = m_motorImpulse;
0187         qreal maxImpulse = step.dt * m_maxMotorTorque;
0188         m_motorImpulse = b2Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse);
0189         impulse = m_motorImpulse - oldImpulse;
0190 
0191         w1 -= i1 * impulse;
0192         w2 += i2 * impulse;
0193     }
0194 
0195     // Solve limit constraint.
0196     if (m_enableLimit && m_limitState != e_inactiveLimit)
0197     {
0198         b2Vec2 r1 = b2Mul(b1->GetTransform().R, m_localAnchor1 - b1->GetLocalCenter());
0199         b2Vec2 r2 = b2Mul(b2->GetTransform().R, m_localAnchor2 - b2->GetLocalCenter());
0200 
0201         // Solve point-to-point constraint
0202         b2Vec2 Cdot1 = v2 + b2Cross(w2, r2) - v1 - b2Cross(w1, r1);
0203         qreal Cdot2 = w2 - w1;
0204         b2Vec3 Cdot(Cdot1.x, Cdot1.y, Cdot2);
0205 
0206         b2Vec3 impulse = m_mass.Solve33(-Cdot);
0207 
0208         if (m_limitState == e_equalLimits)
0209         {
0210             m_impulse += impulse;
0211         }
0212         else if (m_limitState == e_atLowerLimit)
0213         {
0214             qreal newImpulse = m_impulse.z + impulse.z;
0215             if (newImpulse < 0.0f)
0216             {
0217                 b2Vec2 reduced = m_mass.Solve22(-Cdot1);
0218                 impulse.x = reduced.x;
0219                 impulse.y = reduced.y;
0220                 impulse.z = -m_impulse.z;
0221                 m_impulse.x += reduced.x;
0222                 m_impulse.y += reduced.y;
0223                 m_impulse.z = 0.0f;
0224             }
0225         }
0226         else if (m_limitState == e_atUpperLimit)
0227         {
0228             qreal newImpulse = m_impulse.z + impulse.z;
0229             if (newImpulse > 0.0f)
0230             {
0231                 b2Vec2 reduced = m_mass.Solve22(-Cdot1);
0232                 impulse.x = reduced.x;
0233                 impulse.y = reduced.y;
0234                 impulse.z = -m_impulse.z;
0235                 m_impulse.x += reduced.x;
0236                 m_impulse.y += reduced.y;
0237                 m_impulse.z = 0.0f;
0238             }
0239         }
0240 
0241         b2Vec2 P(impulse.x, impulse.y);
0242 
0243         v1 -= m1 * P;
0244         w1 -= i1 * (b2Cross(r1, P) + impulse.z);
0245 
0246         v2 += m2 * P;
0247         w2 += i2 * (b2Cross(r2, P) + impulse.z);
0248     }
0249     else
0250     {
0251         b2Vec2 r1 = b2Mul(b1->GetTransform().R, m_localAnchor1 - b1->GetLocalCenter());
0252         b2Vec2 r2 = b2Mul(b2->GetTransform().R, m_localAnchor2 - b2->GetLocalCenter());
0253 
0254         // Solve point-to-point constraint
0255         b2Vec2 Cdot = v2 + b2Cross(w2, r2) - v1 - b2Cross(w1, r1);
0256         b2Vec2 impulse = m_mass.Solve22(-Cdot);
0257 
0258         m_impulse.x += impulse.x;
0259         m_impulse.y += impulse.y;
0260 
0261         v1 -= m1 * impulse;
0262         w1 -= i1 * b2Cross(r1, impulse);
0263 
0264         v2 += m2 * impulse;
0265         w2 += i2 * b2Cross(r2, impulse);
0266     }
0267 
0268     b1->m_linearVelocity = v1;
0269     b1->m_angularVelocity = w1;
0270     b2->m_linearVelocity = v2;
0271     b2->m_angularVelocity = w2;
0272 }
0273 
0274 bool b2RevoluteJoint::SolvePositionConstraints(qreal baumgarte)
0275 {
0276     // TODO_ERIN block solve with limit.
0277 
0278     B2_NOT_USED(baumgarte);
0279 
0280     b2Body* b1 = m_bodyA;
0281     b2Body* b2 = m_bodyB;
0282 
0283     qreal angularError = 0.0f;
0284     qreal positionError = 0.0f;
0285 
0286     // Solve angular limit constraint.
0287     if (m_enableLimit && m_limitState != e_inactiveLimit)
0288     {
0289         qreal angle = b2->m_sweep.a - b1->m_sweep.a - m_referenceAngle;
0290         qreal limitImpulse = 0.0f;
0291 
0292         if (m_limitState == e_equalLimits)
0293         {
0294             // Prevent large angular corrections
0295             qreal C = b2Clamp(angle - m_lowerAngle, -b2_maxAngularCorrection, b2_maxAngularCorrection);
0296             limitImpulse = -m_motorMass * C;
0297             angularError = b2Abs(C);
0298         }
0299         else if (m_limitState == e_atLowerLimit)
0300         {
0301             qreal C = angle - m_lowerAngle;
0302             angularError = -C;
0303 
0304             // Prevent large angular corrections and allow some slop.
0305             C = b2Clamp(C + b2_angularSlop, -b2_maxAngularCorrection, 0.0f);
0306             limitImpulse = -m_motorMass * C;
0307         }
0308         else if (m_limitState == e_atUpperLimit)
0309         {
0310             qreal C = angle - m_upperAngle;
0311             angularError = C;
0312 
0313             // Prevent large angular corrections and allow some slop.
0314             C = b2Clamp(C - b2_angularSlop, 0.0f, b2_maxAngularCorrection);
0315             limitImpulse = -m_motorMass * C;
0316         }
0317 
0318         b1->m_sweep.a -= b1->m_invI * limitImpulse;
0319         b2->m_sweep.a += b2->m_invI * limitImpulse;
0320 
0321         b1->SynchronizeTransform();
0322         b2->SynchronizeTransform();
0323     }
0324 
0325     // Solve point-to-point constraint.
0326     {
0327         b2Vec2 r1 = b2Mul(b1->GetTransform().R, m_localAnchor1 - b1->GetLocalCenter());
0328         b2Vec2 r2 = b2Mul(b2->GetTransform().R, m_localAnchor2 - b2->GetLocalCenter());
0329 
0330         b2Vec2 C = b2->m_sweep.c + r2 - b1->m_sweep.c - r1;
0331         positionError = C.Length();
0332 
0333         qreal invMass1 = b1->m_invMass, invMass2 = b2->m_invMass;
0334         qreal invI1 = b1->m_invI, invI2 = b2->m_invI;
0335 
0336         // Handle large detachment.
0337         const qreal k_allowedStretch = 10.0f * b2_linearSlop;
0338         if (C.LengthSquared() > k_allowedStretch * k_allowedStretch)
0339         {
0340             // Use a particle solution (no rotation).
0341             b2Vec2 u = C; u.Normalize();
0342             qreal m = invMass1 + invMass2;
0343             if (m > 0.0f)
0344             {
0345                 m = 1.0f / m;
0346             }
0347             b2Vec2 impulse = m * (-C);
0348             const qreal k_beta = 0.5f;
0349             b1->m_sweep.c -= k_beta * invMass1 * impulse;
0350             b2->m_sweep.c += k_beta * invMass2 * impulse;
0351 
0352             C = b2->m_sweep.c + r2 - b1->m_sweep.c - r1;
0353         }
0354 
0355         b2Mat22 K1;
0356         K1.col1.x = invMass1 + invMass2;    K1.col2.x = 0.0f;
0357         K1.col1.y = 0.0f;                   K1.col2.y = invMass1 + invMass2;
0358 
0359         b2Mat22 K2;
0360         K2.col1.x =  invI1 * r1.y * r1.y;   K2.col2.x = -invI1 * r1.x * r1.y;
0361         K2.col1.y = -invI1 * r1.x * r1.y;   K2.col2.y =  invI1 * r1.x * r1.x;
0362 
0363         b2Mat22 K3;
0364         K3.col1.x =  invI2 * r2.y * r2.y;   K3.col2.x = -invI2 * r2.x * r2.y;
0365         K3.col1.y = -invI2 * r2.x * r2.y;   K3.col2.y =  invI2 * r2.x * r2.x;
0366 
0367         b2Mat22 K = K1 + K2 + K3;
0368         b2Vec2 impulse = K.Solve(-C);
0369 
0370         b1->m_sweep.c -= b1->m_invMass * impulse;
0371         b1->m_sweep.a -= b1->m_invI * b2Cross(r1, impulse);
0372 
0373         b2->m_sweep.c += b2->m_invMass * impulse;
0374         b2->m_sweep.a += b2->m_invI * b2Cross(r2, impulse);
0375 
0376         b1->SynchronizeTransform();
0377         b2->SynchronizeTransform();
0378     }
0379     
0380     return positionError <= b2_linearSlop && angularError <= b2_angularSlop;
0381 }
0382 
0383 b2Vec2 b2RevoluteJoint::GetAnchorA() const
0384 {
0385     return m_bodyA->GetWorldPoint(m_localAnchor1);
0386 }
0387 
0388 b2Vec2 b2RevoluteJoint::GetAnchorB() const
0389 {
0390     return m_bodyB->GetWorldPoint(m_localAnchor2);
0391 }
0392 
0393 b2Vec2 b2RevoluteJoint::GetReactionForce(qreal inv_dt) const
0394 {
0395     b2Vec2 P(m_impulse.x, m_impulse.y);
0396     return inv_dt * P;
0397 }
0398 
0399 qreal b2RevoluteJoint::GetReactionTorque(qreal inv_dt) const
0400 {
0401     return inv_dt * m_impulse.z;
0402 }
0403 
0404 qreal b2RevoluteJoint::GetJointAngle() const
0405 {
0406     b2Body* b1 = m_bodyA;
0407     b2Body* b2 = m_bodyB;
0408     return b2->m_sweep.a - b1->m_sweep.a - m_referenceAngle;
0409 }
0410 
0411 qreal b2RevoluteJoint::GetJointSpeed() const
0412 {
0413     b2Body* b1 = m_bodyA;
0414     b2Body* b2 = m_bodyB;
0415     return b2->m_angularVelocity - b1->m_angularVelocity;
0416 }
0417 
0418 bool b2RevoluteJoint::IsMotorEnabled() const
0419 {
0420     return m_enableMotor;
0421 }
0422 
0423 void b2RevoluteJoint::EnableMotor(bool flag)
0424 {
0425     m_bodyA->SetAwake(true);
0426     m_bodyB->SetAwake(true);
0427     m_enableMotor = flag;
0428 }
0429 
0430 qreal b2RevoluteJoint::GetMotorTorque(qreal inv_dt) const
0431 {
0432     return inv_dt * m_motorImpulse;
0433 }
0434 
0435 void b2RevoluteJoint::SetMotorSpeed(qreal speed)
0436 {
0437     m_bodyA->SetAwake(true);
0438     m_bodyB->SetAwake(true);
0439     m_motorSpeed = speed;
0440 }
0441 
0442 void b2RevoluteJoint::SetMaxMotorTorque(qreal torque)
0443 {
0444     m_bodyA->SetAwake(true);
0445     m_bodyB->SetAwake(true);
0446     m_maxMotorTorque = torque;
0447 }
0448 
0449 bool b2RevoluteJoint::IsLimitEnabled() const
0450 {
0451     return m_enableLimit;
0452 }
0453 
0454 void b2RevoluteJoint::EnableLimit(bool flag)
0455 {
0456     m_bodyA->SetAwake(true);
0457     m_bodyB->SetAwake(true);
0458     m_enableLimit = flag;
0459 }
0460 
0461 qreal b2RevoluteJoint::GetLowerLimit() const
0462 {
0463     return m_lowerAngle;
0464 }
0465 
0466 qreal b2RevoluteJoint::GetUpperLimit() const
0467 {
0468     return m_upperAngle;
0469 }
0470 
0471 void b2RevoluteJoint::SetLimits(qreal lower, qreal upper)
0472 {
0473     b2Assert(lower <= upper);
0474     m_bodyA->SetAwake(true);
0475     m_bodyB->SetAwake(true);
0476     m_lowerAngle = lower;
0477     m_upperAngle = upper;
0478 }