//################################################################################################//
//################################################################################################//
// //
// ██ ██████ ██ ██ ██ ██ ██ ██ █████ ████ //
// ██ ██ ███ ███ ██ ██ ██ ██ ██ ██ //
// █████ ██ ███████ █████ █████ █████ ████ ██ █████ ██ █████ █████ ██ ██ █████ ██ //
// ██ ██ ██████ ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ █████ //
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ █████ ██ //
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ //
// █████ ██████ ██ ██ █████ ████ █████ ██ █████ █████ ██ ██ ██ ████ █████ █████ ██ //
// //
//################################################################################################//
//################################################################################################//
// CLASS CONSTRUCTOR
/**
* A motor joint is used to control the relative motion between
* two bodies. A typical usage is to control the movement of a
* dynamic body with respect to the ground.
*
* @class b2MotorJoint
* @constructor
* @param {b2MotorJointDef} motorJointDef
* @extends {b2Joint}
* @module Joints
*/
//TODO: test b2MotorJoint. Ported but not yet tested.
function b2MotorJoint( motorJointDef ) {
/**
*Invoke parent class constructor function reference.
*/
this.constructor( motorJointDef );
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// ██████ ██ ██ //
// ██ ██ ██ //
// ██ ██ ████ █████ █████ █████ ████ █████ ██ █████ █████ //
// ██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ //
// ██ ██ ██ ██ ██ ██ █████ ██ ██ ██ █████ █████ //
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ //
// ██ ██ █████ █████ █████ ██ ████ ██ █████ █████ //
// ██ //
// ██ //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
// property DECLARATIONS
/**
* @public
* @property m_linearOffset
* @type {b2Vec2}
*/
this.m_linearOffset = new b2Vec2;
/**
* @public
* @property m_linearImpulse
* @type {b2Vec2}
*/
this.m_linearImpulse = 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_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_linearError
* @type {b2Vec2}
*/
this.m_linearError = new b2Vec2;
/**
* @public
* @property m_linearMass
* @type {b2Mat22}
*/
this.m_linearMass = new b2Mat22;
/**
* @public
* @property m_qA
* @type {b2Rot|null}
*/
this.m_qA = new b2Rot;
/**
* @public
* @property m_qB
* @type {b2Rot|null}
*/
this.m_qB = new b2Rot;
/**
* @public
* @property m_K
* @type {b2Mat22}
*/
this.m_K = new b2Mat22;
// property INITIALISATIONS
/**
* @public
* @property m_maxForce
* @type {float}
* @default motorJointDef.maxForce.
*/
this.m_maxForce = motorJointDef.maxForce;
/**
* @public
* @property m_maxTorque
* @type {float}
*/
this.m_maxTorque = motorJointDef.maxTorque;
/**
* @public
* @property m_correctionFactor
* @type {float}
*/
this.m_correctionFactor = motorJointDef.correctionFactor;
/**
* @public
* @property m_type
* @type {int}
* @default 0.0
*/
// @this.constructor() above.
/**
*
* @public
* @property m_angularImpulse
* @type {float}
* @default 0.0
*/
this.m_angularImpulse = 0.0;
/**
* @public
* @property m_angularError
* @type {float}
* @default 0.0
*/
this.m_angularError = 0.0;
/**
*
* @public
* @property m_angularOffset
* @type {float}
* @default 0.0
*/
this.m_angularOffset = 0.0;
/**
*
* @public
* @property m_indexA
* @type {int}
*/
this.m_indexA = 0;
/**
*
* @public
* @property m_motorSpeed
* @type {int}
*/
this.m_indexB = 0;
/**
* @public
* @property m_rA
* @type {b2Vec2|null}
*/
this.m_rA = null;
/**
* @public
* @property m_rB
* @type {b2Vec2|null}
*/
this.m_rB = null;
/**
* @public
* @property m_invMassA
* @type {float}
*/
this.m_invMassA = 0.0;
/**
* @public
* @property m_invMassB
* @type {float}
*/
this.m_invMassB = 0.0;
/**
* @public
* @property m_invIA
* @type {float}
*/
this.m_invIA = 0.0;
/**
* @public
* @property m_invIB
* @type {float}
*/
this.m_invIB = 0.0;
/**
* @public
* @property m_angularMass
* @type {float}
*/
this.m_angularMass = 0.0;
////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// ██ ██ ██ ██ //
// ██ ██ ██ //
// ██ █████ █████ █████ ████ ██ █████ █████ █████ █████ █████ //
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ //
// ██ ██ ██ ██ ██ █████ ██ ██ ██ █████ ██ ██ ██ █████ //
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ //
// ██ ██ ██ ██ ██ █████ ██ ██ ████ █████ ██ ██ █████ █████ //
// //
////////////////////////////////////////////////////////////////////////////////////////////////////
} b2MotorJoint.prototype = p = new b2Joint(); Box2D.b2MotorJoint = b2MotorJoint;
// STATIC CLASS PROPERTIES
/**
* Object pool for memory management.
*/
b2MotorJoint._B2VEC2_POOL0 = new b2Vec2;
b2MotorJoint._B2VEC2_POOL1 = new b2Vec2;
b2MotorJoint._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 || b2MotorJoint._B2VEC2_POOL0;
return this.m_bodyA.getPosition( out );
};
/**
* @public
* @method getAnchorB
* @param {b2Vec2|Object=} [out=b2Vec2] reusable object
* @return {b2Vec2|Object} out
*/
p.getAnchorB = function ( out ) {
out = out || b2MotorJoint._B2VEC2_POOL0;
return this.m_bodyB.getPosition( 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 || b2MotorJoint._B2VEC2_POOL0;
return b2Vec2.numTimes( invDeltaTime, this.m_linearImpulse, 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 invDeltaTime * this.m_angularImpulse;
};
/**
* Set the position correction factor in the range [0,1].
*
* @public
* @method setCorrectionFactor
* @param {float} factor
* @return {float}
*/
p.setCorrectionFactor = function ( factor ) {
/*##if EXPORT_ASSERTS */
if ( b2Settings.ASSERTS_ENABLED ) {
b2Assert( isFinite( factor ) && 0.0 <= factor && factor <= 1.0 )
}/*#*/
this.m_correctionFactor = factor;
};
/**
* Get the position correction factor in the range [0,1].
*
* @public
* @method getCorrectionFactor
* @param {float} factor
* @return {float}
*/
p.getCorrectionFactor = function ( factor ) {
/*##if EXPORT_ASSERTS */
if ( b2Settings.ASSERTS_ENABLED ) {
b2Assert( isFinite( factor ) && 0.0 <= factor && factor <= 1.0 )
}/*#*/
this.m_correctionFactor = factor;
};
/**
* Set the target linear offset, in frame A, in meters.
*
* @public
* @method setLinearOffset
* @param {float} linearOffset
* @return {float}
*/
p.setLinearOffset = function ( linearOffset ) {
if ( linearOffset.x != this.m_linearOffset.x || linearOffset.y != this.m_linearOffset.y ) {
this.m_bodyA.setAwake( true );
this.m_bodyB.setAwake( true );
this.m_linearOffset.copy( linearOffset );
}
};
/**
* Get the target linear offset, in frame A, in meters.
*
* @public
* @method getLinearOffset
* @param {b2Vec2|Object=} [out=b2Vec2] reusable object
* @return {b2Vec2|Object} out
*/
p.getLinearOffset = function ( out ) {
return out.copy( this.m_linearOffset );
};
/**
* Set the target angular offset, in radians.
*
* @public
* @method setAngularOffset
* @param {float} angularOffset
* @return {float}
*/
p.setAngularOffset = function ( angularOffset ) {
if ( angularOffset !== this.m_angularOffset ) {
this.m_bodyA.setAwake( true );
this.m_bodyB.setAwake( true );
this.m_angularOffset = angularOffset;
}
};
/**
* Get the target angular offset, in radians.
*
* @public
* @method getAngularOffset
* @param {b2Vec2|Object=} [out=b2Vec2] reusable object
* @return {b2Vec2|Object} out
*/
p.getAngularOffset = function ( out ) {
return out.copy( this.m_linearOffset );
};
/**
* Set the maximum friction force in N.
*
* @public
* @method setMaxForce
* @param {float} force
* @return {float}
*/
p.setMaxForce = function ( force ) {
/*##if EXPORT_ASSERTS */
if ( b2Settings.ASSERTS_ENABLED ) {
b2Assert( isFinite( force ) && force >= 0 );
}/*#*/
this.m_maxForce = force;
};
/**
* Get the maximum friction force in N.
*
* @public
* @method getMaxForce
* @return {float}
*/
p.getMaxForce = function ( out ) {
return this.m_maxForce;
};
/**
* Set the maximum friction torque in N*m.
*
* @public
* @method setMaxForce
* @param {float} torque
* @return {float}
*/
p.setMaxForce = function ( torque ) {
/*##if EXPORT_ASSERTS */
if ( b2Settings.ASSERTS_ENABLED ) {
b2Assert( isFinite( torque ) && torque >= 0 );
}/*#*/
this.m_maxTorque = torque;
};
/**
* Get the maximum friction torque in N*m.
*
* @public
* @method getMaxForce
* @return {float}
*/
p.getMaxTorque = function () {
return this.m_maxTorque;
};
/**
* @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);
// Compute the effective mass matrix.
// this.m_rA = b2Mul(qA, -this.m_localCenterA);
var rA = b2Rot.timesV2(qA, b2Vec2.POOL0.copy(this.m_localCenterA).negative(), this.m_rA);
// this.m_rB = b2Mul(qB, -this.m_localCenterB);
var rB = b2Rot.timesV2(qB, b2Vec2.POOL0.copy(this.m_localCenterB).negative(), this.m_rB);
// J = [-I -r1_skew I r2_skew]
// [ 0 -1 0 1]
// r_skew = [-ry; rx]
// Matlab
// K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB]
// [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB]
// [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB]
/*float32*/ var mA = this.m_invMassA, mB = this.m_invMassB;
/*float32*/ var iA = this.m_invIA, iB = this.m_invIB;
/*b2Mat22*/ var K = this.m_K;
K.ex.x = mA + mB + iA * rA.y * rA.y + iB * rB.y * rB.y;
K.ex.y = -iA * rA.x * rA.y - iB * rB.x * rB.y;
K.ey.x = K.ex.y;
K.ey.y = mA + mB + iA * rA.x * rA.x + iB * rB.x * rB.x;
// this.m_linearMass = K.getInverse();
K.getInverse(this.m_linearMass);
this.m_angularMass = iA + iB;
if (this.m_angularMass > 0) {
this.m_angularMass = 1 / this.m_angularMass;
}
// this.m_linearError = cB + rB - cA - rA - b2Mul(qA, this.m_linearOffset);
b2Vec2.subtract(
b2Vec2.subtract(
b2Vec2.add(cB, rB, b2Vec2.POOL0),
b2Vec2.add(cA, rA, b2Vec2.POOL1),
b2Vec2.POOL2),
b2Rot.timesV2(qA, this.m_linearOffset, b2Vec2.POOL3),
this.m_linearError);
this.m_angularError = aB - aA - this.m_angularOffset;
if (data.step.warmStarting) {
// Scale impulses to support a variable time step.
// this.m_linearImpulse *= data.step.dtRatio;
this.m_linearImpulse.times(data.step.dtRatio);
this.m_angularImpulse *= data.step.dtRatio;
// b2Vec2 P(this.m_linearImpulse.x, this.m_linearImpulse.y);
var P = this.m_linearImpulse;
// vA -= mA * P;
vA.minusEqualsMul(mA, P);
wA -= iA * (b2Vec2.cross(rA, P) + this.m_angularImpulse);
// vB += mB * P;
vB.plusEqualsMul(mB, P);
wB += iB * (b2Vec2.cross(rB, P) + this.m_angularImpulse);
}
else {
this.m_linearImpulse.setZero();
this.m_angularImpulse = 0;
}
// data.velocities[this.m_indexA].v = vA; // vA is a reference
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB; // vB is a reference
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;
/*float32*/ var mA = this.m_invMassA, mB = this.m_invMassB;
/*float32*/ var iA = this.m_invIA, iB = this.m_invIB;
/*float32*/ var h = data.step.dt;
/*float32*/ var inv_h = data.step.inv_dt;
// Solve angular friction
{
/*float*/ var Cdot = wB - wA + inv_h * this.m_correctionFactor * this.m_angularError;
/*float*/ var impulse = -this.m_angularMass * Cdot;
/*float*/ var oldImpulse = this.m_angularImpulse;
/*float*/ var maxImpulse = h * this.m_maxTorque;
this.m_angularImpulse = b2Math.clamp(this.m_angularImpulse + impulse, -maxImpulse, maxImpulse);
impulse = this.m_angularImpulse - oldImpulse;
wA -= iA * impulse;
wB += iB * impulse;
}
// Solve linear friction
{
var rA = this.m_rA;
var rB = this.m_rB;
// b2Vec2 Cdot = vB + b2CrossSV(wB, rB) - vA - b2CrossSV(wA, rA) + inv_h * this.m_correctionFactor * this.m_linearError;
Cdot =
b2Vec2.add(
b2Vec2.subtract(
b2Vec2.add(vB, b2Vec2.numCross(wB, rB, b2Vec2.POOL0), b2Vec2.POOL0),
b2Vec2.add(vA, b2Vec2.numCross(wA, rA, b2Vec2.POOL1), b2Vec2.POOL1), b2Vec2.POOL2),
b2Vec2.numTimes(inv_h * this.m_correctionFactor, this.m_linearError, b2Vec2.POOL3),
b2MotorJoint._B2VEC2_POOL0);
// b2Vec2 impulse = -b2Mul(this.m_linearMass, Cdot);
impulse = b2Mat22.timesV2( this.m_linearMass, Cdot, b2MotorJoint._B2VEC2_POOL1 ).negative();
// b2Vec2 oldImpulse = this.m_linearImpulse;
oldImpulse = b2MotorJoint._B2VEC2_POOL2.copy( this.m_linearImpulse );
// this.m_linearImpulse += impulse;
this.m_linearImpulse.plus( impulse );
maxImpulse = h * this.m_maxForce;
if ( this.m_linearImpulse.lengthSquared() > maxImpulse * maxImpulse ) {
this.m_linearImpulse.norm();
// this.m_linearImpulse *= maxImpulse;
this.m_linearImpulse.times( maxImpulse );
}
// impulse = this.m_linearImpulse - oldImpulse;
b2Vec2.subtract(this.m_linearImpulse, oldImpulse, impulse);
// vA -= mA * impulse;
vA.minusEqualsMul(mA, impulse);
// wA -= iA * b2CrossVV(rA, impulse);
wA -= iA * b2Vec2.cross(rA, impulse);
// vB += mB * impulse;
vB.plusEqualsMul(mB, impulse);
// wB += iB * b2CrossVV(rB, impulse);
wB += iB * b2Vec2.cross(rB, impulse);
}
// data.velocities[this.m_indexA].v = vA; // vA is a reference
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB; // vB is a reference
data.velocities[this.m_indexB].w = wB;
};
/**
* @public
* @override
* @method solvePositionConstraints
* @param {b2SolverData} data
* @return {void}
*/
// Implement b2Joint.solvePositionConstraints
p.solvePositionConstraints = function ( data ) {
return true;
};