@zukhruf/diagram 0.3.32 → 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/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.js +5 -5
- 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 +31 -9
- 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/package.json +1 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { LAYOUT_TOKENS } from "./tokens.js";
|
|
2
|
+
|
|
3
|
+
/** Where along a segment a tag is tried, in order. */
|
|
4
|
+
const FRACTIONS = [0.5, 0.3, 0.7, 0.15, 0.85];
|
|
5
|
+
/**
|
|
6
|
+
* The first free position for a tag of `size` on the route: the longest
|
|
7
|
+
* segment first, and on each segment the fractions in FRACTIONS order. A
|
|
8
|
+
* label sits LABEL_GAP beside the segment (above, then below a horizontal
|
|
9
|
+
* one; right, then left of a vertical one). A badge sits centred on it.
|
|
10
|
+
*/
|
|
11
|
+
export function tagPosition(size, points, placement, isFree) {
|
|
12
|
+
const segments = points.slice(1).map((to, segment) => ({
|
|
13
|
+
from: points[segment],
|
|
14
|
+
to,
|
|
15
|
+
segment
|
|
16
|
+
})).sort((a, b) => length(b.from, b.to) - length(a.from, a.to) || a.segment - b.segment);
|
|
17
|
+
for (const {
|
|
18
|
+
from,
|
|
19
|
+
to,
|
|
20
|
+
segment
|
|
21
|
+
} of segments) {
|
|
22
|
+
for (const t of FRACTIONS) {
|
|
23
|
+
const at = {
|
|
24
|
+
x: from.x + (to.x - from.x) * t,
|
|
25
|
+
y: from.y + (to.y - from.y) * t
|
|
26
|
+
};
|
|
27
|
+
for (const box of candidates(size, at, from.y === to.y, placement)) {
|
|
28
|
+
if (isFree(box)) return {
|
|
29
|
+
box,
|
|
30
|
+
segment
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
function candidates({
|
|
38
|
+
w,
|
|
39
|
+
h
|
|
40
|
+
}, at, horizontal, placement) {
|
|
41
|
+
const gap = LAYOUT_TOKENS.LABEL_GAP;
|
|
42
|
+
if (placement === 'on') return [{
|
|
43
|
+
x: at.x - w / 2,
|
|
44
|
+
y: at.y - h / 2,
|
|
45
|
+
w,
|
|
46
|
+
h
|
|
47
|
+
}];
|
|
48
|
+
if (horizontal) {
|
|
49
|
+
return [{
|
|
50
|
+
x: at.x - w / 2,
|
|
51
|
+
y: at.y - gap - h,
|
|
52
|
+
w,
|
|
53
|
+
h
|
|
54
|
+
}, {
|
|
55
|
+
x: at.x - w / 2,
|
|
56
|
+
y: at.y + gap,
|
|
57
|
+
w,
|
|
58
|
+
h
|
|
59
|
+
}];
|
|
60
|
+
}
|
|
61
|
+
return [{
|
|
62
|
+
x: at.x + gap,
|
|
63
|
+
y: at.y - h / 2,
|
|
64
|
+
w,
|
|
65
|
+
h
|
|
66
|
+
}, {
|
|
67
|
+
x: at.x - gap - w,
|
|
68
|
+
y: at.y - h / 2,
|
|
69
|
+
w,
|
|
70
|
+
h
|
|
71
|
+
}];
|
|
72
|
+
}
|
|
73
|
+
function length(a, b) {
|
|
74
|
+
return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=labels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labels.js","names":["LAYOUT_TOKENS","FRACTIONS","tagPosition","size","points","placement","isFree","segments","slice","map","to","segment","from","sort","a","b","length","t","at","x","y","box","candidates","undefined","w","h","horizontal","gap","LABEL_GAP","Math","abs"],"sources":["../../../../src/lib/layouts/graph/labels.ts"],"sourcesContent":["import type { Box, Point } from '../../model/diagram-ir.ts';\nimport { LAYOUT_TOKENS } from './tokens.ts';\n\n/** Where along a segment a tag is tried, in order. */\nconst FRACTIONS = [0.5, 0.3, 0.7, 0.15, 0.85];\n\nexport type TagPosition = { box: Box; segment: number };\n\n/**\n * The first free position for a tag of `size` on the route: the longest\n * segment first, and on each segment the fractions in FRACTIONS order. A\n * label sits LABEL_GAP beside the segment (above, then below a horizontal\n * one; right, then left of a vertical one). A badge sits centred on it.\n */\nexport function tagPosition(\n size: { w: number; h: number },\n points: Point[],\n placement: 'beside' | 'on',\n isFree: (box: Box) => boolean,\n): TagPosition | undefined {\n const segments = points\n .slice(1)\n .map((to, segment) => ({ from: points[segment]!, to, segment }))\n .sort(\n (a, b) =>\n length(b.from, b.to) - length(a.from, a.to) || a.segment - b.segment,\n );\n for (const { from, to, segment } of segments) {\n for (const t of FRACTIONS) {\n const at = {\n x: from.x + (to.x - from.x) * t,\n y: from.y + (to.y - from.y) * t,\n };\n for (const box of candidates(size, at, from.y === to.y, placement)) {\n if (isFree(box)) return { box, segment };\n }\n }\n }\n return undefined;\n}\n\nfunction candidates(\n { w, h }: { w: number; h: number },\n at: Point,\n horizontal: boolean,\n placement: 'beside' | 'on',\n): Box[] {\n const gap = LAYOUT_TOKENS.LABEL_GAP;\n if (placement === 'on') return [{ x: at.x - w / 2, y: at.y - h / 2, w, h }];\n if (horizontal) {\n return [\n { x: at.x - w / 2, y: at.y - gap - h, w, h },\n { x: at.x - w / 2, y: at.y + gap, w, h },\n ];\n }\n return [\n { x: at.x + gap, y: at.y - h / 2, w, h },\n { x: at.x - gap - w, y: at.y - h / 2, w, h },\n ];\n}\n\nfunction length(a: Point, b: Point): number {\n return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/layout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAO,cAAc,EAAS,MAAM,2BAA2B,CAAC;AAE5E,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/layout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAO,cAAc,EAAS,MAAM,2BAA2B,CAAC;AAE5E,OAAO,KAAK,EAEV,WAAW,EAIZ,MAAM,0BAA0B,CAAC;AAalC;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,GAAG,WAAW,CAoE3D"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { gridAreas } from "./areas.js";
|
|
2
2
|
import { gridGutters } from "./gutters.js";
|
|
3
|
-
import { measureGroupTitle, measureNode } from "./measure.js";
|
|
3
|
+
import { measureGroupTitle, measureNode, placeGroupPorts } from "./measure.js";
|
|
4
|
+
import { routeGraph } from "./route.js";
|
|
4
5
|
import { LAYOUT_TOKENS } from "./tokens.js";
|
|
5
6
|
import { sizeTracks } from "./tracks.js";
|
|
6
7
|
|
|
@@ -11,8 +12,8 @@ import { sizeTracks } from "./tracks.js";
|
|
|
11
12
|
* drawn without overlap.
|
|
12
13
|
*/
|
|
13
14
|
export function layoutGraph(ir) {
|
|
14
|
-
const measured = new Map(ir.nodes.map(node => [node.id, measureNode(node
|
|
15
|
-
const titles = new Map(ir.groups.map(group => [group.id, measureGroupTitle(group
|
|
15
|
+
const measured = new Map(ir.nodes.map(node => [node.id, measureNode(node)]));
|
|
16
|
+
const titles = new Map(ir.groups.map(group => [group.id, measureGroupTitle(group)]));
|
|
16
17
|
const areas = gridAreas(ir);
|
|
17
18
|
const gutters = gridGutters(areas, new Map([...titles].map(([id, title]) => [id, title.height])), ir.edges);
|
|
18
19
|
const items = axis => ir.nodes.map(node => {
|
|
@@ -45,12 +46,17 @@ export function layoutGraph(ir) {
|
|
|
45
46
|
};
|
|
46
47
|
});
|
|
47
48
|
const groups = areas.groups.map(groupArea => placeGroup(groupArea, titles.get(groupArea.group.id), lines, gutters));
|
|
49
|
+
const widestGutter = Math.max(...[...gutters.row, ...gutters.col].map(gutter => gutter.size));
|
|
50
|
+
const {
|
|
51
|
+
edges,
|
|
52
|
+
leaders
|
|
53
|
+
} = routeGraph(ir, nodes, groups, areas, 2 * widestGutter);
|
|
48
54
|
return {
|
|
49
55
|
nodes,
|
|
50
56
|
groups,
|
|
51
|
-
edges
|
|
52
|
-
leaders
|
|
53
|
-
viewBox: viewBoxOf(nodes, groups)
|
|
57
|
+
edges,
|
|
58
|
+
leaders,
|
|
59
|
+
viewBox: viewBoxOf(nodes, groups, edges, leaders)
|
|
54
60
|
};
|
|
55
61
|
}
|
|
56
62
|
function areaBox(area, lines) {
|
|
@@ -108,7 +114,8 @@ function placeGroup({
|
|
|
108
114
|
depth,
|
|
109
115
|
rect,
|
|
110
116
|
...title.place(rect),
|
|
111
|
-
dividers: group.dividers ? dividers(rect, area, title, group.labelSide, lines, gutters) : []
|
|
117
|
+
dividers: group.dividers ? dividers(rect, area, title, group.labelSide, lines, gutters) : [],
|
|
118
|
+
ports: placeGroupPorts(group.ports ?? [], rect)
|
|
112
119
|
};
|
|
113
120
|
}
|
|
114
121
|
|
|
@@ -141,8 +148,23 @@ function dividers(rect, area, title, labelSide, lines, gutters) {
|
|
|
141
148
|
}];
|
|
142
149
|
})];
|
|
143
150
|
}
|
|
144
|
-
function viewBoxOf(nodes, groups) {
|
|
145
|
-
const
|
|
151
|
+
function viewBoxOf(nodes, groups, edges, leaders) {
|
|
152
|
+
const pointBox = ({
|
|
153
|
+
x,
|
|
154
|
+
y
|
|
155
|
+
}) => ({
|
|
156
|
+
x,
|
|
157
|
+
y,
|
|
158
|
+
w: 0,
|
|
159
|
+
h: 0
|
|
160
|
+
});
|
|
161
|
+
const boxes = [...nodes.map(nodeLayout => nodeLayout.visual), ...groups.map(groupLayout => groupLayout.rect), ...edges.flatMap(({
|
|
162
|
+
points,
|
|
163
|
+
label,
|
|
164
|
+
badge
|
|
165
|
+
}) => [...points.map(pointBox), ...(label ? [label.box] : []), ...(badge ? [badge.box] : [])]), ...leaders.flatMap(({
|
|
166
|
+
points
|
|
167
|
+
}) => points.map(pointBox))];
|
|
146
168
|
const pad = LAYOUT_TOKENS.CONTENT_PAD;
|
|
147
169
|
if (boxes.length === 0) return {
|
|
148
170
|
x: -pad,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.js","names":["gridAreas","gridGutters","measureGroupTitle","measureNode","LAYOUT_TOKENS","sizeTracks","layoutGraph","ir","measured","Map","nodes","map","node","id","titles","groups","group","areas","gutters","title","height","edges","items","axis","area","get","frame","overhang","start","row","span","rowSpan","size","h","top","bottom","col","colSpan","w","left","right","line","count","gutter","lines","rows","cols","measuredNode","areaBox","place","nodeFrame","groupArea","placeGroup","leaders","viewBox","viewBoxOf","x","trackStart","y","trackEnd","stretch","visualW","visualH","depth","border","boundary","side","gutterStart","rect","dividers","labelSide","centre","titleAtBottom","inner","Array","from","length","_","index","boxes","nodeLayout","visual","groupLayout","pad","CONTENT_PAD","Math","min","box","max"],"sources":["../../../../src/lib/layouts/graph/layout.ts"],"sourcesContent":["import type { Box, DiagramGraphIR, Point } from '../../model/diagram-ir.ts';\nimport { type CellArea, type GroupArea, gridAreas } from './areas.ts';\nimport type {\n GraphLayout,\n GroupLayout,\n NodeLayout,\n} from './graph-layout-result.ts';\nimport { type Axis, type GridGutters, gridGutters } from './gutters.ts';\nimport {\n type MeasuredGroupTitle,\n type MeasuredNode,\n measureGroupTitle,\n measureNode,\n} from './measure.ts';\nimport { LAYOUT_TOKENS } from './tokens.ts';\nimport { type TrackLine, sizeTracks } from './tracks.ts';\n\n/**\n * Lays out a graph on a content-sized grid: it measures every node, sizes\n * each row and column to the content in it, and reserves the chrome of the\n * groups in the gutters between them. It throws when the grid cannot be\n * drawn without overlap.\n */\nexport function layoutGraph(ir: DiagramGraphIR): GraphLayout {\n const measured = new Map(\n ir.nodes.map(\n (node) => [node.id, measureNode(node, LAYOUT_TOKENS)] as const,\n ),\n );\n const titles = new Map(\n ir.groups.map(\n (group) => [group.id, measureGroupTitle(group, LAYOUT_TOKENS)] as const,\n ),\n );\n const areas = gridAreas(ir);\n const gutters = gridGutters(\n areas,\n new Map([...titles].map(([id, title]) => [id, title.height])),\n ir.edges,\n );\n\n const items = (axis: Axis) =>\n ir.nodes.map((node) => {\n const area = areas.nodes.get(node.id)!;\n const { frame, overhang } = measured.get(node.id)!;\n return axis === 'row'\n ? {\n start: area.row,\n span: area.rowSpan,\n size: frame.h + overhang.top + overhang.bottom,\n }\n : {\n start: area.col,\n span: area.colSpan,\n size: frame.w + overhang.left + overhang.right,\n };\n });\n const line = (axis: Axis, count: number) =>\n sizeTracks(\n count,\n items(axis),\n gutters[axis].map((gutter) => gutter.size),\n );\n const lines: Record<Axis, TrackLine> = {\n row: line('row', areas.rows),\n col: line('col', areas.cols),\n };\n\n const nodes: NodeLayout[] = ir.nodes.map((node) => {\n const measuredNode = measured.get(node.id)!;\n const area = areaBox(areas.nodes.get(node.id)!, lines);\n return { node, ...measuredNode.place(nodeFrame(measuredNode, area)) };\n });\n const groups = areas.groups.map((groupArea) =>\n placeGroup(groupArea, titles.get(groupArea.group.id)!, lines, gutters),\n );\n\n return {\n nodes,\n groups,\n edges: [],\n leaders: [],\n viewBox: viewBoxOf(nodes, groups),\n };\n}\n\nfunction areaBox(area: CellArea, lines: Record<Axis, TrackLine>): Box {\n const x = lines.col.trackStart(area.col);\n const y = lines.row.trackStart(area.row);\n return {\n x,\n y,\n w: lines.col.trackEnd(area.col + area.colSpan - 1) - x,\n h: lines.row.trackEnd(area.row + area.rowSpan - 1) - y,\n };\n}\n\n/** A stretching node fills its area; any other node keeps its measured frame, centred in the area. */\nfunction nodeFrame(measured: MeasuredNode, area: Box): Box {\n const { frame, overhang, stretch } = measured;\n if (stretch) {\n return {\n x: area.x + overhang.left,\n y: area.y + overhang.top,\n w: area.w - overhang.left - overhang.right,\n h: area.h - overhang.top - overhang.bottom,\n };\n }\n const visualW = frame.w + overhang.left + overhang.right;\n const visualH = frame.h + overhang.top + overhang.bottom;\n return {\n x: area.x + (area.w - visualW) / 2 + overhang.left,\n y: area.y + (area.h - visualH) / 2 + overhang.top,\n ...frame,\n };\n}\n\n/** Each side of a group lies on the outer edge of its chrome slot in the gutter where the group opens or closes. */\nfunction placeGroup(\n { group, depth, area }: GroupArea,\n title: MeasuredGroupTitle,\n lines: Record<Axis, TrackLine>,\n gutters: GridGutters,\n): GroupLayout {\n const border = (axis: Axis, boundary: number, side: 'opening' | 'closing') =>\n lines[axis].gutterStart(boundary) +\n gutters[axis][boundary]![side].get(depth)!;\n const x = border('col', area.col, 'opening');\n const y = border('row', area.row, 'opening');\n const rect = {\n x,\n y,\n w: border('col', area.col + area.colSpan, 'closing') - x,\n h: border('row', area.row + area.rowSpan, 'closing') - y,\n };\n return {\n group,\n depth,\n rect,\n ...title.place(rect),\n dividers: group.dividers\n ? dividers(rect, area, title, group.labelSide, lines, gutters)\n : [],\n };\n}\n\n/** A line through the middle of every gutter inside the group, clear of its title. */\nfunction dividers(\n rect: Box,\n area: CellArea,\n title: MeasuredGroupTitle,\n labelSide: 'top' | 'bottom' | undefined,\n lines: Record<Axis, TrackLine>,\n gutters: GridGutters,\n): [Point, Point][] {\n const centre = (axis: Axis, boundary: number) =>\n lines[axis].gutterStart(boundary) + gutters[axis][boundary]!.size / 2;\n const titleAtBottom = labelSide === 'bottom';\n const top = rect.y + (titleAtBottom ? 0 : title.height);\n const bottom = rect.y + rect.h - (titleAtBottom ? title.height : 0);\n const inner = (start: number, span: number) =>\n Array.from({ length: span - 1 }, (_, index) => start + index + 1);\n return [\n ...inner(area.col, area.colSpan).map((boundary): [Point, Point] => {\n const x = centre('col', boundary);\n return [\n { x, y: top },\n { x, y: bottom },\n ];\n }),\n ...inner(area.row, area.rowSpan).map((boundary): [Point, Point] => {\n const y = centre('row', boundary);\n return [\n { x: rect.x, y },\n { x: rect.x + rect.w, y },\n ];\n }),\n ];\n}\n\nfunction viewBoxOf(nodes: NodeLayout[], groups: GroupLayout[]): Box {\n const boxes = [\n ...nodes.map((nodeLayout) => nodeLayout.visual),\n ...groups.map((groupLayout) => groupLayout.rect),\n ];\n const pad = LAYOUT_TOKENS.CONTENT_PAD;\n if (boxes.length === 0) return { x: -pad, y: -pad, w: 2 * pad, h: 2 * pad };\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 {\n x: left - pad,\n y: top - pad,\n w: right - left + 2 * pad,\n h: bottom - top + 2 * pad,\n };\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"layout.js","names":["gridAreas","gridGutters","measureGroupTitle","measureNode","placeGroupPorts","routeGraph","LAYOUT_TOKENS","sizeTracks","layoutGraph","ir","measured","Map","nodes","map","node","id","titles","groups","group","areas","gutters","title","height","edges","items","axis","area","get","frame","overhang","start","row","span","rowSpan","size","h","top","bottom","col","colSpan","w","left","right","line","count","gutter","lines","rows","cols","measuredNode","areaBox","place","nodeFrame","groupArea","placeGroup","widestGutter","Math","max","leaders","viewBox","viewBoxOf","x","trackStart","y","trackEnd","stretch","visualW","visualH","depth","border","boundary","side","gutterStart","rect","dividers","labelSide","ports","centre","titleAtBottom","inner","Array","from","length","_","index","pointBox","boxes","nodeLayout","visual","groupLayout","flatMap","points","label","badge","box","pad","CONTENT_PAD","min"],"sources":["../../../../src/lib/layouts/graph/layout.ts"],"sourcesContent":["import type { Box, DiagramGraphIR, Point } from '../../model/diagram-ir.ts';\nimport { type CellArea, type GroupArea, gridAreas } from './areas.ts';\nimport type {\n EdgeLayout,\n GraphLayout,\n GroupLayout,\n LeaderLayout,\n NodeLayout,\n} from './graph-layout-result.ts';\nimport { type Axis, type GridGutters, gridGutters } from './gutters.ts';\nimport {\n type MeasuredGroupTitle,\n type MeasuredNode,\n measureGroupTitle,\n measureNode,\n placeGroupPorts,\n} from './measure.ts';\nimport { routeGraph } from './route.ts';\nimport { LAYOUT_TOKENS } from './tokens.ts';\nimport { type TrackLine, sizeTracks } from './tracks.ts';\n\n/**\n * Lays out a graph on a content-sized grid: it measures every node, sizes\n * each row and column to the content in it, and reserves the chrome of the\n * groups in the gutters between them. It throws when the grid cannot be\n * drawn without overlap.\n */\nexport function layoutGraph(ir: DiagramGraphIR): GraphLayout {\n const measured = new Map(\n ir.nodes.map((node) => [node.id, measureNode(node)] as const),\n );\n const titles = new Map(\n ir.groups.map((group) => [group.id, measureGroupTitle(group)] as const),\n );\n const areas = gridAreas(ir);\n const gutters = gridGutters(\n areas,\n new Map([...titles].map(([id, title]) => [id, title.height])),\n ir.edges,\n );\n\n const items = (axis: Axis) =>\n ir.nodes.map((node) => {\n const area = areas.nodes.get(node.id)!;\n const { frame, overhang } = measured.get(node.id)!;\n return axis === 'row'\n ? {\n start: area.row,\n span: area.rowSpan,\n size: frame.h + overhang.top + overhang.bottom,\n }\n : {\n start: area.col,\n span: area.colSpan,\n size: frame.w + overhang.left + overhang.right,\n };\n });\n const line = (axis: Axis, count: number) =>\n sizeTracks(\n count,\n items(axis),\n gutters[axis].map((gutter) => gutter.size),\n );\n const lines: Record<Axis, TrackLine> = {\n row: line('row', areas.rows),\n col: line('col', areas.cols),\n };\n\n const nodes: NodeLayout[] = ir.nodes.map((node) => {\n const measuredNode = measured.get(node.id)!;\n const area = areaBox(areas.nodes.get(node.id)!, lines);\n return { node, ...measuredNode.place(nodeFrame(measuredNode, area)) };\n });\n const groups = areas.groups.map((groupArea) =>\n placeGroup(groupArea, titles.get(groupArea.group.id)!, lines, gutters),\n );\n\n const widestGutter = Math.max(\n ...[...gutters.row, ...gutters.col].map((gutter) => gutter.size),\n );\n const { edges, leaders } = routeGraph(\n ir,\n nodes,\n groups,\n areas,\n 2 * widestGutter,\n );\n\n return {\n nodes,\n groups,\n edges,\n leaders,\n viewBox: viewBoxOf(nodes, groups, edges, leaders),\n };\n}\n\nfunction areaBox(area: CellArea, lines: Record<Axis, TrackLine>): Box {\n const x = lines.col.trackStart(area.col);\n const y = lines.row.trackStart(area.row);\n return {\n x,\n y,\n w: lines.col.trackEnd(area.col + area.colSpan - 1) - x,\n h: lines.row.trackEnd(area.row + area.rowSpan - 1) - y,\n };\n}\n\n/** A stretching node fills its area; any other node keeps its measured frame, centred in the area. */\nfunction nodeFrame(measured: MeasuredNode, area: Box): Box {\n const { frame, overhang, stretch } = measured;\n if (stretch) {\n return {\n x: area.x + overhang.left,\n y: area.y + overhang.top,\n w: area.w - overhang.left - overhang.right,\n h: area.h - overhang.top - overhang.bottom,\n };\n }\n const visualW = frame.w + overhang.left + overhang.right;\n const visualH = frame.h + overhang.top + overhang.bottom;\n return {\n x: area.x + (area.w - visualW) / 2 + overhang.left,\n y: area.y + (area.h - visualH) / 2 + overhang.top,\n ...frame,\n };\n}\n\n/** Each side of a group lies on the outer edge of its chrome slot in the gutter where the group opens or closes. */\nfunction placeGroup(\n { group, depth, area }: GroupArea,\n title: MeasuredGroupTitle,\n lines: Record<Axis, TrackLine>,\n gutters: GridGutters,\n): GroupLayout {\n const border = (axis: Axis, boundary: number, side: 'opening' | 'closing') =>\n lines[axis].gutterStart(boundary) +\n gutters[axis][boundary]![side].get(depth)!;\n const x = border('col', area.col, 'opening');\n const y = border('row', area.row, 'opening');\n const rect = {\n x,\n y,\n w: border('col', area.col + area.colSpan, 'closing') - x,\n h: border('row', area.row + area.rowSpan, 'closing') - y,\n };\n return {\n group,\n depth,\n rect,\n ...title.place(rect),\n dividers: group.dividers\n ? dividers(rect, area, title, group.labelSide, lines, gutters)\n : [],\n ports: placeGroupPorts(group.ports ?? [], rect),\n };\n}\n\n/** A line through the middle of every gutter inside the group, clear of its title. */\nfunction dividers(\n rect: Box,\n area: CellArea,\n title: MeasuredGroupTitle,\n labelSide: 'top' | 'bottom' | undefined,\n lines: Record<Axis, TrackLine>,\n gutters: GridGutters,\n): [Point, Point][] {\n const centre = (axis: Axis, boundary: number) =>\n lines[axis].gutterStart(boundary) + gutters[axis][boundary]!.size / 2;\n const titleAtBottom = labelSide === 'bottom';\n const top = rect.y + (titleAtBottom ? 0 : title.height);\n const bottom = rect.y + rect.h - (titleAtBottom ? title.height : 0);\n const inner = (start: number, span: number) =>\n Array.from({ length: span - 1 }, (_, index) => start + index + 1);\n return [\n ...inner(area.col, area.colSpan).map((boundary): [Point, Point] => {\n const x = centre('col', boundary);\n return [\n { x, y: top },\n { x, y: bottom },\n ];\n }),\n ...inner(area.row, area.rowSpan).map((boundary): [Point, Point] => {\n const y = centre('row', boundary);\n return [\n { x: rect.x, y },\n { x: rect.x + rect.w, y },\n ];\n }),\n ];\n}\n\nfunction viewBoxOf(\n nodes: NodeLayout[],\n groups: GroupLayout[],\n edges: EdgeLayout[],\n leaders: LeaderLayout[],\n): Box {\n const pointBox = ({ x, y }: Point): Box => ({ x, y, w: 0, h: 0 });\n const boxes = [\n ...nodes.map((nodeLayout) => nodeLayout.visual),\n ...groups.map((groupLayout) => groupLayout.rect),\n ...edges.flatMap(({ points, label, badge }) => [\n ...points.map(pointBox),\n ...(label ? [label.box] : []),\n ...(badge ? [badge.box] : []),\n ]),\n ...leaders.flatMap(({ points }) => points.map(pointBox)),\n ];\n const pad = LAYOUT_TOKENS.CONTENT_PAD;\n if (boxes.length === 0) return { x: -pad, y: -pad, w: 2 * pad, h: 2 * pad };\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 {\n x: left - pad,\n y: top - pad,\n w: right - left + 2 * pad,\n h: bottom - top + 2 * pad,\n };\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { Box, DiagramEdge, DiagramGroup, DiagramNode, DiagramSide } from '../../model/diagram-ir.ts';
|
|
1
|
+
import type { Box, DiagramEdge, DiagramGroup, DiagramNode, DiagramNodePort, DiagramSide } from '../../model/diagram-ir.ts';
|
|
2
2
|
import type { NodeLayout, TextRun } from './graph-layout-result.ts';
|
|
3
|
-
import type { LayoutTokens } from './tokens.ts';
|
|
4
3
|
type Sides = Record<DiagramSide, number>;
|
|
5
4
|
export type MeasuredNode = {
|
|
6
5
|
/** The smallest frame that holds the node's content. */
|
|
@@ -15,7 +14,9 @@ export type MeasuredNode = {
|
|
|
15
14
|
/** Positions everything the node draws, given its final frame (at least `frame`). */
|
|
16
15
|
place(box: Box): Omit<NodeLayout, 'node'>;
|
|
17
16
|
};
|
|
18
|
-
export declare function measureNode(node: DiagramNode
|
|
17
|
+
export declare function measureNode(node: DiagramNode): MeasuredNode;
|
|
18
|
+
/** A group's ports sit evenly along their side of its rect, like a node's ports along its frame. */
|
|
19
|
+
export declare function placeGroupPorts(ports: DiagramNodePort[], rect: Box): NodeLayout['ports'];
|
|
19
20
|
export type MeasuredGroupTitle = {
|
|
20
21
|
/** The height the title reserves on its side, BAND_PAD above and below included; 0 without a label. */
|
|
21
22
|
height: number;
|
|
@@ -24,13 +25,13 @@ export type MeasuredGroupTitle = {
|
|
|
24
25
|
label: TextRun[];
|
|
25
26
|
};
|
|
26
27
|
};
|
|
27
|
-
export declare function measureGroupTitle(group: DiagramGroup
|
|
28
|
+
export declare function measureGroupTitle(group: DiagramGroup): MeasuredGroupTitle;
|
|
28
29
|
export type MeasuredEdgeLabel = {
|
|
29
30
|
w: number;
|
|
30
31
|
h: number;
|
|
31
32
|
place(box: Box): TextRun[];
|
|
32
33
|
};
|
|
33
|
-
export declare function measureEdgeLabel(edge: DiagramEdge
|
|
34
|
+
export declare function measureEdgeLabel(edge: DiagramEdge): MeasuredEdgeLabel;
|
|
34
35
|
export type MeasuredEdgeBadge = {
|
|
35
36
|
w: number;
|
|
36
37
|
h: number;
|
|
@@ -39,6 +40,6 @@ export type MeasuredEdgeBadge = {
|
|
|
39
40
|
text: TextRun[];
|
|
40
41
|
};
|
|
41
42
|
};
|
|
42
|
-
export declare function measureEdgeBadge(edge: DiagramEdge
|
|
43
|
+
export declare function measureEdgeBadge(edge: DiagramEdge): MeasuredEdgeBadge;
|
|
43
44
|
export {};
|
|
44
45
|
//# sourceMappingURL=measure.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"measure.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/measure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,GAAG,EACH,WAAW,EACX,YAAY,EACZ,WAAW,
|
|
1
|
+
{"version":3,"file":"measure.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/measure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,GAAG,EACH,WAAW,EACX,YAAY,EACZ,WAAW,EACX,eAAe,EACf,WAAW,EACZ,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAKpE,KAAK,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;AAEzC,MAAM,MAAM,YAAY,GAAG;IACzB,wDAAwD;IACxD,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAChC,0EAA0E;IAC1E,QAAQ,EAAE,KAAK,CAAC;IAChB,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;CAC3C,CAAC;AAWF,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,YAAY,CAiD3D;AAsGD,oGAAoG;AACpG,wBAAgB,eAAe,CAC7B,KAAK,EAAE,eAAe,EAAE,EACxB,IAAI,EAAE,GAAG,GACR,UAAU,CAAC,OAAO,CAAC,CAErB;AAsGD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,uGAAuG;IACvG,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,IAAI,EAAE,GAAG,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC;CACpD,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,GAAG,kBAAkB,CA2BzE;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,EAAE,CAAC;CAC5B,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,iBAAiB,CAWrE;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,IAAI,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC;CAClD,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,iBAAiB,CAoBrE"}
|
|
@@ -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":[]}
|