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 }