@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/verification.js
CHANGED
|
@@ -35,6 +35,13 @@ export function computeTolerance() {
|
|
|
35
35
|
// Tolerance is 10 orders of magnitude less than precision
|
|
36
36
|
// For precision=80, tolerance = 1e-70
|
|
37
37
|
const precision = Decimal.precision;
|
|
38
|
+
// Ensure minimum precision of 20 to avoid overly loose tolerances
|
|
39
|
+
// If precision < 20, clamp toleranceExp to avoid 1e-1 or worse
|
|
40
|
+
if (precision < 20) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Decimal precision (${precision}) is too low for verification. Minimum required: 20`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
38
45
|
const toleranceExp = Math.max(1, precision - 10);
|
|
39
46
|
return new Decimal(10).pow(-toleranceExp);
|
|
40
47
|
}
|
|
@@ -67,6 +74,24 @@ export function computeTolerance() {
|
|
|
67
74
|
*/
|
|
68
75
|
export function verifyTransformRoundTrip(matrix, x, y) {
|
|
69
76
|
const tolerance = computeTolerance();
|
|
77
|
+
|
|
78
|
+
// Parameter validation: check for null/undefined
|
|
79
|
+
if (!matrix) {
|
|
80
|
+
return {
|
|
81
|
+
valid: false,
|
|
82
|
+
error: new Decimal(Infinity),
|
|
83
|
+
tolerance,
|
|
84
|
+
message: "Matrix parameter is null or undefined",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (x == null || y == null) {
|
|
88
|
+
return {
|
|
89
|
+
valid: false,
|
|
90
|
+
error: new Decimal(Infinity),
|
|
91
|
+
tolerance,
|
|
92
|
+
message: "Coordinates x or y are null or undefined",
|
|
93
|
+
};
|
|
94
|
+
}
|
|
70
95
|
const origX = D(x);
|
|
71
96
|
const origY = D(y);
|
|
72
97
|
|
|
@@ -135,6 +160,23 @@ export function verifyTransformRoundTrip(matrix, x, y) {
|
|
|
135
160
|
export function verifyTransformGeometry(matrix, points) {
|
|
136
161
|
const tolerance = computeTolerance();
|
|
137
162
|
|
|
163
|
+
// Parameter validation: check for null/undefined
|
|
164
|
+
if (!matrix) {
|
|
165
|
+
return {
|
|
166
|
+
valid: false,
|
|
167
|
+
error: ZERO,
|
|
168
|
+
tolerance,
|
|
169
|
+
message: "Matrix parameter is null or undefined",
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (!points || !Array.isArray(points)) {
|
|
173
|
+
return {
|
|
174
|
+
valid: false,
|
|
175
|
+
error: ZERO,
|
|
176
|
+
tolerance,
|
|
177
|
+
message: "Points parameter is null, undefined, or not an array",
|
|
178
|
+
};
|
|
179
|
+
}
|
|
138
180
|
if (points.length < 3) {
|
|
139
181
|
return {
|
|
140
182
|
valid: false,
|
|
@@ -234,6 +276,16 @@ export function verifyTransformGeometry(matrix, points) {
|
|
|
234
276
|
export function verifyMatrixInversion(matrix) {
|
|
235
277
|
const tolerance = computeTolerance();
|
|
236
278
|
|
|
279
|
+
// Parameter validation: check for null/undefined
|
|
280
|
+
if (!matrix) {
|
|
281
|
+
return {
|
|
282
|
+
valid: false,
|
|
283
|
+
error: new Decimal(Infinity),
|
|
284
|
+
tolerance,
|
|
285
|
+
message: "Matrix parameter is null or undefined",
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
237
289
|
try {
|
|
238
290
|
const inverse = matrix.inverse();
|
|
239
291
|
if (!inverse) {
|
|
@@ -313,6 +365,16 @@ export function verifyMatrixInversion(matrix) {
|
|
|
313
365
|
export function verifyMultiplicationAssociativity(A, B, C) {
|
|
314
366
|
const tolerance = computeTolerance();
|
|
315
367
|
|
|
368
|
+
// Parameter validation: check for null/undefined
|
|
369
|
+
if (!A || !B || !C) {
|
|
370
|
+
return {
|
|
371
|
+
valid: false,
|
|
372
|
+
error: new Decimal(Infinity),
|
|
373
|
+
tolerance,
|
|
374
|
+
message: "One or more matrix parameters are null or undefined",
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
316
378
|
try {
|
|
317
379
|
// (A * B) * C
|
|
318
380
|
const AB = A.mul(B);
|
|
@@ -380,6 +442,23 @@ export function verifyPolygonContainment(
|
|
|
380
442
|
// Distance tolerance for curve approximation - points can be slightly outside
|
|
381
443
|
const maxDistOutside = distanceTolerance || new Decimal("1e-6");
|
|
382
444
|
|
|
445
|
+
// Parameter validation: check for null/undefined and array types
|
|
446
|
+
if (!inner || !Array.isArray(inner)) {
|
|
447
|
+
return {
|
|
448
|
+
valid: false,
|
|
449
|
+
error: ZERO,
|
|
450
|
+
tolerance,
|
|
451
|
+
message: "Inner polygon parameter is null, undefined, or not an array",
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
if (!outer || !Array.isArray(outer)) {
|
|
455
|
+
return {
|
|
456
|
+
valid: false,
|
|
457
|
+
error: ZERO,
|
|
458
|
+
tolerance,
|
|
459
|
+
message: "Outer polygon parameter is null, undefined, or not an array",
|
|
460
|
+
};
|
|
461
|
+
}
|
|
383
462
|
if (inner.length < 3 || outer.length < 3) {
|
|
384
463
|
return {
|
|
385
464
|
valid: false,
|
|
@@ -449,6 +528,16 @@ export function verifyPolygonContainment(
|
|
|
449
528
|
* @returns {Decimal} Minimum distance to any polygon edge
|
|
450
529
|
*/
|
|
451
530
|
function minDistanceToPolygonEdge(point, polygon) {
|
|
531
|
+
// Parameter validation: defensive checks for helper function
|
|
532
|
+
if (!point || point.x == null || point.y == null) {
|
|
533
|
+
throw new Error("Invalid point parameter: must have x and y properties");
|
|
534
|
+
}
|
|
535
|
+
if (!polygon || !Array.isArray(polygon) || polygon.length === 0) {
|
|
536
|
+
throw new Error(
|
|
537
|
+
"Invalid polygon parameter: must be non-empty array of points",
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
|
|
452
541
|
let minDist = new Decimal(Infinity);
|
|
453
542
|
const px = D(point.x),
|
|
454
543
|
py = D(point.y);
|
|
@@ -507,6 +596,32 @@ function minDistanceToPolygonEdge(point, polygon) {
|
|
|
507
596
|
export function verifyPolygonIntersection(poly1, poly2, intersection) {
|
|
508
597
|
const tolerance = computeTolerance();
|
|
509
598
|
|
|
599
|
+
// Parameter validation: check for null/undefined and array types
|
|
600
|
+
if (!poly1 || !Array.isArray(poly1)) {
|
|
601
|
+
return {
|
|
602
|
+
valid: false,
|
|
603
|
+
error: ZERO,
|
|
604
|
+
tolerance,
|
|
605
|
+
message: "Poly1 parameter must be an array",
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
if (!poly2 || !Array.isArray(poly2)) {
|
|
609
|
+
return {
|
|
610
|
+
valid: false,
|
|
611
|
+
error: ZERO,
|
|
612
|
+
tolerance,
|
|
613
|
+
message: "Poly2 parameter must be an array",
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
if (!intersection || !Array.isArray(intersection)) {
|
|
617
|
+
return {
|
|
618
|
+
valid: false,
|
|
619
|
+
error: ZERO,
|
|
620
|
+
tolerance,
|
|
621
|
+
message: "Intersection parameter must be an array",
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
|
|
510
625
|
if (intersection.length < 3) {
|
|
511
626
|
// Empty or degenerate intersection
|
|
512
627
|
return {
|
|
@@ -579,10 +694,39 @@ export function verifyPolygonIntersection(poly1, poly2, intersection) {
|
|
|
579
694
|
*/
|
|
580
695
|
export function verifyCircleToPath(cx, cy, r, pathData) {
|
|
581
696
|
const tolerance = computeTolerance();
|
|
697
|
+
|
|
698
|
+
// Parameter validation: check for null/undefined and types
|
|
699
|
+
if (cx == null || cy == null || r == null) {
|
|
700
|
+
return {
|
|
701
|
+
valid: false,
|
|
702
|
+
error: new Decimal(Infinity),
|
|
703
|
+
tolerance,
|
|
704
|
+
message: "Circle parameters (cx, cy, r) cannot be null or undefined",
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
if (typeof pathData !== "string") {
|
|
708
|
+
return {
|
|
709
|
+
valid: false,
|
|
710
|
+
error: new Decimal(Infinity),
|
|
711
|
+
tolerance,
|
|
712
|
+
message: "PathData parameter must be a string",
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
|
|
582
716
|
const cxD = D(cx),
|
|
583
717
|
cyD = D(cy),
|
|
584
718
|
rD = D(r);
|
|
585
719
|
|
|
720
|
+
// Bounds check: radius must be positive
|
|
721
|
+
if (rD.lessThanOrEqualTo(ZERO)) {
|
|
722
|
+
return {
|
|
723
|
+
valid: false,
|
|
724
|
+
error: new Decimal(Infinity),
|
|
725
|
+
tolerance,
|
|
726
|
+
message: "Radius must be positive",
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
|
|
586
730
|
try {
|
|
587
731
|
// Expected key points (cardinal points)
|
|
588
732
|
const expectedPoints = [
|
|
@@ -656,11 +800,40 @@ export function verifyCircleToPath(cx, cy, r, pathData) {
|
|
|
656
800
|
*/
|
|
657
801
|
export function verifyRectToPath(x, y, width, height, pathData) {
|
|
658
802
|
const tolerance = computeTolerance();
|
|
803
|
+
|
|
804
|
+
// Parameter validation: check for null/undefined and types
|
|
805
|
+
if (x == null || y == null || width == null || height == null) {
|
|
806
|
+
return {
|
|
807
|
+
valid: false,
|
|
808
|
+
error: new Decimal(Infinity),
|
|
809
|
+
tolerance,
|
|
810
|
+
message: "Rectangle parameters (x, y, width, height) cannot be null or undefined",
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
if (typeof pathData !== "string") {
|
|
814
|
+
return {
|
|
815
|
+
valid: false,
|
|
816
|
+
error: new Decimal(Infinity),
|
|
817
|
+
tolerance,
|
|
818
|
+
message: "PathData parameter must be a string",
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
|
|
659
822
|
const xD = D(x),
|
|
660
823
|
yD = D(y),
|
|
661
824
|
wD = D(width),
|
|
662
825
|
hD = D(height);
|
|
663
826
|
|
|
827
|
+
// Bounds check: width and height must be positive
|
|
828
|
+
if (wD.lessThanOrEqualTo(ZERO) || hD.lessThanOrEqualTo(ZERO)) {
|
|
829
|
+
return {
|
|
830
|
+
valid: false,
|
|
831
|
+
error: new Decimal(Infinity),
|
|
832
|
+
tolerance,
|
|
833
|
+
message: "Width and height must be positive",
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
|
|
664
837
|
try {
|
|
665
838
|
// Expected corners
|
|
666
839
|
const expectedCorners = [
|
|
@@ -740,6 +913,32 @@ export function verifyRectToPath(x, y, width, height, pathData) {
|
|
|
740
913
|
export function verifyLinearGradientTransform(original, baked, matrix) {
|
|
741
914
|
const tolerance = computeTolerance();
|
|
742
915
|
|
|
916
|
+
// Parameter validation: check for null/undefined and types
|
|
917
|
+
if (!original || typeof original !== "object") {
|
|
918
|
+
return {
|
|
919
|
+
valid: false,
|
|
920
|
+
error: new Decimal(Infinity),
|
|
921
|
+
tolerance,
|
|
922
|
+
message: "Original gradient parameter must be an object",
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
if (!baked || typeof baked !== "object") {
|
|
926
|
+
return {
|
|
927
|
+
valid: false,
|
|
928
|
+
error: new Decimal(Infinity),
|
|
929
|
+
tolerance,
|
|
930
|
+
message: "Baked gradient parameter must be an object",
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
if (!matrix) {
|
|
934
|
+
return {
|
|
935
|
+
valid: false,
|
|
936
|
+
error: new Decimal(Infinity),
|
|
937
|
+
tolerance,
|
|
938
|
+
message: "Matrix parameter is null or undefined",
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
|
|
743
942
|
try {
|
|
744
943
|
// Transform original points using the provided matrix
|
|
745
944
|
const [expX1, expY1] = Transforms2D.applyTransform(
|
|
@@ -802,6 +1001,17 @@ export function verifyLinearGradientTransform(original, baked, matrix) {
|
|
|
802
1001
|
* @returns {Decimal} Signed area of the triangle
|
|
803
1002
|
*/
|
|
804
1003
|
function triangleArea(p1, p2, p3) {
|
|
1004
|
+
// Parameter validation: defensive checks for helper function
|
|
1005
|
+
if (!p1 || p1.x == null || p1.y == null) {
|
|
1006
|
+
throw new Error("Invalid p1 parameter: must have x and y properties");
|
|
1007
|
+
}
|
|
1008
|
+
if (!p2 || p2.x == null || p2.y == null) {
|
|
1009
|
+
throw new Error("Invalid p2 parameter: must have x and y properties");
|
|
1010
|
+
}
|
|
1011
|
+
if (!p3 || p3.x == null || p3.y == null) {
|
|
1012
|
+
throw new Error("Invalid p3 parameter: must have x and y properties");
|
|
1013
|
+
}
|
|
1014
|
+
|
|
805
1015
|
const x1 = D(p1.x),
|
|
806
1016
|
y1 = D(p1.y);
|
|
807
1017
|
const x2 = D(p2.x),
|
|
@@ -841,6 +1051,10 @@ function areCollinear(p1, p2, p3, tolerance) {
|
|
|
841
1051
|
* @returns {Decimal} Absolute area of the polygon
|
|
842
1052
|
*/
|
|
843
1053
|
function polygonArea(polygon) {
|
|
1054
|
+
// Parameter validation: defensive checks for helper function
|
|
1055
|
+
if (!polygon || !Array.isArray(polygon)) {
|
|
1056
|
+
throw new Error("Invalid polygon parameter: must be an array");
|
|
1057
|
+
}
|
|
844
1058
|
if (polygon.length < 3) return ZERO;
|
|
845
1059
|
|
|
846
1060
|
let area = ZERO;
|
|
@@ -866,6 +1080,16 @@ function polygonArea(polygon) {
|
|
|
866
1080
|
* @returns {boolean} True if point is inside or on polygon boundary
|
|
867
1081
|
*/
|
|
868
1082
|
function isPointInPolygon(point, polygon) {
|
|
1083
|
+
// Parameter validation: defensive checks for helper function
|
|
1084
|
+
if (!point || point.x == null || point.y == null) {
|
|
1085
|
+
throw new Error("Invalid point parameter: must have x and y properties");
|
|
1086
|
+
}
|
|
1087
|
+
if (!polygon || !Array.isArray(polygon) || polygon.length === 0) {
|
|
1088
|
+
throw new Error(
|
|
1089
|
+
"Invalid polygon parameter: must be non-empty array of points",
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
869
1093
|
const px = D(point.x),
|
|
870
1094
|
py = D(point.y);
|
|
871
1095
|
const n = polygon.length;
|
|
@@ -901,6 +1125,19 @@ function isPointInPolygon(point, polygon) {
|
|
|
901
1125
|
* @returns {boolean} True if point is on the segment within tolerance
|
|
902
1126
|
*/
|
|
903
1127
|
function isPointOnSegment(point, segStart, segEnd) {
|
|
1128
|
+
// Parameter validation: defensive checks for helper function
|
|
1129
|
+
if (!point || point.x == null || point.y == null) {
|
|
1130
|
+
throw new Error("Invalid point parameter: must have x and y properties");
|
|
1131
|
+
}
|
|
1132
|
+
if (!segStart || segStart.x == null || segStart.y == null) {
|
|
1133
|
+
throw new Error(
|
|
1134
|
+
"Invalid segStart parameter: must have x and y properties",
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
1137
|
+
if (!segEnd || segEnd.x == null || segEnd.y == null) {
|
|
1138
|
+
throw new Error("Invalid segEnd parameter: must have x and y properties");
|
|
1139
|
+
}
|
|
1140
|
+
|
|
904
1141
|
const tolerance = computeTolerance();
|
|
905
1142
|
const px = D(point.x),
|
|
906
1143
|
py = D(point.y);
|
|
@@ -943,6 +1180,14 @@ function isPointOnSegment(point, segStart, segEnd) {
|
|
|
943
1180
|
* @returns {Decimal} Euclidean distance between the points
|
|
944
1181
|
*/
|
|
945
1182
|
function pointDistance(p1, p2) {
|
|
1183
|
+
// Parameter validation: defensive checks for helper function
|
|
1184
|
+
if (!p1 || p1.x == null || p1.y == null) {
|
|
1185
|
+
throw new Error("Invalid p1 parameter: must have x and y properties");
|
|
1186
|
+
}
|
|
1187
|
+
if (!p2 || p2.x == null || p2.y == null) {
|
|
1188
|
+
throw new Error("Invalid p2 parameter: must have x and y properties");
|
|
1189
|
+
}
|
|
1190
|
+
|
|
946
1191
|
const dx = D(p2.x).minus(D(p1.x));
|
|
947
1192
|
const dy = D(p2.y).minus(D(p1.y));
|
|
948
1193
|
return dx.times(dx).plus(dy.times(dy)).sqrt();
|
|
@@ -955,6 +1200,11 @@ function pointDistance(p1, p2) {
|
|
|
955
1200
|
* @returns {Array<{x: Decimal, y: Decimal}>} Array of extracted coordinate points
|
|
956
1201
|
*/
|
|
957
1202
|
function extractPathPoints(pathData) {
|
|
1203
|
+
// Parameter validation: defensive checks for helper function
|
|
1204
|
+
if (typeof pathData !== "string") {
|
|
1205
|
+
throw new Error("Invalid pathData parameter: must be a string");
|
|
1206
|
+
}
|
|
1207
|
+
|
|
958
1208
|
const points = [];
|
|
959
1209
|
// Match all number pairs in path data using matchAll
|
|
960
1210
|
const regex =
|
|
@@ -976,6 +1226,13 @@ function extractPathPoints(pathData) {
|
|
|
976
1226
|
* @returns {{x: Decimal, y: Decimal}|null} Nearest point or null if array is empty
|
|
977
1227
|
*/
|
|
978
1228
|
function findNearestPoint(target, points) {
|
|
1229
|
+
// Parameter validation: defensive checks for helper function
|
|
1230
|
+
if (!target || target.x == null || target.y == null) {
|
|
1231
|
+
throw new Error("Invalid target parameter: must have x and y properties");
|
|
1232
|
+
}
|
|
1233
|
+
if (!points || !Array.isArray(points)) {
|
|
1234
|
+
throw new Error("Invalid points parameter: must be an array");
|
|
1235
|
+
}
|
|
979
1236
|
if (points.length === 0) return null;
|
|
980
1237
|
|
|
981
1238
|
let nearest = points[0];
|
|
@@ -1090,6 +1347,24 @@ export function verifyClipPathE2E(
|
|
|
1090
1347
|
// Ensure outsideFragments is an array
|
|
1091
1348
|
const fragments = outsideFragments || [];
|
|
1092
1349
|
|
|
1350
|
+
// Parameter validation: check for null/undefined and array types
|
|
1351
|
+
if (!original || !Array.isArray(original)) {
|
|
1352
|
+
return {
|
|
1353
|
+
valid: false,
|
|
1354
|
+
error: new Decimal(Infinity),
|
|
1355
|
+
tolerance,
|
|
1356
|
+
message: "Original polygon parameter must be an array",
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
if (!clipped || !Array.isArray(clipped)) {
|
|
1360
|
+
return {
|
|
1361
|
+
valid: false,
|
|
1362
|
+
error: new Decimal(Infinity),
|
|
1363
|
+
tolerance,
|
|
1364
|
+
message: "Clipped polygon parameter must be an array",
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1093
1368
|
try {
|
|
1094
1369
|
// Compute areas with high precision
|
|
1095
1370
|
const originalArea = polygonArea(original);
|
|
@@ -1158,9 +1433,46 @@ export function verifyClipPathE2E(
|
|
|
1158
1433
|
* @returns {VerificationResult} Verification result
|
|
1159
1434
|
*/
|
|
1160
1435
|
export function verifyPipelineE2E(params) {
|
|
1161
|
-
const { originalPoints, flattenedPoints, expectedTransform } = params;
|
|
1162
1436
|
const tolerance = computeTolerance();
|
|
1163
1437
|
|
|
1438
|
+
// Parameter validation: check params object exists
|
|
1439
|
+
if (!params || typeof params !== "object") {
|
|
1440
|
+
return {
|
|
1441
|
+
valid: false,
|
|
1442
|
+
error: new Decimal(Infinity),
|
|
1443
|
+
tolerance,
|
|
1444
|
+
message: "Params parameter must be an object",
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
const { originalPoints, flattenedPoints, expectedTransform } = params;
|
|
1449
|
+
|
|
1450
|
+
// Validate required properties
|
|
1451
|
+
if (!originalPoints || !Array.isArray(originalPoints)) {
|
|
1452
|
+
return {
|
|
1453
|
+
valid: false,
|
|
1454
|
+
error: new Decimal(Infinity),
|
|
1455
|
+
tolerance,
|
|
1456
|
+
message: "originalPoints must be an array",
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
if (!flattenedPoints || !Array.isArray(flattenedPoints)) {
|
|
1460
|
+
return {
|
|
1461
|
+
valid: false,
|
|
1462
|
+
error: new Decimal(Infinity),
|
|
1463
|
+
tolerance,
|
|
1464
|
+
message: "flattenedPoints must be an array",
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
if (!expectedTransform) {
|
|
1468
|
+
return {
|
|
1469
|
+
valid: false,
|
|
1470
|
+
error: new Decimal(Infinity),
|
|
1471
|
+
tolerance,
|
|
1472
|
+
message: "expectedTransform is required",
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1164
1476
|
if (originalPoints.length !== flattenedPoints.length) {
|
|
1165
1477
|
return {
|
|
1166
1478
|
valid: false,
|
|
@@ -1240,6 +1552,24 @@ export function verifyPipelineE2E(params) {
|
|
|
1240
1552
|
export function verifyPolygonUnionArea(polygons, expectedArea) {
|
|
1241
1553
|
const tolerance = computeTolerance();
|
|
1242
1554
|
|
|
1555
|
+
// Parameter validation: check for null/undefined and array types
|
|
1556
|
+
if (!polygons || !Array.isArray(polygons)) {
|
|
1557
|
+
return {
|
|
1558
|
+
valid: false,
|
|
1559
|
+
error: new Decimal(Infinity),
|
|
1560
|
+
tolerance,
|
|
1561
|
+
message: "Polygons parameter must be an array",
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1564
|
+
if (expectedArea == null) {
|
|
1565
|
+
return {
|
|
1566
|
+
valid: false,
|
|
1567
|
+
error: new Decimal(Infinity),
|
|
1568
|
+
tolerance,
|
|
1569
|
+
message: "ExpectedArea parameter is required",
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1243
1573
|
try {
|
|
1244
1574
|
let totalArea = ZERO;
|
|
1245
1575
|
for (const poly of polygons) {
|
|
@@ -1287,6 +1617,19 @@ export function verifyPolygonUnionArea(polygons, expectedArea) {
|
|
|
1287
1617
|
* @returns {boolean} True if point is on the inside side of the edge
|
|
1288
1618
|
*/
|
|
1289
1619
|
function isInsideEdgeE2E(point, edgeStart, edgeEnd) {
|
|
1620
|
+
// Parameter validation: defensive checks for helper function
|
|
1621
|
+
if (!point || point.x == null || point.y == null) {
|
|
1622
|
+
throw new Error("Invalid point parameter: must have x and y properties");
|
|
1623
|
+
}
|
|
1624
|
+
if (!edgeStart || edgeStart.x == null || edgeStart.y == null) {
|
|
1625
|
+
throw new Error(
|
|
1626
|
+
"Invalid edgeStart parameter: must have x and y properties",
|
|
1627
|
+
);
|
|
1628
|
+
}
|
|
1629
|
+
if (!edgeEnd || edgeEnd.x == null || edgeEnd.y == null) {
|
|
1630
|
+
throw new Error("Invalid edgeEnd parameter: must have x and y properties");
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1290
1633
|
const px = D(point.x).toNumber();
|
|
1291
1634
|
const py = D(point.y).toNumber();
|
|
1292
1635
|
const sx = D(edgeStart.x).toNumber();
|
|
@@ -1308,6 +1651,20 @@ function isInsideEdgeE2E(point, edgeStart, edgeEnd) {
|
|
|
1308
1651
|
* @returns {{x: Decimal, y: Decimal}} Intersection point
|
|
1309
1652
|
*/
|
|
1310
1653
|
function lineIntersectE2E(p1, p2, p3, p4) {
|
|
1654
|
+
// Parameter validation: defensive checks for helper function
|
|
1655
|
+
if (!p1 || p1.x == null || p1.y == null) {
|
|
1656
|
+
throw new Error("Invalid p1 parameter: must have x and y properties");
|
|
1657
|
+
}
|
|
1658
|
+
if (!p2 || p2.x == null || p2.y == null) {
|
|
1659
|
+
throw new Error("Invalid p2 parameter: must have x and y properties");
|
|
1660
|
+
}
|
|
1661
|
+
if (!p3 || p3.x == null || p3.y == null) {
|
|
1662
|
+
throw new Error("Invalid p3 parameter: must have x and y properties");
|
|
1663
|
+
}
|
|
1664
|
+
if (!p4 || p4.x == null || p4.y == null) {
|
|
1665
|
+
throw new Error("Invalid p4 parameter: must have x and y properties");
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1311
1668
|
const x1 = D(p1.x).toNumber(),
|
|
1312
1669
|
y1 = D(p1.y).toNumber();
|
|
1313
1670
|
const x2 = D(p2.x).toNumber(),
|
|
@@ -1318,6 +1675,7 @@ function lineIntersectE2E(p1, p2, p3, p4) {
|
|
|
1318
1675
|
y4 = D(p4.y).toNumber();
|
|
1319
1676
|
|
|
1320
1677
|
const denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
|
1678
|
+
// Division by zero check: if lines are parallel, return midpoint
|
|
1321
1679
|
if (Math.abs(denom) < 1e-10) {
|
|
1322
1680
|
return { x: D((x1 + x2) / 2), y: D((y1 + y2) / 2) };
|
|
1323
1681
|
}
|
|
@@ -1345,12 +1703,45 @@ function lineIntersectE2E(p1, p2, p3, p4) {
|
|
|
1345
1703
|
* @returns {Object} Comprehensive verification report
|
|
1346
1704
|
*/
|
|
1347
1705
|
export function verifyPathTransformation(params) {
|
|
1706
|
+
// Parameter validation: check params object exists
|
|
1707
|
+
if (!params || typeof params !== "object") {
|
|
1708
|
+
return {
|
|
1709
|
+
allPassed: false,
|
|
1710
|
+
verifications: [
|
|
1711
|
+
{
|
|
1712
|
+
name: "Parameter Validation",
|
|
1713
|
+
valid: false,
|
|
1714
|
+
error: new Decimal(Infinity),
|
|
1715
|
+
tolerance: computeTolerance(),
|
|
1716
|
+
message: "Params parameter must be an object",
|
|
1717
|
+
},
|
|
1718
|
+
],
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1348
1722
|
const {
|
|
1349
1723
|
matrix,
|
|
1350
1724
|
originalPath: _originalPath,
|
|
1351
1725
|
transformedPath: _transformedPath,
|
|
1352
1726
|
testPoints = [],
|
|
1353
1727
|
} = params;
|
|
1728
|
+
|
|
1729
|
+
// Validate required properties
|
|
1730
|
+
if (!matrix) {
|
|
1731
|
+
return {
|
|
1732
|
+
allPassed: false,
|
|
1733
|
+
verifications: [
|
|
1734
|
+
{
|
|
1735
|
+
name: "Parameter Validation",
|
|
1736
|
+
valid: false,
|
|
1737
|
+
error: new Decimal(Infinity),
|
|
1738
|
+
tolerance: computeTolerance(),
|
|
1739
|
+
message: "Matrix parameter is required",
|
|
1740
|
+
},
|
|
1741
|
+
],
|
|
1742
|
+
};
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1354
1745
|
const results = {
|
|
1355
1746
|
allPassed: true,
|
|
1356
1747
|
verifications: [],
|