starling-framework
Version:
A fast, productive library for 2D cross-platform development.
717 lines (699 loc) • 19 kB
JavaScript
// 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;