@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.
Files changed (55) hide show
  1. package/bin/svg-matrix.js +7 -6
  2. package/bin/svgm.js +109 -40
  3. package/dist/svg-matrix.min.js +7 -7
  4. package/dist/svg-toolbox.min.js +148 -228
  5. package/dist/svgm.min.js +152 -232
  6. package/dist/version.json +5 -5
  7. package/package.json +1 -1
  8. package/scripts/postinstall.js +72 -41
  9. package/scripts/test-postinstall.js +18 -16
  10. package/scripts/version-sync.js +78 -60
  11. package/src/animation-optimization.js +190 -98
  12. package/src/animation-references.js +11 -3
  13. package/src/arc-length.js +23 -20
  14. package/src/bezier-analysis.js +9 -13
  15. package/src/bezier-intersections.js +18 -4
  16. package/src/browser-verify.js +35 -8
  17. package/src/clip-path-resolver.js +285 -114
  18. package/src/convert-path-data.js +20 -8
  19. package/src/css-specificity.js +33 -9
  20. package/src/douglas-peucker.js +272 -141
  21. package/src/geometry-to-path.js +79 -22
  22. package/src/gjk-collision.js +287 -126
  23. package/src/index.js +56 -21
  24. package/src/inkscape-support.js +122 -101
  25. package/src/logger.js +43 -27
  26. package/src/marker-resolver.js +201 -121
  27. package/src/mask-resolver.js +231 -98
  28. package/src/matrix.js +9 -5
  29. package/src/mesh-gradient.js +22 -14
  30. package/src/off-canvas-detection.js +53 -17
  31. package/src/path-optimization.js +356 -171
  32. package/src/path-simplification.js +671 -256
  33. package/src/pattern-resolver.js +1 -3
  34. package/src/polygon-clip.js +396 -78
  35. package/src/svg-boolean-ops.js +90 -23
  36. package/src/svg-collections.js +1546 -667
  37. package/src/svg-flatten.js +152 -38
  38. package/src/svg-matrix-lib.js +2 -2
  39. package/src/svg-parser.js +5 -1
  40. package/src/svg-rendering-context.js +3 -1
  41. package/src/svg-toolbox-lib.js +2 -2
  42. package/src/svg-toolbox.js +99 -457
  43. package/src/svg-validation-data.js +513 -345
  44. package/src/svg2-polyfills.js +156 -93
  45. package/src/svgm-lib.js +8 -4
  46. package/src/transform-optimization.js +168 -51
  47. package/src/transforms2d.js +73 -40
  48. package/src/transforms3d.js +34 -27
  49. package/src/use-symbol-resolver.js +175 -76
  50. package/src/vector.js +80 -44
  51. package/src/vendor/inkscape-hatch-polyfill.js +143 -108
  52. package/src/vendor/inkscape-hatch-polyfill.min.js +291 -1
  53. package/src/vendor/inkscape-mesh-polyfill.js +953 -766
  54. package/src/vendor/inkscape-mesh-polyfill.min.js +896 -1
  55. package/src/verification.js +3 -4
@@ -19,21 +19,42 @@
19
19
  */
20
20
  export function perpendicularDistance(point, lineStart, lineEnd) {
21
21
  // Validate parameters to prevent undefined access and ensure numeric properties
22
- if (!point || typeof point.x !== 'number' || typeof point.y !== 'number') {
23
- throw new TypeError('perpendicularDistance: point must be an object with numeric x and y properties');
24
- }
25
- if (!lineStart || typeof lineStart.x !== 'number' || typeof lineStart.y !== 'number') {
26
- throw new TypeError('perpendicularDistance: lineStart must be an object with numeric x and y properties');
27
- }
28
- if (!lineEnd || typeof lineEnd.x !== 'number' || typeof lineEnd.y !== 'number') {
29
- throw new TypeError('perpendicularDistance: lineEnd must be an object with numeric x and y properties');
22
+ if (!point || typeof point.x !== "number" || typeof point.y !== "number") {
23
+ throw new TypeError(
24
+ "perpendicularDistance: point must be an object with numeric x and y properties",
25
+ );
26
+ }
27
+ if (
28
+ !lineStart ||
29
+ typeof lineStart.x !== "number" ||
30
+ typeof lineStart.y !== "number"
31
+ ) {
32
+ throw new TypeError(
33
+ "perpendicularDistance: lineStart must be an object with numeric x and y properties",
34
+ );
35
+ }
36
+ if (
37
+ !lineEnd ||
38
+ typeof lineEnd.x !== "number" ||
39
+ typeof lineEnd.y !== "number"
40
+ ) {
41
+ throw new TypeError(
42
+ "perpendicularDistance: lineEnd must be an object with numeric x and y properties",
43
+ );
30
44
  }
31
45
 
32
46
  // Check for NaN/Infinity in coordinates to prevent invalid calculations
33
- if (!Number.isFinite(point.x) || !Number.isFinite(point.y) ||
34
- !Number.isFinite(lineStart.x) || !Number.isFinite(lineStart.y) ||
35
- !Number.isFinite(lineEnd.x) || !Number.isFinite(lineEnd.y)) {
36
- throw new RangeError('perpendicularDistance: all coordinates must be finite numbers');
47
+ if (
48
+ !Number.isFinite(point.x) ||
49
+ !Number.isFinite(point.y) ||
50
+ !Number.isFinite(lineStart.x) ||
51
+ !Number.isFinite(lineStart.y) ||
52
+ !Number.isFinite(lineEnd.x) ||
53
+ !Number.isFinite(lineEnd.y)
54
+ ) {
55
+ throw new RangeError(
56
+ "perpendicularDistance: all coordinates must be finite numbers",
57
+ );
37
58
  }
38
59
 
39
60
  const dx = lineEnd.x - lineStart.x;
@@ -50,7 +71,10 @@ export function perpendicularDistance(point, lineStart, lineEnd) {
50
71
  // Calculate perpendicular distance using cross product formula
51
72
  // |((y2-y1)*x0 - (x2-x1)*y0 + x2*y1 - y2*x1)| / sqrt((y2-y1)^2 + (x2-x1)^2)
52
73
  const numerator = Math.abs(
53
- dy * point.x - dx * point.y + lineEnd.x * lineStart.y - lineEnd.y * lineStart.x
74
+ dy * point.x -
75
+ dx * point.y +
76
+ lineEnd.x * lineStart.y -
77
+ lineEnd.y * lineStart.x,
54
78
  );
55
79
  const denominator = Math.sqrt(lineLengthSq);
56
80
 
@@ -66,36 +90,44 @@ export function perpendicularDistance(point, lineStart, lineEnd) {
66
90
  export function douglasPeucker(points, tolerance) {
67
91
  // Validate points parameter to prevent crashes on invalid input
68
92
  if (!Array.isArray(points)) {
69
- throw new TypeError('douglasPeucker: points must be an array');
93
+ throw new TypeError("douglasPeucker: points must be an array");
70
94
  }
71
95
  if (points.length === 0) {
72
- throw new RangeError('douglasPeucker: points array cannot be empty');
96
+ throw new RangeError("douglasPeucker: points array cannot be empty");
73
97
  }
74
98
 
75
99
  // Validate each point has x and y properties with finite numeric values
76
100
  for (let i = 0; i < points.length; i++) {
77
101
  const p = points[i];
78
- if (!p || typeof p !== 'object') {
79
- throw new TypeError(`douglasPeucker: point at index ${i} must be an object`);
102
+ if (!p || typeof p !== "object") {
103
+ throw new TypeError(
104
+ `douglasPeucker: point at index ${i} must be an object`,
105
+ );
80
106
  }
81
- if (typeof p.x !== 'number' || !Number.isFinite(p.x)) {
82
- throw new TypeError(`douglasPeucker: point at index ${i} must have a finite numeric x property`);
107
+ if (typeof p.x !== "number" || !Number.isFinite(p.x)) {
108
+ throw new TypeError(
109
+ `douglasPeucker: point at index ${i} must have a finite numeric x property`,
110
+ );
83
111
  }
84
- if (typeof p.y !== 'number' || !Number.isFinite(p.y)) {
85
- throw new TypeError(`douglasPeucker: point at index ${i} must have a finite numeric y property`);
112
+ if (typeof p.y !== "number" || !Number.isFinite(p.y)) {
113
+ throw new TypeError(
114
+ `douglasPeucker: point at index ${i} must have a finite numeric y property`,
115
+ );
86
116
  }
87
117
  }
88
118
 
89
119
  // Validate tolerance parameter to ensure valid numeric simplification threshold
90
- if (typeof tolerance !== 'number' || !Number.isFinite(tolerance)) {
91
- throw new TypeError('douglasPeucker: tolerance must be a finite number');
120
+ if (typeof tolerance !== "number" || !Number.isFinite(tolerance)) {
121
+ throw new TypeError("douglasPeucker: tolerance must be a finite number");
92
122
  }
93
123
  if (tolerance < 0) {
94
- throw new RangeError('douglasPeucker: tolerance cannot be negative');
124
+ throw new RangeError("douglasPeucker: tolerance cannot be negative");
95
125
  }
96
126
  // Prevent impractically small tolerances that cause numerical precision issues and extreme recursion depth
97
127
  if (tolerance > 0 && tolerance < 1e-10) {
98
- throw new RangeError('douglasPeucker: tolerance must be either 0 or >= 1e-10 to avoid numerical precision issues');
128
+ throw new RangeError(
129
+ "douglasPeucker: tolerance must be either 0 or >= 1e-10 to avoid numerical precision issues",
130
+ );
99
131
  }
100
132
 
101
133
  if (points.length <= 2) {
@@ -192,32 +224,38 @@ function douglasPeuckerIterative(points, tolerance) {
192
224
  export function visvalingamWhyatt(points, minArea) {
193
225
  // Validate points parameter to prevent crashes on invalid input
194
226
  if (!Array.isArray(points)) {
195
- throw new TypeError('visvalingamWhyatt: points must be an array');
227
+ throw new TypeError("visvalingamWhyatt: points must be an array");
196
228
  }
197
229
  if (points.length === 0) {
198
- throw new RangeError('visvalingamWhyatt: points array cannot be empty');
230
+ throw new RangeError("visvalingamWhyatt: points array cannot be empty");
199
231
  }
200
232
 
201
233
  // Validate each point has x and y properties with finite numeric values
202
234
  for (let i = 0; i < points.length; i++) {
203
235
  const p = points[i];
204
- if (!p || typeof p !== 'object') {
205
- throw new TypeError(`visvalingamWhyatt: point at index ${i} must be an object`);
236
+ if (!p || typeof p !== "object") {
237
+ throw new TypeError(
238
+ `visvalingamWhyatt: point at index ${i} must be an object`,
239
+ );
206
240
  }
207
- if (typeof p.x !== 'number' || !Number.isFinite(p.x)) {
208
- throw new TypeError(`visvalingamWhyatt: point at index ${i} must have a finite numeric x property`);
241
+ if (typeof p.x !== "number" || !Number.isFinite(p.x)) {
242
+ throw new TypeError(
243
+ `visvalingamWhyatt: point at index ${i} must have a finite numeric x property`,
244
+ );
209
245
  }
210
- if (typeof p.y !== 'number' || !Number.isFinite(p.y)) {
211
- throw new TypeError(`visvalingamWhyatt: point at index ${i} must have a finite numeric y property`);
246
+ if (typeof p.y !== "number" || !Number.isFinite(p.y)) {
247
+ throw new TypeError(
248
+ `visvalingamWhyatt: point at index ${i} must have a finite numeric y property`,
249
+ );
212
250
  }
213
251
  }
214
252
 
215
253
  // Validate minArea parameter to ensure valid numeric threshold
216
- if (typeof minArea !== 'number' || !Number.isFinite(minArea)) {
217
- throw new TypeError('visvalingamWhyatt: minArea must be a finite number');
254
+ if (typeof minArea !== "number" || !Number.isFinite(minArea)) {
255
+ throw new TypeError("visvalingamWhyatt: minArea must be a finite number");
218
256
  }
219
257
  if (minArea < 0) {
220
- throw new RangeError('visvalingamWhyatt: minArea cannot be negative');
258
+ throw new RangeError("visvalingamWhyatt: minArea cannot be negative");
221
259
  }
222
260
 
223
261
  if (points.length <= 2) {
@@ -226,9 +264,10 @@ export function visvalingamWhyatt(points, minArea) {
226
264
 
227
265
  // Calculate triangle area for three points
228
266
  const triangleArea = (p1, p2, p3) => {
229
- return Math.abs(
230
- (p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y)
231
- ) / 2;
267
+ return (
268
+ Math.abs((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y)) /
269
+ 2
270
+ );
232
271
  };
233
272
 
234
273
  // Create a copy with area information
@@ -276,15 +315,24 @@ export function visvalingamWhyatt(points, minArea) {
276
315
  let prevPrevIndex = prevIndex - 1;
277
316
  while (prevPrevIndex >= 0 && !kept[prevPrevIndex]) prevPrevIndex--;
278
317
  if (prevPrevIndex >= 0 && nextIndex < pts.length) {
279
- areas[prevIndex] = triangleArea(pts[prevPrevIndex], pts[prevIndex], pts[nextIndex]);
318
+ areas[prevIndex] = triangleArea(
319
+ pts[prevPrevIndex],
320
+ pts[prevIndex],
321
+ pts[nextIndex],
322
+ );
280
323
  }
281
324
  }
282
325
 
283
326
  if (nextIndex < pts.length - 1) {
284
327
  let nextNextIndex = nextIndex + 1;
285
- while (nextNextIndex < pts.length && !kept[nextNextIndex]) nextNextIndex++;
328
+ while (nextNextIndex < pts.length && !kept[nextNextIndex])
329
+ nextNextIndex++;
286
330
  if (nextNextIndex < pts.length && prevIndex >= 0) {
287
- areas[nextIndex] = triangleArea(pts[prevIndex], pts[nextIndex], pts[nextNextIndex]);
331
+ areas[nextIndex] = triangleArea(
332
+ pts[prevIndex],
333
+ pts[nextIndex],
334
+ pts[nextNextIndex],
335
+ );
288
336
  }
289
337
  }
290
338
  }
@@ -300,37 +348,45 @@ export function visvalingamWhyatt(points, minArea) {
300
348
  * @param {'douglas-peucker' | 'visvalingam'} algorithm - Algorithm to use
301
349
  * @returns {Array<{x: number, y: number}>} Simplified points
302
350
  */
303
- export function simplifyPolyline(points, tolerance, algorithm = 'douglas-peucker') {
351
+ export function simplifyPolyline(
352
+ points,
353
+ tolerance,
354
+ algorithm = "douglas-peucker",
355
+ ) {
304
356
  // Validate points parameter to prevent crashes on invalid input
305
357
  if (!Array.isArray(points)) {
306
- throw new TypeError('simplifyPolyline: points must be an array');
358
+ throw new TypeError("simplifyPolyline: points must be an array");
307
359
  }
308
360
  if (points.length === 0) {
309
- throw new RangeError('simplifyPolyline: points array cannot be empty');
361
+ throw new RangeError("simplifyPolyline: points array cannot be empty");
310
362
  }
311
363
 
312
364
  // Validate tolerance parameter to ensure valid numeric threshold
313
- if (typeof tolerance !== 'number' || !Number.isFinite(tolerance)) {
314
- throw new TypeError('simplifyPolyline: tolerance must be a finite number');
365
+ if (typeof tolerance !== "number" || !Number.isFinite(tolerance)) {
366
+ throw new TypeError("simplifyPolyline: tolerance must be a finite number");
315
367
  }
316
368
  if (tolerance < 0) {
317
- throw new RangeError('simplifyPolyline: tolerance cannot be negative');
369
+ throw new RangeError("simplifyPolyline: tolerance cannot be negative");
318
370
  }
319
371
  // Prevent impractically small tolerances that cause numerical precision issues
320
372
  if (tolerance > 0 && tolerance < 1e-10) {
321
- throw new RangeError('simplifyPolyline: tolerance must be either 0 or >= 1e-10 to avoid numerical precision issues');
373
+ throw new RangeError(
374
+ "simplifyPolyline: tolerance must be either 0 or >= 1e-10 to avoid numerical precision issues",
375
+ );
322
376
  }
323
377
 
324
378
  // Validate algorithm parameter to ensure only valid algorithms are used
325
- if (typeof algorithm !== 'string') {
326
- throw new TypeError('simplifyPolyline: algorithm must be a string');
379
+ if (typeof algorithm !== "string") {
380
+ throw new TypeError("simplifyPolyline: algorithm must be a string");
327
381
  }
328
- const validAlgorithms = ['douglas-peucker', 'visvalingam'];
382
+ const validAlgorithms = ["douglas-peucker", "visvalingam"];
329
383
  if (!validAlgorithms.includes(algorithm)) {
330
- throw new RangeError(`simplifyPolyline: algorithm must be one of: ${validAlgorithms.join(', ')}`);
384
+ throw new RangeError(
385
+ `simplifyPolyline: algorithm must be one of: ${validAlgorithms.join(", ")}`,
386
+ );
331
387
  }
332
388
 
333
- if (algorithm === 'visvalingam') {
389
+ if (algorithm === "visvalingam") {
334
390
  // For Visvalingam, tolerance is the minimum triangle area
335
391
  return visvalingamWhyatt(points, tolerance * tolerance);
336
392
  }
@@ -345,23 +401,31 @@ export function simplifyPolyline(points, tolerance, algorithm = 'douglas-peucker
345
401
  export function extractPolylinePoints(commands) {
346
402
  // Validate commands parameter to prevent crashes on invalid input
347
403
  if (!Array.isArray(commands)) {
348
- throw new TypeError('extractPolylinePoints: commands must be an array');
404
+ throw new TypeError("extractPolylinePoints: commands must be an array");
349
405
  }
350
406
 
351
407
  const points = [];
352
- let cx = 0, cy = 0;
353
- let startX = 0, startY = 0;
408
+ let cx = 0,
409
+ cy = 0;
410
+ let startX = 0,
411
+ startY = 0;
354
412
 
355
413
  for (const cmd of commands) {
356
414
  // Validate each command object to prevent undefined access
357
- if (!cmd || typeof cmd !== 'object') {
358
- throw new TypeError('extractPolylinePoints: each command must be an object');
415
+ if (!cmd || typeof cmd !== "object") {
416
+ throw new TypeError(
417
+ "extractPolylinePoints: each command must be an object",
418
+ );
359
419
  }
360
- if (typeof cmd.command !== 'string') {
361
- throw new TypeError('extractPolylinePoints: each command must have a string "command" property');
420
+ if (typeof cmd.command !== "string") {
421
+ throw new TypeError(
422
+ 'extractPolylinePoints: each command must have a string "command" property',
423
+ );
362
424
  }
363
425
  if (!Array.isArray(cmd.args)) {
364
- throw new TypeError('extractPolylinePoints: each command must have an "args" array');
426
+ throw new TypeError(
427
+ 'extractPolylinePoints: each command must have an "args" array',
428
+ );
365
429
  }
366
430
 
367
431
  const { command, args } = cmd;
@@ -369,108 +433,149 @@ export function extractPolylinePoints(commands) {
369
433
  // Helper to validate args length to prevent out-of-bounds access
370
434
  const requireArgs = (count) => {
371
435
  if (args.length < count) {
372
- throw new RangeError(`extractPolylinePoints: command "${command}" requires at least ${count} arguments, got ${args.length}`);
436
+ throw new RangeError(
437
+ `extractPolylinePoints: command "${command}" requires at least ${count} arguments, got ${args.length}`,
438
+ );
373
439
  }
374
440
  };
375
441
 
376
442
  // Helper to validate arg is a finite number to prevent NaN/Infinity in calculations
377
443
  const requireFiniteNumber = (index) => {
378
- if (typeof args[index] !== 'number' || !Number.isFinite(args[index])) {
379
- throw new TypeError(`extractPolylinePoints: command "${command}" argument at index ${index} must be a finite number, got ${args[index]}`);
444
+ if (typeof args[index] !== "number" || !Number.isFinite(args[index])) {
445
+ throw new TypeError(
446
+ `extractPolylinePoints: command "${command}" argument at index ${index} must be a finite number, got ${args[index]}`,
447
+ );
380
448
  }
381
449
  };
382
450
 
383
451
  switch (command) {
384
- case 'M':
452
+ case "M":
385
453
  requireArgs(2);
386
- requireFiniteNumber(0); requireFiniteNumber(1);
387
- cx = args[0]; cy = args[1];
388
- startX = cx; startY = cy;
454
+ requireFiniteNumber(0);
455
+ requireFiniteNumber(1);
456
+ cx = args[0];
457
+ cy = args[1];
458
+ startX = cx;
459
+ startY = cy;
389
460
  points.push({ x: cx, y: cy });
390
461
  break;
391
- case 'm':
462
+ case "m":
392
463
  requireArgs(2);
393
- requireFiniteNumber(0); requireFiniteNumber(1);
394
- cx += args[0]; cy += args[1];
395
- startX = cx; startY = cy;
464
+ requireFiniteNumber(0);
465
+ requireFiniteNumber(1);
466
+ cx += args[0];
467
+ cy += args[1];
468
+ startX = cx;
469
+ startY = cy;
396
470
  points.push({ x: cx, y: cy });
397
471
  break;
398
- case 'L':
472
+ case "L":
399
473
  requireArgs(2);
400
- requireFiniteNumber(0); requireFiniteNumber(1);
401
- cx = args[0]; cy = args[1];
474
+ requireFiniteNumber(0);
475
+ requireFiniteNumber(1);
476
+ cx = args[0];
477
+ cy = args[1];
402
478
  points.push({ x: cx, y: cy });
403
479
  break;
404
- case 'l':
480
+ case "l":
405
481
  requireArgs(2);
406
- requireFiniteNumber(0); requireFiniteNumber(1);
407
- cx += args[0]; cy += args[1];
482
+ requireFiniteNumber(0);
483
+ requireFiniteNumber(1);
484
+ cx += args[0];
485
+ cy += args[1];
408
486
  points.push({ x: cx, y: cy });
409
487
  break;
410
- case 'H':
488
+ case "H":
411
489
  requireArgs(1);
412
490
  requireFiniteNumber(0);
413
491
  cx = args[0];
414
492
  points.push({ x: cx, y: cy });
415
493
  break;
416
- case 'h':
494
+ case "h":
417
495
  requireArgs(1);
418
496
  requireFiniteNumber(0);
419
497
  cx += args[0];
420
498
  points.push({ x: cx, y: cy });
421
499
  break;
422
- case 'V':
500
+ case "V":
423
501
  requireArgs(1);
424
502
  requireFiniteNumber(0);
425
503
  cy = args[0];
426
504
  points.push({ x: cx, y: cy });
427
505
  break;
428
- case 'v':
506
+ case "v":
429
507
  requireArgs(1);
430
508
  requireFiniteNumber(0);
431
509
  cy += args[0];
432
510
  points.push({ x: cx, y: cy });
433
511
  break;
434
- case 'Z':
435
- case 'z':
512
+ case "Z":
513
+ case "z":
436
514
  if (cx !== startX || cy !== startY) {
437
515
  points.push({ x: startX, y: startY });
438
516
  }
439
- cx = startX; cy = startY;
517
+ cx = startX;
518
+ cy = startY;
440
519
  break;
441
520
  // For curves (C, S, Q, T, A), we just track the endpoint
442
- case 'C':
521
+ case "C":
443
522
  requireArgs(6);
444
- requireFiniteNumber(4); requireFiniteNumber(5);
445
- cx = args[4]; cy = args[5]; break;
446
- case 'c':
523
+ requireFiniteNumber(4);
524
+ requireFiniteNumber(5);
525
+ cx = args[4];
526
+ cy = args[5];
527
+ break;
528
+ case "c":
447
529
  requireArgs(6);
448
- requireFiniteNumber(4); requireFiniteNumber(5);
449
- cx += args[4]; cy += args[5]; break;
450
- case 'S': case 'Q':
530
+ requireFiniteNumber(4);
531
+ requireFiniteNumber(5);
532
+ cx += args[4];
533
+ cy += args[5];
534
+ break;
535
+ case "S":
536
+ case "Q":
451
537
  requireArgs(4);
452
- requireFiniteNumber(2); requireFiniteNumber(3);
453
- cx = args[2]; cy = args[3]; break;
454
- case 's': case 'q':
538
+ requireFiniteNumber(2);
539
+ requireFiniteNumber(3);
540
+ cx = args[2];
541
+ cy = args[3];
542
+ break;
543
+ case "s":
544
+ case "q":
455
545
  requireArgs(4);
456
- requireFiniteNumber(2); requireFiniteNumber(3);
457
- cx += args[2]; cy += args[3]; break;
458
- case 'T':
546
+ requireFiniteNumber(2);
547
+ requireFiniteNumber(3);
548
+ cx += args[2];
549
+ cy += args[3];
550
+ break;
551
+ case "T":
459
552
  requireArgs(2);
460
- requireFiniteNumber(0); requireFiniteNumber(1);
461
- cx = args[0]; cy = args[1]; break;
462
- case 't':
553
+ requireFiniteNumber(0);
554
+ requireFiniteNumber(1);
555
+ cx = args[0];
556
+ cy = args[1];
557
+ break;
558
+ case "t":
463
559
  requireArgs(2);
464
- requireFiniteNumber(0); requireFiniteNumber(1);
465
- cx += args[0]; cy += args[1]; break;
466
- case 'A':
560
+ requireFiniteNumber(0);
561
+ requireFiniteNumber(1);
562
+ cx += args[0];
563
+ cy += args[1];
564
+ break;
565
+ case "A":
467
566
  requireArgs(7);
468
- requireFiniteNumber(5); requireFiniteNumber(6);
469
- cx = args[5]; cy = args[6]; break;
470
- case 'a':
567
+ requireFiniteNumber(5);
568
+ requireFiniteNumber(6);
569
+ cx = args[5];
570
+ cy = args[6];
571
+ break;
572
+ case "a":
471
573
  requireArgs(7);
472
- requireFiniteNumber(5); requireFiniteNumber(6);
473
- cx += args[5]; cy += args[6]; break;
574
+ requireFiniteNumber(5);
575
+ requireFiniteNumber(6);
576
+ cx += args[5];
577
+ cy += args[6];
578
+ break;
474
579
  default:
475
580
  break;
476
581
  }
@@ -488,42 +593,48 @@ export function extractPolylinePoints(commands) {
488
593
  export function rebuildPathFromPoints(points, closed = false) {
489
594
  // Validate points parameter to prevent crashes on invalid input
490
595
  if (!Array.isArray(points)) {
491
- throw new TypeError('rebuildPathFromPoints: points must be an array');
596
+ throw new TypeError("rebuildPathFromPoints: points must be an array");
492
597
  }
493
598
 
494
599
  if (points.length === 0) return [];
495
600
 
496
601
  // Validate closed parameter to ensure boolean type
497
- if (typeof closed !== 'boolean') {
498
- throw new TypeError('rebuildPathFromPoints: closed must be a boolean');
602
+ if (typeof closed !== "boolean") {
603
+ throw new TypeError("rebuildPathFromPoints: closed must be a boolean");
499
604
  }
500
605
 
501
606
  // Validate each point has x and y properties with finite numeric values
502
607
  for (let i = 0; i < points.length; i++) {
503
608
  const p = points[i];
504
- if (!p || typeof p !== 'object') {
505
- throw new TypeError(`rebuildPathFromPoints: point at index ${i} must be an object`);
609
+ if (!p || typeof p !== "object") {
610
+ throw new TypeError(
611
+ `rebuildPathFromPoints: point at index ${i} must be an object`,
612
+ );
506
613
  }
507
- if (typeof p.x !== 'number' || !Number.isFinite(p.x)) {
508
- throw new TypeError(`rebuildPathFromPoints: point at index ${i} must have a finite numeric x property`);
614
+ if (typeof p.x !== "number" || !Number.isFinite(p.x)) {
615
+ throw new TypeError(
616
+ `rebuildPathFromPoints: point at index ${i} must have a finite numeric x property`,
617
+ );
509
618
  }
510
- if (typeof p.y !== 'number' || !Number.isFinite(p.y)) {
511
- throw new TypeError(`rebuildPathFromPoints: point at index ${i} must have a finite numeric y property`);
619
+ if (typeof p.y !== "number" || !Number.isFinite(p.y)) {
620
+ throw new TypeError(
621
+ `rebuildPathFromPoints: point at index ${i} must have a finite numeric y property`,
622
+ );
512
623
  }
513
624
  }
514
625
 
515
626
  const commands = [];
516
627
 
517
628
  // First point is M
518
- commands.push({ command: 'M', args: [points[0].x, points[0].y] });
629
+ commands.push({ command: "M", args: [points[0].x, points[0].y] });
519
630
 
520
631
  // Remaining points are L
521
632
  for (let i = 1; i < points.length; i++) {
522
- commands.push({ command: 'L', args: [points[i].x, points[i].y] });
633
+ commands.push({ command: "L", args: [points[i].x, points[i].y] });
523
634
  }
524
635
 
525
636
  if (closed) {
526
- commands.push({ command: 'Z', args: [] });
637
+ commands.push({ command: "Z", args: [] });
527
638
  }
528
639
 
529
640
  return commands;
@@ -537,13 +648,24 @@ export function rebuildPathFromPoints(points, closed = false) {
537
648
  export function isPurePolyline(commands) {
538
649
  // Validate commands parameter to prevent crashes on invalid input
539
650
  if (!Array.isArray(commands)) {
540
- throw new TypeError('isPurePolyline: commands must be an array');
541
- }
542
-
543
- const polylineCommands = new Set(['M', 'm', 'L', 'l', 'H', 'h', 'V', 'v', 'Z', 'z']);
544
- return commands.every(cmd => {
651
+ throw new TypeError("isPurePolyline: commands must be an array");
652
+ }
653
+
654
+ const polylineCommands = new Set([
655
+ "M",
656
+ "m",
657
+ "L",
658
+ "l",
659
+ "H",
660
+ "h",
661
+ "V",
662
+ "v",
663
+ "Z",
664
+ "z",
665
+ ]);
666
+ return commands.every((cmd) => {
545
667
  // Validate each command has the required structure to prevent undefined access
546
- if (!cmd || typeof cmd !== 'object' || typeof cmd.command !== 'string') {
668
+ if (!cmd || typeof cmd !== "object" || typeof cmd.command !== "string") {
547
669
  return false;
548
670
  }
549
671
  return polylineCommands.has(cmd.command);
@@ -557,22 +679,28 @@ export function isPurePolyline(commands) {
557
679
  * @param {string} algorithm - Algorithm to use
558
680
  * @returns {{commands: Array<{command: string, args: number[]}>, simplified: boolean, originalPoints: number, simplifiedPoints: number}}
559
681
  */
560
- export function simplifyPath(commands, tolerance, algorithm = 'douglas-peucker') {
682
+ export function simplifyPath(
683
+ commands,
684
+ tolerance,
685
+ algorithm = "douglas-peucker",
686
+ ) {
561
687
  // Validate commands parameter to prevent crashes on invalid input
562
688
  if (!Array.isArray(commands)) {
563
- throw new TypeError('simplifyPath: commands must be an array');
689
+ throw new TypeError("simplifyPath: commands must be an array");
564
690
  }
565
691
 
566
692
  // Validate tolerance parameter to ensure valid numeric threshold
567
- if (typeof tolerance !== 'number' || !Number.isFinite(tolerance)) {
568
- throw new TypeError('simplifyPath: tolerance must be a finite number');
693
+ if (typeof tolerance !== "number" || !Number.isFinite(tolerance)) {
694
+ throw new TypeError("simplifyPath: tolerance must be a finite number");
569
695
  }
570
696
  if (tolerance < 0) {
571
- throw new RangeError('simplifyPath: tolerance cannot be negative');
697
+ throw new RangeError("simplifyPath: tolerance cannot be negative");
572
698
  }
573
699
  // Prevent impractically small tolerances that cause numerical precision issues
574
700
  if (tolerance > 0 && tolerance < 1e-10) {
575
- throw new RangeError('simplifyPath: tolerance must be either 0 or >= 1e-10 to avoid numerical precision issues');
701
+ throw new RangeError(
702
+ "simplifyPath: tolerance must be either 0 or >= 1e-10 to avoid numerical precision issues",
703
+ );
576
704
  }
577
705
 
578
706
  if (!isPurePolyline(commands) || commands.length < 3) {
@@ -580,7 +708,7 @@ export function simplifyPath(commands, tolerance, algorithm = 'douglas-peucker')
580
708
  commands,
581
709
  simplified: false,
582
710
  originalPoints: 0,
583
- simplifiedPoints: 0
711
+ simplifiedPoints: 0,
584
712
  };
585
713
  }
586
714
 
@@ -592,19 +720,22 @@ export function simplifyPath(commands, tolerance, algorithm = 'douglas-peucker')
592
720
  commands,
593
721
  simplified: false,
594
722
  originalPoints: originalCount,
595
- simplifiedPoints: originalCount
723
+ simplifiedPoints: originalCount,
596
724
  };
597
725
  }
598
726
 
599
727
  // Check if path is closed
600
- const isClosed = commands[commands.length - 1].command.toLowerCase() === 'z';
728
+ const isClosed = commands[commands.length - 1].command.toLowerCase() === "z";
601
729
 
602
730
  // For closed paths, remove duplicate endpoint to avoid degenerate zero-length line segment
603
731
  if (isClosed && points.length > 1) {
604
732
  const first = points[0];
605
733
  const last = points[points.length - 1];
606
734
  // Check if last point is duplicate of first (within floating point tolerance)
607
- if (Math.abs(last.x - first.x) < 1e-10 && Math.abs(last.y - first.y) < 1e-10) {
735
+ if (
736
+ Math.abs(last.x - first.x) < 1e-10 &&
737
+ Math.abs(last.y - first.y) < 1e-10
738
+ ) {
608
739
  points = points.slice(0, -1);
609
740
  }
610
741
  }
@@ -617,7 +748,7 @@ export function simplifyPath(commands, tolerance, algorithm = 'douglas-peucker')
617
748
  commands,
618
749
  simplified: false,
619
750
  originalPoints: originalCount,
620
- simplifiedPoints: originalCount
751
+ simplifiedPoints: originalCount,
621
752
  };
622
753
  }
623
754
 
@@ -627,7 +758,7 @@ export function simplifyPath(commands, tolerance, algorithm = 'douglas-peucker')
627
758
  commands: newCommands,
628
759
  simplified: true,
629
760
  originalPoints: originalCount,
630
- simplifiedPoints: simplifiedCount
761
+ simplifiedPoints: simplifiedCount,
631
762
  };
632
763
  }
633
764
 
@@ -639,5 +770,5 @@ export default {
639
770
  extractPolylinePoints,
640
771
  rebuildPathFromPoints,
641
772
  isPurePolyline,
642
- simplifyPath
773
+ simplifyPath,
643
774
  };