TargetJoint.cpp
Go to the documentation of this file.
1 /*
2  * Original work Copyright (c) 2006-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 
27 
28 namespace playrho {
29 namespace d2 {
30 
31 // p = attached point, m = mouse point
32 // C = p - m
33 // Cdot = v
34 // = v + cross(w, r)
35 // J = [I r_skew]
36 // Identity used:
37 // w k % (rx i + ry j) = w * (-ry i + rx j)
38 
39 
40 bool TargetJoint::IsOkay(const TargetJointConf& def) noexcept
41 {
42  if (!Joint::IsOkay(def))
43  {
44  return false;
45  }
46  if (!IsValid(def.target))
47  {
48  return false;
49  }
50  return true;
51 }
52 
54  Joint{def},
55  m_targetA{def.target},
56  m_localAnchorB{def.bodyB?
57  InverseTransform(def.target, def.bodyB->GetTransformation()):
58  GetInvalid<decltype(m_localAnchorB)>()},
59  m_frequency{def.frequency},
60  m_dampingRatio{def.dampingRatio},
61  m_maxForce{def.maxForce}
62 {
63  assert(IsValid(def.target));
64  assert(IsValid(def.dampingRatio));
65 }
66 
67 void TargetJoint::Accept(JointVisitor& visitor) const
68 {
69  visitor.Visit(*this);
70 }
71 
73 {
74  visitor.Visit(*this);
75 }
76 
77 void TargetJoint::SetTarget(const Length2 target) noexcept
78 {
79  assert(IsValid(target));
80  if (m_targetA != target)
81  {
82  m_targetA = target;
83 
84  GetBodyB()->SetAwake();
85  }
86 }
87 
88 Mass22 TargetJoint::GetEffectiveMassMatrix(const BodyConstraint& body) const noexcept
89 {
90  // K = [(1/m1 + 1/m2) * eye(2) - skew(r1) * invI1 * skew(r1) - skew(r2) * invI2 * skew(r2)]
91  // = [1/m1+1/m2 0 ] + invI1 * [r1.y*r1.y -r1.x*r1.y] + invI2 * [r1.y*r1.y -r1.x*r1.y]
92  // [ 0 1/m1+1/m2] [-r1.x*r1.y r1.x*r1.x] [-r1.x*r1.y r1.x*r1.x]
93 
94  const auto invMass = body.GetInvMass();
95  const auto invRotInertia = body.GetInvRotInertia();
96 
97  const auto exx = InvMass{invMass + (invRotInertia * Square(GetY(m_rB)) / SquareRadian) + m_gamma};
98  const auto exy = InvMass{-invRotInertia * GetX(m_rB) * GetY(m_rB) / SquareRadian};
99  const auto eyy = InvMass{invMass + (invRotInertia * Square(GetX(m_rB)) / SquareRadian) + m_gamma};
100 
101  InvMass22 K;
102  GetX(GetX(K)) = exx;
103  GetY(GetX(K)) = exy;
104  GetX(GetY(K)) = exy;
105  GetY(GetY(K)) = eyy;
106  return Invert(K);
107 }
108 
109 void TargetJoint::InitVelocityConstraints(BodyConstraintsMap& bodies, const StepConf& step,
110  const ConstraintSolverConf&)
111 {
112  auto& bodyConstraintB = At(bodies, GetBodyB());
113 
114  const auto posB = bodyConstraintB->GetPosition();
115  auto velB = bodyConstraintB->GetVelocity();
116 
117  const auto qB = UnitVec::Get(posB.angular);
118 
119  const auto mass = GetMass(*GetBodyB());
120 
121  // Frequency
122  const auto omega = Real{2} * Pi * m_frequency; // T^-1
123 
124  // Damping coefficient
125  const auto d = Real{2} * mass * m_dampingRatio * omega; // M T^-1
126 
127  // Spring stiffness
128  const auto k = mass * Square(omega); // M T^-2
129 
130  // magic formulas
131  // gamma has units of inverse mass.
132  // beta has units of inverse time.
133  const auto h = step.GetTime();
134  const auto tmp = d + h * k; // M T^-1
135  assert(IsValid(Real{tmp * Second / Kilogram}));
136  assert((tmp > Real{0} * Kilogram / Second) && !AlmostZero(tmp * Second / Kilogram));
137  const auto invGamma = Mass{h * tmp}; // M T^-1 * T is simply M.
138  m_gamma = (invGamma != 0_kg)? Real{1} / invGamma: InvMass{0};
139  const auto beta = Frequency{h * k * m_gamma}; // T * M T^-2 * M^-1 is T^-1
140 
141  // Compute the effective mass matrix.
142  m_rB = Rotate(m_localAnchorB - bodyConstraintB->GetLocalCenter(), qB);
143 
144  m_mass = GetEffectiveMassMatrix(*bodyConstraintB);
145 
146  m_C = LinearVelocity2{((posB.linear + m_rB) - m_targetA) * beta};
147  assert(IsValid(m_C));
148 
149  // Cheat with some damping
150  velB.angular *= 0.98f;
151 
152  if (step.doWarmStart)
153  {
154  m_impulse *= step.dtRatio;
155  const auto P = m_impulse;
156  const auto crossBP = AngularMomentum{Cross(m_rB, P) / Radian}; // L * M * L T^-1 is: L^2 M T^-1
157  velB += Velocity{bodyConstraintB->GetInvMass() * P, bodyConstraintB->GetInvRotInertia() * crossBP};
158  }
159  else
160  {
161  m_impulse = Momentum2{};
162  }
163 
164  bodyConstraintB->SetVelocity(velB);
165 }
166 
167 bool TargetJoint::SolveVelocityConstraints(BodyConstraintsMap& bodies, const StepConf& step)
168 {
169  auto& bodyConstraintB = At(bodies, GetBodyB());
170 
171  auto velB = bodyConstraintB->GetVelocity();
172  assert(IsValid(velB));
173 
174  const auto Cdot = LinearVelocity2{velB.linear + (GetRevPerpendicular(m_rB) * (velB.angular / Radian))};
175  const auto ev = Cdot + LinearVelocity2{m_C + (m_gamma * m_impulse)};
176  const auto oldImpulse = m_impulse;
177  const auto addImpulse = Transform(-ev, m_mass);
178  assert(IsValid(addImpulse));
179  m_impulse += addImpulse;
180  const auto maxImpulse = step.GetTime() * Force{m_maxForce};
181  if (GetMagnitudeSquared(m_impulse) > Square(maxImpulse))
182  {
183  m_impulse = GetUnitVector(m_impulse, UnitVec::GetZero()) * maxImpulse;
184  }
185 
186  const auto incImpulse = (m_impulse - oldImpulse);
187  const auto angImpulseB = AngularMomentum{Cross(m_rB, incImpulse) / Radian};
188 
189  velB += Velocity{
190  bodyConstraintB->GetInvMass() * incImpulse,
191  bodyConstraintB->GetInvRotInertia() * angImpulseB
192  };
193 
194  bodyConstraintB->SetVelocity(velB);
195 
196  return incImpulse == Momentum2{};
197 }
198 
199 bool TargetJoint::SolvePositionConstraints(BodyConstraintsMap& bodies, const ConstraintSolverConf& conf) const
200 {
201  NOT_USED(bodies);
202  NOT_USED(conf);
203  return true;
204 }
205 
207 {
208  return GetTarget();
209 }
210 
212 {
213  return GetBodyB()? GetWorldPoint(*GetBodyB(), GetLocalAnchorB()): GetInvalid<Length2>();
214 }
215 
217 {
218  return m_impulse;
219 }
220 
222 {
223  return AngularMomentum{0};
224 }
225 
226 bool TargetJoint::ShiftOrigin(const Length2 newOrigin)
227 {
228  m_targetA -= newOrigin;
229  return true;
230 }
231 
232 } // namespace d2
233 } // namespace playrho