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

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

The newest version!
/*
 * The MIT License (MIT)
 *
 * FXGL - JavaFX Game Library
 *
 * Copyright (c) 2015-2017 AlmasB ([email protected])
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.jbox2d.dynamics.joints;

import com.almasb.fxgl.core.math.Vec2;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.SolverData;
import org.jbox2d.pooling.IWorldPool;

//Linear constraint (point-to-line)
//d = p2 - p1 = x2 + r2 - x1 - r1
//C = dot(perp, d)
//Cdot = dot(d, cross(w1, perp)) + dot(perp, v2 + cross(w2, r2) - v1 - cross(w1, r1))
//   = -dot(perp, v1) - dot(cross(d + r1, perp), w1) + dot(perp, v2) + dot(cross(r2, perp), v2)
//J = [-perp, -cross(d + r1, perp), perp, cross(r2,perp)]
//
//Angular constraint
//C = a2 - a1 + a_initial
//Cdot = w2 - w1
//J = [0 0 -1 0 0 1]
//
//K = J * invM * JT
//
//J = [-a -s1 a s2]
//  [0  -1  0  1]
//a = perp
//s1 = cross(d + r1, a) = cross(p2 - x1, a)
//s2 = cross(r2, a) = cross(p2 - x2, a)


//Motor/Limit linear constraint
//C = dot(ax1, d)
//Cdot = = -dot(ax1, v1) - dot(cross(d + r1, ax1), w1) + dot(ax1, v2) + dot(cross(r2, ax1), v2)
//J = [-ax1 -cross(d+r1,ax1) ax1 cross(r2,ax1)]

//Block Solver
//We develop a block solver that includes the joint limit. This makes the limit stiff (inelastic) even
//when the mass has poor distribution (leading to large torques about the joint anchor points).
//
//The Jacobian has 3 rows:
//J = [-uT -s1 uT s2] // linear
//  [0   -1   0  1] // angular
//  [-vT -a1 vT a2] // limit
//
//u = perp
//v = axis
//s1 = cross(d + r1, u), s2 = cross(r2, u)
//a1 = cross(d + r1, v), a2 = cross(r2, v)

//M * (v2 - v1) = JT * df
//J * v2 = bias
//
//v2 = v1 + invM * JT * df
//J * (v1 + invM * JT * df) = bias
//K * df = bias - J * v1 = -Cdot
//K = J * invM * JT
//Cdot = J * v1 - bias
//
//Now solve for f2.
//df = f2 - f1
//K * (f2 - f1) = -Cdot
//f2 = invK * (-Cdot) + f1
//
//Clamp accumulated limit impulse.
//lower: f2(3) = max(f2(3), 0)
//upper: f2(3) = min(f2(3), 0)
//
//Solve for correct f2(1:2)
//K(1:2, 1:2) * f2(1:2) = -Cdot(1:2) - K(1:2,3) * f2(3) + K(1:2,1:3) * f1
//                    = -Cdot(1:2) - K(1:2,3) * f2(3) + K(1:2,1:2) * f1(1:2) + K(1:2,3) * f1(3)
//K(1:2, 1:2) * f2(1:2) = -Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3)) + K(1:2,1:2) * f1(1:2)
//f2(1:2) = invK(1:2,1:2) * (-Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3))) + f1(1:2)
//
//Now compute impulse to be applied:
//df = f2 - f1

/**
 * A prismatic joint. This joint provides one degree of freedom: translation along an axis fixed in
 * bodyA. Relative rotation is prevented. You can use a joint limit to restrict the range of motion
 * and a joint motor to drive the motion or to model joint friction.
 *
 * @author Daniel
 */
public class PrismaticJoint extends Joint {

    // Solver shared
    protected final Vec2 m_localAnchorA;
    protected final Vec2 m_localAnchorB;
    protected final Vec2 m_localXAxisA;
    protected final Vec2 m_localYAxisA;
    protected float m_referenceAngle;
    private final Vec3 m_impulse;
    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;

    // Solver temp
    private int m_indexA;
    private int m_indexB;
    private final Vec2 m_localCenterA = new Vec2();
    private final Vec2 m_localCenterB = new Vec2();
    private float m_invMassA;
    private float m_invMassB;
    private float m_invIA;
    private float m_invIB;
    private final Vec2 m_axis, m_perp;
    private float m_s1, m_s2;
    private float m_a1, m_a2;
    private final Mat33 m_K;
    private float m_motorMass; // effective mass for motor/limit translational constraint.

    protected PrismaticJoint(IWorldPool argWorld, PrismaticJointDef def) {
        super(argWorld, def);
        m_localAnchorA = new Vec2(def.localAnchorA);
        m_localAnchorB = new Vec2(def.localAnchorB);
        m_localXAxisA = new Vec2(def.localAxisA);
        m_localXAxisA.normalize();
        m_localYAxisA = new Vec2();
        Vec2.crossToOutUnsafe(1f, m_localXAxisA, m_localYAxisA);
        m_referenceAngle = def.referenceAngle;

        m_impulse = new Vec3();
        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_K = new Mat33();
        m_axis = new Vec2();
        m_perp = new Vec2();
    }

    public Vec2 getLocalAnchorA() {
        return m_localAnchorA;
    }

    public Vec2 getLocalAnchorB() {
        return m_localAnchorB;
    }

    @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) {
        Vec2 temp = pool.popVec2();
        temp.set(m_axis).mulLocal(m_motorImpulse + m_impulse.z);
        argOut.set(m_perp).mulLocal(m_impulse.x).addLocal(temp).mulLocal(inv_dt);
        pool.pushVec2(1);
    }

    @Override
    public float getReactionTorque(float inv_dt) {
        return inv_dt * m_impulse.y;
    }

    /**
     * Get the current joint translation, usually in meters.
     */
    public float getJointSpeed() {
        Body bA = m_bodyA;
        Body bB = m_bodyB;

        Vec2 temp = pool.popVec2();
        Vec2 rA = pool.popVec2();
        Vec2 rB = pool.popVec2();
        Vec2 p1 = pool.popVec2();
        Vec2 p2 = pool.popVec2();
        Vec2 d = pool.popVec2();
        Vec2 axis = pool.popVec2();
        Vec2 temp2 = pool.popVec2();
        Vec2 temp3 = pool.popVec2();

        temp.set(m_localAnchorA).subLocal(bA.m_sweep.localCenter);
        Rotation.mulToOutUnsafe(bA.m_xf.q, temp, rA);

        temp.set(m_localAnchorB).subLocal(bB.m_sweep.localCenter);
        Rotation.mulToOutUnsafe(bB.m_xf.q, temp, rB);

        p1.set(bA.m_sweep.c).addLocal(rA);
        p2.set(bB.m_sweep.c).addLocal(rB);

        d.set(p2).subLocal(p1);
        Rotation.mulToOutUnsafe(bA.m_xf.q, m_localXAxisA, axis);

        Vec2 vA = bA.m_linearVelocity;
        Vec2 vB = bB.m_linearVelocity;
        float wA = bA.m_angularVelocity;
        float wB = bB.m_angularVelocity;


        Vec2.crossToOutUnsafe(wA, axis, temp);
        Vec2.crossToOutUnsafe(wB, rB, temp2);
        Vec2.crossToOutUnsafe(wA, rA, temp3);

        temp2.addLocal(vB).subLocal(vA).subLocal(temp3);
        float speed = Vec2.dot(d, temp) + Vec2.dot(axis, temp2);

        pool.pushVec2(9);

        return speed;
    }

    public float getJointTranslation() {
        Vec2 pA = pool.popVec2(), pB = pool.popVec2(), axis = pool.popVec2();
        m_bodyA.getWorldPointToOut(m_localAnchorA, pA);
        m_bodyB.getWorldPointToOut(m_localAnchorB, pB);
        m_bodyA.getWorldVectorToOutUnsafe(m_localXAxisA, axis);
        pB.subLocal(pA);
        float translation = Vec2.dot(pB, axis);
        pool.pushVec2(3);
        return translation;
    }

    /**
     * Is the joint limit enabled?
     *
     * @return
     */
    public boolean isLimitEnabled() {
        return m_enableLimit;
    }

    /**
     * Enable/disable the joint limit.
     *
     * @param flag
     */
    public void enableLimit(boolean flag) {
        if (flag != m_enableLimit) {
            m_bodyA.setAwake(true);
            m_bodyB.setAwake(true);
            m_enableLimit = flag;
            m_impulse.z = 0.0f;
        }
    }

    /**
     * Get the lower joint limit, usually in meters.
     *
     * @return
     */
    public float getLowerLimit() {
        return m_lowerTranslation;
    }

    /**
     * Get the upper joint limit, usually in meters.
     *
     * @return
     */
    public float getUpperLimit() {
        return m_upperTranslation;
    }

    /**
     * Set the joint limits, usually in meters.
     *
     * @param lower
     * @param upper
     */
    public void setLimits(float lower, float upper) {
        assert (lower <= upper);
        if (lower != m_lowerTranslation || upper != m_upperTranslation) {
            m_bodyA.setAwake(true);
            m_bodyB.setAwake(true);
            m_lowerTranslation = lower;
            m_upperTranslation = upper;
            m_impulse.z = 0.0f;
        }
    }

    /**
     * Is the joint motor enabled?
     *
     * @return
     */
    public boolean isMotorEnabled() {
        return m_enableMotor;
    }

    /**
     * Enable/disable the joint motor.
     *
     * @param flag
     */
    public void enableMotor(boolean flag) {
        m_bodyA.setAwake(true);
        m_bodyB.setAwake(true);
        m_enableMotor = flag;
    }

    /**
     * Set the motor speed, usually in meters per second.
     *
     * @param speed
     */
    public void setMotorSpeed(float speed) {
        m_bodyA.setAwake(true);
        m_bodyB.setAwake(true);
        m_motorSpeed = speed;
    }

    /**
     * Get the motor speed, usually in meters per second.
     *
     * @return
     */
    public float getMotorSpeed() {
        return m_motorSpeed;
    }

    /**
     * Set the maximum motor force, usually in N.
     *
     * @param force
     */
    public void setMaxMotorForce(float force) {
        m_bodyA.setAwake(true);
        m_bodyB.setAwake(true);
        m_maxMotorForce = force;
    }

    /**
     * Get the current motor force, usually in N.
     *
     * @param inv_dt
     * @return
     */
    public float getMotorForce(float inv_dt) {
        return m_motorImpulse * inv_dt;
    }

    public float getMaxMotorForce() {
        return m_maxMotorForce;
    }

    public float getReferenceAngle() {
        return m_referenceAngle;
    }

    public Vec2 getLocalAxisA() {
        return m_localXAxisA;
    }

    @Override
    public void initVelocityConstraints(final SolverData data) {
        m_indexA = m_bodyA.m_islandIndex;
        m_indexB = m_bodyB.m_islandIndex;
        m_localCenterA.set(m_bodyA.m_sweep.localCenter);
        m_localCenterB.set(m_bodyB.m_sweep.localCenter);
        m_invMassA = m_bodyA.m_invMass;
        m_invMassB = m_bodyB.m_invMass;
        m_invIA = m_bodyA.m_invI;
        m_invIB = m_bodyB.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;

        final Rotation qA = pool.popRot();
        final Rotation qB = pool.popRot();
        final Vec2 d = pool.popVec2();
        final Vec2 temp = pool.popVec2();
        final Vec2 rA = pool.popVec2();
        final Vec2 rB = pool.popVec2();

        qA.set(aA);
        qB.set(aB);

        // Compute the effective masses.
        Rotation.mulToOutUnsafe(qA, d.set(m_localAnchorA).subLocal(m_localCenterA), rA);
        Rotation.mulToOutUnsafe(qB, d.set(m_localAnchorB).subLocal(m_localCenterB), rB);
        d.set(cB).subLocal(cA).addLocal(rB).subLocal(rA);

        float mA = m_invMassA, mB = m_invMassB;
        float iA = m_invIA, iB = m_invIB;

        // Compute motor Jacobian and effective mass.
        {
            Rotation.mulToOutUnsafe(qA, m_localXAxisA, m_axis);
            temp.set(d).addLocal(rA);
            m_a1 = Vec2.cross(temp, m_axis);
            m_a2 = Vec2.cross(rB, m_axis);

            m_motorMass = mA + mB + iA * m_a1 * m_a1 + iB * m_a2 * m_a2;
            if (m_motorMass > 0.0f) {
                m_motorMass = 1.0f / m_motorMass;
            }
        }

        // Prismatic constraint.
        {
            Rotation.mulToOutUnsafe(qA, m_localYAxisA, m_perp);

            temp.set(d).addLocal(rA);
            m_s1 = Vec2.cross(temp, m_perp);
            m_s2 = Vec2.cross(rB, m_perp);

            float k11 = mA + mB + iA * m_s1 * m_s1 + iB * m_s2 * m_s2;
            float k12 = iA * m_s1 + iB * m_s2;
            float k13 = iA * m_s1 * m_a1 + iB * m_s2 * m_a2;
            float k22 = iA + iB;
            if (k22 == 0.0f) {
                // For bodies with fixed rotation.
                k22 = 1.0f;
            }
            float k23 = iA * m_a1 + iB * m_a2;
            float k33 = mA + mB + iA * m_a1 * m_a1 + iB * m_a2 * m_a2;

            m_K.ex.set(k11, k12, k13);
            m_K.ey.set(k12, k22, k23);
            m_K.ez.set(k13, k23, k33);
        }

        // Compute motor and limit terms.
        if (m_enableLimit) {

            float jointTranslation = Vec2.dot(m_axis, d);
            if (JBoxUtils.abs(m_upperTranslation - m_lowerTranslation) < 2.0f * JBoxSettings.linearSlop) {
                m_limitState = LimitState.EQUAL;
            } else if (jointTranslation <= m_lowerTranslation) {
                if (m_limitState != LimitState.AT_LOWER) {
                    m_limitState = LimitState.AT_LOWER;
                    m_impulse.z = 0.0f;
                }
            } else if (jointTranslation >= m_upperTranslation) {
                if (m_limitState != LimitState.AT_UPPER) {
                    m_limitState = LimitState.AT_UPPER;
                    m_impulse.z = 0.0f;
                }
            } else {
                m_limitState = LimitState.INACTIVE;
                m_impulse.z = 0.0f;
            }
        } else {
            m_limitState = LimitState.INACTIVE;
            m_impulse.z = 0.0f;
        }

        if (!m_enableMotor) {
            m_motorImpulse = 0.0f;
        }

        if (data.step.warmStarting) {
            // Account for variable time step.
            m_impulse.mulLocal(data.step.dtRatio);
            m_motorImpulse *= data.step.dtRatio;

            final Vec2 P = pool.popVec2();
            temp.set(m_axis).mulLocal(m_motorImpulse + m_impulse.z);
            P.set(m_perp).mulLocal(m_impulse.x).addLocal(temp);

            float LA = m_impulse.x * m_s1 + m_impulse.y + (m_motorImpulse + m_impulse.z) * m_a1;
            float LB = m_impulse.x * m_s2 + m_impulse.y + (m_motorImpulse + m_impulse.z) * m_a2;

            vA.x -= mA * P.x;
            vA.y -= mA * P.y;
            wA -= iA * LA;

            vB.x += mB * P.x;
            vB.y += mB * P.y;
            wB += iB * LB;

            pool.pushVec2(1);
        } else {
            m_impulse.setZero();
            m_motorImpulse = 0.0f;
        }

        // data.velocities[m_indexA].v.set(vA);
        data.velocities[m_indexA].w = wA;
        // data.velocities[m_indexB].v.set(vB);
        data.velocities[m_indexB].w = wB;

        pool.pushRot(2);
        pool.pushVec2(4);
    }

    @Override
    public void solveVelocityConstraints(final 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;

        float mA = m_invMassA, mB = m_invMassB;
        float iA = m_invIA, iB = m_invIB;

        final Vec2 temp = pool.popVec2();

        // Solve linear motor constraint.
        if (m_enableMotor && m_limitState != LimitState.EQUAL) {
            temp.set(vB).subLocal(vA);
            float Cdot = Vec2.dot(m_axis, temp) + m_a2 * wB - m_a1 * wA;
            float impulse = m_motorMass * (m_motorSpeed - Cdot);
            float oldImpulse = m_motorImpulse;
            float maxImpulse = data.step.dt * m_maxMotorForce;
            m_motorImpulse = JBoxUtils.clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse);
            impulse = m_motorImpulse - oldImpulse;

            final Vec2 P = pool.popVec2();
            P.set(m_axis).mulLocal(impulse);
            float LA = impulse * m_a1;
            float LB = impulse * m_a2;

            vA.x -= mA * P.x;
            vA.y -= mA * P.y;
            wA -= iA * LA;

            vB.x += mB * P.x;
            vB.y += mB * P.y;
            wB += iB * LB;

            pool.pushVec2(1);
        }

        final Vec2 Cdot1 = pool.popVec2();
        temp.set(vB).subLocal(vA);
        Cdot1.x = Vec2.dot(m_perp, temp) + m_s2 * wB - m_s1 * wA;
        Cdot1.y = wB - wA;
        // System.out.println(Cdot1);

        if (m_enableLimit && m_limitState != LimitState.INACTIVE) {
            // Solve prismatic and limit constraint in block form.
            float Cdot2;
            temp.set(vB).subLocal(vA);
            Cdot2 = Vec2.dot(m_axis, temp) + m_a2 * wB - m_a1 * wA;

            final Vec3 Cdot = pool.popVec3();
            Cdot.set(Cdot1.x, Cdot1.y, Cdot2);

            final Vec3 f1 = pool.popVec3();
            final Vec3 df = pool.popVec3();

            f1.set(m_impulse);
            m_K.solve33ToOut(Cdot.negateLocal(), df);
            // Cdot.negateLocal(); not used anymore
            m_impulse.addLocal(df);

            if (m_limitState == LimitState.AT_LOWER) {
                m_impulse.z = JBoxUtils.max(m_impulse.z, 0.0f);
            } else if (m_limitState == LimitState.AT_UPPER) {
                m_impulse.z = JBoxUtils.min(m_impulse.z, 0.0f);
            }

            // f2(1:2) = invK(1:2,1:2) * (-Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3))) +
            // f1(1:2)
            final Vec2 b = pool.popVec2();
            final Vec2 f2r = pool.popVec2();

            temp.set(m_K.ez.x, m_K.ez.y).mulLocal(m_impulse.z - f1.z);
            b.set(Cdot1).negateLocal().subLocal(temp);

            m_K.solve22ToOut(b, f2r);
            f2r.addLocal(f1.x, f1.y);
            m_impulse.x = f2r.x;
            m_impulse.y = f2r.y;

            df.set(m_impulse).subLocal(f1);

            final Vec2 P = pool.popVec2();
            temp.set(m_axis).mulLocal(df.z);
            P.set(m_perp).mulLocal(df.x).addLocal(temp);

            float LA = df.x * m_s1 + df.y + df.z * m_a1;
            float LB = df.x * m_s2 + df.y + df.z * m_a2;

            vA.x -= mA * P.x;
            vA.y -= mA * P.y;
            wA -= iA * LA;

            vB.x += mB * P.x;
            vB.y += mB * P.y;
            wB += iB * LB;

            pool.pushVec2(3);
            pool.pushVec3(3);
        } else {
            // Limit is inactive, just solve the prismatic constraint in block form.
            final Vec2 df = pool.popVec2();
            m_K.solve22ToOut(Cdot1.negateLocal(), df);
            Cdot1.negateLocal();

            m_impulse.x += df.x;
            m_impulse.y += df.y;

            final Vec2 P = pool.popVec2();
            P.set(m_perp).mulLocal(df.x);
            float LA = df.x * m_s1 + df.y;
            float LB = df.x * m_s2 + df.y;

            vA.x -= mA * P.x;
            vA.y -= mA * P.y;
            wA -= iA * LA;

            vB.x += mB * P.x;
            vB.y += mB * P.y;
            wB += iB * LB;

            pool.pushVec2(2);
        }

        // data.velocities[m_indexA].v.set(vA);
        data.velocities[m_indexA].w = wA;
        // data.velocities[m_indexB].v.set(vB);
        data.velocities[m_indexB].w = wB;

        pool.pushVec2(2);
    }


    @Override
    public boolean solvePositionConstraints(final SolverData data) {

        final Rotation qA = pool.popRot();
        final Rotation qB = pool.popRot();
        final Vec2 rA = pool.popVec2();
        final Vec2 rB = pool.popVec2();
        final Vec2 d = pool.popVec2();
        final Vec2 axis = pool.popVec2();
        final Vec2 perp = pool.popVec2();
        final Vec2 temp = pool.popVec2();
        final Vec2 C1 = pool.popVec2();

        final Vec3 impulse = pool.popVec3();

        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;

        qA.set(aA);
        qB.set(aB);

        float mA = m_invMassA, mB = m_invMassB;
        float iA = m_invIA, iB = m_invIB;

        // Compute fresh Jacobians
        Rotation.mulToOutUnsafe(qA, temp.set(m_localAnchorA).subLocal(m_localCenterA), rA);
        Rotation.mulToOutUnsafe(qB, temp.set(m_localAnchorB).subLocal(m_localCenterB), rB);
        d.set(cB).addLocal(rB).subLocal(cA).subLocal(rA);

        Rotation.mulToOutUnsafe(qA, m_localXAxisA, axis);
        float a1 = Vec2.cross(temp.set(d).addLocal(rA), axis);
        float a2 = Vec2.cross(rB, axis);
        Rotation.mulToOutUnsafe(qA, m_localYAxisA, perp);

        float s1 = Vec2.cross(temp.set(d).addLocal(rA), perp);
        float s2 = Vec2.cross(rB, perp);

        C1.x = Vec2.dot(perp, d);
        C1.y = aB - aA - m_referenceAngle;

        float linearError = JBoxUtils.abs(C1.x);
        float angularError = JBoxUtils.abs(C1.y);

        boolean active = false;
        float C2 = 0.0f;
        if (m_enableLimit) {
            float translation = Vec2.dot(axis, d);
            if (JBoxUtils.abs(m_upperTranslation - m_lowerTranslation) < 2.0f * JBoxSettings.linearSlop) {
                // Prevent large angular corrections
                C2 =
                        JBoxUtils.clamp(translation, -JBoxSettings.maxLinearCorrection,
                                JBoxSettings.maxLinearCorrection);
                linearError = JBoxUtils.max(linearError, JBoxUtils.abs(translation));
                active = true;
            } else if (translation <= m_lowerTranslation) {
                // Prevent large linear corrections and allow some slop.
                C2 =
                        JBoxUtils.clamp(translation - m_lowerTranslation + JBoxSettings.linearSlop,
                                -JBoxSettings.maxLinearCorrection, 0.0f);
                linearError = JBoxUtils.max(linearError, m_lowerTranslation - translation);
                active = true;
            } else if (translation >= m_upperTranslation) {
                // Prevent large linear corrections and allow some slop.
                C2 =
                        JBoxUtils.clamp(translation - m_upperTranslation - JBoxSettings.linearSlop, 0.0f,
                                JBoxSettings.maxLinearCorrection);
                linearError = JBoxUtils.max(linearError, translation - m_upperTranslation);
                active = true;
            }
        }

        if (active) {
            float k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2;
            float k12 = iA * s1 + iB * s2;
            float k13 = iA * s1 * a1 + iB * s2 * a2;
            float k22 = iA + iB;
            if (k22 == 0.0f) {
                // For fixed rotation
                k22 = 1.0f;
            }
            float k23 = iA * a1 + iB * a2;
            float k33 = mA + mB + iA * a1 * a1 + iB * a2 * a2;

            final Mat33 K = pool.popMat33();
            K.ex.set(k11, k12, k13);
            K.ey.set(k12, k22, k23);
            K.ez.set(k13, k23, k33);

            final Vec3 C = pool.popVec3();
            C.x = C1.x;
            C.y = C1.y;
            C.z = C2;

            K.solve33ToOut(C.negateLocal(), impulse);
            pool.pushVec3(1);
            pool.pushMat33(1);
        } else {
            float k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2;
            float k12 = iA * s1 + iB * s2;
            float k22 = iA + iB;
            if (k22 == 0.0f) {
                k22 = 1.0f;
            }

            final Mat22 K = pool.popMat22();
            K.ex.set(k11, k12);
            K.ey.set(k12, k22);

            // temp is impulse1
            K.solveToOut(C1.negateLocal(), temp);
            C1.negateLocal();

            impulse.x = temp.x;
            impulse.y = temp.y;
            impulse.z = 0.0f;

            pool.pushMat22(1);
        }

        float Px = impulse.x * perp.x + impulse.z * axis.x;
        float Py = impulse.x * perp.y + impulse.z * axis.y;
        float LA = impulse.x * s1 + impulse.y + impulse.z * a1;
        float LB = impulse.x * s2 + impulse.y + impulse.z * a2;

        cA.x -= mA * Px;
        cA.y -= mA * Py;
        aA -= iA * LA;
        cB.x += mB * Px;
        cB.y += mB * Py;
        aB += iB * LB;

        // data.positions[m_indexA].c.set(cA);
        data.positions[m_indexA].a = aA;
        // data.positions[m_indexB].c.set(cB);
        data.positions[m_indexB].a = aB;

        pool.pushVec2(7);
        pool.pushVec3(1);
        pool.pushRot(2);

        return linearError <= JBoxSettings.linearSlop && angularError <= JBoxSettings.angularSlop;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy