modern-path2d 1.4.0 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1671 -1645
- package/dist/index.d.cts +4 -3
- package/dist/index.d.mts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -2
- package/dist/index.mjs +1669 -1644
- package/package.json +8 -8
package/dist/index.mjs
CHANGED
|
@@ -1002,272 +1002,825 @@ function svgPathDataToCommands(data) {
|
|
|
1002
1002
|
return commands;
|
|
1003
1003
|
}
|
|
1004
1004
|
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
return 3 * k * k * t * p;
|
|
1020
|
-
}
|
|
1021
|
-
function cubicBezierP2(t, p) {
|
|
1022
|
-
return 3 * (1 - t) * t * t * p;
|
|
1023
|
-
}
|
|
1024
|
-
function cubicBezierP3(t, p) {
|
|
1025
|
-
return t * t * t * p;
|
|
1026
|
-
}
|
|
1027
|
-
function cubicBezier(t, p0, p1, p2, p3) {
|
|
1028
|
-
return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
function fillTriangulate(pointArray, options = {}) {
|
|
1032
|
-
let {
|
|
1033
|
-
vertices = [],
|
|
1034
|
-
indices = [],
|
|
1035
|
-
holes = [],
|
|
1036
|
-
verticesStride = 2,
|
|
1037
|
-
verticesOffset = vertices.length / verticesStride,
|
|
1038
|
-
indicesOffset = indices.length
|
|
1039
|
-
} = options;
|
|
1040
|
-
const triangles = earcut(pointArray, holes, 2);
|
|
1041
|
-
if (triangles.length) {
|
|
1042
|
-
for (let i = 0; i < triangles.length; i += 3) {
|
|
1043
|
-
indices[indicesOffset++] = triangles[i] + verticesOffset;
|
|
1044
|
-
indices[indicesOffset++] = triangles[i + 1] + verticesOffset;
|
|
1045
|
-
indices[indicesOffset++] = triangles[i + 2] + verticesOffset;
|
|
1005
|
+
const dataUri = "data:image/svg+xml;";
|
|
1006
|
+
const base64DataUri = `${dataUri}base64,`;
|
|
1007
|
+
const utf8DataUri = `${dataUri}charset=utf8,`;
|
|
1008
|
+
function svgToDom(svg) {
|
|
1009
|
+
if (typeof svg === "string") {
|
|
1010
|
+
let xml;
|
|
1011
|
+
if (svg.startsWith(base64DataUri)) {
|
|
1012
|
+
svg = svg.substring(base64DataUri.length, svg.length);
|
|
1013
|
+
xml = atob(svg);
|
|
1014
|
+
} else if (svg.startsWith(utf8DataUri)) {
|
|
1015
|
+
svg = svg.substring(utf8DataUri.length, svg.length);
|
|
1016
|
+
xml = decodeURIComponent(svg);
|
|
1017
|
+
} else {
|
|
1018
|
+
xml = svg;
|
|
1046
1019
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1020
|
+
const doc = new DOMParser().parseFromString(xml, "text/xml");
|
|
1021
|
+
const error = doc.querySelector("parsererror");
|
|
1022
|
+
if (error) {
|
|
1023
|
+
throw new Error(`${error.textContent ?? "parser error"}
|
|
1024
|
+
${xml}`);
|
|
1052
1025
|
}
|
|
1026
|
+
return doc.documentElement;
|
|
1027
|
+
} else {
|
|
1028
|
+
return svg;
|
|
1053
1029
|
}
|
|
1054
|
-
return {
|
|
1055
|
-
vertices,
|
|
1056
|
-
indices
|
|
1057
|
-
};
|
|
1058
1030
|
}
|
|
1059
1031
|
|
|
1060
|
-
const
|
|
1061
|
-
const
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
0.
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
points.push(x1234, y1234);
|
|
1107
|
-
return;
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
} else if (d3 > FLT_EPSILON$1) {
|
|
1111
|
-
if (d3 * d3 <= distanceTolerance * (dx * dx + dy * dy)) {
|
|
1112
|
-
{
|
|
1113
|
-
points.push(x1234, y1234);
|
|
1114
|
-
return;
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
} else {
|
|
1118
|
-
dx = x1234 - (x1 + x4) / 2;
|
|
1119
|
-
dy = y1234 - (y1 + y4) / 2;
|
|
1120
|
-
if (dx * dx + dy * dy <= distanceTolerance) {
|
|
1121
|
-
points.push(x1234, y1234);
|
|
1122
|
-
return;
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1032
|
+
const defaultUnit = "px";
|
|
1033
|
+
const defaultDPI = 90;
|
|
1034
|
+
const units = ["mm", "cm", "in", "pt", "pc", "px"];
|
|
1035
|
+
const unitConversion = {
|
|
1036
|
+
mm: {
|
|
1037
|
+
mm: 1,
|
|
1038
|
+
cm: 0.1,
|
|
1039
|
+
in: 1 / 25.4,
|
|
1040
|
+
pt: 72 / 25.4,
|
|
1041
|
+
pc: 6 / 25.4,
|
|
1042
|
+
px: -1
|
|
1043
|
+
},
|
|
1044
|
+
cm: {
|
|
1045
|
+
mm: 10,
|
|
1046
|
+
cm: 1,
|
|
1047
|
+
in: 1 / 2.54,
|
|
1048
|
+
pt: 72 / 2.54,
|
|
1049
|
+
pc: 6 / 2.54,
|
|
1050
|
+
px: -1
|
|
1051
|
+
},
|
|
1052
|
+
in: {
|
|
1053
|
+
mm: 25.4,
|
|
1054
|
+
cm: 2.54,
|
|
1055
|
+
in: 1,
|
|
1056
|
+
pt: 72,
|
|
1057
|
+
pc: 6,
|
|
1058
|
+
px: -1
|
|
1059
|
+
},
|
|
1060
|
+
pt: {
|
|
1061
|
+
mm: 25.4 / 72,
|
|
1062
|
+
cm: 2.54 / 72,
|
|
1063
|
+
in: 1 / 72,
|
|
1064
|
+
pt: 1,
|
|
1065
|
+
pc: 6 / 72,
|
|
1066
|
+
px: -1
|
|
1067
|
+
},
|
|
1068
|
+
pc: {
|
|
1069
|
+
mm: 25.4 / 6,
|
|
1070
|
+
cm: 2.54 / 6,
|
|
1071
|
+
in: 1 / 6,
|
|
1072
|
+
pt: 72 / 6,
|
|
1073
|
+
pc: 1,
|
|
1074
|
+
px: -1
|
|
1075
|
+
},
|
|
1076
|
+
px: {
|
|
1077
|
+
px: 1
|
|
1125
1078
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
const
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
0.99,
|
|
1137
|
-
// a value of 1.0 actually inverts smoothing, so we cap it at 0.99
|
|
1138
|
-
Math.max(0, smoothness)
|
|
1139
|
-
);
|
|
1140
|
-
let distanceTolerance = (PATH_DISTANCE_EPSILON - smoothing) / scale;
|
|
1141
|
-
distanceTolerance *= distanceTolerance;
|
|
1142
|
-
recursive(points, sX, sY, x1, y1, x, y, distanceTolerance, 0);
|
|
1143
|
-
points.push(x, y);
|
|
1144
|
-
return points;
|
|
1145
|
-
}
|
|
1146
|
-
function recursive(points, x1, y1, x2, y2, x3, y3, distanceTolerance, level) {
|
|
1147
|
-
if (level > RECURSION_LIMIT)
|
|
1148
|
-
return;
|
|
1149
|
-
const x12 = (x1 + x2) / 2;
|
|
1150
|
-
const y12 = (y1 + y2) / 2;
|
|
1151
|
-
const x23 = (x2 + x3) / 2;
|
|
1152
|
-
const y23 = (y2 + y3) / 2;
|
|
1153
|
-
const x123 = (x12 + x23) / 2;
|
|
1154
|
-
const y123 = (y12 + y23) / 2;
|
|
1155
|
-
let dx = x3 - x1;
|
|
1156
|
-
let dy = y3 - y1;
|
|
1157
|
-
const d = Math.abs((x2 - x3) * dy - (y2 - y3) * dx);
|
|
1158
|
-
if (d > FLT_EPSILON) {
|
|
1159
|
-
if (d * d <= distanceTolerance * (dx * dx + dy * dy)) {
|
|
1160
|
-
{
|
|
1161
|
-
points.push(x123, y123);
|
|
1162
|
-
return;
|
|
1079
|
+
};
|
|
1080
|
+
function parseFloatWithUnits(string) {
|
|
1081
|
+
let theUnit = "px";
|
|
1082
|
+
if (typeof string === "string") {
|
|
1083
|
+
for (let i = 0, n = units.length; i < n; i++) {
|
|
1084
|
+
const u = units[i];
|
|
1085
|
+
if (string.endsWith(u)) {
|
|
1086
|
+
theUnit = u;
|
|
1087
|
+
string = string.substring(0, string.length - u.length);
|
|
1088
|
+
break;
|
|
1163
1089
|
}
|
|
1164
1090
|
}
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1091
|
+
}
|
|
1092
|
+
let scale;
|
|
1093
|
+
{
|
|
1094
|
+
scale = unitConversion[theUnit][defaultUnit];
|
|
1095
|
+
if (scale < 0) {
|
|
1096
|
+
scale = unitConversion[theUnit].in * defaultDPI;
|
|
1171
1097
|
}
|
|
1172
1098
|
}
|
|
1173
|
-
|
|
1174
|
-
recursive(points, x123, y123, x23, y23, x3, y3, distanceTolerance, level + 1);
|
|
1099
|
+
return scale * Number.parseFloat(string);
|
|
1175
1100
|
}
|
|
1176
1101
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1102
|
+
const tempTransform0$1 = new Matrix3();
|
|
1103
|
+
const tempTransform1$1 = new Matrix3();
|
|
1104
|
+
const tempTransform2$1 = new Matrix3();
|
|
1105
|
+
const tempTransform3 = new Matrix3();
|
|
1106
|
+
function getNodeTransform(node, currentTransform, transformStack) {
|
|
1107
|
+
if (!(node.hasAttribute("transform") || node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y")))) {
|
|
1108
|
+
return null;
|
|
1109
|
+
}
|
|
1110
|
+
const transform = parseNodeTransform(node);
|
|
1111
|
+
if (transformStack.length > 0) {
|
|
1112
|
+
transform.premultiply(transformStack[transformStack.length - 1]);
|
|
1113
|
+
}
|
|
1114
|
+
currentTransform.copy(transform);
|
|
1115
|
+
transformStack.push(transform);
|
|
1116
|
+
return transform;
|
|
1117
|
+
}
|
|
1118
|
+
function parseNodeTransform(node) {
|
|
1119
|
+
const transform = new Matrix3();
|
|
1120
|
+
const currentTransform = tempTransform0$1;
|
|
1121
|
+
if (node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y"))) {
|
|
1122
|
+
transform.translate(
|
|
1123
|
+
parseFloatWithUnits(node.getAttribute("x")),
|
|
1124
|
+
parseFloatWithUnits(node.getAttribute("y"))
|
|
1125
|
+
);
|
|
1126
|
+
}
|
|
1127
|
+
if (node.hasAttribute("transform")) {
|
|
1128
|
+
const transformsTexts = node.getAttribute("transform").split(")");
|
|
1129
|
+
for (let tIndex = transformsTexts.length - 1; tIndex >= 0; tIndex--) {
|
|
1130
|
+
const transformText = transformsTexts[tIndex].trim();
|
|
1131
|
+
if (transformText === "")
|
|
1132
|
+
continue;
|
|
1133
|
+
const openParPos = transformText.indexOf("(");
|
|
1134
|
+
const closeParPos = transformText.length;
|
|
1135
|
+
if (openParPos > 0 && openParPos < closeParPos) {
|
|
1136
|
+
const transformType = transformText.slice(0, openParPos);
|
|
1137
|
+
const array = parsePathDataArgs(transformText.slice(openParPos + 1));
|
|
1138
|
+
currentTransform.identity();
|
|
1139
|
+
switch (transformType) {
|
|
1140
|
+
case "translate":
|
|
1141
|
+
if (array.length >= 1) {
|
|
1142
|
+
const tx = array[0];
|
|
1143
|
+
let ty = 0;
|
|
1144
|
+
if (array.length >= 2) {
|
|
1145
|
+
ty = array[1];
|
|
1146
|
+
}
|
|
1147
|
+
currentTransform.translate(tx, ty);
|
|
1148
|
+
}
|
|
1149
|
+
break;
|
|
1150
|
+
case "rotate":
|
|
1151
|
+
if (array.length >= 1) {
|
|
1152
|
+
let angle = 0;
|
|
1153
|
+
let cx = 0;
|
|
1154
|
+
let cy = 0;
|
|
1155
|
+
angle = array[0] * Math.PI / 180;
|
|
1156
|
+
if (array.length >= 3) {
|
|
1157
|
+
cx = array[1];
|
|
1158
|
+
cy = array[2];
|
|
1159
|
+
}
|
|
1160
|
+
tempTransform1$1.makeTranslation(-cx, -cy);
|
|
1161
|
+
tempTransform2$1.makeRotation(angle);
|
|
1162
|
+
tempTransform3.multiplyMatrices(tempTransform2$1, tempTransform1$1);
|
|
1163
|
+
tempTransform1$1.makeTranslation(cx, cy);
|
|
1164
|
+
currentTransform.multiplyMatrices(tempTransform1$1, tempTransform3);
|
|
1165
|
+
}
|
|
1166
|
+
break;
|
|
1167
|
+
case "scale":
|
|
1168
|
+
if (array.length >= 1) {
|
|
1169
|
+
currentTransform.scale(
|
|
1170
|
+
array[0],
|
|
1171
|
+
array[1] ?? array[0]
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
break;
|
|
1175
|
+
case "skewX":
|
|
1176
|
+
if (array.length === 1) {
|
|
1177
|
+
currentTransform.set(
|
|
1178
|
+
1,
|
|
1179
|
+
Math.tan(array[0] * Math.PI / 180),
|
|
1180
|
+
0,
|
|
1181
|
+
0,
|
|
1182
|
+
1,
|
|
1183
|
+
0,
|
|
1184
|
+
0,
|
|
1185
|
+
0,
|
|
1186
|
+
1
|
|
1187
|
+
);
|
|
1188
|
+
}
|
|
1189
|
+
break;
|
|
1190
|
+
case "skewY":
|
|
1191
|
+
if (array.length === 1) {
|
|
1192
|
+
currentTransform.set(
|
|
1193
|
+
1,
|
|
1194
|
+
0,
|
|
1195
|
+
0,
|
|
1196
|
+
Math.tan(array[0] * Math.PI / 180),
|
|
1197
|
+
1,
|
|
1198
|
+
0,
|
|
1199
|
+
0,
|
|
1200
|
+
0,
|
|
1201
|
+
1
|
|
1202
|
+
);
|
|
1203
|
+
}
|
|
1204
|
+
break;
|
|
1205
|
+
case "matrix":
|
|
1206
|
+
if (array.length === 6) {
|
|
1207
|
+
currentTransform.set(
|
|
1208
|
+
array[0],
|
|
1209
|
+
array[2],
|
|
1210
|
+
array[4],
|
|
1211
|
+
array[1],
|
|
1212
|
+
array[3],
|
|
1213
|
+
array[5],
|
|
1214
|
+
0,
|
|
1215
|
+
0,
|
|
1216
|
+
1
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
break;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
transform.premultiply(currentTransform);
|
|
1201
1223
|
}
|
|
1202
1224
|
}
|
|
1203
|
-
return
|
|
1225
|
+
return transform;
|
|
1204
1226
|
}
|
|
1205
1227
|
|
|
1206
|
-
function
|
|
1207
|
-
|
|
1208
|
-
|
|
1228
|
+
function parseCircleNode(node) {
|
|
1229
|
+
return new Path2D().arc(
|
|
1230
|
+
parseFloatWithUnits(node.getAttribute("cx") || 0),
|
|
1231
|
+
parseFloatWithUnits(node.getAttribute("cy") || 0),
|
|
1232
|
+
parseFloatWithUnits(node.getAttribute("r") || 0),
|
|
1233
|
+
0,
|
|
1234
|
+
Math.PI * 2
|
|
1235
|
+
);
|
|
1209
1236
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1237
|
+
|
|
1238
|
+
function parseCSSStylesheet(node, stylesheets) {
|
|
1239
|
+
if (!node.sheet || !node.sheet.cssRules || !node.sheet.cssRules.length)
|
|
1240
|
+
return;
|
|
1241
|
+
for (let i = 0; i < node.sheet.cssRules.length; i++) {
|
|
1242
|
+
const stylesheet = node.sheet.cssRules[i];
|
|
1243
|
+
if (stylesheet.type !== 1)
|
|
1244
|
+
continue;
|
|
1245
|
+
const selectorList = stylesheet.selectorText.split(/,/g).filter(Boolean).map((i2) => i2.trim());
|
|
1246
|
+
const definitions = {};
|
|
1247
|
+
for (let len = stylesheet.style.length, i2 = 0; i2 < len; i2++) {
|
|
1248
|
+
const name = stylesheet.style.item(i2);
|
|
1249
|
+
definitions[name] = stylesheet.style.getPropertyValue(name);
|
|
1250
|
+
}
|
|
1251
|
+
for (let j = 0; j < selectorList.length; j++) {
|
|
1252
|
+
stylesheets[selectorList[j]] = Object.assign(
|
|
1253
|
+
stylesheets[selectorList[j]] || {},
|
|
1254
|
+
{ ...definitions }
|
|
1255
|
+
);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1212
1258
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1259
|
+
|
|
1260
|
+
function parseEllipseNode(node) {
|
|
1261
|
+
return new Path2D().ellipse(
|
|
1262
|
+
parseFloatWithUnits(node.getAttribute("cx") || 0),
|
|
1263
|
+
parseFloatWithUnits(node.getAttribute("cy") || 0),
|
|
1264
|
+
parseFloatWithUnits(node.getAttribute("rx") || 0),
|
|
1265
|
+
parseFloatWithUnits(node.getAttribute("ry") || 0),
|
|
1266
|
+
0,
|
|
1267
|
+
0,
|
|
1268
|
+
Math.PI * 2
|
|
1269
|
+
);
|
|
1215
1270
|
}
|
|
1216
|
-
|
|
1217
|
-
|
|
1271
|
+
|
|
1272
|
+
function parseLineNode(node) {
|
|
1273
|
+
return new Path2D().moveTo(
|
|
1274
|
+
parseFloatWithUnits(node.getAttribute("x1") || 0),
|
|
1275
|
+
parseFloatWithUnits(node.getAttribute("y1") || 0)
|
|
1276
|
+
).lineTo(
|
|
1277
|
+
parseFloatWithUnits(node.getAttribute("x2") || 0),
|
|
1278
|
+
parseFloatWithUnits(node.getAttribute("y2") || 0)
|
|
1279
|
+
);
|
|
1218
1280
|
}
|
|
1219
1281
|
|
|
1220
|
-
|
|
1221
|
-
const
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
const style = lineStyle;
|
|
1241
|
-
let alignment = style.alignment;
|
|
1242
|
-
if (lineStyle.alignment !== 0.5) {
|
|
1243
|
-
let orientation = getOrientationOfPoints(points);
|
|
1244
|
-
if (flipAlignment)
|
|
1245
|
-
orientation *= -1;
|
|
1246
|
-
alignment = (alignment - 0.5) * orientation + 0.5;
|
|
1247
|
-
}
|
|
1248
|
-
const firstPoint = { x: points[0], y: points[1] };
|
|
1249
|
-
const lastPoint = { x: points[points.length - 2], y: points[points.length - 1] };
|
|
1250
|
-
const closedShape = closed;
|
|
1251
|
-
const closedPath = Math.abs(firstPoint.x - lastPoint.x) < eps && Math.abs(firstPoint.y - lastPoint.y) < eps;
|
|
1252
|
-
if (closedShape) {
|
|
1253
|
-
points = points.slice();
|
|
1254
|
-
if (closedPath) {
|
|
1255
|
-
points.pop();
|
|
1256
|
-
points.pop();
|
|
1257
|
-
lastPoint.x = points[points.length - 2];
|
|
1258
|
-
lastPoint.y = points[points.length - 1];
|
|
1282
|
+
function parsePathNode(node) {
|
|
1283
|
+
const path = new Path2D();
|
|
1284
|
+
const d = node.getAttribute("d");
|
|
1285
|
+
if (!d || d === "none")
|
|
1286
|
+
return null;
|
|
1287
|
+
path.addData(d);
|
|
1288
|
+
return path;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const RE$1 = /([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
|
|
1292
|
+
function parsePolygonNode(node) {
|
|
1293
|
+
const path = new Path2D();
|
|
1294
|
+
let index = 0;
|
|
1295
|
+
node.getAttribute("points")?.replace(RE$1, (match, a, b) => {
|
|
1296
|
+
const x = parseFloatWithUnits(a);
|
|
1297
|
+
const y = parseFloatWithUnits(b);
|
|
1298
|
+
if (index === 0) {
|
|
1299
|
+
path.moveTo(x, y);
|
|
1300
|
+
} else {
|
|
1301
|
+
path.lineTo(x, y);
|
|
1259
1302
|
}
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
const
|
|
1270
|
-
|
|
1303
|
+
index++;
|
|
1304
|
+
return match;
|
|
1305
|
+
});
|
|
1306
|
+
path.currentCurve.autoClose = true;
|
|
1307
|
+
return path;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
const RE = /([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
|
|
1311
|
+
function parsePolylineNode(node) {
|
|
1312
|
+
const path = new Path2D();
|
|
1313
|
+
let index = 0;
|
|
1314
|
+
node.getAttribute("points")?.replace(RE, (match, a, b) => {
|
|
1315
|
+
const x = parseFloatWithUnits(a);
|
|
1316
|
+
const y = parseFloatWithUnits(b);
|
|
1317
|
+
if (index === 0) {
|
|
1318
|
+
path.moveTo(x, y);
|
|
1319
|
+
} else {
|
|
1320
|
+
path.lineTo(x, y);
|
|
1321
|
+
}
|
|
1322
|
+
index++;
|
|
1323
|
+
return match;
|
|
1324
|
+
});
|
|
1325
|
+
path.currentCurve.autoClose = false;
|
|
1326
|
+
return path;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
function parseRectNode(node) {
|
|
1330
|
+
const x = parseFloatWithUnits(node.getAttribute("x") || 0);
|
|
1331
|
+
const y = parseFloatWithUnits(node.getAttribute("y") || 0);
|
|
1332
|
+
const rx = parseFloatWithUnits(node.getAttribute("rx") || node.getAttribute("ry") || 0);
|
|
1333
|
+
const ry = parseFloatWithUnits(node.getAttribute("ry") || node.getAttribute("rx") || 0);
|
|
1334
|
+
const w = parseFloatWithUnits(node.getAttribute("width"));
|
|
1335
|
+
const h = parseFloatWithUnits(node.getAttribute("height"));
|
|
1336
|
+
const bci = 1 - 0.551915024494;
|
|
1337
|
+
const path = new Path2D();
|
|
1338
|
+
path.moveTo(x + rx, y);
|
|
1339
|
+
path.lineTo(x + w - rx, y);
|
|
1340
|
+
if (rx !== 0 || ry !== 0) {
|
|
1341
|
+
path.bezierCurveTo(
|
|
1342
|
+
x + w - rx * bci,
|
|
1343
|
+
y,
|
|
1344
|
+
x + w,
|
|
1345
|
+
y + ry * bci,
|
|
1346
|
+
x + w,
|
|
1347
|
+
y + ry
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
path.lineTo(x + w, y + h - ry);
|
|
1351
|
+
if (rx !== 0 || ry !== 0) {
|
|
1352
|
+
path.bezierCurveTo(
|
|
1353
|
+
x + w,
|
|
1354
|
+
y + h - ry * bci,
|
|
1355
|
+
x + w - rx * bci,
|
|
1356
|
+
y + h,
|
|
1357
|
+
x + w - rx,
|
|
1358
|
+
y + h
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
path.lineTo(x + rx, y + h);
|
|
1362
|
+
if (rx !== 0 || ry !== 0) {
|
|
1363
|
+
path.bezierCurveTo(
|
|
1364
|
+
x + rx * bci,
|
|
1365
|
+
y + h,
|
|
1366
|
+
x,
|
|
1367
|
+
y + h - ry * bci,
|
|
1368
|
+
x,
|
|
1369
|
+
y + h - ry
|
|
1370
|
+
);
|
|
1371
|
+
}
|
|
1372
|
+
path.lineTo(x, y + ry);
|
|
1373
|
+
if (rx !== 0 || ry !== 0) {
|
|
1374
|
+
path.bezierCurveTo(x, y + ry * bci, x + rx * bci, y, x + rx, y);
|
|
1375
|
+
}
|
|
1376
|
+
return path;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
function parseStyle(node, style, stylesheets) {
|
|
1380
|
+
style = Object.assign({}, style);
|
|
1381
|
+
let stylesheetStyles = {};
|
|
1382
|
+
if (node.hasAttribute("class")) {
|
|
1383
|
+
const classSelectors = node.getAttribute("class").split(/\s/).filter(Boolean).map((i) => i.trim());
|
|
1384
|
+
for (let i = 0; i < classSelectors.length; i++) {
|
|
1385
|
+
stylesheetStyles = Object.assign(stylesheetStyles, stylesheets[`.${classSelectors[i]}`]);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
if (node.hasAttribute("id")) {
|
|
1389
|
+
stylesheetStyles = Object.assign(stylesheetStyles, stylesheets[`#${node.getAttribute("id")}`]);
|
|
1390
|
+
}
|
|
1391
|
+
for (let len = node.style.length, i = 0; i < len; i++) {
|
|
1392
|
+
const name = node.style.item(i);
|
|
1393
|
+
const value = node.style.getPropertyValue(name);
|
|
1394
|
+
style[name] = value;
|
|
1395
|
+
stylesheetStyles[name] = value;
|
|
1396
|
+
}
|
|
1397
|
+
function addStyle(svgName, jsName, adjustFunction = copy) {
|
|
1398
|
+
if (node.hasAttribute(svgName))
|
|
1399
|
+
style[jsName] = adjustFunction(node.getAttribute(svgName));
|
|
1400
|
+
if (stylesheetStyles[svgName])
|
|
1401
|
+
style[jsName] = adjustFunction(stylesheetStyles[svgName]);
|
|
1402
|
+
}
|
|
1403
|
+
function copy(v) {
|
|
1404
|
+
if (v.startsWith("url"))
|
|
1405
|
+
console.warn("url access in attributes is not implemented.");
|
|
1406
|
+
return v;
|
|
1407
|
+
}
|
|
1408
|
+
function clamp(v) {
|
|
1409
|
+
return Math.max(0, Math.min(1, parseFloatWithUnits(v)));
|
|
1410
|
+
}
|
|
1411
|
+
function positive(v) {
|
|
1412
|
+
return Math.max(0, parseFloatWithUnits(v));
|
|
1413
|
+
}
|
|
1414
|
+
function array(v) {
|
|
1415
|
+
return v.split(" ").filter((v2) => v2 !== "").map((v2) => parseFloatWithUnits(v2));
|
|
1416
|
+
}
|
|
1417
|
+
addStyle("fill", "fill");
|
|
1418
|
+
addStyle("fill-opacity", "fillOpacity", clamp);
|
|
1419
|
+
addStyle("fill-rule", "fillRule");
|
|
1420
|
+
addStyle("opacity", "opacity", clamp);
|
|
1421
|
+
addStyle("stroke", "stroke");
|
|
1422
|
+
addStyle("stroke-opacity", "strokeOpacity", clamp);
|
|
1423
|
+
addStyle("stroke-width", "strokeWidth", positive);
|
|
1424
|
+
addStyle("stroke-linecap", "strokeLinecap");
|
|
1425
|
+
addStyle("stroke-linejoin", "strokeLinejoin");
|
|
1426
|
+
addStyle("stroke-miterlimit", "strokeMiterlimit", positive);
|
|
1427
|
+
addStyle("stroke-dasharray", "strokeDasharray", array);
|
|
1428
|
+
addStyle("stroke-dashoffset", "strokeDashoffset", parseFloatWithUnits);
|
|
1429
|
+
addStyle("visibility", "visibility");
|
|
1430
|
+
return style;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
function parseNode(node, style, paths = [], stylesheets = {}) {
|
|
1434
|
+
if (node.nodeType !== 1)
|
|
1435
|
+
return paths;
|
|
1436
|
+
let isDefsNode = false;
|
|
1437
|
+
let path = null;
|
|
1438
|
+
let _style = { ...style };
|
|
1439
|
+
switch (node.nodeName) {
|
|
1440
|
+
case "svg":
|
|
1441
|
+
_style = parseStyle(node, _style, stylesheets);
|
|
1442
|
+
break;
|
|
1443
|
+
case "style":
|
|
1444
|
+
parseCSSStylesheet(node, stylesheets);
|
|
1445
|
+
break;
|
|
1446
|
+
case "g":
|
|
1447
|
+
_style = parseStyle(node, _style, stylesheets);
|
|
1448
|
+
break;
|
|
1449
|
+
case "path":
|
|
1450
|
+
_style = parseStyle(node, _style, stylesheets);
|
|
1451
|
+
if (node.hasAttribute("d"))
|
|
1452
|
+
path = parsePathNode(node);
|
|
1453
|
+
break;
|
|
1454
|
+
case "rect":
|
|
1455
|
+
_style = parseStyle(node, _style, stylesheets);
|
|
1456
|
+
path = parseRectNode(node);
|
|
1457
|
+
break;
|
|
1458
|
+
case "polygon":
|
|
1459
|
+
_style = parseStyle(node, _style, stylesheets);
|
|
1460
|
+
path = parsePolygonNode(node);
|
|
1461
|
+
break;
|
|
1462
|
+
case "polyline":
|
|
1463
|
+
_style = parseStyle(node, _style, stylesheets);
|
|
1464
|
+
path = parsePolylineNode(node);
|
|
1465
|
+
break;
|
|
1466
|
+
case "circle":
|
|
1467
|
+
_style = parseStyle(node, _style, stylesheets);
|
|
1468
|
+
path = parseCircleNode(node);
|
|
1469
|
+
break;
|
|
1470
|
+
case "ellipse":
|
|
1471
|
+
_style = parseStyle(node, _style, stylesheets);
|
|
1472
|
+
path = parseEllipseNode(node);
|
|
1473
|
+
break;
|
|
1474
|
+
case "line":
|
|
1475
|
+
_style = parseStyle(node, _style, stylesheets);
|
|
1476
|
+
path = parseLineNode(node);
|
|
1477
|
+
break;
|
|
1478
|
+
case "defs":
|
|
1479
|
+
isDefsNode = true;
|
|
1480
|
+
break;
|
|
1481
|
+
case "use": {
|
|
1482
|
+
_style = parseStyle(node, _style, stylesheets);
|
|
1483
|
+
const href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href") || node.getAttribute("href") || "";
|
|
1484
|
+
const usedNodeId = href.substring(1);
|
|
1485
|
+
const usedNode = node.viewportElement?.getElementById(usedNodeId);
|
|
1486
|
+
if (usedNode) {
|
|
1487
|
+
parseNode(usedNode, _style, paths, stylesheets);
|
|
1488
|
+
} else {
|
|
1489
|
+
console.warn(`'use node' references non-existent node id: ${usedNodeId}`);
|
|
1490
|
+
}
|
|
1491
|
+
break;
|
|
1492
|
+
}
|
|
1493
|
+
default:
|
|
1494
|
+
console.warn(node);
|
|
1495
|
+
break;
|
|
1496
|
+
}
|
|
1497
|
+
if (_style.display === "none") {
|
|
1498
|
+
return paths;
|
|
1499
|
+
}
|
|
1500
|
+
const currentTransform = new Matrix3();
|
|
1501
|
+
const transformStack = [];
|
|
1502
|
+
const transform = getNodeTransform(node, currentTransform, transformStack);
|
|
1503
|
+
if (path) {
|
|
1504
|
+
path.applyTransform(currentTransform);
|
|
1505
|
+
paths.push(path);
|
|
1506
|
+
path.style = { ..._style };
|
|
1507
|
+
}
|
|
1508
|
+
const childNodes = node.childNodes;
|
|
1509
|
+
for (let i = 0, len = childNodes.length; i < len; i++) {
|
|
1510
|
+
const node2 = childNodes[i];
|
|
1511
|
+
if (isDefsNode && node2.nodeName !== "style" && node2.nodeName !== "defs")
|
|
1512
|
+
continue;
|
|
1513
|
+
parseNode(node2, _style, paths, stylesheets);
|
|
1514
|
+
}
|
|
1515
|
+
if (transform) {
|
|
1516
|
+
transformStack.pop();
|
|
1517
|
+
if (transformStack.length > 0) {
|
|
1518
|
+
currentTransform.copy(transformStack[transformStack.length - 1]);
|
|
1519
|
+
} else {
|
|
1520
|
+
currentTransform.identity();
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
return paths;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
function svgToPath2DSet(svg) {
|
|
1527
|
+
const dom = svgToDom(svg);
|
|
1528
|
+
return new Path2DSet(
|
|
1529
|
+
parseNode(dom, {}),
|
|
1530
|
+
dom.getAttribute("viewBox")?.trim().split(" ").map((v) => Number(v))
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
function catmullRom(t, p0, p1, p2, p3) {
|
|
1535
|
+
const v0 = (p2 - p0) * 0.5;
|
|
1536
|
+
const v1 = (p3 - p1) * 0.5;
|
|
1537
|
+
const t2 = t * t;
|
|
1538
|
+
const t3 = t * t2;
|
|
1539
|
+
return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
function cubicBezierP0(t, p) {
|
|
1543
|
+
const k = 1 - t;
|
|
1544
|
+
return k * k * k * p;
|
|
1545
|
+
}
|
|
1546
|
+
function cubicBezierP1(t, p) {
|
|
1547
|
+
const k = 1 - t;
|
|
1548
|
+
return 3 * k * k * t * p;
|
|
1549
|
+
}
|
|
1550
|
+
function cubicBezierP2(t, p) {
|
|
1551
|
+
return 3 * (1 - t) * t * t * p;
|
|
1552
|
+
}
|
|
1553
|
+
function cubicBezierP3(t, p) {
|
|
1554
|
+
return t * t * t * p;
|
|
1555
|
+
}
|
|
1556
|
+
function cubicBezier(t, p0, p1, p2, p3) {
|
|
1557
|
+
return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
function fillTriangulate(pointArray, options = {}) {
|
|
1561
|
+
let {
|
|
1562
|
+
vertices = [],
|
|
1563
|
+
indices = [],
|
|
1564
|
+
holes = [],
|
|
1565
|
+
verticesStride = 2,
|
|
1566
|
+
verticesOffset = vertices.length / verticesStride,
|
|
1567
|
+
indicesOffset = indices.length
|
|
1568
|
+
} = options;
|
|
1569
|
+
const triangles = earcut(pointArray, holes, 2);
|
|
1570
|
+
if (triangles.length) {
|
|
1571
|
+
for (let i = 0; i < triangles.length; i += 3) {
|
|
1572
|
+
indices[indicesOffset++] = triangles[i] + verticesOffset;
|
|
1573
|
+
indices[indicesOffset++] = triangles[i + 1] + verticesOffset;
|
|
1574
|
+
indices[indicesOffset++] = triangles[i + 2] + verticesOffset;
|
|
1575
|
+
}
|
|
1576
|
+
let index = verticesOffset * verticesStride;
|
|
1577
|
+
for (let i = 0; i < pointArray.length; i += 2) {
|
|
1578
|
+
vertices[index] = pointArray[i];
|
|
1579
|
+
vertices[index + 1] = pointArray[i + 1];
|
|
1580
|
+
index += verticesStride;
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
return {
|
|
1584
|
+
vertices,
|
|
1585
|
+
indices
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
const RECURSION_LIMIT$1 = 8;
|
|
1590
|
+
const FLT_EPSILON$1 = 11920929e-14;
|
|
1591
|
+
const PATH_DISTANCE_EPSILON$1 = 1;
|
|
1592
|
+
function getAdaptiveCubicBezierCurvePoints(sX, sY, x1, y1, x2, y2, x, y, smoothness = 0.5, points = []) {
|
|
1593
|
+
const scale = 1;
|
|
1594
|
+
const smoothing = Math.min(
|
|
1595
|
+
0.99,
|
|
1596
|
+
// a value of 1.0 actually inverts smoothing, so we cap it at 0.99
|
|
1597
|
+
Math.max(0, smoothness)
|
|
1598
|
+
);
|
|
1599
|
+
let distanceTolerance = (PATH_DISTANCE_EPSILON$1 - smoothing) / scale;
|
|
1600
|
+
distanceTolerance *= distanceTolerance;
|
|
1601
|
+
recursive$1(sX, sY, x1, y1, x2, y2, x, y, points, distanceTolerance, 0);
|
|
1602
|
+
points.push(x, y);
|
|
1603
|
+
return points;
|
|
1604
|
+
}
|
|
1605
|
+
function recursive$1(x1, y1, x2, y2, x3, y3, x4, y4, points, distanceTolerance, level) {
|
|
1606
|
+
if (level > RECURSION_LIMIT$1)
|
|
1607
|
+
return;
|
|
1608
|
+
const x12 = (x1 + x2) / 2;
|
|
1609
|
+
const y12 = (y1 + y2) / 2;
|
|
1610
|
+
const x23 = (x2 + x3) / 2;
|
|
1611
|
+
const y23 = (y2 + y3) / 2;
|
|
1612
|
+
const x34 = (x3 + x4) / 2;
|
|
1613
|
+
const y34 = (y3 + y4) / 2;
|
|
1614
|
+
const x123 = (x12 + x23) / 2;
|
|
1615
|
+
const y123 = (y12 + y23) / 2;
|
|
1616
|
+
const x234 = (x23 + x34) / 2;
|
|
1617
|
+
const y234 = (y23 + y34) / 2;
|
|
1618
|
+
const x1234 = (x123 + x234) / 2;
|
|
1619
|
+
const y1234 = (y123 + y234) / 2;
|
|
1620
|
+
if (level > 0) {
|
|
1621
|
+
let dx = x4 - x1;
|
|
1622
|
+
let dy = y4 - y1;
|
|
1623
|
+
const d2 = Math.abs((x2 - x4) * dy - (y2 - y4) * dx);
|
|
1624
|
+
const d3 = Math.abs((x3 - x4) * dy - (y3 - y4) * dx);
|
|
1625
|
+
if (d2 > FLT_EPSILON$1 && d3 > FLT_EPSILON$1) {
|
|
1626
|
+
if ((d2 + d3) * (d2 + d3) <= distanceTolerance * (dx * dx + dy * dy)) {
|
|
1627
|
+
{
|
|
1628
|
+
points.push(x1234, y1234);
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
} else if (d2 > FLT_EPSILON$1) {
|
|
1633
|
+
if (d2 * d2 <= distanceTolerance * (dx * dx + dy * dy)) {
|
|
1634
|
+
{
|
|
1635
|
+
points.push(x1234, y1234);
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
} else if (d3 > FLT_EPSILON$1) {
|
|
1640
|
+
if (d3 * d3 <= distanceTolerance * (dx * dx + dy * dy)) {
|
|
1641
|
+
{
|
|
1642
|
+
points.push(x1234, y1234);
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
} else {
|
|
1647
|
+
dx = x1234 - (x1 + x4) / 2;
|
|
1648
|
+
dy = y1234 - (y1 + y4) / 2;
|
|
1649
|
+
if (dx * dx + dy * dy <= distanceTolerance) {
|
|
1650
|
+
points.push(x1234, y1234);
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
recursive$1(x1, y1, x12, y12, x123, y123, x1234, y1234, points, distanceTolerance, level + 1);
|
|
1656
|
+
recursive$1(x1234, y1234, x234, y234, x34, y34, x4, y4, points, distanceTolerance, level + 1);
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
const RECURSION_LIMIT = 8;
|
|
1660
|
+
const FLT_EPSILON = 11920929e-14;
|
|
1661
|
+
const PATH_DISTANCE_EPSILON = 1;
|
|
1662
|
+
function getAdaptiveQuadraticBezierCurvePoints(sX, sY, x1, y1, x, y, smoothness = 0.5, points = []) {
|
|
1663
|
+
const scale = 1;
|
|
1664
|
+
const smoothing = Math.min(
|
|
1665
|
+
0.99,
|
|
1666
|
+
// a value of 1.0 actually inverts smoothing, so we cap it at 0.99
|
|
1667
|
+
Math.max(0, smoothness)
|
|
1668
|
+
);
|
|
1669
|
+
let distanceTolerance = (PATH_DISTANCE_EPSILON - smoothing) / scale;
|
|
1670
|
+
distanceTolerance *= distanceTolerance;
|
|
1671
|
+
recursive(points, sX, sY, x1, y1, x, y, distanceTolerance, 0);
|
|
1672
|
+
points.push(x, y);
|
|
1673
|
+
return points;
|
|
1674
|
+
}
|
|
1675
|
+
function recursive(points, x1, y1, x2, y2, x3, y3, distanceTolerance, level) {
|
|
1676
|
+
if (level > RECURSION_LIMIT)
|
|
1677
|
+
return;
|
|
1678
|
+
const x12 = (x1 + x2) / 2;
|
|
1679
|
+
const y12 = (y1 + y2) / 2;
|
|
1680
|
+
const x23 = (x2 + x3) / 2;
|
|
1681
|
+
const y23 = (y2 + y3) / 2;
|
|
1682
|
+
const x123 = (x12 + x23) / 2;
|
|
1683
|
+
const y123 = (y12 + y23) / 2;
|
|
1684
|
+
let dx = x3 - x1;
|
|
1685
|
+
let dy = y3 - y1;
|
|
1686
|
+
const d = Math.abs((x2 - x3) * dy - (y2 - y3) * dx);
|
|
1687
|
+
if (d > FLT_EPSILON) {
|
|
1688
|
+
if (d * d <= distanceTolerance * (dx * dx + dy * dy)) {
|
|
1689
|
+
{
|
|
1690
|
+
points.push(x123, y123);
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
} else {
|
|
1695
|
+
dx = x123 - (x1 + x3) / 2;
|
|
1696
|
+
dy = y123 - (y1 + y3) / 2;
|
|
1697
|
+
if (dx * dx + dy * dy <= distanceTolerance) {
|
|
1698
|
+
points.push(x123, y123);
|
|
1699
|
+
return;
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
recursive(points, x1, y1, x12, y12, x123, y123, distanceTolerance, level + 1);
|
|
1703
|
+
recursive(points, x123, y123, x23, y23, x3, y3, distanceTolerance, level + 1);
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
function getDirectedArea(vertices) {
|
|
1707
|
+
let area = 0;
|
|
1708
|
+
const n = vertices.length;
|
|
1709
|
+
for (let i = 0; i < n; i += 2) {
|
|
1710
|
+
const x0 = vertices[i];
|
|
1711
|
+
const y0 = vertices[i + 1];
|
|
1712
|
+
const x1 = vertices[(i + 2) % (n - 1)];
|
|
1713
|
+
const y1 = vertices[(i + 3) % n];
|
|
1714
|
+
area += x0 * y1 - x1 * y0;
|
|
1715
|
+
}
|
|
1716
|
+
return area / 2;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
function pointInPolygonEvenOdd(point, polygon) {
|
|
1720
|
+
let inside = false;
|
|
1721
|
+
const [x, y] = point;
|
|
1722
|
+
const len = polygon.length / 2;
|
|
1723
|
+
for (let i = 0, j = len - 1; i < len; j = i++) {
|
|
1724
|
+
const xi = polygon[i * 2];
|
|
1725
|
+
const yi = polygon[i * 2 + 1];
|
|
1726
|
+
const xj = polygon[j * 2];
|
|
1727
|
+
const yj = polygon[j * 2 + 1];
|
|
1728
|
+
if (yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi) {
|
|
1729
|
+
inside = !inside;
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
return inside;
|
|
1733
|
+
}
|
|
1734
|
+
function pointInPolygonNonZero(point, polygon) {
|
|
1735
|
+
const [x, y] = point;
|
|
1736
|
+
const len = polygon.length / 2;
|
|
1737
|
+
let wn = 0;
|
|
1738
|
+
for (let i = 0, j = len - 1; i < len; j = i++) {
|
|
1739
|
+
const xi = polygon[i * 2];
|
|
1740
|
+
const yi = polygon[i * 2 + 1];
|
|
1741
|
+
const xj = polygon[j * 2];
|
|
1742
|
+
const yj = polygon[j * 2 + 1];
|
|
1743
|
+
if (yi <= y) {
|
|
1744
|
+
if (yj > y && cross([xj, yj], [xi, yi], [x, y]) > 0) {
|
|
1745
|
+
wn++;
|
|
1746
|
+
}
|
|
1747
|
+
} else {
|
|
1748
|
+
if (yj <= y && cross([xj, yj], [xi, yi], [x, y]) < 0) {
|
|
1749
|
+
wn--;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
return wn !== 0;
|
|
1754
|
+
}
|
|
1755
|
+
function cross(p0, p1, p2) {
|
|
1756
|
+
return (p1[0] - p0[0]) * (p2[1] - p0[1]) - (p2[0] - p0[0]) * (p1[1] - p0[1]);
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
function quadraticBezierP0(t, p) {
|
|
1760
|
+
const k = 1 - t;
|
|
1761
|
+
return k * k * p;
|
|
1762
|
+
}
|
|
1763
|
+
function quadraticBezierP1(t, p) {
|
|
1764
|
+
return 2 * (1 - t) * t * p;
|
|
1765
|
+
}
|
|
1766
|
+
function quadraticBezierP2(t, p) {
|
|
1767
|
+
return t * t * p;
|
|
1768
|
+
}
|
|
1769
|
+
function quadraticBezier(t, p0, p1, p2) {
|
|
1770
|
+
return quadraticBezierP0(t, p0) + quadraticBezierP1(t, p1) + quadraticBezierP2(t, p2);
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
const closePointEps = 1e-4;
|
|
1774
|
+
const curveEps = 1e-4;
|
|
1775
|
+
function strokeTriangulate(points, options = {}) {
|
|
1776
|
+
const {
|
|
1777
|
+
vertices = [],
|
|
1778
|
+
indices = [],
|
|
1779
|
+
lineStyle = {
|
|
1780
|
+
alignment: 0.5,
|
|
1781
|
+
cap: "butt",
|
|
1782
|
+
join: "miter",
|
|
1783
|
+
width: 1,
|
|
1784
|
+
miterLimit: 10
|
|
1785
|
+
},
|
|
1786
|
+
flipAlignment = false,
|
|
1787
|
+
closed = true
|
|
1788
|
+
} = options;
|
|
1789
|
+
const eps = closePointEps;
|
|
1790
|
+
if (points.length === 0) {
|
|
1791
|
+
return { vertices, indices };
|
|
1792
|
+
}
|
|
1793
|
+
const style = lineStyle;
|
|
1794
|
+
let alignment = style.alignment;
|
|
1795
|
+
if (lineStyle.alignment !== 0.5) {
|
|
1796
|
+
let orientation = getOrientationOfPoints(points);
|
|
1797
|
+
if (flipAlignment)
|
|
1798
|
+
orientation *= -1;
|
|
1799
|
+
alignment = (alignment - 0.5) * orientation + 0.5;
|
|
1800
|
+
}
|
|
1801
|
+
const firstPoint = { x: points[0], y: points[1] };
|
|
1802
|
+
const lastPoint = { x: points[points.length - 2], y: points[points.length - 1] };
|
|
1803
|
+
const closedShape = closed;
|
|
1804
|
+
const closedPath = Math.abs(firstPoint.x - lastPoint.x) < eps && Math.abs(firstPoint.y - lastPoint.y) < eps;
|
|
1805
|
+
if (closedShape) {
|
|
1806
|
+
points = points.slice();
|
|
1807
|
+
if (closedPath) {
|
|
1808
|
+
points.pop();
|
|
1809
|
+
points.pop();
|
|
1810
|
+
lastPoint.x = points[points.length - 2];
|
|
1811
|
+
lastPoint.y = points[points.length - 1];
|
|
1812
|
+
}
|
|
1813
|
+
const midPointX = (firstPoint.x + lastPoint.x) * 0.5;
|
|
1814
|
+
const midPointY = (lastPoint.y + firstPoint.y) * 0.5;
|
|
1815
|
+
points.unshift(midPointX, midPointY);
|
|
1816
|
+
points.push(midPointX, midPointY);
|
|
1817
|
+
}
|
|
1818
|
+
const verts = vertices;
|
|
1819
|
+
const length = points.length / 2;
|
|
1820
|
+
let indexCount = points.length;
|
|
1821
|
+
const indexStart = verts.length / 2;
|
|
1822
|
+
const width = style.width / 2;
|
|
1823
|
+
const widthSquared = width * width;
|
|
1271
1824
|
const miterLimitSquared = style.miterLimit * style.miterLimit;
|
|
1272
1825
|
let x0 = points[0];
|
|
1273
1826
|
let y0 = points[1];
|
|
@@ -1612,10 +2165,7 @@ class Curve {
|
|
|
1612
2165
|
return this.getPoint(this.getUToTMapping(u), output);
|
|
1613
2166
|
}
|
|
1614
2167
|
isClockwise() {
|
|
1615
|
-
|
|
1616
|
-
const cur = this.getPoint(0.5);
|
|
1617
|
-
const next = this.getPoint(1);
|
|
1618
|
-
return (cur.x - prev.x) * (next.y - cur.y) - (cur.y - prev.y) * (next.x - cur.x) < 0;
|
|
2168
|
+
return false;
|
|
1619
2169
|
}
|
|
1620
2170
|
getControlPointRefs() {
|
|
1621
2171
|
return [];
|
|
@@ -1838,9 +2388,9 @@ class Curve {
|
|
|
1838
2388
|
}
|
|
1839
2389
|
}
|
|
1840
2390
|
|
|
1841
|
-
const tempTransform0
|
|
1842
|
-
const tempTransform1
|
|
1843
|
-
const tempTransform2
|
|
2391
|
+
const tempTransform0 = new Matrix3();
|
|
2392
|
+
const tempTransform1 = new Matrix3();
|
|
2393
|
+
const tempTransform2 = new Matrix3();
|
|
1844
2394
|
const tempV2 = new Vector2();
|
|
1845
2395
|
class RoundCurve extends Curve {
|
|
1846
2396
|
constructor(_center = new Vector2(), _radius = new Vector2(), _diff = new Vector2(), rotate = 0, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
|
|
@@ -2118,7 +2668,7 @@ function transfEllipseGeneric(curve, m) {
|
|
|
2118
2668
|
const v2 = new Vector2(-b * sinTheta, b * cosTheta);
|
|
2119
2669
|
const f1 = v1.applyMatrix3(m);
|
|
2120
2670
|
const f2 = v2.applyMatrix3(m);
|
|
2121
|
-
const mF = tempTransform0
|
|
2671
|
+
const mF = tempTransform0.set(
|
|
2122
2672
|
f1.x,
|
|
2123
2673
|
f2.x,
|
|
2124
2674
|
0,
|
|
@@ -2129,8 +2679,8 @@ function transfEllipseGeneric(curve, m) {
|
|
|
2129
2679
|
0,
|
|
2130
2680
|
1
|
|
2131
2681
|
);
|
|
2132
|
-
const mFInv = tempTransform1
|
|
2133
|
-
const mFInvT = tempTransform2
|
|
2682
|
+
const mFInv = tempTransform1.copy(mF).invert();
|
|
2683
|
+
const mFInvT = tempTransform2.copy(mFInv).transpose();
|
|
2134
2684
|
const mQ = mFInvT.multiply(mFInv);
|
|
2135
2685
|
const mQe = mQ.elements;
|
|
2136
2686
|
const ed = eigenDecomposition(mQe[0], mQe[1], mQe[4]);
|
|
@@ -2141,7 +2691,7 @@ function transfEllipseGeneric(curve, m) {
|
|
|
2141
2691
|
curve.rotate = Math.atan2(ed.sn, ed.cs);
|
|
2142
2692
|
const isFullEllipse = (curve.endAngle - curve.startAngle) % (2 * Math.PI) < Number.EPSILON;
|
|
2143
2693
|
if (!isFullEllipse) {
|
|
2144
|
-
const mDsqrt = tempTransform1
|
|
2694
|
+
const mDsqrt = tempTransform1.set(
|
|
2145
2695
|
rt1sqrt,
|
|
2146
2696
|
0,
|
|
2147
2697
|
0,
|
|
@@ -2152,7 +2702,7 @@ function transfEllipseGeneric(curve, m) {
|
|
|
2152
2702
|
0,
|
|
2153
2703
|
1
|
|
2154
2704
|
);
|
|
2155
|
-
const mRT = tempTransform2
|
|
2705
|
+
const mRT = tempTransform2.set(
|
|
2156
2706
|
ed.cs,
|
|
2157
2707
|
ed.sn,
|
|
2158
2708
|
0,
|
|
@@ -2623,1560 +3173,1035 @@ class CubicBezierCurve extends Curve {
|
|
|
2623
3173
|
return this;
|
|
2624
3174
|
}
|
|
2625
3175
|
}
|
|
2626
|
-
|
|
2627
|
-
class EllipseCurve extends RoundCurve {
|
|
2628
|
-
constructor(cx = 0, cy = 0, rx = 1, ry = 1, rotate = 0, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
|
|
2629
|
-
super(
|
|
2630
|
-
new Vector2(cx, cy),
|
|
2631
|
-
new Vector2(rx, ry),
|
|
2632
|
-
new Vector2(),
|
|
2633
|
-
rotate,
|
|
2634
|
-
startAngle,
|
|
2635
|
-
endAngle,
|
|
2636
|
-
clockwise
|
|
2637
|
-
);
|
|
2638
|
-
}
|
|
2639
|
-
drawTo(ctx) {
|
|
2640
|
-
ctx.ellipse(
|
|
2641
|
-
this.cx,
|
|
2642
|
-
this.cy,
|
|
2643
|
-
this.rx,
|
|
2644
|
-
this.ry,
|
|
2645
|
-
this.rotate,
|
|
2646
|
-
this.startAngle,
|
|
2647
|
-
this.endAngle,
|
|
2648
|
-
!this.clockwise
|
|
2649
|
-
);
|
|
2650
|
-
return this;
|
|
2651
|
-
}
|
|
2652
|
-
}
|
|
2653
|
-
|
|
2654
|
-
class PloygonCurve extends CompositeCurve {
|
|
2655
|
-
//
|
|
2656
|
-
}
|
|
2657
|
-
|
|
2658
|
-
class EquilateralPloygonCurve extends PloygonCurve {
|
|
2659
|
-
constructor(cx = 0, cy = 0, radius = 1, sideCount = 3) {
|
|
2660
|
-
super();
|
|
2661
|
-
this.cx = cx;
|
|
2662
|
-
this.cy = cy;
|
|
2663
|
-
this.radius = radius;
|
|
2664
|
-
this.sideCount = sideCount;
|
|
2665
|
-
this.update();
|
|
2666
|
-
}
|
|
2667
|
-
update() {
|
|
2668
|
-
const { cx, cy, radius, sideCount } = this;
|
|
2669
|
-
const points = [];
|
|
2670
|
-
for (let i = 0; i < sideCount; i++) {
|
|
2671
|
-
const radian = i * 2 * Math.PI / sideCount - 0.5 * Math.PI;
|
|
2672
|
-
points.push(
|
|
2673
|
-
new Vector2(
|
|
2674
|
-
radius * Math.cos(radian),
|
|
2675
|
-
radius * Math.sin(radian)
|
|
2676
|
-
).add({ x: cx, y: cy })
|
|
2677
|
-
);
|
|
2678
|
-
}
|
|
2679
|
-
const curves = [];
|
|
2680
|
-
for (let i = 0; i < sideCount; i++) {
|
|
2681
|
-
curves.push(
|
|
2682
|
-
new LineCurve(
|
|
2683
|
-
points[i],
|
|
2684
|
-
points[(i + 1) % sideCount]
|
|
2685
|
-
)
|
|
2686
|
-
);
|
|
2687
|
-
}
|
|
2688
|
-
this.curves = curves;
|
|
2689
|
-
return this;
|
|
2690
|
-
}
|
|
2691
|
-
copy(source) {
|
|
2692
|
-
super.copy(source);
|
|
2693
|
-
this.cx = source.cx;
|
|
2694
|
-
this.cy = source.cy;
|
|
2695
|
-
this.radius = source.radius;
|
|
2696
|
-
this.sideCount = source.sideCount;
|
|
2697
|
-
this.update();
|
|
2698
|
-
return this;
|
|
2699
|
-
}
|
|
2700
|
-
}
|
|
2701
|
-
|
|
2702
|
-
class QuadraticBezierCurve extends Curve {
|
|
2703
|
-
constructor(p1 = new Vector2(), cp = new Vector2(), p2 = new Vector2()) {
|
|
2704
|
-
super();
|
|
2705
|
-
this.p1 = p1;
|
|
2706
|
-
this.cp = cp;
|
|
2707
|
-
this.p2 = p2;
|
|
2708
|
-
}
|
|
2709
|
-
static from(p1x, p1y, cpx, cpy, p2x, p2y) {
|
|
2710
|
-
return new QuadraticBezierCurve(
|
|
2711
|
-
new Vector2(p1x, p1y),
|
|
2712
|
-
new Vector2(cpx, cpy),
|
|
2713
|
-
new Vector2(p2x, p2y)
|
|
2714
|
-
);
|
|
2715
|
-
}
|
|
2716
|
-
getPoint(t, output = new Vector2()) {
|
|
2717
|
-
const { p1, cp, p2 } = this;
|
|
2718
|
-
output.set(
|
|
2719
|
-
quadraticBezier(t, p1.x, cp.x, p2.x),
|
|
2720
|
-
quadraticBezier(t, p1.y, cp.y, p2.y)
|
|
2721
|
-
);
|
|
2722
|
-
return output;
|
|
2723
|
-
}
|
|
2724
|
-
getControlPointRefs() {
|
|
2725
|
-
return [this.p1, this.cp, this.p2];
|
|
2726
|
-
}
|
|
2727
|
-
getAdaptiveVertices(output = []) {
|
|
2728
|
-
return getAdaptiveQuadraticBezierCurvePoints(
|
|
2729
|
-
this.p1.x,
|
|
2730
|
-
this.p1.y,
|
|
2731
|
-
this.cp.x,
|
|
2732
|
-
this.cp.y,
|
|
2733
|
-
this.p2.x,
|
|
2734
|
-
this.p2.y,
|
|
2735
|
-
0.5,
|
|
2736
|
-
output
|
|
2737
|
-
);
|
|
2738
|
-
}
|
|
2739
|
-
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
2740
|
-
const { p1, cp, p2 } = this;
|
|
2741
|
-
const x1 = 0.5 * (p1.x + cp.x);
|
|
2742
|
-
const y1 = 0.5 * (p1.y + cp.y);
|
|
2743
|
-
const x2 = 0.5 * (p1.x + p2.x);
|
|
2744
|
-
const y2 = 0.5 * (p1.y + p2.y);
|
|
2745
|
-
min.x = Math.min(min.x, p1.x, p2.x, x1, x2);
|
|
2746
|
-
min.y = Math.min(min.y, p1.y, p2.y, y1, y2);
|
|
2747
|
-
max.x = Math.max(max.x, p1.x, p2.x, x1, x2);
|
|
2748
|
-
max.y = Math.max(max.y, p1.y, p2.y, y1, y2);
|
|
2749
|
-
return { min: min.finite(), max: max.finite() };
|
|
2750
|
-
}
|
|
2751
|
-
toCommands() {
|
|
2752
|
-
const { p1, cp, p2 } = this;
|
|
2753
|
-
return [
|
|
2754
|
-
{ type: "M", x: p1.x, y: p1.y },
|
|
2755
|
-
{ type: "Q", x1: cp.x, y1: cp.y, x: p2.x, y: p2.y }
|
|
2756
|
-
];
|
|
2757
|
-
}
|
|
2758
|
-
drawTo(ctx) {
|
|
2759
|
-
const { p1, cp, p2 } = this;
|
|
2760
|
-
ctx.lineTo(p1.x, p1.y);
|
|
2761
|
-
ctx.quadraticCurveTo(cp.x, cp.y, p2.x, p2.y);
|
|
2762
|
-
return this;
|
|
2763
|
-
}
|
|
2764
|
-
copy(source) {
|
|
2765
|
-
super.copy(source);
|
|
2766
|
-
this.p1.copy(source.p1);
|
|
2767
|
-
this.cp.copy(source.cp);
|
|
2768
|
-
this.p2.copy(source.p2);
|
|
2769
|
-
return this;
|
|
2770
|
-
}
|
|
2771
|
-
}
|
|
2772
|
-
|
|
2773
|
-
class RectangleCurve extends PloygonCurve {
|
|
2774
|
-
constructor(x = 0, y = 0, width = 0, height = 0) {
|
|
2775
|
-
super();
|
|
2776
|
-
this.x = x;
|
|
2777
|
-
this.y = y;
|
|
2778
|
-
this.width = width;
|
|
2779
|
-
this.height = height;
|
|
2780
|
-
this.update();
|
|
2781
|
-
}
|
|
2782
|
-
update() {
|
|
2783
|
-
const { x, y, width, height } = this;
|
|
2784
|
-
const points = [
|
|
2785
|
-
new Vector2(x, y),
|
|
2786
|
-
new Vector2(x + width, y),
|
|
2787
|
-
new Vector2(x + width, y + height),
|
|
2788
|
-
new Vector2(x, y + height)
|
|
2789
|
-
];
|
|
2790
|
-
this.curves = [
|
|
2791
|
-
new LineCurve(points[0], points[1]),
|
|
2792
|
-
new LineCurve(points[1], points[2]),
|
|
2793
|
-
new LineCurve(points[2], points[3]),
|
|
2794
|
-
new LineCurve(points[3], points[0])
|
|
2795
|
-
];
|
|
2796
|
-
return this;
|
|
2797
|
-
}
|
|
2798
|
-
drawTo(ctx) {
|
|
2799
|
-
ctx.rect(this.x, this.y, this.width, this.height);
|
|
2800
|
-
return this;
|
|
2801
|
-
}
|
|
2802
|
-
getFillVertices(_options = {}) {
|
|
2803
|
-
const { x, y, width, height } = this;
|
|
2804
|
-
return [
|
|
2805
|
-
x,
|
|
2806
|
-
y,
|
|
2807
|
-
x + width,
|
|
2808
|
-
y,
|
|
2809
|
-
x + width,
|
|
2810
|
-
y + height,
|
|
2811
|
-
x,
|
|
2812
|
-
y + height
|
|
2813
|
-
];
|
|
3176
|
+
|
|
3177
|
+
class EllipseCurve extends RoundCurve {
|
|
3178
|
+
constructor(cx = 0, cy = 0, rx = 1, ry = 1, rotate = 0, startAngle = 0, endAngle = Math.PI * 2, clockwise = false) {
|
|
3179
|
+
super(
|
|
3180
|
+
new Vector2(cx, cy),
|
|
3181
|
+
new Vector2(rx, ry),
|
|
3182
|
+
new Vector2(),
|
|
3183
|
+
rotate,
|
|
3184
|
+
startAngle,
|
|
3185
|
+
endAngle,
|
|
3186
|
+
clockwise
|
|
3187
|
+
);
|
|
2814
3188
|
}
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
3189
|
+
drawTo(ctx) {
|
|
3190
|
+
ctx.ellipse(
|
|
3191
|
+
this.cx,
|
|
3192
|
+
this.cy,
|
|
3193
|
+
this.rx,
|
|
3194
|
+
this.ry,
|
|
3195
|
+
this.rotate,
|
|
3196
|
+
this.startAngle,
|
|
3197
|
+
this.endAngle,
|
|
3198
|
+
!this.clockwise
|
|
3199
|
+
);
|
|
2822
3200
|
return this;
|
|
2823
3201
|
}
|
|
2824
3202
|
}
|
|
2825
3203
|
|
|
2826
|
-
class
|
|
2827
|
-
|
|
3204
|
+
class PloygonCurve extends CompositeCurve {
|
|
3205
|
+
//
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3208
|
+
class EquilateralPloygonCurve extends PloygonCurve {
|
|
3209
|
+
constructor(cx = 0, cy = 0, radius = 1, sideCount = 3) {
|
|
2828
3210
|
super();
|
|
2829
|
-
this.
|
|
2830
|
-
this.
|
|
2831
|
-
this.width = width;
|
|
2832
|
-
this.height = height;
|
|
3211
|
+
this.cx = cx;
|
|
3212
|
+
this.cy = cy;
|
|
2833
3213
|
this.radius = radius;
|
|
3214
|
+
this.sideCount = sideCount;
|
|
2834
3215
|
this.update();
|
|
2835
3216
|
}
|
|
2836
3217
|
update() {
|
|
2837
|
-
const {
|
|
2838
|
-
const
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
3218
|
+
const { cx, cy, radius, sideCount } = this;
|
|
3219
|
+
const points = [];
|
|
3220
|
+
for (let i = 0; i < sideCount; i++) {
|
|
3221
|
+
const radian = i * 2 * Math.PI / sideCount - 0.5 * Math.PI;
|
|
3222
|
+
points.push(
|
|
3223
|
+
new Vector2(
|
|
3224
|
+
radius * Math.cos(radian),
|
|
3225
|
+
radius * Math.sin(radian)
|
|
3226
|
+
).add({ x: cx, y: cy })
|
|
3227
|
+
);
|
|
3228
|
+
}
|
|
3229
|
+
const curves = [];
|
|
3230
|
+
for (let i = 0; i < sideCount; i++) {
|
|
3231
|
+
curves.push(
|
|
3232
|
+
new LineCurve(
|
|
3233
|
+
points[i],
|
|
3234
|
+
points[(i + 1) % sideCount]
|
|
3235
|
+
)
|
|
3236
|
+
);
|
|
3237
|
+
}
|
|
3238
|
+
this.curves = curves;
|
|
2852
3239
|
return this;
|
|
2853
3240
|
}
|
|
2854
3241
|
copy(source) {
|
|
2855
3242
|
super.copy(source);
|
|
2856
|
-
this.
|
|
2857
|
-
this.
|
|
2858
|
-
this.width = source.width;
|
|
2859
|
-
this.height = source.height;
|
|
3243
|
+
this.cx = source.cx;
|
|
3244
|
+
this.cy = source.cy;
|
|
2860
3245
|
this.radius = source.radius;
|
|
3246
|
+
this.sideCount = source.sideCount;
|
|
2861
3247
|
this.update();
|
|
2862
3248
|
return this;
|
|
2863
3249
|
}
|
|
2864
3250
|
}
|
|
2865
3251
|
|
|
2866
|
-
class
|
|
2867
|
-
constructor(
|
|
3252
|
+
class QuadraticBezierCurve extends Curve {
|
|
3253
|
+
constructor(p1 = new Vector2(), cp = new Vector2(), p2 = new Vector2()) {
|
|
2868
3254
|
super();
|
|
2869
|
-
this.
|
|
3255
|
+
this.p1 = p1;
|
|
3256
|
+
this.cp = cp;
|
|
3257
|
+
this.p2 = p2;
|
|
3258
|
+
}
|
|
3259
|
+
static from(p1x, p1y, cpx, cpy, p2x, p2y) {
|
|
3260
|
+
return new QuadraticBezierCurve(
|
|
3261
|
+
new Vector2(p1x, p1y),
|
|
3262
|
+
new Vector2(cpx, cpy),
|
|
3263
|
+
new Vector2(p2x, p2y)
|
|
3264
|
+
);
|
|
2870
3265
|
}
|
|
2871
3266
|
getPoint(t, output = new Vector2()) {
|
|
2872
|
-
const {
|
|
2873
|
-
const p = (points.length - 1) * t;
|
|
2874
|
-
const _p = Math.floor(p);
|
|
2875
|
-
const weight = p - _p;
|
|
2876
|
-
const p0 = points[_p === 0 ? _p : _p - 1];
|
|
2877
|
-
const p1 = points[_p];
|
|
2878
|
-
const p2 = points[_p > points.length - 2 ? points.length - 1 : _p + 1];
|
|
2879
|
-
const p3 = points[_p > points.length - 3 ? points.length - 1 : _p + 2];
|
|
3267
|
+
const { p1, cp, p2 } = this;
|
|
2880
3268
|
output.set(
|
|
2881
|
-
|
|
2882
|
-
|
|
3269
|
+
quadraticBezier(t, p1.x, cp.x, p2.x),
|
|
3270
|
+
quadraticBezier(t, p1.y, cp.y, p2.y)
|
|
2883
3271
|
);
|
|
2884
3272
|
return output;
|
|
2885
3273
|
}
|
|
2886
3274
|
getControlPointRefs() {
|
|
2887
|
-
return this.
|
|
2888
|
-
}
|
|
2889
|
-
copy(source) {
|
|
2890
|
-
super.copy(source);
|
|
2891
|
-
this.points = [];
|
|
2892
|
-
for (let i = 0, len = source.points.length; i < len; i++) {
|
|
2893
|
-
this.points.push(source.points[i].clone());
|
|
2894
|
-
}
|
|
2895
|
-
return this;
|
|
2896
|
-
}
|
|
2897
|
-
}
|
|
2898
|
-
|
|
2899
|
-
class CurvePath extends CompositeCurve {
|
|
2900
|
-
startPoint;
|
|
2901
|
-
currentPoint;
|
|
2902
|
-
autoClose = false;
|
|
2903
|
-
constructor(points) {
|
|
2904
|
-
super();
|
|
2905
|
-
if (points) {
|
|
2906
|
-
this.addPoints(points);
|
|
2907
|
-
}
|
|
2908
|
-
}
|
|
2909
|
-
addPoints(points) {
|
|
2910
|
-
this.moveTo(points[0].x, points[0].y);
|
|
2911
|
-
for (let i = 1, len = points.length; i < len; i++) {
|
|
2912
|
-
const { x, y } = points[i];
|
|
2913
|
-
this.lineTo(x, y);
|
|
2914
|
-
}
|
|
2915
|
-
return this;
|
|
2916
|
-
}
|
|
2917
|
-
addCommands(commands) {
|
|
2918
|
-
svgPathCommandsAddToPath2D(commands, this);
|
|
2919
|
-
return this;
|
|
2920
|
-
}
|
|
2921
|
-
addData(data) {
|
|
2922
|
-
this.addCommands(svgPathDataToCommands(data));
|
|
2923
|
-
return this;
|
|
2924
|
-
}
|
|
2925
|
-
_closeVertices(output) {
|
|
2926
|
-
if (this.autoClose && output.length >= 4 && (output[0] !== output[output.length - 2] && output[1] !== output[output.length - 1])) {
|
|
2927
|
-
output.push(output[0], output[1]);
|
|
2928
|
-
}
|
|
2929
|
-
return output;
|
|
2930
|
-
}
|
|
2931
|
-
getUnevenVertices(count = 40, output = []) {
|
|
2932
|
-
return this._closeVertices(
|
|
2933
|
-
super.getUnevenVertices(count, output)
|
|
2934
|
-
);
|
|
2935
|
-
}
|
|
2936
|
-
getSpacedVertices(count = 40, output = []) {
|
|
2937
|
-
return this._closeVertices(
|
|
2938
|
-
super.getSpacedVertices(count, output)
|
|
2939
|
-
);
|
|
3275
|
+
return [this.p1, this.cp, this.p2];
|
|
2940
3276
|
}
|
|
2941
3277
|
getAdaptiveVertices(output = []) {
|
|
2942
|
-
return
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
3278
|
+
return getAdaptiveQuadraticBezierCurvePoints(
|
|
3279
|
+
this.p1.x,
|
|
3280
|
+
this.p1.y,
|
|
3281
|
+
this.cp.x,
|
|
3282
|
+
this.cp.y,
|
|
3283
|
+
this.p2.x,
|
|
3284
|
+
this.p2.y,
|
|
3285
|
+
0.5,
|
|
3286
|
+
output
|
|
2949
3287
|
);
|
|
2950
3288
|
}
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
this.lineTo(first.x, first.y);
|
|
2963
|
-
}
|
|
2964
|
-
}
|
|
2965
|
-
return this;
|
|
2966
|
-
}
|
|
2967
|
-
closePath() {
|
|
2968
|
-
const start = this.startPoint;
|
|
2969
|
-
if (start) {
|
|
2970
|
-
const end = this.currentPoint;
|
|
2971
|
-
if (end && !start.equals(end)) {
|
|
2972
|
-
this.curves.push(new LineCurve(end.clone(), start.clone()));
|
|
2973
|
-
end.copy(start);
|
|
2974
|
-
}
|
|
2975
|
-
this.startPoint = void 0;
|
|
2976
|
-
}
|
|
2977
|
-
return this;
|
|
2978
|
-
}
|
|
2979
|
-
moveTo(x, y) {
|
|
2980
|
-
this.currentPoint = new Vector2(x, y);
|
|
2981
|
-
this.startPoint = this.currentPoint.clone();
|
|
2982
|
-
return this;
|
|
2983
|
-
}
|
|
2984
|
-
lineTo(x, y) {
|
|
2985
|
-
const start = this.currentPoint;
|
|
2986
|
-
if (!start?.equals({ x, y })) {
|
|
2987
|
-
this.curves.push(
|
|
2988
|
-
LineCurve.from(
|
|
2989
|
-
start?.x ?? 0,
|
|
2990
|
-
start?.y ?? 0,
|
|
2991
|
-
x,
|
|
2992
|
-
y
|
|
2993
|
-
)
|
|
2994
|
-
);
|
|
2995
|
-
}
|
|
2996
|
-
this._setCurrentPoint({ x, y });
|
|
2997
|
-
return this;
|
|
2998
|
-
}
|
|
2999
|
-
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
|
|
3000
|
-
const start = this.currentPoint;
|
|
3001
|
-
if (!start?.equals({ x, y })) {
|
|
3002
|
-
this.curves.push(
|
|
3003
|
-
CubicBezierCurve.from(
|
|
3004
|
-
start?.x ?? 0,
|
|
3005
|
-
start?.y ?? 0,
|
|
3006
|
-
cp1x,
|
|
3007
|
-
cp1y,
|
|
3008
|
-
cp2x,
|
|
3009
|
-
cp2y,
|
|
3010
|
-
x,
|
|
3011
|
-
y
|
|
3012
|
-
)
|
|
3013
|
-
);
|
|
3014
|
-
}
|
|
3015
|
-
this._setCurrentPoint({ x, y });
|
|
3016
|
-
return this;
|
|
3289
|
+
getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
|
|
3290
|
+
const { p1, cp, p2 } = this;
|
|
3291
|
+
const x1 = 0.5 * (p1.x + cp.x);
|
|
3292
|
+
const y1 = 0.5 * (p1.y + cp.y);
|
|
3293
|
+
const x2 = 0.5 * (p1.x + p2.x);
|
|
3294
|
+
const y2 = 0.5 * (p1.y + p2.y);
|
|
3295
|
+
min.x = Math.min(min.x, p1.x, p2.x, x1, x2);
|
|
3296
|
+
min.y = Math.min(min.y, p1.y, p2.y, y1, y2);
|
|
3297
|
+
max.x = Math.max(max.x, p1.x, p2.x, x1, x2);
|
|
3298
|
+
max.y = Math.max(max.y, p1.y, p2.y, y1, y2);
|
|
3299
|
+
return { min: min.finite(), max: max.finite() };
|
|
3017
3300
|
}
|
|
3018
|
-
|
|
3019
|
-
const
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
)
|
|
3030
|
-
);
|
|
3031
|
-
}
|
|
3032
|
-
this._setCurrentPoint({ x, y });
|
|
3301
|
+
toCommands() {
|
|
3302
|
+
const { p1, cp, p2 } = this;
|
|
3303
|
+
return [
|
|
3304
|
+
{ type: "M", x: p1.x, y: p1.y },
|
|
3305
|
+
{ type: "Q", x1: cp.x, y1: cp.y, x: p2.x, y: p2.y }
|
|
3306
|
+
];
|
|
3307
|
+
}
|
|
3308
|
+
drawTo(ctx) {
|
|
3309
|
+
const { p1, cp, p2 } = this;
|
|
3310
|
+
ctx.lineTo(p1.x, p1.y);
|
|
3311
|
+
ctx.quadraticCurveTo(cp.x, cp.y, p2.x, p2.y);
|
|
3033
3312
|
return this;
|
|
3034
3313
|
}
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
startAngle,
|
|
3041
|
-
endAngle,
|
|
3042
|
-
!counterclockwise
|
|
3043
|
-
);
|
|
3044
|
-
this._connetLineTo(curve);
|
|
3045
|
-
this.curves.push(curve);
|
|
3046
|
-
this._setCurrentPoint(curve.getPoint(1));
|
|
3314
|
+
copy(source) {
|
|
3315
|
+
super.copy(source);
|
|
3316
|
+
this.p1.copy(source.p1);
|
|
3317
|
+
this.cp.copy(source.cp);
|
|
3318
|
+
this.p2.copy(source.p2);
|
|
3047
3319
|
return this;
|
|
3048
3320
|
}
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
class RectangleCurve extends PloygonCurve {
|
|
3324
|
+
constructor(x = 0, y = 0, width = 0, height = 0) {
|
|
3325
|
+
super();
|
|
3326
|
+
this.x = x;
|
|
3327
|
+
this.y = y;
|
|
3328
|
+
this.width = width;
|
|
3329
|
+
this.height = height;
|
|
3330
|
+
this.update();
|
|
3331
|
+
}
|
|
3332
|
+
update() {
|
|
3333
|
+
const { x, y, width, height } = this;
|
|
3334
|
+
const points = [
|
|
3335
|
+
new Vector2(x, y),
|
|
3336
|
+
new Vector2(x + width, y),
|
|
3337
|
+
new Vector2(x + width, y + height),
|
|
3338
|
+
new Vector2(x, y + height)
|
|
3339
|
+
];
|
|
3340
|
+
this.curves = [
|
|
3341
|
+
new LineCurve(points[0], points[1]),
|
|
3342
|
+
new LineCurve(points[1], points[2]),
|
|
3343
|
+
new LineCurve(points[2], points[3]),
|
|
3344
|
+
new LineCurve(points[3], points[0])
|
|
3345
|
+
];
|
|
3053
3346
|
return this;
|
|
3054
3347
|
}
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
arcTo(x1, y1, x2, y2, radius) {
|
|
3058
|
-
console.warn("Method arcTo not supported yet");
|
|
3348
|
+
drawTo(ctx) {
|
|
3349
|
+
ctx.rect(this.x, this.y, this.width, this.height);
|
|
3059
3350
|
return this;
|
|
3060
3351
|
}
|
|
3061
|
-
|
|
3062
|
-
const
|
|
3352
|
+
getFillVertices(_options = {}) {
|
|
3353
|
+
const { x, y, width, height } = this;
|
|
3354
|
+
return [
|
|
3063
3355
|
x,
|
|
3064
3356
|
y,
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
this._connetLineTo(curve);
|
|
3073
|
-
this.curves.push(curve);
|
|
3074
|
-
this._setCurrentPoint(curve.getPoint(1));
|
|
3075
|
-
return this;
|
|
3076
|
-
}
|
|
3077
|
-
relativeEllipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) {
|
|
3078
|
-
x += this.currentPoint?.x ?? 0;
|
|
3079
|
-
y += this.currentPoint?.y ?? 0;
|
|
3080
|
-
this.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
|
|
3081
|
-
return this;
|
|
3357
|
+
x + width,
|
|
3358
|
+
y,
|
|
3359
|
+
x + width,
|
|
3360
|
+
y + height,
|
|
3361
|
+
x,
|
|
3362
|
+
y + height
|
|
3363
|
+
];
|
|
3082
3364
|
}
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
this.
|
|
3086
|
-
this.
|
|
3087
|
-
this.
|
|
3365
|
+
copy(source) {
|
|
3366
|
+
super.copy(source);
|
|
3367
|
+
this.x = source.x;
|
|
3368
|
+
this.y = source.y;
|
|
3369
|
+
this.width = source.width;
|
|
3370
|
+
this.height = source.height;
|
|
3371
|
+
this.update();
|
|
3088
3372
|
return this;
|
|
3089
3373
|
}
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3374
|
+
}
|
|
3375
|
+
|
|
3376
|
+
class RoundRectangleCurve extends RoundCurve {
|
|
3377
|
+
constructor(x = 0, y = 0, width = 1, height = 1, radius = 1) {
|
|
3378
|
+
super();
|
|
3379
|
+
this.x = x;
|
|
3380
|
+
this.y = y;
|
|
3381
|
+
this.width = width;
|
|
3382
|
+
this.height = height;
|
|
3383
|
+
this.radius = radius;
|
|
3384
|
+
this.update();
|
|
3096
3385
|
}
|
|
3097
|
-
|
|
3098
|
-
const
|
|
3099
|
-
|
|
3100
|
-
|
|
3386
|
+
update() {
|
|
3387
|
+
const { x, y, width, height, radius } = this;
|
|
3388
|
+
const halfWidth = width / 2;
|
|
3389
|
+
const halfHeight = height / 2;
|
|
3390
|
+
const cx = x + halfWidth;
|
|
3391
|
+
const cy = y + halfHeight;
|
|
3392
|
+
const rx = Math.max(0, Math.min(radius, Math.min(halfWidth, halfHeight)));
|
|
3393
|
+
const ry = rx;
|
|
3394
|
+
this._center = new Vector2(cx, cy);
|
|
3395
|
+
this._radius = new Vector2(rx, ry);
|
|
3396
|
+
this._diff = new Vector2(halfWidth - rx, halfHeight - ry);
|
|
3101
3397
|
return this;
|
|
3102
3398
|
}
|
|
3103
3399
|
drawTo(ctx) {
|
|
3104
|
-
const
|
|
3105
|
-
|
|
3106
|
-
ctx.moveTo(point.x, point.y);
|
|
3107
|
-
}
|
|
3108
|
-
this.curves.forEach((curve) => curve.drawTo(ctx));
|
|
3109
|
-
if (this.autoClose) {
|
|
3110
|
-
ctx.closePath();
|
|
3111
|
-
}
|
|
3400
|
+
const { x, y, width, height, radius } = this;
|
|
3401
|
+
ctx.roundRect(x, y, width, height, radius);
|
|
3112
3402
|
return this;
|
|
3113
3403
|
}
|
|
3114
3404
|
copy(source) {
|
|
3115
3405
|
super.copy(source);
|
|
3116
|
-
this.
|
|
3117
|
-
this.
|
|
3406
|
+
this.x = source.x;
|
|
3407
|
+
this.y = source.y;
|
|
3408
|
+
this.width = source.width;
|
|
3409
|
+
this.height = source.height;
|
|
3410
|
+
this.radius = source.radius;
|
|
3411
|
+
this.update();
|
|
3118
3412
|
return this;
|
|
3119
3413
|
}
|
|
3120
3414
|
}
|
|
3121
3415
|
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
const r = p2.clone().sub(p1);
|
|
3127
|
-
const s = q2.clone().sub(q1);
|
|
3128
|
-
const q1p1 = q1.clone().sub(p1);
|
|
3129
|
-
const crossRS = r.cross(s);
|
|
3130
|
-
if (crossRS === 0) {
|
|
3131
|
-
return new Vector2(
|
|
3132
|
-
(p1.x + q1.x) / 2,
|
|
3133
|
-
(p1.y + q1.y) / 2
|
|
3134
|
-
);
|
|
3416
|
+
class SplineCurve extends Curve {
|
|
3417
|
+
constructor(points = []) {
|
|
3418
|
+
super();
|
|
3419
|
+
this.points = points;
|
|
3135
3420
|
}
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3421
|
+
getPoint(t, output = new Vector2()) {
|
|
3422
|
+
const { points } = this;
|
|
3423
|
+
const p = (points.length - 1) * t;
|
|
3424
|
+
const _p = Math.floor(p);
|
|
3425
|
+
const weight = p - _p;
|
|
3426
|
+
const p0 = points[_p === 0 ? _p : _p - 1];
|
|
3427
|
+
const p1 = points[_p];
|
|
3428
|
+
const p2 = points[_p > points.length - 2 ? points.length - 1 : _p + 1];
|
|
3429
|
+
const p3 = points[_p > points.length - 3 ? points.length - 1 : _p + 2];
|
|
3430
|
+
output.set(
|
|
3431
|
+
catmullRom(weight, p0.x, p1.x, p2.x, p3.x),
|
|
3432
|
+
catmullRom(weight, p0.y, p1.y, p2.y, p3.y)
|
|
3141
3433
|
);
|
|
3434
|
+
return output;
|
|
3142
3435
|
}
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
)
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
style;
|
|
3152
|
-
get startPoint() {
|
|
3153
|
-
return this.currentCurve.startPoint;
|
|
3154
|
-
}
|
|
3155
|
-
get currentPoint() {
|
|
3156
|
-
return this.currentCurve.currentPoint;
|
|
3157
|
-
}
|
|
3158
|
-
get strokeWidth() {
|
|
3159
|
-
return this.style.strokeWidth ?? ((this.style.stroke ?? "none") === "none" ? 0 : 1);
|
|
3160
|
-
}
|
|
3161
|
-
constructor(path, style = {}) {
|
|
3162
|
-
super();
|
|
3163
|
-
this.curves.push(this.currentCurve);
|
|
3164
|
-
this.style = style;
|
|
3165
|
-
if (path) {
|
|
3166
|
-
if (path instanceof Path2D) {
|
|
3167
|
-
this.addPath(path);
|
|
3168
|
-
} else if (Array.isArray(path)) {
|
|
3169
|
-
this.addCommands(path);
|
|
3170
|
-
} else {
|
|
3171
|
-
this.addData(path);
|
|
3172
|
-
}
|
|
3173
|
-
}
|
|
3174
|
-
}
|
|
3175
|
-
addPath(path) {
|
|
3176
|
-
const index = this.curves.findIndex((v) => v === this.currentCurve);
|
|
3177
|
-
if (index > -1) {
|
|
3178
|
-
this.curves.splice(index, 1);
|
|
3179
|
-
}
|
|
3180
|
-
if (path instanceof Path2D) {
|
|
3181
|
-
this.curves.push(
|
|
3182
|
-
...path.curves.filter((curvePath) => curvePath.curves.length).map((v) => v.clone())
|
|
3183
|
-
);
|
|
3184
|
-
} else if (path.curves.length) {
|
|
3185
|
-
this.curves.push(path);
|
|
3436
|
+
getControlPointRefs() {
|
|
3437
|
+
return this.points;
|
|
3438
|
+
}
|
|
3439
|
+
copy(source) {
|
|
3440
|
+
super.copy(source);
|
|
3441
|
+
this.points = [];
|
|
3442
|
+
for (let i = 0, len = source.points.length; i < len; i++) {
|
|
3443
|
+
this.points.push(source.points[i].clone());
|
|
3186
3444
|
}
|
|
3187
|
-
this.curves.push(this.currentCurve);
|
|
3188
3445
|
return this;
|
|
3189
3446
|
}
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3447
|
+
}
|
|
3448
|
+
|
|
3449
|
+
class CurvePath extends CompositeCurve {
|
|
3450
|
+
startPoint;
|
|
3451
|
+
currentPoint;
|
|
3452
|
+
autoClose = false;
|
|
3453
|
+
constructor(points) {
|
|
3454
|
+
super();
|
|
3455
|
+
if (points) {
|
|
3456
|
+
this.addPoints(points);
|
|
3198
3457
|
}
|
|
3199
|
-
return this;
|
|
3200
3458
|
}
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
}
|
|
3207
|
-
this.currentCurve.moveTo(x, y);
|
|
3459
|
+
addPoints(points) {
|
|
3460
|
+
this.moveTo(points[0].x, points[0].y);
|
|
3461
|
+
for (let i = 1, len = points.length; i < len; i++) {
|
|
3462
|
+
const { x, y } = points[i];
|
|
3463
|
+
this.lineTo(x, y);
|
|
3208
3464
|
}
|
|
3209
3465
|
return this;
|
|
3210
3466
|
}
|
|
3211
|
-
|
|
3212
|
-
|
|
3467
|
+
addCommands(commands) {
|
|
3468
|
+
svgPathCommandsAddToPath2D(commands, this);
|
|
3213
3469
|
return this;
|
|
3214
3470
|
}
|
|
3215
|
-
|
|
3216
|
-
this.
|
|
3471
|
+
addData(data) {
|
|
3472
|
+
this.addCommands(svgPathDataToCommands(data));
|
|
3217
3473
|
return this;
|
|
3218
3474
|
}
|
|
3219
|
-
|
|
3220
|
-
this.
|
|
3221
|
-
|
|
3475
|
+
_closeVertices(output) {
|
|
3476
|
+
if (this.autoClose && output.length >= 4 && (output[0] !== output[output.length - 2] && output[1] !== output[output.length - 1])) {
|
|
3477
|
+
output.push(output[0], output[1]);
|
|
3478
|
+
}
|
|
3479
|
+
return output;
|
|
3222
3480
|
}
|
|
3223
|
-
|
|
3224
|
-
this.
|
|
3225
|
-
|
|
3481
|
+
getUnevenVertices(count = 40, output = []) {
|
|
3482
|
+
return this._closeVertices(
|
|
3483
|
+
super.getUnevenVertices(count, output)
|
|
3484
|
+
);
|
|
3226
3485
|
}
|
|
3227
|
-
|
|
3228
|
-
this.
|
|
3229
|
-
|
|
3486
|
+
getSpacedVertices(count = 40, output = []) {
|
|
3487
|
+
return this._closeVertices(
|
|
3488
|
+
super.getSpacedVertices(count, output)
|
|
3489
|
+
);
|
|
3230
3490
|
}
|
|
3231
|
-
|
|
3232
|
-
this.
|
|
3491
|
+
getAdaptiveVertices(output = []) {
|
|
3492
|
+
return this._closeVertices(
|
|
3493
|
+
super.getAdaptiveVertices(output)
|
|
3494
|
+
);
|
|
3495
|
+
}
|
|
3496
|
+
getFillVertices(options) {
|
|
3497
|
+
return this._closeVertices(
|
|
3498
|
+
super.getFillVertices(options)
|
|
3499
|
+
);
|
|
3500
|
+
}
|
|
3501
|
+
_setCurrentPoint(point) {
|
|
3502
|
+
this.currentPoint = new Vector2(point.x, point.y);
|
|
3503
|
+
if (!this.startPoint) {
|
|
3504
|
+
this.startPoint = this.currentPoint.clone();
|
|
3505
|
+
}
|
|
3233
3506
|
return this;
|
|
3234
3507
|
}
|
|
3235
|
-
|
|
3236
|
-
this.
|
|
3508
|
+
_connetLineTo(curve) {
|
|
3509
|
+
if (this.curves.length > 0) {
|
|
3510
|
+
const first = curve.getPoint(0);
|
|
3511
|
+
if (!this.currentPoint || !first.equals(this.currentPoint)) {
|
|
3512
|
+
this.lineTo(first.x, first.y);
|
|
3513
|
+
}
|
|
3514
|
+
}
|
|
3237
3515
|
return this;
|
|
3238
3516
|
}
|
|
3239
|
-
|
|
3240
|
-
|
|
3517
|
+
closePath() {
|
|
3518
|
+
const start = this.startPoint;
|
|
3519
|
+
if (start) {
|
|
3520
|
+
const end = this.currentPoint;
|
|
3521
|
+
if (end && !start.equals(end)) {
|
|
3522
|
+
this.curves.push(new LineCurve(end.clone(), start.clone()));
|
|
3523
|
+
end.copy(start);
|
|
3524
|
+
}
|
|
3525
|
+
this.startPoint = void 0;
|
|
3526
|
+
}
|
|
3241
3527
|
return this;
|
|
3242
3528
|
}
|
|
3243
|
-
|
|
3244
|
-
this.
|
|
3245
|
-
this.
|
|
3246
|
-
this.style = {};
|
|
3529
|
+
moveTo(x, y) {
|
|
3530
|
+
this.currentPoint = new Vector2(x, y);
|
|
3531
|
+
this.startPoint = this.currentPoint.clone();
|
|
3247
3532
|
return this;
|
|
3248
3533
|
}
|
|
3249
|
-
|
|
3250
|
-
|
|
3534
|
+
lineTo(x, y) {
|
|
3535
|
+
const start = this.currentPoint;
|
|
3536
|
+
if (!start?.equals({ x, y })) {
|
|
3537
|
+
this.curves.push(
|
|
3538
|
+
LineCurve.from(
|
|
3539
|
+
start?.x ?? 0,
|
|
3540
|
+
start?.y ?? 0,
|
|
3541
|
+
x,
|
|
3542
|
+
y
|
|
3543
|
+
)
|
|
3544
|
+
);
|
|
3545
|
+
}
|
|
3546
|
+
this._setCurrentPoint({ x, y });
|
|
3251
3547
|
return this;
|
|
3252
3548
|
}
|
|
3253
|
-
|
|
3254
|
-
this.
|
|
3549
|
+
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
|
|
3550
|
+
const start = this.currentPoint;
|
|
3551
|
+
if (!start?.equals({ x, y })) {
|
|
3552
|
+
this.curves.push(
|
|
3553
|
+
CubicBezierCurve.from(
|
|
3554
|
+
start?.x ?? 0,
|
|
3555
|
+
start?.y ?? 0,
|
|
3556
|
+
cp1x,
|
|
3557
|
+
cp1y,
|
|
3558
|
+
cp2x,
|
|
3559
|
+
cp2y,
|
|
3560
|
+
x,
|
|
3561
|
+
y
|
|
3562
|
+
)
|
|
3563
|
+
);
|
|
3564
|
+
}
|
|
3565
|
+
this._setCurrentPoint({ x, y });
|
|
3255
3566
|
return this;
|
|
3256
3567
|
}
|
|
3257
|
-
|
|
3258
|
-
this.
|
|
3568
|
+
quadraticCurveTo(cpx, cpy, x, y) {
|
|
3569
|
+
const start = this.currentPoint;
|
|
3570
|
+
if (!start?.equals({ x, y })) {
|
|
3571
|
+
this.curves.push(
|
|
3572
|
+
QuadraticBezierCurve.from(
|
|
3573
|
+
start?.x ?? 0,
|
|
3574
|
+
start?.y ?? 0,
|
|
3575
|
+
cpx,
|
|
3576
|
+
cpy,
|
|
3577
|
+
x,
|
|
3578
|
+
y
|
|
3579
|
+
)
|
|
3580
|
+
);
|
|
3581
|
+
}
|
|
3582
|
+
this._setCurrentPoint({ x, y });
|
|
3259
3583
|
return this;
|
|
3260
3584
|
}
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3585
|
+
arc(x, y, radius, startAngle, endAngle, counterclockwise) {
|
|
3586
|
+
const curve = new ArcCurve(
|
|
3587
|
+
x,
|
|
3588
|
+
y,
|
|
3589
|
+
radius,
|
|
3590
|
+
startAngle,
|
|
3591
|
+
endAngle,
|
|
3592
|
+
!counterclockwise
|
|
3593
|
+
);
|
|
3594
|
+
this._connetLineTo(curve);
|
|
3595
|
+
this.curves.push(curve);
|
|
3596
|
+
this._setCurrentPoint(curve.getPoint(1));
|
|
3265
3597
|
return this;
|
|
3266
3598
|
}
|
|
3267
|
-
|
|
3268
|
-
this.
|
|
3269
|
-
|
|
3270
|
-
|
|
3599
|
+
relativeArc(x, y, radius, startAngle, endAngle, counterclockwise) {
|
|
3600
|
+
x += this.currentPoint?.x ?? 0;
|
|
3601
|
+
y += this.currentPoint?.y ?? 0;
|
|
3602
|
+
this.arc(x, y, radius, startAngle, endAngle, counterclockwise);
|
|
3271
3603
|
return this;
|
|
3272
3604
|
}
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3605
|
+
// TODO
|
|
3606
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
3607
|
+
arcTo(x1, y1, x2, y2, radius) {
|
|
3608
|
+
console.warn("Method arcTo not supported yet");
|
|
3277
3609
|
return this;
|
|
3278
3610
|
}
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
const end = points[points.length - 1] ?? start;
|
|
3294
|
-
_list.push({
|
|
3295
|
-
start: isClockwise ? end : start,
|
|
3296
|
-
end: isClockwise ? start : end,
|
|
3297
|
-
index
|
|
3298
|
-
});
|
|
3299
|
-
});
|
|
3300
|
-
const list = [];
|
|
3301
|
-
_list.forEach((itemA, indexA) => {
|
|
3302
|
-
list[indexA] = [];
|
|
3303
|
-
_list.forEach((itemB, indexB) => {
|
|
3304
|
-
if (itemB.start && itemA.end && indexB !== indexA && itemB.start?.equals(itemA.end)) {
|
|
3305
|
-
list[indexA].push(itemB.index);
|
|
3306
|
-
}
|
|
3307
|
-
});
|
|
3308
|
-
});
|
|
3309
|
-
curves.forEach((curve, index) => {
|
|
3310
|
-
const isClockwise = _isClockwise[index];
|
|
3311
|
-
_points[index].forEach((point) => {
|
|
3312
|
-
point.add(
|
|
3313
|
-
curve.getNormal(
|
|
3314
|
-
curve.getTForPoint(point)
|
|
3315
|
-
).scale(isClockwise ? b : -b)
|
|
3316
|
-
);
|
|
3317
|
-
});
|
|
3318
|
-
});
|
|
3319
|
-
list.forEach((indexes, indexA) => {
|
|
3320
|
-
const pointsA = _points[indexA];
|
|
3321
|
-
indexes.forEach((indexB) => {
|
|
3322
|
-
const pointsB = _points[indexB];
|
|
3323
|
-
const point = getIntersectionPoint(
|
|
3324
|
-
pointsA[pointsA.length - 1],
|
|
3325
|
-
pointsA[pointsA.length - 2] ?? pointsA[pointsA.length - 1],
|
|
3326
|
-
pointsB[0],
|
|
3327
|
-
pointsB[1] ?? pointsB[0]
|
|
3328
|
-
);
|
|
3329
|
-
if (point) {
|
|
3330
|
-
pointsA[pointsA.length - 1].copy(point);
|
|
3331
|
-
pointsB[0].copy(point);
|
|
3332
|
-
}
|
|
3333
|
-
});
|
|
3334
|
-
});
|
|
3611
|
+
ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise = true) {
|
|
3612
|
+
const curve = new EllipseCurve(
|
|
3613
|
+
x,
|
|
3614
|
+
y,
|
|
3615
|
+
radiusX,
|
|
3616
|
+
radiusY,
|
|
3617
|
+
rotation,
|
|
3618
|
+
startAngle,
|
|
3619
|
+
endAngle,
|
|
3620
|
+
!counterclockwise
|
|
3621
|
+
);
|
|
3622
|
+
this._connetLineTo(curve);
|
|
3623
|
+
this.curves.push(curve);
|
|
3624
|
+
this._setCurrentPoint(curve.getPoint(1));
|
|
3335
3625
|
return this;
|
|
3336
3626
|
}
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
this.
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
if (strokeWidth > 1) {
|
|
3343
|
-
const halfStrokeWidth = strokeWidth / 2;
|
|
3344
|
-
const isClockwise = curve.isClockwise();
|
|
3345
|
-
const points = [];
|
|
3346
|
-
for (let t = 0; t <= 1; t += 1 / curve.arcLengthDivision) {
|
|
3347
|
-
const point = curve.getPoint(t);
|
|
3348
|
-
const normal = curve.getNormal(t);
|
|
3349
|
-
const dist1 = normal.clone().scale(isClockwise ? halfStrokeWidth : -halfStrokeWidth);
|
|
3350
|
-
const dist2 = normal.clone().scale(isClockwise ? -halfStrokeWidth : halfStrokeWidth);
|
|
3351
|
-
points.push(
|
|
3352
|
-
point.clone().add(dist1),
|
|
3353
|
-
point.clone().add(dist2),
|
|
3354
|
-
point.clone().add({ x: halfStrokeWidth, y: 0 }),
|
|
3355
|
-
point.clone().add({ x: -halfStrokeWidth, y: 0 }),
|
|
3356
|
-
point.clone().add({ x: 0, y: halfStrokeWidth }),
|
|
3357
|
-
point.clone().add({ x: 0, y: -halfStrokeWidth }),
|
|
3358
|
-
point.clone().add({ x: halfStrokeWidth, y: halfStrokeWidth }),
|
|
3359
|
-
point.clone().add({ x: -halfStrokeWidth, y: -halfStrokeWidth })
|
|
3360
|
-
);
|
|
3361
|
-
}
|
|
3362
|
-
min.min(...points);
|
|
3363
|
-
max.max(...points);
|
|
3364
|
-
}
|
|
3365
|
-
}
|
|
3366
|
-
});
|
|
3367
|
-
return { min: min.finite(), max: max.finite() };
|
|
3627
|
+
relativeEllipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) {
|
|
3628
|
+
x += this.currentPoint?.x ?? 0;
|
|
3629
|
+
y += this.currentPoint?.y ?? 0;
|
|
3630
|
+
this.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
|
|
3631
|
+
return this;
|
|
3368
3632
|
}
|
|
3369
|
-
|
|
3370
|
-
const
|
|
3371
|
-
|
|
3372
|
-
this.curves.
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
indices,
|
|
3376
|
-
vertices,
|
|
3377
|
-
style: { ...this.style }
|
|
3378
|
-
});
|
|
3379
|
-
});
|
|
3380
|
-
return { indices, vertices };
|
|
3633
|
+
rect(x, y, width, height) {
|
|
3634
|
+
const curve = new RectangleCurve(x, y, width, height);
|
|
3635
|
+
this._connetLineTo(curve);
|
|
3636
|
+
this.curves.push(curve);
|
|
3637
|
+
this._setCurrentPoint({ x, y });
|
|
3638
|
+
return this;
|
|
3381
3639
|
}
|
|
3382
|
-
|
|
3383
|
-
const
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
}
|
|
3389
|
-
};
|
|
3390
|
-
const indices = _options.indices ?? [];
|
|
3391
|
-
const vertices = _options.vertices ?? [];
|
|
3392
|
-
const fillRule = _options.style.fillRule ?? "nonzero";
|
|
3393
|
-
if (fillRule === "nonzero") {
|
|
3394
|
-
const pointArrays = this.curves.map((curve) => curve.getFillVertices(_options));
|
|
3395
|
-
const parentMap = /* @__PURE__ */ new Map();
|
|
3396
|
-
const parentd = /* @__PURE__ */ new Set();
|
|
3397
|
-
for (let i = 0; i < pointArrays.length; i++) {
|
|
3398
|
-
const parents = [];
|
|
3399
|
-
for (let j = 0; j < pointArrays.length; j++) {
|
|
3400
|
-
if (i === j)
|
|
3401
|
-
continue;
|
|
3402
|
-
if (pointInPolygon([pointArrays[i][0], pointArrays[i][1]], pointArrays[j])) {
|
|
3403
|
-
parents.push(j);
|
|
3404
|
-
}
|
|
3405
|
-
}
|
|
3406
|
-
if (parents.length) {
|
|
3407
|
-
parents.forEach((pi) => {
|
|
3408
|
-
let set = parentMap.get(pi);
|
|
3409
|
-
if (!set) {
|
|
3410
|
-
set = /* @__PURE__ */ new Set();
|
|
3411
|
-
parentMap.set(pi, set);
|
|
3412
|
-
}
|
|
3413
|
-
set.add(i);
|
|
3414
|
-
});
|
|
3415
|
-
parentd.add(i);
|
|
3416
|
-
}
|
|
3417
|
-
}
|
|
3418
|
-
pointArrays.forEach((pointArray, i) => {
|
|
3419
|
-
if (parentd.has(i) || !pointArray.length) {
|
|
3420
|
-
return;
|
|
3421
|
-
}
|
|
3422
|
-
const _pointArray = pointArray.slice();
|
|
3423
|
-
const holes = [];
|
|
3424
|
-
parentMap.get(i)?.forEach((ci) => {
|
|
3425
|
-
holes.push(_pointArray.length / 2);
|
|
3426
|
-
_pointArray.push(...pointArrays[ci]);
|
|
3427
|
-
});
|
|
3428
|
-
fillTriangulate(_pointArray, {
|
|
3429
|
-
...options,
|
|
3430
|
-
indices,
|
|
3431
|
-
vertices,
|
|
3432
|
-
holes,
|
|
3433
|
-
style: { ...this.style }
|
|
3434
|
-
});
|
|
3435
|
-
});
|
|
3436
|
-
} else {
|
|
3437
|
-
this.curves.forEach((curve) => {
|
|
3438
|
-
curve.fillTriangulate({
|
|
3439
|
-
...options,
|
|
3440
|
-
indices,
|
|
3441
|
-
vertices,
|
|
3442
|
-
style: { ...this.style }
|
|
3443
|
-
});
|
|
3444
|
-
});
|
|
3445
|
-
}
|
|
3446
|
-
return { indices, vertices };
|
|
3640
|
+
roundRect(x, y, width, height, radii) {
|
|
3641
|
+
const curve = new RoundRectangleCurve(x, y, width, height, radii);
|
|
3642
|
+
this._connetLineTo(curve);
|
|
3643
|
+
this.curves.push(curve);
|
|
3644
|
+
this._setCurrentPoint({ x, y });
|
|
3645
|
+
return this;
|
|
3447
3646
|
}
|
|
3448
|
-
|
|
3449
|
-
const
|
|
3450
|
-
|
|
3647
|
+
splineThru(points) {
|
|
3648
|
+
const currentPoint = this.currentPoint ?? new Vector2();
|
|
3649
|
+
this.curves.push(new SplineCurve([currentPoint].concat(points)));
|
|
3650
|
+
this._setCurrentPoint(points[points.length - 1]);
|
|
3651
|
+
return this;
|
|
3451
3652
|
}
|
|
3452
|
-
drawTo(ctx
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
ctx.save();
|
|
3457
|
-
setCanvasContext(ctx, style);
|
|
3458
|
-
this.curves.forEach((path) => {
|
|
3459
|
-
path.drawTo(ctx);
|
|
3460
|
-
});
|
|
3461
|
-
if (fill !== "none") {
|
|
3462
|
-
ctx.fill();
|
|
3653
|
+
drawTo(ctx) {
|
|
3654
|
+
const point = this.curves[0]?.getPoint(0);
|
|
3655
|
+
if (point) {
|
|
3656
|
+
ctx.moveTo(point.x, point.y);
|
|
3463
3657
|
}
|
|
3464
|
-
|
|
3465
|
-
|
|
3658
|
+
this.curves.forEach((curve) => curve.drawTo(ctx));
|
|
3659
|
+
if (this.autoClose) {
|
|
3660
|
+
ctx.closePath();
|
|
3466
3661
|
}
|
|
3467
|
-
ctx.restore();
|
|
3468
3662
|
return this;
|
|
3469
3663
|
}
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
ctx.save();
|
|
3475
|
-
setCanvasContext(ctx, style);
|
|
3476
|
-
this.getControlPointRefs().forEach((point) => {
|
|
3477
|
-
drawPoint(ctx, point.x, point.y, { radius: 4 });
|
|
3478
|
-
});
|
|
3479
|
-
if (fill !== "none") {
|
|
3480
|
-
ctx.fill();
|
|
3481
|
-
}
|
|
3482
|
-
if (stroke !== "none") {
|
|
3483
|
-
ctx.stroke();
|
|
3484
|
-
}
|
|
3485
|
-
ctx.restore();
|
|
3664
|
+
copy(source) {
|
|
3665
|
+
super.copy(source);
|
|
3666
|
+
this.autoClose = source.autoClose;
|
|
3667
|
+
this.currentPoint = source.currentPoint?.clone();
|
|
3486
3668
|
return this;
|
|
3487
3669
|
}
|
|
3488
|
-
|
|
3489
|
-
|
|
3670
|
+
}
|
|
3671
|
+
|
|
3672
|
+
function toKebabCase(str) {
|
|
3673
|
+
return str.replace(/[^a-z0-9]/gi, "-").replace(/\B([A-Z])/g, "-$1").toLowerCase();
|
|
3674
|
+
}
|
|
3675
|
+
function getIntersectionPoint(p1, p2, q1, q2) {
|
|
3676
|
+
const r = p2.clone().sub(p1);
|
|
3677
|
+
const s = q2.clone().sub(q1);
|
|
3678
|
+
const q1p1 = q1.clone().sub(p1);
|
|
3679
|
+
const crossRS = r.cross(s);
|
|
3680
|
+
if (crossRS === 0) {
|
|
3681
|
+
return new Vector2(
|
|
3682
|
+
(p1.x + q1.x) / 2,
|
|
3683
|
+
(p1.y + q1.y) / 2
|
|
3684
|
+
);
|
|
3490
3685
|
}
|
|
3491
|
-
|
|
3492
|
-
|
|
3686
|
+
const t = q1p1.cross(s) / crossRS;
|
|
3687
|
+
if (Math.abs(t) > 1) {
|
|
3688
|
+
return new Vector2(
|
|
3689
|
+
(p1.x + q1.x) / 2,
|
|
3690
|
+
(p1.y + q1.y) / 2
|
|
3691
|
+
);
|
|
3493
3692
|
}
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3693
|
+
return new Vector2(
|
|
3694
|
+
p1.x + t * r.x,
|
|
3695
|
+
p1.y + t * r.y
|
|
3696
|
+
);
|
|
3697
|
+
}
|
|
3698
|
+
|
|
3699
|
+
class Path2D extends CompositeCurve {
|
|
3700
|
+
currentCurve = new CurvePath();
|
|
3701
|
+
style;
|
|
3702
|
+
get startPoint() {
|
|
3703
|
+
return this.currentCurve.startPoint;
|
|
3704
|
+
}
|
|
3705
|
+
get currentPoint() {
|
|
3706
|
+
return this.currentCurve.currentPoint;
|
|
3707
|
+
}
|
|
3708
|
+
get strokeWidth() {
|
|
3709
|
+
return this.style.strokeWidth ?? ((this.style.stroke ?? "none") === "none" ? 0 : 1);
|
|
3710
|
+
}
|
|
3711
|
+
constructor(path, style = {}) {
|
|
3712
|
+
super();
|
|
3713
|
+
this.curves.push(this.currentCurve);
|
|
3714
|
+
this.style = style;
|
|
3715
|
+
if (path) {
|
|
3716
|
+
if (path instanceof Path2D) {
|
|
3717
|
+
this.addPath(path);
|
|
3718
|
+
} else if (Array.isArray(path)) {
|
|
3719
|
+
this.addCommands(path);
|
|
3720
|
+
} else {
|
|
3721
|
+
this.addData(path);
|
|
3513
3722
|
}
|
|
3514
3723
|
}
|
|
3515
|
-
return `<path d="${this.toData()}" style="${cssText}"></path>`;
|
|
3516
3724
|
}
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3725
|
+
addPath(path) {
|
|
3726
|
+
const index = this.curves.findIndex((v) => v === this.currentCurve);
|
|
3727
|
+
if (index > -1) {
|
|
3728
|
+
this.curves.splice(index, 1);
|
|
3729
|
+
}
|
|
3730
|
+
if (path instanceof Path2D) {
|
|
3731
|
+
this.curves.push(
|
|
3732
|
+
...path.curves.filter((curvePath) => curvePath.curves.length).map((v) => v.clone())
|
|
3733
|
+
);
|
|
3734
|
+
} else if (path.curves.length) {
|
|
3735
|
+
this.curves.push(path);
|
|
3736
|
+
}
|
|
3737
|
+
this.curves.push(this.currentCurve);
|
|
3521
3738
|
return this;
|
|
3522
3739
|
}
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
if (!this.paths.length) {
|
|
3532
|
-
return void 0;
|
|
3740
|
+
closePath() {
|
|
3741
|
+
const startPoint = this.startPoint;
|
|
3742
|
+
if (startPoint) {
|
|
3743
|
+
this.currentCurve.closePath();
|
|
3744
|
+
if (this.currentCurve.curves.length) {
|
|
3745
|
+
this.currentCurve = new CurvePath().moveTo(startPoint.x, startPoint.y);
|
|
3746
|
+
this.curves.push(this.currentCurve);
|
|
3747
|
+
}
|
|
3533
3748
|
}
|
|
3534
|
-
|
|
3535
|
-
const max = Vector2.MIN;
|
|
3536
|
-
this.paths.forEach((path) => path.getMinMax(min, max, withStyle));
|
|
3537
|
-
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
3749
|
+
return this;
|
|
3538
3750
|
}
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
results.forEach(({ vertices, indices }) => {
|
|
3545
|
-
const getPoint = (indice) => {
|
|
3546
|
-
const x = vertices[indice * 2];
|
|
3547
|
-
const y = vertices[indice * 2 + 1];
|
|
3548
|
-
min.x = Math.min(min.x, x + padding);
|
|
3549
|
-
max.x = Math.max(max.x, x + padding);
|
|
3550
|
-
min.y = Math.min(min.y, y + padding);
|
|
3551
|
-
max.y = Math.max(max.y, y + padding);
|
|
3552
|
-
return [x, y];
|
|
3553
|
-
};
|
|
3554
|
-
for (let i = 0, len = indices.length; i < len; i += 3) {
|
|
3555
|
-
const p1 = getPoint(indices[i]);
|
|
3556
|
-
const p2 = getPoint(indices[i + 1]);
|
|
3557
|
-
const p3 = getPoint(indices[i + 2]);
|
|
3558
|
-
polygonStr += `<polygon points="${p1.join(",")} ${p2.join(",")} ${p3.join(",")}" fill="black" />`;
|
|
3751
|
+
moveTo(x, y) {
|
|
3752
|
+
if (!this.currentCurve.currentPoint?.equals({ x, y })) {
|
|
3753
|
+
if (this.currentCurve.curves.length) {
|
|
3754
|
+
this.currentCurve = new CurvePath();
|
|
3755
|
+
this.curves.push(this.currentCurve);
|
|
3559
3756
|
}
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
return
|
|
3757
|
+
this.currentCurve.moveTo(x, y);
|
|
3758
|
+
}
|
|
3759
|
+
return this;
|
|
3563
3760
|
}
|
|
3564
|
-
|
|
3565
|
-
|
|
3761
|
+
lineTo(x, y) {
|
|
3762
|
+
this.currentCurve.lineTo(x, y);
|
|
3763
|
+
return this;
|
|
3566
3764
|
}
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
return `<svg viewBox="${x} ${y} ${width} ${height}" width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">${content}</svg>`;
|
|
3765
|
+
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
|
|
3766
|
+
this.currentCurve.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
|
|
3767
|
+
return this;
|
|
3571
3768
|
}
|
|
3572
|
-
|
|
3573
|
-
|
|
3769
|
+
quadraticCurveTo(cpx, cpy, x, y) {
|
|
3770
|
+
this.currentCurve.quadraticCurveTo(cpx, cpy, x, y);
|
|
3771
|
+
return this;
|
|
3574
3772
|
}
|
|
3575
|
-
|
|
3576
|
-
|
|
3773
|
+
arc(x, y, radius, startAngle, endAngle, counterclockwise) {
|
|
3774
|
+
this.currentCurve.arc(x, y, radius, startAngle, endAngle, counterclockwise);
|
|
3775
|
+
return this;
|
|
3577
3776
|
}
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
const canvas = document.createElement("canvas");
|
|
3582
|
-
canvas.width = width * pixelRatio;
|
|
3583
|
-
canvas.height = height * pixelRatio;
|
|
3584
|
-
canvas.style.width = `${width}px`;
|
|
3585
|
-
canvas.style.height = `${height}px`;
|
|
3586
|
-
const ctx = canvas.getContext("2d");
|
|
3587
|
-
if (ctx) {
|
|
3588
|
-
ctx.scale(pixelRatio, pixelRatio);
|
|
3589
|
-
ctx.translate(-left, -top);
|
|
3590
|
-
this.paths.forEach((path) => {
|
|
3591
|
-
path.drawTo(ctx, style);
|
|
3592
|
-
});
|
|
3593
|
-
}
|
|
3594
|
-
return canvas;
|
|
3777
|
+
arcTo(x1, y1, x2, y2, radius) {
|
|
3778
|
+
this.currentCurve.arcTo(x1, y1, x2, y2, radius);
|
|
3779
|
+
return this;
|
|
3595
3780
|
}
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
constructor(rows, cols, width = 1, height = 1) {
|
|
3600
|
-
this.rows = rows;
|
|
3601
|
-
this.cols = cols;
|
|
3602
|
-
this.width = width;
|
|
3603
|
-
this.height = height;
|
|
3604
|
-
for (let i = 0; i < rows; i++) {
|
|
3605
|
-
this.controlPoints[i] = [];
|
|
3606
|
-
for (let j = 0; j < cols; j++) {
|
|
3607
|
-
this.controlPoints[i][j] = {
|
|
3608
|
-
x: j / (cols - 1) * width,
|
|
3609
|
-
y: i / (rows - 1) * height
|
|
3610
|
-
};
|
|
3611
|
-
}
|
|
3612
|
-
}
|
|
3781
|
+
ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) {
|
|
3782
|
+
this.currentCurve.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
|
|
3783
|
+
return this;
|
|
3613
3784
|
}
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
this.controlPoints[i][j].x += dx;
|
|
3617
|
-
this.controlPoints[i][j].y += dy;
|
|
3785
|
+
rect(x, y, width, height) {
|
|
3786
|
+
this.currentCurve.rect(x, y, width, height);
|
|
3618
3787
|
return this;
|
|
3619
3788
|
}
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
B[0] = (1 - t) ** 3 / 6;
|
|
3624
|
-
B[1] = (3 * t ** 3 - 6 * t ** 2 + 4) / 6;
|
|
3625
|
-
B[2] = (-3 * t ** 3 + 3 * t ** 2 + 3 * t + 1) / 6;
|
|
3626
|
-
B[3] = t ** 3 / 6;
|
|
3627
|
-
return B;
|
|
3628
|
-
}
|
|
3629
|
-
function applyFFD(point, grid, width = grid.width, height = grid.height) {
|
|
3630
|
-
const s = point.x / width * (grid.cols - 1);
|
|
3631
|
-
const t = point.y / height * (grid.rows - 1);
|
|
3632
|
-
const i = Math.floor(s);
|
|
3633
|
-
const j = Math.floor(t);
|
|
3634
|
-
const u = s - i;
|
|
3635
|
-
const v = t - j;
|
|
3636
|
-
const bu = bsplineBasis(u);
|
|
3637
|
-
const bv = bsplineBasis(v);
|
|
3638
|
-
let x = 0;
|
|
3639
|
-
let y = 0;
|
|
3640
|
-
for (let m = 0; m < 4; m++) {
|
|
3641
|
-
for (let n = 0; n < 4; n++) {
|
|
3642
|
-
const row = Math.min(Math.max(j - 1 + m, 0), grid.rows - 1);
|
|
3643
|
-
const col = Math.min(Math.max(i - 1 + n, 0), grid.cols - 1);
|
|
3644
|
-
const cp = grid.controlPoints[row][col];
|
|
3645
|
-
const weight = bu[n] * bv[m];
|
|
3646
|
-
x += cp.x * weight;
|
|
3647
|
-
y += cp.y * weight;
|
|
3648
|
-
}
|
|
3789
|
+
roundRect(x, y, width, height, radii) {
|
|
3790
|
+
this.currentCurve.roundRect(x, y, width, height, radii);
|
|
3791
|
+
return this;
|
|
3649
3792
|
}
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
const utf8DataUri = `${dataUri}charset=utf8,`;
|
|
3656
|
-
function svgToDOM(svg) {
|
|
3657
|
-
if (typeof svg === "string") {
|
|
3658
|
-
let xml;
|
|
3659
|
-
if (svg.startsWith(base64DataUri)) {
|
|
3660
|
-
svg = svg.substring(base64DataUri.length, svg.length);
|
|
3661
|
-
xml = atob(svg);
|
|
3662
|
-
} else if (svg.startsWith(utf8DataUri)) {
|
|
3663
|
-
svg = svg.substring(utf8DataUri.length, svg.length);
|
|
3664
|
-
xml = decodeURIComponent(svg);
|
|
3665
|
-
} else {
|
|
3666
|
-
xml = svg;
|
|
3667
|
-
}
|
|
3668
|
-
const doc = new DOMParser().parseFromString(xml, "text/xml");
|
|
3669
|
-
const error = doc.querySelector("parsererror");
|
|
3670
|
-
if (error) {
|
|
3671
|
-
throw new Error(`${error.textContent ?? "parser error"}
|
|
3672
|
-
${xml}`);
|
|
3673
|
-
}
|
|
3674
|
-
return doc.documentElement;
|
|
3675
|
-
} else {
|
|
3676
|
-
return svg;
|
|
3793
|
+
reset() {
|
|
3794
|
+
this.currentCurve = new CurvePath();
|
|
3795
|
+
this.curves = [this.currentCurve];
|
|
3796
|
+
this.style = {};
|
|
3797
|
+
return this;
|
|
3677
3798
|
}
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
mm: 1,
|
|
3686
|
-
cm: 0.1,
|
|
3687
|
-
in: 1 / 25.4,
|
|
3688
|
-
pt: 72 / 25.4,
|
|
3689
|
-
pc: 6 / 25.4,
|
|
3690
|
-
px: -1
|
|
3691
|
-
},
|
|
3692
|
-
cm: {
|
|
3693
|
-
mm: 10,
|
|
3694
|
-
cm: 1,
|
|
3695
|
-
in: 1 / 2.54,
|
|
3696
|
-
pt: 72 / 2.54,
|
|
3697
|
-
pc: 6 / 2.54,
|
|
3698
|
-
px: -1
|
|
3699
|
-
},
|
|
3700
|
-
in: {
|
|
3701
|
-
mm: 25.4,
|
|
3702
|
-
cm: 2.54,
|
|
3703
|
-
in: 1,
|
|
3704
|
-
pt: 72,
|
|
3705
|
-
pc: 6,
|
|
3706
|
-
px: -1
|
|
3707
|
-
},
|
|
3708
|
-
pt: {
|
|
3709
|
-
mm: 25.4 / 72,
|
|
3710
|
-
cm: 2.54 / 72,
|
|
3711
|
-
in: 1 / 72,
|
|
3712
|
-
pt: 1,
|
|
3713
|
-
pc: 6 / 72,
|
|
3714
|
-
px: -1
|
|
3715
|
-
},
|
|
3716
|
-
pc: {
|
|
3717
|
-
mm: 25.4 / 6,
|
|
3718
|
-
cm: 2.54 / 6,
|
|
3719
|
-
in: 1 / 6,
|
|
3720
|
-
pt: 72 / 6,
|
|
3721
|
-
pc: 1,
|
|
3722
|
-
px: -1
|
|
3723
|
-
},
|
|
3724
|
-
px: {
|
|
3725
|
-
px: 1
|
|
3799
|
+
addCommands(commands) {
|
|
3800
|
+
svgPathCommandsAddToPath2D(commands, this);
|
|
3801
|
+
return this;
|
|
3802
|
+
}
|
|
3803
|
+
addData(data) {
|
|
3804
|
+
this.addCommands(svgPathDataToCommands(data));
|
|
3805
|
+
return this;
|
|
3726
3806
|
}
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
if (typeof string === "string") {
|
|
3731
|
-
for (let i = 0, n = units.length; i < n; i++) {
|
|
3732
|
-
const u = units[i];
|
|
3733
|
-
if (string.endsWith(u)) {
|
|
3734
|
-
theUnit = u;
|
|
3735
|
-
string = string.substring(0, string.length - u.length);
|
|
3736
|
-
break;
|
|
3737
|
-
}
|
|
3738
|
-
}
|
|
3807
|
+
splineThru(points) {
|
|
3808
|
+
this.currentCurve.splineThru(points);
|
|
3809
|
+
return this;
|
|
3739
3810
|
}
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
}
|
|
3811
|
+
scale(sx, sy = sx, target = { x: 0, y: 0 }) {
|
|
3812
|
+
this.getControlPointRefs().forEach((point) => {
|
|
3813
|
+
point.scale(sx, sy, target);
|
|
3814
|
+
});
|
|
3815
|
+
return this;
|
|
3746
3816
|
}
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
const tempTransform2 = new Matrix3();
|
|
3753
|
-
const tempTransform3 = new Matrix3();
|
|
3754
|
-
function getNodeTransform(node, currentTransform, transformStack) {
|
|
3755
|
-
if (!(node.hasAttribute("transform") || node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y")))) {
|
|
3756
|
-
return null;
|
|
3817
|
+
skew(ax, ay = 0, target = { x: 0, y: 0 }) {
|
|
3818
|
+
this.getControlPointRefs().forEach((point) => {
|
|
3819
|
+
point.skew(ax, ay, target);
|
|
3820
|
+
});
|
|
3821
|
+
return this;
|
|
3757
3822
|
}
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3823
|
+
rotate(a, target = { x: 0, y: 0 }) {
|
|
3824
|
+
this.getControlPointRefs().forEach((point) => {
|
|
3825
|
+
point.rotate(a, target);
|
|
3826
|
+
});
|
|
3827
|
+
return this;
|
|
3761
3828
|
}
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
}
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3829
|
+
bold(b) {
|
|
3830
|
+
if (b === 0) {
|
|
3831
|
+
return this;
|
|
3832
|
+
}
|
|
3833
|
+
const curves = this.getFlatCurves();
|
|
3834
|
+
const _list = [];
|
|
3835
|
+
const _isClockwise = [];
|
|
3836
|
+
const _points = [];
|
|
3837
|
+
curves.forEach((curve, index) => {
|
|
3838
|
+
const points = curve.getControlPointRefs();
|
|
3839
|
+
const isClockwise = curve.isClockwise();
|
|
3840
|
+
_points[index] = points;
|
|
3841
|
+
_isClockwise[index] = isClockwise;
|
|
3842
|
+
const start = points[0];
|
|
3843
|
+
const end = points[points.length - 1] ?? start;
|
|
3844
|
+
_list.push({
|
|
3845
|
+
start: isClockwise ? end : start,
|
|
3846
|
+
end: isClockwise ? start : end,
|
|
3847
|
+
index
|
|
3848
|
+
});
|
|
3849
|
+
});
|
|
3850
|
+
const list = [];
|
|
3851
|
+
_list.forEach((itemA, indexA) => {
|
|
3852
|
+
list[indexA] = [];
|
|
3853
|
+
_list.forEach((itemB, indexB) => {
|
|
3854
|
+
if (itemB.start && itemA.end && indexB !== indexA && itemB.start?.equals(itemA.end)) {
|
|
3855
|
+
list[indexA].push(itemB.index);
|
|
3856
|
+
}
|
|
3857
|
+
});
|
|
3858
|
+
});
|
|
3859
|
+
curves.forEach((curve, index) => {
|
|
3860
|
+
const isClockwise = _isClockwise[index];
|
|
3861
|
+
_points[index].forEach((point) => {
|
|
3862
|
+
point.add(
|
|
3863
|
+
curve.getNormal(
|
|
3864
|
+
curve.getTForPoint(point)
|
|
3865
|
+
).scale(isClockwise ? b : -b)
|
|
3866
|
+
);
|
|
3867
|
+
});
|
|
3868
|
+
});
|
|
3869
|
+
list.forEach((indexes, indexA) => {
|
|
3870
|
+
const pointsA = _points[indexA];
|
|
3871
|
+
indexes.forEach((indexB) => {
|
|
3872
|
+
const pointsB = _points[indexB];
|
|
3873
|
+
const point = getIntersectionPoint(
|
|
3874
|
+
pointsA[pointsA.length - 1],
|
|
3875
|
+
pointsA[pointsA.length - 2] ?? pointsA[pointsA.length - 1],
|
|
3876
|
+
pointsB[0],
|
|
3877
|
+
pointsB[1] ?? pointsB[0]
|
|
3878
|
+
);
|
|
3879
|
+
if (point) {
|
|
3880
|
+
pointsA[pointsA.length - 1].copy(point);
|
|
3881
|
+
pointsB[0].copy(point);
|
|
3882
|
+
}
|
|
3883
|
+
});
|
|
3884
|
+
});
|
|
3885
|
+
return this;
|
|
3774
3886
|
}
|
|
3775
|
-
|
|
3776
|
-
const
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
if (
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
}
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
if (array.length === 6) {
|
|
3855
|
-
currentTransform.set(
|
|
3856
|
-
array[0],
|
|
3857
|
-
array[2],
|
|
3858
|
-
array[4],
|
|
3859
|
-
array[1],
|
|
3860
|
-
array[3],
|
|
3861
|
-
array[5],
|
|
3862
|
-
0,
|
|
3863
|
-
0,
|
|
3864
|
-
1
|
|
3865
|
-
);
|
|
3887
|
+
getMinMax(min = Vector2.MAX, max = Vector2.MIN, withStyle = true) {
|
|
3888
|
+
const strokeWidth = this.strokeWidth;
|
|
3889
|
+
this.curves.forEach((curve) => {
|
|
3890
|
+
curve.getMinMax(min, max);
|
|
3891
|
+
if (withStyle) {
|
|
3892
|
+
if (strokeWidth > 1) {
|
|
3893
|
+
const halfStrokeWidth = strokeWidth / 2;
|
|
3894
|
+
const isClockwise = curve.isClockwise();
|
|
3895
|
+
const points = [];
|
|
3896
|
+
for (let t = 0; t <= 1; t += 1 / curve.arcLengthDivision) {
|
|
3897
|
+
const point = curve.getPoint(t);
|
|
3898
|
+
const normal = curve.getNormal(t);
|
|
3899
|
+
const dist1 = normal.clone().scale(isClockwise ? halfStrokeWidth : -halfStrokeWidth);
|
|
3900
|
+
const dist2 = normal.clone().scale(isClockwise ? -halfStrokeWidth : halfStrokeWidth);
|
|
3901
|
+
points.push(
|
|
3902
|
+
point.clone().add(dist1),
|
|
3903
|
+
point.clone().add(dist2),
|
|
3904
|
+
point.clone().add({ x: halfStrokeWidth, y: 0 }),
|
|
3905
|
+
point.clone().add({ x: -halfStrokeWidth, y: 0 }),
|
|
3906
|
+
point.clone().add({ x: 0, y: halfStrokeWidth }),
|
|
3907
|
+
point.clone().add({ x: 0, y: -halfStrokeWidth }),
|
|
3908
|
+
point.clone().add({ x: halfStrokeWidth, y: halfStrokeWidth }),
|
|
3909
|
+
point.clone().add({ x: -halfStrokeWidth, y: -halfStrokeWidth })
|
|
3910
|
+
);
|
|
3911
|
+
}
|
|
3912
|
+
min.min(...points);
|
|
3913
|
+
max.max(...points);
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3916
|
+
});
|
|
3917
|
+
return { min: min.finite(), max: max.finite() };
|
|
3918
|
+
}
|
|
3919
|
+
strokeTriangulate(options) {
|
|
3920
|
+
const indices = options?.indices ?? [];
|
|
3921
|
+
const vertices = options?.vertices ?? [];
|
|
3922
|
+
this.curves.forEach((curve) => {
|
|
3923
|
+
curve.strokeTriangulate({
|
|
3924
|
+
...options,
|
|
3925
|
+
indices,
|
|
3926
|
+
vertices,
|
|
3927
|
+
style: { ...this.style }
|
|
3928
|
+
});
|
|
3929
|
+
});
|
|
3930
|
+
return { indices, vertices };
|
|
3931
|
+
}
|
|
3932
|
+
fillTriangulate(options) {
|
|
3933
|
+
const _options = {
|
|
3934
|
+
...options,
|
|
3935
|
+
style: {
|
|
3936
|
+
...this.style,
|
|
3937
|
+
...options?.style
|
|
3938
|
+
}
|
|
3939
|
+
};
|
|
3940
|
+
const indices = _options.indices ?? [];
|
|
3941
|
+
const vertices = _options.vertices ?? [];
|
|
3942
|
+
const fillRule = _options.style.fillRule ?? "nonzero";
|
|
3943
|
+
if (fillRule === "nonzero") {
|
|
3944
|
+
const pointArrays = this.curves.map((curve) => curve.getFillVertices(_options));
|
|
3945
|
+
const parentMap = /* @__PURE__ */ new Map();
|
|
3946
|
+
const parentd = /* @__PURE__ */ new Set();
|
|
3947
|
+
for (let i = 0; i < pointArrays.length; i++) {
|
|
3948
|
+
const parents = [];
|
|
3949
|
+
for (let j = 0; j < pointArrays.length; j++) {
|
|
3950
|
+
if (i === j)
|
|
3951
|
+
continue;
|
|
3952
|
+
let flag = false;
|
|
3953
|
+
for (let k = 0; k < pointArrays[i].length; k += 2) {
|
|
3954
|
+
flag = flag || pointInPolygonNonZero([pointArrays[i][k], pointArrays[i][k + 1]], pointArrays[j]);
|
|
3955
|
+
}
|
|
3956
|
+
if (flag) {
|
|
3957
|
+
parents.push(j);
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
if (parents.length) {
|
|
3961
|
+
parents.forEach((pi) => {
|
|
3962
|
+
let set = parentMap.get(pi);
|
|
3963
|
+
if (!set) {
|
|
3964
|
+
set = /* @__PURE__ */ new Set();
|
|
3965
|
+
parentMap.set(pi, set);
|
|
3866
3966
|
}
|
|
3867
|
-
|
|
3967
|
+
set.add(i);
|
|
3968
|
+
});
|
|
3969
|
+
parentd.add(i);
|
|
3868
3970
|
}
|
|
3869
3971
|
}
|
|
3870
|
-
|
|
3972
|
+
pointArrays.forEach((pointArray, i) => {
|
|
3973
|
+
if (parentd.has(i) || !pointArray.length) {
|
|
3974
|
+
return;
|
|
3975
|
+
}
|
|
3976
|
+
const _pointArray = pointArray.slice();
|
|
3977
|
+
const holes = [];
|
|
3978
|
+
parentMap.get(i)?.forEach((ci) => {
|
|
3979
|
+
holes.push(_pointArray.length / 2);
|
|
3980
|
+
_pointArray.push(...pointArrays[ci]);
|
|
3981
|
+
});
|
|
3982
|
+
fillTriangulate(_pointArray, {
|
|
3983
|
+
...options,
|
|
3984
|
+
indices,
|
|
3985
|
+
vertices,
|
|
3986
|
+
holes,
|
|
3987
|
+
style: { ...this.style }
|
|
3988
|
+
});
|
|
3989
|
+
});
|
|
3990
|
+
} else {
|
|
3991
|
+
this.curves.forEach((curve) => {
|
|
3992
|
+
curve.fillTriangulate({
|
|
3993
|
+
...options,
|
|
3994
|
+
indices,
|
|
3995
|
+
vertices,
|
|
3996
|
+
style: { ...this.style }
|
|
3997
|
+
});
|
|
3998
|
+
});
|
|
3871
3999
|
}
|
|
4000
|
+
return { indices, vertices };
|
|
3872
4001
|
}
|
|
3873
|
-
|
|
3874
|
-
}
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
return;
|
|
3889
|
-
for (let i = 0; i < node.sheet.cssRules.length; i++) {
|
|
3890
|
-
const stylesheet = node.sheet.cssRules[i];
|
|
3891
|
-
if (stylesheet.type !== 1)
|
|
3892
|
-
continue;
|
|
3893
|
-
const selectorList = stylesheet.selectorText.split(/,/g).filter(Boolean).map((i2) => i2.trim());
|
|
3894
|
-
const definitions = {};
|
|
3895
|
-
for (let len = stylesheet.style.length, i2 = 0; i2 < len; i2++) {
|
|
3896
|
-
const name = stylesheet.style.item(i2);
|
|
3897
|
-
definitions[name] = stylesheet.style.getPropertyValue(name);
|
|
4002
|
+
getBoundingBox(withStyle = true) {
|
|
4003
|
+
const { min, max } = this.getMinMax(void 0, void 0, withStyle);
|
|
4004
|
+
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
4005
|
+
}
|
|
4006
|
+
drawTo(ctx, style = {}) {
|
|
4007
|
+
style = { ...this.style, ...style };
|
|
4008
|
+
const { fill = "#000", stroke = "none" } = style;
|
|
4009
|
+
ctx.beginPath();
|
|
4010
|
+
ctx.save();
|
|
4011
|
+
setCanvasContext(ctx, style);
|
|
4012
|
+
this.curves.forEach((path) => {
|
|
4013
|
+
path.drawTo(ctx);
|
|
4014
|
+
});
|
|
4015
|
+
if (fill !== "none") {
|
|
4016
|
+
ctx.fill();
|
|
3898
4017
|
}
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
stylesheets[selectorList[j]] || {},
|
|
3902
|
-
{ ...definitions }
|
|
3903
|
-
);
|
|
4018
|
+
if (stroke !== "none") {
|
|
4019
|
+
ctx.stroke();
|
|
3904
4020
|
}
|
|
4021
|
+
ctx.restore();
|
|
4022
|
+
return this;
|
|
3905
4023
|
}
|
|
3906
|
-
}
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
);
|
|
3918
|
-
}
|
|
3919
|
-
|
|
3920
|
-
function parseLineNode(node) {
|
|
3921
|
-
return new Path2D().moveTo(
|
|
3922
|
-
parseFloatWithUnits(node.getAttribute("x1") || 0),
|
|
3923
|
-
parseFloatWithUnits(node.getAttribute("y1") || 0)
|
|
3924
|
-
).lineTo(
|
|
3925
|
-
parseFloatWithUnits(node.getAttribute("x2") || 0),
|
|
3926
|
-
parseFloatWithUnits(node.getAttribute("y2") || 0)
|
|
3927
|
-
);
|
|
3928
|
-
}
|
|
3929
|
-
|
|
3930
|
-
function parsePathNode(node) {
|
|
3931
|
-
const path = new Path2D();
|
|
3932
|
-
const d = node.getAttribute("d");
|
|
3933
|
-
if (!d || d === "none")
|
|
3934
|
-
return null;
|
|
3935
|
-
path.addData(d);
|
|
3936
|
-
return path;
|
|
3937
|
-
}
|
|
3938
|
-
|
|
3939
|
-
const RE$1 = /([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
|
|
3940
|
-
function parsePolygonNode(node) {
|
|
3941
|
-
const path = new Path2D();
|
|
3942
|
-
let index = 0;
|
|
3943
|
-
node.getAttribute("points")?.replace(RE$1, (match, a, b) => {
|
|
3944
|
-
const x = parseFloatWithUnits(a);
|
|
3945
|
-
const y = parseFloatWithUnits(b);
|
|
3946
|
-
if (index === 0) {
|
|
3947
|
-
path.moveTo(x, y);
|
|
3948
|
-
} else {
|
|
3949
|
-
path.lineTo(x, y);
|
|
4024
|
+
drawControlPointsTo(ctx, style = {}) {
|
|
4025
|
+
style = { ...this.style, ...style };
|
|
4026
|
+
const { fill = "#000", stroke = "none" } = style;
|
|
4027
|
+
ctx.beginPath();
|
|
4028
|
+
ctx.save();
|
|
4029
|
+
setCanvasContext(ctx, style);
|
|
4030
|
+
this.getControlPointRefs().forEach((point) => {
|
|
4031
|
+
drawPoint(ctx, point.x, point.y, { radius: 4 });
|
|
4032
|
+
});
|
|
4033
|
+
if (fill !== "none") {
|
|
4034
|
+
ctx.fill();
|
|
3950
4035
|
}
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
});
|
|
3954
|
-
path.currentCurve.autoClose = true;
|
|
3955
|
-
return path;
|
|
3956
|
-
}
|
|
3957
|
-
|
|
3958
|
-
const RE = /([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
|
|
3959
|
-
function parsePolylineNode(node) {
|
|
3960
|
-
const path = new Path2D();
|
|
3961
|
-
let index = 0;
|
|
3962
|
-
node.getAttribute("points")?.replace(RE, (match, a, b) => {
|
|
3963
|
-
const x = parseFloatWithUnits(a);
|
|
3964
|
-
const y = parseFloatWithUnits(b);
|
|
3965
|
-
if (index === 0) {
|
|
3966
|
-
path.moveTo(x, y);
|
|
3967
|
-
} else {
|
|
3968
|
-
path.lineTo(x, y);
|
|
4036
|
+
if (stroke !== "none") {
|
|
4037
|
+
ctx.stroke();
|
|
3969
4038
|
}
|
|
3970
|
-
|
|
3971
|
-
return
|
|
3972
|
-
});
|
|
3973
|
-
path.currentCurve.autoClose = false;
|
|
3974
|
-
return path;
|
|
3975
|
-
}
|
|
3976
|
-
|
|
3977
|
-
function parseRectNode(node) {
|
|
3978
|
-
const x = parseFloatWithUnits(node.getAttribute("x") || 0);
|
|
3979
|
-
const y = parseFloatWithUnits(node.getAttribute("y") || 0);
|
|
3980
|
-
const rx = parseFloatWithUnits(node.getAttribute("rx") || node.getAttribute("ry") || 0);
|
|
3981
|
-
const ry = parseFloatWithUnits(node.getAttribute("ry") || node.getAttribute("rx") || 0);
|
|
3982
|
-
const w = parseFloatWithUnits(node.getAttribute("width"));
|
|
3983
|
-
const h = parseFloatWithUnits(node.getAttribute("height"));
|
|
3984
|
-
const bci = 1 - 0.551915024494;
|
|
3985
|
-
const path = new Path2D();
|
|
3986
|
-
path.moveTo(x + rx, y);
|
|
3987
|
-
path.lineTo(x + w - rx, y);
|
|
3988
|
-
if (rx !== 0 || ry !== 0) {
|
|
3989
|
-
path.bezierCurveTo(
|
|
3990
|
-
x + w - rx * bci,
|
|
3991
|
-
y,
|
|
3992
|
-
x + w,
|
|
3993
|
-
y + ry * bci,
|
|
3994
|
-
x + w,
|
|
3995
|
-
y + ry
|
|
3996
|
-
);
|
|
4039
|
+
ctx.restore();
|
|
4040
|
+
return this;
|
|
3997
4041
|
}
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
path.bezierCurveTo(
|
|
4001
|
-
x + w,
|
|
4002
|
-
y + h - ry * bci,
|
|
4003
|
-
x + w - rx * bci,
|
|
4004
|
-
y + h,
|
|
4005
|
-
x + w - rx,
|
|
4006
|
-
y + h
|
|
4007
|
-
);
|
|
4042
|
+
toCommands() {
|
|
4043
|
+
return this.curves.flatMap((v) => v.toCommands());
|
|
4008
4044
|
}
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4045
|
+
toData() {
|
|
4046
|
+
return this.curves.filter((v) => v.curves.length).map((v) => v.toData()).join(" ");
|
|
4047
|
+
}
|
|
4048
|
+
toSvgPathString() {
|
|
4049
|
+
const style = {
|
|
4050
|
+
...this.style,
|
|
4051
|
+
fill: this.style.fill ?? "#000",
|
|
4052
|
+
stroke: this.style.stroke ?? "none"
|
|
4053
|
+
};
|
|
4054
|
+
const cssStyle = {};
|
|
4055
|
+
for (const key in style) {
|
|
4056
|
+
if (style[key] !== void 0) {
|
|
4057
|
+
cssStyle[toKebabCase(key)] = style[key];
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
Object.assign(cssStyle, {
|
|
4061
|
+
"stroke-width": `${this.strokeWidth}px`
|
|
4062
|
+
});
|
|
4063
|
+
let cssText = "";
|
|
4064
|
+
for (const key in cssStyle) {
|
|
4065
|
+
if (cssStyle[key] !== void 0) {
|
|
4066
|
+
cssText += `${key}:${cssStyle[key]};`;
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
return `<path d="${this.toData()}" style="${cssText}"></path>`;
|
|
4019
4070
|
}
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4071
|
+
copy(source) {
|
|
4072
|
+
super.copy(source);
|
|
4073
|
+
this.currentCurve = source.currentCurve.clone();
|
|
4074
|
+
this.style = { ...source.style };
|
|
4075
|
+
return this;
|
|
4023
4076
|
}
|
|
4024
|
-
return path;
|
|
4025
4077
|
}
|
|
4026
4078
|
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
const classSelectors = node.getAttribute("class").split(/\s/).filter(Boolean).map((i) => i.trim());
|
|
4032
|
-
for (let i = 0; i < classSelectors.length; i++) {
|
|
4033
|
-
stylesheetStyles = Object.assign(stylesheetStyles, stylesheets[`.${classSelectors[i]}`]);
|
|
4034
|
-
}
|
|
4079
|
+
class Path2DSet {
|
|
4080
|
+
constructor(paths = [], viewBox) {
|
|
4081
|
+
this.paths = paths;
|
|
4082
|
+
this.viewBox = viewBox;
|
|
4035
4083
|
}
|
|
4036
|
-
|
|
4037
|
-
|
|
4084
|
+
getBoundingBox(withStyle = true) {
|
|
4085
|
+
if (!this.paths.length) {
|
|
4086
|
+
return void 0;
|
|
4087
|
+
}
|
|
4088
|
+
const min = Vector2.MAX;
|
|
4089
|
+
const max = Vector2.MIN;
|
|
4090
|
+
this.paths.forEach((path) => path.getMinMax(min, max, withStyle));
|
|
4091
|
+
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
4038
4092
|
}
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
const
|
|
4042
|
-
|
|
4043
|
-
|
|
4093
|
+
toTriangulatedSvgString(result = this.paths.map((p) => p.fillTriangulate()), padding = 0) {
|
|
4094
|
+
let polygonStr = "";
|
|
4095
|
+
const min = { x: -padding, y: -padding };
|
|
4096
|
+
const max = { x: padding, y: padding };
|
|
4097
|
+
const results = Array.isArray(result) ? result : [result];
|
|
4098
|
+
results.forEach(({ vertices, indices }) => {
|
|
4099
|
+
const getPoint = (indice) => {
|
|
4100
|
+
const x = vertices[indice * 2];
|
|
4101
|
+
const y = vertices[indice * 2 + 1];
|
|
4102
|
+
min.x = Math.min(min.x, x + padding);
|
|
4103
|
+
max.x = Math.max(max.x, x + padding);
|
|
4104
|
+
min.y = Math.min(min.y, y + padding);
|
|
4105
|
+
max.y = Math.max(max.y, y + padding);
|
|
4106
|
+
return [x, y];
|
|
4107
|
+
};
|
|
4108
|
+
for (let i = 0, len = indices.length; i < len; i += 3) {
|
|
4109
|
+
const p1 = getPoint(indices[i]);
|
|
4110
|
+
const p2 = getPoint(indices[i + 1]);
|
|
4111
|
+
const p3 = getPoint(indices[i + 2]);
|
|
4112
|
+
polygonStr += `<polygon points="${p1.join(",")} ${p2.join(",")} ${p3.join(",")}" fill="black" />`;
|
|
4113
|
+
}
|
|
4114
|
+
});
|
|
4115
|
+
const viewBox = [min.x, min.y, max.x - min.x, max.y - min.y];
|
|
4116
|
+
return `<svg width="${viewBox[2]}" height="${viewBox[3]}" viewBox="${viewBox.join(" ")}" xmlns="http://www.w3.org/2000/svg">${polygonStr}</svg>`;
|
|
4044
4117
|
}
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
style[jsName] = adjustFunction(node.getAttribute(svgName));
|
|
4048
|
-
if (stylesheetStyles[svgName])
|
|
4049
|
-
style[jsName] = adjustFunction(stylesheetStyles[svgName]);
|
|
4118
|
+
toTriangulatedSvg(result, padding) {
|
|
4119
|
+
return new DOMParser().parseFromString(this.toTriangulatedSvgString(result, padding), "image/svg+xml").documentElement;
|
|
4050
4120
|
}
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
return
|
|
4121
|
+
toSvgString() {
|
|
4122
|
+
const { x, y, width, height } = this.getBoundingBox();
|
|
4123
|
+
const content = this.paths.map((path) => path.toSvgPathString()).join("");
|
|
4124
|
+
return `<svg viewBox="${x} ${y} ${width} ${height}" width="${width}px" height="${height}px" xmlns="http://www.w3.org/2000/svg">${content}</svg>`;
|
|
4055
4125
|
}
|
|
4056
|
-
|
|
4057
|
-
return
|
|
4126
|
+
toSvgUrl() {
|
|
4127
|
+
return `data:image/svg+xml;base64,${btoa(this.toSvgString())}`;
|
|
4058
4128
|
}
|
|
4059
|
-
|
|
4060
|
-
return
|
|
4129
|
+
toSvg() {
|
|
4130
|
+
return new DOMParser().parseFromString(this.toSvgString(), "image/svg+xml").documentElement;
|
|
4061
4131
|
}
|
|
4062
|
-
|
|
4063
|
-
|
|
4132
|
+
toCanvas(options = {}) {
|
|
4133
|
+
const { pixelRatio = 2, ...style } = options;
|
|
4134
|
+
const { left, top, width, height } = this.getBoundingBox();
|
|
4135
|
+
const canvas = document.createElement("canvas");
|
|
4136
|
+
canvas.width = width * pixelRatio;
|
|
4137
|
+
canvas.height = height * pixelRatio;
|
|
4138
|
+
canvas.style.width = `${width}px`;
|
|
4139
|
+
canvas.style.height = `${height}px`;
|
|
4140
|
+
const ctx = canvas.getContext("2d");
|
|
4141
|
+
if (ctx) {
|
|
4142
|
+
ctx.scale(pixelRatio, pixelRatio);
|
|
4143
|
+
ctx.translate(-left, -top);
|
|
4144
|
+
this.paths.forEach((path) => {
|
|
4145
|
+
path.drawTo(ctx, style);
|
|
4146
|
+
});
|
|
4147
|
+
}
|
|
4148
|
+
return canvas;
|
|
4064
4149
|
}
|
|
4065
|
-
addStyle("fill", "fill");
|
|
4066
|
-
addStyle("fill-opacity", "fillOpacity", clamp);
|
|
4067
|
-
addStyle("fill-rule", "fillRule");
|
|
4068
|
-
addStyle("opacity", "opacity", clamp);
|
|
4069
|
-
addStyle("stroke", "stroke");
|
|
4070
|
-
addStyle("stroke-opacity", "strokeOpacity", clamp);
|
|
4071
|
-
addStyle("stroke-width", "strokeWidth", positive);
|
|
4072
|
-
addStyle("stroke-linecap", "strokeLinecap");
|
|
4073
|
-
addStyle("stroke-linejoin", "strokeLinejoin");
|
|
4074
|
-
addStyle("stroke-miterlimit", "strokeMiterlimit", positive);
|
|
4075
|
-
addStyle("stroke-dasharray", "strokeDasharray", array);
|
|
4076
|
-
addStyle("stroke-dashoffset", "strokeDashoffset", parseFloatWithUnits);
|
|
4077
|
-
addStyle("visibility", "visibility");
|
|
4078
|
-
return style;
|
|
4079
4150
|
}
|
|
4080
4151
|
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
case "g":
|
|
4095
|
-
_style = parseStyle(node, _style, stylesheets);
|
|
4096
|
-
break;
|
|
4097
|
-
case "path":
|
|
4098
|
-
_style = parseStyle(node, _style, stylesheets);
|
|
4099
|
-
if (node.hasAttribute("d"))
|
|
4100
|
-
path = parsePathNode(node);
|
|
4101
|
-
break;
|
|
4102
|
-
case "rect":
|
|
4103
|
-
_style = parseStyle(node, _style, stylesheets);
|
|
4104
|
-
path = parseRectNode(node);
|
|
4105
|
-
break;
|
|
4106
|
-
case "polygon":
|
|
4107
|
-
_style = parseStyle(node, _style, stylesheets);
|
|
4108
|
-
path = parsePolygonNode(node);
|
|
4109
|
-
break;
|
|
4110
|
-
case "polyline":
|
|
4111
|
-
_style = parseStyle(node, _style, stylesheets);
|
|
4112
|
-
path = parsePolylineNode(node);
|
|
4113
|
-
break;
|
|
4114
|
-
case "circle":
|
|
4115
|
-
_style = parseStyle(node, _style, stylesheets);
|
|
4116
|
-
path = parseCircleNode(node);
|
|
4117
|
-
break;
|
|
4118
|
-
case "ellipse":
|
|
4119
|
-
_style = parseStyle(node, _style, stylesheets);
|
|
4120
|
-
path = parseEllipseNode(node);
|
|
4121
|
-
break;
|
|
4122
|
-
case "line":
|
|
4123
|
-
_style = parseStyle(node, _style, stylesheets);
|
|
4124
|
-
path = parseLineNode(node);
|
|
4125
|
-
break;
|
|
4126
|
-
case "defs":
|
|
4127
|
-
isDefsNode = true;
|
|
4128
|
-
break;
|
|
4129
|
-
case "use": {
|
|
4130
|
-
_style = parseStyle(node, _style, stylesheets);
|
|
4131
|
-
const href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href") || node.getAttribute("href") || "";
|
|
4132
|
-
const usedNodeId = href.substring(1);
|
|
4133
|
-
const usedNode = node.viewportElement?.getElementById(usedNodeId);
|
|
4134
|
-
if (usedNode) {
|
|
4135
|
-
parseNode(usedNode, _style, paths, stylesheets);
|
|
4136
|
-
} else {
|
|
4137
|
-
console.warn(`'use node' references non-existent node id: ${usedNodeId}`);
|
|
4152
|
+
class FFDControlGrid {
|
|
4153
|
+
constructor(rows, cols, width = 1, height = 1) {
|
|
4154
|
+
this.rows = rows;
|
|
4155
|
+
this.cols = cols;
|
|
4156
|
+
this.width = width;
|
|
4157
|
+
this.height = height;
|
|
4158
|
+
for (let i = 0; i < rows; i++) {
|
|
4159
|
+
this.controlPoints[i] = [];
|
|
4160
|
+
for (let j = 0; j < cols; j++) {
|
|
4161
|
+
this.controlPoints[i][j] = {
|
|
4162
|
+
x: j / (cols - 1) * width,
|
|
4163
|
+
y: i / (rows - 1) * height
|
|
4164
|
+
};
|
|
4138
4165
|
}
|
|
4139
|
-
break;
|
|
4140
4166
|
}
|
|
4141
|
-
default:
|
|
4142
|
-
console.warn(node);
|
|
4143
|
-
break;
|
|
4144
|
-
}
|
|
4145
|
-
if (_style.display === "none") {
|
|
4146
|
-
return paths;
|
|
4147
|
-
}
|
|
4148
|
-
const currentTransform = new Matrix3();
|
|
4149
|
-
const transformStack = [];
|
|
4150
|
-
const transform = getNodeTransform(node, currentTransform, transformStack);
|
|
4151
|
-
if (path) {
|
|
4152
|
-
path.applyTransform(currentTransform);
|
|
4153
|
-
paths.push(path);
|
|
4154
|
-
path.style = { ..._style };
|
|
4155
4167
|
}
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
parseNode(node2, _style, paths, stylesheets);
|
|
4168
|
+
controlPoints = [];
|
|
4169
|
+
moveControlPoint(i, j, dx, dy) {
|
|
4170
|
+
this.controlPoints[i][j].x += dx;
|
|
4171
|
+
this.controlPoints[i][j].y += dy;
|
|
4172
|
+
return this;
|
|
4162
4173
|
}
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4174
|
+
}
|
|
4175
|
+
function bsplineBasis(t) {
|
|
4176
|
+
const B = [];
|
|
4177
|
+
B[0] = (1 - t) ** 3 / 6;
|
|
4178
|
+
B[1] = (3 * t ** 3 - 6 * t ** 2 + 4) / 6;
|
|
4179
|
+
B[2] = (-3 * t ** 3 + 3 * t ** 2 + 3 * t + 1) / 6;
|
|
4180
|
+
B[3] = t ** 3 / 6;
|
|
4181
|
+
return B;
|
|
4182
|
+
}
|
|
4183
|
+
function applyFFD(point, grid, width = grid.width, height = grid.height) {
|
|
4184
|
+
const s = point.x / width * (grid.cols - 1);
|
|
4185
|
+
const t = point.y / height * (grid.rows - 1);
|
|
4186
|
+
const i = Math.floor(s);
|
|
4187
|
+
const j = Math.floor(t);
|
|
4188
|
+
const u = s - i;
|
|
4189
|
+
const v = t - j;
|
|
4190
|
+
const bu = bsplineBasis(u);
|
|
4191
|
+
const bv = bsplineBasis(v);
|
|
4192
|
+
let x = 0;
|
|
4193
|
+
let y = 0;
|
|
4194
|
+
for (let m = 0; m < 4; m++) {
|
|
4195
|
+
for (let n = 0; n < 4; n++) {
|
|
4196
|
+
const row = Math.min(Math.max(j - 1 + m, 0), grid.rows - 1);
|
|
4197
|
+
const col = Math.min(Math.max(i - 1 + n, 0), grid.cols - 1);
|
|
4198
|
+
const cp = grid.controlPoints[row][col];
|
|
4199
|
+
const weight = bu[n] * bv[m];
|
|
4200
|
+
x += cp.x * weight;
|
|
4201
|
+
y += cp.y * weight;
|
|
4169
4202
|
}
|
|
4170
4203
|
}
|
|
4171
|
-
|
|
4172
|
-
}
|
|
4173
|
-
|
|
4174
|
-
function svgToPath2DSet(svg) {
|
|
4175
|
-
const dom = svgToDOM(svg);
|
|
4176
|
-
return new Path2DSet(
|
|
4177
|
-
parseNode(dom, {}),
|
|
4178
|
-
dom.getAttribute("viewBox")?.trim().split(" ").map((v) => Number(v))
|
|
4179
|
-
);
|
|
4204
|
+
point.set(x, y);
|
|
4180
4205
|
}
|
|
4181
4206
|
|
|
4182
|
-
export { ArcCurve, BoundingBox, CompositeCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, EquilateralPloygonCurve, FFDControlGrid, LineCurve, Matrix3, Path2D, Path2DSet, PloygonCurve, QuadraticBezierCurve, RectangleCurve, RoundRectangleCurve, SplineCurve, Vector2, applyFFD, catmullRom, cubicBezier, drawPoint, fillTriangulate, getAdaptiveCubicBezierCurvePoints, getAdaptiveQuadraticBezierCurvePoints, getDirectedArea, parseArcCommand, parsePathDataArgs,
|
|
4207
|
+
export { ArcCurve, BoundingBox, CompositeCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, EquilateralPloygonCurve, FFDControlGrid, LineCurve, Matrix3, Path2D, Path2DSet, PloygonCurve, QuadraticBezierCurve, RectangleCurve, RoundRectangleCurve, SplineCurve, Vector2, applyFFD, catmullRom, cubicBezier, drawPoint, fillTriangulate, getAdaptiveCubicBezierCurvePoints, getAdaptiveQuadraticBezierCurvePoints, getDirectedArea, parseArcCommand, parsePathDataArgs, pointInPolygonEvenOdd, pointInPolygonNonZero, quadraticBezier, setCanvasContext, strokeTriangulate, svgPathCommandsAddToPath2D, svgPathCommandsToData, svgPathDataToCommands, svgToDom, svgToPath2DSet };
|