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