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/README.md +2 -2
- package/dist/index.cjs +358 -145
- package/dist/index.d.cts +64 -45
- package/dist/index.d.mts +64 -45
- package/dist/index.d.ts +64 -45
- package/dist/index.js +1 -1
- package/dist/index.mjs +358 -145
- 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;
|
|
@@ -141,6 +193,11 @@ class BoundingBox {
|
|
|
141
193
|
return this.top + this.height;
|
|
142
194
|
}
|
|
143
195
|
static from(...boxes) {
|
|
196
|
+
if (boxes.length === 0) {
|
|
197
|
+
return new BoundingBox();
|
|
198
|
+
} else if (boxes.length === 1) {
|
|
199
|
+
return boxes[0].clone();
|
|
200
|
+
}
|
|
144
201
|
const firstBox = boxes[0];
|
|
145
202
|
const merged = boxes.slice(1).reduce(
|
|
146
203
|
(merged2, box) => {
|
|
@@ -910,8 +967,14 @@ class Curve {
|
|
|
910
967
|
__publicField$5(this, "_cacheArcLengths");
|
|
911
968
|
__publicField$5(this, "_needsUpdate", false);
|
|
912
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
|
+
}
|
|
913
976
|
getPointAt(u, output = new Vector2()) {
|
|
914
|
-
return this.getPoint(this.
|
|
977
|
+
return this.getPoint(this.getUToTMapping(u), output);
|
|
915
978
|
}
|
|
916
979
|
getPoints(divisions = 5) {
|
|
917
980
|
const points = [];
|
|
@@ -920,6 +983,10 @@ class Curve {
|
|
|
920
983
|
}
|
|
921
984
|
return points;
|
|
922
985
|
}
|
|
986
|
+
forEachControlPoints(cb) {
|
|
987
|
+
this.getControlPoints().forEach(cb);
|
|
988
|
+
return this;
|
|
989
|
+
}
|
|
923
990
|
getSpacedPoints(divisions = 5) {
|
|
924
991
|
const points = [];
|
|
925
992
|
for (let i = 0; i <= divisions; i++) {
|
|
@@ -954,22 +1021,22 @@ class Curve {
|
|
|
954
1021
|
this._needsUpdate = true;
|
|
955
1022
|
this.getLengths();
|
|
956
1023
|
}
|
|
957
|
-
|
|
958
|
-
const
|
|
1024
|
+
getUToTMapping(u, distance) {
|
|
1025
|
+
const lengths = this.getLengths();
|
|
959
1026
|
let i = 0;
|
|
960
|
-
const
|
|
961
|
-
let
|
|
1027
|
+
const lengthsLen = lengths.length;
|
|
1028
|
+
let targetLength;
|
|
962
1029
|
if (distance) {
|
|
963
|
-
|
|
1030
|
+
targetLength = distance;
|
|
964
1031
|
} else {
|
|
965
|
-
|
|
1032
|
+
targetLength = u * lengths[lengthsLen - 1];
|
|
966
1033
|
}
|
|
967
1034
|
let low = 0;
|
|
968
|
-
let high =
|
|
1035
|
+
let high = lengthsLen - 1;
|
|
969
1036
|
let comparison;
|
|
970
1037
|
while (low <= high) {
|
|
971
1038
|
i = Math.floor(low + (high - low) / 2);
|
|
972
|
-
comparison =
|
|
1039
|
+
comparison = lengths[i] - targetLength;
|
|
973
1040
|
if (comparison < 0) {
|
|
974
1041
|
low = i + 1;
|
|
975
1042
|
} else if (comparison > 0) {
|
|
@@ -980,14 +1047,14 @@ class Curve {
|
|
|
980
1047
|
}
|
|
981
1048
|
}
|
|
982
1049
|
i = high;
|
|
983
|
-
if (
|
|
984
|
-
return i / (
|
|
1050
|
+
if (lengths[i] === targetLength) {
|
|
1051
|
+
return i / (lengthsLen - 1);
|
|
985
1052
|
}
|
|
986
|
-
const lengthBefore =
|
|
987
|
-
const lengthAfter =
|
|
1053
|
+
const lengthBefore = lengths[i];
|
|
1054
|
+
const lengthAfter = lengths[i + 1];
|
|
988
1055
|
const segmentLength = lengthAfter - lengthBefore;
|
|
989
|
-
const segmentFraction = (
|
|
990
|
-
return (i + segmentFraction) / (
|
|
1056
|
+
const segmentFraction = (targetLength - lengthBefore) / segmentLength;
|
|
1057
|
+
return (i + segmentFraction) / (lengthsLen - 1);
|
|
991
1058
|
}
|
|
992
1059
|
getTangent(t, output = new Vector2()) {
|
|
993
1060
|
const delta = 1e-4;
|
|
@@ -996,15 +1063,36 @@ class Curve {
|
|
|
996
1063
|
return output.copy(this.getPoint(t2).sub(this.getPoint(t1)).normalize());
|
|
997
1064
|
}
|
|
998
1065
|
getTangentAt(u, output) {
|
|
999
|
-
return this.getTangent(this.
|
|
1066
|
+
return this.getTangent(this.getUToTMapping(u), output);
|
|
1000
1067
|
}
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
return this;
|
|
1068
|
+
getNormal(t, output = new Vector2()) {
|
|
1069
|
+
this.getTangent(t, output);
|
|
1070
|
+
return output.set(-output.y, output.x).normalize();
|
|
1005
1071
|
}
|
|
1006
|
-
|
|
1007
|
-
this.
|
|
1072
|
+
getNormalAt(u, output) {
|
|
1073
|
+
return this.getNormal(this.getUToTMapping(u), output);
|
|
1074
|
+
}
|
|
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));
|
|
1008
1096
|
return this;
|
|
1009
1097
|
}
|
|
1010
1098
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
@@ -1020,7 +1108,7 @@ class Curve {
|
|
|
1020
1108
|
const { min, max } = this.getMinMax();
|
|
1021
1109
|
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
1022
1110
|
}
|
|
1023
|
-
|
|
1111
|
+
toCommands() {
|
|
1024
1112
|
return this.getPoints().map((point, i) => {
|
|
1025
1113
|
if (i === 0) {
|
|
1026
1114
|
return { type: "M", x: point.x, y: point.y };
|
|
@@ -1029,12 +1117,20 @@ class Curve {
|
|
|
1029
1117
|
}
|
|
1030
1118
|
});
|
|
1031
1119
|
}
|
|
1032
|
-
|
|
1033
|
-
return pathCommandsToPathData(this.
|
|
1120
|
+
toData() {
|
|
1121
|
+
return pathCommandsToPathData(this.toCommands());
|
|
1034
1122
|
}
|
|
1035
|
-
/** overrideable */
|
|
1036
|
-
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
1037
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
|
+
});
|
|
1038
1134
|
return this;
|
|
1039
1135
|
}
|
|
1040
1136
|
copy(source) {
|
|
@@ -1056,20 +1152,19 @@ class CircleCurve extends Curve {
|
|
|
1056
1152
|
}
|
|
1057
1153
|
getPoint(t) {
|
|
1058
1154
|
const { radius, center } = this;
|
|
1059
|
-
return center.clone().add(this.getNormal(t).clone().
|
|
1155
|
+
return center.clone().add(this.getNormal(t).clone().scale(radius));
|
|
1060
1156
|
}
|
|
1061
|
-
getTangent(t) {
|
|
1157
|
+
getTangent(t, output = new Vector2()) {
|
|
1062
1158
|
const { x, y } = this.getNormal(t);
|
|
1063
|
-
return
|
|
1159
|
+
return output.set(-y, x);
|
|
1064
1160
|
}
|
|
1065
|
-
getNormal(t) {
|
|
1161
|
+
getNormal(t, output = new Vector2()) {
|
|
1066
1162
|
const { start, end } = this;
|
|
1067
1163
|
const _t = t * (end - start) + start - 0.5 * Math.PI;
|
|
1068
|
-
return
|
|
1164
|
+
return output.set(Math.cos(_t), Math.sin(_t));
|
|
1069
1165
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
return this;
|
|
1166
|
+
getControlPoints() {
|
|
1167
|
+
return [this.center];
|
|
1073
1168
|
}
|
|
1074
1169
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1075
1170
|
min.x = Math.min(min.x, this.center.x - this.radius);
|
|
@@ -1128,18 +1223,18 @@ class CubicBezierCurve extends Curve {
|
|
|
1128
1223
|
}
|
|
1129
1224
|
getPoint(t, output = new Vector2()) {
|
|
1130
1225
|
const { start, startControl, endControl, end } = this;
|
|
1131
|
-
output.set(
|
|
1226
|
+
return output.set(
|
|
1132
1227
|
cubicBezier(t, start.x, startControl.x, endControl.x, end.x),
|
|
1133
1228
|
cubicBezier(t, start.y, startControl.y, endControl.y, end.y)
|
|
1134
1229
|
);
|
|
1135
|
-
return output;
|
|
1136
1230
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1231
|
+
getControlPoints() {
|
|
1232
|
+
return [
|
|
1233
|
+
this.start,
|
|
1234
|
+
this.startControl,
|
|
1235
|
+
this.endControl,
|
|
1236
|
+
this.end
|
|
1237
|
+
];
|
|
1143
1238
|
}
|
|
1144
1239
|
_solveQuadratic(a, b, c) {
|
|
1145
1240
|
const discriminant = b * b - 4 * a * c;
|
|
@@ -1182,7 +1277,7 @@ class CubicBezierCurve extends Curve {
|
|
|
1182
1277
|
samplePoints(tValues, 10);
|
|
1183
1278
|
return { min, max };
|
|
1184
1279
|
}
|
|
1185
|
-
|
|
1280
|
+
toCommands() {
|
|
1186
1281
|
const { start, startControl, endControl, end } = this;
|
|
1187
1282
|
return [
|
|
1188
1283
|
{ type: "M", x: start.x, y: start.y },
|
|
@@ -1190,7 +1285,8 @@ class CubicBezierCurve extends Curve {
|
|
|
1190
1285
|
];
|
|
1191
1286
|
}
|
|
1192
1287
|
drawTo(ctx) {
|
|
1193
|
-
const { startControl, endControl, end } = this;
|
|
1288
|
+
const { start, startControl, endControl, end } = this;
|
|
1289
|
+
ctx.lineTo(start.x, start.y);
|
|
1194
1290
|
ctx.bezierCurveTo(startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y);
|
|
1195
1291
|
return this;
|
|
1196
1292
|
}
|
|
@@ -1219,6 +1315,9 @@ class EllipseCurve extends Curve {
|
|
|
1219
1315
|
this.endAngle = endAngle;
|
|
1220
1316
|
this.clockwise = clockwise;
|
|
1221
1317
|
}
|
|
1318
|
+
isClockwise() {
|
|
1319
|
+
return this.clockwise;
|
|
1320
|
+
}
|
|
1222
1321
|
getPoint(t, output = new Vector2()) {
|
|
1223
1322
|
const twoPi = Math.PI * 2;
|
|
1224
1323
|
let deltaAngle = this.endAngle - this.startAngle;
|
|
@@ -1254,7 +1353,7 @@ class EllipseCurve extends Curve {
|
|
|
1254
1353
|
}
|
|
1255
1354
|
return output.set(_x, _y);
|
|
1256
1355
|
}
|
|
1257
|
-
|
|
1356
|
+
toCommands() {
|
|
1258
1357
|
const { center, radiusX: rx, radiusY: ry, startAngle, endAngle, clockwise, rotation } = this;
|
|
1259
1358
|
const { x: cx, y: cy } = center;
|
|
1260
1359
|
const startX = cx + rx * Math.cos(startAngle) * Math.cos(rotation) - ry * Math.sin(startAngle) * Math.sin(rotation);
|
|
@@ -1295,7 +1394,7 @@ class EllipseCurve extends Curve {
|
|
|
1295
1394
|
);
|
|
1296
1395
|
return this;
|
|
1297
1396
|
}
|
|
1298
|
-
|
|
1397
|
+
matrix(matrix) {
|
|
1299
1398
|
tempV2.set(this.center.x, this.center.y);
|
|
1300
1399
|
tempV2.applyMatrix3(matrix);
|
|
1301
1400
|
this.center.x = tempV2.x;
|
|
@@ -1307,9 +1406,8 @@ class EllipseCurve extends Curve {
|
|
|
1307
1406
|
}
|
|
1308
1407
|
return this;
|
|
1309
1408
|
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
return this;
|
|
1409
|
+
getControlPoints() {
|
|
1410
|
+
return [this.center];
|
|
1313
1411
|
}
|
|
1314
1412
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1315
1413
|
const { center, radiusX: rx, radiusY: ry, rotation: theta } = this;
|
|
@@ -1491,7 +1589,7 @@ class LineCurve extends Curve {
|
|
|
1491
1589
|
if (t === 1) {
|
|
1492
1590
|
output.copy(this.end);
|
|
1493
1591
|
} else {
|
|
1494
|
-
output.copy(this.end).sub(this.start).
|
|
1592
|
+
output.copy(this.end).sub(this.start).scale(t).add(this.start);
|
|
1495
1593
|
}
|
|
1496
1594
|
return output;
|
|
1497
1595
|
}
|
|
@@ -1504,14 +1602,11 @@ class LineCurve extends Curve {
|
|
|
1504
1602
|
getTangentAt(u, output = new Vector2()) {
|
|
1505
1603
|
return this.getTangent(u, output);
|
|
1506
1604
|
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
cb(this.start);
|
|
1513
|
-
cb(this.end);
|
|
1514
|
-
return this;
|
|
1605
|
+
getControlPoints() {
|
|
1606
|
+
return [
|
|
1607
|
+
this.start,
|
|
1608
|
+
this.end
|
|
1609
|
+
];
|
|
1515
1610
|
}
|
|
1516
1611
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1517
1612
|
const { start, end } = this;
|
|
@@ -1521,7 +1616,7 @@ class LineCurve extends Curve {
|
|
|
1521
1616
|
max.y = Math.max(max.y, start.y, end.y);
|
|
1522
1617
|
return { min, max };
|
|
1523
1618
|
}
|
|
1524
|
-
|
|
1619
|
+
toCommands() {
|
|
1525
1620
|
const { start, end } = this;
|
|
1526
1621
|
return [
|
|
1527
1622
|
{ type: "M", x: start.x, y: start.y },
|
|
@@ -1529,7 +1624,8 @@ class LineCurve extends Curve {
|
|
|
1529
1624
|
];
|
|
1530
1625
|
}
|
|
1531
1626
|
drawTo(ctx) {
|
|
1532
|
-
const { end } = this;
|
|
1627
|
+
const { start, end } = this;
|
|
1628
|
+
ctx.lineTo(start.x, start.y);
|
|
1533
1629
|
ctx.lineTo(end.x, end.y);
|
|
1534
1630
|
return this;
|
|
1535
1631
|
}
|
|
@@ -1605,22 +1701,21 @@ class HeartCurve extends Curve {
|
|
|
1605
1701
|
}
|
|
1606
1702
|
return this.curves[index];
|
|
1607
1703
|
}
|
|
1608
|
-
getTangent(t) {
|
|
1609
|
-
return this.getCurve(t).getTangent(this.curveT);
|
|
1704
|
+
getTangent(t, output) {
|
|
1705
|
+
return this.getCurve(t).getTangent(this.curveT, output);
|
|
1610
1706
|
}
|
|
1611
|
-
getNormal(t) {
|
|
1612
|
-
return this.getCurve(t).getNormal(this.curveT);
|
|
1707
|
+
getNormal(t, output) {
|
|
1708
|
+
return this.getCurve(t).getNormal(this.curveT, output);
|
|
1613
1709
|
}
|
|
1614
|
-
|
|
1615
|
-
this.curves.
|
|
1616
|
-
return this;
|
|
1710
|
+
getControlPoints() {
|
|
1711
|
+
return this.curves.flatMap((curve) => curve.getControlPoints());
|
|
1617
1712
|
}
|
|
1618
1713
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1619
1714
|
this.curves.forEach((curve) => curve.getMinMax(min, max));
|
|
1620
1715
|
return { min, max };
|
|
1621
1716
|
}
|
|
1622
|
-
|
|
1623
|
-
return this.curves.flatMap((curve) => curve.
|
|
1717
|
+
toCommands() {
|
|
1718
|
+
return this.curves.flatMap((curve) => curve.toCommands());
|
|
1624
1719
|
}
|
|
1625
1720
|
drawTo(ctx) {
|
|
1626
1721
|
this.curves.forEach((curve) => curve.drawTo(ctx));
|
|
@@ -1683,16 +1778,15 @@ class PloygonCurve extends Curve {
|
|
|
1683
1778
|
getNormal(t, output) {
|
|
1684
1779
|
return this.getCurve(t).getNormal(this.curveT, output);
|
|
1685
1780
|
}
|
|
1686
|
-
|
|
1687
|
-
this.curves.
|
|
1688
|
-
return this;
|
|
1781
|
+
getControlPoints() {
|
|
1782
|
+
return this.curves.flatMap((curve) => curve.getControlPoints());
|
|
1689
1783
|
}
|
|
1690
1784
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1691
1785
|
this.curves.forEach((curve) => curve.getMinMax(min, max));
|
|
1692
1786
|
return { min, max };
|
|
1693
1787
|
}
|
|
1694
|
-
|
|
1695
|
-
return this.curves.flatMap((curve) => curve.
|
|
1788
|
+
toCommands() {
|
|
1789
|
+
return this.curves.flatMap((curve) => curve.toCommands());
|
|
1696
1790
|
}
|
|
1697
1791
|
drawTo(ctx) {
|
|
1698
1792
|
this.curves.forEach((curve) => curve.drawTo(ctx));
|
|
@@ -1715,11 +1809,12 @@ class QuadraticBezierCurve extends Curve {
|
|
|
1715
1809
|
);
|
|
1716
1810
|
return output;
|
|
1717
1811
|
}
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1812
|
+
getControlPoints() {
|
|
1813
|
+
return [
|
|
1814
|
+
this.start,
|
|
1815
|
+
this.control,
|
|
1816
|
+
this.end
|
|
1817
|
+
];
|
|
1723
1818
|
}
|
|
1724
1819
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1725
1820
|
const { start, control, end } = this;
|
|
@@ -1733,7 +1828,7 @@ class QuadraticBezierCurve extends Curve {
|
|
|
1733
1828
|
max.y = Math.max(max.y, start.y, end.y, y1, y2);
|
|
1734
1829
|
return { min, max };
|
|
1735
1830
|
}
|
|
1736
|
-
|
|
1831
|
+
toCommands() {
|
|
1737
1832
|
const { start, control, end } = this;
|
|
1738
1833
|
return [
|
|
1739
1834
|
{ type: "M", x: start.x, y: start.y },
|
|
@@ -1741,7 +1836,8 @@ class QuadraticBezierCurve extends Curve {
|
|
|
1741
1836
|
];
|
|
1742
1837
|
}
|
|
1743
1838
|
drawTo(ctx) {
|
|
1744
|
-
const { control, end } = this;
|
|
1839
|
+
const { start, control, end } = this;
|
|
1840
|
+
ctx.lineTo(start.x, start.y);
|
|
1745
1841
|
ctx.quadraticCurveTo(control.x, control.y, end.x, end.y);
|
|
1746
1842
|
return this;
|
|
1747
1843
|
}
|
|
@@ -1831,16 +1927,15 @@ class RectangularCurve extends Curve {
|
|
|
1831
1927
|
getNormal(t, output) {
|
|
1832
1928
|
return this.getCurve(t).getNormal(this.curveT, output);
|
|
1833
1929
|
}
|
|
1834
|
-
|
|
1835
|
-
this.curves.
|
|
1836
|
-
return this;
|
|
1930
|
+
getControlPoints() {
|
|
1931
|
+
return this.curves.flatMap((curve) => curve.getControlPoints());
|
|
1837
1932
|
}
|
|
1838
1933
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
1839
1934
|
this.curves.forEach((curve) => curve.getMinMax(min, max));
|
|
1840
1935
|
return { min, max };
|
|
1841
1936
|
}
|
|
1842
|
-
|
|
1843
|
-
return this.curves.flatMap((curve) => curve.
|
|
1937
|
+
toCommands() {
|
|
1938
|
+
return this.curves.flatMap((curve) => curve.toCommands());
|
|
1844
1939
|
}
|
|
1845
1940
|
drawTo(ctx) {
|
|
1846
1941
|
this.curves.forEach((curve) => curve.drawTo(ctx));
|
|
@@ -1868,9 +1963,8 @@ class SplineCurve extends Curve {
|
|
|
1868
1963
|
);
|
|
1869
1964
|
return output;
|
|
1870
1965
|
}
|
|
1871
|
-
|
|
1872
|
-
this.points
|
|
1873
|
-
return this;
|
|
1966
|
+
getControlPoints() {
|
|
1967
|
+
return this.points;
|
|
1874
1968
|
}
|
|
1875
1969
|
copy(source) {
|
|
1876
1970
|
super.copy(source);
|
|
@@ -1935,6 +2029,9 @@ class CurvePath extends Curve {
|
|
|
1935
2029
|
}
|
|
1936
2030
|
return output;
|
|
1937
2031
|
}
|
|
2032
|
+
getControlPoints() {
|
|
2033
|
+
return this.curves.flatMap((curve) => curve.getControlPoints());
|
|
2034
|
+
}
|
|
1938
2035
|
getLength() {
|
|
1939
2036
|
const lengths = this.getCurveLengths();
|
|
1940
2037
|
return lengths[lengths.length - 1];
|
|
@@ -2106,10 +2203,6 @@ class CurvePath extends Curve {
|
|
|
2106
2203
|
this._setCurrentPoint(points[points.length - 1]);
|
|
2107
2204
|
return this;
|
|
2108
2205
|
}
|
|
2109
|
-
transformPoint(cb) {
|
|
2110
|
-
this.curves.forEach((curve) => curve.transformPoint(cb));
|
|
2111
|
-
return this;
|
|
2112
|
-
}
|
|
2113
2206
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
2114
2207
|
this.curves.forEach((curve) => curve.getMinMax(min, max));
|
|
2115
2208
|
return { min, max };
|
|
@@ -2118,8 +2211,8 @@ class CurvePath extends Curve {
|
|
|
2118
2211
|
const { min, max } = this.getMinMax();
|
|
2119
2212
|
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
2120
2213
|
}
|
|
2121
|
-
|
|
2122
|
-
return this.curves.flatMap((curve) => curve.
|
|
2214
|
+
toCommands() {
|
|
2215
|
+
return this.curves.flatMap((curve) => curve.toCommands());
|
|
2123
2216
|
}
|
|
2124
2217
|
drawTo(ctx) {
|
|
2125
2218
|
const point = this.curves[0]?.getPoint(0);
|
|
@@ -2147,6 +2240,29 @@ class CurvePath extends Curve {
|
|
|
2147
2240
|
function toKebabCase(str) {
|
|
2148
2241
|
return str.replace(/[^a-z0-9]/gi, "-").replace(/\B([A-Z])/g, "-$1").toLowerCase();
|
|
2149
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
|
+
}
|
|
2150
2266
|
|
|
2151
2267
|
var __defProp = Object.defineProperty;
|
|
2152
2268
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -2249,20 +2365,93 @@ class Path2D {
|
|
|
2249
2365
|
this.currentPath.splineThru(points);
|
|
2250
2366
|
return this;
|
|
2251
2367
|
}
|
|
2252
|
-
|
|
2253
|
-
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
|
+
});
|
|
2378
|
+
return this;
|
|
2379
|
+
}
|
|
2380
|
+
skew(ax, ay = 0, target = { x: 0, y: 0 }) {
|
|
2381
|
+
this.getControlPoints().forEach((point) => {
|
|
2382
|
+
point.skew(ax, ay, target);
|
|
2383
|
+
});
|
|
2254
2384
|
return this;
|
|
2255
2385
|
}
|
|
2256
|
-
|
|
2257
|
-
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
|
+
});
|
|
2258
2447
|
return this;
|
|
2259
2448
|
}
|
|
2260
|
-
|
|
2261
|
-
this.
|
|
2449
|
+
matrix(matrix) {
|
|
2450
|
+
this.getCurves().forEach((curve) => curve.matrix(matrix));
|
|
2262
2451
|
return this;
|
|
2263
2452
|
}
|
|
2264
2453
|
getMinMax(min = Vector2.MAX, max = Vector2.MIN, withStyle = true) {
|
|
2265
|
-
this.
|
|
2454
|
+
this.getCurves().forEach((curve) => curve.getMinMax(min, max));
|
|
2266
2455
|
if (withStyle) {
|
|
2267
2456
|
const strokeHalfWidth = this.strokeWidth / 2;
|
|
2268
2457
|
min.x -= strokeHalfWidth;
|
|
@@ -2276,13 +2465,50 @@ class Path2D {
|
|
|
2276
2465
|
const { min, max } = this.getMinMax(void 0, void 0, withStyle);
|
|
2277
2466
|
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
2278
2467
|
}
|
|
2279
|
-
|
|
2280
|
-
|
|
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;
|
|
2281
2485
|
}
|
|
2282
|
-
|
|
2283
|
-
|
|
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());
|
|
2284
2507
|
}
|
|
2285
|
-
|
|
2508
|
+
toData() {
|
|
2509
|
+
return this.paths.map((path) => path.toData()).join(" ");
|
|
2510
|
+
}
|
|
2511
|
+
toSvgPathString() {
|
|
2286
2512
|
const style = {
|
|
2287
2513
|
...this.style,
|
|
2288
2514
|
fill: this.style.fill ?? "#000",
|
|
@@ -2303,40 +2529,21 @@ class Path2D {
|
|
|
2303
2529
|
cssText += `${key}:${cssStyle[key]};`;
|
|
2304
2530
|
}
|
|
2305
2531
|
}
|
|
2306
|
-
return `<path d="${this.
|
|
2532
|
+
return `<path d="${this.toData()}" style="${cssText}"></path>`;
|
|
2307
2533
|
}
|
|
2308
|
-
|
|
2534
|
+
toSvgString() {
|
|
2309
2535
|
const { x, y, width, height } = this.getBoundingBox();
|
|
2310
|
-
const path = this.
|
|
2536
|
+
const path = this.toSvgPathString();
|
|
2311
2537
|
return `<svg viewBox="${x} ${y} ${width} ${height}" width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">${path}</svg>`;
|
|
2312
2538
|
}
|
|
2313
|
-
|
|
2314
|
-
return `data:image/svg+xml;base64,${btoa(this.
|
|
2315
|
-
}
|
|
2316
|
-
drawTo(ctx, style = {}) {
|
|
2317
|
-
style = { ...this.style, ...style };
|
|
2318
|
-
const { fill = "#000", stroke = "none" } = style;
|
|
2319
|
-
setCanvasContext(ctx, style);
|
|
2320
|
-
this.paths.forEach((path) => {
|
|
2321
|
-
path.drawTo(ctx);
|
|
2322
|
-
});
|
|
2323
|
-
if (fill !== "none") {
|
|
2324
|
-
ctx.fill();
|
|
2325
|
-
}
|
|
2326
|
-
if (stroke !== "none") {
|
|
2327
|
-
ctx.stroke();
|
|
2328
|
-
}
|
|
2329
|
-
}
|
|
2330
|
-
copy(source) {
|
|
2331
|
-
this.currentPath = source.currentPath.clone();
|
|
2332
|
-
this.paths = source.paths.map((path) => path.clone());
|
|
2333
|
-
this.style = { ...source.style };
|
|
2334
|
-
return this;
|
|
2539
|
+
toSvgUrl() {
|
|
2540
|
+
return `data:image/svg+xml;base64,${btoa(this.toSvgString())}`;
|
|
2335
2541
|
}
|
|
2336
2542
|
toSvg() {
|
|
2337
|
-
return new DOMParser().parseFromString(this.
|
|
2543
|
+
return new DOMParser().parseFromString(this.toSvgString(), "image/svg+xml").documentElement;
|
|
2338
2544
|
}
|
|
2339
|
-
toCanvas(
|
|
2545
|
+
toCanvas(options = {}) {
|
|
2546
|
+
const { pixelRatio = 2, ...style } = options;
|
|
2340
2547
|
const { left, top, width, height } = this.getBoundingBox();
|
|
2341
2548
|
const canvas = document.createElement("canvas");
|
|
2342
2549
|
canvas.width = width * pixelRatio;
|
|
@@ -2347,10 +2554,16 @@ class Path2D {
|
|
|
2347
2554
|
if (ctx) {
|
|
2348
2555
|
ctx.scale(pixelRatio, pixelRatio);
|
|
2349
2556
|
ctx.translate(-left, -top);
|
|
2350
|
-
this.drawTo(ctx);
|
|
2557
|
+
this.drawTo(ctx, style);
|
|
2351
2558
|
}
|
|
2352
2559
|
return canvas;
|
|
2353
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
|
+
}
|
|
2354
2567
|
clone() {
|
|
2355
2568
|
return new this.constructor().copy(this);
|
|
2356
2569
|
}
|
|
@@ -2827,7 +3040,7 @@ function parseNode(node, style, paths = []) {
|
|
|
2827
3040
|
const transformStack = [];
|
|
2828
3041
|
const transform = getNodeTransform(node, currentTransform, transformStack);
|
|
2829
3042
|
if (path) {
|
|
2830
|
-
path.
|
|
3043
|
+
path.matrix(currentTransform);
|
|
2831
3044
|
paths.push(path);
|
|
2832
3045
|
path.style = style;
|
|
2833
3046
|
}
|