From 2fa1570e45ac0e25ab2f2b5d05a228bc7f032061 Mon Sep 17 00:00:00 2001 From: liabru Date: Tue, 29 Jul 2014 16:26:49 +0100 Subject: [PATCH] updated edge build --- build/matter.js | 988 +++++++++++++++++++++++++++++--------------- build/matter.min.js | 6 +- src/core/Engine.js | 2 +- 3 files changed, 652 insertions(+), 344 deletions(-) diff --git a/build/matter.js b/build/matter.js index 63be5ac..b41cde4 100644 --- a/build/matter.js +++ b/build/matter.js @@ -1,5 +1,5 @@ /** -* matter.js 0.8.0-edge 2014-06-09 +* matter.js 0.8.0-edge 2014-07-29 * http://brm.io/matter-js/ * License: MIT */ @@ -57,7 +57,8 @@ var Body = {}; Body._inertiaScale = 4; - var _nextGroupId = 1; + var _nextCollidingGroupId = 1, + _nextNonCollidingGroupId = -1; /** * Creates a new rigid body model. The options parameter is an object that specifies any properties you wish to override the defaults. @@ -91,7 +92,11 @@ var Body = {}; restitution: 0, friction: 0.1, frictionAir: 0.01, - groupId: 0, + collisionFilter: { + category: 0x0001, + mask: 0xFFFFFFFF, + group: 0 + }, slop: 0.05, timeScale: 1, render: { @@ -112,12 +117,18 @@ var Body = {}; }; /** - * Returns the next unique groupID number. - * @method nextGroupId - * @return {Number} Unique groupID + * Returns the next unique group index for which bodies will collide. + * If `isNonColliding` is `true`, returns the next unique group index for which bodies will _not_ collide. + * See `body.collisionFilter` for more information. + * @method nextGroup + * @param {bool} [isNonColliding=false] + * @return {Number} Unique group index */ - Body.nextGroupId = function() { - return _nextGroupId++; + Body.nextGroup = function(isNonColliding) { + if (isNonColliding) + return _nextNonCollidingGroupId--; + + return _nextCollidingGroupId++; }; /** @@ -139,14 +150,13 @@ var Body = {}; Sleeping.set(body, body.isSleeping); Vertices.rotate(body.vertices, body.angle, body.position); Axes.rotate(body.axes, body.angle); + Bounds.update(body.bounds, body.vertices, body.velocity); // allow options to override the automatically calculated properties body.axes = options.axes || body.axes; body.area = options.area || body.area; - body.mass = options.mass || body.mass; - body.inertia = options.inertia || body.inertia; - body.inverseMass = 1 / body.mass; - body.inverseInertia = 1 / body.inertia; + Body.setMass(body, options.mass || body.mass); + Body.setInertia(body, options.inertia || body.inertia); // render properties var defaultFillStyle = (body.isStatic ? '#eeeeee' : Common.choose(['#556270', '#4ECDC4', '#C7F464', '#FF6B6B', '#C44D58'])), @@ -169,7 +179,6 @@ var Body = {}; body.friction = 1; body.mass = body.inertia = body.density = Infinity; body.inverseMass = body.inverseInertia = 0; - body.render.lineWidth = 1; body.positionPrev.x = body.position.x; body.positionPrev.y = body.position.y; @@ -181,6 +190,41 @@ var Body = {}; } }; + /** + * Sets the mass of the body. Inverse mass and density are automatically updated to reflect the change. + * @method setMass + * @param {body} body + * @param {number} mass + */ + Body.setMass = function(body, mass) { + body.mass = mass; + body.inverseMass = 1 / body.mass; + body.density = body.mass / body.area; + }; + + /** + * Sets the density of the body. Mass is automatically updated to reflect the change. + * @method setDensity + * @param {body} body + * @param {number} density + */ + Body.setDensity = function(body, density) { + Body.setMass(body, density * body.area); + body.density = density; + }; + + /** + * Sets the moment of inertia (i.e. second moment of area) of the body of the body. + * Inverse inertia is automatically updated to reflect the change. Mass is not changed. + * @method setInertia + * @param {body} body + * @param {number} inertia + */ + Body.setInertia = function(body, inertia) { + body.inertia = inertia; + body.inverseInertia = 1 / body.inertia; + }; + /** * Sets the body's vertices and updates body properties accordingly, including inertia, area and mass (with respect to `body.density`). * Vertices will be automatically transformed to be orientated around their centre of mass as the origin. @@ -204,16 +248,14 @@ var Body = {}; // update properties body.axes = Axes.fromVertices(body.vertices); body.area = Vertices.area(body.vertices); - body.mass = body.density * body.area; - body.inverseMass = 1 / body.mass; + Body.setMass(body, body.density * body.area); // orient vertices around the centre of mass at origin (0, 0) var centre = Vertices.centre(body.vertices); Vertices.translate(body.vertices, centre, -1); // update inertia while vertices are at origin (0, 0) - body.inertia = Body._inertiaScale * Vertices.inertia(body.vertices, body.mass); - body.inverseInertia = 1 / body.inertia; + Body.setInertia(body, Body._inertiaScale * Vertices.inertia(body.vertices, body.mass)); // update geometry Vertices.translate(body.vertices, body.position); @@ -298,7 +340,7 @@ var Body = {}; * @param {number} rotation */ Body.rotate = function(body, rotation) { - Body.setAngle(body, body.angle + angle); + Body.setAngle(body, body.angle + rotation); }; /** @@ -316,13 +358,11 @@ var Body = {}; // update properties body.axes = Axes.fromVertices(body.vertices); body.area = Vertices.area(body.vertices); - body.mass = body.density * body.area; - body.inverseMass = 1 / body.mass; + Body.setMass(body, body.density * body.area); // update inertia (requires vertices to be at origin) Vertices.translate(body.vertices, { x: -body.position.x, y: -body.position.y }); - body.inertia = Vertices.inertia(body.vertices, body.mass); - body.inverseInertia = 1 / body.inertia; + Body.setInertia(body, Vertices.inertia(body.vertices, body.mass)); Vertices.translate(body.vertices, { x: body.position.x, y: body.position.y }); // update bounds @@ -685,18 +725,62 @@ var Body = {}; */ /** - * An integer `Number` that specifies the collision group the body belongs to. - * Bodies with the same `groupId` are considered _as-one_ body and therefore do not interact. - * This allows for creation of segmented bodies that can self-intersect, such as a rope. - * The default value 0 means the body does not belong to a group, and can interact with all other bodies. + * An `Object` that specifies the collision filtering properties of this body. * - * @property groupId - * @type number + * Collisions between two bodies will obey the following rules: + * - If the two bodies have the same non-zero value of `collisionFilter.group`, + * they will always collide if the value is positive, and they will never collide + * if the value is negative. + * - If the two bodies have different values of `collisionFilter.group` or if one + * (or both) of the bodies has a value of 0, then the category/mask rules apply as follows: + * + * Each body belongs to a collision category, given by `collisionFilter.category`. This + * value is used as a bit field and the category should have only one bit set, meaning that + * the value of this property is a power of two in the range [1, 2^31]. Thus, there are 32 + * different collision categories available. + * + * Each body also defines a collision bitmask, given by `collisionFilter.mask` which specifies + * the categories it collides with (the value is the bitwise AND value of all these categories). + * + * Using the category/mask rules, two bodies `A` and `B` collide if each includes the other's + * category in its mask, i.e. `(categoryA & maskB) !== 0` and `(categoryB & maskA) !== 0` + * are both true. + * + * @property collisionFilter + * @type object + */ + + /** + * An Integer `Number`, that specifies the collision group this body belongs to. + * See `body.collisionFilter` for more information. + * + * @property collisionFilter.group + * @type object * @default 0 */ /** - * A `Number` that specifies a tollerance on how far a body is allowed to 'sink' or rotate into other bodies. + * A bit field that specifies the collision category this body belongs to. + * The category value should have only one bit set, for example `0x0001`. + * This means there are up to 32 unique collision categories available. + * See `body.collisionFilter` for more information. + * + * @property collisionFilter.category + * @type object + * @default 1 + */ + + /** + * A bit mask that specifies the collision categories this body may collide with. + * See `body.collisionFilter` for more information. + * + * @property collisionFilter.mask + * @type object + * @default -1 + */ + + /** + * A `Number` that specifies a tolerance on how far a body is allowed to 'sink' or rotate into other bodies. * Avoid changing this value unless you understand the purpose of `slop` in physics engines. * The default should generally suffice, although very large bodies may require larger values for stable stacking. * @@ -812,6 +896,7 @@ var Body = {}; })(); + ; // End src/body/Body.js @@ -882,6 +967,7 @@ var Composite = {}; /** * Generic add function. Adds one or many body(s), constraint(s) or a composite(s) to the given composite. + * Triggers `beforeAdd` and `afterAdd` events on the `composite`. * @method add * @param {composite} composite * @param {} object @@ -890,6 +976,8 @@ var Composite = {}; Composite.add = function(composite, object) { var objects = [].concat(object); + Events.trigger(composite, 'beforeAdd', { object: object }); + for (var i = 0; i < objects.length; i++) { var obj = objects[i]; @@ -911,12 +999,15 @@ var Composite = {}; } } + Events.trigger(composite, 'afterAdd', { object: object }); + return composite; }; /** * Generic remove function. Removes one or many body(s), constraint(s) or a composite(s) to the given composite. * Optionally searching its children recursively. + * Triggers `beforeRemove` and `afterRemove` events on the `composite`. * @method remove * @param {composite} composite * @param {} object @@ -926,6 +1017,8 @@ var Composite = {}; Composite.remove = function(composite, object, deep) { var objects = [].concat(object); + Events.trigger(composite, 'beforeRemove', { object: object }); + for (var i = 0; i < objects.length; i++) { var obj = objects[i]; @@ -947,11 +1040,14 @@ var Composite = {}; } } + Events.trigger(composite, 'afterRemove', { object: object }); + return composite; }; /** * Adds a composite to the given composite + * @private * @method addComposite * @param {composite} compositeA * @param {composite} compositeB @@ -966,6 +1062,7 @@ var Composite = {}; /** * Removes a composite from the given composite, and optionally searching its children recursively + * @private * @method removeComposite * @param {composite} compositeA * @param {composite} compositeB @@ -973,7 +1070,7 @@ var Composite = {}; * @return {composite} The original compositeA with the composite removed */ Composite.removeComposite = function(compositeA, compositeB, deep) { - var position = compositeA.composites.indexOf(compositeB); + var position = Common.indexOf(compositeA.composites, compositeB); if (position !== -1) { Composite.removeCompositeAt(compositeA, position); Composite.setModified(compositeA, true, true, false); @@ -990,6 +1087,7 @@ var Composite = {}; /** * Removes a composite from the given composite + * @private * @method removeCompositeAt * @param {composite} composite * @param {number} position @@ -1003,6 +1101,7 @@ var Composite = {}; /** * Adds a body to the given composite + * @private * @method addBody * @param {composite} composite * @param {body} body @@ -1016,6 +1115,7 @@ var Composite = {}; /** * Removes a body from the given composite, and optionally searching its children recursively + * @private * @method removeBody * @param {composite} composite * @param {body} body @@ -1023,7 +1123,7 @@ var Composite = {}; * @return {composite} The original composite with the body removed */ Composite.removeBody = function(composite, body, deep) { - var position = composite.bodies.indexOf(body); + var position = Common.indexOf(composite.bodies, body); if (position !== -1) { Composite.removeBodyAt(composite, position); Composite.setModified(composite, true, true, false); @@ -1040,6 +1140,7 @@ var Composite = {}; /** * Removes a body from the given composite + * @private * @method removeBodyAt * @param {composite} composite * @param {number} position @@ -1053,6 +1154,7 @@ var Composite = {}; /** * Adds a constraint to the given composite + * @private * @method addConstraint * @param {composite} composite * @param {constraint} constraint @@ -1066,6 +1168,7 @@ var Composite = {}; /** * Removes a constraint from the given composite, and optionally searching its children recursively + * @private * @method removeConstraint * @param {composite} composite * @param {constraint} constraint @@ -1073,7 +1176,7 @@ var Composite = {}; * @return {composite} The original composite with the constraint removed */ Composite.removeConstraint = function(composite, constraint, deep) { - var position = composite.constraints.indexOf(constraint); + var position = Common.indexOf(composite.constraints, constraint); if (position !== -1) { Composite.removeConstraintAt(composite, position); } @@ -1089,6 +1192,7 @@ var Composite = {}; /** * Removes a body from the given composite + * @private * @method removeConstraintAt * @param {composite} composite * @param {number} position @@ -1241,6 +1345,52 @@ var Composite = {}; return composite; }; + /* + * + * Events Documentation + * + */ + + /** + * Fired when a call to `Composite.add` is made, before objects have been added. + * + * @event beforeAdd + * @param {} event An event object + * @param {} event.object The object(s) to be added (may be a single body, constraint, composite or a mixed array of these) + * @param {} event.source The source object of the event + * @param {} event.name The name of the event + */ + + /** + * Fired when a call to `Composite.add` is made, after objects have been added. + * + * @event afterAdd + * @param {} event An event object + * @param {} event.object The object(s) that have been added (may be a single body, constraint, composite or a mixed array of these) + * @param {} event.source The source object of the event + * @param {} event.name The name of the event + */ + + /** + * Fired when a call to `Composite.remove` is made, before objects have been removed. + * + * @event beforeRemove + * @param {} event An event object + * @param {} event.object The object(s) to be removed (may be a single body, constraint, composite or a mixed array of these) + * @param {} event.source The source object of the event + * @param {} event.name The name of the event + */ + + /** + * Fired when a call to `Composite.remove` is made, after objects have been removed. + * + * @event afterRemove + * @param {} event An event object + * @param {} event.object The object(s) that have been removed (may be a single body, constraint, composite or a mixed array of these) + * @param {} event.source The source object of the event + * @param {} event.name The name of the event + */ + /* * * Properties Documentation @@ -1476,13 +1626,11 @@ var Detector = {}; var bodyA = broadphasePairs[i][0], bodyB = broadphasePairs[i][1]; - // NOTE: could share a function for the below, but may drop performance? - - if (bodyA.groupId && bodyB.groupId && bodyA.groupId === bodyB.groupId) - continue; - if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping)) continue; + + if (!Detector.canCollide(bodyA.collisionFilter, bodyB.collisionFilter)) + continue; metrics.midphaseTests += 1; @@ -1537,11 +1685,11 @@ var Detector = {}; // NOTE: could share a function for the below, but may drop performance? - if (bodyA.groupId && bodyB.groupId && bodyA.groupId === bodyB.groupId) - continue; - if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping)) continue; + + if (!Detector.canCollide(bodyA.collisionFilter, bodyB.collisionFilter)) + continue; metrics.midphaseTests += 1; @@ -1578,8 +1726,24 @@ var Detector = {}; return collisions; }; + /** + * Returns `true` if both supplied collision filters will allow a collision to occur. + * See `body.collisionFilter` for more information. + * @method canCollide + * @param {} filterA + * @param {} filterB + * @return {bool} `true` if collision can occur + */ + Detector.canCollide = function(filterA, filterB) { + if (filterA.group === filterB.group && filterA.group !== 0) + return filterA.group > 0; + + return (filterA.mask & filterB.category) !== 0 && (filterB.mask & filterA.category) !== 0; + }; + })(); + ; // End src/collision/Detector.js @@ -1599,18 +1763,21 @@ var Grid = {}; /** * Description * @method create - * @param {number} bucketWidth - * @param {number} bucketHeight + * @param {} options * @return {grid} A new grid */ - Grid.create = function(bucketWidth, bucketHeight) { - return { + Grid.create = function(options) { + var defaults = { + controller: Grid, + detector: Detector.collisions, buckets: {}, pairs: {}, pairsList: [], - bucketWidth: bucketWidth || 48, - bucketHeight: bucketHeight || 48 + bucketWidth: 48, + bucketHeight: 48 }; + + return Common.extend(defaults, options); }; /** @@ -1831,7 +1998,7 @@ var Grid = {}; */ var _bucketRemoveBody = function(grid, bucket, body) { // remove from bucket - bucket.splice(bucket.indexOf(body), 1); + bucket.splice(Common.indexOf(bucket, body), 1); // update pair counts for (var i = 0; i < bucket.length; i++) { @@ -2085,7 +2252,7 @@ var Pairs = {}; // deactivate previously active pairs that are now inactive for (i = 0; i < pairsList.length; i++) { pair = pairsList[i]; - if (pair.isActive && activePairIds.indexOf(pair.id) === -1) { + if (pair.isActive && Common.indexOf(activePairIds, pair.id) === -1) { Pair.setActive(pair, false, timestamp); collisionEnd.push(pair); } @@ -2609,24 +2776,33 @@ var SAT = {}; // find support points, there is always either exactly one or two var verticesB = _findSupports(bodyA, bodyB, collision.normal), + supports = collision.supports || []; + supports.length = 0; + + // find the supports from bodyB that are inside bodyA + if (Vertices.contains(bodyA.vertices, verticesB[0])) + supports.push(verticesB[0]); + + if (Vertices.contains(bodyA.vertices, verticesB[1])) + supports.push(verticesB[1]); + + // find the supports from bodyA that are inside bodyB + if (supports.length < 2) { + var verticesA = _findSupports(bodyB, bodyA, Vector.neg(collision.normal)); + + if (Vertices.contains(bodyB.vertices, verticesA[0])) + supports.push(verticesA[0]); + + if (supports.length < 2 && Vertices.contains(bodyB.vertices, verticesA[1])) + supports.push(verticesA[1]); + } + + // account for the edge case of overlapping but no vertex containment + if (supports.length < 2) supports = [verticesB[0]]; - if (Vertices.contains(bodyA.vertices, verticesB[1])) { - supports.push(verticesB[1]); - } else { - var verticesA = _findSupports(bodyB, bodyA, Vector.neg(collision.normal)); - - if (Vertices.contains(bodyB.vertices, verticesA[0])) { - supports.push(verticesA[0]); - } - - if (supports.length < 2 && Vertices.contains(bodyB.vertices, verticesA[1])) { - supports.push(verticesA[1]); - } - } - collision.supports = supports; - collision.supportCorrected = Vector.sub(verticesB[0], collision.penetration); + collision.supportCorrected = Vector.sub(supports[0], collision.penetration); return collision; }; @@ -3167,7 +3343,7 @@ var MouseConstraint = {}; * @return {MouseConstraint} A new MouseConstraint */ MouseConstraint.create = function(engine, options) { - var mouse = engine.input.mouse; + var mouse = (options && options.mouse) || Mouse.create(engine.render.canvas); var constraint = Constraint.create({ label: 'Mouse Constraint', @@ -3187,7 +3363,12 @@ var MouseConstraint = {}; mouse: mouse, dragBody: null, dragPoint: null, - constraint: constraint + constraint: constraint, + collisionFilter: { + category: 0x0001, + mask: 0xFFFFFFFF, + group: 0 + } }; var mouseConstraint = Common.extend(defaults, options); @@ -3195,6 +3376,7 @@ var MouseConstraint = {}; Events.on(engine, 'tick', function(event) { var allBodies = Composite.allBodies(engine.world); MouseConstraint.update(mouseConstraint, allBodies); + _triggerEvents(mouseConstraint); }); return mouseConstraint; @@ -3216,7 +3398,8 @@ var MouseConstraint = {}; for (var i = 0; i < bodies.length; i++) { var body = bodies[i]; if (Bounds.contains(body.bounds, mouse.position) - && Vertices.contains(body.vertices, mouse.position)) { + && Vertices.contains(body.vertices, mouse.position) + && Detector.canCollide(body.collisionFilter, mouseConstraint.collisionFilter)) { constraint.pointA = mouse.position; constraint.bodyB = body; constraint.pointB = { x: mouse.position.x - body.position.x, y: mouse.position.y - body.position.y }; @@ -3236,6 +3419,65 @@ var MouseConstraint = {}; } }; + /** + * Triggers mouse constraint events + * @method _triggerEvents + * @private + * @param {mouse} mouse + */ + var _triggerEvents = function(mouseConstraint) { + var mouse = mouseConstraint.mouse, + mouseEvents = mouse.sourceEvents; + + if (mouseEvents.mousemove) + Events.trigger(mouseConstraint, 'mousemove', { mouse: mouse }); + + if (mouseEvents.mousedown) + Events.trigger(mouseConstraint, 'mousedown', { mouse: mouse }); + + if (mouseEvents.mouseup) + Events.trigger(mouseConstraint, 'mouseup', { mouse: mouse }); + + // reset the mouse state ready for the next step + Mouse.clearSourceEvents(mouse); + }; + + /* + * + * Events Documentation + * + */ + + /** + * Fired when the mouse has moved (or a touch moves) during the last step + * + * @event mousemove + * @param {} event An event object + * @param {mouse} event.mouse The engine's mouse instance + * @param {} event.source The source object of the event + * @param {} event.name The name of the event + */ + + /** + * Fired when the mouse is down (or a touch has started) during the last step + * + * @event mousedown + * @param {} event An event object + * @param {mouse} event.mouse The engine's mouse instance + * @param {} event.source The source object of the event + * @param {} event.name The name of the event + */ + + /** + * Fired when the mouse is up (or a touch has ended) during the last step + * + * @event mouseup + * @param {} event An event object + * @param {mouse} event.mouse The engine's mouse instance + * @param {} event.source The source object of the event + * @param {} event.name The name of the event + */ + /* * * Properties Documentation @@ -3251,11 +3493,11 @@ var MouseConstraint = {}; */ /** - * The `Mouse` instance in use. + * The `Mouse` instance in use. If not supplied in `MouseConstraint.create`, one will be created. * * @property mouse * @type mouse - * @default engine.input.mouse + * @default mouse */ /** @@ -3281,6 +3523,15 @@ var MouseConstraint = {}; * @type constraint */ + /** + * An `Object` that specifies the collision filter properties. + * The collision filter allows the user to define which types of body this mouse constraint can interact with. + * See `body.collisionFilter` for more information. + * + * @property collisionFilter + * @type object + */ + })(); ; // End src/constraint/MouseConstraint.js @@ -3571,6 +3822,24 @@ var Common = {}; return Common._nextId++; }; + /** + * A cross browser compatible indexOf implementation + * @method indexOf + * @param {array} haystack + * @param {object} needle + */ + Common.indexOf = function(haystack, needle) { + if (haystack.indexOf) + return haystack.indexOf(needle); + + for (var i = 0; i < haystack.length; i++) { + if (haystack[i] === needle) + return i; + } + + return -1; + }; + var _seededRandom = function() { // https://gist.github.com/ngryman/3830489 Common._seed = (Common._seed * 9301 + 49297) % 233280; @@ -3587,6 +3856,7 @@ var Common = {}; /** * The `Matter.Engine` module contains methods for creating and manipulating engines. * An engine is a controller that manages updating and rendering the simulation of the world. +* See `Matter.Runner` for an optional game loop utility. * * See [Demo.js](https://github.com/liabru/matter-js/blob/master/demo/js/Demo.js) * and [DemoMobile.js](https://github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples. @@ -3599,13 +3869,8 @@ var Engine = {}; (function() { var _fps = 60, - _deltaSampleSize = _fps, _delta = 1000 / _fps; - - var _requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame - || window.mozRequestAnimationFrame || window.msRequestAnimationFrame - || function(callback){ window.setTimeout(function() { callback(Common.now()); }, _delta); }; - + /** * 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. @@ -3627,8 +3892,6 @@ var Engine = {}; velocityIterations: 4, constraintIterations: 2, enableSleeping: false, - timeScale: 1, - input: {}, events: [], timing: { fps: _fps, @@ -3638,11 +3901,15 @@ var Engine = {}; deltaMin: 1000 / _fps, deltaMax: 1000 / (_fps * 0.5), timeScale: 1, - isFixed: false + isFixed: false, + frameRequestId: 0 }, render: { element: element, controller: Render + }, + broadphase: { + controller: Grid } }; @@ -3652,118 +3919,15 @@ var Engine = {}; engine.world = World.create(engine.world); engine.pairs = Pairs.create(); engine.metrics = engine.metrics || Metrics.create(); - engine.input.mouse = engine.input.mouse || Mouse.create(engine.render.canvas); - - engine.broadphase = engine.broadphase || { - current: 'grid', - grid: { - controller: Grid, - instance: Grid.create(), - detector: Detector.collisions - }, - bruteForce: { - detector: Detector.bruteForce - } - }; + engine.broadphase = engine.broadphase.controller.create(engine.broadphase); return engine; }; /** - * An optional utility function that provides a game loop, that handles updating the engine for you. - * Calls `Engine.update` and `Engine.render` on the `requestAnimationFrame` event automatically. - * Handles time correction and non-fixed dynamic timing (if enabled). - * Triggers `beforeTick`, `tick` and `afterTick` events. - * @method run - * @param {engine} engine - */ - Engine.run = function(engine) { - var counterTimestamp = 0, - frameCounter = 0, - deltaHistory = [], - timePrev, - timeScalePrev = 1; - - (function render(time){ - _requestAnimationFrame(render); - - if (!engine.enabled) - return; - - var timing = engine.timing, - delta, - correction; - - // create an event object - var event = { - timestamp: time - }; - - Events.trigger(engine, 'beforeTick', event); - - if (timing.isFixed) { - // fixed timestep - delta = timing.delta; - } else { - // dynamic timestep based on wall clock between calls - delta = (time - timePrev) || timing.delta; - timePrev = time; - - // optimistically filter delta over a few frames, to improve stability - deltaHistory.push(delta); - deltaHistory = deltaHistory.slice(-_deltaSampleSize); - delta = Math.min.apply(null, deltaHistory); - - // limit delta - delta = delta < timing.deltaMin ? timing.deltaMin : delta; - delta = delta > timing.deltaMax ? timing.deltaMax : delta; - - // time correction for delta - correction = delta / timing.delta; - - // update engine timing object - timing.delta = delta; - } - - // time correction for time scaling - if (timeScalePrev !== 0) - correction *= timing.timeScale / timeScalePrev; - - if (timing.timeScale === 0) - correction = 0; - - timeScalePrev = timing.timeScale; - - // fps counter - frameCounter += 1; - if (time - counterTimestamp >= 1000) { - timing.fps = frameCounter * ((time - counterTimestamp) / 1000); - counterTimestamp = time; - frameCounter = 0; - } - - Events.trigger(engine, 'tick', event); - - // if world has been modified, clear the render scene graph - if (engine.world.isModified) - engine.render.controller.clear(engine.render); - - // update - Engine.update(engine, delta, correction); - - // trigger events that may have occured during the step - _triggerCollisionEvents(engine); - _triggerMouseEvents(engine); - - // render - Engine.render(engine); - - Events.trigger(engine, 'afterTick', event); - })(); - }; - - /** - * Moves the simulation forward in time by `delta` ms. Triggers `beforeUpdate` and `afterUpdate` events. + * Moves the simulation forward in time by `delta` ms. + * Triggers `beforeUpdate` and `afterUpdate` events. + * Triggers `collisionStart`, `collisionActive` and `collisionEnd` events. * @method update * @param {engine} engine * @param {number} delta @@ -3774,7 +3938,7 @@ var Engine = {}; var world = engine.world, timing = engine.timing, - broadphase = engine.broadphase[engine.broadphase.current], + broadphase = engine.broadphase, broadphasePairs = [], i; @@ -3798,7 +3962,7 @@ var Engine = {}; // if sleeping enabled, call the sleeping controller if (engine.enableSleeping) - Sleeping.update(allBodies); + Sleeping.update(allBodies, timing.timeScale); // applies gravity to all bodies Body.applyGravityAll(allBodies, world.gravity); @@ -3817,11 +3981,11 @@ var Engine = {}; // if world is dirty, we must flush the whole grid if (world.isModified) - broadphase.controller.clear(broadphase.instance); + broadphase.controller.clear(broadphase); // update the grid buckets based on current bodies - broadphase.controller.update(broadphase.instance, allBodies, engine, world.isModified); - broadphasePairs = broadphase.instance.pairsList; + broadphase.controller.update(broadphase, allBodies, engine, world.isModified); + broadphasePairs = broadphase.pairsList; } else { // if no broadphase set, we just pass all bodies @@ -3839,7 +4003,11 @@ var Engine = {}; // wake up bodies involved in collisions if (engine.enableSleeping) - Sleeping.afterCollisions(pairs.list); + Sleeping.afterCollisions(pairs.list, timing.timeScale); + + // trigger collision events + if (pairs.collisionStart.length > 0) + Events.trigger(engine, 'collisionStart', { pairs: pairs.collisionStart }); // iteratively resolve velocity between collisions Resolver.preSolveVelocity(pairs.list); @@ -3853,6 +4021,13 @@ var Engine = {}; } Resolver.postSolvePosition(allBodies); + // trigger collision events + if (pairs.collisionActive.length > 0) + Events.trigger(engine, 'collisionActive', { pairs: pairs.collisionActive }); + + if (pairs.collisionEnd.length > 0) + Events.trigger(engine, 'collisionEnd', { pairs: pairs.collisionEnd }); + // update metrics log Metrics.update(engine.metrics, engine); @@ -3919,73 +4094,19 @@ var Engine = {}; Pairs.clear(engine.pairs); - var broadphase = engine.broadphase[engine.broadphase.current]; + var broadphase = engine.broadphase; if (broadphase.controller) { var bodies = Composite.allBodies(world); - broadphase.controller.clear(broadphase.instance); - broadphase.controller.update(broadphase.instance, bodies, engine, true); + broadphase.controller.clear(broadphase); + broadphase.controller.update(broadphase, bodies, engine, true); } }; /** - * Triggers mouse events - * @method _triggerMouseEvents - * @private + * An alias for `Runner.run`, see `Matter.Runner` for more information. + * @method run * @param {engine} engine */ - var _triggerMouseEvents = function(engine) { - var mouse = engine.input.mouse, - mouseEvents = mouse.sourceEvents; - - if (mouseEvents.mousemove) { - Events.trigger(engine, 'mousemove', { - mouse: mouse - }); - } - - if (mouseEvents.mousedown) { - Events.trigger(engine, 'mousedown', { - mouse: mouse - }); - } - - if (mouseEvents.mouseup) { - Events.trigger(engine, 'mouseup', { - mouse: mouse - }); - } - - // reset the mouse state ready for the next step - Mouse.clearSourceEvents(mouse); - }; - - /** - * Triggers collision events - * @method _triggerMouseEvents - * @private - * @param {engine} engine - */ - var _triggerCollisionEvents = function(engine) { - var pairs = engine.pairs; - - if (pairs.collisionStart.length > 0) { - Events.trigger(engine, 'collisionStart', { - pairs: pairs.collisionStart - }); - } - - if (pairs.collisionActive.length > 0) { - Events.trigger(engine, 'collisionActive', { - pairs: pairs.collisionActive - }); - } - - if (pairs.collisionEnd.length > 0) { - Events.trigger(engine, 'collisionEnd', { - pairs: pairs.collisionEnd - }); - } - }; /* * @@ -4063,36 +4184,6 @@ var Engine = {}; * @param {} event.name The name of the event */ - /** - * Fired when the mouse has moved (or a touch moves) during the last step - * - * @event mousemove - * @param {} event An event object - * @param {mouse} event.mouse The engine's mouse instance - * @param {} event.source The source object of the event - * @param {} event.name The name of the event - */ - - /** - * Fired when the mouse is down (or a touch has started) during the last step - * - * @event mousedown - * @param {} event An event object - * @param {mouse} event.mouse The engine's mouse instance - * @param {} event.source The source object of the event - * @param {} event.name The name of the event - */ - - /** - * Fired when the mouse is up (or a touch has ended) during the last step - * - * @event mouseup - * @param {} event An event object - * @param {mouse} event.mouse The engine's mouse instance - * @param {} event.source The source object of the event - * @param {} 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) * @@ -4204,6 +4295,16 @@ var Engine = {}; * @default 0 */ + /** + * A `Boolean` that specifies if the `Engine.run` game loop should use a fixed timestep (otherwise it is variable). + * If timing is fixed, then the apparant simulation speed will change depending on the frame rate (but behaviour will be deterministic). + * If the timing is variable, then the apparant simulation speed will be constant (approximately, but at the cost of determininism). + * + * @property timing.isFixed + * @type boolean + * @default false + */ + /** * A `Number` that specifies the time step between updates in milliseconds. * If `engine.timing.isFixed` is set to `true`, then `delta` is fixed. @@ -4239,6 +4340,14 @@ var Engine = {}; * @default a Matter.Render instance */ + /** + * An instance of a broadphase controller. The default value is a `Matter.Grid` instance created by `Engine.create`. + * + * @property broadphase + * @type grid + * @default a Matter.Grid instance + */ + /** * A `World` composite object that will contain all simulated bodies and constraints. * @@ -4459,35 +4568,37 @@ var Metrics = {}; * @class Mouse */ -var Mouse; +var Mouse = {}; (function() { - + /** * Description + * @method create * @param {HTMLElement} element + * @return {mouse} A new mouse */ - Mouse = function(element) { - var mouse = this; + Mouse.create = function(element) { + var mouse = {}; - this.element = element || document.body; - this.absolute = { x: 0, y: 0 }; - this.position = { x: 0, y: 0 }; - this.mousedownPosition = { x: 0, y: 0 }; - this.mouseupPosition = { x: 0, y: 0 }; - this.offset = { x: 0, y: 0 }; - this.scale = { x: 1, y: 1 }; - this.wheelDelta = 0; - this.button = -1; + mouse.element = element || document.body; + mouse.absolute = { x: 0, y: 0 }; + mouse.position = { x: 0, y: 0 }; + mouse.mousedownPosition = { x: 0, y: 0 }; + mouse.mouseupPosition = { x: 0, y: 0 }; + mouse.offset = { x: 0, y: 0 }; + mouse.scale = { x: 1, y: 1 }; + mouse.wheelDelta = 0; + mouse.button = -1; - this.sourceEvents = { + mouse.sourceEvents = { mousemove: null, mousedown: null, mouseup: null, mousewheel: null }; - this.mousemove = function(event) { + mouse.mousemove = function(event) { var position = _getRelativeMousePosition(event, mouse.element), touches = event.changedTouches; @@ -4503,7 +4614,7 @@ var Mouse; mouse.sourceEvents.mousemove = event; }; - this.mousedown = function(event) { + mouse.mousedown = function(event) { var position = _getRelativeMousePosition(event, mouse.element), touches = event.changedTouches; @@ -4523,7 +4634,7 @@ var Mouse; mouse.sourceEvents.mousedown = event; }; - this.mouseup = function(event) { + mouse.mouseup = function(event) { var position = _getRelativeMousePosition(event, mouse.element), touches = event.changedTouches; @@ -4541,22 +4652,14 @@ var Mouse; mouse.sourceEvents.mouseup = event; }; - this.mousewheel = function(event) { + mouse.mousewheel = function(event) { mouse.wheelDelta = Math.max(-1, Math.min(1, event.wheelDelta || -event.detail)); event.preventDefault(); }; Mouse.setElement(mouse, mouse.element); - }; - /** - * Description - * @method create - * @param {HTMLElement} element - * @return {mouse} A new mouse - */ - Mouse.create = function(element) { - return new Mouse(element); + return mouse; }; /** @@ -4652,6 +4755,138 @@ var Mouse; ; // End src/core/Mouse.js +// Begin src/core/Runner.js + +/** +* The `Matter.Runner` module is an optional utility which provides a game loop, +* that handles updating and rendering a `Matter.Engine` for you within a browser. +* Note that the method `Engine.run` is an alias for `Runner.run`. +* +* See [Demo.js](https://github.com/liabru/matter-js/blob/master/demo/js/Demo.js) +* and [DemoMobile.js](https://github.com/liabru/matter-js/blob/master/demo/js/DemoMobile.js) for usage examples. +* +* @class Runner +*/ + +var Runner = {}; + +(function() { + + var _fps = 60, + _deltaSampleSize = _fps, + _delta = 1000 / _fps; + + var _requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame + || window.mozRequestAnimationFrame || window.msRequestAnimationFrame + || function(callback){ window.setTimeout(function() { callback(Common.now()); }, _delta); }; + + var _cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame + || window.webkitCancelAnimationFrame || window.msCancelAnimationFrame; + + /** + * Provides a basic game loop that handles updating the engine for you. + * Calls `Engine.update` and `Engine.render` on the `requestAnimationFrame` event automatically. + * Handles time correction and non-fixed dynamic timing (if enabled). + * Triggers `beforeTick`, `tick` and `afterTick` events. + * @method run + * @param {engine} engine + */ + Runner.run = function(engine) { + var counterTimestamp = 0, + frameCounter = 0, + deltaHistory = [], + timePrev, + timeScalePrev = 1; + + (function render(time){ + var timing = engine.timing, + delta, + correction; + + timing.frameRequestId = _requestAnimationFrame(render); + + if (!engine.enabled) + return; + + // create an event object + var event = { + timestamp: time + }; + + Events.trigger(engine, 'beforeTick', event); + + if (timing.isFixed) { + // fixed timestep + delta = timing.delta; + } else { + // dynamic timestep based on wall clock between calls + delta = (time - timePrev) || timing.delta; + timePrev = time; + + // optimistically filter delta over a few frames, to improve stability + deltaHistory.push(delta); + deltaHistory = deltaHistory.slice(-_deltaSampleSize); + delta = Math.min.apply(null, deltaHistory); + + // limit delta + delta = delta < timing.deltaMin ? timing.deltaMin : delta; + delta = delta > timing.deltaMax ? timing.deltaMax : delta; + + // time correction for delta + correction = delta / timing.delta; + + // update engine timing object + timing.delta = delta; + } + + // time correction for time scaling + if (timeScalePrev !== 0) + correction *= timing.timeScale / timeScalePrev; + + if (timing.timeScale === 0) + correction = 0; + + timeScalePrev = timing.timeScale; + + // fps counter + frameCounter += 1; + if (time - counterTimestamp >= 1000) { + timing.fps = frameCounter * ((time - counterTimestamp) / 1000); + counterTimestamp = time; + frameCounter = 0; + } + + Events.trigger(engine, 'tick', event); + + // if world has been modified, clear the render scene graph + if (engine.world.isModified) + engine.render.controller.clear(engine.render); + + // update + Engine.update(engine, delta, correction); + + // render + Engine.render(engine); + + Events.trigger(engine, 'afterTick', event); + })(); + }; + + /** + * Ends execution of `Runner.run` on the given `engine`, by canceling the animation frame request event loop. + * If you wish to only temporarily pause the engine, see `engine.enabled` instead. + * @method stop + * @param {engine} engine + */ + Runner.stop = function(engine) { + _cancelAnimationFrame(engine.timing.frameRequestId); + }; + +})(); + +; // End src/core/Runner.js + + // Begin src/core/Sleeping.js /** @@ -4664,16 +4899,19 @@ var Sleeping = {}; (function() { - var _motionWakeThreshold = 0.18, - _motionSleepThreshold = 0.08, - _minBias = 0.9; + Sleeping._motionWakeThreshold = 0.18; + Sleeping._motionSleepThreshold = 0.08; + Sleeping._minBias = 0.9; /** - * Description + * Puts bodies to sleep or wakes them up depending on their motion. * @method update * @param {body[]} bodies + * @param {number} timeScale */ - Sleeping.update = function(bodies) { + Sleeping.update = function(bodies, timeScale) { + var timeFactor = timeScale * timeScale * timeScale; + // update bodies sleeping status for (var i = 0; i < bodies.length; i++) { var body = bodies[i], @@ -4689,9 +4927,9 @@ var Sleeping = {}; maxMotion = Math.max(body.motion, motion); // biased average motion estimation between frames - body.motion = _minBias * minMotion + (1 - _minBias) * maxMotion; + body.motion = Sleeping._minBias * minMotion + (1 - Sleeping._minBias) * maxMotion; - if (body.sleepThreshold > 0 && body.motion < _motionSleepThreshold) { + if (body.sleepThreshold > 0 && body.motion < Sleeping._motionSleepThreshold * timeFactor) { body.sleepCounter += 1; if (body.sleepCounter >= body.sleepThreshold) @@ -4703,11 +4941,14 @@ var Sleeping = {}; }; /** - * Description + * Given a set of colliding pairs, wakes the sleeping bodies involved. * @method afterCollisions * @param {pair[]} pairs + * @param {number} timeScale */ - Sleeping.afterCollisions = function(pairs) { + Sleeping.afterCollisions = function(pairs, timeScale) { + var timeFactor = timeScale * timeScale * timeScale; + // wake up bodies involved in collisions for (var i = 0; i < pairs.length; i++) { var pair = pairs[i]; @@ -4728,7 +4969,7 @@ var Sleeping = {}; var sleepingBody = (bodyA.isSleeping && !bodyA.isStatic) ? bodyA : bodyB, movingBody = sleepingBody === bodyA ? bodyB : bodyA; - if (!sleepingBody.isStatic && movingBody.motion > _motionWakeThreshold) { + if (!sleepingBody.isStatic && movingBody.motion > Sleeping._motionWakeThreshold * timeFactor) { Sleeping.set(sleepingBody, false); } } @@ -5167,7 +5408,7 @@ var Composites = {}; * @return {composite} A new composite car body */ Composites.car = function(xx, yy, width, height, wheelSize) { - var groupId = Body.nextGroupId(), + var group = Body.nextGroup(true), wheelBase = -20, wheelAOffset = -width * 0.5 + wheelBase, wheelBOffset = width * 0.5 - wheelBase, @@ -5175,7 +5416,9 @@ var Composites = {}; var car = Composite.create({ label: 'Car' }), body = Bodies.trapezoid(xx, yy, width, height, 0.3, { - groupId: groupId, + collisionFilter: { + group: group + }, friction: 0.01, chamfer: { radius: 10 @@ -5183,14 +5426,18 @@ var Composites = {}; }); var wheelA = Bodies.circle(xx + wheelAOffset, yy + wheelYOffset, wheelSize, { - groupId: groupId, + collisionFilter: { + group: group + }, restitution: 0.5, friction: 0.9, density: 0.01 }); var wheelB = Bodies.circle(xx + wheelBOffset, yy + wheelYOffset, wheelSize, { - groupId: groupId, + collisionFilter: { + group: group + }, restitution: 0.5, friction: 0.9, density: 0.01 @@ -5251,6 +5498,7 @@ var Composites = {}; })(); + ; // End src/factory/Composites.js @@ -6165,8 +6413,8 @@ var Render = {}; Render.constraints(constraints, context); - if (options.showBroadphase && engine.broadphase.current === 'grid') - Render.grid(engine, engine.broadphase[engine.broadphase.current].instance, context); + if (options.showBroadphase && engine.broadphase.controller === Grid) + Render.grid(engine, engine.broadphase, context); if (options.showDebug) Render.debug(engine, context); @@ -6731,7 +6979,6 @@ var Render = {}; */ Render.inspector = function(inspector, context) { var engine = inspector.engine, - mouse = engine.input.mouse, selected = inspector.selected, c = context, render = engine.render, @@ -6964,6 +7211,7 @@ var RenderPixi = {}; height: 600, background: '#fafafa', wireframeBackground: '#222', + hasBounds: false, enabled: true, wireframes: true, showSleeping: true, @@ -6980,12 +7228,25 @@ var RenderPixi = {}; } }; - var render = Common.extend(defaults, options); + var render = Common.extend(defaults, options), + transparent = !render.options.wireframes && render.options.background === 'transparent'; // init pixi - render.context = new PIXI.WebGLRenderer(800, 600, render.canvas, false, true); + render.context = new PIXI.WebGLRenderer(render.options.width, render.options.height, render.canvas, transparent, true); render.canvas = render.context.view; + render.container = new PIXI.DisplayObjectContainer(); render.stage = new PIXI.Stage(); + render.stage.addChild(render.container); + render.bounds = render.bounds || { + min: { + x: 0, + y: 0 + }, + max: { + x: render.options.width, + y: render.options.height + } + }; // caches render.textures = {}; @@ -6994,7 +7255,7 @@ var RenderPixi = {}; // use a sprite batch for performance render.spriteBatch = new PIXI.SpriteBatch(); - render.stage.addChild(render.spriteBatch); + render.container.addChild(render.spriteBatch); // insert canvas if (Common.isElement(render.element)) { @@ -7016,12 +7277,12 @@ var RenderPixi = {}; * @param {RenderPixi} render */ RenderPixi.clear = function(render) { - var stage = render.stage, + var container = render.container, spriteBatch = render.spriteBatch; - // clear stage - while (stage.children[0]) { - stage.removeChild(stage.children[0]); + // clear stage container + while (container.children[0]) { + container.removeChild(container.children[0]); } // clear sprite batch @@ -7041,11 +7302,15 @@ var RenderPixi = {}; if (bgSprite) spriteBatch.addChildAt(bgSprite, 0); - // add sprite batch back into stage - render.stage.addChild(render.spriteBatch); + // add sprite batch back into container + render.container.addChild(render.spriteBatch); // reset background state render.currentBackground = null; + + // reset bounds transforms + container.scale.set(1, 1); + container.position.set(0, 0); }; /** @@ -7093,9 +7358,11 @@ var RenderPixi = {}; world = engine.world, context = render.context, stage = render.stage, + container = render.container, options = render.options, bodies = Composite.allBodies(world), - constraints = Composite.allConstraints(world), + allConstraints = Composite.allConstraints(world), + constraints = [], i; if (options.wireframes) { @@ -7104,6 +7371,44 @@ var RenderPixi = {}; RenderPixi.setBackground(render, options.background); } + // handle bounds + var boundsWidth = render.bounds.max.x - render.bounds.min.x, + boundsHeight = render.bounds.max.y - render.bounds.min.y, + boundsScaleX = boundsWidth / render.options.width, + boundsScaleY = boundsHeight / render.options.height; + + if (options.hasBounds) { + // Hide bodies that are not in view + for (i = 0; i < bodies.length; i++) { + var body = bodies[i]; + body.render.sprite.visible = Bounds.overlaps(body.bounds, render.bounds); + } + + // filter out constraints that are not in view + for (i = 0; i < allConstraints.length; i++) { + var constraint = allConstraints[i], + bodyA = constraint.bodyA, + bodyB = constraint.bodyB, + pointAWorld = constraint.pointA, + pointBWorld = constraint.pointB; + + if (bodyA) pointAWorld = Vector.add(bodyA.position, constraint.pointA); + if (bodyB) pointBWorld = Vector.add(bodyB.position, constraint.pointB); + + if (!pointAWorld || !pointBWorld) + continue; + + if (Bounds.contains(render.bounds, pointAWorld) || Bounds.contains(render.bounds, pointBWorld)) + constraints.push(constraint); + } + + // transform the view + container.scale.set(1 / boundsScaleX, 1 / boundsScaleY); + container.position.set(-render.bounds.min.x * (1 / boundsScaleX), -render.bounds.min.y * (1 / boundsScaleY)); + } else { + constraints = allConstraints; + } + for (i = 0; i < bodies.length; i++) RenderPixi.body(engine, bodies[i]); @@ -7126,7 +7431,7 @@ var RenderPixi = {}; bodyB = constraint.bodyB, pointA = constraint.pointA, pointB = constraint.pointB, - stage = render.stage, + container = render.container, constraintRender = constraint.render, primitiveId = 'c-' + constraint.id, primitive = render.primitives[primitiveId]; @@ -7142,8 +7447,8 @@ var RenderPixi = {}; } // add to scene graph if not already there - if (stage.children.indexOf(primitive) === -1) - stage.addChild(primitive); + if (Common.indexOf(container.children, primitive) === -1) + container.addChild(primitive); // render the constraint on every update, since they can change dynamically primitive.clear(); @@ -7188,7 +7493,7 @@ var RenderPixi = {}; sprite = render.sprites[spriteId] = _createBodySprite(render, body); // add to scene graph if not already there - if (spriteBatch.children.indexOf(sprite) === -1) + if (Common.indexOf(spriteBatch.children, sprite) === -1) spriteBatch.addChild(sprite); // update body sprite @@ -7198,7 +7503,7 @@ var RenderPixi = {}; } else { var primitiveId = 'b-' + body.id, primitive = render.primitives[primitiveId], - stage = render.stage; + container = render.container; // initialise body primitive if not existing if (!primitive) { @@ -7207,8 +7512,8 @@ var RenderPixi = {}; } // add to scene graph if not already there - if (stage.children.indexOf(primitive) === -1) - stage.addChild(primitive); + if (Common.indexOf(container.children, primitive) === -1) + container.addChild(primitive); // update body primitive primitive.position.x = body.position.x; @@ -7321,6 +7626,8 @@ World.addBody = Composite.addBody; World.addConstraint = Composite.addConstraint; World.clear = Composite.clear; +Engine.run = Runner.run; + // exports Matter.Body = Body; @@ -7350,6 +7657,7 @@ Matter.Render = Render; Matter.RenderPixi = RenderPixi; Matter.Events = Events; Matter.Query = Query; +Matter.Runner = Runner; // CommonJS module if (typeof exports !== 'undefined') { diff --git a/build/matter.min.js b/build/matter.min.js index 1eb2b1c..5233bc5 100644 --- a/build/matter.min.js +++ b/build/matter.min.js @@ -1,8 +1,8 @@ /** -* matter.min.js 0.8.0-edge 2014-06-09 +* matter.min.js 0.8.0-edge 2014-07-29 * http://brm.io/matter-js/ * License: MIT */ -!function(){var a={},b={};!function(){b._inertiaScale=4;var a=1;b.create=function(a){var b={id:o.nextId(),type:"body",label:"Body",angle:0,vertices:z.fromPath("L 0 0 L 40 0 L 40 40 L 0 40"),position:{x:0,y:0},force:{x:0,y:0},torque:0,positionImpulse:{x:0,y:0},constraintImpulse:{x:0,y:0,angle:0},speed:0,angularSpeed:0,velocity:{x:0,y:0},angularVelocity:0,isStatic:!1,isSleeping:!1,motion:0,sleepThreshold:60,density:.001,restitution:0,friction:.1,frictionAir:.01,groupId:0,slop:.05,timeScale:1,render:{visible:!0,sprite:{xScale:1,yScale:1},lineWidth:1.5}},d=o.extend(b,a);return c(d,a),d},b.nextGroupId=function(){return a++};var c=function(a,c){a.bounds=a.bounds||x.create(a.vertices),a.positionPrev=a.positionPrev||y.clone(a.position),a.anglePrev=a.anglePrev||a.angle,b.setVertices(a,a.vertices),b.setStatic(a,a.isStatic),t.set(a,a.isSleeping),z.rotate(a.vertices,a.angle,a.position),w.rotate(a.axes,a.angle),a.axes=c.axes||a.axes,a.area=c.area||a.area,a.mass=c.mass||a.mass,a.inertia=c.inertia||a.inertia,a.inverseMass=1/a.mass,a.inverseInertia=1/a.inertia;var d=a.isStatic?"#eeeeee":o.choose(["#556270","#4ECDC4","#C7F464","#FF6B6B","#C44D58"]),e=o.shadeColor(d,-20);a.render.fillStyle=a.render.fillStyle||d,a.render.strokeStyle=a.render.strokeStyle||e};b.setStatic=function(a,b){a.isStatic=b,b&&(a.restitution=0,a.friction=1,a.mass=a.inertia=a.density=1/0,a.inverseMass=a.inverseInertia=0,a.render.lineWidth=1,a.positionPrev.x=a.position.x,a.positionPrev.y=a.position.y,a.anglePrev=a.angle,a.angularVelocity=0,a.speed=0,a.angularSpeed=0,a.motion=0)},b.setVertices=function(a,c){a.vertices=c[0].body===a?c:z.create(c,a),a.axes=w.fromVertices(a.vertices),a.area=z.area(a.vertices),a.mass=a.density*a.area,a.inverseMass=1/a.mass;var d=z.centre(a.vertices);z.translate(a.vertices,d,-1),a.inertia=b._inertiaScale*z.inertia(a.vertices,a.mass),a.inverseInertia=1/a.inertia,z.translate(a.vertices,a.position),x.update(a.bounds,a.vertices,a.velocity)},b.setPosition=function(a,b){var c=y.sub(b,a.position);a.position.x=b.x,a.position.y=b.y,a.positionPrev.x+=c.x,a.positionPrev.y+=c.y,z.translate(a.vertices,c),x.update(a.bounds,a.vertices,a.velocity)},b.setAngle=function(a,b){var c=b-a.angle;a.angle=b,a.anglePrev+=c,z.rotate(a.vertices,c,a.position),w.rotate(a.axes,c),x.update(a.bounds,a.vertices,a.velocity)},b.setVelocity=function(a,b){a.positionPrev.x=a.position.x-b.x,a.positionPrev.y=a.position.y-b.y,a.velocity.x=b.x,a.velocity.y=b.y,a.speed=y.magnitude(a.velocity)},b.setAngularVelocity=function(a,b){a.anglePrev=a.angle-b,a.angularVelocity=b,a.angularSpeed=Math.abs(a.angularVelocity)},b.translate=function(a,c){b.setPosition(a,y.add(a.position,c))},b.rotate=function(a){b.setAngle(a,a.angle+angle)},b.scale=function(a,b,c,d){z.scale(a.vertices,b,c,d),a.axes=w.fromVertices(a.vertices),a.area=z.area(a.vertices),a.mass=a.density*a.area,a.inverseMass=1/a.mass,z.translate(a.vertices,{x:-a.position.x,y:-a.position.y}),a.inertia=z.inertia(a.vertices,a.mass),a.inverseInertia=1/a.inertia,z.translate(a.vertices,{x:a.position.x,y:a.position.y}),x.update(a.bounds,a.vertices,a.velocity)},b.resetForcesAll=function(a){for(var b=0;bf.max.x||h.bounds.max.yf.max.y||b.update(h,c,d,e)}},b.update=function(a,b,c,d){var e=Math.pow(b*c*a.timeScale,2),f=1-a.frictionAir*c*a.timeScale,g=a.position.x-a.positionPrev.x,h=a.position.y-a.positionPrev.y;a.velocity.x=g*f*d+a.force.x/a.mass*e,a.velocity.y=h*f*d+a.force.y/a.mass*e,a.positionPrev.x=a.position.x,a.positionPrev.y=a.position.y,a.position.x+=a.velocity.x,a.position.y+=a.velocity.y,a.angularVelocity=(a.angle-a.anglePrev)*f*d+a.torque/a.inertia*e,a.anglePrev=a.angle,a.angle+=a.angularVelocity,a.speed=y.magnitude(a.velocity),a.angularSpeed=Math.abs(a.angularVelocity),z.translate(a.vertices,a.velocity),0!==a.angularVelocity&&(z.rotate(a.vertices,a.angularVelocity,a.position),w.rotate(a.axes,a.angularVelocity)),x.update(a.bounds,a.vertices,a.velocity)},b.applyForce=function(a,b,c){a.force.x+=c.x,a.force.y+=c.y;var d={x:b.x-a.position.x,y:b.y-a.position.y};a.torque+=(d.x*c.y-d.y*c.x)*a.inverseInertia}}();var c={};!function(){c.create=function(a){return o.extend({id:o.nextId(),type:"composite",parent:null,isModified:!1,bodies:[],constraints:[],composites:[],label:"Composite"},a)},c.setModified=function(a,b,d,e){if(a.isModified=b,d&&a.parent&&c.setModified(a.parent,b,d,e),e)for(var f=0;fq.bounds.width||u.bounds.max.y<0||u.bounds.min.y>q.bounds.height)){var v=b(c,u);if(!u.region||v.id!==u.region.id||k){s.broadphaseTests+=1,(!u.region||k)&&(u.region=v);var w=a(v,u.region);for(m=w.startCol;m<=w.endCol;m++)for(n=w.startRow;n<=w.endRow;n++){p=d(m,n),o=r[p];var x=m>=v.startCol&&m<=v.endCol&&n>=v.startRow&&n<=v.endRow,y=m>=u.region.startCol&&m<=u.region.endCol&&n>=u.region.startRow&&n<=u.region.endRow;!x&&y&&y&&o&&i(c,o,u),(u.region===v||x&&!y||k)&&(o||(o=e(r,p)),f(c,o,u))}u.region=v,t=!0}}}t&&(c.pairsList=j(c))},g.clear=function(a){a.buckets={},a.pairs={},a.pairsList=[]};var a=function(a,b){var d=Math.min(a.startCol,b.startCol),e=Math.max(a.endCol,b.endCol),f=Math.min(a.startRow,b.startRow),g=Math.max(a.endRow,b.endRow);return c(d,e,f,g)},b=function(a,b){var d=b.bounds,e=Math.floor(d.min.x/a.bucketWidth),f=Math.floor(d.max.x/a.bucketWidth),g=Math.floor(d.min.y/a.bucketHeight),h=Math.floor(d.max.y/a.bucketHeight);return c(e,f,g,h)},c=function(a,b,c,d){return{id:a+","+b+","+c+","+d,startCol:a,endCol:b,startRow:c,endRow:d}},d=function(a,b){return a+","+b},e=function(a,b){var c=a[b]=[];return c},f=function(a,b,c){for(var d=0;d0?d.push(c):delete a.pairs[b[e]];return d}}();var h={};!function(){h.create=function(a,b){var c=a.bodyA,d=a.bodyB,e={id:h.id(c,d),bodyA:c,bodyB:d,contacts:{},activeContacts:[],separation:0,isActive:!0,timeCreated:b,timeUpdated:b,inverseMass:c.inverseMass+d.inverseMass,friction:Math.min(c.friction,d.friction),restitution:Math.max(c.restitution,d.restitution),slop:Math.max(c.slop,d.slop)};return h.update(e,a,b),e},h.update=function(a,b,c){var d=a.contacts,f=b.supports,g=a.activeContacts;if(a.collision=b,g.length=0,b.collided){for(var i=0;ia&&j.push(g);for(g=0;gD*g.friction*e&&(E=D*g.friction*e*B);var F=y.cross(s,k),G=y.cross(t,k),H=n/(g.inverseMass+i.inverseInertia*F*F+j.inverseInertia*G*G);if(C*=H,E*=H,0>x&&x*x>a*e)q.normalImpulse=0,q.tangentImpulse=0;else{var I=q.normalImpulse;q.normalImpulse=Math.min(q.normalImpulse+C,0),C=q.normalImpulse-I;var J=q.tangentImpulse;q.tangentImpulse=o.clamp(q.tangentImpulse+E,-A,A),E=q.tangentImpulse-J}d.x=k.x*C+l.x*E,d.y=k.y*C+l.y*E,i.isStatic||i.isSleeping||(i.positionPrev.x+=d.x*i.inverseMass,i.positionPrev.y+=d.y*i.inverseMass,i.anglePrev+=y.cross(s,d)*i.inverseInertia),j.isStatic||j.isSleeping||(j.positionPrev.x-=d.x*j.inverseMass,j.positionPrev.y-=d.y*j.inverseMass,j.anglePrev-=y.cross(t,d)*j.inverseInertia)}}}}}();var l={};!function(){l.collides=function(b,d,e){var f,g,h,i,j=e,k=!1;if(j){var l=b.speed*b.speed+b.angularSpeed*b.angularSpeed+d.speed*d.speed+d.angularSpeed*d.angularSpeed;k=j&&j.collided&&.2>l,i=j}else i={collided:!1,bodyA:b,bodyB:d};if(j&&k){var m=[j.bodyA.axes[j.axisNumber]];if(h=a(j.bodyA.vertices,j.bodyB.vertices,m),i.reused=!0,h.overlap<=0)return i.collided=!1,i}else{if(f=a(b.vertices,d.vertices,b.axes),f.overlap<=0)return i.collided=!1,i;if(g=a(d.vertices,b.vertices,d.axes),g.overlap<=0)return i.collided=!1,i;f.overlap0&&(i.normal=y.neg(i.normal)),i.tangent=y.perp(i.normal),i.penetration={x:i.normal.x*i.depth,y:i.normal.y*i.depth};var n=c(b,d,i.normal),o=[n[0]];if(z.contains(b.vertices,n[1]))o.push(n[1]);else{var p=c(d,b,y.neg(i.normal));z.contains(d.vertices,p[0])&&o.push(p[0]),o.length<2&&z.contains(d.vertices,p[1])&&o.push(p[1])}return i.supports=o,i.supportCorrected=y.sub(n[0],i.penetration),i};var a=function(a,c,d){for(var e,f,g={},h={},i={overlap:Number.MAX_VALUE},j=0;j=e)return i.overlap=e,i;ee?e=g:d>g&&(d=g)}a.min=d,a.max=e},c=function(a,b,c){for(var d,e,f=Number.MAX_VALUE,g={x:0,y:0},h=b.vertices,i=a.position,j=h[0],k=h[1],l=0;ld&&(f=d,j=e);var m=j.index-1>=0?j.index-1:h.length-1;e=h[m],g.x=e.x-i.x,g.y=e.y-i.y,f=-y.dot(c,g),k=e;var n=(j.index+1)%h.length;return e=h[n],g.x=e.x-i.x,g.y=e.y-i.y,d=-y.dot(c,g),f>d&&(f=d,k=e),[j,k]}}();var m={};!function(){var a=1e-6,b=.001;m.create=function(b){var c=b;c.bodyA&&!c.pointA&&(c.pointA={x:0,y:0}),c.bodyB&&!c.pointB&&(c.pointB={x:0,y:0});var d=c.bodyA?y.add(c.bodyA.position,c.pointA):c.pointA,e=c.bodyB?y.add(c.bodyB.position,c.pointB):c.pointB,f=y.magnitude(y.sub(d,e));c.length=c.length||f||a;var g={visible:!0,lineWidth:2,strokeStyle:"#666"};return c.render=o.extend(g,c.render),c.id=c.id||o.nextId(),c.label=c.label||"Constraint",c.type="constraint",c.stiffness=c.stiffness||1,c.angularStiffness=c.angularStiffness||0,c.angleA=c.bodyA?c.bodyA.angle:c.angleA,c.angleB=c.bodyB?c.bodyB.angle:c.angleB,c},m.solveAll=function(a,b){for(var c=0;c0&&(B=0);var C,D={x:n.x*B,y:n.y*B};e&&!e.isStatic&&(C=y.cross(s,D)*e.inverseInertia*(1-c.angularStiffness),t.set(e,!1),C=o.clamp(C,-.01,.01),e.constraintImpulse.x-=p.x,e.constraintImpulse.y-=p.y,e.constraintImpulse.angle+=C,e.position.x-=p.x,e.position.y-=p.y,e.angle+=C),f&&!f.isStatic&&(C=y.cross(u,D)*f.inverseInertia*(1-c.angularStiffness),t.set(f,!1),C=o.clamp(C,-.01,.01),f.constraintImpulse.x+=p.x,f.constraintImpulse.y+=p.y,f.constraintImpulse.angle-=C,f.position.x+=p.x,f.position.y+=p.y,f.angle-=C)}}},m.postSolveAll=function(a){for(var b=0;b>16)+d,f=(c>>8&255)+d,g=(255&c)+d;return"#"+(16777216+65536*(255>e?1>e?0:e:255)+256*(255>f?1>f?0:f:255)+(255>g?1>g?0:g:255)).toString(16).slice(1)},o.shuffle=function(a){for(var b=a.length-1;b>0;b--){var c=Math.floor(o.random()*(b+1)),d=a[b];a[b]=a[c],a[c]=d}return a},o.choose=function(a){return a[Math.floor(o.random()*a.length)]},o.isElement=function(a){try{return a instanceof HTMLElement}catch(b){return"object"==typeof a&&1===a.nodeType&&"object"==typeof a.style&&"object"==typeof a.ownerDocument}},o.clamp=function(a,b,c){return b>a?b:a>c?c:a},o.sign=function(a){return 0>a?-1:1},o.now=function(){var a=window.performance;return a?(a.now=a.now||a.webkitNow||a.msNow||a.oNow||a.mozNow,+a.now()):+new Date},o.random=function(b,c){return b="undefined"!=typeof b?b:0,c="undefined"!=typeof c?c:1,b+a()*(c-b)},o.colorToNumber=function(a){return a=a.replace("#",""),3==a.length&&(a=a.charAt(0)+a.charAt(0)+a.charAt(1)+a.charAt(1)+a.charAt(2)+a.charAt(2)),parseInt(a,16)},o.log=function(a,b){if(console&&console.log){var c;switch(b){case"warn":c="color: coral";break;case"error":c="color: red"}console.log("%c [Matter] "+b+": "+a,c)}},o.nextId=function(){return o._nextId++};var a=function(){return o._seed=(9301*o._seed+49297)%233280,o._seed/233280}}();var p={};!function(){var a=60,e=a,h=1e3/a,j=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(function(){a(o.now())},h)};p.create=function(b,c){c=o.isElement(b)?c:b,b=o.isElement(b)?b:null;var e={enabled:!0,positionIterations:6,velocityIterations:4,constraintIterations:2,enableSleeping:!1,timeScale:1,input:{},events:[],timing:{fps:a,timestamp:0,delta:h,correction:1,deltaMin:1e3/a,deltaMax:1e3/(.5*a),timeScale:1,isFixed:!1},render:{element:b,controller:A}},j=o.extend(e,c);return j.render=j.render.controller.create(j.render),j.world=d.create(j.world),j.pairs=i.create(),j.metrics=j.metrics||r.create(),j.input.mouse=j.input.mouse||s.create(j.render.canvas),j.broadphase=j.broadphase||{current:"grid",grid:{controller:g,instance:g.create(),detector:f.collisions},bruteForce:{detector:f.bruteForce}},j},p.run=function(a){var b,c=0,d=0,f=[],g=1;!function h(i){if(j(h),a.enabled){var k,m,o=a.timing,r={timestamp:i};q.trigger(a,"beforeTick",r),o.isFixed?k=o.delta:(k=i-b||o.delta,b=i,f.push(k),f=f.slice(-e),k=Math.min.apply(null,f),k=ko.deltaMax?o.deltaMax:k,m=k/o.delta,o.delta=k),0!==g&&(m*=o.timeScale/g),0===o.timeScale&&(m=0),g=o.timeScale,d+=1,i-c>=1e3&&(o.fps=d*((i-c)/1e3),c=i,d=0),q.trigger(a,"tick",r),a.world.isModified&&a.render.controller.clear(a.render),p.update(a,k,m),n(a),l(a),p.render(a),q.trigger(a,"afterTick",r)}}()},p.update=function(a,d,e){e="undefined"!=typeof e?e:1;var f,g=a.world,h=a.timing,j=a.broadphase[a.broadphase.current],l=[];h.timestamp+=d*h.timeScale,h.correction=e;var n={timestamp:a.timing.timestamp};q.trigger(a,"beforeUpdate",n);var o=c.allBodies(g),p=c.allConstraints(g);for(r.reset(a.metrics),a.enableSleeping&&t.update(o),b.applyGravityAll(o,g.gravity),b.updateAll(o,d,h.timeScale,e,g.bounds),f=0;f0&&q.trigger(a,"collisionStart",{pairs:b.collisionStart}),b.collisionActive.length>0&&q.trigger(a,"collisionActive",{pairs:b.collisionActive}),b.collisionEnd.length>0&&q.trigger(a,"collisionEnd",{pairs:b.collisionEnd})}}();var q={};!function(){q.on=function(a,b,c){for(var d,e=b.split(" "),f=0;f0||e.force.y>0)t.set(e,!1);else{var g=Math.min(e.motion,f),h=Math.max(e.motion,f);e.motion=c*g+(1-c)*h,e.sleepThreshold>0&&e.motion=e.sleepThreshold&&t.set(e,!0)):e.sleepCounter>0&&(e.sleepCounter-=1)}}},t.afterCollisions=function(b){for(var c=0;ca&&t.set(h,!1)}}}},t.set=function(a,b){b?(a.isSleeping=!0,a.sleepCounter=a.sleepThreshold,a.positionImpulse.x=0,a.positionImpulse.y=0,a.positionPrev.x=a.position.x,a.positionPrev.y=a.position.y,a.anglePrev=a.angle,a.speed=0,a.angularSpeed=0,a.motion=0):(a.isSleeping=!1,a.sleepCounter=0)}}();var u={};!function(){u.rectangle=function(a,c,d,e,f){f=f||{};var g={label:"Rectangle Body",position:{x:a,y:c},vertices:z.fromPath("L 0 0 L "+d+" 0 L "+d+" "+e+" L 0 "+e)};if(f.chamfer){var h=f.chamfer;g.vertices=z.chamfer(g.vertices,h.radius,h.quality,h.qualityMin,h.qualityMax),delete f.chamfer}return b.create(o.extend({},g,f))},u.trapezoid=function(a,c,d,e,f,g){g=g||{},f*=.5;var h=(1-2*f)*d,i=d*f,j=i+h,k=j+i,l={label:"Trapezoid Body",position:{x:a,y:c},vertices:z.fromPath("L 0 0 L "+i+" "+-e+" L "+j+" "+-e+" L "+k+" 0")};if(g.chamfer){var m=g.chamfer;l.vertices=z.chamfer(l.vertices,m.radius,m.quality,m.qualityMin,m.qualityMax),delete g.chamfer}return b.create(o.extend({},l,g))},u.circle=function(a,b,c,d,e){d=d||{},d.label="Circle Body",e=e||25;var f=Math.ceil(Math.max(10,Math.min(e,c)));return f%2===1&&(f+=1),d.circleRadius=c,u.polygon(a,b,f,c,d)},u.polygon=function(a,c,d,e,f){if(f=f||{},3>d)return u.circle(a,c,e,f);for(var g=2*Math.PI/d,h="",i=.5*g,j=0;d>j;j+=1){var k=i+j*g,l=Math.cos(k)*e,m=Math.sin(k)*e;h+="L "+l.toFixed(3)+" "+m.toFixed(3)+" "}var n={label:"Polygon Body",position:{x:a,y:c},vertices:z.fromPath(h)};if(f.chamfer){var p=f.chamfer;n.vertices=z.chamfer(n.vertices,p.radius,p.quality,p.qualityMin,p.qualityMax),delete f.chamfer}return b.create(o.extend({},n,f))}}();var v={};!function(){v.stack=function(a,d,e,f,g,h,i){for(var j,k=c.create({label:"Stack"}),l=a,m=d,n=0,o=0;f>o;o++){for(var p=0,q=0;e>q;q++){var r=i(l,m,q,o,j,n);if(r){var s=r.bounds.max.y-r.bounds.min.y,t=r.bounds.max.x-r.bounds.min.x;s>p&&(p=s),b.translate(r,{x:.5*t,y:.5*s}),l=r.bounds.max.x+g,c.addBody(k,r),j=r,n+=1}}m+=p+h,l=a}return k},v.chain=function(a,b,d,e,f,g){for(var h=a.bodies,i=1;ig;g++){for(h=0;b>h;h++)h>0&&(i=l[h-1+g*b],j=l[h+g*b],c.addConstraint(a,m.create(o.extend({bodyA:i,bodyB:j},f))));for(h=0;b>h;h++)g>0&&(i=l[h+(g-1)*b],j=l[h+g*b],c.addConstraint(a,m.create(o.extend({bodyA:i,bodyB:j},f))),e&&h>0&&(k=l[h-1+(g-1)*b],c.addConstraint(a,m.create(o.extend({bodyA:k,bodyB:j},f)))),e&&b-1>h&&(k=l[h+1+(g-1)*b],c.addConstraint(a,m.create(o.extend({bodyA:k,bodyB:j},f)))))}return a.label+=" Mesh",a},v.pyramid=function(a,c,d,e,f,g,h){return v.stack(a,c,d,e,f,g,function(c,g,i,j,k,l){var m=Math.min(e,Math.ceil(d/2)),n=k?k.bounds.max.x-k.bounds.min.x:0;if(!(j>m)){j=m-j;var o=j,p=d-1-j;if(!(o>i||i>p)){1===l&&b.translate(k,{x:(i+(d%2===1?1:-1))*n,y:0});var q=k?i*n:0;return h(a+q+i*f,g,i,j,k,l)}}})},v.newtonsCradle=function(a,b,d,e,f){for(var g=c.create({label:"Newtons Cradle"}),h=0;d>h;h++){var i=1.9,j=u.circle(a+h*e*i,b+f,e,{inertia:99999,restitution:1,friction:0,frictionAir:1e-4,slop:.01}),k=m.create({pointA:{x:a+h*e*i,y:b},bodyB:j});c.addBody(g,j),c.addConstraint(g,k)}return g},v.car=function(a,d,e,f,g){var h=b.nextGroupId(),i=-20,j=.5*-e+i,k=.5*e-i,l=0,n=c.create({label:"Car"}),o=u.trapezoid(a,d,e,f,.3,{groupId:h,friction:.01,chamfer:{radius:10}}),p=u.circle(a+j,d+l,g,{groupId:h,restitution:.5,friction:.9,density:.01}),q=u.circle(a+k,d+l,g,{groupId:h,restitution:.5,friction:.9,density:.01}),r=m.create({bodyA:o,pointA:{x:j,y:l},bodyB:p,stiffness:.5}),s=m.create({bodyA:o,pointA:{x:k,y:l},bodyB:q,stiffness:.5});return c.addBody(n,o),c.addBody(n,p),c.addBody(n,q),c.addConstraint(n,r),c.addConstraint(n,s),n},v.softBody=function(a,b,c,d,e,f,g,h,i,j){i=o.extend({inertia:1/0},i),j=o.extend({stiffness:.4},j);var k=v.stack(a,b,c,d,e,f,function(a,b){return u.circle(a,b,h,i)});return v.mesh(k,c,d,g,j),k.label="Soft Body",k}}();var w={};!function(){w.fromVertices=function(a){for(var b={},c=0;ca.max.x&&(a.max.x=e.x),e.xa.max.y&&(a.max.y=e.y),e.y0?a.max.x+=c.x:a.min.x+=c.x,c.y>0?a.max.y+=c.y:a.min.y+=c.y)},x.contains=function(a,b){return b.x>=a.min.x&&b.x<=a.max.x&&b.y>=a.min.y&&b.y<=a.max.y},x.overlaps=function(a,b){return a.min.x<=b.max.x&&a.max.x>=b.min.x&&a.max.y>=b.min.y&&a.min.y<=b.max.y},x.translate=function(a,b){a.min.x+=b.x,a.max.x+=b.x,a.min.y+=b.y,a.max.y+=b.y},x.shift=function(a,b){var c=a.max.x-a.min.x,d=a.max.y-a.min.y;a.min.x=b.x,a.max.x=b.x+c,a.min.y=b.y,a.max.y=b.y+d}}();var y={};!function(){y.clone=function(a){return{x:a.x,y:a.y}},y.magnitude=function(a){return Math.sqrt(a.x*a.x+a.y*a.y)},y.magnitudeSquared=function(a){return a.x*a.x+a.y*a.y},y.rotate=function(a,b){var c=Math.cos(b),d=Math.sin(b);return{x:a.x*c-a.y*d,y:a.x*d+a.y*c}},y.rotateAbout=function(a,b,c){var d=Math.cos(b),e=Math.sin(b);return{x:c.x+((a.x-c.x)*d-(a.y-c.y)*e),y:c.y+((a.x-c.x)*e+(a.y-c.y)*d)}},y.normalise=function(a){var b=y.magnitude(a);return 0===b?{x:0,y:0}:{x:a.x/b,y:a.y/b}},y.dot=function(a,b){return a.x*b.x+a.y*b.y},y.cross=function(a,b){return a.x*b.y-a.y*b.x},y.add=function(a,b){return{x:a.x+b.x,y:a.y+b.y}},y.sub=function(a,b){return{x:a.x-b.x,y:a.y-b.y}},y.mult=function(a,b){return{x:a.x*b,y:a.y*b}},y.div=function(a,b){return{x:a.x/b,y:a.y/b}},y.perp=function(a,b){return b=b===!0?-1:1,{x:b*-a.y,y:b*a.x}},y.neg=function(a){return{x:-a.x,y:-a.y}},y.angle=function(a,b){return Math.atan2(b.y-a.y,b.x-a.x)}}();var z={};!function(){z.create=function(a,b){for(var c=[],d=0;d0)return!1}return!0},z.scale=function(a,b,c,d){if(1===b&&1===c)return a;d=d||z.centre(a);for(var e,f,g=0;g=0?g-1:a.length-1],i=a[g],j=a[(g+1)%a.length],k=b[gv;v++)f.push(y.add(y.rotate(p,u*v),r))}else f.push(i)}return f}}();var A={};!function(){A.create=function(b){var c={controller:A,element:null,canvas:null,options:{width:800,height:600,background:"#fafafa",wireframeBackground:"#222",hasBounds:!1,enabled:!0,wireframes:!0,showSleeping:!0,showDebug:!1,showBroadphase:!1,showBounds:!1,showVelocity:!1,showCollisions:!1,showAxes:!1,showPositions:!1,showAngleIndicator:!1,showIds:!1,showShadows:!1}},d=o.extend(c,b);return d.canvas=d.canvas||a(d.options.width,d.options.height),d.context=d.canvas.getContext("2d"),d.textures={},d.bounds=d.bounds||{min:{x:0,y:0},max:{x:d.options.width,y:d.options.height}},A.setBackground(d,d.options.background),o.isElement(d.element)?d.element.appendChild(d.canvas):o.log('No "render.element" passed, "render.canvas" was not inserted into document.',"warn"),d},A.clear=function(){},A.setBackground=function(a,b){if(a.currentBackground!==b){var c=b;/(jpg|gif|png)$/.test(b)&&(c="url("+b+")"),a.canvas.style.background=c,a.canvas.style.backgroundSize="contain",a.currentBackground=b}},A.world=function(a){var b,d=a.render,e=a.world,f=d.canvas,g=d.context,h=d.options,i=c.allBodies(e),j=c.allConstraints(e),k=[],l=[];h.wireframes?A.setBackground(d,h.wireframeBackground):A.setBackground(d,h.background),g.globalCompositeOperation="source-in",g.fillStyle="transparent",g.fillRect(0,0,f.width,f.height),g.globalCompositeOperation="source-over";var m=d.bounds.max.x-d.bounds.min.x,n=d.bounds.max.y-d.bounds.min.y,o=m/d.options.width,p=n/d.options.height;if(h.hasBounds){for(b=0;b=500){var k="";k+="fps: "+Math.round(a.timing.fps)+j,a.metrics.extended&&(k+="delta: "+a.timing.delta.toFixed(3)+j,k+="correction: "+a.timing.correction.toFixed(3)+j,k+="bodies: "+i.length+j,a.broadphase.controller===g&&(k+="buckets: "+a.metrics.buckets+j),k+="\n",k+="collisions: "+a.metrics.collisions+j,k+="pairs: "+a.pairs.list.length+j,k+="broad: "+a.metrics.broadEff+j,k+="mid: "+a.metrics.midEff+j,k+="narrow: "+a.metrics.narrowEff+j),f.debugString=k,f.debugTimestamp=a.timing.timestamp}if(f.debugString){d.font="12px Arial",d.fillStyle=h.wireframes?"rgba(255,255,255,0.5)":"rgba(0,0,0,0.5)";for(var l=f.debugString.split("\n"),m=0;m0){var l=d.activeContacts[0].vertex.x,m=d.activeContacts[0].vertex.y;2===d.activeContacts.length&&(l=(d.activeContacts[0].vertex.x+d.activeContacts[1].vertex.x)/2,m=(d.activeContacts[0].vertex.y+d.activeContacts[1].vertex.y)/2),h.moveTo(l-8*e.normal.x,m-8*e.normal.y),h.lineTo(l,m)}h.strokeStyle=i.wireframes?"rgba(255,165,0,0.7)":"orange",h.lineWidth=1,h.stroke()},A.grid=function(a,b,c){var d=c,e=a.render.options;d.strokeStyle=e.wireframes?"rgba(255,180,0,0.1)":"rgba(255,180,0,0.5)",d.beginPath();for(var f=o.keys(b.buckets),g=0;gf.max.x||h.bounds.max.yf.max.y||b.update(h,c,d,e)}},b.update=function(a,b,c,d){var e=Math.pow(b*c*a.timeScale,2),f=1-a.frictionAir*c*a.timeScale,g=a.position.x-a.positionPrev.x,h=a.position.y-a.positionPrev.y;a.velocity.x=g*f*d+a.force.x/a.mass*e,a.velocity.y=h*f*d+a.force.y/a.mass*e,a.positionPrev.x=a.position.x,a.positionPrev.y=a.position.y,a.position.x+=a.velocity.x,a.position.y+=a.velocity.y,a.angularVelocity=(a.angle-a.anglePrev)*f*d+a.torque/a.inertia*e,a.anglePrev=a.angle,a.angle+=a.angularVelocity,a.speed=z.magnitude(a.velocity),a.angularSpeed=Math.abs(a.angularVelocity),A.translate(a.vertices,a.velocity),0!==a.angularVelocity&&(A.rotate(a.vertices,a.angularVelocity,a.position),x.rotate(a.axes,a.angularVelocity)),y.update(a.bounds,a.vertices,a.velocity)},b.applyForce=function(a,b,c){a.force.x+=c.x,a.force.y+=c.y;var d={x:b.x-a.position.x,y:b.y-a.position.y};a.torque+=(d.x*c.y-d.y*c.x)*a.inverseInertia}}();var c={};!function(){c.create=function(a){return o.extend({id:o.nextId(),type:"composite",parent:null,isModified:!1,bodies:[],constraints:[],composites:[],label:"Composite"},a)},c.setModified=function(a,b,d,e){if(a.isModified=b,d&&a.parent&&c.setModified(a.parent,b,d,e),e)for(var f=0;f0:0!==(a.mask&b.category)&&0!==(b.mask&a.category)}}();var g={};!function(){g.create=function(a){var b={controller:g,detector:f.collisions,buckets:{},pairs:{},pairsList:[],bucketWidth:48,bucketHeight:48};return o.extend(b,a)},g.update=function(c,f,g,h){var l,m,n,o,p,q=g.world,r=c.buckets,s=g.metrics,t=!1;for(s.broadphaseTests=0,l=0;lq.bounds.width||u.bounds.max.y<0||u.bounds.min.y>q.bounds.height)){var v=b(c,u);if(!u.region||v.id!==u.region.id||h){s.broadphaseTests+=1,(!u.region||h)&&(u.region=v);var w=a(v,u.region);for(m=w.startCol;m<=w.endCol;m++)for(n=w.startRow;n<=w.endRow;n++){p=d(m,n),o=r[p];var x=m>=v.startCol&&m<=v.endCol&&n>=v.startRow&&n<=v.endRow,y=m>=u.region.startCol&&m<=u.region.endCol&&n>=u.region.startRow&&n<=u.region.endRow;!x&&y&&y&&o&&j(c,o,u),(u.region===v||x&&!y||h)&&(o||(o=e(r,p)),i(c,o,u))}u.region=v,t=!0}}}t&&(c.pairsList=k(c))},g.clear=function(a){a.buckets={},a.pairs={},a.pairsList=[]};var a=function(a,b){var d=Math.min(a.startCol,b.startCol),e=Math.max(a.endCol,b.endCol),f=Math.min(a.startRow,b.startRow),g=Math.max(a.endRow,b.endRow);return c(d,e,f,g)},b=function(a,b){var d=b.bounds,e=Math.floor(d.min.x/a.bucketWidth),f=Math.floor(d.max.x/a.bucketWidth),g=Math.floor(d.min.y/a.bucketHeight),h=Math.floor(d.max.y/a.bucketHeight);return c(e,f,g,h)},c=function(a,b,c,d){return{id:a+","+b+","+c+","+d,startCol:a,endCol:b,startRow:c,endRow:d}},d=function(a,b){return a+","+b},e=function(a,b){var c=a[b]=[];return c},i=function(a,b,c){for(var d=0;d0?d.push(c):delete a.pairs[b[e]];return d}}();var h={};!function(){h.create=function(a,b){var c=a.bodyA,d=a.bodyB,e={id:h.id(c,d),bodyA:c,bodyB:d,contacts:{},activeContacts:[],separation:0,isActive:!0,timeCreated:b,timeUpdated:b,inverseMass:c.inverseMass+d.inverseMass,friction:Math.min(c.friction,d.friction),restitution:Math.max(c.restitution,d.restitution),slop:Math.max(c.slop,d.slop)};return h.update(e,a,b),e},h.update=function(a,b,c){var d=a.contacts,f=b.supports,g=a.activeContacts;if(a.collision=b,g.length=0,b.collided){for(var i=0;ia&&j.push(g);for(g=0;gD*g.friction*e&&(E=D*g.friction*e*B);var F=z.cross(s,k),G=z.cross(t,k),H=n/(g.inverseMass+i.inverseInertia*F*F+j.inverseInertia*G*G);if(C*=H,E*=H,0>x&&x*x>a*e)q.normalImpulse=0,q.tangentImpulse=0;else{var I=q.normalImpulse;q.normalImpulse=Math.min(q.normalImpulse+C,0),C=q.normalImpulse-I;var J=q.tangentImpulse;q.tangentImpulse=o.clamp(q.tangentImpulse+E,-A,A),E=q.tangentImpulse-J}d.x=k.x*C+l.x*E,d.y=k.y*C+l.y*E,i.isStatic||i.isSleeping||(i.positionPrev.x+=d.x*i.inverseMass,i.positionPrev.y+=d.y*i.inverseMass,i.anglePrev+=z.cross(s,d)*i.inverseInertia),j.isStatic||j.isSleeping||(j.positionPrev.x-=d.x*j.inverseMass,j.positionPrev.y-=d.y*j.inverseMass,j.anglePrev-=z.cross(t,d)*j.inverseInertia)}}}}}();var l={};!function(){l.collides=function(b,d,e){var f,g,h,i,j=e,k=!1;if(j){var l=b.speed*b.speed+b.angularSpeed*b.angularSpeed+d.speed*d.speed+d.angularSpeed*d.angularSpeed;k=j&&j.collided&&.2>l,i=j}else i={collided:!1,bodyA:b,bodyB:d};if(j&&k){var m=[j.bodyA.axes[j.axisNumber]];if(h=a(j.bodyA.vertices,j.bodyB.vertices,m),i.reused=!0,h.overlap<=0)return i.collided=!1,i}else{if(f=a(b.vertices,d.vertices,b.axes),f.overlap<=0)return i.collided=!1,i;if(g=a(d.vertices,b.vertices,d.axes),g.overlap<=0)return i.collided=!1,i;f.overlap0&&(i.normal=z.neg(i.normal)),i.tangent=z.perp(i.normal),i.penetration={x:i.normal.x*i.depth,y:i.normal.y*i.depth};var n=c(b,d,i.normal),o=i.supports||[];if(o.length=0,A.contains(b.vertices,n[0])&&o.push(n[0]),A.contains(b.vertices,n[1])&&o.push(n[1]),o.length<2){var p=c(d,b,z.neg(i.normal));A.contains(d.vertices,p[0])&&o.push(p[0]),o.length<2&&A.contains(d.vertices,p[1])&&o.push(p[1])}return o.length<2&&(o=[n[0]]),i.supports=o,i.supportCorrected=z.sub(o[0],i.penetration),i};var a=function(a,c,d){for(var e,f,g={},h={},i={overlap:Number.MAX_VALUE},j=0;j=e)return i.overlap=e,i;ee?e=g:d>g&&(d=g)}a.min=d,a.max=e},c=function(a,b,c){for(var d,e,f=Number.MAX_VALUE,g={x:0,y:0},h=b.vertices,i=a.position,j=h[0],k=h[1],l=0;ld&&(f=d,j=e);var m=j.index-1>=0?j.index-1:h.length-1;e=h[m],g.x=e.x-i.x,g.y=e.y-i.y,f=-z.dot(c,g),k=e;var n=(j.index+1)%h.length;return e=h[n],g.x=e.x-i.x,g.y=e.y-i.y,d=-z.dot(c,g),f>d&&(f=d,k=e),[j,k]}}();var m={};!function(){var a=1e-6,b=.001;m.create=function(b){var c=b;c.bodyA&&!c.pointA&&(c.pointA={x:0,y:0}),c.bodyB&&!c.pointB&&(c.pointB={x:0,y:0});var d=c.bodyA?z.add(c.bodyA.position,c.pointA):c.pointA,e=c.bodyB?z.add(c.bodyB.position,c.pointB):c.pointB,f=z.magnitude(z.sub(d,e));c.length=c.length||f||a;var g={visible:!0,lineWidth:2,strokeStyle:"#666"};return c.render=o.extend(g,c.render),c.id=c.id||o.nextId(),c.label=c.label||"Constraint",c.type="constraint",c.stiffness=c.stiffness||1,c.angularStiffness=c.angularStiffness||0,c.angleA=c.bodyA?c.bodyA.angle:c.angleA,c.angleB=c.bodyB?c.bodyB.angle:c.angleB,c},m.solveAll=function(a,b){for(var c=0;c0&&(B=0);var C,D={x:n.x*B,y:n.y*B};e&&!e.isStatic&&(C=z.cross(s,D)*e.inverseInertia*(1-c.angularStiffness),u.set(e,!1),C=o.clamp(C,-.01,.01),e.constraintImpulse.x-=p.x,e.constraintImpulse.y-=p.y,e.constraintImpulse.angle+=C,e.position.x-=p.x,e.position.y-=p.y,e.angle+=C),f&&!f.isStatic&&(C=z.cross(t,D)*f.inverseInertia*(1-c.angularStiffness),u.set(f,!1),C=o.clamp(C,-.01,.01),f.constraintImpulse.x+=p.x,f.constraintImpulse.y+=p.y,f.constraintImpulse.angle-=C,f.position.x+=p.x,f.position.y+=p.y,f.angle-=C)}}},m.postSolveAll=function(a){for(var b=0;b>16)+d,f=(c>>8&255)+d,g=(255&c)+d;return"#"+(16777216+65536*(255>e?1>e?0:e:255)+256*(255>f?1>f?0:f:255)+(255>g?1>g?0:g:255)).toString(16).slice(1)},o.shuffle=function(a){for(var b=a.length-1;b>0;b--){var c=Math.floor(o.random()*(b+1)),d=a[b];a[b]=a[c],a[c]=d}return a},o.choose=function(a){return a[Math.floor(o.random()*a.length)]},o.isElement=function(a){try{return a instanceof HTMLElement}catch(b){return"object"==typeof a&&1===a.nodeType&&"object"==typeof a.style&&"object"==typeof a.ownerDocument}},o.clamp=function(a,b,c){return b>a?b:a>c?c:a},o.sign=function(a){return 0>a?-1:1},o.now=function(){var a=window.performance;return a?(a.now=a.now||a.webkitNow||a.msNow||a.oNow||a.mozNow,+a.now()):+new Date},o.random=function(b,c){return b="undefined"!=typeof b?b:0,c="undefined"!=typeof c?c:1,b+a()*(c-b)},o.colorToNumber=function(a){return a=a.replace("#",""),3==a.length&&(a=a.charAt(0)+a.charAt(0)+a.charAt(1)+a.charAt(1)+a.charAt(2)+a.charAt(2)),parseInt(a,16)},o.log=function(a,b){if(console&&console.log){var c;switch(b){case"warn":c="color: coral";break;case"error":c="color: red"}console.log("%c [Matter] "+b+": "+a,c)}},o.nextId=function(){return o._nextId++},o.indexOf=function(a,b){if(a.indexOf)return a.indexOf(b);for(var c=0;c0&&q.trigger(a,"collisionStart",{pairs:t.collisionStart}),k.preSolveVelocity(t.list),f=0;f0&&q.trigger(a,"collisionActive",{pairs:t.collisionActive}),t.collisionEnd.length>0&&q.trigger(a,"collisionEnd",{pairs:t.collisionEnd}),r.update(a.metrics,a),b.resetForcesAll(o),g.isModified&&c.setModified(g,!1,!1,!0),q.trigger(a,"afterUpdate",n),a},p.render=function(a){var b={timestamp:a.timing.timestamp};q.trigger(a,"beforeRender",b),a.render.controller.world(a),q.trigger(a,"afterRender",b)},p.merge=function(a,b){if(o.extend(a,b),b.world){a.world=b.world,p.clear(a);for(var d=c.allBodies(a.world),e=0;em.deltaMax?m.deltaMax:k,l=k/m.delta,m.delta=k),0!==h&&(l*=m.timeScale/h),0===m.timeScale&&(l=0),h=m.timeScale,f+=1,j-e>=1e3&&(m.fps=f*((j-e)/1e3),e=j,f=0),q.trigger(a,"tick",n),a.world.isModified&&a.render.controller.clear(a.render),p.update(a,k,l),p.render(a),q.trigger(a,"afterTick",n) +}}()},t.stop=function(a){e(a.timing.frameRequestId)}}();var u={};!function(){u._motionWakeThreshold=.18,u._motionSleepThreshold=.08,u._minBias=.9,u.update=function(a,b){for(var c=b*b*b,d=0;d0||e.force.y>0)u.set(e,!1);else{var g=Math.min(e.motion,f),h=Math.max(e.motion,f);e.motion=u._minBias*g+(1-u._minBias)*h,e.sleepThreshold>0&&e.motion=e.sleepThreshold&&u.set(e,!0)):e.sleepCounter>0&&(e.sleepCounter-=1)}}},u.afterCollisions=function(a,b){for(var c=b*b*b,d=0;du._motionWakeThreshold*c&&u.set(i,!1)}}}},u.set=function(a,b){b?(a.isSleeping=!0,a.sleepCounter=a.sleepThreshold,a.positionImpulse.x=0,a.positionImpulse.y=0,a.positionPrev.x=a.position.x,a.positionPrev.y=a.position.y,a.anglePrev=a.angle,a.speed=0,a.angularSpeed=0,a.motion=0):(a.isSleeping=!1,a.sleepCounter=0)}}();var v={};!function(){v.rectangle=function(a,c,d,e,f){f=f||{};var g={label:"Rectangle Body",position:{x:a,y:c},vertices:A.fromPath("L 0 0 L "+d+" 0 L "+d+" "+e+" L 0 "+e)};if(f.chamfer){var h=f.chamfer;g.vertices=A.chamfer(g.vertices,h.radius,h.quality,h.qualityMin,h.qualityMax),delete f.chamfer}return b.create(o.extend({},g,f))},v.trapezoid=function(a,c,d,e,f,g){g=g||{},f*=.5;var h=(1-2*f)*d,i=d*f,j=i+h,k=j+i,l={label:"Trapezoid Body",position:{x:a,y:c},vertices:A.fromPath("L 0 0 L "+i+" "+-e+" L "+j+" "+-e+" L "+k+" 0")};if(g.chamfer){var m=g.chamfer;l.vertices=A.chamfer(l.vertices,m.radius,m.quality,m.qualityMin,m.qualityMax),delete g.chamfer}return b.create(o.extend({},l,g))},v.circle=function(a,b,c,d,e){d=d||{},d.label="Circle Body",e=e||25;var f=Math.ceil(Math.max(10,Math.min(e,c)));return f%2===1&&(f+=1),d.circleRadius=c,v.polygon(a,b,f,c,d)},v.polygon=function(a,c,d,e,f){if(f=f||{},3>d)return v.circle(a,c,e,f);for(var g=2*Math.PI/d,h="",i=.5*g,j=0;d>j;j+=1){var k=i+j*g,l=Math.cos(k)*e,m=Math.sin(k)*e;h+="L "+l.toFixed(3)+" "+m.toFixed(3)+" "}var n={label:"Polygon Body",position:{x:a,y:c},vertices:A.fromPath(h)};if(f.chamfer){var p=f.chamfer;n.vertices=A.chamfer(n.vertices,p.radius,p.quality,p.qualityMin,p.qualityMax),delete f.chamfer}return b.create(o.extend({},n,f))}}();var w={};!function(){w.stack=function(a,d,e,f,g,h,i){for(var j,k=c.create({label:"Stack"}),l=a,m=d,n=0,o=0;f>o;o++){for(var p=0,q=0;e>q;q++){var r=i(l,m,q,o,j,n);if(r){var s=r.bounds.max.y-r.bounds.min.y,t=r.bounds.max.x-r.bounds.min.x;s>p&&(p=s),b.translate(r,{x:.5*t,y:.5*s}),l=r.bounds.max.x+g,c.addBody(k,r),j=r,n+=1}}m+=p+h,l=a}return k},w.chain=function(a,b,d,e,f,g){for(var h=a.bodies,i=1;ig;g++){for(h=0;b>h;h++)h>0&&(i=l[h-1+g*b],j=l[h+g*b],c.addConstraint(a,m.create(o.extend({bodyA:i,bodyB:j},f))));for(h=0;b>h;h++)g>0&&(i=l[h+(g-1)*b],j=l[h+g*b],c.addConstraint(a,m.create(o.extend({bodyA:i,bodyB:j},f))),e&&h>0&&(k=l[h-1+(g-1)*b],c.addConstraint(a,m.create(o.extend({bodyA:k,bodyB:j},f)))),e&&b-1>h&&(k=l[h+1+(g-1)*b],c.addConstraint(a,m.create(o.extend({bodyA:k,bodyB:j},f)))))}return a.label+=" Mesh",a},w.pyramid=function(a,c,d,e,f,g,h){return w.stack(a,c,d,e,f,g,function(c,g,i,j,k,l){var m=Math.min(e,Math.ceil(d/2)),n=k?k.bounds.max.x-k.bounds.min.x:0;if(!(j>m)){j=m-j;var o=j,p=d-1-j;if(!(o>i||i>p)){1===l&&b.translate(k,{x:(i+(d%2===1?1:-1))*n,y:0});var q=k?i*n:0;return h(a+q+i*f,g,i,j,k,l)}}})},w.newtonsCradle=function(a,b,d,e,f){for(var g=c.create({label:"Newtons Cradle"}),h=0;d>h;h++){var i=1.9,j=v.circle(a+h*e*i,b+f,e,{inertia:99999,restitution:1,friction:0,frictionAir:1e-4,slop:.01}),k=m.create({pointA:{x:a+h*e*i,y:b},bodyB:j});c.addBody(g,j),c.addConstraint(g,k)}return g},w.car=function(a,d,e,f,g){var h=b.nextGroup(!0),i=-20,j=.5*-e+i,k=.5*e-i,l=0,n=c.create({label:"Car"}),o=v.trapezoid(a,d,e,f,.3,{collisionFilter:{group:h},friction:.01,chamfer:{radius:10}}),p=v.circle(a+j,d+l,g,{collisionFilter:{group:h},restitution:.5,friction:.9,density:.01}),q=v.circle(a+k,d+l,g,{collisionFilter:{group:h},restitution:.5,friction:.9,density:.01}),r=m.create({bodyA:o,pointA:{x:j,y:l},bodyB:p,stiffness:.5}),s=m.create({bodyA:o,pointA:{x:k,y:l},bodyB:q,stiffness:.5});return c.addBody(n,o),c.addBody(n,p),c.addBody(n,q),c.addConstraint(n,r),c.addConstraint(n,s),n},w.softBody=function(a,b,c,d,e,f,g,h,i,j){i=o.extend({inertia:1/0},i),j=o.extend({stiffness:.4},j);var k=w.stack(a,b,c,d,e,f,function(a,b){return v.circle(a,b,h,i)});return w.mesh(k,c,d,g,j),k.label="Soft Body",k}}();var x={};!function(){x.fromVertices=function(a){for(var b={},c=0;ca.max.x&&(a.max.x=e.x),e.xa.max.y&&(a.max.y=e.y),e.y0?a.max.x+=c.x:a.min.x+=c.x,c.y>0?a.max.y+=c.y:a.min.y+=c.y)},y.contains=function(a,b){return b.x>=a.min.x&&b.x<=a.max.x&&b.y>=a.min.y&&b.y<=a.max.y},y.overlaps=function(a,b){return a.min.x<=b.max.x&&a.max.x>=b.min.x&&a.max.y>=b.min.y&&a.min.y<=b.max.y},y.translate=function(a,b){a.min.x+=b.x,a.max.x+=b.x,a.min.y+=b.y,a.max.y+=b.y},y.shift=function(a,b){var c=a.max.x-a.min.x,d=a.max.y-a.min.y;a.min.x=b.x,a.max.x=b.x+c,a.min.y=b.y,a.max.y=b.y+d}}();var z={};!function(){z.clone=function(a){return{x:a.x,y:a.y}},z.magnitude=function(a){return Math.sqrt(a.x*a.x+a.y*a.y)},z.magnitudeSquared=function(a){return a.x*a.x+a.y*a.y},z.rotate=function(a,b){var c=Math.cos(b),d=Math.sin(b);return{x:a.x*c-a.y*d,y:a.x*d+a.y*c}},z.rotateAbout=function(a,b,c){var d=Math.cos(b),e=Math.sin(b);return{x:c.x+((a.x-c.x)*d-(a.y-c.y)*e),y:c.y+((a.x-c.x)*e+(a.y-c.y)*d)}},z.normalise=function(a){var b=z.magnitude(a);return 0===b?{x:0,y:0}:{x:a.x/b,y:a.y/b}},z.dot=function(a,b){return a.x*b.x+a.y*b.y},z.cross=function(a,b){return a.x*b.y-a.y*b.x},z.add=function(a,b){return{x:a.x+b.x,y:a.y+b.y}},z.sub=function(a,b){return{x:a.x-b.x,y:a.y-b.y}},z.mult=function(a,b){return{x:a.x*b,y:a.y*b}},z.div=function(a,b){return{x:a.x/b,y:a.y/b}},z.perp=function(a,b){return b=b===!0?-1:1,{x:b*-a.y,y:b*a.x}},z.neg=function(a){return{x:-a.x,y:-a.y}},z.angle=function(a,b){return Math.atan2(b.y-a.y,b.x-a.x)}}();var A={};!function(){A.create=function(a,b){for(var c=[],d=0;d0)return!1}return!0},A.scale=function(a,b,c,d){if(1===b&&1===c)return a;d=d||A.centre(a);for(var e,f,g=0;g=0?g-1:a.length-1],i=a[g],j=a[(g+1)%a.length],k=b[gv;v++)f.push(z.add(z.rotate(p,u*v),r))}else f.push(i)}return f}}();var B={};!function(){B.create=function(b){var c={controller:B,element:null,canvas:null,options:{width:800,height:600,background:"#fafafa",wireframeBackground:"#222",hasBounds:!1,enabled:!0,wireframes:!0,showSleeping:!0,showDebug:!1,showBroadphase:!1,showBounds:!1,showVelocity:!1,showCollisions:!1,showAxes:!1,showPositions:!1,showAngleIndicator:!1,showIds:!1,showShadows:!1}},d=o.extend(c,b);return d.canvas=d.canvas||a(d.options.width,d.options.height),d.context=d.canvas.getContext("2d"),d.textures={},d.bounds=d.bounds||{min:{x:0,y:0},max:{x:d.options.width,y:d.options.height}},B.setBackground(d,d.options.background),o.isElement(d.element)?d.element.appendChild(d.canvas):o.log('No "render.element" passed, "render.canvas" was not inserted into document.',"warn"),d},B.clear=function(){},B.setBackground=function(a,b){if(a.currentBackground!==b){var c=b;/(jpg|gif|png)$/.test(b)&&(c="url("+b+")"),a.canvas.style.background=c,a.canvas.style.backgroundSize="contain",a.currentBackground=b}},B.world=function(a){var b,d=a.render,e=a.world,f=d.canvas,h=d.context,i=d.options,j=c.allBodies(e),k=c.allConstraints(e),l=[],m=[];i.wireframes?B.setBackground(d,i.wireframeBackground):B.setBackground(d,i.background),h.globalCompositeOperation="source-in",h.fillStyle="transparent",h.fillRect(0,0,f.width,f.height),h.globalCompositeOperation="source-over";var n=d.bounds.max.x-d.bounds.min.x,o=d.bounds.max.y-d.bounds.min.y,p=n/d.options.width,q=o/d.options.height;if(i.hasBounds){for(b=0;b=500){var k="";k+="fps: "+Math.round(a.timing.fps)+j,a.metrics.extended&&(k+="delta: "+a.timing.delta.toFixed(3)+j,k+="correction: "+a.timing.correction.toFixed(3)+j,k+="bodies: "+i.length+j,a.broadphase.controller===g&&(k+="buckets: "+a.metrics.buckets+j),k+="\n",k+="collisions: "+a.metrics.collisions+j,k+="pairs: "+a.pairs.list.length+j,k+="broad: "+a.metrics.broadEff+j,k+="mid: "+a.metrics.midEff+j,k+="narrow: "+a.metrics.narrowEff+j),f.debugString=k,f.debugTimestamp=a.timing.timestamp}if(f.debugString){d.font="12px Arial",d.fillStyle=h.wireframes?"rgba(255,255,255,0.5)":"rgba(0,0,0,0.5)";for(var l=f.debugString.split("\n"),m=0;m0){var l=d.activeContacts[0].vertex.x,m=d.activeContacts[0].vertex.y;2===d.activeContacts.length&&(l=(d.activeContacts[0].vertex.x+d.activeContacts[1].vertex.x)/2,m=(d.activeContacts[0].vertex.y+d.activeContacts[1].vertex.y)/2),h.moveTo(l-8*e.normal.x,m-8*e.normal.y),h.lineTo(l,m)}h.strokeStyle=i.wireframes?"rgba(255,165,0,0.7)":"orange",h.lineWidth=1,h.stroke()},B.grid=function(a,b,c){var d=c,e=a.render.options;d.strokeStyle=e.wireframes?"rgba(255,180,0,0.1)":"rgba(255,180,0,0.5)",d.beginPath();for(var f=o.keys(b.buckets),g=0;g