mirror of
https://github.com/liabru/matter-js.git
synced 2024-11-27 09:50:52 -05:00
Merge branch 'timing-improve'
This commit is contained in:
commit
b81d6e6d7f
18 changed files with 589 additions and 322 deletions
|
@ -50,8 +50,8 @@ Example.bridge = function() {
|
|||
});
|
||||
|
||||
Composites.chain(bridge, 0.3, 0, -0.3, 0, {
|
||||
stiffness: 1,
|
||||
length: 0,
|
||||
stiffness: 0.99,
|
||||
length: 0.0001,
|
||||
render: {
|
||||
visible: false
|
||||
}
|
||||
|
|
|
@ -50,19 +50,20 @@ Example.compositeManipulation = function() {
|
|||
engine.gravity.y = 0;
|
||||
|
||||
Events.on(engine, 'afterUpdate', function(event) {
|
||||
var time = engine.timing.timestamp;
|
||||
var time = engine.timing.timestamp,
|
||||
timeScale = (event.delta || (1000 / 60)) / 1000;
|
||||
|
||||
Composite.translate(stack, {
|
||||
x: Math.sin(time * 0.001) * 2,
|
||||
x: Math.sin(time * 0.001) * 10 * timeScale,
|
||||
y: 0
|
||||
});
|
||||
|
||||
Composite.rotate(stack, Math.sin(time * 0.001) * 0.01, {
|
||||
Composite.rotate(stack, Math.sin(time * 0.001) * 0.75 * timeScale, {
|
||||
x: 300,
|
||||
y: 300
|
||||
});
|
||||
|
||||
var scale = 1 + (Math.sin(time * 0.001) * 0.01);
|
||||
var scale = 1 + (Math.sin(time * 0.001) * 0.75 * timeScale);
|
||||
|
||||
Composite.scale(stack, scale, scale, {
|
||||
x: 300,
|
||||
|
|
|
@ -39,13 +39,19 @@ Example.events = function() {
|
|||
// do something with event.object
|
||||
});
|
||||
|
||||
var lastTime = Common.now();
|
||||
|
||||
// an example of using beforeUpdate event on an engine
|
||||
Events.on(engine, 'beforeUpdate', function(event) {
|
||||
var engine = event.source;
|
||||
|
||||
// apply random forces every 5 secs
|
||||
if (event.timestamp % 5000 < 50)
|
||||
if (Common.now() - lastTime >= 5000) {
|
||||
shakeScene(engine);
|
||||
|
||||
// update last time
|
||||
lastTime = Common.now();
|
||||
}
|
||||
});
|
||||
|
||||
// an example of using collisionStart event on an engine
|
||||
|
@ -102,14 +108,17 @@ Example.events = function() {
|
|||
Composite.add(world, stack);
|
||||
|
||||
var shakeScene = function(engine) {
|
||||
var timeScale = (1000 / 60) / engine.timing.lastDelta;
|
||||
var bodies = Composite.allBodies(engine.world);
|
||||
|
||||
for (var i = 0; i < bodies.length; i++) {
|
||||
var body = bodies[i];
|
||||
|
||||
if (!body.isStatic && body.position.y >= 500) {
|
||||
var forceMagnitude = 0.02 * body.mass;
|
||||
// scale force for mass and time applied
|
||||
var forceMagnitude = (0.03 * body.mass) * timeScale;
|
||||
|
||||
// apply the force over a single update
|
||||
Body.applyForce(body, body.position, {
|
||||
x: (forceMagnitude + Common.random() * forceMagnitude) * Common.choose([1, -1]),
|
||||
y: -forceMagnitude + Common.random() * -forceMagnitude
|
||||
|
|
|
@ -35,20 +35,24 @@ Example.manipulation = function() {
|
|||
Runner.run(runner, engine);
|
||||
|
||||
// add bodies
|
||||
var bodyA = Bodies.rectangle(100, 200, 50, 50, { isStatic: true, render: { fillStyle: '#060a19' } }),
|
||||
var bodyA = Bodies.rectangle(100, 300, 50, 50, { isStatic: true, render: { fillStyle: '#060a19' } }),
|
||||
bodyB = Bodies.rectangle(200, 200, 50, 50),
|
||||
bodyC = Bodies.rectangle(300, 200, 50, 50),
|
||||
bodyD = Bodies.rectangle(400, 200, 50, 50),
|
||||
bodyE = Bodies.rectangle(550, 200, 50, 50),
|
||||
bodyF = Bodies.rectangle(700, 200, 50, 50),
|
||||
bodyG = Bodies.circle(400, 100, 25, { render: { fillStyle: '#060a19' } }),
|
||||
partA = Bodies.rectangle(600, 200, 120, 50, { render: { fillStyle: '#060a19' } }),
|
||||
partB = Bodies.rectangle(660, 200, 50, 190, { render: { fillStyle: '#060a19' } }),
|
||||
bodyG = Bodies.circle(400, 100, 25, { render: { fillStyle: '#060a19' } });
|
||||
|
||||
// add compound body
|
||||
var partA = Bodies.rectangle(600, 200, 120 * 0.8, 50 * 0.8, { render: { fillStyle: '#060a19' } }),
|
||||
partB = Bodies.rectangle(660, 200, 50 * 0.8, 190 * 0.8, { render: { fillStyle: '#060a19' } }),
|
||||
compound = Body.create({
|
||||
parts: [partA, partB],
|
||||
isStatic: true
|
||||
});
|
||||
|
||||
Body.setPosition(compound, { x: 600, y: 300 });
|
||||
|
||||
Composite.add(world, [bodyA, bodyB, bodyC, bodyD, bodyE, bodyF, bodyG, compound]);
|
||||
|
||||
Composite.add(world, [
|
||||
|
@ -59,48 +63,57 @@ Example.manipulation = function() {
|
|||
Bodies.rectangle(0, 300, 50, 600, { isStatic: true })
|
||||
]);
|
||||
|
||||
var counter = 0,
|
||||
scaleFactor = 1.01;
|
||||
var lastTime = 0,
|
||||
scaleRate = 0.6;
|
||||
|
||||
Events.on(runner, 'afterTick', function(event) {
|
||||
counter += 1;
|
||||
Events.on(engine, 'beforeUpdate', function(event) {
|
||||
var timeScale = (event.delta || (1000 / 60)) / 1000;
|
||||
|
||||
if (counter === 40)
|
||||
Body.setStatic(bodyG, true);
|
||||
|
||||
if (scaleFactor > 1) {
|
||||
Body.scale(bodyF, scaleFactor, scaleFactor);
|
||||
Body.scale(compound, 0.995, 0.995);
|
||||
if (scaleRate > 0) {
|
||||
Body.scale(bodyF, 1 + (scaleRate * timeScale), 1 + (scaleRate * timeScale));
|
||||
|
||||
// modify bodyE vertices
|
||||
bodyE.vertices[0].x -= 0.2;
|
||||
bodyE.vertices[0].y -= 0.2;
|
||||
bodyE.vertices[1].x += 0.2;
|
||||
bodyE.vertices[1].y -= 0.2;
|
||||
bodyE.vertices[0].x -= 0.2 * timeScale;
|
||||
bodyE.vertices[0].y -= 0.2 * timeScale;
|
||||
bodyE.vertices[1].x += 0.2 * timeScale;
|
||||
bodyE.vertices[1].y -= 0.2 * timeScale;
|
||||
Body.setVertices(bodyE, bodyE.vertices);
|
||||
}
|
||||
|
||||
// make bodyA move up and down
|
||||
// body is static so must manually update velocity for friction to work
|
||||
var py = 300 + 100 * Math.sin(engine.timing.timestamp * 0.002);
|
||||
Body.setVelocity(bodyA, { x: 0, y: py - bodyA.position.y });
|
||||
Body.setPosition(bodyA, { x: 100, y: py });
|
||||
|
||||
// make compound body move up and down and rotate constantly
|
||||
Body.setVelocity(compound, { x: 0, y: py - compound.position.y });
|
||||
Body.setAngularVelocity(compound, 0.02);
|
||||
Body.setPosition(compound, { x: 600, y: py });
|
||||
Body.rotate(compound, 0.02);
|
||||
// manual update velocity required for older releases
|
||||
if (Matter.version === '0.18.0') {
|
||||
Body.setVelocity(bodyA, { x: 0, y: py - bodyA.position.y });
|
||||
Body.setVelocity(compound, { x: 0, y: py - compound.position.y });
|
||||
Body.setAngularVelocity(compound, 1 * Math.PI * timeScale);
|
||||
}
|
||||
|
||||
// every 1.5 sec
|
||||
if (counter >= 60 * 1.5) {
|
||||
// move body and update velocity
|
||||
Body.setPosition(bodyA, { x: 100, y: py }, true);
|
||||
|
||||
// move compound body move up and down and update velocity
|
||||
Body.setPosition(compound, { x: 600, y: py }, true);
|
||||
|
||||
// rotate compound body and update angular velocity
|
||||
Body.rotate(compound, 1 * Math.PI * timeScale, null, true);
|
||||
|
||||
// after first 0.8 sec (simulation time)
|
||||
if (engine.timing.timestamp >= 800)
|
||||
Body.setStatic(bodyG, true);
|
||||
|
||||
// every 1.5 sec (simulation time)
|
||||
if (engine.timing.timestamp - lastTime >= 1500) {
|
||||
Body.setVelocity(bodyB, { x: 0, y: -10 });
|
||||
Body.setAngle(bodyC, -Math.PI * 0.26);
|
||||
Body.setAngularVelocity(bodyD, 0.2);
|
||||
|
||||
// reset counter
|
||||
counter = 0;
|
||||
scaleFactor = 1;
|
||||
// stop scaling
|
||||
scaleRate = 0;
|
||||
|
||||
// update last time
|
||||
lastTime = engine.timing.timestamp;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ Example.newtonsCradle.newtonsCradle = function(xx, yy, number, size, length) {
|
|||
for (var i = 0; i < number; i++) {
|
||||
var separation = 1.9,
|
||||
circle = Bodies.circle(xx + i * (size * separation), yy + length, size,
|
||||
{ inertia: Infinity, restitution: 1, friction: 0, frictionAir: 0.0001, slop: 1 }),
|
||||
{ inertia: Infinity, restitution: 1, friction: 0, frictionAir: 0, slop: size * 0.02 }),
|
||||
constraint = Constraint.create({ pointA: { x: xx + i * (size * separation), y: yy }, bodyB: circle });
|
||||
|
||||
Composite.addBody(newtonsCradle, circle);
|
||||
|
|
|
@ -9,11 +9,9 @@ Example.ragdoll = function() {
|
|||
Common = Matter.Common,
|
||||
Composite = Matter.Composite,
|
||||
Composites = Matter.Composites,
|
||||
Constraint = Matter.Constraint,
|
||||
MouseConstraint = Matter.MouseConstraint,
|
||||
Mouse = Matter.Mouse,
|
||||
Bodies = Matter.Bodies,
|
||||
Vector = Matter.Vector;
|
||||
Bodies = Matter.Bodies;
|
||||
|
||||
// create engine
|
||||
var engine = Engine.create(),
|
||||
|
@ -82,21 +80,20 @@ Example.ragdoll = function() {
|
|||
Composite.add(world, [stack, obstacles, ragdolls]);
|
||||
|
||||
var timeScaleTarget = 1,
|
||||
counter = 0;
|
||||
lastTime = Common.now();
|
||||
|
||||
Events.on(engine, 'afterUpdate', function(event) {
|
||||
var timeScale = (event.delta || (1000 / 60)) / 1000;
|
||||
|
||||
// tween the timescale for slow-mo
|
||||
if (mouse.button === -1) {
|
||||
engine.timing.timeScale += (timeScaleTarget - engine.timing.timeScale) * 0.05;
|
||||
engine.timing.timeScale += (timeScaleTarget - engine.timing.timeScale) * 3 * timeScale;
|
||||
} else {
|
||||
engine.timing.timeScale = 1;
|
||||
}
|
||||
|
||||
counter += 1;
|
||||
|
||||
// every 1.5 sec
|
||||
if (counter >= 60 * 1.5) {
|
||||
|
||||
// every 2 sec (real time)
|
||||
if (Common.now() - lastTime >= 2000) {
|
||||
// flip the timescale
|
||||
if (timeScaleTarget < 1) {
|
||||
timeScaleTarget = 1;
|
||||
|
@ -104,8 +101,8 @@ Example.ragdoll = function() {
|
|||
timeScaleTarget = 0.05;
|
||||
}
|
||||
|
||||
// reset counter
|
||||
counter = 0;
|
||||
// update last time
|
||||
lastTime = Common.now();
|
||||
}
|
||||
|
||||
for (var i = 0; i < stack.bodies.length; i += 1) {
|
||||
|
@ -113,8 +110,8 @@ Example.ragdoll = function() {
|
|||
|
||||
// animate stairs
|
||||
Body.translate(body, {
|
||||
x: -0.5 * engine.timing.timeScale,
|
||||
y: -0.5 * engine.timing.timeScale
|
||||
x: -30 * timeScale,
|
||||
y: -30 * timeScale
|
||||
});
|
||||
|
||||
// loop stairs when they go off screen
|
||||
|
|
|
@ -34,7 +34,7 @@ Example.remove = function() {
|
|||
Runner.run(runner, engine);
|
||||
|
||||
var stack = null,
|
||||
updateCount = 0;
|
||||
lastTimestamp = 0;
|
||||
|
||||
var createStack = function() {
|
||||
return Composites.stack(20, 20, 10, 5, 0, 0, function(x, y) {
|
||||
|
@ -62,14 +62,13 @@ Example.remove = function() {
|
|||
};
|
||||
|
||||
// add and remove stacks every few updates
|
||||
Events.on(engine, 'afterUpdate', function() {
|
||||
Events.on(engine, 'afterUpdate', function(event) {
|
||||
// limit rate
|
||||
if (stack && updateCount <= 50) {
|
||||
updateCount += 1;
|
||||
if (stack && event.timestamp - lastTimestamp < 800) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateCount = 0;
|
||||
lastTimestamp = event.timestamp;
|
||||
|
||||
// remove last stack
|
||||
if (stack) {
|
||||
|
|
|
@ -9,6 +9,7 @@ Example.slingshot = function() {
|
|||
Constraint = Matter.Constraint,
|
||||
MouseConstraint = Matter.MouseConstraint,
|
||||
Mouse = Matter.Mouse,
|
||||
Body = Matter.Body,
|
||||
Composite = Matter.Composite,
|
||||
Bodies = Matter.Bodies;
|
||||
|
||||
|
@ -41,6 +42,8 @@ Example.slingshot = function() {
|
|||
elastic = Constraint.create({
|
||||
pointA: anchor,
|
||||
bodyB: rock,
|
||||
length: 0.01,
|
||||
damping: 0.01,
|
||||
stiffness: 0.05
|
||||
});
|
||||
|
||||
|
@ -58,6 +61,12 @@ Example.slingshot = function() {
|
|||
|
||||
Events.on(engine, 'afterUpdate', function() {
|
||||
if (mouseConstraint.mouse.button === -1 && (rock.position.x > 190 || rock.position.y < 430)) {
|
||||
// Limit maximum speed of current rock.
|
||||
if (Body.getSpeed(rock) > 45) {
|
||||
Body.setSpeed(rock, 45);
|
||||
}
|
||||
|
||||
// Release current rock and add a new one.
|
||||
rock = Bodies.polygon(170, 450, 7, 20, rockOptions);
|
||||
Composite.add(engine.world, rock);
|
||||
elastic.bodyB = rock;
|
||||
|
|
|
@ -35,8 +35,7 @@ Example.staticFriction = function() {
|
|||
|
||||
// add bodies
|
||||
var body = Bodies.rectangle(400, 500, 200, 60, { isStatic: true, chamfer: 10, render: { fillStyle: '#060a19' } }),
|
||||
size = 50,
|
||||
counter = -1;
|
||||
size = 50;
|
||||
|
||||
var stack = Composites.stack(350, 470 - 6 * size, 1, 6, 0, 0, function(x, y) {
|
||||
return Bodies.rectangle(x, y, size * 2, size, {
|
||||
|
@ -56,18 +55,19 @@ Example.staticFriction = function() {
|
|||
Bodies.rectangle(0, 300, 50, 600, { isStatic: true })
|
||||
]);
|
||||
|
||||
Events.on(engine, 'beforeUpdate', function(event) {
|
||||
counter += 0.014;
|
||||
|
||||
if (counter < 0) {
|
||||
Events.on(engine, 'beforeUpdate', function() {
|
||||
if (engine.timing.timestamp < 1500) {
|
||||
return;
|
||||
}
|
||||
|
||||
var px = 400 + 100 * Math.sin(counter);
|
||||
var px = 400 + 100 * Math.sin((engine.timing.timestamp - 1500) * 0.001);
|
||||
|
||||
// body is static so must manually update velocity for friction to work
|
||||
Body.setVelocity(body, { x: px - body.position.x, y: 0 });
|
||||
Body.setPosition(body, { x: px, y: body.position.y });
|
||||
// manual update velocity required for older releases
|
||||
if (Matter.version === '0.18.0') {
|
||||
Body.setVelocity(body, { x: px - body.position.x, y: 0 });
|
||||
}
|
||||
|
||||
Body.setPosition(body, { x: px, y: body.position.y }, true);
|
||||
});
|
||||
|
||||
// add mouse control
|
||||
|
|
|
@ -42,15 +42,18 @@ Example.timescale = function() {
|
|||
Bodies.rectangle(0, 300, 50, 600, { isStatic: true })
|
||||
]);
|
||||
|
||||
var explosion = function(engine) {
|
||||
var explosion = function(engine, delta) {
|
||||
var timeScale = (1000 / 60) / delta;
|
||||
var bodies = Composite.allBodies(engine.world);
|
||||
|
||||
for (var i = 0; i < bodies.length; i++) {
|
||||
var body = bodies[i];
|
||||
|
||||
if (!body.isStatic && body.position.y >= 500) {
|
||||
var forceMagnitude = 0.05 * body.mass;
|
||||
// scale force for mass and time applied
|
||||
var forceMagnitude = (0.05 * body.mass) * timeScale;
|
||||
|
||||
// apply the force over a single update
|
||||
Body.applyForce(body, body.position, {
|
||||
x: (forceMagnitude + Common.random() * forceMagnitude) * Common.choose([1, -1]),
|
||||
y: -forceMagnitude + Common.random() * -forceMagnitude
|
||||
|
@ -60,30 +63,29 @@ Example.timescale = function() {
|
|||
};
|
||||
|
||||
var timeScaleTarget = 1,
|
||||
counter = 0;
|
||||
|
||||
lastTime = Common.now();
|
||||
|
||||
Events.on(engine, 'afterUpdate', function(event) {
|
||||
var timeScale = (event.delta || (1000 / 60)) / 1000;
|
||||
|
||||
// tween the timescale for bullet time slow-mo
|
||||
engine.timing.timeScale += (timeScaleTarget - engine.timing.timeScale) * 0.05;
|
||||
engine.timing.timeScale += (timeScaleTarget - engine.timing.timeScale) * 12 * timeScale;
|
||||
|
||||
counter += 1;
|
||||
|
||||
// every 1.5 sec
|
||||
if (counter >= 60 * 1.5) {
|
||||
// every 2 sec (real time)
|
||||
if (Common.now() - lastTime >= 2000) {
|
||||
|
||||
// flip the timescale
|
||||
if (timeScaleTarget < 1) {
|
||||
timeScaleTarget = 1;
|
||||
} else {
|
||||
timeScaleTarget = 0.05;
|
||||
timeScaleTarget = 0;
|
||||
}
|
||||
|
||||
// create some random forces
|
||||
explosion(engine);
|
||||
explosion(engine, event.delta);
|
||||
|
||||
// reset counter
|
||||
counter = 0;
|
||||
// update last time
|
||||
lastTime = Common.now();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -93,12 +95,10 @@ Example.timescale = function() {
|
|||
restitution: 0.8
|
||||
};
|
||||
|
||||
// add some small bouncy circles... remember Swordfish?
|
||||
Composite.add(world, Composites.stack(20, 100, 15, 3, 20, 40, function(x, y) {
|
||||
return Bodies.circle(x, y, Common.random(10, 20), bodyOptions);
|
||||
}));
|
||||
|
||||
// add some larger random bouncy objects
|
||||
Composite.add(world, Composites.stack(50, 50, 8, 3, 0, 0, function(x, y) {
|
||||
switch (Math.round(Common.random(0, 1))) {
|
||||
|
||||
|
@ -155,4 +155,4 @@ Example.timescale.for = '>=0.14.2';
|
|||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example.timescale;
|
||||
}
|
||||
}
|
427
src/body/Body.js
427
src/body/Body.js
|
@ -1,7 +1,6 @@
|
|||
/**
|
||||
* The `Matter.Body` module contains methods for creating and manipulating body models.
|
||||
* A `Matter.Body` is a rigid body that can be simulated by a `Matter.Engine`.
|
||||
* Factories for commonly used body configurations (such as rectangles, circles and other polygons) can be found in the module `Matter.Bodies`.
|
||||
* The `Matter.Body` module contains methods for creating and manipulating rigid bodies.
|
||||
* For creating bodies with common configurations such as rectangles, circles and other polygons see the module `Matter.Bodies`.
|
||||
*
|
||||
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples).
|
||||
|
||||
|
@ -15,17 +14,18 @@ module.exports = Body;
|
|||
var Vertices = require('../geometry/Vertices');
|
||||
var Vector = require('../geometry/Vector');
|
||||
var Sleeping = require('../core/Sleeping');
|
||||
var Render = require('../render/Render');
|
||||
var Common = require('../core/Common');
|
||||
var Bounds = require('../geometry/Bounds');
|
||||
var Axes = require('../geometry/Axes');
|
||||
|
||||
(function() {
|
||||
|
||||
Body._timeCorrection = true;
|
||||
Body._inertiaScale = 4;
|
||||
Body._nextCollidingGroupId = 1;
|
||||
Body._nextNonCollidingGroupId = -1;
|
||||
Body._nextCategory = 0x0001;
|
||||
Body._baseDelta = 1000 / 60;
|
||||
|
||||
/**
|
||||
* Creates a new rigid body model. The options parameter is an object that specifies any properties you wish to override the defaults.
|
||||
|
@ -96,6 +96,7 @@ var Axes = require('../geometry/Axes');
|
|||
area: 0,
|
||||
mass: 0,
|
||||
inertia: 0,
|
||||
deltaTime: 1000 / 60,
|
||||
_original: null
|
||||
};
|
||||
|
||||
|
@ -231,6 +232,12 @@ var Axes = require('../geometry/Axes');
|
|||
case 'angularVelocity':
|
||||
Body.setAngularVelocity(body, value);
|
||||
break;
|
||||
case 'speed':
|
||||
Body.setSpeed(body, value);
|
||||
break;
|
||||
case 'angularSpeed':
|
||||
Body.setAngularSpeed(body, value);
|
||||
break;
|
||||
case 'parts':
|
||||
Body.setParts(body, value);
|
||||
break;
|
||||
|
@ -320,7 +327,7 @@ var Axes = require('../geometry/Axes');
|
|||
};
|
||||
|
||||
/**
|
||||
* Sets the moment of inertia (i.e. second moment of area) of the body.
|
||||
* Sets the moment of inertia of the body. This is the second moment of area in two dimensions.
|
||||
* Inverse inertia is automatically updated to reflect the change. Mass is not changed.
|
||||
* @method setInertia
|
||||
* @param {body} body
|
||||
|
@ -337,8 +344,8 @@ var Axes = require('../geometry/Axes');
|
|||
* They are then automatically translated to world space based on `body.position`.
|
||||
*
|
||||
* The `vertices` argument should be passed as an array of `Matter.Vector` points (or a `Matter.Vertices` array).
|
||||
* Vertices must form a convex hull, concave hulls are not supported.
|
||||
*
|
||||
* Vertices must form a convex hull. Concave vertices must be decomposed into convex parts.
|
||||
*
|
||||
* @method setVertices
|
||||
* @param {body} body
|
||||
* @param {vector[]} vertices
|
||||
|
@ -375,7 +382,7 @@ var Axes = require('../geometry/Axes');
|
|||
* Note that this method will ensure that the first part in `body.parts` will always be the `body`.
|
||||
* @method setParts
|
||||
* @param {body} body
|
||||
* @param [body] parts
|
||||
* @param {body[]} parts
|
||||
* @param {bool} [autoHull=true]
|
||||
*/
|
||||
Body.setParts = function(body, parts, autoHull) {
|
||||
|
@ -457,15 +464,26 @@ var Axes = require('../geometry/Axes');
|
|||
};
|
||||
|
||||
/**
|
||||
* Sets the position of the body instantly. Velocity, angle, force etc. are unchanged.
|
||||
* Sets the position of the body. By default velocity is unchanged.
|
||||
* If `updateVelocity` is `true` then velocity is inferred from the change in position.
|
||||
* @method setPosition
|
||||
* @param {body} body
|
||||
* @param {vector} position
|
||||
* @param {boolean} [updateVelocity=false]
|
||||
*/
|
||||
Body.setPosition = function(body, position) {
|
||||
Body.setPosition = function(body, position, updateVelocity) {
|
||||
var delta = Vector.sub(position, body.position);
|
||||
body.positionPrev.x += delta.x;
|
||||
body.positionPrev.y += delta.y;
|
||||
|
||||
if (updateVelocity) {
|
||||
body.positionPrev.x = body.position.x;
|
||||
body.positionPrev.y = body.position.y;
|
||||
body.velocity.x = delta.x;
|
||||
body.velocity.y = delta.y;
|
||||
body.speed = Vector.magnitude(delta);
|
||||
} else {
|
||||
body.positionPrev.x += delta.x;
|
||||
body.positionPrev.y += delta.y;
|
||||
}
|
||||
|
||||
for (var i = 0; i < body.parts.length; i++) {
|
||||
var part = body.parts[i];
|
||||
|
@ -477,14 +495,23 @@ var Axes = require('../geometry/Axes');
|
|||
};
|
||||
|
||||
/**
|
||||
* Sets the angle of the body instantly. Angular velocity, position, force etc. are unchanged.
|
||||
* Sets the angle of the body. By default angular velocity is unchanged.
|
||||
* If `updateVelocity` is `true` then angular velocity is inferred from the change in angle.
|
||||
* @method setAngle
|
||||
* @param {body} body
|
||||
* @param {number} angle
|
||||
* @param {boolean} [updateVelocity=false]
|
||||
*/
|
||||
Body.setAngle = function(body, angle) {
|
||||
Body.setAngle = function(body, angle, updateVelocity) {
|
||||
var delta = angle - body.angle;
|
||||
body.anglePrev += delta;
|
||||
|
||||
if (updateVelocity) {
|
||||
body.anglePrev = body.angle;
|
||||
body.angularVelocity = delta;
|
||||
body.angularSpeed = Math.abs(delta);
|
||||
} else {
|
||||
body.anglePrev += delta;
|
||||
}
|
||||
|
||||
for (var i = 0; i < body.parts.length; i++) {
|
||||
var part = body.parts[i];
|
||||
|
@ -499,51 +526,128 @@ var Axes = require('../geometry/Axes');
|
|||
};
|
||||
|
||||
/**
|
||||
* Sets the linear velocity of the body instantly. Position, angle, force etc. are unchanged. See also `Body.applyForce`.
|
||||
* Sets the current linear velocity of the body.
|
||||
* Affects body speed.
|
||||
* @method setVelocity
|
||||
* @param {body} body
|
||||
* @param {vector} velocity
|
||||
*/
|
||||
Body.setVelocity = function(body, velocity) {
|
||||
body.positionPrev.x = body.position.x - velocity.x;
|
||||
body.positionPrev.y = body.position.y - velocity.y;
|
||||
body.velocity.x = velocity.x;
|
||||
body.velocity.y = velocity.y;
|
||||
var timeScale = body.deltaTime / Body._baseDelta;
|
||||
body.positionPrev.x = body.position.x - velocity.x * timeScale;
|
||||
body.positionPrev.y = body.position.y - velocity.y * timeScale;
|
||||
body.velocity.x = (body.position.x - body.positionPrev.x) / timeScale;
|
||||
body.velocity.y = (body.position.y - body.positionPrev.y) / timeScale;
|
||||
body.speed = Vector.magnitude(body.velocity);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the angular velocity of the body instantly. Position, angle, force etc. are unchanged. See also `Body.applyForce`.
|
||||
* Gets the current linear velocity of the body.
|
||||
* @method getVelocity
|
||||
* @param {body} body
|
||||
* @return {vector} velocity
|
||||
*/
|
||||
Body.getVelocity = function(body) {
|
||||
var timeScale = Body._baseDelta / body.deltaTime;
|
||||
|
||||
return {
|
||||
x: (body.position.x - body.positionPrev.x) * timeScale,
|
||||
y: (body.position.y - body.positionPrev.y) * timeScale
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the current linear speed of the body.
|
||||
* Equivalent to the magnitude of its velocity.
|
||||
* @method getSpeed
|
||||
* @param {body} body
|
||||
* @return {number} speed
|
||||
*/
|
||||
Body.getSpeed = function(body) {
|
||||
return Vector.magnitude(Body.getVelocity(body));
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the current linear speed of the body.
|
||||
* Direction is maintained. Affects body velocity.
|
||||
* @method setSpeed
|
||||
* @param {body} body
|
||||
* @param {number} speed
|
||||
*/
|
||||
Body.setSpeed = function(body, speed) {
|
||||
Body.setVelocity(body, Vector.mult(Vector.normalise(Body.getVelocity(body)), speed));
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the current rotational velocity of the body.
|
||||
* Affects body angular speed.
|
||||
* @method setAngularVelocity
|
||||
* @param {body} body
|
||||
* @param {number} velocity
|
||||
*/
|
||||
Body.setAngularVelocity = function(body, velocity) {
|
||||
body.anglePrev = body.angle - velocity;
|
||||
body.angularVelocity = velocity;
|
||||
var timeScale = body.deltaTime / Body._baseDelta;
|
||||
body.anglePrev = body.angle - velocity * timeScale;
|
||||
body.angularVelocity = (body.angle - body.anglePrev) / timeScale;
|
||||
body.angularSpeed = Math.abs(body.angularVelocity);
|
||||
};
|
||||
|
||||
/**
|
||||
* Moves a body by a given vector relative to its current position, without imparting any velocity.
|
||||
* @method translate
|
||||
* Gets the current rotational velocity of the body.
|
||||
* @method getAngularVelocity
|
||||
* @param {body} body
|
||||
* @param {vector} translation
|
||||
* @return {number} angular velocity
|
||||
*/
|
||||
Body.translate = function(body, translation) {
|
||||
Body.setPosition(body, Vector.add(body.position, translation));
|
||||
Body.getAngularVelocity = function(body) {
|
||||
return (body.angle - body.anglePrev) * Body._baseDelta / body.deltaTime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Rotates a body by a given angle relative to its current angle, without imparting any angular velocity.
|
||||
* Gets the current rotational speed of the body.
|
||||
* Equivalent to the magnitude of its angular velocity.
|
||||
* @method getAngularSpeed
|
||||
* @param {body} body
|
||||
* @return {number} angular speed
|
||||
*/
|
||||
Body.getAngularSpeed = function(body) {
|
||||
return Math.abs(Body.getAngularVelocity(body));
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the current rotational speed of the body.
|
||||
* Direction is maintained. Affects body angular velocity.
|
||||
* @method setAngularSpeed
|
||||
* @param {body} body
|
||||
* @param {number} speed
|
||||
*/
|
||||
Body.setAngularSpeed = function(body, speed) {
|
||||
Body.setAngularVelocity(body, Common.sign(Body.getAngularVelocity(body)) * speed);
|
||||
};
|
||||
|
||||
/**
|
||||
* Moves a body by a given vector relative to its current position. By default velocity is unchanged.
|
||||
* If `updateVelocity` is `true` then velocity is inferred from the change in position.
|
||||
* @method translate
|
||||
* @param {body} body
|
||||
* @param {vector} translation
|
||||
* @param {boolean} [updateVelocity=false]
|
||||
*/
|
||||
Body.translate = function(body, translation, updateVelocity) {
|
||||
Body.setPosition(body, Vector.add(body.position, translation), updateVelocity);
|
||||
};
|
||||
|
||||
/**
|
||||
* Rotates a body by a given angle relative to its current angle. By default angular velocity is unchanged.
|
||||
* If `updateVelocity` is `true` then angular velocity is inferred from the change in angle.
|
||||
* @method rotate
|
||||
* @param {body} body
|
||||
* @param {number} rotation
|
||||
* @param {vector} [point]
|
||||
* @param {boolean} [updateVelocity=false]
|
||||
*/
|
||||
Body.rotate = function(body, rotation, point) {
|
||||
Body.rotate = function(body, rotation, point, updateVelocity) {
|
||||
if (!point) {
|
||||
Body.setAngle(body, body.angle + rotation);
|
||||
Body.setAngle(body, body.angle + rotation, updateVelocity);
|
||||
} else {
|
||||
var cos = Math.cos(rotation),
|
||||
sin = Math.sin(rotation),
|
||||
|
@ -553,9 +657,9 @@ var Axes = require('../geometry/Axes');
|
|||
Body.setPosition(body, {
|
||||
x: point.x + (dx * cos - dy * sin),
|
||||
y: point.y + (dx * sin + dy * cos)
|
||||
});
|
||||
}, updateVelocity);
|
||||
|
||||
Body.setAngle(body, body.angle + rotation);
|
||||
Body.setAngle(body, body.angle + rotation, updateVelocity);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -624,39 +728,38 @@ var Axes = require('../geometry/Axes');
|
|||
};
|
||||
|
||||
/**
|
||||
* Performs a simulation step for the given `body`, including updating position and angle using Verlet integration.
|
||||
* Performs an update by integrating the equations of motion on the `body`.
|
||||
* This is applied every update by `Matter.Engine` automatically.
|
||||
* @method update
|
||||
* @param {body} body
|
||||
* @param {number} deltaTime
|
||||
* @param {number} timeScale
|
||||
* @param {number} correction
|
||||
* @param {number} [deltaTime=16.666]
|
||||
*/
|
||||
Body.update = function(body, deltaTime, timeScale, correction) {
|
||||
var deltaTimeSquared = Math.pow(deltaTime * timeScale * body.timeScale, 2);
|
||||
Body.update = function(body, deltaTime) {
|
||||
deltaTime = (typeof deltaTime !== 'undefined' ? deltaTime : (1000 / 60)) * body.timeScale;
|
||||
|
||||
var deltaTimeSquared = deltaTime * deltaTime,
|
||||
correction = Body._timeCorrection ? deltaTime / (body.deltaTime || deltaTime) : 1;
|
||||
|
||||
// from the previous step
|
||||
var frictionAir = 1 - body.frictionAir * timeScale * body.timeScale,
|
||||
velocityPrevX = body.position.x - body.positionPrev.x,
|
||||
velocityPrevY = body.position.y - body.positionPrev.y;
|
||||
var frictionAir = 1 - body.frictionAir * (deltaTime / Common._baseDelta),
|
||||
velocityPrevX = (body.position.x - body.positionPrev.x) * correction,
|
||||
velocityPrevY = (body.position.y - body.positionPrev.y) * correction;
|
||||
|
||||
// update velocity with Verlet integration
|
||||
body.velocity.x = (velocityPrevX * frictionAir * correction) + (body.force.x / body.mass) * deltaTimeSquared;
|
||||
body.velocity.y = (velocityPrevY * frictionAir * correction) + (body.force.y / body.mass) * deltaTimeSquared;
|
||||
body.velocity.x = (velocityPrevX * frictionAir) + (body.force.x / body.mass) * deltaTimeSquared;
|
||||
body.velocity.y = (velocityPrevY * frictionAir) + (body.force.y / body.mass) * deltaTimeSquared;
|
||||
|
||||
body.positionPrev.x = body.position.x;
|
||||
body.positionPrev.y = body.position.y;
|
||||
body.position.x += body.velocity.x;
|
||||
body.position.y += body.velocity.y;
|
||||
body.deltaTime = deltaTime;
|
||||
|
||||
// update angular velocity with Verlet integration
|
||||
body.angularVelocity = ((body.angle - body.anglePrev) * frictionAir * correction) + (body.torque / body.inertia) * deltaTimeSquared;
|
||||
body.anglePrev = body.angle;
|
||||
body.angle += body.angularVelocity;
|
||||
|
||||
// track speed and acceleration
|
||||
body.speed = Vector.magnitude(body.velocity);
|
||||
body.angularSpeed = Math.abs(body.angularVelocity);
|
||||
|
||||
// transform the body geometry
|
||||
for (var i = 0; i < body.parts.length; i++) {
|
||||
var part = body.parts[i];
|
||||
|
@ -681,16 +784,45 @@ var Axes = require('../geometry/Axes');
|
|||
};
|
||||
|
||||
/**
|
||||
* Applies a force to a body from a given world-space position, including resulting torque.
|
||||
* Updates properties `body.velocity`, `body.speed`, `body.angularVelocity` and `body.angularSpeed` which are normalised in relation to `Body._baseDelta`.
|
||||
* @method updateVelocities
|
||||
* @param {body} body
|
||||
*/
|
||||
Body.updateVelocities = function(body) {
|
||||
var timeScale = Body._baseDelta / body.deltaTime,
|
||||
bodyVelocity = body.velocity;
|
||||
|
||||
bodyVelocity.x = (body.position.x - body.positionPrev.x) * timeScale;
|
||||
bodyVelocity.y = (body.position.y - body.positionPrev.y) * timeScale;
|
||||
body.speed = Math.sqrt((bodyVelocity.x * bodyVelocity.x) + (bodyVelocity.y * bodyVelocity.y));
|
||||
|
||||
body.angularVelocity = (body.angle - body.anglePrev) * timeScale;
|
||||
body.angularSpeed = Math.abs(body.angularVelocity);
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies the `force` to the `body` from the force origin `position` in world-space, over a single timestep, including applying any resulting angular torque.
|
||||
*
|
||||
* Forces are useful for effects like gravity, wind or rocket thrust, but can be difficult in practice when precise control is needed. In these cases see `Body.setVelocity` and `Body.setPosition` as an alternative.
|
||||
*
|
||||
* The force from this function is only applied once for the duration of a single timestep, in other words the duration depends directly on the current engine update `delta` and the rate of calls to this function.
|
||||
*
|
||||
* Therefore to account for time, you should apply the force constantly over as many engine updates as equivalent to the intended duration.
|
||||
*
|
||||
* If all or part of the force duration is some fraction of a timestep, first multiply the force by `duration / timestep`.
|
||||
*
|
||||
* The force origin `position` in world-space must also be specified. Passing `body.position` will result in zero angular effect as the force origin would be at the centre of mass.
|
||||
*
|
||||
* The `body` will take time to accelerate under a force, the resulting effect depends on duration of the force, the body mass and other forces on the body including friction combined.
|
||||
* @method applyForce
|
||||
* @param {body} body
|
||||
* @param {vector} position
|
||||
* @param {vector} position The force origin in world-space. Pass `body.position` to avoid angular torque.
|
||||
* @param {vector} force
|
||||
*/
|
||||
Body.applyForce = function(body, position, force) {
|
||||
var offset = { x: position.x - body.position.x, y: position.y - body.position.y };
|
||||
body.force.x += force.x;
|
||||
body.force.y += force.y;
|
||||
var offset = { x: position.x - body.position.x, y: position.y - body.position.y };
|
||||
body.torque += offset.x * force.y - offset.y * force.x;
|
||||
};
|
||||
|
||||
|
@ -769,12 +901,14 @@ var Axes = require('../geometry/Axes');
|
|||
*/
|
||||
|
||||
/**
|
||||
* _Read only_. Set by `Body.create`.
|
||||
*
|
||||
* A `String` denoting the type of object.
|
||||
*
|
||||
* @readOnly
|
||||
* @property type
|
||||
* @type string
|
||||
* @default "body"
|
||||
* @readOnly
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -786,6 +920,8 @@ var Axes = require('../geometry/Axes');
|
|||
*/
|
||||
|
||||
/**
|
||||
* _Read only_. Use `Body.setParts` to set.
|
||||
*
|
||||
* An array of bodies that make up this body.
|
||||
* The first body in the array must always be a self reference to the current body instance.
|
||||
* All bodies in the `parts` array together form a single rigid compound body.
|
||||
|
@ -793,6 +929,7 @@ var Axes = require('../geometry/Axes');
|
|||
* Parts themselves should never be added to a `World`, only the parent body should be.
|
||||
* Use `Body.setParts` when setting parts to ensure correct updates of all properties.
|
||||
*
|
||||
* @readOnly
|
||||
* @property parts
|
||||
* @type body[]
|
||||
*/
|
||||
|
@ -805,10 +942,12 @@ var Axes = require('../geometry/Axes');
|
|||
*/
|
||||
|
||||
/**
|
||||
* A self reference if the body is _not_ a part of another body.
|
||||
* Otherwise this is a reference to the body that this is a part of.
|
||||
* See `body.parts`.
|
||||
* _Read only_. Updated by `Body.setParts`.
|
||||
*
|
||||
* A reference to the body that this is a part of. See `body.parts`.
|
||||
* This is a self reference if the body is not a part of another body.
|
||||
*
|
||||
* @readOnly
|
||||
* @property parent
|
||||
* @type body
|
||||
*/
|
||||
|
@ -822,48 +961,65 @@ var Axes = require('../geometry/Axes');
|
|||
*/
|
||||
|
||||
/**
|
||||
* _Read only_. Use `Body.setVertices` or `Body.setParts` to set. See also `Bodies.fromVertices`.
|
||||
*
|
||||
* An array of `Vector` objects that specify the convex hull of the rigid body.
|
||||
* These should be provided about the origin `(0, 0)`. E.g.
|
||||
*
|
||||
* [{ x: 0, y: 0 }, { x: 25, y: 50 }, { x: 50, y: 0 }]
|
||||
* `[{ x: 0, y: 0 }, { x: 25, y: 50 }, { x: 50, y: 0 }]`
|
||||
*
|
||||
* Vertices must always be convex, in clockwise order and must not contain any duplicate points.
|
||||
*
|
||||
* Concave vertices should be decomposed into convex `parts`, see `Bodies.fromVertices` and `Body.setParts`.
|
||||
*
|
||||
* When passed via `Body.create`, the vertices are translated relative to `body.position` (i.e. world-space, and constantly updated by `Body.update` during simulation).
|
||||
* The `Vector` objects are also augmented with additional properties required for efficient collision detection.
|
||||
*
|
||||
* Other properties such as `inertia` and `bounds` are automatically calculated from the passed vertices (unless provided via `options`).
|
||||
* Concave hulls are not currently supported. The module `Matter.Vertices` contains useful methods for working with vertices.
|
||||
* When set the vertices are translated such that `body.position` is at the centre of mass.
|
||||
* Many other body properties are automatically calculated from these vertices when set including `density`, `area` and `inertia`.
|
||||
*
|
||||
* The module `Matter.Vertices` contains useful methods for working with vertices.
|
||||
*
|
||||
* @readOnly
|
||||
* @property vertices
|
||||
* @type vector[]
|
||||
*/
|
||||
|
||||
/**
|
||||
* _Read only_. Use `Body.setPosition` to set.
|
||||
*
|
||||
* A `Vector` that specifies the current world-space position of the body.
|
||||
*
|
||||
*
|
||||
* @readOnly
|
||||
* @property position
|
||||
* @type vector
|
||||
* @default { x: 0, y: 0 }
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `Vector` that specifies the force to apply in the current step. It is zeroed after every `Body.update`. See also `Body.applyForce`.
|
||||
*
|
||||
* A `Vector` that accumulates the total force applied to the body for a single update.
|
||||
* Force is zeroed after every `Engine.update`, so constant forces should be applied for every update they are needed. See also `Body.applyForce`.
|
||||
*
|
||||
* @property force
|
||||
* @type vector
|
||||
* @default { x: 0, y: 0 }
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `Number` that specifies the torque (turning force) to apply in the current step. It is zeroed after every `Body.update`.
|
||||
* A `Number` that accumulates the total torque (turning force) applied to the body for a single update. See also `Body.applyForce`.
|
||||
* Torque is zeroed after every `Engine.update`, so constant torques should be applied for every update they are needed.
|
||||
*
|
||||
* Torques result in angular acceleration on every update, which depends on body inertia and the engine update delta.
|
||||
*
|
||||
* @property torque
|
||||
* @type number
|
||||
* @default 0
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `Number` that _measures_ the current speed of the body after the last `Body.update`. It is read-only and always positive (it's the magnitude of `body.velocity`).
|
||||
*
|
||||
* _Read only_. Use `Body.setSpeed` to set.
|
||||
*
|
||||
* See `Body.getSpeed` for details.
|
||||
*
|
||||
* Equivalent to the magnitude of `body.velocity` (always positive).
|
||||
*
|
||||
* @readOnly
|
||||
* @property speed
|
||||
* @type number
|
||||
|
@ -871,18 +1027,12 @@ var Axes = require('../geometry/Axes');
|
|||
*/
|
||||
|
||||
/**
|
||||
* A `Number` that _measures_ the current angular speed of the body after the last `Body.update`. It is read-only and always positive (it's the magnitude of `body.angularVelocity`).
|
||||
*
|
||||
* @readOnly
|
||||
* @property angularSpeed
|
||||
* @type number
|
||||
* @default 0
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `Vector` that _measures_ the current velocity of the body after the last `Body.update`. It is read-only.
|
||||
* If you need to modify a body's velocity directly, you should either apply a force or simply change the body's `position` (as the engine uses position-Verlet integration).
|
||||
*
|
||||
* _Read only_. Use `Body.setVelocity` to set.
|
||||
*
|
||||
* See `Body.getVelocity` for details.
|
||||
*
|
||||
* Equivalent to the magnitude of `body.angularVelocity` (always positive).
|
||||
*
|
||||
* @readOnly
|
||||
* @property velocity
|
||||
* @type vector
|
||||
|
@ -890,8 +1040,22 @@ var Axes = require('../geometry/Axes');
|
|||
*/
|
||||
|
||||
/**
|
||||
* A `Number` that _measures_ the current angular velocity of the body after the last `Body.update`. It is read-only.
|
||||
* If you need to modify a body's angular velocity directly, you should apply a torque or simply change the body's `angle` (as the engine uses position-Verlet integration).
|
||||
* _Read only_. Use `Body.setAngularSpeed` to set.
|
||||
*
|
||||
* See `Body.getAngularSpeed` for details.
|
||||
*
|
||||
*
|
||||
* @readOnly
|
||||
* @property angularSpeed
|
||||
* @type number
|
||||
* @default 0
|
||||
*/
|
||||
|
||||
/**
|
||||
* _Read only_. Use `Body.setAngularVelocity` to set.
|
||||
*
|
||||
* See `Body.getAngularVelocity` for details.
|
||||
*
|
||||
*
|
||||
* @readOnly
|
||||
* @property angularVelocity
|
||||
|
@ -900,9 +1064,11 @@ var Axes = require('../geometry/Axes');
|
|||
*/
|
||||
|
||||
/**
|
||||
* _Read only_. Use `Body.setStatic` to set.
|
||||
*
|
||||
* A flag that indicates whether a body is considered static. A static body can never change position or angle and is completely fixed.
|
||||
* If you need to set a body as static after its creation, you should use `Body.setStatic` as this requires more than just setting this flag.
|
||||
*
|
||||
* @readOnly
|
||||
* @property isStatic
|
||||
* @type boolean
|
||||
* @default false
|
||||
|
@ -917,18 +1083,23 @@ var Axes = require('../geometry/Axes');
|
|||
*/
|
||||
|
||||
/**
|
||||
* _Read only_. Use `Sleeping.set` to set.
|
||||
*
|
||||
* A flag that indicates whether the body is considered sleeping. A sleeping body acts similar to a static body, except it is only temporary and can be awoken.
|
||||
* If you need to set a body as sleeping, you should use `Sleeping.set` as this requires more than just setting this flag.
|
||||
*
|
||||
* @readOnly
|
||||
* @property isSleeping
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `Number` that _measures_ the amount of movement a body currently has (a combination of `speed` and `angularSpeed`). It is read-only and always positive.
|
||||
* It is used and updated by the `Matter.Sleeping` module during simulation to decide if a body has come to rest.
|
||||
* _Read only_. Calculated during engine update only when sleeping is enabled.
|
||||
*
|
||||
* A `Number` that loosely measures the amount of movement a body currently has.
|
||||
*
|
||||
* Derived from `body.speed^2 + body.angularSpeed^2`. See `Sleeping.update`.
|
||||
*
|
||||
* @readOnly
|
||||
* @property motion
|
||||
* @type number
|
||||
|
@ -936,52 +1107,66 @@ var Axes = require('../geometry/Axes');
|
|||
*/
|
||||
|
||||
/**
|
||||
* A `Number` that defines the number of updates in which this body must have near-zero velocity before it is set as sleeping by the `Matter.Sleeping` module (if sleeping is enabled by the engine).
|
||||
*
|
||||
* A `Number` that defines the length of time during which this body must have near-zero velocity before it is set as sleeping by the `Matter.Sleeping` module (if sleeping is enabled by the engine).
|
||||
*
|
||||
* @property sleepThreshold
|
||||
* @type number
|
||||
* @default 60
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `Number` that defines the density of the body, that is its mass per unit area.
|
||||
* If you pass the density via `Body.create` the `mass` property is automatically calculated for you based on the size (area) of the object.
|
||||
* This is generally preferable to simply setting mass and allows for more intuitive definition of materials (e.g. rock has a higher density than wood).
|
||||
* _Read only_. Use `Body.setDensity` to set.
|
||||
*
|
||||
* A `Number` that defines the density of the body (mass per unit area).
|
||||
*
|
||||
* Mass will also be updated when set.
|
||||
*
|
||||
* @readOnly
|
||||
* @property density
|
||||
* @type number
|
||||
* @default 0.001
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `Number` that defines the mass of the body, although it may be more appropriate to specify the `density` property instead.
|
||||
* If you modify this value, you must also modify the `body.inverseMass` property (`1 / mass`).
|
||||
*
|
||||
* _Read only_. Use `Body.setMass` to set.
|
||||
*
|
||||
* A `Number` that defines the mass of the body.
|
||||
*
|
||||
* Density will also be updated when set.
|
||||
*
|
||||
* @readOnly
|
||||
* @property mass
|
||||
* @type number
|
||||
*/
|
||||
|
||||
/**
|
||||
* _Read only_. Use `Body.setMass` to set.
|
||||
*
|
||||
* A `Number` that defines the inverse mass of the body (`1 / mass`).
|
||||
* If you modify this value, you must also modify the `body.mass` property.
|
||||
*
|
||||
* @readOnly
|
||||
* @property inverseMass
|
||||
* @type number
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `Number` that defines the moment of inertia (i.e. second moment of area) of the body.
|
||||
* It is automatically calculated from the given convex hull (`vertices` array) and density in `Body.create`.
|
||||
* If you modify this value, you must also modify the `body.inverseInertia` property (`1 / inertia`).
|
||||
*
|
||||
* _Read only_. Automatically calculated when vertices, mass or density are set or set through `Body.setInertia`.
|
||||
*
|
||||
* A `Number` that defines the moment of inertia of the body. This is the second moment of area in two dimensions.
|
||||
*
|
||||
* Can be manually set to `Infinity` to prevent rotation of the body. See `Body.setInertia`.
|
||||
*
|
||||
* @readOnly
|
||||
* @property inertia
|
||||
* @type number
|
||||
*/
|
||||
|
||||
/**
|
||||
* _Read only_. Automatically calculated when vertices, mass or density are set or calculated by `Body.setInertia`.
|
||||
*
|
||||
* A `Number` that defines the inverse moment of inertia of the body (`1 / inertia`).
|
||||
* If you modify this value, you must also modify the `body.inertia` property.
|
||||
*
|
||||
*
|
||||
* @readOnly
|
||||
* @property inverseInertia
|
||||
* @type number
|
||||
*/
|
||||
|
@ -992,7 +1177,7 @@ var Axes = require('../geometry/Axes');
|
|||
* A value of `0.8` means the body may bounce back with approximately 80% of its kinetic energy.
|
||||
* Note that collision response is based on _pairs_ of bodies, and that `restitution` values are _combined_ with the following formula:
|
||||
*
|
||||
* Math.max(bodyA.restitution, bodyB.restitution)
|
||||
* `Math.max(bodyA.restitution, bodyB.restitution)`
|
||||
*
|
||||
* @property restitution
|
||||
* @type number
|
||||
|
@ -1009,7 +1194,7 @@ var Axes = require('../geometry/Axes');
|
|||
* The engine uses a Coulomb friction model including static and kinetic friction.
|
||||
* Note that collision response is based on _pairs_ of bodies, and that `friction` values are _combined_ with the following formula:
|
||||
*
|
||||
* Math.min(bodyA.friction, bodyB.friction)
|
||||
* `Math.min(bodyA.friction, bodyB.friction)`
|
||||
*
|
||||
* @property friction
|
||||
* @type number
|
||||
|
@ -1094,9 +1279,11 @@ var Axes = require('../geometry/Axes');
|
|||
*/
|
||||
|
||||
/**
|
||||
* A `Number` that specifies a tolerance on how far a body is allowed to 'sink' or rotate into other bodies.
|
||||
* Avoid changing this value unless you understand the purpose of `slop` in physics engines.
|
||||
* The default should generally suffice, although very large bodies may require larger values for stable stacking.
|
||||
* A `Number` that specifies a thin boundary around the body where it is allowed to slightly sink into other bodies.
|
||||
*
|
||||
* This is required for proper collision response, including friction and restitution effects.
|
||||
*
|
||||
* The default should generally suffice in most cases. You may need to decrease this value for very small bodies that are nearing the default value in scale.
|
||||
*
|
||||
* @property slop
|
||||
* @type number
|
||||
|
@ -1104,13 +1291,25 @@ var Axes = require('../geometry/Axes');
|
|||
*/
|
||||
|
||||
/**
|
||||
* A `Number` that allows per-body time scaling, e.g. a force-field where bodies inside are in slow-motion, while others are at full speed.
|
||||
* A `Number` that specifies per-body time scaling.
|
||||
*
|
||||
* @property timeScale
|
||||
* @type number
|
||||
* @default 1
|
||||
*/
|
||||
|
||||
/**
|
||||
* _Read only_. Updated during engine update.
|
||||
*
|
||||
* A `Number` that records the last delta time value used to update this body.
|
||||
* Used to calculate speed and velocity.
|
||||
*
|
||||
* @readOnly
|
||||
* @property deltaTime
|
||||
* @type number
|
||||
* @default 1000 / 60
|
||||
*/
|
||||
|
||||
/**
|
||||
* An `Object` that defines the rendering properties to be consumed by the module `Matter.Render`.
|
||||
*
|
||||
|
@ -1208,17 +1407,23 @@ var Axes = require('../geometry/Axes');
|
|||
*/
|
||||
|
||||
/**
|
||||
* _Read only_. Calculated automatically when vertices are set.
|
||||
*
|
||||
* An array of unique axis vectors (edge normals) used for collision detection.
|
||||
* These are automatically calculated from the given convex hull (`vertices` array) in `Body.create`.
|
||||
* These are automatically calculated when vertices are set.
|
||||
* They are constantly updated by `Body.update` during the simulation.
|
||||
*
|
||||
* @readOnly
|
||||
* @property axes
|
||||
* @type vector[]
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `Number` that _measures_ the area of the body's convex hull, calculated at creation by `Body.create`.
|
||||
*
|
||||
* _Read only_. Calculated automatically when vertices are set.
|
||||
*
|
||||
* A `Number` that measures the area of the body's convex hull.
|
||||
*
|
||||
* @readOnly
|
||||
* @property area
|
||||
* @type string
|
||||
* @default
|
||||
|
@ -1226,8 +1431,8 @@ var Axes = require('../geometry/Axes');
|
|||
|
||||
/**
|
||||
* A `Bounds` object that defines the AABB region for the body.
|
||||
* It is automatically calculated from the given convex hull (`vertices` array) in `Body.create` and constantly updated by `Body.update` during simulation.
|
||||
*
|
||||
* It is automatically calculated when vertices are set and constantly updated by `Body.update` during simulation.
|
||||
*
|
||||
* @property bounds
|
||||
* @type bounds
|
||||
*/
|
||||
|
|
|
@ -9,15 +9,17 @@ var Resolver = {};
|
|||
module.exports = Resolver;
|
||||
|
||||
var Vertices = require('../geometry/Vertices');
|
||||
var Common = require('../core/Common');
|
||||
var Bounds = require('../geometry/Bounds');
|
||||
|
||||
(function() {
|
||||
|
||||
Resolver._restingThresh = 4;
|
||||
Resolver._restingThreshTangent = 6;
|
||||
Resolver._restingThresh = 2;
|
||||
Resolver._restingThreshTangent = Math.sqrt(6);
|
||||
Resolver._positionDampen = 0.9;
|
||||
Resolver._positionWarming = 0.8;
|
||||
Resolver._frictionNormalMultiplier = 5;
|
||||
Resolver._frictionMaxStatic = Number.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* Prepare pairs for position solving.
|
||||
|
@ -47,9 +49,10 @@ var Bounds = require('../geometry/Bounds');
|
|||
* Find a solution for pair positions.
|
||||
* @method solvePosition
|
||||
* @param {pair[]} pairs
|
||||
* @param {number} timeScale
|
||||
* @param {number} delta
|
||||
* @param {number} [damping=1]
|
||||
*/
|
||||
Resolver.solvePosition = function(pairs, timeScale) {
|
||||
Resolver.solvePosition = function(pairs, delta, damping) {
|
||||
var i,
|
||||
pair,
|
||||
collision,
|
||||
|
@ -58,7 +61,8 @@ var Bounds = require('../geometry/Bounds');
|
|||
normal,
|
||||
contactShare,
|
||||
positionImpulse,
|
||||
positionDampen = Resolver._positionDampen,
|
||||
positionDampen = Resolver._positionDampen * (damping || 1),
|
||||
slopDampen = Common.clamp(delta / Common._baseDelta, 0, 1),
|
||||
pairsLength = pairs.length;
|
||||
|
||||
// find impulses required to resolve penetration
|
||||
|
@ -89,7 +93,7 @@ var Bounds = require('../geometry/Bounds');
|
|||
bodyA = collision.parentA;
|
||||
bodyB = collision.parentB;
|
||||
normal = collision.normal;
|
||||
positionImpulse = (pair.separation - pair.slop) * timeScale;
|
||||
positionImpulse = pair.separation - pair.slop * slopDampen;
|
||||
|
||||
if (bodyA.isStatic || bodyB.isStatic)
|
||||
positionImpulse *= 2;
|
||||
|
@ -219,14 +223,16 @@ var Bounds = require('../geometry/Bounds');
|
|||
* Find a solution for pair velocities.
|
||||
* @method solveVelocity
|
||||
* @param {pair[]} pairs
|
||||
* @param {number} timeScale
|
||||
* @param {number} delta
|
||||
*/
|
||||
Resolver.solveVelocity = function(pairs, timeScale) {
|
||||
var timeScaleSquared = timeScale * timeScale,
|
||||
restingThresh = Resolver._restingThresh * timeScaleSquared,
|
||||
frictionNormalMultiplier = Resolver._frictionNormalMultiplier,
|
||||
restingThreshTangent = Resolver._restingThreshTangent * timeScaleSquared,
|
||||
NumberMaxValue = Number.MAX_VALUE,
|
||||
Resolver.solveVelocity = function(pairs, delta) {
|
||||
var timeScale = delta / Common._baseDelta,
|
||||
timeScaleSquared = timeScale * timeScale,
|
||||
timeScaleCubed = timeScaleSquared * timeScale,
|
||||
restingThresh = -Resolver._restingThresh * timeScale,
|
||||
restingThreshTangent = Resolver._restingThreshTangent,
|
||||
frictionNormalMultiplier = Resolver._frictionNormalMultiplier * timeScale,
|
||||
frictionMaxStatic = Resolver._frictionMaxStatic,
|
||||
pairsLength = pairs.length,
|
||||
tangentImpulse,
|
||||
maxFriction,
|
||||
|
@ -252,7 +258,7 @@ var Bounds = require('../geometry/Bounds');
|
|||
contactsLength = contacts.length,
|
||||
contactShare = 1 / contactsLength,
|
||||
inverseMassTotal = bodyA.inverseMass + bodyB.inverseMass,
|
||||
friction = pair.friction * pair.frictionStatic * frictionNormalMultiplier * timeScaleSquared;
|
||||
friction = pair.friction * pair.frictionStatic * frictionNormalMultiplier;
|
||||
|
||||
// update body velocities
|
||||
bodyAVelocity.x = bodyA.position.x - bodyA.positionPrev.x;
|
||||
|
@ -287,12 +293,12 @@ var Bounds = require('../geometry/Bounds');
|
|||
var normalOverlap = pair.separation + normalVelocity;
|
||||
var normalForce = Math.min(normalOverlap, 1);
|
||||
normalForce = normalOverlap < 0 ? 0 : normalForce;
|
||||
|
||||
|
||||
var frictionLimit = normalForce * friction;
|
||||
|
||||
if (tangentVelocity > frictionLimit || -tangentVelocity > frictionLimit) {
|
||||
maxFriction = tangentVelocity > 0 ? tangentVelocity : -tangentVelocity;
|
||||
tangentImpulse = pair.friction * (tangentVelocity > 0 ? 1 : -1) * timeScaleSquared;
|
||||
if (tangentVelocity < -frictionLimit || tangentVelocity > frictionLimit) {
|
||||
maxFriction = (tangentVelocity > 0 ? tangentVelocity : -tangentVelocity);
|
||||
tangentImpulse = pair.friction * (tangentVelocity > 0 ? 1 : -1) * timeScaleCubed;
|
||||
|
||||
if (tangentImpulse < -maxFriction) {
|
||||
tangentImpulse = -maxFriction;
|
||||
|
@ -301,7 +307,7 @@ var Bounds = require('../geometry/Bounds');
|
|||
}
|
||||
} else {
|
||||
tangentImpulse = tangentVelocity;
|
||||
maxFriction = NumberMaxValue;
|
||||
maxFriction = frictionMaxStatic;
|
||||
}
|
||||
|
||||
// account for mass, inertia and contact offset
|
||||
|
@ -314,7 +320,7 @@ var Bounds = require('../geometry/Bounds');
|
|||
tangentImpulse *= share;
|
||||
|
||||
// handle high velocity and resting collisions separately
|
||||
if (normalVelocity * normalVelocity > restingThresh && normalVelocity < 0) {
|
||||
if (normalVelocity < restingThresh) {
|
||||
// high normal velocity so clear cached contact normal impulse
|
||||
contact.normalImpulse = 0;
|
||||
} else {
|
||||
|
@ -322,12 +328,12 @@ var Bounds = require('../geometry/Bounds');
|
|||
// impulse constraint tends to 0
|
||||
var contactNormalImpulse = contact.normalImpulse;
|
||||
contact.normalImpulse += normalImpulse;
|
||||
contact.normalImpulse = Math.min(contact.normalImpulse, 0);
|
||||
if (contact.normalImpulse > 0) contact.normalImpulse = 0;
|
||||
normalImpulse = contact.normalImpulse - contactNormalImpulse;
|
||||
}
|
||||
|
||||
// handle high velocity and resting collisions separately
|
||||
if (tangentVelocity * tangentVelocity > restingThreshTangent) {
|
||||
if (tangentVelocity < -restingThreshTangent || tangentVelocity > restingThreshTangent) {
|
||||
// high tangent velocity so clear cached contact tangent impulse
|
||||
contact.tangentImpulse = 0;
|
||||
} else {
|
||||
|
|
|
@ -110,9 +110,11 @@ var Common = require('../core/Common');
|
|||
* @private
|
||||
* @method solveAll
|
||||
* @param {constraint[]} constraints
|
||||
* @param {number} timeScale
|
||||
* @param {number} delta
|
||||
*/
|
||||
Constraint.solveAll = function(constraints, timeScale) {
|
||||
Constraint.solveAll = function(constraints, delta) {
|
||||
var timeScale = Common.clamp(delta / Common._baseDelta, 0, 1);
|
||||
|
||||
// Solve fixed constraints first.
|
||||
for (var i = 0; i < constraints.length; i += 1) {
|
||||
var constraint = constraints[i],
|
||||
|
@ -183,7 +185,10 @@ var Common = require('../core/Common');
|
|||
|
||||
// solve distance constraint with Gauss-Siedel method
|
||||
var difference = (currentLength - constraint.length) / currentLength,
|
||||
stiffness = constraint.stiffness < 1 ? constraint.stiffness * timeScale : constraint.stiffness,
|
||||
isRigid = constraint.stiffness >= 1 || constraint.length === 0,
|
||||
stiffness = isRigid ? constraint.stiffness * timeScale
|
||||
: constraint.stiffness * timeScale * timeScale,
|
||||
damping = constraint.damping * timeScale,
|
||||
force = Vector.mult(delta, difference * stiffness),
|
||||
massTotal = (bodyA ? bodyA.inverseMass : 0) + (bodyB ? bodyB.inverseMass : 0),
|
||||
inertiaTotal = (bodyA ? bodyA.inverseInertia : 0) + (bodyB ? bodyB.inverseInertia : 0),
|
||||
|
@ -193,8 +198,8 @@ var Common = require('../core/Common');
|
|||
normal,
|
||||
normalVelocity,
|
||||
relativeVelocity;
|
||||
|
||||
if (constraint.damping) {
|
||||
|
||||
if (damping > 0) {
|
||||
var zero = Vector.create();
|
||||
normal = Vector.div(delta, currentLength);
|
||||
|
||||
|
@ -218,9 +223,9 @@ var Common = require('../core/Common');
|
|||
bodyA.position.y -= force.y * share;
|
||||
|
||||
// apply damping
|
||||
if (constraint.damping) {
|
||||
bodyA.positionPrev.x -= constraint.damping * normal.x * normalVelocity * share;
|
||||
bodyA.positionPrev.y -= constraint.damping * normal.y * normalVelocity * share;
|
||||
if (damping > 0) {
|
||||
bodyA.positionPrev.x -= damping * normal.x * normalVelocity * share;
|
||||
bodyA.positionPrev.y -= damping * normal.y * normalVelocity * share;
|
||||
}
|
||||
|
||||
// apply torque
|
||||
|
@ -241,9 +246,9 @@ var Common = require('../core/Common');
|
|||
bodyB.position.y += force.y * share;
|
||||
|
||||
// apply damping
|
||||
if (constraint.damping) {
|
||||
bodyB.positionPrev.x += constraint.damping * normal.x * normalVelocity * share;
|
||||
bodyB.positionPrev.y += constraint.damping * normal.y * normalVelocity * share;
|
||||
if (damping > 0) {
|
||||
bodyB.positionPrev.x += damping * normal.x * normalVelocity * share;
|
||||
bodyB.positionPrev.y += damping * normal.y * normalVelocity * share;
|
||||
}
|
||||
|
||||
// apply torque
|
||||
|
|
|
@ -10,6 +10,7 @@ module.exports = Common;
|
|||
|
||||
(function() {
|
||||
|
||||
Common._baseDelta = 1000 / 60;
|
||||
Common._nextId = 0;
|
||||
Common._seed = 0;
|
||||
Common._nowStartTime = +(new Date());
|
||||
|
@ -322,7 +323,8 @@ module.exports = Common;
|
|||
* - 2 = Info
|
||||
* - 3 = Warn
|
||||
* - 4 = Error
|
||||
* @property Common.logLevel
|
||||
* @static
|
||||
* @property logLevel
|
||||
* @type {Number}
|
||||
* @default 1
|
||||
*/
|
||||
|
|
|
@ -71,26 +71,16 @@ var Body = require('../body/Body');
|
|||
};
|
||||
|
||||
/**
|
||||
* Moves the simulation forward in time by `delta` ms.
|
||||
* The `correction` argument is an optional `Number` that specifies the time correction factor to apply to the update.
|
||||
* This can help improve the accuracy of the simulation in cases where `delta` is changing between updates.
|
||||
* The value of `correction` is defined as `delta / lastDelta`, i.e. the percentage change of `delta` over the last step.
|
||||
* Therefore the value is always `1` (no correction) when `delta` constant (or when no correction is desired, which is the default).
|
||||
* See the paper on <a href="http://lonesock.net/article/verlet.html">Time Corrected Verlet</a> for more information.
|
||||
*
|
||||
* Moves the simulation forward in time by `delta` milliseconds.
|
||||
* Triggers `beforeUpdate` and `afterUpdate` events.
|
||||
* Triggers `collisionStart`, `collisionActive` and `collisionEnd` events.
|
||||
* @method update
|
||||
* @param {engine} engine
|
||||
* @param {number} [delta=16.666]
|
||||
* @param {number} [correction=1]
|
||||
*/
|
||||
Engine.update = function(engine, delta, correction) {
|
||||
Engine.update = function(engine, delta) {
|
||||
var startTime = Common.now();
|
||||
|
||||
delta = delta || 1000 / 60;
|
||||
correction = correction || 1;
|
||||
|
||||
var world = engine.world,
|
||||
detector = engine.detector,
|
||||
pairs = engine.pairs,
|
||||
|
@ -98,13 +88,17 @@ var Body = require('../body/Body');
|
|||
timestamp = timing.timestamp,
|
||||
i;
|
||||
|
||||
delta = typeof delta !== 'undefined' ? delta : Common._baseDelta;
|
||||
delta *= timing.timeScale;
|
||||
|
||||
// increment timestamp
|
||||
timing.timestamp += delta * timing.timeScale;
|
||||
timing.lastDelta = delta * timing.timeScale;
|
||||
timing.timestamp += delta;
|
||||
timing.lastDelta = delta;
|
||||
|
||||
// create an event object
|
||||
var event = {
|
||||
timestamp: timing.timestamp
|
||||
timestamp: timing.timestamp,
|
||||
delta: delta
|
||||
};
|
||||
|
||||
Events.trigger(engine, 'beforeUpdate', event);
|
||||
|
@ -113,30 +107,31 @@ var Body = require('../body/Body');
|
|||
var allBodies = Composite.allBodies(world),
|
||||
allConstraints = Composite.allConstraints(world);
|
||||
|
||||
// update the detector bodies if they have changed
|
||||
// if the world has changed
|
||||
if (world.isModified) {
|
||||
// update the detector bodies
|
||||
Detector.setBodies(detector, allBodies);
|
||||
}
|
||||
|
||||
// reset all composite modified flags
|
||||
if (world.isModified) {
|
||||
// reset all composite modified flags
|
||||
Composite.setModified(world, false, false, true);
|
||||
}
|
||||
|
||||
// update sleeping if enabled
|
||||
if (engine.enableSleeping)
|
||||
Sleeping.update(allBodies, timing.timeScale);
|
||||
Sleeping.update(allBodies, delta);
|
||||
|
||||
// apply gravity to all bodies
|
||||
Engine._bodiesApplyGravity(allBodies, engine.gravity);
|
||||
|
||||
// update all body position and rotation by integration
|
||||
Engine._bodiesUpdate(allBodies, delta, timing.timeScale, correction, world.bounds);
|
||||
if (delta > 0) {
|
||||
Engine._bodiesUpdate(allBodies, delta);
|
||||
}
|
||||
|
||||
// update all constraints (first pass)
|
||||
Constraint.preSolveAll(allBodies);
|
||||
for (i = 0; i < engine.constraintIterations; i++) {
|
||||
Constraint.solveAll(allConstraints, timing.timeScale);
|
||||
Constraint.solveAll(allConstraints, delta);
|
||||
}
|
||||
Constraint.postSolveAll(allBodies);
|
||||
|
||||
|
@ -149,33 +144,37 @@ var Body = require('../body/Body');
|
|||
|
||||
// wake up bodies involved in collisions
|
||||
if (engine.enableSleeping)
|
||||
Sleeping.afterCollisions(pairs.list, timing.timeScale);
|
||||
|
||||
// trigger collision events
|
||||
if (pairs.collisionStart.length > 0)
|
||||
Events.trigger(engine, 'collisionStart', { pairs: pairs.collisionStart });
|
||||
Sleeping.afterCollisions(pairs.list);
|
||||
|
||||
// iteratively resolve position between collisions
|
||||
var positionDamping = Common.clamp(20 / engine.positionIterations, 0, 1);
|
||||
|
||||
Resolver.preSolvePosition(pairs.list);
|
||||
for (i = 0; i < engine.positionIterations; i++) {
|
||||
Resolver.solvePosition(pairs.list, timing.timeScale);
|
||||
Resolver.solvePosition(pairs.list, delta, positionDamping);
|
||||
}
|
||||
Resolver.postSolvePosition(allBodies);
|
||||
|
||||
// update all constraints (second pass)
|
||||
Constraint.preSolveAll(allBodies);
|
||||
for (i = 0; i < engine.constraintIterations; i++) {
|
||||
Constraint.solveAll(allConstraints, timing.timeScale);
|
||||
Constraint.solveAll(allConstraints, delta);
|
||||
}
|
||||
Constraint.postSolveAll(allBodies);
|
||||
|
||||
// iteratively resolve velocity between collisions
|
||||
Resolver.preSolveVelocity(pairs.list);
|
||||
for (i = 0; i < engine.velocityIterations; i++) {
|
||||
Resolver.solveVelocity(pairs.list, timing.timeScale);
|
||||
Resolver.solveVelocity(pairs.list, delta);
|
||||
}
|
||||
|
||||
// update body speed and velocity properties
|
||||
Engine._bodiesUpdateVelocities(allBodies);
|
||||
|
||||
// trigger collision events
|
||||
if (pairs.collisionStart.length > 0)
|
||||
Events.trigger(engine, 'collisionStart', { pairs: pairs.collisionStart });
|
||||
|
||||
if (pairs.collisionActive.length > 0)
|
||||
Events.trigger(engine, 'collisionActive', { pairs: pairs.collisionActive });
|
||||
|
||||
|
@ -234,7 +233,9 @@ var Body = require('../body/Body');
|
|||
* @param {body[]} bodies
|
||||
*/
|
||||
Engine._bodiesClearForces = function(bodies) {
|
||||
for (var i = 0; i < bodies.length; i++) {
|
||||
var bodiesLength = bodies.length;
|
||||
|
||||
for (var i = 0; i < bodiesLength; i++) {
|
||||
var body = bodies[i];
|
||||
|
||||
// reset force buffers
|
||||
|
@ -245,51 +246,65 @@ var Body = require('../body/Body');
|
|||
};
|
||||
|
||||
/**
|
||||
* Applys a mass dependant force to all given bodies.
|
||||
* Applies gravitational acceleration to all `bodies`.
|
||||
* This models a [uniform gravitational field](https://en.wikipedia.org/wiki/Gravity_of_Earth), similar to near the surface of a planet.
|
||||
*
|
||||
* @method _bodiesApplyGravity
|
||||
* @private
|
||||
* @param {body[]} bodies
|
||||
* @param {vector} gravity
|
||||
*/
|
||||
Engine._bodiesApplyGravity = function(bodies, gravity) {
|
||||
var gravityScale = typeof gravity.scale !== 'undefined' ? gravity.scale : 0.001;
|
||||
var gravityScale = typeof gravity.scale !== 'undefined' ? gravity.scale : 0.001,
|
||||
bodiesLength = bodies.length;
|
||||
|
||||
if ((gravity.x === 0 && gravity.y === 0) || gravityScale === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < bodies.length; i++) {
|
||||
for (var i = 0; i < bodiesLength; i++) {
|
||||
var body = bodies[i];
|
||||
|
||||
if (body.isStatic || body.isSleeping)
|
||||
continue;
|
||||
|
||||
// apply gravity
|
||||
// add the resultant force of gravity
|
||||
body.force.y += body.mass * gravity.y * gravityScale;
|
||||
body.force.x += body.mass * gravity.x * gravityScale;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Applys `Body.update` to all given `bodies`.
|
||||
* Applies `Body.update` to all given `bodies`.
|
||||
* @method _bodiesUpdate
|
||||
* @private
|
||||
* @param {body[]} bodies
|
||||
* @param {number} deltaTime
|
||||
* The amount of time elapsed between updates
|
||||
* @param {number} timeScale
|
||||
* @param {number} correction
|
||||
* The Verlet correction factor (deltaTime / lastDeltaTime)
|
||||
* @param {bounds} worldBounds
|
||||
* @param {number} delta The amount of time elapsed between updates
|
||||
*/
|
||||
Engine._bodiesUpdate = function(bodies, deltaTime, timeScale, correction, worldBounds) {
|
||||
for (var i = 0; i < bodies.length; i++) {
|
||||
Engine._bodiesUpdate = function(bodies, delta) {
|
||||
var bodiesLength = bodies.length;
|
||||
|
||||
for (var i = 0; i < bodiesLength; i++) {
|
||||
var body = bodies[i];
|
||||
|
||||
if (body.isStatic || body.isSleeping)
|
||||
continue;
|
||||
|
||||
Body.update(body, deltaTime, timeScale, correction);
|
||||
Body.update(body, delta);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies `Body.updateVelocities` to all given `bodies`.
|
||||
* @method _bodiesUpdateVelocities
|
||||
* @private
|
||||
* @param {body[]} bodies
|
||||
*/
|
||||
Engine._bodiesUpdateVelocities = function(bodies) {
|
||||
var bodiesLength = bodies.length;
|
||||
|
||||
for (var i = 0; i < bodiesLength; i++) {
|
||||
Body.updateVelocities(bodies[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -306,6 +321,7 @@ var Body = require('../body/Body');
|
|||
* @event beforeUpdate
|
||||
* @param {object} event An event object
|
||||
* @param {number} event.timestamp The engine.timing.timestamp of the event
|
||||
* @param {number} event.delta The delta time in milliseconds value used in the update
|
||||
* @param {engine} event.source The source object of the event
|
||||
* @param {string} event.name The name of the event
|
||||
*/
|
||||
|
@ -316,6 +332,7 @@ var Body = require('../body/Body');
|
|||
* @event afterUpdate
|
||||
* @param {object} event An event object
|
||||
* @param {number} event.timestamp The engine.timing.timestamp of the event
|
||||
* @param {number} event.delta The delta time in milliseconds value used in the update
|
||||
* @param {engine} event.source The source object of the event
|
||||
* @param {string} event.name The name of the event
|
||||
*/
|
||||
|
@ -327,6 +344,7 @@ var Body = require('../body/Body');
|
|||
* @param {object} event An event object
|
||||
* @param {pair[]} event.pairs List of affected pairs
|
||||
* @param {number} event.timestamp The engine.timing.timestamp of the event
|
||||
* @param {number} event.delta The delta time in milliseconds value used in the update
|
||||
* @param {engine} event.source The source object of the event
|
||||
* @param {string} event.name The name of the event
|
||||
*/
|
||||
|
@ -338,6 +356,7 @@ var Body = require('../body/Body');
|
|||
* @param {object} event An event object
|
||||
* @param {pair[]} event.pairs List of affected pairs
|
||||
* @param {number} event.timestamp The engine.timing.timestamp of the event
|
||||
* @param {number} event.delta The delta time in milliseconds value used in the update
|
||||
* @param {engine} event.source The source object of the event
|
||||
* @param {string} event.name The name of the event
|
||||
*/
|
||||
|
@ -349,6 +368,7 @@ var Body = require('../body/Body');
|
|||
* @param {object} event An event object
|
||||
* @param {pair[]} event.pairs List of affected pairs
|
||||
* @param {number} event.timestamp The engine.timing.timestamp of the event
|
||||
* @param {number} event.delta The delta time in milliseconds value used in the update
|
||||
* @param {engine} event.source The source object of the event
|
||||
* @param {string} event.name The name of the event
|
||||
*/
|
||||
|
@ -417,7 +437,7 @@ var Body = require('../body/Body');
|
|||
/**
|
||||
* A `Number` that specifies the current simulation-time in milliseconds starting from `0`.
|
||||
* It is incremented on every `Engine.update` by the given `delta` argument.
|
||||
*
|
||||
*
|
||||
* @property timing.timestamp
|
||||
* @type number
|
||||
* @default 0
|
||||
|
@ -428,7 +448,7 @@ var Body = require('../body/Body');
|
|||
* It is updated by timing from the start of the last `Engine.update` call until it ends.
|
||||
*
|
||||
* This value will also include the total execution time of all event handlers directly or indirectly triggered by the engine update.
|
||||
*
|
||||
*
|
||||
* @property timing.lastElapsed
|
||||
* @type number
|
||||
* @default 0
|
||||
|
@ -436,7 +456,7 @@ var Body = require('../body/Body');
|
|||
|
||||
/**
|
||||
* A `Number` that represents the `delta` value used in the last engine update.
|
||||
*
|
||||
*
|
||||
* @property timing.lastDelta
|
||||
* @type number
|
||||
* @default 0
|
||||
|
@ -484,22 +504,29 @@ var Body = require('../body/Body');
|
|||
*/
|
||||
|
||||
/**
|
||||
* The gravity to apply on all bodies in `engine.world`.
|
||||
* An optional gravitational acceleration applied to all bodies in `engine.world` on every update.
|
||||
*
|
||||
* This models a [uniform gravitational field](https://en.wikipedia.org/wiki/Gravity_of_Earth), similar to near the surface of a planet. For gravity in other contexts, disable this and apply forces as needed.
|
||||
*
|
||||
* To disable set the `scale` component to `0`.
|
||||
*
|
||||
* This is split into three components for ease of use:
|
||||
* a normalised direction (`x` and `y`) and magnitude (`scale`).
|
||||
*
|
||||
* @property gravity
|
||||
* @type object
|
||||
*/
|
||||
|
||||
/**
|
||||
* The gravity x component.
|
||||
*
|
||||
* The gravitational direction normal `x` component, to be multiplied by `gravity.scale`.
|
||||
*
|
||||
* @property gravity.x
|
||||
* @type object
|
||||
* @default 0
|
||||
*/
|
||||
|
||||
/**
|
||||
* The gravity y component.
|
||||
* The gravitational direction normal `y` component, to be multiplied by `gravity.scale`.
|
||||
*
|
||||
* @property gravity.y
|
||||
* @type object
|
||||
|
@ -507,8 +534,8 @@ var Body = require('../body/Body');
|
|||
*/
|
||||
|
||||
/**
|
||||
* The gravity scale factor.
|
||||
*
|
||||
* The magnitude of the gravitational acceleration.
|
||||
*
|
||||
* @property gravity.scale
|
||||
* @type object
|
||||
* @default 0.001
|
||||
|
|
|
@ -53,13 +53,11 @@ var Common = require('./Common');
|
|||
Runner.create = function(options) {
|
||||
var defaults = {
|
||||
fps: 60,
|
||||
correction: 1,
|
||||
deltaSampleSize: 60,
|
||||
counterTimestamp: 0,
|
||||
frameCounter: 0,
|
||||
deltaHistory: [],
|
||||
timePrev: null,
|
||||
timeScalePrev: 1,
|
||||
frameRequestId: null,
|
||||
isFixed: false,
|
||||
enabled: true
|
||||
|
@ -87,8 +85,8 @@ var Common = require('./Common');
|
|||
runner = Runner.create();
|
||||
}
|
||||
|
||||
(function render(time){
|
||||
runner.frameRequestId = _requestAnimationFrame(render);
|
||||
(function run(time){
|
||||
runner.frameRequestId = _requestAnimationFrame(run);
|
||||
|
||||
if (time && runner.enabled) {
|
||||
Runner.tick(runner, engine, time);
|
||||
|
@ -109,16 +107,8 @@ var Common = require('./Common');
|
|||
*/
|
||||
Runner.tick = function(runner, engine, time) {
|
||||
var timing = engine.timing,
|
||||
correction = 1,
|
||||
delta;
|
||||
|
||||
// create an event object
|
||||
var event = {
|
||||
timestamp: timing.timestamp
|
||||
};
|
||||
|
||||
Events.trigger(runner, 'beforeTick', event);
|
||||
|
||||
if (runner.isFixed) {
|
||||
// fixed timestep
|
||||
delta = runner.delta;
|
||||
|
@ -131,27 +121,21 @@ var Common = require('./Common');
|
|||
runner.deltaHistory.push(delta);
|
||||
runner.deltaHistory = runner.deltaHistory.slice(-runner.deltaSampleSize);
|
||||
delta = Math.min.apply(null, runner.deltaHistory);
|
||||
|
||||
|
||||
// limit delta
|
||||
delta = delta < runner.deltaMin ? runner.deltaMin : delta;
|
||||
delta = delta > runner.deltaMax ? runner.deltaMax : delta;
|
||||
|
||||
// correction for delta
|
||||
correction = delta / runner.delta;
|
||||
|
||||
// update engine timing object
|
||||
runner.delta = delta;
|
||||
}
|
||||
|
||||
// time correction for time scaling
|
||||
if (runner.timeScalePrev !== 0)
|
||||
correction *= timing.timeScale / runner.timeScalePrev;
|
||||
// create an event object
|
||||
var event = {
|
||||
timestamp: timing.timestamp
|
||||
};
|
||||
|
||||
if (timing.timeScale === 0)
|
||||
correction = 0;
|
||||
|
||||
runner.timeScalePrev = timing.timeScale;
|
||||
runner.correction = correction;
|
||||
Events.trigger(runner, 'beforeTick', event);
|
||||
|
||||
// fps counter
|
||||
runner.frameCounter += 1;
|
||||
|
@ -165,7 +149,9 @@ var Common = require('./Common');
|
|||
|
||||
// update
|
||||
Events.trigger(runner, 'beforeUpdate', event);
|
||||
Engine.update(engine, delta, correction);
|
||||
|
||||
Engine.update(engine, delta);
|
||||
|
||||
Events.trigger(runner, 'afterUpdate', event);
|
||||
|
||||
Events.trigger(runner, 'afterTick', event);
|
||||
|
|
|
@ -8,7 +8,9 @@ var Sleeping = {};
|
|||
|
||||
module.exports = Sleeping;
|
||||
|
||||
var Body = require('../body/Body');
|
||||
var Events = require('./Events');
|
||||
var Common = require('./Common');
|
||||
|
||||
(function() {
|
||||
|
||||
|
@ -20,15 +22,18 @@ var Events = require('./Events');
|
|||
* Puts bodies to sleep or wakes them up depending on their motion.
|
||||
* @method update
|
||||
* @param {body[]} bodies
|
||||
* @param {number} timeScale
|
||||
* @param {number} delta
|
||||
*/
|
||||
Sleeping.update = function(bodies, timeScale) {
|
||||
var timeFactor = timeScale * timeScale * timeScale;
|
||||
|
||||
Sleeping.update = function(bodies, delta) {
|
||||
var timeScale = delta / Common._baseDelta,
|
||||
motionSleepThreshold = Sleeping._motionSleepThreshold;
|
||||
|
||||
// update bodies sleeping status
|
||||
for (var i = 0; i < bodies.length; i++) {
|
||||
var body = bodies[i],
|
||||
motion = body.speed * body.speed + body.angularSpeed * body.angularSpeed;
|
||||
speed = Body.getSpeed(body),
|
||||
angularSpeed = Body.getAngularSpeed(body),
|
||||
motion = speed * speed + angularSpeed * angularSpeed;
|
||||
|
||||
// wake up bodies if they have a force applied
|
||||
if (body.force.x !== 0 || body.force.y !== 0) {
|
||||
|
@ -41,12 +46,13 @@ var Events = require('./Events');
|
|||
|
||||
// biased average motion estimation between frames
|
||||
body.motion = Sleeping._minBias * minMotion + (1 - Sleeping._minBias) * maxMotion;
|
||||
|
||||
if (body.sleepThreshold > 0 && body.motion < Sleeping._motionSleepThreshold * timeFactor) {
|
||||
|
||||
if (body.sleepThreshold > 0 && body.motion < motionSleepThreshold) {
|
||||
body.sleepCounter += 1;
|
||||
|
||||
if (body.sleepCounter >= body.sleepThreshold)
|
||||
if (body.sleepCounter >= body.sleepThreshold / timeScale) {
|
||||
Sleeping.set(body, true);
|
||||
}
|
||||
} else if (body.sleepCounter > 0) {
|
||||
body.sleepCounter -= 1;
|
||||
}
|
||||
|
@ -57,10 +63,9 @@ var Events = require('./Events');
|
|||
* Given a set of colliding pairs, wakes the sleeping bodies involved.
|
||||
* @method afterCollisions
|
||||
* @param {pair[]} pairs
|
||||
* @param {number} timeScale
|
||||
*/
|
||||
Sleeping.afterCollisions = function(pairs, timeScale) {
|
||||
var timeFactor = timeScale * timeScale * timeScale;
|
||||
Sleeping.afterCollisions = function(pairs) {
|
||||
var motionSleepThreshold = Sleeping._motionSleepThreshold;
|
||||
|
||||
// wake up bodies involved in collisions
|
||||
for (var i = 0; i < pairs.length; i++) {
|
||||
|
@ -82,7 +87,7 @@ var Events = require('./Events');
|
|||
var sleepingBody = (bodyA.isSleeping && !bodyA.isStatic) ? bodyA : bodyB,
|
||||
movingBody = sleepingBody === bodyA ? bodyB : bodyA;
|
||||
|
||||
if (!sleepingBody.isStatic && movingBody.motion > Sleeping._motionWakeThreshold * timeFactor) {
|
||||
if (!sleepingBody.isStatic && movingBody.motion > motionSleepThreshold) {
|
||||
Sleeping.set(sleepingBody, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ var Render = {};
|
|||
|
||||
module.exports = Render;
|
||||
|
||||
var Body = require('../body/Body');
|
||||
var Common = require('../core/Common');
|
||||
var Composite = require('../body/Composite');
|
||||
var Bounds = require('../geometry/Bounds');
|
||||
|
@ -1106,8 +1107,10 @@ var Mouse = require('../core/Mouse');
|
|||
if (!body.render.visible)
|
||||
continue;
|
||||
|
||||
var velocity = Body.getVelocity(body);
|
||||
|
||||
c.moveTo(body.position.x, body.position.y);
|
||||
c.lineTo(body.position.x + (body.position.x - body.positionPrev.x) * 2, body.position.y + (body.position.y - body.positionPrev.y) * 2);
|
||||
c.lineTo(body.position.x + velocity.x, body.position.y + velocity.y);
|
||||
}
|
||||
|
||||
c.lineWidth = 3;
|
||||
|
|
Loading…
Reference in a new issue