UNPKG

planck-js

Version:

2D physics engine for JavaScript/HTML5 game development

1,044 lines (886 loc) 27.2 kB
/* * Copyright (c) 2016 Ali Shakiba http://shakiba.me/planck.js * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ module.exports = Body; 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 World = require('./World'); 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 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. */ function BodyDef() { this.type = staticBody; this.position = new Vec2(); this.angle = 0.0; this.linearVelocity = new Vec2(); this.angularVelocity = 0.0; this.linearDamping = 0.0; this.angularDamping = 0.0; this.fixedRotation = false; this.bullet = false; this.gravityScale = 1.0; this.allowSleep = true; this.awake = true; this.active = true; this.userData = null; } BodyDef.DEFULTS = new BodyDef(); /** * @class * * A rigid body composed of one or more fixtures. * * @param {BodyDef} def */ function Body(world, def) { def = options(def, BodyDef.DEFULTS); Assert(Vec2.IsValid(def.position)); Assert(Vec2.IsValid(def.linearVelocity)); Assert(Math.isFinite(def.angle)); Assert(Math.isFinite(def.angularVelocity)); Assert(Math.isFinite(def.angularDamping) && def.angularDamping >= 0.0); 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 = new Transform(def.position, 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 = new Vec2(); this.m_torque = 0.0; this.m_linearVelocity = Vec2(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_fixtureCount = 0; this.m_prev = null; this.m_next = null; } 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) { // TODO assert type 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(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(this.IsWorldLocked() == false); if (this.IsWorldLocked() == true) { return; } this.m_xf.q.Set(angle); this.m_xf.p.Set(position); 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(alpha) { this.m_sweep.GetTransform(this.m_xf, 1); }; /** * Update fixtures in broad-phase. */ Body.prototype.SynchronizeFixtures = function() { var xf = new Transform(); 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(); 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(this.IsDynamic()); // Accumulate mass over all fixtures. var localCenter = Vec2(); 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.WAdd(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(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(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(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(this.m_I > 0.0); this.m_invI = 1.0 / this.m_I; } // Move center of mass. var oldCenter = Vec2(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 += this.m_invMass * impulse; this.m_angularVelocity += this.m_invI * Vec2.Cross(Vec2Sub(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; }; /** * Creates a fixture and attach it to this body. Use this function if you need * to set some fixture parameters, like friction. Otherwise you can create the * fixture directly from a shape. * * 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 def the fixture definition. * * Creates a fixture from a shape and attach it to this body. * * This is a convenience function. Use b2FixtureDef if you need to set * parameters like friction, restitution, user data, or filtering. * * If the density is non-zero, this function automatically updates the mass of * the body. * * @param shape the shape to be cloned. * @param density the shape density (set to zero for static bodies). */ Body.prototype.CreateFixture = function(shape, fixdef) { Assert(!!shape && typeof shape.Clone === 'function'); Assert(this.IsWorldLocked() == false); if (this.IsWorldLocked() == true) { return null; } var fixture = new Fixture(this, shape, fixdef) 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; ++this.m_fixtureCount; // 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; }; /** * 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(this.IsWorldLocked() == false); if (this.IsWorldLocked() == true) { return; } Assert(fixture.m_body == this); // Remove the fixture from this body's singly linked list. Assert(this.m_fixtureCount > 0); var node = this.m_fixtureList; var found = false; while (node != null) { if (node == fixture) { node = fixture.m_next; found = true; break; } node = node.m_next; } // You tried to remove a shape that is not attached to this body. 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.Destroy(); fixture.m_body = null; fixture.m_next = null; --this.m_fixtureCount; // Reset the mass data. this.ResetMassData(); }; /** * Get the corresponding world point of a local point. */ Body.prototype.GetWorldPoint = function(localPoint) { return Transform.Mul(this.m_xf, localPoint); }; /** * Get the corresponding world vector of a local vector. */ Body.prototype.GetWorldVector = function(localVector) { return Rot.Mul(this.m_xf.q, localVector); }; /** * Gets the corresponding local point of a world point. */ Body.prototype.GetLocalPoint = function(worldPoint) { return Transform.MulT(this.m_xf, worldPoint); }; /** * * Gets the corresponding local vector of a world vector. */ Body.prototype.GetLocalVector = function(worldVector) { return Rot.MulT(this.m_xf.q, worldVector); };