sketchmark 1.1.6 → 1.2.1
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 +200 -152
- package/dist/animation/index.d.ts +2 -0
- package/dist/animation/index.d.ts.map +1 -1
- package/dist/ast/types.d.ts +3 -0
- package/dist/ast/types.d.ts.map +1 -1
- package/dist/index.cjs +711 -105
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +711 -105
- package/dist/index.js.map +1 -1
- package/dist/layout/index.d.ts +8 -0
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/parser/index.d.ts +2 -1
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/tokenizer.d.ts.map +1 -1
- package/dist/plugins.d.ts +12 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/render.d.ts +2 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/renderer/shapes/path-geometry.d.ts +8 -0
- package/dist/renderer/shapes/path-geometry.d.ts.map +1 -0
- package/dist/renderer/shapes/path.d.ts.map +1 -1
- package/dist/renderer/shared.d.ts +1 -1
- package/dist/renderer/shared.d.ts.map +1 -1
- package/dist/renderer/svg/index.d.ts.map +1 -1
- package/dist/scene/index.d.ts +2 -0
- package/dist/scene/index.d.ts.map +1 -1
- package/dist/sketchmark.iife.js +711 -105
- package/dist/ui/canvas.d.ts +2 -0
- package/dist/ui/canvas.d.ts.map +1 -1
- package/dist/ui/embed.d.ts +2 -0
- package/dist/ui/embed.d.ts.map +1 -1
- package/package.json +20 -1
package/dist/sketchmark.iife.js
CHANGED
|
@@ -147,10 +147,16 @@ var AIDiagram = (function (exports) {
|
|
|
147
147
|
val += "\n";
|
|
148
148
|
else if (esc === "t")
|
|
149
149
|
val += "\t";
|
|
150
|
+
else if (esc === "r")
|
|
151
|
+
val += "\r";
|
|
150
152
|
else if (esc === "\\")
|
|
151
153
|
val += "\\";
|
|
154
|
+
else if (esc === q)
|
|
155
|
+
val += q;
|
|
156
|
+
else if (esc)
|
|
157
|
+
val += `\\${esc}`;
|
|
152
158
|
else
|
|
153
|
-
val +=
|
|
159
|
+
val += "\\";
|
|
154
160
|
}
|
|
155
161
|
else
|
|
156
162
|
val += src[i];
|
|
@@ -227,6 +233,47 @@ var AIDiagram = (function (exports) {
|
|
|
227
233
|
return tokens;
|
|
228
234
|
}
|
|
229
235
|
|
|
236
|
+
function pluginMessage(plugin, stage, error) {
|
|
237
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
238
|
+
return `Plugin "${plugin.name}" ${stage} failed: ${detail}`;
|
|
239
|
+
}
|
|
240
|
+
function applyPluginPreprocessors(source, plugins = []) {
|
|
241
|
+
let nextSource = source;
|
|
242
|
+
for (const plugin of plugins) {
|
|
243
|
+
if (!plugin.preprocess)
|
|
244
|
+
continue;
|
|
245
|
+
try {
|
|
246
|
+
const transformed = plugin.preprocess(nextSource);
|
|
247
|
+
if (typeof transformed !== "string") {
|
|
248
|
+
throw new Error("preprocess must return a string");
|
|
249
|
+
}
|
|
250
|
+
nextSource = transformed;
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
throw new Error(pluginMessage(plugin, "preprocess", error));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return nextSource;
|
|
257
|
+
}
|
|
258
|
+
function applyPluginAstTransforms(ast, plugins = []) {
|
|
259
|
+
let nextAst = ast;
|
|
260
|
+
for (const plugin of plugins) {
|
|
261
|
+
if (!plugin.transformAst)
|
|
262
|
+
continue;
|
|
263
|
+
try {
|
|
264
|
+
const transformed = plugin.transformAst(nextAst);
|
|
265
|
+
if (!transformed || transformed.kind !== "diagram") {
|
|
266
|
+
throw new Error('transformAst must return a DiagramAST with kind="diagram"');
|
|
267
|
+
}
|
|
268
|
+
nextAst = transformed;
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
throw new Error(pluginMessage(plugin, "transformAst", error));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return nextAst;
|
|
275
|
+
}
|
|
276
|
+
|
|
230
277
|
// ============================================================
|
|
231
278
|
// sketchmark - Parser (Tokens -> DiagramAST)
|
|
232
279
|
// ============================================================
|
|
@@ -313,9 +360,10 @@ var AIDiagram = (function (exports) {
|
|
|
313
360
|
function isPropKeyToken(t) {
|
|
314
361
|
return !!t && (t.type === "IDENT" || t.type === "KEYWORD");
|
|
315
362
|
}
|
|
316
|
-
function parse(src) {
|
|
363
|
+
function parse(src, options = {}) {
|
|
317
364
|
resetUid();
|
|
318
|
-
const
|
|
365
|
+
const preparedSource = applyPluginPreprocessors(src, options.plugins);
|
|
366
|
+
const tokens = tokenize$1(preparedSource).filter((t) => t.type !== "NEWLINE" || t.value === "\n");
|
|
319
367
|
const flat = [];
|
|
320
368
|
let lastNL = false;
|
|
321
369
|
for (const t of tokens) {
|
|
@@ -498,6 +546,7 @@ var AIDiagram = (function (exports) {
|
|
|
498
546
|
const toks = lineTokens();
|
|
499
547
|
const id = requireExplicitId(keywordTok, toks);
|
|
500
548
|
const props = parseSimpleProps(toks, 1);
|
|
549
|
+
const meta = extractNodeMeta(props);
|
|
501
550
|
const node = {
|
|
502
551
|
kind: "node",
|
|
503
552
|
id,
|
|
@@ -512,6 +561,7 @@ var AIDiagram = (function (exports) {
|
|
|
512
561
|
...(props.dy ? { dy: parseFloat(props.dy) } : {}),
|
|
513
562
|
...(props.factor ? { factor: parseFloat(props.factor) } : {}),
|
|
514
563
|
...(props.theme ? { theme: props.theme } : {}),
|
|
564
|
+
...(meta ? { meta } : {}),
|
|
515
565
|
style: propsToStyle(props),
|
|
516
566
|
};
|
|
517
567
|
if (props.url)
|
|
@@ -536,12 +586,14 @@ var AIDiagram = (function (exports) {
|
|
|
536
586
|
j = 2;
|
|
537
587
|
}
|
|
538
588
|
Object.assign(props, parseSimpleProps(toks, j));
|
|
589
|
+
const meta = extractNodeMeta(props);
|
|
539
590
|
return {
|
|
540
591
|
kind: "node",
|
|
541
592
|
id,
|
|
542
593
|
shape: "note",
|
|
543
594
|
label: (props.label ?? "").replace(/\\n/g, "\n"),
|
|
544
595
|
theme: props.theme,
|
|
596
|
+
...(meta ? { meta } : {}),
|
|
545
597
|
style: propsToStyle(props),
|
|
546
598
|
...(props.width ? { width: parseFloat(props.width) } : {}),
|
|
547
599
|
...(props.height ? { height: parseFloat(props.height) } : {}),
|
|
@@ -553,6 +605,13 @@ var AIDiagram = (function (exports) {
|
|
|
553
605
|
...(props.factor ? { factor: parseFloat(props.factor) } : {}),
|
|
554
606
|
};
|
|
555
607
|
}
|
|
608
|
+
function extractNodeMeta(props) {
|
|
609
|
+
const meta = {};
|
|
610
|
+
if (props["animation-parent"]) {
|
|
611
|
+
meta.animationParent = props["animation-parent"];
|
|
612
|
+
}
|
|
613
|
+
return Object.keys(meta).length ? meta : undefined;
|
|
614
|
+
}
|
|
556
615
|
function parseGroup() {
|
|
557
616
|
const keywordTok = cur();
|
|
558
617
|
skip();
|
|
@@ -625,6 +684,8 @@ var AIDiagram = (function (exports) {
|
|
|
625
684
|
to: toTok.value,
|
|
626
685
|
connector: connector,
|
|
627
686
|
label: props.label,
|
|
687
|
+
fromAnchor: props["anchor-from"],
|
|
688
|
+
toAnchor: props["anchor-to"],
|
|
628
689
|
dashed,
|
|
629
690
|
bidirectional,
|
|
630
691
|
style: propsToStyle(props),
|
|
@@ -938,6 +999,7 @@ var AIDiagram = (function (exports) {
|
|
|
938
999
|
registerAuthoredId(grp.id, "group", t);
|
|
939
1000
|
if (isBare) {
|
|
940
1001
|
grp.label = "";
|
|
1002
|
+
grp.padding = grp.padding ?? 0;
|
|
941
1003
|
grp.style = {
|
|
942
1004
|
...grp.style,
|
|
943
1005
|
fill: grp.style?.fill ?? "none",
|
|
@@ -1108,7 +1170,7 @@ var AIDiagram = (function (exports) {
|
|
|
1108
1170
|
node.style = { ...ast.styles[node.id], ...node.style };
|
|
1109
1171
|
}
|
|
1110
1172
|
}
|
|
1111
|
-
return ast;
|
|
1173
|
+
return applyPluginAstTransforms(ast, options.plugins);
|
|
1112
1174
|
}
|
|
1113
1175
|
|
|
1114
1176
|
// ============================================================
|
|
@@ -3555,6 +3617,8 @@ var AIDiagram = (function (exports) {
|
|
|
3555
3617
|
to: e.to,
|
|
3556
3618
|
connector: e.connector,
|
|
3557
3619
|
label: e.label,
|
|
3620
|
+
fromAnchor: e.fromAnchor,
|
|
3621
|
+
toAnchor: e.toAnchor,
|
|
3558
3622
|
dashed: e.dashed ?? false,
|
|
3559
3623
|
bidirectional: e.bidirectional ?? false,
|
|
3560
3624
|
style: e.style ?? {},
|
|
@@ -4132,28 +4196,13 @@ var AIDiagram = (function (exports) {
|
|
|
4132
4196
|
return { arrowAt: "start", dashed };
|
|
4133
4197
|
return { arrowAt: "end", dashed };
|
|
4134
4198
|
}
|
|
4135
|
-
// ── Generic rect connection point ────────────────────────────────────────
|
|
4136
|
-
function rectConnPoint$1(rx, ry, rw, rh, ox, oy) {
|
|
4137
|
-
const cx = rx + rw / 2, cy = ry + rh / 2;
|
|
4138
|
-
const dx = ox - cx, dy = oy - cy;
|
|
4139
|
-
if (Math.abs(dx) < 0.01 && Math.abs(dy) < 0.01)
|
|
4140
|
-
return [cx, cy];
|
|
4141
|
-
const hw = rw / 2 - 2, hh = rh / 2 - 2;
|
|
4142
|
-
const tx = Math.abs(dx) > 0.01 ? hw / Math.abs(dx) : 1e9;
|
|
4143
|
-
const ty = Math.abs(dy) > 0.01 ? hh / Math.abs(dy) : 1e9;
|
|
4144
|
-
const t = Math.min(tx, ty);
|
|
4145
|
-
return [cx + t * dx, cy + t * dy];
|
|
4146
|
-
}
|
|
4147
4199
|
// ── Resolve an endpoint entity by ID across all maps ─────────────────────
|
|
4148
4200
|
function resolveEndpoint(id, nm, tm, gm, cm) {
|
|
4149
4201
|
return nm.get(id) ?? tm.get(id) ?? gm.get(id) ?? cm.get(id) ?? null;
|
|
4150
4202
|
}
|
|
4151
4203
|
// ── Get connection point for any entity ──────────────────────────────────
|
|
4152
|
-
function getConnPoint(src, dstCX, dstCY) {
|
|
4153
|
-
|
|
4154
|
-
return connPoint(src, { x: dstCX - 1, y: dstCY - 1, w: 2, h: 2});
|
|
4155
|
-
}
|
|
4156
|
-
return rectConnPoint$1(src.x, src.y, src.w, src.h, dstCX, dstCY);
|
|
4204
|
+
function getConnPoint(src, dstCX, dstCY, anchor) {
|
|
4205
|
+
return anchoredConnPoint(src, anchor, dstCX, dstCY);
|
|
4157
4206
|
}
|
|
4158
4207
|
// ── Group depth (for paint order) ────────────────────────────────────────
|
|
4159
4208
|
function groupDepth(g, gm) {
|
|
@@ -4251,37 +4300,510 @@ var AIDiagram = (function (exports) {
|
|
|
4251
4300
|
},
|
|
4252
4301
|
};
|
|
4253
4302
|
|
|
4303
|
+
const COMMAND_RE = /^[AaCcHhLlMmQqSsTtVvZz]$/;
|
|
4304
|
+
const TOKEN_RE = /[AaCcHhLlMmQqSsTtVvZz]|[-+]?(?:\d*\.\d+|\d+)(?:[eE][-+]?\d+)?/g;
|
|
4305
|
+
const EPSILON = 1e-6;
|
|
4306
|
+
const PARAM_COUNTS = {
|
|
4307
|
+
A: 7,
|
|
4308
|
+
C: 6,
|
|
4309
|
+
H: 1,
|
|
4310
|
+
L: 2,
|
|
4311
|
+
M: 2,
|
|
4312
|
+
Q: 4,
|
|
4313
|
+
S: 4,
|
|
4314
|
+
T: 2,
|
|
4315
|
+
V: 1,
|
|
4316
|
+
Z: 0,
|
|
4317
|
+
};
|
|
4318
|
+
function isCommandToken(token) {
|
|
4319
|
+
return COMMAND_RE.test(token);
|
|
4320
|
+
}
|
|
4321
|
+
function formatNumber(value) {
|
|
4322
|
+
const rounded = Math.abs(value) < EPSILON ? 0 : Number(value.toFixed(3));
|
|
4323
|
+
return Object.is(rounded, -0) ? "0" : String(rounded);
|
|
4324
|
+
}
|
|
4325
|
+
function parseRawSegments(pathData) {
|
|
4326
|
+
const tokens = pathData.match(TOKEN_RE) ?? [];
|
|
4327
|
+
if (!tokens.length)
|
|
4328
|
+
return [];
|
|
4329
|
+
const segments = [];
|
|
4330
|
+
let index = 0;
|
|
4331
|
+
let currentCommand = null;
|
|
4332
|
+
while (index < tokens.length) {
|
|
4333
|
+
const token = tokens[index];
|
|
4334
|
+
if (isCommandToken(token)) {
|
|
4335
|
+
currentCommand = token;
|
|
4336
|
+
index += 1;
|
|
4337
|
+
if (token === "Z" || token === "z") {
|
|
4338
|
+
segments.push({ command: "Z", values: [] });
|
|
4339
|
+
}
|
|
4340
|
+
continue;
|
|
4341
|
+
}
|
|
4342
|
+
if (!currentCommand)
|
|
4343
|
+
break;
|
|
4344
|
+
const upper = currentCommand.toUpperCase();
|
|
4345
|
+
const paramCount = PARAM_COUNTS[upper];
|
|
4346
|
+
if (!paramCount) {
|
|
4347
|
+
index += 1;
|
|
4348
|
+
continue;
|
|
4349
|
+
}
|
|
4350
|
+
let isFirstMove = upper === "M";
|
|
4351
|
+
while (index < tokens.length && !isCommandToken(tokens[index])) {
|
|
4352
|
+
if (index + paramCount > tokens.length)
|
|
4353
|
+
return segments;
|
|
4354
|
+
const values = tokens
|
|
4355
|
+
.slice(index, index + paramCount)
|
|
4356
|
+
.map((value) => Number(value));
|
|
4357
|
+
if (values.some((value) => Number.isNaN(value))) {
|
|
4358
|
+
return segments;
|
|
4359
|
+
}
|
|
4360
|
+
if (upper === "M") {
|
|
4361
|
+
const moveCommand = isFirstMove
|
|
4362
|
+
? currentCommand
|
|
4363
|
+
: currentCommand === "m"
|
|
4364
|
+
? "l"
|
|
4365
|
+
: "L";
|
|
4366
|
+
segments.push({ command: moveCommand, values });
|
|
4367
|
+
isFirstMove = false;
|
|
4368
|
+
}
|
|
4369
|
+
else {
|
|
4370
|
+
segments.push({ command: currentCommand, values });
|
|
4371
|
+
}
|
|
4372
|
+
index += paramCount;
|
|
4373
|
+
}
|
|
4374
|
+
}
|
|
4375
|
+
return segments;
|
|
4376
|
+
}
|
|
4377
|
+
function reflect(control, around) {
|
|
4378
|
+
return {
|
|
4379
|
+
x: around.x * 2 - control.x,
|
|
4380
|
+
y: around.y * 2 - control.y,
|
|
4381
|
+
};
|
|
4382
|
+
}
|
|
4383
|
+
function toAbsoluteSegments(rawSegments) {
|
|
4384
|
+
const segments = [];
|
|
4385
|
+
let current = { x: 0, y: 0 };
|
|
4386
|
+
let subpathStart = { x: 0, y: 0 };
|
|
4387
|
+
let previousCubicControl = null;
|
|
4388
|
+
let previousQuadraticControl = null;
|
|
4389
|
+
for (const segment of rawSegments) {
|
|
4390
|
+
const isRelative = segment.command === segment.command.toLowerCase();
|
|
4391
|
+
const command = segment.command.toUpperCase();
|
|
4392
|
+
const values = segment.values;
|
|
4393
|
+
switch (command) {
|
|
4394
|
+
case "M": {
|
|
4395
|
+
const x = isRelative ? current.x + values[0] : values[0];
|
|
4396
|
+
const y = isRelative ? current.y + values[1] : values[1];
|
|
4397
|
+
current = { x, y };
|
|
4398
|
+
subpathStart = { x, y };
|
|
4399
|
+
previousCubicControl = null;
|
|
4400
|
+
previousQuadraticControl = null;
|
|
4401
|
+
segments.push({ command: "M", values: [x, y] });
|
|
4402
|
+
break;
|
|
4403
|
+
}
|
|
4404
|
+
case "L": {
|
|
4405
|
+
const x = isRelative ? current.x + values[0] : values[0];
|
|
4406
|
+
const y = isRelative ? current.y + values[1] : values[1];
|
|
4407
|
+
current = { x, y };
|
|
4408
|
+
previousCubicControl = null;
|
|
4409
|
+
previousQuadraticControl = null;
|
|
4410
|
+
segments.push({ command: "L", values: [x, y] });
|
|
4411
|
+
break;
|
|
4412
|
+
}
|
|
4413
|
+
case "H": {
|
|
4414
|
+
const x = isRelative ? current.x + values[0] : values[0];
|
|
4415
|
+
current = { x, y: current.y };
|
|
4416
|
+
previousCubicControl = null;
|
|
4417
|
+
previousQuadraticControl = null;
|
|
4418
|
+
segments.push({ command: "L", values: [x, current.y] });
|
|
4419
|
+
break;
|
|
4420
|
+
}
|
|
4421
|
+
case "V": {
|
|
4422
|
+
const y = isRelative ? current.y + values[0] : values[0];
|
|
4423
|
+
current = { x: current.x, y };
|
|
4424
|
+
previousCubicControl = null;
|
|
4425
|
+
previousQuadraticControl = null;
|
|
4426
|
+
segments.push({ command: "L", values: [current.x, y] });
|
|
4427
|
+
break;
|
|
4428
|
+
}
|
|
4429
|
+
case "C": {
|
|
4430
|
+
const x1 = isRelative ? current.x + values[0] : values[0];
|
|
4431
|
+
const y1 = isRelative ? current.y + values[1] : values[1];
|
|
4432
|
+
const x2 = isRelative ? current.x + values[2] : values[2];
|
|
4433
|
+
const y2 = isRelative ? current.y + values[3] : values[3];
|
|
4434
|
+
const x = isRelative ? current.x + values[4] : values[4];
|
|
4435
|
+
const y = isRelative ? current.y + values[5] : values[5];
|
|
4436
|
+
current = { x, y };
|
|
4437
|
+
previousCubicControl = { x: x2, y: y2 };
|
|
4438
|
+
previousQuadraticControl = null;
|
|
4439
|
+
segments.push({ command: "C", values: [x1, y1, x2, y2, x, y] });
|
|
4440
|
+
break;
|
|
4441
|
+
}
|
|
4442
|
+
case "S": {
|
|
4443
|
+
const control1 = previousCubicControl
|
|
4444
|
+
? reflect(previousCubicControl, current)
|
|
4445
|
+
: { ...current };
|
|
4446
|
+
const x2 = isRelative ? current.x + values[0] : values[0];
|
|
4447
|
+
const y2 = isRelative ? current.y + values[1] : values[1];
|
|
4448
|
+
const x = isRelative ? current.x + values[2] : values[2];
|
|
4449
|
+
const y = isRelative ? current.y + values[3] : values[3];
|
|
4450
|
+
current = { x, y };
|
|
4451
|
+
previousCubicControl = { x: x2, y: y2 };
|
|
4452
|
+
previousQuadraticControl = null;
|
|
4453
|
+
segments.push({
|
|
4454
|
+
command: "C",
|
|
4455
|
+
values: [control1.x, control1.y, x2, y2, x, y],
|
|
4456
|
+
});
|
|
4457
|
+
break;
|
|
4458
|
+
}
|
|
4459
|
+
case "Q": {
|
|
4460
|
+
const x1 = isRelative ? current.x + values[0] : values[0];
|
|
4461
|
+
const y1 = isRelative ? current.y + values[1] : values[1];
|
|
4462
|
+
const x = isRelative ? current.x + values[2] : values[2];
|
|
4463
|
+
const y = isRelative ? current.y + values[3] : values[3];
|
|
4464
|
+
current = { x, y };
|
|
4465
|
+
previousCubicControl = null;
|
|
4466
|
+
previousQuadraticControl = { x: x1, y: y1 };
|
|
4467
|
+
segments.push({ command: "Q", values: [x1, y1, x, y] });
|
|
4468
|
+
break;
|
|
4469
|
+
}
|
|
4470
|
+
case "T": {
|
|
4471
|
+
const control = previousQuadraticControl
|
|
4472
|
+
? reflect(previousQuadraticControl, current)
|
|
4473
|
+
: { ...current };
|
|
4474
|
+
const x = isRelative ? current.x + values[0] : values[0];
|
|
4475
|
+
const y = isRelative ? current.y + values[1] : values[1];
|
|
4476
|
+
current = { x, y };
|
|
4477
|
+
previousCubicControl = null;
|
|
4478
|
+
previousQuadraticControl = control;
|
|
4479
|
+
segments.push({ command: "Q", values: [control.x, control.y, x, y] });
|
|
4480
|
+
break;
|
|
4481
|
+
}
|
|
4482
|
+
case "A": {
|
|
4483
|
+
const rx = Math.abs(values[0]);
|
|
4484
|
+
const ry = Math.abs(values[1]);
|
|
4485
|
+
const rotation = values[2];
|
|
4486
|
+
const largeArc = values[3];
|
|
4487
|
+
const sweep = values[4];
|
|
4488
|
+
const x = isRelative ? current.x + values[5] : values[5];
|
|
4489
|
+
const y = isRelative ? current.y + values[6] : values[6];
|
|
4490
|
+
current = { x, y };
|
|
4491
|
+
previousCubicControl = null;
|
|
4492
|
+
previousQuadraticControl = null;
|
|
4493
|
+
segments.push({
|
|
4494
|
+
command: "A",
|
|
4495
|
+
values: [rx, ry, rotation, largeArc, sweep, x, y],
|
|
4496
|
+
});
|
|
4497
|
+
break;
|
|
4498
|
+
}
|
|
4499
|
+
case "Z": {
|
|
4500
|
+
current = { ...subpathStart };
|
|
4501
|
+
previousCubicControl = null;
|
|
4502
|
+
previousQuadraticControl = null;
|
|
4503
|
+
segments.push({ command: "Z", values: [] });
|
|
4504
|
+
break;
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
}
|
|
4508
|
+
return segments;
|
|
4509
|
+
}
|
|
4510
|
+
function cubicAt(p0, p1, p2, p3, t) {
|
|
4511
|
+
const mt = 1 - t;
|
|
4512
|
+
return (mt * mt * mt * p0 +
|
|
4513
|
+
3 * mt * mt * t * p1 +
|
|
4514
|
+
3 * mt * t * t * p2 +
|
|
4515
|
+
t * t * t * p3);
|
|
4516
|
+
}
|
|
4517
|
+
function quadraticAt(p0, p1, p2, t) {
|
|
4518
|
+
const mt = 1 - t;
|
|
4519
|
+
return mt * mt * p0 + 2 * mt * t * p1 + t * t * p2;
|
|
4520
|
+
}
|
|
4521
|
+
function cubicExtrema(p0, p1, p2, p3) {
|
|
4522
|
+
const a = -p0 + 3 * p1 - 3 * p2 + p3;
|
|
4523
|
+
const b = 3 * p0 - 6 * p1 + 3 * p2;
|
|
4524
|
+
const c = -3 * p0 + 3 * p1;
|
|
4525
|
+
if (Math.abs(a) < EPSILON) {
|
|
4526
|
+
if (Math.abs(b) < EPSILON)
|
|
4527
|
+
return [];
|
|
4528
|
+
return [-c / (2 * b)].filter((t) => t > 0 && t < 1);
|
|
4529
|
+
}
|
|
4530
|
+
const discriminant = 4 * b * b - 12 * a * c;
|
|
4531
|
+
if (discriminant < 0)
|
|
4532
|
+
return [];
|
|
4533
|
+
const sqrtDiscriminant = Math.sqrt(discriminant);
|
|
4534
|
+
return [
|
|
4535
|
+
(-2 * b + sqrtDiscriminant) / (6 * a),
|
|
4536
|
+
(-2 * b - sqrtDiscriminant) / (6 * a),
|
|
4537
|
+
].filter((t) => t > 0 && t < 1);
|
|
4538
|
+
}
|
|
4539
|
+
function quadraticExtrema(p0, p1, p2) {
|
|
4540
|
+
const denominator = p0 - 2 * p1 + p2;
|
|
4541
|
+
if (Math.abs(denominator) < EPSILON)
|
|
4542
|
+
return [];
|
|
4543
|
+
const t = (p0 - p1) / denominator;
|
|
4544
|
+
return t > 0 && t < 1 ? [t] : [];
|
|
4545
|
+
}
|
|
4546
|
+
function angleBetween(u, v) {
|
|
4547
|
+
const magnitude = Math.hypot(u.x, u.y) * Math.hypot(v.x, v.y);
|
|
4548
|
+
if (magnitude < EPSILON)
|
|
4549
|
+
return 0;
|
|
4550
|
+
const sign = u.x * v.y - u.y * v.x < 0 ? -1 : 1;
|
|
4551
|
+
const cosine = Math.min(1, Math.max(-1, (u.x * v.x + u.y * v.y) / magnitude));
|
|
4552
|
+
return sign * Math.acos(cosine);
|
|
4553
|
+
}
|
|
4554
|
+
function sampleArc(start, values) {
|
|
4555
|
+
let [rx, ry, rotation, largeArcFlag, sweepFlag, endX, endY] = values;
|
|
4556
|
+
if ((Math.abs(start.x - endX) < EPSILON && Math.abs(start.y - endY) < EPSILON) || rx < EPSILON || ry < EPSILON) {
|
|
4557
|
+
return [start, { x: endX, y: endY }];
|
|
4558
|
+
}
|
|
4559
|
+
rx = Math.abs(rx);
|
|
4560
|
+
ry = Math.abs(ry);
|
|
4561
|
+
const phi = (rotation * Math.PI) / 180;
|
|
4562
|
+
const cosPhi = Math.cos(phi);
|
|
4563
|
+
const sinPhi = Math.sin(phi);
|
|
4564
|
+
const dx2 = (start.x - endX) / 2;
|
|
4565
|
+
const dy2 = (start.y - endY) / 2;
|
|
4566
|
+
const x1p = cosPhi * dx2 + sinPhi * dy2;
|
|
4567
|
+
const y1p = -sinPhi * dx2 + cosPhi * dy2;
|
|
4568
|
+
const lambda = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry);
|
|
4569
|
+
if (lambda > 1) {
|
|
4570
|
+
const scale = Math.sqrt(lambda);
|
|
4571
|
+
rx *= scale;
|
|
4572
|
+
ry *= scale;
|
|
4573
|
+
}
|
|
4574
|
+
const rx2 = rx * rx;
|
|
4575
|
+
const ry2 = ry * ry;
|
|
4576
|
+
const x1p2 = x1p * x1p;
|
|
4577
|
+
const y1p2 = y1p * y1p;
|
|
4578
|
+
const numerator = rx2 * ry2 - rx2 * y1p2 - ry2 * x1p2;
|
|
4579
|
+
const denominator = rx2 * y1p2 + ry2 * x1p2;
|
|
4580
|
+
const factor = denominator < EPSILON ? 0 : Math.sqrt(Math.max(0, numerator / denominator));
|
|
4581
|
+
const sign = largeArcFlag === sweepFlag ? -1 : 1;
|
|
4582
|
+
const cxp = sign * factor * ((rx * y1p) / ry);
|
|
4583
|
+
const cyp = sign * factor * (-(ry * x1p) / rx);
|
|
4584
|
+
const cx = cosPhi * cxp - sinPhi * cyp + (start.x + endX) / 2;
|
|
4585
|
+
const cy = sinPhi * cxp + cosPhi * cyp + (start.y + endY) / 2;
|
|
4586
|
+
const startVector = {
|
|
4587
|
+
x: (x1p - cxp) / rx,
|
|
4588
|
+
y: (y1p - cyp) / ry,
|
|
4589
|
+
};
|
|
4590
|
+
const endVector = {
|
|
4591
|
+
x: (-x1p - cxp) / rx,
|
|
4592
|
+
y: (-y1p - cyp) / ry,
|
|
4593
|
+
};
|
|
4594
|
+
let deltaTheta = angleBetween(startVector, endVector);
|
|
4595
|
+
if (!sweepFlag && deltaTheta > 0)
|
|
4596
|
+
deltaTheta -= Math.PI * 2;
|
|
4597
|
+
if (sweepFlag && deltaTheta < 0)
|
|
4598
|
+
deltaTheta += Math.PI * 2;
|
|
4599
|
+
const theta1 = angleBetween({ x: 1, y: 0 }, startVector);
|
|
4600
|
+
const steps = Math.max(12, Math.ceil(Math.abs(deltaTheta) / (Math.PI / 8)));
|
|
4601
|
+
const points = [];
|
|
4602
|
+
for (let index = 0; index <= steps; index += 1) {
|
|
4603
|
+
const theta = theta1 + (deltaTheta * index) / steps;
|
|
4604
|
+
const cosTheta = Math.cos(theta);
|
|
4605
|
+
const sinTheta = Math.sin(theta);
|
|
4606
|
+
points.push({
|
|
4607
|
+
x: cx + rx * cosPhi * cosTheta - ry * sinPhi * sinTheta,
|
|
4608
|
+
y: cy + rx * sinPhi * cosTheta + ry * cosPhi * sinTheta,
|
|
4609
|
+
});
|
|
4610
|
+
}
|
|
4611
|
+
return points;
|
|
4612
|
+
}
|
|
4613
|
+
function boundsFromAbsoluteSegments(segments) {
|
|
4614
|
+
if (!segments.length)
|
|
4615
|
+
return null;
|
|
4616
|
+
let minX = Infinity;
|
|
4617
|
+
let minY = Infinity;
|
|
4618
|
+
let maxX = -Infinity;
|
|
4619
|
+
let maxY = -Infinity;
|
|
4620
|
+
const include = (point) => {
|
|
4621
|
+
minX = Math.min(minX, point.x);
|
|
4622
|
+
minY = Math.min(minY, point.y);
|
|
4623
|
+
maxX = Math.max(maxX, point.x);
|
|
4624
|
+
maxY = Math.max(maxY, point.y);
|
|
4625
|
+
};
|
|
4626
|
+
let current = { x: 0, y: 0 };
|
|
4627
|
+
let subpathStart = { x: 0, y: 0 };
|
|
4628
|
+
for (const segment of segments) {
|
|
4629
|
+
switch (segment.command) {
|
|
4630
|
+
case "M": {
|
|
4631
|
+
current = { x: segment.values[0], y: segment.values[1] };
|
|
4632
|
+
subpathStart = { ...current };
|
|
4633
|
+
include(current);
|
|
4634
|
+
break;
|
|
4635
|
+
}
|
|
4636
|
+
case "L": {
|
|
4637
|
+
include(current);
|
|
4638
|
+
current = { x: segment.values[0], y: segment.values[1] };
|
|
4639
|
+
include(current);
|
|
4640
|
+
break;
|
|
4641
|
+
}
|
|
4642
|
+
case "C": {
|
|
4643
|
+
const [x1, y1, x2, y2, x, y] = segment.values;
|
|
4644
|
+
const ts = new Set([0, 1]);
|
|
4645
|
+
cubicExtrema(current.x, x1, x2, x).forEach((value) => ts.add(value));
|
|
4646
|
+
cubicExtrema(current.y, y1, y2, y).forEach((value) => ts.add(value));
|
|
4647
|
+
for (const t of ts) {
|
|
4648
|
+
include({
|
|
4649
|
+
x: cubicAt(current.x, x1, x2, x, t),
|
|
4650
|
+
y: cubicAt(current.y, y1, y2, y, t),
|
|
4651
|
+
});
|
|
4652
|
+
}
|
|
4653
|
+
current = { x, y };
|
|
4654
|
+
break;
|
|
4655
|
+
}
|
|
4656
|
+
case "Q": {
|
|
4657
|
+
const [x1, y1, x, y] = segment.values;
|
|
4658
|
+
const ts = new Set([0, 1]);
|
|
4659
|
+
quadraticExtrema(current.x, x1, x).forEach((value) => ts.add(value));
|
|
4660
|
+
quadraticExtrema(current.y, y1, y).forEach((value) => ts.add(value));
|
|
4661
|
+
for (const t of ts) {
|
|
4662
|
+
include({
|
|
4663
|
+
x: quadraticAt(current.x, x1, x, t),
|
|
4664
|
+
y: quadraticAt(current.y, y1, y, t),
|
|
4665
|
+
});
|
|
4666
|
+
}
|
|
4667
|
+
current = { x, y };
|
|
4668
|
+
break;
|
|
4669
|
+
}
|
|
4670
|
+
case "A": {
|
|
4671
|
+
for (const point of sampleArc(current, segment.values)) {
|
|
4672
|
+
include(point);
|
|
4673
|
+
}
|
|
4674
|
+
current = { x: segment.values[5], y: segment.values[6] };
|
|
4675
|
+
break;
|
|
4676
|
+
}
|
|
4677
|
+
case "Z": {
|
|
4678
|
+
include(current);
|
|
4679
|
+
include(subpathStart);
|
|
4680
|
+
current = { ...subpathStart };
|
|
4681
|
+
break;
|
|
4682
|
+
}
|
|
4683
|
+
}
|
|
4684
|
+
}
|
|
4685
|
+
if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(maxX) || !Number.isFinite(maxY)) {
|
|
4686
|
+
return null;
|
|
4687
|
+
}
|
|
4688
|
+
return { minX, minY, maxX, maxY };
|
|
4689
|
+
}
|
|
4690
|
+
function transformX(x, bounds, scaleX) {
|
|
4691
|
+
return (x - bounds.minX) * scaleX;
|
|
4692
|
+
}
|
|
4693
|
+
function transformY(y, bounds, scaleY) {
|
|
4694
|
+
return (y - bounds.minY) * scaleY;
|
|
4695
|
+
}
|
|
4696
|
+
function buildScaledPathData(segments, bounds, width, height) {
|
|
4697
|
+
const sourceWidth = Math.max(bounds.maxX - bounds.minX, EPSILON);
|
|
4698
|
+
const sourceHeight = Math.max(bounds.maxY - bounds.minY, EPSILON);
|
|
4699
|
+
const scaleX = width / sourceWidth;
|
|
4700
|
+
const scaleY = height / sourceHeight;
|
|
4701
|
+
return segments
|
|
4702
|
+
.map((segment) => {
|
|
4703
|
+
switch (segment.command) {
|
|
4704
|
+
case "M":
|
|
4705
|
+
case "L":
|
|
4706
|
+
return [
|
|
4707
|
+
segment.command,
|
|
4708
|
+
formatNumber(transformX(segment.values[0], bounds, scaleX)),
|
|
4709
|
+
formatNumber(transformY(segment.values[1], bounds, scaleY)),
|
|
4710
|
+
].join(" ");
|
|
4711
|
+
case "C":
|
|
4712
|
+
return [
|
|
4713
|
+
"C",
|
|
4714
|
+
formatNumber(transformX(segment.values[0], bounds, scaleX)),
|
|
4715
|
+
formatNumber(transformY(segment.values[1], bounds, scaleY)),
|
|
4716
|
+
formatNumber(transformX(segment.values[2], bounds, scaleX)),
|
|
4717
|
+
formatNumber(transformY(segment.values[3], bounds, scaleY)),
|
|
4718
|
+
formatNumber(transformX(segment.values[4], bounds, scaleX)),
|
|
4719
|
+
formatNumber(transformY(segment.values[5], bounds, scaleY)),
|
|
4720
|
+
].join(" ");
|
|
4721
|
+
case "Q":
|
|
4722
|
+
return [
|
|
4723
|
+
"Q",
|
|
4724
|
+
formatNumber(transformX(segment.values[0], bounds, scaleX)),
|
|
4725
|
+
formatNumber(transformY(segment.values[1], bounds, scaleY)),
|
|
4726
|
+
formatNumber(transformX(segment.values[2], bounds, scaleX)),
|
|
4727
|
+
formatNumber(transformY(segment.values[3], bounds, scaleY)),
|
|
4728
|
+
].join(" ");
|
|
4729
|
+
case "A":
|
|
4730
|
+
return [
|
|
4731
|
+
"A",
|
|
4732
|
+
formatNumber(segment.values[0] * scaleX),
|
|
4733
|
+
formatNumber(segment.values[1] * scaleY),
|
|
4734
|
+
formatNumber(segment.values[2]),
|
|
4735
|
+
formatNumber(segment.values[3]),
|
|
4736
|
+
formatNumber(segment.values[4]),
|
|
4737
|
+
formatNumber(transformX(segment.values[5], bounds, scaleX)),
|
|
4738
|
+
formatNumber(transformY(segment.values[6], bounds, scaleY)),
|
|
4739
|
+
].join(" ");
|
|
4740
|
+
case "Z":
|
|
4741
|
+
return "Z";
|
|
4742
|
+
}
|
|
4743
|
+
})
|
|
4744
|
+
.join(" ");
|
|
4745
|
+
}
|
|
4746
|
+
function intrinsicSizeFromBounds(bounds) {
|
|
4747
|
+
if (!bounds)
|
|
4748
|
+
return { width: 100, height: 100 };
|
|
4749
|
+
return {
|
|
4750
|
+
width: Math.max(1, Math.ceil(bounds.maxX - bounds.minX)),
|
|
4751
|
+
height: Math.max(1, Math.ceil(bounds.maxY - bounds.minY)),
|
|
4752
|
+
};
|
|
4753
|
+
}
|
|
4754
|
+
function parsePathGeometry(pathData) {
|
|
4755
|
+
const segments = toAbsoluteSegments(parseRawSegments(pathData));
|
|
4756
|
+
return {
|
|
4757
|
+
segments,
|
|
4758
|
+
bounds: boundsFromAbsoluteSegments(segments),
|
|
4759
|
+
};
|
|
4760
|
+
}
|
|
4761
|
+
function getPathIntrinsicSize(pathData) {
|
|
4762
|
+
if (!pathData)
|
|
4763
|
+
return { width: 100, height: 100 };
|
|
4764
|
+
return intrinsicSizeFromBounds(parsePathGeometry(pathData).bounds);
|
|
4765
|
+
}
|
|
4766
|
+
function getRenderablePathData(pathData, width, height) {
|
|
4767
|
+
if (!pathData)
|
|
4768
|
+
return null;
|
|
4769
|
+
const { segments, bounds } = parsePathGeometry(pathData);
|
|
4770
|
+
if (!segments.length || !bounds)
|
|
4771
|
+
return pathData;
|
|
4772
|
+
return buildScaledPathData(segments, bounds, Math.max(1, width), Math.max(1, height));
|
|
4773
|
+
}
|
|
4774
|
+
function getRenderableNodePathData(node) {
|
|
4775
|
+
return getRenderablePathData(node.pathData, node.w, node.h);
|
|
4776
|
+
}
|
|
4777
|
+
|
|
4254
4778
|
const pathShape = {
|
|
4255
4779
|
size(n, labelW) {
|
|
4256
|
-
|
|
4257
|
-
const w = n.width ?? Math.max(
|
|
4780
|
+
const intrinsic = getPathIntrinsicSize(n.pathData);
|
|
4781
|
+
const w = n.width ?? Math.max(intrinsic.width, Math.min(300, labelW + 20));
|
|
4258
4782
|
n.w = w;
|
|
4259
4783
|
if (!n.h) {
|
|
4260
|
-
if (!n.width && labelW + 20 > w) {
|
|
4784
|
+
if (!n.width && !n.height && labelW + 20 > w) {
|
|
4261
4785
|
const fontSize = Number(n.style?.fontSize ?? 14);
|
|
4262
4786
|
const lines = Math.ceil(labelW / (w - 20));
|
|
4263
|
-
n.h = Math.max(
|
|
4787
|
+
n.h = Math.max(intrinsic.height, lines * fontSize * 1.5 + 20);
|
|
4264
4788
|
}
|
|
4265
4789
|
else {
|
|
4266
|
-
n.h = n.height ??
|
|
4790
|
+
n.h = n.height ?? intrinsic.height;
|
|
4267
4791
|
}
|
|
4268
4792
|
}
|
|
4269
4793
|
},
|
|
4270
4794
|
renderSVG(rc, n, _palette, opts) {
|
|
4271
|
-
const d = n
|
|
4795
|
+
const d = getRenderableNodePathData(n);
|
|
4272
4796
|
if (!d) {
|
|
4273
|
-
// No path data — render placeholder box
|
|
4274
4797
|
return [rc.rectangle(n.x + 1, n.y + 1, n.w - 2, n.h - 2, opts)];
|
|
4275
4798
|
}
|
|
4276
4799
|
const el = rc.path(d, opts);
|
|
4277
|
-
// Wrap in a group to translate the user's path to the node position
|
|
4278
4800
|
const g = document.createElementNS(SVG_NS, "g");
|
|
4279
4801
|
g.setAttribute("transform", `translate(${n.x},${n.y})`);
|
|
4280
4802
|
g.appendChild(el);
|
|
4281
4803
|
return [g];
|
|
4282
4804
|
},
|
|
4283
4805
|
renderCanvas(rc, ctx, n, _palette, opts) {
|
|
4284
|
-
const d = n
|
|
4806
|
+
const d = getRenderableNodePathData(n);
|
|
4285
4807
|
if (!d) {
|
|
4286
4808
|
rc.rectangle(n.x + 1, n.y + 1, n.w - 2, n.h - 2, opts);
|
|
4287
4809
|
return;
|
|
@@ -4588,6 +5110,50 @@ var AIDiagram = (function (exports) {
|
|
|
4588
5110
|
const t = Math.min(tx, ty);
|
|
4589
5111
|
return [cx + t * dx, cy + t * dy];
|
|
4590
5112
|
}
|
|
5113
|
+
function clampInset(value) {
|
|
5114
|
+
return Math.max(2, value);
|
|
5115
|
+
}
|
|
5116
|
+
function anchoredConnPoint(entity, anchor, otherCX, otherCY) {
|
|
5117
|
+
if (!anchor) {
|
|
5118
|
+
if (entity.shape && otherCX != null && otherCY != null) {
|
|
5119
|
+
return connPoint(entity, { x: otherCX - 1, y: otherCY - 1, w: 2, h: 2});
|
|
5120
|
+
}
|
|
5121
|
+
if (otherCX != null && otherCY != null) {
|
|
5122
|
+
return rectConnPoint(entity.x, entity.y, entity.w, entity.h, otherCX, otherCY);
|
|
5123
|
+
}
|
|
5124
|
+
return [entity.x + entity.w / 2, entity.y + entity.h / 2];
|
|
5125
|
+
}
|
|
5126
|
+
const insetX = clampInset(Math.min(10, entity.w / 2));
|
|
5127
|
+
const insetY = clampInset(Math.min(10, entity.h / 2));
|
|
5128
|
+
const left = entity.x + insetX;
|
|
5129
|
+
const right = entity.x + entity.w - insetX;
|
|
5130
|
+
const top = entity.y + insetY;
|
|
5131
|
+
const bottom = entity.y + entity.h - insetY;
|
|
5132
|
+
const cx = entity.x + entity.w / 2;
|
|
5133
|
+
const cy = entity.y + entity.h / 2;
|
|
5134
|
+
switch (anchor) {
|
|
5135
|
+
case "top":
|
|
5136
|
+
return [cx, top];
|
|
5137
|
+
case "right":
|
|
5138
|
+
return [right, cy];
|
|
5139
|
+
case "bottom":
|
|
5140
|
+
return [cx, bottom];
|
|
5141
|
+
case "left":
|
|
5142
|
+
return [left, cy];
|
|
5143
|
+
case "center":
|
|
5144
|
+
return [cx, cy];
|
|
5145
|
+
case "top-left":
|
|
5146
|
+
return [left, top];
|
|
5147
|
+
case "top-right":
|
|
5148
|
+
return [right, top];
|
|
5149
|
+
case "bottom-left":
|
|
5150
|
+
return [left, bottom];
|
|
5151
|
+
case "bottom-right":
|
|
5152
|
+
return [right, bottom];
|
|
5153
|
+
default:
|
|
5154
|
+
return [cx, cy];
|
|
5155
|
+
}
|
|
5156
|
+
}
|
|
4591
5157
|
function rectConnPoint(rx, ry, rw, rh, ox, oy) {
|
|
4592
5158
|
const cx = rx + rw / 2, cy = ry + rh / 2;
|
|
4593
5159
|
const dx = ox - cx, dy = oy - cy;
|
|
@@ -4619,17 +5185,6 @@ var AIDiagram = (function (exports) {
|
|
|
4619
5185
|
return c;
|
|
4620
5186
|
return null;
|
|
4621
5187
|
}
|
|
4622
|
-
function connPt(src, dstCX, dstCY) {
|
|
4623
|
-
// SceneNode has a .shape field; use the existing connPoint for it
|
|
4624
|
-
if ("shape" in src && src.shape) {
|
|
4625
|
-
return connPoint(src, {
|
|
4626
|
-
x: dstCX - 1,
|
|
4627
|
-
y: dstCY - 1,
|
|
4628
|
-
w: 2,
|
|
4629
|
-
h: 2});
|
|
4630
|
-
}
|
|
4631
|
-
return rectConnPoint(src.x, src.y, src.w, src.h, dstCX, dstCY);
|
|
4632
|
-
}
|
|
4633
5188
|
for (const e of sg.edges) {
|
|
4634
5189
|
const src = resolve(e.from);
|
|
4635
5190
|
const dst = resolve(e.to);
|
|
@@ -4639,7 +5194,10 @@ var AIDiagram = (function (exports) {
|
|
|
4639
5194
|
}
|
|
4640
5195
|
const dstCX = dst.x + dst.w / 2, dstCY = dst.y + dst.h / 2;
|
|
4641
5196
|
const srcCX = src.x + src.w / 2, srcCY = src.y + src.h / 2;
|
|
4642
|
-
e.points = [
|
|
5197
|
+
e.points = [
|
|
5198
|
+
anchoredConnPoint(src, e.fromAnchor, dstCX, dstCY),
|
|
5199
|
+
anchoredConnPoint(dst, e.toAnchor, srcCX, srcCY),
|
|
5200
|
+
];
|
|
4643
5201
|
}
|
|
4644
5202
|
}
|
|
4645
5203
|
function computeBounds(sg, margin) {
|
|
@@ -7780,8 +8338,8 @@ var AIDiagram = (function (exports) {
|
|
|
7780
8338
|
continue;
|
|
7781
8339
|
const dstCX = dst.x + dst.w / 2, dstCY = dst.y + dst.h / 2;
|
|
7782
8340
|
const srcCX = src.x + src.w / 2, srcCY = src.y + src.h / 2;
|
|
7783
|
-
const [x1, y1] = getConnPoint(src, dstCX, dstCY);
|
|
7784
|
-
const [x2, y2] = getConnPoint(dst, srcCX, srcCY);
|
|
8341
|
+
const [x1, y1] = getConnPoint(src, dstCX, dstCY, e.fromAnchor);
|
|
8342
|
+
const [x2, y2] = getConnPoint(dst, srcCX, srcCY, e.toAnchor);
|
|
7785
8343
|
const eg = mkGroup(`edge-${e.from}-${e.to}`, "eg");
|
|
7786
8344
|
if (e.style?.opacity != null)
|
|
7787
8345
|
eg.setAttribute("opacity", String(e.style.opacity));
|
|
@@ -7857,7 +8415,9 @@ var AIDiagram = (function (exports) {
|
|
|
7857
8415
|
ng.dataset.w = String(n.w);
|
|
7858
8416
|
ng.dataset.h = String(n.h);
|
|
7859
8417
|
if (n.pathData)
|
|
7860
|
-
ng.dataset.pathData = n.pathData;
|
|
8418
|
+
ng.dataset.pathData = getRenderableNodePathData(n) ?? n.pathData;
|
|
8419
|
+
if (n.meta?.animationParent)
|
|
8420
|
+
ng.dataset.animationParent = n.meta.animationParent;
|
|
7861
8421
|
if (n.style?.opacity != null)
|
|
7862
8422
|
ng.setAttribute("opacity", String(n.style.opacity));
|
|
7863
8423
|
// ── Static transform (deg, dx, dy, factor) ──────────
|
|
@@ -8511,8 +9071,8 @@ var AIDiagram = (function (exports) {
|
|
|
8511
9071
|
continue;
|
|
8512
9072
|
const dstCX = dst.x + dst.w / 2, dstCY = dst.y + dst.h / 2;
|
|
8513
9073
|
const srcCX = src.x + src.w / 2, srcCY = src.y + src.h / 2;
|
|
8514
|
-
const [x1, y1] = getConnPoint(src, dstCX, dstCY);
|
|
8515
|
-
const [x2, y2] = getConnPoint(dst, srcCX, srcCY);
|
|
9074
|
+
const [x1, y1] = getConnPoint(src, dstCX, dstCY, e.fromAnchor);
|
|
9075
|
+
const [x2, y2] = getConnPoint(dst, srcCX, srcCY, e.toAnchor);
|
|
8516
9076
|
if (e.style?.opacity != null)
|
|
8517
9077
|
ctx.globalAlpha = Number(e.style.opacity);
|
|
8518
9078
|
const ecol = String(e.style?.stroke ?? palette.edgeStroke);
|
|
@@ -9369,6 +9929,13 @@ var AIDiagram = (function (exports) {
|
|
|
9369
9929
|
this.drawTargetNodes.delete(`node-${s.target}`);
|
|
9370
9930
|
}
|
|
9371
9931
|
}
|
|
9932
|
+
this._relatedElementIdsByPrimaryId = this._buildRelatedElementIndex();
|
|
9933
|
+
for (const nodeId of Array.from(this.drawTargetNodes)) {
|
|
9934
|
+
const relatedIds = this._relatedElementIdsByPrimaryId.get(nodeId);
|
|
9935
|
+
if (!relatedIds)
|
|
9936
|
+
continue;
|
|
9937
|
+
relatedIds.forEach((id) => this.drawTargetNodes.add(id));
|
|
9938
|
+
}
|
|
9372
9939
|
this._drawStepIndexByElementId = this._buildDrawStepIndex();
|
|
9373
9940
|
const { parentGroupByElementId, groupDescendantIds } = this._buildGroupVisibilityIndex();
|
|
9374
9941
|
this._parentGroupByElementId = parentGroupByElementId;
|
|
@@ -9399,10 +9966,30 @@ var AIDiagram = (function (exports) {
|
|
|
9399
9966
|
const el = resolveNonEdgeDrawEl(this.svg, step.target);
|
|
9400
9967
|
if (el && !drawStepIndexByElementId.has(el.id)) {
|
|
9401
9968
|
drawStepIndexByElementId.set(el.id, stepIndex);
|
|
9969
|
+
this._relatedElementIdsByPrimaryId.get(el.id)?.forEach((relatedId) => {
|
|
9970
|
+
if (!drawStepIndexByElementId.has(relatedId)) {
|
|
9971
|
+
drawStepIndexByElementId.set(relatedId, stepIndex);
|
|
9972
|
+
}
|
|
9973
|
+
});
|
|
9402
9974
|
}
|
|
9403
9975
|
});
|
|
9404
9976
|
return drawStepIndexByElementId;
|
|
9405
9977
|
}
|
|
9978
|
+
_buildRelatedElementIndex() {
|
|
9979
|
+
const relatedElementIdsByPrimaryId = new Map();
|
|
9980
|
+
this.svg.querySelectorAll(POSITIONABLE_SELECTOR).forEach((el) => {
|
|
9981
|
+
const animationParent = el.dataset.animationParent;
|
|
9982
|
+
if (!animationParent)
|
|
9983
|
+
return;
|
|
9984
|
+
const primaryEl = resolveNonEdgeDrawEl(this.svg, animationParent);
|
|
9985
|
+
if (!primaryEl || primaryEl.id === el.id)
|
|
9986
|
+
return;
|
|
9987
|
+
const related = relatedElementIdsByPrimaryId.get(primaryEl.id) ?? new Set();
|
|
9988
|
+
related.add(el.id);
|
|
9989
|
+
relatedElementIdsByPrimaryId.set(primaryEl.id, related);
|
|
9990
|
+
});
|
|
9991
|
+
return relatedElementIdsByPrimaryId;
|
|
9992
|
+
}
|
|
9406
9993
|
_buildGroupVisibilityIndex() {
|
|
9407
9994
|
const parentGroupByElementId = new Map();
|
|
9408
9995
|
const directChildIdsByGroup = new Map();
|
|
@@ -9481,10 +10068,18 @@ var AIDiagram = (function (exports) {
|
|
|
9481
10068
|
const el = resolveEl(this.svg, target);
|
|
9482
10069
|
if (!el)
|
|
9483
10070
|
return [];
|
|
9484
|
-
if (!el.id.startsWith("group-"))
|
|
9485
|
-
|
|
10071
|
+
if (!el.id.startsWith("group-")) {
|
|
10072
|
+
const ids = new Set([el.id]);
|
|
10073
|
+
this._relatedElementIdsByPrimaryId.get(el.id)?.forEach((id) => ids.add(id));
|
|
10074
|
+
return Array.from(ids)
|
|
10075
|
+
.map((id) => getEl(this.svg, id))
|
|
10076
|
+
.filter((candidate) => candidate != null);
|
|
10077
|
+
}
|
|
9486
10078
|
const ids = new Set([el.id]);
|
|
9487
10079
|
this._groupDescendantIds.get(el.id)?.forEach((id) => ids.add(id));
|
|
10080
|
+
Array.from(ids).forEach((id) => {
|
|
10081
|
+
this._relatedElementIdsByPrimaryId.get(id)?.forEach((relatedId) => ids.add(relatedId));
|
|
10082
|
+
});
|
|
9488
10083
|
return Array.from(ids)
|
|
9489
10084
|
.map((id) => getEl(this.svg, id))
|
|
9490
10085
|
.filter((candidate) => candidate != null);
|
|
@@ -9893,9 +10488,11 @@ var AIDiagram = (function (exports) {
|
|
|
9893
10488
|
// ── highlight ────────────────────────────────────────────
|
|
9894
10489
|
_doHighlight(target) {
|
|
9895
10490
|
this.svg
|
|
9896
|
-
.querySelectorAll(".ng.hl, .tg.hl, .ntg.hl, .cg.hl, .eg.hl")
|
|
10491
|
+
.querySelectorAll(".ng.hl, .gg.hl, .tg.hl, .ntg.hl, .cg.hl, .mdg.hl, .eg.hl")
|
|
9897
10492
|
.forEach((e) => e.classList.remove("hl"));
|
|
9898
|
-
|
|
10493
|
+
for (const el of this._resolveCascadeTargets(target)) {
|
|
10494
|
+
el.classList.add("hl");
|
|
10495
|
+
}
|
|
9899
10496
|
}
|
|
9900
10497
|
// ── fade / unfade ─────────────────────────────────────────
|
|
9901
10498
|
_doFade(target, doFade) {
|
|
@@ -9931,8 +10528,8 @@ var AIDiagram = (function (exports) {
|
|
|
9931
10528
|
}
|
|
9932
10529
|
// ── move ──────────────────────────────────────────────────
|
|
9933
10530
|
_doMove(target, step, silent) {
|
|
9934
|
-
const
|
|
9935
|
-
if (!
|
|
10531
|
+
const targets = this._resolveCascadeTargets(target);
|
|
10532
|
+
if (!targets.length)
|
|
9936
10533
|
return;
|
|
9937
10534
|
const cur = this._transforms.get(target) ?? {
|
|
9938
10535
|
tx: 0,
|
|
@@ -9945,12 +10542,14 @@ var AIDiagram = (function (exports) {
|
|
|
9945
10542
|
tx: cur.tx + (step.dx ?? 0),
|
|
9946
10543
|
ty: cur.ty + (step.dy ?? 0),
|
|
9947
10544
|
});
|
|
9948
|
-
|
|
10545
|
+
for (const el of targets) {
|
|
10546
|
+
this._writeTransform(el, target, silent, step.duration ?? 420);
|
|
10547
|
+
}
|
|
9949
10548
|
}
|
|
9950
10549
|
// ── scale ─────────────────────────────────────────────────
|
|
9951
10550
|
_doScale(target, step, silent) {
|
|
9952
|
-
const
|
|
9953
|
-
if (!
|
|
10551
|
+
const targets = this._resolveCascadeTargets(target);
|
|
10552
|
+
if (!targets.length)
|
|
9954
10553
|
return;
|
|
9955
10554
|
const cur = this._transforms.get(target) ?? {
|
|
9956
10555
|
tx: 0,
|
|
@@ -9959,12 +10558,14 @@ var AIDiagram = (function (exports) {
|
|
|
9959
10558
|
rotate: 0,
|
|
9960
10559
|
};
|
|
9961
10560
|
this._transforms.set(target, { ...cur, scale: step.factor ?? 1 });
|
|
9962
|
-
|
|
10561
|
+
for (const el of targets) {
|
|
10562
|
+
this._writeTransform(el, target, silent, step.duration ?? 350);
|
|
10563
|
+
}
|
|
9963
10564
|
}
|
|
9964
10565
|
// ── rotate ────────────────────────────────────────────────
|
|
9965
10566
|
_doRotate(target, step, silent) {
|
|
9966
|
-
const
|
|
9967
|
-
if (!
|
|
10567
|
+
const targets = this._resolveCascadeTargets(target);
|
|
10568
|
+
if (!targets.length)
|
|
9968
10569
|
return;
|
|
9969
10570
|
const cur = this._transforms.get(target) ?? {
|
|
9970
10571
|
tx: 0,
|
|
@@ -9976,7 +10577,9 @@ var AIDiagram = (function (exports) {
|
|
|
9976
10577
|
...cur,
|
|
9977
10578
|
rotate: cur.rotate + (step.deg ?? 0),
|
|
9978
10579
|
});
|
|
9979
|
-
|
|
10580
|
+
for (const el of targets) {
|
|
10581
|
+
this._writeTransform(el, target, silent, step.duration ?? 400);
|
|
10582
|
+
}
|
|
9980
10583
|
}
|
|
9981
10584
|
_doDraw(step, silent) {
|
|
9982
10585
|
const { target } = step;
|
|
@@ -10120,18 +10723,20 @@ var AIDiagram = (function (exports) {
|
|
|
10120
10723
|
return;
|
|
10121
10724
|
}
|
|
10122
10725
|
// ── Node draw ──────────────────────────────────────
|
|
10123
|
-
const
|
|
10124
|
-
if (!
|
|
10726
|
+
const nodeEls = this._resolveCascadeTargets(target).filter((el) => el.classList.contains("ng"));
|
|
10727
|
+
if (!nodeEls.length)
|
|
10125
10728
|
return;
|
|
10126
|
-
|
|
10127
|
-
|
|
10128
|
-
|
|
10129
|
-
|
|
10130
|
-
|
|
10131
|
-
|
|
10132
|
-
|
|
10729
|
+
for (const nodeEl of nodeEls) {
|
|
10730
|
+
showDrawEl(nodeEl);
|
|
10731
|
+
if (silent) {
|
|
10732
|
+
revealNodeInstant(nodeEl);
|
|
10733
|
+
}
|
|
10734
|
+
else {
|
|
10735
|
+
if (!nodeGuidePathEl(nodeEl) && !nodeEl.querySelector("path")?.style.strokeDasharray) {
|
|
10736
|
+
prepareNodeForDraw(nodeEl);
|
|
10737
|
+
}
|
|
10738
|
+
animateNodeDraw(nodeEl, step.duration ?? ANIMATION.nodeStrokeDur, step.duration ?? ANIMATION.textRevealMs);
|
|
10133
10739
|
}
|
|
10134
|
-
animateNodeDraw(nodeEl, step.duration ?? ANIMATION.nodeStrokeDur, step.duration ?? ANIMATION.textRevealMs);
|
|
10135
10740
|
}
|
|
10136
10741
|
}
|
|
10137
10742
|
// ── erase ─────────────────────────────────────────────────
|
|
@@ -10152,45 +10757,44 @@ var AIDiagram = (function (exports) {
|
|
|
10152
10757
|
}
|
|
10153
10758
|
// ── pulse ─────────────────────────────────────────────────
|
|
10154
10759
|
_doPulse(target, duration = 500) {
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
|
|
10760
|
+
for (const el of this._resolveCascadeTargets(target)) {
|
|
10761
|
+
el.animate([
|
|
10762
|
+
{ filter: "brightness(1)" },
|
|
10763
|
+
{ filter: "brightness(1.6)" },
|
|
10764
|
+
{ filter: "brightness(1)" },
|
|
10765
|
+
], { duration, iterations: 3 });
|
|
10766
|
+
}
|
|
10160
10767
|
}
|
|
10161
10768
|
// ── color ─────────────────────────────────────────────────
|
|
10162
10769
|
_doColor(target, color) {
|
|
10163
10770
|
if (!color)
|
|
10164
10771
|
return;
|
|
10165
|
-
const el
|
|
10166
|
-
|
|
10167
|
-
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
|
|
10177
|
-
|
|
10178
|
-
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
|
|
10185
|
-
if (attrFill === null && c.tagName === "path")
|
|
10186
|
-
return;
|
|
10187
|
-
c.style.fill = color;
|
|
10188
|
-
hit = true;
|
|
10189
|
-
});
|
|
10190
|
-
if (!hit) {
|
|
10191
|
-
el.querySelectorAll("text").forEach((t) => {
|
|
10192
|
-
t.style.fill = color;
|
|
10772
|
+
for (const el of this._resolveCascadeTargets(target)) {
|
|
10773
|
+
if (parseEdgeTarget(target)) {
|
|
10774
|
+
el.querySelectorAll("path, line, polyline").forEach((p) => {
|
|
10775
|
+
p.style.stroke = color;
|
|
10776
|
+
});
|
|
10777
|
+
el.querySelectorAll("polygon").forEach((p) => {
|
|
10778
|
+
p.style.fill = color;
|
|
10779
|
+
p.style.stroke = color;
|
|
10780
|
+
});
|
|
10781
|
+
continue;
|
|
10782
|
+
}
|
|
10783
|
+
let hit = false;
|
|
10784
|
+
el.querySelectorAll("path, rect, ellipse, polygon").forEach((c) => {
|
|
10785
|
+
const attrFill = c.getAttribute("fill");
|
|
10786
|
+
if (attrFill === "none")
|
|
10787
|
+
return;
|
|
10788
|
+
if (attrFill === null && c.tagName === "path")
|
|
10789
|
+
return;
|
|
10790
|
+
c.style.fill = color;
|
|
10791
|
+
hit = true;
|
|
10193
10792
|
});
|
|
10793
|
+
if (!hit) {
|
|
10794
|
+
el.querySelectorAll("text").forEach((t) => {
|
|
10795
|
+
t.style.fill = color;
|
|
10796
|
+
});
|
|
10797
|
+
}
|
|
10194
10798
|
}
|
|
10195
10799
|
}
|
|
10196
10800
|
// ── narration ───────────────────────────────────────────
|
|
@@ -10784,7 +11388,7 @@ var AIDiagram = (function (exports) {
|
|
|
10784
11388
|
}
|
|
10785
11389
|
|
|
10786
11390
|
function render(options) {
|
|
10787
|
-
const { container: rawContainer, dsl, renderer = "svg", injectCSS = true, tts, svgOptions = {}, canvasOptions = {}, onNodeClick, onReady, } = options;
|
|
11391
|
+
const { container: rawContainer, dsl, plugins, renderer = "svg", injectCSS = true, tts, svgOptions = {}, canvasOptions = {}, onNodeClick, onReady, } = options;
|
|
10788
11392
|
if (injectCSS && !document.getElementById("ai-diagram-css")) {
|
|
10789
11393
|
const style = document.createElement("style");
|
|
10790
11394
|
style.id = "ai-diagram-css";
|
|
@@ -10800,7 +11404,7 @@ var AIDiagram = (function (exports) {
|
|
|
10800
11404
|
else {
|
|
10801
11405
|
el = rawContainer;
|
|
10802
11406
|
}
|
|
10803
|
-
const ast = parse(dsl);
|
|
11407
|
+
const ast = parse(dsl, { plugins });
|
|
10804
11408
|
const scene = buildSceneGraph(ast);
|
|
10805
11409
|
layout(scene);
|
|
10806
11410
|
let svg;
|
|
@@ -11144,6 +11748,7 @@ var AIDiagram = (function (exports) {
|
|
|
11144
11748
|
const instance = render({
|
|
11145
11749
|
container: this.diagramWrap,
|
|
11146
11750
|
dsl: this.dsl,
|
|
11751
|
+
plugins: this.options.plugins,
|
|
11147
11752
|
renderer: this.renderer,
|
|
11148
11753
|
svgOptions: { interactive: true, showTitle: true, theme: this.options.svgOptions?.theme ?? this.theme, ...this.options.svgOptions },
|
|
11149
11754
|
canvasOptions: this.options.canvasOptions,
|
|
@@ -12221,6 +12826,7 @@ var AIDiagram = (function (exports) {
|
|
|
12221
12826
|
const instance = render({
|
|
12222
12827
|
container: this.diagramWrap,
|
|
12223
12828
|
dsl: this.dsl,
|
|
12829
|
+
plugins: this.options.plugins,
|
|
12224
12830
|
renderer: "svg",
|
|
12225
12831
|
svgOptions: {
|
|
12226
12832
|
showTitle: true,
|