Upgrade to MatterJS v0.19

This commit is contained in:
Richard Davey 2023-04-03 22:01:39 +01:00
parent b9b9ca9e64
commit 24c85af875
15 changed files with 1031 additions and 491 deletions

View file

@ -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);
},
/**

View file

@ -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;
};

View file

@ -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;

View file

@ -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

View file

@ -10,6 +10,7 @@ module.exports = Common;
(function() {
Common._baseDelta = 1000 / 60;
Common._nextId = 0;
Common._seed = 0;
Common._nowStartTime = +(new Date());

View file

@ -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

View file

@ -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`.

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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) {

View file

@ -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');
})();

View file

@ -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`

View file

@ -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)

View file

@ -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`

View file

@ -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
*/
})();