All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jbox2d.dynamics.joints.LineJoint Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
/*******************************************************************************
 * 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 9:06:02 PM Jan 21, 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.Transform;
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 LineJoint extends Joint {
	
	private final Vec2 m_localAnchor1 = new Vec2();
	private final Vec2 m_localAnchor2 = new Vec2();
	private final Vec2 m_localXAxis1 = new Vec2();
	private final Vec2 m_localYAxis1 = new Vec2();
	
	private final Vec2 m_axis = new Vec2();
	private final Vec2 m_perp = new Vec2();
	private float m_s1, m_s2;
	private float m_a1, m_a2;
	
	private final Mat22 m_K = new Mat22();
	private final Vec2 m_impulse = new Vec2();
	
	private float m_motorMass; // effective mass for motor/limit translational
								// constraint.
	private float m_motorImpulse;
	
	private float m_lowerTranslation;
	private float m_upperTranslation;
	private float m_maxMotorForce;
	private float m_motorSpeed;
	
	private boolean m_enableLimit;
	private boolean m_enableMotor;
	private LimitState m_limitState;
	
	public LineJoint(IWorldPool argPool, LineJointDef def) {
		super(argPool, def);
		m_localAnchor1.set(def.localAnchorA);
		m_localAnchor2.set(def.localAnchorB);
		m_localXAxis1.set(def.localAxisA);
		Vec2.crossToOut(1.0f, m_localXAxis1, m_localYAxis1);
		
		m_impulse.setZero();
		m_motorMass = 0.0f;
		m_motorImpulse = 0.0f;
		
		m_lowerTranslation = def.lowerTranslation;
		m_upperTranslation = def.upperTranslation;
		m_maxMotorForce = def.maxMotorForce;
		m_motorSpeed = def.motorSpeed;
		m_enableLimit = def.enableLimit;
		m_enableMotor = def.enableMotor;
		m_limitState = LimitState.INACTIVE;
		
		m_axis.setZero();
		m_perp.setZero();
	}
	
	/**
	 * @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) {
		final Vec2 temp = pool.popVec2();
		temp.set(m_perp).mulLocal(m_impulse.x);
		argOut.set(m_axis).mulLocal(m_motorImpulse + m_impulse.y).addLocal(temp).mulLocal(inv_dt);
		pool.pushVec2(1);
	}
	
	/**
	 * @see org.jbox2d.dynamics.joints.Joint#getReactionTorque(float)
	 */
	@Override
	public float getReactionTorque(float inv_dt) {
		return 0.0f;
	}
	
	public float getJointTranslation() {
		Body b1 = m_bodyA;
		Body b2 = m_bodyB;
		
		Vec2 p1 = pool.popVec2();
		Vec2 p2 = pool.popVec2();
		Vec2 axis = pool.popVec2();
		b1.getWorldPointToOut(m_localAnchor1, p1);
		b2.getWorldPointToOut(m_localAnchor1, p2);
		p2.subLocal(p1);
		b1.getWorldVectorToOut(m_localXAxis1, axis);
		
		float translation = Vec2.dot(p2, axis);
		pool.pushVec2(3);
		return translation;
	}
	
	public float getJointSpeed() {
		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();
		
		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);
		p2.subLocal(p1);
		
		final Vec2 axis = pool.popVec2();
		b1.getWorldPointToOut(m_localXAxis1, axis);
		
		final Vec2 v1 = b1.m_linearVelocity;
		final Vec2 v2 = b2.m_linearVelocity;
		float w1 = b1.m_angularVelocity;
		float w2 = b2.m_angularVelocity;
		
		final Vec2 temp1 = pool.popVec2();
		final Vec2 temp2 = pool.popVec2();
		
		Vec2.crossToOut(w1, r1, temp1);
		Vec2.crossToOut(w2, r2, temp2);
		temp2.addLocal(v2).subLocal(v1).subLocal(temp1);
		float s2 = Vec2.dot(axis, temp2);
		
		Vec2.crossToOut(w1, axis, temp1);
		float speed = Vec2.dot(p2, temp1) + s2;
		
		pool.pushVec2(7);
		return speed;
	}
	
	public boolean isLimitEnabled() {
		return m_enableLimit;
	}
	
	public void EnableLimit(boolean flag) {
		m_bodyA.setAwake(true);
		m_bodyB.setAwake(true);
		m_enableLimit = flag;
	}
	
	public float getLowerLimit() {
		return m_lowerTranslation;
	}
	
	public float getUpperLimit() {
		return m_upperTranslation;
	}
	
	public void setLimits(float lower, float upper) {
		assert (lower <= upper);
		m_bodyA.setAwake(true);
		m_bodyB.setAwake(true);
		m_lowerTranslation = lower;
		m_upperTranslation = upper;
	}
	
	public boolean isMotorEnabled() {
		return m_enableMotor;
	}
	
	public void EnableMotor(boolean flag) {
		m_bodyA.setAwake(true);
		m_bodyB.setAwake(true);
		m_enableMotor = flag;
	}
	
	public void setMotorSpeed(float speed) {
		m_bodyA.setAwake(true);
		m_bodyB.setAwake(true);
		m_motorSpeed = speed;
	}
	
	public void setMaxMotorForce(float force) {
		m_bodyA.setAwake(true);
		m_bodyB.setAwake(true);
		m_maxMotorForce = force;
	}
	
	public float getMotorForce() {
		return m_motorImpulse;
	}
	
	/**
	 * @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;
		
		m_localCenterA.set(b1.getLocalCenter());
		m_localCenterB.set(b2.getLocalCenter());
		
		Transform xf1 = b1.getTransform();
		Transform xf2 = b2.getTransform();
		
		// Compute the effective masses.
		final Vec2 r1 = pool.popVec2();
		final Vec2 r2 = pool.popVec2();
		final Vec2 temp = pool.popVec2();
		
		r1.set(m_localAnchor1).subLocal(m_localCenterA);
		r2.set(m_localAnchor2).subLocal(m_localCenterB);
		Mat22.mulToOut(xf1.R, r1, r1);
		Mat22.mulToOut(xf2.R, r2, r2);
		
		final Vec2 d = pool.popVec2();
		d.set(b2.m_sweep.c).addLocal(r2).subLocal(b1.m_sweep.c).subLocal(r1);
		
		m_invMassA = b1.m_invMass;
		m_invIA = b1.m_invI;
		m_invMassB = b2.m_invMass;
		m_invIB = b2.m_invI;
		
		// Compute motor Jacobian and effective mass.
		{
			Mat22.mulToOut(xf1.R, m_localXAxis1, m_axis);
			temp.set(d).addLocal(r1);
			m_a1 = Vec2.cross(temp, m_axis);
			m_a2 = Vec2.cross(r2, m_axis);
			
			m_motorMass = m_invMassA + m_invMassB + m_invIA * m_a1 * m_a1 + m_invIB * m_a2 * m_a2;
			if (m_motorMass > Settings.EPSILON) {
				m_motorMass = 1.0f / m_motorMass;
			}
			else {
				m_motorMass = 0.0f;
			}
		}
		
		// Prismatic constraint.
		{
			Mat22.mulToOut(xf1.R, m_localYAxis1, m_perp);
			
			temp.set(d).addLocal(r1);
			m_s1 = Vec2.cross(temp, m_perp);
			m_s2 = Vec2.cross(r2, m_perp);
			
			float m1 = m_invMassA, m2 = m_invMassB;
			float i1 = m_invIA, i2 = m_invIB;
			
			float k11 = m1 + m2 + i1 * m_s1 * m_s1 + i2 * m_s2 * m_s2;
			float k12 = i1 * m_s1 * m_a1 + i2 * m_s2 * m_a2;
			float k22 = m1 + m2 + i1 * m_a1 * m_a1 + i2 * m_a2 * m_a2;
			
      m_K.m11 = k11;
      m_K.m12 = k12;
      m_K.m21 = k12;
      m_K.m22 = k22;
		}
		
		// Compute motor and limit terms.
		if (m_enableLimit) {
			float jointTranslation = Vec2.dot(m_axis, d);
			if (MathUtils.abs(m_upperTranslation - m_lowerTranslation) < 2.0f * Settings.linearSlop) {
				m_limitState = LimitState.EQUAL;
			}
			else if (jointTranslation <= m_lowerTranslation) {
				if (m_limitState != LimitState.AT_LOWER) {
					m_limitState = LimitState.AT_LOWER;
					m_impulse.y = 0.0f;
				}
			}
			else if (jointTranslation >= m_upperTranslation) {
				if (m_limitState != LimitState.AT_UPPER) {
					m_limitState = LimitState.AT_UPPER;
					m_impulse.y = 0.0f;
				}
			}
			else {
				m_limitState = LimitState.INACTIVE;
				m_impulse.y = 0.0f;
			}
		}
		else {
			m_limitState = LimitState.INACTIVE;
		}
		
		if (m_enableMotor == false) {
			m_motorImpulse = 0.0f;
		}
		
		if (step.warmStarting) {
			// Account for variable time step.
			m_impulse.mulLocal(step.dtRatio);
			m_motorImpulse *= step.dtRatio;
			
			final Vec2 P = pool.popVec2();
			temp.set(m_axis).mulLocal(m_motorImpulse + m_impulse.y);
			P.set(m_perp).mulLocal(m_impulse.x).addLocal(temp);
			
			float L1 = m_impulse.x * m_s1 + (m_motorImpulse + m_impulse.y) * m_a1;
			float L2 = m_impulse.x * m_s2 + (m_motorImpulse + m_impulse.y) * m_a2;
			
			temp.set(P).mulLocal(m_invMassA);
			b1.m_linearVelocity.subLocal(temp);
			b1.m_angularVelocity -= m_invIA * L1;
			
			temp.set(P).mulLocal(m_invMassB);
			b2.m_linearVelocity.addLocal(temp);
			b2.m_angularVelocity += m_invIB * L2;
			pool.pushVec2(1);
		}
		else {
			m_impulse.setZero();
			m_motorImpulse = 0.0f;
		}
		pool.pushVec2(4);
	}
	
	/**
	 * @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 v1 = b1.m_linearVelocity;
		float w1 = b1.m_angularVelocity;
		final Vec2 v2 = b2.m_linearVelocity;
		float w2 = b2.m_angularVelocity;
		
		final Vec2 temp = pool.popVec2();
		
		// Solve linear motor constraint.
		if (m_enableMotor && m_limitState != LimitState.EQUAL) {
			temp.set(v2).subLocal(v1);
			float Cdot = Vec2.dot(m_axis, temp) + m_a2 * w2 - m_a1 * w1;
			float impulse = m_motorMass * (m_motorSpeed - Cdot);
			float oldImpulse = m_motorImpulse;
			float maxImpulse = step.dt * m_maxMotorForce;
			m_motorImpulse = MathUtils.clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse);
			impulse = m_motorImpulse - oldImpulse;
			
			final Vec2 P = pool.popVec2();
			P.set(m_axis).mulLocal(impulse);
			float L1 = impulse * m_a1;
			float L2 = impulse * m_a2;
			
			temp.set(P).mulLocal(m_invMassA);
			v1.subLocal(temp);
			w1 -= m_invIA * L1;
			
			temp.set(P).mulLocal(m_invMassB);
			v2.addLocal(temp);
			w2 += m_invIB * L2;
			pool.pushVec2(1);
		}
		
		temp.set(v2).subLocal(v1);
		float Cdot1 = Vec2.dot(m_perp, temp) + m_s2 * w2 - m_s1 * w1;
		
		if (m_enableLimit && m_limitState != LimitState.INACTIVE) {
			// Solve prismatic and limit constraint in block form.
			temp.set(v2).subLocal(v1);
			float Cdot2 = Vec2.dot(m_axis, temp) + m_a2 * w2 - m_a1 * w1;
			
			final Vec2 Cdot = pool.popVec2();
			Cdot.set(Cdot1, Cdot2);
			
			final Vec2 f1 = pool.popVec2();
			f1.set(m_impulse);
			final Vec2 df = pool.popVec2();
			m_K.solveToOut(Cdot.negateLocal(), df); // just leave negated
			m_impulse.addLocal(df);
			
			if (m_limitState == LimitState.AT_LOWER) {
				m_impulse.y = MathUtils.max(m_impulse.y, 0.0f);
			}
			else if (m_limitState == LimitState.AT_UPPER) {
				m_impulse.y = MathUtils.min(m_impulse.y, 0.0f);
			}
			
			// f2(1) = invK(1,1) * (-Cdot(1) - K(1,2) * (f2(2) - f1(2))) + f1(1)
			float b = -Cdot1 - (m_impulse.y - f1.y) * m_K.m21;
			float f2r;
			if (m_K.m11 != 0.0f) {
				f2r = b / m_K.m11 + f1.x;
			}
			else {
				f2r = f1.x;
			}
			
			m_impulse.x = f2r;
			
			df.set(m_impulse).subLocal(f1);
			
			final Vec2 P = pool.popVec2();
			temp.set(m_axis).mulLocal(df.y);
			P.set(m_perp).mulLocal(df.x).addLocal(temp);
			
			float L1 = df.x * m_s1 + df.y * m_a1;
			float L2 = df.x * m_s2 + df.y * m_a2;
			
			temp.set(P).mulLocal(m_invMassA);
			v1.subLocal(temp);
			w1 -= m_invIA * L1;
			
			temp.set(P).mulLocal(m_invMassB);
			v2.addLocal(temp);
			w2 += m_invIB * L2;
			pool.pushVec2(4);
		}
		else {
			// Limit is inactive, just solve the prismatic constraint in block
			// form.
			float df;
			if (m_K.m11 != 0.0f) {
				df = -Cdot1 / m_K.m11;
			}
			else {
				df = 0.0f;
			}
			m_impulse.x += df;
			
			final Vec2 P = pool.popVec2();
			P.set(m_perp).mulLocal(df);
			
			float L1 = df * m_s1;
			float L2 = df * m_s2;
			
			temp.set(P).mulLocal(m_invMassA);
			v1.subLocal(temp);
			w1 -= m_invIA * L1;
			
			temp.set(P).mulLocal(m_invMassB);
			v2.addLocal(temp);
			w2 += m_invIB * L2;
			pool.pushVec2(1);
		}
		
		pool.pushVec2(1);
		
		b1.m_angularVelocity = w1;
		b2.m_angularVelocity = w2;
	}
	
	/**
	 * @see org.jbox2d.dynamics.joints.Joint#solvePositionConstraints(float)
	 */
	@Override
	public boolean solvePositionConstraints(float baumgarte) {
		Body b1 = m_bodyA;
		Body b2 = m_bodyB;
		
		final Vec2 c1 = b1.m_sweep.c;
		float a1 = b1.m_sweep.a;
		
		final Vec2 c2 = b2.m_sweep.c;
		float a2 = b2.m_sweep.a;
		
		// Solve linear limit constraint.
		float linearError = 0.0f, angularError = 0.0f;
		boolean active = false;
		float C2 = 0.0f;
		
		Mat22 R1 = pool.popMat22();
		Mat22 R2 = pool.popMat22();
		R1.set(a1);
		R2.set(a2);
		
		final Vec2 r1 = pool.popVec2();
		final Vec2 r2 = pool.popVec2();
		final Vec2 temp = pool.popVec2();
		final Vec2 d = pool.popVec2();
		
		r1.set(m_localAnchor1).subLocal(m_localCenterA);
		r2.set(m_localAnchor2).subLocal(m_localCenterB);
		Mat22.mulToOut(R1, r1, r1);
		Mat22.mulToOut(R2, r2, r2);
		d.set(c2).addLocal(r2).subLocal(c1).subLocal(r1);
		
		if (m_enableLimit) {
			Mat22.mulToOut(R1, m_localXAxis1, m_axis);
			
			temp.set(d).addLocal(r1);
			m_a1 = Vec2.cross(temp, m_axis);
			m_a2 = Vec2.cross(r2, m_axis);
			
			float translation = Vec2.dot(m_axis, d);
			if (MathUtils.abs(m_upperTranslation - m_lowerTranslation) < 2.0f * Settings.linearSlop) {
				// Prevent large angular corrections
				C2 = MathUtils.clamp(translation, -Settings.maxLinearCorrection, Settings.maxLinearCorrection);
				linearError = MathUtils.abs(translation);
				active = true;
			}
			else if (translation <= m_lowerTranslation) {
				// Prevent large linear corrections and allow some slop.
				C2 = MathUtils.clamp(translation - m_lowerTranslation + Settings.linearSlop,
						-Settings.maxLinearCorrection, 0.0f);
				linearError = m_lowerTranslation - translation;
				active = true;
			}
			else if (translation >= m_upperTranslation) {
				// Prevent large linear corrections and allow some slop.
				C2 = MathUtils.clamp(translation - m_upperTranslation - Settings.linearSlop, 0.0f,
						Settings.maxLinearCorrection);
				linearError = translation - m_upperTranslation;
				active = true;
			}
		}
		
		Mat22.mulToOut(R1, m_localYAxis1, m_perp);
		
		temp.set(d).addLocal(r1);
		m_s1 = Vec2.cross(temp, m_perp);
		m_s2 = Vec2.cross(r2, m_perp);
		
		final Vec2 impulse = pool.popVec2();
		float C1;
		C1 = Vec2.dot(m_perp, d);
		
		linearError = MathUtils.max(linearError, MathUtils.abs(C1));
		angularError = 0.0f;
		
		if (active) {
			float m1 = m_invMassA, m2 = m_invMassB;
			float i1 = m_invIA, i2 = m_invIB;
			
			float k11 = m1 + m2 + i1 * m_s1 * m_s1 + i2 * m_s2 * m_s2;
			float k12 = i1 * m_s1 * m_a1 + i2 * m_s2 * m_a2;
			float k22 = m1 + m2 + i1 * m_a1 * m_a1 + i2 * m_a2 * m_a2;
			
      m_K.m11 = k11;
      m_K.m12 = k12;
      m_K.m21 = k12;
      m_K.m22 = k22;
			
			final Vec2 C = pool.popVec2();
			C.x = C1;
			C.y = C2;
			
			m_K.solveToOut(C.negateLocal(), impulse);
			pool.pushVec2(1);
		}
		else {
			float m1 = m_invMassA, m2 = m_invMassB;
			float i1 = m_invIA, i2 = m_invIB;
			
			float k11 = m1 + m2 + i1 * m_s1 * m_s1 + i2 * m_s2 * m_s2;
			
			float impulse1;
			if (k11 != 0.0f) {
				impulse1 = -C1 / k11;
			}
			else {
				impulse1 = 0.0f;
			}
			
			impulse.x = impulse1;
			impulse.y = 0.0f;
		}
		
		final Vec2 P = pool.popVec2();
		temp.set(m_axis).mulLocal(impulse.y);
		P.set(m_perp).mulLocal(impulse.x).add(temp);
		
		float L1 = impulse.x * m_s1 + impulse.y * m_a1;
		float L2 = impulse.x * m_s2 + impulse.y * m_a2;
		
		temp.set(P).mulLocal(m_invMassA);
		c1.subLocal(temp);
		a1 -= m_invIA * L1;
		
		temp.set(P).mulLocal(m_invMassB);
		c2.addLocal(temp);
		a2 += m_invIB * L2;
		
		// TODO_ERIN remove need for this.
		b1.m_sweep.a = a1;
		b2.m_sweep.a = a2;
		b1.synchronizeTransform();
		b2.synchronizeTransform();
		
		pool.pushVec2(6);
		pool.pushMat22(2);
		
		return linearError <= Settings.linearSlop && angularError <= Settings.angularSlop;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy