@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
@@ -84,6 +84,19 @@ const DEFAULT_OPTIONS = {
84
84
  * @returns {{svg: string, stats: Object}} Flattened SVG and statistics
85
85
  */
86
86
  export function flattenSVG(svgString, options = {}) {
87
+ // Validate required parameters
88
+ if (svgString === null || svgString === undefined) {
89
+ throw new Error("flattenSVG: svgString parameter is required");
90
+ }
91
+ if (typeof svgString !== "string") {
92
+ throw new Error(
93
+ `flattenSVG: svgString must be a string, got ${typeof svgString}`,
94
+ );
95
+ }
96
+ if (svgString.trim().length === 0) {
97
+ throw new Error("flattenSVG: svgString cannot be empty");
98
+ }
99
+
87
100
  const opts = { ...DEFAULT_OPTIONS, ...options };
88
101
  const stats = {
89
102
  useResolved: 0,
@@ -260,6 +273,17 @@ function containsPreserveElements(el) {
260
273
  }
261
274
 
262
275
  function resolveAllUseElements(root, defsMap, opts) {
276
+ // Validate parameters
277
+ if (!root || !root.getElementsByTagName) {
278
+ throw new Error("resolveAllUseElements: invalid root element");
279
+ }
280
+ if (!defsMap || !(defsMap instanceof Map)) {
281
+ throw new Error("resolveAllUseElements: defsMap must be a Map");
282
+ }
283
+ if (!opts || typeof opts !== "object") {
284
+ throw new Error("resolveAllUseElements: opts must be an object");
285
+ }
286
+
263
287
  const errors = [];
264
288
  let count = 0;
265
289
 
@@ -306,6 +330,11 @@ function resolveAllUseElements(root, defsMap, opts) {
306
330
  // Get use element positioning and transform
307
331
  const useX = parseFloat(useEl.getAttribute("x") || "0");
308
332
  const useY = parseFloat(useEl.getAttribute("y") || "0");
333
+ // Validate parseFloat results to prevent NaN propagation
334
+ if (isNaN(useX) || isNaN(useY)) {
335
+ errors.push(`use: invalid x/y coordinates for element #${refId}`);
336
+ continue;
337
+ }
309
338
  const useTransform = useEl.getAttribute("transform") || "";
310
339
 
311
340
  // Build combined transform: use transform + translation from x/y
@@ -436,6 +465,17 @@ function resolveAllUseElements(root, defsMap, opts) {
436
465
  * @private
437
466
  */
438
467
  function resolveAllMarkers(root, defsMap, opts) {
468
+ // Validate parameters
469
+ if (!root || !root.getElementsByTagName) {
470
+ throw new Error("resolveAllMarkers: invalid root element");
471
+ }
472
+ if (!defsMap || !(defsMap instanceof Map)) {
473
+ throw new Error("resolveAllMarkers: defsMap must be a Map");
474
+ }
475
+ if (!opts || typeof opts !== "object") {
476
+ throw new Error("resolveAllMarkers: opts must be an object");
477
+ }
478
+
439
479
  const errors = [];
440
480
  let count = 0;
441
481
 
@@ -517,6 +557,17 @@ function resolveAllMarkers(root, defsMap, opts) {
517
557
  * @private
518
558
  */
519
559
  function resolveAllPatterns(root, defsMap, opts) {
560
+ // Validate parameters
561
+ if (!root || !root.getElementsByTagName) {
562
+ throw new Error("resolveAllPatterns: invalid root element");
563
+ }
564
+ if (!defsMap || !(defsMap instanceof Map)) {
565
+ throw new Error("resolveAllPatterns: defsMap must be a Map");
566
+ }
567
+ if (!opts || typeof opts !== "object") {
568
+ throw new Error("resolveAllPatterns: opts must be an object");
569
+ }
570
+
520
571
  const errors = [];
521
572
  let count = 0;
522
573
 
@@ -617,6 +668,17 @@ function resolveAllPatterns(root, defsMap, opts) {
617
668
  * @private
618
669
  */
619
670
  function resolveAllMasks(root, defsMap, opts) {
671
+ // Validate parameters
672
+ if (!root || !root.getElementsByTagName) {
673
+ throw new Error("resolveAllMasks: invalid root element");
674
+ }
675
+ if (!defsMap || !(defsMap instanceof Map)) {
676
+ throw new Error("resolveAllMasks: defsMap must be a Map");
677
+ }
678
+ if (!opts || typeof opts !== "object") {
679
+ throw new Error("resolveAllMasks: opts must be an object");
680
+ }
681
+
620
682
  const errors = [];
621
683
  let count = 0;
622
684
 
@@ -718,6 +780,20 @@ function resolveAllMasks(root, defsMap, opts) {
718
780
  * @private
719
781
  */
720
782
  function applyAllClipPaths(root, defsMap, opts, stats) {
783
+ // Validate parameters
784
+ if (!root || !root.getElementsByTagName) {
785
+ throw new Error("applyAllClipPaths: invalid root element");
786
+ }
787
+ if (!defsMap || !(defsMap instanceof Map)) {
788
+ throw new Error("applyAllClipPaths: defsMap must be a Map");
789
+ }
790
+ if (!opts || typeof opts !== "object") {
791
+ throw new Error("applyAllClipPaths: opts must be an object");
792
+ }
793
+ if (!stats || typeof stats !== "object") {
794
+ throw new Error("applyAllClipPaths: stats must be an object");
795
+ }
796
+
721
797
  const errors = [];
722
798
  let count = 0;
723
799
 
@@ -768,12 +844,14 @@ function applyAllClipPaths(root, defsMap, opts, stats) {
768
844
 
769
845
  // Get clip path data from clipPath element's children
770
846
  let clipPathData = "";
771
- for (const child of clipPathEl.children) {
772
- // Use tagName check instead of instanceof
773
- if (child && child.tagName) {
774
- const childPath = getElementPathData(child, opts.precision);
775
- if (childPath) {
776
- clipPathData += (clipPathData ? " " : "") + childPath;
847
+ if (clipPathEl.children && clipPathEl.children.length > 0) {
848
+ for (const child of clipPathEl.children) {
849
+ // Use tagName check instead of instanceof
850
+ if (child && child.tagName) {
851
+ const childPath = getElementPathData(child, opts.precision);
852
+ if (childPath) {
853
+ clipPathData += (clipPathData ? " " : "") + childPath;
854
+ }
777
855
  }
778
856
  }
779
857
  }
@@ -914,6 +992,17 @@ function applyAllClipPaths(root, defsMap, opts, stats) {
914
992
  * @private
915
993
  */
916
994
  function flattenAllTransforms(root, opts, stats) {
995
+ // Validate parameters
996
+ if (!root || !root.getElementsByTagName) {
997
+ throw new Error("flattenAllTransforms: invalid root element");
998
+ }
999
+ if (!opts || typeof opts !== "object") {
1000
+ throw new Error("flattenAllTransforms: opts must be an object");
1001
+ }
1002
+ if (!stats || typeof stats !== "object") {
1003
+ throw new Error("flattenAllTransforms: stats must be an object");
1004
+ }
1005
+
917
1006
  const errors = [];
918
1007
  let count = 0;
919
1008
 
@@ -1028,6 +1117,20 @@ function flattenAllTransforms(root, opts, stats) {
1028
1117
  * @private
1029
1118
  */
1030
1119
  function propagateTransformToChildren(group, ctm, opts, stats) {
1120
+ // Validate parameters
1121
+ if (!group || !group.children) {
1122
+ throw new Error("propagateTransformToChildren: invalid group element");
1123
+ }
1124
+ if (!ctm || !ctm.data) {
1125
+ throw new Error("propagateTransformToChildren: invalid matrix");
1126
+ }
1127
+ if (!opts || typeof opts !== "object") {
1128
+ throw new Error("propagateTransformToChildren: opts must be an object");
1129
+ }
1130
+ if (!stats || typeof stats !== "object") {
1131
+ throw new Error("propagateTransformToChildren: stats must be an object");
1132
+ }
1133
+
1031
1134
  for (const child of [...group.children]) {
1032
1135
  // Use tagName check instead of instanceof
1033
1136
  if (!(child && child.tagName)) continue;
@@ -1107,6 +1210,17 @@ function propagateTransformToChildren(group, ctm, opts, stats) {
1107
1210
  * @private
1108
1211
  */
1109
1212
  function bakeAllGradientTransforms(root, opts, stats) {
1213
+ // Validate parameters
1214
+ if (!root || !root.getElementsByTagName) {
1215
+ throw new Error("bakeAllGradientTransforms: invalid root element");
1216
+ }
1217
+ if (!opts || typeof opts !== "object") {
1218
+ throw new Error("bakeAllGradientTransforms: opts must be an object");
1219
+ }
1220
+ if (!stats || typeof stats !== "object") {
1221
+ throw new Error("bakeAllGradientTransforms: stats must be an object");
1222
+ }
1223
+
1110
1224
  const errors = [];
1111
1225
  let count = 0;
1112
1226
 
@@ -1125,6 +1239,14 @@ function bakeAllGradientTransforms(root, opts, stats) {
1125
1239
  const x2 = parseFloat(grad.getAttribute("x2") || "1");
1126
1240
  const y2 = parseFloat(grad.getAttribute("y2") || "0");
1127
1241
 
1242
+ // Validate parseFloat results to prevent NaN propagation
1243
+ if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) {
1244
+ errors.push(
1245
+ `linearGradient: invalid coordinate values for gradient ${grad.getAttribute("id") || "unknown"}`,
1246
+ );
1247
+ continue;
1248
+ }
1249
+
1128
1250
  const [tx1, ty1] = Transforms2D.applyTransform(ctm, x1, y1);
1129
1251
  const [tx2, ty2] = Transforms2D.applyTransform(ctm, x2, y2);
1130
1252
 
@@ -1178,13 +1300,33 @@ function bakeAllGradientTransforms(root, opts, stats) {
1178
1300
  const fy = parseFloat(grad.getAttribute("fy") || cy.toString());
1179
1301
  const r = parseFloat(grad.getAttribute("r") || "0.5");
1180
1302
 
1303
+ // Validate parseFloat results to prevent NaN propagation
1304
+ if (isNaN(cx) || isNaN(cy) || isNaN(fx) || isNaN(fy) || isNaN(r)) {
1305
+ errors.push(
1306
+ `radialGradient: invalid coordinate/radius values for gradient ${grad.getAttribute("id") || "unknown"}`,
1307
+ );
1308
+ continue;
1309
+ }
1310
+ if (r <= 0) {
1311
+ errors.push(
1312
+ `radialGradient: radius must be positive for gradient ${grad.getAttribute("id") || "unknown"}`,
1313
+ );
1314
+ continue;
1315
+ }
1316
+
1181
1317
  const [tcx, tcy] = Transforms2D.applyTransform(ctm, cx, cy);
1182
1318
  const [tfx, tfy] = Transforms2D.applyTransform(ctm, fx, fy);
1183
1319
 
1184
1320
  // Scale radius by average scale factor
1185
- const scale = Math.sqrt(
1186
- Math.abs(ctm.data[0][0].toNumber() * ctm.data[1][1].toNumber()),
1187
- );
1321
+ const scaleProduct =
1322
+ ctm.data[0][0].toNumber() * ctm.data[1][1].toNumber();
1323
+ if (Math.abs(scaleProduct) < 1e-10) {
1324
+ errors.push(
1325
+ `radialGradient: matrix determinant too close to zero (degenerate transform)`,
1326
+ );
1327
+ continue;
1328
+ }
1329
+ const scale = Math.sqrt(Math.abs(scaleProduct));
1188
1330
  const tr = r * scale;
1189
1331
 
1190
1332
  grad.setAttribute("cx", tcx.toFixed(opts.precision));
@@ -1220,10 +1362,19 @@ function bakeAllGradientTransforms(root, opts, stats) {
1220
1362
  * @private
1221
1363
  */
1222
1364
  function collectAllReferences(root) {
1365
+ // Validate parameter
1366
+ if (!root || !root.getElementsByTagName) {
1367
+ throw new Error("collectAllReferences: invalid root element");
1368
+ }
1369
+
1223
1370
  const usedIds = new Set();
1224
1371
 
1225
1372
  // Collect references from an element and all its children
1226
1373
  const collectReferences = (el) => {
1374
+ // Validate element parameter
1375
+ if (!el || !el.getAttributeNames) {
1376
+ return;
1377
+ }
1227
1378
  // Check for <style> elements and parse their CSS content for url(#id) references
1228
1379
  // This is CRITICAL for SVG 2.0 features like shape-inside: url(#textShape)
1229
1380
  if (el.tagName && el.tagName.toLowerCase() === "style") {
@@ -1289,10 +1440,23 @@ function collectAllReferences(root) {
1289
1440
  * @private
1290
1441
  */
1291
1442
  function removeUnusedDefinitions(root, referencedIds) {
1443
+ // Validate parameters
1444
+ if (!root || !root.getElementsByTagName) {
1445
+ throw new Error("removeUnusedDefinitions: invalid root element");
1446
+ }
1447
+ if (!referencedIds || !(referencedIds instanceof Set)) {
1448
+ throw new Error("removeUnusedDefinitions: referencedIds must be a Set");
1449
+ }
1450
+
1292
1451
  let count = 0;
1293
1452
 
1294
1453
  // Check if an element or any of its descendants has a referenced ID
1295
1454
  function hasReferencedDescendant(el) {
1455
+ // Validate element
1456
+ if (!el || !el.getAttribute) {
1457
+ return false;
1458
+ }
1459
+
1296
1460
  // Check self
1297
1461
  const id = el.getAttribute("id");
1298
1462
  if (id && referencedIds.has(id)) {
@@ -1346,6 +1510,16 @@ function removeUnusedDefinitions(root, referencedIds) {
1346
1510
  * @private
1347
1511
  */
1348
1512
  function getElementPathData(el, precision) {
1513
+ // Validate parameters
1514
+ if (!el || !el.tagName) {
1515
+ return null;
1516
+ }
1517
+ if (typeof precision !== "number" || isNaN(precision) || precision < 0) {
1518
+ throw new Error(
1519
+ `getElementPathData: precision must be a non-negative number, got ${precision}`,
1520
+ );
1521
+ }
1522
+
1349
1523
  const tagName = el.tagName.toLowerCase();
1350
1524
 
1351
1525
  if (tagName === "path") {
@@ -1355,21 +1529,33 @@ function getElementPathData(el, precision) {
1355
1529
  // Use GeometryToPath for shape conversion
1356
1530
  const getAttr = (name, def = 0) => {
1357
1531
  const val = el.getAttribute(name);
1358
- return val !== null ? parseFloat(val) : def;
1532
+ if (val === null) return def;
1533
+ const parsed = parseFloat(val);
1534
+ // Validate parseFloat result to prevent NaN propagation
1535
+ if (isNaN(parsed)) {
1536
+ throw new Error(
1537
+ `getElementPathData: invalid numeric value "${val}" for attribute "${name}"`,
1538
+ );
1539
+ }
1540
+ return parsed;
1359
1541
  };
1360
1542
 
1361
1543
  switch (tagName) {
1362
- case "rect":
1544
+ case "rect": {
1545
+ const ry = el.getAttribute("ry");
1546
+ // ry can be null (not specified), or a numeric value including 0
1547
+ const ryValue = ry !== null ? getAttr("ry") : null;
1363
1548
  return GeometryToPath.rectToPathData(
1364
1549
  getAttr("x"),
1365
1550
  getAttr("y"),
1366
1551
  getAttr("width"),
1367
1552
  getAttr("height"),
1368
1553
  getAttr("rx"),
1369
- getAttr("ry") || null,
1554
+ ryValue,
1370
1555
  false,
1371
1556
  precision,
1372
1557
  );
1558
+ }
1373
1559
  case "circle":
1374
1560
  return GeometryToPath.circleToPathData(
1375
1561
  getAttr("cx"),
@@ -1417,6 +1603,11 @@ function getElementPathData(el, precision) {
1417
1603
  * @private
1418
1604
  */
1419
1605
  function getElementBBox(el) {
1606
+ // Validate parameter
1607
+ if (!el || !el.tagName) {
1608
+ return null;
1609
+ }
1610
+
1420
1611
  const pathData = getElementPathData(el, 6);
1421
1612
  if (!pathData) return null;
1422
1613
 
@@ -1489,6 +1680,11 @@ function getElementBBox(el) {
1489
1680
  * - Visual effects (opacity, paint-order, vector-effect)
1490
1681
  */
1491
1682
  function extractPresentationAttrs(el) {
1683
+ // Validate parameter
1684
+ if (!el || !el.getAttribute) {
1685
+ return {};
1686
+ }
1687
+
1492
1688
  const presentationAttrs = [
1493
1689
  // Stroke properties
1494
1690
  "stroke",
@@ -1580,6 +1776,11 @@ function extractPresentationAttrs(el) {
1580
1776
  * attribute instead.
1581
1777
  */
1582
1778
  function getShapeSpecificAttrs(tagName) {
1779
+ // Validate parameter
1780
+ if (!tagName || typeof tagName !== "string") {
1781
+ return [];
1782
+ }
1783
+
1583
1784
  const attrs = {
1584
1785
  rect: ["x", "y", "width", "height", "rx", "ry"],
1585
1786
  circle: ["cx", "cy", "r"],
@@ -1610,12 +1811,42 @@ function getShapeSpecificAttrs(tagName) {
1610
1811
  * @private
1611
1812
  */
1612
1813
  function matrixToTransform(matrix) {
1814
+ // Validate parameter
1815
+ if (!matrix || !matrix.data || !Array.isArray(matrix.data)) {
1816
+ throw new Error("matrixToTransform: invalid matrix");
1817
+ }
1818
+ if (
1819
+ !matrix.data[0] ||
1820
+ !matrix.data[1] ||
1821
+ !matrix.data[0][0] ||
1822
+ !matrix.data[0][1] ||
1823
+ !matrix.data[0][2] ||
1824
+ !matrix.data[1][0] ||
1825
+ !matrix.data[1][1] ||
1826
+ !matrix.data[1][2]
1827
+ ) {
1828
+ throw new Error("matrixToTransform: matrix is missing required elements");
1829
+ }
1830
+
1613
1831
  const a = matrix.data[0][0].toNumber();
1614
1832
  const b = matrix.data[1][0].toNumber();
1615
1833
  const c = matrix.data[0][1].toNumber();
1616
1834
  const d = matrix.data[1][1].toNumber();
1617
1835
  const e = matrix.data[0][2].toNumber();
1618
1836
  const f = matrix.data[1][2].toNumber();
1837
+
1838
+ // Validate all values are finite numbers
1839
+ if (
1840
+ !isFinite(a) ||
1841
+ !isFinite(b) ||
1842
+ !isFinite(c) ||
1843
+ !isFinite(d) ||
1844
+ !isFinite(e) ||
1845
+ !isFinite(f)
1846
+ ) {
1847
+ throw new Error("matrixToTransform: matrix contains non-finite values");
1848
+ }
1849
+
1619
1850
  return `matrix(${a} ${b} ${c} ${d} ${e} ${f})`;
1620
1851
  }
1621
1852
 
@@ -1690,14 +1921,27 @@ function intersectPolygons(subject, clip) {
1690
1921
  * @private
1691
1922
  */
1692
1923
  function isInsideEdge(point, edgeStart, edgeEnd) {
1693
- const px = point.x instanceof Decimal ? point.x.toNumber() : point.x;
1694
- const py = point.y instanceof Decimal ? point.y.toNumber() : point.y;
1924
+ // Validate parameters
1925
+ if (!point || (point.x === undefined && point.y === undefined)) {
1926
+ throw new Error("isInsideEdge: invalid point");
1927
+ }
1928
+ if (!edgeStart || (edgeStart.x === undefined && edgeStart.y === undefined)) {
1929
+ throw new Error("isInsideEdge: invalid edgeStart");
1930
+ }
1931
+ if (!edgeEnd || (edgeEnd.x === undefined && edgeEnd.y === undefined)) {
1932
+ throw new Error("isInsideEdge: invalid edgeEnd");
1933
+ }
1934
+
1935
+ const px = point.x instanceof Decimal ? point.x.toNumber() : point.x || 0;
1936
+ const py = point.y instanceof Decimal ? point.y.toNumber() : point.y || 0;
1695
1937
  const sx =
1696
- edgeStart.x instanceof Decimal ? edgeStart.x.toNumber() : edgeStart.x;
1938
+ edgeStart.x instanceof Decimal ? edgeStart.x.toNumber() : edgeStart.x || 0;
1697
1939
  const sy =
1698
- edgeStart.y instanceof Decimal ? edgeStart.y.toNumber() : edgeStart.y;
1699
- const ex = edgeEnd.x instanceof Decimal ? edgeEnd.x.toNumber() : edgeEnd.x;
1700
- const ey = edgeEnd.y instanceof Decimal ? edgeEnd.y.toNumber() : edgeEnd.y;
1940
+ edgeStart.y instanceof Decimal ? edgeStart.y.toNumber() : edgeStart.y || 0;
1941
+ const ex =
1942
+ edgeEnd.x instanceof Decimal ? edgeEnd.x.toNumber() : edgeEnd.x || 0;
1943
+ const ey =
1944
+ edgeEnd.y instanceof Decimal ? edgeEnd.y.toNumber() : edgeEnd.y || 0;
1701
1945
 
1702
1946
  return (ex - sx) * (py - sy) - (ey - sy) * (px - sx) >= 0;
1703
1947
  }
@@ -1716,14 +1960,28 @@ function isInsideEdge(point, edgeStart, edgeEnd) {
1716
1960
  * @returns {Object} Intersection point with x, y as Decimal
1717
1961
  */
1718
1962
  function lineIntersect(p1, p2, p3, p4) {
1719
- const x1 = p1.x instanceof Decimal ? p1.x.toNumber() : p1.x;
1720
- const y1 = p1.y instanceof Decimal ? p1.y.toNumber() : p1.y;
1721
- const x2 = p2.x instanceof Decimal ? p2.x.toNumber() : p2.x;
1722
- const y2 = p2.y instanceof Decimal ? p2.y.toNumber() : p2.y;
1723
- const x3 = p3.x instanceof Decimal ? p3.x.toNumber() : p3.x;
1724
- const y3 = p3.y instanceof Decimal ? p3.y.toNumber() : p3.y;
1725
- const x4 = p4.x instanceof Decimal ? p4.x.toNumber() : p4.x;
1726
- const y4 = p4.y instanceof Decimal ? p4.y.toNumber() : p4.y;
1963
+ // Validate parameters
1964
+ if (!p1 || (p1.x === undefined && p1.y === undefined)) {
1965
+ throw new Error("lineIntersect: invalid p1");
1966
+ }
1967
+ if (!p2 || (p2.x === undefined && p2.y === undefined)) {
1968
+ throw new Error("lineIntersect: invalid p2");
1969
+ }
1970
+ if (!p3 || (p3.x === undefined && p3.y === undefined)) {
1971
+ throw new Error("lineIntersect: invalid p3");
1972
+ }
1973
+ if (!p4 || (p4.x === undefined && p4.y === undefined)) {
1974
+ throw new Error("lineIntersect: invalid p4");
1975
+ }
1976
+
1977
+ const x1 = p1.x instanceof Decimal ? p1.x.toNumber() : p1.x || 0;
1978
+ const y1 = p1.y instanceof Decimal ? p1.y.toNumber() : p1.y || 0;
1979
+ const x2 = p2.x instanceof Decimal ? p2.x.toNumber() : p2.x || 0;
1980
+ const y2 = p2.y instanceof Decimal ? p2.y.toNumber() : p2.y || 0;
1981
+ const x3 = p3.x instanceof Decimal ? p3.x.toNumber() : p3.x || 0;
1982
+ const y3 = p3.y instanceof Decimal ? p3.y.toNumber() : p3.y || 0;
1983
+ const x4 = p4.x instanceof Decimal ? p4.x.toNumber() : p4.x || 0;
1984
+ const y4 = p4.y instanceof Decimal ? p4.y.toNumber() : p4.y || 0;
1727
1985
 
1728
1986
  const denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
1729
1987