@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.
Files changed (49) hide show
  1. package/dist/lib/layouts/graph/attach.d.ts +25 -0
  2. package/dist/lib/layouts/graph/attach.d.ts.map +1 -0
  3. package/dist/lib/layouts/graph/attach.js +216 -0
  4. package/dist/lib/layouts/graph/attach.js.map +1 -0
  5. package/dist/lib/layouts/graph/connections.d.ts +38 -0
  6. package/dist/lib/layouts/graph/connections.d.ts.map +1 -0
  7. package/dist/lib/layouts/graph/connections.js +112 -0
  8. package/dist/lib/layouts/graph/connections.js.map +1 -0
  9. package/dist/lib/layouts/graph/graph-layout-result.d.ts +1 -1
  10. package/dist/lib/layouts/graph/graph-layout-result.d.ts.map +1 -1
  11. package/dist/lib/layouts/graph/graph-layout-result.js.map +1 -1
  12. package/dist/lib/layouts/graph/gutters.js +5 -5
  13. package/dist/lib/layouts/graph/gutters.js.map +1 -1
  14. package/dist/lib/layouts/graph/labels.d.ts +16 -0
  15. package/dist/lib/layouts/graph/labels.d.ts.map +1 -0
  16. package/dist/lib/layouts/graph/labels.js +76 -0
  17. package/dist/lib/layouts/graph/labels.js.map +1 -0
  18. package/dist/lib/layouts/graph/layout.d.ts.map +1 -1
  19. package/dist/lib/layouts/graph/layout.js +31 -9
  20. package/dist/lib/layouts/graph/layout.js.map +1 -1
  21. package/dist/lib/layouts/graph/measure.d.ts +7 -6
  22. package/dist/lib/layouts/graph/measure.d.ts.map +1 -1
  23. package/dist/lib/layouts/graph/measure.js +35 -29
  24. package/dist/lib/layouts/graph/measure.js.map +1 -1
  25. package/dist/lib/layouts/graph/min-heap.d.ts +12 -0
  26. package/dist/lib/layouts/graph/min-heap.d.ts.map +1 -0
  27. package/dist/lib/layouts/graph/min-heap.js +60 -0
  28. package/dist/lib/layouts/graph/min-heap.js.map +1 -0
  29. package/dist/lib/layouts/graph/node-content.d.ts +1 -2
  30. package/dist/lib/layouts/graph/node-content.d.ts.map +1 -1
  31. package/dist/lib/layouts/graph/node-content.js +43 -42
  32. package/dist/lib/layouts/graph/node-content.js.map +1 -1
  33. package/dist/lib/layouts/graph/path.d.ts +14 -0
  34. package/dist/lib/layouts/graph/path.d.ts.map +1 -0
  35. package/dist/lib/layouts/graph/path.js +55 -0
  36. package/dist/lib/layouts/graph/path.js.map +1 -0
  37. package/dist/lib/layouts/graph/route-search.d.ts +34 -0
  38. package/dist/lib/layouts/graph/route-search.d.ts.map +1 -0
  39. package/dist/lib/layouts/graph/route-search.js +208 -0
  40. package/dist/lib/layouts/graph/route-search.js.map +1 -0
  41. package/dist/lib/layouts/graph/route.d.ts +16 -0
  42. package/dist/lib/layouts/graph/route.d.ts.map +1 -0
  43. package/dist/lib/layouts/graph/route.js +241 -0
  44. package/dist/lib/layouts/graph/route.js.map +1 -0
  45. package/dist/lib/layouts/graph/sides.d.ts +23 -0
  46. package/dist/lib/layouts/graph/sides.d.ts.map +1 -0
  47. package/dist/lib/layouts/graph/sides.js +91 -0
  48. package/dist/lib/layouts/graph/sides.js.map +1 -0
  49. 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,EACV,WAAW,EAGZ,MAAM,0BAA0B,CAAC;AAWlC;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,GAAG,WAAW,CA6D3D"}
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, LAYOUT_TOKENS)]));
15
- const titles = new Map(ir.groups.map(group => [group.id, measureGroupTitle(group, LAYOUT_TOKENS)]));
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 boxes = [...nodes.map(nodeLayout => nodeLayout.visual), ...groups.map(groupLayout => groupLayout.rect)];
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, tokens: LayoutTokens): MeasuredNode;
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, tokens: LayoutTokens): MeasuredGroupTitle;
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, tokens: LayoutTokens): MeasuredEdgeLabel;
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, tokens: LayoutTokens): MeasuredEdgeBadge;
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,EAEX,WAAW,EACZ,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAGpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,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,CACzB,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,YAAY,GACnB,YAAY,CAiDd;AA6MD,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,CAC/B,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,YAAY,GACnB,kBAAkB,CAwBpB;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,CAC9B,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,YAAY,GACnB,iBAAiB,CAQnB;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,CAC9B,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,YAAY,GACnB,iBAAiB,CAiBnB"}
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, tokens) {
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, tokens);
17
- const ports = portGeometry(node.ports ?? [], tokens);
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, tokens);
28
- const caption = placeCaption(node, union([box, ...frames]), tokens);
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, tokens),
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, tokens) {
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' ? tokens.FLAT_RADIUS : tokens.RADIUS;
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, tokens) {
110
- const font = tokens.fonts.portLabel;
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(tokens.PORT_SPACING, labelLength + tokens.LABEL_GAP);
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, tokens) {
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) * tokens.STACK_STEP.x;
202
- const dy = (direction.startsWith('up') ? -1 : 1) * tokens.STACK_STEP.y;
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, tokens) {
217
- const font = tokens.fonts.caption;
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 = tokens.CAPTION_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, tokens) {
276
- const lines = textBlock(labelLines(group.label), tokens.fonts.groupLabel);
277
- const height = lines.h > 0 ? lines.h + 2 * tokens.BAND_PAD : 0;
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 + tokens.BAND_PAD;
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 + tokens.PAD_X, top, 'start');
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 - tokens.PAD_X, top, 'end');
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, tokens) {
310
- const lines = textBlock(labelLines(edge.label), tokens.fonts.edgeLabel);
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, tokens) {
319
- const lines = textBlock(labelLines(edge.badgeLabel), tokens.fonts.badge);
320
- const icon = edge.badgeIcon ? tokens.BADGE_ICON : 0;
321
- const gap = icon > 0 && lines.w > 0 ? tokens.BLOCK_GAP : 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":[]}