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
JavaScript
/**
* 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