0
0
Fork 0
mirror of https://github.com/liabru/matter-js.git synced 2024-11-23 09:26:51 -05:00

Update to latest poly-decomp.js API

This commit is contained in:
Robert Herhold 2016-09-13 12:40:39 -04:00
parent e3e90807e8
commit 3d8f88e979
2 changed files with 234 additions and 296 deletions

View file

@ -1,14 +1,11 @@
!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.decomp=e():"undefined"!=typeof global?global.decomp=e():"undefined"!=typeof self&&(self.decomp=e())}(function(){var define,module,exports; !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.decomp=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){
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(require,module,exports){ module.exports = {
var Scalar = require('./Scalar'); decomp: polygonDecomp,
quickDecomp: polygonQuickDecomp,
module.exports = Line; isSimple: polygonIsSimple,
removeCollinearPoints: polygonRemoveCollinearPoints,
/** makeCCW: polygonMakeCCW
* Container for line-related functions };
* @class Line
*/
function Line(){};
/** /**
* Compute the intersection between two lines. * Compute the intersection between two lines.
@ -19,7 +16,7 @@ function Line(){};
* @param {Number} precision Precision to use when checking if the lines are parallel * @param {Number} precision Precision to use when checking if the lines are parallel
* @return {Array} The intersection point. * @return {Array} The intersection point.
*/ */
Line.lineInt = function(l1,l2,precision){ function lineInt(l1,l2,precision){
precision = precision || 0; precision = precision || 0;
var i = [0,0]; // point var i = [0,0]; // point
var a1, b1, c1, a2, b2, c2, det; // scalars var a1, b1, c1, a2, b2, c2, det; // scalars
@ -30,12 +27,12 @@ Line.lineInt = function(l1,l2,precision){
b2 = l2[0][0] - l2[1][0]; b2 = l2[0][0] - l2[1][0];
c2 = a2 * l2[0][0] + b2 * l2[0][1]; c2 = a2 * l2[0][0] + b2 * l2[0][1];
det = a1 * b2 - a2*b1; det = a1 * b2 - a2*b1;
if (!Scalar.eq(det, 0, precision)) { // lines are not parallel if (!scalar_eq(det, 0, precision)) { // lines are not parallel
i[0] = (b2 * c1 - b1 * c2) / det; i[0] = (b2 * c1 - b1 * c2) / det;
i[1] = (a1 * c2 - a2 * c1) / det; i[1] = (a1 * c2 - a2 * c1) / det;
} }
return i; return i;
}; }
/** /**
* Checks if two line segments intersects. * Checks if two line segments intersects.
@ -46,31 +43,22 @@ Line.lineInt = function(l1,l2,precision){
* @param {Array} q2 The end 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 * @return {Boolean} True if the two line segments intersect
*/ */
Line.segmentsIntersect = function(p1, p2, q1, q2){ function lineSegmentsIntersect(p1, p2, q1, q2){
var dx = p2[0] - p1[0]; var dx = p2[0] - p1[0];
var dy = p2[1] - p1[1]; var dy = p2[1] - p1[1];
var da = q2[0] - q1[0]; var da = q2[0] - q1[0];
var db = q2[1] - q1[1]; var db = q2[1] - q1[1];
// segments are parallel // segments are parallel
if(da*dy - db*dx == 0) if((da*dy - db*dx) === 0){
return false; return false;
}
var s = (dx * (q1[1] - p1[1]) + dy * (p1[0] - q1[0])) / (da * dy - db * dx) 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) 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); return (s>=0 && s<=1 && t>=0 && t<=1);
}; }
},{"./Scalar":4}],2:[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. * 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.
@ -81,25 +69,25 @@ function Point(){};
* @param {Array} c * @param {Array} c
* @return {Number} * @return {Number}
*/ */
Point.area = function(a,b,c){ function triangleArea(a,b,c){
return (((b[0] - a[0])*(c[1] - a[1]))-((c[0] - a[0])*(b[1] - a[1]))); return (((b[0] - a[0])*(c[1] - a[1]))-((c[0] - a[0])*(b[1] - a[1])));
}; }
Point.left = function(a,b,c){ function isLeft(a,b,c){
return Point.area(a,b,c) > 0; return triangleArea(a,b,c) > 0;
}; }
Point.leftOn = function(a,b,c) { function isLeftOn(a,b,c) {
return Point.area(a, b, c) >= 0; return triangleArea(a, b, c) >= 0;
}; }
Point.right = function(a,b,c) { function isRight(a,b,c) {
return Point.area(a, b, c) < 0; return triangleArea(a, b, c) < 0;
}; }
Point.rightOn = function(a,b,c) { function isRightOn(a,b,c) {
return Point.area(a, b, c) <= 0; return triangleArea(a, b, c) <= 0;
}; }
var tmpPoint1 = [], var tmpPoint1 = [],
tmpPoint2 = []; tmpPoint2 = [];
@ -113,10 +101,10 @@ var tmpPoint1 = [],
* @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. * @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} * @return {Boolean}
*/ */
Point.collinear = function(a,b,c,thresholdAngle) { function collinear(a,b,c,thresholdAngle) {
if(!thresholdAngle) if(!thresholdAngle){
return Point.area(a, b, c) == 0; return triangleArea(a, b, c) === 0;
else { } else {
var ab = tmpPoint1, var ab = tmpPoint1,
bc = tmpPoint2; bc = tmpPoint2;
@ -131,34 +119,12 @@ Point.collinear = function(a,b,c,thresholdAngle) {
angle = Math.acos(dot/(magA*magB)); angle = Math.acos(dot/(magA*magB));
return angle < thresholdAngle; return angle < thresholdAngle;
} }
}; }
Point.sqdist = function(a,b){ function sqdist(a,b){
var dx = b[0] - a[0]; var dx = b[0] - a[0];
var dy = b[1] - a[1]; var dy = b[1] - a[1];
return dx * dx + dy * dy; return dx * dx + dy * dy;
};
},{}],3:[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 = [];
} }
/** /**
@ -167,38 +133,19 @@ function Polygon(){
* @param {Number} i * @param {Number} i
* @return {Array} * @return {Array}
*/ */
Polygon.prototype.at = function(i){ function polygonAt(polygon, i){
var v = this.vertices, var s = polygon.length;
s = v.length; return polygon[i < 0 ? i % s + s : i % s];
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 * Clear the polygon data
* @method clear * @method clear
* @return {Array} * @return {Array}
*/ */
Polygon.prototype.clear = function(){ function polygonClear(polygon){
this.vertices.length = 0; polygon.length = 0;
}; }
/** /**
* Append points "from" to "to"-1 from an other polygon "poly" onto this one. * Append points "from" to "to"-1 from an other polygon "poly" onto this one.
@ -208,51 +155,47 @@ Polygon.prototype.clear = function(){
* @param {Number} to The end vertex index in "poly". Note that this vertex is NOT included when appending. * @param {Number} to The end vertex index in "poly". Note that this vertex is NOT included when appending.
* @return {Array} * @return {Array}
*/ */
Polygon.prototype.append = function(poly,from,to){ function polygonAppend(polygon, 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++){ for(var i=from; i<to; i++){
this.vertices.push(poly.vertices[i]); polygon.push(poly[i]);
} }
}; }
/** /**
* Make sure that the polygon vertices are ordered counter-clockwise. * Make sure that the polygon vertices are ordered counter-clockwise.
* @method makeCCW * @method makeCCW
*/ */
Polygon.prototype.makeCCW = function(){ function polygonMakeCCW(polygon){
var br = 0, var br = 0,
v = this.vertices; v = polygon;
// find bottom right point // find bottom right point
for (var i = 1; i < this.vertices.length; ++i) { for (var i = 1; i < polygon.length; ++i) {
if (v[i][1] < v[br][1] || (v[i][1] == v[br][1] && v[i][0] > v[br][0])) { if (v[i][1] < v[br][1] || (v[i][1] === v[br][1] && v[i][0] > v[br][0])) {
br = i; br = i;
} }
} }
// reverse poly if clockwise // reverse poly if clockwise
if (!Point.left(this.at(br - 1), this.at(br), this.at(br + 1))) { if (!isLeft(polygonAt(polygon, br - 1), polygonAt(polygon, br), polygonAt(polygon, br + 1))) {
this.reverse(); polygonReverse(polygon);
} }
}; }
/** /**
* Reverse the vertices in the polygon * Reverse the vertices in the polygon
* @method reverse * @method reverse
*/ */
Polygon.prototype.reverse = function(){ function polygonReverse(polygon){
var tmp = []; var tmp = [];
for(var i=0, N=this.vertices.length; i!==N; i++){ var N = polygon.length;
tmp.push(this.vertices.pop()); for(var i=0; i!==N; i++){
tmp.push(polygon.pop());
} }
this.vertices = tmp; for(var i=0; i!==N; i++){
}; polygon[i] = tmp[i];
}
}
/** /**
* Check if a point in the polygon is a reflex point * Check if a point in the polygon is a reflex point
@ -260,9 +203,9 @@ Polygon.prototype.reverse = function(){
* @param {Number} i * @param {Number} i
* @return {Boolean} * @return {Boolean}
*/ */
Polygon.prototype.isReflex = function(i){ function polygonIsReflex(polygon, i){
return Point.right(this.at(i - 1), this.at(i), this.at(i + 1)); return isRight(polygonAt(polygon, i - 1), polygonAt(polygon, i), polygonAt(polygon, i + 1));
}; }
var tmpLine1=[], var tmpLine1=[],
tmpLine2=[]; tmpLine2=[];
@ -274,30 +217,31 @@ var tmpLine1=[],
* @param {Number} b Vertex index 2 * @param {Number} b Vertex index 2
* @return {Boolean} * @return {Boolean}
*/ */
Polygon.prototype.canSee = function(a,b) { function polygonCanSee(polygon, a,b) {
var p, dist, l1=tmpLine1, l2=tmpLine2; 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))) { if (isLeftOn(polygonAt(polygon, a + 1), polygonAt(polygon, a), polygonAt(polygon, b)) && isRightOn(polygonAt(polygon, a - 1), polygonAt(polygon, a), polygonAt(polygon, b))) {
return false; return false;
} }
dist = Point.sqdist(this.at(a), this.at(b)); dist = sqdist(polygonAt(polygon, a), polygonAt(polygon, b));
for (var i = 0; i !== this.vertices.length; ++i) { // for each edge for (var i = 0; i !== polygon.length; ++i) { // for each edge
if ((i + 1) % this.vertices.length === a || i === a) // ignore incident edges if ((i + 1) % polygon.length === a || i === a){ // ignore incident edges
continue; 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); if (isLeftOn(polygonAt(polygon, a), polygonAt(polygon, b), polygonAt(polygon, i + 1)) && isRightOn(polygonAt(polygon, a), polygonAt(polygon, b), polygonAt(polygon, i))) { // if diag intersects an edge
l1[1] = this.at(b); l1[0] = polygonAt(polygon, a);
l2[0] = this.at(i); l1[1] = polygonAt(polygon, b);
l2[1] = this.at(i + 1); l2[0] = polygonAt(polygon, i);
p = Line.lineInt(l1,l2); l2[1] = polygonAt(polygon, i + 1);
if (Point.sqdist(this.at(a), p) < dist) { // if edge is blocking visibility to b p = lineInt(l1,l2);
if (sqdist(polygonAt(polygon, a), p) < dist) { // if edge is blocking visibility to b
return false; return false;
} }
} }
} }
return true; return true;
}; }
/** /**
* Copy the polygon from vertex i to vertex j. * Copy the polygon from vertex i to vertex j.
@ -307,27 +251,30 @@ Polygon.prototype.canSee = function(a,b) {
* @param {Polygon} [targetPoly] Optional target polygon to save in. * @param {Polygon} [targetPoly] Optional target polygon to save in.
* @return {Polygon} The resulting copy. * @return {Polygon} The resulting copy.
*/ */
Polygon.prototype.copy = function(i,j,targetPoly){ function polygonCopy(polygon, i,j,targetPoly){
var p = targetPoly || new Polygon(); var p = targetPoly || [];
p.clear(); polygonClear(p);
if (i < j) { if (i < j) {
// Insert all vertices from i to j // Insert all vertices from i to j
for(var k=i; k<=j; k++) for(var k=i; k<=j; k++){
p.vertices.push(this.vertices[k]); p.push(polygon[k]);
}
} else { } else {
// Insert vertices 0 to j // Insert vertices 0 to j
for(var k=0; k<=j; k++) for(var k=0; k<=j; k++){
p.vertices.push(this.vertices[k]); p.push(polygon[k]);
}
// Insert vertices i to end // Insert vertices i to end
for(var k=i; k<this.vertices.length; k++) for(var k=i; k<polygon.length; k++){
p.vertices.push(this.vertices[k]); p.push(polygon[k]);
}
} }
return p; return p;
}; }
/** /**
* Decomposes the polygon into convex pieces. Returns a list of edges [[p1,p2],[p2,p3],...] that cuts the polygon. * Decomposes the polygon into convex pieces. Returns a list of edges [[p1,p2],[p2,p3],...] that cuts the polygon.
@ -335,24 +282,25 @@ Polygon.prototype.copy = function(i,j,targetPoly){
* @method getCutEdges * @method getCutEdges
* @return {Array} * @return {Array}
*/ */
Polygon.prototype.getCutEdges = function() { function polygonGetCutEdges(polygon) {
var min=[], tmp1=[], tmp2=[], tmpPoly = new Polygon(); var min=[], tmp1=[], tmp2=[], tmpPoly = [];
var nDiags = Number.MAX_VALUE; var nDiags = Number.MAX_VALUE;
for (var i = 0; i < this.vertices.length; ++i) { for (var i = 0; i < polygon.length; ++i) {
if (this.isReflex(i)) { if (polygonIsReflex(polygon, i)) {
for (var j = 0; j < this.vertices.length; ++j) { for (var j = 0; j < polygon.length; ++j) {
if (this.canSee(i, j)) { if (polygonCanSee(polygon, i, j)) {
tmp1 = this.copy(i, j, tmpPoly).getCutEdges(); tmp1 = polygonGetCutEdges(polygonCopy(polygon, i, j, tmpPoly));
tmp2 = this.copy(j, i, tmpPoly).getCutEdges(); tmp2 = polygonGetCutEdges(polygonCopy(polygon, j, i, tmpPoly));
for(var k=0; k<tmp2.length; k++) for(var k=0; k<tmp2.length; k++){
tmp1.push(tmp2[k]); tmp1.push(tmp2[k]);
}
if (tmp1.length < nDiags) { if (tmp1.length < nDiags) {
min = tmp1; min = tmp1;
nDiags = tmp1.length; nDiags = tmp1.length;
min.push([this.at(i), this.at(j)]); min.push([polygonAt(polygon, i), polygonAt(polygon, j)]);
} }
} }
} }
@ -360,20 +308,21 @@ Polygon.prototype.getCutEdges = function() {
} }
return min; return min;
}; }
/** /**
* Decomposes the polygon into one or more convex sub-Polygons. * Decomposes the polygon into one or more convex sub-Polygons.
* @method decomp * @method decomp
* @return {Array} An array or Polygon objects. * @return {Array} An array or Polygon objects.
*/ */
Polygon.prototype.decomp = function(){ function polygonDecomp(polygon){
var edges = this.getCutEdges(); var edges = polygonGetCutEdges(polygon);
if(edges.length > 0) if(edges.length > 0){
return this.slice(edges); return polygonSlice(polygon, edges);
else } else {
return [this]; return [polygon];
}; }
}
/** /**
* 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. * 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.
@ -381,18 +330,20 @@ Polygon.prototype.decomp = function(){
* @param {Array} cutEdges A list of edges, as returned by .getCutEdges() * @param {Array} cutEdges A list of edges, as returned by .getCutEdges()
* @return {Array} * @return {Array}
*/ */
Polygon.prototype.slice = function(cutEdges){ function polygonSlice(polygon, cutEdges){
if(cutEdges.length == 0) return [this]; if(cutEdges.length === 0){
if(cutEdges instanceof Array && cutEdges.length && cutEdges[0] instanceof Array && cutEdges[0].length==2 && cutEdges[0][0] instanceof Array){ return [polygon];
}
if(cutEdges instanceof Array && cutEdges.length && cutEdges[0] instanceof Array && cutEdges[0].length===2 && cutEdges[0][0] instanceof Array){
var polys = [this]; var polys = [polygon];
for(var i=0; i<cutEdges.length; i++){ for(var i=0; i<cutEdges.length; i++){
var cutEdge = cutEdges[i]; var cutEdge = cutEdges[i];
// Cut all polys // Cut all polys
for(var j=0; j<polys.length; j++){ for(var j=0; j<polys.length; j++){
var poly = polys[j]; var poly = polys[j];
var result = poly.slice(cutEdge); var result = polygonSlice(poly, cutEdge);
if(result){ if(result){
// Found poly! Cut and quit // Found poly! Cut and quit
polys.splice(j,1); polys.splice(j,1);
@ -407,17 +358,17 @@ Polygon.prototype.slice = function(cutEdges){
// Was given one edge // Was given one edge
var cutEdge = cutEdges; var cutEdge = cutEdges;
var i = this.vertices.indexOf(cutEdge[0]); var i = polygon.indexOf(cutEdge[0]);
var j = this.vertices.indexOf(cutEdge[1]); var j = polygon.indexOf(cutEdge[1]);
if(i != -1 && j != -1){ if(i !== -1 && j !== -1){
return [this.copy(i,j), return [polygonCopy(polygon, i,j),
this.copy(j,i)]; polygonCopy(polygon, j,i)];
} else { } else {
return false; return false;
} }
} }
}; }
/** /**
* Checks that the line segments of this polygon do not intersect each other. * Checks that the line segments of this polygon do not intersect each other.
@ -426,41 +377,42 @@ Polygon.prototype.slice = function(cutEdges){
* @return {Boolean} * @return {Boolean}
* @todo Should it check all segments with all others? * @todo Should it check all segments with all others?
*/ */
Polygon.prototype.isSimple = function(){ function polygonIsSimple(polygon){
var path = this.vertices; var path = polygon, i;
// Check // Check
for(var i=0; i<path.length-1; i++){ for(i=0; i<path.length-1; i++){
for(var j=0; j<i-1; j++){ for(var j=0; j<i-1; j++){
if(Line.segmentsIntersect(path[i], path[i+1], path[j], path[j+1] )){ if(lineSegmentsIntersect(path[i], path[i+1], path[j], path[j+1] )){
return false; return false;
} }
} }
} }
// Check the segment between the last and the first point to all others // Check the segment between the last and the first point to all others
for(var i=1; i<path.length-2; i++){ for(i=1; i<path.length-2; i++){
if(Line.segmentsIntersect(path[0], path[path.length-1], path[i], path[i+1] )){ if(lineSegmentsIntersect(path[0], path[path.length-1], path[i], path[i+1] )){
return false; return false;
} }
} }
return true; return true;
}; }
function getIntersectionPoint(p1, p2, q1, q2, delta){ function getIntersectionPoint(p1, p2, q1, q2, delta){
delta = delta || 0; delta = delta || 0;
var a1 = p2[1] - p1[1]; var a1 = p2[1] - p1[1];
var b1 = p1[0] - p2[0]; var b1 = p1[0] - p2[0];
var c1 = (a1 * p1[0]) + (b1 * p1[1]); var c1 = (a1 * p1[0]) + (b1 * p1[1]);
var a2 = q2[1] - q1[1]; var a2 = q2[1] - q1[1];
var b2 = q1[0] - q2[0]; var b2 = q1[0] - q2[0];
var c2 = (a2 * q1[0]) + (b2 * q1[1]); var c2 = (a2 * q1[0]) + (b2 * q1[1]);
var det = (a1 * b2) - (a2 * b1); var det = (a1 * b2) - (a2 * b1);
if(!Scalar.eq(det,0,delta)) if(!scalar_eq(det,0,delta)){
return [((b2 * c1) - (b1 * c2)) / det, ((a1 * c2) - (a2 * c1)) / det] return [((b2 * c1) - (b1 * c2)) / det, ((a1 * c2) - (a2 * c1)) / det];
else } else {
return [0,0] return [0,0];
}
} }
/** /**
@ -474,22 +426,24 @@ function getIntersectionPoint(p1, p2, q1, q2, delta){
* @param {Number} [level] * @param {Number} [level]
* @return {Array} * @return {Array}
*/ */
Polygon.prototype.quickDecomp = function(result,reflexVertices,steinerPoints,delta,maxlevel,level){ function polygonQuickDecomp(polygon, result,reflexVertices,steinerPoints,delta,maxlevel,level){
maxlevel = maxlevel || 100; maxlevel = maxlevel || 100;
level = level || 0; level = level || 0;
delta = delta || 25; delta = delta || 25;
result = typeof(result)!="undefined" ? result : []; result = typeof(result)!=="undefined" ? result : [];
reflexVertices = reflexVertices || []; reflexVertices = reflexVertices || [];
steinerPoints = steinerPoints || []; steinerPoints = steinerPoints || [];
var upperInt=[0,0], lowerInt=[0,0], p=[0,0]; // Points var upperInt=[0,0], lowerInt=[0,0], p=[0,0]; // Points
var upperDist=0, lowerDist=0, d=0, closestDist=0; // scalars var upperDist=0, lowerDist=0, d=0, closestDist=0; // scalars
var upperIndex=0, lowerIndex=0, closestIndex=0; // Integers var upperIndex=0, lowerIndex=0, closestIndex=0; // Integers
var lowerPoly=new Polygon(), upperPoly=new Polygon(); // polygons var lowerPoly=[], upperPoly=[]; // polygons
var poly = this, var poly = polygon,
v = this.vertices; v = polygon;
if(v.length < 3) return result; if(v.length < 3){
return result;
}
level++; level++;
if(level > maxlevel){ if(level > maxlevel){
@ -497,18 +451,17 @@ Polygon.prototype.quickDecomp = function(result,reflexVertices,steinerPoints,del
return result; return result;
} }
for (var i = 0; i < this.vertices.length; ++i) { for (var i = 0; i < polygon.length; ++i) {
if (poly.isReflex(i)) { if (polygonIsReflex(poly, i)) {
reflexVertices.push(poly.vertices[i]); reflexVertices.push(poly[i]);
upperDist = lowerDist = Number.MAX_VALUE; upperDist = lowerDist = Number.MAX_VALUE;
for (var j = 0; j < this.vertices.length; ++j) { for (var j = 0; j < polygon.length; ++j) {
if (Point.left(poly.at(i - 1), poly.at(i), poly.at(j)) if (isLeft(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j)) && isRightOn(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j - 1))) { // if line intersects with an edge
&& Point.rightOn(poly.at(i - 1), poly.at(i), poly.at(j - 1))) { // if line intersects with an edge p = getIntersectionPoint(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j), polygonAt(poly, j - 1)); // find the point of intersection
p = getIntersectionPoint(poly.at(i - 1), poly.at(i), poly.at(j), poly.at(j - 1)); // find the point of intersection if (isRight(polygonAt(poly, i + 1), polygonAt(poly, i), p)) { // make sure it's inside the poly
if (Point.right(poly.at(i + 1), poly.at(i), p)) { // make sure it's inside the poly d = sqdist(poly[i], p);
d = Point.sqdist(poly.vertices[i], p);
if (d < lowerDist) { // keep only the closest intersection if (d < lowerDist) { // keep only the closest intersection
lowerDist = d; lowerDist = d;
lowerInt = p; lowerInt = p;
@ -516,11 +469,10 @@ Polygon.prototype.quickDecomp = function(result,reflexVertices,steinerPoints,del
} }
} }
} }
if (Point.left(poly.at(i + 1), poly.at(i), poly.at(j + 1)) if (isLeft(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j + 1)) && isRightOn(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j))) {
&& Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) { p = getIntersectionPoint(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j), polygonAt(poly, j + 1));
p = getIntersectionPoint(poly.at(i + 1), poly.at(i), poly.at(j), poly.at(j + 1)); if (isLeft(polygonAt(poly, i - 1), polygonAt(poly, i), p)) {
if (Point.left(poly.at(i - 1), poly.at(i), p)) { d = sqdist(poly[i], p);
d = Point.sqdist(poly.vertices[i], p);
if (d < upperDist) { if (d < upperDist) {
upperDist = d; upperDist = d;
upperInt = p; upperInt = p;
@ -531,41 +483,41 @@ Polygon.prototype.quickDecomp = function(result,reflexVertices,steinerPoints,del
} }
// if there are no vertices to connect to, choose a point in the middle // if there are no vertices to connect to, choose a point in the middle
if (lowerIndex == (upperIndex + 1) % this.vertices.length) { if (lowerIndex === (upperIndex + 1) % polygon.length) {
//console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+this.vertices.length+")"); //console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+polygon.length+")");
p[0] = (lowerInt[0] + upperInt[0]) / 2; p[0] = (lowerInt[0] + upperInt[0]) / 2;
p[1] = (lowerInt[1] + upperInt[1]) / 2; p[1] = (lowerInt[1] + upperInt[1]) / 2;
steinerPoints.push(p); steinerPoints.push(p);
if (i < upperIndex) { if (i < upperIndex) {
//lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.begin() + upperIndex + 1); //lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.begin() + upperIndex + 1);
lowerPoly.append(poly, i, upperIndex+1); polygonAppend(lowerPoly, poly, i, upperIndex+1);
lowerPoly.vertices.push(p); lowerPoly.push(p);
upperPoly.vertices.push(p); upperPoly.push(p);
if (lowerIndex != 0){ if (lowerIndex !== 0){
//upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.end()); //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.end());
upperPoly.append(poly,lowerIndex,poly.vertices.length); polygonAppend(upperPoly, poly,lowerIndex,poly.length);
} }
//upperPoly.insert(upperPoly.end(), poly.begin(), poly.begin() + i + 1); //upperPoly.insert(upperPoly.end(), poly.begin(), poly.begin() + i + 1);
upperPoly.append(poly,0,i+1); polygonAppend(upperPoly, poly,0,i+1);
} else { } else {
if (i != 0){ if (i !== 0){
//lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.end()); //lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.end());
lowerPoly.append(poly,i,poly.vertices.length); polygonAppend(lowerPoly, poly,i,poly.length);
} }
//lowerPoly.insert(lowerPoly.end(), poly.begin(), poly.begin() + upperIndex + 1); //lowerPoly.insert(lowerPoly.end(), poly.begin(), poly.begin() + upperIndex + 1);
lowerPoly.append(poly,0,upperIndex+1); polygonAppend(lowerPoly, poly,0,upperIndex+1);
lowerPoly.vertices.push(p); lowerPoly.push(p);
upperPoly.vertices.push(p); upperPoly.push(p);
//upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.begin() + i + 1); //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.begin() + i + 1);
upperPoly.append(poly,lowerIndex,i+1); polygonAppend(upperPoly, poly,lowerIndex,i+1);
} }
} else { } else {
// connect to the closest point within the triangle // connect to the closest point within the triangle
//console.log("Case 2: Vertex("+i+"), closestIndex("+closestIndex+"), poly.size("+this.vertices.length+")\n"); //console.log("Case 2: Vertex("+i+"), closestIndex("+closestIndex+"), poly.size("+polygon.length+")\n");
if (lowerIndex > upperIndex) { if (lowerIndex > upperIndex) {
upperIndex += this.vertices.length; upperIndex += polygon.length;
} }
closestDist = Number.MAX_VALUE; closestDist = Number.MAX_VALUE;
@ -574,47 +526,46 @@ Polygon.prototype.quickDecomp = function(result,reflexVertices,steinerPoints,del
} }
for (var j = lowerIndex; j <= upperIndex; ++j) { for (var j = lowerIndex; j <= upperIndex; ++j) {
if (Point.leftOn(poly.at(i - 1), poly.at(i), poly.at(j)) if (isLeftOn(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j)) && isRightOn(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j))) {
&& Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) { d = sqdist(polygonAt(poly, i), polygonAt(poly, j));
d = Point.sqdist(poly.at(i), poly.at(j));
if (d < closestDist) { if (d < closestDist) {
closestDist = d; closestDist = d;
closestIndex = j % this.vertices.length; closestIndex = j % polygon.length;
} }
} }
} }
if (i < closestIndex) { if (i < closestIndex) {
lowerPoly.append(poly,i,closestIndex+1); polygonAppend(lowerPoly, poly,i,closestIndex+1);
if (closestIndex != 0){ if (closestIndex !== 0){
upperPoly.append(poly,closestIndex,v.length); polygonAppend(upperPoly, poly,closestIndex,v.length);
} }
upperPoly.append(poly,0,i+1); polygonAppend(upperPoly, poly,0,i+1);
} else { } else {
if (i != 0){ if (i !== 0){
lowerPoly.append(poly,i,v.length); polygonAppend(lowerPoly, poly,i,v.length);
} }
lowerPoly.append(poly,0,closestIndex+1); polygonAppend(lowerPoly, poly,0,closestIndex+1);
upperPoly.append(poly,closestIndex,i+1); polygonAppend(upperPoly, poly,closestIndex,i+1);
} }
} }
// solve smallest poly first // solve smallest poly first
if (lowerPoly.vertices.length < upperPoly.vertices.length) { if (lowerPoly.length < upperPoly.length) {
lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); polygonQuickDecomp(lowerPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level);
upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); polygonQuickDecomp(upperPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level);
} else { } else {
upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); polygonQuickDecomp(upperPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level);
lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); polygonQuickDecomp(lowerPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level);
} }
return result; return result;
} }
} }
result.push(this); result.push(polygon);
return result; return result;
}; }
/** /**
* Remove collinear points in the polygon. * Remove collinear points in the polygon.
@ -622,27 +573,17 @@ Polygon.prototype.quickDecomp = function(result,reflexVertices,steinerPoints,del
* @param {Number} [precision] The threshold angle to use when determining whether two edges are collinear. Use zero for finest precision. * @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 * @return {Number} The number of points removed
*/ */
Polygon.prototype.removeCollinearPoints = function(precision){ function polygonRemoveCollinearPoints(polygon, precision){
var num = 0; var num = 0;
for(var i=this.vertices.length-1; this.vertices.length>3 && i>=0; --i){ for(var i=polygon.length-1; polygon.length>3 && i>=0; --i){
if(Point.collinear(this.at(i-1),this.at(i),this.at(i+1),precision)){ if(collinear(polygonAt(polygon, i-1),polygonAt(polygon, i),polygonAt(polygon, i+1),precision)){
// Remove the middle point // Remove the middle point
this.vertices.splice(i%this.vertices.length,1); polygon.splice(i%polygon.length,1);
i--; // Jump one point forward. Otherwise we may get a chain removal
num++; num++;
} }
} }
return num; return num;
}; }
},{"./Line":1,"./Point":2,"./Scalar":4}],4:[function(require,module,exports){
module.exports = Scalar;
/**
* Scalar functions
* @class Scalar
*/
function Scalar(){}
/** /**
* Check if two scalars are equal * Check if two scalars are equal
@ -653,18 +594,11 @@ function Scalar(){}
* @param {Number} [precision] * @param {Number} [precision]
* @return {Boolean} * @return {Boolean}
*/ */
Scalar.eq = function(a,b,precision){ function scalar_eq(a,b,precision){
precision = precision || 0; precision = precision || 0;
return Math.abs(a-b) < precision; return Math.abs(a-b) < precision;
}; }
},{}],5:[function(require,module,exports){ },{}]},{},[1])
module.exports = { (1)
Polygon : require("./Polygon"),
Point : require("./Point"),
};
},{"./Point":2,"./Polygon":3}]},{},[5])
(5)
}); });
;

View file

@ -213,7 +213,9 @@ var Vector = require('../geometry/Vector');
removeCollinear = typeof removeCollinear !== 'undefined' ? removeCollinear : 0.01; removeCollinear = typeof removeCollinear !== 'undefined' ? removeCollinear : 0.01;
minimumArea = typeof minimumArea !== 'undefined' ? minimumArea : 10; minimumArea = typeof minimumArea !== 'undefined' ? minimumArea : 10;
if (!window.decomp) { var decomp = window.decomp;
if (!decomp) {
Common.log('Bodies.fromVertices: poly-decomp.js required. Could not decompose vertices. Fallback to convex hull.', 'warn'); Common.log('Bodies.fromVertices: poly-decomp.js required. Could not decompose vertices. Fallback to convex hull.', 'warn');
} }
@ -226,7 +228,7 @@ var Vector = require('../geometry/Vector');
vertices = vertexSets[v]; vertices = vertexSets[v];
isConvex = Vertices.isConvex(vertices); isConvex = Vertices.isConvex(vertices);
if (isConvex || !window.decomp) { if (isConvex || !decomp) {
if (isConvex) { if (isConvex) {
vertices = Vertices.clockwiseSort(vertices); vertices = Vertices.clockwiseSort(vertices);
} else { } else {
@ -240,28 +242,29 @@ var Vector = require('../geometry/Vector');
}); });
} else { } else {
// initialise a decomposition // initialise a decomposition
var concave = new decomp.Polygon(); var concave = vertices.map(function(vertex) {
for (i = 0; i < vertices.length; i++) { return [vertex.x, vertex.y];
concave.vertices.push([vertices[i].x, vertices[i].y]); });
}
// vertices are concave and simple, we can decompose into parts // vertices are concave and simple, we can decompose into parts
concave.makeCCW(); decomp.makeCCW(concave);
if (removeCollinear !== false) if (removeCollinear !== false)
concave.removeCollinearPoints(removeCollinear); decomp.removeCollinearPoints(concave, removeCollinear);
// use the quick decomposition algorithm (Bayazit) // use the quick decomposition algorithm (Bayazit)
var decomposed = concave.quickDecomp(); var decomposed = decomp.quickDecomp(concave);
// for each decomposed chunk // for each decomposed chunk
for (i = 0; i < decomposed.length; i++) { for (i = 0; i < decomposed.length; i++) {
var chunk = decomposed[i], var chunk = decomposed[i];
chunkVertices = [];
// convert vertices into the correct structure // convert vertices into the correct structure
for (j = 0; j < chunk.vertices.length; j++) { var chunkVertices = chunk.map(function(vertices) {
chunkVertices.push({ x: chunk.vertices[j][0], y: chunk.vertices[j][1] }); return {
} x: vertices[0],
y: vertices[1]
};
});
// skip small chunks // skip small chunks
if (minimumArea > 0 && Vertices.area(chunkVertices) < minimumArea) if (minimumArea > 0 && Vertices.area(chunkVertices) < minimumArea)
@ -326,4 +329,5 @@ var Vector = require('../geometry/Vector');
} }
}; };
})(); })();