@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
@@ -20,7 +20,7 @@
20
20
  * @module animation-optimization
21
21
  */
22
22
 
23
- import Decimal from 'decimal.js';
23
+ import Decimal from "decimal.js";
24
24
 
25
25
  // Configure Decimal for high precision internally
26
26
  Decimal.set({ precision: 20, rounding: Decimal.ROUND_HALF_UP });
@@ -32,9 +32,9 @@ Decimal.set({ precision: 20, rounding: Decimal.ROUND_HALF_UP });
32
32
  export const STANDARD_EASINGS = {
33
33
  linear: [0, 0, 1, 1],
34
34
  ease: [0.25, 0.1, 0.25, 1],
35
- 'ease-in': [0.42, 0, 1, 1],
36
- 'ease-out': [0, 0, 0.58, 1],
37
- 'ease-in-out': [0.42, 0, 0.58, 1],
35
+ "ease-in": [0.42, 0, 1, 1],
36
+ "ease-out": [0, 0, 0.58, 1],
37
+ "ease-in-out": [0.42, 0, 0.58, 1],
38
38
  };
39
39
 
40
40
  /**
@@ -46,18 +46,26 @@ export const STANDARD_EASINGS = {
46
46
  export function formatSplineValue(value, precision = 3) {
47
47
  // Validate value parameter
48
48
  if (value === null || value === undefined) {
49
- throw new Error('formatSplineValue: value parameter is required');
49
+ throw new Error("formatSplineValue: value parameter is required");
50
50
  }
51
51
 
52
52
  // Validate precision parameter
53
- if (typeof precision !== 'number' || precision < 0 || !Number.isFinite(precision)) {
54
- throw new Error('formatSplineValue: precision must be a non-negative finite number');
53
+ if (
54
+ typeof precision !== "number" ||
55
+ precision < 0 ||
56
+ !Number.isFinite(precision)
57
+ ) {
58
+ throw new Error(
59
+ "formatSplineValue: precision must be a non-negative finite number",
60
+ );
55
61
  }
56
62
 
57
63
  // Check for NaN/Infinity before creating Decimal
58
- const numValue = typeof value === 'number' ? value : parseFloat(value);
64
+ const numValue = typeof value === "number" ? value : parseFloat(value);
59
65
  if (!Number.isFinite(numValue)) {
60
- throw new Error(`formatSplineValue: value must be a finite number, got ${value}`);
66
+ throw new Error(
67
+ `formatSplineValue: value must be a finite number, got ${value}`,
68
+ );
61
69
  }
62
70
 
63
71
  const num = new Decimal(value);
@@ -69,19 +77,19 @@ export function formatSplineValue(value, precision = 3) {
69
77
  let str = rounded.toString();
70
78
 
71
79
  // Remove trailing zeros after decimal point
72
- if (str.includes('.')) {
73
- str = str.replace(/\.?0+$/, '');
80
+ if (str.includes(".")) {
81
+ str = str.replace(/\.?0+$/, "");
74
82
  }
75
83
 
76
84
  // Remove leading zero for values between -1 and 1 (exclusive)
77
- if (str.startsWith('0.')) {
85
+ if (str.startsWith("0.")) {
78
86
  str = str.substring(1); // "0.5" -> ".5"
79
- } else if (str.startsWith('-0.')) {
80
- str = '-' + str.substring(2); // "-0.5" -> "-.5"
87
+ } else if (str.startsWith("-0.")) {
88
+ str = "-" + str.substring(2); // "-0.5" -> "-.5"
81
89
  }
82
90
 
83
91
  // Handle edge cases: ".0" should be "0", "-0" should be "0"
84
- if (str === '' || str === '.' || str === '-0') str = '0';
92
+ if (str === "" || str === "." || str === "-0") str = "0";
85
93
 
86
94
  return str;
87
95
  }
@@ -93,25 +101,30 @@ export function formatSplineValue(value, precision = 3) {
93
101
  * @returns {number[][]} Array of [x1, y1, x2, y2] arrays
94
102
  */
95
103
  export function parseKeySplines(keySplines) {
96
- if (!keySplines || typeof keySplines !== 'string') return [];
104
+ if (!keySplines || typeof keySplines !== "string") return [];
97
105
 
98
106
  // Split by semicolon to get individual splines
99
- const splines = keySplines.split(';').map(s => s.trim()).filter(s => s);
100
-
101
- return splines
102
- .map(spline => {
103
- // Split by whitespace or comma to get control points
104
- const values = spline.split(/[\s,]+/)
105
- .map(v => parseFloat(v))
106
- .filter(v => Number.isFinite(v)); // Filter out NaN and Infinity
107
-
108
- // Each spline must have exactly 4 control points
109
- if (values.length !== 4) {
110
- throw new Error(`parseKeySplines: invalid spline "${spline}", expected 4 values, got ${values.length}`);
111
- }
107
+ const splines = keySplines
108
+ .split(";")
109
+ .map((s) => s.trim())
110
+ .filter((s) => s);
111
+
112
+ return splines.map((spline) => {
113
+ // Split by whitespace or comma to get control points
114
+ const values = spline
115
+ .split(/[\s,]+/)
116
+ .map((v) => parseFloat(v))
117
+ .filter((v) => Number.isFinite(v)); // Filter out NaN and Infinity
118
+
119
+ // Each spline must have exactly 4 control points
120
+ if (values.length !== 4) {
121
+ throw new Error(
122
+ `parseKeySplines: invalid spline "${spline}", expected 4 values, got ${values.length}`,
123
+ );
124
+ }
112
125
 
113
- return values;
114
- });
126
+ return values;
127
+ });
115
128
  }
116
129
 
117
130
  /**
@@ -123,25 +136,37 @@ export function parseKeySplines(keySplines) {
123
136
  export function serializeKeySplines(splines, precision = 3) {
124
137
  // Validate splines parameter
125
138
  if (!Array.isArray(splines)) {
126
- throw new Error('serializeKeySplines: splines must be an array');
139
+ throw new Error("serializeKeySplines: splines must be an array");
127
140
  }
128
141
 
129
142
  // Validate precision parameter
130
- if (typeof precision !== 'number' || precision < 0 || !Number.isFinite(precision)) {
131
- throw new Error('serializeKeySplines: precision must be a non-negative finite number');
143
+ if (
144
+ typeof precision !== "number" ||
145
+ precision < 0 ||
146
+ !Number.isFinite(precision)
147
+ ) {
148
+ throw new Error(
149
+ "serializeKeySplines: precision must be a non-negative finite number",
150
+ );
132
151
  }
133
152
 
134
- return splines.map((spline, index) => {
135
- // Validate each spline is an array with 4 values
136
- if (!Array.isArray(spline)) {
137
- throw new Error(`serializeKeySplines: spline at index ${index} must be an array`);
138
- }
139
- if (spline.length !== 4) {
140
- throw new Error(`serializeKeySplines: spline at index ${index} must have exactly 4 values, got ${spline.length}`);
141
- }
153
+ return splines
154
+ .map((spline, index) => {
155
+ // Validate each spline is an array with 4 values
156
+ if (!Array.isArray(spline)) {
157
+ throw new Error(
158
+ `serializeKeySplines: spline at index ${index} must be an array`,
159
+ );
160
+ }
161
+ if (spline.length !== 4) {
162
+ throw new Error(
163
+ `serializeKeySplines: spline at index ${index} must have exactly 4 values, got ${spline.length}`,
164
+ );
165
+ }
142
166
 
143
- return spline.map(v => formatSplineValue(v, precision)).join(' ');
144
- }).join('; ');
167
+ return spline.map((v) => formatSplineValue(v, precision)).join(" ");
168
+ })
169
+ .join("; ");
145
170
  }
146
171
 
147
172
  /**
@@ -155,14 +180,25 @@ export function isLinearSpline(spline, tolerance = 0.001) {
155
180
  if (!Array.isArray(spline) || spline.length !== 4) return false;
156
181
 
157
182
  // Validate tolerance parameter
158
- if (typeof tolerance !== 'number' || tolerance < 0 || !Number.isFinite(tolerance)) {
159
- throw new Error('isLinearSpline: tolerance must be a non-negative finite number');
183
+ if (
184
+ typeof tolerance !== "number" ||
185
+ tolerance < 0 ||
186
+ !Number.isFinite(tolerance)
187
+ ) {
188
+ throw new Error(
189
+ "isLinearSpline: tolerance must be a non-negative finite number",
190
+ );
160
191
  }
161
192
 
162
193
  const [x1, y1, x2, y2] = spline;
163
194
 
164
195
  // Check for NaN values in spline
165
- if (!Number.isFinite(x1) || !Number.isFinite(y1) || !Number.isFinite(x2) || !Number.isFinite(y2)) {
196
+ if (
197
+ !Number.isFinite(x1) ||
198
+ !Number.isFinite(y1) ||
199
+ !Number.isFinite(x2) ||
200
+ !Number.isFinite(y2)
201
+ ) {
166
202
  return false;
167
203
  }
168
204
 
@@ -183,7 +219,7 @@ export function areAllSplinesLinear(keySplines) {
183
219
  const splines = parseKeySplines(keySplines);
184
220
  if (splines.length === 0) return false;
185
221
  // Must wrap in arrow function to avoid .every() passing index as tolerance
186
- return splines.every(s => isLinearSpline(s));
222
+ return splines.every((s) => isLinearSpline(s));
187
223
  }
188
224
 
189
225
  /**
@@ -197,15 +233,23 @@ export function identifyStandardEasing(spline, tolerance = 0.01) {
197
233
  if (!Array.isArray(spline) || spline.length !== 4) return null;
198
234
 
199
235
  // Validate tolerance parameter
200
- if (typeof tolerance !== 'number' || tolerance < 0 || !Number.isFinite(tolerance)) {
201
- throw new Error('identifyStandardEasing: tolerance must be a non-negative finite number');
236
+ if (
237
+ typeof tolerance !== "number" ||
238
+ tolerance < 0 ||
239
+ !Number.isFinite(tolerance)
240
+ ) {
241
+ throw new Error(
242
+ "identifyStandardEasing: tolerance must be a non-negative finite number",
243
+ );
202
244
  }
203
245
 
204
246
  // Check for NaN values in spline
205
- if (!spline.every(val => Number.isFinite(val))) return null;
247
+ if (!spline.every((val) => Number.isFinite(val))) return null;
206
248
 
207
249
  for (const [name, standard] of Object.entries(STANDARD_EASINGS)) {
208
- const matches = spline.every((val, i) => Math.abs(val - standard[i]) < tolerance);
250
+ const matches = spline.every(
251
+ (val, i) => Math.abs(val - standard[i]) < tolerance,
252
+ );
209
253
  if (matches) return name;
210
254
  }
211
255
  return null;
@@ -230,10 +274,12 @@ export function optimizeKeySplines(keySplines, options = {}) {
230
274
  }
231
275
 
232
276
  // Must wrap in arrow function to avoid .every() passing index as tolerance
233
- const allLinear = splines.every(s => isLinearSpline(s));
277
+ const allLinear = splines.every((s) => isLinearSpline(s));
234
278
 
235
279
  // Identify standard easings
236
- const standardEasings = splines.map(s => identifyStandardEasing(s)).filter(Boolean);
280
+ const standardEasings = splines
281
+ .map((s) => identifyStandardEasing(s))
282
+ .filter(Boolean);
237
283
 
238
284
  // If all linear and removeLinear is true, suggest removing keySplines
239
285
  if (allLinear && removeLinear) {
@@ -252,8 +298,11 @@ export function optimizeKeySplines(keySplines, options = {}) {
252
298
  * @returns {number[]} Array of time values
253
299
  */
254
300
  export function parseKeyTimes(keyTimes) {
255
- if (!keyTimes || typeof keyTimes !== 'string') return [];
256
- return keyTimes.split(';').map(s => parseFloat(s.trim())).filter(v => Number.isFinite(v));
301
+ if (!keyTimes || typeof keyTimes !== "string") return [];
302
+ return keyTimes
303
+ .split(";")
304
+ .map((s) => parseFloat(s.trim()))
305
+ .filter((v) => Number.isFinite(v));
257
306
  }
258
307
 
259
308
  /**
@@ -265,18 +314,24 @@ export function parseKeyTimes(keyTimes) {
265
314
  export function serializeKeyTimes(times, precision = 3) {
266
315
  // Validate times parameter
267
316
  if (!Array.isArray(times)) {
268
- throw new Error('serializeKeyTimes: times must be an array');
317
+ throw new Error("serializeKeyTimes: times must be an array");
269
318
  }
270
319
 
271
320
  // Validate precision parameter
272
- if (typeof precision !== 'number' || precision < 0 || !Number.isFinite(precision)) {
273
- throw new Error('serializeKeyTimes: precision must be a non-negative finite number');
321
+ if (
322
+ typeof precision !== "number" ||
323
+ precision < 0 ||
324
+ !Number.isFinite(precision)
325
+ ) {
326
+ throw new Error(
327
+ "serializeKeyTimes: precision must be a non-negative finite number",
328
+ );
274
329
  }
275
330
 
276
331
  // Return empty string for empty array
277
- if (times.length === 0) return '';
332
+ if (times.length === 0) return "";
278
333
 
279
- return times.map(t => formatSplineValue(t, precision)).join('; ');
334
+ return times.map((t) => formatSplineValue(t, precision)).join("; ");
280
335
  }
281
336
 
282
337
  /**
@@ -288,12 +343,18 @@ export function serializeKeyTimes(times, precision = 3) {
288
343
  export function optimizeKeyTimes(keyTimes, precision = 3) {
289
344
  // Validate keyTimes parameter
290
345
  if (keyTimes === null || keyTimes === undefined) {
291
- throw new Error('optimizeKeyTimes: keyTimes parameter is required');
346
+ throw new Error("optimizeKeyTimes: keyTimes parameter is required");
292
347
  }
293
348
 
294
349
  // Validate precision parameter
295
- if (typeof precision !== 'number' || precision < 0 || !Number.isFinite(precision)) {
296
- throw new Error('optimizeKeyTimes: precision must be a non-negative finite number');
350
+ if (
351
+ typeof precision !== "number" ||
352
+ precision < 0 ||
353
+ !Number.isFinite(precision)
354
+ ) {
355
+ throw new Error(
356
+ "optimizeKeyTimes: precision must be a non-negative finite number",
357
+ );
297
358
  }
298
359
 
299
360
  const times = parseKeyTimes(keyTimes);
@@ -309,39 +370,45 @@ export function optimizeKeyTimes(keyTimes, precision = 3) {
309
370
  * @returns {string} Optimized values
310
371
  */
311
372
  export function optimizeAnimationValues(values, precision = 3) {
312
- if (!values || typeof values !== 'string') return values;
373
+ if (!values || typeof values !== "string") return values;
313
374
 
314
375
  // Validate precision parameter
315
- if (typeof precision !== 'number' || precision < 0 || !Number.isFinite(precision)) {
316
- throw new Error('optimizeAnimationValues: precision must be a non-negative finite number');
376
+ if (
377
+ typeof precision !== "number" ||
378
+ precision < 0 ||
379
+ !Number.isFinite(precision)
380
+ ) {
381
+ throw new Error(
382
+ "optimizeAnimationValues: precision must be a non-negative finite number",
383
+ );
317
384
  }
318
385
 
319
386
  // Split by semicolon
320
- const parts = values.split(';');
387
+ const parts = values.split(";");
321
388
 
322
389
  // Handle empty values string
323
390
  if (parts.length === 0) return values;
324
391
 
325
- const optimized = parts.map(part => {
392
+ const optimized = parts.map((part) => {
326
393
  const trimmed = part.trim();
327
394
 
328
395
  // Preserve ID references exactly
329
- if (trimmed.startsWith('#') || trimmed.includes('url(')) {
396
+ if (trimmed.startsWith("#") || trimmed.includes("url(")) {
330
397
  return trimmed;
331
398
  }
332
399
 
333
400
  // Try to parse as numbers (could be space-separated like "0 0" for translate)
334
- const nums = trimmed.split(/[\s,]+/).filter(n => n); // Filter empty strings
335
- const optimizedNums = nums.map(n => {
401
+ const nums = trimmed.split(/[\s,]+/).filter((n) => n); // Filter empty strings
402
+ const optimizedNums = nums.map((n) => {
336
403
  const num = parseFloat(n);
337
404
  if (!Number.isFinite(num)) return n; // Not a finite number, preserve as-is
338
405
  return formatSplineValue(num, precision);
339
406
  });
340
407
 
341
- return optimizedNums.join(' ');
408
+ return optimizedNums.join(" ");
342
409
  });
343
410
 
344
- return optimized.join('; ');
411
+ return optimized.join("; ");
345
412
  }
346
413
 
347
414
  /**
@@ -352,11 +419,19 @@ export function optimizeAnimationValues(values, precision = 3) {
352
419
  */
353
420
  export function optimizeElementTiming(el, options = {}) {
354
421
  // Validate el parameter
355
- if (!el || typeof el !== 'object') {
356
- throw new Error('optimizeElementTiming: el parameter must be a valid element');
422
+ if (!el || typeof el !== "object") {
423
+ throw new Error(
424
+ "optimizeElementTiming: el parameter must be a valid element",
425
+ );
357
426
  }
358
- if (typeof el.getAttribute !== 'function' || typeof el.setAttribute !== 'function' || typeof el.removeAttribute !== 'function') {
359
- throw new Error('optimizeElementTiming: el parameter must have getAttribute, setAttribute, and removeAttribute methods');
427
+ if (
428
+ typeof el.getAttribute !== "function" ||
429
+ typeof el.setAttribute !== "function" ||
430
+ typeof el.removeAttribute !== "function"
431
+ ) {
432
+ throw new Error(
433
+ "optimizeElementTiming: el parameter must have getAttribute, setAttribute, and removeAttribute methods",
434
+ );
360
435
  }
361
436
 
362
437
  const precision = options.precision ?? 3;
@@ -364,40 +439,49 @@ export function optimizeElementTiming(el, options = {}) {
364
439
  const optimizeValues = options.optimizeValues !== false;
365
440
 
366
441
  // Validate precision from options
367
- if (typeof precision !== 'number' || precision < 0 || !Number.isFinite(precision)) {
368
- throw new Error('optimizeElementTiming: options.precision must be a non-negative finite number');
442
+ if (
443
+ typeof precision !== "number" ||
444
+ precision < 0 ||
445
+ !Number.isFinite(precision)
446
+ ) {
447
+ throw new Error(
448
+ "optimizeElementTiming: options.precision must be a non-negative finite number",
449
+ );
369
450
  }
370
451
 
371
452
  const changes = [];
372
453
  let modified = false;
373
454
 
374
455
  // Optimize keySplines
375
- const keySplines = el.getAttribute('keySplines');
456
+ const keySplines = el.getAttribute("keySplines");
376
457
  if (keySplines) {
377
- const result = optimizeKeySplines(keySplines, { precision, removeLinear: removeLinearSplines });
458
+ const result = optimizeKeySplines(keySplines, {
459
+ precision,
460
+ removeLinear: removeLinearSplines,
461
+ });
378
462
 
379
463
  if (result.allLinear && removeLinearSplines) {
380
464
  // All splines are linear - can simplify to calcMode="linear"
381
- const calcMode = el.getAttribute('calcMode');
382
- if (calcMode === 'spline') {
383
- el.setAttribute('calcMode', 'linear');
384
- el.removeAttribute('keySplines');
465
+ const calcMode = el.getAttribute("calcMode");
466
+ if (calcMode === "spline") {
467
+ el.setAttribute("calcMode", "linear");
468
+ el.removeAttribute("keySplines");
385
469
  changes.push('Converted linear splines to calcMode="linear"');
386
470
  modified = true;
387
471
  }
388
472
  } else if (result.value && result.value !== keySplines) {
389
- el.setAttribute('keySplines', result.value);
473
+ el.setAttribute("keySplines", result.value);
390
474
  changes.push(`keySplines: "${keySplines}" -> "${result.value}"`);
391
475
  modified = true;
392
476
  }
393
477
  }
394
478
 
395
479
  // Optimize keyTimes
396
- const keyTimes = el.getAttribute('keyTimes');
480
+ const keyTimes = el.getAttribute("keyTimes");
397
481
  if (keyTimes) {
398
482
  const optimized = optimizeKeyTimes(keyTimes, precision);
399
483
  if (optimized !== keyTimes) {
400
- el.setAttribute('keyTimes', optimized);
484
+ el.setAttribute("keyTimes", optimized);
401
485
  changes.push(`keyTimes: "${keyTimes}" -> "${optimized}"`);
402
486
  modified = true;
403
487
  }
@@ -405,11 +489,11 @@ export function optimizeElementTiming(el, options = {}) {
405
489
 
406
490
  // Optimize values (optimizeAnimationValues internally preserves ID refs)
407
491
  if (optimizeValues) {
408
- const values = el.getAttribute('values');
492
+ const values = el.getAttribute("values");
409
493
  if (values) {
410
494
  const optimized = optimizeAnimationValues(values, precision);
411
495
  if (optimized !== values) {
412
- el.setAttribute('values', optimized);
496
+ el.setAttribute("values", optimized);
413
497
  changes.push(`values: "${values}" -> "${optimized}"`);
414
498
  modified = true;
415
499
  }
@@ -417,7 +501,7 @@ export function optimizeElementTiming(el, options = {}) {
417
501
  }
418
502
 
419
503
  // Optimize from/to/by (optimizeAnimationValues internally preserves ID refs)
420
- for (const attr of ['from', 'to', 'by']) {
504
+ for (const attr of ["from", "to", "by"]) {
421
505
  const val = el.getAttribute(attr);
422
506
  if (val) {
423
507
  const optimized = optimizeAnimationValues(val, precision);
@@ -436,7 +520,13 @@ export function optimizeElementTiming(el, options = {}) {
436
520
  * Animation elements that can have timing attributes
437
521
  * Note: all lowercase to match svg-parser tagName normalization
438
522
  */
439
- export const ANIMATION_ELEMENTS = ['animate', 'animatetransform', 'animatemotion', 'animatecolor', 'set'];
523
+ export const ANIMATION_ELEMENTS = [
524
+ "animate",
525
+ "animatetransform",
526
+ "animatemotion",
527
+ "animatecolor",
528
+ "set",
529
+ ];
440
530
 
441
531
  /**
442
532
  * Optimize all animation timing in an SVG document
@@ -446,8 +536,10 @@ export const ANIMATION_ELEMENTS = ['animate', 'animatetransform', 'animatemotion
446
536
  */
447
537
  export function optimizeDocumentAnimationTiming(root, options = {}) {
448
538
  // Validate root parameter
449
- if (!root || typeof root !== 'object') {
450
- throw new Error('optimizeDocumentAnimationTiming: root parameter must be a valid element');
539
+ if (!root || typeof root !== "object") {
540
+ throw new Error(
541
+ "optimizeDocumentAnimationTiming: root parameter must be a valid element",
542
+ );
451
543
  }
452
544
 
453
545
  let elementsModified = 0;
@@ -456,7 +548,7 @@ export function optimizeDocumentAnimationTiming(root, options = {}) {
456
548
 
457
549
  const processElement = (el) => {
458
550
  // Skip if not a valid element
459
- if (!el || typeof el !== 'object') return;
551
+ if (!el || typeof el !== "object") return;
460
552
 
461
553
  const tagName = el.tagName?.toLowerCase();
462
554
 
@@ -470,8 +562,8 @@ export function optimizeDocumentAnimationTiming(root, options = {}) {
470
562
  totalChanges += result.changes.length;
471
563
  details.push({
472
564
  element: tagName,
473
- id: el.getAttribute('id') || null,
474
- changes: result.changes
565
+ id: el.getAttribute("id") || null,
566
+ changes: result.changes,
475
567
  });
476
568
  }
477
569
  }
@@ -642,7 +642,9 @@ export function getIdReferenceInfo(root, id) {
642
642
  export function findUnreferencedDefs(root) {
643
643
  // Validate input parameter
644
644
  if (!root || typeof root !== "object" || !(root instanceof SVGElement)) {
645
- throw new TypeError("findUnreferencedDefs: root must be a valid SVGElement");
645
+ throw new TypeError(
646
+ "findUnreferencedDefs: root must be a valid SVGElement",
647
+ );
646
648
  }
647
649
 
648
650
  const refs = collectAllReferences(root);
@@ -668,7 +670,10 @@ export function findUnreferencedDefs(root) {
668
670
 
669
671
  for (const defs of defsElements) {
670
672
  // Validate defs.children exists and is iterable
671
- if (!defs.children || typeof defs.children[Symbol.iterator] !== "function") {
673
+ if (
674
+ !defs.children ||
675
+ typeof defs.children[Symbol.iterator] !== "function"
676
+ ) {
672
677
  continue; // Skip this defs element if children is not iterable
673
678
  }
674
679
 
@@ -728,7 +733,10 @@ export function removeUnreferencedDefsSafe(root) {
728
733
 
729
734
  for (const defs of defsElements) {
730
735
  // Validate defs.children exists and is iterable before spreading
731
- if (!defs.children || typeof defs.children[Symbol.iterator] !== "function") {
736
+ if (
737
+ !defs.children ||
738
+ typeof defs.children[Symbol.iterator] !== "function"
739
+ ) {
732
740
  continue; // Skip this defs element if children is not iterable
733
741
  }
734
742
 
package/src/arc-length.js CHANGED
@@ -103,7 +103,7 @@ const DEFAULT_ARC_LENGTH_TOLERANCE = "1e-30";
103
103
  * differ by less than this, we accept the higher-order result.
104
104
  * NOTE: Currently unused - kept for potential future enhancement.
105
105
  */
106
-
106
+
107
107
  const _SUBDIVISION_CONVERGENCE_THRESHOLD = new Decimal("1e-15");
108
108
 
109
109
  /**
@@ -255,9 +255,7 @@ function adaptiveQuadrature(f, a, b, tol, maxDepth, minDepth, depth) {
255
255
  throw new Error("adaptiveQuadrature: b must be a Decimal");
256
256
  }
257
257
  if (!tol || !(tol instanceof Decimal) || tol.lte(0)) {
258
- throw new Error(
259
- "adaptiveQuadrature: tol must be a positive Decimal",
260
- );
258
+ throw new Error("adaptiveQuadrature: tol must be a positive Decimal");
261
259
  }
262
260
  if (
263
261
  typeof maxDepth !== "number" ||
@@ -277,14 +275,8 @@ function adaptiveQuadrature(f, a, b, tol, maxDepth, minDepth, depth) {
277
275
  "adaptiveQuadrature: minDepth must be a non-negative integer",
278
276
  );
279
277
  }
280
- if (
281
- typeof depth !== "number" ||
282
- depth < 0 ||
283
- !Number.isInteger(depth)
284
- ) {
285
- throw new Error(
286
- "adaptiveQuadrature: depth must be a non-negative integer",
287
- );
278
+ if (typeof depth !== "number" || depth < 0 || !Number.isInteger(depth)) {
279
+ throw new Error("adaptiveQuadrature: depth must be a non-negative integer");
288
280
  }
289
281
 
290
282
  // Compute integral using 5-point and 10-point rules
@@ -354,7 +346,12 @@ function gaussLegendre(f, a, b, order) {
354
346
 
355
347
  // INTERNAL CONSISTENCY CHECK: Verify Gauss-Legendre table has correct structure
356
348
  // WHY: Ensures the precomputed tables haven't been corrupted or misconfigured
357
- if (!gl.nodes || !gl.weights || gl.nodes.length !== order || gl.weights.length !== order) {
349
+ if (
350
+ !gl.nodes ||
351
+ !gl.weights ||
352
+ gl.nodes.length !== order ||
353
+ gl.weights.length !== order
354
+ ) {
358
355
  throw new Error(
359
356
  `gaussLegendre: GAUSS_LEGENDRE[${order}] table is malformed (expected ${order} nodes and weights)`,
360
357
  );
@@ -385,16 +382,24 @@ function gaussLegendre(f, a, b, order) {
385
382
  // VALIDATION: Ensure function returns a valid Decimal
386
383
  // WHY: If f returns null, undefined, NaN, or non-Decimal, arithmetic operations will fail
387
384
  if (fValue === null || fValue === undefined) {
388
- throw new Error("gaussLegendre: integrand function f returned null or undefined");
385
+ throw new Error(
386
+ "gaussLegendre: integrand function f returned null or undefined",
387
+ );
389
388
  }
390
389
  if (!(fValue instanceof Decimal)) {
391
- throw new Error("gaussLegendre: integrand function f must return a Decimal instance");
390
+ throw new Error(
391
+ "gaussLegendre: integrand function f must return a Decimal instance",
392
+ );
392
393
  }
393
394
  if (fValue.isNaN()) {
394
- throw new Error(`gaussLegendre: integrand function f returned NaN at t=${t}`);
395
+ throw new Error(
396
+ `gaussLegendre: integrand function f returned NaN at t=${t}`,
397
+ );
395
398
  }
396
399
  if (!fValue.isFinite()) {
397
- throw new Error(`gaussLegendre: integrand function f returned non-finite value at t=${t}`);
400
+ throw new Error(
401
+ `gaussLegendre: integrand function f returned non-finite value at t=${t}`,
402
+ );
398
403
  }
399
404
 
400
405
  sum = sum.plus(weight.times(fValue));
@@ -1112,9 +1117,7 @@ export function verifyArcLengthTable(points, samples = 50) {
1112
1117
  !Number.isInteger(samples) ||
1113
1118
  samples < 2
1114
1119
  ) {
1115
- throw new Error(
1116
- "verifyArcLengthTable: samples must be an integer >= 2",
1117
- );
1120
+ throw new Error("verifyArcLengthTable: samples must be an integer >= 2");
1118
1121
  }
1119
1122
 
1120
1123
  const errors = [];