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