PulleyJoint.cpp
Go to the documentation of this file.
1 /*
2  * Original work Copyright (c) 2007 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 namespace playrho {
30 namespace d2 {
31 
32 // Pulley:
33 // length1 = norm(p1 - s1)
34 // length2 = norm(p2 - s2)
35 // C0 = (length1 + ratio * length2)_initial
36 // C = C0 - (length1 + ratio * length2)
37 // u1 = (p1 - s1) / norm(p1 - s1)
38 // u2 = (p2 - s2) / norm(p2 - s2)
39 // Cdot = -dot(u1, v1 + cross(w1, r1)) - ratio * dot(u2, v2 + cross(w2, r2))
40 // J = -[u1 cross(r1, u1) ratio * u2 ratio * cross(r2, u2)]
41 // K = J * invM * JT
42 // = invMass1 + invI1 * cross(r1, u1)^2 + ratio^2 * (invMass2 + invI2 * cross(r2, u2)^2)
43 
45  Joint(def),
46  m_groundAnchorA(def.groundAnchorA),
47  m_groundAnchorB(def.groundAnchorB),
48  m_localAnchorA(def.localAnchorA),
49  m_localAnchorB(def.localAnchorB),
50  m_lengthA(def.lengthA),
51  m_lengthB(def.lengthB),
52  m_ratio(def.ratio),
53  m_constant(def.lengthA + def.ratio * def.lengthB)
54 {
55  assert(!AlmostZero(def.ratio));
56 }
57 
58 void PulleyJoint::Accept(JointVisitor& visitor) const
59 {
60  visitor.Visit(*this);
61 }
62 
64 {
65  visitor.Visit(*this);
66 }
67 
68 void PulleyJoint::InitVelocityConstraints(BodyConstraintsMap& bodies,
69  const StepConf& step,
70  const ConstraintSolverConf&)
71 {
72  auto& bodyConstraintA = At(bodies, GetBodyA());
73  auto& bodyConstraintB = At(bodies, GetBodyB());
74 
75  const auto posA = bodyConstraintA->GetPosition();
76  const auto invMassA = bodyConstraintA->GetInvMass();
77  const auto invRotInertiaA = bodyConstraintA->GetInvRotInertia();
78  auto velA = bodyConstraintA->GetVelocity();
79 
80  const auto posB = bodyConstraintB->GetPosition();
81  const auto invMassB = bodyConstraintB->GetInvMass();
82  const auto invRotInertiaB = bodyConstraintB->GetInvRotInertia();
83  auto velB = bodyConstraintB->GetVelocity();
84 
85  const auto qA = UnitVec::Get(posA.angular);
86  const auto qB = UnitVec::Get(posB.angular);
87 
88  m_rA = Rotate(m_localAnchorA - bodyConstraintA->GetLocalCenter(), qA);
89  m_rB = Rotate(m_localAnchorB - bodyConstraintB->GetLocalCenter(), qB);
90 
91  // Get the pulley axes.
92  const auto pulleyAxisA = Length2{posA.linear + m_rA - m_groundAnchorA};
93  const auto pulleyAxisB = Length2{posB.linear + m_rB - m_groundAnchorB};
94 
95  m_uA = GetUnitVector(pulleyAxisA, UnitVec::GetZero());
96  m_uB = GetUnitVector(pulleyAxisB, UnitVec::GetZero());
97 
98  // Compute effective mass.
99  const auto ruA = Cross(m_rA, m_uA);
100  const auto ruB = Cross(m_rB, m_uB);
101 
102  const auto totInvMassA = invMassA + (invRotInertiaA * Square(ruA)) / SquareRadian;
103  const auto totInvMassB = invMassB + (invRotInertiaB * Square(ruB)) / SquareRadian;
104 
105  const auto totalInvMass = totInvMassA + m_ratio * m_ratio * totInvMassB;
106 
107  m_mass = (totalInvMass > InvMass{0})? Real{1} / totalInvMass: 0_kg;
108 
109  if (step.doWarmStart)
110  {
111  // Scale impulses to support variable time steps.
112  m_impulse *= step.dtRatio;
113 
114  // Warm starting.
115  const auto PA = -(m_impulse) * m_uA;
116  const auto PB = (-m_ratio * m_impulse) * m_uB;
117 
118  velA += Velocity{invMassA * PA, invRotInertiaA * Cross(m_rA, PA) / Radian};
119  velB += Velocity{invMassB * PB, invRotInertiaB * Cross(m_rB, PB) / Radian};
120  }
121  else
122  {
123  m_impulse = 0;
124  }
125 
126  bodyConstraintA->SetVelocity(velA);
127  bodyConstraintB->SetVelocity(velB);
128 }
129 
130 bool PulleyJoint::SolveVelocityConstraints(BodyConstraintsMap& bodies, const StepConf&)
131 {
132  auto& bodyConstraintA = At(bodies, GetBodyA());
133  auto& bodyConstraintB = At(bodies, GetBodyB());
134 
135  const auto invMassA = bodyConstraintA->GetInvMass();
136  const auto invRotInertiaA = bodyConstraintA->GetInvRotInertia();
137  auto velA = bodyConstraintA->GetVelocity();
138 
139  const auto invMassB = bodyConstraintB->GetInvMass();
140  const auto invRotInertiaB = bodyConstraintB->GetInvRotInertia();
141  auto velB = bodyConstraintB->GetVelocity();
142 
143  const auto vpA = LinearVelocity2{velA.linear + GetRevPerpendicular(m_rA) * (velA.angular / Radian)};
144  const auto vpB = LinearVelocity2{velB.linear + GetRevPerpendicular(m_rB) * (velB.angular / Radian)};
145 
146  const auto Cdot = LinearVelocity{-Dot(m_uA, vpA) - m_ratio * Dot(m_uB, vpB)};
147  const auto impulse = -m_mass * Cdot;
148  m_impulse += impulse;
149 
150  const auto PA = -impulse * m_uA;
151  const auto PB = -m_ratio * impulse * m_uB;
152  velA += Velocity{invMassA * PA, invRotInertiaA * Cross(m_rA, PA) / Radian};
153  velB += Velocity{invMassB * PB, invRotInertiaB * Cross(m_rB, PB) / Radian};
154 
155  bodyConstraintA->SetVelocity(velA);
156  bodyConstraintB->SetVelocity(velB);
157 
158  return impulse == 0_Ns;
159 }
160 
161 bool PulleyJoint::SolvePositionConstraints(BodyConstraintsMap& bodies,
162  const ConstraintSolverConf& conf) const
163 {
164  auto& bodyConstraintA = At(bodies, GetBodyA());
165  auto& bodyConstraintB = At(bodies, GetBodyB());
166 
167  const auto invMassA = bodyConstraintA->GetInvMass();
168  const auto invRotInertiaA = bodyConstraintA->GetInvRotInertia();
169  auto posA = bodyConstraintA->GetPosition();
170 
171  const auto invMassB = bodyConstraintB->GetInvMass();
172  const auto invRotInertiaB = bodyConstraintB->GetInvRotInertia();
173  auto posB = bodyConstraintB->GetPosition();
174 
175  const auto rA = Rotate(m_localAnchorA - bodyConstraintA->GetLocalCenter(),
176  UnitVec::Get(posA.angular));
177  const auto rB = Rotate(m_localAnchorB - bodyConstraintB->GetLocalCenter(),
178  UnitVec::Get(posB.angular));
179 
180  // Get the pulley axes.
181  const auto pA = Length2{posA.linear + rA - m_groundAnchorA};
182  const auto uvresultA = UnitVec::Get(pA[0], pA[1]);
183  const auto uA = std::get<UnitVec>(uvresultA);
184  const auto lengthA = std::get<Length>(uvresultA);
185 
186  const auto pB = Length2{posB.linear + rB - m_groundAnchorB};
187  const auto uvresultB = UnitVec::Get(pB[0], pB[1]);
188  const auto uB = std::get<UnitVec>(uvresultB);
189  const auto lengthB = std::get<Length>(uvresultB);
190 
191  // Compute effective mass.
192  const auto ruA = Length{Cross(rA, uA)};
193  const auto ruB = Length{Cross(rB, uB)};
194 
195  const auto totalInvMassA = invMassA + invRotInertiaA * ruA * ruA / SquareRadian;
196  const auto totalInvMassB = invMassB + invRotInertiaB * ruB * ruB / SquareRadian;
197 
198  const auto totalInvMass = totalInvMassA + m_ratio * m_ratio * totalInvMassB;
199  const auto mass = (totalInvMass > InvMass{0})? Real{1} / totalInvMass: 0_kg;
200 
201  const auto C = Length{m_constant - lengthA - (m_ratio * lengthB)};
202  const auto linearError = abs(C);
203 
204  const auto impulse = -mass * C;
205 
206  const auto PA = -impulse * uA;
207  const auto PB = -m_ratio * impulse * uB;
208 
209  posA += Position{invMassA * PA, invRotInertiaA * Cross(rA, PA) / Radian};
210  posB += Position{invMassB * PB, invRotInertiaB * Cross(rB, PB) / Radian};
211 
212  bodyConstraintA->SetPosition(posA);
213  bodyConstraintB->SetPosition(posB);
214 
215  return linearError < conf.linearSlop;
216 }
217 
219 {
220  return GetWorldPoint(*GetBodyA(), GetLocalAnchorA());
221 }
222 
224 {
225  return GetWorldPoint(*GetBodyB(), GetLocalAnchorB());
226 }
227 
229 {
230  return m_impulse * m_uB;
231 }
232 
234 {
235  return AngularMomentum{0};
236 }
237 
238 bool PulleyJoint::ShiftOrigin(const Length2 newOrigin)
239 {
240  m_groundAnchorA -= newOrigin;
241  m_groundAnchorB -= newOrigin;
242  return true;
243 }
244 
246 {
247  return GetMagnitude(GetWorldPoint(*joint.GetBodyA(),
248  joint.GetLocalAnchorA()) - joint.GetGroundAnchorA());
249 }
250 
252 {
253  return GetMagnitude(GetWorldPoint(*joint.GetBodyB(),
254  joint.GetLocalAnchorB()) - joint.GetGroundAnchorB());
255 }
256 
257 } // namespace d2
258 } // namespace playrho