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.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.
|
|
103
|
+
return Math.sqrt(this.lengthSquared());
|
|
73
104
|
}
|
|
74
|
-
|
|
75
|
-
this.x
|
|
76
|
-
this.y
|
|
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
|
-
|
|
80
|
-
|
|
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
|
-
|
|
88
|
-
|
|
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.
|
|
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
|
-
|
|
956
|
-
const
|
|
1022
|
+
getUToTMapping(u, distance) {
|
|
1023
|
+
const lengths = this.getLengths();
|
|
957
1024
|
let i = 0;
|
|
958
|
-
const
|
|
959
|
-
let
|
|
1025
|
+
const lengthsLen = lengths.length;
|
|
1026
|
+
let targetLength;
|
|
960
1027
|
if (distance) {
|
|
961
|
-
|
|
1028
|
+
targetLength = distance;
|
|
962
1029
|
} else {
|
|
963
|
-
|
|
1030
|
+
targetLength = u * lengths[lengthsLen - 1];
|
|
964
1031
|
}
|
|
965
1032
|
let low = 0;
|
|
966
|
-
let high =
|
|
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 =
|
|
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 (
|
|
982
|
-
return i / (
|
|
1048
|
+
if (lengths[i] === targetLength) {
|
|
1049
|
+
return i / (lengthsLen - 1);
|
|
983
1050
|
}
|
|
984
|
-
const lengthBefore =
|
|
985
|
-
const lengthAfter =
|
|
1051
|
+
const lengthBefore = lengths[i];
|
|
1052
|
+
const lengthAfter = lengths[i + 1];
|
|
986
1053
|
const segmentLength = lengthAfter - lengthBefore;
|
|
987
|
-
const segmentFraction = (
|
|
988
|
-
return (i + segmentFraction) / (
|
|
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.
|
|
1064
|
+
return this.getTangent(this.getUToTMapping(u), output);
|
|
998
1065
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
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
|
-
|
|
1005
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
1031
|
-
return pathCommandsToPathData(this.
|
|
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().
|
|
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
|
|
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
|
|
1162
|
+
return output.set(Math.cos(_t), Math.sin(_t));
|
|
1067
1163
|
}
|
|
1068
|
-
|
|
1069
|
-
|
|
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
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1309
|
-
|
|
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).
|
|
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
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1613
|
-
this.curves.
|
|
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
|
-
|
|
1621
|
-
return this.curves.flatMap((curve) => curve.
|
|
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
|
-
|
|
1685
|
-
this.curves.
|
|
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
|
-
|
|
1693
|
-
return this.curves.flatMap((curve) => curve.
|
|
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
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1833
|
-
this.curves.
|
|
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
|
-
|
|
1841
|
-
return this.curves.flatMap((curve) => curve.
|
|
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
|
-
|
|
1870
|
-
this.points
|
|
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
|
-
|
|
2120
|
-
return this.curves.flatMap((curve) => curve.
|
|
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
|
-
|
|
2251
|
-
this.paths.
|
|
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
|
-
|
|
2255
|
-
this.
|
|
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
|
-
|
|
2259
|
-
this.
|
|
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.
|
|
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
|
-
|
|
2278
|
-
|
|
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
|
-
|
|
2281
|
-
|
|
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
|
-
|
|
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.
|
|
2530
|
+
return `<path d="${this.toData()}" style="${cssText}"></path>`;
|
|
2305
2531
|
}
|
|
2306
|
-
|
|
2532
|
+
toSvgString() {
|
|
2307
2533
|
const { x, y, width, height } = this.getBoundingBox();
|
|
2308
|
-
const path = this.
|
|
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
|
-
|
|
2312
|
-
return `data:image/svg+xml;base64,${btoa(this.
|
|
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.
|
|
2541
|
+
return new DOMParser().parseFromString(this.toSvgString(), "image/svg+xml").documentElement;
|
|
2336
2542
|
}
|
|
2337
|
-
toCanvas(
|
|
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.
|
|
3041
|
+
path.matrix(currentTransform);
|
|
2829
3042
|
paths.push(path);
|
|
2830
3043
|
path.style = style;
|
|
2831
3044
|
}
|