@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/advanced.js CHANGED
@@ -27682,12 +27682,1344 @@ var init_renderer6 = __esm({
27682
27682
  }
27683
27683
  });
27684
27684
 
27685
+ // src/boxes-and-lines/layout-layered.ts
27686
+ function rng(s) {
27687
+ return () => {
27688
+ s |= 0;
27689
+ s = s + 1831565813 | 0;
27690
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
27691
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
27692
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
27693
+ };
27694
+ }
27695
+ function layeredCandidates(parsed, sizes, opts) {
27696
+ if (parsed.groups.length > 0) return [];
27697
+ const nCount = parsed.nodes.length;
27698
+ if (nCount < 3 || nCount > 40) return [];
27699
+ const isTB = parsed.direction === "TB";
27700
+ const nodesep = opts?.nodesep ?? NODESEP;
27701
+ const ranksep = opts?.ranksep ?? RANKSEP;
27702
+ const backEdgeSide = opts?.backEdgeSide ?? "nearest";
27703
+ const restarts = opts?.restarts ?? (nCount <= 14 ? 28 : nCount <= 25 ? 14 : 6);
27704
+ const keepK = opts?.keepK ?? 3;
27705
+ const labels = parsed.nodes.map((n) => n.label);
27706
+ const labelSet = new Set(labels);
27707
+ const edges = parsed.edges.map((e, i) => ({ e, i })).filter(
27708
+ ({ e }) => labelSet.has(e.source) && labelSet.has(e.target) && e.source !== e.target
27709
+ );
27710
+ if (edges.length === 0) return [];
27711
+ const adj = /* @__PURE__ */ new Map();
27712
+ for (const l of labels) adj.set(l, []);
27713
+ for (const { e, i } of edges)
27714
+ adj.get(e.source).push({ to: e.target, idx: i });
27715
+ const reversed = /* @__PURE__ */ new Set();
27716
+ const state = /* @__PURE__ */ new Map();
27717
+ for (const l of labels) state.set(l, 0);
27718
+ const dfs = (u) => {
27719
+ state.set(u, 1);
27720
+ for (const { to, idx } of adj.get(u)) {
27721
+ const st = state.get(to);
27722
+ if (st === 1)
27723
+ reversed.add(idx);
27724
+ else if (st === 0) dfs(to);
27725
+ }
27726
+ state.set(u, 2);
27727
+ };
27728
+ for (const l of labels) if (state.get(l) === 0) dfs(l);
27729
+ const dag = edges.map(
27730
+ ({ 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 }
27731
+ );
27732
+ const outDag = /* @__PURE__ */ new Map();
27733
+ const inDeg = /* @__PURE__ */ new Map();
27734
+ for (const l of labels) {
27735
+ outDag.set(l, []);
27736
+ inDeg.set(l, 0);
27737
+ }
27738
+ for (const d of dag) {
27739
+ outDag.get(d.from).push(d.to);
27740
+ inDeg.set(d.to, (inDeg.get(d.to) ?? 0) + 1);
27741
+ }
27742
+ const rank = /* @__PURE__ */ new Map();
27743
+ for (const l of labels) rank.set(l, 0);
27744
+ const queue = labels.filter((l) => (inDeg.get(l) ?? 0) === 0);
27745
+ const indeg2 = new Map(inDeg);
27746
+ const topo = [];
27747
+ while (queue.length) {
27748
+ const u = queue.shift();
27749
+ topo.push(u);
27750
+ for (const v of outDag.get(u)) {
27751
+ rank.set(v, Math.max(rank.get(v), rank.get(u) + 1));
27752
+ indeg2.set(v, indeg2.get(v) - 1);
27753
+ if (indeg2.get(v) === 0) queue.push(v);
27754
+ }
27755
+ }
27756
+ if (topo.length !== labels.length) return [];
27757
+ const maxRank = Math.max(...labels.map((l) => rank.get(l)));
27758
+ const node = /* @__PURE__ */ new Map();
27759
+ for (const n of parsed.nodes) {
27760
+ const s = sizes.get(n.label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
27761
+ node.set(n.label, {
27762
+ id: n.label,
27763
+ dummy: false,
27764
+ rank: rank.get(n.label),
27765
+ cross: 0,
27766
+ thick: isTB ? s.width : s.height,
27767
+ depth: isTB ? s.height : s.width,
27768
+ realW: s.width,
27769
+ realH: s.height
27770
+ });
27771
+ }
27772
+ const segs = [];
27773
+ const chains = [];
27774
+ const backEdges = [];
27775
+ for (const d of dag) {
27776
+ if (d.rev) {
27777
+ backEdges.push({ edgeIdx: d.idx, src: d.to, tgt: d.from });
27778
+ continue;
27779
+ }
27780
+ const r0 = rank.get(d.from);
27781
+ const r1 = rank.get(d.to);
27782
+ const dagChain = [d.from];
27783
+ let prev = d.from;
27784
+ for (let r = r0 + 1; r < r1; r++) {
27785
+ const id = `__d${d.idx}_${r}`;
27786
+ node.set(id, {
27787
+ id,
27788
+ dummy: true,
27789
+ rank: r,
27790
+ cross: 0,
27791
+ thick: DUMMY_THICK,
27792
+ depth: 0,
27793
+ realW: 0,
27794
+ realH: 0
27795
+ });
27796
+ segs.push({ a: prev, b: id });
27797
+ dagChain.push(id);
27798
+ prev = id;
27799
+ }
27800
+ segs.push({ a: prev, b: d.to });
27801
+ dagChain.push(d.to);
27802
+ chains.push({
27803
+ edgeIdx: d.idx,
27804
+ chain: d.rev ? dagChain.slice().reverse() : dagChain
27805
+ });
27806
+ }
27807
+ const ranks = Array.from({ length: maxRank + 1 }, () => []);
27808
+ for (const n of node.values()) ranks[n.rank].push(n.id);
27809
+ const up = /* @__PURE__ */ new Map();
27810
+ const down = /* @__PURE__ */ new Map();
27811
+ for (const id of node.keys()) {
27812
+ up.set(id, []);
27813
+ down.set(id, []);
27814
+ }
27815
+ for (const s of segs) {
27816
+ down.get(s.a).push(s.b);
27817
+ up.get(s.b).push(s.a);
27818
+ }
27819
+ const orderOf = /* @__PURE__ */ new Map();
27820
+ const applyOrder = (ord) => {
27821
+ for (const id of node.keys()) orderOf.set(id, ord.get(id));
27822
+ };
27823
+ const gapCrossings = (r) => {
27824
+ const lower = ranks[r + 1];
27825
+ if (!lower) return 0;
27826
+ const es = [];
27827
+ for (const a of ranks[r]) {
27828
+ const pa = orderOf.get(a);
27829
+ for (const b of down.get(a)) es.push({ p: pa, q: orderOf.get(b) });
27830
+ }
27831
+ let c = 0;
27832
+ for (let i = 0; i < es.length; i++)
27833
+ for (let j = i + 1; j < es.length; j++) {
27834
+ const A = es[i], B = es[j];
27835
+ if ((A.p - B.p) * (A.q - B.q) < 0) c++;
27836
+ }
27837
+ return c;
27838
+ };
27839
+ const totalCrossings = () => {
27840
+ let c = 0;
27841
+ for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
27842
+ return c;
27843
+ };
27844
+ const median = (vals) => {
27845
+ if (vals.length === 0) return -1;
27846
+ vals.sort((x, y) => x - y);
27847
+ const m = Math.floor(vals.length / 2);
27848
+ if (vals.length % 2 === 1) return vals[m];
27849
+ if (vals.length === 2) return (vals[0] + vals[1]) / 2;
27850
+ const left = vals[m - 1] - vals[0];
27851
+ const right = vals[vals.length - 1] - vals[m];
27852
+ return left + right === 0 ? (vals[m - 1] + vals[m]) / 2 : (vals[m - 1] * right + vals[m] * left) / (left + right);
27853
+ };
27854
+ const wmedianRank = (r, useUp) => {
27855
+ const ids = ranks[r];
27856
+ const neigh = useUp ? up : down;
27857
+ const med = /* @__PURE__ */ new Map();
27858
+ for (const id of ids)
27859
+ med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
27860
+ const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
27861
+ const out = new Array(ids.length).fill(null);
27862
+ for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
27863
+ let mi = 0;
27864
+ for (let slot = 0; slot < out.length; slot++)
27865
+ if (out[slot] === null) out[slot] = movable[mi++];
27866
+ const final = out;
27867
+ final.forEach((id, i) => orderOf.set(id, i));
27868
+ ranks[r] = final;
27869
+ };
27870
+ const transpose = () => {
27871
+ let improved = true;
27872
+ let guard = 0;
27873
+ while (improved && guard++ < 12) {
27874
+ improved = false;
27875
+ for (let r = 0; r <= maxRank; r++) {
27876
+ const ids = ranks[r];
27877
+ for (let i = 0; i < ids.length - 1; i++) {
27878
+ const before = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
27879
+ const a = ids[i], b = ids[i + 1];
27880
+ ids[i] = b;
27881
+ ids[i + 1] = a;
27882
+ orderOf.set(b, i);
27883
+ orderOf.set(a, i + 1);
27884
+ const after = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
27885
+ if (after < before) {
27886
+ improved = true;
27887
+ } else {
27888
+ ids[i] = a;
27889
+ ids[i + 1] = b;
27890
+ orderOf.set(a, i);
27891
+ orderOf.set(b, i + 1);
27892
+ }
27893
+ }
27894
+ }
27895
+ }
27896
+ };
27897
+ const initOrder = (seed) => {
27898
+ const r = seed === 0 ? null : rng(seed);
27899
+ for (let rr = 0; rr <= maxRank; rr++) {
27900
+ const ids = ranks[rr].slice();
27901
+ if (r) {
27902
+ for (let i = ids.length - 1; i > 0; i--) {
27903
+ const j = Math.floor(r() * (i + 1));
27904
+ [ids[i], ids[j]] = [ids[j], ids[i]];
27905
+ }
27906
+ }
27907
+ ranks[rr] = ids;
27908
+ ids.forEach((id, i) => orderOf.set(id, i));
27909
+ }
27910
+ };
27911
+ const found = [];
27912
+ for (let s = 0; s < restarts; s++) {
27913
+ initOrder(s);
27914
+ let best = totalCrossings();
27915
+ let bestSnap = new Map(orderOf);
27916
+ for (let iter = 0; iter < 8; iter++) {
27917
+ const down1 = iter % 2 === 0;
27918
+ if (down1) for (let r = 1; r <= maxRank; r++) wmedianRank(r, true);
27919
+ else for (let r = maxRank - 1; r >= 0; r--) wmedianRank(r, false);
27920
+ transpose();
27921
+ const c = totalCrossings();
27922
+ if (c < best) {
27923
+ best = c;
27924
+ bestSnap = new Map(orderOf);
27925
+ }
27926
+ if (c === 0) break;
27927
+ }
27928
+ applyOrder(bestSnap);
27929
+ for (let r = 0; r <= maxRank; r++)
27930
+ ranks[r].sort((a, b) => orderOf.get(a) - orderOf.get(b));
27931
+ const sig = ranks.map((r) => r.join(",")).join("|");
27932
+ if (!found.some((f) => f.sig === sig))
27933
+ found.push({ sig, cross: best, order: new Map(bestSnap) });
27934
+ }
27935
+ found.sort((a, b) => a.cross - b.cross);
27936
+ const keep = found.slice(0, keepK);
27937
+ if (keep.length === 0) return [];
27938
+ const results = [];
27939
+ for (const cand of keep) {
27940
+ applyOrder(cand.order);
27941
+ const rk = Array.from({ length: maxRank + 1 }, () => []);
27942
+ for (const n of node.values()) rk[n.rank].push(n.id);
27943
+ for (let r = 0; r <= maxRank; r++)
27944
+ rk[r].sort((a, b) => cand.order.get(a) - cand.order.get(b));
27945
+ results.push(realize(rk));
27946
+ }
27947
+ return results;
27948
+ function realize(rk) {
27949
+ const bandDepth = rk.map(
27950
+ (ids) => Math.max(0, ...ids.map((id) => node.get(id).depth))
27951
+ );
27952
+ const bandCenter = [];
27953
+ let acc = MARGIN3;
27954
+ for (let r = 0; r <= maxRank; r++) {
27955
+ bandCenter[r] = acc + bandDepth[r] / 2;
27956
+ acc += bandDepth[r] + ranksep;
27957
+ }
27958
+ for (let r = 0; r <= maxRank; r++) {
27959
+ let x = MARGIN3;
27960
+ for (const id of rk[r]) {
27961
+ const n = node.get(id);
27962
+ n.cross = x + n.thick / 2;
27963
+ x += n.thick + nodesep;
27964
+ }
27965
+ }
27966
+ const ITER = 18;
27967
+ for (let it = 0; it < ITER; it++) {
27968
+ const downPass = it % 2 === 0;
27969
+ const order = downPass ? Array.from({ length: maxRank + 1 }, (_, i) => i) : Array.from({ length: maxRank + 1 }, (_, i) => maxRank - i);
27970
+ for (const r of order) {
27971
+ const ids = rk[r];
27972
+ const desired = ids.map((id) => {
27973
+ const n = node.get(id);
27974
+ let sw = 0, sx2 = 0;
27975
+ for (const nb of up.get(id)) {
27976
+ const w = weight(n, node.get(nb));
27977
+ sw += w;
27978
+ sx2 += w * node.get(nb).cross;
27979
+ }
27980
+ for (const nb of down.get(id)) {
27981
+ const w = weight(n, node.get(nb));
27982
+ sw += w;
27983
+ sx2 += w * node.get(nb).cross;
27984
+ }
27985
+ return sw > 0 ? sx2 / sw : n.cross;
27986
+ });
27987
+ placeWithSeparation(ids, desired);
27988
+ }
27989
+ }
27990
+ let minC = Infinity;
27991
+ for (const n of node.values()) minC = Math.min(minC, n.cross - n.thick / 2);
27992
+ const shift = MARGIN3 - minC;
27993
+ for (const n of node.values()) n.cross += shift;
27994
+ const coord = (n) => isTB ? { x: n.cross, y: bandCenter[n.rank] } : { x: bandCenter[n.rank], y: n.cross };
27995
+ const layoutNodes = parsed.nodes.map((pn) => {
27996
+ const n = node.get(pn.label);
27997
+ const c = coord(n);
27998
+ return {
27999
+ label: pn.label,
28000
+ x: c.x,
28001
+ y: c.y,
28002
+ width: n.realW,
28003
+ height: n.realH
28004
+ };
28005
+ });
28006
+ const chainById = /* @__PURE__ */ new Map();
28007
+ for (const ch of chains) chainById.set(ch.edgeIdx, ch.chain);
28008
+ let minCross = Infinity, maxCross = -Infinity;
28009
+ for (const n of node.values()) {
28010
+ if (n.dummy) continue;
28011
+ minCross = Math.min(minCross, n.cross - n.thick / 2);
28012
+ maxCross = Math.max(maxCross, n.cross + n.thick / 2);
28013
+ }
28014
+ const GAP = 34;
28015
+ const LANE = 28;
28016
+ const sideOf = (b) => {
28017
+ if (backEdgeSide === "left") return -1;
28018
+ if (backEdgeSide === "right") return 1;
28019
+ const d = node.get(b.src).cross - node.get(b.tgt).cross;
28020
+ return d >= 0 ? 1 : -1;
28021
+ };
28022
+ const backSorted = backEdges.map((b) => ({
28023
+ ...b,
28024
+ side: sideOf(b),
28025
+ span: Math.abs(node.get(b.src).rank - node.get(b.tgt).rank)
28026
+ })).sort((a, b) => a.side - b.side || a.span - b.span);
28027
+ const sideCounts = /* @__PURE__ */ new Map();
28028
+ for (const b of backSorted)
28029
+ sideCounts.set(b.side, (sideCounts.get(b.side) ?? 0) + 1);
28030
+ const laneCounter = /* @__PURE__ */ new Map();
28031
+ const backPoints = /* @__PURE__ */ new Map();
28032
+ const faceLim = (depth) => Math.max(0, depth / 2 - 6);
28033
+ const hubIndex = /* @__PURE__ */ new Map();
28034
+ const hubSize = /* @__PURE__ */ new Map();
28035
+ {
28036
+ const counter = /* @__PURE__ */ new Map();
28037
+ for (const b of backSorted) {
28038
+ const key = `${b.side}|${b.tgt}`;
28039
+ const idx = counter.get(key) ?? 0;
28040
+ counter.set(key, idx + 1);
28041
+ hubIndex.set(b.edgeIdx, idx);
28042
+ }
28043
+ for (const [key, v] of counter) hubSize.set(key, v);
28044
+ }
28045
+ for (const b of backSorted) {
28046
+ const s = b.side;
28047
+ const k = laneCounter.get(s) ?? 0;
28048
+ laneCounter.set(s, k + 1);
28049
+ const K = sideCounts.get(s);
28050
+ const src = node.get(b.src);
28051
+ const tgt = node.get(b.tgt);
28052
+ const laneC = s > 0 ? maxCross + GAP + k * LANE : minCross - GAP - k * LANE;
28053
+ const srcCtr = bandCenter[src.rank];
28054
+ const tgtCtr = bandCenter[tgt.rank];
28055
+ const frac = K > 1 ? (k + 1) / (K + 1) : 0;
28056
+ const srcR = srcCtr + frac * faceLim(src.depth);
28057
+ const tgtR = tgtCtr - frac * faceLim(tgt.depth);
28058
+ const m = (c, r) => isTB ? { x: c, y: r } : { x: r, y: c };
28059
+ const off = 10;
28060
+ const srcEdgeC = src.cross + s * (src.thick / 2 + off);
28061
+ const tgtEdgeC = tgt.cross + s * (tgt.thick / 2 + off);
28062
+ const hubK = hubIndex.get(b.edgeIdx);
28063
+ const hubN = hubSize.get(`${s}|${b.tgt}`);
28064
+ if (hubN > 1 && hubK > 0) {
28065
+ const tgtTop = tgtCtr - (tgt.depth / 2 + GAP + (hubK - 1) * LANE);
28066
+ const entryCross = tgt.cross + s * faceLim(tgt.thick) * (hubK / hubN);
28067
+ backPoints.set(b.edgeIdx, [
28068
+ m(src.cross, srcCtr),
28069
+ m(srcEdgeC, srcR),
28070
+ m(laneC, srcR),
28071
+ m(laneC, tgtTop),
28072
+ m(entryCross, tgtTop),
28073
+ m(tgt.cross, tgtCtr)
28074
+ ]);
28075
+ } else {
28076
+ backPoints.set(b.edgeIdx, [
28077
+ m(src.cross, srcCtr),
28078
+ m(srcEdgeC, srcR),
28079
+ m(laneC, srcR),
28080
+ m(laneC, tgtR),
28081
+ m(tgtEdgeC, tgtR),
28082
+ m(tgt.cross, tgtCtr)
28083
+ ]);
28084
+ }
28085
+ }
28086
+ const layoutEdges = [];
28087
+ for (let i = 0; i < parsed.edges.length; i++) {
28088
+ const e = parsed.edges[i];
28089
+ if (!labelSet.has(e.source) || !labelSet.has(e.target)) continue;
28090
+ let points;
28091
+ if (e.source === e.target) {
28092
+ const c = coord(node.get(e.source));
28093
+ points = [c, c];
28094
+ } else if (backPoints.has(i)) {
28095
+ points = backPoints.get(i);
28096
+ } else {
28097
+ const chain = chainById.get(i);
28098
+ if (!chain) continue;
28099
+ points = chain.map((id) => coord(node.get(id)));
28100
+ }
28101
+ layoutEdges.push({
28102
+ source: e.source,
28103
+ target: e.target,
28104
+ ...e.label !== void 0 && { label: e.label },
28105
+ bidirectional: e.bidirectional,
28106
+ lineNumber: e.lineNumber,
28107
+ points,
28108
+ yOffset: 0,
28109
+ parallelCount: 1,
28110
+ metadata: e.metadata
28111
+ });
28112
+ }
28113
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
28114
+ for (const n of layoutNodes) {
28115
+ minX = Math.min(minX, n.x - n.width / 2);
28116
+ minY = Math.min(minY, n.y - n.height / 2);
28117
+ maxX = Math.max(maxX, n.x + n.width / 2);
28118
+ maxY = Math.max(maxY, n.y + n.height / 2);
28119
+ }
28120
+ for (const e of layoutEdges)
28121
+ for (const p of e.points) {
28122
+ minX = Math.min(minX, p.x);
28123
+ minY = Math.min(minY, p.y);
28124
+ maxX = Math.max(maxX, p.x);
28125
+ maxY = Math.max(maxY, p.y);
28126
+ }
28127
+ const sx = MARGIN3 - minX;
28128
+ const sy = MARGIN3 - minY;
28129
+ const shifted = sx !== 0 || sy !== 0 ? {
28130
+ nodes: layoutNodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })),
28131
+ edges: layoutEdges.map((e) => ({
28132
+ ...e,
28133
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
28134
+ }))
28135
+ } : { nodes: layoutNodes, edges: layoutEdges };
28136
+ return {
28137
+ nodes: shifted.nodes,
28138
+ edges: shifted.edges,
28139
+ groups: [],
28140
+ width: maxX + sx + MARGIN3,
28141
+ height: maxY + sy + MARGIN3
28142
+ };
28143
+ }
28144
+ function weight(a, b) {
28145
+ if (a.dummy && b.dummy) return W_DUMMY_DUMMY;
28146
+ if (a.dummy || b.dummy) return W_REAL_DUMMY;
28147
+ return W_REAL_REAL;
28148
+ }
28149
+ function placeWithSeparation(ids, desired) {
28150
+ const n = ids.length;
28151
+ if (n === 0) return;
28152
+ const half = ids.map((id) => node.get(id).thick / 2);
28153
+ const off = [0];
28154
+ for (let i = 1; i < n; i++)
28155
+ off[i] = off[i - 1] + half[i - 1] + nodesep + half[i];
28156
+ const d = desired.map((v, i) => v - off[i]);
28157
+ const blocks = [];
28158
+ for (let i = 0; i < n; i++) {
28159
+ let b = { pos: d[i], count: 1, sumOffset: d[i], first: i };
28160
+ while (blocks.length && blocks[blocks.length - 1].pos >= b.pos) {
28161
+ const prev = blocks.pop();
28162
+ const count = prev.count + b.count;
28163
+ const sumOffset = prev.sumOffset + b.sumOffset;
28164
+ b = { pos: sumOffset / count, count, sumOffset, first: prev.first };
28165
+ }
28166
+ blocks.push(b);
28167
+ }
28168
+ const xs = new Array(n);
28169
+ for (const b of blocks)
28170
+ for (let k = 0; k < b.count; k++) xs[b.first + k] = b.pos;
28171
+ for (let i = 0; i < n; i++) node.get(ids[i]).cross = xs[i] + off[i];
28172
+ }
28173
+ }
28174
+ var NODESEP, RANKSEP, DUMMY_THICK, MARGIN3, W_REAL_REAL, W_REAL_DUMMY, W_DUMMY_DUMMY;
28175
+ var init_layout_layered = __esm({
28176
+ "src/boxes-and-lines/layout-layered.ts"() {
28177
+ "use strict";
28178
+ init_layout5();
28179
+ NODESEP = 50;
28180
+ RANKSEP = 60;
28181
+ DUMMY_THICK = 10;
28182
+ MARGIN3 = 40;
28183
+ W_REAL_REAL = 1;
28184
+ W_REAL_DUMMY = 2;
28185
+ W_DUMMY_DUMMY = 8;
28186
+ }
28187
+ });
28188
+
28189
+ // src/boxes-and-lines/layout-search.ts
28190
+ var layout_search_exports = {};
28191
+ __export(layout_search_exports, {
28192
+ countEdgeNearMiss: () => countEdgeNearMiss,
28193
+ countEdgeNodePierces: () => countEdgeNodePierces,
28194
+ countEdgeOverlaps: () => countEdgeOverlaps,
28195
+ countGroupOverlaps: () => countGroupOverlaps,
28196
+ countSplineCrossings: () => countSplineCrossings,
28197
+ deroutePierces: () => deroutePierces,
28198
+ detectEdgeNodePierces: () => detectEdgeNodePierces,
28199
+ detectEdgeOverlaps: () => detectEdgeOverlaps,
28200
+ detectGroupOverlaps: () => detectGroupOverlaps,
28201
+ layoutBoxesAndLinesSearch: () => layoutBoxesAndLinesSearch,
28202
+ separateGroupBands: () => separateGroupBands
28203
+ });
28204
+ import dagre4 from "@dagrejs/dagre";
28205
+ import { line as d3line, curveBasis as curveBasis5 } from "d3-shape";
28206
+ function rng2(s) {
28207
+ return () => {
28208
+ s |= 0;
28209
+ s = s + 1831565813 | 0;
28210
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
28211
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
28212
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
28213
+ };
28214
+ }
28215
+ function shuffle(a, r) {
28216
+ const x = a.slice();
28217
+ for (let i = x.length - 1; i > 0; i--) {
28218
+ const j = Math.floor(r() * (i + 1));
28219
+ [x[i], x[j]] = [x[j], x[i]];
28220
+ }
28221
+ return x;
28222
+ }
28223
+ function flatten(d) {
28224
+ const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
28225
+ const pts = [];
28226
+ let i = 0, cx = 0, cy = 0, cmd = "";
28227
+ const num = () => parseFloat(toks[i++]);
28228
+ const samp = (p0, c1, c2, p1) => {
28229
+ for (let t = 0; t <= 1; t += 0.12) {
28230
+ const u = 1 - t;
28231
+ if (c2)
28232
+ pts.push({
28233
+ x: u * u * u * p0.x + 3 * u * u * t * c1.x + 3 * u * t * t * c2.x + t * t * t * p1.x,
28234
+ y: u * u * u * p0.y + 3 * u * u * t * c1.y + 3 * u * t * t * c2.y + t * t * t * p1.y
28235
+ });
28236
+ else
28237
+ pts.push({
28238
+ x: u * u * p0.x + 2 * u * t * c1.x + t * t * p1.x,
28239
+ y: u * u * p0.y + 2 * u * t * c1.y + t * t * p1.y
28240
+ });
28241
+ }
28242
+ };
28243
+ while (i < toks.length) {
28244
+ const tk = toks[i];
28245
+ if (/[MLQC]/i.test(tk)) {
28246
+ cmd = tk;
28247
+ i++;
28248
+ }
28249
+ if (cmd === "M" || cmd === "L") {
28250
+ const x = num(), y = num();
28251
+ pts.push({ x, y });
28252
+ cx = x;
28253
+ cy = y;
28254
+ } else if (cmd === "Q") {
28255
+ const c1 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
28256
+ samp({ x: cx, y: cy }, c1, null, p1);
28257
+ cx = p1.x;
28258
+ cy = p1.y;
28259
+ } else if (cmd === "C") {
28260
+ const c1 = { x: num(), y: num() }, c2 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
28261
+ samp({ x: cx, y: cy }, c1, c2, p1);
28262
+ cx = p1.x;
28263
+ cy = p1.y;
28264
+ } else i++;
28265
+ }
28266
+ return pts;
28267
+ }
28268
+ function segPoint(p1, p2, p3, p4) {
28269
+ const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
28270
+ if (Math.abs(den) < 1e-9) return null;
28271
+ 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;
28272
+ 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;
28273
+ }
28274
+ function countSplineCrossings(layout) {
28275
+ const center = /* @__PURE__ */ new Map();
28276
+ for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
28277
+ for (const g of layout.groups)
28278
+ if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
28279
+ const polys = layout.edges.map((e) => {
28280
+ const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
28281
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28282
+ for (const p of pts) {
28283
+ if (p.x < x0) x0 = p.x;
28284
+ if (p.x > x1) x1 = p.x;
28285
+ if (p.y < y0) y0 = p.y;
28286
+ if (p.y > y1) y1 = p.y;
28287
+ }
28288
+ return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
28289
+ });
28290
+ const R = 34;
28291
+ let total = 0;
28292
+ for (let a = 0; a < polys.length; a++)
28293
+ for (let b = a + 1; b < polys.length; b++) {
28294
+ const A = polys[a], B = polys[b];
28295
+ if (A.pts.length < 2 || B.pts.length < 2) continue;
28296
+ if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
28297
+ const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
28298
+ const hits = [];
28299
+ for (let i = 1; i < A.pts.length; i++)
28300
+ for (let j = 1; j < B.pts.length; j++) {
28301
+ const p = segPoint(
28302
+ A.pts[i - 1],
28303
+ A.pts[i],
28304
+ B.pts[j - 1],
28305
+ B.pts[j]
28306
+ );
28307
+ if (!p) continue;
28308
+ if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
28309
+ continue;
28310
+ if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
28311
+ hits.push(p);
28312
+ }
28313
+ total += hits.length;
28314
+ }
28315
+ return total;
28316
+ }
28317
+ function pointSegDist(p, a, b) {
28318
+ const dx = b.x - a.x, dy = b.y - a.y;
28319
+ const len2 = dx * dx + dy * dy;
28320
+ if (len2 < 1e-9) return Math.hypot(p.x - a.x, p.y - a.y);
28321
+ let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
28322
+ t = Math.max(0, Math.min(1, t));
28323
+ return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));
28324
+ }
28325
+ function distToPoly(p, poly) {
28326
+ let m = Infinity;
28327
+ for (let i = 1; i < poly.length; i++)
28328
+ m = Math.min(m, pointSegDist(p, poly[i - 1], poly[i]));
28329
+ return m;
28330
+ }
28331
+ function pointRectDist(p, r) {
28332
+ const dx = Math.max(r.x - r.w / 2 - p.x, 0, p.x - (r.x + r.w / 2));
28333
+ const dy = Math.max(r.y - r.h / 2 - p.y, 0, p.y - (r.y + r.h / 2));
28334
+ return Math.hypot(dx, dy);
28335
+ }
28336
+ function detectEdgeOverlaps(layout, opts) {
28337
+ const dist = opts?.dist ?? 8;
28338
+ const minLen = opts?.minLen ?? 16;
28339
+ const nodeClear = opts?.nodeClear ?? 12;
28340
+ const rect = /* @__PURE__ */ new Map();
28341
+ for (const n of layout.nodes)
28342
+ rect.set(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
+ rect.set("__group_" + g.label, {
28346
+ x: g.x,
28347
+ y: g.y,
28348
+ w: g.width,
28349
+ h: g.height
28350
+ });
28351
+ const polys = layout.edges.map((e) => {
28352
+ const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
28353
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28354
+ for (const p of pts) {
28355
+ if (p.x < x0) x0 = p.x;
28356
+ if (p.x > x1) x1 = p.x;
28357
+ if (p.y < y0) y0 = p.y;
28358
+ if (p.y > y1) y1 = p.y;
28359
+ }
28360
+ return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
28361
+ });
28362
+ const runs = [];
28363
+ for (let a = 0; a < polys.length; a++)
28364
+ for (let b = a + 1; b < polys.length; b++) {
28365
+ const A = polys[a], B = polys[b];
28366
+ if (A.pts.length < 2 || B.pts.length < 2) continue;
28367
+ if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
28368
+ continue;
28369
+ const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
28370
+ let run = [];
28371
+ let runLen = 0;
28372
+ const flush = () => {
28373
+ if (runLen >= minLen && run.length >= 2)
28374
+ runs.push({
28375
+ mid: run[Math.floor(run.length / 2)],
28376
+ length: runLen,
28377
+ pts: run.slice()
28378
+ });
28379
+ run = [];
28380
+ runLen = 0;
28381
+ };
28382
+ for (const p of A.pts) {
28383
+ const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
28384
+ const covered = !nearShared && distToPoly(p, B.pts) < dist;
28385
+ if (covered) {
28386
+ if (run.length)
28387
+ runLen += Math.hypot(
28388
+ p.x - run[run.length - 1].x,
28389
+ p.y - run[run.length - 1].y
28390
+ );
28391
+ run.push(p);
28392
+ } else flush();
28393
+ }
28394
+ flush();
28395
+ }
28396
+ return runs;
28397
+ }
28398
+ function countEdgeOverlaps(layout, opts) {
28399
+ return detectEdgeOverlaps(layout, opts).length;
28400
+ }
28401
+ function detectEdgeNodePierces(layout, opts) {
28402
+ const inset = opts?.inset ?? 6;
28403
+ const minPts = opts?.minPts ?? 2;
28404
+ const rects = [];
28405
+ for (const n of layout.nodes)
28406
+ rects.push({ key: n.label, x: n.x, y: n.y, w: n.width, h: n.height });
28407
+ for (const g of layout.groups)
28408
+ if (g.collapsed)
28409
+ rects.push({
28410
+ key: "__group_" + g.label,
28411
+ x: g.x,
28412
+ y: g.y,
28413
+ w: g.width,
28414
+ h: g.height
28415
+ });
28416
+ const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
28417
+ const out = [];
28418
+ layout.edges.forEach((e, idx) => {
28419
+ if (e.points.length < 2) return;
28420
+ const poly = flatten(splineGen(e.points) ?? "");
28421
+ for (const r of rects) {
28422
+ if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
28423
+ continue;
28424
+ const hits = poly.filter((p) => inside(p, r));
28425
+ if (hits.length >= minPts)
28426
+ out.push({ edgeIdx: idx, node: r.key, pts: hits });
28427
+ }
28428
+ });
28429
+ return out;
28430
+ }
28431
+ function countEdgeNodePierces(layout, opts) {
28432
+ return detectEdgeNodePierces(layout, opts).length;
28433
+ }
28434
+ function deroutePierces(layout) {
28435
+ const pierces = detectEdgeNodePierces(layout);
28436
+ if (!pierces.length) return layout;
28437
+ const rect = /* @__PURE__ */ new Map();
28438
+ for (const n of layout.nodes)
28439
+ rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
28440
+ for (const g of layout.groups)
28441
+ if (g.collapsed)
28442
+ rect.set("__group_" + g.label, {
28443
+ x: g.x,
28444
+ y: g.y,
28445
+ w: g.width,
28446
+ h: g.height
28447
+ });
28448
+ const byEdge = /* @__PURE__ */ new Map();
28449
+ for (const p of pierces) {
28450
+ const arr = byEdge.get(p.edgeIdx);
28451
+ if (arr) arr.push(p.node);
28452
+ else byEdge.set(p.edgeIdx, [p.node]);
28453
+ }
28454
+ const edges = layout.edges.map((e, idx) => {
28455
+ const nodes = byEdge.get(idx);
28456
+ if (!nodes) return e;
28457
+ let pts = e.points.map((p) => ({ x: p.x, y: p.y }));
28458
+ for (const label of nodes) {
28459
+ const r = rect.get(label);
28460
+ if (r) pts = detourAround(pts, r);
28461
+ }
28462
+ return { ...e, points: pts };
28463
+ });
28464
+ return { ...layout, edges };
28465
+ }
28466
+ function detourAround(pts, r) {
28467
+ if (pts.length < 2) return pts;
28468
+ const c = { x: r.x, y: r.y };
28469
+ let bestSeg = -1;
28470
+ let bestT = 0;
28471
+ let bestD = Infinity;
28472
+ let bestPt = pts[0];
28473
+ for (let i = 0; i < pts.length - 1; i++) {
28474
+ const a2 = pts[i], b2 = pts[i + 1];
28475
+ const dx = b2.x - a2.x, dy = b2.y - a2.y;
28476
+ const len2 = dx * dx + dy * dy || 1e-9;
28477
+ let t = ((c.x - a2.x) * dx + (c.y - a2.y) * dy) / len2;
28478
+ t = Math.max(0, Math.min(1, t));
28479
+ const q = { x: a2.x + t * dx, y: a2.y + t * dy };
28480
+ const d = Math.hypot(q.x - c.x, q.y - c.y);
28481
+ if (d < bestD) {
28482
+ bestD = d;
28483
+ bestSeg = i;
28484
+ bestT = t;
28485
+ bestPt = q;
28486
+ }
28487
+ }
28488
+ if (bestSeg < 0) return pts;
28489
+ let nx = bestPt.x - c.x;
28490
+ let ny = bestPt.y - c.y;
28491
+ if (Math.hypot(nx, ny) < 1) {
28492
+ if (r.w <= r.h) {
28493
+ nx = 1;
28494
+ ny = 0;
28495
+ } else {
28496
+ nx = 0;
28497
+ ny = 1;
28498
+ }
28499
+ }
28500
+ const nlen = Math.hypot(nx, ny) || 1;
28501
+ nx /= nlen;
28502
+ ny /= nlen;
28503
+ const a = pts[bestSeg], b = pts[bestSeg + 1];
28504
+ let ex = b.x - a.x, ey = b.y - a.y;
28505
+ const elen = Math.hypot(ex, ey) || 1;
28506
+ ex /= elen;
28507
+ ey /= elen;
28508
+ const clear = Math.hypot(r.w, r.h) / 2 + 18;
28509
+ const hw = Math.hypot(r.w, r.h) / 2;
28510
+ void bestT;
28511
+ const before = {
28512
+ x: bestPt.x - ex * hw + nx * clear * 0.5,
28513
+ y: bestPt.y - ey * hw + ny * clear * 0.5
28514
+ };
28515
+ const peak = { x: c.x + nx * clear, y: c.y + ny * clear };
28516
+ const after = {
28517
+ x: bestPt.x + ex * hw + nx * clear * 0.5,
28518
+ y: bestPt.y + ey * hw + ny * clear * 0.5
28519
+ };
28520
+ const out = pts.slice(0, bestSeg + 1);
28521
+ out.push(before, peak, after);
28522
+ out.push(...pts.slice(bestSeg + 1));
28523
+ return out;
28524
+ }
28525
+ function countEdgeNearMiss(layout, opts) {
28526
+ const near = opts?.near ?? 14;
28527
+ const tight = opts?.tight ?? 8;
28528
+ const minLen = opts?.minLen ?? 18;
28529
+ const close = detectEdgeOverlaps(layout, { dist: near, minLen }).length;
28530
+ const over = detectEdgeOverlaps(layout, { dist: tight, minLen }).length;
28531
+ return Math.max(0, close - over);
28532
+ }
28533
+ function detectGroupOverlaps(layout, opts) {
28534
+ const margin = opts?.margin ?? 4;
28535
+ const raw = layout.groups.map((g) => ({
28536
+ label: g.label,
28537
+ l: g.x - g.width / 2,
28538
+ r: g.x + g.width / 2,
28539
+ t: g.y - g.height / 2,
28540
+ b: g.y + g.height / 2
28541
+ }));
28542
+ const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
28543
+ const rend = raw.map((a, i) => {
28544
+ const parent = raw.some((b, j) => j !== i && contains(a, b));
28545
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
28546
+ });
28547
+ const hit = /* @__PURE__ */ new Set();
28548
+ for (let i = 0; i < raw.length; i++)
28549
+ for (let j = i + 1; j < raw.length; j++) {
28550
+ if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
28551
+ const a = rend[i], b = rend[j];
28552
+ const dx = Math.max(a.l - b.r, b.l - a.r);
28553
+ const dy = Math.max(a.t - b.b, b.t - a.b);
28554
+ if (Math.max(dx, dy) < margin) {
28555
+ hit.add(raw[i].label);
28556
+ hit.add(raw[j].label);
28557
+ }
28558
+ }
28559
+ return [...hit];
28560
+ }
28561
+ function countGroupOverlaps(layout, opts) {
28562
+ const margin = opts?.margin ?? 4;
28563
+ const raw = layout.groups.map((g) => ({
28564
+ l: g.x - g.width / 2,
28565
+ r: g.x + g.width / 2,
28566
+ t: g.y - g.height / 2,
28567
+ b: g.y + g.height / 2
28568
+ }));
28569
+ const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
28570
+ const rend = raw.map((a, i) => {
28571
+ const parent = raw.some((b, j) => j !== i && contains(a, b));
28572
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
28573
+ });
28574
+ let count = 0;
28575
+ for (let i = 0; i < raw.length; i++)
28576
+ for (let j = i + 1; j < raw.length; j++) {
28577
+ if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
28578
+ const a = rend[i], b = rend[j];
28579
+ const dx = Math.max(a.l - b.r, b.l - a.r);
28580
+ const dy = Math.max(a.t - b.b, b.t - a.b);
28581
+ if (Math.max(dx, dy) < margin) count++;
28582
+ }
28583
+ return count;
28584
+ }
28585
+ function separateGroupBands(layout, parsed) {
28586
+ if (layout.groups.some((g) => g.collapsed)) return layout;
28587
+ if (countGroupOverlaps(layout) === 0) return layout;
28588
+ const parentOf = /* @__PURE__ */ new Map();
28589
+ for (const g of parsed.groups) parentOf.set(g.label, g.parentGroup);
28590
+ const topOf = (label) => {
28591
+ let cur = label;
28592
+ const seen = /* @__PURE__ */ new Set();
28593
+ for (; ; ) {
28594
+ const p = parentOf.get(cur);
28595
+ if (!p || seen.has(p)) return cur;
28596
+ seen.add(cur);
28597
+ cur = p;
28598
+ }
28599
+ };
28600
+ const topLabels = parsed.groups.filter((g) => !g.parentGroup).map((g) => g.label);
28601
+ if (topLabels.length < 2) return layout;
28602
+ const nodeTop = /* @__PURE__ */ new Map();
28603
+ for (const g of parsed.groups)
28604
+ for (const child of g.children)
28605
+ if (!parsed.groups.some((gg) => gg.label === child))
28606
+ nodeTop.set(child, topOf(g.label));
28607
+ const axis = parsed.direction === "TB" ? "x" : "y";
28608
+ const boxByLabel = new Map(layout.groups.map((g) => [g.label, g]));
28609
+ const bands = topLabels.map((label) => {
28610
+ const g = boxByLabel.get(label);
28611
+ const half2 = (axis === "y" ? g.height : g.width) / 2;
28612
+ let lo = (axis === "y" ? g.y : g.x) - half2;
28613
+ const hi = (axis === "y" ? g.y : g.x) + half2;
28614
+ const isParent = parsed.groups.some((c) => c.parentGroup === label);
28615
+ if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE2;
28616
+ return { label, lo, hi, c: (lo + hi) / 2 };
28617
+ });
28618
+ bands.sort((a, b) => a.c - b.c);
28619
+ const GAP = 16;
28620
+ const half = bands.map((b) => (b.hi - b.lo) / 2);
28621
+ const off = [0];
28622
+ for (let i = 1; i < bands.length; i++)
28623
+ off[i] = off[i - 1] + half[i - 1] + GAP + half[i];
28624
+ const desired = bands.map((b, i) => b.c - off[i]);
28625
+ const blocks = [];
28626
+ for (let i = 0; i < bands.length; i++) {
28627
+ let blk = { pos: desired[i], count: 1, sum: desired[i], first: i };
28628
+ while (blocks.length && blocks[blocks.length - 1].pos >= blk.pos) {
28629
+ const prev = blocks.pop();
28630
+ const count = prev.count + blk.count;
28631
+ const sum = prev.sum + blk.sum;
28632
+ blk = { pos: sum / count, count, sum, first: prev.first };
28633
+ }
28634
+ blocks.push(blk);
28635
+ }
28636
+ const newC = new Array(bands.length);
28637
+ for (const blk of blocks)
28638
+ for (let k = 0; k < blk.count; k++)
28639
+ newC[blk.first + k] = blk.pos + off[blk.first + k];
28640
+ const delta = /* @__PURE__ */ new Map();
28641
+ let moved = false;
28642
+ bands.forEach((b, i) => {
28643
+ const d = newC[i] - b.c;
28644
+ delta.set(b.label, d);
28645
+ if (Math.abs(d) > 0.5) moved = true;
28646
+ });
28647
+ if (!moved) return layout;
28648
+ const nodeDelta = (label) => {
28649
+ const top = nodeTop.get(label);
28650
+ return top ? delta.get(top) ?? 0 : 0;
28651
+ };
28652
+ const shift = (p, d) => axis === "y" ? { x: p.x, y: p.y + d } : { x: p.x + d, y: p.y };
28653
+ const nodes = layout.nodes.map((n) => {
28654
+ const d = nodeDelta(n.label);
28655
+ return d ? { ...n, ...axis === "y" ? { y: n.y + d } : { x: n.x + d } } : n;
28656
+ });
28657
+ const groups = layout.groups.map((g) => {
28658
+ const d = delta.get(topOf(g.label)) ?? 0;
28659
+ return d ? { ...g, ...axis === "y" ? { y: g.y + d } : { x: g.x + d } } : g;
28660
+ });
28661
+ const edges = layout.edges.map((e) => {
28662
+ const ds = nodeDelta(e.source);
28663
+ const dt = nodeDelta(e.target);
28664
+ if (!ds && !dt) return e;
28665
+ const N = e.points.length;
28666
+ const points = e.points.map((p, i) => {
28667
+ const f = N > 1 ? i / (N - 1) : 0;
28668
+ return shift(p, ds * (1 - f) + dt * f);
28669
+ });
28670
+ return { ...e, points };
28671
+ });
28672
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
28673
+ const acc = (x, y) => {
28674
+ if (x < minX) minX = x;
28675
+ if (x > maxX) maxX = x;
28676
+ if (y < minY) minY = y;
28677
+ if (y > maxY) maxY = y;
28678
+ };
28679
+ for (const n of nodes) {
28680
+ acc(n.x - n.width / 2, n.y - n.height / 2);
28681
+ acc(n.x + n.width / 2, n.y + n.height / 2);
28682
+ }
28683
+ for (const g of groups) {
28684
+ acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE2);
28685
+ acc(g.x + g.width / 2, g.y + g.height / 2);
28686
+ }
28687
+ for (const e of edges) for (const p of e.points) acc(p.x, p.y);
28688
+ const M = 40;
28689
+ const sx = M - minX, sy = M - minY;
28690
+ const reshift = sx !== 0 || sy !== 0;
28691
+ return {
28692
+ nodes: reshift ? nodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })) : nodes,
28693
+ groups: reshift ? groups.map((g) => ({ ...g, x: g.x + sx, y: g.y + sy })) : groups,
28694
+ edges: reshift ? edges.map((e) => ({
28695
+ ...e,
28696
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
28697
+ })) : edges,
28698
+ width: maxX - minX + 2 * M,
28699
+ height: maxY - minY + 2 * M
28700
+ };
28701
+ }
28702
+ function segCross(p1, p2, p3, p4) {
28703
+ const d1x = p2.x - p1.x, d1y = p2.y - p1.y, d2x = p4.x - p3.x, d2y = p4.y - p3.y;
28704
+ const den = d1x * d2y - d1y * d2x;
28705
+ if (Math.abs(den) < 1e-9) return false;
28706
+ const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / den;
28707
+ const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / den;
28708
+ return t > 1e-3 && t < 0.999 && s > 1e-3 && s < 0.999;
28709
+ }
28710
+ function countCrossingsFast(layout) {
28711
+ const E = layout.edges.filter((e) => e.points.length >= 2);
28712
+ const bb = E.map((e) => {
28713
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28714
+ for (const p of e.points) {
28715
+ if (p.x < x0) x0 = p.x;
28716
+ if (p.x > x1) x1 = p.x;
28717
+ if (p.y < y0) y0 = p.y;
28718
+ if (p.y > y1) y1 = p.y;
28719
+ }
28720
+ return { x0, y0, x1, y1 };
28721
+ });
28722
+ let count = 0;
28723
+ for (let i = 0; i < E.length; i++)
28724
+ for (let j = i + 1; j < E.length; j++) {
28725
+ const A = E[i], B = E[j];
28726
+ if (A.source === B.source || A.source === B.target || A.target === B.source || A.target === B.target)
28727
+ continue;
28728
+ const a = bb[i], b = bb[j];
28729
+ if (a.x1 < b.x0 || b.x1 < a.x0 || a.y1 < b.y0 || b.y1 < a.y0) continue;
28730
+ const pa = A.points, pb = B.points;
28731
+ let hit = false;
28732
+ for (let ai = 0; ai < pa.length - 1 && !hit; ai++)
28733
+ for (let bi = 0; bi < pb.length - 1; bi++) {
28734
+ if (segCross(pa[ai], pa[ai + 1], pb[bi], pb[bi + 1])) {
28735
+ hit = true;
28736
+ break;
28737
+ }
28738
+ }
28739
+ if (hit) count++;
28740
+ }
28741
+ return count;
28742
+ }
28743
+ function meanDrift(layout, prev) {
28744
+ if (!prev?.size) return 0;
28745
+ let sum = 0, n = 0;
28746
+ for (const node of layout.nodes) {
28747
+ const p = prev.get(node.label);
28748
+ if (p) {
28749
+ sum += Math.hypot(node.x - p.x, node.y - p.y);
28750
+ n++;
28751
+ }
28752
+ }
28753
+ return n ? sum / n : 0;
28754
+ }
28755
+ function edgeLength(layout) {
28756
+ let total = 0;
28757
+ for (const e of layout.edges)
28758
+ for (let i = 1; i < e.points.length; i++)
28759
+ total += Math.hypot(
28760
+ e.points[i].x - e.points[i - 1].x,
28761
+ e.points[i].y - e.points[i - 1].y
28762
+ );
28763
+ return total;
28764
+ }
28765
+ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
28766
+ const hideDescriptions = opts?.hideDescriptions ?? false;
28767
+ const collapsedGroupLabels = /* @__PURE__ */ new Set();
28768
+ if (collapseInfo) {
28769
+ const missing = /* @__PURE__ */ new Set();
28770
+ for (const og of collapseInfo.originalGroups)
28771
+ if (!parsed.groups.some((g) => g.label === og.label))
28772
+ missing.add(og.label);
28773
+ for (const label of missing) {
28774
+ const og = collapseInfo.originalGroups.find((g) => g.label === label);
28775
+ const parent = og?.parentGroup;
28776
+ if (!parent || !missing.has(parent)) collapsedGroupLabels.add(label);
28777
+ }
28778
+ }
28779
+ const sizes = /* @__PURE__ */ new Map();
28780
+ let maxDescH = 0;
28781
+ for (const node of parsed.nodes) {
28782
+ const s = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
28783
+ sizes.set(node.label, s);
28784
+ if (!hideDescriptions && node.description && node.description.length > 0)
28785
+ maxDescH = Math.max(maxDescH, s.height);
28786
+ }
28787
+ if (maxDescH > 0) {
28788
+ for (const node of parsed.nodes)
28789
+ if (node.description && node.description.length > 0) {
28790
+ const s = sizes.get(node.label);
28791
+ sizes.set(node.label, { width: s.width, height: maxDescH });
28792
+ }
28793
+ }
28794
+ const gid = (label) => `__group_${label}`;
28795
+ const rankdir = parsed.direction === "TB" ? "TB" : "LR";
28796
+ function place(cfg) {
28797
+ const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
28798
+ const ord = (a) => r ? shuffle(a, r) : a.slice();
28799
+ const g = new dagre4.graphlib.Graph({ compound: true, multigraph: true });
28800
+ g.setGraph({
28801
+ rankdir,
28802
+ ranker: cfg.ranker,
28803
+ nodesep: cfg.nodesep,
28804
+ ranksep: cfg.ranksep,
28805
+ edgesep: 20,
28806
+ marginx: 40,
28807
+ marginy: 40
28808
+ });
28809
+ g.setDefaultEdgeLabel(() => ({}));
28810
+ for (const grp of ord(parsed.groups))
28811
+ g.setNode(gid(grp.label), { label: grp.label });
28812
+ for (const node of ord(parsed.nodes)) {
28813
+ const s = sizes.get(node.label);
28814
+ g.setNode(node.label, { width: s.width, height: s.height });
28815
+ }
28816
+ for (const label of collapsedGroupLabels)
28817
+ g.setNode(gid(label), { width: NODE_WIDTH, height: NODE_HEIGHT });
28818
+ for (const grp of parsed.groups) {
28819
+ if (grp.parentGroup && g.hasNode(gid(grp.parentGroup)))
28820
+ g.setParent(gid(grp.label), gid(grp.parentGroup));
28821
+ for (const c of ord(grp.children)) {
28822
+ if (g.hasNode(c)) g.setParent(c, gid(grp.label));
28823
+ }
28824
+ }
28825
+ if (collapseInfo)
28826
+ for (const label of collapsedGroupLabels) {
28827
+ const og = collapseInfo.originalGroups.find((x) => x.label === label);
28828
+ if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && g.hasNode(gid(og.parentGroup)))
28829
+ g.setParent(gid(label), gid(og.parentGroup));
28830
+ }
28831
+ for (const e of ord(parsed.edges))
28832
+ if (g.hasNode(e.source) && g.hasNode(e.target))
28833
+ g.setEdge(e.source, e.target, {});
28834
+ dagre4.layout(g);
28835
+ const nodes = parsed.nodes.map((n2) => {
28836
+ const p = g.node(n2.label);
28837
+ return {
28838
+ label: n2.label,
28839
+ x: p.x,
28840
+ y: p.y,
28841
+ width: p.width,
28842
+ height: p.height
28843
+ };
28844
+ });
28845
+ const groups = parsed.groups.map(
28846
+ (grp) => {
28847
+ const p = g.node(gid(grp.label));
28848
+ return {
28849
+ label: grp.label,
28850
+ lineNumber: grp.lineNumber,
28851
+ x: p.x,
28852
+ y: p.y,
28853
+ width: p.width,
28854
+ height: p.height,
28855
+ collapsed: false,
28856
+ childCount: grp.children.length
28857
+ };
28858
+ }
28859
+ );
28860
+ for (const label of collapsedGroupLabels) {
28861
+ const p = g.node(gid(label));
28862
+ groups.push({
28863
+ label,
28864
+ lineNumber: 0,
28865
+ x: p.x,
28866
+ y: p.y,
28867
+ width: p.width,
28868
+ height: p.height,
28869
+ collapsed: true,
28870
+ childCount: collapseInfo?.collapsedChildCounts.get(label) ?? 0
28871
+ });
28872
+ }
28873
+ const edges = parsed.edges.filter((e) => g.hasEdge(e.source, e.target)).map((e) => {
28874
+ const ed = g.edge(e.source, e.target);
28875
+ return {
28876
+ source: e.source,
28877
+ target: e.target,
28878
+ ...e.label !== void 0 && { label: e.label },
28879
+ bidirectional: e.bidirectional,
28880
+ lineNumber: e.lineNumber,
28881
+ points: ed?.points ?? [],
28882
+ yOffset: 0,
28883
+ parallelCount: 1,
28884
+ metadata: e.metadata
28885
+ };
28886
+ });
28887
+ const gg = g.graph();
28888
+ return {
28889
+ nodes,
28890
+ edges,
28891
+ groups,
28892
+ width: gg.width ?? 800,
28893
+ height: gg.height ?? 600
28894
+ };
28895
+ }
28896
+ const n = parsed.nodes.length;
28897
+ const seedCount = opts?.seeds ?? (n <= 12 ? 80 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
28898
+ const REFINE_K = opts?.refineK ?? 6;
28899
+ const lambda = opts?.lambda ?? DEFAULT_LAMBDA;
28900
+ const prev = opts?.previousPositions;
28901
+ const RANKERS = ["network-simplex", "tight-tree", "longest-path"];
28902
+ const SPACINGS = [
28903
+ { nodesep: 50, ranksep: 60 },
28904
+ { nodesep: 34, ranksep: 46 },
28905
+ { nodesep: 66, ranksep: 82 }
28906
+ ];
28907
+ const configs = [];
28908
+ for (const ranker of RANKERS)
28909
+ for (const sp of SPACINGS) configs.push({ ranker, ...sp });
28910
+ for (let s = 0; s < seedCount; s++)
28911
+ configs.push({
28912
+ ranker: "network-simplex",
28913
+ nodesep: 50,
28914
+ ranksep: 60,
28915
+ seed: s
28916
+ });
28917
+ const badness = (lay, floor) => {
28918
+ const x = countSplineCrossings(lay);
28919
+ if (x > floor) return Infinity;
28920
+ return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
28921
+ };
28922
+ const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
28923
+ const pool = [];
28924
+ for (const cfg of configs) {
28925
+ try {
28926
+ pool.push(place(cfg));
28927
+ } catch {
28928
+ }
28929
+ }
28930
+ if (!pool.length)
28931
+ return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
28932
+ let layered = [];
28933
+ try {
28934
+ layered = layeredCandidates(parsed, sizes);
28935
+ } catch {
28936
+ }
28937
+ pool.sort(
28938
+ (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
28939
+ );
28940
+ const refineK = Math.min(REFINE_K, pool.length);
28941
+ let best = pool[0];
28942
+ let bestObj = Infinity;
28943
+ let bestBad = Infinity;
28944
+ const consider = (lay) => {
28945
+ const bad = badness(lay, bestBad);
28946
+ if (bad === Infinity) return;
28947
+ const sc = objective(lay, bad);
28948
+ if (sc < bestObj) {
28949
+ bestObj = sc;
28950
+ bestBad = bad;
28951
+ best = lay;
28952
+ }
28953
+ };
28954
+ for (const lay of pool.slice(0, refineK)) consider(lay);
28955
+ if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
28956
+ const extra = [];
28957
+ for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
28958
+ try {
28959
+ extra.push(
28960
+ place({
28961
+ ranker: "network-simplex",
28962
+ nodesep: 50,
28963
+ ranksep: 60,
28964
+ seed: s
28965
+ })
28966
+ );
28967
+ } catch {
28968
+ }
28969
+ }
28970
+ extra.sort(
28971
+ (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
28972
+ );
28973
+ for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
28974
+ }
28975
+ for (const lay of layered) {
28976
+ const variants = [lay];
28977
+ const dp = deroutePierces(lay);
28978
+ if (dp !== lay) variants.push(dp);
28979
+ for (const v of variants) {
28980
+ const bad = badness(v, bestBad - 1);
28981
+ if (bad < bestBad) {
28982
+ bestBad = bad;
28983
+ best = v;
28984
+ }
28985
+ }
28986
+ }
28987
+ if (bestBad > 0) {
28988
+ const rerouted = deroutePierces(best);
28989
+ if (rerouted !== best && badness(rerouted, bestBad - 1) < bestBad)
28990
+ best = rerouted;
28991
+ }
28992
+ if (bestBad > 0 && countGroupOverlaps(best) > 0) {
28993
+ const separated = separateGroupBands(best, parsed);
28994
+ if (separated !== best && badness(separated, bestBad - 1) < bestBad)
28995
+ best = separated;
28996
+ }
28997
+ return best;
28998
+ }
28999
+ var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
29000
+ var init_layout_search = __esm({
29001
+ "src/boxes-and-lines/layout-search.ts"() {
29002
+ "use strict";
29003
+ init_layout5();
29004
+ init_layout_layered();
29005
+ DEFAULT_LAMBDA = 4;
29006
+ ESCALATE_THRESHOLD = 4;
29007
+ ESCALATE_MAX_N = 45;
29008
+ ESCALATE_SEEDS = 18;
29009
+ ESCALATE_REFINE = 10;
29010
+ splineGen = d3line().x((d) => d.x).y((d) => d.y).curve(curveBasis5);
29011
+ GROUP_LABEL_ZONE2 = 32;
29012
+ }
29013
+ });
29014
+
27685
29015
  // src/boxes-and-lines/layout.ts
27686
29016
  var layout_exports5 = {};
27687
29017
  __export(layout_exports5, {
29018
+ NODE_HEIGHT: () => NODE_HEIGHT,
29019
+ NODE_WIDTH: () => NODE_WIDTH,
29020
+ computeNodeSize: () => computeNodeSize,
27688
29021
  layoutBoxesAndLines: () => layoutBoxesAndLines
27689
29022
  });
27690
- import ELK from "elkjs/lib/elk.bundled.js";
27691
29023
  function splitCamelCase2(word) {
27692
29024
  const parts = [];
27693
29025
  let start = 0;
@@ -27758,417 +29090,21 @@ function computeNodeSize(node, reserveValueRow) {
27758
29090
  const totalHeight = labelHeight + SEPARATOR_GAP5 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
27759
29091
  return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
27760
29092
  }
27761
- function getElk() {
27762
- if (!elkInstance) elkInstance = new ELK();
27763
- return elkInstance;
27764
- }
27765
- function baseOptions() {
27766
- return {
27767
- "elk.algorithm": "layered",
27768
- // INCLUDE_CHILDREN lets ELK route edges across container boundaries.
27769
- "elk.hierarchyHandling": "INCLUDE_CHILDREN",
27770
- "elk.edgeRouting": "ORTHOGONAL",
27771
- "elk.layered.unnecessaryBendpoints": "true",
27772
- // Let edges leave from top/bottom of nodes (not just the flow-direction
27773
- // sides) when it reduces crossings.
27774
- "elk.layered.allowNonFlowPortsToSwitchSides": "true"
27775
- };
27776
- }
27777
- function bkBaseline() {
27778
- return {
27779
- ...baseOptions(),
27780
- "elk.layered.nodePlacement.strategy": "BRANDES_KOEPF",
27781
- "elk.layered.nodePlacement.bk.fixedAlignment": "BALANCED",
27782
- "elk.layered.nodePlacement.bk.edgeStraightening": "IMPROVE_STRAIGHTNESS",
27783
- "elk.layered.compaction.connectedComponents": "true",
27784
- "elk.layered.spacing.nodeNodeBetweenLayers": "90",
27785
- "elk.spacing.nodeNode": "55",
27786
- "elk.spacing.edgeNode": "55",
27787
- "elk.spacing.edgeEdge": "18"
27788
- };
27789
- }
27790
- function getVariants() {
27791
- const bk = bkBaseline();
27792
- return [
27793
- {
27794
- name: "bk-baseline",
27795
- options: {
27796
- ...bk,
27797
- "elk.layered.crossingMinimization.greedySwitch.type": "ONE_SIDED"
27798
- }
27799
- },
27800
- {
27801
- name: "bk-aggressive",
27802
- options: {
27803
- ...bk,
27804
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27805
- "elk.layered.thoroughness": "50"
27806
- }
27807
- },
27808
- {
27809
- name: "bk-wide",
27810
- options: {
27811
- ...bk,
27812
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27813
- "elk.layered.thoroughness": "50",
27814
- "elk.spacing.nodeNode": "70",
27815
- "elk.spacing.edgeNode": "75",
27816
- "elk.spacing.edgeEdge": "22",
27817
- "elk.layered.spacing.nodeNodeBetweenLayers": "120"
27818
- }
27819
- },
27820
- {
27821
- name: "longest-path",
27822
- options: {
27823
- ...bk,
27824
- "elk.layered.layering.strategy": "LONGEST_PATH",
27825
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27826
- "elk.layered.thoroughness": "50"
27827
- }
27828
- },
27829
- {
27830
- name: "bounded-width",
27831
- options: {
27832
- ...bk,
27833
- "elk.layered.layering.strategy": "COFFMAN_GRAHAM",
27834
- "elk.layered.layering.coffmanGraham.layerBound": "3",
27835
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27836
- "elk.layered.thoroughness": "50"
27837
- }
27838
- }
27839
- ];
27840
- }
27841
- function countCrossings(edges) {
27842
- let count = 0;
27843
- for (let i = 0; i < edges.length; i++) {
27844
- const edgeI = edges[i];
27845
- const a = edgeI.points;
27846
- if (a.length < 2) continue;
27847
- for (let j = i + 1; j < edges.length; j++) {
27848
- const edgeJ = edges[j];
27849
- const b = edgeJ.points;
27850
- if (b.length < 2) continue;
27851
- if (edgeI.source === edgeJ.source) continue;
27852
- if (edgeI.source === edgeJ.target) continue;
27853
- if (edgeI.target === edgeJ.source) continue;
27854
- if (edgeI.target === edgeJ.target) continue;
27855
- for (let ai = 0; ai < a.length - 1; ai++) {
27856
- for (let bi = 0; bi < b.length - 1; bi++) {
27857
- if (segmentsCross(a[ai], a[ai + 1], b[bi], b[bi + 1])) count++;
27858
- }
27859
- }
27860
- }
27861
- }
27862
- return count;
27863
- }
27864
- function segmentsCross(p1, p2, p3, p4) {
27865
- const d1x = p2.x - p1.x;
27866
- const d1y = p2.y - p1.y;
27867
- const d2x = p4.x - p3.x;
27868
- const d2y = p4.y - p3.y;
27869
- const denom = d1x * d2y - d1y * d2x;
27870
- if (Math.abs(denom) < 1e-9) return false;
27871
- const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / denom;
27872
- const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / denom;
27873
- const EPS = 1e-3;
27874
- return t > EPS && t < 1 - EPS && s > EPS && s < 1 - EPS;
27875
- }
27876
- function countTotalBends(edges) {
27877
- let bends = 0;
27878
- for (const e of edges) bends += Math.max(0, e.points.length - 2);
27879
- return bends;
27880
- }
27881
- function scoreLayout(layout) {
27882
- return {
27883
- crossings: countCrossings(layout.edges),
27884
- bends: countTotalBends(layout.edges),
27885
- area: layout.width * layout.height
27886
- };
27887
- }
27888
- function cmpScore(a, b) {
27889
- const aBucket = a.crossings <= CROSSINGS_FORGIVENESS ? 0 : a.crossings;
27890
- const bBucket = b.crossings <= CROSSINGS_FORGIVENESS ? 0 : b.crossings;
27891
- if (aBucket !== bBucket) return aBucket - bBucket;
27892
- if (a.area !== b.area) return a.area - b.area;
27893
- return a.bends - b.bends;
27894
- }
27895
29093
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
27896
- const hideDescriptions = layoutOptions?.hideDescriptions ?? false;
27897
- const direction = parsed.direction === "TB" ? "DOWN" : "RIGHT";
27898
- const collapsedGroupLabels = /* @__PURE__ */ new Set();
27899
- if (collapseInfo) {
27900
- const missingGroups = /* @__PURE__ */ new Set();
27901
- for (const og of collapseInfo.originalGroups) {
27902
- if (!parsed.groups.some((g) => g.label === og.label)) {
27903
- missingGroups.add(og.label);
27904
- }
27905
- }
27906
- for (const label of missingGroups) {
27907
- const og = collapseInfo.originalGroups.find((g) => g.label === label);
27908
- const parentLabel = og?.parentGroup;
27909
- if (!parentLabel || !missingGroups.has(parentLabel)) {
27910
- collapsedGroupLabels.add(label);
27911
- }
27912
- }
27913
- }
27914
- const nodeSizes = /* @__PURE__ */ new Map();
27915
- let maxDescHeight = 0;
27916
- for (const node of parsed.nodes) {
27917
- const size = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
27918
- nodeSizes.set(node.label, size);
27919
- if (!hideDescriptions && node.description && node.description.length > 0) {
27920
- maxDescHeight = Math.max(maxDescHeight, size.height);
27921
- }
27922
- }
27923
- if (maxDescHeight > 0) {
27924
- for (const node of parsed.nodes) {
27925
- if (node.description && node.description.length > 0) {
27926
- const size = nodeSizes.get(node.label);
27927
- nodeSizes.set(node.label, { width: size.width, height: maxDescHeight });
27928
- }
27929
- }
27930
- }
27931
- const expandedGroupSet = new Set(parsed.groups.map((g) => g.label));
27932
- const gid = (label) => `__group_${label}`;
27933
- function buildGraph() {
27934
- const nodeById = /* @__PURE__ */ new Map();
27935
- const parentOf = /* @__PURE__ */ new Map();
27936
- for (const node of parsed.nodes) {
27937
- const size = nodeSizes.get(node.label);
27938
- nodeById.set(node.label, {
27939
- id: node.label,
27940
- width: size.width,
27941
- height: size.height,
27942
- labels: [{ text: node.label }]
27943
- });
27944
- }
27945
- for (const group of parsed.groups) {
27946
- nodeById.set(gid(group.label), {
27947
- id: gid(group.label),
27948
- labels: [{ text: group.label }],
27949
- layoutOptions: {
27950
- "elk.padding": `[top=${CONTAINER_PAD_TOP2},left=${CONTAINER_PAD_X3},bottom=${CONTAINER_PAD_BOTTOM3},right=${CONTAINER_PAD_X3}]`,
27951
- // Suggest square-ish containers — has limited effect with
27952
- // INCLUDE_CHILDREN but doesn't hurt.
27953
- "elk.aspectRatio": "1.4"
27954
- },
27955
- children: [],
27956
- edges: []
27957
- });
27958
- }
27959
- for (const label of collapsedGroupLabels) {
27960
- nodeById.set(gid(label), {
27961
- id: gid(label),
27962
- width: NODE_WIDTH,
27963
- height: NODE_HEIGHT,
27964
- labels: [{ text: label }]
27965
- });
27966
- }
27967
- for (const group of parsed.groups) {
27968
- if (group.parentGroup && nodeById.has(gid(group.parentGroup))) {
27969
- parentOf.set(gid(group.label), gid(group.parentGroup));
27970
- }
27971
- }
27972
- if (collapseInfo) {
27973
- for (const label of collapsedGroupLabels) {
27974
- const og = collapseInfo.originalGroups.find((g) => g.label === label);
27975
- if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && nodeById.has(gid(og.parentGroup))) {
27976
- parentOf.set(gid(label), gid(og.parentGroup));
27977
- }
27978
- }
27979
- }
27980
- for (const group of parsed.groups) {
27981
- for (const child of group.children) {
27982
- if (expandedGroupSet.has(child)) continue;
27983
- if (nodeById.has(child)) {
27984
- parentOf.set(child, gid(group.label));
27985
- }
27986
- }
27987
- }
27988
- const roots = [];
27989
- for (const [id, node] of nodeById) {
27990
- const parentId = parentOf.get(id);
27991
- if (parentId) {
27992
- const parent = nodeById.get(parentId);
27993
- parent.children = parent.children ?? [];
27994
- parent.children.push(node);
27995
- } else {
27996
- roots.push(node);
27997
- }
27998
- }
27999
- const rootEdges = [];
28000
- for (let i = 0; i < parsed.edges.length; i++) {
28001
- const edge = parsed.edges[i];
28002
- if (!nodeById.has(edge.source) || !nodeById.has(edge.target)) continue;
28003
- rootEdges.push({
28004
- id: `e${i}`,
28005
- sources: [edge.source],
28006
- targets: [edge.target]
28007
- });
28008
- }
28009
- return { roots, rootEdges };
28010
- }
28011
- async function runVariant(variant) {
28012
- const { roots, rootEdges } = buildGraph();
28013
- const elkRoot = {
28014
- id: "root",
28015
- layoutOptions: {
28016
- ...variant.options,
28017
- "elk.direction": direction,
28018
- "elk.padding": `[top=${MARGIN3},left=${MARGIN3},bottom=${MARGIN3},right=${MARGIN3}]`
28019
- },
28020
- children: roots,
28021
- edges: rootEdges
28022
- };
28023
- const result = await getElk().layout(elkRoot);
28024
- return extractLayout(result);
28025
- }
28026
- function extractLayout(result) {
28027
- const layoutNodes = [];
28028
- const layoutGroups = [];
28029
- const allEdges = [];
28030
- const containerAbs = /* @__PURE__ */ new Map();
28031
- function walk(n, offsetX, offsetY, isRoot) {
28032
- const nx = (n.x ?? 0) + offsetX;
28033
- const ny = (n.y ?? 0) + offsetY;
28034
- const nw = n.width ?? 0;
28035
- const nh = n.height ?? 0;
28036
- if (isRoot) {
28037
- containerAbs.set("root", { x: nx, y: ny });
28038
- } else {
28039
- const isGroup = n.id.startsWith("__group_");
28040
- if (isGroup) {
28041
- const label = n.id.slice("__group_".length);
28042
- const collapsed = collapsedGroupLabels.has(label);
28043
- const og = collapseInfo?.originalGroups.find(
28044
- (g) => g.label === label
28045
- );
28046
- const pg = parsed.groups.find((g) => g.label === label);
28047
- const childCount = collapsed ? collapseInfo?.collapsedChildCounts.get(label) ?? 0 : void 0;
28048
- layoutGroups.push({
28049
- label,
28050
- lineNumber: pg?.lineNumber ?? og?.lineNumber ?? 0,
28051
- x: nx + nw / 2,
28052
- y: ny + nh / 2,
28053
- width: nw,
28054
- height: nh,
28055
- collapsed,
28056
- ...childCount !== void 0 && { childCount }
28057
- });
28058
- if (!collapsed) containerAbs.set(n.id, { x: nx, y: ny });
28059
- } else {
28060
- layoutNodes.push({
28061
- label: n.id,
28062
- x: nx + nw / 2,
28063
- y: ny + nh / 2,
28064
- width: nw,
28065
- height: nh
28066
- });
28067
- }
28068
- }
28069
- if (n.edges) for (const e of n.edges) allEdges.push(e);
28070
- if (n.children) for (const c of n.children) walk(c, nx, ny, false);
28071
- }
28072
- walk(result, 0, 0, true);
28073
- const edgeYOffsets = new Array(parsed.edges.length).fill(0);
28074
- const edgeParallelCounts = new Array(parsed.edges.length).fill(1);
28075
- const parallelGroups = /* @__PURE__ */ new Map();
28076
- for (let i = 0; i < parsed.edges.length; i++) {
28077
- const edge = parsed.edges[i];
28078
- const [a, b] = edge.source < edge.target ? [edge.source, edge.target] : [edge.target, edge.source];
28079
- const key = `${a}\0${b}`;
28080
- if (!parallelGroups.has(key)) parallelGroups.set(key, []);
28081
- parallelGroups.get(key).push(i);
28082
- }
28083
- for (const group of parallelGroups.values()) {
28084
- const capped = group.slice(0, MAX_PARALLEL_EDGES);
28085
- for (const idx of group.slice(MAX_PARALLEL_EDGES)) {
28086
- edgeParallelCounts[idx] = 0;
28087
- }
28088
- if (capped.length < 2) continue;
28089
- for (let j = 0; j < capped.length; j++) {
28090
- const cappedJ = capped[j];
28091
- edgeYOffsets[cappedJ] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
28092
- edgeParallelCounts[cappedJ] = capped.length;
28093
- }
28094
- }
28095
- const edgeById = /* @__PURE__ */ new Map();
28096
- for (const e of allEdges) edgeById.set(e.id, e);
28097
- const layoutEdges = [];
28098
- for (let i = 0; i < parsed.edges.length; i++) {
28099
- const edge = parsed.edges[i];
28100
- if (edgeParallelCounts[i] === 0) continue;
28101
- const elkEdge = edgeById.get(`e${i}`);
28102
- if (!elkEdge?.sections || elkEdge.sections.length === 0) continue;
28103
- const container = elkEdge.container ?? "root";
28104
- const off = containerAbs.get(container) ?? { x: 0, y: 0 };
28105
- const s = elkEdge.sections[0];
28106
- const points = [
28107
- { x: s.startPoint.x + off.x, y: s.startPoint.y + off.y },
28108
- ...(s.bendPoints ?? []).map((p) => ({
28109
- x: p.x + off.x,
28110
- y: p.y + off.y
28111
- })),
28112
- { x: s.endPoint.x + off.x, y: s.endPoint.y + off.y }
28113
- ];
28114
- let labelX;
28115
- let labelY;
28116
- if (edge.label && points.length >= 2) {
28117
- const mid = Math.floor(points.length / 2);
28118
- const midPoint = points[mid];
28119
- labelX = midPoint.x;
28120
- labelY = midPoint.y - 10;
28121
- }
28122
- layoutEdges.push({
28123
- source: edge.source,
28124
- target: edge.target,
28125
- ...edge.label !== void 0 && { label: edge.label },
28126
- bidirectional: edge.bidirectional,
28127
- lineNumber: edge.lineNumber,
28128
- points,
28129
- ...labelX !== void 0 && { labelX },
28130
- ...labelY !== void 0 && { labelY },
28131
- // In-bounds — i < parsed.edges.length, arrays sized to that length.
28132
- yOffset: edgeYOffsets[i],
28133
- parallelCount: edgeParallelCounts[i],
28134
- metadata: edge.metadata,
28135
- deferred: true
28136
- });
28137
- }
28138
- let maxX = 0;
28139
- let maxY = 0;
28140
- for (const node of layoutNodes) {
28141
- maxX = Math.max(maxX, node.x + node.width / 2);
28142
- maxY = Math.max(maxY, node.y + node.height / 2);
28143
- }
28144
- for (const group of layoutGroups) {
28145
- maxX = Math.max(maxX, group.x + group.width / 2);
28146
- maxY = Math.max(maxY, group.y + group.height / 2);
29094
+ const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
29095
+ const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
29096
+ ...layoutOptions?.hideDescriptions !== void 0 && {
29097
+ hideDescriptions: layoutOptions.hideDescriptions
29098
+ },
29099
+ ...layoutOptions?.previousPositions !== void 0 && {
29100
+ previousPositions: layoutOptions.previousPositions
28147
29101
  }
28148
- return {
28149
- nodes: layoutNodes,
28150
- edges: layoutEdges,
28151
- groups: layoutGroups,
28152
- width: maxX + MARGIN3,
28153
- height: maxY + MARGIN3
28154
- };
28155
- }
28156
- const N = parsed.nodes.length + parsed.groups.length;
28157
- const E = parsed.edges.length;
28158
- const trivial = N < 8 && E < 10;
28159
- const variants = trivial ? [getVariants()[1]] : getVariants();
28160
- const results = await Promise.all(variants.map((v) => runVariant(v)));
28161
- let best = results[0];
28162
- let bestScore = scoreLayout(best);
28163
- for (let i = 1; i < results.length; i++) {
28164
- const resultI = results[i];
28165
- const s = scoreLayout(resultI);
28166
- if (cmpScore(s, bestScore) < 0) {
28167
- best = resultI;
28168
- bestScore = s;
28169
- }
28170
- }
28171
- return attachNotes(best, parsed, layoutOptions?.collapsedNotes);
29102
+ });
29103
+ return attachNotes(
29104
+ applyParallelEdgeOffsets(searched),
29105
+ parsed,
29106
+ layoutOptions?.collapsedNotes
29107
+ );
28172
29108
  }
28173
29109
  function attachNotes(layout, parsed, collapsedNotes) {
28174
29110
  const notesSuppressed = parsed.options?.["no-notes"] === "on";
@@ -28246,20 +29182,47 @@ function attachNotes(layout, parsed, collapsedNotes) {
28246
29182
  nodes: finalNodes,
28247
29183
  edges: finalEdges,
28248
29184
  groups: finalGroups,
28249
- width: bbMaxX + shiftX + MARGIN3,
28250
- height: bbMaxY + shiftY + MARGIN3
29185
+ width: bbMaxX + shiftX + MARGIN4,
29186
+ height: bbMaxY + shiftY + MARGIN4
29187
+ };
29188
+ }
29189
+ function applyParallelEdgeOffsets(layout) {
29190
+ const groups = /* @__PURE__ */ new Map();
29191
+ layout.edges.forEach((e, i) => {
29192
+ const [a, b] = e.source < e.target ? [e.source, e.target] : [e.target, e.source];
29193
+ const key = `${a}\0${b}`;
29194
+ const arr = groups.get(key);
29195
+ if (arr) arr.push(i);
29196
+ else groups.set(key, [i]);
29197
+ });
29198
+ if ([...groups.values()].every((g) => g.length < 2)) return layout;
29199
+ const yOffset = new Array(layout.edges.length).fill(0);
29200
+ const count = new Array(layout.edges.length).fill(1);
29201
+ for (const idxs of groups.values()) {
29202
+ const capped = idxs.slice(0, MAX_PARALLEL_EDGES);
29203
+ for (const drop of idxs.slice(MAX_PARALLEL_EDGES)) count[drop] = 0;
29204
+ if (capped.length < 2) continue;
29205
+ capped.forEach((idx, j) => {
29206
+ yOffset[idx] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
29207
+ count[idx] = capped.length;
29208
+ });
29209
+ }
29210
+ return {
29211
+ ...layout,
29212
+ edges: layout.edges.map((e, i) => ({
29213
+ ...e,
29214
+ yOffset: yOffset[i],
29215
+ parallelCount: count[i]
29216
+ }))
28251
29217
  };
28252
29218
  }
28253
- 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;
29219
+ 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;
28254
29220
  var init_layout5 = __esm({
28255
29221
  "src/boxes-and-lines/layout.ts"() {
28256
29222
  "use strict";
28257
29223
  init_text_measure();
28258
29224
  init_notes();
28259
- MARGIN3 = 40;
28260
- CONTAINER_PAD_X3 = 30;
28261
- CONTAINER_PAD_TOP2 = 40;
28262
- CONTAINER_PAD_BOTTOM3 = 24;
29225
+ MARGIN4 = 40;
28263
29226
  MAX_PARALLEL_EDGES = 5;
28264
29227
  PARALLEL_SPACING = 22;
28265
29228
  PHI = 1.618;
@@ -28276,8 +29239,6 @@ var init_layout5 = __esm({
28276
29239
  LABEL_PAD = 12;
28277
29240
  VALUE_ROW_FONT = 11;
28278
29241
  VALUE_ROW_H = SEPARATOR_GAP5 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
28279
- elkInstance = null;
28280
- CROSSINGS_FORGIVENESS = 1;
28281
29242
  }
28282
29243
  });
28283
29244
 
@@ -28432,7 +29393,7 @@ function layoutMindmap(parsed, _palette, options) {
28432
29393
  leafWidth: ctx.structural(LEAF_WIDTH),
28433
29394
  hGap: ctx.aesthetic(H_GAP2),
28434
29395
  vGap: ctx.aesthetic(V_GAP2),
28435
- margin: ctx.aesthetic(MARGIN4),
29396
+ margin: ctx.aesthetic(MARGIN5),
28436
29397
  multiRootGap: ctx.aesthetic(MULTI_ROOT_GAP)
28437
29398
  };
28438
29399
  populateDepthCache(roots);
@@ -28810,7 +29771,7 @@ function populateDepthCache(roots) {
28810
29771
  };
28811
29772
  walk(roots, 0);
28812
29773
  }
28813
- 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;
29774
+ 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;
28814
29775
  var init_layout6 = __esm({
28815
29776
  "src/mindmap/layout.ts"() {
28816
29777
  "use strict";
@@ -28826,7 +29787,7 @@ var init_layout6 = __esm({
28826
29787
  NODE_V_PAD = 10;
28827
29788
  H_GAP2 = 40;
28828
29789
  V_GAP2 = 12;
28829
- MARGIN4 = 40;
29790
+ MARGIN5 = 40;
28830
29791
  MULTI_ROOT_GAP = 60;
28831
29792
  nodeDepthCache = /* @__PURE__ */ new Map();
28832
29793
  }
@@ -30101,7 +31062,7 @@ __export(layout_exports8, {
30101
31062
  layoutC4Deployment: () => layoutC4Deployment,
30102
31063
  rollUpContextRelationships: () => rollUpContextRelationships
30103
31064
  });
30104
- import dagre4 from "@dagrejs/dagre";
31065
+ import dagre5 from "@dagrejs/dagre";
30105
31066
  function computeEdgePenalty(edgeList, nodePositions, degrees, nodeGeometry) {
30106
31067
  let penalty = 0;
30107
31068
  for (const edge of edgeList) {
@@ -30526,7 +31487,7 @@ function layoutC4Context(parsed, activeTagGroup) {
30526
31487
  }
30527
31488
  const contextRels = rollUpContextRelationships(parsed);
30528
31489
  const spacing = computeAdaptiveSpacing(contextRels);
30529
- const g = new dagre4.graphlib.Graph();
31490
+ const g = new dagre5.graphlib.Graph();
30530
31491
  g.setGraph({
30531
31492
  rankdir: "TB",
30532
31493
  nodesep: spacing.nodesep,
@@ -30547,7 +31508,7 @@ function layoutC4Context(parsed, activeTagGroup) {
30547
31508
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
30548
31509
  }
30549
31510
  }
30550
- dagre4.layout(g);
31511
+ dagre5.layout(g);
30551
31512
  reduceCrossings(
30552
31513
  g,
30553
31514
  validRels.map((r) => ({ source: r.sourceName, target: r.targetName }))
@@ -30614,8 +31575,8 @@ function layoutC4Context(parsed, activeTagGroup) {
30614
31575
  }
30615
31576
  }
30616
31577
  if (nodes.length > 0) {
30617
- const shiftX = MARGIN5 - minX;
30618
- const shiftY = MARGIN5 - minY;
31578
+ const shiftX = MARGIN6 - minX;
31579
+ const shiftY = MARGIN6 - minY;
30619
31580
  for (const node of nodes) {
30620
31581
  node.x += shiftX;
30621
31582
  node.y += shiftY;
@@ -30627,12 +31588,12 @@ function layoutC4Context(parsed, activeTagGroup) {
30627
31588
  }
30628
31589
  }
30629
31590
  }
30630
- let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN5 * 2 : 0;
30631
- let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN5 * 2 : 0;
31591
+ let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN6 * 2 : 0;
31592
+ let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN6 * 2 : 0;
30632
31593
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
30633
31594
  if (legendGroups.length > 0) {
30634
- const legendY = totalHeight + MARGIN5;
30635
- let legendX = MARGIN5;
31595
+ const legendY = totalHeight + MARGIN6;
31596
+ let legendX = MARGIN6;
30636
31597
  for (const lg of legendGroups) {
30637
31598
  lg.x = legendX;
30638
31599
  lg.y = legendY;
@@ -30725,7 +31686,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30725
31686
  }
30726
31687
  }
30727
31688
  const hasGroups = elementToGroup.size > 0;
30728
- const g = hasGroups ? new dagre4.graphlib.Graph({ compound: true }) : new dagre4.graphlib.Graph();
31689
+ const g = hasGroups ? new dagre5.graphlib.Graph({ compound: true }) : new dagre5.graphlib.Graph();
30729
31690
  g.setDefaultEdgeLabel(() => ({}));
30730
31691
  if (hasGroups) {
30731
31692
  const seenGroups = /* @__PURE__ */ new Set();
@@ -30803,7 +31764,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30803
31764
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
30804
31765
  }
30805
31766
  }
30806
- dagre4.layout(g);
31767
+ dagre5.layout(g);
30807
31768
  const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
30808
31769
  reduceCrossings(
30809
31770
  g,
@@ -30964,8 +31925,8 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30964
31925
  if (pt.y > maxY) maxY = pt.y;
30965
31926
  }
30966
31927
  }
30967
- const shiftX = MARGIN5 - minX;
30968
- const shiftY = MARGIN5 - minY;
31928
+ const shiftX = MARGIN6 - minX;
31929
+ const shiftY = MARGIN6 - minY;
30969
31930
  for (const node of nodes) {
30970
31931
  node.x += shiftX;
30971
31932
  node.y += shiftY;
@@ -30982,12 +31943,12 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30982
31943
  pt.y += shiftY;
30983
31944
  }
30984
31945
  }
30985
- let totalWidth = maxX - minX + MARGIN5 * 2;
30986
- let totalHeight = maxY - minY + MARGIN5 * 2;
31946
+ let totalWidth = maxX - minX + MARGIN6 * 2;
31947
+ let totalHeight = maxY - minY + MARGIN6 * 2;
30987
31948
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
30988
31949
  if (legendGroups.length > 0) {
30989
- const legendY = totalHeight + MARGIN5;
30990
- let legendX = MARGIN5;
31950
+ const legendY = totalHeight + MARGIN6;
31951
+ let legendX = MARGIN6;
30991
31952
  for (const lg of legendGroups) {
30992
31953
  lg.x = legendX;
30993
31954
  lg.y = legendY;
@@ -31132,7 +32093,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31132
32093
  }
31133
32094
  }
31134
32095
  const hasGroups = elementToGroup.size > 0;
31135
- const g = hasGroups ? new dagre4.graphlib.Graph({ compound: true }) : new dagre4.graphlib.Graph();
32096
+ const g = hasGroups ? new dagre5.graphlib.Graph({ compound: true }) : new dagre5.graphlib.Graph();
31136
32097
  g.setDefaultEdgeLabel(() => ({}));
31137
32098
  if (hasGroups) {
31138
32099
  const seenGroups = /* @__PURE__ */ new Set();
@@ -31216,7 +32177,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31216
32177
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
31217
32178
  }
31218
32179
  }
31219
- dagre4.layout(g);
32180
+ dagre5.layout(g);
31220
32181
  const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
31221
32182
  reduceCrossings(
31222
32183
  g,
@@ -31379,8 +32340,8 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31379
32340
  if (pt.y > maxY) maxY = pt.y;
31380
32341
  }
31381
32342
  }
31382
- const shiftX = MARGIN5 - minX;
31383
- const shiftY = MARGIN5 - minY;
32343
+ const shiftX = MARGIN6 - minX;
32344
+ const shiftY = MARGIN6 - minY;
31384
32345
  for (const node of nodes) {
31385
32346
  node.x += shiftX;
31386
32347
  node.y += shiftY;
@@ -31397,12 +32358,12 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31397
32358
  pt.y += shiftY;
31398
32359
  }
31399
32360
  }
31400
- let totalWidth = maxX - minX + MARGIN5 * 2;
31401
- let totalHeight = maxY - minY + MARGIN5 * 2;
32361
+ let totalWidth = maxX - minX + MARGIN6 * 2;
32362
+ let totalHeight = maxY - minY + MARGIN6 * 2;
31402
32363
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
31403
32364
  if (legendGroups.length > 0) {
31404
- const legendY = totalHeight + MARGIN5;
31405
- let legendX = MARGIN5;
32365
+ const legendY = totalHeight + MARGIN6;
32366
+ let legendX = MARGIN6;
31406
32367
  for (const lg of legendGroups) {
31407
32368
  lg.x = legendX;
31408
32369
  lg.y = legendY;
@@ -31508,7 +32469,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31508
32469
  for (const r of refEntries) {
31509
32470
  nameToElement.set(r.element.name, r.element);
31510
32471
  }
31511
- const g = new dagre4.graphlib.Graph({ compound: true });
32472
+ const g = new dagre5.graphlib.Graph({ compound: true });
31512
32473
  g.setDefaultEdgeLabel(() => ({}));
31513
32474
  for (const [infraId] of infraIds) {
31514
32475
  g.setNode(infraId, {});
@@ -31552,7 +32513,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31552
32513
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
31553
32514
  }
31554
32515
  }
31555
- dagre4.layout(g);
32516
+ dagre5.layout(g);
31556
32517
  const nodeInfraMap = /* @__PURE__ */ new Map();
31557
32518
  for (const r of refEntries) nodeInfraMap.set(r.element.name, r.infraId);
31558
32519
  reduceCrossings(
@@ -31690,8 +32651,8 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31690
32651
  if (pt.y > maxY) maxY = pt.y;
31691
32652
  }
31692
32653
  }
31693
- const shiftX = MARGIN5 - minX;
31694
- const shiftY = MARGIN5 - minY;
32654
+ const shiftX = MARGIN6 - minX;
32655
+ const shiftY = MARGIN6 - minY;
31695
32656
  for (const node of nodes) {
31696
32657
  node.x += shiftX;
31697
32658
  node.y += shiftY;
@@ -31706,12 +32667,12 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31706
32667
  pt.y += shiftY;
31707
32668
  }
31708
32669
  }
31709
- let totalWidth = maxX - minX + MARGIN5 * 2;
31710
- let totalHeight = maxY - minY + MARGIN5 * 2;
32670
+ let totalWidth = maxX - minX + MARGIN6 * 2;
32671
+ let totalHeight = maxY - minY + MARGIN6 * 2;
31711
32672
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
31712
32673
  if (legendGroups.length > 0) {
31713
- const legendY = totalHeight + MARGIN5;
31714
- let legendX = MARGIN5;
32674
+ const legendY = totalHeight + MARGIN6;
32675
+ let legendX = MARGIN6;
31715
32676
  for (const lg of legendGroups) {
31716
32677
  lg.x = legendX;
31717
32678
  lg.y = legendY;
@@ -31731,7 +32692,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31731
32692
  height: totalHeight
31732
32693
  };
31733
32694
  }
31734
- 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;
32695
+ 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;
31735
32696
  var init_layout8 = __esm({
31736
32697
  "src/c4/layout.ts"() {
31737
32698
  "use strict";
@@ -31751,7 +32712,7 @@ var init_layout8 = __esm({
31751
32712
  CARD_H_PAD3 = 20;
31752
32713
  META_LINE_HEIGHT5 = 16;
31753
32714
  META_FONT_SIZE5 = 11;
31754
- MARGIN5 = 40;
32715
+ MARGIN6 = 40;
31755
32716
  BOUNDARY_PAD = 40;
31756
32717
  GROUP_BOUNDARY_PAD = 24;
31757
32718
  EDGE_NODE_COLLISION_WEIGHT = 5e3;
@@ -32786,7 +33747,7 @@ var layout_exports9 = {};
32786
33747
  __export(layout_exports9, {
32787
33748
  layoutGraph: () => layoutGraph
32788
33749
  });
32789
- import dagre5 from "@dagrejs/dagre";
33750
+ import dagre6 from "@dagrejs/dagre";
32790
33751
  function computeNodeWidth(label, shape) {
32791
33752
  if (shape === "pseudostate") return 24;
32792
33753
  const base = Math.max(120, label.length * 9 + 40);
@@ -32820,7 +33781,7 @@ function layoutGraph(graph, options) {
32820
33781
  if (allNodes.length === 0) {
32821
33782
  return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
32822
33783
  }
32823
- const g = new dagre5.graphlib.Graph({ compound: true });
33784
+ const g = new dagre6.graphlib.Graph({ compound: true });
32824
33785
  g.setGraph({
32825
33786
  rankdir: graph.direction,
32826
33787
  nodesep: 50,
@@ -32871,7 +33832,7 @@ function layoutGraph(graph, options) {
32871
33832
  label: edge.label ?? ""
32872
33833
  });
32873
33834
  }
32874
- dagre5.layout(g);
33835
+ dagre6.layout(g);
32875
33836
  const collapsedGroupIds = collapsedChildCounts ? new Set(collapsedChildCounts.keys()) : /* @__PURE__ */ new Set();
32876
33837
  const basePositioned = allNodes.map((node) => {
32877
33838
  const pos = g.node(node.id);
@@ -34575,7 +35536,7 @@ __export(layout_exports10, {
34575
35536
  layoutInfra: () => layoutInfra,
34576
35537
  separateGroups: () => separateGroups
34577
35538
  });
34578
- import dagre6 from "@dagrejs/dagre";
35539
+ import dagre7 from "@dagrejs/dagre";
34579
35540
  function countDisplayProps(node, expanded, options) {
34580
35541
  if (!expanded) return 0;
34581
35542
  let count = node.properties.filter((p) => DISPLAY_KEYS.has(p.key)).length;
@@ -34877,7 +35838,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
34877
35838
  };
34878
35839
  }
34879
35840
  const isLR = computed.direction !== "TB";
34880
- const g = new dagre6.graphlib.Graph();
35841
+ const g = new dagre7.graphlib.Graph();
34881
35842
  g.setGraph({
34882
35843
  rankdir: computed.direction === "TB" ? "TB" : "LR",
34883
35844
  nodesep: isLR ? 70 : 60,
@@ -34928,7 +35889,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
34928
35889
  g.setEdge(edge.sourceId, edge.targetId, { label: edge.label });
34929
35890
  }
34930
35891
  }
34931
- dagre6.layout(g);
35892
+ dagre7.layout(g);
34932
35893
  const layoutNodes = computed.nodes.map(
34933
35894
  (node) => {
34934
35895
  const pos = g.node(node.id);
@@ -36670,17 +37631,17 @@ function mulberry32(seed) {
36670
37631
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
36671
37632
  };
36672
37633
  }
36673
- function standardNormal(rng) {
37634
+ function standardNormal(rng3) {
36674
37635
  let u = 0;
36675
37636
  let v = 0;
36676
- while (u === 0) u = rng();
36677
- while (v === 0) v = rng();
37637
+ while (u === 0) u = rng3();
37638
+ while (v === 0) v = rng3();
36678
37639
  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
36679
37640
  }
36680
- function gamma(shape, rng) {
37641
+ function gamma(shape, rng3) {
36681
37642
  if (shape < 1) {
36682
- const u = rng();
36683
- return gamma(shape + 1, rng) * Math.pow(u, 1 / shape);
37643
+ const u = rng3();
37644
+ return gamma(shape + 1, rng3) * Math.pow(u, 1 / shape);
36684
37645
  }
36685
37646
  const d = shape - 1 / 3;
36686
37647
  const c = 1 / Math.sqrt(9 * d);
@@ -36688,29 +37649,29 @@ function gamma(shape, rng) {
36688
37649
  let x;
36689
37650
  let v;
36690
37651
  do {
36691
- x = standardNormal(rng);
37652
+ x = standardNormal(rng3);
36692
37653
  v = 1 + c * x;
36693
37654
  } while (v <= 0);
36694
37655
  v = v * v * v;
36695
- const u = rng();
37656
+ const u = rng3();
36696
37657
  if (u < 1 - 0.0331 * x * x * x * x) return d * v;
36697
37658
  if (Math.log(u) < x * x / 2 + d * (1 - v + Math.log(v))) return d * v;
36698
37659
  }
36699
37660
  }
36700
- function sampleBeta(alpha, beta, rng) {
36701
- const x = gamma(alpha, rng);
36702
- const y = gamma(beta, rng);
37661
+ function sampleBeta(alpha, beta, rng3) {
37662
+ const x = gamma(alpha, rng3);
37663
+ const y = gamma(beta, rng3);
36703
37664
  return x / (x + y);
36704
37665
  }
36705
- function sampleBetaPert(o, m, p, rng) {
37666
+ function sampleBetaPert(o, m, p, rng3) {
36706
37667
  if (o === p) return m;
36707
37668
  const range = p - o;
36708
37669
  const alpha = 1 + 4 * (m - o) / range;
36709
37670
  const beta = 1 + 4 * (p - m) / range;
36710
- return o + sampleBeta(alpha, beta, rng) * range;
37671
+ return o + sampleBeta(alpha, beta, rng3) * range;
36711
37672
  }
36712
37673
  function simulate(resolved, expanded, _predecessors, _successors, topo, terminals, poisoned, opts) {
36713
- const rng = mulberry32(opts.seed);
37674
+ const rng3 = mulberry32(opts.seed);
36714
37675
  const expById = /* @__PURE__ */ new Map();
36715
37676
  for (const e of expanded) expById.set(e.id, e);
36716
37677
  const completionTimes = new Array(opts.trials);
@@ -36733,7 +37694,7 @@ function simulate(resolved, expanded, _predecessors, _successors, topo, terminal
36733
37694
  } else if (exp.o === exp.p) {
36734
37695
  duration = exp.m;
36735
37696
  } else {
36736
- duration = sampleBetaPert(exp.o, exp.m, exp.p, rng);
37697
+ duration = sampleBetaPert(exp.o, exp.m, exp.p, rng3);
36737
37698
  }
36738
37699
  let esLower = 0;
36739
37700
  let efLower = -Infinity;
@@ -37725,7 +38686,7 @@ __export(layout_exports11, {
37725
38686
  layoutPert: () => layoutPert,
37726
38687
  relayoutPert: () => relayoutPert
37727
38688
  });
37728
- import dagre7 from "@dagrejs/dagre";
38689
+ import dagre8 from "@dagrejs/dagre";
37729
38690
  function computeNodeSizing(resolved) {
37730
38691
  const unit = resolved.options.timeUnit;
37731
38692
  const sprintMode = resolved.options.sprintMode;
@@ -37843,7 +38804,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
37843
38804
  }
37844
38805
  }
37845
38806
  const dagreId = (id) => memberToGroup.get(id) ?? id;
37846
- const g = new dagre7.graphlib.Graph();
38807
+ const g = new dagre8.graphlib.Graph();
37847
38808
  g.setGraph({
37848
38809
  rankdir: resolved.options.direction,
37849
38810
  nodesep: 50,
@@ -37882,7 +38843,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
37882
38843
  seenEdges.add(k);
37883
38844
  g.setEdge(src, tgt, {});
37884
38845
  }
37885
- dagre7.layout(g);
38846
+ dagre8.layout(g);
37886
38847
  const swimApplied = applySwimLanes(
37887
38848
  g,
37888
38849
  resolved,
@@ -38228,7 +39189,7 @@ function reduceCrossings2(g, direction) {
38228
39189
  buckets.get(key).push(id);
38229
39190
  }
38230
39191
  const edges = g.edges().map((e) => ({ v: e.v, w: e.w }));
38231
- const countCrossings2 = () => {
39192
+ const countCrossings = () => {
38232
39193
  let total = 0;
38233
39194
  for (let i = 0; i < edges.length; i++) {
38234
39195
  const a = edges[i];
@@ -38241,13 +39202,13 @@ function reduceCrossings2(g, direction) {
38241
39202
  const b1 = g.node(b.v);
38242
39203
  const b2 = g.node(b.w);
38243
39204
  if (!b1 || !b2) continue;
38244
- if (segmentsCross2(a1, a2, b1, b2)) total++;
39205
+ if (segmentsCross(a1, a2, b1, b2)) total++;
38245
39206
  }
38246
39207
  }
38247
39208
  return total;
38248
39209
  };
38249
39210
  const MAX_ITER = 8;
38250
- let baseline = countCrossings2();
39211
+ let baseline = countCrossings();
38251
39212
  if (baseline === 0) return;
38252
39213
  for (let iter = 0; iter < MAX_ITER; iter++) {
38253
39214
  let improved = false;
@@ -38265,7 +39226,7 @@ function reduceCrossings2(g, direction) {
38265
39226
  const bv = bn[slotAxis];
38266
39227
  an[slotAxis] = bv;
38267
39228
  bn[slotAxis] = av;
38268
- const after = countCrossings2();
39229
+ const after = countCrossings();
38269
39230
  if (after < baseline) {
38270
39231
  baseline = after;
38271
39232
  improved = true;
@@ -38286,7 +39247,7 @@ function reduceCrossings2(g, direction) {
38286
39247
  data.points = smoothEdge(src, tgt, direction);
38287
39248
  }
38288
39249
  }
38289
- function segmentsCross2(a1, a2, b1, b2) {
39250
+ function segmentsCross(a1, a2, b1, b2) {
38290
39251
  const ccw = (p, q, r) => (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x);
38291
39252
  const d1 = ccw(b1, b2, a1);
38292
39253
  const d2 = ccw(b1, b2, a2);
@@ -48047,10 +49008,10 @@ function tierBand(maxSpanDeg) {
48047
49008
  }
48048
49009
  function labelBudget(width, height, band) {
48049
49010
  const bandCap = {
48050
- world: 6,
48051
- continental: 5,
48052
- regional: 4,
48053
- local: 3
49011
+ world: 7,
49012
+ continental: 6,
49013
+ regional: 5,
49014
+ local: 4
48054
49015
  };
48055
49016
  const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
48056
49017
  return Math.max(0, Math.min(area2, bandCap[band]));
@@ -48183,8 +49144,11 @@ function placeContextLabels(args) {
48183
49144
  color: waterColor,
48184
49145
  fontSize: FONT,
48185
49146
  // water names keep the base font (no footprint to scale on)
48186
- // Water before any country (×1000), then by tier, then kind, then name.
48187
- sort: tier * 10 + KIND_ORDER[kind]
49147
+ // Orientation-value bands (lower = earlier): MAJOR water (oceans + major
49148
+ // seas, tier ≤ 1) leads at `tier*10+kind` (0..~16); MINOR water (tier ≥ 2:
49149
+ // smaller seas, bays, gulfs, straits) drops to band 2 (2000+) — BELOW the
49150
+ // country band (1000+), so a big country outranks a minor basin.
49151
+ sort: (tier <= 1 ? 0 : 2e3) + tier * 10 + KIND_ORDER[kind]
48188
49152
  });
48189
49153
  }
48190
49154
  const ranked = countries.map((c) => {
@@ -48197,7 +49161,7 @@ function placeContextLabels(args) {
48197
49161
  let ci = 0;
48198
49162
  for (const r of ranked) {
48199
49163
  const { c, w, h, area: area2 } = r;
48200
- if (w > width * 0.66 || h > height * 0.66) continue;
49164
+ if (!c.curatedAnchor && w > width * 0.66 && h > height * 0.66) continue;
48201
49165
  if (!insideViewport(c.anchor, width, height)) continue;
48202
49166
  const sizeFrac = Math.sqrt(area2) / canvasLinear;
48203
49167
  const t = Math.min(
@@ -48222,15 +49186,23 @@ function placeContextLabels(args) {
48222
49186
  letterSpacing: 0,
48223
49187
  color,
48224
49188
  fontSize,
48225
- // Always after every water body (+1e6); larger area = earlier.
48226
- sort: 1e6 + ci++
49189
+ // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
49190
+ // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
49191
+ // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
49192
+ // Campeche) yet never displaces an ocean. Larger area = earlier within the
49193
+ // band (`ci` is the area-desc rank index).
49194
+ sort: 1e3 + ci++
48227
49195
  });
48228
49196
  }
48229
49197
  candidates.sort((a, b) => a.sort - b.sort);
48230
49198
  const placed = [];
48231
49199
  const placedRects = [];
49200
+ const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
49201
+ const waterCap = budget - Math.min(2, countryCount);
49202
+ let waterPlaced = 0;
48232
49203
  for (const cand of candidates) {
48233
49204
  if (placed.length >= budget) break;
49205
+ if (cand.italic && waterPlaced >= waterCap) continue;
48234
49206
  const rect = rectAround(
48235
49207
  cand.cx,
48236
49208
  cand.cy,
@@ -48257,6 +49229,7 @@ function placeContextLabels(args) {
48257
49229
  if (collides(rect)) continue;
48258
49230
  if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
48259
49231
  placedRects.push(rect);
49232
+ if (cand.italic) waterPlaced++;
48260
49233
  placed.push({
48261
49234
  x: cand.cx,
48262
49235
  y: cand.cy,
@@ -50230,16 +51203,16 @@ function layoutMap(resolved, data, size, opts) {
50230
51203
  countryCandidates.push({
50231
51204
  name: f.properties?.name ?? iso,
50232
51205
  bbox: [x0, y0, x1, y1],
50233
- anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
51206
+ anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
51207
+ curatedAnchor: !!anchorLngLat
50234
51208
  });
50235
51209
  }
50236
51210
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
50237
51211
  (id) => id.startsWith("US-")
50238
51212
  );
50239
51213
  if (usLayer && framedStateContainers) {
50240
- const containerSet = new Set(resolved.poiFrameContainers);
50241
51214
  for (const [iso, f] of usLayer) {
50242
- if (containerSet.has(iso) || regionById.has(iso)) continue;
51215
+ if (regionById.has(iso)) continue;
50243
51216
  const viewF = cullFeatureToView(f);
50244
51217
  if (!viewF) continue;
50245
51218
  const b = path.bounds(viewF);
@@ -50367,8 +51340,15 @@ var init_layout15 = __esm({
50367
51340
  W_MAX = 8;
50368
51341
  FONT2 = 11;
50369
51342
  WORLD_LABEL_ANCHORS = {
50370
- US: [-98.5, 39.5]
51343
+ US: [-98.5, 39.5],
50371
51344
  // CONUS geographic centre (near Lebanon, Kansas)
51345
+ // Russia crosses the antimeridian (Chukotka at ~170°W), so on a non-global
51346
+ // (e.g. Europe) projection its geometry smears across the whole frame and the
51347
+ // area-weighted centroid lands mid-map (over Europe) — useless as a label
51348
+ // anchor. Pin it to European Russia (~Volga) so a Europe view labels visible
51349
+ // western Russia on its eastern margin; on a world view this still sits over
51350
+ // Russian land. (See the curated-anchor smear-gate bypass in context-labels.)
51351
+ RU: [45, 58]
50372
51352
  };
50373
51353
  MAX_CLUSTER_EXTENT_FACTOR = 0.18;
50374
51354
  MAX_COLUMN_ROWS = 7;