@zukhruf/diagram 0.3.32 → 0.3.34

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 (122) hide show
  1. package/README.md +98 -52
  2. package/dist/index.d.ts +5 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/compiler/diagram-compile.d.ts +3 -4
  7. package/dist/lib/compiler/diagram-compile.d.ts.map +1 -1
  8. package/dist/lib/compiler/diagram-compile.js +85 -370
  9. package/dist/lib/compiler/diagram-compile.js.map +1 -1
  10. package/dist/lib/layouts/graph/attach.d.ts +25 -0
  11. package/dist/lib/layouts/graph/attach.d.ts.map +1 -0
  12. package/dist/lib/layouts/graph/attach.js +272 -0
  13. package/dist/lib/layouts/graph/attach.js.map +1 -0
  14. package/dist/lib/layouts/graph/connections.d.ts +45 -0
  15. package/dist/lib/layouts/graph/connections.d.ts.map +1 -0
  16. package/dist/lib/layouts/graph/connections.js +138 -0
  17. package/dist/lib/layouts/graph/connections.js.map +1 -0
  18. package/dist/lib/layouts/graph/graph-layout-result.d.ts +5 -3
  19. package/dist/lib/layouts/graph/graph-layout-result.d.ts.map +1 -1
  20. package/dist/lib/layouts/graph/graph-layout-result.js.map +1 -1
  21. package/dist/lib/layouts/graph/gutters.js +41 -22
  22. package/dist/lib/layouts/graph/gutters.js.map +1 -1
  23. package/dist/lib/layouts/graph/labels.d.ts +23 -0
  24. package/dist/lib/layouts/graph/labels.d.ts.map +1 -0
  25. package/dist/lib/layouts/graph/labels.js +120 -0
  26. package/dist/lib/layouts/graph/labels.js.map +1 -0
  27. package/dist/lib/layouts/graph/layout.d.ts +1 -1
  28. package/dist/lib/layouts/graph/layout.d.ts.map +1 -1
  29. package/dist/lib/layouts/graph/layout.js +37 -12
  30. package/dist/lib/layouts/graph/layout.js.map +1 -1
  31. package/dist/lib/layouts/graph/measure.d.ts +15 -11
  32. package/dist/lib/layouts/graph/measure.d.ts.map +1 -1
  33. package/dist/lib/layouts/graph/measure.js +82 -38
  34. package/dist/lib/layouts/graph/measure.js.map +1 -1
  35. package/dist/lib/layouts/graph/min-heap.d.ts +8 -0
  36. package/dist/lib/layouts/graph/min-heap.d.ts.map +1 -0
  37. package/dist/lib/layouts/graph/min-heap.js +56 -0
  38. package/dist/lib/layouts/graph/min-heap.js.map +1 -0
  39. package/dist/lib/layouts/graph/node-content.d.ts +2 -3
  40. package/dist/lib/layouts/graph/node-content.d.ts.map +1 -1
  41. package/dist/lib/layouts/graph/node-content.js +67 -47
  42. package/dist/lib/layouts/graph/node-content.js.map +1 -1
  43. package/dist/lib/layouts/graph/path.d.ts +6 -0
  44. package/dist/lib/layouts/graph/path.d.ts.map +1 -0
  45. package/dist/lib/layouts/graph/path.js +53 -0
  46. package/dist/lib/layouts/graph/path.js.map +1 -0
  47. package/dist/lib/layouts/graph/round.d.ts +11 -0
  48. package/dist/lib/layouts/graph/round.d.ts.map +1 -0
  49. package/dist/lib/layouts/graph/round.js +181 -0
  50. package/dist/lib/layouts/graph/round.js.map +1 -0
  51. package/dist/lib/layouts/graph/route-search.d.ts +34 -0
  52. package/dist/lib/layouts/graph/route-search.d.ts.map +1 -0
  53. package/dist/lib/layouts/graph/route-search.js +208 -0
  54. package/dist/lib/layouts/graph/route-search.js.map +1 -0
  55. package/dist/lib/layouts/graph/route.d.ts +20 -0
  56. package/dist/lib/layouts/graph/route.d.ts.map +1 -0
  57. package/dist/lib/layouts/graph/route.js +276 -0
  58. package/dist/lib/layouts/graph/route.js.map +1 -0
  59. package/dist/lib/layouts/graph/sides.d.ts +28 -0
  60. package/dist/lib/layouts/graph/sides.d.ts.map +1 -0
  61. package/dist/lib/layouts/graph/sides.js +98 -0
  62. package/dist/lib/layouts/graph/sides.js.map +1 -0
  63. package/dist/lib/layouts/timeline/timeline-layout.d.ts +2 -1
  64. package/dist/lib/layouts/timeline/timeline-layout.d.ts.map +1 -1
  65. package/dist/lib/layouts/timeline/timeline-layout.js +9 -4
  66. package/dist/lib/layouts/timeline/timeline-layout.js.map +1 -1
  67. package/dist/lib/model/diagram-ir.d.ts +19 -156
  68. package/dist/lib/model/diagram-ir.d.ts.map +1 -1
  69. package/dist/lib/model/diagram-ir.js.map +1 -1
  70. package/dist/lib/model/validate-graph.d.ts +7 -0
  71. package/dist/lib/model/validate-graph.d.ts.map +1 -0
  72. package/dist/lib/model/validate-graph.js +54 -0
  73. package/dist/lib/model/validate-graph.js.map +1 -0
  74. package/dist/lib/react/Diagram.d.ts +3 -9
  75. package/dist/lib/react/Diagram.d.ts.map +1 -1
  76. package/dist/lib/react/Diagram.js +1 -13
  77. package/dist/lib/react/Diagram.js.map +1 -1
  78. package/dist/lib/react/diagram-types.d.ts +17 -46
  79. package/dist/lib/react/diagram-types.d.ts.map +1 -1
  80. package/dist/lib/react/diagram-types.js.map +1 -1
  81. package/dist/lib/renderers/canvas/graph-canvas.d.ts +2 -5
  82. package/dist/lib/renderers/canvas/graph-canvas.d.ts.map +1 -1
  83. package/dist/lib/renderers/canvas/graph-canvas.js +68 -43
  84. package/dist/lib/renderers/canvas/graph-canvas.js.map +1 -1
  85. package/dist/lib/renderers/html/graph-html.d.ts +2 -3
  86. package/dist/lib/renderers/html/graph-html.d.ts.map +1 -1
  87. package/dist/lib/renderers/html/graph-html.js +298 -289
  88. package/dist/lib/renderers/html/graph-html.js.map +1 -1
  89. package/dist/lib/renderers/shared/graph-parts.d.ts +3 -1
  90. package/dist/lib/renderers/shared/graph-parts.d.ts.map +1 -1
  91. package/dist/lib/renderers/shared/graph-parts.js +350 -418
  92. package/dist/lib/renderers/shared/graph-parts.js.map +1 -1
  93. package/dist/lib/renderers/shared/node-parts.d.ts +5 -6
  94. package/dist/lib/renderers/shared/node-parts.d.ts.map +1 -1
  95. package/dist/lib/renderers/shared/node-parts.js +112 -190
  96. package/dist/lib/renderers/shared/node-parts.js.map +1 -1
  97. package/dist/lib/renderers/shared/run-spans.js +2 -2
  98. package/dist/lib/renderers/shared/run-spans.js.map +1 -1
  99. package/dist/lib/renderers/shared/text-runs.d.ts +0 -6
  100. package/dist/lib/renderers/shared/text-runs.d.ts.map +1 -1
  101. package/dist/lib/renderers/shared/text-runs.js +0 -12
  102. package/dist/lib/renderers/shared/text-runs.js.map +1 -1
  103. package/dist/lib/renderers/svg/graph-svg.d.ts +1 -3
  104. package/dist/lib/renderers/svg/graph-svg.d.ts.map +1 -1
  105. package/dist/lib/renderers/svg/graph-svg.js +66 -80
  106. package/dist/lib/renderers/svg/graph-svg.js.map +1 -1
  107. package/dist/lib/renderers/svg/timeline-svg.d.ts.map +1 -1
  108. package/dist/lib/renderers/svg/timeline-svg.js +7 -5
  109. package/dist/lib/renderers/svg/timeline-svg.js.map +1 -1
  110. package/package.json +1 -1
  111. package/dist/lib/layouts/graph/graph-layout.d.ts +0 -154
  112. package/dist/lib/layouts/graph/graph-layout.d.ts.map +0 -1
  113. package/dist/lib/layouts/graph/graph-layout.js +0 -2340
  114. package/dist/lib/layouts/graph/graph-layout.js.map +0 -1
  115. package/dist/lib/layouts/graph/legacy-graph-layout.d.ts +0 -6
  116. package/dist/lib/layouts/graph/legacy-graph-layout.d.ts.map +0 -1
  117. package/dist/lib/layouts/graph/legacy-graph-layout.js +0 -542
  118. package/dist/lib/layouts/graph/legacy-graph-layout.js.map +0 -1
  119. package/dist/lib/layouts/text-metrics.d.ts +0 -7
  120. package/dist/lib/layouts/text-metrics.d.ts.map +0 -1
  121. package/dist/lib/layouts/text-metrics.js +0 -40
  122. package/dist/lib/layouts/text-metrics.js.map +0 -1
@@ -0,0 +1,276 @@
1
+ import { attachConnections } from "./attach.js";
2
+ import { graphConnections } from "./connections.js";
3
+ import { tagPosition } from "./labels.js";
4
+ import { measureEdgeTags } from "./measure.js";
5
+ import { simplifyPath } from "./path.js";
6
+ import { searchRoute } from "./route-search.js";
7
+ import { OPPOSITE, OUTWARD } from "./sides.js";
8
+ import { LAYOUT_TOKENS } from "./tokens.js";
9
+
10
+ /** How far around the content a route may detour. */
11
+ const ROUTING_MARGIN = 64;
12
+ /** How far a route keeps from a node that it does not connect. */
13
+ const NODE_CLEARANCE = 8;
14
+
15
+ /** The routes, before the layout rounds them and draws their paths. */
16
+
17
+ /**
18
+ * Routes every edge and note leader orthogonally, nearest cells first, and
19
+ * places each edge's label and badge before the next edge routes around
20
+ * them. A connection with no route takes its next choice of sides, and
21
+ * everything routes again. It throws when a connection has no choice left,
22
+ * or a label has no room; it never draws a diagonal or straight line
23
+ * through what is in the way.
24
+ */
25
+ export function routeGraph(ir, nodes, groups, areas, /** How far beyond the bounding box of its two shapes a route first searches. */
26
+ searchMargin) {
27
+ const candidates = graphConnections(ir, nodes, groups, areas);
28
+ const choices = candidates.map(() => 0);
29
+ for (;;) {
30
+ const routed = routeConnections(ir, candidates.map((options, index) => options[choices[index]]), nodes, groups, searchMargin);
31
+ if (!('unrouted' in routed)) return routed;
32
+ const options = candidates[routed.unrouted];
33
+ const next = choices[routed.unrouted] + 1;
34
+ if (next === options.length) throw noRoute(options[0]);
35
+ choices[routed.unrouted] = next;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Routes one choice of sides for every connection, or names the first
41
+ * connection, in routing order, that has no route.
42
+ */
43
+ function routeConnections(ir, connections, nodes, groups, searchMargin) {
44
+ if (connections.length === 0) return {
45
+ edges: [],
46
+ leaders: []
47
+ };
48
+ const groupLabels = groups.flatMap(({
49
+ group,
50
+ label
51
+ }) => label.map(run => ({
52
+ group: group.id,
53
+ box: inflate(run.bounds, LAYOUT_TOKENS.LABEL_GAP)
54
+ })));
55
+ const attachments = attachConnections(connections, groupLabels);
56
+ const content = union([...nodes.map(nodeLayout => nodeLayout.visual), ...groups.map(groupLayout => groupLayout.rect)]);
57
+ const bounds = inflate(content, ROUTING_MARGIN);
58
+ const solid = nodes.filter(({
59
+ box
60
+ }) => box.w > 0 && box.h > 0);
61
+ const routed = [];
62
+ const tags = [];
63
+ const paths = new Map();
64
+ const labels = new Map();
65
+ const order = connections.map((connection, index) => ({
66
+ connection,
67
+ index
68
+ })).sort((a, b) => Number(a.connection.edge === undefined) - Number(b.connection.edge === undefined) || a.connection.cellDistance - b.connection.cellDistance || a.index - b.index);
69
+ for (const {
70
+ connection,
71
+ index
72
+ } of order) {
73
+ const {
74
+ from,
75
+ to
76
+ } = connection;
77
+ const ends = new Set([from.shape.id, to.shape.id]);
78
+ const junctions = new Set([from, to].filter(end => end.shape.kind === 'junction').map(end => end.shape.id));
79
+ const walls = [...solid.flatMap(({
80
+ node,
81
+ box,
82
+ visual,
83
+ frames,
84
+ caption
85
+ }) => {
86
+ if (!ends.has(node.id)) return [inflate(visual, NODE_CLEARANCE)];
87
+ if (node.kind === 'junction') return [];
88
+ // Its own parts, not their bounding box: a caption wider than the frame leaves room beside the frame.
89
+ return [box, ...frames, ...(caption ? [caption.box] : [])];
90
+ }), ...groupLabels.flatMap(({
91
+ group,
92
+ box
93
+ }) => ends.has(group) ? [] : [box]), ...tags.map(tag => inflate(tag, LAYOUT_TOKENS.LABEL_GAP))];
94
+ const runs = routed.filter(route => !overlaps(route.junctions, junctions)).flatMap(route => segments(route.points));
95
+ const {
96
+ start,
97
+ startStub,
98
+ end,
99
+ endStub
100
+ } = attachments[index];
101
+ const stubs = [[start, startStub], [endStub, end]];
102
+ const stubsClear = stubs.every(([a, b]) => walls.every(wall => !crosses(a, b, wall)));
103
+ const search = window => searchRoute({
104
+ window,
105
+ lines: {
106
+ x: [startStub.x, endStub.x],
107
+ y: [startStub.y, endStub.y]
108
+ },
109
+ walls,
110
+ runs,
111
+ borders: groups.flatMap(({
112
+ rect,
113
+ band
114
+ }) => band ? [rect, band] : [rect]),
115
+ start: startStub,
116
+ startHeading: OUTWARD[from.side],
117
+ goal: endStub,
118
+ goalHeading: OUTWARD[OPPOSITE[to.side]]
119
+ });
120
+ const near = intersection(inflate(union([from.shape.box, to.shape.box, spanOf(startStub, endStub)]), searchMargin), bounds);
121
+ const found = stubsClear ? search(near) ?? search(bounds) : undefined;
122
+ if (!found) return {
123
+ unrouted: index
124
+ };
125
+ const points = simplifyPath([start, ...found, end]);
126
+ paths.set(connection, points);
127
+ if (connection.edge !== undefined) {
128
+ const edge = ir.edges[connection.edge];
129
+ const isFree = (box, ownSegments) => solid.every(({
130
+ visual
131
+ }) => !intersects(box, visual)) && groups.every(({
132
+ rect,
133
+ band,
134
+ label
135
+ }) => !(band && intersects(box, band)) && label.every(run => !intersects(box, run.bounds)) && !(intersects(box, rect) && !contains(rect, box))) && tags.every(tag => !intersects(box, tag)) && [...routed.flatMap(route => segments(route.points)), ...(ownSegments ? segments(points) : [])].every(([a, b]) => !crosses(a, b, box));
136
+ const measured = measureEdgeTags(edge);
137
+ const {
138
+ label,
139
+ badge
140
+ } = measured;
141
+ const placed = {};
142
+ if (label || badge) {
143
+ const boxes = tagPosition(measured, points, (candidate, kind) => isFree(candidate, kind === 'label'));
144
+ if (!boxes) {
145
+ const named = [];
146
+ if (label) {
147
+ named.push(`label "${edge.label.join(' / ')}" (${Math.ceil(label.w)}×${Math.ceil(label.h)})`);
148
+ }
149
+ if (badge) {
150
+ named.push(`badge "${(edge.badgeLabel ?? []).join(' / ')}" (${Math.ceil(badge.w)}×${Math.ceil(badge.h)})`);
151
+ }
152
+ throw new Error(`Diagram ${connection.name}: ${named.join(' and ')} ${named.length > 1 ? 'have' : 'has'} no free position ${badge ? 'on' : 'beside'} its route.`);
153
+ }
154
+ if (label && boxes.label) {
155
+ placed.label = {
156
+ box: boxes.label,
157
+ text: label.place(boxes.label)
158
+ };
159
+ tags.push(boxes.label);
160
+ }
161
+ if (badge && boxes.badge) {
162
+ placed.badge = {
163
+ box: boxes.badge,
164
+ ...badge.place(boxes.badge)
165
+ };
166
+ tags.push(boxes.badge);
167
+ }
168
+ }
169
+ labels.set(connection, placed);
170
+ }
171
+ routed.push({
172
+ points,
173
+ junctions
174
+ });
175
+ }
176
+ const edges = [];
177
+ const leaders = [];
178
+ for (const connection of connections) {
179
+ const points = paths.get(connection);
180
+ if (connection.edge === undefined) {
181
+ leaders.push({
182
+ note: connection.from.shape.id,
183
+ target: connection.to.shape.id,
184
+ points
185
+ });
186
+ continue;
187
+ }
188
+ edges.push({
189
+ edge: ir.edges[connection.edge],
190
+ exitSide: connection.from.side,
191
+ entrySide: connection.to.side,
192
+ points,
193
+ ...labels.get(connection)
194
+ });
195
+ }
196
+ return {
197
+ edges,
198
+ leaders
199
+ };
200
+ }
201
+ function noRoute({
202
+ name,
203
+ edge,
204
+ from,
205
+ to
206
+ }) {
207
+ const advice = edge === undefined ? 'Move the note or its target.' : 'Change exitSide/entrySide or move a node.';
208
+ return new Error(`Diagram ${name}: no orthogonal route from ${endName(from)} to ${endName(to)} avoids the nodes and labels. ${advice}`);
209
+ }
210
+ function endName({
211
+ shape,
212
+ side,
213
+ port
214
+ }) {
215
+ if (port) return `port "${shape.id}:${port.id}"`;
216
+ if (shape.kind === 'junction') return `the centre of "${shape.id}"`;
217
+ return `${side} of "${shape.id}"`;
218
+ }
219
+ function segments(points) {
220
+ return points.slice(1).map((point, index) => [points[index], point]);
221
+ }
222
+ function overlaps(a, b) {
223
+ for (const id of a) if (b.has(id)) return true;
224
+ return false;
225
+ }
226
+ function spanOf(a, b) {
227
+ return {
228
+ x: Math.min(a.x, b.x),
229
+ y: Math.min(a.y, b.y),
230
+ w: Math.abs(a.x - b.x),
231
+ h: Math.abs(a.y - b.y)
232
+ };
233
+ }
234
+
235
+ /** Whether the segment enters the box's interior, not only its border. */
236
+ function crosses(a, b, box) {
237
+ const span = spanOf(a, b);
238
+ return span.x + span.w > box.x && span.x < box.x + box.w && span.y + span.h > box.y && span.y < box.y + box.h;
239
+ }
240
+ function intersects(a, b) {
241
+ return a.x < b.x + b.w && b.x < a.x + a.w && a.y < b.y + b.h && b.y < a.y + a.h;
242
+ }
243
+ function contains(outer, inner) {
244
+ return inner.x >= outer.x && inner.y >= outer.y && inner.x + inner.w <= outer.x + outer.w && inner.y + inner.h <= outer.y + outer.h;
245
+ }
246
+ function inflate(box, by) {
247
+ return {
248
+ x: box.x - by,
249
+ y: box.y - by,
250
+ w: box.w + 2 * by,
251
+ h: box.h + 2 * by
252
+ };
253
+ }
254
+ function intersection(a, b) {
255
+ const x = Math.max(a.x, b.x);
256
+ const y = Math.max(a.y, b.y);
257
+ return {
258
+ x,
259
+ y,
260
+ w: Math.min(a.x + a.w, b.x + b.w) - x,
261
+ h: Math.min(a.y + a.h, b.y + b.h) - y
262
+ };
263
+ }
264
+ function union(boxes) {
265
+ const left = Math.min(...boxes.map(box => box.x));
266
+ const top = Math.min(...boxes.map(box => box.y));
267
+ const right = Math.max(...boxes.map(box => box.x + box.w));
268
+ const bottom = Math.max(...boxes.map(box => box.y + box.h));
269
+ return {
270
+ x: left,
271
+ y: top,
272
+ w: right - left,
273
+ h: bottom - top
274
+ };
275
+ }
276
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.js","names":["attachConnections","graphConnections","tagPosition","measureEdgeTags","simplifyPath","searchRoute","OPPOSITE","OUTWARD","LAYOUT_TOKENS","ROUTING_MARGIN","NODE_CLEARANCE","routeGraph","ir","nodes","groups","areas","searchMargin","candidates","choices","map","routed","routeConnections","options","index","unrouted","next","length","noRoute","connections","edges","leaders","groupLabels","flatMap","group","label","run","id","box","inflate","bounds","LABEL_GAP","attachments","content","union","nodeLayout","visual","groupLayout","rect","solid","filter","w","h","tags","paths","Map","labels","order","connection","sort","a","b","Number","edge","undefined","cellDistance","from","to","ends","Set","shape","junctions","end","kind","walls","node","frames","caption","has","tag","runs","route","overlaps","segments","points","start","startStub","endStub","stubs","stubsClear","every","wall","crosses","search","window","lines","x","y","borders","band","startHeading","side","goal","goalHeading","near","intersection","spanOf","found","set","isFree","ownSegments","intersects","contains","measured","badge","placed","boxes","candidate","named","push","join","Math","ceil","badgeLabel","Error","name","text","place","get","note","target","exitSide","entrySide","advice","endName","port","slice","point","min","abs","span","outer","inner","by","max","left","top","right","bottom"],"sources":["../../../../src/lib/layouts/graph/route.ts"],"sourcesContent":["import type { Box, DiagramGraphIR, Point } from '../../model/diagram-ir.ts';\nimport type { GridAreas } from './areas.ts';\nimport { attachConnections } from './attach.ts';\nimport {\n type Connection,\n type ConnectionEnd,\n graphConnections,\n} from './connections.ts';\nimport type {\n EdgeLayout,\n GroupLayout,\n LeaderLayout,\n NodeLayout,\n} from './graph-layout-result.ts';\nimport { tagPosition } from './labels.ts';\nimport { measureEdgeTags } from './measure.ts';\nimport { simplifyPath } from './path.ts';\nimport { searchRoute } from './route-search.ts';\nimport { OPPOSITE, OUTWARD } from './sides.ts';\nimport { LAYOUT_TOKENS } from './tokens.ts';\n\n/** How far around the content a route may detour. */\nconst ROUTING_MARGIN = 64;\n/** How far a route keeps from a node that it does not connect. */\nconst NODE_CLEARANCE = 8;\n\n/** The routes, before the layout rounds them and draws their paths. */\nexport type RoutedGraph = {\n edges: Omit<EdgeLayout, 'd'>[];\n leaders: Omit<LeaderLayout, 'd'>[];\n};\n\ntype Routed = {\n points: Point[];\n /** The junctions the route ends at: routes that share one may run together into it. */\n junctions: Set<string>;\n};\n\n/**\n * Routes every edge and note leader orthogonally, nearest cells first, and\n * places each edge's label and badge before the next edge routes around\n * them. A connection with no route takes its next choice of sides, and\n * everything routes again. It throws when a connection has no choice left,\n * or a label has no room; it never draws a diagonal or straight line\n * through what is in the way.\n */\nexport function routeGraph(\n ir: DiagramGraphIR,\n nodes: NodeLayout[],\n groups: GroupLayout[],\n areas: GridAreas,\n /** How far beyond the bounding box of its two shapes a route first searches. */\n searchMargin: number,\n): RoutedGraph {\n const candidates = graphConnections(ir, nodes, groups, areas);\n const choices = candidates.map(() => 0);\n for (;;) {\n const routed = routeConnections(\n ir,\n candidates.map((options, index) => options[choices[index]!]!),\n nodes,\n groups,\n searchMargin,\n );\n if (!('unrouted' in routed)) return routed;\n const options = candidates[routed.unrouted]!;\n const next = choices[routed.unrouted]! + 1;\n if (next === options.length) throw noRoute(options[0]!);\n choices[routed.unrouted] = next;\n }\n}\n\n/**\n * Routes one choice of sides for every connection, or names the first\n * connection, in routing order, that has no route.\n */\nfunction routeConnections(\n ir: DiagramGraphIR,\n connections: Connection[],\n nodes: NodeLayout[],\n groups: GroupLayout[],\n searchMargin: number,\n): RoutedGraph | { unrouted: number } {\n if (connections.length === 0) return { edges: [], leaders: [] };\n const groupLabels = groups.flatMap(({ group, label }) =>\n label.map((run) => ({\n group: group.id,\n box: inflate(run.bounds, LAYOUT_TOKENS.LABEL_GAP),\n })),\n );\n const attachments = attachConnections(connections, groupLabels);\n const content = union([\n ...nodes.map((nodeLayout) => nodeLayout.visual),\n ...groups.map((groupLayout) => groupLayout.rect),\n ]);\n const bounds = inflate(content, ROUTING_MARGIN);\n const solid = nodes.filter(({ box }) => box.w > 0 && box.h > 0);\n\n const routed: Routed[] = [];\n const tags: Box[] = [];\n const paths = new Map<Connection, Point[]>();\n const labels = new Map<Connection, Pick<EdgeLayout, 'label' | 'badge'>>();\n\n const order = connections\n .map((connection, index) => ({ connection, index }))\n .sort(\n (a, b) =>\n Number(a.connection.edge === undefined) -\n Number(b.connection.edge === undefined) ||\n a.connection.cellDistance - b.connection.cellDistance ||\n a.index - b.index,\n );\n\n for (const { connection, index } of order) {\n const { from, to } = connection;\n const ends = new Set([from.shape.id, to.shape.id]);\n const junctions = new Set(\n [from, to]\n .filter((end) => end.shape.kind === 'junction')\n .map((end) => end.shape.id),\n );\n const walls = [\n ...solid.flatMap(({ node, box, visual, frames, caption }) => {\n if (!ends.has(node.id)) return [inflate(visual, NODE_CLEARANCE)];\n if (node.kind === 'junction') return [];\n // Its own parts, not their bounding box: a caption wider than the frame leaves room beside the frame.\n return [box, ...frames, ...(caption ? [caption.box] : [])];\n }),\n ...groupLabels.flatMap(({ group, box }) =>\n ends.has(group) ? [] : [box],\n ),\n ...tags.map((tag) => inflate(tag, LAYOUT_TOKENS.LABEL_GAP)),\n ];\n const runs = routed\n .filter((route) => !overlaps(route.junctions, junctions))\n .flatMap((route) => segments(route.points));\n const { start, startStub, end, endStub } = attachments[index]!;\n const stubs: [Point, Point][] = [\n [start, startStub],\n [endStub, end],\n ];\n const stubsClear = stubs.every(([a, b]) =>\n walls.every((wall) => !crosses(a, b, wall)),\n );\n\n const search = (window: Box): Point[] | undefined =>\n searchRoute({\n window,\n lines: {\n x: [startStub.x, endStub.x],\n y: [startStub.y, endStub.y],\n },\n walls,\n runs,\n borders: groups.flatMap(({ rect, band }) =>\n band ? [rect, band] : [rect],\n ),\n start: startStub,\n startHeading: OUTWARD[from.side],\n goal: endStub,\n goalHeading: OUTWARD[OPPOSITE[to.side]],\n });\n const near = intersection(\n inflate(\n union([from.shape.box, to.shape.box, spanOf(startStub, endStub)]),\n searchMargin,\n ),\n bounds,\n );\n const found = stubsClear ? (search(near) ?? search(bounds)) : undefined;\n if (!found) return { unrouted: index };\n const points = simplifyPath([start, ...found, end]);\n paths.set(connection, points);\n\n if (connection.edge !== undefined) {\n const edge = ir.edges[connection.edge]!;\n const isFree = (box: Box, ownSegments: boolean) =>\n solid.every(({ visual }) => !intersects(box, visual)) &&\n groups.every(\n ({ rect, band, label }) =>\n !(band && intersects(box, band)) &&\n label.every((run) => !intersects(box, run.bounds)) &&\n !(intersects(box, rect) && !contains(rect, box)),\n ) &&\n tags.every((tag) => !intersects(box, tag)) &&\n [\n ...routed.flatMap((route) => segments(route.points)),\n ...(ownSegments ? segments(points) : []),\n ].every(([a, b]) => !crosses(a, b, box));\n const measured = measureEdgeTags(edge);\n const { label, badge } = measured;\n const placed: Pick<EdgeLayout, 'label' | 'badge'> = {};\n if (label || badge) {\n const boxes = tagPosition(measured, points, (candidate, kind) =>\n isFree(candidate, kind === 'label'),\n );\n if (!boxes) {\n const named: string[] = [];\n if (label) {\n named.push(\n `label \"${edge.label!.join(' / ')}\" (${Math.ceil(label.w)}×${Math.ceil(label.h)})`,\n );\n }\n if (badge) {\n named.push(\n `badge \"${(edge.badgeLabel ?? []).join(' / ')}\" (${Math.ceil(badge.w)}×${Math.ceil(badge.h)})`,\n );\n }\n throw new Error(\n `Diagram ${connection.name}: ${named.join(' and ')} ${named.length > 1 ? 'have' : 'has'} no free position ${badge ? 'on' : 'beside'} its route.`,\n );\n }\n if (label && boxes.label) {\n placed.label = { box: boxes.label, text: label.place(boxes.label) };\n tags.push(boxes.label);\n }\n if (badge && boxes.badge) {\n placed.badge = { box: boxes.badge, ...badge.place(boxes.badge) };\n tags.push(boxes.badge);\n }\n }\n labels.set(connection, placed);\n }\n routed.push({ points, junctions });\n }\n\n const edges: RoutedGraph['edges'] = [];\n const leaders: RoutedGraph['leaders'] = [];\n for (const connection of connections) {\n const points = paths.get(connection)!;\n if (connection.edge === undefined) {\n leaders.push({\n note: connection.from.shape.id,\n target: connection.to.shape.id,\n points,\n });\n continue;\n }\n edges.push({\n edge: ir.edges[connection.edge]!,\n exitSide: connection.from.side,\n entrySide: connection.to.side,\n points,\n ...labels.get(connection),\n });\n }\n return { edges, leaders };\n}\n\nfunction noRoute({ name, edge, from, to }: Connection): Error {\n const advice =\n edge === undefined\n ? 'Move the note or its target.'\n : 'Change exitSide/entrySide or move a node.';\n return new Error(\n `Diagram ${name}: no orthogonal route from ${endName(from)} to ${endName(to)} avoids the nodes and labels. ${advice}`,\n );\n}\n\nfunction endName({ shape, side, port }: ConnectionEnd): string {\n if (port) return `port \"${shape.id}:${port.id}\"`;\n if (shape.kind === 'junction') return `the centre of \"${shape.id}\"`;\n return `${side} of \"${shape.id}\"`;\n}\n\nfunction segments(points: Point[]): [Point, Point][] {\n return points.slice(1).map((point, index) => [points[index]!, point]);\n}\n\nfunction overlaps(a: Set<string>, b: Set<string>): boolean {\n for (const id of a) if (b.has(id)) return true;\n return false;\n}\n\nfunction spanOf(a: Point, b: Point): Box {\n return {\n x: Math.min(a.x, b.x),\n y: Math.min(a.y, b.y),\n w: Math.abs(a.x - b.x),\n h: Math.abs(a.y - b.y),\n };\n}\n\n/** Whether the segment enters the box's interior, not only its border. */\nfunction crosses(a: Point, b: Point, box: Box): boolean {\n const span = spanOf(a, b);\n return (\n span.x + span.w > box.x &&\n span.x < box.x + box.w &&\n span.y + span.h > box.y &&\n span.y < box.y + box.h\n );\n}\n\nfunction intersects(a: Box, b: Box): boolean {\n return (\n a.x < b.x + b.w && b.x < a.x + a.w && a.y < b.y + b.h && b.y < a.y + a.h\n );\n}\n\nfunction contains(outer: Box, inner: Box): boolean {\n return (\n inner.x >= outer.x &&\n inner.y >= outer.y &&\n inner.x + inner.w <= outer.x + outer.w &&\n inner.y + inner.h <= outer.y + outer.h\n );\n}\n\nfunction inflate(box: Box, by: number): Box {\n return { x: box.x - by, y: box.y - by, w: box.w + 2 * by, h: box.h + 2 * by };\n}\n\nfunction intersection(a: Box, b: Box): Box {\n const x = Math.max(a.x, b.x);\n const y = Math.max(a.y, b.y);\n return {\n x,\n y,\n w: Math.min(a.x + a.w, b.x + b.w) - x,\n h: Math.min(a.y + a.h, b.y + b.h) - y,\n };\n}\n\nfunction union(boxes: Box[]): Box {\n const left = Math.min(...boxes.map((box) => box.x));\n const top = Math.min(...boxes.map((box) => box.y));\n const right = Math.max(...boxes.map((box) => box.x + box.w));\n const bottom = Math.max(...boxes.map((box) => box.y + box.h));\n return { x: left, y: top, w: right - left, h: bottom - top };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,28 @@
1
+ import type { Box, DiagramSide, Point } from '../../model/diagram-ir.ts';
2
+ import type { CellArea } from './areas.ts';
3
+ import type { Heading } from './route-search.ts';
4
+ export declare const OPPOSITE: Record<DiagramSide, DiagramSide>;
5
+ /** The heading of a line that leaves a box through `side`. */
6
+ export declare const OUTWARD: Record<DiagramSide, Heading>;
7
+ export declare function isVertical(side: DiagramSide): boolean;
8
+ /** The side a connection leaves by, and the side it enters by. */
9
+ export type SidePair = [DiagramSide, DiagramSide];
10
+ /**
11
+ * The pairs of sides a connection may leave and enter by, read from the
12
+ * cells, in order of preference. First the facing pair: areas that share
13
+ * rows face left and right, areas that share columns face top and bottom,
14
+ * and otherwise the axis with the larger cell delta wins, the vertical one
15
+ * on a tie. Then the facing pair on the other axis, then the first pair's
16
+ * exit with the other pair's entry, then the other pair's exit with the
17
+ * first pair's entry.
18
+ */
19
+ export declare function sidePairs(from: CellArea, to: CellArea): [SidePair, ...SidePair[]];
20
+ /** The coordinate of the side's line: x for left and right, y for top and bottom. */
21
+ export declare function sideLine(box: Box, side: DiagramSide): number;
22
+ /** Where the side runs along its line: [start, end] in x for top and bottom, in y for left and right. */
23
+ export declare function sideSpan(box: Box, side: DiagramSide): [number, number];
24
+ /** The point on `side` of `box` at `along` on the side's span. */
25
+ export declare function pointOnSide(box: Box, side: DiagramSide, along: number): Point;
26
+ /** The point `distance` beyond `point` out of `side`. */
27
+ export declare function beyond(point: Point, side: DiagramSide, distance: number): Point;
28
+ //# sourceMappingURL=sides.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sides.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/sides.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE,WAAW,CAKrD,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,WAAW,EAAE,OAAO,CAKhD,CAAC;AAEF,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAErD;AAED,kEAAkE;AAClE,MAAM,MAAM,QAAQ,GAAG,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AAElD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,QAAQ,EACd,EAAE,EAAE,QAAQ,GACX,CAAC,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAAC,CAe3B;AAED,qFAAqF;AACrF,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAW5D;AAED,yGAAyG;AACzG,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAEtE;AAED,kEAAkE;AAClE,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAG7E;AAED,yDAAyD;AACzD,wBAAgB,MAAM,CACpB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,MAAM,GACf,KAAK,CAWP"}
@@ -0,0 +1,98 @@
1
+ export const OPPOSITE = {
2
+ top: 'bottom',
3
+ bottom: 'top',
4
+ left: 'right',
5
+ right: 'left'
6
+ };
7
+
8
+ /** The heading of a line that leaves a box through `side`. */
9
+ export const OUTWARD = {
10
+ right: 0,
11
+ left: 1,
12
+ bottom: 2,
13
+ top: 3
14
+ };
15
+ export function isVertical(side) {
16
+ return side === 'top' || side === 'bottom';
17
+ }
18
+
19
+ /** The side a connection leaves by, and the side it enters by. */
20
+
21
+ /**
22
+ * The pairs of sides a connection may leave and enter by, read from the
23
+ * cells, in order of preference. First the facing pair: areas that share
24
+ * rows face left and right, areas that share columns face top and bottom,
25
+ * and otherwise the axis with the larger cell delta wins, the vertical one
26
+ * on a tie. Then the facing pair on the other axis, then the first pair's
27
+ * exit with the other pair's entry, then the other pair's exit with the
28
+ * first pair's entry.
29
+ */
30
+ export function sidePairs(from, to) {
31
+ const shareRows = Math.max(from.row, to.row) < Math.min(from.row + from.rowSpan, to.row + to.rowSpan);
32
+ const shareCols = Math.max(from.col, to.col) < Math.min(from.col + from.colSpan, to.col + to.colSpan);
33
+ const dr = to.row + to.rowSpan / 2 - (from.row + from.rowSpan / 2);
34
+ const dc = to.col + to.colSpan / 2 - (from.col + from.colSpan / 2);
35
+ const across = dc > 0 ? ['right', 'left'] : ['left', 'right'];
36
+ const down = dr >= 0 ? ['bottom', 'top'] : ['top', 'bottom'];
37
+ const horizontal = shareRows !== shareCols ? shareRows : Math.abs(dc) > Math.abs(dr);
38
+ const [first, other] = horizontal ? [across, down] : [down, across];
39
+ return [first, other, [first[0], other[1]], [other[0], first[1]]];
40
+ }
41
+
42
+ /** The coordinate of the side's line: x for left and right, y for top and bottom. */
43
+ export function sideLine(box, side) {
44
+ switch (side) {
45
+ case 'top':
46
+ return box.y;
47
+ case 'bottom':
48
+ return box.y + box.h;
49
+ case 'left':
50
+ return box.x;
51
+ case 'right':
52
+ return box.x + box.w;
53
+ }
54
+ }
55
+
56
+ /** Where the side runs along its line: [start, end] in x for top and bottom, in y for left and right. */
57
+ export function sideSpan(box, side) {
58
+ return isVertical(side) ? [box.x, box.x + box.w] : [box.y, box.y + box.h];
59
+ }
60
+
61
+ /** The point on `side` of `box` at `along` on the side's span. */
62
+ export function pointOnSide(box, side, along) {
63
+ const line = sideLine(box, side);
64
+ return isVertical(side) ? {
65
+ x: along,
66
+ y: line
67
+ } : {
68
+ x: line,
69
+ y: along
70
+ };
71
+ }
72
+
73
+ /** The point `distance` beyond `point` out of `side`. */
74
+ export function beyond(point, side, distance) {
75
+ switch (side) {
76
+ case 'top':
77
+ return {
78
+ x: point.x,
79
+ y: point.y - distance
80
+ };
81
+ case 'bottom':
82
+ return {
83
+ x: point.x,
84
+ y: point.y + distance
85
+ };
86
+ case 'left':
87
+ return {
88
+ x: point.x - distance,
89
+ y: point.y
90
+ };
91
+ case 'right':
92
+ return {
93
+ x: point.x + distance,
94
+ y: point.y
95
+ };
96
+ }
97
+ }
98
+ //# sourceMappingURL=sides.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sides.js","names":["OPPOSITE","top","bottom","left","right","OUTWARD","isVertical","side","sidePairs","from","to","shareRows","Math","max","row","min","rowSpan","shareCols","col","colSpan","dr","dc","across","down","horizontal","abs","first","other","sideLine","box","y","h","x","w","sideSpan","pointOnSide","along","line","beyond","point","distance"],"sources":["../../../../src/lib/layouts/graph/sides.ts"],"sourcesContent":["import type { Box, DiagramSide, Point } from '../../model/diagram-ir.ts';\nimport type { CellArea } from './areas.ts';\nimport type { Heading } from './route-search.ts';\n\nexport const OPPOSITE: Record<DiagramSide, DiagramSide> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n};\n\n/** The heading of a line that leaves a box through `side`. */\nexport const OUTWARD: Record<DiagramSide, Heading> = {\n right: 0,\n left: 1,\n bottom: 2,\n top: 3,\n};\n\nexport function isVertical(side: DiagramSide): boolean {\n return side === 'top' || side === 'bottom';\n}\n\n/** The side a connection leaves by, and the side it enters by. */\nexport type SidePair = [DiagramSide, DiagramSide];\n\n/**\n * The pairs of sides a connection may leave and enter by, read from the\n * cells, in order of preference. First the facing pair: areas that share\n * rows face left and right, areas that share columns face top and bottom,\n * and otherwise the axis with the larger cell delta wins, the vertical one\n * on a tie. Then the facing pair on the other axis, then the first pair's\n * exit with the other pair's entry, then the other pair's exit with the\n * first pair's entry.\n */\nexport function sidePairs(\n from: CellArea,\n to: CellArea,\n): [SidePair, ...SidePair[]] {\n const shareRows =\n Math.max(from.row, to.row) <\n Math.min(from.row + from.rowSpan, to.row + to.rowSpan);\n const shareCols =\n Math.max(from.col, to.col) <\n Math.min(from.col + from.colSpan, to.col + to.colSpan);\n const dr = to.row + to.rowSpan / 2 - (from.row + from.rowSpan / 2);\n const dc = to.col + to.colSpan / 2 - (from.col + from.colSpan / 2);\n const across: SidePair = dc > 0 ? ['right', 'left'] : ['left', 'right'];\n const down: SidePair = dr >= 0 ? ['bottom', 'top'] : ['top', 'bottom'];\n const horizontal =\n shareRows !== shareCols ? shareRows : Math.abs(dc) > Math.abs(dr);\n const [first, other] = horizontal ? [across, down] : [down, across];\n return [first, other, [first[0], other[1]], [other[0], first[1]]];\n}\n\n/** The coordinate of the side's line: x for left and right, y for top and bottom. */\nexport function sideLine(box: Box, side: DiagramSide): number {\n switch (side) {\n case 'top':\n return box.y;\n case 'bottom':\n return box.y + box.h;\n case 'left':\n return box.x;\n case 'right':\n return box.x + box.w;\n }\n}\n\n/** Where the side runs along its line: [start, end] in x for top and bottom, in y for left and right. */\nexport function sideSpan(box: Box, side: DiagramSide): [number, number] {\n return isVertical(side) ? [box.x, box.x + box.w] : [box.y, box.y + box.h];\n}\n\n/** The point on `side` of `box` at `along` on the side's span. */\nexport function pointOnSide(box: Box, side: DiagramSide, along: number): Point {\n const line = sideLine(box, side);\n return isVertical(side) ? { x: along, y: line } : { x: line, y: along };\n}\n\n/** The point `distance` beyond `point` out of `side`. */\nexport function beyond(\n point: Point,\n side: DiagramSide,\n distance: number,\n): Point {\n switch (side) {\n case 'top':\n return { x: point.x, y: point.y - distance };\n case 'bottom':\n return { x: point.x, y: point.y + distance };\n case 'left':\n return { x: point.x - distance, y: point.y };\n case 'right':\n return { x: point.x + distance, y: point.y };\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,4 +1,5 @@
1
1
  import type { DiagramSignal, DiagramSignalBox, DiagramTimelineSpec } from '../../model/diagram-ir.ts';
2
+ import type { Font } from '../text/font.ts';
2
3
  export type { DiagramFrameElement, DiagramLane, DiagramSignal, DiagramSignalBox, } from '../../model/diagram-ir.ts';
3
4
  export type TimelineLayoutSpec = DiagramTimelineSpec & {
4
5
  title?: string[];
@@ -16,7 +17,7 @@ export declare const FRAME_HEADER_H = 22;
16
17
  export declare const FRAME_PAD = 12;
17
18
  export declare const BOTTOM_ACTOR_GAP = 50;
18
19
  export declare const DEFAULT_BOXED_STUB = 12;
19
- export declare const SIGNAL_LABEL_FONT_SIZE = 12;
20
+ export declare const SIGNAL_LABEL_FONT: Font;
20
21
  export declare const SIGNAL_LABEL_PAD_X = 14;
21
22
  export declare const SIGNAL_LABEL_PAD_Y = 10;
22
23
  export declare const SIGNAL_LABEL_LINE_H = 18;
@@ -1 +1 @@
1
- {"version":3,"file":"timeline-layout.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/timeline/timeline-layout.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"timeline-layout.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/timeline/timeline-layout.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { textAscent, widestLine } from "../text-metrics.js";
1
+ import { fontAscent, measureText } from "../text/measure-text.js";
2
2
  export const SEQ_W = 1000;
3
3
  export const ACTOR_W = 190;
4
4
  export const ACTOR_H = 72;
@@ -12,7 +12,11 @@ export const FRAME_HEADER_H = 22;
12
12
  export const FRAME_PAD = 12;
13
13
  export const BOTTOM_ACTOR_GAP = 50;
14
14
  export const DEFAULT_BOXED_STUB = 12;
15
- export const SIGNAL_LABEL_FONT_SIZE = 12;
15
+ export const SIGNAL_LABEL_FONT = {
16
+ face: 'mono',
17
+ size: 12,
18
+ weight: 400
19
+ };
16
20
  export const SIGNAL_LABEL_PAD_X = 14;
17
21
  export const SIGNAL_LABEL_PAD_Y = 10;
18
22
  export const SIGNAL_LABEL_LINE_H = 18;
@@ -62,8 +66,9 @@ export function buildTimelineSegments(totalSignals, hasLoop, frameMsgs) {
62
66
  return segments;
63
67
  }
64
68
  export function measureSignalLabel(label) {
69
+ const widestLineW = Math.max(0, ...label.map(line => measureText(line, SIGNAL_LABEL_FONT)));
65
70
  return {
66
- boxW: Math.ceil(widestLine(label, SIGNAL_LABEL_FONT_SIZE, 'mono')) + SIGNAL_LABEL_PAD_X * 2,
71
+ boxW: Math.ceil(widestLineW) + SIGNAL_LABEL_PAD_X * 2,
67
72
  boxH: label.length * SIGNAL_LABEL_LINE_H + SIGNAL_LABEL_PAD_Y * 2
68
73
  };
69
74
  }
@@ -71,7 +76,7 @@ export function measureSignalLabel(label) {
71
76
  /** Distance from a signal's row up to the top of its label or label box. */
72
77
  export function signalLabelHeadroom(signal) {
73
78
  if (signal.boxed) return measureSignalLabel(signal.label).boxH / 2;
74
- return SIGNAL_LABEL_BASELINE_GAP + (signal.label.length - 1) * SIGNAL_LABEL_LINE_STEP + textAscent(SIGNAL_LABEL_FONT_SIZE, 'mono');
79
+ return SIGNAL_LABEL_BASELINE_GAP + (signal.label.length - 1) * SIGNAL_LABEL_LINE_STEP + fontAscent(SIGNAL_LABEL_FONT);
75
80
  }
76
81
 
77
82
  /** Distance from a signal's row down to the bottom of its label box. */