UNPKG

starling-framework

Version:

A fast, productive library for 2D cross-platform development.

717 lines (699 loc) 19 kB
// Class: starling.utils.Earcut var $global = typeof window != "undefined" ? window : typeof global != "undefined" ? global : typeof self != "undefined" ? self : this $global.Object.defineProperty(exports, "__esModule", {value: true}); var __map_reserved = {}; // Imports var $hxClasses = require("./../../hxClasses_stub").default; var $hxEnums = require("./../../hxEnums_stub").default; var $import = require("./../../import_stub").default; function _$UInt_UInt_$Impl_$() {return require("./../../_UInt/UInt_Impl_");} function openfl__$Vector_Vector_$Impl_$() {return require("./../../openfl/_Vector/Vector_Impl_");} function Std() {return require("./../../Std");} function starling_utils_Node() {return require("./../../starling/utils/Node");} function openfl__$Vector_VectorIterator() {return require("./../../openfl/_Vector/VectorIterator");} // Constructor var Earcut = function(){} // Meta Earcut.__name__ = "starling.utils.Earcut"; Earcut.__isInterface__ = false; Earcut.prototype = { }; Earcut.prototype.__class__ = Earcut.prototype.constructor = $hxClasses["starling.utils.Earcut"] = Earcut; // Init // Statics Earcut.earcut = function(vertices,holes,dimensions) { if(dimensions == null) { dimensions = 2; } var hasHoles = holes != null && holes.length != 0; var outerLen = hasHoles ? (_$UInt_UInt_$Impl_$().default).toFloat(holes[0] * dimensions) : vertices.length; var outerNode = Earcut.linkedList(vertices,0,outerLen,dimensions,true); var triangles = (openfl__$Vector_Vector_$Impl_$().default)._new(); if(outerNode == null || outerNode.next == outerNode.prev) { return triangles; } var minX = NaN; var minY = NaN; var invSize = NaN; if(hasHoles) { outerNode = Earcut.eliminateHoles(vertices,holes,outerNode,dimensions); } if(vertices.length > 80 * dimensions) { minX = Infinity; minY = Infinity; var maxX = -Infinity; var maxY = -Infinity; var i = dimensions; while(i < outerLen) { var x = vertices[i]; var y = vertices[i + 1]; if(x < minX) { minX = x; } if(y < minY) { minY = y; } if(x > maxX) { maxX = x; } if(y > maxY) { maxY = y; } i += dimensions; } invSize = Math.max(maxX - minX,maxY - minY); invSize = invSize != 0.0 ? 32767 / invSize : 0.0; } Earcut.earcutLinked(outerNode,triangles,dimensions,minX,minY,invSize,0); return triangles; } Earcut.linkedList = function(data,start,end,dim,clockwise) { var last = null; if(clockwise == Earcut.signedArea(data,start,end,dim) > 0) { var i = start; while(i < end) { last = Earcut.insertNode((Std().default).int(i / (_$UInt_UInt_$Impl_$().default).toFloat(dim)) | 0,data[(Std().default).int(i)],data[(Std().default).int(i) + 1],last); i = (_$UInt_UInt_$Impl_$().default).toFloat(dim) + i; } } else { var l = end - (_$UInt_UInt_$Impl_$().default).toFloat(dim); while(l >= start) { last = Earcut.insertNode((Std().default).int(l / (_$UInt_UInt_$Impl_$().default).toFloat(dim)) | 0,data[(Std().default).int(l)],data[(Std().default).int(l) + 1],last); l -= (_$UInt_UInt_$Impl_$().default).toFloat(dim); } } if(last != null && Earcut.equals(last,last.next)) { Earcut.removeNode(last); last = last.next; } return last; } Earcut.filterPoints = function(start,end) { if(start == null) { return start; } if(end == null) { end = start; } var p = start; var again; while(true) { again = false; if(!p.steiner && (Earcut.equals(p,p.next) || Earcut.area(p.prev,p,p.next) == 0)) { Earcut.removeNode(p); end = p.prev; p = end; if(p == p.next) { break; } again = true; } else { p = p.next; } if(!(again || p != end)) { break; } } return end; } Earcut.earcutLinked = function(ear,triangles,dim,minX,minY,invSize,pass) { if(ear == null) { return; } if(pass == 0.0 && (invSize == invSize && invSize != 0.0)) { Earcut.indexCurve(ear,minX,minY,invSize); } var stop = ear; while(ear.prev != ear.next) { var prev = ear.prev; var next = ear.next; if(invSize == invSize && invSize != 0.0 ? Earcut.isEarHashed(ear,minX,minY,invSize) : Earcut.isEar(ear)) { (openfl__$Vector_Vector_$Impl_$().default).push(triangles,(Std().default).int(prev.i)); (openfl__$Vector_Vector_$Impl_$().default).push(triangles,(Std().default).int(ear.i)); (openfl__$Vector_Vector_$Impl_$().default).push(triangles,(Std().default).int(next.i)); Earcut.removeNode(ear); ear = next.next; stop = next.next; continue; } ear = next; if(ear == stop) { if(pass == 0.0) { Earcut.earcutLinked(Earcut.filterPoints(ear),triangles,dim,minX,minY,invSize,1); } else if(pass == 1.0) { ear = Earcut.cureLocalIntersections(Earcut.filterPoints(ear),triangles); Earcut.earcutLinked(ear,triangles,dim,minX,minY,invSize,2); } else if(pass == 2.0) { Earcut.splitEarcut(ear,triangles,dim,minX,minY,invSize); } break; } } } Earcut.isEar = function(ear) { var a = ear.prev; var b = ear; var c = ear.next; if(Earcut.area(a,b,c) >= 0.0) { return false; } var ax = a.x; var bx = b.x; var cx = c.x; var ay = a.y; var by = b.y; var cy = c.y; var x0 = Math.min(Math.min(ax,bx),cx); var y0 = Math.min(Math.min(ay,by),cy); var x1 = Math.max(Math.max(ax,bx),cx); var y1 = Math.max(Math.max(ay,by),cy); var p = c.next; while(p != a) { if(p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && Earcut.pointInTriangleExceptFirst(ax,ay,bx,by,cx,cy,p.x,p.y) && Earcut.area(p.prev,p,p.next) >= 0) { return false; } p = p.next; } return true; } Earcut.isEarHashed = function(ear,minX,minY,invSize) { var a = ear.prev; var b = ear; var c = ear.next; if(Earcut.area(a,b,c) >= 0.0) { return false; } var ax = a.x; var bx = b.x; var cx = c.x; var ay = a.y; var by = b.y; var cy = c.y; var x0 = Math.min(Math.min(ax,bx),cx); var y0 = Math.min(Math.min(ay,by),cy); var x1 = Math.max(Math.max(ax,bx),cx); var y1 = Math.max(Math.max(ay,by),cy); var minZ = Earcut.zOrder(x0,y0,minX,minY,invSize); var maxZ = Earcut.zOrder(x1,y1,minX,minY,invSize); var p = ear.prevZ; var n = ear.nextZ; while(p != null && p.z >= minZ && n != null && n.z <= maxZ) { if(p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p != a && p != c && Earcut.pointInTriangleExceptFirst(ax,ay,bx,by,cx,cy,p.x,p.y) && Earcut.area(p.prev,p,p.next) >= 0) { return false; } p = p.prevZ; if(n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n != a && n != c && Earcut.pointInTriangleExceptFirst(ax,ay,bx,by,cx,cy,n.x,n.y) && Earcut.area(n.prev,n,n.next) >= 0) { return false; } n = n.nextZ; } while(p != null && p.z >= minZ) { if(p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p != a && p != c && Earcut.pointInTriangleExceptFirst(ax,ay,bx,by,cx,cy,p.x,p.y) && Earcut.area(p.prev,p,p.next) >= 0) { return false; } p = p.prevZ; } while(n != null && n.z <= maxZ) { if(n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n != a && n != c && Earcut.pointInTriangleExceptFirst(ax,ay,bx,by,cx,cy,n.x,n.y) && Earcut.area(n.prev,n,n.next) >= 0) { return false; } n = n.nextZ; } return true; } Earcut.cureLocalIntersections = function(start,triangles) { var p = start; while(true) { var a = p.prev; var b = p.next.next; if(!Earcut.equals(a,b) && Earcut.intersects(a,p,p.next,b) && Earcut.locallyInside(a,b) && Earcut.locallyInside(b,a)) { (openfl__$Vector_Vector_$Impl_$().default).push(triangles,(Std().default).int(a.i)); (openfl__$Vector_Vector_$Impl_$().default).push(triangles,(Std().default).int(p.i)); (openfl__$Vector_Vector_$Impl_$().default).push(triangles,(Std().default).int(b.i)); Earcut.removeNode(p); Earcut.removeNode(p.next); start = b; p = start; } p = p.next; if(!(p != start)) { break; } } return Earcut.filterPoints(p); } Earcut.splitEarcut = function(start,triangles,dim,minX,minY,invSize) { var a = start; while(true) { var b = a.next.next; while(b != a.prev) { if(a.i != b.i && Earcut.isValidDiagonal(a,b)) { var c = Earcut.splitPolygon(a,b); a = Earcut.filterPoints(a,a.next); c = Earcut.filterPoints(c,c.next); Earcut.earcutLinked(a,triangles,dim,minX,minY,invSize,0); Earcut.earcutLinked(c,triangles,dim,minX,minY,invSize,0); return; } b = b.next; } a = a.next; if(!(a != start)) { break; } } } Earcut.eliminateHoles = function(data,holeIndices,outerNode,dim) { var queue = (openfl__$Vector_Vector_$Impl_$().default)._new(); var len = holeIndices.length; var _g = 0; var _g1 = len; while(_g < _g1) { var i = _g++; var start = holeIndices[i] * dim; var end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; var list = Earcut.linkedList(data,start,end,dim,false); if(list == list.next) { list.steiner = true; } (openfl__$Vector_Vector_$Impl_$().default).push(queue,Earcut.getLeftmost(list)); } (Array.prototype.sort.call)(queue,Earcut.compareXYSlope); var _g2 = 0; var _g3 = queue.length; while(_g2 < _g3) { var l = _g2++; outerNode = Earcut.eliminateHole(queue[l],outerNode); } return outerNode; } Earcut.compareXYSlope = function(a,b) { var result = a.x - b.x; if(result == 0) { result = a.y - b.y; if(result == 0) { var aSlope = (a.next.y - a.y) / (a.next.x - a.x); var bSlope = (b.next.y - b.y) / (b.next.x - b.x); result = aSlope - bSlope; } } if(result > 0.0) { return 1; } else if(result < 0.0) { return -1; } else { return 0; } } Earcut.eliminateHole = function(hole,outerNode) { var bridge = Earcut.findHoleBridge(hole,outerNode); if(bridge == null) { return outerNode; } var bridgeReverse = Earcut.splitPolygon(bridge,hole); Earcut.filterPoints(bridgeReverse,bridgeReverse.next); return Earcut.filterPoints(bridge,bridge.next); } Earcut.findHoleBridge = function(hole,outerNode) { var p = outerNode; var hx = hole.x; var hy = hole.y; var qx = -Infinity; var m = null; if(Earcut.equals(hole,p)) { return p; } while(true) { if(Earcut.equals(hole,p.next)) { return p.next; } else if(hy <= p.y && hy >= p.next.y && p.next.y != p.y) { var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); if(x <= hx && x > qx) { qx = x; m = p.x < p.next.x ? p : p.next; if(x == hx) { return m; } } } p = p.next; if(!(p != outerNode)) { break; } } if(m == null) { return null; } var stop = m; var mx = m.x; var my = m.y; var tanMin = Infinity; p = m; while(true) { if(hx >= p.x && p.x >= mx && hx != p.x && Earcut.pointInTriangle(hy < my ? hx : qx,hy,mx,my,hy < my ? qx : hx,hy,p.x,p.y)) { var tan = Math.abs(hy - p.y) / (hx - p.x); if(Earcut.locallyInside(p,hole) && (tan < tanMin || tan == tanMin && (p.x > m.x || p.x == m.x && Earcut.sectorContainsSector(m,p)))) { m = p; tanMin = tan; } } p = p.next; if(!(p != stop)) { break; } } return m; } Earcut.sectorContainsSector = function(m,p) { if(Earcut.area(m.prev,m,p.prev) < 0.0) { return Earcut.area(p.next,m,m.next) < 0.0; } else { return false; } } Earcut.indexCurve = function(start,minX,minY,invSize) { var p = start; while(true) { if(p.z == 0) { p.z = Earcut.zOrder(p.x,p.y,minX,minY,invSize); } p.prevZ = p.prev; p.nextZ = p.next; p = p.next; if(!(p != start)) { break; } } p.prevZ.nextZ = null; p.prevZ = null; Earcut.sortLinked(p); } Earcut.sortLinked = function(list) { var numMerges; var inSize = 1; while(true) { var p = list; var e; list = null; var tail = null; numMerges = 0; while(p != null) { ++numMerges; var q = p; var pSize = 0; var _g = 0; var _g1 = inSize; while(_g < _g1) { var i = _g++; ++pSize; q = q.nextZ; if(q == null) { break; } } var qSize = inSize; while(pSize > 0 || qSize > 0 && q != null) { if(pSize != 0 && (qSize == 0 || q == null || p.z <= q.z)) { e = p; p = p.nextZ; --pSize; } else { e = q; q = q.nextZ; --qSize; } if(tail != null) { tail.nextZ = e; } else { list = e; } e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; if(!(numMerges > 1)) { break; } } return list; } Earcut.zOrder = function(x,y,minX,minY,invSize) { var intX = (Std().default).int((x - minX) * invSize) | 0; var intY = (Std().default).int((y - minY) * invSize) | 0; intX = (intX | intX << 8) & 16711935; intX = (intX | intX << 4) & 252645135; intX = (intX | intX << 2) & 858993459; intX = (intX | intX << 1) & 1431655765; intY = (intY | intY << 8) & 16711935; intY = (intY | intY << 4) & 252645135; intY = (intY | intY << 2) & 858993459; intY = (intY | intY << 1) & 1431655765; return intX | intY << 1; } Earcut.getLeftmost = function(start) { var p = start; var leftmost = start; while(true) { if(p.x < leftmost.x || p.x == leftmost.x && p.y < leftmost.y) { leftmost = p; } p = p.next; if(!(p != start)) { break; } } return leftmost; } Earcut.pointInTriangle = function(ax,ay,bx,by,cx,cy,px,py) { if((cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py)) { return (bx - px) * (cy - py) >= (cx - px) * (by - py); } else { return false; } } Earcut.pointInTriangleExceptFirst = function(ax,ay,bx,by,cx,cy,px,py) { if(!(ax == px && ay == py)) { return Earcut.pointInTriangle(ax,ay,bx,by,cx,cy,px,py); } else { return false; } } Earcut.isValidDiagonal = function(a,b) { if(a.next.i != b.i && a.prev.i != b.i && !Earcut.intersectsPolygon(a,b)) { if(!(Earcut.locallyInside(a,b) && Earcut.locallyInside(b,a) && Earcut.middleInside(a,b) && (Earcut.area(a.prev,a,b.prev) != 0.0 || Earcut.area(a,b.prev,b) != 0.0))) { if(Earcut.equals(a,b) && Earcut.area(a.prev,a,a.next) > 0.0) { return Earcut.area(b.prev,b,b.next) > 0.0; } else { return false; } } else { return true; } } else { return false; } } Earcut.area = function(p,q,r) { return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); } Earcut.equals = function(p1,p2) { if(p1.x == p2.x) { return p1.y == p2.y; } else { return false; } } Earcut.intersects = function(p1,q1,p2,q2) { var o1 = Earcut.sign(Earcut.area(p1,q1,p2)); var o2 = Earcut.sign(Earcut.area(p1,q1,q2)); var o3 = Earcut.sign(Earcut.area(p2,q2,p1)); var o4 = Earcut.sign(Earcut.area(p2,q2,q1)); if(o1 != o2 && o3 != o4) { return true; } if(o1 == 0 && Earcut.onSegment(p1,p2,q1)) { return true; } if(o2 == 0 && Earcut.onSegment(p1,q2,q1)) { return true; } if(o3 == 0 && Earcut.onSegment(p2,p1,q2)) { return true; } if(o4 == 0 && Earcut.onSegment(p2,q1,q2)) { return true; } return false; } Earcut.onSegment = function(p,q,r) { if(q.x <= Math.max(p.x,r.x) && q.x >= Math.min(p.x,r.x) && q.y <= Math.max(p.y,r.y)) { return q.y >= Math.min(p.y,r.y); } else { return false; } } Earcut.sign = function(num) { if(num > 0) { return 1; } else if(num < 0) { return -1; } else { return 0; } } Earcut.intersectsPolygon = function(a,b) { var p = a; while(true) { if(p.i != a.i && p.next.i != a.i && p.i != b.i && p.next.i != b.i && Earcut.intersects(p,p.next,a,b)) { return true; } p = p.next; if(!(p != a)) { break; } } return false; } Earcut.locallyInside = function(a,b) { if(Earcut.area(a.prev,a,a.next) < 0) { if(Earcut.area(a,b,a.next) >= 0) { return Earcut.area(a,a.prev,b) >= 0; } else { return false; } } else if(!(Earcut.area(a,b,a.prev) < 0)) { return Earcut.area(a,a.next,b) < 0; } else { return true; } } Earcut.middleInside = function(a,b) { var p = a; var inside = false; var px = (a.x + b.x) / 2.0; var py = (a.y + b.y) / 2.0; while(true) { if(p.y > py != p.next.y > py && p.next.y != p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x) { inside = !inside; } p = p.next; if(!(p != a)) { break; } } return inside; } Earcut.splitPolygon = function(a,b) { var a2 = Earcut.createNode(a.i,a.x,a.y); var b2 = Earcut.createNode(b.i,b.x,b.y); var an = a.next; var bp = b.next; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return bp; } Earcut.insertNode = function(i,x,y,last) { var p = Earcut.createNode(i,x,y); if(last == null) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } Earcut.removeNode = function(p) { p.next.prev = p.prev; p.prev.next = p.next; if(p.prevZ != null) { p.prevZ.nextZ = p.nextZ; } if(p.nextZ != null) { p.nextZ.prevZ = p.prevZ; } } Earcut.createNode = function(i,x,y) { return new (starling_utils_Node().default)(i,x,y); } Earcut.deviation = function(vertices,holes,dimensions,triangles) { var hasHoles = holes != null && holes.length != 0; var outerLen = hasHoles ? holes[0] * dimensions : vertices.length; var polygonArea = Math.abs(Earcut.signedArea(vertices,0,outerLen,dimensions)); if(hasHoles) { var len = holes.length; var _g = 0; var _g1 = len; while(_g < _g1) { var i = _g++; var start = holes[i] * dimensions; var end = i < len - 1 ? holes[i + 1] * dimensions : vertices.length; polygonArea -= Math.abs(Earcut.signedArea(vertices,start,end,dimensions)); } } var trianglesArea = 0; var l = 0; while(l < triangles.length) { var a = (Std().default).int(triangles[l] * dimensions); var b = (Std().default).int(triangles[l + 1] * dimensions); var c = (Std().default).int(triangles[l + 2] * dimensions); trianglesArea += Math.abs((triangles[a] - triangles[c]) * (triangles[b + 1] - triangles[a + 1]) - (triangles[a] - triangles[b]) * (triangles[c + 1] - triangles[a + 1])); l += 3; } if(polygonArea == 0 && trianglesArea == 0) { return 0; } else { return Math.abs((trianglesArea - polygonArea) / polygonArea); } } Earcut.signedArea = function(data,start,end,dim) { var sum = 0; var i = start; var j = end - dim; while(i < end) { sum += (data[(Std().default).int(j)] - data[(Std().default).int(i)]) * (data[(Std().default).int(i) + 1] + data[(Std().default).int(j) + 1]); j = i; i += dim; } return sum; } Earcut.flatten = function(data) { var vertices = (openfl__$Vector_Vector_$Impl_$().default)._new(); var holes = (openfl__$Vector_Vector_$Impl_$().default)._new(); var dimensions = data[0][0].length; var holeIndex = 0; var prevLen = 0; var ring = new (openfl__$Vector_VectorIterator().default)(data); while(ring.hasNext()) { var ring1 = ring.next(); var p = new (openfl__$Vector_VectorIterator().default)(ring1); while(p.hasNext()) { var p1 = p.next(); var _g2 = 0; var _g3 = dimensions; while(_g2 < _g3) { var d = _g2++; (openfl__$Vector_Vector_$Impl_$().default).push(vertices,p1[d]); } } if(prevLen > 0) { holeIndex += prevLen; (openfl__$Vector_Vector_$Impl_$().default).push(holes,holeIndex); } prevLen = ring1.length; } return { vertices : vertices, holes : holes, dimensions : dimensions}; } // Export exports.default = Earcut;