//################################################################################################//
//################################################################################################//
// //
// ██ ██████ ██████ ██ ██ ██ ██ ██ //
// ██ ██ ██ ██ ██ ██ ██ ██ //
// █████ ██ ██ ██ ██ ██ ██ ██ █████ ██ ██ ██ █████ ██ █████ █████ //
// ██ ██ ██████ ██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ //
// ██ ██ ██ ██ ██ ██ ██ ██ █████ ██ ██ ██ ██ ██ ██ ██ ██ ██ //
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ //
// █████ ██████ ██ █████ ██ ██ █████ █████ █████ █████ ██ ██ ██ ████ //
// ██ //
// █████ //
// //
//################################################################################################//
//################################################################################################//
// CLASS CONSTRUCTOR
/**
* The pulley joint is connected to two bodies and two fixed ground points.
* The pulley supports a ratio such that:
* lengthA + ratio * lengthB <= constant
* Yes, the force transmitted is scaled by the ratio.
*
* Warning: the pulley joint can get a bit squirrelly by itself.
* They often work better when combined with prismatic joints.
* You should also cover the the anchor points with static
* shapes to prevent one side from going to zero length.
*
* @class b2PulleyJoint
* @constructor
* @param {b2PulleyJointDef} pulleyJointDef
* @extends {b2Joint}
* @module Joints
*/
function b2PulleyJoint( pulleyJointDef ) {
/**
* Invokes parent class constructor function reference.
*/
this.constructor( pulleyJointDef );
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// ██████ ██ ██ //
// ██ ██ ██ //
// ██ ██ ████ █████ █████ █████ ████ █████ ██ █████ █████ //
// ██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ //
// ██ ██ ██ ██ ██ ██ █████ ██ ██ ██ █████ █████ //
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ //
// ██ ██ █████ █████ █████ ██ ████ ██ █████ █████ //
// ██ //
// ██ //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
// property DECLARATIONS
/**
*
* @public
* @property m_groundAnchorA
* @type {b2Vec2}
*/
this.m_groundAnchorA = new b2Vec2;
/**
*
* @public
* @property m_groundAnchorB
* @type {b2Vec2}
*/
this.m_groundAnchorB = new b2Vec2;
/**
*
* @public
* @property m_localAnchorA
* @type {b2Vec2}
*/
this.m_localAnchorA = new b2Vec2;
/**
*
* @public
* @property m_localAnchorB
* @type {b2Vec2}
*/
this.m_localAnchorB = new b2Vec2;
/**
*
* @public
* @property m_uA
* @type {b2Vec2}
*/
this.m_uA = new b2Vec2;
/**
*
* @public
* @property m_uB
* @type {b2Vec2}
*/
this.m_uB = 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_localCenterA
* @type {b2Vec2}
*/
this.m_localCenterA = new b2Vec2;
/**
*
* @public
* @property m_localCenterB
* @type {b2Vec2}
*/
this.m_localCenterB = new b2Vec2;
/**
*
* @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 {b2Rot}
*/
this.m_lalcA = new b2Vec2;
/**
*
* @public
* @property m_lalcB
* @type {b2Rot}
*/
this.m_lalcB = new b2Vec2;
// property INITIALISATIONS
this.m_groundAnchorA.x = pulleyJointDef.groundAnchorA.x;
this.m_groundAnchorA.y = pulleyJointDef.groundAnchorA.y;
this.m_groundAnchorB.x = pulleyJointDef.groundAnchorB.x;
this.m_groundAnchorB.y = pulleyJointDef.groundAnchorB.y;
this.m_localAnchorA.x = pulleyJointDef.localAnchorA.x;
this.m_localAnchorA.y = pulleyJointDef.localAnchorA.y;
this.m_localAnchorA.x = pulleyJointDef.localAnchorB.x;
this.m_localAnchorA.y = pulleyJointDef.localAnchorB.y;
/**
*
* @public
* @property m_lengthA
* @type {float}
*/
this.m_lengthA = pulleyJointDef.lengthA;
/**
*
* @public
* @property m_lengthB
* @type {float}
*/
this.m_lengthB = pulleyJointDef.lengthB;
/*##if EXPORT_ASSERTS */
if ( b2Settings.ASSERTS_ENABLED ) { b2Assert( pulleyJointDef.ratio !== 0 ); }
/*#end EXPORT_ASSERTS */
/**
*
* @public
* @property m_ratio
* @type {float}
*/
this.m_ratio = pulleyJointDef.ratio;
/**
*
* @public
* @property m_constant
* @type {float}
*/
this.m_constant = pulleyJointDef.lengthA + this.m_ratio * pulleyJointDef.lengthB;
/**
*
* @public
* @property m_impulse
* @type {float}
* @default 0.0
*/
this.m_impulse = 0.0;
/**
*
* @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_indexB
* @type {float}
* @default 0.0
*/
this.m_invMassA = 0.0;
/**
*
* @public
* @property m_indexB
* @type {float}
* @default 0.0
*/
this.m_invMassB = 0.0;
/**
*
* @public
* @property m_indexB
* @type {float}
* @default 0.0
*/
this.m_invIA = 0.0;
/**
*
* @public
* @property m_indexB
* @type {float}
* @default 0.0
*/
this.m_invIB = 0.0;
/**
*
* @public
* @property m_mass
* @type {float}
* @default 0.0
*/
this.m_mass = 0.0;
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// ██ ██ ██ ██ //
// ██ ██ ██ //
// ██ █████ █████ █████ ████ ██ █████ █████ █████ █████ █████ //
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ //
// ██ ██ ██ ██ ██ █████ ██ ██ ██ █████ ██ ██ ██ █████ //
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ //
// ██ ██ ██ ██ ██ █████ ██ ██ ████ █████ ██ ██ █████ █████ //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
} b2PulleyJoint.prototype = p = new b2Joint; Box2D.b2PulleyJoint = b2PulleyJoint;
// STATIC CLASS PROPERTIES
b2PulleyJoint.b2_minPulleyLength = 2.0;
/**
* Object pool for memory management.
*/
b2PulleyJoint._B2VEC2_POOL0 = new b2Vec2;
b2PulleyJoint._B2VEC2_POOL1 = new b2Vec2;
b2PulleyJoint._B2VEC2_POOL2 = new b2Vec2;
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// ██ ██ ██ ██ ██ //
// ███ ███ ██ ██ ██ //
// ███████ █████ █████ █████ █████ █████ █████ //
// ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ //
// ██ ██ █████ ██ ██ ██ ██ ██ ██ ██ █████ //
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ //
// ██ ██ █████ ████ ██ ██ █████ █████ █████ //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
// INSTANCE METHODS
/**
* @public
* @method getAnchorB
* @param {b2Vec2|Object=} [out=b2Vec2] reusable object
* @return {b2Vec2|Object} out
*/
p.getAnchorA = function ( out ) {
out = out || b2PulleyJoint._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 || b2PulleyJoint._B2VEC2_POOL0;
return this.m_bodyB.getWorldPoint( this.m_localAnchorB, out );
};
/**
* @public
* @method getGroundAnchorA
* @param {b2Vec2|Object=} [out=b2Vec2] reusable object
* @return {b2Vec2|Object} out
*/
p.getGroundAnchorA = function ( out ) {
out = out || b2PulleyJoint._B2VEC2_POOL0;
return out.copy( this.m_groundAnchorA );
};
/**
* @public
* @method getGroundAnchorB
* @param {b2Vec2|Object=} [out=b2Vec2] reusable object
* @return {b2Vec2|Object} out
*/
p.getGroundAnchorB = function ( out ) {
out = out || b2PulleyJoint._B2VEC2_POOL0;
return out.copy( this.m_groundAnchorB );
};
/**
* Get the current length of the segment attached to bodyA.
*
* @public
* @method getLengthA
* @return {float}
*/
p.getLengthA = function () {
return this.m_lengthA;
};
/**
* Get the current length of the segment attached to bodyB.
*
* @public
* @method getLengthB
* @return {float}
*/
p.getLengthB = function () {
return this.m_lengthB;
};
/**
* Get the current length of the segment attached to bodyA.
*
* @public
* @method getCurrentLengthA
* @return {float}
*/
p.getCurrentLengthA = function () {
var p = this.m_bodyA.getWorldPoint( this.m_localAnchorA, b2PulleyJoint._B2VEC2_POOL0 );
var s = this.m_groundAnchorA;
return b2Vec2.distance( p, s );
};
/**
* Get the current length of the segment attached to bodyB.
*
* @public
* @method getCurrentLengthB
* @return {float}
*/
p.getCurrentLengthB = function () {
var p = this.m_bodyB.getWorldPoint( this.m_localAnchorB, b2PulleyJoint._B2VEC2_POOL0 );
var s = this.m_groundAnchorB;
return b2Vec2.distance( p, s );
};
/**
* Get the pulley ratio.
*
* @public
* @method getRatio
* @return {float}
*/
p.getRatio = function () {
return this.m_ratio;
};
/**
* 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 || b2PulleyJoint._B2VEC2_POOL0;
return out.set( invDeltaTime * this.m_impulse * this.m_uB.x, invDeltaTime * this.m_impulse * this.m_uB.y );
};
/**
* 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.0;
};
/**
* @public
* @method shiftOrigin
* @param {b2Vec2} newOrigin
* @return {void}
*/
// Implement b2Joint.shiftOrigin
p.shiftOrigin = function ( newOrigin ) {
this.m_groundAnchorA.minus( newOrigin );
this.m_groundAnchorB.minus( newOrigin );
};
/**
* @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 qA(aA), qB(aB);
/*b2Rot*/ var qA = this.m_qA.setAngle(aA), qB = this.m_qB.setAngle(aB);
// m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
b2Vec2.subtract(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
b2Rot.timesV2(qA, this.m_lalcA, this.m_rA);
// m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
b2Vec2.subtract(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
b2Rot.timesV2(qB, this.m_lalcB, this.m_rB);
// Get the pulley axes.
// m_uA = cA + m_rA - m_groundAnchorA;
this.m_uA.copy(cA).plus(this.m_rA).minus(this.m_groundAnchorA);
// m_uB = cB + m_rB - m_groundAnchorB;
this.m_uB.copy(cB).plus(this.m_rB).minus(this.m_groundAnchorB);
/*float32*/ var lengthA = this.m_uA.length();
/*float32*/ var lengthB = this.m_uB.length();
if ( lengthA > 10 * b2Settings.b2_linearSlop ) {
this.m_uA.times( 1 / lengthA );
}
else {
this.m_uA.setZero();
}
if ( lengthB > 10 * b2Settings.b2_linearSlop ) {
this.m_uB.times( 1 / lengthB );
}
else {
this.m_uB.setZero();
}
// Compute effective mass.
/*float32*/ var ruA = b2Vec2.cross(this.m_rA, this.m_uA);
/*float32*/ var ruB = b2Vec2.cross(this.m_rB, this.m_uB);
/*float32*/ var mA = this.m_invMassA + this.m_invIA * ruA * ruA;
/*float32*/ var mB = this.m_invMassB + this.m_invIB * ruB * ruB;
this.m_mass = mA + this.m_ratio * this.m_ratio * mB;
if ( this.m_mass > 0 ) {
this.m_mass = 1 / this.m_mass;
}
if ( data.step.warmStarting ) {
// Scale impulses to support variable time steps.
this.m_impulse *= data.step.dtRatio;
// Warm starting.
// b2Vec2 PA = -(m_impulse) * m_uA;
var PA = b2Vec2.numTimes(-(this.m_impulse), this.m_uA, b2PulleyJoint._B2VEC2_POOL0 );
// b2Vec2 PB = (-m_ratio * m_impulse) * m_uB;
var PB = b2Vec2.numTimes((-this.m_ratio * this.m_impulse), this.m_uB, b2PulleyJoint._B2VEC2_POOL1 );
// vA += m_invMassA * PA;
vA.plusEqualsMul(this.m_invMassA, PA);
wA += this.m_invIA * b2Vec2.cross(this.m_rA, PA);
// vB += m_invMassB * PB;
vB.plusEqualsMul(this.m_invMassB, PB);
wB += this.m_invIB * b2Vec2.cross(this.m_rB, PB);
}
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;
// b2Vec2 vpA = vA + b2Cross(wA, m_rA);
var vpA = b2Vec2.vPlusCrossFV(vA, wA, this.m_rA, b2PulleyJoint._B2VEC2_POOL0);
// b2Vec2 vpB = vB + b2Cross(wB, m_rB);
var vpB = b2Vec2.vPlusCrossFV(vB, wB, this.m_rB, b2PulleyJoint._B2VEC2_POOL1);
/*float32*/ var Cdot = -b2Vec2.dot(this.m_uA, vpA) - this.m_ratio * b2Vec2.dot(this.m_uB, vpB);
/*float32*/ var impulse = -this.m_mass * Cdot;
this.m_impulse += impulse;
// b2Vec2 PA = -impulse * m_uA;
var PA = b2Vec2.numTimes(-impulse, this.m_uA, b2PulleyJoint._B2VEC2_POOL2);
// b2Vec2 PB = -m_ratio * impulse * m_uB;
var PB = b2Vec2.numTimes(-this.m_ratio * impulse, this.m_uB, b2PulleyJoint._B2VEC2_POOL3);
// vA += m_invMassA * PA;
vA.plusEqualsMul(this.m_invMassA, PA);
wA += this.m_invIA * b2Vec2.cross(this.m_rA, PA);
// vB += m_invMassB * PB;
vB.plusEqualsMul(this.m_invMassB, PB);
wB += this.m_invIB * b2Vec2.cross(this.m_rB, PB);
// 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 qA(aA), qB(aB);
/*b2Rot*/ var qA = this.m_qA.setAngle(aA), qB = this.m_qB.setAngle(aB);
// b2Vec2 rA = b2Mul(qA, m_localAnchorA - 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, m_localAnchorB - m_localCenterB);
b2Vec2.subtract(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
var rB = b2Rot.timesV2(qB, this.m_lalcB, this.m_rB);
// Get the pulley axes.
// b2Vec2 uA = cA + rA - m_groundAnchorA;
var uA = this.m_uA.copy(cA).plus(rA).minus(this.m_groundAnchorA);
// b2Vec2 uB = cB + rB - m_groundAnchorB;
var uB = this.m_uB.copy(cB).plus(rB).minus(this.m_groundAnchorB);
/*float32*/ var lengthA = uA.length();
/*float32*/ var lengthB = uB.length();
if ( lengthA > 10 * b2Settings.b2_linearSlop ) {
uA.times( 1 / lengthA );
}
else {
uA.setZero();
}
if ( lengthB > 10 * b2Settings.b2_linearSlop ) {
uB.times( 1 / lengthB );
}
else {
uB.setZero();
}
// Compute effective mass.
/*float32*/ var ruA = b2Vec2.cross(rA, uA);
/*float32*/ var ruB = b2Vec2.cross(rB, uB);
/*float32*/ var mA = this.m_invMassA + this.m_invIA * ruA * ruA;
/*float32*/ var mB = this.m_invMassB + this.m_invIB * ruB * ruB;
/*float32*/ var mass = mA + this.m_ratio * this.m_ratio * mB;
if ( mass > 0 ) {
mass = 1 / mass;
}
/*float32*/ var C = this.m_constant - lengthA - this.m_ratio * lengthB;
/*float32*/ var linearError = Math.abs(C);
/*float32*/ var impulse = -mass * C;
// b2Vec2 PA = -impulse * uA;
var PA = b2Vec2.numTimes(-impulse, uA, b2PulleyJoint._B2VEC2_POOL0);
// b2Vec2 PB = -m_ratio * impulse * uB;
var PB = b2Vec2.numTimes(-this.m_ratio * impulse, uB, b2PulleyJoint._B2VEC2_POOL1);
// cA += m_invMassA * PA;
cA.plusEqualsMul(this.m_invMassA, PA);
aA += this.m_invIA * b2Vec2.cross(rA, PA);
// cB += m_invMassB * PB;
cB.plusEqualsMul(this.m_invMassB, PB);
aB += this.m_invIB * b2Vec2.cross(rB, PB);
// 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 linearError < b2Settings.b2_linearSlop;
};