@diagrammo/dgmo 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -27602,9 +27602,1342 @@ var init_renderer6 = __esm({
27602
27602
  }
27603
27603
  });
27604
27604
 
27605
+ // src/boxes-and-lines/layout-layered.ts
27606
+ function rng(s) {
27607
+ return () => {
27608
+ s |= 0;
27609
+ s = s + 1831565813 | 0;
27610
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
27611
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
27612
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
27613
+ };
27614
+ }
27615
+ function layeredCandidates(parsed, sizes, opts) {
27616
+ if (parsed.groups.length > 0) return [];
27617
+ const nCount = parsed.nodes.length;
27618
+ if (nCount < 3 || nCount > 40) return [];
27619
+ const isTB = parsed.direction === "TB";
27620
+ const nodesep = opts?.nodesep ?? NODESEP;
27621
+ const ranksep = opts?.ranksep ?? RANKSEP;
27622
+ const backEdgeSide = opts?.backEdgeSide ?? "nearest";
27623
+ const restarts = opts?.restarts ?? (nCount <= 14 ? 28 : nCount <= 25 ? 14 : 6);
27624
+ const keepK = opts?.keepK ?? 3;
27625
+ const labels = parsed.nodes.map((n) => n.label);
27626
+ const labelSet = new Set(labels);
27627
+ const edges = parsed.edges.map((e, i) => ({ e, i })).filter(
27628
+ ({ e }) => labelSet.has(e.source) && labelSet.has(e.target) && e.source !== e.target
27629
+ );
27630
+ if (edges.length === 0) return [];
27631
+ const adj = /* @__PURE__ */ new Map();
27632
+ for (const l of labels) adj.set(l, []);
27633
+ for (const { e, i } of edges)
27634
+ adj.get(e.source).push({ to: e.target, idx: i });
27635
+ const reversed = /* @__PURE__ */ new Set();
27636
+ const state = /* @__PURE__ */ new Map();
27637
+ for (const l of labels) state.set(l, 0);
27638
+ const dfs = (u) => {
27639
+ state.set(u, 1);
27640
+ for (const { to, idx } of adj.get(u)) {
27641
+ const st = state.get(to);
27642
+ if (st === 1)
27643
+ reversed.add(idx);
27644
+ else if (st === 0) dfs(to);
27645
+ }
27646
+ state.set(u, 2);
27647
+ };
27648
+ for (const l of labels) if (state.get(l) === 0) dfs(l);
27649
+ const dag = edges.map(
27650
+ ({ 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 }
27651
+ );
27652
+ const outDag = /* @__PURE__ */ new Map();
27653
+ const inDeg = /* @__PURE__ */ new Map();
27654
+ for (const l of labels) {
27655
+ outDag.set(l, []);
27656
+ inDeg.set(l, 0);
27657
+ }
27658
+ for (const d of dag) {
27659
+ outDag.get(d.from).push(d.to);
27660
+ inDeg.set(d.to, (inDeg.get(d.to) ?? 0) + 1);
27661
+ }
27662
+ const rank = /* @__PURE__ */ new Map();
27663
+ for (const l of labels) rank.set(l, 0);
27664
+ const queue = labels.filter((l) => (inDeg.get(l) ?? 0) === 0);
27665
+ const indeg2 = new Map(inDeg);
27666
+ const topo = [];
27667
+ while (queue.length) {
27668
+ const u = queue.shift();
27669
+ topo.push(u);
27670
+ for (const v of outDag.get(u)) {
27671
+ rank.set(v, Math.max(rank.get(v), rank.get(u) + 1));
27672
+ indeg2.set(v, indeg2.get(v) - 1);
27673
+ if (indeg2.get(v) === 0) queue.push(v);
27674
+ }
27675
+ }
27676
+ if (topo.length !== labels.length) return [];
27677
+ const maxRank = Math.max(...labels.map((l) => rank.get(l)));
27678
+ const node = /* @__PURE__ */ new Map();
27679
+ for (const n of parsed.nodes) {
27680
+ const s = sizes.get(n.label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
27681
+ node.set(n.label, {
27682
+ id: n.label,
27683
+ dummy: false,
27684
+ rank: rank.get(n.label),
27685
+ cross: 0,
27686
+ thick: isTB ? s.width : s.height,
27687
+ depth: isTB ? s.height : s.width,
27688
+ realW: s.width,
27689
+ realH: s.height
27690
+ });
27691
+ }
27692
+ const segs = [];
27693
+ const chains = [];
27694
+ const backEdges = [];
27695
+ for (const d of dag) {
27696
+ if (d.rev) {
27697
+ backEdges.push({ edgeIdx: d.idx, src: d.to, tgt: d.from });
27698
+ continue;
27699
+ }
27700
+ const r0 = rank.get(d.from);
27701
+ const r1 = rank.get(d.to);
27702
+ const dagChain = [d.from];
27703
+ let prev = d.from;
27704
+ for (let r = r0 + 1; r < r1; r++) {
27705
+ const id = `__d${d.idx}_${r}`;
27706
+ node.set(id, {
27707
+ id,
27708
+ dummy: true,
27709
+ rank: r,
27710
+ cross: 0,
27711
+ thick: DUMMY_THICK,
27712
+ depth: 0,
27713
+ realW: 0,
27714
+ realH: 0
27715
+ });
27716
+ segs.push({ a: prev, b: id });
27717
+ dagChain.push(id);
27718
+ prev = id;
27719
+ }
27720
+ segs.push({ a: prev, b: d.to });
27721
+ dagChain.push(d.to);
27722
+ chains.push({
27723
+ edgeIdx: d.idx,
27724
+ chain: d.rev ? dagChain.slice().reverse() : dagChain
27725
+ });
27726
+ }
27727
+ const ranks = Array.from({ length: maxRank + 1 }, () => []);
27728
+ for (const n of node.values()) ranks[n.rank].push(n.id);
27729
+ const up = /* @__PURE__ */ new Map();
27730
+ const down = /* @__PURE__ */ new Map();
27731
+ for (const id of node.keys()) {
27732
+ up.set(id, []);
27733
+ down.set(id, []);
27734
+ }
27735
+ for (const s of segs) {
27736
+ down.get(s.a).push(s.b);
27737
+ up.get(s.b).push(s.a);
27738
+ }
27739
+ const orderOf = /* @__PURE__ */ new Map();
27740
+ const applyOrder = (ord) => {
27741
+ for (const id of node.keys()) orderOf.set(id, ord.get(id));
27742
+ };
27743
+ const gapCrossings = (r) => {
27744
+ const lower = ranks[r + 1];
27745
+ if (!lower) return 0;
27746
+ const es = [];
27747
+ for (const a of ranks[r]) {
27748
+ const pa = orderOf.get(a);
27749
+ for (const b of down.get(a)) es.push({ p: pa, q: orderOf.get(b) });
27750
+ }
27751
+ let c = 0;
27752
+ for (let i = 0; i < es.length; i++)
27753
+ for (let j = i + 1; j < es.length; j++) {
27754
+ const A = es[i], B = es[j];
27755
+ if ((A.p - B.p) * (A.q - B.q) < 0) c++;
27756
+ }
27757
+ return c;
27758
+ };
27759
+ const totalCrossings = () => {
27760
+ let c = 0;
27761
+ for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
27762
+ return c;
27763
+ };
27764
+ const median = (vals) => {
27765
+ if (vals.length === 0) return -1;
27766
+ vals.sort((x, y) => x - y);
27767
+ const m = Math.floor(vals.length / 2);
27768
+ if (vals.length % 2 === 1) return vals[m];
27769
+ if (vals.length === 2) return (vals[0] + vals[1]) / 2;
27770
+ const left = vals[m - 1] - vals[0];
27771
+ const right = vals[vals.length - 1] - vals[m];
27772
+ return left + right === 0 ? (vals[m - 1] + vals[m]) / 2 : (vals[m - 1] * right + vals[m] * left) / (left + right);
27773
+ };
27774
+ const wmedianRank = (r, useUp) => {
27775
+ const ids = ranks[r];
27776
+ const neigh = useUp ? up : down;
27777
+ const med = /* @__PURE__ */ new Map();
27778
+ for (const id of ids)
27779
+ med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
27780
+ const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
27781
+ const out = new Array(ids.length).fill(null);
27782
+ for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
27783
+ let mi = 0;
27784
+ for (let slot = 0; slot < out.length; slot++)
27785
+ if (out[slot] === null) out[slot] = movable[mi++];
27786
+ const final = out;
27787
+ final.forEach((id, i) => orderOf.set(id, i));
27788
+ ranks[r] = final;
27789
+ };
27790
+ const transpose = () => {
27791
+ let improved = true;
27792
+ let guard = 0;
27793
+ while (improved && guard++ < 12) {
27794
+ improved = false;
27795
+ for (let r = 0; r <= maxRank; r++) {
27796
+ const ids = ranks[r];
27797
+ for (let i = 0; i < ids.length - 1; i++) {
27798
+ const before = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
27799
+ const a = ids[i], b = ids[i + 1];
27800
+ ids[i] = b;
27801
+ ids[i + 1] = a;
27802
+ orderOf.set(b, i);
27803
+ orderOf.set(a, i + 1);
27804
+ const after = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
27805
+ if (after < before) {
27806
+ improved = true;
27807
+ } else {
27808
+ ids[i] = a;
27809
+ ids[i + 1] = b;
27810
+ orderOf.set(a, i);
27811
+ orderOf.set(b, i + 1);
27812
+ }
27813
+ }
27814
+ }
27815
+ }
27816
+ };
27817
+ const initOrder = (seed) => {
27818
+ const r = seed === 0 ? null : rng(seed);
27819
+ for (let rr = 0; rr <= maxRank; rr++) {
27820
+ const ids = ranks[rr].slice();
27821
+ if (r) {
27822
+ for (let i = ids.length - 1; i > 0; i--) {
27823
+ const j = Math.floor(r() * (i + 1));
27824
+ [ids[i], ids[j]] = [ids[j], ids[i]];
27825
+ }
27826
+ }
27827
+ ranks[rr] = ids;
27828
+ ids.forEach((id, i) => orderOf.set(id, i));
27829
+ }
27830
+ };
27831
+ const found = [];
27832
+ for (let s = 0; s < restarts; s++) {
27833
+ initOrder(s);
27834
+ let best = totalCrossings();
27835
+ let bestSnap = new Map(orderOf);
27836
+ for (let iter = 0; iter < 8; iter++) {
27837
+ const down1 = iter % 2 === 0;
27838
+ if (down1) for (let r = 1; r <= maxRank; r++) wmedianRank(r, true);
27839
+ else for (let r = maxRank - 1; r >= 0; r--) wmedianRank(r, false);
27840
+ transpose();
27841
+ const c = totalCrossings();
27842
+ if (c < best) {
27843
+ best = c;
27844
+ bestSnap = new Map(orderOf);
27845
+ }
27846
+ if (c === 0) break;
27847
+ }
27848
+ applyOrder(bestSnap);
27849
+ for (let r = 0; r <= maxRank; r++)
27850
+ ranks[r].sort((a, b) => orderOf.get(a) - orderOf.get(b));
27851
+ const sig = ranks.map((r) => r.join(",")).join("|");
27852
+ if (!found.some((f) => f.sig === sig))
27853
+ found.push({ sig, cross: best, order: new Map(bestSnap) });
27854
+ }
27855
+ found.sort((a, b) => a.cross - b.cross);
27856
+ const keep = found.slice(0, keepK);
27857
+ if (keep.length === 0) return [];
27858
+ const results = [];
27859
+ for (const cand of keep) {
27860
+ applyOrder(cand.order);
27861
+ const rk = Array.from({ length: maxRank + 1 }, () => []);
27862
+ for (const n of node.values()) rk[n.rank].push(n.id);
27863
+ for (let r = 0; r <= maxRank; r++)
27864
+ rk[r].sort((a, b) => cand.order.get(a) - cand.order.get(b));
27865
+ results.push(realize(rk));
27866
+ }
27867
+ return results;
27868
+ function realize(rk) {
27869
+ const bandDepth = rk.map(
27870
+ (ids) => Math.max(0, ...ids.map((id) => node.get(id).depth))
27871
+ );
27872
+ const bandCenter = [];
27873
+ let acc = MARGIN3;
27874
+ for (let r = 0; r <= maxRank; r++) {
27875
+ bandCenter[r] = acc + bandDepth[r] / 2;
27876
+ acc += bandDepth[r] + ranksep;
27877
+ }
27878
+ for (let r = 0; r <= maxRank; r++) {
27879
+ let x = MARGIN3;
27880
+ for (const id of rk[r]) {
27881
+ const n = node.get(id);
27882
+ n.cross = x + n.thick / 2;
27883
+ x += n.thick + nodesep;
27884
+ }
27885
+ }
27886
+ const ITER = 18;
27887
+ for (let it = 0; it < ITER; it++) {
27888
+ const downPass = it % 2 === 0;
27889
+ const order = downPass ? Array.from({ length: maxRank + 1 }, (_, i) => i) : Array.from({ length: maxRank + 1 }, (_, i) => maxRank - i);
27890
+ for (const r of order) {
27891
+ const ids = rk[r];
27892
+ const desired = ids.map((id) => {
27893
+ const n = node.get(id);
27894
+ let sw = 0, sx2 = 0;
27895
+ for (const nb of up.get(id)) {
27896
+ const w = weight(n, node.get(nb));
27897
+ sw += w;
27898
+ sx2 += w * node.get(nb).cross;
27899
+ }
27900
+ for (const nb of down.get(id)) {
27901
+ const w = weight(n, node.get(nb));
27902
+ sw += w;
27903
+ sx2 += w * node.get(nb).cross;
27904
+ }
27905
+ return sw > 0 ? sx2 / sw : n.cross;
27906
+ });
27907
+ placeWithSeparation(ids, desired);
27908
+ }
27909
+ }
27910
+ let minC = Infinity;
27911
+ for (const n of node.values()) minC = Math.min(minC, n.cross - n.thick / 2);
27912
+ const shift = MARGIN3 - minC;
27913
+ for (const n of node.values()) n.cross += shift;
27914
+ const coord = (n) => isTB ? { x: n.cross, y: bandCenter[n.rank] } : { x: bandCenter[n.rank], y: n.cross };
27915
+ const layoutNodes = parsed.nodes.map((pn) => {
27916
+ const n = node.get(pn.label);
27917
+ const c = coord(n);
27918
+ return {
27919
+ label: pn.label,
27920
+ x: c.x,
27921
+ y: c.y,
27922
+ width: n.realW,
27923
+ height: n.realH
27924
+ };
27925
+ });
27926
+ const chainById = /* @__PURE__ */ new Map();
27927
+ for (const ch of chains) chainById.set(ch.edgeIdx, ch.chain);
27928
+ let minCross = Infinity, maxCross = -Infinity;
27929
+ for (const n of node.values()) {
27930
+ if (n.dummy) continue;
27931
+ minCross = Math.min(minCross, n.cross - n.thick / 2);
27932
+ maxCross = Math.max(maxCross, n.cross + n.thick / 2);
27933
+ }
27934
+ const GAP = 34;
27935
+ const LANE = 28;
27936
+ const sideOf = (b) => {
27937
+ if (backEdgeSide === "left") return -1;
27938
+ if (backEdgeSide === "right") return 1;
27939
+ const d = node.get(b.src).cross - node.get(b.tgt).cross;
27940
+ return d >= 0 ? 1 : -1;
27941
+ };
27942
+ const backSorted = backEdges.map((b) => ({
27943
+ ...b,
27944
+ side: sideOf(b),
27945
+ span: Math.abs(node.get(b.src).rank - node.get(b.tgt).rank)
27946
+ })).sort((a, b) => a.side - b.side || a.span - b.span);
27947
+ const sideCounts = /* @__PURE__ */ new Map();
27948
+ for (const b of backSorted)
27949
+ sideCounts.set(b.side, (sideCounts.get(b.side) ?? 0) + 1);
27950
+ const laneCounter = /* @__PURE__ */ new Map();
27951
+ const backPoints = /* @__PURE__ */ new Map();
27952
+ const faceLim = (depth) => Math.max(0, depth / 2 - 6);
27953
+ const hubIndex = /* @__PURE__ */ new Map();
27954
+ const hubSize = /* @__PURE__ */ new Map();
27955
+ {
27956
+ const counter = /* @__PURE__ */ new Map();
27957
+ for (const b of backSorted) {
27958
+ const key = `${b.side}|${b.tgt}`;
27959
+ const idx = counter.get(key) ?? 0;
27960
+ counter.set(key, idx + 1);
27961
+ hubIndex.set(b.edgeIdx, idx);
27962
+ }
27963
+ for (const [key, v] of counter) hubSize.set(key, v);
27964
+ }
27965
+ for (const b of backSorted) {
27966
+ const s = b.side;
27967
+ const k = laneCounter.get(s) ?? 0;
27968
+ laneCounter.set(s, k + 1);
27969
+ const K = sideCounts.get(s);
27970
+ const src = node.get(b.src);
27971
+ const tgt = node.get(b.tgt);
27972
+ const laneC = s > 0 ? maxCross + GAP + k * LANE : minCross - GAP - k * LANE;
27973
+ const srcCtr = bandCenter[src.rank];
27974
+ const tgtCtr = bandCenter[tgt.rank];
27975
+ const frac = K > 1 ? (k + 1) / (K + 1) : 0;
27976
+ const srcR = srcCtr + frac * faceLim(src.depth);
27977
+ const tgtR = tgtCtr - frac * faceLim(tgt.depth);
27978
+ const m = (c, r) => isTB ? { x: c, y: r } : { x: r, y: c };
27979
+ const off = 10;
27980
+ const srcEdgeC = src.cross + s * (src.thick / 2 + off);
27981
+ const tgtEdgeC = tgt.cross + s * (tgt.thick / 2 + off);
27982
+ const hubK = hubIndex.get(b.edgeIdx);
27983
+ const hubN = hubSize.get(`${s}|${b.tgt}`);
27984
+ if (hubN > 1 && hubK > 0) {
27985
+ const tgtTop = tgtCtr - (tgt.depth / 2 + GAP + (hubK - 1) * LANE);
27986
+ const entryCross = tgt.cross + s * faceLim(tgt.thick) * (hubK / hubN);
27987
+ backPoints.set(b.edgeIdx, [
27988
+ m(src.cross, srcCtr),
27989
+ m(srcEdgeC, srcR),
27990
+ m(laneC, srcR),
27991
+ m(laneC, tgtTop),
27992
+ m(entryCross, tgtTop),
27993
+ m(tgt.cross, tgtCtr)
27994
+ ]);
27995
+ } else {
27996
+ backPoints.set(b.edgeIdx, [
27997
+ m(src.cross, srcCtr),
27998
+ m(srcEdgeC, srcR),
27999
+ m(laneC, srcR),
28000
+ m(laneC, tgtR),
28001
+ m(tgtEdgeC, tgtR),
28002
+ m(tgt.cross, tgtCtr)
28003
+ ]);
28004
+ }
28005
+ }
28006
+ const layoutEdges = [];
28007
+ for (let i = 0; i < parsed.edges.length; i++) {
28008
+ const e = parsed.edges[i];
28009
+ if (!labelSet.has(e.source) || !labelSet.has(e.target)) continue;
28010
+ let points;
28011
+ if (e.source === e.target) {
28012
+ const c = coord(node.get(e.source));
28013
+ points = [c, c];
28014
+ } else if (backPoints.has(i)) {
28015
+ points = backPoints.get(i);
28016
+ } else {
28017
+ const chain = chainById.get(i);
28018
+ if (!chain) continue;
28019
+ points = chain.map((id) => coord(node.get(id)));
28020
+ }
28021
+ layoutEdges.push({
28022
+ source: e.source,
28023
+ target: e.target,
28024
+ ...e.label !== void 0 && { label: e.label },
28025
+ bidirectional: e.bidirectional,
28026
+ lineNumber: e.lineNumber,
28027
+ points,
28028
+ yOffset: 0,
28029
+ parallelCount: 1,
28030
+ metadata: e.metadata
28031
+ });
28032
+ }
28033
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
28034
+ for (const n of layoutNodes) {
28035
+ minX = Math.min(minX, n.x - n.width / 2);
28036
+ minY = Math.min(minY, n.y - n.height / 2);
28037
+ maxX = Math.max(maxX, n.x + n.width / 2);
28038
+ maxY = Math.max(maxY, n.y + n.height / 2);
28039
+ }
28040
+ for (const e of layoutEdges)
28041
+ for (const p of e.points) {
28042
+ minX = Math.min(minX, p.x);
28043
+ minY = Math.min(minY, p.y);
28044
+ maxX = Math.max(maxX, p.x);
28045
+ maxY = Math.max(maxY, p.y);
28046
+ }
28047
+ const sx = MARGIN3 - minX;
28048
+ const sy = MARGIN3 - minY;
28049
+ const shifted = sx !== 0 || sy !== 0 ? {
28050
+ nodes: layoutNodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })),
28051
+ edges: layoutEdges.map((e) => ({
28052
+ ...e,
28053
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
28054
+ }))
28055
+ } : { nodes: layoutNodes, edges: layoutEdges };
28056
+ return {
28057
+ nodes: shifted.nodes,
28058
+ edges: shifted.edges,
28059
+ groups: [],
28060
+ width: maxX + sx + MARGIN3,
28061
+ height: maxY + sy + MARGIN3
28062
+ };
28063
+ }
28064
+ function weight(a, b) {
28065
+ if (a.dummy && b.dummy) return W_DUMMY_DUMMY;
28066
+ if (a.dummy || b.dummy) return W_REAL_DUMMY;
28067
+ return W_REAL_REAL;
28068
+ }
28069
+ function placeWithSeparation(ids, desired) {
28070
+ const n = ids.length;
28071
+ if (n === 0) return;
28072
+ const half = ids.map((id) => node.get(id).thick / 2);
28073
+ const off = [0];
28074
+ for (let i = 1; i < n; i++)
28075
+ off[i] = off[i - 1] + half[i - 1] + nodesep + half[i];
28076
+ const d = desired.map((v, i) => v - off[i]);
28077
+ const blocks = [];
28078
+ for (let i = 0; i < n; i++) {
28079
+ let b = { pos: d[i], count: 1, sumOffset: d[i], first: i };
28080
+ while (blocks.length && blocks[blocks.length - 1].pos >= b.pos) {
28081
+ const prev = blocks.pop();
28082
+ const count = prev.count + b.count;
28083
+ const sumOffset = prev.sumOffset + b.sumOffset;
28084
+ b = { pos: sumOffset / count, count, sumOffset, first: prev.first };
28085
+ }
28086
+ blocks.push(b);
28087
+ }
28088
+ const xs = new Array(n);
28089
+ for (const b of blocks)
28090
+ for (let k = 0; k < b.count; k++) xs[b.first + k] = b.pos;
28091
+ for (let i = 0; i < n; i++) node.get(ids[i]).cross = xs[i] + off[i];
28092
+ }
28093
+ }
28094
+ var NODESEP, RANKSEP, DUMMY_THICK, MARGIN3, W_REAL_REAL, W_REAL_DUMMY, W_DUMMY_DUMMY;
28095
+ var init_layout_layered = __esm({
28096
+ "src/boxes-and-lines/layout-layered.ts"() {
28097
+ "use strict";
28098
+ init_layout5();
28099
+ NODESEP = 50;
28100
+ RANKSEP = 60;
28101
+ DUMMY_THICK = 10;
28102
+ MARGIN3 = 40;
28103
+ W_REAL_REAL = 1;
28104
+ W_REAL_DUMMY = 2;
28105
+ W_DUMMY_DUMMY = 8;
28106
+ }
28107
+ });
28108
+
28109
+ // src/boxes-and-lines/layout-search.ts
28110
+ var layout_search_exports = {};
28111
+ __export(layout_search_exports, {
28112
+ countEdgeNearMiss: () => countEdgeNearMiss,
28113
+ countEdgeNodePierces: () => countEdgeNodePierces,
28114
+ countEdgeOverlaps: () => countEdgeOverlaps,
28115
+ countGroupOverlaps: () => countGroupOverlaps,
28116
+ countSplineCrossings: () => countSplineCrossings,
28117
+ deroutePierces: () => deroutePierces,
28118
+ detectEdgeNodePierces: () => detectEdgeNodePierces,
28119
+ detectEdgeOverlaps: () => detectEdgeOverlaps,
28120
+ detectGroupOverlaps: () => detectGroupOverlaps,
28121
+ layoutBoxesAndLinesSearch: () => layoutBoxesAndLinesSearch,
28122
+ separateGroupBands: () => separateGroupBands
28123
+ });
28124
+ function rng2(s) {
28125
+ return () => {
28126
+ s |= 0;
28127
+ s = s + 1831565813 | 0;
28128
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
28129
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
28130
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
28131
+ };
28132
+ }
28133
+ function shuffle(a, r) {
28134
+ const x = a.slice();
28135
+ for (let i = x.length - 1; i > 0; i--) {
28136
+ const j = Math.floor(r() * (i + 1));
28137
+ [x[i], x[j]] = [x[j], x[i]];
28138
+ }
28139
+ return x;
28140
+ }
28141
+ function flatten(d) {
28142
+ const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
28143
+ const pts = [];
28144
+ let i = 0, cx = 0, cy = 0, cmd = "";
28145
+ const num = () => parseFloat(toks[i++]);
28146
+ const samp = (p0, c1, c2, p1) => {
28147
+ for (let t = 0; t <= 1; t += 0.12) {
28148
+ const u = 1 - t;
28149
+ if (c2)
28150
+ pts.push({
28151
+ x: u * u * u * p0.x + 3 * u * u * t * c1.x + 3 * u * t * t * c2.x + t * t * t * p1.x,
28152
+ y: u * u * u * p0.y + 3 * u * u * t * c1.y + 3 * u * t * t * c2.y + t * t * t * p1.y
28153
+ });
28154
+ else
28155
+ pts.push({
28156
+ x: u * u * p0.x + 2 * u * t * c1.x + t * t * p1.x,
28157
+ y: u * u * p0.y + 2 * u * t * c1.y + t * t * p1.y
28158
+ });
28159
+ }
28160
+ };
28161
+ while (i < toks.length) {
28162
+ const tk = toks[i];
28163
+ if (/[MLQC]/i.test(tk)) {
28164
+ cmd = tk;
28165
+ i++;
28166
+ }
28167
+ if (cmd === "M" || cmd === "L") {
28168
+ const x = num(), y = num();
28169
+ pts.push({ x, y });
28170
+ cx = x;
28171
+ cy = y;
28172
+ } else if (cmd === "Q") {
28173
+ const c1 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
28174
+ samp({ x: cx, y: cy }, c1, null, p1);
28175
+ cx = p1.x;
28176
+ cy = p1.y;
28177
+ } else if (cmd === "C") {
28178
+ const c1 = { x: num(), y: num() }, c2 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
28179
+ samp({ x: cx, y: cy }, c1, c2, p1);
28180
+ cx = p1.x;
28181
+ cy = p1.y;
28182
+ } else i++;
28183
+ }
28184
+ return pts;
28185
+ }
28186
+ function segPoint(p1, p2, p3, p4) {
28187
+ const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
28188
+ if (Math.abs(den) < 1e-9) return null;
28189
+ 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;
28190
+ 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;
28191
+ }
28192
+ function countSplineCrossings(layout) {
28193
+ const center = /* @__PURE__ */ new Map();
28194
+ for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
28195
+ for (const g of layout.groups)
28196
+ if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
28197
+ const polys = layout.edges.map((e) => {
28198
+ const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
28199
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28200
+ for (const p of pts) {
28201
+ if (p.x < x0) x0 = p.x;
28202
+ if (p.x > x1) x1 = p.x;
28203
+ if (p.y < y0) y0 = p.y;
28204
+ if (p.y > y1) y1 = p.y;
28205
+ }
28206
+ return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
28207
+ });
28208
+ const R = 34;
28209
+ let total = 0;
28210
+ for (let a = 0; a < polys.length; a++)
28211
+ for (let b = a + 1; b < polys.length; b++) {
28212
+ const A = polys[a], B = polys[b];
28213
+ if (A.pts.length < 2 || B.pts.length < 2) continue;
28214
+ if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
28215
+ const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
28216
+ const hits = [];
28217
+ for (let i = 1; i < A.pts.length; i++)
28218
+ for (let j = 1; j < B.pts.length; j++) {
28219
+ const p = segPoint(
28220
+ A.pts[i - 1],
28221
+ A.pts[i],
28222
+ B.pts[j - 1],
28223
+ B.pts[j]
28224
+ );
28225
+ if (!p) continue;
28226
+ if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
28227
+ continue;
28228
+ if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
28229
+ hits.push(p);
28230
+ }
28231
+ total += hits.length;
28232
+ }
28233
+ return total;
28234
+ }
28235
+ function pointSegDist(p, a, b) {
28236
+ const dx = b.x - a.x, dy = b.y - a.y;
28237
+ const len2 = dx * dx + dy * dy;
28238
+ if (len2 < 1e-9) return Math.hypot(p.x - a.x, p.y - a.y);
28239
+ let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
28240
+ t = Math.max(0, Math.min(1, t));
28241
+ return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));
28242
+ }
28243
+ function distToPoly(p, poly) {
28244
+ let m = Infinity;
28245
+ for (let i = 1; i < poly.length; i++)
28246
+ m = Math.min(m, pointSegDist(p, poly[i - 1], poly[i]));
28247
+ return m;
28248
+ }
28249
+ function pointRectDist(p, r) {
28250
+ const dx = Math.max(r.x - r.w / 2 - p.x, 0, p.x - (r.x + r.w / 2));
28251
+ const dy = Math.max(r.y - r.h / 2 - p.y, 0, p.y - (r.y + r.h / 2));
28252
+ return Math.hypot(dx, dy);
28253
+ }
28254
+ function detectEdgeOverlaps(layout, opts) {
28255
+ const dist = opts?.dist ?? 8;
28256
+ const minLen = opts?.minLen ?? 16;
28257
+ const nodeClear = opts?.nodeClear ?? 12;
28258
+ const rect = /* @__PURE__ */ new Map();
28259
+ for (const n of layout.nodes)
28260
+ rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
28261
+ for (const g of layout.groups)
28262
+ if (g.collapsed)
28263
+ rect.set("__group_" + g.label, {
28264
+ x: g.x,
28265
+ y: g.y,
28266
+ w: g.width,
28267
+ h: g.height
28268
+ });
28269
+ const polys = layout.edges.map((e) => {
28270
+ const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
28271
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28272
+ for (const p of pts) {
28273
+ if (p.x < x0) x0 = p.x;
28274
+ if (p.x > x1) x1 = p.x;
28275
+ if (p.y < y0) y0 = p.y;
28276
+ if (p.y > y1) y1 = p.y;
28277
+ }
28278
+ return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
28279
+ });
28280
+ const runs = [];
28281
+ for (let a = 0; a < polys.length; a++)
28282
+ for (let b = a + 1; b < polys.length; b++) {
28283
+ const A = polys[a], B = polys[b];
28284
+ if (A.pts.length < 2 || B.pts.length < 2) continue;
28285
+ if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
28286
+ continue;
28287
+ const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
28288
+ let run = [];
28289
+ let runLen = 0;
28290
+ const flush = () => {
28291
+ if (runLen >= minLen && run.length >= 2)
28292
+ runs.push({
28293
+ mid: run[Math.floor(run.length / 2)],
28294
+ length: runLen,
28295
+ pts: run.slice()
28296
+ });
28297
+ run = [];
28298
+ runLen = 0;
28299
+ };
28300
+ for (const p of A.pts) {
28301
+ const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
28302
+ const covered = !nearShared && distToPoly(p, B.pts) < dist;
28303
+ if (covered) {
28304
+ if (run.length)
28305
+ runLen += Math.hypot(
28306
+ p.x - run[run.length - 1].x,
28307
+ p.y - run[run.length - 1].y
28308
+ );
28309
+ run.push(p);
28310
+ } else flush();
28311
+ }
28312
+ flush();
28313
+ }
28314
+ return runs;
28315
+ }
28316
+ function countEdgeOverlaps(layout, opts) {
28317
+ return detectEdgeOverlaps(layout, opts).length;
28318
+ }
28319
+ function detectEdgeNodePierces(layout, opts) {
28320
+ const inset = opts?.inset ?? 6;
28321
+ const minPts = opts?.minPts ?? 2;
28322
+ const rects = [];
28323
+ for (const n of layout.nodes)
28324
+ rects.push({ key: n.label, x: n.x, y: n.y, w: n.width, h: n.height });
28325
+ for (const g of layout.groups)
28326
+ if (g.collapsed)
28327
+ rects.push({
28328
+ key: "__group_" + g.label,
28329
+ x: g.x,
28330
+ y: g.y,
28331
+ w: g.width,
28332
+ h: g.height
28333
+ });
28334
+ const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
28335
+ const out = [];
28336
+ layout.edges.forEach((e, idx) => {
28337
+ if (e.points.length < 2) return;
28338
+ const poly = flatten(splineGen(e.points) ?? "");
28339
+ for (const r of rects) {
28340
+ if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
28341
+ continue;
28342
+ const hits = poly.filter((p) => inside(p, r));
28343
+ if (hits.length >= minPts)
28344
+ out.push({ edgeIdx: idx, node: r.key, pts: hits });
28345
+ }
28346
+ });
28347
+ return out;
28348
+ }
28349
+ function countEdgeNodePierces(layout, opts) {
28350
+ return detectEdgeNodePierces(layout, opts).length;
28351
+ }
28352
+ function deroutePierces(layout) {
28353
+ const pierces = detectEdgeNodePierces(layout);
28354
+ if (!pierces.length) return layout;
28355
+ const rect = /* @__PURE__ */ new Map();
28356
+ for (const n of layout.nodes)
28357
+ rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
28358
+ for (const g of layout.groups)
28359
+ if (g.collapsed)
28360
+ rect.set("__group_" + g.label, {
28361
+ x: g.x,
28362
+ y: g.y,
28363
+ w: g.width,
28364
+ h: g.height
28365
+ });
28366
+ const byEdge = /* @__PURE__ */ new Map();
28367
+ for (const p of pierces) {
28368
+ const arr = byEdge.get(p.edgeIdx);
28369
+ if (arr) arr.push(p.node);
28370
+ else byEdge.set(p.edgeIdx, [p.node]);
28371
+ }
28372
+ const edges = layout.edges.map((e, idx) => {
28373
+ const nodes = byEdge.get(idx);
28374
+ if (!nodes) return e;
28375
+ let pts = e.points.map((p) => ({ x: p.x, y: p.y }));
28376
+ for (const label of nodes) {
28377
+ const r = rect.get(label);
28378
+ if (r) pts = detourAround(pts, r);
28379
+ }
28380
+ return { ...e, points: pts };
28381
+ });
28382
+ return { ...layout, edges };
28383
+ }
28384
+ function detourAround(pts, r) {
28385
+ if (pts.length < 2) return pts;
28386
+ const c = { x: r.x, y: r.y };
28387
+ let bestSeg = -1;
28388
+ let bestT = 0;
28389
+ let bestD = Infinity;
28390
+ let bestPt = pts[0];
28391
+ for (let i = 0; i < pts.length - 1; i++) {
28392
+ const a2 = pts[i], b2 = pts[i + 1];
28393
+ const dx = b2.x - a2.x, dy = b2.y - a2.y;
28394
+ const len2 = dx * dx + dy * dy || 1e-9;
28395
+ let t = ((c.x - a2.x) * dx + (c.y - a2.y) * dy) / len2;
28396
+ t = Math.max(0, Math.min(1, t));
28397
+ const q = { x: a2.x + t * dx, y: a2.y + t * dy };
28398
+ const d = Math.hypot(q.x - c.x, q.y - c.y);
28399
+ if (d < bestD) {
28400
+ bestD = d;
28401
+ bestSeg = i;
28402
+ bestT = t;
28403
+ bestPt = q;
28404
+ }
28405
+ }
28406
+ if (bestSeg < 0) return pts;
28407
+ let nx = bestPt.x - c.x;
28408
+ let ny = bestPt.y - c.y;
28409
+ if (Math.hypot(nx, ny) < 1) {
28410
+ if (r.w <= r.h) {
28411
+ nx = 1;
28412
+ ny = 0;
28413
+ } else {
28414
+ nx = 0;
28415
+ ny = 1;
28416
+ }
28417
+ }
28418
+ const nlen = Math.hypot(nx, ny) || 1;
28419
+ nx /= nlen;
28420
+ ny /= nlen;
28421
+ const a = pts[bestSeg], b = pts[bestSeg + 1];
28422
+ let ex = b.x - a.x, ey = b.y - a.y;
28423
+ const elen = Math.hypot(ex, ey) || 1;
28424
+ ex /= elen;
28425
+ ey /= elen;
28426
+ const clear = Math.hypot(r.w, r.h) / 2 + 18;
28427
+ const hw = Math.hypot(r.w, r.h) / 2;
28428
+ void bestT;
28429
+ const before = {
28430
+ x: bestPt.x - ex * hw + nx * clear * 0.5,
28431
+ y: bestPt.y - ey * hw + ny * clear * 0.5
28432
+ };
28433
+ const peak = { x: c.x + nx * clear, y: c.y + ny * clear };
28434
+ const after = {
28435
+ x: bestPt.x + ex * hw + nx * clear * 0.5,
28436
+ y: bestPt.y + ey * hw + ny * clear * 0.5
28437
+ };
28438
+ const out = pts.slice(0, bestSeg + 1);
28439
+ out.push(before, peak, after);
28440
+ out.push(...pts.slice(bestSeg + 1));
28441
+ return out;
28442
+ }
28443
+ function countEdgeNearMiss(layout, opts) {
28444
+ const near = opts?.near ?? 14;
28445
+ const tight = opts?.tight ?? 8;
28446
+ const minLen = opts?.minLen ?? 18;
28447
+ const close = detectEdgeOverlaps(layout, { dist: near, minLen }).length;
28448
+ const over = detectEdgeOverlaps(layout, { dist: tight, minLen }).length;
28449
+ return Math.max(0, close - over);
28450
+ }
28451
+ function detectGroupOverlaps(layout, opts) {
28452
+ const margin = opts?.margin ?? 4;
28453
+ const raw = layout.groups.map((g) => ({
28454
+ label: g.label,
28455
+ l: g.x - g.width / 2,
28456
+ r: g.x + g.width / 2,
28457
+ t: g.y - g.height / 2,
28458
+ b: g.y + g.height / 2
28459
+ }));
28460
+ const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
28461
+ const rend = raw.map((a, i) => {
28462
+ const parent = raw.some((b, j) => j !== i && contains(a, b));
28463
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
28464
+ });
28465
+ const hit = /* @__PURE__ */ new Set();
28466
+ for (let i = 0; i < raw.length; i++)
28467
+ for (let j = i + 1; j < raw.length; j++) {
28468
+ if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
28469
+ const a = rend[i], b = rend[j];
28470
+ const dx = Math.max(a.l - b.r, b.l - a.r);
28471
+ const dy = Math.max(a.t - b.b, b.t - a.b);
28472
+ if (Math.max(dx, dy) < margin) {
28473
+ hit.add(raw[i].label);
28474
+ hit.add(raw[j].label);
28475
+ }
28476
+ }
28477
+ return [...hit];
28478
+ }
28479
+ function countGroupOverlaps(layout, opts) {
28480
+ const margin = opts?.margin ?? 4;
28481
+ const raw = layout.groups.map((g) => ({
28482
+ l: g.x - g.width / 2,
28483
+ r: g.x + g.width / 2,
28484
+ t: g.y - g.height / 2,
28485
+ b: g.y + g.height / 2
28486
+ }));
28487
+ const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
28488
+ const rend = raw.map((a, i) => {
28489
+ const parent = raw.some((b, j) => j !== i && contains(a, b));
28490
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
28491
+ });
28492
+ let count = 0;
28493
+ for (let i = 0; i < raw.length; i++)
28494
+ for (let j = i + 1; j < raw.length; j++) {
28495
+ if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
28496
+ const a = rend[i], b = rend[j];
28497
+ const dx = Math.max(a.l - b.r, b.l - a.r);
28498
+ const dy = Math.max(a.t - b.b, b.t - a.b);
28499
+ if (Math.max(dx, dy) < margin) count++;
28500
+ }
28501
+ return count;
28502
+ }
28503
+ function separateGroupBands(layout, parsed) {
28504
+ if (layout.groups.some((g) => g.collapsed)) return layout;
28505
+ if (countGroupOverlaps(layout) === 0) return layout;
28506
+ const parentOf = /* @__PURE__ */ new Map();
28507
+ for (const g of parsed.groups) parentOf.set(g.label, g.parentGroup);
28508
+ const topOf = (label) => {
28509
+ let cur = label;
28510
+ const seen = /* @__PURE__ */ new Set();
28511
+ for (; ; ) {
28512
+ const p = parentOf.get(cur);
28513
+ if (!p || seen.has(p)) return cur;
28514
+ seen.add(cur);
28515
+ cur = p;
28516
+ }
28517
+ };
28518
+ const topLabels = parsed.groups.filter((g) => !g.parentGroup).map((g) => g.label);
28519
+ if (topLabels.length < 2) return layout;
28520
+ const nodeTop = /* @__PURE__ */ new Map();
28521
+ for (const g of parsed.groups)
28522
+ for (const child of g.children)
28523
+ if (!parsed.groups.some((gg) => gg.label === child))
28524
+ nodeTop.set(child, topOf(g.label));
28525
+ const axis = parsed.direction === "TB" ? "x" : "y";
28526
+ const boxByLabel = new Map(layout.groups.map((g) => [g.label, g]));
28527
+ const bands = topLabels.map((label) => {
28528
+ const g = boxByLabel.get(label);
28529
+ const half2 = (axis === "y" ? g.height : g.width) / 2;
28530
+ let lo = (axis === "y" ? g.y : g.x) - half2;
28531
+ const hi = (axis === "y" ? g.y : g.x) + half2;
28532
+ const isParent = parsed.groups.some((c) => c.parentGroup === label);
28533
+ if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE2;
28534
+ return { label, lo, hi, c: (lo + hi) / 2 };
28535
+ });
28536
+ bands.sort((a, b) => a.c - b.c);
28537
+ const GAP = 16;
28538
+ const half = bands.map((b) => (b.hi - b.lo) / 2);
28539
+ const off = [0];
28540
+ for (let i = 1; i < bands.length; i++)
28541
+ off[i] = off[i - 1] + half[i - 1] + GAP + half[i];
28542
+ const desired = bands.map((b, i) => b.c - off[i]);
28543
+ const blocks = [];
28544
+ for (let i = 0; i < bands.length; i++) {
28545
+ let blk = { pos: desired[i], count: 1, sum: desired[i], first: i };
28546
+ while (blocks.length && blocks[blocks.length - 1].pos >= blk.pos) {
28547
+ const prev = blocks.pop();
28548
+ const count = prev.count + blk.count;
28549
+ const sum = prev.sum + blk.sum;
28550
+ blk = { pos: sum / count, count, sum, first: prev.first };
28551
+ }
28552
+ blocks.push(blk);
28553
+ }
28554
+ const newC = new Array(bands.length);
28555
+ for (const blk of blocks)
28556
+ for (let k = 0; k < blk.count; k++)
28557
+ newC[blk.first + k] = blk.pos + off[blk.first + k];
28558
+ const delta = /* @__PURE__ */ new Map();
28559
+ let moved = false;
28560
+ bands.forEach((b, i) => {
28561
+ const d = newC[i] - b.c;
28562
+ delta.set(b.label, d);
28563
+ if (Math.abs(d) > 0.5) moved = true;
28564
+ });
28565
+ if (!moved) return layout;
28566
+ const nodeDelta = (label) => {
28567
+ const top = nodeTop.get(label);
28568
+ return top ? delta.get(top) ?? 0 : 0;
28569
+ };
28570
+ const shift = (p, d) => axis === "y" ? { x: p.x, y: p.y + d } : { x: p.x + d, y: p.y };
28571
+ const nodes = layout.nodes.map((n) => {
28572
+ const d = nodeDelta(n.label);
28573
+ return d ? { ...n, ...axis === "y" ? { y: n.y + d } : { x: n.x + d } } : n;
28574
+ });
28575
+ const groups = layout.groups.map((g) => {
28576
+ const d = delta.get(topOf(g.label)) ?? 0;
28577
+ return d ? { ...g, ...axis === "y" ? { y: g.y + d } : { x: g.x + d } } : g;
28578
+ });
28579
+ const edges = layout.edges.map((e) => {
28580
+ const ds = nodeDelta(e.source);
28581
+ const dt = nodeDelta(e.target);
28582
+ if (!ds && !dt) return e;
28583
+ const N = e.points.length;
28584
+ const points = e.points.map((p, i) => {
28585
+ const f = N > 1 ? i / (N - 1) : 0;
28586
+ return shift(p, ds * (1 - f) + dt * f);
28587
+ });
28588
+ return { ...e, points };
28589
+ });
28590
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
28591
+ const acc = (x, y) => {
28592
+ if (x < minX) minX = x;
28593
+ if (x > maxX) maxX = x;
28594
+ if (y < minY) minY = y;
28595
+ if (y > maxY) maxY = y;
28596
+ };
28597
+ for (const n of nodes) {
28598
+ acc(n.x - n.width / 2, n.y - n.height / 2);
28599
+ acc(n.x + n.width / 2, n.y + n.height / 2);
28600
+ }
28601
+ for (const g of groups) {
28602
+ acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE2);
28603
+ acc(g.x + g.width / 2, g.y + g.height / 2);
28604
+ }
28605
+ for (const e of edges) for (const p of e.points) acc(p.x, p.y);
28606
+ const M = 40;
28607
+ const sx = M - minX, sy = M - minY;
28608
+ const reshift = sx !== 0 || sy !== 0;
28609
+ return {
28610
+ nodes: reshift ? nodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })) : nodes,
28611
+ groups: reshift ? groups.map((g) => ({ ...g, x: g.x + sx, y: g.y + sy })) : groups,
28612
+ edges: reshift ? edges.map((e) => ({
28613
+ ...e,
28614
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
28615
+ })) : edges,
28616
+ width: maxX - minX + 2 * M,
28617
+ height: maxY - minY + 2 * M
28618
+ };
28619
+ }
28620
+ function segCross(p1, p2, p3, p4) {
28621
+ const d1x = p2.x - p1.x, d1y = p2.y - p1.y, d2x = p4.x - p3.x, d2y = p4.y - p3.y;
28622
+ const den = d1x * d2y - d1y * d2x;
28623
+ if (Math.abs(den) < 1e-9) return false;
28624
+ const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / den;
28625
+ const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / den;
28626
+ return t > 1e-3 && t < 0.999 && s > 1e-3 && s < 0.999;
28627
+ }
28628
+ function countCrossingsFast(layout) {
28629
+ const E = layout.edges.filter((e) => e.points.length >= 2);
28630
+ const bb = E.map((e) => {
28631
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28632
+ for (const p of e.points) {
28633
+ if (p.x < x0) x0 = p.x;
28634
+ if (p.x > x1) x1 = p.x;
28635
+ if (p.y < y0) y0 = p.y;
28636
+ if (p.y > y1) y1 = p.y;
28637
+ }
28638
+ return { x0, y0, x1, y1 };
28639
+ });
28640
+ let count = 0;
28641
+ for (let i = 0; i < E.length; i++)
28642
+ for (let j = i + 1; j < E.length; j++) {
28643
+ const A = E[i], B = E[j];
28644
+ if (A.source === B.source || A.source === B.target || A.target === B.source || A.target === B.target)
28645
+ continue;
28646
+ const a = bb[i], b = bb[j];
28647
+ if (a.x1 < b.x0 || b.x1 < a.x0 || a.y1 < b.y0 || b.y1 < a.y0) continue;
28648
+ const pa = A.points, pb = B.points;
28649
+ let hit = false;
28650
+ for (let ai = 0; ai < pa.length - 1 && !hit; ai++)
28651
+ for (let bi = 0; bi < pb.length - 1; bi++) {
28652
+ if (segCross(pa[ai], pa[ai + 1], pb[bi], pb[bi + 1])) {
28653
+ hit = true;
28654
+ break;
28655
+ }
28656
+ }
28657
+ if (hit) count++;
28658
+ }
28659
+ return count;
28660
+ }
28661
+ function meanDrift(layout, prev) {
28662
+ if (!prev?.size) return 0;
28663
+ let sum = 0, n = 0;
28664
+ for (const node of layout.nodes) {
28665
+ const p = prev.get(node.label);
28666
+ if (p) {
28667
+ sum += Math.hypot(node.x - p.x, node.y - p.y);
28668
+ n++;
28669
+ }
28670
+ }
28671
+ return n ? sum / n : 0;
28672
+ }
28673
+ function edgeLength(layout) {
28674
+ let total = 0;
28675
+ for (const e of layout.edges)
28676
+ for (let i = 1; i < e.points.length; i++)
28677
+ total += Math.hypot(
28678
+ e.points[i].x - e.points[i - 1].x,
28679
+ e.points[i].y - e.points[i - 1].y
28680
+ );
28681
+ return total;
28682
+ }
28683
+ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
28684
+ const hideDescriptions = opts?.hideDescriptions ?? false;
28685
+ const collapsedGroupLabels = /* @__PURE__ */ new Set();
28686
+ if (collapseInfo) {
28687
+ const missing = /* @__PURE__ */ new Set();
28688
+ for (const og of collapseInfo.originalGroups)
28689
+ if (!parsed.groups.some((g) => g.label === og.label))
28690
+ missing.add(og.label);
28691
+ for (const label of missing) {
28692
+ const og = collapseInfo.originalGroups.find((g) => g.label === label);
28693
+ const parent = og?.parentGroup;
28694
+ if (!parent || !missing.has(parent)) collapsedGroupLabels.add(label);
28695
+ }
28696
+ }
28697
+ const sizes = /* @__PURE__ */ new Map();
28698
+ let maxDescH = 0;
28699
+ for (const node of parsed.nodes) {
28700
+ const s = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
28701
+ sizes.set(node.label, s);
28702
+ if (!hideDescriptions && node.description && node.description.length > 0)
28703
+ maxDescH = Math.max(maxDescH, s.height);
28704
+ }
28705
+ if (maxDescH > 0) {
28706
+ for (const node of parsed.nodes)
28707
+ if (node.description && node.description.length > 0) {
28708
+ const s = sizes.get(node.label);
28709
+ sizes.set(node.label, { width: s.width, height: maxDescH });
28710
+ }
28711
+ }
28712
+ const gid = (label) => `__group_${label}`;
28713
+ const rankdir = parsed.direction === "TB" ? "TB" : "LR";
28714
+ function place(cfg) {
28715
+ const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
28716
+ const ord = (a) => r ? shuffle(a, r) : a.slice();
28717
+ const g = new import_dagre4.default.graphlib.Graph({ compound: true, multigraph: true });
28718
+ g.setGraph({
28719
+ rankdir,
28720
+ ranker: cfg.ranker,
28721
+ nodesep: cfg.nodesep,
28722
+ ranksep: cfg.ranksep,
28723
+ edgesep: 20,
28724
+ marginx: 40,
28725
+ marginy: 40
28726
+ });
28727
+ g.setDefaultEdgeLabel(() => ({}));
28728
+ for (const grp of ord(parsed.groups))
28729
+ g.setNode(gid(grp.label), { label: grp.label });
28730
+ for (const node of ord(parsed.nodes)) {
28731
+ const s = sizes.get(node.label);
28732
+ g.setNode(node.label, { width: s.width, height: s.height });
28733
+ }
28734
+ for (const label of collapsedGroupLabels)
28735
+ g.setNode(gid(label), { width: NODE_WIDTH, height: NODE_HEIGHT });
28736
+ for (const grp of parsed.groups) {
28737
+ if (grp.parentGroup && g.hasNode(gid(grp.parentGroup)))
28738
+ g.setParent(gid(grp.label), gid(grp.parentGroup));
28739
+ for (const c of ord(grp.children)) {
28740
+ if (g.hasNode(c)) g.setParent(c, gid(grp.label));
28741
+ }
28742
+ }
28743
+ if (collapseInfo)
28744
+ for (const label of collapsedGroupLabels) {
28745
+ const og = collapseInfo.originalGroups.find((x) => x.label === label);
28746
+ if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && g.hasNode(gid(og.parentGroup)))
28747
+ g.setParent(gid(label), gid(og.parentGroup));
28748
+ }
28749
+ for (const e of ord(parsed.edges))
28750
+ if (g.hasNode(e.source) && g.hasNode(e.target))
28751
+ g.setEdge(e.source, e.target, {});
28752
+ import_dagre4.default.layout(g);
28753
+ const nodes = parsed.nodes.map((n2) => {
28754
+ const p = g.node(n2.label);
28755
+ return {
28756
+ label: n2.label,
28757
+ x: p.x,
28758
+ y: p.y,
28759
+ width: p.width,
28760
+ height: p.height
28761
+ };
28762
+ });
28763
+ const groups = parsed.groups.map(
28764
+ (grp) => {
28765
+ const p = g.node(gid(grp.label));
28766
+ return {
28767
+ label: grp.label,
28768
+ lineNumber: grp.lineNumber,
28769
+ x: p.x,
28770
+ y: p.y,
28771
+ width: p.width,
28772
+ height: p.height,
28773
+ collapsed: false,
28774
+ childCount: grp.children.length
28775
+ };
28776
+ }
28777
+ );
28778
+ for (const label of collapsedGroupLabels) {
28779
+ const p = g.node(gid(label));
28780
+ groups.push({
28781
+ label,
28782
+ lineNumber: 0,
28783
+ x: p.x,
28784
+ y: p.y,
28785
+ width: p.width,
28786
+ height: p.height,
28787
+ collapsed: true,
28788
+ childCount: collapseInfo?.collapsedChildCounts.get(label) ?? 0
28789
+ });
28790
+ }
28791
+ const edges = parsed.edges.filter((e) => g.hasEdge(e.source, e.target)).map((e) => {
28792
+ const ed = g.edge(e.source, e.target);
28793
+ return {
28794
+ source: e.source,
28795
+ target: e.target,
28796
+ ...e.label !== void 0 && { label: e.label },
28797
+ bidirectional: e.bidirectional,
28798
+ lineNumber: e.lineNumber,
28799
+ points: ed?.points ?? [],
28800
+ yOffset: 0,
28801
+ parallelCount: 1,
28802
+ metadata: e.metadata
28803
+ };
28804
+ });
28805
+ const gg = g.graph();
28806
+ return {
28807
+ nodes,
28808
+ edges,
28809
+ groups,
28810
+ width: gg.width ?? 800,
28811
+ height: gg.height ?? 600
28812
+ };
28813
+ }
28814
+ const n = parsed.nodes.length;
28815
+ const seedCount = opts?.seeds ?? (n <= 12 ? 80 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
28816
+ const REFINE_K = opts?.refineK ?? 6;
28817
+ const lambda = opts?.lambda ?? DEFAULT_LAMBDA;
28818
+ const prev = opts?.previousPositions;
28819
+ const RANKERS = ["network-simplex", "tight-tree", "longest-path"];
28820
+ const SPACINGS = [
28821
+ { nodesep: 50, ranksep: 60 },
28822
+ { nodesep: 34, ranksep: 46 },
28823
+ { nodesep: 66, ranksep: 82 }
28824
+ ];
28825
+ const configs = [];
28826
+ for (const ranker of RANKERS)
28827
+ for (const sp of SPACINGS) configs.push({ ranker, ...sp });
28828
+ for (let s = 0; s < seedCount; s++)
28829
+ configs.push({
28830
+ ranker: "network-simplex",
28831
+ nodesep: 50,
28832
+ ranksep: 60,
28833
+ seed: s
28834
+ });
28835
+ const badness = (lay, floor) => {
28836
+ const x = countSplineCrossings(lay);
28837
+ if (x > floor) return Infinity;
28838
+ return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
28839
+ };
28840
+ const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
28841
+ const pool = [];
28842
+ for (const cfg of configs) {
28843
+ try {
28844
+ pool.push(place(cfg));
28845
+ } catch {
28846
+ }
28847
+ }
28848
+ if (!pool.length)
28849
+ return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
28850
+ let layered = [];
28851
+ try {
28852
+ layered = layeredCandidates(parsed, sizes);
28853
+ } catch {
28854
+ }
28855
+ pool.sort(
28856
+ (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
28857
+ );
28858
+ const refineK = Math.min(REFINE_K, pool.length);
28859
+ let best = pool[0];
28860
+ let bestObj = Infinity;
28861
+ let bestBad = Infinity;
28862
+ const consider = (lay) => {
28863
+ const bad = badness(lay, bestBad);
28864
+ if (bad === Infinity) return;
28865
+ const sc = objective(lay, bad);
28866
+ if (sc < bestObj) {
28867
+ bestObj = sc;
28868
+ bestBad = bad;
28869
+ best = lay;
28870
+ }
28871
+ };
28872
+ for (const lay of pool.slice(0, refineK)) consider(lay);
28873
+ if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
28874
+ const extra = [];
28875
+ for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
28876
+ try {
28877
+ extra.push(
28878
+ place({
28879
+ ranker: "network-simplex",
28880
+ nodesep: 50,
28881
+ ranksep: 60,
28882
+ seed: s
28883
+ })
28884
+ );
28885
+ } catch {
28886
+ }
28887
+ }
28888
+ extra.sort(
28889
+ (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
28890
+ );
28891
+ for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
28892
+ }
28893
+ for (const lay of layered) {
28894
+ const variants = [lay];
28895
+ const dp = deroutePierces(lay);
28896
+ if (dp !== lay) variants.push(dp);
28897
+ for (const v of variants) {
28898
+ const bad = badness(v, bestBad - 1);
28899
+ if (bad < bestBad) {
28900
+ bestBad = bad;
28901
+ best = v;
28902
+ }
28903
+ }
28904
+ }
28905
+ if (bestBad > 0) {
28906
+ const rerouted = deroutePierces(best);
28907
+ if (rerouted !== best && badness(rerouted, bestBad - 1) < bestBad)
28908
+ best = rerouted;
28909
+ }
28910
+ if (bestBad > 0 && countGroupOverlaps(best) > 0) {
28911
+ const separated = separateGroupBands(best, parsed);
28912
+ if (separated !== best && badness(separated, bestBad - 1) < bestBad)
28913
+ best = separated;
28914
+ }
28915
+ return best;
28916
+ }
28917
+ var import_dagre4, import_d3_shape, DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
28918
+ var init_layout_search = __esm({
28919
+ "src/boxes-and-lines/layout-search.ts"() {
28920
+ "use strict";
28921
+ import_dagre4 = __toESM(require("@dagrejs/dagre"), 1);
28922
+ import_d3_shape = require("d3-shape");
28923
+ init_layout5();
28924
+ init_layout_layered();
28925
+ DEFAULT_LAMBDA = 4;
28926
+ ESCALATE_THRESHOLD = 4;
28927
+ ESCALATE_MAX_N = 45;
28928
+ ESCALATE_SEEDS = 18;
28929
+ ESCALATE_REFINE = 10;
28930
+ splineGen = (0, import_d3_shape.line)().x((d) => d.x).y((d) => d.y).curve(import_d3_shape.curveBasis);
28931
+ GROUP_LABEL_ZONE2 = 32;
28932
+ }
28933
+ });
28934
+
27605
28935
  // src/boxes-and-lines/layout.ts
27606
28936
  var layout_exports5 = {};
27607
28937
  __export(layout_exports5, {
28938
+ NODE_HEIGHT: () => NODE_HEIGHT,
28939
+ NODE_WIDTH: () => NODE_WIDTH,
28940
+ computeNodeSize: () => computeNodeSize,
27608
28941
  layoutBoxesAndLines: () => layoutBoxesAndLines
27609
28942
  });
27610
28943
  function splitCamelCase2(word) {
@@ -27677,417 +29010,21 @@ function computeNodeSize(node, reserveValueRow) {
27677
29010
  const totalHeight = labelHeight + SEPARATOR_GAP5 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
27678
29011
  return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
27679
29012
  }
27680
- function getElk() {
27681
- if (!elkInstance) elkInstance = new import_elk_bundled.default();
27682
- return elkInstance;
27683
- }
27684
- function baseOptions() {
27685
- return {
27686
- "elk.algorithm": "layered",
27687
- // INCLUDE_CHILDREN lets ELK route edges across container boundaries.
27688
- "elk.hierarchyHandling": "INCLUDE_CHILDREN",
27689
- "elk.edgeRouting": "ORTHOGONAL",
27690
- "elk.layered.unnecessaryBendpoints": "true",
27691
- // Let edges leave from top/bottom of nodes (not just the flow-direction
27692
- // sides) when it reduces crossings.
27693
- "elk.layered.allowNonFlowPortsToSwitchSides": "true"
27694
- };
27695
- }
27696
- function bkBaseline() {
27697
- return {
27698
- ...baseOptions(),
27699
- "elk.layered.nodePlacement.strategy": "BRANDES_KOEPF",
27700
- "elk.layered.nodePlacement.bk.fixedAlignment": "BALANCED",
27701
- "elk.layered.nodePlacement.bk.edgeStraightening": "IMPROVE_STRAIGHTNESS",
27702
- "elk.layered.compaction.connectedComponents": "true",
27703
- "elk.layered.spacing.nodeNodeBetweenLayers": "90",
27704
- "elk.spacing.nodeNode": "55",
27705
- "elk.spacing.edgeNode": "55",
27706
- "elk.spacing.edgeEdge": "18"
27707
- };
27708
- }
27709
- function getVariants() {
27710
- const bk = bkBaseline();
27711
- return [
27712
- {
27713
- name: "bk-baseline",
27714
- options: {
27715
- ...bk,
27716
- "elk.layered.crossingMinimization.greedySwitch.type": "ONE_SIDED"
27717
- }
27718
- },
27719
- {
27720
- name: "bk-aggressive",
27721
- options: {
27722
- ...bk,
27723
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27724
- "elk.layered.thoroughness": "50"
27725
- }
27726
- },
27727
- {
27728
- name: "bk-wide",
27729
- options: {
27730
- ...bk,
27731
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27732
- "elk.layered.thoroughness": "50",
27733
- "elk.spacing.nodeNode": "70",
27734
- "elk.spacing.edgeNode": "75",
27735
- "elk.spacing.edgeEdge": "22",
27736
- "elk.layered.spacing.nodeNodeBetweenLayers": "120"
27737
- }
27738
- },
27739
- {
27740
- name: "longest-path",
27741
- options: {
27742
- ...bk,
27743
- "elk.layered.layering.strategy": "LONGEST_PATH",
27744
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27745
- "elk.layered.thoroughness": "50"
27746
- }
27747
- },
27748
- {
27749
- name: "bounded-width",
27750
- options: {
27751
- ...bk,
27752
- "elk.layered.layering.strategy": "COFFMAN_GRAHAM",
27753
- "elk.layered.layering.coffmanGraham.layerBound": "3",
27754
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27755
- "elk.layered.thoroughness": "50"
27756
- }
27757
- }
27758
- ];
27759
- }
27760
- function countCrossings(edges) {
27761
- let count = 0;
27762
- for (let i = 0; i < edges.length; i++) {
27763
- const edgeI = edges[i];
27764
- const a = edgeI.points;
27765
- if (a.length < 2) continue;
27766
- for (let j = i + 1; j < edges.length; j++) {
27767
- const edgeJ = edges[j];
27768
- const b = edgeJ.points;
27769
- if (b.length < 2) continue;
27770
- if (edgeI.source === edgeJ.source) continue;
27771
- if (edgeI.source === edgeJ.target) continue;
27772
- if (edgeI.target === edgeJ.source) continue;
27773
- if (edgeI.target === edgeJ.target) continue;
27774
- for (let ai = 0; ai < a.length - 1; ai++) {
27775
- for (let bi = 0; bi < b.length - 1; bi++) {
27776
- if (segmentsCross(a[ai], a[ai + 1], b[bi], b[bi + 1])) count++;
27777
- }
27778
- }
27779
- }
27780
- }
27781
- return count;
27782
- }
27783
- function segmentsCross(p1, p2, p3, p4) {
27784
- const d1x = p2.x - p1.x;
27785
- const d1y = p2.y - p1.y;
27786
- const d2x = p4.x - p3.x;
27787
- const d2y = p4.y - p3.y;
27788
- const denom = d1x * d2y - d1y * d2x;
27789
- if (Math.abs(denom) < 1e-9) return false;
27790
- const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / denom;
27791
- const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / denom;
27792
- const EPS = 1e-3;
27793
- return t > EPS && t < 1 - EPS && s > EPS && s < 1 - EPS;
27794
- }
27795
- function countTotalBends(edges) {
27796
- let bends = 0;
27797
- for (const e of edges) bends += Math.max(0, e.points.length - 2);
27798
- return bends;
27799
- }
27800
- function scoreLayout(layout) {
27801
- return {
27802
- crossings: countCrossings(layout.edges),
27803
- bends: countTotalBends(layout.edges),
27804
- area: layout.width * layout.height
27805
- };
27806
- }
27807
- function cmpScore(a, b) {
27808
- const aBucket = a.crossings <= CROSSINGS_FORGIVENESS ? 0 : a.crossings;
27809
- const bBucket = b.crossings <= CROSSINGS_FORGIVENESS ? 0 : b.crossings;
27810
- if (aBucket !== bBucket) return aBucket - bBucket;
27811
- if (a.area !== b.area) return a.area - b.area;
27812
- return a.bends - b.bends;
27813
- }
27814
29013
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
27815
- const hideDescriptions = layoutOptions?.hideDescriptions ?? false;
27816
- const direction = parsed.direction === "TB" ? "DOWN" : "RIGHT";
27817
- const collapsedGroupLabels = /* @__PURE__ */ new Set();
27818
- if (collapseInfo) {
27819
- const missingGroups = /* @__PURE__ */ new Set();
27820
- for (const og of collapseInfo.originalGroups) {
27821
- if (!parsed.groups.some((g) => g.label === og.label)) {
27822
- missingGroups.add(og.label);
27823
- }
27824
- }
27825
- for (const label of missingGroups) {
27826
- const og = collapseInfo.originalGroups.find((g) => g.label === label);
27827
- const parentLabel = og?.parentGroup;
27828
- if (!parentLabel || !missingGroups.has(parentLabel)) {
27829
- collapsedGroupLabels.add(label);
27830
- }
27831
- }
27832
- }
27833
- const nodeSizes = /* @__PURE__ */ new Map();
27834
- let maxDescHeight = 0;
27835
- for (const node of parsed.nodes) {
27836
- const size = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
27837
- nodeSizes.set(node.label, size);
27838
- if (!hideDescriptions && node.description && node.description.length > 0) {
27839
- maxDescHeight = Math.max(maxDescHeight, size.height);
27840
- }
27841
- }
27842
- if (maxDescHeight > 0) {
27843
- for (const node of parsed.nodes) {
27844
- if (node.description && node.description.length > 0) {
27845
- const size = nodeSizes.get(node.label);
27846
- nodeSizes.set(node.label, { width: size.width, height: maxDescHeight });
27847
- }
27848
- }
27849
- }
27850
- const expandedGroupSet = new Set(parsed.groups.map((g) => g.label));
27851
- const gid = (label) => `__group_${label}`;
27852
- function buildGraph() {
27853
- const nodeById = /* @__PURE__ */ new Map();
27854
- const parentOf = /* @__PURE__ */ new Map();
27855
- for (const node of parsed.nodes) {
27856
- const size = nodeSizes.get(node.label);
27857
- nodeById.set(node.label, {
27858
- id: node.label,
27859
- width: size.width,
27860
- height: size.height,
27861
- labels: [{ text: node.label }]
27862
- });
27863
- }
27864
- for (const group of parsed.groups) {
27865
- nodeById.set(gid(group.label), {
27866
- id: gid(group.label),
27867
- labels: [{ text: group.label }],
27868
- layoutOptions: {
27869
- "elk.padding": `[top=${CONTAINER_PAD_TOP2},left=${CONTAINER_PAD_X3},bottom=${CONTAINER_PAD_BOTTOM3},right=${CONTAINER_PAD_X3}]`,
27870
- // Suggest square-ish containers — has limited effect with
27871
- // INCLUDE_CHILDREN but doesn't hurt.
27872
- "elk.aspectRatio": "1.4"
27873
- },
27874
- children: [],
27875
- edges: []
27876
- });
27877
- }
27878
- for (const label of collapsedGroupLabels) {
27879
- nodeById.set(gid(label), {
27880
- id: gid(label),
27881
- width: NODE_WIDTH,
27882
- height: NODE_HEIGHT,
27883
- labels: [{ text: label }]
27884
- });
27885
- }
27886
- for (const group of parsed.groups) {
27887
- if (group.parentGroup && nodeById.has(gid(group.parentGroup))) {
27888
- parentOf.set(gid(group.label), gid(group.parentGroup));
27889
- }
27890
- }
27891
- if (collapseInfo) {
27892
- for (const label of collapsedGroupLabels) {
27893
- const og = collapseInfo.originalGroups.find((g) => g.label === label);
27894
- if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && nodeById.has(gid(og.parentGroup))) {
27895
- parentOf.set(gid(label), gid(og.parentGroup));
27896
- }
27897
- }
27898
- }
27899
- for (const group of parsed.groups) {
27900
- for (const child of group.children) {
27901
- if (expandedGroupSet.has(child)) continue;
27902
- if (nodeById.has(child)) {
27903
- parentOf.set(child, gid(group.label));
27904
- }
27905
- }
27906
- }
27907
- const roots = [];
27908
- for (const [id, node] of nodeById) {
27909
- const parentId = parentOf.get(id);
27910
- if (parentId) {
27911
- const parent = nodeById.get(parentId);
27912
- parent.children = parent.children ?? [];
27913
- parent.children.push(node);
27914
- } else {
27915
- roots.push(node);
27916
- }
27917
- }
27918
- const rootEdges = [];
27919
- for (let i = 0; i < parsed.edges.length; i++) {
27920
- const edge = parsed.edges[i];
27921
- if (!nodeById.has(edge.source) || !nodeById.has(edge.target)) continue;
27922
- rootEdges.push({
27923
- id: `e${i}`,
27924
- sources: [edge.source],
27925
- targets: [edge.target]
27926
- });
27927
- }
27928
- return { roots, rootEdges };
27929
- }
27930
- async function runVariant(variant) {
27931
- const { roots, rootEdges } = buildGraph();
27932
- const elkRoot = {
27933
- id: "root",
27934
- layoutOptions: {
27935
- ...variant.options,
27936
- "elk.direction": direction,
27937
- "elk.padding": `[top=${MARGIN3},left=${MARGIN3},bottom=${MARGIN3},right=${MARGIN3}]`
27938
- },
27939
- children: roots,
27940
- edges: rootEdges
27941
- };
27942
- const result = await getElk().layout(elkRoot);
27943
- return extractLayout(result);
27944
- }
27945
- function extractLayout(result) {
27946
- const layoutNodes = [];
27947
- const layoutGroups = [];
27948
- const allEdges = [];
27949
- const containerAbs = /* @__PURE__ */ new Map();
27950
- function walk(n, offsetX, offsetY, isRoot) {
27951
- const nx = (n.x ?? 0) + offsetX;
27952
- const ny = (n.y ?? 0) + offsetY;
27953
- const nw = n.width ?? 0;
27954
- const nh = n.height ?? 0;
27955
- if (isRoot) {
27956
- containerAbs.set("root", { x: nx, y: ny });
27957
- } else {
27958
- const isGroup = n.id.startsWith("__group_");
27959
- if (isGroup) {
27960
- const label = n.id.slice("__group_".length);
27961
- const collapsed = collapsedGroupLabels.has(label);
27962
- const og = collapseInfo?.originalGroups.find(
27963
- (g) => g.label === label
27964
- );
27965
- const pg = parsed.groups.find((g) => g.label === label);
27966
- const childCount = collapsed ? collapseInfo?.collapsedChildCounts.get(label) ?? 0 : void 0;
27967
- layoutGroups.push({
27968
- label,
27969
- lineNumber: pg?.lineNumber ?? og?.lineNumber ?? 0,
27970
- x: nx + nw / 2,
27971
- y: ny + nh / 2,
27972
- width: nw,
27973
- height: nh,
27974
- collapsed,
27975
- ...childCount !== void 0 && { childCount }
27976
- });
27977
- if (!collapsed) containerAbs.set(n.id, { x: nx, y: ny });
27978
- } else {
27979
- layoutNodes.push({
27980
- label: n.id,
27981
- x: nx + nw / 2,
27982
- y: ny + nh / 2,
27983
- width: nw,
27984
- height: nh
27985
- });
27986
- }
27987
- }
27988
- if (n.edges) for (const e of n.edges) allEdges.push(e);
27989
- if (n.children) for (const c of n.children) walk(c, nx, ny, false);
27990
- }
27991
- walk(result, 0, 0, true);
27992
- const edgeYOffsets = new Array(parsed.edges.length).fill(0);
27993
- const edgeParallelCounts = new Array(parsed.edges.length).fill(1);
27994
- const parallelGroups = /* @__PURE__ */ new Map();
27995
- for (let i = 0; i < parsed.edges.length; i++) {
27996
- const edge = parsed.edges[i];
27997
- const [a, b] = edge.source < edge.target ? [edge.source, edge.target] : [edge.target, edge.source];
27998
- const key = `${a}\0${b}`;
27999
- if (!parallelGroups.has(key)) parallelGroups.set(key, []);
28000
- parallelGroups.get(key).push(i);
28001
- }
28002
- for (const group of parallelGroups.values()) {
28003
- const capped = group.slice(0, MAX_PARALLEL_EDGES);
28004
- for (const idx of group.slice(MAX_PARALLEL_EDGES)) {
28005
- edgeParallelCounts[idx] = 0;
28006
- }
28007
- if (capped.length < 2) continue;
28008
- for (let j = 0; j < capped.length; j++) {
28009
- const cappedJ = capped[j];
28010
- edgeYOffsets[cappedJ] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
28011
- edgeParallelCounts[cappedJ] = capped.length;
28012
- }
28013
- }
28014
- const edgeById = /* @__PURE__ */ new Map();
28015
- for (const e of allEdges) edgeById.set(e.id, e);
28016
- const layoutEdges = [];
28017
- for (let i = 0; i < parsed.edges.length; i++) {
28018
- const edge = parsed.edges[i];
28019
- if (edgeParallelCounts[i] === 0) continue;
28020
- const elkEdge = edgeById.get(`e${i}`);
28021
- if (!elkEdge?.sections || elkEdge.sections.length === 0) continue;
28022
- const container = elkEdge.container ?? "root";
28023
- const off = containerAbs.get(container) ?? { x: 0, y: 0 };
28024
- const s = elkEdge.sections[0];
28025
- const points = [
28026
- { x: s.startPoint.x + off.x, y: s.startPoint.y + off.y },
28027
- ...(s.bendPoints ?? []).map((p) => ({
28028
- x: p.x + off.x,
28029
- y: p.y + off.y
28030
- })),
28031
- { x: s.endPoint.x + off.x, y: s.endPoint.y + off.y }
28032
- ];
28033
- let labelX;
28034
- let labelY;
28035
- if (edge.label && points.length >= 2) {
28036
- const mid = Math.floor(points.length / 2);
28037
- const midPoint = points[mid];
28038
- labelX = midPoint.x;
28039
- labelY = midPoint.y - 10;
28040
- }
28041
- layoutEdges.push({
28042
- source: edge.source,
28043
- target: edge.target,
28044
- ...edge.label !== void 0 && { label: edge.label },
28045
- bidirectional: edge.bidirectional,
28046
- lineNumber: edge.lineNumber,
28047
- points,
28048
- ...labelX !== void 0 && { labelX },
28049
- ...labelY !== void 0 && { labelY },
28050
- // In-bounds — i < parsed.edges.length, arrays sized to that length.
28051
- yOffset: edgeYOffsets[i],
28052
- parallelCount: edgeParallelCounts[i],
28053
- metadata: edge.metadata,
28054
- deferred: true
28055
- });
28056
- }
28057
- let maxX = 0;
28058
- let maxY = 0;
28059
- for (const node of layoutNodes) {
28060
- maxX = Math.max(maxX, node.x + node.width / 2);
28061
- maxY = Math.max(maxY, node.y + node.height / 2);
28062
- }
28063
- for (const group of layoutGroups) {
28064
- maxX = Math.max(maxX, group.x + group.width / 2);
28065
- maxY = Math.max(maxY, group.y + group.height / 2);
29014
+ const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
29015
+ const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
29016
+ ...layoutOptions?.hideDescriptions !== void 0 && {
29017
+ hideDescriptions: layoutOptions.hideDescriptions
29018
+ },
29019
+ ...layoutOptions?.previousPositions !== void 0 && {
29020
+ previousPositions: layoutOptions.previousPositions
28066
29021
  }
28067
- return {
28068
- nodes: layoutNodes,
28069
- edges: layoutEdges,
28070
- groups: layoutGroups,
28071
- width: maxX + MARGIN3,
28072
- height: maxY + MARGIN3
28073
- };
28074
- }
28075
- const N = parsed.nodes.length + parsed.groups.length;
28076
- const E = parsed.edges.length;
28077
- const trivial = N < 8 && E < 10;
28078
- const variants = trivial ? [getVariants()[1]] : getVariants();
28079
- const results = await Promise.all(variants.map((v) => runVariant(v)));
28080
- let best = results[0];
28081
- let bestScore = scoreLayout(best);
28082
- for (let i = 1; i < results.length; i++) {
28083
- const resultI = results[i];
28084
- const s = scoreLayout(resultI);
28085
- if (cmpScore(s, bestScore) < 0) {
28086
- best = resultI;
28087
- bestScore = s;
28088
- }
28089
- }
28090
- return attachNotes(best, parsed, layoutOptions?.collapsedNotes);
29022
+ });
29023
+ return attachNotes(
29024
+ applyParallelEdgeOffsets(searched),
29025
+ parsed,
29026
+ layoutOptions?.collapsedNotes
29027
+ );
28091
29028
  }
28092
29029
  function attachNotes(layout, parsed, collapsedNotes) {
28093
29030
  const notesSuppressed = parsed.options?.["no-notes"] === "on";
@@ -28165,21 +29102,47 @@ function attachNotes(layout, parsed, collapsedNotes) {
28165
29102
  nodes: finalNodes,
28166
29103
  edges: finalEdges,
28167
29104
  groups: finalGroups,
28168
- width: bbMaxX + shiftX + MARGIN3,
28169
- height: bbMaxY + shiftY + MARGIN3
29105
+ width: bbMaxX + shiftX + MARGIN4,
29106
+ height: bbMaxY + shiftY + MARGIN4
29107
+ };
29108
+ }
29109
+ function applyParallelEdgeOffsets(layout) {
29110
+ const groups = /* @__PURE__ */ new Map();
29111
+ layout.edges.forEach((e, i) => {
29112
+ const [a, b] = e.source < e.target ? [e.source, e.target] : [e.target, e.source];
29113
+ const key = `${a}\0${b}`;
29114
+ const arr = groups.get(key);
29115
+ if (arr) arr.push(i);
29116
+ else groups.set(key, [i]);
29117
+ });
29118
+ if ([...groups.values()].every((g) => g.length < 2)) return layout;
29119
+ const yOffset = new Array(layout.edges.length).fill(0);
29120
+ const count = new Array(layout.edges.length).fill(1);
29121
+ for (const idxs of groups.values()) {
29122
+ const capped = idxs.slice(0, MAX_PARALLEL_EDGES);
29123
+ for (const drop of idxs.slice(MAX_PARALLEL_EDGES)) count[drop] = 0;
29124
+ if (capped.length < 2) continue;
29125
+ capped.forEach((idx, j) => {
29126
+ yOffset[idx] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
29127
+ count[idx] = capped.length;
29128
+ });
29129
+ }
29130
+ return {
29131
+ ...layout,
29132
+ edges: layout.edges.map((e, i) => ({
29133
+ ...e,
29134
+ yOffset: yOffset[i],
29135
+ parallelCount: count[i]
29136
+ }))
28170
29137
  };
28171
29138
  }
28172
- var import_elk_bundled, 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;
29139
+ 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;
28173
29140
  var init_layout5 = __esm({
28174
29141
  "src/boxes-and-lines/layout.ts"() {
28175
29142
  "use strict";
28176
- import_elk_bundled = __toESM(require("elkjs/lib/elk.bundled.js"), 1);
28177
29143
  init_text_measure();
28178
29144
  init_notes();
28179
- MARGIN3 = 40;
28180
- CONTAINER_PAD_X3 = 30;
28181
- CONTAINER_PAD_TOP2 = 40;
28182
- CONTAINER_PAD_BOTTOM3 = 24;
29145
+ MARGIN4 = 40;
28183
29146
  MAX_PARALLEL_EDGES = 5;
28184
29147
  PARALLEL_SPACING = 22;
28185
29148
  PHI = 1.618;
@@ -28196,8 +29159,6 @@ var init_layout5 = __esm({
28196
29159
  LABEL_PAD = 12;
28197
29160
  VALUE_ROW_FONT = 11;
28198
29161
  VALUE_ROW_H = SEPARATOR_GAP5 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
28199
- elkInstance = null;
28200
- CROSSINGS_FORGIVENESS = 1;
28201
29162
  }
28202
29163
  });
28203
29164
 
@@ -28352,7 +29313,7 @@ function layoutMindmap(parsed, _palette, options) {
28352
29313
  leafWidth: ctx.structural(LEAF_WIDTH),
28353
29314
  hGap: ctx.aesthetic(H_GAP2),
28354
29315
  vGap: ctx.aesthetic(V_GAP2),
28355
- margin: ctx.aesthetic(MARGIN4),
29316
+ margin: ctx.aesthetic(MARGIN5),
28356
29317
  multiRootGap: ctx.aesthetic(MULTI_ROOT_GAP)
28357
29318
  };
28358
29319
  populateDepthCache(roots);
@@ -28730,7 +29691,7 @@ function populateDepthCache(roots) {
28730
29691
  };
28731
29692
  walk(roots, 0);
28732
29693
  }
28733
- 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;
29694
+ 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;
28734
29695
  var init_layout6 = __esm({
28735
29696
  "src/mindmap/layout.ts"() {
28736
29697
  "use strict";
@@ -28746,7 +29707,7 @@ var init_layout6 = __esm({
28746
29707
  NODE_V_PAD = 10;
28747
29708
  H_GAP2 = 40;
28748
29709
  V_GAP2 = 12;
28749
- MARGIN4 = 40;
29710
+ MARGIN5 = 40;
28750
29711
  MULTI_ROOT_GAP = 60;
28751
29712
  nodeDepthCache = /* @__PURE__ */ new Map();
28752
29713
  }
@@ -30445,7 +31406,7 @@ function layoutC4Context(parsed, activeTagGroup) {
30445
31406
  }
30446
31407
  const contextRels = rollUpContextRelationships(parsed);
30447
31408
  const spacing = computeAdaptiveSpacing(contextRels);
30448
- const g = new import_dagre4.default.graphlib.Graph();
31409
+ const g = new import_dagre5.default.graphlib.Graph();
30449
31410
  g.setGraph({
30450
31411
  rankdir: "TB",
30451
31412
  nodesep: spacing.nodesep,
@@ -30466,7 +31427,7 @@ function layoutC4Context(parsed, activeTagGroup) {
30466
31427
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
30467
31428
  }
30468
31429
  }
30469
- import_dagre4.default.layout(g);
31430
+ import_dagre5.default.layout(g);
30470
31431
  reduceCrossings(
30471
31432
  g,
30472
31433
  validRels.map((r) => ({ source: r.sourceName, target: r.targetName }))
@@ -30533,8 +31494,8 @@ function layoutC4Context(parsed, activeTagGroup) {
30533
31494
  }
30534
31495
  }
30535
31496
  if (nodes.length > 0) {
30536
- const shiftX = MARGIN5 - minX;
30537
- const shiftY = MARGIN5 - minY;
31497
+ const shiftX = MARGIN6 - minX;
31498
+ const shiftY = MARGIN6 - minY;
30538
31499
  for (const node of nodes) {
30539
31500
  node.x += shiftX;
30540
31501
  node.y += shiftY;
@@ -30546,12 +31507,12 @@ function layoutC4Context(parsed, activeTagGroup) {
30546
31507
  }
30547
31508
  }
30548
31509
  }
30549
- let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN5 * 2 : 0;
30550
- let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN5 * 2 : 0;
31510
+ let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN6 * 2 : 0;
31511
+ let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN6 * 2 : 0;
30551
31512
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
30552
31513
  if (legendGroups.length > 0) {
30553
- const legendY = totalHeight + MARGIN5;
30554
- let legendX = MARGIN5;
31514
+ const legendY = totalHeight + MARGIN6;
31515
+ let legendX = MARGIN6;
30555
31516
  for (const lg of legendGroups) {
30556
31517
  lg.x = legendX;
30557
31518
  lg.y = legendY;
@@ -30644,7 +31605,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30644
31605
  }
30645
31606
  }
30646
31607
  const hasGroups = elementToGroup.size > 0;
30647
- const g = hasGroups ? new import_dagre4.default.graphlib.Graph({ compound: true }) : new import_dagre4.default.graphlib.Graph();
31608
+ const g = hasGroups ? new import_dagre5.default.graphlib.Graph({ compound: true }) : new import_dagre5.default.graphlib.Graph();
30648
31609
  g.setDefaultEdgeLabel(() => ({}));
30649
31610
  if (hasGroups) {
30650
31611
  const seenGroups = /* @__PURE__ */ new Set();
@@ -30722,7 +31683,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30722
31683
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
30723
31684
  }
30724
31685
  }
30725
- import_dagre4.default.layout(g);
31686
+ import_dagre5.default.layout(g);
30726
31687
  const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
30727
31688
  reduceCrossings(
30728
31689
  g,
@@ -30883,8 +31844,8 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30883
31844
  if (pt.y > maxY) maxY = pt.y;
30884
31845
  }
30885
31846
  }
30886
- const shiftX = MARGIN5 - minX;
30887
- const shiftY = MARGIN5 - minY;
31847
+ const shiftX = MARGIN6 - minX;
31848
+ const shiftY = MARGIN6 - minY;
30888
31849
  for (const node of nodes) {
30889
31850
  node.x += shiftX;
30890
31851
  node.y += shiftY;
@@ -30901,12 +31862,12 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30901
31862
  pt.y += shiftY;
30902
31863
  }
30903
31864
  }
30904
- let totalWidth = maxX - minX + MARGIN5 * 2;
30905
- let totalHeight = maxY - minY + MARGIN5 * 2;
31865
+ let totalWidth = maxX - minX + MARGIN6 * 2;
31866
+ let totalHeight = maxY - minY + MARGIN6 * 2;
30906
31867
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
30907
31868
  if (legendGroups.length > 0) {
30908
- const legendY = totalHeight + MARGIN5;
30909
- let legendX = MARGIN5;
31869
+ const legendY = totalHeight + MARGIN6;
31870
+ let legendX = MARGIN6;
30910
31871
  for (const lg of legendGroups) {
30911
31872
  lg.x = legendX;
30912
31873
  lg.y = legendY;
@@ -31051,7 +32012,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31051
32012
  }
31052
32013
  }
31053
32014
  const hasGroups = elementToGroup.size > 0;
31054
- const g = hasGroups ? new import_dagre4.default.graphlib.Graph({ compound: true }) : new import_dagre4.default.graphlib.Graph();
32015
+ const g = hasGroups ? new import_dagre5.default.graphlib.Graph({ compound: true }) : new import_dagre5.default.graphlib.Graph();
31055
32016
  g.setDefaultEdgeLabel(() => ({}));
31056
32017
  if (hasGroups) {
31057
32018
  const seenGroups = /* @__PURE__ */ new Set();
@@ -31135,7 +32096,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31135
32096
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
31136
32097
  }
31137
32098
  }
31138
- import_dagre4.default.layout(g);
32099
+ import_dagre5.default.layout(g);
31139
32100
  const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
31140
32101
  reduceCrossings(
31141
32102
  g,
@@ -31298,8 +32259,8 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31298
32259
  if (pt.y > maxY) maxY = pt.y;
31299
32260
  }
31300
32261
  }
31301
- const shiftX = MARGIN5 - minX;
31302
- const shiftY = MARGIN5 - minY;
32262
+ const shiftX = MARGIN6 - minX;
32263
+ const shiftY = MARGIN6 - minY;
31303
32264
  for (const node of nodes) {
31304
32265
  node.x += shiftX;
31305
32266
  node.y += shiftY;
@@ -31316,12 +32277,12 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31316
32277
  pt.y += shiftY;
31317
32278
  }
31318
32279
  }
31319
- let totalWidth = maxX - minX + MARGIN5 * 2;
31320
- let totalHeight = maxY - minY + MARGIN5 * 2;
32280
+ let totalWidth = maxX - minX + MARGIN6 * 2;
32281
+ let totalHeight = maxY - minY + MARGIN6 * 2;
31321
32282
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
31322
32283
  if (legendGroups.length > 0) {
31323
- const legendY = totalHeight + MARGIN5;
31324
- let legendX = MARGIN5;
32284
+ const legendY = totalHeight + MARGIN6;
32285
+ let legendX = MARGIN6;
31325
32286
  for (const lg of legendGroups) {
31326
32287
  lg.x = legendX;
31327
32288
  lg.y = legendY;
@@ -31427,7 +32388,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31427
32388
  for (const r of refEntries) {
31428
32389
  nameToElement.set(r.element.name, r.element);
31429
32390
  }
31430
- const g = new import_dagre4.default.graphlib.Graph({ compound: true });
32391
+ const g = new import_dagre5.default.graphlib.Graph({ compound: true });
31431
32392
  g.setDefaultEdgeLabel(() => ({}));
31432
32393
  for (const [infraId] of infraIds) {
31433
32394
  g.setNode(infraId, {});
@@ -31471,7 +32432,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31471
32432
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
31472
32433
  }
31473
32434
  }
31474
- import_dagre4.default.layout(g);
32435
+ import_dagre5.default.layout(g);
31475
32436
  const nodeInfraMap = /* @__PURE__ */ new Map();
31476
32437
  for (const r of refEntries) nodeInfraMap.set(r.element.name, r.infraId);
31477
32438
  reduceCrossings(
@@ -31609,8 +32570,8 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31609
32570
  if (pt.y > maxY) maxY = pt.y;
31610
32571
  }
31611
32572
  }
31612
- const shiftX = MARGIN5 - minX;
31613
- const shiftY = MARGIN5 - minY;
32573
+ const shiftX = MARGIN6 - minX;
32574
+ const shiftY = MARGIN6 - minY;
31614
32575
  for (const node of nodes) {
31615
32576
  node.x += shiftX;
31616
32577
  node.y += shiftY;
@@ -31625,12 +32586,12 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31625
32586
  pt.y += shiftY;
31626
32587
  }
31627
32588
  }
31628
- let totalWidth = maxX - minX + MARGIN5 * 2;
31629
- let totalHeight = maxY - minY + MARGIN5 * 2;
32589
+ let totalWidth = maxX - minX + MARGIN6 * 2;
32590
+ let totalHeight = maxY - minY + MARGIN6 * 2;
31630
32591
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
31631
32592
  if (legendGroups.length > 0) {
31632
- const legendY = totalHeight + MARGIN5;
31633
- let legendX = MARGIN5;
32593
+ const legendY = totalHeight + MARGIN6;
32594
+ let legendX = MARGIN6;
31634
32595
  for (const lg of legendGroups) {
31635
32596
  lg.x = legendX;
31636
32597
  lg.y = legendY;
@@ -31650,11 +32611,11 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31650
32611
  height: totalHeight
31651
32612
  };
31652
32613
  }
31653
- var import_dagre4, 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;
32614
+ var import_dagre5, 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;
31654
32615
  var init_layout8 = __esm({
31655
32616
  "src/c4/layout.ts"() {
31656
32617
  "use strict";
31657
- import_dagre4 = __toESM(require("@dagrejs/dagre"), 1);
32618
+ import_dagre5 = __toESM(require("@dagrejs/dagre"), 1);
31658
32619
  init_legend_constants();
31659
32620
  init_text_measure();
31660
32621
  gNode = (g, name) => g.node(name);
@@ -31671,7 +32632,7 @@ var init_layout8 = __esm({
31671
32632
  CARD_H_PAD3 = 20;
31672
32633
  META_LINE_HEIGHT5 = 16;
31673
32634
  META_FONT_SIZE5 = 11;
31674
- MARGIN5 = 40;
32635
+ MARGIN6 = 40;
31675
32636
  BOUNDARY_PAD = 40;
31676
32637
  GROUP_BOUNDARY_PAD = 24;
31677
32638
  EDGE_NODE_COLLISION_WEIGHT = 5e3;
@@ -32739,7 +33700,7 @@ function layoutGraph(graph, options) {
32739
33700
  if (allNodes.length === 0) {
32740
33701
  return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
32741
33702
  }
32742
- const g = new import_dagre5.default.graphlib.Graph({ compound: true });
33703
+ const g = new import_dagre6.default.graphlib.Graph({ compound: true });
32743
33704
  g.setGraph({
32744
33705
  rankdir: graph.direction,
32745
33706
  nodesep: 50,
@@ -32790,7 +33751,7 @@ function layoutGraph(graph, options) {
32790
33751
  label: edge.label ?? ""
32791
33752
  });
32792
33753
  }
32793
- import_dagre5.default.layout(g);
33754
+ import_dagre6.default.layout(g);
32794
33755
  const collapsedGroupIds = collapsedChildCounts ? new Set(collapsedChildCounts.keys()) : /* @__PURE__ */ new Set();
32795
33756
  const basePositioned = allNodes.map((node) => {
32796
33757
  const pos = g.node(node.id);
@@ -32986,11 +33947,11 @@ function layoutGraph(graph, options) {
32986
33947
  height: totalHeight
32987
33948
  };
32988
33949
  }
32989
- var import_dagre5, GROUP_PADDING;
33950
+ var import_dagre6, GROUP_PADDING;
32990
33951
  var init_layout9 = __esm({
32991
33952
  "src/graph/layout.ts"() {
32992
33953
  "use strict";
32993
- import_dagre5 = __toESM(require("@dagrejs/dagre"), 1);
33954
+ import_dagre6 = __toESM(require("@dagrejs/dagre"), 1);
32994
33955
  init_notes2();
32995
33956
  init_note_box();
32996
33957
  init_notes();
@@ -34796,7 +35757,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
34796
35757
  };
34797
35758
  }
34798
35759
  const isLR = computed.direction !== "TB";
34799
- const g = new import_dagre6.default.graphlib.Graph();
35760
+ const g = new import_dagre7.default.graphlib.Graph();
34800
35761
  g.setGraph({
34801
35762
  rankdir: computed.direction === "TB" ? "TB" : "LR",
34802
35763
  nodesep: isLR ? 70 : 60,
@@ -34847,7 +35808,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
34847
35808
  g.setEdge(edge.sourceId, edge.targetId, { label: edge.label });
34848
35809
  }
34849
35810
  }
34850
- import_dagre6.default.layout(g);
35811
+ import_dagre7.default.layout(g);
34851
35812
  const layoutNodes = computed.nodes.map(
34852
35813
  (node) => {
34853
35814
  const pos = g.node(node.id);
@@ -35019,11 +35980,11 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
35019
35980
  height: totalHeight
35020
35981
  };
35021
35982
  }
35022
- var import_dagre6, MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT7, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT5, NODE_FONT_SIZE3, META_FONT_SIZE7, EDGE_LABEL_FONT_SIZE8, PADDING_X3, GROUP_PADDING2, GROUP_HEADER_HEIGHT, EDGE_MARGIN, DISPLAY_KEYS, DISPLAY_NAMES, GROUP_GAP;
35983
+ var import_dagre7, MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT7, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT5, NODE_FONT_SIZE3, META_FONT_SIZE7, EDGE_LABEL_FONT_SIZE8, PADDING_X3, GROUP_PADDING2, GROUP_HEADER_HEIGHT, EDGE_MARGIN, DISPLAY_KEYS, DISPLAY_NAMES, GROUP_GAP;
35023
35984
  var init_layout10 = __esm({
35024
35985
  "src/infra/layout.ts"() {
35025
35986
  "use strict";
35026
- import_dagre6 = __toESM(require("@dagrejs/dagre"), 1);
35987
+ import_dagre7 = __toESM(require("@dagrejs/dagre"), 1);
35027
35988
  init_text_measure();
35028
35989
  MIN_NODE_WIDTH2 = 140;
35029
35990
  NODE_HEADER_HEIGHT = 28;
@@ -36590,17 +37551,17 @@ function mulberry32(seed) {
36590
37551
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
36591
37552
  };
36592
37553
  }
36593
- function standardNormal(rng) {
37554
+ function standardNormal(rng3) {
36594
37555
  let u = 0;
36595
37556
  let v = 0;
36596
- while (u === 0) u = rng();
36597
- while (v === 0) v = rng();
37557
+ while (u === 0) u = rng3();
37558
+ while (v === 0) v = rng3();
36598
37559
  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
36599
37560
  }
36600
- function gamma(shape, rng) {
37561
+ function gamma(shape, rng3) {
36601
37562
  if (shape < 1) {
36602
- const u = rng();
36603
- return gamma(shape + 1, rng) * Math.pow(u, 1 / shape);
37563
+ const u = rng3();
37564
+ return gamma(shape + 1, rng3) * Math.pow(u, 1 / shape);
36604
37565
  }
36605
37566
  const d = shape - 1 / 3;
36606
37567
  const c = 1 / Math.sqrt(9 * d);
@@ -36608,29 +37569,29 @@ function gamma(shape, rng) {
36608
37569
  let x;
36609
37570
  let v;
36610
37571
  do {
36611
- x = standardNormal(rng);
37572
+ x = standardNormal(rng3);
36612
37573
  v = 1 + c * x;
36613
37574
  } while (v <= 0);
36614
37575
  v = v * v * v;
36615
- const u = rng();
37576
+ const u = rng3();
36616
37577
  if (u < 1 - 0.0331 * x * x * x * x) return d * v;
36617
37578
  if (Math.log(u) < x * x / 2 + d * (1 - v + Math.log(v))) return d * v;
36618
37579
  }
36619
37580
  }
36620
- function sampleBeta(alpha, beta, rng) {
36621
- const x = gamma(alpha, rng);
36622
- const y = gamma(beta, rng);
37581
+ function sampleBeta(alpha, beta, rng3) {
37582
+ const x = gamma(alpha, rng3);
37583
+ const y = gamma(beta, rng3);
36623
37584
  return x / (x + y);
36624
37585
  }
36625
- function sampleBetaPert(o, m, p, rng) {
37586
+ function sampleBetaPert(o, m, p, rng3) {
36626
37587
  if (o === p) return m;
36627
37588
  const range = p - o;
36628
37589
  const alpha = 1 + 4 * (m - o) / range;
36629
37590
  const beta = 1 + 4 * (p - m) / range;
36630
- return o + sampleBeta(alpha, beta, rng) * range;
37591
+ return o + sampleBeta(alpha, beta, rng3) * range;
36631
37592
  }
36632
37593
  function simulate(resolved, expanded, _predecessors, _successors, topo, terminals, poisoned, opts) {
36633
- const rng = mulberry32(opts.seed);
37594
+ const rng3 = mulberry32(opts.seed);
36634
37595
  const expById = /* @__PURE__ */ new Map();
36635
37596
  for (const e of expanded) expById.set(e.id, e);
36636
37597
  const completionTimes = new Array(opts.trials);
@@ -36653,7 +37614,7 @@ function simulate(resolved, expanded, _predecessors, _successors, topo, terminal
36653
37614
  } else if (exp.o === exp.p) {
36654
37615
  duration = exp.m;
36655
37616
  } else {
36656
- duration = sampleBetaPert(exp.o, exp.m, exp.p, rng);
37617
+ duration = sampleBetaPert(exp.o, exp.m, exp.p, rng3);
36657
37618
  }
36658
37619
  let esLower = 0;
36659
37620
  let efLower = -Infinity;
@@ -37759,7 +38720,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
37759
38720
  }
37760
38721
  }
37761
38722
  const dagreId = (id) => memberToGroup.get(id) ?? id;
37762
- const g = new import_dagre7.default.graphlib.Graph();
38723
+ const g = new import_dagre8.default.graphlib.Graph();
37763
38724
  g.setGraph({
37764
38725
  rankdir: resolved.options.direction,
37765
38726
  nodesep: 50,
@@ -37798,7 +38759,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
37798
38759
  seenEdges.add(k);
37799
38760
  g.setEdge(src, tgt, {});
37800
38761
  }
37801
- import_dagre7.default.layout(g);
38762
+ import_dagre8.default.layout(g);
37802
38763
  const swimApplied = applySwimLanes(
37803
38764
  g,
37804
38765
  resolved,
@@ -38144,7 +39105,7 @@ function reduceCrossings2(g, direction) {
38144
39105
  buckets.get(key).push(id);
38145
39106
  }
38146
39107
  const edges = g.edges().map((e) => ({ v: e.v, w: e.w }));
38147
- const countCrossings2 = () => {
39108
+ const countCrossings = () => {
38148
39109
  let total = 0;
38149
39110
  for (let i = 0; i < edges.length; i++) {
38150
39111
  const a = edges[i];
@@ -38157,13 +39118,13 @@ function reduceCrossings2(g, direction) {
38157
39118
  const b1 = g.node(b.v);
38158
39119
  const b2 = g.node(b.w);
38159
39120
  if (!b1 || !b2) continue;
38160
- if (segmentsCross2(a1, a2, b1, b2)) total++;
39121
+ if (segmentsCross(a1, a2, b1, b2)) total++;
38161
39122
  }
38162
39123
  }
38163
39124
  return total;
38164
39125
  };
38165
39126
  const MAX_ITER = 8;
38166
- let baseline = countCrossings2();
39127
+ let baseline = countCrossings();
38167
39128
  if (baseline === 0) return;
38168
39129
  for (let iter = 0; iter < MAX_ITER; iter++) {
38169
39130
  let improved = false;
@@ -38181,7 +39142,7 @@ function reduceCrossings2(g, direction) {
38181
39142
  const bv = bn[slotAxis];
38182
39143
  an[slotAxis] = bv;
38183
39144
  bn[slotAxis] = av;
38184
- const after = countCrossings2();
39145
+ const after = countCrossings();
38185
39146
  if (after < baseline) {
38186
39147
  baseline = after;
38187
39148
  improved = true;
@@ -38202,7 +39163,7 @@ function reduceCrossings2(g, direction) {
38202
39163
  data.points = smoothEdge(src, tgt, direction);
38203
39164
  }
38204
39165
  }
38205
- function segmentsCross2(a1, a2, b1, b2) {
39166
+ function segmentsCross(a1, a2, b1, b2) {
38206
39167
  const ccw = (p, q, r) => (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x);
38207
39168
  const d1 = ccw(b1, b2, a1);
38208
39169
  const d2 = ccw(b1, b2, a2);
@@ -38210,11 +39171,11 @@ function segmentsCross2(a1, a2, b1, b2) {
38210
39171
  const d4 = ccw(a1, a2, b2);
38211
39172
  return (d1 > 0 && d2 < 0 || d1 < 0 && d2 > 0) && (d32 > 0 && d4 < 0 || d32 < 0 && d4 > 0);
38212
39173
  }
38213
- var import_dagre7, DEFAULT_NODE_HEIGHT, MILESTONE_NODE_HEIGHT, COLLAPSED_GROUP_HEIGHT, DIAGRAM_PADDING10, GROUP_PADDING3, GROUP_TOP_PADDING, SWIMLANE_SLOT_SEP, SWIMLANE_GAP, NODE_CELL_FONT_SIZE, NODE_NAME_FONT_SIZE, MILESTONE_NAME_FONT_SIZE, CELL_PAD_X, NAME_PAD_X, NAME_PIN_WIDTH, MIN_CELL_WIDTH, MIN_NODE_WIDTH3, MAX_NODE_WIDTH2, MIN_MILESTONE_WIDTH, MAX_MILESTONE_WIDTH;
39174
+ var import_dagre8, DEFAULT_NODE_HEIGHT, MILESTONE_NODE_HEIGHT, COLLAPSED_GROUP_HEIGHT, DIAGRAM_PADDING10, GROUP_PADDING3, GROUP_TOP_PADDING, SWIMLANE_SLOT_SEP, SWIMLANE_GAP, NODE_CELL_FONT_SIZE, NODE_NAME_FONT_SIZE, MILESTONE_NAME_FONT_SIZE, CELL_PAD_X, NAME_PAD_X, NAME_PIN_WIDTH, MIN_CELL_WIDTH, MIN_NODE_WIDTH3, MAX_NODE_WIDTH2, MIN_MILESTONE_WIDTH, MAX_MILESTONE_WIDTH;
38214
39175
  var init_layout11 = __esm({
38215
39176
  "src/pert/layout.ts"() {
38216
39177
  "use strict";
38217
- import_dagre7 = __toESM(require("@dagrejs/dagre"), 1);
39178
+ import_dagre8 = __toESM(require("@dagrejs/dagre"), 1);
38218
39179
  init_internal();
38219
39180
  init_text_measure();
38220
39181
  DEFAULT_NODE_HEIGHT = 90;
@@ -47744,10 +48705,10 @@ function tierBand(maxSpanDeg) {
47744
48705
  }
47745
48706
  function labelBudget(width, height, band) {
47746
48707
  const bandCap = {
47747
- world: 6,
47748
- continental: 5,
47749
- regional: 4,
47750
- local: 3
48708
+ world: 7,
48709
+ continental: 6,
48710
+ regional: 5,
48711
+ local: 4
47751
48712
  };
47752
48713
  const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
47753
48714
  return Math.max(0, Math.min(area2, bandCap[band]));
@@ -47880,8 +48841,11 @@ function placeContextLabels(args) {
47880
48841
  color: waterColor,
47881
48842
  fontSize: FONT,
47882
48843
  // water names keep the base font (no footprint to scale on)
47883
- // Water before any country (×1000), then by tier, then kind, then name.
47884
- sort: tier * 10 + KIND_ORDER[kind]
48844
+ // Orientation-value bands (lower = earlier): MAJOR water (oceans + major
48845
+ // seas, tier ≤ 1) leads at `tier*10+kind` (0..~16); MINOR water (tier ≥ 2:
48846
+ // smaller seas, bays, gulfs, straits) drops to band 2 (2000+) — BELOW the
48847
+ // country band (1000+), so a big country outranks a minor basin.
48848
+ sort: (tier <= 1 ? 0 : 2e3) + tier * 10 + KIND_ORDER[kind]
47885
48849
  });
47886
48850
  }
47887
48851
  const ranked = countries.map((c) => {
@@ -47894,7 +48858,7 @@ function placeContextLabels(args) {
47894
48858
  let ci = 0;
47895
48859
  for (const r of ranked) {
47896
48860
  const { c, w, h, area: area2 } = r;
47897
- if (w > width * 0.66 || h > height * 0.66) continue;
48861
+ if (!c.curatedAnchor && w > width * 0.66 && h > height * 0.66) continue;
47898
48862
  if (!insideViewport(c.anchor, width, height)) continue;
47899
48863
  const sizeFrac = Math.sqrt(area2) / canvasLinear;
47900
48864
  const t = Math.min(
@@ -47919,15 +48883,23 @@ function placeContextLabels(args) {
47919
48883
  letterSpacing: 0,
47920
48884
  color,
47921
48885
  fontSize,
47922
- // Always after every water body (+1e6); larger area = earlier.
47923
- sort: 1e6 + ci++
48886
+ // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
48887
+ // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
48888
+ // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
48889
+ // Campeche) yet never displaces an ocean. Larger area = earlier within the
48890
+ // band (`ci` is the area-desc rank index).
48891
+ sort: 1e3 + ci++
47924
48892
  });
47925
48893
  }
47926
48894
  candidates.sort((a, b) => a.sort - b.sort);
47927
48895
  const placed = [];
47928
48896
  const placedRects = [];
48897
+ const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
48898
+ const waterCap = budget - Math.min(2, countryCount);
48899
+ let waterPlaced = 0;
47929
48900
  for (const cand of candidates) {
47930
48901
  if (placed.length >= budget) break;
48902
+ if (cand.italic && waterPlaced >= waterCap) continue;
47931
48903
  const rect = rectAround(
47932
48904
  cand.cx,
47933
48905
  cand.cy,
@@ -47954,6 +48926,7 @@ function placeContextLabels(args) {
47954
48926
  if (collides(rect)) continue;
47955
48927
  if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
47956
48928
  placedRects.push(rect);
48929
+ if (cand.italic) waterPlaced++;
47957
48930
  placed.push({
47958
48931
  x: cand.cx,
47959
48932
  y: cand.cy,
@@ -49916,16 +50889,16 @@ function layoutMap(resolved, data, size, opts) {
49916
50889
  countryCandidates.push({
49917
50890
  name: f.properties?.name ?? iso,
49918
50891
  bbox: [x0, y0, x1, y1],
49919
- anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
50892
+ anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
50893
+ curatedAnchor: !!anchorLngLat
49920
50894
  });
49921
50895
  }
49922
50896
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
49923
50897
  (id) => id.startsWith("US-")
49924
50898
  );
49925
50899
  if (usLayer && framedStateContainers) {
49926
- const containerSet = new Set(resolved.poiFrameContainers);
49927
50900
  for (const [iso, f] of usLayer) {
49928
- if (containerSet.has(iso) || regionById.has(iso)) continue;
50901
+ if (regionById.has(iso)) continue;
49929
50902
  const viewF = cullFeatureToView(f);
49930
50903
  if (!viewF) continue;
49931
50904
  const b = path.bounds(viewF);
@@ -50055,8 +51028,15 @@ var init_layout15 = __esm({
50055
51028
  W_MAX = 8;
50056
51029
  FONT2 = 11;
50057
51030
  WORLD_LABEL_ANCHORS = {
50058
- US: [-98.5, 39.5]
51031
+ US: [-98.5, 39.5],
50059
51032
  // CONUS geographic centre (near Lebanon, Kansas)
51033
+ // Russia crosses the antimeridian (Chukotka at ~170°W), so on a non-global
51034
+ // (e.g. Europe) projection its geometry smears across the whole frame and the
51035
+ // area-weighted centroid lands mid-map (over Europe) — useless as a label
51036
+ // anchor. Pin it to European Russia (~Volga) so a Europe view labels visible
51037
+ // western Russia on its eastern margin; on a world view this still sits over
51038
+ // Russian land. (See the curated-anchor smear-gate bypass in context-labels.)
51039
+ RU: [45, 58]
50060
51040
  };
50061
51041
  MAX_CLUSTER_EXTENT_FACTOR = 0.18;
50062
51042
  MAX_COLUMN_ROWS = 7;