@statelyai/layout 0.2.0 → 0.4.0

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 (132) hide show
  1. package/NOTICE.md +4 -2
  2. package/README.md +221 -13
  3. package/dist/elkjs/index.cjs +3 -2
  4. package/dist/elkjs/index.d.cts +13 -2
  5. package/dist/elkjs/index.d.mts +13 -2
  6. package/dist/elkjs/index.mjs +3 -3
  7. package/dist/elkjs/main.cjs +2 -2
  8. package/dist/elkjs/main.d.cts +2 -2
  9. package/dist/elkjs/main.d.mts +2 -2
  10. package/dist/elkjs/main.mjs +2 -2
  11. package/dist/elkjs/worker-api.cjs +1 -1
  12. package/dist/elkjs/worker-api.d.cts +2 -2
  13. package/dist/elkjs/worker-api.d.mts +2 -2
  14. package/dist/elkjs/worker-api.mjs +1 -1
  15. package/dist/elkjs/worker.cjs +2 -2
  16. package/dist/elkjs/worker.mjs +2 -2
  17. package/dist/elkjs-UciYx7lD.mjs +2973 -0
  18. package/dist/elkjs-ggjsBuVP.cjs +3002 -0
  19. package/dist/index-B6cWNrMw.d.cts +80 -0
  20. package/dist/index-CJQLUKD9.d.mts +80 -0
  21. package/dist/{index-BSH2bloA.d.mts → index-DT6lPMfS.d.cts} +117 -5
  22. package/dist/{index-CG712jzV.d.cts → index-F1U-fCTu.d.mts} +117 -5
  23. package/dist/index.cjs +39 -9
  24. package/dist/index.d.cts +4 -3
  25. package/dist/index.d.mts +4 -3
  26. package/dist/index.mjs +4 -3
  27. package/dist/layered/index.cjs +3 -1
  28. package/dist/layered/index.d.cts +3 -2
  29. package/dist/layered/index.d.mts +3 -2
  30. package/dist/layered/index.mjs +2 -2
  31. package/dist/{layered-Bz2nWmVp.mjs → layered-C7ERzZjR.mjs} +12592 -4682
  32. package/dist/{layered-Cf9Rn8l7.cjs → layered-CBPg6_6_.cjs} +12711 -4717
  33. package/dist/{layout-engine-BNex9jxg.cjs → layout-engine-DAw06RhN.cjs} +5 -18
  34. package/dist/{layout-engine-WbC0YjjT.mjs → layout-engine-DC3IYkIy.mjs} +3 -10
  35. package/dist/routing/index.cjs +29 -0
  36. package/dist/routing/index.d.cts +3 -0
  37. package/dist/routing/index.d.mts +3 -0
  38. package/dist/routing/index.mjs +3 -0
  39. package/dist/routing-CR8flv8b.mjs +2780 -0
  40. package/dist/routing-QIdGGFBe.cjs +2996 -0
  41. package/dist/types-CT2akdK0.d.mts +133 -0
  42. package/dist/types-uxPlGRof.d.cts +133 -0
  43. package/dist/{worker-client-DAT_z2OR.d.mts → worker-client-BwGGRD5y.d.mts} +1 -1
  44. package/dist/{worker-client-DVhklF6A.d.cts → worker-client-D4zNyPEI.d.cts} +1 -1
  45. package/package.json +14 -3
  46. package/src/authoring/coordinates.ts +38 -10
  47. package/src/authoring/partial.ts +7 -2
  48. package/src/authoring/routing.ts +4 -1
  49. package/src/constraints.ts +174 -6
  50. package/src/elkjs/index.ts +1397 -452
  51. package/src/elkjs/layout-quality.ts +219 -0
  52. package/src/elkjs/opposing-tracks.ts +161 -0
  53. package/src/elkjs/orthogonal-junctions.ts +174 -0
  54. package/src/elkjs/routes.ts +147 -0
  55. package/src/elkjs/statechart.ts +9 -3
  56. package/src/elkjs/types.ts +2 -0
  57. package/src/index.ts +8 -0
  58. package/src/internal/elkjs-compatibility.ts +4 -1
  59. package/src/internal/layered-trace.ts +48 -0
  60. package/src/internal/layout-engine.ts +2 -1
  61. package/src/java-random.ts +6 -0
  62. package/src/layered/bk-node-placement.ts +403 -92
  63. package/src/layered/center-labels.ts +398 -0
  64. package/src/layered/compaction-bounds.ts +26 -0
  65. package/src/layered/compaction-errors.ts +2 -0
  66. package/src/layered/compaction-scanline.ts +63 -0
  67. package/src/layered/compound-boundaries.ts +99 -0
  68. package/src/layered/compound.ts +445 -0
  69. package/src/layered/constraint-orientation.ts +141 -0
  70. package/src/layered/crossing-constraints.ts +243 -0
  71. package/src/layered/crossing-counter.ts +221 -0
  72. package/src/layered/crossing-graph.ts +401 -0
  73. package/src/layered/cycle-random.ts +22 -0
  74. package/src/layered/elk-port-lists.ts +243 -0
  75. package/src/layered/end-labels.ts +124 -0
  76. package/src/layered/external-port-dummy.ts +137 -0
  77. package/src/layered/feedback-chains.ts +114 -0
  78. package/src/layered/fixed-self-loop.ts +150 -0
  79. package/src/layered/greedy-switch-decider.ts +356 -0
  80. package/src/layered/grouped-compaction.ts +787 -0
  81. package/src/layered/gwt-sort.ts +53 -0
  82. package/src/layered/hierarchical-port-phases.ts +242 -0
  83. package/src/layered/hierarchical-port-restoration.ts +233 -0
  84. package/src/layered/hierarchical-port-routing.ts +179 -0
  85. package/src/layered/hierarchy-crossing.ts +121 -0
  86. package/src/layered/hierarchy-sweepiness.ts +83 -0
  87. package/src/layered/hints.ts +371 -0
  88. package/src/layered/hyperedge-dummy-merger.ts +195 -0
  89. package/src/layered/index.ts +911 -261
  90. package/src/layered/initial-model-order.ts +404 -0
  91. package/src/layered/inverted-ports.ts +207 -0
  92. package/src/layered/label-dummy-switcher.ts +200 -0
  93. package/src/layered/long-edges.ts +135 -44
  94. package/src/layered/loop-envelopes.ts +184 -0
  95. package/src/layered/native-routing.ts +275 -0
  96. package/src/layered/network-simplex.ts +11 -15
  97. package/src/layered/node-margins.ts +59 -0
  98. package/src/layered/north-south-ports.ts +271 -0
  99. package/src/layered/opposing-tracks.ts +944 -0
  100. package/src/layered/orthogonal-cycle-order.ts +106 -0
  101. package/src/layered/orthogonal-hypersegments.ts +92 -0
  102. package/src/layered/orthogonal-junctions.ts +202 -0
  103. package/src/layered/orthogonal-segments.ts +243 -0
  104. package/src/layered/placement-orientation.ts +22 -0
  105. package/src/layered/port-distributor.ts +192 -0
  106. package/src/layered/port-edge-order.ts +20 -0
  107. package/src/layered/replace-routing.ts +45 -0
  108. package/src/layered/routing-coordinates.ts +32 -0
  109. package/src/layered/separate-exterior-labels.ts +24 -6
  110. package/src/layered/spacing.ts +34 -12
  111. package/src/layered/splitter-order.ts +106 -0
  112. package/src/layered/strategies.ts +2024 -883
  113. package/src/layered/types.ts +34 -0
  114. package/src/layered/weighted-compaction.ts +84 -0
  115. package/src/routing/adapters.ts +143 -0
  116. package/src/routing/algorithms.ts +1088 -0
  117. package/src/routing/coordination.ts +91 -0
  118. package/src/routing/engine.ts +473 -0
  119. package/src/routing/index.ts +69 -0
  120. package/src/routing/layout-cache.ts +65 -0
  121. package/src/routing/model.ts +218 -0
  122. package/src/routing/path.ts +324 -0
  123. package/src/routing/persistent.ts +117 -0
  124. package/src/routing/search.ts +358 -0
  125. package/src/routing/spatial.ts +78 -0
  126. package/src/routing/types.ts +146 -0
  127. package/dist/elkjs-BVnDwnBQ.cjs +0 -1551
  128. package/dist/elkjs-CFlfkEBz.mjs +0 -1528
  129. /package/dist/{public-types-B8rAALJq.d.mts → public-types-9NCPoqbp.d.mts} +0 -0
  130. /package/dist/{public-types-CVB0hXGk.d.cts → public-types-BcRZoJE3.d.cts} +0 -0
  131. /package/dist/{worker-client-DkwS4sHx.mjs → worker-client-BNjwT1AJ.mjs} +0 -0
  132. /package/dist/{worker-client-D3HMK33r.cjs → worker-client-D4oEvhAl.cjs} +0 -0
@@ -0,0 +1,3002 @@
1
+ const require_routing = require('./routing-QIdGGFBe.cjs');
2
+ const require_layered = require('./layered-CBPg6_6_.cjs');
3
+ const require_layout_engine = require('./layout-engine-DAw06RhN.cjs');
4
+ let _statelyai_graph = require("@statelyai/graph");
5
+
6
+ //#region src/layered/hierarchy-sweepiness.ts
7
+ function useBottomUpHierarchySweep(input, orientation, layers, parent) {
8
+ const boundary = input.settings["crossingMinimization.hierarchicalSweepiness"] ?? .1;
9
+ if (boundary < -1 || parent.portOrderFixed || parent.inputPorts < 2 && parent.outputPorts < 2) return true;
10
+ if (parent.deterministic) return false;
11
+ const nodes = new Map(input.graph.nodes.map((node) => [node.id, node]));
12
+ const infos = new Map(input.graph.nodes.map((node) => [node.id, {
13
+ random: 0,
14
+ hierarchy: 0,
15
+ edges: 0
16
+ }]));
17
+ const outgoing = /* @__PURE__ */ new Map(), incoming = /* @__PURE__ */ new Map();
18
+ for (const edge of input.graph.edges) {
19
+ const reversed = orientation.reversedEdgeIds.has(edge.id);
20
+ const source = reversed ? edge.targetId : edge.sourceId, target = reversed ? edge.sourceId : edge.targetId;
21
+ outgoing.set(source, [...outgoing.get(source) ?? [], target]);
22
+ incoming.set(target, [...incoming.get(target) ?? [], source]);
23
+ }
24
+ const units = require_layered.getCrossingUnits(input);
25
+ let randomPaths = 0, hierarchyPaths = 0;
26
+ const transfer = (source, target) => {
27
+ const a = infos.get(source), b = infos.get(target);
28
+ b.random += a.random;
29
+ b.hierarchy += a.hierarchy;
30
+ b.edges += a.edges + 1;
31
+ };
32
+ const outputSide = {
33
+ right: "EAST",
34
+ left: "WEST",
35
+ down: "SOUTH",
36
+ up: "NORTH"
37
+ }[input.direction];
38
+ const passEdges = (id) => {
39
+ const state = infos.get(id);
40
+ for (const target of outgoing.get(id) ?? []) {
41
+ randomPaths += state.random;
42
+ hierarchyPaths += state.hierarchy;
43
+ transfer(id, target);
44
+ }
45
+ };
46
+ for (const layer of layers) {
47
+ const northSouth = [];
48
+ for (const id of layer) {
49
+ if (id.startsWith("__layout_dummy:north-south:")) {
50
+ northSouth.push(id);
51
+ continue;
52
+ }
53
+ const node = nodes.get(id), state = infos.get(id);
54
+ const external = require_layered.externalPortDummyOf(node);
55
+ if (external) {
56
+ state.hierarchy = 1;
57
+ if (external.side === outputSide) hierarchyPaths += state.edges;
58
+ } else if (!incoming.get(id)?.length) state.random = 1;
59
+ else if (!outgoing.get(id)?.length) randomPaths += state.edges;
60
+ passEdges(id);
61
+ for (const dummy of units?.associates.get(id) ?? []) {
62
+ randomPaths += state.random;
63
+ hierarchyPaths += state.hierarchy;
64
+ transfer(id, dummy);
65
+ }
66
+ }
67
+ for (const id of northSouth) passEdges(id);
68
+ }
69
+ const all = randomPaths + hierarchyPaths;
70
+ return (all === 0 ? Number.POSITIVE_INFINITY : (randomPaths - hierarchyPaths) / all) >= boundary;
71
+ }
72
+
73
+ //#endregion
74
+ //#region src/layered/compound-boundaries.ts
75
+ /** Return both effects of ELK's transfer: parent port and corrected content-space dummy. */
76
+ function transferExternalPort(input) {
77
+ const { contentSize: size, padding: p, offset: o, borderOffset: b, portSize } = input;
78
+ const dummy = {
79
+ x: input.dummy.x,
80
+ y: input.dummy.y
81
+ };
82
+ const port = {
83
+ x: dummy.x + input.dummy.width / 2,
84
+ y: dummy.y + input.dummy.height / 2
85
+ };
86
+ switch (input.side) {
87
+ case "NORTH":
88
+ port.x += p.left + o.x - portSize.width / 2;
89
+ port.y = -portSize.height - b;
90
+ dummy.y = -(p.top + b + o.y);
91
+ break;
92
+ case "EAST":
93
+ port.x = size.width + p.left + p.right + b;
94
+ port.y += p.top + o.y - portSize.height / 2;
95
+ dummy.x = size.width + p.right + b - o.x;
96
+ break;
97
+ case "SOUTH":
98
+ port.x += p.left + o.x - portSize.width / 2;
99
+ port.y = size.height + p.top + p.bottom + b;
100
+ dummy.y = size.height + p.bottom + b - o.y;
101
+ break;
102
+ case "WEST":
103
+ port.x = -portSize.width - b;
104
+ port.y += p.top + o.y - portSize.height / 2;
105
+ dummy.x = -(p.left + b + o.x);
106
+ break;
107
+ }
108
+ return {
109
+ port,
110
+ dummy
111
+ };
112
+ }
113
+ /** Segments are directed source-to-target and translated to one reference graph. */
114
+ function joinCompoundRouteSegments(segments, unnecessaryBendpoints = false) {
115
+ const bends = [];
116
+ let start, end, last;
117
+ for (const segment of segments) {
118
+ if (segment.points.length < 2) throw new Error("Compound route segment needs two anchors");
119
+ const points = segment.points.map((p) => ({
120
+ x: p.x + segment.offset.x,
121
+ y: p.y + segment.offset.y
122
+ }));
123
+ const source = points[0], target = points.at(-1);
124
+ const inner = points.slice(1, -1);
125
+ if (!start) start = source;
126
+ if (last) {
127
+ const next = inner[0] ?? target;
128
+ const dx = Math.abs(last.x - next.x) > 1e-6;
129
+ const dy = Math.abs(last.y - next.y) > 1e-6;
130
+ if (unnecessaryBendpoints ? dx || dy : dx && dy) bends.push(source);
131
+ }
132
+ bends.push(...inner);
133
+ last = inner.at(-1) ?? source;
134
+ end = target;
135
+ }
136
+ if (!start || !end) return [];
137
+ const route = [
138
+ start,
139
+ ...bends,
140
+ end
141
+ ];
142
+ const straight = (a, b) => Math.abs(a.x - b.x) <= 1e-6 || Math.abs(a.y - b.y) <= 1e-6;
143
+ for (let index = 0; index + 1 < route.length; index++) {
144
+ const a = route[index], b = route[index + 1];
145
+ if (straight(a, b)) continue;
146
+ const next = route[index + 2], previous = route[index - 1];
147
+ const verticalFirst = next !== void 0 ? Math.abs(b.y - next.y) <= 1e-6 : previous !== void 0 && Math.abs(previous.x - a.x) <= 1e-6;
148
+ route.splice(index + 1, 0, verticalFirst ? {
149
+ x: a.x,
150
+ y: b.y
151
+ } : {
152
+ x: b.x,
153
+ y: a.y
154
+ });
155
+ }
156
+ return route;
157
+ }
158
+
159
+ //#endregion
160
+ //#region src/elkjs/layout-quality.ts
161
+ const EPS = 1e-6;
162
+ /** Shared endpoint nodes get this clearance, so fan-in and fan-out are not penalized. */
163
+ const TERMINAL_MARGIN = 12;
164
+ const horizontal = (s) => Math.abs(s.a.y - s.b.y) < EPS;
165
+ const vertical = (s) => Math.abs(s.a.x - s.b.x) < EPS;
166
+ const span = (s, axis) => [Math.min(s.a[axis], s.b[axis]), Math.max(s.a[axis], s.b[axis])];
167
+ const strictlyInside = (value, low, high) => value > low + EPS && value < high - EPS;
168
+ const overlapLength = (a, b) => Math.max(0, Math.min(a[1], b[1]) - Math.max(a[0], b[0]));
169
+ const inRect = (point, rect, margin) => point.x > rect.x - margin && point.x < rect.x + rect.width + margin && point.y > rect.y - margin && point.y < rect.y + rect.height + margin;
170
+ /** Measure an ELK JSON result in absolute coordinates. */
171
+ function measureLayout(root) {
172
+ const frames = new Map([[String(root.id), {
173
+ x: 0,
174
+ y: 0
175
+ }]]);
176
+ const rects = /* @__PURE__ */ new Map();
177
+ const owner = /* @__PURE__ */ new Map();
178
+ const leaves = [];
179
+ const bounds = [];
180
+ const place = (parent, offset) => {
181
+ for (const child of parent.children ?? []) {
182
+ const id = String(child.id);
183
+ const at = {
184
+ x: offset.x + (child.x ?? 0),
185
+ y: offset.y + (child.y ?? 0)
186
+ };
187
+ const rect = {
188
+ ...at,
189
+ width: child.width ?? 0,
190
+ height: child.height ?? 0
191
+ };
192
+ frames.set(id, at);
193
+ rects.set(id, rect);
194
+ owner.set(id, id);
195
+ for (const port of child.ports ?? []) owner.set(String(port.id), id);
196
+ bounds.push(at, {
197
+ x: at.x + rect.width,
198
+ y: at.y + rect.height
199
+ });
200
+ if (child.children?.length) place(child, at);
201
+ else leaves.push(rect);
202
+ }
203
+ };
204
+ place(root, {
205
+ x: 0,
206
+ y: 0
207
+ });
208
+ const routes = [];
209
+ let defects = 0, bends = 0, length = 0;
210
+ const collect = (parent) => {
211
+ for (const edge of parent.edges ?? []) {
212
+ const container = String(edge.container ?? parent.id);
213
+ const offset = frames.get(container) ?? {
214
+ x: 0,
215
+ y: 0
216
+ };
217
+ const ends = [edge.sources?.[0], edge.targets?.[0]].flatMap((end) => {
218
+ const node = owner.get(String(end));
219
+ return node === void 0 ? [] : [node];
220
+ });
221
+ const segments = [];
222
+ for (const section of edge.sections ?? []) {
223
+ const points = [
224
+ section.startPoint,
225
+ ...section.bendPoints ?? [],
226
+ section.endPoint
227
+ ].map((point) => ({
228
+ x: point.x + offset.x,
229
+ y: point.y + offset.y
230
+ }));
231
+ bounds.push(...points);
232
+ let direction;
233
+ for (let index = 0; index + 1 < points.length; index++) {
234
+ const segment = {
235
+ a: points[index],
236
+ b: points[index + 1]
237
+ };
238
+ const size = Math.hypot(segment.b.x - segment.a.x, segment.b.y - segment.a.y);
239
+ if (size < EPS) continue;
240
+ if (!horizontal(segment) && !vertical(segment)) defects++;
241
+ const next = {
242
+ x: Math.sign(segment.b.x - segment.a.x),
243
+ y: Math.sign(segment.b.y - segment.a.y)
244
+ };
245
+ if (direction && (direction.x !== next.x || direction.y !== next.y)) bends++;
246
+ direction = next;
247
+ length += size;
248
+ segments.push(segment);
249
+ }
250
+ }
251
+ const labels = (edge.labels ?? []).flatMap((label) => label.width && label.height ? [{
252
+ x: (label.x ?? 0) + offset.x,
253
+ y: (label.y ?? 0) + offset.y,
254
+ width: label.width,
255
+ height: label.height
256
+ }] : []);
257
+ for (const label of labels) bounds.push(label, {
258
+ x: label.x + label.width,
259
+ y: label.y + label.height
260
+ });
261
+ for (const segment of segments) for (const rect of leaves) if (horizontal(segment) && strictlyInside(segment.a.y, rect.y, rect.y + rect.height) && overlapLength(span(segment, "x"), [rect.x, rect.x + rect.width]) > EPS || vertical(segment) && strictlyInside(segment.a.x, rect.x, rect.x + rect.width) && overlapLength(span(segment, "y"), [rect.y, rect.y + rect.height]) > EPS) defects++;
262
+ for (const [index, first] of segments.entries()) for (const second of segments.slice(index + 1)) if (sharedLength(first, second) > EPS) defects++;
263
+ routes.push({
264
+ ends,
265
+ segments,
266
+ labels
267
+ });
268
+ }
269
+ for (const child of parent.children ?? []) collect(child);
270
+ };
271
+ collect(root);
272
+ const allLabels = routes.flatMap((route) => route.labels);
273
+ const covers = (a, b) => a.x < b.x + b.width - EPS && a.x + a.width > b.x + EPS && a.y < b.y + b.height - EPS && a.y + a.height > b.y + EPS;
274
+ for (const [index, label] of allLabels.entries()) {
275
+ for (const rect of leaves) if (covers(label, rect)) defects++;
276
+ for (const other of allLabels.slice(index + 1)) if (covers(label, other)) defects++;
277
+ }
278
+ for (const route of routes) for (const other of routes) if (other !== route) {
279
+ for (const label of other.labels) if (route.segments.some((segment) => horizontal(segment) && strictlyInside(segment.a.y, label.y, label.y + label.height) && overlapLength(span(segment, "x"), [label.x, label.x + label.width]) > EPS || vertical(segment) && strictlyInside(segment.a.x, label.x, label.x + label.width) && overlapLength(span(segment, "y"), [label.y, label.y + label.height]) > EPS)) defects++;
280
+ }
281
+ let crossings = 0, overlap = 0;
282
+ for (const [index, first] of routes.entries()) for (const second of routes.slice(index + 1)) {
283
+ const shared = first.ends.filter((id) => second.ends.includes(id)).flatMap((id) => {
284
+ const rect = rects.get(id);
285
+ return rect ? [rect] : [];
286
+ });
287
+ const clear = (point) => shared.every((rect) => !inRect(point, rect, TERMINAL_MARGIN));
288
+ for (const s of first.segments) for (const t of second.segments) {
289
+ const h = horizontal(s) && vertical(t) ? s : horizontal(t) && vertical(s) ? t : void 0;
290
+ if (h) {
291
+ const v = h === s ? t : s;
292
+ const point = {
293
+ x: v.a.x,
294
+ y: h.a.y
295
+ };
296
+ if (strictlyInside(point.x, ...span(h, "x")) && strictlyInside(point.y, ...span(v, "y")) && clear(point)) crossings++;
297
+ continue;
298
+ }
299
+ const shared$1 = sharedLength(s, t);
300
+ if (shared$1 > EPS && clear(s.a) && clear(s.b)) overlap += shared$1;
301
+ }
302
+ }
303
+ const xs = bounds.map((point) => point.x).filter(Number.isFinite), ys = bounds.map((point) => point.y).filter(Number.isFinite);
304
+ const area = xs.length ? (Math.max(...xs) - Math.min(...xs)) * (Math.max(...ys) - Math.min(...ys)) : 0;
305
+ return {
306
+ defects,
307
+ crossings,
308
+ overlap,
309
+ bends,
310
+ length,
311
+ area
312
+ };
313
+ }
314
+ function sharedLength(s, t) {
315
+ if (horizontal(s) && horizontal(t) && Math.abs(s.a.y - t.a.y) < EPS) return overlapLength(span(s, "x"), span(t, "x"));
316
+ if (vertical(s) && vertical(t) && Math.abs(s.a.x - t.a.x) < EPS) return overlapLength(span(s, "y"), span(t, "y"));
317
+ return 0;
318
+ }
319
+ const TOLERANCE = .02;
320
+ /** True when `candidate` is better at the first measure where the two layouts differ. */
321
+ function isBetterLayout(candidate, current) {
322
+ for (const key of [
323
+ "defects",
324
+ "crossings",
325
+ "overlap",
326
+ "bends",
327
+ "length",
328
+ "area"
329
+ ]) {
330
+ const limit = key === "overlap" || key === "length" || key === "area" ? Math.max(TOLERANCE * Math.abs(current[key]), EPS) : 0;
331
+ if (Math.abs(candidate[key] - current[key]) > limit) return candidate[key] < current[key];
332
+ }
333
+ return false;
334
+ }
335
+
336
+ //#endregion
337
+ //#region src/elkjs/orthogonal-junctions.ts
338
+ /** Reconstruct native segment spans from final orthogonal tracks, then emit ELK's unique junctions. */
339
+ function applyOrthogonalJunctions(root, horizontal$1, increasing, mergeEdges, hypernodes, preservedEdges) {
340
+ const edges = root.edges ?? [], ports = [], byPort = /* @__PURE__ */ new Map();
341
+ const explicit = new Map((root.children ?? []).flatMap((n) => (n.ports ?? []).map((p) => [String(p.id), n])));
342
+ const owner = new Map((root.children ?? []).map((node) => [String(node.id), node]));
343
+ for (const [id, node] of explicit) owner.set(id, node);
344
+ const records = edges.flatMap((edge, index) => {
345
+ if (preservedEdges.has(String(edge.id))) return [];
346
+ const sourceOwner = owner.get(String(edge.sources?.[0] ?? edge.source));
347
+ if (sourceOwner !== void 0 && sourceOwner === owner.get(String(edge.targets?.[0] ?? edge.target))) return [];
348
+ const section = edge.sections?.[0];
349
+ if (!section || edge.sections?.length !== 1) return [];
350
+ let points = [
351
+ section.startPoint,
352
+ ...section.bendPoints ?? [],
353
+ section.endPoint
354
+ ];
355
+ if (points.some((p, i) => i > 0 && Math.abs(p.x - points[i - 1].x) > .001 && Math.abs(p.y - points[i - 1].y) > .001)) return [];
356
+ let source = String(edge.sources?.[0] ?? edge.source), target = String(edge.targets?.[0] ?? edge.target);
357
+ const flow$1 = (p) => horizontal$1 ? p.x : p.y;
358
+ if (flow$1(points[0]) > flow$1(points.at(-1)) === increasing) {
359
+ points = [...points].reverse();
360
+ [source, target] = [target, source];
361
+ }
362
+ const key = (ref, side) => explicit.has(ref) ? JSON.stringify(["port", ref]) : mergeEdges || hypernodes.has(ref) ? JSON.stringify([
363
+ "implicit",
364
+ ref,
365
+ side
366
+ ]) : JSON.stringify([
367
+ "edge",
368
+ index,
369
+ side
370
+ ]);
371
+ return [{
372
+ edge,
373
+ points,
374
+ source: key(source, "source"),
375
+ target: key(target, "target")
376
+ }];
377
+ });
378
+ const cross$1 = (p) => horizontal$1 ? p.y : p.x, flow = (p) => horizontal$1 ? p.x : p.y;
379
+ for (const side of ["source", "target"]) {
380
+ const ordered = [...records].sort((a, b) => cross$1(side === "source" ? a.points[0] : a.points.at(-1)) - cross$1(side === "source" ? b.points[0] : b.points.at(-1)));
381
+ for (const record of ordered) {
382
+ const id = record[side];
383
+ if (!byPort.has(id)) {
384
+ const port = {
385
+ id,
386
+ side,
387
+ position: cross$1(side === "source" ? record.points[0] : record.points.at(-1))
388
+ };
389
+ byPort.set(id, port);
390
+ ports.push(port);
391
+ }
392
+ }
393
+ }
394
+ const grouped = require_layered.createOrthogonalHypersegments(ports, records.map((r) => ({
395
+ source: r.source,
396
+ target: r.target
397
+ })));
398
+ const created = /* @__PURE__ */ new Set();
399
+ for (const group of grouped.segments) {
400
+ const members = records.filter((r) => group.ports.includes(r.source));
401
+ if (members.length === 1) {
402
+ delete members[0].edge.junctionPoints;
403
+ continue;
404
+ }
405
+ const tracks = /* @__PURE__ */ new Map();
406
+ const track = (point) => {
407
+ const key = flow(point);
408
+ let t = tracks.get(key);
409
+ if (!t) {
410
+ t = {
411
+ start: Infinity,
412
+ end: -Infinity,
413
+ incoming: [],
414
+ outgoing: []
415
+ };
416
+ tracks.set(key, t);
417
+ }
418
+ t.start = Math.min(t.start, cross$1(point));
419
+ t.end = Math.max(t.end, cross$1(point));
420
+ return t;
421
+ };
422
+ for (const r of members) {
423
+ const bends = r.points.slice(1, -1);
424
+ if (!bends.length) continue;
425
+ for (const p of bends) track(p);
426
+ track(bends[0]).incoming.push(cross$1(bends[0]));
427
+ track(bends.at(-1)).outgoing.push(cross$1(bends.at(-1)));
428
+ for (let i = 1; i < bends.length; i++) if (Math.abs(flow(bends[i]) - flow(bends[i - 1])) > .001) {
429
+ track(bends[i - 1]).outgoing.push(cross$1(bends[i - 1]));
430
+ track(bends[i]).incoming.push(cross$1(bends[i]));
431
+ }
432
+ }
433
+ for (const r of members.filter((r$1) => r$1.points.length === 2)) {
434
+ const start = Math.min(flow(r.points[0]), flow(r.points[1]));
435
+ const end = Math.max(flow(r.points[0]), flow(r.points[1]));
436
+ for (const [position, t] of tracks) {
437
+ if (position < start || position > end) continue;
438
+ t.incoming.push(cross$1(r.points[0]));
439
+ t.outgoing.push(cross$1(r.points[1]));
440
+ }
441
+ }
442
+ for (const r of members) for (let index = 1; index < r.points.length; index++) {
443
+ const a = r.points[index - 1], b = r.points[index];
444
+ if (Math.abs(cross$1(a) - cross$1(b)) >= .001) continue;
445
+ const start = Math.min(flow(a), flow(b)), end = Math.max(flow(a), flow(b));
446
+ for (const [position, t] of tracks) {
447
+ if (position <= start || position >= end) continue;
448
+ t.incoming.push(cross$1(a));
449
+ t.outgoing.push(cross$1(b));
450
+ }
451
+ }
452
+ for (const t of tracks.values()) {
453
+ t.start = Math.min(t.start, ...t.incoming, ...t.outgoing);
454
+ t.end = Math.max(t.end, ...t.incoming, ...t.outgoing);
455
+ }
456
+ for (const port of group.ports) for (const r of members.filter((r$1) => r$1.source === port)) {
457
+ delete r.edge.junctionPoints;
458
+ for (const p of r.points.slice(1, -1)) {
459
+ const t = tracks.get(flow(p)), value = cross$1(p), key = `${p.x}:${p.y}`;
460
+ const boundary = t.incoming.length && t.outgoing.length && (Math.abs(value - Math.min(...t.incoming)) < .001 && Math.abs(value - Math.min(...t.outgoing)) < .001 || Math.abs(value - Math.max(...t.incoming)) < .001 && Math.abs(value - Math.max(...t.outgoing)) < .001);
461
+ if (!created.has(key) && (value > t.start && value < t.end || boundary)) {
462
+ (r.edge.junctionPoints ??= []).push({ ...p });
463
+ created.add(key);
464
+ }
465
+ }
466
+ }
467
+ }
468
+ }
469
+
470
+ //#endregion
471
+ //#region src/elkjs/opposing-tracks.ts
472
+ const option$1 = (options, suffix) => Object.entries(options ?? {}).find(([key]) => key === suffix || key.endsWith(`.${suffix}`))?.[1];
473
+ /**
474
+ * Separate opposite-direction track sharing in a finished ELK JSON layout,
475
+ * across nested containers, in world coordinates.
476
+ */
477
+ function separateElkOpposingTracks(root, options) {
478
+ const orthogonal = (node, inherited) => {
479
+ const value = option$1(node.layoutOptions, "edgeRouting");
480
+ return value === void 0 ? inherited : String(value) === "ORTHOGONAL";
481
+ };
482
+ const rootOrthogonal = orthogonal(root, String(option$1(options, "edgeRouting") ?? "ORTHOGONAL") === "ORTHOGONAL");
483
+ const frames = new Map([[String(root.id), {
484
+ x: 0,
485
+ y: 0
486
+ }]]);
487
+ const routing = new Map([[String(root.id), rootOrthogonal]]);
488
+ const nodes = /* @__PURE__ */ new Map(), leaves = /* @__PURE__ */ new Set(), owner = /* @__PURE__ */ new Map(), ports = /* @__PURE__ */ new Map();
489
+ const place = (parent, offset) => {
490
+ for (const child of parent.children ?? []) {
491
+ const id = String(child.id);
492
+ const at = {
493
+ x: offset.x + (child.x ?? 0),
494
+ y: offset.y + (child.y ?? 0)
495
+ };
496
+ frames.set(id, at);
497
+ routing.set(id, orthogonal(child, routing.get(String(parent.id)) ?? rootOrthogonal));
498
+ nodes.set(id, {
499
+ ...at,
500
+ width: child.width ?? 0,
501
+ height: child.height ?? 0
502
+ });
503
+ owner.set(id, id);
504
+ for (const port of child.ports ?? []) {
505
+ owner.set(String(port.id), id);
506
+ ports.set(String(port.id), {
507
+ node: id,
508
+ rect: {
509
+ x: at.x + (port.x ?? 0),
510
+ y: at.y + (port.y ?? 0),
511
+ width: port.width ?? 0,
512
+ height: port.height ?? 0
513
+ }
514
+ });
515
+ }
516
+ if (child.children?.length) place(child, at);
517
+ else leaves.add(id);
518
+ }
519
+ };
520
+ place(root, {
521
+ x: 0,
522
+ y: 0
523
+ });
524
+ const records = [];
525
+ const collect = (parent) => {
526
+ for (const edge of parent.edges ?? []) {
527
+ if ((edge.sections?.length ?? 0) > 1) continue;
528
+ const container = String(edge.container ?? parent.id);
529
+ const offset = frames.get(container) ?? {
530
+ x: 0,
531
+ y: 0
532
+ };
533
+ const points = (edge.sections ?? []).flatMap((section) => [
534
+ section.startPoint,
535
+ ...section.bendPoints ?? [],
536
+ section.endPoint
537
+ ].map((p) => ({
538
+ x: p.x + offset.x,
539
+ y: p.y + offset.y
540
+ })));
541
+ const axisParallel = points.every((p, i) => i === 0 || Math.abs(p.x - points[i - 1].x) < 1e-6 || Math.abs(p.y - points[i - 1].y) < 1e-6);
542
+ const ends = [edge.sources?.[0] ?? edge.source, edge.targets?.[0] ?? edge.target].flatMap((end) => {
543
+ const id = owner.get(String(end));
544
+ return id === void 0 ? [] : [id];
545
+ });
546
+ const portOf = (end) => ports.has(String(end)) ? String(end) : void 0;
547
+ records.push({
548
+ edge,
549
+ offset,
550
+ route: {
551
+ id: String(edge.id),
552
+ ends,
553
+ points,
554
+ ports: [portOf(edge.sources?.[0] ?? edge.source), portOf(edge.targets?.[0] ?? edge.target)],
555
+ labels: (edge.labels ?? []).flatMap((label) => label.width && label.height ? [{
556
+ x: (label.x ?? 0) + offset.x,
557
+ y: (label.y ?? 0) + offset.y,
558
+ width: label.width,
559
+ height: label.height
560
+ }] : []),
561
+ movable: points.length > 0 && axisParallel && (routing.get(container) ?? rootOrthogonal)
562
+ }
563
+ });
564
+ }
565
+ for (const child of parent.children ?? []) collect(child);
566
+ };
567
+ collect(root);
568
+ if (!records.some((record) => record.route.movable)) return;
569
+ const spacing = Number(option$1(options, "spacing.edgeEdge") ?? 10);
570
+ const changed = require_layered.separateOpposingTracks({
571
+ routes: records.map((record) => record.route),
572
+ nodes,
573
+ leaves,
574
+ ports,
575
+ spacing: Number.isFinite(spacing) && spacing > 0 ? spacing : 10
576
+ });
577
+ if (!changed.size) return;
578
+ const routes = new Map(records.map((record) => [record.route.id, changed.get(record.route.id) ?? record.route.points]));
579
+ const on = (point, points) => points.some((b, i) => {
580
+ const a = points[i - 1];
581
+ return a !== void 0 && point.x >= Math.min(a.x, b.x) - 1e-6 && point.x <= Math.max(a.x, b.x) + 1e-6 && point.y >= Math.min(a.y, b.y) - 1e-6 && point.y <= Math.max(a.y, b.y) + 1e-6;
582
+ });
583
+ for (const { edge, offset, route } of records) {
584
+ const points = changed.get(route.id);
585
+ if (points) {
586
+ const section = edge.sections[0];
587
+ const local = points.map((p) => ({
588
+ x: p.x - offset.x,
589
+ y: p.y - offset.y
590
+ }));
591
+ section.startPoint = local[0];
592
+ section.endPoint = local.at(-1);
593
+ section.bendPoints = local.slice(1, -1);
594
+ }
595
+ if (!edge.junctionPoints?.length) continue;
596
+ const own = routes.get(route.id);
597
+ edge.junctionPoints = edge.junctionPoints.filter((junction) => {
598
+ const point = {
599
+ x: junction.x + offset.x,
600
+ y: junction.y + offset.y
601
+ };
602
+ return on(point, own) && [...routes].some(([id, other]) => id !== route.id && on(point, other));
603
+ });
604
+ if (!edge.junctionPoints.length) delete edge.junctionPoints;
605
+ }
606
+ }
607
+
608
+ //#endregion
609
+ //#region src/elkjs/statechart.ts
610
+ function option(node, suffix) {
611
+ const settings = {
612
+ ...node.properties,
613
+ ...node.layoutOptions
614
+ };
615
+ return settings[suffix] ?? settings[`elk.${suffix}`] ?? settings[`org.eclipse.elk.${suffix}`];
616
+ }
617
+ const endpoint$1 = (edge, source) => source ? edge.sources?.[0] ?? edge.source : edge.targets?.[0] ?? edge.target;
618
+ /** Compile semantic hints into settings implemented by this package's ELK adapter. */
619
+ function compileStatechartLayout(input, scopes) {
620
+ const graph = structuredClone(input);
621
+ const paths = Object.create(null);
622
+ const commonExits = Object.create(null);
623
+ const containers = /* @__PURE__ */ new Map();
624
+ const edges = [];
625
+ const collect = (node) => {
626
+ if (node.id === void 0 || containers.has(String(node.id))) throw new Error("Statechart nodes require unique IDs");
627
+ containers.set(String(node.id), node);
628
+ edges.push(...node.edges ?? []);
629
+ node.children?.forEach(collect);
630
+ };
631
+ collect(graph);
632
+ for (const [id, scope] of Object.entries(scopes)) {
633
+ const container = containers.get(id);
634
+ if (!container) throw new Error(`Unknown statechart container: ${id}`);
635
+ const children = container.children ?? [];
636
+ const ids = new Set(children.map((node) => node.id));
637
+ const owner = /* @__PURE__ */ new Map();
638
+ const own = (node, childId) => {
639
+ owner.set(node.id, childId);
640
+ node.ports?.forEach((port) => owner.set(port.id, childId));
641
+ node.children?.forEach((descendant) => own(descendant, childId));
642
+ };
643
+ children.forEach((child) => own(child, child.id));
644
+ const localEdges = edges.filter((edge) => owner.has(endpoint$1(edge, true)) && owner.has(endpoint$1(edge, false)));
645
+ const successors = new Map(children.map((node) => [node.id, /* @__PURE__ */ new Set()]));
646
+ const predecessors = new Map(children.map((node) => [node.id, /* @__PURE__ */ new Set()]));
647
+ for (const edge of localEdges) {
648
+ const source = owner.get(endpoint$1(edge, true));
649
+ const target = owner.get(endpoint$1(edge, false));
650
+ if (source === target) continue;
651
+ successors.get(source).add(target);
652
+ predecessors.get(target).add(source);
653
+ }
654
+ const exits = children.filter((node) => successors.get(node.id).size === 0 && predecessors.get(node.id).size >= 2).map((node) => node.id);
655
+ commonExits[id] = exits;
656
+ const path = [...scope.preferredPath ?? (scope.initialNodeId === void 0 ? [] : [scope.initialNodeId])];
657
+ if (new Set(path).size !== path.length || path.some((nodeId) => !ids.has(nodeId))) throw new Error(`Path must contain distinct direct children of ${id}`);
658
+ if (scope.initialNodeId !== void 0 && (!ids.has(scope.initialNodeId) || path[0] !== scope.initialNodeId)) throw new Error(`Path must start at initial child of ${id}`);
659
+ if (scope.preferredPath) {
660
+ for (let i = 1; i < path.length; i++) if (!successors.get(path[i - 1])?.has(path[i])) throw new Error(`Disconnected preferred path in ${id}`);
661
+ } else while (path.length) {
662
+ const next = [...successors.get(path.at(-1))].filter((nodeId) => !path.includes(nodeId) && !exits.includes(nodeId));
663
+ if (next.length !== 1) break;
664
+ path.push(next[0]);
665
+ }
666
+ paths[id] = path;
667
+ container.children = [
668
+ ...path,
669
+ ...children.map((node) => node.id).filter((nodeId) => !path.includes(nodeId) && !exits.includes(nodeId)),
670
+ ...exits.filter((nodeId) => !path.includes(nodeId))
671
+ ].map((nodeId) => children.find((node) => node.id === nodeId));
672
+ container.layoutOptions = {
673
+ ...path.length > 1 && option(container, "layered.cycleBreaking.strategy") === void 0 ? { "elk.layered.cycleBreaking.strategy": "MODEL_ORDER" } : {},
674
+ ...path.length > 1 && option(container, "layered.feedbackEdges") === void 0 ? { "elk.layered.feedbackEdges": true } : {},
675
+ ...container.layoutOptions,
676
+ ...scope.direction ? { direction: scope.direction } : {}
677
+ };
678
+ }
679
+ return {
680
+ graph,
681
+ paths,
682
+ commonExits
683
+ };
684
+ }
685
+ const intersects = (a, b) => a.x < b.x + b.width && b.x < a.x + a.width && a.y < b.y + b.height && b.y < a.y + a.height;
686
+ const cross = (a, b, c) => (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
687
+ /** Diagnostic objective, not a proof of geometric validity. Coordinates are container-relative. */
688
+ function scoreStatechartLayout(graph, paths = {}) {
689
+ const score = {
690
+ invalid: 0,
691
+ overlaps: 0,
692
+ pathOrder: 0,
693
+ crossings: 0,
694
+ bends: 0,
695
+ routeLength: 0
696
+ };
697
+ const segments = [];
698
+ const boxes = [];
699
+ const rect = (shape, x, y) => {
700
+ if ([
701
+ shape.x,
702
+ shape.y,
703
+ shape.width,
704
+ shape.height
705
+ ].some((value) => value === void 0 || !Number.isFinite(value)) || (shape.width ?? 0) < 0 || (shape.height ?? 0) < 0) score.invalid++;
706
+ return {
707
+ x: x + (shape.x ?? 0),
708
+ y: y + (shape.y ?? 0),
709
+ width: shape.width ?? 0,
710
+ height: shape.height ?? 0
711
+ };
712
+ };
713
+ const visit = (node, x, y, ancestors, inheritedDirection = "RIGHT") => {
714
+ const direction = String(option(node, "direction") ?? inheritedDirection).toUpperCase();
715
+ const horizontal$1 = direction === "RIGHT" || direction === "LEFT";
716
+ const sign = direction === "LEFT" || direction === "UP" ? -1 : 1;
717
+ const path = paths[String(node.id)] ?? [];
718
+ for (let i = 1; i < path.length; i++) {
719
+ const a = node.children?.find((child) => child.id === path[i - 1]);
720
+ const b = node.children?.find((child) => child.id === path[i]);
721
+ if (!a || !b || sign * (horizontal$1 ? b.x - a.x : b.y - a.y) <= 0) score.pathOrder++;
722
+ }
723
+ for (const child of node.children ?? []) {
724
+ if (String(option(child, "noLayout")).toLowerCase() === "true") continue;
725
+ const box = rect(child, x, y);
726
+ if ((child.x ?? 0) < 0 || (child.y ?? 0) < 0 || (child.x ?? 0) + (child.width ?? 0) > (node.width ?? Infinity) + .001 || (child.y ?? 0) + (child.height ?? 0) > (node.height ?? Infinity) + .001) score.invalid++;
727
+ boxes.push({
728
+ rect: box,
729
+ ancestors,
730
+ node: child
731
+ });
732
+ visit(child, box.x, box.y, [...ancestors, child], direction);
733
+ }
734
+ for (const edge of node.edges ?? []) {
735
+ if (String(option(edge, "noLayout")).toLowerCase() === "true") continue;
736
+ if (!edge.sections?.length) score.invalid++;
737
+ for (const label of edge.labels ?? []) {
738
+ if (String(option(label, "noLayout")).toLowerCase() === "true") continue;
739
+ boxes.push({
740
+ rect: rect(label, x, y),
741
+ ancestors
742
+ });
743
+ }
744
+ for (const section of edge.sections ?? []) {
745
+ const points = [
746
+ section.startPoint,
747
+ ...section.bendPoints ?? [],
748
+ section.endPoint
749
+ ];
750
+ score.bends += section.bendPoints?.length ?? 0;
751
+ for (const point of points) if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) score.invalid++;
752
+ for (let i = 1; i < points.length; i++) {
753
+ const a = {
754
+ x: x + points[i - 1].x,
755
+ y: y + points[i - 1].y
756
+ };
757
+ const b = {
758
+ x: x + points[i].x,
759
+ y: y + points[i].y
760
+ };
761
+ score.routeLength += Math.hypot(b.x - a.x, b.y - a.y);
762
+ segments.push({
763
+ a,
764
+ b,
765
+ edge
766
+ });
767
+ }
768
+ }
769
+ }
770
+ };
771
+ visit(graph, 0, 0, [graph]);
772
+ for (let i = 0; i < boxes.length; i++) for (let j = i + 1; j < boxes.length; j++) {
773
+ const a = boxes[i], b = boxes[j];
774
+ if (a.node && b.ancestors.includes(a.node) || b.node && a.ancestors.includes(b.node)) continue;
775
+ if (intersects(a.rect, b.rect)) score.overlaps++;
776
+ }
777
+ for (let i = 0; i < segments.length; i++) for (let j = i + 1; j < segments.length; j++) {
778
+ const a = segments[i], b = segments[j];
779
+ if (a.edge === b.edge) continue;
780
+ if (cross(a.a, a.b, b.a) * cross(a.a, a.b, b.b) < 0 && cross(b.a, b.b, a.a) * cross(b.a, b.b, a.b) < 0) score.crossings++;
781
+ }
782
+ if (!Number.isFinite(score.routeLength)) score.routeLength = Number.MAX_VALUE;
783
+ return score;
784
+ }
785
+ const objectives = [
786
+ "invalid",
787
+ "overlaps",
788
+ "pathOrder",
789
+ "crossings",
790
+ "bends",
791
+ "routeLength"
792
+ ];
793
+ function better(a, b) {
794
+ for (const key of objectives) if (a[key] !== b[key]) return a[key] < b[key];
795
+ return false;
796
+ }
797
+ /** Each attempt starts from original input. No post-layout geometry edits. */
798
+ async function layoutStatechart(input, options) {
799
+ const maxAttempts = options.maxAttempts ?? 3;
800
+ if (!Number.isInteger(maxAttempts) || maxAttempts < 1 || maxAttempts > 3) throw new RangeError("maxAttempts must be an integer from 1 to 3");
801
+ const plan = compileStatechartLayout(input, options.scopes);
802
+ const attempts = [];
803
+ let selected;
804
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
805
+ const candidate = structuredClone(attempt === 0 ? input : plan.graph);
806
+ const configure = (node) => {
807
+ const scope = Object.hasOwn(options.scopes, String(node.id)) ? options.scopes[String(node.id)] : void 0;
808
+ if (scope?.direction) node.layoutOptions = {
809
+ ...node.layoutOptions,
810
+ direction: scope.direction
811
+ };
812
+ if (attempt === 2 && scope) node.layoutOptions = {
813
+ ...node.layoutOptions,
814
+ "spacing.nodeNode": Math.max(40, Number(option(node, "spacing.nodeNode") ?? 20) * 1.5),
815
+ "layered.spacing.nodeNodeBetweenLayers": Math.max(60, Number(option(node, "layered.spacing.nodeNodeBetweenLayers") ?? 20) * 1.5)
816
+ };
817
+ if (attempt === 2 && scope) {
818
+ const groups = /* @__PURE__ */ new Map();
819
+ for (const edge of node.edges ?? []) {
820
+ if (!edge.labels?.length) continue;
821
+ const key = JSON.stringify([endpoint$1(edge, true), endpoint$1(edge, false)]);
822
+ const group = groups.get(key) ?? [];
823
+ group.push(edge);
824
+ groups.set(key, group);
825
+ }
826
+ for (const group of groups.values()) for (let i = 1; i < Math.min(group.length, 3); i++) {
827
+ const edge = group[i];
828
+ if (option(edge, "edgeLabels.placement") === void 0 && edge.labels.every((label) => option(label, "edgeLabels.placement") === void 0)) {
829
+ const placement = i === 1 ? "HEAD" : "TAIL";
830
+ edge.layoutOptions = {
831
+ ...edge.layoutOptions,
832
+ "elk.edgeLabels.placement": placement
833
+ };
834
+ edge.labels = edge.labels.map((label) => ({
835
+ ...label,
836
+ layoutOptions: {
837
+ ...label.layoutOptions,
838
+ "elk.edgeLabels.placement": placement
839
+ }
840
+ }));
841
+ }
842
+ }
843
+ }
844
+ node.children?.forEach(configure);
845
+ };
846
+ configure(candidate);
847
+ const graph = await new ELK().layout(candidate);
848
+ const score = scoreStatechartLayout(graph, plan.paths);
849
+ attempts.push({
850
+ kind: attempt === 0 ? "baseline" : attempt === 1 ? "policy" : "spacing",
851
+ score
852
+ });
853
+ if (!selected || better(score, selected.score)) selected = {
854
+ graph,
855
+ score,
856
+ attempt
857
+ };
858
+ }
859
+ return {
860
+ ...selected,
861
+ attempts,
862
+ paths: plan.paths,
863
+ commonExits: plan.commonExits
864
+ };
865
+ }
866
+
867
+ //#endregion
868
+ //#region src/elkjs/routes.ts
869
+ /** Normalize any ELK layout's section geometry, preserving ports and hyperedge junctions. */
870
+ function getElkRoutes(graph, options = {}) {
871
+ const shapes = /* @__PURE__ */ new Map(), frames = /* @__PURE__ */ new Map();
872
+ const edges = [];
873
+ function visit(node, offset, inherited) {
874
+ const position = {
875
+ x: offset.x + (node.x ?? 0),
876
+ y: offset.y + (node.y ?? 0)
877
+ };
878
+ const routing = node.layoutOptions?.["elk.edgeRouting"] ?? node.layoutOptions?.["org.eclipse.elk.edgeRouting"] ?? inherited;
879
+ frames.set(node.id, position);
880
+ shapes.set(node.id, {
881
+ ref: {
882
+ kind: "node",
883
+ nodeId: node.id
884
+ },
885
+ center: {
886
+ x: position.x + (node.width ?? 0) / 2,
887
+ y: position.y + (node.height ?? 0) / 2
888
+ }
889
+ });
890
+ for (const port of node.ports ?? []) shapes.set(port.id, {
891
+ ref: {
892
+ kind: "node",
893
+ nodeId: node.id,
894
+ port: port.id
895
+ },
896
+ center: {
897
+ x: position.x + (port.x ?? 0) + (port.width ?? 0) / 2,
898
+ y: position.y + (port.y ?? 0) + (port.height ?? 0) / 2
899
+ }
900
+ });
901
+ for (const edge of node.edges ?? []) edges.push({
902
+ edge,
903
+ offset: position,
904
+ routing: edge.layoutOptions?.["elk.edgeRouting"] ?? edge.layoutOptions?.["org.eclipse.elk.edgeRouting"] ?? routing
905
+ });
906
+ for (const child of node.children ?? []) visit(child, position, routing);
907
+ }
908
+ visit(graph, {
909
+ x: 0,
910
+ y: 0
911
+ }, options.routing ?? "POLYLINE");
912
+ const result = /* @__PURE__ */ new Map();
913
+ for (const { edge, offset: inheritedOffset, routing } of edges) {
914
+ const offset = edge.container ? frames.get(edge.container) ?? inheritedOffset : inheritedOffset;
915
+ const point = (p) => ({
916
+ x: p.x + offset.x,
917
+ y: p.y + offset.y
918
+ });
919
+ const parents = /* @__PURE__ */ new Map();
920
+ const root = (key) => {
921
+ let current = key;
922
+ while (parents.has(current)) current = parents.get(current);
923
+ return current;
924
+ };
925
+ const join = (a, b) => {
926
+ const x = root(a), y = root(b);
927
+ if (x !== y) parents.set(x < y ? y : x, x < y ? x : y);
928
+ };
929
+ for (const section of edge.sections ?? []) {
930
+ for (const id of section.outgoingSections ?? []) join(`${section.id}:end`, `${id}:start`);
931
+ for (const id of section.incomingSections ?? []) join(`${id}:end`, `${section.id}:start`);
932
+ }
933
+ const sections = [];
934
+ let malformed = false;
935
+ for (const section of edge.sections ?? []) {
936
+ const points = [
937
+ section.startPoint,
938
+ ...section.bendPoints ?? [],
939
+ section.endPoint
940
+ ].map(point);
941
+ let path;
942
+ if (points.some((p) => !Number.isFinite(p.x) || !Number.isFinite(p.y))) {
943
+ malformed = true;
944
+ for (let i = 0; i < points.length; i++) if (!Number.isFinite(points[i].x) || !Number.isFinite(points[i].y)) points[i] = {
945
+ x: offset.x + i * 20,
946
+ y: offset.y
947
+ };
948
+ }
949
+ if (routing === "SPLINES") if ((points.length - 1) % 3 || points.length < 4) {
950
+ malformed = true;
951
+ path = require_routing.pathFromPoints([points[0], points.at(-1)]);
952
+ } else path = require_routing.pathFromSplinePoints(points);
953
+ else path = require_routing.pathFromPoints(points);
954
+ const fromShape = section.incomingShape ?? (!section.incomingSections?.length && edge.sources.length === 1 ? edge.sources[0] : void 0);
955
+ const toShape = section.outgoingShape ?? (!section.outgoingSections?.length && edge.targets.length === 1 ? edge.targets[0] : void 0);
956
+ sections.push({
957
+ id: section.id,
958
+ from: fromShape ? shapes.get(fromShape)?.ref ?? {
959
+ kind: "node",
960
+ nodeId: fromShape
961
+ } : {
962
+ kind: "junction",
963
+ id: `${edge.id}:${root(`${section.id}:start`)}`
964
+ },
965
+ to: toShape ? shapes.get(toShape)?.ref ?? {
966
+ kind: "node",
967
+ nodeId: toShape
968
+ } : {
969
+ kind: "junction",
970
+ id: `${edge.id}:${root(`${section.id}:end`)}`
971
+ },
972
+ path
973
+ });
974
+ }
975
+ if (!sections.length) {
976
+ malformed = true;
977
+ const source = shapes.get(edge.sources[0] ?? ""), origin = source?.center ?? {
978
+ x: 0,
979
+ y: 0
980
+ };
981
+ const targets = [...edge.targets, ...edge.sources.slice(1)];
982
+ for (const [i, id] of (targets.length ? targets : [""]).entries()) {
983
+ const target = shapes.get(id);
984
+ sections.push({
985
+ id: `${edge.id}:fallback:${i}`,
986
+ from: source?.ref ?? {
987
+ kind: "node",
988
+ nodeId: edge.sources[0] ?? ""
989
+ },
990
+ to: target?.ref ?? {
991
+ kind: "node",
992
+ nodeId: id
993
+ },
994
+ path: require_routing.pathFromPoints([origin, target?.center ?? {
995
+ x: origin.x + 40,
996
+ y: origin.y + 40
997
+ }])
998
+ });
999
+ }
1000
+ }
1001
+ result.set(edge.id, require_routing.freeze({
1002
+ edgeId: edge.id,
1003
+ sections,
1004
+ status: malformed ? "fallback" : "routed",
1005
+ diagnostics: malformed ? [{
1006
+ code: "MISSING_GEOMETRY",
1007
+ edgeId: edge.id,
1008
+ message: "ELK sections contain missing or invalid geometry; rendered fallback paths"
1009
+ }] : []
1010
+ }));
1011
+ }
1012
+ return result;
1013
+ }
1014
+
1015
+ //#endregion
1016
+ //#region src/elkjs/index.ts
1017
+ function isLayoutEdgeLabel(label) {
1018
+ return getBooleanOption(label.layoutOptions ?? {}, "noLayout") !== true && (Boolean(label.text) || label.width !== void 0 || label.height !== void 0);
1019
+ }
1020
+ var ELK = class {
1021
+ #options;
1022
+ #algorithmIds;
1023
+ constructor(options = {}) {
1024
+ this.#options = options;
1025
+ this.#algorithmIds = new Set([
1026
+ "box",
1027
+ "fixed",
1028
+ "random",
1029
+ "rectpacking",
1030
+ "sporeCompaction",
1031
+ "sporeOverlap",
1032
+ ...(options.algorithms ?? ["layered"]).filter((id) => id === "layered")
1033
+ ]);
1034
+ }
1035
+ async knownLayoutAlgorithms() {
1036
+ return [...this.#algorithmIds].map((id) => ({
1037
+ id: id === "layered" ? "org.eclipse.elk.layered" : id,
1038
+ name: {
1039
+ layered: "Layered",
1040
+ box: "Box",
1041
+ fixed: "Fixed",
1042
+ random: "Random",
1043
+ rectpacking: "Rectangle Packing",
1044
+ sporeCompaction: "SPOrE Compaction",
1045
+ sporeOverlap: "SPOrE Overlap Removal"
1046
+ }[id],
1047
+ category: id === "layered" ? "layered" : "other",
1048
+ knownOptions: id === "layered" ? require_layered.elkLayeredOptionDefinitions.map((definition) => definition.elkId) : id === "box" ? [
1049
+ "padding",
1050
+ "spacing.nodeNode",
1051
+ "aspectRatio",
1052
+ "box.packingMode"
1053
+ ] : id === "random" ? [
1054
+ "padding",
1055
+ "spacing.nodeNode",
1056
+ "aspectRatio",
1057
+ "randomSeed"
1058
+ ] : ["position", "bendPoints"]
1059
+ }));
1060
+ }
1061
+ async knownLayoutOptions() {
1062
+ return [{
1063
+ id: "org.eclipse.elk.algorithm",
1064
+ name: "Layout Algorithm",
1065
+ type: "STRING"
1066
+ }, ...require_layered.elkLayeredOptionDefinitions.map((definition) => ({
1067
+ id: definition.elkId,
1068
+ name: definition.name,
1069
+ type: definition.type,
1070
+ targets: [...definition.targets]
1071
+ }))];
1072
+ }
1073
+ async knownLayoutCategories() {
1074
+ return [{
1075
+ id: "layered",
1076
+ name: "Layered",
1077
+ knownLayouters: this.#algorithmIds.has("layered") ? ["layered"] : []
1078
+ }, {
1079
+ id: "other",
1080
+ name: "Other",
1081
+ knownLayouters: [
1082
+ "box",
1083
+ "fixed",
1084
+ "random"
1085
+ ]
1086
+ }];
1087
+ }
1088
+ terminateWorker() {}
1089
+ async layout(graph, arguments_ = {}) {
1090
+ const options = {
1091
+ ...this.#options.defaultLayoutOptions,
1092
+ ...arguments_.layoutOptions
1093
+ };
1094
+ const variants = crossingVariants(options, graph);
1095
+ const pristine = variants.length ? structuredClone(graph) : void 0;
1096
+ let best = separated(await this.#layoutWithoutDefectiveCompaction(graph, arguments_), options);
1097
+ if (!pristine) return best;
1098
+ let quality = measureLayout(best);
1099
+ if (quality.defects === 0 && !thorough(options, graph)) return best;
1100
+ for (const variant of variants) {
1101
+ const candidate = separated(await this.#layoutWithoutDefectiveCompaction(structuredClone(pristine), {
1102
+ ...arguments_,
1103
+ layoutOptions: {
1104
+ ...arguments_.layoutOptions,
1105
+ ...variant
1106
+ }
1107
+ }), options);
1108
+ const candidateQuality = measureLayout(candidate);
1109
+ if (isBetterLayout(candidateQuality, quality)) {
1110
+ best = candidate;
1111
+ quality = candidateQuality;
1112
+ }
1113
+ }
1114
+ return best;
1115
+ }
1116
+ async #layoutWithoutDefectiveCompaction(graph, arguments_) {
1117
+ const pristine = usesPostCompaction({
1118
+ ...this.#options.defaultLayoutOptions,
1119
+ ...arguments_.layoutOptions
1120
+ }, graph) ? structuredClone(graph) : void 0;
1121
+ const result = await this.#layout(graph, arguments_);
1122
+ const defects = pristine ? measureLayout(result).defects : 0;
1123
+ if (!pristine || defects === 0) return result;
1124
+ const strategy = { "elk.layered.compaction.postCompaction.strategy": "NONE" };
1125
+ const uncompacted = await this.#layout(disablePostCompaction(pristine), {
1126
+ ...arguments_,
1127
+ layoutOptions: {
1128
+ ...arguments_.layoutOptions,
1129
+ ...strategy
1130
+ }
1131
+ });
1132
+ return measureLayout(uncompacted).defects < defects ? uncompacted : result;
1133
+ }
1134
+ async #layout(graph, arguments_ = {}, compoundLayout = false) {
1135
+ const prepared = await this.#prepareLayout(graph, arguments_, compoundLayout);
1136
+ const orders = coordinatePreparedScopes(prepared);
1137
+ return await prepared.finish(orders);
1138
+ }
1139
+ async #prepareLayout(graph, arguments_ = {}, compoundLayout = false) {
1140
+ const startedAt = performance.now();
1141
+ if (graph === void 0 || graph === null) throw new TypeError("Missing mandatory parameter: graph");
1142
+ if (typeof graph.id !== "string" && !(typeof graph.id === "number" && Number.isInteger(graph.id))) throw new TypeError("Graph id must be a string or integer");
1143
+ delete graph.logging;
1144
+ const layoutOptions = {
1145
+ ...this.#options.defaultLayoutOptions,
1146
+ ...arguments_.layoutOptions,
1147
+ ...graph.properties,
1148
+ ...graph.layoutOptions
1149
+ };
1150
+ getOption(layoutOptions, "debugMode");
1151
+ getOption(layoutOptions, "interactiveLayout");
1152
+ getOption(layoutOptions, "layered.generatePositionAndLayerIds");
1153
+ getOption(layoutOptions, "topdown.scaleFactor");
1154
+ getOption(layoutOptions, "contentAlignment");
1155
+ for (const child of graph.children ?? []) {
1156
+ const childOptions = child.layoutOptions ?? {};
1157
+ getOption(childOptions, "layered.layering.layerId");
1158
+ getOption(childOptions, "layered.crossingMinimization.positionId");
1159
+ getOption(childOptions, "layered.layering.layerChoiceConstraint");
1160
+ getOption(childOptions, "layered.crossingMinimization.positionChoiceConstraint");
1161
+ getOption(childOptions, "layered.crossingMinimization.inLayerPredOf");
1162
+ getOption(childOptions, "layered.crossingMinimization.inLayerSuccOf");
1163
+ getOption(childOptions, "topdown.scaleFactor");
1164
+ getOption(childOptions, "layered.considerModelOrder.groupModelOrder.componentGroupId");
1165
+ for (const port of child.ports ?? []) getOption(port.layoutOptions ?? {}, "layered.considerModelOrder.groupModelOrder.componentGroupId");
1166
+ }
1167
+ for (const edge of graph.edges ?? []) getOption(edge.layoutOptions ?? {}, "layered.considerModelOrder.groupModelOrder.componentGroupId");
1168
+ const requestedAlgorithm = String(getOption(layoutOptions, "algorithm") ?? "layered");
1169
+ const algorithm = requestedAlgorithm.replace(/^(?:org\.eclipse\.)?elk\./, "");
1170
+ if (algorithm !== "layered" && algorithm !== "box" && algorithm !== "fixed" && algorithm !== "random" && algorithm !== "rectpacking" && algorithm !== "sporeCompaction" && algorithm !== "sporeOverlap") throw new Error(`org.eclipse.elk.core.UnsupportedConfigurationException: Layout algorithm '${requestedAlgorithm}' not found`);
1171
+ const preparedChildren = /* @__PURE__ */ new Map();
1172
+ const finishChildren = [];
1173
+ const hasHierarchy = (graph.children ?? []).some((child) => (child.children?.length ?? 0) > 0);
1174
+ const insideSelfLoopBaseHeightByNodeId = /* @__PURE__ */ new Map();
1175
+ const hierarchyRestorations = [];
1176
+ const boundaryRoutes = /* @__PURE__ */ new Map();
1177
+ const syntheticPortIds = /* @__PURE__ */ new Set();
1178
+ const authoredPortsByCompound = /* @__PURE__ */ new Map();
1179
+ const authoredOptionsByCompound = /* @__PURE__ */ new Map();
1180
+ const originalHierarchyEndpoints = new Map((graph.edges ?? []).map((edge) => [edge, {
1181
+ sourceId: String(edge.sources?.[0] ?? edge.source),
1182
+ targetId: String(edge.targets?.[0] ?? edge.target),
1183
+ sources: edge.sources,
1184
+ targets: edge.targets,
1185
+ source: edge.source,
1186
+ target: edge.target
1187
+ }]));
1188
+ const hierarchyHandling = getOption(layoutOptions, "hierarchyHandling");
1189
+ const topdownLayout = getBooleanOption(layoutOptions, "topdownLayout") === true;
1190
+ const separateHierarchy = hasHierarchy && hierarchyHandling !== void 0 && hierarchyHandling !== "INCLUDE_CHILDREN";
1191
+ if (hasHierarchy && topdownLayout && hierarchyHandling === "INCLUDE_CHILDREN") throw new Error("org.eclipse.elk.core.UnsupportedConfigurationException: Topdown layout cannot be used together with hierarchy handling.");
1192
+ if (separateHierarchy) {
1193
+ const hasCrossHierarchyEdge = (container) => {
1194
+ const directEndpointIds = /* @__PURE__ */ new Set();
1195
+ for (const child of container.children ?? []) {
1196
+ directEndpointIds.add(String(child.id));
1197
+ for (const port of child.ports ?? []) directEndpointIds.add(String(port.id));
1198
+ }
1199
+ if ((container.edges ?? []).some((edge) => {
1200
+ const sourceId = String(edge.sources?.[0] ?? edge.source);
1201
+ const targetId = String(edge.targets?.[0] ?? edge.target);
1202
+ return !directEndpointIds.has(sourceId) || !directEndpointIds.has(targetId);
1203
+ })) return true;
1204
+ return (container.children ?? []).some((child) => hasCrossHierarchyEdge(child));
1205
+ };
1206
+ if (hasCrossHierarchyEdge(graph)) throw new Error("org.eclipse.elk.core.UnsupportedGraphException: Hierarchical edges require INCLUDE_CHILDREN");
1207
+ for (const child of graph.children ?? []) {
1208
+ if ((child.children?.length ?? 0) === 0) continue;
1209
+ await this.#layout(child, {
1210
+ ...arguments_,
1211
+ layoutOptions: {
1212
+ ...arguments_.layoutOptions,
1213
+ ...child.layoutOptions,
1214
+ hierarchyHandling
1215
+ },
1216
+ logging: false,
1217
+ measureExecutionTime: false
1218
+ });
1219
+ }
1220
+ }
1221
+ if (hasHierarchy && topdownLayout) {
1222
+ if (getOption(layoutOptions, "topdown.nodeType") === void 0) throw new Error(`${String(graph.id)} has not been assigned a top-down node type.`);
1223
+ for (const child of graph.children ?? []) {
1224
+ if ((child.children?.length ?? 0) === 0) continue;
1225
+ const childOptions = {
1226
+ ...layoutOptions,
1227
+ ...child.layoutOptions
1228
+ };
1229
+ const childPadding = parsePadding(getOption(childOptions, "padding"), 12);
1230
+ const parallelNode = getOption(childOptions, "topdown.nodeType") === "PARALLEL_NODE";
1231
+ const width = getNumberOption(childOptions, "topdown.hierarchicalNodeWidth") ?? (parallelNode ? 150 : 0);
1232
+ const aspectRatio = getNumberOption(childOptions, "topdown.hierarchicalNodeAspectRatio") ?? (parallelNode ? 1.414 : 1 / Math.sqrt(2));
1233
+ child.width = Math.max(child.width ?? 0, width + childPadding.left + childPadding.right);
1234
+ child.height = Math.max(child.height ?? 0, width / aspectRatio + childPadding.top + childPadding.bottom);
1235
+ }
1236
+ } else if (hasHierarchy && !separateHierarchy) {
1237
+ const scopeSegmentOrder = segmentOrderByScope.get(graph) ?? elkSegmentOrder(graph, graph, originalHierarchyEndpoints, false);
1238
+ for (const child of graph.children ?? []) {
1239
+ if ((child.children?.length ?? 0) === 0) continue;
1240
+ const descendantOwnerByEndpointId = /* @__PURE__ */ new Map();
1241
+ const collectDescendants = (node) => {
1242
+ for (const descendant of node.children ?? []) {
1243
+ descendantOwnerByEndpointId.set(String(descendant.id), descendant);
1244
+ for (const port of descendant.ports ?? []) descendantOwnerByEndpointId.set(String(port.id), descendant);
1245
+ collectDescendants(descendant);
1246
+ }
1247
+ };
1248
+ collectDescendants(child);
1249
+ const internalEdges = (graph.edges ?? []).filter((edge) => {
1250
+ const { sourceId, targetId } = originalHierarchyEndpoints.get(edge);
1251
+ return descendantOwnerByEndpointId.has(sourceId) && descendantOwnerByEndpointId.has(targetId);
1252
+ });
1253
+ for (const edge of child.edges ?? []) if (!originalHierarchyEndpoints.has(edge)) originalHierarchyEndpoints.set(edge, {
1254
+ sourceId: String(edge.sources?.[0] ?? edge.source),
1255
+ targetId: String(edge.targets?.[0] ?? edge.target),
1256
+ sources: edge.sources,
1257
+ targets: edge.targets,
1258
+ source: edge.source,
1259
+ target: edge.target
1260
+ });
1261
+ const crossingEdges = [...graph.edges ?? [], ...child.edges ?? []].filter((edge) => {
1262
+ const { sourceId, targetId } = originalHierarchyEndpoints.get(edge);
1263
+ return descendantOwnerByEndpointId.has(sourceId) !== descendantOwnerByEndpointId.has(targetId);
1264
+ });
1265
+ const boundaryRank = new Map(elkBoundaryEdgeOrder(child, graph, originalHierarchyEndpoints).map((edge, index) => [edge, index]));
1266
+ crossingEdges.sort((a, b) => (boundaryRank.get(a) ?? Number.MAX_SAFE_INTEGER) - (boundaryRank.get(b) ?? Number.MAX_SAFE_INTEGER));
1267
+ const mergeHierarchyEdges = getBooleanOption(layoutOptions, "layered.mergeHierarchyEdges") !== false;
1268
+ const proxyByKind = /* @__PURE__ */ new Map();
1269
+ const implicitOwnPorts = /* @__PURE__ */ new Map();
1270
+ const ownPortFor = (edge) => {
1271
+ const e = originalHierarchyEndpoints.get(edge);
1272
+ const outside = descendantOwnerByEndpointId.has(e.sourceId) ? e.targetId : e.sourceId;
1273
+ if (e.sourceId === String(child.id) || e.targetId === String(child.id)) {
1274
+ let port = implicitOwnPorts.get(edge);
1275
+ if (!port) {
1276
+ const direction = getDirection(layoutOptions);
1277
+ const side = e.sourceId === String(child.id) ? {
1278
+ right: "EAST",
1279
+ left: "WEST",
1280
+ down: "SOUTH",
1281
+ up: "NORTH"
1282
+ }[direction] : {
1283
+ right: "WEST",
1284
+ left: "EAST",
1285
+ down: "NORTH",
1286
+ up: "SOUTH"
1287
+ }[direction];
1288
+ port = {
1289
+ id: `__native_hierarchy_own_${String(child.id)}_${implicitOwnPorts.size}`,
1290
+ width: 0,
1291
+ height: 0,
1292
+ layoutOptions: { "elk.port.side": side }
1293
+ };
1294
+ implicitOwnPorts.set(edge, port);
1295
+ syntheticPortIds.add(String(port.id));
1296
+ }
1297
+ return child.ports?.find((p) => p.id === port.id) ?? port;
1298
+ }
1299
+ return child.ports?.find((p) => String(p.id) === outside);
1300
+ };
1301
+ const proxyKey = (kind, edge) => {
1302
+ const own = ownPortFor(edge);
1303
+ if (own) return JSON.stringify(["authored", own.id]);
1304
+ const endpoints = originalHierarchyEndpoints.get(edge);
1305
+ const endpoint$2 = kind === "output" ? endpoints.sourceId : endpoints.targetId;
1306
+ const explicitPort = descendantOwnerByEndpointId.get(endpoint$2)?.ports?.some((port) => String(port.id) === endpoint$2);
1307
+ return JSON.stringify([kind, mergeHierarchyEdges && explicitPort ? endpoint$2 : String(edge.id)]);
1308
+ };
1309
+ const createBoundaryProxy = (key, kind, own, netFlow) => {
1310
+ let proxy = proxyByKind.get(key);
1311
+ if (!proxy) {
1312
+ const direction = getDirection({
1313
+ ...layoutOptions,
1314
+ ...child.layoutOptions
1315
+ });
1316
+ const authoredAnchor = own ? getElementLayeredSettings(own.layoutOptions ?? {})["port.anchor"] : void 0;
1317
+ const origin = require_layered.createExternalPortDummy({
1318
+ constraints: getOption(child.layoutOptions ?? {}, "portConstraints") ?? "FREE",
1319
+ side: own ? getOption(own.layoutOptions ?? {}, "port.side") ?? (kind === "output" ? "EAST" : "WEST") : kind === "output" ? "EAST" : "WEST",
1320
+ position: {
1321
+ x: own?.x ?? 0,
1322
+ y: own?.y ?? 0
1323
+ },
1324
+ anchor: authoredAnchor,
1325
+ index: own ? getNumberOption(own.layoutOptions ?? {}, "port.index") : void 0,
1326
+ direction: direction.toUpperCase(),
1327
+ netFlow,
1328
+ borderOffset: own ? getNumberOption(own.layoutOptions ?? {}, "port.borderOffset") ?? 0 : (getNumberOption(layoutOptions, "spacing.edgeEdge") ?? 10) / 2,
1329
+ size: {
1330
+ width: own?.width ?? 0,
1331
+ height: own?.height ?? 0
1332
+ }
1333
+ });
1334
+ if (own) origin.parentPortId = String(own.id);
1335
+ const id = `__native_hierarchy_${String(child.id)}_${proxyByKind.size}`;
1336
+ proxy = require_layered.attachExternalPortDummy({
1337
+ id,
1338
+ width: origin.width,
1339
+ height: origin.height,
1340
+ layoutOptions: {
1341
+ "elk.portConstraints": origin.constraints,
1342
+ "elk.layered.layering.layerConstraint": origin.side === {
1343
+ right: "EAST",
1344
+ left: "WEST",
1345
+ down: "SOUTH",
1346
+ up: "NORTH"
1347
+ }[direction] ? "LAST_SEPARATE" : origin.side === {
1348
+ right: "WEST",
1349
+ left: "EAST",
1350
+ down: "NORTH",
1351
+ up: "SOUTH"
1352
+ }[direction] ? "FIRST_SEPARATE" : void 0
1353
+ },
1354
+ ports: [{
1355
+ id: `${id}:boundary`,
1356
+ x: origin.port.x,
1357
+ y: origin.port.y,
1358
+ width: 0,
1359
+ height: 0,
1360
+ layoutOptions: { "elk.port.side": origin.port.side }
1361
+ }]
1362
+ }, origin);
1363
+ proxyByKind.set(key, proxy);
1364
+ }
1365
+ return proxy;
1366
+ };
1367
+ const ownedPortNetFlow = (id) => {
1368
+ let votes = 0;
1369
+ for (const edge of originalHierarchyEndpoints.values()) {
1370
+ if (edge.sourceId === id) votes += descendantOwnerByEndpointId.has(edge.targetId) ? -1 : 1;
1371
+ if (edge.targetId === id) votes += descendantOwnerByEndpointId.has(edge.sourceId) ? 1 : -1;
1372
+ }
1373
+ return votes;
1374
+ };
1375
+ const proxyFor = (kind, edge) => {
1376
+ const own = ownPortFor(edge);
1377
+ const flow = implicitOwnPorts.has(edge) ? kind === "output" ? 1 : -1 : own ? ownedPortNetFlow(String(own.id)) : kind === "output" ? 1 : -1;
1378
+ return createBoundaryProxy(proxyKey(kind, edge), kind, own, flow);
1379
+ };
1380
+ const temporaryEdges = [
1381
+ ...crossingEdges.map((edge) => {
1382
+ const { sourceId, targetId } = originalHierarchyEndpoints.get(edge);
1383
+ const sourceInside = descendantOwnerByEndpointId.has(sourceId);
1384
+ const proxy = proxyFor(sourceInside ? "output" : "input", edge);
1385
+ const segment = {
1386
+ ...edge,
1387
+ id: `__native_hierarchy_edge_${String(child.id)}_${String(edge.id)}`,
1388
+ sources: [sourceInside ? sourceId : proxy.ports[0].id],
1389
+ targets: [sourceInside ? proxy.ports[0].id : targetId],
1390
+ source: void 0,
1391
+ target: void 0,
1392
+ sections: void 0
1393
+ };
1394
+ originalEdgeId.set(segment, originalEdgeId.get(edge) ?? String(edge.id));
1395
+ return segment;
1396
+ }),
1397
+ ...(child.edges ?? []).filter((edge) => !crossingEdges.includes(edge)),
1398
+ ...internalEdges.filter((edge) => !(child.edges ?? []).some((candidate) => String(candidate.id) === String(edge.id)))
1399
+ ];
1400
+ const hasExternalPorts = proxyByKind.size > 0;
1401
+ const inactiveAuthoredPorts = [];
1402
+ if (hasExternalPorts) for (const port of child.ports ?? []) {
1403
+ const key = JSON.stringify(["authored", port.id]);
1404
+ if (proxyByKind.has(key)) continue;
1405
+ const flow = ownedPortNetFlow(String(port.id));
1406
+ inactiveAuthoredPorts.push({
1407
+ port,
1408
+ proxy: createBoundaryProxy(key, flow >= 0 ? "output" : "input", port, flow)
1409
+ });
1410
+ }
1411
+ const outsideNodeEdges = !hasExternalPorts ? [] : (graph.edges ?? []).filter((edge) => {
1412
+ const e = originalHierarchyEndpoints.get(edge);
1413
+ return (e.sourceId === String(child.id) || e.targetId === String(child.id)) && !descendantOwnerByEndpointId.has(e.sourceId) && !descendantOwnerByEndpointId.has(e.targetId);
1414
+ });
1415
+ for (const edge of outsideNodeEdges) proxyFor(originalHierarchyEndpoints.get(edge).sourceId === String(child.id) ? "output" : "input", edge);
1416
+ const temporaryChild = {
1417
+ ...child,
1418
+ layoutOptions: proxyByKind.size ? {
1419
+ ...child.layoutOptions,
1420
+ "elk.portConstraints": "FIXED_SIDE"
1421
+ } : child.layoutOptions,
1422
+ children: [...child.children ?? [], ...proxyByKind.values()],
1423
+ edges: temporaryEdges
1424
+ };
1425
+ const childSegmentOrder = elkSegmentOrder(child, graph, originalHierarchyEndpoints, true);
1426
+ segmentOrderByScope.set(temporaryChild, childSegmentOrder);
1427
+ for (const edge of temporaryEdges) {
1428
+ const id = originalEdgeId.get(edge);
1429
+ const rank = id === void 0 ? void 0 : childSegmentOrder.get(id);
1430
+ if (rank !== void 0) hierarchySegmentRank.set(edge, rank);
1431
+ }
1432
+ const preparedChild = await this.#prepareLayout(temporaryChild, {
1433
+ ...arguments_,
1434
+ layoutOptions: {
1435
+ ...arguments_.layoutOptions,
1436
+ direction: getDirection(getOption(child.layoutOptions ?? {}, "direction") === void 0 ? layoutOptions : child.layoutOptions).toUpperCase(),
1437
+ hierarchyHandling: "INCLUDE_CHILDREN"
1438
+ },
1439
+ logging: false,
1440
+ measureExecutionTime: false
1441
+ }, true);
1442
+ preparedChildren.set(String(child.id), preparedChild);
1443
+ authoredPortsByCompound.set(child, child.ports);
1444
+ authoredOptionsByCompound.set(child, child.layoutOptions);
1445
+ const initialPorts = (child.ports ?? []).map((port) => ({
1446
+ ...port,
1447
+ layoutOptions: port.layoutOptions ? { ...port.layoutOptions } : void 0
1448
+ }));
1449
+ for (const edge of crossingEdges) {
1450
+ const own = ownPortFor(edge);
1451
+ if (own) {
1452
+ const original$1 = originalHierarchyEndpoints.get(edge);
1453
+ const sourceInside$1 = descendantOwnerByEndpointId.has(original$1.sourceId);
1454
+ const origin$1 = require_layered.externalPortDummyOf(proxyByKind.get(proxyKey(sourceInside$1 ? "output" : "input", edge)));
1455
+ let port = initialPorts.find((p) => p.id === own.id);
1456
+ if (!port) {
1457
+ port = {
1458
+ ...own,
1459
+ layoutOptions: { ...own.layoutOptions }
1460
+ };
1461
+ initialPorts.push(port);
1462
+ }
1463
+ port.layoutOptions = {
1464
+ ...port.layoutOptions,
1465
+ "elk.port.side": origin$1.side
1466
+ };
1467
+ continue;
1468
+ }
1469
+ const original = originalHierarchyEndpoints.get(edge);
1470
+ const sourceInside = descendantOwnerByEndpointId.has(original.sourceId);
1471
+ const proxy = proxyByKind.get(proxyKey(sourceInside ? "output" : "input", edge));
1472
+ const origin = require_layered.externalPortDummyOf(proxy);
1473
+ const portId = `${String(proxy.id)}:parent`;
1474
+ syntheticPortIds.add(portId);
1475
+ if (!initialPorts.some((port) => port.id === portId)) initialPorts.push({
1476
+ id: portId,
1477
+ width: origin.externalSize.width,
1478
+ height: origin.externalSize.height,
1479
+ x: 0,
1480
+ y: 0,
1481
+ layoutOptions: {
1482
+ "elk.port.side": origin.side,
1483
+ "elk.port.borderOffset": origin.borderOffset
1484
+ }
1485
+ });
1486
+ if (!hierarchyRestorations.some((restoration) => restoration.edge === edge)) hierarchyRestorations.push({
1487
+ edge,
1488
+ ...original
1489
+ });
1490
+ if (sourceInside) edge.sources = [portId];
1491
+ else edge.targets = [portId];
1492
+ const rank = scopeSegmentOrder.get(originalEdgeId.get(edge) ?? String(edge.id));
1493
+ if (rank !== void 0) hierarchySegmentRank.set(edge, rank);
1494
+ edge.source = void 0;
1495
+ edge.target = void 0;
1496
+ }
1497
+ for (const { port, proxy } of inactiveAuthoredPorts) {
1498
+ const actual = initialPorts.find((p) => p.id === port.id);
1499
+ actual.layoutOptions = {
1500
+ ...actual.layoutOptions,
1501
+ "elk.port.side": require_layered.externalPortDummyOf(proxy).side
1502
+ };
1503
+ }
1504
+ for (const edge of outsideNodeEdges) {
1505
+ const original = originalHierarchyEndpoints.get(edge);
1506
+ const source = original.sourceId === String(child.id);
1507
+ const proxy = proxyFor(source ? "output" : "input", edge), own = ownPortFor(edge);
1508
+ const origin = require_layered.externalPortDummyOf(proxy);
1509
+ if (!initialPorts.some((p) => p.id === own.id)) initialPorts.push({
1510
+ ...own,
1511
+ layoutOptions: {
1512
+ ...own.layoutOptions,
1513
+ "elk.port.side": origin.side
1514
+ }
1515
+ });
1516
+ if (!hierarchyRestorations.some((r) => r.edge === edge)) hierarchyRestorations.push({
1517
+ edge,
1518
+ ...original
1519
+ });
1520
+ if (source) edge.sources = [own.id];
1521
+ else edge.targets = [own.id];
1522
+ edge.source = void 0;
1523
+ edge.target = void 0;
1524
+ }
1525
+ child.ports = initialPorts;
1526
+ child.layoutOptions = {
1527
+ ...child.layoutOptions,
1528
+ "elk.portConstraints": crossingEdges.length > 0 || outsideNodeEdges.length > 0 ? "FIXED_SIDE" : getOption(authoredOptionsByCompound.get(child) ?? {}, "portConstraints") ?? "FREE"
1529
+ };
1530
+ finishChildren.push(async () => {
1531
+ await preparedChild.finish(activeOrders);
1532
+ const childPadding = parsePadding(getOption({
1533
+ ...layoutOptions,
1534
+ ...child.layoutOptions
1535
+ }, "padding"), 12);
1536
+ for (const internalEdge of internalEdges) {
1537
+ const temporary = temporaryEdges.find((candidate) => String(candidate.id) === String(internalEdge.id));
1538
+ if (temporary?.sections) {
1539
+ internalEdge.sections = temporary.sections;
1540
+ internalEdge.container = temporary.container ?? child.id;
1541
+ }
1542
+ }
1543
+ const childDirection = getDirection({
1544
+ ...layoutOptions,
1545
+ ...child.layoutOptions
1546
+ });
1547
+ const horizontalChild = childDirection === "right" || childDirection === "left";
1548
+ const restoredPerpendicular = [...proxyByKind.values()].some(require_layered.isPerpendicularPortRestored);
1549
+ child.width = restoredPerpendicular ? temporaryChild.width : horizontalChild ? Math.max(0, ...(temporaryChild.children ?? []).map((node) => (node.x ?? 0) + (node.width ?? 0))) + childPadding.right : temporaryChild.width;
1550
+ child.height = restoredPerpendicular ? temporaryChild.height : horizontalChild ? temporaryChild.height : Math.max(0, ...(temporaryChild.children ?? []).map((node) => (node.y ?? 0) + (node.height ?? 0))) + childPadding.bottom;
1551
+ const relativeRect = (id) => {
1552
+ const owner = descendantOwnerByEndpointId.get(id);
1553
+ if (!owner) return void 0;
1554
+ const path = [];
1555
+ const visit = (parent) => {
1556
+ for (const candidate of parent.children ?? []) {
1557
+ path.push(candidate);
1558
+ if (candidate === owner || visit(candidate)) return true;
1559
+ path.pop();
1560
+ }
1561
+ return false;
1562
+ };
1563
+ if (!visit(child)) return void 0;
1564
+ const port = owner.ports?.find((candidate) => String(candidate.id) === id);
1565
+ const ownerX = path.reduce((sum, node) => sum + (node.x ?? 0), 0);
1566
+ const ownerY = path.reduce((sum, node) => sum + (node.y ?? 0), 0);
1567
+ if (port) return {
1568
+ x: ownerX + (port.x ?? 0) + (port.width ?? 0) / 2,
1569
+ y: ownerY + (port.y ?? 0) + (port.height ?? 0) / 2,
1570
+ width: 0,
1571
+ height: 0
1572
+ };
1573
+ return {
1574
+ x: ownerX,
1575
+ y: ownerY,
1576
+ width: path.at(-1)?.width ?? 0,
1577
+ height: path.at(-1)?.height ?? 0
1578
+ };
1579
+ };
1580
+ const ports = (authoredPortsByCompound.get(child) ?? []).map((original) => child.ports?.find((port) => port.id === original.id) ?? original);
1581
+ for (const edge of crossingEdges) {
1582
+ const original = originalHierarchyEndpoints.get(edge);
1583
+ const { sourceId, targetId } = original;
1584
+ const sourceInside = descendantOwnerByEndpointId.has(sourceId);
1585
+ if (!relativeRect(sourceInside ? sourceId : targetId)) continue;
1586
+ const internalEdge = temporaryEdges.find((candidate) => String(candidate.id) === `__native_hierarchy_edge_${String(child.id)}_${String(edge.id)}`);
1587
+ const internalRoute = internalEdge?.sections;
1588
+ const kind = sourceInside ? "output" : "input";
1589
+ const proxy = proxyByKind.get(proxyKey(kind, edge));
1590
+ const origin = require_layered.externalPortDummyOf(proxy);
1591
+ const endpoint$2 = sourceInside ? internalRoute?.at(-1)?.endPoint : internalRoute?.[0]?.startPoint;
1592
+ if (!endpoint$2) throw new Error(`Missing compound boundary route for ${String(edge.id)}`);
1593
+ const transferred = transferExternalPort({
1594
+ contentSize: {
1595
+ width: child.width - childPadding.left - childPadding.right,
1596
+ height: child.height - childPadding.top - childPadding.bottom
1597
+ },
1598
+ padding: childPadding,
1599
+ offset: {
1600
+ x: 0,
1601
+ y: 0
1602
+ },
1603
+ dummy: {
1604
+ x: endpoint$2.x - childPadding.left - (ownPortFor(edge) ? origin.port.x : 0),
1605
+ y: endpoint$2.y - childPadding.top - (ownPortFor(edge) ? origin.port.y : 0),
1606
+ width: origin.width,
1607
+ height: origin.height
1608
+ },
1609
+ side: origin.side,
1610
+ borderOffset: origin.borderOffset,
1611
+ portSize: origin.externalSize
1612
+ });
1613
+ const points = internalRoute.flatMap((section, index) => [
1614
+ ...index === 0 ? [section.startPoint] : [],
1615
+ ...section.bendPoints ?? [],
1616
+ section.endPoint
1617
+ ]).map((point) => ({ ...point }));
1618
+ const boundary = sourceInside ? points.at(-1) : points[0];
1619
+ boundary.x = transferred.dummy.x + childPadding.left;
1620
+ boundary.y = transferred.dummy.y + childPadding.top;
1621
+ const own = ownPortFor(edge);
1622
+ if (own) {
1623
+ if (implicitOwnPorts.has(edge) && !ports.some((p) => p.id === own.id)) ports.push(own);
1624
+ own.x = transferred.port.x;
1625
+ own.y = transferred.port.y;
1626
+ const anchor = getElementLayeredSettings(own.layoutOptions ?? {})["port.anchor"] ?? {
1627
+ x: origin.side === "EAST" ? own.width ?? 0 : origin.side === "WEST" ? 0 : (own.width ?? 0) / 2,
1628
+ y: origin.side === "SOUTH" ? own.height ?? 0 : origin.side === "NORTH" ? 0 : (own.height ?? 0) / 2
1629
+ };
1630
+ boundary.x = own.x + anchor.x;
1631
+ boundary.y = own.y + anchor.y;
1632
+ edge.sections = [{
1633
+ id: `${String(edge.id)}_s0`,
1634
+ startPoint: points[0],
1635
+ endPoint: points.at(-1),
1636
+ ...points.length > 2 ? { bendPoints: points.slice(1, -1) } : {},
1637
+ incomingShape: sourceId,
1638
+ outgoingShape: targetId
1639
+ }];
1640
+ if (internalEdge?.junctionPoints?.length) edge.junctionPoints = internalEdge.junctionPoints.map((point) => ({ ...point }));
1641
+ else delete edge.junctionPoints;
1642
+ edge.container = child.id;
1643
+ continue;
1644
+ }
1645
+ const routes = boundaryRoutes.get(edge) ?? {};
1646
+ routes[sourceInside ? "source" : "target"] = {
1647
+ owner: child,
1648
+ points,
1649
+ junctionPoints: internalEdge?.junctionPoints
1650
+ };
1651
+ boundaryRoutes.set(edge, routes);
1652
+ const portId = `${String(proxy.id)}:parent`;
1653
+ syntheticPortIds.add(portId);
1654
+ if (!ports.some((port) => port.id === portId)) ports.push({
1655
+ id: portId,
1656
+ width: origin.externalSize.width,
1657
+ height: origin.externalSize.height,
1658
+ x: transferred.port.x,
1659
+ y: transferred.port.y,
1660
+ layoutOptions: {
1661
+ "elk.port.side": origin.side,
1662
+ "elk.port.borderOffset": origin.borderOffset
1663
+ }
1664
+ });
1665
+ if (!hierarchyRestorations.some((restoration) => restoration.edge === edge)) hierarchyRestorations.push({
1666
+ edge,
1667
+ ...original
1668
+ });
1669
+ if (sourceInside) edge.sources = [portId];
1670
+ else edge.targets = [portId];
1671
+ edge.source = void 0;
1672
+ edge.target = void 0;
1673
+ }
1674
+ for (const { port, proxy } of inactiveAuthoredPorts) {
1675
+ const origin = require_layered.externalPortDummyOf(proxy);
1676
+ const actual = ports.find((p) => p.id === port.id);
1677
+ const transferred = transferExternalPort({
1678
+ contentSize: {
1679
+ width: child.width - childPadding.left - childPadding.right,
1680
+ height: child.height - childPadding.top - childPadding.bottom
1681
+ },
1682
+ padding: childPadding,
1683
+ offset: {
1684
+ x: 0,
1685
+ y: 0
1686
+ },
1687
+ dummy: {
1688
+ x: (proxy.x ?? 0) - childPadding.left,
1689
+ y: (proxy.y ?? 0) - childPadding.top,
1690
+ width: origin.width,
1691
+ height: origin.height
1692
+ },
1693
+ side: origin.side,
1694
+ borderOffset: origin.borderOffset,
1695
+ portSize: origin.externalSize
1696
+ });
1697
+ actual.x = transferred.port.x;
1698
+ actual.y = transferred.port.y;
1699
+ }
1700
+ for (const edge of outsideNodeEdges) {
1701
+ const proxy = proxyFor(originalHierarchyEndpoints.get(edge).sourceId === String(child.id) ? "output" : "input", edge), origin = require_layered.externalPortDummyOf(proxy), own = ownPortFor(edge);
1702
+ const transferred = transferExternalPort({
1703
+ contentSize: {
1704
+ width: child.width - childPadding.left - childPadding.right,
1705
+ height: child.height - childPadding.top - childPadding.bottom
1706
+ },
1707
+ padding: childPadding,
1708
+ offset: {
1709
+ x: 0,
1710
+ y: 0
1711
+ },
1712
+ dummy: {
1713
+ x: (proxy.x ?? 0) - childPadding.left,
1714
+ y: (proxy.y ?? 0) - childPadding.top,
1715
+ width: origin.width,
1716
+ height: origin.height
1717
+ },
1718
+ side: origin.side,
1719
+ borderOffset: origin.borderOffset,
1720
+ portSize: origin.externalSize
1721
+ });
1722
+ own.x = transferred.port.x;
1723
+ own.y = transferred.port.y;
1724
+ if (!ports.some((p) => p.id === own.id)) ports.push(own);
1725
+ }
1726
+ child.ports = ports;
1727
+ child.layoutOptions = {
1728
+ ...child.layoutOptions,
1729
+ "elk.portConstraints": "FIXED_POS"
1730
+ };
1731
+ const insideLoopCount = (graph.edges ?? []).filter((edge) => isInsideSelfLoop(graph, edge, String(child.id))).length;
1732
+ if (insideLoopCount > 0) {
1733
+ const baseHeight = child.height ?? 0;
1734
+ insideSelfLoopBaseHeightByNodeId.set(String(child.id), baseHeight);
1735
+ child.height = baseHeight + insideLoopCount * 11;
1736
+ }
1737
+ });
1738
+ }
1739
+ }
1740
+ applyNodeMicroLayout(graph, layoutOptions);
1741
+ const graph_ = toGraph(graph, layoutOptions);
1742
+ const hierarchyProxyIds = new Set((graph.children ?? []).filter((node) => String(node.id).startsWith("__native_hierarchy_")).map((node) => String(node.id)));
1743
+ const layerConstraintByNodeId = new Map((graph.children ?? []).map((node) => [String(node.id), String(getOption(node.layoutOptions ?? {}, "layered.layering.layerConstraint") ?? getOption(node.layoutOptions ?? {}, "layerConstraint") ?? "NONE")]));
1744
+ if (algorithm === "layered" && graph_.edges.some((edge) => {
1745
+ if (edge.sourceId === edge.targetId) return false;
1746
+ if (hierarchyProxyIds.has(edge.sourceId) || hierarchyProxyIds.has(edge.targetId)) return false;
1747
+ const sourceConstraint = layerConstraintByNodeId.get(edge.sourceId);
1748
+ const targetConstraint = layerConstraintByNodeId.get(edge.targetId);
1749
+ return (sourceConstraint === "FIRST" || sourceConstraint === "FIRST_SEPARATE") && (targetConstraint === "FIRST" || targetConstraint === "FIRST_SEPARATE");
1750
+ })) throw new Error("org.eclipse.elk.core.UnsupportedConfigurationException: Layer constraints conflict");
1751
+ const padding = parsePadding(getOption(layoutOptions, "padding"), algorithm === "layered" ? 12 : 0);
1752
+ const options = {
1753
+ direction: getDirection(layoutOptions),
1754
+ spacing: {
1755
+ node: getNumberOption(layoutOptions, "spacing.nodeNode") ?? getNumberOption(layoutOptions, "layered.spacing.baseValue"),
1756
+ layer: getNumberOption(layoutOptions, "layered.spacing.nodeNodeBetweenLayers") ?? getNumberOption(layoutOptions, "layered.spacing.baseValue") ?? 20
1757
+ },
1758
+ padding,
1759
+ constraints: { layer: () => void 0 },
1760
+ settings: {
1761
+ ...getLayeredSettings(layoutOptions),
1762
+ ...compoundLayout || hasHierarchy && !separateHierarchy && !topdownLayout ? { separateConnectedComponents: false } : {},
1763
+ ...hierarchyHandling === "INCLUDE_CHILDREN" ? { "crossingMinimization.greedySwitch.type": String(getOption(layoutOptions, "layered.crossingMinimization.greedySwitchHierarchical.type") ?? "TWO_SIDED") } : {}
1764
+ },
1765
+ nodeSettings: (node) => {
1766
+ const child = findById(graph.children, node.id)?.item;
1767
+ return getElementLayeredSettings(child?.layoutOptions ?? {});
1768
+ },
1769
+ edgeSettings: (edge) => {
1770
+ const elkEdge = findById(graph.edges, edge.id)?.item;
1771
+ return {
1772
+ ...getElementLayeredSettings(elkEdge?.layoutOptions ?? {}),
1773
+ ...getElementLayeredSettings(elkEdge?.labels?.[0]?.layoutOptions ?? {}),
1774
+ "edge.hierarchyRank": elkEdge && hierarchySegmentRank.get(elkEdge)
1775
+ };
1776
+ },
1777
+ portSettings: (port, node) => {
1778
+ const child = findById(graph.children, node.id)?.item;
1779
+ const found = findById(child?.ports, port.name);
1780
+ const elkPort = found?.item;
1781
+ return {
1782
+ ...getElementLayeredSettings(elkPort?.layoutOptions ?? {}),
1783
+ "port.authoredIndex": found?.position,
1784
+ "port.labelWidth": Math.max(0, ...(elkPort?.labels ?? []).map((label) => label.width ?? 0)),
1785
+ "port.labelHeight": Math.max(0, ...(elkPort?.labels ?? []).map((label) => label.height ?? 0))
1786
+ };
1787
+ }
1788
+ };
1789
+ const pipeline = algorithm === "layered" ? require_layered.createLayeredScopePipeline(graph_, options) : void 0;
1790
+ const initial = pipeline?.next();
1791
+ const phase = initial && !initial.done ? initial.value : void 0;
1792
+ let activeOrders;
1793
+ let finished;
1794
+ const prepared = {
1795
+ graph,
1796
+ native: graph_,
1797
+ phase,
1798
+ options,
1799
+ children: preparedChildren,
1800
+ finish: (orders) => {
1801
+ if (finished) return finished;
1802
+ activeOrders = orders;
1803
+ finished = finish();
1804
+ return finished;
1805
+ }
1806
+ };
1807
+ const finishLayered = () => {
1808
+ if (!pipeline || !initial) throw new Error("Missing layered pipeline");
1809
+ if (initial.done) return initial.value;
1810
+ const completed = pipeline.next(activeOrders?.has(prepared) ? initial.value.finishOrder(activeOrders.get(prepared)) : initial.value.minimize());
1811
+ if (!completed.done) throw new Error("Layered scope yielded more than one crossing phase");
1812
+ return completed.value;
1813
+ };
1814
+ const finish = async () => {
1815
+ for (const complete of finishChildren) await complete();
1816
+ for (const nativeGraph of new Set([graph_, phase?.input.graph].filter((value) => value !== void 0))) for (const child of graph.children ?? []) {
1817
+ const native = nativeGraph.nodes.find((node) => node.id === String(child.id));
1818
+ if (!native || !preparedChildren.has(String(child.id))) continue;
1819
+ phase?.updateNodeSize(native.id, {
1820
+ width: child.width ?? 0,
1821
+ height: child.height ?? 0
1822
+ });
1823
+ Object.assign(native, {
1824
+ width: child.width,
1825
+ height: child.height
1826
+ });
1827
+ for (const port of native.ports ?? []) {
1828
+ const actual = child.ports?.find((candidate) => String(candidate.id) === port.name);
1829
+ if (actual) Object.assign(port, {
1830
+ x: actual.x,
1831
+ y: actual.y,
1832
+ width: actual.width,
1833
+ height: actual.height
1834
+ });
1835
+ }
1836
+ }
1837
+ const laidOut = await (algorithm === "sporeCompaction" ? require_layout_engine.executeElkjs0111Layout({
1838
+ algorithm: "sporeCompaction",
1839
+ graph: graph_,
1840
+ options: {
1841
+ padding,
1842
+ spacing: getNumberOption(layoutOptions, "spacing.nodeNode")
1843
+ }
1844
+ }) : algorithm === "sporeOverlap" ? require_layout_engine.executeElkjs0111Layout({
1845
+ algorithm: "sporeOverlap",
1846
+ graph: graph_,
1847
+ options: {
1848
+ padding,
1849
+ spacing: getNumberOption(layoutOptions, "spacing.nodeNode")
1850
+ }
1851
+ }) : algorithm === "rectpacking" ? require_layout_engine.executeElkjs0111Layout({
1852
+ algorithm: "rectpacking",
1853
+ graph: graph_,
1854
+ options: {
1855
+ padding: getOption(layoutOptions, "padding") === void 0 ? 15 : padding,
1856
+ spacing: getNumberOption(layoutOptions, "spacing.nodeNode") ?? 15
1857
+ }
1858
+ }) : algorithm === "random" ? require_layout_engine.executeElkjs0111Layout({
1859
+ algorithm: "random",
1860
+ graph: graph_,
1861
+ options: {
1862
+ padding: getOption(layoutOptions, "padding") === void 0 ? 15 : padding,
1863
+ spacing: getNumberOption(layoutOptions, "spacing.nodeNode"),
1864
+ aspectRatio: getNumberOption(layoutOptions, "aspectRatio"),
1865
+ seed: getNumberOption(layoutOptions, "randomSeed")
1866
+ }
1867
+ }) : algorithm === "box" ? require_layout_engine.executeElkjs0111Layout({
1868
+ algorithm: "box",
1869
+ graph: graph_,
1870
+ options: {
1871
+ padding: getOption(layoutOptions, "padding") === void 0 ? 15 : padding,
1872
+ spacing: getNumberOption(layoutOptions, "spacing.nodeNode"),
1873
+ aspectRatio: getNumberOption(layoutOptions, "aspectRatio"),
1874
+ interactive: getBooleanOption(layoutOptions, "interactive"),
1875
+ expandNodes: getBooleanOption(layoutOptions, "expandNodes"),
1876
+ priority: (node) => {
1877
+ const child = graph.children?.find((candidate) => String(candidate.id) === node.id);
1878
+ return getNumberOption(child?.layoutOptions ?? {}, "priority");
1879
+ }
1880
+ }
1881
+ }) : algorithm === "fixed" ? require_layout_engine.executeElkjs0111Layout({
1882
+ algorithm: "fixed",
1883
+ graph: graph_,
1884
+ options: { direction: getDirection(layoutOptions) }
1885
+ }) : finishLayered());
1886
+ const resultPolicy = laidOut[require_layered.elkjs0111ResultPolicy];
1887
+ applyLayout(graph, laidOut, padding, layoutOptions, resultPolicy);
1888
+ if (resultPolicy?.providerBounds) {
1889
+ graph.width = resultPolicy.providerBounds.width;
1890
+ graph.height = resultPolicy.providerBounds.height;
1891
+ }
1892
+ for (const restoration of hierarchyRestorations) {
1893
+ const splits = boundaryRoutes.get(restoration.edge);
1894
+ const sections = restoration.edge.sections;
1895
+ if (splits && sections?.length) {
1896
+ const parentPoints = sections.flatMap((section, index) => [
1897
+ ...index === 0 ? [section.startPoint] : [],
1898
+ ...section.bendPoints ?? [],
1899
+ section.endPoint
1900
+ ]);
1901
+ const points = joinCompoundRouteSegments([
1902
+ ...splits.source ? [{
1903
+ points: splits.source.points,
1904
+ offset: {
1905
+ x: splits.source.owner.x ?? 0,
1906
+ y: splits.source.owner.y ?? 0
1907
+ }
1908
+ }] : [],
1909
+ {
1910
+ points: parentPoints,
1911
+ offset: {
1912
+ x: 0,
1913
+ y: 0
1914
+ }
1915
+ },
1916
+ ...splits.target ? [{
1917
+ points: splits.target.points,
1918
+ offset: {
1919
+ x: splits.target.owner.x ?? 0,
1920
+ y: splits.target.owner.y ?? 0
1921
+ }
1922
+ }] : []
1923
+ ], getBooleanOption(layoutOptions, "unnecessaryBendpoints") === true);
1924
+ const childJunctions = (split) => (split?.junctionPoints ?? []).map((point) => ({
1925
+ x: point.x + (split?.owner.x ?? 0),
1926
+ y: point.y + (split?.owner.y ?? 0)
1927
+ }));
1928
+ const junctions = [
1929
+ ...childJunctions(splits.source),
1930
+ ...restoration.edge.junctionPoints ?? [],
1931
+ ...childJunctions(splits.target)
1932
+ ];
1933
+ if (junctions.length) restoration.edge.junctionPoints = junctions;
1934
+ else delete restoration.edge.junctionPoints;
1935
+ restoration.edge.sections = [{
1936
+ ...sections[0],
1937
+ startPoint: points[0],
1938
+ endPoint: points.at(-1),
1939
+ bendPoints: points.slice(1, -1)
1940
+ }];
1941
+ }
1942
+ restoration.edge.sources = restoration.sources;
1943
+ restoration.edge.targets = restoration.targets;
1944
+ restoration.edge.source = restoration.source;
1945
+ restoration.edge.target = restoration.target;
1946
+ for (const section of restoration.edge.sections ?? []) {
1947
+ if (section.incomingShape != null && syntheticPortIds.has(String(section.incomingShape))) section.incomingShape = restoration.sources?.[0] ?? restoration.source;
1948
+ if (section.outgoingShape != null && syntheticPortIds.has(String(section.outgoingShape))) section.outgoingShape = restoration.targets?.[0] ?? restoration.target;
1949
+ }
1950
+ }
1951
+ for (const [compound, ports] of authoredPortsByCompound) {
1952
+ for (const port of ports ?? []) {
1953
+ const actual = compound.ports?.find((p) => p.id === port.id);
1954
+ if (actual) Object.assign(port, {
1955
+ x: actual.x,
1956
+ y: actual.y,
1957
+ width: actual.width,
1958
+ height: actual.height
1959
+ });
1960
+ }
1961
+ compound.ports = ports;
1962
+ }
1963
+ for (const [compound, options_] of authoredOptionsByCompound) compound.layoutOptions = options_;
1964
+ if (hasHierarchy && topdownLayout) for (const child of graph.children ?? []) {
1965
+ if ((child.children?.length ?? 0) === 0) continue;
1966
+ const position = {
1967
+ x: child.x,
1968
+ y: child.y
1969
+ };
1970
+ await this.#layout(child, {
1971
+ ...arguments_,
1972
+ logging: false,
1973
+ measureExecutionTime: false
1974
+ });
1975
+ child.x = position.x;
1976
+ child.y = position.y;
1977
+ }
1978
+ applyInsideSelfLoops(graph, insideSelfLoopBaseHeightByNodeId);
1979
+ if (arguments_.logging || arguments_.measureExecutionTime) graph.logging = {
1980
+ name: "Native TypeScript layout",
1981
+ children: [{ name: String(algorithm) }],
1982
+ ...arguments_.measureExecutionTime ? { executionTime: (performance.now() - startedAt) / 1e3 } : {}
1983
+ };
1984
+ return graph;
1985
+ };
1986
+ return prepared;
1987
+ }
1988
+ };
1989
+ /** Move opposite-direction track sharing apart on the finished layout. */
1990
+ function separated(graph, options) {
1991
+ separateElkOpposingTracks(graph, {
1992
+ ...options,
1993
+ ...graph.layoutOptions
1994
+ });
1995
+ return graph;
1996
+ }
1997
+ function coordinatePreparedScopes(root) {
1998
+ if (!root.phase || root.children.size === 0) return void 0;
1999
+ const prepared = [root];
2000
+ for (let i = 0; i < prepared.length; i++) prepared.push(...prepared[i].children.values());
2001
+ if (prepared.some((scope) => !scope.phase || !["LAYER_SWEEP", "MEDIAN_LAYER_SWEEP"].includes(scope.options?.settings?.["crossingMinimization.strategy"] ?? "LAYER_SWEEP"))) return void 0;
2002
+ const scopes = /* @__PURE__ */ new Map();
2003
+ for (const scope of prepared) {
2004
+ const phase = scope.phase;
2005
+ scopes.set(scope, {
2006
+ session: require_layered.createLayerSweepSession(phase.input, phase.orientation, phase.assignment, scope.options?.settings?.["crossingMinimization.strategy"] === "MEDIAN_LAYER_SWEEP" ? "median" : "mean", 7, { resetSeed: scope === root }),
2007
+ childrenByNodeId: /* @__PURE__ */ new Map(),
2008
+ useBottomUp: true,
2009
+ independentRandom: scope !== root
2010
+ });
2011
+ }
2012
+ for (const parent of prepared) {
2013
+ const parentScope = scopes.get(parent);
2014
+ for (const [id, child] of parent.children) {
2015
+ const nativeParent = parent.phase.input.graph.nodes.find((node) => node.id === id);
2016
+ const parentPortOrderFixed = [
2017
+ "FIXED_ORDER",
2018
+ "FIXED_POS",
2019
+ "FIXED_RATIO"
2020
+ ].includes(parent.phase.input.nodeSettings?.(nativeParent)?.portConstraints ?? "FREE");
2021
+ const ports = nativeParent.ports ?? [];
2022
+ const childPhase = child.phase;
2023
+ const childSession = scopes.get(child).session;
2024
+ const childNodeById = /* @__PURE__ */ new Map();
2025
+ for (const node of childPhase.input.graph.nodes) if (!childNodeById.has(node.id)) childNodeById.set(node.id, node);
2026
+ const parentEdgeById = /* @__PURE__ */ new Map();
2027
+ for (const edge of parent.phase.input.graph.edges) if (!parentEdgeById.has(edge.id)) parentEdgeById.set(edge.id, edge);
2028
+ const flowSides = require_layered.hierarchicalPortSides(parent.phase.input);
2029
+ const inputPorts = ports.filter((port) => parent.phase.input.portSettings?.(port, nativeParent)?.["port.side"] === flowSides.before).length;
2030
+ const outputPorts = ports.filter((port) => parent.phase.input.portSettings?.(port, nativeParent)?.["port.side"] === flowSides.after).length;
2031
+ const childScope = {
2032
+ ...scopes.get(child),
2033
+ useBottomUp: useBottomUpHierarchySweep(childPhase.input, childPhase.orientation, childSession.snapshot().layers, {
2034
+ portOrderFixed: parentPortOrderFixed,
2035
+ inputPorts,
2036
+ outputPorts,
2037
+ deterministic: child.options?.settings?.["crossingMinimization.strategy"] === "MEDIAN_LAYER_SWEEP"
2038
+ }),
2039
+ alignBoundary: (forward) => {
2040
+ const childLayers = childSession.currentLayers();
2041
+ const layerIndex = forward ? 0 : childLayers.length - 1;
2042
+ const layer = childLayers[layerIndex];
2043
+ if (!layer.every((id$1) => require_layered.externalPortDummyOf(childNodeById.get(id$1)))) return false;
2044
+ const physical = parentScope.session.physicalPortOrder(id);
2045
+ const dummyByParentPort = new Map(childPhase.input.graph.nodes.filter((n) => require_layered.externalPortDummyOf(n)).map((n) => [require_layered.externalPortDummyOf(n).parentPortId ?? `${n.id}:parent`, n.id]));
2046
+ const ordered = physical.flatMap((portId) => {
2047
+ const [nodeId, portName] = JSON.parse(portId);
2048
+ const dummy = nodeId === id ? dummyByParentPort.get(portName) : void 0;
2049
+ return dummy && layer.includes(dummy) ? [dummy] : [];
2050
+ });
2051
+ if (forward) ordered.reverse();
2052
+ const unique = [...new Set(ordered)];
2053
+ if (unique.length !== layer.length) throw new Error(`Incomplete hierarchical port order for ${id}`);
2054
+ childSession.replaceLayer(layerIndex, unique);
2055
+ return true;
2056
+ },
2057
+ publishBoundary: (forward) => {
2058
+ const childLayers = childSession.currentLayers();
2059
+ const layer = childLayers[forward ? childLayers.length - 1 : 0];
2060
+ if (!layer.every((id$1) => require_layered.externalPortDummyOf(childNodeById.get(id$1)))) return;
2061
+ const rank = new Map(layer.map((dummy, index) => [require_layered.externalPortDummyOf(childNodeById.get(dummy)).parentPortId ?? `${dummy}:parent`, index]));
2062
+ const physical = parentScope.session.physicalPortOrder(id);
2063
+ const boundarySlots = physical.flatMap((portId, index) => {
2064
+ const [nodeId, portName] = JSON.parse(portId);
2065
+ return nodeId === id && rank.has(portName) ? [index] : [];
2066
+ });
2067
+ const boundaryPorts = boundarySlots.map((index) => physical[index]).sort((a, b) => {
2068
+ const portA = JSON.parse(a)[1];
2069
+ const portB = JSON.parse(b)[1];
2070
+ const result = rank.get(portA) - rank.get(portB);
2071
+ return forward ? result : -result;
2072
+ });
2073
+ const updatedPhysical = [...physical];
2074
+ boundarySlots.forEach((slot, index) => {
2075
+ updatedPhysical[slot] = boundaryPorts[index];
2076
+ });
2077
+ const edgePortRank = (edgeId) => {
2078
+ const edge = parentEdgeById.get(edgeId);
2079
+ const portName = edge.sourceId === id ? edge.sourcePort : edge.targetPort;
2080
+ return updatedPhysical.indexOf(JSON.stringify([id, portName]));
2081
+ };
2082
+ const { incoming, outgoing } = parentScope.session.edgeOrders(id);
2083
+ const byPort = (current) => [...current].sort((a, b) => edgePortRank(a) - edgePortRank(b));
2084
+ parentScope.session.reorderNodePorts(id, updatedPhysical, byPort(incoming), byPort(outgoing));
2085
+ }
2086
+ };
2087
+ childScope.publishBottomUp = () => {
2088
+ childScope.publishBoundary(true);
2089
+ childScope.publishBoundary(false);
2090
+ parentScope.session.lockPortOrder(id);
2091
+ };
2092
+ scopes.set(child, childScope);
2093
+ parentScope.childrenByNodeId.set(id, childScope);
2094
+ }
2095
+ }
2096
+ const orders = require_layered.minimizeHierarchyCrossings(scopes.get(root));
2097
+ return new Map(prepared.map((scope) => [scope, orders.get(scopes.get(scope))]));
2098
+ }
2099
+ function parsePadding(value, fallback = 0) {
2100
+ if (typeof value === "number") return {
2101
+ top: value,
2102
+ right: value,
2103
+ bottom: value,
2104
+ left: value
2105
+ };
2106
+ if (typeof value !== "string") return {
2107
+ top: fallback,
2108
+ right: fallback,
2109
+ bottom: fallback,
2110
+ left: fallback
2111
+ };
2112
+ const padding = {
2113
+ top: 0,
2114
+ right: 0,
2115
+ bottom: 0,
2116
+ left: 0
2117
+ };
2118
+ for (const match of value.matchAll(/(top|right|bottom|left)\s*=\s*(-?\d+(?:\.\d+)?)/g)) {
2119
+ const side = match[1];
2120
+ padding[side] = Number(match[2]);
2121
+ }
2122
+ return padding;
2123
+ }
2124
+ function parseVector(value) {
2125
+ if (typeof value === "object" && value !== null && "x" in value && "y" in value) return {
2126
+ x: Number(value.x),
2127
+ y: Number(value.y)
2128
+ };
2129
+ if (typeof value !== "string") return void 0;
2130
+ const match = value.match(/^\s*\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\)\s*$/);
2131
+ return match ? {
2132
+ x: Number(match[1]),
2133
+ y: Number(match[2])
2134
+ } : void 0;
2135
+ }
2136
+ function applyNodeMicroLayout(graph, globalOptions) {
2137
+ const labelPadding = parsePadding(getOption(globalOptions, "nodeLabels.padding"), 5);
2138
+ for (const node of graph.children ?? []) {
2139
+ const constraints = String(getOption(node.layoutOptions ?? {}, "nodeSize.constraints") ?? "");
2140
+ if (!constraints) continue;
2141
+ const configuredSizeOptions = getOption(node.layoutOptions ?? {}, "nodeSize.options");
2142
+ const sizeOptions = new Set(configuredSizeOptions === void 0 ? ["DEFAULT_MINIMUM_SIZE"] : String(configuredSizeOptions).split(/[\s,;]+/).filter(Boolean));
2143
+ const effectivelyFixedPortLabelSize = constraints.includes("PORT_LABELS") && !constraints.includes("NODE_LABELS") && !constraints.includes("MINIMUM_SIZE");
2144
+ const preserveComputedCompoundSize = (node.children?.length ?? 0) > 0;
2145
+ let width = effectivelyFixedPortLabelSize || preserveComputedCompoundSize ? node.width ?? 0 : 0;
2146
+ let height = effectivelyFixedPortLabelSize || preserveComputedCompoundSize ? node.height ?? 0 : 0;
2147
+ let insideHorizontalInset = 0;
2148
+ let insideVerticalInset = 0;
2149
+ const insideLabelCells = /* @__PURE__ */ new Map();
2150
+ if (constraints.includes("PORTS")) {
2151
+ const spacing = getNumberOption(globalOptions, "spacing.portPort") ?? 10;
2152
+ const sideCounts = {
2153
+ NORTH: 0,
2154
+ EAST: 0,
2155
+ SOUTH: 0,
2156
+ WEST: 0
2157
+ };
2158
+ for (const port of node.ports ?? []) {
2159
+ const side = String(getOption(port.layoutOptions ?? {}, "port.side") ?? "EAST");
2160
+ if (side in sideCounts) sideCounts[side]++;
2161
+ }
2162
+ const nodeId = String(node.id);
2163
+ for (const edge of graph.edges ?? []) {
2164
+ const source = String(edge.sources?.[0] ?? edge.source);
2165
+ const target = String(edge.targets?.[0] ?? edge.target);
2166
+ if (source === nodeId) sideCounts.EAST++;
2167
+ if (target === nodeId) sideCounts.WEST++;
2168
+ }
2169
+ width = Math.max(width, (Math.max(sideCounts.NORTH, sideCounts.SOUTH) + 1) * spacing);
2170
+ height = Math.max(height, (Math.max(sideCounts.EAST, sideCounts.WEST) + 1) * spacing);
2171
+ if (!preserveComputedCompoundSize && sideCounts.NORTH === 0 && sideCounts.SOUTH === 0) width = 0;
2172
+ if (!preserveComputedCompoundSize && sideCounts.EAST === 0 && sideCounts.WEST === 0) height = 0;
2173
+ }
2174
+ if (constraints.includes("NODE_LABELS")) {
2175
+ for (const label of node.labels ?? []) {
2176
+ if (!label.text) continue;
2177
+ const placement = String(getOption({
2178
+ ...globalOptions,
2179
+ ...node.layoutOptions,
2180
+ ...label.layoutOptions
2181
+ }, "nodeLabels.placement") ?? "");
2182
+ if (!placement) continue;
2183
+ const labelWidth = label.width ?? 0;
2184
+ const labelHeight = label.height ?? 0;
2185
+ if (placement.includes("OUTSIDE")) {
2186
+ if (!sizeOptions.has("OUTSIDE_NODE_LABELS_OVERHANG")) width = Math.max(width, labelWidth);
2187
+ } else {
2188
+ width = Math.max(width, labelWidth + labelPadding.left + labelPadding.right);
2189
+ const verticalInset = labelHeight * (placement.includes("V_CENTER") ? 1 : 2) + labelPadding.top + labelPadding.bottom;
2190
+ height = Math.max(height, verticalInset);
2191
+ insideHorizontalInset = Math.max(insideHorizontalInset, labelPadding.left + labelPadding.right);
2192
+ insideVerticalInset = Math.max(insideVerticalInset, verticalInset);
2193
+ const key = `${placement.includes("V_CENTER") ? "center" : placement.includes("V_BOTTOM") ? "bottom" : "top"}:${placement.includes("H_CENTER") ? "center" : placement.includes("H_RIGHT") ? "right" : "left"}`;
2194
+ const cell = insideLabelCells.get(key) ?? {
2195
+ width: 0,
2196
+ height: 0
2197
+ };
2198
+ cell.width = Math.max(cell.width, labelWidth);
2199
+ cell.height += labelHeight;
2200
+ insideLabelCells.set(key, cell);
2201
+ }
2202
+ }
2203
+ if (insideLabelCells.size > 0) {
2204
+ const rows = [
2205
+ "top",
2206
+ "center",
2207
+ "bottom"
2208
+ ];
2209
+ const columns = [
2210
+ "left",
2211
+ "center",
2212
+ "right"
2213
+ ];
2214
+ const cellWidth = (row, column) => insideLabelCells.get(`${row}:${column}`)?.width ?? 0;
2215
+ const forceTabular = sizeOptions.has("FORCE_TABULAR_NODE_LABELS");
2216
+ const asymmetrical = sizeOptions.has("ASYMMETRICAL");
2217
+ const globalColumns = columns.map((column) => Math.max(...rows.map((row) => cellWidth(row, column))));
2218
+ const labelGridWidth = forceTabular ? globalColumns.reduce((sum, value) => sum + value, 0) : Math.max(...rows.map((row) => {
2219
+ const left = cellWidth(row, "left");
2220
+ const center = cellWidth(row, "center");
2221
+ const right = cellWidth(row, "right");
2222
+ return asymmetrical ? left + center + right : 2 * Math.max(left, right) + center;
2223
+ }));
2224
+ const labelGridHeight = rows.reduce((sum, row) => sum + Math.max(...columns.map((column) => insideLabelCells.get(`${row}:${column}`)?.height ?? 0)), 0);
2225
+ width = Math.max(width, labelGridWidth + labelPadding.left + labelPadding.right);
2226
+ height = Math.max(height, labelGridHeight + labelPadding.top + labelPadding.bottom);
2227
+ }
2228
+ }
2229
+ if (constraints.includes("MINIMUM_SIZE")) {
2230
+ const configuredMinimum = parseVector(getOption(node.layoutOptions ?? {}, "nodeSize.minimum"));
2231
+ const minimum = {
2232
+ x: configuredMinimum?.x && configuredMinimum.x > 0 ? configuredMinimum.x : sizeOptions.has("DEFAULT_MINIMUM_SIZE") ? 20 : 0,
2233
+ y: configuredMinimum?.y && configuredMinimum.y > 0 ? configuredMinimum.y : sizeOptions.has("DEFAULT_MINIMUM_SIZE") ? 20 : 0
2234
+ };
2235
+ if (sizeOptions.has("MINIMUM_SIZE_ACCOUNTS_FOR_PADDING")) {
2236
+ width = Math.max(width, minimum.x + insideHorizontalInset);
2237
+ height = Math.max(height, minimum.y + insideVerticalInset);
2238
+ } else {
2239
+ width = Math.max(width, minimum.x);
2240
+ height = Math.max(height, minimum.y);
2241
+ }
2242
+ }
2243
+ sizeOptions.has("COMPUTE_PADDING");
2244
+ node.width = width;
2245
+ node.height = height;
2246
+ }
2247
+ }
2248
+ function getOption(options, suffix) {
2249
+ const exactKeys = [
2250
+ suffix,
2251
+ `elk.${suffix}`,
2252
+ `org.eclipse.elk.${suffix}`
2253
+ ];
2254
+ for (const key of exactKeys) if (options[key] !== void 0) return options[key];
2255
+ }
2256
+ const ergonomicallyMappedLayeredSettings = new Set([
2257
+ "direction",
2258
+ "padding",
2259
+ "spacing.node",
2260
+ "spacing.layer"
2261
+ ]);
2262
+ function coerceLayeredOptionValue(value, type) {
2263
+ if (type === "BOOLEAN") {
2264
+ if (typeof value === "boolean") return value;
2265
+ if (typeof value === "string") return value.toLowerCase() === "true";
2266
+ }
2267
+ if (type === "DOUBLE" || type === "INT") {
2268
+ if (typeof value === "number") return value;
2269
+ if (typeof value === "string" && value.trim() !== "") return Number(value);
2270
+ }
2271
+ return value;
2272
+ }
2273
+ function parseIndividualSpacing(value) {
2274
+ if (typeof value === "object" && value !== null) return value;
2275
+ if (typeof value !== "string") return value;
2276
+ const result = {};
2277
+ for (const entry of value.split(/;,;|;/)) {
2278
+ const match = entry.match(/^\s*(?:org\.eclipse\.elk\.)?(?:layered\.)?([^:]+)\s*:\s*(-?\d+(?:\.\d+)?)\s*$/);
2279
+ if (!match) continue;
2280
+ const sourceName = match[1];
2281
+ const definition = require_layered.elkLayeredOptionDefinitions.find((candidate) => {
2282
+ const suffix = candidate.elkId.replace(/^org\.eclipse\.elk\./, "");
2283
+ return candidate.name === sourceName || suffix === sourceName;
2284
+ });
2285
+ if (definition) result[definition.name] = Number(match[2]);
2286
+ }
2287
+ return result;
2288
+ }
2289
+ function parseMargin(value) {
2290
+ if (typeof value === "object" && value !== null) return value;
2291
+ if (typeof value !== "string") return value;
2292
+ return parsePadding(value, 0);
2293
+ }
2294
+ const layeredOptionAliases = /* @__PURE__ */ new Map();
2295
+ for (const definition of require_layered.elkLayeredOptionDefinitions) {
2296
+ const suffix = definition.elkId.replace(/^org\.eclipse\.elk\./, "");
2297
+ [
2298
+ definition.name,
2299
+ suffix,
2300
+ `elk.${suffix}`,
2301
+ definition.elkId
2302
+ ].forEach((key, rank) => {
2303
+ const current = layeredOptionAliases.get(key);
2304
+ if (!current || rank < current.rank) layeredOptionAliases.set(key, {
2305
+ definition,
2306
+ rank
2307
+ });
2308
+ });
2309
+ }
2310
+ const indexByIdCache = /* @__PURE__ */ new WeakMap();
2311
+ /** First element whose stringified id matches, like `find`, via a per-array index. */
2312
+ function findById(items, id) {
2313
+ if (!items) return void 0;
2314
+ let cached = indexByIdCache.get(items);
2315
+ if (!cached || cached.length !== items.length) {
2316
+ const index = /* @__PURE__ */ new Map();
2317
+ items.forEach((item, position$1) => {
2318
+ const key = String(item.id);
2319
+ if (!index.has(key)) index.set(key, position$1);
2320
+ });
2321
+ indexByIdCache.set(items, cached = {
2322
+ length: items.length,
2323
+ index
2324
+ });
2325
+ }
2326
+ const position = cached.index.get(id);
2327
+ return position === void 0 ? void 0 : {
2328
+ item: items[position],
2329
+ position
2330
+ };
2331
+ }
2332
+ function getElementLayeredSettings(options) {
2333
+ const settings = {};
2334
+ const chosen = /* @__PURE__ */ new Map();
2335
+ for (const [key, value] of Object.entries(options)) {
2336
+ const alias = layeredOptionAliases.get(key);
2337
+ if (!alias || value === void 0) continue;
2338
+ const current = chosen.get(alias.definition);
2339
+ if (!current || alias.rank < current.rank) chosen.set(alias.definition, {
2340
+ rank: alias.rank,
2341
+ value
2342
+ });
2343
+ }
2344
+ for (const [definition, { value }] of chosen) {
2345
+ const vectorMatch = definition.name === "port.anchor" && typeof value === "string" ? value.match(/^\s*\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\)\s*$/) : void 0;
2346
+ settings[definition.name] = vectorMatch ? {
2347
+ x: Number(vectorMatch[1]),
2348
+ y: Number(vectorMatch[2])
2349
+ } : definition.name === "spacing.individual" ? parseIndividualSpacing(value) : definition.name === "spacing.portsSurrounding" ? parseMargin(value) : coerceLayeredOptionValue(value, definition.type);
2350
+ }
2351
+ return settings;
2352
+ }
2353
+ function getLayeredSettings(options) {
2354
+ const settings = getElementLayeredSettings(options);
2355
+ const baseValue = settings["spacing.baseValue"];
2356
+ if (baseValue !== void 0) for (const [name, defaultValue] of [
2357
+ ["spacing.componentComponent", 20],
2358
+ ["spacing.edgeEdge", 10],
2359
+ ["spacing.edgeLabel", 2],
2360
+ ["spacing.edgeNode", 10],
2361
+ ["spacing.labelLabel", 0],
2362
+ ["spacing.labelNode", 5],
2363
+ ["spacing.labelPortHorizontal", 1],
2364
+ ["spacing.labelPortVertical", 1],
2365
+ ["spacing.nodeSelfLoop", 10],
2366
+ ["spacing.portPort", 10],
2367
+ ["spacing.edgeEdgeBetweenLayers", 10],
2368
+ ["spacing.edgeNodeBetweenLayers", 10]
2369
+ ]) settings[name] ??= baseValue * defaultValue / 20;
2370
+ for (const name of ergonomicallyMappedLayeredSettings) delete settings[name];
2371
+ return settings;
2372
+ }
2373
+ function getNumberOption(options, suffix) {
2374
+ const value = getOption(options, suffix);
2375
+ if (typeof value === "number" && Number.isFinite(value)) return value;
2376
+ if (typeof value === "string" && value.trim() !== "") {
2377
+ const parsed = Number(value);
2378
+ if (Number.isFinite(parsed)) return parsed;
2379
+ }
2380
+ }
2381
+ function getBooleanOption(options, suffix) {
2382
+ const value = getOption(options, suffix);
2383
+ if (typeof value === "boolean") return value;
2384
+ if (typeof value === "string") {
2385
+ if (value.toLowerCase() === "true") return true;
2386
+ if (value.toLowerCase() === "false") return false;
2387
+ }
2388
+ }
2389
+ function getDirection(options) {
2390
+ const direction = String(getOption(options, "direction") ?? "RIGHT").toLowerCase();
2391
+ return direction === "up" || direction === "left" || direction === "down" ? direction : "right";
2392
+ }
2393
+ function endpoint(value, portOwnerById) {
2394
+ const id = String(value);
2395
+ const ownerId = portOwnerById.get(id);
2396
+ return ownerId === void 0 ? { nodeId: id } : {
2397
+ nodeId: ownerId,
2398
+ port: id
2399
+ };
2400
+ }
2401
+ function isInsideSelfLoop(root, edge, expectedNodeId) {
2402
+ const source = String(edge.sources?.[0] ?? edge.source);
2403
+ if (source !== String(edge.targets?.[0] ?? edge.target) || expectedNodeId !== void 0 && source !== expectedNodeId) return false;
2404
+ const node = root.children?.find((child) => String(child.id) === source);
2405
+ return getBooleanOption(node?.layoutOptions ?? {}, "insideSelfLoops.activate") === true && getBooleanOption(edge.layoutOptions ?? {}, "insideSelfLoops.yo") === true;
2406
+ }
2407
+ function applyInsideSelfLoops(root, baseHeightByNodeId) {
2408
+ const indexByNodeId = /* @__PURE__ */ new Map();
2409
+ for (const edge of root.edges ?? []) {
2410
+ if (!isInsideSelfLoop(root, edge)) continue;
2411
+ const nodeId = String(edge.sources?.[0] ?? edge.source);
2412
+ const node = root.children?.find((child) => String(child.id) === nodeId);
2413
+ const baseHeight = baseHeightByNodeId.get(nodeId);
2414
+ if (!node || baseHeight === void 0) continue;
2415
+ const index = indexByNodeId.get(nodeId) ?? 0;
2416
+ indexByNodeId.set(nodeId, index + 1);
2417
+ const y = (node.y ?? 0) + baseHeight - 2 + index * 11;
2418
+ edge.sections = [{
2419
+ id: `${String(edge.id)}_s0`,
2420
+ startPoint: {
2421
+ x: node.x ?? 0,
2422
+ y
2423
+ },
2424
+ endPoint: {
2425
+ x: (node.x ?? 0) + (node.width ?? 0),
2426
+ y
2427
+ },
2428
+ incomingShape: edge.sources?.[0] ?? edge.source,
2429
+ outgoingShape: edge.targets?.[0] ?? edge.target
2430
+ }];
2431
+ }
2432
+ }
2433
+ function toGraph(root, globalOptions = {}) {
2434
+ const children = (root.children ?? []).filter((child) => getBooleanOption(child.layoutOptions ?? {}, "noLayout") !== true);
2435
+ const nodeIds = new Set(children.map((child) => String(child.id)));
2436
+ const portOwnerById = /* @__PURE__ */ new Map();
2437
+ for (const child of children) for (const port of child.ports ?? []) if (port.id !== void 0) portOwnerById.set(String(port.id), String(child.id));
2438
+ const sourcePortIds = new Set((root.edges ?? []).flatMap((edge) => (edge.sources ?? (edge.source === void 0 ? [] : [edge.source])).map(String)));
2439
+ const targetPortIds = new Set((root.edges ?? []).flatMap((edge) => (edge.targets ?? (edge.target === void 0 ? [] : [edge.target])).map(String)));
2440
+ const sourcePortDegree = /* @__PURE__ */ new Map();
2441
+ const targetPortDegree = /* @__PURE__ */ new Map();
2442
+ const edgeModelOrderByPortId = /* @__PURE__ */ new Map();
2443
+ for (const [edgeIndex, edge] of (root.edges ?? []).entries()) {
2444
+ for (const source of edge.sources ?? (edge.source === void 0 ? [] : [edge.source])) {
2445
+ const id = String(source);
2446
+ sourcePortDegree.set(id, (sourcePortDegree.get(id) ?? 0) + 1);
2447
+ if (portOwnerById.has(id) && !edgeModelOrderByPortId.has(id)) edgeModelOrderByPortId.set(id, edgeIndex);
2448
+ }
2449
+ for (const target of edge.targets ?? (edge.target === void 0 ? [] : [edge.target])) {
2450
+ const id = String(target);
2451
+ targetPortDegree.set(id, (targetPortDegree.get(id) ?? 0) + 1);
2452
+ if (portOwnerById.has(id) && !edgeModelOrderByPortId.has(id)) edgeModelOrderByPortId.set(id, edgeIndex);
2453
+ }
2454
+ }
2455
+ const orderedPorts = (child) => {
2456
+ const ports = [...child.ports ?? []];
2457
+ if (String(getOption(child.layoutOptions ?? {}, "portConstraints")) !== "FIXED_SIDE") return ports;
2458
+ const canonicalSides = getDirection(globalOptions) === "left" ? {
2459
+ NORTH: "NORTH",
2460
+ EAST: "WEST",
2461
+ SOUTH: "SOUTH",
2462
+ WEST: "EAST"
2463
+ } : getDirection(globalOptions) === "down" ? {
2464
+ NORTH: "WEST",
2465
+ EAST: "SOUTH",
2466
+ SOUTH: "EAST",
2467
+ WEST: "NORTH"
2468
+ } : getDirection(globalOptions) === "up" ? {
2469
+ NORTH: "EAST",
2470
+ EAST: "SOUTH",
2471
+ SOUTH: "WEST",
2472
+ WEST: "NORTH"
2473
+ } : {
2474
+ NORTH: "NORTH",
2475
+ EAST: "EAST",
2476
+ SOUTH: "SOUTH",
2477
+ WEST: "WEST"
2478
+ };
2479
+ const side = (port) => canonicalSides[String(getOption(port.layoutOptions ?? {}, "port.side"))] ?? "UNDEFINED";
2480
+ const sideOrder = [
2481
+ "NORTH",
2482
+ "EAST",
2483
+ "SOUTH",
2484
+ "WEST"
2485
+ ];
2486
+ ports.sort((left, right) => {
2487
+ const leftSide = side(left);
2488
+ const rightSide = side(right);
2489
+ const sideDifference = sideOrder.indexOf(leftSide) - sideOrder.indexOf(rightSide);
2490
+ if (sideDifference !== 0) return sideDifference;
2491
+ if (String(getOption(globalOptions, "layered.portSortingStrategy")) === "PORT_DEGREE") {
2492
+ if (leftSide === "EAST") return (sourcePortDegree.get(String(right.id)) ?? 0) - (sourcePortDegree.get(String(left.id)) ?? 0);
2493
+ if (leftSide === "WEST") return (targetPortDegree.get(String(left.id)) ?? 0) - (targetPortDegree.get(String(right.id)) ?? 0);
2494
+ }
2495
+ const modelOrderStrategy = String(getOption(globalOptions, "layered.considerModelOrder.strategy") ?? "NONE");
2496
+ const usePortModelOrder = getBooleanOption(globalOptions, "layered.considerModelOrder.portModelOrder") === true;
2497
+ if (modelOrderStrategy !== "NONE" && !usePortModelOrder) {
2498
+ const leftOrder = edgeModelOrderByPortId.get(String(left.id));
2499
+ const rightOrder = edgeModelOrderByPortId.get(String(right.id));
2500
+ if (leftOrder === void 0 && rightOrder === void 0) return 0;
2501
+ if (leftOrder !== rightOrder) return (leftOrder ?? Infinity) - (rightOrder ?? Infinity);
2502
+ }
2503
+ return (leftSide === "WEST" ? -1 : 1) * ((child.ports?.indexOf(left) ?? 0) - (child.ports?.indexOf(right) ?? 0));
2504
+ });
2505
+ return ports;
2506
+ };
2507
+ const native = (0, _statelyai_graph.createGraph)({
2508
+ id: String(root.id),
2509
+ nodes: children.map((child) => ({
2510
+ id: String(child.id),
2511
+ x: child.x,
2512
+ y: child.y,
2513
+ ...parsePosition(getOption(child.layoutOptions ?? {}, "position")),
2514
+ width: child.width,
2515
+ height: child.height,
2516
+ label: child.labels?.[0]?.text,
2517
+ ports: orderedPorts(child).filter((port) => getBooleanOption(port.layoutOptions ?? {}, "noLayout") !== true).map((port) => ({
2518
+ name: String(port.id),
2519
+ direction: sourcePortIds.has(String(port.id)) ? targetPortIds.has(String(port.id)) ? "inout" : "out" : targetPortIds.has(String(port.id)) ? "in" : "inout",
2520
+ x: port.x,
2521
+ y: port.y,
2522
+ width: port.width,
2523
+ height: port.height
2524
+ }))
2525
+ })),
2526
+ edges: (root.edges ?? []).flatMap((edge) => {
2527
+ if (getBooleanOption(edge.layoutOptions ?? {}, "noLayout") === true) return [];
2528
+ if (isInsideSelfLoop(root, edge)) return [];
2529
+ const source = endpoint(edge.sources?.[0] ?? edge.source, portOwnerById);
2530
+ const target = endpoint(edge.targets?.[0] ?? edge.target, portOwnerById);
2531
+ const labels = (edge.labels ?? []).filter(isLayoutEdgeLabel);
2532
+ const labelLabelSpacing = getNumberOption(globalOptions, "spacing.labelLabel") ?? 0;
2533
+ const labelWidth = Math.max(0, ...labels.map((label) => label.width ?? 0));
2534
+ const labelHeight = labels.reduce((sum, label) => sum + (label.height ?? 0), 0) + Math.max(0, labels.length - 1) * labelLabelSpacing;
2535
+ return nodeIds.has(source.nodeId) && nodeIds.has(target.nodeId) ? [{
2536
+ id: String(edge.id),
2537
+ sourceId: source.nodeId,
2538
+ targetId: target.nodeId,
2539
+ sourcePort: source.port,
2540
+ targetPort: target.port,
2541
+ label: edge.labels?.[0]?.text,
2542
+ width: labelWidth,
2543
+ height: labelHeight,
2544
+ points: parsePoints(getOption(edge.layoutOptions ?? {}, "bendPoints"))
2545
+ }] : [];
2546
+ })
2547
+ });
2548
+ for (const node of native.nodes) {
2549
+ const child = children.find((candidate) => String(candidate.id) === node.id);
2550
+ const origin = child && require_layered.externalPortDummyOf(child);
2551
+ if (origin) require_layered.attachExternalPortDummy(node, origin);
2552
+ }
2553
+ return native;
2554
+ }
2555
+ function parsePoints(value) {
2556
+ if (typeof value !== "string") return void 0;
2557
+ const points = [...value.matchAll(/\{\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\}/g)].map((match) => ({
2558
+ x: Number(match[1]),
2559
+ y: Number(match[2])
2560
+ }));
2561
+ return points.length > 0 ? points : void 0;
2562
+ }
2563
+ function parsePosition(value) {
2564
+ if (typeof value !== "string") return void 0;
2565
+ const match = value.match(/\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\)/);
2566
+ return match ? {
2567
+ x: Number(match[1]),
2568
+ y: Number(match[2])
2569
+ } : void 0;
2570
+ }
2571
+ function toSection(edge, points) {
2572
+ const startPoint = points[0];
2573
+ const endPoint = points.at(-1);
2574
+ if (!startPoint || !endPoint) return void 0;
2575
+ return {
2576
+ id: `${String(edge.id)}_s0`,
2577
+ startPoint: { ...startPoint },
2578
+ endPoint: { ...endPoint },
2579
+ incomingShape: edge.sources?.[0] ?? edge.source,
2580
+ outgoingShape: edge.targets?.[0] ?? edge.target,
2581
+ ...points.length > 2 ? { bendPoints: points.slice(1, -1).map((point) => ({ ...point })) } : {}
2582
+ };
2583
+ }
2584
+ function applyLayout(root, graph, padding, layoutOptions, resultPolicy) {
2585
+ const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
2586
+ const edgeById = new Map(graph.edges.map((edge) => [edge.id, edge]));
2587
+ for (const child of root.children ?? []) {
2588
+ const node = nodeById.get(String(child.id));
2589
+ if (!node) continue;
2590
+ child.x = node.x;
2591
+ child.y = node.y;
2592
+ child.width = node.width;
2593
+ child.height = node.height;
2594
+ placeNodeLabels(child, layoutOptions);
2595
+ for (const port of child.ports ?? []) {
2596
+ if (getBooleanOption(port.layoutOptions ?? {}, "noLayout") === true) continue;
2597
+ const laidOutPort = node.ports?.find((candidate) => candidate.name === String(port.id));
2598
+ if (!laidOutPort) continue;
2599
+ port.x = laidOutPort.x;
2600
+ port.y = laidOutPort.y;
2601
+ port.width = laidOutPort.width;
2602
+ port.height = laidOutPort.height;
2603
+ }
2604
+ placePortLabels(child, layoutOptions);
2605
+ }
2606
+ for (const edge of root.edges ?? []) {
2607
+ if (resultPolicy?.preserveEdgeSections) {
2608
+ for (const section$1 of edge.sections ?? []) {
2609
+ section$1.incomingShape ??= edge.sources?.[0] ?? edge.source;
2610
+ section$1.outgoingShape ??= edge.targets?.[0] ?? edge.target;
2611
+ }
2612
+ continue;
2613
+ }
2614
+ const laidOutEdge = edgeById.get(String(edge.id));
2615
+ if (!laidOutEdge) {
2616
+ if (getBooleanOption(edge.layoutOptions ?? {}, "noLayout") === true) {
2617
+ for (const section$2 of edge.sections ?? []) {
2618
+ section$2.incomingShape ??= edge.sources?.[0] ?? edge.source;
2619
+ section$2.outgoingShape ??= edge.targets?.[0] ?? edge.target;
2620
+ }
2621
+ continue;
2622
+ }
2623
+ const section$1 = getParentEdgeSection(root, edge);
2624
+ if (section$1) {
2625
+ edge.sections = [section$1];
2626
+ edge.container = root.id;
2627
+ }
2628
+ continue;
2629
+ }
2630
+ const nativeJunctions = resultPolicy?.junctionPointsByEdgeId?.get(String(edge.id));
2631
+ if (nativeJunctions) if (nativeJunctions.length) edge.junctionPoints = nativeJunctions.map((point) => ({ ...point }));
2632
+ else delete edge.junctionPoints;
2633
+ const section = toSection(edge, laidOutEdge.points ?? []);
2634
+ edge.sections = section ? [section] : [];
2635
+ if (section) edge.container = root.id;
2636
+ const target = root.children?.find((child) => String(child.id) === String(edge.targets?.[0] ?? edge.target));
2637
+ if (getBooleanOption(target?.layoutOptions ?? {}, "hypernode") === true && (section?.bendPoints?.length ?? 0) > 0) edge.junctionPoints = [section.bendPoints.at(-1)];
2638
+ let labelY = laidOutEdge.y ?? 0;
2639
+ const points = laidOutEdge.points ?? [];
2640
+ const firstPoint = points[0] ?? {
2641
+ x: laidOutEdge.x ?? 0,
2642
+ y: laidOutEdge.y ?? 0
2643
+ };
2644
+ const lastPoint = points.at(-1) ?? firstPoint;
2645
+ const midpointX = (laidOutEdge.x ?? 0) + (laidOutEdge.width ?? 0) / 2;
2646
+ const edgeLabelSpacing = getNumberOption(layoutOptions, "spacing.edgeLabel") ?? 2;
2647
+ const labelLabelSpacing = getNumberOption(layoutOptions, "spacing.labelLabel") ?? 0;
2648
+ const loneLabel = (edge.labels ?? []).filter(isLayoutEdgeLabel).length === 1;
2649
+ for (const label of edge.labels ?? []) {
2650
+ if (getBooleanOption(label.layoutOptions ?? {}, "noLayout") === true) {
2651
+ label.x ??= 0;
2652
+ label.y ??= 0;
2653
+ continue;
2654
+ }
2655
+ if (!isLayoutEdgeLabel(label)) {
2656
+ label.x ??= 0;
2657
+ label.y ??= 0;
2658
+ continue;
2659
+ }
2660
+ const placement = String(getOption(label.layoutOptions ?? {}, "edgeLabels.placement") ?? "CENTER");
2661
+ const width = label.width ?? 0;
2662
+ label.x = loneLabel && placement !== "CENTER" && laidOutEdge.x !== void 0 ? laidOutEdge.x : placement === "TAIL" ? firstPoint.x + edgeLabelSpacing : placement === "HEAD" ? lastPoint.x - width - edgeLabelSpacing : midpointX - width / 2;
2663
+ label.y = labelY;
2664
+ labelY += (label.height ?? 0) + labelLabelSpacing;
2665
+ }
2666
+ }
2667
+ if (!resultPolicy?.skipBoundsNormalization) normalizeElkGraphBounds(root, padding, layoutOptions, resultPolicy?.normalizationBounds, resultPolicy?.placementCrossBounds);
2668
+ if (!resultPolicy?.preserveEdgeSections && String(getOption(layoutOptions, "edgeRouting") ?? "ORTHOGONAL") === "ORTHOGONAL") {
2669
+ const direction = getDirection(layoutOptions);
2670
+ applyOrthogonalJunctions(root, direction === "right" || direction === "left", direction === "right" || direction === "down", getBooleanOption(layoutOptions, "layered.mergeEdges") === true, new Set((root.children ?? []).filter((n) => getBooleanOption(n.layoutOptions ?? {}, "hypernode") === true).map((n) => String(n.id))), new Set((root.edges ?? []).filter((e) => getBooleanOption(e.layoutOptions ?? {}, "noLayout") === true || resultPolicy?.junctionPointsByEdgeId?.has(String(e.id)) === true).map((e) => String(e.id))));
2671
+ }
2672
+ }
2673
+ function normalizeElkGraphBounds(root, padding, layoutOptions = {}, compactionBounds, placementCrossBounds) {
2674
+ const layoutEdges = (root.edges ?? []).filter((edge) => edge.container == null || String(edge.container) === String(root.id));
2675
+ const authoredWidth = root.width;
2676
+ const authoredHeight = root.height;
2677
+ const fixedGraphSize = getBooleanOption(layoutOptions, "nodeSize.fixedGraphSize") === true;
2678
+ const layoutChildren = (root.children ?? []).filter((node) => getBooleanOption(node.layoutOptions ?? {}, "noLayout") !== true);
2679
+ let minimumX = compactionBounds?.left ?? Number.POSITIVE_INFINITY;
2680
+ let minimumY = compactionBounds?.top ?? Number.POSITIVE_INFINITY;
2681
+ for (const node of layoutChildren) {
2682
+ minimumX = Math.min(minimumX, node.x ?? 0, ...(node.labels ?? []).map((label) => (node.x ?? 0) + (label.x ?? 0)), ...(node.ports ?? []).map((port) => (node.x ?? 0) + (port.x ?? 0)), ...(node.ports ?? []).flatMap((port) => (port.labels ?? []).map((label) => (node.x ?? 0) + (port.x ?? 0) + (label.x ?? 0))));
2683
+ minimumY = Math.min(minimumY, node.y ?? 0, ...(node.labels ?? []).map((label) => (node.y ?? 0) + (label.y ?? 0)), ...(node.ports ?? []).map((port) => (node.y ?? 0) + (port.y ?? 0)), ...(node.ports ?? []).flatMap((port) => (port.labels ?? []).map((label) => (node.y ?? 0) + (port.y ?? 0) + (label.y ?? 0))));
2684
+ }
2685
+ for (const edge of layoutEdges) {
2686
+ const laidOutLabels = (edge.labels ?? []).filter(isLayoutEdgeLabel);
2687
+ minimumX = Math.min(minimumX, ...laidOutLabels.map((label) => label.x ?? 0));
2688
+ minimumY = Math.min(minimumY, ...laidOutLabels.map((label) => label.y ?? 0));
2689
+ if (getBooleanOption(edge.layoutOptions ?? {}, "noLayout") !== true) {
2690
+ const points = (edge.sections ?? []).flatMap((section) => [
2691
+ section.startPoint,
2692
+ ...section.bendPoints ?? [],
2693
+ section.endPoint
2694
+ ]);
2695
+ minimumX = Math.min(minimumX, ...points.map((point) => point.x));
2696
+ minimumY = Math.min(minimumY, ...points.map((point) => point.y));
2697
+ }
2698
+ }
2699
+ const shiftX = Number.isFinite(minimumX) ? Math.max(0, padding.left - minimumX) : 0;
2700
+ const shiftY = Number.isFinite(minimumY) ? Math.max(0, padding.top - minimumY) : 0;
2701
+ if (shiftX !== 0 || shiftY !== 0) {
2702
+ for (const node of layoutChildren) {
2703
+ node.x = (node.x ?? 0) + shiftX;
2704
+ node.y = (node.y ?? 0) + shiftY;
2705
+ }
2706
+ for (const edge of layoutEdges) {
2707
+ for (const section of edge.sections ?? []) for (const point of [
2708
+ section.startPoint,
2709
+ ...section.bendPoints ?? [],
2710
+ section.endPoint
2711
+ ]) {
2712
+ point.x += shiftX;
2713
+ point.y += shiftY;
2714
+ }
2715
+ for (const point of edge.junctionPoints ?? []) {
2716
+ point.x += shiftX;
2717
+ point.y += shiftY;
2718
+ }
2719
+ for (const label of (edge.labels ?? []).filter(isLayoutEdgeLabel)) {
2720
+ label.x = (label.x ?? 0) + shiftX;
2721
+ label.y = (label.y ?? 0) + shiftY;
2722
+ }
2723
+ }
2724
+ }
2725
+ const maximumNodeX = Math.max(0, ...layoutChildren.map((node) => (node.x ?? 0) + (node.width ?? 0)));
2726
+ const maximumNodeY = Math.max(0, ...layoutChildren.map((node) => (node.y ?? 0) + (node.height ?? 0)));
2727
+ const layoutEdgePoints = layoutEdges.flatMap((edge) => getBooleanOption(edge.layoutOptions ?? {}, "noLayout") === true ? [] : (edge.sections ?? []).flatMap((section) => [
2728
+ section.startPoint,
2729
+ ...section.bendPoints ?? [],
2730
+ section.endPoint
2731
+ ]));
2732
+ const wrappingStrategy = String(getOption(layoutOptions, "layered.wrapping.strategy") ?? "OFF");
2733
+ const direction = getDirection(layoutOptions);
2734
+ const laidOutChildById = new Map((root.children ?? []).map((child) => [String(child.id), child]));
2735
+ const childByEndpointId = new Map(laidOutChildById);
2736
+ for (const child of root.children ?? []) for (const port of child.ports ?? []) childByEndpointId.set(String(port.id), child);
2737
+ const wrappedEdgeCount = layoutEdges.filter((edge) => {
2738
+ const source = childByEndpointId.get(String(edge.sources?.[0] ?? edge.source));
2739
+ const target = childByEndpointId.get(String(edge.targets?.[0] ?? edge.target));
2740
+ if (!source || !target) return false;
2741
+ return direction === "right" ? (source.x ?? 0) > (target.x ?? 0) : direction === "left" ? (source.x ?? 0) < (target.x ?? 0) : direction === "down" ? (source.y ?? 0) > (target.y ?? 0) : (source.y ?? 0) < (target.y ?? 0);
2742
+ }).length;
2743
+ const hasWrappedEdge = wrappedEdgeCount > 0;
2744
+ const retainPlacementBounds = wrappingStrategy !== "MULTI_EDGE" && !(wrappingStrategy === "SINGLE_EDGE" && hasWrappedEdge) && String(getOption(layoutOptions, "layered.compaction.postCompaction.strategy") ?? "NONE") === "NONE" && getBooleanOption(layoutOptions, "layered.feedbackEdges") !== true && String(getOption(layoutOptions, "layered.layering.nodePromotion.strategy") ?? "NONE") !== "MODEL_ORDER_LEFT_TO_RIGHT";
2745
+ const addBoundaryPixel = retainPlacementBounds && !(root.children ?? []).some((child) => (child.children?.length ?? 0) > 0);
2746
+ const isSelfLoop = (edge) => {
2747
+ const source = childByEndpointId.get(String(edge.sources?.[0] ?? edge.source));
2748
+ return source !== void 0 && source === childByEndpointId.get(String(edge.targets?.[0] ?? edge.target));
2749
+ };
2750
+ const hasSelfLoops = layoutEdges.some(isSelfLoop);
2751
+ const edgeBoundsExtraX = addBoundaryPixel && placementCrossBounds?.axis !== "x" && !(hasSelfLoops && (direction === "right" || direction === "left")) && layoutEdgePoints.length > 0 && Math.max(...layoutEdgePoints.map((point) => point.x)) >= maximumNodeX - 1e-9 ? 1 : 0;
2752
+ const edgeBoundsExtraY = addBoundaryPixel && placementCrossBounds?.axis !== "y" && !(hasSelfLoops && (direction === "down" || direction === "up")) && layoutEdgePoints.length > 0 && Math.max(...layoutEdgePoints.map((point) => point.y)) >= maximumNodeY - 1e-9 ? 1 : 0;
2753
+ const singleMultiEdgeCutBoundsExtraY = wrappingStrategy === "MULTI_EDGE" && wrappedEdgeCount === 1 && layoutEdgePoints.length > 0 && Math.max(...layoutEdgePoints.map((point) => point.y)) >= maximumNodeY - 1e-9 ? 1 : 0;
2754
+ const postCompactionBoundsExtraX = !compactionBounds && !addBoundaryPixel && !hasWrappedEdge && String(getOption(layoutOptions, "layered.compaction.postCompaction.strategy") ?? "NONE") !== "NONE" && layoutEdgePoints.length > 0 && Math.max(...layoutEdgePoints.map((point) => point.x)) > maximumNodeX + 1e-9 ? .04 : 0;
2755
+ const postCompactionBoundsExtraY = !compactionBounds && !addBoundaryPixel && !hasWrappedEdge && String(getOption(layoutOptions, "layered.compaction.postCompaction.strategy") ?? "NONE") !== "NONE" && layoutEdgePoints.length > 0 && Math.max(...layoutEdgePoints.map((point) => point.y)) > maximumNodeY + 1e-9 ? .04 : 0;
2756
+ const calculatedWidth = Math.max(compactionBounds ? compactionBounds.right + shiftX : 0, retainPlacementBounds && placementCrossBounds?.axis === "x" ? placementCrossBounds.maximum + shiftX : 0, ...layoutChildren.flatMap((node) => [
2757
+ (node.x ?? 0) + (node.width ?? 0),
2758
+ ...(node.labels ?? []).map((label) => (node.x ?? 0) + (label.x ?? 0) + (label.width ?? 0)),
2759
+ ...(node.ports ?? []).map((port) => (node.x ?? 0) + (port.x ?? 0) + (port.width ?? 0)),
2760
+ ...(node.ports ?? []).flatMap((port) => (port.labels ?? []).map((label) => (node.x ?? 0) + (port.x ?? 0) + (label.x ?? 0) + (label.width ?? 0)))
2761
+ ]), ...layoutEdges.flatMap((edge) => (edge.labels ?? []).filter(isLayoutEdgeLabel).map((label) => (label.x ?? 0) + (label.width ?? 0))), ...layoutEdges.flatMap((edge) => getBooleanOption(edge.layoutOptions ?? {}, "noLayout") === true ? [] : (edge.sections ?? []).flatMap((section) => [
2762
+ section.startPoint.x + (isSelfLoop(edge) ? 0 : edgeBoundsExtraX),
2763
+ ...(section.bendPoints ?? []).map((point) => point.x + (isSelfLoop(edge) ? 0 : edgeBoundsExtraX)),
2764
+ section.endPoint.x + (isSelfLoop(edge) ? 0 : edgeBoundsExtraX)
2765
+ ]))) + padding.right + postCompactionBoundsExtraX + (getBooleanOption(layoutOptions, "layered.feedbackEdges") === true && (getDirection(layoutOptions) === "down" || getDirection(layoutOptions) === "up") ? 1 : 0);
2766
+ const calculatedHeight = Math.max(compactionBounds ? compactionBounds.bottom + shiftY : 0, retainPlacementBounds && placementCrossBounds?.axis === "y" ? placementCrossBounds.maximum + shiftY : 0, ...layoutChildren.flatMap((node) => [
2767
+ (node.y ?? 0) + (node.height ?? 0),
2768
+ ...(node.labels ?? []).map((label) => (node.y ?? 0) + (label.y ?? 0) + (label.height ?? 0)),
2769
+ ...(node.ports ?? []).map((port) => (node.y ?? 0) + (port.y ?? 0) + (port.height ?? 0)),
2770
+ ...(node.ports ?? []).flatMap((port) => (port.labels ?? []).map((label) => (node.y ?? 0) + (port.y ?? 0) + (label.y ?? 0) + (label.height ?? 0)))
2771
+ ]), ...layoutEdges.flatMap((edge) => (edge.labels ?? []).filter(isLayoutEdgeLabel).map((label) => (label.y ?? 0) + (label.height ?? 0) + (String(getOption(label.layoutOptions ?? {}, "edgeLabels.placement") ?? "CENTER") === "CENTER" && childByEndpointId.get(String(edge.sources?.[0] ?? edge.source)) !== childByEndpointId.get(String(edge.targets?.[0] ?? edge.target)) ? 1 : 0))), ...layoutEdges.flatMap((edge) => getBooleanOption(edge.layoutOptions ?? {}, "noLayout") === true ? [] : (edge.sections ?? []).flatMap((section) => [
2772
+ section.startPoint.y + (isSelfLoop(edge) ? 0 : edgeBoundsExtraY),
2773
+ ...(section.bendPoints ?? []).map((point) => point.y + (isSelfLoop(edge) ? 0 : edgeBoundsExtraY)),
2774
+ section.endPoint.y + (isSelfLoop(edge) ? 0 : edgeBoundsExtraY)
2775
+ ]))) + padding.bottom + singleMultiEdgeCutBoundsExtraY + postCompactionBoundsExtraY + (getBooleanOption(layoutOptions, "layered.feedbackEdges") === true && (getDirection(layoutOptions) === "right" || getDirection(layoutOptions) === "left") ? 1 : 0);
2776
+ root.width = fixedGraphSize ? authoredWidth ?? 0 : calculatedWidth;
2777
+ root.height = fixedGraphSize ? authoredHeight ?? 0 : calculatedHeight;
2778
+ }
2779
+ function getParentEdgeSection(root, edge) {
2780
+ const sourceId = String(edge.sources?.[0] ?? edge.source);
2781
+ const targetId = String(edge.targets?.[0] ?? edge.target);
2782
+ const rootId = String(root.id);
2783
+ const source = root.children?.find((child) => String(child.id) === sourceId);
2784
+ const target = root.children?.find((child) => String(child.id) === targetId);
2785
+ if (source && targetId === rootId) {
2786
+ const startPoint = {
2787
+ x: (source.x ?? 0) + (source.width ?? 0) / 2,
2788
+ y: (source.y ?? 0) + (source.height ?? 0)
2789
+ };
2790
+ return {
2791
+ id: `${String(edge.id)}_s0`,
2792
+ startPoint,
2793
+ endPoint: {
2794
+ x: startPoint.x,
2795
+ y: 0
2796
+ }
2797
+ };
2798
+ }
2799
+ if (sourceId === rootId && target) {
2800
+ const endPoint = {
2801
+ x: (target.x ?? 0) + (target.width ?? 0) / 2,
2802
+ y: target.y ?? 0
2803
+ };
2804
+ return {
2805
+ id: `${String(edge.id)}_s0`,
2806
+ startPoint: {
2807
+ x: endPoint.x,
2808
+ y: 0
2809
+ },
2810
+ endPoint
2811
+ };
2812
+ }
2813
+ }
2814
+ function placeNodeLabels(node, globalOptions) {
2815
+ const labelPadding = parsePadding(getOption(globalOptions, "nodeLabels.padding"), 5);
2816
+ for (const label of node.labels ?? []) {
2817
+ if (!label.text) continue;
2818
+ if (getBooleanOption(label.layoutOptions ?? {}, "noLayout") === true) continue;
2819
+ const placement = String(getOption({
2820
+ ...globalOptions,
2821
+ ...node.layoutOptions,
2822
+ ...label.layoutOptions
2823
+ }, "nodeLabels.placement") ?? "");
2824
+ if (!placement) continue;
2825
+ const width = label.width ?? 0;
2826
+ const height = label.height ?? 0;
2827
+ const nodeWidth = node.width ?? 0;
2828
+ const nodeHeight = node.height ?? 0;
2829
+ label.x = placement.includes("H_CENTER") ? (nodeWidth - width + labelPadding.left - labelPadding.right) / 2 : placement.includes("H_RIGHT") ? nodeWidth - width - labelPadding.right : labelPadding.left;
2830
+ if (placement.includes("OUTSIDE") && placement.includes("V_TOP")) label.y = -height - Number(getOption(globalOptions, "spacing.labelNode") ?? 5);
2831
+ else if (placement.includes("OUTSIDE") && placement.includes("V_BOTTOM")) label.y = nodeHeight + Number(getOption(globalOptions, "spacing.labelNode") ?? 5);
2832
+ else if (placement.includes("V_CENTER")) label.y = (nodeHeight - height + labelPadding.top - labelPadding.bottom) / 2;
2833
+ else if (placement.includes("V_BOTTOM")) label.y = nodeHeight - height - labelPadding.bottom;
2834
+ else label.y = labelPadding.top;
2835
+ }
2836
+ }
2837
+ function placePortLabels(node, globalOptions) {
2838
+ const placement = String(getOption({
2839
+ ...globalOptions,
2840
+ ...node.layoutOptions
2841
+ }, "portLabels.placement") ?? "OUTSIDE");
2842
+ const inside = placement.includes("INSIDE");
2843
+ const alwaysOtherSide = placement.includes("ALWAYS_OTHER_SAME_SIDE");
2844
+ const spaceEfficient = placement.includes("SPACE_EFFICIENT") || String(getOption(node.layoutOptions ?? {}, "nodeSize.options") ?? "").includes("SPACE_EFFICIENT_PORT_LABELS");
2845
+ const horizontalSpacing = getNumberOption(globalOptions, "spacing.labelPortHorizontal") ?? 1;
2846
+ const verticalSpacing = getNumberOption(globalOptions, "spacing.labelPortVertical") ?? 1;
2847
+ const labelSpacing = getNumberOption(globalOptions, "spacing.labelLabel") ?? 0;
2848
+ const treatAsGroup = getBooleanOption(node.layoutOptions ?? {}, "portLabels.treatAsGroup") ?? false;
2849
+ const placeNextToPort = placement.includes("NEXT_TO_PORT_IF_POSSIBLE") || getBooleanOption(node.layoutOptions ?? {}, "portLabels.nextToPortIfPossible") === true;
2850
+ const nodeWidth = node.width ?? 0;
2851
+ for (const port of node.ports ?? []) {
2852
+ if (getBooleanOption(port.layoutOptions ?? {}, "noLayout") === true) continue;
2853
+ const portWidth = port.width ?? 0;
2854
+ const portHeight = port.height ?? 0;
2855
+ const side = (port.x ?? 0) < 0 ? "WEST" : (port.x ?? 0) >= nodeWidth ? "EAST" : (port.y ?? 0) < 0 ? "NORTH" : "SOUTH";
2856
+ const portsOnSide = (node.ports ?? []).filter((candidate) => {
2857
+ return ((candidate.x ?? 0) < 0 ? "WEST" : (candidate.x ?? 0) >= nodeWidth ? "EAST" : (candidate.y ?? 0) < 0 ? "NORTH" : "SOUTH") === side;
2858
+ });
2859
+ const labels = (port.labels ?? []).filter((label) => Boolean(label.text) && getBooleanOption(label.layoutOptions ?? {}, "noLayout") !== true);
2860
+ const totalLabelHeight = labels.reduce((sum, label) => sum + (label.height ?? 0), 0) + Math.max(0, labels.length - 1) * labelSpacing;
2861
+ let stackedY = labels.length > 1 ? inside || placeNextToPort ? treatAsGroup ? (portHeight - totalLabelHeight) / 2 : (portHeight - (labels[0]?.height ?? 0)) / 2 : portHeight + verticalSpacing : void 0;
2862
+ for (const label of labels) {
2863
+ if (!label.text) continue;
2864
+ const width = label.width ?? 0;
2865
+ const height = label.height ?? 0;
2866
+ if (side === "EAST") {
2867
+ label.x = inside ? -width - horizontalSpacing : portWidth + horizontalSpacing;
2868
+ label.y = stackedY ?? (inside || placeNextToPort ? (portHeight - height) / 2 : alwaysOtherSide ? -height - verticalSpacing : portHeight + verticalSpacing);
2869
+ } else if (side === "WEST") {
2870
+ label.x = inside ? portWidth + horizontalSpacing : -width - horizontalSpacing;
2871
+ label.y = stackedY ?? (inside || placeNextToPort ? (portHeight - height) / 2 : alwaysOtherSide ? -height - verticalSpacing : portHeight + verticalSpacing);
2872
+ } else if (side === "NORTH") {
2873
+ label.x = inside || placeNextToPort ? (portWidth - width) / 2 : alwaysOtherSide ? -width - horizontalSpacing : portWidth + horizontalSpacing;
2874
+ label.y = inside ? portHeight + verticalSpacing : -height - verticalSpacing;
2875
+ } else {
2876
+ label.x = inside || placeNextToPort ? (portWidth - width) / 2 : alwaysOtherSide ? -width - horizontalSpacing : portWidth + horizontalSpacing;
2877
+ label.y = inside ? -height - verticalSpacing : portHeight + verticalSpacing;
2878
+ }
2879
+ if (spaceEfficient && portsOnSide[0] === port && portsOnSide.length >= 2 && !placeNextToPort) if (side === "EAST" || side === "WEST") label.y = -height - verticalSpacing;
2880
+ else label.x = -width - horizontalSpacing;
2881
+ if (stackedY !== void 0) stackedY += height + labelSpacing;
2882
+ }
2883
+ }
2884
+ }
2885
+ /** Edges crossing `group`, in the order ELK's CompoundGraphPreprocessor creates its boundary ports. */
2886
+ function elkBoundaryEdgeOrder(group, scope, originals) {
2887
+ const edges = [];
2888
+ const collect = (node) => {
2889
+ edges.push(...node.edges ?? []);
2890
+ for (const child of node.children ?? []) collect(child);
2891
+ };
2892
+ collect(scope);
2893
+ const endpoints = (edge) => originals.get(edge) ?? {
2894
+ sourceId: String(edge.sources?.[0] ?? edge.source),
2895
+ targetId: String(edge.targets?.[0] ?? edge.target)
2896
+ };
2897
+ const order = (node) => {
2898
+ const inside = /* @__PURE__ */ new Set();
2899
+ const mark = (parent) => {
2900
+ for (const child of parent.children ?? []) {
2901
+ inside.add(String(child.id));
2902
+ for (const port of child.ports ?? []) inside.add(String(port.id));
2903
+ mark(child);
2904
+ }
2905
+ };
2906
+ mark(node);
2907
+ const leaves = (edge) => inside.has(endpoints(edge).sourceId) !== inside.has(endpoints(edge).targetId);
2908
+ const result = /* @__PURE__ */ new Set();
2909
+ for (const child of node.children ?? []) if (child.children?.length) {
2910
+ for (const edge of order(child)) if (leaves(edge)) result.add(edge);
2911
+ }
2912
+ for (const child of node.children ?? []) for (const port of [...(child.ports ?? []).map((p) => String(p.id)), String(child.id)]) {
2913
+ for (const edge of edges) if (endpoints(edge).sourceId === port && leaves(edge)) result.add(edge);
2914
+ for (const edge of edges) if (endpoints(edge).targetId === port && leaves(edge)) result.add(edge);
2915
+ }
2916
+ return [...result];
2917
+ };
2918
+ return order(group);
2919
+ }
2920
+ /** Creation order of hierarchy segments in ELK's CompoundGraphPreprocessor. */
2921
+ const hierarchySegmentRank = /* @__PURE__ */ new WeakMap();
2922
+ const segmentOrderByScope = /* @__PURE__ */ new WeakMap();
2923
+ const originalEdgeId = /* @__PURE__ */ new WeakMap();
2924
+ /**
2925
+ * Segments inside `group`, in creation order: the inner pass connects child
2926
+ * groups' exported ports first, then the outer pass leaves `group` itself.
2927
+ */
2928
+ function elkSegmentOrder(group, scope, originals, ownBoundary) {
2929
+ const order = /* @__PURE__ */ new Map();
2930
+ const add = (edge) => {
2931
+ const id = originalEdgeId.get(edge) ?? String(edge.id);
2932
+ if (!order.has(id)) order.set(id, order.size);
2933
+ };
2934
+ for (const child of group.children ?? []) if (child.children?.length) for (const edge of elkBoundaryEdgeOrder(child, scope, originals)) add(edge);
2935
+ if (ownBoundary) for (const edge of elkBoundaryEdgeOrder(group, scope, originals)) add(edge);
2936
+ return order;
2937
+ }
2938
+ const isPostCompactionKey = (key) => /(^|\.)compaction\.postCompaction\.strategy$/.test(key);
2939
+ function usesPostCompaction(options, node) {
2940
+ const active = (values) => Object.entries(values ?? {}).some(([key, value]) => isPostCompactionKey(key) && String(value) !== "NONE");
2941
+ const visit = (current) => active(current.layoutOptions) || active(current.properties) || (current.children ?? []).some(visit);
2942
+ return active(options) || visit(node);
2943
+ }
2944
+ function disablePostCompaction(node) {
2945
+ for (const values of [node.layoutOptions, node.properties]) for (const key of Object.keys(values ?? {})) if (isPostCompactionKey(key)) values[key] = "NONE";
2946
+ for (const child of node.children ?? []) disablePostCompaction(child);
2947
+ return node;
2948
+ }
2949
+ /**
2950
+ * Alternative random seeds worth trying for a compound layout, unless the
2951
+ * graph chooses its hierarchical greedy switch explicitly.
2952
+ */
2953
+ function crossingVariants(options, graph) {
2954
+ const own = {
2955
+ ...options,
2956
+ ...graph.properties,
2957
+ ...graph.layoutOptions
2958
+ };
2959
+ if (!(graph.children ?? []).some((child) => child.children?.length) || getOption(own, "hierarchyHandling") !== "INCLUDE_CHILDREN" || getOption(own, "layered.crossingMinimization.greedySwitchHierarchical.type") !== void 0) return [];
2960
+ return [{ "elk.randomSeed": "2" }, { "elk.randomSeed": "3" }];
2961
+ }
2962
+ /** Whether the graph asks for more than ELK's default layered thoroughness (7). */
2963
+ function thorough(options, graph) {
2964
+ const own = {
2965
+ ...options,
2966
+ ...graph.properties,
2967
+ ...graph.layoutOptions
2968
+ };
2969
+ return Number(getOption(own, "layered.thoroughness") ?? 7) > 7;
2970
+ }
2971
+
2972
+ //#endregion
2973
+ Object.defineProperty(exports, 'ELK', {
2974
+ enumerable: true,
2975
+ get: function () {
2976
+ return ELK;
2977
+ }
2978
+ });
2979
+ Object.defineProperty(exports, 'compileStatechartLayout', {
2980
+ enumerable: true,
2981
+ get: function () {
2982
+ return compileStatechartLayout;
2983
+ }
2984
+ });
2985
+ Object.defineProperty(exports, 'getElkRoutes', {
2986
+ enumerable: true,
2987
+ get: function () {
2988
+ return getElkRoutes;
2989
+ }
2990
+ });
2991
+ Object.defineProperty(exports, 'layoutStatechart', {
2992
+ enumerable: true,
2993
+ get: function () {
2994
+ return layoutStatechart;
2995
+ }
2996
+ });
2997
+ Object.defineProperty(exports, 'scoreStatechartLayout', {
2998
+ enumerable: true,
2999
+ get: function () {
3000
+ return scoreStatechartLayout;
3001
+ }
3002
+ });