Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update earcut to v2.2.1 #8528

Merged
merged 2 commits into from
Jan 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Change Log
* Added `Globe.showSkirts` to support the ability to hide terrain skirts when viewing terrain from below the surface. [#8489](https://github.com/AnalyticalGraphicsInc/cesium/pull/8489)
* Fixed `BoundingSphere.projectTo2D` when the bounding sphere’s center is at the origin. [#8482](https://github.com/AnalyticalGraphicsInc/cesium/pull/8482)
* Added `minificationFilter` and `magnificationFilter` options to `Material` to control texture filtering. [#8473](https://github.com/AnalyticalGraphicsInc/cesium/pull/8473)
* Update [earcut](https://github.com/mapbox/earcut) to 2.2.1 [#8528](https://github.com/AnalyticalGraphicsInc/cesium/pull/8528)

##### Fixes :wrench:
* Fixed a bug where the camera could go underground during mouse navigation. [#8504](https://github.com/AnalyticalGraphicsInc/cesium/pull/8504)
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/PolygonPipeline.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import earcut from '../ThirdParty/earcut-2.1.1.js';
import earcut from '../ThirdParty/earcut-2.2.1.js';
import Cartesian2 from './Cartesian2.js';
import Cartesian3 from './Cartesian3.js';
import Cartographic from './Cartographic.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ function earcut(data, holeIndices, dim) {
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, size;
var minX, minY, maxX, maxY, x, y, invSize;

if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);

Expand All @@ -27,11 +27,12 @@ function earcut(data, holeIndices, dim) {
if (y > maxY) maxY = y;
}

// minX, minY and size are later used to transform coords into integers for z-order calculation
size = Math.max(maxX - minX, maxY - minY);
// 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;
}

earcutLinked(outerNode, triangles, dim, minX, minY, size);
earcutLinked(outerNode, triangles, dim, minX, minY, invSize);

return triangles;
}
Expand Down Expand Up @@ -67,7 +68,7 @@ function filterPoints(start, end) {
if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
removeNode(p);
p = end = p.prev;
if (p === p.next) return null;
if (p === p.next) break;
again = true;

} else {
Expand All @@ -79,11 +80,11 @@ function filterPoints(start, end) {
}

// main ear slicing loop which triangulates a polygon (given as a linked list)
function earcutLinked(ear, triangles, dim, minX, minY, size, pass) {
function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
if (!ear) return;

// interlink polygon nodes in z-order
if (!pass && size) indexCurve(ear, minX, minY, size);
if (!pass && invSize) indexCurve(ear, minX, minY, invSize);

var stop = ear,
prev, next;
Expand All @@ -93,15 +94,15 @@ function earcutLinked(ear, triangles, dim, minX, minY, size, pass) {
prev = ear.prev;
next = ear.next;

if (size ? isEarHashed(ear, minX, minY, size) : isEar(ear)) {
if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
// cut off the triangle
triangles.push(prev.i / dim);
triangles.push(ear.i / dim);
triangles.push(next.i / dim);

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;

Expand All @@ -114,16 +115,16 @@ function earcutLinked(ear, triangles, dim, minX, minY, size, pass) {
if (ear === stop) {
// try filtering points and slicing again
if (!pass) {
earcutLinked(filterPoints(ear), triangles, dim, minX, minY, size, 1);
earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);

// if this didn't work, try curing all small self-intersections locally
} else if (pass === 1) {
ear = cureLocalIntersections(ear, triangles, dim);
earcutLinked(ear, triangles, dim, minX, minY, size, 2);
ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);

// as a last resort, try splitting the remaining polygon into two
} else if (pass === 2) {
splitEarcut(ear, triangles, dim, minX, minY, size);
splitEarcut(ear, triangles, dim, minX, minY, invSize);
}

break;
Expand Down Expand Up @@ -151,7 +152,7 @@ function isEar(ear) {
return true;
}

function isEarHashed(ear, minX, minY, size) {
function isEarHashed(ear, minX, minY, invSize) {
var a = ear.prev,
b = ear,
c = ear.next;
Expand All @@ -165,29 +166,41 @@ function isEarHashed(ear, minX, minY, size) {
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, size),
maxZ = zOrder(maxTX, maxTY, minX, minY, size);
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.nextZ;
var p = ear.prevZ,
n = 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;
}
p = p.prevZ;

// then look for points in decreasing z-order
p = ear.prevZ;
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;
}

// 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;
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;
}

Expand All @@ -213,11 +226,11 @@ function cureLocalIntersections(start, triangles, dim) {
p = p.next;
} while (p !== start);

return p;
return filterPoints(p);
}

// try splitting polygon into two and triangulate them independently
function splitEarcut(start, triangles, dim, minX, minY, size) {
function splitEarcut(start, triangles, dim, minX, minY, invSize) {
// look for a valid diagonal that divides the polygon into two
var a = start;
do {
Expand All @@ -232,8 +245,8 @@ function splitEarcut(start, triangles, dim, minX, minY, size) {
c = filterPoints(c, c.next);

// run earcut on each half
earcutLinked(a, triangles, dim, minX, minY, size);
earcutLinked(c, triangles, dim, minX, minY, size);
earcutLinked(a, triangles, dim, minX, minY, invSize);
earcutLinked(c, triangles, dim, minX, minY, invSize);
return;
}
b = b.next;
Expand Down Expand Up @@ -290,7 +303,7 @@ 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) {
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;
Expand All @@ -306,7 +319,7 @@ function findHoleBridge(hole, outerNode) {

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;
// if there are no points found, we have a valid connection;
Expand All @@ -318,31 +331,37 @@ function findHoleBridge(hole, outerNode) {
tanMin = Infinity,
tan;

p = m.next;
p = m;

while (p !== stop) {
if (hx >= p.x && p.x >= mx &&
do {
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)) {

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;
tanMin = tan;
}
}

p = p.next;
}
} while (p !== stop);

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
function indexCurve(start, minX, minY, size) {
function indexCurve(start, minX, minY, invSize) {
var p = start;
do {
if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, size);
if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize);
p.prevZ = p.prev;
p.nextZ = p.next;
p = p.next;
Expand Down Expand Up @@ -375,20 +394,11 @@ function sortLinked(list) {
q = q.nextZ;
if (!q) break;
}

qSize = inSize;

while (pSize > 0 || (qSize > 0 && q)) {

if (pSize === 0) {
e = q;
q = q.nextZ;
qSize--;
} else if (qSize === 0 || !q) {
e = p;
p = p.nextZ;
pSize--;
} else if (p.z <= q.z) {
if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
e = p;
p = p.nextZ;
pSize--;
Expand Down Expand Up @@ -416,11 +426,11 @@ function sortLinked(list) {
return list;
}

// z-order of a point given coords and size of the data bounding box
function zOrder(x, y, minX, minY, size) {
// 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) / size;
y = 32767 * (y - minY) / size;
x = 32767 * (x - minX) * invSize;
y = 32767 * (y - minY) * invSize;

x = (x | (x << 8)) & 0x00FF00FF;
x = (x | (x << 4)) & 0x0F0F0F0F;
Expand All @@ -440,7 +450,7 @@ function getLeftmost(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);

Expand All @@ -456,8 +466,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)
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);
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) && // 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
Expand All @@ -472,10 +484,28 @@ 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;
return area(p1, q1, p2) > 0 !== area(p1, q1, q2) > 0 &&
area(p2, q2, p1) > 0 !== area(p2, q2, q1) > 0;
var o1 = sign(area(p1, q1, p2));
var o2 = sign(area(p1, q1, q2));
var o3 = sign(area(p2, q2, p1));
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
Expand Down Expand Up @@ -504,7 +534,8 @@ function middleInside(a, b) {
px = (a.x + b.x) / 2,
py = (a.y + b.y) / 2;
do {
if (((p.y > py) !== (p.next.y > py)) && (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x))
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;
} while (p !== a);
Expand Down Expand Up @@ -561,14 +592,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;

Expand Down