phaser-ce
Version:
Phaser CE (Community Edition) is a fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers.
1,664 lines (1,468 loc) • 3.32 MB
JavaScript
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2016 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*
* @overview
*
* Phaser - http://phaser.io
*
* v2.9.2 "2017-11-09" - Built: Thu Nov 09 2017 18:05:28
*
* By Richard Davey http://www.photonstorm.com @photonstorm
*
* Phaser is a fun, free and fast 2D game framework for making HTML5 games
* for desktop and mobile web browsers, supporting Canvas and WebGL rendering.
*
* Phaser uses Pixi.js for rendering, created by Mat Groves http://matgroves.com @Doormat23
* Phaser uses p2.js for full-body physics, created by Stefan Hedman https://github.com/schteppe/p2.js @schteppe
* Phaser contains a port of N+ Physics, converted by Richard Davey, original by http://www.metanetsoftware.com
*
* Many thanks to Adam Saltsman (@ADAMATOMIC) for releasing Flixel, from which both Phaser and my love of framework development originate.
*
* Follow development at http://phaser.io and on our forum
*
* "If you want your children to be intelligent, read them fairy tales."
* "If you want them to be more intelligent, read them more fairy tales."
* -- Albert Einstein
*/
/**
* 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("object"==typeof exports)module.exports=e();else if("function"==typeof define&&false)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(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.enabledEquations = true;
/**
* The friction slip force to use when creating friction equations.
* @property slipForce
* @type {Number}
*/
this.slipForce = 10.0;
/**
* The friction value to use in the upcoming friction equations.
* @property frictionCoefficient
* @type {Number}
*/
this.frictionCoefficient = 0.3;
/**
* Will be the .relativeVelocity in each produced FrictionEquation.
* @property {Number} surfaceVelocity
*/
this.surfaceVelocity = 0;
/**
* Keeps track of the allocated ContactEquations.
* @property {ContactEquationPool} contactEquationPool
*
* @example
*
* // Allocate a few equations before starting the simulation.
* // This way, no contact objects need to be created on the fly in the game loop.
* world.narrowphase.contactEquationPool.resize(1024);
* world.narrowphase.frictionEquationPool.resize(1024);
*/
this.contactEquationPool = new ContactEquationPool({ size: 32 });
/**
* Keeps track of the allocated ContactEquations.
* @property {FrictionEquationPool} frictionEquationPool
*/
this.frictionEquationPool = new FrictionEquationPool({ size: 64 });
/**
* The restitution value to use in the next contact equations.
* @property restitution
* @type {Number}
*/
this.restitution = 0;
/**
* The stiffness value to use in the next contact equations.
* @property {Number} stiffness
*/
this.stiffness = Equation.DEFAULT_STIFFNESS;
/**
* The stiffness value to use in the next contact equations.
* @property {Number} stiffness
*/
this.relaxation = Equation.DEFAULT_RELAXATION;
/**
* The stiffness value to use in the next friction equations.
* @property frictionStiffness
* @type {Number}
*/
this.frictionStiffness = Equation.DEFAULT_STIFFNESS;
/**
* The relaxation value to use in the next friction equations.
* @property frictionRelaxation
* @type {Number}
*/
this.frictionRelaxation = Equation.DEFAULT_RELAXATION;
/**
* Enable reduction of friction equations. If disabled, a box on a plane will generate 2 contact equations and 2 friction equations. If enabled, there will be only one friction equation. Same kind of simplifications are made for all collision types.
* @property enableFrictionReduction
* @type {Boolean}
* @deprecated This flag will be removed when the feature is stable enough.
* @default true
*/
this.enableFrictionReduction = true;
/**
* Keeps track of the colliding bodies last step.
* @private
* @property collidingBodiesLastStep
* @type {TupleDictionary}
*/
this.collidingBodiesLastStep = new TupleDictionary();
/**
* Contact skin size value to use in the next contact equations.
* @property {Number} contactSkinSize
* @default 0.01
*/
this.contactSkinSize = 0.01;
}
var bodiesOverlap_shapePositionA = vec2.create();
var bodiesOverlap_shapePositionB = vec2.create();
/**
* @method bodiesOverlap
* @param {Body} bodyA
* @param {Body} bodyB
* @return {Boolean}
* @todo shape world transforms are wrong
*/
Narrowphase.prototype.bodiesOverlap = function(bodyA, bodyB){
var shapePositionA = bodiesOverlap_shapePositionA;
var shapePositionB = bodiesOverlap_shapePositionB;
// Loop over all shapes of bodyA
for(var k=0, Nshapesi=bodyA.shapes.length; k!==Nshapesi; k++){
var shapeA = bodyA.shapes[k];
bodyA.toWorldFrame(shapePositionA, shapeA.position);
// All shapes of body j
for(var l=0, Nshapesj=bodyB.shapes.length; l!==Nshapesj; l++){
var shapeB = bodyB.shapes[l];
bodyB.toWorldFrame(shapePositionB, shapeB.position);
if(this[shapeA.type | shapeB.type](
bodyA,
shapeA,
shapePositionA,
shapeA.angle + bodyA.angle,
bodyB,
shapeB,
shapePositionB,
shapeB.angle + bodyB.angle,
true
)){
return true;
}
}
}
return false;
};
/**
* Check if the bodies were in contact since the last reset().
* @method collidedLastStep
* @param {Body} bodyA
* @param {Body} bodyB
* @return {Boolean}
*/
Narrowphase.prototype.collidedLastStep = function(bodyA, bodyB){
var id1 = bodyA.id|0,
id2 = bodyB.id|0;
return !!this.collidingBodiesLastStep.get(id1, id2);
};
/**
* Throws away the old equations and gets ready to create new
* @method reset
*/
Narrowphase.prototype.reset = function(){
this.collidingBodiesLastStep.reset();
var eqs = this.contactEquations;
var l = eqs.length;
while(l--){
var eq = eqs[l],
id1 = eq.bodyA.id,
id2 = eq.bodyB.id;
this.collidingBodiesLastStep.set(id1, id2, true);
}
var ce = this.contactEquations,
fe = this.frictionEquations;
for(var i=0; i<ce.length; i++){
this.contactEquationPool.release(ce[i]);
}
for(var i=0; i<fe.length; i++){
this.frictionEquationPool.release(fe[i]);
}
// 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.contactEquationPool.get();
c.bodyA = bodyA;
c.bodyB = bodyB;
c.shapeA = shapeA;
c.shapeB = shapeB;
c.restitution = this.restitution;
c.firstImpact = !this.collidedLastStep(bodyA,bodyB);
c.stiffness = this.stiffness;
c.relaxation = this.relaxation;
c.needsUpdate = true;
c.enabled = this.enabledEquations;
c.offset = this.contactSkinSize;
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.frictionEquationPool.get();
c.bodyA = bodyA;
c.bodyB = bodyB;
c.shapeA = shapeA;
c.shapeB = shapeB;
c.setSlipForce(this.slipForce);
c.frictionCoefficient = this.frictionCoefficient;
c.relativeVelocity = this.surfaceVelocity;
c.enabled = this.enabledEquations;
c.needsUpdate = true;
c.stiffness = this.frictionStiffness;
c.relaxation = this.frictionRelaxation;
c.contactEquations.length = 0;
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.bodyA, c.bodyB, c.shapeA, c.shapeB);
vec2.copy(eq.contactPointA, c.contactPointA);
vec2.copy(eq.contactPointB, c.contactPointB);
vec2.rotate90cw(eq.t, c.normalA);
eq.contactEquations.push(c);
return eq;
};
// Take the average N latest contact point on the plane.
Narrowphase.prototype.createFrictionFromAverage = function(numContacts){
var c = this.contactEquations[this.contactEquations.length - 1];
var eq = this.createFrictionEquation(c.bodyA, c.bodyB, c.shapeA, c.shapeB);
var bodyA = c.bodyA;
var bodyB = c.bodyB;
vec2.set(eq.contactPointA, 0, 0);
vec2.set(eq.contactPointB, 0, 0);
vec2.set(eq.t, 0, 0);
for(var i=0; i!==numContacts; i++){
c = this.contactEquations[this.contactEquations.length - 1 - i];
if(c.bodyA === bodyA){
vec2.add(eq.t, eq.t, c.normalA);
vec2.add(eq.contactPointA, eq.contactPointA, c.contactPointA);
vec2.add(eq.contactPointB, eq.contactPointB, c.contactPointB);
} else {
vec2.sub(eq.t, eq.t, c.normalA);
vec2.add(eq.contactPointA, eq.contactPointA, c.contactPointB);
vec2.add(eq.contactPointB, eq.contactPointB, c.contactPointA);
}
eq.contactEquations.push(c);
}
var invNumContacts = 1/numContacts;
vec2.scale(eq.contactPointA, eq.contactPointA, invNumContacts);
vec2.scale(eq.contactPointB, eq.contactPointB, invNumContacts);
vec2.normalize(eq.t, eq.t);
vec2.rotate90cw(eq.t, eq.t);
return eq;
};
/**
* Convex/line narrowphase
* @method convexLine
* @param {Body} convexBody
* @param {Convex} convexShape
* @param {Array} convexOffset
* @param {Number} convexAngle
* @param {Body} lineBody
* @param {Line} lineShape
* @param {Array} lineOffset
* @param {Number} lineAngle
* @param {boolean} justTest
* @todo Implement me!
*/
Narrowphase.prototype[Shape.LINE | Shape.CONVEX] =
Narrowphase.prototype.convexLine = function(
convexBody,
convexShape,
convexOffset,
convexAngle,
lineBody,
lineShape,
lineOffset,
lineAngle,
justTest
){
// TODO
if(justTest){
return false;
} else {
return 0;
}
};
/**
* Line/box narrowphase
* @method lineBox
* @param {Body} lineBody
* @param {Line} lineShape
* @param {Array} lineOffset
* @param {Number} lineAngle
* @param {Body} boxBody
* @param {Box} boxShape
* @param {Array} boxOffset
* @param {Number} boxAngle
* @param {Boolean} justTest
* @todo Implement me!
*/
Narrowphase.prototype[Shape.LINE | Shape.BOX] =
Narrowphase.prototype.lineBox = function(
lineBody,
lineShape,
lineOffset,
lineAngle,
boxBody,
boxShape,
boxOffset,
boxAngle,
justTest
){
// TODO
if(justTest){
return false;
} else {
return 0;
}
};
function setConvexToCapsuleShapeMiddle(convexShape, capsuleShape){
vec2.set(convexShape.vertices[0], -capsuleShape.length * 0.5, -capsuleShape.radius);
vec2.set(convexShape.vertices[1], capsuleShape.length * 0.5, -capsuleShape.radius);
vec2.set(convexShape.vertices[2], capsuleShape.length * 0.5, capsuleShape.radius);
vec2.set(convexShape.vertices[3], -capsuleShape.length * 0.5, capsuleShape.radius);
}
var convexCapsule_tempRect = new Box({ width: 1, height: 1 }),
convexCapsule_tempVec = vec2.create();
/**
* Convex/capsule narrowphase
* @method convexCapsule
* @param {Body} convexBody
* @param {Convex} convexShape
* @param {Array} convexPosition
* @param {Number} convexAngle
* @param {Body} capsuleBody
* @param {Capsule} capsuleShape
* @param {Array} capsulePosition
* @param {Number} capsuleAngle
*/
Narrowphase.prototype[Shape.CAPSULE | Shape.CONVEX] =
Narrowphase.prototype[Shape.CAPSULE | Shape.BOX] =
Narrowphase.prototype.convexCapsule = function(
convexBody,
convexShape,
convexPosition,
convexAngle,
capsuleBody,
capsuleShape,
capsulePosition,
capsuleAngle,
justTest
){
// Check the circles
// Add offsets!
var circlePos = convexCapsule_tempVec;
vec2.set(circlePos, capsuleShape.length/2,0);
vec2.rotate(circlePos,circlePos,capsuleAngle);
v