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

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/b2GearJoint.h>
0020 #include <Box2D/Dynamics/Joints/b2RevoluteJoint.h>
0021 #include <Box2D/Dynamics/Joints/b2PrismaticJoint.h>
0022 #include <Box2D/Dynamics/b2Body.h>
0023 #include <Box2D/Dynamics/b2TimeStep.h>
0024 
0025 // Gear Joint:
0026 // C0 = (coordinate1 + ratio * coordinate2)_initial
0027 // C = C0 - (cordinate1 + ratio * coordinate2) = 0
0028 // Cdot = -(Cdot1 + ratio * Cdot2)
0029 // J = -[J1 ratio * J2]
0030 // K = J * invM * JT
0031 //   = J1 * invM1 * J1T + ratio * ratio * J2 * invM2 * J2T
0032 //
0033 // Revolute:
0034 // coordinate = rotation
0035 // Cdot = angularVelocity
0036 // J = [0 0 1]
0037 // K = J * invM * JT = invI
0038 //
0039 // Prismatic:
0040 // coordinate = dot(p - pg, ug)
0041 // Cdot = dot(v + cross(w, r), ug)
0042 // J = [ug cross(r, ug)]
0043 // K = J * invM * JT = invMass + invI * cross(r, ug)^2
0044 
0045 b2GearJoint::b2GearJoint(const b2GearJointDef* def)
0046 : b2Joint(def)
0047 {
0048     b2JointType type1 = def->joint1->GetType();
0049     b2JointType type2 = def->joint2->GetType();
0050 
0051     b2Assert(type1 == e_revoluteJoint || type1 == e_prismaticJoint);
0052     b2Assert(type2 == e_revoluteJoint || type2 == e_prismaticJoint);
0053     b2Assert(def->joint1->GetBodyA()->GetType() == b2_staticBody);
0054     b2Assert(def->joint2->GetBodyA()->GetType() == b2_staticBody);
0055 
0056     m_revolute1 = NULL;
0057     m_prismatic1 = NULL;
0058     m_revolute2 = NULL;
0059     m_prismatic2 = NULL;
0060 
0061     qreal coordinate1, coordinate2;
0062 
0063     m_ground1 = def->joint1->GetBodyA();
0064     m_bodyA = def->joint1->GetBodyB();
0065     if (type1 == e_revoluteJoint)
0066     {
0067         m_revolute1 = (b2RevoluteJoint*)def->joint1;
0068         m_groundAnchor1 = m_revolute1->m_localAnchor1;
0069         m_localAnchor1 = m_revolute1->m_localAnchor2;
0070         coordinate1 = m_revolute1->GetJointAngle();
0071     }
0072     else
0073     {
0074         m_prismatic1 = (b2PrismaticJoint*)def->joint1;
0075         m_groundAnchor1 = m_prismatic1->m_localAnchor1;
0076         m_localAnchor1 = m_prismatic1->m_localAnchor2;
0077         coordinate1 = m_prismatic1->GetJointTranslation();
0078     }
0079 
0080     m_ground2 = def->joint2->GetBodyA();
0081     m_bodyB = def->joint2->GetBodyB();
0082     if (type2 == e_revoluteJoint)
0083     {
0084         m_revolute2 = (b2RevoluteJoint*)def->joint2;
0085         m_groundAnchor2 = m_revolute2->m_localAnchor1;
0086         m_localAnchor2 = m_revolute2->m_localAnchor2;
0087         coordinate2 = m_revolute2->GetJointAngle();
0088     }
0089     else
0090     {
0091         m_prismatic2 = (b2PrismaticJoint*)def->joint2;
0092         m_groundAnchor2 = m_prismatic2->m_localAnchor1;
0093         m_localAnchor2 = m_prismatic2->m_localAnchor2;
0094         coordinate2 = m_prismatic2->GetJointTranslation();
0095     }
0096 
0097     m_ratio = def->ratio;
0098 
0099     m_constant = coordinate1 + m_ratio * coordinate2;
0100 
0101     m_impulse = 0.0f;
0102 }
0103 
0104 void b2GearJoint::InitVelocityConstraints(const b2TimeStep& step)
0105 {
0106     b2Body* g1 = m_ground1;
0107     b2Body* g2 = m_ground2;
0108     b2Body* b1 = m_bodyA;
0109     b2Body* b2 = m_bodyB;
0110 
0111     qreal K = 0.0f;
0112     m_J.SetZero();
0113 
0114     if (m_revolute1)
0115     {
0116         m_J.angularA = -1.0f;
0117         K += b1->m_invI;
0118     }
0119     else
0120     {
0121         b2Vec2 ug = b2Mul(g1->GetTransform().R, m_prismatic1->m_localXAxis1);
0122         b2Vec2 r = b2Mul(b1->GetTransform().R, m_localAnchor1 - b1->GetLocalCenter());
0123         qreal crug = b2Cross(r, ug);
0124         m_J.linearA = -ug;
0125         m_J.angularA = -crug;
0126         K += b1->m_invMass + b1->m_invI * crug * crug;
0127     }
0128 
0129     if (m_revolute2)
0130     {
0131         m_J.angularB = -m_ratio;
0132         K += m_ratio * m_ratio * b2->m_invI;
0133     }
0134     else
0135     {
0136         b2Vec2 ug = b2Mul(g2->GetTransform().R, m_prismatic2->m_localXAxis1);
0137         b2Vec2 r = b2Mul(b2->GetTransform().R, m_localAnchor2 - b2->GetLocalCenter());
0138         qreal crug = b2Cross(r, ug);
0139         m_J.linearB = -m_ratio * ug;
0140         m_J.angularB = -m_ratio * crug;
0141         K += m_ratio * m_ratio * (b2->m_invMass + b2->m_invI * crug * crug);
0142     }
0143 
0144     // Compute effective mass.
0145     m_mass = K > 0.0f ? 1.0f / K : 0.0f;
0146 
0147     if (step.warmStarting)
0148     {
0149         // Warm starting.
0150         b1->m_linearVelocity += b1->m_invMass * m_impulse * m_J.linearA;
0151         b1->m_angularVelocity += b1->m_invI * m_impulse * m_J.angularA;
0152         b2->m_linearVelocity += b2->m_invMass * m_impulse * m_J.linearB;
0153         b2->m_angularVelocity += b2->m_invI * m_impulse * m_J.angularB;
0154     }
0155     else
0156     {
0157         m_impulse = 0.0f;
0158     }
0159 }
0160 
0161 void b2GearJoint::SolveVelocityConstraints(const b2TimeStep& step)
0162 {
0163     B2_NOT_USED(step);
0164 
0165     b2Body* b1 = m_bodyA;
0166     b2Body* b2 = m_bodyB;
0167 
0168     qreal Cdot = m_J.Compute(   b1->m_linearVelocity, b1->m_angularVelocity,
0169                                 b2->m_linearVelocity, b2->m_angularVelocity);
0170 
0171     qreal impulse = m_mass * (-Cdot);
0172     m_impulse += impulse;
0173 
0174     b1->m_linearVelocity += b1->m_invMass * impulse * m_J.linearA;
0175     b1->m_angularVelocity += b1->m_invI * impulse * m_J.angularA;
0176     b2->m_linearVelocity += b2->m_invMass * impulse * m_J.linearB;
0177     b2->m_angularVelocity += b2->m_invI * impulse * m_J.angularB;
0178 }
0179 
0180 bool b2GearJoint::SolvePositionConstraints(qreal baumgarte)
0181 {
0182     B2_NOT_USED(baumgarte);
0183     
0184     qreal linearError = 0.0f;
0185 
0186     b2Body* b1 = m_bodyA;
0187     b2Body* b2 = m_bodyB;
0188 
0189     qreal coordinate1, coordinate2;
0190     if (m_revolute1)
0191     {
0192         coordinate1 = m_revolute1->GetJointAngle();
0193     }
0194     else
0195     {
0196         coordinate1 = m_prismatic1->GetJointTranslation();
0197     }
0198 
0199     if (m_revolute2)
0200     {
0201         coordinate2 = m_revolute2->GetJointAngle();
0202     }
0203     else
0204     {
0205         coordinate2 = m_prismatic2->GetJointTranslation();
0206     }
0207 
0208     qreal C = m_constant - (coordinate1 + m_ratio * coordinate2);
0209 
0210     qreal impulse = m_mass * (-C);
0211 
0212     b1->m_sweep.c += b1->m_invMass * impulse * m_J.linearA;
0213     b1->m_sweep.a += b1->m_invI * impulse * m_J.angularA;
0214     b2->m_sweep.c += b2->m_invMass * impulse * m_J.linearB;
0215     b2->m_sweep.a += b2->m_invI * impulse * m_J.angularB;
0216 
0217     b1->SynchronizeTransform();
0218     b2->SynchronizeTransform();
0219 
0220     // TODO_ERIN not implemented
0221     return linearError < b2_linearSlop;
0222 }
0223 
0224 b2Vec2 b2GearJoint::GetAnchorA() const
0225 {
0226     return m_bodyA->GetWorldPoint(m_localAnchor1);
0227 }
0228 
0229 b2Vec2 b2GearJoint::GetAnchorB() const
0230 {
0231     return m_bodyB->GetWorldPoint(m_localAnchor2);
0232 }
0233 
0234 b2Vec2 b2GearJoint::GetReactionForce(qreal inv_dt) const
0235 {
0236     // TODO_ERIN not tested
0237     b2Vec2 P = m_impulse * m_J.linearB;
0238     return inv_dt * P;
0239 }
0240 
0241 qreal b2GearJoint::GetReactionTorque(qreal inv_dt) const
0242 {
0243     // TODO_ERIN not tested
0244     b2Vec2 r = b2Mul(m_bodyB->GetTransform().R, m_localAnchor2 - m_bodyB->GetLocalCenter());
0245     b2Vec2 P = m_impulse * m_J.linearB;
0246     qreal L = m_impulse * m_J.angularB - b2Cross(r, P);
0247     return inv_dt * L;
0248 }
0249 
0250 void b2GearJoint::SetRatio(qreal ratio)
0251 {
0252     b2Assert(b2IsValid(ratio));
0253     m_ratio = ratio;
0254 }
0255 
0256 qreal b2GearJoint::GetRatio() const
0257 {
0258     return m_ratio;
0259 }