earcut 3.0.0 → 3.0.2
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/LICENSE +1 -1
- package/README.md +1 -0
- package/dist/earcut.dev.js +44 -25
- package/dist/earcut.min.js +1 -1
- package/package.json +4 -7
- package/src/earcut.js +44 -25
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -118,3 +118,4 @@ Alternatively, there's a UMD browser bundle with an `earcut` global variable (ex
|
|
|
118
118
|
- [the3deers/earcut-java](https://github.com/the3deers/earcut-java) (Java)
|
|
119
119
|
- [Larpon/earcut](https://github.com/Larpon/earcut) (V)
|
|
120
120
|
- [Cawfree/earcut-j](https://github.com/Cawfree/earcut-j) (Java, outdated)
|
|
121
|
+
- [measuredweighed/SwiftEarcut](https://github.com/measuredweighed/SwiftEarcut) (Swift)
|
package/dist/earcut.dev.js
CHANGED
|
@@ -19,10 +19,10 @@ function earcut(data, holeIndices, dim = 2) {
|
|
|
19
19
|
|
|
20
20
|
// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
|
|
21
21
|
if (data.length > 80 * dim) {
|
|
22
|
-
minX =
|
|
23
|
-
minY =
|
|
24
|
-
let maxX =
|
|
25
|
-
let maxY =
|
|
22
|
+
minX = data[0];
|
|
23
|
+
minY = data[1];
|
|
24
|
+
let maxX = minX;
|
|
25
|
+
let maxY = minY;
|
|
26
26
|
|
|
27
27
|
for (let i = dim; i < outerLen; i += dim) {
|
|
28
28
|
const x = data[i];
|
|
@@ -145,16 +145,16 @@ function isEar(ear) {
|
|
|
145
145
|
// now make sure we don't have other points inside the potential ear
|
|
146
146
|
const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
|
|
147
147
|
|
|
148
|
-
// triangle bbox
|
|
149
|
-
const x0 =
|
|
150
|
-
y0 =
|
|
151
|
-
x1 =
|
|
152
|
-
y1 =
|
|
148
|
+
// triangle bbox
|
|
149
|
+
const x0 = Math.min(ax, bx, cx),
|
|
150
|
+
y0 = Math.min(ay, by, cy),
|
|
151
|
+
x1 = Math.max(ax, bx, cx),
|
|
152
|
+
y1 = Math.max(ay, by, cy);
|
|
153
153
|
|
|
154
154
|
let p = c.next;
|
|
155
155
|
while (p !== a) {
|
|
156
156
|
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 &&
|
|
157
|
-
|
|
157
|
+
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) &&
|
|
158
158
|
area(p.prev, p, p.next) >= 0) return false;
|
|
159
159
|
p = p.next;
|
|
160
160
|
}
|
|
@@ -171,11 +171,11 @@ function isEarHashed(ear, minX, minY, invSize) {
|
|
|
171
171
|
|
|
172
172
|
const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
|
|
173
173
|
|
|
174
|
-
// triangle bbox
|
|
175
|
-
const x0 =
|
|
176
|
-
y0 =
|
|
177
|
-
x1 =
|
|
178
|
-
y1 =
|
|
174
|
+
// triangle bbox
|
|
175
|
+
const x0 = Math.min(ax, bx, cx),
|
|
176
|
+
y0 = Math.min(ay, by, cy),
|
|
177
|
+
x1 = Math.max(ax, bx, cx),
|
|
178
|
+
y1 = Math.max(ay, by, cy);
|
|
179
179
|
|
|
180
180
|
// z-order range for the current triangle bbox;
|
|
181
181
|
const minZ = zOrder(x0, y0, minX, minY, invSize),
|
|
@@ -187,25 +187,25 @@ function isEarHashed(ear, minX, minY, invSize) {
|
|
|
187
187
|
// look for points inside the triangle in both directions
|
|
188
188
|
while (p && p.z >= minZ && n && n.z <= maxZ) {
|
|
189
189
|
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
|
|
190
|
-
|
|
190
|
+
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
|
|
191
191
|
p = p.prevZ;
|
|
192
192
|
|
|
193
193
|
if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
|
|
194
|
-
|
|
194
|
+
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
|
|
195
195
|
n = n.nextZ;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
// look for remaining points in decreasing z-order
|
|
199
199
|
while (p && p.z >= minZ) {
|
|
200
200
|
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
|
|
201
|
-
|
|
201
|
+
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
|
|
202
202
|
p = p.prevZ;
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
// look for remaining points in increasing z-order
|
|
206
206
|
while (n && n.z <= maxZ) {
|
|
207
207
|
if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
|
|
208
|
-
|
|
208
|
+
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
|
|
209
209
|
n = n.nextZ;
|
|
210
210
|
}
|
|
211
211
|
|
|
@@ -273,7 +273,7 @@ function eliminateHoles(data, holeIndices, outerNode, dim) {
|
|
|
273
273
|
queue.push(getLeftmost(list));
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
-
queue.sort(
|
|
276
|
+
queue.sort(compareXYSlope);
|
|
277
277
|
|
|
278
278
|
// process holes from left to right
|
|
279
279
|
for (let i = 0; i < queue.length; i++) {
|
|
@@ -283,11 +283,22 @@ function eliminateHoles(data, holeIndices, outerNode, dim) {
|
|
|
283
283
|
return outerNode;
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
-
function
|
|
287
|
-
|
|
286
|
+
function compareXYSlope(a, b) {
|
|
287
|
+
let result = a.x - b.x;
|
|
288
|
+
// when the left-most point of 2 holes meet at a vertex, sort the holes counterclockwise so that when we find
|
|
289
|
+
// the bridge to the outer shell is always the point that they meet at.
|
|
290
|
+
if (result === 0) {
|
|
291
|
+
result = a.y - b.y;
|
|
292
|
+
if (result === 0) {
|
|
293
|
+
const aSlope = (a.next.y - a.y) / (a.next.x - a.x);
|
|
294
|
+
const bSlope = (b.next.y - b.y) / (b.next.x - b.x);
|
|
295
|
+
result = aSlope - bSlope;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return result;
|
|
288
299
|
}
|
|
289
300
|
|
|
290
|
-
// find a bridge between vertices that connects hole with an outer ring and
|
|
301
|
+
// find a bridge between vertices that connects hole with an outer ring and link it
|
|
291
302
|
function eliminateHole(hole, outerNode) {
|
|
292
303
|
const bridge = findHoleBridge(hole, outerNode);
|
|
293
304
|
if (!bridge) {
|
|
@@ -311,8 +322,11 @@ function findHoleBridge(hole, outerNode) {
|
|
|
311
322
|
|
|
312
323
|
// find a segment intersected by a ray from the hole's leftmost point to the left;
|
|
313
324
|
// segment's endpoint with lesser x will be potential connection point
|
|
325
|
+
// unless they intersect at a vertex, then choose the vertex
|
|
326
|
+
if (equals(hole, p)) return p;
|
|
314
327
|
do {
|
|
315
|
-
if (
|
|
328
|
+
if (equals(hole, p.next)) return p.next;
|
|
329
|
+
else if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
|
|
316
330
|
const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
|
|
317
331
|
if (x <= hx && x > qx) {
|
|
318
332
|
qx = x;
|
|
@@ -468,9 +482,14 @@ function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
|
|
|
468
482
|
(bx - px) * (cy - py) >= (cx - px) * (by - py);
|
|
469
483
|
}
|
|
470
484
|
|
|
485
|
+
// check if a point lies within a convex triangle but false if its equal to the first point of the triangle
|
|
486
|
+
function pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, px, py) {
|
|
487
|
+
return !(ax === px && ay === py) && pointInTriangle(ax, ay, bx, by, cx, cy, px, py);
|
|
488
|
+
}
|
|
489
|
+
|
|
471
490
|
// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
|
|
472
491
|
function isValidDiagonal(a, b) {
|
|
473
|
-
return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && //
|
|
492
|
+
return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // doesn't intersect other edges
|
|
474
493
|
(locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
|
|
475
494
|
(area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
|
|
476
495
|
equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case
|
package/dist/earcut.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).earcut={})}(this,
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).earcut={})}(this,function(e){"use strict";function t(e,t,n,r,x){let i;if(x===k(e,t,n,r)>0)for(let x=t;x<n;x+=r)i=w(x/r|0,e[x],e[x+1],i);else for(let x=n-r;x>=t;x-=r)i=w(x/r|0,e[x],e[x+1],i);return i&&Z(i,i.next)&&(z(i),i=i.next),i}function n(e,t){if(!e)return e;t||(t=e);let n,r=e;do{if(n=!1,r.steiner||!Z(r,r.next)&&0!==a(r.prev,r,r.next))r=r.next;else{if(z(r),r=t=r.prev,r===r.next)break;n=!0}}while(n||r!==t);return t}function r(e,t,f,l,c,p,s){if(!e)return;!s&&p&&function(e,t,n,r){let x=e;do{0===x.z&&(x.z=y(x.x,x.y,t,n,r)),x.prevZ=x.prev,x.nextZ=x.next,x=x.next}while(x!==e);x.prevZ.nextZ=null,x.prevZ=null,function(e){let t,n=1;do{let r,x=e;e=null;let i=null;for(t=0;x;){t++;let o=x,u=0;for(let e=0;e<n&&(u++,o=o.nextZ,o);e++);let f=n;for(;u>0||f>0&&o;)0!==u&&(0===f||!o||x.z<=o.z)?(r=x,x=x.nextZ,u--):(r=o,o=o.nextZ,f--),i?i.nextZ=r:e=r,r.prevZ=i,i=r;x=o}i.nextZ=null,n*=2}while(t>1)}(x)}(e,l,c,p);let v=e;for(;e.prev!==e.next;){const y=e.prev,h=e.next;if(p?i(e,l,c,p):x(e))t.push(y.i,e.i,h.i),z(e),e=h.next,v=h.next;else if((e=h)===v){s?1===s?r(e=o(n(e),t),t,f,l,c,p,2):2===s&&u(e,t,f,l,c,p):r(n(e),t,f,l,c,p,1);break}}}function x(e){const t=e.prev,n=e,r=e.next;if(a(t,n,r)>=0)return!1;const x=t.x,i=n.x,o=r.x,u=t.y,f=n.y,l=r.y,c=Math.min(x,i,o),y=Math.min(u,f,l),p=Math.max(x,i,o),s=Math.max(u,f,l);let h=r.next;for(;h!==t;){if(h.x>=c&&h.x<=p&&h.y>=y&&h.y<=s&&v(x,u,i,f,o,l,h.x,h.y)&&a(h.prev,h,h.next)>=0)return!1;h=h.next}return!0}function i(e,t,n,r){const x=e.prev,i=e,o=e.next;if(a(x,i,o)>=0)return!1;const u=x.x,f=i.x,l=o.x,c=x.y,p=i.y,s=o.y,h=Math.min(u,f,l),Z=Math.min(c,p,s),d=Math.max(u,f,l),M=Math.max(c,p,s),m=y(h,Z,t,n,r),g=y(d,M,t,n,r);let b=e.prevZ,w=e.nextZ;for(;b&&b.z>=m&&w&&w.z<=g;){if(b.x>=h&&b.x<=d&&b.y>=Z&&b.y<=M&&b!==x&&b!==o&&v(u,c,f,p,l,s,b.x,b.y)&&a(b.prev,b,b.next)>=0)return!1;if(b=b.prevZ,w.x>=h&&w.x<=d&&w.y>=Z&&w.y<=M&&w!==x&&w!==o&&v(u,c,f,p,l,s,w.x,w.y)&&a(w.prev,w,w.next)>=0)return!1;w=w.nextZ}for(;b&&b.z>=m;){if(b.x>=h&&b.x<=d&&b.y>=Z&&b.y<=M&&b!==x&&b!==o&&v(u,c,f,p,l,s,b.x,b.y)&&a(b.prev,b,b.next)>=0)return!1;b=b.prevZ}for(;w&&w.z<=g;){if(w.x>=h&&w.x<=d&&w.y>=Z&&w.y<=M&&w!==x&&w!==o&&v(u,c,f,p,l,s,w.x,w.y)&&a(w.prev,w,w.next)>=0)return!1;w=w.nextZ}return!0}function o(e,t){let r=e;do{const n=r.prev,x=r.next.next;!Z(n,x)&&d(n,r,r.next,x)&&g(n,x)&&g(x,n)&&(t.push(n.i,r.i,x.i),z(r),z(r.next),r=e=x),r=r.next}while(r!==e);return n(r)}function u(e,t,x,i,o,u){let f=e;do{let e=f.next.next;for(;e!==f.prev;){if(f.i!==e.i&&h(f,e)){let l=b(f,e);return f=n(f,f.next),l=n(l,l.next),r(f,t,x,i,o,u,0),void r(l,t,x,i,o,u,0)}e=e.next}f=f.next}while(f!==e)}function f(e,t){let n=e.x-t.x;if(0===n&&(n=e.y-t.y,0===n)){n=(e.next.y-e.y)/(e.next.x-e.x)-(t.next.y-t.y)/(t.next.x-t.x)}return n}function l(e,t){const r=function(e,t){let n=t;const r=e.x,x=e.y;let i,o=-1/0;if(Z(e,n))return n;do{if(Z(e,n.next))return n.next;if(x<=n.y&&x>=n.next.y&&n.next.y!==n.y){const e=n.x+(x-n.y)*(n.next.x-n.x)/(n.next.y-n.y);if(e<=r&&e>o&&(o=e,i=n.x<n.next.x?n:n.next,e===r))return i}n=n.next}while(n!==t);if(!i)return null;const u=i,f=i.x,l=i.y;let y=1/0;n=i;do{if(r>=n.x&&n.x>=f&&r!==n.x&&s(x<l?r:o,x,f,l,x<l?o:r,x,n.x,n.y)){const t=Math.abs(x-n.y)/(r-n.x);g(n,e)&&(t<y||t===y&&(n.x>i.x||n.x===i.x&&c(i,n)))&&(i=n,y=t)}n=n.next}while(n!==u);return i}(e,t);if(!r)return t;const x=b(r,e);return n(x,x.next),n(r,r.next)}function c(e,t){return a(e.prev,e,t.prev)<0&&a(t.next,e,e.next)<0}function y(e,t,n,r,x){return(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e=(e-n)*x|0)|e<<8))|e<<4))|e<<2))|e<<1))|(t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t=(t-r)*x|0)|t<<8))|t<<4))|t<<2))|t<<1))<<1}function p(e){let t=e,n=e;do{(t.x<n.x||t.x===n.x&&t.y<n.y)&&(n=t),t=t.next}while(t!==e);return n}function s(e,t,n,r,x,i,o,u){return(x-o)*(t-u)>=(e-o)*(i-u)&&(e-o)*(r-u)>=(n-o)*(t-u)&&(n-o)*(i-u)>=(x-o)*(r-u)}function v(e,t,n,r,x,i,o,u){return!(e===o&&t===u)&&s(e,t,n,r,x,i,o,u)}function h(e,t){return e.next.i!==t.i&&e.prev.i!==t.i&&!function(e,t){let n=e;do{if(n.i!==e.i&&n.next.i!==e.i&&n.i!==t.i&&n.next.i!==t.i&&d(n,n.next,e,t))return!0;n=n.next}while(n!==e);return!1}(e,t)&&(g(e,t)&&g(t,e)&&function(e,t){let n=e,r=!1;const x=(e.x+t.x)/2,i=(e.y+t.y)/2;do{n.y>i!=n.next.y>i&&n.next.y!==n.y&&x<(n.next.x-n.x)*(i-n.y)/(n.next.y-n.y)+n.x&&(r=!r),n=n.next}while(n!==e);return r}(e,t)&&(a(e.prev,e,t.prev)||a(e,t.prev,t))||Z(e,t)&&a(e.prev,e,e.next)>0&&a(t.prev,t,t.next)>0)}function a(e,t,n){return(t.y-e.y)*(n.x-t.x)-(t.x-e.x)*(n.y-t.y)}function Z(e,t){return e.x===t.x&&e.y===t.y}function d(e,t,n,r){const x=m(a(e,t,n)),i=m(a(e,t,r)),o=m(a(n,r,e)),u=m(a(n,r,t));return x!==i&&o!==u||(!(0!==x||!M(e,n,t))||(!(0!==i||!M(e,r,t))||(!(0!==o||!M(n,e,r))||!(0!==u||!M(n,t,r)))))}function M(e,t,n){return t.x<=Math.max(e.x,n.x)&&t.x>=Math.min(e.x,n.x)&&t.y<=Math.max(e.y,n.y)&&t.y>=Math.min(e.y,n.y)}function m(e){return e>0?1:e<0?-1:0}function g(e,t){return a(e.prev,e,e.next)<0?a(e,t,e.next)>=0&&a(e,e.prev,t)>=0:a(e,t,e.prev)<0||a(e,e.next,t)<0}function b(e,t){const n=j(e.i,e.x,e.y),r=j(t.i,t.x,t.y),x=e.next,i=t.prev;return e.next=t,t.prev=e,n.next=x,x.prev=n,r.next=n,n.prev=r,i.next=r,r.prev=i,r}function w(e,t,n,r){const x=j(e,t,n);return r?(x.next=r.next,x.prev=r,r.next.prev=x,r.next=x):(x.prev=x,x.next=x),x}function z(e){e.next.prev=e.prev,e.prev.next=e.next,e.prevZ&&(e.prevZ.nextZ=e.nextZ),e.nextZ&&(e.nextZ.prevZ=e.prevZ)}function j(e,t,n){return{i:e,x:t,y:n,prev:null,next:null,z:0,prevZ:null,nextZ:null,steiner:!1}}function k(e,t,n,r){let x=0;for(let i=t,o=n-r;i<n;i+=r)x+=(e[o]-e[i])*(e[i+1]+e[o+1]),o=i;return x}e.default=function(e,n,x=2){const i=n&&n.length,o=i?n[0]*x:e.length;let u=t(e,0,o,x,!0);const c=[];if(!u||u.next===u.prev)return c;let y,s,v;if(i&&(u=function(e,n,r,x){const i=[];for(let r=0,o=n.length;r<o;r++){const u=t(e,n[r]*x,r<o-1?n[r+1]*x:e.length,x,!1);u===u.next&&(u.steiner=!0),i.push(p(u))}i.sort(f);for(let e=0;e<i.length;e++)r=l(i[e],r);return r}(e,n,u,x)),e.length>80*x){y=e[0],s=e[1];let t=y,n=s;for(let r=x;r<o;r+=x){const x=e[r],i=e[r+1];x<y&&(y=x),i<s&&(s=i),x>t&&(t=x),i>n&&(n=i)}v=Math.max(t-y,n-s),v=0!==v?32767/v:0}return r(u,c,x,y,s,v,0),c},e.deviation=function(e,t,n,r){const x=t&&t.length,i=x?t[0]*n:e.length;let o=Math.abs(k(e,0,i,n));if(x)for(let r=0,x=t.length;r<x;r++){const i=t[r]*n,u=r<x-1?t[r+1]*n:e.length;o-=Math.abs(k(e,i,u,n))}let u=0;for(let t=0;t<r.length;t+=3){const x=r[t]*n,i=r[t+1]*n,o=r[t+2]*n;u+=Math.abs((e[x]-e[o])*(e[i+1]-e[x+1])-(e[x]-e[i])*(e[o+1]-e[x+1]))}return 0===o&&0===u?0:Math.abs((u-o)/o)},e.flatten=function(e){const t=[],n=[],r=e[0][0].length;let x=0,i=0;for(const o of e){for(const e of o)for(let n=0;n<r;n++)t.push(e[n]);i&&(x+=i,n.push(x)),i=o.length}return{vertices:t,holes:n,dimensions:r}},Object.defineProperty(e,"__esModule",{value:!0})});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "earcut",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "The fastest and smallest JavaScript polygon triangulation library for your WebGL apps",
|
|
5
5
|
"main": "src/earcut.js",
|
|
6
6
|
"type": "module",
|
|
@@ -22,12 +22,9 @@
|
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@rollup/plugin-terser": "^0.4.4",
|
|
24
24
|
"benchmark": "^2.1.4",
|
|
25
|
-
"
|
|
26
|
-
"eslint": "^
|
|
27
|
-
"
|
|
28
|
-
"rollup": "^4.18.0",
|
|
29
|
-
"uglify-js": "^3.18.0",
|
|
30
|
-
"watchify": "^4.0.0"
|
|
25
|
+
"eslint": "^9.31.0",
|
|
26
|
+
"eslint-config-mourner": "^4.1.0",
|
|
27
|
+
"rollup": "^4.45.1"
|
|
31
28
|
},
|
|
32
29
|
"eslintConfig": {
|
|
33
30
|
"extends": "mourner",
|
package/src/earcut.js
CHANGED
|
@@ -14,10 +14,10 @@ export default function earcut(data, holeIndices, dim = 2) {
|
|
|
14
14
|
|
|
15
15
|
// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
|
|
16
16
|
if (data.length > 80 * dim) {
|
|
17
|
-
minX =
|
|
18
|
-
minY =
|
|
19
|
-
let maxX =
|
|
20
|
-
let maxY =
|
|
17
|
+
minX = data[0];
|
|
18
|
+
minY = data[1];
|
|
19
|
+
let maxX = minX;
|
|
20
|
+
let maxY = minY;
|
|
21
21
|
|
|
22
22
|
for (let i = dim; i < outerLen; i += dim) {
|
|
23
23
|
const x = data[i];
|
|
@@ -140,16 +140,16 @@ function isEar(ear) {
|
|
|
140
140
|
// now make sure we don't have other points inside the potential ear
|
|
141
141
|
const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
|
|
142
142
|
|
|
143
|
-
// triangle bbox
|
|
144
|
-
const x0 =
|
|
145
|
-
y0 =
|
|
146
|
-
x1 =
|
|
147
|
-
y1 =
|
|
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
148
|
|
|
149
149
|
let p = c.next;
|
|
150
150
|
while (p !== a) {
|
|
151
151
|
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 &&
|
|
152
|
-
|
|
152
|
+
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) &&
|
|
153
153
|
area(p.prev, p, p.next) >= 0) return false;
|
|
154
154
|
p = p.next;
|
|
155
155
|
}
|
|
@@ -166,11 +166,11 @@ function isEarHashed(ear, minX, minY, invSize) {
|
|
|
166
166
|
|
|
167
167
|
const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
|
|
168
168
|
|
|
169
|
-
// triangle bbox
|
|
170
|
-
const x0 =
|
|
171
|
-
y0 =
|
|
172
|
-
x1 =
|
|
173
|
-
y1 =
|
|
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);
|
|
174
174
|
|
|
175
175
|
// z-order range for the current triangle bbox;
|
|
176
176
|
const minZ = zOrder(x0, y0, minX, minY, invSize),
|
|
@@ -182,25 +182,25 @@ function isEarHashed(ear, minX, minY, invSize) {
|
|
|
182
182
|
// look for points inside the triangle in both directions
|
|
183
183
|
while (p && p.z >= minZ && n && n.z <= maxZ) {
|
|
184
184
|
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
|
|
185
|
-
|
|
185
|
+
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
|
|
186
186
|
p = p.prevZ;
|
|
187
187
|
|
|
188
188
|
if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
|
|
189
|
-
|
|
189
|
+
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
|
|
190
190
|
n = n.nextZ;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
// look for remaining points in decreasing z-order
|
|
194
194
|
while (p && p.z >= minZ) {
|
|
195
195
|
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&
|
|
196
|
-
|
|
196
|
+
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
|
|
197
197
|
p = p.prevZ;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
// look for remaining points in increasing z-order
|
|
201
201
|
while (n && n.z <= maxZ) {
|
|
202
202
|
if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&
|
|
203
|
-
|
|
203
|
+
pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
|
|
204
204
|
n = n.nextZ;
|
|
205
205
|
}
|
|
206
206
|
|
|
@@ -268,7 +268,7 @@ function eliminateHoles(data, holeIndices, outerNode, dim) {
|
|
|
268
268
|
queue.push(getLeftmost(list));
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
queue.sort(
|
|
271
|
+
queue.sort(compareXYSlope);
|
|
272
272
|
|
|
273
273
|
// process holes from left to right
|
|
274
274
|
for (let i = 0; i < queue.length; i++) {
|
|
@@ -278,11 +278,22 @@ function eliminateHoles(data, holeIndices, outerNode, dim) {
|
|
|
278
278
|
return outerNode;
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
function
|
|
282
|
-
|
|
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;
|
|
283
294
|
}
|
|
284
295
|
|
|
285
|
-
// find a bridge between vertices that connects hole with an outer ring and
|
|
296
|
+
// find a bridge between vertices that connects hole with an outer ring and link it
|
|
286
297
|
function eliminateHole(hole, outerNode) {
|
|
287
298
|
const bridge = findHoleBridge(hole, outerNode);
|
|
288
299
|
if (!bridge) {
|
|
@@ -306,8 +317,11 @@ function findHoleBridge(hole, outerNode) {
|
|
|
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 (
|
|
323
|
+
if (equals(hole, p.next)) return p.next;
|
|
324
|
+
else if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
|
|
311
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;
|
|
@@ -463,9 +477,14 @@ function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
|
|
|
463
477
|
(bx - px) * (cy - py) >= (cx - px) * (by - py);
|
|
464
478
|
}
|
|
465
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
|
+
|
|
466
485
|
// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
|
|
467
486
|
function isValidDiagonal(a, b) {
|
|
468
|
-
return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && //
|
|
487
|
+
return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // doesn't intersect other edges
|
|
469
488
|
(locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
|
|
470
489
|
(area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
|
|
471
490
|
equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case
|