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