earcut 2.2.4 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/earcut.js CHANGED
@@ -1,31 +1,27 @@
1
- 'use strict';
2
1
 
3
- module.exports = earcut;
4
- module.exports.default = earcut;
2
+ export default function earcut(data, holeIndices, dim = 2) {
5
3
 
6
- function earcut(data, holeIndices, dim) {
7
-
8
- dim = dim || 2;
9
-
10
- var hasHoles = holeIndices && holeIndices.length,
11
- outerLen = hasHoles ? holeIndices[0] * dim : data.length,
12
- outerNode = linkedList(data, 0, outerLen, dim, true),
13
- triangles = [];
4
+ const hasHoles = holeIndices && holeIndices.length;
5
+ const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
6
+ let outerNode = linkedList(data, 0, outerLen, dim, true);
7
+ const triangles = [];
14
8
 
15
9
  if (!outerNode || outerNode.next === outerNode.prev) return triangles;
16
10
 
17
- var minX, minY, maxX, maxY, x, y, invSize;
11
+ let minX, minY, invSize;
18
12
 
19
13
  if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
20
14
 
21
15
  // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
22
16
  if (data.length > 80 * dim) {
23
- minX = maxX = data[0];
24
- minY = maxY = data[1];
25
-
26
- for (var i = dim; i < outerLen; i += dim) {
27
- x = data[i];
28
- y = data[i + 1];
17
+ minX = Infinity;
18
+ minY = Infinity;
19
+ let maxX = -Infinity;
20
+ let maxY = -Infinity;
21
+
22
+ for (let i = dim; i < outerLen; i += dim) {
23
+ const x = data[i];
24
+ const y = data[i + 1];
29
25
  if (x < minX) minX = x;
30
26
  if (y < minY) minY = y;
31
27
  if (x > maxX) maxX = x;
@@ -44,12 +40,12 @@ function earcut(data, holeIndices, dim) {
44
40
 
45
41
  // create a circular doubly linked list from polygon points in the specified winding order
46
42
  function linkedList(data, start, end, dim, clockwise) {
47
- var i, last;
43
+ let last;
48
44
 
49
45
  if (clockwise === (signedArea(data, start, end, dim) > 0)) {
50
- for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
46
+ for (let i = start; i < end; i += dim) last = insertNode(i / dim | 0, data[i], data[i + 1], last);
51
47
  } else {
52
- for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
48
+ for (let i = end - dim; i >= start; i -= dim) last = insertNode(i / dim | 0, data[i], data[i + 1], last);
53
49
  }
54
50
 
55
51
  if (last && equals(last, last.next)) {
@@ -65,7 +61,7 @@ function filterPoints(start, end) {
65
61
  if (!start) return start;
66
62
  if (!end) end = start;
67
63
 
68
- var p = start,
64
+ let p = start,
69
65
  again;
70
66
  do {
71
67
  again = false;
@@ -91,19 +87,15 @@ function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
91
87
  // interlink polygon nodes in z-order
92
88
  if (!pass && invSize) indexCurve(ear, minX, minY, invSize);
93
89
 
94
- var stop = ear,
95
- prev, next;
90
+ let stop = ear;
96
91
 
97
92
  // iterate through ears, slicing them one by one
98
93
  while (ear.prev !== ear.next) {
99
- prev = ear.prev;
100
- next = ear.next;
94
+ const prev = ear.prev;
95
+ const next = ear.next;
101
96
 
102
97
  if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
103
- // cut off the triangle
104
- triangles.push(prev.i / dim | 0);
105
- triangles.push(ear.i / dim | 0);
106
- triangles.push(next.i / dim | 0);
98
+ triangles.push(prev.i, ear.i, next.i); // cut off the triangle
107
99
 
108
100
  removeNode(ear);
109
101
 
@@ -124,7 +116,7 @@ function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
124
116
 
125
117
  // if this didn't work, try curing all small self-intersections locally
126
118
  } else if (pass === 1) {
127
- ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
119
+ ear = cureLocalIntersections(filterPoints(ear), triangles);
128
120
  earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
129
121
 
130
122
  // as a last resort, try splitting the remaining polygon into two
@@ -139,25 +131,25 @@ function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
139
131
 
140
132
  // check whether a polygon node forms a valid ear with adjacent nodes
141
133
  function isEar(ear) {
142
- var a = ear.prev,
134
+ const a = ear.prev,
143
135
  b = ear,
144
136
  c = ear.next;
145
137
 
146
138
  if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
147
139
 
148
140
  // now make sure we don't have other points inside the potential ear
149
- var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
141
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
150
142
 
151
- // triangle bbox; min & max are calculated like this for speed
152
- var x0 = ax < bx ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx),
153
- y0 = ay < by ? (ay < cy ? ay : cy) : (by < cy ? by : cy),
154
- x1 = ax > bx ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx),
155
- y1 = ay > by ? (ay > cy ? ay : cy) : (by > cy ? by : cy);
143
+ // triangle bbox
144
+ const x0 = Math.min(ax, bx, cx),
145
+ y0 = Math.min(ay, by, cy),
146
+ x1 = Math.max(ax, bx, cx),
147
+ y1 = Math.max(ay, by, cy);
156
148
 
157
- var p = c.next;
149
+ let p = c.next;
158
150
  while (p !== a) {
159
151
  if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 &&
160
- pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) &&
152
+ pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) &&
161
153
  area(p.prev, p, p.next) >= 0) return false;
162
154
  p = p.next;
163
155
  }
@@ -166,49 +158,49 @@ function isEar(ear) {
166
158
  }
167
159
 
168
160
  function isEarHashed(ear, minX, minY, invSize) {
169
- var a = ear.prev,
161
+ const a = ear.prev,
170
162
  b = ear,
171
163
  c = ear.next;
172
164
 
173
165
  if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
174
166
 
175
- var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
167
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
176
168
 
177
- // triangle bbox; min & max are calculated like this for speed
178
- var x0 = ax < bx ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx),
179
- y0 = ay < by ? (ay < cy ? ay : cy) : (by < cy ? by : cy),
180
- x1 = ax > bx ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx),
181
- y1 = ay > by ? (ay > cy ? ay : cy) : (by > cy ? by : cy);
169
+ // triangle bbox
170
+ const x0 = Math.min(ax, bx, cx),
171
+ y0 = Math.min(ay, by, cy),
172
+ x1 = Math.max(ax, bx, cx),
173
+ y1 = Math.max(ay, by, cy);
182
174
 
183
175
  // z-order range for the current triangle bbox;
184
- var minZ = zOrder(x0, y0, minX, minY, invSize),
176
+ const minZ = zOrder(x0, y0, minX, minY, invSize),
185
177
  maxZ = zOrder(x1, y1, minX, minY, invSize);
186
178
 
187
- var p = ear.prevZ,
179
+ let p = ear.prevZ,
188
180
  n = ear.nextZ;
189
181
 
190
182
  // look for points inside the triangle in both directions
191
183
  while (p && p.z >= minZ && n && n.z <= maxZ) {
192
184
  if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
193
- pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
185
+ pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
194
186
  p = p.prevZ;
195
187
 
196
188
  if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
197
- pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
189
+ pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
198
190
  n = n.nextZ;
199
191
  }
200
192
 
201
193
  // look for remaining points in decreasing z-order
202
194
  while (p && p.z >= minZ) {
203
195
  if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
204
- pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
196
+ pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
205
197
  p = p.prevZ;
206
198
  }
207
199
 
208
200
  // look for remaining points in increasing z-order
209
201
  while (n && n.z <= maxZ) {
210
202
  if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
211
- pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
203
+ pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
212
204
  n = n.nextZ;
213
205
  }
214
206
 
@@ -216,17 +208,15 @@ function isEarHashed(ear, minX, minY, invSize) {
216
208
  }
217
209
 
218
210
  // go through all polygon nodes and cure small local self-intersections
219
- function cureLocalIntersections(start, triangles, dim) {
220
- var p = start;
211
+ function cureLocalIntersections(start, triangles) {
212
+ let p = start;
221
213
  do {
222
- var a = p.prev,
214
+ const a = p.prev,
223
215
  b = p.next.next;
224
216
 
225
217
  if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
226
218
 
227
- triangles.push(a.i / dim | 0);
228
- triangles.push(p.i / dim | 0);
229
- triangles.push(b.i / dim | 0);
219
+ triangles.push(a.i, p.i, b.i);
230
220
 
231
221
  // remove two nodes involved
232
222
  removeNode(p);
@@ -243,13 +233,13 @@ function cureLocalIntersections(start, triangles, dim) {
243
233
  // try splitting polygon into two and triangulate them independently
244
234
  function splitEarcut(start, triangles, dim, minX, minY, invSize) {
245
235
  // look for a valid diagonal that divides the polygon into two
246
- var a = start;
236
+ let a = start;
247
237
  do {
248
- var b = a.next.next;
238
+ let b = a.next.next;
249
239
  while (b !== a.prev) {
250
240
  if (a.i !== b.i && isValidDiagonal(a, b)) {
251
241
  // split the polygon in two by the diagonal
252
- var c = splitPolygon(a, b);
242
+ let c = splitPolygon(a, b);
253
243
 
254
244
  // filter colinear points around the cuts
255
245
  a = filterPoints(a, a.next);
@@ -268,39 +258,49 @@ function splitEarcut(start, triangles, dim, minX, minY, invSize) {
268
258
 
269
259
  // link every hole into the outer loop, producing a single-ring polygon without holes
270
260
  function eliminateHoles(data, holeIndices, outerNode, dim) {
271
- var queue = [],
272
- i, len, start, end, list;
261
+ const queue = [];
273
262
 
274
- for (i = 0, len = holeIndices.length; i < len; i++) {
275
- start = holeIndices[i] * dim;
276
- end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
277
- list = linkedList(data, start, end, dim, false);
263
+ for (let i = 0, len = holeIndices.length; i < len; i++) {
264
+ const start = holeIndices[i] * dim;
265
+ const end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
266
+ const list = linkedList(data, start, end, dim, false);
278
267
  if (list === list.next) list.steiner = true;
279
268
  queue.push(getLeftmost(list));
280
269
  }
281
270
 
282
- queue.sort(compareX);
271
+ queue.sort(compareXYSlope);
283
272
 
284
273
  // process holes from left to right
285
- for (i = 0; i < queue.length; i++) {
274
+ for (let i = 0; i < queue.length; i++) {
286
275
  outerNode = eliminateHole(queue[i], outerNode);
287
276
  }
288
277
 
289
278
  return outerNode;
290
279
  }
291
280
 
292
- function compareX(a, b) {
293
- return a.x - b.x;
281
+ function compareXYSlope(a, b) {
282
+ let result = a.x - b.x;
283
+ // when the left-most point of 2 holes meet at a vertex, sort the holes counterclockwise so that when we find
284
+ // the bridge to the outer shell is always the point that they meet at.
285
+ if (result === 0) {
286
+ result = a.y - b.y;
287
+ if (result === 0) {
288
+ const aSlope = (a.next.y - a.y) / (a.next.x - a.x);
289
+ const bSlope = (b.next.y - b.y) / (b.next.x - b.x);
290
+ result = aSlope - bSlope;
291
+ }
292
+ }
293
+ return result;
294
294
  }
295
295
 
296
296
  // find a bridge between vertices that connects hole with an outer ring and and link it
297
297
  function eliminateHole(hole, outerNode) {
298
- var bridge = findHoleBridge(hole, outerNode);
298
+ const bridge = findHoleBridge(hole, outerNode);
299
299
  if (!bridge) {
300
300
  return outerNode;
301
301
  }
302
302
 
303
- var bridgeReverse = splitPolygon(bridge, hole);
303
+ const bridgeReverse = splitPolygon(bridge, hole);
304
304
 
305
305
  // filter collinear points around the cuts
306
306
  filterPoints(bridgeReverse, bridgeReverse.next);
@@ -309,17 +309,20 @@ function eliminateHole(hole, outerNode) {
309
309
 
310
310
  // David Eberly's algorithm for finding a bridge between hole and outer polygon
311
311
  function findHoleBridge(hole, outerNode) {
312
- var p = outerNode,
313
- hx = hole.x,
314
- hy = hole.y,
315
- qx = -Infinity,
316
- m;
312
+ let p = outerNode;
313
+ const hx = hole.x;
314
+ const hy = hole.y;
315
+ let qx = -Infinity;
316
+ let m;
317
317
 
318
318
  // find a segment intersected by a ray from the hole's leftmost point to the left;
319
319
  // segment's endpoint with lesser x will be potential connection point
320
+ // unless they intersect at a vertex, then choose the vertex
321
+ if (equals(hole, p)) return p;
320
322
  do {
321
- if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
322
- var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
323
+ if (equals(hole, p.next)) return p.next;
324
+ else if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
325
+ const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
323
326
  if (x <= hx && x > qx) {
324
327
  qx = x;
325
328
  m = p.x < p.next.x ? p : p.next;
@@ -335,11 +338,10 @@ function findHoleBridge(hole, outerNode) {
335
338
  // if there are no points found, we have a valid connection;
336
339
  // otherwise choose the point of the minimum angle with the ray as connection point
337
340
 
338
- var stop = m,
339
- mx = m.x,
340
- my = m.y,
341
- tanMin = Infinity,
342
- tan;
341
+ const stop = m;
342
+ const mx = m.x;
343
+ const my = m.y;
344
+ let tanMin = Infinity;
343
345
 
344
346
  p = m;
345
347
 
@@ -347,7 +349,7 @@ function findHoleBridge(hole, outerNode) {
347
349
  if (hx >= p.x && p.x >= mx && hx !== p.x &&
348
350
  pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
349
351
 
350
- tan = Math.abs(hy - p.y) / (hx - p.x); // tangential
352
+ const tan = Math.abs(hy - p.y) / (hx - p.x); // tangential
351
353
 
352
354
  if (locallyInside(p, hole) &&
353
355
  (tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) {
@@ -369,7 +371,7 @@ function sectorContainsSector(m, p) {
369
371
 
370
372
  // interlink polygon nodes in z-order
371
373
  function indexCurve(start, minX, minY, invSize) {
372
- var p = start;
374
+ let p = start;
373
375
  do {
374
376
  if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize);
375
377
  p.prevZ = p.prev;
@@ -386,25 +388,26 @@ function indexCurve(start, minX, minY, invSize) {
386
388
  // Simon Tatham's linked list merge sort algorithm
387
389
  // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
388
390
  function sortLinked(list) {
389
- var i, p, q, e, tail, numMerges, pSize, qSize,
390
- inSize = 1;
391
+ let numMerges;
392
+ let inSize = 1;
391
393
 
392
394
  do {
393
- p = list;
395
+ let p = list;
396
+ let e;
394
397
  list = null;
395
- tail = null;
398
+ let tail = null;
396
399
  numMerges = 0;
397
400
 
398
401
  while (p) {
399
402
  numMerges++;
400
- q = p;
401
- pSize = 0;
402
- for (i = 0; i < inSize; i++) {
403
+ let q = p;
404
+ let pSize = 0;
405
+ for (let i = 0; i < inSize; i++) {
403
406
  pSize++;
404
407
  q = q.nextZ;
405
408
  if (!q) break;
406
409
  }
407
- qSize = inSize;
410
+ let qSize = inSize;
408
411
 
409
412
  while (pSize > 0 || (qSize > 0 && q)) {
410
413
 
@@ -457,7 +460,7 @@ function zOrder(x, y, minX, minY, invSize) {
457
460
 
458
461
  // find the leftmost node of a polygon ring
459
462
  function getLeftmost(start) {
460
- var p = start,
463
+ let p = start,
461
464
  leftmost = start;
462
465
  do {
463
466
  if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) leftmost = p;
@@ -474,6 +477,11 @@ function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
474
477
  (bx - px) * (cy - py) >= (cx - px) * (by - py);
475
478
  }
476
479
 
480
+ // check if a point lies within a convex triangle but false if its equal to the first point of the triangle
481
+ function pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, px, py) {
482
+ return !(ax === px && ay === py) && pointInTriangle(ax, ay, bx, by, cx, cy, px, py);
483
+ }
484
+
477
485
  // check if a diagonal between two polygon nodes is valid (lies in polygon interior)
478
486
  function isValidDiagonal(a, b) {
479
487
  return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges
@@ -494,10 +502,10 @@ function equals(p1, p2) {
494
502
 
495
503
  // check if two segments intersect
496
504
  function intersects(p1, q1, p2, q2) {
497
- var o1 = sign(area(p1, q1, p2));
498
- var o2 = sign(area(p1, q1, q2));
499
- var o3 = sign(area(p2, q2, p1));
500
- var o4 = sign(area(p2, q2, q1));
505
+ const o1 = sign(area(p1, q1, p2));
506
+ const o2 = sign(area(p1, q1, q2));
507
+ const o3 = sign(area(p2, q2, p1));
508
+ const o4 = sign(area(p2, q2, q1));
501
509
 
502
510
  if (o1 !== o2 && o3 !== o4) return true; // general case
503
511
 
@@ -520,7 +528,7 @@ function sign(num) {
520
528
 
521
529
  // check if a polygon diagonal intersects any polygon segments
522
530
  function intersectsPolygon(a, b) {
523
- var p = a;
531
+ let p = a;
524
532
  do {
525
533
  if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&
526
534
  intersects(p, p.next, a, b)) return true;
@@ -539,10 +547,10 @@ function locallyInside(a, b) {
539
547
 
540
548
  // check if the middle point of a polygon diagonal is inside the polygon
541
549
  function middleInside(a, b) {
542
- var p = a,
543
- inside = false,
544
- px = (a.x + b.x) / 2,
545
- py = (a.y + b.y) / 2;
550
+ let p = a;
551
+ let inside = false;
552
+ const px = (a.x + b.x) / 2;
553
+ const py = (a.y + b.y) / 2;
546
554
  do {
547
555
  if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y &&
548
556
  (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x))
@@ -556,8 +564,8 @@ function middleInside(a, b) {
556
564
  // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;
557
565
  // if one belongs to the outer ring and another to a hole, it merges it into a single ring
558
566
  function splitPolygon(a, b) {
559
- var a2 = new Node(a.i, a.x, a.y),
560
- b2 = new Node(b.i, b.x, b.y),
567
+ const a2 = createNode(a.i, a.x, a.y),
568
+ b2 = createNode(b.i, b.x, b.y),
561
569
  an = a.next,
562
570
  bp = b.prev;
563
571
 
@@ -578,7 +586,7 @@ function splitPolygon(a, b) {
578
586
 
579
587
  // create a node and optionally link it with previous one (in a circular doubly linked list)
580
588
  function insertNode(i, x, y, last) {
581
- var p = new Node(i, x, y);
589
+ const p = createNode(i, x, y);
582
590
 
583
591
  if (!last) {
584
592
  p.prev = p;
@@ -601,49 +609,39 @@ function removeNode(p) {
601
609
  if (p.nextZ) p.nextZ.prevZ = p.prevZ;
602
610
  }
603
611
 
604
- function Node(i, x, y) {
605
- // vertex index in coordinates array
606
- this.i = i;
607
-
608
- // vertex coordinates
609
- this.x = x;
610
- this.y = y;
611
-
612
- // previous and next vertex nodes in a polygon ring
613
- this.prev = null;
614
- this.next = null;
615
-
616
- // z-order curve value
617
- this.z = 0;
618
-
619
- // previous and next nodes in z-order
620
- this.prevZ = null;
621
- this.nextZ = null;
622
-
623
- // indicates whether this is a steiner point
624
- this.steiner = false;
612
+ function createNode(i, x, y) {
613
+ return {
614
+ i, // vertex index in coordinates array
615
+ x, y, // vertex coordinates
616
+ prev: null, // previous and next vertex nodes in a polygon ring
617
+ next: null,
618
+ z: 0, // z-order curve value
619
+ prevZ: null, // previous and next nodes in z-order
620
+ nextZ: null,
621
+ steiner: false // indicates whether this is a steiner point
622
+ };
625
623
  }
626
624
 
627
625
  // return a percentage difference between the polygon area and its triangulation area;
628
626
  // used to verify correctness of triangulation
629
- earcut.deviation = function (data, holeIndices, dim, triangles) {
630
- var hasHoles = holeIndices && holeIndices.length;
631
- var outerLen = hasHoles ? holeIndices[0] * dim : data.length;
627
+ export function deviation(data, holeIndices, dim, triangles) {
628
+ const hasHoles = holeIndices && holeIndices.length;
629
+ const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
632
630
 
633
- var polygonArea = Math.abs(signedArea(data, 0, outerLen, dim));
631
+ let polygonArea = Math.abs(signedArea(data, 0, outerLen, dim));
634
632
  if (hasHoles) {
635
- for (var i = 0, len = holeIndices.length; i < len; i++) {
636
- var start = holeIndices[i] * dim;
637
- var end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
633
+ for (let i = 0, len = holeIndices.length; i < len; i++) {
634
+ const start = holeIndices[i] * dim;
635
+ const end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
638
636
  polygonArea -= Math.abs(signedArea(data, start, end, dim));
639
637
  }
640
638
  }
641
639
 
642
- var trianglesArea = 0;
643
- for (i = 0; i < triangles.length; i += 3) {
644
- var a = triangles[i] * dim;
645
- var b = triangles[i + 1] * dim;
646
- var c = triangles[i + 2] * dim;
640
+ let trianglesArea = 0;
641
+ for (let i = 0; i < triangles.length; i += 3) {
642
+ const a = triangles[i] * dim;
643
+ const b = triangles[i + 1] * dim;
644
+ const c = triangles[i + 2] * dim;
647
645
  trianglesArea += Math.abs(
648
646
  (data[a] - data[c]) * (data[b + 1] - data[a + 1]) -
649
647
  (data[a] - data[b]) * (data[c + 1] - data[a + 1]));
@@ -651,11 +649,11 @@ earcut.deviation = function (data, holeIndices, dim, triangles) {
651
649
 
652
650
  return polygonArea === 0 && trianglesArea === 0 ? 0 :
653
651
  Math.abs((trianglesArea - polygonArea) / polygonArea);
654
- };
652
+ }
655
653
 
656
654
  function signedArea(data, start, end, dim) {
657
- var sum = 0;
658
- for (var i = start, j = end - dim; i < end; i += dim) {
655
+ let sum = 0;
656
+ for (let i = start, j = end - dim; i < end; i += dim) {
659
657
  sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
660
658
  j = i;
661
659
  }
@@ -663,19 +661,22 @@ function signedArea(data, start, end, dim) {
663
661
  }
664
662
 
665
663
  // turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts
666
- earcut.flatten = function (data) {
667
- var dim = data[0][0].length,
668
- result = {vertices: [], holes: [], dimensions: dim},
669
- holeIndex = 0;
670
-
671
- for (var i = 0; i < data.length; i++) {
672
- for (var j = 0; j < data[i].length; j++) {
673
- for (var d = 0; d < dim; d++) result.vertices.push(data[i][j][d]);
664
+ export function flatten(data) {
665
+ const vertices = [];
666
+ const holes = [];
667
+ const dimensions = data[0][0].length;
668
+ let holeIndex = 0;
669
+ let prevLen = 0;
670
+
671
+ for (const ring of data) {
672
+ for (const p of ring) {
673
+ for (let d = 0; d < dimensions; d++) vertices.push(p[d]);
674
674
  }
675
- if (i > 0) {
676
- holeIndex += data[i - 1].length;
677
- result.holes.push(holeIndex);
675
+ if (prevLen) {
676
+ holeIndex += prevLen;
677
+ holes.push(holeIndex);
678
678
  }
679
+ prevLen = ring.length;
679
680
  }
680
- return result;
681
- };
681
+ return {vertices, holes, dimensions};
682
+ }