PrismaticJoint.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 
27 
28 #include <algorithm>
29 
30 namespace playrho {
31 namespace d2 {
32 
33 // Linear constraint (point-to-line)
34 // d = p2 - p1 = x2 + r2 - x1 - r1
35 // C = dot(perp, d)
36 // Cdot = dot(d, cross(w1, perp)) + dot(perp, v2 + cross(w2, r2) - v1 - cross(w1, r1))
37 // = -dot(perp, v1) - dot(cross(d + r1, perp), w1) + dot(perp, v2) + dot(cross(r2, perp), v2)
38 // J = [-perp, -cross(d + r1, perp), perp, cross(r2,perp)]
39 //
40 // Angular constraint
41 // C = a2 - a1 + a_initial
42 // Cdot = w2 - w1
43 // J = [0 0 -1 0 0 1]
44 //
45 // K = J * invM * JT
46 //
47 // J = [-a -s1 a s2]
48 // [0 -1 0 1]
49 // a = perp
50 // s1 = cross(d + r1, a) = cross(p2 - x1, a)
51 // s2 = cross(r2, a) = cross(p2 - x2, a)
52 
53 
54 // Motor/Limit linear constraint
55 // C = dot(ax1, d)
56 // Cdot = = -dot(ax1, v1) - dot(cross(d + r1, ax1), w1) + dot(ax1, v2) + dot(cross(r2, ax1), v2)
57 // J = [-ax1 -cross(d+r1,ax1) ax1 cross(r2,ax1)]
58 
59 // Block Solver
60 // We develop a block solver that includes the joint limit. This makes the limit stiff (inelastic) even
61 // when the mass has poor distribution (leading to large torques about the joint anchor points).
62 //
63 // The Jacobian has 3 rows:
64 // J = [-uT -s1 uT s2] // linear
65 // [0 -1 0 1] // angular
66 // [-vT -a1 vT a2] // limit
67 //
68 // u = perp
69 // v = axis
70 // s1 = cross(d + r1, u), s2 = cross(r2, u)
71 // a1 = cross(d + r1, v), a2 = cross(r2, v)
72 
73 // M * (v2 - v1) = JT * df
74 // J * v2 = bias
75 //
76 // v2 = v1 + invM * JT * df
77 // J * (v1 + invM * JT * df) = bias
78 // K * df = bias - J * v1 = -Cdot
79 // K = J * invM * JT
80 // Cdot = J * v1 - bias
81 //
82 // Now solve for f2.
83 // df = f2 - f1
84 // K * (f2 - f1) = -Cdot
85 // f2 = invK * (-Cdot) + f1
86 //
87 // Clamp accumulated limit impulse.
88 // lower: f2(3) = max(f2(3), 0)
89 // upper: f2(3) = min(f2(3), 0)
90 //
91 // Solve for correct f2(1:2)
92 // K(1:2, 1:2) * f2(1:2) = -Cdot(1:2) - K(1:2,3) * f2(3) + K(1:2,1:3) * f1
93 // = -Cdot(1:2) - K(1:2,3) * f2(3) + K(1:2,1:2) * f1(1:2) + K(1:2,3) * f1(3)
94 // K(1:2, 1:2) * f2(1:2) = -Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3)) + K(1:2,1:2) * f1(1:2)
95 // f2(1:2) = invK(1:2,1:2) * (-Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3))) + f1(1:2)
96 //
97 // Now compute impulse to be applied:
98 // df = f2 - f1
99 
101  Joint(def),
102  m_localAnchorA{def.localAnchorA},
103  m_localAnchorB{def.localAnchorB},
104  m_localXAxisA{def.localAxisA},
105  m_localYAxisA{GetRevPerpendicular(m_localXAxisA)},
106  m_referenceAngle{def.referenceAngle},
107  m_lowerTranslation{def.lowerTranslation},
108  m_upperTranslation{def.upperTranslation},
109  m_maxMotorForce{def.maxMotorForce},
110  m_motorSpeed{def.motorSpeed},
111  m_enableLimit{def.enableLimit},
112  m_enableMotor{def.enableMotor}
113 {
114  // Intentionally empty.
115 }
116 
118 {
119  visitor.Visit(*this);
120 }
121 
123 {
124  visitor.Visit(*this);
125 }
126 
127 void PrismaticJoint::InitVelocityConstraints(BodyConstraintsMap& bodies,
128  const StepConf& step,
129  const ConstraintSolverConf& conf)
130 {
131  auto& bodyConstraintA = At(bodies, GetBodyA());
132  auto& bodyConstraintB = At(bodies, GetBodyB());
133 
134  const auto posA = bodyConstraintA->GetPosition();
135  const auto invMassA = bodyConstraintA->GetInvMass();
136  const auto invRotInertiaA = bodyConstraintA->GetInvRotInertia();
137  auto velA = bodyConstraintA->GetVelocity();
138 
139  const auto posB = bodyConstraintB->GetPosition();
140  const auto invMassB = bodyConstraintB->GetInvMass();
141  const auto invRotInertiaB = bodyConstraintB->GetInvRotInertia();
142  auto velB = bodyConstraintB->GetVelocity();
143 
144  const auto qA = UnitVec::Get(posA.angular);
145  const auto qB = UnitVec::Get(posB.angular);
146 
147  // Compute the effective masses.
148  const auto rA = Rotate(m_localAnchorA - bodyConstraintA->GetLocalCenter(), qA); // Length2
149  const auto rB = Rotate(m_localAnchorB - bodyConstraintB->GetLocalCenter(), qB); // Length2
150  const auto d = (posB.linear - posA.linear) + rB - rA; // Length2
151 
152  // Compute motor Jacobian and effective mass.
153  m_axis = Rotate(m_localXAxisA, qA);
154  m_a1 = Cross(d + rA, m_axis); // Length
155  m_a2 = Cross(rB, m_axis); // Length
156 
157  const auto invRotMassA = InvMass{invRotInertiaA * m_a1 * m_a1 / SquareRadian};
158  const auto invRotMassB = InvMass{invRotInertiaB * m_a2 * m_a2 / SquareRadian};
159  const auto totalInvMass = invMassA + invMassB + invRotMassA + invRotMassB;
160  m_motorMass = (totalInvMass > InvMass{0})? Real{1} / totalInvMass: 0_kg;
161 
162  // Prismatic constraint.
163  {
164  m_perp = Rotate(m_localYAxisA, qA);
165 
166  m_s1 = Cross(d + rA, m_perp);
167  m_s2 = Cross(rB, m_perp);
168 
169  const auto invRotMassA2 = InvMass{invRotInertiaA * m_s1 * m_s1 / SquareRadian};
170  const auto invRotMassB2 = InvMass{invRotInertiaB * m_s2 * m_s2 / SquareRadian};
171  const auto k11 = StripUnit(invMassA + invMassB + invRotMassA2 + invRotMassB2);
172 
173  // L^-2 M^-1 QP^2 * L is: L^-1 M^-1 QP^2.
174  const auto k12 = (invRotInertiaA * m_s1 + invRotInertiaB * m_s2) * Meter * Kilogram / SquareRadian;
175  const auto k13 = StripUnit(InvMass{(invRotInertiaA * m_s1 * m_a1 + invRotInertiaB * m_s2 * m_a2) / SquareRadian});
176  const auto totalInvRotInertia = invRotInertiaA + invRotInertiaB;
177 
178  const auto k22 = (totalInvRotInertia == InvRotInertia{0})? Real{1}: StripUnit(totalInvRotInertia);
179  const auto k23 = (invRotInertiaA * m_a1 + invRotInertiaB * m_a2) * Meter * Kilogram / SquareRadian;
180  const auto k33 = StripUnit(totalInvMass);
181 
182  GetX(m_K) = Vec3{k11, k12, k13};
183  GetY(m_K) = Vec3{k12, k22, k23};
184  GetZ(m_K) = Vec3{k13, k23, k33};
185  }
186 
187  // Compute motor and limit terms.
188  if (m_enableLimit)
189  {
190  const auto jointTranslation = Length{Dot(m_axis, d)};
191  if (abs(m_upperTranslation - m_lowerTranslation) < (conf.linearSlop * Real{2}))
192  {
193  m_limitState = e_equalLimits;
194  }
195  else if (jointTranslation <= m_lowerTranslation)
196  {
197  if (m_limitState != e_atLowerLimit)
198  {
199  m_limitState = e_atLowerLimit;
200  GetZ(m_impulse) = 0;
201  }
202  }
203  else if (jointTranslation >= m_upperTranslation)
204  {
205  if (m_limitState != e_atUpperLimit)
206  {
207  m_limitState = e_atUpperLimit;
208  GetZ(m_impulse) = 0;
209  }
210  }
211  else
212  {
213  m_limitState = e_inactiveLimit;
214  GetZ(m_impulse) = 0;
215  }
216  }
217  else
218  {
219  m_limitState = e_inactiveLimit;
220  GetZ(m_impulse) = 0;
221  }
222 
223  if (!m_enableMotor)
224  {
225  m_motorImpulse = 0;
226  }
227 
228  if (step.doWarmStart)
229  {
230  // Account for variable time step.
231  m_impulse *= step.dtRatio;
232  m_motorImpulse *= step.dtRatio;
233 
234  const auto ulImpulseX = GetX(m_impulse) * m_perp;
235  const auto Px = Momentum2{GetX(ulImpulseX) * NewtonSecond, GetY(ulImpulseX) * NewtonSecond};
236  const auto Pxs1 = Momentum{GetX(m_impulse) * m_s1 * Kilogram / Second};
237  const auto Pxs2 = Momentum{GetX(m_impulse) * m_s2 * Kilogram / Second};
238  const auto PzLength = Momentum{m_motorImpulse + GetZ(m_impulse) * NewtonSecond};
239  const auto Pz = Momentum2{PzLength * m_axis};
240  const auto P = Px + Pz;
241 
242  // AngularMomentum is L^2 M T^-1 QP^-1.
243  const auto L = AngularMomentum{GetY(m_impulse) * SquareMeter * Kilogram / (Second * Radian)};
244  const auto LA = L + (Pxs1 * Meter + PzLength * m_a1) / Radian;
245  const auto LB = L + (Pxs2 * Meter + PzLength * m_a2) / Radian;
246 
247  // InvRotInertia is L^-2 M^-1 QP^2
248  velA -= Velocity{invMassA * P, invRotInertiaA * LA};
249  velB += Velocity{invMassB * P, invRotInertiaB * LB};
250  }
251  else
252  {
253  m_impulse = Vec3{};
254  m_motorImpulse = 0;
255  }
256 
257  bodyConstraintA->SetVelocity(velA);
258  bodyConstraintB->SetVelocity(velB);
259 }
260 
261 bool PrismaticJoint::SolveVelocityConstraints(BodyConstraintsMap& bodies, const StepConf& step)
262 {
263  auto& bodyConstraintA = At(bodies, GetBodyA());
264  auto& bodyConstraintB = At(bodies, GetBodyB());
265 
266  const auto oldVelA = bodyConstraintA->GetVelocity();
267  auto velA = oldVelA;
268  const auto invMassA = bodyConstraintA->GetInvMass();
269  const auto invRotInertiaA = bodyConstraintA->GetInvRotInertia();
270 
271  const auto oldVelB = bodyConstraintB->GetVelocity();
272  auto velB = oldVelB;
273  const auto invMassB = bodyConstraintB->GetInvMass();
274  const auto invRotInertiaB = bodyConstraintB->GetInvRotInertia();
275 
276  // Solve linear motor constraint.
277  if (m_enableMotor && m_limitState != e_equalLimits)
278  {
279  const auto vDot = LinearVelocity{Dot(m_axis, velB.linear - velA.linear)};
280  const auto Cdot = vDot + (m_a2 * velB.angular - m_a1 * velA.angular) / Radian;
281  auto impulse = Momentum{m_motorMass * (m_motorSpeed * Meter / Radian - Cdot)};
282  const auto oldImpulse = m_motorImpulse;
283  const auto maxImpulse = step.GetTime() * m_maxMotorForce;
284  m_motorImpulse = std::clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse);
285  impulse = m_motorImpulse - oldImpulse;
286 
287  const auto P = Momentum2{impulse * m_axis};
288 
289  // Momentum is L^2 M T^-1. AngularMomentum is L^2 M T^-1 QP^-1.
290  const auto LA = impulse * m_a1 / Radian;
291  const auto LB = impulse * m_a2 / Radian;
292 
293  velA -= Velocity{invMassA * P, invRotInertiaA * LA};
294  velB += Velocity{invMassB * P, invRotInertiaB * LB};
295  }
296 
297  const auto velDelta = velB.linear - velA.linear;
298  const auto sRotSpeed = LinearVelocity{(m_s2 * velB.angular - m_s1 * velA.angular) / Radian};
299  const auto Cdot1 = Vec2{
300  StripUnit(Dot(m_perp, velDelta) + sRotSpeed),
301  StripUnit(velB.angular - velA.angular)
302  };
303 
304  if (m_enableLimit && (m_limitState != e_inactiveLimit))
305  {
306  // Solve prismatic and limit constraint in block form.
307  const auto deltaDot = LinearVelocity{Dot(m_axis, velDelta)};
308  const auto aRotSpeed = LinearVelocity{(m_a2 * velB.angular - m_a1 * velA.angular) / Radian};
309  const auto Cdot2 = StripUnit(deltaDot + aRotSpeed);
310  const auto Cdot = Vec3{GetX(Cdot1), GetY(Cdot1), Cdot2};
311 
312  const auto f1 = m_impulse;
313  m_impulse += Solve33(m_K, -Cdot);
314 
315  if (m_limitState == e_atLowerLimit)
316  {
317  GetZ(m_impulse) = std::max(GetZ(m_impulse), Real{0});
318  }
319  else if (m_limitState == e_atUpperLimit)
320  {
321  GetZ(m_impulse) = std::min(GetZ(m_impulse), Real{0});
322  }
323 
324  // f2(1:2) = invK(1:2,1:2) * (-Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3))) + f1(1:2)
325  const auto b = -Cdot1 - (GetZ(m_impulse) - GetZ(f1)) * Vec2{GetX(GetZ(m_K)), GetY(GetZ(m_K))};
326  const auto f2r = Solve22(m_K, b) + Vec2{GetX(f1), GetY(f1)};
327  GetX(m_impulse) = GetX(f2r);
328  GetY(m_impulse) = GetY(f2r);
329 
330  const auto df = m_impulse - f1;
331 
332  const auto ulP = GetX(df) * m_perp + GetZ(df) * m_axis;
333  const auto P = Momentum2{GetX(ulP) * NewtonSecond, GetY(ulP) * NewtonSecond};
334  const auto LA = AngularMomentum{
335  (GetX(df) * m_s1 + GetY(df) * Meter + GetZ(df) * m_a1) * NewtonSecond / Radian
336  };
337  const auto LB = AngularMomentum{
338  (GetX(df) * m_s2 + GetY(df) * Meter + GetZ(df) * m_a2) * NewtonSecond / Radian
339  };
340 
341  velA -= Velocity{invMassA * P, invRotInertiaA * LA};
342  velB += Velocity{invMassB * P, invRotInertiaB * LB};
343  }
344  else
345  {
346  // Limit is inactive, just solve the prismatic constraint in block form.
347  const auto df = Solve22(m_K, -Cdot1);
348 
349  // m_impulse is a Vec3 while df is a Vec2; so can't just m_impulse += df
350  GetX(m_impulse) += GetX(df);
351  GetY(m_impulse) += GetY(df);
352 
353  const auto ulP = GetX(df) * m_perp;
354  const auto P = Momentum2{GetX(ulP) * NewtonSecond, GetY(ulP) * NewtonSecond};
355  const auto LA = AngularMomentum{
356  (GetX(df) * m_s1 + GetY(df) * Meter) * NewtonSecond / Radian
357  };
358  const auto LB = AngularMomentum{
359  (GetX(df) * m_s2 + GetY(df) * Meter) * NewtonSecond / Radian
360  };
361 
362  velA -= Velocity{invMassA * P, invRotInertiaA * LA};
363  velB += Velocity{invMassB * P, invRotInertiaB * LB};
364  }
365 
366  if ((velA != oldVelA) || (velB != oldVelB))
367  {
368  bodyConstraintA->SetVelocity(velA);
369  bodyConstraintB->SetVelocity(velB);
370  return false;
371  }
372  return true;
373 }
374 
375 // A velocity based solver computes reaction forces(impulses) using the velocity constraint solver.
376 // Under this context, the position solver is not there to resolve forces. It is only there to cope
377 // with integration error.
378 //
379 // Therefore, the pseudo impulses in the position solver do not have any physical meaning. Thus it
380 // is okay if they suck.
381 //
382 // We could take the active state from the velocity solver. However, the joint might push past the
383 // limit when the velocity solver indicates the limit is inactive.
384 //
385 bool PrismaticJoint::SolvePositionConstraints(BodyConstraintsMap& bodies, const ConstraintSolverConf& conf) const
386 {
387  auto& bodyConstraintA = At(bodies, GetBodyA());
388  auto& bodyConstraintB = At(bodies, GetBodyB());
389 
390  auto posA = bodyConstraintA->GetPosition();
391  const auto invMassA = bodyConstraintA->GetInvMass();
392  const auto invRotInertiaA = bodyConstraintA->GetInvRotInertia();
393 
394  auto posB = bodyConstraintB->GetPosition();
395  const auto invMassB = bodyConstraintB->GetInvMass();
396  const auto invRotInertiaB = bodyConstraintB->GetInvRotInertia();
397 
398  const auto qA = UnitVec::Get(posA.angular);
399  const auto qB = UnitVec::Get(posB.angular);
400 
401  // Compute fresh Jacobians
402  const auto rA = Rotate(m_localAnchorA - bodyConstraintA->GetLocalCenter(), qA);
403  const auto rB = Rotate(m_localAnchorB - bodyConstraintB->GetLocalCenter(), qB);
404  const auto d = Length2{(posB.linear + rB) - (posA.linear + rA)};
405 
406  const auto axis = Rotate(m_localXAxisA, qA);
407  const auto a1 = Length{Cross(d + rA, axis)};
408  const auto a2 = Length{Cross(rB, axis)};
409  const auto perp = Rotate(m_localYAxisA, qA);
410 
411  const auto s1 = Length{Cross(d + rA, perp)};
412  const auto s2 = Length{Cross(rB, perp)};
413 
414  const auto C1 = Vec2{
415  Dot(perp, d) / Meter,
416  (posB.angular - posA.angular - m_referenceAngle) / Radian
417  };
418 
419  auto linearError = Length{abs(GetX(C1)) * Meter};
420  const auto angularError = Angle{abs(GetY(C1)) * Radian};
421 
422  auto active = false;
423  auto C2 = Real{0};
424  if (m_enableLimit)
425  {
426  const auto translation = Length{Dot(axis, d)};
427  if (abs(m_upperTranslation - m_lowerTranslation) < (Real{2} * conf.linearSlop))
428  {
429  // Prevent large angular corrections
430  C2 = StripUnit(std::clamp(translation, -conf.maxLinearCorrection, conf.maxLinearCorrection));
431  linearError = std::max(linearError, abs(translation));
432  active = true;
433  }
434  else if (translation <= m_lowerTranslation)
435  {
436  // Prevent large linear corrections and allow some slop.
437  C2 = StripUnit(std::clamp(translation - m_lowerTranslation + conf.linearSlop, -conf.maxLinearCorrection, 0_m));
438  linearError = std::max(linearError, m_lowerTranslation - translation);
439  active = true;
440  }
441  else if (translation >= m_upperTranslation)
442  {
443  // Prevent large linear corrections and allow some slop.
444  C2 = StripUnit(std::clamp(translation - m_upperTranslation - conf.linearSlop, 0_m, conf.maxLinearCorrection));
445  linearError = std::max(linearError, translation - m_upperTranslation);
446  active = true;
447  }
448  }
449 
450  Vec3 impulse;
451  if (active)
452  {
453  const auto k11 = StripUnit(InvMass{
454  invMassA + invRotInertiaA * s1 * s1 / SquareRadian +
455  invMassB + invRotInertiaB * s2 * s2 / SquareRadian
456  });
457  const auto k12 = StripUnit(InvMass{
458  invRotInertiaA * s1 * Meter / SquareRadian +
459  invRotInertiaB * s2 * Meter / SquareRadian
460  });
461  const auto k13 = StripUnit(InvMass{
462  invRotInertiaA * s1 * a1 / SquareRadian +
463  invRotInertiaB * s2 * a2 / SquareRadian
464  });
465 
466  // InvRotInertia is L^-2 M^-1 QP^2
467  auto k22 = StripUnit(invRotInertiaA + invRotInertiaB);
468  if (k22 == Real{0})
469  {
470  // For fixed rotation
471  k22 = StripUnit(Real{1} * SquareRadian / (Kilogram * SquareMeter));
472  }
473  const auto k23 = StripUnit(InvMass{
474  invRotInertiaA * a1 * Meter / SquareRadian +
475  invRotInertiaB * a2 * Meter / SquareRadian
476  });
477  const auto k33 = StripUnit(InvMass{
478  invMassA + invRotInertiaA * Square(a1) / SquareRadian +
479  invMassB + invRotInertiaB * Square(a2) / SquareRadian
480  });
481 
482  const auto K = Mat33{Vec3{k11, k12, k13}, Vec3{k12, k22, k23}, Vec3{k13, k23, k33}};
483  const auto C = Vec3{GetX(C1), GetY(C1), C2};
484 
485  impulse = Solve33(K, -C);
486  }
487  else
488  {
489  const auto k11 = StripUnit(InvMass{
490  invMassA + invRotInertiaA * s1 * s1 / SquareRadian +
491  invMassB + invRotInertiaB * s2 * s2 / SquareRadian
492  });
493  const auto k12 = StripUnit(InvMass{
494  invRotInertiaA * s1 * Meter / SquareRadian +
495  invRotInertiaB * s2 * Meter / SquareRadian
496  });
497  auto k22 = StripUnit(invRotInertiaA + invRotInertiaB);
498  if (k22 == 0)
499  {
500  k22 = 1;
501  }
502 
503  const auto K = Mat22{Vec2{k11, k12}, Vec2{k12, k22}};
504 
505  const auto impulse1 = Solve(K, -C1);
506  GetX(impulse) = GetX(impulse1);
507  GetY(impulse) = GetY(impulse1);
508  GetZ(impulse) = 0;
509  }
510 
511  const auto P = (GetX(impulse) * perp + GetZ(impulse) * axis) * Kilogram * Meter;
512  const auto LA = (GetX(impulse) * s1 + GetY(impulse) * Meter + GetZ(impulse) * a1) * Kilogram * Meter / Radian;
513  const auto LB = (GetX(impulse) * s2 + GetY(impulse) * Meter + GetZ(impulse) * a2) * Kilogram * Meter / Radian;
514 
515  posA -= Position{Length2{invMassA * P}, invRotInertiaA * LA};
516  posB += Position{invMassB * P, invRotInertiaB * LB};
517 
518  bodyConstraintA->SetPosition(posA);
519  bodyConstraintB->SetPosition(posB);
520 
521  return (linearError <= conf.linearSlop) && (angularError <= conf.angularSlop);
522 }
523 
525 {
526  return GetWorldPoint(*GetBodyA(), GetLocalAnchorA());
527 }
528 
530 {
531  return GetWorldPoint(*GetBodyB(), GetLocalAnchorB());
532 }
533 
535 {
536  const auto ulImpulse = GetX(m_impulse) * m_perp;
537  const auto impulse = Momentum2{GetX(ulImpulse) * NewtonSecond, GetY(ulImpulse) * NewtonSecond};
538  return Momentum2{impulse + (m_motorImpulse + GetZ(m_impulse) * NewtonSecond) * m_axis};
539 }
540 
542 {
543  return GetY(m_impulse) * SquareMeter * Kilogram / (Second * Radian);
544 }
545 
546 void PrismaticJoint::EnableLimit(bool flag) noexcept
547 {
548  if (m_enableLimit != flag)
549  {
550  m_enableLimit = flag;
551  GetZ(m_impulse) = 0;
552 
553  GetBodyA()->SetAwake();
554  GetBodyB()->SetAwake();
555  }
556 }
557 
558 void PrismaticJoint::SetLimits(Length lower, Length upper) noexcept
559 {
560  assert(lower <= upper);
561  if ((lower != m_lowerTranslation) || (upper != m_upperTranslation))
562  {
563  m_lowerTranslation = lower;
564  m_upperTranslation = upper;
565  GetZ(m_impulse) = 0;
566 
567  GetBodyA()->SetAwake();
568  GetBodyB()->SetAwake();
569  }
570 }
571 
572 void PrismaticJoint::EnableMotor(bool flag) noexcept
573 {
574  if (m_enableMotor != flag)
575  {
576  m_enableMotor = flag;
577 
578  // XXX Should these be called regardless of whether the state changed?
579  GetBodyA()->SetAwake();
580  GetBodyB()->SetAwake();
581  }
582 }
583 
585 {
586  if (m_motorSpeed != speed)
587  {
588  m_motorSpeed = speed;
589 
590  // XXX Should these be called regardless of whether the state changed?
591  GetBodyA()->SetAwake();
592  GetBodyB()->SetAwake();
593  }
594 }
595 
597 {
598  if (m_maxMotorForce != force)
599  {
600  m_maxMotorForce = force;
601 
602  // XXX Should these be called regardless of whether the state changed?
603  GetBodyA()->SetAwake();
604  GetBodyB()->SetAwake();
605  }
606 }
607 
609 {
610  const auto pA = GetWorldPoint(*joint.GetBodyA(), joint.GetLocalAnchorA());
611  const auto pB = GetWorldPoint(*joint.GetBodyB(), joint.GetLocalAnchorB());
612  return Dot(pB - pA, GetWorldVector(*joint.GetBodyA(), joint.GetLocalAxisA()));
613 }
614 
616 {
617  const auto bA = joint.GetBodyA();
618  const auto bB = joint.GetBodyB();
619 
620  const auto rA = Rotate(joint.GetLocalAnchorA() - bA->GetLocalCenter(), bA->GetTransformation().q);
621  const auto rB = Rotate(joint.GetLocalAnchorB() - bB->GetLocalCenter(), bB->GetTransformation().q);
622  const auto p1 = bA->GetWorldCenter() + rA;
623  const auto p2 = bB->GetWorldCenter() + rB;
624  const auto d = p2 - p1;
625  const auto axis = Rotate(joint.GetLocalAxisA(), bA->GetTransformation().q);
626 
627  const auto vA = bA->GetVelocity().linear;
628  const auto vB = bB->GetVelocity().linear;
629  const auto wA = bA->GetVelocity().angular;
630  const auto wB = bB->GetVelocity().angular;
631 
632  const auto vel = (vB + (GetRevPerpendicular(rB) * (wB / Radian))) -
633  (vA + (GetRevPerpendicular(rA) * (wA / Radian)));
634  return Dot(d, (GetRevPerpendicular(axis) * (wA / Radian))) + Dot(axis, vel);
635 }
636 
637 } // namespace d2
638 } // namespace playrho