diff --git a/build/p2.js b/build/p2.js deleted file mode 100644 index d3fb16a1a1..0000000000 --- a/build/p2.js +++ /dev/null @@ -1,8768 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (c) 2013 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){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.p2=e():"undefined"!=typeof global?self.p2=e():"undefined"!=typeof self&&(self.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 0) { - //TODO: evaluate use of glm_invsqrt here? - len = 1 / Math.sqrt(len); - out[0] = a[0] * len; - out[1] = a[1] * len; - } - return out; -}; - -/** - * Caclulates the dot product of two vec2's - * - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {Number} dot product of a and b - */ -vec2.dot = function (a, b) { - return a[0] * b[0] + a[1] * b[1]; -}; - -/** - * Computes the cross product of two vec2's - * Note that the cross product must by definition produce a 3D vector - * - * @param {vec3} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec3} out - */ -vec2.cross = function(out, a, b) { - var z = a[0] * b[1] - a[1] * b[0]; - out[0] = out[1] = 0; - out[2] = z; - return out; -}; - -/** - * Performs a linear interpolation between two vec2's - * - * @param {vec3} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {vec2} out - */ -vec2.lerp = function (out, a, b, t) { - var ax = a[0], - ay = a[1]; - out[0] = ax + t * (b[0] - ax); - out[1] = ay + t * (b[1] - ay); - return out; -}; - -/** - * Transforms the vec2 with a mat2 - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to transform - * @param {mat2} m matrix to transform with - * @returns {vec2} out - */ -vec2.transformMat2 = function(out, a, m) { - var x = a[0], - y = a[1]; - out[0] = x * m[0] + y * m[1]; - out[1] = x * m[2] + y * m[3]; - return out; -}; - -/** - * Perform some operation over an array of vec2s. - * - * @param {Array} a the array of vectors to iterate over - * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed - * @param {Number} offset Number of elements to skip at the beginning of the array - * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array - * @param {Function} fn Function to call for each vector in the array - * @param {Object} [arg] additional argument to pass to fn - * @returns {Array} a - * @function - */ -vec2.forEach = (function() { - var vec = new Float32Array(2); - - return function(a, stride, offset, count, fn, arg) { - var i, l; - if(!stride) { - stride = 2; - } - - if(!offset) { - offset = 0; - } - - if(count) { - l = Math.min((count * stride) + offset, a.length); - } else { - l = a.length; - } - - for(i = offset; i < l; i += stride) { - vec[0] = a[i]; vec[1] = a[i+1]; - fn(vec, vec, arg); - a[i] = vec[0]; a[i+1] = vec[1]; - } - - return a; - }; -})(); - -/** - * Returns a string representation of a vector - * - * @param {vec2} vec vector to represent as a string - * @returns {String} string representation of the vector - */ -vec2.str = function (a) { - return 'vec2(' + a[0] + ', ' + a[1] + ')'; -}; - -if(typeof(exports) !== 'undefined') { - exports.vec2 = vec2; -} - -},{}],3:[function(require,module,exports){ -var Scalar = require('./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":6}],4:[function(require,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; -}; - -},{}],5:[function(require,module,exports){ -var Line = require("./Line") -, Point = require("./Point") -, Scalar = require("./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 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 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 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":3,"./Point":4,"./Scalar":6}],6:[function(require,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; -}; - -},{}],7:[function(require,module,exports){ -module.exports = { - Polygon : require("./Polygon"), - Point : require("./Point"), -}; - -},{"./Point":4,"./Polygon":5}],8:[function(require,module,exports){ -module.exports={ - "name": "p2", - "version": "0.4.0", - "description": "A JavaScript 2D physics engine.", - "author": "Stefan Hedman (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" : { - "jshint" : "latest", - "nodeunit" : "latest", - "grunt": "~0.4.0", - "grunt-contrib-jshint": "~0.1.1", - "grunt-contrib-nodeunit": "~0.1.2", - "grunt-contrib-concat": "~0.1.3", - "grunt-contrib-uglify": "*", - "grunt-browserify" : "*", - "browserify":"*" - }, - "dependencies" : { - "underscore":"*", - "poly-decomp" : "git://github.com/schteppe/poly-decomp.js", - "gl-matrix":"2.0.0", - "jsonschema":"*" - } -} - -},{}],9:[function(require,module,exports){ -var vec2 = require('../math/vec2') - -module.exports = Broadphase; - -/** - * Base class for broadphase implementations. - * @class Broadphase - * @constructor - */ -function Broadphase(){ - - /** - * The resulting overlapping pairs. Will be filled with results during .getCollisionPairs(). - * @property result - * @type {Array} - */ - this.result = []; -}; - -/** - * 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){ - throw new Error("getCollisionPairs must be implemented in a subclass!"); -}; - -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; -}; - -},{"../math/vec2":30}],10:[function(require,module,exports){ -var Circle = require('../shapes/Circle') -, Plane = require('../shapes/Plane') -, Particle = require('../shapes/Particle') -, Broadphase = require('../collision/Broadphase') -, vec2 = require('../math/vec2') - -module.exports = GridBroadphase; - -/** - * Broadphase that uses axis-aligned bins. - * @class GridBroadphase - * @constructor - * @extends Broadphase - * @param {number} xmin Lower x bound of the grid - * @param {number} xmax Upper x bound - * @param {number} ymin Lower y bound - * @param {number} ymax Upper y bound - * @param {number} nx Number of bins along x axis - * @param {number} ny Number of bins along y axis - * @todo test - */ -function GridBroadphase(xmin,xmax,ymin,ymax,nx,ny){ - Broadphase.apply(this); - - nx = nx || 10; - ny = ny || 10; - - this.binsizeX = (xmax-xmin) / nx; - this.binsizeY = (ymax-ymin) / ny; - this.nx = nx; - this.ny = ny; - this.xmin = xmin; - this.ymin = ymin; - this.xmax = xmax; - this.ymax = ymax; -}; -GridBroadphase.prototype = new Broadphase(); - -/** - * Get a bin index given a world coordinate - * @method getBinIndex - * @param {Number} x - * @param {Number} y - * @return {Number} Integer index - */ -GridBroadphase.prototype.getBinIndex = function(x,y){ - var nx = this.nx, - ny = this.ny, - xmin = this.xmin, - ymin = this.ymin, - xmax = this.xmax, - ymax = this.ymax; - - var xi = Math.floor(nx * (x - xmin) / (xmax-xmin)); - var yi = Math.floor(ny * (y - ymin) / (ymax-ymin)); - return xi*ny + yi; -} - -/** - * Get collision pairs. - * @method getCollisionPairs - * @param {World} world - * @return {Array} - */ -GridBroadphase.prototype.getCollisionPairs = function(world){ - var result = [], - collidingBodies = world.bodies, - Ncolliding = Ncolliding=collidingBodies.length, - binsizeX = this.binsizeX, - binsizeY = this.binsizeY; - - var bins=[], Nbins=nx*ny; - for(var i=0; i= 0 && xi*(ny-1) + yi < Nbins) - bins[ xi*(ny-1) + yi ].push(bi); - } - } - } else if(si instanceof Plane){ - // Put in all bins for now - if(bi.angle == 0){ - var y = bi.position[1]; - for(var j=0; j!==Nbins && ymin+binsizeY*(j-1) id2){ - var tmp = id1; - id1 = id2; - id2 = tmp; - } - return !!this.collidingBodiesLastStep[id1 + " " + id2]; -}; - -/** - * Throws away the old equatons and gets ready to create new - * @method reset - */ -Narrowphase.prototype.reset = function(){ - - // Save the colliding bodies data - for(var key in this.collidingBodiesLastStep) - delete this.collidingBodiesLastStep[key]; - for(var i=0; i!==this.contactEquations.length; i++){ - var eq = this.contactEquations[i], - id1 = eq.bi.id, - id2 = eq.bj.id; - if(id1 > id2){ - var tmp = id1; - id1 = id2; - id2 = tmp; - } - this.collidingBodiesLastStep[id1 + " " + id2] = true; - } - - if(this.reuseObjects){ - var ce = this.contactEquations, - fe = this.frictionEquations, - rfe = this.reusableFrictionEquations, - rce = this.reusableContactEquations; - Utils.appendArray(rce,ce); - Utils.appendArray(rfe,fe); - } - - // Reset - this.contactEquations.length = this.frictionEquations.length = 0; -}; - -/** - * Creates a ContactEquation, either by reusing an existing object or creating a new one. - * @method createContactEquation - * @param {Body} bodyA - * @param {Body} bodyB - * @return {ContactEquation} - */ -Narrowphase.prototype.createContactEquation = function(bodyA,bodyB,shapeA,shapeB){ - var c = this.reusableContactEquations.length ? this.reusableContactEquations.pop() : new ContactEquation(bodyA,bodyB); - c.bi = bodyA; - c.bj = bodyB; - c.shapeA = shapeA; - c.shapeB = shapeB; - c.restitution = this.restitution; - c.firstImpact = !this.collidedLastStep(bodyA,bodyB); - return c; -}; - -/** - * Creates a FrictionEquation, either by reusing an existing object or creating a new one. - * @method createFrictionEquation - * @param {Body} bodyA - * @param {Body} bodyB - * @return {FrictionEquation} - */ -Narrowphase.prototype.createFrictionEquation = function(bodyA,bodyB,shapeA,shapeB){ - var c = this.reusableFrictionEquations.length ? this.reusableFrictionEquations.pop() : new FrictionEquation(bodyA,bodyB); - c.bi = bodyA; - c.bj = bodyB; - c.shapeA = shapeA; - c.shapeB = shapeB; - c.setSlipForce(this.slipForce); - c.frictionCoefficient = this.frictionCoefficient; - return c; -}; - -/** - * Creates a FrictionEquation given the data in the ContactEquation. Uses same offset vectors ri and rj, but the tangent vector will be constructed from the collision normal. - * @method createFrictionFromContact - * @param {ContactEquation} contactEquation - * @return {FrictionEquation} - */ -Narrowphase.prototype.createFrictionFromContact = function(c){ - var eq = this.createFrictionEquation(c.bi,c.bj); - vec2.copy(eq.ri, c.ri); - vec2.copy(eq.rj, c.rj); - vec2.rotate(eq.t, c.ni, -Math.PI / 2); - eq.contactEquation = c; - return eq; -} - -/** - * Plane/line Narrowphase - * @method planeLine - * @param {Body} bi - * @param {Plane} si - * @param {Array} xi - * @param {Number} ai - * @param {Body} bj - * @param {Line} sj - * @param {Array} xj - * @param {Number} aj - */ -Narrowphase.prototype[Shape.PLANE | Shape.LINE] = -Narrowphase.prototype.planeLine = function(bi,si,xi,ai, bj,sj,xj,aj){ - var lineShape = sj, - lineAngle = aj, - lineBody = bj, - lineOffset = xj, - planeOffset = xi, - planeAngle = ai, - planeBody = bi, - planeShape = si; - - var worldVertex0 = tmp1, - worldVertex1 = tmp2, - worldVertex01 = tmp3, - worldVertex11 = tmp4, - worldEdge = tmp5, - worldEdgeUnit = tmp6, - dist = tmp7, - worldNormal = tmp8, - worldTangent = tmp9; - - // Get start and end points - vec2.set(worldVertex0, -lineShape.length/2, 0); - vec2.set(worldVertex1, lineShape.length/2, 0); - - // Not sure why we have to use worldVertex*1 here, but it won't work otherwise. Tired. - vec2.rotate(worldVertex01, worldVertex0, lineAngle); - vec2.rotate(worldVertex11, worldVertex1, lineAngle); - - add(worldVertex01, worldVertex01, lineOffset); - add(worldVertex11, worldVertex11, lineOffset); - - vec2.copy(worldVertex0,worldVertex01); - vec2.copy(worldVertex1,worldVertex11); - - // Get vector along the line - sub(worldEdge, worldVertex1, worldVertex0); - vec2.normalize(worldEdgeUnit, worldEdge); - - // Get tangent to the edge. - vec2.rotate(worldTangent, worldEdgeUnit, -Math.PI/2); - - vec2.rotate(worldNormal, yAxis, planeAngle); - - // Check line ends - var verts = [worldVertex0, worldVertex1]; - for(var i=0; i pos0 && pos < pos1){ - // We got contact! - - if(justTest) return true; - - var c = this.createContactEquation(circleBody,lineBody,si,sj); - - vec2.scale(c.ni, orthoDist, -1); - vec2.normalize(c.ni, c.ni); - - vec2.scale( c.ri, c.ni, circleRadius); - add(c.ri, c.ri, circleOffset); - sub(c.ri, c.ri, circleBody.position); - - sub(c.rj, projectedPoint, lineOffset); - add(c.rj, c.rj, lineOffset); - sub(c.rj, c.rj, lineBody.position); - - this.contactEquations.push(c); - - if(this.enableFriction){ - this.frictionEquations.push(this.createFrictionFromContact(c)); - } - - return true; - } - } - - // Add corner - var verts = [worldVertex0, worldVertex1]; - - for(var i=0; i 0){ - - // Now project the circle onto the edge - vec2.scale(orthoDist, worldTangent, d); - sub(projectedPoint, circleOffset, orthoDist); - - - // Check if the point is within the edge span - var pos = dot(worldEdgeUnit, projectedPoint); - var pos0 = dot(worldEdgeUnit, worldVertex0); - var pos1 = dot(worldEdgeUnit, worldVertex1); - - if(pos > pos0 && pos < pos1){ - // We got contact! - - if(justTest) return true; - - if(closestEdgeDistance === null || d*d 0){ - for(var i=0; i= 0){ - - // Now project the particle onto the edge - vec2.scale(orthoDist, worldTangent, d); - sub(projectedPoint, particleOffset, orthoDist); - - // Check if the point is within the edge span - var pos = dot(worldEdgeUnit, projectedPoint); - var pos0 = dot(worldEdgeUnit, worldVertex0); - var pos1 = dot(worldEdgeUnit, worldVertex1); - - if(pos > pos0 && pos < pos1){ - // We got contact! - if(justTest) return true; - - if(closestEdgeDistance === null || d*d r*r){ - return false; - } - - if(justTest) return true; - - var c = this.createContactEquation(bodyA,bodyB,si,sj); - sub(c.ni, offsetB, offsetA); - vec2.normalize(c.ni,c.ni); - - vec2.scale( c.ri, c.ni, shapeA.radius); - vec2.scale( c.rj, c.ni, -shapeB.radius); - - add(c.ri, c.ri, offsetA); - sub(c.ri, c.ri, bodyA.position); - - add(c.rj, c.rj, offsetB); - sub(c.rj, c.rj, bodyB.position); - - this.contactEquations.push(c); - - if(this.enableFriction){ - this.frictionEquations.push(this.createFrictionFromContact(c)); - } - return true; -}; - -/** - * Plane/Convex Narrowphase - * @method planeConvex - * @param {Body} bi - * @param {Plane} si - * @param {Array} xi - * @param {Number} ai - * @param {Body} bj - * @param {Convex} sj - * @param {Array} xj - * @param {Number} aj - */ -Narrowphase.prototype[Shape.PLANE | Shape.CONVEX] = -Narrowphase.prototype.planeConvex = function( bi,si,xi,ai, bj,sj,xj,aj ){ - var convexBody = bj, - convexOffset = xj, - convexShape = sj, - convexAngle = aj, - planeBody = bi, - planeShape = si, - planeOffset = xi, - planeAngle = ai; - - var worldVertex = tmp1, - worldNormal = tmp2, - dist = tmp3; - - var numReported = 0; - vec2.rotate(worldNormal, yAxis, planeAngle); - - for(var i=0; i= 2) - break; - } - } - return numReported > 0; -}; - -/** - * @method convexPlane - * @deprecated Use .planeConvex() instead! - */ -Narrowphase.prototype.convexPlane = function( bi,si,xi,ai, bj,sj,xj,aj ){ - console.warn("Narrowphase.prototype.convexPlane is deprecated. Use planeConvex instead!"); - return this.planeConvex( bj,sj,xj,aj, bi,si,xi,ai ); -} - -/** - * Narrowphase for particle vs plane - * @method particlePlane - * @param {Body} bi The particle body - * @param {Particle} si Particle shape - * @param {Array} xi World position for the particle - * @param {Number} ai World angle for the particle - * @param {Body} bj Plane body - * @param {Plane} sj Plane shape - * @param {Array} xj World position for the plane - * @param {Number} aj World angle for the plane - */ -Narrowphase.prototype[Shape.PARTICLE | Shape.PLANE] = -Narrowphase.prototype.particlePlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){ - var particleBody = bi, - particleShape = si, - particleOffset = xi, - planeBody = bj, - planeShape = sj, - planeOffset = xj, - planeAngle = aj; - - var dist = tmp1, - worldNormal = tmp2; - - planeAngle = planeAngle || 0; - - sub(dist, particleOffset, planeOffset); - vec2.rotate(worldNormal, yAxis, planeAngle); - - var d = dot(dist, worldNormal); - - if(d > 0) return false; - if(justTest) return true; - - var c = this.createContactEquation(planeBody,particleBody,sj,si); - - vec2.copy(c.ni, worldNormal); - vec2.scale( dist, c.ni, d ); - // dist is now the distance vector in the normal direction - - // ri is the particle position projected down onto the plane, from the plane center - sub( c.ri, particleOffset, dist); - sub( c.ri, c.ri, planeBody.position); - - // rj is from the body center to the particle center - sub( c.rj, particleOffset, particleBody.position ); - - this.contactEquations.push(c); - - if(this.enableFriction){ - this.frictionEquations.push(this.createFrictionFromContact(c)); - } - return true; -}; - -/** - * Circle/Particle Narrowphase - * @method circleParticle - * @param {Body} bi - * @param {Circle} si - * @param {Array} xi - * @param {Number} ai - * @param {Body} bj - * @param {Particle} sj - * @param {Array} xj - * @param {Number} aj - */ -Narrowphase.prototype[Shape.CIRCLE | Shape.PARTICLE] = -Narrowphase.prototype.circleParticle = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){ - var circleBody = bi, - circleShape = si, - circleOffset = xi, - particleBody = bj, - particleShape = sj, - particleOffset = xj, - dist = tmp1; - - sub(dist, particleOffset, circleOffset); - if(vec2.squaredLength(dist) > circleShape.radius*circleShape.radius) return false; - if(justTest) return true; - - var c = this.createContactEquation(circleBody,particleBody,si,sj); - vec2.copy(c.ni, dist); - vec2.normalize(c.ni,c.ni); - - // Vector from circle to contact point is the normal times the circle radius - vec2.scale(c.ri, c.ni, circleShape.radius); - add(c.ri, c.ri, circleOffset); - sub(c.ri, c.ri, circleBody.position); - - // Vector from particle center to contact point is zero - sub(c.rj, particleOffset, particleBody.position); - - this.contactEquations.push(c); - - if(this.enableFriction){ - this.frictionEquations.push(this.createFrictionFromContact(c)); - } - - return true; -}; - -var capsulePlane_tmpCircle = new Circle(1), - capsulePlane_tmp1 = vec2.create(), - capsulePlane_tmp2 = vec2.create(), - capsulePlane_tmp3 = vec2.create(); - -Narrowphase.prototype[Shape.PLANE | Shape.CAPSULE] = -Narrowphase.prototype.planeCapsule = function( bi,si,xi,ai, bj,sj,xj,aj ){ - var end1 = capsulePlane_tmp1, - end2 = capsulePlane_tmp2, - circle = capsulePlane_tmpCircle, - dst = capsulePlane_tmp3; - - // Compute world end positions - vec2.set(end1, -sj.length/2, 0); - vec2.rotate(end1,end1,aj); - add(end1,end1,xj); - - vec2.set(end2, sj.length/2, 0); - vec2.rotate(end2,end2,aj); - add(end2,end2,xj); - - circle.radius = sj.radius; - - // Do Narrowphase as two circles - this.circlePlane(bj,circle,end1,0, bi,si,xi,ai); - this.circlePlane(bj,circle,end2,0, bi,si,xi,ai); -}; - -/** - * @method capsulePlane - * @deprecated Use .planeCapsule() instead! - */ -Narrowphase.prototype.capsulePlane = function( bi,si,xi,ai, bj,sj,xj,aj ){ - console.warn("Narrowphase.prototype.capsulePlane() is deprecated. Use .planeCapsule() instead!"); - return this.planeCapsule( bj,sj,xj,aj, bi,si,xi,ai ); -} - -/** - * Creates ContactEquations and FrictionEquations for a collision. - * @method circlePlane - * @param {Body} bi The first body that should be connected to the equations. - * @param {Circle} si The circle shape participating in the collision. - * @param {Array} xi Extra offset to take into account for the Shape, in addition to the one in circleBody.position. Will *not* be rotated by circleBody.angle (maybe it should, for sake of homogenity?). Set to null if none. - * @param {Body} bj The second body that should be connected to the equations. - * @param {Plane} sj The Plane shape that is participating - * @param {Array} xj Extra offset for the plane shape. - * @param {Number} aj Extra angle to apply to the plane - */ -Narrowphase.prototype[Shape.CIRCLE | Shape.PLANE] = -Narrowphase.prototype.circlePlane = function( bi,si,xi,ai, bj,sj,xj,aj ){ - var circleBody = bi, - circleShape = si, - circleOffset = xi, // Offset from body center, rotated! - planeBody = bj, - shapeB = sj, - planeOffset = xj, - planeAngle = aj; - - planeAngle = planeAngle || 0; - - // Vector from plane to circle - var planeToCircle = tmp1, - worldNormal = tmp2, - temp = tmp3; - - sub(planeToCircle, circleOffset, planeOffset); - - // World plane normal - vec2.rotate(worldNormal, yAxis, planeAngle); - - // Normal direction distance - var d = dot(worldNormal, planeToCircle); - - if(d > circleShape.radius) return false; // No overlap. Abort. - - // Create contact - var contact = this.createContactEquation(planeBody,circleBody,sj,si); - - // ni is the plane world normal - vec2.copy(contact.ni, worldNormal); - - // rj is the vector from circle center to the contact point - vec2.scale(contact.rj, contact.ni, -circleShape.radius); - add(contact.rj, contact.rj, circleOffset); - sub(contact.rj, contact.rj, circleBody.position); - - // ri is the distance from plane center to contact. - vec2.scale(temp, contact.ni, d); - sub(contact.ri, planeToCircle, temp ); // Subtract normal distance vector from the distance vector - add(contact.ri, contact.ri, planeOffset); - sub(contact.ri, contact.ri, planeBody.position); - - this.contactEquations.push(contact); - - if(this.enableFriction){ - this.frictionEquations.push( this.createFrictionFromContact(contact) ); - } - - return true; -}; - - -/** - * Convex/convex Narrowphase.See this article for more info. - * @method convexConvex - * @param {Body} bi - * @param {Convex} si - * @param {Array} xi - * @param {Number} ai - * @param {Body} bj - * @param {Convex} sj - * @param {Array} xj - * @param {Number} aj - */ -Narrowphase.prototype[Shape.CONVEX] = -Narrowphase.prototype.convexConvex = function( bi,si,xi,ai, bj,sj,xj,aj ){ - var sepAxis = tmp1, - worldPoint = tmp2, - worldPoint0 = tmp3, - worldPoint1 = tmp4, - worldEdge = tmp5, - projected = tmp6, - penetrationVec = tmp7, - dist = tmp8, - worldNormal = tmp9; - - var found = Narrowphase.findSeparatingAxis(si,xi,ai,sj,xj,aj,sepAxis); - if(!found) return false; - - // Make sure the separating axis is directed from shape i to shape j - sub(dist,xj,xi); - if(dot(sepAxis,dist) > 0){ - vec2.scale(sepAxis,sepAxis,-1); - } - - // Find edges with normals closest to the separating axis - var closestEdge1 = Narrowphase.getClosestEdge(si,ai,sepAxis,true), // Flipped axis - closestEdge2 = Narrowphase.getClosestEdge(sj,aj,sepAxis); - - if(closestEdge1==-1 || closestEdge2==-1) return false; - - // Loop over the shapes - for(var k=0; k<2; k++){ - - var closestEdgeA = closestEdge1, - closestEdgeB = closestEdge2, - shapeA = si, shapeB = sj, - offsetA = xi, offsetB = xj, - angleA = ai, angleB = aj, - bodyA = bi, bodyB = bj; - - if(k==0){ - // Swap! - var tmp; - tmp = closestEdgeA; closestEdgeA = closestEdgeB; closestEdgeB = tmp; - tmp = shapeA; shapeA = shapeB; shapeB = tmp; - tmp = offsetA; offsetA = offsetB; offsetB = tmp; - tmp = angleA; angleA = angleB; angleB = tmp; - tmp = bodyA; bodyA = bodyB; bodyB = tmp; - } - - // Loop over 2 points in convex B - for(var j=closestEdgeB; j max) max = value; - if(min === null || value < min) min = value; - } - - if(min > max){ - var t = min; - min = max; - max = t; - } - - // Project the position of the body onto the axis - need to add this to the result - var offset = dot(convexOffset, worldAxis); - - vec2.set( result, min + offset, max + offset); -}; - -// .findSeparatingAxis is called by other functions, need local tmp vectors -var fsa_tmp1 = vec2.fromValues(0,0) -, fsa_tmp2 = vec2.fromValues(0,0) -, fsa_tmp3 = vec2.fromValues(0,0) -, fsa_tmp4 = vec2.fromValues(0,0) -, fsa_tmp5 = vec2.fromValues(0,0) -, fsa_tmp6 = vec2.fromValues(0,0) - -/** - * Find a separating axis between the shapes, that maximizes the separating distance between them. - * @method findSeparatingAxis - * @static - * @param {Convex} c1 - * @param {Array} offset1 - * @param {Number} angle1 - * @param {Convex} c2 - * @param {Array} offset2 - * @param {Number} angle2 - * @param {Array} sepAxis The resulting axis - * @return {Boolean} Whether the axis could be found. - */ -Narrowphase.findSeparatingAxis = function(c1,offset1,angle1,c2,offset2,angle2,sepAxis){ - var maxDist = null, - overlap = false, - found = false, - edge = fsa_tmp1, - worldPoint0 = fsa_tmp2, - worldPoint1 = fsa_tmp3, - normal = fsa_tmp4, - span1 = fsa_tmp5, - span2 = fsa_tmp6; - - for(var j=0; j!==2; j++){ - var c = c1, - angle = angle1; - if(j===1){ - c = c2; - angle = angle2; - } - - for(var i=0; i!==c.vertices.length; i++){ - // Get the world edge - vec2.rotate(worldPoint0, c.vertices[i], angle); - vec2.rotate(worldPoint1, c.vertices[(i+1)%c.vertices.length], angle); - - sub(edge, worldPoint1, worldPoint0); - - // Get normal - just rotate 90 degrees since vertices are given in CCW - vec2.rotate(normal, edge, -Math.PI / 2); - vec2.normalize(normal,normal); - - // Project hulls onto that normal - Narrowphase.projectConvexOntoAxis(c1,offset1,angle1,normal,span1); - Narrowphase.projectConvexOntoAxis(c2,offset2,angle2,normal,span2); - - // Order by span position - var a=span1, - b=span2, - swapped = false; - if(span1[0] > span2[0]){ - b=span1; - a=span2; - swapped = true; - } - - // Get separating distance - var dist = b[0] - a[1]; - overlap = dist < 0; - - if(maxDist===null || dist > maxDist){ - vec2.copy(sepAxis, normal); - maxDist = dist; - found = overlap; - } - } - } - - return found; -}; - -// .getClosestEdge is called by other functions, need local tmp vectors -var gce_tmp1 = vec2.fromValues(0,0) -, gce_tmp2 = vec2.fromValues(0,0) -, gce_tmp3 = vec2.fromValues(0,0) - -/** - * Get the edge that has a normal closest to an axis. - * @method getClosestEdge - * @static - * @param {Convex} c - * @param {Number} angle - * @param {Array} axis - * @param {Boolean} flip - * @return {Number} Index of the edge that is closest. This index and the next spans the resulting edge. Returns -1 if failed. - */ -Narrowphase.getClosestEdge = function(c,angle,axis,flip){ - var localAxis = gce_tmp1, - edge = gce_tmp2, - normal = gce_tmp3; - - // Convert the axis to local coords of the body - vec2.rotate(localAxis, axis, -angle); - if(flip){ - vec2.scale(localAxis,localAxis,-1); - } - - var closestEdge = -1, - N = c.vertices.length, - halfPi = Math.PI / 2; - for(var i=0; i!==N; i++){ - // Get the edge - sub(edge, c.vertices[(i+1)%N], c.vertices[i%N]); - - // Get normal - just rotate 90 degrees since vertices are given in CCW - vec2.rotate(normal, edge, -halfPi); - vec2.normalize(normal,normal); - - var d = dot(normal,localAxis); - if(closestEdge == -1 || d > maxDot){ - closestEdge = i % N; - maxDot = d; - } - } - - return closestEdge; -}; - - -},{"../constraints/ContactEquation":16,"../constraints/FrictionEquation":19,"../math/vec2":30,"../shapes/Circle":35,"../shapes/Shape":41,"../utils/Utils":46}],13:[function(require,module,exports){ -var Plane = require("../shapes/Plane"); -var Broadphase = require("../collision/Broadphase"); - -module.exports = { - QuadTree : QuadTree, - Node : Node, - BoundsNode : BoundsNode, -}; - -/** - * QuadTree data structure. See https://github.com/mikechambers/ExamplesByMesh/tree/master/JavaScript/QuadTree - * @class QuadTree - * @constructor - * @param {Object} An object representing the bounds of the top level of the QuadTree. The object - * should contain the following properties : x, y, width, height - * @param {Boolean} pointQuad Whether the QuadTree will contain points (true), or items with bounds - * (width / height)(false). Default value is false. - * @param {Number} maxDepth The maximum number of levels that the quadtree will create. Default is 4. - * @param {Number} maxChildren The maximum number of children that a node can contain before it is split into sub-nodes. - */ -function QuadTree(bounds, pointQuad, maxDepth, maxChildren){ - var node; - if(pointQuad){ - node = new Node(bounds, 0, maxDepth, maxChildren); - } else { - node = new BoundsNode(bounds, 0, maxDepth, maxChildren); - } - - /** - * The root node of the QuadTree which covers the entire area being segmented. - * @property root - * @type Node - */ - this.root = node; -} - -/** - * Inserts an item into the QuadTree. - * @method insert - * @param {Object|Array} item The item or Array of items to be inserted into the QuadTree. The item should expose x, y - * properties that represents its position in 2D space. - */ -QuadTree.prototype.insert = function(item){ - if(item instanceof Array){ - var len = item.length; - for(var i = 0; i < len; i++){ - this.root.insert(item[i]); - } - } else { - this.root.insert(item); - } -} - -/** - * Clears all nodes and children from the QuadTree - * @method clear - */ -QuadTree.prototype.clear = function(){ - this.root.clear(); -} - -/** - * Retrieves all items / points in the same node as the specified item / point. If the specified item - * overlaps the bounds of a node, then all children in both nodes will be returned. - * @method retrieve - * @param {Object} item An object representing a 2D coordinate point (with x, y properties), or a shape - * with dimensions (x, y, width, height) properties. - */ -QuadTree.prototype.retrieve = function(item){ - //get a copy of the array of items - var out = this.root.retrieve(item).slice(0); - return out; -} - -QuadTree.prototype.getCollisionPairs = function(world){ - - var result = []; - - // Add all bodies - this.insert(world.bodies); - - /* - console.log("bodies",world.bodies.length); - console.log("maxDepth",this.root.maxDepth,"maxChildren",this.root.maxChildren); - */ - - for(var i=0; i!==world.bodies.length; i++){ - var b = world.bodies[i], - items = this.retrieve(b); - - //console.log("items",items.length); - - // Check results - for(var j=0, len=items.length; j!==len; j++){ - var item = items[j]; - - if(b === item) continue; // Do not add self - - // Check if they were already added - var found = false; - for(var k=0, numAdded=result.length; k= this.maxDepth) && len > this.maxChildren) { - this.subdivide(); - - for(var i = 0; i < len; i++){ - this.insert(this.children[i]); - } - - this.children.length = 0; - } -} - -Node.prototype.retrieve = function(item){ - if(this.nodes.length){ - var index = this.findIndex(item); - return this.nodes[index].retrieve(item); - } - - return this.children; -} - -Node.prototype.findIndex = function(item){ - var b = this.bounds; - var left = (item.position[0]-item.boundingRadius > b.x + b.width / 2) ? false : true; - var top = (item.position[1]-item.boundingRadius > b.y + b.height / 2) ? false : true; - - if(item instanceof Plane){ - left = top = false; // Will overlap the left/top boundary since it is infinite - } - - //top left - var index = Node.TOP_LEFT; - if(left){ - if(!top){ - index = Node.BOTTOM_LEFT; - } - } else { - if(top){ - index = Node.TOP_RIGHT; - } else { - index = Node.BOTTOM_RIGHT; - } - } - - return index; -} - - -Node.prototype.subdivide = function(){ - var depth = this.depth + 1; - - var bx = this.bounds.x; - var by = this.bounds.y; - - //floor the values - var b_w_h = (this.bounds.width / 2); - var b_h_h = (this.bounds.height / 2); - var bx_b_w_h = bx + b_w_h; - var by_b_h_h = by + b_h_h; - - //top left - this.nodes[Node.TOP_LEFT] = new this.classConstructor({ - x:bx, - y:by, - width:b_w_h, - height:b_h_h - }, - depth); - - //top right - this.nodes[Node.TOP_RIGHT] = new this.classConstructor({ - x:bx_b_w_h, - y:by, - width:b_w_h, - height:b_h_h - }, - depth); - - //bottom left - this.nodes[Node.BOTTOM_LEFT] = new this.classConstructor({ - x:bx, - y:by_b_h_h, - width:b_w_h, - height:b_h_h - }, - depth); - - - //bottom right - this.nodes[Node.BOTTOM_RIGHT] = new this.classConstructor({ - x:bx_b_w_h, - y:by_b_h_h, - width:b_w_h, - height:b_h_h - }, - depth); -} - -Node.prototype.clear = function(){ - this.children.length = 0; - - var len = this.nodes.length; - for(var i = 0; i < len; i++){ - this.nodes[i].clear(); - } - - this.nodes.length = 0; -} - - -// BoundsQuadTree - -function BoundsNode(bounds, depth, maxChildren, maxDepth){ - Node.call(this, bounds, depth, maxChildren, maxDepth); - this.stuckChildren = []; -} - -BoundsNode.prototype = new Node(); -BoundsNode.prototype.classConstructor = BoundsNode; -BoundsNode.prototype.stuckChildren = null; - -//we use this to collect and conctenate items being retrieved. This way -//we dont have to continuously create new Array instances. -//Note, when returned from QuadTree.retrieve, we then copy the array -BoundsNode.prototype.out = []; - -BoundsNode.prototype.insert = function(item){ - if(this.nodes.length){ - var index = this.findIndex(item); - var node = this.nodes[index]; - - /* - console.log("radius:",item.boundingRadius); - console.log("item x:",item.position[0] - item.boundingRadius,"x range:",node.bounds.x,node.bounds.x+node.bounds.width); - console.log("item y:",item.position[1] - item.boundingRadius,"y range:",node.bounds.y,node.bounds.y+node.bounds.height); - */ - - //todo: make _bounds bounds - if( !(item instanceof Plane) && // Plane is infinite.. Make it a "stuck" child - item.position[0] - item.boundingRadius >= node.bounds.x && - item.position[0] + item.boundingRadius <= node.bounds.x + node.bounds.width && - item.position[1] - item.boundingRadius >= node.bounds.y && - item.position[1] + item.boundingRadius <= node.bounds.y + node.bounds.height){ - this.nodes[index].insert(item); - } else { - this.stuckChildren.push(item); - } - - return; - } - - this.children.push(item); - - var len = this.children.length; - - if(this.depth < this.maxDepth && len > this.maxChildren){ - this.subdivide(); - - for(var i=0; i