mirror of
https://github.com/photonstorm/phaser
synced 2024-11-22 12:43:26 +00:00
Upgrade to MatterJS v0.19
This commit is contained in:
parent
b9b9ca9e64
commit
24c85af875
15 changed files with 1031 additions and 491 deletions
|
@ -110,20 +110,6 @@ var World = new Class({
|
|||
*/
|
||||
this.enabled = GetValue(config, 'enabled', true);
|
||||
|
||||
/**
|
||||
* 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 is constant (or when no correction is desired, which is the default).
|
||||
* See the paper on Time Corrected Verlet for more information.
|
||||
*
|
||||
* @name Phaser.Physics.Matter.World#correction
|
||||
* @type {number}
|
||||
* @default 1
|
||||
* @since 3.4.0
|
||||
*/
|
||||
this.correction = GetValue(config, 'correction', 1);
|
||||
|
||||
/**
|
||||
* This function is called every time the core game loop steps, which is bound to the
|
||||
* Request Animation Frame frequency unless otherwise modified.
|
||||
|
@ -181,7 +167,6 @@ var World = new Class({
|
|||
*/
|
||||
this.runner = {
|
||||
fps: fps,
|
||||
correction: GetFastValue(runnerConfig, 'correction', 1),
|
||||
deltaSampleSize: GetFastValue(runnerConfig, 'deltaSampleSize', 60),
|
||||
counterTimestamp: 0,
|
||||
frameCounter: 0,
|
||||
|
@ -1122,7 +1107,6 @@ var World = new Class({
|
|||
var runner = this.runner;
|
||||
|
||||
var timing = engine.timing;
|
||||
var correction = this.correction;
|
||||
|
||||
if (runner.isFixed)
|
||||
{
|
||||
|
@ -1144,26 +1128,11 @@ var World = new Class({
|
|||
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;
|
||||
}
|
||||
|
||||
if (timing.timeScale === 0)
|
||||
{
|
||||
correction = 0;
|
||||
}
|
||||
|
||||
runner.timeScalePrev = timing.timeScale;
|
||||
runner.correction = correction;
|
||||
|
||||
// fps counter
|
||||
runner.frameCounter += 1;
|
||||
|
@ -1175,7 +1144,7 @@ var World = new Class({
|
|||
runner.frameCounter = 0;
|
||||
}
|
||||
|
||||
Engine.update(engine, delta, correction);
|
||||
Engine.update(engine, delta);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1203,11 +1172,10 @@ var World = new Class({
|
|||
* @since 3.4.0
|
||||
*
|
||||
* @param {number} [delta=16.666] - The delta value.
|
||||
* @param {number} [correction=1] - Optional delta correction value.
|
||||
*/
|
||||
step: function (delta, correction)
|
||||
step: function (delta)
|
||||
{
|
||||
Engine.update(this.engine, delta, correction);
|
||||
Engine.update(this.engine, delta);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,10 +20,12 @@ 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.
|
||||
|
@ -82,6 +84,7 @@ var Axes = require('../geometry/Axes');
|
|||
mass: 0,
|
||||
inverseMass: 0,
|
||||
inertia: 0,
|
||||
deltaTime: 1000 / 60,
|
||||
inverseInertia: 0,
|
||||
_original: null,
|
||||
render: {
|
||||
|
@ -189,11 +192,9 @@ var Axes = require('../geometry/Axes');
|
|||
parent: body.parent || body
|
||||
});
|
||||
|
||||
var bounds = body.bounds;
|
||||
|
||||
Vertices.rotate(body.vertices, body.angle, body.position);
|
||||
Axes.rotate(body.axes, body.angle);
|
||||
Bounds.update(bounds, body.vertices, body.velocity);
|
||||
Bounds.update(body.bounds, body.vertices, body.velocity);
|
||||
|
||||
// allow options to override the automatically calculated properties
|
||||
Body.set(body, {
|
||||
|
@ -205,6 +206,8 @@ var Axes = require('../geometry/Axes');
|
|||
|
||||
if (body.parts.length === 1)
|
||||
{
|
||||
var bounds = body.bounds;
|
||||
|
||||
var centerOfMass = body.centerOfMass;
|
||||
var centerOffset = body.centerOffset;
|
||||
|
||||
|
@ -217,6 +220,10 @@ var Axes = require('../geometry/Axes');
|
|||
centerOffset.x = bodyWidth * centerOfMass.x;
|
||||
centerOffset.y = bodyHeight * centerOfMass.y;
|
||||
}
|
||||
|
||||
// From Matter render code:
|
||||
// body.render.sprite.xOffset += -(body.bounds.min.x - body.position.x) / (body.bounds.max.x - body.bounds.min.x);
|
||||
// body.render.sprite.yOffset += -(body.bounds.min.y - body.position.y) / (body.bounds.max.y - body.bounds.min.y);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -273,6 +280,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;
|
||||
|
@ -294,9 +307,9 @@ var Axes = require('../geometry/Axes');
|
|||
Body.setStatic = function(body, isStatic) {
|
||||
for (var i = 0; i < body.parts.length; i++) {
|
||||
var part = body.parts[i];
|
||||
part.isStatic = isStatic;
|
||||
|
||||
if (isStatic) {
|
||||
if (!part.isStatic) {
|
||||
part._original = {
|
||||
restitution: part.restitution,
|
||||
friction: part.friction,
|
||||
|
@ -306,6 +319,7 @@ var Axes = require('../geometry/Axes');
|
|||
inverseMass: part.inverseMass,
|
||||
inverseInertia: part.inverseInertia
|
||||
};
|
||||
}
|
||||
|
||||
part.restitution = 0;
|
||||
part.friction = 1;
|
||||
|
@ -330,6 +344,7 @@ var Axes = require('../geometry/Axes');
|
|||
|
||||
part._original = null;
|
||||
}
|
||||
part.isStatic = isStatic;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -484,6 +499,12 @@ var Axes = require('../geometry/Axes');
|
|||
body.positionPrev.x = cx;
|
||||
body.positionPrev.y = cy;
|
||||
|
||||
// Matter.js original
|
||||
// body.position.x = total.centre.x;
|
||||
// body.position.y = total.centre.y;
|
||||
// body.positionPrev.x = total.centre.x;
|
||||
// body.positionPrev.y = total.centre.y;
|
||||
|
||||
Body.setMass(body, total.mass);
|
||||
Body.setInertia(body, total.inertia);
|
||||
Body.setPosition(body, total.centre);
|
||||
|
@ -520,10 +541,18 @@ var Axes = require('../geometry/Axes');
|
|||
* @param {body} body
|
||||
* @param {vector} position
|
||||
*/
|
||||
Body.setPosition = function(body, position) {
|
||||
Body.setPosition = function(body, position, updateVelocity) {
|
||||
var delta = Vector.sub(position, body.position);
|
||||
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];
|
||||
|
@ -540,9 +569,15 @@ var Axes = require('../geometry/Axes');
|
|||
* @param {body} body
|
||||
* @param {number} angle
|
||||
*/
|
||||
Body.setAngle = function(body, angle) {
|
||||
Body.setAngle = function(body, angle, updateVelocity) {
|
||||
var delta = angle - body.angle;
|
||||
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];
|
||||
|
@ -563,13 +598,51 @@ var Axes = require('../geometry/Axes');
|
|||
* @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);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 angular velocity of the body instantly. Position, angle, force etc. are unchanged. See also `Body.applyForce`.
|
||||
* @method setAngularVelocity
|
||||
|
@ -577,19 +650,52 @@ var Axes = require('../geometry/Axes');
|
|||
* @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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the current rotational velocity of the body.
|
||||
* @method getAngularVelocity
|
||||
* @param {body} body
|
||||
* @return {number} angular velocity
|
||||
*/
|
||||
Body.getAngularVelocity = function(body) {
|
||||
return (body.angle - body.anglePrev) * Body._baseDelta / body.deltaTime;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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, without imparting any velocity.
|
||||
* @method translate
|
||||
* @param {body} body
|
||||
* @param {vector} translation
|
||||
*/
|
||||
Body.translate = function(body, translation) {
|
||||
Body.setPosition(body, Vector.add(body.position, translation));
|
||||
Body.translate = function(body, translation, updateVelocity) {
|
||||
Body.setPosition(body, Vector.add(body.position, translation), updateVelocity);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -599,9 +705,9 @@ var Axes = require('../geometry/Axes');
|
|||
* @param {number} rotation
|
||||
* @param {vector} [point]
|
||||
*/
|
||||
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),
|
||||
|
@ -611,9 +717,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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -692,22 +798,25 @@ var Axes = require('../geometry/Axes');
|
|||
* @param {number} timeScale
|
||||
* @param {number} correction
|
||||
*/
|
||||
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;
|
||||
|
@ -741,6 +850,23 @@ var Axes = require('../geometry/Axes');
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 a force to a body from a given world-space position, including resulting torque.
|
||||
* @method applyForce
|
||||
|
@ -749,9 +875,9 @@ var Axes = require('../geometry/Axes');
|
|||
* @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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
@ -33,10 +35,10 @@ var Bounds = require('../geometry/Bounds');
|
|||
// find total contacts on each body
|
||||
for (i = 0; i < pairsLength; i++) {
|
||||
pair = pairs[i];
|
||||
|
||||
|
||||
if (!pair.isActive)
|
||||
continue;
|
||||
|
||||
|
||||
activeCount = pair.activeContacts.length;
|
||||
pair.collision.parentA.totalContacts += activeCount;
|
||||
pair.collision.parentB.totalContacts += activeCount;
|
||||
|
@ -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,13 +61,14 @@ 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
|
||||
for (i = 0; i < pairsLength; i++) {
|
||||
pair = pairs[i];
|
||||
|
||||
|
||||
if (!pair.isActive || pair.isSensor)
|
||||
continue;
|
||||
|
||||
|
@ -74,26 +78,26 @@ var Bounds = require('../geometry/Bounds');
|
|||
normal = collision.normal;
|
||||
|
||||
// get current separation between body edges involved in collision
|
||||
pair.separation =
|
||||
pair.separation =
|
||||
normal.x * (bodyB.positionImpulse.x + collision.penetration.x - bodyA.positionImpulse.x)
|
||||
+ normal.y * (bodyB.positionImpulse.y + collision.penetration.y - bodyA.positionImpulse.y);
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < pairsLength; i++) {
|
||||
pair = pairs[i];
|
||||
|
||||
if (!pair.isActive || pair.isSensor)
|
||||
continue;
|
||||
|
||||
|
||||
collision = pair.collision;
|
||||
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;
|
||||
|
||||
|
||||
if (!(bodyA.isStatic || bodyA.isSleeping)) {
|
||||
contactShare = positionDampen / bodyA.totalContacts;
|
||||
bodyA.positionImpulse.x += normal.x * positionImpulse * contactShare;
|
||||
|
@ -165,13 +169,13 @@ var Bounds = require('../geometry/Bounds');
|
|||
var pairsLength = pairs.length,
|
||||
i,
|
||||
j;
|
||||
|
||||
|
||||
for (i = 0; i < pairsLength; i++) {
|
||||
var pair = pairs[i];
|
||||
|
||||
|
||||
if (!pair.isActive || pair.isSensor)
|
||||
continue;
|
||||
|
||||
|
||||
var contacts = pair.activeContacts,
|
||||
contactsLength = contacts.length,
|
||||
collision = pair.collision,
|
||||
|
@ -179,19 +183,19 @@ var Bounds = require('../geometry/Bounds');
|
|||
bodyB = collision.parentB,
|
||||
normal = collision.normal,
|
||||
tangent = collision.tangent;
|
||||
|
||||
|
||||
// resolve each contact
|
||||
for (j = 0; j < contactsLength; j++) {
|
||||
var contact = contacts[j],
|
||||
contactVertex = contact.vertex,
|
||||
normalImpulse = contact.normalImpulse,
|
||||
tangentImpulse = contact.tangentImpulse;
|
||||
|
||||
|
||||
if (normalImpulse !== 0 || tangentImpulse !== 0) {
|
||||
// total impulse from contact
|
||||
var impulseX = normal.x * normalImpulse + tangent.x * tangentImpulse,
|
||||
impulseY = normal.y * normalImpulse + tangent.y * tangentImpulse;
|
||||
|
||||
|
||||
// apply impulse from contact
|
||||
if (!(bodyA.isStatic || bodyA.isSleeping)) {
|
||||
bodyA.positionPrev.x += impulseX * bodyA.inverseMass;
|
||||
|
@ -201,12 +205,12 @@ var Bounds = require('../geometry/Bounds');
|
|||
- (contactVertex.y - bodyA.position.y) * impulseX
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (!(bodyB.isStatic || bodyB.isSleeping)) {
|
||||
bodyB.positionPrev.x -= impulseX * bodyB.inverseMass;
|
||||
bodyB.positionPrev.y -= impulseY * bodyB.inverseMass;
|
||||
bodyB.anglePrev -= bodyB.inverseInertia * (
|
||||
(contactVertex.x - bodyB.position.x) * impulseY
|
||||
(contactVertex.x - bodyB.position.x) * impulseY
|
||||
- (contactVertex.y - bodyB.position.y) * impulseX
|
||||
);
|
||||
}
|
||||
|
@ -221,12 +225,14 @@ var Bounds = require('../geometry/Bounds');
|
|||
* @param {pair[]} pairs
|
||||
* @param {number} timeScale
|
||||
*/
|
||||
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,
|
||||
|
@ -235,10 +241,10 @@ var Bounds = require('../geometry/Bounds');
|
|||
|
||||
for (i = 0; i < pairsLength; i++) {
|
||||
var pair = pairs[i];
|
||||
|
||||
|
||||
if (!pair.isActive || pair.isSensor)
|
||||
continue;
|
||||
|
||||
|
||||
var collision = pair.collision,
|
||||
bodyA = collision.parentA,
|
||||
bodyB = collision.parentB,
|
||||
|
@ -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;
|
||||
|
@ -271,7 +277,7 @@ var Bounds = require('../geometry/Bounds');
|
|||
offsetAY = contactVertex.y - bodyA.position.y,
|
||||
offsetBX = contactVertex.x - bodyB.position.x,
|
||||
offsetBY = contactVertex.y - bodyB.position.y;
|
||||
|
||||
|
||||
var velocityPointAX = bodyAVelocity.x - offsetAY * bodyA.angularVelocity,
|
||||
velocityPointAY = bodyAVelocity.y + offsetAX * bodyA.angularVelocity,
|
||||
velocityPointBX = bodyBVelocity.x - offsetBY * bodyB.angularVelocity,
|
||||
|
@ -287,13 +293,13 @@ 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;
|
||||
} else if (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 {
|
||||
|
@ -343,7 +349,7 @@ var Bounds = require('../geometry/Bounds');
|
|||
// total impulse from contact
|
||||
var impulseX = normalX * normalImpulse + tangentX * tangentImpulse,
|
||||
impulseY = normalY * normalImpulse + tangentY * tangentImpulse;
|
||||
|
||||
|
||||
// apply impulse from contact
|
||||
if (!(bodyA.isStatic || bodyA.isSleeping)) {
|
||||
bodyA.positionPrev.x += impulseX * bodyA.inverseMass;
|
||||
|
|
|
@ -114,9 +114,10 @@ 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],
|
||||
|
@ -187,7 +188,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),
|
||||
|
@ -198,7 +202,7 @@ var Common = require('../core/Common');
|
|||
normalVelocity,
|
||||
relativeVelocity;
|
||||
|
||||
if (constraint.damping) {
|
||||
if (damping > 0) {
|
||||
var zero = Vector.create();
|
||||
normal = Vector.div(delta, currentLength);
|
||||
|
||||
|
@ -222,9 +226,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
|
||||
|
@ -245,9 +249,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
|
||||
|
@ -334,6 +338,32 @@ var Common = require('../core/Common');
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the current length of the constraint.
|
||||
* This is the distance between both of the constraint's end points.
|
||||
* See `constraint.length` for the target rest length.
|
||||
* @method currentLength
|
||||
* @param {constraint} constraint
|
||||
* @returns {number} the current length
|
||||
*/
|
||||
Constraint.currentLength = function(constraint) {
|
||||
var pointAX = (constraint.bodyA ? constraint.bodyA.position.x : 0)
|
||||
+ (constraint.pointA ? constraint.pointA.x : 0);
|
||||
|
||||
var pointAY = (constraint.bodyA ? constraint.bodyA.position.y : 0)
|
||||
+ (constraint.pointA ? constraint.pointA.y : 0);
|
||||
|
||||
var pointBX = (constraint.bodyB ? constraint.bodyB.position.x : 0)
|
||||
+ (constraint.pointB ? constraint.pointB.x : 0);
|
||||
|
||||
var pointBY = (constraint.bodyB ? constraint.bodyB.position.y : 0)
|
||||
+ (constraint.pointB ? constraint.pointB.y : 0);
|
||||
|
||||
var deltaX = pointAX - pointBX;
|
||||
var deltaY = pointAY - pointBY;
|
||||
|
||||
return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
};
|
||||
/*
|
||||
*
|
||||
* Properties Documentation
|
||||
|
|
|
@ -10,6 +10,7 @@ module.exports = Common;
|
|||
|
||||
(function() {
|
||||
|
||||
Common._baseDelta = 1000 / 60;
|
||||
Common._nextId = 0;
|
||||
Common._seed = 0;
|
||||
Common._nowStartTime = +(new Date());
|
||||
|
|
|
@ -66,31 +66,21 @@ var Body = require('../body/Body');
|
|||
engine.world.gravity = engine.gravity;
|
||||
engine.broadphase = engine.grid;
|
||||
engine.metrics = {};
|
||||
|
||||
|
||||
return engine;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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,33 @@ 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);
|
||||
}
|
||||
|
||||
Events.trigger(engine, 'beforeSolve', event);
|
||||
|
||||
// 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,38 +146,58 @@ var Body = require('../body/Body');
|
|||
|
||||
// wake up bodies involved in collisions
|
||||
if (engine.enableSleeping)
|
||||
Sleeping.afterCollisions(pairs.list, timing.timeScale);
|
||||
Sleeping.afterCollisions(pairs.list);
|
||||
|
||||
// trigger collision events
|
||||
if (pairs.collisionStart.length > 0)
|
||||
Events.trigger(engine, 'collisionStart', { pairs: pairs.collisionStart });
|
||||
if (pairs.collisionStart.length > 0) {
|
||||
Events.trigger(engine, 'collisionStart', {
|
||||
pairs: pairs.collisionStart,
|
||||
timestamp: timing.timestamp,
|
||||
delta: delta
|
||||
});
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// trigger collision events
|
||||
if (pairs.collisionActive.length > 0)
|
||||
Events.trigger(engine, 'collisionActive', { pairs: pairs.collisionActive });
|
||||
// update body speed and velocity properties
|
||||
Engine._bodiesUpdateVelocities(allBodies);
|
||||
|
||||
if (pairs.collisionEnd.length > 0)
|
||||
Events.trigger(engine, 'collisionEnd', { pairs: pairs.collisionEnd });
|
||||
// trigger collision events
|
||||
if (pairs.collisionActive.length > 0) {
|
||||
Events.trigger(engine, 'collisionActive', {
|
||||
pairs: pairs.collisionActive,
|
||||
timestamp: timing.timestamp,
|
||||
delta: delta
|
||||
});
|
||||
}
|
||||
|
||||
if (pairs.collisionEnd.length > 0) {
|
||||
Events.trigger(engine, 'collisionEnd', {
|
||||
pairs: pairs.collisionEnd,
|
||||
timestamp: timing.timestamp,
|
||||
delta: delta
|
||||
});
|
||||
}
|
||||
|
||||
// clear force buffers
|
||||
Engine._bodiesClearForces(allBodies);
|
||||
|
@ -192,7 +209,7 @@ var Body = require('../body/Body');
|
|||
|
||||
return engine;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Merges two engines by keeping the configuration of `engineA` but replacing the world with the one from `engineB`.
|
||||
* @method merge
|
||||
|
@ -201,7 +218,7 @@ var Body = require('../body/Body');
|
|||
*/
|
||||
Engine.merge = function(engineA, engineB) {
|
||||
Common.extend(engineA, engineB);
|
||||
|
||||
|
||||
if (engineB.world) {
|
||||
engineA.world = engineB.world;
|
||||
|
||||
|
@ -234,7 +251,8 @@ 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
|
||||
|
@ -252,44 +270,56 @@ var Body = require('../body/Body');
|
|||
* @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++) {
|
||||
var body = bodies[i];
|
||||
|
||||
if (body.ignoreGravity || body.isStatic || body.isSleeping)
|
||||
continue;
|
||||
|
||||
// apply gravity
|
||||
body.force.x += (body.mass * gravity.x * gravityScale) * body.gravityScale.x;
|
||||
body.force.y += (body.mass * gravity.y * gravityScale) * body.gravityScale.y;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Applys `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
|
||||
*/
|
||||
Engine._bodiesUpdate = function(bodies, deltaTime, timeScale, correction, worldBounds) {
|
||||
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;
|
||||
|
||||
Body.update(body, deltaTime, timeScale, correction);
|
||||
// add the resultant force of gravity
|
||||
body.force.y += body.mass * gravity.y * gravityScale;
|
||||
body.force.x += body.mass * gravity.x * gravityScale;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies `Body.update` to all given `bodies`.
|
||||
* @method _bodiesUpdate
|
||||
* @private
|
||||
* @param {body[]} bodies
|
||||
* @param {number} delta The amount of time elapsed between updates
|
||||
*/
|
||||
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, 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]);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -397,7 +427,7 @@ var Body = require('../body/Body');
|
|||
*/
|
||||
|
||||
/**
|
||||
* An `Object` containing properties regarding the timing systems of the engine.
|
||||
* An `Object` containing properties regarding the timing systems of the engine.
|
||||
*
|
||||
* @property timing
|
||||
* @type object
|
||||
|
@ -415,8 +445,8 @@ 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.
|
||||
* 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
|
||||
|
|
|
@ -27,7 +27,7 @@ var Common = require('./Common');
|
|||
* @readOnly
|
||||
* @type {String}
|
||||
*/
|
||||
Matter.version = '0.18.0';
|
||||
Matter.version = '0.19.0';
|
||||
|
||||
/**
|
||||
* A list of plugin dependencies to be installed. These are normally set and installed through `Matter.use`.
|
||||
|
|
|
@ -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,8 @@ 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ var Vector = require('../geometry/Vector');
|
|||
|
||||
/**
|
||||
* Creates a new rigid body model with a trapezoid hull.
|
||||
* The `slope` is parameterised as a fraction of `width` and must be < 1 to form a valid trapezoid.
|
||||
* The options parameter is an object that specifies any properties you wish to override the defaults.
|
||||
* See the properties section of the `Matter.Body` module for detailed information on what you can pass via the `options` object.
|
||||
* @method trapezoid
|
||||
|
@ -61,7 +62,7 @@ var Vector = require('../geometry/Vector');
|
|||
* @param {number} y
|
||||
* @param {number} width
|
||||
* @param {number} height
|
||||
* @param {number} slope
|
||||
* @param {number} slope Must be a number < 1.
|
||||
* @param {object} [options]
|
||||
* @return {body} A new trapezoid body
|
||||
*/
|
||||
|
@ -309,9 +310,37 @@ var Vector = require('../geometry/Vector');
|
|||
}
|
||||
|
||||
// flag internal edges (coincident part edges)
|
||||
if (flagInternal)
|
||||
{
|
||||
Bodies.flagCoincidentParts(parts, 5);
|
||||
if (flagInternal) {
|
||||
var coincident_max_dist = 5;
|
||||
|
||||
for (i = 0; i < parts.length; i++) {
|
||||
var partA = parts[i];
|
||||
|
||||
for (j = i + 1; j < parts.length; j++) {
|
||||
var partB = parts[j];
|
||||
|
||||
if (Bounds.overlaps(partA.bounds, partB.bounds)) {
|
||||
var pav = partA.vertices,
|
||||
pbv = partB.vertices;
|
||||
|
||||
// iterate vertices of both parts
|
||||
for (k = 0; k < partA.vertices.length; k++) {
|
||||
for (z = 0; z < partB.vertices.length; z++) {
|
||||
// find distances between the vertices
|
||||
var da = Vector.magnitudeSquared(Vector.sub(pav[(k + 1) % pav.length], pbv[z])),
|
||||
db = Vector.magnitudeSquared(Vector.sub(pav[k], pbv[(z + 1) % pbv.length]));
|
||||
|
||||
// if both vertices are very close, consider the edge concident (internal)
|
||||
if (da < coincident_max_dist && db < coincident_max_dist) {
|
||||
pav[k].isInternal = true;
|
||||
pbv[z].isInternal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parts.length > 1) {
|
||||
|
|
|
@ -16,7 +16,6 @@ var Constraint = require('../constraint/Constraint');
|
|||
var Common = require('../core/Common');
|
||||
var Body = require('../body/Body');
|
||||
var Bodies = require('./Bodies');
|
||||
var deprecated = Common.deprecated;
|
||||
|
||||
(function() {
|
||||
|
||||
|
@ -24,8 +23,8 @@ var deprecated = Common.deprecated;
|
|||
* Create a new composite containing bodies created in the callback in a grid arrangement.
|
||||
* This function uses the body's bounds to prevent overlaps.
|
||||
* @method stack
|
||||
* @param {number} xx
|
||||
* @param {number} yy
|
||||
* @param {number} x Starting position in X.
|
||||
* @param {number} y Starting position in Y.
|
||||
* @param {number} columns
|
||||
* @param {number} rows
|
||||
* @param {number} columnGap
|
||||
|
@ -33,46 +32,46 @@ var deprecated = Common.deprecated;
|
|||
* @param {function} callback
|
||||
* @return {composite} A new composite containing objects created in the callback
|
||||
*/
|
||||
Composites.stack = function(xx, yy, columns, rows, columnGap, rowGap, callback) {
|
||||
Composites.stack = function(x, y, columns, rows, columnGap, rowGap, callback) {
|
||||
var stack = Composite.create({ label: 'Stack' }),
|
||||
x = xx,
|
||||
y = yy,
|
||||
currentX = x,
|
||||
currentY = y,
|
||||
lastBody,
|
||||
i = 0;
|
||||
|
||||
for (var row = 0; row < rows; row++) {
|
||||
var maxHeight = 0;
|
||||
|
||||
|
||||
for (var column = 0; column < columns; column++) {
|
||||
var body = callback(x, y, column, row, lastBody, i);
|
||||
|
||||
var body = callback(currentX, currentY, column, row, lastBody, i);
|
||||
|
||||
if (body) {
|
||||
var bodyHeight = body.bounds.max.y - body.bounds.min.y,
|
||||
bodyWidth = body.bounds.max.x - body.bounds.min.x;
|
||||
bodyWidth = body.bounds.max.x - body.bounds.min.x;
|
||||
|
||||
if (bodyHeight > maxHeight)
|
||||
maxHeight = bodyHeight;
|
||||
|
||||
|
||||
Body.translate(body, { x: bodyWidth * 0.5, y: bodyHeight * 0.5 });
|
||||
|
||||
x = body.bounds.max.x + columnGap;
|
||||
currentX = body.bounds.max.x + columnGap;
|
||||
|
||||
Composite.addBody(stack, body);
|
||||
|
||||
|
||||
lastBody = body;
|
||||
i += 1;
|
||||
} else {
|
||||
x += columnGap;
|
||||
currentX += columnGap;
|
||||
}
|
||||
}
|
||||
|
||||
y += maxHeight + rowGap;
|
||||
x = xx;
|
||||
|
||||
currentY += maxHeight + rowGap;
|
||||
currentX = x;
|
||||
}
|
||||
|
||||
return stack;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Chains all bodies in the given composite together using constraints.
|
||||
* @method chain
|
||||
|
@ -86,29 +85,29 @@ var deprecated = Common.deprecated;
|
|||
*/
|
||||
Composites.chain = function(composite, xOffsetA, yOffsetA, xOffsetB, yOffsetB, options) {
|
||||
var bodies = composite.bodies;
|
||||
|
||||
|
||||
for (var i = 1; i < bodies.length; i++) {
|
||||
var bodyA = bodies[i - 1],
|
||||
bodyB = bodies[i],
|
||||
bodyAHeight = bodyA.bounds.max.y - bodyA.bounds.min.y,
|
||||
bodyAWidth = bodyA.bounds.max.x - bodyA.bounds.min.x,
|
||||
bodyAWidth = bodyA.bounds.max.x - bodyA.bounds.min.x,
|
||||
bodyBHeight = bodyB.bounds.max.y - bodyB.bounds.min.y,
|
||||
bodyBWidth = bodyB.bounds.max.x - bodyB.bounds.min.x;
|
||||
|
||||
|
||||
var defaults = {
|
||||
bodyA: bodyA,
|
||||
pointA: { x: bodyAWidth * xOffsetA, y: bodyAHeight * yOffsetA },
|
||||
bodyB: bodyB,
|
||||
pointB: { x: bodyBWidth * xOffsetB, y: bodyBHeight * yOffsetB }
|
||||
};
|
||||
|
||||
|
||||
var constraint = Common.extend(defaults, options);
|
||||
|
||||
|
||||
Composite.addConstraint(composite, Constraint.create(constraint));
|
||||
}
|
||||
|
||||
composite.label += ' Chain';
|
||||
|
||||
|
||||
return composite;
|
||||
};
|
||||
|
||||
|
@ -129,7 +128,7 @@ var deprecated = Common.deprecated;
|
|||
bodyA,
|
||||
bodyB,
|
||||
bodyC;
|
||||
|
||||
|
||||
for (row = 0; row < rows; row++) {
|
||||
for (col = 1; col < columns; col++) {
|
||||
bodyA = bodies[(col - 1) + (row * columns)];
|
||||
|
@ -157,16 +156,16 @@ var deprecated = Common.deprecated;
|
|||
}
|
||||
|
||||
composite.label += ' Mesh';
|
||||
|
||||
|
||||
return composite;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create a new composite containing bodies created in the callback in a pyramid arrangement.
|
||||
* This function uses the body's bounds to prevent overlaps.
|
||||
* @method pyramid
|
||||
* @param {number} xx
|
||||
* @param {number} yy
|
||||
* @param {number} x Starting position in X.
|
||||
* @param {number} y Starting position in Y.
|
||||
* @param {number} columns
|
||||
* @param {number} rows
|
||||
* @param {number} columnGap
|
||||
|
@ -174,31 +173,31 @@ var deprecated = Common.deprecated;
|
|||
* @param {function} callback
|
||||
* @return {composite} A new composite containing objects created in the callback
|
||||
*/
|
||||
Composites.pyramid = function(xx, yy, columns, rows, columnGap, rowGap, callback) {
|
||||
return Composites.stack(xx, yy, columns, rows, columnGap, rowGap, function(x, y, column, row, lastBody, i) {
|
||||
Composites.pyramid = function(x, y, columns, rows, columnGap, rowGap, callback) {
|
||||
return Composites.stack(x, y, columns, rows, columnGap, rowGap, function(stackX, stackY, column, row, lastBody, i) {
|
||||
var actualRows = Math.min(rows, Math.ceil(columns / 2)),
|
||||
lastBodyWidth = lastBody ? lastBody.bounds.max.x - lastBody.bounds.min.x : 0;
|
||||
|
||||
|
||||
if (row > actualRows)
|
||||
return;
|
||||
|
||||
|
||||
// reverse row order
|
||||
row = actualRows - row;
|
||||
|
||||
|
||||
var start = row,
|
||||
end = columns - 1 - row;
|
||||
|
||||
if (column < start || column > end)
|
||||
return;
|
||||
|
||||
|
||||
// retroactively fix the first body's position, since width was unknown
|
||||
if (i === 1) {
|
||||
Body.translate(lastBody, { x: (column + (columns % 2 === 1 ? 1 : -1)) * lastBodyWidth, y: 0 });
|
||||
}
|
||||
|
||||
var xOffset = lastBody ? column * lastBodyWidth : 0;
|
||||
|
||||
return callback(xx + xOffset + column * columnGap, y, column, row, lastBody, i);
|
||||
|
||||
return callback(x + xOffset + column * columnGap, stackY, column, row, lastBody, i);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -206,21 +205,21 @@ var deprecated = Common.deprecated;
|
|||
* This has now moved to the [newtonsCradle example](https://github.com/liabru/matter-js/blob/master/examples/newtonsCradle.js), follow that instead as this function is deprecated here.
|
||||
* @deprecated moved to newtonsCradle example
|
||||
* @method newtonsCradle
|
||||
* @param {number} xx
|
||||
* @param {number} yy
|
||||
* @param {number} x Starting position in X.
|
||||
* @param {number} y Starting position in Y.
|
||||
* @param {number} number
|
||||
* @param {number} size
|
||||
* @param {number} length
|
||||
* @return {composite} A new composite newtonsCradle body
|
||||
*/
|
||||
Composites.newtonsCradle = function(xx, yy, number, size, length) {
|
||||
Composites.newtonsCradle = function(x, y, number, size, length) {
|
||||
var newtonsCradle = Composite.create({ label: 'Newtons Cradle' });
|
||||
|
||||
for (var i = 0; i < number; i++) {
|
||||
var separation = 1.9,
|
||||
circle = Bodies.circle(xx + i * (size * separation), yy + length, size,
|
||||
circle = Bodies.circle(x + i * (size * separation), y + length, size,
|
||||
{ inertia: Infinity, restitution: 1, friction: 0, frictionAir: 0.0001, slop: 1 }),
|
||||
constraint = Constraint.create({ pointA: { x: xx + i * (size * separation), y: yy }, bodyB: circle });
|
||||
constraint = Constraint.create({ pointA: { x: x + i * (size * separation), y: y }, bodyB: circle });
|
||||
|
||||
Composite.addBody(newtonsCradle, circle);
|
||||
Composite.addConstraint(newtonsCradle, constraint);
|
||||
|
@ -229,28 +228,26 @@ var deprecated = Common.deprecated;
|
|||
return newtonsCradle;
|
||||
};
|
||||
|
||||
deprecated(Composites, 'newtonsCradle', 'Composites.newtonsCradle ➤ moved to newtonsCradle example');
|
||||
|
||||
/**
|
||||
* This has now moved to the [car example](https://github.com/liabru/matter-js/blob/master/examples/car.js), follow that instead as this function is deprecated here.
|
||||
* @deprecated moved to car example
|
||||
* @method car
|
||||
* @param {number} xx
|
||||
* @param {number} yy
|
||||
* @param {number} x Starting position in X.
|
||||
* @param {number} y Starting position in Y.
|
||||
* @param {number} width
|
||||
* @param {number} height
|
||||
* @param {number} wheelSize
|
||||
* @return {composite} A new composite car body
|
||||
*/
|
||||
Composites.car = function(xx, yy, width, height, wheelSize) {
|
||||
Composites.car = function(x, y, width, height, wheelSize) {
|
||||
var group = Body.nextGroup(true),
|
||||
wheelBase = 20,
|
||||
wheelAOffset = -width * 0.5 + wheelBase,
|
||||
wheelBOffset = width * 0.5 - wheelBase,
|
||||
wheelYOffset = 0;
|
||||
|
||||
|
||||
var car = Composite.create({ label: 'Car' }),
|
||||
body = Bodies.rectangle(xx, yy, width, height, {
|
||||
body = Bodies.rectangle(x, y, width, height, {
|
||||
collisionFilter: {
|
||||
group: group
|
||||
},
|
||||
|
@ -259,21 +256,21 @@ var deprecated = Common.deprecated;
|
|||
},
|
||||
density: 0.0002
|
||||
});
|
||||
|
||||
var wheelA = Bodies.circle(xx + wheelAOffset, yy + wheelYOffset, wheelSize, {
|
||||
|
||||
var wheelA = Bodies.circle(x + wheelAOffset, y + wheelYOffset, wheelSize, {
|
||||
collisionFilter: {
|
||||
group: group
|
||||
},
|
||||
friction: 0.8
|
||||
});
|
||||
|
||||
var wheelB = Bodies.circle(xx + wheelBOffset, yy + wheelYOffset, wheelSize, {
|
||||
|
||||
var wheelB = Bodies.circle(x + wheelBOffset, y + wheelYOffset, wheelSize, {
|
||||
collisionFilter: {
|
||||
group: group
|
||||
},
|
||||
friction: 0.8
|
||||
});
|
||||
|
||||
|
||||
var axelA = Constraint.create({
|
||||
bodyB: body,
|
||||
pointB: { x: wheelAOffset, y: wheelYOffset },
|
||||
|
@ -281,7 +278,7 @@ var deprecated = Common.deprecated;
|
|||
stiffness: 1,
|
||||
length: 0
|
||||
});
|
||||
|
||||
|
||||
var axelB = Constraint.create({
|
||||
bodyB: body,
|
||||
pointB: { x: wheelBOffset, y: wheelYOffset },
|
||||
|
@ -289,7 +286,7 @@ var deprecated = Common.deprecated;
|
|||
stiffness: 1,
|
||||
length: 0
|
||||
});
|
||||
|
||||
|
||||
Composite.addBody(car, body);
|
||||
Composite.addBody(car, wheelA);
|
||||
Composite.addBody(car, wheelB);
|
||||
|
@ -299,15 +296,13 @@ var deprecated = Common.deprecated;
|
|||
return car;
|
||||
};
|
||||
|
||||
deprecated(Composites, 'car', 'Composites.car ➤ moved to car example');
|
||||
|
||||
/**
|
||||
* This has now moved to the [softBody example](https://github.com/liabru/matter-js/blob/master/examples/softBody.js)
|
||||
* and the [cloth example](https://github.com/liabru/matter-js/blob/master/examples/cloth.js), follow those instead as this function is deprecated here.
|
||||
* @deprecated moved to softBody and cloth examples
|
||||
* @method softBody
|
||||
* @param {number} xx
|
||||
* @param {number} yy
|
||||
* @param {number} x Starting position in X.
|
||||
* @param {number} y Starting position in Y.
|
||||
* @param {number} columns
|
||||
* @param {number} rows
|
||||
* @param {number} columnGap
|
||||
|
@ -318,12 +313,12 @@ var deprecated = Common.deprecated;
|
|||
* @param {} constraintOptions
|
||||
* @return {composite} A new composite softBody
|
||||
*/
|
||||
Composites.softBody = function(xx, yy, columns, rows, columnGap, rowGap, crossBrace, particleRadius, particleOptions, constraintOptions) {
|
||||
Composites.softBody = function(x, y, columns, rows, columnGap, rowGap, crossBrace, particleRadius, particleOptions, constraintOptions) {
|
||||
particleOptions = Common.extend({ inertia: Infinity }, particleOptions);
|
||||
constraintOptions = Common.extend({ stiffness: 0.2, render: { type: 'line', anchors: false } }, constraintOptions);
|
||||
|
||||
var softBody = Composites.stack(xx, yy, columns, rows, columnGap, rowGap, function(x, y) {
|
||||
return Bodies.circle(x, y, particleRadius, particleOptions);
|
||||
var softBody = Composites.stack(x, y, columns, rows, columnGap, rowGap, function(stackX, stackY) {
|
||||
return Bodies.circle(stackX, stackY, particleRadius, particleOptions);
|
||||
});
|
||||
|
||||
Composites.mesh(softBody, columns, rows, crossBrace, constraintOptions);
|
||||
|
@ -333,5 +328,4 @@ var deprecated = Common.deprecated;
|
|||
return softBody;
|
||||
};
|
||||
|
||||
deprecated(Composites, 'softBody', 'Composites.softBody ➤ moved to softBody and cloth examples');
|
||||
})();
|
||||
|
|
|
@ -9,7 +9,7 @@ var MatterAttractors =
|
|||
{
|
||||
name: 'matter-attractors',
|
||||
version: '0.1.7',
|
||||
for: 'matter-js@^0.18.0',
|
||||
for: 'matter-js@^0.19.0',
|
||||
silent: true,
|
||||
|
||||
// installs the plugin where `base` is `Matter`
|
||||
|
|
|
@ -8,7 +8,7 @@ var MatterCollisionEvents = {
|
|||
|
||||
name: 'matter-collision-events',
|
||||
version: '0.1.6',
|
||||
for: 'matter-js@^0.18.0',
|
||||
for: 'matter-js@^0.19.0',
|
||||
silent: true,
|
||||
|
||||
install: function (matter)
|
||||
|
|
|
@ -9,7 +9,7 @@ var MatterWrap = {
|
|||
// plugin meta
|
||||
name: 'matter-wrap', // PLUGIN_NAME
|
||||
version: '0.1.4', // PLUGIN_VERSION
|
||||
for: 'matter-js@^0.18.0',
|
||||
for: 'matter-js@^0.19.0',
|
||||
silent: true, // no console log please
|
||||
|
||||
// installs the plugin where `base` is `Matter`
|
||||
|
|
|
@ -10,11 +10,11 @@ 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');
|
||||
var Events = require('../core/Events');
|
||||
var Grid = require('../collision/Grid');
|
||||
var Vector = require('../geometry/Vector');
|
||||
// var Mouse = require('../core/Mouse');
|
||||
|
||||
|
@ -32,6 +32,9 @@ var Vector = require('../geometry/Vector');
|
|||
|| window.webkitCancelAnimationFrame || window.msCancelAnimationFrame;
|
||||
}
|
||||
|
||||
Render._goodFps = 30;
|
||||
Render._goodDelta = 1000 / 60;
|
||||
|
||||
/**
|
||||
* Creates a new renderer. The options parameter is an object that specifies any properties you wish to override the defaults.
|
||||
* All properties have default values, and many are pre-calculated automatically based on other properties.
|
||||
|
@ -42,24 +45,38 @@ var Vector = require('../geometry/Vector');
|
|||
*/
|
||||
Render.create = function(options) {
|
||||
var defaults = {
|
||||
controller: Render,
|
||||
engine: null,
|
||||
element: null,
|
||||
canvas: null,
|
||||
mouse: null,
|
||||
frameRequestId: null,
|
||||
timing: {
|
||||
historySize: 60,
|
||||
delta: 0,
|
||||
deltaHistory: [],
|
||||
lastTime: 0,
|
||||
lastTimestamp: 0,
|
||||
lastElapsed: 0,
|
||||
timestampElapsed: 0,
|
||||
timestampElapsedHistory: [],
|
||||
engineDeltaHistory: [],
|
||||
engineElapsedHistory: [],
|
||||
elapsedHistory: []
|
||||
},
|
||||
options: {
|
||||
width: 800,
|
||||
height: 600,
|
||||
pixelRatio: 1,
|
||||
background: '#18181d',
|
||||
wireframeBackground: '#0f0f13',
|
||||
background: '#14151f',
|
||||
wireframeBackground: '#14151f',
|
||||
wireframeStrokeStyle: '#bbb',
|
||||
hasBounds: !!options.bounds,
|
||||
enabled: true,
|
||||
wireframes: true,
|
||||
showSleeping: true,
|
||||
showDebug: false,
|
||||
showBroadphase: false,
|
||||
showStats: false,
|
||||
showPerformance: false,
|
||||
showBounds: false,
|
||||
showVelocity: false,
|
||||
showCollisions: false,
|
||||
|
@ -68,7 +85,6 @@ var Vector = require('../geometry/Vector');
|
|||
showPositions: false,
|
||||
showAngleIndicator: false,
|
||||
showIds: false,
|
||||
showShadows: false,
|
||||
showVertexNumbers: false,
|
||||
showConvexHulls: false,
|
||||
showInternalEdges: false,
|
||||
|
@ -86,7 +102,7 @@ var Vector = require('../geometry/Vector');
|
|||
render.mouse = options.mouse;
|
||||
render.engine = options.engine;
|
||||
render.canvas = render.canvas || _createCanvas(render.options.width, render.options.height);
|
||||
render.context = render.canvas.getContext('2d', { willReadFrequently: true });
|
||||
render.context = render.canvas.getContext('2d');
|
||||
render.textures = {};
|
||||
|
||||
render.bounds = render.bounds || {
|
||||
|
@ -100,14 +116,16 @@ var Vector = require('../geometry/Vector');
|
|||
}
|
||||
};
|
||||
|
||||
// for temporary back compatibility only
|
||||
render.controller = Render;
|
||||
render.options.showBroadphase = false;
|
||||
|
||||
if (render.options.pixelRatio !== 1) {
|
||||
Render.setPixelRatio(render, render.options.pixelRatio);
|
||||
}
|
||||
|
||||
if (Common.isElement(render.element)) {
|
||||
render.element.appendChild(render.canvas);
|
||||
} else {
|
||||
Common.log('Render.create: options.element was undefined, render.canvas was created but not appended', 'warn');
|
||||
}
|
||||
|
||||
return render;
|
||||
|
@ -121,7 +139,18 @@ var Vector = require('../geometry/Vector');
|
|||
Render.run = function(render) {
|
||||
(function loop(time){
|
||||
render.frameRequestId = _requestAnimationFrame(loop);
|
||||
Render.world(render);
|
||||
|
||||
_updateTiming(render, time);
|
||||
|
||||
Render.world(render, time);
|
||||
|
||||
if (render.options.showStats || render.options.showDebug) {
|
||||
Render.stats(render, render.context, time);
|
||||
}
|
||||
|
||||
if (render.options.showPerformance || render.options.showDebug) {
|
||||
Render.performance(render, render.context, time);
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
|
@ -155,7 +184,36 @@ var Vector = require('../geometry/Vector');
|
|||
canvas.height = options.height * pixelRatio;
|
||||
canvas.style.width = options.width + 'px';
|
||||
canvas.style.height = options.height + 'px';
|
||||
render.context.scale(pixelRatio, pixelRatio);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the render `width` and `height`.
|
||||
*
|
||||
* Updates the canvas accounting for `render.options.pixelRatio`.
|
||||
*
|
||||
* Updates the bottom right render bound `render.bounds.max` relative to the provided `width` and `height`.
|
||||
* The top left render bound `render.bounds.min` isn't changed.
|
||||
*
|
||||
* Follow this call with `Render.lookAt` if you need to change the render bounds.
|
||||
*
|
||||
* See also `Render.setPixelRatio`.
|
||||
* @method setSize
|
||||
* @param {render} render
|
||||
* @param {number} width The width (in CSS pixels)
|
||||
* @param {number} height The height (in CSS pixels)
|
||||
*/
|
||||
Render.setSize = function(render, width, height) {
|
||||
render.options.width = width;
|
||||
render.options.height = height;
|
||||
render.bounds.max.x = render.bounds.min.x + width;
|
||||
render.bounds.max.y = render.bounds.min.y + height;
|
||||
|
||||
if (render.options.pixelRatio !== 1) {
|
||||
Render.setPixelRatio(render, render.options.pixelRatio);
|
||||
} else {
|
||||
render.canvas.width = width;
|
||||
render.canvas.height = height;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -267,7 +325,11 @@ var Vector = require('../geometry/Vector');
|
|||
boundsScaleX = boundsWidth / render.options.width,
|
||||
boundsScaleY = boundsHeight / render.options.height;
|
||||
|
||||
render.context.scale(1 / boundsScaleX, 1 / boundsScaleY);
|
||||
render.context.setTransform(
|
||||
render.options.pixelRatio / boundsScaleX, 0, 0,
|
||||
render.options.pixelRatio / boundsScaleY, 0, 0
|
||||
);
|
||||
|
||||
render.context.translate(-render.bounds.min.x, -render.bounds.min.y);
|
||||
};
|
||||
|
||||
|
@ -286,13 +348,16 @@ var Vector = require('../geometry/Vector');
|
|||
* @method world
|
||||
* @param {render} render
|
||||
*/
|
||||
Render.world = function(render) {
|
||||
var engine = render.engine,
|
||||
Render.world = function(render, time) {
|
||||
var startTime = Common.now(),
|
||||
engine = render.engine,
|
||||
world = engine.world,
|
||||
canvas = render.canvas,
|
||||
context = render.context,
|
||||
options = render.options,
|
||||
allBodies = Composite.allBodies(world),
|
||||
timing = render.timing;
|
||||
|
||||
var allBodies = Composite.allBodies(world),
|
||||
allConstraints = Composite.allConstraints(world),
|
||||
background = options.wireframes ? options.wireframeBackground : options.background,
|
||||
bodies = [],
|
||||
|
@ -347,16 +412,20 @@ var Vector = require('../geometry/Vector');
|
|||
|
||||
// update mouse
|
||||
if (render.mouse) {
|
||||
// Mouse.setScale(render.mouse, {
|
||||
// x: (render.bounds.max.x - render.bounds.min.x) / render.canvas.width,
|
||||
// y: (render.bounds.max.y - render.bounds.min.y) / render.canvas.height
|
||||
// });
|
||||
Mouse.setScale(render.mouse, {
|
||||
x: (render.bounds.max.x - render.bounds.min.x) / render.options.width,
|
||||
y: (render.bounds.max.y - render.bounds.min.y) / render.options.height
|
||||
});
|
||||
|
||||
// Mouse.setOffset(render.mouse, render.bounds.min);
|
||||
Mouse.setOffset(render.mouse, render.bounds.min);
|
||||
}
|
||||
} else {
|
||||
constraints = allConstraints;
|
||||
bodies = allBodies;
|
||||
|
||||
if (render.options.pixelRatio !== 1) {
|
||||
render.context.setTransform(render.options.pixelRatio, 0, 0, render.options.pixelRatio, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.wireframes || (engine.enableSleeping && options.showSleeping)) {
|
||||
|
@ -394,89 +463,193 @@ var Vector = require('../geometry/Vector');
|
|||
if (options.showVertexNumbers)
|
||||
Render.vertexNumbers(render, bodies, context);
|
||||
|
||||
// if (options.showMousePosition)
|
||||
// Render.mousePosition(render, render.mouse, context);
|
||||
if (options.showMousePosition)
|
||||
Render.mousePosition(render, render.mouse, context);
|
||||
|
||||
Render.constraints(constraints, context);
|
||||
|
||||
if (options.showBroadphase && engine.broadphase.controller === Grid)
|
||||
Render.grid(render, engine.broadphase, context);
|
||||
|
||||
if (options.showDebug)
|
||||
Render.debug(render, context);
|
||||
|
||||
if (options.hasBounds) {
|
||||
// revert view transforms
|
||||
Render.endViewTransform(render);
|
||||
}
|
||||
|
||||
Events.trigger(render, 'afterRender', event);
|
||||
|
||||
// log the time elapsed computing this update
|
||||
timing.lastElapsed = Common.now() - startTime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Description
|
||||
* Renders statistics about the engine and world useful for debugging.
|
||||
* @private
|
||||
* @method debug
|
||||
* @method stats
|
||||
* @param {render} render
|
||||
* @param {RenderingContext} context
|
||||
* @param {Number} time
|
||||
*/
|
||||
Render.stats = function(render, context, time) {
|
||||
var engine = render.engine,
|
||||
world = engine.world,
|
||||
bodies = Composite.allBodies(world),
|
||||
parts = 0,
|
||||
width = 55,
|
||||
height = 44,
|
||||
x = 0,
|
||||
y = 0;
|
||||
|
||||
// count parts
|
||||
for (var i = 0; i < bodies.length; i += 1) {
|
||||
parts += bodies[i].parts.length;
|
||||
}
|
||||
|
||||
// sections
|
||||
var sections = {
|
||||
'Part': parts,
|
||||
'Body': bodies.length,
|
||||
'Cons': Composite.allConstraints(world).length,
|
||||
'Comp': Composite.allComposites(world).length,
|
||||
'Pair': engine.pairs.list.length
|
||||
};
|
||||
|
||||
// background
|
||||
context.fillStyle = '#0e0f19';
|
||||
context.fillRect(x, y, width * 5.5, height);
|
||||
|
||||
context.font = '12px Arial';
|
||||
context.textBaseline = 'top';
|
||||
context.textAlign = 'right';
|
||||
|
||||
// sections
|
||||
for (var key in sections) {
|
||||
var section = sections[key];
|
||||
// label
|
||||
context.fillStyle = '#aaa';
|
||||
context.fillText(key, x + width, y + 8);
|
||||
|
||||
// value
|
||||
context.fillStyle = '#eee';
|
||||
context.fillText(section, x + width, y + 26);
|
||||
|
||||
x += width;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders engine and render performance information.
|
||||
* @private
|
||||
* @method performance
|
||||
* @param {render} render
|
||||
* @param {RenderingContext} context
|
||||
*/
|
||||
Render.debug = function(render, context) {
|
||||
var c = context,
|
||||
engine = render.engine,
|
||||
world = engine.world,
|
||||
metrics = engine.metrics,
|
||||
options = render.options,
|
||||
bodies = Composite.allBodies(world),
|
||||
space = " ";
|
||||
Render.performance = function(render, context) {
|
||||
var engine = render.engine,
|
||||
timing = render.timing,
|
||||
deltaHistory = timing.deltaHistory,
|
||||
elapsedHistory = timing.elapsedHistory,
|
||||
timestampElapsedHistory = timing.timestampElapsedHistory,
|
||||
engineDeltaHistory = timing.engineDeltaHistory,
|
||||
engineElapsedHistory = timing.engineElapsedHistory,
|
||||
lastEngineDelta = engine.timing.lastDelta;
|
||||
|
||||
var deltaMean = _mean(deltaHistory),
|
||||
elapsedMean = _mean(elapsedHistory),
|
||||
engineDeltaMean = _mean(engineDeltaHistory),
|
||||
engineElapsedMean = _mean(engineElapsedHistory),
|
||||
timestampElapsedMean = _mean(timestampElapsedHistory),
|
||||
rateMean = (timestampElapsedMean / deltaMean) || 0,
|
||||
fps = (1000 / deltaMean) || 0;
|
||||
|
||||
if (engine.timing.timestamp - (render.debugTimestamp || 0) >= 500) {
|
||||
var text = "";
|
||||
var graphHeight = 4,
|
||||
gap = 12,
|
||||
width = 60,
|
||||
height = 34,
|
||||
x = 10,
|
||||
y = 69;
|
||||
|
||||
if (metrics.timing) {
|
||||
text += "fps: " + Math.round(metrics.timing.fps) + space;
|
||||
}
|
||||
// background
|
||||
context.fillStyle = '#0e0f19';
|
||||
context.fillRect(0, 50, gap * 4 + width * 5 + 22, height);
|
||||
|
||||
// @if DEBUG
|
||||
if (metrics.extended) {
|
||||
if (metrics.timing) {
|
||||
text += "delta: " + metrics.timing.delta.toFixed(3) + space;
|
||||
text += "correction: " + metrics.timing.correction.toFixed(3) + space;
|
||||
}
|
||||
// show FPS
|
||||
Render.status(
|
||||
context, x, y, width, graphHeight, deltaHistory.length,
|
||||
Math.round(fps) + ' fps',
|
||||
fps / Render._goodFps,
|
||||
function(i) { return (deltaHistory[i] / deltaMean) - 1; }
|
||||
);
|
||||
|
||||
text += "bodies: " + bodies.length + space;
|
||||
// show engine delta
|
||||
Render.status(
|
||||
context, x + gap + width, y, width, graphHeight, engineDeltaHistory.length,
|
||||
lastEngineDelta.toFixed(2) + ' dt',
|
||||
Render._goodDelta / lastEngineDelta,
|
||||
function(i) { return (engineDeltaHistory[i] / engineDeltaMean) - 1; }
|
||||
);
|
||||
|
||||
if (engine.broadphase.controller === Grid)
|
||||
text += "buckets: " + metrics.buckets + space;
|
||||
// show engine update time
|
||||
Render.status(
|
||||
context, x + (gap + width) * 2, y, width, graphHeight, engineElapsedHistory.length,
|
||||
engineElapsedMean.toFixed(2) + ' ut',
|
||||
1 - (engineElapsedMean / Render._goodFps),
|
||||
function(i) { return (engineElapsedHistory[i] / engineElapsedMean) - 1; }
|
||||
);
|
||||
|
||||
text += "\n";
|
||||
// show render time
|
||||
Render.status(
|
||||
context, x + (gap + width) * 3, y, width, graphHeight, elapsedHistory.length,
|
||||
elapsedMean.toFixed(2) + ' rt',
|
||||
1 - (elapsedMean / Render._goodFps),
|
||||
function(i) { return (elapsedHistory[i] / elapsedMean) - 1; }
|
||||
);
|
||||
|
||||
text += "collisions: " + metrics.collisions + space;
|
||||
text += "pairs: " + engine.pairs.list.length + space;
|
||||
text += "broad: " + metrics.broadEff + space;
|
||||
text += "mid: " + metrics.midEff + space;
|
||||
text += "narrow: " + metrics.narrowEff + space;
|
||||
}
|
||||
// @endif
|
||||
// show effective speed
|
||||
Render.status(
|
||||
context, x + (gap + width) * 4, y, width, graphHeight, timestampElapsedHistory.length,
|
||||
rateMean.toFixed(2) + ' x',
|
||||
rateMean * rateMean * rateMean,
|
||||
function(i) { return (((timestampElapsedHistory[i] / deltaHistory[i]) / rateMean) || 0) - 1; }
|
||||
);
|
||||
};
|
||||
|
||||
render.debugString = text;
|
||||
render.debugTimestamp = engine.timing.timestamp;
|
||||
/**
|
||||
* Renders a label, indicator and a chart.
|
||||
* @private
|
||||
* @method status
|
||||
* @param {RenderingContext} context
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {number} width
|
||||
* @param {number} height
|
||||
* @param {number} count
|
||||
* @param {string} label
|
||||
* @param {string} indicator
|
||||
* @param {function} plotY
|
||||
*/
|
||||
Render.status = function(context, x, y, width, height, count, label, indicator, plotY) {
|
||||
// background
|
||||
context.strokeStyle = '#888';
|
||||
context.fillStyle = '#444';
|
||||
context.lineWidth = 1;
|
||||
context.fillRect(x, y + 7, width, 1);
|
||||
|
||||
// chart
|
||||
context.beginPath();
|
||||
context.moveTo(x, y + 7 - height * Common.clamp(0.4 * plotY(0), -2, 2));
|
||||
for (var i = 0; i < width; i += 1) {
|
||||
context.lineTo(x + i, y + 7 - (i < count ? height * Common.clamp(0.4 * plotY(i), -2, 2) : 0));
|
||||
}
|
||||
context.stroke();
|
||||
|
||||
if (render.debugString) {
|
||||
c.font = "12px Arial";
|
||||
// indicator
|
||||
context.fillStyle = 'hsl(' + Common.clamp(25 + 95 * indicator, 0, 120) + ',100%,60%)';
|
||||
context.fillRect(x, y - 7, 4, 4);
|
||||
|
||||
if (options.wireframes) {
|
||||
c.fillStyle = 'rgba(255,255,255,0.5)';
|
||||
} else {
|
||||
c.fillStyle = 'rgba(0,0,0,0.5)';
|
||||
}
|
||||
|
||||
var split = render.debugString.split('\n');
|
||||
|
||||
for (var i = 0; i < split.length; i++) {
|
||||
c.fillText(split[i], 50, 50 + i * 18);
|
||||
}
|
||||
}
|
||||
// label
|
||||
context.font = '12px Arial';
|
||||
context.textBaseline = 'middle';
|
||||
context.textAlign = 'right';
|
||||
context.fillStyle = '#eee';
|
||||
context.fillText(label, x + width, y - 5);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -556,55 +729,6 @@ var Vector = require('../geometry/Vector');
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @private
|
||||
* @method bodyShadows
|
||||
* @param {render} render
|
||||
* @param {body[]} bodies
|
||||
* @param {RenderingContext} context
|
||||
*/
|
||||
Render.bodyShadows = function(render, bodies, context) {
|
||||
var c = context,
|
||||
engine = render.engine;
|
||||
|
||||
for (var i = 0; i < bodies.length; i++) {
|
||||
var body = bodies[i];
|
||||
|
||||
if (!body.render.visible)
|
||||
continue;
|
||||
|
||||
if (body.circleRadius) {
|
||||
c.beginPath();
|
||||
c.arc(body.position.x, body.position.y, body.circleRadius, 0, 2 * Math.PI);
|
||||
c.closePath();
|
||||
} else {
|
||||
c.beginPath();
|
||||
c.moveTo(body.vertices[0].x, body.vertices[0].y);
|
||||
for (var j = 1; j < body.vertices.length; j++) {
|
||||
c.lineTo(body.vertices[j].x, body.vertices[j].y);
|
||||
}
|
||||
c.closePath();
|
||||
}
|
||||
|
||||
var distanceX = body.position.x - render.options.width * 0.5,
|
||||
distanceY = body.position.y - render.options.height * 0.2,
|
||||
distance = Math.abs(distanceX) + Math.abs(distanceY);
|
||||
|
||||
c.shadowColor = 'rgba(0,0,0,0.15)';
|
||||
c.shadowOffsetX = 0.05 * distanceX;
|
||||
c.shadowOffsetY = 0.05 * distanceY;
|
||||
c.shadowBlur = 1 + 12 * Math.min(1, distance / 1000);
|
||||
|
||||
c.fill();
|
||||
|
||||
c.shadowColor = null;
|
||||
c.shadowOffsetX = null;
|
||||
c.shadowOffsetY = null;
|
||||
c.shadowBlur = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @private
|
||||
|
@ -698,7 +822,7 @@ var Vector = require('../geometry/Vector');
|
|||
c.fill();
|
||||
} else {
|
||||
c.lineWidth = 1;
|
||||
c.strokeStyle = '#bbb';
|
||||
c.strokeStyle = render.options.wireframeStrokeStyle;
|
||||
c.stroke();
|
||||
}
|
||||
}
|
||||
|
@ -757,7 +881,7 @@ var Vector = require('../geometry/Vector');
|
|||
}
|
||||
|
||||
c.lineWidth = 1;
|
||||
c.strokeStyle = '#bbb';
|
||||
c.strokeStyle = render.options.wireframeStrokeStyle;
|
||||
c.stroke();
|
||||
};
|
||||
|
||||
|
@ -920,7 +1044,7 @@ var Vector = require('../geometry/Vector');
|
|||
// render a single axis indicator
|
||||
c.moveTo(part.position.x, part.position.y);
|
||||
c.lineTo((part.vertices[0].x + part.vertices[part.vertices.length-1].x) / 2,
|
||||
(part.vertices[0].y + part.vertices[part.vertices.length-1].y) / 2);
|
||||
(part.vertices[0].y + part.vertices[part.vertices.length-1].y) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1014,8 +1138,10 @@ var Vector = require('../geometry/Vector');
|
|||
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;
|
||||
|
@ -1190,45 +1316,6 @@ var Vector = require('../geometry/Vector');
|
|||
c.stroke();
|
||||
};
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @private
|
||||
* @method grid
|
||||
* @param {render} render
|
||||
* @param {grid} grid
|
||||
* @param {RenderingContext} context
|
||||
*/
|
||||
Render.grid = function(render, grid, context) {
|
||||
var c = context,
|
||||
options = render.options;
|
||||
|
||||
if (options.wireframes) {
|
||||
c.strokeStyle = 'rgba(255,180,0,0.1)';
|
||||
} else {
|
||||
c.strokeStyle = 'rgba(255,180,0,0.5)';
|
||||
}
|
||||
|
||||
c.beginPath();
|
||||
|
||||
var bucketKeys = Common.keys(grid.buckets);
|
||||
|
||||
for (var i = 0; i < bucketKeys.length; i++) {
|
||||
var bucketId = bucketKeys[i];
|
||||
|
||||
if (grid.buckets[bucketId].length < 2)
|
||||
continue;
|
||||
|
||||
var region = bucketId.split(/C|R/);
|
||||
c.rect(0.5 + parseInt(region[1], 10) * grid.bucketWidth,
|
||||
0.5 + parseInt(region[2], 10) * grid.bucketHeight,
|
||||
grid.bucketWidth,
|
||||
grid.bucketHeight);
|
||||
}
|
||||
|
||||
c.lineWidth = 1;
|
||||
c.stroke();
|
||||
};
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @private
|
||||
|
@ -1269,7 +1356,7 @@ var Vector = require('../geometry/Vector');
|
|||
bounds = item.bounds;
|
||||
context.beginPath();
|
||||
context.rect(Math.floor(bounds.min.x - 3), Math.floor(bounds.min.y - 3),
|
||||
Math.floor(bounds.max.x - bounds.min.x + 6), Math.floor(bounds.max.y - bounds.min.y + 6));
|
||||
Math.floor(bounds.max.x - bounds.min.x + 6), Math.floor(bounds.max.y - bounds.min.y + 6));
|
||||
context.closePath();
|
||||
context.stroke();
|
||||
|
||||
|
@ -1303,7 +1390,7 @@ var Vector = require('../geometry/Vector');
|
|||
bounds = inspector.selectBounds;
|
||||
context.beginPath();
|
||||
context.rect(Math.floor(bounds.min.x), Math.floor(bounds.min.y),
|
||||
Math.floor(bounds.max.x - bounds.min.x), Math.floor(bounds.max.y - bounds.min.y));
|
||||
Math.floor(bounds.max.x - bounds.min.x), Math.floor(bounds.max.y - bounds.min.y));
|
||||
context.closePath();
|
||||
context.stroke();
|
||||
context.fill();
|
||||
|
@ -1315,7 +1402,56 @@ var Vector = require('../geometry/Vector');
|
|||
};
|
||||
|
||||
/**
|
||||
* Description
|
||||
* Updates render timing.
|
||||
* @method _updateTiming
|
||||
* @private
|
||||
* @param {render} render
|
||||
* @param {number} time
|
||||
*/
|
||||
var _updateTiming = function(render, time) {
|
||||
var engine = render.engine,
|
||||
timing = render.timing,
|
||||
historySize = timing.historySize,
|
||||
timestamp = engine.timing.timestamp;
|
||||
|
||||
timing.delta = time - timing.lastTime || Render._goodDelta;
|
||||
timing.lastTime = time;
|
||||
|
||||
timing.timestampElapsed = timestamp - timing.lastTimestamp || 0;
|
||||
timing.lastTimestamp = timestamp;
|
||||
|
||||
timing.deltaHistory.unshift(timing.delta);
|
||||
timing.deltaHistory.length = Math.min(timing.deltaHistory.length, historySize);
|
||||
|
||||
timing.engineDeltaHistory.unshift(engine.timing.lastDelta);
|
||||
timing.engineDeltaHistory.length = Math.min(timing.engineDeltaHistory.length, historySize);
|
||||
|
||||
timing.timestampElapsedHistory.unshift(timing.timestampElapsed);
|
||||
timing.timestampElapsedHistory.length = Math.min(timing.timestampElapsedHistory.length, historySize);
|
||||
|
||||
timing.engineElapsedHistory.unshift(engine.timing.lastElapsed);
|
||||
timing.engineElapsedHistory.length = Math.min(timing.engineElapsedHistory.length, historySize);
|
||||
|
||||
timing.elapsedHistory.unshift(timing.lastElapsed);
|
||||
timing.elapsedHistory.length = Math.min(timing.elapsedHistory.length, historySize);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the mean value of the given numbers.
|
||||
* @method _mean
|
||||
* @private
|
||||
* @param {Number[]} values
|
||||
* @return {Number} the mean of given values
|
||||
*/
|
||||
var _mean = function(values) {
|
||||
var result = 0;
|
||||
for (var i = 0; i < values.length; i += 1) {
|
||||
result += values[i];
|
||||
}
|
||||
return (result / values.length) || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @method _createCanvas
|
||||
* @private
|
||||
* @param {} width
|
||||
|
@ -1339,7 +1475,7 @@ var Vector = require('../geometry/Vector');
|
|||
* @return {Number} pixel ratio
|
||||
*/
|
||||
var _getPixelRatio = function(canvas) {
|
||||
var context = canvas.getContext('2d', { willReadFrequently: true }),
|
||||
var context = canvas.getContext('2d'),
|
||||
devicePixelRatio = window.devicePixelRatio || 1,
|
||||
backingStorePixelRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio
|
||||
|| context.msBackingStorePixelRatio || context.oBackingStorePixelRatio
|
||||
|
@ -1421,6 +1557,7 @@ var Vector = require('../geometry/Vector');
|
|||
/**
|
||||
* A back-reference to the `Matter.Render` module.
|
||||
*
|
||||
* @deprecated
|
||||
* @property controller
|
||||
* @type render
|
||||
*/
|
||||
|
@ -1448,37 +1585,6 @@ var Vector = require('../geometry/Vector');
|
|||
* @default null
|
||||
*/
|
||||
|
||||
/**
|
||||
* The configuration options of the renderer.
|
||||
*
|
||||
* @property options
|
||||
* @type {}
|
||||
*/
|
||||
|
||||
/**
|
||||
* The target width in pixels of the `render.canvas` to be created.
|
||||
*
|
||||
* @property options.width
|
||||
* @type number
|
||||
* @default 800
|
||||
*/
|
||||
|
||||
/**
|
||||
* The target height in pixels of the `render.canvas` to be created.
|
||||
*
|
||||
* @property options.height
|
||||
* @type number
|
||||
* @default 600
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag that specifies if `render.bounds` should be used when rendering.
|
||||
*
|
||||
* @property options.hasBounds
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A `Bounds` object that specifies the drawing view region.
|
||||
* Rendering will be automatically transformed and scaled to fit within the canvas size (`render.options.width` and `render.options.height`).
|
||||
|
@ -1503,4 +1609,264 @@ var Vector = require('../geometry/Vector');
|
|||
* @type {}
|
||||
*/
|
||||
|
||||
/**
|
||||
* The mouse to render if `render.options.showMousePosition` is enabled.
|
||||
*
|
||||
* @property mouse
|
||||
* @type mouse
|
||||
* @default null
|
||||
*/
|
||||
|
||||
/**
|
||||
* The configuration options of the renderer.
|
||||
*
|
||||
* @property options
|
||||
* @type {}
|
||||
*/
|
||||
|
||||
/**
|
||||
* The target width in pixels of the `render.canvas` to be created.
|
||||
* See also the `options.pixelRatio` property to change render quality.
|
||||
*
|
||||
* @property options.width
|
||||
* @type number
|
||||
* @default 800
|
||||
*/
|
||||
|
||||
/**
|
||||
* The target height in pixels of the `render.canvas` to be created.
|
||||
* See also the `options.pixelRatio` property to change render quality.
|
||||
*
|
||||
* @property options.height
|
||||
* @type number
|
||||
* @default 600
|
||||
*/
|
||||
|
||||
/**
|
||||
* The [pixel ratio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) to use when rendering.
|
||||
*
|
||||
* @property options.pixelRatio
|
||||
* @type number
|
||||
* @default 1
|
||||
*/
|
||||
|
||||
/**
|
||||
* A CSS background color string to use when `render.options.wireframes` is disabled.
|
||||
* This may be also set to `'transparent'` or equivalent.
|
||||
*
|
||||
* @property options.background
|
||||
* @type string
|
||||
* @default '#14151f'
|
||||
*/
|
||||
|
||||
/**
|
||||
* A CSS color string to use for background when `render.options.wireframes` is enabled.
|
||||
* This may be also set to `'transparent'` or equivalent.
|
||||
*
|
||||
* @property options.wireframeBackground
|
||||
* @type string
|
||||
* @default '#14151f'
|
||||
*/
|
||||
|
||||
/**
|
||||
* A CSS color string to use for stroke when `render.options.wireframes` is enabled.
|
||||
* This may be also set to `'transparent'` or equivalent.
|
||||
*
|
||||
* @property options.wireframeStrokeStyle
|
||||
* @type string
|
||||
* @default '#bbb'
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag that specifies if `render.bounds` should be used when rendering.
|
||||
*
|
||||
* @property options.hasBounds
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable all debug information overlays together.
|
||||
* This includes and has priority over the values of:
|
||||
*
|
||||
* - `render.options.showStats`
|
||||
* - `render.options.showPerformance`
|
||||
*
|
||||
* @property options.showDebug
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the engine stats info overlay.
|
||||
* From left to right, the values shown are:
|
||||
*
|
||||
* - body parts total
|
||||
* - body total
|
||||
* - constraints total
|
||||
* - composites total
|
||||
* - collision pairs total
|
||||
*
|
||||
* @property options.showStats
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable performance charts.
|
||||
* From left to right, the values shown are:
|
||||
*
|
||||
* - average render frequency (e.g. 60 fps)
|
||||
* - exact engine delta time used for last update (e.g. 16.66ms)
|
||||
* - average engine execution duration (e.g. 5.00ms)
|
||||
* - average render execution duration (e.g. 0.40ms)
|
||||
* - average effective play speed (e.g. '1.00x' is 'real-time')
|
||||
*
|
||||
* Each value is recorded over a fixed sample of past frames (60 frames).
|
||||
*
|
||||
* A chart shown below each value indicates the variance from the average over the sample.
|
||||
* The more stable or fixed the value is the flatter the chart will appear.
|
||||
*
|
||||
* @property options.showPerformance
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable rendering entirely.
|
||||
*
|
||||
* @property options.enabled
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to toggle wireframe rendering otherwise solid fill rendering is used.
|
||||
*
|
||||
* @property options.wireframes
|
||||
* @type boolean
|
||||
* @default true
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable sleeping bodies indicators.
|
||||
*
|
||||
* @property options.showSleeping
|
||||
* @type boolean
|
||||
* @default true
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the debug information overlay.
|
||||
*
|
||||
* @property options.showDebug
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the collision broadphase debug overlay.
|
||||
*
|
||||
* @deprecated no longer implemented
|
||||
* @property options.showBroadphase
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the body bounds debug overlay.
|
||||
*
|
||||
* @property options.showBounds
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the body velocity debug overlay.
|
||||
*
|
||||
* @property options.showVelocity
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the body collisions debug overlay.
|
||||
*
|
||||
* @property options.showCollisions
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the collision resolver separations debug overlay.
|
||||
*
|
||||
* @property options.showSeparations
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the body axes debug overlay.
|
||||
*
|
||||
* @property options.showAxes
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the body positions debug overlay.
|
||||
*
|
||||
* @property options.showPositions
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the body angle debug overlay.
|
||||
*
|
||||
* @property options.showAngleIndicator
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the body and part ids debug overlay.
|
||||
*
|
||||
* @property options.showIds
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the body vertex numbers debug overlay.
|
||||
*
|
||||
* @property options.showVertexNumbers
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the body convex hulls debug overlay.
|
||||
*
|
||||
* @property options.showConvexHulls
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the body internal edges debug overlay.
|
||||
*
|
||||
* @property options.showInternalEdges
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
/**
|
||||
* A flag to enable or disable the mouse position debug overlay.
|
||||
*
|
||||
* @property options.showMousePosition
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
|
||||
})();
|
||||
|
|
Loading…
Reference in a new issue