Skip to content

Commit 616dbfc

Browse files
committed
Earcut, used for polygon triangulation, has been updated from 2.1.4 to 2.2.2.
1 parent 24e3a0c commit 616dbfc

1 file changed

Lines changed: 49 additions & 20 deletions

File tree

src/geom/polygon/Earcut.js

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@
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
8989
function 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
385394
function 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)
492501
function 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
508519
function 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

Comments
 (0)