var vec2 = require('../math/vec2')
, decomp = require('poly-decomp')
, Convex = require('../shapes/Convex')
, RaycastResult = require('../collision/RaycastResult')
, Ray = require('../collision/Ray')
, AABB = require('../collision/AABB')
, EventEmitter = require('../events/EventEmitter');
module.exports = Body;
/**
* A rigid body. Has got a center of mass, position, velocity and a number of
* shapes that are used for collisions.
*
* @class Body
* @constructor
* @extends EventEmitter
* @param {Object} [options]
* @param {Array} [options.force]
* @param {Array} [options.position]
* @param {Array} [options.velocity]
* @param {Boolean} [options.allowSleep]
* @param {Boolean} [options.collisionResponse]
* @param {Number} [options.angle=0]
* @param {Number} [options.angularForce=0]
* @param {Number} [options.angularVelocity=0]
* @param {Number} [options.ccdIterations=10]
* @param {Number} [options.ccdSpeedThreshold=-1]
* @param {Number} [options.fixedRotation=false]
* @param {Number} [options.gravityScale]
* @param {Number} [options.id]
* @param {Number} [options.mass=0] A number >= 0. If zero, the .type will be set to Body.STATIC.
* @param {Number} [options.sleepSpeedLimit]
* @param {Number} [options.sleepTimeLimit]
*
* @example
*
* // Create a typical dynamic body
* var body = new Body({
* mass: 1,
* position: [0, 0],
* angle: 0,
* velocity: [0, 0],
* angularVelocity: 0
* });
*
* // Add a circular shape to the body
* body.addShape(new Circle({ radius: 1 }));
*
* // Add the body to the world
* world.addBody(body);
*/
function Body(options){
options = options || {};
EventEmitter.call(this);
/**
* The body identifyer
* @property id
* @type {Number}
*/
this.id = options.id || ++Body._idCounter;
/**
* The world that this body is added to. This property is set to NULL if the body is not added to any world.
* @property world
* @type {World}
*/
this.world = null;
/**
* The shapes of the body.
*
* @property shapes
* @type {Array}
*/
this.shapes = [];
/**
* The mass of the body.
* @property mass
* @type {number}
*/
this.mass = options.mass || 0;
/**
* The inverse mass of the body.
* @property invMass
* @type {number}
*/
this.invMass = 0;
/**
* The inertia of the body around the Z axis.
* @property inertia
* @type {number}
*/
this.inertia = 0;
/**
* The inverse inertia of the body.
* @property invInertia
* @type {number}
*/
this.invInertia = 0;
this.invMassSolve = 0;
this.invInertiaSolve = 0;
/**
* Set to true if you want to fix the rotation of the body.
* @property fixedRotation
* @type {Boolean}
*/
this.fixedRotation = !!options.fixedRotation;
/**
* Set to true if you want to fix the body movement along the X axis. The body will still be able to move along Y.
* @property {Boolean} fixedX
*/
this.fixedX = !!options.fixedX;
/**
* Set to true if you want to fix the body movement along the Y axis. The body will still be able to move along X.
* @property {Boolean} fixedY
*/
this.fixedY = !!options.fixedY;
/**
* @private
* @property {array} massMultiplier
*/
this.massMultiplier = vec2.create();
/**
* The position of the body
* @property position
* @type {Array}
*/
this.position = vec2.fromValues(0,0);
if(options.position){
vec2.copy(this.position, options.position);
}
/**
* The interpolated position of the body. Use this for rendering.
* @property interpolatedPosition
* @type {Array}
*/
this.interpolatedPosition = vec2.fromValues(0,0);
/**
* The interpolated angle of the body. Use this for rendering.
* @property interpolatedAngle
* @type {Number}
*/
this.interpolatedAngle = 0;
/**
* The previous position of the body.
* @property previousPosition
* @type {Array}
*/
this.previousPosition = vec2.fromValues(0,0);
/**
* The previous angle of the body.
* @property previousAngle
* @type {Number}
*/
this.previousAngle = 0;
/**
* The current velocity of the body.
* @property velocity
* @type {Array}
*/
this.velocity = vec2.fromValues(0,0);
if(options.velocity){
vec2.copy(this.velocity, options.velocity);
}
/**
* Constraint velocity that was added to the body during the last step.
* @property vlambda
* @type {Array}
*/
this.vlambda = vec2.fromValues(0,0);
/**
* Angular constraint velocity that was added to the body during last step.
* @property wlambda
* @type {Array}
*/
this.wlambda = 0;
/**
* The angle of the body, in radians.
* @property angle
* @type {number}
* @example
* // The angle property is not normalized to the interval 0 to 2*pi, it can be any value.
* // If you need a value between 0 and 2*pi, use the following function to normalize it.
* function normalizeAngle(angle){
* angle = angle % (2*Math.PI);
* if(angle < 0){
* angle += (2*Math.PI);
* }
* return angle;
* }
*/
this.angle = options.angle || 0;
/**
* The angular velocity of the body, in radians per second.
* @property angularVelocity
* @type {number}
*/
this.angularVelocity = options.angularVelocity || 0;
/**
* The force acting on the body. Since the body force (and {{#crossLink "Body/angularForce:property"}}{{/crossLink}}) will be zeroed after each step, so you need to set the force before each step.
* @property force
* @type {Array}
*
* @example
* // This produces a forcefield of 1 Newton in the positive x direction.
* for(var i=0; i<numSteps; i++){
* body.force[0] = 1;
* world.step(1/60);
* }
*
* @example
* // This will apply a rotational force on the body
* for(var i=0; i<numSteps; i++){
* body.angularForce = -3;
* world.step(1/60);
* }
*/
this.force = vec2.create();
if(options.force){
vec2.copy(this.force, options.force);
}
/**
* The angular force acting on the body. See {{#crossLink "Body/force:property"}}{{/crossLink}}.
* @property angularForce
* @type {number}
*/
this.angularForce = options.angularForce || 0;
/**
* The linear damping acting on the body in the velocity direction. Should be a value between 0 and 1.
* @property damping
* @type {Number}
* @default 0.1
*/
this.damping = typeof(options.damping) === "number" ? options.damping : 0.1;
/**
* The angular force acting on the body. Should be a value between 0 and 1.
* @property angularDamping
* @type {Number}
* @default 0.1
*/
this.angularDamping = typeof(options.angularDamping) === "number" ? options.angularDamping : 0.1;
/**
* The type of motion this body has. Should be one of: {{#crossLink "Body/STATIC:property"}}Body.STATIC{{/crossLink}}, {{#crossLink "Body/DYNAMIC:property"}}Body.DYNAMIC{{/crossLink}} and {{#crossLink "Body/KINEMATIC:property"}}Body.KINEMATIC{{/crossLink}}.
*
* * Static bodies do not move, and they do not respond to forces or collision.
* * Dynamic bodies body can move and respond to collisions and forces.
* * Kinematic bodies only moves according to its .velocity, and does not respond to collisions or force.
*
* @property type
* @type {number}
*
* @example
* // Bodies are static by default. Static bodies will never move.
* var body = new Body();
* console.log(body.type == Body.STATIC); // true
*
* @example
* // By setting the mass of a body to a nonzero number, the body
* // will become dynamic and will move and interact with other bodies.
* var dynamicBody = new Body({
* mass : 1
* });
* console.log(dynamicBody.type == Body.DYNAMIC); // true
*
* @example
* // Kinematic bodies will only move if you change their velocity.
* var kinematicBody = new Body({
* type: Body.KINEMATIC // Type can be set via the options object.
* });
*/
this.type = Body.STATIC;
if(typeof(options.type) !== 'undefined'){
this.type = options.type;
} else if(!options.mass){
this.type = Body.STATIC;
} else {
this.type = Body.DYNAMIC;
}
/**
* Bounding circle radius.
* @property boundingRadius
* @type {Number}
*/
this.boundingRadius = 0;
/**
* Bounding box of this body.
* @property aabb
* @type {AABB}
*/
this.aabb = new AABB();
/**
* Indicates if the AABB needs update. Update it with {{#crossLink "Body/updateAABB:method"}}.updateAABB(){{/crossLink}}.
* @property aabbNeedsUpdate
* @type {Boolean}
* @see updateAABB
*
* @example
* // Force update the AABB
* body.aabbNeedsUpdate = true;
* body.updateAABB();
* console.log(body.aabbNeedsUpdate); // false
*/
this.aabbNeedsUpdate = true;
/**
* If true, the body will automatically fall to sleep. Note that you need to enable sleeping in the {{#crossLink "World"}}{{/crossLink}} before anything will happen.
* @property allowSleep
* @type {Boolean}
* @default true
*/
this.allowSleep = options.allowSleep !== undefined ? options.allowSleep : true;
this.wantsToSleep = false;
/**
* One of {{#crossLink "Body/AWAKE:property"}}Body.AWAKE{{/crossLink}}, {{#crossLink "Body/SLEEPY:property"}}Body.SLEEPY{{/crossLink}} and {{#crossLink "Body/SLEEPING:property"}}Body.SLEEPING{{/crossLink}}.
*
* The body is initially Body.AWAKE. If its velocity norm is below .sleepSpeedLimit, the sleepState will become Body.SLEEPY. If the body continues to be Body.SLEEPY for .sleepTimeLimit seconds, it will fall asleep (Body.SLEEPY).
*
* @property sleepState
* @type {Number}
* @default Body.AWAKE
*/
this.sleepState = Body.AWAKE;
/**
* If the speed (the norm of the velocity) is smaller than this value, the body is considered sleepy.
* @property sleepSpeedLimit
* @type {Number}
* @default 0.2
*/
this.sleepSpeedLimit = options.sleepSpeedLimit !== undefined ? options.sleepSpeedLimit : 0.2;
/**
* If the body has been sleepy for this sleepTimeLimit seconds, it is considered sleeping.
* @property sleepTimeLimit
* @type {Number}
* @default 1
*/
this.sleepTimeLimit = options.sleepTimeLimit !== undefined ? options.sleepTimeLimit : 1;
/**
* Gravity scaling factor. If you want the body to ignore gravity, set this to zero. If you want to reverse gravity, set it to -1.
* @property {Number} gravityScale
* @default 1
*/
this.gravityScale = options.gravityScale !== undefined ? options.gravityScale : 1;
/**
* Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled. That means that this body will move through other bodies, but it will still trigger contact events, etc.
* @property {Boolean} collisionResponse
*/
this.collisionResponse = options.collisionResponse !== undefined ? options.collisionResponse : true;
/**
* How long the body has been sleeping.
* @property {Number} idleTime
*/
this.idleTime = 0;
/**
* The last time when the body went to SLEEPY state.
* @property {Number} timeLastSleepy
* @private
*/
this.timeLastSleepy = 0;
/**
* If the body speed exceeds this threshold, CCD (continuous collision detection) will be enabled. Set it to a negative number to disable CCD completely for this body.
* @property {number} ccdSpeedThreshold
* @default -1
*/
this.ccdSpeedThreshold = options.ccdSpeedThreshold !== undefined ? options.ccdSpeedThreshold : -1;
/**
* The number of iterations that should be used when searching for the time of impact during CCD. A larger number will assure that there's a small penetration on CCD collision, but a small number will give more performance.
* @property {number} ccdIterations
* @default 10
*/
this.ccdIterations = options.ccdIterations !== undefined ? options.ccdIterations : 10;
this.concavePath = null;
this._wakeUpAfterNarrowphase = false;
this.updateMassProperties();
}
Body.prototype = new EventEmitter();
Body.prototype.constructor = Body;
Body._idCounter = 0;
/**
* @private
* @method updateSolveMassProperties
*/
Body.prototype.updateSolveMassProperties = function(){
if(this.sleepState === Body.SLEEPING || this.type === Body.KINEMATIC){
this.invMassSolve = 0;
this.invInertiaSolve = 0;
} else {
this.invMassSolve = this.invMass;
this.invInertiaSolve = this.invInertia;
}
};
/**
* Set the total density of the body
* @method setDensity
* @param {number} density
*/
Body.prototype.setDensity = function(density) {
var totalArea = this.getArea();
this.mass = totalArea * density;
this.updateMassProperties();
};
/**
* Get the total area of all shapes in the body
* @method getArea
* @return {Number}
*/
Body.prototype.getArea = function() {
var totalArea = 0;
for(var i=0; i<this.shapes.length; i++){
totalArea += this.shapes[i].area;
}
return totalArea;
};
/**
* Get the AABB from the body. The AABB is updated if necessary.
* @method getAABB
* @return {AABB} The AABB instance (this.aabb)
*/
Body.prototype.getAABB = function(){
if(this.aabbNeedsUpdate){
this.updateAABB();
}
return this.aabb;
};
var shapeAABB = new AABB(),
tmp = vec2.create();
/**
* Updates the AABB of the Body, and set .aabbNeedsUpdate = false.
* @method updateAABB
*/
Body.prototype.updateAABB = function() {
var shapes = this.shapes,
N = shapes.length,
offset = tmp,
bodyAngle = this.angle;
for(var i=0; i!==N; i++){
var shape = shapes[i],
angle = shape.angle + bodyAngle;
// Get shape world offset
vec2.rotate(offset, shape.position, bodyAngle);
vec2.add(offset, offset, this.position);
// Get shape AABB
shape.computeAABB(shapeAABB, offset, angle);
if(i===0){
this.aabb.copy(shapeAABB);
} else {
this.aabb.extend(shapeAABB);
}
}
this.aabbNeedsUpdate = false;
};
/**
* Update the bounding radius of the body (this.boundingRadius). Should be done if any of the shape dimensions or positions are changed.
* @method updateBoundingRadius
*/
Body.prototype.updateBoundingRadius = function(){
var shapes = this.shapes,
N = shapes.length,
radius = 0;
for(var i=0; i!==N; i++){
var shape = shapes[i],
offset = vec2.length(shape.position),
r = shape.boundingRadius;
if(offset + r > radius){
radius = offset + r;
}
}
this.boundingRadius = radius;
};
/**
* Add a shape to the body. You can pass a local transform when adding a shape,
* so that the shape gets an offset and angle relative to the body center of mass.
* Will automatically update the mass properties and bounding radius.
*
* @method addShape
* @param {Shape} shape
* @param {Array} [offset] Local body offset of the shape.
* @param {Number} [angle] Local body angle.
*
* @example
* var body = new Body(),
* shape = new Circle({ radius: 1 });
*
* // Add the shape to the body, positioned in the center
* body.addShape(shape);
*
* // Add another shape to the body, positioned 1 unit length from the body center of mass along the local x-axis.
* body.addShape(shape,[1,0]);
*
* // Add another shape to the body, positioned 1 unit length from the body center of mass along the local y-axis, and rotated 90 degrees CCW.
* body.addShape(shape,[0,1],Math.PI/2);
*/
Body.prototype.addShape = function(shape, offset, angle){
if(shape.body){
throw new Error('A shape can only be added to one body.');
}
shape.body = this;
// Copy the offset vector
if(offset){
vec2.copy(shape.position, offset);
} else {
vec2.set(shape.position, 0, 0);
}
shape.angle = angle || 0;
this.shapes.push(shape);
this.updateMassProperties();
this.updateBoundingRadius();
this.aabbNeedsUpdate = true;
};
/**
* Remove a shape
* @method removeShape
* @param {Shape} shape
* @return {Boolean} True if the shape was found and removed, else false.
*/
Body.prototype.removeShape = function(shape){
var idx = this.shapes.indexOf(shape);
if(idx !== -1){
this.shapes.splice(idx,1);
this.aabbNeedsUpdate = true;
shape.body = null;
return true;
} else {
return false;
}
};
/**
* Updates .inertia, .invMass, .invInertia for this Body. Should be called when
* changing the structure or mass of the Body.
*
* @method updateMassProperties
*
* @example
* body.mass += 1;
* body.updateMassProperties();
*/
Body.prototype.updateMassProperties = function(){
if(this.type === Body.STATIC || this.type === Body.KINEMATIC){
this.mass = Number.MAX_VALUE;
this.invMass = 0;
this.inertia = Number.MAX_VALUE;
this.invInertia = 0;
} else {
var shapes = this.shapes,
N = shapes.length,
m = this.mass / N,
I = 0;
if(!this.fixedRotation){
for(var i=0; i<N; i++){
var shape = shapes[i],
r2 = vec2.squaredLength(shape.position),
Icm = shape.computeMomentOfInertia(m);
I += Icm + m*r2;
}
this.inertia = I;
this.invInertia = I>0 ? 1/I : 0;
} else {
this.inertia = Number.MAX_VALUE;
this.invInertia = 0;
}
// Inverse mass properties are easy
this.invMass = 1 / this.mass;
vec2.set(
this.massMultiplier,
this.fixedX ? 0 : 1,
this.fixedY ? 0 : 1
);
}
};
var Body_applyForce_r = vec2.create();
/**
* Apply force to a point relative to the center of mass of the body. This could for example be a point on the RigidBody surface. Applying force this way will add to Body.force and Body.angularForce. If relativePoint is zero, the force will be applied directly on the center of mass, and the torque produced will be zero.
* @method applyForce
* @param {Array} force The force to add.
* @param {Array} [relativePoint] A world point to apply the force on.
*/
Body.prototype.applyForce = function(force, relativePoint){
// Add linear force
vec2.add(this.force, this.force, force);
if(relativePoint){
// Compute produced rotational force
var rotForce = vec2.crossLength(relativePoint,force);
// Add rotational force
this.angularForce += rotForce;
}
};
/**
* Apply force to a body-local point.
* @method applyForceLocal
* @param {Array} localForce The force vector to add, oriented in local body space.
* @param {Array} [localPoint] A point relative to the body in world space. If not given, it is set to zero and all of the impulse will be excerted on the center of mass.
*/
var Body_applyForce_forceWorld = vec2.create();
var Body_applyForce_pointWorld = vec2.create();
var Body_applyForce_pointLocal = vec2.create();
Body.prototype.applyForceLocal = function(localForce, localPoint){
localPoint = localPoint || Body_applyForce_pointLocal;
var worldForce = Body_applyForce_forceWorld;
var worldPoint = Body_applyForce_pointWorld;
this.vectorToWorldFrame(worldForce, localForce);
this.vectorToWorldFrame(worldPoint, localPoint);
this.applyForce(worldForce, worldPoint);
};
/**
* Apply impulse to a point relative to the body. This could for example be a point on the Body surface. An impulse is a force added to a body during a short period of time (impulse = force * time). Impulses will be added to Body.velocity and Body.angularVelocity.
* @method applyImpulse
* @param {Array} impulse The impulse vector to add, oriented in world space.
* @param {Array} [relativePoint] A point relative to the body in world space. If not given, it is set to zero and all of the impulse will be excerted on the center of mass.
*/
var Body_applyImpulse_velo = vec2.create();
Body.prototype.applyImpulse = function(impulseVector, relativePoint){
if(this.type !== Body.DYNAMIC){
return;
}
// Compute produced central impulse velocity
var velo = Body_applyImpulse_velo;
vec2.scale(velo, impulseVector, this.invMass);
vec2.multiply(velo, this.massMultiplier, velo);
// Add linear impulse
vec2.add(this.velocity, velo, this.velocity);
if(relativePoint){
// Compute produced rotational impulse velocity
var rotVelo = vec2.crossLength(relativePoint, impulseVector);
rotVelo *= this.invInertia;
// Add rotational Impulse
this.angularVelocity += rotVelo;
}
};
/**
* Apply impulse to a point relative to the body. This could for example be a point on the Body surface. An impulse is a force added to a body during a short period of time (impulse = force * time). Impulses will be added to Body.velocity and Body.angularVelocity.
* @method applyImpulseLocal
* @param {Array} impulse The impulse vector to add, oriented in world space.
* @param {Array} [relativePoint] A point relative to the body in world space. If not given, it is set to zero and all of the impulse will be excerted on the center of mass.
*/
var Body_applyImpulse_impulseWorld = vec2.create();
var Body_applyImpulse_pointWorld = vec2.create();
var Body_applyImpulse_pointLocal = vec2.create();
Body.prototype.applyImpulseLocal = function(localImpulse, localPoint){
localPoint = localPoint || Body_applyImpulse_pointLocal;
var worldImpulse = Body_applyImpulse_impulseWorld;
var worldPoint = Body_applyImpulse_pointWorld;
this.vectorToWorldFrame(worldImpulse, localImpulse);
this.vectorToWorldFrame(worldPoint, localPoint);
this.applyImpulse(worldImpulse, worldPoint);
};
/**
* Transform a world point to local body frame.
* @method toLocalFrame
* @param {Array} out The vector to store the result in
* @param {Array} worldPoint The input world point
*/
Body.prototype.toLocalFrame = function(out, worldPoint){
vec2.toLocalFrame(out, worldPoint, this.position, this.angle);
};
/**
* Transform a local point to world frame.
* @method toWorldFrame
* @param {Array} out The vector to store the result in
* @param {Array} localPoint The input local point
*/
Body.prototype.toWorldFrame = function(out, localPoint){
vec2.toGlobalFrame(out, localPoint, this.position, this.angle);
};
/**
* Transform a world point to local body frame.
* @method vectorToLocalFrame
* @param {Array} out The vector to store the result in
* @param {Array} worldVector The input world vector
*/
Body.prototype.vectorToLocalFrame = function(out, worldVector){
vec2.vectorToLocalFrame(out, worldVector, this.angle);
};
/**
* Transform a local point to world frame.
* @method vectorToWorldFrame
* @param {Array} out The vector to store the result in
* @param {Array} localVector The input local vector
*/
Body.prototype.vectorToWorldFrame = function(out, localVector){
vec2.vectorToGlobalFrame(out, localVector, this.angle);
};
/**
* Reads a polygon shape path, and assembles convex shapes from that and puts them at proper offset points.
* @method fromPolygon
* @param {Array} path An array of 2d vectors, e.g. [[0,0],[0,1],...] that resembles a concave or convex polygon. The shape must be simple and without holes.
* @param {Object} [options]
* @param {Boolean} [options.optimalDecomp=false] Set to true if you need optimal decomposition. Warning: very slow for polygons with more than 10 vertices.
* @param {Boolean} [options.skipSimpleCheck=false] Set to true if you already know that the path is not intersecting itself.
* @param {Boolean|Number} [options.removeCollinearPoints=false] Set to a number (angle threshold value) to remove collinear points, or false to keep all points.
* @return {Boolean} True on success, else false.
*/
Body.prototype.fromPolygon = function(path,options){
options = options || {};
// Remove all shapes
for(var i=this.shapes.length; i>=0; --i){
this.removeShape(this.shapes[i]);
}
var p = new decomp.Polygon();
p.vertices = path;
// Make it counter-clockwise
p.makeCCW();
if(typeof(options.removeCollinearPoints) === "number"){
p.removeCollinearPoints(options.removeCollinearPoints);
}
// Check if any line segment intersects the path itself
if(typeof(options.skipSimpleCheck) === "undefined"){
if(!p.isSimple()){
return false;
}
}
// Save this path for later
this.concavePath = p.vertices.slice(0);
for(var i=0; i<this.concavePath.length; i++){
var v = [0,0];
vec2.copy(v,this.concavePath[i]);
this.concavePath[i] = v;
}
// Slow or fast decomp?
var convexes;
if(options.optimalDecomp){
convexes = p.decomp();
} else {
convexes = p.quickDecomp();
}
var cm = vec2.create();
// Add convexes
for(var i=0; i!==convexes.length; i++){
// Create convex
var c = new Convex({ vertices: convexes[i].vertices });
// Move all vertices so its center of mass is in the local center of the convex
for(var j=0; j!==c.vertices.length; j++){
var v = c.vertices[j];
vec2.sub(v,v,c.centerOfMass);
}
vec2.scale(cm,c.centerOfMass,1);
c.updateTriangles();
c.updateCenterOfMass();
c.updateBoundingRadius();
// Add the shape
this.addShape(c,cm);
}
this.adjustCenterOfMass();
this.aabbNeedsUpdate = true;
return true;
};
var adjustCenterOfMass_tmp1 = vec2.fromValues(0,0),
adjustCenterOfMass_tmp2 = vec2.fromValues(0,0),
adjustCenterOfMass_tmp3 = vec2.fromValues(0,0),
adjustCenterOfMass_tmp4 = vec2.fromValues(0,0);
/**
* Moves the shape offsets so their center of mass becomes the body center of mass.
* @method adjustCenterOfMass
*/
Body.prototype.adjustCenterOfMass = function(){
var offset_times_area = adjustCenterOfMass_tmp2,
sum = adjustCenterOfMass_tmp3,
cm = adjustCenterOfMass_tmp4,
totalArea = 0;
vec2.set(sum,0,0);
for(var i=0; i!==this.shapes.length; i++){
var s = this.shapes[i];
vec2.scale(offset_times_area, s.position, s.area);
vec2.add(sum, sum, offset_times_area);
totalArea += s.area;
}
vec2.scale(cm,sum,1/totalArea);
// Now move all shapes
for(var i=0; i!==this.shapes.length; i++){
var s = this.shapes[i];
vec2.sub(s.position, s.position, cm);
}
// Move the body position too
vec2.add(this.position,this.position,cm);
// And concave path
for(var i=0; this.concavePath && i<this.concavePath.length; i++){
vec2.sub(this.concavePath[i], this.concavePath[i], cm);
}
this.updateMassProperties();
this.updateBoundingRadius();
};
/**
* Sets the force on the body to zero.
* @method setZeroForce
*/
Body.prototype.setZeroForce = function(){
vec2.set(this.force,0.0,0.0);
this.angularForce = 0.0;
};
Body.prototype.resetConstraintVelocity = function(){
var b = this,
vlambda = b.vlambda;
vec2.set(vlambda,0,0);
b.wlambda = 0;
};
Body.prototype.addConstraintVelocity = function(){
var b = this,
v = b.velocity;
vec2.add( v, v, b.vlambda);
b.angularVelocity += b.wlambda;
};
/**
* Apply damping, see <a href="http://code.google.com/p/bullet/issues/detail?id=74">this</a> for details.
* @method applyDamping
* @param {number} dt Current time step
*/
Body.prototype.applyDamping = function(dt){
if(this.type === Body.DYNAMIC){ // Only for dynamic bodies
var v = this.velocity;
vec2.scale(v, v, Math.pow(1.0 - this.damping,dt));
this.angularVelocity *= Math.pow(1.0 - this.angularDamping,dt);
}
};
/**
* Wake the body up. Normally you should not need this, as the body is automatically awoken at events such as collisions.
* Sets the sleepState to {{#crossLink "Body/AWAKE:property"}}Body.AWAKE{{/crossLink}} and emits the wakeUp event if the body wasn't awake before.
* @method wakeUp
*/
Body.prototype.wakeUp = function(){
var s = this.sleepState;
this.sleepState = Body.AWAKE;
this.idleTime = 0;
if(s !== Body.AWAKE){
this.emit(Body.wakeUpEvent);
}
};
/**
* Force body sleep
* @method sleep
*/
Body.prototype.sleep = function(){
this.sleepState = Body.SLEEPING;
this.angularVelocity = 0;
this.angularForce = 0;
vec2.set(this.velocity,0,0);
vec2.set(this.force,0,0);
this.emit(Body.sleepEvent);
};
/**
* Called every timestep to update internal sleep timer and change sleep state if needed.
* @method sleepTick
* @param {number} time The world time in seconds
* @param {boolean} dontSleep
* @param {number} dt
*/
Body.prototype.sleepTick = function(time, dontSleep, dt){
if(!this.allowSleep || this.type === Body.SLEEPING){
return;
}
this.wantsToSleep = false;
var sleepState = this.sleepState,
speedSquared = vec2.squaredLength(this.velocity) + Math.pow(this.angularVelocity,2),
speedLimitSquared = Math.pow(this.sleepSpeedLimit,2);
// Add to idle time
if(speedSquared >= speedLimitSquared){
this.idleTime = 0;
this.sleepState = Body.AWAKE;
} else {
this.idleTime += dt;
this.sleepState = Body.SLEEPY;
}
if(this.idleTime > this.sleepTimeLimit){
if(!dontSleep){
this.sleep();
} else {
this.wantsToSleep = true;
}
}
};
/**
* Check if the body is overlapping another body. Note that this method only works if the body was added to a World and if at least one step was taken.
* @method overlaps
* @param {Body} body
* @return {boolean}
*/
Body.prototype.overlaps = function(body){
return this.world.overlapKeeper.bodiesAreOverlapping(this, body);
};
var integrate_fhMinv = vec2.create();
var integrate_velodt = vec2.create();
/**
* Move the body forward in time given its current velocity.
* @method integrate
* @param {Number} dt
*/
Body.prototype.integrate = function(dt){
var minv = this.invMass,
f = this.force,
pos = this.position,
velo = this.velocity;
// Save old position
vec2.copy(this.previousPosition, this.position);
this.previousAngle = this.angle;
// Velocity update
if(!this.fixedRotation){
this.angularVelocity += this.angularForce * this.invInertia * dt;
}
vec2.scale(integrate_fhMinv, f, dt * minv);
vec2.multiply(integrate_fhMinv, this.massMultiplier, integrate_fhMinv);
vec2.add(velo, integrate_fhMinv, velo);
// CCD
if(!this.integrateToTimeOfImpact(dt)){
// Regular position update
vec2.scale(integrate_velodt, velo, dt);
vec2.add(pos, pos, integrate_velodt);
if(!this.fixedRotation){
this.angle += this.angularVelocity * dt;
}
}
this.aabbNeedsUpdate = true;
};
var result = new RaycastResult();
var ray = new Ray({
mode: Ray.ALL
});
var direction = vec2.create();
var end = vec2.create();
var startToEnd = vec2.create();
var rememberPosition = vec2.create();
Body.prototype.integrateToTimeOfImpact = function(dt){
if(this.ccdSpeedThreshold < 0 || vec2.squaredLength(this.velocity) < Math.pow(this.ccdSpeedThreshold, 2)){
return false;
}
vec2.normalize(direction, this.velocity);
vec2.scale(end, this.velocity, dt);
vec2.add(end, end, this.position);
vec2.sub(startToEnd, end, this.position);
var startToEndAngle = this.angularVelocity * dt;
var len = vec2.length(startToEnd);
var timeOfImpact = 1;
var hit;
var that = this;
result.reset();
ray.callback = function (result) {
if(result.body === that){
return;
}
hit = result.body;
result.getHitPoint(end, ray);
vec2.sub(startToEnd, end, that.position);
timeOfImpact = vec2.length(startToEnd) / len;
result.stop();
};
vec2.copy(ray.from, this.position);
vec2.copy(ray.to, end);
ray.update();
this.world.raycast(result, ray);
if(!hit){
return false;
}
var rememberAngle = this.angle;
vec2.copy(rememberPosition, this.position);
// Got a start and end point. Approximate time of impact using binary search
var iter = 0;
var tmin = 0;
var tmid = 0;
var tmax = timeOfImpact;
while (tmax >= tmin && iter < this.ccdIterations) {
iter++;
// calculate the midpoint
tmid = (tmax - tmin) / 2;
// Move the body to that point
vec2.scale(integrate_velodt, startToEnd, timeOfImpact);
vec2.add(this.position, rememberPosition, integrate_velodt);
this.angle = rememberAngle + startToEndAngle * timeOfImpact;
this.updateAABB();
// check overlap
var overlaps = this.aabb.overlaps(hit.aabb) && this.world.narrowphase.bodiesOverlap(this, hit);
if (overlaps) {
// change min to search upper interval
tmin = tmid;
} else {
// change max to search lower interval
tmax = tmid;
}
}
timeOfImpact = tmid;
vec2.copy(this.position, rememberPosition);
this.angle = rememberAngle;
// move to TOI
vec2.scale(integrate_velodt, startToEnd, timeOfImpact);
vec2.add(this.position, this.position, integrate_velodt);
if(!this.fixedRotation){
this.angle += startToEndAngle * timeOfImpact;
}
return true;
};
/**
* Get velocity of a point in the body.
* @method getVelocityAtPoint
* @param {Array} result A vector to store the result in
* @param {Array} relativePoint A world oriented vector, indicating the position of the point to get the velocity from
* @return {Array} The result vector
*/
Body.prototype.getVelocityAtPoint = function(result, relativePoint){
vec2.crossVZ(result, relativePoint, this.angularVelocity);
vec2.subtract(result, this.velocity, result);
return result;
};
/**
* @event sleepy
*/
Body.sleepyEvent = {
type: "sleepy"
};
/**
* @event sleep
*/
Body.sleepEvent = {
type: "sleep"
};
/**
* @event wakeup
*/
Body.wakeUpEvent = {
type: "wakeup"
};
/**
* Dynamic body.
* @property DYNAMIC
* @type {Number}
* @static
*/
Body.DYNAMIC = 1;
/**
* Static body.
* @property STATIC
* @type {Number}
* @static
*/
Body.STATIC = 2;
/**
* Kinematic body.
* @property KINEMATIC
* @type {Number}
* @static
*/
Body.KINEMATIC = 4;
/**
* @property AWAKE
* @type {Number}
* @static
*/
Body.AWAKE = 0;
/**
* @property SLEEPY
* @type {Number}
* @static
*/
Body.SLEEPY = 1;
/**
* @property SLEEPING
* @type {Number}
* @static
*/
Body.SLEEPING = 2;