modern-path2d 0.1.18 → 0.2.1

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/dist/index.mjs CHANGED
@@ -60,6 +60,34 @@ class Vector2 {
60
60
  this.y -= vec.y;
61
61
  return this;
62
62
  }
63
+ multiply(vec) {
64
+ this.x *= vec.x;
65
+ this.y *= vec.y;
66
+ return this;
67
+ }
68
+ divide(vec) {
69
+ this.x /= vec.x;
70
+ this.y /= vec.y;
71
+ return this;
72
+ }
73
+ dot(vec) {
74
+ return this.x * vec.x + this.y * vec.y;
75
+ }
76
+ cross(vec) {
77
+ return this.x * vec.y - this.y * vec.x;
78
+ }
79
+ rotate(a, target = { x: 0, y: 0 }) {
80
+ const rotation = -a / 180 * Math.PI;
81
+ const x = this.x - target.x;
82
+ const y = -(this.y - target.y);
83
+ const sin = Math.sin(rotation);
84
+ const cos = Math.cos(rotation);
85
+ this.set(
86
+ target.x + (x * cos - y * sin),
87
+ target.y - (x * sin + y * cos)
88
+ );
89
+ return this;
90
+ }
63
91
  distanceTo(vec) {
64
92
  return Math.sqrt(this.distanceToSquared(vec));
65
93
  }
@@ -68,24 +96,48 @@ class Vector2 {
68
96
  const dy = this.y - vec.y;
69
97
  return dx * dx + dy * dy;
70
98
  }
99
+ lengthSquared() {
100
+ return this.x * this.x + this.y * this.y;
101
+ }
71
102
  length() {
72
- return Math.sqrt(this.x * this.x + this.y * this.y);
103
+ return Math.sqrt(this.lengthSquared());
73
104
  }
74
- multiplyScalar(scalar) {
75
- this.x *= scalar;
76
- this.y *= scalar;
105
+ scale(sx, sy = sx, target = { x: 0, y: 0 }) {
106
+ const x = sx < 0 ? target.x - this.x + target.x : this.x;
107
+ const y = sy < 0 ? target.y - this.y + target.y : this.y;
108
+ this.x = x * Math.abs(sx);
109
+ this.y = y * Math.abs(sy);
77
110
  return this;
78
111
  }
79
- divideScalar(scalar) {
80
- return this.multiplyScalar(1 / scalar);
112
+ skew(ax, ay = 0, target = { x: 0, y: 0 }) {
113
+ const dx = this.x - target.x;
114
+ const dy = this.y - target.y;
115
+ this.x = target.x + (dx + Math.tan(ax) * dy);
116
+ this.y = target.y + (dy + Math.tan(ay) * dx);
117
+ return this;
118
+ }
119
+ normalize() {
120
+ return this.scale(1 / (this.length() || 1));
121
+ }
122
+ addVectors(a, b) {
123
+ this.x = a.x + b.x;
124
+ this.y = a.y + b.y;
125
+ return this;
81
126
  }
82
127
  subVectors(a, b) {
83
128
  this.x = a.x - b.x;
84
129
  this.y = a.y - b.y;
85
130
  return this;
86
131
  }
87
- normalize() {
88
- return this.divideScalar(this.length() || 1);
132
+ multiplyVectors(a, b) {
133
+ this.x = a.x * b.x;
134
+ this.y = a.y * b.y;
135
+ return this;
136
+ }
137
+ divideVectors(a, b) {
138
+ this.x = a.x / b.x;
139
+ this.y = a.y / b.y;
140
+ return this;
89
141
  }
90
142
  lerpVectors(v1, v2, alpha) {
91
143
  this.x = v1.x + (v2.x - v1.x) * alpha;
@@ -913,8 +965,14 @@ class Curve {
913
965
  __publicField$5(this, "_cacheArcLengths");
914
966
  __publicField$5(this, "_needsUpdate", false);
915
967
  }
968
+ isClockwise() {
969
+ const prev = this.getPoint(1);
970
+ const cur = this.getPoint(0.5);
971
+ const next = this.getPoint(1);
972
+ return (cur.x - prev.x) * (next.y - cur.y) - (cur.y - prev.y) * (next.x - cur.x) < 0;
973
+ }
916
974
  getPointAt(u, output = new Vector2()) {
917
- return this.getPoint(this.getUtoTmapping(u), output);
975
+ return this.getPoint(this.getUToTMapping(u), output);
918
976
  }
919
977
  getPoints(divisions = 5) {
920
978
  const points = [];
@@ -923,6 +981,10 @@ class Curve {
923
981
  }
924
982
  return points;
925
983
  }
984
+ forEachControlPoints(cb) {
985
+ this.getControlPoints().forEach(cb);
986
+ return this;
987
+ }
926
988
  getSpacedPoints(divisions = 5) {
927
989
  const points = [];
928
990
  for (let i = 0; i <= divisions; i++) {
@@ -957,22 +1019,22 @@ class Curve {
957
1019
  this._needsUpdate = true;
958
1020
  this.getLengths();
959
1021
  }
960
- getUtoTmapping(u, distance) {
961
- const arcLengths = this.getLengths();
1022
+ getUToTMapping(u, distance) {
1023
+ const lengths = this.getLengths();
962
1024
  let i = 0;
963
- const il = arcLengths.length;
964
- let targetArcLength;
1025
+ const lengthsLen = lengths.length;
1026
+ let targetLength;
965
1027
  if (distance) {
966
- targetArcLength = distance;
1028
+ targetLength = distance;
967
1029
  } else {
968
- targetArcLength = u * arcLengths[il - 1];
1030
+ targetLength = u * lengths[lengthsLen - 1];
969
1031
  }
970
1032
  let low = 0;
971
- let high = il - 1;
1033
+ let high = lengthsLen - 1;
972
1034
  let comparison;
973
1035
  while (low <= high) {
974
1036
  i = Math.floor(low + (high - low) / 2);
975
- comparison = arcLengths[i] - targetArcLength;
1037
+ comparison = lengths[i] - targetLength;
976
1038
  if (comparison < 0) {
977
1039
  low = i + 1;
978
1040
  } else if (comparison > 0) {
@@ -983,14 +1045,14 @@ class Curve {
983
1045
  }
984
1046
  }
985
1047
  i = high;
986
- if (arcLengths[i] === targetArcLength) {
987
- return i / (il - 1);
1048
+ if (lengths[i] === targetLength) {
1049
+ return i / (lengthsLen - 1);
988
1050
  }
989
- const lengthBefore = arcLengths[i];
990
- const lengthAfter = arcLengths[i + 1];
1051
+ const lengthBefore = lengths[i];
1052
+ const lengthAfter = lengths[i + 1];
991
1053
  const segmentLength = lengthAfter - lengthBefore;
992
- const segmentFraction = (targetArcLength - lengthBefore) / segmentLength;
993
- return (i + segmentFraction) / (il - 1);
1054
+ const segmentFraction = (targetLength - lengthBefore) / segmentLength;
1055
+ return (i + segmentFraction) / (lengthsLen - 1);
994
1056
  }
995
1057
  getTangent(t, output = new Vector2()) {
996
1058
  const delta = 1e-4;
@@ -999,15 +1061,36 @@ class Curve {
999
1061
  return output.copy(this.getPoint(t2).sub(this.getPoint(t1)).normalize());
1000
1062
  }
1001
1063
  getTangentAt(u, output) {
1002
- return this.getTangent(this.getUtoTmapping(u), output);
1064
+ return this.getTangent(this.getUToTMapping(u), output);
1003
1065
  }
1004
- /** overrideable */
1005
- // eslint-disable-next-line unused-imports/no-unused-vars
1006
- transformPoint(cb) {
1007
- return this;
1066
+ getNormal(t, output = new Vector2()) {
1067
+ this.getTangent(t, output);
1068
+ return output.set(-output.y, output.x).normalize();
1069
+ }
1070
+ getNormalAt(u, output) {
1071
+ return this.getNormal(this.getUToTMapping(u), output);
1008
1072
  }
1009
- transform(matrix) {
1010
- this.transformPoint((point) => point.applyMatrix3(matrix));
1073
+ getTForPoint(target, epsilon = 1e-3) {
1074
+ let low = 0;
1075
+ let high = 1;
1076
+ let mid = (low + high) / 2;
1077
+ while (high - low > epsilon) {
1078
+ mid = (low + high) / 2;
1079
+ const point = this.getPoint(mid);
1080
+ const distance = point.distanceTo(target);
1081
+ if (distance < epsilon) {
1082
+ return mid;
1083
+ }
1084
+ if (point.x < target.x) {
1085
+ low = mid;
1086
+ } else {
1087
+ high = mid;
1088
+ }
1089
+ }
1090
+ return mid;
1091
+ }
1092
+ matrix(matrix) {
1093
+ this.forEachControlPoints((point) => point.applyMatrix3(matrix));
1011
1094
  return this;
1012
1095
  }
1013
1096
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
@@ -1023,7 +1106,7 @@ class Curve {
1023
1106
  const { min, max } = this.getMinMax();
1024
1107
  return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
1025
1108
  }
1026
- getCommands() {
1109
+ toCommands() {
1027
1110
  return this.getPoints().map((point, i) => {
1028
1111
  if (i === 0) {
1029
1112
  return { type: "M", x: point.x, y: point.y };
@@ -1032,12 +1115,20 @@ class Curve {
1032
1115
  }
1033
1116
  });
1034
1117
  }
1035
- getData() {
1036
- return pathCommandsToPathData(this.getCommands());
1118
+ toData() {
1119
+ return pathCommandsToPathData(this.toCommands());
1037
1120
  }
1038
- /** overrideable */
1039
- // eslint-disable-next-line unused-imports/no-unused-vars
1040
1121
  drawTo(ctx) {
1122
+ this.toCommands().forEach((cmd) => {
1123
+ switch (cmd.type) {
1124
+ case "M":
1125
+ ctx.moveTo(cmd.x, cmd.y);
1126
+ break;
1127
+ case "L":
1128
+ ctx.lineTo(cmd.x, cmd.y);
1129
+ break;
1130
+ }
1131
+ });
1041
1132
  return this;
1042
1133
  }
1043
1134
  copy(source) {
@@ -1059,20 +1150,19 @@ class CircleCurve extends Curve {
1059
1150
  }
1060
1151
  getPoint(t) {
1061
1152
  const { radius, center } = this;
1062
- return center.clone().add(this.getNormal(t).clone().multiplyScalar(radius));
1153
+ return center.clone().add(this.getNormal(t).clone().scale(radius));
1063
1154
  }
1064
- getTangent(t) {
1155
+ getTangent(t, output = new Vector2()) {
1065
1156
  const { x, y } = this.getNormal(t);
1066
- return new Vector2(-y, x);
1157
+ return output.set(-y, x);
1067
1158
  }
1068
- getNormal(t) {
1159
+ getNormal(t, output = new Vector2()) {
1069
1160
  const { start, end } = this;
1070
1161
  const _t = t * (end - start) + start - 0.5 * Math.PI;
1071
- return new Vector2(Math.cos(_t), Math.sin(_t));
1162
+ return output.set(Math.cos(_t), Math.sin(_t));
1072
1163
  }
1073
- transformPoint(cb) {
1074
- cb(this.center);
1075
- return this;
1164
+ getControlPoints() {
1165
+ return [this.center];
1076
1166
  }
1077
1167
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1078
1168
  min.x = Math.min(min.x, this.center.x - this.radius);
@@ -1131,18 +1221,18 @@ class CubicBezierCurve extends Curve {
1131
1221
  }
1132
1222
  getPoint(t, output = new Vector2()) {
1133
1223
  const { start, startControl, endControl, end } = this;
1134
- output.set(
1224
+ return output.set(
1135
1225
  cubicBezier(t, start.x, startControl.x, endControl.x, end.x),
1136
1226
  cubicBezier(t, start.y, startControl.y, endControl.y, end.y)
1137
1227
  );
1138
- return output;
1139
1228
  }
1140
- transformPoint(cb) {
1141
- cb(this.start);
1142
- cb(this.startControl);
1143
- cb(this.endControl);
1144
- cb(this.end);
1145
- return this;
1229
+ getControlPoints() {
1230
+ return [
1231
+ this.start,
1232
+ this.startControl,
1233
+ this.endControl,
1234
+ this.end
1235
+ ];
1146
1236
  }
1147
1237
  _solveQuadratic(a, b, c) {
1148
1238
  const discriminant = b * b - 4 * a * c;
@@ -1185,7 +1275,7 @@ class CubicBezierCurve extends Curve {
1185
1275
  samplePoints(tValues, 10);
1186
1276
  return { min, max };
1187
1277
  }
1188
- getCommands() {
1278
+ toCommands() {
1189
1279
  const { start, startControl, endControl, end } = this;
1190
1280
  return [
1191
1281
  { type: "M", x: start.x, y: start.y },
@@ -1193,7 +1283,8 @@ class CubicBezierCurve extends Curve {
1193
1283
  ];
1194
1284
  }
1195
1285
  drawTo(ctx) {
1196
- const { startControl, endControl, end } = this;
1286
+ const { start, startControl, endControl, end } = this;
1287
+ ctx.lineTo(start.x, start.y);
1197
1288
  ctx.bezierCurveTo(startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y);
1198
1289
  return this;
1199
1290
  }
@@ -1222,6 +1313,9 @@ class EllipseCurve extends Curve {
1222
1313
  this.endAngle = endAngle;
1223
1314
  this.clockwise = clockwise;
1224
1315
  }
1316
+ isClockwise() {
1317
+ return this.clockwise;
1318
+ }
1225
1319
  getPoint(t, output = new Vector2()) {
1226
1320
  const twoPi = Math.PI * 2;
1227
1321
  let deltaAngle = this.endAngle - this.startAngle;
@@ -1257,7 +1351,7 @@ class EllipseCurve extends Curve {
1257
1351
  }
1258
1352
  return output.set(_x, _y);
1259
1353
  }
1260
- getCommands() {
1354
+ toCommands() {
1261
1355
  const { center, radiusX: rx, radiusY: ry, startAngle, endAngle, clockwise, rotation } = this;
1262
1356
  const { x: cx, y: cy } = center;
1263
1357
  const startX = cx + rx * Math.cos(startAngle) * Math.cos(rotation) - ry * Math.sin(startAngle) * Math.sin(rotation);
@@ -1298,7 +1392,7 @@ class EllipseCurve extends Curve {
1298
1392
  );
1299
1393
  return this;
1300
1394
  }
1301
- transform(matrix) {
1395
+ matrix(matrix) {
1302
1396
  tempV2.set(this.center.x, this.center.y);
1303
1397
  tempV2.applyMatrix3(matrix);
1304
1398
  this.center.x = tempV2.x;
@@ -1310,9 +1404,8 @@ class EllipseCurve extends Curve {
1310
1404
  }
1311
1405
  return this;
1312
1406
  }
1313
- transformPoint(cb) {
1314
- cb(this.center);
1315
- return this;
1407
+ getControlPoints() {
1408
+ return [this.center];
1316
1409
  }
1317
1410
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1318
1411
  const { center, radiusX: rx, radiusY: ry, rotation: theta } = this;
@@ -1494,7 +1587,7 @@ class LineCurve extends Curve {
1494
1587
  if (t === 1) {
1495
1588
  output.copy(this.end);
1496
1589
  } else {
1497
- output.copy(this.end).sub(this.start).multiplyScalar(t).add(this.start);
1590
+ output.copy(this.end).sub(this.start).scale(t).add(this.start);
1498
1591
  }
1499
1592
  return output;
1500
1593
  }
@@ -1507,14 +1600,11 @@ class LineCurve extends Curve {
1507
1600
  getTangentAt(u, output = new Vector2()) {
1508
1601
  return this.getTangent(u, output);
1509
1602
  }
1510
- getNormal(t, output = new Vector2()) {
1511
- const { x, y } = this.getPoint(t).sub(this.start);
1512
- return output.set(y, -x).normalize();
1513
- }
1514
- transformPoint(cb) {
1515
- cb(this.start);
1516
- cb(this.end);
1517
- return this;
1603
+ getControlPoints() {
1604
+ return [
1605
+ this.start,
1606
+ this.end
1607
+ ];
1518
1608
  }
1519
1609
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1520
1610
  const { start, end } = this;
@@ -1524,7 +1614,7 @@ class LineCurve extends Curve {
1524
1614
  max.y = Math.max(max.y, start.y, end.y);
1525
1615
  return { min, max };
1526
1616
  }
1527
- getCommands() {
1617
+ toCommands() {
1528
1618
  const { start, end } = this;
1529
1619
  return [
1530
1620
  { type: "M", x: start.x, y: start.y },
@@ -1532,7 +1622,8 @@ class LineCurve extends Curve {
1532
1622
  ];
1533
1623
  }
1534
1624
  drawTo(ctx) {
1535
- const { end } = this;
1625
+ const { start, end } = this;
1626
+ ctx.lineTo(start.x, start.y);
1536
1627
  ctx.lineTo(end.x, end.y);
1537
1628
  return this;
1538
1629
  }
@@ -1608,22 +1699,21 @@ class HeartCurve extends Curve {
1608
1699
  }
1609
1700
  return this.curves[index];
1610
1701
  }
1611
- getTangent(t) {
1612
- return this.getCurve(t).getTangent(this.curveT);
1702
+ getTangent(t, output) {
1703
+ return this.getCurve(t).getTangent(this.curveT, output);
1613
1704
  }
1614
- getNormal(t) {
1615
- return this.getCurve(t).getNormal(this.curveT);
1705
+ getNormal(t, output) {
1706
+ return this.getCurve(t).getNormal(this.curveT, output);
1616
1707
  }
1617
- transformPoint(cb) {
1618
- this.curves.forEach((curve) => curve.transformPoint(cb));
1619
- return this;
1708
+ getControlPoints() {
1709
+ return this.curves.flatMap((curve) => curve.getControlPoints());
1620
1710
  }
1621
1711
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1622
1712
  this.curves.forEach((curve) => curve.getMinMax(min, max));
1623
1713
  return { min, max };
1624
1714
  }
1625
- getCommands() {
1626
- return this.curves.flatMap((curve) => curve.getCommands());
1715
+ toCommands() {
1716
+ return this.curves.flatMap((curve) => curve.toCommands());
1627
1717
  }
1628
1718
  drawTo(ctx) {
1629
1719
  this.curves.forEach((curve) => curve.drawTo(ctx));
@@ -1686,16 +1776,15 @@ class PloygonCurve extends Curve {
1686
1776
  getNormal(t, output) {
1687
1777
  return this.getCurve(t).getNormal(this.curveT, output);
1688
1778
  }
1689
- transformPoint(cb) {
1690
- this.curves.forEach((curve) => curve.transformPoint(cb));
1691
- return this;
1779
+ getControlPoints() {
1780
+ return this.curves.flatMap((curve) => curve.getControlPoints());
1692
1781
  }
1693
1782
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1694
1783
  this.curves.forEach((curve) => curve.getMinMax(min, max));
1695
1784
  return { min, max };
1696
1785
  }
1697
- getCommands() {
1698
- return this.curves.flatMap((curve) => curve.getCommands());
1786
+ toCommands() {
1787
+ return this.curves.flatMap((curve) => curve.toCommands());
1699
1788
  }
1700
1789
  drawTo(ctx) {
1701
1790
  this.curves.forEach((curve) => curve.drawTo(ctx));
@@ -1718,11 +1807,12 @@ class QuadraticBezierCurve extends Curve {
1718
1807
  );
1719
1808
  return output;
1720
1809
  }
1721
- transformPoint(cb) {
1722
- cb(this.start);
1723
- cb(this.control);
1724
- cb(this.end);
1725
- return this;
1810
+ getControlPoints() {
1811
+ return [
1812
+ this.start,
1813
+ this.control,
1814
+ this.end
1815
+ ];
1726
1816
  }
1727
1817
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1728
1818
  const { start, control, end } = this;
@@ -1736,7 +1826,7 @@ class QuadraticBezierCurve extends Curve {
1736
1826
  max.y = Math.max(max.y, start.y, end.y, y1, y2);
1737
1827
  return { min, max };
1738
1828
  }
1739
- getCommands() {
1829
+ toCommands() {
1740
1830
  const { start, control, end } = this;
1741
1831
  return [
1742
1832
  { type: "M", x: start.x, y: start.y },
@@ -1744,7 +1834,8 @@ class QuadraticBezierCurve extends Curve {
1744
1834
  ];
1745
1835
  }
1746
1836
  drawTo(ctx) {
1747
- const { control, end } = this;
1837
+ const { start, control, end } = this;
1838
+ ctx.lineTo(start.x, start.y);
1748
1839
  ctx.quadraticCurveTo(control.x, control.y, end.x, end.y);
1749
1840
  return this;
1750
1841
  }
@@ -1834,16 +1925,15 @@ class RectangularCurve extends Curve {
1834
1925
  getNormal(t, output) {
1835
1926
  return this.getCurve(t).getNormal(this.curveT, output);
1836
1927
  }
1837
- transformPoint(cb) {
1838
- this.curves.forEach((curve) => curve.transformPoint(cb));
1839
- return this;
1928
+ getControlPoints() {
1929
+ return this.curves.flatMap((curve) => curve.getControlPoints());
1840
1930
  }
1841
1931
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1842
1932
  this.curves.forEach((curve) => curve.getMinMax(min, max));
1843
1933
  return { min, max };
1844
1934
  }
1845
- getCommands() {
1846
- return this.curves.flatMap((curve) => curve.getCommands());
1935
+ toCommands() {
1936
+ return this.curves.flatMap((curve) => curve.toCommands());
1847
1937
  }
1848
1938
  drawTo(ctx) {
1849
1939
  this.curves.forEach((curve) => curve.drawTo(ctx));
@@ -1871,9 +1961,8 @@ class SplineCurve extends Curve {
1871
1961
  );
1872
1962
  return output;
1873
1963
  }
1874
- transformPoint(cb) {
1875
- this.points.forEach((point) => cb(point));
1876
- return this;
1964
+ getControlPoints() {
1965
+ return this.points;
1877
1966
  }
1878
1967
  copy(source) {
1879
1968
  super.copy(source);
@@ -1938,6 +2027,9 @@ class CurvePath extends Curve {
1938
2027
  }
1939
2028
  return output;
1940
2029
  }
2030
+ getControlPoints() {
2031
+ return this.curves.flatMap((curve) => curve.getControlPoints());
2032
+ }
1941
2033
  getLength() {
1942
2034
  const lengths = this.getCurveLengths();
1943
2035
  return lengths[lengths.length - 1];
@@ -2109,10 +2201,6 @@ class CurvePath extends Curve {
2109
2201
  this._setCurrentPoint(points[points.length - 1]);
2110
2202
  return this;
2111
2203
  }
2112
- transformPoint(cb) {
2113
- this.curves.forEach((curve) => curve.transformPoint(cb));
2114
- return this;
2115
- }
2116
2204
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
2117
2205
  this.curves.forEach((curve) => curve.getMinMax(min, max));
2118
2206
  return { min, max };
@@ -2121,8 +2209,8 @@ class CurvePath extends Curve {
2121
2209
  const { min, max } = this.getMinMax();
2122
2210
  return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
2123
2211
  }
2124
- getCommands() {
2125
- return this.curves.flatMap((curve) => curve.getCommands());
2212
+ toCommands() {
2213
+ return this.curves.flatMap((curve) => curve.toCommands());
2126
2214
  }
2127
2215
  drawTo(ctx) {
2128
2216
  const point = this.curves[0]?.getPoint(0);
@@ -2150,6 +2238,29 @@ class CurvePath extends Curve {
2150
2238
  function toKebabCase(str) {
2151
2239
  return str.replace(/[^a-z0-9]/gi, "-").replace(/\B([A-Z])/g, "-$1").toLowerCase();
2152
2240
  }
2241
+ function getIntersectionPoint(p1, p2, q1, q2) {
2242
+ const r = p2.clone().sub(p1);
2243
+ const s = q2.clone().sub(q1);
2244
+ const q1p1 = q1.clone().sub(p1);
2245
+ const crossRS = r.cross(s);
2246
+ if (crossRS === 0) {
2247
+ return new Vector2(
2248
+ (p1.x + q1.x) / 2,
2249
+ (p1.y + q1.y) / 2
2250
+ );
2251
+ }
2252
+ const t = q1p1.cross(s) / crossRS;
2253
+ if (Math.abs(t) > 1) {
2254
+ return new Vector2(
2255
+ (p1.x + q1.x) / 2,
2256
+ (p1.y + q1.y) / 2
2257
+ );
2258
+ }
2259
+ return new Vector2(
2260
+ p1.x + t * r.x,
2261
+ p1.y + t * r.y
2262
+ );
2263
+ }
2153
2264
 
2154
2265
  var __defProp = Object.defineProperty;
2155
2266
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -2252,20 +2363,93 @@ class Path2D {
2252
2363
  this.currentPath.splineThru(points);
2253
2364
  return this;
2254
2365
  }
2255
- forEachCurve(cb) {
2256
- this.paths.forEach((path) => path.curves.forEach((curve) => cb(curve)));
2366
+ getControlPoints() {
2367
+ return this.paths.flatMap((path) => path.getControlPoints());
2368
+ }
2369
+ getCurves() {
2370
+ return this.paths.flatMap((path) => path.curves);
2371
+ }
2372
+ scale(sx, sy = sx, target = { x: 0, y: 0 }) {
2373
+ this.getControlPoints().forEach((point) => {
2374
+ point.scale(sx, sy, target);
2375
+ });
2257
2376
  return this;
2258
2377
  }
2259
- transformPoint(cb) {
2260
- this.forEachCurve((curve) => curve.transformPoint(cb));
2378
+ skew(ax, ay = 0, target = { x: 0, y: 0 }) {
2379
+ this.getControlPoints().forEach((point) => {
2380
+ point.skew(ax, ay, target);
2381
+ });
2261
2382
  return this;
2262
2383
  }
2263
- transform(matrix) {
2264
- this.forEachCurve((curve) => curve.transform(matrix));
2384
+ rotate(a, target = { x: 0, y: 0 }) {
2385
+ this.getControlPoints().forEach((point) => {
2386
+ point.rotate(a, target);
2387
+ });
2388
+ return this;
2389
+ }
2390
+ bold(b) {
2391
+ if (b === 0) {
2392
+ return this;
2393
+ }
2394
+ const curves = this.getCurves();
2395
+ const _list = [];
2396
+ const _isClockwise = [];
2397
+ const _points = [];
2398
+ curves.forEach((curve, index) => {
2399
+ const points = curve.getControlPoints();
2400
+ const isClockwise = curve.isClockwise();
2401
+ _points[index] = points;
2402
+ _isClockwise[index] = isClockwise;
2403
+ const start = points[0];
2404
+ const end = points[points.length - 1] ?? start;
2405
+ _list.push({
2406
+ start: isClockwise ? end : start,
2407
+ end: isClockwise ? start : end,
2408
+ index
2409
+ });
2410
+ });
2411
+ const list = [];
2412
+ _list.forEach((itemA, indexA) => {
2413
+ list[indexA] = [];
2414
+ _list.forEach((itemB, indexB) => {
2415
+ if (indexB !== indexA && itemB.start.equals(itemA.end)) {
2416
+ list[indexA].push(itemB.index);
2417
+ }
2418
+ });
2419
+ });
2420
+ curves.forEach((curve, index) => {
2421
+ const isClockwise = _isClockwise[index];
2422
+ const points = _points[index];
2423
+ points.forEach((point) => {
2424
+ const t = curve.getTForPoint(point);
2425
+ const dist = curve.getNormal(t).scale(isClockwise ? b : -b);
2426
+ point.add(dist);
2427
+ });
2428
+ });
2429
+ list.forEach((indexes, indexA) => {
2430
+ const pointsA = _points[indexA];
2431
+ indexes.forEach((indexB) => {
2432
+ const pointsB = _points[indexB];
2433
+ const point = getIntersectionPoint(
2434
+ pointsA[pointsA.length - 1],
2435
+ pointsA[pointsA.length - 2] ?? pointsA[pointsA.length - 1],
2436
+ pointsB[0],
2437
+ pointsB[1] ?? pointsB[0]
2438
+ );
2439
+ if (point) {
2440
+ pointsA[pointsA.length - 1].copy(point);
2441
+ pointsB[0].copy(point);
2442
+ }
2443
+ });
2444
+ });
2445
+ return this;
2446
+ }
2447
+ matrix(matrix) {
2448
+ this.getCurves().forEach((curve) => curve.matrix(matrix));
2265
2449
  return this;
2266
2450
  }
2267
2451
  getMinMax(min = Vector2.MAX, max = Vector2.MIN, withStyle = true) {
2268
- this.forEachCurve((curve) => curve.getMinMax(min, max));
2452
+ this.getCurves().forEach((curve) => curve.getMinMax(min, max));
2269
2453
  if (withStyle) {
2270
2454
  const strokeHalfWidth = this.strokeWidth / 2;
2271
2455
  min.x -= strokeHalfWidth;
@@ -2279,13 +2463,50 @@ class Path2D {
2279
2463
  const { min, max } = this.getMinMax(void 0, void 0, withStyle);
2280
2464
  return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
2281
2465
  }
2282
- getCommands() {
2283
- return this.paths.flatMap((path) => path.getCommands());
2466
+ drawTo(ctx, style = {}) {
2467
+ style = { ...this.style, ...style };
2468
+ const { fill = "#000", stroke = "none" } = style;
2469
+ ctx.beginPath();
2470
+ ctx.save();
2471
+ setCanvasContext(ctx, style);
2472
+ this.paths.forEach((path) => {
2473
+ path.drawTo(ctx);
2474
+ });
2475
+ if (fill !== "none") {
2476
+ ctx.fill();
2477
+ }
2478
+ if (stroke !== "none") {
2479
+ ctx.stroke();
2480
+ }
2481
+ ctx.restore();
2482
+ return this;
2284
2483
  }
2285
- getData() {
2286
- return this.paths.map((path) => path.getData()).join(" ");
2484
+ drawControlPointsTo(ctx, style = {}) {
2485
+ style = { ...this.style, ...style };
2486
+ const { fill = "#000", stroke = "none" } = style;
2487
+ ctx.beginPath();
2488
+ ctx.save();
2489
+ setCanvasContext(ctx, style);
2490
+ this.getControlPoints().forEach((point) => {
2491
+ ctx.moveTo(point.x, point.y);
2492
+ ctx.arc(point.x, point.y, 4, 0, Math.PI * 2);
2493
+ });
2494
+ if (fill !== "none") {
2495
+ ctx.fill();
2496
+ }
2497
+ if (stroke !== "none") {
2498
+ ctx.stroke();
2499
+ }
2500
+ ctx.restore();
2501
+ return this;
2502
+ }
2503
+ toCommands() {
2504
+ return this.paths.flatMap((path) => path.toCommands());
2287
2505
  }
2288
- getSvgPathXml() {
2506
+ toData() {
2507
+ return this.paths.map((path) => path.toData()).join(" ");
2508
+ }
2509
+ toSvgPathString() {
2289
2510
  const style = {
2290
2511
  ...this.style,
2291
2512
  fill: this.style.fill ?? "#000",
@@ -2306,40 +2527,21 @@ class Path2D {
2306
2527
  cssText += `${key}:${cssStyle[key]};`;
2307
2528
  }
2308
2529
  }
2309
- return `<path d="${this.getData()}" style="${cssText}"></path>`;
2530
+ return `<path d="${this.toData()}" style="${cssText}"></path>`;
2310
2531
  }
2311
- getSvgXml() {
2532
+ toSvgString() {
2312
2533
  const { x, y, width, height } = this.getBoundingBox();
2313
- const path = this.getSvgPathXml();
2534
+ const path = this.toSvgPathString();
2314
2535
  return `<svg viewBox="${x} ${y} ${width} ${height}" width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">${path}</svg>`;
2315
2536
  }
2316
- getSvgDataUri() {
2317
- return `data:image/svg+xml;base64,${btoa(this.getSvgXml())}`;
2318
- }
2319
- drawTo(ctx, style = {}) {
2320
- style = { ...this.style, ...style };
2321
- const { fill = "#000", stroke = "none" } = style;
2322
- setCanvasContext(ctx, style);
2323
- this.paths.forEach((path) => {
2324
- path.drawTo(ctx);
2325
- });
2326
- if (fill !== "none") {
2327
- ctx.fill();
2328
- }
2329
- if (stroke !== "none") {
2330
- ctx.stroke();
2331
- }
2332
- }
2333
- copy(source) {
2334
- this.currentPath = source.currentPath.clone();
2335
- this.paths = source.paths.map((path) => path.clone());
2336
- this.style = { ...source.style };
2337
- return this;
2537
+ toSvgUrl() {
2538
+ return `data:image/svg+xml;base64,${btoa(this.toSvgString())}`;
2338
2539
  }
2339
2540
  toSvg() {
2340
- return new DOMParser().parseFromString(this.getSvgXml(), "image/svg+xml").documentElement;
2541
+ return new DOMParser().parseFromString(this.toSvgString(), "image/svg+xml").documentElement;
2341
2542
  }
2342
- toCanvas(pixelRatio = 2) {
2543
+ toCanvas(options = {}) {
2544
+ const { pixelRatio = 2, ...style } = options;
2343
2545
  const { left, top, width, height } = this.getBoundingBox();
2344
2546
  const canvas = document.createElement("canvas");
2345
2547
  canvas.width = width * pixelRatio;
@@ -2350,10 +2552,16 @@ class Path2D {
2350
2552
  if (ctx) {
2351
2553
  ctx.scale(pixelRatio, pixelRatio);
2352
2554
  ctx.translate(-left, -top);
2353
- this.drawTo(ctx);
2555
+ this.drawTo(ctx, style);
2354
2556
  }
2355
2557
  return canvas;
2356
2558
  }
2559
+ copy(source) {
2560
+ this.currentPath = source.currentPath.clone();
2561
+ this.paths = source.paths.map((path) => path.clone());
2562
+ this.style = { ...source.style };
2563
+ return this;
2564
+ }
2357
2565
  clone() {
2358
2566
  return new this.constructor().copy(this);
2359
2567
  }
@@ -2830,7 +3038,7 @@ function parseNode(node, style, paths = []) {
2830
3038
  const transformStack = [];
2831
3039
  const transform = getNodeTransform(node, currentTransform, transformStack);
2832
3040
  if (path) {
2833
- path.transform(currentTransform);
3041
+ path.matrix(currentTransform);
2834
3042
  paths.push(path);
2835
3043
  path.style = style;
2836
3044
  }
@@ -2879,4 +3087,14 @@ function parseSvg(svg) {
2879
3087
  return parseNode(parseSvgToDom(svg), {});
2880
3088
  }
2881
3089
 
2882
- export { BoundingBox, CircleCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, HeartCurve, LineCurve, Matrix3, Path2D, PloygonCurve, QuadraticBezierCurve, RectangularCurve, SplineCurve, Vector2, addPathCommandsToPath2D, parseArcCommand, parsePathDataArgs, parseSvg, parseSvgToDom, pathCommandsToPathData, pathDataToPathCommands, setCanvasContext };
3090
+ function getPathsBoundingBox(paths) {
3091
+ if (!paths.length) {
3092
+ return void 0;
3093
+ }
3094
+ const min = Vector2.MAX;
3095
+ const max = Vector2.MIN;
3096
+ paths.forEach((path) => path.getMinMax(min, max));
3097
+ return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
3098
+ }
3099
+
3100
+ export { BoundingBox, CircleCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, HeartCurve, LineCurve, Matrix3, Path2D, PloygonCurve, QuadraticBezierCurve, RectangularCurve, SplineCurve, Vector2, addPathCommandsToPath2D, getPathsBoundingBox, parseArcCommand, parsePathDataArgs, parseSvg, parseSvgToDom, pathCommandsToPathData, pathDataToPathCommands, setCanvasContext };