UNPKG

p2s

Version:

A JavaScript 2D physics engine.

492 lines (431 loc) 15.3 kB
var Body = require(__dirname + '/../../src/objects/Body'); var Circle = require(__dirname + '/../../src/shapes/Circle'); var Box = require(__dirname + '/../../src/shapes/Box'); var World = require(__dirname + '/../../src/world/World'); var AABB = require(__dirname + '/../../src/collision/AABB'); var vec2 = require(__dirname + '/../../src/math/vec2'); var Plane = require(__dirname + '/../../src/shapes/Plane'); module.exports = { construct: function(test){ // Static via mass=0 var body = new Body({ mass : 0 }); test.equal(body.invMass,0); test.equal(body.type,Body.STATIC); // Setting things via options var o = { position:[0,1], velocity:[1,2], force:[3,4], angularVelocity:5, angularForce:5, angle:Math.PI/2 }; body = new Body(o); test.deepEqual(body.position, o.position); test.deepEqual(body.interpolatedPosition, o.position); test.deepEqual(body.previousPosition, o.position); test.deepEqual(body.velocity, o.velocity); test.deepEqual(body.force, o.force); test.equal(body.angle, o.angle); test.equal(body.previousAngle, o.angle); test.equal(body.interpolatedAngle, o.angle); test.equal(body.angularVelocity, o.angularVelocity); test.equal(body.angularForce, o.angularForce); // id tick test.notEqual(new Body().id, new Body().id); test.done(); }, addShape: { normal: function(test){ var body = new Body(); var shape = new Circle({ radius: 1 }); body.addShape(shape); test.deepEqual(body.shapes, [shape]); test.done(); }, duringStep: function(test){ var world = new World(); var body = new Body(); world.addBody(body); world.on('postBroadphase', function(){ test.throws(function(){ body.addShape(new Circle()); }, 'should throw on adding shapes during step'); test.done(); }); world.step(1); } }, adjustCenterOfMass: function(test){ var body = new Body(); var shape = new Circle({ radius: 1 }); body.addShape(shape, [1, 0], 0); body.adjustCenterOfMass(); test.deepEqual(body.position, vec2.fromValues(1,0)); test.deepEqual(body.shapes[0].position, vec2.fromValues(0,0)); test.done(); }, applyDamping: function(test){ var body = new Body({ mass: 1, velocity: [1, 0], angularVelocity: 1, damping: 0.5, angularDamping: 0.5 }); body.applyDamping(1); test.deepEqual(body.velocity, [0.5, 0]); test.deepEqual(body.angularVelocity, 0.5); test.done(); }, applyForce: { withPoint: function(test){ var body = new Body({ mass: 1, position: [2,3] }); var force = [0,1]; var point = [1,0]; body.applyForce(force, point); test.equal(body.force[0], 0); test.equal(body.force[1], 1); test.equal(body.angularForce, 1); // [1,0,0] cross [0,1,0] is [0,0,1] test.done(); }, withoutPoint: function(test){ var body = new Body({ mass: 1, position: [2,3] }); var force = [0,1]; body.applyForce(force); test.equal(body.force[0], 0); test.equal(body.force[1], 1); test.equal(body.angularForce, 0); test.done(); }, }, applyForceLocal: { withPoint: function(test){ var bodyA = new Body({ mass: 1, position: [2,3], angle: Math.PI // rotated 180 degrees }); bodyA.addShape(new Circle({ radius: 1 })); bodyA.applyForceLocal([-1,0],[0,1]); test.ok(bodyA.angularForce > 0); test.ok(bodyA.force[0] > 0); test.ok(Math.abs(bodyA.force[1]) < 0.001); test.done(); }, withoutPoint: function(test){ var bodyA = new Body({ mass: 1, position: [2,3], angle: Math.PI // rotated 180 degrees }); bodyA.addShape(new Circle({ radius: 1 })); bodyA.applyForceLocal([-1,0]); test.equal(bodyA.angularForce, 0); test.ok(bodyA.force[0] > 0); test.ok(Math.abs(bodyA.force[1]) < 0.001); test.done(); } }, applyImpulse: { withPoint: function(test){ var bodyA = new Body({ mass: 1, position: [2,3] }); bodyA.addShape(new Circle({ radius: 1 })); bodyA.applyImpulse([-1,0],[0,1]); test.ok(bodyA.angularVelocity !== 0); test.ok(bodyA.velocity[0] !== 0); test.equal(bodyA.velocity[1], 0); test.done(); }, withoutPoint: function(test){ var bodyA = new Body({ mass: 1, position: [2,3] }); bodyA.addShape(new Circle({ radius: 1 })); bodyA.applyImpulse([-1,0]); test.equal(bodyA.angularVelocity, 0); test.ok(bodyA.velocity[0] !== 0); test.equal(bodyA.velocity[1], 0); test.done(); } }, applyImpulseLocal: { withPoint: function(test){ var bodyA = new Body({ mass: 1, position: [2,3], angle: Math.PI // rotated 180 degrees }); bodyA.addShape(new Circle({ radius: 1 })); bodyA.applyImpulseLocal([-1,0],[0,1]); test.ok(bodyA.angularVelocity > 0); test.ok(bodyA.velocity[0] > 0); test.ok(Math.abs(bodyA.velocity[1]) < 0.001); test.done(); }, withoutPoint: function(test){ var bodyA = new Body({ mass: 1, position: [2,3], angle: Math.PI // rotated 180 degrees }); bodyA.addShape(new Circle({ radius: 1 })); bodyA.applyImpulseLocal([-1,0]); test.equal(bodyA.angularVelocity, 0); test.ok(bodyA.velocity[0] > 0); test.ok(Math.abs(bodyA.velocity[1]) < 0.001); test.done(); } }, fromPolygon: function(test){ var b = new Body(); test.ok(b.fromPolygon( [[-1, 1], [-1, 0], [1, 0], [1, 1], [0.5, 0.5]])); test.ok(b.shapes.length > 0); test.done(); }, overlaps: function(test){ var bodyA = new Body({ mass: 1 }); var bodyB = new Body({ mass: 1 }); bodyA.addShape(new Circle()); bodyB.addShape(new Circle()); var world = new World(); world.addBody(bodyA); world.addBody(bodyB); world.step(1/60); test.ok(bodyA.overlaps(bodyB)); test.done(); }, removeShape: { canRemove: function(test){ var body = new Body(); body.addShape(new Circle({ radius: 1 })); test.ok(body.removeShape(body.shapes[0])); test.ok(!body.removeShape(new Circle({ radius: 1 }))); test.equal(body.shapes.length, 0); test.done(); }, duringStep: function(test){ var world = new World(); var body = new Body(); var shape = new Circle(); world.addBody(body); body.addShape(shape); world.on('postBroadphase', function(){ test.throws(function(){ body.removeShape(shape); }, 'should throw on removing shapes during step'); test.done(); }); world.step(1); } }, getArea: function(test){ var body = new Body(); body.addShape(new Box({ width: 1, height: 1 })); test.equal(body.getArea(), 1); test.done(); }, getAABB: function(test){ var body = new Body(); body.addShape(new Box({ width: 1, height: 1 })); test.deepEqual(body.getAABB(), new AABB({ lowerBound: [-0.5, -0.5], upperBound: [0.5, 0.5] })); test.done(); }, setDensity: function(test){ var body = new Body({ mass: 1 }); body.addShape(new Circle({ radius: 1 })); var inertiaBefore = body.inertia; body.setDensity(10); test.equal(body.mass, body.getArea() * 10); test.ok(inertiaBefore !== body.inertia); test.done(); }, setZeroForce: function(test){ var b = new Body({ force:[1,2], angularForce:3 }); b.setZeroForce(); test.equal(vec2.length(b.force),0); test.equal(b.angularForce,0); test.done(); }, sleep: function(test){ var b = new Body({ mass: 1 }); test.equal(b.sleepState, Body.AWAKE); b.sleep(); test.equal(b.sleepState, Body.SLEEPING); test.done(); }, toLocalFrame: function(test){ var b = new Body({ position: [1,1] }); var localPoint = [123, 456]; b.toLocalFrame(localPoint, [1,1]); test.deepEqual(localPoint, [0,0]); test.done(); }, toWorldFrame: function(test){ var b = new Body({ position: [1,1] }); var worldPoint = [123, 456]; b.toWorldFrame(worldPoint, [1,1]); test.deepEqual(worldPoint, [2,2]); test.done(); }, vectorToLocalFrame: function(test){ var b = new Body({ angle: Math.PI, position: [1,1] }); var v = [1, 0]; b.vectorToLocalFrame(v, v); test.ok(vec2.distance(v, [-1,0]) < 0.01); test.done(); }, vectorToWorldFrame: function(test){ var b = new Body({ angle: Math.PI, position: [1,1] }); var v = [1, 0]; b.vectorToWorldFrame(v, v); test.ok(vec2.distance(v, [-1,0]) < 0.01); test.done(); }, updateAABB: function(test){ var b = new Body(); b.updateAABB(); var b = new Body(), s = new Circle({ radius: 1 }); b.addShape(s); b.updateAABB(); test.equal(b.aabb.lowerBound[0], -1, 'Lower AABB bound should be -1'); test.equal(b.aabb.upperBound[0], 1, 'Upper AABB bound should be 1'); test.equal(b.aabb.lowerBound[1], -1, 'Lower AABB bound should be -1'); test.equal(b.aabb.upperBound[1], 1, 'Upper AABB bound should be 1'); var b = new Body(), s = new Circle({ radius: 1 }), offset = [-2,3]; b.addShape(s,offset,Math.PI/2); b.updateAABB(); test.equal(b.aabb.lowerBound[0], -s.radius + offset[0]); test.equal(b.aabb.upperBound[0], s.radius + offset[0]); test.equal(b.aabb.lowerBound[1], -s.radius + offset[1]); test.equal(b.aabb.upperBound[1], s.radius + offset[1]); test.done(); }, updateBoundingRadius: function(test){ var body = new Body({ mass: 1 }); var shape = new Circle({ radius: 1 }); body.addShape(shape); test.equal(body.boundingRadius, 1); shape.radius = 2; shape.updateBoundingRadius(); body.updateBoundingRadius(); test.equal(body.boundingRadius, 2); test.done(); }, updateMassProperties: function(test){ // STUB test.done(); }, wakeUp: function(test){ var b = new Body({ mass: 1 }); b.sleep(); test.equal(b.sleepState, Body.SLEEPING); b.wakeUp(); test.equal(b.sleepState, Body.AWAKE); test.done(); }, integrate: { withoutCCD: function(test){ var body = new Body({ velocity: [1,0], mass: 1 }); var world = new World(); world.addBody(body); body.integrate(1); test.deepEqual(body.position, vec2.fromValues(1,0)); test.done(); }, withCCD: function(test){ var body = new Body({ velocity: [2,0], position: [-1,0], mass: 1, ccdSpeedThreshold: 0, shape: new Circle({ radius: 0.01 }) }); var world = new World(); world.addBody(body); body.integrate(1); test.deepEqual(body.position, vec2.fromValues(1,0)); test.done(); }, withCCDAndObstacle: function(test){ var world = new World({ gravity: [0,0] }); var body = new Body({ velocity: [2,0], position: [-1,0], mass: 1, ccdSpeedThreshold: 0, ccdIterations: 10, }); body.addShape(new Circle({ radius: 0.01 })); world.addBody(body); var planeBody = new Body({ mass: 0, angle: Math.PI / 2 }); planeBody.addShape(new Plane()); world.addBody(planeBody); world.step(1); // Need to use world.step() instead of body.integrate() test.ok(vec2.distance(body.position, [0,0]) < 0.1); test.done(); } }, getVelocityAtPoint: function(test){ var body = new Body({ mass: 1, velocity: [1,0] }); var velocity = [0,0]; body.getVelocityAtPoint(velocity, [0,0]); test.deepEqual(velocity, [1,0]); body.velocity[0] = 0; body.angularVelocity = 1; body.getVelocityAtPoint(velocity, [1,0]); test.ok(Math.abs(velocity[0]) < 0.001); test.equal(velocity[1], 1); // r x w = 1 x 1 = 1 test.done(); }, collisionResponse: function(test){ var bodyA = new Body({ mass: 1, position: [1, 0] }); bodyA.addShape(new Circle({ radius: 1 })); var bodyB = new Body({ mass: 1, position: [-1, 0] }); bodyB.addShape(new Circle({ radius: 1 })); var world = new World(); world.addBody(bodyA); world.addBody(bodyB); world.step(1 / 60); test.ok(world.narrowphase.contactEquations[0].enabled); bodyA.collisionResponse = false; world.step(1 / 60); test.ok(!world.narrowphase.contactEquations[0].enabled); bodyA.collisionResponse = true; bodyA.shapes[0].collisionResponse = false; world.step(1 / 60); test.ok(!world.narrowphase.contactEquations[0].enabled); test.done(); }, index: function(test){ var bodyA = new Body(); var bodyB = new Body(); test.equal(bodyA.index, -1); test.equal(bodyB.index, -1); var world = new World(); world.addBody(bodyA); world.addBody(bodyB); test.equal(bodyA.index, 0); test.equal(bodyB.index, 1); world.removeBody(bodyA); test.equal(bodyA.index, -1); test.equal(bodyB.index, 0); test.done(); } };