@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/index.js CHANGED
@@ -27618,12 +27618,1344 @@ var init_renderer6 = __esm({
27618
27618
  }
27619
27619
  });
27620
27620
 
27621
+ // src/boxes-and-lines/layout-layered.ts
27622
+ function rng(s) {
27623
+ return () => {
27624
+ s |= 0;
27625
+ s = s + 1831565813 | 0;
27626
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
27627
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
27628
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
27629
+ };
27630
+ }
27631
+ function layeredCandidates(parsed, sizes, opts) {
27632
+ if (parsed.groups.length > 0) return [];
27633
+ const nCount = parsed.nodes.length;
27634
+ if (nCount < 3 || nCount > 40) return [];
27635
+ const isTB = parsed.direction === "TB";
27636
+ const nodesep = opts?.nodesep ?? NODESEP;
27637
+ const ranksep = opts?.ranksep ?? RANKSEP;
27638
+ const backEdgeSide = opts?.backEdgeSide ?? "nearest";
27639
+ const restarts = opts?.restarts ?? (nCount <= 14 ? 28 : nCount <= 25 ? 14 : 6);
27640
+ const keepK = opts?.keepK ?? 3;
27641
+ const labels = parsed.nodes.map((n) => n.label);
27642
+ const labelSet = new Set(labels);
27643
+ const edges = parsed.edges.map((e, i) => ({ e, i })).filter(
27644
+ ({ e }) => labelSet.has(e.source) && labelSet.has(e.target) && e.source !== e.target
27645
+ );
27646
+ if (edges.length === 0) return [];
27647
+ const adj = /* @__PURE__ */ new Map();
27648
+ for (const l of labels) adj.set(l, []);
27649
+ for (const { e, i } of edges)
27650
+ adj.get(e.source).push({ to: e.target, idx: i });
27651
+ const reversed = /* @__PURE__ */ new Set();
27652
+ const state = /* @__PURE__ */ new Map();
27653
+ for (const l of labels) state.set(l, 0);
27654
+ const dfs = (u) => {
27655
+ state.set(u, 1);
27656
+ for (const { to, idx } of adj.get(u)) {
27657
+ const st = state.get(to);
27658
+ if (st === 1)
27659
+ reversed.add(idx);
27660
+ else if (st === 0) dfs(to);
27661
+ }
27662
+ state.set(u, 2);
27663
+ };
27664
+ for (const l of labels) if (state.get(l) === 0) dfs(l);
27665
+ const dag = edges.map(
27666
+ ({ 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 }
27667
+ );
27668
+ const outDag = /* @__PURE__ */ new Map();
27669
+ const inDeg = /* @__PURE__ */ new Map();
27670
+ for (const l of labels) {
27671
+ outDag.set(l, []);
27672
+ inDeg.set(l, 0);
27673
+ }
27674
+ for (const d of dag) {
27675
+ outDag.get(d.from).push(d.to);
27676
+ inDeg.set(d.to, (inDeg.get(d.to) ?? 0) + 1);
27677
+ }
27678
+ const rank = /* @__PURE__ */ new Map();
27679
+ for (const l of labels) rank.set(l, 0);
27680
+ const queue = labels.filter((l) => (inDeg.get(l) ?? 0) === 0);
27681
+ const indeg2 = new Map(inDeg);
27682
+ const topo = [];
27683
+ while (queue.length) {
27684
+ const u = queue.shift();
27685
+ topo.push(u);
27686
+ for (const v of outDag.get(u)) {
27687
+ rank.set(v, Math.max(rank.get(v), rank.get(u) + 1));
27688
+ indeg2.set(v, indeg2.get(v) - 1);
27689
+ if (indeg2.get(v) === 0) queue.push(v);
27690
+ }
27691
+ }
27692
+ if (topo.length !== labels.length) return [];
27693
+ const maxRank = Math.max(...labels.map((l) => rank.get(l)));
27694
+ const node = /* @__PURE__ */ new Map();
27695
+ for (const n of parsed.nodes) {
27696
+ const s = sizes.get(n.label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
27697
+ node.set(n.label, {
27698
+ id: n.label,
27699
+ dummy: false,
27700
+ rank: rank.get(n.label),
27701
+ cross: 0,
27702
+ thick: isTB ? s.width : s.height,
27703
+ depth: isTB ? s.height : s.width,
27704
+ realW: s.width,
27705
+ realH: s.height
27706
+ });
27707
+ }
27708
+ const segs = [];
27709
+ const chains = [];
27710
+ const backEdges = [];
27711
+ for (const d of dag) {
27712
+ if (d.rev) {
27713
+ backEdges.push({ edgeIdx: d.idx, src: d.to, tgt: d.from });
27714
+ continue;
27715
+ }
27716
+ const r0 = rank.get(d.from);
27717
+ const r1 = rank.get(d.to);
27718
+ const dagChain = [d.from];
27719
+ let prev = d.from;
27720
+ for (let r = r0 + 1; r < r1; r++) {
27721
+ const id = `__d${d.idx}_${r}`;
27722
+ node.set(id, {
27723
+ id,
27724
+ dummy: true,
27725
+ rank: r,
27726
+ cross: 0,
27727
+ thick: DUMMY_THICK,
27728
+ depth: 0,
27729
+ realW: 0,
27730
+ realH: 0
27731
+ });
27732
+ segs.push({ a: prev, b: id });
27733
+ dagChain.push(id);
27734
+ prev = id;
27735
+ }
27736
+ segs.push({ a: prev, b: d.to });
27737
+ dagChain.push(d.to);
27738
+ chains.push({
27739
+ edgeIdx: d.idx,
27740
+ chain: d.rev ? dagChain.slice().reverse() : dagChain
27741
+ });
27742
+ }
27743
+ const ranks = Array.from({ length: maxRank + 1 }, () => []);
27744
+ for (const n of node.values()) ranks[n.rank].push(n.id);
27745
+ const up = /* @__PURE__ */ new Map();
27746
+ const down = /* @__PURE__ */ new Map();
27747
+ for (const id of node.keys()) {
27748
+ up.set(id, []);
27749
+ down.set(id, []);
27750
+ }
27751
+ for (const s of segs) {
27752
+ down.get(s.a).push(s.b);
27753
+ up.get(s.b).push(s.a);
27754
+ }
27755
+ const orderOf = /* @__PURE__ */ new Map();
27756
+ const applyOrder = (ord) => {
27757
+ for (const id of node.keys()) orderOf.set(id, ord.get(id));
27758
+ };
27759
+ const gapCrossings = (r) => {
27760
+ const lower = ranks[r + 1];
27761
+ if (!lower) return 0;
27762
+ const es = [];
27763
+ for (const a of ranks[r]) {
27764
+ const pa = orderOf.get(a);
27765
+ for (const b of down.get(a)) es.push({ p: pa, q: orderOf.get(b) });
27766
+ }
27767
+ let c = 0;
27768
+ for (let i = 0; i < es.length; i++)
27769
+ for (let j = i + 1; j < es.length; j++) {
27770
+ const A = es[i], B = es[j];
27771
+ if ((A.p - B.p) * (A.q - B.q) < 0) c++;
27772
+ }
27773
+ return c;
27774
+ };
27775
+ const totalCrossings = () => {
27776
+ let c = 0;
27777
+ for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
27778
+ return c;
27779
+ };
27780
+ const median = (vals) => {
27781
+ if (vals.length === 0) return -1;
27782
+ vals.sort((x, y) => x - y);
27783
+ const m = Math.floor(vals.length / 2);
27784
+ if (vals.length % 2 === 1) return vals[m];
27785
+ if (vals.length === 2) return (vals[0] + vals[1]) / 2;
27786
+ const left = vals[m - 1] - vals[0];
27787
+ const right = vals[vals.length - 1] - vals[m];
27788
+ return left + right === 0 ? (vals[m - 1] + vals[m]) / 2 : (vals[m - 1] * right + vals[m] * left) / (left + right);
27789
+ };
27790
+ const wmedianRank = (r, useUp) => {
27791
+ const ids = ranks[r];
27792
+ const neigh = useUp ? up : down;
27793
+ const med = /* @__PURE__ */ new Map();
27794
+ for (const id of ids)
27795
+ med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
27796
+ const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
27797
+ const out = new Array(ids.length).fill(null);
27798
+ for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
27799
+ let mi = 0;
27800
+ for (let slot = 0; slot < out.length; slot++)
27801
+ if (out[slot] === null) out[slot] = movable[mi++];
27802
+ const final = out;
27803
+ final.forEach((id, i) => orderOf.set(id, i));
27804
+ ranks[r] = final;
27805
+ };
27806
+ const transpose = () => {
27807
+ let improved = true;
27808
+ let guard = 0;
27809
+ while (improved && guard++ < 12) {
27810
+ improved = false;
27811
+ for (let r = 0; r <= maxRank; r++) {
27812
+ const ids = ranks[r];
27813
+ for (let i = 0; i < ids.length - 1; i++) {
27814
+ const before = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
27815
+ const a = ids[i], b = ids[i + 1];
27816
+ ids[i] = b;
27817
+ ids[i + 1] = a;
27818
+ orderOf.set(b, i);
27819
+ orderOf.set(a, i + 1);
27820
+ const after = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
27821
+ if (after < before) {
27822
+ improved = true;
27823
+ } else {
27824
+ ids[i] = a;
27825
+ ids[i + 1] = b;
27826
+ orderOf.set(a, i);
27827
+ orderOf.set(b, i + 1);
27828
+ }
27829
+ }
27830
+ }
27831
+ }
27832
+ };
27833
+ const initOrder = (seed) => {
27834
+ const r = seed === 0 ? null : rng(seed);
27835
+ for (let rr = 0; rr <= maxRank; rr++) {
27836
+ const ids = ranks[rr].slice();
27837
+ if (r) {
27838
+ for (let i = ids.length - 1; i > 0; i--) {
27839
+ const j = Math.floor(r() * (i + 1));
27840
+ [ids[i], ids[j]] = [ids[j], ids[i]];
27841
+ }
27842
+ }
27843
+ ranks[rr] = ids;
27844
+ ids.forEach((id, i) => orderOf.set(id, i));
27845
+ }
27846
+ };
27847
+ const found = [];
27848
+ for (let s = 0; s < restarts; s++) {
27849
+ initOrder(s);
27850
+ let best = totalCrossings();
27851
+ let bestSnap = new Map(orderOf);
27852
+ for (let iter = 0; iter < 8; iter++) {
27853
+ const down1 = iter % 2 === 0;
27854
+ if (down1) for (let r = 1; r <= maxRank; r++) wmedianRank(r, true);
27855
+ else for (let r = maxRank - 1; r >= 0; r--) wmedianRank(r, false);
27856
+ transpose();
27857
+ const c = totalCrossings();
27858
+ if (c < best) {
27859
+ best = c;
27860
+ bestSnap = new Map(orderOf);
27861
+ }
27862
+ if (c === 0) break;
27863
+ }
27864
+ applyOrder(bestSnap);
27865
+ for (let r = 0; r <= maxRank; r++)
27866
+ ranks[r].sort((a, b) => orderOf.get(a) - orderOf.get(b));
27867
+ const sig = ranks.map((r) => r.join(",")).join("|");
27868
+ if (!found.some((f) => f.sig === sig))
27869
+ found.push({ sig, cross: best, order: new Map(bestSnap) });
27870
+ }
27871
+ found.sort((a, b) => a.cross - b.cross);
27872
+ const keep = found.slice(0, keepK);
27873
+ if (keep.length === 0) return [];
27874
+ const results = [];
27875
+ for (const cand of keep) {
27876
+ applyOrder(cand.order);
27877
+ const rk = Array.from({ length: maxRank + 1 }, () => []);
27878
+ for (const n of node.values()) rk[n.rank].push(n.id);
27879
+ for (let r = 0; r <= maxRank; r++)
27880
+ rk[r].sort((a, b) => cand.order.get(a) - cand.order.get(b));
27881
+ results.push(realize(rk));
27882
+ }
27883
+ return results;
27884
+ function realize(rk) {
27885
+ const bandDepth = rk.map(
27886
+ (ids) => Math.max(0, ...ids.map((id) => node.get(id).depth))
27887
+ );
27888
+ const bandCenter = [];
27889
+ let acc = MARGIN3;
27890
+ for (let r = 0; r <= maxRank; r++) {
27891
+ bandCenter[r] = acc + bandDepth[r] / 2;
27892
+ acc += bandDepth[r] + ranksep;
27893
+ }
27894
+ for (let r = 0; r <= maxRank; r++) {
27895
+ let x = MARGIN3;
27896
+ for (const id of rk[r]) {
27897
+ const n = node.get(id);
27898
+ n.cross = x + n.thick / 2;
27899
+ x += n.thick + nodesep;
27900
+ }
27901
+ }
27902
+ const ITER = 18;
27903
+ for (let it = 0; it < ITER; it++) {
27904
+ const downPass = it % 2 === 0;
27905
+ const order = downPass ? Array.from({ length: maxRank + 1 }, (_, i) => i) : Array.from({ length: maxRank + 1 }, (_, i) => maxRank - i);
27906
+ for (const r of order) {
27907
+ const ids = rk[r];
27908
+ const desired = ids.map((id) => {
27909
+ const n = node.get(id);
27910
+ let sw = 0, sx2 = 0;
27911
+ for (const nb of up.get(id)) {
27912
+ const w = weight(n, node.get(nb));
27913
+ sw += w;
27914
+ sx2 += w * node.get(nb).cross;
27915
+ }
27916
+ for (const nb of down.get(id)) {
27917
+ const w = weight(n, node.get(nb));
27918
+ sw += w;
27919
+ sx2 += w * node.get(nb).cross;
27920
+ }
27921
+ return sw > 0 ? sx2 / sw : n.cross;
27922
+ });
27923
+ placeWithSeparation(ids, desired);
27924
+ }
27925
+ }
27926
+ let minC = Infinity;
27927
+ for (const n of node.values()) minC = Math.min(minC, n.cross - n.thick / 2);
27928
+ const shift = MARGIN3 - minC;
27929
+ for (const n of node.values()) n.cross += shift;
27930
+ const coord = (n) => isTB ? { x: n.cross, y: bandCenter[n.rank] } : { x: bandCenter[n.rank], y: n.cross };
27931
+ const layoutNodes = parsed.nodes.map((pn) => {
27932
+ const n = node.get(pn.label);
27933
+ const c = coord(n);
27934
+ return {
27935
+ label: pn.label,
27936
+ x: c.x,
27937
+ y: c.y,
27938
+ width: n.realW,
27939
+ height: n.realH
27940
+ };
27941
+ });
27942
+ const chainById = /* @__PURE__ */ new Map();
27943
+ for (const ch of chains) chainById.set(ch.edgeIdx, ch.chain);
27944
+ let minCross = Infinity, maxCross = -Infinity;
27945
+ for (const n of node.values()) {
27946
+ if (n.dummy) continue;
27947
+ minCross = Math.min(minCross, n.cross - n.thick / 2);
27948
+ maxCross = Math.max(maxCross, n.cross + n.thick / 2);
27949
+ }
27950
+ const GAP = 34;
27951
+ const LANE = 28;
27952
+ const sideOf = (b) => {
27953
+ if (backEdgeSide === "left") return -1;
27954
+ if (backEdgeSide === "right") return 1;
27955
+ const d = node.get(b.src).cross - node.get(b.tgt).cross;
27956
+ return d >= 0 ? 1 : -1;
27957
+ };
27958
+ const backSorted = backEdges.map((b) => ({
27959
+ ...b,
27960
+ side: sideOf(b),
27961
+ span: Math.abs(node.get(b.src).rank - node.get(b.tgt).rank)
27962
+ })).sort((a, b) => a.side - b.side || a.span - b.span);
27963
+ const sideCounts = /* @__PURE__ */ new Map();
27964
+ for (const b of backSorted)
27965
+ sideCounts.set(b.side, (sideCounts.get(b.side) ?? 0) + 1);
27966
+ const laneCounter = /* @__PURE__ */ new Map();
27967
+ const backPoints = /* @__PURE__ */ new Map();
27968
+ const faceLim = (depth) => Math.max(0, depth / 2 - 6);
27969
+ const hubIndex = /* @__PURE__ */ new Map();
27970
+ const hubSize = /* @__PURE__ */ new Map();
27971
+ {
27972
+ const counter = /* @__PURE__ */ new Map();
27973
+ for (const b of backSorted) {
27974
+ const key = `${b.side}|${b.tgt}`;
27975
+ const idx = counter.get(key) ?? 0;
27976
+ counter.set(key, idx + 1);
27977
+ hubIndex.set(b.edgeIdx, idx);
27978
+ }
27979
+ for (const [key, v] of counter) hubSize.set(key, v);
27980
+ }
27981
+ for (const b of backSorted) {
27982
+ const s = b.side;
27983
+ const k = laneCounter.get(s) ?? 0;
27984
+ laneCounter.set(s, k + 1);
27985
+ const K = sideCounts.get(s);
27986
+ const src = node.get(b.src);
27987
+ const tgt = node.get(b.tgt);
27988
+ const laneC = s > 0 ? maxCross + GAP + k * LANE : minCross - GAP - k * LANE;
27989
+ const srcCtr = bandCenter[src.rank];
27990
+ const tgtCtr = bandCenter[tgt.rank];
27991
+ const frac = K > 1 ? (k + 1) / (K + 1) : 0;
27992
+ const srcR = srcCtr + frac * faceLim(src.depth);
27993
+ const tgtR = tgtCtr - frac * faceLim(tgt.depth);
27994
+ const m = (c, r) => isTB ? { x: c, y: r } : { x: r, y: c };
27995
+ const off = 10;
27996
+ const srcEdgeC = src.cross + s * (src.thick / 2 + off);
27997
+ const tgtEdgeC = tgt.cross + s * (tgt.thick / 2 + off);
27998
+ const hubK = hubIndex.get(b.edgeIdx);
27999
+ const hubN = hubSize.get(`${s}|${b.tgt}`);
28000
+ if (hubN > 1 && hubK > 0) {
28001
+ const tgtTop = tgtCtr - (tgt.depth / 2 + GAP + (hubK - 1) * LANE);
28002
+ const entryCross = tgt.cross + s * faceLim(tgt.thick) * (hubK / hubN);
28003
+ backPoints.set(b.edgeIdx, [
28004
+ m(src.cross, srcCtr),
28005
+ m(srcEdgeC, srcR),
28006
+ m(laneC, srcR),
28007
+ m(laneC, tgtTop),
28008
+ m(entryCross, tgtTop),
28009
+ m(tgt.cross, tgtCtr)
28010
+ ]);
28011
+ } else {
28012
+ backPoints.set(b.edgeIdx, [
28013
+ m(src.cross, srcCtr),
28014
+ m(srcEdgeC, srcR),
28015
+ m(laneC, srcR),
28016
+ m(laneC, tgtR),
28017
+ m(tgtEdgeC, tgtR),
28018
+ m(tgt.cross, tgtCtr)
28019
+ ]);
28020
+ }
28021
+ }
28022
+ const layoutEdges = [];
28023
+ for (let i = 0; i < parsed.edges.length; i++) {
28024
+ const e = parsed.edges[i];
28025
+ if (!labelSet.has(e.source) || !labelSet.has(e.target)) continue;
28026
+ let points;
28027
+ if (e.source === e.target) {
28028
+ const c = coord(node.get(e.source));
28029
+ points = [c, c];
28030
+ } else if (backPoints.has(i)) {
28031
+ points = backPoints.get(i);
28032
+ } else {
28033
+ const chain = chainById.get(i);
28034
+ if (!chain) continue;
28035
+ points = chain.map((id) => coord(node.get(id)));
28036
+ }
28037
+ layoutEdges.push({
28038
+ source: e.source,
28039
+ target: e.target,
28040
+ ...e.label !== void 0 && { label: e.label },
28041
+ bidirectional: e.bidirectional,
28042
+ lineNumber: e.lineNumber,
28043
+ points,
28044
+ yOffset: 0,
28045
+ parallelCount: 1,
28046
+ metadata: e.metadata
28047
+ });
28048
+ }
28049
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
28050
+ for (const n of layoutNodes) {
28051
+ minX = Math.min(minX, n.x - n.width / 2);
28052
+ minY = Math.min(minY, n.y - n.height / 2);
28053
+ maxX = Math.max(maxX, n.x + n.width / 2);
28054
+ maxY = Math.max(maxY, n.y + n.height / 2);
28055
+ }
28056
+ for (const e of layoutEdges)
28057
+ for (const p of e.points) {
28058
+ minX = Math.min(minX, p.x);
28059
+ minY = Math.min(minY, p.y);
28060
+ maxX = Math.max(maxX, p.x);
28061
+ maxY = Math.max(maxY, p.y);
28062
+ }
28063
+ const sx = MARGIN3 - minX;
28064
+ const sy = MARGIN3 - minY;
28065
+ const shifted = sx !== 0 || sy !== 0 ? {
28066
+ nodes: layoutNodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })),
28067
+ edges: layoutEdges.map((e) => ({
28068
+ ...e,
28069
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
28070
+ }))
28071
+ } : { nodes: layoutNodes, edges: layoutEdges };
28072
+ return {
28073
+ nodes: shifted.nodes,
28074
+ edges: shifted.edges,
28075
+ groups: [],
28076
+ width: maxX + sx + MARGIN3,
28077
+ height: maxY + sy + MARGIN3
28078
+ };
28079
+ }
28080
+ function weight(a, b) {
28081
+ if (a.dummy && b.dummy) return W_DUMMY_DUMMY;
28082
+ if (a.dummy || b.dummy) return W_REAL_DUMMY;
28083
+ return W_REAL_REAL;
28084
+ }
28085
+ function placeWithSeparation(ids, desired) {
28086
+ const n = ids.length;
28087
+ if (n === 0) return;
28088
+ const half = ids.map((id) => node.get(id).thick / 2);
28089
+ const off = [0];
28090
+ for (let i = 1; i < n; i++)
28091
+ off[i] = off[i - 1] + half[i - 1] + nodesep + half[i];
28092
+ const d = desired.map((v, i) => v - off[i]);
28093
+ const blocks = [];
28094
+ for (let i = 0; i < n; i++) {
28095
+ let b = { pos: d[i], count: 1, sumOffset: d[i], first: i };
28096
+ while (blocks.length && blocks[blocks.length - 1].pos >= b.pos) {
28097
+ const prev = blocks.pop();
28098
+ const count = prev.count + b.count;
28099
+ const sumOffset = prev.sumOffset + b.sumOffset;
28100
+ b = { pos: sumOffset / count, count, sumOffset, first: prev.first };
28101
+ }
28102
+ blocks.push(b);
28103
+ }
28104
+ const xs = new Array(n);
28105
+ for (const b of blocks)
28106
+ for (let k = 0; k < b.count; k++) xs[b.first + k] = b.pos;
28107
+ for (let i = 0; i < n; i++) node.get(ids[i]).cross = xs[i] + off[i];
28108
+ }
28109
+ }
28110
+ var NODESEP, RANKSEP, DUMMY_THICK, MARGIN3, W_REAL_REAL, W_REAL_DUMMY, W_DUMMY_DUMMY;
28111
+ var init_layout_layered = __esm({
28112
+ "src/boxes-and-lines/layout-layered.ts"() {
28113
+ "use strict";
28114
+ init_layout5();
28115
+ NODESEP = 50;
28116
+ RANKSEP = 60;
28117
+ DUMMY_THICK = 10;
28118
+ MARGIN3 = 40;
28119
+ W_REAL_REAL = 1;
28120
+ W_REAL_DUMMY = 2;
28121
+ W_DUMMY_DUMMY = 8;
28122
+ }
28123
+ });
28124
+
28125
+ // src/boxes-and-lines/layout-search.ts
28126
+ var layout_search_exports = {};
28127
+ __export(layout_search_exports, {
28128
+ countEdgeNearMiss: () => countEdgeNearMiss,
28129
+ countEdgeNodePierces: () => countEdgeNodePierces,
28130
+ countEdgeOverlaps: () => countEdgeOverlaps,
28131
+ countGroupOverlaps: () => countGroupOverlaps,
28132
+ countSplineCrossings: () => countSplineCrossings,
28133
+ deroutePierces: () => deroutePierces,
28134
+ detectEdgeNodePierces: () => detectEdgeNodePierces,
28135
+ detectEdgeOverlaps: () => detectEdgeOverlaps,
28136
+ detectGroupOverlaps: () => detectGroupOverlaps,
28137
+ layoutBoxesAndLinesSearch: () => layoutBoxesAndLinesSearch,
28138
+ separateGroupBands: () => separateGroupBands
28139
+ });
28140
+ import dagre4 from "@dagrejs/dagre";
28141
+ import { line as d3line, curveBasis as curveBasis5 } from "d3-shape";
28142
+ function rng2(s) {
28143
+ return () => {
28144
+ s |= 0;
28145
+ s = s + 1831565813 | 0;
28146
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
28147
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
28148
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
28149
+ };
28150
+ }
28151
+ function shuffle(a, r) {
28152
+ const x = a.slice();
28153
+ for (let i = x.length - 1; i > 0; i--) {
28154
+ const j = Math.floor(r() * (i + 1));
28155
+ [x[i], x[j]] = [x[j], x[i]];
28156
+ }
28157
+ return x;
28158
+ }
28159
+ function flatten(d) {
28160
+ const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
28161
+ const pts = [];
28162
+ let i = 0, cx = 0, cy = 0, cmd = "";
28163
+ const num = () => parseFloat(toks[i++]);
28164
+ const samp = (p0, c1, c2, p1) => {
28165
+ for (let t = 0; t <= 1; t += 0.12) {
28166
+ const u = 1 - t;
28167
+ if (c2)
28168
+ pts.push({
28169
+ x: u * u * u * p0.x + 3 * u * u * t * c1.x + 3 * u * t * t * c2.x + t * t * t * p1.x,
28170
+ y: u * u * u * p0.y + 3 * u * u * t * c1.y + 3 * u * t * t * c2.y + t * t * t * p1.y
28171
+ });
28172
+ else
28173
+ pts.push({
28174
+ x: u * u * p0.x + 2 * u * t * c1.x + t * t * p1.x,
28175
+ y: u * u * p0.y + 2 * u * t * c1.y + t * t * p1.y
28176
+ });
28177
+ }
28178
+ };
28179
+ while (i < toks.length) {
28180
+ const tk = toks[i];
28181
+ if (/[MLQC]/i.test(tk)) {
28182
+ cmd = tk;
28183
+ i++;
28184
+ }
28185
+ if (cmd === "M" || cmd === "L") {
28186
+ const x = num(), y = num();
28187
+ pts.push({ x, y });
28188
+ cx = x;
28189
+ cy = y;
28190
+ } else if (cmd === "Q") {
28191
+ const c1 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
28192
+ samp({ x: cx, y: cy }, c1, null, p1);
28193
+ cx = p1.x;
28194
+ cy = p1.y;
28195
+ } else if (cmd === "C") {
28196
+ const c1 = { x: num(), y: num() }, c2 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
28197
+ samp({ x: cx, y: cy }, c1, c2, p1);
28198
+ cx = p1.x;
28199
+ cy = p1.y;
28200
+ } else i++;
28201
+ }
28202
+ return pts;
28203
+ }
28204
+ function segPoint(p1, p2, p3, p4) {
28205
+ const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
28206
+ if (Math.abs(den) < 1e-9) return null;
28207
+ 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;
28208
+ 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;
28209
+ }
28210
+ function countSplineCrossings(layout) {
28211
+ const center = /* @__PURE__ */ new Map();
28212
+ for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
28213
+ for (const g of layout.groups)
28214
+ if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
28215
+ const polys = layout.edges.map((e) => {
28216
+ const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
28217
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28218
+ for (const p of pts) {
28219
+ if (p.x < x0) x0 = p.x;
28220
+ if (p.x > x1) x1 = p.x;
28221
+ if (p.y < y0) y0 = p.y;
28222
+ if (p.y > y1) y1 = p.y;
28223
+ }
28224
+ return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
28225
+ });
28226
+ const R = 34;
28227
+ let total = 0;
28228
+ for (let a = 0; a < polys.length; a++)
28229
+ for (let b = a + 1; b < polys.length; b++) {
28230
+ const A = polys[a], B = polys[b];
28231
+ if (A.pts.length < 2 || B.pts.length < 2) continue;
28232
+ if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
28233
+ const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
28234
+ const hits = [];
28235
+ for (let i = 1; i < A.pts.length; i++)
28236
+ for (let j = 1; j < B.pts.length; j++) {
28237
+ const p = segPoint(
28238
+ A.pts[i - 1],
28239
+ A.pts[i],
28240
+ B.pts[j - 1],
28241
+ B.pts[j]
28242
+ );
28243
+ if (!p) continue;
28244
+ if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
28245
+ continue;
28246
+ if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
28247
+ hits.push(p);
28248
+ }
28249
+ total += hits.length;
28250
+ }
28251
+ return total;
28252
+ }
28253
+ function pointSegDist(p, a, b) {
28254
+ const dx = b.x - a.x, dy = b.y - a.y;
28255
+ const len2 = dx * dx + dy * dy;
28256
+ if (len2 < 1e-9) return Math.hypot(p.x - a.x, p.y - a.y);
28257
+ let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
28258
+ t = Math.max(0, Math.min(1, t));
28259
+ return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));
28260
+ }
28261
+ function distToPoly(p, poly) {
28262
+ let m = Infinity;
28263
+ for (let i = 1; i < poly.length; i++)
28264
+ m = Math.min(m, pointSegDist(p, poly[i - 1], poly[i]));
28265
+ return m;
28266
+ }
28267
+ function pointRectDist(p, r) {
28268
+ const dx = Math.max(r.x - r.w / 2 - p.x, 0, p.x - (r.x + r.w / 2));
28269
+ const dy = Math.max(r.y - r.h / 2 - p.y, 0, p.y - (r.y + r.h / 2));
28270
+ return Math.hypot(dx, dy);
28271
+ }
28272
+ function detectEdgeOverlaps(layout, opts) {
28273
+ const dist = opts?.dist ?? 8;
28274
+ const minLen = opts?.minLen ?? 16;
28275
+ const nodeClear = opts?.nodeClear ?? 12;
28276
+ const rect = /* @__PURE__ */ new Map();
28277
+ for (const n of layout.nodes)
28278
+ rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
28279
+ for (const g of layout.groups)
28280
+ if (g.collapsed)
28281
+ rect.set("__group_" + g.label, {
28282
+ x: g.x,
28283
+ y: g.y,
28284
+ w: g.width,
28285
+ h: g.height
28286
+ });
28287
+ const polys = layout.edges.map((e) => {
28288
+ const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
28289
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28290
+ for (const p of pts) {
28291
+ if (p.x < x0) x0 = p.x;
28292
+ if (p.x > x1) x1 = p.x;
28293
+ if (p.y < y0) y0 = p.y;
28294
+ if (p.y > y1) y1 = p.y;
28295
+ }
28296
+ return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
28297
+ });
28298
+ const runs = [];
28299
+ for (let a = 0; a < polys.length; a++)
28300
+ for (let b = a + 1; b < polys.length; b++) {
28301
+ const A = polys[a], B = polys[b];
28302
+ if (A.pts.length < 2 || B.pts.length < 2) continue;
28303
+ if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
28304
+ continue;
28305
+ const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
28306
+ let run = [];
28307
+ let runLen = 0;
28308
+ const flush = () => {
28309
+ if (runLen >= minLen && run.length >= 2)
28310
+ runs.push({
28311
+ mid: run[Math.floor(run.length / 2)],
28312
+ length: runLen,
28313
+ pts: run.slice()
28314
+ });
28315
+ run = [];
28316
+ runLen = 0;
28317
+ };
28318
+ for (const p of A.pts) {
28319
+ const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
28320
+ const covered = !nearShared && distToPoly(p, B.pts) < dist;
28321
+ if (covered) {
28322
+ if (run.length)
28323
+ runLen += Math.hypot(
28324
+ p.x - run[run.length - 1].x,
28325
+ p.y - run[run.length - 1].y
28326
+ );
28327
+ run.push(p);
28328
+ } else flush();
28329
+ }
28330
+ flush();
28331
+ }
28332
+ return runs;
28333
+ }
28334
+ function countEdgeOverlaps(layout, opts) {
28335
+ return detectEdgeOverlaps(layout, opts).length;
28336
+ }
28337
+ function detectEdgeNodePierces(layout, opts) {
28338
+ const inset = opts?.inset ?? 6;
28339
+ const minPts = opts?.minPts ?? 2;
28340
+ const rects = [];
28341
+ for (const n of layout.nodes)
28342
+ rects.push({ key: n.label, x: n.x, y: n.y, w: n.width, h: n.height });
28343
+ for (const g of layout.groups)
28344
+ if (g.collapsed)
28345
+ rects.push({
28346
+ key: "__group_" + g.label,
28347
+ x: g.x,
28348
+ y: g.y,
28349
+ w: g.width,
28350
+ h: g.height
28351
+ });
28352
+ const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
28353
+ const out = [];
28354
+ layout.edges.forEach((e, idx) => {
28355
+ if (e.points.length < 2) return;
28356
+ const poly = flatten(splineGen(e.points) ?? "");
28357
+ for (const r of rects) {
28358
+ if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
28359
+ continue;
28360
+ const hits = poly.filter((p) => inside(p, r));
28361
+ if (hits.length >= minPts)
28362
+ out.push({ edgeIdx: idx, node: r.key, pts: hits });
28363
+ }
28364
+ });
28365
+ return out;
28366
+ }
28367
+ function countEdgeNodePierces(layout, opts) {
28368
+ return detectEdgeNodePierces(layout, opts).length;
28369
+ }
28370
+ function deroutePierces(layout) {
28371
+ const pierces = detectEdgeNodePierces(layout);
28372
+ if (!pierces.length) return layout;
28373
+ const rect = /* @__PURE__ */ new Map();
28374
+ for (const n of layout.nodes)
28375
+ rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
28376
+ for (const g of layout.groups)
28377
+ if (g.collapsed)
28378
+ rect.set("__group_" + g.label, {
28379
+ x: g.x,
28380
+ y: g.y,
28381
+ w: g.width,
28382
+ h: g.height
28383
+ });
28384
+ const byEdge = /* @__PURE__ */ new Map();
28385
+ for (const p of pierces) {
28386
+ const arr = byEdge.get(p.edgeIdx);
28387
+ if (arr) arr.push(p.node);
28388
+ else byEdge.set(p.edgeIdx, [p.node]);
28389
+ }
28390
+ const edges = layout.edges.map((e, idx) => {
28391
+ const nodes = byEdge.get(idx);
28392
+ if (!nodes) return e;
28393
+ let pts = e.points.map((p) => ({ x: p.x, y: p.y }));
28394
+ for (const label of nodes) {
28395
+ const r = rect.get(label);
28396
+ if (r) pts = detourAround(pts, r);
28397
+ }
28398
+ return { ...e, points: pts };
28399
+ });
28400
+ return { ...layout, edges };
28401
+ }
28402
+ function detourAround(pts, r) {
28403
+ if (pts.length < 2) return pts;
28404
+ const c = { x: r.x, y: r.y };
28405
+ let bestSeg = -1;
28406
+ let bestT = 0;
28407
+ let bestD = Infinity;
28408
+ let bestPt = pts[0];
28409
+ for (let i = 0; i < pts.length - 1; i++) {
28410
+ const a2 = pts[i], b2 = pts[i + 1];
28411
+ const dx = b2.x - a2.x, dy = b2.y - a2.y;
28412
+ const len2 = dx * dx + dy * dy || 1e-9;
28413
+ let t = ((c.x - a2.x) * dx + (c.y - a2.y) * dy) / len2;
28414
+ t = Math.max(0, Math.min(1, t));
28415
+ const q = { x: a2.x + t * dx, y: a2.y + t * dy };
28416
+ const d = Math.hypot(q.x - c.x, q.y - c.y);
28417
+ if (d < bestD) {
28418
+ bestD = d;
28419
+ bestSeg = i;
28420
+ bestT = t;
28421
+ bestPt = q;
28422
+ }
28423
+ }
28424
+ if (bestSeg < 0) return pts;
28425
+ let nx = bestPt.x - c.x;
28426
+ let ny = bestPt.y - c.y;
28427
+ if (Math.hypot(nx, ny) < 1) {
28428
+ if (r.w <= r.h) {
28429
+ nx = 1;
28430
+ ny = 0;
28431
+ } else {
28432
+ nx = 0;
28433
+ ny = 1;
28434
+ }
28435
+ }
28436
+ const nlen = Math.hypot(nx, ny) || 1;
28437
+ nx /= nlen;
28438
+ ny /= nlen;
28439
+ const a = pts[bestSeg], b = pts[bestSeg + 1];
28440
+ let ex = b.x - a.x, ey = b.y - a.y;
28441
+ const elen = Math.hypot(ex, ey) || 1;
28442
+ ex /= elen;
28443
+ ey /= elen;
28444
+ const clear = Math.hypot(r.w, r.h) / 2 + 18;
28445
+ const hw = Math.hypot(r.w, r.h) / 2;
28446
+ void bestT;
28447
+ const before = {
28448
+ x: bestPt.x - ex * hw + nx * clear * 0.5,
28449
+ y: bestPt.y - ey * hw + ny * clear * 0.5
28450
+ };
28451
+ const peak = { x: c.x + nx * clear, y: c.y + ny * clear };
28452
+ const after = {
28453
+ x: bestPt.x + ex * hw + nx * clear * 0.5,
28454
+ y: bestPt.y + ey * hw + ny * clear * 0.5
28455
+ };
28456
+ const out = pts.slice(0, bestSeg + 1);
28457
+ out.push(before, peak, after);
28458
+ out.push(...pts.slice(bestSeg + 1));
28459
+ return out;
28460
+ }
28461
+ function countEdgeNearMiss(layout, opts) {
28462
+ const near = opts?.near ?? 14;
28463
+ const tight = opts?.tight ?? 8;
28464
+ const minLen = opts?.minLen ?? 18;
28465
+ const close = detectEdgeOverlaps(layout, { dist: near, minLen }).length;
28466
+ const over = detectEdgeOverlaps(layout, { dist: tight, minLen }).length;
28467
+ return Math.max(0, close - over);
28468
+ }
28469
+ function detectGroupOverlaps(layout, opts) {
28470
+ const margin = opts?.margin ?? 4;
28471
+ const raw = layout.groups.map((g) => ({
28472
+ label: g.label,
28473
+ l: g.x - g.width / 2,
28474
+ r: g.x + g.width / 2,
28475
+ t: g.y - g.height / 2,
28476
+ b: g.y + g.height / 2
28477
+ }));
28478
+ const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
28479
+ const rend = raw.map((a, i) => {
28480
+ const parent = raw.some((b, j) => j !== i && contains(a, b));
28481
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
28482
+ });
28483
+ const hit = /* @__PURE__ */ new Set();
28484
+ for (let i = 0; i < raw.length; i++)
28485
+ for (let j = i + 1; j < raw.length; j++) {
28486
+ if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
28487
+ const a = rend[i], b = rend[j];
28488
+ const dx = Math.max(a.l - b.r, b.l - a.r);
28489
+ const dy = Math.max(a.t - b.b, b.t - a.b);
28490
+ if (Math.max(dx, dy) < margin) {
28491
+ hit.add(raw[i].label);
28492
+ hit.add(raw[j].label);
28493
+ }
28494
+ }
28495
+ return [...hit];
28496
+ }
28497
+ function countGroupOverlaps(layout, opts) {
28498
+ const margin = opts?.margin ?? 4;
28499
+ const raw = layout.groups.map((g) => ({
28500
+ l: g.x - g.width / 2,
28501
+ r: g.x + g.width / 2,
28502
+ t: g.y - g.height / 2,
28503
+ b: g.y + g.height / 2
28504
+ }));
28505
+ const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
28506
+ const rend = raw.map((a, i) => {
28507
+ const parent = raw.some((b, j) => j !== i && contains(a, b));
28508
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
28509
+ });
28510
+ let count = 0;
28511
+ for (let i = 0; i < raw.length; i++)
28512
+ for (let j = i + 1; j < raw.length; j++) {
28513
+ if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
28514
+ const a = rend[i], b = rend[j];
28515
+ const dx = Math.max(a.l - b.r, b.l - a.r);
28516
+ const dy = Math.max(a.t - b.b, b.t - a.b);
28517
+ if (Math.max(dx, dy) < margin) count++;
28518
+ }
28519
+ return count;
28520
+ }
28521
+ function separateGroupBands(layout, parsed) {
28522
+ if (layout.groups.some((g) => g.collapsed)) return layout;
28523
+ if (countGroupOverlaps(layout) === 0) return layout;
28524
+ const parentOf = /* @__PURE__ */ new Map();
28525
+ for (const g of parsed.groups) parentOf.set(g.label, g.parentGroup);
28526
+ const topOf = (label) => {
28527
+ let cur = label;
28528
+ const seen = /* @__PURE__ */ new Set();
28529
+ for (; ; ) {
28530
+ const p = parentOf.get(cur);
28531
+ if (!p || seen.has(p)) return cur;
28532
+ seen.add(cur);
28533
+ cur = p;
28534
+ }
28535
+ };
28536
+ const topLabels = parsed.groups.filter((g) => !g.parentGroup).map((g) => g.label);
28537
+ if (topLabels.length < 2) return layout;
28538
+ const nodeTop = /* @__PURE__ */ new Map();
28539
+ for (const g of parsed.groups)
28540
+ for (const child of g.children)
28541
+ if (!parsed.groups.some((gg) => gg.label === child))
28542
+ nodeTop.set(child, topOf(g.label));
28543
+ const axis = parsed.direction === "TB" ? "x" : "y";
28544
+ const boxByLabel = new Map(layout.groups.map((g) => [g.label, g]));
28545
+ const bands = topLabels.map((label) => {
28546
+ const g = boxByLabel.get(label);
28547
+ const half2 = (axis === "y" ? g.height : g.width) / 2;
28548
+ let lo = (axis === "y" ? g.y : g.x) - half2;
28549
+ const hi = (axis === "y" ? g.y : g.x) + half2;
28550
+ const isParent = parsed.groups.some((c) => c.parentGroup === label);
28551
+ if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE2;
28552
+ return { label, lo, hi, c: (lo + hi) / 2 };
28553
+ });
28554
+ bands.sort((a, b) => a.c - b.c);
28555
+ const GAP = 16;
28556
+ const half = bands.map((b) => (b.hi - b.lo) / 2);
28557
+ const off = [0];
28558
+ for (let i = 1; i < bands.length; i++)
28559
+ off[i] = off[i - 1] + half[i - 1] + GAP + half[i];
28560
+ const desired = bands.map((b, i) => b.c - off[i]);
28561
+ const blocks = [];
28562
+ for (let i = 0; i < bands.length; i++) {
28563
+ let blk = { pos: desired[i], count: 1, sum: desired[i], first: i };
28564
+ while (blocks.length && blocks[blocks.length - 1].pos >= blk.pos) {
28565
+ const prev = blocks.pop();
28566
+ const count = prev.count + blk.count;
28567
+ const sum = prev.sum + blk.sum;
28568
+ blk = { pos: sum / count, count, sum, first: prev.first };
28569
+ }
28570
+ blocks.push(blk);
28571
+ }
28572
+ const newC = new Array(bands.length);
28573
+ for (const blk of blocks)
28574
+ for (let k = 0; k < blk.count; k++)
28575
+ newC[blk.first + k] = blk.pos + off[blk.first + k];
28576
+ const delta = /* @__PURE__ */ new Map();
28577
+ let moved = false;
28578
+ bands.forEach((b, i) => {
28579
+ const d = newC[i] - b.c;
28580
+ delta.set(b.label, d);
28581
+ if (Math.abs(d) > 0.5) moved = true;
28582
+ });
28583
+ if (!moved) return layout;
28584
+ const nodeDelta = (label) => {
28585
+ const top = nodeTop.get(label);
28586
+ return top ? delta.get(top) ?? 0 : 0;
28587
+ };
28588
+ const shift = (p, d) => axis === "y" ? { x: p.x, y: p.y + d } : { x: p.x + d, y: p.y };
28589
+ const nodes = layout.nodes.map((n) => {
28590
+ const d = nodeDelta(n.label);
28591
+ return d ? { ...n, ...axis === "y" ? { y: n.y + d } : { x: n.x + d } } : n;
28592
+ });
28593
+ const groups = layout.groups.map((g) => {
28594
+ const d = delta.get(topOf(g.label)) ?? 0;
28595
+ return d ? { ...g, ...axis === "y" ? { y: g.y + d } : { x: g.x + d } } : g;
28596
+ });
28597
+ const edges = layout.edges.map((e) => {
28598
+ const ds = nodeDelta(e.source);
28599
+ const dt = nodeDelta(e.target);
28600
+ if (!ds && !dt) return e;
28601
+ const N = e.points.length;
28602
+ const points = e.points.map((p, i) => {
28603
+ const f = N > 1 ? i / (N - 1) : 0;
28604
+ return shift(p, ds * (1 - f) + dt * f);
28605
+ });
28606
+ return { ...e, points };
28607
+ });
28608
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
28609
+ const acc = (x, y) => {
28610
+ if (x < minX) minX = x;
28611
+ if (x > maxX) maxX = x;
28612
+ if (y < minY) minY = y;
28613
+ if (y > maxY) maxY = y;
28614
+ };
28615
+ for (const n of nodes) {
28616
+ acc(n.x - n.width / 2, n.y - n.height / 2);
28617
+ acc(n.x + n.width / 2, n.y + n.height / 2);
28618
+ }
28619
+ for (const g of groups) {
28620
+ acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE2);
28621
+ acc(g.x + g.width / 2, g.y + g.height / 2);
28622
+ }
28623
+ for (const e of edges) for (const p of e.points) acc(p.x, p.y);
28624
+ const M = 40;
28625
+ const sx = M - minX, sy = M - minY;
28626
+ const reshift = sx !== 0 || sy !== 0;
28627
+ return {
28628
+ nodes: reshift ? nodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })) : nodes,
28629
+ groups: reshift ? groups.map((g) => ({ ...g, x: g.x + sx, y: g.y + sy })) : groups,
28630
+ edges: reshift ? edges.map((e) => ({
28631
+ ...e,
28632
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
28633
+ })) : edges,
28634
+ width: maxX - minX + 2 * M,
28635
+ height: maxY - minY + 2 * M
28636
+ };
28637
+ }
28638
+ function segCross(p1, p2, p3, p4) {
28639
+ const d1x = p2.x - p1.x, d1y = p2.y - p1.y, d2x = p4.x - p3.x, d2y = p4.y - p3.y;
28640
+ const den = d1x * d2y - d1y * d2x;
28641
+ if (Math.abs(den) < 1e-9) return false;
28642
+ const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / den;
28643
+ const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / den;
28644
+ return t > 1e-3 && t < 0.999 && s > 1e-3 && s < 0.999;
28645
+ }
28646
+ function countCrossingsFast(layout) {
28647
+ const E = layout.edges.filter((e) => e.points.length >= 2);
28648
+ const bb = E.map((e) => {
28649
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28650
+ for (const p of e.points) {
28651
+ if (p.x < x0) x0 = p.x;
28652
+ if (p.x > x1) x1 = p.x;
28653
+ if (p.y < y0) y0 = p.y;
28654
+ if (p.y > y1) y1 = p.y;
28655
+ }
28656
+ return { x0, y0, x1, y1 };
28657
+ });
28658
+ let count = 0;
28659
+ for (let i = 0; i < E.length; i++)
28660
+ for (let j = i + 1; j < E.length; j++) {
28661
+ const A = E[i], B = E[j];
28662
+ if (A.source === B.source || A.source === B.target || A.target === B.source || A.target === B.target)
28663
+ continue;
28664
+ const a = bb[i], b = bb[j];
28665
+ if (a.x1 < b.x0 || b.x1 < a.x0 || a.y1 < b.y0 || b.y1 < a.y0) continue;
28666
+ const pa = A.points, pb = B.points;
28667
+ let hit = false;
28668
+ for (let ai = 0; ai < pa.length - 1 && !hit; ai++)
28669
+ for (let bi = 0; bi < pb.length - 1; bi++) {
28670
+ if (segCross(pa[ai], pa[ai + 1], pb[bi], pb[bi + 1])) {
28671
+ hit = true;
28672
+ break;
28673
+ }
28674
+ }
28675
+ if (hit) count++;
28676
+ }
28677
+ return count;
28678
+ }
28679
+ function meanDrift(layout, prev) {
28680
+ if (!prev?.size) return 0;
28681
+ let sum = 0, n = 0;
28682
+ for (const node of layout.nodes) {
28683
+ const p = prev.get(node.label);
28684
+ if (p) {
28685
+ sum += Math.hypot(node.x - p.x, node.y - p.y);
28686
+ n++;
28687
+ }
28688
+ }
28689
+ return n ? sum / n : 0;
28690
+ }
28691
+ function edgeLength(layout) {
28692
+ let total = 0;
28693
+ for (const e of layout.edges)
28694
+ for (let i = 1; i < e.points.length; i++)
28695
+ total += Math.hypot(
28696
+ e.points[i].x - e.points[i - 1].x,
28697
+ e.points[i].y - e.points[i - 1].y
28698
+ );
28699
+ return total;
28700
+ }
28701
+ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
28702
+ const hideDescriptions = opts?.hideDescriptions ?? false;
28703
+ const collapsedGroupLabels = /* @__PURE__ */ new Set();
28704
+ if (collapseInfo) {
28705
+ const missing = /* @__PURE__ */ new Set();
28706
+ for (const og of collapseInfo.originalGroups)
28707
+ if (!parsed.groups.some((g) => g.label === og.label))
28708
+ missing.add(og.label);
28709
+ for (const label of missing) {
28710
+ const og = collapseInfo.originalGroups.find((g) => g.label === label);
28711
+ const parent = og?.parentGroup;
28712
+ if (!parent || !missing.has(parent)) collapsedGroupLabels.add(label);
28713
+ }
28714
+ }
28715
+ const sizes = /* @__PURE__ */ new Map();
28716
+ let maxDescH = 0;
28717
+ for (const node of parsed.nodes) {
28718
+ const s = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
28719
+ sizes.set(node.label, s);
28720
+ if (!hideDescriptions && node.description && node.description.length > 0)
28721
+ maxDescH = Math.max(maxDescH, s.height);
28722
+ }
28723
+ if (maxDescH > 0) {
28724
+ for (const node of parsed.nodes)
28725
+ if (node.description && node.description.length > 0) {
28726
+ const s = sizes.get(node.label);
28727
+ sizes.set(node.label, { width: s.width, height: maxDescH });
28728
+ }
28729
+ }
28730
+ const gid = (label) => `__group_${label}`;
28731
+ const rankdir = parsed.direction === "TB" ? "TB" : "LR";
28732
+ function place(cfg) {
28733
+ const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
28734
+ const ord = (a) => r ? shuffle(a, r) : a.slice();
28735
+ const g = new dagre4.graphlib.Graph({ compound: true, multigraph: true });
28736
+ g.setGraph({
28737
+ rankdir,
28738
+ ranker: cfg.ranker,
28739
+ nodesep: cfg.nodesep,
28740
+ ranksep: cfg.ranksep,
28741
+ edgesep: 20,
28742
+ marginx: 40,
28743
+ marginy: 40
28744
+ });
28745
+ g.setDefaultEdgeLabel(() => ({}));
28746
+ for (const grp of ord(parsed.groups))
28747
+ g.setNode(gid(grp.label), { label: grp.label });
28748
+ for (const node of ord(parsed.nodes)) {
28749
+ const s = sizes.get(node.label);
28750
+ g.setNode(node.label, { width: s.width, height: s.height });
28751
+ }
28752
+ for (const label of collapsedGroupLabels)
28753
+ g.setNode(gid(label), { width: NODE_WIDTH, height: NODE_HEIGHT });
28754
+ for (const grp of parsed.groups) {
28755
+ if (grp.parentGroup && g.hasNode(gid(grp.parentGroup)))
28756
+ g.setParent(gid(grp.label), gid(grp.parentGroup));
28757
+ for (const c of ord(grp.children)) {
28758
+ if (g.hasNode(c)) g.setParent(c, gid(grp.label));
28759
+ }
28760
+ }
28761
+ if (collapseInfo)
28762
+ for (const label of collapsedGroupLabels) {
28763
+ const og = collapseInfo.originalGroups.find((x) => x.label === label);
28764
+ if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && g.hasNode(gid(og.parentGroup)))
28765
+ g.setParent(gid(label), gid(og.parentGroup));
28766
+ }
28767
+ for (const e of ord(parsed.edges))
28768
+ if (g.hasNode(e.source) && g.hasNode(e.target))
28769
+ g.setEdge(e.source, e.target, {});
28770
+ dagre4.layout(g);
28771
+ const nodes = parsed.nodes.map((n2) => {
28772
+ const p = g.node(n2.label);
28773
+ return {
28774
+ label: n2.label,
28775
+ x: p.x,
28776
+ y: p.y,
28777
+ width: p.width,
28778
+ height: p.height
28779
+ };
28780
+ });
28781
+ const groups = parsed.groups.map(
28782
+ (grp) => {
28783
+ const p = g.node(gid(grp.label));
28784
+ return {
28785
+ label: grp.label,
28786
+ lineNumber: grp.lineNumber,
28787
+ x: p.x,
28788
+ y: p.y,
28789
+ width: p.width,
28790
+ height: p.height,
28791
+ collapsed: false,
28792
+ childCount: grp.children.length
28793
+ };
28794
+ }
28795
+ );
28796
+ for (const label of collapsedGroupLabels) {
28797
+ const p = g.node(gid(label));
28798
+ groups.push({
28799
+ label,
28800
+ lineNumber: 0,
28801
+ x: p.x,
28802
+ y: p.y,
28803
+ width: p.width,
28804
+ height: p.height,
28805
+ collapsed: true,
28806
+ childCount: collapseInfo?.collapsedChildCounts.get(label) ?? 0
28807
+ });
28808
+ }
28809
+ const edges = parsed.edges.filter((e) => g.hasEdge(e.source, e.target)).map((e) => {
28810
+ const ed = g.edge(e.source, e.target);
28811
+ return {
28812
+ source: e.source,
28813
+ target: e.target,
28814
+ ...e.label !== void 0 && { label: e.label },
28815
+ bidirectional: e.bidirectional,
28816
+ lineNumber: e.lineNumber,
28817
+ points: ed?.points ?? [],
28818
+ yOffset: 0,
28819
+ parallelCount: 1,
28820
+ metadata: e.metadata
28821
+ };
28822
+ });
28823
+ const gg = g.graph();
28824
+ return {
28825
+ nodes,
28826
+ edges,
28827
+ groups,
28828
+ width: gg.width ?? 800,
28829
+ height: gg.height ?? 600
28830
+ };
28831
+ }
28832
+ const n = parsed.nodes.length;
28833
+ const seedCount = opts?.seeds ?? (n <= 12 ? 80 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
28834
+ const REFINE_K = opts?.refineK ?? 6;
28835
+ const lambda = opts?.lambda ?? DEFAULT_LAMBDA;
28836
+ const prev = opts?.previousPositions;
28837
+ const RANKERS = ["network-simplex", "tight-tree", "longest-path"];
28838
+ const SPACINGS = [
28839
+ { nodesep: 50, ranksep: 60 },
28840
+ { nodesep: 34, ranksep: 46 },
28841
+ { nodesep: 66, ranksep: 82 }
28842
+ ];
28843
+ const configs = [];
28844
+ for (const ranker of RANKERS)
28845
+ for (const sp of SPACINGS) configs.push({ ranker, ...sp });
28846
+ for (let s = 0; s < seedCount; s++)
28847
+ configs.push({
28848
+ ranker: "network-simplex",
28849
+ nodesep: 50,
28850
+ ranksep: 60,
28851
+ seed: s
28852
+ });
28853
+ const badness = (lay, floor) => {
28854
+ const x = countSplineCrossings(lay);
28855
+ if (x > floor) return Infinity;
28856
+ return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
28857
+ };
28858
+ const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
28859
+ const pool = [];
28860
+ for (const cfg of configs) {
28861
+ try {
28862
+ pool.push(place(cfg));
28863
+ } catch {
28864
+ }
28865
+ }
28866
+ if (!pool.length)
28867
+ return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
28868
+ let layered = [];
28869
+ try {
28870
+ layered = layeredCandidates(parsed, sizes);
28871
+ } catch {
28872
+ }
28873
+ pool.sort(
28874
+ (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
28875
+ );
28876
+ const refineK = Math.min(REFINE_K, pool.length);
28877
+ let best = pool[0];
28878
+ let bestObj = Infinity;
28879
+ let bestBad = Infinity;
28880
+ const consider = (lay) => {
28881
+ const bad = badness(lay, bestBad);
28882
+ if (bad === Infinity) return;
28883
+ const sc = objective(lay, bad);
28884
+ if (sc < bestObj) {
28885
+ bestObj = sc;
28886
+ bestBad = bad;
28887
+ best = lay;
28888
+ }
28889
+ };
28890
+ for (const lay of pool.slice(0, refineK)) consider(lay);
28891
+ if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
28892
+ const extra = [];
28893
+ for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
28894
+ try {
28895
+ extra.push(
28896
+ place({
28897
+ ranker: "network-simplex",
28898
+ nodesep: 50,
28899
+ ranksep: 60,
28900
+ seed: s
28901
+ })
28902
+ );
28903
+ } catch {
28904
+ }
28905
+ }
28906
+ extra.sort(
28907
+ (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
28908
+ );
28909
+ for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
28910
+ }
28911
+ for (const lay of layered) {
28912
+ const variants = [lay];
28913
+ const dp = deroutePierces(lay);
28914
+ if (dp !== lay) variants.push(dp);
28915
+ for (const v of variants) {
28916
+ const bad = badness(v, bestBad - 1);
28917
+ if (bad < bestBad) {
28918
+ bestBad = bad;
28919
+ best = v;
28920
+ }
28921
+ }
28922
+ }
28923
+ if (bestBad > 0) {
28924
+ const rerouted = deroutePierces(best);
28925
+ if (rerouted !== best && badness(rerouted, bestBad - 1) < bestBad)
28926
+ best = rerouted;
28927
+ }
28928
+ if (bestBad > 0 && countGroupOverlaps(best) > 0) {
28929
+ const separated = separateGroupBands(best, parsed);
28930
+ if (separated !== best && badness(separated, bestBad - 1) < bestBad)
28931
+ best = separated;
28932
+ }
28933
+ return best;
28934
+ }
28935
+ var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
28936
+ var init_layout_search = __esm({
28937
+ "src/boxes-and-lines/layout-search.ts"() {
28938
+ "use strict";
28939
+ init_layout5();
28940
+ init_layout_layered();
28941
+ DEFAULT_LAMBDA = 4;
28942
+ ESCALATE_THRESHOLD = 4;
28943
+ ESCALATE_MAX_N = 45;
28944
+ ESCALATE_SEEDS = 18;
28945
+ ESCALATE_REFINE = 10;
28946
+ splineGen = d3line().x((d) => d.x).y((d) => d.y).curve(curveBasis5);
28947
+ GROUP_LABEL_ZONE2 = 32;
28948
+ }
28949
+ });
28950
+
27621
28951
  // src/boxes-and-lines/layout.ts
27622
28952
  var layout_exports5 = {};
27623
28953
  __export(layout_exports5, {
28954
+ NODE_HEIGHT: () => NODE_HEIGHT,
28955
+ NODE_WIDTH: () => NODE_WIDTH,
28956
+ computeNodeSize: () => computeNodeSize,
27624
28957
  layoutBoxesAndLines: () => layoutBoxesAndLines
27625
28958
  });
27626
- import ELK from "elkjs/lib/elk.bundled.js";
27627
28959
  function splitCamelCase2(word) {
27628
28960
  const parts = [];
27629
28961
  let start = 0;
@@ -27694,417 +29026,21 @@ function computeNodeSize(node, reserveValueRow) {
27694
29026
  const totalHeight = labelHeight + SEPARATOR_GAP5 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
27695
29027
  return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
27696
29028
  }
27697
- function getElk() {
27698
- if (!elkInstance) elkInstance = new ELK();
27699
- return elkInstance;
27700
- }
27701
- function baseOptions() {
27702
- return {
27703
- "elk.algorithm": "layered",
27704
- // INCLUDE_CHILDREN lets ELK route edges across container boundaries.
27705
- "elk.hierarchyHandling": "INCLUDE_CHILDREN",
27706
- "elk.edgeRouting": "ORTHOGONAL",
27707
- "elk.layered.unnecessaryBendpoints": "true",
27708
- // Let edges leave from top/bottom of nodes (not just the flow-direction
27709
- // sides) when it reduces crossings.
27710
- "elk.layered.allowNonFlowPortsToSwitchSides": "true"
27711
- };
27712
- }
27713
- function bkBaseline() {
27714
- return {
27715
- ...baseOptions(),
27716
- "elk.layered.nodePlacement.strategy": "BRANDES_KOEPF",
27717
- "elk.layered.nodePlacement.bk.fixedAlignment": "BALANCED",
27718
- "elk.layered.nodePlacement.bk.edgeStraightening": "IMPROVE_STRAIGHTNESS",
27719
- "elk.layered.compaction.connectedComponents": "true",
27720
- "elk.layered.spacing.nodeNodeBetweenLayers": "90",
27721
- "elk.spacing.nodeNode": "55",
27722
- "elk.spacing.edgeNode": "55",
27723
- "elk.spacing.edgeEdge": "18"
27724
- };
27725
- }
27726
- function getVariants() {
27727
- const bk = bkBaseline();
27728
- return [
27729
- {
27730
- name: "bk-baseline",
27731
- options: {
27732
- ...bk,
27733
- "elk.layered.crossingMinimization.greedySwitch.type": "ONE_SIDED"
27734
- }
27735
- },
27736
- {
27737
- name: "bk-aggressive",
27738
- options: {
27739
- ...bk,
27740
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27741
- "elk.layered.thoroughness": "50"
27742
- }
27743
- },
27744
- {
27745
- name: "bk-wide",
27746
- options: {
27747
- ...bk,
27748
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27749
- "elk.layered.thoroughness": "50",
27750
- "elk.spacing.nodeNode": "70",
27751
- "elk.spacing.edgeNode": "75",
27752
- "elk.spacing.edgeEdge": "22",
27753
- "elk.layered.spacing.nodeNodeBetweenLayers": "120"
27754
- }
27755
- },
27756
- {
27757
- name: "longest-path",
27758
- options: {
27759
- ...bk,
27760
- "elk.layered.layering.strategy": "LONGEST_PATH",
27761
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27762
- "elk.layered.thoroughness": "50"
27763
- }
27764
- },
27765
- {
27766
- name: "bounded-width",
27767
- options: {
27768
- ...bk,
27769
- "elk.layered.layering.strategy": "COFFMAN_GRAHAM",
27770
- "elk.layered.layering.coffmanGraham.layerBound": "3",
27771
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27772
- "elk.layered.thoroughness": "50"
27773
- }
27774
- }
27775
- ];
27776
- }
27777
- function countCrossings(edges) {
27778
- let count = 0;
27779
- for (let i = 0; i < edges.length; i++) {
27780
- const edgeI = edges[i];
27781
- const a = edgeI.points;
27782
- if (a.length < 2) continue;
27783
- for (let j = i + 1; j < edges.length; j++) {
27784
- const edgeJ = edges[j];
27785
- const b = edgeJ.points;
27786
- if (b.length < 2) continue;
27787
- if (edgeI.source === edgeJ.source) continue;
27788
- if (edgeI.source === edgeJ.target) continue;
27789
- if (edgeI.target === edgeJ.source) continue;
27790
- if (edgeI.target === edgeJ.target) continue;
27791
- for (let ai = 0; ai < a.length - 1; ai++) {
27792
- for (let bi = 0; bi < b.length - 1; bi++) {
27793
- if (segmentsCross(a[ai], a[ai + 1], b[bi], b[bi + 1])) count++;
27794
- }
27795
- }
27796
- }
27797
- }
27798
- return count;
27799
- }
27800
- function segmentsCross(p1, p2, p3, p4) {
27801
- const d1x = p2.x - p1.x;
27802
- const d1y = p2.y - p1.y;
27803
- const d2x = p4.x - p3.x;
27804
- const d2y = p4.y - p3.y;
27805
- const denom = d1x * d2y - d1y * d2x;
27806
- if (Math.abs(denom) < 1e-9) return false;
27807
- const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / denom;
27808
- const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / denom;
27809
- const EPS = 1e-3;
27810
- return t > EPS && t < 1 - EPS && s > EPS && s < 1 - EPS;
27811
- }
27812
- function countTotalBends(edges) {
27813
- let bends = 0;
27814
- for (const e of edges) bends += Math.max(0, e.points.length - 2);
27815
- return bends;
27816
- }
27817
- function scoreLayout(layout) {
27818
- return {
27819
- crossings: countCrossings(layout.edges),
27820
- bends: countTotalBends(layout.edges),
27821
- area: layout.width * layout.height
27822
- };
27823
- }
27824
- function cmpScore(a, b) {
27825
- const aBucket = a.crossings <= CROSSINGS_FORGIVENESS ? 0 : a.crossings;
27826
- const bBucket = b.crossings <= CROSSINGS_FORGIVENESS ? 0 : b.crossings;
27827
- if (aBucket !== bBucket) return aBucket - bBucket;
27828
- if (a.area !== b.area) return a.area - b.area;
27829
- return a.bends - b.bends;
27830
- }
27831
29029
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
27832
- const hideDescriptions = layoutOptions?.hideDescriptions ?? false;
27833
- const direction = parsed.direction === "TB" ? "DOWN" : "RIGHT";
27834
- const collapsedGroupLabels = /* @__PURE__ */ new Set();
27835
- if (collapseInfo) {
27836
- const missingGroups = /* @__PURE__ */ new Set();
27837
- for (const og of collapseInfo.originalGroups) {
27838
- if (!parsed.groups.some((g) => g.label === og.label)) {
27839
- missingGroups.add(og.label);
27840
- }
27841
- }
27842
- for (const label of missingGroups) {
27843
- const og = collapseInfo.originalGroups.find((g) => g.label === label);
27844
- const parentLabel = og?.parentGroup;
27845
- if (!parentLabel || !missingGroups.has(parentLabel)) {
27846
- collapsedGroupLabels.add(label);
27847
- }
27848
- }
27849
- }
27850
- const nodeSizes = /* @__PURE__ */ new Map();
27851
- let maxDescHeight = 0;
27852
- for (const node of parsed.nodes) {
27853
- const size = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
27854
- nodeSizes.set(node.label, size);
27855
- if (!hideDescriptions && node.description && node.description.length > 0) {
27856
- maxDescHeight = Math.max(maxDescHeight, size.height);
27857
- }
27858
- }
27859
- if (maxDescHeight > 0) {
27860
- for (const node of parsed.nodes) {
27861
- if (node.description && node.description.length > 0) {
27862
- const size = nodeSizes.get(node.label);
27863
- nodeSizes.set(node.label, { width: size.width, height: maxDescHeight });
27864
- }
27865
- }
27866
- }
27867
- const expandedGroupSet = new Set(parsed.groups.map((g) => g.label));
27868
- const gid = (label) => `__group_${label}`;
27869
- function buildGraph() {
27870
- const nodeById = /* @__PURE__ */ new Map();
27871
- const parentOf = /* @__PURE__ */ new Map();
27872
- for (const node of parsed.nodes) {
27873
- const size = nodeSizes.get(node.label);
27874
- nodeById.set(node.label, {
27875
- id: node.label,
27876
- width: size.width,
27877
- height: size.height,
27878
- labels: [{ text: node.label }]
27879
- });
27880
- }
27881
- for (const group of parsed.groups) {
27882
- nodeById.set(gid(group.label), {
27883
- id: gid(group.label),
27884
- labels: [{ text: group.label }],
27885
- layoutOptions: {
27886
- "elk.padding": `[top=${CONTAINER_PAD_TOP2},left=${CONTAINER_PAD_X3},bottom=${CONTAINER_PAD_BOTTOM3},right=${CONTAINER_PAD_X3}]`,
27887
- // Suggest square-ish containers — has limited effect with
27888
- // INCLUDE_CHILDREN but doesn't hurt.
27889
- "elk.aspectRatio": "1.4"
27890
- },
27891
- children: [],
27892
- edges: []
27893
- });
27894
- }
27895
- for (const label of collapsedGroupLabels) {
27896
- nodeById.set(gid(label), {
27897
- id: gid(label),
27898
- width: NODE_WIDTH,
27899
- height: NODE_HEIGHT,
27900
- labels: [{ text: label }]
27901
- });
27902
- }
27903
- for (const group of parsed.groups) {
27904
- if (group.parentGroup && nodeById.has(gid(group.parentGroup))) {
27905
- parentOf.set(gid(group.label), gid(group.parentGroup));
27906
- }
27907
- }
27908
- if (collapseInfo) {
27909
- for (const label of collapsedGroupLabels) {
27910
- const og = collapseInfo.originalGroups.find((g) => g.label === label);
27911
- if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && nodeById.has(gid(og.parentGroup))) {
27912
- parentOf.set(gid(label), gid(og.parentGroup));
27913
- }
27914
- }
27915
- }
27916
- for (const group of parsed.groups) {
27917
- for (const child of group.children) {
27918
- if (expandedGroupSet.has(child)) continue;
27919
- if (nodeById.has(child)) {
27920
- parentOf.set(child, gid(group.label));
27921
- }
27922
- }
27923
- }
27924
- const roots = [];
27925
- for (const [id, node] of nodeById) {
27926
- const parentId = parentOf.get(id);
27927
- if (parentId) {
27928
- const parent = nodeById.get(parentId);
27929
- parent.children = parent.children ?? [];
27930
- parent.children.push(node);
27931
- } else {
27932
- roots.push(node);
27933
- }
27934
- }
27935
- const rootEdges = [];
27936
- for (let i = 0; i < parsed.edges.length; i++) {
27937
- const edge = parsed.edges[i];
27938
- if (!nodeById.has(edge.source) || !nodeById.has(edge.target)) continue;
27939
- rootEdges.push({
27940
- id: `e${i}`,
27941
- sources: [edge.source],
27942
- targets: [edge.target]
27943
- });
27944
- }
27945
- return { roots, rootEdges };
27946
- }
27947
- async function runVariant(variant) {
27948
- const { roots, rootEdges } = buildGraph();
27949
- const elkRoot = {
27950
- id: "root",
27951
- layoutOptions: {
27952
- ...variant.options,
27953
- "elk.direction": direction,
27954
- "elk.padding": `[top=${MARGIN3},left=${MARGIN3},bottom=${MARGIN3},right=${MARGIN3}]`
27955
- },
27956
- children: roots,
27957
- edges: rootEdges
27958
- };
27959
- const result = await getElk().layout(elkRoot);
27960
- return extractLayout(result);
27961
- }
27962
- function extractLayout(result) {
27963
- const layoutNodes = [];
27964
- const layoutGroups = [];
27965
- const allEdges = [];
27966
- const containerAbs = /* @__PURE__ */ new Map();
27967
- function walk(n, offsetX, offsetY, isRoot) {
27968
- const nx = (n.x ?? 0) + offsetX;
27969
- const ny = (n.y ?? 0) + offsetY;
27970
- const nw = n.width ?? 0;
27971
- const nh = n.height ?? 0;
27972
- if (isRoot) {
27973
- containerAbs.set("root", { x: nx, y: ny });
27974
- } else {
27975
- const isGroup = n.id.startsWith("__group_");
27976
- if (isGroup) {
27977
- const label = n.id.slice("__group_".length);
27978
- const collapsed = collapsedGroupLabels.has(label);
27979
- const og = collapseInfo?.originalGroups.find(
27980
- (g) => g.label === label
27981
- );
27982
- const pg = parsed.groups.find((g) => g.label === label);
27983
- const childCount = collapsed ? collapseInfo?.collapsedChildCounts.get(label) ?? 0 : void 0;
27984
- layoutGroups.push({
27985
- label,
27986
- lineNumber: pg?.lineNumber ?? og?.lineNumber ?? 0,
27987
- x: nx + nw / 2,
27988
- y: ny + nh / 2,
27989
- width: nw,
27990
- height: nh,
27991
- collapsed,
27992
- ...childCount !== void 0 && { childCount }
27993
- });
27994
- if (!collapsed) containerAbs.set(n.id, { x: nx, y: ny });
27995
- } else {
27996
- layoutNodes.push({
27997
- label: n.id,
27998
- x: nx + nw / 2,
27999
- y: ny + nh / 2,
28000
- width: nw,
28001
- height: nh
28002
- });
28003
- }
28004
- }
28005
- if (n.edges) for (const e of n.edges) allEdges.push(e);
28006
- if (n.children) for (const c of n.children) walk(c, nx, ny, false);
28007
- }
28008
- walk(result, 0, 0, true);
28009
- const edgeYOffsets = new Array(parsed.edges.length).fill(0);
28010
- const edgeParallelCounts = new Array(parsed.edges.length).fill(1);
28011
- const parallelGroups = /* @__PURE__ */ new Map();
28012
- for (let i = 0; i < parsed.edges.length; i++) {
28013
- const edge = parsed.edges[i];
28014
- const [a, b] = edge.source < edge.target ? [edge.source, edge.target] : [edge.target, edge.source];
28015
- const key = `${a}\0${b}`;
28016
- if (!parallelGroups.has(key)) parallelGroups.set(key, []);
28017
- parallelGroups.get(key).push(i);
28018
- }
28019
- for (const group of parallelGroups.values()) {
28020
- const capped = group.slice(0, MAX_PARALLEL_EDGES);
28021
- for (const idx of group.slice(MAX_PARALLEL_EDGES)) {
28022
- edgeParallelCounts[idx] = 0;
28023
- }
28024
- if (capped.length < 2) continue;
28025
- for (let j = 0; j < capped.length; j++) {
28026
- const cappedJ = capped[j];
28027
- edgeYOffsets[cappedJ] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
28028
- edgeParallelCounts[cappedJ] = capped.length;
28029
- }
28030
- }
28031
- const edgeById = /* @__PURE__ */ new Map();
28032
- for (const e of allEdges) edgeById.set(e.id, e);
28033
- const layoutEdges = [];
28034
- for (let i = 0; i < parsed.edges.length; i++) {
28035
- const edge = parsed.edges[i];
28036
- if (edgeParallelCounts[i] === 0) continue;
28037
- const elkEdge = edgeById.get(`e${i}`);
28038
- if (!elkEdge?.sections || elkEdge.sections.length === 0) continue;
28039
- const container = elkEdge.container ?? "root";
28040
- const off = containerAbs.get(container) ?? { x: 0, y: 0 };
28041
- const s = elkEdge.sections[0];
28042
- const points = [
28043
- { x: s.startPoint.x + off.x, y: s.startPoint.y + off.y },
28044
- ...(s.bendPoints ?? []).map((p) => ({
28045
- x: p.x + off.x,
28046
- y: p.y + off.y
28047
- })),
28048
- { x: s.endPoint.x + off.x, y: s.endPoint.y + off.y }
28049
- ];
28050
- let labelX;
28051
- let labelY;
28052
- if (edge.label && points.length >= 2) {
28053
- const mid = Math.floor(points.length / 2);
28054
- const midPoint = points[mid];
28055
- labelX = midPoint.x;
28056
- labelY = midPoint.y - 10;
28057
- }
28058
- layoutEdges.push({
28059
- source: edge.source,
28060
- target: edge.target,
28061
- ...edge.label !== void 0 && { label: edge.label },
28062
- bidirectional: edge.bidirectional,
28063
- lineNumber: edge.lineNumber,
28064
- points,
28065
- ...labelX !== void 0 && { labelX },
28066
- ...labelY !== void 0 && { labelY },
28067
- // In-bounds — i < parsed.edges.length, arrays sized to that length.
28068
- yOffset: edgeYOffsets[i],
28069
- parallelCount: edgeParallelCounts[i],
28070
- metadata: edge.metadata,
28071
- deferred: true
28072
- });
28073
- }
28074
- let maxX = 0;
28075
- let maxY = 0;
28076
- for (const node of layoutNodes) {
28077
- maxX = Math.max(maxX, node.x + node.width / 2);
28078
- maxY = Math.max(maxY, node.y + node.height / 2);
28079
- }
28080
- for (const group of layoutGroups) {
28081
- maxX = Math.max(maxX, group.x + group.width / 2);
28082
- maxY = Math.max(maxY, group.y + group.height / 2);
29030
+ const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
29031
+ const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
29032
+ ...layoutOptions?.hideDescriptions !== void 0 && {
29033
+ hideDescriptions: layoutOptions.hideDescriptions
29034
+ },
29035
+ ...layoutOptions?.previousPositions !== void 0 && {
29036
+ previousPositions: layoutOptions.previousPositions
28083
29037
  }
28084
- return {
28085
- nodes: layoutNodes,
28086
- edges: layoutEdges,
28087
- groups: layoutGroups,
28088
- width: maxX + MARGIN3,
28089
- height: maxY + MARGIN3
28090
- };
28091
- }
28092
- const N = parsed.nodes.length + parsed.groups.length;
28093
- const E = parsed.edges.length;
28094
- const trivial = N < 8 && E < 10;
28095
- const variants = trivial ? [getVariants()[1]] : getVariants();
28096
- const results = await Promise.all(variants.map((v) => runVariant(v)));
28097
- let best = results[0];
28098
- let bestScore = scoreLayout(best);
28099
- for (let i = 1; i < results.length; i++) {
28100
- const resultI = results[i];
28101
- const s = scoreLayout(resultI);
28102
- if (cmpScore(s, bestScore) < 0) {
28103
- best = resultI;
28104
- bestScore = s;
28105
- }
28106
- }
28107
- return attachNotes(best, parsed, layoutOptions?.collapsedNotes);
29038
+ });
29039
+ return attachNotes(
29040
+ applyParallelEdgeOffsets(searched),
29041
+ parsed,
29042
+ layoutOptions?.collapsedNotes
29043
+ );
28108
29044
  }
28109
29045
  function attachNotes(layout, parsed, collapsedNotes) {
28110
29046
  const notesSuppressed = parsed.options?.["no-notes"] === "on";
@@ -28182,20 +29118,47 @@ function attachNotes(layout, parsed, collapsedNotes) {
28182
29118
  nodes: finalNodes,
28183
29119
  edges: finalEdges,
28184
29120
  groups: finalGroups,
28185
- width: bbMaxX + shiftX + MARGIN3,
28186
- height: bbMaxY + shiftY + MARGIN3
29121
+ width: bbMaxX + shiftX + MARGIN4,
29122
+ height: bbMaxY + shiftY + MARGIN4
29123
+ };
29124
+ }
29125
+ function applyParallelEdgeOffsets(layout) {
29126
+ const groups = /* @__PURE__ */ new Map();
29127
+ layout.edges.forEach((e, i) => {
29128
+ const [a, b] = e.source < e.target ? [e.source, e.target] : [e.target, e.source];
29129
+ const key = `${a}\0${b}`;
29130
+ const arr = groups.get(key);
29131
+ if (arr) arr.push(i);
29132
+ else groups.set(key, [i]);
29133
+ });
29134
+ if ([...groups.values()].every((g) => g.length < 2)) return layout;
29135
+ const yOffset = new Array(layout.edges.length).fill(0);
29136
+ const count = new Array(layout.edges.length).fill(1);
29137
+ for (const idxs of groups.values()) {
29138
+ const capped = idxs.slice(0, MAX_PARALLEL_EDGES);
29139
+ for (const drop of idxs.slice(MAX_PARALLEL_EDGES)) count[drop] = 0;
29140
+ if (capped.length < 2) continue;
29141
+ capped.forEach((idx, j) => {
29142
+ yOffset[idx] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
29143
+ count[idx] = capped.length;
29144
+ });
29145
+ }
29146
+ return {
29147
+ ...layout,
29148
+ edges: layout.edges.map((e, i) => ({
29149
+ ...e,
29150
+ yOffset: yOffset[i],
29151
+ parallelCount: count[i]
29152
+ }))
28187
29153
  };
28188
29154
  }
28189
- 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;
29155
+ 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;
28190
29156
  var init_layout5 = __esm({
28191
29157
  "src/boxes-and-lines/layout.ts"() {
28192
29158
  "use strict";
28193
29159
  init_text_measure();
28194
29160
  init_notes();
28195
- MARGIN3 = 40;
28196
- CONTAINER_PAD_X3 = 30;
28197
- CONTAINER_PAD_TOP2 = 40;
28198
- CONTAINER_PAD_BOTTOM3 = 24;
29161
+ MARGIN4 = 40;
28199
29162
  MAX_PARALLEL_EDGES = 5;
28200
29163
  PARALLEL_SPACING = 22;
28201
29164
  PHI = 1.618;
@@ -28212,8 +29175,6 @@ var init_layout5 = __esm({
28212
29175
  LABEL_PAD = 12;
28213
29176
  VALUE_ROW_FONT = 11;
28214
29177
  VALUE_ROW_H = SEPARATOR_GAP5 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
28215
- elkInstance = null;
28216
- CROSSINGS_FORGIVENESS = 1;
28217
29178
  }
28218
29179
  });
28219
29180
 
@@ -28368,7 +29329,7 @@ function layoutMindmap(parsed, _palette, options) {
28368
29329
  leafWidth: ctx.structural(LEAF_WIDTH),
28369
29330
  hGap: ctx.aesthetic(H_GAP2),
28370
29331
  vGap: ctx.aesthetic(V_GAP2),
28371
- margin: ctx.aesthetic(MARGIN4),
29332
+ margin: ctx.aesthetic(MARGIN5),
28372
29333
  multiRootGap: ctx.aesthetic(MULTI_ROOT_GAP)
28373
29334
  };
28374
29335
  populateDepthCache(roots);
@@ -28746,7 +29707,7 @@ function populateDepthCache(roots) {
28746
29707
  };
28747
29708
  walk(roots, 0);
28748
29709
  }
28749
- 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;
29710
+ 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;
28750
29711
  var init_layout6 = __esm({
28751
29712
  "src/mindmap/layout.ts"() {
28752
29713
  "use strict";
@@ -28762,7 +29723,7 @@ var init_layout6 = __esm({
28762
29723
  NODE_V_PAD = 10;
28763
29724
  H_GAP2 = 40;
28764
29725
  V_GAP2 = 12;
28765
- MARGIN4 = 40;
29726
+ MARGIN5 = 40;
28766
29727
  MULTI_ROOT_GAP = 60;
28767
29728
  nodeDepthCache = /* @__PURE__ */ new Map();
28768
29729
  }
@@ -30037,7 +30998,7 @@ __export(layout_exports8, {
30037
30998
  layoutC4Deployment: () => layoutC4Deployment,
30038
30999
  rollUpContextRelationships: () => rollUpContextRelationships
30039
31000
  });
30040
- import dagre4 from "@dagrejs/dagre";
31001
+ import dagre5 from "@dagrejs/dagre";
30041
31002
  function computeEdgePenalty(edgeList, nodePositions, degrees, nodeGeometry) {
30042
31003
  let penalty = 0;
30043
31004
  for (const edge of edgeList) {
@@ -30462,7 +31423,7 @@ function layoutC4Context(parsed, activeTagGroup) {
30462
31423
  }
30463
31424
  const contextRels = rollUpContextRelationships(parsed);
30464
31425
  const spacing = computeAdaptiveSpacing(contextRels);
30465
- const g = new dagre4.graphlib.Graph();
31426
+ const g = new dagre5.graphlib.Graph();
30466
31427
  g.setGraph({
30467
31428
  rankdir: "TB",
30468
31429
  nodesep: spacing.nodesep,
@@ -30483,7 +31444,7 @@ function layoutC4Context(parsed, activeTagGroup) {
30483
31444
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
30484
31445
  }
30485
31446
  }
30486
- dagre4.layout(g);
31447
+ dagre5.layout(g);
30487
31448
  reduceCrossings(
30488
31449
  g,
30489
31450
  validRels.map((r) => ({ source: r.sourceName, target: r.targetName }))
@@ -30550,8 +31511,8 @@ function layoutC4Context(parsed, activeTagGroup) {
30550
31511
  }
30551
31512
  }
30552
31513
  if (nodes.length > 0) {
30553
- const shiftX = MARGIN5 - minX;
30554
- const shiftY = MARGIN5 - minY;
31514
+ const shiftX = MARGIN6 - minX;
31515
+ const shiftY = MARGIN6 - minY;
30555
31516
  for (const node of nodes) {
30556
31517
  node.x += shiftX;
30557
31518
  node.y += shiftY;
@@ -30563,12 +31524,12 @@ function layoutC4Context(parsed, activeTagGroup) {
30563
31524
  }
30564
31525
  }
30565
31526
  }
30566
- let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN5 * 2 : 0;
30567
- let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN5 * 2 : 0;
31527
+ let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN6 * 2 : 0;
31528
+ let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN6 * 2 : 0;
30568
31529
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
30569
31530
  if (legendGroups.length > 0) {
30570
- const legendY = totalHeight + MARGIN5;
30571
- let legendX = MARGIN5;
31531
+ const legendY = totalHeight + MARGIN6;
31532
+ let legendX = MARGIN6;
30572
31533
  for (const lg of legendGroups) {
30573
31534
  lg.x = legendX;
30574
31535
  lg.y = legendY;
@@ -30661,7 +31622,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30661
31622
  }
30662
31623
  }
30663
31624
  const hasGroups = elementToGroup.size > 0;
30664
- const g = hasGroups ? new dagre4.graphlib.Graph({ compound: true }) : new dagre4.graphlib.Graph();
31625
+ const g = hasGroups ? new dagre5.graphlib.Graph({ compound: true }) : new dagre5.graphlib.Graph();
30665
31626
  g.setDefaultEdgeLabel(() => ({}));
30666
31627
  if (hasGroups) {
30667
31628
  const seenGroups = /* @__PURE__ */ new Set();
@@ -30739,7 +31700,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30739
31700
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
30740
31701
  }
30741
31702
  }
30742
- dagre4.layout(g);
31703
+ dagre5.layout(g);
30743
31704
  const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
30744
31705
  reduceCrossings(
30745
31706
  g,
@@ -30900,8 +31861,8 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30900
31861
  if (pt.y > maxY) maxY = pt.y;
30901
31862
  }
30902
31863
  }
30903
- const shiftX = MARGIN5 - minX;
30904
- const shiftY = MARGIN5 - minY;
31864
+ const shiftX = MARGIN6 - minX;
31865
+ const shiftY = MARGIN6 - minY;
30905
31866
  for (const node of nodes) {
30906
31867
  node.x += shiftX;
30907
31868
  node.y += shiftY;
@@ -30918,12 +31879,12 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30918
31879
  pt.y += shiftY;
30919
31880
  }
30920
31881
  }
30921
- let totalWidth = maxX - minX + MARGIN5 * 2;
30922
- let totalHeight = maxY - minY + MARGIN5 * 2;
31882
+ let totalWidth = maxX - minX + MARGIN6 * 2;
31883
+ let totalHeight = maxY - minY + MARGIN6 * 2;
30923
31884
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
30924
31885
  if (legendGroups.length > 0) {
30925
- const legendY = totalHeight + MARGIN5;
30926
- let legendX = MARGIN5;
31886
+ const legendY = totalHeight + MARGIN6;
31887
+ let legendX = MARGIN6;
30927
31888
  for (const lg of legendGroups) {
30928
31889
  lg.x = legendX;
30929
31890
  lg.y = legendY;
@@ -31068,7 +32029,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31068
32029
  }
31069
32030
  }
31070
32031
  const hasGroups = elementToGroup.size > 0;
31071
- const g = hasGroups ? new dagre4.graphlib.Graph({ compound: true }) : new dagre4.graphlib.Graph();
32032
+ const g = hasGroups ? new dagre5.graphlib.Graph({ compound: true }) : new dagre5.graphlib.Graph();
31072
32033
  g.setDefaultEdgeLabel(() => ({}));
31073
32034
  if (hasGroups) {
31074
32035
  const seenGroups = /* @__PURE__ */ new Set();
@@ -31152,7 +32113,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31152
32113
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
31153
32114
  }
31154
32115
  }
31155
- dagre4.layout(g);
32116
+ dagre5.layout(g);
31156
32117
  const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
31157
32118
  reduceCrossings(
31158
32119
  g,
@@ -31315,8 +32276,8 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31315
32276
  if (pt.y > maxY) maxY = pt.y;
31316
32277
  }
31317
32278
  }
31318
- const shiftX = MARGIN5 - minX;
31319
- const shiftY = MARGIN5 - minY;
32279
+ const shiftX = MARGIN6 - minX;
32280
+ const shiftY = MARGIN6 - minY;
31320
32281
  for (const node of nodes) {
31321
32282
  node.x += shiftX;
31322
32283
  node.y += shiftY;
@@ -31333,12 +32294,12 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31333
32294
  pt.y += shiftY;
31334
32295
  }
31335
32296
  }
31336
- let totalWidth = maxX - minX + MARGIN5 * 2;
31337
- let totalHeight = maxY - minY + MARGIN5 * 2;
32297
+ let totalWidth = maxX - minX + MARGIN6 * 2;
32298
+ let totalHeight = maxY - minY + MARGIN6 * 2;
31338
32299
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
31339
32300
  if (legendGroups.length > 0) {
31340
- const legendY = totalHeight + MARGIN5;
31341
- let legendX = MARGIN5;
32301
+ const legendY = totalHeight + MARGIN6;
32302
+ let legendX = MARGIN6;
31342
32303
  for (const lg of legendGroups) {
31343
32304
  lg.x = legendX;
31344
32305
  lg.y = legendY;
@@ -31444,7 +32405,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31444
32405
  for (const r of refEntries) {
31445
32406
  nameToElement.set(r.element.name, r.element);
31446
32407
  }
31447
- const g = new dagre4.graphlib.Graph({ compound: true });
32408
+ const g = new dagre5.graphlib.Graph({ compound: true });
31448
32409
  g.setDefaultEdgeLabel(() => ({}));
31449
32410
  for (const [infraId] of infraIds) {
31450
32411
  g.setNode(infraId, {});
@@ -31488,7 +32449,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31488
32449
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
31489
32450
  }
31490
32451
  }
31491
- dagre4.layout(g);
32452
+ dagre5.layout(g);
31492
32453
  const nodeInfraMap = /* @__PURE__ */ new Map();
31493
32454
  for (const r of refEntries) nodeInfraMap.set(r.element.name, r.infraId);
31494
32455
  reduceCrossings(
@@ -31626,8 +32587,8 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31626
32587
  if (pt.y > maxY) maxY = pt.y;
31627
32588
  }
31628
32589
  }
31629
- const shiftX = MARGIN5 - minX;
31630
- const shiftY = MARGIN5 - minY;
32590
+ const shiftX = MARGIN6 - minX;
32591
+ const shiftY = MARGIN6 - minY;
31631
32592
  for (const node of nodes) {
31632
32593
  node.x += shiftX;
31633
32594
  node.y += shiftY;
@@ -31642,12 +32603,12 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31642
32603
  pt.y += shiftY;
31643
32604
  }
31644
32605
  }
31645
- let totalWidth = maxX - minX + MARGIN5 * 2;
31646
- let totalHeight = maxY - minY + MARGIN5 * 2;
32606
+ let totalWidth = maxX - minX + MARGIN6 * 2;
32607
+ let totalHeight = maxY - minY + MARGIN6 * 2;
31647
32608
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
31648
32609
  if (legendGroups.length > 0) {
31649
- const legendY = totalHeight + MARGIN5;
31650
- let legendX = MARGIN5;
32610
+ const legendY = totalHeight + MARGIN6;
32611
+ let legendX = MARGIN6;
31651
32612
  for (const lg of legendGroups) {
31652
32613
  lg.x = legendX;
31653
32614
  lg.y = legendY;
@@ -31667,7 +32628,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31667
32628
  height: totalHeight
31668
32629
  };
31669
32630
  }
31670
- 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;
32631
+ 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;
31671
32632
  var init_layout8 = __esm({
31672
32633
  "src/c4/layout.ts"() {
31673
32634
  "use strict";
@@ -31687,7 +32648,7 @@ var init_layout8 = __esm({
31687
32648
  CARD_H_PAD3 = 20;
31688
32649
  META_LINE_HEIGHT5 = 16;
31689
32650
  META_FONT_SIZE5 = 11;
31690
- MARGIN5 = 40;
32651
+ MARGIN6 = 40;
31691
32652
  BOUNDARY_PAD = 40;
31692
32653
  GROUP_BOUNDARY_PAD = 24;
31693
32654
  EDGE_NODE_COLLISION_WEIGHT = 5e3;
@@ -32722,7 +33683,7 @@ var layout_exports9 = {};
32722
33683
  __export(layout_exports9, {
32723
33684
  layoutGraph: () => layoutGraph
32724
33685
  });
32725
- import dagre5 from "@dagrejs/dagre";
33686
+ import dagre6 from "@dagrejs/dagre";
32726
33687
  function computeNodeWidth(label, shape) {
32727
33688
  if (shape === "pseudostate") return 24;
32728
33689
  const base = Math.max(120, label.length * 9 + 40);
@@ -32756,7 +33717,7 @@ function layoutGraph(graph, options) {
32756
33717
  if (allNodes.length === 0) {
32757
33718
  return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
32758
33719
  }
32759
- const g = new dagre5.graphlib.Graph({ compound: true });
33720
+ const g = new dagre6.graphlib.Graph({ compound: true });
32760
33721
  g.setGraph({
32761
33722
  rankdir: graph.direction,
32762
33723
  nodesep: 50,
@@ -32807,7 +33768,7 @@ function layoutGraph(graph, options) {
32807
33768
  label: edge.label ?? ""
32808
33769
  });
32809
33770
  }
32810
- dagre5.layout(g);
33771
+ dagre6.layout(g);
32811
33772
  const collapsedGroupIds = collapsedChildCounts ? new Set(collapsedChildCounts.keys()) : /* @__PURE__ */ new Set();
32812
33773
  const basePositioned = allNodes.map((node) => {
32813
33774
  const pos = g.node(node.id);
@@ -34511,7 +35472,7 @@ __export(layout_exports10, {
34511
35472
  layoutInfra: () => layoutInfra,
34512
35473
  separateGroups: () => separateGroups
34513
35474
  });
34514
- import dagre6 from "@dagrejs/dagre";
35475
+ import dagre7 from "@dagrejs/dagre";
34515
35476
  function countDisplayProps(node, expanded, options) {
34516
35477
  if (!expanded) return 0;
34517
35478
  let count = node.properties.filter((p) => DISPLAY_KEYS.has(p.key)).length;
@@ -34813,7 +35774,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
34813
35774
  };
34814
35775
  }
34815
35776
  const isLR = computed.direction !== "TB";
34816
- const g = new dagre6.graphlib.Graph();
35777
+ const g = new dagre7.graphlib.Graph();
34817
35778
  g.setGraph({
34818
35779
  rankdir: computed.direction === "TB" ? "TB" : "LR",
34819
35780
  nodesep: isLR ? 70 : 60,
@@ -34864,7 +35825,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
34864
35825
  g.setEdge(edge.sourceId, edge.targetId, { label: edge.label });
34865
35826
  }
34866
35827
  }
34867
- dagre6.layout(g);
35828
+ dagre7.layout(g);
34868
35829
  const layoutNodes = computed.nodes.map(
34869
35830
  (node) => {
34870
35831
  const pos = g.node(node.id);
@@ -36606,17 +37567,17 @@ function mulberry32(seed) {
36606
37567
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
36607
37568
  };
36608
37569
  }
36609
- function standardNormal(rng) {
37570
+ function standardNormal(rng3) {
36610
37571
  let u = 0;
36611
37572
  let v = 0;
36612
- while (u === 0) u = rng();
36613
- while (v === 0) v = rng();
37573
+ while (u === 0) u = rng3();
37574
+ while (v === 0) v = rng3();
36614
37575
  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
36615
37576
  }
36616
- function gamma(shape, rng) {
37577
+ function gamma(shape, rng3) {
36617
37578
  if (shape < 1) {
36618
- const u = rng();
36619
- return gamma(shape + 1, rng) * Math.pow(u, 1 / shape);
37579
+ const u = rng3();
37580
+ return gamma(shape + 1, rng3) * Math.pow(u, 1 / shape);
36620
37581
  }
36621
37582
  const d = shape - 1 / 3;
36622
37583
  const c = 1 / Math.sqrt(9 * d);
@@ -36624,29 +37585,29 @@ function gamma(shape, rng) {
36624
37585
  let x;
36625
37586
  let v;
36626
37587
  do {
36627
- x = standardNormal(rng);
37588
+ x = standardNormal(rng3);
36628
37589
  v = 1 + c * x;
36629
37590
  } while (v <= 0);
36630
37591
  v = v * v * v;
36631
- const u = rng();
37592
+ const u = rng3();
36632
37593
  if (u < 1 - 0.0331 * x * x * x * x) return d * v;
36633
37594
  if (Math.log(u) < x * x / 2 + d * (1 - v + Math.log(v))) return d * v;
36634
37595
  }
36635
37596
  }
36636
- function sampleBeta(alpha, beta, rng) {
36637
- const x = gamma(alpha, rng);
36638
- const y = gamma(beta, rng);
37597
+ function sampleBeta(alpha, beta, rng3) {
37598
+ const x = gamma(alpha, rng3);
37599
+ const y = gamma(beta, rng3);
36639
37600
  return x / (x + y);
36640
37601
  }
36641
- function sampleBetaPert(o, m, p, rng) {
37602
+ function sampleBetaPert(o, m, p, rng3) {
36642
37603
  if (o === p) return m;
36643
37604
  const range = p - o;
36644
37605
  const alpha = 1 + 4 * (m - o) / range;
36645
37606
  const beta = 1 + 4 * (p - m) / range;
36646
- return o + sampleBeta(alpha, beta, rng) * range;
37607
+ return o + sampleBeta(alpha, beta, rng3) * range;
36647
37608
  }
36648
37609
  function simulate(resolved, expanded, _predecessors, _successors, topo, terminals, poisoned, opts) {
36649
- const rng = mulberry32(opts.seed);
37610
+ const rng3 = mulberry32(opts.seed);
36650
37611
  const expById = /* @__PURE__ */ new Map();
36651
37612
  for (const e of expanded) expById.set(e.id, e);
36652
37613
  const completionTimes = new Array(opts.trials);
@@ -36669,7 +37630,7 @@ function simulate(resolved, expanded, _predecessors, _successors, topo, terminal
36669
37630
  } else if (exp.o === exp.p) {
36670
37631
  duration = exp.m;
36671
37632
  } else {
36672
- duration = sampleBetaPert(exp.o, exp.m, exp.p, rng);
37633
+ duration = sampleBetaPert(exp.o, exp.m, exp.p, rng3);
36673
37634
  }
36674
37635
  let esLower = 0;
36675
37636
  let efLower = -Infinity;
@@ -37658,7 +38619,7 @@ __export(layout_exports11, {
37658
38619
  layoutPert: () => layoutPert,
37659
38620
  relayoutPert: () => relayoutPert
37660
38621
  });
37661
- import dagre7 from "@dagrejs/dagre";
38622
+ import dagre8 from "@dagrejs/dagre";
37662
38623
  function computeNodeSizing(resolved) {
37663
38624
  const unit = resolved.options.timeUnit;
37664
38625
  const sprintMode = resolved.options.sprintMode;
@@ -37776,7 +38737,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
37776
38737
  }
37777
38738
  }
37778
38739
  const dagreId = (id) => memberToGroup.get(id) ?? id;
37779
- const g = new dagre7.graphlib.Graph();
38740
+ const g = new dagre8.graphlib.Graph();
37780
38741
  g.setGraph({
37781
38742
  rankdir: resolved.options.direction,
37782
38743
  nodesep: 50,
@@ -37815,7 +38776,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
37815
38776
  seenEdges.add(k);
37816
38777
  g.setEdge(src, tgt, {});
37817
38778
  }
37818
- dagre7.layout(g);
38779
+ dagre8.layout(g);
37819
38780
  const swimApplied = applySwimLanes(
37820
38781
  g,
37821
38782
  resolved,
@@ -38161,7 +39122,7 @@ function reduceCrossings2(g, direction) {
38161
39122
  buckets.get(key).push(id);
38162
39123
  }
38163
39124
  const edges = g.edges().map((e) => ({ v: e.v, w: e.w }));
38164
- const countCrossings2 = () => {
39125
+ const countCrossings = () => {
38165
39126
  let total = 0;
38166
39127
  for (let i = 0; i < edges.length; i++) {
38167
39128
  const a = edges[i];
@@ -38174,13 +39135,13 @@ function reduceCrossings2(g, direction) {
38174
39135
  const b1 = g.node(b.v);
38175
39136
  const b2 = g.node(b.w);
38176
39137
  if (!b1 || !b2) continue;
38177
- if (segmentsCross2(a1, a2, b1, b2)) total++;
39138
+ if (segmentsCross(a1, a2, b1, b2)) total++;
38178
39139
  }
38179
39140
  }
38180
39141
  return total;
38181
39142
  };
38182
39143
  const MAX_ITER = 8;
38183
- let baseline = countCrossings2();
39144
+ let baseline = countCrossings();
38184
39145
  if (baseline === 0) return;
38185
39146
  for (let iter = 0; iter < MAX_ITER; iter++) {
38186
39147
  let improved = false;
@@ -38198,7 +39159,7 @@ function reduceCrossings2(g, direction) {
38198
39159
  const bv = bn[slotAxis];
38199
39160
  an[slotAxis] = bv;
38200
39161
  bn[slotAxis] = av;
38201
- const after = countCrossings2();
39162
+ const after = countCrossings();
38202
39163
  if (after < baseline) {
38203
39164
  baseline = after;
38204
39165
  improved = true;
@@ -38219,7 +39180,7 @@ function reduceCrossings2(g, direction) {
38219
39180
  data.points = smoothEdge(src, tgt, direction);
38220
39181
  }
38221
39182
  }
38222
- function segmentsCross2(a1, a2, b1, b2) {
39183
+ function segmentsCross(a1, a2, b1, b2) {
38223
39184
  const ccw = (p, q, r) => (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x);
38224
39185
  const d1 = ccw(b1, b2, a1);
38225
39186
  const d2 = ccw(b1, b2, a2);
@@ -47760,10 +48721,10 @@ function tierBand(maxSpanDeg) {
47760
48721
  }
47761
48722
  function labelBudget(width, height, band) {
47762
48723
  const bandCap = {
47763
- world: 6,
47764
- continental: 5,
47765
- regional: 4,
47766
- local: 3
48724
+ world: 7,
48725
+ continental: 6,
48726
+ regional: 5,
48727
+ local: 4
47767
48728
  };
47768
48729
  const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
47769
48730
  return Math.max(0, Math.min(area2, bandCap[band]));
@@ -47896,8 +48857,11 @@ function placeContextLabels(args) {
47896
48857
  color: waterColor,
47897
48858
  fontSize: FONT,
47898
48859
  // water names keep the base font (no footprint to scale on)
47899
- // Water before any country (×1000), then by tier, then kind, then name.
47900
- sort: tier * 10 + KIND_ORDER[kind]
48860
+ // Orientation-value bands (lower = earlier): MAJOR water (oceans + major
48861
+ // seas, tier ≤ 1) leads at `tier*10+kind` (0..~16); MINOR water (tier ≥ 2:
48862
+ // smaller seas, bays, gulfs, straits) drops to band 2 (2000+) — BELOW the
48863
+ // country band (1000+), so a big country outranks a minor basin.
48864
+ sort: (tier <= 1 ? 0 : 2e3) + tier * 10 + KIND_ORDER[kind]
47901
48865
  });
47902
48866
  }
47903
48867
  const ranked = countries.map((c) => {
@@ -47910,7 +48874,7 @@ function placeContextLabels(args) {
47910
48874
  let ci = 0;
47911
48875
  for (const r of ranked) {
47912
48876
  const { c, w, h, area: area2 } = r;
47913
- if (w > width * 0.66 || h > height * 0.66) continue;
48877
+ if (!c.curatedAnchor && w > width * 0.66 && h > height * 0.66) continue;
47914
48878
  if (!insideViewport(c.anchor, width, height)) continue;
47915
48879
  const sizeFrac = Math.sqrt(area2) / canvasLinear;
47916
48880
  const t = Math.min(
@@ -47935,15 +48899,23 @@ function placeContextLabels(args) {
47935
48899
  letterSpacing: 0,
47936
48900
  color,
47937
48901
  fontSize,
47938
- // Always after every water body (+1e6); larger area = earlier.
47939
- sort: 1e6 + ci++
48902
+ // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
48903
+ // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
48904
+ // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
48905
+ // Campeche) yet never displaces an ocean. Larger area = earlier within the
48906
+ // band (`ci` is the area-desc rank index).
48907
+ sort: 1e3 + ci++
47940
48908
  });
47941
48909
  }
47942
48910
  candidates.sort((a, b) => a.sort - b.sort);
47943
48911
  const placed = [];
47944
48912
  const placedRects = [];
48913
+ const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
48914
+ const waterCap = budget - Math.min(2, countryCount);
48915
+ let waterPlaced = 0;
47945
48916
  for (const cand of candidates) {
47946
48917
  if (placed.length >= budget) break;
48918
+ if (cand.italic && waterPlaced >= waterCap) continue;
47947
48919
  const rect = rectAround(
47948
48920
  cand.cx,
47949
48921
  cand.cy,
@@ -47970,6 +48942,7 @@ function placeContextLabels(args) {
47970
48942
  if (collides(rect)) continue;
47971
48943
  if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
47972
48944
  placedRects.push(rect);
48945
+ if (cand.italic) waterPlaced++;
47973
48946
  placed.push({
47974
48947
  x: cand.cx,
47975
48948
  y: cand.cy,
@@ -49943,16 +50916,16 @@ function layoutMap(resolved, data, size, opts) {
49943
50916
  countryCandidates.push({
49944
50917
  name: f.properties?.name ?? iso,
49945
50918
  bbox: [x0, y0, x1, y1],
49946
- anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
50919
+ anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
50920
+ curatedAnchor: !!anchorLngLat
49947
50921
  });
49948
50922
  }
49949
50923
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
49950
50924
  (id) => id.startsWith("US-")
49951
50925
  );
49952
50926
  if (usLayer && framedStateContainers) {
49953
- const containerSet = new Set(resolved.poiFrameContainers);
49954
50927
  for (const [iso, f] of usLayer) {
49955
- if (containerSet.has(iso) || regionById.has(iso)) continue;
50928
+ if (regionById.has(iso)) continue;
49956
50929
  const viewF = cullFeatureToView(f);
49957
50930
  if (!viewF) continue;
49958
50931
  const b = path.bounds(viewF);
@@ -50080,8 +51053,15 @@ var init_layout15 = __esm({
50080
51053
  W_MAX = 8;
50081
51054
  FONT2 = 11;
50082
51055
  WORLD_LABEL_ANCHORS = {
50083
- US: [-98.5, 39.5]
51056
+ US: [-98.5, 39.5],
50084
51057
  // CONUS geographic centre (near Lebanon, Kansas)
51058
+ // Russia crosses the antimeridian (Chukotka at ~170°W), so on a non-global
51059
+ // (e.g. Europe) projection its geometry smears across the whole frame and the
51060
+ // area-weighted centroid lands mid-map (over Europe) — useless as a label
51061
+ // anchor. Pin it to European Russia (~Volga) so a Europe view labels visible
51062
+ // western Russia on its eastern margin; on a world view this still sits over
51063
+ // Russian land. (See the curated-anchor smear-gate bypass in context-labels.)
51064
+ RU: [45, 58]
50085
51065
  };
50086
51066
  MAX_CLUSTER_EXTENT_FACTOR = 0.18;
50087
51067
  MAX_COLUMN_ROWS = 7;