/*
* JBox2D - A Java Port of Erin Catto's Box2D
*
* JBox2D homepage: http://jbox2d.sourceforge.net/
* Box2D homepage: http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
package org.jbox2d.dynamics.joints;
import org.jbox2d.common.Mat22;
import org.jbox2d.common.Settings;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.TimeStep;
import org.jbox2d.dynamics.World;
//Updated to rev 56->97->137 of b2GearJoint.cpp/.h
/**
* 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).
*/
public class GearJoint extends Joint {
// Gear Joint:
// C0 = (coordinate1 + ratio * coordinate2)_initial
// C = C0 - (cordinate1 + ratio * coordinate2) = 0
// Cdot = -(Cdot1 + ratio * Cdot2)
// 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
public Body m_ground1;
public Body m_ground2;
// One of these is NULL.
public RevoluteJoint m_revolute1;
public PrismaticJoint m_prismatic1;
// One of these is NULL.
public RevoluteJoint m_revolute2;
public PrismaticJoint m_prismatic2;
public Vec2 m_groundAnchor1;
public Vec2 m_groundAnchor2;
public Vec2 m_localAnchor1;
public Vec2 m_localAnchor2;
public Jacobian m_J;
public float m_constant;
public float m_ratio;
/** Effective mass */
float m_mass;
/** Force for accumulation/warm starting. */
float m_force;
public GearJoint(GearJointDef def) {
super(def);
m_J = new Jacobian();
int type1 = def.joint1.getType();
int type2 = def.joint2.getType();
//assert(type1 == JointType.REVOLUTE_JOINT || type1 == JointType.PRISMATIC_JOINT);
//assert(type2 == JointType.REVOLUTE_JOINT || type2 == JointType.PRISMATIC_JOINT);
//assert(def.joint1.getBody1().isStatic());
//assert(def.joint2.getBody1().isStatic());
m_revolute1 = null;
m_prismatic1 = null;
m_revolute2 = null;
m_prismatic2 = null;
float coordinate1, coordinate2;
m_ground1 = def.joint1.getBody1();
m_body1 = def.joint1.getBody2();
if (type1 == JointType.REVOLUTE_JOINT) {
m_revolute1 = (RevoluteJoint) def.joint1;
m_groundAnchor1 = m_revolute1.m_localAnchor1;
m_localAnchor1 = m_revolute1.m_localAnchor2;
coordinate1 = m_revolute1.getJointAngle();
}
else {
m_prismatic1 = (PrismaticJoint) def.joint1;
m_groundAnchor1 = m_prismatic1.m_localAnchor1;
m_localAnchor1 = m_prismatic1.m_localAnchor2;
coordinate1 = m_prismatic1.getJointTranslation();
}
m_ground2 = def.joint2.getBody1();
m_body2 = def.joint2.getBody2();
if (type2 == JointType.REVOLUTE_JOINT) {
m_revolute2 = (RevoluteJoint) def.joint2;
m_groundAnchor2 = m_revolute2.m_localAnchor1;
m_localAnchor2 = m_revolute2.m_localAnchor2;
coordinate2 = m_revolute2.getJointAngle();
}
else {
m_prismatic2 = (PrismaticJoint) def.joint2;
m_groundAnchor2 = m_prismatic2.m_localAnchor1;
m_localAnchor2 = m_prismatic2.m_localAnchor2;
coordinate2 = m_prismatic2.getJointTranslation();
}
m_ratio = def.ratio;
m_constant = coordinate1 + m_ratio * coordinate2;
m_force = 0.0f;
}
public void initVelocityConstraints(TimeStep step) {
Body g1 = m_ground1;
Body g2 = m_ground2;
Body b1 = m_body1;
Body b2 = m_body2;
float K = 0.0f;
m_J.setZero();
if (m_revolute1 != null) {
m_J.angular1 = -1.0f;
K += b1.m_invI;
}
else {
Vec2 ug = Mat22.mul(g1.getXForm().R, m_prismatic1.m_localXAxis1);
Vec2 r = Mat22.mul(b1.getXForm().R, m_localAnchor1.sub(b1.getLocalCenter()));
float crug = Vec2.cross(r, ug);
m_J.linear1 = ug.negate();
m_J.angular1 = -crug;
K += b1.m_invMass + b1.m_invI * crug * crug;
}
if (m_revolute2 != null) {
m_J.angular2 = -m_ratio;
K += m_ratio * m_ratio * b2.m_invI;
}
else {
Vec2 ug = Mat22.mul(g2.getXForm().R, m_prismatic2.m_localXAxis1);
Vec2 r = Mat22.mul(b2.getXForm().R, m_localAnchor2.sub(b2.getLocalCenter()));
float crug = Vec2.cross(r, ug);
m_J.linear2 = ug.mulLocal(-m_ratio);
m_J.angular2 = -m_ratio * crug;
K += m_ratio * m_ratio * (b2.m_invMass + b2.m_invI * crug * crug);
}
// Compute effective mass.
//assert (K > 0.0f);
m_mass = 1.0f / K;
if (step.warmStarting) {
// Warm starting.
float P = step.dt * m_force;
b1.m_linearVelocity.x += b1.m_invMass * P * m_J.linear1.x;
b1.m_linearVelocity.y += b1.m_invMass * P * m_J.linear1.y;
b1.m_angularVelocity += b1.m_invI * P * m_J.angular1;
b2.m_linearVelocity.x += b2.m_invMass * P * m_J.linear2.x;
b2.m_linearVelocity.y += b2.m_invMass * P * m_J.linear2.y;
b2.m_angularVelocity += b2.m_invI * P * m_J.angular2;
} else {
m_force = 0.0f;
}
}
public void solveVelocityConstraints(TimeStep step) {
Body b1 = m_body1;
Body b2 = m_body2;
float Cdot = m_J.compute( b1.m_linearVelocity, b1.m_angularVelocity,
b2.m_linearVelocity, b2.m_angularVelocity);
float force = -step.inv_dt * m_mass * Cdot;
m_force += force;
float P = step.dt * force;
b1.m_linearVelocity.x += b1.m_invMass * P * m_J.linear1.x;
b1.m_linearVelocity.y += b1.m_invMass * P * m_J.linear1.y;
b1.m_angularVelocity += b1.m_invI * P * m_J.angular1;
b2.m_linearVelocity.x += b2.m_invMass * P * m_J.linear2.x;
b2.m_linearVelocity.y += b2.m_invMass * P * m_J.linear2.y;
b2.m_angularVelocity += b2.m_invI * P * m_J.angular2;
}
public boolean solvePositionConstraints() {
float linearError = 0.0f;
Body b1 = m_body1;
Body b2 = m_body2;
float coordinate1, coordinate2;
if (m_revolute1 != null) {
coordinate1 = m_revolute1.getJointAngle();
}
else {
coordinate1 = m_prismatic1.getJointTranslation();
}
if (m_revolute2 != null) {
coordinate2 = m_revolute2.getJointAngle();
}
else {
coordinate2 = m_prismatic2.getJointTranslation();
}
float C = m_constant - (coordinate1 + m_ratio * coordinate2);
float impulse = -m_mass * C;
b1.m_sweep.c.x += b1.m_invMass * impulse * m_J.linear1.x;
b1.m_sweep.c.y += b1.m_invMass * impulse * m_J.linear1.y;
b1.m_sweep.a += b1.m_invI * impulse * m_J.angular1;
b2.m_sweep.c.x += b2.m_invMass * impulse * m_J.linear2.x;
b2.m_sweep.c.y += b2.m_invMass * impulse * m_J.linear2.y;
b2.m_sweep.a += b2.m_invI * impulse * m_J.angular2;
b1.synchronizeTransform();
b2.synchronizeTransform();
return linearError < Settings.linearSlop;
}
public Vec2 getAnchor1() {
return m_body1.getWorldPoint(m_localAnchor1);
}
public Vec2 getAnchor2() {
return m_body2.getWorldPoint(m_localAnchor2);
}
public Vec2 getReactionForce() {
// TODO_ERIN not tested
return new Vec2(m_force * m_J.linear2.x, m_force * m_J.linear2.y);
}
public float getReactionTorque() {
// TODO_ERIN not tested
Vec2 r = Mat22.mul(m_body2.getXForm().R, m_localAnchor2.sub(m_body2.getLocalCenter()));
Vec2 F = new Vec2(m_force * m_J.linear2.x, m_force * m_J.linear2.y);
float T = m_force * m_J.angular2 - Vec2.cross(r, F);
return T;
}
public float getRatio() {
return m_ratio;
}
}