RopeJoint.cpp
Go to the documentation of this file.
1 /*
2  * Original work Copyright (c) 2007-2011 Erin Catto http://www.box2d.org
3  * Modified work Copyright (c) 2017 Louis Langholtz https://github.com/louis-langholtz/PlayRho
4  *
5  * This software is provided 'as-is', without any express or implied
6  * warranty. In no event will the authors be held liable for any damages
7  * arising from the use of this software.
8  *
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  *
13  * 1. The origin of this software must not be misrepresented; you must not
14  * claim that you wrote the original software. If you use this software
15  * in a product, an acknowledgment in the product documentation would be
16  * appreciated but is not required.
17  * 2. Altered source versions must be plainly marked as such, and must not be
18  * misrepresented as being the original software.
19  * 3. This notice may not be removed or altered from any source distribution.
20  */
21 
28 
29 #include <algorithm>
30 
31 namespace playrho {
32 namespace d2 {
33 
34 // Limit:
35 // C = norm(pB - pA) - L
36 // u = (pB - pA) / norm(pB - pA)
37 // Cdot = dot(u, vB + cross(wB, rB) - vA - cross(wA, rA))
38 // J = [-u -cross(rA, u) u cross(rB, u)]
39 // K = J * invM * JT
40 // = invMassA + invIA * cross(rA, u)^2 + invMassB + invIB * cross(rB, u)^2
41 
43  Joint{def},
44  m_localAnchorA{def.localAnchorA},
45  m_localAnchorB{def.localAnchorB},
46  m_maxLength{def.maxLength}
47 {
48 }
49 
50 void RopeJoint::Accept(JointVisitor& visitor) const
51 {
52  visitor.Visit(*this);
53 }
54 
56 {
57  visitor.Visit(*this);
58 }
59 
60 void RopeJoint::InitVelocityConstraints(BodyConstraintsMap& bodies,
61  const StepConf& step,
62  const ConstraintSolverConf& conf)
63 {
64  auto& bodyConstraintA = At(bodies, GetBodyA());
65  auto& bodyConstraintB = At(bodies, GetBodyB());
66 
67  const auto invMassA = bodyConstraintA->GetInvMass();
68  const auto invRotInertiaA = bodyConstraintA->GetInvRotInertia();
69  const auto posA = bodyConstraintA->GetPosition();
70  auto velA = bodyConstraintA->GetVelocity();
71 
72  const auto invMassB = bodyConstraintB->GetInvMass();
73  const auto invRotInertiaB = bodyConstraintB->GetInvRotInertia();
74  const auto posB = bodyConstraintB->GetPosition();
75  auto velB = bodyConstraintB->GetVelocity();
76 
77  const auto qA = UnitVec::Get(posA.angular);
78  const auto qB = UnitVec::Get(posB.angular);
79 
80  m_rA = Rotate(m_localAnchorA - bodyConstraintA->GetLocalCenter(), qA);
81  m_rB = Rotate(m_localAnchorB - bodyConstraintB->GetLocalCenter(), qB);
82  const auto posDelta = Length2{(posB.linear + m_rB) - (posA.linear + m_rA)};
83 
84  const auto uvresult = UnitVec::Get(posDelta[0], posDelta[1]);
85  const auto uv = std::get<UnitVec>(uvresult);
86  m_length = std::get<Length>(uvresult);
87 
88  const auto C = m_length - m_maxLength;
89  m_state = (C > 0_m)? e_atUpperLimit: e_inactiveLimit;
90 
91  if (m_length > conf.linearSlop)
92  {
93  m_u = uv;
94  }
95  else
96  {
97  m_u = UnitVec::GetZero();
98  m_mass = 0_kg;
99  m_impulse = 0;
100  return;
101  }
102 
103  // Compute effective mass.
104  const auto crA = Cross(m_rA, m_u);
105  const auto crB = Cross(m_rB, m_u);
106  const auto invRotMassA = InvMass{invRotInertiaA * Square(crA) / SquareRadian};
107  const auto invRotMassB = InvMass{invRotInertiaB * Square(crB) / SquareRadian};
108  const auto invMass = invMassA + invMassB + invRotMassA + invRotMassB;
109 
110  m_mass = (invMass != InvMass{0}) ? Real{1} / invMass : 0_kg;
111 
112  if (step.doWarmStart)
113  {
114  // Scale the impulse to support a variable time step.
115  m_impulse *= step.dtRatio;
116 
117  const auto P = m_impulse * m_u;
118 
119  // L * M * L T^-1 / QP is: L^2 M T^-1 QP^-1 which is: AngularMomentum.
120  const auto crossAP = AngularMomentum{Cross(m_rA, P) / Radian};
121  const auto crossBP = AngularMomentum{Cross(m_rB, P) / Radian}; // L * M * L T^-1 is: L^2 M T^-1
122 
123  velA -= Velocity{bodyConstraintA->GetInvMass() * P, invRotInertiaA * crossAP};
124  velB += Velocity{bodyConstraintB->GetInvMass() * P, invRotInertiaB * crossBP};
125  }
126  else
127  {
128  m_impulse = 0;
129  }
130 
131  bodyConstraintA->SetVelocity(velA);
132  bodyConstraintB->SetVelocity(velB);
133 }
134 
135 bool RopeJoint::SolveVelocityConstraints(BodyConstraintsMap& bodies, const StepConf& step)
136 {
137  auto& bodyConstraintA = At(bodies, GetBodyA());
138  auto& bodyConstraintB = At(bodies, GetBodyB());
139 
140  auto velA = bodyConstraintA->GetVelocity();
141  auto velB = bodyConstraintB->GetVelocity();
142 
143  // Cdot = dot(u, v + cross(w, r))
144  const auto vpA = velA.linear + GetRevPerpendicular(m_rA) * (velA.angular / Radian);
145  const auto vpB = velB.linear + GetRevPerpendicular(m_rB) * (velB.angular / Radian);
146  const auto C = m_length - m_maxLength;
147  const auto vpDelta = LinearVelocity2{vpB - vpA};
148 
149  // Predictive constraint.
150  const auto Cdot = LinearVelocity{Dot(m_u, vpDelta)}
151  + ((C < 0_m)? LinearVelocity{step.GetInvTime() * C}: 0_mps);
152 
153  auto impulse = -m_mass * Cdot;
154  const auto oldImpulse = m_impulse;
155  m_impulse = std::min(0_Ns, m_impulse + impulse);
156  impulse = m_impulse - oldImpulse;
157 
158  const auto P = impulse * m_u;
159 
160  // L * M * L T^-1 / QP is: L^2 M T^-1 QP^-1 which is: AngularMomentum.
161  const auto crossAP = AngularMomentum{Cross(m_rA, P) / Radian};
162  const auto crossBP = AngularMomentum{Cross(m_rB, P) / Radian}; // L * M * L T^-1 is: L^2 M T^-1
163 
164  velA -= Velocity{bodyConstraintA->GetInvMass() * P, bodyConstraintA->GetInvRotInertia() * crossAP};
165  velB += Velocity{bodyConstraintB->GetInvMass() * P, bodyConstraintB->GetInvRotInertia() * crossBP};
166 
167  bodyConstraintA->SetVelocity(velA);
168  bodyConstraintB->SetVelocity(velB);
169 
170  return impulse == 0_Ns;
171 }
172 
173 bool RopeJoint::SolvePositionConstraints(BodyConstraintsMap& bodies, const ConstraintSolverConf& conf) const
174 {
175  auto& bodyConstraintA = At(bodies, GetBodyA());
176  auto& bodyConstraintB = At(bodies, GetBodyB());
177 
178  auto posA = bodyConstraintA->GetPosition();
179  auto posB = bodyConstraintB->GetPosition();
180 
181  const auto qA = UnitVec::Get(posA.angular);
182  const auto qB = UnitVec::Get(posB.angular);
183 
184  const auto rA = Length2{Rotate(m_localAnchorA - bodyConstraintA->GetLocalCenter(), qA)};
185  const auto rB = Length2{Rotate(m_localAnchorB - bodyConstraintB->GetLocalCenter(), qB)};
186  const auto posDelta = (posB.linear + rB) - (posA.linear + rA);
187 
188  const auto uvresult = UnitVec::Get(posDelta[0], posDelta[1]);
189  const auto u = std::get<UnitVec>(uvresult);
190  const auto length = std::get<Length>(uvresult);
191 
192  const auto C = std::clamp(length - m_maxLength, 0_m, conf.maxLinearCorrection);
193 
194  const auto impulse = -m_mass * C;
195  const auto linImpulse = impulse * u;
196 
197  const auto angImpulseA = Cross(rA, linImpulse) / Radian;
198  const auto angImpulseB = Cross(rB, linImpulse) / Radian;
199 
200  posA -= Position{bodyConstraintA->GetInvMass() * linImpulse, bodyConstraintA->GetInvRotInertia() * angImpulseA};
201  posB += Position{bodyConstraintB->GetInvMass() * linImpulse, bodyConstraintB->GetInvRotInertia() * angImpulseB};
202 
203  bodyConstraintA->SetPosition(posA);
204  bodyConstraintB->SetPosition(posB);
205 
206  return (length - m_maxLength) < conf.linearSlop;
207 }
208 
210 {
211  return GetWorldPoint(*GetBodyA(), GetLocalAnchorA());
212 }
213 
215 {
216  return GetWorldPoint(*GetBodyB(), GetLocalAnchorB());
217 }
218 
220 {
221  return m_impulse * m_u;
222 }
223 
225 {
226  return AngularMomentum{0};
227 }
228 
230 {
231  return m_maxLength;
232 }
233 
235 {
236  return m_state;
237 }
238 
239 } // namespace d2
240 } // namespace playrho