Skip to content

Commit

Permalink
better robustness and performance with holes
Browse files Browse the repository at this point in the history
  • Loading branch information
mourner committed Jan 21, 2015
1 parent 847d281 commit f39e038
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 18 deletions.
12 changes: 6 additions & 6 deletions README.md
Expand Up @@ -23,10 +23,10 @@ Some benchmarks:

(ops/sec) | pts | earcut | libtess | poly2tri | pnltri
------------------| ---- | --------- | -------- | -------- | ---------
OSM building | 15 | _605,427_ | _28,124_ | _28,131_ | _210,320_
dude shape | 94 | _28,757_ | _5,904_ | _3,544_ | _12,916_
holed dude shape | 104 | _11,575_ | _5,204_ | _3,205_ | _2,232_
complex OSM water | 2523 | _35.93_ | _64.73_ | failure | failure
OSM building | 15 | _603,533_ | _28,124_ | _28,131_ | _210,320_
dude shape | 94 | _28,620_ | _5,904_ | _3,544_ | _12,916_
holed dude shape | 104 | _13,913_ | _5,204_ | _3,205_ | _2,232_
complex OSM water | 2523 | _45.13_ | _64.73_ | failure | failure

Earcut may be slow for huge complex shapes,
but when it comes to triangulating lots of shapes with relatively low number of vertices on average
Expand Down Expand Up @@ -74,8 +74,8 @@ npm test

##### 1.1.0 (Jan 21)

- Improved performance on complex polygons with lots of holes
by switching from Held to Eberly hole elimination algorithm
- Improved performance on polygons with holes by switching from Held to Eberly hole elimination algorithm
- More robustness fixes and tests

##### 1.0.1 — 1.0.6 (Jan 20, 2015)

Expand Down
27 changes: 16 additions & 11 deletions src/earcut.js
Expand Up @@ -4,9 +4,9 @@ module.exports = earcut;

function earcut(points) {

var outerNode = filterPoints(linkedList(points[0], true));
var outerNode = linkedList(points[0], true);

if (points.length > 1) eliminateHoles(points, outerNode);
if (points.length > 1) outerNode = eliminateHoles(points, outerNode);

var triangles = [];
if (outerNode) earcutLinked(outerNode, triangles);
Expand Down Expand Up @@ -58,7 +58,10 @@ function filterPoints(start) {
return start;
}

function earcutLinked(ear, triangles) {
function earcutLinked(ear, triangles, secondPass) {
ear = filterPoints(ear);
if (!ear) return;

var stop = ear,
prev, next;

Expand All @@ -82,8 +85,10 @@ function earcutLinked(ear, triangles) {
ear = next;

if (ear === stop) {
// if we can't find valid ears anymore, split remaining polygon into two
splitEarcut(ear, triangles);
// if we can't find any more ears, try filtering points and cutting again
if (!secondPass) earcutLinked(ear, triangles, true);
// if this didn't work, try splitting the remaining polygon into two
else splitEarcut(ear, triangles);
break;
}
}
Expand Down Expand Up @@ -135,11 +140,6 @@ function isEar(ear) {

function splitEarcut(start, triangles) {
// find a valid diagonal that divides the polygon into two

start = filterPoints(start);
if (!start) return;

// iterate through all potential diagonals
var a = start;
do {
var b = a.next.next;
Expand Down Expand Up @@ -170,7 +170,12 @@ function eliminateHoles(points, outerNode) {
queue.sort(compareX);

// process holes from left to right
for (i = 0; i < queue.length; i++) eliminateHole(queue[i], outerNode);
for (i = 0; i < queue.length; i++) {
eliminateHole(queue[i], outerNode);
outerNode = filterPoints(outerNode);
}

return outerNode;
}

function eliminateHole(holeNode, outerNode) {
Expand Down
4 changes: 4 additions & 0 deletions test/fixtures/empty-square.json
@@ -0,0 +1,4 @@
[
[[0,0],[4000,0],[4000,4000],[0,4000]],
[[0,0],[4000,0],[4000,4000],[0,4000]]
]
5 changes: 5 additions & 0 deletions test/fixtures/water3b.json
@@ -0,0 +1,5 @@
[
[[-128,4224],[-128,-128],[4224,-128],[4224,4224],[-128,4224]],
[[3832,-21],[3840,-17],[3877,21],[3895,39],[3961,-21],[3893,-98],[3855,-128],[3688,-128],[3742,-81],[3793,-41],[3832,-21],[3832,-21]],
[[4205,596],[4224,572],[4224,248],[4166,163],[4119,50],[4020,36],[4004,21],[3969,21],[3936,62],[3982,117],[4088,293],[4152,419],[4185,544],[4205,596],[4205,596]]
]
4 changes: 3 additions & 1 deletion test/test.js
Expand Up @@ -7,11 +7,13 @@ areaTest('dude');
areaTest('water', 0.0021);
areaTest('water2');
areaTest('water3');
areaTest('water3b');
areaTest('water4');
areaTest('water-huge', 0.0012);
areaTest('water-huge', 0.0021);
areaTest('water-huge2', 0.0023);
areaTest('degenerate');
areaTest('bad-hole', 0.05);
areaTest('empty-square');

function areaTest(filename, expectedDeviation) {
expectedDeviation = expectedDeviation || 0.000001;
Expand Down

0 comments on commit f39e038

Please sign in to comment.