@emasoft/svg-matrix 1.1.0 → 1.2.0
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/bin/svg-matrix.js +7 -6
- package/bin/svgm.js +109 -40
- package/dist/svg-matrix.min.js +7 -7
- package/dist/svg-toolbox.min.js +148 -228
- package/dist/svgm.min.js +152 -232
- package/dist/version.json +5 -5
- package/package.json +1 -1
- package/scripts/postinstall.js +72 -41
- package/scripts/test-postinstall.js +18 -16
- package/scripts/version-sync.js +78 -60
- package/src/animation-optimization.js +190 -98
- package/src/animation-references.js +11 -3
- package/src/arc-length.js +23 -20
- package/src/bezier-analysis.js +9 -13
- package/src/bezier-intersections.js +18 -4
- package/src/browser-verify.js +35 -8
- package/src/clip-path-resolver.js +285 -114
- package/src/convert-path-data.js +20 -8
- package/src/css-specificity.js +33 -9
- package/src/douglas-peucker.js +272 -141
- package/src/geometry-to-path.js +79 -22
- package/src/gjk-collision.js +287 -126
- package/src/index.js +56 -21
- package/src/inkscape-support.js +122 -101
- package/src/logger.js +43 -27
- package/src/marker-resolver.js +201 -121
- package/src/mask-resolver.js +231 -98
- package/src/matrix.js +9 -5
- package/src/mesh-gradient.js +22 -14
- package/src/off-canvas-detection.js +53 -17
- package/src/path-optimization.js +356 -171
- package/src/path-simplification.js +671 -256
- package/src/pattern-resolver.js +1 -3
- package/src/polygon-clip.js +396 -78
- package/src/svg-boolean-ops.js +90 -23
- package/src/svg-collections.js +1546 -667
- package/src/svg-flatten.js +152 -38
- package/src/svg-matrix-lib.js +2 -2
- package/src/svg-parser.js +5 -1
- package/src/svg-rendering-context.js +3 -1
- package/src/svg-toolbox-lib.js +2 -2
- package/src/svg-toolbox.js +99 -457
- package/src/svg-validation-data.js +513 -345
- package/src/svg2-polyfills.js +156 -93
- package/src/svgm-lib.js +8 -4
- package/src/transform-optimization.js +168 -51
- package/src/transforms2d.js +73 -40
- package/src/transforms3d.js +34 -27
- package/src/use-symbol-resolver.js +175 -76
- package/src/vector.js +80 -44
- package/src/vendor/inkscape-hatch-polyfill.js +143 -108
- package/src/vendor/inkscape-hatch-polyfill.min.js +291 -1
- package/src/vendor/inkscape-mesh-polyfill.js +953 -766
- package/src/vendor/inkscape-mesh-polyfill.min.js +896 -1
- package/src/verification.js +3 -4
package/src/gjk-collision.js
CHANGED
|
@@ -29,16 +29,16 @@
|
|
|
29
29
|
* @module gjk-collision
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
|
-
import Decimal from
|
|
32
|
+
import Decimal from "decimal.js";
|
|
33
33
|
|
|
34
34
|
// Set high precision for all calculations
|
|
35
35
|
Decimal.set({ precision: 80 });
|
|
36
36
|
|
|
37
37
|
// Helper to convert to Decimal
|
|
38
|
-
const D = x => (x instanceof Decimal ? x : new Decimal(x));
|
|
38
|
+
const D = (x) => (x instanceof Decimal ? x : new Decimal(x));
|
|
39
39
|
|
|
40
40
|
// Near-zero threshold for comparisons
|
|
41
|
-
const EPSILON = new Decimal(
|
|
41
|
+
const EPSILON = new Decimal("1e-40");
|
|
42
42
|
|
|
43
43
|
// Maximum iterations to prevent infinite loops
|
|
44
44
|
const MAX_ITERATIONS = 100;
|
|
@@ -55,8 +55,10 @@ const MAX_ITERATIONS = 100;
|
|
|
55
55
|
* @throws {TypeError} If x or y is null or undefined
|
|
56
56
|
*/
|
|
57
57
|
export function point(x, y) {
|
|
58
|
-
if (x == null)
|
|
59
|
-
|
|
58
|
+
if (x == null)
|
|
59
|
+
throw new TypeError("point: x coordinate cannot be null or undefined");
|
|
60
|
+
if (y == null)
|
|
61
|
+
throw new TypeError("point: y coordinate cannot be null or undefined");
|
|
60
62
|
return { x: D(x), y: D(y) };
|
|
61
63
|
}
|
|
62
64
|
|
|
@@ -68,8 +70,12 @@ export function point(x, y) {
|
|
|
68
70
|
* @throws {TypeError} If a or b is invalid or missing x/y properties
|
|
69
71
|
*/
|
|
70
72
|
export function vectorAdd(a, b) {
|
|
71
|
-
if (!a || a.x == null || a.y == null)
|
|
72
|
-
|
|
73
|
+
if (!a || a.x == null || a.y == null)
|
|
74
|
+
throw new TypeError("vectorAdd: first vector must have x and y properties");
|
|
75
|
+
if (!b || b.x == null || b.y == null)
|
|
76
|
+
throw new TypeError(
|
|
77
|
+
"vectorAdd: second vector must have x and y properties",
|
|
78
|
+
);
|
|
73
79
|
return { x: a.x.plus(b.x), y: a.y.plus(b.y) };
|
|
74
80
|
}
|
|
75
81
|
|
|
@@ -81,8 +87,12 @@ export function vectorAdd(a, b) {
|
|
|
81
87
|
* @throws {TypeError} If a or b is invalid or missing x/y properties
|
|
82
88
|
*/
|
|
83
89
|
export function vectorSub(a, b) {
|
|
84
|
-
if (!a || a.x == null || a.y == null)
|
|
85
|
-
|
|
90
|
+
if (!a || a.x == null || a.y == null)
|
|
91
|
+
throw new TypeError("vectorSub: first vector must have x and y properties");
|
|
92
|
+
if (!b || b.x == null || b.y == null)
|
|
93
|
+
throw new TypeError(
|
|
94
|
+
"vectorSub: second vector must have x and y properties",
|
|
95
|
+
);
|
|
86
96
|
return { x: a.x.minus(b.x), y: a.y.minus(b.y) };
|
|
87
97
|
}
|
|
88
98
|
|
|
@@ -93,7 +103,8 @@ export function vectorSub(a, b) {
|
|
|
93
103
|
* @throws {TypeError} If v is invalid or missing x/y properties
|
|
94
104
|
*/
|
|
95
105
|
export function vectorNeg(v) {
|
|
96
|
-
if (!v || v.x == null || v.y == null)
|
|
106
|
+
if (!v || v.x == null || v.y == null)
|
|
107
|
+
throw new TypeError("vectorNeg: vector must have x and y properties");
|
|
97
108
|
return { x: v.x.neg(), y: v.y.neg() };
|
|
98
109
|
}
|
|
99
110
|
|
|
@@ -105,8 +116,12 @@ export function vectorNeg(v) {
|
|
|
105
116
|
* @throws {TypeError} If v is invalid, missing x/y properties, or s is null/undefined
|
|
106
117
|
*/
|
|
107
118
|
export function vectorScale(v, s) {
|
|
108
|
-
if (!v || v.x == null || v.y == null)
|
|
109
|
-
|
|
119
|
+
if (!v || v.x == null || v.y == null)
|
|
120
|
+
throw new TypeError("vectorScale: vector must have x and y properties");
|
|
121
|
+
if (s == null)
|
|
122
|
+
throw new TypeError(
|
|
123
|
+
"vectorScale: scale factor cannot be null or undefined",
|
|
124
|
+
);
|
|
110
125
|
const sd = D(s);
|
|
111
126
|
return { x: v.x.mul(sd), y: v.y.mul(sd) };
|
|
112
127
|
}
|
|
@@ -119,8 +134,10 @@ export function vectorScale(v, s) {
|
|
|
119
134
|
* @throws {TypeError} If a or b is invalid or missing x/y properties
|
|
120
135
|
*/
|
|
121
136
|
export function dot(a, b) {
|
|
122
|
-
if (!a || a.x == null || a.y == null)
|
|
123
|
-
|
|
137
|
+
if (!a || a.x == null || a.y == null)
|
|
138
|
+
throw new TypeError("dot: first vector must have x and y properties");
|
|
139
|
+
if (!b || b.x == null || b.y == null)
|
|
140
|
+
throw new TypeError("dot: second vector must have x and y properties");
|
|
124
141
|
return a.x.mul(b.x).plus(a.y.mul(b.y));
|
|
125
142
|
}
|
|
126
143
|
|
|
@@ -132,8 +149,10 @@ export function dot(a, b) {
|
|
|
132
149
|
* @throws {TypeError} If a or b is invalid or missing x/y properties
|
|
133
150
|
*/
|
|
134
151
|
export function cross(a, b) {
|
|
135
|
-
if (!a || a.x == null || a.y == null)
|
|
136
|
-
|
|
152
|
+
if (!a || a.x == null || a.y == null)
|
|
153
|
+
throw new TypeError("cross: first vector must have x and y properties");
|
|
154
|
+
if (!b || b.x == null || b.y == null)
|
|
155
|
+
throw new TypeError("cross: second vector must have x and y properties");
|
|
137
156
|
return a.x.mul(b.y).minus(a.y.mul(b.x));
|
|
138
157
|
}
|
|
139
158
|
|
|
@@ -144,7 +163,10 @@ export function cross(a, b) {
|
|
|
144
163
|
* @throws {TypeError} If v is invalid or missing x/y properties
|
|
145
164
|
*/
|
|
146
165
|
export function magnitudeSquared(v) {
|
|
147
|
-
if (!v || v.x == null || v.y == null)
|
|
166
|
+
if (!v || v.x == null || v.y == null)
|
|
167
|
+
throw new TypeError(
|
|
168
|
+
"magnitudeSquared: vector must have x and y properties",
|
|
169
|
+
);
|
|
148
170
|
return v.x.mul(v.x).plus(v.y.mul(v.y));
|
|
149
171
|
}
|
|
150
172
|
|
|
@@ -155,7 +177,8 @@ export function magnitudeSquared(v) {
|
|
|
155
177
|
* @throws {TypeError} If v is invalid or missing x/y properties
|
|
156
178
|
*/
|
|
157
179
|
export function magnitude(v) {
|
|
158
|
-
if (!v || v.x == null || v.y == null)
|
|
180
|
+
if (!v || v.x == null || v.y == null)
|
|
181
|
+
throw new TypeError("magnitude: vector must have x and y properties");
|
|
159
182
|
return magnitudeSquared(v).sqrt();
|
|
160
183
|
}
|
|
161
184
|
|
|
@@ -166,7 +189,8 @@ export function magnitude(v) {
|
|
|
166
189
|
* @throws {TypeError} If v is invalid or missing x/y properties
|
|
167
190
|
*/
|
|
168
191
|
export function normalize(v) {
|
|
169
|
-
if (!v || v.x == null || v.y == null)
|
|
192
|
+
if (!v || v.x == null || v.y == null)
|
|
193
|
+
throw new TypeError("normalize: vector must have x and y properties");
|
|
170
194
|
const mag = magnitude(v);
|
|
171
195
|
if (mag.lessThan(EPSILON)) {
|
|
172
196
|
return { x: D(0), y: D(0) };
|
|
@@ -181,7 +205,8 @@ export function normalize(v) {
|
|
|
181
205
|
* @throws {TypeError} If v is invalid or missing x/y properties
|
|
182
206
|
*/
|
|
183
207
|
export function perpendicular(v) {
|
|
184
|
-
if (!v || v.x == null || v.y == null)
|
|
208
|
+
if (!v || v.x == null || v.y == null)
|
|
209
|
+
throw new TypeError("perpendicular: vector must have x and y properties");
|
|
185
210
|
return { x: v.y.neg(), y: v.x };
|
|
186
211
|
}
|
|
187
212
|
|
|
@@ -195,9 +220,18 @@ export function perpendicular(v) {
|
|
|
195
220
|
* @throws {TypeError} If any vector is invalid or missing x/y properties
|
|
196
221
|
*/
|
|
197
222
|
export function tripleProduct(a, b, c) {
|
|
198
|
-
if (!a || a.x == null || a.y == null)
|
|
199
|
-
|
|
200
|
-
|
|
223
|
+
if (!a || a.x == null || a.y == null)
|
|
224
|
+
throw new TypeError(
|
|
225
|
+
"tripleProduct: first vector must have x and y properties",
|
|
226
|
+
);
|
|
227
|
+
if (!b || b.x == null || b.y == null)
|
|
228
|
+
throw new TypeError(
|
|
229
|
+
"tripleProduct: second vector must have x and y properties",
|
|
230
|
+
);
|
|
231
|
+
if (!c || c.x == null || c.y == null)
|
|
232
|
+
throw new TypeError(
|
|
233
|
+
"tripleProduct: third vector must have x and y properties",
|
|
234
|
+
);
|
|
201
235
|
// In 2D: (A × B) × C = B(A·C) - A(B·C)
|
|
202
236
|
const ac = dot(a, c);
|
|
203
237
|
const bc = dot(b, c);
|
|
@@ -220,15 +254,19 @@ export function tripleProduct(a, b, c) {
|
|
|
220
254
|
* @throws {TypeError} If polygon is not an array or direction is invalid
|
|
221
255
|
*/
|
|
222
256
|
export function supportPoint(polygon, direction) {
|
|
223
|
-
if (!Array.isArray(polygon))
|
|
224
|
-
|
|
257
|
+
if (!Array.isArray(polygon))
|
|
258
|
+
throw new TypeError("supportPoint: polygon must be an array");
|
|
259
|
+
if (!direction || direction.x == null || direction.y == null)
|
|
260
|
+
throw new TypeError("supportPoint: direction must have x and y properties");
|
|
225
261
|
if (polygon.length === 0) {
|
|
226
|
-
throw new TypeError(
|
|
262
|
+
throw new TypeError("supportPoint: polygon cannot be empty");
|
|
227
263
|
}
|
|
228
264
|
|
|
229
265
|
// Validate first point
|
|
230
266
|
if (!polygon[0] || polygon[0].x == null || polygon[0].y == null) {
|
|
231
|
-
throw new TypeError(
|
|
267
|
+
throw new TypeError(
|
|
268
|
+
"supportPoint: polygon[0] must have x and y properties",
|
|
269
|
+
);
|
|
232
270
|
}
|
|
233
271
|
|
|
234
272
|
let maxDot = dot(polygon[0], direction);
|
|
@@ -236,7 +274,9 @@ export function supportPoint(polygon, direction) {
|
|
|
236
274
|
|
|
237
275
|
for (let i = 1; i < polygon.length; i++) {
|
|
238
276
|
if (!polygon[i] || polygon[i].x == null || polygon[i].y == null) {
|
|
239
|
-
throw new TypeError(
|
|
277
|
+
throw new TypeError(
|
|
278
|
+
`supportPoint: polygon[${i}] must have x and y properties`,
|
|
279
|
+
);
|
|
240
280
|
}
|
|
241
281
|
const d = dot(polygon[i], direction);
|
|
242
282
|
if (d.greaterThan(maxDot)) {
|
|
@@ -261,9 +301,14 @@ export function supportPoint(polygon, direction) {
|
|
|
261
301
|
* @throws {TypeError} If polygons are not arrays or direction is invalid
|
|
262
302
|
*/
|
|
263
303
|
export function minkowskiSupport(polygonA, polygonB, direction) {
|
|
264
|
-
if (!Array.isArray(polygonA))
|
|
265
|
-
|
|
266
|
-
if (!
|
|
304
|
+
if (!Array.isArray(polygonA))
|
|
305
|
+
throw new TypeError("minkowskiSupport: polygonA must be an array");
|
|
306
|
+
if (!Array.isArray(polygonB))
|
|
307
|
+
throw new TypeError("minkowskiSupport: polygonB must be an array");
|
|
308
|
+
if (!direction || direction.x == null || direction.y == null)
|
|
309
|
+
throw new TypeError(
|
|
310
|
+
"minkowskiSupport: direction must have x and y properties",
|
|
311
|
+
);
|
|
267
312
|
const pointA = supportPoint(polygonA, direction);
|
|
268
313
|
const pointB = supportPoint(polygonB, vectorNeg(direction));
|
|
269
314
|
return vectorSub(pointA, pointB);
|
|
@@ -286,11 +331,24 @@ export function minkowskiSupport(polygonA, polygonB, direction) {
|
|
|
286
331
|
* @throws {TypeError} If simplex is not an array, has wrong length, or direction is invalid
|
|
287
332
|
*/
|
|
288
333
|
export function processLineSimplex(simplex, direction) {
|
|
289
|
-
if (!Array.isArray(simplex))
|
|
290
|
-
|
|
291
|
-
if (
|
|
292
|
-
|
|
293
|
-
|
|
334
|
+
if (!Array.isArray(simplex))
|
|
335
|
+
throw new TypeError("processLineSimplex: simplex must be an array");
|
|
336
|
+
if (simplex.length !== 2)
|
|
337
|
+
throw new TypeError(
|
|
338
|
+
"processLineSimplex: simplex must have exactly 2 points",
|
|
339
|
+
);
|
|
340
|
+
if (!direction || direction.x == null || direction.y == null)
|
|
341
|
+
throw new TypeError(
|
|
342
|
+
"processLineSimplex: direction must have x and y properties",
|
|
343
|
+
);
|
|
344
|
+
if (!simplex[0] || simplex[0].x == null || simplex[0].y == null)
|
|
345
|
+
throw new TypeError(
|
|
346
|
+
"processLineSimplex: simplex[0] must have x and y properties",
|
|
347
|
+
);
|
|
348
|
+
if (!simplex[1] || simplex[1].x == null || simplex[1].y == null)
|
|
349
|
+
throw new TypeError(
|
|
350
|
+
"processLineSimplex: simplex[1] must have x and y properties",
|
|
351
|
+
);
|
|
294
352
|
const A = simplex[0]; // Newest point
|
|
295
353
|
const B = simplex[1];
|
|
296
354
|
|
|
@@ -317,7 +375,10 @@ export function processLineSimplex(simplex, direction) {
|
|
|
317
375
|
// Origin is on the line - check if between A and B
|
|
318
376
|
const dotAB_AO = dot(AB, AO);
|
|
319
377
|
|
|
320
|
-
if (
|
|
378
|
+
if (
|
|
379
|
+
dotAB_AO.greaterThanOrEqualTo(0) &&
|
|
380
|
+
dotAB_AO.lessThanOrEqualTo(ABmagSq)
|
|
381
|
+
) {
|
|
321
382
|
// Origin is on the segment - we have intersection
|
|
322
383
|
return { contains: true, newDirection: direction };
|
|
323
384
|
}
|
|
@@ -343,12 +404,28 @@ export function processLineSimplex(simplex, direction) {
|
|
|
343
404
|
* @throws {TypeError} If simplex is not an array, has wrong length, or direction is invalid
|
|
344
405
|
*/
|
|
345
406
|
export function processTriangleSimplex(simplex, direction) {
|
|
346
|
-
if (!Array.isArray(simplex))
|
|
347
|
-
|
|
348
|
-
if (
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
407
|
+
if (!Array.isArray(simplex))
|
|
408
|
+
throw new TypeError("processTriangleSimplex: simplex must be an array");
|
|
409
|
+
if (simplex.length !== 3)
|
|
410
|
+
throw new TypeError(
|
|
411
|
+
"processTriangleSimplex: simplex must have exactly 3 points",
|
|
412
|
+
);
|
|
413
|
+
if (!direction || direction.x == null || direction.y == null)
|
|
414
|
+
throw new TypeError(
|
|
415
|
+
"processTriangleSimplex: direction must have x and y properties",
|
|
416
|
+
);
|
|
417
|
+
if (!simplex[0] || simplex[0].x == null || simplex[0].y == null)
|
|
418
|
+
throw new TypeError(
|
|
419
|
+
"processTriangleSimplex: simplex[0] must have x and y properties",
|
|
420
|
+
);
|
|
421
|
+
if (!simplex[1] || simplex[1].x == null || simplex[1].y == null)
|
|
422
|
+
throw new TypeError(
|
|
423
|
+
"processTriangleSimplex: simplex[1] must have x and y properties",
|
|
424
|
+
);
|
|
425
|
+
if (!simplex[2] || simplex[2].x == null || simplex[2].y == null)
|
|
426
|
+
throw new TypeError(
|
|
427
|
+
"processTriangleSimplex: simplex[2] must have x and y properties",
|
|
428
|
+
);
|
|
352
429
|
const A = simplex[0]; // Newest point
|
|
353
430
|
const B = simplex[1];
|
|
354
431
|
const C = simplex[2];
|
|
@@ -366,7 +443,7 @@ export function processTriangleSimplex(simplex, direction) {
|
|
|
366
443
|
return {
|
|
367
444
|
contains: false,
|
|
368
445
|
newDirection: tripleProduct(AC, AO, AC),
|
|
369
|
-
newSimplex: [A, C]
|
|
446
|
+
newSimplex: [A, C],
|
|
370
447
|
};
|
|
371
448
|
}
|
|
372
449
|
|
|
@@ -375,7 +452,7 @@ export function processTriangleSimplex(simplex, direction) {
|
|
|
375
452
|
return {
|
|
376
453
|
contains: false,
|
|
377
454
|
newDirection: tripleProduct(AB, AO, AB),
|
|
378
|
-
newSimplex: [A, B]
|
|
455
|
+
newSimplex: [A, B],
|
|
379
456
|
};
|
|
380
457
|
}
|
|
381
458
|
|
|
@@ -384,7 +461,10 @@ export function processTriangleSimplex(simplex, direction) {
|
|
|
384
461
|
const ACperp = tripleProduct(AB, AC, AC);
|
|
385
462
|
|
|
386
463
|
// Check if perpendiculars are degenerate (collinear points)
|
|
387
|
-
if (
|
|
464
|
+
if (
|
|
465
|
+
magnitudeSquared(ABperp).lessThan(EPSILON) &&
|
|
466
|
+
magnitudeSquared(ACperp).lessThan(EPSILON)
|
|
467
|
+
) {
|
|
388
468
|
// All three points are collinear - reduce to line segment
|
|
389
469
|
// Choose the two points farthest apart
|
|
390
470
|
const BC = vectorSub(C, B);
|
|
@@ -393,19 +473,19 @@ export function processTriangleSimplex(simplex, direction) {
|
|
|
393
473
|
return {
|
|
394
474
|
contains: false,
|
|
395
475
|
newDirection: tripleProduct(BC, vectorNeg(B), BC),
|
|
396
|
-
newSimplex: [B, C]
|
|
476
|
+
newSimplex: [B, C],
|
|
397
477
|
};
|
|
398
478
|
} else if (ABmagSq.greaterThan(ACmagSq)) {
|
|
399
479
|
return {
|
|
400
480
|
contains: false,
|
|
401
481
|
newDirection: tripleProduct(AB, AO, AB),
|
|
402
|
-
newSimplex: [A, B]
|
|
482
|
+
newSimplex: [A, B],
|
|
403
483
|
};
|
|
404
484
|
} else {
|
|
405
485
|
return {
|
|
406
486
|
contains: false,
|
|
407
487
|
newDirection: tripleProduct(AC, AO, AC),
|
|
408
|
-
newSimplex: [A, C]
|
|
488
|
+
newSimplex: [A, C],
|
|
409
489
|
};
|
|
410
490
|
}
|
|
411
491
|
}
|
|
@@ -417,7 +497,7 @@ export function processTriangleSimplex(simplex, direction) {
|
|
|
417
497
|
return {
|
|
418
498
|
contains: false,
|
|
419
499
|
newDirection: ABperp,
|
|
420
|
-
newSimplex: [A, B]
|
|
500
|
+
newSimplex: [A, B],
|
|
421
501
|
};
|
|
422
502
|
}
|
|
423
503
|
|
|
@@ -428,7 +508,7 @@ export function processTriangleSimplex(simplex, direction) {
|
|
|
428
508
|
return {
|
|
429
509
|
contains: false,
|
|
430
510
|
newDirection: ACperp,
|
|
431
|
-
newSimplex: [A, C]
|
|
511
|
+
newSimplex: [A, C],
|
|
432
512
|
};
|
|
433
513
|
}
|
|
434
514
|
|
|
@@ -436,7 +516,7 @@ export function processTriangleSimplex(simplex, direction) {
|
|
|
436
516
|
return {
|
|
437
517
|
contains: true,
|
|
438
518
|
newDirection: direction,
|
|
439
|
-
newSimplex: simplex
|
|
519
|
+
newSimplex: simplex,
|
|
440
520
|
};
|
|
441
521
|
}
|
|
442
522
|
|
|
@@ -459,8 +539,10 @@ export function processTriangleSimplex(simplex, direction) {
|
|
|
459
539
|
* @throws {TypeError} If polygons are not arrays
|
|
460
540
|
*/
|
|
461
541
|
export function gjkIntersects(polygonA, polygonB) {
|
|
462
|
-
if (!Array.isArray(polygonA))
|
|
463
|
-
|
|
542
|
+
if (!Array.isArray(polygonA))
|
|
543
|
+
throw new TypeError("gjkIntersects: polygonA must be an array");
|
|
544
|
+
if (!Array.isArray(polygonB))
|
|
545
|
+
throw new TypeError("gjkIntersects: polygonB must be an array");
|
|
464
546
|
// Handle empty polygons
|
|
465
547
|
if (polygonA.length === 0 || polygonB.length === 0) {
|
|
466
548
|
return { intersects: false, iterations: 0, simplex: [], verified: true };
|
|
@@ -469,25 +551,26 @@ export function gjkIntersects(polygonA, polygonB) {
|
|
|
469
551
|
// Handle single points
|
|
470
552
|
if (polygonA.length === 1 && polygonB.length === 1) {
|
|
471
553
|
if (!polygonA[0] || polygonA[0].x == null || polygonA[0].y == null) {
|
|
472
|
-
throw new TypeError(
|
|
554
|
+
throw new TypeError(
|
|
555
|
+
"gjkIntersects: polygonA[0] must have x and y properties",
|
|
556
|
+
);
|
|
473
557
|
}
|
|
474
558
|
if (!polygonB[0] || polygonB[0].x == null || polygonB[0].y == null) {
|
|
475
|
-
throw new TypeError(
|
|
559
|
+
throw new TypeError(
|
|
560
|
+
"gjkIntersects: polygonB[0] must have x and y properties",
|
|
561
|
+
);
|
|
476
562
|
}
|
|
477
563
|
const dist = magnitude(vectorSub(polygonA[0], polygonB[0]));
|
|
478
564
|
return {
|
|
479
565
|
intersects: dist.lessThan(EPSILON),
|
|
480
566
|
iterations: 1,
|
|
481
567
|
simplex: [],
|
|
482
|
-
verified: true
|
|
568
|
+
verified: true,
|
|
483
569
|
};
|
|
484
570
|
}
|
|
485
571
|
|
|
486
572
|
// Initial direction: from center of A to center of B
|
|
487
|
-
let direction = vectorSub(
|
|
488
|
-
centroid(polygonB),
|
|
489
|
-
centroid(polygonA)
|
|
490
|
-
);
|
|
573
|
+
let direction = vectorSub(centroid(polygonB), centroid(polygonA));
|
|
491
574
|
|
|
492
575
|
// If centers are the same, use arbitrary direction
|
|
493
576
|
if (magnitudeSquared(direction).lessThan(EPSILON)) {
|
|
@@ -506,7 +589,7 @@ export function gjkIntersects(polygonA, polygonB) {
|
|
|
506
589
|
intersects: true,
|
|
507
590
|
iterations: 1,
|
|
508
591
|
simplex,
|
|
509
|
-
verified: verifyIntersection(polygonA, polygonB)
|
|
592
|
+
verified: verifyIntersection(polygonA, polygonB),
|
|
510
593
|
};
|
|
511
594
|
}
|
|
512
595
|
|
|
@@ -523,7 +606,7 @@ export function gjkIntersects(polygonA, polygonB) {
|
|
|
523
606
|
intersects: false,
|
|
524
607
|
iterations: iteration + 1,
|
|
525
608
|
simplex,
|
|
526
|
-
verified: true
|
|
609
|
+
verified: true,
|
|
527
610
|
};
|
|
528
611
|
}
|
|
529
612
|
|
|
@@ -539,7 +622,7 @@ export function gjkIntersects(polygonA, polygonB) {
|
|
|
539
622
|
intersects: true,
|
|
540
623
|
iterations: iteration + 1,
|
|
541
624
|
simplex,
|
|
542
|
-
verified: verifyIntersection(polygonA, polygonB)
|
|
625
|
+
verified: verifyIntersection(polygonA, polygonB),
|
|
543
626
|
};
|
|
544
627
|
}
|
|
545
628
|
direction = result.newDirection;
|
|
@@ -551,7 +634,7 @@ export function gjkIntersects(polygonA, polygonB) {
|
|
|
551
634
|
intersects: true,
|
|
552
635
|
iterations: iteration + 1,
|
|
553
636
|
simplex: result.newSimplex,
|
|
554
|
-
verified: verifyIntersection(polygonA, polygonB)
|
|
637
|
+
verified: verifyIntersection(polygonA, polygonB),
|
|
555
638
|
};
|
|
556
639
|
}
|
|
557
640
|
simplex = result.newSimplex;
|
|
@@ -566,7 +649,7 @@ export function gjkIntersects(polygonA, polygonB) {
|
|
|
566
649
|
intersects: false,
|
|
567
650
|
iterations: iteration + 1,
|
|
568
651
|
simplex,
|
|
569
|
-
verified: false
|
|
652
|
+
verified: false,
|
|
570
653
|
};
|
|
571
654
|
}
|
|
572
655
|
|
|
@@ -579,7 +662,7 @@ export function gjkIntersects(polygonA, polygonB) {
|
|
|
579
662
|
intersects: false,
|
|
580
663
|
iterations: MAX_ITERATIONS,
|
|
581
664
|
simplex,
|
|
582
|
-
verified: false
|
|
665
|
+
verified: false,
|
|
583
666
|
};
|
|
584
667
|
}
|
|
585
668
|
|
|
@@ -594,9 +677,10 @@ export function gjkIntersects(polygonA, polygonB) {
|
|
|
594
677
|
* @throws {TypeError} If polygon is not an array
|
|
595
678
|
*/
|
|
596
679
|
export function centroid(polygon) {
|
|
597
|
-
if (!Array.isArray(polygon))
|
|
680
|
+
if (!Array.isArray(polygon))
|
|
681
|
+
throw new TypeError("centroid: polygon must be an array");
|
|
598
682
|
if (polygon.length === 0) {
|
|
599
|
-
throw new TypeError(
|
|
683
|
+
throw new TypeError("centroid: polygon cannot be empty");
|
|
600
684
|
}
|
|
601
685
|
|
|
602
686
|
let sumX = D(0);
|
|
@@ -604,7 +688,9 @@ export function centroid(polygon) {
|
|
|
604
688
|
|
|
605
689
|
for (let i = 0; i < polygon.length; i++) {
|
|
606
690
|
if (!polygon[i] || polygon[i].x == null || polygon[i].y == null) {
|
|
607
|
-
throw new TypeError(
|
|
691
|
+
throw new TypeError(
|
|
692
|
+
`centroid: polygon[${i}] must have x and y properties`,
|
|
693
|
+
);
|
|
608
694
|
}
|
|
609
695
|
sumX = sumX.plus(polygon[i].x);
|
|
610
696
|
sumY = sumY.plus(polygon[i].y);
|
|
@@ -626,8 +712,12 @@ export function centroid(polygon) {
|
|
|
626
712
|
* @throws {TypeError} If pt is invalid or polygon is not an array
|
|
627
713
|
*/
|
|
628
714
|
export function pointInConvexPolygon(pt, polygon) {
|
|
629
|
-
if (!pt || pt.x == null || pt.y == null)
|
|
630
|
-
|
|
715
|
+
if (!pt || pt.x == null || pt.y == null)
|
|
716
|
+
throw new TypeError(
|
|
717
|
+
"pointInConvexPolygon: point must have x and y properties",
|
|
718
|
+
);
|
|
719
|
+
if (!Array.isArray(polygon))
|
|
720
|
+
throw new TypeError("pointInConvexPolygon: polygon must be an array");
|
|
631
721
|
if (polygon.length < 3) {
|
|
632
722
|
return false;
|
|
633
723
|
}
|
|
@@ -639,10 +729,14 @@ export function pointInConvexPolygon(pt, polygon) {
|
|
|
639
729
|
const p2 = polygon[(i + 1) % polygon.length];
|
|
640
730
|
|
|
641
731
|
if (!p1 || p1.x == null || p1.y == null) {
|
|
642
|
-
throw new TypeError(
|
|
732
|
+
throw new TypeError(
|
|
733
|
+
`pointInConvexPolygon: polygon[${i}] must have x and y properties`,
|
|
734
|
+
);
|
|
643
735
|
}
|
|
644
736
|
if (!p2 || p2.x == null || p2.y == null) {
|
|
645
|
-
throw new TypeError(
|
|
737
|
+
throw new TypeError(
|
|
738
|
+
`pointInConvexPolygon: polygon[${(i + 1) % polygon.length}] must have x and y properties`,
|
|
739
|
+
);
|
|
646
740
|
}
|
|
647
741
|
|
|
648
742
|
const edge = vectorSub(p2, p1);
|
|
@@ -679,12 +773,16 @@ export function pointInConvexPolygon(pt, polygon) {
|
|
|
679
773
|
* @throws {TypeError} If polygons are not arrays
|
|
680
774
|
*/
|
|
681
775
|
export function verifyIntersection(polygonA, polygonB) {
|
|
682
|
-
if (!Array.isArray(polygonA))
|
|
683
|
-
|
|
776
|
+
if (!Array.isArray(polygonA))
|
|
777
|
+
throw new TypeError("verifyIntersection: polygonA must be an array");
|
|
778
|
+
if (!Array.isArray(polygonB))
|
|
779
|
+
throw new TypeError("verifyIntersection: polygonB must be an array");
|
|
684
780
|
// Check if any vertex of A is inside B
|
|
685
781
|
for (let i = 0; i < polygonA.length; i++) {
|
|
686
782
|
if (!polygonA[i] || polygonA[i].x == null || polygonA[i].y == null) {
|
|
687
|
-
throw new TypeError(
|
|
783
|
+
throw new TypeError(
|
|
784
|
+
`verifyIntersection: polygonA[${i}] must have x and y properties`,
|
|
785
|
+
);
|
|
688
786
|
}
|
|
689
787
|
if (pointInConvexPolygon(polygonA[i], polygonB)) {
|
|
690
788
|
return true;
|
|
@@ -694,7 +792,9 @@ export function verifyIntersection(polygonA, polygonB) {
|
|
|
694
792
|
// Check if any vertex of B is inside A
|
|
695
793
|
for (let i = 0; i < polygonB.length; i++) {
|
|
696
794
|
if (!polygonB[i] || polygonB[i].x == null || polygonB[i].y == null) {
|
|
697
|
-
throw new TypeError(
|
|
795
|
+
throw new TypeError(
|
|
796
|
+
`verifyIntersection: polygonB[${i}] must have x and y properties`,
|
|
797
|
+
);
|
|
698
798
|
}
|
|
699
799
|
if (pointInConvexPolygon(polygonB[i], polygonA)) {
|
|
700
800
|
return true;
|
|
@@ -707,10 +807,14 @@ export function verifyIntersection(polygonA, polygonB) {
|
|
|
707
807
|
const a2 = polygonA[(i + 1) % polygonA.length];
|
|
708
808
|
|
|
709
809
|
if (!a1 || a1.x == null || a1.y == null) {
|
|
710
|
-
throw new TypeError(
|
|
810
|
+
throw new TypeError(
|
|
811
|
+
`verifyIntersection: polygonA[${i}] must have x and y properties`,
|
|
812
|
+
);
|
|
711
813
|
}
|
|
712
814
|
if (!a2 || a2.x == null || a2.y == null) {
|
|
713
|
-
throw new TypeError(
|
|
815
|
+
throw new TypeError(
|
|
816
|
+
`verifyIntersection: polygonA[${(i + 1) % polygonA.length}] must have x and y properties`,
|
|
817
|
+
);
|
|
714
818
|
}
|
|
715
819
|
|
|
716
820
|
for (let j = 0; j < polygonB.length; j++) {
|
|
@@ -718,10 +822,14 @@ export function verifyIntersection(polygonA, polygonB) {
|
|
|
718
822
|
const b2 = polygonB[(j + 1) % polygonB.length];
|
|
719
823
|
|
|
720
824
|
if (!b1 || b1.x == null || b1.y == null) {
|
|
721
|
-
throw new TypeError(
|
|
825
|
+
throw new TypeError(
|
|
826
|
+
`verifyIntersection: polygonB[${j}] must have x and y properties`,
|
|
827
|
+
);
|
|
722
828
|
}
|
|
723
829
|
if (!b2 || b2.x == null || b2.y == null) {
|
|
724
|
-
throw new TypeError(
|
|
830
|
+
throw new TypeError(
|
|
831
|
+
`verifyIntersection: polygonB[${(j + 1) % polygonB.length}] must have x and y properties`,
|
|
832
|
+
);
|
|
725
833
|
}
|
|
726
834
|
|
|
727
835
|
if (segmentsIntersect(a1, a2, b1, b2)) {
|
|
@@ -744,10 +852,14 @@ export function verifyIntersection(polygonA, polygonB) {
|
|
|
744
852
|
* @throws {TypeError} If any point is invalid or missing x/y properties
|
|
745
853
|
*/
|
|
746
854
|
export function segmentsIntersect(a1, a2, b1, b2) {
|
|
747
|
-
if (!a1 || a1.x == null || a1.y == null)
|
|
748
|
-
|
|
749
|
-
if (!
|
|
750
|
-
|
|
855
|
+
if (!a1 || a1.x == null || a1.y == null)
|
|
856
|
+
throw new TypeError("segmentsIntersect: a1 must have x and y properties");
|
|
857
|
+
if (!a2 || a2.x == null || a2.y == null)
|
|
858
|
+
throw new TypeError("segmentsIntersect: a2 must have x and y properties");
|
|
859
|
+
if (!b1 || b1.x == null || b1.y == null)
|
|
860
|
+
throw new TypeError("segmentsIntersect: b1 must have x and y properties");
|
|
861
|
+
if (!b2 || b2.x == null || b2.y == null)
|
|
862
|
+
throw new TypeError("segmentsIntersect: b2 must have x and y properties");
|
|
751
863
|
|
|
752
864
|
const d1 = vectorSub(a2, a1);
|
|
753
865
|
const d2 = vectorSub(b2, b1);
|
|
@@ -791,8 +903,12 @@ export function segmentsIntersect(a1, a2, b1, b2) {
|
|
|
791
903
|
const u = cross(d3, d1).div(crossD);
|
|
792
904
|
|
|
793
905
|
// Check if intersection is within both segments
|
|
794
|
-
return
|
|
795
|
-
|
|
906
|
+
return (
|
|
907
|
+
t.greaterThanOrEqualTo(0) &&
|
|
908
|
+
t.lessThanOrEqualTo(1) &&
|
|
909
|
+
u.greaterThanOrEqualTo(0) &&
|
|
910
|
+
u.lessThanOrEqualTo(1)
|
|
911
|
+
);
|
|
796
912
|
}
|
|
797
913
|
|
|
798
914
|
// ============================================================================
|
|
@@ -811,10 +927,14 @@ export function segmentsIntersect(a1, a2, b1, b2) {
|
|
|
811
927
|
* @throws {TypeError} If polygons are not arrays or are empty
|
|
812
928
|
*/
|
|
813
929
|
export function gjkDistance(polygonA, polygonB) {
|
|
814
|
-
if (!Array.isArray(polygonA))
|
|
815
|
-
|
|
816
|
-
if (
|
|
817
|
-
|
|
930
|
+
if (!Array.isArray(polygonA))
|
|
931
|
+
throw new TypeError("gjkDistance: polygonA must be an array");
|
|
932
|
+
if (!Array.isArray(polygonB))
|
|
933
|
+
throw new TypeError("gjkDistance: polygonB must be an array");
|
|
934
|
+
if (polygonA.length === 0)
|
|
935
|
+
throw new TypeError("gjkDistance: polygonA cannot be empty");
|
|
936
|
+
if (polygonB.length === 0)
|
|
937
|
+
throw new TypeError("gjkDistance: polygonB cannot be empty");
|
|
818
938
|
|
|
819
939
|
// First check if they intersect
|
|
820
940
|
const intersection = gjkIntersects(polygonA, polygonB);
|
|
@@ -824,7 +944,7 @@ export function gjkDistance(polygonA, polygonB) {
|
|
|
824
944
|
distance: D(0),
|
|
825
945
|
closestA: centroid(polygonA),
|
|
826
946
|
closestB: centroid(polygonB),
|
|
827
|
-
verified: true
|
|
947
|
+
verified: true,
|
|
828
948
|
};
|
|
829
949
|
}
|
|
830
950
|
|
|
@@ -836,10 +956,14 @@ export function gjkDistance(polygonA, polygonB) {
|
|
|
836
956
|
|
|
837
957
|
// Validate first points before using them as initial values
|
|
838
958
|
if (!polygonA[0] || polygonA[0].x == null || polygonA[0].y == null) {
|
|
839
|
-
throw new TypeError(
|
|
959
|
+
throw new TypeError(
|
|
960
|
+
"gjkDistance: polygonA[0] must have x and y properties",
|
|
961
|
+
);
|
|
840
962
|
}
|
|
841
963
|
if (!polygonB[0] || polygonB[0].x == null || polygonB[0].y == null) {
|
|
842
|
-
throw new TypeError(
|
|
964
|
+
throw new TypeError(
|
|
965
|
+
"gjkDistance: polygonB[0] must have x and y properties",
|
|
966
|
+
);
|
|
843
967
|
}
|
|
844
968
|
|
|
845
969
|
let minDist = D(Infinity);
|
|
@@ -849,11 +973,15 @@ export function gjkDistance(polygonA, polygonB) {
|
|
|
849
973
|
// Check all pairs of vertices
|
|
850
974
|
for (let i = 0; i < polygonA.length; i++) {
|
|
851
975
|
if (!polygonA[i] || polygonA[i].x == null || polygonA[i].y == null) {
|
|
852
|
-
throw new TypeError(
|
|
976
|
+
throw new TypeError(
|
|
977
|
+
`gjkDistance: polygonA[${i}] must have x and y properties`,
|
|
978
|
+
);
|
|
853
979
|
}
|
|
854
980
|
for (let j = 0; j < polygonB.length; j++) {
|
|
855
981
|
if (!polygonB[j] || polygonB[j].x == null || polygonB[j].y == null) {
|
|
856
|
-
throw new TypeError(
|
|
982
|
+
throw new TypeError(
|
|
983
|
+
`gjkDistance: polygonB[${j}] must have x and y properties`,
|
|
984
|
+
);
|
|
857
985
|
}
|
|
858
986
|
const dist = magnitude(vectorSub(polygonA[i], polygonB[j]));
|
|
859
987
|
if (dist.lessThan(minDist)) {
|
|
@@ -867,16 +995,22 @@ export function gjkDistance(polygonA, polygonB) {
|
|
|
867
995
|
// Check vertex-to-edge distances
|
|
868
996
|
for (let i = 0; i < polygonA.length; i++) {
|
|
869
997
|
if (!polygonA[i] || polygonA[i].x == null || polygonA[i].y == null) {
|
|
870
|
-
throw new TypeError(
|
|
998
|
+
throw new TypeError(
|
|
999
|
+
`gjkDistance: polygonA[${i}] must have x and y properties`,
|
|
1000
|
+
);
|
|
871
1001
|
}
|
|
872
1002
|
for (let j = 0; j < polygonB.length; j++) {
|
|
873
1003
|
const e1 = polygonB[j];
|
|
874
1004
|
const e2 = polygonB[(j + 1) % polygonB.length];
|
|
875
1005
|
if (!e1 || e1.x == null || e1.y == null) {
|
|
876
|
-
throw new TypeError(
|
|
1006
|
+
throw new TypeError(
|
|
1007
|
+
`gjkDistance: polygonB[${j}] must have x and y properties`,
|
|
1008
|
+
);
|
|
877
1009
|
}
|
|
878
1010
|
if (!e2 || e2.x == null || e2.y == null) {
|
|
879
|
-
throw new TypeError(
|
|
1011
|
+
throw new TypeError(
|
|
1012
|
+
`gjkDistance: polygonB[${(j + 1) % polygonB.length}] must have x and y properties`,
|
|
1013
|
+
);
|
|
880
1014
|
}
|
|
881
1015
|
const closest = closestPointOnSegment(polygonA[i], e1, e2);
|
|
882
1016
|
const dist = magnitude(vectorSub(polygonA[i], closest));
|
|
@@ -890,16 +1024,22 @@ export function gjkDistance(polygonA, polygonB) {
|
|
|
890
1024
|
|
|
891
1025
|
for (let i = 0; i < polygonB.length; i++) {
|
|
892
1026
|
if (!polygonB[i] || polygonB[i].x == null || polygonB[i].y == null) {
|
|
893
|
-
throw new TypeError(
|
|
1027
|
+
throw new TypeError(
|
|
1028
|
+
`gjkDistance: polygonB[${i}] must have x and y properties`,
|
|
1029
|
+
);
|
|
894
1030
|
}
|
|
895
1031
|
for (let j = 0; j < polygonA.length; j++) {
|
|
896
1032
|
const e1 = polygonA[j];
|
|
897
1033
|
const e2 = polygonA[(j + 1) % polygonA.length];
|
|
898
1034
|
if (!e1 || e1.x == null || e1.y == null) {
|
|
899
|
-
throw new TypeError(
|
|
1035
|
+
throw new TypeError(
|
|
1036
|
+
`gjkDistance: polygonA[${j}] must have x and y properties`,
|
|
1037
|
+
);
|
|
900
1038
|
}
|
|
901
1039
|
if (!e2 || e2.x == null || e2.y == null) {
|
|
902
|
-
throw new TypeError(
|
|
1040
|
+
throw new TypeError(
|
|
1041
|
+
`gjkDistance: polygonA[${(j + 1) % polygonA.length}] must have x and y properties`,
|
|
1042
|
+
);
|
|
903
1043
|
}
|
|
904
1044
|
const closest = closestPointOnSegment(polygonB[i], e1, e2);
|
|
905
1045
|
const dist = magnitude(vectorSub(polygonB[i], closest));
|
|
@@ -919,7 +1059,7 @@ export function gjkDistance(polygonA, polygonB) {
|
|
|
919
1059
|
distance: minDist,
|
|
920
1060
|
closestA,
|
|
921
1061
|
closestB,
|
|
922
|
-
verified
|
|
1062
|
+
verified,
|
|
923
1063
|
};
|
|
924
1064
|
}
|
|
925
1065
|
|
|
@@ -933,9 +1073,18 @@ export function gjkDistance(polygonA, polygonB) {
|
|
|
933
1073
|
* @throws {TypeError} If any point is invalid or missing x/y properties
|
|
934
1074
|
*/
|
|
935
1075
|
export function closestPointOnSegment(pt, a, b) {
|
|
936
|
-
if (!pt || pt.x == null || pt.y == null)
|
|
937
|
-
|
|
938
|
-
|
|
1076
|
+
if (!pt || pt.x == null || pt.y == null)
|
|
1077
|
+
throw new TypeError(
|
|
1078
|
+
"closestPointOnSegment: pt must have x and y properties",
|
|
1079
|
+
);
|
|
1080
|
+
if (!a || a.x == null || a.y == null)
|
|
1081
|
+
throw new TypeError(
|
|
1082
|
+
"closestPointOnSegment: a must have x and y properties",
|
|
1083
|
+
);
|
|
1084
|
+
if (!b || b.x == null || b.y == null)
|
|
1085
|
+
throw new TypeError(
|
|
1086
|
+
"closestPointOnSegment: b must have x and y properties",
|
|
1087
|
+
);
|
|
939
1088
|
|
|
940
1089
|
const ab = vectorSub(b, a);
|
|
941
1090
|
const ap = vectorSub(pt, a);
|
|
@@ -969,18 +1118,24 @@ export function closestPointOnSegment(pt, a, b) {
|
|
|
969
1118
|
* @throws {TypeError} If polygons are not arrays
|
|
970
1119
|
*/
|
|
971
1120
|
export function polygonsOverlap(polygonA, polygonB) {
|
|
972
|
-
if (!Array.isArray(polygonA))
|
|
973
|
-
|
|
1121
|
+
if (!Array.isArray(polygonA))
|
|
1122
|
+
throw new TypeError("polygonsOverlap: polygonA must be an array");
|
|
1123
|
+
if (!Array.isArray(polygonB))
|
|
1124
|
+
throw new TypeError("polygonsOverlap: polygonB must be an array");
|
|
974
1125
|
// Normalize input to Decimal
|
|
975
1126
|
const normA = polygonA.map((p, i) => {
|
|
976
1127
|
if (!p || p.x == null || p.y == null) {
|
|
977
|
-
throw new TypeError(
|
|
1128
|
+
throw new TypeError(
|
|
1129
|
+
`polygonsOverlap: polygonA[${i}] must have x and y properties`,
|
|
1130
|
+
);
|
|
978
1131
|
}
|
|
979
1132
|
return point(p.x, p.y);
|
|
980
1133
|
});
|
|
981
1134
|
const normB = polygonB.map((p, i) => {
|
|
982
1135
|
if (!p || p.x == null || p.y == null) {
|
|
983
|
-
throw new TypeError(
|
|
1136
|
+
throw new TypeError(
|
|
1137
|
+
`polygonsOverlap: polygonB[${i}] must have x and y properties`,
|
|
1138
|
+
);
|
|
984
1139
|
}
|
|
985
1140
|
return point(p.x, p.y);
|
|
986
1141
|
});
|
|
@@ -989,7 +1144,7 @@ export function polygonsOverlap(polygonA, polygonB) {
|
|
|
989
1144
|
|
|
990
1145
|
return {
|
|
991
1146
|
overlaps: result.intersects,
|
|
992
|
-
verified: result.verified
|
|
1147
|
+
verified: result.verified,
|
|
993
1148
|
};
|
|
994
1149
|
}
|
|
995
1150
|
|
|
@@ -1002,18 +1157,24 @@ export function polygonsOverlap(polygonA, polygonB) {
|
|
|
1002
1157
|
* @throws {TypeError} If polygons are not arrays
|
|
1003
1158
|
*/
|
|
1004
1159
|
export function polygonsDistance(polygonA, polygonB) {
|
|
1005
|
-
if (!Array.isArray(polygonA))
|
|
1006
|
-
|
|
1160
|
+
if (!Array.isArray(polygonA))
|
|
1161
|
+
throw new TypeError("polygonsDistance: polygonA must be an array");
|
|
1162
|
+
if (!Array.isArray(polygonB))
|
|
1163
|
+
throw new TypeError("polygonsDistance: polygonB must be an array");
|
|
1007
1164
|
// Normalize input to Decimal
|
|
1008
1165
|
const normA = polygonA.map((p, i) => {
|
|
1009
1166
|
if (!p || p.x == null || p.y == null) {
|
|
1010
|
-
throw new TypeError(
|
|
1167
|
+
throw new TypeError(
|
|
1168
|
+
`polygonsDistance: polygonA[${i}] must have x and y properties`,
|
|
1169
|
+
);
|
|
1011
1170
|
}
|
|
1012
1171
|
return point(p.x, p.y);
|
|
1013
1172
|
});
|
|
1014
1173
|
const normB = polygonB.map((p, i) => {
|
|
1015
1174
|
if (!p || p.x == null || p.y == null) {
|
|
1016
|
-
throw new TypeError(
|
|
1175
|
+
throw new TypeError(
|
|
1176
|
+
`polygonsDistance: polygonB[${i}] must have x and y properties`,
|
|
1177
|
+
);
|
|
1017
1178
|
}
|
|
1018
1179
|
return point(p.x, p.y);
|
|
1019
1180
|
});
|
|
@@ -1024,7 +1185,7 @@ export function polygonsDistance(polygonA, polygonB) {
|
|
|
1024
1185
|
distance: result.distance,
|
|
1025
1186
|
closestA: result.closestA,
|
|
1026
1187
|
closestB: result.closestB,
|
|
1027
|
-
verified: result.verified
|
|
1188
|
+
verified: result.verified,
|
|
1028
1189
|
};
|
|
1029
1190
|
}
|
|
1030
1191
|
|
|
@@ -1037,12 +1198,16 @@ export function polygonsDistance(polygonA, polygonB) {
|
|
|
1037
1198
|
* @throws {TypeError} If pt is invalid or polygon is not an array
|
|
1038
1199
|
*/
|
|
1039
1200
|
export function isPointInPolygon(pt, polygon) {
|
|
1040
|
-
if (!pt || pt.x == null || pt.y == null)
|
|
1041
|
-
|
|
1201
|
+
if (!pt || pt.x == null || pt.y == null)
|
|
1202
|
+
throw new TypeError("isPointInPolygon: pt must have x and y properties");
|
|
1203
|
+
if (!Array.isArray(polygon))
|
|
1204
|
+
throw new TypeError("isPointInPolygon: polygon must be an array");
|
|
1042
1205
|
const normPt = point(pt.x, pt.y);
|
|
1043
1206
|
const normPoly = polygon.map((p, i) => {
|
|
1044
1207
|
if (!p || p.x == null || p.y == null) {
|
|
1045
|
-
throw new TypeError(
|
|
1208
|
+
throw new TypeError(
|
|
1209
|
+
`isPointInPolygon: polygon[${i}] must have x and y properties`,
|
|
1210
|
+
);
|
|
1046
1211
|
}
|
|
1047
1212
|
return point(p.x, p.y);
|
|
1048
1213
|
});
|
|
@@ -1054,11 +1219,7 @@ export function isPointInPolygon(pt, polygon) {
|
|
|
1054
1219
|
// Exports
|
|
1055
1220
|
// ============================================================================
|
|
1056
1221
|
|
|
1057
|
-
export {
|
|
1058
|
-
EPSILON,
|
|
1059
|
-
MAX_ITERATIONS,
|
|
1060
|
-
D
|
|
1061
|
-
};
|
|
1222
|
+
export { EPSILON, MAX_ITERATIONS, D };
|
|
1062
1223
|
|
|
1063
1224
|
export default {
|
|
1064
1225
|
// Point/vector utilities
|
|
@@ -1101,5 +1262,5 @@ export default {
|
|
|
1101
1262
|
|
|
1102
1263
|
// Constants
|
|
1103
1264
|
EPSILON,
|
|
1104
|
-
MAX_ITERATIONS
|
|
1265
|
+
MAX_ITERATIONS,
|
|
1105
1266
|
};
|