UNPKG

spark-matter-js

Version:

A fork of the Matter.js 2D physics engine for Meta Spark

1,606 lines (1,398 loc) 258 kB
/*! * spark-matter-js 0.18.0 by @liabru * https://github.com/andypotato/spark-matter-js * License MIT * * The MIT License (MIT) * * Copyright (c) Liam Brummitt and contributors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define("Matter", [], factory); else if(typeof exports === 'object') exports["Matter"] = factory(); else root["Matter"] = factory(); })(this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 18); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports) { /** * The `Matter.Common` module contains utility functions that are common to all modules. * * @class Common */ var Common = {}; module.exports = Common; (function() { Common._nextId = 0; Common._seed = 0; Common._nowStartTime = +(new Date()); Common._warnedOnce = {}; Common._decomp = null; /** * Extends the object in the first argument using the object in the second argument. * @method extend * @param {} obj * @param {boolean} deep * @return {} obj extended */ Common.extend = function(obj, deep) { var argsStart, args, deepClone; if (typeof deep === 'boolean') { argsStart = 2; deepClone = deep; } else { argsStart = 1; deepClone = true; } for (var i = argsStart; i < arguments.length; i++) { var source = arguments[i]; if (source) { for (var prop in source) { if (deepClone && source[prop] && source[prop].constructor === Object) { if (!obj[prop] || obj[prop].constructor === Object) { obj[prop] = obj[prop] || {}; Common.extend(obj[prop], deepClone, source[prop]); } else { obj[prop] = source[prop]; } } else { obj[prop] = source[prop]; } } } } return obj; }; /** * Creates a new clone of the object, if deep is true references will also be cloned. * @method clone * @param {} obj * @param {bool} deep * @return {} obj cloned */ Common.clone = function(obj, deep) { return Common.extend({}, deep, obj); }; /** * Returns the list of keys for the given object. * @method keys * @param {} obj * @return {string[]} keys */ Common.keys = function(obj) { if (Object.keys) return Object.keys(obj); // avoid hasOwnProperty for performance var keys = []; for (var key in obj) keys.push(key); return keys; }; /** * Returns the list of values for the given object. * @method values * @param {} obj * @return {array} Array of the objects property values */ Common.values = function(obj) { var values = []; if (Object.keys) { var keys = Object.keys(obj); for (var i = 0; i < keys.length; i++) { values.push(obj[keys[i]]); } return values; } // avoid hasOwnProperty for performance for (var key in obj) values.push(obj[key]); return values; }; /** * Gets a value from `base` relative to the `path` string. * @method get * @param {} obj The base object * @param {string} path The path relative to `base`, e.g. 'Foo.Bar.baz' * @param {number} [begin] Path slice begin * @param {number} [end] Path slice end * @return {} The object at the given path */ Common.get = function(obj, path, begin, end) { path = path.split('.').slice(begin, end); for (var i = 0; i < path.length; i += 1) { obj = obj[path[i]]; } return obj; }; /** * Sets a value on `base` relative to the given `path` string. * @method set * @param {} obj The base object * @param {string} path The path relative to `base`, e.g. 'Foo.Bar.baz' * @param {} val The value to set * @param {number} [begin] Path slice begin * @param {number} [end] Path slice end * @return {} Pass through `val` for chaining */ Common.set = function(obj, path, val, begin, end) { var parts = path.split('.').slice(begin, end); Common.get(obj, path, 0, -1)[parts[parts.length - 1]] = val; return val; }; /** * Shuffles the given array in-place. * The function uses a seeded random generator. * @method shuffle * @param {array} array * @return {array} array shuffled randomly */ Common.shuffle = function(array) { for (var i = array.length - 1; i > 0; i--) { var j = Math.floor(Common.random() * (i + 1)); var temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; }; /** * Randomly chooses a value from a list with equal probability. * The function uses a seeded random generator. * @method choose * @param {array} choices * @return {object} A random choice object from the array */ Common.choose = function(choices) { return choices[Math.floor(Common.random() * choices.length)]; }; /** * Returns true if the object is a HTMLElement, otherwise false. * @method isElement * @param {object} obj * @return {boolean} True if the object is a HTMLElement, otherwise false */ Common.isElement = function(obj) { if (typeof HTMLElement !== 'undefined') { return obj instanceof HTMLElement; } return !!(obj && obj.nodeType && obj.nodeName); }; /** * Returns true if the object is an array. * @method isArray * @param {object} obj * @return {boolean} True if the object is an array, otherwise false */ Common.isArray = function(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; }; /** * Returns true if the object is a function. * @method isFunction * @param {object} obj * @return {boolean} True if the object is a function, otherwise false */ Common.isFunction = function(obj) { return typeof obj === "function"; }; /** * Returns true if the object is a plain object. * @method isPlainObject * @param {object} obj * @return {boolean} True if the object is a plain object, otherwise false */ Common.isPlainObject = function(obj) { return typeof obj === 'object' && obj.constructor === Object; }; /** * Returns true if the object is a string. * @method isString * @param {object} obj * @return {boolean} True if the object is a string, otherwise false */ Common.isString = function(obj) { return toString.call(obj) === '[object String]'; }; /** * Returns the given value clamped between a minimum and maximum value. * @method clamp * @param {number} value * @param {number} min * @param {number} max * @return {number} The value clamped between min and max inclusive */ Common.clamp = function(value, min, max) { if (value < min) return min; if (value > max) return max; return value; }; /** * Returns the sign of the given value. * @method sign * @param {number} value * @return {number} -1 if negative, +1 if 0 or positive */ Common.sign = function(value) { return value < 0 ? -1 : 1; }; /** * Returns the current timestamp since the time origin (e.g. from page load). * The result is in milliseconds and will use high-resolution timing if available. * @method now * @return {number} the current timestamp in milliseconds */ Common.now = function() { if (typeof window !== 'undefined' && window.performance) { if (window.performance.now) { return window.performance.now(); } else if (window.performance.webkitNow) { return window.performance.webkitNow(); } } if (Date.now) { return Date.now(); } return (new Date()) - Common._nowStartTime; }; /** * Returns a random value between a minimum and a maximum value inclusive. * The function uses a seeded random generator. * @method random * @param {number} min * @param {number} max * @return {number} A random number between min and max inclusive */ Common.random = function(min, max) { min = (typeof min !== "undefined") ? min : 0; max = (typeof max !== "undefined") ? max : 1; return min + _seededRandom() * (max - min); }; var _seededRandom = function() { // https://en.wikipedia.org/wiki/Linear_congruential_generator Common._seed = (Common._seed * 9301 + 49297) % 233280; return Common._seed / 233280; }; /** * Converts a CSS hex colour string into an integer. * @method colorToNumber * @param {string} colorString * @return {number} An integer representing the CSS hex string */ Common.colorToNumber = function(colorString) { colorString = colorString.replace('#',''); if (colorString.length == 3) { colorString = colorString.charAt(0) + colorString.charAt(0) + colorString.charAt(1) + colorString.charAt(1) + colorString.charAt(2) + colorString.charAt(2); } return parseInt(colorString, 16); }; /** * The console logging level to use, where each level includes all levels above and excludes the levels below. * The default level is 'debug' which shows all console messages. * * Possible level values are: * - 0 = None * - 1 = Debug * - 2 = Info * - 3 = Warn * - 4 = Error * @property Common.logLevel * @type {Number} * @default 1 */ Common.logLevel = 1; /** * Shows a `console.log` message only if the current `Common.logLevel` allows it. * The message will be prefixed with 'matter-js' to make it easily identifiable. * @method log * @param ...objs {} The objects to log. */ Common.log = function() { if (console && Common.logLevel > 0 && Common.logLevel <= 3) { console.log.apply(console, ['matter-js:'].concat(Array.prototype.slice.call(arguments))); } }; /** * Shows a `console.info` message only if the current `Common.logLevel` allows it. * The message will be prefixed with 'matter-js' to make it easily identifiable. * @method info * @param ...objs {} The objects to log. */ Common.info = function() { if (console && Common.logLevel > 0 && Common.logLevel <= 2) { console.info.apply(console, ['matter-js:'].concat(Array.prototype.slice.call(arguments))); } }; /** * Shows a `console.warn` message only if the current `Common.logLevel` allows it. * The message will be prefixed with 'matter-js' to make it easily identifiable. * @method warn * @param ...objs {} The objects to log. */ Common.warn = function() { if (console && Common.logLevel > 0 && Common.logLevel <= 3) { console.warn.apply(console, ['matter-js:'].concat(Array.prototype.slice.call(arguments))); } }; /** * Uses `Common.warn` to log the given message one time only. * @method warnOnce * @param ...objs {} The objects to log. */ Common.warnOnce = function() { var message = Array.prototype.slice.call(arguments).join(' '); if (!Common._warnedOnce[message]) { Common.warn(message); Common._warnedOnce[message] = true; } }; /** * Shows a deprecated console warning when the function on the given object is called. * The target function will be replaced with a new function that first shows the warning * and then calls the original function. * @method deprecated * @param {object} obj The object or module * @param {string} name The property name of the function on obj * @param {string} warning The one-time message to show if the function is called */ Common.deprecated = function(obj, prop, warning) { obj[prop] = Common.chain(function() { Common.warnOnce('🔅 deprecated 🔅', warning); }, obj[prop]); }; /** * Returns the next unique sequential ID. * @method nextId * @return {Number} Unique sequential ID */ Common.nextId = function() { return Common._nextId++; }; /** * A cross browser compatible indexOf implementation. * @method indexOf * @param {array} haystack * @param {object} needle * @return {number} The position of needle in haystack, otherwise -1. */ Common.indexOf = function(haystack, needle) { if (haystack.indexOf) return haystack.indexOf(needle); for (var i = 0; i < haystack.length; i++) { if (haystack[i] === needle) return i; } return -1; }; /** * A cross browser compatible array map implementation. * @method map * @param {array} list * @param {function} func * @return {array} Values from list transformed by func. */ Common.map = function(list, func) { if (list.map) { return list.map(func); } var mapped = []; for (var i = 0; i < list.length; i += 1) { mapped.push(func(list[i])); } return mapped; }; /** * Takes a directed graph and returns the partially ordered set of vertices in topological order. * Circular dependencies are allowed. * @method topologicalSort * @param {object} graph * @return {array} Partially ordered set of vertices in topological order. */ Common.topologicalSort = function(graph) { // https://github.com/mgechev/javascript-algorithms // Copyright (c) Minko Gechev (MIT license) // Modifications: tidy formatting and naming var result = [], visited = [], temp = []; for (var node in graph) { if (!visited[node] && !temp[node]) { Common._topologicalSort(node, visited, temp, graph, result); } } return result; }; Common._topologicalSort = function(node, visited, temp, graph, result) { var neighbors = graph[node] || []; temp[node] = true; for (var i = 0; i < neighbors.length; i += 1) { var neighbor = neighbors[i]; if (temp[neighbor]) { // skip circular dependencies continue; } if (!visited[neighbor]) { Common._topologicalSort(neighbor, visited, temp, graph, result); } } temp[node] = false; visited[node] = true; result.push(node); }; /** * Provide the [poly-decomp](https://github.com/schteppe/poly-decomp.js) library module to enable * concave vertex decomposition support when using `Bodies.fromVertices` e.g. `Common.setDecomp(require('poly-decomp'))`. * @method setDecomp * @param {} decomp The [poly-decomp](https://github.com/schteppe/poly-decomp.js) library module. */ Common.setDecomp = function(decomp) { Common._decomp = decomp; }; /** * Returns the [poly-decomp](https://github.com/schteppe/poly-decomp.js) library module provided through `Common.setDecomp`, * otherwise returns the global `decomp` if set. * @method getDecomp * @return {} The [poly-decomp](https://github.com/schteppe/poly-decomp.js) library module if provided. */ Common.getDecomp = function() { // get user provided decomp if set var decomp = Common._decomp; if(!decomp) { return null; } return decomp; }; })(); /***/ }), /* 1 */ /***/ (function(module, exports) { /** * The `Matter.Bounds` module contains methods for creating and manipulating axis-aligned bounding boxes (AABB). * * @class Bounds */ var Bounds = {}; module.exports = Bounds; (function() { /** * Creates a new axis-aligned bounding box (AABB) for the given vertices. * @method create * @param {vertices} vertices * @return {bounds} A new bounds object */ Bounds.create = function(vertices) { var bounds = { min: { x: 0, y: 0 }, max: { x: 0, y: 0 } }; if (vertices) Bounds.update(bounds, vertices); return bounds; }; /** * Updates bounds using the given vertices and extends the bounds given a velocity. * @method update * @param {bounds} bounds * @param {vertices} vertices * @param {vector} velocity */ Bounds.update = function(bounds, vertices, velocity) { bounds.min.x = Infinity; bounds.max.x = -Infinity; bounds.min.y = Infinity; bounds.max.y = -Infinity; for (var i = 0; i < vertices.length; i++) { var vertex = vertices[i]; if (vertex.x > bounds.max.x) bounds.max.x = vertex.x; if (vertex.x < bounds.min.x) bounds.min.x = vertex.x; if (vertex.y > bounds.max.y) bounds.max.y = vertex.y; if (vertex.y < bounds.min.y) bounds.min.y = vertex.y; } if (velocity) { if (velocity.x > 0) { bounds.max.x += velocity.x; } else { bounds.min.x += velocity.x; } if (velocity.y > 0) { bounds.max.y += velocity.y; } else { bounds.min.y += velocity.y; } } }; /** * Returns true if the bounds contains the given point. * @method contains * @param {bounds} bounds * @param {vector} point * @return {boolean} True if the bounds contain the point, otherwise false */ Bounds.contains = function(bounds, point) { return point.x >= bounds.min.x && point.x <= bounds.max.x && point.y >= bounds.min.y && point.y <= bounds.max.y; }; /** * Returns true if the two bounds intersect. * @method overlaps * @param {bounds} boundsA * @param {bounds} boundsB * @return {boolean} True if the bounds overlap, otherwise false */ Bounds.overlaps = function(boundsA, boundsB) { return (boundsA.min.x <= boundsB.max.x && boundsA.max.x >= boundsB.min.x && boundsA.max.y >= boundsB.min.y && boundsA.min.y <= boundsB.max.y); }; /** * Translates the bounds by the given vector. * @method translate * @param {bounds} bounds * @param {vector} vector */ Bounds.translate = function(bounds, vector) { bounds.min.x += vector.x; bounds.max.x += vector.x; bounds.min.y += vector.y; bounds.max.y += vector.y; }; /** * Shifts the bounds to the given position. * @method shift * @param {bounds} bounds * @param {vector} position */ Bounds.shift = function(bounds, position) { var deltaX = bounds.max.x - bounds.min.x, deltaY = bounds.max.y - bounds.min.y; bounds.min.x = position.x; bounds.max.x = position.x + deltaX; bounds.min.y = position.y; bounds.max.y = position.y + deltaY; }; })(); /***/ }), /* 2 */ /***/ (function(module, exports) { /** * The `Matter.Vector` module contains methods for creating and manipulating vectors. * Vectors are the basis of all the geometry related operations in the engine. * A `Matter.Vector` object is of the form `{ x: 0, y: 0 }`. * * See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). * * @class Vector */ // TODO: consider params for reusing vector objects var Vector = {}; module.exports = Vector; (function() { /** * Creates a new vector. * @method create * @param {number} x * @param {number} y * @return {vector} A new vector */ Vector.create = function(x, y) { return { x: x || 0, y: y || 0 }; }; /** * Returns a new vector with `x` and `y` copied from the given `vector`. * @method clone * @param {vector} vector * @return {vector} A new cloned vector */ Vector.clone = function(vector) { return { x: vector.x, y: vector.y }; }; /** * Returns the magnitude (length) of a vector. * @method magnitude * @param {vector} vector * @return {number} The magnitude of the vector */ Vector.magnitude = function(vector) { return Math.sqrt((vector.x * vector.x) + (vector.y * vector.y)); }; /** * Returns the magnitude (length) of a vector (therefore saving a `sqrt` operation). * @method magnitudeSquared * @param {vector} vector * @return {number} The squared magnitude of the vector */ Vector.magnitudeSquared = function(vector) { return (vector.x * vector.x) + (vector.y * vector.y); }; /** * Rotates the vector about (0, 0) by specified angle. * @method rotate * @param {vector} vector * @param {number} angle * @param {vector} [output] * @return {vector} The vector rotated about (0, 0) */ Vector.rotate = function(vector, angle, output) { var cos = Math.cos(angle), sin = Math.sin(angle); if (!output) output = {}; var x = vector.x * cos - vector.y * sin; output.y = vector.x * sin + vector.y * cos; output.x = x; return output; }; /** * Rotates the vector about a specified point by specified angle. * @method rotateAbout * @param {vector} vector * @param {number} angle * @param {vector} point * @param {vector} [output] * @return {vector} A new vector rotated about the point */ Vector.rotateAbout = function(vector, angle, point, output) { var cos = Math.cos(angle), sin = Math.sin(angle); if (!output) output = {}; var x = point.x + ((vector.x - point.x) * cos - (vector.y - point.y) * sin); output.y = point.y + ((vector.x - point.x) * sin + (vector.y - point.y) * cos); output.x = x; return output; }; /** * Normalises a vector (such that its magnitude is `1`). * @method normalise * @param {vector} vector * @return {vector} A new vector normalised */ Vector.normalise = function(vector) { var magnitude = Vector.magnitude(vector); if (magnitude === 0) return { x: 0, y: 0 }; return { x: vector.x / magnitude, y: vector.y / magnitude }; }; /** * Returns the dot-product of two vectors. * @method dot * @param {vector} vectorA * @param {vector} vectorB * @return {number} The dot product of the two vectors */ Vector.dot = function(vectorA, vectorB) { return (vectorA.x * vectorB.x) + (vectorA.y * vectorB.y); }; /** * Returns the cross-product of two vectors. * @method cross * @param {vector} vectorA * @param {vector} vectorB * @return {number} The cross product of the two vectors */ Vector.cross = function(vectorA, vectorB) { return (vectorA.x * vectorB.y) - (vectorA.y * vectorB.x); }; /** * Returns the cross-product of three vectors. * @method cross3 * @param {vector} vectorA * @param {vector} vectorB * @param {vector} vectorC * @return {number} The cross product of the three vectors */ Vector.cross3 = function(vectorA, vectorB, vectorC) { return (vectorB.x - vectorA.x) * (vectorC.y - vectorA.y) - (vectorB.y - vectorA.y) * (vectorC.x - vectorA.x); }; /** * Adds the two vectors. * @method add * @param {vector} vectorA * @param {vector} vectorB * @param {vector} [output] * @return {vector} A new vector of vectorA and vectorB added */ Vector.add = function(vectorA, vectorB, output) { if (!output) output = {}; output.x = vectorA.x + vectorB.x; output.y = vectorA.y + vectorB.y; return output; }; /** * Subtracts the two vectors. * @method sub * @param {vector} vectorA * @param {vector} vectorB * @param {vector} [output] * @return {vector} A new vector of vectorA and vectorB subtracted */ Vector.sub = function(vectorA, vectorB, output) { if (!output) output = {}; output.x = vectorA.x - vectorB.x; output.y = vectorA.y - vectorB.y; return output; }; /** * Multiplies a vector and a scalar. * @method mult * @param {vector} vector * @param {number} scalar * @return {vector} A new vector multiplied by scalar */ Vector.mult = function(vector, scalar) { return { x: vector.x * scalar, y: vector.y * scalar }; }; /** * Divides a vector and a scalar. * @method div * @param {vector} vector * @param {number} scalar * @return {vector} A new vector divided by scalar */ Vector.div = function(vector, scalar) { return { x: vector.x / scalar, y: vector.y / scalar }; }; /** * Returns the perpendicular vector. Set `negate` to true for the perpendicular in the opposite direction. * @method perp * @param {vector} vector * @param {bool} [negate=false] * @return {vector} The perpendicular vector */ Vector.perp = function(vector, negate) { negate = negate === true ? -1 : 1; return { x: negate * -vector.y, y: negate * vector.x }; }; /** * Negates both components of a vector such that it points in the opposite direction. * @method neg * @param {vector} vector * @return {vector} The negated vector */ Vector.neg = function(vector) { return { x: -vector.x, y: -vector.y }; }; /** * Returns the angle between the vector `vectorB - vectorA` and the x-axis in radians. * @method angle * @param {vector} vectorA * @param {vector} vectorB * @return {number} The angle in radians */ Vector.angle = function(vectorA, vectorB) { return Math.atan2(vectorB.y - vectorA.y, vectorB.x - vectorA.x); }; /** * Temporary vector pool (not thread-safe). * @property _temp * @type {vector[]} * @private */ Vector._temp = [ Vector.create(), Vector.create(), Vector.create(), Vector.create(), Vector.create(), Vector.create() ]; })(); /***/ }), /* 3 */ /***/ (function(module, exports, __webpack_require__) { /** * The `Matter.Vertices` module contains methods for creating and manipulating sets of vertices. * A set of vertices is an array of `Matter.Vector` with additional indexing properties inserted by `Vertices.create`. * A `Matter.Body` maintains a set of vertices to represent the shape of the object (its convex hull). * * See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). * * @class Vertices */ var Vertices = {}; module.exports = Vertices; var Vector = __webpack_require__(2); var Common = __webpack_require__(0); (function() { /** * Creates a new set of `Matter.Body` compatible vertices. * The `points` argument accepts an array of `Matter.Vector` points orientated around the origin `(0, 0)`, for example: * * [{ x: 0, y: 0 }, { x: 25, y: 50 }, { x: 50, y: 0 }] * * The `Vertices.create` method returns a new array of vertices, which are similar to Matter.Vector objects, * but with some additional references required for efficient collision detection routines. * * Vertices must be specified in clockwise order. * * Note that the `body` argument is not optional, a `Matter.Body` reference must be provided. * * @method create * @param {vector[]} points * @param {body} body */ Vertices.create = function(points, body) { var vertices = []; for (var i = 0; i < points.length; i++) { var point = points[i], vertex = { x: point.x, y: point.y, index: i, body: body, isInternal: false }; vertices.push(vertex); } return vertices; }; /** * Parses a string containing ordered x y pairs separated by spaces (and optionally commas), * into a `Matter.Vertices` object for the given `Matter.Body`. * For parsing SVG paths, see `Svg.pathToVertices`. * @method fromPath * @param {string} path * @param {body} body * @return {vertices} vertices */ Vertices.fromPath = function(path, body) { var pathPattern = /L?\s*([-\d.e]+)[\s,]*([-\d.e]+)*/ig, points = []; path.replace(pathPattern, function(match, x, y) { points.push({ x: parseFloat(x), y: parseFloat(y) }); }); return Vertices.create(points, body); }; /** * Returns the centre (centroid) of the set of vertices. * @method centre * @param {vertices} vertices * @return {vector} The centre point */ Vertices.centre = function(vertices) { var area = Vertices.area(vertices, true), centre = { x: 0, y: 0 }, cross, temp, j; for (var i = 0; i < vertices.length; i++) { j = (i + 1) % vertices.length; cross = Vector.cross(vertices[i], vertices[j]); temp = Vector.mult(Vector.add(vertices[i], vertices[j]), cross); centre = Vector.add(centre, temp); } return Vector.div(centre, 6 * area); }; /** * Returns the average (mean) of the set of vertices. * @method mean * @param {vertices} vertices * @return {vector} The average point */ Vertices.mean = function(vertices) { var average = { x: 0, y: 0 }; for (var i = 0; i < vertices.length; i++) { average.x += vertices[i].x; average.y += vertices[i].y; } return Vector.div(average, vertices.length); }; /** * Returns the area of the set of vertices. * @method area * @param {vertices} vertices * @param {bool} signed * @return {number} The area */ Vertices.area = function(vertices, signed) { var area = 0, j = vertices.length - 1; for (var i = 0; i < vertices.length; i++) { area += (vertices[j].x - vertices[i].x) * (vertices[j].y + vertices[i].y); j = i; } if (signed) return area / 2; return Math.abs(area) / 2; }; /** * Returns the moment of inertia (second moment of area) of the set of vertices given the total mass. * @method inertia * @param {vertices} vertices * @param {number} mass * @return {number} The polygon's moment of inertia */ Vertices.inertia = function(vertices, mass) { var numerator = 0, denominator = 0, v = vertices, cross, j; // find the polygon's moment of inertia, using second moment of area // from equations at http://www.physicsforums.com/showthread.php?t=25293 for (var n = 0; n < v.length; n++) { j = (n + 1) % v.length; cross = Math.abs(Vector.cross(v[j], v[n])); numerator += cross * (Vector.dot(v[j], v[j]) + Vector.dot(v[j], v[n]) + Vector.dot(v[n], v[n])); denominator += cross; } return (mass / 6) * (numerator / denominator); }; /** * Translates the set of vertices in-place. * @method translate * @param {vertices} vertices * @param {vector} vector * @param {number} scalar */ Vertices.translate = function(vertices, vector, scalar) { 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; }; /** * Rotates the set of vertices in-place. * @method rotate * @param {vertices} vertices * @param {number} angle * @param {vector} point */ Vertices.rotate = function(vertices, angle, point) { if (angle === 0) return; var cos = Math.cos(angle), sin = Math.sin(angle), pointX = point.x, pointY = point.y, verticesLength = vertices.length, vertex, dx, dy, i; 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; }; /** * Returns `true` if the `point` is inside the set of `vertices`. * @method contains * @param {vertices} vertices * @param {vector} point * @return {boolean} True if the vertices contains point, otherwise false */ Vertices.contains = function(vertices, point) { 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; }; /** * Scales the vertices from a point (default is centre) in-place. * @method scale * @param {vertices} vertices * @param {number} scaleX * @param {number} scaleY * @param {vector} point */ Vertices.scale = function(vertices, scaleX, scaleY, point) { if (scaleX === 1 && scaleY === 1) return vertices; point = point || Vertices.centre(vertices); var vertex, delta; for (var i = 0; i < vertices.length; i++) { vertex = vertices[i]; delta = Vector.sub(vertex, point); vertices[i].x = point.x + delta.x * scaleX; vertices[i].y = point.y + delta.y * scaleY; } return vertices; }; /** * Chamfers a set of vertices by giving them rounded corners, returns a new set of vertices. * The radius parameter is a single number or an array to specify the radius for each vertex. * @method chamfer * @param {vertices} vertices * @param {number[]} radius * @param {number} quality * @param {number} qualityMin * @param {number} qualityMax */ Vertices.chamfer = function(vertices, radius, quality, qualityMin, qualityMax) { if (typeof radius === 'number') { radius = [radius]; } else { radius = radius || [8]; } // quality defaults to -1, which is auto quality = (typeof quality !== 'undefined') ? quality : -1; qualityMin = qualityMin || 2; qualityMax = qualityMax || 14; var newVertices = []; for (var i = 0; i < vertices.length; i++) { var prevVertex = vertices[i - 1 >= 0 ? i - 1 : vertices.length - 1], vertex = vertices[i], nextVertex = vertices[(i + 1) % vertices.length], currentRadius = radius[i < radius.length ? i : radius.length - 1]; if (currentRadius === 0) { newVertices.push(vertex); continue; } var prevNormal = Vector.normalise({ x: vertex.y - prevVertex.y, y: prevVertex.x - vertex.x }); var nextNormal = Vector.normalise({ x: nextVertex.y - vertex.y, y: vertex.x - nextVertex.x }); var diagonalRadius = Math.sqrt(2 * Math.pow(currentRadius, 2)), radiusVector = Vector.mult(Common.clone(prevNormal), currentRadius), midNormal = Vector.normalise(Vector.mult(Vector.add(prevNormal, nextNormal), 0.5)), scaledVertex = Vector.sub(vertex, Vector.mult(midNormal, diagonalRadius)); var precision = quality; if (quality === -1) { // automatically decide precision precision = Math.pow(currentRadius, 0.32) * 1.75; } precision = Common.clamp(precision, qualityMin, qualityMax); // use an even value for precision, more likely to reduce axes by using symmetry if (precision % 2 === 1) precision += 1; var alpha = Math.acos(Vector.dot(prevNormal, nextNormal)), theta = alpha / precision; for (var j = 0; j < precision; j++) { newVertices.push(Vector.add(Vector.rotate(radiusVector, theta * j), scaledVertex)); } } return newVertices; }; /** * Sorts the input vertices into clockwise order in place. * @method clockwiseSort * @param {vertices} vertices * @return {vertices} vertices */ Vertices.clockwiseSort = function(vertices) { var centre = Vertices.mean(vertices); vertices.sort(function(vertexA, vertexB) { return Vector.angle(centre, vertexA) - Vector.angle(centre, vertexB); }); return vertices; }; /** * Returns true if the vertices form a convex shape (vertices must be in clockwise order). * @method isConvex * @param {vertices} vertices * @return {bool} `true` if the `vertices` are convex, `false` if not (or `null` if not computable). */ Vertices.isConvex = function(vertices) { // http://paulbourke.net/geometry/polygonmesh/ // Copyright (c) Paul Bourke (use permitted) var flag = 0, n = vertices.length, i, j, k, z; if (n < 3) return null; for (i = 0; i < n; i++) { j = (i + 1) % n; k = (i + 2) % n; z = (vertices[j].x - vertices[i].x) * (vertices[k].y - vertices[j].y); z -= (vertices[j].y - vertices[i].y) * (vertices[k].x - vertices[j].x); if (z < 0) { flag |= 1; } else if (z > 0) { flag |= 2; } if (flag === 3) { return false; } } if (flag !== 0){ return true; } else { return null; } }; /** * Returns the convex hull of the input vertices as a new array of points. * @method hull * @param {vertices} vertices * @return [vertex] vertices */ Vertices.hull = function(vertices) { // http://geomalgorithms.com/a10-_hull-1.html var upper = [], lower = [], vertex, i; // sort vertices on x-axis (y-axis for ties) vertices = vertices.slice(0); vertices.sort(function(vertexA, vertexB) { var dx = vertexA.x - vertexB.x; return dx !== 0 ? dx : vertexA.y - vertexB.y; }); // build lower hull for (i = 0; i < vertices.length; i += 1) { vertex = vertices[i]; while (lower.length >= 2 && Vector.cross3(lower[lower.length - 2], lower[lower.length - 1], vertex) <= 0) { lower.pop(); } lower.push(vertex); } // build upper hull for (i = vertices.length - 1; i >= 0; i -= 1) { vertex = vertices[i]; while (upper.length >= 2 && Vector.cross3(upper[upper.length - 2], upper[upper.length - 1], vertex) <= 0) { upper.pop(); } upper.push(vertex); } // concatenation of the lower and upper hulls gives the convex hull // omit last points because they are repeated at the beginning of the other list upper.pop(); lower.pop(); return upper.concat(lower); }; })(); /***/ }), /* 4 */ /***/ (function(module, exports, __webpack_require__) { /** * The `Matter.Body` module contains methods for creating and manipulating body models. * A `Matter.Body` is a rigid body that can be simulated by a `Matter.Engine`. * Factories for commonly used body configurations (such as rectangles, circles and other polygons) can be found in the module `Matter.Bodies`. * * See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). * @class Body */ var Body = {}; module.exports = Body; var Vertices = __webpack_require__(3); var Vector = __webpack_require__(2); var Sleeping = __webpack_require__(5); var Common = __webpack_require__(0); var Bounds = __webpack_require__(1); var Axes = __webpack_require__(10); (function() { Body._inertiaScale = 4; Body._nextCollidingGroupId = 1; Body._nextNonCollidingGroupId = -1; Body._nextCategory = 0x0001; /** * Creates a new rigid body model. The options parameter is an object that specifies any properties you wish to override the defaults. * All properties have default values, and many are pre-calculated automatically based on other properties. * Vertices must be specified in clockwise order. * See the properties section below for detailed information on what you can pass via the `options` object. * @method create * @param {} options * @return {body} body */ Body.create = function(options) { var defaults = { id: Common.nextId(), type: 'body', label: 'Body', parts: [], plugin: {}, angle: 0, vertices: Vertices.fromPath('L 0 0 L 40 0 L 40 40 L 0 40'), position: { x: 0, y: 0 }, force: { x: 0, y: 0 }, torque: 0, positionImpulse: { x: 0, y: 0 }, constraintImpulse: { x: 0, y: 0, angle: 0 }, totalContacts: 0, speed: 0, angularSpeed: 0, velocity: { x: 0, y: 0 }, angularVelocity: 0, isSensor: false, isStatic: false, isSleeping: false, motion: 0, sleepThreshold: 60, density: 0.001, restitution: 0, friction: 0.1, frictionStatic: 0.5, frictionAir: 0.01, collisionFilter: { category: 0x0001, mask: 0xFFFFFFFF, group: 0 }, slop: 0.05, timeScale: 1, events: null, bounds: null, chamfer: null, circleRadius: 0, positionPrev: null, anglePrev: 0, parent: null, axes: null, area: 0, mass: 0, inertia: 0, _original: null }; var body = Common.extend(defaults, options); _initProperties(body, options); return body; }; /** * Returns the next unique group index for which bodies will collide. * If `isNonColliding` is `true`, returns the next unique group index for which bodies will _not_ collide. * See `body.collisionFilter` for more information. * @method nextGroup * @param {bool} [isNonColliding=false] * @return {Number} Unique group index */ Body.nextGroup = function(isNonColliding) { if (isNonColliding) return Body._nextNonCollidingGroupId--; return Body._nextCollidingGroupId++; }; /** * Returns the next unique category bitfield (starting after the initial default category `0x0001`). * There are 32 available. See `body.collisionFilter` for more information. * @method nextCategory * @return {Number} Unique category bitfield */ Body.nextCategory = function() { Body._nextCategory = Body._nextCategory << 1; return Body._nextCategory;