@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/path-optimization.js
CHANGED
|
@@ -45,19 +45,19 @@
|
|
|
45
45
|
* @module path-optimization
|
|
46
46
|
*/
|
|
47
47
|
|
|
48
|
-
import Decimal from
|
|
48
|
+
import Decimal from "decimal.js";
|
|
49
49
|
|
|
50
50
|
// Set high precision for all calculations
|
|
51
51
|
Decimal.set({ precision: 80 });
|
|
52
52
|
|
|
53
53
|
// Helper to convert to Decimal
|
|
54
|
-
const D = x => (x instanceof Decimal ? x : new Decimal(x));
|
|
54
|
+
const D = (x) => (x instanceof Decimal ? x : new Decimal(x));
|
|
55
55
|
|
|
56
56
|
// Near-zero threshold for comparisons
|
|
57
|
-
const EPSILON = new Decimal(
|
|
57
|
+
const EPSILON = new Decimal("1e-40");
|
|
58
58
|
|
|
59
59
|
// Default tolerance for optimization (user-configurable)
|
|
60
|
-
const DEFAULT_TOLERANCE = new Decimal(
|
|
60
|
+
const DEFAULT_TOLERANCE = new Decimal("1e-10");
|
|
61
61
|
|
|
62
62
|
// ============================================================================
|
|
63
63
|
// Point and Distance Utilities
|
|
@@ -70,8 +70,10 @@ 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)
|
|
74
|
-
|
|
73
|
+
if (x === null || x === undefined)
|
|
74
|
+
throw new Error("point: x coordinate is required");
|
|
75
|
+
if (y === null || y === undefined)
|
|
76
|
+
throw new Error("point: y coordinate is required");
|
|
75
77
|
return { x: D(x), y: D(y) };
|
|
76
78
|
}
|
|
77
79
|
|
|
@@ -82,9 +84,11 @@ export function point(x, y) {
|
|
|
82
84
|
* @returns {Decimal} Distance
|
|
83
85
|
*/
|
|
84
86
|
export function distance(p1, p2) {
|
|
85
|
-
if (!p1 || !p2) throw new Error(
|
|
86
|
-
if (p1.x === undefined || p1.y === undefined)
|
|
87
|
-
|
|
87
|
+
if (!p1 || !p2) throw new Error("distance: both points are required");
|
|
88
|
+
if (p1.x === undefined || p1.y === undefined)
|
|
89
|
+
throw new Error("distance: p1 must have x and y properties");
|
|
90
|
+
if (p2.x === undefined || p2.y === undefined)
|
|
91
|
+
throw new Error("distance: p2 must have x and y properties");
|
|
88
92
|
// Ensure coordinates are Decimal objects
|
|
89
93
|
const x1 = p1.x instanceof Decimal ? p1.x : D(p1.x);
|
|
90
94
|
const y1 = p1.y instanceof Decimal ? p1.y : D(p1.y);
|
|
@@ -103,9 +107,11 @@ export function distance(p1, p2) {
|
|
|
103
107
|
* @returns {boolean} True if points are equal
|
|
104
108
|
*/
|
|
105
109
|
export function pointsEqual(p1, p2, tolerance = EPSILON) {
|
|
106
|
-
if (!p1 || !p2) throw new Error(
|
|
107
|
-
if (p1.x === undefined || p1.y === undefined)
|
|
108
|
-
|
|
110
|
+
if (!p1 || !p2) throw new Error("pointsEqual: both points are required");
|
|
111
|
+
if (p1.x === undefined || p1.y === undefined)
|
|
112
|
+
throw new Error("pointsEqual: p1 must have x and y properties");
|
|
113
|
+
if (p2.x === undefined || p2.y === undefined)
|
|
114
|
+
throw new Error("pointsEqual: p2 must have x and y properties");
|
|
109
115
|
const tol = D(tolerance);
|
|
110
116
|
// Ensure coordinates are Decimal objects
|
|
111
117
|
const x1 = p1.x instanceof Decimal ? p1.x : D(p1.x);
|
|
@@ -131,27 +137,34 @@ export function pointsEqual(p1, p2, tolerance = EPSILON) {
|
|
|
131
137
|
* @returns {{x: Decimal, y: Decimal}} Point on curve
|
|
132
138
|
*/
|
|
133
139
|
export function evaluateCubicBezier(p0, p1, p2, p3, t) {
|
|
134
|
-
if (!p0 || !p1 || !p2 || !p3)
|
|
135
|
-
|
|
136
|
-
if (
|
|
137
|
-
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
+
if (!p0 || !p1 || !p2 || !p3)
|
|
141
|
+
throw new Error("evaluateCubicBezier: all points are required");
|
|
142
|
+
if (p0.x === undefined || p0.y === undefined)
|
|
143
|
+
throw new Error("evaluateCubicBezier: p0 must have x and y properties");
|
|
144
|
+
if (p1.x === undefined || p1.y === undefined)
|
|
145
|
+
throw new Error("evaluateCubicBezier: p1 must have x and y properties");
|
|
146
|
+
if (p2.x === undefined || p2.y === undefined)
|
|
147
|
+
throw new Error("evaluateCubicBezier: p2 must have x and y properties");
|
|
148
|
+
if (p3.x === undefined || p3.y === undefined)
|
|
149
|
+
throw new Error("evaluateCubicBezier: p3 must have x and y properties");
|
|
150
|
+
if (t === null || t === undefined)
|
|
151
|
+
throw new Error("evaluateCubicBezier: parameter t is required");
|
|
140
152
|
|
|
141
153
|
const tD = D(t);
|
|
142
154
|
// Validate t is within valid range [0, 1] for Bezier curves
|
|
143
|
-
if (tD.lessThan(0) || tD.greaterThan(1))
|
|
155
|
+
if (tD.lessThan(0) || tD.greaterThan(1))
|
|
156
|
+
throw new Error("evaluateCubicBezier: parameter t must be in range [0, 1]");
|
|
144
157
|
const oneMinusT = D(1).minus(tD);
|
|
145
158
|
|
|
146
159
|
// Bernstein basis polynomials
|
|
147
|
-
const b0 = oneMinusT.pow(3);
|
|
148
|
-
const b1 = D(3).mul(oneMinusT.pow(2)).mul(tD);
|
|
149
|
-
const b2 = D(3).mul(oneMinusT).mul(tD.pow(2));
|
|
150
|
-
const b3 = tD.pow(3);
|
|
160
|
+
const b0 = oneMinusT.pow(3); // (1-t)³
|
|
161
|
+
const b1 = D(3).mul(oneMinusT.pow(2)).mul(tD); // 3(1-t)²t
|
|
162
|
+
const b2 = D(3).mul(oneMinusT).mul(tD.pow(2)); // 3(1-t)t²
|
|
163
|
+
const b3 = tD.pow(3); // t³
|
|
151
164
|
|
|
152
165
|
return {
|
|
153
166
|
x: b0.mul(p0.x).plus(b1.mul(p1.x)).plus(b2.mul(p2.x)).plus(b3.mul(p3.x)),
|
|
154
|
-
y: b0.mul(p0.y).plus(b1.mul(p1.y)).plus(b2.mul(p2.y)).plus(b3.mul(p3.y))
|
|
167
|
+
y: b0.mul(p0.y).plus(b1.mul(p1.y)).plus(b2.mul(p2.y)).plus(b3.mul(p3.y)),
|
|
155
168
|
};
|
|
156
169
|
}
|
|
157
170
|
|
|
@@ -166,25 +179,33 @@ export function evaluateCubicBezier(p0, p1, p2, p3, t) {
|
|
|
166
179
|
* @returns {{x: Decimal, y: Decimal}} Point on curve
|
|
167
180
|
*/
|
|
168
181
|
export function evaluateQuadraticBezier(p0, p1, p2, t) {
|
|
169
|
-
if (!p0 || !p1 || !p2)
|
|
170
|
-
|
|
171
|
-
if (
|
|
172
|
-
|
|
173
|
-
if (
|
|
182
|
+
if (!p0 || !p1 || !p2)
|
|
183
|
+
throw new Error("evaluateQuadraticBezier: all points are required");
|
|
184
|
+
if (p0.x === undefined || p0.y === undefined)
|
|
185
|
+
throw new Error("evaluateQuadraticBezier: p0 must have x and y properties");
|
|
186
|
+
if (p1.x === undefined || p1.y === undefined)
|
|
187
|
+
throw new Error("evaluateQuadraticBezier: p1 must have x and y properties");
|
|
188
|
+
if (p2.x === undefined || p2.y === undefined)
|
|
189
|
+
throw new Error("evaluateQuadraticBezier: p2 must have x and y properties");
|
|
190
|
+
if (t === null || t === undefined)
|
|
191
|
+
throw new Error("evaluateQuadraticBezier: parameter t is required");
|
|
174
192
|
|
|
175
193
|
const tD = D(t);
|
|
176
194
|
// Validate t is within valid range [0, 1] for Bezier curves
|
|
177
|
-
if (tD.lessThan(0) || tD.greaterThan(1))
|
|
195
|
+
if (tD.lessThan(0) || tD.greaterThan(1))
|
|
196
|
+
throw new Error(
|
|
197
|
+
"evaluateQuadraticBezier: parameter t must be in range [0, 1]",
|
|
198
|
+
);
|
|
178
199
|
const oneMinusT = D(1).minus(tD);
|
|
179
200
|
|
|
180
201
|
// Bernstein basis polynomials
|
|
181
|
-
const b0 = oneMinusT.pow(2);
|
|
182
|
-
const b1 = D(2).mul(oneMinusT).mul(tD);
|
|
183
|
-
const b2 = tD.pow(2);
|
|
202
|
+
const b0 = oneMinusT.pow(2); // (1-t)²
|
|
203
|
+
const b1 = D(2).mul(oneMinusT).mul(tD); // 2(1-t)t
|
|
204
|
+
const b2 = tD.pow(2); // t²
|
|
184
205
|
|
|
185
206
|
return {
|
|
186
207
|
x: b0.mul(p0.x).plus(b1.mul(p1.x)).plus(b2.mul(p2.x)),
|
|
187
|
-
y: b0.mul(p0.y).plus(b1.mul(p1.y)).plus(b2.mul(p2.y))
|
|
208
|
+
y: b0.mul(p0.y).plus(b1.mul(p1.y)).plus(b2.mul(p2.y)),
|
|
188
209
|
};
|
|
189
210
|
}
|
|
190
211
|
|
|
@@ -205,10 +226,14 @@ export function evaluateQuadraticBezier(p0, p1, p2, t) {
|
|
|
205
226
|
* @returns {{canConvert: boolean, endX: Decimal, verified: boolean}} Conversion result
|
|
206
227
|
*/
|
|
207
228
|
export function lineToHorizontal(x1, y1, x2, y2, tolerance = EPSILON) {
|
|
208
|
-
if (x1 === null || x1 === undefined)
|
|
209
|
-
|
|
210
|
-
if (
|
|
211
|
-
|
|
229
|
+
if (x1 === null || x1 === undefined)
|
|
230
|
+
throw new Error("lineToHorizontal: x1 is required");
|
|
231
|
+
if (y1 === null || y1 === undefined)
|
|
232
|
+
throw new Error("lineToHorizontal: y1 is required");
|
|
233
|
+
if (x2 === null || x2 === undefined)
|
|
234
|
+
throw new Error("lineToHorizontal: x2 is required");
|
|
235
|
+
if (y2 === null || y2 === undefined)
|
|
236
|
+
throw new Error("lineToHorizontal: y2 is required");
|
|
212
237
|
|
|
213
238
|
const tol = D(tolerance);
|
|
214
239
|
const startY = D(y1);
|
|
@@ -227,7 +252,7 @@ export function lineToHorizontal(x1, y1, x2, y2, tolerance = EPSILON) {
|
|
|
227
252
|
return {
|
|
228
253
|
canConvert,
|
|
229
254
|
endX,
|
|
230
|
-
verified
|
|
255
|
+
verified,
|
|
231
256
|
};
|
|
232
257
|
}
|
|
233
258
|
|
|
@@ -244,10 +269,14 @@ export function lineToHorizontal(x1, y1, x2, y2, tolerance = EPSILON) {
|
|
|
244
269
|
* @returns {{canConvert: boolean, endY: Decimal, verified: boolean}} Conversion result
|
|
245
270
|
*/
|
|
246
271
|
export function lineToVertical(x1, y1, x2, y2, tolerance = EPSILON) {
|
|
247
|
-
if (x1 === null || x1 === undefined)
|
|
248
|
-
|
|
249
|
-
if (
|
|
250
|
-
|
|
272
|
+
if (x1 === null || x1 === undefined)
|
|
273
|
+
throw new Error("lineToVertical: x1 is required");
|
|
274
|
+
if (y1 === null || y1 === undefined)
|
|
275
|
+
throw new Error("lineToVertical: y1 is required");
|
|
276
|
+
if (x2 === null || x2 === undefined)
|
|
277
|
+
throw new Error("lineToVertical: x2 is required");
|
|
278
|
+
if (y2 === null || y2 === undefined)
|
|
279
|
+
throw new Error("lineToVertical: y2 is required");
|
|
251
280
|
|
|
252
281
|
const tol = D(tolerance);
|
|
253
282
|
const startX = D(x1);
|
|
@@ -266,7 +295,7 @@ export function lineToVertical(x1, y1, x2, y2, tolerance = EPSILON) {
|
|
|
266
295
|
return {
|
|
267
296
|
canConvert,
|
|
268
297
|
endY,
|
|
269
|
-
verified
|
|
298
|
+
verified,
|
|
270
299
|
};
|
|
271
300
|
}
|
|
272
301
|
|
|
@@ -283,14 +312,19 @@ export function lineToVertical(x1, y1, x2, y2, tolerance = EPSILON) {
|
|
|
283
312
|
* @returns {{x: Decimal, y: Decimal}} Reflected point
|
|
284
313
|
*/
|
|
285
314
|
export function reflectPoint(control, center) {
|
|
286
|
-
if (!control || !center)
|
|
287
|
-
|
|
288
|
-
|
|
315
|
+
if (!control || !center)
|
|
316
|
+
throw new Error(
|
|
317
|
+
"reflectPoint: both control and center points are required",
|
|
318
|
+
);
|
|
319
|
+
if (control.x === undefined || control.y === undefined)
|
|
320
|
+
throw new Error("reflectPoint: control must have x and y properties");
|
|
321
|
+
if (center.x === undefined || center.y === undefined)
|
|
322
|
+
throw new Error("reflectPoint: center must have x and y properties");
|
|
289
323
|
|
|
290
324
|
// Reflection formula: reflected = center + (center - control) = 2*center - control
|
|
291
325
|
return {
|
|
292
326
|
x: D(2).mul(center.x).minus(control.x),
|
|
293
|
-
y: D(2).mul(center.y).minus(control.y)
|
|
327
|
+
y: D(2).mul(center.y).minus(control.y),
|
|
294
328
|
};
|
|
295
329
|
}
|
|
296
330
|
|
|
@@ -316,15 +350,34 @@ export function reflectPoint(control, center) {
|
|
|
316
350
|
* @param {Decimal} [tolerance=DEFAULT_TOLERANCE] - Maximum allowed deviation
|
|
317
351
|
* @returns {{canConvert: boolean, cp2X: Decimal, cp2Y: Decimal, endX: Decimal, endY: Decimal, maxDeviation: Decimal, verified: boolean}}
|
|
318
352
|
*/
|
|
319
|
-
export function curveToSmooth(
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
353
|
+
export function curveToSmooth(
|
|
354
|
+
prevControl,
|
|
355
|
+
x0,
|
|
356
|
+
y0,
|
|
357
|
+
x1,
|
|
358
|
+
y1,
|
|
359
|
+
x2,
|
|
360
|
+
y2,
|
|
361
|
+
x3,
|
|
362
|
+
y3,
|
|
363
|
+
tolerance = DEFAULT_TOLERANCE,
|
|
364
|
+
) {
|
|
365
|
+
if (x0 === null || x0 === undefined)
|
|
366
|
+
throw new Error("curveToSmooth: x0 is required");
|
|
367
|
+
if (y0 === null || y0 === undefined)
|
|
368
|
+
throw new Error("curveToSmooth: y0 is required");
|
|
369
|
+
if (x1 === null || x1 === undefined)
|
|
370
|
+
throw new Error("curveToSmooth: x1 is required");
|
|
371
|
+
if (y1 === null || y1 === undefined)
|
|
372
|
+
throw new Error("curveToSmooth: y1 is required");
|
|
373
|
+
if (x2 === null || x2 === undefined)
|
|
374
|
+
throw new Error("curveToSmooth: x2 is required");
|
|
375
|
+
if (y2 === null || y2 === undefined)
|
|
376
|
+
throw new Error("curveToSmooth: y2 is required");
|
|
377
|
+
if (x3 === null || x3 === undefined)
|
|
378
|
+
throw new Error("curveToSmooth: x3 is required");
|
|
379
|
+
if (y3 === null || y3 === undefined)
|
|
380
|
+
throw new Error("curveToSmooth: y3 is required");
|
|
328
381
|
|
|
329
382
|
const tol = D(tolerance);
|
|
330
383
|
const p0 = point(x0, y0);
|
|
@@ -341,7 +394,7 @@ export function curveToSmooth(prevControl, x0, y0, x1, y1, x2, y2, x3, y3, toler
|
|
|
341
394
|
endX: p3.x,
|
|
342
395
|
endY: p3.y,
|
|
343
396
|
maxDeviation: D(Infinity),
|
|
344
|
-
verified: true
|
|
397
|
+
verified: true,
|
|
345
398
|
};
|
|
346
399
|
}
|
|
347
400
|
|
|
@@ -359,7 +412,7 @@ export function curveToSmooth(prevControl, x0, y0, x1, y1, x2, y2, x3, y3, toler
|
|
|
359
412
|
endX: p3.x,
|
|
360
413
|
endY: p3.y,
|
|
361
414
|
maxDeviation: controlDeviation,
|
|
362
|
-
verified: true
|
|
415
|
+
verified: true,
|
|
363
416
|
};
|
|
364
417
|
}
|
|
365
418
|
|
|
@@ -388,7 +441,7 @@ export function curveToSmooth(prevControl, x0, y0, x1, y1, x2, y2, x3, y3, toler
|
|
|
388
441
|
endX: p3.x,
|
|
389
442
|
endY: p3.y,
|
|
390
443
|
maxDeviation: Decimal.max(controlDeviation, maxSampleDeviation),
|
|
391
|
-
verified: true
|
|
444
|
+
verified: true,
|
|
392
445
|
};
|
|
393
446
|
}
|
|
394
447
|
|
|
@@ -412,13 +465,28 @@ export function curveToSmooth(prevControl, x0, y0, x1, y1, x2, y2, x3, y3, toler
|
|
|
412
465
|
* @param {Decimal} [tolerance=DEFAULT_TOLERANCE] - Maximum allowed deviation
|
|
413
466
|
* @returns {{canConvert: boolean, endX: Decimal, endY: Decimal, maxDeviation: Decimal, verified: boolean}}
|
|
414
467
|
*/
|
|
415
|
-
export function quadraticToSmooth(
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
468
|
+
export function quadraticToSmooth(
|
|
469
|
+
prevControl,
|
|
470
|
+
x0,
|
|
471
|
+
y0,
|
|
472
|
+
x1,
|
|
473
|
+
y1,
|
|
474
|
+
x2,
|
|
475
|
+
y2,
|
|
476
|
+
tolerance = DEFAULT_TOLERANCE,
|
|
477
|
+
) {
|
|
478
|
+
if (x0 === null || x0 === undefined)
|
|
479
|
+
throw new Error("quadraticToSmooth: x0 is required");
|
|
480
|
+
if (y0 === null || y0 === undefined)
|
|
481
|
+
throw new Error("quadraticToSmooth: y0 is required");
|
|
482
|
+
if (x1 === null || x1 === undefined)
|
|
483
|
+
throw new Error("quadraticToSmooth: x1 is required");
|
|
484
|
+
if (y1 === null || y1 === undefined)
|
|
485
|
+
throw new Error("quadraticToSmooth: y1 is required");
|
|
486
|
+
if (x2 === null || x2 === undefined)
|
|
487
|
+
throw new Error("quadraticToSmooth: x2 is required");
|
|
488
|
+
if (y2 === null || y2 === undefined)
|
|
489
|
+
throw new Error("quadraticToSmooth: y2 is required");
|
|
422
490
|
|
|
423
491
|
const tol = D(tolerance);
|
|
424
492
|
const p0 = point(x0, y0);
|
|
@@ -432,7 +500,7 @@ export function quadraticToSmooth(prevControl, x0, y0, x1, y1, x2, y2, tolerance
|
|
|
432
500
|
endX: p2.x,
|
|
433
501
|
endY: p2.y,
|
|
434
502
|
maxDeviation: D(Infinity),
|
|
435
|
-
verified: true
|
|
503
|
+
verified: true,
|
|
436
504
|
};
|
|
437
505
|
}
|
|
438
506
|
|
|
@@ -448,7 +516,7 @@ export function quadraticToSmooth(prevControl, x0, y0, x1, y1, x2, y2, tolerance
|
|
|
448
516
|
endX: p2.x,
|
|
449
517
|
endY: p2.y,
|
|
450
518
|
maxDeviation: controlDeviation,
|
|
451
|
-
verified: true
|
|
519
|
+
verified: true,
|
|
452
520
|
};
|
|
453
521
|
}
|
|
454
522
|
|
|
@@ -475,7 +543,7 @@ export function quadraticToSmooth(prevControl, x0, y0, x1, y1, x2, y2, tolerance
|
|
|
475
543
|
endX: p2.x,
|
|
476
544
|
endY: p2.y,
|
|
477
545
|
maxDeviation: Decimal.max(controlDeviation, maxSampleDeviation),
|
|
478
|
-
verified: true
|
|
546
|
+
verified: true,
|
|
479
547
|
};
|
|
480
548
|
}
|
|
481
549
|
|
|
@@ -488,18 +556,30 @@ export function quadraticToSmooth(prevControl, x0, y0, x1, y1, x2, y2, tolerance
|
|
|
488
556
|
* Used to avoid infinite recursion in verification.
|
|
489
557
|
*/
|
|
490
558
|
function _toRelativeArgs(cmd, args, cx, cy) {
|
|
491
|
-
if (!cmd || typeof cmd !==
|
|
492
|
-
|
|
493
|
-
if (
|
|
494
|
-
|
|
559
|
+
if (!cmd || typeof cmd !== "string")
|
|
560
|
+
throw new Error("_toRelativeArgs: cmd must be a non-empty string");
|
|
561
|
+
if (!Array.isArray(args))
|
|
562
|
+
throw new Error("_toRelativeArgs: args must be an array");
|
|
563
|
+
if (cx === null || cx === undefined)
|
|
564
|
+
throw new Error("_toRelativeArgs: cx is required");
|
|
565
|
+
if (cy === null || cy === undefined)
|
|
566
|
+
throw new Error("_toRelativeArgs: cy is required");
|
|
495
567
|
|
|
496
568
|
// Validate that commands requiring arguments have at least one
|
|
497
|
-
if (
|
|
498
|
-
|
|
569
|
+
if (
|
|
570
|
+
["M", "L", "H", "V", "C", "S", "Q", "T", "A"].includes(cmd) &&
|
|
571
|
+
args.length === 0
|
|
572
|
+
) {
|
|
573
|
+
throw new Error(
|
|
574
|
+
`_toRelativeArgs: ${cmd} command requires arguments but received empty array`,
|
|
575
|
+
);
|
|
499
576
|
}
|
|
500
577
|
|
|
501
|
-
if (cmd ===
|
|
502
|
-
if (args.length % 2 !== 0)
|
|
578
|
+
if (cmd === "M" || cmd === "L" || cmd === "T") {
|
|
579
|
+
if (args.length % 2 !== 0)
|
|
580
|
+
throw new Error(
|
|
581
|
+
`_toRelativeArgs: ${cmd} requires pairs of coordinates, got ${args.length} args`,
|
|
582
|
+
);
|
|
503
583
|
const relativeArgs = [];
|
|
504
584
|
for (let i = 0; i < args.length; i += 2) {
|
|
505
585
|
relativeArgs.push(args[i].minus(cx));
|
|
@@ -507,14 +587,17 @@ function _toRelativeArgs(cmd, args, cx, cy) {
|
|
|
507
587
|
}
|
|
508
588
|
return relativeArgs;
|
|
509
589
|
}
|
|
510
|
-
if (cmd ===
|
|
511
|
-
return args.map(x => x.minus(cx));
|
|
590
|
+
if (cmd === "H") {
|
|
591
|
+
return args.map((x) => x.minus(cx));
|
|
512
592
|
}
|
|
513
|
-
if (cmd ===
|
|
514
|
-
return args.map(y => y.minus(cy));
|
|
593
|
+
if (cmd === "V") {
|
|
594
|
+
return args.map((y) => y.minus(cy));
|
|
515
595
|
}
|
|
516
|
-
if (cmd ===
|
|
517
|
-
if (args.length % 2 !== 0)
|
|
596
|
+
if (cmd === "C" || cmd === "S" || cmd === "Q") {
|
|
597
|
+
if (args.length % 2 !== 0)
|
|
598
|
+
throw new Error(
|
|
599
|
+
`_toRelativeArgs: ${cmd} requires pairs of coordinates, got ${args.length} args`,
|
|
600
|
+
);
|
|
518
601
|
const relativeArgs = [];
|
|
519
602
|
for (let i = 0; i < args.length; i += 2) {
|
|
520
603
|
relativeArgs.push(args[i].minus(cx));
|
|
@@ -522,8 +605,11 @@ function _toRelativeArgs(cmd, args, cx, cy) {
|
|
|
522
605
|
}
|
|
523
606
|
return relativeArgs;
|
|
524
607
|
}
|
|
525
|
-
if (cmd ===
|
|
526
|
-
if (args.length % 7 !== 0)
|
|
608
|
+
if (cmd === "A") {
|
|
609
|
+
if (args.length % 7 !== 0)
|
|
610
|
+
throw new Error(
|
|
611
|
+
`_toRelativeArgs: A requires groups of 7 args, got ${args.length} args`,
|
|
612
|
+
);
|
|
527
613
|
const relativeArgs = [];
|
|
528
614
|
for (let i = 0; i < args.length; i += 7) {
|
|
529
615
|
relativeArgs.push(args[i]);
|
|
@@ -544,18 +630,30 @@ function _toRelativeArgs(cmd, args, cx, cy) {
|
|
|
544
630
|
* Used to avoid infinite recursion in verification.
|
|
545
631
|
*/
|
|
546
632
|
function _toAbsoluteArgs(cmd, args, cx, cy) {
|
|
547
|
-
if (!cmd || typeof cmd !==
|
|
548
|
-
|
|
549
|
-
if (
|
|
550
|
-
|
|
633
|
+
if (!cmd || typeof cmd !== "string")
|
|
634
|
+
throw new Error("_toAbsoluteArgs: cmd must be a non-empty string");
|
|
635
|
+
if (!Array.isArray(args))
|
|
636
|
+
throw new Error("_toAbsoluteArgs: args must be an array");
|
|
637
|
+
if (cx === null || cx === undefined)
|
|
638
|
+
throw new Error("_toAbsoluteArgs: cx is required");
|
|
639
|
+
if (cy === null || cy === undefined)
|
|
640
|
+
throw new Error("_toAbsoluteArgs: cy is required");
|
|
551
641
|
|
|
552
642
|
// Validate that commands requiring arguments have at least one
|
|
553
|
-
if (
|
|
554
|
-
|
|
643
|
+
if (
|
|
644
|
+
["m", "l", "h", "v", "c", "s", "q", "t", "a"].includes(cmd) &&
|
|
645
|
+
args.length === 0
|
|
646
|
+
) {
|
|
647
|
+
throw new Error(
|
|
648
|
+
`_toAbsoluteArgs: ${cmd} command requires arguments but received empty array`,
|
|
649
|
+
);
|
|
555
650
|
}
|
|
556
651
|
|
|
557
|
-
if (cmd ===
|
|
558
|
-
if (args.length % 2 !== 0)
|
|
652
|
+
if (cmd === "m" || cmd === "l" || cmd === "t") {
|
|
653
|
+
if (args.length % 2 !== 0)
|
|
654
|
+
throw new Error(
|
|
655
|
+
`_toAbsoluteArgs: ${cmd} requires pairs of coordinates, got ${args.length} args`,
|
|
656
|
+
);
|
|
559
657
|
const absoluteArgs = [];
|
|
560
658
|
for (let i = 0; i < args.length; i += 2) {
|
|
561
659
|
absoluteArgs.push(args[i].plus(cx));
|
|
@@ -563,14 +661,17 @@ function _toAbsoluteArgs(cmd, args, cx, cy) {
|
|
|
563
661
|
}
|
|
564
662
|
return absoluteArgs;
|
|
565
663
|
}
|
|
566
|
-
if (cmd ===
|
|
567
|
-
return args.map(dx => dx.plus(cx));
|
|
664
|
+
if (cmd === "h") {
|
|
665
|
+
return args.map((dx) => dx.plus(cx));
|
|
568
666
|
}
|
|
569
|
-
if (cmd ===
|
|
570
|
-
return args.map(dy => dy.plus(cy));
|
|
667
|
+
if (cmd === "v") {
|
|
668
|
+
return args.map((dy) => dy.plus(cy));
|
|
571
669
|
}
|
|
572
|
-
if (cmd ===
|
|
573
|
-
if (args.length % 2 !== 0)
|
|
670
|
+
if (cmd === "c" || cmd === "s" || cmd === "q") {
|
|
671
|
+
if (args.length % 2 !== 0)
|
|
672
|
+
throw new Error(
|
|
673
|
+
`_toAbsoluteArgs: ${cmd} requires pairs of coordinates, got ${args.length} args`,
|
|
674
|
+
);
|
|
574
675
|
const absoluteArgs = [];
|
|
575
676
|
for (let i = 0; i < args.length; i += 2) {
|
|
576
677
|
absoluteArgs.push(args[i].plus(cx));
|
|
@@ -578,8 +679,11 @@ function _toAbsoluteArgs(cmd, args, cx, cy) {
|
|
|
578
679
|
}
|
|
579
680
|
return absoluteArgs;
|
|
580
681
|
}
|
|
581
|
-
if (cmd ===
|
|
582
|
-
if (args.length % 7 !== 0)
|
|
682
|
+
if (cmd === "a") {
|
|
683
|
+
if (args.length % 7 !== 0)
|
|
684
|
+
throw new Error(
|
|
685
|
+
`_toAbsoluteArgs: a requires groups of 7 args, got ${args.length} args`,
|
|
686
|
+
);
|
|
583
687
|
const absoluteArgs = [];
|
|
584
688
|
for (let i = 0; i < args.length; i += 7) {
|
|
585
689
|
absoluteArgs.push(args[i]);
|
|
@@ -607,11 +711,15 @@ function _toAbsoluteArgs(cmd, args, cx, cy) {
|
|
|
607
711
|
* @returns {{command: string, args: Array<Decimal>, verified: boolean}}
|
|
608
712
|
*/
|
|
609
713
|
export function toRelative(command, currentX, currentY) {
|
|
610
|
-
if (!command) throw new Error(
|
|
611
|
-
if (!command.command)
|
|
612
|
-
|
|
613
|
-
if (
|
|
614
|
-
|
|
714
|
+
if (!command) throw new Error("toRelative: command object is required");
|
|
715
|
+
if (!command.command)
|
|
716
|
+
throw new Error("toRelative: command.command is required");
|
|
717
|
+
if (!Array.isArray(command.args))
|
|
718
|
+
throw new Error("toRelative: command.args must be an array");
|
|
719
|
+
if (currentX === null || currentX === undefined)
|
|
720
|
+
throw new Error("toRelative: currentX is required");
|
|
721
|
+
if (currentY === null || currentY === undefined)
|
|
722
|
+
throw new Error("toRelative: currentY is required");
|
|
615
723
|
|
|
616
724
|
const cmd = command.command;
|
|
617
725
|
const args = command.args.map(D);
|
|
@@ -619,11 +727,11 @@ export function toRelative(command, currentX, currentY) {
|
|
|
619
727
|
const cy = D(currentY);
|
|
620
728
|
|
|
621
729
|
// Z command is already relative (closes to start of subpath)
|
|
622
|
-
if (cmd ===
|
|
730
|
+
if (cmd === "Z" || cmd === "z") {
|
|
623
731
|
return {
|
|
624
|
-
command:
|
|
732
|
+
command: "z",
|
|
625
733
|
args: [],
|
|
626
|
-
verified: true
|
|
734
|
+
verified: true,
|
|
627
735
|
};
|
|
628
736
|
}
|
|
629
737
|
|
|
@@ -632,7 +740,7 @@ export function toRelative(command, currentX, currentY) {
|
|
|
632
740
|
return {
|
|
633
741
|
command: cmd,
|
|
634
742
|
args,
|
|
635
|
-
verified: true
|
|
743
|
+
verified: true,
|
|
636
744
|
};
|
|
637
745
|
}
|
|
638
746
|
|
|
@@ -653,7 +761,7 @@ export function toRelative(command, currentX, currentY) {
|
|
|
653
761
|
return {
|
|
654
762
|
command: relCmd,
|
|
655
763
|
args: relativeArgs,
|
|
656
|
-
verified
|
|
764
|
+
verified,
|
|
657
765
|
};
|
|
658
766
|
}
|
|
659
767
|
|
|
@@ -669,11 +777,15 @@ export function toRelative(command, currentX, currentY) {
|
|
|
669
777
|
* @returns {{command: string, args: Array<Decimal>, verified: boolean}}
|
|
670
778
|
*/
|
|
671
779
|
export function toAbsolute(command, currentX, currentY) {
|
|
672
|
-
if (!command) throw new Error(
|
|
673
|
-
if (!command.command)
|
|
674
|
-
|
|
675
|
-
if (
|
|
676
|
-
|
|
780
|
+
if (!command) throw new Error("toAbsolute: command object is required");
|
|
781
|
+
if (!command.command)
|
|
782
|
+
throw new Error("toAbsolute: command.command is required");
|
|
783
|
+
if (!Array.isArray(command.args))
|
|
784
|
+
throw new Error("toAbsolute: command.args must be an array");
|
|
785
|
+
if (currentX === null || currentX === undefined)
|
|
786
|
+
throw new Error("toAbsolute: currentX is required");
|
|
787
|
+
if (currentY === null || currentY === undefined)
|
|
788
|
+
throw new Error("toAbsolute: currentY is required");
|
|
677
789
|
|
|
678
790
|
const cmd = command.command;
|
|
679
791
|
const args = command.args.map(D);
|
|
@@ -681,11 +793,11 @@ export function toAbsolute(command, currentX, currentY) {
|
|
|
681
793
|
const cy = D(currentY);
|
|
682
794
|
|
|
683
795
|
// Z command is always absolute (closes to start of subpath)
|
|
684
|
-
if (cmd ===
|
|
796
|
+
if (cmd === "Z" || cmd === "z") {
|
|
685
797
|
return {
|
|
686
|
-
command:
|
|
798
|
+
command: "Z",
|
|
687
799
|
args: [],
|
|
688
|
-
verified: true
|
|
800
|
+
verified: true,
|
|
689
801
|
};
|
|
690
802
|
}
|
|
691
803
|
|
|
@@ -694,7 +806,7 @@ export function toAbsolute(command, currentX, currentY) {
|
|
|
694
806
|
return {
|
|
695
807
|
command: cmd,
|
|
696
808
|
args,
|
|
697
|
-
verified: true
|
|
809
|
+
verified: true,
|
|
698
810
|
};
|
|
699
811
|
}
|
|
700
812
|
|
|
@@ -715,7 +827,7 @@ export function toAbsolute(command, currentX, currentY) {
|
|
|
715
827
|
return {
|
|
716
828
|
command: absCmd,
|
|
717
829
|
args: absoluteArgs,
|
|
718
|
-
verified
|
|
830
|
+
verified,
|
|
719
831
|
};
|
|
720
832
|
}
|
|
721
833
|
|
|
@@ -731,23 +843,31 @@ export function toAbsolute(command, currentX, currentY) {
|
|
|
731
843
|
* @returns {string} Formatted command string
|
|
732
844
|
*/
|
|
733
845
|
function formatCommand(command, precision = 6) {
|
|
734
|
-
if (!command) throw new Error(
|
|
735
|
-
if (!command.command)
|
|
736
|
-
|
|
737
|
-
if (
|
|
846
|
+
if (!command) throw new Error("formatCommand: command object is required");
|
|
847
|
+
if (!command.command)
|
|
848
|
+
throw new Error("formatCommand: command.command is required");
|
|
849
|
+
if (!Array.isArray(command.args))
|
|
850
|
+
throw new Error("formatCommand: command.args must be an array");
|
|
851
|
+
if (typeof precision !== "number" || precision < 0)
|
|
852
|
+
throw new Error("formatCommand: precision must be a non-negative number");
|
|
738
853
|
|
|
739
854
|
const cmd = command.command;
|
|
740
|
-
const args = command.args
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
855
|
+
const args = command.args
|
|
856
|
+
.map((arg, index) => {
|
|
857
|
+
// Convert to Decimal if not already, then to number
|
|
858
|
+
const argDecimal = arg instanceof Decimal ? arg : D(arg);
|
|
859
|
+
const num = argDecimal.toNumber();
|
|
860
|
+
// Validate the number is finite
|
|
861
|
+
if (!Number.isFinite(num))
|
|
862
|
+
throw new Error(
|
|
863
|
+
`formatCommand: argument at index ${index} converted to non-finite number: ${num}`,
|
|
864
|
+
);
|
|
865
|
+
// Format with specified precision and remove trailing zeros
|
|
866
|
+
// Use toFixed for precision, then parseFloat to remove trailing zeros
|
|
867
|
+
const formatted = num.toFixed(precision);
|
|
868
|
+
return parseFloat(formatted).toString();
|
|
869
|
+
})
|
|
870
|
+
.join(",");
|
|
751
871
|
|
|
752
872
|
return args.length > 0 ? `${cmd}${args}` : cmd;
|
|
753
873
|
}
|
|
@@ -763,16 +883,29 @@ function formatCommand(command, precision = 6) {
|
|
|
763
883
|
* @returns {{command: string, args: Array<Decimal>, isShorter: boolean, savedBytes: number, verified: boolean}}
|
|
764
884
|
*/
|
|
765
885
|
export function chooseShorterForm(absCommand, relCommand, precision = 6) {
|
|
766
|
-
if (!absCommand) throw new Error(
|
|
767
|
-
if (!absCommand.command)
|
|
768
|
-
|
|
769
|
-
if (!
|
|
770
|
-
|
|
771
|
-
if (!
|
|
772
|
-
if (
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
886
|
+
if (!absCommand) throw new Error("chooseShorterForm: absCommand is required");
|
|
887
|
+
if (!absCommand.command)
|
|
888
|
+
throw new Error("chooseShorterForm: absCommand.command is required");
|
|
889
|
+
if (!Array.isArray(absCommand.args))
|
|
890
|
+
throw new Error("chooseShorterForm: absCommand.args must be an array");
|
|
891
|
+
if (!relCommand) throw new Error("chooseShorterForm: relCommand is required");
|
|
892
|
+
if (!relCommand.command)
|
|
893
|
+
throw new Error("chooseShorterForm: relCommand.command is required");
|
|
894
|
+
if (!Array.isArray(relCommand.args))
|
|
895
|
+
throw new Error("chooseShorterForm: relCommand.args must be an array");
|
|
896
|
+
if (typeof precision !== "number" || precision < 0)
|
|
897
|
+
throw new Error(
|
|
898
|
+
"chooseShorterForm: precision must be a non-negative number",
|
|
899
|
+
);
|
|
900
|
+
|
|
901
|
+
const absStr = formatCommand(
|
|
902
|
+
{ command: absCommand.command, args: absCommand.args.map(D) },
|
|
903
|
+
precision,
|
|
904
|
+
);
|
|
905
|
+
const relStr = formatCommand(
|
|
906
|
+
{ command: relCommand.command, args: relCommand.args.map(D) },
|
|
907
|
+
precision,
|
|
908
|
+
);
|
|
776
909
|
|
|
777
910
|
const absLen = absStr.length;
|
|
778
911
|
const relLen = relStr.length;
|
|
@@ -792,7 +925,7 @@ export function chooseShorterForm(absCommand, relCommand, precision = 6) {
|
|
|
792
925
|
args: relCommand.args.map(D),
|
|
793
926
|
isShorter,
|
|
794
927
|
savedBytes,
|
|
795
|
-
verified
|
|
928
|
+
verified,
|
|
796
929
|
};
|
|
797
930
|
} else {
|
|
798
931
|
return {
|
|
@@ -800,7 +933,7 @@ export function chooseShorterForm(absCommand, relCommand, precision = 6) {
|
|
|
800
933
|
args: absCommand.args.map(D),
|
|
801
934
|
isShorter: false,
|
|
802
935
|
savedBytes: 0,
|
|
803
|
-
verified
|
|
936
|
+
verified,
|
|
804
937
|
};
|
|
805
938
|
}
|
|
806
939
|
}
|
|
@@ -821,26 +954,60 @@ export function chooseShorterForm(absCommand, relCommand, precision = 6) {
|
|
|
821
954
|
* @returns {{commands: Array<{command: string, args: Array<Decimal>}>, collapseCount: number, verified: boolean}}
|
|
822
955
|
*/
|
|
823
956
|
export function collapseRepeated(commands) {
|
|
824
|
-
if (!Array.isArray(commands))
|
|
957
|
+
if (!Array.isArray(commands))
|
|
958
|
+
throw new Error("collapseRepeated: commands must be an array");
|
|
825
959
|
|
|
826
960
|
// Validate each command object
|
|
827
961
|
for (let i = 0; i < commands.length; i++) {
|
|
828
|
-
if (!commands[i])
|
|
829
|
-
|
|
830
|
-
|
|
962
|
+
if (!commands[i])
|
|
963
|
+
throw new Error(
|
|
964
|
+
`collapseRepeated: command at index ${i} is null or undefined`,
|
|
965
|
+
);
|
|
966
|
+
if (!commands[i].command)
|
|
967
|
+
throw new Error(
|
|
968
|
+
`collapseRepeated: command at index ${i} is missing 'command' property`,
|
|
969
|
+
);
|
|
970
|
+
if (!Array.isArray(commands[i].args))
|
|
971
|
+
throw new Error(
|
|
972
|
+
`collapseRepeated: command at index ${i} is missing 'args' array property`,
|
|
973
|
+
);
|
|
831
974
|
// Validate that commands requiring arguments have them
|
|
832
975
|
const cmd = commands[i].command;
|
|
833
|
-
const requiresArgs = [
|
|
976
|
+
const requiresArgs = [
|
|
977
|
+
"M",
|
|
978
|
+
"m",
|
|
979
|
+
"L",
|
|
980
|
+
"l",
|
|
981
|
+
"H",
|
|
982
|
+
"h",
|
|
983
|
+
"V",
|
|
984
|
+
"v",
|
|
985
|
+
"C",
|
|
986
|
+
"c",
|
|
987
|
+
"S",
|
|
988
|
+
"s",
|
|
989
|
+
"Q",
|
|
990
|
+
"q",
|
|
991
|
+
"T",
|
|
992
|
+
"t",
|
|
993
|
+
"A",
|
|
994
|
+
"a",
|
|
995
|
+
].includes(cmd);
|
|
834
996
|
if (requiresArgs && commands[i].args.length === 0) {
|
|
835
|
-
throw new Error(
|
|
997
|
+
throw new Error(
|
|
998
|
+
`collapseRepeated: command '${cmd}' at index ${i} requires arguments but has empty args array`,
|
|
999
|
+
);
|
|
836
1000
|
}
|
|
837
1001
|
}
|
|
838
1002
|
|
|
839
1003
|
if (commands.length < 2) {
|
|
840
1004
|
return {
|
|
841
|
-
commands: commands.map(cmd => ({
|
|
1005
|
+
commands: commands.map((cmd) => ({
|
|
1006
|
+
command: cmd.command,
|
|
1007
|
+
args: cmd.args.map(D),
|
|
1008
|
+
})),
|
|
842
1009
|
collapseCount: 0,
|
|
843
|
-
verified: true
|
|
1010
|
+
verified: true,
|
|
844
1011
|
};
|
|
845
1012
|
}
|
|
846
1013
|
|
|
@@ -857,7 +1024,22 @@ export function collapseRepeated(commands) {
|
|
|
857
1024
|
// Note: M/m are excluded because M has special semantics (first pair is moveto, subsequent pairs become lineto)
|
|
858
1025
|
// Note: A/a are excluded because arc commands have complex 7-parameter structure and flags
|
|
859
1026
|
// Note: Z/z are excluded because they have no arguments
|
|
860
|
-
const canCollapse = [
|
|
1027
|
+
const canCollapse = [
|
|
1028
|
+
"L",
|
|
1029
|
+
"l",
|
|
1030
|
+
"H",
|
|
1031
|
+
"h",
|
|
1032
|
+
"V",
|
|
1033
|
+
"v",
|
|
1034
|
+
"T",
|
|
1035
|
+
"t",
|
|
1036
|
+
"C",
|
|
1037
|
+
"c",
|
|
1038
|
+
"S",
|
|
1039
|
+
"s",
|
|
1040
|
+
"Q",
|
|
1041
|
+
"q",
|
|
1042
|
+
].includes(command);
|
|
861
1043
|
|
|
862
1044
|
if (canCollapse && command === currentCommand) {
|
|
863
1045
|
// Same command - append args
|
|
@@ -879,14 +1061,17 @@ export function collapseRepeated(commands) {
|
|
|
879
1061
|
}
|
|
880
1062
|
|
|
881
1063
|
// VERIFICATION: Count total arguments should remain the same
|
|
882
|
-
const originalArgCount = commands.reduce(
|
|
1064
|
+
const originalArgCount = commands.reduce(
|
|
1065
|
+
(sum, cmd) => sum + cmd.args.length,
|
|
1066
|
+
0,
|
|
1067
|
+
);
|
|
883
1068
|
const resultArgCount = result.reduce((sum, cmd) => sum + cmd.args.length, 0);
|
|
884
1069
|
const verified = originalArgCount === resultArgCount;
|
|
885
1070
|
|
|
886
1071
|
return {
|
|
887
1072
|
commands: result,
|
|
888
1073
|
collapseCount,
|
|
889
|
-
verified
|
|
1074
|
+
verified,
|
|
890
1075
|
};
|
|
891
1076
|
}
|
|
892
1077
|
|
|
@@ -907,10 +1092,14 @@ export function collapseRepeated(commands) {
|
|
|
907
1092
|
* @returns {{canConvert: boolean, deviation: Decimal, verified: boolean}}
|
|
908
1093
|
*/
|
|
909
1094
|
export function lineToZ(lastX, lastY, startX, startY, tolerance = EPSILON) {
|
|
910
|
-
if (lastX === null || lastX === undefined)
|
|
911
|
-
|
|
912
|
-
if (
|
|
913
|
-
|
|
1095
|
+
if (lastX === null || lastX === undefined)
|
|
1096
|
+
throw new Error("lineToZ: lastX is required");
|
|
1097
|
+
if (lastY === null || lastY === undefined)
|
|
1098
|
+
throw new Error("lineToZ: lastY is required");
|
|
1099
|
+
if (startX === null || startX === undefined)
|
|
1100
|
+
throw new Error("lineToZ: startX is required");
|
|
1101
|
+
if (startY === null || startY === undefined)
|
|
1102
|
+
throw new Error("lineToZ: startY is required");
|
|
914
1103
|
|
|
915
1104
|
const tol = D(tolerance);
|
|
916
1105
|
const last = point(lastX, lastY);
|
|
@@ -927,7 +1116,7 @@ export function lineToZ(lastX, lastY, startX, startY, tolerance = EPSILON) {
|
|
|
927
1116
|
return {
|
|
928
1117
|
canConvert,
|
|
929
1118
|
deviation,
|
|
930
|
-
verified
|
|
1119
|
+
verified,
|
|
931
1120
|
};
|
|
932
1121
|
}
|
|
933
1122
|
|
|
@@ -935,11 +1124,7 @@ export function lineToZ(lastX, lastY, startX, startY, tolerance = EPSILON) {
|
|
|
935
1124
|
// Exports
|
|
936
1125
|
// ============================================================================
|
|
937
1126
|
|
|
938
|
-
export {
|
|
939
|
-
EPSILON,
|
|
940
|
-
DEFAULT_TOLERANCE,
|
|
941
|
-
D
|
|
942
|
-
};
|
|
1127
|
+
export { EPSILON, DEFAULT_TOLERANCE, D };
|
|
943
1128
|
|
|
944
1129
|
export default {
|
|
945
1130
|
// Point utilities
|
|
@@ -975,5 +1160,5 @@ export default {
|
|
|
975
1160
|
|
|
976
1161
|
// Constants
|
|
977
1162
|
EPSILON,
|
|
978
|
-
DEFAULT_TOLERANCE
|
|
1163
|
+
DEFAULT_TOLERANCE,
|
|
979
1164
|
};
|