GearJoint.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 
30 
31 namespace playrho {
32 namespace d2 {
33 
34 // Gear Joint:
35 // C0 = (coordinate1 + ratio * coordinate2)_initial
36 // C = (coordinate1 + ratio * coordinate2) - C0 = 0
37 // J = [J1 ratio * J2]
38 // K = J * invM * JT
39 // = J1 * invM1 * J1T + ratio * ratio * J2 * invM2 * J2T
40 //
41 // Revolute:
42 // coordinate = rotation
43 // Cdot = angularVelocity
44 // J = [0 0 1]
45 // K = J * invM * JT = invI
46 //
47 // Prismatic:
48 // coordinate = dot(p - pg, ug)
49 // Cdot = dot(v + cross(w, r), ug)
50 // J = [ug cross(r, ug)]
51 // K = J * invM * JT = invMass + invI * cross(r, ug)^2
52 
53 namespace {
54 
55 inline bool IsValidType(JointType t) noexcept
56 {
57  return (t == JointType::Revolute) || (t == JointType::Prismatic);
58 }
59 
60 } // unnamed namespace
61 
62 bool GearJoint::IsOkay(const GearJointConf& def) noexcept
63 {
64  const auto t1 = GetType(*def.joint1);
65  const auto t2 = GetType(*def.joint2);
66  if (!IsValidType(t1) || !IsValidType(t2))
67  {
68  return false;
69  }
70  if (!Joint::IsOkay(def))
71  {
72  return false;
73  }
74  return true;
75 }
76 
78  Joint(def),
79  m_joint1(def.joint1),
80  m_joint2(def.joint2),
81  m_typeA(GetType(*def.joint1)),
82  m_typeB(GetType(*def.joint2)),
83  m_ratio(def.ratio)
84 {
85  assert(IsValidType(m_typeA));
86  assert(IsValidType(m_typeB));
87 
88  // TODO_ERIN there might be some problem with the joint edges in Joint.
89 
90  m_bodyC = m_joint1->GetBodyA();
91 
92  // Get geometry of joint1
93  const auto xfA = GetBodyA()->GetTransformation();
94  const auto aA = GetBodyA()->GetAngle();
95  const auto xfC = m_bodyC->GetTransformation();
96  const auto aC = m_bodyC->GetAngle();
97 
98  Real coordinateA; // Duck-typed to handle m_typeA's type.
99  if (m_typeA == JointType::Revolute)
100  {
101  const auto revolute = static_cast<const RevoluteJoint*>(static_cast<Joint*>(def.joint1));
102  m_localAnchorC = revolute->GetLocalAnchorA();
103  m_localAnchorA = revolute->GetLocalAnchorB();
104  m_referenceAngleA = revolute->GetReferenceAngle();
105  m_localAxisC = UnitVec::GetZero();
106  coordinateA = (aA - aC - m_referenceAngleA) / Radian;
107  }
108  else // if (m_typeA != JointType::Revolute)
109  {
110  const auto prismatic = static_cast<const PrismaticJoint*>(static_cast<Joint*>(def.joint1));
111  m_localAnchorC = prismatic->GetLocalAnchorA();
112  m_localAnchorA = prismatic->GetLocalAnchorB();
113  m_referenceAngleA = prismatic->GetReferenceAngle();
114  m_localAxisC = prismatic->GetLocalAxisA();
115 
116  const auto pC = m_localAnchorC;
117  const auto pA = InverseRotate(Rotate(m_localAnchorA, xfA.q) + (xfA.p - xfC.p), xfC.q);
118  coordinateA = Dot(pA - pC, m_localAxisC) / Meter;
119  }
120 
121  m_bodyD = m_joint2->GetBodyA();
122 
123  // Get geometry of joint2
124  const auto xfB = GetBodyB()->GetTransformation();
125  const auto aB = GetBodyB()->GetAngle();
126  const auto xfD = m_bodyD->GetTransformation();
127  const auto aD = m_bodyD->GetAngle();
128 
129  Real coordinateB; // Duck-typed to handle m_typeB's type.
130  if (m_typeB == JointType::Revolute)
131  {
132  const auto revolute = static_cast<const RevoluteJoint*>(static_cast<Joint*>(def.joint2));
133  m_localAnchorD = revolute->GetLocalAnchorA();
134  m_localAnchorB = revolute->GetLocalAnchorB();
135  m_referenceAngleB = revolute->GetReferenceAngle();
136  m_localAxisD = UnitVec::GetZero();
137  coordinateB = (aB - aD - m_referenceAngleB) / Radian;
138  }
139  else
140  {
141  const auto prismatic = static_cast<const PrismaticJoint*>(static_cast<Joint*>(def.joint2));
142  m_localAnchorD = prismatic->GetLocalAnchorA();
143  m_localAnchorB = prismatic->GetLocalAnchorB();
144  m_referenceAngleB = prismatic->GetReferenceAngle();
145  m_localAxisD = prismatic->GetLocalAxisA();
146 
147  const auto pD = m_localAnchorD;
148  const auto pB = InverseRotate(Rotate(m_localAnchorB, xfB.q) + (xfB.p - xfD.p), xfD.q);
149  coordinateB = Dot(pB - pD, m_localAxisD) / Meter;
150  }
151 
152  m_constant = coordinateA + m_ratio * coordinateB;
153 }
154 
155 void GearJoint::Accept(JointVisitor& visitor) const
156 {
157  visitor.Visit(*this);
158 }
159 
161 {
162  visitor.Visit(*this);
163 }
164 
165 void GearJoint::InitVelocityConstraints(BodyConstraintsMap& bodies, const StepConf& step,
166  const ConstraintSolverConf&)
167 {
168  auto& bodyConstraintA = At(bodies, GetBodyA());
169  auto& bodyConstraintB = At(bodies, GetBodyB());
170  auto& bodyConstraintC = At(bodies, m_bodyC);
171  auto& bodyConstraintD = At(bodies, m_bodyD);
172 
173  auto velA = bodyConstraintA->GetVelocity();
174  const auto aA = bodyConstraintA->GetPosition().angular;
175 
176  auto velB = bodyConstraintB->GetVelocity();
177  const auto aB = bodyConstraintB->GetPosition().angular;
178 
179  auto velC = bodyConstraintC->GetVelocity();
180  const auto aC = bodyConstraintC->GetPosition().angular;
181 
182  auto velD = bodyConstraintD->GetVelocity();
183  const auto aD = bodyConstraintD->GetPosition().angular;
184 
185  const auto qA = UnitVec::Get(aA);
186  const auto qB = UnitVec::Get(aB);
187  const auto qC = UnitVec::Get(aC);
188  const auto qD = UnitVec::Get(aD);
189 
190  auto invMass = Real{0}; // Unitless to double for either linear mass or angular mass.
191 
192  if (m_typeA == JointType::Revolute)
193  {
194  m_JvAC = Vec2{};
195  m_JwA = 1_m;
196  m_JwC = 1_m;
197  const auto invAngMass = bodyConstraintA->GetInvRotInertia() + bodyConstraintC->GetInvRotInertia();
198  invMass += StripUnit(invAngMass);
199  }
200  else
201  {
202  const auto u = Rotate(m_localAxisC, qC);
203  const auto rC = Length2{Rotate(m_localAnchorC - bodyConstraintC->GetLocalCenter(), qC)};
204  const auto rA = Length2{Rotate(m_localAnchorA - bodyConstraintA->GetLocalCenter(), qA)};
205  m_JvAC = Real{1} * u;
206  m_JwC = Cross(rC, u);
207  m_JwA = Cross(rA, u);
208  const auto invRotMassC = InvMass{bodyConstraintC->GetInvRotInertia() * Square(m_JwC) / SquareRadian};
209  const auto invRotMassA = InvMass{bodyConstraintA->GetInvRotInertia() * Square(m_JwA) / SquareRadian};
210  const auto invLinMass = InvMass{bodyConstraintC->GetInvMass() + bodyConstraintA->GetInvMass() + invRotMassC + invRotMassA};
211  invMass += StripUnit(invLinMass);
212  }
213 
214  if (m_typeB == JointType::Revolute)
215  {
216  m_JvBD = Vec2{};
217  m_JwB = m_ratio * Meter;
218  m_JwD = m_ratio * Meter;
219  const auto invAngMass = InvRotInertia{Square(m_ratio) * (bodyConstraintB->GetInvRotInertia() + bodyConstraintD->GetInvRotInertia())};
220  invMass += StripUnit(invAngMass);
221  }
222  else
223  {
224  const auto u = Rotate(m_localAxisD, qD);
225  const auto rD = Rotate(m_localAnchorD - bodyConstraintD->GetLocalCenter(), qD);
226  const auto rB = Rotate(m_localAnchorB - bodyConstraintB->GetLocalCenter(), qB);
227  m_JvBD = m_ratio * u;
228  m_JwD = m_ratio * Cross(rD, u);
229  m_JwB = m_ratio * Cross(rB, u);
230  const auto invRotMassD = InvMass{bodyConstraintD->GetInvRotInertia() * Square(m_JwD) / SquareRadian};
231  const auto invRotMassB = InvMass{bodyConstraintB->GetInvRotInertia() * Square(m_JwB) / SquareRadian};
232  const auto invLinMass = InvMass{
233  Square(m_ratio) * (bodyConstraintD->GetInvMass() + bodyConstraintB->GetInvMass()) +
234  invRotMassD + invRotMassB
235  };
236  invMass += StripUnit(invLinMass);
237  }
238 
239  // Compute effective mass.
240  m_mass = (invMass > Real{0})? Real{1} / invMass: Real{0};
241 
242  if (step.doWarmStart)
243  {
244  velA += Velocity{
245  (bodyConstraintA->GetInvMass() * m_impulse) * m_JvAC,
246  bodyConstraintA->GetInvRotInertia() * m_impulse * m_JwA / Radian
247  };
248  velB += Velocity{
249  (bodyConstraintB->GetInvMass() * m_impulse) * m_JvBD,
250  bodyConstraintB->GetInvRotInertia() * m_impulse * m_JwB / Radian
251  };
252  velC -= Velocity{
253  (bodyConstraintC->GetInvMass() * m_impulse) * m_JvAC,
254  bodyConstraintC->GetInvRotInertia() * m_impulse * m_JwC / Radian
255  };
256  velD -= Velocity{
257  (bodyConstraintD->GetInvMass() * m_impulse) * m_JvBD,
258  bodyConstraintD->GetInvRotInertia() * m_impulse * m_JwD / Radian
259  };
260  }
261  else
262  {
263  m_impulse = 0_Ns;
264  }
265 
266  bodyConstraintA->SetVelocity(velA);
267  bodyConstraintB->SetVelocity(velB);
268  bodyConstraintC->SetVelocity(velC);
269  bodyConstraintD->SetVelocity(velD);
270 }
271 
272 bool GearJoint::SolveVelocityConstraints(BodyConstraintsMap& bodies, const StepConf&)
273 {
274  auto& bodyConstraintA = At(bodies, GetBodyA());
275  auto& bodyConstraintB = At(bodies, GetBodyB());
276  auto& bodyConstraintC = At(bodies, m_bodyC);
277  auto& bodyConstraintD = At(bodies, m_bodyD);
278 
279  auto velA = bodyConstraintA->GetVelocity();
280  auto velB = bodyConstraintB->GetVelocity();
281  auto velC = bodyConstraintC->GetVelocity();
282  auto velD = bodyConstraintD->GetVelocity();
283 
284  const auto acDot = LinearVelocity{Dot(m_JvAC, velA.linear - velC.linear)};
285  const auto bdDot = LinearVelocity{Dot(m_JvBD, velB.linear - velD.linear)};
286  const auto Cdot = acDot + bdDot
287  + (m_JwA * velA.angular - m_JwC * velC.angular) / Radian
288  + (m_JwB * velB.angular - m_JwD * velD.angular) / Radian;
289 
290  const auto impulse = Momentum{-m_mass * Kilogram * Cdot};
291  m_impulse += impulse;
292 
293  velA += Velocity{
294  (bodyConstraintA->GetInvMass() * impulse) * m_JvAC,
295  bodyConstraintA->GetInvRotInertia() * impulse * m_JwA / Radian
296  };
297  velB += Velocity{
298  (bodyConstraintB->GetInvMass() * impulse) * m_JvBD,
299  bodyConstraintB->GetInvRotInertia() * impulse * m_JwB / Radian
300  };
301  velC -= Velocity{
302  (bodyConstraintC->GetInvMass() * impulse) * m_JvAC,
303  bodyConstraintC->GetInvRotInertia() * impulse * m_JwC / Radian
304  };
305  velD -= Velocity{
306  (bodyConstraintD->GetInvMass() * impulse) * m_JvBD,
307  bodyConstraintD->GetInvRotInertia() * impulse * m_JwD / Radian
308  };
309 
310  bodyConstraintA->SetVelocity(velA);
311  bodyConstraintB->SetVelocity(velB);
312  bodyConstraintC->SetVelocity(velC);
313  bodyConstraintD->SetVelocity(velD);
314 
315  return impulse == 0_Ns;
316 }
317 
318 bool GearJoint::SolvePositionConstraints(BodyConstraintsMap& bodies, const ConstraintSolverConf& conf) const
319 {
320  auto& bodyConstraintA = At(bodies, GetBodyA());
321  auto& bodyConstraintB = At(bodies, GetBodyB());
322  auto& bodyConstraintC = At(bodies, m_bodyC);
323  auto& bodyConstraintD = At(bodies, m_bodyD);
324 
325  auto posA = bodyConstraintA->GetPosition();
326  auto posB = bodyConstraintB->GetPosition();
327  auto posC = bodyConstraintC->GetPosition();
328  auto posD = bodyConstraintD->GetPosition();
329 
330  const auto qA = UnitVec::Get(posA.angular);
331  const auto qB = UnitVec::Get(posB.angular);
332  const auto qC = UnitVec::Get(posC.angular);
333  const auto qD = UnitVec::Get(posD.angular);
334 
335  const auto linearError = 0_m;
336 
337  Vec2 JvAC, JvBD;
338  Real JwA, JwB, JwC, JwD;
339 
340  auto coordinateA = Real{0}; // Angle or length.
341  auto coordinateB = Real{0};
342  auto invMass = Real{0}; // Inverse linear mass or inverse angular mass.
343 
344  if (m_typeA == JointType::Revolute)
345  {
346  JvAC = Vec2{};
347  JwA = 1;
348  JwC = 1;
349  const auto invAngMass = bodyConstraintA->GetInvRotInertia() + bodyConstraintC->GetInvRotInertia();
350  invMass += StripUnit(invAngMass);
351  coordinateA = (posA.angular - posC.angular - m_referenceAngleA) / Radian;
352  }
353  else
354  {
355  const auto u = Rotate(m_localAxisC, qC);
356  const auto rC = Rotate(m_localAnchorC - bodyConstraintC->GetLocalCenter(), qC);
357  const auto rA = Rotate(m_localAnchorA - bodyConstraintA->GetLocalCenter(), qA);
358  JvAC = u * Real{1};
359  JwC = StripUnit(Length{Cross(rC, u)});
360  JwA = StripUnit(Length{Cross(rA, u)});
361  const auto invLinMass = InvMass{bodyConstraintC->GetInvMass() + bodyConstraintA->GetInvMass()};
362  const auto invRotMassC = InvMass{bodyConstraintC->GetInvRotInertia() * Square(JwC * Meter / Radian)};
363  const auto invRotMassA = InvMass{bodyConstraintA->GetInvRotInertia() * Square(JwA * Meter / Radian)};
364  invMass += StripUnit(invLinMass + invRotMassC + invRotMassA);
365  const auto pC = m_localAnchorC - bodyConstraintC->GetLocalCenter();
366  const auto pA = InverseRotate(rA + (posA.linear - posC.linear), qC);
367  coordinateA = Dot(pA - pC, m_localAxisC) / Meter;
368  }
369 
370  if (m_typeB == JointType::Revolute)
371  {
372  JvBD = Vec2{};
373  JwB = m_ratio;
374  JwD = m_ratio;
375  const auto invAngMass = InvRotInertia{
376  Square(m_ratio) * (bodyConstraintB->GetInvRotInertia() + bodyConstraintD->GetInvRotInertia())
377  };
378  invMass += StripUnit(invAngMass);
379  coordinateB = (posB.angular - posD.angular - m_referenceAngleB) / Radian;
380  }
381  else
382  {
383  const auto u = Rotate(m_localAxisD, qD);
384  const auto rD = Rotate(m_localAnchorD - bodyConstraintD->GetLocalCenter(), qD);
385  const auto rB = Rotate(m_localAnchorB - bodyConstraintB->GetLocalCenter(), qB);
386  JvBD = m_ratio * u;
387  JwD = m_ratio * StripUnit(Length{Cross(rD, u)});
388  JwB = m_ratio * StripUnit(Length{Cross(rB, u)});
389  const auto invLinMass = InvMass{Square(m_ratio) * (bodyConstraintD->GetInvMass() + bodyConstraintB->GetInvMass())};
390  const auto invRotMassD = InvMass{bodyConstraintD->GetInvRotInertia() * Square(JwD * Meter / Radian)};
391  const auto invRotMassB = InvMass{bodyConstraintB->GetInvRotInertia() * Square(JwB * Meter / Radian)};
392  invMass += StripUnit(invLinMass + invRotMassD + invRotMassB);
393  const auto pD = m_localAnchorD - bodyConstraintD->GetLocalCenter();
394  const auto pB = InverseRotate(rB + (posB.linear - posD.linear), qD);
395  coordinateB = Dot(pB - pD, m_localAxisD) / Meter;
396  }
397 
398  const auto C = ((coordinateA + m_ratio * coordinateB) - m_constant);
399 
400  const auto impulse = ((invMass > 0)? -C / invMass: 0) * Kilogram * Meter;
401 
402  posA += Position{
403  bodyConstraintA->GetInvMass() * impulse * JvAC,
404  bodyConstraintA->GetInvRotInertia() * impulse * JwA * Meter / Radian
405  };
406  posB += Position{
407  bodyConstraintB->GetInvMass() * impulse * JvBD,
408  bodyConstraintB->GetInvRotInertia() * impulse * JwB * Meter / Radian
409  };
410  posC -= Position{
411  bodyConstraintC->GetInvMass() * impulse * JvAC,
412  bodyConstraintC->GetInvRotInertia() * impulse * JwC * Meter / Radian
413  };
414  posD -= Position{
415  bodyConstraintD->GetInvMass() * impulse * JvBD,
416  bodyConstraintD->GetInvRotInertia() * impulse * JwD * Meter / Radian
417  };
418 
419  bodyConstraintA->SetPosition(posA);
420  bodyConstraintB->SetPosition(posB);
421  bodyConstraintC->SetPosition(posC);
422  bodyConstraintD->SetPosition(posD);
423 
424  // TODO_ERIN not implemented
425  return linearError < conf.linearSlop;
426 }
427 
429 {
430  return GetWorldPoint(*GetBodyA(), GetLocalAnchorA());
431 }
432 
434 {
435  return GetWorldPoint(*GetBodyB(), GetLocalAnchorB());
436 }
437 
439 {
440  return m_impulse * m_JvAC;
441 }
442 
444 {
445  return m_impulse * m_JwA / Radian;
446 }
447 
449 {
450  assert(IsValid(ratio));
451  m_ratio = ratio;
452 }
453 
454 } // namespace d2
455 } // namespace playrho