@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.
Files changed (45) hide show
  1. package/bin/svg-matrix.js +310 -61
  2. package/bin/svglinter.cjs +102 -3
  3. package/bin/svgm.js +236 -27
  4. package/package.json +1 -1
  5. package/src/animation-optimization.js +137 -17
  6. package/src/animation-references.js +123 -6
  7. package/src/arc-length.js +213 -4
  8. package/src/bezier-analysis.js +217 -21
  9. package/src/bezier-intersections.js +275 -12
  10. package/src/browser-verify.js +237 -4
  11. package/src/clip-path-resolver.js +168 -0
  12. package/src/convert-path-data.js +479 -28
  13. package/src/css-specificity.js +73 -10
  14. package/src/douglas-peucker.js +219 -2
  15. package/src/flatten-pipeline.js +284 -26
  16. package/src/geometry-to-path.js +250 -25
  17. package/src/gjk-collision.js +236 -33
  18. package/src/index.js +261 -3
  19. package/src/inkscape-support.js +86 -28
  20. package/src/logger.js +48 -3
  21. package/src/marker-resolver.js +278 -74
  22. package/src/mask-resolver.js +265 -66
  23. package/src/matrix.js +44 -5
  24. package/src/mesh-gradient.js +352 -102
  25. package/src/off-canvas-detection.js +382 -13
  26. package/src/path-analysis.js +192 -18
  27. package/src/path-data-plugins.js +309 -5
  28. package/src/path-optimization.js +129 -5
  29. package/src/path-simplification.js +188 -32
  30. package/src/pattern-resolver.js +454 -106
  31. package/src/polygon-clip.js +324 -1
  32. package/src/svg-boolean-ops.js +226 -9
  33. package/src/svg-collections.js +7 -5
  34. package/src/svg-flatten.js +386 -62
  35. package/src/svg-parser.js +179 -8
  36. package/src/svg-rendering-context.js +235 -6
  37. package/src/svg-toolbox.js +45 -8
  38. package/src/svg2-polyfills.js +40 -10
  39. package/src/transform-decomposition.js +258 -32
  40. package/src/transform-optimization.js +259 -13
  41. package/src/transforms2d.js +82 -9
  42. package/src/transforms3d.js +62 -10
  43. package/src/use-symbol-resolver.js +286 -42
  44. package/src/vector.js +64 -8
  45. package/src/verification.js +392 -1
@@ -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: [],