API Documentation for:
Show:

File:addBox2D\Dynamics\Joints\b2AreaJoint.js

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

 // CLASS CONSTRUCTOR

    /**
     * Definition for a b2AreaJoint, which connects a
     * group of bodies together so they maintain a constant area
     * within them as the group distorts in reaction to collisions..
     *
     * @class   b2AreaJoint
     * @constructor
     * @extends {b2Joint}
     * @param   {b2AreaJointDef}  areaJointDef
     * @module  Joints
     */
    // TODO: test b2AreaJoint. Ported but untested.
    function b2AreaJoint( areaJointDef ) {

        /**
         * Invokes parent class constructor function reference.
         */
        this.constructor( areaJointDef );
        /*##if EXPORT_ASSERTS */
        if ( b2Settings.ASSERTS_ENABLED ) {
            b2Assert( areaJointDef.bodies.length >= 3, "You cannot create an area joint with less than three bodies." );
        }/*#*/

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

     // property DECLARATIONS

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

        /**
         * @public
         * @property  m_deltas
         * @type      {Array.<b2Vec2>}
         */
        this.m_deltas = b2Vec2.newVector( areaJointDef.bodies.length );

        /**
         * @public
         * @property  m_joints
         * @type      {Array.<b2Vec2>}
         */
        this.m_joints = new Array( areaJointDef.bodies.length );

        /**
         *
         * @public
         * @property  m_normals
         * @type      {Array.<b2Vec2>}
         */
        this.m_normals = b2Vec2.newVector( areaJointDef.bodies.length );

        /**
         *
         * @public
         * @property  m_targetLengths
         * @type      {Array}
         */
        this.m_targetLengths = newNumberVector( areaJointDef.bodies.length );

     // property INITIALISATIONS

        /**
         *
         * @public
         * @property  m_bodies
         * @type      {Array.<b2Body>}
         */
        this.m_bodies = areaJointDef.bodies;

        /**
         * @public
         * @property  m_dampingRatio
         * @type      {float}
         */
        this.m_dampingRatio = areaJointDef.dampingRatio;

        /**
         * @public
         * @property  m_frequencyHz
         * @type      {float}
         */
        this.m_frequencyHz = areaJointDef.frequencyHz;

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

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

     // constructor LOGIC

        var djd = new b2DistanceJointDef();
        djd.frequencyHz = areaJointDef.frequencyHz;
        djd.dampingRatio = areaJointDef.dampingRatio;


        for (var i = 0, ict = this.m_bodies.length; i < ict; ++i) {
            var body = this.m_bodies[i];
            var next = this.m_bodies[(i + 1) % ict];

            var body_c = body.getWorldCenter();
            var next_c = next.getWorldCenter();

            this.m_targetLengths[i] = b2Vec2.distance( body_c, next_c );

            this.m_targetArea += b2Vec2.cross( body_c, next_c );

            djd.initialize( body, next, body_c, next_c );
            this.m_joints[i] = areaJointDef.world.createJoint( djd );
        }

        this.m_targetArea *= 0.5;

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

    } b2AreaJoint.prototype = p = new b2Joint(); Box2D.b2AreaJoint = b2AreaJoint;

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

 // INSTANCE METHODS

    /**
     * Get the reaction force given the inverse time step.
     * Unit is Newtons.
     *
     * @public
     * @override
     * @method  getReactionForce
     * @return {b2Vec2}
     * @param {number} invDeltaTime
     * @param {b2Vec2} out
     */
    p.getReactionForce = function ( invDeltaTime, out ) {
        return out.setZero();
    };

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

    /**
     * Set frequency in Hz.
     *
     * @public
     * @method  setFrequency
     * @param   {float}    hz
     * @return  {void}
     */
    p.setFrequency = function ( hz ) {
        this.m_frequencyHz = hz;
        for (var i = 0, ict = this.m_joints.length; i < ict; ++i) {
            this.m_joints[i].setFrequency( hz );
        }
    };

    /**
     * Get frequency in Hz.
     *
     * @public
     * @method  setFrequency
     * @return  {float}
     */
    p.getFrequency = function () {
        return this.m_frequencyHz;
    };

    /**
     * Set damping ratio.
     *
     * @public
     * @method  setDampingRatio
     * @param   {float}    ratio
     * @return  {void}
     */
    p.setDampingRatio = function ( ratio ) {
        this.m_dampingRatio = ratio;

        for (var i = 0, ict = this.m_joints.length; i < ict; ++i) {
            this.m_joints[i].setDampingRatio( ratio );
        }
    };

    /**
     * Get damping ratio.
     *
     * @public
     * @method  getDampingRatio
     * @return  {float}
     */
    p.getDampingRatio = function () {
        return this.m_dampingRatio;
    };

//    /**
//     *
//     * @public
//     * @method  getBodies
//     * @return  {}
//     */
//    p.getBodies = function () {
//        return this.m_bodies;
//    };
//
//    /**
//     *
//     * @public
//     * @method  inflate
//     * @return  {}
//     */
//    p.inflate = function ( factor ) {
//        this.m_targetVolume *= factor;
//    };
//
//    /**
//     *
//     * @public
//     * @method  getArea
//     * @return  {}
//     */
//    p.getArea = function () {
//        var area = 0;
//        area += this.m_bodies[this.m_bodies.length - 1].getWorldCenter().x * this.m_bodies[0].getWorldCenter().y - this.m_bodies[0].getWorldCenter().x * this.m_bodies[this.m_bodies.length - 1].getWorldCenter().y;
//
//        for (var i = 0, length = this.m_bodies.length; i < length - 1; ++i) {
//            area += this.m_bodies[i].getWorldCenter().x * this.m_bodies[i + 1].getWorldCenter().y - this.m_bodies[i + 1].getWorldCenter().x * this.m_bodies[i].getWorldCenter().y;
//        }
//
//        area *= 0.5;
//        return area;
//    };
//
//    /**
//     *
//     * @public
//     * @method  constrainEdges
//     * @return  {}
//     */
//    p.constrainEdges = function ( step ) {
//        var perimeter = 0;
//
//        for (var i = 0, length = this.m_bodies.length; i < length; ++i) {
//            var next = (i == this.m_bodies.length - 1 ? 0 : i + 1);
//            var dx = this.m_bodies[next].getWorldCenter().x - this.m_bodies[i].getWorldCenter().x;
//            var dy = this.m_bodies[next].getWorldCenter().y - this.m_bodies[i].getWorldCenter().y;
//            var dist = Math.sqrt( dx * dx + dy * dy );
//
//            if ( dist < b2Settings.b2_linearSleepTolerance ) {
//                dist = 1;
//            }
//
//            this.m_normals[i].x = dy / dist;
//            this.m_normals[i].y = -dx / dist;
//            perimeter += dist;
//        }
//
//        var deltaArea = this.m_targetVolume - this.getArea();
//        var toExtrude = 0.5 * deltaArea / perimeter;
//        var done = true;
//
//        for (i = 0, length = this.m_bodies.length; i < length; ++i) {
//            var next = (i == this.m_bodies.length - 1 ? 0 : i + 1);
//
//            var delta = new b2Vec2(
//                    toExtrude * (this.m_normals[i].x + this.m_normals[next].x),
//                    toExtrude * (this.m_normals[i].y + this.m_normals[next].y)
//            );
//
//            var norm = delta.length();
//
//            if ( norm > b2Settings.b2_maxLinearCorrection ) {
//                delta.multiply( b2Settings.b2_maxLinearCorrection / norm );
//            }
//
//            if ( norm > b2Settings.b2_linearSlop ) {
//                done = false;
//            }
//
//            this.m_bodies[next].m_sweep.c.x += delta.x;
//            this.m_bodies[next].m_sweep.c.y += delta.y;
//            this.m_bodies[next].synchronizeTransform();
//        }
//
//        return done;
//    };

    /**
     * @public
     * @override
     * @method  initVelocityConstraints
     * @param   {b2SolverData} data
     * @return  {void}
     */
    // Implement b2Joint.initVelocityConstraints
    p.initVelocityConstraints = function ( data ) {
        for (var i = 0, ict = this.m_bodies.length; i < ict; ++i) {
            var prev = this.m_bodies[(i + ict - 1) % ict];
            var next = this.m_bodies[(i + 1) % ict];
            var prev_c = data.positions[prev.m_islandIndex].c;
            var next_c = data.positions[next.m_islandIndex].c;
            var delta = this.m_deltas[i];

            b2Vec2.subtract( next_c, prev_c, delta );
        }

        if ( data.step.warmStarting ) {
            this.m_impulse *= data.step.dtRatio;

            for (var i = 0, ict = this.m_bodies.length; i < ict; ++i) {
                var body = this.m_bodies[i];
                var body_v = data.velocities[body.m_islandIndex].v;
                var delta = this.m_deltas[i];

                body_v.x += body.m_invMass * delta.y * 0.5 * this.m_impulse;
                body_v.y += body.m_invMass * -delta.x * 0.5 * this.m_impulse;
            }
        }
        else {
            this.m_impulse = 0;
        }
    };

    /**
     * @public
     * @override
     * @method  solvePositionConstraints
     * @param   {b2SolverData} data
     * @return  {void}
     */
    // Implement b2Joint.solvePositionConstraints
    p.solvePositionConstraints = function ( data ) {
        var perimeter = 0;
        var area = 0;

        for (var i = 0, ict = this.m_bodies.length; i < ict; ++i) {
            var body = this.m_bodies[i];
            var next = this.m_bodies[(i + 1) % ict];
            var body_c = data.positions[body.m_islandIndex].c;
            var next_c = data.positions[next.m_islandIndex].c;

            var delta = b2Vec2.subtract( next_c, body_c, this.m_delta );

            var dist = delta.length();
            if ( dist < b2Settings.b2_epsilon ) {
                dist = 1;
            }

            this.m_normals[i].x = delta.y / dist;
            this.m_normals[i].y = -delta.x / dist;

            perimeter += dist;

            area += b2Vec2.cross( body_c, next_c );
        }

        area *= 0.5;

        var deltaArea = this.m_targetArea - area;
        var toExtrude = 0.5 * deltaArea / perimeter;
        var done = true;

        for (var i = 0, ict = this.m_bodies.length; i < ict; ++i) {
            var body = this.m_bodies[i];
            var body_c = data.positions[body.m_islandIndex].c;
            var next_i = (i + 1) % ict;

            var delta = b2Vec2.add( this.m_normals[i], this.m_normals[next_i], this.m_delta );
            delta.times( toExtrude );

            var normSquared = delta.lengthSquared();
            if ( normSquared > ( this.m_radius * this.m_radius ) ) {
                delta.times( b2Settings.b2_maxLinearCorrection / Math.sqrt( normSquared ) );
            }
            if ( normSquared > ( this.m_radius * this.m_radius ) ) {
                done = false;
            }

            body_c.x += delta.x;
            body_c.y += delta.y;
        }

        return done;
    };

    /**
     * @public
     * @override
     * @method  solveVelocityConstraints
     * @param   {b2SolverData} data
     * @return  {void}
     */
    // Implement b2Joint.solveVelocityConstraints
    p.solveVelocityConstraints = function ( data ) {
        var dotMassSum = 0;
        var crossMassSum = 0;

        for (var i = 0, ict = this.m_bodies.length; i < ict; ++i) {
            var body = this.m_bodies[i];
            var body_v = data.velocities[body.m_islandIndex].v;
            var delta = this.m_deltas[i];

            dotMassSum += delta.lengthSquared() / body.getMass();
            crossMassSum += b2Vec2.cross( body_v, delta );
        }

        var lambda = -2 * crossMassSum / dotMassSum;
        //lambda = b2Math.clamp(lambda, -b2Settings.b2_maxLinearCorrection, b2Settings.b2_maxLinearCorrection);

        this.m_impulse += lambda;

        for (var i = 0, ict = this.m_bodies.length; i < ict; ++i) {
            var body = this.m_bodies[i];
            var body_v = data.velocities[body.m_islandIndex].v;
            var delta = this.m_deltas[i];

            body_v.x += body.m_invMass * delta.y * 0.5 * lambda;
            body_v.y += body.m_invMass * -delta.x * 0.5 * lambda;
        }
    };