API Documentation for:
Show:

File:addBox2D\Dynamics\Joints\b2RopeJoint.js

//################################################################################################//
//################################################################################################//
//                                                                                                //
//                ██    ██████ ██████                      ██       ██        ██                  //
//                ██        ██ ██  ██                      ██                 ██                  //
//                █████     ██ ██  ██ █████ █████ █████    ██ █████ ██ █████ █████                //
//                ██ ██ ██████ ██████ ██ ██ ██ ██ ██ ██    ██ ██ ██ ██ ██ ██  ██                  //
//                ██ ██ ██     ██ ██  ██ ██ ██ ██ █████    ██ ██ ██ ██ ██ ██  ██                  //
//                ██ ██ ██     ██  ██ ██ ██ ██ ██ ██       ██ ██ ██ ██ ██ ██  ██                  //
//                █████ ██████ ██  ██ █████ █████ █████ █████ █████ ██ ██ ██  ████                //
//                                          ██                                                    //
//                                          ██                                                    //
//                                                                                                //
//################################################################################################//
//################################################################################################//

 // CLASS CONSTRUCTOR

    /**
     * A rope joint enforces a maximum distance between two points on two bodies.
     * It has no other effect.
     *
     * Warning: if you attempt to change the maximum length during
     * the simulation you will get some non-physical behavior. A model that would allow you
     * to dynamically modify the length would have some sponginess, so I chose not to
     * implement it that way. See b2DistanceJoint if you want to dynamically  control length.
     *
     *      @example Limit:
     *               C = norm(pB - pA) - L
     *               u = (pB - pA) / norm(pB - pA)
     *               Cdot = dot(u, vB + cross(wB, rB) - vA - cross(wA, rA))
     *               J = [-u -cross(rA, u) u cross(rB, u)]
     *               K = J * invM * JT
     *                 = invMassA + invIA * cross(rA, u)^2 + invMassB + invIB * cross(rB, u)^2
     *
     * @class   b2RopeJoint
     * @constructor
     * @extends {b2Joint}
     * @param   {b2RopeJointDef}  ropeJointDef
     * @module  Joints
     */
    //TODO: test b2RopeJoint. Ported but not yet tested.
    function b2RopeJoint( ropeJointDef ) {

        /**
         *Invoke parent class constructor function reference.
         */
        this.constructor( ropeJointDef );

////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                //
//                  ██████                              ██   ██                                   //
//                  ██  ██                              ██                                        //
//                  ██  ██ ████ █████ █████ █████ ████ █████ ██ █████ █████                       //
//                  ██████ ██   ██ ██ ██ ██ ██ ██ ██    ██   ██ ██ ██ ██                          //
//                  ██     ██   ██ ██ ██ ██ █████ ██    ██   ██ █████ █████                       //
//                  ██     ██   ██ ██ ██ ██ ██    ██    ██   ██ ██       ██                       //
//                  ██     ██   █████ █████ █████ ██    ████ ██ █████ █████                       //
//                                    ██                                                          //
//                                    ██                                                          //
//                                                                                                //
////////////////////////////////////////////////////////////////////////////////////////////////////

     // property DECLARATIONS

        /**
         *
         * @public
         * @property  m_localAnchorA
         * @type      {b2Vec2}
         * @default   {b2ropeDef.localAnchorB}  (0,0)
         */
        this.m_localAnchorA = new b2Vec2;

        /**
         *
         * @public
         * @property  m_localAnchorB
         * @type      {b2Vec2}
         * @default   {b2ropeDef.localAnchorB}  (0,0)
         */
        this.m_localAnchorB = new b2Vec2;

        /**
         *
         * @public
         * @property  m_localCenterA
         * @type      {b2Vec2}
         */
        this.m_localCenterA = new b2Vec2;

        /**
         *
         * @public
         * @property  m_localCenterB
         * @type      {b2Vec2}
         */
        this.m_localCenterB = new b2Vec2;

        /**
         *
         * @public
         * @property  m_u
         * @type      {b2Vec2}
         */
        this.m_u = new b2Vec2;

        /**
         *
         * @public
         * @property  m_rA
         * @type      {b2Vec2}
         */
        this.m_rA = new b2Vec2;

        /**
         * @public
         * @property  m_rB
         * @type      {b2Vec2}
         */
        this.m_rB = new b2Vec2;

        /**
         * @public
         * @property  m_qA
         * @type      {b2Rot}
         */
        this.m_qA = new b2Rot;

        /**
         * @public
         * @property  m_qA
         * @type      {b2Rot}
         */
        this.m_qA = new b2Rot;

        /**
         * @public
         * @property  m_qB
         * @type      {b2Rot}
         */
        this.m_qB = new b2Rot;

        /**
         * @public
         * @property  m_lalcA
         * @type      {b2Vec2}
         */
        this.m_lalcA = new b2Vec2;

        /**
         * @public
         * @property  m_lalcB
         * @type      {b2Vec2}
         */
        this.m_lalcB = new b2Vec2;

     // property INITIALISATIONS

        this.m_localAnchorA.copy(ropeJointDef.localAnchorA);
        this.m_localAnchorB.copy(ropeJointDef.localAnchorB);

        /**
         *
         * @public
         * @property  m_maxLength
         * @type      {float}
         * @default   {b2ropeDef.maxLength}   0.0
         */
        this.m_maxLength = ropeJointDef.maxLength;

        /**
         *
         * @public
         * @property  m_frequencyHz
         * @type      {float}
         * @default   {b2ropeDef.frequencyHz}   0.0
         */
        this.m_frequencyHz = ropeJointDef.frequencyHz;

        /**
         *
         * @public
         * @property  m_bias
         * @type      {float}
         * @default   (0,0)
         */
        this.m_bias = 0.0;

        /**
         *
         * @public
         * @property  m_gamma
         * @type      {float}
         * @default   (0,0)
         */
        this.m_gamma = 0.0;

        /**
         *
         * @public
         * @property  m_impulse
         * @type      {float}
         * @default   0.0
         */
        this.m_impulse = 0.0;

        /**
         *
         * @protected
         * @property  m_state
         * @type      {float}
         * @default   b2Joint.e_inactiveLimit
         */
        this.m_limitState = b2Joint.e_inactiveLimit;

        /**
         *
         * @public
         * @property  m_length
         * @type      {float}
         * @default   {b2ropeDef.length}    1.0
         */
        this.m_length = ropeJointDef.length;

        /**
         *
         * @protected
         * @property  m_mass
         * @type      {float}
         * @default   0.0
         */
        this.m_mass = 0.0;

        /**
         *
         * @public
         * @property  m_maxLength
         * @type      {float}
         * @default   0.0
         */
        this.m_maxLength = ropeJointDef.maxLength;

        /**
         *
         * @public
         * @property  m_indexA
         * @type      {int}
         * @default   0
         */
        this.m_indexA = 0;

        /**
         *
         * @public
         * @property  m_indexB
         * @type      {int}
         * @default   0
         */
        this.m_indexB = 0;

        /**
         *
         * @public
         * @property  m_state
         * @type      {int}
         * @default   b2Joint.e_inactiveLimit
         */
        this.m_state = b2Joint.e_inactiveLimit;

        /**
         *
         * @public
         * @property  m_invMassB
         * @type      {int}
         * @default   0.0
         */
        this.m_invMassB = 0;

        /**
         *
         * @public
         * @property  m_invMassB
         * @type      {int}
         * @default   0
         * */
        this.m_invIA = 0;


////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                //
//                  ██       ██               ██  ██                                              //
//                  ██       ██                   ██                                              //
//                  ██ █████ █████ █████ ████ ██ █████ █████ █████ █████ █████                    //
//                  ██ ██ ██ ██ ██ ██ ██ ██   ██  ██      ██ ██ ██ ██    ██ ██                    //
//                  ██ ██ ██ ██ ██ █████ ██   ██  ██   █████ ██ ██ ██    █████                    //
//                  ██ ██ ██ ██ ██ ██    ██   ██  ██   ██ ██ ██ ██ ██    ██                       //
//                  ██ ██ ██ ██ ██ █████ ██   ██  ████ █████ ██ ██ █████ █████                    //
//                                                                                                //
////////////////////////////////////////////////////////////////////////////////////////////////////

    } b2RopeJoint.prototype = p = new b2Joint; Box2D.b2RopeJoint = b2RopeJoint;

      // STATIC CLASS PROPERTIES

         /**
          * Object pool for memory management.
          */
         b2RopeJoint._B2VEC2_POOL0 = new b2Vec2;
         b2RopeJoint._B2VEC2_POOL1 = new b2Vec2;
         b2RopeJoint._B2VEC2_POOL2 = new b2Vec2;

 ////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                //
//                       ██   ██        ██   ██             ██                                    //
//                       ███ ███        ██   ██             ██                                    //
//                       ███████ █████ █████ █████ █████ █████ █████                              //
//                       ██ █ ██ ██ ██  ██   ██ ██ ██ ██ ██ ██ ██                                 //
//                       ██   ██ █████  ██   ██ ██ ██ ██ ██ ██ █████                              //
//                       ██   ██ ██     ██   ██ ██ ██ ██ ██ ██    ██                              //
//                       ██   ██ █████  ████ ██ ██ █████ █████ █████                              //
//                                                                                                //
////////////////////////////////////////////////////////////////////////////////////////////////////

  // INSTANCE METHODS

     /**
      * @public
      * @method  getAnchorA
      * @param   {b2Vec2|Object=}   [out=b2Vec2]   reusable object
      * @return  {b2Vec2|Object}    out
      */
     p.getAnchorA = function ( out ) {
         out = out || b2RopeJoint._B2VEC2_POOL0;
         return this.m_bodyA.getWorldPoint( this.m_localAnchorA, out );
     };

     /**
      * @public
      * @method  getAnchorB
      * @param   {b2Vec2|Object=}   [out=b2Vec2]   reusable object
      * @return  {b2Vec2|Object}    out
      */
     p.getAnchorB = function ( out ) {
         out = out || b2RopeJoint._B2VEC2_POOL0;
         return this.m_bodyB.getWorldPoint( this.m_localAnchorB, out );
     };

     /**
      * Get the reaction force given the inverse time step.
      * Unit is N.
      *
      * @public
      * @method  getReactionForce
      * @param   {float}     invDeltaTime
      * @param   {b2Vec2|Object=}   [out=b2Vec2]   reusable object
      * @return  {b2Vec2|Object}    out
      */
     p.getReactionForce = function ( invDeltaTime, out ) {
         out = out || b2RopeJoint._B2VEC2_POOL0;
         /*b2Vec2*/return b2Vec2.numTimes( (invDeltaTime * this.m_impulse), this.m_u, out );
     };

     /**
      * Get the reaction torque given the inverse time step.
      * Unit is N*m. This is always zero for a distance joint.
      *
      * @public
      * @method  getReactionTorque
      * @param   {float}     invDeltaTime
      * @return  {float}
      */
     p.getReactionTorque = function ( invDeltaTime ) {
         return 0;
     };

     /**
      * The local anchor point relative to bodyA's origin.
      *
      * @public
      * @method  getLocalAnchorA
      * @param   {b2Vec2|Object=}   [out=b2Vec2]   reusable object
      * @return  {b2Vec2|Object}    out
      */
     p.getLocalAnchorA = function ( out ) {
         out = out || b2RopeJoint._B2VEC2_POOL0;
         return out.copy( this.m_localAnchorA );
     };

     /**
      * The local anchor point relative to bodyB's origin.
      *
      * @public
      * @method  getLocalAnchorB
      * @param   {b2Vec2|Object=}   [out=b2Vec2]   reusable object
      * @return  {b2Vec2|Object}    out
      */
     p.getLocalAnchorB = function ( out ) {
         out = out || b2RopeJoint._B2VEC2_POOL0;
         return out.copy( this.m_localAnchorB );
     };

     /**
      * Set the maximum length of the rope.
      *
      * @public
      * @method  setMaxLength
      * @param   {float}     length
      * @return  {void}
      */
     p.setMaxLength = function ( length ) {
         this.m_maxLength = length;
     };

     /**
      * Get the maximum length of the rope.
      *
      * @public
      * @method  getMaxLength
      * @return  {float}
      */
     p.getMaxLength = function () {
         return this.m_maxLength;
     };

     /**
      * @public
      * @method  getLimitState
      * @return  {int}
      */
     p.getLimitState = function () {
         return this.m_state;
     };

     /**
      * @public
      * @override
      * @method  initVelocityConstraints
      * @param   {b2SolverData} data
      * @return  {void}
      */
     // Implement b2Joint.initVelocityConstraints
     p.initVelocityConstraints = function ( data ) {
        this.m_indexA = this.m_bodyA.m_islandIndex;
        this.m_indexB = this.m_bodyB.m_islandIndex;
        this.m_localCenterA.copy(this.m_bodyA.m_sweep.localCenter);
        this.m_localCenterB.copy(this.m_bodyB.m_sweep.localCenter);
        this.m_invMassA = this.m_bodyA.m_invMass;
        this.m_invMassB = this.m_bodyB.m_invMass;
        this.m_invIA = this.m_bodyA.m_invI;
        this.m_invIB = this.m_bodyB.m_invI;

        /*b2Vec2&*/ var cA = data.positions[this.m_indexA].c;
        /*float32*/ var aA = data.positions[this.m_indexA].a;
        /*b2Vec2&*/ var vA = data.velocities[this.m_indexA].v;
        /*float32*/ var wA = data.velocities[this.m_indexA].w;

        /*b2Vec2&*/ var cB = data.positions[this.m_indexB].c;
        /*float32*/ var aB = data.positions[this.m_indexB].a;
        /*b2Vec2&*/ var vB = data.velocities[this.m_indexB].v;
        /*float32*/ var wB = data.velocities[this.m_indexB].w;

        /*b2Rot*/ var qA = this.m_qA.setAngle(aA), qB = this.m_qB.setAngle(aB);

        // this.m_rA = b2Mul(qA, this.m_localAnchorA - this.m_localCenterA);
        b2Vec2.subtract(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
        b2Rot.timesV2(qA, this.m_lalcA, this.m_rA);
        // this.m_rB = b2Mul(qB, this.m_localAnchorB - this.m_localCenterB);
        b2Vec2.subtract(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
        b2Rot.timesV2(qB, this.m_lalcB, this.m_rB);
        // this.m_u = cB + this.m_rB - cA - this.m_rA;
        this.m_u.copy(cB).plus(this.m_rB).minus(cA).minus(this.m_rA);

        this.m_length = this.m_u.length();

         /*float32*/ var C = this.m_length - this.m_maxLength;
         if ( C > 0 ) {
             this.m_state = b2Joint.e_atUpperLimit;
         }
         else {
             this.m_state = b2Joint.e_inactiveLimit;
         }

         if ( this.m_length > b2Settings.b2_linearSlop ) {
             this.m_u.times( 1 / this.m_length );
         }
         else {
             this.m_u.setZero();
             this.m_mass = 0;
             this.m_impulse = 0;
             return;
         }

        // Compute effective mass.
        /*float32*/ var crA = b2Vec2.cross(this.m_rA, this.m_u);
        /*float32*/ var crB = b2Vec2.cross(this.m_rB, this.m_u);
        /*float32*/ var invMass = this.m_invMassA + this.m_invIA * crA * crA + this.m_invMassB + this.m_invIB * crB * crB;

        this.m_mass = invMass !== 0 ? 1 / invMass : 0;

         if ( data.step.warmStarting ) {
            // Scale the impulse to support a variable time step.
            this.m_impulse *= data.step.dtRatio;

            // b2Vec2 P = m_impulse * m_u;
            var P = b2Vec2.numTimes(this.m_impulse, this.m_u, b2RopeJoint._B2VEC2_POOL0);
            // vA -= m_invMassA * P;
            vA.minusEqualsMul(this.m_invMassA, P);
            wA -= this.m_invIA * b2Vec2.cross(this.m_rA, P);
            // vB += m_invMassB * P;
            vB.plusEqualsMul(this.m_invMassB, P);
            wB += this.m_invIB * b2Vec2.cross(this.m_rB, P);
         }
         else {
             this.m_impulse = 0;
         }

        // data.velocities[this.m_indexA].v = vA;
        data.velocities[this.m_indexA].w = wA;
        // data.velocities[this.m_indexB].v = vB;
        data.velocities[this.m_indexB].w = wB;
     };

     /**
      *
      * @public
      * @override
      * @method  solveVelocityConstraints
      * @param   {b2SolverData} data
      * @return  {void}
      */
     // Implement b2Joint.solveVelocityConstraints
     p.solveVelocityConstraints = function ( data ) {
        /*b2Vec2&*/ var vA = data.velocities[this.m_indexA].v;
        /*float32*/ var wA = data.velocities[this.m_indexA].w;
        /*b2Vec2&*/ var vB = data.velocities[this.m_indexB].v;
        /*float32*/ var wB = data.velocities[this.m_indexB].w;

        // Cdot = dot(u, v + cross(w, r))
        // b2Vec2 vpA = vA + b2Cross(wA, m_rA);
        var vpA = b2Vec2.vPlusCrossFV(vA, wA, this.m_rA, b2RopeJoint._B2VEC2_POOL0);
        // b2Vec2 vpB = vB + b2Cross(wB, m_rB);
        var vpB = b2Vec2.vPlusCrossFV(vB, wB, this.m_rB, b2RopeJoint._B2VEC2_POOL1);
        // float32 C = m_length - m_maxLength;
        /*float32*/ var C = this.m_length - this.m_maxLength;
        // float32 Cdot = b2Dot(m_u, vpB - vpA);
        /*float32*/ var Cdot = b2Vec2.dot(this.m_u, b2Vec2.subtract(vpB, vpA, b2Vec2.POOL0));

        // Predictive constraint.
        /*float32*/ var impulse = -this.m_mass * Cdot;
        /*float32*/ var oldImpulse = this.m_impulse;
        this.m_impulse = Math.min(0, this.m_impulse + impulse);
        impulse = this.m_impulse - oldImpulse;

        // b2Vec2 P = impulse * m_u;
        var P = b2Vec2.numTimes(impulse, this.m_u, b2RopeJoint._B2VEC2_POOL2);
        // vA -= m_invMassA * P;
        vA.minusEqualsMul(this.m_invMassA, P);
        wA -= this.m_invIA * b2Vec2.cross(this.m_rA, P);
        // vB += m_invMassB * P;
        vB.plusEqualsMul(this.m_invMassB, P);
        wB += this.m_invIB * b2Vec2.cross(this.m_rB, P);

        // data.velocities[this.m_indexA].v = vA;
        data.velocities[this.m_indexA].w = wA;
        // data.velocities[this.m_indexB].v = vB;
        data.velocities[this.m_indexB].w = wB;
     };

     /**
      * @public
      * @override
      * @method  solvePositionConstraints
      * @param   {b2SolverData} data
      * @return  {void}
      */
     // Implement b2Joint.solvePositionConstraints
     p.solvePositionConstraints = function ( data ) {
         /*b2Vec2&*/ var cA = data.positions[this.m_indexA].c;
         /*float32*/ var aA = data.positions[this.m_indexA].a;
         /*b2Vec2&*/ var cB = data.positions[this.m_indexB].c;
         /*float32*/ var aB = data.positions[this.m_indexB].a;

         /*b2Rot*/ var qA = this.m_qA.setAngle(aA), qB = this.m_qB.setAngle(aB);

         //	b2Vec2 rA = b2Mul(qA, this.m_localAnchorA - this.m_localCenterA);
         b2Vec2.subtract(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
         var rA = b2Rot.timesV2(qA, this.m_lalcA, this.m_rA);
         //	b2Vec2 rB = b2Mul(qB, this.m_localAnchorB - this.m_localCenterB);
         b2Vec2.subtract(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
         var rB = b2Rot.timesV2(qB, this.m_lalcB, this.m_rB);
         //	b2Vec2 u = cB + rB - cA - rA;
         /*b2Vec2*/ var u = this.m_u.copy(cB).plus(rB).minus(cA).minus(rA);

         /*float32*/ var length = u.norm();
         /*float32*/ var C = length - this.m_maxLength;

         C = b2Math.clamp(C, 0, b2Settings.b2_maxLinearCorrection);

         /*float32*/ var impulse = -this.m_mass * C;
         //	b2Vec2 P = impulse * u;
         var P = b2Vec2.numTimes(impulse, u, b2RopeJoint._B2VEC2_POOL0);

         //	cA -= m_invMassA * P;
         cA.minusEqualsMul(this.m_invMassA, P);
         aA -= this.m_invIA * b2Vec2.cross(rA, P);
         //	cB += m_invMassB * P;
         cB.plusEqualsMul(this.m_invMassB, P);
         aB += this.m_invIB * b2Vec2.cross(rB, P);

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

         return length - this.m_maxLength < b2Settings.b2_linearSlop;
     };