diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8c8d9c8..3119f18 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,44 @@
+## 0.18.0 (2021-12-15)
+
+* added test capture sort to improve comparison ([ea3c11b](https://github.com/liabru/matter-js/commit/ea3c11b))
+* added triangles to mixed bodies example ([b116f64](https://github.com/liabru/matter-js/commit/b116f64))
+* added behaviour metric to tests and refactor tests ([8125966](https://github.com/liabru/matter-js/commit/8125966))
+* added benchmark test command ([7f34c45](https://github.com/liabru/matter-js/commit/7f34c45))
+* added broadphase to Matter.Detector ([a6b5e7d](https://github.com/liabru/matter-js/commit/a6b5e7d))
+* added cache checks to Matter.Composite ([32fd285](https://github.com/liabru/matter-js/commit/32fd285))
+* added example for Composite.remove ([bc07f56](https://github.com/liabru/matter-js/commit/bc07f56))
+* added example stress 3 ([d0ee246](https://github.com/liabru/matter-js/commit/d0ee246))
+* added filesize to test comparison report ([b3a8aa3](https://github.com/liabru/matter-js/commit/b3a8aa3))
+* added Matter.Collision ([9037f36](https://github.com/liabru/matter-js/commit/9037f36))
+* added memory comparison to tests ([bedf84c](https://github.com/liabru/matter-js/commit/bedf84c))
+* added note about webpack performance to readme ([80cf76b](https://github.com/liabru/matter-js/commit/80cf76b))
+* added stable sorting to test worker and refactor ([81dd2fb](https://github.com/liabru/matter-js/commit/81dd2fb))
+* added support for build metadata in Plugin.versionParse ([8bfaff0](https://github.com/liabru/matter-js/commit/8bfaff0))
+* changed raycasting example events to enable use in tests ([10afaea](https://github.com/liabru/matter-js/commit/10afaea))
+* changed build config to 'source-map' devtool ([e909b04](https://github.com/liabru/matter-js/commit/e909b04))
+* changed tests to use a production build rather than source ([55feb89](https://github.com/liabru/matter-js/commit/55feb89))
+* deprecated Matter.Grid ([e366d0e](https://github.com/liabru/matter-js/commit/e366d0e))
+* fixed sync issues on demo compare tool ([826ed46](https://github.com/liabru/matter-js/commit/826ed46))
+* improved performance measurement accuracy in tests ([cd289ec](https://github.com/liabru/matter-js/commit/cd289ec))
+* improved test comparison report ([de04c00](https://github.com/liabru/matter-js/commit/de04c00))
+* optimised Matter.Detector ([c7cec16](https://github.com/liabru/matter-js/commit/c7cec16)), ([fd1a70e](https://github.com/liabru/matter-js/commit/fd1a70e)), ([caeb07e](https://github.com/liabru/matter-js/commit/caeb07e)), ([efede6e](https://github.com/liabru/matter-js/commit/efede6e))
+* optimised Matter.Composite ([52e7977](https://github.com/liabru/matter-js/commit/52e7977))
+* optimised Matter.Pair ([d8a6380](https://github.com/liabru/matter-js/commit/d8a6380)), ([48673db](https://github.com/liabru/matter-js/commit/48673db)), ([1073dde](https://github.com/liabru/matter-js/commit/1073dde))
+* optimised Matter.Pairs ([a30707f](https://github.com/liabru/matter-js/commit/a30707f)), ([a882a74](https://github.com/liabru/matter-js/commit/a882a74))
+* optimised Matter.Resolver ([fceb0ca](https://github.com/liabru/matter-js/commit/fceb0ca)), ([49fbfba](https://github.com/liabru/matter-js/commit/49fbfba)), ([0b07a31](https://github.com/liabru/matter-js/commit/0b07a31)), ([f847f4c](https://github.com/liabru/matter-js/commit/f847f4c)), ([3cf65e8](https://github.com/liabru/matter-js/commit/3cf65e8)), ([30b899c](https://github.com/liabru/matter-js/commit/30b899c)), ([e4b35d3](https://github.com/liabru/matter-js/commit/e4b35d3))
+* optimised Matter.SAT ([0d90a17](https://github.com/liabru/matter-js/commit/0d90a17)), ([2096961](https://github.com/liabru/matter-js/commit/2096961)), ([0af144c](https://github.com/liabru/matter-js/commit/0af144c))
+* optimised Matter.Vertices ([c198878](https://github.com/liabru/matter-js/commit/c198878)), ([6883d0d](https://github.com/liabru/matter-js/commit/6883d0d)), ([792ae2e](https://github.com/liabru/matter-js/commit/792ae2e))
+* refactor test worker and prevent test cache ([ca2fe75](https://github.com/liabru/matter-js/commit/ca2fe75)), ([bcc3168](https://github.com/liabru/matter-js/commit/bcc3168))
+* replaced Matter.SAT with Matter.Collision ([b9e7d9d](https://github.com/liabru/matter-js/commit/b9e7d9d))
+* show debug stats in dev demo ([2f14ec5](https://github.com/liabru/matter-js/commit/2f14ec5))
+* updated dev dependencies ([c5028d5](https://github.com/liabru/matter-js/commit/c5028d5))
+* updated examples ([c80ed5c](https://github.com/liabru/matter-js/commit/c80ed5c))
+* updated test scripts ([afa467a](https://github.com/liabru/matter-js/commit/afa467a))
+* use force exit in tests ([8adf810](https://github.com/liabru/matter-js/commit/8adf810))
+* use Matter.Runner in test worker ([2581595](https://github.com/liabru/matter-js/commit/2581595))
+
+
+
## 0.17.1 (2021-04-14)
* deprecate Engine.run alias replaced by Runner.run ([5817046](https://github.com/liabru/matter-js/commit/5817046))
diff --git a/RELEASE.md b/RELEASE.md
index 3ecfb0e..069318a 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -1,3 +1,61 @@
+## ▲.● matter.js `0.18.0`
+
+Release notes for `0.18.0`. See the release [readme](https://github.com/liabru/matter-js/blob/0.18.0/README.md) for further information.
+
+### Highlights ✺
+
+- **Up to ~40% performance improvement (on average measured over all examples, in Node on a Mac Air M1)**
+- Replaces `Matter.Grid` with a faster and more efficient broadphase in `Matter.Detector`
+- Reduced memory usage and garbage collection
+- Resolves issues in `Matter.SAT` related to collision reuse
+- Removes performance issues from `Matter.Grid`
+- Improved collision accuracy
+- Improved API and docs for collision modules
+- Improved [documentation](https://brm.io/matter-js/docs/) pages
+- Added note about avoiding Webpack [performance problems](https://github.com/liabru/matter-js/blob/master/README.md#install)
+
+### Changes ✲
+
+See the release [compare page](https://github.com/liabru/matter-js/compare/0.17.1...0.18.0) and the [changelog](https://github.com/liabru/matter-js/blob/0.18.0/CHANGELOG.md) for a detailed list of changes.
+
+### Migration ⌲
+
+- Behaviour and similarity is in practice the same (a fractional difference from improved accuracy)
+- API is the same other than:
+ - [Matter.Detector](https://brm.io/matter-js/docs/classes/Detector.html) replaces [Matter.Grid](https://brm.io/matter-js/docs/classes/Grid.html) (which is now deprecated but will remain for a short term)
+ - [render.options.showBroadphase](https://brm.io/matter-js/docs/classes/Render.html#property_options.showBroadphase) is deprecated (no longer implemented)
+ - [Matter.SAT](https://brm.io/matter-js/docs/classes/SAT.html) is renamed [Matter.Collision](https://brm.io/matter-js/docs/classes/Collision.html)
+ - [Matter.SAT.collides](https://brm.io/matter-js/docs/classes/SAT.html#method_collides) is now [Matter.Collision.collides](https://brm.io/matter-js/docs/classes/Collision.html#method_collides) with a slightly different set of arguments
+
+### Comparison ⎄
+
+Differences in behaviour, quality and performance against the previous release `0.17.1`. For more information see [comparison method](https://github.com/liabru/matter-js/pull/794).
+
+```ocaml
+Output comparison of 43 examples against previous release matter-js@0.17.1
+
+Behaviour 99.99% Similarity 99.98% Overlap -0.00%
+Performance +40.62% Memory -6.18% Filesize -0.16% 77.73 KB
+
+airFriction · · avalanche ● · ballPool · · bridge · · car · · catapult · ·
+chains · · circleStack · · cloth · · collisionFiltering · · compositeManipulation ● ·
+compound · · compoundStack · · concave · · constraints ● · doublePendulum · ·
+events · · friction · · gravity · · gyro · · manipulation · ◆
+mixed · · newtonsCradle · · pyramid · · ragdoll · · raycasting · ·
+remove · · restitution · · rounded · · sensors · · sleeping · ◆
+slingshot · · softBody · · sprites · · stack · · staticFriction · ·
+stats · · stress · · stress2 · · stress3 · · timescale · ·
+views · · wreckingBall · ·
+
+where · no change ● extrinsics changed ◆ intrinsics changed
+```
+
+### Contributors ♥︎
+
+Many thanks to the [contributors](https://github.com/liabru/matter-js/compare/0.17.1...0.18.0) of this release, [past contributors](https://github.com/liabru/matter-js/graphs/contributors) as well those involved in the [community](https://github.com/liabru/matter-js/issues) for your input and support.
+
+---
+
## ▲.● matter.js `0.17.0`
Release notes for `0.17.0`. See the release [readme](https://github.com/liabru/matter-js/blob/0.17.0/README.md) for further information.
diff --git a/build/matter.js b/build/matter.js
index b44b5db..ae3bc3d 100644
--- a/build/matter.js
+++ b/build/matter.js
@@ -1,5 +1,5 @@
/*!
- * matter-js 0.17.1 by @liabru
+ * matter-js 0.18.0 by @liabru
* http://brm.io/matter-js/
* License MIT
*
@@ -118,7 +118,7 @@ return /******/ (function(modules) { // webpackBootstrap
/******/
/******/
/******/ // Load entry module and return exports
-/******/ return __webpack_require__(__webpack_require__.s = 22);
+/******/ return __webpack_require__(__webpack_require__.s = 21);
/******/ })
/************************************************************************/
/******/ ([
@@ -1283,17 +1283,16 @@ var Common = __webpack_require__(0);
* @param {number} scalar
*/
Vertices.translate = function(vertices, vector, scalar) {
- var i;
- if (scalar) {
- for (i = 0; i < vertices.length; i++) {
- vertices[i].x += vector.x * scalar;
- vertices[i].y += vector.y * scalar;
- }
- } else {
- for (i = 0; i < vertices.length; i++) {
- vertices[i].x += vector.x;
- vertices[i].y += vector.y;
- }
+ scalar = typeof scalar !== 'undefined' ? scalar : 1;
+
+ var verticesLength = vertices.length,
+ translateX = vector.x * scalar,
+ translateY = vector.y * scalar,
+ i;
+
+ for (i = 0; i < verticesLength; i++) {
+ vertices[i].x += translateX;
+ vertices[i].y += translateY;
}
return vertices;
@@ -1311,15 +1310,21 @@ var Common = __webpack_require__(0);
return;
var cos = Math.cos(angle),
- sin = Math.sin(angle);
+ sin = Math.sin(angle),
+ pointX = point.x,
+ pointY = point.y,
+ verticesLength = vertices.length,
+ vertex,
+ dx,
+ dy,
+ i;
- for (var i = 0; i < vertices.length; i++) {
- var vertice = vertices[i],
- dx = vertice.x - point.x,
- dy = vertice.y - point.y;
-
- vertice.x = point.x + (dx * cos - dy * sin);
- vertice.y = point.y + (dx * sin + dy * cos);
+ for (i = 0; i < verticesLength; i++) {
+ vertex = vertices[i];
+ dx = vertex.x - pointX;
+ dy = vertex.y - pointY;
+ vertex.x = pointX + (dx * cos - dy * sin);
+ vertex.y = pointY + (dx * sin + dy * cos);
}
return vertices;
@@ -1333,12 +1338,21 @@ var Common = __webpack_require__(0);
* @return {boolean} True if the vertices contains point, otherwise false
*/
Vertices.contains = function(vertices, point) {
- for (var i = 0; i < vertices.length; i++) {
- var vertice = vertices[i],
- nextVertice = vertices[(i + 1) % vertices.length];
- if ((point.x - vertice.x) * (nextVertice.y - vertice.y) + (point.y - vertice.y) * (vertice.x - nextVertice.x) > 0) {
+ var pointX = point.x,
+ pointY = point.y,
+ verticesLength = vertices.length,
+ vertex = vertices[verticesLength - 1],
+ nextVertex;
+
+ for (var i = 0; i < verticesLength; i++) {
+ nextVertex = vertices[i];
+
+ if ((pointX - vertex.x) * (nextVertex.y - vertex.y)
+ + (pointY - vertex.y) * (vertex.x - nextVertex.x) > 0) {
return false;
}
+
+ vertex = nextVertex;
}
return true;
@@ -1724,7 +1738,12 @@ var Body = __webpack_require__(6);
constraints: [],
composites: [],
label: 'Composite',
- plugin: {}
+ plugin: {},
+ cache: {
+ allBodies: null,
+ allConstraints: null,
+ allComposites: null
+ }
}, options);
};
@@ -1732,6 +1751,7 @@ var Body = __webpack_require__(6);
* Sets the composite's `isModified` flag.
* If `updateParents` is true, all parents will be set (default: false).
* If `updateChildren` is true, all children will be set (default: false).
+ * @private
* @method setModified
* @param {composite} composite
* @param {boolean} isModified
@@ -1741,12 +1761,18 @@ var Body = __webpack_require__(6);
Composite.setModified = function(composite, isModified, updateParents, updateChildren) {
composite.isModified = isModified;
+ if (isModified && composite.cache) {
+ composite.cache.allBodies = null;
+ composite.cache.allConstraints = null;
+ composite.cache.allComposites = null;
+ }
+
if (updateParents && composite.parent) {
Composite.setModified(composite.parent, isModified, updateParents, updateChildren);
}
if (updateChildren) {
- for(var i = 0; i < composite.composites.length; i++) {
+ for (var i = 0; i < composite.composites.length; i++) {
var childComposite = composite.composites[i];
Composite.setModified(childComposite, isModified, updateParents, updateChildren);
}
@@ -1867,7 +1893,6 @@ var Body = __webpack_require__(6);
var position = Common.indexOf(compositeA.composites, compositeB);
if (position !== -1) {
Composite.removeCompositeAt(compositeA, position);
- Composite.setModified(compositeA, true, true, false);
}
if (deep) {
@@ -1920,7 +1945,6 @@ var Body = __webpack_require__(6);
var position = Common.indexOf(composite.bodies, body);
if (position !== -1) {
Composite.removeBodyAt(composite, position);
- Composite.setModified(composite, true, true, false);
}
if (deep) {
@@ -2021,6 +2045,7 @@ var Body = __webpack_require__(6);
composite.constraints.length = 0;
composite.composites.length = 0;
+
Composite.setModified(composite, true, true, false);
return composite;
@@ -2033,11 +2058,19 @@ var Body = __webpack_require__(6);
* @return {body[]} All the bodies
*/
Composite.allBodies = function(composite) {
+ if (composite.cache && composite.cache.allBodies) {
+ return composite.cache.allBodies;
+ }
+
var bodies = [].concat(composite.bodies);
for (var i = 0; i < composite.composites.length; i++)
bodies = bodies.concat(Composite.allBodies(composite.composites[i]));
+ if (composite.cache) {
+ composite.cache.allBodies = bodies;
+ }
+
return bodies;
};
@@ -2048,11 +2081,19 @@ var Body = __webpack_require__(6);
* @return {constraint[]} All the constraints
*/
Composite.allConstraints = function(composite) {
+ if (composite.cache && composite.cache.allConstraints) {
+ return composite.cache.allConstraints;
+ }
+
var constraints = [].concat(composite.constraints);
for (var i = 0; i < composite.composites.length; i++)
constraints = constraints.concat(Composite.allConstraints(composite.composites[i]));
+ if (composite.cache) {
+ composite.cache.allConstraints = constraints;
+ }
+
return constraints;
};
@@ -2063,11 +2104,19 @@ var Body = __webpack_require__(6);
* @return {composite[]} All the composites
*/
Composite.allComposites = function(composite) {
+ if (composite.cache && composite.cache.allComposites) {
+ return composite.cache.allComposites;
+ }
+
var composites = [].concat(composite.composites);
for (var i = 0; i < composite.composites.length; i++)
composites = composites.concat(Composite.allComposites(composite.composites[i]));
+ if (composite.cache) {
+ composite.cache.allComposites = composites;
+ }
+
return composites;
};
@@ -2134,8 +2183,6 @@ var Body = __webpack_require__(6);
objects[i].id = Common.nextId();
}
- Composite.setModified(composite, true, true, false);
-
return composite;
};
@@ -2154,8 +2201,6 @@ var Body = __webpack_require__(6);
Body.translate(bodies[i], translation);
}
- Composite.setModified(composite, true, true, false);
-
return composite;
};
@@ -2185,8 +2230,6 @@ var Body = __webpack_require__(6);
Body.rotate(body, rotation);
}
- Composite.setModified(composite, true, true, false);
-
return composite;
};
@@ -2215,8 +2258,6 @@ var Body = __webpack_require__(6);
Body.scale(body, scaleX, scaleY);
}
- Composite.setModified(composite, true, true, false);
-
return composite;
};
@@ -2316,8 +2357,7 @@ var Body = __webpack_require__(6);
/**
* A flag that specifies whether the composite has been modified during the current step.
- * Most `Matter.Composite` methods will automatically set this flag to `true` to inform the engine of changes to be handled.
- * If you need to change it manually, you should use the `Composite.setModified` method.
+ * This is automatically managed when bodies, constraints or composites are added or removed.
*
* @property isModified
* @type boolean
@@ -2369,6 +2409,15 @@ var Body = __webpack_require__(6);
* @type {}
*/
+ /**
+ * An object used for storing cached results for performance reasons.
+ * This is used internally only and is automatically managed.
+ *
+ * @private
+ * @property cache
+ * @type {}
+ */
+
})();
@@ -2396,7 +2445,7 @@ var Sleeping = __webpack_require__(7);
var Render = __webpack_require__(16);
var Common = __webpack_require__(0);
var Bounds = __webpack_require__(1);
-var Axes = __webpack_require__(10);
+var Axes = __webpack_require__(11);
(function() {
@@ -3752,6 +3801,550 @@ var Events = __webpack_require__(4);
/* 8 */
/***/ (function(module, exports, __webpack_require__) {
+/**
+* The `Matter.Collision` module contains methods for detecting collisions between a given pair of bodies.
+*
+* For efficient detection between a list of bodies, see `Matter.Detector` and `Matter.Query`.
+*
+* See `Matter.Engine` for collision events.
+*
+* @class Collision
+*/
+
+var Collision = {};
+
+module.exports = Collision;
+
+var Vertices = __webpack_require__(3);
+var Pair = __webpack_require__(9);
+
+(function() {
+ var _supports = [];
+
+ var _overlapAB = {
+ overlap: 0,
+ axis: null
+ };
+
+ var _overlapBA = {
+ overlap: 0,
+ axis: null
+ };
+
+ /**
+ * Creates a new collision record.
+ * @method create
+ * @param {body} bodyA The first body part represented by the collision record
+ * @param {body} bodyB The second body part represented by the collision record
+ * @return {collision} A new collision record
+ */
+ Collision.create = function(bodyA, bodyB) {
+ return {
+ pair: null,
+ collided: false,
+ bodyA: bodyA,
+ bodyB: bodyB,
+ parentA: bodyA.parent,
+ parentB: bodyB.parent,
+ depth: 0,
+ normal: { x: 0, y: 0 },
+ tangent: { x: 0, y: 0 },
+ penetration: { x: 0, y: 0 },
+ supports: []
+ };
+ };
+
+ /**
+ * Detect collision between two bodies.
+ * @method collides
+ * @param {body} bodyA
+ * @param {body} bodyB
+ * @param {pairs} [pairs] Optionally reuse collision records from existing pairs.
+ * @return {collision|null} A collision record if detected, otherwise null
+ */
+ Collision.collides = function(bodyA, bodyB, pairs) {
+ Collision._overlapAxes(_overlapAB, bodyA.vertices, bodyB.vertices, bodyA.axes);
+
+ if (_overlapAB.overlap <= 0) {
+ return null;
+ }
+
+ Collision._overlapAxes(_overlapBA, bodyB.vertices, bodyA.vertices, bodyB.axes);
+
+ if (_overlapBA.overlap <= 0) {
+ return null;
+ }
+
+ // reuse collision records for gc efficiency
+ var pair = pairs && pairs.table[Pair.id(bodyA, bodyB)],
+ collision;
+
+ if (!pair) {
+ collision = Collision.create(bodyA, bodyB);
+ collision.collided = true;
+ collision.bodyA = bodyA.id < bodyB.id ? bodyA : bodyB;
+ collision.bodyB = bodyA.id < bodyB.id ? bodyB : bodyA;
+ collision.parentA = collision.bodyA.parent;
+ collision.parentB = collision.bodyB.parent;
+ } else {
+ collision = pair.collision;
+ }
+
+ bodyA = collision.bodyA;
+ bodyB = collision.bodyB;
+
+ var minOverlap;
+
+ if (_overlapAB.overlap < _overlapBA.overlap) {
+ minOverlap = _overlapAB;
+ } else {
+ minOverlap = _overlapBA;
+ }
+
+ var normal = collision.normal,
+ supports = collision.supports,
+ minAxis = minOverlap.axis,
+ minAxisX = minAxis.x,
+ minAxisY = minAxis.y;
+
+ // ensure normal is facing away from bodyA
+ if (minAxisX * (bodyB.position.x - bodyA.position.x) + minAxisY * (bodyB.position.y - bodyA.position.y) < 0) {
+ normal.x = minAxisX;
+ normal.y = minAxisY;
+ } else {
+ normal.x = -minAxisX;
+ normal.y = -minAxisY;
+ }
+
+ collision.tangent.x = -normal.y;
+ collision.tangent.y = normal.x;
+
+ collision.depth = minOverlap.overlap;
+
+ collision.penetration.x = normal.x * collision.depth;
+ collision.penetration.y = normal.y * collision.depth;
+
+ // find support points, there is always either exactly one or two
+ var supportsB = Collision._findSupports(bodyA, bodyB, normal, 1),
+ supportCount = 0;
+
+ // find the supports from bodyB that are inside bodyA
+ if (Vertices.contains(bodyA.vertices, supportsB[0])) {
+ supports[supportCount++] = supportsB[0];
+ }
+
+ if (Vertices.contains(bodyA.vertices, supportsB[1])) {
+ supports[supportCount++] = supportsB[1];
+ }
+
+ // find the supports from bodyA that are inside bodyB
+ if (supportCount < 2) {
+ var supportsA = Collision._findSupports(bodyB, bodyA, normal, -1);
+
+ if (Vertices.contains(bodyB.vertices, supportsA[0])) {
+ supports[supportCount++] = supportsA[0];
+ }
+
+ if (supportCount < 2 && Vertices.contains(bodyB.vertices, supportsA[1])) {
+ supports[supportCount++] = supportsA[1];
+ }
+ }
+
+ // account for the edge case of overlapping but no vertex containment
+ if (supportCount === 0) {
+ supports[supportCount++] = supportsB[0];
+ }
+
+ // update supports array size
+ supports.length = supportCount;
+
+ return collision;
+ };
+
+ /**
+ * Find the overlap between two sets of vertices.
+ * @method _overlapAxes
+ * @private
+ * @param {object} result
+ * @param {vertices} verticesA
+ * @param {vertices} verticesB
+ * @param {axes} axes
+ */
+ Collision._overlapAxes = function(result, verticesA, verticesB, axes) {
+ var verticesALength = verticesA.length,
+ verticesBLength = verticesB.length,
+ verticesAX = verticesA[0].x,
+ verticesAY = verticesA[0].y,
+ verticesBX = verticesB[0].x,
+ verticesBY = verticesB[0].y,
+ axesLength = axes.length,
+ overlapMin = Number.MAX_VALUE,
+ overlapAxisNumber = 0,
+ overlap,
+ overlapAB,
+ overlapBA,
+ dot,
+ i,
+ j;
+
+ for (i = 0; i < axesLength; i++) {
+ var axis = axes[i],
+ axisX = axis.x,
+ axisY = axis.y,
+ minA = verticesAX * axisX + verticesAY * axisY,
+ minB = verticesBX * axisX + verticesBY * axisY,
+ maxA = minA,
+ maxB = minB;
+
+ for (j = 1; j < verticesALength; j += 1) {
+ dot = verticesA[j].x * axisX + verticesA[j].y * axisY;
+
+ if (dot > maxA) {
+ maxA = dot;
+ } else if (dot < minA) {
+ minA = dot;
+ }
+ }
+
+ for (j = 1; j < verticesBLength; j += 1) {
+ dot = verticesB[j].x * axisX + verticesB[j].y * axisY;
+
+ if (dot > maxB) {
+ maxB = dot;
+ } else if (dot < minB) {
+ minB = dot;
+ }
+ }
+
+ overlapAB = maxA - minB;
+ overlapBA = maxB - minA;
+ overlap = overlapAB < overlapBA ? overlapAB : overlapBA;
+
+ if (overlap < overlapMin) {
+ overlapMin = overlap;
+ overlapAxisNumber = i;
+
+ if (overlap <= 0) {
+ // can not be intersecting
+ break;
+ }
+ }
+ }
+
+ result.axis = axes[overlapAxisNumber];
+ result.overlap = overlapMin;
+ };
+
+ /**
+ * Projects vertices on an axis and returns an interval.
+ * @method _projectToAxis
+ * @private
+ * @param {} projection
+ * @param {} vertices
+ * @param {} axis
+ */
+ Collision._projectToAxis = function(projection, vertices, axis) {
+ var min = vertices[0].x * axis.x + vertices[0].y * axis.y,
+ max = min;
+
+ for (var i = 1; i < vertices.length; i += 1) {
+ var dot = vertices[i].x * axis.x + vertices[i].y * axis.y;
+
+ if (dot > max) {
+ max = dot;
+ } else if (dot < min) {
+ min = dot;
+ }
+ }
+
+ projection.min = min;
+ projection.max = max;
+ };
+
+ /**
+ * Finds supporting vertices given two bodies along a given direction using hill-climbing.
+ * @method _findSupports
+ * @private
+ * @param {body} bodyA
+ * @param {body} bodyB
+ * @param {vector} normal
+ * @param {number} direction
+ * @return [vector]
+ */
+ Collision._findSupports = function(bodyA, bodyB, normal, direction) {
+ var vertices = bodyB.vertices,
+ verticesLength = vertices.length,
+ bodyAPositionX = bodyA.position.x,
+ bodyAPositionY = bodyA.position.y,
+ normalX = normal.x * direction,
+ normalY = normal.y * direction,
+ nearestDistance = Number.MAX_VALUE,
+ vertexA,
+ vertexB,
+ vertexC,
+ distance,
+ j;
+
+ // find deepest vertex relative to the axis
+ for (j = 0; j < verticesLength; j += 1) {
+ vertexB = vertices[j];
+ distance = normalX * (bodyAPositionX - vertexB.x) + normalY * (bodyAPositionY - vertexB.y);
+
+ // convex hill-climbing
+ if (distance < nearestDistance) {
+ nearestDistance = distance;
+ vertexA = vertexB;
+ }
+ }
+
+ // measure next vertex
+ vertexC = vertices[(verticesLength + vertexA.index - 1) % verticesLength];
+ nearestDistance = normalX * (bodyAPositionX - vertexC.x) + normalY * (bodyAPositionY - vertexC.y);
+
+ // compare with previous vertex
+ vertexB = vertices[(vertexA.index + 1) % verticesLength];
+ if (normalX * (bodyAPositionX - vertexB.x) + normalY * (bodyAPositionY - vertexB.y) < nearestDistance) {
+ _supports[0] = vertexA;
+ _supports[1] = vertexB;
+
+ return _supports;
+ }
+
+ _supports[0] = vertexA;
+ _supports[1] = vertexC;
+
+ return _supports;
+ };
+
+ /*
+ *
+ * Properties Documentation
+ *
+ */
+
+ /**
+ * A reference to the pair using this collision record, if there is one.
+ *
+ * @property pair
+ * @type {pair|null}
+ * @default null
+ */
+
+ /**
+ * A flag that indicates if the bodies were colliding when the collision was last updated.
+ *
+ * @property collided
+ * @type boolean
+ * @default false
+ */
+
+ /**
+ * The first body part represented by the collision (see also `collision.parentA`).
+ *
+ * @property bodyA
+ * @type body
+ */
+
+ /**
+ * The second body part represented by the collision (see also `collision.parentB`).
+ *
+ * @property bodyB
+ * @type body
+ */
+
+ /**
+ * The first body represented by the collision (i.e. `collision.bodyA.parent`).
+ *
+ * @property parentA
+ * @type body
+ */
+
+ /**
+ * The second body represented by the collision (i.e. `collision.bodyB.parent`).
+ *
+ * @property parentB
+ * @type body
+ */
+
+ /**
+ * A `Number` that represents the minimum separating distance between the bodies along the collision normal.
+ *
+ * @readOnly
+ * @property depth
+ * @type number
+ * @default 0
+ */
+
+ /**
+ * A normalised `Vector` that represents the direction between the bodies that provides the minimum separating distance.
+ *
+ * @property normal
+ * @type vector
+ * @default { x: 0, y: 0 }
+ */
+
+ /**
+ * A normalised `Vector` that is the tangent direction to the collision normal.
+ *
+ * @property tangent
+ * @type vector
+ * @default { x: 0, y: 0 }
+ */
+
+ /**
+ * A `Vector` that represents the direction and depth of the collision.
+ *
+ * @property penetration
+ * @type vector
+ * @default { x: 0, y: 0 }
+ */
+
+ /**
+ * An array of body vertices that represent the support points in the collision.
+ * These are the deepest vertices (along the collision normal) of each body that are contained by the other body's vertices.
+ *
+ * @property supports
+ * @type vector[]
+ * @default []
+ */
+
+})();
+
+
+/***/ }),
+/* 9 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/**
+* The `Matter.Pair` module contains methods for creating and manipulating collision pairs.
+*
+* @class Pair
+*/
+
+var Pair = {};
+
+module.exports = Pair;
+
+var Contact = __webpack_require__(17);
+
+(function() {
+
+ /**
+ * Creates a pair.
+ * @method create
+ * @param {collision} collision
+ * @param {number} timestamp
+ * @return {pair} A new pair
+ */
+ Pair.create = function(collision, timestamp) {
+ var bodyA = collision.bodyA,
+ bodyB = collision.bodyB;
+
+ var pair = {
+ id: Pair.id(bodyA, bodyB),
+ bodyA: bodyA,
+ bodyB: bodyB,
+ collision: collision,
+ contacts: [],
+ activeContacts: [],
+ separation: 0,
+ isActive: true,
+ confirmedActive: true,
+ isSensor: bodyA.isSensor || bodyB.isSensor,
+ timeCreated: timestamp,
+ timeUpdated: timestamp,
+ inverseMass: 0,
+ friction: 0,
+ frictionStatic: 0,
+ restitution: 0,
+ slop: 0
+ };
+
+ Pair.update(pair, collision, timestamp);
+
+ return pair;
+ };
+
+ /**
+ * Updates a pair given a collision.
+ * @method update
+ * @param {pair} pair
+ * @param {collision} collision
+ * @param {number} timestamp
+ */
+ Pair.update = function(pair, collision, timestamp) {
+ var contacts = pair.contacts,
+ supports = collision.supports,
+ activeContacts = pair.activeContacts,
+ parentA = collision.parentA,
+ parentB = collision.parentB,
+ parentAVerticesLength = parentA.vertices.length;
+
+ pair.isActive = true;
+ pair.timeUpdated = timestamp;
+ pair.collision = collision;
+ pair.separation = collision.depth;
+ pair.inverseMass = parentA.inverseMass + parentB.inverseMass;
+ pair.friction = parentA.friction < parentB.friction ? parentA.friction : parentB.friction;
+ pair.frictionStatic = parentA.frictionStatic > parentB.frictionStatic ? parentA.frictionStatic : parentB.frictionStatic;
+ pair.restitution = parentA.restitution > parentB.restitution ? parentA.restitution : parentB.restitution;
+ pair.slop = parentA.slop > parentB.slop ? parentA.slop : parentB.slop;
+
+ collision.pair = pair;
+ activeContacts.length = 0;
+
+ for (var i = 0; i < supports.length; i++) {
+ var support = supports[i],
+ contactId = support.body === parentA ? support.index : parentAVerticesLength + support.index,
+ contact = contacts[contactId];
+
+ if (contact) {
+ activeContacts.push(contact);
+ } else {
+ activeContacts.push(contacts[contactId] = Contact.create(support));
+ }
+ }
+ };
+
+ /**
+ * Set a pair as active or inactive.
+ * @method setActive
+ * @param {pair} pair
+ * @param {bool} isActive
+ * @param {number} timestamp
+ */
+ Pair.setActive = function(pair, isActive, timestamp) {
+ if (isActive) {
+ pair.isActive = true;
+ pair.timeUpdated = timestamp;
+ } else {
+ pair.isActive = false;
+ pair.activeContacts.length = 0;
+ }
+ };
+
+ /**
+ * Get the id for the given pair.
+ * @method id
+ * @param {body} bodyA
+ * @param {body} bodyB
+ * @return {string} Unique pairId
+ */
+ Pair.id = function(bodyA, bodyB) {
+ if (bodyA.id < bodyB.id) {
+ return 'A' + bodyA.id + 'B' + bodyB.id;
+ } else {
+ return 'A' + bodyB.id + 'B' + bodyA.id;
+ }
+ };
+
+})();
+
+
+/***/ }),
+/* 10 */
+/***/ (function(module, exports, __webpack_require__) {
+
/**
* The `Matter.Constraint` module contains methods for creating and manipulating constraints.
* Constraints are used for specifying that a fixed distance must be maintained between two bodies (or a body and a fixed world-space position).
@@ -3770,7 +4363,7 @@ var Vertices = __webpack_require__(3);
var Vector = __webpack_require__(2);
var Sleeping = __webpack_require__(7);
var Bounds = __webpack_require__(1);
-var Axes = __webpack_require__(10);
+var Axes = __webpack_require__(11);
var Common = __webpack_require__(0);
(function() {
@@ -4234,140 +4827,7 @@ var Common = __webpack_require__(0);
/***/ }),
-/* 9 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/**
-* The `Matter.Pair` module contains methods for creating and manipulating collision pairs.
-*
-* @class Pair
-*/
-
-var Pair = {};
-
-module.exports = Pair;
-
-var Contact = __webpack_require__(17);
-
-(function() {
-
- /**
- * Creates a pair.
- * @method create
- * @param {collision} collision
- * @param {number} timestamp
- * @return {pair} A new pair
- */
- Pair.create = function(collision, timestamp) {
- var bodyA = collision.bodyA,
- bodyB = collision.bodyB,
- parentA = collision.parentA,
- parentB = collision.parentB;
-
- var pair = {
- id: Pair.id(bodyA, bodyB),
- bodyA: bodyA,
- bodyB: bodyB,
- contacts: {},
- activeContacts: [],
- separation: 0,
- isActive: true,
- confirmedActive: true,
- isSensor: bodyA.isSensor || bodyB.isSensor,
- timeCreated: timestamp,
- timeUpdated: timestamp,
- inverseMass: parentA.inverseMass + parentB.inverseMass,
- friction: Math.min(parentA.friction, parentB.friction),
- frictionStatic: Math.max(parentA.frictionStatic, parentB.frictionStatic),
- restitution: Math.max(parentA.restitution, parentB.restitution),
- slop: Math.max(parentA.slop, parentB.slop)
- };
-
- Pair.update(pair, collision, timestamp);
-
- return pair;
- };
-
- /**
- * Updates a pair given a collision.
- * @method update
- * @param {pair} pair
- * @param {collision} collision
- * @param {number} timestamp
- */
- Pair.update = function(pair, collision, timestamp) {
- var contacts = pair.contacts,
- supports = collision.supports,
- activeContacts = pair.activeContacts,
- parentA = collision.parentA,
- parentB = collision.parentB;
-
- pair.collision = collision;
- pair.inverseMass = parentA.inverseMass + parentB.inverseMass;
- pair.friction = Math.min(parentA.friction, parentB.friction);
- pair.frictionStatic = Math.max(parentA.frictionStatic, parentB.frictionStatic);
- pair.restitution = Math.max(parentA.restitution, parentB.restitution);
- pair.slop = Math.max(parentA.slop, parentB.slop);
- activeContacts.length = 0;
-
- if (collision.collided) {
- for (var i = 0; i < supports.length; i++) {
- var support = supports[i],
- contactId = Contact.id(support),
- contact = contacts[contactId];
-
- if (contact) {
- activeContacts.push(contact);
- } else {
- activeContacts.push(contacts[contactId] = Contact.create(support));
- }
- }
-
- pair.separation = collision.depth;
- Pair.setActive(pair, true, timestamp);
- } else {
- if (pair.isActive === true)
- Pair.setActive(pair, false, timestamp);
- }
- };
-
- /**
- * Set a pair as active or inactive.
- * @method setActive
- * @param {pair} pair
- * @param {bool} isActive
- * @param {number} timestamp
- */
- Pair.setActive = function(pair, isActive, timestamp) {
- if (isActive) {
- pair.isActive = true;
- pair.timeUpdated = timestamp;
- } else {
- pair.isActive = false;
- pair.activeContacts.length = 0;
- }
- };
-
- /**
- * Get the id for the given pair.
- * @method id
- * @param {body} bodyA
- * @param {body} bodyB
- * @return {string} Unique pairId
- */
- Pair.id = function(bodyA, bodyB) {
- if (bodyA.id < bodyB.id) {
- return 'A' + bodyA.id + 'B' + bodyB.id;
- } else {
- return 'A' + bodyB.id + 'B' + bodyA.id;
- }
- };
-
-})();
-
-
-/***/ }),
-/* 10 */
+/* 11 */
/***/ (function(module, exports, __webpack_require__) {
/**
@@ -4437,7 +4897,7 @@ var Common = __webpack_require__(0);
/***/ }),
-/* 11 */
+/* 12 */
/***/ (function(module, exports, __webpack_require__) {
/**
@@ -4801,7 +5261,7 @@ var Vector = __webpack_require__(2);
/***/ }),
-/* 12 */
+/* 13 */
/***/ (function(module, exports, __webpack_require__) {
/**
@@ -5008,72 +5468,137 @@ var Common = __webpack_require__(0);
/***/ }),
-/* 13 */
+/* 14 */
/***/ (function(module, exports, __webpack_require__) {
/**
-* The `Matter.Detector` module contains methods for detecting collisions given a set of pairs.
+* The `Matter.Detector` module contains methods for efficiently detecting collisions between a list of bodies using a broadphase algorithm.
*
* @class Detector
*/
-// TODO: speculative contacts
-
var Detector = {};
module.exports = Detector;
-var SAT = __webpack_require__(14);
-var Pair = __webpack_require__(9);
-var Bounds = __webpack_require__(1);
+var Common = __webpack_require__(0);
+var Collision = __webpack_require__(8);
(function() {
/**
- * Finds all collisions given a list of pairs.
- * @method collisions
- * @param {pair[]} broadphasePairs
- * @param {engine} engine
- * @return {array} collisions
+ * Creates a new collision detector.
+ * @method create
+ * @param {} options
+ * @return {detector} A new collision detector
*/
- Detector.collisions = function(broadphasePairs, engine) {
+ Detector.create = function(options) {
+ var defaults = {
+ bodies: [],
+ pairs: null
+ };
+
+ return Common.extend(defaults, options);
+ };
+
+ /**
+ * Sets the list of bodies in the detector.
+ * @method setBodies
+ * @param {detector} detector
+ * @param {body[]} bodies
+ */
+ Detector.setBodies = function(detector, bodies) {
+ detector.bodies = bodies.slice(0);
+ };
+
+ /**
+ * Clears the detector including its list of bodies.
+ * @method clear
+ * @param {detector} detector
+ */
+ Detector.clear = function(detector) {
+ detector.bodies = [];
+ };
+
+ /**
+ * Efficiently finds all collisions among all the bodies in `detector.bodies` using a broadphase algorithm.
+ *
+ * _Note:_ The specific ordering of collisions returned is not guaranteed between releases and may change for performance reasons.
+ * If a specific ordering is required then apply a sort to the resulting array.
+ * @method collisions
+ * @param {detector} detector
+ * @return {collision[]} collisions
+ */
+ Detector.collisions = function(detector) {
var collisions = [],
- pairsTable = engine.pairs.table;
+ pairs = detector.pairs,
+ bodies = detector.bodies,
+ bodiesLength = bodies.length,
+ canCollide = Detector.canCollide,
+ collides = Collision.collides,
+ i,
+ j;
- for (var i = 0; i < broadphasePairs.length; i++) {
- var bodyA = broadphasePairs[i][0],
- bodyB = broadphasePairs[i][1];
+ bodies.sort(Detector._compareBoundsX);
- if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping))
- continue;
-
- if (!Detector.canCollide(bodyA.collisionFilter, bodyB.collisionFilter))
- continue;
+ for (i = 0; i < bodiesLength; i++) {
+ var bodyA = bodies[i],
+ boundsA = bodyA.bounds,
+ boundXMax = bodyA.bounds.max.x,
+ boundYMax = bodyA.bounds.max.y,
+ boundYMin = bodyA.bounds.min.y,
+ bodyAStatic = bodyA.isStatic || bodyA.isSleeping,
+ partsALength = bodyA.parts.length,
+ partsASingle = partsALength === 1;
- // mid phase
- if (Bounds.overlaps(bodyA.bounds, bodyB.bounds)) {
- for (var j = bodyA.parts.length > 1 ? 1 : 0; j < bodyA.parts.length; j++) {
- var partA = bodyA.parts[j];
+ for (j = i + 1; j < bodiesLength; j++) {
+ var bodyB = bodies[j],
+ boundsB = bodyB.bounds;
- for (var k = bodyB.parts.length > 1 ? 1 : 0; k < bodyB.parts.length; k++) {
- var partB = bodyB.parts[k];
+ if (boundsB.min.x > boundXMax) {
+ break;
+ }
- if ((partA === bodyA && partB === bodyB) || Bounds.overlaps(partA.bounds, partB.bounds)) {
- // find a previous collision we could reuse
- var pairId = Pair.id(partA, partB),
- pair = pairsTable[pairId],
- previousCollision;
+ if (boundYMax < boundsB.min.y || boundYMin > boundsB.max.y) {
+ continue;
+ }
- if (pair && pair.isActive) {
- previousCollision = pair.collision;
- } else {
- previousCollision = null;
+ if (bodyAStatic && (bodyB.isStatic || bodyB.isSleeping)) {
+ continue;
+ }
+
+ if (!canCollide(bodyA.collisionFilter, bodyB.collisionFilter)) {
+ continue;
+ }
+
+ var partsBLength = bodyB.parts.length;
+
+ if (partsASingle && partsBLength === 1) {
+ var collision = collides(bodyA, bodyB, pairs);
+
+ if (collision) {
+ collisions.push(collision);
+ }
+ } else {
+ var partsAStart = partsALength > 1 ? 1 : 0,
+ partsBStart = partsBLength > 1 ? 1 : 0;
+
+ for (var k = partsAStart; k < partsALength; k++) {
+ var partA = bodyA.parts[k],
+ boundsA = partA.bounds;
+
+ for (var z = partsBStart; z < partsBLength; z++) {
+ var partB = bodyB.parts[z],
+ boundsB = partB.bounds;
+
+ if (boundsA.min.x > boundsB.max.x || boundsA.max.x < boundsB.min.x
+ || boundsA.max.y < boundsB.min.y || boundsA.min.y > boundsB.max.y) {
+ continue;
}
- // narrow phase
- var collision = SAT.collides(partA, partB, previousCollision);
+ var collision = collides(partA, partB, pairs);
- if (collision.collided) {
+ if (collision) {
collisions.push(collision);
}
}
@@ -5100,281 +5625,40 @@ var Bounds = __webpack_require__(1);
return (filterA.mask & filterB.category) !== 0 && (filterB.mask & filterA.category) !== 0;
};
-})();
-
-
-/***/ }),
-/* 14 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/**
-* The `Matter.SAT` module contains methods for detecting collisions using the Separating Axis Theorem.
-*
-* @class SAT
-*/
-
-// TODO: true circles and curves
-
-var SAT = {};
-
-module.exports = SAT;
-
-var Vertices = __webpack_require__(3);
-var Vector = __webpack_require__(2);
-
-(function() {
-
/**
- * Detect collision between two bodies using the Separating Axis Theorem.
- * @method collides
+ * The comparison function used in the broadphase algorithm.
+ * Returns the signed delta of the bodies bounds on the x-axis.
+ * @private
+ * @method _sortCompare
* @param {body} bodyA
* @param {body} bodyB
- * @param {collision} previousCollision
- * @return {collision} collision
+ * @return {number} The signed delta used for sorting
*/
- SAT.collides = function(bodyA, bodyB, previousCollision) {
- var overlapAB,
- overlapBA,
- minOverlap,
- collision,
- canReusePrevCol = false;
-
- if (previousCollision) {
- // estimate total motion
- var parentA = bodyA.parent,
- parentB = bodyB.parent,
- motion = parentA.speed * parentA.speed + parentA.angularSpeed * parentA.angularSpeed
- + parentB.speed * parentB.speed + parentB.angularSpeed * parentB.angularSpeed;
-
- // we may be able to (partially) reuse collision result
- // but only safe if collision was resting
- canReusePrevCol = previousCollision && previousCollision.collided && motion < 0.2;
-
- // reuse collision object
- collision = previousCollision;
- } else {
- collision = { collided: false, bodyA: bodyA, bodyB: bodyB };
- }
-
- if (previousCollision && canReusePrevCol) {
- // if we can reuse the collision result
- // we only need to test the previously found axis
- var axisBodyA = collision.axisBody,
- axisBodyB = axisBodyA === bodyA ? bodyB : bodyA,
- axes = [axisBodyA.axes[previousCollision.axisNumber]];
-
- minOverlap = SAT._overlapAxes(axisBodyA.vertices, axisBodyB.vertices, axes);
- collision.reused = true;
-
- if (minOverlap.overlap <= 0) {
- collision.collided = false;
- return collision;
- }
- } else {
- // if we can't reuse a result, perform a full SAT test
-
- overlapAB = SAT._overlapAxes(bodyA.vertices, bodyB.vertices, bodyA.axes);
-
- if (overlapAB.overlap <= 0) {
- collision.collided = false;
- return collision;
- }
-
- overlapBA = SAT._overlapAxes(bodyB.vertices, bodyA.vertices, bodyB.axes);
-
- if (overlapBA.overlap <= 0) {
- collision.collided = false;
- return collision;
- }
-
- if (overlapAB.overlap < overlapBA.overlap) {
- minOverlap = overlapAB;
- collision.axisBody = bodyA;
- } else {
- minOverlap = overlapBA;
- collision.axisBody = bodyB;
- }
-
- // important for reuse later
- collision.axisNumber = minOverlap.axisNumber;
- }
-
- collision.bodyA = bodyA.id < bodyB.id ? bodyA : bodyB;
- collision.bodyB = bodyA.id < bodyB.id ? bodyB : bodyA;
- collision.collided = true;
- collision.depth = minOverlap.overlap;
- collision.parentA = collision.bodyA.parent;
- collision.parentB = collision.bodyB.parent;
-
- bodyA = collision.bodyA;
- bodyB = collision.bodyB;
-
- // ensure normal is facing away from bodyA
- if (Vector.dot(minOverlap.axis, Vector.sub(bodyB.position, bodyA.position)) < 0) {
- collision.normal = {
- x: minOverlap.axis.x,
- y: minOverlap.axis.y
- };
- } else {
- collision.normal = {
- x: -minOverlap.axis.x,
- y: -minOverlap.axis.y
- };
- }
-
- collision.tangent = Vector.perp(collision.normal);
-
- collision.penetration = collision.penetration || {};
- collision.penetration.x = collision.normal.x * collision.depth;
- collision.penetration.y = collision.normal.y * collision.depth;
-
- // find support points, there is always either exactly one or two
- var verticesB = SAT._findSupports(bodyA, bodyB, collision.normal),
- supports = [];
-
- // 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 = SAT._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 < 1)
- supports = [verticesB[0]];
-
- collision.supports = supports;
-
- return collision;
+ Detector._compareBoundsX = function(bodyA, bodyB) {
+ return bodyA.bounds.min.x - bodyB.bounds.min.x;
};
+ /*
+ *
+ * Properties Documentation
+ *
+ */
+
/**
- * Find the overlap between two sets of vertices.
- * @method _overlapAxes
- * @private
- * @param {} verticesA
- * @param {} verticesB
- * @param {} axes
- * @return result
+ * The array of `Matter.Body` between which the detector finds collisions.
+ *
+ * _Note:_ The order of bodies in this array _is not fixed_ and will be continually managed by the detector.
+ * @property bodies
+ * @type body[]
+ * @default []
*/
- SAT._overlapAxes = function(verticesA, verticesB, axes) {
- var projectionA = Vector._temp[0],
- projectionB = Vector._temp[1],
- result = { overlap: Number.MAX_VALUE },
- overlap,
- axis;
-
- for (var i = 0; i < axes.length; i++) {
- axis = axes[i];
-
- SAT._projectToAxis(projectionA, verticesA, axis);
- SAT._projectToAxis(projectionB, verticesB, axis);
-
- overlap = Math.min(projectionA.max - projectionB.min, projectionB.max - projectionA.min);
-
- if (overlap <= 0) {
- result.overlap = overlap;
- return result;
- }
-
- if (overlap < result.overlap) {
- result.overlap = overlap;
- result.axis = axis;
- result.axisNumber = i;
- }
- }
-
- return result;
- };
/**
- * Projects vertices on an axis and returns an interval.
- * @method _projectToAxis
- * @private
- * @param {} projection
- * @param {} vertices
- * @param {} axis
+ * Optional. A `Matter.Pairs` object from which previous collision objects may be reused. Intended for internal `Matter.Engine` usage.
+ * @property pairs
+ * @type {pairs|null}
+ * @default null
*/
- SAT._projectToAxis = function(projection, vertices, axis) {
- var min = Vector.dot(vertices[0], axis),
- max = min;
-
- for (var i = 1; i < vertices.length; i += 1) {
- var dot = Vector.dot(vertices[i], axis);
-
- if (dot > max) {
- max = dot;
- } else if (dot < min) {
- min = dot;
- }
- }
-
- projection.min = min;
- projection.max = max;
- };
-
- /**
- * Finds supporting vertices given two bodies along a given direction using hill-climbing.
- * @method _findSupports
- * @private
- * @param {} bodyA
- * @param {} bodyB
- * @param {} normal
- * @return [vector]
- */
- SAT._findSupports = function(bodyA, bodyB, normal) {
- var nearestDistance = Number.MAX_VALUE,
- vertexToBody = Vector._temp[0],
- vertices = bodyB.vertices,
- bodyAPosition = bodyA.position,
- distance,
- vertex,
- vertexA,
- vertexB;
-
- // find closest vertex on bodyB
- for (var i = 0; i < vertices.length; i++) {
- vertex = vertices[i];
- vertexToBody.x = vertex.x - bodyAPosition.x;
- vertexToBody.y = vertex.y - bodyAPosition.y;
- distance = -Vector.dot(normal, vertexToBody);
-
- if (distance < nearestDistance) {
- nearestDistance = distance;
- vertexA = vertex;
- }
- }
-
- // find next closest vertex using the two connected to it
- var prevIndex = vertexA.index - 1 >= 0 ? vertexA.index - 1 : vertices.length - 1;
- vertex = vertices[prevIndex];
- vertexToBody.x = vertex.x - bodyAPosition.x;
- vertexToBody.y = vertex.y - bodyAPosition.y;
- nearestDistance = -Vector.dot(normal, vertexToBody);
- vertexB = vertex;
-
- var nextIndex = (vertexA.index + 1) % vertices.length;
- vertex = vertices[nextIndex];
- vertexToBody.x = vertex.x - bodyAPosition.x;
- vertexToBody.y = vertex.y - bodyAPosition.y;
- distance = -Vector.dot(normal, vertexToBody);
- if (distance < nearestDistance) {
- vertexB = vertex;
- }
-
- return [vertexA, vertexB];
- };
})();
@@ -5625,7 +5909,7 @@ var Common = __webpack_require__(0);
*/
Plugin.dependencyParse = function(dependency) {
if (Common.isString(dependency)) {
- var pattern = /^[\w-]+(@(\*|[\^~]?\d+\.\d+\.\d+(-[0-9A-Za-z-]+)?))?$/;
+ var pattern = /^[\w-]+(@(\*|[\^~]?\d+\.\d+\.\d+(-[0-9A-Za-z-+]+)?))?$/;
if (!pattern.test(dependency)) {
Common.warn('Plugin.dependencyParse:', dependency, 'is not a valid dependency string.');
@@ -5660,7 +5944,7 @@ var Common = __webpack_require__(0);
* @return {object} The version range parsed into its components.
*/
Plugin.versionParse = function(range) {
- var pattern = /^(\*)|(\^|~|>=|>)?\s*((\d+)\.(\d+)\.(\d+))(-[0-9A-Za-z-]+)?$/;
+ var pattern = /^(\*)|(\^|~|>=|>)?\s*((\d+)\.(\d+)\.(\d+))(-[0-9A-Za-z-+]+)?$/;
if (!pattern.test(range)) {
Common.warn('Plugin.versionParse:', range, 'is not a valid version or range.');
@@ -5757,7 +6041,7 @@ var Composite = __webpack_require__(5);
var Bounds = __webpack_require__(1);
var Events = __webpack_require__(4);
var Vector = __webpack_require__(2);
-var Mouse = __webpack_require__(12);
+var Mouse = __webpack_require__(13);
(function() {
@@ -5818,7 +6102,6 @@ var Mouse = __webpack_require__(12);
showDebug: false,
showStats: false,
showPerformance: false,
- showBroadphase: false,
showBounds: false,
showVelocity: false,
showCollisions: false,
@@ -5858,6 +6141,9 @@ var Mouse = __webpack_require__(12);
}
};
+ // for temporary back compatibility only
+ render.options.showBroadphase = false;
+
if (render.options.pixelRatio !== 1) {
Render.setPixelRatio(render, render.options.pixelRatio);
}
@@ -6178,9 +6464,6 @@ var Mouse = __webpack_require__(12);
Render.constraints(constraints, context);
- if (options.showBroadphase)
- Render.grid(render, engine.grid, context);
-
if (options.hasBounds) {
// revert view transforms
Render.endViewTransform(render);
@@ -7027,45 +7310,6 @@ var Mouse = __webpack_require__(12);
c.stroke();
};
- /**
- * Description
- * @private
- * @method grid
- * @param {render} render
- * @param {grid} grid
- * @param {RenderingContext} context
- */
- Render.grid = function(render, grid, context) {
- var c = context,
- options = render.options;
-
- if (options.wireframes) {
- c.strokeStyle = 'rgba(255,180,0,0.1)';
- } else {
- c.strokeStyle = 'rgba(255,180,0,0.5)';
- }
-
- c.beginPath();
-
- var bucketKeys = Common.keys(grid.buckets);
-
- for (var i = 0; i < bucketKeys.length; i++) {
- var bucketId = bucketKeys[i];
-
- if (grid.buckets[bucketId].length < 2)
- continue;
-
- var region = bucketId.split(/C|R/);
- c.rect(0.5 + parseInt(region[1], 10) * grid.bucketWidth,
- 0.5 + parseInt(region[2], 10) * grid.bucketHeight,
- grid.bucketWidth,
- grid.bucketHeight);
- }
-
- c.lineWidth = 1;
- c.stroke();
- };
-
/**
* Description
* @private
@@ -7507,6 +7751,7 @@ var Mouse = __webpack_require__(12);
/**
* A flag to enable or disable the collision broadphase debug overlay.
*
+ * @deprecated no longer implemented
* @property options.showBroadphase
* @type boolean
* @default false
@@ -7635,22 +7880,11 @@ module.exports = Contact;
*/
Contact.create = function(vertex) {
return {
- id: Contact.id(vertex),
vertex: vertex,
normalImpulse: 0,
tangentImpulse: 0
};
};
-
- /**
- * Generates a contact id.
- * @method id
- * @param {vertex} vertex
- * @return {string} Unique contactID
- */
- Contact.id = function(vertex) {
- return vertex.body.id + '_' + vertex.index;
- };
})();
@@ -7675,12 +7909,11 @@ module.exports = Engine;
var Sleeping = __webpack_require__(7);
var Resolver = __webpack_require__(19);
-var Detector = __webpack_require__(13);
+var Detector = __webpack_require__(14);
var Pairs = __webpack_require__(20);
-var Grid = __webpack_require__(21);
var Events = __webpack_require__(4);
var Composite = __webpack_require__(5);
-var Constraint = __webpack_require__(8);
+var Constraint = __webpack_require__(10);
var Common = __webpack_require__(0);
var Body = __webpack_require__(6);
@@ -7704,7 +7937,6 @@ var Body = __webpack_require__(6);
enableSleeping: false,
events: [],
plugin: {},
- grid: null,
gravity: {
x: 0,
y: 1,
@@ -7721,10 +7953,11 @@ var Body = __webpack_require__(6);
var engine = Common.extend(defaults, options);
engine.world = options.world || Composite.create({ label: 'World' });
- engine.grid = Grid.create(options.grid || options.broadphase);
- engine.pairs = Pairs.create();
+ engine.pairs = options.pairs || Pairs.create();
+ engine.detector = options.detector || Detector.create();
- // temporary back compatibility
+ // for temporary back compatibility only
+ engine.grid = { buckets: [] };
engine.world.gravity = engine.gravity;
engine.broadphase = engine.grid;
engine.metrics = {};
@@ -7754,9 +7987,10 @@ var Body = __webpack_require__(6);
correction = correction || 1;
var world = engine.world,
+ detector = engine.detector,
+ pairs = engine.pairs,
timing = engine.timing,
- grid = engine.grid,
- gridPairs = [],
+ timestamp = timing.timestamp,
i;
// increment timestamp
@@ -7770,15 +8004,25 @@ var Body = __webpack_require__(6);
Events.trigger(engine, 'beforeUpdate', event);
- // get lists of all bodies and constraints, no matter what composites they are in
+ // get all bodies and all constraints in the world
var allBodies = Composite.allBodies(world),
allConstraints = Composite.allConstraints(world);
- // if sleeping enabled, call the sleeping controller
+ // update the detector bodies if they have changed
+ if (world.isModified) {
+ Detector.setBodies(detector, allBodies);
+ }
+
+ // reset all composite modified flags
+ if (world.isModified) {
+ Composite.setModified(world, false, false, true);
+ }
+
+ // update sleeping if enabled
if (engine.enableSleeping)
Sleeping.update(allBodies, timing.timeScale);
- // applies gravity to all bodies
+ // apply gravity to all bodies
Engine._bodiesApplyGravity(allBodies, engine.gravity);
// update all body position and rotation by integration
@@ -7791,29 +8035,12 @@ var Body = __webpack_require__(6);
}
Constraint.postSolveAll(allBodies);
- // broadphase pass: find potential collision pairs
-
- // if world is dirty, we must flush the whole grid
- if (world.isModified)
- Grid.clear(grid);
-
- // update the grid buckets based on current bodies
- Grid.update(grid, allBodies, engine, world.isModified);
- gridPairs = grid.pairsList;
-
- // clear all composite modified flags
- if (world.isModified) {
- Composite.setModified(world, false, false, true);
- }
-
- // narrowphase pass: find actual collisions, then create or update collision pairs
- var collisions = Detector.collisions(gridPairs, engine);
+ // find all collisions
+ detector.pairs = engine.pairs;
+ var collisions = Detector.collisions(detector);
// update collision pairs
- var pairs = engine.pairs,
- timestamp = timing.timestamp;
Pairs.update(pairs, collisions, timestamp);
- Pairs.removeOld(pairs, timestamp);
// wake up bodies involved in collisions
if (engine.enableSleeping)
@@ -7886,17 +8113,13 @@ var Body = __webpack_require__(6);
};
/**
- * Clears the engine including the world, pairs and broadphase.
+ * Clears the engine pairs and detector.
* @method clear
* @param {engine} engine
*/
Engine.clear = function(engine) {
- var world = engine.world,
- bodies = Composite.allBodies(world);
-
Pairs.clear(engine.pairs);
- Grid.clear(engine.grid);
- Grid.update(engine.grid, bodies, engine, true);
+ Detector.clear(engine.detector);
};
/**
@@ -7976,53 +8199,53 @@ var Body = __webpack_require__(6);
* Fired just before an update
*
* @event beforeUpdate
- * @param {} event An event object
+ * @param {object} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
- * @param {} event.source The source object of the event
- * @param {} event.name The name of the event
+ * @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 {} event An event object
+ * @param {object} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
- * @param {} event.source The source object of the event
- * @param {} event.name The name of the event
+ * @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 {} event An event object
- * @param {} event.pairs List of affected pairs
+ * @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 {} event.source The source object of the event
- * @param {} event.name The name of the event
+ * @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 {} event An event object
- * @param {} event.pairs List of affected pairs
+ * @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 {} event.source The source object of the event
- * @param {} event.name The name of the event
+ * @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 {} event An event object
- * @param {} event.pairs List of affected pairs
+ * @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 {} event.source The source object of the event
- * @param {} event.name The name of the event
+ * @param {engine} event.source The source object of the event
+ * @param {string} event.name The name of the event
*/
/*
@@ -8114,9 +8337,18 @@ var Body = __webpack_require__(6);
* @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
@@ -8125,7 +8357,7 @@ var Body = __webpack_require__(6);
/**
* Replaced by and now alias for `engine.grid`.
*
- * @deprecated use `engine.grid`
+ * @deprecated replaced by `engine.detector`
* @property broadphase
* @type grid
* @default a Matter.Grid instance
@@ -8195,8 +8427,6 @@ var Resolver = {};
module.exports = Resolver;
var Vertices = __webpack_require__(3);
-var Vector = __webpack_require__(2);
-var Common = __webpack_require__(0);
var Bounds = __webpack_require__(1);
(function() {
@@ -8215,10 +8445,11 @@ var Bounds = __webpack_require__(1);
Resolver.preSolvePosition = function(pairs) {
var i,
pair,
- activeCount;
+ activeCount,
+ pairsLength = pairs.length;
// find total contacts on each body
- for (i = 0; i < pairs.length; i++) {
+ for (i = 0; i < pairsLength; i++) {
pair = pairs[i];
if (!pair.isActive)
@@ -8243,17 +8474,13 @@ var Bounds = __webpack_require__(1);
bodyA,
bodyB,
normal,
- bodyBtoA,
contactShare,
positionImpulse,
- contactCount = {},
- tempA = Vector._temp[0],
- tempB = Vector._temp[1],
- tempC = Vector._temp[2],
- tempD = Vector._temp[3];
+ positionDampen = Resolver._positionDampen,
+ pairsLength = pairs.length;
// find impulses required to resolve penetration
- for (i = 0; i < pairs.length; i++) {
+ for (i = 0; i < pairsLength; i++) {
pair = pairs[i];
if (!pair.isActive || pair.isSensor)
@@ -8265,14 +8492,12 @@ var Bounds = __webpack_require__(1);
normal = collision.normal;
// get current separation between body edges involved in collision
- bodyBtoA = Vector.sub(Vector.add(bodyB.positionImpulse, bodyB.position, tempA),
- Vector.add(bodyA.positionImpulse,
- Vector.sub(bodyB.position, collision.penetration, tempB), tempC), tempD);
-
- pair.separation = Vector.dot(normal, bodyBtoA);
+ pair.separation =
+ normal.x * (bodyB.positionImpulse.x + collision.penetration.x - bodyA.positionImpulse.x)
+ + normal.y * (bodyB.positionImpulse.y + collision.penetration.y - bodyA.positionImpulse.y);
}
- for (i = 0; i < pairs.length; i++) {
+ for (i = 0; i < pairsLength; i++) {
pair = pairs[i];
if (!pair.isActive || pair.isSensor)
@@ -8288,13 +8513,13 @@ var Bounds = __webpack_require__(1);
positionImpulse *= 2;
if (!(bodyA.isStatic || bodyA.isSleeping)) {
- contactShare = Resolver._positionDampen / bodyA.totalContacts;
+ contactShare = positionDampen / bodyA.totalContacts;
bodyA.positionImpulse.x += normal.x * positionImpulse * contactShare;
bodyA.positionImpulse.y += normal.y * positionImpulse * contactShare;
}
if (!(bodyB.isStatic || bodyB.isSleeping)) {
- contactShare = Resolver._positionDampen / bodyB.totalContacts;
+ contactShare = positionDampen / bodyB.totalContacts;
bodyB.positionImpulse.x -= normal.x * positionImpulse * contactShare;
bodyB.positionImpulse.y -= normal.y * positionImpulse * contactShare;
}
@@ -8307,34 +8532,43 @@ var Bounds = __webpack_require__(1);
* @param {body[]} bodies
*/
Resolver.postSolvePosition = function(bodies) {
- for (var i = 0; i < bodies.length; i++) {
- var body = bodies[i];
+ var positionWarming = Resolver._positionWarming,
+ bodiesLength = bodies.length,
+ verticesTranslate = Vertices.translate,
+ boundsUpdate = Bounds.update;
+
+ for (var i = 0; i < bodiesLength; i++) {
+ var body = bodies[i],
+ positionImpulse = body.positionImpulse,
+ positionImpulseX = positionImpulse.x,
+ positionImpulseY = positionImpulse.y,
+ velocity = body.velocity;
// reset contact count
body.totalContacts = 0;
- if (body.positionImpulse.x !== 0 || body.positionImpulse.y !== 0) {
+ if (positionImpulseX !== 0 || positionImpulseY !== 0) {
// update body geometry
for (var j = 0; j < body.parts.length; j++) {
var part = body.parts[j];
- Vertices.translate(part.vertices, body.positionImpulse);
- Bounds.update(part.bounds, part.vertices, body.velocity);
- part.position.x += body.positionImpulse.x;
- part.position.y += body.positionImpulse.y;
+ verticesTranslate(part.vertices, positionImpulse);
+ boundsUpdate(part.bounds, part.vertices, velocity);
+ part.position.x += positionImpulseX;
+ part.position.y += positionImpulseY;
}
// move the body without changing velocity
- body.positionPrev.x += body.positionImpulse.x;
- body.positionPrev.y += body.positionImpulse.y;
+ body.positionPrev.x += positionImpulseX;
+ body.positionPrev.y += positionImpulseY;
- if (Vector.dot(body.positionImpulse, body.velocity) < 0) {
+ if (positionImpulseX * velocity.x + positionImpulseY * velocity.y < 0) {
// reset cached impulse if the body has velocity along it
- body.positionImpulse.x = 0;
- body.positionImpulse.y = 0;
+ positionImpulse.x = 0;
+ positionImpulse.y = 0;
} else {
// warm the next iteration
- body.positionImpulse.x *= Resolver._positionWarming;
- body.positionImpulse.y *= Resolver._positionWarming;
+ positionImpulse.x *= positionWarming;
+ positionImpulse.y *= positionWarming;
}
}
}
@@ -8346,61 +8580,53 @@ var Bounds = __webpack_require__(1);
* @param {pair[]} pairs
*/
Resolver.preSolveVelocity = function(pairs) {
- var i,
- j,
- pair,
- contacts,
- collision,
- bodyA,
- bodyB,
- normal,
- tangent,
- contact,
- contactVertex,
- normalImpulse,
- tangentImpulse,
- offset,
- impulse = Vector._temp[0],
- tempA = Vector._temp[1];
+ var pairsLength = pairs.length,
+ i,
+ j;
- for (i = 0; i < pairs.length; i++) {
- pair = pairs[i];
+ for (i = 0; i < pairsLength; i++) {
+ var pair = pairs[i];
if (!pair.isActive || pair.isSensor)
continue;
- contacts = pair.activeContacts;
- collision = pair.collision;
- bodyA = collision.parentA;
- bodyB = collision.parentB;
- normal = collision.normal;
- tangent = collision.tangent;
-
+ var contacts = pair.activeContacts,
+ contactsLength = contacts.length,
+ collision = pair.collision,
+ bodyA = collision.parentA,
+ bodyB = collision.parentB,
+ normal = collision.normal,
+ tangent = collision.tangent;
+
// resolve each contact
- for (j = 0; j < contacts.length; j++) {
- contact = contacts[j];
- contactVertex = contact.vertex;
- normalImpulse = contact.normalImpulse;
- tangentImpulse = contact.tangentImpulse;
-
+ for (j = 0; j < contactsLength; j++) {
+ var contact = contacts[j],
+ contactVertex = contact.vertex,
+ normalImpulse = contact.normalImpulse,
+ tangentImpulse = contact.tangentImpulse;
+
if (normalImpulse !== 0 || tangentImpulse !== 0) {
// total impulse from contact
- impulse.x = (normal.x * normalImpulse) + (tangent.x * tangentImpulse);
- impulse.y = (normal.y * normalImpulse) + (tangent.y * tangentImpulse);
+ var impulseX = normal.x * normalImpulse + tangent.x * tangentImpulse,
+ impulseY = normal.y * normalImpulse + tangent.y * tangentImpulse;
// apply impulse from contact
if (!(bodyA.isStatic || bodyA.isSleeping)) {
- offset = Vector.sub(contactVertex, bodyA.position, tempA);
- bodyA.positionPrev.x += impulse.x * bodyA.inverseMass;
- bodyA.positionPrev.y += impulse.y * bodyA.inverseMass;
- bodyA.anglePrev += Vector.cross(offset, impulse) * bodyA.inverseInertia;
+ bodyA.positionPrev.x += impulseX * bodyA.inverseMass;
+ bodyA.positionPrev.y += impulseY * bodyA.inverseMass;
+ bodyA.anglePrev += bodyA.inverseInertia * (
+ (contactVertex.x - bodyA.position.x) * impulseY
+ - (contactVertex.y - bodyA.position.y) * impulseX
+ );
}
-
+
if (!(bodyB.isStatic || bodyB.isSleeping)) {
- offset = Vector.sub(contactVertex, bodyB.position, tempA);
- bodyB.positionPrev.x -= impulse.x * bodyB.inverseMass;
- bodyB.positionPrev.y -= impulse.y * bodyB.inverseMass;
- bodyB.anglePrev -= Vector.cross(offset, impulse) * bodyB.inverseInertia;
+ bodyB.positionPrev.x -= impulseX * bodyB.inverseMass;
+ bodyB.positionPrev.y -= impulseY * bodyB.inverseMass;
+ bodyB.anglePrev -= bodyB.inverseInertia * (
+ (contactVertex.x - bodyB.position.x) * impulseY
+ - (contactVertex.y - bodyB.position.y) * impulseX
+ );
}
}
}
@@ -8415,14 +8641,17 @@ var Bounds = __webpack_require__(1);
*/
Resolver.solveVelocity = function(pairs, timeScale) {
var timeScaleSquared = timeScale * timeScale,
- impulse = Vector._temp[0],
- tempA = Vector._temp[1],
- tempB = Vector._temp[2],
- tempC = Vector._temp[3],
- tempD = Vector._temp[4],
- tempE = Vector._temp[5];
-
- for (var i = 0; i < pairs.length; i++) {
+ restingThresh = Resolver._restingThresh * timeScaleSquared,
+ frictionNormalMultiplier = Resolver._frictionNormalMultiplier,
+ restingThreshTangent = Resolver._restingThreshTangent * timeScaleSquared,
+ NumberMaxValue = Number.MAX_VALUE,
+ pairsLength = pairs.length,
+ tangentImpulse,
+ maxFriction,
+ i,
+ j;
+
+ for (i = 0; i < pairsLength; i++) {
var pair = pairs[i];
if (!pair.isActive || pair.isSensor)
@@ -8431,97 +8660,119 @@ var Bounds = __webpack_require__(1);
var collision = pair.collision,
bodyA = collision.parentA,
bodyB = collision.parentB,
- normal = collision.normal,
- tangent = collision.tangent,
+ bodyAVelocity = bodyA.velocity,
+ bodyBVelocity = bodyB.velocity,
+ normalX = collision.normal.x,
+ normalY = collision.normal.y,
+ tangentX = collision.tangent.x,
+ tangentY = collision.tangent.y,
contacts = pair.activeContacts,
- contactShare = 1 / contacts.length;
+ contactsLength = contacts.length,
+ contactShare = 1 / contactsLength,
+ inverseMassTotal = bodyA.inverseMass + bodyB.inverseMass,
+ friction = pair.friction * pair.frictionStatic * frictionNormalMultiplier * timeScaleSquared;
// update body velocities
- bodyA.velocity.x = bodyA.position.x - bodyA.positionPrev.x;
- bodyA.velocity.y = bodyA.position.y - bodyA.positionPrev.y;
- bodyB.velocity.x = bodyB.position.x - bodyB.positionPrev.x;
- bodyB.velocity.y = bodyB.position.y - bodyB.positionPrev.y;
+ bodyAVelocity.x = bodyA.position.x - bodyA.positionPrev.x;
+ bodyAVelocity.y = bodyA.position.y - bodyA.positionPrev.y;
+ bodyBVelocity.x = bodyB.position.x - bodyB.positionPrev.x;
+ bodyBVelocity.y = bodyB.position.y - bodyB.positionPrev.y;
bodyA.angularVelocity = bodyA.angle - bodyA.anglePrev;
bodyB.angularVelocity = bodyB.angle - bodyB.anglePrev;
// resolve each contact
- for (var j = 0; j < contacts.length; j++) {
+ for (j = 0; j < contactsLength; j++) {
var contact = contacts[j],
- contactVertex = contact.vertex,
- offsetA = Vector.sub(contactVertex, bodyA.position, tempA),
- offsetB = Vector.sub(contactVertex, bodyB.position, tempB),
- velocityPointA = Vector.add(bodyA.velocity, Vector.mult(Vector.perp(offsetA), bodyA.angularVelocity), tempC),
- velocityPointB = Vector.add(bodyB.velocity, Vector.mult(Vector.perp(offsetB), bodyB.angularVelocity), tempD),
- relativeVelocity = Vector.sub(velocityPointA, velocityPointB, tempE),
- normalVelocity = Vector.dot(normal, relativeVelocity);
+ contactVertex = contact.vertex;
- var tangentVelocity = Vector.dot(tangent, relativeVelocity),
- tangentSpeed = Math.abs(tangentVelocity),
- tangentVelocityDirection = Common.sign(tangentVelocity);
+ var offsetAX = contactVertex.x - bodyA.position.x,
+ offsetAY = contactVertex.y - bodyA.position.y,
+ offsetBX = contactVertex.x - bodyB.position.x,
+ offsetBY = contactVertex.y - bodyB.position.y;
+
+ var velocityPointAX = bodyAVelocity.x - offsetAY * bodyA.angularVelocity,
+ velocityPointAY = bodyAVelocity.y + offsetAX * bodyA.angularVelocity,
+ velocityPointBX = bodyBVelocity.x - offsetBY * bodyB.angularVelocity,
+ velocityPointBY = bodyBVelocity.y + offsetBX * bodyB.angularVelocity;
- // raw impulses
- var normalImpulse = (1 + pair.restitution) * normalVelocity,
- normalForce = Common.clamp(pair.separation + normalVelocity, 0, 1) * Resolver._frictionNormalMultiplier;
+ var relativeVelocityX = velocityPointAX - velocityPointBX,
+ relativeVelocityY = velocityPointAY - velocityPointBY;
+
+ var normalVelocity = normalX * relativeVelocityX + normalY * relativeVelocityY,
+ tangentVelocity = tangentX * relativeVelocityX + tangentY * relativeVelocityY;
// coulomb friction
- var tangentImpulse = tangentVelocity,
- maxFriction = Infinity;
+ var normalOverlap = pair.separation + normalVelocity;
+ var normalForce = Math.min(normalOverlap, 1);
+ normalForce = normalOverlap < 0 ? 0 : normalForce;
+
+ var frictionLimit = normalForce * friction;
- if (tangentSpeed > pair.friction * pair.frictionStatic * normalForce * timeScaleSquared) {
- maxFriction = tangentSpeed;
- tangentImpulse = Common.clamp(
- pair.friction * tangentVelocityDirection * timeScaleSquared,
- -maxFriction, maxFriction
- );
+ if (tangentVelocity > frictionLimit || -tangentVelocity > frictionLimit) {
+ maxFriction = tangentVelocity > 0 ? tangentVelocity : -tangentVelocity;
+ tangentImpulse = pair.friction * (tangentVelocity > 0 ? 1 : -1) * timeScaleSquared;
+
+ if (tangentImpulse < -maxFriction) {
+ tangentImpulse = -maxFriction;
+ } else if (tangentImpulse > maxFriction) {
+ tangentImpulse = maxFriction;
+ }
+ } else {
+ tangentImpulse = tangentVelocity;
+ maxFriction = NumberMaxValue;
}
- // modify impulses accounting for mass, inertia and offset
- var oAcN = Vector.cross(offsetA, normal),
- oBcN = Vector.cross(offsetB, normal),
- share = contactShare / (bodyA.inverseMass + bodyB.inverseMass + bodyA.inverseInertia * oAcN * oAcN + bodyB.inverseInertia * oBcN * oBcN);
+ // account for mass, inertia and contact offset
+ var oAcN = offsetAX * normalY - offsetAY * normalX,
+ oBcN = offsetBX * normalY - offsetBY * normalX,
+ share = contactShare / (inverseMassTotal + bodyA.inverseInertia * oAcN * oAcN + bodyB.inverseInertia * oBcN * oBcN);
- normalImpulse *= share;
+ // raw impulses
+ var normalImpulse = (1 + pair.restitution) * normalVelocity * share;
tangentImpulse *= share;
// handle high velocity and resting collisions separately
- if (normalVelocity < 0 && normalVelocity * normalVelocity > Resolver._restingThresh * timeScaleSquared) {
+ if (normalVelocity * normalVelocity > restingThresh && normalVelocity < 0) {
// high normal velocity so clear cached contact normal impulse
contact.normalImpulse = 0;
} else {
// solve resting collision constraints using Erin Catto's method (GDC08)
// impulse constraint tends to 0
var contactNormalImpulse = contact.normalImpulse;
- contact.normalImpulse = Math.min(contact.normalImpulse + normalImpulse, 0);
+ contact.normalImpulse += normalImpulse;
+ contact.normalImpulse = Math.min(contact.normalImpulse, 0);
normalImpulse = contact.normalImpulse - contactNormalImpulse;
}
// handle high velocity and resting collisions separately
- if (tangentVelocity * tangentVelocity > Resolver._restingThreshTangent * timeScaleSquared) {
+ if (tangentVelocity * tangentVelocity > restingThreshTangent) {
// high tangent velocity so clear cached contact tangent impulse
contact.tangentImpulse = 0;
} else {
// solve resting collision constraints using Erin Catto's method (GDC08)
// tangent impulse tends to -tangentSpeed or +tangentSpeed
var contactTangentImpulse = contact.tangentImpulse;
- contact.tangentImpulse = Common.clamp(contact.tangentImpulse + tangentImpulse, -maxFriction, maxFriction);
+ contact.tangentImpulse += tangentImpulse;
+ if (contact.tangentImpulse < -maxFriction) contact.tangentImpulse = -maxFriction;
+ if (contact.tangentImpulse > maxFriction) contact.tangentImpulse = maxFriction;
tangentImpulse = contact.tangentImpulse - contactTangentImpulse;
}
// total impulse from contact
- impulse.x = (normal.x * normalImpulse) + (tangent.x * tangentImpulse);
- impulse.y = (normal.y * normalImpulse) + (tangent.y * tangentImpulse);
+ var impulseX = normalX * normalImpulse + tangentX * tangentImpulse,
+ impulseY = normalY * normalImpulse + tangentY * tangentImpulse;
// apply impulse from contact
if (!(bodyA.isStatic || bodyA.isSleeping)) {
- bodyA.positionPrev.x += impulse.x * bodyA.inverseMass;
- bodyA.positionPrev.y += impulse.y * bodyA.inverseMass;
- bodyA.anglePrev += Vector.cross(offsetA, impulse) * bodyA.inverseInertia;
+ bodyA.positionPrev.x += impulseX * bodyA.inverseMass;
+ bodyA.positionPrev.y += impulseY * bodyA.inverseMass;
+ bodyA.anglePrev += (offsetAX * impulseY - offsetAY * impulseX) * bodyA.inverseInertia;
}
if (!(bodyB.isStatic || bodyB.isSleeping)) {
- bodyB.positionPrev.x -= impulse.x * bodyB.inverseMass;
- bodyB.positionPrev.y -= impulse.y * bodyB.inverseMass;
- bodyB.anglePrev -= Vector.cross(offsetB, impulse) * bodyB.inverseInertia;
+ bodyB.positionPrev.x -= impulseX * bodyB.inverseMass;
+ bodyB.positionPrev.y -= impulseY * bodyB.inverseMass;
+ bodyB.anglePrev -= (offsetBX * impulseY - offsetBY * impulseX) * bodyB.inverseInertia;
}
}
}
@@ -8548,8 +8799,6 @@ var Pair = __webpack_require__(9);
var Common = __webpack_require__(0);
(function() {
-
- Pairs._pairMaxIdleLife = 1000;
/**
* Creates a new pairs structure.
@@ -8576,12 +8825,14 @@ var Common = __webpack_require__(0);
*/
Pairs.update = function(pairs, collisions, timestamp) {
var pairsList = pairs.list,
+ pairsListLength = pairsList.length,
pairsTable = pairs.table,
+ collisionsLength = collisions.length,
collisionStart = pairs.collisionStart,
collisionEnd = pairs.collisionEnd,
collisionActive = pairs.collisionActive,
collision,
- pairId,
+ pairIndex,
pair,
i;
@@ -8590,90 +8841,61 @@ var Common = __webpack_require__(0);
collisionEnd.length = 0;
collisionActive.length = 0;
- for (i = 0; i < pairsList.length; i++) {
+ for (i = 0; i < pairsListLength; i++) {
pairsList[i].confirmedActive = false;
}
- for (i = 0; i < collisions.length; i++) {
+ for (i = 0; i < collisionsLength; i++) {
collision = collisions[i];
+ pair = collision.pair;
- if (collision.collided) {
- pairId = Pair.id(collision.bodyA, collision.bodyB);
-
- pair = pairsTable[pairId];
-
- if (pair) {
- // pair already exists (but may or may not be active)
- if (pair.isActive) {
- // pair exists and is active
- collisionActive.push(pair);
- } else {
- // pair exists but was inactive, so a collision has just started again
- collisionStart.push(pair);
- }
-
- // update the pair
- Pair.update(pair, collision, timestamp);
- pair.confirmedActive = true;
+ if (pair) {
+ // pair already exists (but may or may not be active)
+ if (pair.isActive) {
+ // pair exists and is active
+ collisionActive.push(pair);
} else {
- // pair did not exist, create a new pair
- pair = Pair.create(collision, timestamp);
- pairsTable[pairId] = pair;
-
- // push the new pair
+ // pair exists but was inactive, so a collision has just started again
collisionStart.push(pair);
- pairsList.push(pair);
+ }
+
+ // update the pair
+ Pair.update(pair, collision, timestamp);
+ pair.confirmedActive = true;
+ } else {
+ // pair did not exist, create a new pair
+ pair = Pair.create(collision, timestamp);
+ pairsTable[pair.id] = pair;
+
+ // push the new pair
+ collisionStart.push(pair);
+ pairsList.push(pair);
+ }
+ }
+
+ // find pairs that are no longer active
+ var removePairIndex = [];
+ pairsListLength = pairsList.length;
+
+ for (i = 0; i < pairsListLength; i++) {
+ pair = pairsList[i];
+
+ if (!pair.confirmedActive) {
+ Pair.setActive(pair, false, timestamp);
+ collisionEnd.push(pair);
+
+ if (!pair.collision.bodyA.isSleeping && !pair.collision.bodyB.isSleeping) {
+ removePairIndex.push(i);
}
}
}
- // deactivate previously active pairs that are now inactive
- for (i = 0; i < pairsList.length; i++) {
- pair = pairsList[i];
- if (pair.isActive && !pair.confirmedActive) {
- Pair.setActive(pair, false, timestamp);
- collisionEnd.push(pair);
- }
- }
- };
-
- /**
- * Finds and removes pairs that have been inactive for a set amount of time.
- * @method removeOld
- * @param {object} pairs
- * @param {number} timestamp
- */
- Pairs.removeOld = function(pairs, timestamp) {
- var pairsList = pairs.list,
- pairsTable = pairs.table,
- indexesToRemove = [],
- pair,
- collision,
- pairIndex,
- i;
-
- for (i = 0; i < pairsList.length; i++) {
- pair = pairsList[i];
- collision = pair.collision;
-
- // never remove sleeping pairs
- if (collision.bodyA.isSleeping || collision.bodyB.isSleeping) {
- pair.timeUpdated = timestamp;
- continue;
- }
-
- // if pair is inactive for too long, mark it to be removed
- if (timestamp - pair.timeUpdated > Pairs._pairMaxIdleLife) {
- indexesToRemove.push(i);
- }
- }
-
- // remove marked pairs
- for (i = 0; i < indexesToRemove.length; i++) {
- pairIndex = indexesToRemove[i] - i;
+ // remove inactive pairs
+ for (i = 0; i < removePairIndex.length; i++) {
+ pairIndex = removePairIndex[i] - i;
pair = pairsList[pairIndex];
- delete pairsTable[pair.id];
pairsList.splice(pairIndex, 1);
+ delete pairsTable[pair.id];
}
};
@@ -8699,337 +8921,23 @@ var Common = __webpack_require__(0);
/* 21 */
/***/ (function(module, exports, __webpack_require__) {
-/**
-* The `Matter.Grid` module contains methods for creating and manipulating collision broadphase grid structures.
-*
-* @class Grid
-*/
-
-var Grid = {};
-
-module.exports = Grid;
-
-var Pair = __webpack_require__(9);
-var Common = __webpack_require__(0);
-
-(function() {
-
- /**
- * Creates a new grid.
- * @method create
- * @param {} options
- * @return {grid} A new grid
- */
- Grid.create = function(options) {
- var defaults = {
- buckets: {},
- pairs: {},
- pairsList: [],
- bucketWidth: 48,
- bucketHeight: 48
- };
-
- return Common.extend(defaults, options);
- };
-
- /**
- * The width of a single grid bucket.
- *
- * @property bucketWidth
- * @type number
- * @default 48
- */
-
- /**
- * The height of a single grid bucket.
- *
- * @property bucketHeight
- * @type number
- * @default 48
- */
-
- /**
- * Updates the grid.
- * @method update
- * @param {grid} grid
- * @param {body[]} bodies
- * @param {engine} engine
- * @param {boolean} forceUpdate
- */
- Grid.update = function(grid, bodies, engine, forceUpdate) {
- var i, col, row,
- world = engine.world,
- buckets = grid.buckets,
- bucket,
- bucketId,
- gridChanged = false;
-
- for (i = 0; i < bodies.length; i++) {
- var body = bodies[i];
-
- if (body.isSleeping && !forceUpdate)
- continue;
-
- // temporary back compatibility bounds check
- if (world.bounds && (body.bounds.max.x < world.bounds.min.x || body.bounds.min.x > world.bounds.max.x
- || body.bounds.max.y < world.bounds.min.y || body.bounds.min.y > world.bounds.max.y))
- continue;
-
- var newRegion = Grid._getRegion(grid, body);
-
- // if the body has changed grid region
- if (!body.region || newRegion.id !== body.region.id || forceUpdate) {
-
- if (!body.region || forceUpdate)
- body.region = newRegion;
-
- var union = Grid._regionUnion(newRegion, body.region);
-
- // update grid buckets affected by region change
- // iterate over the union of both regions
- for (col = union.startCol; col <= union.endCol; col++) {
- for (row = union.startRow; row <= union.endRow; row++) {
- bucketId = Grid._getBucketId(col, row);
- bucket = buckets[bucketId];
-
- var isInsideNewRegion = (col >= newRegion.startCol && col <= newRegion.endCol
- && row >= newRegion.startRow && row <= newRegion.endRow);
-
- var isInsideOldRegion = (col >= body.region.startCol && col <= body.region.endCol
- && row >= body.region.startRow && row <= body.region.endRow);
-
- // remove from old region buckets
- if (!isInsideNewRegion && isInsideOldRegion) {
- if (isInsideOldRegion) {
- if (bucket)
- Grid._bucketRemoveBody(grid, bucket, body);
- }
- }
-
- // add to new region buckets
- if (body.region === newRegion || (isInsideNewRegion && !isInsideOldRegion) || forceUpdate) {
- if (!bucket)
- bucket = Grid._createBucket(buckets, bucketId);
- Grid._bucketAddBody(grid, bucket, body);
- }
- }
- }
-
- // set the new region
- body.region = newRegion;
-
- // flag changes so we can update pairs
- gridChanged = true;
- }
- }
-
- // update pairs list only if pairs changed (i.e. a body changed region)
- if (gridChanged)
- grid.pairsList = Grid._createActivePairsList(grid);
- };
-
- /**
- * Clears the grid.
- * @method clear
- * @param {grid} grid
- */
- Grid.clear = function(grid) {
- grid.buckets = {};
- grid.pairs = {};
- grid.pairsList = [];
- };
-
- /**
- * Finds the union of two regions.
- * @method _regionUnion
- * @private
- * @param {} regionA
- * @param {} regionB
- * @return {} region
- */
- Grid._regionUnion = function(regionA, regionB) {
- var startCol = Math.min(regionA.startCol, regionB.startCol),
- endCol = Math.max(regionA.endCol, regionB.endCol),
- startRow = Math.min(regionA.startRow, regionB.startRow),
- endRow = Math.max(regionA.endRow, regionB.endRow);
-
- return Grid._createRegion(startCol, endCol, startRow, endRow);
- };
-
- /**
- * Gets the region a given body falls in for a given grid.
- * @method _getRegion
- * @private
- * @param {} grid
- * @param {} body
- * @return {} region
- */
- Grid._getRegion = function(grid, body) {
- var bounds = body.bounds,
- startCol = Math.floor(bounds.min.x / grid.bucketWidth),
- endCol = Math.floor(bounds.max.x / grid.bucketWidth),
- startRow = Math.floor(bounds.min.y / grid.bucketHeight),
- endRow = Math.floor(bounds.max.y / grid.bucketHeight);
-
- return Grid._createRegion(startCol, endCol, startRow, endRow);
- };
-
- /**
- * Creates a region.
- * @method _createRegion
- * @private
- * @param {} startCol
- * @param {} endCol
- * @param {} startRow
- * @param {} endRow
- * @return {} region
- */
- Grid._createRegion = function(startCol, endCol, startRow, endRow) {
- return {
- id: startCol + ',' + endCol + ',' + startRow + ',' + endRow,
- startCol: startCol,
- endCol: endCol,
- startRow: startRow,
- endRow: endRow
- };
- };
-
- /**
- * Gets the bucket id at the given position.
- * @method _getBucketId
- * @private
- * @param {} column
- * @param {} row
- * @return {string} bucket id
- */
- Grid._getBucketId = function(column, row) {
- return 'C' + column + 'R' + row;
- };
-
- /**
- * Creates a bucket.
- * @method _createBucket
- * @private
- * @param {} buckets
- * @param {} bucketId
- * @return {} bucket
- */
- Grid._createBucket = function(buckets, bucketId) {
- var bucket = buckets[bucketId] = [];
- return bucket;
- };
-
- /**
- * Adds a body to a bucket.
- * @method _bucketAddBody
- * @private
- * @param {} grid
- * @param {} bucket
- * @param {} body
- */
- Grid._bucketAddBody = function(grid, bucket, body) {
- // add new pairs
- for (var i = 0; i < bucket.length; i++) {
- var bodyB = bucket[i];
-
- if (body.id === bodyB.id || (body.isStatic && bodyB.isStatic))
- continue;
-
- // keep track of the number of buckets the pair exists in
- // important for Grid.update to work
- var pairId = Pair.id(body, bodyB),
- pair = grid.pairs[pairId];
-
- if (pair) {
- pair[2] += 1;
- } else {
- grid.pairs[pairId] = [body, bodyB, 1];
- }
- }
-
- // add to bodies (after pairs, otherwise pairs with self)
- bucket.push(body);
- };
-
- /**
- * Removes a body from a bucket.
- * @method _bucketRemoveBody
- * @private
- * @param {} grid
- * @param {} bucket
- * @param {} body
- */
- Grid._bucketRemoveBody = function(grid, bucket, body) {
- // remove from bucket
- bucket.splice(Common.indexOf(bucket, body), 1);
-
- // update pair counts
- for (var i = 0; i < bucket.length; i++) {
- // keep track of the number of buckets the pair exists in
- // important for _createActivePairsList to work
- var bodyB = bucket[i],
- pairId = Pair.id(body, bodyB),
- pair = grid.pairs[pairId];
-
- if (pair)
- pair[2] -= 1;
- }
- };
-
- /**
- * Generates a list of the active pairs in the grid.
- * @method _createActivePairsList
- * @private
- * @param {} grid
- * @return [] pairs
- */
- Grid._createActivePairsList = function(grid) {
- var pairKeys,
- pair,
- pairs = [];
-
- // grid.pairs is used as a hashmap
- pairKeys = Common.keys(grid.pairs);
-
- // iterate over grid.pairs
- for (var k = 0; k < pairKeys.length; k++) {
- pair = grid.pairs[pairKeys[k]];
-
- // if pair exists in at least one bucket
- // it is a pair that needs further collision testing so push it
- if (pair[2] > 0) {
- pairs.push(pair);
- } else {
- delete grid.pairs[pairKeys[k]];
- }
- }
-
- return pairs;
- };
-
-})();
-
-
-/***/ }),
-/* 22 */
-/***/ (function(module, exports, __webpack_require__) {
-
-var Matter = module.exports = __webpack_require__(23);
+var Matter = module.exports = __webpack_require__(22);
-Matter.Axes = __webpack_require__(10);
-Matter.Bodies = __webpack_require__(11);
+Matter.Axes = __webpack_require__(11);
+Matter.Bodies = __webpack_require__(12);
Matter.Body = __webpack_require__(6);
Matter.Bounds = __webpack_require__(1);
+Matter.Collision = __webpack_require__(8);
Matter.Common = __webpack_require__(0);
Matter.Composite = __webpack_require__(5);
-Matter.Composites = __webpack_require__(24);
-Matter.Constraint = __webpack_require__(8);
+Matter.Composites = __webpack_require__(23);
+Matter.Constraint = __webpack_require__(10);
Matter.Contact = __webpack_require__(17);
-Matter.Detector = __webpack_require__(13);
+Matter.Detector = __webpack_require__(14);
Matter.Engine = __webpack_require__(18);
Matter.Events = __webpack_require__(4);
-Matter.Grid = __webpack_require__(21);
-Matter.Mouse = __webpack_require__(12);
+Matter.Grid = __webpack_require__(24);
+Matter.Mouse = __webpack_require__(13);
Matter.MouseConstraint = __webpack_require__(25);
Matter.Pair = __webpack_require__(9);
Matter.Pairs = __webpack_require__(20);
@@ -9038,12 +8946,12 @@ Matter.Query = __webpack_require__(26);
Matter.Render = __webpack_require__(16);
Matter.Resolver = __webpack_require__(19);
Matter.Runner = __webpack_require__(27);
-Matter.SAT = __webpack_require__(14);
+Matter.SAT = __webpack_require__(28);
Matter.Sleeping = __webpack_require__(7);
-Matter.Svg = __webpack_require__(28);
+Matter.Svg = __webpack_require__(29);
Matter.Vector = __webpack_require__(2);
Matter.Vertices = __webpack_require__(3);
-Matter.World = __webpack_require__(29);
+Matter.World = __webpack_require__(30);
// temporary back compatibility
Matter.Engine.run = Matter.Runner.run;
@@ -9051,7 +8959,7 @@ Matter.Common.deprecated(Matter.Engine, 'run', 'Engine.run ➤ use Matter.Runner
/***/ }),
-/* 23 */
+/* 22 */
/***/ (function(module, exports, __webpack_require__) {
/**
@@ -9083,7 +8991,7 @@ var Common = __webpack_require__(0);
* @readOnly
* @type {String}
*/
- Matter.version = true ? "0.17.1" : undefined;
+ Matter.version = true ? "0.18.0" : undefined;
/**
* A list of plugin dependencies to be installed. These are normally set and installed through `Matter.use`.
@@ -9143,7 +9051,7 @@ var Common = __webpack_require__(0);
/***/ }),
-/* 24 */
+/* 23 */
/***/ (function(module, exports, __webpack_require__) {
/**
@@ -9160,10 +9068,10 @@ var Composites = {};
module.exports = Composites;
var Composite = __webpack_require__(5);
-var Constraint = __webpack_require__(8);
+var Constraint = __webpack_require__(10);
var Common = __webpack_require__(0);
var Body = __webpack_require__(6);
-var Bodies = __webpack_require__(11);
+var Bodies = __webpack_require__(12);
var deprecated = Common.deprecated;
(function() {
@@ -9485,6 +9393,352 @@ var deprecated = Common.deprecated;
})();
+/***/ }),
+/* 24 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/**
+* This module has now been replaced by `Matter.Detector`.
+*
+* All usage should be migrated to `Matter.Detector` or another alternative.
+* For back-compatibility purposes this module will remain for a short term and then later removed in a future release.
+*
+* The `Matter.Grid` module contains methods for creating and manipulating collision broadphase grid structures.
+*
+* @class Grid
+* @deprecated
+*/
+
+var Grid = {};
+
+module.exports = Grid;
+
+var Pair = __webpack_require__(9);
+var Common = __webpack_require__(0);
+var deprecated = Common.deprecated;
+
+(function() {
+
+ /**
+ * Creates a new grid.
+ * @deprecated replaced by Matter.Detector
+ * @method create
+ * @param {} options
+ * @return {grid} A new grid
+ */
+ Grid.create = function(options) {
+ var defaults = {
+ buckets: {},
+ pairs: {},
+ pairsList: [],
+ bucketWidth: 48,
+ bucketHeight: 48
+ };
+
+ return Common.extend(defaults, options);
+ };
+
+ /**
+ * The width of a single grid bucket.
+ *
+ * @property bucketWidth
+ * @type number
+ * @default 48
+ */
+
+ /**
+ * The height of a single grid bucket.
+ *
+ * @property bucketHeight
+ * @type number
+ * @default 48
+ */
+
+ /**
+ * Updates the grid.
+ * @deprecated replaced by Matter.Detector
+ * @method update
+ * @param {grid} grid
+ * @param {body[]} bodies
+ * @param {engine} engine
+ * @param {boolean} forceUpdate
+ */
+ Grid.update = function(grid, bodies, engine, forceUpdate) {
+ var i, col, row,
+ world = engine.world,
+ buckets = grid.buckets,
+ bucket,
+ bucketId,
+ gridChanged = false;
+
+ for (i = 0; i < bodies.length; i++) {
+ var body = bodies[i];
+
+ if (body.isSleeping && !forceUpdate)
+ continue;
+
+ // temporary back compatibility bounds check
+ if (world.bounds && (body.bounds.max.x < world.bounds.min.x || body.bounds.min.x > world.bounds.max.x
+ || body.bounds.max.y < world.bounds.min.y || body.bounds.min.y > world.bounds.max.y))
+ continue;
+
+ var newRegion = Grid._getRegion(grid, body);
+
+ // if the body has changed grid region
+ if (!body.region || newRegion.id !== body.region.id || forceUpdate) {
+
+ if (!body.region || forceUpdate)
+ body.region = newRegion;
+
+ var union = Grid._regionUnion(newRegion, body.region);
+
+ // update grid buckets affected by region change
+ // iterate over the union of both regions
+ for (col = union.startCol; col <= union.endCol; col++) {
+ for (row = union.startRow; row <= union.endRow; row++) {
+ bucketId = Grid._getBucketId(col, row);
+ bucket = buckets[bucketId];
+
+ var isInsideNewRegion = (col >= newRegion.startCol && col <= newRegion.endCol
+ && row >= newRegion.startRow && row <= newRegion.endRow);
+
+ var isInsideOldRegion = (col >= body.region.startCol && col <= body.region.endCol
+ && row >= body.region.startRow && row <= body.region.endRow);
+
+ // remove from old region buckets
+ if (!isInsideNewRegion && isInsideOldRegion) {
+ if (isInsideOldRegion) {
+ if (bucket)
+ Grid._bucketRemoveBody(grid, bucket, body);
+ }
+ }
+
+ // add to new region buckets
+ if (body.region === newRegion || (isInsideNewRegion && !isInsideOldRegion) || forceUpdate) {
+ if (!bucket)
+ bucket = Grid._createBucket(buckets, bucketId);
+ Grid._bucketAddBody(grid, bucket, body);
+ }
+ }
+ }
+
+ // set the new region
+ body.region = newRegion;
+
+ // flag changes so we can update pairs
+ gridChanged = true;
+ }
+ }
+
+ // update pairs list only if pairs changed (i.e. a body changed region)
+ if (gridChanged)
+ grid.pairsList = Grid._createActivePairsList(grid);
+ };
+
+ deprecated(Grid, 'update', 'Grid.update ➤ replaced by Matter.Detector');
+
+ /**
+ * Clears the grid.
+ * @deprecated replaced by Matter.Detector
+ * @method clear
+ * @param {grid} grid
+ */
+ Grid.clear = function(grid) {
+ grid.buckets = {};
+ grid.pairs = {};
+ grid.pairsList = [];
+ };
+
+ deprecated(Grid, 'clear', 'Grid.clear ➤ replaced by Matter.Detector');
+
+ /**
+ * Finds the union of two regions.
+ * @method _regionUnion
+ * @deprecated replaced by Matter.Detector
+ * @private
+ * @param {} regionA
+ * @param {} regionB
+ * @return {} region
+ */
+ Grid._regionUnion = function(regionA, regionB) {
+ var startCol = Math.min(regionA.startCol, regionB.startCol),
+ endCol = Math.max(regionA.endCol, regionB.endCol),
+ startRow = Math.min(regionA.startRow, regionB.startRow),
+ endRow = Math.max(regionA.endRow, regionB.endRow);
+
+ return Grid._createRegion(startCol, endCol, startRow, endRow);
+ };
+
+ /**
+ * Gets the region a given body falls in for a given grid.
+ * @method _getRegion
+ * @deprecated replaced by Matter.Detector
+ * @private
+ * @param {} grid
+ * @param {} body
+ * @return {} region
+ */
+ Grid._getRegion = function(grid, body) {
+ var bounds = body.bounds,
+ startCol = Math.floor(bounds.min.x / grid.bucketWidth),
+ endCol = Math.floor(bounds.max.x / grid.bucketWidth),
+ startRow = Math.floor(bounds.min.y / grid.bucketHeight),
+ endRow = Math.floor(bounds.max.y / grid.bucketHeight);
+
+ return Grid._createRegion(startCol, endCol, startRow, endRow);
+ };
+
+ /**
+ * Creates a region.
+ * @method _createRegion
+ * @deprecated replaced by Matter.Detector
+ * @private
+ * @param {} startCol
+ * @param {} endCol
+ * @param {} startRow
+ * @param {} endRow
+ * @return {} region
+ */
+ Grid._createRegion = function(startCol, endCol, startRow, endRow) {
+ return {
+ id: startCol + ',' + endCol + ',' + startRow + ',' + endRow,
+ startCol: startCol,
+ endCol: endCol,
+ startRow: startRow,
+ endRow: endRow
+ };
+ };
+
+ /**
+ * Gets the bucket id at the given position.
+ * @method _getBucketId
+ * @deprecated replaced by Matter.Detector
+ * @private
+ * @param {} column
+ * @param {} row
+ * @return {string} bucket id
+ */
+ Grid._getBucketId = function(column, row) {
+ return 'C' + column + 'R' + row;
+ };
+
+ /**
+ * Creates a bucket.
+ * @method _createBucket
+ * @deprecated replaced by Matter.Detector
+ * @private
+ * @param {} buckets
+ * @param {} bucketId
+ * @return {} bucket
+ */
+ Grid._createBucket = function(buckets, bucketId) {
+ var bucket = buckets[bucketId] = [];
+ return bucket;
+ };
+
+ /**
+ * Adds a body to a bucket.
+ * @method _bucketAddBody
+ * @deprecated replaced by Matter.Detector
+ * @private
+ * @param {} grid
+ * @param {} bucket
+ * @param {} body
+ */
+ Grid._bucketAddBody = function(grid, bucket, body) {
+ var gridPairs = grid.pairs,
+ pairId = Pair.id,
+ bucketLength = bucket.length,
+ i;
+
+ // add new pairs
+ for (i = 0; i < bucketLength; i++) {
+ var bodyB = bucket[i];
+
+ if (body.id === bodyB.id || (body.isStatic && bodyB.isStatic))
+ continue;
+
+ // keep track of the number of buckets the pair exists in
+ // important for Grid.update to work
+ var id = pairId(body, bodyB),
+ pair = gridPairs[id];
+
+ if (pair) {
+ pair[2] += 1;
+ } else {
+ gridPairs[id] = [body, bodyB, 1];
+ }
+ }
+
+ // add to bodies (after pairs, otherwise pairs with self)
+ bucket.push(body);
+ };
+
+ /**
+ * Removes a body from a bucket.
+ * @method _bucketRemoveBody
+ * @deprecated replaced by Matter.Detector
+ * @private
+ * @param {} grid
+ * @param {} bucket
+ * @param {} body
+ */
+ Grid._bucketRemoveBody = function(grid, bucket, body) {
+ var gridPairs = grid.pairs,
+ pairId = Pair.id,
+ i;
+
+ // remove from bucket
+ bucket.splice(Common.indexOf(bucket, body), 1);
+
+ var bucketLength = bucket.length;
+
+ // update pair counts
+ for (i = 0; i < bucketLength; i++) {
+ // keep track of the number of buckets the pair exists in
+ // important for _createActivePairsList to work
+ var pair = gridPairs[pairId(body, bucket[i])];
+
+ if (pair)
+ pair[2] -= 1;
+ }
+ };
+
+ /**
+ * Generates a list of the active pairs in the grid.
+ * @method _createActivePairsList
+ * @deprecated replaced by Matter.Detector
+ * @private
+ * @param {} grid
+ * @return [] pairs
+ */
+ Grid._createActivePairsList = function(grid) {
+ var pair,
+ gridPairs = grid.pairs,
+ pairKeys = Common.keys(gridPairs),
+ pairKeysLength = pairKeys.length,
+ pairs = [],
+ k;
+
+ // iterate over grid.pairs
+ for (k = 0; k < pairKeysLength; k++) {
+ pair = gridPairs[pairKeys[k]];
+
+ // if pair exists in at least one bucket
+ // it is a pair that needs further collision testing so push it
+ if (pair[2] > 0) {
+ pairs.push(pair);
+ } else {
+ delete gridPairs[pairKeys[k]];
+ }
+ }
+
+ return pairs;
+ };
+
+})();
+
+
/***/ }),
/* 25 */
/***/ (function(module, exports, __webpack_require__) {
@@ -9504,10 +9758,10 @@ module.exports = MouseConstraint;
var Vertices = __webpack_require__(3);
var Sleeping = __webpack_require__(7);
-var Mouse = __webpack_require__(12);
+var Mouse = __webpack_require__(13);
var Events = __webpack_require__(4);
-var Detector = __webpack_require__(13);
-var Constraint = __webpack_require__(8);
+var Detector = __webpack_require__(14);
+var Constraint = __webpack_require__(10);
var Composite = __webpack_require__(5);
var Common = __webpack_require__(0);
var Bounds = __webpack_require__(1);
@@ -9769,9 +10023,9 @@ var Query = {};
module.exports = Query;
var Vector = __webpack_require__(2);
-var SAT = __webpack_require__(14);
+var Collision = __webpack_require__(8);
var Bounds = __webpack_require__(1);
-var Bodies = __webpack_require__(11);
+var Bodies = __webpack_require__(12);
var Vertices = __webpack_require__(3);
(function() {
@@ -9781,22 +10035,28 @@ var Vertices = __webpack_require__(3);
* @method collides
* @param {body} body
* @param {body[]} bodies
- * @return {object[]} Collisions
+ * @return {collision[]} Collisions
*/
Query.collides = function(body, bodies) {
- var collisions = [];
+ var collisions = [],
+ bodiesLength = bodies.length,
+ bounds = body.bounds,
+ collides = Collision.collides,
+ overlaps = Bounds.overlaps;
- for (var i = 0; i < bodies.length; i++) {
- var bodyA = bodies[i];
+ for (var i = 0; i < bodiesLength; i++) {
+ var bodyA = bodies[i],
+ partsALength = bodyA.parts.length,
+ partsAStart = partsALength === 1 ? 0 : 1;
- if (Bounds.overlaps(bodyA.bounds, body.bounds)) {
- for (var j = bodyA.parts.length === 1 ? 0 : 1; j < bodyA.parts.length; j++) {
+ if (overlaps(bodyA.bounds, bounds)) {
+ for (var j = partsAStart; j < partsALength; j++) {
var part = bodyA.parts[j];
- if (Bounds.overlaps(part.bounds, body.bounds)) {
- var collision = SAT.collides(part, body);
+ if (overlaps(part.bounds, bounds)) {
+ var collision = collides(part, body);
- if (collision.collided) {
+ if (collision) {
collisions.push(collision);
break;
}
@@ -9815,7 +10075,7 @@ var Vertices = __webpack_require__(3);
* @param {vector} startPoint
* @param {vector} endPoint
* @param {number} [rayWidth]
- * @return {object[]} Collisions
+ * @return {collision[]} Collisions
*/
Query.ray = function(bodies, startPoint, endPoint, rayWidth) {
rayWidth = rayWidth || 1e-100;
@@ -10182,6 +10442,49 @@ var Common = __webpack_require__(0);
/* 28 */
/***/ (function(module, exports, __webpack_require__) {
+/**
+* This module has now been replaced by `Matter.Collision`.
+*
+* All usage should be migrated to `Matter.Collision`.
+* For back-compatibility purposes this module will remain for a short term and then later removed in a future release.
+*
+* The `Matter.SAT` module contains methods for detecting collisions using the Separating Axis Theorem.
+*
+* @class SAT
+* @deprecated
+*/
+
+var SAT = {};
+
+module.exports = SAT;
+
+var Collision = __webpack_require__(8);
+var Common = __webpack_require__(0);
+var deprecated = Common.deprecated;
+
+(function() {
+
+ /**
+ * Detect collision between two bodies using the Separating Axis Theorem.
+ * @deprecated replaced by Collision.collides
+ * @method collides
+ * @param {body} bodyA
+ * @param {body} bodyB
+ * @return {collision} collision
+ */
+ SAT.collides = function(bodyA, bodyB) {
+ return Collision.collides(bodyA, bodyB);
+ };
+
+ deprecated(SAT, 'collides', 'SAT.collides ➤ replaced by Collision.collides');
+
+})();
+
+
+/***/ }),
+/* 29 */
+/***/ (function(module, exports, __webpack_require__) {
+
/**
* The `Matter.Svg` module contains methods for converting SVG images into an array of vector points.
*
@@ -10410,7 +10713,7 @@ var Common = __webpack_require__(0);
})();
/***/ }),
-/* 29 */
+/* 30 */
/***/ (function(module, exports, __webpack_require__) {
/**
diff --git a/build/matter.min.js b/build/matter.min.js
index da8f1e1..29d25db 100644
--- a/build/matter.min.js
+++ b/build/matter.min.js
@@ -1,6 +1,6 @@
/*!
- * matter-js 0.17.1 by @liabru
+ * matter-js 0.18.0 by @liabru
* http://brm.io/matter-js/
* License MIT
*/
-!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("Matter",[],t):"object"==typeof exports?exports.Matter=t():e.Matter=t()}(this,(function(){return function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(i,o,function(t){return e[t]}.bind(null,o));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=22)}([function(e,t){var n={};e.exports=n,function(){n._nextId=0,n._seed=0,n._nowStartTime=+new Date,n._warnedOnce={},n._decomp=null,n.extend=function(e,t){var i,o;"boolean"==typeof t?(i=2,o=t):(i=1,o=!0);for(var r=i;r0;t--){var i=Math.floor(n.random()*(t+1)),o=e[t];e[t]=e[i],e[i]=o}return e},n.choose=function(e){return e[Math.floor(n.random()*e.length)]},n.isElement=function(e){return"undefined"!=typeof HTMLElement?e instanceof HTMLElement:!!(e&&e.nodeType&&e.nodeName)},n.isArray=function(e){return"[object Array]"===Object.prototype.toString.call(e)},n.isFunction=function(e){return"function"==typeof e},n.isPlainObject=function(e){return"object"==typeof e&&e.constructor===Object},n.isString=function(e){return"[object String]"===toString.call(e)},n.clamp=function(e,t,n){return en?n:e},n.sign=function(e){return e<0?-1:1},n.now=function(){if("undefined"!=typeof window&&window.performance){if(window.performance.now)return window.performance.now();if(window.performance.webkitNow)return window.performance.webkitNow()}return Date.now?Date.now():new Date-n._nowStartTime},n.random=function(t,n){return n=void 0!==n?n:1,(t=void 0!==t?t:0)+e()*(n-t)};var e=function(){return n._seed=(9301*n._seed+49297)%233280,n._seed/233280};n.colorToNumber=function(e){return 3==(e=e.replace("#","")).length&&(e=e.charAt(0)+e.charAt(0)+e.charAt(1)+e.charAt(1)+e.charAt(2)+e.charAt(2)),parseInt(e,16)},n.logLevel=1,n.log=function(){console&&n.logLevel>0&&n.logLevel<=3&&console.log.apply(console,["matter-js:"].concat(Array.prototype.slice.call(arguments)))},n.info=function(){console&&n.logLevel>0&&n.logLevel<=2&&console.info.apply(console,["matter-js:"].concat(Array.prototype.slice.call(arguments)))},n.warn=function(){console&&n.logLevel>0&&n.logLevel<=3&&console.warn.apply(console,["matter-js:"].concat(Array.prototype.slice.call(arguments)))},n.warnOnce=function(){var e=Array.prototype.slice.call(arguments).join(" ");n._warnedOnce[e]||(n.warn(e),n._warnedOnce[e]=!0)},n.deprecated=function(e,t,i){e[t]=n.chain((function(){n.warnOnce("🔅 deprecated 🔅",i)}),e[t])},n.nextId=function(){return n._nextId++},n.indexOf=function(e,t){if(e.indexOf)return e.indexOf(t);for(var n=0;ne.max.x&&(e.max.x=o.x),o.xe.max.y&&(e.max.y=o.y),o.y0?e.max.x+=n.x:e.min.x+=n.x,n.y>0?e.max.y+=n.y:e.min.y+=n.y)},n.contains=function(e,t){return t.x>=e.min.x&&t.x<=e.max.x&&t.y>=e.min.y&&t.y<=e.max.y},n.overlaps=function(e,t){return e.min.x<=t.max.x&&e.max.x>=t.min.x&&e.max.y>=t.min.y&&e.min.y<=t.max.y},n.translate=function(e,t){e.min.x+=t.x,e.max.x+=t.x,e.min.y+=t.y,e.max.y+=t.y},n.shift=function(e,t){var n=e.max.x-e.min.x,i=e.max.y-e.min.y;e.min.x=t.x,e.max.x=t.x+n,e.min.y=t.y,e.max.y=t.y+i}},function(e,t){var n={};e.exports=n,n.create=function(e,t){return{x:e||0,y:t||0}},n.clone=function(e){return{x:e.x,y:e.y}},n.magnitude=function(e){return Math.sqrt(e.x*e.x+e.y*e.y)},n.magnitudeSquared=function(e){return e.x*e.x+e.y*e.y},n.rotate=function(e,t,n){var i=Math.cos(t),o=Math.sin(t);n||(n={});var r=e.x*i-e.y*o;return n.y=e.x*o+e.y*i,n.x=r,n},n.rotateAbout=function(e,t,n,i){var o=Math.cos(t),r=Math.sin(t);i||(i={});var a=n.x+((e.x-n.x)*o-(e.y-n.y)*r);return i.y=n.y+((e.x-n.x)*r+(e.y-n.y)*o),i.x=a,i},n.normalise=function(e){var t=n.magnitude(e);return 0===t?{x:0,y:0}:{x:e.x/t,y:e.y/t}},n.dot=function(e,t){return e.x*t.x+e.y*t.y},n.cross=function(e,t){return e.x*t.y-e.y*t.x},n.cross3=function(e,t,n){return(t.x-e.x)*(n.y-e.y)-(t.y-e.y)*(n.x-e.x)},n.add=function(e,t,n){return n||(n={}),n.x=e.x+t.x,n.y=e.y+t.y,n},n.sub=function(e,t,n){return n||(n={}),n.x=e.x-t.x,n.y=e.y-t.y,n},n.mult=function(e,t){return{x:e.x*t,y:e.y*t}},n.div=function(e,t){return{x:e.x/t,y:e.y/t}},n.perp=function(e,t){return{x:(t=!0===t?-1:1)*-e.y,y:t*e.x}},n.neg=function(e){return{x:-e.x,y:-e.y}},n.angle=function(e,t){return Math.atan2(t.y-e.y,t.x-e.x)},n._temp=[n.create(),n.create(),n.create(),n.create(),n.create(),n.create()]},function(e,t,n){var i={};e.exports=i;var o=n(2),r=n(0);i.create=function(e,t){for(var n=[],i=0;i0)return!1}return!0},i.scale=function(e,t,n,r){if(1===t&&1===n)return e;var a,s;r=r||i.centre(e);for(var l=0;l=0?l-1:e.length-1],u=e[l],d=e[(l+1)%e.length],p=t[l0&&(r|=2),3===r)return!1;return 0!==r||null},i.hull=function(e){var t,n,i=[],r=[];for((e=e.slice(0)).sort((function(e,t){var n=e.x-t.x;return 0!==n?n:e.y-t.y})),n=0;n=2&&o.cross3(r[r.length-2],r[r.length-1],t)<=0;)r.pop();r.push(t)}for(n=e.length-1;n>=0;n-=1){for(t=e[n];i.length>=2&&o.cross3(i[i.length-2],i[i.length-1],t)<=0;)i.pop();i.push(t)}return i.pop(),r.pop(),i.concat(r)}},function(e,t,n){var i={};e.exports=i;var o=n(0);i.on=function(e,t,n){for(var i,o=t.split(" "),r=0;r0){n||(n={}),i=t.split(" ");for(var c=0;c0&&r.rotateAbout(a.position,n,e.position,a.position)}},i.setVelocity=function(e,t){e.positionPrev.x=e.position.x-t.x,e.positionPrev.y=e.position.y-t.y,e.velocity.x=t.x,e.velocity.y=t.y,e.speed=r.magnitude(e.velocity)},i.setAngularVelocity=function(e,t){e.anglePrev=e.angle-t,e.angularVelocity=t,e.angularSpeed=Math.abs(e.angularVelocity)},i.translate=function(e,t){i.setPosition(e,r.add(e.position,t))},i.rotate=function(e,t,n){if(n){var o=Math.cos(t),r=Math.sin(t),a=e.position.x-n.x,s=e.position.y-n.y;i.setPosition(e,{x:n.x+(a*o-s*r),y:n.y+(a*r+s*o)}),i.setAngle(e,e.angle+t)}else i.setAngle(e,e.angle+t)},i.scale=function(e,t,n,r){var a=0,s=0;r=r||e.position;for(var u=0;u0&&(a+=d.area,s+=d.inertia),d.position.x=r.x+(d.position.x-r.x)*t,d.position.y=r.y+(d.position.y-r.y)*n,l.update(d.bounds,d.vertices,e.velocity)}e.parts.length>1&&(e.area=a,e.isStatic||(i.setMass(e,e.density*a),i.setInertia(e,s))),e.circleRadius&&(t===n?e.circleRadius*=t:e.circleRadius=null)},i.update=function(e,t,n,i){var a=Math.pow(t*n*e.timeScale,2),s=1-e.frictionAir*n*e.timeScale,u=e.position.x-e.positionPrev.x,d=e.position.y-e.positionPrev.y;e.velocity.x=u*s*i+e.force.x/e.mass*a,e.velocity.y=d*s*i+e.force.y/e.mass*a,e.positionPrev.x=e.position.x,e.positionPrev.y=e.position.y,e.position.x+=e.velocity.x,e.position.y+=e.velocity.y,e.angularVelocity=(e.angle-e.anglePrev)*s*i+e.torque/e.inertia*a,e.anglePrev=e.angle,e.angle+=e.angularVelocity,e.speed=r.magnitude(e.velocity),e.angularSpeed=Math.abs(e.angularVelocity);for(var p=0;p0&&(f.position.x+=e.velocity.x,f.position.y+=e.velocity.y),0!==e.angularVelocity&&(o.rotate(f.vertices,e.angularVelocity,e.position),c.rotate(f.axes,e.angularVelocity),p>0&&r.rotateAbout(f.position,e.angularVelocity,e.position,f.position)),l.update(f.bounds,f.vertices,e.velocity)}},i.applyForce=function(e,t,n){e.force.x+=n.x,e.force.y+=n.y;var i=t.x-e.position.x,o=t.y-e.position.y;e.torque+=i*n.y-o*n.x},i._totalProperties=function(e){for(var t={mass:0,area:0,inertia:0,centre:{x:0,y:0}},n=1===e.parts.length?0:1;n0&&r.motion=r.sleepThreshold&&i.set(r,!0)):r.sleepCounter>0&&(r.sleepCounter-=1)}else i.set(r,!1)}},i.afterCollisions=function(e,t){for(var n=t*t*t,o=0;oi._motionWakeThreshold*n&&i.set(c,!1)}}}},i.set=function(e,t){var n=e.isSleeping;t?(e.isSleeping=!0,e.sleepCounter=e.sleepThreshold,e.positionImpulse.x=0,e.positionImpulse.y=0,e.positionPrev.x=e.position.x,e.positionPrev.y=e.position.y,e.anglePrev=e.angle,e.speed=0,e.angularSpeed=0,e.motion=0,n||o.trigger(e,"sleepStart")):(e.isSleeping=!1,e.sleepCounter=0,n&&o.trigger(e,"sleepEnd"))}},function(e,t,n){var i={};e.exports=i;var o=n(3),r=n(2),a=n(7),s=n(1),l=n(10),c=n(0);i._warming=.4,i._torqueDampen=1,i._minLength=1e-6,i.create=function(e){var t=e;t.bodyA&&!t.pointA&&(t.pointA={x:0,y:0}),t.bodyB&&!t.pointB&&(t.pointB={x:0,y:0});var n=t.bodyA?r.add(t.bodyA.position,t.pointA):t.pointA,i=t.bodyB?r.add(t.bodyB.position,t.pointB):t.pointB,o=r.magnitude(r.sub(n,i));t.length=void 0!==t.length?t.length:o,t.id=t.id||c.nextId(),t.label=t.label||"Constraint",t.type="constraint",t.stiffness=t.stiffness||(t.length>0?1:.7),t.damping=t.damping||0,t.angularStiffness=t.angularStiffness||0,t.angleA=t.bodyA?t.bodyA.angle:t.angleA,t.angleB=t.bodyB?t.bodyB.angle:t.angleB,t.plugin={};var a={visible:!0,lineWidth:2,strokeStyle:"#ffffff",type:"line",anchors:!0};return 0===t.length&&t.stiffness>.1?(a.type="pin",a.anchors=!1):t.stiffness<.9&&(a.type="spring"),t.render=c.extend(a,t.render),t},i.preSolveAll=function(e){for(var t=0;t0&&(d.position.x+=c.x,d.position.y+=c.y),0!==c.angle&&(o.rotate(d.vertices,c.angle,n.position),l.rotate(d.axes,c.angle),u>0&&r.rotateAbout(d.position,c.angle,n.position,d.position)),s.update(d.bounds,d.vertices,n.velocity)}c.angle*=i._warming,c.x*=i._warming,c.y*=i._warming}}},i.pointAWorld=function(e){return{x:(e.bodyA?e.bodyA.position.x:0)+e.pointA.x,y:(e.bodyA?e.bodyA.position.y:0)+e.pointA.y}},i.pointBWorld=function(e){return{x:(e.bodyB?e.bodyB.position.x:0)+e.pointB.x,y:(e.bodyB?e.bodyB.position.y:0)+e.pointB.y}}},function(e,t,n){var i={};e.exports=i;var o=n(17);i.create=function(e,t){var n=e.bodyA,o=e.bodyB,r=e.parentA,a=e.parentB,s={id:i.id(n,o),bodyA:n,bodyB:o,contacts:{},activeContacts:[],separation:0,isActive:!0,confirmedActive:!0,isSensor:n.isSensor||o.isSensor,timeCreated:t,timeUpdated:t,inverseMass:r.inverseMass+a.inverseMass,friction:Math.min(r.friction,a.friction),frictionStatic:Math.max(r.frictionStatic,a.frictionStatic),restitution:Math.max(r.restitution,a.restitution),slop:Math.max(r.slop,a.slop)};return i.update(s,e,t),s},i.update=function(e,t,n){var r=e.contacts,a=t.supports,s=e.activeContacts,l=t.parentA,c=t.parentB;if(e.collision=t,e.inverseMass=l.inverseMass+c.inverseMass,e.friction=Math.min(l.friction,c.friction),e.frictionStatic=Math.max(l.frictionStatic,c.frictionStatic),e.restitution=Math.max(l.restitution,c.restitution),e.slop=Math.max(l.slop,c.slop),s.length=0,t.collided){for(var u=0;u0&&o.area(C)1?(v=a.create(r.extend({parts:m.slice(0)},i)),a.setPosition(v,{x:e,y:t}),v):m[0]}},function(e,t,n){var i={};e.exports=i;var o=n(0);i.create=function(e){var t={};return e||o.log("Mouse.create: element was undefined, defaulting to document.body","warn"),t.element=e||document.body,t.absolute={x:0,y:0},t.position={x:0,y:0},t.mousedownPosition={x:0,y:0},t.mouseupPosition={x:0,y:0},t.offset={x:0,y:0},t.scale={x:1,y:1},t.wheelDelta=0,t.button=-1,t.pixelRatio=parseInt(t.element.getAttribute("data-pixel-ratio"),10)||1,t.sourceEvents={mousemove:null,mousedown:null,mouseup:null,mousewheel:null},t.mousemove=function(e){var n=i._getRelativeMousePosition(e,t.element,t.pixelRatio);e.changedTouches&&(t.button=0,e.preventDefault()),t.absolute.x=n.x,t.absolute.y=n.y,t.position.x=t.absolute.x*t.scale.x+t.offset.x,t.position.y=t.absolute.y*t.scale.y+t.offset.y,t.sourceEvents.mousemove=e},t.mousedown=function(e){var n=i._getRelativeMousePosition(e,t.element,t.pixelRatio);e.changedTouches?(t.button=0,e.preventDefault()):t.button=e.button,t.absolute.x=n.x,t.absolute.y=n.y,t.position.x=t.absolute.x*t.scale.x+t.offset.x,t.position.y=t.absolute.y*t.scale.y+t.offset.y,t.mousedownPosition.x=t.position.x,t.mousedownPosition.y=t.position.y,t.sourceEvents.mousedown=e},t.mouseup=function(e){var n=i._getRelativeMousePosition(e,t.element,t.pixelRatio);e.changedTouches&&e.preventDefault(),t.button=-1,t.absolute.x=n.x,t.absolute.y=n.y,t.position.x=t.absolute.x*t.scale.x+t.offset.x,t.position.y=t.absolute.y*t.scale.y+t.offset.y,t.mouseupPosition.x=t.position.x,t.mouseupPosition.y=t.position.y,t.sourceEvents.mouseup=e},t.mousewheel=function(e){t.wheelDelta=Math.max(-1,Math.min(1,e.wheelDelta||-e.detail)),e.preventDefault()},i.setElement(t,t.element),t},i.setElement=function(e,t){e.element=t,t.addEventListener("mousemove",e.mousemove),t.addEventListener("mousedown",e.mousedown),t.addEventListener("mouseup",e.mouseup),t.addEventListener("mousewheel",e.mousewheel),t.addEventListener("DOMMouseScroll",e.mousewheel),t.addEventListener("touchmove",e.mousemove),t.addEventListener("touchstart",e.mousedown),t.addEventListener("touchend",e.mouseup)},i.clearSourceEvents=function(e){e.sourceEvents.mousemove=null,e.sourceEvents.mousedown=null,e.sourceEvents.mouseup=null,e.sourceEvents.mousewheel=null,e.wheelDelta=0},i.setOffset=function(e,t){e.offset.x=t.x,e.offset.y=t.y,e.position.x=e.absolute.x*e.scale.x+e.offset.x,e.position.y=e.absolute.y*e.scale.y+e.offset.y},i.setScale=function(e,t){e.scale.x=t.x,e.scale.y=t.y,e.position.x=e.absolute.x*e.scale.x+e.offset.x,e.position.y=e.absolute.y*e.scale.y+e.offset.y},i._getRelativeMousePosition=function(e,t,n){var i,o,r=t.getBoundingClientRect(),a=document.documentElement||document.body.parentNode||document.body,s=void 0!==window.pageXOffset?window.pageXOffset:a.scrollLeft,l=void 0!==window.pageYOffset?window.pageYOffset:a.scrollTop,c=e.changedTouches;return c?(i=c[0].pageX-r.left-s,o=c[0].pageY-r.top-l):(i=e.pageX-r.left-s,o=e.pageY-r.top-l),{x:i/(t.clientWidth/(t.width||t.clientWidth)*n),y:o/(t.clientHeight/(t.height||t.clientHeight)*n)}}},function(e,t,n){var i={};e.exports=i;var o=n(14),r=n(9),a=n(1);i.collisions=function(e,t){for(var n=[],s=t.pairs.table,l=0;l1?1:0;d1?1:0;f0:0!=(e.mask&t.category)&&0!=(t.mask&e.category)}},function(e,t,n){var i={};e.exports=i;var o=n(3),r=n(2);i.collides=function(e,t,n){var a,s,l,c,u=!1;if(n){var d=e.parent,p=t.parent,f=d.speed*d.speed+d.angularSpeed*d.angularSpeed+p.speed*p.speed+p.angularSpeed*p.angularSpeed;u=n&&n.collided&&f<.2,c=n}else c={collided:!1,bodyA:e,bodyB:t};if(n&&u){var v=c.axisBody,m=v===e?t:e,y=[v.axes[n.axisNumber]];if(l=i._overlapAxes(v.vertices,m.vertices,y),c.reused=!0,l.overlap<=0)return c.collided=!1,c}else{if((a=i._overlapAxes(e.vertices,t.vertices,e.axes)).overlap<=0)return c.collided=!1,c;if((s=i._overlapAxes(t.vertices,e.vertices,t.axes)).overlap<=0)return c.collided=!1,c;a.overlapo?o=s:s=0?a.index-1:u.length-1],c.x=o.x-d.x,c.y=o.y-d.y,l=-r.dot(n,c),s=o,o=u[(a.index+1)%u.length],c.x=o.x-d.x,c.y=o.y-d.y,(i=-r.dot(n,c))r?(o.warn("Plugin.register:",i.toString(t),"was upgraded to",i.toString(e)),i._registry[e.name]=e):n-1},i.isFor=function(e,t){var n=e.for&&i.dependencyParse(e.for);return!e.for||t.name===n.name&&i.versionSatisfies(t.version,n.range)},i.use=function(e,t){if(e.uses=(e.uses||[]).concat(t||[]),0!==e.uses.length){for(var n=i.dependencies(e),r=o.topologicalSort(n),a=[],s=0;s0&&o.info(a.join(" "))}else o.warn("Plugin.use:",i.toString(e),"does not specify any dependencies to install.")},i.dependencies=function(e,t){var n=i.dependencyParse(e),r=n.name;if(!(r in(t=t||{}))){e=i.resolve(e)||e,t[r]=o.map(e.uses||[],(function(t){i.isPlugin(t)&&i.register(t);var r=i.dependencyParse(t),a=i.resolve(t);return a&&!i.versionSatisfies(a.version,r.range)?(o.warn("Plugin.dependencies:",i.toString(a),"does not satisfy",i.toString(r),"used by",i.toString(n)+"."),a._warned=!0,e._warned=!0):a||(o.warn("Plugin.dependencies:",i.toString(t),"used by",i.toString(n),"could not be resolved."),e._warned=!0),r.name}));for(var a=0;a=|>)?\s*((\d+)\.(\d+)\.(\d+))(-[0-9A-Za-z-]+)?$/;t.test(e)||o.warn("Plugin.versionParse:",e,"is not a valid version or range.");var n=t.exec(e),i=Number(n[4]),r=Number(n[5]),a=Number(n[6]);return{isRange:Boolean(n[1]||n[2]),version:n[3],range:e,operator:n[1]||n[2]||"",major:i,minor:r,patch:a,parts:[i,r,a],prerelease:n[7],number:1e8*i+1e4*r+a}},i.versionSatisfies=function(e,t){t=t||"*";var n=i.versionParse(t),o=i.versionParse(e);if(n.isRange){if("*"===n.operator||"*"===e)return!0;if(">"===n.operator)return o.number>n.number;if(">="===n.operator)return o.number>=n.number;if("~"===n.operator)return o.major===n.major&&o.minor===n.minor&&o.patch>=n.patch;if("^"===n.operator)return n.major>0?o.major===n.major&&o.number>=n.number:n.minor>0?o.minor===n.minor&&o.patch>=n.patch:o.patch===n.patch}return e===t||"*"===e}},function(e,t,n){var i={};e.exports=i;var o=n(0),r=n(5),a=n(1),s=n(4),l=n(2),c=n(12);!function(){var e,t;"undefined"!=typeof window&&(e=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||function(e){window.setTimeout((function(){e(o.now())}),1e3/60)},t=window.cancelAnimationFrame||window.mozCancelAnimationFrame||window.webkitCancelAnimationFrame||window.msCancelAnimationFrame),i._goodFps=30,i._goodDelta=1e3/60,i.create=function(e){var t={controller:i,engine:null,element:null,canvas:null,mouse:null,frameRequestId:null,timing:{historySize:60,delta:0,deltaHistory:[],lastTime:0,lastTimestamp:0,lastElapsed:0,timestampElapsed:0,timestampElapsedHistory:[],engineDeltaHistory:[],engineElapsedHistory:[],elapsedHistory:[]},options:{width:800,height:600,pixelRatio:1,background:"#14151f",wireframeBackground:"#14151f",hasBounds:!!e.bounds,enabled:!0,wireframes:!0,showSleeping:!0,showDebug:!1,showStats:!1,showPerformance:!1,showBroadphase:!1,showBounds:!1,showVelocity:!1,showCollisions:!1,showSeparations:!1,showAxes:!1,showPositions:!1,showAngleIndicator:!1,showIds:!1,showVertexNumbers:!1,showConvexHulls:!1,showInternalEdges:!1,showMousePosition:!1}},n=o.extend(t,e);return n.canvas&&(n.canvas.width=n.options.width||n.canvas.width,n.canvas.height=n.options.height||n.canvas.height),n.mouse=e.mouse,n.engine=e.engine,n.canvas=n.canvas||d(n.options.width,n.options.height),n.context=n.canvas.getContext("2d"),n.textures={},n.bounds=n.bounds||{min:{x:0,y:0},max:{x:n.canvas.width,y:n.canvas.height}},1!==n.options.pixelRatio&&i.setPixelRatio(n,n.options.pixelRatio),o.isElement(n.element)?n.element.appendChild(n.canvas):n.canvas.parentNode||o.log("Render.create: options.element was undefined, render.canvas was created but not appended","warn"),n},i.run=function(t){!function o(r){t.frameRequestId=e(o),n(t,r),i.world(t,r),(t.options.showStats||t.options.showDebug)&&i.stats(t,t.context,r),(t.options.showPerformance||t.options.showDebug)&&i.performance(t,t.context,r)}()},i.stop=function(e){t(e.frameRequestId)},i.setPixelRatio=function(e,t){var n=e.options,i=e.canvas;"auto"===t&&(t=p(i)),n.pixelRatio=t,i.setAttribute("data-pixel-ratio",t),i.width=n.width*t,i.height=n.height*t,i.style.width=n.width+"px",i.style.height=n.height+"px"},i.lookAt=function(e,t,n,i){i=void 0===i||i,t=o.isArray(t)?t:[t],n=n||{x:0,y:0};for(var r={min:{x:1/0,y:1/0},max:{x:-1/0,y:-1/0}},a=0;ar.max.x&&(r.max.x=u.x),l.yr.max.y&&(r.max.y=u.y))}var d=r.max.x-r.min.x+2*n.x,p=r.max.y-r.min.y+2*n.y,f=e.canvas.height,v=e.canvas.width/f,m=d/p,y=1,g=1;m>v?g=m/v:y=v/m,e.options.hasBounds=!0,e.bounds.min.x=r.min.x,e.bounds.max.x=r.min.x+d*y,e.bounds.min.y=r.min.y,e.bounds.max.y=r.min.y+p*g,i&&(e.bounds.min.x+=.5*d-d*y*.5,e.bounds.max.x+=.5*d-d*y*.5,e.bounds.min.y+=.5*p-p*g*.5,e.bounds.max.y+=.5*p-p*g*.5),e.bounds.min.x-=n.x,e.bounds.max.x-=n.x,e.bounds.min.y-=n.y,e.bounds.max.y-=n.y,e.mouse&&(c.setScale(e.mouse,{x:(e.bounds.max.x-e.bounds.min.x)/e.canvas.width,y:(e.bounds.max.y-e.bounds.min.y)/e.canvas.height}),c.setOffset(e.mouse,e.bounds.min))},i.startViewTransform=function(e){var t=e.bounds.max.x-e.bounds.min.x,n=e.bounds.max.y-e.bounds.min.y,i=t/e.options.width,o=n/e.options.height;e.context.setTransform(e.options.pixelRatio/i,0,0,e.options.pixelRatio/o,0,0),e.context.translate(-e.bounds.min.x,-e.bounds.min.y)},i.endViewTransform=function(e){e.context.setTransform(e.options.pixelRatio,0,0,e.options.pixelRatio,0,0)},i.world=function(e,t){var n,u=o.now(),d=e.engine,p=d.world,f=e.canvas,m=e.context,y=e.options,g=e.timing,x=r.allBodies(p),h=r.allConstraints(p),b=y.wireframes?y.wireframeBackground:y.background,S=[],w=[],A={timestamp:d.timing.timestamp};if(s.trigger(e,"beforeRender",A),e.currentBackground!==b&&v(e,b),m.globalCompositeOperation="source-in",m.fillStyle="transparent",m.fillRect(0,0,f.width,f.height),m.globalCompositeOperation="source-over",y.hasBounds){for(n=0;n1?1:0;a1?1:0;s1?1:0;r1?1:0;s1?1:0;r1?1:0;r1?1:0;o0)){var u=i.activeContacts[0].vertex.x,d=i.activeContacts[0].vertex.y;2===i.activeContacts.length&&(u=(i.activeContacts[0].vertex.x+i.activeContacts[1].vertex.x)/2,d=(i.activeContacts[0].vertex.y+i.activeContacts[1].vertex.y)/2),o.bodyB===o.supports[0].body||!0===o.bodyA.isStatic?s.moveTo(u-8*o.normal.x,d-8*o.normal.y):s.moveTo(u+8*o.normal.x,d+8*o.normal.y),s.lineTo(u,d)}l.wireframes?s.strokeStyle="rgba(255,165,0,0.7)":s.strokeStyle="orange",s.lineWidth=1,s.stroke()},i.separations=function(e,t,n){var i,o,r,a,s,l=n,c=e.options;for(l.beginPath(),s=0;s0&&c.trigger(e,"collisionStart",{pairs:A.collisionStart}),r.preSolvePosition(A.list),m=0;m0&&c.trigger(e,"collisionActive",{pairs:A.collisionActive}),A.collisionEnd.length>0&&c.trigger(e,"collisionEnd",{pairs:A.collisionEnd}),i._bodiesClearForces(b),c.trigger(e,"afterUpdate",h),e.timing.lastElapsed=p.now()-f,e},i.merge=function(e,t){if(p.extend(e,t),t.world){e.world=t.world,i.clear(e);for(var n=u.allBodies(e.world),r=0;rf.friction*f.frictionStatic*L*n&&(O=T,V=a.clamp(f.friction*R*n,-O,O));var F=r.cross(P,g),D=r.cross(M,g),H=b/(m.inverseMass+y.inverseMass+m.inverseInertia*F*F+y.inverseInertia*D*D);if(E*=H,V*=H,k<0&&k*k>i._restingThresh*n)w.normalImpulse=0;else{var j=w.normalImpulse;w.normalImpulse=Math.min(w.normalImpulse+E,0),E=w.normalImpulse-j}if(I*I>i._restingThreshTangent*n)w.tangentImpulse=0;else{var W=w.tangentImpulse;w.tangentImpulse=a.clamp(w.tangentImpulse+V,-O,O),V=w.tangentImpulse-W}o.x=g.x*E+x.x*V,o.y=g.y*E+x.y*V,m.isStatic||m.isSleeping||(m.positionPrev.x+=o.x*m.inverseMass,m.positionPrev.y+=o.y*m.inverseMass,m.anglePrev+=r.cross(P,o)*m.inverseInertia),y.isStatic||y.isSleeping||(y.positionPrev.x-=o.x*y.inverseMass,y.positionPrev.y-=o.y*y.inverseMass,y.anglePrev-=r.cross(M,o)*y.inverseInertia)}}}}},function(e,t,n){var i={};e.exports=i;var o=n(9),r=n(0);i._pairMaxIdleLife=1e3,i.create=function(e){return r.extend({table:{},list:[],collisionStart:[],collisionActive:[],collisionEnd:[]},e)},i.update=function(e,t,n){var i,r,a,s,l=e.list,c=e.table,u=e.collisionStart,d=e.collisionEnd,p=e.collisionActive;for(u.length=0,d.length=0,p.length=0,s=0;si._pairMaxIdleLife&&c.push(a);for(a=0;au.bounds.max.x||f.bounds.max.yu.bounds.max.y))){var v=i._getRegion(e,f);if(!f.region||v.id!==f.region.id||o){f.region&&!o||(f.region=v);var m=i._regionUnion(v,f.region);for(a=m.startCol;a<=m.endCol;a++)for(s=m.startRow;s<=m.endRow;s++){l=d[c=i._getBucketId(a,s)];var y=a>=v.startCol&&a<=v.endCol&&s>=v.startRow&&s<=v.endRow,g=a>=f.region.startCol&&a<=f.region.endCol&&s>=f.region.startRow&&s<=f.region.endRow;!y&&g&&g&&l&&i._bucketRemoveBody(e,l,f),(f.region===v||y&&!g||o)&&(l||(l=i._createBucket(d,c)),i._bucketAddBody(e,l,f))}f.region=v,p=!0}}}p&&(e.pairsList=i._createActivePairsList(e))},i.clear=function(e){e.buckets={},e.pairs={},e.pairsList=[]},i._regionUnion=function(e,t){var n=Math.min(e.startCol,t.startCol),o=Math.max(e.endCol,t.endCol),r=Math.min(e.startRow,t.startRow),a=Math.max(e.endRow,t.endRow);return i._createRegion(n,o,r,a)},i._getRegion=function(e,t){var n=t.bounds,o=Math.floor(n.min.x/e.bucketWidth),r=Math.floor(n.max.x/e.bucketWidth),a=Math.floor(n.min.y/e.bucketHeight),s=Math.floor(n.max.y/e.bucketHeight);return i._createRegion(o,r,a,s)},i._createRegion=function(e,t,n,i){return{id:e+","+t+","+n+","+i,startCol:e,endCol:t,startRow:n,endRow:i}},i._getBucketId=function(e,t){return"C"+e+"R"+t},i._createBucket=function(e,t){return e[t]=[]},i._bucketAddBody=function(e,t,n){for(var i=0;i0?i.push(n):delete e.pairs[t[o]];return i}},function(e,t,n){var i=e.exports=n(23);i.Axes=n(10),i.Bodies=n(11),i.Body=n(6),i.Bounds=n(1),i.Common=n(0),i.Composite=n(5),i.Composites=n(24),i.Constraint=n(8),i.Contact=n(17),i.Detector=n(13),i.Engine=n(18),i.Events=n(4),i.Grid=n(21),i.Mouse=n(12),i.MouseConstraint=n(25),i.Pair=n(9),i.Pairs=n(20),i.Plugin=n(15),i.Query=n(26),i.Render=n(16),i.Resolver=n(19),i.Runner=n(27),i.SAT=n(14),i.Sleeping=n(7),i.Svg=n(28),i.Vector=n(2),i.Vertices=n(3),i.World=n(29),i.Engine.run=i.Runner.run,i.Common.deprecated(i.Engine,"run","Engine.run ➤ use Matter.Runner.run(engine) instead")},function(e,t,n){var i={};e.exports=i;var o=n(15),r=n(0);i.name="matter-js",i.version="0.17.1",i.uses=[],i.used=[],i.use=function(){o.use(i,Array.prototype.slice.call(arguments))},i.before=function(e,t){return e=e.replace(/^Matter./,""),r.chainPathBefore(i,e,t)},i.after=function(e,t){return e=e.replace(/^Matter./,""),r.chainPathAfter(i,e,t)}},function(e,t,n){var i={};e.exports=i;var o=n(5),r=n(8),a=n(0),s=n(6),l=n(11),c=a.deprecated;i.stack=function(e,t,n,i,r,a,l){for(var c,u=o.create({label:"Stack"}),d=e,p=t,f=0,v=0;vm&&(m=x),s.translate(g,{x:.5*h,y:.5*x}),d=g.bounds.max.x+r,o.addBody(u,g),c=g,f+=1}else d+=r}p+=m+a,d=e}return u},i.chain=function(e,t,n,i,s,l){for(var c=e.bodies,u=1;u0)for(c=0;c0&&(p=f[c-1+(l-1)*t],o.addConstraint(e,r.create(a.extend({bodyA:p,bodyB:d},s)))),i&&cp||a<(c=p-c)||a>n-1-c))return 1===d&&s.translate(u,{x:(a+(n%2==1?1:-1))*f,y:0}),l(e+(u?a*f:0)+a*r,i,a,c,u,d)}))},i.newtonsCradle=function(e,t,n,i,a){for(var s=o.create({label:"Newtons Cradle"}),c=0;c1?1:0;ue.deltaMax?e.deltaMax:i)/e.delta,e.delta=i),0!==e.timeScalePrev&&(s*=a.timeScale/e.timeScalePrev),0===a.timeScale&&(s=0),e.timeScalePrev=a.timeScale,e.correction=s,e.frameCounter+=1,n-e.counterTimestamp>=1e3&&(e.fps=e.frameCounter*((n-e.counterTimestamp)/1e3),e.counterTimestamp=n,e.frameCounter=0),o.trigger(e,"tick",l),o.trigger(e,"beforeUpdate",l),r.update(t,i,s),o.trigger(e,"afterUpdate",l),o.trigger(e,"afterTick",l)},i.stop=function(e){t(e.frameRequestId)},i.start=function(e,t){i.run(e,t)}}()},function(e,t,n){var i={};e.exports=i;n(1);var o=n(0);i.pathToVertices=function(e,t){"undefined"==typeof window||"SVGPathSeg"in window||o.warn("Svg.pathToVertices: SVGPathSeg not defined, a polyfill is required.");var n,r,a,s,l,c,u,d,p,f,v,m=[],y=0,g=0,x=0;t=t||15;var h=function(e,t,n){var i=n%2==1&&n>1;if(!p||e!=p.x||t!=p.y){p&&i?(f=p.x,v=p.y):(f=0,v=0);var o={x:f+e,y:v+t};!i&&p||(p=o),m.push(o),g=f+e,x=v+t}},b=function(e){var t=e.pathSegTypeAsLetter.toUpperCase();if("Z"!==t){switch(t){case"M":case"L":case"T":case"C":case"S":case"Q":g=e.x,x=e.y;break;case"H":g=e.x;break;case"V":x=e.y}h(g,x,e.pathSegType)}};for(i._svgPathToAbsolute(e),a=e.getTotalLength(),c=[],n=0;n0;t--){var i=Math.floor(n.random()*(t+1)),o=e[t];e[t]=e[i],e[i]=o}return e},n.choose=function(e){return e[Math.floor(n.random()*e.length)]},n.isElement=function(e){return"undefined"!=typeof HTMLElement?e instanceof HTMLElement:!!(e&&e.nodeType&&e.nodeName)},n.isArray=function(e){return"[object Array]"===Object.prototype.toString.call(e)},n.isFunction=function(e){return"function"==typeof e},n.isPlainObject=function(e){return"object"==typeof e&&e.constructor===Object},n.isString=function(e){return"[object String]"===toString.call(e)},n.clamp=function(e,t,n){return en?n:e},n.sign=function(e){return e<0?-1:1},n.now=function(){if("undefined"!=typeof window&&window.performance){if(window.performance.now)return window.performance.now();if(window.performance.webkitNow)return window.performance.webkitNow()}return Date.now?Date.now():new Date-n._nowStartTime},n.random=function(t,n){return n=void 0!==n?n:1,(t=void 0!==t?t:0)+e()*(n-t)};var e=function(){return n._seed=(9301*n._seed+49297)%233280,n._seed/233280};n.colorToNumber=function(e){return 3==(e=e.replace("#","")).length&&(e=e.charAt(0)+e.charAt(0)+e.charAt(1)+e.charAt(1)+e.charAt(2)+e.charAt(2)),parseInt(e,16)},n.logLevel=1,n.log=function(){console&&n.logLevel>0&&n.logLevel<=3&&console.log.apply(console,["matter-js:"].concat(Array.prototype.slice.call(arguments)))},n.info=function(){console&&n.logLevel>0&&n.logLevel<=2&&console.info.apply(console,["matter-js:"].concat(Array.prototype.slice.call(arguments)))},n.warn=function(){console&&n.logLevel>0&&n.logLevel<=3&&console.warn.apply(console,["matter-js:"].concat(Array.prototype.slice.call(arguments)))},n.warnOnce=function(){var e=Array.prototype.slice.call(arguments).join(" ");n._warnedOnce[e]||(n.warn(e),n._warnedOnce[e]=!0)},n.deprecated=function(e,t,i){e[t]=n.chain((function(){n.warnOnce("🔅 deprecated 🔅",i)}),e[t])},n.nextId=function(){return n._nextId++},n.indexOf=function(e,t){if(e.indexOf)return e.indexOf(t);for(var n=0;ne.max.x&&(e.max.x=o.x),o.xe.max.y&&(e.max.y=o.y),o.y0?e.max.x+=n.x:e.min.x+=n.x,n.y>0?e.max.y+=n.y:e.min.y+=n.y)},n.contains=function(e,t){return t.x>=e.min.x&&t.x<=e.max.x&&t.y>=e.min.y&&t.y<=e.max.y},n.overlaps=function(e,t){return e.min.x<=t.max.x&&e.max.x>=t.min.x&&e.max.y>=t.min.y&&e.min.y<=t.max.y},n.translate=function(e,t){e.min.x+=t.x,e.max.x+=t.x,e.min.y+=t.y,e.max.y+=t.y},n.shift=function(e,t){var n=e.max.x-e.min.x,i=e.max.y-e.min.y;e.min.x=t.x,e.max.x=t.x+n,e.min.y=t.y,e.max.y=t.y+i}},function(e,t){var n={};e.exports=n,n.create=function(e,t){return{x:e||0,y:t||0}},n.clone=function(e){return{x:e.x,y:e.y}},n.magnitude=function(e){return Math.sqrt(e.x*e.x+e.y*e.y)},n.magnitudeSquared=function(e){return e.x*e.x+e.y*e.y},n.rotate=function(e,t,n){var i=Math.cos(t),o=Math.sin(t);n||(n={});var r=e.x*i-e.y*o;return n.y=e.x*o+e.y*i,n.x=r,n},n.rotateAbout=function(e,t,n,i){var o=Math.cos(t),r=Math.sin(t);i||(i={});var a=n.x+((e.x-n.x)*o-(e.y-n.y)*r);return i.y=n.y+((e.x-n.x)*r+(e.y-n.y)*o),i.x=a,i},n.normalise=function(e){var t=n.magnitude(e);return 0===t?{x:0,y:0}:{x:e.x/t,y:e.y/t}},n.dot=function(e,t){return e.x*t.x+e.y*t.y},n.cross=function(e,t){return e.x*t.y-e.y*t.x},n.cross3=function(e,t,n){return(t.x-e.x)*(n.y-e.y)-(t.y-e.y)*(n.x-e.x)},n.add=function(e,t,n){return n||(n={}),n.x=e.x+t.x,n.y=e.y+t.y,n},n.sub=function(e,t,n){return n||(n={}),n.x=e.x-t.x,n.y=e.y-t.y,n},n.mult=function(e,t){return{x:e.x*t,y:e.y*t}},n.div=function(e,t){return{x:e.x/t,y:e.y/t}},n.perp=function(e,t){return{x:(t=!0===t?-1:1)*-e.y,y:t*e.x}},n.neg=function(e){return{x:-e.x,y:-e.y}},n.angle=function(e,t){return Math.atan2(t.y-e.y,t.x-e.x)},n._temp=[n.create(),n.create(),n.create(),n.create(),n.create(),n.create()]},function(e,t,n){var i={};e.exports=i;var o=n(2),r=n(0);i.create=function(e,t){for(var n=[],i=0;i0)return!1;a=n}return!0},i.scale=function(e,t,n,r){if(1===t&&1===n)return e;var a,s;r=r||i.centre(e);for(var l=0;l=0?l-1:e.length-1],u=e[l],d=e[(l+1)%e.length],p=t[l0&&(r|=2),3===r)return!1;return 0!==r||null},i.hull=function(e){var t,n,i=[],r=[];for((e=e.slice(0)).sort((function(e,t){var n=e.x-t.x;return 0!==n?n:e.y-t.y})),n=0;n=2&&o.cross3(r[r.length-2],r[r.length-1],t)<=0;)r.pop();r.push(t)}for(n=e.length-1;n>=0;n-=1){for(t=e[n];i.length>=2&&o.cross3(i[i.length-2],i[i.length-1],t)<=0;)i.pop();i.push(t)}return i.pop(),r.pop(),i.concat(r)}},function(e,t,n){var i={};e.exports=i;var o=n(0);i.on=function(e,t,n){for(var i,o=t.split(" "),r=0;r0){n||(n={}),i=t.split(" ");for(var c=0;c0&&r.rotateAbout(a.position,n,e.position,a.position)}},i.setVelocity=function(e,t){e.positionPrev.x=e.position.x-t.x,e.positionPrev.y=e.position.y-t.y,e.velocity.x=t.x,e.velocity.y=t.y,e.speed=r.magnitude(e.velocity)},i.setAngularVelocity=function(e,t){e.anglePrev=e.angle-t,e.angularVelocity=t,e.angularSpeed=Math.abs(e.angularVelocity)},i.translate=function(e,t){i.setPosition(e,r.add(e.position,t))},i.rotate=function(e,t,n){if(n){var o=Math.cos(t),r=Math.sin(t),a=e.position.x-n.x,s=e.position.y-n.y;i.setPosition(e,{x:n.x+(a*o-s*r),y:n.y+(a*r+s*o)}),i.setAngle(e,e.angle+t)}else i.setAngle(e,e.angle+t)},i.scale=function(e,t,n,r){var a=0,s=0;r=r||e.position;for(var u=0;u0&&(a+=d.area,s+=d.inertia),d.position.x=r.x+(d.position.x-r.x)*t,d.position.y=r.y+(d.position.y-r.y)*n,l.update(d.bounds,d.vertices,e.velocity)}e.parts.length>1&&(e.area=a,e.isStatic||(i.setMass(e,e.density*a),i.setInertia(e,s))),e.circleRadius&&(t===n?e.circleRadius*=t:e.circleRadius=null)},i.update=function(e,t,n,i){var a=Math.pow(t*n*e.timeScale,2),s=1-e.frictionAir*n*e.timeScale,u=e.position.x-e.positionPrev.x,d=e.position.y-e.positionPrev.y;e.velocity.x=u*s*i+e.force.x/e.mass*a,e.velocity.y=d*s*i+e.force.y/e.mass*a,e.positionPrev.x=e.position.x,e.positionPrev.y=e.position.y,e.position.x+=e.velocity.x,e.position.y+=e.velocity.y,e.angularVelocity=(e.angle-e.anglePrev)*s*i+e.torque/e.inertia*a,e.anglePrev=e.angle,e.angle+=e.angularVelocity,e.speed=r.magnitude(e.velocity),e.angularSpeed=Math.abs(e.angularVelocity);for(var p=0;p0&&(f.position.x+=e.velocity.x,f.position.y+=e.velocity.y),0!==e.angularVelocity&&(o.rotate(f.vertices,e.angularVelocity,e.position),c.rotate(f.axes,e.angularVelocity),p>0&&r.rotateAbout(f.position,e.angularVelocity,e.position,f.position)),l.update(f.bounds,f.vertices,e.velocity)}},i.applyForce=function(e,t,n){e.force.x+=n.x,e.force.y+=n.y;var i=t.x-e.position.x,o=t.y-e.position.y;e.torque+=i*n.y-o*n.x},i._totalProperties=function(e){for(var t={mass:0,area:0,inertia:0,centre:{x:0,y:0}},n=1===e.parts.length?0:1;n0&&r.motion=r.sleepThreshold&&i.set(r,!0)):r.sleepCounter>0&&(r.sleepCounter-=1)}else i.set(r,!1)}},i.afterCollisions=function(e,t){for(var n=t*t*t,o=0;oi._motionWakeThreshold*n&&i.set(c,!1)}}}},i.set=function(e,t){var n=e.isSleeping;t?(e.isSleeping=!0,e.sleepCounter=e.sleepThreshold,e.positionImpulse.x=0,e.positionImpulse.y=0,e.positionPrev.x=e.position.x,e.positionPrev.y=e.position.y,e.anglePrev=e.angle,e.speed=0,e.angularSpeed=0,e.motion=0,n||o.trigger(e,"sleepStart")):(e.isSleeping=!1,e.sleepCounter=0,n&&o.trigger(e,"sleepEnd"))}},function(e,t,n){var i={};e.exports=i;var o,r,a,s=n(3),l=n(9);o=[],r={overlap:0,axis:null},a={overlap:0,axis:null},i.create=function(e,t){return{pair:null,collided:!1,bodyA:e,bodyB:t,parentA:e.parent,parentB:t.parent,depth:0,normal:{x:0,y:0},tangent:{x:0,y:0},penetration:{x:0,y:0},supports:[]}},i.collides=function(e,t,n){if(i._overlapAxes(r,e.vertices,t.vertices,e.axes),r.overlap<=0)return null;if(i._overlapAxes(a,t.vertices,e.vertices,t.axes),a.overlap<=0)return null;var o,c,u=n&&n.table[l.id(e,t)];u?o=u.collision:((o=i.create(e,t)).collided=!0,o.bodyA=e.idP?P=s:sC?C=s:so?o=a:al.frictionStatic?s.frictionStatic:l.frictionStatic,e.restitution=s.restitution>l.restitution?s.restitution:l.restitution,e.slop=s.slop>l.slop?s.slop:l.slop,t.pair=e,a.length=0;for(var u=0;u0?1:.7),t.damping=t.damping||0,t.angularStiffness=t.angularStiffness||0,t.angleA=t.bodyA?t.bodyA.angle:t.angleA,t.angleB=t.bodyB?t.bodyB.angle:t.angleB,t.plugin={};var a={visible:!0,lineWidth:2,strokeStyle:"#ffffff",type:"line",anchors:!0};return 0===t.length&&t.stiffness>.1?(a.type="pin",a.anchors=!1):t.stiffness<.9&&(a.type="spring"),t.render=c.extend(a,t.render),t},i.preSolveAll=function(e){for(var t=0;t0&&(d.position.x+=c.x,d.position.y+=c.y),0!==c.angle&&(o.rotate(d.vertices,c.angle,n.position),l.rotate(d.axes,c.angle),u>0&&r.rotateAbout(d.position,c.angle,n.position,d.position)),s.update(d.bounds,d.vertices,n.velocity)}c.angle*=i._warming,c.x*=i._warming,c.y*=i._warming}}},i.pointAWorld=function(e){return{x:(e.bodyA?e.bodyA.position.x:0)+e.pointA.x,y:(e.bodyA?e.bodyA.position.y:0)+e.pointA.y}},i.pointBWorld=function(e){return{x:(e.bodyB?e.bodyB.position.x:0)+e.pointB.x,y:(e.bodyB?e.bodyB.position.y:0)+e.pointB.y}}},function(e,t,n){var i={};e.exports=i;var o=n(2),r=n(0);i.fromVertices=function(e){for(var t={},n=0;n0&&o.area(M)1?(v=a.create(r.extend({parts:y.slice(0)},i)),a.setPosition(v,{x:e,y:t}),v):y[0]}},function(e,t,n){var i={};e.exports=i;var o=n(0);i.create=function(e){var t={};return e||o.log("Mouse.create: element was undefined, defaulting to document.body","warn"),t.element=e||document.body,t.absolute={x:0,y:0},t.position={x:0,y:0},t.mousedownPosition={x:0,y:0},t.mouseupPosition={x:0,y:0},t.offset={x:0,y:0},t.scale={x:1,y:1},t.wheelDelta=0,t.button=-1,t.pixelRatio=parseInt(t.element.getAttribute("data-pixel-ratio"),10)||1,t.sourceEvents={mousemove:null,mousedown:null,mouseup:null,mousewheel:null},t.mousemove=function(e){var n=i._getRelativeMousePosition(e,t.element,t.pixelRatio);e.changedTouches&&(t.button=0,e.preventDefault()),t.absolute.x=n.x,t.absolute.y=n.y,t.position.x=t.absolute.x*t.scale.x+t.offset.x,t.position.y=t.absolute.y*t.scale.y+t.offset.y,t.sourceEvents.mousemove=e},t.mousedown=function(e){var n=i._getRelativeMousePosition(e,t.element,t.pixelRatio);e.changedTouches?(t.button=0,e.preventDefault()):t.button=e.button,t.absolute.x=n.x,t.absolute.y=n.y,t.position.x=t.absolute.x*t.scale.x+t.offset.x,t.position.y=t.absolute.y*t.scale.y+t.offset.y,t.mousedownPosition.x=t.position.x,t.mousedownPosition.y=t.position.y,t.sourceEvents.mousedown=e},t.mouseup=function(e){var n=i._getRelativeMousePosition(e,t.element,t.pixelRatio);e.changedTouches&&e.preventDefault(),t.button=-1,t.absolute.x=n.x,t.absolute.y=n.y,t.position.x=t.absolute.x*t.scale.x+t.offset.x,t.position.y=t.absolute.y*t.scale.y+t.offset.y,t.mouseupPosition.x=t.position.x,t.mouseupPosition.y=t.position.y,t.sourceEvents.mouseup=e},t.mousewheel=function(e){t.wheelDelta=Math.max(-1,Math.min(1,e.wheelDelta||-e.detail)),e.preventDefault()},i.setElement(t,t.element),t},i.setElement=function(e,t){e.element=t,t.addEventListener("mousemove",e.mousemove),t.addEventListener("mousedown",e.mousedown),t.addEventListener("mouseup",e.mouseup),t.addEventListener("mousewheel",e.mousewheel),t.addEventListener("DOMMouseScroll",e.mousewheel),t.addEventListener("touchmove",e.mousemove),t.addEventListener("touchstart",e.mousedown),t.addEventListener("touchend",e.mouseup)},i.clearSourceEvents=function(e){e.sourceEvents.mousemove=null,e.sourceEvents.mousedown=null,e.sourceEvents.mouseup=null,e.sourceEvents.mousewheel=null,e.wheelDelta=0},i.setOffset=function(e,t){e.offset.x=t.x,e.offset.y=t.y,e.position.x=e.absolute.x*e.scale.x+e.offset.x,e.position.y=e.absolute.y*e.scale.y+e.offset.y},i.setScale=function(e,t){e.scale.x=t.x,e.scale.y=t.y,e.position.x=e.absolute.x*e.scale.x+e.offset.x,e.position.y=e.absolute.y*e.scale.y+e.offset.y},i._getRelativeMousePosition=function(e,t,n){var i,o,r=t.getBoundingClientRect(),a=document.documentElement||document.body.parentNode||document.body,s=void 0!==window.pageXOffset?window.pageXOffset:a.scrollLeft,l=void 0!==window.pageYOffset?window.pageYOffset:a.scrollTop,c=e.changedTouches;return c?(i=c[0].pageX-r.left-s,o=c[0].pageY-r.top-l):(i=e.pageX-r.left-s,o=e.pageY-r.top-l),{x:i/(t.clientWidth/(t.width||t.clientWidth)*n),y:o/(t.clientHeight/(t.height||t.clientHeight)*n)}}},function(e,t,n){var i={};e.exports=i;var o=n(0),r=n(8);i.create=function(e){return o.extend({bodies:[],pairs:null},e)},i.setBodies=function(e,t){e.bodies=t.slice(0)},i.clear=function(e){e.bodies=[]},i.collisions=function(e){var t,n,o=[],a=e.pairs,s=e.bodies,l=s.length,c=i.canCollide,u=r.collides;for(s.sort(i._compareBoundsX),t=0;tf)break;if(!(vB.max.y)&&(!m||!h.isStatic&&!h.isSleeping)&&c(d.collisionFilter,h.collisionFilter)){var b=h.parts.length;if(x&&1===b)(C=u(d,h,a))&&o.push(C);else for(var S=b>1?1:0,w=g>1?1:0;wB.max.x||p.max.xB.max.y||(C=u(A,M,a))&&o.push(C)}}}}return o},i.canCollide=function(e,t){return e.group===t.group&&0!==e.group?e.group>0:0!=(e.mask&t.category)&&0!=(t.mask&e.category)},i._compareBoundsX=function(e,t){return e.bounds.min.x-t.bounds.min.x}},function(e,t,n){var i={};e.exports=i;var o=n(0);i._registry={},i.register=function(e){if(i.isPlugin(e)||o.warn("Plugin.register:",i.toString(e),"does not implement all required fields."),e.name in i._registry){var t=i._registry[e.name],n=i.versionParse(e.version).number,r=i.versionParse(t.version).number;n>r?(o.warn("Plugin.register:",i.toString(t),"was upgraded to",i.toString(e)),i._registry[e.name]=e):n-1},i.isFor=function(e,t){var n=e.for&&i.dependencyParse(e.for);return!e.for||t.name===n.name&&i.versionSatisfies(t.version,n.range)},i.use=function(e,t){if(e.uses=(e.uses||[]).concat(t||[]),0!==e.uses.length){for(var n=i.dependencies(e),r=o.topologicalSort(n),a=[],s=0;s0&&o.info(a.join(" "))}else o.warn("Plugin.use:",i.toString(e),"does not specify any dependencies to install.")},i.dependencies=function(e,t){var n=i.dependencyParse(e),r=n.name;if(!(r in(t=t||{}))){e=i.resolve(e)||e,t[r]=o.map(e.uses||[],(function(t){i.isPlugin(t)&&i.register(t);var r=i.dependencyParse(t),a=i.resolve(t);return a&&!i.versionSatisfies(a.version,r.range)?(o.warn("Plugin.dependencies:",i.toString(a),"does not satisfy",i.toString(r),"used by",i.toString(n)+"."),a._warned=!0,e._warned=!0):a||(o.warn("Plugin.dependencies:",i.toString(t),"used by",i.toString(n),"could not be resolved."),e._warned=!0),r.name}));for(var a=0;a=|>)?\s*((\d+)\.(\d+)\.(\d+))(-[0-9A-Za-z-+]+)?$/;t.test(e)||o.warn("Plugin.versionParse:",e,"is not a valid version or range.");var n=t.exec(e),i=Number(n[4]),r=Number(n[5]),a=Number(n[6]);return{isRange:Boolean(n[1]||n[2]),version:n[3],range:e,operator:n[1]||n[2]||"",major:i,minor:r,patch:a,parts:[i,r,a],prerelease:n[7],number:1e8*i+1e4*r+a}},i.versionSatisfies=function(e,t){t=t||"*";var n=i.versionParse(t),o=i.versionParse(e);if(n.isRange){if("*"===n.operator||"*"===e)return!0;if(">"===n.operator)return o.number>n.number;if(">="===n.operator)return o.number>=n.number;if("~"===n.operator)return o.major===n.major&&o.minor===n.minor&&o.patch>=n.patch;if("^"===n.operator)return n.major>0?o.major===n.major&&o.number>=n.number:n.minor>0?o.minor===n.minor&&o.patch>=n.patch:o.patch===n.patch}return e===t||"*"===e}},function(e,t,n){var i={};e.exports=i;var o=n(0),r=n(5),a=n(1),s=n(4),l=n(2),c=n(13);!function(){var e,t;"undefined"!=typeof window&&(e=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||function(e){window.setTimeout((function(){e(o.now())}),1e3/60)},t=window.cancelAnimationFrame||window.mozCancelAnimationFrame||window.webkitCancelAnimationFrame||window.msCancelAnimationFrame),i._goodFps=30,i._goodDelta=1e3/60,i.create=function(e){var t={controller:i,engine:null,element:null,canvas:null,mouse:null,frameRequestId:null,timing:{historySize:60,delta:0,deltaHistory:[],lastTime:0,lastTimestamp:0,lastElapsed:0,timestampElapsed:0,timestampElapsedHistory:[],engineDeltaHistory:[],engineElapsedHistory:[],elapsedHistory:[]},options:{width:800,height:600,pixelRatio:1,background:"#14151f",wireframeBackground:"#14151f",hasBounds:!!e.bounds,enabled:!0,wireframes:!0,showSleeping:!0,showDebug:!1,showStats:!1,showPerformance:!1,showBounds:!1,showVelocity:!1,showCollisions:!1,showSeparations:!1,showAxes:!1,showPositions:!1,showAngleIndicator:!1,showIds:!1,showVertexNumbers:!1,showConvexHulls:!1,showInternalEdges:!1,showMousePosition:!1}},n=o.extend(t,e);return n.canvas&&(n.canvas.width=n.options.width||n.canvas.width,n.canvas.height=n.options.height||n.canvas.height),n.mouse=e.mouse,n.engine=e.engine,n.canvas=n.canvas||d(n.options.width,n.options.height),n.context=n.canvas.getContext("2d"),n.textures={},n.bounds=n.bounds||{min:{x:0,y:0},max:{x:n.canvas.width,y:n.canvas.height}},n.options.showBroadphase=!1,1!==n.options.pixelRatio&&i.setPixelRatio(n,n.options.pixelRatio),o.isElement(n.element)?n.element.appendChild(n.canvas):n.canvas.parentNode||o.log("Render.create: options.element was undefined, render.canvas was created but not appended","warn"),n},i.run=function(t){!function o(r){t.frameRequestId=e(o),n(t,r),i.world(t,r),(t.options.showStats||t.options.showDebug)&&i.stats(t,t.context,r),(t.options.showPerformance||t.options.showDebug)&&i.performance(t,t.context,r)}()},i.stop=function(e){t(e.frameRequestId)},i.setPixelRatio=function(e,t){var n=e.options,i=e.canvas;"auto"===t&&(t=p(i)),n.pixelRatio=t,i.setAttribute("data-pixel-ratio",t),i.width=n.width*t,i.height=n.height*t,i.style.width=n.width+"px",i.style.height=n.height+"px"},i.lookAt=function(e,t,n,i){i=void 0===i||i,t=o.isArray(t)?t:[t],n=n||{x:0,y:0};for(var r={min:{x:1/0,y:1/0},max:{x:-1/0,y:-1/0}},a=0;ar.max.x&&(r.max.x=u.x),l.yr.max.y&&(r.max.y=u.y))}var d=r.max.x-r.min.x+2*n.x,p=r.max.y-r.min.y+2*n.y,f=e.canvas.height,v=e.canvas.width/f,y=d/p,m=1,g=1;y>v?g=y/v:m=v/y,e.options.hasBounds=!0,e.bounds.min.x=r.min.x,e.bounds.max.x=r.min.x+d*m,e.bounds.min.y=r.min.y,e.bounds.max.y=r.min.y+p*g,i&&(e.bounds.min.x+=.5*d-d*m*.5,e.bounds.max.x+=.5*d-d*m*.5,e.bounds.min.y+=.5*p-p*g*.5,e.bounds.max.y+=.5*p-p*g*.5),e.bounds.min.x-=n.x,e.bounds.max.x-=n.x,e.bounds.min.y-=n.y,e.bounds.max.y-=n.y,e.mouse&&(c.setScale(e.mouse,{x:(e.bounds.max.x-e.bounds.min.x)/e.canvas.width,y:(e.bounds.max.y-e.bounds.min.y)/e.canvas.height}),c.setOffset(e.mouse,e.bounds.min))},i.startViewTransform=function(e){var t=e.bounds.max.x-e.bounds.min.x,n=e.bounds.max.y-e.bounds.min.y,i=t/e.options.width,o=n/e.options.height;e.context.setTransform(e.options.pixelRatio/i,0,0,e.options.pixelRatio/o,0,0),e.context.translate(-e.bounds.min.x,-e.bounds.min.y)},i.endViewTransform=function(e){e.context.setTransform(e.options.pixelRatio,0,0,e.options.pixelRatio,0,0)},i.world=function(e,t){var n,u=o.now(),d=e.engine,p=d.world,f=e.canvas,y=e.context,m=e.options,g=e.timing,x=r.allBodies(p),h=r.allConstraints(p),b=m.wireframes?m.wireframeBackground:m.background,S=[],w=[],A={timestamp:d.timing.timestamp};if(s.trigger(e,"beforeRender",A),e.currentBackground!==b&&v(e,b),y.globalCompositeOperation="source-in",y.fillStyle="transparent",y.fillRect(0,0,f.width,f.height),y.globalCompositeOperation="source-over",m.hasBounds){for(n=0;n1?1:0;a1?1:0;s1?1:0;r1?1:0;s1?1:0;r1?1:0;r1?1:0;o0)){var u=i.activeContacts[0].vertex.x,d=i.activeContacts[0].vertex.y;2===i.activeContacts.length&&(u=(i.activeContacts[0].vertex.x+i.activeContacts[1].vertex.x)/2,d=(i.activeContacts[0].vertex.y+i.activeContacts[1].vertex.y)/2),o.bodyB===o.supports[0].body||!0===o.bodyA.isStatic?s.moveTo(u-8*o.normal.x,d-8*o.normal.y):s.moveTo(u+8*o.normal.x,d+8*o.normal.y),s.lineTo(u,d)}l.wireframes?s.strokeStyle="rgba(255,165,0,0.7)":s.strokeStyle="orange",s.lineWidth=1,s.stroke()},i.separations=function(e,t,n){var i,o,r,a,s,l=n,c=e.options;for(l.beginPath(),s=0;s0&&l.trigger(e,"collisionStart",{pairs:m.collisionStart}),r.preSolvePosition(m.list),f=0;f0&&l.trigger(e,"collisionActive",{pairs:m.collisionActive}),m.collisionEnd.length>0&&l.trigger(e,"collisionEnd",{pairs:m.collisionEnd}),i._bodiesClearForces(b),l.trigger(e,"afterUpdate",h),e.timing.lastElapsed=d.now()-p,e},i.merge=function(e,t){if(d.extend(e,t),t.world){e.world=t.world,i.clear(e);for(var n=c.allBodies(e.world),r=0;rW||-H>W?(o=H>0?H:-H,(n=f.friction*(H>0?1:-1)*s)<-o?n=-o:n>o&&(n=o)):(n=H,o=d);var G=I*b-T*h,N=R*b-E*h,U=C/(M+y.inverseInertia*G*G+m.inverseInertia*N*N),z=(1+f.restitution)*F*U;if(n*=U,F*F>l&&F<0)k.normalImpulse=0;else{var X=k.normalImpulse;k.normalImpulse+=z,k.normalImpulse=Math.min(k.normalImpulse,0),z=k.normalImpulse-X}if(H*H>u)k.tangentImpulse=0;else{var Q=k.tangentImpulse;k.tangentImpulse+=n,k.tangentImpulse<-o&&(k.tangentImpulse=-o),k.tangentImpulse>o&&(k.tangentImpulse=o),n=k.tangentImpulse-Q}var Y=h*z+S*n,Z=b*z+w*n;y.isStatic||y.isSleeping||(y.positionPrev.x+=Y*y.inverseMass,y.positionPrev.y+=Z*y.inverseMass,y.anglePrev+=(I*Z-T*Y)*y.inverseInertia),m.isStatic||m.isSleeping||(m.positionPrev.x-=Y*m.inverseMass,m.positionPrev.y-=Z*m.inverseMass,m.anglePrev-=(R*Z-E*Y)*m.inverseInertia)}}}}},function(e,t,n){var i={};e.exports=i;var o=n(9),r=n(0);i.create=function(e){return r.extend({table:{},list:[],collisionStart:[],collisionActive:[],collisionEnd:[]},e)},i.update=function(e,t,n){var i,r,a,s,l=e.list,c=l.length,u=e.table,d=t.length,p=e.collisionStart,f=e.collisionEnd,v=e.collisionActive;for(p.length=0,f.length=0,v.length=0,s=0;sy&&(y=x),s.translate(g,{x:.5*h,y:.5*x}),d=g.bounds.max.x+r,o.addBody(u,g),c=g,f+=1}else d+=r}p+=y+a,d=e}return u},i.chain=function(e,t,n,i,s,l){for(var c=e.bodies,u=1;u0)for(c=0;c0&&(p=f[c-1+(l-1)*t],o.addConstraint(e,r.create(a.extend({bodyA:p,bodyB:d},s)))),i&&cp||a<(c=p-c)||a>n-1-c))return 1===d&&s.translate(u,{x:(a+(n%2==1?1:-1))*f,y:0}),l(e+(u?a*f:0)+a*r,i,a,c,u,d)}))},i.newtonsCradle=function(e,t,n,i,a){for(var s=o.create({label:"Newtons Cradle"}),c=0;cu.bounds.max.x||f.bounds.max.yu.bounds.max.y))){var v=i._getRegion(e,f);if(!f.region||v.id!==f.region.id||o){f.region&&!o||(f.region=v);var y=i._regionUnion(v,f.region);for(a=y.startCol;a<=y.endCol;a++)for(s=y.startRow;s<=y.endRow;s++){l=d[c=i._getBucketId(a,s)];var m=a>=v.startCol&&a<=v.endCol&&s>=v.startRow&&s<=v.endRow,g=a>=f.region.startCol&&a<=f.region.endCol&&s>=f.region.startRow&&s<=f.region.endRow;!m&&g&&g&&l&&i._bucketRemoveBody(e,l,f),(f.region===v||m&&!g||o)&&(l||(l=i._createBucket(d,c)),i._bucketAddBody(e,l,f))}f.region=v,p=!0}}}p&&(e.pairsList=i._createActivePairsList(e))},a(i,"update","Grid.update ➤ replaced by Matter.Detector"),i.clear=function(e){e.buckets={},e.pairs={},e.pairsList=[]},a(i,"clear","Grid.clear ➤ replaced by Matter.Detector"),i._regionUnion=function(e,t){var n=Math.min(e.startCol,t.startCol),o=Math.max(e.endCol,t.endCol),r=Math.min(e.startRow,t.startRow),a=Math.max(e.endRow,t.endRow);return i._createRegion(n,o,r,a)},i._getRegion=function(e,t){var n=t.bounds,o=Math.floor(n.min.x/e.bucketWidth),r=Math.floor(n.max.x/e.bucketWidth),a=Math.floor(n.min.y/e.bucketHeight),s=Math.floor(n.max.y/e.bucketHeight);return i._createRegion(o,r,a,s)},i._createRegion=function(e,t,n,i){return{id:e+","+t+","+n+","+i,startCol:e,endCol:t,startRow:n,endRow:i}},i._getBucketId=function(e,t){return"C"+e+"R"+t},i._createBucket=function(e,t){return e[t]=[]},i._bucketAddBody=function(e,t,n){var i,r=e.pairs,a=o.id,s=t.length;for(i=0;i0?s.push(t):delete i[o[n]];return s}},function(e,t,n){var i={};e.exports=i;var o=n(3),r=n(7),a=n(13),s=n(4),l=n(14),c=n(10),u=n(5),d=n(0),p=n(1);i.create=function(e,t){var n=(e?e.mouse:null)||(t?t.mouse:null);n||(e&&e.render&&e.render.canvas?n=a.create(e.render.canvas):t&&t.element?n=a.create(t.element):(n=a.create(),d.warn("MouseConstraint.create: options.mouse was undefined, options.element was undefined, may not function as expected")));var o={type:"mouseConstraint",mouse:n,element:null,body:null,constraint:c.create({label:"Mouse Constraint",pointA:n.position,pointB:{x:0,y:0},length:.01,stiffness:.1,angularStiffness:1,render:{strokeStyle:"#90EE90",lineWidth:3}}),collisionFilter:{category:1,mask:4294967295,group:0}},r=d.extend(o,t);return s.on(e,"beforeUpdate",(function(){var t=u.allBodies(e.world);i.update(r,t),i._triggerEvents(r)})),r},i.update=function(e,t){var n=e.mouse,i=e.constraint,a=e.body;if(0===n.button){if(i.bodyB)r.set(i.bodyB,!1),i.pointA=n.position;else for(var c=0;c1?1:0;ue.deltaMax?e.deltaMax:i)/e.delta,e.delta=i),0!==e.timeScalePrev&&(s*=a.timeScale/e.timeScalePrev),0===a.timeScale&&(s=0),e.timeScalePrev=a.timeScale,e.correction=s,e.frameCounter+=1,n-e.counterTimestamp>=1e3&&(e.fps=e.frameCounter*((n-e.counterTimestamp)/1e3),e.counterTimestamp=n,e.frameCounter=0),o.trigger(e,"tick",l),o.trigger(e,"beforeUpdate",l),r.update(t,i,s),o.trigger(e,"afterUpdate",l),o.trigger(e,"afterTick",l)},i.stop=function(e){t(e.frameRequestId)},i.start=function(e,t){i.run(e,t)}}()},function(e,t,n){var i={};e.exports=i;var o=n(8),r=n(0).deprecated;i.collides=function(e,t){return o.collides(e,t)},r(i,"collides","SAT.collides ➤ replaced by Collision.collides")},function(e,t,n){var i={};e.exports=i;n(1);var o=n(0);i.pathToVertices=function(e,t){"undefined"==typeof window||"SVGPathSeg"in window||o.warn("Svg.pathToVertices: SVGPathSeg not defined, a polyfill is required.");var n,r,a,s,l,c,u,d,p,f,v,y=[],m=0,g=0,x=0;t=t||15;var h=function(e,t,n){var i=n%2==1&&n>1;if(!p||e!=p.x||t!=p.y){p&&i?(f=p.x,v=p.y):(f=0,v=0);var o={x:f+e,y:v+t};!i&&p||(p=o),y.push(o),g=f+e,x=v+t}},b=function(e){var t=e.pathSegTypeAsLetter.toUpperCase();if("Z"!==t){switch(t){case"M":case"L":case"T":case"C":case"S":case"Q":g=e.x,x=e.y;break;case"H":g=e.x;break;case"V":x=e.y}h(g,x,e.pathSegType)}};for(i._svgPathToAbsolute(e),a=e.getTotalLength(),c=[],n=0;n
-
-
-
-
-
-
+
+
+
+
+
+