Earcut, used for polygon triangulation, has been updated from 2.1.4 to 2.2.2.

This commit is contained in:
Richard Davey 2020-08-18 17:24:45 +01:00
parent 24e3a0ca42
commit 616dbfceb5

View file

@ -4,17 +4,17 @@
* @license {@link https://opensource.org/licenses/MIT|MIT License} * @license {@link https://opensource.org/licenses/MIT|MIT License}
*/ */
// Earcut 2.1.4 (December 4th 2018) // Earcut 2.2.2 (January 21st 2020)
/* /*
* ISC License * ISC License
* *
* Copyright (c) 2016, Mapbox * Copyright (c) 2016, Mapbox
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose * Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted, provided that the above copyright notice * with or without fee is hereby granted, provided that the above copyright notice
* and this permission notice appear in all copies. * and this permission notice appear in all copies.
* *
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
@ -85,7 +85,7 @@ function linkedList(data, start, end, dim, clockwise) {
return last; return last;
} }
// eliminate collinear or duplicate points // eliminate colinear or duplicate points
function filterPoints(start, end) { function filterPoints(start, end) {
if (!start) return start; if (!start) return start;
if (!end) end = start; if (!end) end = start;
@ -149,7 +149,7 @@ function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
// if this didn't work, try curing all small self-intersections locally // if this didn't work, try curing all small self-intersections locally
} else if (pass === 1) { } else if (pass === 1) {
ear = cureLocalIntersections(ear, triangles, dim); ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
// as a last resort, try splitting the remaining polygon into two // as a last resort, try splitting the remaining polygon into two
@ -256,7 +256,7 @@ function cureLocalIntersections(start, triangles, dim) {
p = p.next; p = p.next;
} while (p !== start); } while (p !== start);
return p; return filterPoints(p);
} }
// try splitting polygon into two and triangulate them independently // try splitting polygon into two and triangulate them independently
@ -270,7 +270,7 @@ function splitEarcut(start, triangles, dim, minX, minY, invSize) {
// split the polygon in two by the diagonal // split the polygon in two by the diagonal
var c = splitPolygon(a, b); var c = splitPolygon(a, b);
// filter collinear points around the cuts // filter colinear points around the cuts
a = filterPoints(a, a.next); a = filterPoints(a, a.next);
c = filterPoints(c, c.next); c = filterPoints(c, c.next);
@ -318,6 +318,9 @@ function eliminateHole(hole, outerNode) {
outerNode = findHoleBridge(hole, outerNode); outerNode = findHoleBridge(hole, outerNode);
if (outerNode) { if (outerNode) {
var b = splitPolygon(outerNode, hole); var b = splitPolygon(outerNode, hole);
// filter collinear points around the cuts
filterPoints(outerNode, outerNode.next);
filterPoints(b, b.next); filterPoints(b, b.next);
} }
} }
@ -349,7 +352,7 @@ function findHoleBridge(hole, outerNode) {
if (!m) return null; if (!m) return null;
if (hx === qx) return m.prev; // hole touches outer segment; pick lower endpoint if (hx === qx) return m; // hole touches outer segment; pick leftmost endpoint
// look for points inside the triangle of hole point, segment intersection and endpoint; // look for points inside the triangle of hole point, segment intersection and endpoint;
// if there are no points found, we have a valid connection; // if there are no points found, we have a valid connection;
@ -361,26 +364,32 @@ function findHoleBridge(hole, outerNode) {
tanMin = Infinity, tanMin = Infinity,
tan; tan;
p = m.next; p = m;
while (p !== stop) { do {
if (hx >= p.x && p.x >= mx && hx !== p.x && 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 tan = Math.abs(hy - p.y) / (hx - p.x); // tangential
if ((tan < tanMin || (tan === tanMin && p.x > m.x)) && locallyInside(p, hole)) { if (locallyInside(p, hole) &&
(tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) {
m = p; m = p;
tanMin = tan; tanMin = tan;
} }
} }
p = p.next; p = p.next;
} } while (p !== stop);
return m; return m;
} }
// whether sector in vertex m contains sector in vertex p in the same coordinates
function sectorContainsSector(m, p) {
return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
}
// interlink polygon nodes in z-order // interlink polygon nodes in z-order
function indexCurve(start, minX, minY, invSize) { function indexCurve(start, minX, minY, invSize) {
var p = start; var p = start;
@ -474,7 +483,7 @@ function getLeftmost(start) {
var p = start, var p = start,
leftmost = start; leftmost = start;
do { 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; p = p.next;
} while (p !== start); } while (p !== start);
@ -490,8 +499,10 @@ function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
// check if a diagonal between two polygon nodes is valid (lies in polygon interior) // check if a diagonal between two polygon nodes is valid (lies in polygon interior)
function isValidDiagonal(a, b) { function isValidDiagonal(a, b) {
return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges
locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b); (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
(area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case
} }
// signed area of a triangle // signed area of a triangle
@ -506,10 +517,28 @@ function equals(p1, p2) {
// check if two segments intersect // check if two segments intersect
function intersects(p1, q1, p2, q2) { function intersects(p1, q1, p2, q2) {
if ((equals(p1, q1) && equals(p2, q2)) || var o1 = sign(area(p1, q1, p2));
(equals(p1, q2) && equals(p2, q1))) return true; var o2 = sign(area(p1, q1, q2));
return area(p1, q1, p2) > 0 !== area(p1, q1, q2) > 0 && var o3 = sign(area(p2, q2, p1));
area(p2, q2, p1) > 0 !== area(p2, q2, q1) > 0; var o4 = sign(area(p2, q2, q1));
if (o1 !== o2 && o3 !== o4) return true; // general case
if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1
if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1
if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2
if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2
return false;
}
// for collinear points p, q, r, check if point q lies on segment pr
function onSegment(p, q, r) {
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);
}
function sign(num) {
return num > 0 ? 1 : num < 0 ? -1 : 0;
} }
// check if a polygon diagonal intersects any polygon segments // check if a polygon diagonal intersects any polygon segments