@emasoft/svg-matrix 1.0.30 → 1.0.31
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 +310 -61
- package/bin/svglinter.cjs +102 -3
- package/bin/svgm.js +236 -27
- package/package.json +1 -1
- package/src/animation-optimization.js +137 -17
- package/src/animation-references.js +123 -6
- package/src/arc-length.js +213 -4
- package/src/bezier-analysis.js +217 -21
- package/src/bezier-intersections.js +275 -12
- package/src/browser-verify.js +237 -4
- package/src/clip-path-resolver.js +168 -0
- package/src/convert-path-data.js +479 -28
- package/src/css-specificity.js +73 -10
- package/src/douglas-peucker.js +219 -2
- package/src/flatten-pipeline.js +284 -26
- package/src/geometry-to-path.js +250 -25
- package/src/gjk-collision.js +236 -33
- package/src/index.js +261 -3
- package/src/inkscape-support.js +86 -28
- package/src/logger.js +48 -3
- package/src/marker-resolver.js +278 -74
- package/src/mask-resolver.js +265 -66
- package/src/matrix.js +44 -5
- package/src/mesh-gradient.js +352 -102
- package/src/off-canvas-detection.js +382 -13
- package/src/path-analysis.js +192 -18
- package/src/path-data-plugins.js +309 -5
- package/src/path-optimization.js +129 -5
- package/src/path-simplification.js +188 -32
- package/src/pattern-resolver.js +454 -106
- package/src/polygon-clip.js +324 -1
- package/src/svg-boolean-ops.js +226 -9
- package/src/svg-collections.js +7 -5
- package/src/svg-flatten.js +386 -62
- package/src/svg-parser.js +179 -8
- package/src/svg-rendering-context.js +235 -6
- package/src/svg-toolbox.js +45 -8
- package/src/svg2-polyfills.js +40 -10
- package/src/transform-decomposition.js +258 -32
- package/src/transform-optimization.js +259 -13
- package/src/transforms2d.js +82 -9
- package/src/transforms3d.js +62 -10
- package/src/use-symbol-resolver.js +286 -42
- package/src/vector.js +64 -8
- package/src/verification.js +392 -1
package/src/path-optimization.js
CHANGED
|
@@ -70,6 +70,8 @@ const DEFAULT_TOLERANCE = new Decimal('1e-10');
|
|
|
70
70
|
* @returns {{x: Decimal, y: Decimal}} Point object
|
|
71
71
|
*/
|
|
72
72
|
export function point(x, y) {
|
|
73
|
+
if (x === null || x === undefined) throw new Error('point: x coordinate is required');
|
|
74
|
+
if (y === null || y === undefined) throw new Error('point: y coordinate is required');
|
|
73
75
|
return { x: D(x), y: D(y) };
|
|
74
76
|
}
|
|
75
77
|
|
|
@@ -80,8 +82,16 @@ export function point(x, y) {
|
|
|
80
82
|
* @returns {Decimal} Distance
|
|
81
83
|
*/
|
|
82
84
|
export function distance(p1, p2) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
if (!p1 || !p2) throw new Error('distance: both points are required');
|
|
86
|
+
if (p1.x === undefined || p1.y === undefined) throw new Error('distance: p1 must have x and y properties');
|
|
87
|
+
if (p2.x === undefined || p2.y === undefined) throw new Error('distance: p2 must have x and y properties');
|
|
88
|
+
// Ensure coordinates are Decimal objects
|
|
89
|
+
const x1 = p1.x instanceof Decimal ? p1.x : D(p1.x);
|
|
90
|
+
const y1 = p1.y instanceof Decimal ? p1.y : D(p1.y);
|
|
91
|
+
const x2 = p2.x instanceof Decimal ? p2.x : D(p2.x);
|
|
92
|
+
const y2 = p2.y instanceof Decimal ? p2.y : D(p2.y);
|
|
93
|
+
const dx = x2.minus(x1);
|
|
94
|
+
const dy = y2.minus(y1);
|
|
85
95
|
return dx.mul(dx).plus(dy.mul(dy)).sqrt();
|
|
86
96
|
}
|
|
87
97
|
|
|
@@ -93,8 +103,16 @@ export function distance(p1, p2) {
|
|
|
93
103
|
* @returns {boolean} True if points are equal
|
|
94
104
|
*/
|
|
95
105
|
export function pointsEqual(p1, p2, tolerance = EPSILON) {
|
|
106
|
+
if (!p1 || !p2) throw new Error('pointsEqual: both points are required');
|
|
107
|
+
if (p1.x === undefined || p1.y === undefined) throw new Error('pointsEqual: p1 must have x and y properties');
|
|
108
|
+
if (p2.x === undefined || p2.y === undefined) throw new Error('pointsEqual: p2 must have x and y properties');
|
|
96
109
|
const tol = D(tolerance);
|
|
97
|
-
|
|
110
|
+
// Ensure coordinates are Decimal objects
|
|
111
|
+
const x1 = p1.x instanceof Decimal ? p1.x : D(p1.x);
|
|
112
|
+
const y1 = p1.y instanceof Decimal ? p1.y : D(p1.y);
|
|
113
|
+
const x2 = p2.x instanceof Decimal ? p2.x : D(p2.x);
|
|
114
|
+
const y2 = p2.y instanceof Decimal ? p2.y : D(p2.y);
|
|
115
|
+
return x1.minus(x2).abs().lessThan(tol) && y1.minus(y2).abs().lessThan(tol);
|
|
98
116
|
}
|
|
99
117
|
|
|
100
118
|
// ============================================================================
|
|
@@ -113,7 +131,16 @@ export function pointsEqual(p1, p2, tolerance = EPSILON) {
|
|
|
113
131
|
* @returns {{x: Decimal, y: Decimal}} Point on curve
|
|
114
132
|
*/
|
|
115
133
|
export function evaluateCubicBezier(p0, p1, p2, p3, t) {
|
|
134
|
+
if (!p0 || !p1 || !p2 || !p3) throw new Error('evaluateCubicBezier: all points are required');
|
|
135
|
+
if (p0.x === undefined || p0.y === undefined) throw new Error('evaluateCubicBezier: p0 must have x and y properties');
|
|
136
|
+
if (p1.x === undefined || p1.y === undefined) throw new Error('evaluateCubicBezier: p1 must have x and y properties');
|
|
137
|
+
if (p2.x === undefined || p2.y === undefined) throw new Error('evaluateCubicBezier: p2 must have x and y properties');
|
|
138
|
+
if (p3.x === undefined || p3.y === undefined) throw new Error('evaluateCubicBezier: p3 must have x and y properties');
|
|
139
|
+
if (t === null || t === undefined) throw new Error('evaluateCubicBezier: parameter t is required');
|
|
140
|
+
|
|
116
141
|
const tD = D(t);
|
|
142
|
+
// Validate t is within valid range [0, 1] for Bezier curves
|
|
143
|
+
if (tD.lessThan(0) || tD.greaterThan(1)) throw new Error('evaluateCubicBezier: parameter t must be between 0 and 1');
|
|
117
144
|
const oneMinusT = D(1).minus(tD);
|
|
118
145
|
|
|
119
146
|
// Bernstein basis polynomials
|
|
@@ -139,7 +166,15 @@ export function evaluateCubicBezier(p0, p1, p2, p3, t) {
|
|
|
139
166
|
* @returns {{x: Decimal, y: Decimal}} Point on curve
|
|
140
167
|
*/
|
|
141
168
|
export function evaluateQuadraticBezier(p0, p1, p2, t) {
|
|
169
|
+
if (!p0 || !p1 || !p2) throw new Error('evaluateQuadraticBezier: all points are required');
|
|
170
|
+
if (p0.x === undefined || p0.y === undefined) throw new Error('evaluateQuadraticBezier: p0 must have x and y properties');
|
|
171
|
+
if (p1.x === undefined || p1.y === undefined) throw new Error('evaluateQuadraticBezier: p1 must have x and y properties');
|
|
172
|
+
if (p2.x === undefined || p2.y === undefined) throw new Error('evaluateQuadraticBezier: p2 must have x and y properties');
|
|
173
|
+
if (t === null || t === undefined) throw new Error('evaluateQuadraticBezier: parameter t is required');
|
|
174
|
+
|
|
142
175
|
const tD = D(t);
|
|
176
|
+
// Validate t is within valid range [0, 1] for Bezier curves
|
|
177
|
+
if (tD.lessThan(0) || tD.greaterThan(1)) throw new Error('evaluateQuadraticBezier: parameter t must be between 0 and 1');
|
|
143
178
|
const oneMinusT = D(1).minus(tD);
|
|
144
179
|
|
|
145
180
|
// Bernstein basis polynomials
|
|
@@ -170,6 +205,11 @@ export function evaluateQuadraticBezier(p0, p1, p2, t) {
|
|
|
170
205
|
* @returns {{canConvert: boolean, endX: Decimal, verified: boolean}} Conversion result
|
|
171
206
|
*/
|
|
172
207
|
export function lineToHorizontal(x1, y1, x2, y2, tolerance = EPSILON) {
|
|
208
|
+
if (x1 === null || x1 === undefined) throw new Error('lineToHorizontal: x1 is required');
|
|
209
|
+
if (y1 === null || y1 === undefined) throw new Error('lineToHorizontal: y1 is required');
|
|
210
|
+
if (x2 === null || x2 === undefined) throw new Error('lineToHorizontal: x2 is required');
|
|
211
|
+
if (y2 === null || y2 === undefined) throw new Error('lineToHorizontal: y2 is required');
|
|
212
|
+
|
|
173
213
|
const tol = D(tolerance);
|
|
174
214
|
const startY = D(y1);
|
|
175
215
|
const endY = D(y2);
|
|
@@ -203,6 +243,11 @@ export function lineToHorizontal(x1, y1, x2, y2, tolerance = EPSILON) {
|
|
|
203
243
|
* @returns {{canConvert: boolean, endY: Decimal, verified: boolean}} Conversion result
|
|
204
244
|
*/
|
|
205
245
|
export function lineToVertical(x1, y1, x2, y2, tolerance = EPSILON) {
|
|
246
|
+
if (x1 === null || x1 === undefined) throw new Error('lineToVertical: x1 is required');
|
|
247
|
+
if (y1 === null || y1 === undefined) throw new Error('lineToVertical: y1 is required');
|
|
248
|
+
if (x2 === null || x2 === undefined) throw new Error('lineToVertical: x2 is required');
|
|
249
|
+
if (y2 === null || y2 === undefined) throw new Error('lineToVertical: y2 is required');
|
|
250
|
+
|
|
206
251
|
const tol = D(tolerance);
|
|
207
252
|
const startX = D(x1);
|
|
208
253
|
const endX = D(x2);
|
|
@@ -236,6 +281,10 @@ export function lineToVertical(x1, y1, x2, y2, tolerance = EPSILON) {
|
|
|
236
281
|
* @returns {{x: Decimal, y: Decimal}} Reflected point
|
|
237
282
|
*/
|
|
238
283
|
export function reflectPoint(control, center) {
|
|
284
|
+
if (!control || !center) throw new Error('reflectPoint: both control and center points are required');
|
|
285
|
+
if (control.x === undefined || control.y === undefined) throw new Error('reflectPoint: control must have x and y properties');
|
|
286
|
+
if (center.x === undefined || center.y === undefined) throw new Error('reflectPoint: center must have x and y properties');
|
|
287
|
+
|
|
239
288
|
// Reflection formula: reflected = center + (center - control) = 2*center - control
|
|
240
289
|
return {
|
|
241
290
|
x: D(2).mul(center.x).minus(control.x),
|
|
@@ -266,6 +315,15 @@ export function reflectPoint(control, center) {
|
|
|
266
315
|
* @returns {{canConvert: boolean, cp2X: Decimal, cp2Y: Decimal, endX: Decimal, endY: Decimal, maxDeviation: Decimal, verified: boolean}}
|
|
267
316
|
*/
|
|
268
317
|
export function curveToSmooth(prevControl, x0, y0, x1, y1, x2, y2, x3, y3, tolerance = DEFAULT_TOLERANCE) {
|
|
318
|
+
if (x0 === null || x0 === undefined) throw new Error('curveToSmooth: x0 is required');
|
|
319
|
+
if (y0 === null || y0 === undefined) throw new Error('curveToSmooth: y0 is required');
|
|
320
|
+
if (x1 === null || x1 === undefined) throw new Error('curveToSmooth: x1 is required');
|
|
321
|
+
if (y1 === null || y1 === undefined) throw new Error('curveToSmooth: y1 is required');
|
|
322
|
+
if (x2 === null || x2 === undefined) throw new Error('curveToSmooth: x2 is required');
|
|
323
|
+
if (y2 === null || y2 === undefined) throw new Error('curveToSmooth: y2 is required');
|
|
324
|
+
if (x3 === null || x3 === undefined) throw new Error('curveToSmooth: x3 is required');
|
|
325
|
+
if (y3 === null || y3 === undefined) throw new Error('curveToSmooth: y3 is required');
|
|
326
|
+
|
|
269
327
|
const tol = D(tolerance);
|
|
270
328
|
const p0 = point(x0, y0);
|
|
271
329
|
const p1 = point(x1, y1);
|
|
@@ -351,6 +409,13 @@ export function curveToSmooth(prevControl, x0, y0, x1, y1, x2, y2, x3, y3, toler
|
|
|
351
409
|
* @returns {{canConvert: boolean, endX: Decimal, endY: Decimal, maxDeviation: Decimal, verified: boolean}}
|
|
352
410
|
*/
|
|
353
411
|
export function quadraticToSmooth(prevControl, x0, y0, x1, y1, x2, y2, tolerance = DEFAULT_TOLERANCE) {
|
|
412
|
+
if (x0 === null || x0 === undefined) throw new Error('quadraticToSmooth: x0 is required');
|
|
413
|
+
if (y0 === null || y0 === undefined) throw new Error('quadraticToSmooth: y0 is required');
|
|
414
|
+
if (x1 === null || x1 === undefined) throw new Error('quadraticToSmooth: x1 is required');
|
|
415
|
+
if (y1 === null || y1 === undefined) throw new Error('quadraticToSmooth: y1 is required');
|
|
416
|
+
if (x2 === null || x2 === undefined) throw new Error('quadraticToSmooth: x2 is required');
|
|
417
|
+
if (y2 === null || y2 === undefined) throw new Error('quadraticToSmooth: y2 is required');
|
|
418
|
+
|
|
354
419
|
const tol = D(tolerance);
|
|
355
420
|
const p0 = point(x0, y0);
|
|
356
421
|
const p1 = point(x1, y1);
|
|
@@ -417,7 +482,13 @@ export function quadraticToSmooth(prevControl, x0, y0, x1, y1, x2, y2, tolerance
|
|
|
417
482
|
* Used to avoid infinite recursion in verification.
|
|
418
483
|
*/
|
|
419
484
|
function _toRelativeArgs(cmd, args, cx, cy) {
|
|
485
|
+
if (!cmd || typeof cmd !== 'string') throw new Error('_toRelativeArgs: cmd must be a non-empty string');
|
|
486
|
+
if (!Array.isArray(args)) throw new Error('_toRelativeArgs: args must be an array');
|
|
487
|
+
if (cx === null || cx === undefined) throw new Error('_toRelativeArgs: cx is required');
|
|
488
|
+
if (cy === null || cy === undefined) throw new Error('_toRelativeArgs: cy is required');
|
|
489
|
+
|
|
420
490
|
if (cmd === 'M' || cmd === 'L' || cmd === 'T') {
|
|
491
|
+
if (args.length % 2 !== 0) throw new Error(`_toRelativeArgs: ${cmd} requires pairs of coordinates, got ${args.length} args`);
|
|
421
492
|
const relativeArgs = [];
|
|
422
493
|
for (let i = 0; i < args.length; i += 2) {
|
|
423
494
|
relativeArgs.push(args[i].minus(cx));
|
|
@@ -432,6 +503,7 @@ function _toRelativeArgs(cmd, args, cx, cy) {
|
|
|
432
503
|
return args.map(y => y.minus(cy));
|
|
433
504
|
}
|
|
434
505
|
if (cmd === 'C' || cmd === 'S' || cmd === 'Q') {
|
|
506
|
+
if (args.length % 2 !== 0) throw new Error(`_toRelativeArgs: ${cmd} requires pairs of coordinates, got ${args.length} args`);
|
|
435
507
|
const relativeArgs = [];
|
|
436
508
|
for (let i = 0; i < args.length; i += 2) {
|
|
437
509
|
relativeArgs.push(args[i].minus(cx));
|
|
@@ -440,6 +512,7 @@ function _toRelativeArgs(cmd, args, cx, cy) {
|
|
|
440
512
|
return relativeArgs;
|
|
441
513
|
}
|
|
442
514
|
if (cmd === 'A') {
|
|
515
|
+
if (args.length % 7 !== 0) throw new Error(`_toRelativeArgs: A requires groups of 7 args, got ${args.length} args`);
|
|
443
516
|
const relativeArgs = [];
|
|
444
517
|
for (let i = 0; i < args.length; i += 7) {
|
|
445
518
|
relativeArgs.push(args[i]);
|
|
@@ -460,7 +533,13 @@ function _toRelativeArgs(cmd, args, cx, cy) {
|
|
|
460
533
|
* Used to avoid infinite recursion in verification.
|
|
461
534
|
*/
|
|
462
535
|
function _toAbsoluteArgs(cmd, args, cx, cy) {
|
|
536
|
+
if (!cmd || typeof cmd !== 'string') throw new Error('_toAbsoluteArgs: cmd must be a non-empty string');
|
|
537
|
+
if (!Array.isArray(args)) throw new Error('_toAbsoluteArgs: args must be an array');
|
|
538
|
+
if (cx === null || cx === undefined) throw new Error('_toAbsoluteArgs: cx is required');
|
|
539
|
+
if (cy === null || cy === undefined) throw new Error('_toAbsoluteArgs: cy is required');
|
|
540
|
+
|
|
463
541
|
if (cmd === 'm' || cmd === 'l' || cmd === 't') {
|
|
542
|
+
if (args.length % 2 !== 0) throw new Error(`_toAbsoluteArgs: ${cmd} requires pairs of coordinates, got ${args.length} args`);
|
|
464
543
|
const absoluteArgs = [];
|
|
465
544
|
for (let i = 0; i < args.length; i += 2) {
|
|
466
545
|
absoluteArgs.push(args[i].plus(cx));
|
|
@@ -475,6 +554,7 @@ function _toAbsoluteArgs(cmd, args, cx, cy) {
|
|
|
475
554
|
return args.map(dy => dy.plus(cy));
|
|
476
555
|
}
|
|
477
556
|
if (cmd === 'c' || cmd === 's' || cmd === 'q') {
|
|
557
|
+
if (args.length % 2 !== 0) throw new Error(`_toAbsoluteArgs: ${cmd} requires pairs of coordinates, got ${args.length} args`);
|
|
478
558
|
const absoluteArgs = [];
|
|
479
559
|
for (let i = 0; i < args.length; i += 2) {
|
|
480
560
|
absoluteArgs.push(args[i].plus(cx));
|
|
@@ -483,6 +563,7 @@ function _toAbsoluteArgs(cmd, args, cx, cy) {
|
|
|
483
563
|
return absoluteArgs;
|
|
484
564
|
}
|
|
485
565
|
if (cmd === 'a') {
|
|
566
|
+
if (args.length % 7 !== 0) throw new Error(`_toAbsoluteArgs: a requires groups of 7 args, got ${args.length} args`);
|
|
486
567
|
const absoluteArgs = [];
|
|
487
568
|
for (let i = 0; i < args.length; i += 7) {
|
|
488
569
|
absoluteArgs.push(args[i]);
|
|
@@ -510,6 +591,12 @@ function _toAbsoluteArgs(cmd, args, cx, cy) {
|
|
|
510
591
|
* @returns {{command: string, args: Array<Decimal>, verified: boolean}}
|
|
511
592
|
*/
|
|
512
593
|
export function toRelative(command, currentX, currentY) {
|
|
594
|
+
if (!command) throw new Error('toRelative: command object is required');
|
|
595
|
+
if (!command.command) throw new Error('toRelative: command.command is required');
|
|
596
|
+
if (!Array.isArray(command.args)) throw new Error('toRelative: command.args must be an array');
|
|
597
|
+
if (currentX === null || currentX === undefined) throw new Error('toRelative: currentX is required');
|
|
598
|
+
if (currentY === null || currentY === undefined) throw new Error('toRelative: currentY is required');
|
|
599
|
+
|
|
513
600
|
const cmd = command.command;
|
|
514
601
|
const args = command.args.map(D);
|
|
515
602
|
const cx = D(currentX);
|
|
@@ -566,6 +653,12 @@ export function toRelative(command, currentX, currentY) {
|
|
|
566
653
|
* @returns {{command: string, args: Array<Decimal>, verified: boolean}}
|
|
567
654
|
*/
|
|
568
655
|
export function toAbsolute(command, currentX, currentY) {
|
|
656
|
+
if (!command) throw new Error('toAbsolute: command object is required');
|
|
657
|
+
if (!command.command) throw new Error('toAbsolute: command.command is required');
|
|
658
|
+
if (!Array.isArray(command.args)) throw new Error('toAbsolute: command.args must be an array');
|
|
659
|
+
if (currentX === null || currentX === undefined) throw new Error('toAbsolute: currentX is required');
|
|
660
|
+
if (currentY === null || currentY === undefined) throw new Error('toAbsolute: currentY is required');
|
|
661
|
+
|
|
569
662
|
const cmd = command.command;
|
|
570
663
|
const args = command.args.map(D);
|
|
571
664
|
const cx = D(currentX);
|
|
@@ -622,9 +715,18 @@ export function toAbsolute(command, currentX, currentY) {
|
|
|
622
715
|
* @returns {string} Formatted command string
|
|
623
716
|
*/
|
|
624
717
|
function formatCommand(command, precision = 6) {
|
|
718
|
+
if (!command) throw new Error('formatCommand: command object is required');
|
|
719
|
+
if (!command.command) throw new Error('formatCommand: command.command is required');
|
|
720
|
+
if (!Array.isArray(command.args)) throw new Error('formatCommand: command.args must be an array');
|
|
721
|
+
if (typeof precision !== 'number' || precision < 0) throw new Error('formatCommand: precision must be a non-negative number');
|
|
722
|
+
|
|
625
723
|
const cmd = command.command;
|
|
626
|
-
const args = command.args.map(arg => {
|
|
627
|
-
|
|
724
|
+
const args = command.args.map((arg, index) => {
|
|
725
|
+
// Convert to Decimal if not already, then to number
|
|
726
|
+
const argDecimal = arg instanceof Decimal ? arg : D(arg);
|
|
727
|
+
const num = argDecimal.toNumber();
|
|
728
|
+
// Validate the number is finite
|
|
729
|
+
if (!Number.isFinite(num)) throw new Error(`formatCommand: argument at index ${index} converted to non-finite number: ${num}`);
|
|
628
730
|
// Format with specified precision and remove trailing zeros
|
|
629
731
|
return parseFloat(num.toFixed(precision)).toString();
|
|
630
732
|
}).join(',');
|
|
@@ -643,6 +745,14 @@ function formatCommand(command, precision = 6) {
|
|
|
643
745
|
* @returns {{command: string, args: Array<Decimal>, isShorter: boolean, savedBytes: number, verified: boolean}}
|
|
644
746
|
*/
|
|
645
747
|
export function chooseShorterForm(absCommand, relCommand, precision = 6) {
|
|
748
|
+
if (!absCommand) throw new Error('chooseShorterForm: absCommand is required');
|
|
749
|
+
if (!absCommand.command) throw new Error('chooseShorterForm: absCommand.command is required');
|
|
750
|
+
if (!Array.isArray(absCommand.args)) throw new Error('chooseShorterForm: absCommand.args must be an array');
|
|
751
|
+
if (!relCommand) throw new Error('chooseShorterForm: relCommand is required');
|
|
752
|
+
if (!relCommand.command) throw new Error('chooseShorterForm: relCommand.command is required');
|
|
753
|
+
if (!Array.isArray(relCommand.args)) throw new Error('chooseShorterForm: relCommand.args must be an array');
|
|
754
|
+
if (typeof precision !== 'number' || precision < 0) throw new Error('chooseShorterForm: precision must be a non-negative number');
|
|
755
|
+
|
|
646
756
|
const absStr = formatCommand({ command: absCommand.command, args: absCommand.args.map(D) }, precision);
|
|
647
757
|
const relStr = formatCommand({ command: relCommand.command, args: relCommand.args.map(D) }, precision);
|
|
648
758
|
|
|
@@ -690,6 +800,15 @@ export function chooseShorterForm(absCommand, relCommand, precision = 6) {
|
|
|
690
800
|
* @returns {{commands: Array<{command: string, args: Array<Decimal>}>, collapseCount: number, verified: boolean}}
|
|
691
801
|
*/
|
|
692
802
|
export function collapseRepeated(commands) {
|
|
803
|
+
if (!Array.isArray(commands)) throw new Error('collapseRepeated: commands must be an array');
|
|
804
|
+
|
|
805
|
+
// Validate each command object
|
|
806
|
+
for (let i = 0; i < commands.length; i++) {
|
|
807
|
+
if (!commands[i]) throw new Error(`collapseRepeated: command at index ${i} is null or undefined`);
|
|
808
|
+
if (!commands[i].command) throw new Error(`collapseRepeated: command at index ${i} is missing 'command' property`);
|
|
809
|
+
if (!Array.isArray(commands[i].args)) throw new Error(`collapseRepeated: command at index ${i} is missing 'args' array property`);
|
|
810
|
+
}
|
|
811
|
+
|
|
693
812
|
if (commands.length < 2) {
|
|
694
813
|
return {
|
|
695
814
|
commands: commands.map(cmd => ({ command: cmd.command, args: cmd.args.map(D) })),
|
|
@@ -759,6 +878,11 @@ export function collapseRepeated(commands) {
|
|
|
759
878
|
* @returns {{canConvert: boolean, deviation: Decimal, verified: boolean}}
|
|
760
879
|
*/
|
|
761
880
|
export function lineToZ(lastX, lastY, startX, startY, tolerance = EPSILON) {
|
|
881
|
+
if (lastX === null || lastX === undefined) throw new Error('lineToZ: lastX is required');
|
|
882
|
+
if (lastY === null || lastY === undefined) throw new Error('lineToZ: lastY is required');
|
|
883
|
+
if (startX === null || startX === undefined) throw new Error('lineToZ: startX is required');
|
|
884
|
+
if (startY === null || startY === undefined) throw new Error('lineToZ: startY is required');
|
|
885
|
+
|
|
762
886
|
const tol = D(tolerance);
|
|
763
887
|
const last = point(lastX, lastY);
|
|
764
888
|
const start = point(startX, startY);
|