@zukhruf/diagram 0.3.31 → 0.3.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/layouts/graph/areas.js +17 -8
- package/dist/lib/layouts/graph/areas.js.map +1 -1
- package/dist/lib/layouts/graph/attach.d.ts +25 -0
- package/dist/lib/layouts/graph/attach.d.ts.map +1 -0
- package/dist/lib/layouts/graph/attach.js +216 -0
- package/dist/lib/layouts/graph/attach.js.map +1 -0
- package/dist/lib/layouts/graph/connections.d.ts +38 -0
- package/dist/lib/layouts/graph/connections.d.ts.map +1 -0
- package/dist/lib/layouts/graph/connections.js +112 -0
- package/dist/lib/layouts/graph/connections.js.map +1 -0
- package/dist/lib/layouts/graph/graph-layout-result.d.ts +1 -1
- package/dist/lib/layouts/graph/graph-layout-result.d.ts.map +1 -1
- package/dist/lib/layouts/graph/graph-layout-result.js.map +1 -1
- package/dist/lib/layouts/graph/gutters.d.ts +1 -2
- package/dist/lib/layouts/graph/gutters.d.ts.map +1 -1
- package/dist/lib/layouts/graph/gutters.js +20 -19
- package/dist/lib/layouts/graph/gutters.js.map +1 -1
- package/dist/lib/layouts/graph/labels.d.ts +16 -0
- package/dist/lib/layouts/graph/labels.d.ts.map +1 -0
- package/dist/lib/layouts/graph/labels.js +76 -0
- package/dist/lib/layouts/graph/labels.js.map +1 -0
- package/dist/lib/layouts/graph/layout.d.ts.map +1 -1
- package/dist/lib/layouts/graph/layout.js +34 -13
- package/dist/lib/layouts/graph/layout.js.map +1 -1
- package/dist/lib/layouts/graph/measure.d.ts +7 -6
- package/dist/lib/layouts/graph/measure.d.ts.map +1 -1
- package/dist/lib/layouts/graph/measure.js +35 -29
- package/dist/lib/layouts/graph/measure.js.map +1 -1
- package/dist/lib/layouts/graph/min-heap.d.ts +12 -0
- package/dist/lib/layouts/graph/min-heap.d.ts.map +1 -0
- package/dist/lib/layouts/graph/min-heap.js +60 -0
- package/dist/lib/layouts/graph/min-heap.js.map +1 -0
- package/dist/lib/layouts/graph/node-content.d.ts +1 -2
- package/dist/lib/layouts/graph/node-content.d.ts.map +1 -1
- package/dist/lib/layouts/graph/node-content.js +43 -42
- package/dist/lib/layouts/graph/node-content.js.map +1 -1
- package/dist/lib/layouts/graph/path.d.ts +14 -0
- package/dist/lib/layouts/graph/path.d.ts.map +1 -0
- package/dist/lib/layouts/graph/path.js +55 -0
- package/dist/lib/layouts/graph/path.js.map +1 -0
- package/dist/lib/layouts/graph/route-search.d.ts +34 -0
- package/dist/lib/layouts/graph/route-search.d.ts.map +1 -0
- package/dist/lib/layouts/graph/route-search.js +208 -0
- package/dist/lib/layouts/graph/route-search.js.map +1 -0
- package/dist/lib/layouts/graph/route.d.ts +16 -0
- package/dist/lib/layouts/graph/route.d.ts.map +1 -0
- package/dist/lib/layouts/graph/route.js +241 -0
- package/dist/lib/layouts/graph/route.js.map +1 -0
- package/dist/lib/layouts/graph/sides.d.ts +23 -0
- package/dist/lib/layouts/graph/sides.d.ts.map +1 -0
- package/dist/lib/layouts/graph/sides.js +91 -0
- package/dist/lib/layouts/graph/sides.js.map +1 -0
- package/dist/lib/layouts/graph/tokens.d.ts +2 -7
- package/dist/lib/layouts/graph/tokens.d.ts.map +1 -1
- package/dist/lib/layouts/graph/tokens.js +91 -104
- package/dist/lib/layouts/graph/tokens.js.map +1 -1
- package/dist/lib/model/diagram-ir.d.ts +0 -3
- package/dist/lib/model/diagram-ir.d.ts.map +1 -1
- package/dist/lib/model/diagram-ir.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { nodeContent } from "./node-content.js";
|
|
2
2
|
import { labelLines, textBlock } from "./text-block.js";
|
|
3
|
+
import { LAYOUT_TOKENS } from "./tokens.js";
|
|
3
4
|
/** The distance from a port's point to its label: the port marker's radius plus a gap. */
|
|
4
5
|
const PORT_LABEL_OFFSET = 8;
|
|
5
6
|
const EDGE_LABEL_FRAME_PAD = 4;
|
|
@@ -10,11 +11,11 @@ const BADGE_PAD = {
|
|
|
10
11
|
};
|
|
11
12
|
const BADGE_MIN_CONTENT = 24;
|
|
12
13
|
const SIDES = ['top', 'right', 'bottom', 'left'];
|
|
13
|
-
export function measureNode(node
|
|
14
|
+
export function measureNode(node) {
|
|
14
15
|
const kind = node.kind ?? 'node';
|
|
15
16
|
if (node.hidden && kind === 'node') return hiddenNode();
|
|
16
|
-
const content = nodeContent(node
|
|
17
|
-
const ports = portGeometry(node.ports ?? []
|
|
17
|
+
const content = nodeContent(node);
|
|
18
|
+
const ports = portGeometry(node.ports ?? []);
|
|
18
19
|
const w = Math.max(content.w + 2 * content.pad.x + ports.inset.left + ports.inset.right, ports.minLength.top, ports.minLength.bottom);
|
|
19
20
|
const h = Math.max(content.h + 2 * content.pad.y + ports.inset.top + ports.inset.bottom, ports.minLength.left, ports.minLength.right);
|
|
20
21
|
const place = box => {
|
|
@@ -24,13 +25,13 @@ export function measureNode(node, tokens) {
|
|
|
24
25
|
left: content.pad.x,
|
|
25
26
|
right: content.pad.x
|
|
26
27
|
});
|
|
27
|
-
const frames = stackFrames(node, box
|
|
28
|
-
const caption = placeCaption(node, union([box, ...frames])
|
|
28
|
+
const frames = stackFrames(node, box);
|
|
29
|
+
const caption = placeCaption(node, union([box, ...frames]));
|
|
29
30
|
return {
|
|
30
31
|
box,
|
|
31
32
|
visual: union([box, ...frames, ...(caption ? [caption.box] : [])]),
|
|
32
33
|
frames,
|
|
33
|
-
radius: frameRadius(node, box
|
|
34
|
+
radius: frameRadius(node, box),
|
|
34
35
|
...content.place(inner),
|
|
35
36
|
...(caption ? {
|
|
36
37
|
caption
|
|
@@ -85,7 +86,7 @@ function hiddenNode() {
|
|
|
85
86
|
})
|
|
86
87
|
};
|
|
87
88
|
}
|
|
88
|
-
function frameRadius(node, box
|
|
89
|
+
function frameRadius(node, box) {
|
|
89
90
|
switch (node.kind) {
|
|
90
91
|
case 'connector':
|
|
91
92
|
return box.h / 2;
|
|
@@ -97,7 +98,7 @@ function frameRadius(node, box, tokens) {
|
|
|
97
98
|
return 0;
|
|
98
99
|
default:
|
|
99
100
|
if (node.frame === 'none') return 0;
|
|
100
|
-
return node.frame === 'flat' ?
|
|
101
|
+
return node.frame === 'flat' ? LAYOUT_TOKENS.FLAT_RADIUS : LAYOUT_TOKENS.RADIUS;
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
|
|
@@ -106,8 +107,8 @@ function frameRadius(node, box, tokens) {
|
|
|
106
107
|
* side's labels push the content inward, and a side with k ports is long
|
|
107
108
|
* enough for k + 1 port spacings.
|
|
108
109
|
*/
|
|
109
|
-
function portGeometry(ports
|
|
110
|
-
const font =
|
|
110
|
+
function portGeometry(ports) {
|
|
111
|
+
const font = LAYOUT_TOKENS.fonts.portLabel;
|
|
111
112
|
const bySide = new Map(SIDES.map(side => [side, ports.filter(port => port.side === side).map(port => ({
|
|
112
113
|
port,
|
|
113
114
|
label: port.label ? textBlock(labelLines(port.label, port.accent ? 'accent' : undefined), font) : undefined
|
|
@@ -126,7 +127,7 @@ function portGeometry(ports, tokens) {
|
|
|
126
127
|
const count = bySide.get(side).length;
|
|
127
128
|
if (count === 0) return 0;
|
|
128
129
|
const labelLength = side === 'top' || side === 'bottom' ? widest(side) : tallest(side);
|
|
129
|
-
const pitch = Math.max(
|
|
130
|
+
const pitch = Math.max(LAYOUT_TOKENS.PORT_SPACING, labelLength + LAYOUT_TOKENS.LABEL_GAP);
|
|
130
131
|
return (count + 1) * pitch;
|
|
131
132
|
};
|
|
132
133
|
return {
|
|
@@ -154,6 +155,11 @@ function portGeometry(ports, tokens) {
|
|
|
154
155
|
};
|
|
155
156
|
}
|
|
156
157
|
|
|
158
|
+
/** A group's ports sit evenly along their side of its rect, like a node's ports along its frame. */
|
|
159
|
+
export function placeGroupPorts(ports, rect) {
|
|
160
|
+
return portGeometry(ports).place(rect);
|
|
161
|
+
}
|
|
162
|
+
|
|
157
163
|
/** The point `fraction` of the way along `side` of `box`. */
|
|
158
164
|
function portPoint(box, side, fraction) {
|
|
159
165
|
switch (side) {
|
|
@@ -193,13 +199,13 @@ function placePortLabel(label, point, side) {
|
|
|
193
199
|
}
|
|
194
200
|
|
|
195
201
|
/** The back frames of a stack, farthest first, each one STACK_STEP further in the stack direction. */
|
|
196
|
-
function stackFrames(node, box
|
|
202
|
+
function stackFrames(node, box) {
|
|
197
203
|
const count = Math.max(1, Math.floor(node.stack ?? 1));
|
|
198
204
|
const kind = node.kind ?? 'node';
|
|
199
205
|
if (count === 1 || kind !== 'node' || node.frame === 'none') return [];
|
|
200
206
|
const direction = node.stackDirection ?? 'up-left';
|
|
201
|
-
const dx = (direction.endsWith('left') ? -1 : 1) *
|
|
202
|
-
const dy = (direction.startsWith('up') ? -1 : 1) *
|
|
207
|
+
const dx = (direction.endsWith('left') ? -1 : 1) * LAYOUT_TOKENS.STACK_STEP.x;
|
|
208
|
+
const dy = (direction.startsWith('up') ? -1 : 1) * LAYOUT_TOKENS.STACK_STEP.y;
|
|
203
209
|
return Array.from({
|
|
204
210
|
length: count - 1
|
|
205
211
|
}, (_, index) => {
|
|
@@ -213,8 +219,8 @@ function stackFrames(node, box, tokens) {
|
|
|
213
219
|
}
|
|
214
220
|
|
|
215
221
|
/** The caption sits beside `box`, the frame together with its stacked back frames. */
|
|
216
|
-
function placeCaption(node, box
|
|
217
|
-
const font =
|
|
222
|
+
function placeCaption(node, box) {
|
|
223
|
+
const font = LAYOUT_TOKENS.fonts.caption;
|
|
218
224
|
const lines = textBlock(labelLines([node.caption ?? []].flat().map(line => line.trim()), node.captionColor), font);
|
|
219
225
|
if (lines.h === 0) return undefined;
|
|
220
226
|
const framed = node.captionFrame ?? false;
|
|
@@ -227,7 +233,7 @@ function placeCaption(node, box, tokens) {
|
|
|
227
233
|
};
|
|
228
234
|
const w = lines.w + 2 * pad.x;
|
|
229
235
|
const h = lines.h + 2 * pad.y;
|
|
230
|
-
const gap =
|
|
236
|
+
const gap = LAYOUT_TOKENS.CAPTION_GAP;
|
|
231
237
|
const side = node.captionSide ?? 'bottom';
|
|
232
238
|
const centreX = box.x + (box.w - w) / 2;
|
|
233
239
|
const centreY = box.y + (box.h - h) / 2;
|
|
@@ -272,9 +278,9 @@ function placeCaption(node, box, tokens) {
|
|
|
272
278
|
text
|
|
273
279
|
};
|
|
274
280
|
}
|
|
275
|
-
export function measureGroupTitle(group
|
|
276
|
-
const lines = textBlock(labelLines(group.label),
|
|
277
|
-
const height = lines.h > 0 ? lines.h + 2 *
|
|
281
|
+
export function measureGroupTitle(group) {
|
|
282
|
+
const lines = textBlock(labelLines(group.label), LAYOUT_TOKENS.fonts.groupLabel);
|
|
283
|
+
const height = lines.h > 0 ? lines.h + 2 * LAYOUT_TOKENS.BAND_PAD : 0;
|
|
278
284
|
return {
|
|
279
285
|
height,
|
|
280
286
|
place(rect) {
|
|
@@ -282,13 +288,13 @@ export function measureGroupTitle(group, tokens) {
|
|
|
282
288
|
label: []
|
|
283
289
|
};
|
|
284
290
|
const y = group.labelSide === 'bottom' ? rect.y + rect.h - height : rect.y;
|
|
285
|
-
const top = y +
|
|
291
|
+
const top = y + LAYOUT_TOKENS.BAND_PAD;
|
|
286
292
|
const align = group.labelAlign ?? 'center';
|
|
287
293
|
let label;
|
|
288
294
|
if (align === 'start') {
|
|
289
|
-
label = lines.place(rect.x +
|
|
295
|
+
label = lines.place(rect.x + LAYOUT_TOKENS.PAD_X, top, 'start');
|
|
290
296
|
} else if (align === 'end') {
|
|
291
|
-
label = lines.place(rect.x + rect.w -
|
|
297
|
+
label = lines.place(rect.x + rect.w - LAYOUT_TOKENS.PAD_X, top, 'end');
|
|
292
298
|
} else {
|
|
293
299
|
label = lines.place(rect.x + rect.w / 2, top, 'middle');
|
|
294
300
|
}
|
|
@@ -306,8 +312,8 @@ export function measureGroupTitle(group, tokens) {
|
|
|
306
312
|
}
|
|
307
313
|
};
|
|
308
314
|
}
|
|
309
|
-
export function measureEdgeLabel(edge
|
|
310
|
-
const lines = textBlock(labelLines(edge.label),
|
|
315
|
+
export function measureEdgeLabel(edge) {
|
|
316
|
+
const lines = textBlock(labelLines(edge.label), LAYOUT_TOKENS.fonts.edgeLabel);
|
|
311
317
|
const pad = edge.labelFrame ? EDGE_LABEL_FRAME_PAD : 0;
|
|
312
318
|
return {
|
|
313
319
|
w: lines.w + 2 * pad,
|
|
@@ -315,10 +321,10 @@ export function measureEdgeLabel(edge, tokens) {
|
|
|
315
321
|
place: box => lines.place(box.x + box.w / 2, box.y + pad, 'middle')
|
|
316
322
|
};
|
|
317
323
|
}
|
|
318
|
-
export function measureEdgeBadge(edge
|
|
319
|
-
const lines = textBlock(labelLines(edge.badgeLabel),
|
|
320
|
-
const icon = edge.badgeIcon ?
|
|
321
|
-
const gap = icon > 0 && lines.w > 0 ?
|
|
324
|
+
export function measureEdgeBadge(edge) {
|
|
325
|
+
const lines = textBlock(labelLines(edge.badgeLabel), LAYOUT_TOKENS.fonts.badge);
|
|
326
|
+
const icon = edge.badgeIcon ? LAYOUT_TOKENS.BADGE_ICON : 0;
|
|
327
|
+
const gap = icon > 0 && lines.w > 0 ? LAYOUT_TOKENS.BLOCK_GAP : 0;
|
|
322
328
|
const rowW = icon + gap + lines.w;
|
|
323
329
|
return {
|
|
324
330
|
w: Math.max(BADGE_MIN_CONTENT, rowW) + 2 * BADGE_PAD.x,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"measure.js","names":["nodeContent","labelLines","textBlock","PORT_LABEL_OFFSET","EDGE_LABEL_FRAME_PAD","BADGE_PAD","x","y","BADGE_MIN_CONTENT","SIDES","measureNode","node","tokens","kind","hidden","hiddenNode","content","ports","portGeometry","w","Math","max","pad","inset","left","right","minLength","top","bottom","h","place","box","inner","frames","stackFrames","caption","placeCaption","union","visual","radius","frameRadius","frame","overhang","stretch","text","bullets","min","FLAT_RADIUS","RADIUS","font","fonts","portLabel","bySide","Map","map","side","filter","port","label","accent","undefined","widest","get","tallest","labelDepth","depth","sideLength","count","length","labelLength","pitch","PORT_SPACING","LABEL_GAP","sidesOf","flatMap","onSide","index","fraction","point","portPoint","run","placePortLabel","id","floor","stack","direction","stackDirection","dx","endsWith","STACK_STEP","dy","startsWith","Array","from","_","level","lines","flat","line","trim","captionColor","framed","captionFrame","size","gap","CAPTION_GAP","captionSide","centreX","centreY","captionBox","measureGroupTitle","group","groupLabel","height","BAND_PAD","rect","labelSide","align","labelAlign","PAD_X","titleBand","band","measureEdgeLabel","edge","edgeLabel","labelFrame","measureEdgeBadge","badgeLabel","badge","icon","badgeIcon","BADGE_ICON","BLOCK_GAP","rowW","cy","value","by","boxes"],"sources":["../../../../src/lib/layouts/graph/measure.ts"],"sourcesContent":["import type {\n Box,\n DiagramEdge,\n DiagramGroup,\n DiagramNode,\n DiagramNodePort,\n DiagramSide,\n} from '../../model/diagram-ir.ts';\nimport type { NodeLayout, TextRun } from './graph-layout-result.ts';\nimport { nodeContent } from './node-content.ts';\nimport { type TextBlock, labelLines, textBlock } from './text-block.ts';\nimport type { LayoutTokens } from './tokens.ts';\n\ntype Sides = Record<DiagramSide, number>;\n\nexport type MeasuredNode = {\n /** The smallest frame that holds the node's content. */\n frame: { w: number; h: number };\n /** How far the stacked frames and the caption reach outside the frame. */\n overhang: Sides;\n /** Whether the frame grows to fill its grid area. */\n stretch: boolean;\n /** Positions everything the node draws, given its final frame (at least `frame`). */\n place(box: Box): Omit<NodeLayout, 'node'>;\n};\n\n/** The distance from a port's point to its label: the port marker's radius plus a gap. */\nconst PORT_LABEL_OFFSET = 8;\nconst EDGE_LABEL_FRAME_PAD = 4;\n/** Mirrors the badge drawn before content sizing: 8px side inset, 6px above and below, 24px content at least. */\nconst BADGE_PAD = { x: 8, y: 6 };\nconst BADGE_MIN_CONTENT = 24;\n\nconst SIDES: DiagramSide[] = ['top', 'right', 'bottom', 'left'];\n\nexport function measureNode(\n node: DiagramNode,\n tokens: LayoutTokens,\n): MeasuredNode {\n const kind = node.kind ?? 'node';\n if (node.hidden && kind === 'node') return hiddenNode();\n\n const content = nodeContent(node, tokens);\n const ports = portGeometry(node.ports ?? [], tokens);\n const w = Math.max(\n content.w + 2 * content.pad.x + ports.inset.left + ports.inset.right,\n ports.minLength.top,\n ports.minLength.bottom,\n );\n const h = Math.max(\n content.h + 2 * content.pad.y + ports.inset.top + ports.inset.bottom,\n ports.minLength.left,\n ports.minLength.right,\n );\n\n const place = (box: Box): Omit<NodeLayout, 'node'> => {\n const inner = inset(inset(box, ports.inset), {\n top: content.pad.y,\n bottom: content.pad.y,\n left: content.pad.x,\n right: content.pad.x,\n });\n const frames = stackFrames(node, box, tokens);\n const caption = placeCaption(node, union([box, ...frames]), tokens);\n return {\n box,\n visual: union([box, ...frames, ...(caption ? [caption.box] : [])]),\n frames,\n radius: frameRadius(node, box, tokens),\n ...content.place(inner),\n ...(caption ? { caption } : {}),\n ports: ports.place(box),\n };\n };\n\n const { visual } = place({ x: 0, y: 0, w, h });\n return {\n frame: { w, h },\n overhang: {\n top: 0 - visual.y,\n left: 0 - visual.x,\n bottom: visual.y + visual.h - h,\n right: visual.x + visual.w - w,\n },\n stretch: kind === 'node' && (node.stretch ?? true),\n place,\n };\n}\n\nfunction hiddenNode(): MeasuredNode {\n return {\n frame: { w: 0, h: 0 },\n overhang: { top: 0, right: 0, bottom: 0, left: 0 },\n stretch: false,\n place: (box) => ({\n box,\n visual: box,\n frames: [],\n radius: 0,\n text: [],\n bullets: [],\n ports: [],\n }),\n };\n}\n\nfunction frameRadius(\n node: DiagramNode,\n box: Box,\n tokens: LayoutTokens,\n): number {\n switch (node.kind) {\n case 'connector':\n return box.h / 2;\n case 'junction':\n return Math.min(box.w, box.h) / 2;\n case 'marker':\n case 'appliance':\n case 'note':\n return 0;\n default:\n if (node.frame === 'none') return 0;\n return node.frame === 'flat' ? tokens.FLAT_RADIUS : tokens.RADIUS;\n }\n}\n\n/**\n * Ports sit evenly along their side, with their labels inside the frame. A\n * side's labels push the content inward, and a side with k ports is long\n * enough for k + 1 port spacings.\n */\nfunction portGeometry(ports: DiagramNodePort[], tokens: LayoutTokens) {\n const font = tokens.fonts.portLabel;\n const bySide = new Map(\n SIDES.map((side) => [\n side,\n ports\n .filter((port) => port.side === side)\n .map((port) => ({\n port,\n label: port.label\n ? textBlock(\n labelLines(port.label, port.accent ? 'accent' : undefined),\n font,\n )\n : undefined,\n })),\n ]),\n );\n const widest = (side: DiagramSide) =>\n Math.max(0, ...bySide.get(side)!.map(({ label }) => label?.w ?? 0));\n const tallest = (side: DiagramSide) =>\n Math.max(0, ...bySide.get(side)!.map(({ label }) => label?.h ?? 0));\n const labelDepth = (side: DiagramSide) => {\n const depth =\n side === 'left' || side === 'right' ? widest(side) : tallest(side);\n return depth > 0 ? PORT_LABEL_OFFSET + depth : 0;\n };\n const sideLength = (side: DiagramSide) => {\n const count = bySide.get(side)!.length;\n if (count === 0) return 0;\n const labelLength =\n side === 'top' || side === 'bottom' ? widest(side) : tallest(side);\n const pitch = Math.max(tokens.PORT_SPACING, labelLength + tokens.LABEL_GAP);\n return (count + 1) * pitch;\n };\n\n return {\n inset: sidesOf(labelDepth),\n minLength: sidesOf(sideLength),\n place: (box: Box): NodeLayout['ports'] =>\n SIDES.flatMap((side) => {\n const onSide = bySide.get(side)!;\n return onSide.map(({ port, label }, index) => {\n const fraction = (index + 1) / (onSide.length + 1);\n const point = portPoint(box, side, fraction);\n const run = label && placePortLabel(label, point, side)[0];\n return {\n id: port.id,\n side,\n point,\n ...(run ? { label: run } : {}),\n };\n });\n }),\n };\n}\n\n/** The point `fraction` of the way along `side` of `box`. */\nfunction portPoint(\n box: Box,\n side: DiagramSide,\n fraction: number,\n): { x: number; y: number } {\n switch (side) {\n case 'top':\n return { x: box.x + box.w * fraction, y: box.y };\n case 'bottom':\n return { x: box.x + box.w * fraction, y: box.y + box.h };\n case 'left':\n return { x: box.x, y: box.y + box.h * fraction };\n case 'right':\n return { x: box.x + box.w, y: box.y + box.h * fraction };\n }\n}\n\nfunction placePortLabel(\n label: TextBlock,\n point: { x: number; y: number },\n side: DiagramSide,\n): TextRun[] {\n switch (side) {\n case 'left':\n return label.place(\n point.x + PORT_LABEL_OFFSET,\n point.y - label.h / 2,\n 'start',\n );\n case 'right':\n return label.place(\n point.x - PORT_LABEL_OFFSET,\n point.y - label.h / 2,\n 'end',\n );\n case 'top':\n return label.place(point.x, point.y + PORT_LABEL_OFFSET, 'middle');\n case 'bottom':\n return label.place(\n point.x,\n point.y - PORT_LABEL_OFFSET - label.h,\n 'middle',\n );\n }\n}\n\n/** The back frames of a stack, farthest first, each one STACK_STEP further in the stack direction. */\nfunction stackFrames(node: DiagramNode, box: Box, tokens: LayoutTokens): Box[] {\n const count = Math.max(1, Math.floor(node.stack ?? 1));\n const kind = node.kind ?? 'node';\n if (count === 1 || kind !== 'node' || node.frame === 'none') return [];\n const direction = node.stackDirection ?? 'up-left';\n const dx = (direction.endsWith('left') ? -1 : 1) * tokens.STACK_STEP.x;\n const dy = (direction.startsWith('up') ? -1 : 1) * tokens.STACK_STEP.y;\n return Array.from({ length: count - 1 }, (_, index) => {\n const level = count - 1 - index;\n return { ...box, x: box.x + level * dx, y: box.y + level * dy };\n });\n}\n\n/** The caption sits beside `box`, the frame together with its stacked back frames. */\nfunction placeCaption(\n node: DiagramNode,\n box: Box,\n tokens: LayoutTokens,\n): NodeLayout['caption'] {\n const font = tokens.fonts.caption;\n const lines = textBlock(\n labelLines(\n [node.caption ?? []].flat().map((line) => line.trim()),\n node.captionColor,\n ),\n font,\n );\n if (lines.h === 0) return undefined;\n const framed = node.captionFrame ?? false;\n const pad = framed\n ? { x: 0.9 * font.size, y: 0.55 * font.size }\n : { x: 0, y: 0 };\n const w = lines.w + 2 * pad.x;\n const h = lines.h + 2 * pad.y;\n const gap = tokens.CAPTION_GAP;\n const side = node.captionSide ?? 'bottom';\n const centreX = box.x + (box.w - w) / 2;\n const centreY = box.y + (box.h - h) / 2;\n const captionBox: Box = {\n top: { x: centreX, y: box.y - gap - h, w, h },\n bottom: { x: centreX, y: box.y + box.h + gap, w, h },\n left: { x: box.x - gap - w, y: centreY, w, h },\n right: { x: box.x + box.w + gap, y: centreY, w, h },\n }[side];\n const top = captionBox.y + pad.y;\n let text: TextRun[];\n if (side === 'left') {\n text = lines.place(captionBox.x + w - pad.x, top, 'end');\n } else if (side === 'right') {\n text = lines.place(captionBox.x + pad.x, top, 'start');\n } else {\n text = lines.place(captionBox.x + w / 2, top, 'middle');\n }\n return { box: captionBox, framed, text };\n}\n\nexport type MeasuredGroupTitle = {\n /** The height the title reserves on its side, BAND_PAD above and below included; 0 without a label. */\n height: number;\n place(rect: Box): { band?: Box; label: TextRun[] };\n};\n\nexport function measureGroupTitle(\n group: DiagramGroup,\n tokens: LayoutTokens,\n): MeasuredGroupTitle {\n const lines = textBlock(labelLines(group.label), tokens.fonts.groupLabel);\n const height = lines.h > 0 ? lines.h + 2 * tokens.BAND_PAD : 0;\n return {\n height,\n place(rect) {\n if (height === 0) return { label: [] };\n const y =\n group.labelSide === 'bottom' ? rect.y + rect.h - height : rect.y;\n const top = y + tokens.BAND_PAD;\n const align = group.labelAlign ?? 'center';\n let label: TextRun[];\n if (align === 'start') {\n label = lines.place(rect.x + tokens.PAD_X, top, 'start');\n } else if (align === 'end') {\n label = lines.place(rect.x + rect.w - tokens.PAD_X, top, 'end');\n } else {\n label = lines.place(rect.x + rect.w / 2, top, 'middle');\n }\n return group.titleBand\n ? { band: { x: rect.x, y, w: rect.w, h: height }, label }\n : { label };\n },\n };\n}\n\nexport type MeasuredEdgeLabel = {\n w: number;\n h: number;\n place(box: Box): TextRun[];\n};\n\nexport function measureEdgeLabel(\n edge: DiagramEdge,\n tokens: LayoutTokens,\n): MeasuredEdgeLabel {\n const lines = textBlock(labelLines(edge.label), tokens.fonts.edgeLabel);\n const pad = edge.labelFrame ? EDGE_LABEL_FRAME_PAD : 0;\n return {\n w: lines.w + 2 * pad,\n h: lines.h + 2 * pad,\n place: (box) => lines.place(box.x + box.w / 2, box.y + pad, 'middle'),\n };\n}\n\nexport type MeasuredEdgeBadge = {\n w: number;\n h: number;\n place(box: Box): { icon?: Box; text: TextRun[] };\n};\n\nexport function measureEdgeBadge(\n edge: DiagramEdge,\n tokens: LayoutTokens,\n): MeasuredEdgeBadge {\n const lines = textBlock(labelLines(edge.badgeLabel), tokens.fonts.badge);\n const icon = edge.badgeIcon ? tokens.BADGE_ICON : 0;\n const gap = icon > 0 && lines.w > 0 ? tokens.BLOCK_GAP : 0;\n const rowW = icon + gap + lines.w;\n return {\n w: Math.max(BADGE_MIN_CONTENT, rowW) + 2 * BADGE_PAD.x,\n h: Math.max(BADGE_MIN_CONTENT, lines.h) + 2 * BADGE_PAD.y,\n place(box) {\n const x = box.x + (box.w - rowW) / 2;\n const cy = box.y + box.h / 2;\n const text = lines.place(x + icon + gap, cy - lines.h / 2, 'start');\n return icon > 0\n ? { icon: { x, y: cy - icon / 2, w: icon, h: icon }, text }\n : { text };\n },\n };\n}\n\nfunction sidesOf(value: (side: DiagramSide) => number): Sides {\n return {\n top: value('top'),\n right: value('right'),\n bottom: value('bottom'),\n left: value('left'),\n };\n}\n\nfunction inset(box: Box, by: Sides): Box {\n return {\n x: box.x + by.left,\n y: box.y + by.top,\n w: box.w - by.left - by.right,\n h: box.h - by.top - by.bottom,\n };\n}\n\nfunction union(boxes: Box[]): Box {\n const left = Math.min(...boxes.map((box) => box.x));\n const top = Math.min(...boxes.map((box) => box.y));\n const right = Math.max(...boxes.map((box) => box.x + box.w));\n const bottom = Math.max(...boxes.map((box) => box.y + box.h));\n return { x: left, y: top, w: right - left, h: bottom - top };\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"measure.js","names":["nodeContent","labelLines","textBlock","LAYOUT_TOKENS","PORT_LABEL_OFFSET","EDGE_LABEL_FRAME_PAD","BADGE_PAD","x","y","BADGE_MIN_CONTENT","SIDES","measureNode","node","kind","hidden","hiddenNode","content","ports","portGeometry","w","Math","max","pad","inset","left","right","minLength","top","bottom","h","place","box","inner","frames","stackFrames","caption","placeCaption","union","visual","radius","frameRadius","frame","overhang","stretch","text","bullets","min","FLAT_RADIUS","RADIUS","font","fonts","portLabel","bySide","Map","map","side","filter","port","label","accent","undefined","widest","get","tallest","labelDepth","depth","sideLength","count","length","labelLength","pitch","PORT_SPACING","LABEL_GAP","sidesOf","flatMap","onSide","index","fraction","point","portPoint","run","placePortLabel","id","placeGroupPorts","rect","floor","stack","direction","stackDirection","dx","endsWith","STACK_STEP","dy","startsWith","Array","from","_","level","lines","flat","line","trim","captionColor","framed","captionFrame","size","gap","CAPTION_GAP","captionSide","centreX","centreY","captionBox","measureGroupTitle","group","groupLabel","height","BAND_PAD","labelSide","align","labelAlign","PAD_X","titleBand","band","measureEdgeLabel","edge","edgeLabel","labelFrame","measureEdgeBadge","badgeLabel","badge","icon","badgeIcon","BADGE_ICON","BLOCK_GAP","rowW","cy","value","by","boxes"],"sources":["../../../../src/lib/layouts/graph/measure.ts"],"sourcesContent":["import type {\n Box,\n DiagramEdge,\n DiagramGroup,\n DiagramNode,\n DiagramNodePort,\n DiagramSide,\n} from '../../model/diagram-ir.ts';\nimport type { NodeLayout, TextRun } from './graph-layout-result.ts';\nimport { nodeContent } from './node-content.ts';\nimport { type TextBlock, labelLines, textBlock } from './text-block.ts';\nimport { LAYOUT_TOKENS } from './tokens.ts';\n\ntype Sides = Record<DiagramSide, number>;\n\nexport type MeasuredNode = {\n /** The smallest frame that holds the node's content. */\n frame: { w: number; h: number };\n /** How far the stacked frames and the caption reach outside the frame. */\n overhang: Sides;\n /** Whether the frame grows to fill its grid area. */\n stretch: boolean;\n /** Positions everything the node draws, given its final frame (at least `frame`). */\n place(box: Box): Omit<NodeLayout, 'node'>;\n};\n\n/** The distance from a port's point to its label: the port marker's radius plus a gap. */\nconst PORT_LABEL_OFFSET = 8;\nconst EDGE_LABEL_FRAME_PAD = 4;\n/** Mirrors the badge drawn before content sizing: 8px side inset, 6px above and below, 24px content at least. */\nconst BADGE_PAD = { x: 8, y: 6 };\nconst BADGE_MIN_CONTENT = 24;\n\nconst SIDES: DiagramSide[] = ['top', 'right', 'bottom', 'left'];\n\nexport function measureNode(node: DiagramNode): MeasuredNode {\n const kind = node.kind ?? 'node';\n if (node.hidden && kind === 'node') return hiddenNode();\n\n const content = nodeContent(node);\n const ports = portGeometry(node.ports ?? []);\n const w = Math.max(\n content.w + 2 * content.pad.x + ports.inset.left + ports.inset.right,\n ports.minLength.top,\n ports.minLength.bottom,\n );\n const h = Math.max(\n content.h + 2 * content.pad.y + ports.inset.top + ports.inset.bottom,\n ports.minLength.left,\n ports.minLength.right,\n );\n\n const place = (box: Box): Omit<NodeLayout, 'node'> => {\n const inner = inset(inset(box, ports.inset), {\n top: content.pad.y,\n bottom: content.pad.y,\n left: content.pad.x,\n right: content.pad.x,\n });\n const frames = stackFrames(node, box);\n const caption = placeCaption(node, union([box, ...frames]));\n return {\n box,\n visual: union([box, ...frames, ...(caption ? [caption.box] : [])]),\n frames,\n radius: frameRadius(node, box),\n ...content.place(inner),\n ...(caption ? { caption } : {}),\n ports: ports.place(box),\n };\n };\n\n const { visual } = place({ x: 0, y: 0, w, h });\n return {\n frame: { w, h },\n overhang: {\n top: 0 - visual.y,\n left: 0 - visual.x,\n bottom: visual.y + visual.h - h,\n right: visual.x + visual.w - w,\n },\n stretch: kind === 'node' && (node.stretch ?? true),\n place,\n };\n}\n\nfunction hiddenNode(): MeasuredNode {\n return {\n frame: { w: 0, h: 0 },\n overhang: { top: 0, right: 0, bottom: 0, left: 0 },\n stretch: false,\n place: (box) => ({\n box,\n visual: box,\n frames: [],\n radius: 0,\n text: [],\n bullets: [],\n ports: [],\n }),\n };\n}\n\nfunction frameRadius(node: DiagramNode, box: Box): number {\n switch (node.kind) {\n case 'connector':\n return box.h / 2;\n case 'junction':\n return Math.min(box.w, box.h) / 2;\n case 'marker':\n case 'appliance':\n case 'note':\n return 0;\n default:\n if (node.frame === 'none') return 0;\n return node.frame === 'flat'\n ? LAYOUT_TOKENS.FLAT_RADIUS\n : LAYOUT_TOKENS.RADIUS;\n }\n}\n\n/**\n * Ports sit evenly along their side, with their labels inside the frame. A\n * side's labels push the content inward, and a side with k ports is long\n * enough for k + 1 port spacings.\n */\nfunction portGeometry(ports: DiagramNodePort[]) {\n const font = LAYOUT_TOKENS.fonts.portLabel;\n const bySide = new Map(\n SIDES.map((side) => [\n side,\n ports\n .filter((port) => port.side === side)\n .map((port) => ({\n port,\n label: port.label\n ? textBlock(\n labelLines(port.label, port.accent ? 'accent' : undefined),\n font,\n )\n : undefined,\n })),\n ]),\n );\n const widest = (side: DiagramSide) =>\n Math.max(0, ...bySide.get(side)!.map(({ label }) => label?.w ?? 0));\n const tallest = (side: DiagramSide) =>\n Math.max(0, ...bySide.get(side)!.map(({ label }) => label?.h ?? 0));\n const labelDepth = (side: DiagramSide) => {\n const depth =\n side === 'left' || side === 'right' ? widest(side) : tallest(side);\n return depth > 0 ? PORT_LABEL_OFFSET + depth : 0;\n };\n const sideLength = (side: DiagramSide) => {\n const count = bySide.get(side)!.length;\n if (count === 0) return 0;\n const labelLength =\n side === 'top' || side === 'bottom' ? widest(side) : tallest(side);\n const pitch = Math.max(\n LAYOUT_TOKENS.PORT_SPACING,\n labelLength + LAYOUT_TOKENS.LABEL_GAP,\n );\n return (count + 1) * pitch;\n };\n\n return {\n inset: sidesOf(labelDepth),\n minLength: sidesOf(sideLength),\n place: (box: Box): NodeLayout['ports'] =>\n SIDES.flatMap((side) => {\n const onSide = bySide.get(side)!;\n return onSide.map(({ port, label }, index) => {\n const fraction = (index + 1) / (onSide.length + 1);\n const point = portPoint(box, side, fraction);\n const run = label && placePortLabel(label, point, side)[0];\n return {\n id: port.id,\n side,\n point,\n ...(run ? { label: run } : {}),\n };\n });\n }),\n };\n}\n\n/** A group's ports sit evenly along their side of its rect, like a node's ports along its frame. */\nexport function placeGroupPorts(\n ports: DiagramNodePort[],\n rect: Box,\n): NodeLayout['ports'] {\n return portGeometry(ports).place(rect);\n}\n\n/** The point `fraction` of the way along `side` of `box`. */\nfunction portPoint(\n box: Box,\n side: DiagramSide,\n fraction: number,\n): { x: number; y: number } {\n switch (side) {\n case 'top':\n return { x: box.x + box.w * fraction, y: box.y };\n case 'bottom':\n return { x: box.x + box.w * fraction, y: box.y + box.h };\n case 'left':\n return { x: box.x, y: box.y + box.h * fraction };\n case 'right':\n return { x: box.x + box.w, y: box.y + box.h * fraction };\n }\n}\n\nfunction placePortLabel(\n label: TextBlock,\n point: { x: number; y: number },\n side: DiagramSide,\n): TextRun[] {\n switch (side) {\n case 'left':\n return label.place(\n point.x + PORT_LABEL_OFFSET,\n point.y - label.h / 2,\n 'start',\n );\n case 'right':\n return label.place(\n point.x - PORT_LABEL_OFFSET,\n point.y - label.h / 2,\n 'end',\n );\n case 'top':\n return label.place(point.x, point.y + PORT_LABEL_OFFSET, 'middle');\n case 'bottom':\n return label.place(\n point.x,\n point.y - PORT_LABEL_OFFSET - label.h,\n 'middle',\n );\n }\n}\n\n/** The back frames of a stack, farthest first, each one STACK_STEP further in the stack direction. */\nfunction stackFrames(node: DiagramNode, box: Box): Box[] {\n const count = Math.max(1, Math.floor(node.stack ?? 1));\n const kind = node.kind ?? 'node';\n if (count === 1 || kind !== 'node' || node.frame === 'none') return [];\n const direction = node.stackDirection ?? 'up-left';\n const dx = (direction.endsWith('left') ? -1 : 1) * LAYOUT_TOKENS.STACK_STEP.x;\n const dy = (direction.startsWith('up') ? -1 : 1) * LAYOUT_TOKENS.STACK_STEP.y;\n return Array.from({ length: count - 1 }, (_, index) => {\n const level = count - 1 - index;\n return { ...box, x: box.x + level * dx, y: box.y + level * dy };\n });\n}\n\n/** The caption sits beside `box`, the frame together with its stacked back frames. */\nfunction placeCaption(node: DiagramNode, box: Box): NodeLayout['caption'] {\n const font = LAYOUT_TOKENS.fonts.caption;\n const lines = textBlock(\n labelLines(\n [node.caption ?? []].flat().map((line) => line.trim()),\n node.captionColor,\n ),\n font,\n );\n if (lines.h === 0) return undefined;\n const framed = node.captionFrame ?? false;\n const pad = framed\n ? { x: 0.9 * font.size, y: 0.55 * font.size }\n : { x: 0, y: 0 };\n const w = lines.w + 2 * pad.x;\n const h = lines.h + 2 * pad.y;\n const gap = LAYOUT_TOKENS.CAPTION_GAP;\n const side = node.captionSide ?? 'bottom';\n const centreX = box.x + (box.w - w) / 2;\n const centreY = box.y + (box.h - h) / 2;\n const captionBox: Box = {\n top: { x: centreX, y: box.y - gap - h, w, h },\n bottom: { x: centreX, y: box.y + box.h + gap, w, h },\n left: { x: box.x - gap - w, y: centreY, w, h },\n right: { x: box.x + box.w + gap, y: centreY, w, h },\n }[side];\n const top = captionBox.y + pad.y;\n let text: TextRun[];\n if (side === 'left') {\n text = lines.place(captionBox.x + w - pad.x, top, 'end');\n } else if (side === 'right') {\n text = lines.place(captionBox.x + pad.x, top, 'start');\n } else {\n text = lines.place(captionBox.x + w / 2, top, 'middle');\n }\n return { box: captionBox, framed, text };\n}\n\nexport type MeasuredGroupTitle = {\n /** The height the title reserves on its side, BAND_PAD above and below included; 0 without a label. */\n height: number;\n place(rect: Box): { band?: Box; label: TextRun[] };\n};\n\nexport function measureGroupTitle(group: DiagramGroup): MeasuredGroupTitle {\n const lines = textBlock(\n labelLines(group.label),\n LAYOUT_TOKENS.fonts.groupLabel,\n );\n const height = lines.h > 0 ? lines.h + 2 * LAYOUT_TOKENS.BAND_PAD : 0;\n return {\n height,\n place(rect) {\n if (height === 0) return { label: [] };\n const y =\n group.labelSide === 'bottom' ? rect.y + rect.h - height : rect.y;\n const top = y + LAYOUT_TOKENS.BAND_PAD;\n const align = group.labelAlign ?? 'center';\n let label: TextRun[];\n if (align === 'start') {\n label = lines.place(rect.x + LAYOUT_TOKENS.PAD_X, top, 'start');\n } else if (align === 'end') {\n label = lines.place(rect.x + rect.w - LAYOUT_TOKENS.PAD_X, top, 'end');\n } else {\n label = lines.place(rect.x + rect.w / 2, top, 'middle');\n }\n return group.titleBand\n ? { band: { x: rect.x, y, w: rect.w, h: height }, label }\n : { label };\n },\n };\n}\n\nexport type MeasuredEdgeLabel = {\n w: number;\n h: number;\n place(box: Box): TextRun[];\n};\n\nexport function measureEdgeLabel(edge: DiagramEdge): MeasuredEdgeLabel {\n const lines = textBlock(\n labelLines(edge.label),\n LAYOUT_TOKENS.fonts.edgeLabel,\n );\n const pad = edge.labelFrame ? EDGE_LABEL_FRAME_PAD : 0;\n return {\n w: lines.w + 2 * pad,\n h: lines.h + 2 * pad,\n place: (box) => lines.place(box.x + box.w / 2, box.y + pad, 'middle'),\n };\n}\n\nexport type MeasuredEdgeBadge = {\n w: number;\n h: number;\n place(box: Box): { icon?: Box; text: TextRun[] };\n};\n\nexport function measureEdgeBadge(edge: DiagramEdge): MeasuredEdgeBadge {\n const lines = textBlock(\n labelLines(edge.badgeLabel),\n LAYOUT_TOKENS.fonts.badge,\n );\n const icon = edge.badgeIcon ? LAYOUT_TOKENS.BADGE_ICON : 0;\n const gap = icon > 0 && lines.w > 0 ? LAYOUT_TOKENS.BLOCK_GAP : 0;\n const rowW = icon + gap + lines.w;\n return {\n w: Math.max(BADGE_MIN_CONTENT, rowW) + 2 * BADGE_PAD.x,\n h: Math.max(BADGE_MIN_CONTENT, lines.h) + 2 * BADGE_PAD.y,\n place(box) {\n const x = box.x + (box.w - rowW) / 2;\n const cy = box.y + box.h / 2;\n const text = lines.place(x + icon + gap, cy - lines.h / 2, 'start');\n return icon > 0\n ? { icon: { x, y: cy - icon / 2, w: icon, h: icon }, text }\n : { text };\n },\n };\n}\n\nfunction sidesOf(value: (side: DiagramSide) => number): Sides {\n return {\n top: value('top'),\n right: value('right'),\n bottom: value('bottom'),\n left: value('left'),\n };\n}\n\nfunction inset(box: Box, by: Sides): Box {\n return {\n x: box.x + by.left,\n y: box.y + by.top,\n w: box.w - by.left - by.right,\n h: box.h - by.top - by.bottom,\n };\n}\n\nfunction union(boxes: Box[]): Box {\n const left = Math.min(...boxes.map((box) => box.x));\n const top = Math.min(...boxes.map((box) => box.y));\n const right = Math.max(...boxes.map((box) => box.x + box.w));\n const bottom = Math.max(...boxes.map((box) => box.y + box.h));\n return { x: left, y: top, w: right - left, h: bottom - top };\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A binary min-heap of items by priority.
|
|
3
|
+
*
|
|
4
|
+
* Moved from graph-layout.ts, whose copy step 6 deletes with the old engine.
|
|
5
|
+
*/
|
|
6
|
+
export declare class MinHeap<T> {
|
|
7
|
+
#private;
|
|
8
|
+
get size(): number;
|
|
9
|
+
push(item: T, priority: number): void;
|
|
10
|
+
pop(): T | undefined;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=min-heap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"min-heap.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/min-heap.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,OAAO,CAAC,CAAC;;IAGpB,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKrC,GAAG,IAAI,CAAC,GAAG,SAAS;CA2CrB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A binary min-heap of items by priority.
|
|
3
|
+
*
|
|
4
|
+
* Moved from graph-layout.ts, whose copy step 6 deletes with the old engine.
|
|
5
|
+
*/
|
|
6
|
+
export class MinHeap {
|
|
7
|
+
#data = [];
|
|
8
|
+
get size() {
|
|
9
|
+
return this.#data.length;
|
|
10
|
+
}
|
|
11
|
+
push(item, priority) {
|
|
12
|
+
this.#data.push({
|
|
13
|
+
item,
|
|
14
|
+
priority
|
|
15
|
+
});
|
|
16
|
+
this.#bubbleUp(this.#data.length - 1);
|
|
17
|
+
}
|
|
18
|
+
pop() {
|
|
19
|
+
const top = this.#data[0];
|
|
20
|
+
if (!top) return undefined;
|
|
21
|
+
const last = this.#data.pop();
|
|
22
|
+
if (this.#data.length > 0) {
|
|
23
|
+
this.#data[0] = last;
|
|
24
|
+
this.#bubbleDown(0);
|
|
25
|
+
}
|
|
26
|
+
return top.item;
|
|
27
|
+
}
|
|
28
|
+
#bubbleUp(index) {
|
|
29
|
+
let i = index;
|
|
30
|
+
while (i > 0) {
|
|
31
|
+
const parent = i - 1 >> 1;
|
|
32
|
+
const entry = this.#data[i];
|
|
33
|
+
const parentEntry = this.#data[parent];
|
|
34
|
+
if (entry.priority >= parentEntry.priority) break;
|
|
35
|
+
this.#data[i] = parentEntry;
|
|
36
|
+
this.#data[parent] = entry;
|
|
37
|
+
i = parent;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
#bubbleDown(index) {
|
|
41
|
+
let i = index;
|
|
42
|
+
for (;;) {
|
|
43
|
+
const entry = this.#data[i];
|
|
44
|
+
let smallest = i;
|
|
45
|
+
let smallestEntry = entry;
|
|
46
|
+
for (const child of [2 * i + 1, 2 * i + 2]) {
|
|
47
|
+
const candidate = this.#data[child];
|
|
48
|
+
if (candidate && candidate.priority < smallestEntry.priority) {
|
|
49
|
+
smallest = child;
|
|
50
|
+
smallestEntry = candidate;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (smallest === i) break;
|
|
54
|
+
this.#data[i] = smallestEntry;
|
|
55
|
+
this.#data[smallest] = entry;
|
|
56
|
+
i = smallest;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=min-heap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"min-heap.js","names":["MinHeap","data","size","length","push","item","priority","bubbleUp","pop","top","undefined","last","bubbleDown","#bubbleUp","index","i","parent","entry","parentEntry","#bubbleDown","smallest","smallestEntry","child","candidate"],"sources":["../../../../src/lib/layouts/graph/min-heap.ts"],"sourcesContent":["/**\n * A binary min-heap of items by priority.\n *\n * Moved from graph-layout.ts, whose copy step 6 deletes with the old engine.\n */\nexport class MinHeap<T> {\n readonly #data: { item: T; priority: number }[] = [];\n\n get size(): number {\n return this.#data.length;\n }\n\n push(item: T, priority: number): void {\n this.#data.push({ item, priority });\n this.#bubbleUp(this.#data.length - 1);\n }\n\n pop(): T | undefined {\n const top = this.#data[0];\n if (!top) return undefined;\n const last = this.#data.pop()!;\n if (this.#data.length > 0) {\n this.#data[0] = last;\n this.#bubbleDown(0);\n }\n return top.item;\n }\n\n #bubbleUp(index: number): void {\n let i = index;\n while (i > 0) {\n const parent = (i - 1) >> 1;\n const entry = this.#data[i]!;\n const parentEntry = this.#data[parent]!;\n if (entry.priority >= parentEntry.priority) break;\n this.#data[i] = parentEntry;\n this.#data[parent] = entry;\n i = parent;\n }\n }\n\n #bubbleDown(index: number): void {\n let i = index;\n for (;;) {\n const entry = this.#data[i]!;\n let smallest = i;\n let smallestEntry = entry;\n for (const child of [2 * i + 1, 2 * i + 2]) {\n const candidate = this.#data[child];\n if (candidate && candidate.priority < smallestEntry.priority) {\n smallest = child;\n smallestEntry = candidate;\n }\n }\n if (smallest === i) break;\n this.#data[i] = smallestEntry;\n this.#data[smallest] = entry;\n i = smallest;\n }\n }\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMA,OAAO,CAAI;EACb,CAACC,IAAI,GAAoC,EAAE;EAEpD,IAAIC,IAAIA,CAAA,EAAW;IACjB,OAAO,IAAI,CAAC,CAACD,IAAI,CAACE,MAAM;EAC1B;EAEAC,IAAIA,CAACC,IAAO,EAAEC,QAAgB,EAAQ;IACpC,IAAI,CAAC,CAACL,IAAI,CAACG,IAAI,CAAC;MAAEC,IAAI;MAAEC;IAAS,CAAC,CAAC;IACnC,IAAI,CAAC,CAACC,QAAQ,CAAC,IAAI,CAAC,CAACN,IAAI,CAACE,MAAM,GAAG,CAAC,CAAC;EACvC;EAEAK,GAAGA,CAAA,EAAkB;IACnB,MAAMC,GAAG,GAAG,IAAI,CAAC,CAACR,IAAI,CAAC,CAAC,CAAC;IACzB,IAAI,CAACQ,GAAG,EAAE,OAAOC,SAAS;IAC1B,MAAMC,IAAI,GAAG,IAAI,CAAC,CAACV,IAAI,CAACO,GAAG,CAAC,CAAE;IAC9B,IAAI,IAAI,CAAC,CAACP,IAAI,CAACE,MAAM,GAAG,CAAC,EAAE;MACzB,IAAI,CAAC,CAACF,IAAI,CAAC,CAAC,CAAC,GAAGU,IAAI;MACpB,IAAI,CAAC,CAACC,UAAU,CAAC,CAAC,CAAC;IACrB;IACA,OAAOH,GAAG,CAACJ,IAAI;EACjB;EAEA,CAACE,QAAQM,CAACC,KAAa,EAAQ;IAC7B,IAAIC,CAAC,GAAGD,KAAK;IACb,OAAOC,CAAC,GAAG,CAAC,EAAE;MACZ,MAAMC,MAAM,GAAID,CAAC,GAAG,CAAC,IAAK,CAAC;MAC3B,MAAME,KAAK,GAAG,IAAI,CAAC,CAAChB,IAAI,CAACc,CAAC,CAAE;MAC5B,MAAMG,WAAW,GAAG,IAAI,CAAC,CAACjB,IAAI,CAACe,MAAM,CAAE;MACvC,IAAIC,KAAK,CAACX,QAAQ,IAAIY,WAAW,CAACZ,QAAQ,EAAE;MAC5C,IAAI,CAAC,CAACL,IAAI,CAACc,CAAC,CAAC,GAAGG,WAAW;MAC3B,IAAI,CAAC,CAACjB,IAAI,CAACe,MAAM,CAAC,GAAGC,KAAK;MAC1BF,CAAC,GAAGC,MAAM;IACZ;EACF;EAEA,CAACJ,UAAUO,CAACL,KAAa,EAAQ;IAC/B,IAAIC,CAAC,GAAGD,KAAK;IACb,SAAS;MACP,MAAMG,KAAK,GAAG,IAAI,CAAC,CAAChB,IAAI,CAACc,CAAC,CAAE;MAC5B,IAAIK,QAAQ,GAAGL,CAAC;MAChB,IAAIM,aAAa,GAAGJ,KAAK;MACzB,KAAK,MAAMK,KAAK,IAAI,CAAC,CAAC,GAAGP,CAAC,GAAG,CAAC,EAAE,CAAC,GAAGA,CAAC,GAAG,CAAC,CAAC,EAAE;QAC1C,MAAMQ,SAAS,GAAG,IAAI,CAAC,CAACtB,IAAI,CAACqB,KAAK,CAAC;QACnC,IAAIC,SAAS,IAAIA,SAAS,CAACjB,QAAQ,GAAGe,aAAa,CAACf,QAAQ,EAAE;UAC5Dc,QAAQ,GAAGE,KAAK;UAChBD,aAAa,GAAGE,SAAS;QAC3B;MACF;MACA,IAAIH,QAAQ,KAAKL,CAAC,EAAE;MACpB,IAAI,CAAC,CAACd,IAAI,CAACc,CAAC,CAAC,GAAGM,aAAa;MAC7B,IAAI,CAAC,CAACpB,IAAI,CAACmB,QAAQ,CAAC,GAAGH,KAAK;MAC5BF,CAAC,GAAGK,QAAQ;IACd;EACF;AACF","ignoreList":[]}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Box, DiagramNode } from '../../model/diagram-ir.ts';
|
|
2
2
|
import type { NodeLayout } from './graph-layout-result.ts';
|
|
3
|
-
import type { LayoutTokens } from './tokens.ts';
|
|
4
3
|
export type ContentPlacement = Pick<NodeLayout, 'text' | 'bullets'> & Partial<Pick<NodeLayout, 'icon' | 'appliance'>>;
|
|
5
4
|
/** What a node draws inside its frame: its measured size, the padding around it, and where it goes. */
|
|
6
5
|
export type NodeContent = {
|
|
@@ -12,5 +11,5 @@ export type NodeContent = {
|
|
|
12
11
|
};
|
|
13
12
|
place(inner: Box): ContentPlacement;
|
|
14
13
|
};
|
|
15
|
-
export declare function nodeContent(node: DiagramNode
|
|
14
|
+
export declare function nodeContent(node: DiagramNode): NodeContent;
|
|
16
15
|
//# sourceMappingURL=node-content.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-content.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/node-content.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAS,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"node-content.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/node-content.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAS,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAI3D,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC,GACjE,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC;AAElD,uGAAuG;AACvG,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,KAAK,CAAC,KAAK,EAAE,GAAG,GAAG,gBAAgB,CAAC;CACrC,CAAC;AAQF,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,CAe1D"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { labelLines, textBlock } from "./text-block.js";
|
|
2
|
+
import { LAYOUT_TOKENS } from "./tokens.js";
|
|
2
3
|
|
|
3
4
|
/** What a node draws inside its frame: its measured size, the padding around it, and where it goes. */
|
|
4
5
|
|
|
@@ -12,40 +13,40 @@ const NOTHING = {
|
|
|
12
13
|
text: [],
|
|
13
14
|
bullets: []
|
|
14
15
|
};
|
|
15
|
-
export function nodeContent(node
|
|
16
|
+
export function nodeContent(node) {
|
|
16
17
|
switch (node.kind) {
|
|
17
18
|
case 'junction':
|
|
18
|
-
return junctionContent(
|
|
19
|
+
return junctionContent();
|
|
19
20
|
case 'marker':
|
|
20
|
-
return markerContent(node
|
|
21
|
+
return markerContent(node);
|
|
21
22
|
case 'connector':
|
|
22
|
-
return connectorContent(node
|
|
23
|
+
return connectorContent(node);
|
|
23
24
|
case 'appliance':
|
|
24
|
-
return applianceContent(node
|
|
25
|
+
return applianceContent(node);
|
|
25
26
|
case 'note':
|
|
26
|
-
return noteContent(node
|
|
27
|
+
return noteContent(node);
|
|
27
28
|
default:
|
|
28
|
-
return boxContent(node
|
|
29
|
+
return boxContent(node);
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
|
-
function boxContent(node
|
|
32
|
+
function boxContent(node) {
|
|
32
33
|
const pad = node.frame === 'none' ? {
|
|
33
|
-
x:
|
|
34
|
-
y:
|
|
34
|
+
x: LAYOUT_TOKENS.LABEL_GAP,
|
|
35
|
+
y: LAYOUT_TOKENS.LABEL_GAP
|
|
35
36
|
} : {
|
|
36
|
-
x:
|
|
37
|
-
y:
|
|
37
|
+
x: LAYOUT_TOKENS.PAD_X,
|
|
38
|
+
y: LAYOUT_TOKENS.PAD_Y
|
|
38
39
|
};
|
|
39
40
|
const isCard = node.textAlign === 'start' || node.description !== undefined || (node.items?.length ?? 0) > 0;
|
|
40
41
|
if (isCard) return {
|
|
41
|
-
...cardContent(node
|
|
42
|
+
...cardContent(node),
|
|
42
43
|
pad
|
|
43
44
|
};
|
|
44
45
|
if (node.icon) return {
|
|
45
|
-
...iconRowContent(node
|
|
46
|
+
...iconRowContent(node),
|
|
46
47
|
pad
|
|
47
48
|
};
|
|
48
|
-
const lines = textBlock(labelLines(node.label, node.textColor),
|
|
49
|
+
const lines = textBlock(labelLines(node.label, node.textColor), LAYOUT_TOKENS.fonts.boxLabel);
|
|
49
50
|
return {
|
|
50
51
|
w: lines.w,
|
|
51
52
|
h: lines.h,
|
|
@@ -56,12 +57,12 @@ function boxContent(node, tokens) {
|
|
|
56
57
|
})
|
|
57
58
|
};
|
|
58
59
|
}
|
|
59
|
-
function iconRowContent(node
|
|
60
|
+
function iconRowContent(node) {
|
|
60
61
|
const {
|
|
61
62
|
ICON,
|
|
62
63
|
ICON_GAP
|
|
63
|
-
} =
|
|
64
|
-
const title = textBlock(labelLines(node.label, node.textColor),
|
|
64
|
+
} = LAYOUT_TOKENS;
|
|
65
|
+
const title = textBlock(labelLines(node.label, node.textColor), LAYOUT_TOKENS.fonts.title);
|
|
65
66
|
const w = title.w > 0 ? ICON + ICON_GAP + title.w : ICON;
|
|
66
67
|
const h = Math.max(ICON, title.h);
|
|
67
68
|
return {
|
|
@@ -82,16 +83,16 @@ function iconRowContent(node, tokens) {
|
|
|
82
83
|
}
|
|
83
84
|
};
|
|
84
85
|
}
|
|
85
|
-
function cardContent(node
|
|
86
|
+
function cardContent(node) {
|
|
86
87
|
const {
|
|
87
88
|
ICON,
|
|
88
89
|
ICON_GAP,
|
|
89
90
|
BLOCK_GAP,
|
|
90
91
|
BULLET_INDENT
|
|
91
|
-
} =
|
|
92
|
-
const title = textBlock(labelLines(node.label, node.textColor),
|
|
93
|
-
const description = textBlock(labelLines(node.description, node.textColor),
|
|
94
|
-
const items = textBlock(labelLines(node.items, node.textColor),
|
|
92
|
+
} = LAYOUT_TOKENS;
|
|
93
|
+
const title = textBlock(labelLines(node.label, node.textColor), LAYOUT_TOKENS.fonts.title);
|
|
94
|
+
const description = textBlock(labelLines(node.description, node.textColor), LAYOUT_TOKENS.fonts.body);
|
|
95
|
+
const items = textBlock(labelLines(node.items, node.textColor), LAYOUT_TOKENS.fonts.body);
|
|
95
96
|
let iconW = 0;
|
|
96
97
|
if (node.icon) iconW = title.w > 0 ? ICON + ICON_GAP : ICON;
|
|
97
98
|
const headerH = Math.max(node.icon ? ICON : 0, title.h);
|
|
@@ -105,7 +106,7 @@ function cardContent(node, tokens) {
|
|
|
105
106
|
h: items.h,
|
|
106
107
|
w: items.h > 0 ? BULLET_INDENT + items.w : 0
|
|
107
108
|
}].filter(section => section.h > 0);
|
|
108
|
-
const bulletRise =
|
|
109
|
+
const bulletRise = LAYOUT_TOKENS.fonts.body.size * 0.3;
|
|
109
110
|
return {
|
|
110
111
|
w: Math.max(0, ...sections.map(section => section.w)),
|
|
111
112
|
h: sections.reduce((sum, section) => sum + section.h, 0) + Math.max(0, sections.length - 1) * BLOCK_GAP,
|
|
@@ -137,14 +138,14 @@ function cardContent(node, tokens) {
|
|
|
137
138
|
}
|
|
138
139
|
};
|
|
139
140
|
}
|
|
140
|
-
function noteContent(node
|
|
141
|
-
const lines = textBlock(labelLines(node.label, node.textColor ?? 'dim'),
|
|
141
|
+
function noteContent(node) {
|
|
142
|
+
const lines = textBlock(labelLines(node.label, node.textColor ?? 'dim'), LAYOUT_TOKENS.fonts.note);
|
|
142
143
|
return {
|
|
143
144
|
w: lines.w,
|
|
144
145
|
h: lines.h,
|
|
145
146
|
pad: {
|
|
146
|
-
x:
|
|
147
|
-
y:
|
|
147
|
+
x: LAYOUT_TOKENS.LABEL_GAP,
|
|
148
|
+
y: LAYOUT_TOKENS.LABEL_GAP
|
|
148
149
|
},
|
|
149
150
|
place: inner => ({
|
|
150
151
|
text: lines.place(inner.x, inner.y, 'start'),
|
|
@@ -152,20 +153,20 @@ function noteContent(node, tokens) {
|
|
|
152
153
|
})
|
|
153
154
|
};
|
|
154
155
|
}
|
|
155
|
-
function markerContent(node
|
|
156
|
+
function markerContent(node) {
|
|
156
157
|
const {
|
|
157
158
|
MARKER_ICON,
|
|
158
159
|
BLOCK_GAP
|
|
159
|
-
} =
|
|
160
|
-
const lines = textBlock(labelLines(node.label, node.textColor),
|
|
160
|
+
} = LAYOUT_TOKENS;
|
|
161
|
+
const lines = textBlock(labelLines(node.label, node.textColor), LAYOUT_TOKENS.fonts.marker);
|
|
161
162
|
const gap = lines.h > 0 ? BLOCK_GAP : 0;
|
|
162
163
|
const h = MARKER_ICON + gap + lines.h;
|
|
163
164
|
return {
|
|
164
165
|
w: Math.max(MARKER_ICON, lines.w),
|
|
165
166
|
h,
|
|
166
167
|
pad: {
|
|
167
|
-
x:
|
|
168
|
-
y:
|
|
168
|
+
x: LAYOUT_TOKENS.LABEL_GAP,
|
|
169
|
+
y: LAYOUT_TOKENS.LABEL_GAP
|
|
169
170
|
},
|
|
170
171
|
place: inner => {
|
|
171
172
|
const top = centredTop(inner, h);
|
|
@@ -182,12 +183,12 @@ function markerContent(node, tokens) {
|
|
|
182
183
|
}
|
|
183
184
|
};
|
|
184
185
|
}
|
|
185
|
-
function connectorContent(node
|
|
186
|
+
function connectorContent(node) {
|
|
186
187
|
const {
|
|
187
188
|
CONNECTOR_ICON,
|
|
188
189
|
BLOCK_GAP
|
|
189
|
-
} =
|
|
190
|
-
const lines = textBlock(labelLines(node.label, node.textColor),
|
|
190
|
+
} = LAYOUT_TOKENS;
|
|
191
|
+
const lines = textBlock(labelLines(node.label, node.textColor), LAYOUT_TOKENS.fonts.connector);
|
|
191
192
|
const iconW = node.icon ? CONNECTOR_ICON + (lines.w > 0 ? BLOCK_GAP : 0) : 0;
|
|
192
193
|
const h = Math.max(CONNECTOR_MIN_HEIGHT - 2 * CONNECTOR_PAD.y, lines.h);
|
|
193
194
|
return {
|
|
@@ -215,10 +216,10 @@ function connectorContent(node, tokens) {
|
|
|
215
216
|
}
|
|
216
217
|
};
|
|
217
218
|
}
|
|
218
|
-
function junctionContent(
|
|
219
|
+
function junctionContent() {
|
|
219
220
|
return {
|
|
220
|
-
w:
|
|
221
|
-
h:
|
|
221
|
+
w: LAYOUT_TOKENS.JUNCTION,
|
|
222
|
+
h: LAYOUT_TOKENS.JUNCTION,
|
|
222
223
|
pad: {
|
|
223
224
|
x: 0,
|
|
224
225
|
y: 0
|
|
@@ -226,12 +227,12 @@ function junctionContent(tokens) {
|
|
|
226
227
|
place: () => NOTHING
|
|
227
228
|
};
|
|
228
229
|
}
|
|
229
|
-
function applianceContent(node
|
|
230
|
+
function applianceContent(node) {
|
|
230
231
|
const {
|
|
231
232
|
APPLIANCE,
|
|
232
233
|
LABEL_GAP
|
|
233
|
-
} =
|
|
234
|
-
const lines = textBlock(labelLines(node.label, node.textColor),
|
|
234
|
+
} = LAYOUT_TOKENS;
|
|
235
|
+
const lines = textBlock(labelLines(node.label, node.textColor), LAYOUT_TOKENS.fonts.appliance);
|
|
235
236
|
return {
|
|
236
237
|
w: Math.max(APPLIANCE.w, lines.w + 2 * LABEL_GAP),
|
|
237
238
|
h: APPLIANCE.h + (lines.h > 0 ? lines.h + LABEL_GAP : 0),
|