1 /*
2 * Copyright (c) 2006-2007 Erin Catto http://www.box2d.org
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty.  In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 * Permission is granted to anyone to use this software for any purpose,
8 * including commercial applications, and to alter it and redistribute it
9 * freely, subject to the following restrictions:
10 * 1. The origin of this software must not be misrepresented; you must not
11 * claim that you wrote the original software. If you use this software
12 * in a product, an acknowledgment in the product documentation would be
13 * appreciated but is not required.
14 * 2. Altered source versions must be plainly marked as such, and must not be
15 * misrepresented as being the original software.
16 * 3. This notice may not be removed or altered from any source distribution.
17 */
18 
19 #include <Box2D/Dynamics/Joints/b2WheelJoint.h>
20 #include <Box2D/Dynamics/b2Body.h>
21 #include <Box2D/Dynamics/b2TimeStep.h>
22 
23 // Linear constraint (point-to-line)
24 // d = pB - pA = xB + rB - xA - rA
25 // C = dot(ay, d)
26 // Cdot = dot(d, cross(wA, ay)) + dot(ay, vB + cross(wB, rB) - vA - cross(wA, rA))
27 //      = -dot(ay, vA) - dot(cross(d + rA, ay), wA) + dot(ay, vB) + dot(cross(rB, ay), vB)
28 // J = [-ay, -cross(d + rA, ay), ay, cross(rB, ay)]
29 
30 // Spring linear constraint
31 // C = dot(ax, d)
32 // Cdot = = -dot(ax, vA) - dot(cross(d + rA, ax), wA) + dot(ax, vB) + dot(cross(rB, ax), vB)
33 // J = [-ax -cross(d+rA, ax) ax cross(rB, ax)]
34 
35 // Motor rotational constraint
36 // Cdot = wB - wA
37 // J = [0 0 -1 0 0 1]
38 
Initialize(b2Body * bA,b2Body * bB,const b2Vec2 & anchor,const b2Vec2 & axis)39 void b2WheelJointDef::Initialize(b2Body* bA, b2Body* bB, const b2Vec2& anchor, const b2Vec2& axis)
40 {
41 	bodyA = bA;
42 	bodyB = bB;
43 	localAnchorA = bodyA->GetLocalPoint(anchor);
44 	localAnchorB = bodyB->GetLocalPoint(anchor);
45 	localAxisA = bodyA->GetLocalVector(axis);
46 }
47 
b2WheelJoint(const b2WheelJointDef * def)48 b2WheelJoint::b2WheelJoint(const b2WheelJointDef* def)
49 : b2Joint(def)
50 {
51 	m_localAnchorA = def->localAnchorA;
52 	m_localAnchorB = def->localAnchorB;
53 	m_localXAxisA = def->localAxisA;
54 	m_localYAxisA = b2Cross(1.0f, m_localXAxisA);
55 
56 	m_mass = 0.0f;
57 	m_impulse = 0.0f;
58 	m_motorMass = 0.0;
59 	m_motorImpulse = 0.0f;
60 	m_springMass = 0.0f;
61 	m_springImpulse = 0.0f;
62 
63 	m_maxMotorTorque = def->maxMotorTorque;
64 	m_motorSpeed = def->motorSpeed;
65 	m_enableMotor = def->enableMotor;
66 
67 	m_frequencyHz = def->frequencyHz;
68 	m_dampingRatio = def->dampingRatio;
69 
70 	m_bias = 0.0f;
71 	m_gamma = 0.0f;
72 
73 	m_ax.SetZero();
74 	m_ay.SetZero();
75 }
76 
InitVelocityConstraints(const b2SolverData & data)77 void b2WheelJoint::InitVelocityConstraints(const b2SolverData& data)
78 {
79 	m_indexA = m_bodyA->m_islandIndex;
80 	m_indexB = m_bodyB->m_islandIndex;
81 	m_localCenterA = m_bodyA->m_sweep.localCenter;
82 	m_localCenterB = m_bodyB->m_sweep.localCenter;
83 	m_invMassA = m_bodyA->m_invMass;
84 	m_invMassB = m_bodyB->m_invMass;
85 	m_invIA = m_bodyA->m_invI;
86 	m_invIB = m_bodyB->m_invI;
87 
88 	float32 mA = m_invMassA, mB = m_invMassB;
89 	float32 iA = m_invIA, iB = m_invIB;
90 
91 	b2Vec2 cA = data.positions[m_indexA].c;
92 	float32 aA = data.positions[m_indexA].a;
93 	b2Vec2 vA = data.velocities[m_indexA].v;
94 	float32 wA = data.velocities[m_indexA].w;
95 
96 	b2Vec2 cB = data.positions[m_indexB].c;
97 	float32 aB = data.positions[m_indexB].a;
98 	b2Vec2 vB = data.velocities[m_indexB].v;
99 	float32 wB = data.velocities[m_indexB].w;
100 
101 	b2Rot qA(aA), qB(aB);
102 
103 	// Compute the effective masses.
104 	b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
105 	b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
106 	b2Vec2 d = cB + rB - cA - rA;
107 
108 	// Point to line constraint
109 	{
110 		m_ay = b2Mul(qA, m_localYAxisA);
111 		m_sAy = b2Cross(d + rA, m_ay);
112 		m_sBy = b2Cross(rB, m_ay);
113 
114 		m_mass = mA + mB + iA * m_sAy * m_sAy + iB * m_sBy * m_sBy;
115 
116 		if (m_mass > 0.0f)
117 		{
118 			m_mass = 1.0f / m_mass;
119 		}
120 	}
121 
122 	// Spring constraint
123 	m_springMass = 0.0f;
124 	m_bias = 0.0f;
125 	m_gamma = 0.0f;
126 	if (m_frequencyHz > 0.0f)
127 	{
128 		m_ax = b2Mul(qA, m_localXAxisA);
129 		m_sAx = b2Cross(d + rA, m_ax);
130 		m_sBx = b2Cross(rB, m_ax);
131 
132 		float32 invMass = mA + mB + iA * m_sAx * m_sAx + iB * m_sBx * m_sBx;
133 
134 		if (invMass > 0.0f)
135 		{
136 			m_springMass = 1.0f / invMass;
137 
138 			float32 C = b2Dot(d, m_ax);
139 
140 			// Frequency
141 			float32 omega = 2.0f * b2_pi * m_frequencyHz;
142 
143 			// Damping coefficient
144 			float32 d = 2.0f * m_springMass * m_dampingRatio * omega;
145 
146 			// Spring stiffness
147 			float32 k = m_springMass * omega * omega;
148 
149 			// magic formulas
150 			float32 h = data.step.dt;
151 			m_gamma = h * (d + h * k);
152 			if (m_gamma > 0.0f)
153 			{
154 				m_gamma = 1.0f / m_gamma;
155 			}
156 
157 			m_bias = C * h * k * m_gamma;
158 
159 			m_springMass = invMass + m_gamma;
160 			if (m_springMass > 0.0f)
161 			{
162 				m_springMass = 1.0f / m_springMass;
163 			}
164 		}
165 	}
166 	else
167 	{
168 		m_springImpulse = 0.0f;
169 	}
170 
171 	// Rotational motor
172 	if (m_enableMotor)
173 	{
174 		m_motorMass = iA + iB;
175 		if (m_motorMass > 0.0f)
176 		{
177 			m_motorMass = 1.0f / m_motorMass;
178 		}
179 	}
180 	else
181 	{
182 		m_motorMass = 0.0f;
183 		m_motorImpulse = 0.0f;
184 	}
185 
186 	if (data.step.warmStarting)
187 	{
188 		// Account for variable time step.
189 		m_impulse *= data.step.dtRatio;
190 		m_springImpulse *= data.step.dtRatio;
191 		m_motorImpulse *= data.step.dtRatio;
192 
193 		b2Vec2 P = m_impulse * m_ay + m_springImpulse * m_ax;
194 		float32 LA = m_impulse * m_sAy + m_springImpulse * m_sAx + m_motorImpulse;
195 		float32 LB = m_impulse * m_sBy + m_springImpulse * m_sBx + m_motorImpulse;
196 
197 		vA -= m_invMassA * P;
198 		wA -= m_invIA * LA;
199 
200 		vB += m_invMassB * P;
201 		wB += m_invIB * LB;
202 	}
203 	else
204 	{
205 		m_impulse = 0.0f;
206 		m_springImpulse = 0.0f;
207 		m_motorImpulse = 0.0f;
208 	}
209 
210 	data.velocities[m_indexA].v = vA;
211 	data.velocities[m_indexA].w = wA;
212 	data.velocities[m_indexB].v = vB;
213 	data.velocities[m_indexB].w = wB;
214 }
215 
SolveVelocityConstraints(const b2SolverData & data)216 void b2WheelJoint::SolveVelocityConstraints(const b2SolverData& data)
217 {
218 	float32 mA = m_invMassA, mB = m_invMassB;
219 	float32 iA = m_invIA, iB = m_invIB;
220 
221 	b2Vec2 vA = data.velocities[m_indexA].v;
222 	float32 wA = data.velocities[m_indexA].w;
223 	b2Vec2 vB = data.velocities[m_indexB].v;
224 	float32 wB = data.velocities[m_indexB].w;
225 
226 	// Solve spring constraint
227 	{
228 		float32 Cdot = b2Dot(m_ax, vB - vA) + m_sBx * wB - m_sAx * wA;
229 		float32 impulse = -m_springMass * (Cdot + m_bias + m_gamma * m_springImpulse);
230 		m_springImpulse += impulse;
231 
232 		b2Vec2 P = impulse * m_ax;
233 		float32 LA = impulse * m_sAx;
234 		float32 LB = impulse * m_sBx;
235 
236 		vA -= mA * P;
237 		wA -= iA * LA;
238 
239 		vB += mB * P;
240 		wB += iB * LB;
241 	}
242 
243 	// Solve rotational motor constraint
244 	{
245 		float32 Cdot = wB - wA - m_motorSpeed;
246 		float32 impulse = -m_motorMass * Cdot;
247 
248 		float32 oldImpulse = m_motorImpulse;
249 		float32 maxImpulse = data.step.dt * m_maxMotorTorque;
250 		m_motorImpulse = b2Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse);
251 		impulse = m_motorImpulse - oldImpulse;
252 
253 		wA -= iA * impulse;
254 		wB += iB * impulse;
255 	}
256 
257 	// Solve point to line constraint
258 	{
259 		float32 Cdot = b2Dot(m_ay, vB - vA) + m_sBy * wB - m_sAy * wA;
260 		float32 impulse = -m_mass * Cdot;
261 		m_impulse += impulse;
262 
263 		b2Vec2 P = impulse * m_ay;
264 		float32 LA = impulse * m_sAy;
265 		float32 LB = impulse * m_sBy;
266 
267 		vA -= mA * P;
268 		wA -= iA * LA;
269 
270 		vB += mB * P;
271 		wB += iB * LB;
272 	}
273 
274 	data.velocities[m_indexA].v = vA;
275 	data.velocities[m_indexA].w = wA;
276 	data.velocities[m_indexB].v = vB;
277 	data.velocities[m_indexB].w = wB;
278 }
279 
SolvePositionConstraints(const b2SolverData & data)280 bool b2WheelJoint::SolvePositionConstraints(const b2SolverData& data)
281 {
282 	b2Vec2 cA = data.positions[m_indexA].c;
283 	float32 aA = data.positions[m_indexA].a;
284 	b2Vec2 cB = data.positions[m_indexB].c;
285 	float32 aB = data.positions[m_indexB].a;
286 
287 	b2Rot qA(aA), qB(aB);
288 
289 	b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
290 	b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
291 	b2Vec2 d = (cB - cA) + rB - rA;
292 
293 	b2Vec2 ay = b2Mul(qA, m_localYAxisA);
294 
295 	float32 sAy = b2Cross(d + rA, ay);
296 	float32 sBy = b2Cross(rB, ay);
297 
298 	float32 C = b2Dot(d, ay);
299 
300 	float32 k = m_invMassA + m_invMassB + m_invIA * m_sAy * m_sAy + m_invIB * m_sBy * m_sBy;
301 
302 	float32 impulse;
303 	if (k != 0.0f)
304 	{
305 		impulse = - C / k;
306 	}
307 	else
308 	{
309 		impulse = 0.0f;
310 	}
311 
312 	b2Vec2 P = impulse * ay;
313 	float32 LA = impulse * sAy;
314 	float32 LB = impulse * sBy;
315 
316 	cA -= m_invMassA * P;
317 	aA -= m_invIA * LA;
318 	cB += m_invMassB * P;
319 	aB += m_invIB * LB;
320 
321 	data.positions[m_indexA].c = cA;
322 	data.positions[m_indexA].a = aA;
323 	data.positions[m_indexB].c = cB;
324 	data.positions[m_indexB].a = aB;
325 
326 	return b2Abs(C) <= b2_linearSlop;
327 }
328 
GetAnchorA() const329 b2Vec2 b2WheelJoint::GetAnchorA() const
330 {
331 	return m_bodyA->GetWorldPoint(m_localAnchorA);
332 }
333 
GetAnchorB() const334 b2Vec2 b2WheelJoint::GetAnchorB() const
335 {
336 	return m_bodyB->GetWorldPoint(m_localAnchorB);
337 }
338 
GetReactionForce(float32 inv_dt) const339 b2Vec2 b2WheelJoint::GetReactionForce(float32 inv_dt) const
340 {
341 	return inv_dt * (m_impulse * m_ay + m_springImpulse * m_ax);
342 }
343 
GetReactionTorque(float32 inv_dt) const344 float32 b2WheelJoint::GetReactionTorque(float32 inv_dt) const
345 {
346 	return inv_dt * m_motorImpulse;
347 }
348 
GetJointTranslation() const349 float32 b2WheelJoint::GetJointTranslation() const
350 {
351 	b2Body* bA = m_bodyA;
352 	b2Body* bB = m_bodyB;
353 
354 	b2Vec2 pA = bA->GetWorldPoint(m_localAnchorA);
355 	b2Vec2 pB = bB->GetWorldPoint(m_localAnchorB);
356 	b2Vec2 d = pB - pA;
357 	b2Vec2 axis = bA->GetWorldVector(m_localXAxisA);
358 
359 	float32 translation = b2Dot(d, axis);
360 	return translation;
361 }
362 
GetJointSpeed() const363 float32 b2WheelJoint::GetJointSpeed() const
364 {
365 	float32 wA = m_bodyA->m_angularVelocity;
366 	float32 wB = m_bodyB->m_angularVelocity;
367 	return wB - wA;
368 }
369 
IsMotorEnabled() const370 bool b2WheelJoint::IsMotorEnabled() const
371 {
372 	return m_enableMotor;
373 }
374 
EnableMotor(bool flag)375 void b2WheelJoint::EnableMotor(bool flag)
376 {
377 	m_bodyA->SetAwake(true);
378 	m_bodyB->SetAwake(true);
379 	m_enableMotor = flag;
380 }
381 
SetMotorSpeed(float32 speed)382 void b2WheelJoint::SetMotorSpeed(float32 speed)
383 {
384 	m_bodyA->SetAwake(true);
385 	m_bodyB->SetAwake(true);
386 	m_motorSpeed = speed;
387 }
388 
SetMaxMotorTorque(float32 torque)389 void b2WheelJoint::SetMaxMotorTorque(float32 torque)
390 {
391 	m_bodyA->SetAwake(true);
392 	m_bodyB->SetAwake(true);
393 	m_maxMotorTorque = torque;
394 }
395 
GetMotorTorque(float32 inv_dt) const396 float32 b2WheelJoint::GetMotorTorque(float32 inv_dt) const
397 {
398 	return inv_dt * m_motorImpulse;
399 }
400 
Dump()401 void b2WheelJoint::Dump()
402 {
403 	int32 indexA = m_bodyA->m_islandIndex;
404 	int32 indexB = m_bodyB->m_islandIndex;
405 
406 	b2Log("  b2WheelJointDef jd;\n");
407 	b2Log("  jd.bodyA = bodies[%d];\n", indexA);
408 	b2Log("  jd.bodyB = bodies[%d];\n", indexB);
409 	b2Log("  jd.collideConnected = bool(%d);\n", m_collideConnected);
410 	b2Log("  jd.localAnchorA.Set(%.15lef, %.15lef);\n", m_localAnchorA.x, m_localAnchorA.y);
411 	b2Log("  jd.localAnchorB.Set(%.15lef, %.15lef);\n", m_localAnchorB.x, m_localAnchorB.y);
412 	b2Log("  jd.localAxisA.Set(%.15lef, %.15lef);\n", m_localXAxisA.x, m_localXAxisA.y);
413 	b2Log("  jd.enableMotor = bool(%d);\n", m_enableMotor);
414 	b2Log("  jd.motorSpeed = %.15lef;\n", m_motorSpeed);
415 	b2Log("  jd.maxMotorTorque = %.15lef;\n", m_maxMotorTorque);
416 	b2Log("  jd.frequencyHz = %.15lef;\n", m_frequencyHz);
417 	b2Log("  jd.dampingRatio = %.15lef;\n", m_dampingRatio);
418 	b2Log("  joints[%d] = m_world->CreateJoint(&jd);\n", m_index);
419 }
420