44 * @license {@link https://opensource.org/licenses/MIT|MIT License }
55 */
66
7- // Earcut 2.1.4 (December 4th 2018 )
7+ // Earcut 2.2.2 (January 21st 2020 )
88
99/*
1010 * ISC License
11- *
11+ *
1212 * Copyright (c) 2016, Mapbox
13- *
13+ *
1414 * Permission to use, copy, modify, and/or distribute this software for any purpose
1515 * with or without fee is hereby granted, provided that the above copyright notice
1616 * and this permission notice appear in all copies.
17- *
17+ *
1818 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
1919 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
2020 * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
@@ -85,7 +85,7 @@ function linkedList(data, start, end, dim, clockwise) {
8585 return last ;
8686}
8787
88- // eliminate collinear or duplicate points
88+ // eliminate colinear or duplicate points
8989function filterPoints ( start , end ) {
9090 if ( ! start ) return start ;
9191 if ( ! end ) end = start ;
@@ -149,7 +149,7 @@ function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
149149
150150 // if this didn't work, try curing all small self-intersections locally
151151 } else if ( pass === 1 ) {
152- ear = cureLocalIntersections ( ear , triangles , dim ) ;
152+ ear = cureLocalIntersections ( filterPoints ( ear ) , triangles , dim ) ;
153153 earcutLinked ( ear , triangles , dim , minX , minY , invSize , 2 ) ;
154154
155155 // as a last resort, try splitting the remaining polygon into two
@@ -256,7 +256,7 @@ function cureLocalIntersections(start, triangles, dim) {
256256 p = p . next ;
257257 } while ( p !== start ) ;
258258
259- return p ;
259+ return filterPoints ( p ) ;
260260}
261261
262262// try splitting polygon into two and triangulate them independently
@@ -270,7 +270,7 @@ function splitEarcut(start, triangles, dim, minX, minY, invSize) {
270270 // split the polygon in two by the diagonal
271271 var c = splitPolygon ( a , b ) ;
272272
273- // filter collinear points around the cuts
273+ // filter colinear points around the cuts
274274 a = filterPoints ( a , a . next ) ;
275275 c = filterPoints ( c , c . next ) ;
276276
@@ -318,6 +318,9 @@ function eliminateHole(hole, outerNode) {
318318 outerNode = findHoleBridge ( hole , outerNode ) ;
319319 if ( outerNode ) {
320320 var b = splitPolygon ( outerNode , hole ) ;
321+
322+ // filter collinear points around the cuts
323+ filterPoints ( outerNode , outerNode . next ) ;
321324 filterPoints ( b , b . next ) ;
322325 }
323326}
@@ -349,7 +352,7 @@ function findHoleBridge(hole, outerNode) {
349352
350353 if ( ! m ) return null ;
351354
352- if ( hx === qx ) return m . prev ; // hole touches outer segment; pick lower endpoint
355+ if ( hx === qx ) return m ; // hole touches outer segment; pick leftmost endpoint
353356
354357 // look for points inside the triangle of hole point, segment intersection and endpoint;
355358 // if there are no points found, we have a valid connection;
@@ -361,26 +364,32 @@ function findHoleBridge(hole, outerNode) {
361364 tanMin = Infinity ,
362365 tan ;
363366
364- p = m . next ;
367+ p = m ;
365368
366- while ( p !== stop ) {
369+ do {
367370 if ( hx >= p . x && p . x >= mx && hx !== p . x &&
368371 pointInTriangle ( hy < my ? hx : qx , hy , mx , my , hy < my ? qx : hx , hy , p . x , p . y ) ) {
369372
370373 tan = Math . abs ( hy - p . y ) / ( hx - p . x ) ; // tangential
371374
372- if ( ( tan < tanMin || ( tan === tanMin && p . x > m . x ) ) && locallyInside ( p , hole ) ) {
375+ if ( locallyInside ( p , hole ) &&
376+ ( tan < tanMin || ( tan === tanMin && ( p . x > m . x || ( p . x === m . x && sectorContainsSector ( m , p ) ) ) ) ) ) {
373377 m = p ;
374378 tanMin = tan ;
375379 }
376380 }
377381
378382 p = p . next ;
379- }
383+ } while ( p !== stop ) ;
380384
381385 return m ;
382386}
383387
388+ // whether sector in vertex m contains sector in vertex p in the same coordinates
389+ function sectorContainsSector ( m , p ) {
390+ return area ( m . prev , m , p . prev ) < 0 && area ( p . next , m , m . next ) < 0 ;
391+ }
392+
384393// interlink polygon nodes in z-order
385394function indexCurve ( start , minX , minY , invSize ) {
386395 var p = start ;
@@ -474,7 +483,7 @@ function getLeftmost(start) {
474483 var p = start ,
475484 leftmost = start ;
476485 do {
477- if ( p . x < leftmost . x ) leftmost = p ;
486+ if ( p . x < leftmost . x || ( p . x === leftmost . x && p . y < leftmost . y ) ) leftmost = p ;
478487 p = p . next ;
479488 } while ( p !== start ) ;
480489
@@ -490,8 +499,10 @@ function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
490499
491500// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
492501function isValidDiagonal ( a , b ) {
493- return a . next . i !== b . i && a . prev . i !== b . i && ! intersectsPolygon ( a , b ) &&
494- locallyInside ( a , b ) && locallyInside ( b , a ) && middleInside ( a , b ) ;
502+ return a . next . i !== b . i && a . prev . i !== b . i && ! intersectsPolygon ( a , b ) && // dones't intersect other edges
503+ ( locallyInside ( a , b ) && locallyInside ( b , a ) && middleInside ( a , b ) && // locally visible
504+ ( area ( a . prev , a , b . prev ) || area ( a , b . prev , b ) ) || // does not create opposite-facing sectors
505+ equals ( a , b ) && area ( a . prev , a , a . next ) > 0 && area ( b . prev , b , b . next ) > 0 ) ; // special zero-length case
495506}
496507
497508// signed area of a triangle
@@ -506,10 +517,28 @@ function equals(p1, p2) {
506517
507518// check if two segments intersect
508519function intersects ( p1 , q1 , p2 , q2 ) {
509- if ( ( equals ( p1 , q1 ) && equals ( p2 , q2 ) ) ||
510- ( equals ( p1 , q2 ) && equals ( p2 , q1 ) ) ) return true ;
511- return area ( p1 , q1 , p2 ) > 0 !== area ( p1 , q1 , q2 ) > 0 &&
512- area ( p2 , q2 , p1 ) > 0 !== area ( p2 , q2 , q1 ) > 0 ;
520+ var o1 = sign ( area ( p1 , q1 , p2 ) ) ;
521+ var o2 = sign ( area ( p1 , q1 , q2 ) ) ;
522+ var o3 = sign ( area ( p2 , q2 , p1 ) ) ;
523+ var o4 = sign ( area ( p2 , q2 , q1 ) ) ;
524+
525+ if ( o1 !== o2 && o3 !== o4 ) return true ; // general case
526+
527+ if ( o1 === 0 && onSegment ( p1 , p2 , q1 ) ) return true ; // p1, q1 and p2 are collinear and p2 lies on p1q1
528+ if ( o2 === 0 && onSegment ( p1 , q2 , q1 ) ) return true ; // p1, q1 and q2 are collinear and q2 lies on p1q1
529+ if ( o3 === 0 && onSegment ( p2 , p1 , q2 ) ) return true ; // p2, q2 and p1 are collinear and p1 lies on p2q2
530+ if ( o4 === 0 && onSegment ( p2 , q1 , q2 ) ) return true ; // p2, q2 and q1 are collinear and q1 lies on p2q2
531+
532+ return false ;
533+ }
534+
535+ // for collinear points p, q, r, check if point q lies on segment pr
536+ function onSegment ( p , q , r ) {
537+ return q . x <= Math . max ( p . x , r . x ) && q . x >= Math . min ( p . x , r . x ) && q . y <= Math . max ( p . y , r . y ) && q . y >= Math . min ( p . y , r . y ) ;
538+ }
539+
540+ function sign ( num ) {
541+ return num > 0 ? 1 : num < 0 ? - 1 : 0 ;
513542}
514543
515544// check if a polygon diagonal intersects any polygon segments
0 commit comments