Show:

File: src/core/Engine.js

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