Package org.jbox2d.dynamics.joints

Source Code of org.jbox2d.dynamics.joints.RevoluteJoint

/*******************************************************************************
* 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 <organization> 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.
******************************************************************************/
/*
* 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.Mat33;
import org.jbox2d.common.MathUtils;
import org.jbox2d.common.Settings;
import org.jbox2d.common.Vec2;
import org.jbox2d.common.Vec3;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.TimeStep;
import org.jbox2d.pooling.IWorldPool;

//Point-to-point constraint
//C = p2 - p1
//Cdot = v2 - v1
//   = v2 + cross(w2, r2) - v1 - cross(w1, r1)
//J = [-I -r1_skew I r2_skew ]
//Identity used:
//w k % (rx i + ry j) = w * (-ry i + rx j)

//Motor constraint
//Cdot = w2 - w1
//J = [0 0 -1 0 0 1]
//K = invI1 + invI2

/**
* A revolute joint constrains two bodies to share a common point while they
* are free to rotate about the point. The relative rotation about the shared
* point is the joint angle. You can limit the relative rotation with
* a joint limit that specifies a lower and upper angle. You can use a motor
* to drive the relative rotation about the shared point. A maximum motor torque
* is provided so that infinite forces are not generated.
*
* @author Daniel Murphy
*/
public class RevoluteJoint extends Joint {
 
  public final Vec2 m_localAnchor1 = new Vec2(); // relative
  public final Vec2 m_localAnchor2 = new Vec2();
  public final Vec3 m_impulse = new Vec3();
  public float m_motorImpulse;
 
  public final Mat33 m_mass = new Mat33(); // effective mass for point-to-point
                        // constraint.
  public float m_motorMass; // effective mass for motor/limit angular constraint.
 
  public boolean m_enableMotor;
  public float m_maxMotorTorque;
  public float m_motorSpeed;
 
  public boolean m_enableLimit;
  public float m_referenceAngle;
  public float m_lowerAngle;
  public float m_upperAngle;
  public LimitState m_limitState;
 
  public RevoluteJoint(IWorldPool argWorld, RevoluteJointDef def) {
    super(argWorld, def);
    m_localAnchor1.set(def.localAnchorA);
    m_localAnchor2.set(def.localAnchorB);
    m_referenceAngle = def.referenceAngle;
   
    m_motorImpulse = 0;
   
    m_lowerAngle = def.lowerAngle;
    m_upperAngle = def.upperAngle;
    m_maxMotorTorque = def.maxMotorTorque;
    m_motorSpeed = def.motorSpeed;
    m_enableLimit = def.enableLimit;
    m_enableMotor = def.enableMotor;
  }
 
  @Override
  public void initVelocityConstraints(final TimeStep step) {
    final Body b1 = m_bodyA;
    final Body b2 = m_bodyB;
   
    if (m_enableMotor || m_enableLimit) {
      // You cannot create a rotation limit between bodies that
      // both have fixed rotation.
      assert (b1.m_invI > 0.0f || b2.m_invI > 0.0f);
    }
   
    final Vec2 r1 = pool.popVec2();
    final Vec2 r2 = pool.popVec2();
   
    // Compute the effective mass matrix.
    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);
   
    // J = [-I -r1_skew I r2_skew]
    // [ 0 -1 0 1]
    // r_skew = [-ry; rx]
   
    // Matlab
    // K = [ m1+r1y^2*i1+m2+r2y^2*i2, -r1y*i1*r1x-r2y*i2*r2x, -r1y*i1-r2y*i2]
    // [ -r1y*i1*r1x-r2y*i2*r2x, m1+r1x^2*i1+m2+r2x^2*i2, r1x*i1+r2x*i2]
    // [ -r1y*i1-r2y*i2, r1x*i1+r2x*i2, i1+i2]
   
    // K = [(1/m1 + 1/m2) * eye(2) - skew(r1) * invI1 * skew(r1) - skew(r2) * invI2 *
    // skew(r2)]
    // = [1/m1+1/m2 0 ] + invI1 * [r1.y*r1.y -r1.x*r1.y] + invI2 * [r1.y*r1.y
    // -r1.x*r1.y]
    // [ 0 1/m1+1/m2] [-r1.x*r1.y r1.x*r1.x] [-r1.x*r1.y r1.x*r1.x]
   
    float m1 = b1.m_invMass, m2 = b2.m_invMass;
    float i1 = b1.m_invI, i2 = b2.m_invI;
   
    m_mass.col1.x = m1 + m2 + r1.y * r1.y * i1 + r2.y * r2.y * i2;
    m_mass.col2.x = -r1.y * r1.x * i1 - r2.y * r2.x * i2;
    m_mass.col3.x = -r1.y * i1 - r2.y * i2;
    m_mass.col1.y = m_mass.col2.x;
    m_mass.col2.y = m1 + m2 + r1.x * r1.x * i1 + r2.x * r2.x * i2;
    m_mass.col3.y = r1.x * i1 + r2.x * i2;
    m_mass.col1.z = m_mass.col3.x;
    m_mass.col2.z = m_mass.col3.y;
    m_mass.col3.z = i1 + i2;
   
    m_motorMass = i1 + i2;
    if (m_motorMass > 0.0f) {
      m_motorMass = 1.0f / m_motorMass;
    }
   
    if (m_enableMotor == false) {
      m_motorImpulse = 0.0f;
    }
   
//    System.out.printf("joint angle: %f, %f, and %f\n", b2.m_sweep.a, b1.m_sweep.a, m_referenceAngle);
   
    if (m_enableLimit) {
      float jointAngle = b2.m_sweep.a - b1.m_sweep.a - m_referenceAngle;
      if (MathUtils.abs(m_upperAngle - m_lowerAngle) < 2.0f * Settings.angularSlop) {
        m_limitState = LimitState.EQUAL;
      }
      else if (jointAngle <= m_lowerAngle) {
        if (m_limitState != LimitState.AT_LOWER) {
          m_impulse.z = 0.0f;
        }
        m_limitState = LimitState.AT_LOWER;
      }
      else if (jointAngle >= m_upperAngle) {
        if (m_limitState != LimitState.AT_UPPER) {
          m_impulse.z = 0.0f;
        }
        m_limitState = LimitState.AT_UPPER;
      }
      else {
        m_limitState = LimitState.INACTIVE;
        m_impulse.z = 0.0f;
      }
    }
    else {
      m_limitState = LimitState.INACTIVE;
    }
//    System.out.printf("limit state: %s\n", m_limitState.toString());
   
    if (step.warmStarting) {
      // Scale impulses to support a variable time step.
      m_impulse.mulLocal(step.dtRatio);
      m_motorImpulse *= step.dtRatio;
     
      Vec2 temp = pool.popVec2();
      Vec2 P = pool.popVec2();
      P.set(m_impulse.x, m_impulse.y);
     
      temp.set(P).mulLocal(m1);
      b1.m_linearVelocity.subLocal(temp);
      b1.m_angularVelocity -= i1 * (Vec2.cross(r1, P) + m_motorImpulse + m_impulse.z);
     
      temp.set(P).mulLocal(m2);
      b2.m_linearVelocity.addLocal(temp);
      b2.m_angularVelocity += i2 * (Vec2.cross(r2, P) + m_motorImpulse + m_impulse.z);
     
      pool.pushVec2(2);
    }
    else {
      m_impulse.setZero();
      m_motorImpulse = 0.0f;
    }
    pool.pushVec2(2);
  }
 
  @Override
  public void solveVelocityConstraints(final TimeStep step) {
    final Body b1 = m_bodyA;
    final 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;
   
    float m1 = b1.m_invMass, m2 = b2.m_invMass;
    float i1 = b1.m_invI, i2 = b2.m_invI;
   
    // Solve motor constraint.
    if (m_enableMotor && m_limitState != LimitState.EQUAL) {
      float Cdot = w2 - w1 - m_motorSpeed;
      float impulse = m_motorMass * (-Cdot);
      float oldImpulse = m_motorImpulse;
      float maxImpulse = step.dt * m_maxMotorTorque;
      m_motorImpulse = MathUtils.clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse);
      impulse = m_motorImpulse - oldImpulse;
     
      w1 -= i1 * impulse;
      w2 += i2 * impulse;
    }
    final Vec2 temp = pool.popVec2();
    final Vec2 r1 = pool.popVec2();
    final Vec2 r2 = pool.popVec2();
   
    // Solve limit constraint.
    if (m_enableLimit && m_limitState != LimitState.INACTIVE) {
     
      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);
      // Vec2 r1 = b2Mul(b1.getTransform().R, m_localAnchor1 - b1.getLocalCenter());
      // Vec2 r2 = b2Mul(b2.getTransform().R, m_localAnchor2 - b2.getLocalCenter());
     
      final Vec2 Cdot1 = pool.popVec2();
      final Vec3 Cdot = pool.popVec3();
     
      // Solve point-to-point constraint
      Vec2.crossToOut(w1, r1, temp);
      Vec2.crossToOut(w2, r2, Cdot1);
      Cdot1.addLocal(v2).subLocal(v1).subLocal(temp);
      float Cdot2 = w2 - w1;
      Cdot.set(Cdot1.x, Cdot1.y, Cdot2);
      // Vec2 Cdot1 = v2 + b2Cross(w2, r2) - v1 - b2Cross(w1, r1);
      // float Cdot2 = w2 - w1;
      // b2Vec3 Cdot(Cdot1.x, Cdot1.y, Cdot2);
     
      Vec3 impulse = pool.popVec3();
      m_mass.solve33ToOut(Cdot.negateLocal(), impulse);
      // Cdot.negateLocal(); just leave negated, we don't use later
     
      if (m_limitState == LimitState.EQUAL) {
        m_impulse.addLocal(impulse);
      }
      else if (m_limitState == LimitState.AT_LOWER) {
        float newImpulse = m_impulse.z + impulse.z;
        if (newImpulse < 0.0f) {
          m_mass.solve22ToOut(Cdot1.negateLocal(), temp);
          //Cdot1.negateLocal(); just leave negated, we don't use it again
          impulse.x = temp.x;
          impulse.y = temp.y;
          impulse.z = -m_impulse.z;
          m_impulse.x += temp.x;
          m_impulse.y += temp.y;
          m_impulse.z = 0.0f;
        }
      }
      else if (m_limitState == LimitState.AT_UPPER) {
        float newImpulse = m_impulse.z + impulse.z;
        if (newImpulse > 0.0f) {
          m_mass.solve22ToOut(Cdot1.negateLocal(), temp);
          //Cdot1.negateLocal(); just leave negated, we don't use it again
          impulse.x = temp.x;
          impulse.y = temp.y;
          impulse.z = -m_impulse.z;
          m_impulse.x += temp.x;
          m_impulse.y += temp.y;
          m_impulse.z = 0.0f;
        }
      }
      final Vec2 P = pool.popVec2();
     
      P.set(impulse.x, impulse.y);
     
      temp.set(P).mulLocal(m1);
      v1.subLocal(temp);
      w1 -= i1 * (Vec2.cross(r1, P) + impulse.z);
     
      temp.set(P).mulLocal(m2);
      v2.addLocal(temp);
      w2 += i2 * (Vec2.cross(r2, P) + impulse.z);
     
      pool.pushVec2(2);
      pool.pushVec3(2);
    }
    else {
      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);
      // Vec2 r1 = b2Mul(b1.getTransform().R, m_localAnchor1 - b1.getLocalCenter());
      // Vec2 r2 = b2Mul(b2.getTransform().R, m_localAnchor2 - b2.getLocalCenter());
     
      // Solve point-to-point constraint
      Vec2 Cdot = pool.popVec2();
      Vec2 impulse = pool.popVec2();
     
      Vec2.crossToOut(w1, r1, temp);
      Vec2.crossToOut(w2, r2, Cdot);
      Cdot.addLocal(v2).subLocal(v1).subLocal(temp);
      m_mass.solve22ToOut(Cdot.negateLocal(), impulse); // just leave negated
     
      m_impulse.x += impulse.x;
      m_impulse.y += impulse.y;
     
      temp.set(impulse).mulLocal(m1);
      v1.subLocal(temp);
      w1 -= i1 * Vec2.cross(r1, impulse);
     
      temp.set(impulse).mulLocal(m2);
      v2.addLocal(temp);
      w2 += i2 * Vec2.cross(r2, impulse);
     
      pool.pushVec2(2);
    }
   
    b1.m_angularVelocity = w1;
    b2.m_angularVelocity = w2;
   
    pool.pushVec2(3);
  }
 
  @Override
  public boolean solvePositionConstraints(float baumgarte) {
    final Body b1 = m_bodyA;
    final Body b2 = m_bodyB;
   
    // TODO_ERIN block solve with limit.
   
    float angularError = 0.0f;
    float positionError = 0.0f;
   
    // Solve angular limit constraint.
    if (m_enableLimit && m_limitState != LimitState.INACTIVE) {
      float angle = b2.m_sweep.a - b1.m_sweep.a - m_referenceAngle;
      float limitImpulse = 0.0f;
     
      if (m_limitState == LimitState.EQUAL) {
        // Prevent large angular corrections
        float C = MathUtils.clamp(angle - m_lowerAngle, -Settings.maxAngularCorrection,
            Settings.maxAngularCorrection);
        limitImpulse = -m_motorMass * C;
        angularError = MathUtils.abs(C);
      }
      else if (m_limitState == LimitState.AT_LOWER) {
        float C = angle - m_lowerAngle;
        angularError = -C;
       
        // Prevent large angular corrections and allow some slop.
        C = MathUtils.clamp(C + Settings.angularSlop, -Settings.maxAngularCorrection, 0.0f);
        limitImpulse = -m_motorMass * C;
      }
      else if (m_limitState == LimitState.AT_UPPER) {
        float C = angle - m_upperAngle;
        angularError = C;
       
        // Prevent large angular corrections and allow some slop.
        C = MathUtils.clamp(C - Settings.angularSlop, 0.0f, Settings.maxAngularCorrection);
        limitImpulse = -m_motorMass * C;
      }
     
      b1.m_sweep.a -= b1.m_invI * limitImpulse;
      b2.m_sweep.a += b2.m_invI * limitImpulse;
     
      b1.synchronizeTransform();
      b2.synchronizeTransform();
    }
   
    // Solve point-to-point constraint.
    {
      Vec2 impulse = pool.popVec2();

      Vec2 r1 = pool.popVec2();
      Vec2 r2 = pool.popVec2();
      Vec2 C = 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);
     
      C.set(b2.m_sweep.c).addLocal(r2).subLocal(b1.m_sweep.c).subLocal(r1);
      positionError = C.length();
     
      float invMass1 = b1.m_invMass, invMass2 = b2.m_invMass;
      float invI1 = b1.m_invI, invI2 = b2.m_invI;
     
      // Handle large detachment.
      final float k_allowedStretch = 10.0f * Settings.linearSlop;
      if (C.lengthSquared() > k_allowedStretch * k_allowedStretch) {
        Vec2 u = pool.popVec2();
       
        // Use a particle solution (no rotation).
        // u.set(C);
        // u.normalize(); ?? we don't even use this
        float m = invMass1 + invMass2;
        if (m > 0.0f) {
          m = 1.0f / m;
        }
        impulse.set(C).negateLocal().mulLocal(m);
        final float k_beta = 0.5f;
        // using u as temp variable
        u.set(impulse).mulLocal(k_beta * invMass1);
        b1.m_sweep.c.subLocal(u);
        u.set(impulse).mulLocal(k_beta * invMass2);
        b2.m_sweep.c.addLocal(u);
       
        C.set(b2.m_sweep.c).addLocal(r2).subLocal(b1.m_sweep.c).subLocal(r1);
       
        pool.pushVec2(1);
      }
     
      Mat22 K1 = pool.popMat22();
      K1.m11 = invMass1 + invMass2;
      K1.m21 = 0.0f;
      K1.m12 = 0.0f;
      K1.m22 = invMass1 + invMass2;
     
      Mat22 K2 = pool.popMat22();
      K2.m11 = invI1 * r1.y * r1.y;
      K2.m21 = -invI1 * r1.x * r1.y;
      K2.m12 = -invI1 * r1.x * r1.y;
      K2.m22 = invI1 * r1.x * r1.x;
     
      Mat22 K3 = pool.popMat22();
      K3.m11 = invI2 * r2.y * r2.y;
      K3.m21 = -invI2 * r2.x * r2.y;
      K3.m12 = -invI2 * r2.x * r2.y;
      K3.m22 = invI2 * r2.x * r2.x;
     
      K1.addLocal(K2).addLocal(K3);
      K1.solveToOut(C.negateLocal(), impulse); // just leave c negated
     
      // using C as temp variable
      C.set(impulse).mulLocal(b1.m_invMass);
      b1.m_sweep.c.subLocal(C);
      b1.m_sweep.a -= b1.m_invI * Vec2.cross(r1, impulse);
     
      C.set(impulse).mulLocal(b2.m_invMass);
      b2.m_sweep.c.addLocal(C);
      b2.m_sweep.a += b2.m_invI * Vec2.cross(r2, impulse);
     
      b1.synchronizeTransform();
      b2.synchronizeTransform();
     
      pool.pushMat22(3);
      pool.pushVec2(4);
     
    }
   
    return positionError <= Settings.linearSlop && angularError <= Settings.angularSlop;
  }
 
  @Override
  public void getAnchorA(Vec2 argOut) {
    m_bodyA.getWorldPointToOut(m_localAnchor1, argOut);
  }
 
  @Override
  public void getAnchorB(Vec2 argOut) {
    m_bodyB.getWorldPointToOut(m_localAnchor2, argOut);
  }
 
  @Override
  public void getReactionForce(float inv_dt, Vec2 argOut) {
    argOut.set(m_impulse.x, m_impulse.y).mulLocal(inv_dt);
  }
 
  @Override
  public float getReactionTorque(float inv_dt) {
    return inv_dt * m_impulse.z;
  }
 
  public float getJointAngle() {
    final Body b1 = m_bodyA;
    final Body b2 = m_bodyB;
    return b2.m_sweep.a - b1.m_sweep.a - m_referenceAngle;
  }
 
  public float getJointSpeed() {
    final Body b1 = m_bodyA;
    final Body b2 = m_bodyB;
    return b2.m_angularVelocity - b1.m_angularVelocity;
  }
 
  public boolean isMotorEnabled() {
    return m_enableMotor;
  }
 
  public void enableMotor(boolean flag) {
    m_bodyA.setAwake(true);
    m_bodyB.setAwake(true);
    m_enableMotor = flag;
  }
 
  public float getMotorTorque() {
    return m_motorImpulse;
  }
 
  public void setMotorSpeed(final float speed) {
    m_bodyA.setAwake(true);
    m_bodyB.setAwake(true);
    m_motorSpeed = speed;
  }
 
  public void setMaxMotorTorque(final float torque) {
    m_bodyA.setAwake(true);
    m_bodyB.setAwake(true);
    m_maxMotorTorque = torque;
  }
 
  public boolean isLimitEnabled() {
    return m_enableLimit;
  }
 
  public void enableLimit(final boolean flag) {
    m_bodyA.setAwake(true);
    m_bodyB.setAwake(true);
    m_enableLimit = flag;
  }
 
  public float getLowerLimit() {
    return m_lowerAngle;
  }
 
  public float getUpperLimit() {
    return m_upperAngle;
  }
 
  public void setLimits(final float lower, final float upper) {
    assert (lower <= upper);
    m_bodyA.setAwake(true);
    m_bodyB.setAwake(true);
    m_lowerAngle = lower;
    m_upperAngle = upper;
  }
}
TOP

Related Classes of org.jbox2d.dynamics.joints.RevoluteJoint

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.