org.jbox2d.dynamics.joints.PulleyJoint Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of playn-jbox2d Show documentation
Show all versions of playn-jbox2d Show documentation
A GWT-compatible port of JBox2D, for use with PlayN games.
/*******************************************************************************
* Copyright (c) 2011, 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.
* * Neither the name of the nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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 DANIEL MURPHY 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 12:12:02 PM Jan 23, 2011
*/
package org.jbox2d.dynamics.joints;
import org.jbox2d.common.Mat22;
import org.jbox2d.common.MathUtils;
import org.jbox2d.common.Settings;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.TimeStep;
import org.jbox2d.pooling.IWorldPool;
/**
* @author Daniel Murphy
*/
public class PulleyJoint extends Joint {
public static final float MIN_PULLEY_LENGTH = 2.0f;
private final Vec2 m_groundAnchor1 = new Vec2();
private final Vec2 m_groundAnchor2 = new Vec2();
private final Vec2 m_localAnchor1 = new Vec2();
private final Vec2 m_localAnchor2 = new Vec2();
private final Vec2 m_u1 = new Vec2();
private final Vec2 m_u2 = new Vec2();
private float m_constant;
private float m_ratio;
private float m_maxLength1;
private float m_maxLength2;
// Effective masses
private float m_pulleyMass;
private float m_limitMass1;
private float m_limitMass2;
// Impulses for accumulation/warm starting.
private float m_impulse;
private float m_limitImpulse1;
private float m_limitImpulse2;
private LimitState m_state;
private LimitState m_limitState1;
private LimitState m_limitState2;
/**
* @param argWorldPool
* @param def
*/
public PulleyJoint(IWorldPool argWorldPool, PulleyJointDef def) {
super(argWorldPool, def);
m_groundAnchor1.set(def.groundAnchorA);
m_groundAnchor2.set(def.groundAnchorB);
m_localAnchor1.set(def.localAnchorA);
m_localAnchor2.set(def.localAnchorB);
assert (def.ratio != 0.0f);
m_ratio = def.ratio;
m_constant = def.lengthA + m_ratio * def.lengthB;
m_maxLength1 = MathUtils.min(def.maxLengthA, m_constant - m_ratio * MIN_PULLEY_LENGTH);
m_maxLength2 = MathUtils.min(def.maxLengthB, (m_constant - MIN_PULLEY_LENGTH) / m_ratio);
m_impulse = 0.0f;
m_limitImpulse1 = 0.0f;
m_limitImpulse2 = 0.0f;
}
/**
* @see org.jbox2d.dynamics.joints.Joint#getAnchorA(org.jbox2d.common.Vec2)
*/
@Override
public void getAnchorA(Vec2 argOut) {
m_bodyA.getWorldPointToOut(m_localAnchor1, argOut);
}
/**
* @see org.jbox2d.dynamics.joints.Joint#getAnchorB(org.jbox2d.common.Vec2)
*/
@Override
public void getAnchorB(Vec2 argOut) {
m_bodyB.getWorldPointToOut(m_localAnchor2, argOut);
}
/**
* @see org.jbox2d.dynamics.joints.Joint#getReactionForce(float,
* org.jbox2d.common.Vec2)
*/
@Override
public void getReactionForce(float inv_dt, Vec2 argOut) {
argOut.set(m_u2).mulLocal(m_impulse).mulLocal(inv_dt);
}
/**
* @see org.jbox2d.dynamics.joints.Joint#getReactionTorque(float)
*/
@Override
public float getReactionTorque(float inv_dt) {
return 0f;
}
public Vec2 getGroundAnchorA() {
return m_groundAnchor1;
}
public Vec2 getGroundAnchorB() {
return m_groundAnchor2;
}
public float getLength1() {
final Vec2 p = pool.popVec2();
m_bodyA.getWorldPointToOut(m_localAnchor1, p);
p.subLocal(m_groundAnchor1);
float len = p.length();
pool.pushVec2(1);
return len;
}
public float getLength2() {
final Vec2 p = pool.popVec2();
m_bodyB.getWorldPointToOut(m_localAnchor2, p);
p.subLocal(m_groundAnchor2);
float len = p.length();
pool.pushVec2(1);
return len;
}
public float getRatio() {
return m_ratio;
}
/**
* @see org.jbox2d.dynamics.joints.Joint#initVelocityConstraints(org.jbox2d.dynamics.TimeStep)
*/
@Override
public void initVelocityConstraints(TimeStep step) {
Body b1 = m_bodyA;
Body b2 = m_bodyB;
final Vec2 r1 = pool.popVec2();
final Vec2 r2 = pool.popVec2();
final Vec2 p1 = pool.popVec2();
final Vec2 p2 = pool.popVec2();
final Vec2 s1 = pool.popVec2();
final Vec2 s2 = pool.popVec2();
r1.set(m_localAnchor1).subLocal(b1.getLocalCenter());
r2.set(m_localAnchor2).subLocal(b2.getLocalCenter());
Mat22.mulToOut(b1.getTransform().R, r1, r1);
Mat22.mulToOut(b2.getTransform().R, r2, r2);
p1.set(b1.m_sweep.c).addLocal(r1);
p2.set(b2.m_sweep.c).addLocal(r2);
s1.set(m_groundAnchor1);
s2.set(m_groundAnchor2);
// Get the pulley axes.
m_u1.set(p1).subLocal(s1);
m_u2.set(p2).subLocal(s2);
float length1 = m_u1.length();
float length2 = m_u2.length();
if (length1 > Settings.linearSlop) {
m_u1.mulLocal(1.0f / length1);
}
else {
m_u1.setZero();
}
if (length2 > Settings.linearSlop) {
m_u2.mulLocal(1.0f / length2);
}
else {
m_u2.setZero();
}
float C = m_constant - length1 - m_ratio * length2;
if (C > 0.0f) {
m_state = LimitState.INACTIVE;
m_impulse = 0.0f;
}
else {
m_state = LimitState.AT_UPPER;
}
if (length1 < m_maxLength1) {
m_limitState1 = LimitState.INACTIVE;
m_limitImpulse1 = 0.0f;
}
else {
m_limitState1 = LimitState.AT_UPPER;
}
if (length2 < m_maxLength2) {
m_limitState2 = LimitState.INACTIVE;
m_limitImpulse2 = 0.0f;
}
else {
m_limitState2 = LimitState.AT_UPPER;
}
// Compute effective mass.
float cr1u1 = Vec2.cross(r1, m_u1);
float cr2u2 = Vec2.cross(r2, m_u2);
m_limitMass1 = b1.m_invMass + b1.m_invI * cr1u1 * cr1u1;
m_limitMass2 = b2.m_invMass + b2.m_invI * cr2u2 * cr2u2;
m_pulleyMass = m_limitMass1 + m_ratio * m_ratio * m_limitMass2;
assert (m_limitMass1 > Settings.EPSILON);
assert (m_limitMass2 > Settings.EPSILON);
assert (m_pulleyMass > Settings.EPSILON);
m_limitMass1 = 1.0f / m_limitMass1;
m_limitMass2 = 1.0f / m_limitMass2;
m_pulleyMass = 1.0f / m_pulleyMass;
if (step.warmStarting) {
// Scale impulses to support variable time steps.
m_impulse *= step.dtRatio;
m_limitImpulse1 *= step.dtRatio;
m_limitImpulse2 *= step.dtRatio;
final Vec2 P1 = pool.popVec2();
final Vec2 P2 = pool.popVec2();
final Vec2 temp = pool.popVec2();
// Warm starting.
P1.set(m_u1).mulLocal(-(m_impulse + m_limitImpulse1));
P2.set(m_u2).mulLocal(-m_ratio * m_impulse - m_limitImpulse2);
temp.set(P1).mulLocal(b1.m_invMass);
b1.m_linearVelocity.addLocal(temp);
b1.m_angularVelocity += b1.m_invI * Vec2.cross(r1, P1);
temp.set(P2).mulLocal(b2.m_invMass);
b2.m_linearVelocity.addLocal(temp);
b2.m_angularVelocity += b2.m_invI * Vec2.cross(r2, P2);
pool.pushVec2(3);
}
else {
m_impulse = 0.0f;
m_limitImpulse1 = 0.0f;
m_limitImpulse2 = 0.0f;
}
pool.pushVec2(6);
}
/**
* @see org.jbox2d.dynamics.joints.Joint#solveVelocityConstraints(org.jbox2d.dynamics.TimeStep)
*/
@Override
public void solveVelocityConstraints(TimeStep step) {
Body b1 = m_bodyA;
Body b2 = m_bodyB;
final Vec2 r1 = pool.popVec2();
final Vec2 r2 = pool.popVec2();
r1.set(m_localAnchor1).subLocal(b1.getLocalCenter());
r2.set(m_localAnchor2).subLocal(b2.getLocalCenter());
Mat22.mulToOut(b1.getTransform().R, r1, r1);
Mat22.mulToOut(b2.getTransform().R, r2, r2);
if (m_state == LimitState.AT_UPPER) {
final Vec2 v1 = pool.popVec2();
final Vec2 v2 = pool.popVec2();
Vec2.crossToOut(b1.m_angularVelocity, r1, v1);
Vec2.crossToOut(b2.m_angularVelocity, r2, v2);
v1.addLocal(b1.m_linearVelocity);
v2.addLocal(b2.m_linearVelocity);
float Cdot = -Vec2.dot(m_u1, v1) - m_ratio * Vec2.dot(m_u2, v2);
float impulse = m_pulleyMass * (-Cdot);
float oldImpulse = m_impulse;
m_impulse = MathUtils.max(0.0f, m_impulse + impulse);
impulse = m_impulse - oldImpulse;
final Vec2 P1 = pool.popVec2();
final Vec2 P2 = pool.popVec2();
final Vec2 temp = pool.popVec2();
P1.set(m_u1).mulLocal(-impulse);
P2.set(m_u2).mulLocal(-m_ratio * impulse);
temp.set(P1).mulLocal(b1.m_invMass);
b1.m_linearVelocity.addLocal(temp);
b1.m_angularVelocity += b1.m_invI * Vec2.cross(r1, P1);
temp.set(P2).mulLocal(b2.m_invMass);
b2.m_linearVelocity.addLocal(temp);
b2.m_angularVelocity += b2.m_invI * Vec2.cross(r2, P2);
pool.pushVec2(5);
}
if (m_limitState1 == LimitState.AT_UPPER) {
final Vec2 v1 = pool.popVec2();
Vec2.crossToOut(b1.m_angularVelocity, r1, v1);
v1.addLocal(b1.m_linearVelocity);
float Cdot = -Vec2.dot(m_u1, v1);
float impulse = -m_limitMass1 * Cdot;
float oldImpulse = m_limitImpulse1;
m_limitImpulse1 = MathUtils.max(0.0f, m_limitImpulse1 + impulse);
impulse = m_limitImpulse1 - oldImpulse;
final Vec2 P1 = pool.popVec2();
final Vec2 temp = pool.popVec2();
P1.set(m_u1).mulLocal(-impulse);
temp.set(P1).mulLocal(b1.m_invMass);
b1.m_linearVelocity.addLocal(temp);
b1.m_angularVelocity += b1.m_invI * Vec2.cross(r1, P1);
pool.pushVec2(3);
}
if (m_limitState2 == LimitState.AT_UPPER) {
final Vec2 v2 = pool.popVec2();
Vec2.crossToOut(b2.m_angularVelocity, r2, v2);
v2.addLocal(b2.m_linearVelocity);
float Cdot = -Vec2.dot(m_u2, v2);
float impulse = -m_limitMass2 * Cdot;
float oldImpulse = m_limitImpulse2;
m_limitImpulse2 = MathUtils.max(0.0f, m_limitImpulse2 + impulse);
impulse = m_limitImpulse2 - oldImpulse;
final Vec2 P2 = pool.popVec2();
final Vec2 temp = pool.popVec2();
P2.set(m_u2).mulLocal(-impulse);
temp.set(P2).mulLocal(b2.m_invMass);
b2.m_linearVelocity.addLocal(temp);
b2.m_angularVelocity += b2.m_invI * Vec2.cross(r2, P2);
pool.pushVec2(3);
}
pool.pushVec2(2);
}
/**
* @see org.jbox2d.dynamics.joints.Joint#solvePositionConstraints(float)
*/
@Override
public boolean solvePositionConstraints(float baumgarte) {
Body b1 = m_bodyA;
Body b2 = m_bodyB;
final Vec2 s1 = pool.popVec2();
final Vec2 s2 = pool.popVec2();
s1.set(m_groundAnchor1);
s2.set(m_groundAnchor2);
float linearError = 0.0f;
if (m_state == LimitState.AT_UPPER) {
final Vec2 r1 = pool.popVec2();
final Vec2 r2 = pool.popVec2();
final Vec2 p1 = pool.popVec2();
final Vec2 p2 = pool.popVec2();
r1.set(m_localAnchor1).subLocal(b1.getLocalCenter());
r2.set(m_localAnchor2).subLocal(b2.getLocalCenter());
Mat22.mulToOut(b1.getTransform().R, r1, r1);
Mat22.mulToOut(b2.getTransform().R, r2, r2);
p1.set(b1.m_sweep.c).addLocal(r1);
p2.set(b2.m_sweep.c).addLocal(r2);
// Get the pulley axes.
m_u1.set(p1).subLocal(s1);
m_u2.set(p2).subLocal(s2);
float length1 = m_u1.length();
float length2 = m_u2.length();
if (length1 > Settings.linearSlop) {
m_u1.mulLocal(1.0f / length1);
}
else {
m_u1.setZero();
}
if (length2 > Settings.linearSlop) {
m_u2.mulLocal(1.0f / length2);
}
else {
m_u2.setZero();
}
float C = m_constant - length1 - m_ratio * length2;
linearError = MathUtils.max(linearError, -C);
C = MathUtils.clamp(C + Settings.linearSlop, -Settings.maxLinearCorrection, 0.0f);
float impulse = -m_pulleyMass * C;
final Vec2 P1 = pool.popVec2();
final Vec2 P2 = pool.popVec2();
final Vec2 temp = pool.popVec2();
P1.set(m_u1).mulLocal(-impulse);
P2.set(m_u2).mulLocal(-m_ratio * impulse);
temp.set(P1).mulLocal(b1.m_invMass);
b1.m_sweep.c.addLocal(temp);
b1.m_sweep.a += b1.m_invI * Vec2.cross(r1, P1);
temp.set(P2).mulLocal(b2.m_invMass);
b2.m_sweep.c.addLocal(temp);
b2.m_sweep.a += b2.m_invI * Vec2.cross(r2, P2);
b1.synchronizeTransform();
b2.synchronizeTransform();
pool.pushVec2(7);
}
if (m_limitState1 == LimitState.AT_UPPER) {
final Vec2 r1 = pool.popVec2();
final Vec2 p1 = pool.popVec2();
r1.set(m_localAnchor1).subLocal(b1.getLocalCenter());
Mat22.mulToOut(b1.getTransform().R, r1, r1);
p1.set(b1.m_sweep.c).addLocal(r1);
m_u1.set(p1).subLocal(s1);
float length1 = m_u1.length();
if (length1 > Settings.linearSlop) {
m_u1.mulLocal(1.0f / length1);
}
else {
m_u1.setZero();
}
float C = m_maxLength1 - length1;
linearError = MathUtils.max(linearError, -C);
C = MathUtils.clamp(C + Settings.linearSlop, -Settings.maxLinearCorrection, 0.0f);
float impulse = -m_limitMass1 * C;
final Vec2 P1 = pool.popVec2();
final Vec2 temp = pool.popVec2();
P1.set(m_u1).mulLocal(-impulse);
temp.set(P1).mulLocal(b1.m_invMass);
b1.m_sweep.c.addLocal(temp);
b1.m_sweep.a += b1.m_invI * Vec2.cross(r1, P1);
b1.synchronizeTransform();
pool.pushVec2(4);
}
if (m_limitState2 == LimitState.AT_UPPER) {
final Vec2 r2 = pool.popVec2();
final Vec2 p2 = pool.popVec2();
r2.set(m_localAnchor2).subLocal(b2.getLocalCenter());
Mat22.mulToOut(b2.getTransform().R, r2, r2);
p2.set(b2.m_sweep.c).addLocal(r2);
// Get the pulley axes.
m_u2.set(p2).subLocal(s2);
float length2 = m_u2.length();
if (length2 > Settings.linearSlop) {
m_u2.mulLocal(1.0f / length2);
}
else {
m_u2.setZero();
}
float C = m_maxLength2 - length2;
linearError = MathUtils.max(linearError, -C);
C = MathUtils.clamp(C + Settings.linearSlop, -Settings.maxLinearCorrection, 0.0f);
float impulse = -m_limitMass2 * C;
final Vec2 P2 = pool.popVec2();
final Vec2 temp = pool.popVec2();
P2.set(m_u2).mulLocal(-impulse);
temp.set(P2).mulLocal(b2.m_invMass);
b2.m_sweep.c.addLocal(temp);
b2.m_sweep.a += b2.m_invI * Vec2.cross(r2, P2);
b2.synchronizeTransform();
pool.pushVec2(4);
}
pool.pushVec2(2);
return linearError < Settings.linearSlop;
}
}