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;
}
};