!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(){}; /** * 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(require,module,exports){ var Line = require("./Line") , Point = require("./Point") , Scalar = require("./Scalar") module.exports = Polygon; /** * Polygon class. * @class Polygon * @constructor */ function Polygon(){ /** * Vertices that this polygon consists of. An array of array of numbers, example: [[0,0],[1,0],..] * @property vertices * @type {Array} */ this.vertices = []; } /** * Get a vertex at position i. It does not matter if i is out of bounds, this function will just cycle. * @method at * @param {Number} i * @return {Array} */ Polygon.prototype.at = function(i){ var v = this.vertices, s = v.length; return v[i < 0 ? i % s + s : i % s]; }; /** * Get first vertex * @method first * @return {Array} */ Polygon.prototype.first = function(){ return this.vertices[0]; }; /** * Get last vertex * @method last * @return {Array} */ Polygon.prototype.last = function(){ return this.vertices[this.vertices.length-1]; }; /** * Clear the polygon data * @method clear * @return {Array} */ Polygon.prototype.clear = function(){ this.vertices.length = 0; }; /** * Append points "from" to "to"-1 from an other polygon "poly" onto this one. * @method append * @param {Polygon} poly The polygon to get points from. * @param {Number} from The vertex index in "poly". * @param {Number} to The end vertex index in "poly". Note that this vertex is NOT included when appending. * @return {Array} */ Polygon.prototype.append = function(poly,from,to){ if(typeof(from) == "undefined") throw new Error("From is not given!"); if(typeof(to) == "undefined") throw new Error("To is not given!"); if(to-1 < from) throw new Error("lol1"); if(to > poly.vertices.length) throw new Error("lol2"); if(from < 0) throw new Error("lol3"); for(var i=from; i v[br][0])) { br = i; } } // reverse poly if clockwise if (!Point.left(this.at(br - 1), this.at(br), this.at(br + 1))) { this.reverse(); } }; /** * Reverse the vertices in the polygon * @method reverse */ Polygon.prototype.reverse = function(){ var tmp = []; for(var i=0, N=this.vertices.length; i!==N; i++){ tmp.push(this.vertices.pop()); } this.vertices = tmp; }; /** * Check if a point in the polygon is a reflex point * @method isReflex * @param {Number} i * @return {Boolean} */ Polygon.prototype.isReflex = function(i){ return Point.right(this.at(i - 1), this.at(i), this.at(i + 1)); }; var tmpLine1=[], tmpLine2=[]; /** * Check if two vertices in the polygon can see each other * @method canSee * @param {Number} a Vertex index 1 * @param {Number} b Vertex index 2 * @return {Boolean} */ Polygon.prototype.canSee = function(a,b) { var p, dist, l1=tmpLine1, l2=tmpLine2; if (Point.leftOn(this.at(a + 1), this.at(a), this.at(b)) && Point.rightOn(this.at(a - 1), this.at(a), this.at(b))) { return false; } dist = Point.sqdist(this.at(a), this.at(b)); for (var i = 0; i !== this.vertices.length; ++i) { // for each edge if ((i + 1) % this.vertices.length === a || i === a) // ignore incident edges continue; if (Point.leftOn(this.at(a), this.at(b), this.at(i + 1)) && Point.rightOn(this.at(a), this.at(b), this.at(i))) { // if diag intersects an edge l1[0] = this.at(a); l1[1] = this.at(b); l2[0] = this.at(i); l2[1] = this.at(i + 1); p = Line.lineInt(l1,l2); if (Point.sqdist(this.at(a), p) < dist) { // if edge is blocking visibility to b return false; } } } return true; }; /** * Copy the polygon from vertex i to vertex j. * @method copy * @param {Number} i * @param {Number} j * @param {Polygon} [targetPoly] Optional target polygon to save in. * @return {Polygon} The resulting copy. */ Polygon.prototype.copy = function(i,j,targetPoly){ var p = targetPoly || new Polygon(); p.clear(); if (i < j) { // Insert all vertices from i to j for(var k=i; k<=j; k++) p.vertices.push(this.vertices[k]); } else { // Insert vertices 0 to j for(var k=0; k<=j; k++) p.vertices.push(this.vertices[k]); // Insert vertices i to end for(var k=i; k 0) return this.slice(edges); else return [this]; }; /** * Slices the polygon given one or more cut edges. If given one, this function will return two polygons (false on failure). If many, an array of polygons. * @method slice * @param {Array} cutEdges A list of edges, as returned by .getCutEdges() * @return {Array} */ Polygon.prototype.slice = function(cutEdges){ if(cutEdges.length == 0) return [this]; if(cutEdges instanceof Array && cutEdges.length && cutEdges[0] instanceof Array && cutEdges[0].length==2 && cutEdges[0][0] instanceof Array){ var polys = [this]; for(var i=0; i maxlevel){ console.warn("quickDecomp: max level ("+maxlevel+") reached."); return result; } for (var i = 0; i < this.vertices.length; ++i) { if (poly.isReflex(i)) { reflexVertices.push(poly.vertices[i]); upperDist = lowerDist = Number.MAX_VALUE; for (var j = 0; j < this.vertices.length; ++j) { if (Point.left(poly.at(i - 1), poly.at(i), poly.at(j)) && Point.rightOn(poly.at(i - 1), poly.at(i), poly.at(j - 1))) { // if line intersects with an edge p = getIntersectionPoint(poly.at(i - 1), poly.at(i), poly.at(j), poly.at(j - 1)); // find the point of intersection if (Point.right(poly.at(i + 1), poly.at(i), p)) { // make sure it's inside the poly d = Point.sqdist(poly.vertices[i], p); if (d < lowerDist) { // keep only the closest intersection lowerDist = d; lowerInt = p; lowerIndex = j; } } } if (Point.left(poly.at(i + 1), poly.at(i), poly.at(j + 1)) && Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) { p = getIntersectionPoint(poly.at(i + 1), poly.at(i), poly.at(j), poly.at(j + 1)); if (Point.left(poly.at(i - 1), poly.at(i), p)) { d = Point.sqdist(poly.vertices[i], p); if (d < upperDist) { upperDist = d; upperInt = p; upperIndex = j; } } } } // if there are no vertices to connect to, choose a point in the middle if (lowerIndex == (upperIndex + 1) % this.vertices.length) { //console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+this.vertices.length+")"); p[0] = (lowerInt[0] + upperInt[0]) / 2; p[1] = (lowerInt[1] + upperInt[1]) / 2; steinerPoints.push(p); if (i < upperIndex) { //lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.begin() + upperIndex + 1); lowerPoly.append(poly, i, upperIndex+1); lowerPoly.vertices.push(p); upperPoly.vertices.push(p); if (lowerIndex != 0){ //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.end()); upperPoly.append(poly,lowerIndex,poly.vertices.length); } //upperPoly.insert(upperPoly.end(), poly.begin(), poly.begin() + i + 1); upperPoly.append(poly,0,i+1); } else { if (i != 0){ //lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.end()); lowerPoly.append(poly,i,poly.vertices.length); } //lowerPoly.insert(lowerPoly.end(), poly.begin(), poly.begin() + upperIndex + 1); lowerPoly.append(poly,0,upperIndex+1); lowerPoly.vertices.push(p); upperPoly.vertices.push(p); //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.begin() + i + 1); upperPoly.append(poly,lowerIndex,i+1); } } else { // connect to the closest point within the triangle //console.log("Case 2: Vertex("+i+"), closestIndex("+closestIndex+"), poly.size("+this.vertices.length+")\n"); if (lowerIndex > upperIndex) { upperIndex += this.vertices.length; } closestDist = Number.MAX_VALUE; if(upperIndex < lowerIndex){ return result; } for (var j = lowerIndex; j <= upperIndex; ++j) { if (Point.leftOn(poly.at(i - 1), poly.at(i), poly.at(j)) && Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) { d = Point.sqdist(poly.at(i), poly.at(j)); if (d < closestDist) { closestDist = d; closestIndex = j % this.vertices.length; } } } if (i < closestIndex) { lowerPoly.append(poly,i,closestIndex+1); if (closestIndex != 0){ upperPoly.append(poly,closestIndex,v.length); } upperPoly.append(poly,0,i+1); } else { if (i != 0){ lowerPoly.append(poly,i,v.length); } lowerPoly.append(poly,0,closestIndex+1); upperPoly.append(poly,closestIndex,i+1); } } // solve smallest poly first if (lowerPoly.vertices.length < upperPoly.vertices.length) { lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); } else { upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); } return result; } } result.push(this); return result; }; /** * Remove collinear points in the polygon. * @method removeCollinearPoints * @param {Number} [precision] The threshold angle to use when determining whether two edges are collinear. Use zero for finest precision. * @return {Number} The number of points removed */ Polygon.prototype.removeCollinearPoints = function(precision){ var num = 0; for(var i=this.vertices.length-1; this.vertices.length>3 && i>=0; --i){ if(Point.collinear(this.at(i-1),this.at(i),this.at(i+1),precision)){ // Remove the middle point this.vertices.splice(i%this.vertices.length,1); i--; // Jump one point forward. Otherwise we may get a chain removal num++; } } return num; }; },{"./Line":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 * @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(require,module,exports){ module.exports = { Polygon : require("./Polygon"), Point : require("./Point"), }; },{"./Point":2,"./Polygon":3}]},{},[5]) (5) }); ;