@weasel-js/diagram 1.10.1 → 1.11.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.
@@ -0,0 +1,2865 @@
1
+ import { AUTO_POSE_DESCRIPTOR, rotatePoint, rectPath, polygonFromPoints, PATH_M, PATH_L, PATH_C, flattenCubic, PATH_Q, PATH_Z, PathBuilder } from '@weasel-js/core/math';
2
+ import { transformPath } from '@weasel-js/geom';
3
+ import { cssFamilyName } from '@weasel-js/font/metrics';
4
+ import { createSimulation } from '@weasel-js/simulation';
5
+
6
+ // src/outline.ts
7
+ var LEAN = 0.2;
8
+ var POINT = 0.2;
9
+ var KAPPA = 0.5522847498307936;
10
+ function outlinePath(outline, bounds) {
11
+ const { x, y, width: w, height: h } = bounds;
12
+ if (w <= 0 || h <= 0) return rectPath(x, y, Math.max(w, 0), Math.max(h, 0));
13
+ if (typeof outline !== "string") return transformPath(outline, [w, 0, 0, h, x, y]);
14
+ switch (outline) {
15
+ case "rect":
16
+ return rectPath(x, y, w, h);
17
+ case "diamond":
18
+ return polygonFromPoints([
19
+ { x: x + w / 2, y },
20
+ { x: x + w, y: y + h / 2 },
21
+ { x: x + w / 2, y: y + h },
22
+ { x, y: y + h / 2 }
23
+ ]);
24
+ case "parallelogram": {
25
+ const lean = Math.min(w * LEAN, w / 2);
26
+ return polygonFromPoints([
27
+ { x: x + lean, y },
28
+ { x: x + w, y },
29
+ { x: x + w - lean, y: y + h },
30
+ { x, y: y + h }
31
+ ]);
32
+ }
33
+ case "trapezoid":
34
+ return polygonFromPoints([
35
+ { x, y: y + h / 4 },
36
+ { x: x + w, y },
37
+ { x: x + w, y: y + h },
38
+ { x, y: y + h * 3 / 4 }
39
+ ]);
40
+ case "hexagon":
41
+ return polygonFromPoints([
42
+ { x, y: y + h / 2 },
43
+ { x: x + w * POINT, y },
44
+ { x: x + w * (1 - POINT), y },
45
+ { x: x + w, y: y + h / 2 },
46
+ { x: x + w * (1 - POINT), y: y + h },
47
+ { x: x + w * POINT, y: y + h }
48
+ ]);
49
+ case "stadium":
50
+ return stadiumPath(x, y, w, h);
51
+ }
52
+ }
53
+ function stadiumPath(x, y, w, h) {
54
+ const r = Math.min(w, h) / 2;
55
+ const k = r * KAPPA;
56
+ const b = new PathBuilder();
57
+ if (w >= h) {
58
+ const left2 = x + r;
59
+ const right2 = x + w - r;
60
+ const top2 = y;
61
+ const bottom2 = y + h;
62
+ const midY = y + h / 2;
63
+ return b.moveTo(left2, top2).lineTo(right2, top2).curveTo(right2 + k, top2, x + w, midY - k, x + w, midY).curveTo(x + w, midY + k, right2 + k, bottom2, right2, bottom2).lineTo(left2, bottom2).curveTo(left2 - k, bottom2, x, midY + k, x, midY).curveTo(x, midY - k, left2 - k, top2, left2, top2).close().build();
64
+ }
65
+ const top = y + r;
66
+ const bottom = y + h - r;
67
+ const left = x;
68
+ const right = x + w;
69
+ const midX = x + w / 2;
70
+ return b.moveTo(right, top).lineTo(right, bottom).curveTo(right, bottom + k, midX + k, y + h, midX, y + h).curveTo(midX - k, y + h, left, bottom + k, left, bottom).lineTo(left, top).curveTo(left, top - k, midX - k, y, midX, y).curveTo(midX + k, y, right, top - k, right, top).close().build();
71
+ }
72
+ function contentBox(outline, bounds) {
73
+ if (typeof outline !== "string") return bounds;
74
+ const { x, y, width: w, height: h } = bounds;
75
+ switch (outline) {
76
+ case "rect":
77
+ return bounds;
78
+ case "diamond":
79
+ return { x: x + w / 4, y: y + h / 4, width: w / 2, height: h / 2 };
80
+ case "parallelogram": {
81
+ const lean = Math.min(w * LEAN, w / 2);
82
+ return { x: x + lean, y, width: Math.max(w - lean * 2, 0), height: h };
83
+ }
84
+ case "trapezoid":
85
+ return { x, y: y + h / 4, width: w, height: h / 2 };
86
+ case "hexagon":
87
+ return { x: x + w * POINT, y, width: w * (1 - POINT * 2), height: h };
88
+ case "stadium": {
89
+ const r = Math.min(w, h) / 2;
90
+ return w >= h ? { x: x + r, y, width: Math.max(w - r * 2, 0), height: h } : { x, y: y + r, width: w, height: Math.max(h - r * 2, 0) };
91
+ }
92
+ }
93
+ }
94
+ function boxForContent(outline, content) {
95
+ if (typeof outline !== "string") return content;
96
+ switch (outline) {
97
+ case "rect":
98
+ return content;
99
+ case "diamond":
100
+ return { width: content.width * 2, height: content.height * 2 };
101
+ case "parallelogram":
102
+ return { width: content.width / (1 - LEAN * 2), height: content.height };
103
+ case "trapezoid":
104
+ return { width: content.width, height: content.height * 2 };
105
+ case "hexagon":
106
+ return { width: content.width / (1 - POINT * 2), height: content.height };
107
+ case "stadium":
108
+ return { width: content.width + content.height, height: content.height };
109
+ }
110
+ }
111
+ var TOLERANCE = 0.25;
112
+ function outlinePolyline(outline, bounds) {
113
+ const path = outlinePath(outline, bounds);
114
+ return flattenPath(path);
115
+ }
116
+ function flattenPath(path) {
117
+ if (path.kind === "rect") {
118
+ const { x, y, width: w, height: h } = path;
119
+ return [x, y, x + w, y, x + w, y + h, x, y + h, x, y];
120
+ }
121
+ const out = [];
122
+ const { commands, coords } = path;
123
+ let ci = 0;
124
+ let cx = 0;
125
+ let cy = 0;
126
+ let sx = 0;
127
+ let sy = 0;
128
+ for (const cmd of commands) {
129
+ if (cmd === PATH_M) {
130
+ cx = coords[ci];
131
+ cy = coords[ci + 1];
132
+ ci += 2;
133
+ sx = cx;
134
+ sy = cy;
135
+ out.push(cx, cy);
136
+ } else if (cmd === PATH_L) {
137
+ cx = coords[ci];
138
+ cy = coords[ci + 1];
139
+ ci += 2;
140
+ out.push(cx, cy);
141
+ } else if (cmd === PATH_C) {
142
+ const x1 = coords[ci], y1 = coords[ci + 1];
143
+ const x2 = coords[ci + 2], y2 = coords[ci + 3];
144
+ const x3 = coords[ci + 4], y3 = coords[ci + 5];
145
+ ci += 6;
146
+ flattenCubic(cx, cy, x1, y1, x2, y2, x3, y3, TOLERANCE, out);
147
+ cx = x3;
148
+ cy = y3;
149
+ } else if (cmd === PATH_Q) {
150
+ const qx = coords[ci], qy = coords[ci + 1];
151
+ const x3 = coords[ci + 2], y3 = coords[ci + 3];
152
+ ci += 4;
153
+ flattenCubic(
154
+ cx,
155
+ cy,
156
+ cx + 2 / 3 * (qx - cx),
157
+ cy + 2 / 3 * (qy - cy),
158
+ x3 + 2 / 3 * (qx - x3),
159
+ y3 + 2 / 3 * (qy - y3),
160
+ x3,
161
+ y3,
162
+ TOLERANCE,
163
+ out
164
+ );
165
+ cx = x3;
166
+ cy = y3;
167
+ } else if (cmd === PATH_Z) {
168
+ out.push(sx, sy);
169
+ cx = sx;
170
+ cy = sy;
171
+ }
172
+ }
173
+ if (out.length >= 2 && (out[0] !== out[out.length - 2] || out[1] !== out[out.length - 1])) {
174
+ out.push(out[0], out[1]);
175
+ }
176
+ return out;
177
+ }
178
+ function rayHit(poly, center2, point) {
179
+ const dx = point.x - center2.x;
180
+ const dy = point.y - center2.y;
181
+ if (dx === 0 && dy === 0) return null;
182
+ let bestT = -Infinity;
183
+ let hit = null;
184
+ for (let i = 0; i + 3 < poly.length; i += 2) {
185
+ const ax = poly[i], ay = poly[i + 1];
186
+ const bx = poly[i + 2], by = poly[i + 3];
187
+ const ex = bx - ax;
188
+ const ey = by - ay;
189
+ const denom = dx * ey - dy * ex;
190
+ if (denom === 0) continue;
191
+ const t = ((ax - center2.x) * ey - (ay - center2.y) * ex) / denom;
192
+ const u = ((ax - center2.x) * dy - (ay - center2.y) * dx) / denom;
193
+ if (t < 0 || u < 0 || u > 1) continue;
194
+ if (t > bestT) {
195
+ bestT = t;
196
+ hit = { x: center2.x + dx * t, y: center2.y + dy * t };
197
+ }
198
+ }
199
+ return hit;
200
+ }
201
+ function portsOnOutline(ports, outline, bounds) {
202
+ if (bounds.width <= 0 || bounds.height <= 0) return [...ports];
203
+ const poly = outlinePolyline(outline, bounds);
204
+ const center2 = { x: bounds.x + bounds.width / 2, y: bounds.y + bounds.height / 2 };
205
+ return ports.map((port) => {
206
+ const hit = rayHit(poly, center2, port.point);
207
+ return hit === null ? port : { ...port, point: hit };
208
+ });
209
+ }
210
+
211
+ // src/trait.ts
212
+ var DIAGRAM_TRAIT_KEY = "diagram";
213
+ var dataKeyReader = (node) => {
214
+ const data = node.data;
215
+ if (data === null || typeof data !== "object") return null;
216
+ const trait = data[DIAGRAM_TRAIT_KEY];
217
+ if (trait === null || typeof trait !== "object") return null;
218
+ if ("from" in trait && "to" in trait) return null;
219
+ if ("label" in trait || "anchor" in trait || "mark" in trait) return null;
220
+ return trait;
221
+ };
222
+ function createDiagramNodes(entries) {
223
+ const seen = /* @__PURE__ */ new Set();
224
+ for (const entry of entries) {
225
+ if (seen.has(entry.name)) {
226
+ throw new Error(`createDiagramNodes: duplicate entry name "${entry.name}"`);
227
+ }
228
+ seen.add(entry.name);
229
+ }
230
+ return (node) => {
231
+ const own = dataKeyReader(node);
232
+ if (own !== null) return own;
233
+ for (const entry of entries) {
234
+ if (!entry.matches(node.data)) continue;
235
+ return typeof entry.trait === "function" ? entry.trait(node.data) : entry.trait;
236
+ }
237
+ return null;
238
+ };
239
+ }
240
+ function diagramNodeOf(node, read = dataKeyReader) {
241
+ return read(node);
242
+ }
243
+ function isPinned(node, read) {
244
+ return diagramNodeOf(node, read)?.pinned === true;
245
+ }
246
+ var COMPASS = {
247
+ nw: { u: 0, v: 0 },
248
+ n: { u: 0.5, v: 0 },
249
+ ne: { u: 1, v: 0 },
250
+ e: { u: 1, v: 0.5 },
251
+ se: { u: 1, v: 1 },
252
+ s: { u: 0.5, v: 1 },
253
+ sw: { u: 0, v: 1 },
254
+ w: { u: 0, v: 0.5 }
255
+ };
256
+ var DEFAULT_PORTS = Object.freeze([
257
+ { id: "n", at: COMPASS.n },
258
+ { id: "e", at: COMPASS.e },
259
+ { id: "s", at: COMPASS.s },
260
+ { id: "w", at: COMPASS.w }
261
+ ]);
262
+ function normalOf(at2) {
263
+ const x = at2.u * 2 - 1;
264
+ const y = at2.v * 2 - 1;
265
+ const len = Math.hypot(x, y);
266
+ if (len === 0) return null;
267
+ return { x: x / len, y: y / len };
268
+ }
269
+ function portsOf(node, pose, opts = {}) {
270
+ const placer = placerOf(node, pose, opts);
271
+ return placer === null ? [] : placer.specs.map((spec) => placer.place(spec, spec.at, spec.normal));
272
+ }
273
+ function portOf(node, pose, portId, opts = {}) {
274
+ return portsOf(node, pose, opts).find((p) => p.id === portId);
275
+ }
276
+ function portSitesOf(node, pose, portId, opts = {}) {
277
+ const placer = placerOf(node, pose, opts);
278
+ const spec = placer?.specs.find((s) => s.id === portId);
279
+ if (placer === null || spec === void 0) return [];
280
+ return [placer.place(spec, spec.at, spec.normal), ...(spec.sites ?? []).map((at2) => placer.place(spec, at2, at2.normal))];
281
+ }
282
+ function placerOf(node, pose, opts) {
283
+ const trait = diagramNodeOf(node, opts.read);
284
+ if (trait === null) return null;
285
+ const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR;
286
+ const bounds = geometry.getBounds(pose);
287
+ const rotation = geometry.getRotation?.(pose) ?? 0;
288
+ const cx = bounds.x + bounds.width / 2;
289
+ const cy = bounds.y + bounds.height / 2;
290
+ const poly = trait.outline === void 0 || bounds.width <= 0 || bounds.height <= 0 ? null : outlinePolyline(trait.outline, bounds);
291
+ const place = (spec, at2, given) => {
292
+ const anchored = {
293
+ x: bounds.x + at2.u * bounds.width,
294
+ y: bounds.y + at2.v * bounds.height
295
+ };
296
+ const flat = (poly === null ? null : rayHit(poly, { x: cx, y: cy }, anchored)) ?? anchored;
297
+ const normal = given === void 0 ? normalOf(at2) : given;
298
+ if (rotation === 0) {
299
+ return { id: spec.id, nodeId: node.id, point: flat, normal, ...extrasOf(spec) };
300
+ }
301
+ return {
302
+ id: spec.id,
303
+ nodeId: node.id,
304
+ point: rotatePoint(flat.x, flat.y, cx, cy, rotation),
305
+ // A direction rotates about the origin, not about the node's center.
306
+ normal: normal === null ? null : rotatePoint(normal.x, normal.y, 0, 0, rotation),
307
+ ...extrasOf(spec)
308
+ };
309
+ };
310
+ return { specs: trait.ports ?? DEFAULT_PORTS, place };
311
+ }
312
+ function extrasOf(spec) {
313
+ return {
314
+ ...spec.type === void 0 ? {} : { type: spec.type },
315
+ ...spec.label === void 0 ? {} : { label: spec.label }
316
+ };
317
+ }
318
+
319
+ // src/portLabels.ts
320
+ var PORT_LABEL_GAP = 4;
321
+ var EPS = 1e-6;
322
+ function portLabelBox(port, size, gap = PORT_LABEL_GAP) {
323
+ const { point: p, normal: n } = port;
324
+ const { width, height } = size;
325
+ if (n === null) return { x: p.x - width / 2, y: p.y - gap - height, width, height };
326
+ const outX = n.x > 0 ? p.x + gap : p.x - gap - width;
327
+ const outY = n.y > 0 ? p.y + gap : p.y - gap - height;
328
+ const lean = Math.abs(n.x) - Math.abs(n.y);
329
+ if (lean > EPS) return { x: outX, y: p.y - gap / 2 - height, width, height };
330
+ if (lean < -EPS) return { x: p.x + gap / 2, y: outY, width, height };
331
+ return { x: outX, y: outY, width, height };
332
+ }
333
+ function portLabelsOf(trait, bounds, size) {
334
+ const out = [];
335
+ for (const port of portsOf({ id: "", kind: "container", data: null }, bounds, { read: () => trait })) {
336
+ if (port.label === void 0) continue;
337
+ const labeled = port;
338
+ out.push({ port: labeled, box: portLabelBox(labeled, size(labeled.label)) });
339
+ }
340
+ return out;
341
+ }
342
+ function marginOf(bounds, boxes) {
343
+ if (boxes.length === 0) return void 0;
344
+ const m = { top: 0, right: 0, bottom: 0, left: 0 };
345
+ for (const b of boxes) {
346
+ m.top = Math.max(m.top, bounds.y - b.y);
347
+ m.left = Math.max(m.left, bounds.x - b.x);
348
+ m.right = Math.max(m.right, b.x + b.width - (bounds.x + bounds.width));
349
+ m.bottom = Math.max(m.bottom, b.y + b.height - (bounds.y + bounds.height));
350
+ }
351
+ return m;
352
+ }
353
+ function withLabels(ports, labels) {
354
+ if (labels === void 0) return [...ports];
355
+ return ports.map((p) => Object.hasOwn(labels, p.id) ? { ...p, label: labels[p.id] } : p);
356
+ }
357
+ var DEFAULT_PADDING = 8;
358
+ var DEFAULT_GAP = 4;
359
+ var FALLBACK_LINE = 16;
360
+ function canvasMeasure(ctx) {
361
+ return (text, style) => {
362
+ const size = style.fontSize ?? FALLBACK_LINE;
363
+ ctx.font = `${style.bold === true ? "bold " : ""}${size}px ${cssFamilyName(style.fontFamily ?? "sans-serif")}`;
364
+ return { width: ctx.measureText(text).width, height: Math.ceil(size * 1.2) };
365
+ };
366
+ }
367
+ function measureRow(row, measure) {
368
+ const text = (s, style) => measure === void 0 ? {
369
+ width: s.length * (style?.fontSize ?? FALLBACK_LINE) * 0.6,
370
+ height: style?.fontSize === void 0 ? FALLBACK_LINE : Math.ceil(style.fontSize * 1.2)
371
+ } : measure(s, style ?? {});
372
+ switch (row.kind) {
373
+ case "label":
374
+ return text(row.text, row.style);
375
+ case "field": {
376
+ const l = text(row.label, row.style);
377
+ const v = text(row.value, row.style);
378
+ const room = row.room === void 0 ? 0 : text(row.room, row.style).width;
379
+ return { width: l.width + DEFAULT_GAP * 2 + Math.max(v.width, room), height: Math.max(l.height, v.height) };
380
+ }
381
+ case "ports": {
382
+ const side = (ports) => (ports ?? []).reduce((w, p) => Math.max(w, p.label === void 0 ? 0 : text(p.label).width), 0);
383
+ const rows = Math.max(row.left?.length ?? 0, row.right?.length ?? 0, 1);
384
+ return {
385
+ width: side(row.left) + DEFAULT_GAP * 2 + side(row.right),
386
+ height: row.height ?? rows * FALLBACK_LINE
387
+ };
388
+ }
389
+ case "slot":
390
+ return { width: row.width ?? 0, height: row.height };
391
+ }
392
+ }
393
+ function measureBody(spec, measure) {
394
+ const padding = spec.padding ?? DEFAULT_PADDING;
395
+ const gap = spec.gap ?? DEFAULT_GAP;
396
+ let width = 0;
397
+ let height = 0;
398
+ for (const [i, row] of spec.rows.entries()) {
399
+ const m = measureRow(row, measure);
400
+ width = Math.max(width, m.width);
401
+ height += m.height + (i > 0 ? gap : 0);
402
+ }
403
+ const box = boxForContent(spec.outline, {
404
+ width: width + padding * 2,
405
+ height: height + padding * 2
406
+ });
407
+ return { minWidth: box.width, minHeight: box.height };
408
+ }
409
+ function sizeToBody(pose, floor) {
410
+ const width = Math.max(pose.width, floor.minWidth);
411
+ const height = Math.max(pose.height, floor.minHeight);
412
+ if (width === pose.width && height === pose.height) return pose;
413
+ return { ...pose, width, height };
414
+ }
415
+ function layoutBody(spec, bounds, measure) {
416
+ const padding = spec.padding ?? DEFAULT_PADDING;
417
+ const gap = spec.gap ?? DEFAULT_GAP;
418
+ const inner = contentBox(spec.outline, bounds);
419
+ const width = Math.max(inner.width - padding * 2, 0);
420
+ const heights = spec.rows.map((row) => measureRow(row, measure).height);
421
+ const used = heights.reduce((sum, h) => sum + h, 0) + gap * Math.max(heights.length - 1, 0);
422
+ const slack = Math.max(inner.height - padding * 2 - used, 0);
423
+ const share = { top: 0, center: 0.5, bottom: 1 }[spec.verticalAlign ?? "center"];
424
+ let y = inner.y + padding + slack * share;
425
+ return spec.rows.map((row, index) => {
426
+ const h = heights[index];
427
+ const w = row.kind === "slot" && row.width !== void 0 ? Math.min(row.width, width) : width;
428
+ const box = { row, index, x: inner.x + padding + (width - w) / 2, y, width: w, height: h };
429
+ y += h + gap;
430
+ return box;
431
+ });
432
+ }
433
+ function layoutRowPorts(row, box, measure) {
434
+ const width = (label) => label === void 0 ? 0 : measureRow({ kind: "label", text: label }, measure).width;
435
+ const slots = Math.max(row.left?.length ?? 0, row.right?.length ?? 0, 1);
436
+ const slotHeight = box.height / slots;
437
+ const out = [];
438
+ const side = (ports, which) => {
439
+ for (const [index, port] of (ports ?? []).entries()) {
440
+ const w = width(port.label);
441
+ out.push({
442
+ port,
443
+ side: which,
444
+ index,
445
+ x: which === "left" ? box.x : box.x + box.width - w,
446
+ y: box.y + index * slotHeight,
447
+ width: w,
448
+ height: slotHeight
449
+ });
450
+ }
451
+ };
452
+ side(row.left, "left");
453
+ side(row.right, "right");
454
+ return out;
455
+ }
456
+ function bodyTrait(spec, bounds, measure) {
457
+ const outline = spec.outline;
458
+ const compass = withLabels(spec.ports ?? DEFAULT_PORTS, spec.portLabels);
459
+ if (bounds.height <= 0) return { outline, ports: compass };
460
+ const rowPorts = [];
461
+ let hasLeft = false;
462
+ let hasRight = false;
463
+ for (const box of layoutBody(spec, bounds, measure)) {
464
+ if (box.row.kind !== "ports") continue;
465
+ for (const pb of layoutRowPorts(box.row, box, measure)) {
466
+ if (pb.side === "left") hasLeft = true;
467
+ else hasRight = true;
468
+ const v = (pb.y + pb.height / 2 - bounds.y) / bounds.height;
469
+ rowPorts.push(withType(
470
+ { id: pb.port.id, at: { u: pb.side === "left" ? 0 : 1, v } },
471
+ pb.port.type
472
+ ));
473
+ }
474
+ }
475
+ const kept = spec.ports !== void 0 ? compass : compass.filter((p) => !(p.id === "w" && hasLeft) && !(p.id === "e" && hasRight));
476
+ const trait = { outline, ports: [...kept, ...rowPorts] };
477
+ const margin = marginOf(bounds, portLabelsOf(trait, bounds, labelSize(measure)).map((l) => l.box));
478
+ return margin === void 0 ? trait : { ...trait, margin };
479
+ }
480
+ var labelSize = (measure) => (text) => measureRow({ kind: "label", text }, measure);
481
+ function withType(spec, type) {
482
+ return type === void 0 ? spec : { ...spec, type };
483
+ }
484
+ function bodyOutline(spec, bounds) {
485
+ return outlinePath(spec.outline, bounds);
486
+ }
487
+ function buildBody(spec, at2, opts) {
488
+ const pose = sizeToBody(at2, measureBody(spec, opts.measure));
489
+ const trait = bodyTrait(spec, pose, opts.measure);
490
+ const specs = [{
491
+ kind: "container",
492
+ layer: opts.layer,
493
+ pose,
494
+ data: opts.body(trait, pose),
495
+ ...opts.id === void 0 ? {} : { id: opts.id }
496
+ }];
497
+ const leaf = (box, data, rowId) => {
498
+ specs.push({
499
+ kind: "leaf",
500
+ layer: opts.layer,
501
+ pose: { ...pose, x: box.x, y: box.y, width: box.width, height: box.height },
502
+ data,
503
+ pickable: false,
504
+ ...opts.id === void 0 ? {} : { parent: opts.id },
505
+ ...opts.id !== void 0 && rowId !== void 0 ? { id: `${opts.id}/${rowId}` } : {}
506
+ });
507
+ };
508
+ const portLabel = opts.portLabel;
509
+ for (const box of layoutBody(spec, pose, opts.measure)) {
510
+ const data = opts.row(rowText(box.row), box);
511
+ if (data !== null) leaf(box, data, "id" in box.row ? box.row.id : void 0);
512
+ if (portLabel === void 0 || box.row.kind !== "ports") continue;
513
+ for (const pb of layoutRowPorts(box.row, box, opts.measure)) {
514
+ if (pb.port.label === void 0) continue;
515
+ const labelData = portLabel(pb.port.label, pb);
516
+ if (labelData !== null) leaf(pb, labelData);
517
+ }
518
+ }
519
+ const perimeter = portLabel === void 0 ? [] : portLabelsOf(trait, pose, labelSize(opts.measure));
520
+ for (const { port, box } of perimeter) {
521
+ const labelData = portLabel(port.label, { ...box, port: trait.ports.find((p) => p.id === port.id), side: "outside" });
522
+ if (labelData !== null) leaf(box, labelData);
523
+ }
524
+ return { pose, specs };
525
+ }
526
+ function rowText(row) {
527
+ if (row.kind === "label") return row.text;
528
+ if (row.kind === "field") return `${row.label}: ${row.value}`;
529
+ return "";
530
+ }
531
+
532
+ // src/footprint.ts
533
+ function anchorIn(box, p) {
534
+ return {
535
+ u: box.width > 0 ? (p.x - box.x) / box.width : 0.5,
536
+ v: box.height > 0 ? (p.y - box.y) / box.height : 0.5
537
+ };
538
+ }
539
+ function grownBy(bounds, margin) {
540
+ if (margin === void 0) return bounds;
541
+ return {
542
+ x: bounds.x - margin.left,
543
+ y: bounds.y - margin.top,
544
+ width: bounds.width + margin.left + margin.right,
545
+ height: bounds.height + margin.top + margin.bottom
546
+ };
547
+ }
548
+ function footprintNode(node) {
549
+ const m = node.margin;
550
+ if (m === void 0) return node;
551
+ const b = node.bounds;
552
+ const bounds = grownBy(b, m);
553
+ const specs = node.ports ?? DEFAULT_PORTS;
554
+ const trait = { ports: specs, ...node.outline !== void 0 ? { outline: node.outline } : {} };
555
+ const ports = specs.map((spec) => {
556
+ const [at2, ...sites] = portSitesOf({ id: node.id, kind: "leaf", data: null }, b, spec.id, { read: () => trait });
557
+ if (at2 === void 0) return spec;
558
+ return {
559
+ ...spec,
560
+ at: anchorIn(bounds, at2.point),
561
+ normal: at2.normal,
562
+ ...spec.sites !== void 0 ? { sites: sites.map((s) => ({ ...anchorIn(bounds, s.point), normal: s.normal })) } : {}
563
+ };
564
+ });
565
+ const { outline: _o, margin: _m, ...kept } = node;
566
+ return { ...kept, bounds, ports };
567
+ }
568
+ function planWithMargins(graph, plan) {
569
+ if (!graph.nodes.some((n) => n.margin !== void 0)) return plan(graph);
570
+ const nodes = graph.nodes.map(footprintNode);
571
+ const byId = new Map(nodes.map((n) => [n.id, n]));
572
+ const back = (out2) => {
573
+ const moves = /* @__PURE__ */ new Map();
574
+ for (const [id, at2] of out2.moves) {
575
+ const m = graph.node(id)?.margin;
576
+ moves.set(id, m === void 0 ? at2 : { x: at2.x + m.left, y: at2.y + m.top });
577
+ }
578
+ return { ...out2, moves };
579
+ };
580
+ const out = plan({ ...graph, nodes, node: (id) => byId.get(id) });
581
+ return isPromise(out) ? out.then(back) : back(out);
582
+ }
583
+ var isPromise = (answer) => typeof answer?.then === "function";
584
+
585
+ // src/layout.ts
586
+ function planOf(layout, graph, opts) {
587
+ return planWithMargins(graph, (g) => layout.plan !== void 0 ? layout.plan(g, opts) : { moves: layout(g, opts) });
588
+ }
589
+ function plannerPlan(planner, graph, opts) {
590
+ return planWithMargins(graph, (g) => planner.plan(g, opts));
591
+ }
592
+ var DEFAULT_NODE_GAP = 48;
593
+ var DEFAULT_RANK_GAP = 96;
594
+ var DEFAULT_TOLERANCE = 1e-6;
595
+ function axesFor(direction = "down") {
596
+ switch (direction) {
597
+ case "up":
598
+ return { rank: "y", cross: "x", sign: -1 };
599
+ case "right":
600
+ return { rank: "x", cross: "y", sign: 1 };
601
+ case "left":
602
+ return { rank: "x", cross: "y", sign: -1 };
603
+ default:
604
+ return { rank: "y", cross: "x", sign: 1 };
605
+ }
606
+ }
607
+ function extent(bounds, axis) {
608
+ return axis === "x" ? bounds.width : bounds.height;
609
+ }
610
+ function center(bounds, axis) {
611
+ return axis === "x" ? bounds.x + bounds.width / 2 : bounds.y + bounds.height / 2;
612
+ }
613
+ function pinnedSet(graph, opts = {}) {
614
+ const pinned = new Set(opts.pin ?? []);
615
+ for (const node of graph.nodes) {
616
+ if (node.pinned) pinned.add(node.id);
617
+ }
618
+ return pinned;
619
+ }
620
+ function settle(graph, slots, opts = {}) {
621
+ const axes = axesFor(opts.direction);
622
+ const pinned = pinnedSet(graph, opts);
623
+ const tolerance = opts.tolerance ?? DEFAULT_TOLERANCE;
624
+ const placed = /* @__PURE__ */ new Map();
625
+ for (const node of graph.nodes) {
626
+ const slot = slots.get(node.id);
627
+ if (slot === void 0) continue;
628
+ placed.set(node.id, worldOf(slot, extent(node.bounds, axes.rank), axes));
629
+ }
630
+ return translated(graph, placed, pinned, tolerance);
631
+ }
632
+ function worldOf(slot, depth, axes) {
633
+ const along = axes.sign === 1 ? slot.rank : -(slot.rank + depth);
634
+ return axes.rank === "y" ? { x: slot.cross, y: along } : { x: along, y: slot.cross };
635
+ }
636
+ function untilSettled(once, graph, opts = {}, passes = 8) {
637
+ const first = once(graph, opts);
638
+ if (first.size === 0) return first;
639
+ const at2 = new Map(first);
640
+ for (let pass = 1; pass < passes; pass++) {
641
+ const nodes = graph.nodes.map((node) => {
642
+ const to = at2.get(node.id);
643
+ return to === void 0 ? node : { ...node, bounds: { ...node.bounds, x: to.x, y: to.y } };
644
+ });
645
+ const byId = new Map(nodes.map((n) => [n.id, n]));
646
+ const next = once({ ...graph, nodes, node: (id) => byId.get(id) }, opts);
647
+ if (next.size === 0) break;
648
+ for (const [id, to] of next) at2.set(id, to);
649
+ }
650
+ const tolerance = opts.tolerance ?? DEFAULT_TOLERANCE;
651
+ const out = /* @__PURE__ */ new Map();
652
+ for (const node of graph.nodes) {
653
+ const to = at2.get(node.id);
654
+ if (to === void 0) continue;
655
+ if (Math.abs(to.x - node.bounds.x) <= tolerance && Math.abs(to.y - node.bounds.y) <= tolerance) continue;
656
+ out.set(node.id, to);
657
+ }
658
+ return out;
659
+ }
660
+ function translated(graph, placed, pinned, tolerance = DEFAULT_TOLERANCE) {
661
+ const anchor = anchorOffset(graph, placed, pinned);
662
+ const out = /* @__PURE__ */ new Map();
663
+ for (const node of graph.nodes) {
664
+ const at2 = placed.get(node.id);
665
+ if (at2 === void 0 || pinned.has(node.id)) continue;
666
+ const x = at2.x + anchor.x;
667
+ const y = at2.y + anchor.y;
668
+ if (Math.abs(x - node.bounds.x) <= tolerance && Math.abs(y - node.bounds.y) <= tolerance) {
669
+ continue;
670
+ }
671
+ out.set(node.id, { x, y });
672
+ }
673
+ return out;
674
+ }
675
+ function anchorOffset(graph, placed, pinned) {
676
+ for (const node of graph.nodes) {
677
+ if (!pinned.has(node.id)) continue;
678
+ const at2 = placed.get(node.id);
679
+ if (at2 !== void 0) return { x: node.bounds.x - at2.x, y: node.bounds.y - at2.y };
680
+ }
681
+ let minCurX = Infinity;
682
+ let minCurY = Infinity;
683
+ let minNewX = Infinity;
684
+ let minNewY = Infinity;
685
+ for (const node of graph.nodes) {
686
+ const at2 = placed.get(node.id);
687
+ if (at2 === void 0) continue;
688
+ minCurX = Math.min(minCurX, node.bounds.x);
689
+ minCurY = Math.min(minCurY, node.bounds.y);
690
+ minNewX = Math.min(minNewX, at2.x);
691
+ minNewY = Math.min(minNewY, at2.y);
692
+ }
693
+ if (!Number.isFinite(minCurX)) return { x: 0, y: 0 };
694
+ return { x: minCurX - minNewX, y: minCurY - minNewY };
695
+ }
696
+ function packAcross(nodes, axis, gap) {
697
+ const at2 = /* @__PURE__ */ new Map();
698
+ let cursor = 0;
699
+ for (const node of nodes) {
700
+ at2.set(node.id, cursor);
701
+ cursor += extent(node.bounds, axis) + gap;
702
+ }
703
+ return { at: at2, span: nodes.length === 0 ? 0 : cursor - gap };
704
+ }
705
+ function seededOrder(nodes, axis, indexOf) {
706
+ return [...nodes].sort((a, b) => {
707
+ const d = center(a.bounds, axis) - center(b.bounds, axis);
708
+ return d !== 0 ? d : indexOf(a.id) - indexOf(b.id);
709
+ });
710
+ }
711
+ function graphOrder(graph) {
712
+ const index = /* @__PURE__ */ new Map();
713
+ graph.nodes.forEach((node, i) => index.set(node.id, i));
714
+ return (id) => index.get(id) ?? Number.MAX_SAFE_INTEGER;
715
+ }
716
+
717
+ // src/cluster.ts
718
+ function insetOf(inset) {
719
+ if (typeof inset === "number") return { top: inset, right: inset, bottom: inset, left: inset };
720
+ return {
721
+ top: inset?.top ?? DEFAULT_GROUP_INSET,
722
+ right: inset?.right ?? DEFAULT_GROUP_INSET,
723
+ bottom: inset?.bottom ?? DEFAULT_GROUP_INSET,
724
+ left: inset?.left ?? DEFAULT_GROUP_INSET
725
+ };
726
+ }
727
+ var DEFAULT_GROUP_INSET = 12;
728
+ function canonical(inset, axes) {
729
+ if (axes.rank === "y") {
730
+ return axes.sign === 1 ? { crossLo: inset.left, crossHi: inset.right, rankLo: inset.top, rankHi: inset.bottom } : { crossLo: inset.left, crossHi: inset.right, rankLo: inset.bottom, rankHi: inset.top };
731
+ }
732
+ return axes.sign === 1 ? { crossLo: inset.top, crossHi: inset.bottom, rankLo: inset.left, rankHi: inset.right } : { crossLo: inset.top, crossHi: inset.bottom, rankLo: inset.right, rankHi: inset.left };
733
+ }
734
+ function crossInset(inset, axes) {
735
+ const c = canonical(inset, axes);
736
+ return { lo: c.crossLo, hi: c.crossHi };
737
+ }
738
+ function rankGaps(graph, rows, axes, rankGap) {
739
+ const gaps = rows.slice(1).map(() => rankGap);
740
+ const ends2 = /* @__PURE__ */ new Map();
741
+ const starts = /* @__PURE__ */ new Map();
742
+ const reach = (span, side) => {
743
+ const own = canonical(span.group.inset, axes)[side === "lo" ? "rankLo" : "rankHi"];
744
+ const flush = span.children.filter((c) => c[side] === span[side]);
745
+ return own + Math.max(0, ...flush.map((c) => reach(c, side)));
746
+ };
747
+ for (const span of groupSpans(graph, rows)) {
748
+ ends2.set(span.hi, Math.max(ends2.get(span.hi) ?? 0, reach(span, "hi")));
749
+ starts.set(span.lo, Math.max(starts.get(span.lo) ?? 0, reach(span, "lo")));
750
+ }
751
+ for (let i = 0; i < gaps.length; i++) {
752
+ gaps[i] = Math.max(gaps[i], rankGap + (ends2.get(i) ?? 0) + (starts.get(i + 1) ?? 0));
753
+ }
754
+ return gaps;
755
+ }
756
+ function groupSpans(graph, rows) {
757
+ const rankOf = /* @__PURE__ */ new Map();
758
+ rows.forEach((row, i) => row.forEach((n) => rankOf.set(n.id, i)));
759
+ const spans = [];
760
+ const byId = /* @__PURE__ */ new Map();
761
+ for (const group of graph.groups ?? []) {
762
+ const ranks = group.members.map((id) => rankOf.get(id)).filter((r) => r !== void 0);
763
+ if (ranks.length === 0) continue;
764
+ const span = { group, lo: Math.min(...ranks), hi: Math.max(...ranks), children: [] };
765
+ spans.push(span);
766
+ byId.set(group.id, span);
767
+ }
768
+ for (const span of spans) {
769
+ const parent = span.group.parent === void 0 ? void 0 : byId.get(span.group.parent);
770
+ if (parent === void 0) continue;
771
+ span.parent = parent;
772
+ parent.children.push(span);
773
+ }
774
+ return spans;
775
+ }
776
+ function packClusters(graph, rows, preferred2, axes, nodeGap) {
777
+ const spans = groupSpans(graph, rows);
778
+ const spanById = new Map(spans.map((s) => [s.group.id, s]));
779
+ const spanOf4 = (n) => n.group === void 0 ? void 0 : spanById.get(n.group);
780
+ const width = (n) => extent(n.bounds, axes.cross);
781
+ const pref = (n) => preferred2.get(n.id) ?? 0;
782
+ const insets = new Map(spans.map((s) => [s, canonical(s.group.inset, axes)]));
783
+ const centerOf = /* @__PURE__ */ new Map();
784
+ for (const span of spans) {
785
+ const inside = new Set(span.group.members);
786
+ let sum = 0;
787
+ let count = 0;
788
+ for (const row of rows) {
789
+ for (const n of row) {
790
+ if (!inside.has(n.id)) continue;
791
+ sum += pref(n) + width(n) / 2;
792
+ count++;
793
+ }
794
+ }
795
+ centerOf.set(span, count === 0 ? 0 : sum / count);
796
+ }
797
+ const itemsByRow = (owner, among) => rows.map((row, r) => {
798
+ const items = [];
799
+ for (const node of row) {
800
+ if (spanOf4(node) === owner) items.push({ kind: "node", node, center: pref(node) + width(node) / 2 });
801
+ }
802
+ for (const span of among) {
803
+ if (r >= span.lo && r <= span.hi) items.push({ kind: "group", span, center: centerOf.get(span) });
804
+ }
805
+ return items.map((item, i) => ({ item, i })).sort((a, b) => a.item.center - b.item.center || a.i - b.i).map(({ item }) => item);
806
+ });
807
+ const settle2 = (byRow, start2, nodeFloor, origin) => {
808
+ const nodeAt = /* @__PURE__ */ new Map();
809
+ let ends2 = [];
810
+ for (let pass = 0; pass <= start2.size; pass++) {
811
+ let raised = false;
812
+ ends2 = byRow.map((items) => {
813
+ let cursor = origin;
814
+ for (const item of items) {
815
+ if (item.kind === "node") {
816
+ const x2 = Math.max(nodeFloor(item.node), cursor);
817
+ nodeAt.set(item.node.id, x2);
818
+ cursor = x2 + width(item.node) + nodeGap;
819
+ continue;
820
+ }
821
+ const s = start2.get(item.span);
822
+ const x = Math.max(s, cursor);
823
+ if (x > s) {
824
+ start2.set(item.span, x);
825
+ raised = true;
826
+ }
827
+ cursor = x + outer(item.span) + nodeGap;
828
+ }
829
+ return items.length === 0 ? origin : cursor - nodeGap;
830
+ });
831
+ if (!raised) break;
832
+ }
833
+ return { nodeAt, ends: ends2 };
834
+ };
835
+ const blocks = /* @__PURE__ */ new Map();
836
+ const blockOf = (span) => {
837
+ const known = blocks.get(span);
838
+ if (known !== void 0) return known;
839
+ const byRow = itemsByRow(span, span.children);
840
+ const base = Math.min(...rows.flat().filter((n) => span.group.members.includes(n.id) || spanOf4(n) === span).map(pref));
841
+ const childAt = new Map(span.children.map((c) => [c, Math.max(0, centerOf.get(c) - outer(c) / 2 - base)]));
842
+ const { nodeAt, ends: ends2 } = settle2(byRow, childAt, (n) => pref(n) - base, 0);
843
+ const block = { width: Math.max(0, ...ends2), nodeAt, childAt };
844
+ const slack = widen(block, span.group, insets.get(span), axes);
845
+ for (const [id, x] of nodeAt) nodeAt.set(id, x + slack);
846
+ blocks.set(span, block);
847
+ return block;
848
+ };
849
+ function outer(span) {
850
+ const inset = insets.get(span);
851
+ return blockOf(span).width + inset.crossLo + inset.crossHi;
852
+ }
853
+ const roots = spans.filter((s) => s.parent === void 0);
854
+ const start = new Map(roots.map((s) => [s, centerOf.get(s) - outer(s) / 2]));
855
+ const { nodeAt: at2 } = settle2(itemsByRow(void 0, roots), start, pref, -Infinity);
856
+ const place = (span, from) => {
857
+ const origin = from + insets.get(span).crossLo;
858
+ const block = blockOf(span);
859
+ for (const [id, local] of block.nodeAt) at2.set(id, origin + local);
860
+ for (const [child, local] of block.childAt) place(child, origin + local);
861
+ };
862
+ for (const root of roots) place(root, start.get(root));
863
+ return at2;
864
+ }
865
+ function widen(block, group, inset, axes) {
866
+ const floor = axes.cross === "x" ? (group.minWidth ?? 0) - inset.crossLo - inset.crossHi : 0;
867
+ const slack = Math.max(0, floor - block.width) / 2;
868
+ block.width += 2 * slack;
869
+ for (const [child, local] of block.childAt) block.childAt.set(child, local + slack);
870
+ return slack;
871
+ }
872
+ function expandGroupEdges(edges, groups) {
873
+ const byId = new Map(groups.map((g) => [g.id, g]));
874
+ if (byId.size === 0) return [...edges];
875
+ const within = (id, group) => {
876
+ if (!byId.has(id)) return group.members.includes(id);
877
+ for (let at2 = byId.get(id); at2 !== void 0; at2 = at2.parent === void 0 ? void 0 : byId.get(at2.parent)) {
878
+ if (at2 === group) return true;
879
+ }
880
+ return false;
881
+ };
882
+ const entries = /* @__PURE__ */ new Map();
883
+ const exits = /* @__PURE__ */ new Map();
884
+ for (const g of groups) {
885
+ const inside = new Set(g.members);
886
+ const pointedAt = /* @__PURE__ */ new Set();
887
+ const pointing = /* @__PURE__ */ new Set();
888
+ for (const e of edges) {
889
+ if (inside.has(e.from) && inside.has(e.to) && e.from !== e.to) {
890
+ pointedAt.add(e.to);
891
+ pointing.add(e.from);
892
+ }
893
+ }
894
+ const entry = g.members.filter((m) => !pointedAt.has(m));
895
+ const exit = g.members.filter((m) => !pointing.has(m));
896
+ entries.set(g.id, entry.length > 0 ? entry : g.members);
897
+ exits.set(g.id, exit.length > 0 ? exit : g.members);
898
+ }
899
+ const out = [];
900
+ for (const e of edges) {
901
+ const fromGroup = byId.has(e.from) ? e.from : void 0;
902
+ const toGroup = byId.has(e.to) ? e.to : void 0;
903
+ if (fromGroup === void 0 && toGroup === void 0) {
904
+ out.push(e);
905
+ continue;
906
+ }
907
+ if (fromGroup !== void 0 && within(e.to, byId.get(fromGroup))) continue;
908
+ if (toGroup !== void 0 && within(e.from, byId.get(toGroup))) continue;
909
+ const froms = fromGroup !== void 0 ? exits.get(fromGroup) : [e.from];
910
+ const tos = toGroup !== void 0 ? entries.get(toGroup) : [e.to];
911
+ let k = 0;
912
+ const ports = {
913
+ ...fromGroup === void 0 && e.fromPort !== void 0 ? { fromPort: e.fromPort } : {},
914
+ ...toGroup === void 0 && e.toPort !== void 0 ? { toPort: e.toPort } : {},
915
+ ...e.label !== void 0 ? { label: e.label } : {},
916
+ ...e.waypoints !== void 0 ? { waypoints: e.waypoints } : {},
917
+ ...e.authored ? { authored: true } : {}
918
+ };
919
+ for (const from of froms) {
920
+ for (const to of tos) out.push({ id: `${e.id}#${k++}`, from, to, ...ports, origin: { id: e.id, from: e.from, to: e.to } });
921
+ }
922
+ }
923
+ return out;
924
+ }
925
+
926
+ // src/forceGroups.ts
927
+ function resolve(groups, byId) {
928
+ const out = [];
929
+ for (const group of groups) {
930
+ const members = group.members.map((id) => byId.get(id)).filter((b) => b !== void 0);
931
+ if (members.length > 0) out.push({ members, group, children: [] });
932
+ }
933
+ const byGroup = new Map(out.map((g) => [g.group.id, g]));
934
+ for (const g of out) {
935
+ const parent = g.group.parent === void 0 ? void 0 : byGroup.get(g.group.parent);
936
+ if (parent === void 0) continue;
937
+ g.parent = parent;
938
+ parent.children.push(g);
939
+ }
940
+ return out;
941
+ }
942
+ function groupCohesion(groups, byId, strength) {
943
+ const resolved = resolve(groups, byId);
944
+ return (alpha) => {
945
+ for (const { members } of resolved) {
946
+ let cx = 0;
947
+ let cy = 0;
948
+ for (const m of members) {
949
+ cx += m.x;
950
+ cy += m.y;
951
+ }
952
+ cx /= members.length;
953
+ cy /= members.length;
954
+ for (const m of members) {
955
+ m.vx += (cx - m.x) * strength * alpha;
956
+ m.vy += (cy - m.y) * strength * alpha;
957
+ }
958
+ }
959
+ };
960
+ }
961
+ function boxOf(g) {
962
+ let x0 = Infinity;
963
+ let y0 = Infinity;
964
+ let x1 = -Infinity;
965
+ let y1 = -Infinity;
966
+ for (const m of g.members) {
967
+ x0 = Math.min(x0, m.x - m.hw);
968
+ y0 = Math.min(y0, m.y - m.hh);
969
+ x1 = Math.max(x1, m.x + m.hw);
970
+ y1 = Math.max(y1, m.y + m.hh);
971
+ }
972
+ for (const child of g.children) {
973
+ const b = boxOf(child);
974
+ x0 = Math.min(x0, b.x0);
975
+ y0 = Math.min(y0, b.y0);
976
+ x1 = Math.max(x1, b.x1);
977
+ y1 = Math.max(y1, b.y1);
978
+ }
979
+ const { inset } = g.group;
980
+ return { x0: x0 - inset.left, y0: y0 - inset.top, x1: x1 + inset.right, y1: y1 + inset.bottom };
981
+ }
982
+ function clearance(a, b, padding) {
983
+ const gapX = Math.min(a.x1, b.x1) - Math.max(a.x0, b.x0) + padding;
984
+ const gapY = Math.min(a.y1, b.y1) - Math.max(a.y0, b.y0) + padding;
985
+ if (gapX <= 0 || gapY <= 0) return null;
986
+ const dx = b.x0 + b.x1 - (a.x0 + a.x1);
987
+ const dy = b.y0 + b.y1 - (a.y0 + a.y1);
988
+ return gapX < gapY ? { x: (Math.sign(dx) || 1) * gapX, y: 0 } : { x: 0, y: (Math.sign(dy) || 1) * gapY };
989
+ }
990
+ function holds(outer, inner) {
991
+ for (let at2 = inner.parent; at2 !== void 0; at2 = at2.parent) {
992
+ if (at2 === outer) return true;
993
+ }
994
+ return false;
995
+ }
996
+ function shove(bodies, x, y) {
997
+ for (const body of bodies) {
998
+ body.vx += x;
999
+ body.vy += y;
1000
+ }
1001
+ }
1002
+ function separateGroups(groups, bodies, byId, padding) {
1003
+ const resolved = resolve(groups, byId);
1004
+ const memberSets = resolved.map((g) => new Set(g.members));
1005
+ return () => {
1006
+ const boxes = resolved.map(boxOf);
1007
+ for (let i = 0; i < resolved.length; i++) {
1008
+ for (let j = i + 1; j < resolved.length; j++) {
1009
+ if (holds(resolved[i], resolved[j]) || holds(resolved[j], resolved[i])) continue;
1010
+ const push = clearance(boxes[i], boxes[j], padding);
1011
+ if (push === null) continue;
1012
+ shove(resolved[i].members, -push.x / 2, -push.y / 2);
1013
+ shove(resolved[j].members, push.x / 2, push.y / 2);
1014
+ }
1015
+ for (const body of bodies) {
1016
+ if (memberSets[i].has(body)) continue;
1017
+ const box = { x0: body.x - body.hw, y0: body.y - body.hh, x1: body.x + body.hw, y1: body.y + body.hh };
1018
+ const push = clearance(boxes[i], box, padding);
1019
+ if (push === null) continue;
1020
+ shove(resolved[i].members, -push.x / 2, -push.y / 2);
1021
+ body.vx += push.x / 2;
1022
+ body.vy += push.y / 2;
1023
+ }
1024
+ }
1025
+ };
1026
+ }
1027
+
1028
+ // src/force.ts
1029
+ var DEFAULTS = {
1030
+ iterations: 300,
1031
+ linkDistance: 160,
1032
+ linkStrength: 0.35,
1033
+ charge: -3e3,
1034
+ gravity: 0.02,
1035
+ padding: 24,
1036
+ groupStrength: 0.2
1037
+ };
1038
+ var GOLDEN = Math.PI * (3 - Math.sqrt(5));
1039
+ var NUDGE = 0.5;
1040
+ function charge(bodies, strength) {
1041
+ return (alpha) => {
1042
+ for (let i = 0; i < bodies.length; i++) {
1043
+ const a = bodies[i];
1044
+ for (let j = i + 1; j < bodies.length; j++) {
1045
+ const b = bodies[j];
1046
+ const dx = b.x - a.x;
1047
+ const dy = b.y - a.y;
1048
+ const d2 = dx * dx + dy * dy;
1049
+ if (d2 === 0) continue;
1050
+ const w = strength * alpha / d2;
1051
+ a.vx += dx * w;
1052
+ a.vy += dy * w;
1053
+ b.vx -= dx * w;
1054
+ b.vy -= dy * w;
1055
+ }
1056
+ }
1057
+ };
1058
+ }
1059
+ function links(bodies, graph, distance, strength) {
1060
+ return (alpha) => {
1061
+ for (const edge of graph.edges) {
1062
+ const a = bodies.get(edge.from);
1063
+ const b = bodies.get(edge.to);
1064
+ if (a === void 0 || b === void 0 || a === b) continue;
1065
+ const dx = b.x - a.x;
1066
+ const dy = b.y - a.y;
1067
+ const d = Math.hypot(dx, dy);
1068
+ if (d === 0) continue;
1069
+ const pull = (d - distance) / d * alpha * strength;
1070
+ a.vx += dx * pull;
1071
+ a.vy += dy * pull;
1072
+ b.vx -= dx * pull;
1073
+ b.vy -= dy * pull;
1074
+ }
1075
+ };
1076
+ }
1077
+ function separateBoxes(bodies, padding) {
1078
+ return () => {
1079
+ for (let i = 0; i < bodies.length; i++) {
1080
+ const a = bodies[i];
1081
+ for (let j = i + 1; j < bodies.length; j++) {
1082
+ const b = bodies[j];
1083
+ const dx = b.x - a.x;
1084
+ const dy = b.y - a.y;
1085
+ const gapX = a.hw + b.hw + padding - Math.abs(dx);
1086
+ const gapY = a.hh + b.hh + padding - Math.abs(dy);
1087
+ if (gapX <= 0 || gapY <= 0) continue;
1088
+ if (gapX < gapY) {
1089
+ const push = (Math.sign(dx) || 1) * gapX * 0.5;
1090
+ a.vx -= push;
1091
+ b.vx += push;
1092
+ } else {
1093
+ const push = (Math.sign(dy) || 1) * gapY * 0.5;
1094
+ a.vy -= push;
1095
+ b.vy += push;
1096
+ }
1097
+ }
1098
+ }
1099
+ };
1100
+ }
1101
+ function gravity(bodies, at2, strength) {
1102
+ return (alpha) => {
1103
+ for (const body of bodies) {
1104
+ body.vx += (at2.x - body.x) * strength * alpha;
1105
+ body.vy += (at2.y - body.y) * strength * alpha;
1106
+ }
1107
+ };
1108
+ }
1109
+ function forceRelaxation(graph, opts = {}) {
1110
+ const {
1111
+ linkDistance = DEFAULTS.linkDistance,
1112
+ linkStrength = DEFAULTS.linkStrength,
1113
+ charge: chargeStrength = DEFAULTS.charge,
1114
+ gravity: gravityStrength = DEFAULTS.gravity,
1115
+ padding = DEFAULTS.padding,
1116
+ groupStrength = DEFAULTS.groupStrength
1117
+ } = opts;
1118
+ const pinned = pinnedSet(graph, opts);
1119
+ const bodies = graph.nodes.map((node) => {
1120
+ const body = {
1121
+ id: node.id,
1122
+ x: center(node.bounds, "x"),
1123
+ y: center(node.bounds, "y"),
1124
+ hw: node.bounds.width / 2,
1125
+ hh: node.bounds.height / 2
1126
+ };
1127
+ if (pinned.has(node.id)) {
1128
+ body.fx = body.x;
1129
+ body.fy = body.y;
1130
+ }
1131
+ return body;
1132
+ });
1133
+ separate(bodies);
1134
+ const byId = new Map(bodies.map((b) => [b.id, b]));
1135
+ const centroid = {
1136
+ x: bodies.reduce((sum, b) => sum + b.x, 0) / bodies.length,
1137
+ y: bodies.reduce((sum, b) => sum + b.y, 0) / bodies.length
1138
+ };
1139
+ const groups = graph.groups ?? [];
1140
+ const sim = createSimulation({
1141
+ nodes: bodies,
1142
+ forces: [
1143
+ charge(bodies, chargeStrength),
1144
+ links(byId, graph, linkDistance, linkStrength),
1145
+ gravity(bodies, centroid, gravityStrength),
1146
+ separateBoxes(bodies, padding),
1147
+ ...groups.length > 0 ? [groupCohesion(groups, byId, groupStrength), separateGroups(groups, bodies, byId, padding)] : []
1148
+ ]
1149
+ });
1150
+ return {
1151
+ bodies,
1152
+ byId,
1153
+ pinned,
1154
+ sim,
1155
+ placed() {
1156
+ const placed = /* @__PURE__ */ new Map();
1157
+ for (const node of graph.nodes) {
1158
+ const body = byId.get(node.id);
1159
+ placed.set(node.id, {
1160
+ x: body.x - node.bounds.width / 2,
1161
+ y: body.y - node.bounds.height / 2
1162
+ });
1163
+ }
1164
+ return placed;
1165
+ }
1166
+ };
1167
+ }
1168
+ var force = (graph, opts = {}) => {
1169
+ if (graph.nodes.length === 0) return /* @__PURE__ */ new Map();
1170
+ const relaxation = forceRelaxation(graph, opts);
1171
+ relaxation.sim.tick(opts.iterations ?? DEFAULTS.iterations);
1172
+ return translated(graph, relaxation.placed(), relaxation.pinned, opts.tolerance);
1173
+ };
1174
+ function separate(bodies) {
1175
+ const seen = /* @__PURE__ */ new Map();
1176
+ bodies.forEach((body, i) => {
1177
+ const key = `${body.x},${body.y}`;
1178
+ const nth = seen.get(key) ?? 0;
1179
+ seen.set(key, nth + 1);
1180
+ if (nth === 0 || body.fx != null || body.fy != null) return;
1181
+ const angle = GOLDEN * i;
1182
+ const radius = NUDGE * nth;
1183
+ body.x += Math.cos(angle) * radius;
1184
+ body.y += Math.sin(angle) * radius;
1185
+ });
1186
+ }
1187
+
1188
+ // src/layered/cycles.ts
1189
+ function backEdges(graph) {
1190
+ const back = /* @__PURE__ */ new Set();
1191
+ const done = /* @__PURE__ */ new Set();
1192
+ const onStack = /* @__PURE__ */ new Set();
1193
+ const walk = (id) => {
1194
+ if (done.has(id)) return;
1195
+ onStack.add(id);
1196
+ for (const edge of graph.outgoing(id)) {
1197
+ if (edge.to === id) continue;
1198
+ if (onStack.has(edge.to)) back.add(edge.id);
1199
+ else walk(edge.to);
1200
+ }
1201
+ onStack.delete(id);
1202
+ done.add(id);
1203
+ };
1204
+ for (const node of graph.nodes) walk(node.id);
1205
+ return back;
1206
+ }
1207
+ function ranksOf(graph, back) {
1208
+ const ranks = /* @__PURE__ */ new Map();
1209
+ const resolving = /* @__PURE__ */ new Set();
1210
+ const rankOf = (id) => {
1211
+ const known = ranks.get(id);
1212
+ if (known !== void 0) return known;
1213
+ if (resolving.has(id)) return 0;
1214
+ resolving.add(id);
1215
+ let rank = 0;
1216
+ for (const edge of graph.incoming(id)) {
1217
+ if (back.has(edge.id) || edge.from === id) continue;
1218
+ rank = Math.max(rank, rankOf(edge.from) + 1);
1219
+ }
1220
+ resolving.delete(id);
1221
+ ranks.set(id, rank);
1222
+ return rank;
1223
+ };
1224
+ for (const node of graph.nodes) rankOf(node.id);
1225
+ return ranks;
1226
+ }
1227
+
1228
+ // src/route.ts
1229
+ var FACES = {
1230
+ down: { out: "s", in: "n" },
1231
+ up: { out: "n", in: "s" },
1232
+ right: { out: "e", in: "w" },
1233
+ left: { out: "w", in: "e" }
1234
+ };
1235
+ function layeredRoutes(input) {
1236
+ const { ranks, members, compass } = input;
1237
+ const faces = FACES[input.direction ?? "down"];
1238
+ const span = (id) => {
1239
+ const rs = (members.get(id) ?? [id]).map((m) => ranks.get(m)).filter((r) => r !== void 0);
1240
+ return rs.length === 0 ? null : [Math.min(...rs), Math.max(...rs)];
1241
+ };
1242
+ const routes = /* @__PURE__ */ new Map();
1243
+ for (const edge of input.edges) {
1244
+ const from = span(edge.from);
1245
+ const to = span(edge.to);
1246
+ const route = {};
1247
+ if (from !== null && to !== null && (from[1] < to[0] || to[1] < from[0])) {
1248
+ const ahead = from[1] < to[0];
1249
+ if (compass.has(edge.from)) route.fromPort = ahead ? faces.out : faces.in;
1250
+ if (compass.has(edge.to)) route.toPort = ahead ? faces.in : faces.out;
1251
+ }
1252
+ const waypoints = input.waypoints.get(edge.id);
1253
+ if (waypoints !== void 0 && waypoints.length > 0) {
1254
+ route.waypoints = waypoints;
1255
+ const label = input.labels?.get(edge.id);
1256
+ if (label !== void 0) route.labelAt = label;
1257
+ }
1258
+ if (route.fromPort !== void 0 || route.toPort !== void 0 || route.waypoints !== void 0) {
1259
+ routes.set(edge.id, route);
1260
+ }
1261
+ }
1262
+ return routes;
1263
+ }
1264
+ function layeredPorts(graph, ranks, direction) {
1265
+ const faces = FACES[direction ?? "down"];
1266
+ const compass = new Set(graph.groups?.map((g) => g.id) ?? []);
1267
+ for (const node of graph.nodes) {
1268
+ if (hasCompassPort(node, faces.out) && hasCompassPort(node, faces.in)) compass.add(node.id);
1269
+ }
1270
+ const edges = /* @__PURE__ */ new Map();
1271
+ for (const e of graph.edges) {
1272
+ const id = e.origin?.id ?? e.id;
1273
+ if (!edges.has(id)) edges.set(id, { id, from: e.origin?.from ?? e.from, to: e.origin?.to ?? e.to });
1274
+ }
1275
+ const routes = layeredRoutes({
1276
+ edges: [...edges.values()],
1277
+ ranks,
1278
+ waypoints: /* @__PURE__ */ new Map(),
1279
+ members: new Map((graph.groups ?? []).map((g) => [g.id, g.members])),
1280
+ compass,
1281
+ ...direction !== void 0 ? { direction } : {}
1282
+ });
1283
+ return new Map([...routes].map(([id, r]) => [id, {
1284
+ ...r.fromPort !== void 0 ? { from: r.fromPort } : {},
1285
+ ...r.toPort !== void 0 ? { to: r.toPort } : {}
1286
+ }]));
1287
+ }
1288
+ function hasCompassPort(node, face) {
1289
+ const point = COMPASS[face];
1290
+ return (node.ports ?? DEFAULT_PORTS).some((p) => p.id === face && p.at.u === point.u && p.at.v === point.v);
1291
+ }
1292
+ function authoredOver(edges, plan) {
1293
+ return new Map(edges.map((e) => {
1294
+ const ports = plan.ports?.get(e.id);
1295
+ const planned = plan.waypoints?.get(e.id);
1296
+ const fromPort = e.fromPort ?? ports?.from;
1297
+ const toPort = e.toPort ?? ports?.to;
1298
+ const waypoints = e.waypoints ?? (planned !== void 0 && planned.length > 0 ? planned : void 0);
1299
+ const labelAt = e.waypoints === void 0 && waypoints !== void 0 ? plan.labels?.get(e.id) : void 0;
1300
+ return [e.id, {
1301
+ ...fromPort ? { fromPort } : {},
1302
+ ...toPort ? { toPort } : {},
1303
+ ...waypoints ? { waypoints } : {},
1304
+ ...labelAt !== void 0 ? { labelAt } : {}
1305
+ }];
1306
+ }));
1307
+ }
1308
+
1309
+ // src/layered/rank.ts
1310
+ function ranksByLength(graph, back) {
1311
+ const index = /* @__PURE__ */ new Map();
1312
+ graph.nodes.forEach((node, i) => index.set(node.id, i));
1313
+ const arcs = [];
1314
+ for (const edge of graph.edges) {
1315
+ const from = index.get(edge.from);
1316
+ const to = index.get(edge.to);
1317
+ if (from === void 0 || to === void 0 || from === to) continue;
1318
+ arcs.push(back.has(edge.id) ? { tail: to, head: from } : { tail: from, head: to });
1319
+ }
1320
+ const start = longestPath(graph.nodes.length, arcs);
1321
+ const ranks = /* @__PURE__ */ new Map();
1322
+ for (const part of components(graph.nodes.length, arcs)) {
1323
+ const rank = part.nodes.map((g) => start[g]);
1324
+ simplex(part.arcs, rank);
1325
+ const min = Math.min(...rank);
1326
+ part.nodes.forEach((g, i) => ranks.set(graph.nodes[g].id, rank[i] - min));
1327
+ }
1328
+ return ranks;
1329
+ }
1330
+ function longestPath(count, arcs) {
1331
+ const rank = new Array(count).fill(0);
1332
+ const pending = new Array(count).fill(0);
1333
+ const out = Array.from({ length: count }, () => []);
1334
+ for (const arc of arcs) {
1335
+ pending[arc.head]++;
1336
+ out[arc.tail].push(arc.head);
1337
+ }
1338
+ const ready = [];
1339
+ for (let i = 0; i < count; i++) if (pending[i] === 0) ready.push(i);
1340
+ for (let k = 0; k < ready.length; k++) {
1341
+ const u = ready[k];
1342
+ for (const v of out[u]) {
1343
+ rank[v] = Math.max(rank[v], rank[u] + 1);
1344
+ if (--pending[v] === 0) ready.push(v);
1345
+ }
1346
+ }
1347
+ return rank;
1348
+ }
1349
+ function components(count, arcs) {
1350
+ const near = Array.from({ length: count }, () => []);
1351
+ for (const arc of arcs) {
1352
+ near[arc.tail].push(arc.head);
1353
+ near[arc.head].push(arc.tail);
1354
+ }
1355
+ const part = new Array(count).fill(-1);
1356
+ const parts = [];
1357
+ for (let seed = 0; seed < count; seed++) {
1358
+ if (part[seed] !== -1) continue;
1359
+ const id = parts.length;
1360
+ const found = [seed];
1361
+ part[seed] = id;
1362
+ for (let k = 0; k < found.length; k++) {
1363
+ for (const v of near[found[k]]) {
1364
+ if (part[v] === -1) {
1365
+ part[v] = id;
1366
+ found.push(v);
1367
+ }
1368
+ }
1369
+ }
1370
+ parts.push({ nodes: found.sort((a, b) => a - b), arcs: [] });
1371
+ }
1372
+ const local = new Array(count);
1373
+ for (const p of parts) p.nodes.forEach((g, i) => local[g] = i);
1374
+ for (const arc of arcs) {
1375
+ parts[part[arc.tail]].arcs.push({ tail: local[arc.tail], head: local[arc.head] });
1376
+ }
1377
+ return parts;
1378
+ }
1379
+ var slackOf = (arc, rank) => rank[arc.head] - rank[arc.tail] - 1;
1380
+ function simplex(arcs, rank) {
1381
+ if (arcs.length === 0 || arcs.some((arc) => slackOf(arc, rank) < 0)) return;
1382
+ const inTree = feasibleTree(arcs, rank);
1383
+ const cap = 4 * arcs.length + 100;
1384
+ for (let round = 0; round < cap; round++) {
1385
+ const tree2 = postorder(rank.length, arcs, inTree);
1386
+ let leave = -1;
1387
+ let tailInside = false;
1388
+ for (let a = 0; a < arcs.length && leave === -1; a++) {
1389
+ if (!inTree[a]) continue;
1390
+ const cut = cutValue(arcs, a, tree2);
1391
+ if (cut.value < 0) {
1392
+ leave = a;
1393
+ tailInside = cut.tailInside;
1394
+ }
1395
+ }
1396
+ if (leave === -1) return;
1397
+ const inside = insideOf(tree2, childOf(arcs[leave], leave, tree2));
1398
+ let enter = -1;
1399
+ for (let a = 0; a < arcs.length; a++) {
1400
+ const arc = arcs[a];
1401
+ if (inTree[a]) continue;
1402
+ const crosses = tailInside ? !inside(arc.tail) && inside(arc.head) : inside(arc.tail) && !inside(arc.head);
1403
+ if (crosses && (enter === -1 || slackOf(arc, rank) < slackOf(arcs[enter], rank))) enter = a;
1404
+ }
1405
+ if (enter === -1) return;
1406
+ inTree[leave] = false;
1407
+ inTree[enter] = true;
1408
+ tighten(rank, arcs, inTree);
1409
+ }
1410
+ }
1411
+ function feasibleTree(arcs, rank) {
1412
+ const inTree = arcs.map(() => false);
1413
+ const has = rank.map(() => false);
1414
+ has[0] = true;
1415
+ let size = 1;
1416
+ const grow = () => {
1417
+ for (let changed = true; changed; ) {
1418
+ changed = false;
1419
+ arcs.forEach((arc, a) => {
1420
+ if (has[arc.tail] === has[arc.head] || slackOf(arc, rank) !== 0) return;
1421
+ inTree[a] = true;
1422
+ has[arc.tail] = has[arc.head] = true;
1423
+ size++;
1424
+ changed = true;
1425
+ });
1426
+ }
1427
+ };
1428
+ grow();
1429
+ while (size < rank.length) {
1430
+ let best = -1;
1431
+ arcs.forEach((arc2, a) => {
1432
+ if (has[arc2.tail] === has[arc2.head]) return;
1433
+ if (best === -1 || slackOf(arc2, rank) < slackOf(arcs[best], rank)) best = a;
1434
+ });
1435
+ const arc = arcs[best];
1436
+ const delta = has[arc.head] ? -slackOf(arc, rank) : slackOf(arc, rank);
1437
+ for (let v = 0; v < rank.length; v++) if (has[v]) rank[v] += delta;
1438
+ grow();
1439
+ }
1440
+ return inTree;
1441
+ }
1442
+ function treeAdjacency(count, arcs, inTree) {
1443
+ const adj = Array.from({ length: count }, () => []);
1444
+ arcs.forEach((arc, a) => {
1445
+ if (!inTree[a]) return;
1446
+ adj[arc.tail].push(a);
1447
+ adj[arc.head].push(a);
1448
+ });
1449
+ return adj;
1450
+ }
1451
+ function postorder(count, arcs, inTree) {
1452
+ const adj = treeAdjacency(count, arcs, inTree);
1453
+ const low = new Array(count).fill(0);
1454
+ const lim = new Array(count).fill(0);
1455
+ const parentArc = new Array(count).fill(-1);
1456
+ let next = 1;
1457
+ const stack = [{ v: 0, i: 0 }];
1458
+ low[0] = next;
1459
+ while (stack.length > 0) {
1460
+ const top = stack[stack.length - 1];
1461
+ const a = adj[top.v][top.i++];
1462
+ if (a === void 0) {
1463
+ lim[top.v] = next++;
1464
+ stack.pop();
1465
+ continue;
1466
+ }
1467
+ if (a === parentArc[top.v]) continue;
1468
+ const arc = arcs[a];
1469
+ const w = arc.tail === top.v ? arc.head : arc.tail;
1470
+ parentArc[w] = a;
1471
+ low[w] = next;
1472
+ stack.push({ v: w, i: 0 });
1473
+ }
1474
+ return { low, lim, parentArc };
1475
+ }
1476
+ function childOf(arc, a, tree2) {
1477
+ return tree2.parentArc[arc.tail] === a ? arc.tail : arc.head;
1478
+ }
1479
+ function insideOf(tree2, child) {
1480
+ const lo = tree2.low[child];
1481
+ const hi = tree2.lim[child];
1482
+ return (v) => lo <= tree2.lim[v] && tree2.lim[v] <= hi;
1483
+ }
1484
+ function cutValue(arcs, a, tree2) {
1485
+ const treeArc = arcs[a];
1486
+ const child = childOf(treeArc, a, tree2);
1487
+ const inside = insideOf(tree2, child);
1488
+ const tailInside = child === treeArc.tail;
1489
+ let value = 0;
1490
+ for (const arc of arcs) {
1491
+ const from = inside(arc.tail);
1492
+ if (from === inside(arc.head)) continue;
1493
+ value += from === tailInside ? 1 : -1;
1494
+ }
1495
+ return { value, tailInside };
1496
+ }
1497
+ function tighten(rank, arcs, inTree) {
1498
+ const adj = treeAdjacency(rank.length, arcs, inTree);
1499
+ const seen = rank.map(() => false);
1500
+ seen[0] = true;
1501
+ const queue = [0];
1502
+ for (let k = 0; k < queue.length; k++) {
1503
+ const v = queue[k];
1504
+ for (const a of adj[v]) {
1505
+ const arc = arcs[a];
1506
+ const w = arc.tail === v ? arc.head : arc.tail;
1507
+ if (seen[w]) continue;
1508
+ seen[w] = true;
1509
+ rank[w] = arc.tail === v ? rank[v] + 1 : rank[v] - 1;
1510
+ queue.push(w);
1511
+ }
1512
+ }
1513
+ }
1514
+
1515
+ // src/layered/types.ts
1516
+ var PLACEHOLDER = "\0hop:";
1517
+ var isPlaceholder = (id) => id.startsWith(PLACEHOLDER);
1518
+
1519
+ // src/layered/expand.ts
1520
+ function labelRowsOf(drawn, ranks, span) {
1521
+ const gapOf = /* @__PURE__ */ new Map();
1522
+ for (const d of drawn) {
1523
+ if (d.label === void 0) continue;
1524
+ const a = span(d.back ? d.to : d.from, ranks);
1525
+ const b = span(d.back ? d.from : d.to, ranks);
1526
+ if (a === null || b === null || a[1] >= b[0]) continue;
1527
+ gapOf.set(d.id, a[1] + Math.floor((b[0] - a[1] - 1) / 2));
1528
+ }
1529
+ const gaps = [...new Set(gapOf.values())];
1530
+ const rowOf = (r) => r + gaps.filter((g) => g < r).length;
1531
+ const rows = new Map([...ranks].map(([id, r]) => [id, rowOf(r)]));
1532
+ return { rows, labelRow: new Map([...gapOf].map(([id, g]) => [id, rowOf(g) + 1])) };
1533
+ }
1534
+ function labelRoom(room, axes, back, nodeGap) {
1535
+ const across = extent({ width: room.width, height: room.height }, axes.cross);
1536
+ const depth = extent({ width: room.width, height: room.height }, axes.rank);
1537
+ const offset = room.offset ?? 0;
1538
+ if (offset === 0) return { size: across, depth, wire: across / 2 };
1539
+ const left = (axes.rank === "y" ? 1 : -1) * axes.sign * (back ? -1 : 1);
1540
+ const margin = nodeGap / 4;
1541
+ const size = margin + Math.abs(offset) + across;
1542
+ return { size, depth, wire: left * Math.sign(offset) > 0 ? margin : size - margin };
1543
+ }
1544
+ var MIDDLE = [0.5];
1545
+ function portPlaces(node, port, axes) {
1546
+ if (node === void 0 || port === void 0) return MIDDLE;
1547
+ const { bounds } = node;
1548
+ const trait = { ...node.ports !== void 0 ? { ports: node.ports } : {}, ...node.outline !== void 0 ? { outline: node.outline } : {} };
1549
+ const sites = portSitesOf({ id: node.id, kind: "leaf", data: null }, bounds, port, { read: () => trait });
1550
+ const size = extent(bounds, axes.cross);
1551
+ if (sites.length === 0 || size <= 0) return MIDDLE;
1552
+ const low = axes.cross === "x" ? bounds.x : bounds.y;
1553
+ return sites.map((p) => (p.point[axes.cross] - low) / size);
1554
+ }
1555
+ function portOf2(places, side) {
1556
+ const at2 = places[0];
1557
+ if (side === "from") return places.length > 1 ? { fromAt: at2, fromPlaces: places } : { fromAt: at2 };
1558
+ return places.length > 1 ? { toAt: at2, toPlaces: places } : { toAt: at2 };
1559
+ }
1560
+ function expand(graph, nodeRanks, back, opts) {
1561
+ const { axes } = opts;
1562
+ const groups = new Map((graph.groups ?? []).map((g) => [g.id, g]));
1563
+ const spanIn = (id, ranks2) => {
1564
+ const ids = groups.get(id)?.members ?? [id];
1565
+ const rs = ids.map((m) => ranks2.get(m)).filter((r) => r !== void 0);
1566
+ return rs.length === 0 ? null : [Math.min(...rs), Math.max(...rs)];
1567
+ };
1568
+ const drawn = drawnEdges(graph.edges, back);
1569
+ const labeled = labelRowsOf(drawn, nodeRanks, spanIn);
1570
+ const ranks = labeled.rows;
1571
+ const span = (id) => spanIn(id, ranks);
1572
+ const inRank = (id, r) => (groups.get(id)?.members ?? [id]).filter((m) => ranks.get(m) === r && graph.node(m) !== void 0);
1573
+ const ancestry = (id) => {
1574
+ const out = [];
1575
+ let at2 = groups.has(id) ? groups.get(id).parent : graph.node(id)?.group;
1576
+ while (at2 !== void 0) {
1577
+ out.push(at2);
1578
+ at2 = groups.get(at2)?.parent;
1579
+ }
1580
+ return out;
1581
+ };
1582
+ const sharedGroup = (a, b) => {
1583
+ const up = new Set(ancestry(b));
1584
+ return ancestry(a).find((g) => up.has(g));
1585
+ };
1586
+ const crossCenter = (id) => {
1587
+ const node = graph.node(id);
1588
+ if (node !== void 0) return center(node.bounds, axes.cross);
1589
+ const ms = (groups.get(id)?.members ?? []).map((m) => graph.node(m)).filter((n) => n !== void 0);
1590
+ return ms.length === 0 ? 0 : ms.reduce((s, n) => s + center(n.bounds, axes.cross), 0) / ms.length;
1591
+ };
1592
+ const byRank = /* @__PURE__ */ new Map();
1593
+ const add = (r, n) => byRank.set(r, [...byRank.get(r) ?? [], n]);
1594
+ for (const node of graph.nodes) add(ranks.get(node.id) ?? 0, node);
1595
+ const hops = [];
1596
+ const chains = /* @__PURE__ */ new Map();
1597
+ const labels = /* @__PURE__ */ new Map();
1598
+ const wires = /* @__PURE__ */ new Map();
1599
+ for (const d of drawn) {
1600
+ const [from, to] = d.back ? [d.to, d.from] : [d.from, d.to];
1601
+ const [fromPort, toPort] = d.back ? [d.toPort, d.fromPort] : [d.fromPort, d.toPort];
1602
+ const leave = (id) => id === from ? portOf2(portPlaces(graph.node(id), fromPort, axes), "from") : {};
1603
+ const arrive = (id) => id === to ? portOf2(portPlaces(graph.node(id), toPort, axes), "to") : {};
1604
+ const a = span(from);
1605
+ const b = span(to);
1606
+ if (a === null || b === null || a[1] >= b[0]) continue;
1607
+ if (d.authored && b[0] - a[1] > 1) continue;
1608
+ const group = sharedGroup(from, to);
1609
+ const start = crossCenter(from);
1610
+ const end = crossCenter(to);
1611
+ const crossings = b[0] - a[1] - 1;
1612
+ const now = d.waypoints?.length === crossings * 2 ? d.waypoints : void 0;
1613
+ const seed = (j) => {
1614
+ if (now !== void 0) return now[(d.back ? crossings - 1 - j : j) * 2][axes.cross];
1615
+ return start + (end - start) * (j + 1) / (crossings + 1);
1616
+ };
1617
+ const labelRow = labeled.labelRow.get(d.id);
1618
+ let prev = inRank(from, a[1]);
1619
+ let prevAt = null;
1620
+ const chain2 = [];
1621
+ for (let r = a[1] + 1; r < b[0]; r++) {
1622
+ const id = `${PLACEHOLDER}${d.id}:${r}`;
1623
+ const room = r === labelRow && d.label !== void 0 ? labelRoom(d.label, axes, d.back, opts.nodeGap) : { size: opts.nodeGap / 2, depth: 0, wire: opts.nodeGap / 4 };
1624
+ const at2 = seed(r - a[1] - 1) - room.wire;
1625
+ const bounds = axes.cross === "x" ? { x: at2, y: 0, width: room.size, height: room.depth } : { x: 0, y: at2, width: room.depth, height: room.size };
1626
+ add(r, { id, bounds, pinned: false, ...group !== void 0 ? { group } : {} });
1627
+ const toAt = room.wire / room.size;
1628
+ for (const p of prev) hops.push({ from: p, to: id, edge: d.id, ...prevAt ?? leave(p), toAt });
1629
+ if (r === labelRow) {
1630
+ labels.set(d.id, id);
1631
+ wires.set(id, room.wire);
1632
+ }
1633
+ prev = [id];
1634
+ prevAt = { fromAt: toAt };
1635
+ chain2.push(id);
1636
+ }
1637
+ for (const p of prev) for (const q of inRank(to, b[0])) hops.push({ from: p, to: q, edge: d.id, ...prevAt ?? leave(p), ...arrive(q) });
1638
+ if (chain2.length > 0) {
1639
+ const ends2 = [...groups.get(from)?.members ?? [from], ...groups.get(to)?.members ?? [to]];
1640
+ chains.set(d.id, { ids: d.back ? chain2.reverse() : chain2, back: d.back, ends: ends2 });
1641
+ }
1642
+ }
1643
+ const order = graphOrder(graph);
1644
+ const top = Math.max(-1, ...byRank.keys());
1645
+ const rows = [];
1646
+ for (let r = 0; r <= top; r++) {
1647
+ const row = byRank.get(r) ?? [];
1648
+ rows.push(seededOrder(row, axes.cross, order));
1649
+ }
1650
+ return { rows, hops: dedupe(hops), chains, labelRows: new Set(labeled.labelRow.values()), labels, wires };
1651
+ }
1652
+ function drawnEdges(edges, back) {
1653
+ const seen = /* @__PURE__ */ new Map();
1654
+ for (const e of edges) {
1655
+ const id = e.origin?.id ?? e.id;
1656
+ if (seen.has(id)) continue;
1657
+ const from = e.origin?.from ?? e.from;
1658
+ const to = e.origin?.to ?? e.to;
1659
+ if (from === to) continue;
1660
+ seen.set(id, {
1661
+ id,
1662
+ from,
1663
+ to,
1664
+ ...e.fromPort !== void 0 ? { fromPort: e.fromPort } : {},
1665
+ ...e.toPort !== void 0 ? { toPort: e.toPort } : {},
1666
+ ...e.label !== void 0 ? { label: e.label } : {},
1667
+ ...e.waypoints !== void 0 ? { waypoints: e.waypoints } : {},
1668
+ ...e.authored ? { authored: true } : {},
1669
+ back: back.has(e.id)
1670
+ });
1671
+ }
1672
+ return [...seen.values()];
1673
+ }
1674
+ function dedupe(hops) {
1675
+ const seen = /* @__PURE__ */ new Set();
1676
+ return hops.filter((h) => {
1677
+ const key = `${h.from}\0${h.to}\0${h.edge}`;
1678
+ if (seen.has(key)) return false;
1679
+ seen.add(key);
1680
+ return true;
1681
+ });
1682
+ }
1683
+ function depthOf(row, axes) {
1684
+ return Math.max(0, ...row.map((n) => extent(n.bounds, axes.rank)));
1685
+ }
1686
+
1687
+ // src/layered/places.ts
1688
+ function sidesOf(hops) {
1689
+ const down = /* @__PURE__ */ new Map();
1690
+ const up = /* @__PURE__ */ new Map();
1691
+ for (const h of hops) {
1692
+ (down.get(h.from) ?? down.set(h.from, []).get(h.from)).push(h.to);
1693
+ (up.get(h.to) ?? up.set(h.to, []).get(h.to)).push(h.from);
1694
+ }
1695
+ return { down, up };
1696
+ }
1697
+ function placeAmong(places, other, neighbors, pos) {
1698
+ const p = pos.get(other) ?? 0;
1699
+ let before = 0;
1700
+ let level = 0;
1701
+ for (const n of neighbors) {
1702
+ const q = pos.get(n) ?? 0;
1703
+ if (q < p) before++;
1704
+ else if (q === p) level++;
1705
+ }
1706
+ const count = Math.max(neighbors.length, 1);
1707
+ const expected = (before + Math.max(level, 1) / 2) / count;
1708
+ let best = places[0];
1709
+ for (const at2 of places) if (Math.abs(at2 - expected) < Math.abs(best - expected)) best = at2;
1710
+ return best;
1711
+ }
1712
+ function settlePlaces(rows, hops) {
1713
+ if (!hops.some((h) => h.fromPlaces !== void 0 || h.toPlaces !== void 0)) return [...hops];
1714
+ const pos = /* @__PURE__ */ new Map();
1715
+ for (const row of rows) row.forEach((n, i) => pos.set(n.id, i));
1716
+ const sides = sidesOf(hops);
1717
+ return hops.map((h) => {
1718
+ if (h.fromPlaces === void 0 && h.toPlaces === void 0) return h;
1719
+ const { fromPlaces, toPlaces, ...rest } = h;
1720
+ return {
1721
+ ...rest,
1722
+ ...fromPlaces !== void 0 ? { fromAt: placeAmong(fromPlaces, h.to, sides.down.get(h.from) ?? [], pos) } : {},
1723
+ ...toPlaces !== void 0 ? { toAt: placeAmong(toPlaces, h.from, sides.up.get(h.to) ?? [], pos) } : {}
1724
+ };
1725
+ });
1726
+ }
1727
+
1728
+ // src/layered/order.ts
1729
+ function ends(p, pos, sides) {
1730
+ const fa = p.fromPlaces === void 0 ? p.fromAt : placeAmong(p.fromPlaces, p.to, sides.down.get(p.from) ?? [], pos);
1731
+ const fb = p.toPlaces === void 0 ? p.toAt : placeAmong(p.toPlaces, p.from, sides.up.get(p.to) ?? [], pos);
1732
+ return [at(pos.get(p.from), fa), at(pos.get(p.to), fb)];
1733
+ }
1734
+ var at = (pos, frac) => pos + 0.25 + frac / 2;
1735
+ function hopsByRank(rows, hops) {
1736
+ const rank = /* @__PURE__ */ new Map();
1737
+ rows.forEach((row, r) => row.forEach((n) => rank.set(n.id, r)));
1738
+ const out = rows.map(() => []);
1739
+ for (const h of hops) {
1740
+ const r = rank.get(h.from);
1741
+ if (r !== void 0 && rank.get(h.to) === r + 1) {
1742
+ out[r].push({ from: h.from, to: h.to, fromAt: h.fromAt ?? 0.5, toAt: h.toAt ?? 0.5, fromPlaces: h.fromPlaces, toPlaces: h.toPlaces });
1743
+ }
1744
+ }
1745
+ return out;
1746
+ }
1747
+ function positions(rows) {
1748
+ const pos = /* @__PURE__ */ new Map();
1749
+ for (const row of rows) row.forEach((n, i) => pos.set(n.id, i));
1750
+ return pos;
1751
+ }
1752
+ function pairCrossings(pairs, pos, sides) {
1753
+ const met = pairs.map((p) => ends(p, pos, sides)).sort((p, q) => p[0] - q[0] || p[1] - q[1]);
1754
+ const lows = [...new Set(met.map(([, b]) => b))].sort((p, q) => p - q);
1755
+ const slot = new Map(lows.map((b, i) => [b, i]));
1756
+ const width = lows.length;
1757
+ const tree2 = new Array(width + 1).fill(0);
1758
+ let seen = 0;
1759
+ let count = 0;
1760
+ for (const [, end] of met) {
1761
+ const b = slot.get(end);
1762
+ let atMost = 0;
1763
+ for (let i = b + 1; i > 0; i -= i & -i) atMost += tree2[i];
1764
+ count += seen - atMost;
1765
+ for (let i = b + 1; i <= width; i += i & -i) tree2[i]++;
1766
+ seen++;
1767
+ }
1768
+ return count;
1769
+ }
1770
+ function total(rows, byRank, pos, sides) {
1771
+ let sum = 0;
1772
+ for (let r = 0; r + 1 < rows.length; r++) sum += pairCrossings(byRank[r], pos, sides);
1773
+ return sum;
1774
+ }
1775
+ function chainsOf(groups) {
1776
+ const parent = new Map(groups.map((g) => [g.id, g.parent]));
1777
+ const cache = /* @__PURE__ */ new Map();
1778
+ const chain2 = (id, seen) => {
1779
+ const known = cache.get(id);
1780
+ if (known !== void 0) return known;
1781
+ const up = parent.get(id);
1782
+ seen.add(id);
1783
+ const out = up === void 0 || seen.has(up) ? [id] : [...chain2(up, seen), id];
1784
+ cache.set(id, out);
1785
+ return out;
1786
+ };
1787
+ return (group) => group === void 0 ? [] : chain2(group, /* @__PURE__ */ new Set());
1788
+ }
1789
+ var newBlock = (first) => ({ items: [], sub: /* @__PURE__ */ new Map(), sum: 0, count: 0, first });
1790
+ function sortRank(row, key, chainOf) {
1791
+ const root = newBlock(0);
1792
+ const keyOf = /* @__PURE__ */ new Map();
1793
+ const firstOf = /* @__PURE__ */ new Map();
1794
+ row.forEach((n, i) => {
1795
+ const k = key(n, i);
1796
+ let at2 = root;
1797
+ for (const g of chainOf(n.group)) {
1798
+ let next = at2.sub.get(g);
1799
+ if (next === void 0) {
1800
+ next = newBlock(i);
1801
+ at2.sub.set(g, next);
1802
+ at2.items.push(next);
1803
+ }
1804
+ next.sum += k;
1805
+ next.count++;
1806
+ at2 = next;
1807
+ }
1808
+ at2.items.push(n);
1809
+ keyOf.set(n, k);
1810
+ firstOf.set(n, i);
1811
+ });
1812
+ const out = [];
1813
+ const flatten = (block) => {
1814
+ const items = block.items.map((item) => {
1815
+ if ("id" in item) return { item, k: keyOf.get(item), first: firstOf.get(item) };
1816
+ return { item, k: item.sum / item.count, first: item.first };
1817
+ });
1818
+ items.sort((a, b) => a.k - b.k || a.first - b.first);
1819
+ for (const { item } of items) {
1820
+ if ("id" in item) out.push(item);
1821
+ else flatten(item);
1822
+ }
1823
+ };
1824
+ flatten(root);
1825
+ return out;
1826
+ }
1827
+ function landing(near, self, work) {
1828
+ const frac = near.places === void 0 ? near.frac : placeAmong(near.places, self, work.sides[near.side].get(near.id) ?? [], work.pos);
1829
+ return at(work.pos.get(near.id), frac);
1830
+ }
1831
+ function reindex(work, r) {
1832
+ work.rows[r].forEach((n, i) => work.pos.set(n.id, i));
1833
+ }
1834
+ function reorder(work, r, side) {
1835
+ const key = (n, i) => {
1836
+ const near = side.get(n.id);
1837
+ if (near === void 0 || near.length === 0) return i;
1838
+ return near.reduce((s, m) => s + landing(m, n.id, work) - 0.5, 0) / near.length;
1839
+ };
1840
+ work.rows[r] = sortRank(work.rows[r], key, work.chainOf);
1841
+ reindex(work, r);
1842
+ }
1843
+ function between(work, side, u, v) {
1844
+ const nu = side.get(u);
1845
+ const nv = side.get(v);
1846
+ if (nu === void 0 || nv === void 0) return 0;
1847
+ let n = 0;
1848
+ for (const a of nu) for (const b of nv) if (landing(a, u, work) > landing(b, v, work)) n++;
1849
+ return n;
1850
+ }
1851
+ function transpose(work) {
1852
+ const { rows, pos, ups, downs } = work;
1853
+ const cost = (a, b) => between(work, ups, a, b) + between(work, downs, a, b);
1854
+ let improved = true;
1855
+ while (improved) {
1856
+ improved = false;
1857
+ for (const row of rows) {
1858
+ for (let i = 0; i + 1 < row.length; i++) {
1859
+ const u = row[i];
1860
+ const v = row[i + 1];
1861
+ if (u.group !== v.group) continue;
1862
+ const now = cost(u.id, v.id);
1863
+ pos.set(v.id, i);
1864
+ pos.set(u.id, i + 1);
1865
+ if (cost(v.id, u.id) < now) {
1866
+ row[i] = v;
1867
+ row[i + 1] = u;
1868
+ improved = true;
1869
+ } else {
1870
+ pos.set(u.id, i);
1871
+ pos.set(v.id, i + 1);
1872
+ }
1873
+ }
1874
+ }
1875
+ }
1876
+ }
1877
+ function orderRows(rows, hops, opts = {}) {
1878
+ const patience = opts.patience ?? 4;
1879
+ const maxSweeps = opts.maxSweeps ?? 24;
1880
+ const chainOf = chainsOf(opts.groups ?? []);
1881
+ const byRank = hopsByRank(rows, hops);
1882
+ const ups = /* @__PURE__ */ new Map();
1883
+ const downs = /* @__PURE__ */ new Map();
1884
+ for (const pairs of byRank) {
1885
+ for (const p of pairs) {
1886
+ (downs.get(p.from) ?? downs.set(p.from, []).get(p.from)).push({ id: p.to, frac: p.toAt, places: p.toPlaces, side: "up" });
1887
+ (ups.get(p.to) ?? ups.set(p.to, []).get(p.to)).push({ id: p.from, frac: p.fromAt, places: p.fromPlaces, side: "down" });
1888
+ }
1889
+ }
1890
+ const seeded = rows.map((row) => sortRank(row, (_, i) => i, chainOf));
1891
+ const sides = sidesOf(hops);
1892
+ const work = { rows: seeded, pos: positions(seeded), ups, downs, sides, chainOf };
1893
+ let best = seeded.map((row) => [...row]);
1894
+ let fewest = total(work.rows, byRank, work.pos, sides);
1895
+ for (let sweep = 0, stale = 0; sweep < maxSweeps && stale < patience && fewest > 0; sweep++) {
1896
+ if (sweep % 2 === 0) for (let r = 1; r < work.rows.length; r++) reorder(work, r, ups);
1897
+ else for (let r = work.rows.length - 2; r >= 0; r--) reorder(work, r, downs);
1898
+ transpose(work);
1899
+ const n = total(work.rows, byRank, work.pos, sides);
1900
+ if (n < fewest) {
1901
+ fewest = n;
1902
+ best = work.rows.map((row) => [...row]);
1903
+ stale = 0;
1904
+ } else stale++;
1905
+ }
1906
+ return best;
1907
+ }
1908
+
1909
+ // src/layered/level.ts
1910
+ var SIDES = { x: { low: "w", high: "e" }, y: { low: "n", high: "s" } };
1911
+ var spanOf = (b, axis) => {
1912
+ const lo = axis === "x" ? b.x : b.y;
1913
+ return { lo, hi: lo + extent(b, axis) };
1914
+ };
1915
+ var overlaps = (p, q) => p.lo < q.hi && q.lo < p.hi;
1916
+ function levelRoutes(input) {
1917
+ const { graph, boxes, axes } = input;
1918
+ const out = { ports: /* @__PURE__ */ new Map(), waypoints: /* @__PURE__ */ new Map() };
1919
+ const seen = /* @__PURE__ */ new Set();
1920
+ for (const e of graph.edges) {
1921
+ const id = e.origin?.id ?? e.id;
1922
+ if (seen.has(id)) continue;
1923
+ seen.add(id);
1924
+ const from = e.origin?.from ?? e.from;
1925
+ const to = e.origin?.to ?? e.to;
1926
+ const a = boxes.get(from);
1927
+ const b = boxes.get(to);
1928
+ if (from === to || a === void 0 || b === void 0) continue;
1929
+ if (!overlaps(spanOf(a, axes.rank), spanOf(b, axes.rank)) || overlaps(spanOf(a, axes.cross), spanOf(b, axes.cross))) continue;
1930
+ const others = [...boxes].filter(([n]) => n !== from && n !== to).map(([, box]) => box);
1931
+ if (between2(a, b, others, axes)) {
1932
+ const face = FACES[input.direction ?? "down"].out;
1933
+ out.ports.set(id, named(graph, [from, face], [to, face]));
1934
+ out.waypoints.set(id, around(a, b, others, axes, input.rankGap));
1935
+ } else {
1936
+ const sides = SIDES[axes.cross];
1937
+ const ahead = center(a, axes.cross) < center(b, axes.cross);
1938
+ out.ports.set(id, named(graph, [from, ahead ? sides.high : sides.low], [to, ahead ? sides.low : sides.high]));
1939
+ }
1940
+ }
1941
+ return out;
1942
+ }
1943
+ function named(graph, [from, f], [to, t]) {
1944
+ const has = (id, face) => {
1945
+ const node = graph.node(id);
1946
+ return node !== void 0 && hasCompassPort(node, face);
1947
+ };
1948
+ return { ...has(from, f) ? { from: f } : {}, ...has(to, t) ? { to: t } : {} };
1949
+ }
1950
+ function between2(a, b, others, axes) {
1951
+ const [ca, cb] = [spanOf(a, axes.cross), spanOf(b, axes.cross)];
1952
+ const gap = ca.hi <= cb.lo ? { lo: ca.hi, hi: cb.lo } : { lo: cb.hi, hi: ca.lo };
1953
+ const [ma, mb] = [center(a, axes.rank), center(b, axes.rank)];
1954
+ const run = { lo: Math.min(ma, mb), hi: Math.max(ma, mb) };
1955
+ return others.some((o) => {
1956
+ const r = spanOf(o, axes.rank);
1957
+ return overlaps(spanOf(o, axes.cross), gap) && r.lo <= run.hi && r.hi >= run.lo;
1958
+ });
1959
+ }
1960
+ function around(a, b, others, axes, rankGap) {
1961
+ const ahead = (box) => axes.sign === 1 ? spanOf(box, axes.rank).hi : -spanOf(box, axes.rank).lo;
1962
+ const behind = (box) => axes.sign === 1 ? spanOf(box, axes.rank).lo : -spanOf(box, axes.rank).hi;
1963
+ const edge = Math.max(ahead(a), ahead(b));
1964
+ const [ma, mb] = [center(a, axes.cross), center(b, axes.cross)];
1965
+ const run = { lo: Math.min(ma, mb), hi: Math.max(ma, mb) };
1966
+ let standoff = rankGap / 2;
1967
+ for (const o of others) {
1968
+ const room = behind(o) - edge;
1969
+ const c = spanOf(o, axes.cross);
1970
+ if (room > 0 && c.lo <= run.hi && c.hi >= run.lo) standoff = Math.min(standoff, room / 2);
1971
+ }
1972
+ const rank = axes.sign * (edge + standoff);
1973
+ const point = (cross) => axes.cross === "x" ? { x: cross, y: rank } : { x: rank, y: cross };
1974
+ return [point(ma), point(mb)];
1975
+ }
1976
+
1977
+ // src/layered/across.ts
1978
+ var EPS2 = 1e-6;
1979
+ function settleAcross(input) {
1980
+ const { graph, rows, axes } = input;
1981
+ const groups = new Map((graph.groups ?? []).map((g) => [g.id, g]));
1982
+ const rankOf = /* @__PURE__ */ new Map();
1983
+ const sizes = /* @__PURE__ */ new Map();
1984
+ const chains = /* @__PURE__ */ new Map();
1985
+ for (const [r, row] of rows.entries()) {
1986
+ for (const node of row) {
1987
+ rankOf.set(node.id, r);
1988
+ sizes.set(node.id, extent(node.bounds, axes.cross));
1989
+ const chain2 = [];
1990
+ for (let g = node.group === void 0 ? void 0 : groups.get(node.group); g !== void 0; g = g.parent === void 0 ? void 0 : groups.get(g.parent)) chain2.unshift(g);
1991
+ chains.set(node.id, chain2);
1992
+ }
1993
+ }
1994
+ const ctx = { input, groups, rankOf, sizes, chains };
1995
+ const out = new Map(input.preferred);
1996
+ return settleLevel(ctx, [...rankOf.keys()], 0, input.preferred, out) ? out : void 0;
1997
+ }
1998
+ function settleLevel(ctx, ids, depth, pref, out) {
1999
+ const { held, nodeGap: gap } = ctx.input;
2000
+ const { items, byRank } = itemsAt(ctx, ids, depth, pref);
2001
+ for (const item of items) {
2002
+ const pin = item.members.find((id) => held.has(id));
2003
+ if (pin === void 0) continue;
2004
+ if (item.group === void 0) {
2005
+ item.fixed = held.get(pin);
2006
+ continue;
2007
+ }
2008
+ const shift = held.get(pin) - pref.get(pin);
2009
+ const shifted = new Map(pref);
2010
+ for (const id of item.members) shifted.set(id, pref.get(id) + shift);
2011
+ if (!settleLevel(ctx, item.members, depth + 1, shifted, out)) return false;
2012
+ Object.assign(item, spanOf2(ctx, item.group, item.members, out));
2013
+ item.fixed = item.pref;
2014
+ }
2015
+ const xs = items.map((it) => it.fixed ?? it.pref);
2016
+ const order = byRank.map((list) => [...list].sort((p, q) => xs[p] + items[p].width / 2 - (xs[q] + items[q].width / 2) || p - q));
2017
+ squeeze(items, order, xs, gap);
2018
+ for (const list of order) {
2019
+ if (clear(items, list, xs, gap)) continue;
2020
+ if (list.some((i) => items[i].fixed === void 0 && items[i].tall)) return false;
2021
+ jump(items, list, xs, gap);
2022
+ if (!clear(items, list, xs, gap)) return false;
2023
+ }
2024
+ for (const [i, item] of items.entries()) {
2025
+ if (item.fixed !== void 0 && item.group !== void 0) continue;
2026
+ for (const id of item.members) out.set(id, item.fixed !== void 0 ? item.fixed : pref.get(id) + xs[i] - item.pref);
2027
+ }
2028
+ return true;
2029
+ }
2030
+ function squeeze(items, order, xs, gap) {
2031
+ const free = (i) => items[i].fixed === void 0;
2032
+ for (let moved = true, pass = 0; moved && pass <= items.length; pass++) {
2033
+ moved = false;
2034
+ for (const list of order) {
2035
+ let edge = -Infinity;
2036
+ for (const i of list) {
2037
+ if (free(i) && xs[i] < edge - EPS2) [xs[i], moved] = [edge, true];
2038
+ edge = Math.max(edge, xs[i] + items[i].width + gap);
2039
+ }
2040
+ }
2041
+ }
2042
+ for (let moved = true, pass = 0; moved && pass <= items.length; pass++) {
2043
+ moved = false;
2044
+ for (const list of order) {
2045
+ let edge = Infinity;
2046
+ for (const i of [...list].reverse()) {
2047
+ const most = edge - items[i].width - gap;
2048
+ if (free(i) && xs[i] > most + EPS2) [xs[i], moved] = [most, true];
2049
+ edge = Math.min(edge, xs[i]);
2050
+ }
2051
+ }
2052
+ }
2053
+ }
2054
+ function itemsAt(ctx, ids, depth, pref) {
2055
+ const items = [];
2056
+ const byKey = /* @__PURE__ */ new Map();
2057
+ const ranks = [];
2058
+ for (const id of ids) {
2059
+ const group = ctx.chains.get(id)[depth];
2060
+ const key = group?.id ?? `\0node:${id}`;
2061
+ const r = ctx.rankOf.get(id);
2062
+ let i = byKey.get(key);
2063
+ if (i === void 0) {
2064
+ i = items.length;
2065
+ byKey.set(key, i);
2066
+ items.push({ pref: 0, width: 0, members: [], tall: false, ...group !== void 0 ? { group } : {} });
2067
+ ranks.push([r, r]);
2068
+ }
2069
+ items[i].members.push(id);
2070
+ ranks[i] = [Math.min(ranks[i][0], r), Math.max(ranks[i][1], r)];
2071
+ }
2072
+ for (const [i, item] of items.entries()) {
2073
+ Object.assign(item, spanOf2(ctx, item.group, item.members, pref));
2074
+ item.tall = ranks[i][0] !== ranks[i][1];
2075
+ }
2076
+ const byRank = ctx.input.rows.map(() => []);
2077
+ for (const [i, [lo, hi]] of ranks.entries()) for (let r = lo; r <= hi; r++) byRank[r].push(i);
2078
+ return { items, byRank };
2079
+ }
2080
+ function spanOf2(ctx, group, members, at2) {
2081
+ const lo = Math.min(...members.map((id) => at2.get(id)));
2082
+ const hi = Math.max(...members.map((id) => at2.get(id) + ctx.sizes.get(id)));
2083
+ const pad = group === void 0 ? { lo: 0, hi: 0 } : padOf(group, ctx.groups, ctx.input.axes);
2084
+ return { pref: lo - pad.lo, width: hi - lo + pad.lo + pad.hi };
2085
+ }
2086
+ function padOf(group, groups, axes) {
2087
+ const own = crossInset(group.inset, axes);
2088
+ let lo = 0;
2089
+ let hi = 0;
2090
+ for (const child of groups.values()) {
2091
+ if (child.parent !== group.id) continue;
2092
+ const pad = padOf(child, groups, axes);
2093
+ lo = Math.max(lo, pad.lo);
2094
+ hi = Math.max(hi, pad.hi);
2095
+ }
2096
+ return { lo: own.lo + lo, hi: own.hi + hi };
2097
+ }
2098
+ function clear(items, list, xs, gap) {
2099
+ for (const [k, i] of list.entries()) {
2100
+ for (const j of list.slice(k + 1)) {
2101
+ const pin = (k2) => items[k2].fixed !== void 0 && items[k2].group === void 0;
2102
+ if (pin(i) && pin(j)) continue;
2103
+ const apart = xs[j] >= xs[i] + items[i].width + gap - EPS2 || xs[i] >= xs[j] + items[j].width + gap - EPS2;
2104
+ if (!apart) return false;
2105
+ }
2106
+ }
2107
+ return true;
2108
+ }
2109
+ function jump(items, list, xs, gap) {
2110
+ const blocks = list.filter((i) => items[i].fixed !== void 0).map((i) => ({ lo: xs[i] - gap, hi: xs[i] + items[i].width + gap }));
2111
+ const free = list.filter((i) => items[i].fixed === void 0);
2112
+ const along = (dir) => {
2113
+ const out = [];
2114
+ let cursor = dir * -Infinity;
2115
+ for (const i of dir === 1 ? free : [...free].reverse()) {
2116
+ const width = items[i].width;
2117
+ let x = dir === 1 ? Math.max(items[i].pref, cursor) : Math.min(items[i].pref, cursor - width);
2118
+ for (let moved = true; moved; ) {
2119
+ moved = false;
2120
+ for (const b of blocks) {
2121
+ if (x < b.hi - EPS2 && x + width > b.lo + EPS2) [x, moved] = [dir === 1 ? b.hi : b.lo - width, true];
2122
+ }
2123
+ }
2124
+ out[i] = x;
2125
+ cursor = dir === 1 ? x + width + gap : x - gap;
2126
+ }
2127
+ return out;
2128
+ };
2129
+ const cost = (ys) => free.reduce((sum, i) => sum + Math.abs(ys[i] - items[i].pref), 0);
2130
+ const up = along(1);
2131
+ const down = along(-1);
2132
+ const best = cost(up) <= cost(down) ? up : down;
2133
+ for (const i of free) xs[i] = best[i];
2134
+ }
2135
+
2136
+ // src/layered/bands.ts
2137
+ var EPS3 = 1e-6;
2138
+ function placeBands(input, covers) {
2139
+ const { natural, minGaps } = input;
2140
+ const n = natural.length;
2141
+ const hiOf = (k, lo) => Math.max(lo + natural[k], covers.get(k)?.hi ?? -Infinity);
2142
+ const earliest = [];
2143
+ for (let k = 0, lo = -Infinity; k < n; k++) {
2144
+ earliest.push(lo);
2145
+ const cover = covers.get(k);
2146
+ if (cover !== void 0 && lo > cover.lo + EPS3) return void 0;
2147
+ lo = hiOf(k, lo) + (minGaps[k] ?? 0);
2148
+ }
2149
+ const los = preferred(input, covers, hiOf).map((lo, k) => Math.max(lo, earliest[k]));
2150
+ for (let k = n - 1; k >= 0; k--) {
2151
+ if (k < n - 1) los[k] = Math.min(los[k], los[k + 1] - minGaps[k] - natural[k]);
2152
+ const cover = covers.get(k);
2153
+ if (cover !== void 0) los[k] = Math.min(los[k], cover.lo);
2154
+ }
2155
+ return spread(input, covers, los, hiOf);
2156
+ }
2157
+ function preferred(input, covers, hiOf) {
2158
+ const { natural, gaps, home } = input;
2159
+ const cover = (k, lo) => {
2160
+ const c = covers.get(k);
2161
+ return c === void 0 ? lo : Math.min(c.lo, Math.max(lo, c.hi - natural[k]));
2162
+ };
2163
+ const los = [];
2164
+ los[home.rank] = cover(home.rank, home.lo);
2165
+ for (let k = home.rank + 1; k < natural.length; k++) los[k] = cover(k, hiOf(k - 1, los[k - 1]) + gaps[k - 1]);
2166
+ for (let k = home.rank - 1; k >= 0; k--) los[k] = cover(k, los[k + 1] - gaps[k] - natural[k]);
2167
+ return los;
2168
+ }
2169
+ function spread(input, covers, los, hiOf) {
2170
+ const { natural, gaps, minGaps } = input;
2171
+ const n = natural.length;
2172
+ const held = [...covers.keys()].sort((p, q) => p - q);
2173
+ const first = held[0];
2174
+ const last = held[held.length - 1];
2175
+ for (let k = first - 1; k >= 0; k--) los[k] = los[k + 1] - gaps[k] - natural[k];
2176
+ for (let k = last + 1; k < n; k++) los[k] = hiOf(k - 1, los[k - 1]) + gaps[k - 1];
2177
+ for (let h = 1; h < held.length; h++) {
2178
+ const [f, g] = [held[h - 1], held[h]];
2179
+ if (g === f + 1) continue;
2180
+ let room = los[g] - hiOf(f, los[f]);
2181
+ for (let k = f + 1; k < g; k++) room -= natural[k];
2182
+ const ks = Array.from({ length: g - f }, (_, i) => f + i);
2183
+ const least = ks.reduce((sum, k) => sum + minGaps[k], 0);
2184
+ const usual = ks.reduce((sum, k) => sum + gaps[k], 0);
2185
+ const share = (k) => {
2186
+ if (room >= usual) return gaps[k] + (usual > 0 ? (room - usual) * gaps[k] / usual : (room - usual) / ks.length);
2187
+ const slack = usual - least;
2188
+ return minGaps[k] + (slack > EPS3 ? (room - least) * (gaps[k] - minGaps[k]) / slack : (room - least) / ks.length);
2189
+ };
2190
+ for (let k = f + 1; k < g; k++) los[k] = hiOf(k - 1, los[k - 1]) + share(k - 1);
2191
+ }
2192
+ return { los, his: los.map((lo, k) => hiOf(k, lo)) };
2193
+ }
2194
+
2195
+ // src/layered/pins.ts
2196
+ function holdPins(input) {
2197
+ const { graph, rows, axes, pinned } = input;
2198
+ const natural = rows.map((row) => depthOf(row, axes));
2199
+ const rankAt = /* @__PURE__ */ new Map();
2200
+ const unheld = /* @__PURE__ */ new Set();
2201
+ const rowOf = /* @__PURE__ */ new Map();
2202
+ rows.forEach((row, r) => row.forEach((n) => rowOf.set(n.id, r)));
2203
+ const pins = graph.nodes.filter((n) => pinned.has(n.id) && rowOf.has(n.id));
2204
+ if (pins.length < 2) return { crossAt: new Map(input.crossAt), gaps: [...input.gaps], depths: natural, rankAt, unheld };
2205
+ const anchor = pins[0];
2206
+ const a = rowOf.get(anchor.id);
2207
+ let start = 0;
2208
+ for (let r = 0; r < a; r++) start += natural[r] + input.gaps[r];
2209
+ const bandInput = { natural, gaps: input.gaps, minGaps: input.minGaps, home: { rank: a, lo: start } };
2210
+ const anchorExt = extent(anchor.bounds, axes.rank);
2211
+ const anchorRank = start + (natural[a] - anchorExt) / 2;
2212
+ const anchorWorld = worldOf({ cross: input.crossAt.get(anchor.id), rank: anchorRank }, anchorExt, axes);
2213
+ const target = (id) => {
2214
+ const node = graph.node(id);
2215
+ const x = node.bounds.x - (anchor.bounds.x - anchorWorld.x);
2216
+ const y = node.bounds.y - (anchor.bounds.y - anchorWorld.y);
2217
+ const ext = extent(node.bounds, axes.rank);
2218
+ const rankWorld = axes.rank === "x" ? x : y;
2219
+ return { cross: axes.cross === "x" ? x : y, rank: axes.sign === 1 ? rankWorld : -rankWorld - ext };
2220
+ };
2221
+ const rest = pins.slice(1).sort((p, q) => Math.abs(rowOf.get(p.id) - a) - Math.abs(rowOf.get(q.id) - a));
2222
+ const held = /* @__PURE__ */ new Map([[anchor.id, input.crossAt.get(anchor.id)]]);
2223
+ const ranks = /* @__PURE__ */ new Map([[anchor.id, anchorRank]]);
2224
+ let covers = /* @__PURE__ */ new Map([[a, { lo: anchorRank, hi: anchorRank + anchorExt }]]);
2225
+ let placed = placeBands(bandInput, covers);
2226
+ let crossAt = new Map(input.crossAt);
2227
+ for (const pin of rest) {
2228
+ const r = rowOf.get(pin.id);
2229
+ const at2 = target(pin.id);
2230
+ const was = covers.get(r);
2231
+ const hi = at2.rank + extent(pin.bounds, axes.rank);
2232
+ const next = new Map(covers).set(r, { lo: Math.min(was?.lo ?? at2.rank, at2.rank), hi: Math.max(was?.hi ?? hi, hi) });
2233
+ const bands = placeBands(bandInput, next);
2234
+ const settled = bands && settleAcross({ graph, rows, preferred: input.crossAt, held: new Map(held).set(pin.id, at2.cross), axes, nodeGap: input.nodeGap });
2235
+ if (bands === void 0 || settled === void 0) {
2236
+ unheld.add(pin.id);
2237
+ continue;
2238
+ }
2239
+ held.set(pin.id, at2.cross);
2240
+ ranks.set(pin.id, at2.rank);
2241
+ [covers, placed, crossAt] = [next, bands, settled];
2242
+ }
2243
+ const { los, his } = placed;
2244
+ for (const [id, rank] of ranks) rankAt.set(id, rank - los[0]);
2245
+ const gaps = los.slice(1).map((lo, k) => lo - his[k]);
2246
+ return { crossAt, gaps, depths: los.map((lo, k) => his[k] - lo), rankAt, unheld };
2247
+ }
2248
+
2249
+ // src/layered/place.ts
2250
+ var pairKey = (upper, lower) => `${upper}\0${lower}`;
2251
+ function placeAcross(rows, hops, axis, nodeGap) {
2252
+ const frame = { rows: rows.map((row) => row.map((n) => n.id)), size: /* @__PURE__ */ new Map(), gap: nodeGap, ports: /* @__PURE__ */ new Map() };
2253
+ for (const row of rows) for (const node of row) frame.size.set(node.id, extent(node.bounds, axis));
2254
+ for (const hop of hops) {
2255
+ const key = pairKey(hop.from, hop.to);
2256
+ if (!frame.ports.has(key)) frame.ports.set(key, [hop.fromAt ?? 0.5, hop.toAt ?? 0.5]);
2257
+ }
2258
+ const { up, down } = neighborsOf(frame.rows, hops);
2259
+ const conflicts = markConflicts(frame.rows, up);
2260
+ const layouts = [];
2261
+ for (const downward of [true, false]) {
2262
+ for (const right of [false, true]) {
2263
+ layouts.push({ xs: alignedLayout(frame, downward ? up : down, conflicts, downward, right), right });
2264
+ }
2265
+ }
2266
+ const spans = layouts.map(({ xs }) => spanOf3(xs, frame.size));
2267
+ let narrowest = 0;
2268
+ for (let i = 1; i < spans.length; i++) if (spans[i].max - spans[i].min < spans[narrowest].max - spans[narrowest].min) narrowest = i;
2269
+ const target = spans[narrowest];
2270
+ for (const [i, { xs, right }] of layouts.entries()) {
2271
+ const delta = right ? target.max - spans[i].max : target.min - spans[i].min;
2272
+ for (const [id, x] of xs) xs.set(id, x + delta);
2273
+ }
2274
+ const low = /* @__PURE__ */ new Map();
2275
+ let least = Infinity;
2276
+ for (const [id, size] of frame.size) {
2277
+ const four = layouts.map(({ xs }) => xs.get(id)).sort((a, b) => a - b);
2278
+ const corner = (four[1] + four[2]) / 2 - size / 2;
2279
+ low.set(id, corner);
2280
+ least = Math.min(least, corner);
2281
+ }
2282
+ for (const [id, corner] of low) low.set(id, corner - least);
2283
+ return low;
2284
+ }
2285
+ function neighborsOf(rows, hops) {
2286
+ const rank = /* @__PURE__ */ new Map();
2287
+ const pos = /* @__PURE__ */ new Map();
2288
+ const up = /* @__PURE__ */ new Map();
2289
+ const down = /* @__PURE__ */ new Map();
2290
+ for (const [r, row] of rows.entries()) {
2291
+ for (const [i, id] of row.entries()) {
2292
+ rank.set(id, r);
2293
+ pos.set(id, i);
2294
+ up.set(id, []);
2295
+ down.set(id, []);
2296
+ }
2297
+ }
2298
+ for (const hop of hops) {
2299
+ const from = rank.get(hop.from);
2300
+ const to = rank.get(hop.to);
2301
+ if (from === void 0 || to === void 0 || to !== from + 1) {
2302
+ throw new Error(`placeAcross: hop ${hop.from} -> ${hop.to} does not join adjacent ranks`);
2303
+ }
2304
+ const ups = up.get(hop.to);
2305
+ if (!ups.includes(hop.from)) ups.push(hop.from);
2306
+ const downs = down.get(hop.from);
2307
+ if (!downs.includes(hop.to)) downs.push(hop.to);
2308
+ }
2309
+ const byPos = (a, b) => pos.get(a) - pos.get(b);
2310
+ for (const list of up.values()) list.sort(byPos);
2311
+ for (const list of down.values()) list.sort(byPos);
2312
+ return { up, down };
2313
+ }
2314
+ var isMarked = (conflicts, a, b) => conflicts.get(a)?.has(b) ?? false;
2315
+ function mark(conflicts, a, b) {
2316
+ for (const [x, y] of [[a, b], [b, a]]) {
2317
+ let set = conflicts.get(x);
2318
+ if (set === void 0) conflicts.set(x, set = /* @__PURE__ */ new Set());
2319
+ set.add(y);
2320
+ }
2321
+ }
2322
+ function markConflicts(rows, up) {
2323
+ const conflicts = /* @__PURE__ */ new Map();
2324
+ for (let r = 1; r < rows.length; r++) {
2325
+ const upperPos = new Map(rows[r - 1].map((id, i) => [id, i]));
2326
+ const lower = rows[r];
2327
+ let k0 = 0;
2328
+ let scan = 0;
2329
+ for (let l1 = 0; l1 < lower.length; l1++) {
2330
+ const v = lower[l1];
2331
+ const inner = isPlaceholder(v) ? up.get(v).find(isPlaceholder) : void 0;
2332
+ if (inner === void 0 && l1 !== lower.length - 1) continue;
2333
+ const k1 = inner !== void 0 ? upperPos.get(inner) : rows[r - 1].length - 1;
2334
+ for (; scan <= l1; scan++) {
2335
+ const w = lower[scan];
2336
+ for (const u of up.get(w)) {
2337
+ const k = upperPos.get(u);
2338
+ if ((k < k0 || k > k1) && !(isPlaceholder(u) && isPlaceholder(w))) mark(conflicts, u, w);
2339
+ }
2340
+ }
2341
+ k0 = k1;
2342
+ }
2343
+ }
2344
+ return conflicts;
2345
+ }
2346
+ function alignedLayout(frame, nbrs, conflicts, downward, right) {
2347
+ const ranks = downward ? frame.rows : [...frame.rows].reverse();
2348
+ const rows = right ? ranks.map((row) => [...row].reverse()) : ranks;
2349
+ const pos = /* @__PURE__ */ new Map();
2350
+ for (const row of rows) for (const [i, id] of row.entries()) pos.set(id, i);
2351
+ const root = /* @__PURE__ */ new Map();
2352
+ const align = /* @__PURE__ */ new Map();
2353
+ const shift = /* @__PURE__ */ new Map();
2354
+ for (const row of rows) {
2355
+ for (const id of row) {
2356
+ root.set(id, id);
2357
+ align.set(id, id);
2358
+ shift.set(id, 0);
2359
+ }
2360
+ }
2361
+ const offset = (id, other) => {
2362
+ const below = frame.ports.get(pairKey(id, other));
2363
+ const frac = below !== void 0 ? below[0] : frame.ports.get(pairKey(other, id))?.[1] ?? 0.5;
2364
+ return ((right ? 1 - frac : frac) - 0.5) * frame.size.get(id);
2365
+ };
2366
+ for (const row of rows) {
2367
+ let taken = -1;
2368
+ for (const v of row) {
2369
+ const ws = [...nbrs.get(v)].sort((a, b) => pos.get(a) - pos.get(b));
2370
+ if (ws.length === 0) continue;
2371
+ const lo = Math.floor((ws.length - 1) / 2);
2372
+ const hi = Math.ceil((ws.length - 1) / 2);
2373
+ for (const m of lo === hi ? [lo] : [lo, hi]) {
2374
+ if (align.get(v) !== v) break;
2375
+ const u = ws[m];
2376
+ if (taken < pos.get(u) && !isMarked(conflicts, u, v)) {
2377
+ align.set(u, v);
2378
+ root.set(v, root.get(u));
2379
+ align.set(v, root.get(v));
2380
+ shift.set(v, shift.get(u) + offset(u, v) - offset(v, u));
2381
+ taken = pos.get(u);
2382
+ }
2383
+ }
2384
+ }
2385
+ }
2386
+ const xs = compact(rows, root, shift, frame);
2387
+ const out = /* @__PURE__ */ new Map();
2388
+ for (const [id, r] of root) {
2389
+ const x = xs.get(r) + shift.get(id);
2390
+ out.set(id, right ? -x : x);
2391
+ }
2392
+ return out;
2393
+ }
2394
+ function compact(rows, root, shift, frame) {
2395
+ const blocks = [];
2396
+ const preds = /* @__PURE__ */ new Map();
2397
+ const succs = /* @__PURE__ */ new Map();
2398
+ for (const row of rows) {
2399
+ for (const id of row) {
2400
+ const r = root.get(id);
2401
+ if (preds.has(r)) continue;
2402
+ blocks.push(r);
2403
+ preds.set(r, /* @__PURE__ */ new Map());
2404
+ succs.set(r, /* @__PURE__ */ new Map());
2405
+ }
2406
+ }
2407
+ for (const row of rows) {
2408
+ for (let j = 1; j < row.length; j++) {
2409
+ const a = row[j - 1];
2410
+ const b = row[j];
2411
+ const ra = root.get(a);
2412
+ const rb = root.get(b);
2413
+ const sep = (frame.size.get(a) + frame.size.get(b)) / 2 + frame.gap + shift.get(a) - shift.get(b);
2414
+ const prior = preds.get(rb).get(ra) ?? -Infinity;
2415
+ if (sep <= prior) continue;
2416
+ preds.get(rb).set(ra, sep);
2417
+ succs.get(ra).set(rb, sep);
2418
+ }
2419
+ }
2420
+ const waiting = new Map(blocks.map((b) => [b, preds.get(b).size]));
2421
+ const order = [];
2422
+ const ready = blocks.filter((b) => waiting.get(b) === 0);
2423
+ while (ready.length > 0) {
2424
+ const b = ready.shift();
2425
+ order.push(b);
2426
+ for (const s of succs.get(b).keys()) {
2427
+ const left = waiting.get(s) - 1;
2428
+ waiting.set(s, left);
2429
+ if (left === 0) ready.push(s);
2430
+ }
2431
+ }
2432
+ if (order.length !== blocks.length) throw new Error("placeAcross: alignment produced crossing blocks");
2433
+ const xs = /* @__PURE__ */ new Map();
2434
+ for (const b of order) {
2435
+ let x = 0;
2436
+ for (const [p, sep] of preds.get(b)) x = Math.max(x, xs.get(p) + sep);
2437
+ xs.set(b, x);
2438
+ }
2439
+ for (let i = order.length - 1; i >= 0; i--) {
2440
+ const b = order[i];
2441
+ let x = Infinity;
2442
+ for (const [s, sep] of succs.get(b)) x = Math.min(x, xs.get(s) - sep);
2443
+ if (x !== Infinity && x > xs.get(b)) xs.set(b, x);
2444
+ }
2445
+ return xs;
2446
+ }
2447
+ function spanOf3(xs, size) {
2448
+ let min = Infinity;
2449
+ let max = -Infinity;
2450
+ for (const [id, x] of xs) {
2451
+ min = Math.min(min, x - size.get(id) / 2);
2452
+ max = Math.max(max, x + size.get(id) / 2);
2453
+ }
2454
+ return { min, max };
2455
+ }
2456
+
2457
+ // src/layered/plan.ts
2458
+ var LABEL_GAP = 0.75;
2459
+ var MIN_GAP = 0.25;
2460
+ function layeredPlan(graph, opts = {}) {
2461
+ if ((graph.groups?.length ?? 0) === 0 || opts.order === "barycenter") return planOnce(graph, opts);
2462
+ let last;
2463
+ const moves = untilSettled((g, o) => {
2464
+ last = planOnce(last === void 0 ? g : { ...g, edges: g.edges.map((e) => routed(e, last)) }, o);
2465
+ return last.moves;
2466
+ }, graph, opts);
2467
+ return { ...last, moves };
2468
+ }
2469
+ function routed(edge, plan) {
2470
+ const { waypoints: _, ...rest } = edge;
2471
+ const route = plan.waypoints.get(edge.origin?.id ?? edge.id);
2472
+ return route === void 0 ? rest : { ...rest, waypoints: route };
2473
+ }
2474
+ function planOnce(graph, opts = {}) {
2475
+ const axes = axesFor(opts.direction);
2476
+ const nodeGap = opts.nodeGap ?? DEFAULT_NODE_GAP;
2477
+ const rankGap = opts.rankGap ?? DEFAULT_RANK_GAP;
2478
+ const finding = opts.order === "barycenter";
2479
+ const back = backEdges(graph);
2480
+ const ranks = ranksByLength(graph, back);
2481
+ const expanded = expand(graph, ranks, back, { axes, nodeGap });
2482
+ const rows = finding ? orderRows(expanded.rows, expanded.hops, { groups: graph.groups ?? [] }) : expanded.rows;
2483
+ const preferred2 = placeAcross(rows, settlePlaces(rows, expanded.hops), axes.cross, nodeGap);
2484
+ const grouped = (graph.groups?.length ?? 0) > 0;
2485
+ const packed = grouped ? packClusters(graph, rows, preferred2, axes, nodeGap) : preferred2;
2486
+ const spaced = grouped ? rankGaps(graph, rows, axes, rankGap) : rows.slice(1).map(() => rankGap);
2487
+ const minGaps = spaced.map((gap) => gap - rankGap + rankGap * MIN_GAP);
2488
+ for (const r of expanded.labelRows) {
2489
+ spaced[r - 1] = spaced[r - 1] - rankGap * (1 - LABEL_GAP);
2490
+ spaced[r] = spaced[r] - rankGap * (1 - LABEL_GAP);
2491
+ }
2492
+ const pinned = pinnedSet(graph, opts);
2493
+ const { crossAt, gaps, depths, rankAt, unheld } = holdPins({ graph, rows, crossAt: packed, gaps: spaced, pinned, axes, nodeGap, minGaps });
2494
+ const slots = /* @__PURE__ */ new Map();
2495
+ const bands = [];
2496
+ let along = 0;
2497
+ for (const [i, row] of rows.entries()) {
2498
+ const depth = depths[i];
2499
+ bands.push([along, along + depth]);
2500
+ for (const node of row) {
2501
+ slots.set(node.id, { cross: crossAt.get(node.id), rank: rankAt.get(node.id) ?? along + (depth - extent(node.bounds, axes.rank)) / 2 });
2502
+ }
2503
+ along += depth + (gaps[i] ?? 0);
2504
+ }
2505
+ const placed = /* @__PURE__ */ new Map();
2506
+ for (const node of graph.nodes) {
2507
+ const slot = slots.get(node.id);
2508
+ if (slot !== void 0) placed.set(node.id, worldOf(slot, extent(node.bounds, axes.rank), axes));
2509
+ }
2510
+ const moves = translated(graph, placed, pinned, opts.tolerance);
2511
+ const offset = anchorOffset(graph, placed, pinned);
2512
+ const displaced = /* @__PURE__ */ new Set();
2513
+ for (const id of pinned) {
2514
+ const at2 = placed.get(id);
2515
+ const node = graph.node(id);
2516
+ if (at2 === void 0 || node === void 0) continue;
2517
+ if (Math.abs(at2.x + offset.x - node.bounds.x) > 0.5 || Math.abs(at2.y + offset.y - node.bounds.y) > 0.5) displaced.add(id);
2518
+ }
2519
+ const waypoints = /* @__PURE__ */ new Map();
2520
+ const labels = /* @__PURE__ */ new Map();
2521
+ const rankOf = /* @__PURE__ */ new Map();
2522
+ rows.forEach((row, r) => row.forEach((n) => rankOf.set(n.id, r)));
2523
+ const widthOf = new Map(rows.flat().map((n) => [n.id, extent(n.bounds, axes.cross)]));
2524
+ for (const [edge, chain2] of expanded.chains) {
2525
+ if (chain2.ends.some((id) => displaced.has(id))) continue;
2526
+ const beside = chain2.ids.indexOf(expanded.labels.get(edge) ?? "");
2527
+ const points = [];
2528
+ for (const id of chain2.ids) {
2529
+ const cross = crossAt.get(id) + (expanded.wires.get(id) ?? widthOf.get(id) / 2);
2530
+ const [lo, hi] = bands[rankOf.get(id)];
2531
+ for (const rank of chain2.back ? [hi, lo] : [lo, hi]) {
2532
+ const at2 = worldOf({ cross, rank }, 0, axes);
2533
+ points.push({ x: at2.x + offset.x, y: at2.y + offset.y });
2534
+ }
2535
+ }
2536
+ waypoints.set(edge, points);
2537
+ if (beside >= 0) {
2538
+ const [a, b] = [points[beside * 2], points[beside * 2 + 1]];
2539
+ labels.set(edge, { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 });
2540
+ }
2541
+ }
2542
+ const ports = layeredPorts(graph, ranks, opts.direction);
2543
+ const boxes = new Map(graph.nodes.map((n) => [n.id, { ...n.bounds, ...moves.get(n.id) }]));
2544
+ const level = levelRoutes({ graph, boxes, axes, rankGap, ...opts.direction !== void 0 ? { direction: opts.direction } : {} });
2545
+ for (const [edge, named2] of level.ports) {
2546
+ ports.set(edge, named2);
2547
+ waypoints.delete(edge);
2548
+ labels.delete(edge);
2549
+ }
2550
+ for (const [edge, points] of level.waypoints) waypoints.set(edge, points);
2551
+ return { moves, ranks, back, waypoints, labels, ports, unheld };
2552
+ }
2553
+
2554
+ // src/layered.ts
2555
+ var layered = Object.assign(
2556
+ (graph, opts = {}) => layeredPlan(graph, opts).moves,
2557
+ { plan: layeredPlan }
2558
+ );
2559
+
2560
+ // src/chainRoutes.ts
2561
+ var portOf3 = (node, id) => id === void 0 ? void 0 : node.ports?.find((p) => p.id === id);
2562
+ function coord(node, at2, id, axis) {
2563
+ const spec = portOf3(node, id);
2564
+ if (spec === void 0) return void 0;
2565
+ const anchor = axis === "x" ? spec.at.u : spec.at.v;
2566
+ return { along: (axis === "x" ? at2.x : at2.y) + anchor * (axis === "x" ? node.bounds.width : node.bounds.height), anchor };
2567
+ }
2568
+ function sidesOf2(graph, hanging, moves, axes, before) {
2569
+ const at2 = (node) => moves.get(node.id) ?? node.bounds;
2570
+ const byLine = /* @__PURE__ */ new Map();
2571
+ for (const edge of graph.edges) {
2572
+ const lineFirst = hanging.level.get(edge.from) === 0;
2573
+ const [lineId, hungId] = lineFirst ? [edge.from, edge.to] : [edge.to, edge.from];
2574
+ if (hanging.level.get(lineId) !== 0 || hanging.level.get(hungId) !== 1 || hanging.owner.get(hungId) !== lineId) continue;
2575
+ const line = graph.node(lineId);
2576
+ const hung = graph.node(hungId);
2577
+ const [linePort, hungPort] = lineFirst ? [edge.fromPort, edge.toPort] : [edge.toPort, edge.fromPort];
2578
+ const lineCross = coord(line, at2(line), linePort, axes.cross);
2579
+ const hungCross = coord(hung, at2(hung), hungPort, axes.cross);
2580
+ if (lineCross?.anchor !== (before ? 0 : 1) || hungCross?.anchor !== (before ? 1 : 0)) continue;
2581
+ const side = {
2582
+ edge,
2583
+ lineFirst,
2584
+ port: coord(line, at2(line), linePort, axes.rank).along,
2585
+ hung: coord(hung, at2(hung), hungPort, axes.rank).along,
2586
+ lineEdge: lineCross.along,
2587
+ hungEdge: hungCross.along
2588
+ };
2589
+ const list = byLine.get(lineId);
2590
+ if (list === void 0) byLine.set(lineId, [side]);
2591
+ else list.push(side);
2592
+ }
2593
+ return byLine;
2594
+ }
2595
+ var SIDE_TRACK_GAP = 12;
2596
+ function turning(sides) {
2597
+ const sideways = sides.filter((s) => Math.abs(s.hung - s.port) > 0.5);
2598
+ return [
2599
+ sideways.filter((s) => s.hung < s.port).sort((a, b) => a.port - b.port),
2600
+ sideways.filter((s) => s.hung > s.port).sort((a, b) => b.port - a.port)
2601
+ ];
2602
+ }
2603
+ function tracksNeeded(graph, hanging, moves, axes, before) {
2604
+ let most = 0;
2605
+ for (const sides of sidesOf2(graph, hanging, moves, axes, before).values()) {
2606
+ const [low, high] = turning(sides);
2607
+ most = Math.max(most, low.length, high.length);
2608
+ }
2609
+ return most;
2610
+ }
2611
+ function sideRoutes(graph, hanging, moves, axes, before) {
2612
+ const out = /* @__PURE__ */ new Map();
2613
+ for (const sides of sidesOf2(graph, hanging, moves, axes, before).values()) {
2614
+ const [low, high] = turning(sides);
2615
+ const lineEdge = sides[0].lineEdge;
2616
+ const hungEdge = before ? Math.max(...sides.map((s) => s.hungEdge)) : Math.min(...sides.map((s) => s.hungEdge));
2617
+ const step = (hungEdge - lineEdge) / (Math.max(low.length, high.length) + 1);
2618
+ for (const run of [low, high]) {
2619
+ run.forEach((s, k) => {
2620
+ const track = lineEdge + step * (k + 1);
2621
+ const dir = Math.sign(s.hung - s.port);
2622
+ const half = Math.abs(s.hung - s.port) / 2;
2623
+ const turns = [
2624
+ { rank: s.port + dir * Math.min(Math.abs(track - s.lineEdge), half), cross: track },
2625
+ { rank: s.hung - dir * Math.min(Math.abs(s.hungEdge - track), half), cross: track }
2626
+ ];
2627
+ const points = turns.map((t) => axes.rank === "x" ? { x: t.rank, y: t.cross } : { x: t.cross, y: t.rank });
2628
+ out.set(s.edge.id, s.lineFirst ? points : points.reverse());
2629
+ });
2630
+ }
2631
+ }
2632
+ return out;
2633
+ }
2634
+ function inPortOrder(graph, lineId, row, axes) {
2635
+ const line = graph.node(lineId);
2636
+ const keyOf = (node) => {
2637
+ const anchors = [
2638
+ ...graph.outgoing(node.id).filter((e) => e.to === lineId).map((e) => e.toPort),
2639
+ ...graph.incoming(node.id).filter((e) => e.from === lineId).map((e) => e.fromPort)
2640
+ ].flatMap((id) => {
2641
+ const spec = portOf3(line, id);
2642
+ return spec === void 0 ? [] : [axes.rank === "x" ? spec.at.u : spec.at.v];
2643
+ });
2644
+ return anchors.length === 0 ? void 0 : axes.sign * anchors.reduce((a, b) => a + b, 0) / anchors.length;
2645
+ };
2646
+ const keyed = row.map((node) => ({ node, key: keyOf(node) }));
2647
+ const slots = keyed.flatMap((k, i) => k.key === void 0 ? [] : [i]);
2648
+ const sorted = keyed.filter((k) => k.key !== void 0).sort((a, b) => a.key - b.key);
2649
+ const out = [...row];
2650
+ slots.forEach((i, j) => {
2651
+ out[i] = sorted[j].node;
2652
+ });
2653
+ return out;
2654
+ }
2655
+
2656
+ // src/chain.ts
2657
+ function lineOf(graph, ids) {
2658
+ const seen = /* @__PURE__ */ new Set();
2659
+ const given = (ids ?? []).flatMap((id) => {
2660
+ const node = graph.node(id);
2661
+ if (node === void 0 || seen.has(id)) return [];
2662
+ seen.add(id);
2663
+ return [node];
2664
+ });
2665
+ if (given.length > 0) return given;
2666
+ const back = backEdges(graph);
2667
+ const ranks = ranksOf(graph, back);
2668
+ let end;
2669
+ for (const node of graph.nodes) {
2670
+ if (end === void 0 || ranks.get(node.id) > ranks.get(end.id)) end = node;
2671
+ }
2672
+ const line = [];
2673
+ for (let at2 = end; at2 !== void 0; ) {
2674
+ line.unshift(at2);
2675
+ const rank = ranks.get(at2.id);
2676
+ const edge = graph.incoming(at2.id).find((e) => !back.has(e.id) && ranks.get(e.from) === rank - 1);
2677
+ at2 = edge === void 0 ? void 0 : graph.node(edge.from);
2678
+ }
2679
+ return line;
2680
+ }
2681
+ function hangFrom(graph, line) {
2682
+ const owner = new Map(line.map((n) => [n.id, n.id]));
2683
+ const level = new Map(line.map((n) => [n.id, 0]));
2684
+ for (let frontier = line.map((n) => n.id); frontier.length > 0; ) {
2685
+ const next = [];
2686
+ for (const id of frontier) {
2687
+ const near = [...graph.incoming(id).map((e) => e.from), ...graph.outgoing(id).map((e) => e.to)];
2688
+ for (const other of near) {
2689
+ if (owner.has(other) || graph.node(other) === void 0) continue;
2690
+ owner.set(other, owner.get(id));
2691
+ level.set(other, level.get(id) + 1);
2692
+ next.push(other);
2693
+ }
2694
+ }
2695
+ frontier = next;
2696
+ }
2697
+ const last = line[line.length - 1].id;
2698
+ for (const node of graph.nodes) {
2699
+ if (owner.has(node.id)) continue;
2700
+ owner.set(node.id, last);
2701
+ level.set(node.id, 1);
2702
+ }
2703
+ return { owner, level };
2704
+ }
2705
+ var chain = Object.assign(
2706
+ (graph, opts = {}) => chainPlan(graph, opts).moves,
2707
+ { plan: chainPlan }
2708
+ );
2709
+ function chainPlan(graph, opts = {}) {
2710
+ const axes = axesFor(opts.direction);
2711
+ const nodeGap = opts.nodeGap ?? DEFAULT_NODE_GAP;
2712
+ const rankGap = opts.rankGap ?? DEFAULT_RANK_GAP;
2713
+ const line = lineOf(graph, opts.chain);
2714
+ if (line.length === 0) return { moves: /* @__PURE__ */ new Map() };
2715
+ const hanging = hangFrom(graph, line);
2716
+ const { owner, level } = hanging;
2717
+ const rows = new Map(line.map((n) => [n.id, []]));
2718
+ const levelSize = [Math.max(...line.map((n) => extent(n.bounds, axes.cross)))];
2719
+ for (const node of graph.nodes) {
2720
+ const at2 = level.get(node.id);
2721
+ if (at2 === 0) continue;
2722
+ const column = rows.get(owner.get(node.id));
2723
+ (column[at2] ??= []).push(node);
2724
+ levelSize[at2] = Math.max(levelSize[at2] ?? 0, extent(node.bounds, axes.cross));
2725
+ }
2726
+ for (const [id, column] of rows) if (column[1]) column[1] = inPortOrder(graph, id, column[1], axes);
2727
+ const before = opts.chainSide === "before";
2728
+ const place = (side2) => settle(graph, slotsFor(line, rows, levelSize, axes, { node: nodeGap, rank: rankGap, side: side2 }, before), opts);
2729
+ let moves = place(nodeGap);
2730
+ const side = (tracksNeeded(graph, hanging, moves, axes, before) + 1) * SIDE_TRACK_GAP;
2731
+ if (side > nodeGap) moves = place(side);
2732
+ return { moves, waypoints: sideRoutes(graph, hanging, moves, axes, before) };
2733
+ }
2734
+ function slotsFor(line, rows, levelSize, axes, gaps, before) {
2735
+ const { node: nodeGap, rank: rankGap } = gaps;
2736
+ const rowSpan = (row) => row.reduce((sum, n) => sum + extent(n.bounds, axes.rank), 0) + nodeGap * Math.max(row.length - 1, 0);
2737
+ const bandStart = (at2) => {
2738
+ let offset = 0;
2739
+ const first = before ? 1 : 0;
2740
+ for (let k = first; k < (before ? at2 + 1 : at2); k++) offset += (levelSize[k] ?? 0) + (k === first ? gaps.side : nodeGap);
2741
+ return before ? -offset : offset;
2742
+ };
2743
+ const slots = /* @__PURE__ */ new Map();
2744
+ let cursor = 0;
2745
+ for (const node of line) {
2746
+ const column = rows.get(node.id);
2747
+ const span = Math.max(extent(node.bounds, axes.rank), ...Array.from(column, (row) => row ? rowSpan(row) : 0));
2748
+ slots.set(node.id, {
2749
+ cross: (levelSize[0] - extent(node.bounds, axes.cross)) / 2,
2750
+ rank: cursor + (span - extent(node.bounds, axes.rank)) / 2
2751
+ });
2752
+ column.forEach((row, at2) => {
2753
+ if (!row) return;
2754
+ let along = cursor + (span - rowSpan(row)) / 2;
2755
+ for (const hung of row) {
2756
+ const cross = before ? bandStart(at2) + levelSize[at2] - extent(hung.bounds, axes.cross) : bandStart(at2);
2757
+ slots.set(hung.id, { cross, rank: along });
2758
+ along += extent(hung.bounds, axes.rank) + nodeGap;
2759
+ }
2760
+ });
2761
+ cursor += span + rankGap;
2762
+ }
2763
+ return slots;
2764
+ }
2765
+
2766
+ // src/tree.ts
2767
+ function forestOf(graph, crossAxis) {
2768
+ const order = graphOrder(graph);
2769
+ const children = /* @__PURE__ */ new Map();
2770
+ const depth = /* @__PURE__ */ new Map();
2771
+ const roots = [];
2772
+ const claim = (node, at2) => {
2773
+ depth.set(node.id, at2);
2774
+ const kids = [];
2775
+ for (const edge of graph.outgoing(node.id)) {
2776
+ if (edge.to === node.id || depth.has(edge.to)) continue;
2777
+ const child = graph.node(edge.to);
2778
+ if (child === void 0) continue;
2779
+ depth.set(child.id, at2 + 1);
2780
+ kids.push(child);
2781
+ }
2782
+ const ordered = seededOrder(kids, crossAxis, order);
2783
+ children.set(node.id, ordered);
2784
+ for (const child of ordered) claim(child, at2 + 1);
2785
+ };
2786
+ const rootish = graph.nodes.filter((node) => graph.incoming(node.id).every((edge) => edge.from === node.id));
2787
+ for (const node of seededOrder(rootish, crossAxis, order)) {
2788
+ if (depth.has(node.id)) continue;
2789
+ roots.push(node);
2790
+ claim(node, 0);
2791
+ }
2792
+ for (const node of graph.nodes) {
2793
+ if (depth.has(node.id)) continue;
2794
+ roots.push(node);
2795
+ claim(node, 0);
2796
+ }
2797
+ return { roots, children, depth };
2798
+ }
2799
+ var tree = (graph, opts = {}) => (graph.groups?.length ?? 0) > 0 ? untilSettled(treeOnce, graph, opts) : treeOnce(graph, opts);
2800
+ var treeOnce = (graph, opts = {}) => {
2801
+ const axes = axesFor(opts.direction);
2802
+ const nodeGap = opts.nodeGap ?? DEFAULT_NODE_GAP;
2803
+ const rankGap = opts.rankGap ?? DEFAULT_RANK_GAP;
2804
+ const { roots, children, depth } = forestOf(graph, axes.cross);
2805
+ const bands = /* @__PURE__ */ new Map();
2806
+ for (const node of graph.nodes) {
2807
+ const at2 = depth.get(node.id) ?? 0;
2808
+ bands.set(at2, Math.max(bands.get(at2) ?? 0, extent(node.bounds, axes.rank)));
2809
+ }
2810
+ const depths = [...bands.keys()].sort((a, b) => a - b);
2811
+ const rowsAt = depths.map((at2) => graph.nodes.filter((n) => (depth.get(n.id) ?? 0) === at2));
2812
+ const grouped = (graph.groups?.length ?? 0) > 0;
2813
+ const gaps = grouped ? rankGaps(graph, rowsAt, axes, rankGap) : depths.slice(1).map(() => rankGap);
2814
+ const bandStart = /* @__PURE__ */ new Map();
2815
+ let along = 0;
2816
+ for (const [i, at2] of depths.entries()) {
2817
+ bandStart.set(at2, along);
2818
+ along += bands.get(at2) + (gaps[i] ?? 0);
2819
+ }
2820
+ const slots = /* @__PURE__ */ new Map();
2821
+ const measured = /* @__PURE__ */ new Map();
2822
+ const measure = (node) => {
2823
+ const known = measured.get(node.id);
2824
+ if (known !== void 0) return known;
2825
+ const kids = children.get(node.id) ?? [];
2826
+ let width = 0;
2827
+ for (const child of kids) width += measure(child);
2828
+ if (kids.length > 0) width += nodeGap * (kids.length - 1);
2829
+ const block = Math.max(extent(node.bounds, axes.cross), width);
2830
+ measured.set(node.id, block);
2831
+ return block;
2832
+ };
2833
+ const place = (node, from) => {
2834
+ const kids = children.get(node.id) ?? [];
2835
+ const block = measure(node);
2836
+ const width = kids.reduce((sum, k) => sum + measure(k), 0) + Math.max(0, kids.length - 1) * nodeGap;
2837
+ let cursor2 = from + (block - width) / 2;
2838
+ for (const child of kids) {
2839
+ place(child, cursor2);
2840
+ cursor2 += measure(child) + nodeGap;
2841
+ }
2842
+ const own = extent(node.bounds, axes.cross);
2843
+ const at2 = depth.get(node.id) ?? 0;
2844
+ slots.set(node.id, {
2845
+ cross: from + (block - own) / 2,
2846
+ rank: bandStart.get(at2) + (bands.get(at2) - extent(node.bounds, axes.rank)) / 2
2847
+ });
2848
+ };
2849
+ let cursor = 0;
2850
+ for (const root of roots) {
2851
+ place(root, cursor);
2852
+ cursor += measure(root) + nodeGap;
2853
+ }
2854
+ if (grouped) {
2855
+ const preferred2 = new Map([...slots].map(([id, slot]) => [id, slot.cross]));
2856
+ const ordered = rowsAt.map((row) => [...row].sort((a, b) => preferred2.get(a.id) - preferred2.get(b.id)));
2857
+ const crossAt = packClusters(graph, ordered, preferred2, axes, nodeGap);
2858
+ for (const [id, slot] of slots) slots.set(id, { ...slot, cross: crossAt.get(id) ?? slot.cross });
2859
+ }
2860
+ return settle(graph, slots, opts);
2861
+ };
2862
+
2863
+ export { COMPASS, DEFAULT_GROUP_INSET, DEFAULT_NODE_GAP, DEFAULT_PORTS, DEFAULT_RANK_GAP, DIAGRAM_TRAIT_KEY, PORT_LABEL_GAP, SIDE_TRACK_GAP, anchorOffset, authoredOver, axesFor, backEdges, bodyOutline, bodyTrait, boxForContent, buildBody, canvasMeasure, center, chain, contentBox, createDiagramNodes, dataKeyReader, diagramNodeOf, expandGroupEdges, extent, footprintNode, force, forceRelaxation, forestOf, graphOrder, grownBy, insetOf, isPinned, isPromise, layered, layeredPlan, layeredRoutes, layoutBody, layoutRowPorts, marginOf, measureBody, outlinePath, outlinePolyline, packAcross, packClusters, pinnedSet, planOf, plannerPlan, portLabelBox, portLabelsOf, portOf, portSitesOf, portsOf, portsOnOutline, rankGaps, ranksByLength, ranksOf, rayHit, seededOrder, settle, sizeToBody, translated, tree, untilSettled };
2864
+ //# sourceMappingURL=chunk-NUMMW5BI.js.map
2865
+ //# sourceMappingURL=chunk-NUMMW5BI.js.map