From 3d8f88e979b26bd1ed0e7d644aa8ac038a30cb16 Mon Sep 17 00:00:00 2001 From: Robert Herhold Date: Tue, 13 Sep 2016 12:40:39 -0400 Subject: [PATCH] Update to latest poly-decomp.js API --- demo/js/lib/decomp.js | 496 ++++++++++++++++++------------------------ src/factory/Bodies.js | 34 +-- 2 files changed, 234 insertions(+), 296 deletions(-) diff --git a/demo/js/lib/decomp.js b/demo/js/lib/decomp.js index f16bc45..6119908 100644 --- a/demo/js/lib/decomp.js +++ b/demo/js/lib/decomp.js @@ -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; -return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o=0 && s<=1 && t>=0 && t<=1); -}; - - -},{"./Scalar":4}],2:[function(require,module,exports){ -module.exports = Point; - -/** - * Point related functions - * @class Point - */ -function Point(){}; + return (s>=0 && s<=1 && t>=0 && t<=1); +} /** * 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 * @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]))); -}; +} -Point.left = function(a,b,c){ - return Point.area(a,b,c) > 0; -}; +function isLeft(a,b,c){ + return triangleArea(a,b,c) > 0; +} -Point.leftOn = function(a,b,c) { - return Point.area(a, b, c) >= 0; -}; +function isLeftOn(a,b,c) { + return triangleArea(a, b, c) >= 0; +} -Point.right = function(a,b,c) { - return Point.area(a, b, c) < 0; -}; +function isRight(a,b,c) { + return triangleArea(a, b, c) < 0; +} -Point.rightOn = function(a,b,c) { - return Point.area(a, b, c) <= 0; -}; +function isRightOn(a,b,c) { + return triangleArea(a, b, c) <= 0; +} var tmpPoint1 = [], 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. * @return {Boolean} */ -Point.collinear = function(a,b,c,thresholdAngle) { - if(!thresholdAngle) - return Point.area(a, b, c) == 0; - else { +function collinear(a,b,c,thresholdAngle) { + if(!thresholdAngle){ + return triangleArea(a, b, c) === 0; + } else { var ab = tmpPoint1, bc = tmpPoint2; @@ -131,34 +119,12 @@ Point.collinear = function(a,b,c,thresholdAngle) { angle = Math.acos(dot/(magA*magB)); return angle < thresholdAngle; } -}; +} -Point.sqdist = function(a,b){ +function sqdist(a,b){ var dx = b[0] - a[0]; var dy = b[1] - a[1]; 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 * @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]; -}; +function polygonAt(polygon, i){ + var s = polygon.length; + return polygon[i < 0 ? i % s + s : i % s]; +} /** * Clear the polygon data * @method clear * @return {Array} */ -Polygon.prototype.clear = function(){ - this.vertices.length = 0; -}; +function polygonClear(polygon){ + polygon.length = 0; +} /** * 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. * @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"); - +function polygonAppend(polygon, poly, from, to){ for(var i=from; i v[br][0])) { + 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])) { br = i; } } // reverse poly if clockwise - if (!Point.left(this.at(br - 1), this.at(br), this.at(br + 1))) { - this.reverse(); + if (!isLeft(polygonAt(polygon, br - 1), polygonAt(polygon, br), polygonAt(polygon, br + 1))) { + polygonReverse(polygon); } -}; +} /** * Reverse the vertices in the polygon * @method reverse */ -Polygon.prototype.reverse = function(){ +function polygonReverse(polygon){ var tmp = []; - for(var i=0, N=this.vertices.length; i!==N; i++){ - tmp.push(this.vertices.pop()); + var N = polygon.length; + 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 @@ -260,9 +203,9 @@ Polygon.prototype.reverse = function(){ * @param {Number} i * @return {Boolean} */ -Polygon.prototype.isReflex = function(i){ - return Point.right(this.at(i - 1), this.at(i), this.at(i + 1)); -}; +function polygonIsReflex(polygon, i){ + return isRight(polygonAt(polygon, i - 1), polygonAt(polygon, i), polygonAt(polygon, i + 1)); +} var tmpLine1=[], tmpLine2=[]; @@ -274,30 +217,31 @@ var tmpLine1=[], * @param {Number} b Vertex index 2 * @return {Boolean} */ -Polygon.prototype.canSee = function(a,b) { +function polygonCanSee(polygon, 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))) { + 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; } - 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 + dist = sqdist(polygonAt(polygon, a), polygonAt(polygon, b)); + for (var i = 0; i !== polygon.length; ++i) { // for each edge + if ((i + 1) % polygon.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 + } + 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[0] = polygonAt(polygon, a); + l1[1] = polygonAt(polygon, b); + l2[0] = polygonAt(polygon, i); + l2[1] = polygonAt(polygon, i + 1); + p = lineInt(l1,l2); + if (sqdist(polygonAt(polygon, a), p) < dist) { // if edge is blocking visibility to b return false; } } } return true; -}; +} /** * 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. * @return {Polygon} The resulting copy. */ -Polygon.prototype.copy = function(i,j,targetPoly){ - var p = targetPoly || new Polygon(); - p.clear(); +function polygonCopy(polygon, i,j,targetPoly){ + var p = targetPoly || []; + polygonClear(p); if (i < j) { // Insert all vertices from i to j - for(var k=i; k<=j; k++) - p.vertices.push(this.vertices[k]); + for(var k=i; k<=j; k++){ + p.push(polygon[k]); + } } else { // Insert vertices 0 to j - for(var k=0; k<=j; k++) - p.vertices.push(this.vertices[k]); + for(var k=0; k<=j; k++){ + p.push(polygon[k]); + } // Insert vertices i to end - for(var k=i; k 0) - return this.slice(edges); - else - return [this]; -}; +function polygonDecomp(polygon){ + var edges = polygonGetCutEdges(polygon); + if(edges.length > 0){ + return polygonSlice(polygon, edges); + } else { + 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. @@ -381,18 +330,20 @@ Polygon.prototype.decomp = function(){ * @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){ +function polygonSlice(polygon, cutEdges){ + if(cutEdges.length === 0){ + 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 maxlevel){ @@ -497,18 +451,17 @@ Polygon.prototype.quickDecomp = function(result,reflexVertices,steinerPoints,del return result; } - for (var i = 0; i < this.vertices.length; ++i) { - if (poly.isReflex(i)) { - reflexVertices.push(poly.vertices[i]); + for (var i = 0; i < polygon.length; ++i) { + if (polygonIsReflex(poly, i)) { + reflexVertices.push(poly[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); + for (var j = 0; j < polygon.length; ++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 + p = getIntersectionPoint(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j), polygonAt(poly, j - 1)); // find the point of intersection + if (isRight(polygonAt(poly, i + 1), polygonAt(poly, i), p)) { // make sure it's inside the poly + d = sqdist(poly[i], p); if (d < lowerDist) { // keep only the closest intersection lowerDist = d; 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)) - && 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 (isLeft(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j + 1)) && isRightOn(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j))) { + p = getIntersectionPoint(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j), polygonAt(poly, j + 1)); + if (isLeft(polygonAt(poly, i - 1), polygonAt(poly, i), p)) { + d = sqdist(poly[i], p); if (d < upperDist) { upperDist = d; 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 (lowerIndex == (upperIndex + 1) % this.vertices.length) { - //console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+this.vertices.length+")"); + if (lowerIndex === (upperIndex + 1) % polygon.length) { + //console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+polygon.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){ + polygonAppend(lowerPoly, poly, i, upperIndex+1); + lowerPoly.push(p); + upperPoly.push(p); + if (lowerIndex !== 0){ //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.append(poly,0,i+1); + polygonAppend(upperPoly, poly,0,i+1); } else { - if (i != 0){ + if (i !== 0){ //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.append(poly,0,upperIndex+1); - lowerPoly.vertices.push(p); - upperPoly.vertices.push(p); + polygonAppend(lowerPoly, poly,0,upperIndex+1); + lowerPoly.push(p); + upperPoly.push(p); //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.begin() + i + 1); - upperPoly.append(poly,lowerIndex,i+1); + polygonAppend(upperPoly, 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"); + //console.log("Case 2: Vertex("+i+"), closestIndex("+closestIndex+"), poly.size("+polygon.length+")\n"); if (lowerIndex > upperIndex) { - upperIndex += this.vertices.length; + upperIndex += polygon.length; } closestDist = Number.MAX_VALUE; @@ -574,47 +526,46 @@ Polygon.prototype.quickDecomp = function(result,reflexVertices,steinerPoints,del } 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 (isLeftOn(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j)) && isRightOn(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j))) { + d = sqdist(polygonAt(poly, i), polygonAt(poly, j)); if (d < closestDist) { closestDist = d; - closestIndex = j % this.vertices.length; + closestIndex = j % polygon.length; } } } if (i < closestIndex) { - lowerPoly.append(poly,i,closestIndex+1); - if (closestIndex != 0){ - upperPoly.append(poly,closestIndex,v.length); + polygonAppend(lowerPoly, poly,i,closestIndex+1); + if (closestIndex !== 0){ + polygonAppend(upperPoly, poly,closestIndex,v.length); } - upperPoly.append(poly,0,i+1); + polygonAppend(upperPoly, poly,0,i+1); } else { - if (i != 0){ - lowerPoly.append(poly,i,v.length); + if (i !== 0){ + polygonAppend(lowerPoly, poly,i,v.length); } - lowerPoly.append(poly,0,closestIndex+1); - upperPoly.append(poly,closestIndex,i+1); + polygonAppend(lowerPoly, poly,0,closestIndex+1); + polygonAppend(upperPoly, 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); + if (lowerPoly.length < upperPoly.length) { + polygonQuickDecomp(lowerPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level); + polygonQuickDecomp(upperPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level); } else { - upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); - lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); + polygonQuickDecomp(upperPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level); + polygonQuickDecomp(lowerPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level); } return result; } } - result.push(this); + result.push(polygon); return result; -}; +} /** * 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. * @return {Number} The number of points removed */ -Polygon.prototype.removeCollinearPoints = function(precision){ +function polygonRemoveCollinearPoints(polygon, 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)){ + for(var i=polygon.length-1; polygon.length>3 && i>=0; --i){ + if(collinear(polygonAt(polygon, i-1),polygonAt(polygon, i),polygonAt(polygon, 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 + polygon.splice(i%polygon.length,1); 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 @@ -653,18 +594,11 @@ function Scalar(){} * @param {Number} [precision] * @return {Boolean} */ -Scalar.eq = function(a,b,precision){ +function scalar_eq(a,b,precision){ precision = precision || 0; return Math.abs(a-b) < precision; -}; +} -},{}],5:[function(require,module,exports){ -module.exports = { - Polygon : require("./Polygon"), - Point : require("./Point"), -}; - -},{"./Point":2,"./Polygon":3}]},{},[5]) -(5) +},{}]},{},[1]) +(1) }); -; \ No newline at end of file diff --git a/src/factory/Bodies.js b/src/factory/Bodies.js index c5f2588..3a50462 100644 --- a/src/factory/Bodies.js +++ b/src/factory/Bodies.js @@ -213,7 +213,9 @@ var Vector = require('../geometry/Vector'); removeCollinear = typeof removeCollinear !== 'undefined' ? removeCollinear : 0.01; 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'); } @@ -226,7 +228,7 @@ var Vector = require('../geometry/Vector'); vertices = vertexSets[v]; isConvex = Vertices.isConvex(vertices); - if (isConvex || !window.decomp) { + if (isConvex || !decomp) { if (isConvex) { vertices = Vertices.clockwiseSort(vertices); } else { @@ -240,28 +242,29 @@ var Vector = require('../geometry/Vector'); }); } else { // initialise a decomposition - var concave = new decomp.Polygon(); - for (i = 0; i < vertices.length; i++) { - concave.vertices.push([vertices[i].x, vertices[i].y]); - } + var concave = vertices.map(function(vertex) { + return [vertex.x, vertex.y]; + }); // vertices are concave and simple, we can decompose into parts - concave.makeCCW(); + decomp.makeCCW(concave); if (removeCollinear !== false) - concave.removeCollinearPoints(removeCollinear); + decomp.removeCollinearPoints(concave, removeCollinear); // use the quick decomposition algorithm (Bayazit) - var decomposed = concave.quickDecomp(); + var decomposed = decomp.quickDecomp(concave); // for each decomposed chunk for (i = 0; i < decomposed.length; i++) { - var chunk = decomposed[i], - chunkVertices = []; + var chunk = decomposed[i]; // convert vertices into the correct structure - for (j = 0; j < chunk.vertices.length; j++) { - chunkVertices.push({ x: chunk.vertices[j][0], y: chunk.vertices[j][1] }); - } + var chunkVertices = chunk.map(function(vertices) { + return { + x: vertices[0], + y: vertices[1] + }; + }); // skip small chunks if (minimumArea > 0 && Vertices.area(chunkVertices) < minimumArea) @@ -326,4 +329,5 @@ var Vector = require('../geometry/Vector'); } }; -})(); \ No newline at end of file +})(); +