/******************************************************************************* * Copyright (c) 2013, Daniel Murphy * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ /** * Created at 11:34:45 AM Jan 23, 2011 */ package org.jbox2d.dynamics.joints; import org.jbox2d.common.Rot; import org.jbox2d.common.Settings; import org.jbox2d.common.Transform; import org.jbox2d.common.Vec2; import org.jbox2d.dynamics.Body; import org.jbox2d.dynamics.SolverData; import org.jbox2d.pooling.IWorldPool; //Gear Joint: //C0 = (coordinate1 + ratio * coordinate2)_initial //C = (coordinate1 + ratio * coordinate2) - C0 = 0 //J = [J1 ratio * J2] //K = J * invM * JT //= J1 * invM1 * J1T + ratio * ratio * J2 * invM2 * J2T // //Revolute: //coordinate = rotation //Cdot = angularVelocity //J = [0 0 1] //K = J * invM * JT = invI // //Prismatic: //coordinate = dot(p - pg, ug) //Cdot = dot(v + cross(w, r), ug) //J = [ug cross(r, ug)] //K = J * invM * JT = invMass + invI * cross(r, ug)^2 /** * A gear joint is used to connect two joints together. Either joint can be a revolute or prismatic * joint. You specify a gear ratio to bind the motions together: coordinate1 + ratio * coordinate2 = * constant The ratio can be negative or positive. If one joint is a revolute joint and the other * joint is a prismatic joint, then the ratio will have units of length or units of 1/length. * * @warning The revolute and prismatic joints must be attached to fixed bodies (which must be body1 * on those joints). * @warning You have to manually destroy the gear joint if joint1 or joint2 is destroyed. * @author Daniel Murphy */ public class GearJoint extends Joint { private final Joint m_joint1; private final Joint m_joint2; private final JointType m_typeA; private final JointType m_typeB; // Body A is connected to body C // Body B is connected to body D private final Body m_bodyC; private final Body m_bodyD; // Solver shared private final Vec2 m_localAnchorA = new Vec2(); private final Vec2 m_localAnchorB = new Vec2(); private final Vec2 m_localAnchorC = new Vec2(); private final Vec2 m_localAnchorD = new Vec2(); private final Vec2 m_localAxisC = new Vec2(); private final Vec2 m_localAxisD = new Vec2(); private float m_referenceAngleA; private float m_referenceAngleB; private float m_constant; private float m_ratio; private float m_impulse; // Solver temp private int m_indexA, m_indexB, m_indexC, m_indexD; private final Vec2 m_lcA = new Vec2(), m_lcB = new Vec2(), m_lcC = new Vec2(), m_lcD = new Vec2(); private float m_mA, m_mB, m_mC, m_mD; private float m_iA, m_iB, m_iC, m_iD; private final Vec2 m_JvAC = new Vec2(), m_JvBD = new Vec2(); private float m_JwA, m_JwB, m_JwC, m_JwD; private float m_mass; protected GearJoint(IWorldPool argWorldPool, GearJointDef def) { super(argWorldPool, def); m_joint1 = def.joint1; m_joint2 = def.joint2; m_typeA = m_joint1.getType(); m_typeB = m_joint2.getType(); assert (m_typeA == JointType.REVOLUTE || m_typeA == JointType.PRISMATIC); assert (m_typeB == JointType.REVOLUTE || m_typeB == JointType.PRISMATIC); float coordinateA, coordinateB; // TODO_ERIN there might be some problem with the joint edges in Joint. m_bodyC = m_joint1.getBodyA(); m_bodyA = m_joint1.getBodyB(); // Get geometry of joint1 Transform xfA = m_bodyA.m_xf; float aA = m_bodyA.m_sweep.a; Transform xfC = m_bodyC.m_xf; float aC = m_bodyC.m_sweep.a; if (m_typeA == JointType.REVOLUTE) { RevoluteJoint revolute = (RevoluteJoint) def.joint1; m_localAnchorC.set(revolute.m_localAnchorA); m_localAnchorA.set(revolute.m_localAnchorB); m_referenceAngleA = revolute.m_referenceAngle; m_localAxisC.setZero(); coordinateA = aA - aC - m_referenceAngleA; } else { Vec2 pA = pool.popVec2(); Vec2 temp = pool.popVec2(); PrismaticJoint prismatic = (PrismaticJoint) def.joint1; m_localAnchorC.set(prismatic.m_localAnchorA); m_localAnchorA.set(prismatic.m_localAnchorB); m_referenceAngleA = prismatic.m_referenceAngle; m_localAxisC.set(prismatic.m_localXAxisA); Vec2 pC = m_localAnchorC; Rot.mulToOutUnsafe(xfA.q, m_localAnchorA, temp); temp.addLocal(xfA.p).subLocal(xfC.p); Rot.mulTransUnsafe(xfC.q, temp, pA); coordinateA = Vec2.dot(pA.subLocal(pC), m_localAxisC); pool.pushVec2(2); } m_bodyD = m_joint2.getBodyA(); m_bodyB = m_joint2.getBodyB(); // Get geometry of joint2 Transform xfB = m_bodyB.m_xf; float aB = m_bodyB.m_sweep.a; Transform xfD = m_bodyD.m_xf; float aD = m_bodyD.m_sweep.a; if (m_typeB == JointType.REVOLUTE) { RevoluteJoint revolute = (RevoluteJoint) def.joint2; m_localAnchorD.set(revolute.m_localAnchorA); m_localAnchorB.set(revolute.m_localAnchorB); m_referenceAngleB = revolute.m_referenceAngle; m_localAxisD.setZero(); coordinateB = aB - aD - m_referenceAngleB; } else { Vec2 pB = pool.popVec2(); Vec2 temp = pool.popVec2(); PrismaticJoint prismatic = (PrismaticJoint) def.joint2; m_localAnchorD.set(prismatic.m_localAnchorA); m_localAnchorB.set(prismatic.m_localAnchorB); m_referenceAngleB = prismatic.m_referenceAngle; m_localAxisD.set(prismatic.m_localXAxisA); Vec2 pD = m_localAnchorD; Rot.mulToOutUnsafe(xfB.q, m_localAnchorB, temp); temp.addLocal(xfB.p).subLocal(xfD.p); Rot.mulTransUnsafe(xfD.q, temp, pB); coordinateB = Vec2.dot(pB.subLocal(pD), m_localAxisD); pool.pushVec2(2); } m_ratio = def.ratio; m_constant = coordinateA + m_ratio * coordinateB; m_impulse = 0.0f; } @Override public void getAnchorA(Vec2 argOut) { m_bodyA.getWorldPointToOut(m_localAnchorA, argOut); } @Override public void getAnchorB(Vec2 argOut) { m_bodyB.getWorldPointToOut(m_localAnchorB, argOut); } @Override public void getReactionForce(float inv_dt, Vec2 argOut) { argOut.set(m_JvAC).mulLocal(m_impulse); argOut.mulLocal(inv_dt); } @Override public float getReactionTorque(float inv_dt) { float L = m_impulse * m_JwA; return inv_dt * L; } public void setRatio(float argRatio) { m_ratio = argRatio; } public float getRatio() { return m_ratio; } @Override public void initVelocityConstraints(SolverData data) { m_indexA = m_bodyA.m_islandIndex; m_indexB = m_bodyB.m_islandIndex; m_indexC = m_bodyC.m_islandIndex; m_indexD = m_bodyD.m_islandIndex; m_lcA.set(m_bodyA.m_sweep.localCenter); m_lcB.set(m_bodyB.m_sweep.localCenter); m_lcC.set(m_bodyC.m_sweep.localCenter); m_lcD.set(m_bodyD.m_sweep.localCenter); m_mA = m_bodyA.m_invMass; m_mB = m_bodyB.m_invMass; m_mC = m_bodyC.m_invMass; m_mD = m_bodyD.m_invMass; m_iA = m_bodyA.m_invI; m_iB = m_bodyB.m_invI; m_iC = m_bodyC.m_invI; m_iD = m_bodyD.m_invI; // Vec2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; Vec2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; // Vec2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; Vec2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; // Vec2 cC = data.positions[m_indexC].c; float aC = data.positions[m_indexC].a; Vec2 vC = data.velocities[m_indexC].v; float wC = data.velocities[m_indexC].w; // Vec2 cD = data.positions[m_indexD].c; float aD = data.positions[m_indexD].a; Vec2 vD = data.velocities[m_indexD].v; float wD = data.velocities[m_indexD].w; Rot qA = pool.popRot(), qB = pool.popRot(), qC = pool.popRot(), qD = pool.popRot(); qA.set(aA); qB.set(aB); qC.set(aC); qD.set(aD); m_mass = 0.0f; Vec2 temp = pool.popVec2(); if (m_typeA == JointType.REVOLUTE) { m_JvAC.setZero(); m_JwA = 1.0f; m_JwC = 1.0f; m_mass += m_iA + m_iC; } else { Vec2 rC = pool.popVec2(); Vec2 rA = pool.popVec2(); Rot.mulToOutUnsafe(qC, m_localAxisC, m_JvAC); Rot.mulToOutUnsafe(qC, temp.set(m_localAnchorC).subLocal(m_lcC), rC); Rot.mulToOutUnsafe(qA, temp.set(m_localAnchorA).subLocal(m_lcA), rA); m_JwC = Vec2.cross(rC, m_JvAC); m_JwA = Vec2.cross(rA, m_JvAC); m_mass += m_mC + m_mA + m_iC * m_JwC * m_JwC + m_iA * m_JwA * m_JwA; pool.pushVec2(2); } if (m_typeB == JointType.REVOLUTE) { m_JvBD.setZero(); m_JwB = m_ratio; m_JwD = m_ratio; m_mass += m_ratio * m_ratio * (m_iB + m_iD); } else { Vec2 u = pool.popVec2(); Vec2 rD = pool.popVec2(); Vec2 rB = pool.popVec2(); Rot.mulToOutUnsafe(qD, m_localAxisD, u); Rot.mulToOutUnsafe(qD, temp.set(m_localAnchorD).subLocal(m_lcD), rD); Rot.mulToOutUnsafe(qB, temp.set(m_localAnchorB).subLocal(m_lcB), rB); m_JvBD.set(u).mulLocal(m_ratio); m_JwD = m_ratio * Vec2.cross(rD, u); m_JwB = m_ratio * Vec2.cross(rB, u); m_mass += m_ratio * m_ratio * (m_mD + m_mB) + m_iD * m_JwD * m_JwD + m_iB * m_JwB * m_JwB; pool.pushVec2(3); } // Compute effective mass. m_mass = m_mass > 0.0f ? 1.0f / m_mass : 0.0f; if (data.step.warmStarting) { vA.x += (m_mA * m_impulse) * m_JvAC.x; vA.y += (m_mA * m_impulse) * m_JvAC.y; wA += m_iA * m_impulse * m_JwA; vB.x += (m_mB * m_impulse) * m_JvBD.x; vB.y += (m_mB * m_impulse) * m_JvBD.y; wB += m_iB * m_impulse * m_JwB; vC.x -= (m_mC * m_impulse) * m_JvAC.x; vC.y -= (m_mC * m_impulse) * m_JvAC.y; wC -= m_iC * m_impulse * m_JwC; vD.x -= (m_mD * m_impulse) * m_JvBD.x; vD.y -= (m_mD * m_impulse) * m_JvBD.y; wD -= m_iD * m_impulse * m_JwD; } else { m_impulse = 0.0f; } pool.pushVec2(1); pool.pushRot(4); // data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; // data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; // data.velocities[m_indexC].v = vC; data.velocities[m_indexC].w = wC; // data.velocities[m_indexD].v = vD; data.velocities[m_indexD].w = wD; } @Override public void solveVelocityConstraints(SolverData data) { Vec2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; Vec2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; Vec2 vC = data.velocities[m_indexC].v; float wC = data.velocities[m_indexC].w; Vec2 vD = data.velocities[m_indexD].v; float wD = data.velocities[m_indexD].w; Vec2 temp1 = pool.popVec2(); Vec2 temp2 = pool.popVec2(); float Cdot = Vec2.dot(m_JvAC, temp1.set(vA).subLocal(vC)) + Vec2.dot(m_JvBD, temp2.set(vB).subLocal(vD)); Cdot += (m_JwA * wA - m_JwC * wC) + (m_JwB * wB - m_JwD * wD); pool.pushVec2(2); float impulse = -m_mass * Cdot; m_impulse += impulse; vA.x += (m_mA * impulse) * m_JvAC.x; vA.y += (m_mA * impulse) * m_JvAC.y; wA += m_iA * impulse * m_JwA; vB.x += (m_mB * impulse) * m_JvBD.x; vB.y += (m_mB * impulse) * m_JvBD.y; wB += m_iB * impulse * m_JwB; vC.x -= (m_mC * impulse) * m_JvAC.x; vC.y -= (m_mC * impulse) * m_JvAC.y; wC -= m_iC * impulse * m_JwC; vD.x -= (m_mD * impulse) * m_JvBD.x; vD.y -= (m_mD * impulse) * m_JvBD.y; wD -= m_iD * impulse * m_JwD; // data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; // data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; // data.velocities[m_indexC].v = vC; data.velocities[m_indexC].w = wC; // data.velocities[m_indexD].v = vD; data.velocities[m_indexD].w = wD; } public Joint getJoint1() { return m_joint1; } public Joint getJoint2() { return m_joint2; } @Override public boolean solvePositionConstraints(SolverData data) { Vec2 cA = data.positions[m_indexA].c; float aA = data.positions[m_indexA].a; Vec2 cB = data.positions[m_indexB].c; float aB = data.positions[m_indexB].a; Vec2 cC = data.positions[m_indexC].c; float aC = data.positions[m_indexC].a; Vec2 cD = data.positions[m_indexD].c; float aD = data.positions[m_indexD].a; Rot qA = pool.popRot(), qB = pool.popRot(), qC = pool.popRot(), qD = pool.popRot(); qA.set(aA); qB.set(aB); qC.set(aC); qD.set(aD); float linearError = 0.0f; float coordinateA, coordinateB; Vec2 temp = pool.popVec2(); Vec2 JvAC = pool.popVec2(); Vec2 JvBD = pool.popVec2(); float JwA, JwB, JwC, JwD; float mass = 0.0f; if (m_typeA == JointType.REVOLUTE) { JvAC.setZero(); JwA = 1.0f; JwC = 1.0f; mass += m_iA + m_iC; coordinateA = aA - aC - m_referenceAngleA; } else { Vec2 rC = pool.popVec2(); Vec2 rA = pool.popVec2(); Vec2 pC = pool.popVec2(); Vec2 pA = pool.popVec2(); Rot.mulToOutUnsafe(qC, m_localAxisC, JvAC); Rot.mulToOutUnsafe(qC, temp.set(m_localAnchorC).subLocal(m_lcC), rC); Rot.mulToOutUnsafe(qA, temp.set(m_localAnchorA).subLocal(m_lcA), rA); JwC = Vec2.cross(rC, JvAC); JwA = Vec2.cross(rA, JvAC); mass += m_mC + m_mA + m_iC * JwC * JwC + m_iA * JwA * JwA; pC.set(m_localAnchorC).subLocal(m_lcC); Rot.mulTransUnsafe(qC, temp.set(rA).addLocal(cA).subLocal(cC), pA); coordinateA = Vec2.dot(pA.subLocal(pC), m_localAxisC); pool.pushVec2(4); } if (m_typeB == JointType.REVOLUTE) { JvBD.setZero(); JwB = m_ratio; JwD = m_ratio; mass += m_ratio * m_ratio * (m_iB + m_iD); coordinateB = aB - aD - m_referenceAngleB; } else { Vec2 u = pool.popVec2(); Vec2 rD = pool.popVec2(); Vec2 rB = pool.popVec2(); Vec2 pD = pool.popVec2(); Vec2 pB = pool.popVec2(); Rot.mulToOutUnsafe(qD, m_localAxisD, u); Rot.mulToOutUnsafe(qD, temp.set(m_localAnchorD).subLocal(m_lcD), rD); Rot.mulToOutUnsafe(qB, temp.set(m_localAnchorB).subLocal(m_lcB), rB); JvBD.set(u).mulLocal(m_ratio); JwD = Vec2.cross(rD, u); JwB = Vec2.cross(rB, u); mass += m_ratio * m_ratio * (m_mD + m_mB) + m_iD * JwD * JwD + m_iB * JwB * JwB; pD.set(m_localAnchorD).subLocal(m_lcD); Rot.mulTransUnsafe(qD, temp.set(rB).addLocal(cB).subLocal(cD), pB); coordinateB = Vec2.dot(pB.subLocal(pD), m_localAxisD); pool.pushVec2(5); } float C = (coordinateA + m_ratio * coordinateB) - m_constant; float impulse = 0.0f; if (mass > 0.0f) { impulse = -C / mass; } pool.pushVec2(3); pool.pushRot(4); cA.x += (m_mA * impulse) * JvAC.x; cA.y += (m_mA * impulse) * JvAC.y; aA += m_iA * impulse * JwA; cB.x += (m_mB * impulse) * JvBD.x; cB.y += (m_mB * impulse) * JvBD.y; aB += m_iB * impulse * JwB; cC.x -= (m_mC * impulse) * JvAC.x; cC.y -= (m_mC * impulse) * JvAC.y; aC -= m_iC * impulse * JwC; cD.x -= (m_mD * impulse) * JvBD.x; cD.y -= (m_mD * impulse) * JvBD.y; aD -= m_iD * impulse * JwD; // data.positions[m_indexA].c = cA; data.positions[m_indexA].a = aA; // data.positions[m_indexB].c = cB; data.positions[m_indexB].a = aB; // data.positions[m_indexC].c = cC; data.positions[m_indexC].a = aC; // data.positions[m_indexD].c = cD; data.positions[m_indexD].a = aD; // TODO_ERIN not implemented return linearError < Settings.linearSlop; } }