UNPKG

phaser-ce

Version:

Phaser CE (Community Edition) is a fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers.

1,415 lines (1,253 loc) 495 kB
/** * The MIT License (MIT) * * Copyright (c) 2015 p2.js authors * * 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 (e) { if(typeof exports == 'object') { module.exports = e(); } else if(typeof define == 'function' && false) { define(e); } else{ var f; typeof window != 'undefined' ? f = window : typeof global != 'undefined' ? f = global : typeof self != 'undefined' && (f = self),f.p2 = e(); } }(function () { var define,module,exports; return (function e (t,n,r) { function s (o,u) { if(!n[o]) { if(!t[o]) { var a = typeof require == 'function' && require; if(!u && a) { return a(o,!0); }if(i) { return i(o,!0); }throw new Error('Cannot find module \'' + o + '\''); }var f = n[o] = {exports: {}}; t[o][0].call(f.exports,function (e) { var n = t[o][1][e]; return s(n ? n : e); },f,f.exports,e,t,n,r); }return n[o].exports; }var i = typeof require == 'function' && require; for(var o = 0; o < r.length; o++) { s(r[o]); }return s; })({ 1: [ function (_dereq_,module,exports) { var Scalar = _dereq_('./Scalar'); module.exports = Line; /** * Container for line-related functions * @class Line */ function Line () {} /** * Compute the intersection between two lines. * @static * @method lineInt * @param {Array} l1 Line vector 1 * @param {Array} l2 Line vector 2 * @param {Number} precision Precision to use when checking if the lines are parallel * @return {Array} The intersection point. */ Line.lineInt = function (l1,l2,precision) { precision = precision || 0; var i = [ 0,0 ]; // point var a1, b1, c1, a2, b2, c2, det; // scalars a1 = l1[1][1] - l1[0][1]; b1 = l1[0][0] - l1[1][0]; c1 = a1 * l1[0][0] + b1 * l1[0][1]; a2 = l2[1][1] - l2[0][1]; b2 = l2[0][0] - l2[1][0]; c2 = a2 * l2[0][0] + b2 * l2[0][1]; det = a1 * b2 - a2 * b1; if (!Scalar.eq(det, 0, precision)) { // lines are not parallel i[0] = (b2 * c1 - b1 * c2) / det; i[1] = (a1 * c2 - a2 * c1) / det; } return i; }; /** * Checks if two line segments intersects. * @method segmentsIntersect * @param {Array} p1 The start vertex of the first line segment. * @param {Array} p2 The end vertex of the first line segment. * @param {Array} q1 The start vertex of the second line segment. * @param {Array} q2 The end vertex of the second line segment. * @return {Boolean} True if the two line segments intersect */ Line.segmentsIntersect = function (p1, p2, q1, q2) { var dx = p2[0] - p1[0]; var dy = p2[1] - p1[1]; var da = q2[0] - q1[0]; var db = q2[1] - q1[1]; // segments are parallel if(da * dy - db * dx == 0) { return false; } var s = (dx * (q1[1] - p1[1]) + dy * (p1[0] - q1[0])) / (da * dy - db * dx); var t = (da * (p1[1] - q1[1]) + db * (q1[0] - p1[0])) / (db * dx - da * dy); return (s >= 0 && s <= 1 && t >= 0 && t <= 1); }; },{'./Scalar': 4} ],2: [ function (_dereq_,module,exports) { module.exports = Point; /** * Point related functions * @class Point */ function Point () {} /** * Get the area of a triangle spanned by the three given points. Note that the area will be negative if the points are not given in counter-clockwise order. * @static * @method area * @param {Array} a * @param {Array} b * @param {Array} c * @return {Number} */ Point.area = function (a,b,c) { return (((b[0] - a[0]) * (c[1] - a[1])) - ((c[0] - a[0]) * (b[1] - a[1]))); }; Point.left = function (a,b,c) { return Point.area(a,b,c) > 0; }; Point.leftOn = function (a,b,c) { return Point.area(a, b, c) >= 0; }; Point.right = function (a,b,c) { return Point.area(a, b, c) < 0; }; Point.rightOn = function (a,b,c) { return Point.area(a, b, c) <= 0; }; var tmpPoint1 = [], tmpPoint2 = []; /** * Check if three points are collinear * @method collinear * @param {Array} a * @param {Array} b * @param {Array} c * @param {Number} [thresholdAngle=0] Threshold angle to use when comparing the vectors. The function will return true if the angle between the resulting vectors is less than this value. Use zero for max precision. * @return {Boolean} */ Point.collinear = function (a,b,c,thresholdAngle) { if(!thresholdAngle) { return Point.area(a, b, c) == 0; } else { var ab = tmpPoint1, bc = tmpPoint2; ab[0] = b[0] - a[0]; ab[1] = b[1] - a[1]; bc[0] = c[0] - b[0]; bc[1] = c[1] - b[1]; var dot = ab[0] * bc[0] + ab[1] * bc[1], magA = Math.sqrt(ab[0] * ab[0] + ab[1] * ab[1]), magB = Math.sqrt(bc[0] * bc[0] + bc[1] * bc[1]), angle = Math.acos(dot / (magA * magB)); return angle < thresholdAngle; } }; Point.sqdist = function (a,b) { var dx = b[0] - a[0]; var dy = b[1] - a[1]; return dx * dx + dy * dy; }; },{} ],3: [ function (_dereq_,module,exports) { var Line = _dereq_('./Line'), Point = _dereq_('./Point'), Scalar = _dereq_('./Scalar'); module.exports = Polygon; /** * Polygon class. * @class Polygon * @constructor */ function Polygon () { /** * Vertices that this polygon consists of. An array of array of numbers, example: [[0,0],[1,0],..] * @property vertices * @type {Array} */ this.vertices = []; } /** * Get a vertex at position i. It does not matter if i is out of bounds, this function will just cycle. * @method at * @param {Number} i * @return {Array} */ Polygon.prototype.at = function (i) { var v = this.vertices, s = v.length; return v[i < 0 ? i % s + s : i % s]; }; /** * Get first vertex * @method first * @return {Array} */ Polygon.prototype.first = function () { return this.vertices[0]; }; /** * Get last vertex * @method last * @return {Array} */ Polygon.prototype.last = function () { return this.vertices[this.vertices.length - 1]; }; /** * Clear the polygon data * @method clear * @return {Array} */ Polygon.prototype.clear = function () { this.vertices.length = 0; }; /** * Append points "from" to "to"-1 from an other polygon "poly" onto this one. * @method append * @param {Polygon} poly The polygon to get points from. * @param {Number} from The vertex index in "poly". * @param {Number} to The end vertex index in "poly". Note that this vertex is NOT included when appending. * @return {Array} */ Polygon.prototype.append = function (poly,from,to) { if(typeof(from) == 'undefined') { throw new Error('From is not given!'); } if(typeof(to) == 'undefined') { throw new Error('To is not given!'); } if(to - 1 < from) { throw new Error('lol1'); } if(to > poly.vertices.length) { throw new Error('lol2'); } if(from < 0) { throw new Error('lol3'); } for(var i = from; i < to; i++) { this.vertices.push(poly.vertices[i]); } }; /** * Make sure that the polygon vertices are ordered counter-clockwise. * @method makeCCW */ Polygon.prototype.makeCCW = function () { var br = 0, v = this.vertices; // find bottom right point for (var i = 1; i < this.vertices.length; ++i) { if (v[i][1] < v[br][1] || (v[i][1] == v[br][1] && v[i][0] > v[br][0])) { br = i; } } // reverse poly if clockwise if (!Point.left(this.at(br - 1), this.at(br), this.at(br + 1))) { this.reverse(); } }; /** * Reverse the vertices in the polygon * @method reverse */ Polygon.prototype.reverse = function () { var tmp = []; for(var i = 0, N = this.vertices.length; i !== N; i++) { tmp.push(this.vertices.pop()); } this.vertices = tmp; }; /** * Check if a point in the polygon is a reflex point * @method isReflex * @param {Number} i * @return {Boolean} */ Polygon.prototype.isReflex = function (i) { return Point.right(this.at(i - 1), this.at(i), this.at(i + 1)); }; var tmpLine1 = [], tmpLine2 = []; /** * Check if two vertices in the polygon can see each other * @method canSee * @param {Number} a Vertex index 1 * @param {Number} b Vertex index 2 * @return {Boolean} */ Polygon.prototype.canSee = function (a,b) { var p, dist, l1 = tmpLine1, l2 = tmpLine2; if (Point.leftOn(this.at(a + 1), this.at(a), this.at(b)) && Point.rightOn(this.at(a - 1), this.at(a), this.at(b))) { return false; } dist = Point.sqdist(this.at(a), this.at(b)); for (var i = 0; i !== this.vertices.length; ++i) { // for each edge if ((i + 1) % this.vertices.length === a || i === a) // ignore incident edges { continue; } if (Point.leftOn(this.at(a), this.at(b), this.at(i + 1)) && Point.rightOn(this.at(a), this.at(b), this.at(i))) { // if diag intersects an edge l1[0] = this.at(a); l1[1] = this.at(b); l2[0] = this.at(i); l2[1] = this.at(i + 1); p = Line.lineInt(l1,l2); if (Point.sqdist(this.at(a), p) < dist) { // if edge is blocking visibility to b return false; } } } return true; }; /** * Copy the polygon from vertex i to vertex j. * @method copy * @param {Number} i * @param {Number} j * @param {Polygon} [targetPoly] Optional target polygon to save in. * @return {Polygon} The resulting copy. */ Polygon.prototype.copy = function (i,j,targetPoly) { var p = targetPoly || new Polygon(); p.clear(); if (i < j) { // Insert all vertices from i to j for(var k = i; k <= j; k++) { p.vertices.push(this.vertices[k]); } } else { // Insert vertices 0 to j for(var k = 0; k <= j; k++) { p.vertices.push(this.vertices[k]); } // Insert vertices i to end for(var k = i; k < this.vertices.length; k++) { p.vertices.push(this.vertices[k]); } } return p; }; /** * Decomposes the polygon into convex pieces. Returns a list of edges [[p1,p2],[p2,p3],...] that cuts the polygon. * Note that this algorithm has complexity O(N^4) and will be very slow for polygons with many vertices. * @method getCutEdges * @return {Array} */ Polygon.prototype.getCutEdges = function () { var min = [], tmp1 = [], tmp2 = [], tmpPoly = new Polygon(); var nDiags = Number.MAX_VALUE; for (var i = 0; i < this.vertices.length; ++i) { if (this.isReflex(i)) { for (var j = 0; j < this.vertices.length; ++j) { if (this.canSee(i, j)) { tmp1 = this.copy(i, j, tmpPoly).getCutEdges(); tmp2 = this.copy(j, i, tmpPoly).getCutEdges(); for(var k = 0; k < tmp2.length; k++) { tmp1.push(tmp2[k]); } if (tmp1.length < nDiags) { min = tmp1; nDiags = tmp1.length; min.push([ this.at(i), this.at(j) ]); } } } } } return min; }; /** * Decomposes the polygon into one or more convex sub-Polygons. * @method decomp * @return {Array} An array or Polygon objects. */ Polygon.prototype.decomp = function () { var edges = this.getCutEdges(); if(edges.length > 0) { return this.slice(edges); } else { return [ this ]; } }; /** * Slices the polygon given one or more cut edges. If given one, this function will return two polygons (false on failure). If many, an array of polygons. * @method slice * @param {Array} cutEdges A list of edges, as returned by .getCutEdges() * @return {Array} */ Polygon.prototype.slice = function (cutEdges) { if(cutEdges.length == 0) { return [ this ]; } if(cutEdges instanceof Array && cutEdges.length && cutEdges[0] instanceof Array && cutEdges[0].length == 2 && cutEdges[0][0] instanceof Array) { var polys = [ this ]; for(var i = 0; i < cutEdges.length; i++) { var cutEdge = cutEdges[i]; // Cut all polys for(var j = 0; j < polys.length; j++) { var poly = polys[j]; var result = poly.slice(cutEdge); if(result) { // Found poly! Cut and quit polys.splice(j,1); polys.push(result[0],result[1]); break; } } } return polys; } else { // Was given one edge var cutEdge = cutEdges; var i = this.vertices.indexOf(cutEdge[0]); var j = this.vertices.indexOf(cutEdge[1]); if(i != -1 && j != -1) { return [ this.copy(i,j), this.copy(j,i) ]; } else { return false; } } }; /** * Checks that the line segments of this polygon do not intersect each other. * @method isSimple * @param {Array} path An array of vertices e.g. [[0,0],[0,1],...] * @return {Boolean} * @todo Should it check all segments with all others? */ Polygon.prototype.isSimple = function () { var path = this.vertices; // Check for(var i = 0; i < path.length - 1; i++) { for(var j = 0; j < i - 1; j++) { if(Line.segmentsIntersect(path[i], path[i + 1], path[j], path[j + 1])) { return false; } } } // Check the segment between the last and the first point to all others for(var i = 1; i < path.length - 2; i++) { if(Line.segmentsIntersect(path[0], path[path.length - 1], path[i], path[i + 1])) { return false; } } return true; }; function getIntersectionPoint (p1, p2, q1, q2, delta) { delta = delta || 0; var a1 = p2[1] - p1[1]; var b1 = p1[0] - p2[0]; var c1 = (a1 * p1[0]) + (b1 * p1[1]); var a2 = q2[1] - q1[1]; var b2 = q1[0] - q2[0]; var c2 = (a2 * q1[0]) + (b2 * q1[1]); var det = (a1 * b2) - (a2 * b1); if(!Scalar.eq(det,0,delta)) { return [ ((b2 * c1) - (b1 * c2)) / det, ((a1 * c2) - (a2 * c1)) / det ]; } else { return [ 0,0 ]; } } /** * Quickly decompose the Polygon into convex sub-polygons. * @method quickDecomp * @param {Array} result * @param {Array} [reflexVertices] * @param {Array} [steinerPoints] * @param {Number} [delta] * @param {Number} [maxlevel] * @param {Number} [level] * @return {Array} */ Polygon.prototype.quickDecomp = function (result,reflexVertices,steinerPoints,delta,maxlevel,level) { maxlevel = maxlevel || 100; level = level || 0; delta = delta || 25; result = typeof(result) != 'undefined' ? result : []; reflexVertices = reflexVertices || []; steinerPoints = steinerPoints || []; var upperInt = [ 0,0 ], lowerInt = [ 0,0 ], p = [ 0,0 ]; // Points var upperDist = 0, lowerDist = 0, d = 0, closestDist = 0; // scalars var upperIndex = 0, lowerIndex = 0, closestIndex = 0; // Integers var lowerPoly = new Polygon(), upperPoly = new Polygon(); // polygons var poly = this, v = this.vertices; if(v.length < 3) { return result; } level++; if(level > maxlevel) { console.warn('quickDecomp: max level (' + maxlevel + ') reached.'); return result; } for (var i = 0; i < this.vertices.length; ++i) { if (poly.isReflex(i)) { reflexVertices.push(poly.vertices[i]); upperDist = lowerDist = Number.MAX_VALUE; for (var j = 0; j < this.vertices.length; ++j) { if (Point.left(poly.at(i - 1), poly.at(i), poly.at(j)) && Point.rightOn(poly.at(i - 1), poly.at(i), poly.at(j - 1))) { // if line intersects with an edge p = getIntersectionPoint(poly.at(i - 1), poly.at(i), poly.at(j), poly.at(j - 1)); // find the point of intersection if (Point.right(poly.at(i + 1), poly.at(i), p)) { // make sure it's inside the poly d = Point.sqdist(poly.vertices[i], p); if (d < lowerDist) { // keep only the closest intersection lowerDist = d; lowerInt = p; lowerIndex = j; } } } if (Point.left(poly.at(i + 1), poly.at(i), poly.at(j + 1)) && Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) { p = getIntersectionPoint(poly.at(i + 1), poly.at(i), poly.at(j), poly.at(j + 1)); if (Point.left(poly.at(i - 1), poly.at(i), p)) { d = Point.sqdist(poly.vertices[i], p); if (d < upperDist) { upperDist = d; upperInt = p; upperIndex = j; } } } } // if there are no vertices to connect to, choose a point in the middle if (lowerIndex == (upperIndex + 1) % this.vertices.length) { // console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+this.vertices.length+")"); p[0] = (lowerInt[0] + upperInt[0]) / 2; p[1] = (lowerInt[1] + upperInt[1]) / 2; steinerPoints.push(p); if (i < upperIndex) { // lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.begin() + upperIndex + 1); lowerPoly.append(poly, i, upperIndex + 1); lowerPoly.vertices.push(p); upperPoly.vertices.push(p); if (lowerIndex != 0) { // upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.end()); upperPoly.append(poly,lowerIndex,poly.vertices.length); } // upperPoly.insert(upperPoly.end(), poly.begin(), poly.begin() + i + 1); upperPoly.append(poly,0,i + 1); } else { if (i != 0) { // lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.end()); lowerPoly.append(poly,i,poly.vertices.length); } // lowerPoly.insert(lowerPoly.end(), poly.begin(), poly.begin() + upperIndex + 1); lowerPoly.append(poly,0,upperIndex + 1); lowerPoly.vertices.push(p); upperPoly.vertices.push(p); // upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.begin() + i + 1); upperPoly.append(poly,lowerIndex,i + 1); } } else { // connect to the closest point within the triangle // console.log("Case 2: Vertex("+i+"), closestIndex("+closestIndex+"), poly.size("+this.vertices.length+")\n"); if (lowerIndex > upperIndex) { upperIndex += this.vertices.length; } closestDist = Number.MAX_VALUE; if(upperIndex < lowerIndex) { return result; } for (var j = lowerIndex; j <= upperIndex; ++j) { if (Point.leftOn(poly.at(i - 1), poly.at(i), poly.at(j)) && Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) { d = Point.sqdist(poly.at(i), poly.at(j)); if (d < closestDist) { closestDist = d; closestIndex = j % this.vertices.length; } } } if (i < closestIndex) { lowerPoly.append(poly,i,closestIndex + 1); if (closestIndex != 0) { upperPoly.append(poly,closestIndex,v.length); } upperPoly.append(poly,0,i + 1); } else { if (i != 0) { lowerPoly.append(poly,i,v.length); } lowerPoly.append(poly,0,closestIndex + 1); upperPoly.append(poly,closestIndex,i + 1); } } // solve smallest poly first if (lowerPoly.vertices.length < upperPoly.vertices.length) { lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); } else { upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); } return result; } } result.push(this); return result; }; /** * Remove collinear points in the polygon. * @method removeCollinearPoints * @param {Number} [precision] The threshold angle to use when determining whether two edges are collinear. Use zero for finest precision. * @return {Number} The number of points removed */ Polygon.prototype.removeCollinearPoints = function (precision) { var num = 0; for(var i = this.vertices.length - 1; this.vertices.length > 3 && i >= 0; --i) { if(Point.collinear(this.at(i - 1),this.at(i),this.at(i + 1),precision)) { // Remove the middle point this.vertices.splice(i % this.vertices.length,1); i--; // Jump one point forward. Otherwise we may get a chain removal num++; } } return num; }; },{'./Line': 1,'./Point': 2,'./Scalar': 4} ],4: [ function (_dereq_,module,exports) { module.exports = Scalar; /** * Scalar functions * @class Scalar */ function Scalar () {} /** * Check if two scalars are equal * @static * @method eq * @param {Number} a * @param {Number} b * @param {Number} [precision] * @return {Boolean} */ Scalar.eq = function (a,b,precision) { precision = precision || 0; return Math.abs(a - b) < precision; }; },{} ],5: [ function (_dereq_,module,exports) { module.exports = { Polygon: _dereq_('./Polygon'), Point: _dereq_('./Point') }; },{'./Point': 2,'./Polygon': 3} ],6: [ function (_dereq_,module,exports) { module.exports = { name: 'p2', version: '0.7.1', description: 'A JavaScript 2D physics engine.', author: 'Stefan Hedman <schteppe@gmail.com> (http://steffe.se)', keywords: [ 'p2.js', 'p2', 'physics', 'engine', '2d' ], main: './src/p2.js', engines: {node: '*'}, repository: { type: 'git', url: 'https://github.com/schteppe/p2.js.git' }, bugs: {url: 'https://github.com/schteppe/p2.js/issues'}, licenses: [ {type: 'MIT'} ], devDependencies: { grunt: '^0.4.5', 'grunt-contrib-jshint': '^0.11.2', 'grunt-contrib-nodeunit': '^0.4.1', 'grunt-contrib-uglify': '~0.4.0', 'grunt-contrib-watch': '~0.5.0', 'grunt-browserify': '~2.0.1', 'grunt-contrib-concat': '^0.4.0' }, dependencies: {'poly-decomp': '0.1.1'} }; },{} ],7: [ function (_dereq_,module,exports) { var vec2 = _dereq_('../math/vec2'), Utils = _dereq_('../utils/Utils'); module.exports = AABB; /** * Axis aligned bounding box class. * @class AABB * @constructor * @param {Object} [options] * @param {Array} [options.upperBound] * @param {Array} [options.lowerBound] */ function AABB (options) { /** * The lower bound of the bounding box. * @property lowerBound * @type {Array} */ this.lowerBound = vec2.create(); if(options && options.lowerBound) { vec2.copy(this.lowerBound, options.lowerBound); } /** * The upper bound of the bounding box. * @property upperBound * @type {Array} */ this.upperBound = vec2.create(); if(options && options.upperBound) { vec2.copy(this.upperBound, options.upperBound); } } var tmp = vec2.create(); /** * Set the AABB bounds from a set of points, transformed by the given position and angle. * @method setFromPoints * @param {Array} points An array of vec2's. * @param {Array} position * @param {number} angle * @param {number} skinSize Some margin to be added to the AABB. */ AABB.prototype.setFromPoints = function (points, position, angle, skinSize) { var l = this.lowerBound, u = this.upperBound; if(typeof(angle) !== 'number') { angle = 0; } // Set to the first point if(angle !== 0) { vec2.rotate(l, points[0], angle); } else { vec2.copy(l, points[0]); } vec2.copy(u, l); // Compute cosines and sines just once var cosAngle = Math.cos(angle), sinAngle = Math.sin(angle); for(var i = 1; i < points.length; i++) { var p = points[i]; if(angle !== 0) { var x = p[0], y = p[1]; tmp[0] = cosAngle * x - sinAngle * y; tmp[1] = sinAngle * x + cosAngle * y; p = tmp; } for(var j = 0; j < 2; j++) { if(p[j] > u[j]) { u[j] = p[j]; } if(p[j] < l[j]) { l[j] = p[j]; } } } // Add offset if(position) { vec2.add(this.lowerBound, this.lowerBound, position); vec2.add(this.upperBound, this.upperBound, position); } if(skinSize) { this.lowerBound[0] -= skinSize; this.lowerBound[1] -= skinSize; this.upperBound[0] += skinSize; this.upperBound[1] += skinSize; } }; /** * Copy bounds from an AABB to this AABB * @method copy * @param {AABB} aabb */ AABB.prototype.copy = function (aabb) { vec2.copy(this.lowerBound, aabb.lowerBound); vec2.copy(this.upperBound, aabb.upperBound); }; /** * Extend this AABB so that it covers the given AABB too. * @method extend * @param {AABB} aabb */ AABB.prototype.extend = function (aabb) { // Loop over x and y var i = 2; while(i--) { // Extend lower bound var l = aabb.lowerBound[i]; if(this.lowerBound[i] > l) { this.lowerBound[i] = l; } // Upper var u = aabb.upperBound[i]; if(this.upperBound[i] < u) { this.upperBound[i] = u; } } }; /** * Returns true if the given AABB overlaps this AABB. * @method overlaps * @param {AABB} aabb * @return {Boolean} */ AABB.prototype.overlaps = function (aabb) { var l1 = this.lowerBound, u1 = this.upperBound, l2 = aabb.lowerBound, u2 = aabb.upperBound; // l2 u2 // |---------| // |--------| // l1 u1 return ((l2[0] <= u1[0] && u1[0] <= u2[0]) || (l1[0] <= u2[0] && u2[0] <= u1[0])) && ((l2[1] <= u1[1] && u1[1] <= u2[1]) || (l1[1] <= u2[1] && u2[1] <= u1[1])); }; /** * @method containsPoint * @param {Array} point * @return {boolean} */ AABB.prototype.containsPoint = function (point) { var l = this.lowerBound, u = this.upperBound; return l[0] <= point[0] && point[0] <= u[0] && l[1] <= point[1] && point[1] <= u[1]; }; /** * Check if the AABB is hit by a ray. * @method overlapsRay * @param {Ray} ray * @return {number} -1 if no hit, a number between 0 and 1 if hit. */ AABB.prototype.overlapsRay = function (ray) { var t = 0; // ray.direction is unit direction vector of ray var dirFracX = 1 / ray.direction[0]; var dirFracY = 1 / ray.direction[1]; // this.lowerBound is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner var t1 = (this.lowerBound[0] - ray.from[0]) * dirFracX; var t2 = (this.upperBound[0] - ray.from[0]) * dirFracX; var t3 = (this.lowerBound[1] - ray.from[1]) * dirFracY; var t4 = (this.upperBound[1] - ray.from[1]) * dirFracY; var tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4))); var tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4))); // if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us if (tmax < 0) { // t = tmax; return -1; } // if tmin > tmax, ray doesn't intersect AABB if (tmin > tmax) { // t = tmax; return -1; } return tmin; }; },{'../math/vec2': 30,'../utils/Utils': 57} ],8: [ function (_dereq_,module,exports) { var vec2 = _dereq_('../math/vec2'); var Body = _dereq_('../objects/Body'); module.exports = Broadphase; /** * Base class for broadphase implementations. * @class Broadphase * @constructor */ function Broadphase (type) { this.type = type; /** * The resulting overlapping pairs. Will be filled with results during .getCollisionPairs(). * @property result * @type {Array} */ this.result = []; /** * The world to search for collision pairs in. To change it, use .setWorld() * @property world * @type {World} * @readOnly */ this.world = null; /** * The bounding volume type to use in the broadphase algorithms. Should be set to Broadphase.AABB or Broadphase.BOUNDING_CIRCLE. * @property {Number} boundingVolumeType */ this.boundingVolumeType = Broadphase.AABB; } /** * Axis aligned bounding box type. * @static * @property {Number} AABB */ Broadphase.AABB = 1; /** * Bounding circle type. * @static * @property {Number} BOUNDING_CIRCLE */ Broadphase.BOUNDING_CIRCLE = 2; /** * Set the world that we are searching for collision pairs in. * @method setWorld * @param {World} world */ Broadphase.prototype.setWorld = function (world) { this.world = world; }; /** * Get all potential intersecting body pairs. * @method getCollisionPairs * @param {World} world The world to search in. * @return {Array} An array of the bodies, ordered in pairs. Example: A result of [a,b,c,d] means that the potential pairs are: (a,b), (c,d). */ Broadphase.prototype.getCollisionPairs = function (world) {}; var dist = vec2.create(); /** * Check whether the bounding radius of two bodies overlap. * @method boundingRadiusCheck * @param {Body} bodyA * @param {Body} bodyB * @return {Boolean} */ Broadphase.boundingRadiusCheck = function (bodyA, bodyB) { vec2.sub(dist, bodyA.position, bodyB.position); var d2 = vec2.squaredLength(dist), r = bodyA.boundingRadius + bodyB.boundingRadius; return d2 <= r * r; }; /** * Check whether the bounding radius of two bodies overlap. * @method boundingRadiusCheck * @param {Body} bodyA * @param {Body} bodyB * @return {Boolean} */ Broadphase.aabbCheck = function (bodyA, bodyB) { return bodyA.getAABB().overlaps(bodyB.getAABB()); }; /** * Check whether the bounding radius of two bodies overlap. * @method boundingRadiusCheck * @param {Body} bodyA * @param {Body} bodyB * @return {Boolean} */ Broadphase.prototype.boundingVolumeCheck = function (bodyA, bodyB) { var result; switch(this.boundingVolumeType) { case Broadphase.BOUNDING_CIRCLE: result = Broadphase.boundingRadiusCheck(bodyA,bodyB); break; case Broadphase.AABB: result = Broadphase.aabbCheck(bodyA,bodyB); break; default: throw new Error('Bounding volume type not recognized: ' + this.boundingVolumeType); } return result; }; /** * Check whether two bodies are allowed to collide at all. * @method canCollide * @param {Body} bodyA * @param {Body} bodyB * @return {Boolean} */ Broadphase.canCollide = function (bodyA, bodyB) { var KINEMATIC = Body.KINEMATIC; var STATIC = Body.STATIC; // Cannot collide static bodies if(bodyA.type === STATIC && bodyB.type === STATIC) { return false; } // Cannot collide static vs kinematic bodies if((bodyA.type === KINEMATIC && bodyB.type === STATIC) || (bodyA.type === STATIC && bodyB.type === KINEMATIC)) { return false; } // Cannot collide kinematic vs kinematic if(bodyA.type === KINEMATIC && bodyB.type === KINEMATIC) { return false; } // Cannot collide both sleeping bodies if(bodyA.sleepState === Body.SLEEPING && bodyB.sleepState === Body.SLEEPING) { return false; } // Cannot collide if one is static and the other is sleeping if((bodyA.sleepState === Body.SLEEPING && bodyB.type === STATIC) || (bodyB.sleepState === Body.SLEEPING && bodyA.type === STATIC)) { return false; } return true; }; Broadphase.NAIVE = 1; Broadphase.SAP = 2; },{'../math/vec2': 30,'../objects/Body': 31} ],9: [ function (_dereq_,module,exports) { var Circle = _dereq_('../shapes/Circle'), Plane = _dereq_('../shapes/Plane'), Shape = _dereq_('../shapes/Shape'), Particle = _dereq_('../shapes/Particle'), Broadphase = _dereq_('../collision/Broadphase'), vec2 = _dereq_('../math/vec2'); module.exports = NaiveBroadphase; /** * Naive broadphase implementation. Does N^2 tests. * * @class NaiveBroadphase * @constructor * @extends Broadphase */ function NaiveBroadphase () { Broadphase.call(this, Broadphase.NAIVE); } NaiveBroadphase.prototype = new Broadphase(); NaiveBroadphase.prototype.constructor = NaiveBroadphase; /** * Get the colliding pairs * @method getCollisionPairs * @param {World} world * @return {Array} */ NaiveBroadphase.prototype.getCollisionPairs = function (world) { var bodies = world.bodies, result = this.result; result.length = 0; for(var i = 0, Ncolliding = bodies.length; i !== Ncolliding; i++) { var bi = bodies[i]; for(var j = 0; j < i; j++) { var bj = bodies[j]; if(Broadphase.canCollide(bi,bj) && this.boundingVolumeCheck(bi,bj)) { result.push(bi,bj); } } } return result; }; /** * Returns all the bodies within an AABB. * @method aabbQuery * @param {World} world * @param {AABB} aabb * @param {array} result An array to store resulting bodies in. * @return {array} */ NaiveBroadphase.prototype.aabbQuery = function (world, aabb, result) { result = result || []; var bodies = world.bodies; for(var i = 0; i < bodies.length; i++) { var b = bodies[i]; if(b.aabbNeedsUpdate) { b.updateAABB(); } if(b.aabb.overlaps(aabb)) { result.push(b); } } return result; }; },{'../collision/Broadphase': 8,'../math/vec2': 30,'../shapes/Circle': 39,'../shapes/Particle': 43,'../shapes/Plane': 44,'../shapes/Shape': 45} ],10: [ function (_dereq_,module,exports) { var vec2 = _dereq_('../math/vec2'), sub = vec2.sub, add = vec2.add, dot = vec2.dot, Utils = _dereq_('../utils/Utils'), ContactEquationPool = _dereq_('../utils/ContactEquationPool'), FrictionEquationPool = _dereq_('../utils/FrictionEquationPool'), TupleDictionary = _dereq_('../utils/TupleDictionary'), Equation = _dereq_('../equations/Equation'), ContactEquation = _dereq_('../equations/ContactEquation'), FrictionEquation = _dereq_('../equations/FrictionEquation'), Circle = _dereq_('../shapes/Circle'), Convex = _dereq_('../shapes/Convex'), Shape = _dereq_('../shapes/Shape'), Body = _dereq_('../objects/Body'), Box = _dereq_('../shapes/Box'); module.exports = Narrowphase; // Temp things var yAxis = vec2.fromValues(0,1); var tmp1 = vec2.fromValues(0,0), tmp2 = vec2.fromValues(0,0), tmp3 = vec2.fromValues(0,0), tmp4 = vec2.fromValues(0,0), tmp5 = vec2.fromValues(0,0), tmp6 = vec2.fromValues(0,0), tmp7 = vec2.fromValues(0,0), tmp8 = vec2.fromValues(0,0), tmp9 = vec2.fromValues(0,0), tmp10 = vec2.fromValues(0,0), tmp11 = vec2.fromValues(0,0), tmp12 = vec2.fromValues(0,0), tmp13 = vec2.fromValues(0,0), tmp14 = vec2.fromValues(0,0), tmp15 = vec2.fromValues(0,0), tmp16 = vec2.fromValues(0,0), tmp17 = vec2.fromValues(0,0), tmp18 = vec2.fromValues(0,0), tmpArray = []; /** * Narrowphase. Creates contacts and friction given shapes and transforms. * @class Narrowphase * @constructor */ function Narrowphase () { /** * @property contactEquations * @type {Array} */ this.contactEquations = []; /** * @property frictionEquations * @type {Array} */ this.frictionEquations = []; /** * Whether to make friction equations in the upcoming contacts. * @property enableFriction * @type {Boolean} */ this.enableFriction = true; /** * Whether to make equations enabled in upcoming contacts. * @property enabledEquations * @type {Boolean} */ this.enabledEquati