modern-path2d 0.1.17 → 0.2.0

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;
@@ -139,6 +191,11 @@ class BoundingBox {
139
191
  return this.top + this.height;
140
192
  }
141
193
  static from(...boxes) {
194
+ if (boxes.length === 0) {
195
+ return new BoundingBox();
196
+ } else if (boxes.length === 1) {
197
+ return boxes[0].clone();
198
+ }
142
199
  const firstBox = boxes[0];
143
200
  const merged = boxes.slice(1).reduce(
144
201
  (merged2, box) => {
@@ -908,8 +965,14 @@ class Curve {
908
965
  __publicField$5(this, "_cacheArcLengths");
909
966
  __publicField$5(this, "_needsUpdate", false);
910
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
+ }
911
974
  getPointAt(u, output = new Vector2()) {
912
- return this.getPoint(this.getUtoTmapping(u), output);
975
+ return this.getPoint(this.getUToTMapping(u), output);
913
976
  }
914
977
  getPoints(divisions = 5) {
915
978
  const points = [];
@@ -918,6 +981,10 @@ class Curve {
918
981
  }
919
982
  return points;
920
983
  }
984
+ forEachControlPoints(cb) {
985
+ this.getControlPoints().forEach(cb);
986
+ return this;
987
+ }
921
988
  getSpacedPoints(divisions = 5) {
922
989
  const points = [];
923
990
  for (let i = 0; i <= divisions; i++) {
@@ -952,22 +1019,22 @@ class Curve {
952
1019
  this._needsUpdate = true;
953
1020
  this.getLengths();
954
1021
  }
955
- getUtoTmapping(u, distance) {
956
- const arcLengths = this.getLengths();
1022
+ getUToTMapping(u, distance) {
1023
+ const lengths = this.getLengths();
957
1024
  let i = 0;
958
- const il = arcLengths.length;
959
- let targetArcLength;
1025
+ const lengthsLen = lengths.length;
1026
+ let targetLength;
960
1027
  if (distance) {
961
- targetArcLength = distance;
1028
+ targetLength = distance;
962
1029
  } else {
963
- targetArcLength = u * arcLengths[il - 1];
1030
+ targetLength = u * lengths[lengthsLen - 1];
964
1031
  }
965
1032
  let low = 0;
966
- let high = il - 1;
1033
+ let high = lengthsLen - 1;
967
1034
  let comparison;
968
1035
  while (low <= high) {
969
1036
  i = Math.floor(low + (high - low) / 2);
970
- comparison = arcLengths[i] - targetArcLength;
1037
+ comparison = lengths[i] - targetLength;
971
1038
  if (comparison < 0) {
972
1039
  low = i + 1;
973
1040
  } else if (comparison > 0) {
@@ -978,14 +1045,14 @@ class Curve {
978
1045
  }
979
1046
  }
980
1047
  i = high;
981
- if (arcLengths[i] === targetArcLength) {
982
- return i / (il - 1);
1048
+ if (lengths[i] === targetLength) {
1049
+ return i / (lengthsLen - 1);
983
1050
  }
984
- const lengthBefore = arcLengths[i];
985
- const lengthAfter = arcLengths[i + 1];
1051
+ const lengthBefore = lengths[i];
1052
+ const lengthAfter = lengths[i + 1];
986
1053
  const segmentLength = lengthAfter - lengthBefore;
987
- const segmentFraction = (targetArcLength - lengthBefore) / segmentLength;
988
- return (i + segmentFraction) / (il - 1);
1054
+ const segmentFraction = (targetLength - lengthBefore) / segmentLength;
1055
+ return (i + segmentFraction) / (lengthsLen - 1);
989
1056
  }
990
1057
  getTangent(t, output = new Vector2()) {
991
1058
  const delta = 1e-4;
@@ -994,15 +1061,36 @@ class Curve {
994
1061
  return output.copy(this.getPoint(t2).sub(this.getPoint(t1)).normalize());
995
1062
  }
996
1063
  getTangentAt(u, output) {
997
- return this.getTangent(this.getUtoTmapping(u), output);
1064
+ return this.getTangent(this.getUToTMapping(u), output);
998
1065
  }
999
- /** overrideable */
1000
- // eslint-disable-next-line unused-imports/no-unused-vars
1001
- transformPoint(cb) {
1002
- return this;
1066
+ getNormal(t, output = new Vector2()) {
1067
+ this.getTangent(t, output);
1068
+ return output.set(-output.y, output.x).normalize();
1003
1069
  }
1004
- transform(matrix) {
1005
- this.transformPoint((point) => point.applyMatrix3(matrix));
1070
+ getNormalAt(u, output) {
1071
+ return this.getNormal(this.getUToTMapping(u), output);
1072
+ }
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));
1006
1094
  return this;
1007
1095
  }
1008
1096
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
@@ -1018,7 +1106,7 @@ class Curve {
1018
1106
  const { min, max } = this.getMinMax();
1019
1107
  return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
1020
1108
  }
1021
- getCommands() {
1109
+ toCommands() {
1022
1110
  return this.getPoints().map((point, i) => {
1023
1111
  if (i === 0) {
1024
1112
  return { type: "M", x: point.x, y: point.y };
@@ -1027,12 +1115,20 @@ class Curve {
1027
1115
  }
1028
1116
  });
1029
1117
  }
1030
- getData() {
1031
- return pathCommandsToPathData(this.getCommands());
1118
+ toData() {
1119
+ return pathCommandsToPathData(this.toCommands());
1032
1120
  }
1033
- /** overrideable */
1034
- // eslint-disable-next-line unused-imports/no-unused-vars
1035
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
+ });
1036
1132
  return this;
1037
1133
  }
1038
1134
  copy(source) {
@@ -1054,20 +1150,19 @@ class CircleCurve extends Curve {
1054
1150
  }
1055
1151
  getPoint(t) {
1056
1152
  const { radius, center } = this;
1057
- return center.clone().add(this.getNormal(t).clone().multiplyScalar(radius));
1153
+ return center.clone().add(this.getNormal(t).clone().scale(radius));
1058
1154
  }
1059
- getTangent(t) {
1155
+ getTangent(t, output = new Vector2()) {
1060
1156
  const { x, y } = this.getNormal(t);
1061
- return new Vector2(-y, x);
1157
+ return output.set(-y, x);
1062
1158
  }
1063
- getNormal(t) {
1159
+ getNormal(t, output = new Vector2()) {
1064
1160
  const { start, end } = this;
1065
1161
  const _t = t * (end - start) + start - 0.5 * Math.PI;
1066
- return new Vector2(Math.cos(_t), Math.sin(_t));
1162
+ return output.set(Math.cos(_t), Math.sin(_t));
1067
1163
  }
1068
- transformPoint(cb) {
1069
- cb(this.center);
1070
- return this;
1164
+ getControlPoints() {
1165
+ return [this.center];
1071
1166
  }
1072
1167
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1073
1168
  min.x = Math.min(min.x, this.center.x - this.radius);
@@ -1126,18 +1221,18 @@ class CubicBezierCurve extends Curve {
1126
1221
  }
1127
1222
  getPoint(t, output = new Vector2()) {
1128
1223
  const { start, startControl, endControl, end } = this;
1129
- output.set(
1224
+ return output.set(
1130
1225
  cubicBezier(t, start.x, startControl.x, endControl.x, end.x),
1131
1226
  cubicBezier(t, start.y, startControl.y, endControl.y, end.y)
1132
1227
  );
1133
- return output;
1134
1228
  }
1135
- transformPoint(cb) {
1136
- cb(this.start);
1137
- cb(this.startControl);
1138
- cb(this.endControl);
1139
- cb(this.end);
1140
- return this;
1229
+ getControlPoints() {
1230
+ return [
1231
+ this.start,
1232
+ this.startControl,
1233
+ this.endControl,
1234
+ this.end
1235
+ ];
1141
1236
  }
1142
1237
  _solveQuadratic(a, b, c) {
1143
1238
  const discriminant = b * b - 4 * a * c;
@@ -1180,7 +1275,7 @@ class CubicBezierCurve extends Curve {
1180
1275
  samplePoints(tValues, 10);
1181
1276
  return { min, max };
1182
1277
  }
1183
- getCommands() {
1278
+ toCommands() {
1184
1279
  const { start, startControl, endControl, end } = this;
1185
1280
  return [
1186
1281
  { type: "M", x: start.x, y: start.y },
@@ -1188,7 +1283,8 @@ class CubicBezierCurve extends Curve {
1188
1283
  ];
1189
1284
  }
1190
1285
  drawTo(ctx) {
1191
- const { startControl, endControl, end } = this;
1286
+ const { start, startControl, endControl, end } = this;
1287
+ ctx.lineTo(start.x, start.y);
1192
1288
  ctx.bezierCurveTo(startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y);
1193
1289
  return this;
1194
1290
  }
@@ -1217,6 +1313,9 @@ class EllipseCurve extends Curve {
1217
1313
  this.endAngle = endAngle;
1218
1314
  this.clockwise = clockwise;
1219
1315
  }
1316
+ isClockwise() {
1317
+ return this.clockwise;
1318
+ }
1220
1319
  getPoint(t, output = new Vector2()) {
1221
1320
  const twoPi = Math.PI * 2;
1222
1321
  let deltaAngle = this.endAngle - this.startAngle;
@@ -1252,7 +1351,7 @@ class EllipseCurve extends Curve {
1252
1351
  }
1253
1352
  return output.set(_x, _y);
1254
1353
  }
1255
- getCommands() {
1354
+ toCommands() {
1256
1355
  const { center, radiusX: rx, radiusY: ry, startAngle, endAngle, clockwise, rotation } = this;
1257
1356
  const { x: cx, y: cy } = center;
1258
1357
  const startX = cx + rx * Math.cos(startAngle) * Math.cos(rotation) - ry * Math.sin(startAngle) * Math.sin(rotation);
@@ -1293,7 +1392,7 @@ class EllipseCurve extends Curve {
1293
1392
  );
1294
1393
  return this;
1295
1394
  }
1296
- transform(matrix) {
1395
+ matrix(matrix) {
1297
1396
  tempV2.set(this.center.x, this.center.y);
1298
1397
  tempV2.applyMatrix3(matrix);
1299
1398
  this.center.x = tempV2.x;
@@ -1305,9 +1404,8 @@ class EllipseCurve extends Curve {
1305
1404
  }
1306
1405
  return this;
1307
1406
  }
1308
- transformPoint(cb) {
1309
- cb(this.center);
1310
- return this;
1407
+ getControlPoints() {
1408
+ return [this.center];
1311
1409
  }
1312
1410
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1313
1411
  const { center, radiusX: rx, radiusY: ry, rotation: theta } = this;
@@ -1489,7 +1587,7 @@ class LineCurve extends Curve {
1489
1587
  if (t === 1) {
1490
1588
  output.copy(this.end);
1491
1589
  } else {
1492
- 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);
1493
1591
  }
1494
1592
  return output;
1495
1593
  }
@@ -1502,14 +1600,11 @@ class LineCurve extends Curve {
1502
1600
  getTangentAt(u, output = new Vector2()) {
1503
1601
  return this.getTangent(u, output);
1504
1602
  }
1505
- getNormal(t, output = new Vector2()) {
1506
- const { x, y } = this.getPoint(t).sub(this.start);
1507
- return output.set(y, -x).normalize();
1508
- }
1509
- transformPoint(cb) {
1510
- cb(this.start);
1511
- cb(this.end);
1512
- return this;
1603
+ getControlPoints() {
1604
+ return [
1605
+ this.start,
1606
+ this.end
1607
+ ];
1513
1608
  }
1514
1609
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1515
1610
  const { start, end } = this;
@@ -1519,7 +1614,7 @@ class LineCurve extends Curve {
1519
1614
  max.y = Math.max(max.y, start.y, end.y);
1520
1615
  return { min, max };
1521
1616
  }
1522
- getCommands() {
1617
+ toCommands() {
1523
1618
  const { start, end } = this;
1524
1619
  return [
1525
1620
  { type: "M", x: start.x, y: start.y },
@@ -1527,7 +1622,8 @@ class LineCurve extends Curve {
1527
1622
  ];
1528
1623
  }
1529
1624
  drawTo(ctx) {
1530
- const { end } = this;
1625
+ const { start, end } = this;
1626
+ ctx.lineTo(start.x, start.y);
1531
1627
  ctx.lineTo(end.x, end.y);
1532
1628
  return this;
1533
1629
  }
@@ -1603,22 +1699,21 @@ class HeartCurve extends Curve {
1603
1699
  }
1604
1700
  return this.curves[index];
1605
1701
  }
1606
- getTangent(t) {
1607
- return this.getCurve(t).getTangent(this.curveT);
1702
+ getTangent(t, output) {
1703
+ return this.getCurve(t).getTangent(this.curveT, output);
1608
1704
  }
1609
- getNormal(t) {
1610
- return this.getCurve(t).getNormal(this.curveT);
1705
+ getNormal(t, output) {
1706
+ return this.getCurve(t).getNormal(this.curveT, output);
1611
1707
  }
1612
- transformPoint(cb) {
1613
- this.curves.forEach((curve) => curve.transformPoint(cb));
1614
- return this;
1708
+ getControlPoints() {
1709
+ return this.curves.flatMap((curve) => curve.getControlPoints());
1615
1710
  }
1616
1711
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1617
1712
  this.curves.forEach((curve) => curve.getMinMax(min, max));
1618
1713
  return { min, max };
1619
1714
  }
1620
- getCommands() {
1621
- return this.curves.flatMap((curve) => curve.getCommands());
1715
+ toCommands() {
1716
+ return this.curves.flatMap((curve) => curve.toCommands());
1622
1717
  }
1623
1718
  drawTo(ctx) {
1624
1719
  this.curves.forEach((curve) => curve.drawTo(ctx));
@@ -1681,16 +1776,15 @@ class PloygonCurve extends Curve {
1681
1776
  getNormal(t, output) {
1682
1777
  return this.getCurve(t).getNormal(this.curveT, output);
1683
1778
  }
1684
- transformPoint(cb) {
1685
- this.curves.forEach((curve) => curve.transformPoint(cb));
1686
- return this;
1779
+ getControlPoints() {
1780
+ return this.curves.flatMap((curve) => curve.getControlPoints());
1687
1781
  }
1688
1782
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1689
1783
  this.curves.forEach((curve) => curve.getMinMax(min, max));
1690
1784
  return { min, max };
1691
1785
  }
1692
- getCommands() {
1693
- return this.curves.flatMap((curve) => curve.getCommands());
1786
+ toCommands() {
1787
+ return this.curves.flatMap((curve) => curve.toCommands());
1694
1788
  }
1695
1789
  drawTo(ctx) {
1696
1790
  this.curves.forEach((curve) => curve.drawTo(ctx));
@@ -1713,11 +1807,12 @@ class QuadraticBezierCurve extends Curve {
1713
1807
  );
1714
1808
  return output;
1715
1809
  }
1716
- transformPoint(cb) {
1717
- cb(this.start);
1718
- cb(this.control);
1719
- cb(this.end);
1720
- return this;
1810
+ getControlPoints() {
1811
+ return [
1812
+ this.start,
1813
+ this.control,
1814
+ this.end
1815
+ ];
1721
1816
  }
1722
1817
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1723
1818
  const { start, control, end } = this;
@@ -1731,7 +1826,7 @@ class QuadraticBezierCurve extends Curve {
1731
1826
  max.y = Math.max(max.y, start.y, end.y, y1, y2);
1732
1827
  return { min, max };
1733
1828
  }
1734
- getCommands() {
1829
+ toCommands() {
1735
1830
  const { start, control, end } = this;
1736
1831
  return [
1737
1832
  { type: "M", x: start.x, y: start.y },
@@ -1739,7 +1834,8 @@ class QuadraticBezierCurve extends Curve {
1739
1834
  ];
1740
1835
  }
1741
1836
  drawTo(ctx) {
1742
- const { control, end } = this;
1837
+ const { start, control, end } = this;
1838
+ ctx.lineTo(start.x, start.y);
1743
1839
  ctx.quadraticCurveTo(control.x, control.y, end.x, end.y);
1744
1840
  return this;
1745
1841
  }
@@ -1829,16 +1925,15 @@ class RectangularCurve extends Curve {
1829
1925
  getNormal(t, output) {
1830
1926
  return this.getCurve(t).getNormal(this.curveT, output);
1831
1927
  }
1832
- transformPoint(cb) {
1833
- this.curves.forEach((curve) => curve.transformPoint(cb));
1834
- return this;
1928
+ getControlPoints() {
1929
+ return this.curves.flatMap((curve) => curve.getControlPoints());
1835
1930
  }
1836
1931
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
1837
1932
  this.curves.forEach((curve) => curve.getMinMax(min, max));
1838
1933
  return { min, max };
1839
1934
  }
1840
- getCommands() {
1841
- return this.curves.flatMap((curve) => curve.getCommands());
1935
+ toCommands() {
1936
+ return this.curves.flatMap((curve) => curve.toCommands());
1842
1937
  }
1843
1938
  drawTo(ctx) {
1844
1939
  this.curves.forEach((curve) => curve.drawTo(ctx));
@@ -1866,9 +1961,8 @@ class SplineCurve extends Curve {
1866
1961
  );
1867
1962
  return output;
1868
1963
  }
1869
- transformPoint(cb) {
1870
- this.points.forEach((point) => cb(point));
1871
- return this;
1964
+ getControlPoints() {
1965
+ return this.points;
1872
1966
  }
1873
1967
  copy(source) {
1874
1968
  super.copy(source);
@@ -1933,6 +2027,9 @@ class CurvePath extends Curve {
1933
2027
  }
1934
2028
  return output;
1935
2029
  }
2030
+ getControlPoints() {
2031
+ return this.curves.flatMap((curve) => curve.getControlPoints());
2032
+ }
1936
2033
  getLength() {
1937
2034
  const lengths = this.getCurveLengths();
1938
2035
  return lengths[lengths.length - 1];
@@ -2104,10 +2201,6 @@ class CurvePath extends Curve {
2104
2201
  this._setCurrentPoint(points[points.length - 1]);
2105
2202
  return this;
2106
2203
  }
2107
- transformPoint(cb) {
2108
- this.curves.forEach((curve) => curve.transformPoint(cb));
2109
- return this;
2110
- }
2111
2204
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
2112
2205
  this.curves.forEach((curve) => curve.getMinMax(min, max));
2113
2206
  return { min, max };
@@ -2116,8 +2209,8 @@ class CurvePath extends Curve {
2116
2209
  const { min, max } = this.getMinMax();
2117
2210
  return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
2118
2211
  }
2119
- getCommands() {
2120
- return this.curves.flatMap((curve) => curve.getCommands());
2212
+ toCommands() {
2213
+ return this.curves.flatMap((curve) => curve.toCommands());
2121
2214
  }
2122
2215
  drawTo(ctx) {
2123
2216
  const point = this.curves[0]?.getPoint(0);
@@ -2145,6 +2238,29 @@ class CurvePath extends Curve {
2145
2238
  function toKebabCase(str) {
2146
2239
  return str.replace(/[^a-z0-9]/gi, "-").replace(/\B([A-Z])/g, "-$1").toLowerCase();
2147
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
+ }
2148
2264
 
2149
2265
  var __defProp = Object.defineProperty;
2150
2266
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -2247,20 +2363,93 @@ class Path2D {
2247
2363
  this.currentPath.splineThru(points);
2248
2364
  return this;
2249
2365
  }
2250
- forEachCurve(cb) {
2251
- 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
+ });
2376
+ return this;
2377
+ }
2378
+ skew(ax, ay = 0, target = { x: 0, y: 0 }) {
2379
+ this.getControlPoints().forEach((point) => {
2380
+ point.skew(ax, ay, target);
2381
+ });
2252
2382
  return this;
2253
2383
  }
2254
- transformPoint(cb) {
2255
- this.forEachCurve((curve) => curve.transformPoint(cb));
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
+ });
2256
2445
  return this;
2257
2446
  }
2258
- transform(matrix) {
2259
- this.forEachCurve((curve) => curve.transform(matrix));
2447
+ matrix(matrix) {
2448
+ this.getCurves().forEach((curve) => curve.matrix(matrix));
2260
2449
  return this;
2261
2450
  }
2262
2451
  getMinMax(min = Vector2.MAX, max = Vector2.MIN, withStyle = true) {
2263
- this.forEachCurve((curve) => curve.getMinMax(min, max));
2452
+ this.getCurves().forEach((curve) => curve.getMinMax(min, max));
2264
2453
  if (withStyle) {
2265
2454
  const strokeHalfWidth = this.strokeWidth / 2;
2266
2455
  min.x -= strokeHalfWidth;
@@ -2274,13 +2463,50 @@ class Path2D {
2274
2463
  const { min, max } = this.getMinMax(void 0, void 0, withStyle);
2275
2464
  return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
2276
2465
  }
2277
- getCommands() {
2278
- 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;
2279
2483
  }
2280
- getData() {
2281
- 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());
2282
2505
  }
2283
- getSvgPathXml() {
2506
+ toData() {
2507
+ return this.paths.map((path) => path.toData()).join(" ");
2508
+ }
2509
+ toSvgPathString() {
2284
2510
  const style = {
2285
2511
  ...this.style,
2286
2512
  fill: this.style.fill ?? "#000",
@@ -2301,40 +2527,21 @@ class Path2D {
2301
2527
  cssText += `${key}:${cssStyle[key]};`;
2302
2528
  }
2303
2529
  }
2304
- return `<path d="${this.getData()}" style="${cssText}"></path>`;
2530
+ return `<path d="${this.toData()}" style="${cssText}"></path>`;
2305
2531
  }
2306
- getSvgXml() {
2532
+ toSvgString() {
2307
2533
  const { x, y, width, height } = this.getBoundingBox();
2308
- const path = this.getSvgPathXml();
2534
+ const path = this.toSvgPathString();
2309
2535
  return `<svg viewBox="${x} ${y} ${width} ${height}" width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">${path}</svg>`;
2310
2536
  }
2311
- getSvgDataUri() {
2312
- return `data:image/svg+xml;base64,${btoa(this.getSvgXml())}`;
2313
- }
2314
- drawTo(ctx, style = {}) {
2315
- style = { ...this.style, ...style };
2316
- const { fill = "#000", stroke = "none" } = style;
2317
- setCanvasContext(ctx, style);
2318
- this.paths.forEach((path) => {
2319
- path.drawTo(ctx);
2320
- });
2321
- if (fill !== "none") {
2322
- ctx.fill();
2323
- }
2324
- if (stroke !== "none") {
2325
- ctx.stroke();
2326
- }
2327
- }
2328
- copy(source) {
2329
- this.currentPath = source.currentPath.clone();
2330
- this.paths = source.paths.map((path) => path.clone());
2331
- this.style = { ...source.style };
2332
- return this;
2537
+ toSvgUrl() {
2538
+ return `data:image/svg+xml;base64,${btoa(this.toSvgString())}`;
2333
2539
  }
2334
2540
  toSvg() {
2335
- return new DOMParser().parseFromString(this.getSvgXml(), "image/svg+xml").documentElement;
2541
+ return new DOMParser().parseFromString(this.toSvgString(), "image/svg+xml").documentElement;
2336
2542
  }
2337
- toCanvas(pixelRatio = 2) {
2543
+ toCanvas(options = {}) {
2544
+ const { pixelRatio = 2, ...style } = options;
2338
2545
  const { left, top, width, height } = this.getBoundingBox();
2339
2546
  const canvas = document.createElement("canvas");
2340
2547
  canvas.width = width * pixelRatio;
@@ -2345,10 +2552,16 @@ class Path2D {
2345
2552
  if (ctx) {
2346
2553
  ctx.scale(pixelRatio, pixelRatio);
2347
2554
  ctx.translate(-left, -top);
2348
- this.drawTo(ctx);
2555
+ this.drawTo(ctx, style);
2349
2556
  }
2350
2557
  return canvas;
2351
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
+ }
2352
2565
  clone() {
2353
2566
  return new this.constructor().copy(this);
2354
2567
  }
@@ -2825,7 +3038,7 @@ function parseNode(node, style, paths = []) {
2825
3038
  const transformStack = [];
2826
3039
  const transform = getNodeTransform(node, currentTransform, transformStack);
2827
3040
  if (path) {
2828
- path.transform(currentTransform);
3041
+ path.matrix(currentTransform);
2829
3042
  paths.push(path);
2830
3043
  path.style = style;
2831
3044
  }