vizo-mcp 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/fonts/CascadiaCode-Regular.ttf +0 -0
- package/dist/fonts/CascadiaCode-Regular.woff2 +0 -0
- package/dist/fonts/ComicShanns-Regular-279a7b317d12eb88de06167bd672b4b4.ttf +0 -0
- package/dist/fonts/ComicShanns-Regular-279a7b317d12eb88de06167bd672b4b4.woff2 +0 -0
- package/dist/fonts/ComicShanns-Regular-6e066e8de2ac57ea9283adb9c24d7f0c.ttf +0 -0
- package/dist/fonts/ComicShanns-Regular-6e066e8de2ac57ea9283adb9c24d7f0c.woff2 +0 -0
- package/dist/fonts/ComicShanns-Regular-dc6a8806fa96795d7b3be5026f989a17.ttf +0 -0
- package/dist/fonts/ComicShanns-Regular-dc6a8806fa96795d7b3be5026f989a17.woff2 +0 -0
- package/dist/fonts/ComicShanns-Regular-fcb0fc02dcbee4c9846b3e2508668039.ttf +0 -0
- package/dist/fonts/ComicShanns-Regular-fcb0fc02dcbee4c9846b3e2508668039.woff2 +0 -0
- package/dist/fonts/Excalifont-Regular-349fac6ca4700ffec595a7150a0d1e1d.ttf +0 -0
- package/dist/fonts/Excalifont-Regular-349fac6ca4700ffec595a7150a0d1e1d.woff2 +0 -0
- package/dist/fonts/Excalifont-Regular-3f2c5db56cc93c5a6873b1361d730c16.ttf +0 -0
- package/dist/fonts/Excalifont-Regular-3f2c5db56cc93c5a6873b1361d730c16.woff2 +0 -0
- package/dist/fonts/Excalifont-Regular-41b173a47b57366892116a575a43e2b6.ttf +0 -0
- package/dist/fonts/Excalifont-Regular-41b173a47b57366892116a575a43e2b6.woff2 +0 -0
- package/dist/fonts/Excalifont-Regular-623ccf21b21ef6b3a0d87738f77eb071.ttf +0 -0
- package/dist/fonts/Excalifont-Regular-623ccf21b21ef6b3a0d87738f77eb071.woff2 +0 -0
- package/dist/fonts/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.ttf +0 -0
- package/dist/fonts/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2 +0 -0
- package/dist/fonts/Excalifont-Regular-b9dcf9d2e50a1eaf42fc664b50a3fd0d.ttf +0 -0
- package/dist/fonts/Excalifont-Regular-b9dcf9d2e50a1eaf42fc664b50a3fd0d.woff2 +0 -0
- package/dist/fonts/Excalifont-Regular-be310b9bcd4f1a43f571c46df7809174.ttf +0 -0
- package/dist/fonts/Excalifont-Regular-be310b9bcd4f1a43f571c46df7809174.woff2 +0 -0
- package/dist/fonts/LiberationSans-Regular.ttf +0 -0
- package/dist/fonts/LiberationSans-Regular.woff2 +0 -0
- package/dist/fonts/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.ttf +0 -0
- package/dist/fonts/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.woff2 +0 -0
- package/dist/fonts/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYEF8RXi4EwQ.ttf +0 -0
- package/dist/fonts/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYEF8RXi4EwQ.woff2 +0 -0
- package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXWjgevT5.ttf +0 -0
- package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXWjgevT5.woff2 +0 -0
- package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.ttf +0 -0
- package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.woff2 +0 -0
- package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTk3j6zbXWjgevT5.ttf +0 -0
- package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTk3j6zbXWjgevT5.woff2 +0 -0
- package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTo3j6zbXWjgevT5.ttf +0 -0
- package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTo3j6zbXWjgevT5.woff2 +0 -0
- package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTs3j6zbXWjgevT5.ttf +0 -0
- package/dist/fonts/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTs3j6zbXWjgevT5.woff2 +0 -0
- package/dist/fonts/Virgil-Regular.ttf +0 -0
- package/dist/fonts/Virgil-Regular.woff2 +0 -0
- package/dist/fonts/fonts.json +146 -0
- package/dist/index.js +2778 -79
- package/fonts/README.md +15 -0
- package/package.json +13 -9
package/dist/index.js
CHANGED
|
@@ -245,7 +245,7 @@ function measureLine(text2, fontSize, font) {
|
|
|
245
245
|
}
|
|
246
246
|
function measureText(text2, fontSize, font) {
|
|
247
247
|
const lines = text2.split("\n");
|
|
248
|
-
const width = Math.max(0, ...lines.map((
|
|
248
|
+
const width = Math.max(0, ...lines.map((l2) => measureLine(l2, fontSize, font)));
|
|
249
249
|
const height = lines.length * fontSize * LINE_HEIGHT[font];
|
|
250
250
|
return { width: Math.ceil(width), height: Math.ceil(height), lines: lines.length };
|
|
251
251
|
}
|
|
@@ -253,17 +253,17 @@ function wrapText(text2, fontSize, font, maxWidth) {
|
|
|
253
253
|
const out = [];
|
|
254
254
|
for (const para of text2.split("\n")) {
|
|
255
255
|
const words = para.split(/\s+/).filter(Boolean);
|
|
256
|
-
let
|
|
256
|
+
let line2 = "";
|
|
257
257
|
for (const word of words) {
|
|
258
|
-
const candidate =
|
|
259
|
-
if (!
|
|
260
|
-
|
|
258
|
+
const candidate = line2 ? `${line2} ${word}` : word;
|
|
259
|
+
if (!line2 || measureLine(candidate, fontSize, font) <= maxWidth) {
|
|
260
|
+
line2 = candidate;
|
|
261
261
|
} else {
|
|
262
|
-
out.push(
|
|
263
|
-
|
|
262
|
+
out.push(line2);
|
|
263
|
+
line2 = word;
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
|
-
out.push(
|
|
266
|
+
out.push(line2);
|
|
267
267
|
}
|
|
268
268
|
return out.join("\n");
|
|
269
269
|
}
|
|
@@ -302,7 +302,7 @@ var randomNonce = () => Math.floor(Math.random() * 2 ** 31);
|
|
|
302
302
|
function randomId(len = 20) {
|
|
303
303
|
const abc = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
304
304
|
const bytes = crypto.getRandomValues(new Uint8Array(len));
|
|
305
|
-
return Array.from(bytes, (
|
|
305
|
+
return Array.from(bytes, (b2) => abc[b2 % abc.length]).join("");
|
|
306
306
|
}
|
|
307
307
|
function base(id2, type, x, y, width, height, o = {}) {
|
|
308
308
|
return {
|
|
@@ -401,23 +401,23 @@ function arrow(id2, pts, o = {}) {
|
|
|
401
401
|
const bind = (target, p) => {
|
|
402
402
|
if (!target) return null;
|
|
403
403
|
target.boundElements = [...target.boundElements ?? [], { id: id2, type: "arrow" }];
|
|
404
|
-
const
|
|
404
|
+
const b2 = { elementId: target.id, focus: 0, gap: 4 };
|
|
405
405
|
if (o.elbowed) {
|
|
406
|
-
|
|
406
|
+
b2.fixedPoint = [
|
|
407
407
|
clamp((p[0] - target.x) / (target.width || 1), 0, 1),
|
|
408
408
|
clamp((p[1] - target.y) / (target.height || 1), 0, 1)
|
|
409
409
|
];
|
|
410
|
-
|
|
410
|
+
b2.gap = 0;
|
|
411
411
|
}
|
|
412
|
-
return
|
|
412
|
+
return b2;
|
|
413
413
|
};
|
|
414
414
|
el.startBinding = bind(o.start, clean[0]);
|
|
415
415
|
el.endBinding = bind(o.end, clean[clean.length - 1]);
|
|
416
416
|
return el;
|
|
417
417
|
}
|
|
418
|
-
function arrowLabel(
|
|
419
|
-
const t = text(`${
|
|
420
|
-
const pts =
|
|
418
|
+
function arrowLabel(a2, value, o = {}) {
|
|
419
|
+
const t = text(`${a2.id}.t`, 0, 0, value, { textAlign: "center", verticalAlign: "middle", fontSize: 16, ...o });
|
|
420
|
+
const pts = a2.points;
|
|
421
421
|
let mid;
|
|
422
422
|
if (pts.length % 2 === 1) mid = pts[Math.floor(pts.length / 2)];
|
|
423
423
|
else {
|
|
@@ -425,12 +425,12 @@ function arrowLabel(a, value, o = {}) {
|
|
|
425
425
|
const q = pts[pts.length / 2];
|
|
426
426
|
mid = [(p[0] + q[0]) / 2, (p[1] + q[1]) / 2];
|
|
427
427
|
}
|
|
428
|
-
t.containerId =
|
|
429
|
-
t.x = round(
|
|
430
|
-
t.y = round(
|
|
431
|
-
t.groupIds =
|
|
432
|
-
if (
|
|
433
|
-
|
|
428
|
+
t.containerId = a2.id;
|
|
429
|
+
t.x = round(a2.x + mid[0] - t.width / 2);
|
|
430
|
+
t.y = round(a2.y + mid[1] - t.height / 2);
|
|
431
|
+
t.groupIds = a2.groupIds ?? [];
|
|
432
|
+
if (a2.customData) t.customData = { ...a2.customData, role: "label" };
|
|
433
|
+
a2.boundElements = [...a2.boundElements ?? [], { id: t.id, type: "text" }];
|
|
434
434
|
return t;
|
|
435
435
|
}
|
|
436
436
|
function frame(id2, x, y, w, h, name, o = {}) {
|
|
@@ -445,18 +445,18 @@ function dedupe(pts) {
|
|
|
445
445
|
}
|
|
446
446
|
const res = [];
|
|
447
447
|
for (let i = 0; i < out.length; i++) {
|
|
448
|
-
const
|
|
449
|
-
const
|
|
448
|
+
const a2 = res[res.length - 1];
|
|
449
|
+
const b2 = out[i];
|
|
450
450
|
const c = out[i + 1];
|
|
451
|
-
if (
|
|
451
|
+
if (a2 && c && (Math.abs(a2[0] - b2[0]) < 0.5 && Math.abs(b2[0] - c[0]) < 0.5 || Math.abs(a2[1] - b2[1]) < 0.5 && Math.abs(b2[1] - c[1]) < 0.5))
|
|
452
452
|
continue;
|
|
453
|
-
res.push(
|
|
453
|
+
res.push(b2);
|
|
454
454
|
}
|
|
455
455
|
return res.length >= 2 ? res : [out[0], [out[0][0] + 1, out[0][1]]];
|
|
456
456
|
}
|
|
457
457
|
var clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
458
458
|
function elementBounds(els) {
|
|
459
|
-
let
|
|
459
|
+
let b2 = null;
|
|
460
460
|
for (const e of els) {
|
|
461
461
|
if (e.isDeleted) continue;
|
|
462
462
|
let x1 = e.x, y1 = e.y, x2 = e.x + e.width, y2 = e.y + e.height;
|
|
@@ -468,15 +468,15 @@ function elementBounds(els) {
|
|
|
468
468
|
y1 = Math.min(...ys);
|
|
469
469
|
y2 = Math.max(...ys);
|
|
470
470
|
}
|
|
471
|
-
if (!
|
|
471
|
+
if (!b2) b2 = { minX: x1, minY: y1, maxX: x2, maxY: y2 };
|
|
472
472
|
else {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
473
|
+
b2.minX = Math.min(b2.minX, x1);
|
|
474
|
+
b2.minY = Math.min(b2.minY, y1);
|
|
475
|
+
b2.maxX = Math.max(b2.maxX, x2);
|
|
476
|
+
b2.maxY = Math.max(b2.maxY, y2);
|
|
477
477
|
}
|
|
478
478
|
}
|
|
479
|
-
return
|
|
479
|
+
return b2;
|
|
480
480
|
}
|
|
481
481
|
function translate(els, dx, dy) {
|
|
482
482
|
for (const e of els) {
|
|
@@ -567,7 +567,7 @@ async function layoutGraph(m) {
|
|
|
567
567
|
for (let p = parentOf.get(id2); p; p = parentOf.get(p)) d++;
|
|
568
568
|
return d;
|
|
569
569
|
};
|
|
570
|
-
for (const g of [...m.groups].sort((
|
|
570
|
+
for (const g of [...m.groups].sort((a2, b2) => depth(a2.id) - depth(b2.id))) {
|
|
571
571
|
const p = pos.get(g.id);
|
|
572
572
|
const c = PALETTE[g.color ?? "gray"];
|
|
573
573
|
const gid = `${m.idPrefix}.g.${g.id}`;
|
|
@@ -619,7 +619,7 @@ async function layoutGraph(m) {
|
|
|
619
619
|
];
|
|
620
620
|
} else continue;
|
|
621
621
|
const isSelf = e.from === e.to;
|
|
622
|
-
const
|
|
622
|
+
const a2 = arrow(`${m.idPrefix}.e.${e.id}`, pts, {
|
|
623
623
|
start: from,
|
|
624
624
|
end: to,
|
|
625
625
|
startArrowhead: e.startArrowhead ?? null,
|
|
@@ -629,8 +629,8 @@ async function layoutGraph(m) {
|
|
|
629
629
|
elbowed: m.elbowed && !isSelf && pts.length > 2,
|
|
630
630
|
customData: m.customData("edge", e.id)
|
|
631
631
|
});
|
|
632
|
-
edgeEls.push(
|
|
633
|
-
if (e.label) edgeEls.push(arrowLabel(
|
|
632
|
+
edgeEls.push(a2);
|
|
633
|
+
if (e.label) edgeEls.push(arrowLabel(a2, e.label, { font: m.look.font }));
|
|
634
634
|
const endLabel = (lbl, p, q, role) => {
|
|
635
635
|
if (!lbl) return;
|
|
636
636
|
const dx = Math.sign(q[0] - p[0]);
|
|
@@ -639,7 +639,7 @@ async function layoutGraph(m) {
|
|
|
639
639
|
const x = p[0] + (dx !== 0 ? dx * 8 + (dx < 0 ? -m2.width : 0) : 8);
|
|
640
640
|
const y = p[1] + (dy !== 0 ? dy * 6 + (dy < 0 ? -m2.height : 0) : -m2.height - 4);
|
|
641
641
|
edgeEls.push(
|
|
642
|
-
text(`${
|
|
642
|
+
text(`${a2.id}.${role}`, x, y, lbl, { fontSize: 14, font: m.look.font, customData: m.customData(role, e.id) })
|
|
643
643
|
);
|
|
644
644
|
};
|
|
645
645
|
endLabel(e.fromLabel, pts[0], pts[1], "from-mult");
|
|
@@ -754,10 +754,10 @@ function classModel(spec, ctx) {
|
|
|
754
754
|
const nodes = spec.classes.map((c) => {
|
|
755
755
|
const header = (c.stereotype ? `\xAB${c.stereotype}\xBB
|
|
756
756
|
` : "") + c.name;
|
|
757
|
-
const
|
|
757
|
+
const attrs2 = (c.attributes ?? []).join("\n");
|
|
758
758
|
const meths = (c.methods ?? []).join("\n");
|
|
759
759
|
const hM = measureText(header, 20, font);
|
|
760
|
-
const aM =
|
|
760
|
+
const aM = attrs2 ? measureText(attrs2, 16, font) : { width: 0, height: 20 };
|
|
761
761
|
const mM = meths ? measureText(meths, 16, font) : { width: 0, height: 20 };
|
|
762
762
|
const padX = 14;
|
|
763
763
|
const w = Math.max(180, hM.width + padX * 2 + 10, aM.width + padX * 2, mM.width + padX * 2);
|
|
@@ -802,9 +802,9 @@ function classModel(spec, ctx) {
|
|
|
802
802
|
[x + w, y + hH + aH]
|
|
803
803
|
], { kind: "line", roughness: ctx.look.roughness, groupIds: group, strokeWidth: 1 });
|
|
804
804
|
els.push(div);
|
|
805
|
-
if (
|
|
805
|
+
if (attrs2)
|
|
806
806
|
els.push(
|
|
807
|
-
text(`${id2}.attrs`, x + padX, y + hH + 8,
|
|
807
|
+
text(`${id2}.attrs`, x + padX, y + hH + 8, attrs2, { fontSize: 16, font, groupIds: group, customData: ctx.cd("class-attrs", c.id) })
|
|
808
808
|
);
|
|
809
809
|
if (meths)
|
|
810
810
|
els.push(
|
|
@@ -931,15 +931,15 @@ function placeRadial(root) {
|
|
|
931
931
|
const ring = Math.max(220, maxW(root) + 80);
|
|
932
932
|
const rec = (t, a0, a1, depth) => {
|
|
933
933
|
const total = leaves(t);
|
|
934
|
-
let
|
|
934
|
+
let a2 = a0;
|
|
935
935
|
for (const c of t.children) {
|
|
936
936
|
const share = (a1 - a0) * leaves(c) / total;
|
|
937
|
-
const mid =
|
|
937
|
+
const mid = a2 + share / 2;
|
|
938
938
|
const r = ring * depth + (depth === 1 ? root.w / 4 : 0);
|
|
939
939
|
c.x = root.x + root.w / 2 + Math.cos(mid) * r - c.w / 2;
|
|
940
940
|
c.y = root.y + root.h / 2 + Math.sin(mid) * r - c.h / 2;
|
|
941
|
-
rec(c,
|
|
942
|
-
|
|
941
|
+
rec(c, a2, a2 + share, depth + 1);
|
|
942
|
+
a2 += share;
|
|
943
943
|
}
|
|
944
944
|
};
|
|
945
945
|
rec(root, -Math.PI / 2, 3 * Math.PI / 2, 1);
|
|
@@ -1024,8 +1024,8 @@ function edgePoint(e, target) {
|
|
|
1024
1024
|
const dy = target[1] - cy;
|
|
1025
1025
|
if (dx === 0 && dy === 0) return [cx, cy];
|
|
1026
1026
|
if (e.type === "ellipse") {
|
|
1027
|
-
const
|
|
1028
|
-
const t2 = 1 / Math.sqrt(dx * dx / (
|
|
1027
|
+
const a2 = e.width / 2, b2 = e.height / 2;
|
|
1028
|
+
const t2 = 1 / Math.sqrt(dx * dx / (a2 * a2) + dy * dy / (b2 * b2));
|
|
1029
1029
|
return [cx + dx * t2, cy + dy * t2];
|
|
1030
1030
|
}
|
|
1031
1031
|
const t = Math.min(e.width / 2 / Math.abs(dx || 1e-9), e.height / 2 / Math.abs(dy || 1e-9));
|
|
@@ -1047,24 +1047,24 @@ function layoutSequence(spec, ctx) {
|
|
|
1047
1047
|
const fit = fitContainer(kind, m.width, m.height, 18, 10);
|
|
1048
1048
|
return { lbl, kind, w: Math.max(130, fit.w), h: Math.max(HEAD_H, fit.h) };
|
|
1049
1049
|
});
|
|
1050
|
-
const headH = Math.max(...boxes.map((
|
|
1050
|
+
const headH = Math.max(...boxes.map((b2) => b2.h));
|
|
1051
1051
|
const gaps = P.map((_, i) => i === 0 ? 0 : Math.max(boxes[i - 1].w / 2 + boxes[i].w / 2 + 50, 180));
|
|
1052
1052
|
const msgLabels = spec.messages.map((m) => m.label ? wrapText(m.label, MSG_FONT, font, 320) : "");
|
|
1053
|
-
const order = spec.messages.map((_, i) => i).sort((
|
|
1053
|
+
const order = spec.messages.map((_, i) => i).sort((a2, b2) => span(a2) - span(b2));
|
|
1054
1054
|
function span(i) {
|
|
1055
1055
|
const m = spec.messages[i];
|
|
1056
1056
|
return Math.abs(idx.get(m.from) - idx.get(m.to));
|
|
1057
1057
|
}
|
|
1058
1058
|
for (const i of order) {
|
|
1059
1059
|
const m = spec.messages[i];
|
|
1060
|
-
const
|
|
1061
|
-
const
|
|
1060
|
+
const a2 = idx.get(m.from);
|
|
1061
|
+
const b2 = idx.get(m.to);
|
|
1062
1062
|
const lw = msgLabels[i] ? measureText(msgLabels[i], MSG_FONT, font).width : 0;
|
|
1063
|
-
if (
|
|
1064
|
-
if (
|
|
1063
|
+
if (a2 === b2) {
|
|
1064
|
+
if (a2 + 1 < P.length) gaps[a2 + 1] = Math.max(gaps[a2 + 1], lw + 90);
|
|
1065
1065
|
continue;
|
|
1066
1066
|
}
|
|
1067
|
-
const [lo, hi] =
|
|
1067
|
+
const [lo, hi] = a2 < b2 ? [a2, b2] : [b2, a2];
|
|
1068
1068
|
let cur = 0;
|
|
1069
1069
|
for (let k = lo + 1; k <= hi; k++) cur += gaps[k];
|
|
1070
1070
|
const need = lw + 60;
|
|
@@ -1108,8 +1108,8 @@ function layoutSequence(spec, ctx) {
|
|
|
1108
1108
|
emitNotes(-1);
|
|
1109
1109
|
spec.messages.forEach((m, i) => {
|
|
1110
1110
|
y += (fragStarts.get(i) ?? 0) * FRAG_HEADER;
|
|
1111
|
-
const
|
|
1112
|
-
const
|
|
1111
|
+
const a2 = idx.get(m.from);
|
|
1112
|
+
const b2 = idx.get(m.to);
|
|
1113
1113
|
const lbl = msgLabels[i];
|
|
1114
1114
|
const lm = lbl ? measureText(lbl, MSG_FONT, font) : { width: 0, height: 0 };
|
|
1115
1115
|
const top = y;
|
|
@@ -1123,29 +1123,29 @@ function layoutSequence(spec, ctx) {
|
|
|
1123
1123
|
groupIds: grp,
|
|
1124
1124
|
customData: ctx.cd("message", String(i))
|
|
1125
1125
|
};
|
|
1126
|
-
if (
|
|
1126
|
+
if (a2 === b2) {
|
|
1127
1127
|
const ay = y + Math.max(lm.height - 6, 0);
|
|
1128
1128
|
els.push(
|
|
1129
1129
|
arrow(
|
|
1130
1130
|
id2,
|
|
1131
1131
|
[
|
|
1132
|
-
[cx[
|
|
1133
|
-
[cx[
|
|
1134
|
-
[cx[
|
|
1135
|
-
[cx[
|
|
1132
|
+
[cx[a2], ay],
|
|
1133
|
+
[cx[a2] + 50, ay],
|
|
1134
|
+
[cx[a2] + 50, ay + 34],
|
|
1135
|
+
[cx[a2] + 4, ay + 34]
|
|
1136
1136
|
],
|
|
1137
1137
|
common2
|
|
1138
1138
|
)
|
|
1139
1139
|
);
|
|
1140
|
-
if (lbl) els.push(text(`${id2}.t`, cx[
|
|
1140
|
+
if (lbl) els.push(text(`${id2}.t`, cx[a2] + 60, ay + 17 - lm.height / 2, lbl, { fontSize: MSG_FONT, font, groupIds: grp, customData: ctx.cd("message-label", String(i)) }));
|
|
1141
1141
|
y = ay + 34 + 26;
|
|
1142
1142
|
} else {
|
|
1143
1143
|
const ay = y + lm.height + 6;
|
|
1144
|
-
const dir = Math.sign(cx[
|
|
1145
|
-
els.push(arrow(id2, [[cx[
|
|
1144
|
+
const dir = Math.sign(cx[b2] - cx[a2]);
|
|
1145
|
+
els.push(arrow(id2, [[cx[a2], ay], [cx[b2] - dir * 2, ay]], common2));
|
|
1146
1146
|
if (lbl)
|
|
1147
1147
|
els.push(
|
|
1148
|
-
text(`${id2}.t`, (cx[
|
|
1148
|
+
text(`${id2}.t`, (cx[a2] + cx[b2]) / 2 - lm.width / 2, y, lbl, {
|
|
1149
1149
|
fontSize: MSG_FONT,
|
|
1150
1150
|
font,
|
|
1151
1151
|
textAlign: "center",
|
|
@@ -1190,15 +1190,15 @@ function layoutSequence(spec, ctx) {
|
|
|
1190
1190
|
});
|
|
1191
1191
|
const headEls = [];
|
|
1192
1192
|
P.forEach((p, i) => {
|
|
1193
|
-
const
|
|
1193
|
+
const b2 = boxes[i];
|
|
1194
1194
|
const id2 = `${ctx.idPrefix}.p.${p.id}`;
|
|
1195
1195
|
const color = PALETTE[p.type === "actor" ? "pink" : p.type === "database" ? "cyan" : p.type === "external" ? "gray" : "blue"];
|
|
1196
|
-
const s = shape(
|
|
1196
|
+
const s = shape(b2.kind, id2, cx[i] - b2.w / 2, (headH - b2.h) / 2, b2.w, b2.h, {
|
|
1197
1197
|
backgroundColor: color[1],
|
|
1198
1198
|
roughness,
|
|
1199
1199
|
customData: ctx.cd("participant", p.id)
|
|
1200
1200
|
});
|
|
1201
|
-
headEls.push(s, boundText(s,
|
|
1201
|
+
headEls.push(s, boundText(s, b2.lbl, { fontSize: 18, font, strokeColor: INK }));
|
|
1202
1202
|
headEls.push(
|
|
1203
1203
|
arrow(`${id2}.life`, [[cx[i], headH], [cx[i], bottom]], {
|
|
1204
1204
|
kind: "line",
|
|
@@ -1260,11 +1260,11 @@ async function renderDiagram(spec, opts) {
|
|
|
1260
1260
|
els = layoutMindmap(spec, ctx);
|
|
1261
1261
|
break;
|
|
1262
1262
|
}
|
|
1263
|
-
const
|
|
1263
|
+
const b2 = elementBounds(els);
|
|
1264
1264
|
const origin = opts.origin ?? { x: 0, y: 0 };
|
|
1265
|
-
translate(els, origin.x + FRAME_PAD -
|
|
1266
|
-
const w =
|
|
1267
|
-
const h =
|
|
1265
|
+
translate(els, origin.x + FRAME_PAD - b2.minX, origin.y + FRAME_PAD - b2.minY);
|
|
1266
|
+
const w = b2.maxX - b2.minX + FRAME_PAD * 2;
|
|
1267
|
+
const h = b2.maxY - b2.minY + FRAME_PAD * 2;
|
|
1268
1268
|
const frameId = `${opts.diagramId}.frame`;
|
|
1269
1269
|
const f = frame(frameId, origin.x, origin.y, w, h, spec.title ?? defaultTitle(spec), {
|
|
1270
1270
|
customData: cd("frame", opts.diagramId)
|
|
@@ -1307,18 +1307,2698 @@ var ConnectInput = z2.object({
|
|
|
1307
1307
|
color: Color.optional()
|
|
1308
1308
|
});
|
|
1309
1309
|
|
|
1310
|
+
// src/export.ts
|
|
1311
|
+
import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
1312
|
+
import { dirname, extname, resolve } from "node:path";
|
|
1313
|
+
import { fileURLToPath } from "node:url";
|
|
1314
|
+
|
|
1315
|
+
// ../../node_modules/.pnpm/hachure-fill@0.5.2/node_modules/hachure-fill/bin/hachure.js
|
|
1316
|
+
function rotatePoints(points, center, degrees) {
|
|
1317
|
+
if (points && points.length) {
|
|
1318
|
+
const [cx, cy] = center;
|
|
1319
|
+
const angle = Math.PI / 180 * degrees;
|
|
1320
|
+
const cos = Math.cos(angle);
|
|
1321
|
+
const sin = Math.sin(angle);
|
|
1322
|
+
for (const p of points) {
|
|
1323
|
+
const [x, y] = p;
|
|
1324
|
+
p[0] = (x - cx) * cos - (y - cy) * sin + cx;
|
|
1325
|
+
p[1] = (x - cx) * sin + (y - cy) * cos + cy;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
function rotateLines(lines, center, degrees) {
|
|
1330
|
+
const points = [];
|
|
1331
|
+
lines.forEach((line2) => points.push(...line2));
|
|
1332
|
+
rotatePoints(points, center, degrees);
|
|
1333
|
+
}
|
|
1334
|
+
function areSamePoints(p1, p2) {
|
|
1335
|
+
return p1[0] === p2[0] && p1[1] === p2[1];
|
|
1336
|
+
}
|
|
1337
|
+
function hachureLines(polygons, hachureGap, hachureAngle, hachureStepOffset = 1) {
|
|
1338
|
+
const angle = hachureAngle;
|
|
1339
|
+
const gap = Math.max(hachureGap, 0.1);
|
|
1340
|
+
const polygonList = polygons[0] && polygons[0][0] && typeof polygons[0][0] === "number" ? [polygons] : polygons;
|
|
1341
|
+
const rotationCenter = [0, 0];
|
|
1342
|
+
if (angle) {
|
|
1343
|
+
for (const polygon2 of polygonList) {
|
|
1344
|
+
rotatePoints(polygon2, rotationCenter, angle);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
const lines = straightHachureLines(polygonList, gap, hachureStepOffset);
|
|
1348
|
+
if (angle) {
|
|
1349
|
+
for (const polygon2 of polygonList) {
|
|
1350
|
+
rotatePoints(polygon2, rotationCenter, -angle);
|
|
1351
|
+
}
|
|
1352
|
+
rotateLines(lines, rotationCenter, -angle);
|
|
1353
|
+
}
|
|
1354
|
+
return lines;
|
|
1355
|
+
}
|
|
1356
|
+
function straightHachureLines(polygons, gap, hachureStepOffset) {
|
|
1357
|
+
const vertexArray = [];
|
|
1358
|
+
for (const polygon2 of polygons) {
|
|
1359
|
+
const vertices = [...polygon2];
|
|
1360
|
+
if (!areSamePoints(vertices[0], vertices[vertices.length - 1])) {
|
|
1361
|
+
vertices.push([vertices[0][0], vertices[0][1]]);
|
|
1362
|
+
}
|
|
1363
|
+
if (vertices.length > 2) {
|
|
1364
|
+
vertexArray.push(vertices);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
const lines = [];
|
|
1368
|
+
gap = Math.max(gap, 0.1);
|
|
1369
|
+
const edges = [];
|
|
1370
|
+
for (const vertices of vertexArray) {
|
|
1371
|
+
for (let i = 0; i < vertices.length - 1; i++) {
|
|
1372
|
+
const p1 = vertices[i];
|
|
1373
|
+
const p2 = vertices[i + 1];
|
|
1374
|
+
if (p1[1] !== p2[1]) {
|
|
1375
|
+
const ymin = Math.min(p1[1], p2[1]);
|
|
1376
|
+
edges.push({
|
|
1377
|
+
ymin,
|
|
1378
|
+
ymax: Math.max(p1[1], p2[1]),
|
|
1379
|
+
x: ymin === p1[1] ? p1[0] : p2[0],
|
|
1380
|
+
islope: (p2[0] - p1[0]) / (p2[1] - p1[1])
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
edges.sort((e1, e2) => {
|
|
1386
|
+
if (e1.ymin < e2.ymin) {
|
|
1387
|
+
return -1;
|
|
1388
|
+
}
|
|
1389
|
+
if (e1.ymin > e2.ymin) {
|
|
1390
|
+
return 1;
|
|
1391
|
+
}
|
|
1392
|
+
if (e1.x < e2.x) {
|
|
1393
|
+
return -1;
|
|
1394
|
+
}
|
|
1395
|
+
if (e1.x > e2.x) {
|
|
1396
|
+
return 1;
|
|
1397
|
+
}
|
|
1398
|
+
if (e1.ymax === e2.ymax) {
|
|
1399
|
+
return 0;
|
|
1400
|
+
}
|
|
1401
|
+
return (e1.ymax - e2.ymax) / Math.abs(e1.ymax - e2.ymax);
|
|
1402
|
+
});
|
|
1403
|
+
if (!edges.length) {
|
|
1404
|
+
return lines;
|
|
1405
|
+
}
|
|
1406
|
+
let activeEdges = [];
|
|
1407
|
+
let y = edges[0].ymin;
|
|
1408
|
+
let iteration = 0;
|
|
1409
|
+
while (activeEdges.length || edges.length) {
|
|
1410
|
+
if (edges.length) {
|
|
1411
|
+
let ix = -1;
|
|
1412
|
+
for (let i = 0; i < edges.length; i++) {
|
|
1413
|
+
if (edges[i].ymin > y) {
|
|
1414
|
+
break;
|
|
1415
|
+
}
|
|
1416
|
+
ix = i;
|
|
1417
|
+
}
|
|
1418
|
+
const removed = edges.splice(0, ix + 1);
|
|
1419
|
+
removed.forEach((edge) => {
|
|
1420
|
+
activeEdges.push({ s: y, edge });
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
activeEdges = activeEdges.filter((ae2) => {
|
|
1424
|
+
if (ae2.edge.ymax <= y) {
|
|
1425
|
+
return false;
|
|
1426
|
+
}
|
|
1427
|
+
return true;
|
|
1428
|
+
});
|
|
1429
|
+
activeEdges.sort((ae1, ae2) => {
|
|
1430
|
+
if (ae1.edge.x === ae2.edge.x) {
|
|
1431
|
+
return 0;
|
|
1432
|
+
}
|
|
1433
|
+
return (ae1.edge.x - ae2.edge.x) / Math.abs(ae1.edge.x - ae2.edge.x);
|
|
1434
|
+
});
|
|
1435
|
+
if (hachureStepOffset !== 1 || iteration % gap === 0) {
|
|
1436
|
+
if (activeEdges.length > 1) {
|
|
1437
|
+
for (let i = 0; i < activeEdges.length; i = i + 2) {
|
|
1438
|
+
const nexti = i + 1;
|
|
1439
|
+
if (nexti >= activeEdges.length) {
|
|
1440
|
+
break;
|
|
1441
|
+
}
|
|
1442
|
+
const ce2 = activeEdges[i].edge;
|
|
1443
|
+
const ne = activeEdges[nexti].edge;
|
|
1444
|
+
lines.push([
|
|
1445
|
+
[Math.round(ce2.x), y],
|
|
1446
|
+
[Math.round(ne.x), y]
|
|
1447
|
+
]);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
y += hachureStepOffset;
|
|
1452
|
+
activeEdges.forEach((ae2) => {
|
|
1453
|
+
ae2.edge.x = ae2.edge.x + hachureStepOffset * ae2.edge.islope;
|
|
1454
|
+
});
|
|
1455
|
+
iteration++;
|
|
1456
|
+
}
|
|
1457
|
+
return lines;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
// ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/scan-line-hachure.js
|
|
1461
|
+
function polygonHachureLines(polygonList, o) {
|
|
1462
|
+
var _a;
|
|
1463
|
+
const angle = o.hachureAngle + 90;
|
|
1464
|
+
let gap = o.hachureGap;
|
|
1465
|
+
if (gap < 0) {
|
|
1466
|
+
gap = o.strokeWidth * 4;
|
|
1467
|
+
}
|
|
1468
|
+
gap = Math.round(Math.max(gap, 0.1));
|
|
1469
|
+
let skipOffset = 1;
|
|
1470
|
+
if (o.roughness >= 1) {
|
|
1471
|
+
if ((((_a = o.randomizer) === null || _a === void 0 ? void 0 : _a.next()) || Math.random()) > 0.7) {
|
|
1472
|
+
skipOffset = gap;
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
return hachureLines(polygonList, gap, angle, skipOffset || 1);
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
// ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/hachure-filler.js
|
|
1479
|
+
var HachureFiller = class {
|
|
1480
|
+
constructor(helper2) {
|
|
1481
|
+
this.helper = helper2;
|
|
1482
|
+
}
|
|
1483
|
+
fillPolygons(polygonList, o) {
|
|
1484
|
+
return this._fillPolygons(polygonList, o);
|
|
1485
|
+
}
|
|
1486
|
+
_fillPolygons(polygonList, o) {
|
|
1487
|
+
const lines = polygonHachureLines(polygonList, o);
|
|
1488
|
+
const ops = this.renderLines(lines, o);
|
|
1489
|
+
return { type: "fillSketch", ops };
|
|
1490
|
+
}
|
|
1491
|
+
renderLines(lines, o) {
|
|
1492
|
+
const ops = [];
|
|
1493
|
+
for (const line2 of lines) {
|
|
1494
|
+
ops.push(...this.helper.doubleLineOps(line2[0][0], line2[0][1], line2[1][0], line2[1][1], o));
|
|
1495
|
+
}
|
|
1496
|
+
return ops;
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
|
|
1500
|
+
// ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/geometry.js
|
|
1501
|
+
function lineLength(line2) {
|
|
1502
|
+
const p1 = line2[0];
|
|
1503
|
+
const p2 = line2[1];
|
|
1504
|
+
return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2));
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
// ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/zigzag-filler.js
|
|
1508
|
+
var ZigZagFiller = class extends HachureFiller {
|
|
1509
|
+
fillPolygons(polygonList, o) {
|
|
1510
|
+
let gap = o.hachureGap;
|
|
1511
|
+
if (gap < 0) {
|
|
1512
|
+
gap = o.strokeWidth * 4;
|
|
1513
|
+
}
|
|
1514
|
+
gap = Math.max(gap, 0.1);
|
|
1515
|
+
const o2 = Object.assign({}, o, { hachureGap: gap });
|
|
1516
|
+
const lines = polygonHachureLines(polygonList, o2);
|
|
1517
|
+
const zigZagAngle = Math.PI / 180 * o.hachureAngle;
|
|
1518
|
+
const zigzagLines = [];
|
|
1519
|
+
const dgx = gap * 0.5 * Math.cos(zigZagAngle);
|
|
1520
|
+
const dgy = gap * 0.5 * Math.sin(zigZagAngle);
|
|
1521
|
+
for (const [p1, p2] of lines) {
|
|
1522
|
+
if (lineLength([p1, p2])) {
|
|
1523
|
+
zigzagLines.push([
|
|
1524
|
+
[p1[0] - dgx, p1[1] + dgy],
|
|
1525
|
+
[...p2]
|
|
1526
|
+
], [
|
|
1527
|
+
[p1[0] + dgx, p1[1] - dgy],
|
|
1528
|
+
[...p2]
|
|
1529
|
+
]);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
const ops = this.renderLines(zigzagLines, o);
|
|
1533
|
+
return { type: "fillSketch", ops };
|
|
1534
|
+
}
|
|
1535
|
+
};
|
|
1536
|
+
|
|
1537
|
+
// ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/hatch-filler.js
|
|
1538
|
+
var HatchFiller = class extends HachureFiller {
|
|
1539
|
+
fillPolygons(polygonList, o) {
|
|
1540
|
+
const set = this._fillPolygons(polygonList, o);
|
|
1541
|
+
const o2 = Object.assign({}, o, { hachureAngle: o.hachureAngle + 90 });
|
|
1542
|
+
const set2 = this._fillPolygons(polygonList, o2);
|
|
1543
|
+
set.ops = set.ops.concat(set2.ops);
|
|
1544
|
+
return set;
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
|
|
1548
|
+
// ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/dot-filler.js
|
|
1549
|
+
var DotFiller = class {
|
|
1550
|
+
constructor(helper2) {
|
|
1551
|
+
this.helper = helper2;
|
|
1552
|
+
}
|
|
1553
|
+
fillPolygons(polygonList, o) {
|
|
1554
|
+
o = Object.assign({}, o, { hachureAngle: 0 });
|
|
1555
|
+
const lines = polygonHachureLines(polygonList, o);
|
|
1556
|
+
return this.dotsOnLines(lines, o);
|
|
1557
|
+
}
|
|
1558
|
+
dotsOnLines(lines, o) {
|
|
1559
|
+
const ops = [];
|
|
1560
|
+
let gap = o.hachureGap;
|
|
1561
|
+
if (gap < 0) {
|
|
1562
|
+
gap = o.strokeWidth * 4;
|
|
1563
|
+
}
|
|
1564
|
+
gap = Math.max(gap, 0.1);
|
|
1565
|
+
let fweight = o.fillWeight;
|
|
1566
|
+
if (fweight < 0) {
|
|
1567
|
+
fweight = o.strokeWidth / 2;
|
|
1568
|
+
}
|
|
1569
|
+
const ro = gap / 4;
|
|
1570
|
+
for (const line2 of lines) {
|
|
1571
|
+
const length = lineLength(line2);
|
|
1572
|
+
const dl = length / gap;
|
|
1573
|
+
const count = Math.ceil(dl) - 1;
|
|
1574
|
+
const offset = length - count * gap;
|
|
1575
|
+
const x = (line2[0][0] + line2[1][0]) / 2 - gap / 4;
|
|
1576
|
+
const minY = Math.min(line2[0][1], line2[1][1]);
|
|
1577
|
+
for (let i = 0; i < count; i++) {
|
|
1578
|
+
const y = minY + offset + i * gap;
|
|
1579
|
+
const cx = x - ro + Math.random() * 2 * ro;
|
|
1580
|
+
const cy = y - ro + Math.random() * 2 * ro;
|
|
1581
|
+
const el = this.helper.ellipse(cx, cy, fweight, fweight, o);
|
|
1582
|
+
ops.push(...el.ops);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
return { type: "fillSketch", ops };
|
|
1586
|
+
}
|
|
1587
|
+
};
|
|
1588
|
+
|
|
1589
|
+
// ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/dashed-filler.js
|
|
1590
|
+
var DashedFiller = class {
|
|
1591
|
+
constructor(helper2) {
|
|
1592
|
+
this.helper = helper2;
|
|
1593
|
+
}
|
|
1594
|
+
fillPolygons(polygonList, o) {
|
|
1595
|
+
const lines = polygonHachureLines(polygonList, o);
|
|
1596
|
+
return { type: "fillSketch", ops: this.dashedLine(lines, o) };
|
|
1597
|
+
}
|
|
1598
|
+
dashedLine(lines, o) {
|
|
1599
|
+
const offset = o.dashOffset < 0 ? o.hachureGap < 0 ? o.strokeWidth * 4 : o.hachureGap : o.dashOffset;
|
|
1600
|
+
const gap = o.dashGap < 0 ? o.hachureGap < 0 ? o.strokeWidth * 4 : o.hachureGap : o.dashGap;
|
|
1601
|
+
const ops = [];
|
|
1602
|
+
lines.forEach((line2) => {
|
|
1603
|
+
const length = lineLength(line2);
|
|
1604
|
+
const count = Math.floor(length / (offset + gap));
|
|
1605
|
+
const startOffset = (length + gap - count * (offset + gap)) / 2;
|
|
1606
|
+
let p1 = line2[0];
|
|
1607
|
+
let p2 = line2[1];
|
|
1608
|
+
if (p1[0] > p2[0]) {
|
|
1609
|
+
p1 = line2[1];
|
|
1610
|
+
p2 = line2[0];
|
|
1611
|
+
}
|
|
1612
|
+
const alpha = Math.atan((p2[1] - p1[1]) / (p2[0] - p1[0]));
|
|
1613
|
+
for (let i = 0; i < count; i++) {
|
|
1614
|
+
const lstart = i * (offset + gap);
|
|
1615
|
+
const lend = lstart + offset;
|
|
1616
|
+
const start = [p1[0] + lstart * Math.cos(alpha) + startOffset * Math.cos(alpha), p1[1] + lstart * Math.sin(alpha) + startOffset * Math.sin(alpha)];
|
|
1617
|
+
const end = [p1[0] + lend * Math.cos(alpha) + startOffset * Math.cos(alpha), p1[1] + lend * Math.sin(alpha) + startOffset * Math.sin(alpha)];
|
|
1618
|
+
ops.push(...this.helper.doubleLineOps(start[0], start[1], end[0], end[1], o));
|
|
1619
|
+
}
|
|
1620
|
+
});
|
|
1621
|
+
return ops;
|
|
1622
|
+
}
|
|
1623
|
+
};
|
|
1624
|
+
|
|
1625
|
+
// ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/zigzag-line-filler.js
|
|
1626
|
+
var ZigZagLineFiller = class {
|
|
1627
|
+
constructor(helper2) {
|
|
1628
|
+
this.helper = helper2;
|
|
1629
|
+
}
|
|
1630
|
+
fillPolygons(polygonList, o) {
|
|
1631
|
+
const gap = o.hachureGap < 0 ? o.strokeWidth * 4 : o.hachureGap;
|
|
1632
|
+
const zo = o.zigzagOffset < 0 ? gap : o.zigzagOffset;
|
|
1633
|
+
o = Object.assign({}, o, { hachureGap: gap + zo });
|
|
1634
|
+
const lines = polygonHachureLines(polygonList, o);
|
|
1635
|
+
return { type: "fillSketch", ops: this.zigzagLines(lines, zo, o) };
|
|
1636
|
+
}
|
|
1637
|
+
zigzagLines(lines, zo, o) {
|
|
1638
|
+
const ops = [];
|
|
1639
|
+
lines.forEach((line2) => {
|
|
1640
|
+
const length = lineLength(line2);
|
|
1641
|
+
const count = Math.round(length / (2 * zo));
|
|
1642
|
+
let p1 = line2[0];
|
|
1643
|
+
let p2 = line2[1];
|
|
1644
|
+
if (p1[0] > p2[0]) {
|
|
1645
|
+
p1 = line2[1];
|
|
1646
|
+
p2 = line2[0];
|
|
1647
|
+
}
|
|
1648
|
+
const alpha = Math.atan((p2[1] - p1[1]) / (p2[0] - p1[0]));
|
|
1649
|
+
for (let i = 0; i < count; i++) {
|
|
1650
|
+
const lstart = i * 2 * zo;
|
|
1651
|
+
const lend = (i + 1) * 2 * zo;
|
|
1652
|
+
const dz = Math.sqrt(2 * Math.pow(zo, 2));
|
|
1653
|
+
const start = [p1[0] + lstart * Math.cos(alpha), p1[1] + lstart * Math.sin(alpha)];
|
|
1654
|
+
const end = [p1[0] + lend * Math.cos(alpha), p1[1] + lend * Math.sin(alpha)];
|
|
1655
|
+
const middle = [start[0] + dz * Math.cos(alpha + Math.PI / 4), start[1] + dz * Math.sin(alpha + Math.PI / 4)];
|
|
1656
|
+
ops.push(...this.helper.doubleLineOps(start[0], start[1], middle[0], middle[1], o), ...this.helper.doubleLineOps(middle[0], middle[1], end[0], end[1], o));
|
|
1657
|
+
}
|
|
1658
|
+
});
|
|
1659
|
+
return ops;
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1662
|
+
|
|
1663
|
+
// ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/fillers/filler.js
|
|
1664
|
+
var fillers = {};
|
|
1665
|
+
function getFiller(o, helper2) {
|
|
1666
|
+
let fillerName = o.fillStyle || "hachure";
|
|
1667
|
+
if (!fillers[fillerName]) {
|
|
1668
|
+
switch (fillerName) {
|
|
1669
|
+
case "zigzag":
|
|
1670
|
+
if (!fillers[fillerName]) {
|
|
1671
|
+
fillers[fillerName] = new ZigZagFiller(helper2);
|
|
1672
|
+
}
|
|
1673
|
+
break;
|
|
1674
|
+
case "cross-hatch":
|
|
1675
|
+
if (!fillers[fillerName]) {
|
|
1676
|
+
fillers[fillerName] = new HatchFiller(helper2);
|
|
1677
|
+
}
|
|
1678
|
+
break;
|
|
1679
|
+
case "dots":
|
|
1680
|
+
if (!fillers[fillerName]) {
|
|
1681
|
+
fillers[fillerName] = new DotFiller(helper2);
|
|
1682
|
+
}
|
|
1683
|
+
break;
|
|
1684
|
+
case "dashed":
|
|
1685
|
+
if (!fillers[fillerName]) {
|
|
1686
|
+
fillers[fillerName] = new DashedFiller(helper2);
|
|
1687
|
+
}
|
|
1688
|
+
break;
|
|
1689
|
+
case "zigzag-line":
|
|
1690
|
+
if (!fillers[fillerName]) {
|
|
1691
|
+
fillers[fillerName] = new ZigZagLineFiller(helper2);
|
|
1692
|
+
}
|
|
1693
|
+
break;
|
|
1694
|
+
case "hachure":
|
|
1695
|
+
default:
|
|
1696
|
+
fillerName = "hachure";
|
|
1697
|
+
if (!fillers[fillerName]) {
|
|
1698
|
+
fillers[fillerName] = new HachureFiller(helper2);
|
|
1699
|
+
}
|
|
1700
|
+
break;
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
return fillers[fillerName];
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
// ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/math.js
|
|
1707
|
+
function randomSeed() {
|
|
1708
|
+
return Math.floor(Math.random() * 2 ** 31);
|
|
1709
|
+
}
|
|
1710
|
+
var Random = class {
|
|
1711
|
+
constructor(seed) {
|
|
1712
|
+
this.seed = seed;
|
|
1713
|
+
}
|
|
1714
|
+
next() {
|
|
1715
|
+
if (this.seed) {
|
|
1716
|
+
return (2 ** 31 - 1 & (this.seed = Math.imul(48271, this.seed))) / 2 ** 31;
|
|
1717
|
+
} else {
|
|
1718
|
+
return Math.random();
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
};
|
|
1722
|
+
|
|
1723
|
+
// ../../node_modules/.pnpm/path-data-parser@0.1.0/node_modules/path-data-parser/lib/parser.js
|
|
1724
|
+
var COMMAND = 0;
|
|
1725
|
+
var NUMBER = 1;
|
|
1726
|
+
var EOD = 2;
|
|
1727
|
+
var PARAMS = { A: 7, a: 7, C: 6, c: 6, H: 1, h: 1, L: 2, l: 2, M: 2, m: 2, Q: 4, q: 4, S: 4, s: 4, T: 2, t: 2, V: 1, v: 1, Z: 0, z: 0 };
|
|
1728
|
+
function tokenize(d) {
|
|
1729
|
+
const tokens = new Array();
|
|
1730
|
+
while (d !== "") {
|
|
1731
|
+
if (d.match(/^([ \t\r\n,]+)/)) {
|
|
1732
|
+
d = d.substr(RegExp.$1.length);
|
|
1733
|
+
} else if (d.match(/^([aAcChHlLmMqQsStTvVzZ])/)) {
|
|
1734
|
+
tokens[tokens.length] = { type: COMMAND, text: RegExp.$1 };
|
|
1735
|
+
d = d.substr(RegExp.$1.length);
|
|
1736
|
+
} else if (d.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/)) {
|
|
1737
|
+
tokens[tokens.length] = { type: NUMBER, text: `${parseFloat(RegExp.$1)}` };
|
|
1738
|
+
d = d.substr(RegExp.$1.length);
|
|
1739
|
+
} else {
|
|
1740
|
+
return [];
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
tokens[tokens.length] = { type: EOD, text: "" };
|
|
1744
|
+
return tokens;
|
|
1745
|
+
}
|
|
1746
|
+
function isType(token, type) {
|
|
1747
|
+
return token.type === type;
|
|
1748
|
+
}
|
|
1749
|
+
function parsePath(d) {
|
|
1750
|
+
const segments = [];
|
|
1751
|
+
const tokens = tokenize(d);
|
|
1752
|
+
let mode = "BOD";
|
|
1753
|
+
let index = 0;
|
|
1754
|
+
let token = tokens[index];
|
|
1755
|
+
while (!isType(token, EOD)) {
|
|
1756
|
+
let paramsCount = 0;
|
|
1757
|
+
const params = [];
|
|
1758
|
+
if (mode === "BOD") {
|
|
1759
|
+
if (token.text === "M" || token.text === "m") {
|
|
1760
|
+
index++;
|
|
1761
|
+
paramsCount = PARAMS[token.text];
|
|
1762
|
+
mode = token.text;
|
|
1763
|
+
} else {
|
|
1764
|
+
return parsePath("M0,0" + d);
|
|
1765
|
+
}
|
|
1766
|
+
} else if (isType(token, NUMBER)) {
|
|
1767
|
+
paramsCount = PARAMS[mode];
|
|
1768
|
+
} else {
|
|
1769
|
+
index++;
|
|
1770
|
+
paramsCount = PARAMS[token.text];
|
|
1771
|
+
mode = token.text;
|
|
1772
|
+
}
|
|
1773
|
+
if (index + paramsCount < tokens.length) {
|
|
1774
|
+
for (let i = index; i < index + paramsCount; i++) {
|
|
1775
|
+
const numbeToken = tokens[i];
|
|
1776
|
+
if (isType(numbeToken, NUMBER)) {
|
|
1777
|
+
params[params.length] = +numbeToken.text;
|
|
1778
|
+
} else {
|
|
1779
|
+
throw new Error("Param not a number: " + mode + "," + numbeToken.text);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
if (typeof PARAMS[mode] === "number") {
|
|
1783
|
+
const segment = { key: mode, data: params };
|
|
1784
|
+
segments.push(segment);
|
|
1785
|
+
index += paramsCount;
|
|
1786
|
+
token = tokens[index];
|
|
1787
|
+
if (mode === "M")
|
|
1788
|
+
mode = "L";
|
|
1789
|
+
if (mode === "m")
|
|
1790
|
+
mode = "l";
|
|
1791
|
+
} else {
|
|
1792
|
+
throw new Error("Bad segment: " + mode);
|
|
1793
|
+
}
|
|
1794
|
+
} else {
|
|
1795
|
+
throw new Error("Path data ended short");
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
return segments;
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
// ../../node_modules/.pnpm/path-data-parser@0.1.0/node_modules/path-data-parser/lib/absolutize.js
|
|
1802
|
+
function absolutize(segments) {
|
|
1803
|
+
let cx = 0, cy = 0;
|
|
1804
|
+
let subx = 0, suby = 0;
|
|
1805
|
+
const out = [];
|
|
1806
|
+
for (const { key, data } of segments) {
|
|
1807
|
+
switch (key) {
|
|
1808
|
+
case "M":
|
|
1809
|
+
out.push({ key: "M", data: [...data] });
|
|
1810
|
+
[cx, cy] = data;
|
|
1811
|
+
[subx, suby] = data;
|
|
1812
|
+
break;
|
|
1813
|
+
case "m":
|
|
1814
|
+
cx += data[0];
|
|
1815
|
+
cy += data[1];
|
|
1816
|
+
out.push({ key: "M", data: [cx, cy] });
|
|
1817
|
+
subx = cx;
|
|
1818
|
+
suby = cy;
|
|
1819
|
+
break;
|
|
1820
|
+
case "L":
|
|
1821
|
+
out.push({ key: "L", data: [...data] });
|
|
1822
|
+
[cx, cy] = data;
|
|
1823
|
+
break;
|
|
1824
|
+
case "l":
|
|
1825
|
+
cx += data[0];
|
|
1826
|
+
cy += data[1];
|
|
1827
|
+
out.push({ key: "L", data: [cx, cy] });
|
|
1828
|
+
break;
|
|
1829
|
+
case "C":
|
|
1830
|
+
out.push({ key: "C", data: [...data] });
|
|
1831
|
+
cx = data[4];
|
|
1832
|
+
cy = data[5];
|
|
1833
|
+
break;
|
|
1834
|
+
case "c": {
|
|
1835
|
+
const newdata = data.map((d, i) => i % 2 ? d + cy : d + cx);
|
|
1836
|
+
out.push({ key: "C", data: newdata });
|
|
1837
|
+
cx = newdata[4];
|
|
1838
|
+
cy = newdata[5];
|
|
1839
|
+
break;
|
|
1840
|
+
}
|
|
1841
|
+
case "Q":
|
|
1842
|
+
out.push({ key: "Q", data: [...data] });
|
|
1843
|
+
cx = data[2];
|
|
1844
|
+
cy = data[3];
|
|
1845
|
+
break;
|
|
1846
|
+
case "q": {
|
|
1847
|
+
const newdata = data.map((d, i) => i % 2 ? d + cy : d + cx);
|
|
1848
|
+
out.push({ key: "Q", data: newdata });
|
|
1849
|
+
cx = newdata[2];
|
|
1850
|
+
cy = newdata[3];
|
|
1851
|
+
break;
|
|
1852
|
+
}
|
|
1853
|
+
case "A":
|
|
1854
|
+
out.push({ key: "A", data: [...data] });
|
|
1855
|
+
cx = data[5];
|
|
1856
|
+
cy = data[6];
|
|
1857
|
+
break;
|
|
1858
|
+
case "a":
|
|
1859
|
+
cx += data[5];
|
|
1860
|
+
cy += data[6];
|
|
1861
|
+
out.push({ key: "A", data: [data[0], data[1], data[2], data[3], data[4], cx, cy] });
|
|
1862
|
+
break;
|
|
1863
|
+
case "H":
|
|
1864
|
+
out.push({ key: "H", data: [...data] });
|
|
1865
|
+
cx = data[0];
|
|
1866
|
+
break;
|
|
1867
|
+
case "h":
|
|
1868
|
+
cx += data[0];
|
|
1869
|
+
out.push({ key: "H", data: [cx] });
|
|
1870
|
+
break;
|
|
1871
|
+
case "V":
|
|
1872
|
+
out.push({ key: "V", data: [...data] });
|
|
1873
|
+
cy = data[0];
|
|
1874
|
+
break;
|
|
1875
|
+
case "v":
|
|
1876
|
+
cy += data[0];
|
|
1877
|
+
out.push({ key: "V", data: [cy] });
|
|
1878
|
+
break;
|
|
1879
|
+
case "S":
|
|
1880
|
+
out.push({ key: "S", data: [...data] });
|
|
1881
|
+
cx = data[2];
|
|
1882
|
+
cy = data[3];
|
|
1883
|
+
break;
|
|
1884
|
+
case "s": {
|
|
1885
|
+
const newdata = data.map((d, i) => i % 2 ? d + cy : d + cx);
|
|
1886
|
+
out.push({ key: "S", data: newdata });
|
|
1887
|
+
cx = newdata[2];
|
|
1888
|
+
cy = newdata[3];
|
|
1889
|
+
break;
|
|
1890
|
+
}
|
|
1891
|
+
case "T":
|
|
1892
|
+
out.push({ key: "T", data: [...data] });
|
|
1893
|
+
cx = data[0];
|
|
1894
|
+
cy = data[1];
|
|
1895
|
+
break;
|
|
1896
|
+
case "t":
|
|
1897
|
+
cx += data[0];
|
|
1898
|
+
cy += data[1];
|
|
1899
|
+
out.push({ key: "T", data: [cx, cy] });
|
|
1900
|
+
break;
|
|
1901
|
+
case "Z":
|
|
1902
|
+
case "z":
|
|
1903
|
+
out.push({ key: "Z", data: [] });
|
|
1904
|
+
cx = subx;
|
|
1905
|
+
cy = suby;
|
|
1906
|
+
break;
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
return out;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
// ../../node_modules/.pnpm/path-data-parser@0.1.0/node_modules/path-data-parser/lib/normalize.js
|
|
1913
|
+
function normalize(segments) {
|
|
1914
|
+
const out = [];
|
|
1915
|
+
let lastType = "";
|
|
1916
|
+
let cx = 0, cy = 0;
|
|
1917
|
+
let subx = 0, suby = 0;
|
|
1918
|
+
let lcx = 0, lcy = 0;
|
|
1919
|
+
for (const { key, data } of segments) {
|
|
1920
|
+
switch (key) {
|
|
1921
|
+
case "M":
|
|
1922
|
+
out.push({ key: "M", data: [...data] });
|
|
1923
|
+
[cx, cy] = data;
|
|
1924
|
+
[subx, suby] = data;
|
|
1925
|
+
break;
|
|
1926
|
+
case "C":
|
|
1927
|
+
out.push({ key: "C", data: [...data] });
|
|
1928
|
+
cx = data[4];
|
|
1929
|
+
cy = data[5];
|
|
1930
|
+
lcx = data[2];
|
|
1931
|
+
lcy = data[3];
|
|
1932
|
+
break;
|
|
1933
|
+
case "L":
|
|
1934
|
+
out.push({ key: "L", data: [...data] });
|
|
1935
|
+
[cx, cy] = data;
|
|
1936
|
+
break;
|
|
1937
|
+
case "H":
|
|
1938
|
+
cx = data[0];
|
|
1939
|
+
out.push({ key: "L", data: [cx, cy] });
|
|
1940
|
+
break;
|
|
1941
|
+
case "V":
|
|
1942
|
+
cy = data[0];
|
|
1943
|
+
out.push({ key: "L", data: [cx, cy] });
|
|
1944
|
+
break;
|
|
1945
|
+
case "S": {
|
|
1946
|
+
let cx1 = 0, cy1 = 0;
|
|
1947
|
+
if (lastType === "C" || lastType === "S") {
|
|
1948
|
+
cx1 = cx + (cx - lcx);
|
|
1949
|
+
cy1 = cy + (cy - lcy);
|
|
1950
|
+
} else {
|
|
1951
|
+
cx1 = cx;
|
|
1952
|
+
cy1 = cy;
|
|
1953
|
+
}
|
|
1954
|
+
out.push({ key: "C", data: [cx1, cy1, ...data] });
|
|
1955
|
+
lcx = data[0];
|
|
1956
|
+
lcy = data[1];
|
|
1957
|
+
cx = data[2];
|
|
1958
|
+
cy = data[3];
|
|
1959
|
+
break;
|
|
1960
|
+
}
|
|
1961
|
+
case "T": {
|
|
1962
|
+
const [x, y] = data;
|
|
1963
|
+
let x1 = 0, y1 = 0;
|
|
1964
|
+
if (lastType === "Q" || lastType === "T") {
|
|
1965
|
+
x1 = cx + (cx - lcx);
|
|
1966
|
+
y1 = cy + (cy - lcy);
|
|
1967
|
+
} else {
|
|
1968
|
+
x1 = cx;
|
|
1969
|
+
y1 = cy;
|
|
1970
|
+
}
|
|
1971
|
+
const cx1 = cx + 2 * (x1 - cx) / 3;
|
|
1972
|
+
const cy1 = cy + 2 * (y1 - cy) / 3;
|
|
1973
|
+
const cx2 = x + 2 * (x1 - x) / 3;
|
|
1974
|
+
const cy2 = y + 2 * (y1 - y) / 3;
|
|
1975
|
+
out.push({ key: "C", data: [cx1, cy1, cx2, cy2, x, y] });
|
|
1976
|
+
lcx = x1;
|
|
1977
|
+
lcy = y1;
|
|
1978
|
+
cx = x;
|
|
1979
|
+
cy = y;
|
|
1980
|
+
break;
|
|
1981
|
+
}
|
|
1982
|
+
case "Q": {
|
|
1983
|
+
const [x1, y1, x, y] = data;
|
|
1984
|
+
const cx1 = cx + 2 * (x1 - cx) / 3;
|
|
1985
|
+
const cy1 = cy + 2 * (y1 - cy) / 3;
|
|
1986
|
+
const cx2 = x + 2 * (x1 - x) / 3;
|
|
1987
|
+
const cy2 = y + 2 * (y1 - y) / 3;
|
|
1988
|
+
out.push({ key: "C", data: [cx1, cy1, cx2, cy2, x, y] });
|
|
1989
|
+
lcx = x1;
|
|
1990
|
+
lcy = y1;
|
|
1991
|
+
cx = x;
|
|
1992
|
+
cy = y;
|
|
1993
|
+
break;
|
|
1994
|
+
}
|
|
1995
|
+
case "A": {
|
|
1996
|
+
const r1 = Math.abs(data[0]);
|
|
1997
|
+
const r22 = Math.abs(data[1]);
|
|
1998
|
+
const angle = data[2];
|
|
1999
|
+
const largeArcFlag = data[3];
|
|
2000
|
+
const sweepFlag = data[4];
|
|
2001
|
+
const x = data[5];
|
|
2002
|
+
const y = data[6];
|
|
2003
|
+
if (r1 === 0 || r22 === 0) {
|
|
2004
|
+
out.push({ key: "C", data: [cx, cy, x, y, x, y] });
|
|
2005
|
+
cx = x;
|
|
2006
|
+
cy = y;
|
|
2007
|
+
} else {
|
|
2008
|
+
if (cx !== x || cy !== y) {
|
|
2009
|
+
const curves = arcToCubicCurves(cx, cy, x, y, r1, r22, angle, largeArcFlag, sweepFlag);
|
|
2010
|
+
curves.forEach(function(curve2) {
|
|
2011
|
+
out.push({ key: "C", data: curve2 });
|
|
2012
|
+
});
|
|
2013
|
+
cx = x;
|
|
2014
|
+
cy = y;
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
break;
|
|
2018
|
+
}
|
|
2019
|
+
case "Z":
|
|
2020
|
+
out.push({ key: "Z", data: [] });
|
|
2021
|
+
cx = subx;
|
|
2022
|
+
cy = suby;
|
|
2023
|
+
break;
|
|
2024
|
+
}
|
|
2025
|
+
lastType = key;
|
|
2026
|
+
}
|
|
2027
|
+
return out;
|
|
2028
|
+
}
|
|
2029
|
+
function degToRad(degrees) {
|
|
2030
|
+
return Math.PI * degrees / 180;
|
|
2031
|
+
}
|
|
2032
|
+
function rotate(x, y, angleRad) {
|
|
2033
|
+
const X = x * Math.cos(angleRad) - y * Math.sin(angleRad);
|
|
2034
|
+
const Y = x * Math.sin(angleRad) + y * Math.cos(angleRad);
|
|
2035
|
+
return [X, Y];
|
|
2036
|
+
}
|
|
2037
|
+
function arcToCubicCurves(x1, y1, x2, y2, r1, r22, angle, largeArcFlag, sweepFlag, recursive) {
|
|
2038
|
+
const angleRad = degToRad(angle);
|
|
2039
|
+
let params = [];
|
|
2040
|
+
let f1 = 0, f2 = 0, cx = 0, cy = 0;
|
|
2041
|
+
if (recursive) {
|
|
2042
|
+
[f1, f2, cx, cy] = recursive;
|
|
2043
|
+
} else {
|
|
2044
|
+
[x1, y1] = rotate(x1, y1, -angleRad);
|
|
2045
|
+
[x2, y2] = rotate(x2, y2, -angleRad);
|
|
2046
|
+
const x = (x1 - x2) / 2;
|
|
2047
|
+
const y = (y1 - y2) / 2;
|
|
2048
|
+
let h = x * x / (r1 * r1) + y * y / (r22 * r22);
|
|
2049
|
+
if (h > 1) {
|
|
2050
|
+
h = Math.sqrt(h);
|
|
2051
|
+
r1 = h * r1;
|
|
2052
|
+
r22 = h * r22;
|
|
2053
|
+
}
|
|
2054
|
+
const sign = largeArcFlag === sweepFlag ? -1 : 1;
|
|
2055
|
+
const r1Pow = r1 * r1;
|
|
2056
|
+
const r2Pow = r22 * r22;
|
|
2057
|
+
const left = r1Pow * r2Pow - r1Pow * y * y - r2Pow * x * x;
|
|
2058
|
+
const right = r1Pow * y * y + r2Pow * x * x;
|
|
2059
|
+
const k = sign * Math.sqrt(Math.abs(left / right));
|
|
2060
|
+
cx = k * r1 * y / r22 + (x1 + x2) / 2;
|
|
2061
|
+
cy = k * -r22 * x / r1 + (y1 + y2) / 2;
|
|
2062
|
+
f1 = Math.asin(parseFloat(((y1 - cy) / r22).toFixed(9)));
|
|
2063
|
+
f2 = Math.asin(parseFloat(((y2 - cy) / r22).toFixed(9)));
|
|
2064
|
+
if (x1 < cx) {
|
|
2065
|
+
f1 = Math.PI - f1;
|
|
2066
|
+
}
|
|
2067
|
+
if (x2 < cx) {
|
|
2068
|
+
f2 = Math.PI - f2;
|
|
2069
|
+
}
|
|
2070
|
+
if (f1 < 0) {
|
|
2071
|
+
f1 = Math.PI * 2 + f1;
|
|
2072
|
+
}
|
|
2073
|
+
if (f2 < 0) {
|
|
2074
|
+
f2 = Math.PI * 2 + f2;
|
|
2075
|
+
}
|
|
2076
|
+
if (sweepFlag && f1 > f2) {
|
|
2077
|
+
f1 = f1 - Math.PI * 2;
|
|
2078
|
+
}
|
|
2079
|
+
if (!sweepFlag && f2 > f1) {
|
|
2080
|
+
f2 = f2 - Math.PI * 2;
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
let df = f2 - f1;
|
|
2084
|
+
if (Math.abs(df) > Math.PI * 120 / 180) {
|
|
2085
|
+
const f2old = f2;
|
|
2086
|
+
const x2old = x2;
|
|
2087
|
+
const y2old = y2;
|
|
2088
|
+
if (sweepFlag && f2 > f1) {
|
|
2089
|
+
f2 = f1 + Math.PI * 120 / 180 * 1;
|
|
2090
|
+
} else {
|
|
2091
|
+
f2 = f1 + Math.PI * 120 / 180 * -1;
|
|
2092
|
+
}
|
|
2093
|
+
x2 = cx + r1 * Math.cos(f2);
|
|
2094
|
+
y2 = cy + r22 * Math.sin(f2);
|
|
2095
|
+
params = arcToCubicCurves(x2, y2, x2old, y2old, r1, r22, angle, 0, sweepFlag, [f2, f2old, cx, cy]);
|
|
2096
|
+
}
|
|
2097
|
+
df = f2 - f1;
|
|
2098
|
+
const c1 = Math.cos(f1);
|
|
2099
|
+
const s1 = Math.sin(f1);
|
|
2100
|
+
const c2 = Math.cos(f2);
|
|
2101
|
+
const s2 = Math.sin(f2);
|
|
2102
|
+
const t = Math.tan(df / 4);
|
|
2103
|
+
const hx = 4 / 3 * r1 * t;
|
|
2104
|
+
const hy = 4 / 3 * r22 * t;
|
|
2105
|
+
const m1 = [x1, y1];
|
|
2106
|
+
const m2 = [x1 + hx * s1, y1 - hy * c1];
|
|
2107
|
+
const m3 = [x2 + hx * s2, y2 - hy * c2];
|
|
2108
|
+
const m4 = [x2, y2];
|
|
2109
|
+
m2[0] = 2 * m1[0] - m2[0];
|
|
2110
|
+
m2[1] = 2 * m1[1] - m2[1];
|
|
2111
|
+
if (recursive) {
|
|
2112
|
+
return [m2, m3, m4].concat(params);
|
|
2113
|
+
} else {
|
|
2114
|
+
params = [m2, m3, m4].concat(params);
|
|
2115
|
+
const curves = [];
|
|
2116
|
+
for (let i = 0; i < params.length; i += 3) {
|
|
2117
|
+
const r12 = rotate(params[i][0], params[i][1], angleRad);
|
|
2118
|
+
const r23 = rotate(params[i + 1][0], params[i + 1][1], angleRad);
|
|
2119
|
+
const r3 = rotate(params[i + 2][0], params[i + 2][1], angleRad);
|
|
2120
|
+
curves.push([r12[0], r12[1], r23[0], r23[1], r3[0], r3[1]]);
|
|
2121
|
+
}
|
|
2122
|
+
return curves;
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
// ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/renderer.js
|
|
2127
|
+
var helper = {
|
|
2128
|
+
randOffset,
|
|
2129
|
+
randOffsetWithRange,
|
|
2130
|
+
ellipse,
|
|
2131
|
+
doubleLineOps: doubleLineFillOps
|
|
2132
|
+
};
|
|
2133
|
+
function line(x1, y1, x2, y2, o) {
|
|
2134
|
+
return { type: "path", ops: _doubleLine(x1, y1, x2, y2, o) };
|
|
2135
|
+
}
|
|
2136
|
+
function linearPath(points, close, o) {
|
|
2137
|
+
const len = (points || []).length;
|
|
2138
|
+
if (len > 2) {
|
|
2139
|
+
const ops = [];
|
|
2140
|
+
for (let i = 0; i < len - 1; i++) {
|
|
2141
|
+
ops.push(..._doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o));
|
|
2142
|
+
}
|
|
2143
|
+
if (close) {
|
|
2144
|
+
ops.push(..._doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o));
|
|
2145
|
+
}
|
|
2146
|
+
return { type: "path", ops };
|
|
2147
|
+
} else if (len === 2) {
|
|
2148
|
+
return line(points[0][0], points[0][1], points[1][0], points[1][1], o);
|
|
2149
|
+
}
|
|
2150
|
+
return { type: "path", ops: [] };
|
|
2151
|
+
}
|
|
2152
|
+
function polygon(points, o) {
|
|
2153
|
+
return linearPath(points, true, o);
|
|
2154
|
+
}
|
|
2155
|
+
function rectangle(x, y, width, height, o) {
|
|
2156
|
+
const points = [
|
|
2157
|
+
[x, y],
|
|
2158
|
+
[x + width, y],
|
|
2159
|
+
[x + width, y + height],
|
|
2160
|
+
[x, y + height]
|
|
2161
|
+
];
|
|
2162
|
+
return polygon(points, o);
|
|
2163
|
+
}
|
|
2164
|
+
function curve(inputPoints, o) {
|
|
2165
|
+
if (inputPoints.length) {
|
|
2166
|
+
const p1 = inputPoints[0];
|
|
2167
|
+
const pointsList = typeof p1[0] === "number" ? [inputPoints] : inputPoints;
|
|
2168
|
+
const o1 = _curveWithOffset(pointsList[0], 1 * (1 + o.roughness * 0.2), o);
|
|
2169
|
+
const o2 = o.disableMultiStroke ? [] : _curveWithOffset(pointsList[0], 1.5 * (1 + o.roughness * 0.22), cloneOptionsAlterSeed(o));
|
|
2170
|
+
for (let i = 1; i < pointsList.length; i++) {
|
|
2171
|
+
const points = pointsList[i];
|
|
2172
|
+
if (points.length) {
|
|
2173
|
+
const underlay = _curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o);
|
|
2174
|
+
const overlay = o.disableMultiStroke ? [] : _curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), cloneOptionsAlterSeed(o));
|
|
2175
|
+
for (const item of underlay) {
|
|
2176
|
+
if (item.op !== "move") {
|
|
2177
|
+
o1.push(item);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
for (const item of overlay) {
|
|
2181
|
+
if (item.op !== "move") {
|
|
2182
|
+
o2.push(item);
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
return { type: "path", ops: o1.concat(o2) };
|
|
2188
|
+
}
|
|
2189
|
+
return { type: "path", ops: [] };
|
|
2190
|
+
}
|
|
2191
|
+
function ellipse(x, y, width, height, o) {
|
|
2192
|
+
const params = generateEllipseParams(width, height, o);
|
|
2193
|
+
return ellipseWithParams(x, y, o, params).opset;
|
|
2194
|
+
}
|
|
2195
|
+
function generateEllipseParams(width, height, o) {
|
|
2196
|
+
const psq = Math.sqrt(Math.PI * 2 * Math.sqrt((Math.pow(width / 2, 2) + Math.pow(height / 2, 2)) / 2));
|
|
2197
|
+
const stepCount = Math.ceil(Math.max(o.curveStepCount, o.curveStepCount / Math.sqrt(200) * psq));
|
|
2198
|
+
const increment = Math.PI * 2 / stepCount;
|
|
2199
|
+
let rx = Math.abs(width / 2);
|
|
2200
|
+
let ry = Math.abs(height / 2);
|
|
2201
|
+
const curveFitRandomness = 1 - o.curveFitting;
|
|
2202
|
+
rx += _offsetOpt(rx * curveFitRandomness, o);
|
|
2203
|
+
ry += _offsetOpt(ry * curveFitRandomness, o);
|
|
2204
|
+
return { increment, rx, ry };
|
|
2205
|
+
}
|
|
2206
|
+
function ellipseWithParams(x, y, o, ellipseParams) {
|
|
2207
|
+
const [ap1, cp1] = _computeEllipsePoints(ellipseParams.increment, x, y, ellipseParams.rx, ellipseParams.ry, 1, ellipseParams.increment * _offset(0.1, _offset(0.4, 1, o), o), o);
|
|
2208
|
+
let o1 = _curve(ap1, null, o);
|
|
2209
|
+
if (!o.disableMultiStroke && o.roughness !== 0) {
|
|
2210
|
+
const [ap2] = _computeEllipsePoints(ellipseParams.increment, x, y, ellipseParams.rx, ellipseParams.ry, 1.5, 0, o);
|
|
2211
|
+
const o2 = _curve(ap2, null, o);
|
|
2212
|
+
o1 = o1.concat(o2);
|
|
2213
|
+
}
|
|
2214
|
+
return {
|
|
2215
|
+
estimatedPoints: cp1,
|
|
2216
|
+
opset: { type: "path", ops: o1 }
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
function arc(x, y, width, height, start, stop, closed, roughClosure, o) {
|
|
2220
|
+
const cx = x;
|
|
2221
|
+
const cy = y;
|
|
2222
|
+
let rx = Math.abs(width / 2);
|
|
2223
|
+
let ry = Math.abs(height / 2);
|
|
2224
|
+
rx += _offsetOpt(rx * 0.01, o);
|
|
2225
|
+
ry += _offsetOpt(ry * 0.01, o);
|
|
2226
|
+
let strt = start;
|
|
2227
|
+
let stp = stop;
|
|
2228
|
+
while (strt < 0) {
|
|
2229
|
+
strt += Math.PI * 2;
|
|
2230
|
+
stp += Math.PI * 2;
|
|
2231
|
+
}
|
|
2232
|
+
if (stp - strt > Math.PI * 2) {
|
|
2233
|
+
strt = 0;
|
|
2234
|
+
stp = Math.PI * 2;
|
|
2235
|
+
}
|
|
2236
|
+
const ellipseInc = Math.PI * 2 / o.curveStepCount;
|
|
2237
|
+
const arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2);
|
|
2238
|
+
const ops = _arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o);
|
|
2239
|
+
if (!o.disableMultiStroke) {
|
|
2240
|
+
const o2 = _arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o);
|
|
2241
|
+
ops.push(...o2);
|
|
2242
|
+
}
|
|
2243
|
+
if (closed) {
|
|
2244
|
+
if (roughClosure) {
|
|
2245
|
+
ops.push(..._doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o), ..._doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o));
|
|
2246
|
+
} else {
|
|
2247
|
+
ops.push({ op: "lineTo", data: [cx, cy] }, { op: "lineTo", data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] });
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
return { type: "path", ops };
|
|
2251
|
+
}
|
|
2252
|
+
function svgPath(path, o) {
|
|
2253
|
+
const segments = normalize(absolutize(parsePath(path)));
|
|
2254
|
+
const ops = [];
|
|
2255
|
+
let first = [0, 0];
|
|
2256
|
+
let current = [0, 0];
|
|
2257
|
+
for (const { key, data } of segments) {
|
|
2258
|
+
switch (key) {
|
|
2259
|
+
case "M": {
|
|
2260
|
+
current = [data[0], data[1]];
|
|
2261
|
+
first = [data[0], data[1]];
|
|
2262
|
+
break;
|
|
2263
|
+
}
|
|
2264
|
+
case "L":
|
|
2265
|
+
ops.push(..._doubleLine(current[0], current[1], data[0], data[1], o));
|
|
2266
|
+
current = [data[0], data[1]];
|
|
2267
|
+
break;
|
|
2268
|
+
case "C": {
|
|
2269
|
+
const [x1, y1, x2, y2, x, y] = data;
|
|
2270
|
+
ops.push(..._bezierTo(x1, y1, x2, y2, x, y, current, o));
|
|
2271
|
+
current = [x, y];
|
|
2272
|
+
break;
|
|
2273
|
+
}
|
|
2274
|
+
case "Z":
|
|
2275
|
+
ops.push(..._doubleLine(current[0], current[1], first[0], first[1], o));
|
|
2276
|
+
current = [first[0], first[1]];
|
|
2277
|
+
break;
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
return { type: "path", ops };
|
|
2281
|
+
}
|
|
2282
|
+
function solidFillPolygon(polygonList, o) {
|
|
2283
|
+
const ops = [];
|
|
2284
|
+
for (const points of polygonList) {
|
|
2285
|
+
if (points.length) {
|
|
2286
|
+
const offset = o.maxRandomnessOffset || 0;
|
|
2287
|
+
const len = points.length;
|
|
2288
|
+
if (len > 2) {
|
|
2289
|
+
ops.push({ op: "move", data: [points[0][0] + _offsetOpt(offset, o), points[0][1] + _offsetOpt(offset, o)] });
|
|
2290
|
+
for (let i = 1; i < len; i++) {
|
|
2291
|
+
ops.push({ op: "lineTo", data: [points[i][0] + _offsetOpt(offset, o), points[i][1] + _offsetOpt(offset, o)] });
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
return { type: "fillPath", ops };
|
|
2297
|
+
}
|
|
2298
|
+
function patternFillPolygons(polygonList, o) {
|
|
2299
|
+
return getFiller(o, helper).fillPolygons(polygonList, o);
|
|
2300
|
+
}
|
|
2301
|
+
function patternFillArc(x, y, width, height, start, stop, o) {
|
|
2302
|
+
const cx = x;
|
|
2303
|
+
const cy = y;
|
|
2304
|
+
let rx = Math.abs(width / 2);
|
|
2305
|
+
let ry = Math.abs(height / 2);
|
|
2306
|
+
rx += _offsetOpt(rx * 0.01, o);
|
|
2307
|
+
ry += _offsetOpt(ry * 0.01, o);
|
|
2308
|
+
let strt = start;
|
|
2309
|
+
let stp = stop;
|
|
2310
|
+
while (strt < 0) {
|
|
2311
|
+
strt += Math.PI * 2;
|
|
2312
|
+
stp += Math.PI * 2;
|
|
2313
|
+
}
|
|
2314
|
+
if (stp - strt > Math.PI * 2) {
|
|
2315
|
+
strt = 0;
|
|
2316
|
+
stp = Math.PI * 2;
|
|
2317
|
+
}
|
|
2318
|
+
const increment = (stp - strt) / o.curveStepCount;
|
|
2319
|
+
const points = [];
|
|
2320
|
+
for (let angle = strt; angle <= stp; angle = angle + increment) {
|
|
2321
|
+
points.push([cx + rx * Math.cos(angle), cy + ry * Math.sin(angle)]);
|
|
2322
|
+
}
|
|
2323
|
+
points.push([cx + rx * Math.cos(stp), cy + ry * Math.sin(stp)]);
|
|
2324
|
+
points.push([cx, cy]);
|
|
2325
|
+
return patternFillPolygons([points], o);
|
|
2326
|
+
}
|
|
2327
|
+
function randOffset(x, o) {
|
|
2328
|
+
return _offsetOpt(x, o);
|
|
2329
|
+
}
|
|
2330
|
+
function randOffsetWithRange(min, max, o) {
|
|
2331
|
+
return _offset(min, max, o);
|
|
2332
|
+
}
|
|
2333
|
+
function doubleLineFillOps(x1, y1, x2, y2, o) {
|
|
2334
|
+
return _doubleLine(x1, y1, x2, y2, o, true);
|
|
2335
|
+
}
|
|
2336
|
+
function cloneOptionsAlterSeed(ops) {
|
|
2337
|
+
const result = Object.assign({}, ops);
|
|
2338
|
+
result.randomizer = void 0;
|
|
2339
|
+
if (ops.seed) {
|
|
2340
|
+
result.seed = ops.seed + 1;
|
|
2341
|
+
}
|
|
2342
|
+
return result;
|
|
2343
|
+
}
|
|
2344
|
+
function random(ops) {
|
|
2345
|
+
if (!ops.randomizer) {
|
|
2346
|
+
ops.randomizer = new Random(ops.seed || 0);
|
|
2347
|
+
}
|
|
2348
|
+
return ops.randomizer.next();
|
|
2349
|
+
}
|
|
2350
|
+
function _offset(min, max, ops, roughnessGain = 1) {
|
|
2351
|
+
return ops.roughness * roughnessGain * (random(ops) * (max - min) + min);
|
|
2352
|
+
}
|
|
2353
|
+
function _offsetOpt(x, ops, roughnessGain = 1) {
|
|
2354
|
+
return _offset(-x, x, ops, roughnessGain);
|
|
2355
|
+
}
|
|
2356
|
+
function _doubleLine(x1, y1, x2, y2, o, filling = false) {
|
|
2357
|
+
const singleStroke = filling ? o.disableMultiStrokeFill : o.disableMultiStroke;
|
|
2358
|
+
const o1 = _line(x1, y1, x2, y2, o, true, false);
|
|
2359
|
+
if (singleStroke) {
|
|
2360
|
+
return o1;
|
|
2361
|
+
}
|
|
2362
|
+
const o2 = _line(x1, y1, x2, y2, o, true, true);
|
|
2363
|
+
return o1.concat(o2);
|
|
2364
|
+
}
|
|
2365
|
+
function _line(x1, y1, x2, y2, o, move, overlay) {
|
|
2366
|
+
const lengthSq = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2);
|
|
2367
|
+
const length = Math.sqrt(lengthSq);
|
|
2368
|
+
let roughnessGain = 1;
|
|
2369
|
+
if (length < 200) {
|
|
2370
|
+
roughnessGain = 1;
|
|
2371
|
+
} else if (length > 500) {
|
|
2372
|
+
roughnessGain = 0.4;
|
|
2373
|
+
} else {
|
|
2374
|
+
roughnessGain = -16668e-7 * length + 1.233334;
|
|
2375
|
+
}
|
|
2376
|
+
let offset = o.maxRandomnessOffset || 0;
|
|
2377
|
+
if (offset * offset * 100 > lengthSq) {
|
|
2378
|
+
offset = length / 10;
|
|
2379
|
+
}
|
|
2380
|
+
const halfOffset = offset / 2;
|
|
2381
|
+
const divergePoint = 0.2 + random(o) * 0.2;
|
|
2382
|
+
let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200;
|
|
2383
|
+
let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200;
|
|
2384
|
+
midDispX = _offsetOpt(midDispX, o, roughnessGain);
|
|
2385
|
+
midDispY = _offsetOpt(midDispY, o, roughnessGain);
|
|
2386
|
+
const ops = [];
|
|
2387
|
+
const randomHalf = () => _offsetOpt(halfOffset, o, roughnessGain);
|
|
2388
|
+
const randomFull = () => _offsetOpt(offset, o, roughnessGain);
|
|
2389
|
+
const preserveVertices = o.preserveVertices;
|
|
2390
|
+
if (move) {
|
|
2391
|
+
if (overlay) {
|
|
2392
|
+
ops.push({
|
|
2393
|
+
op: "move",
|
|
2394
|
+
data: [
|
|
2395
|
+
x1 + (preserveVertices ? 0 : randomHalf()),
|
|
2396
|
+
y1 + (preserveVertices ? 0 : randomHalf())
|
|
2397
|
+
]
|
|
2398
|
+
});
|
|
2399
|
+
} else {
|
|
2400
|
+
ops.push({
|
|
2401
|
+
op: "move",
|
|
2402
|
+
data: [
|
|
2403
|
+
x1 + (preserveVertices ? 0 : _offsetOpt(offset, o, roughnessGain)),
|
|
2404
|
+
y1 + (preserveVertices ? 0 : _offsetOpt(offset, o, roughnessGain))
|
|
2405
|
+
]
|
|
2406
|
+
});
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
if (overlay) {
|
|
2410
|
+
ops.push({
|
|
2411
|
+
op: "bcurveTo",
|
|
2412
|
+
data: [
|
|
2413
|
+
midDispX + x1 + (x2 - x1) * divergePoint + randomHalf(),
|
|
2414
|
+
midDispY + y1 + (y2 - y1) * divergePoint + randomHalf(),
|
|
2415
|
+
midDispX + x1 + 2 * (x2 - x1) * divergePoint + randomHalf(),
|
|
2416
|
+
midDispY + y1 + 2 * (y2 - y1) * divergePoint + randomHalf(),
|
|
2417
|
+
x2 + (preserveVertices ? 0 : randomHalf()),
|
|
2418
|
+
y2 + (preserveVertices ? 0 : randomHalf())
|
|
2419
|
+
]
|
|
2420
|
+
});
|
|
2421
|
+
} else {
|
|
2422
|
+
ops.push({
|
|
2423
|
+
op: "bcurveTo",
|
|
2424
|
+
data: [
|
|
2425
|
+
midDispX + x1 + (x2 - x1) * divergePoint + randomFull(),
|
|
2426
|
+
midDispY + y1 + (y2 - y1) * divergePoint + randomFull(),
|
|
2427
|
+
midDispX + x1 + 2 * (x2 - x1) * divergePoint + randomFull(),
|
|
2428
|
+
midDispY + y1 + 2 * (y2 - y1) * divergePoint + randomFull(),
|
|
2429
|
+
x2 + (preserveVertices ? 0 : randomFull()),
|
|
2430
|
+
y2 + (preserveVertices ? 0 : randomFull())
|
|
2431
|
+
]
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
return ops;
|
|
2435
|
+
}
|
|
2436
|
+
function _curveWithOffset(points, offset, o) {
|
|
2437
|
+
if (!points.length) {
|
|
2438
|
+
return [];
|
|
2439
|
+
}
|
|
2440
|
+
const ps = [];
|
|
2441
|
+
ps.push([
|
|
2442
|
+
points[0][0] + _offsetOpt(offset, o),
|
|
2443
|
+
points[0][1] + _offsetOpt(offset, o)
|
|
2444
|
+
]);
|
|
2445
|
+
ps.push([
|
|
2446
|
+
points[0][0] + _offsetOpt(offset, o),
|
|
2447
|
+
points[0][1] + _offsetOpt(offset, o)
|
|
2448
|
+
]);
|
|
2449
|
+
for (let i = 1; i < points.length; i++) {
|
|
2450
|
+
ps.push([
|
|
2451
|
+
points[i][0] + _offsetOpt(offset, o),
|
|
2452
|
+
points[i][1] + _offsetOpt(offset, o)
|
|
2453
|
+
]);
|
|
2454
|
+
if (i === points.length - 1) {
|
|
2455
|
+
ps.push([
|
|
2456
|
+
points[i][0] + _offsetOpt(offset, o),
|
|
2457
|
+
points[i][1] + _offsetOpt(offset, o)
|
|
2458
|
+
]);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
return _curve(ps, null, o);
|
|
2462
|
+
}
|
|
2463
|
+
function _curve(points, closePoint, o) {
|
|
2464
|
+
const len = points.length;
|
|
2465
|
+
const ops = [];
|
|
2466
|
+
if (len > 3) {
|
|
2467
|
+
const b2 = [];
|
|
2468
|
+
const s = 1 - o.curveTightness;
|
|
2469
|
+
ops.push({ op: "move", data: [points[1][0], points[1][1]] });
|
|
2470
|
+
for (let i = 1; i + 2 < len; i++) {
|
|
2471
|
+
const cachedVertArray = points[i];
|
|
2472
|
+
b2[0] = [cachedVertArray[0], cachedVertArray[1]];
|
|
2473
|
+
b2[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6];
|
|
2474
|
+
b2[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6];
|
|
2475
|
+
b2[3] = [points[i + 1][0], points[i + 1][1]];
|
|
2476
|
+
ops.push({ op: "bcurveTo", data: [b2[1][0], b2[1][1], b2[2][0], b2[2][1], b2[3][0], b2[3][1]] });
|
|
2477
|
+
}
|
|
2478
|
+
if (closePoint && closePoint.length === 2) {
|
|
2479
|
+
const ro = o.maxRandomnessOffset;
|
|
2480
|
+
ops.push({ op: "lineTo", data: [closePoint[0] + _offsetOpt(ro, o), closePoint[1] + _offsetOpt(ro, o)] });
|
|
2481
|
+
}
|
|
2482
|
+
} else if (len === 3) {
|
|
2483
|
+
ops.push({ op: "move", data: [points[1][0], points[1][1]] });
|
|
2484
|
+
ops.push({
|
|
2485
|
+
op: "bcurveTo",
|
|
2486
|
+
data: [
|
|
2487
|
+
points[1][0],
|
|
2488
|
+
points[1][1],
|
|
2489
|
+
points[2][0],
|
|
2490
|
+
points[2][1],
|
|
2491
|
+
points[2][0],
|
|
2492
|
+
points[2][1]
|
|
2493
|
+
]
|
|
2494
|
+
});
|
|
2495
|
+
} else if (len === 2) {
|
|
2496
|
+
ops.push(..._line(points[0][0], points[0][1], points[1][0], points[1][1], o, true, true));
|
|
2497
|
+
}
|
|
2498
|
+
return ops;
|
|
2499
|
+
}
|
|
2500
|
+
function _computeEllipsePoints(increment, cx, cy, rx, ry, offset, overlap, o) {
|
|
2501
|
+
const coreOnly = o.roughness === 0;
|
|
2502
|
+
const corePoints = [];
|
|
2503
|
+
const allPoints = [];
|
|
2504
|
+
if (coreOnly) {
|
|
2505
|
+
increment = increment / 4;
|
|
2506
|
+
allPoints.push([
|
|
2507
|
+
cx + rx * Math.cos(-increment),
|
|
2508
|
+
cy + ry * Math.sin(-increment)
|
|
2509
|
+
]);
|
|
2510
|
+
for (let angle = 0; angle <= Math.PI * 2; angle = angle + increment) {
|
|
2511
|
+
const p = [
|
|
2512
|
+
cx + rx * Math.cos(angle),
|
|
2513
|
+
cy + ry * Math.sin(angle)
|
|
2514
|
+
];
|
|
2515
|
+
corePoints.push(p);
|
|
2516
|
+
allPoints.push(p);
|
|
2517
|
+
}
|
|
2518
|
+
allPoints.push([
|
|
2519
|
+
cx + rx * Math.cos(0),
|
|
2520
|
+
cy + ry * Math.sin(0)
|
|
2521
|
+
]);
|
|
2522
|
+
allPoints.push([
|
|
2523
|
+
cx + rx * Math.cos(increment),
|
|
2524
|
+
cy + ry * Math.sin(increment)
|
|
2525
|
+
]);
|
|
2526
|
+
} else {
|
|
2527
|
+
const radOffset = _offsetOpt(0.5, o) - Math.PI / 2;
|
|
2528
|
+
allPoints.push([
|
|
2529
|
+
_offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment),
|
|
2530
|
+
_offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment)
|
|
2531
|
+
]);
|
|
2532
|
+
const endAngle = Math.PI * 2 + radOffset - 0.01;
|
|
2533
|
+
for (let angle = radOffset; angle < endAngle; angle = angle + increment) {
|
|
2534
|
+
const p = [
|
|
2535
|
+
_offsetOpt(offset, o) + cx + rx * Math.cos(angle),
|
|
2536
|
+
_offsetOpt(offset, o) + cy + ry * Math.sin(angle)
|
|
2537
|
+
];
|
|
2538
|
+
corePoints.push(p);
|
|
2539
|
+
allPoints.push(p);
|
|
2540
|
+
}
|
|
2541
|
+
allPoints.push([
|
|
2542
|
+
_offsetOpt(offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5),
|
|
2543
|
+
_offsetOpt(offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5)
|
|
2544
|
+
]);
|
|
2545
|
+
allPoints.push([
|
|
2546
|
+
_offsetOpt(offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap),
|
|
2547
|
+
_offsetOpt(offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap)
|
|
2548
|
+
]);
|
|
2549
|
+
allPoints.push([
|
|
2550
|
+
_offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5),
|
|
2551
|
+
_offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5)
|
|
2552
|
+
]);
|
|
2553
|
+
}
|
|
2554
|
+
return [allPoints, corePoints];
|
|
2555
|
+
}
|
|
2556
|
+
function _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) {
|
|
2557
|
+
const radOffset = strt + _offsetOpt(0.1, o);
|
|
2558
|
+
const points = [];
|
|
2559
|
+
points.push([
|
|
2560
|
+
_offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment),
|
|
2561
|
+
_offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment)
|
|
2562
|
+
]);
|
|
2563
|
+
for (let angle = radOffset; angle <= stp; angle = angle + increment) {
|
|
2564
|
+
points.push([
|
|
2565
|
+
_offsetOpt(offset, o) + cx + rx * Math.cos(angle),
|
|
2566
|
+
_offsetOpt(offset, o) + cy + ry * Math.sin(angle)
|
|
2567
|
+
]);
|
|
2568
|
+
}
|
|
2569
|
+
points.push([
|
|
2570
|
+
cx + rx * Math.cos(stp),
|
|
2571
|
+
cy + ry * Math.sin(stp)
|
|
2572
|
+
]);
|
|
2573
|
+
points.push([
|
|
2574
|
+
cx + rx * Math.cos(stp),
|
|
2575
|
+
cy + ry * Math.sin(stp)
|
|
2576
|
+
]);
|
|
2577
|
+
return _curve(points, null, o);
|
|
2578
|
+
}
|
|
2579
|
+
function _bezierTo(x1, y1, x2, y2, x, y, current, o) {
|
|
2580
|
+
const ops = [];
|
|
2581
|
+
const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.3];
|
|
2582
|
+
let f = [0, 0];
|
|
2583
|
+
const iterations = o.disableMultiStroke ? 1 : 2;
|
|
2584
|
+
const preserveVertices = o.preserveVertices;
|
|
2585
|
+
for (let i = 0; i < iterations; i++) {
|
|
2586
|
+
if (i === 0) {
|
|
2587
|
+
ops.push({ op: "move", data: [current[0], current[1]] });
|
|
2588
|
+
} else {
|
|
2589
|
+
ops.push({ op: "move", data: [current[0] + (preserveVertices ? 0 : _offsetOpt(ros[0], o)), current[1] + (preserveVertices ? 0 : _offsetOpt(ros[0], o))] });
|
|
2590
|
+
}
|
|
2591
|
+
f = preserveVertices ? [x, y] : [x + _offsetOpt(ros[i], o), y + _offsetOpt(ros[i], o)];
|
|
2592
|
+
ops.push({
|
|
2593
|
+
op: "bcurveTo",
|
|
2594
|
+
data: [
|
|
2595
|
+
x1 + _offsetOpt(ros[i], o),
|
|
2596
|
+
y1 + _offsetOpt(ros[i], o),
|
|
2597
|
+
x2 + _offsetOpt(ros[i], o),
|
|
2598
|
+
y2 + _offsetOpt(ros[i], o),
|
|
2599
|
+
f[0],
|
|
2600
|
+
f[1]
|
|
2601
|
+
]
|
|
2602
|
+
});
|
|
2603
|
+
}
|
|
2604
|
+
return ops;
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
// ../../node_modules/.pnpm/points-on-curve@0.2.0/node_modules/points-on-curve/lib/curve-to-bezier.js
|
|
2608
|
+
function clone(p) {
|
|
2609
|
+
return [...p];
|
|
2610
|
+
}
|
|
2611
|
+
function curveToBezier(pointsIn, curveTightness = 0) {
|
|
2612
|
+
const len = pointsIn.length;
|
|
2613
|
+
if (len < 3) {
|
|
2614
|
+
throw new Error("A curve must have at least three points.");
|
|
2615
|
+
}
|
|
2616
|
+
const out = [];
|
|
2617
|
+
if (len === 3) {
|
|
2618
|
+
out.push(clone(pointsIn[0]), clone(pointsIn[1]), clone(pointsIn[2]), clone(pointsIn[2]));
|
|
2619
|
+
} else {
|
|
2620
|
+
const points = [];
|
|
2621
|
+
points.push(pointsIn[0], pointsIn[0]);
|
|
2622
|
+
for (let i = 1; i < pointsIn.length; i++) {
|
|
2623
|
+
points.push(pointsIn[i]);
|
|
2624
|
+
if (i === pointsIn.length - 1) {
|
|
2625
|
+
points.push(pointsIn[i]);
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
const b2 = [];
|
|
2629
|
+
const s = 1 - curveTightness;
|
|
2630
|
+
out.push(clone(points[0]));
|
|
2631
|
+
for (let i = 1; i + 2 < points.length; i++) {
|
|
2632
|
+
const cachedVertArray = points[i];
|
|
2633
|
+
b2[0] = [cachedVertArray[0], cachedVertArray[1]];
|
|
2634
|
+
b2[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6];
|
|
2635
|
+
b2[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6];
|
|
2636
|
+
b2[3] = [points[i + 1][0], points[i + 1][1]];
|
|
2637
|
+
out.push(b2[1], b2[2], b2[3]);
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
return out;
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
// ../../node_modules/.pnpm/points-on-curve@0.2.0/node_modules/points-on-curve/lib/index.js
|
|
2644
|
+
function distance(p1, p2) {
|
|
2645
|
+
return Math.sqrt(distanceSq(p1, p2));
|
|
2646
|
+
}
|
|
2647
|
+
function distanceSq(p1, p2) {
|
|
2648
|
+
return Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2);
|
|
2649
|
+
}
|
|
2650
|
+
function distanceToSegmentSq(p, v, w) {
|
|
2651
|
+
const l2 = distanceSq(v, w);
|
|
2652
|
+
if (l2 === 0) {
|
|
2653
|
+
return distanceSq(p, v);
|
|
2654
|
+
}
|
|
2655
|
+
let t = ((p[0] - v[0]) * (w[0] - v[0]) + (p[1] - v[1]) * (w[1] - v[1])) / l2;
|
|
2656
|
+
t = Math.max(0, Math.min(1, t));
|
|
2657
|
+
return distanceSq(p, lerp(v, w, t));
|
|
2658
|
+
}
|
|
2659
|
+
function lerp(a2, b2, t) {
|
|
2660
|
+
return [
|
|
2661
|
+
a2[0] + (b2[0] - a2[0]) * t,
|
|
2662
|
+
a2[1] + (b2[1] - a2[1]) * t
|
|
2663
|
+
];
|
|
2664
|
+
}
|
|
2665
|
+
function flatness(points, offset) {
|
|
2666
|
+
const p1 = points[offset + 0];
|
|
2667
|
+
const p2 = points[offset + 1];
|
|
2668
|
+
const p3 = points[offset + 2];
|
|
2669
|
+
const p4 = points[offset + 3];
|
|
2670
|
+
let ux = 3 * p2[0] - 2 * p1[0] - p4[0];
|
|
2671
|
+
ux *= ux;
|
|
2672
|
+
let uy = 3 * p2[1] - 2 * p1[1] - p4[1];
|
|
2673
|
+
uy *= uy;
|
|
2674
|
+
let vx = 3 * p3[0] - 2 * p4[0] - p1[0];
|
|
2675
|
+
vx *= vx;
|
|
2676
|
+
let vy = 3 * p3[1] - 2 * p4[1] - p1[1];
|
|
2677
|
+
vy *= vy;
|
|
2678
|
+
if (ux < vx) {
|
|
2679
|
+
ux = vx;
|
|
2680
|
+
}
|
|
2681
|
+
if (uy < vy) {
|
|
2682
|
+
uy = vy;
|
|
2683
|
+
}
|
|
2684
|
+
return ux + uy;
|
|
2685
|
+
}
|
|
2686
|
+
function getPointsOnBezierCurveWithSplitting(points, offset, tolerance, newPoints) {
|
|
2687
|
+
const outPoints = newPoints || [];
|
|
2688
|
+
if (flatness(points, offset) < tolerance) {
|
|
2689
|
+
const p0 = points[offset + 0];
|
|
2690
|
+
if (outPoints.length) {
|
|
2691
|
+
const d = distance(outPoints[outPoints.length - 1], p0);
|
|
2692
|
+
if (d > 1) {
|
|
2693
|
+
outPoints.push(p0);
|
|
2694
|
+
}
|
|
2695
|
+
} else {
|
|
2696
|
+
outPoints.push(p0);
|
|
2697
|
+
}
|
|
2698
|
+
outPoints.push(points[offset + 3]);
|
|
2699
|
+
} else {
|
|
2700
|
+
const t = 0.5;
|
|
2701
|
+
const p1 = points[offset + 0];
|
|
2702
|
+
const p2 = points[offset + 1];
|
|
2703
|
+
const p3 = points[offset + 2];
|
|
2704
|
+
const p4 = points[offset + 3];
|
|
2705
|
+
const q1 = lerp(p1, p2, t);
|
|
2706
|
+
const q2 = lerp(p2, p3, t);
|
|
2707
|
+
const q3 = lerp(p3, p4, t);
|
|
2708
|
+
const r1 = lerp(q1, q2, t);
|
|
2709
|
+
const r22 = lerp(q2, q3, t);
|
|
2710
|
+
const red = lerp(r1, r22, t);
|
|
2711
|
+
getPointsOnBezierCurveWithSplitting([p1, q1, r1, red], 0, tolerance, outPoints);
|
|
2712
|
+
getPointsOnBezierCurveWithSplitting([red, r22, q3, p4], 0, tolerance, outPoints);
|
|
2713
|
+
}
|
|
2714
|
+
return outPoints;
|
|
2715
|
+
}
|
|
2716
|
+
function simplify(points, distance2) {
|
|
2717
|
+
return simplifyPoints(points, 0, points.length, distance2);
|
|
2718
|
+
}
|
|
2719
|
+
function simplifyPoints(points, start, end, epsilon, newPoints) {
|
|
2720
|
+
const outPoints = newPoints || [];
|
|
2721
|
+
const s = points[start];
|
|
2722
|
+
const e = points[end - 1];
|
|
2723
|
+
let maxDistSq = 0;
|
|
2724
|
+
let maxNdx = 1;
|
|
2725
|
+
for (let i = start + 1; i < end - 1; ++i) {
|
|
2726
|
+
const distSq = distanceToSegmentSq(points[i], s, e);
|
|
2727
|
+
if (distSq > maxDistSq) {
|
|
2728
|
+
maxDistSq = distSq;
|
|
2729
|
+
maxNdx = i;
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
if (Math.sqrt(maxDistSq) > epsilon) {
|
|
2733
|
+
simplifyPoints(points, start, maxNdx + 1, epsilon, outPoints);
|
|
2734
|
+
simplifyPoints(points, maxNdx, end, epsilon, outPoints);
|
|
2735
|
+
} else {
|
|
2736
|
+
if (!outPoints.length) {
|
|
2737
|
+
outPoints.push(s);
|
|
2738
|
+
}
|
|
2739
|
+
outPoints.push(e);
|
|
2740
|
+
}
|
|
2741
|
+
return outPoints;
|
|
2742
|
+
}
|
|
2743
|
+
function pointsOnBezierCurves(points, tolerance = 0.15, distance2) {
|
|
2744
|
+
const newPoints = [];
|
|
2745
|
+
const numSegments = (points.length - 1) / 3;
|
|
2746
|
+
for (let i = 0; i < numSegments; i++) {
|
|
2747
|
+
const offset = i * 3;
|
|
2748
|
+
getPointsOnBezierCurveWithSplitting(points, offset, tolerance, newPoints);
|
|
2749
|
+
}
|
|
2750
|
+
if (distance2 && distance2 > 0) {
|
|
2751
|
+
return simplifyPoints(newPoints, 0, newPoints.length, distance2);
|
|
2752
|
+
}
|
|
2753
|
+
return newPoints;
|
|
2754
|
+
}
|
|
2755
|
+
|
|
2756
|
+
// ../../node_modules/.pnpm/points-on-path@0.2.1/node_modules/points-on-path/lib/index.js
|
|
2757
|
+
function pointsOnPath(path, tolerance, distance2) {
|
|
2758
|
+
const segments = parsePath(path);
|
|
2759
|
+
const normalized = normalize(absolutize(segments));
|
|
2760
|
+
const sets = [];
|
|
2761
|
+
let currentPoints = [];
|
|
2762
|
+
let start = [0, 0];
|
|
2763
|
+
let pendingCurve = [];
|
|
2764
|
+
const appendPendingCurve = () => {
|
|
2765
|
+
if (pendingCurve.length >= 4) {
|
|
2766
|
+
currentPoints.push(...pointsOnBezierCurves(pendingCurve, tolerance));
|
|
2767
|
+
}
|
|
2768
|
+
pendingCurve = [];
|
|
2769
|
+
};
|
|
2770
|
+
const appendPendingPoints = () => {
|
|
2771
|
+
appendPendingCurve();
|
|
2772
|
+
if (currentPoints.length) {
|
|
2773
|
+
sets.push(currentPoints);
|
|
2774
|
+
currentPoints = [];
|
|
2775
|
+
}
|
|
2776
|
+
};
|
|
2777
|
+
for (const { key, data } of normalized) {
|
|
2778
|
+
switch (key) {
|
|
2779
|
+
case "M":
|
|
2780
|
+
appendPendingPoints();
|
|
2781
|
+
start = [data[0], data[1]];
|
|
2782
|
+
currentPoints.push(start);
|
|
2783
|
+
break;
|
|
2784
|
+
case "L":
|
|
2785
|
+
appendPendingCurve();
|
|
2786
|
+
currentPoints.push([data[0], data[1]]);
|
|
2787
|
+
break;
|
|
2788
|
+
case "C":
|
|
2789
|
+
if (!pendingCurve.length) {
|
|
2790
|
+
const lastPoint = currentPoints.length ? currentPoints[currentPoints.length - 1] : start;
|
|
2791
|
+
pendingCurve.push([lastPoint[0], lastPoint[1]]);
|
|
2792
|
+
}
|
|
2793
|
+
pendingCurve.push([data[0], data[1]]);
|
|
2794
|
+
pendingCurve.push([data[2], data[3]]);
|
|
2795
|
+
pendingCurve.push([data[4], data[5]]);
|
|
2796
|
+
break;
|
|
2797
|
+
case "Z":
|
|
2798
|
+
appendPendingCurve();
|
|
2799
|
+
currentPoints.push([start[0], start[1]]);
|
|
2800
|
+
break;
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
appendPendingPoints();
|
|
2804
|
+
if (!distance2) {
|
|
2805
|
+
return sets;
|
|
2806
|
+
}
|
|
2807
|
+
const out = [];
|
|
2808
|
+
for (const set of sets) {
|
|
2809
|
+
const simplifiedSet = simplify(set, distance2);
|
|
2810
|
+
if (simplifiedSet.length) {
|
|
2811
|
+
out.push(simplifiedSet);
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
return out;
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
// ../../node_modules/.pnpm/roughjs@4.6.6/node_modules/roughjs/bin/generator.js
|
|
2818
|
+
var NOS = "none";
|
|
2819
|
+
var RoughGenerator = class {
|
|
2820
|
+
constructor(config) {
|
|
2821
|
+
this.defaultOptions = {
|
|
2822
|
+
maxRandomnessOffset: 2,
|
|
2823
|
+
roughness: 1,
|
|
2824
|
+
bowing: 1,
|
|
2825
|
+
stroke: "#000",
|
|
2826
|
+
strokeWidth: 1,
|
|
2827
|
+
curveTightness: 0,
|
|
2828
|
+
curveFitting: 0.95,
|
|
2829
|
+
curveStepCount: 9,
|
|
2830
|
+
fillStyle: "hachure",
|
|
2831
|
+
fillWeight: -1,
|
|
2832
|
+
hachureAngle: -41,
|
|
2833
|
+
hachureGap: -1,
|
|
2834
|
+
dashOffset: -1,
|
|
2835
|
+
dashGap: -1,
|
|
2836
|
+
zigzagOffset: -1,
|
|
2837
|
+
seed: 0,
|
|
2838
|
+
disableMultiStroke: false,
|
|
2839
|
+
disableMultiStrokeFill: false,
|
|
2840
|
+
preserveVertices: false,
|
|
2841
|
+
fillShapeRoughnessGain: 0.8
|
|
2842
|
+
};
|
|
2843
|
+
this.config = config || {};
|
|
2844
|
+
if (this.config.options) {
|
|
2845
|
+
this.defaultOptions = this._o(this.config.options);
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
static newSeed() {
|
|
2849
|
+
return randomSeed();
|
|
2850
|
+
}
|
|
2851
|
+
_o(options) {
|
|
2852
|
+
return options ? Object.assign({}, this.defaultOptions, options) : this.defaultOptions;
|
|
2853
|
+
}
|
|
2854
|
+
_d(shape2, sets, options) {
|
|
2855
|
+
return { shape: shape2, sets: sets || [], options: options || this.defaultOptions };
|
|
2856
|
+
}
|
|
2857
|
+
line(x1, y1, x2, y2, options) {
|
|
2858
|
+
const o = this._o(options);
|
|
2859
|
+
return this._d("line", [line(x1, y1, x2, y2, o)], o);
|
|
2860
|
+
}
|
|
2861
|
+
rectangle(x, y, width, height, options) {
|
|
2862
|
+
const o = this._o(options);
|
|
2863
|
+
const paths = [];
|
|
2864
|
+
const outline = rectangle(x, y, width, height, o);
|
|
2865
|
+
if (o.fill) {
|
|
2866
|
+
const points = [[x, y], [x + width, y], [x + width, y + height], [x, y + height]];
|
|
2867
|
+
if (o.fillStyle === "solid") {
|
|
2868
|
+
paths.push(solidFillPolygon([points], o));
|
|
2869
|
+
} else {
|
|
2870
|
+
paths.push(patternFillPolygons([points], o));
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
if (o.stroke !== NOS) {
|
|
2874
|
+
paths.push(outline);
|
|
2875
|
+
}
|
|
2876
|
+
return this._d("rectangle", paths, o);
|
|
2877
|
+
}
|
|
2878
|
+
ellipse(x, y, width, height, options) {
|
|
2879
|
+
const o = this._o(options);
|
|
2880
|
+
const paths = [];
|
|
2881
|
+
const ellipseParams = generateEllipseParams(width, height, o);
|
|
2882
|
+
const ellipseResponse = ellipseWithParams(x, y, o, ellipseParams);
|
|
2883
|
+
if (o.fill) {
|
|
2884
|
+
if (o.fillStyle === "solid") {
|
|
2885
|
+
const shape2 = ellipseWithParams(x, y, o, ellipseParams).opset;
|
|
2886
|
+
shape2.type = "fillPath";
|
|
2887
|
+
paths.push(shape2);
|
|
2888
|
+
} else {
|
|
2889
|
+
paths.push(patternFillPolygons([ellipseResponse.estimatedPoints], o));
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
if (o.stroke !== NOS) {
|
|
2893
|
+
paths.push(ellipseResponse.opset);
|
|
2894
|
+
}
|
|
2895
|
+
return this._d("ellipse", paths, o);
|
|
2896
|
+
}
|
|
2897
|
+
circle(x, y, diameter, options) {
|
|
2898
|
+
const ret = this.ellipse(x, y, diameter, diameter, options);
|
|
2899
|
+
ret.shape = "circle";
|
|
2900
|
+
return ret;
|
|
2901
|
+
}
|
|
2902
|
+
linearPath(points, options) {
|
|
2903
|
+
const o = this._o(options);
|
|
2904
|
+
return this._d("linearPath", [linearPath(points, false, o)], o);
|
|
2905
|
+
}
|
|
2906
|
+
arc(x, y, width, height, start, stop, closed = false, options) {
|
|
2907
|
+
const o = this._o(options);
|
|
2908
|
+
const paths = [];
|
|
2909
|
+
const outline = arc(x, y, width, height, start, stop, closed, true, o);
|
|
2910
|
+
if (closed && o.fill) {
|
|
2911
|
+
if (o.fillStyle === "solid") {
|
|
2912
|
+
const fillOptions = Object.assign({}, o);
|
|
2913
|
+
fillOptions.disableMultiStroke = true;
|
|
2914
|
+
const shape2 = arc(x, y, width, height, start, stop, true, false, fillOptions);
|
|
2915
|
+
shape2.type = "fillPath";
|
|
2916
|
+
paths.push(shape2);
|
|
2917
|
+
} else {
|
|
2918
|
+
paths.push(patternFillArc(x, y, width, height, start, stop, o));
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
if (o.stroke !== NOS) {
|
|
2922
|
+
paths.push(outline);
|
|
2923
|
+
}
|
|
2924
|
+
return this._d("arc", paths, o);
|
|
2925
|
+
}
|
|
2926
|
+
curve(points, options) {
|
|
2927
|
+
const o = this._o(options);
|
|
2928
|
+
const paths = [];
|
|
2929
|
+
const outline = curve(points, o);
|
|
2930
|
+
if (o.fill && o.fill !== NOS) {
|
|
2931
|
+
if (o.fillStyle === "solid") {
|
|
2932
|
+
const fillShape = curve(points, Object.assign(Object.assign({}, o), { disableMultiStroke: true, roughness: o.roughness ? o.roughness + o.fillShapeRoughnessGain : 0 }));
|
|
2933
|
+
paths.push({
|
|
2934
|
+
type: "fillPath",
|
|
2935
|
+
ops: this._mergedShape(fillShape.ops)
|
|
2936
|
+
});
|
|
2937
|
+
} else {
|
|
2938
|
+
const polyPoints = [];
|
|
2939
|
+
const inputPoints = points;
|
|
2940
|
+
if (inputPoints.length) {
|
|
2941
|
+
const p1 = inputPoints[0];
|
|
2942
|
+
const pointsList = typeof p1[0] === "number" ? [inputPoints] : inputPoints;
|
|
2943
|
+
for (const points2 of pointsList) {
|
|
2944
|
+
if (points2.length < 3) {
|
|
2945
|
+
polyPoints.push(...points2);
|
|
2946
|
+
} else if (points2.length === 3) {
|
|
2947
|
+
polyPoints.push(...pointsOnBezierCurves(curveToBezier([
|
|
2948
|
+
points2[0],
|
|
2949
|
+
points2[0],
|
|
2950
|
+
points2[1],
|
|
2951
|
+
points2[2]
|
|
2952
|
+
]), 10, (1 + o.roughness) / 2));
|
|
2953
|
+
} else {
|
|
2954
|
+
polyPoints.push(...pointsOnBezierCurves(curveToBezier(points2), 10, (1 + o.roughness) / 2));
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
if (polyPoints.length) {
|
|
2959
|
+
paths.push(patternFillPolygons([polyPoints], o));
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
if (o.stroke !== NOS) {
|
|
2964
|
+
paths.push(outline);
|
|
2965
|
+
}
|
|
2966
|
+
return this._d("curve", paths, o);
|
|
2967
|
+
}
|
|
2968
|
+
polygon(points, options) {
|
|
2969
|
+
const o = this._o(options);
|
|
2970
|
+
const paths = [];
|
|
2971
|
+
const outline = linearPath(points, true, o);
|
|
2972
|
+
if (o.fill) {
|
|
2973
|
+
if (o.fillStyle === "solid") {
|
|
2974
|
+
paths.push(solidFillPolygon([points], o));
|
|
2975
|
+
} else {
|
|
2976
|
+
paths.push(patternFillPolygons([points], o));
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
if (o.stroke !== NOS) {
|
|
2980
|
+
paths.push(outline);
|
|
2981
|
+
}
|
|
2982
|
+
return this._d("polygon", paths, o);
|
|
2983
|
+
}
|
|
2984
|
+
path(d, options) {
|
|
2985
|
+
const o = this._o(options);
|
|
2986
|
+
const paths = [];
|
|
2987
|
+
if (!d) {
|
|
2988
|
+
return this._d("path", paths, o);
|
|
2989
|
+
}
|
|
2990
|
+
d = (d || "").replace(/\n/g, " ").replace(/(-\s)/g, "-").replace("/(ss)/g", " ");
|
|
2991
|
+
const hasFill = o.fill && o.fill !== "transparent" && o.fill !== NOS;
|
|
2992
|
+
const hasStroke = o.stroke !== NOS;
|
|
2993
|
+
const simplified = !!(o.simplification && o.simplification < 1);
|
|
2994
|
+
const distance2 = simplified ? 4 - 4 * (o.simplification || 1) : (1 + o.roughness) / 2;
|
|
2995
|
+
const sets = pointsOnPath(d, 1, distance2);
|
|
2996
|
+
const shape2 = svgPath(d, o);
|
|
2997
|
+
if (hasFill) {
|
|
2998
|
+
if (o.fillStyle === "solid") {
|
|
2999
|
+
if (sets.length === 1) {
|
|
3000
|
+
const fillShape = svgPath(d, Object.assign(Object.assign({}, o), { disableMultiStroke: true, roughness: o.roughness ? o.roughness + o.fillShapeRoughnessGain : 0 }));
|
|
3001
|
+
paths.push({
|
|
3002
|
+
type: "fillPath",
|
|
3003
|
+
ops: this._mergedShape(fillShape.ops)
|
|
3004
|
+
});
|
|
3005
|
+
} else {
|
|
3006
|
+
paths.push(solidFillPolygon(sets, o));
|
|
3007
|
+
}
|
|
3008
|
+
} else {
|
|
3009
|
+
paths.push(patternFillPolygons(sets, o));
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
if (hasStroke) {
|
|
3013
|
+
if (simplified) {
|
|
3014
|
+
sets.forEach((set) => {
|
|
3015
|
+
paths.push(linearPath(set, false, o));
|
|
3016
|
+
});
|
|
3017
|
+
} else {
|
|
3018
|
+
paths.push(shape2);
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
return this._d("path", paths, o);
|
|
3022
|
+
}
|
|
3023
|
+
opsToPath(drawing, fixedDecimals) {
|
|
3024
|
+
let path = "";
|
|
3025
|
+
for (const item of drawing.ops) {
|
|
3026
|
+
const data = typeof fixedDecimals === "number" && fixedDecimals >= 0 ? item.data.map((d) => +d.toFixed(fixedDecimals)) : item.data;
|
|
3027
|
+
switch (item.op) {
|
|
3028
|
+
case "move":
|
|
3029
|
+
path += `M${data[0]} ${data[1]} `;
|
|
3030
|
+
break;
|
|
3031
|
+
case "bcurveTo":
|
|
3032
|
+
path += `C${data[0]} ${data[1]}, ${data[2]} ${data[3]}, ${data[4]} ${data[5]} `;
|
|
3033
|
+
break;
|
|
3034
|
+
case "lineTo":
|
|
3035
|
+
path += `L${data[0]} ${data[1]} `;
|
|
3036
|
+
break;
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
return path.trim();
|
|
3040
|
+
}
|
|
3041
|
+
toPaths(drawable) {
|
|
3042
|
+
const sets = drawable.sets || [];
|
|
3043
|
+
const o = drawable.options || this.defaultOptions;
|
|
3044
|
+
const paths = [];
|
|
3045
|
+
for (const drawing of sets) {
|
|
3046
|
+
let path = null;
|
|
3047
|
+
switch (drawing.type) {
|
|
3048
|
+
case "path":
|
|
3049
|
+
path = {
|
|
3050
|
+
d: this.opsToPath(drawing),
|
|
3051
|
+
stroke: o.stroke,
|
|
3052
|
+
strokeWidth: o.strokeWidth,
|
|
3053
|
+
fill: NOS
|
|
3054
|
+
};
|
|
3055
|
+
break;
|
|
3056
|
+
case "fillPath":
|
|
3057
|
+
path = {
|
|
3058
|
+
d: this.opsToPath(drawing),
|
|
3059
|
+
stroke: NOS,
|
|
3060
|
+
strokeWidth: 0,
|
|
3061
|
+
fill: o.fill || NOS
|
|
3062
|
+
};
|
|
3063
|
+
break;
|
|
3064
|
+
case "fillSketch":
|
|
3065
|
+
path = this.fillSketch(drawing, o);
|
|
3066
|
+
break;
|
|
3067
|
+
}
|
|
3068
|
+
if (path) {
|
|
3069
|
+
paths.push(path);
|
|
3070
|
+
}
|
|
3071
|
+
}
|
|
3072
|
+
return paths;
|
|
3073
|
+
}
|
|
3074
|
+
fillSketch(drawing, o) {
|
|
3075
|
+
let fweight = o.fillWeight;
|
|
3076
|
+
if (fweight < 0) {
|
|
3077
|
+
fweight = o.strokeWidth / 2;
|
|
3078
|
+
}
|
|
3079
|
+
return {
|
|
3080
|
+
d: this.opsToPath(drawing),
|
|
3081
|
+
stroke: o.fill || NOS,
|
|
3082
|
+
strokeWidth: fweight,
|
|
3083
|
+
fill: NOS
|
|
3084
|
+
};
|
|
3085
|
+
}
|
|
3086
|
+
_mergedShape(input) {
|
|
3087
|
+
return input.filter((d, i) => {
|
|
3088
|
+
if (i === 0) {
|
|
3089
|
+
return true;
|
|
3090
|
+
}
|
|
3091
|
+
if (d.op === "move") {
|
|
3092
|
+
return false;
|
|
3093
|
+
}
|
|
3094
|
+
return true;
|
|
3095
|
+
});
|
|
3096
|
+
}
|
|
3097
|
+
};
|
|
3098
|
+
|
|
3099
|
+
// ../../node_modules/.pnpm/perfect-freehand@1.2.0/node_modules/perfect-freehand/dist/esm/index.js
|
|
3100
|
+
function $(e, t, u, x = (h) => h) {
|
|
3101
|
+
return e * x(0.5 - t * (0.5 - u));
|
|
3102
|
+
}
|
|
3103
|
+
function se(e) {
|
|
3104
|
+
return [-e[0], -e[1]];
|
|
3105
|
+
}
|
|
3106
|
+
function l(e, t) {
|
|
3107
|
+
return [e[0] + t[0], e[1] + t[1]];
|
|
3108
|
+
}
|
|
3109
|
+
function a(e, t) {
|
|
3110
|
+
return [e[0] - t[0], e[1] - t[1]];
|
|
3111
|
+
}
|
|
3112
|
+
function b(e, t) {
|
|
3113
|
+
return [e[0] * t, e[1] * t];
|
|
3114
|
+
}
|
|
3115
|
+
function he(e, t) {
|
|
3116
|
+
return [e[0] / t, e[1] / t];
|
|
3117
|
+
}
|
|
3118
|
+
function R(e) {
|
|
3119
|
+
return [e[1], -e[0]];
|
|
3120
|
+
}
|
|
3121
|
+
function B(e, t) {
|
|
3122
|
+
return e[0] * t[0] + e[1] * t[1];
|
|
3123
|
+
}
|
|
3124
|
+
function ue(e, t) {
|
|
3125
|
+
return e[0] === t[0] && e[1] === t[1];
|
|
3126
|
+
}
|
|
3127
|
+
function ge(e) {
|
|
3128
|
+
return Math.hypot(e[0], e[1]);
|
|
3129
|
+
}
|
|
3130
|
+
function de(e) {
|
|
3131
|
+
return e[0] * e[0] + e[1] * e[1];
|
|
3132
|
+
}
|
|
3133
|
+
function A(e, t) {
|
|
3134
|
+
return de(a(e, t));
|
|
3135
|
+
}
|
|
3136
|
+
function G(e) {
|
|
3137
|
+
return he(e, ge(e));
|
|
3138
|
+
}
|
|
3139
|
+
function ie(e, t) {
|
|
3140
|
+
return Math.hypot(e[1] - t[1], e[0] - t[0]);
|
|
3141
|
+
}
|
|
3142
|
+
function L(e, t, u) {
|
|
3143
|
+
let x = Math.sin(u), h = Math.cos(u), y = e[0] - t[0], n = e[1] - t[1], f = y * h - n * x, d = y * x + n * h;
|
|
3144
|
+
return [f + t[0], d + t[1]];
|
|
3145
|
+
}
|
|
3146
|
+
function K(e, t, u) {
|
|
3147
|
+
return l(e, b(a(t, e), u));
|
|
3148
|
+
}
|
|
3149
|
+
function ee(e, t, u) {
|
|
3150
|
+
return l(e, b(t, u));
|
|
3151
|
+
}
|
|
3152
|
+
var { min: C, PI: xe } = Math;
|
|
3153
|
+
var pe = 0.275;
|
|
3154
|
+
var V = xe + 1e-4;
|
|
3155
|
+
function ce(e, t = {}) {
|
|
3156
|
+
let { size: u = 16, smoothing: x = 0.5, thinning: h = 0.5, simulatePressure: y = true, easing: n = (r) => r, start: f = {}, end: d = {}, last: D = false } = t, { cap: S = true, easing: j = (r) => r * (2 - r) } = f, { cap: q = true, easing: c = (r) => --r * r * r + 1 } = d;
|
|
3157
|
+
if (e.length === 0 || u <= 0) return [];
|
|
3158
|
+
let p = e[e.length - 1].runningLength, g = f.taper === false ? 0 : f.taper === true ? Math.max(u, p) : f.taper, T = d.taper === false ? 0 : d.taper === true ? Math.max(u, p) : d.taper, te = Math.pow(u * x, 2), _ = [], M = [], H = e.slice(0, 10).reduce((r, i) => {
|
|
3159
|
+
let o = i.pressure;
|
|
3160
|
+
if (y) {
|
|
3161
|
+
let s = C(1, i.distance / u), W2 = C(1, 1 - s);
|
|
3162
|
+
o = C(1, r + (W2 - r) * (s * pe));
|
|
3163
|
+
}
|
|
3164
|
+
return (r + o) / 2;
|
|
3165
|
+
}, e[0].pressure), m = $(u, h, e[e.length - 1].pressure, n), U, X = e[0].vector, z3 = e[0].point, F = z3, O = z3, E = F, J = false;
|
|
3166
|
+
for (let r = 0; r < e.length; r++) {
|
|
3167
|
+
let { pressure: i } = e[r], { point: o, vector: s, distance: W2, runningLength: I } = e[r];
|
|
3168
|
+
if (r < e.length - 1 && p - I < 3) continue;
|
|
3169
|
+
if (h) {
|
|
3170
|
+
if (y) {
|
|
3171
|
+
let v = C(1, W2 / u), Z = C(1, 1 - v);
|
|
3172
|
+
i = C(1, H + (Z - H) * (v * pe));
|
|
3173
|
+
}
|
|
3174
|
+
m = $(u, h, i, n);
|
|
3175
|
+
} else m = u / 2;
|
|
3176
|
+
U === void 0 && (U = m);
|
|
3177
|
+
let le = I < g ? j(I / g) : 1, fe = p - I < T ? c((p - I) / T) : 1;
|
|
3178
|
+
m = Math.max(0.01, m * Math.min(le, fe));
|
|
3179
|
+
let re = (r < e.length - 1 ? e[r + 1] : e[r]).vector, Y = r < e.length - 1 ? B(s, re) : 1, be = B(s, X) < 0 && !J, ne = Y !== null && Y < 0;
|
|
3180
|
+
if (be || ne) {
|
|
3181
|
+
let v = b(R(X), m);
|
|
3182
|
+
for (let Z = 1 / 13, w = 0; w <= 1; w += Z) O = L(a(o, v), o, V * w), _.push(O), E = L(l(o, v), o, V * -w), M.push(E);
|
|
3183
|
+
z3 = O, F = E, ne && (J = true);
|
|
3184
|
+
continue;
|
|
3185
|
+
}
|
|
3186
|
+
if (J = false, r === e.length - 1) {
|
|
3187
|
+
let v = b(R(s), m);
|
|
3188
|
+
_.push(a(o, v)), M.push(l(o, v));
|
|
3189
|
+
continue;
|
|
3190
|
+
}
|
|
3191
|
+
let oe = b(R(K(re, s, Y)), m);
|
|
3192
|
+
O = a(o, oe), (r <= 1 || A(z3, O) > te) && (_.push(O), z3 = O), E = l(o, oe), (r <= 1 || A(F, E) > te) && (M.push(E), F = E), H = i, X = s;
|
|
3193
|
+
}
|
|
3194
|
+
let P = e[0].point.slice(0, 2), k = e.length > 1 ? e[e.length - 1].point.slice(0, 2) : l(e[0].point, [1, 1]), Q = [], N = [];
|
|
3195
|
+
if (e.length === 1) {
|
|
3196
|
+
if (!(g || T) || D) {
|
|
3197
|
+
let r = ee(P, G(R(a(P, k))), -(U || m)), i = [];
|
|
3198
|
+
for (let o = 1 / 13, s = o; s <= 1; s += o) i.push(L(r, P, V * 2 * s));
|
|
3199
|
+
return i;
|
|
3200
|
+
}
|
|
3201
|
+
} else {
|
|
3202
|
+
if (!(g || T && e.length === 1)) if (S) for (let i = 1 / 13, o = i; o <= 1; o += i) {
|
|
3203
|
+
let s = L(M[0], P, V * o);
|
|
3204
|
+
Q.push(s);
|
|
3205
|
+
}
|
|
3206
|
+
else {
|
|
3207
|
+
let i = a(_[0], M[0]), o = b(i, 0.5), s = b(i, 0.51);
|
|
3208
|
+
Q.push(a(P, o), a(P, s), l(P, s), l(P, o));
|
|
3209
|
+
}
|
|
3210
|
+
let r = R(se(e[e.length - 1].vector));
|
|
3211
|
+
if (T || g && e.length === 1) N.push(k);
|
|
3212
|
+
else if (q) {
|
|
3213
|
+
let i = ee(k, r, m);
|
|
3214
|
+
for (let o = 1 / 29, s = o; s < 1; s += o) N.push(L(i, k, V * 3 * s));
|
|
3215
|
+
} else N.push(l(k, b(r, m)), l(k, b(r, m * 0.99)), a(k, b(r, m * 0.99)), a(k, b(r, m)));
|
|
3216
|
+
}
|
|
3217
|
+
return _.concat(N, M.reverse(), Q);
|
|
3218
|
+
}
|
|
3219
|
+
function me(e, t = {}) {
|
|
3220
|
+
var q;
|
|
3221
|
+
let { streamline: u = 0.5, size: x = 16, last: h = false } = t;
|
|
3222
|
+
if (e.length === 0) return [];
|
|
3223
|
+
let y = 0.15 + (1 - u) * 0.85, n = Array.isArray(e[0]) ? e : e.map(({ x: c, y: p, pressure: g = 0.5 }) => [c, p, g]);
|
|
3224
|
+
if (n.length === 2) {
|
|
3225
|
+
let c = n[1];
|
|
3226
|
+
n = n.slice(0, -1);
|
|
3227
|
+
for (let p = 1; p < 5; p++) n.push(K(n[0], c, p / 4));
|
|
3228
|
+
}
|
|
3229
|
+
n.length === 1 && (n = [...n, [...l(n[0], [1, 1]), ...n[0].slice(2)]]);
|
|
3230
|
+
let f = [{ point: [n[0][0], n[0][1]], pressure: n[0][2] >= 0 ? n[0][2] : 0.25, vector: [1, 1], distance: 0, runningLength: 0 }], d = false, D = 0, S = f[0], j = n.length - 1;
|
|
3231
|
+
for (let c = 1; c < n.length; c++) {
|
|
3232
|
+
let p = h && c === j ? n[c].slice(0, 2) : K(S.point, n[c], y);
|
|
3233
|
+
if (ue(S.point, p)) continue;
|
|
3234
|
+
let g = ie(p, S.point);
|
|
3235
|
+
if (D += g, c < j && !d) {
|
|
3236
|
+
if (D < x) continue;
|
|
3237
|
+
d = true;
|
|
3238
|
+
}
|
|
3239
|
+
S = { point: p, pressure: n[c][2] >= 0 ? n[c][2] : 0.5, vector: G(a(S.point, p)), distance: g, runningLength: D }, f.push(S);
|
|
3240
|
+
}
|
|
3241
|
+
return f[0].vector = ((q = f[1]) == null ? void 0 : q.vector) || [0, 0], f;
|
|
3242
|
+
}
|
|
3243
|
+
function ae(e, t = {}) {
|
|
3244
|
+
return ce(me(e, t), t);
|
|
3245
|
+
}
|
|
3246
|
+
|
|
3247
|
+
// ../diagram/src/export/svg.ts
|
|
3248
|
+
var FONT_FAMILY = {
|
|
3249
|
+
1: "Virgil",
|
|
3250
|
+
2: "Helvetica",
|
|
3251
|
+
3: "Cascadia",
|
|
3252
|
+
5: "Excalifont",
|
|
3253
|
+
6: "Nunito",
|
|
3254
|
+
7: "Lilita One",
|
|
3255
|
+
8: "Comic Shanns",
|
|
3256
|
+
9: "Liberation Sans"
|
|
3257
|
+
};
|
|
3258
|
+
var METRICS = {
|
|
3259
|
+
1: { unitsPerEm: 1e3, ascender: 886, descender: -374 },
|
|
3260
|
+
2: { unitsPerEm: 2048, ascender: 1577, descender: -471 },
|
|
3261
|
+
3: { unitsPerEm: 2048, ascender: 1900, descender: -480 },
|
|
3262
|
+
5: { unitsPerEm: 1e3, ascender: 886, descender: -374 },
|
|
3263
|
+
6: { unitsPerEm: 1e3, ascender: 1011, descender: -353 },
|
|
3264
|
+
7: { unitsPerEm: 1e3, ascender: 923, descender: -220 },
|
|
3265
|
+
8: { unitsPerEm: 1e3, ascender: 750, descender: -250 },
|
|
3266
|
+
9: { unitsPerEm: 2048, ascender: 1854, descender: -434 }
|
|
3267
|
+
};
|
|
3268
|
+
var fontFamilyCss = (id2) => {
|
|
3269
|
+
const name = FONT_FAMILY[id2];
|
|
3270
|
+
if (!name) return "Segoe UI Emoji";
|
|
3271
|
+
return id2 === 5 ? `${name}, Xiaolai, Segoe UI Emoji` : `${name}, Segoe UI Emoji`;
|
|
3272
|
+
};
|
|
3273
|
+
function fontsUsed(elements) {
|
|
3274
|
+
const s = /* @__PURE__ */ new Set();
|
|
3275
|
+
for (const e of elements) {
|
|
3276
|
+
if (e.type === "text") s.add(e.fontFamily ?? 5);
|
|
3277
|
+
if (e.type === "frame" || e.type === "magicframe") s.add(2);
|
|
3278
|
+
}
|
|
3279
|
+
return s;
|
|
3280
|
+
}
|
|
3281
|
+
var DECIMALS = 2;
|
|
3282
|
+
var r2 = (n) => Math.round(n * 100) / 100;
|
|
3283
|
+
var rotate2 = ([x, y], [cx, cy], a2) => [
|
|
3284
|
+
(x - cx) * Math.cos(a2) - (y - cy) * Math.sin(a2) + cx,
|
|
3285
|
+
(x - cx) * Math.sin(a2) + (y - cy) * Math.cos(a2) + cy
|
|
3286
|
+
];
|
|
3287
|
+
var isLinear = (e) => e.type === "arrow" || e.type === "line";
|
|
3288
|
+
var isFrame = (e) => e.type === "frame" || e.type === "magicframe";
|
|
3289
|
+
var isTransparent = (c) => !c || c === "transparent" || c.length === 5 && c.endsWith("0") || c.length === 9 && c.endsWith("00");
|
|
3290
|
+
function localBounds(e) {
|
|
3291
|
+
if ((isLinear(e) || e.type === "freedraw") && e.points?.length) {
|
|
3292
|
+
const xs = e.points.map((p) => p[0]);
|
|
3293
|
+
const ys = e.points.map((p) => p[1]);
|
|
3294
|
+
return [Math.min(...xs), Math.min(...ys), Math.max(...xs), Math.max(...ys)];
|
|
3295
|
+
}
|
|
3296
|
+
return [0, 0, e.width, e.height];
|
|
3297
|
+
}
|
|
3298
|
+
function elementBounds2(e) {
|
|
3299
|
+
const [x1, y1, x2, y2] = localBounds(e);
|
|
3300
|
+
const c = [e.x + (x1 + x2) / 2, e.y + (y1 + y2) / 2];
|
|
3301
|
+
const pts = (isLinear(e) || e.type === "freedraw") && e.points?.length ? e.points.map((p) => [e.x + p[0], e.y + p[1]]) : [
|
|
3302
|
+
[e.x + x1, e.y + y1],
|
|
3303
|
+
[e.x + x2, e.y + y1],
|
|
3304
|
+
[e.x + x2, e.y + y2],
|
|
3305
|
+
[e.x + x1, e.y + y2]
|
|
3306
|
+
];
|
|
3307
|
+
const r = e.angle ? pts.map((p) => rotate2(p, c, e.angle)) : pts;
|
|
3308
|
+
return [Math.min(...r.map((p) => p[0])), Math.min(...r.map((p) => p[1])), Math.max(...r.map((p) => p[0])), Math.max(...r.map((p) => p[1]))];
|
|
3309
|
+
}
|
|
3310
|
+
var gen = new RoughGenerator();
|
|
3311
|
+
var DEFAULT_PROPORTIONAL_RADIUS = 0.25;
|
|
3312
|
+
var DEFAULT_ADAPTIVE_RADIUS = 32;
|
|
3313
|
+
function cornerRadius(x, e) {
|
|
3314
|
+
const t = e.roundness?.type;
|
|
3315
|
+
if (t === 2 || t === 1) return x * DEFAULT_PROPORTIONAL_RADIUS;
|
|
3316
|
+
if (t === 3) {
|
|
3317
|
+
const fixed = e.roundness?.value ?? DEFAULT_ADAPTIVE_RADIUS;
|
|
3318
|
+
return x <= fixed / DEFAULT_PROPORTIONAL_RADIUS ? x * DEFAULT_PROPORTIONAL_RADIUS : fixed;
|
|
3319
|
+
}
|
|
3320
|
+
return 0;
|
|
3321
|
+
}
|
|
3322
|
+
var isPathALoop = (points) => points.length >= 3 && Math.hypot(points[0][0] - points.at(-1)[0], points[0][1] - points.at(-1)[1]) <= 8;
|
|
3323
|
+
function adjustRoughness(e) {
|
|
3324
|
+
const max = Math.max(e.width, e.height);
|
|
3325
|
+
const min = Math.min(e.width, e.height);
|
|
3326
|
+
const roundable = e.type === "rectangle" || e.type === "diamond" || isLinear(e);
|
|
3327
|
+
if (min >= 20 && max >= 50 || min >= 15 && !!e.roundness && roundable || isLinear(e) && max >= 50) return e.roughness;
|
|
3328
|
+
return Math.min(e.roughness / (max < 10 ? 3 : 2), 2.5);
|
|
3329
|
+
}
|
|
3330
|
+
function roughOptions(e, continuousPath = false) {
|
|
3331
|
+
const sw = e.strokeWidth ?? 2;
|
|
3332
|
+
const o = {
|
|
3333
|
+
seed: e.seed,
|
|
3334
|
+
strokeLineDash: e.strokeStyle === "dashed" ? [8, 8 + sw] : e.strokeStyle === "dotted" ? [1.5, 6 + sw] : void 0,
|
|
3335
|
+
disableMultiStroke: e.strokeStyle !== "solid",
|
|
3336
|
+
strokeWidth: e.strokeStyle !== "solid" ? sw + 0.5 : sw,
|
|
3337
|
+
fillWeight: sw / 2,
|
|
3338
|
+
hachureGap: sw * 4,
|
|
3339
|
+
roughness: adjustRoughness(e),
|
|
3340
|
+
stroke: e.strokeColor,
|
|
3341
|
+
preserveVertices: continuousPath || e.roughness < 2,
|
|
3342
|
+
fixedDecimalPlaceDigits: DECIMALS
|
|
3343
|
+
};
|
|
3344
|
+
if (e.type === "rectangle" || e.type === "diamond" || e.type === "ellipse") {
|
|
3345
|
+
o.fillStyle = e.fillStyle;
|
|
3346
|
+
o.fill = isTransparent(e.backgroundColor) ? void 0 : e.backgroundColor;
|
|
3347
|
+
if (e.type === "ellipse") o.curveFitting = 1;
|
|
3348
|
+
} else if ((e.type === "line" || e.type === "freedraw") && isPathALoop(e.points ?? [])) {
|
|
3349
|
+
o.fillStyle = e.fillStyle;
|
|
3350
|
+
o.fill = e.backgroundColor === "transparent" ? void 0 : e.backgroundColor;
|
|
3351
|
+
}
|
|
3352
|
+
return o;
|
|
3353
|
+
}
|
|
3354
|
+
function diamondPoints(e) {
|
|
3355
|
+
const topX = Math.floor(e.width / 2) + 1;
|
|
3356
|
+
const rightY = Math.floor(e.height / 2) + 1;
|
|
3357
|
+
return [topX, 0, e.width, rightY, topX, e.height, 0, rightY];
|
|
3358
|
+
}
|
|
3359
|
+
function elbowPath(points, radius) {
|
|
3360
|
+
const sub = [];
|
|
3361
|
+
const horizontal = (a2, b2) => Math.abs(a2[0] - b2[0]) >= Math.abs(a2[1] - b2[1]);
|
|
3362
|
+
for (let i = 1; i < points.length - 1; i++) {
|
|
3363
|
+
const [prev, p, next] = [points[i - 1], points[i], points[i + 1]];
|
|
3364
|
+
const corner = Math.min(radius, Math.hypot(p[0] - next[0], p[1] - next[1]) / 2, Math.hypot(p[0] - prev[0], p[1] - prev[1]) / 2);
|
|
3365
|
+
if (horizontal(p, prev)) sub.push(prev[0] < p[0] ? [p[0] - corner, p[1]] : [p[0] + corner, p[1]]);
|
|
3366
|
+
else sub.push(prev[1] < p[1] ? [p[0], p[1] - corner] : [p[0], p[1] + corner]);
|
|
3367
|
+
sub.push(p);
|
|
3368
|
+
if (horizontal(next, p)) sub.push(next[0] < p[0] ? [p[0] - corner, p[1]] : [p[0] + corner, p[1]]);
|
|
3369
|
+
else sub.push(next[1] < p[1] ? [p[0], p[1] - corner] : [p[0], p[1] + corner]);
|
|
3370
|
+
}
|
|
3371
|
+
const d = [`M ${points[0][0]} ${points[0][1]}`];
|
|
3372
|
+
for (let i = 0; i < sub.length; i += 3) d.push(`L ${sub[i][0]} ${sub[i][1]}`, `Q ${sub[i + 1][0]} ${sub[i + 1][1]}, ${sub[i + 2][0]} ${sub[i + 2][1]}`);
|
|
3373
|
+
d.push(`L ${points.at(-1)[0]} ${points.at(-1)[1]}`);
|
|
3374
|
+
return d.join(" ");
|
|
3375
|
+
}
|
|
3376
|
+
function arrowheadSize(a2) {
|
|
3377
|
+
if (a2 === "arrow") return 25;
|
|
3378
|
+
if (a2 === "diamond" || a2 === "diamond_outline") return 12;
|
|
3379
|
+
if (a2.startsWith("crowfoot")) return 20;
|
|
3380
|
+
return 15;
|
|
3381
|
+
}
|
|
3382
|
+
var arrowheadAngle = (a2) => a2 === "bar" ? 90 : a2 === "arrow" ? 20 : 25;
|
|
3383
|
+
function curveOps(shape2) {
|
|
3384
|
+
return (shape2.sets.find((s) => s.type === "path") ?? shape2.sets[0]).ops;
|
|
3385
|
+
}
|
|
3386
|
+
function arrowheadPoints(e, shape2, position, head) {
|
|
3387
|
+
const ops = curveOps(shape2);
|
|
3388
|
+
if (ops.length < 2) return null;
|
|
3389
|
+
const index = position === "start" ? 1 : ops.length - 1;
|
|
3390
|
+
const data = ops[index].data;
|
|
3391
|
+
if (data.length !== 6) return null;
|
|
3392
|
+
const p3 = [data[4], data[5]];
|
|
3393
|
+
const p2 = [data[2], data[3]];
|
|
3394
|
+
const p1 = [data[0], data[1]];
|
|
3395
|
+
const prev = ops[index - 1];
|
|
3396
|
+
let p0 = [0, 0];
|
|
3397
|
+
if (prev.op === "move") p0 = [prev.data[0], prev.data[1]];
|
|
3398
|
+
else if (prev.op === "bcurveTo") p0 = [prev.data[4], prev.data[5]];
|
|
3399
|
+
const eq = (t, i) => (1 - t) ** 3 * p3[i] + 3 * t * (1 - t) ** 2 * p2[i] + 3 * t ** 2 * (1 - t) * p1[i] + p0[i] * t ** 3;
|
|
3400
|
+
const [x2, y2] = position === "start" ? p0 : p3;
|
|
3401
|
+
const [x1, y1] = [eq(0.3, 0), eq(0.3, 1)];
|
|
3402
|
+
const dist = Math.hypot(x2 - x1, y2 - y1);
|
|
3403
|
+
const nx = (x2 - x1) / dist;
|
|
3404
|
+
const ny = (y2 - y1) / dist;
|
|
3405
|
+
const pts = e.points;
|
|
3406
|
+
const [cx, cy] = position === "end" ? pts.at(-1) : pts[0];
|
|
3407
|
+
const [px, py] = pts.length > 1 ? position === "end" ? pts.at(-2) : pts[1] : [0, 0];
|
|
3408
|
+
const length = Math.hypot(cx - px, cy - py);
|
|
3409
|
+
const minSize = Math.min(arrowheadSize(head), length * (head === "diamond" || head === "diamond_outline" ? 0.25 : 0.5));
|
|
3410
|
+
const xs = x2 - nx * minSize;
|
|
3411
|
+
const ys = y2 - ny * minSize;
|
|
3412
|
+
if (head === "dot" || head === "circle" || head === "circle_outline") return [x2, y2, Math.hypot(ys - y2, xs - x2) + (e.strokeWidth ?? 2) - 2];
|
|
3413
|
+
const angle = arrowheadAngle(head) * Math.PI / 180;
|
|
3414
|
+
if (head === "crowfoot_many" || head === "crowfoot_one_or_many") {
|
|
3415
|
+
const [x32, y32] = rotate2([x2, y2], [xs, ys], -angle);
|
|
3416
|
+
const [x42, y42] = rotate2([x2, y2], [xs, ys], angle);
|
|
3417
|
+
return [xs, ys, x32, y32, x42, y42];
|
|
3418
|
+
}
|
|
3419
|
+
const [x3, y3] = rotate2([xs, ys], [x2, y2], -angle);
|
|
3420
|
+
const [x4, y4] = rotate2([xs, ys], [x2, y2], angle);
|
|
3421
|
+
if (head === "diamond" || head === "diamond_outline") {
|
|
3422
|
+
let o;
|
|
3423
|
+
if (position === "start") {
|
|
3424
|
+
const [qx, qy] = pts.length > 1 ? pts[1] : [0, 0];
|
|
3425
|
+
o = rotate2([x2 + minSize * 2, y2], [x2, y2], Math.atan2(qy - y2, qx - x2));
|
|
3426
|
+
} else {
|
|
3427
|
+
const [qx, qy] = pts.length > 1 ? pts.at(-2) : [0, 0];
|
|
3428
|
+
o = rotate2([x2 - minSize * 2, y2], [x2, y2], Math.atan2(y2 - qy, x2 - qx));
|
|
3429
|
+
}
|
|
3430
|
+
return [x2, y2, x3, y3, o[0], o[1], x4, y4];
|
|
3431
|
+
}
|
|
3432
|
+
return [x2, y2, x3, y3, x4, y4];
|
|
3433
|
+
}
|
|
3434
|
+
function arrowheadShapes(e, shape2, position, head, options, bg) {
|
|
3435
|
+
const pts = arrowheadPoints(e, shape2, position, head);
|
|
3436
|
+
if (!pts) return [];
|
|
3437
|
+
const o = { ...options };
|
|
3438
|
+
switch (head) {
|
|
3439
|
+
case "dot":
|
|
3440
|
+
case "circle":
|
|
3441
|
+
case "circle_outline": {
|
|
3442
|
+
delete o.strokeLineDash;
|
|
3443
|
+
return [gen.circle(pts[0], pts[1], pts[2], { ...o, fill: head === "circle_outline" ? bg : e.strokeColor, fillStyle: "solid", stroke: e.strokeColor, roughness: Math.min(0.5, o.roughness || 0) })];
|
|
3444
|
+
}
|
|
3445
|
+
case "triangle":
|
|
3446
|
+
case "triangle_outline": {
|
|
3447
|
+
delete o.strokeLineDash;
|
|
3448
|
+
const [x, y, x2, y2, x3, y3] = pts;
|
|
3449
|
+
return [gen.polygon([[x, y], [x2, y2], [x3, y3], [x, y]], { ...o, fill: head === "triangle_outline" ? bg : e.strokeColor, fillStyle: "solid", roughness: Math.min(1, o.roughness || 0) })];
|
|
3450
|
+
}
|
|
3451
|
+
case "diamond":
|
|
3452
|
+
case "diamond_outline": {
|
|
3453
|
+
delete o.strokeLineDash;
|
|
3454
|
+
const [x, y, x2, y2, x3, y3, x4, y4] = pts;
|
|
3455
|
+
return [gen.polygon([[x, y], [x2, y2], [x3, y3], [x4, y4], [x, y]], { ...o, fill: head === "diamond_outline" ? bg : e.strokeColor, fillStyle: "solid", roughness: Math.min(1, o.roughness || 0) })];
|
|
3456
|
+
}
|
|
3457
|
+
case "crowfoot_one": {
|
|
3458
|
+
const [, , x3, y3, x4, y4] = pts;
|
|
3459
|
+
return [gen.line(x3, y3, x4, y4, o)];
|
|
3460
|
+
}
|
|
3461
|
+
default: {
|
|
3462
|
+
const [, , x3, y3, x4, y4] = pts;
|
|
3463
|
+
const [x2, y2] = [pts[0], pts[1]];
|
|
3464
|
+
if (e.strokeStyle === "dotted") o.strokeLineDash = [1.5, 6 + (e.strokeWidth ?? 2) - 1 - 1];
|
|
3465
|
+
else delete o.strokeLineDash;
|
|
3466
|
+
o.roughness = Math.min(1, o.roughness || 0);
|
|
3467
|
+
const out = [gen.line(x3, y3, x2, y2, o), gen.line(x4, y4, x2, y2, o)];
|
|
3468
|
+
if (head === "crowfoot_one_or_many") out.push(...arrowheadShapes(e, shape2, position, "crowfoot_one", options, bg));
|
|
3469
|
+
return out;
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
function shapeOf(e, bg) {
|
|
3474
|
+
switch (e.type) {
|
|
3475
|
+
case "rectangle": {
|
|
3476
|
+
if (e.roundness) {
|
|
3477
|
+
const { width: w, height: h } = e;
|
|
3478
|
+
const r = cornerRadius(Math.min(w, h), e);
|
|
3479
|
+
return [gen.path(`M ${r} 0 L ${w - r} 0 Q ${w} 0, ${w} ${r} L ${w} ${h - r} Q ${w} ${h}, ${w - r} ${h} L ${r} ${h} Q 0 ${h}, 0 ${h - r} L 0 ${r} Q 0 0, ${r} 0`, roughOptions(e, true))];
|
|
3480
|
+
}
|
|
3481
|
+
return [gen.rectangle(0, 0, e.width, e.height, roughOptions(e))];
|
|
3482
|
+
}
|
|
3483
|
+
case "diamond": {
|
|
3484
|
+
const [tX, tY, rX, rY, bX, bY, lX, lY] = diamondPoints(e);
|
|
3485
|
+
if (e.roundness) {
|
|
3486
|
+
const v = cornerRadius(Math.abs(tX - lX), e);
|
|
3487
|
+
const h = cornerRadius(Math.abs(rY - tY), e);
|
|
3488
|
+
return [
|
|
3489
|
+
gen.path(
|
|
3490
|
+
`M ${tX + v} ${tY + h} L ${rX - v} ${rY - h} C ${rX} ${rY}, ${rX} ${rY}, ${rX - v} ${rY + h} L ${bX + v} ${bY - h} C ${bX} ${bY}, ${bX} ${bY}, ${bX - v} ${bY - h} L ${lX + v} ${lY + h} C ${lX} ${lY}, ${lX} ${lY}, ${lX + v} ${lY - h} L ${tX - v} ${tY + h} C ${tX} ${tY}, ${tX} ${tY}, ${tX + v} ${tY + h}`,
|
|
3491
|
+
roughOptions(e, true)
|
|
3492
|
+
)
|
|
3493
|
+
];
|
|
3494
|
+
}
|
|
3495
|
+
return [gen.polygon([[tX, tY], [rX, rY], [bX, bY], [lX, lY]], roughOptions(e))];
|
|
3496
|
+
}
|
|
3497
|
+
case "ellipse":
|
|
3498
|
+
return [gen.ellipse(e.width / 2, e.height / 2, e.width, e.height, roughOptions(e))];
|
|
3499
|
+
case "line":
|
|
3500
|
+
case "arrow": {
|
|
3501
|
+
const o = roughOptions(e);
|
|
3502
|
+
const points = e.points?.length ? e.points : [[0, 0]];
|
|
3503
|
+
let shapes;
|
|
3504
|
+
if (e.elbowed) shapes = [gen.path(elbowPath(points, 16), roughOptions(e, true))];
|
|
3505
|
+
else if (!e.roundness) shapes = [o.fill ? gen.polygon(points, o) : gen.linearPath(points, o)];
|
|
3506
|
+
else shapes = [gen.curve(points, o)];
|
|
3507
|
+
if (e.type === "arrow" && shapes.length) {
|
|
3508
|
+
const { startArrowhead = null, endArrowhead = "arrow" } = e;
|
|
3509
|
+
if (startArrowhead) shapes.push(...arrowheadShapes(e, shapes[0], "start", startArrowhead, o, bg));
|
|
3510
|
+
if (endArrowhead) shapes.push(...arrowheadShapes(e, shapes[0], "end", endArrowhead, o, bg));
|
|
3511
|
+
}
|
|
3512
|
+
return shapes;
|
|
3513
|
+
}
|
|
3514
|
+
case "freedraw":
|
|
3515
|
+
return isPathALoop(e.points ?? []) ? [gen.curve(e.points, { ...roughOptions(e), stroke: "none" })] : [];
|
|
3516
|
+
default:
|
|
3517
|
+
return [];
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
var esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3521
|
+
var attrs = (a2) => Object.entries(a2).filter(([, v]) => v !== void 0 && v !== null && v !== "").map(([k, v]) => ` ${k}="${esc(String(v))}"`).join("");
|
|
3522
|
+
function drawableToSvg(d, extra = {}) {
|
|
3523
|
+
const o = d.options;
|
|
3524
|
+
const parts = d.sets.map((set) => {
|
|
3525
|
+
const path = gen.opsToPath(set, DECIMALS);
|
|
3526
|
+
if (set.type === "path")
|
|
3527
|
+
return `<path${attrs({ d: path, stroke: o.stroke, "stroke-width": o.strokeWidth, fill: "none", "stroke-dasharray": o.strokeLineDash?.join(" ") })}/>`;
|
|
3528
|
+
if (set.type === "fillPath")
|
|
3529
|
+
return `<path${attrs({ d: path, stroke: "none", "stroke-width": 0, fill: o.fill, "fill-rule": d.shape === "curve" || d.shape === "polygon" ? "evenodd" : void 0 })}/>`;
|
|
3530
|
+
const fw = o.fillWeight < 0 ? o.strokeWidth / 2 : o.fillWeight;
|
|
3531
|
+
return `<path${attrs({ d: path, stroke: o.fill, "stroke-width": fw, fill: "none", "stroke-dasharray": o.fillLineDash?.join(" ") })}/>`;
|
|
3532
|
+
});
|
|
3533
|
+
return `<g${attrs(extra)}>${parts.join("")}</g>`;
|
|
3534
|
+
}
|
|
3535
|
+
function freedrawPath(e) {
|
|
3536
|
+
const pts = e.simulatePressure ? e.points : e.points?.length ? e.points.map(([x, y], i) => [x, y, e.pressures?.[i] ?? 0.5]) : [[0, 0, 0.5]];
|
|
3537
|
+
const outline = ae(pts, {
|
|
3538
|
+
simulatePressure: e.simulatePressure,
|
|
3539
|
+
size: (e.strokeWidth ?? 2) * 4.25,
|
|
3540
|
+
thinning: 0.6,
|
|
3541
|
+
smoothing: 0.5,
|
|
3542
|
+
streamline: 0.5,
|
|
3543
|
+
easing: (t) => Math.sin(t * Math.PI / 2),
|
|
3544
|
+
last: !!e.lastCommittedPoint
|
|
3545
|
+
});
|
|
3546
|
+
if (!outline.length) return "";
|
|
3547
|
+
const med = (a2, b2) => [(a2[0] + b2[0]) / 2, (a2[1] + b2[1]) / 2];
|
|
3548
|
+
const d = ["M", outline[0], "Q"];
|
|
3549
|
+
outline.forEach((p, i) => {
|
|
3550
|
+
if (i === outline.length - 1) d.push(p, med(p, outline[0]), "L", outline[0], "Z");
|
|
3551
|
+
else d.push(p, med(p, outline[i + 1]));
|
|
3552
|
+
});
|
|
3553
|
+
return d.map((x) => typeof x === "string" ? x : `${r2(x[0])},${r2(x[1])}`).join(" ");
|
|
3554
|
+
}
|
|
3555
|
+
var RTL = /[-߿יִ-﷽ﹰ-ﻼ]/;
|
|
3556
|
+
function textSvg(e, transform, opacity) {
|
|
3557
|
+
const lines = String(e.text ?? "").replace(/\r\n?/g, "\n").split("\n");
|
|
3558
|
+
const lineHeightPx = e.fontSize * (e.lineHeight ?? 1.25);
|
|
3559
|
+
const m = METRICS[e.fontFamily] ?? METRICS[1];
|
|
3560
|
+
const em = e.fontSize / m.unitsPerEm;
|
|
3561
|
+
const verticalOffset = em * m.ascender + (lineHeightPx - em * m.ascender + em * m.descender) / 2;
|
|
3562
|
+
const hOffset = e.textAlign === "center" ? e.width / 2 : e.textAlign === "right" ? e.width : 0;
|
|
3563
|
+
const direction = RTL.test(e.text ?? "") ? "rtl" : "ltr";
|
|
3564
|
+
const anchor = e.textAlign === "center" ? "middle" : e.textAlign === "right" || direction === "rtl" ? "end" : "start";
|
|
3565
|
+
const body = lines.map(
|
|
3566
|
+
(l2, i) => `<text${attrs({
|
|
3567
|
+
x: r2(hOffset),
|
|
3568
|
+
y: r2(i * lineHeightPx + verticalOffset),
|
|
3569
|
+
"font-family": fontFamilyCss(e.fontFamily),
|
|
3570
|
+
"font-size": `${e.fontSize}px`,
|
|
3571
|
+
fill: e.strokeColor,
|
|
3572
|
+
"text-anchor": anchor,
|
|
3573
|
+
style: "white-space: pre;",
|
|
3574
|
+
direction,
|
|
3575
|
+
"dominant-baseline": "alphabetic"
|
|
3576
|
+
})}>${esc(l2)}</text>`
|
|
3577
|
+
).join("");
|
|
3578
|
+
return `<g${attrs({ transform, "stroke-opacity": opacity !== 1 ? opacity : void 0, "fill-opacity": opacity !== 1 ? opacity : void 0 })}>${body}</g>`;
|
|
3579
|
+
}
|
|
3580
|
+
function frameLabel(f) {
|
|
3581
|
+
const fontSize = 14;
|
|
3582
|
+
const height = fontSize * 1.25;
|
|
3583
|
+
const name = f.name ?? (f.type === "magicframe" ? "AI Frame" : "Frame");
|
|
3584
|
+
const maxChars = Math.max(1, Math.floor(f.width / (fontSize * 0.55)));
|
|
3585
|
+
const text2 = name.length > maxChars ? `${name.slice(0, Math.max(1, maxChars - 1))}\u2026` : name;
|
|
3586
|
+
return {
|
|
3587
|
+
id: `${f.id}.label`,
|
|
3588
|
+
type: "text",
|
|
3589
|
+
x: f.x,
|
|
3590
|
+
y: f.y - 3 - height,
|
|
3591
|
+
width: Math.min(f.width, text2.length * fontSize * 0.55),
|
|
3592
|
+
height,
|
|
3593
|
+
angle: 0,
|
|
3594
|
+
text: text2,
|
|
3595
|
+
fontSize,
|
|
3596
|
+
fontFamily: 2,
|
|
3597
|
+
lineHeight: 1.25,
|
|
3598
|
+
textAlign: "left",
|
|
3599
|
+
strokeColor: "#999999",
|
|
3600
|
+
opacity: 100,
|
|
3601
|
+
version: 1,
|
|
3602
|
+
versionNonce: 0,
|
|
3603
|
+
isDeleted: false
|
|
3604
|
+
};
|
|
3605
|
+
}
|
|
3606
|
+
function exportSvg(input, files, opts = {}) {
|
|
3607
|
+
const bgColor = opts.backgroundColor ?? "#ffffff";
|
|
3608
|
+
const padding = opts.padding ?? 10;
|
|
3609
|
+
const live = input.filter((e) => !e.isDeleted).map((e, i) => ({ e, i })).sort((a2, b2) => a2.e.index && b2.e.index ? a2.e.index < b2.e.index ? -1 : a2.e.index > b2.e.index ? 1 : 0 : a2.i - b2.i).map((x) => x.e);
|
|
3610
|
+
const byId = new Map(live.map((e) => [e.id, e]));
|
|
3611
|
+
const frames = live.filter(isFrame);
|
|
3612
|
+
const elements = [];
|
|
3613
|
+
for (const e of live) {
|
|
3614
|
+
if (isFrame(e)) elements.push(frameLabel(e));
|
|
3615
|
+
elements.push(e);
|
|
3616
|
+
}
|
|
3617
|
+
const roots = elements.filter((e) => isFrame(e) || !e.frameId || !byId.has(e.frameId));
|
|
3618
|
+
let [minX, minY, maxX, maxY] = [Infinity, Infinity, -Infinity, -Infinity];
|
|
3619
|
+
for (const e of roots) {
|
|
3620
|
+
const [a2, b2, c, d] = elementBounds2(e);
|
|
3621
|
+
minX = Math.min(minX, a2);
|
|
3622
|
+
minY = Math.min(minY, b2);
|
|
3623
|
+
maxX = Math.max(maxX, c);
|
|
3624
|
+
maxY = Math.max(maxY, d);
|
|
3625
|
+
}
|
|
3626
|
+
if (!Number.isFinite(minX)) [minX, minY, maxX, maxY] = [0, 0, 0, 0];
|
|
3627
|
+
const width = maxX - minX + padding * 2;
|
|
3628
|
+
const height = maxY - minY + padding * 2;
|
|
3629
|
+
const ox = -minX + padding;
|
|
3630
|
+
const oy = -minY + padding;
|
|
3631
|
+
const scale = opts.scale ?? 1;
|
|
3632
|
+
const defs = [];
|
|
3633
|
+
const body = [];
|
|
3634
|
+
const clipIds = /* @__PURE__ */ new Map();
|
|
3635
|
+
frames.forEach((f, i) => {
|
|
3636
|
+
const id2 = `frame-clip-${i}`;
|
|
3637
|
+
clipIds.set(f.id, id2);
|
|
3638
|
+
const [x1, , x2] = localBounds(f);
|
|
3639
|
+
const cx = (x2 - x1) / 2;
|
|
3640
|
+
const cy = f.height / 2;
|
|
3641
|
+
defs.push(`<clipPath id="${id2}"><rect${attrs({ transform: `translate(${r2(f.x + ox)} ${r2(f.y + oy)}) rotate(${f.angle ?? 0} ${cx} ${cy})`, width: f.width, height: f.height, rx: 8, ry: 8 })}/></clipPath>`);
|
|
3642
|
+
});
|
|
3643
|
+
const clip = (e, svg2) => {
|
|
3644
|
+
const id2 = e.frameId && clipIds.get(e.frameId);
|
|
3645
|
+
return id2 ? `<g clip-path="url(#${id2})">${svg2}</g>` : svg2;
|
|
3646
|
+
};
|
|
3647
|
+
const imageSymbols = /* @__PURE__ */ new Set();
|
|
3648
|
+
const render = (e) => {
|
|
3649
|
+
const [x1, y1, x2, y2] = localBounds(e);
|
|
3650
|
+
const cx = (x2 - x1) / 2 + x1;
|
|
3651
|
+
const cy = (y2 - y1) / 2 + y1;
|
|
3652
|
+
const deg = 180 * (e.angle ?? 0) / Math.PI;
|
|
3653
|
+
let tx = e.x + ox;
|
|
3654
|
+
let ty = e.y + oy;
|
|
3655
|
+
const frame2 = e.frameId ? byId.get(e.frameId) : void 0;
|
|
3656
|
+
const opacity = (frame2?.opacity ?? 100) * (e.opacity ?? 100) / 1e4;
|
|
3657
|
+
const op = opacity !== 1 ? { "stroke-opacity": opacity, "fill-opacity": opacity } : {};
|
|
3658
|
+
const transform = () => `translate(${r2(tx)} ${r2(ty)}) rotate(${r2(deg)} ${r2(cx)} ${r2(cy)})`;
|
|
3659
|
+
switch (e.type) {
|
|
3660
|
+
case "rectangle":
|
|
3661
|
+
case "diamond":
|
|
3662
|
+
case "ellipse": {
|
|
3663
|
+
const [shape2] = shapeOf(e, bgColor);
|
|
3664
|
+
body.push(clip(e, drawableToSvg(shape2, { ...op, "stroke-linecap": "round", transform: transform() })));
|
|
3665
|
+
return;
|
|
3666
|
+
}
|
|
3667
|
+
case "line":
|
|
3668
|
+
case "arrow": {
|
|
3669
|
+
const label2 = e.boundElements?.find((b2) => b2.type === "text");
|
|
3670
|
+
const t = label2 && byId.get(label2.id);
|
|
3671
|
+
let mask = "";
|
|
3672
|
+
if (t) {
|
|
3673
|
+
mask = `<mask id="mask-${esc(e.id)}"><rect x="0" y="0" fill="#fff" width="${r2(e.width + 100 + tx)}" height="${r2(e.height + 100 + ty)}"/><rect fill="#000" x="${r2(tx + t.x - e.x)}" y="${r2(ty + t.y - e.y)}" width="${r2(t.width)}" height="${r2(t.height)}" opacity="1"/></mask>`;
|
|
3674
|
+
}
|
|
3675
|
+
const loopFill = e.type === "line" && isPathALoop(e.points ?? []) && e.backgroundColor !== "transparent" ? { "fill-rule": "evenodd" } : {};
|
|
3676
|
+
const g = shapeOf(e, bgColor).map((s) => drawableToSvg(s, { ...op, ...loopFill, transform: transform() })).join("");
|
|
3677
|
+
body.push(clip(e, `<g${attrs({ mask: t ? `url(#mask-${e.id})` : void 0, "stroke-linecap": "round" })}>${g}</g>${mask}`));
|
|
3678
|
+
return;
|
|
3679
|
+
}
|
|
3680
|
+
case "freedraw": {
|
|
3681
|
+
const [fill2] = shapeOf(e, bgColor);
|
|
3682
|
+
const path = `<path fill="${esc(e.strokeColor)}" d="${freedrawPath(e)}"/>`;
|
|
3683
|
+
const inner = fill2 ? drawableToSvg(fill2).replace(/<\/g>$/, `${path}</g>`) : `<g>${path}</g>`;
|
|
3684
|
+
body.push(clip(e, inner.replace(/^<g/, `<g${attrs({ ...op, transform: transform(), stroke: "none" })}`)));
|
|
3685
|
+
return;
|
|
3686
|
+
}
|
|
3687
|
+
case "image": {
|
|
3688
|
+
const file = e.fileId && files[e.fileId];
|
|
3689
|
+
if (!file) return;
|
|
3690
|
+
const symbolId = `image-${file.id}`;
|
|
3691
|
+
if (!imageSymbols.has(symbolId)) {
|
|
3692
|
+
imageSymbols.add(symbolId);
|
|
3693
|
+
defs.unshift(`<symbol id="${esc(symbolId)}"><image href="${esc(file.dataURL)}" preserveAspectRatio="none" width="100%" height="100%"/></symbol>`);
|
|
3694
|
+
}
|
|
3695
|
+
const flip = e.scale && (e.scale[0] !== 1 || e.scale[1] !== 1) ? `translate(${cx} ${cy}) scale(${e.scale[0]} ${e.scale[1]}) translate(${-cx} ${-cy})` : void 0;
|
|
3696
|
+
let g = `<g${attrs({ transform: transform() })}><use${attrs({ href: `#${symbolId}`, width: Math.round(e.width), height: Math.round(e.height), opacity, transform: flip })}/></g>`;
|
|
3697
|
+
if (e.roundness) {
|
|
3698
|
+
const r = cornerRadius(Math.min(e.width, e.height), e);
|
|
3699
|
+
const id2 = `image-clip-${esc(e.id)}`;
|
|
3700
|
+
defs.push(`<clipPath id="${id2}"><rect width="${e.width}" height="${e.height}" rx="${r}" ry="${r}"/></clipPath>`);
|
|
3701
|
+
g = g.replace(/^<g/, `<g clip-path="url(#${id2})"`);
|
|
3702
|
+
}
|
|
3703
|
+
body.push(clip(e, g));
|
|
3704
|
+
return;
|
|
3705
|
+
}
|
|
3706
|
+
case "frame":
|
|
3707
|
+
case "magicframe":
|
|
3708
|
+
body.push(`<rect${attrs({ transform: transform(), width: `${e.width}px`, height: `${e.height}px`, rx: 8, ry: 8, fill: "none", stroke: "#bbb", "stroke-width": 2 })}/>`);
|
|
3709
|
+
return;
|
|
3710
|
+
case "text": {
|
|
3711
|
+
body.push(clip(e, textSvg(e, transform(), opacity)));
|
|
3712
|
+
return;
|
|
3713
|
+
}
|
|
3714
|
+
default: {
|
|
3715
|
+
if (e.type === "embeddable" || e.type === "iframe") {
|
|
3716
|
+
body.push(`<rect${attrs({ transform: transform(), width: e.width, height: e.height, fill: "#d3d3d3", stroke: "#bbb" })}/>`);
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
};
|
|
3721
|
+
for (const e of elements) {
|
|
3722
|
+
if (e.type === "text" && e.containerId && byId.has(e.containerId)) continue;
|
|
3723
|
+
render(e);
|
|
3724
|
+
const label2 = e.boundElements?.find((b2) => b2.type === "text");
|
|
3725
|
+
const t = label2 && byId.get(label2.id);
|
|
3726
|
+
if (t) render(t);
|
|
3727
|
+
}
|
|
3728
|
+
const style = opts.fontCss ? `<style class="style-fonts">${opts.fontCss}</style>` : "";
|
|
3729
|
+
const bg = opts.background !== false ? `<rect x="0" y="0" width="${r2(width)}" height="${r2(height)}" fill="${esc(bgColor)}"/>` : "";
|
|
3730
|
+
const svg = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${r2(width)} ${r2(height)}" width="${r2(width * scale)}" height="${r2(height * scale)}"><!-- svg-source:vizo --><defs>${defs.join("")}${style}</defs>${bg}${body.join("")}</svg>`;
|
|
3731
|
+
return { svg, width, height };
|
|
3732
|
+
}
|
|
3733
|
+
|
|
3734
|
+
// src/ttf.ts
|
|
3735
|
+
import { readFileSync } from "node:fs";
|
|
3736
|
+
function loadFace(path) {
|
|
3737
|
+
const b2 = readFileSync(path);
|
|
3738
|
+
const tables = /* @__PURE__ */ new Map();
|
|
3739
|
+
for (let i = 0; i < b2.readUInt16BE(4); i++) tables.set(b2.toString("latin1", 12 + i * 16, 16 + i * 16), b2.readUInt32BE(12 + i * 16 + 8));
|
|
3740
|
+
const head = tables.get("head");
|
|
3741
|
+
const unitsPerEm = b2.readUInt16BE(head + 18);
|
|
3742
|
+
const numberOfHMetrics = b2.readUInt16BE(tables.get("hhea") + 34);
|
|
3743
|
+
const hmtx = tables.get("hmtx");
|
|
3744
|
+
const glyphOf = cmap(b2, tables.get("cmap"));
|
|
3745
|
+
const width = (g) => b2.readUInt16BE(hmtx + Math.min(g, numberOfHMetrics - 1) * 4);
|
|
3746
|
+
return {
|
|
3747
|
+
has: (cp) => glyphOf(cp) > 0,
|
|
3748
|
+
advance: (cp) => width(glyphOf(cp)) / unitsPerEm
|
|
3749
|
+
};
|
|
3750
|
+
}
|
|
3751
|
+
function cmap(b2, at) {
|
|
3752
|
+
let f4 = -1;
|
|
3753
|
+
let f12 = -1;
|
|
3754
|
+
for (let i = 0; i < b2.readUInt16BE(at + 2); i++) {
|
|
3755
|
+
const r = at + 4 + i * 8;
|
|
3756
|
+
const [platform, encoding] = [b2.readUInt16BE(r), b2.readUInt16BE(r + 2)];
|
|
3757
|
+
if (!(platform === 0 || platform === 3 && (encoding === 1 || encoding === 10))) continue;
|
|
3758
|
+
const sub = at + b2.readUInt32BE(r + 4);
|
|
3759
|
+
const format = b2.readUInt16BE(sub);
|
|
3760
|
+
if (format === 12) f12 = sub;
|
|
3761
|
+
else if (format === 4) f4 = sub;
|
|
3762
|
+
}
|
|
3763
|
+
if (f12 >= 0) {
|
|
3764
|
+
const n = b2.readUInt32BE(f12 + 12);
|
|
3765
|
+
return (cp) => {
|
|
3766
|
+
let [lo, hi] = [0, n - 1];
|
|
3767
|
+
while (lo <= hi) {
|
|
3768
|
+
const mid = lo + hi >> 1;
|
|
3769
|
+
const g = f12 + 16 + mid * 12;
|
|
3770
|
+
const [start, end] = [b2.readUInt32BE(g), b2.readUInt32BE(g + 4)];
|
|
3771
|
+
if (cp < start) hi = mid - 1;
|
|
3772
|
+
else if (cp > end) lo = mid + 1;
|
|
3773
|
+
else return b2.readUInt32BE(g + 8) + cp - start;
|
|
3774
|
+
}
|
|
3775
|
+
return 0;
|
|
3776
|
+
};
|
|
3777
|
+
}
|
|
3778
|
+
if (f4 < 0) return () => 0;
|
|
3779
|
+
const segX2 = b2.readUInt16BE(f4 + 6);
|
|
3780
|
+
const ends = f4 + 14;
|
|
3781
|
+
const starts = ends + segX2 + 2;
|
|
3782
|
+
const deltas = starts + segX2;
|
|
3783
|
+
const offsets = deltas + segX2;
|
|
3784
|
+
return (cp) => {
|
|
3785
|
+
if (cp > 65535) return 0;
|
|
3786
|
+
for (let i = 0; i < segX2; i += 2) {
|
|
3787
|
+
if (cp > b2.readUInt16BE(ends + i)) continue;
|
|
3788
|
+
const start = b2.readUInt16BE(starts + i);
|
|
3789
|
+
if (cp < start) return 0;
|
|
3790
|
+
const delta = b2.readInt16BE(deltas + i);
|
|
3791
|
+
const ro = b2.readUInt16BE(offsets + i);
|
|
3792
|
+
if (!ro) return cp + delta & 65535;
|
|
3793
|
+
const g = b2.readUInt16BE(offsets + i + ro + (cp - start) * 2);
|
|
3794
|
+
return g ? g + delta & 65535 : 0;
|
|
3795
|
+
}
|
|
3796
|
+
return 0;
|
|
3797
|
+
};
|
|
3798
|
+
}
|
|
3799
|
+
|
|
3800
|
+
// src/export.ts
|
|
3801
|
+
var EXPORT_TOOL = {
|
|
3802
|
+
name: "export_image",
|
|
3803
|
+
title: "Export image",
|
|
3804
|
+
description: "Save a board, or some of its diagrams/elements, as a PNG or SVG file on this machine, drawn the way the board shows it (hand-drawn strokes, fonts, images). Returns the file path, and by default a small preview image so you can check the result.",
|
|
3805
|
+
inputSchema: {
|
|
3806
|
+
type: "object",
|
|
3807
|
+
properties: {
|
|
3808
|
+
boardId: { type: "string", description: "Board id (e.g. 'b_xxx') or full board URL" },
|
|
3809
|
+
format: { type: "string", enum: ["png", "svg"], description: "Default png" },
|
|
3810
|
+
diagramIds: { type: "array", items: { type: "string" }, description: "Only these diagrams (ids from get_board), with their frame and title" },
|
|
3811
|
+
elementIds: { type: "array", items: { type: "string" }, description: "Only these elements; a frame brings its contents, a shape its label" },
|
|
3812
|
+
scale: { type: "number", minimum: 0.1, maximum: 8, description: "PNG pixels per board unit (default 2 = sharp on high-DPI screens)" },
|
|
3813
|
+
background: { type: "boolean", description: "Fill with the board background (default true); false gives a transparent PNG/SVG" },
|
|
3814
|
+
path: { type: "string", description: "Output file. Default: <board title>.<format> in the current directory (never overwrites; adds -2, -3\u2026)" },
|
|
3815
|
+
preview: { type: "boolean", description: "Also return a small PNG preview to look at (default true)" }
|
|
3816
|
+
},
|
|
3817
|
+
required: ["boardId"],
|
|
3818
|
+
additionalProperties: false
|
|
3819
|
+
},
|
|
3820
|
+
annotations: { readOnlyHint: false, destructiveHint: false }
|
|
3821
|
+
};
|
|
3822
|
+
var MAX_SIDE = 16384;
|
|
3823
|
+
var MAX_AREA = 16384 * 16384;
|
|
3824
|
+
var PREVIEW_MAX_SIDE = 1200;
|
|
3825
|
+
var ExportError = class extends Error {
|
|
3826
|
+
};
|
|
3827
|
+
function pickElements(scene, args) {
|
|
3828
|
+
const all = scene.elements.filter((e) => !e.isDeleted);
|
|
3829
|
+
if (!args.diagramIds?.length && !args.elementIds?.length) return all;
|
|
3830
|
+
const byId = new Map(all.map((e) => [e.id, e]));
|
|
3831
|
+
const want = /* @__PURE__ */ new Set();
|
|
3832
|
+
const unknown = [];
|
|
3833
|
+
for (const d of args.diagramIds ?? []) {
|
|
3834
|
+
const diagram = scene.diagrams.find((x) => x.id === d);
|
|
3835
|
+
if (diagram && byId.has(diagram.frameElementId)) want.add(diagram.frameElementId);
|
|
3836
|
+
else unknown.push(`diagram '${d}'`);
|
|
3837
|
+
}
|
|
3838
|
+
for (const id2 of args.elementIds ?? []) {
|
|
3839
|
+
if (byId.has(id2)) want.add(id2);
|
|
3840
|
+
else unknown.push(`element '${id2}'`);
|
|
3841
|
+
}
|
|
3842
|
+
if (unknown.length) throw new ExportError(`Not on this board: ${unknown.join(", ")}. Use get_board for ids.`);
|
|
3843
|
+
for (const e of all) if (e.frameId && want.has(e.frameId)) want.add(e.id);
|
|
3844
|
+
for (const e of all) if (e.type === "text" && e.containerId && want.has(e.containerId)) want.add(e.id);
|
|
3845
|
+
return all.filter((e) => want.has(e.id));
|
|
3846
|
+
}
|
|
3847
|
+
var FONT_DIR = new URL("./fonts/", import.meta.url);
|
|
3848
|
+
var manifest = null;
|
|
3849
|
+
var fonts = () => manifest ??= JSON.parse(readFileSync2(new URL("fonts.json", FONT_DIR), "utf8"));
|
|
3850
|
+
function inRange(range, cp) {
|
|
3851
|
+
if (!range) return true;
|
|
3852
|
+
return range.split(",").some((r) => {
|
|
3853
|
+
const [a2, b2] = r.trim().replace(/^U\+/i, "").split("-");
|
|
3854
|
+
const lo = parseInt(a2, 16);
|
|
3855
|
+
return cp >= lo && cp <= (b2 ? parseInt(b2, 16) : lo);
|
|
3856
|
+
});
|
|
3857
|
+
}
|
|
3858
|
+
function fontCss(elements) {
|
|
3859
|
+
const used = fontsUsed(elements);
|
|
3860
|
+
const chars = /* @__PURE__ */ new Map();
|
|
3861
|
+
for (const e of elements) {
|
|
3862
|
+
const id2 = e.type === "text" ? e.fontFamily ?? 5 : e.type === "frame" || e.type === "magicframe" ? 2 : null;
|
|
3863
|
+
if (id2 === null) continue;
|
|
3864
|
+
const set = chars.get(id2) ?? /* @__PURE__ */ new Set();
|
|
3865
|
+
for (const ch of String(e.type === "text" ? e.text : e.name ?? "Frame")) set.add(ch.codePointAt(0));
|
|
3866
|
+
chars.set(id2, set);
|
|
3867
|
+
}
|
|
3868
|
+
return fonts().filter((f) => used.has(f.id) && [...chars.get(f.id) ?? []].some((cp) => inRange(f.unicodeRange, cp))).map((f) => {
|
|
3869
|
+
const data = readFileSync2(new URL(f.file, FONT_DIR)).toString("base64");
|
|
3870
|
+
return `@font-face { font-family: "${f.family}"; src: url(data:font/woff2;base64,${data}); ${f.unicodeRange ? `unicode-range: ${f.unicodeRange}; ` : ""}}`;
|
|
3871
|
+
}).join("\n");
|
|
3872
|
+
}
|
|
3873
|
+
var unescapeXml = (s) => s.replace(/&(lt|gt|quot|amp);/g, (_, e) => ({ lt: "<", gt: ">", quot: '"', amp: "&" })[e]);
|
|
3874
|
+
var escapeXml = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3875
|
+
var BORROW_ORDER = ["Virgil", "Excalifont", "Comic Shanns", "Liberation Sans", "Nunito", "Cascadia", "Lilita One"];
|
|
3876
|
+
var ttfPath = (f) => fileURLToPath(new URL(f.file.replace(/\.woff2$/, ".ttf"), FONT_DIR));
|
|
3877
|
+
var faces = /* @__PURE__ */ new Map();
|
|
3878
|
+
var face = (f) => faces.get(f.file) ?? faces.set(f.file, loadFace(ttfPath(f))).get(f.file);
|
|
3879
|
+
function fontFor(family, cp) {
|
|
3880
|
+
const own = fonts().filter((f) => f.family === family);
|
|
3881
|
+
const others = fonts().filter((f) => f.family !== family).sort((a2, b2) => BORROW_ORDER.indexOf(a2.family) - BORROW_ORDER.indexOf(b2.family));
|
|
3882
|
+
return [...own, ...others].find((f) => face(f).has(cp)) ?? null;
|
|
3883
|
+
}
|
|
3884
|
+
function guessAdvance(cp) {
|
|
3885
|
+
if (cp === 8205 || cp >= 65024 && cp <= 65039) return 0;
|
|
3886
|
+
const wide = cp >= 4352 && cp <= 4447 || cp >= 11904 && cp <= 42191 || cp >= 44032 && cp <= 55203 || cp >= 63744 && cp <= 64255 || cp >= 65280 && cp <= 65376 || cp >= 126976;
|
|
3887
|
+
return wide ? 1 : 0.8;
|
|
3888
|
+
}
|
|
3889
|
+
var setAttr = (attrs2, name, value) => new RegExp(` ${name}="[^"]*"`).test(attrs2) ? attrs2.replace(new RegExp(` ${name}="[^"]*"`), ` ${name}="${value}"`) : `${attrs2} ${name}="${value}"`;
|
|
3890
|
+
var EMOJI = new RegExp("\\p{Extended_Pictographic}", "u");
|
|
3891
|
+
function useTtfFamilies(svg) {
|
|
3892
|
+
return svg.replace(/<text([^>]*?) font-family="([^"]*)"([^>]*)>([^<]*)<\/text>/g, (_, pre, list, post, body) => {
|
|
3893
|
+
let family = list.split(",")[0].trim();
|
|
3894
|
+
if (family === "Helvetica") family = "Liberation Sans";
|
|
3895
|
+
const attrs2 = `${pre}${post} xml:space="preserve"`;
|
|
3896
|
+
const runs = [];
|
|
3897
|
+
for (const ch of unescapeXml(body)) {
|
|
3898
|
+
const cp = ch.codePointAt(0);
|
|
3899
|
+
const last = runs.at(-1);
|
|
3900
|
+
const joiner = cp === 8205 || cp >= 65024 && cp <= 65039 || cp >= 127995 && cp <= 127999;
|
|
3901
|
+
if (last && (ch === " " || joiner)) {
|
|
3902
|
+
last.text += ch;
|
|
3903
|
+
continue;
|
|
3904
|
+
}
|
|
3905
|
+
const emoji = EMOJI.test(ch) && cp > 255;
|
|
3906
|
+
const font = emoji ? null : fontFor(family, cp);
|
|
3907
|
+
if (last && last.font === font && last.emoji === emoji) last.text += ch;
|
|
3908
|
+
else runs.push({ font, emoji, text: ch });
|
|
3909
|
+
}
|
|
3910
|
+
const fontName = (r) => r.font?.ttfFamily ?? family;
|
|
3911
|
+
if (runs.length === 1 && !runs[0].emoji) return `<text${attrs2} font-family="${fontName(runs[0])}">${body}</text>`;
|
|
3912
|
+
if (!runs.length || / direction="rtl"/.test(attrs2)) return `<text${attrs2} font-family="${family}">${body}</text>`;
|
|
3913
|
+
const size = parseFloat(/font-size="([\d.]+)/.exec(attrs2)?.[1] ?? "20");
|
|
3914
|
+
const x = parseFloat(/ x="([-\d.]+)"/.exec(attrs2)?.[1] ?? "0");
|
|
3915
|
+
const anchor = / text-anchor="(\w+)"/.exec(attrs2)?.[1] ?? "start";
|
|
3916
|
+
const widths = runs.map((r) => [...r.text].reduce((w, ch) => w + (r.font ? face(r.font).advance(ch.codePointAt(0)) : guessAdvance(ch.codePointAt(0))), 0) * size);
|
|
3917
|
+
const total = widths.reduce((a2, b2) => a2 + b2, 0);
|
|
3918
|
+
let at = anchor === "middle" ? x - total / 2 : anchor === "end" ? x - total : x;
|
|
3919
|
+
return runs.map((r, i) => {
|
|
3920
|
+
const a2 = setAttr(setAttr(attrs2, "x", Math.round(at * 100) / 100), "text-anchor", "start");
|
|
3921
|
+
at += widths[i];
|
|
3922
|
+
return r.emoji ? "" : `<text${a2} font-family="${fontName(r)}">${escapeXml(r.text)}</text>`;
|
|
3923
|
+
}).join("");
|
|
3924
|
+
});
|
|
3925
|
+
}
|
|
3926
|
+
async function rasterize(svg, width) {
|
|
3927
|
+
const { Resvg } = await import("@resvg/resvg-js");
|
|
3928
|
+
svg = useTtfFamilies(svg);
|
|
3929
|
+
const fontFiles = fonts().map(ttfPath);
|
|
3930
|
+
const png = new Resvg(svg, {
|
|
3931
|
+
fitTo: { mode: "width", value: Math.max(1, Math.round(width)) },
|
|
3932
|
+
// System fonts cover what Excalidraw's own fonts don't (CJK, emoji); Helvetica falls back to Liberation Sans.
|
|
3933
|
+
font: { fontFiles, loadSystemFonts: true, defaultFontFamily: "Liberation Sans", sansSerifFamily: "Liberation Sans" },
|
|
3934
|
+
imageRendering: 0,
|
|
3935
|
+
shapeRendering: 2,
|
|
3936
|
+
textRendering: 1
|
|
3937
|
+
}).render();
|
|
3938
|
+
return { data: png.asPng(), width: png.width, height: png.height };
|
|
3939
|
+
}
|
|
3940
|
+
function outputPath(requested, title, format) {
|
|
3941
|
+
if (requested) {
|
|
3942
|
+
const p2 = resolve(requested);
|
|
3943
|
+
return extname(p2).toLowerCase() === `.${format}` ? p2 : `${p2}.${format}`;
|
|
3944
|
+
}
|
|
3945
|
+
const base2 = (title || "board").replace(/[\\/:*?"<>|\x00-\x1f]+/g, "-").trim() || "board";
|
|
3946
|
+
let p = resolve(`${base2}.${format}`);
|
|
3947
|
+
for (let n = 2; existsSync(p); n++) p = resolve(`${base2}-${n}.${format}`);
|
|
3948
|
+
return p;
|
|
3949
|
+
}
|
|
3950
|
+
async function exportImage(scene, args) {
|
|
3951
|
+
const format = args.format ?? "png";
|
|
3952
|
+
const elements = pickElements(scene, args);
|
|
3953
|
+
if (!elements.length) throw new ExportError("Nothing to export: the board is empty.");
|
|
3954
|
+
const files = Object.fromEntries(scene.files.map((f) => [f.id, f]));
|
|
3955
|
+
const background = args.background !== false;
|
|
3956
|
+
const scale = args.scale ?? 2;
|
|
3957
|
+
const svgOpts = { background, backgroundColor: "#ffffff", fontCss: fontCss(elements) };
|
|
3958
|
+
const { svg, width, height } = exportSvg(elements, files, { ...svgOpts, scale: format === "svg" ? 1 : scale });
|
|
3959
|
+
const path = outputPath(args.path, scene.title ?? "", format);
|
|
3960
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
3961
|
+
let size;
|
|
3962
|
+
let bytes;
|
|
3963
|
+
if (format === "svg") {
|
|
3964
|
+
writeFileSync(path, svg);
|
|
3965
|
+
size = { width: Math.round(width), height: Math.round(height) };
|
|
3966
|
+
bytes = Buffer.byteLength(svg);
|
|
3967
|
+
} else {
|
|
3968
|
+
const w = Math.round(width * scale);
|
|
3969
|
+
const h = Math.round(height * scale);
|
|
3970
|
+
if (w > MAX_SIDE || h > MAX_SIDE || w * h > MAX_AREA)
|
|
3971
|
+
throw new ExportError(`${w}\xD7${h} px is too large (max ${MAX_SIDE} px per side). Use a smaller scale, or export fewer diagrams.`);
|
|
3972
|
+
const png = await rasterize(svg, w);
|
|
3973
|
+
writeFileSync(path, png.data);
|
|
3974
|
+
size = { width: png.width, height: png.height };
|
|
3975
|
+
bytes = png.data.length;
|
|
3976
|
+
}
|
|
3977
|
+
const content = [
|
|
3978
|
+
{ type: "text", text: JSON.stringify({ path, format, ...size, bytes, elements: elements.length }, null, 1) }
|
|
3979
|
+
];
|
|
3980
|
+
if (args.preview !== false) {
|
|
3981
|
+
const fit = Math.min(1, PREVIEW_MAX_SIDE / Math.max(width, height));
|
|
3982
|
+
const preview = await rasterize(svg, width * fit);
|
|
3983
|
+
content.push({ type: "image", data: Buffer.from(preview.data).toString("base64"), mimeType: "image/png" });
|
|
3984
|
+
}
|
|
3985
|
+
return { content };
|
|
3986
|
+
}
|
|
3987
|
+
|
|
1310
3988
|
// src/index.ts
|
|
1311
3989
|
var PROXY_HEADER = "X-Vizo-Proxy";
|
|
1312
3990
|
var PUT_TOOL = "put_diagram_elements";
|
|
3991
|
+
var SCENE_TOOL = "get_board_scene";
|
|
3992
|
+
var HIDDEN = /* @__PURE__ */ new Set([PUT_TOOL, SCENE_TOOL]);
|
|
1313
3993
|
var LOCAL_TOOLS = /* @__PURE__ */ new Set(["draw_diagram", "update_diagram"]);
|
|
1314
3994
|
var toolError = (text2) => ({ content: [{ type: "text", text: text2 }], isError: true });
|
|
1315
3995
|
function arg(name) {
|
|
1316
3996
|
const i = process.argv.indexOf(`--${name}`);
|
|
1317
3997
|
if (i >= 0) return process.argv[i + 1];
|
|
1318
|
-
const eq = process.argv.find((
|
|
3998
|
+
const eq = process.argv.find((a2) => a2.startsWith(`--${name}=`));
|
|
1319
3999
|
return eq?.slice(name.length + 3);
|
|
1320
4000
|
}
|
|
1321
|
-
var log = (...
|
|
4001
|
+
var log = (...a2) => console.error("[vizo-mcp]", ...a2);
|
|
1322
4002
|
async function main() {
|
|
1323
4003
|
const base2 = (arg("url") ?? process.env.VIZO_URL ?? "").replace(/\/$/, "");
|
|
1324
4004
|
const token = arg("token") ?? process.env.VIZO_TOKEN;
|
|
@@ -1328,32 +4008,38 @@ async function main() {
|
|
|
1328
4008
|
process.exit(2);
|
|
1329
4009
|
}
|
|
1330
4010
|
const endpoint = new URL(base2.endsWith("/mcp") ? base2 : `${base2}/mcp`);
|
|
1331
|
-
const upstream = new Client({ name: "vizo-mcp-proxy", version: "0.
|
|
4011
|
+
const upstream = new Client({ name: "vizo-mcp-proxy", version: "0.2.0" });
|
|
1332
4012
|
await upstream.connect(
|
|
1333
4013
|
new StreamableHTTPClientTransport(endpoint, { requestInit: { headers: { Authorization: `Bearer ${token}`, [PROXY_HEADER]: String(LAYOUT_VERSION) } } })
|
|
1334
4014
|
);
|
|
1335
4015
|
const caps = upstream.getServerCapabilities() ?? {};
|
|
1336
4016
|
log(`connected to ${endpoint.href} (local layout v${LAYOUT_VERSION})`);
|
|
1337
4017
|
const server = new Server(
|
|
1338
|
-
{ name: "vizo", version: "0.
|
|
4018
|
+
{ name: "vizo", version: "0.2.0" },
|
|
1339
4019
|
{
|
|
1340
4020
|
capabilities: {
|
|
1341
4021
|
tools: caps.tools ? {} : void 0,
|
|
1342
4022
|
resources: caps.resources ? {} : void 0,
|
|
1343
4023
|
prompts: caps.prompts ? {} : void 0
|
|
1344
4024
|
},
|
|
1345
|
-
instructions: upstream.getInstructions()
|
|
4025
|
+
instructions: `${upstream.getInstructions() ?? ""}
|
|
4026
|
+
|
|
4027
|
+
This local proxy also has export_image: save a board or some of its diagrams as PNG/SVG on this machine (e.g. for docs or a PR), and look at the returned preview to check your drawing.`.trim()
|
|
1346
4028
|
}
|
|
1347
4029
|
);
|
|
1348
4030
|
if (caps.tools) {
|
|
1349
4031
|
let serverSupportsLocal;
|
|
4032
|
+
let serverSupportsExport = false;
|
|
1350
4033
|
server.setRequestHandler(ListToolsRequestSchema, async (req) => {
|
|
1351
4034
|
const r = await upstream.listTools(req.params);
|
|
1352
4035
|
if (r.tools.some((t) => t.name === PUT_TOOL)) serverSupportsLocal = true;
|
|
1353
|
-
|
|
4036
|
+
serverSupportsExport = r.tools.some((t) => t.name === SCENE_TOOL);
|
|
4037
|
+
const tools = r.tools.filter((t) => !HIDDEN.has(t.name));
|
|
4038
|
+
return { ...r, tools: serverSupportsExport && !r.nextCursor ? [...tools, EXPORT_TOOL] : tools };
|
|
1354
4039
|
});
|
|
1355
4040
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
1356
4041
|
const { name, arguments: args = {} } = req.params;
|
|
4042
|
+
if (name === EXPORT_TOOL.name) return exportLocally(args);
|
|
1357
4043
|
if (!LOCAL_TOOLS.has(name)) return upstream.callTool(req.params);
|
|
1358
4044
|
serverSupportsLocal ??= (await upstream.listTools()).tools.some((t) => t.name === PUT_TOOL);
|
|
1359
4045
|
if (!serverSupportsLocal) {
|
|
@@ -1398,6 +4084,19 @@ async function main() {
|
|
|
1398
4084
|
}
|
|
1399
4085
|
});
|
|
1400
4086
|
}
|
|
4087
|
+
async function exportLocally(args) {
|
|
4088
|
+
if (!args?.boardId) return toolError("boardId is required");
|
|
4089
|
+
const r = await upstream.callTool({ name: SCENE_TOOL, arguments: { boardId: args.boardId } });
|
|
4090
|
+
const body = r.content?.find((c) => c.type === "text")?.text ?? "";
|
|
4091
|
+
if (r.isError) return toolError(body || "Couldn't read the board.");
|
|
4092
|
+
try {
|
|
4093
|
+
return await exportImage(JSON.parse(body), args);
|
|
4094
|
+
} catch (e) {
|
|
4095
|
+
if (e instanceof ExportError) return toolError(e.message);
|
|
4096
|
+
log("export failed:", e);
|
|
4097
|
+
return toolError(`Export failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
1401
4100
|
await server.connect(new StdioServerTransport());
|
|
1402
4101
|
const shutdown = async () => {
|
|
1403
4102
|
await Promise.allSettled([server.close(), upstream.close()]);
|