/**
* The `Matter.Engine` module contains methods for creating and manipulating engines.
* An engine is a controller that manages updating the simulation of the world.
* See `Matter.Runner` for an optional game loop utility.
*
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples).
*
* @class Engine
*/
var Engine = {};
module.exports = Engine;
var Sleeping = require('./Sleeping');
var Resolver = require('../collision/Resolver');
var Detector = require('../collision/Detector');
var Pairs = require('../collision/Pairs');
var Events = require('./Events');
var Composite = require('../body/Composite');
var Constraint = require('../constraint/Constraint');
var Common = require('./Common');
var Body = require('../body/Body');
(function() {
Engine._deltaMax = 1000 / 60;
/**
* Creates a new engine. 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.
* See the properties section below for detailed information on what you can pass via the `options` object.
* @method create
* @param {object} [options]
* @return {engine} engine
*/
Engine.create = function(options) {
options = options || {};
var defaults = {
positionIterations: 6,
velocityIterations: 4,
constraintIterations: 2,
enableSleeping: false,
events: [],
plugin: {},
gravity: {
x: 0,
y: 1,
scale: 0.001
},
timing: {
timestamp: 0,
timeScale: 1,
lastDelta: 0,
lastElapsed: 0,
lastUpdatesPerFrame: 0
}
};
var engine = Common.extend(defaults, options);
engine.world = options.world || Composite.create({ label: 'World' });
engine.pairs = options.pairs || Pairs.create();
engine.detector = options.detector || Detector.create();
engine.detector.pairs = engine.pairs;
// for temporary back compatibility only
engine.grid = { buckets: [] };
engine.world.gravity = engine.gravity;
engine.broadphase = engine.grid;
engine.metrics = {};
return engine;
};
/**
* Moves the simulation forward in time by `delta` milliseconds.
* Triggers `beforeUpdate`, `beforeSolve` and `afterUpdate` events.
* Triggers `collisionStart`, `collisionActive` and `collisionEnd` events.
* @method update
* @param {engine} engine
* @param {number} [delta=16.666]
*/
Engine.update = function(engine, delta) {
var startTime = Common.now();
var world = engine.world,
detector = engine.detector,
pairs = engine.pairs,
timing = engine.timing,
timestamp = timing.timestamp,
i;
// warn if high delta
if (delta > Engine._deltaMax) {
Common.warnOnce(
'Matter.Engine.update: delta argument is recommended to be less than or equal to', Engine._deltaMax.toFixed(3), 'ms.'
);
}
delta = typeof delta !== 'undefined' ? delta : Common._baseDelta;
delta *= timing.timeScale;
// increment timestamp
timing.timestamp += delta;
timing.lastDelta = delta;
// create an event object
var event = {
timestamp: timing.timestamp,
delta: delta
};
Events.trigger(engine, 'beforeUpdate', event);
// get all bodies and all constraints in the world
var allBodies = Composite.allBodies(world),
allConstraints = Composite.allConstraints(world);
// if the world has changed
if (world.isModified) {
// update the detector bodies
Detector.setBodies(detector, allBodies);
// reset all composite modified flags
Composite.setModified(world, false, false, true);
}
// update sleeping if enabled
if (engine.enableSleeping)
Sleeping.update(allBodies, delta);
// apply gravity to all bodies
Engine._bodiesApplyGravity(allBodies, engine.gravity);
// update all body position and rotation by integration
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, delta);
}
Constraint.postSolveAll(allBodies);
// find all collisions
var collisions = Detector.collisions(detector);
// update collision pairs
Pairs.update(pairs, collisions, timestamp);
// wake up bodies involved in collisions
if (engine.enableSleeping)
Sleeping.afterCollisions(pairs.list);
// trigger collision events
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, delta, positionDamping);
}
Resolver.postSolvePosition(allBodies);
// update all constraints (second pass)
Constraint.preSolveAll(allBodies);
for (i = 0; i < engine.constraintIterations; i++) {
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, delta);
}
// update body speed and velocity properties
Engine._bodiesUpdateVelocities(allBodies);
// 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);
Events.trigger(engine, 'afterUpdate', event);
// log the time elapsed computing this update
engine.timing.lastElapsed = Common.now() - startTime;
return engine;
};
/**
* Merges two engines by keeping the configuration of `engineA` but replacing the world with the one from `engineB`.
* @method merge
* @param {engine} engineA
* @param {engine} engineB
*/
Engine.merge = function(engineA, engineB) {
Common.extend(engineA, engineB);
if (engineB.world) {
engineA.world = engineB.world;
Engine.clear(engineA);
var bodies = Composite.allBodies(engineA.world);
for (var i = 0; i < bodies.length; i++) {
var body = bodies[i];
Sleeping.set(body, false);
body.id = Common.nextId();
}
}
};
/**
* Clears the engine pairs and detector.
* @method clear
* @param {engine} engine
*/
Engine.clear = function(engine) {
Pairs.clear(engine.pairs);
Detector.clear(engine.detector);
};
/**
* Zeroes the `body.force` and `body.torque` force buffers.
* @method _bodiesClearForces
* @private
* @param {body[]} bodies
*/
Engine._bodiesClearForces = function(bodies) {
var bodiesLength = bodies.length;
for (var i = 0; i < bodiesLength; i++) {
var body = bodies[i];
// reset force buffers
body.force.x = 0;
body.force.y = 0;
body.torque = 0;
}
};
/**
* Applies gravitational acceleration to all `bodies`.
* This models a [uniform gravitational field](https://en.wikipedia.org/wiki/Gravity_of_Earth), similar to near the surface of a planet.
*
* @method _bodiesApplyGravity
* @private
* @param {body[]} bodies
* @param {vector} gravity
*/
Engine._bodiesApplyGravity = function(bodies, gravity) {
var gravityScale = typeof gravity.scale !== 'undefined' ? gravity.scale : 0.001,
bodiesLength = bodies.length;
if ((gravity.x === 0 && gravity.y === 0) || gravityScale === 0) {
return;
}
for (var i = 0; i < bodiesLength; i++) {
var body = bodies[i];
if (body.isStatic || body.isSleeping)
continue;
// 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]);
}
};
/**
* A deprecated alias for `Runner.run`, use `Matter.Runner.run(engine)` instead and see `Matter.Runner` for more information.
* @deprecated use Matter.Runner.run(engine) instead
* @method run
* @param {engine} engine
*/
/**
* Fired just before an update
*
* @event beforeUpdate
* @param {object} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {number} event.delta The delta time in milliseconds value used in the update
* @param {engine} event.source The source object of the event
* @param {string} event.name The name of the event
*/
/**
* Fired after bodies updated based on their velocity and forces, but before any collision detection, constraints and resolving etc.
*
* @event beforeSolve
* @param {object} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {number} event.delta The delta time in milliseconds value used in the update
* @param {engine} event.source The source object of the event
* @param {string} event.name The name of the event
*/
/**
* Fired after engine update and all collision events
*
* @event afterUpdate
* @param {object} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {number} event.delta The delta time in milliseconds value used in the update
* @param {engine} event.source The source object of the event
* @param {string} event.name The name of the event
*/
/**
* Fired after engine update, provides a list of all pairs that have started to collide in the current tick (if any)
*
* @event collisionStart
* @param {object} event An event object
* @param {pair[]} event.pairs List of affected pairs
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {number} event.delta The delta time in milliseconds value used in the update
* @param {engine} event.source The source object of the event
* @param {string} event.name The name of the event
*/
/**
* Fired after engine update, provides a list of all pairs that are colliding in the current tick (if any)
*
* @event collisionActive
* @param {object} event An event object
* @param {pair[]} event.pairs List of affected pairs
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {number} event.delta The delta time in milliseconds value used in the update
* @param {engine} event.source The source object of the event
* @param {string} event.name The name of the event
*/
/**
* Fired after engine update, provides a list of all pairs that have ended collision in the current tick (if any)
*
* @event collisionEnd
* @param {object} event An event object
* @param {pair[]} event.pairs List of affected pairs
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {number} event.delta The delta time in milliseconds value used in the update
* @param {engine} event.source The source object of the event
* @param {string} event.name The name of the event
*/
/*
*
* Properties Documentation
*
*/
/**
* An integer `Number` that specifies the number of position iterations to perform each update.
* The higher the value, the higher quality the simulation will be at the expense of performance.
*
* @property positionIterations
* @type number
* @default 6
*/
/**
* An integer `Number` that specifies the number of velocity iterations to perform each update.
* The higher the value, the higher quality the simulation will be at the expense of performance.
*
* @property velocityIterations
* @type number
* @default 4
*/
/**
* An integer `Number` that specifies the number of constraint iterations to perform each update.
* The higher the value, the higher quality the simulation will be at the expense of performance.
* The default value of `2` is usually very adequate.
*
* @property constraintIterations
* @type number
* @default 2
*/
/**
* A flag that specifies whether the engine should allow sleeping via the `Matter.Sleeping` module.
* Sleeping can improve stability and performance, but often at the expense of accuracy.
*
* @property enableSleeping
* @type boolean
* @default false
*/
/**
* An `Object` containing properties regarding the timing systems of the engine.
*
* @property timing
* @type object
*/
/**
* A `Number` that specifies the global scaling factor of time for all bodies.
* A value of `0` freezes the simulation.
* A value of `0.1` gives a slow-motion effect.
* A value of `1.2` gives a speed-up effect.
*
* @property timing.timeScale
* @type number
* @default 1
*/
/**
* A `Number` that specifies the current simulation-time in milliseconds starting from `0`.
* It is incremented on every `Engine.update` by the given `delta` argument.
*
* @property timing.timestamp
* @type number
* @default 0
*/
/**
* A `Number` that represents the total execution time elapsed during the last `Engine.update` in milliseconds.
* It is updated by timing from the start of the last `Engine.update` call until it ends.
*
* This value will also include the total execution time of all event handlers directly or indirectly triggered by the engine update.
*
* @property timing.lastElapsed
* @type number
* @default 0
*/
/**
* A `Number` that represents the `delta` value used in the last engine update.
*
* @property timing.lastDelta
* @type number
* @default 0
*/
/**
* A `Matter.Detector` instance.
*
* @property detector
* @type detector
* @default a Matter.Detector instance
*/
/**
* A `Matter.Grid` instance.
*
* @deprecated replaced by `engine.detector`
* @property grid
* @type grid
* @default a Matter.Grid instance
*/
/**
* Replaced by and now alias for `engine.grid`.
*
* @deprecated replaced by `engine.detector`
* @property broadphase
* @type grid
* @default a Matter.Grid instance
*/
/**
* The root `Matter.Composite` instance that will contain all bodies, constraints and other composites to be simulated by this engine.
*
* @property world
* @type composite
* @default a Matter.Composite instance
*/
/**
* An object reserved for storing plugin-specific properties.
*
* @property plugin
* @type {}
*/
/**
* An optional gravitational acceleration applied to all bodies in `engine.world` on every update.
*
* This models a [uniform gravitational field](https://en.wikipedia.org/wiki/Gravity_of_Earth), similar to near the surface of a planet. For gravity in other contexts, disable this and apply forces as needed.
*
* To disable set the `scale` component to `0`.
*
* This is split into three components for ease of use:
* a normalised direction (`x` and `y`) and magnitude (`scale`).
*
* @property gravity
* @type object
*/
/**
* The gravitational direction normal `x` component, to be multiplied by `gravity.scale`.
*
* @property gravity.x
* @type object
* @default 0
*/
/**
* The gravitational direction normal `y` component, to be multiplied by `gravity.scale`.
*
* @property gravity.y
* @type object
* @default 1
*/
/**
* The magnitude of the gravitational acceleration.
*
* @property gravity.scale
* @type object
* @default 0.001
*/
})();