@diagrammo/dgmo 0.27.0 → 0.28.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.
package/dist/auto.mjs CHANGED
@@ -27543,12 +27543,1344 @@ var init_renderer6 = __esm({
27543
27543
  }
27544
27544
  });
27545
27545
 
27546
+ // src/boxes-and-lines/layout-layered.ts
27547
+ function rng(s) {
27548
+ return () => {
27549
+ s |= 0;
27550
+ s = s + 1831565813 | 0;
27551
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
27552
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
27553
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
27554
+ };
27555
+ }
27556
+ function layeredCandidates(parsed, sizes, opts) {
27557
+ if (parsed.groups.length > 0) return [];
27558
+ const nCount = parsed.nodes.length;
27559
+ if (nCount < 3 || nCount > 40) return [];
27560
+ const isTB = parsed.direction === "TB";
27561
+ const nodesep = opts?.nodesep ?? NODESEP;
27562
+ const ranksep = opts?.ranksep ?? RANKSEP;
27563
+ const backEdgeSide = opts?.backEdgeSide ?? "nearest";
27564
+ const restarts = opts?.restarts ?? (nCount <= 14 ? 28 : nCount <= 25 ? 14 : 6);
27565
+ const keepK = opts?.keepK ?? 3;
27566
+ const labels = parsed.nodes.map((n) => n.label);
27567
+ const labelSet = new Set(labels);
27568
+ const edges = parsed.edges.map((e, i) => ({ e, i })).filter(
27569
+ ({ e }) => labelSet.has(e.source) && labelSet.has(e.target) && e.source !== e.target
27570
+ );
27571
+ if (edges.length === 0) return [];
27572
+ const adj = /* @__PURE__ */ new Map();
27573
+ for (const l of labels) adj.set(l, []);
27574
+ for (const { e, i } of edges)
27575
+ adj.get(e.source).push({ to: e.target, idx: i });
27576
+ const reversed = /* @__PURE__ */ new Set();
27577
+ const state = /* @__PURE__ */ new Map();
27578
+ for (const l of labels) state.set(l, 0);
27579
+ const dfs = (u) => {
27580
+ state.set(u, 1);
27581
+ for (const { to, idx } of adj.get(u)) {
27582
+ const st = state.get(to);
27583
+ if (st === 1)
27584
+ reversed.add(idx);
27585
+ else if (st === 0) dfs(to);
27586
+ }
27587
+ state.set(u, 2);
27588
+ };
27589
+ for (const l of labels) if (state.get(l) === 0) dfs(l);
27590
+ const dag = edges.map(
27591
+ ({ e, i }) => reversed.has(i) ? { from: e.target, to: e.source, idx: i, rev: true } : { from: e.source, to: e.target, idx: i, rev: false }
27592
+ );
27593
+ const outDag = /* @__PURE__ */ new Map();
27594
+ const inDeg = /* @__PURE__ */ new Map();
27595
+ for (const l of labels) {
27596
+ outDag.set(l, []);
27597
+ inDeg.set(l, 0);
27598
+ }
27599
+ for (const d of dag) {
27600
+ outDag.get(d.from).push(d.to);
27601
+ inDeg.set(d.to, (inDeg.get(d.to) ?? 0) + 1);
27602
+ }
27603
+ const rank = /* @__PURE__ */ new Map();
27604
+ for (const l of labels) rank.set(l, 0);
27605
+ const queue = labels.filter((l) => (inDeg.get(l) ?? 0) === 0);
27606
+ const indeg2 = new Map(inDeg);
27607
+ const topo = [];
27608
+ while (queue.length) {
27609
+ const u = queue.shift();
27610
+ topo.push(u);
27611
+ for (const v of outDag.get(u)) {
27612
+ rank.set(v, Math.max(rank.get(v), rank.get(u) + 1));
27613
+ indeg2.set(v, indeg2.get(v) - 1);
27614
+ if (indeg2.get(v) === 0) queue.push(v);
27615
+ }
27616
+ }
27617
+ if (topo.length !== labels.length) return [];
27618
+ const maxRank = Math.max(...labels.map((l) => rank.get(l)));
27619
+ const node = /* @__PURE__ */ new Map();
27620
+ for (const n of parsed.nodes) {
27621
+ const s = sizes.get(n.label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
27622
+ node.set(n.label, {
27623
+ id: n.label,
27624
+ dummy: false,
27625
+ rank: rank.get(n.label),
27626
+ cross: 0,
27627
+ thick: isTB ? s.width : s.height,
27628
+ depth: isTB ? s.height : s.width,
27629
+ realW: s.width,
27630
+ realH: s.height
27631
+ });
27632
+ }
27633
+ const segs = [];
27634
+ const chains = [];
27635
+ const backEdges = [];
27636
+ for (const d of dag) {
27637
+ if (d.rev) {
27638
+ backEdges.push({ edgeIdx: d.idx, src: d.to, tgt: d.from });
27639
+ continue;
27640
+ }
27641
+ const r0 = rank.get(d.from);
27642
+ const r1 = rank.get(d.to);
27643
+ const dagChain = [d.from];
27644
+ let prev = d.from;
27645
+ for (let r = r0 + 1; r < r1; r++) {
27646
+ const id = `__d${d.idx}_${r}`;
27647
+ node.set(id, {
27648
+ id,
27649
+ dummy: true,
27650
+ rank: r,
27651
+ cross: 0,
27652
+ thick: DUMMY_THICK,
27653
+ depth: 0,
27654
+ realW: 0,
27655
+ realH: 0
27656
+ });
27657
+ segs.push({ a: prev, b: id });
27658
+ dagChain.push(id);
27659
+ prev = id;
27660
+ }
27661
+ segs.push({ a: prev, b: d.to });
27662
+ dagChain.push(d.to);
27663
+ chains.push({
27664
+ edgeIdx: d.idx,
27665
+ chain: d.rev ? dagChain.slice().reverse() : dagChain
27666
+ });
27667
+ }
27668
+ const ranks = Array.from({ length: maxRank + 1 }, () => []);
27669
+ for (const n of node.values()) ranks[n.rank].push(n.id);
27670
+ const up = /* @__PURE__ */ new Map();
27671
+ const down = /* @__PURE__ */ new Map();
27672
+ for (const id of node.keys()) {
27673
+ up.set(id, []);
27674
+ down.set(id, []);
27675
+ }
27676
+ for (const s of segs) {
27677
+ down.get(s.a).push(s.b);
27678
+ up.get(s.b).push(s.a);
27679
+ }
27680
+ const orderOf = /* @__PURE__ */ new Map();
27681
+ const applyOrder = (ord) => {
27682
+ for (const id of node.keys()) orderOf.set(id, ord.get(id));
27683
+ };
27684
+ const gapCrossings = (r) => {
27685
+ const lower = ranks[r + 1];
27686
+ if (!lower) return 0;
27687
+ const es = [];
27688
+ for (const a of ranks[r]) {
27689
+ const pa = orderOf.get(a);
27690
+ for (const b of down.get(a)) es.push({ p: pa, q: orderOf.get(b) });
27691
+ }
27692
+ let c = 0;
27693
+ for (let i = 0; i < es.length; i++)
27694
+ for (let j = i + 1; j < es.length; j++) {
27695
+ const A = es[i], B = es[j];
27696
+ if ((A.p - B.p) * (A.q - B.q) < 0) c++;
27697
+ }
27698
+ return c;
27699
+ };
27700
+ const totalCrossings = () => {
27701
+ let c = 0;
27702
+ for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
27703
+ return c;
27704
+ };
27705
+ const median = (vals) => {
27706
+ if (vals.length === 0) return -1;
27707
+ vals.sort((x, y) => x - y);
27708
+ const m = Math.floor(vals.length / 2);
27709
+ if (vals.length % 2 === 1) return vals[m];
27710
+ if (vals.length === 2) return (vals[0] + vals[1]) / 2;
27711
+ const left = vals[m - 1] - vals[0];
27712
+ const right = vals[vals.length - 1] - vals[m];
27713
+ return left + right === 0 ? (vals[m - 1] + vals[m]) / 2 : (vals[m - 1] * right + vals[m] * left) / (left + right);
27714
+ };
27715
+ const wmedianRank = (r, useUp) => {
27716
+ const ids = ranks[r];
27717
+ const neigh = useUp ? up : down;
27718
+ const med = /* @__PURE__ */ new Map();
27719
+ for (const id of ids)
27720
+ med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
27721
+ const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
27722
+ const out = new Array(ids.length).fill(null);
27723
+ for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
27724
+ let mi = 0;
27725
+ for (let slot = 0; slot < out.length; slot++)
27726
+ if (out[slot] === null) out[slot] = movable[mi++];
27727
+ const final = out;
27728
+ final.forEach((id, i) => orderOf.set(id, i));
27729
+ ranks[r] = final;
27730
+ };
27731
+ const transpose = () => {
27732
+ let improved = true;
27733
+ let guard = 0;
27734
+ while (improved && guard++ < 12) {
27735
+ improved = false;
27736
+ for (let r = 0; r <= maxRank; r++) {
27737
+ const ids = ranks[r];
27738
+ for (let i = 0; i < ids.length - 1; i++) {
27739
+ const before = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
27740
+ const a = ids[i], b = ids[i + 1];
27741
+ ids[i] = b;
27742
+ ids[i + 1] = a;
27743
+ orderOf.set(b, i);
27744
+ orderOf.set(a, i + 1);
27745
+ const after = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
27746
+ if (after < before) {
27747
+ improved = true;
27748
+ } else {
27749
+ ids[i] = a;
27750
+ ids[i + 1] = b;
27751
+ orderOf.set(a, i);
27752
+ orderOf.set(b, i + 1);
27753
+ }
27754
+ }
27755
+ }
27756
+ }
27757
+ };
27758
+ const initOrder = (seed) => {
27759
+ const r = seed === 0 ? null : rng(seed);
27760
+ for (let rr = 0; rr <= maxRank; rr++) {
27761
+ const ids = ranks[rr].slice();
27762
+ if (r) {
27763
+ for (let i = ids.length - 1; i > 0; i--) {
27764
+ const j = Math.floor(r() * (i + 1));
27765
+ [ids[i], ids[j]] = [ids[j], ids[i]];
27766
+ }
27767
+ }
27768
+ ranks[rr] = ids;
27769
+ ids.forEach((id, i) => orderOf.set(id, i));
27770
+ }
27771
+ };
27772
+ const found = [];
27773
+ for (let s = 0; s < restarts; s++) {
27774
+ initOrder(s);
27775
+ let best = totalCrossings();
27776
+ let bestSnap = new Map(orderOf);
27777
+ for (let iter = 0; iter < 8; iter++) {
27778
+ const down1 = iter % 2 === 0;
27779
+ if (down1) for (let r = 1; r <= maxRank; r++) wmedianRank(r, true);
27780
+ else for (let r = maxRank - 1; r >= 0; r--) wmedianRank(r, false);
27781
+ transpose();
27782
+ const c = totalCrossings();
27783
+ if (c < best) {
27784
+ best = c;
27785
+ bestSnap = new Map(orderOf);
27786
+ }
27787
+ if (c === 0) break;
27788
+ }
27789
+ applyOrder(bestSnap);
27790
+ for (let r = 0; r <= maxRank; r++)
27791
+ ranks[r].sort((a, b) => orderOf.get(a) - orderOf.get(b));
27792
+ const sig = ranks.map((r) => r.join(",")).join("|");
27793
+ if (!found.some((f) => f.sig === sig))
27794
+ found.push({ sig, cross: best, order: new Map(bestSnap) });
27795
+ }
27796
+ found.sort((a, b) => a.cross - b.cross);
27797
+ const keep = found.slice(0, keepK);
27798
+ if (keep.length === 0) return [];
27799
+ const results = [];
27800
+ for (const cand of keep) {
27801
+ applyOrder(cand.order);
27802
+ const rk = Array.from({ length: maxRank + 1 }, () => []);
27803
+ for (const n of node.values()) rk[n.rank].push(n.id);
27804
+ for (let r = 0; r <= maxRank; r++)
27805
+ rk[r].sort((a, b) => cand.order.get(a) - cand.order.get(b));
27806
+ results.push(realize(rk));
27807
+ }
27808
+ return results;
27809
+ function realize(rk) {
27810
+ const bandDepth = rk.map(
27811
+ (ids) => Math.max(0, ...ids.map((id) => node.get(id).depth))
27812
+ );
27813
+ const bandCenter = [];
27814
+ let acc = MARGIN3;
27815
+ for (let r = 0; r <= maxRank; r++) {
27816
+ bandCenter[r] = acc + bandDepth[r] / 2;
27817
+ acc += bandDepth[r] + ranksep;
27818
+ }
27819
+ for (let r = 0; r <= maxRank; r++) {
27820
+ let x = MARGIN3;
27821
+ for (const id of rk[r]) {
27822
+ const n = node.get(id);
27823
+ n.cross = x + n.thick / 2;
27824
+ x += n.thick + nodesep;
27825
+ }
27826
+ }
27827
+ const ITER = 18;
27828
+ for (let it = 0; it < ITER; it++) {
27829
+ const downPass = it % 2 === 0;
27830
+ const order = downPass ? Array.from({ length: maxRank + 1 }, (_, i) => i) : Array.from({ length: maxRank + 1 }, (_, i) => maxRank - i);
27831
+ for (const r of order) {
27832
+ const ids = rk[r];
27833
+ const desired = ids.map((id) => {
27834
+ const n = node.get(id);
27835
+ let sw = 0, sx2 = 0;
27836
+ for (const nb of up.get(id)) {
27837
+ const w = weight(n, node.get(nb));
27838
+ sw += w;
27839
+ sx2 += w * node.get(nb).cross;
27840
+ }
27841
+ for (const nb of down.get(id)) {
27842
+ const w = weight(n, node.get(nb));
27843
+ sw += w;
27844
+ sx2 += w * node.get(nb).cross;
27845
+ }
27846
+ return sw > 0 ? sx2 / sw : n.cross;
27847
+ });
27848
+ placeWithSeparation(ids, desired);
27849
+ }
27850
+ }
27851
+ let minC = Infinity;
27852
+ for (const n of node.values()) minC = Math.min(minC, n.cross - n.thick / 2);
27853
+ const shift = MARGIN3 - minC;
27854
+ for (const n of node.values()) n.cross += shift;
27855
+ const coord = (n) => isTB ? { x: n.cross, y: bandCenter[n.rank] } : { x: bandCenter[n.rank], y: n.cross };
27856
+ const layoutNodes = parsed.nodes.map((pn) => {
27857
+ const n = node.get(pn.label);
27858
+ const c = coord(n);
27859
+ return {
27860
+ label: pn.label,
27861
+ x: c.x,
27862
+ y: c.y,
27863
+ width: n.realW,
27864
+ height: n.realH
27865
+ };
27866
+ });
27867
+ const chainById = /* @__PURE__ */ new Map();
27868
+ for (const ch of chains) chainById.set(ch.edgeIdx, ch.chain);
27869
+ let minCross = Infinity, maxCross = -Infinity;
27870
+ for (const n of node.values()) {
27871
+ if (n.dummy) continue;
27872
+ minCross = Math.min(minCross, n.cross - n.thick / 2);
27873
+ maxCross = Math.max(maxCross, n.cross + n.thick / 2);
27874
+ }
27875
+ const GAP = 34;
27876
+ const LANE = 28;
27877
+ const sideOf = (b) => {
27878
+ if (backEdgeSide === "left") return -1;
27879
+ if (backEdgeSide === "right") return 1;
27880
+ const d = node.get(b.src).cross - node.get(b.tgt).cross;
27881
+ return d >= 0 ? 1 : -1;
27882
+ };
27883
+ const backSorted = backEdges.map((b) => ({
27884
+ ...b,
27885
+ side: sideOf(b),
27886
+ span: Math.abs(node.get(b.src).rank - node.get(b.tgt).rank)
27887
+ })).sort((a, b) => a.side - b.side || a.span - b.span);
27888
+ const sideCounts = /* @__PURE__ */ new Map();
27889
+ for (const b of backSorted)
27890
+ sideCounts.set(b.side, (sideCounts.get(b.side) ?? 0) + 1);
27891
+ const laneCounter = /* @__PURE__ */ new Map();
27892
+ const backPoints = /* @__PURE__ */ new Map();
27893
+ const faceLim = (depth) => Math.max(0, depth / 2 - 6);
27894
+ const hubIndex = /* @__PURE__ */ new Map();
27895
+ const hubSize = /* @__PURE__ */ new Map();
27896
+ {
27897
+ const counter = /* @__PURE__ */ new Map();
27898
+ for (const b of backSorted) {
27899
+ const key = `${b.side}|${b.tgt}`;
27900
+ const idx = counter.get(key) ?? 0;
27901
+ counter.set(key, idx + 1);
27902
+ hubIndex.set(b.edgeIdx, idx);
27903
+ }
27904
+ for (const [key, v] of counter) hubSize.set(key, v);
27905
+ }
27906
+ for (const b of backSorted) {
27907
+ const s = b.side;
27908
+ const k = laneCounter.get(s) ?? 0;
27909
+ laneCounter.set(s, k + 1);
27910
+ const K = sideCounts.get(s);
27911
+ const src = node.get(b.src);
27912
+ const tgt = node.get(b.tgt);
27913
+ const laneC = s > 0 ? maxCross + GAP + k * LANE : minCross - GAP - k * LANE;
27914
+ const srcCtr = bandCenter[src.rank];
27915
+ const tgtCtr = bandCenter[tgt.rank];
27916
+ const frac = K > 1 ? (k + 1) / (K + 1) : 0;
27917
+ const srcR = srcCtr + frac * faceLim(src.depth);
27918
+ const tgtR = tgtCtr - frac * faceLim(tgt.depth);
27919
+ const m = (c, r) => isTB ? { x: c, y: r } : { x: r, y: c };
27920
+ const off = 10;
27921
+ const srcEdgeC = src.cross + s * (src.thick / 2 + off);
27922
+ const tgtEdgeC = tgt.cross + s * (tgt.thick / 2 + off);
27923
+ const hubK = hubIndex.get(b.edgeIdx);
27924
+ const hubN = hubSize.get(`${s}|${b.tgt}`);
27925
+ if (hubN > 1 && hubK > 0) {
27926
+ const tgtTop = tgtCtr - (tgt.depth / 2 + GAP + (hubK - 1) * LANE);
27927
+ const entryCross = tgt.cross + s * faceLim(tgt.thick) * (hubK / hubN);
27928
+ backPoints.set(b.edgeIdx, [
27929
+ m(src.cross, srcCtr),
27930
+ m(srcEdgeC, srcR),
27931
+ m(laneC, srcR),
27932
+ m(laneC, tgtTop),
27933
+ m(entryCross, tgtTop),
27934
+ m(tgt.cross, tgtCtr)
27935
+ ]);
27936
+ } else {
27937
+ backPoints.set(b.edgeIdx, [
27938
+ m(src.cross, srcCtr),
27939
+ m(srcEdgeC, srcR),
27940
+ m(laneC, srcR),
27941
+ m(laneC, tgtR),
27942
+ m(tgtEdgeC, tgtR),
27943
+ m(tgt.cross, tgtCtr)
27944
+ ]);
27945
+ }
27946
+ }
27947
+ const layoutEdges = [];
27948
+ for (let i = 0; i < parsed.edges.length; i++) {
27949
+ const e = parsed.edges[i];
27950
+ if (!labelSet.has(e.source) || !labelSet.has(e.target)) continue;
27951
+ let points;
27952
+ if (e.source === e.target) {
27953
+ const c = coord(node.get(e.source));
27954
+ points = [c, c];
27955
+ } else if (backPoints.has(i)) {
27956
+ points = backPoints.get(i);
27957
+ } else {
27958
+ const chain = chainById.get(i);
27959
+ if (!chain) continue;
27960
+ points = chain.map((id) => coord(node.get(id)));
27961
+ }
27962
+ layoutEdges.push({
27963
+ source: e.source,
27964
+ target: e.target,
27965
+ ...e.label !== void 0 && { label: e.label },
27966
+ bidirectional: e.bidirectional,
27967
+ lineNumber: e.lineNumber,
27968
+ points,
27969
+ yOffset: 0,
27970
+ parallelCount: 1,
27971
+ metadata: e.metadata
27972
+ });
27973
+ }
27974
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
27975
+ for (const n of layoutNodes) {
27976
+ minX = Math.min(minX, n.x - n.width / 2);
27977
+ minY = Math.min(minY, n.y - n.height / 2);
27978
+ maxX = Math.max(maxX, n.x + n.width / 2);
27979
+ maxY = Math.max(maxY, n.y + n.height / 2);
27980
+ }
27981
+ for (const e of layoutEdges)
27982
+ for (const p of e.points) {
27983
+ minX = Math.min(minX, p.x);
27984
+ minY = Math.min(minY, p.y);
27985
+ maxX = Math.max(maxX, p.x);
27986
+ maxY = Math.max(maxY, p.y);
27987
+ }
27988
+ const sx = MARGIN3 - minX;
27989
+ const sy = MARGIN3 - minY;
27990
+ const shifted = sx !== 0 || sy !== 0 ? {
27991
+ nodes: layoutNodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })),
27992
+ edges: layoutEdges.map((e) => ({
27993
+ ...e,
27994
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
27995
+ }))
27996
+ } : { nodes: layoutNodes, edges: layoutEdges };
27997
+ return {
27998
+ nodes: shifted.nodes,
27999
+ edges: shifted.edges,
28000
+ groups: [],
28001
+ width: maxX + sx + MARGIN3,
28002
+ height: maxY + sy + MARGIN3
28003
+ };
28004
+ }
28005
+ function weight(a, b) {
28006
+ if (a.dummy && b.dummy) return W_DUMMY_DUMMY;
28007
+ if (a.dummy || b.dummy) return W_REAL_DUMMY;
28008
+ return W_REAL_REAL;
28009
+ }
28010
+ function placeWithSeparation(ids, desired) {
28011
+ const n = ids.length;
28012
+ if (n === 0) return;
28013
+ const half = ids.map((id) => node.get(id).thick / 2);
28014
+ const off = [0];
28015
+ for (let i = 1; i < n; i++)
28016
+ off[i] = off[i - 1] + half[i - 1] + nodesep + half[i];
28017
+ const d = desired.map((v, i) => v - off[i]);
28018
+ const blocks = [];
28019
+ for (let i = 0; i < n; i++) {
28020
+ let b = { pos: d[i], count: 1, sumOffset: d[i], first: i };
28021
+ while (blocks.length && blocks[blocks.length - 1].pos >= b.pos) {
28022
+ const prev = blocks.pop();
28023
+ const count = prev.count + b.count;
28024
+ const sumOffset = prev.sumOffset + b.sumOffset;
28025
+ b = { pos: sumOffset / count, count, sumOffset, first: prev.first };
28026
+ }
28027
+ blocks.push(b);
28028
+ }
28029
+ const xs = new Array(n);
28030
+ for (const b of blocks)
28031
+ for (let k = 0; k < b.count; k++) xs[b.first + k] = b.pos;
28032
+ for (let i = 0; i < n; i++) node.get(ids[i]).cross = xs[i] + off[i];
28033
+ }
28034
+ }
28035
+ var NODESEP, RANKSEP, DUMMY_THICK, MARGIN3, W_REAL_REAL, W_REAL_DUMMY, W_DUMMY_DUMMY;
28036
+ var init_layout_layered = __esm({
28037
+ "src/boxes-and-lines/layout-layered.ts"() {
28038
+ "use strict";
28039
+ init_layout5();
28040
+ NODESEP = 50;
28041
+ RANKSEP = 60;
28042
+ DUMMY_THICK = 10;
28043
+ MARGIN3 = 40;
28044
+ W_REAL_REAL = 1;
28045
+ W_REAL_DUMMY = 2;
28046
+ W_DUMMY_DUMMY = 8;
28047
+ }
28048
+ });
28049
+
28050
+ // src/boxes-and-lines/layout-search.ts
28051
+ var layout_search_exports = {};
28052
+ __export(layout_search_exports, {
28053
+ countEdgeNearMiss: () => countEdgeNearMiss,
28054
+ countEdgeNodePierces: () => countEdgeNodePierces,
28055
+ countEdgeOverlaps: () => countEdgeOverlaps,
28056
+ countGroupOverlaps: () => countGroupOverlaps,
28057
+ countSplineCrossings: () => countSplineCrossings,
28058
+ deroutePierces: () => deroutePierces,
28059
+ detectEdgeNodePierces: () => detectEdgeNodePierces,
28060
+ detectEdgeOverlaps: () => detectEdgeOverlaps,
28061
+ detectGroupOverlaps: () => detectGroupOverlaps,
28062
+ layoutBoxesAndLinesSearch: () => layoutBoxesAndLinesSearch,
28063
+ separateGroupBands: () => separateGroupBands
28064
+ });
28065
+ import dagre4 from "@dagrejs/dagre";
28066
+ import { line as d3line, curveBasis as curveBasis5 } from "d3-shape";
28067
+ function rng2(s) {
28068
+ return () => {
28069
+ s |= 0;
28070
+ s = s + 1831565813 | 0;
28071
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
28072
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
28073
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
28074
+ };
28075
+ }
28076
+ function shuffle(a, r) {
28077
+ const x = a.slice();
28078
+ for (let i = x.length - 1; i > 0; i--) {
28079
+ const j = Math.floor(r() * (i + 1));
28080
+ [x[i], x[j]] = [x[j], x[i]];
28081
+ }
28082
+ return x;
28083
+ }
28084
+ function flatten(d) {
28085
+ const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
28086
+ const pts = [];
28087
+ let i = 0, cx = 0, cy = 0, cmd = "";
28088
+ const num = () => parseFloat(toks[i++]);
28089
+ const samp = (p0, c1, c2, p1) => {
28090
+ for (let t = 0; t <= 1; t += 0.12) {
28091
+ const u = 1 - t;
28092
+ if (c2)
28093
+ pts.push({
28094
+ x: u * u * u * p0.x + 3 * u * u * t * c1.x + 3 * u * t * t * c2.x + t * t * t * p1.x,
28095
+ y: u * u * u * p0.y + 3 * u * u * t * c1.y + 3 * u * t * t * c2.y + t * t * t * p1.y
28096
+ });
28097
+ else
28098
+ pts.push({
28099
+ x: u * u * p0.x + 2 * u * t * c1.x + t * t * p1.x,
28100
+ y: u * u * p0.y + 2 * u * t * c1.y + t * t * p1.y
28101
+ });
28102
+ }
28103
+ };
28104
+ while (i < toks.length) {
28105
+ const tk = toks[i];
28106
+ if (/[MLQC]/i.test(tk)) {
28107
+ cmd = tk;
28108
+ i++;
28109
+ }
28110
+ if (cmd === "M" || cmd === "L") {
28111
+ const x = num(), y = num();
28112
+ pts.push({ x, y });
28113
+ cx = x;
28114
+ cy = y;
28115
+ } else if (cmd === "Q") {
28116
+ const c1 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
28117
+ samp({ x: cx, y: cy }, c1, null, p1);
28118
+ cx = p1.x;
28119
+ cy = p1.y;
28120
+ } else if (cmd === "C") {
28121
+ const c1 = { x: num(), y: num() }, c2 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
28122
+ samp({ x: cx, y: cy }, c1, c2, p1);
28123
+ cx = p1.x;
28124
+ cy = p1.y;
28125
+ } else i++;
28126
+ }
28127
+ return pts;
28128
+ }
28129
+ function segPoint(p1, p2, p3, p4) {
28130
+ const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
28131
+ if (Math.abs(den) < 1e-9) return null;
28132
+ const t = ((p3.x - p1.x) * (p4.y - p3.y) - (p3.y - p1.y) * (p4.x - p3.x)) / den, u = ((p3.x - p1.x) * (p2.y - p1.y) - (p3.y - p1.y) * (p2.x - p1.x)) / den;
28133
+ return t > 0 && t < 1 && u > 0 && u < 1 ? { x: p1.x + t * (p2.x - p1.x), y: p1.y + t * (p2.y - p1.y) } : null;
28134
+ }
28135
+ function countSplineCrossings(layout) {
28136
+ const center = /* @__PURE__ */ new Map();
28137
+ for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
28138
+ for (const g of layout.groups)
28139
+ if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
28140
+ const polys = layout.edges.map((e) => {
28141
+ const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
28142
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28143
+ for (const p of pts) {
28144
+ if (p.x < x0) x0 = p.x;
28145
+ if (p.x > x1) x1 = p.x;
28146
+ if (p.y < y0) y0 = p.y;
28147
+ if (p.y > y1) y1 = p.y;
28148
+ }
28149
+ return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
28150
+ });
28151
+ const R = 34;
28152
+ let total = 0;
28153
+ for (let a = 0; a < polys.length; a++)
28154
+ for (let b = a + 1; b < polys.length; b++) {
28155
+ const A = polys[a], B = polys[b];
28156
+ if (A.pts.length < 2 || B.pts.length < 2) continue;
28157
+ if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
28158
+ const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
28159
+ const hits = [];
28160
+ for (let i = 1; i < A.pts.length; i++)
28161
+ for (let j = 1; j < B.pts.length; j++) {
28162
+ const p = segPoint(
28163
+ A.pts[i - 1],
28164
+ A.pts[i],
28165
+ B.pts[j - 1],
28166
+ B.pts[j]
28167
+ );
28168
+ if (!p) continue;
28169
+ if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
28170
+ continue;
28171
+ if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
28172
+ hits.push(p);
28173
+ }
28174
+ total += hits.length;
28175
+ }
28176
+ return total;
28177
+ }
28178
+ function pointSegDist(p, a, b) {
28179
+ const dx = b.x - a.x, dy = b.y - a.y;
28180
+ const len2 = dx * dx + dy * dy;
28181
+ if (len2 < 1e-9) return Math.hypot(p.x - a.x, p.y - a.y);
28182
+ let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
28183
+ t = Math.max(0, Math.min(1, t));
28184
+ return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));
28185
+ }
28186
+ function distToPoly(p, poly) {
28187
+ let m = Infinity;
28188
+ for (let i = 1; i < poly.length; i++)
28189
+ m = Math.min(m, pointSegDist(p, poly[i - 1], poly[i]));
28190
+ return m;
28191
+ }
28192
+ function pointRectDist(p, r) {
28193
+ const dx = Math.max(r.x - r.w / 2 - p.x, 0, p.x - (r.x + r.w / 2));
28194
+ const dy = Math.max(r.y - r.h / 2 - p.y, 0, p.y - (r.y + r.h / 2));
28195
+ return Math.hypot(dx, dy);
28196
+ }
28197
+ function detectEdgeOverlaps(layout, opts) {
28198
+ const dist = opts?.dist ?? 8;
28199
+ const minLen = opts?.minLen ?? 16;
28200
+ const nodeClear = opts?.nodeClear ?? 12;
28201
+ const rect = /* @__PURE__ */ new Map();
28202
+ for (const n of layout.nodes)
28203
+ rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
28204
+ for (const g of layout.groups)
28205
+ if (g.collapsed)
28206
+ rect.set("__group_" + g.label, {
28207
+ x: g.x,
28208
+ y: g.y,
28209
+ w: g.width,
28210
+ h: g.height
28211
+ });
28212
+ const polys = layout.edges.map((e) => {
28213
+ const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
28214
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28215
+ for (const p of pts) {
28216
+ if (p.x < x0) x0 = p.x;
28217
+ if (p.x > x1) x1 = p.x;
28218
+ if (p.y < y0) y0 = p.y;
28219
+ if (p.y > y1) y1 = p.y;
28220
+ }
28221
+ return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
28222
+ });
28223
+ const runs = [];
28224
+ for (let a = 0; a < polys.length; a++)
28225
+ for (let b = a + 1; b < polys.length; b++) {
28226
+ const A = polys[a], B = polys[b];
28227
+ if (A.pts.length < 2 || B.pts.length < 2) continue;
28228
+ if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
28229
+ continue;
28230
+ const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
28231
+ let run2 = [];
28232
+ let runLen = 0;
28233
+ const flush = () => {
28234
+ if (runLen >= minLen && run2.length >= 2)
28235
+ runs.push({
28236
+ mid: run2[Math.floor(run2.length / 2)],
28237
+ length: runLen,
28238
+ pts: run2.slice()
28239
+ });
28240
+ run2 = [];
28241
+ runLen = 0;
28242
+ };
28243
+ for (const p of A.pts) {
28244
+ const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
28245
+ const covered = !nearShared && distToPoly(p, B.pts) < dist;
28246
+ if (covered) {
28247
+ if (run2.length)
28248
+ runLen += Math.hypot(
28249
+ p.x - run2[run2.length - 1].x,
28250
+ p.y - run2[run2.length - 1].y
28251
+ );
28252
+ run2.push(p);
28253
+ } else flush();
28254
+ }
28255
+ flush();
28256
+ }
28257
+ return runs;
28258
+ }
28259
+ function countEdgeOverlaps(layout, opts) {
28260
+ return detectEdgeOverlaps(layout, opts).length;
28261
+ }
28262
+ function detectEdgeNodePierces(layout, opts) {
28263
+ const inset = opts?.inset ?? 6;
28264
+ const minPts = opts?.minPts ?? 2;
28265
+ const rects = [];
28266
+ for (const n of layout.nodes)
28267
+ rects.push({ key: n.label, x: n.x, y: n.y, w: n.width, h: n.height });
28268
+ for (const g of layout.groups)
28269
+ if (g.collapsed)
28270
+ rects.push({
28271
+ key: "__group_" + g.label,
28272
+ x: g.x,
28273
+ y: g.y,
28274
+ w: g.width,
28275
+ h: g.height
28276
+ });
28277
+ const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
28278
+ const out = [];
28279
+ layout.edges.forEach((e, idx) => {
28280
+ if (e.points.length < 2) return;
28281
+ const poly = flatten(splineGen(e.points) ?? "");
28282
+ for (const r of rects) {
28283
+ if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
28284
+ continue;
28285
+ const hits = poly.filter((p) => inside(p, r));
28286
+ if (hits.length >= minPts)
28287
+ out.push({ edgeIdx: idx, node: r.key, pts: hits });
28288
+ }
28289
+ });
28290
+ return out;
28291
+ }
28292
+ function countEdgeNodePierces(layout, opts) {
28293
+ return detectEdgeNodePierces(layout, opts).length;
28294
+ }
28295
+ function deroutePierces(layout) {
28296
+ const pierces = detectEdgeNodePierces(layout);
28297
+ if (!pierces.length) return layout;
28298
+ const rect = /* @__PURE__ */ new Map();
28299
+ for (const n of layout.nodes)
28300
+ rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
28301
+ for (const g of layout.groups)
28302
+ if (g.collapsed)
28303
+ rect.set("__group_" + g.label, {
28304
+ x: g.x,
28305
+ y: g.y,
28306
+ w: g.width,
28307
+ h: g.height
28308
+ });
28309
+ const byEdge = /* @__PURE__ */ new Map();
28310
+ for (const p of pierces) {
28311
+ const arr = byEdge.get(p.edgeIdx);
28312
+ if (arr) arr.push(p.node);
28313
+ else byEdge.set(p.edgeIdx, [p.node]);
28314
+ }
28315
+ const edges = layout.edges.map((e, idx) => {
28316
+ const nodes = byEdge.get(idx);
28317
+ if (!nodes) return e;
28318
+ let pts = e.points.map((p) => ({ x: p.x, y: p.y }));
28319
+ for (const label of nodes) {
28320
+ const r = rect.get(label);
28321
+ if (r) pts = detourAround(pts, r);
28322
+ }
28323
+ return { ...e, points: pts };
28324
+ });
28325
+ return { ...layout, edges };
28326
+ }
28327
+ function detourAround(pts, r) {
28328
+ if (pts.length < 2) return pts;
28329
+ const c = { x: r.x, y: r.y };
28330
+ let bestSeg = -1;
28331
+ let bestT = 0;
28332
+ let bestD = Infinity;
28333
+ let bestPt = pts[0];
28334
+ for (let i = 0; i < pts.length - 1; i++) {
28335
+ const a2 = pts[i], b2 = pts[i + 1];
28336
+ const dx = b2.x - a2.x, dy = b2.y - a2.y;
28337
+ const len2 = dx * dx + dy * dy || 1e-9;
28338
+ let t = ((c.x - a2.x) * dx + (c.y - a2.y) * dy) / len2;
28339
+ t = Math.max(0, Math.min(1, t));
28340
+ const q = { x: a2.x + t * dx, y: a2.y + t * dy };
28341
+ const d = Math.hypot(q.x - c.x, q.y - c.y);
28342
+ if (d < bestD) {
28343
+ bestD = d;
28344
+ bestSeg = i;
28345
+ bestT = t;
28346
+ bestPt = q;
28347
+ }
28348
+ }
28349
+ if (bestSeg < 0) return pts;
28350
+ let nx = bestPt.x - c.x;
28351
+ let ny = bestPt.y - c.y;
28352
+ if (Math.hypot(nx, ny) < 1) {
28353
+ if (r.w <= r.h) {
28354
+ nx = 1;
28355
+ ny = 0;
28356
+ } else {
28357
+ nx = 0;
28358
+ ny = 1;
28359
+ }
28360
+ }
28361
+ const nlen = Math.hypot(nx, ny) || 1;
28362
+ nx /= nlen;
28363
+ ny /= nlen;
28364
+ const a = pts[bestSeg], b = pts[bestSeg + 1];
28365
+ let ex = b.x - a.x, ey = b.y - a.y;
28366
+ const elen = Math.hypot(ex, ey) || 1;
28367
+ ex /= elen;
28368
+ ey /= elen;
28369
+ const clear = Math.hypot(r.w, r.h) / 2 + 18;
28370
+ const hw = Math.hypot(r.w, r.h) / 2;
28371
+ void bestT;
28372
+ const before = {
28373
+ x: bestPt.x - ex * hw + nx * clear * 0.5,
28374
+ y: bestPt.y - ey * hw + ny * clear * 0.5
28375
+ };
28376
+ const peak = { x: c.x + nx * clear, y: c.y + ny * clear };
28377
+ const after = {
28378
+ x: bestPt.x + ex * hw + nx * clear * 0.5,
28379
+ y: bestPt.y + ey * hw + ny * clear * 0.5
28380
+ };
28381
+ const out = pts.slice(0, bestSeg + 1);
28382
+ out.push(before, peak, after);
28383
+ out.push(...pts.slice(bestSeg + 1));
28384
+ return out;
28385
+ }
28386
+ function countEdgeNearMiss(layout, opts) {
28387
+ const near = opts?.near ?? 14;
28388
+ const tight = opts?.tight ?? 8;
28389
+ const minLen = opts?.minLen ?? 18;
28390
+ const close = detectEdgeOverlaps(layout, { dist: near, minLen }).length;
28391
+ const over = detectEdgeOverlaps(layout, { dist: tight, minLen }).length;
28392
+ return Math.max(0, close - over);
28393
+ }
28394
+ function detectGroupOverlaps(layout, opts) {
28395
+ const margin = opts?.margin ?? 4;
28396
+ const raw = layout.groups.map((g) => ({
28397
+ label: g.label,
28398
+ l: g.x - g.width / 2,
28399
+ r: g.x + g.width / 2,
28400
+ t: g.y - g.height / 2,
28401
+ b: g.y + g.height / 2
28402
+ }));
28403
+ const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
28404
+ const rend = raw.map((a, i) => {
28405
+ const parent = raw.some((b, j) => j !== i && contains(a, b));
28406
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
28407
+ });
28408
+ const hit = /* @__PURE__ */ new Set();
28409
+ for (let i = 0; i < raw.length; i++)
28410
+ for (let j = i + 1; j < raw.length; j++) {
28411
+ if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
28412
+ const a = rend[i], b = rend[j];
28413
+ const dx = Math.max(a.l - b.r, b.l - a.r);
28414
+ const dy = Math.max(a.t - b.b, b.t - a.b);
28415
+ if (Math.max(dx, dy) < margin) {
28416
+ hit.add(raw[i].label);
28417
+ hit.add(raw[j].label);
28418
+ }
28419
+ }
28420
+ return [...hit];
28421
+ }
28422
+ function countGroupOverlaps(layout, opts) {
28423
+ const margin = opts?.margin ?? 4;
28424
+ const raw = layout.groups.map((g) => ({
28425
+ l: g.x - g.width / 2,
28426
+ r: g.x + g.width / 2,
28427
+ t: g.y - g.height / 2,
28428
+ b: g.y + g.height / 2
28429
+ }));
28430
+ const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
28431
+ const rend = raw.map((a, i) => {
28432
+ const parent = raw.some((b, j) => j !== i && contains(a, b));
28433
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
28434
+ });
28435
+ let count = 0;
28436
+ for (let i = 0; i < raw.length; i++)
28437
+ for (let j = i + 1; j < raw.length; j++) {
28438
+ if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
28439
+ const a = rend[i], b = rend[j];
28440
+ const dx = Math.max(a.l - b.r, b.l - a.r);
28441
+ const dy = Math.max(a.t - b.b, b.t - a.b);
28442
+ if (Math.max(dx, dy) < margin) count++;
28443
+ }
28444
+ return count;
28445
+ }
28446
+ function separateGroupBands(layout, parsed) {
28447
+ if (layout.groups.some((g) => g.collapsed)) return layout;
28448
+ if (countGroupOverlaps(layout) === 0) return layout;
28449
+ const parentOf = /* @__PURE__ */ new Map();
28450
+ for (const g of parsed.groups) parentOf.set(g.label, g.parentGroup);
28451
+ const topOf = (label) => {
28452
+ let cur = label;
28453
+ const seen = /* @__PURE__ */ new Set();
28454
+ for (; ; ) {
28455
+ const p = parentOf.get(cur);
28456
+ if (!p || seen.has(p)) return cur;
28457
+ seen.add(cur);
28458
+ cur = p;
28459
+ }
28460
+ };
28461
+ const topLabels = parsed.groups.filter((g) => !g.parentGroup).map((g) => g.label);
28462
+ if (topLabels.length < 2) return layout;
28463
+ const nodeTop = /* @__PURE__ */ new Map();
28464
+ for (const g of parsed.groups)
28465
+ for (const child of g.children)
28466
+ if (!parsed.groups.some((gg) => gg.label === child))
28467
+ nodeTop.set(child, topOf(g.label));
28468
+ const axis = parsed.direction === "TB" ? "x" : "y";
28469
+ const boxByLabel = new Map(layout.groups.map((g) => [g.label, g]));
28470
+ const bands = topLabels.map((label) => {
28471
+ const g = boxByLabel.get(label);
28472
+ const half2 = (axis === "y" ? g.height : g.width) / 2;
28473
+ let lo = (axis === "y" ? g.y : g.x) - half2;
28474
+ const hi = (axis === "y" ? g.y : g.x) + half2;
28475
+ const isParent = parsed.groups.some((c) => c.parentGroup === label);
28476
+ if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE2;
28477
+ return { label, lo, hi, c: (lo + hi) / 2 };
28478
+ });
28479
+ bands.sort((a, b) => a.c - b.c);
28480
+ const GAP = 16;
28481
+ const half = bands.map((b) => (b.hi - b.lo) / 2);
28482
+ const off = [0];
28483
+ for (let i = 1; i < bands.length; i++)
28484
+ off[i] = off[i - 1] + half[i - 1] + GAP + half[i];
28485
+ const desired = bands.map((b, i) => b.c - off[i]);
28486
+ const blocks = [];
28487
+ for (let i = 0; i < bands.length; i++) {
28488
+ let blk = { pos: desired[i], count: 1, sum: desired[i], first: i };
28489
+ while (blocks.length && blocks[blocks.length - 1].pos >= blk.pos) {
28490
+ const prev = blocks.pop();
28491
+ const count = prev.count + blk.count;
28492
+ const sum = prev.sum + blk.sum;
28493
+ blk = { pos: sum / count, count, sum, first: prev.first };
28494
+ }
28495
+ blocks.push(blk);
28496
+ }
28497
+ const newC = new Array(bands.length);
28498
+ for (const blk of blocks)
28499
+ for (let k = 0; k < blk.count; k++)
28500
+ newC[blk.first + k] = blk.pos + off[blk.first + k];
28501
+ const delta = /* @__PURE__ */ new Map();
28502
+ let moved = false;
28503
+ bands.forEach((b, i) => {
28504
+ const d = newC[i] - b.c;
28505
+ delta.set(b.label, d);
28506
+ if (Math.abs(d) > 0.5) moved = true;
28507
+ });
28508
+ if (!moved) return layout;
28509
+ const nodeDelta = (label) => {
28510
+ const top = nodeTop.get(label);
28511
+ return top ? delta.get(top) ?? 0 : 0;
28512
+ };
28513
+ const shift = (p, d) => axis === "y" ? { x: p.x, y: p.y + d } : { x: p.x + d, y: p.y };
28514
+ const nodes = layout.nodes.map((n) => {
28515
+ const d = nodeDelta(n.label);
28516
+ return d ? { ...n, ...axis === "y" ? { y: n.y + d } : { x: n.x + d } } : n;
28517
+ });
28518
+ const groups = layout.groups.map((g) => {
28519
+ const d = delta.get(topOf(g.label)) ?? 0;
28520
+ return d ? { ...g, ...axis === "y" ? { y: g.y + d } : { x: g.x + d } } : g;
28521
+ });
28522
+ const edges = layout.edges.map((e) => {
28523
+ const ds = nodeDelta(e.source);
28524
+ const dt = nodeDelta(e.target);
28525
+ if (!ds && !dt) return e;
28526
+ const N = e.points.length;
28527
+ const points = e.points.map((p, i) => {
28528
+ const f = N > 1 ? i / (N - 1) : 0;
28529
+ return shift(p, ds * (1 - f) + dt * f);
28530
+ });
28531
+ return { ...e, points };
28532
+ });
28533
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
28534
+ const acc = (x, y) => {
28535
+ if (x < minX) minX = x;
28536
+ if (x > maxX) maxX = x;
28537
+ if (y < minY) minY = y;
28538
+ if (y > maxY) maxY = y;
28539
+ };
28540
+ for (const n of nodes) {
28541
+ acc(n.x - n.width / 2, n.y - n.height / 2);
28542
+ acc(n.x + n.width / 2, n.y + n.height / 2);
28543
+ }
28544
+ for (const g of groups) {
28545
+ acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE2);
28546
+ acc(g.x + g.width / 2, g.y + g.height / 2);
28547
+ }
28548
+ for (const e of edges) for (const p of e.points) acc(p.x, p.y);
28549
+ const M = 40;
28550
+ const sx = M - minX, sy = M - minY;
28551
+ const reshift = sx !== 0 || sy !== 0;
28552
+ return {
28553
+ nodes: reshift ? nodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })) : nodes,
28554
+ groups: reshift ? groups.map((g) => ({ ...g, x: g.x + sx, y: g.y + sy })) : groups,
28555
+ edges: reshift ? edges.map((e) => ({
28556
+ ...e,
28557
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
28558
+ })) : edges,
28559
+ width: maxX - minX + 2 * M,
28560
+ height: maxY - minY + 2 * M
28561
+ };
28562
+ }
28563
+ function segCross(p1, p2, p3, p4) {
28564
+ const d1x = p2.x - p1.x, d1y = p2.y - p1.y, d2x = p4.x - p3.x, d2y = p4.y - p3.y;
28565
+ const den = d1x * d2y - d1y * d2x;
28566
+ if (Math.abs(den) < 1e-9) return false;
28567
+ const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / den;
28568
+ const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / den;
28569
+ return t > 1e-3 && t < 0.999 && s > 1e-3 && s < 0.999;
28570
+ }
28571
+ function countCrossingsFast(layout) {
28572
+ const E = layout.edges.filter((e) => e.points.length >= 2);
28573
+ const bb = E.map((e) => {
28574
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28575
+ for (const p of e.points) {
28576
+ if (p.x < x0) x0 = p.x;
28577
+ if (p.x > x1) x1 = p.x;
28578
+ if (p.y < y0) y0 = p.y;
28579
+ if (p.y > y1) y1 = p.y;
28580
+ }
28581
+ return { x0, y0, x1, y1 };
28582
+ });
28583
+ let count = 0;
28584
+ for (let i = 0; i < E.length; i++)
28585
+ for (let j = i + 1; j < E.length; j++) {
28586
+ const A = E[i], B = E[j];
28587
+ if (A.source === B.source || A.source === B.target || A.target === B.source || A.target === B.target)
28588
+ continue;
28589
+ const a = bb[i], b = bb[j];
28590
+ if (a.x1 < b.x0 || b.x1 < a.x0 || a.y1 < b.y0 || b.y1 < a.y0) continue;
28591
+ const pa = A.points, pb = B.points;
28592
+ let hit = false;
28593
+ for (let ai = 0; ai < pa.length - 1 && !hit; ai++)
28594
+ for (let bi = 0; bi < pb.length - 1; bi++) {
28595
+ if (segCross(pa[ai], pa[ai + 1], pb[bi], pb[bi + 1])) {
28596
+ hit = true;
28597
+ break;
28598
+ }
28599
+ }
28600
+ if (hit) count++;
28601
+ }
28602
+ return count;
28603
+ }
28604
+ function meanDrift(layout, prev) {
28605
+ if (!prev?.size) return 0;
28606
+ let sum = 0, n = 0;
28607
+ for (const node of layout.nodes) {
28608
+ const p = prev.get(node.label);
28609
+ if (p) {
28610
+ sum += Math.hypot(node.x - p.x, node.y - p.y);
28611
+ n++;
28612
+ }
28613
+ }
28614
+ return n ? sum / n : 0;
28615
+ }
28616
+ function edgeLength(layout) {
28617
+ let total = 0;
28618
+ for (const e of layout.edges)
28619
+ for (let i = 1; i < e.points.length; i++)
28620
+ total += Math.hypot(
28621
+ e.points[i].x - e.points[i - 1].x,
28622
+ e.points[i].y - e.points[i - 1].y
28623
+ );
28624
+ return total;
28625
+ }
28626
+ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
28627
+ const hideDescriptions = opts?.hideDescriptions ?? false;
28628
+ const collapsedGroupLabels = /* @__PURE__ */ new Set();
28629
+ if (collapseInfo) {
28630
+ const missing = /* @__PURE__ */ new Set();
28631
+ for (const og of collapseInfo.originalGroups)
28632
+ if (!parsed.groups.some((g) => g.label === og.label))
28633
+ missing.add(og.label);
28634
+ for (const label of missing) {
28635
+ const og = collapseInfo.originalGroups.find((g) => g.label === label);
28636
+ const parent = og?.parentGroup;
28637
+ if (!parent || !missing.has(parent)) collapsedGroupLabels.add(label);
28638
+ }
28639
+ }
28640
+ const sizes = /* @__PURE__ */ new Map();
28641
+ let maxDescH = 0;
28642
+ for (const node of parsed.nodes) {
28643
+ const s = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
28644
+ sizes.set(node.label, s);
28645
+ if (!hideDescriptions && node.description && node.description.length > 0)
28646
+ maxDescH = Math.max(maxDescH, s.height);
28647
+ }
28648
+ if (maxDescH > 0) {
28649
+ for (const node of parsed.nodes)
28650
+ if (node.description && node.description.length > 0) {
28651
+ const s = sizes.get(node.label);
28652
+ sizes.set(node.label, { width: s.width, height: maxDescH });
28653
+ }
28654
+ }
28655
+ const gid = (label) => `__group_${label}`;
28656
+ const rankdir = parsed.direction === "TB" ? "TB" : "LR";
28657
+ function place(cfg) {
28658
+ const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
28659
+ const ord = (a) => r ? shuffle(a, r) : a.slice();
28660
+ const g = new dagre4.graphlib.Graph({ compound: true, multigraph: true });
28661
+ g.setGraph({
28662
+ rankdir,
28663
+ ranker: cfg.ranker,
28664
+ nodesep: cfg.nodesep,
28665
+ ranksep: cfg.ranksep,
28666
+ edgesep: 20,
28667
+ marginx: 40,
28668
+ marginy: 40
28669
+ });
28670
+ g.setDefaultEdgeLabel(() => ({}));
28671
+ for (const grp of ord(parsed.groups))
28672
+ g.setNode(gid(grp.label), { label: grp.label });
28673
+ for (const node of ord(parsed.nodes)) {
28674
+ const s = sizes.get(node.label);
28675
+ g.setNode(node.label, { width: s.width, height: s.height });
28676
+ }
28677
+ for (const label of collapsedGroupLabels)
28678
+ g.setNode(gid(label), { width: NODE_WIDTH, height: NODE_HEIGHT });
28679
+ for (const grp of parsed.groups) {
28680
+ if (grp.parentGroup && g.hasNode(gid(grp.parentGroup)))
28681
+ g.setParent(gid(grp.label), gid(grp.parentGroup));
28682
+ for (const c of ord(grp.children)) {
28683
+ if (g.hasNode(c)) g.setParent(c, gid(grp.label));
28684
+ }
28685
+ }
28686
+ if (collapseInfo)
28687
+ for (const label of collapsedGroupLabels) {
28688
+ const og = collapseInfo.originalGroups.find((x) => x.label === label);
28689
+ if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && g.hasNode(gid(og.parentGroup)))
28690
+ g.setParent(gid(label), gid(og.parentGroup));
28691
+ }
28692
+ for (const e of ord(parsed.edges))
28693
+ if (g.hasNode(e.source) && g.hasNode(e.target))
28694
+ g.setEdge(e.source, e.target, {});
28695
+ dagre4.layout(g);
28696
+ const nodes = parsed.nodes.map((n2) => {
28697
+ const p = g.node(n2.label);
28698
+ return {
28699
+ label: n2.label,
28700
+ x: p.x,
28701
+ y: p.y,
28702
+ width: p.width,
28703
+ height: p.height
28704
+ };
28705
+ });
28706
+ const groups = parsed.groups.map(
28707
+ (grp) => {
28708
+ const p = g.node(gid(grp.label));
28709
+ return {
28710
+ label: grp.label,
28711
+ lineNumber: grp.lineNumber,
28712
+ x: p.x,
28713
+ y: p.y,
28714
+ width: p.width,
28715
+ height: p.height,
28716
+ collapsed: false,
28717
+ childCount: grp.children.length
28718
+ };
28719
+ }
28720
+ );
28721
+ for (const label of collapsedGroupLabels) {
28722
+ const p = g.node(gid(label));
28723
+ groups.push({
28724
+ label,
28725
+ lineNumber: 0,
28726
+ x: p.x,
28727
+ y: p.y,
28728
+ width: p.width,
28729
+ height: p.height,
28730
+ collapsed: true,
28731
+ childCount: collapseInfo?.collapsedChildCounts.get(label) ?? 0
28732
+ });
28733
+ }
28734
+ const edges = parsed.edges.filter((e) => g.hasEdge(e.source, e.target)).map((e) => {
28735
+ const ed = g.edge(e.source, e.target);
28736
+ return {
28737
+ source: e.source,
28738
+ target: e.target,
28739
+ ...e.label !== void 0 && { label: e.label },
28740
+ bidirectional: e.bidirectional,
28741
+ lineNumber: e.lineNumber,
28742
+ points: ed?.points ?? [],
28743
+ yOffset: 0,
28744
+ parallelCount: 1,
28745
+ metadata: e.metadata
28746
+ };
28747
+ });
28748
+ const gg = g.graph();
28749
+ return {
28750
+ nodes,
28751
+ edges,
28752
+ groups,
28753
+ width: gg.width ?? 800,
28754
+ height: gg.height ?? 600
28755
+ };
28756
+ }
28757
+ const n = parsed.nodes.length;
28758
+ const seedCount = opts?.seeds ?? (n <= 12 ? 80 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
28759
+ const REFINE_K = opts?.refineK ?? 6;
28760
+ const lambda = opts?.lambda ?? DEFAULT_LAMBDA;
28761
+ const prev = opts?.previousPositions;
28762
+ const RANKERS = ["network-simplex", "tight-tree", "longest-path"];
28763
+ const SPACINGS = [
28764
+ { nodesep: 50, ranksep: 60 },
28765
+ { nodesep: 34, ranksep: 46 },
28766
+ { nodesep: 66, ranksep: 82 }
28767
+ ];
28768
+ const configs = [];
28769
+ for (const ranker of RANKERS)
28770
+ for (const sp of SPACINGS) configs.push({ ranker, ...sp });
28771
+ for (let s = 0; s < seedCount; s++)
28772
+ configs.push({
28773
+ ranker: "network-simplex",
28774
+ nodesep: 50,
28775
+ ranksep: 60,
28776
+ seed: s
28777
+ });
28778
+ const badness = (lay, floor) => {
28779
+ const x = countSplineCrossings(lay);
28780
+ if (x > floor) return Infinity;
28781
+ return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
28782
+ };
28783
+ const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
28784
+ const pool = [];
28785
+ for (const cfg of configs) {
28786
+ try {
28787
+ pool.push(place(cfg));
28788
+ } catch {
28789
+ }
28790
+ }
28791
+ if (!pool.length)
28792
+ return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
28793
+ let layered = [];
28794
+ try {
28795
+ layered = layeredCandidates(parsed, sizes);
28796
+ } catch {
28797
+ }
28798
+ pool.sort(
28799
+ (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
28800
+ );
28801
+ const refineK = Math.min(REFINE_K, pool.length);
28802
+ let best = pool[0];
28803
+ let bestObj = Infinity;
28804
+ let bestBad = Infinity;
28805
+ const consider = (lay) => {
28806
+ const bad = badness(lay, bestBad);
28807
+ if (bad === Infinity) return;
28808
+ const sc = objective(lay, bad);
28809
+ if (sc < bestObj) {
28810
+ bestObj = sc;
28811
+ bestBad = bad;
28812
+ best = lay;
28813
+ }
28814
+ };
28815
+ for (const lay of pool.slice(0, refineK)) consider(lay);
28816
+ if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
28817
+ const extra = [];
28818
+ for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
28819
+ try {
28820
+ extra.push(
28821
+ place({
28822
+ ranker: "network-simplex",
28823
+ nodesep: 50,
28824
+ ranksep: 60,
28825
+ seed: s
28826
+ })
28827
+ );
28828
+ } catch {
28829
+ }
28830
+ }
28831
+ extra.sort(
28832
+ (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
28833
+ );
28834
+ for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
28835
+ }
28836
+ for (const lay of layered) {
28837
+ const variants = [lay];
28838
+ const dp = deroutePierces(lay);
28839
+ if (dp !== lay) variants.push(dp);
28840
+ for (const v of variants) {
28841
+ const bad = badness(v, bestBad - 1);
28842
+ if (bad < bestBad) {
28843
+ bestBad = bad;
28844
+ best = v;
28845
+ }
28846
+ }
28847
+ }
28848
+ if (bestBad > 0) {
28849
+ const rerouted = deroutePierces(best);
28850
+ if (rerouted !== best && badness(rerouted, bestBad - 1) < bestBad)
28851
+ best = rerouted;
28852
+ }
28853
+ if (bestBad > 0 && countGroupOverlaps(best) > 0) {
28854
+ const separated = separateGroupBands(best, parsed);
28855
+ if (separated !== best && badness(separated, bestBad - 1) < bestBad)
28856
+ best = separated;
28857
+ }
28858
+ return best;
28859
+ }
28860
+ var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
28861
+ var init_layout_search = __esm({
28862
+ "src/boxes-and-lines/layout-search.ts"() {
28863
+ "use strict";
28864
+ init_layout5();
28865
+ init_layout_layered();
28866
+ DEFAULT_LAMBDA = 4;
28867
+ ESCALATE_THRESHOLD = 4;
28868
+ ESCALATE_MAX_N = 45;
28869
+ ESCALATE_SEEDS = 18;
28870
+ ESCALATE_REFINE = 10;
28871
+ splineGen = d3line().x((d) => d.x).y((d) => d.y).curve(curveBasis5);
28872
+ GROUP_LABEL_ZONE2 = 32;
28873
+ }
28874
+ });
28875
+
27546
28876
  // src/boxes-and-lines/layout.ts
27547
28877
  var layout_exports5 = {};
27548
28878
  __export(layout_exports5, {
28879
+ NODE_HEIGHT: () => NODE_HEIGHT,
28880
+ NODE_WIDTH: () => NODE_WIDTH,
28881
+ computeNodeSize: () => computeNodeSize,
27549
28882
  layoutBoxesAndLines: () => layoutBoxesAndLines
27550
28883
  });
27551
- import ELK from "elkjs/lib/elk.bundled.js";
27552
28884
  function splitCamelCase2(word) {
27553
28885
  const parts = [];
27554
28886
  let start = 0;
@@ -27619,417 +28951,21 @@ function computeNodeSize(node, reserveValueRow) {
27619
28951
  const totalHeight = labelHeight + SEPARATOR_GAP5 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
27620
28952
  return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
27621
28953
  }
27622
- function getElk() {
27623
- if (!elkInstance) elkInstance = new ELK();
27624
- return elkInstance;
27625
- }
27626
- function baseOptions() {
27627
- return {
27628
- "elk.algorithm": "layered",
27629
- // INCLUDE_CHILDREN lets ELK route edges across container boundaries.
27630
- "elk.hierarchyHandling": "INCLUDE_CHILDREN",
27631
- "elk.edgeRouting": "ORTHOGONAL",
27632
- "elk.layered.unnecessaryBendpoints": "true",
27633
- // Let edges leave from top/bottom of nodes (not just the flow-direction
27634
- // sides) when it reduces crossings.
27635
- "elk.layered.allowNonFlowPortsToSwitchSides": "true"
27636
- };
27637
- }
27638
- function bkBaseline() {
27639
- return {
27640
- ...baseOptions(),
27641
- "elk.layered.nodePlacement.strategy": "BRANDES_KOEPF",
27642
- "elk.layered.nodePlacement.bk.fixedAlignment": "BALANCED",
27643
- "elk.layered.nodePlacement.bk.edgeStraightening": "IMPROVE_STRAIGHTNESS",
27644
- "elk.layered.compaction.connectedComponents": "true",
27645
- "elk.layered.spacing.nodeNodeBetweenLayers": "90",
27646
- "elk.spacing.nodeNode": "55",
27647
- "elk.spacing.edgeNode": "55",
27648
- "elk.spacing.edgeEdge": "18"
27649
- };
27650
- }
27651
- function getVariants() {
27652
- const bk = bkBaseline();
27653
- return [
27654
- {
27655
- name: "bk-baseline",
27656
- options: {
27657
- ...bk,
27658
- "elk.layered.crossingMinimization.greedySwitch.type": "ONE_SIDED"
27659
- }
27660
- },
27661
- {
27662
- name: "bk-aggressive",
27663
- options: {
27664
- ...bk,
27665
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27666
- "elk.layered.thoroughness": "50"
27667
- }
27668
- },
27669
- {
27670
- name: "bk-wide",
27671
- options: {
27672
- ...bk,
27673
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27674
- "elk.layered.thoroughness": "50",
27675
- "elk.spacing.nodeNode": "70",
27676
- "elk.spacing.edgeNode": "75",
27677
- "elk.spacing.edgeEdge": "22",
27678
- "elk.layered.spacing.nodeNodeBetweenLayers": "120"
27679
- }
27680
- },
27681
- {
27682
- name: "longest-path",
27683
- options: {
27684
- ...bk,
27685
- "elk.layered.layering.strategy": "LONGEST_PATH",
27686
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27687
- "elk.layered.thoroughness": "50"
27688
- }
27689
- },
27690
- {
27691
- name: "bounded-width",
27692
- options: {
27693
- ...bk,
27694
- "elk.layered.layering.strategy": "COFFMAN_GRAHAM",
27695
- "elk.layered.layering.coffmanGraham.layerBound": "3",
27696
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27697
- "elk.layered.thoroughness": "50"
27698
- }
27699
- }
27700
- ];
27701
- }
27702
- function countCrossings(edges) {
27703
- let count = 0;
27704
- for (let i = 0; i < edges.length; i++) {
27705
- const edgeI = edges[i];
27706
- const a = edgeI.points;
27707
- if (a.length < 2) continue;
27708
- for (let j = i + 1; j < edges.length; j++) {
27709
- const edgeJ = edges[j];
27710
- const b = edgeJ.points;
27711
- if (b.length < 2) continue;
27712
- if (edgeI.source === edgeJ.source) continue;
27713
- if (edgeI.source === edgeJ.target) continue;
27714
- if (edgeI.target === edgeJ.source) continue;
27715
- if (edgeI.target === edgeJ.target) continue;
27716
- for (let ai = 0; ai < a.length - 1; ai++) {
27717
- for (let bi = 0; bi < b.length - 1; bi++) {
27718
- if (segmentsCross(a[ai], a[ai + 1], b[bi], b[bi + 1])) count++;
27719
- }
27720
- }
27721
- }
27722
- }
27723
- return count;
27724
- }
27725
- function segmentsCross(p1, p2, p3, p4) {
27726
- const d1x = p2.x - p1.x;
27727
- const d1y = p2.y - p1.y;
27728
- const d2x = p4.x - p3.x;
27729
- const d2y = p4.y - p3.y;
27730
- const denom = d1x * d2y - d1y * d2x;
27731
- if (Math.abs(denom) < 1e-9) return false;
27732
- const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / denom;
27733
- const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / denom;
27734
- const EPS = 1e-3;
27735
- return t > EPS && t < 1 - EPS && s > EPS && s < 1 - EPS;
27736
- }
27737
- function countTotalBends(edges) {
27738
- let bends = 0;
27739
- for (const e of edges) bends += Math.max(0, e.points.length - 2);
27740
- return bends;
27741
- }
27742
- function scoreLayout(layout) {
27743
- return {
27744
- crossings: countCrossings(layout.edges),
27745
- bends: countTotalBends(layout.edges),
27746
- area: layout.width * layout.height
27747
- };
27748
- }
27749
- function cmpScore(a, b) {
27750
- const aBucket = a.crossings <= CROSSINGS_FORGIVENESS ? 0 : a.crossings;
27751
- const bBucket = b.crossings <= CROSSINGS_FORGIVENESS ? 0 : b.crossings;
27752
- if (aBucket !== bBucket) return aBucket - bBucket;
27753
- if (a.area !== b.area) return a.area - b.area;
27754
- return a.bends - b.bends;
27755
- }
27756
28954
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
27757
- const hideDescriptions = layoutOptions?.hideDescriptions ?? false;
27758
- const direction = parsed.direction === "TB" ? "DOWN" : "RIGHT";
27759
- const collapsedGroupLabels = /* @__PURE__ */ new Set();
27760
- if (collapseInfo) {
27761
- const missingGroups = /* @__PURE__ */ new Set();
27762
- for (const og of collapseInfo.originalGroups) {
27763
- if (!parsed.groups.some((g) => g.label === og.label)) {
27764
- missingGroups.add(og.label);
27765
- }
27766
- }
27767
- for (const label of missingGroups) {
27768
- const og = collapseInfo.originalGroups.find((g) => g.label === label);
27769
- const parentLabel = og?.parentGroup;
27770
- if (!parentLabel || !missingGroups.has(parentLabel)) {
27771
- collapsedGroupLabels.add(label);
27772
- }
27773
- }
27774
- }
27775
- const nodeSizes = /* @__PURE__ */ new Map();
27776
- let maxDescHeight = 0;
27777
- for (const node of parsed.nodes) {
27778
- const size = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
27779
- nodeSizes.set(node.label, size);
27780
- if (!hideDescriptions && node.description && node.description.length > 0) {
27781
- maxDescHeight = Math.max(maxDescHeight, size.height);
27782
- }
27783
- }
27784
- if (maxDescHeight > 0) {
27785
- for (const node of parsed.nodes) {
27786
- if (node.description && node.description.length > 0) {
27787
- const size = nodeSizes.get(node.label);
27788
- nodeSizes.set(node.label, { width: size.width, height: maxDescHeight });
27789
- }
27790
- }
27791
- }
27792
- const expandedGroupSet = new Set(parsed.groups.map((g) => g.label));
27793
- const gid = (label) => `__group_${label}`;
27794
- function buildGraph() {
27795
- const nodeById = /* @__PURE__ */ new Map();
27796
- const parentOf = /* @__PURE__ */ new Map();
27797
- for (const node of parsed.nodes) {
27798
- const size = nodeSizes.get(node.label);
27799
- nodeById.set(node.label, {
27800
- id: node.label,
27801
- width: size.width,
27802
- height: size.height,
27803
- labels: [{ text: node.label }]
27804
- });
27805
- }
27806
- for (const group of parsed.groups) {
27807
- nodeById.set(gid(group.label), {
27808
- id: gid(group.label),
27809
- labels: [{ text: group.label }],
27810
- layoutOptions: {
27811
- "elk.padding": `[top=${CONTAINER_PAD_TOP2},left=${CONTAINER_PAD_X3},bottom=${CONTAINER_PAD_BOTTOM3},right=${CONTAINER_PAD_X3}]`,
27812
- // Suggest square-ish containers — has limited effect with
27813
- // INCLUDE_CHILDREN but doesn't hurt.
27814
- "elk.aspectRatio": "1.4"
27815
- },
27816
- children: [],
27817
- edges: []
27818
- });
27819
- }
27820
- for (const label of collapsedGroupLabels) {
27821
- nodeById.set(gid(label), {
27822
- id: gid(label),
27823
- width: NODE_WIDTH,
27824
- height: NODE_HEIGHT,
27825
- labels: [{ text: label }]
27826
- });
27827
- }
27828
- for (const group of parsed.groups) {
27829
- if (group.parentGroup && nodeById.has(gid(group.parentGroup))) {
27830
- parentOf.set(gid(group.label), gid(group.parentGroup));
27831
- }
27832
- }
27833
- if (collapseInfo) {
27834
- for (const label of collapsedGroupLabels) {
27835
- const og = collapseInfo.originalGroups.find((g) => g.label === label);
27836
- if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && nodeById.has(gid(og.parentGroup))) {
27837
- parentOf.set(gid(label), gid(og.parentGroup));
27838
- }
27839
- }
27840
- }
27841
- for (const group of parsed.groups) {
27842
- for (const child of group.children) {
27843
- if (expandedGroupSet.has(child)) continue;
27844
- if (nodeById.has(child)) {
27845
- parentOf.set(child, gid(group.label));
27846
- }
27847
- }
27848
- }
27849
- const roots = [];
27850
- for (const [id, node] of nodeById) {
27851
- const parentId = parentOf.get(id);
27852
- if (parentId) {
27853
- const parent = nodeById.get(parentId);
27854
- parent.children = parent.children ?? [];
27855
- parent.children.push(node);
27856
- } else {
27857
- roots.push(node);
27858
- }
27859
- }
27860
- const rootEdges = [];
27861
- for (let i = 0; i < parsed.edges.length; i++) {
27862
- const edge = parsed.edges[i];
27863
- if (!nodeById.has(edge.source) || !nodeById.has(edge.target)) continue;
27864
- rootEdges.push({
27865
- id: `e${i}`,
27866
- sources: [edge.source],
27867
- targets: [edge.target]
27868
- });
27869
- }
27870
- return { roots, rootEdges };
27871
- }
27872
- async function runVariant(variant) {
27873
- const { roots, rootEdges } = buildGraph();
27874
- const elkRoot = {
27875
- id: "root",
27876
- layoutOptions: {
27877
- ...variant.options,
27878
- "elk.direction": direction,
27879
- "elk.padding": `[top=${MARGIN3},left=${MARGIN3},bottom=${MARGIN3},right=${MARGIN3}]`
27880
- },
27881
- children: roots,
27882
- edges: rootEdges
27883
- };
27884
- const result = await getElk().layout(elkRoot);
27885
- return extractLayout(result);
27886
- }
27887
- function extractLayout(result) {
27888
- const layoutNodes = [];
27889
- const layoutGroups = [];
27890
- const allEdges = [];
27891
- const containerAbs = /* @__PURE__ */ new Map();
27892
- function walk(n, offsetX, offsetY, isRoot) {
27893
- const nx = (n.x ?? 0) + offsetX;
27894
- const ny = (n.y ?? 0) + offsetY;
27895
- const nw = n.width ?? 0;
27896
- const nh = n.height ?? 0;
27897
- if (isRoot) {
27898
- containerAbs.set("root", { x: nx, y: ny });
27899
- } else {
27900
- const isGroup = n.id.startsWith("__group_");
27901
- if (isGroup) {
27902
- const label = n.id.slice("__group_".length);
27903
- const collapsed = collapsedGroupLabels.has(label);
27904
- const og = collapseInfo?.originalGroups.find(
27905
- (g) => g.label === label
27906
- );
27907
- const pg = parsed.groups.find((g) => g.label === label);
27908
- const childCount = collapsed ? collapseInfo?.collapsedChildCounts.get(label) ?? 0 : void 0;
27909
- layoutGroups.push({
27910
- label,
27911
- lineNumber: pg?.lineNumber ?? og?.lineNumber ?? 0,
27912
- x: nx + nw / 2,
27913
- y: ny + nh / 2,
27914
- width: nw,
27915
- height: nh,
27916
- collapsed,
27917
- ...childCount !== void 0 && { childCount }
27918
- });
27919
- if (!collapsed) containerAbs.set(n.id, { x: nx, y: ny });
27920
- } else {
27921
- layoutNodes.push({
27922
- label: n.id,
27923
- x: nx + nw / 2,
27924
- y: ny + nh / 2,
27925
- width: nw,
27926
- height: nh
27927
- });
27928
- }
27929
- }
27930
- if (n.edges) for (const e of n.edges) allEdges.push(e);
27931
- if (n.children) for (const c of n.children) walk(c, nx, ny, false);
27932
- }
27933
- walk(result, 0, 0, true);
27934
- const edgeYOffsets = new Array(parsed.edges.length).fill(0);
27935
- const edgeParallelCounts = new Array(parsed.edges.length).fill(1);
27936
- const parallelGroups = /* @__PURE__ */ new Map();
27937
- for (let i = 0; i < parsed.edges.length; i++) {
27938
- const edge = parsed.edges[i];
27939
- const [a, b] = edge.source < edge.target ? [edge.source, edge.target] : [edge.target, edge.source];
27940
- const key = `${a}\0${b}`;
27941
- if (!parallelGroups.has(key)) parallelGroups.set(key, []);
27942
- parallelGroups.get(key).push(i);
27943
- }
27944
- for (const group of parallelGroups.values()) {
27945
- const capped = group.slice(0, MAX_PARALLEL_EDGES);
27946
- for (const idx of group.slice(MAX_PARALLEL_EDGES)) {
27947
- edgeParallelCounts[idx] = 0;
27948
- }
27949
- if (capped.length < 2) continue;
27950
- for (let j = 0; j < capped.length; j++) {
27951
- const cappedJ = capped[j];
27952
- edgeYOffsets[cappedJ] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
27953
- edgeParallelCounts[cappedJ] = capped.length;
27954
- }
27955
- }
27956
- const edgeById = /* @__PURE__ */ new Map();
27957
- for (const e of allEdges) edgeById.set(e.id, e);
27958
- const layoutEdges = [];
27959
- for (let i = 0; i < parsed.edges.length; i++) {
27960
- const edge = parsed.edges[i];
27961
- if (edgeParallelCounts[i] === 0) continue;
27962
- const elkEdge = edgeById.get(`e${i}`);
27963
- if (!elkEdge?.sections || elkEdge.sections.length === 0) continue;
27964
- const container = elkEdge.container ?? "root";
27965
- const off = containerAbs.get(container) ?? { x: 0, y: 0 };
27966
- const s = elkEdge.sections[0];
27967
- const points = [
27968
- { x: s.startPoint.x + off.x, y: s.startPoint.y + off.y },
27969
- ...(s.bendPoints ?? []).map((p) => ({
27970
- x: p.x + off.x,
27971
- y: p.y + off.y
27972
- })),
27973
- { x: s.endPoint.x + off.x, y: s.endPoint.y + off.y }
27974
- ];
27975
- let labelX;
27976
- let labelY;
27977
- if (edge.label && points.length >= 2) {
27978
- const mid = Math.floor(points.length / 2);
27979
- const midPoint = points[mid];
27980
- labelX = midPoint.x;
27981
- labelY = midPoint.y - 10;
27982
- }
27983
- layoutEdges.push({
27984
- source: edge.source,
27985
- target: edge.target,
27986
- ...edge.label !== void 0 && { label: edge.label },
27987
- bidirectional: edge.bidirectional,
27988
- lineNumber: edge.lineNumber,
27989
- points,
27990
- ...labelX !== void 0 && { labelX },
27991
- ...labelY !== void 0 && { labelY },
27992
- // In-bounds — i < parsed.edges.length, arrays sized to that length.
27993
- yOffset: edgeYOffsets[i],
27994
- parallelCount: edgeParallelCounts[i],
27995
- metadata: edge.metadata,
27996
- deferred: true
27997
- });
27998
- }
27999
- let maxX = 0;
28000
- let maxY = 0;
28001
- for (const node of layoutNodes) {
28002
- maxX = Math.max(maxX, node.x + node.width / 2);
28003
- maxY = Math.max(maxY, node.y + node.height / 2);
28004
- }
28005
- for (const group of layoutGroups) {
28006
- maxX = Math.max(maxX, group.x + group.width / 2);
28007
- maxY = Math.max(maxY, group.y + group.height / 2);
28955
+ const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
28956
+ const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
28957
+ ...layoutOptions?.hideDescriptions !== void 0 && {
28958
+ hideDescriptions: layoutOptions.hideDescriptions
28959
+ },
28960
+ ...layoutOptions?.previousPositions !== void 0 && {
28961
+ previousPositions: layoutOptions.previousPositions
28008
28962
  }
28009
- return {
28010
- nodes: layoutNodes,
28011
- edges: layoutEdges,
28012
- groups: layoutGroups,
28013
- width: maxX + MARGIN3,
28014
- height: maxY + MARGIN3
28015
- };
28016
- }
28017
- const N = parsed.nodes.length + parsed.groups.length;
28018
- const E = parsed.edges.length;
28019
- const trivial = N < 8 && E < 10;
28020
- const variants = trivial ? [getVariants()[1]] : getVariants();
28021
- const results = await Promise.all(variants.map((v) => runVariant(v)));
28022
- let best = results[0];
28023
- let bestScore = scoreLayout(best);
28024
- for (let i = 1; i < results.length; i++) {
28025
- const resultI = results[i];
28026
- const s = scoreLayout(resultI);
28027
- if (cmpScore(s, bestScore) < 0) {
28028
- best = resultI;
28029
- bestScore = s;
28030
- }
28031
- }
28032
- return attachNotes(best, parsed, layoutOptions?.collapsedNotes);
28963
+ });
28964
+ return attachNotes(
28965
+ applyParallelEdgeOffsets(searched),
28966
+ parsed,
28967
+ layoutOptions?.collapsedNotes
28968
+ );
28033
28969
  }
28034
28970
  function attachNotes(layout, parsed, collapsedNotes) {
28035
28971
  const notesSuppressed = parsed.options?.["no-notes"] === "on";
@@ -28107,20 +29043,47 @@ function attachNotes(layout, parsed, collapsedNotes) {
28107
29043
  nodes: finalNodes,
28108
29044
  edges: finalEdges,
28109
29045
  groups: finalGroups,
28110
- width: bbMaxX + shiftX + MARGIN3,
28111
- height: bbMaxY + shiftY + MARGIN3
29046
+ width: bbMaxX + shiftX + MARGIN4,
29047
+ height: bbMaxY + shiftY + MARGIN4
29048
+ };
29049
+ }
29050
+ function applyParallelEdgeOffsets(layout) {
29051
+ const groups = /* @__PURE__ */ new Map();
29052
+ layout.edges.forEach((e, i) => {
29053
+ const [a, b] = e.source < e.target ? [e.source, e.target] : [e.target, e.source];
29054
+ const key = `${a}\0${b}`;
29055
+ const arr = groups.get(key);
29056
+ if (arr) arr.push(i);
29057
+ else groups.set(key, [i]);
29058
+ });
29059
+ if ([...groups.values()].every((g) => g.length < 2)) return layout;
29060
+ const yOffset = new Array(layout.edges.length).fill(0);
29061
+ const count = new Array(layout.edges.length).fill(1);
29062
+ for (const idxs of groups.values()) {
29063
+ const capped = idxs.slice(0, MAX_PARALLEL_EDGES);
29064
+ for (const drop of idxs.slice(MAX_PARALLEL_EDGES)) count[drop] = 0;
29065
+ if (capped.length < 2) continue;
29066
+ capped.forEach((idx, j) => {
29067
+ yOffset[idx] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
29068
+ count[idx] = capped.length;
29069
+ });
29070
+ }
29071
+ return {
29072
+ ...layout,
29073
+ edges: layout.edges.map((e, i) => ({
29074
+ ...e,
29075
+ yOffset: yOffset[i],
29076
+ parallelCount: count[i]
29077
+ }))
28112
29078
  };
28113
29079
  }
28114
- var MARGIN3, CONTAINER_PAD_X3, CONTAINER_PAD_TOP2, CONTAINER_PAD_BOTTOM3, MAX_PARALLEL_EDGES, PARALLEL_SPACING, PHI, NODE_HEIGHT, NODE_WIDTH, DESC_NODE_WIDTH, DESC_FONT_SIZE2, DESC_LINE_HEIGHT2, DESC_PADDING, SEPARATOR_GAP5, MAX_DESC_LINES2, MAX_LABEL_LINES, LABEL_LINE_HEIGHT, LABEL_PAD, VALUE_ROW_FONT, VALUE_ROW_H, elkInstance, CROSSINGS_FORGIVENESS;
29080
+ var MARGIN4, MAX_PARALLEL_EDGES, PARALLEL_SPACING, PHI, NODE_HEIGHT, NODE_WIDTH, DESC_NODE_WIDTH, DESC_FONT_SIZE2, DESC_LINE_HEIGHT2, DESC_PADDING, SEPARATOR_GAP5, MAX_DESC_LINES2, MAX_LABEL_LINES, LABEL_LINE_HEIGHT, LABEL_PAD, VALUE_ROW_FONT, VALUE_ROW_H;
28115
29081
  var init_layout5 = __esm({
28116
29082
  "src/boxes-and-lines/layout.ts"() {
28117
29083
  "use strict";
28118
29084
  init_text_measure();
28119
29085
  init_notes();
28120
- MARGIN3 = 40;
28121
- CONTAINER_PAD_X3 = 30;
28122
- CONTAINER_PAD_TOP2 = 40;
28123
- CONTAINER_PAD_BOTTOM3 = 24;
29086
+ MARGIN4 = 40;
28124
29087
  MAX_PARALLEL_EDGES = 5;
28125
29088
  PARALLEL_SPACING = 22;
28126
29089
  PHI = 1.618;
@@ -28137,8 +29100,6 @@ var init_layout5 = __esm({
28137
29100
  LABEL_PAD = 12;
28138
29101
  VALUE_ROW_FONT = 11;
28139
29102
  VALUE_ROW_H = SEPARATOR_GAP5 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
28140
- elkInstance = null;
28141
- CROSSINGS_FORGIVENESS = 1;
28142
29103
  }
28143
29104
  });
28144
29105
 
@@ -28293,7 +29254,7 @@ function layoutMindmap(parsed, _palette, options) {
28293
29254
  leafWidth: ctx.structural(LEAF_WIDTH),
28294
29255
  hGap: ctx.aesthetic(H_GAP2),
28295
29256
  vGap: ctx.aesthetic(V_GAP2),
28296
- margin: ctx.aesthetic(MARGIN4),
29257
+ margin: ctx.aesthetic(MARGIN5),
28297
29258
  multiRootGap: ctx.aesthetic(MULTI_ROOT_GAP)
28298
29259
  };
28299
29260
  populateDepthCache(roots);
@@ -28671,7 +29632,7 @@ function populateDepthCache(roots) {
28671
29632
  };
28672
29633
  walk(roots, 0);
28673
29634
  }
28674
- var ROOT_WIDTH, DEPTH1_WIDTH, LEAF_WIDTH, SINGLE_LABEL_HEIGHT, LABEL_LINE_HEIGHT2, DESC_LINE_HEIGHT3, NODE_V_PAD, H_GAP2, V_GAP2, MARGIN4, MULTI_ROOT_GAP, nodeDepthCache;
29635
+ var ROOT_WIDTH, DEPTH1_WIDTH, LEAF_WIDTH, SINGLE_LABEL_HEIGHT, LABEL_LINE_HEIGHT2, DESC_LINE_HEIGHT3, NODE_V_PAD, H_GAP2, V_GAP2, MARGIN5, MULTI_ROOT_GAP, nodeDepthCache;
28675
29636
  var init_layout6 = __esm({
28676
29637
  "src/mindmap/layout.ts"() {
28677
29638
  "use strict";
@@ -28687,7 +29648,7 @@ var init_layout6 = __esm({
28687
29648
  NODE_V_PAD = 10;
28688
29649
  H_GAP2 = 40;
28689
29650
  V_GAP2 = 12;
28690
- MARGIN4 = 40;
29651
+ MARGIN5 = 40;
28691
29652
  MULTI_ROOT_GAP = 60;
28692
29653
  nodeDepthCache = /* @__PURE__ */ new Map();
28693
29654
  }
@@ -29962,7 +30923,7 @@ __export(layout_exports8, {
29962
30923
  layoutC4Deployment: () => layoutC4Deployment,
29963
30924
  rollUpContextRelationships: () => rollUpContextRelationships
29964
30925
  });
29965
- import dagre4 from "@dagrejs/dagre";
30926
+ import dagre5 from "@dagrejs/dagre";
29966
30927
  function computeEdgePenalty(edgeList, nodePositions, degrees, nodeGeometry) {
29967
30928
  let penalty = 0;
29968
30929
  for (const edge of edgeList) {
@@ -30387,7 +31348,7 @@ function layoutC4Context(parsed, activeTagGroup) {
30387
31348
  }
30388
31349
  const contextRels = rollUpContextRelationships(parsed);
30389
31350
  const spacing = computeAdaptiveSpacing(contextRels);
30390
- const g = new dagre4.graphlib.Graph();
31351
+ const g = new dagre5.graphlib.Graph();
30391
31352
  g.setGraph({
30392
31353
  rankdir: "TB",
30393
31354
  nodesep: spacing.nodesep,
@@ -30408,7 +31369,7 @@ function layoutC4Context(parsed, activeTagGroup) {
30408
31369
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
30409
31370
  }
30410
31371
  }
30411
- dagre4.layout(g);
31372
+ dagre5.layout(g);
30412
31373
  reduceCrossings(
30413
31374
  g,
30414
31375
  validRels.map((r) => ({ source: r.sourceName, target: r.targetName }))
@@ -30475,8 +31436,8 @@ function layoutC4Context(parsed, activeTagGroup) {
30475
31436
  }
30476
31437
  }
30477
31438
  if (nodes.length > 0) {
30478
- const shiftX = MARGIN5 - minX;
30479
- const shiftY = MARGIN5 - minY;
31439
+ const shiftX = MARGIN6 - minX;
31440
+ const shiftY = MARGIN6 - minY;
30480
31441
  for (const node of nodes) {
30481
31442
  node.x += shiftX;
30482
31443
  node.y += shiftY;
@@ -30488,12 +31449,12 @@ function layoutC4Context(parsed, activeTagGroup) {
30488
31449
  }
30489
31450
  }
30490
31451
  }
30491
- let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN5 * 2 : 0;
30492
- let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN5 * 2 : 0;
31452
+ let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN6 * 2 : 0;
31453
+ let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN6 * 2 : 0;
30493
31454
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
30494
31455
  if (legendGroups.length > 0) {
30495
- const legendY = totalHeight + MARGIN5;
30496
- let legendX = MARGIN5;
31456
+ const legendY = totalHeight + MARGIN6;
31457
+ let legendX = MARGIN6;
30497
31458
  for (const lg of legendGroups) {
30498
31459
  lg.x = legendX;
30499
31460
  lg.y = legendY;
@@ -30586,7 +31547,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30586
31547
  }
30587
31548
  }
30588
31549
  const hasGroups = elementToGroup.size > 0;
30589
- const g = hasGroups ? new dagre4.graphlib.Graph({ compound: true }) : new dagre4.graphlib.Graph();
31550
+ const g = hasGroups ? new dagre5.graphlib.Graph({ compound: true }) : new dagre5.graphlib.Graph();
30590
31551
  g.setDefaultEdgeLabel(() => ({}));
30591
31552
  if (hasGroups) {
30592
31553
  const seenGroups = /* @__PURE__ */ new Set();
@@ -30664,7 +31625,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30664
31625
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
30665
31626
  }
30666
31627
  }
30667
- dagre4.layout(g);
31628
+ dagre5.layout(g);
30668
31629
  const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
30669
31630
  reduceCrossings(
30670
31631
  g,
@@ -30825,8 +31786,8 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30825
31786
  if (pt.y > maxY) maxY = pt.y;
30826
31787
  }
30827
31788
  }
30828
- const shiftX = MARGIN5 - minX;
30829
- const shiftY = MARGIN5 - minY;
31789
+ const shiftX = MARGIN6 - minX;
31790
+ const shiftY = MARGIN6 - minY;
30830
31791
  for (const node of nodes) {
30831
31792
  node.x += shiftX;
30832
31793
  node.y += shiftY;
@@ -30843,12 +31804,12 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30843
31804
  pt.y += shiftY;
30844
31805
  }
30845
31806
  }
30846
- let totalWidth = maxX - minX + MARGIN5 * 2;
30847
- let totalHeight = maxY - minY + MARGIN5 * 2;
31807
+ let totalWidth = maxX - minX + MARGIN6 * 2;
31808
+ let totalHeight = maxY - minY + MARGIN6 * 2;
30848
31809
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
30849
31810
  if (legendGroups.length > 0) {
30850
- const legendY = totalHeight + MARGIN5;
30851
- let legendX = MARGIN5;
31811
+ const legendY = totalHeight + MARGIN6;
31812
+ let legendX = MARGIN6;
30852
31813
  for (const lg of legendGroups) {
30853
31814
  lg.x = legendX;
30854
31815
  lg.y = legendY;
@@ -30993,7 +31954,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
30993
31954
  }
30994
31955
  }
30995
31956
  const hasGroups = elementToGroup.size > 0;
30996
- const g = hasGroups ? new dagre4.graphlib.Graph({ compound: true }) : new dagre4.graphlib.Graph();
31957
+ const g = hasGroups ? new dagre5.graphlib.Graph({ compound: true }) : new dagre5.graphlib.Graph();
30997
31958
  g.setDefaultEdgeLabel(() => ({}));
30998
31959
  if (hasGroups) {
30999
31960
  const seenGroups = /* @__PURE__ */ new Set();
@@ -31077,7 +32038,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31077
32038
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
31078
32039
  }
31079
32040
  }
31080
- dagre4.layout(g);
32041
+ dagre5.layout(g);
31081
32042
  const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
31082
32043
  reduceCrossings(
31083
32044
  g,
@@ -31240,8 +32201,8 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31240
32201
  if (pt.y > maxY) maxY = pt.y;
31241
32202
  }
31242
32203
  }
31243
- const shiftX = MARGIN5 - minX;
31244
- const shiftY = MARGIN5 - minY;
32204
+ const shiftX = MARGIN6 - minX;
32205
+ const shiftY = MARGIN6 - minY;
31245
32206
  for (const node of nodes) {
31246
32207
  node.x += shiftX;
31247
32208
  node.y += shiftY;
@@ -31258,12 +32219,12 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31258
32219
  pt.y += shiftY;
31259
32220
  }
31260
32221
  }
31261
- let totalWidth = maxX - minX + MARGIN5 * 2;
31262
- let totalHeight = maxY - minY + MARGIN5 * 2;
32222
+ let totalWidth = maxX - minX + MARGIN6 * 2;
32223
+ let totalHeight = maxY - minY + MARGIN6 * 2;
31263
32224
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
31264
32225
  if (legendGroups.length > 0) {
31265
- const legendY = totalHeight + MARGIN5;
31266
- let legendX = MARGIN5;
32226
+ const legendY = totalHeight + MARGIN6;
32227
+ let legendX = MARGIN6;
31267
32228
  for (const lg of legendGroups) {
31268
32229
  lg.x = legendX;
31269
32230
  lg.y = legendY;
@@ -31369,7 +32330,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31369
32330
  for (const r of refEntries) {
31370
32331
  nameToElement.set(r.element.name, r.element);
31371
32332
  }
31372
- const g = new dagre4.graphlib.Graph({ compound: true });
32333
+ const g = new dagre5.graphlib.Graph({ compound: true });
31373
32334
  g.setDefaultEdgeLabel(() => ({}));
31374
32335
  for (const [infraId] of infraIds) {
31375
32336
  g.setNode(infraId, {});
@@ -31413,7 +32374,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31413
32374
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
31414
32375
  }
31415
32376
  }
31416
- dagre4.layout(g);
32377
+ dagre5.layout(g);
31417
32378
  const nodeInfraMap = /* @__PURE__ */ new Map();
31418
32379
  for (const r of refEntries) nodeInfraMap.set(r.element.name, r.infraId);
31419
32380
  reduceCrossings(
@@ -31551,8 +32512,8 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31551
32512
  if (pt.y > maxY) maxY = pt.y;
31552
32513
  }
31553
32514
  }
31554
- const shiftX = MARGIN5 - minX;
31555
- const shiftY = MARGIN5 - minY;
32515
+ const shiftX = MARGIN6 - minX;
32516
+ const shiftY = MARGIN6 - minY;
31556
32517
  for (const node of nodes) {
31557
32518
  node.x += shiftX;
31558
32519
  node.y += shiftY;
@@ -31567,12 +32528,12 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31567
32528
  pt.y += shiftY;
31568
32529
  }
31569
32530
  }
31570
- let totalWidth = maxX - minX + MARGIN5 * 2;
31571
- let totalHeight = maxY - minY + MARGIN5 * 2;
32531
+ let totalWidth = maxX - minX + MARGIN6 * 2;
32532
+ let totalHeight = maxY - minY + MARGIN6 * 2;
31572
32533
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
31573
32534
  if (legendGroups.length > 0) {
31574
- const legendY = totalHeight + MARGIN5;
31575
- let legendX = MARGIN5;
32535
+ const legendY = totalHeight + MARGIN6;
32536
+ let legendX = MARGIN6;
31576
32537
  for (const lg of legendGroups) {
31577
32538
  lg.x = legendX;
31578
32539
  lg.y = legendY;
@@ -31592,7 +32553,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31592
32553
  height: totalHeight
31593
32554
  };
31594
32555
  }
31595
- var gNode, gEdge, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, NAME_FONT_SIZE, DESC_LINE_HEIGHT5, DESC_FONT_SIZE4, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT5, META_FONT_SIZE5, MARGIN5, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
32556
+ var gNode, gEdge, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, NAME_FONT_SIZE, DESC_LINE_HEIGHT5, DESC_FONT_SIZE4, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT5, META_FONT_SIZE5, MARGIN6, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
31596
32557
  var init_layout8 = __esm({
31597
32558
  "src/c4/layout.ts"() {
31598
32559
  "use strict";
@@ -31612,7 +32573,7 @@ var init_layout8 = __esm({
31612
32573
  CARD_H_PAD3 = 20;
31613
32574
  META_LINE_HEIGHT5 = 16;
31614
32575
  META_FONT_SIZE5 = 11;
31615
- MARGIN5 = 40;
32576
+ MARGIN6 = 40;
31616
32577
  BOUNDARY_PAD = 40;
31617
32578
  GROUP_BOUNDARY_PAD = 24;
31618
32579
  EDGE_NODE_COLLISION_WEIGHT = 5e3;
@@ -32647,7 +33608,7 @@ var layout_exports9 = {};
32647
33608
  __export(layout_exports9, {
32648
33609
  layoutGraph: () => layoutGraph
32649
33610
  });
32650
- import dagre5 from "@dagrejs/dagre";
33611
+ import dagre6 from "@dagrejs/dagre";
32651
33612
  function computeNodeWidth(label, shape) {
32652
33613
  if (shape === "pseudostate") return 24;
32653
33614
  const base = Math.max(120, label.length * 9 + 40);
@@ -32681,7 +33642,7 @@ function layoutGraph(graph, options) {
32681
33642
  if (allNodes.length === 0) {
32682
33643
  return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
32683
33644
  }
32684
- const g = new dagre5.graphlib.Graph({ compound: true });
33645
+ const g = new dagre6.graphlib.Graph({ compound: true });
32685
33646
  g.setGraph({
32686
33647
  rankdir: graph.direction,
32687
33648
  nodesep: 50,
@@ -32732,7 +33693,7 @@ function layoutGraph(graph, options) {
32732
33693
  label: edge.label ?? ""
32733
33694
  });
32734
33695
  }
32735
- dagre5.layout(g);
33696
+ dagre6.layout(g);
32736
33697
  const collapsedGroupIds = collapsedChildCounts ? new Set(collapsedChildCounts.keys()) : /* @__PURE__ */ new Set();
32737
33698
  const basePositioned = allNodes.map((node) => {
32738
33699
  const pos = g.node(node.id);
@@ -34436,7 +35397,7 @@ __export(layout_exports10, {
34436
35397
  layoutInfra: () => layoutInfra,
34437
35398
  separateGroups: () => separateGroups
34438
35399
  });
34439
- import dagre6 from "@dagrejs/dagre";
35400
+ import dagre7 from "@dagrejs/dagre";
34440
35401
  function countDisplayProps(node, expanded, options) {
34441
35402
  if (!expanded) return 0;
34442
35403
  let count = node.properties.filter((p) => DISPLAY_KEYS.has(p.key)).length;
@@ -34738,7 +35699,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
34738
35699
  };
34739
35700
  }
34740
35701
  const isLR = computed.direction !== "TB";
34741
- const g = new dagre6.graphlib.Graph();
35702
+ const g = new dagre7.graphlib.Graph();
34742
35703
  g.setGraph({
34743
35704
  rankdir: computed.direction === "TB" ? "TB" : "LR",
34744
35705
  nodesep: isLR ? 70 : 60,
@@ -34789,7 +35750,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
34789
35750
  g.setEdge(edge.sourceId, edge.targetId, { label: edge.label });
34790
35751
  }
34791
35752
  }
34792
- dagre6.layout(g);
35753
+ dagre7.layout(g);
34793
35754
  const layoutNodes = computed.nodes.map(
34794
35755
  (node) => {
34795
35756
  const pos = g.node(node.id);
@@ -36531,17 +37492,17 @@ function mulberry32(seed) {
36531
37492
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
36532
37493
  };
36533
37494
  }
36534
- function standardNormal(rng) {
37495
+ function standardNormal(rng3) {
36535
37496
  let u = 0;
36536
37497
  let v = 0;
36537
- while (u === 0) u = rng();
36538
- while (v === 0) v = rng();
37498
+ while (u === 0) u = rng3();
37499
+ while (v === 0) v = rng3();
36539
37500
  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
36540
37501
  }
36541
- function gamma(shape, rng) {
37502
+ function gamma(shape, rng3) {
36542
37503
  if (shape < 1) {
36543
- const u = rng();
36544
- return gamma(shape + 1, rng) * Math.pow(u, 1 / shape);
37504
+ const u = rng3();
37505
+ return gamma(shape + 1, rng3) * Math.pow(u, 1 / shape);
36545
37506
  }
36546
37507
  const d = shape - 1 / 3;
36547
37508
  const c = 1 / Math.sqrt(9 * d);
@@ -36549,29 +37510,29 @@ function gamma(shape, rng) {
36549
37510
  let x;
36550
37511
  let v;
36551
37512
  do {
36552
- x = standardNormal(rng);
37513
+ x = standardNormal(rng3);
36553
37514
  v = 1 + c * x;
36554
37515
  } while (v <= 0);
36555
37516
  v = v * v * v;
36556
- const u = rng();
37517
+ const u = rng3();
36557
37518
  if (u < 1 - 0.0331 * x * x * x * x) return d * v;
36558
37519
  if (Math.log(u) < x * x / 2 + d * (1 - v + Math.log(v))) return d * v;
36559
37520
  }
36560
37521
  }
36561
- function sampleBeta(alpha, beta, rng) {
36562
- const x = gamma(alpha, rng);
36563
- const y = gamma(beta, rng);
37522
+ function sampleBeta(alpha, beta, rng3) {
37523
+ const x = gamma(alpha, rng3);
37524
+ const y = gamma(beta, rng3);
36564
37525
  return x / (x + y);
36565
37526
  }
36566
- function sampleBetaPert(o, m, p, rng) {
37527
+ function sampleBetaPert(o, m, p, rng3) {
36567
37528
  if (o === p) return m;
36568
37529
  const range = p - o;
36569
37530
  const alpha = 1 + 4 * (m - o) / range;
36570
37531
  const beta = 1 + 4 * (p - m) / range;
36571
- return o + sampleBeta(alpha, beta, rng) * range;
37532
+ return o + sampleBeta(alpha, beta, rng3) * range;
36572
37533
  }
36573
37534
  function simulate(resolved, expanded, _predecessors, _successors, topo, terminals, poisoned, opts) {
36574
- const rng = mulberry32(opts.seed);
37535
+ const rng3 = mulberry32(opts.seed);
36575
37536
  const expById = /* @__PURE__ */ new Map();
36576
37537
  for (const e of expanded) expById.set(e.id, e);
36577
37538
  const completionTimes = new Array(opts.trials);
@@ -36594,7 +37555,7 @@ function simulate(resolved, expanded, _predecessors, _successors, topo, terminal
36594
37555
  } else if (exp.o === exp.p) {
36595
37556
  duration = exp.m;
36596
37557
  } else {
36597
- duration = sampleBetaPert(exp.o, exp.m, exp.p, rng);
37558
+ duration = sampleBetaPert(exp.o, exp.m, exp.p, rng3);
36598
37559
  }
36599
37560
  let esLower = 0;
36600
37561
  let efLower = -Infinity;
@@ -37583,7 +38544,7 @@ __export(layout_exports11, {
37583
38544
  layoutPert: () => layoutPert,
37584
38545
  relayoutPert: () => relayoutPert
37585
38546
  });
37586
- import dagre7 from "@dagrejs/dagre";
38547
+ import dagre8 from "@dagrejs/dagre";
37587
38548
  function computeNodeSizing(resolved) {
37588
38549
  const unit = resolved.options.timeUnit;
37589
38550
  const sprintMode = resolved.options.sprintMode;
@@ -37701,7 +38662,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
37701
38662
  }
37702
38663
  }
37703
38664
  const dagreId = (id) => memberToGroup.get(id) ?? id;
37704
- const g = new dagre7.graphlib.Graph();
38665
+ const g = new dagre8.graphlib.Graph();
37705
38666
  g.setGraph({
37706
38667
  rankdir: resolved.options.direction,
37707
38668
  nodesep: 50,
@@ -37740,7 +38701,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
37740
38701
  seenEdges.add(k);
37741
38702
  g.setEdge(src, tgt, {});
37742
38703
  }
37743
- dagre7.layout(g);
38704
+ dagre8.layout(g);
37744
38705
  const swimApplied = applySwimLanes(
37745
38706
  g,
37746
38707
  resolved,
@@ -38086,7 +39047,7 @@ function reduceCrossings2(g, direction) {
38086
39047
  buckets.get(key).push(id);
38087
39048
  }
38088
39049
  const edges = g.edges().map((e) => ({ v: e.v, w: e.w }));
38089
- const countCrossings2 = () => {
39050
+ const countCrossings = () => {
38090
39051
  let total = 0;
38091
39052
  for (let i = 0; i < edges.length; i++) {
38092
39053
  const a = edges[i];
@@ -38099,13 +39060,13 @@ function reduceCrossings2(g, direction) {
38099
39060
  const b1 = g.node(b.v);
38100
39061
  const b2 = g.node(b.w);
38101
39062
  if (!b1 || !b2) continue;
38102
- if (segmentsCross2(a1, a2, b1, b2)) total++;
39063
+ if (segmentsCross(a1, a2, b1, b2)) total++;
38103
39064
  }
38104
39065
  }
38105
39066
  return total;
38106
39067
  };
38107
39068
  const MAX_ITER = 8;
38108
- let baseline = countCrossings2();
39069
+ let baseline = countCrossings();
38109
39070
  if (baseline === 0) return;
38110
39071
  for (let iter = 0; iter < MAX_ITER; iter++) {
38111
39072
  let improved = false;
@@ -38123,7 +39084,7 @@ function reduceCrossings2(g, direction) {
38123
39084
  const bv = bn[slotAxis];
38124
39085
  an[slotAxis] = bv;
38125
39086
  bn[slotAxis] = av;
38126
- const after = countCrossings2();
39087
+ const after = countCrossings();
38127
39088
  if (after < baseline) {
38128
39089
  baseline = after;
38129
39090
  improved = true;
@@ -38144,7 +39105,7 @@ function reduceCrossings2(g, direction) {
38144
39105
  data.points = smoothEdge(src, tgt, direction);
38145
39106
  }
38146
39107
  }
38147
- function segmentsCross2(a1, a2, b1, b2) {
39108
+ function segmentsCross(a1, a2, b1, b2) {
38148
39109
  const ccw = (p, q, r) => (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x);
38149
39110
  const d1 = ccw(b1, b2, a1);
38150
39111
  const d2 = ccw(b1, b2, a2);
@@ -47685,10 +48646,10 @@ function tierBand(maxSpanDeg) {
47685
48646
  }
47686
48647
  function labelBudget(width, height, band) {
47687
48648
  const bandCap = {
47688
- world: 6,
47689
- continental: 5,
47690
- regional: 4,
47691
- local: 3
48649
+ world: 7,
48650
+ continental: 6,
48651
+ regional: 5,
48652
+ local: 4
47692
48653
  };
47693
48654
  const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
47694
48655
  return Math.max(0, Math.min(area2, bandCap[band]));
@@ -47821,8 +48782,11 @@ function placeContextLabels(args) {
47821
48782
  color: waterColor,
47822
48783
  fontSize: FONT,
47823
48784
  // water names keep the base font (no footprint to scale on)
47824
- // Water before any country (×1000), then by tier, then kind, then name.
47825
- sort: tier * 10 + KIND_ORDER[kind]
48785
+ // Orientation-value bands (lower = earlier): MAJOR water (oceans + major
48786
+ // seas, tier ≤ 1) leads at `tier*10+kind` (0..~16); MINOR water (tier ≥ 2:
48787
+ // smaller seas, bays, gulfs, straits) drops to band 2 (2000+) — BELOW the
48788
+ // country band (1000+), so a big country outranks a minor basin.
48789
+ sort: (tier <= 1 ? 0 : 2e3) + tier * 10 + KIND_ORDER[kind]
47826
48790
  });
47827
48791
  }
47828
48792
  const ranked = countries.map((c) => {
@@ -47835,7 +48799,7 @@ function placeContextLabels(args) {
47835
48799
  let ci = 0;
47836
48800
  for (const r of ranked) {
47837
48801
  const { c, w, h, area: area2 } = r;
47838
- if (w > width * 0.66 || h > height * 0.66) continue;
48802
+ if (!c.curatedAnchor && w > width * 0.66 && h > height * 0.66) continue;
47839
48803
  if (!insideViewport(c.anchor, width, height)) continue;
47840
48804
  const sizeFrac = Math.sqrt(area2) / canvasLinear;
47841
48805
  const t = Math.min(
@@ -47860,15 +48824,23 @@ function placeContextLabels(args) {
47860
48824
  letterSpacing: 0,
47861
48825
  color,
47862
48826
  fontSize,
47863
- // Always after every water body (+1e6); larger area = earlier.
47864
- sort: 1e6 + ci++
48827
+ // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
48828
+ // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
48829
+ // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
48830
+ // Campeche) yet never displaces an ocean. Larger area = earlier within the
48831
+ // band (`ci` is the area-desc rank index).
48832
+ sort: 1e3 + ci++
47865
48833
  });
47866
48834
  }
47867
48835
  candidates.sort((a, b) => a.sort - b.sort);
47868
48836
  const placed = [];
47869
48837
  const placedRects = [];
48838
+ const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
48839
+ const waterCap = budget - Math.min(2, countryCount);
48840
+ let waterPlaced = 0;
47870
48841
  for (const cand of candidates) {
47871
48842
  if (placed.length >= budget) break;
48843
+ if (cand.italic && waterPlaced >= waterCap) continue;
47872
48844
  const rect = rectAround(
47873
48845
  cand.cx,
47874
48846
  cand.cy,
@@ -47895,6 +48867,7 @@ function placeContextLabels(args) {
47895
48867
  if (collides(rect)) continue;
47896
48868
  if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
47897
48869
  placedRects.push(rect);
48870
+ if (cand.italic) waterPlaced++;
47898
48871
  placed.push({
47899
48872
  x: cand.cx,
47900
48873
  y: cand.cy,
@@ -49868,16 +50841,16 @@ function layoutMap(resolved, data, size, opts) {
49868
50841
  countryCandidates.push({
49869
50842
  name: f.properties?.name ?? iso,
49870
50843
  bbox: [x0, y0, x1, y1],
49871
- anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
50844
+ anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
50845
+ curatedAnchor: !!anchorLngLat
49872
50846
  });
49873
50847
  }
49874
50848
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
49875
50849
  (id) => id.startsWith("US-")
49876
50850
  );
49877
50851
  if (usLayer && framedStateContainers) {
49878
- const containerSet = new Set(resolved.poiFrameContainers);
49879
50852
  for (const [iso, f] of usLayer) {
49880
- if (containerSet.has(iso) || regionById.has(iso)) continue;
50853
+ if (regionById.has(iso)) continue;
49881
50854
  const viewF = cullFeatureToView(f);
49882
50855
  if (!viewF) continue;
49883
50856
  const b = path.bounds(viewF);
@@ -50005,8 +50978,15 @@ var init_layout15 = __esm({
50005
50978
  W_MAX = 8;
50006
50979
  FONT2 = 11;
50007
50980
  WORLD_LABEL_ANCHORS = {
50008
- US: [-98.5, 39.5]
50981
+ US: [-98.5, 39.5],
50009
50982
  // CONUS geographic centre (near Lebanon, Kansas)
50983
+ // Russia crosses the antimeridian (Chukotka at ~170°W), so on a non-global
50984
+ // (e.g. Europe) projection its geometry smears across the whole frame and the
50985
+ // area-weighted centroid lands mid-map (over Europe) — useless as a label
50986
+ // anchor. Pin it to European Russia (~Volga) so a Europe view labels visible
50987
+ // western Russia on its eastern margin; on a world view this still sits over
50988
+ // Russian land. (See the curated-anchor smear-gate bypass in context-labels.)
50989
+ RU: [45, 58]
50010
50990
  };
50011
50991
  MAX_CLUSTER_EXTENT_FACTOR = 0.18;
50012
50992
  MAX_COLUMN_ROWS = 7;
@@ -60716,7 +61696,7 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
60716
61696
 
60717
61697
  // src/auto/index.ts
60718
61698
  init_safe_href();
60719
- var VERSION = "0.27.0";
61699
+ var VERSION = "0.28.0";
60720
61700
  var DEFAULTS = {
60721
61701
  theme: "auto",
60722
61702
  palette: "slate",