@xeokit/xeokit-sdk 2.6.65 → 2.6.67

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.
Files changed (46) hide show
  1. package/{LICENSE.txt → LICENSE} +30 -27
  2. package/README.md +13 -6
  3. package/dist/xeokit-sdk.cjs.js +50322 -25848
  4. package/dist/xeokit-sdk.es.js +50322 -25848
  5. package/dist/xeokit-sdk.es5.js +7982 -4807
  6. package/dist/xeokit-sdk.min.cjs.js +5 -5
  7. package/dist/xeokit-sdk.min.es.js +5 -5
  8. package/dist/xeokit-sdk.min.es5.js +4 -4
  9. package/package.json +34 -32
  10. package/src/extras/PointerCircle/PointerCircle.js +1 -1
  11. package/src/plugins/AnnotationsPlugin/Annotation.js +45 -5
  12. package/src/plugins/CityJSONLoaderPlugin/CityJSONLoaderPlugin.js +1 -1
  13. package/src/plugins/GLTFLoaderPlugin/GLTFSceneModelLoader.js +3 -2
  14. package/src/plugins/LASLoaderPlugin/LASLoaderPlugin.js +1 -1
  15. package/src/plugins/SectionPlanesPlugin/Control.js +11 -12
  16. package/src/plugins/XKTLoaderPlugin/XKTLoaderPlugin.js +57 -2
  17. package/src/plugins/XKTLoaderPlugin/parsers/ParserV10.js +6 -0
  18. package/src/plugins/XKTLoaderPlugin/parsers/ParserV11.js +6 -0
  19. package/src/plugins/XKTLoaderPlugin/parsers/ParserV12.js +822 -0
  20. package/src/plugins/XKTLoaderPlugin/parsers/ParserV6.js +6 -0
  21. package/src/plugins/XKTLoaderPlugin/parsers/ParserV7.js +6 -0
  22. package/src/plugins/XKTLoaderPlugin/parsers/ParserV8.js +6 -0
  23. package/src/plugins/XKTLoaderPlugin/parsers/ParserV9.js +6 -0
  24. package/src/{plugins/lib → viewer/scene/libs}/earcut.js +194 -189
  25. package/src/viewer/scene/materials/EmphasisMaterial.js +5 -1
  26. package/src/viewer/scene/math/math.js +6 -2
  27. package/src/viewer/scene/model/SceneModel.js +1 -0
  28. package/src/viewer/scene/model/SceneModelEntity.js +26 -0
  29. package/src/viewer/scene/model/SceneModelMesh.js +4 -0
  30. package/src/viewer/scene/model/dtx/triangles/DTXTrianglesLayer.js +1 -1
  31. package/src/viewer/scene/model/vbo/batching/triangles/VBOBatchingTrianglesLayer.js +1 -1
  32. package/src/viewer/scene/model/vbo/batching/triangles/renderers/TrianglesColorRenderer.js +4 -1
  33. package/src/viewer/scene/model/vbo/instancing/triangles/VBOInstancingTrianglesLayer.js +1 -1
  34. package/src/viewer/scene/model/vbo/instancing/triangles/renderers/TrianglesColorRenderer.js +4 -1
  35. package/src/viewer/scene/scene/Scene.js +50 -6
  36. package/src/viewer/scene/sectionCaps/SectionCaps.js +774 -0
  37. package/src/viewer/scene/sectionCaps/index.js +1 -0
  38. package/src/viewer/scene/sectionPlane/SectionPlane.js +79 -1
  39. package/src/viewer/scene/webgl/Renderer.js +19 -10
  40. package/types/extras/PointerCircle/PointerCircle.d.ts +50 -0
  41. package/types/extras/PointerCircle/index.d.ts +1 -0
  42. package/types/plugins/WebIFCLoaderPlugin/WebIFCLoaderPlugin.d.ts +2 -1
  43. package/types/plugins/XKTLoaderPlugin/XKTLoaderPlugin.d.ts +28 -0
  44. package/types/plugins/index.d.ts +1 -0
  45. package/types/plugins/lib/ui/index.d.ts +12 -0
  46. package/types/viewer/scene/models/SceneModelMesh.d.ts +7 -0
@@ -1,27 +1,27 @@
1
- /** @private */
2
- function earcut(data, holeIndices, dim) {
3
1
 
4
- dim = dim || 2;
2
+ export default function earcut(data, holeIndices, dim = 2) {
5
3
 
6
- var hasHoles = holeIndices && holeIndices.length,
7
- outerLen = hasHoles ? holeIndices[0] * dim : data.length,
8
- outerNode = linkedList(data, 0, outerLen, dim, true),
9
- 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 = [];
10
8
 
11
9
  if (!outerNode || outerNode.next === outerNode.prev) return triangles;
12
10
 
13
- var minX, minY, maxX, maxY, x, y, invSize;
11
+ let minX, minY, invSize;
14
12
 
15
13
  if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
16
14
 
17
15
  // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
18
16
  if (data.length > 80 * dim) {
19
- minX = maxX = data[0];
20
- minY = maxY = data[1];
21
-
22
- for (var i = dim; i < outerLen; i += dim) {
23
- x = data[i];
24
- 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];
25
25
  if (x < minX) minX = x;
26
26
  if (y < minY) minY = y;
27
27
  if (x > maxX) maxX = x;
@@ -30,22 +30,22 @@ function earcut(data, holeIndices, dim) {
30
30
 
31
31
  // minX, minY and invSize are later used to transform coords into integers for z-order calculation
32
32
  invSize = Math.max(maxX - minX, maxY - minY);
33
- invSize = invSize !== 0 ? 1 / invSize : 0;
33
+ invSize = invSize !== 0 ? 32767 / invSize : 0;
34
34
  }
35
35
 
36
- earcutLinked(outerNode, triangles, dim, minX, minY, invSize);
36
+ earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);
37
37
 
38
38
  return triangles;
39
39
  }
40
40
 
41
41
  // create a circular doubly linked list from polygon points in the specified winding order
42
42
  function linkedList(data, start, end, dim, clockwise) {
43
- var i, last;
43
+ let last;
44
44
 
45
45
  if (clockwise === (signedArea(data, start, end, dim) > 0)) {
46
- 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);
47
47
  } else {
48
- 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);
49
49
  }
50
50
 
51
51
  if (last && equals(last, last.next)) {
@@ -61,7 +61,7 @@ function filterPoints(start, end) {
61
61
  if (!start) return start;
62
62
  if (!end) end = start;
63
63
 
64
- var p = start,
64
+ let p = start,
65
65
  again;
66
66
  do {
67
67
  again = false;
@@ -87,19 +87,15 @@ function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
87
87
  // interlink polygon nodes in z-order
88
88
  if (!pass && invSize) indexCurve(ear, minX, minY, invSize);
89
89
 
90
- var stop = ear,
91
- prev, next;
90
+ let stop = ear;
92
91
 
93
92
  // iterate through ears, slicing them one by one
94
93
  while (ear.prev !== ear.next) {
95
- prev = ear.prev;
96
- next = ear.next;
94
+ const prev = ear.prev;
95
+ const next = ear.next;
97
96
 
98
97
  if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
99
- // cut off the triangle
100
- triangles.push(prev.i / dim);
101
- triangles.push(ear.i / dim);
102
- triangles.push(next.i / dim);
98
+ triangles.push(prev.i, ear.i, next.i); // cut off the triangle
103
99
 
104
100
  removeNode(ear);
105
101
 
@@ -118,12 +114,12 @@ function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
118
114
  if (!pass) {
119
115
  earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
120
116
 
121
- // if this didn't work, try curing all small self-intersections locally
117
+ // if this didn't work, try curing all small self-intersections locally
122
118
  } else if (pass === 1) {
123
- ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
119
+ ear = cureLocalIntersections(filterPoints(ear), triangles);
124
120
  earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
125
121
 
126
- // as a last resort, try splitting the remaining polygon into two
122
+ // as a last resort, try splitting the remaining polygon into two
127
123
  } else if (pass === 2) {
128
124
  splitEarcut(ear, triangles, dim, minX, minY, invSize);
129
125
  }
@@ -135,17 +131,25 @@ function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
135
131
 
136
132
  // check whether a polygon node forms a valid ear with adjacent nodes
137
133
  function isEar(ear) {
138
- var a = ear.prev,
134
+ const a = ear.prev,
139
135
  b = ear,
140
136
  c = ear.next;
141
137
 
142
138
  if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
143
139
 
144
140
  // now make sure we don't have other points inside the potential ear
145
- var p = ear.next.next;
146
-
147
- while (p !== ear.prev) {
148
- if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
141
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
142
+
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);
148
+
149
+ let p = c.next;
150
+ while (p !== a) {
151
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 &&
152
+ pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) &&
149
153
  area(p.prev, p, p.next) >= 0) return false;
150
154
  p = p.next;
151
155
  }
@@ -154,51 +158,49 @@ function isEar(ear) {
154
158
  }
155
159
 
156
160
  function isEarHashed(ear, minX, minY, invSize) {
157
- var a = ear.prev,
161
+ const a = ear.prev,
158
162
  b = ear,
159
163
  c = ear.next;
160
164
 
161
165
  if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
162
166
 
163
- // triangle bbox; min & max are calculated like this for speed
164
- var minTX = a.x < b.x ? (a.x < c.x ? a.x : c.x) : (b.x < c.x ? b.x : c.x),
165
- minTY = a.y < b.y ? (a.y < c.y ? a.y : c.y) : (b.y < c.y ? b.y : c.y),
166
- maxTX = a.x > b.x ? (a.x > c.x ? a.x : c.x) : (b.x > c.x ? b.x : c.x),
167
- maxTY = a.y > b.y ? (a.y > c.y ? a.y : c.y) : (b.y > c.y ? b.y : c.y);
167
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
168
+
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);
168
174
 
169
175
  // z-order range for the current triangle bbox;
170
- var minZ = zOrder(minTX, minTY, minX, minY, invSize),
171
- maxZ = zOrder(maxTX, maxTY, minX, minY, invSize);
176
+ const minZ = zOrder(x0, y0, minX, minY, invSize),
177
+ maxZ = zOrder(x1, y1, minX, minY, invSize);
172
178
 
173
- var p = ear.prevZ,
179
+ let p = ear.prevZ,
174
180
  n = ear.nextZ;
175
181
 
176
182
  // look for points inside the triangle in both directions
177
183
  while (p && p.z >= minZ && n && n.z <= maxZ) {
178
- if (p !== ear.prev && p !== ear.next &&
179
- pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
180
- area(p.prev, p, p.next) >= 0) return false;
184
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
185
+ pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
181
186
  p = p.prevZ;
182
187
 
183
- if (n !== ear.prev && n !== ear.next &&
184
- pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) &&
185
- area(n.prev, n, n.next) >= 0) return false;
188
+ if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
189
+ pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
186
190
  n = n.nextZ;
187
191
  }
188
192
 
189
193
  // look for remaining points in decreasing z-order
190
194
  while (p && p.z >= minZ) {
191
- if (p !== ear.prev && p !== ear.next &&
192
- pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
193
- area(p.prev, p, p.next) >= 0) return false;
195
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
196
+ pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
194
197
  p = p.prevZ;
195
198
  }
196
199
 
197
200
  // look for remaining points in increasing z-order
198
201
  while (n && n.z <= maxZ) {
199
- if (n !== ear.prev && n !== ear.next &&
200
- pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) &&
201
- area(n.prev, n, n.next) >= 0) return false;
202
+ if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
203
+ pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
202
204
  n = n.nextZ;
203
205
  }
204
206
 
@@ -206,17 +208,15 @@ function isEarHashed(ear, minX, minY, invSize) {
206
208
  }
207
209
 
208
210
  // go through all polygon nodes and cure small local self-intersections
209
- function cureLocalIntersections(start, triangles, dim) {
210
- var p = start;
211
+ function cureLocalIntersections(start, triangles) {
212
+ let p = start;
211
213
  do {
212
- var a = p.prev,
214
+ const a = p.prev,
213
215
  b = p.next.next;
214
216
 
215
217
  if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
216
218
 
217
- triangles.push(a.i / dim);
218
- triangles.push(p.i / dim);
219
- triangles.push(b.i / dim);
219
+ triangles.push(a.i, p.i, b.i);
220
220
 
221
221
  // remove two nodes involved
222
222
  removeNode(p);
@@ -233,21 +233,21 @@ function cureLocalIntersections(start, triangles, dim) {
233
233
  // try splitting polygon into two and triangulate them independently
234
234
  function splitEarcut(start, triangles, dim, minX, minY, invSize) {
235
235
  // look for a valid diagonal that divides the polygon into two
236
- var a = start;
236
+ let a = start;
237
237
  do {
238
- var b = a.next.next;
238
+ let b = a.next.next;
239
239
  while (b !== a.prev) {
240
240
  if (a.i !== b.i && isValidDiagonal(a, b)) {
241
241
  // split the polygon in two by the diagonal
242
- var c = splitPolygon(a, b);
242
+ let c = splitPolygon(a, b);
243
243
 
244
244
  // filter colinear points around the cuts
245
245
  a = filterPoints(a, a.next);
246
246
  c = filterPoints(c, c.next);
247
247
 
248
248
  // run earcut on each half
249
- earcutLinked(a, triangles, dim, minX, minY, invSize);
250
- earcutLinked(c, triangles, dim, minX, minY, invSize);
249
+ earcutLinked(a, triangles, dim, minX, minY, invSize, 0);
250
+ earcutLinked(c, triangles, dim, minX, minY, invSize, 0);
251
251
  return;
252
252
  }
253
253
  b = b.next;
@@ -258,64 +258,75 @@ function splitEarcut(start, triangles, dim, minX, minY, invSize) {
258
258
 
259
259
  // link every hole into the outer loop, producing a single-ring polygon without holes
260
260
  function eliminateHoles(data, holeIndices, outerNode, dim) {
261
- var queue = [],
262
- i, len, start, end, list;
261
+ const queue = [];
263
262
 
264
- for (i = 0, len = holeIndices.length; i < len; i++) {
265
- start = holeIndices[i] * dim;
266
- end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
267
- 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);
268
267
  if (list === list.next) list.steiner = true;
269
268
  queue.push(getLeftmost(list));
270
269
  }
271
270
 
272
- queue.sort(compareX);
271
+ queue.sort(compareXYSlope);
273
272
 
274
273
  // process holes from left to right
275
- for (i = 0; i < queue.length; i++) {
276
- eliminateHole(queue[i], outerNode);
277
- outerNode = filterPoints(outerNode, outerNode.next);
274
+ for (let i = 0; i < queue.length; i++) {
275
+ outerNode = eliminateHole(queue[i], outerNode);
278
276
  }
279
277
 
280
278
  return outerNode;
281
279
  }
282
280
 
283
- function compareX(a, b) {
284
- 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;
285
294
  }
286
295
 
287
296
  // find a bridge between vertices that connects hole with an outer ring and and link it
288
297
  function eliminateHole(hole, outerNode) {
289
- outerNode = findHoleBridge(hole, outerNode);
290
- if (outerNode) {
291
- var b = splitPolygon(outerNode, hole);
292
-
293
- // filter collinear points around the cuts
294
- filterPoints(outerNode, outerNode.next);
295
- filterPoints(b, b.next);
298
+ const bridge = findHoleBridge(hole, outerNode);
299
+ if (!bridge) {
300
+ return outerNode;
296
301
  }
302
+
303
+ const bridgeReverse = splitPolygon(bridge, hole);
304
+
305
+ // filter collinear points around the cuts
306
+ filterPoints(bridgeReverse, bridgeReverse.next);
307
+ return filterPoints(bridge, bridge.next);
297
308
  }
298
309
 
299
310
  // David Eberly's algorithm for finding a bridge between hole and outer polygon
300
311
  function findHoleBridge(hole, outerNode) {
301
- var p = outerNode,
302
- hx = hole.x,
303
- hy = hole.y,
304
- qx = -Infinity,
305
- m;
312
+ let p = outerNode;
313
+ const hx = hole.x;
314
+ const hy = hole.y;
315
+ let qx = -Infinity;
316
+ let m;
306
317
 
307
318
  // find a segment intersected by a ray from the hole's leftmost point to the left;
308
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;
309
322
  do {
310
- if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
311
- 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);
312
326
  if (x <= hx && x > qx) {
313
327
  qx = x;
314
- if (x === hx) {
315
- if (hy === p.y) return p;
316
- if (hy === p.next.y) return p.next;
317
- }
318
328
  m = p.x < p.next.x ? p : p.next;
329
+ if (x === hx) return m; // hole touches outer segment; pick leftmost endpoint
319
330
  }
320
331
  }
321
332
  p = p.next;
@@ -323,25 +334,22 @@ function findHoleBridge(hole, outerNode) {
323
334
 
324
335
  if (!m) return null;
325
336
 
326
- if (hx === qx) return m; // hole touches outer segment; pick leftmost endpoint
327
-
328
337
  // look for points inside the triangle of hole point, segment intersection and endpoint;
329
338
  // if there are no points found, we have a valid connection;
330
339
  // otherwise choose the point of the minimum angle with the ray as connection point
331
340
 
332
- var stop = m,
333
- mx = m.x,
334
- my = m.y,
335
- tanMin = Infinity,
336
- tan;
341
+ const stop = m;
342
+ const mx = m.x;
343
+ const my = m.y;
344
+ let tanMin = Infinity;
337
345
 
338
346
  p = m;
339
347
 
340
348
  do {
341
349
  if (hx >= p.x && p.x >= mx && hx !== p.x &&
342
- pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
350
+ pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
343
351
 
344
- tan = Math.abs(hy - p.y) / (hx - p.x); // tangential
352
+ const tan = Math.abs(hy - p.y) / (hx - p.x); // tangential
345
353
 
346
354
  if (locallyInside(p, hole) &&
347
355
  (tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) {
@@ -363,9 +371,9 @@ function sectorContainsSector(m, p) {
363
371
 
364
372
  // interlink polygon nodes in z-order
365
373
  function indexCurve(start, minX, minY, invSize) {
366
- var p = start;
374
+ let p = start;
367
375
  do {
368
- if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize);
376
+ if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize);
369
377
  p.prevZ = p.prev;
370
378
  p.nextZ = p.next;
371
379
  p = p.next;
@@ -380,25 +388,26 @@ function indexCurve(start, minX, minY, invSize) {
380
388
  // Simon Tatham's linked list merge sort algorithm
381
389
  // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
382
390
  function sortLinked(list) {
383
- var i, p, q, e, tail, numMerges, pSize, qSize,
384
- inSize = 1;
391
+ let numMerges;
392
+ let inSize = 1;
385
393
 
386
394
  do {
387
- p = list;
395
+ let p = list;
396
+ let e;
388
397
  list = null;
389
- tail = null;
398
+ let tail = null;
390
399
  numMerges = 0;
391
400
 
392
401
  while (p) {
393
402
  numMerges++;
394
- q = p;
395
- pSize = 0;
396
- for (i = 0; i < inSize; i++) {
403
+ let q = p;
404
+ let pSize = 0;
405
+ for (let i = 0; i < inSize; i++) {
397
406
  pSize++;
398
407
  q = q.nextZ;
399
408
  if (!q) break;
400
409
  }
401
- qSize = inSize;
410
+ let qSize = inSize;
402
411
 
403
412
  while (pSize > 0 || (qSize > 0 && q)) {
404
413
 
@@ -433,8 +442,8 @@ function sortLinked(list) {
433
442
  // z-order of a point given coords and inverse of the longer side of data bbox
434
443
  function zOrder(x, y, minX, minY, invSize) {
435
444
  // coords are transformed into non-negative 15-bit integer range
436
- x = 32767 * (x - minX) * invSize;
437
- y = 32767 * (y - minY) * invSize;
445
+ x = (x - minX) * invSize | 0;
446
+ y = (y - minY) * invSize | 0;
438
447
 
439
448
  x = (x | (x << 8)) & 0x00FF00FF;
440
449
  x = (x | (x << 4)) & 0x0F0F0F0F;
@@ -451,7 +460,7 @@ function zOrder(x, y, minX, minY, invSize) {
451
460
 
452
461
  // find the leftmost node of a polygon ring
453
462
  function getLeftmost(start) {
454
- var p = start,
463
+ let p = start,
455
464
  leftmost = start;
456
465
  do {
457
466
  if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) leftmost = p;
@@ -463,15 +472,20 @@ function getLeftmost(start) {
463
472
 
464
473
  // check if a point lies within a convex triangle
465
474
  function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
466
- return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 &&
467
- (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 &&
468
- (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0;
475
+ return (cx - px) * (ay - py) >= (ax - px) * (cy - py) &&
476
+ (ax - px) * (by - py) >= (bx - px) * (ay - py) &&
477
+ (bx - px) * (cy - py) >= (cx - px) * (by - py);
478
+ }
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);
469
483
  }
470
484
 
471
485
  // check if a diagonal between two polygon nodes is valid (lies in polygon interior)
472
486
  function isValidDiagonal(a, b) {
473
487
  return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges
474
- (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
488
+ (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
475
489
  (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
476
490
  equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case
477
491
  }
@@ -488,10 +502,10 @@ function equals(p1, p2) {
488
502
 
489
503
  // check if two segments intersect
490
504
  function intersects(p1, q1, p2, q2) {
491
- var o1 = sign(area(p1, q1, p2));
492
- var o2 = sign(area(p1, q1, q2));
493
- var o3 = sign(area(p2, q2, p1));
494
- 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));
495
509
 
496
510
  if (o1 !== o2 && o3 !== o4) return true; // general case
497
511
 
@@ -514,10 +528,10 @@ function sign(num) {
514
528
 
515
529
  // check if a polygon diagonal intersects any polygon segments
516
530
  function intersectsPolygon(a, b) {
517
- var p = a;
531
+ let p = a;
518
532
  do {
519
533
  if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&
520
- intersects(p, p.next, a, b)) return true;
534
+ intersects(p, p.next, a, b)) return true;
521
535
  p = p.next;
522
536
  } while (p !== a);
523
537
 
@@ -533,13 +547,13 @@ function locallyInside(a, b) {
533
547
 
534
548
  // check if the middle point of a polygon diagonal is inside the polygon
535
549
  function middleInside(a, b) {
536
- var p = a,
537
- inside = false,
538
- px = (a.x + b.x) / 2,
539
- 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;
540
554
  do {
541
555
  if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y &&
542
- (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x))
556
+ (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x))
543
557
  inside = !inside;
544
558
  p = p.next;
545
559
  } while (p !== a);
@@ -550,8 +564,8 @@ function middleInside(a, b) {
550
564
  // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;
551
565
  // if one belongs to the outer ring and another to a hole, it merges it into a single ring
552
566
  function splitPolygon(a, b) {
553
- var a2 = new Node(a.i, a.x, a.y),
554
- 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),
555
569
  an = a.next,
556
570
  bp = b.prev;
557
571
 
@@ -572,7 +586,7 @@ function splitPolygon(a, b) {
572
586
 
573
587
  // create a node and optionally link it with previous one (in a circular doubly linked list)
574
588
  function insertNode(i, x, y, last) {
575
- var p = new Node(i, x, y);
589
+ const p = createNode(i, x, y);
576
590
 
577
591
  if (!last) {
578
592
  p.prev = p;
@@ -595,49 +609,39 @@ function removeNode(p) {
595
609
  if (p.nextZ) p.nextZ.prevZ = p.prevZ;
596
610
  }
597
611
 
598
- function Node(i, x, y) {
599
- // vertex index in coordinates array
600
- this.i = i;
601
-
602
- // vertex coordinates
603
- this.x = x;
604
- this.y = y;
605
-
606
- // previous and next vertex nodes in a polygon ring
607
- this.prev = null;
608
- this.next = null;
609
-
610
- // z-order curve value
611
- this.z = null;
612
-
613
- // previous and next nodes in z-order
614
- this.prevZ = null;
615
- this.nextZ = null;
616
-
617
- // indicates whether this is a steiner point
618
- 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
+ };
619
623
  }
620
624
 
621
625
  // return a percentage difference between the polygon area and its triangulation area;
622
626
  // used to verify correctness of triangulation
623
- earcut.deviation = function (data, holeIndices, dim, triangles) {
624
- var hasHoles = holeIndices && holeIndices.length;
625
- 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;
626
630
 
627
- var polygonArea = Math.abs(signedArea(data, 0, outerLen, dim));
631
+ let polygonArea = Math.abs(signedArea(data, 0, outerLen, dim));
628
632
  if (hasHoles) {
629
- for (var i = 0, len = holeIndices.length; i < len; i++) {
630
- var start = holeIndices[i] * dim;
631
- 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;
632
636
  polygonArea -= Math.abs(signedArea(data, start, end, dim));
633
637
  }
634
638
  }
635
639
 
636
- var trianglesArea = 0;
637
- for (i = 0; i < triangles.length; i += 3) {
638
- var a = triangles[i] * dim;
639
- var b = triangles[i + 1] * dim;
640
- 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;
641
645
  trianglesArea += Math.abs(
642
646
  (data[a] - data[c]) * (data[b + 1] - data[a + 1]) -
643
647
  (data[a] - data[b]) * (data[c + 1] - data[a + 1]));
@@ -645,11 +649,11 @@ earcut.deviation = function (data, holeIndices, dim, triangles) {
645
649
 
646
650
  return polygonArea === 0 && trianglesArea === 0 ? 0 :
647
651
  Math.abs((trianglesArea - polygonArea) / polygonArea);
648
- };
652
+ }
649
653
 
650
654
  function signedArea(data, start, end, dim) {
651
- var sum = 0;
652
- 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) {
653
657
  sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
654
658
  j = i;
655
659
  }
@@ -657,21 +661,22 @@ function signedArea(data, start, end, dim) {
657
661
  }
658
662
 
659
663
  // turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts
660
- earcut.flatten = function (data) {
661
- var dim = data[0][0].length,
662
- result = {vertices: [], holes: [], dimensions: dim},
663
- holeIndex = 0;
664
-
665
- for (var i = 0; i < data.length; i++) {
666
- for (var j = 0; j < data[i].length; j++) {
667
- 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]);
668
674
  }
669
- if (i > 0) {
670
- holeIndex += data[i - 1].length;
671
- result.holes.push(holeIndex);
675
+ if (prevLen) {
676
+ holeIndex += prevLen;
677
+ holes.push(holeIndex);
672
678
  }
679
+ prevLen = ring.length;
673
680
  }
674
- return result;
675
- };
676
-
677
- export {earcut};
681
+ return {vertices, holes, dimensions};
682
+ }