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

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/b2PrismaticJoint.h>
0020 #include <Box2D/Dynamics/b2Body.h>
0021 #include <Box2D/Dynamics/b2TimeStep.h>
0022 
0023 // Linear constraint (point-to-line)
0024 // d = p2 - p1 = x2 + r2 - x1 - r1
0025 // C = dot(perp, d)
0026 // Cdot = dot(d, cross(w1, perp)) + dot(perp, v2 + cross(w2, r2) - v1 - cross(w1, r1))
0027 //      = -dot(perp, v1) - dot(cross(d + r1, perp), w1) + dot(perp, v2) + dot(cross(r2, perp), v2)
0028 // J = [-perp, -cross(d + r1, perp), perp, cross(r2,perp)]
0029 //
0030 // Angular constraint
0031 // C = a2 - a1 + a_initial
0032 // Cdot = w2 - w1
0033 // J = [0 0 -1 0 0 1]
0034 //
0035 // K = J * invM * JT
0036 //
0037 // J = [-a -s1 a s2]
0038 //     [0  -1  0  1]
0039 // a = perp
0040 // s1 = cross(d + r1, a) = cross(p2 - x1, a)
0041 // s2 = cross(r2, a) = cross(p2 - x2, a)
0042 
0043 
0044 // Motor/Limit linear constraint
0045 // C = dot(ax1, d)
0046 // Cdot = = -dot(ax1, v1) - dot(cross(d + r1, ax1), w1) + dot(ax1, v2) + dot(cross(r2, ax1), v2)
0047 // J = [-ax1 -cross(d+r1,ax1) ax1 cross(r2,ax1)]
0048 
0049 // Block Solver
0050 // We develop a block solver that includes the joint limit. This makes the limit stiff (inelastic) even
0051 // when the mass has poor distribution (leading to large torques about the joint anchor points).
0052 //
0053 // The Jacobian has 3 rows:
0054 // J = [-uT -s1 uT s2] // linear
0055 //     [0   -1   0  1] // angular
0056 //     [-vT -a1 vT a2] // limit
0057 //
0058 // u = perp
0059 // v = axis
0060 // s1 = cross(d + r1, u), s2 = cross(r2, u)
0061 // a1 = cross(d + r1, v), a2 = cross(r2, v)
0062 
0063 // M * (v2 - v1) = JT * df
0064 // J * v2 = bias
0065 //
0066 // v2 = v1 + invM * JT * df
0067 // J * (v1 + invM * JT * df) = bias
0068 // K * df = bias - J * v1 = -Cdot
0069 // K = J * invM * JT
0070 // Cdot = J * v1 - bias
0071 //
0072 // Now solve for f2.
0073 // df = f2 - f1
0074 // K * (f2 - f1) = -Cdot
0075 // f2 = invK * (-Cdot) + f1
0076 //
0077 // Clamp accumulated limit impulse.
0078 // lower: f2(3) = max(f2(3), 0)
0079 // upper: f2(3) = min(f2(3), 0)
0080 //
0081 // Solve for correct f2(1:2)
0082 // K(1:2, 1:2) * f2(1:2) = -Cdot(1:2) - K(1:2,3) * f2(3) + K(1:2,1:3) * f1
0083 //                       = -Cdot(1:2) - K(1:2,3) * f2(3) + K(1:2,1:2) * f1(1:2) + K(1:2,3) * f1(3)
0084 // K(1:2, 1:2) * f2(1:2) = -Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3)) + K(1:2,1:2) * f1(1:2)
0085 // f2(1:2) = invK(1:2,1:2) * (-Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3))) + f1(1:2)
0086 //
0087 // Now compute impulse to be applied:
0088 // df = f2 - f1
0089 
0090 void b2PrismaticJointDef::Initialize(b2Body* b1, b2Body* b2, const b2Vec2& anchor, const b2Vec2& axis)
0091 {
0092     bodyA = b1;
0093     bodyB = b2;
0094     localAnchorA = bodyA->GetLocalPoint(anchor);
0095     localAnchorB = bodyB->GetLocalPoint(anchor);
0096     localAxis1 = bodyA->GetLocalVector(axis);
0097     referenceAngle = bodyB->GetAngle() - bodyA->GetAngle();
0098 }
0099 
0100 b2PrismaticJoint::b2PrismaticJoint(const b2PrismaticJointDef* def)
0101 : b2Joint(def)
0102 {
0103     m_localAnchor1 = def->localAnchorA;
0104     m_localAnchor2 = def->localAnchorB;
0105     m_localXAxis1 = def->localAxis1;
0106     m_localYAxis1 = b2Cross(1.0f, m_localXAxis1);
0107     m_refAngle = def->referenceAngle;
0108 
0109     m_impulse.SetZero();
0110     m_motorMass = 0.0;
0111     m_motorImpulse = 0.0f;
0112 
0113     m_lowerTranslation = def->lowerTranslation;
0114     m_upperTranslation = def->upperTranslation;
0115     m_maxMotorForce = def->maxMotorForce;
0116     m_motorSpeed = def->motorSpeed;
0117     m_enableLimit = def->enableLimit;
0118     m_enableMotor = def->enableMotor;
0119     m_limitState = e_inactiveLimit;
0120 
0121     m_axis.SetZero();
0122     m_perp.SetZero();
0123 }
0124 
0125 void b2PrismaticJoint::InitVelocityConstraints(const b2TimeStep& step)
0126 {
0127     b2Body* b1 = m_bodyA;
0128     b2Body* b2 = m_bodyB;
0129 
0130     m_localCenterA = b1->GetLocalCenter();
0131     m_localCenterB = b2->GetLocalCenter();
0132 
0133     b2Transform xf1 = b1->GetTransform();
0134     b2Transform xf2 = b2->GetTransform();
0135 
0136     // Compute the effective masses.
0137     b2Vec2 r1 = b2Mul(xf1.R, m_localAnchor1 - m_localCenterA);
0138     b2Vec2 r2 = b2Mul(xf2.R, m_localAnchor2 - m_localCenterB);
0139     b2Vec2 d = b2->m_sweep.c + r2 - b1->m_sweep.c - r1;
0140 
0141     m_invMassA = b1->m_invMass;
0142     m_invIA = b1->m_invI;
0143     m_invMassB = b2->m_invMass;
0144     m_invIB = b2->m_invI;
0145 
0146     // Compute motor Jacobian and effective mass.
0147     {
0148         m_axis = b2Mul(xf1.R, m_localXAxis1);
0149         m_a1 = b2Cross(d + r1, m_axis);
0150         m_a2 = b2Cross(r2, m_axis);
0151 
0152         m_motorMass = m_invMassA + m_invMassB + m_invIA * m_a1 * m_a1 + m_invIB * m_a2 * m_a2;
0153         if (m_motorMass > b2_epsilon)
0154         {
0155             m_motorMass = 1.0f / m_motorMass;
0156         }
0157     }
0158 
0159     // Prismatic constraint.
0160     {
0161         m_perp = b2Mul(xf1.R, m_localYAxis1);
0162 
0163         m_s1 = b2Cross(d + r1, m_perp);
0164         m_s2 = b2Cross(r2, m_perp);
0165 
0166         qreal m1 = m_invMassA, m2 = m_invMassB;
0167         qreal i1 = m_invIA, i2 = m_invIB;
0168 
0169         qreal k11 = m1 + m2 + i1 * m_s1 * m_s1 + i2 * m_s2 * m_s2;
0170         qreal k12 = i1 * m_s1 + i2 * m_s2;
0171         qreal k13 = i1 * m_s1 * m_a1 + i2 * m_s2 * m_a2;
0172         qreal k22 = i1 + i2;
0173         if (k22 == 0.0f)
0174         {
0175             k22 = 1.0f;
0176         }
0177         qreal k23 = i1 * m_a1 + i2 * m_a2;
0178         qreal k33 = m1 + m2 + i1 * m_a1 * m_a1 + i2 * m_a2 * m_a2;
0179 
0180         m_K.col1.Set(k11, k12, k13);
0181         m_K.col2.Set(k12, k22, k23);
0182         m_K.col3.Set(k13, k23, k33);
0183     }
0184 
0185     // Compute motor and limit terms.
0186     if (m_enableLimit)
0187     {
0188         qreal jointTranslation = b2Dot(m_axis, d);
0189         if (b2Abs(m_upperTranslation - m_lowerTranslation) < 2.0f * b2_linearSlop)
0190         {
0191             m_limitState = e_equalLimits;
0192         }
0193         else if (jointTranslation <= m_lowerTranslation)
0194         {
0195             if (m_limitState != e_atLowerLimit)
0196             {
0197                 m_limitState = e_atLowerLimit;
0198                 m_impulse.z = 0.0f;
0199             }
0200         }
0201         else if (jointTranslation >= m_upperTranslation)
0202         {
0203             if (m_limitState != e_atUpperLimit)
0204             {
0205                 m_limitState = e_atUpperLimit;
0206                 m_impulse.z = 0.0f;
0207             }
0208         }
0209         else
0210         {
0211             m_limitState = e_inactiveLimit;
0212             m_impulse.z = 0.0f;
0213         }
0214     }
0215     else
0216     {
0217         m_limitState = e_inactiveLimit;
0218         m_impulse.z = 0.0f;
0219     }
0220 
0221     if (m_enableMotor == false)
0222     {
0223         m_motorImpulse = 0.0f;
0224     }
0225 
0226     if (step.warmStarting)
0227     {
0228         // Account for variable time step.
0229         m_impulse *= step.dtRatio;
0230         m_motorImpulse *= step.dtRatio;
0231 
0232         b2Vec2 P = m_impulse.x * m_perp + (m_motorImpulse + m_impulse.z) * m_axis;
0233         qreal L1 = m_impulse.x * m_s1 + m_impulse.y + (m_motorImpulse + m_impulse.z) * m_a1;
0234         qreal L2 = m_impulse.x * m_s2 + m_impulse.y + (m_motorImpulse + m_impulse.z) * m_a2;
0235 
0236         b1->m_linearVelocity -= m_invMassA * P;
0237         b1->m_angularVelocity -= m_invIA * L1;
0238 
0239         b2->m_linearVelocity += m_invMassB * P;
0240         b2->m_angularVelocity += m_invIB * L2;
0241     }
0242     else
0243     {
0244         m_impulse.SetZero();
0245         m_motorImpulse = 0.0f;
0246     }
0247 }
0248 
0249 void b2PrismaticJoint::SolveVelocityConstraints(const b2TimeStep& step)
0250 {
0251     b2Body* b1 = m_bodyA;
0252     b2Body* b2 = m_bodyB;
0253 
0254     b2Vec2 v1 = b1->m_linearVelocity;
0255     qreal w1 = b1->m_angularVelocity;
0256     b2Vec2 v2 = b2->m_linearVelocity;
0257     qreal w2 = b2->m_angularVelocity;
0258 
0259     // Solve linear motor constraint.
0260     if (m_enableMotor && m_limitState != e_equalLimits)
0261     {
0262         qreal Cdot = b2Dot(m_axis, v2 - v1) + m_a2 * w2 - m_a1 * w1;
0263         qreal impulse = m_motorMass * (m_motorSpeed - Cdot);
0264         qreal oldImpulse = m_motorImpulse;
0265         qreal maxImpulse = step.dt * m_maxMotorForce;
0266         m_motorImpulse = b2Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse);
0267         impulse = m_motorImpulse - oldImpulse;
0268 
0269         b2Vec2 P = impulse * m_axis;
0270         qreal L1 = impulse * m_a1;
0271         qreal L2 = impulse * m_a2;
0272 
0273         v1 -= m_invMassA * P;
0274         w1 -= m_invIA * L1;
0275 
0276         v2 += m_invMassB * P;
0277         w2 += m_invIB * L2;
0278     }
0279 
0280     b2Vec2 Cdot1;
0281     Cdot1.x = b2Dot(m_perp, v2 - v1) + m_s2 * w2 - m_s1 * w1;
0282     Cdot1.y = w2 - w1;
0283 
0284     if (m_enableLimit && m_limitState != e_inactiveLimit)
0285     {
0286         // Solve prismatic and limit constraint in block form.
0287         qreal Cdot2;
0288         Cdot2 = b2Dot(m_axis, v2 - v1) + m_a2 * w2 - m_a1 * w1;
0289         b2Vec3 Cdot(Cdot1.x, Cdot1.y, Cdot2);
0290 
0291         b2Vec3 f1 = m_impulse;
0292         b2Vec3 df =  m_K.Solve33(-Cdot);
0293         m_impulse += df;
0294 
0295         if (m_limitState == e_atLowerLimit)
0296         {
0297             m_impulse.z = b2Max(m_impulse.z, 0.0f);
0298         }
0299         else if (m_limitState == e_atUpperLimit)
0300         {
0301             m_impulse.z = b2Min(m_impulse.z, 0.0f);
0302         }
0303 
0304         // f2(1:2) = invK(1:2,1:2) * (-Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3))) + f1(1:2)
0305         b2Vec2 b = -Cdot1 - (m_impulse.z - f1.z) * b2Vec2(m_K.col3.x, m_K.col3.y);
0306         b2Vec2 f2r = m_K.Solve22(b) + b2Vec2(f1.x, f1.y);
0307         m_impulse.x = f2r.x;
0308         m_impulse.y = f2r.y;
0309 
0310         df = m_impulse - f1;
0311 
0312         b2Vec2 P = df.x * m_perp + df.z * m_axis;
0313         qreal L1 = df.x * m_s1 + df.y + df.z * m_a1;
0314         qreal L2 = df.x * m_s2 + df.y + df.z * m_a2;
0315 
0316         v1 -= m_invMassA * P;
0317         w1 -= m_invIA * L1;
0318 
0319         v2 += m_invMassB * P;
0320         w2 += m_invIB * L2;
0321     }
0322     else
0323     {
0324         // Limit is inactive, just solve the prismatic constraint in block form.
0325         b2Vec2 df = m_K.Solve22(-Cdot1);
0326         m_impulse.x += df.x;
0327         m_impulse.y += df.y;
0328 
0329         b2Vec2 P = df.x * m_perp;
0330         qreal L1 = df.x * m_s1 + df.y;
0331         qreal L2 = df.x * m_s2 + df.y;
0332 
0333         v1 -= m_invMassA * P;
0334         w1 -= m_invIA * L1;
0335 
0336         v2 += m_invMassB * P;
0337         w2 += m_invIB * L2;
0338     }
0339 
0340     b1->m_linearVelocity = v1;
0341     b1->m_angularVelocity = w1;
0342     b2->m_linearVelocity = v2;
0343     b2->m_angularVelocity = w2;
0344 }
0345 
0346 bool b2PrismaticJoint::SolvePositionConstraints(qreal baumgarte)
0347 {
0348     B2_NOT_USED(baumgarte);
0349 
0350     b2Body* b1 = m_bodyA;
0351     b2Body* b2 = m_bodyB;
0352 
0353     b2Vec2 c1 = b1->m_sweep.c;
0354     qreal a1 = b1->m_sweep.a;
0355 
0356     b2Vec2 c2 = b2->m_sweep.c;
0357     qreal a2 = b2->m_sweep.a;
0358 
0359     // Solve linear limit constraint.
0360     qreal linearError = 0.0f, angularError = 0.0f;
0361     bool active = false;
0362     qreal C2 = 0.0f;
0363 
0364     b2Mat22 R1(a1), R2(a2);
0365 
0366     b2Vec2 r1 = b2Mul(R1, m_localAnchor1 - m_localCenterA);
0367     b2Vec2 r2 = b2Mul(R2, m_localAnchor2 - m_localCenterB);
0368     b2Vec2 d = c2 + r2 - c1 - r1;
0369 
0370     if (m_enableLimit)
0371     {
0372         m_axis = b2Mul(R1, m_localXAxis1);
0373 
0374         m_a1 = b2Cross(d + r1, m_axis);
0375         m_a2 = b2Cross(r2, m_axis);
0376 
0377         qreal translation = b2Dot(m_axis, d);
0378         if (b2Abs(m_upperTranslation - m_lowerTranslation) < 2.0f * b2_linearSlop)
0379         {
0380             // Prevent large angular corrections
0381             C2 = b2Clamp(translation, -b2_maxLinearCorrection, b2_maxLinearCorrection);
0382             linearError = b2Abs(translation);
0383             active = true;
0384         }
0385         else if (translation <= m_lowerTranslation)
0386         {
0387             // Prevent large linear corrections and allow some slop.
0388             C2 = b2Clamp(translation - m_lowerTranslation + b2_linearSlop, -b2_maxLinearCorrection, 0.0f);
0389             linearError = m_lowerTranslation - translation;
0390             active = true;
0391         }
0392         else if (translation >= m_upperTranslation)
0393         {
0394             // Prevent large linear corrections and allow some slop.
0395             C2 = b2Clamp(translation - m_upperTranslation - b2_linearSlop, 0.0f, b2_maxLinearCorrection);
0396             linearError = translation - m_upperTranslation;
0397             active = true;
0398         }
0399     }
0400 
0401     m_perp = b2Mul(R1, m_localYAxis1);
0402 
0403     m_s1 = b2Cross(d + r1, m_perp);
0404     m_s2 = b2Cross(r2, m_perp);
0405 
0406     b2Vec3 impulse;
0407     b2Vec2 C1;
0408     C1.x = b2Dot(m_perp, d);
0409     C1.y = a2 - a1 - m_refAngle;
0410 
0411     linearError = b2Max(linearError, b2Abs(C1.x));
0412     angularError = b2Abs(C1.y);
0413 
0414     if (active)
0415     {
0416         qreal m1 = m_invMassA, m2 = m_invMassB;
0417         qreal i1 = m_invIA, i2 = m_invIB;
0418 
0419         qreal k11 = m1 + m2 + i1 * m_s1 * m_s1 + i2 * m_s2 * m_s2;
0420         qreal k12 = i1 * m_s1 + i2 * m_s2;
0421         qreal k13 = i1 * m_s1 * m_a1 + i2 * m_s2 * m_a2;
0422         qreal k22 = i1 + i2;
0423         if (k22 == 0.0f)
0424         {
0425             k22 = 1.0f;
0426         }
0427         qreal k23 = i1 * m_a1 + i2 * m_a2;
0428         qreal k33 = m1 + m2 + i1 * m_a1 * m_a1 + i2 * m_a2 * m_a2;
0429 
0430         m_K.col1.Set(k11, k12, k13);
0431         m_K.col2.Set(k12, k22, k23);
0432         m_K.col3.Set(k13, k23, k33);
0433 
0434         b2Vec3 C;
0435         C.x = C1.x;
0436         C.y = C1.y;
0437         C.z = C2;
0438 
0439         impulse = m_K.Solve33(-C);
0440     }
0441     else
0442     {
0443         qreal m1 = m_invMassA, m2 = m_invMassB;
0444         qreal i1 = m_invIA, i2 = m_invIB;
0445 
0446         qreal k11 = m1 + m2 + i1 * m_s1 * m_s1 + i2 * m_s2 * m_s2;
0447         qreal k12 = i1 * m_s1 + i2 * m_s2;
0448         qreal k22 = i1 + i2;
0449         if (k22 == 0.0f)
0450         {
0451             k22 = 1.0f;
0452         }
0453 
0454         m_K.col1.Set(k11, k12, 0.0f);
0455         m_K.col2.Set(k12, k22, 0.0f);
0456 
0457         b2Vec2 impulse1 = m_K.Solve22(-C1);
0458         impulse.x = impulse1.x;
0459         impulse.y = impulse1.y;
0460         impulse.z = 0.0f;
0461     }
0462 
0463     b2Vec2 P = impulse.x * m_perp + impulse.z * m_axis;
0464     qreal L1 = impulse.x * m_s1 + impulse.y + impulse.z * m_a1;
0465     qreal L2 = impulse.x * m_s2 + impulse.y + impulse.z * m_a2;
0466 
0467     c1 -= m_invMassA * P;
0468     a1 -= m_invIA * L1;
0469     c2 += m_invMassB * P;
0470     a2 += m_invIB * L2;
0471 
0472     // TODO_ERIN remove need for this.
0473     b1->m_sweep.c = c1;
0474     b1->m_sweep.a = a1;
0475     b2->m_sweep.c = c2;
0476     b2->m_sweep.a = a2;
0477     b1->SynchronizeTransform();
0478     b2->SynchronizeTransform();
0479     
0480     return linearError <= b2_linearSlop && angularError <= b2_angularSlop;
0481 }
0482 
0483 b2Vec2 b2PrismaticJoint::GetAnchorA() const
0484 {
0485     return m_bodyA->GetWorldPoint(m_localAnchor1);
0486 }
0487 
0488 b2Vec2 b2PrismaticJoint::GetAnchorB() const
0489 {
0490     return m_bodyB->GetWorldPoint(m_localAnchor2);
0491 }
0492 
0493 b2Vec2 b2PrismaticJoint::GetReactionForce(qreal inv_dt) const
0494 {
0495     return inv_dt * (m_impulse.x * m_perp + (m_motorImpulse + m_impulse.z) * m_axis);
0496 }
0497 
0498 qreal b2PrismaticJoint::GetReactionTorque(qreal inv_dt) const
0499 {
0500     return inv_dt * m_impulse.y;
0501 }
0502 
0503 qreal b2PrismaticJoint::GetJointTranslation() const
0504 {
0505     b2Body* b1 = m_bodyA;
0506     b2Body* b2 = m_bodyB;
0507 
0508     b2Vec2 p1 = b1->GetWorldPoint(m_localAnchor1);
0509     b2Vec2 p2 = b2->GetWorldPoint(m_localAnchor2);
0510     b2Vec2 d = p2 - p1;
0511     b2Vec2 axis = b1->GetWorldVector(m_localXAxis1);
0512 
0513     qreal translation = b2Dot(d, axis);
0514     return translation;
0515 }
0516 
0517 qreal b2PrismaticJoint::GetJointSpeed() const
0518 {
0519     b2Body* b1 = m_bodyA;
0520     b2Body* b2 = m_bodyB;
0521 
0522     b2Vec2 r1 = b2Mul(b1->GetTransform().R, m_localAnchor1 - b1->GetLocalCenter());
0523     b2Vec2 r2 = b2Mul(b2->GetTransform().R, m_localAnchor2 - b2->GetLocalCenter());
0524     b2Vec2 p1 = b1->m_sweep.c + r1;
0525     b2Vec2 p2 = b2->m_sweep.c + r2;
0526     b2Vec2 d = p2 - p1;
0527     b2Vec2 axis = b1->GetWorldVector(m_localXAxis1);
0528 
0529     b2Vec2 v1 = b1->m_linearVelocity;
0530     b2Vec2 v2 = b2->m_linearVelocity;
0531     qreal w1 = b1->m_angularVelocity;
0532     qreal w2 = b2->m_angularVelocity;
0533 
0534     qreal speed = b2Dot(d, b2Cross(w1, axis)) + b2Dot(axis, v2 + b2Cross(w2, r2) - v1 - b2Cross(w1, r1));
0535     return speed;
0536 }
0537 
0538 bool b2PrismaticJoint::IsLimitEnabled() const
0539 {
0540     return m_enableLimit;
0541 }
0542 
0543 void b2PrismaticJoint::EnableLimit(bool flag)
0544 {
0545     m_bodyA->SetAwake(true);
0546     m_bodyB->SetAwake(true);
0547     m_enableLimit = flag;
0548 }
0549 
0550 qreal b2PrismaticJoint::GetLowerLimit() const
0551 {
0552     return m_lowerTranslation;
0553 }
0554 
0555 qreal b2PrismaticJoint::GetUpperLimit() const
0556 {
0557     return m_upperTranslation;
0558 }
0559 
0560 void b2PrismaticJoint::SetLimits(qreal lower, qreal upper)
0561 {
0562     b2Assert(lower <= upper);
0563     m_bodyA->SetAwake(true);
0564     m_bodyB->SetAwake(true);
0565     m_lowerTranslation = lower;
0566     m_upperTranslation = upper;
0567 }
0568 
0569 bool b2PrismaticJoint::IsMotorEnabled() const
0570 {
0571     return m_enableMotor;
0572 }
0573 
0574 void b2PrismaticJoint::EnableMotor(bool flag)
0575 {
0576     m_bodyA->SetAwake(true);
0577     m_bodyB->SetAwake(true);
0578     m_enableMotor = flag;
0579 }
0580 
0581 void b2PrismaticJoint::SetMotorSpeed(qreal speed)
0582 {
0583     m_bodyA->SetAwake(true);
0584     m_bodyB->SetAwake(true);
0585     m_motorSpeed = speed;
0586 }
0587 
0588 void b2PrismaticJoint::SetMaxMotorForce(qreal force)
0589 {
0590     m_bodyA->SetAwake(true);
0591     m_bodyB->SetAwake(true);
0592     m_maxMotorForce = force;
0593 }
0594 
0595 qreal b2PrismaticJoint::GetMotorForce(qreal inv_dt) const
0596 {
0597     return inv_dt * m_motorImpulse;
0598 }