Browse Source

Updated Earcut to 2.1.5.

Mr.doob 6 years ago
parent
commit
838a65f6f9
1 changed files with 54 additions and 104 deletions
  1. 54 104
      src/extras/Earcut.js

+ 54 - 104
src/extras/Earcut.js

@@ -1,6 +1,6 @@
 /**
  * @author Mugen87 / https://github.com/Mugen87
- * Port from https://github.com/mapbox/earcut (v2.1.2)
+ * Port from https://github.com/mapbox/earcut (v2.1.5)
  */
 
 var Earcut = {
@@ -14,14 +14,13 @@ var Earcut = {
 			outerNode = linkedList( data, 0, outerLen, dim, true ),
 			triangles = [];
 
-		if ( ! outerNode ) return triangles;
+		if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles;
 
 		var minX, minY, maxX, maxY, x, y, invSize;
 
 		if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim );
 
 		// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
-
 		if ( data.length > 80 * dim ) {
 
 			minX = maxX = data[ 0 ];
@@ -39,7 +38,6 @@ var Earcut = {
 			}
 
 			// minX, minY and invSize are later used to transform coords into integers for z-order calculation
-
 			invSize = Math.max( maxX - minX, maxY - minY );
 			invSize = invSize !== 0 ? 1 / invSize : 0;
 
@@ -54,7 +52,6 @@ var Earcut = {
 };
 
 // create a circular doubly linked list from polygon points in the specified winding order
-
 function linkedList( data, start, end, dim, clockwise ) {
 
 	var i, last;
@@ -81,14 +78,13 @@ function linkedList( data, start, end, dim, clockwise ) {
 }
 
 // eliminate colinear or duplicate points
-
 function filterPoints( start, end ) {
 
 	if ( ! start ) return start;
 	if ( ! end ) end = start;
 
-	var p = start, again;
-
+	var p = start,
+		again;
 	do {
 
 		again = false;
@@ -113,19 +109,17 @@ function filterPoints( start, end ) {
 }
 
 // main ear slicing loop which triangulates a polygon (given as a linked list)
-
 function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) {
 
 	if ( ! ear ) return;
 
 	// interlink polygon nodes in z-order
-
 	if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize );
 
-	var stop = ear, prev, next;
+	var stop = ear,
+		prev, next;
 
 	// iterate through ears, slicing them one by one
-
 	while ( ear.prev !== ear.next ) {
 
 		prev = ear.prev;
@@ -140,7 +134,7 @@ function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) {
 
 			removeNode( ear );
 
-			// skipping the next vertice leads to less sliver triangles
+			// skipping the next vertex leads to less sliver triangles
 			ear = next.next;
 			stop = next.next;
 
@@ -151,11 +145,9 @@ function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) {
 		ear = next;
 
 		// if we looped through the whole remaining polygon and can't find any more ears
-
 		if ( ear === stop ) {
 
 			// try filtering points and slicing again
-
 			if ( ! pass ) {
 
 				earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 );
@@ -184,7 +176,6 @@ function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) {
 }
 
 // check whether a polygon node forms a valid ear with adjacent nodes
-
 function isEar( ear ) {
 
 	var a = ear.prev,
@@ -198,12 +189,8 @@ function isEar( ear ) {
 
 	while ( p !== ear.prev ) {
 
-		if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) {
-
-			return false;
-
-		}
-
+		if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) &&
+			area( p.prev, p, p.next ) >= 0 ) return false;
 		p = p.next;
 
 	}
@@ -221,57 +208,65 @@ function isEarHashed( ear, minX, minY, invSize ) {
 	if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear
 
 	// triangle bbox; min & max are calculated like this for speed
-
 	var minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ),
 		minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ),
 		maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ),
 		maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y );
 
 	// z-order range for the current triangle bbox;
-
 	var minZ = zOrder( minTX, minTY, minX, minY, invSize ),
 		maxZ = zOrder( maxTX, maxTY, minX, minY, invSize );
 
-	// first look for points inside the triangle in increasing z-order
+	var p = ear.prevZ,
+		n = ear.nextZ;
 
-	var p = ear.nextZ;
-
-	while ( p && p.z <= maxZ ) {
+	// look for points inside the triangle in both directions
+	while ( p && p.z >= minZ && n && n.z <= maxZ ) {
 
 		if ( p !== ear.prev && p !== ear.next &&
-				pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) &&
-				area( p.prev, p, p.next ) >= 0 ) return false;
-		p = p.nextZ;
-
-	}
+			pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) &&
+			area( p.prev, p, p.next ) >= 0 ) return false;
+		p = p.prevZ;
 
-	// then look for points in decreasing z-order
+		if ( n !== ear.prev && n !== ear.next &&
+			pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) &&
+			area( n.prev, n, n.next ) >= 0 ) return false;
+		n = n.nextZ;
 
-	p = ear.prevZ;
+	}
 
+	// look for remaining points in decreasing z-order
 	while ( p && p.z >= minZ ) {
 
 		if ( p !== ear.prev && p !== ear.next &&
-				pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) &&
-				area( p.prev, p, p.next ) >= 0 ) return false;
-
+			pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) &&
+			area( p.prev, p, p.next ) >= 0 ) return false;
 		p = p.prevZ;
 
 	}
 
+	// look for remaining points in increasing z-order
+	while ( n && n.z <= maxZ ) {
+
+		if ( n !== ear.prev && n !== ear.next &&
+			pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) &&
+			area( n.prev, n, n.next ) >= 0 ) return false;
+		n = n.nextZ;
+
+	}
+
 	return true;
 
 }
 
 // go through all polygon nodes and cure small local self-intersections
-
 function cureLocalIntersections( start, triangles, dim ) {
 
 	var p = start;
-
 	do {
 
-		var a = p.prev, b = p.next.next;
+		var a = p.prev,
+			b = p.next.next;
 
 		if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) {
 
@@ -280,7 +275,6 @@ function cureLocalIntersections( start, triangles, dim ) {
 			triangles.push( b.i / dim );
 
 			// remove two nodes involved
-
 			removeNode( p );
 			removeNode( p.next );
 
@@ -297,32 +291,25 @@ function cureLocalIntersections( start, triangles, dim ) {
 }
 
 // try splitting polygon into two and triangulate them independently
-
 function splitEarcut( start, triangles, dim, minX, minY, invSize ) {
 
 	// look for a valid diagonal that divides the polygon into two
-
 	var a = start;
-
 	do {
 
 		var b = a.next.next;
-
 		while ( b !== a.prev ) {
 
 			if ( a.i !== b.i && isValidDiagonal( a, b ) ) {
 
 				// split the polygon in two by the diagonal
-
 				var c = splitPolygon( a, b );
 
 				// filter colinear points around the cuts
-
 				a = filterPoints( a, a.next );
 				c = filterPoints( c, c.next );
 
 				// run earcut on each half
-
 				earcutLinked( a, triangles, dim, minX, minY, invSize );
 				earcutLinked( c, triangles, dim, minX, minY, invSize );
 				return;
@@ -340,10 +327,10 @@ function splitEarcut( start, triangles, dim, minX, minY, invSize ) {
 }
 
 // link every hole into the outer loop, producing a single-ring polygon without holes
-
 function eliminateHoles( data, holeIndices, outerNode, dim ) {
 
-	var queue = [], i, len, start, end, list;
+	var queue = [],
+		i, len, start, end, list;
 
 	for ( i = 0, len = holeIndices.length; i < len; i ++ ) {
 
@@ -358,7 +345,6 @@ function eliminateHoles( data, holeIndices, outerNode, dim ) {
 	queue.sort( compareX );
 
 	// process holes from left to right
-
 	for ( i = 0; i < queue.length; i ++ ) {
 
 		eliminateHole( queue[ i ], outerNode );
@@ -377,15 +363,12 @@ function compareX( a, b ) {
 }
 
 // find a bridge between vertices that connects hole with an outer ring and and link it
-
 function eliminateHole( hole, outerNode ) {
 
 	outerNode = findHoleBridge( hole, outerNode );
-
 	if ( outerNode ) {
 
 		var b = splitPolygon( outerNode, hole );
-
 		filterPoints( b, b.next );
 
 	}
@@ -393,7 +376,6 @@ function eliminateHole( hole, outerNode ) {
 }
 
 // David Eberly's algorithm for finding a bridge between hole and outer polygon
-
 function findHoleBridge( hole, outerNode ) {
 
 	var p = outerNode,
@@ -404,17 +386,14 @@ function findHoleBridge( hole, outerNode ) {
 
 	// find a segment intersected by a ray from the hole's leftmost point to the left;
 	// segment's endpoint with lesser x will be potential connection point
-
 	do {
 
 		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;
-
 				if ( x === hx ) {
 
 					if ( hy === p.y ) return p;
@@ -451,7 +430,7 @@ function findHoleBridge( hole, outerNode ) {
 	while ( p !== stop ) {
 
 		if ( hx >= p.x && p.x >= mx && hx !== p.x &&
-						pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) {
+				pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) {
 
 			tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential
 
@@ -473,11 +452,9 @@ function findHoleBridge( hole, outerNode ) {
 }
 
 // interlink polygon nodes in z-order
-
 function indexCurve( start, minX, minY, invSize ) {
 
 	var p = start;
-
 	do {
 
 		if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize );
@@ -496,10 +473,10 @@ function indexCurve( start, minX, minY, invSize ) {
 
 // Simon Tatham's linked list merge sort algorithm
 // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
-
 function sortLinked( list ) {
 
-	var i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1;
+	var i, p, q, e, tail, numMerges, pSize, qSize,
+		inSize = 1;
 
 	do {
 
@@ -513,7 +490,6 @@ function sortLinked( list ) {
 			numMerges ++;
 			q = p;
 			pSize = 0;
-
 			for ( i = 0; i < inSize; i ++ ) {
 
 				pSize ++;
@@ -562,11 +538,9 @@ function sortLinked( list ) {
 }
 
 // z-order of a point given coords and inverse of the longer side of data bbox
-
 function zOrder( x, y, minX, minY, invSize ) {
 
 	// coords are transformed into non-negative 15-bit integer range
-
 	x = 32767 * ( x - minX ) * invSize;
 	y = 32767 * ( y - minY ) * invSize;
 
@@ -585,14 +559,13 @@ function zOrder( x, y, minX, minY, invSize ) {
 }
 
 // find the leftmost node of a polygon ring
-
 function getLeftmost( start ) {
 
-	var p = start, leftmost = start;
-
+	var p = start,
+		leftmost = start;
 	do {
 
-		if ( p.x < leftmost.x ) leftmost = p;
+		if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p;
 		p = p.next;
 
 	} while ( p !== start );
@@ -602,26 +575,23 @@ function getLeftmost( start ) {
 }
 
 // check if a point lies within a convex triangle
-
 function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) {
 
 	return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 &&
-	 ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 &&
-	 ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0;
+		   ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 &&
+		   ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0;
 
 }
 
 // check if a diagonal between two polygon nodes is valid (lies in polygon interior)
-
 function isValidDiagonal( a, b ) {
 
 	return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) &&
-		locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b );
+		   locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b );
 
 }
 
 // signed area of a triangle
-
 function area( p, q, r ) {
 
 	return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y );
@@ -629,7 +599,6 @@ function area( p, q, r ) {
 }
 
 // check if two points are equal
-
 function equals( p1, p2 ) {
 
 	return p1.x === p2.x && p1.y === p2.y;
@@ -637,32 +606,23 @@ function equals( p1, p2 ) {
 }
 
 // check if two segments intersect
-
 function intersects( p1, q1, p2, q2 ) {
 
-	if ( ( equals( p1, q1 ) && equals( p2, q2 ) ) ||
-			( equals( p1, q2 ) && equals( p2, q1 ) ) ) return true;
-
+	if ( ( equals( p1, p2 ) && equals( q1, q2 ) ) ||
+		( equals( p1, q2 ) && equals( p2, q1 ) ) ) return true;
 	return area( p1, q1, p2 ) > 0 !== area( p1, q1, q2 ) > 0 &&
-				 area( p2, q2, p1 ) > 0 !== area( p2, q2, q1 ) > 0;
+		   area( p2, q2, p1 ) > 0 !== area( p2, q2, q1 ) > 0;
 
 }
 
 // check if a polygon diagonal intersects any polygon segments
-
 function intersectsPolygon( a, b ) {
 
 	var p = a;
-
 	do {
 
 		if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&
-						intersects( p, p.next, a, b ) ) {
-
-			return true;
-
-		}
-
+				intersects( p, p.next, a, b ) ) return true;
 		p = p.next;
 
 	} while ( p !== a );
@@ -672,7 +632,6 @@ function intersectsPolygon( a, b ) {
 }
 
 // check if a polygon diagonal is locally inside the polygon
-
 function locallyInside( a, b ) {
 
 	return area( a.prev, a, a.next ) < 0 ?
@@ -682,23 +641,17 @@ function locallyInside( a, b ) {
 }
 
 // check if the middle point of a polygon diagonal is inside the polygon
-
 function middleInside( a, b ) {
 
 	var p = a,
 		inside = false,
 		px = ( a.x + b.x ) / 2,
 		py = ( a.y + b.y ) / 2;
-
 	do {
 
 		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 ) ) {
-
+				( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) )
 			inside = ! inside;
-
-		}
-
 		p = p.next;
 
 	} while ( p !== a );
@@ -709,7 +662,6 @@ function middleInside( a, b ) {
 
 // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;
 // if one belongs to the outer ring and another to a hole, it merges it into a single ring
-
 function splitPolygon( a, b ) {
 
 	var a2 = new Node( a.i, a.x, a.y ),
@@ -734,7 +686,6 @@ function splitPolygon( a, b ) {
 }
 
 // create a node and optionally link it with previous one (in a circular doubly linked list)
-
 function insertNode( i, x, y, last ) {
 
 	var p = new Node( i, x, y );
@@ -769,14 +720,14 @@ function removeNode( p ) {
 
 function Node( i, x, y ) {
 
-	// vertice index in coordinates array
+	// vertex index in coordinates array
 	this.i = i;
 
 	// vertex coordinates
 	this.x = x;
 	this.y = y;
 
-	// previous and next vertice nodes in a polygon ring
+	// previous and next vertex nodes in a polygon ring
 	this.prev = null;
 	this.next = null;
 
@@ -795,7 +746,6 @@ function Node( i, x, y ) {
 function signedArea( data, start, end, dim ) {
 
 	var sum = 0;
-
 	for ( var i = start, j = end - dim; i < end; i += dim ) {
 
 		sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] );