@emasoft/svg-matrix 1.0.28 → 1.0.30

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 (46) hide show
  1. package/README.md +325 -0
  2. package/bin/svg-matrix.js +985 -378
  3. package/bin/svglinter.cjs +4172 -433
  4. package/bin/svgm.js +723 -180
  5. package/package.json +16 -4
  6. package/src/animation-references.js +71 -52
  7. package/src/arc-length.js +160 -96
  8. package/src/bezier-analysis.js +257 -117
  9. package/src/bezier-intersections.js +411 -148
  10. package/src/browser-verify.js +240 -100
  11. package/src/clip-path-resolver.js +350 -142
  12. package/src/convert-path-data.js +279 -134
  13. package/src/css-specificity.js +78 -70
  14. package/src/flatten-pipeline.js +751 -263
  15. package/src/geometry-to-path.js +511 -182
  16. package/src/index.js +191 -46
  17. package/src/inkscape-support.js +18 -7
  18. package/src/marker-resolver.js +278 -164
  19. package/src/mask-resolver.js +209 -98
  20. package/src/matrix.js +147 -67
  21. package/src/mesh-gradient.js +187 -96
  22. package/src/off-canvas-detection.js +201 -104
  23. package/src/path-analysis.js +187 -107
  24. package/src/path-data-plugins.js +628 -167
  25. package/src/path-simplification.js +0 -1
  26. package/src/pattern-resolver.js +125 -88
  27. package/src/polygon-clip.js +111 -66
  28. package/src/svg-boolean-ops.js +194 -118
  29. package/src/svg-collections.js +22 -18
  30. package/src/svg-flatten.js +282 -164
  31. package/src/svg-parser.js +427 -200
  32. package/src/svg-rendering-context.js +147 -104
  33. package/src/svg-toolbox.js +16381 -3370
  34. package/src/svg2-polyfills.js +93 -224
  35. package/src/transform-decomposition.js +46 -41
  36. package/src/transform-optimization.js +89 -68
  37. package/src/transforms2d.js +49 -16
  38. package/src/transforms3d.js +58 -22
  39. package/src/use-symbol-resolver.js +150 -110
  40. package/src/vector.js +67 -15
  41. package/src/vendor/README.md +110 -0
  42. package/src/vendor/inkscape-hatch-polyfill.js +401 -0
  43. package/src/vendor/inkscape-hatch-polyfill.min.js +8 -0
  44. package/src/vendor/inkscape-mesh-polyfill.js +843 -0
  45. package/src/vendor/inkscape-mesh-polyfill.min.js +8 -0
  46. package/src/verification.js +288 -124
@@ -88,16 +88,16 @@
88
88
  * @module polygon-clip
89
89
  */
90
90
 
91
- import Decimal from 'decimal.js';
91
+ import Decimal from "decimal.js";
92
92
 
93
93
  // Set high precision for all calculations
94
94
  Decimal.set({ precision: 80 });
95
95
 
96
96
  // Helper to convert to Decimal
97
- const D = x => (x instanceof Decimal ? x : new Decimal(x));
97
+ const D = (x) => (x instanceof Decimal ? x : new Decimal(x));
98
98
 
99
99
  // Near-zero threshold for comparisons
100
- const EPSILON = new Decimal('1e-40');
100
+ const EPSILON = new Decimal("1e-40");
101
101
 
102
102
  // ============================================================================
103
103
  // Point and Vector Primitives
@@ -153,8 +153,9 @@ export function point(x, y) {
153
153
  * pointsEqual(p1, p2); // false
154
154
  */
155
155
  export function pointsEqual(p1, p2, tolerance = EPSILON) {
156
- return p1.x.minus(p2.x).abs().lt(tolerance) &&
157
- p1.y.minus(p2.y).abs().lt(tolerance);
156
+ return (
157
+ p1.x.minus(p2.x).abs().lt(tolerance) && p1.y.minus(p2.y).abs().lt(tolerance)
158
+ );
158
159
  }
159
160
 
160
161
  /**
@@ -355,7 +356,7 @@ export function segmentIntersection(a, b, c, d) {
355
356
  x: a.x.plus(dx1.mul(t)),
356
357
  y: a.y.plus(dy1.mul(t)),
357
358
  t: t,
358
- s: s
359
+ s: s,
359
360
  };
360
361
  }
361
362
 
@@ -421,7 +422,7 @@ export function lineSegmentIntersection(lineA, lineB, segA, segB) {
421
422
  return {
422
423
  x: segA.x.plus(dx2.mul(s)),
423
424
  y: segA.y.plus(dy2.mul(s)),
424
- s: s
425
+ s: s,
425
426
  };
426
427
  }
427
428
 
@@ -557,8 +558,12 @@ export function pointOnSegment(pt, a, b) {
557
558
  const minY = Decimal.min(a.y, b.y);
558
559
  const maxY = Decimal.max(a.y, b.y);
559
560
 
560
- return pt.x.gte(minX.minus(EPSILON)) && pt.x.lte(maxX.plus(EPSILON)) &&
561
- pt.y.gte(minY.minus(EPSILON)) && pt.y.lte(maxY.plus(EPSILON));
561
+ return (
562
+ pt.x.gte(minX.minus(EPSILON)) &&
563
+ pt.x.lte(maxX.plus(EPSILON)) &&
564
+ pt.y.gte(minY.minus(EPSILON)) &&
565
+ pt.y.lte(maxY.plus(EPSILON))
566
+ );
562
567
  }
563
568
 
564
569
  // ============================================================================
@@ -614,8 +619,8 @@ export function clipPolygonSH(subject, clip) {
614
619
  }
615
620
 
616
621
  // Convert all points to Decimal
617
- let output = subject.map(p => point(p.x, p.y));
618
- const clipPoly = clip.map(p => point(p.x, p.y));
622
+ let output = subject.map((p) => point(p.x, p.y));
623
+ const clipPoly = clip.map((p) => point(p.x, p.y));
619
624
 
620
625
  // Clip against each edge of the clipping polygon
621
626
  for (let i = 0; i < clipPoly.length; i++) {
@@ -639,7 +644,12 @@ export function clipPolygonSH(subject, clip) {
639
644
  if (currentInside) {
640
645
  if (!prevInside) {
641
646
  // Entering: add intersection point
642
- const intersection = lineIntersection(prev, current, clipEdgeStart, clipEdgeEnd);
647
+ const intersection = lineIntersection(
648
+ prev,
649
+ current,
650
+ clipEdgeStart,
651
+ clipEdgeEnd,
652
+ );
643
653
  if (intersection) {
644
654
  output.push(intersection);
645
655
  }
@@ -647,7 +657,12 @@ export function clipPolygonSH(subject, clip) {
647
657
  output.push(current);
648
658
  } else if (prevInside) {
649
659
  // Leaving: add intersection point
650
- const intersection = lineIntersection(prev, current, clipEdgeStart, clipEdgeEnd);
660
+ const intersection = lineIntersection(
661
+ prev,
662
+ current,
663
+ clipEdgeStart,
664
+ clipEdgeEnd,
665
+ );
651
666
  if (intersection) {
652
667
  output.push(intersection);
653
668
  }
@@ -707,7 +722,7 @@ function lineIntersection(a, b, c, d) {
707
722
 
708
723
  return {
709
724
  x: a.x.plus(dx1.mul(t)),
710
- y: a.y.plus(dy1.mul(t))
725
+ y: a.y.plus(dy1.mul(t)),
711
726
  };
712
727
  }
713
728
 
@@ -876,17 +891,19 @@ export function ensureCCW(polygon) {
876
891
  */
877
892
  export function convexHull(points) {
878
893
  if (points.length < 3) {
879
- return points.map(p => point(p.x, p.y));
894
+ return points.map((p) => point(p.x, p.y));
880
895
  }
881
896
 
882
897
  // Convert to Decimal points
883
- const pts = points.map(p => point(p.x, p.y));
898
+ const pts = points.map((p) => point(p.x, p.y));
884
899
 
885
900
  // Find the lowest point (and leftmost if tie)
886
901
  let lowest = 0;
887
902
  for (let i = 1; i < pts.length; i++) {
888
- if (pts[i].y.lt(pts[lowest].y) ||
889
- (pts[i].y.eq(pts[lowest].y) && pts[i].x.lt(pts[lowest].x))) {
903
+ if (
904
+ pts[i].y.lt(pts[lowest].y) ||
905
+ (pts[i].y.eq(pts[lowest].y) && pts[i].x.lt(pts[lowest].x))
906
+ ) {
890
907
  lowest = i;
891
908
  }
892
909
  }
@@ -911,7 +928,10 @@ export function convexHull(points) {
911
928
  const hull = [pivot];
912
929
 
913
930
  for (const pt of sorted) {
914
- while (hull.length > 1 && cross(hull[hull.length - 2], hull[hull.length - 1], pt).lte(0)) {
931
+ while (
932
+ hull.length > 1 &&
933
+ cross(hull[hull.length - 2], hull[hull.length - 1], pt).lte(0)
934
+ ) {
915
935
  hull.pop();
916
936
  }
917
937
  hull.push(pt);
@@ -996,8 +1016,12 @@ export function boundingBox(polygon) {
996
1016
  */
997
1017
  export function bboxIntersects(bb1, bb2) {
998
1018
  if (!bb1 || !bb2) return false;
999
- return !(bb1.maxX.lt(bb2.minX) || bb2.maxX.lt(bb1.minX) ||
1000
- bb1.maxY.lt(bb2.minY) || bb2.maxY.lt(bb1.minY));
1019
+ return !(
1020
+ bb1.maxX.lt(bb2.minX) ||
1021
+ bb2.maxX.lt(bb1.minX) ||
1022
+ bb1.maxY.lt(bb2.minY) ||
1023
+ bb2.maxY.lt(bb1.minY)
1024
+ );
1001
1025
  }
1002
1026
 
1003
1027
  // ============================================================================
@@ -1044,8 +1068,8 @@ export function bboxIntersects(bb1, bb2) {
1044
1068
  */
1045
1069
  export function polygonIntersection(subject, clip) {
1046
1070
  // Convert to Decimal points
1047
- const subjectPoly = subject.map(p => point(p.x, p.y));
1048
- const clipPoly = clip.map(p => point(p.x, p.y));
1071
+ const subjectPoly = subject.map((p) => point(p.x, p.y));
1072
+ const clipPoly = clip.map((p) => point(p.x, p.y));
1049
1073
 
1050
1074
  // Quick bounding box check
1051
1075
  const bb1 = boundingBox(subjectPoly);
@@ -1100,7 +1124,7 @@ export function isConvex(polygon) {
1100
1124
  const n = polygon.length;
1101
1125
  if (n < 3) return false;
1102
1126
 
1103
- let sign = 0;
1127
+ let crossSign = 0;
1104
1128
 
1105
1129
  for (let i = 0; i < n; i++) {
1106
1130
  const p0 = polygon[i];
@@ -1108,12 +1132,12 @@ export function isConvex(polygon) {
1108
1132
  const p2 = polygon[(i + 2) % n];
1109
1133
 
1110
1134
  const crossVal = cross(p0, p1, p2);
1111
- const currentSign = crossVal.gt(0) ? 1 : (crossVal.lt(0) ? -1 : 0);
1135
+ const currentSign = crossVal.gt(0) ? 1 : crossVal.lt(0) ? -1 : 0;
1112
1136
 
1113
1137
  if (currentSign !== 0) {
1114
- if (sign === 0) {
1115
- sign = currentSign;
1116
- } else if (sign !== currentSign) {
1138
+ if (crossSign === 0) {
1139
+ crossSign = currentSign;
1140
+ } else if (crossSign !== currentSign) {
1117
1141
  return false;
1118
1142
  }
1119
1143
  }
@@ -1162,10 +1186,10 @@ function generalPolygonIntersection(subject, clip) {
1162
1186
  }
1163
1187
 
1164
1188
  // Find subject vertices inside clip
1165
- const subjectInside = subject.filter(p => pointInPolygon(p, clip) >= 0);
1189
+ const subjectInside = subject.filter((p) => pointInPolygon(p, clip) >= 0);
1166
1190
 
1167
1191
  // Find clip vertices inside subject
1168
- const clipInside = clip.filter(p => pointInPolygon(p, subject) >= 0);
1192
+ const clipInside = clip.filter((p) => pointInPolygon(p, subject) >= 0);
1169
1193
 
1170
1194
  // Collect all points
1171
1195
  const allPoints = [...intersectionPoints, ...subjectInside, ...clipInside];
@@ -1253,8 +1277,8 @@ function removeDuplicatePoints(points) {
1253
1277
  * polygonUnion(square1, square2); // [square1, square2]
1254
1278
  */
1255
1279
  export function polygonUnion(polygon1, polygon2) {
1256
- const poly1 = polygon1.map(p => point(p.x, p.y));
1257
- const poly2 = polygon2.map(p => point(p.x, p.y));
1280
+ const poly1 = polygon1.map((p) => point(p.x, p.y));
1281
+ const poly2 = polygon2.map((p) => point(p.x, p.y));
1258
1282
 
1259
1283
  const bb1 = boundingBox(poly1);
1260
1284
  const bb2 = boundingBox(poly2);
@@ -1304,19 +1328,25 @@ function traceBoundaryUnion(poly1, poly2) {
1304
1328
  }
1305
1329
 
1306
1330
  // Build augmented polygons with intersection points inserted
1307
- const aug1 = augmentPolygon(poly1, intersections.map(i => ({
1308
- edgeIndex: i.edge1,
1309
- t: i.t1,
1310
- point: i.point,
1311
- intersectionId: i.id
1312
- })));
1313
-
1314
- const aug2 = augmentPolygon(poly2, intersections.map(i => ({
1315
- edgeIndex: i.edge2,
1316
- t: i.t2,
1317
- point: i.point,
1318
- intersectionId: i.id
1319
- })));
1331
+ const aug1 = augmentPolygon(
1332
+ poly1,
1333
+ intersections.map((i) => ({
1334
+ edgeIndex: i.edge1,
1335
+ t: i.t1,
1336
+ point: i.point,
1337
+ intersectionId: i.id,
1338
+ })),
1339
+ );
1340
+
1341
+ const aug2 = augmentPolygon(
1342
+ poly2,
1343
+ intersections.map((i) => ({
1344
+ edgeIndex: i.edge2,
1345
+ t: i.t2,
1346
+ point: i.point,
1347
+ intersectionId: i.id,
1348
+ })),
1349
+ );
1320
1350
 
1321
1351
  // Build lookup from intersection ID to indices in both polygons
1322
1352
  const intersectionMap = new Map();
@@ -1387,7 +1417,10 @@ function traceBoundaryUnion(poly1, poly2) {
1387
1417
  const vertex = aug[currentIdx];
1388
1418
 
1389
1419
  // Add vertex to result (avoid duplicates)
1390
- if (result.length === 0 || !pointsEqual(result[result.length - 1], vertex)) {
1420
+ if (
1421
+ result.length === 0 ||
1422
+ !pointsEqual(result[result.length - 1], vertex)
1423
+ ) {
1391
1424
  result.push(point(vertex.x, vertex.y));
1392
1425
  }
1393
1426
 
@@ -1396,7 +1429,10 @@ function traceBoundaryUnion(poly1, poly2) {
1396
1429
  const nextVertex = aug[nextIdx];
1397
1430
 
1398
1431
  // If next vertex is an intersection we haven't used, switch polygons
1399
- if (nextVertex.intersectionId !== undefined && !usedIntersections.has(nextVertex.intersectionId)) {
1432
+ if (
1433
+ nextVertex.intersectionId !== undefined &&
1434
+ !usedIntersections.has(nextVertex.intersectionId)
1435
+ ) {
1400
1436
  // Add the intersection point
1401
1437
  result.push(point(nextVertex.x, nextVertex.y));
1402
1438
  usedIntersections.add(nextVertex.intersectionId);
@@ -1453,7 +1489,7 @@ function findAllIntersections(poly1, poly2) {
1453
1489
  edge1: i,
1454
1490
  edge2: j,
1455
1491
  t1: intersection.t,
1456
- t2: intersection.s
1492
+ t2: intersection.s,
1457
1493
  });
1458
1494
  }
1459
1495
  }
@@ -1497,7 +1533,7 @@ function augmentPolygon(polygon, insertions) {
1497
1533
  result.push({
1498
1534
  x: ins.point.x,
1499
1535
  y: ins.point.y,
1500
- intersectionId: ins.intersectionId
1536
+ intersectionId: ins.intersectionId,
1501
1537
  });
1502
1538
  }
1503
1539
  }
@@ -1547,8 +1583,8 @@ function augmentPolygon(polygon, insertions) {
1547
1583
  * polygonDifference(small, large); // []
1548
1584
  */
1549
1585
  export function polygonDifference(polygon1, polygon2) {
1550
- const poly1 = polygon1.map(p => point(p.x, p.y));
1551
- const poly2 = polygon2.map(p => point(p.x, p.y));
1586
+ const poly1 = polygon1.map((p) => point(p.x, p.y));
1587
+ const poly2 = polygon2.map((p) => point(p.x, p.y));
1552
1588
 
1553
1589
  const bb1 = boundingBox(poly1);
1554
1590
  const bb2 = boundingBox(poly2);
@@ -1592,19 +1628,25 @@ function traceBoundaryDifference(poly1, poly2) {
1592
1628
  }
1593
1629
 
1594
1630
  // Build augmented polygons with intersection points inserted
1595
- const aug1 = augmentPolygon(poly1, intersections.map(i => ({
1596
- edgeIndex: i.edge1,
1597
- t: i.t1,
1598
- point: i.point,
1599
- intersectionId: i.id
1600
- })));
1601
-
1602
- const aug2 = augmentPolygon(poly2, intersections.map(i => ({
1603
- edgeIndex: i.edge2,
1604
- t: i.t2,
1605
- point: i.point,
1606
- intersectionId: i.id
1607
- })));
1631
+ const aug1 = augmentPolygon(
1632
+ poly1,
1633
+ intersections.map((i) => ({
1634
+ edgeIndex: i.edge1,
1635
+ t: i.t1,
1636
+ point: i.point,
1637
+ intersectionId: i.id,
1638
+ })),
1639
+ );
1640
+
1641
+ const aug2 = augmentPolygon(
1642
+ poly2,
1643
+ intersections.map((i) => ({
1644
+ edgeIndex: i.edge2,
1645
+ t: i.t2,
1646
+ point: i.point,
1647
+ intersectionId: i.id,
1648
+ })),
1649
+ );
1608
1650
 
1609
1651
  // Build lookup from intersection ID to indices in both polygons
1610
1652
  const intersectionMap = new Map();
@@ -1655,7 +1697,10 @@ function traceBoundaryDifference(poly1, poly2) {
1655
1697
  const vertex = poly[currentIdx];
1656
1698
 
1657
1699
  // Add vertex to result (avoid duplicates)
1658
- if (result.length === 0 || !pointsEqual(result[result.length - 1], vertex)) {
1700
+ if (
1701
+ result.length === 0 ||
1702
+ !pointsEqual(result[result.length - 1], vertex)
1703
+ ) {
1659
1704
  result.push(point(vertex.x, vertex.y));
1660
1705
  }
1661
1706
 
@@ -1764,5 +1809,5 @@ export default {
1764
1809
  polygonDifference,
1765
1810
 
1766
1811
  // Constants
1767
- EPSILON
1812
+ EPSILON,
1768
1813
  };