DistanceJoint.cpp
Go to the documentation of this file.
1 /*
2  * Original work Copyright (c) 2006-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 // 1-D constrained system
35 // m (v2 - v1) = lambda
36 // v2 + (beta/h) * x1 + gamma * lambda = 0, gamma has units of inverse mass.
37 // x2 = x1 + h * v2
38 
39 // 1-D mass-damper-spring system
40 // m (v2 - v1) + h * d * v2 + h * k *
41 
42 // C = norm(p2 - p1) - L
43 // u = (p2 - p1) / norm(p2 - p1)
44 // Cdot = dot(u, v2 + cross(w2, r2) - v1 - cross(w1, r1))
45 // J = [-u -cross(r1, u) u cross(r2, u)]
46 // K = J * invM * JT
47 // = invMass1 + invI1 * cross(r1, u)^2 + invMass2 + invI2 * cross(r2, u)^2
48 
49 bool DistanceJoint::IsOkay(const DistanceJointConf& def) noexcept
50 {
51  if (!Joint::IsOkay(def))
52  {
53  return false;
54  }
55  return true;
56 }
57 
59  Joint(def),
60  m_localAnchorA(def.localAnchorA),
61  m_localAnchorB(def.localAnchorB),
62  m_length(def.length),
63  m_frequency(def.frequency),
64  m_dampingRatio(def.dampingRatio)
65 {
66  // Intentionally empty.
67 }
68 
69 void DistanceJoint::Accept(JointVisitor& visitor) const
70 {
71  visitor.Visit(*this);
72 }
73 
75 {
76  visitor.Visit(*this);
77 }
78 
79 void DistanceJoint::InitVelocityConstraints(BodyConstraintsMap& bodies,
80  const playrho::StepConf& step,
81  const ConstraintSolverConf&)
82 {
83  auto& bodyConstraintA = At(bodies, GetBodyA());
84  auto& bodyConstraintB = At(bodies, GetBodyB());
85 
86  const auto invMassA = bodyConstraintA->GetInvMass();
87  const auto invRotInertiaA = bodyConstraintA->GetInvRotInertia(); // L^-2 M^-1 QP^2
88  const auto invMassB = bodyConstraintB->GetInvMass();
89  const auto invRotInertiaB = bodyConstraintB->GetInvRotInertia(); // L^-2 M^-1 QP^2
90 
91  const auto posA = bodyConstraintA->GetPosition();
92  auto velA = bodyConstraintA->GetVelocity();
93 
94  const auto posB = bodyConstraintB->GetPosition();
95  auto velB = bodyConstraintB->GetVelocity();
96 
97  const auto qA = UnitVec::Get(posA.angular);
98  const auto qB = UnitVec::Get(posB.angular);
99 
100  m_rA = Rotate(m_localAnchorA - bodyConstraintA->GetLocalCenter(), qA);
101  m_rB = Rotate(m_localAnchorB - bodyConstraintB->GetLocalCenter(), qB);
102  const auto deltaLocation = Length2{(posB.linear + m_rB) - (posA.linear + m_rA)};
103 
104  // Handle singularity.
105  const auto uvresult = UnitVec::Get(deltaLocation[0], deltaLocation[1]);
106  m_u = std::get<UnitVec>(uvresult);
107  const auto length = std::get<Length>(uvresult);
108 
109  const auto crAu = Length{Cross(m_rA, m_u)} / Radian;
110  const auto crBu = Length{Cross(m_rB, m_u)} / Radian;
111  const auto invRotMassA = invRotInertiaA * Square(crAu);
112  const auto invRotMassB = invRotInertiaB * Square(crBu);
113  auto invMass = InvMass{invMassA + invRotMassA + invMassB + invRotMassB};
114 
115  // Compute the effective mass matrix.
116  m_mass = (invMass != InvMass{0}) ? Real{1} / invMass: 0_kg;
117 
118  if (m_frequency > 0_Hz)
119  {
120  const auto C = length - m_length; // L
121 
122  // Frequency
123  const auto omega = Real{2} * Pi * m_frequency;
124 
125  // Damping coefficient
126  const auto d = Real{2} * m_mass * m_dampingRatio * omega; // M T^-1
127 
128  // Spring stiffness
129  const auto k = m_mass * Square(omega); // M T^-2
130 
131  // magic formulas
132  const auto h = step.GetTime();
133  const auto gamma = Mass{h * (d + h * k)}; // T (M T^-1 + T M T^-2) = M
134  m_invGamma = (gamma != 0_kg)? Real{1} / gamma: 0;
135  m_bias = C * h * k * m_invGamma; // L T M T^-2 M^-1 = L T^-1
136 
137  invMass += m_invGamma;
138  m_mass = (invMass != InvMass{0}) ? Real{1} / invMass: 0;
139  }
140  else
141  {
142  m_invGamma = InvMass{0};
143  m_bias = 0_mps;
144  }
145 
146  if (step.doWarmStart)
147  {
148  // Scale the impulse to support a variable time step.
149  m_impulse *= step.dtRatio;
150 
151  const auto P = m_impulse * m_u;
152 
153  // P is M L T^-2
154  // Cross(Length2, P) is: M L^2 T^-1
155  // inv rotational inertia is: L^-2 M^-1 QP^2
156  // Product is: L^-2 M^-1 QP^2 M L^2 T^-1 = QP^2 T^-1
157  const auto LA = Cross(m_rA, P) / Radian;
158  const auto LB = Cross(m_rB, P) / Radian;
159  velA -= Velocity{invMassA * P, invRotInertiaA * LA};
160  velB += Velocity{invMassB * P, invRotInertiaB * LB};
161  }
162  else
163  {
164  m_impulse = 0;
165  }
166 
167  bodyConstraintA->SetVelocity(velA);
168  bodyConstraintB->SetVelocity(velB);
169 }
170 
171 bool DistanceJoint::SolveVelocityConstraints(BodyConstraintsMap& bodies, const StepConf&)
172 {
173  auto& bodyConstraintA = At(bodies, GetBodyA());
174  auto& bodyConstraintB = At(bodies, GetBodyB());
175 
176  const auto invMassA = bodyConstraintA->GetInvMass();
177  const auto invRotInertiaA = bodyConstraintA->GetInvRotInertia();
178  const auto invMassB = bodyConstraintB->GetInvMass();
179  const auto invRotInertiaB = bodyConstraintB->GetInvRotInertia();
180 
181  auto velA = bodyConstraintA->GetVelocity();
182  auto velB = bodyConstraintB->GetVelocity();
183 
184  // Cdot = dot(u, v + cross(w, r))
185  const auto vpA = velA.linear + GetRevPerpendicular(m_rA) * (velA.angular / Radian);
186  const auto vpB = velB.linear + GetRevPerpendicular(m_rB) * (velB.angular / Radian);
187  const auto Cdot = LinearVelocity{Dot(m_u, vpB - vpA)};
188 
189  const auto impulse = Momentum{-m_mass * (Cdot + m_bias + m_invGamma * m_impulse)};
190  m_impulse += impulse;
191 
192  const auto P = impulse * m_u;
193  const auto LA = Cross(m_rA, P) / Radian;
194  const auto LB = Cross(m_rB, P) / Radian;
195  velA -= Velocity{invMassA * P, invRotInertiaA * LA};
196  velB += Velocity{invMassB * P, invRotInertiaB * LB};
197 
198  bodyConstraintA->SetVelocity(velA);
199  bodyConstraintB->SetVelocity(velB);
200 
201  return impulse == 0_Ns;
202 }
203 
204 bool DistanceJoint::SolvePositionConstraints(BodyConstraintsMap& bodies,
205  const ConstraintSolverConf& conf) const
206 {
207  if (m_frequency > 0_Hz)
208  {
209  // There is no position correction for soft distance constraints.
210  return true;
211  }
212 
213  auto& bodyConstraintA = At(bodies, GetBodyA());
214  auto& bodyConstraintB = At(bodies, GetBodyB());
215 
216  const auto invMassA = bodyConstraintA->GetInvMass();
217  const auto invRotInertiaA = bodyConstraintA->GetInvRotInertia();
218  const auto invMassB = bodyConstraintB->GetInvMass();
219  const auto invRotInertiaB = bodyConstraintB->GetInvRotInertia();
220 
221  auto posA = bodyConstraintA->GetPosition();
222  auto posB = bodyConstraintB->GetPosition();
223 
224  const auto qA = UnitVec::Get(posA.angular);
225  const auto qB = UnitVec::Get(posB.angular);
226 
227  const auto rA = Length2{Rotate(m_localAnchorA - bodyConstraintA->GetLocalCenter(), qA)};
228  const auto rB = Length2{Rotate(m_localAnchorB - bodyConstraintB->GetLocalCenter(), qB)};
229  const auto relLoc = Length2{(posB.linear + rB) - (posA.linear + rA)};
230 
231  const auto uvresult = UnitVec::Get(relLoc[0], relLoc[1]);
232  const auto u = std::get<UnitVec>(uvresult);
233  const auto length = std::get<Length>(uvresult);
234  const auto deltaLength = length - m_length;
235  const auto C = std::clamp(deltaLength, -conf.maxLinearCorrection, conf.maxLinearCorrection);
236 
237  const auto impulse = -m_mass * C;
238  const auto P = impulse * u;
239 
240  posA -= Position{invMassA * P, invRotInertiaA * Cross(rA, P) / Radian};
241  posB += Position{invMassB * P, invRotInertiaB * Cross(rB, P) / Radian};
242 
243  bodyConstraintA->SetPosition(posA);
244  bodyConstraintB->SetPosition(posB);
245 
246  return abs(C) < conf.linearSlop;
247 }
248 
250 {
251  return GetWorldPoint(*GetBodyA(), GetLocalAnchorA());
252 }
253 
255 {
256  return GetWorldPoint(*GetBodyB(), GetLocalAnchorB());
257 }
258 
260 {
261  return m_impulse * m_u;
262 }
263 
265 {
266  return AngularMomentum{0};
267 }
268 
269 } // namespace d2
270 } // namespace playrho