planck-js
Version:
2D JavaScript physics engine for cross-platform HTML5 game development
1,085 lines (921 loc) • 28.5 kB
JavaScript
/*
* Planck.js
* The MIT License
* Copyright (c) 2021 Erin Catto, Ali Shakiba
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
var _DEBUG = typeof DEBUG === 'undefined' ? false : DEBUG;
var _ASSERT = typeof ASSERT === 'undefined' ? false : ASSERT;
module.exports = Body;
var common = require('./util/common');
var options = require('./util/options');
var Vec2 = require('./common/Vec2');
var Rot = require('./common/Rot');
var Math = require('./common/Math');
var Sweep = require('./common/Sweep');
var Transform = require('./common/Transform');
var Velocity = require('./common/Velocity');
var Position = require('./common/Position');
var Fixture = require('./Fixture');
var Shape = require('./Shape');
var staticBody = Body.STATIC = 'static';
var kinematicBody = Body.KINEMATIC = 'kinematic';
var dynamicBody = Body.DYNAMIC = 'dynamic';
/**
* @typedef {Object} BodyDef
*
* @prop type Body types are static, kinematic, or dynamic. Note: if a dynamic
* body would have zero mass, the mass is set to one.
*
* @prop position The world position of the body. Avoid creating bodies at the
* origin since this can lead to many overlapping shapes.
*
* @prop angle The world angle of the body in radians.
*
* @prop linearVelocity The linear velocity of the body's origin in world
* co-ordinates.
*
* @prop angularVelocity
*
* @prop linearDamping Linear damping is use to reduce the linear velocity. The
* damping parameter can be larger than 1.0 but the damping effect becomes
* sensitive to the time step when the damping parameter is large.
*
* @prop angularDamping Angular damping is use to reduce the angular velocity.
* The damping parameter can be larger than 1.0 but the damping effect
* becomes sensitive to the time step when the damping parameter is large.
*
* @prop fixedRotation Should this body be prevented from rotating? Useful for
* characters.
*
* @prop bullet Is this a fast moving body that should be prevented from
* tunneling through other moving bodies? Note that all bodies are
* prevented from tunneling through kinematic and static bodies. This
* setting is only considered on dynamic bodies. Warning: You should use
* this flag sparingly since it increases processing time.
*
* @prop active Does this body start out active?
*
* @prop awake Is this body initially awake or sleeping?
*
* @prop allowSleep Set this flag to false if this body should never fall
* asleep. Note that this increases CPU usage.
*/
var BodyDef = {
type : staticBody,
position : Vec2.zero(),
angle : 0.0,
linearVelocity : Vec2.zero(),
angularVelocity : 0.0,
linearDamping : 0.0,
angularDamping : 0.0,
fixedRotation : false,
bullet : false,
gravityScale : 1.0,
allowSleep : true,
awake : true,
active : true,
userData : null
};
/**
* @class
*
* A rigid body composed of one or more fixtures.
*
* @param {World} world
* @param {BodyDef} def
*/
function Body(world, def) {
def = options(def, BodyDef);
_ASSERT && common.assert(Vec2.isValid(def.position));
_ASSERT && common.assert(Vec2.isValid(def.linearVelocity));
_ASSERT && common.assert(Math.isFinite(def.angle));
_ASSERT && common.assert(Math.isFinite(def.angularVelocity));
_ASSERT && common.assert(Math.isFinite(def.angularDamping) && def.angularDamping >= 0.0);
_ASSERT && common.assert(Math.isFinite(def.linearDamping) && def.linearDamping >= 0.0);
this.m_world = world;
this.m_awakeFlag = def.awake;
this.m_autoSleepFlag = def.allowSleep;
this.m_bulletFlag = def.bullet;
this.m_fixedRotationFlag = def.fixedRotation;
this.m_activeFlag = def.active;
this.m_islandFlag = false;
this.m_toiFlag = false;
this.m_userData = def.userData;
this.m_type = def.type;
if (this.m_type == dynamicBody) {
this.m_mass = 1.0;
this.m_invMass = 1.0;
} else {
this.m_mass = 0.0;
this.m_invMass = 0.0;
}
// Rotational inertia about the center of mass.
this.m_I = 0.0;
this.m_invI = 0.0;
// the body origin transform
this.m_xf = Transform.identity();
this.m_xf.p = Vec2.clone(def.position);
this.m_xf.q.setAngle(def.angle);
// the swept motion for CCD
this.m_sweep = new Sweep();
this.m_sweep.setTransform(this.m_xf);
// position and velocity correction
this.c_velocity = new Velocity();
this.c_position = new Position();
this.m_force = Vec2.zero();
this.m_torque = 0.0;
this.m_linearVelocity = Vec2.clone(def.linearVelocity);
this.m_angularVelocity = def.angularVelocity;
this.m_linearDamping = def.linearDamping;
this.m_angularDamping = def.angularDamping;
this.m_gravityScale = def.gravityScale;
this.m_sleepTime = 0.0;
this.m_jointList = null;
this.m_contactList = null;
this.m_fixtureList = null;
this.m_prev = null;
this.m_next = null;
this.m_destroyed = false;
}
Body.prototype._serialize = function() {
var fixtures = [];
for (var f = this.m_fixtureList; f; f = f.m_next) {
fixtures.push(f);
}
return {
type: this.m_type,
bullet: this.m_bulletFlag,
position: this.m_xf.p,
angle: this.m_xf.q.getAngle(),
linearVelocity: this.m_linearVelocity,
angularVelocity: this.m_angularVelocity,
fixtures: fixtures,
};
};
Body._deserialize = function(data, world, restore) {
var body = new Body(world, data);
if (data.fixtures) {
for (var i = data.fixtures.length - 1; i >= 0; i--) {
var fixture = restore(Fixture, data.fixtures[i], body);
body._addFixture(fixture);
}
}
return body;
};
Body.prototype.isWorldLocked = function() {
return this.m_world && this.m_world.isLocked() ? true : false;
};
Body.prototype.getWorld = function() {
return this.m_world;
};
Body.prototype.getNext = function() {
return this.m_next;
};
Body.prototype.setUserData = function(data) {
this.m_userData = data;
};
Body.prototype.getUserData = function() {
return this.m_userData;
};
Body.prototype.getFixtureList = function() {
return this.m_fixtureList;
};
Body.prototype.getJointList = function() {
return this.m_jointList;
};
/**
* Warning: this list changes during the time step and you may miss some
* collisions if you don't use ContactListener.
*/
Body.prototype.getContactList = function() {
return this.m_contactList;
};
Body.prototype.isStatic = function() {
return this.m_type == staticBody;
};
Body.prototype.isDynamic = function() {
return this.m_type == dynamicBody;
};
Body.prototype.isKinematic = function() {
return this.m_type == kinematicBody;
};
/**
* This will alter the mass and velocity.
*/
Body.prototype.setStatic = function() {
this.setType(staticBody);
return this;
};
Body.prototype.setDynamic = function() {
this.setType(dynamicBody);
return this;
};
Body.prototype.setKinematic = function() {
this.setType(kinematicBody);
return this;
};
/**
* @private
*/
Body.prototype.getType = function() {
return this.m_type;
};
/**
*
* @private
*/
Body.prototype.setType = function(type) {
_ASSERT && common.assert(type === staticBody || type === kinematicBody || type === dynamicBody);
_ASSERT && common.assert(this.isWorldLocked() == false);
if (this.isWorldLocked() == true) {
return;
}
if (this.m_type == type) {
return;
}
this.m_type = type;
this.resetMassData();
if (this.m_type == staticBody) {
this.m_linearVelocity.setZero();
this.m_angularVelocity = 0.0;
this.m_sweep.forward();
this.synchronizeFixtures();
}
this.setAwake(true);
this.m_force.setZero();
this.m_torque = 0.0;
// Delete the attached contacts.
var ce = this.m_contactList;
while (ce) {
var ce0 = ce;
ce = ce.next;
this.m_world.destroyContact(ce0.contact);
}
this.m_contactList = null;
// Touch the proxies so that new contacts will be created (when appropriate)
var broadPhase = this.m_world.m_broadPhase;
for (var f = this.m_fixtureList; f; f = f.m_next) {
var proxyCount = f.m_proxyCount;
for (var i = 0; i < proxyCount; ++i) {
broadPhase.touchProxy(f.m_proxies[i].proxyId);
}
}
};
Body.prototype.isBullet = function() {
return this.m_bulletFlag;
};
/**
* Should this body be treated like a bullet for continuous collision detection?
*/
Body.prototype.setBullet = function(flag) {
this.m_bulletFlag = !!flag;
};
Body.prototype.isSleepingAllowed = function() {
return this.m_autoSleepFlag;
};
Body.prototype.setSleepingAllowed = function(flag) {
this.m_autoSleepFlag = !!flag;
if (this.m_autoSleepFlag == false) {
this.setAwake(true);
}
};
Body.prototype.isAwake = function() {
return this.m_awakeFlag;
};
/**
* Set the sleep state of the body. A sleeping body has very low CPU cost.
*
* @param flag Set to true to wake the body, false to put it to sleep.
*/
Body.prototype.setAwake = function(flag) {
if (flag) {
if (this.m_awakeFlag == false) {
this.m_awakeFlag = true;
this.m_sleepTime = 0.0;
}
} else {
this.m_awakeFlag = false;
this.m_sleepTime = 0.0;
this.m_linearVelocity.setZero();
this.m_angularVelocity = 0.0;
this.m_force.setZero();
this.m_torque = 0.0;
}
};
Body.prototype.isActive = function() {
return this.m_activeFlag;
};
/**
* Set the active state of the body. An inactive body is not simulated and
* cannot be collided with or woken up. If you pass a flag of true, all fixtures
* will be added to the broad-phase. If you pass a flag of false, all fixtures
* will be removed from the broad-phase and all contacts will be destroyed.
* Fixtures and joints are otherwise unaffected.
*
* You may continue to create/destroy fixtures and joints on inactive bodies.
* Fixtures on an inactive body are implicitly inactive and will not participate
* in collisions, ray-casts, or queries. Joints connected to an inactive body
* are implicitly inactive. An inactive body is still owned by a World object
* and remains
*/
Body.prototype.setActive = function(flag) {
_ASSERT && common.assert(this.isWorldLocked() == false);
if (flag == this.m_activeFlag) {
return;
}
this.m_activeFlag = !!flag;
if (this.m_activeFlag) {
// Create all proxies.
var broadPhase = this.m_world.m_broadPhase;
for (var f = this.m_fixtureList; f; f = f.m_next) {
f.createProxies(broadPhase, this.m_xf);
}
// Contacts are created the next time step.
} else {
// Destroy all proxies.
var broadPhase = this.m_world.m_broadPhase;
for (var f = this.m_fixtureList; f; f = f.m_next) {
f.destroyProxies(broadPhase);
}
// Destroy the attached contacts.
var ce = this.m_contactList;
while (ce) {
var ce0 = ce;
ce = ce.next;
this.m_world.destroyContact(ce0.contact);
}
this.m_contactList = null;
}
};
Body.prototype.isFixedRotation = function() {
return this.m_fixedRotationFlag;
};
/**
* Set this body to have fixed rotation. This causes the mass to be reset.
*/
Body.prototype.setFixedRotation = function(flag) {
if (this.m_fixedRotationFlag == flag) {
return;
}
this.m_fixedRotationFlag = !!flag;
this.m_angularVelocity = 0.0;
this.resetMassData();
};
/**
* Get the world transform for the body's origin.
*/
Body.prototype.getTransform = function() {
return this.m_xf;
};
/**
* Set the position of the body's origin and rotation. Manipulating a body's
* transform may cause non-physical behavior. Note: contacts are updated on the
* next call to World.step.
*
* @param position The world position of the body's local origin.
* @param angle The world rotation in radians.
*/
Body.prototype.setTransform = function(position, angle) {
_ASSERT && common.assert(this.isWorldLocked() == false);
if (this.isWorldLocked() == true) {
return;
}
this.m_xf.set(position, angle);
this.m_sweep.setTransform(this.m_xf);
var broadPhase = this.m_world.m_broadPhase;
for (var f = this.m_fixtureList; f; f = f.m_next) {
f.synchronize(broadPhase, this.m_xf, this.m_xf);
}
};
Body.prototype.synchronizeTransform = function() {
this.m_sweep.getTransform(this.m_xf, 1);
};
/**
* Update fixtures in broad-phase.
*/
Body.prototype.synchronizeFixtures = function() {
var xf = Transform.identity();
this.m_sweep.getTransform(xf, 0);
var broadPhase = this.m_world.m_broadPhase;
for (var f = this.m_fixtureList; f; f = f.m_next) {
f.synchronize(broadPhase, xf, this.m_xf);
}
};
/**
* Used in TOI.
*/
Body.prototype.advance = function(alpha) {
// Advance to the new safe time. This doesn't sync the broad-phase.
this.m_sweep.advance(alpha);
this.m_sweep.c.set(this.m_sweep.c0);
this.m_sweep.a = this.m_sweep.a0;
this.m_sweep.getTransform(this.m_xf, 1);
};
/**
* Get the world position for the body's origin.
*/
Body.prototype.getPosition = function() {
return this.m_xf.p;
};
Body.prototype.setPosition = function(p) {
this.setTransform(p, this.m_sweep.a);
};
/**
* Get the current world rotation angle in radians.
*/
Body.prototype.getAngle = function() {
return this.m_sweep.a;
};
Body.prototype.setAngle = function(angle) {
this.setTransform(this.m_xf.p, angle);
};
/**
* Get the world position of the center of mass.
*/
Body.prototype.getWorldCenter = function() {
return this.m_sweep.c;
};
/**
* Get the local position of the center of mass.
*/
Body.prototype.getLocalCenter = function() {
return this.m_sweep.localCenter;
};
/**
* Get the linear velocity of the center of mass.
*
* @return the linear velocity of the center of mass.
*/
Body.prototype.getLinearVelocity = function() {
return this.m_linearVelocity;
};
/**
* Get the world linear velocity of a world point attached to this body.
*
* @param worldPoint A point in world coordinates.
*/
Body.prototype.getLinearVelocityFromWorldPoint = function(worldPoint) {
var localCenter = Vec2.sub(worldPoint, this.m_sweep.c);
return Vec2.add(this.m_linearVelocity, Vec2.cross(this.m_angularVelocity,
localCenter));
};
/**
* Get the world velocity of a local point.
*
* @param localPoint A point in local coordinates.
*/
Body.prototype.getLinearVelocityFromLocalPoint = function(localPoint) {
return this.getLinearVelocityFromWorldPoint(this.getWorldPoint(localPoint));
};
/**
* Set the linear velocity of the center of mass.
*
* @param v The new linear velocity of the center of mass.
*/
Body.prototype.setLinearVelocity = function(v) {
if (this.m_type == staticBody) {
return;
}
if (Vec2.dot(v, v) > 0.0) {
this.setAwake(true);
}
this.m_linearVelocity.set(v);
};
/**
* Get the angular velocity.
*
* @returns the angular velocity in radians/second.
*/
Body.prototype.getAngularVelocity = function() {
return this.m_angularVelocity;
};
/**
* Set the angular velocity.
*
* @param omega The new angular velocity in radians/second.
*/
Body.prototype.setAngularVelocity = function(w) {
if (this.m_type == staticBody) {
return;
}
if (w * w > 0.0) {
this.setAwake(true);
}
this.m_angularVelocity = w;
};
Body.prototype.getLinearDamping = function() {
return this.m_linearDamping;
};
Body.prototype.setLinearDamping = function(linearDamping) {
this.m_linearDamping = linearDamping;
};
Body.prototype.getAngularDamping = function() {
return this.m_angularDamping;
};
Body.prototype.setAngularDamping = function(angularDamping) {
this.m_angularDamping = angularDamping;
};
Body.prototype.getGravityScale = function() {
return this.m_gravityScale;
};
/**
* Scale the gravity applied to this body.
*/
Body.prototype.setGravityScale = function(scale) {
this.m_gravityScale = scale;
};
/**
* Get the total mass of the body.
*
* @returns The mass, usually in kilograms (kg).
*/
Body.prototype.getMass = function() {
return this.m_mass;
};
/**
* Get the rotational inertia of the body about the local origin.
*
* @return the rotational inertia, usually in kg-m^2.
*/
Body.prototype.getInertia = function() {
return this.m_I + this.m_mass
* Vec2.dot(this.m_sweep.localCenter, this.m_sweep.localCenter);
};
/**
* @typedef {Object} MassData This holds the mass data computed for a shape.
*
* @prop mass The mass of the shape, usually in kilograms.
* @prop center The position of the shape's centroid relative to the shape's
* origin.
* @prop I The rotational inertia of the shape about the local origin.
*/
function MassData() {
this.mass = 0;
this.center = Vec2.zero();
this.I = 0;
};
/**
* Copy the mass data of the body to data.
*/
Body.prototype.getMassData = function(data) {
data.mass = this.m_mass;
data.I = this.getInertia();
data.center.set(this.m_sweep.localCenter);
};
/**
* This resets the mass properties to the sum of the mass properties of the
* fixtures. This normally does not need to be called unless you called
* SetMassData to override the mass and you later want to reset the mass.
*/
Body.prototype.resetMassData = function() {
// Compute mass data from shapes. Each shape has its own density.
this.m_mass = 0.0;
this.m_invMass = 0.0;
this.m_I = 0.0;
this.m_invI = 0.0;
this.m_sweep.localCenter.setZero();
// Static and kinematic bodies have zero mass.
if (this.isStatic() || this.isKinematic()) {
this.m_sweep.c0.set(this.m_xf.p);
this.m_sweep.c.set(this.m_xf.p);
this.m_sweep.a0 = this.m_sweep.a;
return;
}
_ASSERT && common.assert(this.isDynamic());
// Accumulate mass over all fixtures.
var localCenter = Vec2.zero();
for (var f = this.m_fixtureList; f; f = f.m_next) {
if (f.m_density == 0.0) {
continue;
}
var massData = new MassData();
f.getMassData(massData);
this.m_mass += massData.mass;
localCenter.addMul(massData.mass, massData.center);
this.m_I += massData.I;
}
// Compute center of mass.
if (this.m_mass > 0.0) {
this.m_invMass = 1.0 / this.m_mass;
localCenter.mul(this.m_invMass);
} else {
// Force all dynamic bodies to have a positive mass.
this.m_mass = 1.0;
this.m_invMass = 1.0;
}
if (this.m_I > 0.0 && this.m_fixedRotationFlag == false) {
// Center the inertia about the center of mass.
this.m_I -= this.m_mass * Vec2.dot(localCenter, localCenter);
_ASSERT && common.assert(this.m_I > 0.0);
this.m_invI = 1.0 / this.m_I;
} else {
this.m_I = 0.0;
this.m_invI = 0.0;
}
// Move center of mass.
var oldCenter = Vec2.clone(this.m_sweep.c);
this.m_sweep.setLocalCenter(localCenter, this.m_xf);
// Update center of mass velocity.
this.m_linearVelocity.add(Vec2.cross(this.m_angularVelocity, Vec2.sub(
this.m_sweep.c, oldCenter)));
};
/**
* Set the mass properties to override the mass properties of the fixtures. Note
* that this changes the center of mass position. Note that creating or
* destroying fixtures can also alter the mass. This function has no effect if
* the body isn't dynamic.
*
* @param massData The mass properties.
*/
Body.prototype.setMassData = function(massData) {
_ASSERT && common.assert(this.isWorldLocked() == false);
if (this.isWorldLocked() == true) {
return;
}
if (this.m_type != dynamicBody) {
return;
}
this.m_invMass = 0.0;
this.m_I = 0.0;
this.m_invI = 0.0;
this.m_mass = massData.mass;
if (this.m_mass <= 0.0) {
this.m_mass = 1.0;
}
this.m_invMass = 1.0 / this.m_mass;
if (massData.I > 0.0 && this.m_fixedRotationFlag == false) {
this.m_I = massData.I - this.m_mass
* Vec2.dot(massData.center, massData.center);
_ASSERT && common.assert(this.m_I > 0.0);
this.m_invI = 1.0 / this.m_I;
}
// Move center of mass.
var oldCenter = Vec2.clone(this.m_sweep.c);
this.m_sweep.setLocalCenter(massData.center, this.m_xf);
// Update center of mass velocity.
this.m_linearVelocity.add(Vec2.cross(this.m_angularVelocity, Vec2.sub(
this.m_sweep.c, oldCenter)));
};
/**
* Apply a force at a world point. If the force is not applied at the center of
* mass, it will generate a torque and affect the angular velocity. This wakes
* up the body.
*
* @param force The world force vector, usually in Newtons (N).
* @param point The world position of the point of application.
* @param wake Also wake up the body
*/
Body.prototype.applyForce = function(force, point, wake) {
if (this.m_type != dynamicBody) {
return;
}
if (wake && this.m_awakeFlag == false) {
this.setAwake(true);
}
// Don't accumulate a force if the body is sleeping.
if (this.m_awakeFlag) {
this.m_force.add(force);
this.m_torque += Vec2.cross(Vec2.sub(point, this.m_sweep.c), force);
}
};
/**
* Apply a force to the center of mass. This wakes up the body.
*
* @param force The world force vector, usually in Newtons (N).
* @param wake Also wake up the body
*/
Body.prototype.applyForceToCenter = function(force, wake) {
if (this.m_type != dynamicBody) {
return;
}
if (wake && this.m_awakeFlag == false) {
this.setAwake(true);
}
// Don't accumulate a force if the body is sleeping
if (this.m_awakeFlag) {
this.m_force.add(force);
}
};
/**
* Apply a torque. This affects the angular velocity without affecting the
* linear velocity of the center of mass. This wakes up the body.
*
* @param torque About the z-axis (out of the screen), usually in N-m.
* @param wake Also wake up the body
*/
Body.prototype.applyTorque = function(torque, wake) {
if (this.m_type != dynamicBody) {
return;
}
if (wake && this.m_awakeFlag == false) {
this.setAwake(true);
}
// Don't accumulate a force if the body is sleeping
if (this.m_awakeFlag) {
this.m_torque += torque;
}
};
/**
* Apply an impulse at a point. This immediately modifies the velocity. It also
* modifies the angular velocity if the point of application is not at the
* center of mass. This wakes up the body.
*
* @param impulse The world impulse vector, usually in N-seconds or kg-m/s.
* @param point The world position of the point of application.
* @param wake Also wake up the body
*/
Body.prototype.applyLinearImpulse = function(impulse, point, wake) {
if (this.m_type != dynamicBody) {
return;
}
if (wake && this.m_awakeFlag == false) {
this.setAwake(true);
}
// Don't accumulate velocity if the body is sleeping
if (this.m_awakeFlag) {
this.m_linearVelocity.addMul(this.m_invMass, impulse);
this.m_angularVelocity += this.m_invI * Vec2.cross(Vec2.sub(point, this.m_sweep.c), impulse);
}
};
/**
* Apply an angular impulse.
*
* @param impulse The angular impulse in units of kg*m*m/s
* @param wake Also wake up the body
*/
Body.prototype.applyAngularImpulse = function(impulse, wake) {
if (this.m_type != dynamicBody) {
return;
}
if (wake && this.m_awakeFlag == false) {
this.setAwake(true);
}
// Don't accumulate velocity if the body is sleeping
if (this.m_awakeFlag) {
this.m_angularVelocity += this.m_invI * impulse;
}
};
/**
* This is used to prevent connected bodies (by joints) from colliding,
* depending on the joint's collideConnected flag.
*/
Body.prototype.shouldCollide = function(that) {
// At least one body should be dynamic.
if (this.m_type != dynamicBody && that.m_type != dynamicBody) {
return false;
}
// Does a joint prevent collision?
for (var jn = this.m_jointList; jn; jn = jn.next) {
if (jn.other == that) {
if (jn.joint.m_collideConnected == false) {
return false;
}
}
}
return true;
};
/**
* @internal Used for deserialize.
*/
Body.prototype._addFixture = function(fixture) {
_ASSERT && common.assert(this.isWorldLocked() == false);
if (this.isWorldLocked() == true) {
return null;
}
if (this.m_activeFlag) {
var broadPhase = this.m_world.m_broadPhase;
fixture.createProxies(broadPhase, this.m_xf);
}
fixture.m_next = this.m_fixtureList;
this.m_fixtureList = fixture;
// Adjust mass properties if needed.
if (fixture.m_density > 0.0) {
this.resetMassData();
}
// Let the world know we have a new fixture. This will cause new contacts
// to be created at the beginning of the next time step.
this.m_world.m_newFixture = true;
return fixture
};
/**
* Creates a fixture and attach it to this body.
*
* If the density is non-zero, this function automatically updates the mass of
* the body.
*
* Contacts are not created until the next time step.
*
* Warning: This function is locked during callbacks.
* @param {Shape|FixtureDef} shape Shape or fixture definition.
* @param {FixtureDef|number} fixdef Fixture definition or just density.
*/
Body.prototype.createFixture = function(shape, fixdef) {
_ASSERT && common.assert(this.isWorldLocked() == false);
if (this.isWorldLocked() == true) {
return null;
}
var fixture = new Fixture(this, shape, fixdef);
this._addFixture(fixture);
return fixture
};
/**
* Destroy a fixture. This removes the fixture from the broad-phase and destroys
* all contacts associated with this fixture. This will automatically adjust the
* mass of the body if the body is dynamic and the fixture has positive density.
* All fixtures attached to a body are implicitly destroyed when the body is
* destroyed.
*
* Warning: This function is locked during callbacks.
*
* @param fixture The fixture to be removed.
*/
Body.prototype.destroyFixture = function(fixture) {
_ASSERT && common.assert(this.isWorldLocked() == false);
if (this.isWorldLocked() == true) {
return;
}
_ASSERT && common.assert(fixture.m_body == this);
// Remove the fixture from this body's singly linked list.
var found = false;
if (this.m_fixtureList === fixture) {
this.m_fixtureList = fixture.m_next;
found = true;
} else {
var node = this.m_fixtureList;
while (node != null) {
if (node.m_next === fixture) {
node.m_next = fixture.m_next;
found = true;
break;
}
node = node.m_next;
}
}
// You tried to remove a shape that is not attached to this body.
_ASSERT && common.assert(found);
// Destroy any contacts associated with the fixture.
var edge = this.m_contactList;
while (edge) {
var c = edge.contact;
edge = edge.next;
var fixtureA = c.getFixtureA();
var fixtureB = c.getFixtureB();
if (fixture == fixtureA || fixture == fixtureB) {
// This destroys the contact and removes it from
// this body's contact list.
this.m_world.destroyContact(c);
}
}
if (this.m_activeFlag) {
var broadPhase = this.m_world.m_broadPhase;
fixture.destroyProxies(broadPhase);
}
fixture.m_body = null;
fixture.m_next = null;
this.m_world.publish('remove-fixture', fixture);
// Reset the mass data.
this.resetMassData();
};
/**
* Get the corresponding world point of a local point.
*/
Body.prototype.getWorldPoint = function(localPoint) {
return Transform.mulVec2(this.m_xf, localPoint);
};
/**
* Get the corresponding world vector of a local vector.
*/
Body.prototype.getWorldVector = function(localVector) {
return Rot.mulVec2(this.m_xf.q, localVector);
};
/**
* Gets the corresponding local point of a world point.
*/
Body.prototype.getLocalPoint = function(worldPoint) {
return Transform.mulTVec2(this.m_xf, worldPoint);
};
/**
*
* Gets the corresponding local vector of a world vector.
*/
Body.prototype.getLocalVector = function(worldVector) {
return Rot.mulTVec2(this.m_xf.q, worldVector);
};