@diagrammo/dgmo 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/auto.cjs CHANGED
@@ -27527,9 +27527,1342 @@ var init_renderer6 = __esm({
27527
27527
  }
27528
27528
  });
27529
27529
 
27530
+ // src/boxes-and-lines/layout-layered.ts
27531
+ function rng(s) {
27532
+ return () => {
27533
+ s |= 0;
27534
+ s = s + 1831565813 | 0;
27535
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
27536
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
27537
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
27538
+ };
27539
+ }
27540
+ function layeredCandidates(parsed, sizes, opts) {
27541
+ if (parsed.groups.length > 0) return [];
27542
+ const nCount = parsed.nodes.length;
27543
+ if (nCount < 3 || nCount > 40) return [];
27544
+ const isTB = parsed.direction === "TB";
27545
+ const nodesep = opts?.nodesep ?? NODESEP;
27546
+ const ranksep = opts?.ranksep ?? RANKSEP;
27547
+ const backEdgeSide = opts?.backEdgeSide ?? "nearest";
27548
+ const restarts = opts?.restarts ?? (nCount <= 14 ? 28 : nCount <= 25 ? 14 : 6);
27549
+ const keepK = opts?.keepK ?? 3;
27550
+ const labels = parsed.nodes.map((n) => n.label);
27551
+ const labelSet = new Set(labels);
27552
+ const edges = parsed.edges.map((e, i) => ({ e, i })).filter(
27553
+ ({ e }) => labelSet.has(e.source) && labelSet.has(e.target) && e.source !== e.target
27554
+ );
27555
+ if (edges.length === 0) return [];
27556
+ const adj = /* @__PURE__ */ new Map();
27557
+ for (const l of labels) adj.set(l, []);
27558
+ for (const { e, i } of edges)
27559
+ adj.get(e.source).push({ to: e.target, idx: i });
27560
+ const reversed = /* @__PURE__ */ new Set();
27561
+ const state = /* @__PURE__ */ new Map();
27562
+ for (const l of labels) state.set(l, 0);
27563
+ const dfs = (u) => {
27564
+ state.set(u, 1);
27565
+ for (const { to, idx } of adj.get(u)) {
27566
+ const st = state.get(to);
27567
+ if (st === 1)
27568
+ reversed.add(idx);
27569
+ else if (st === 0) dfs(to);
27570
+ }
27571
+ state.set(u, 2);
27572
+ };
27573
+ for (const l of labels) if (state.get(l) === 0) dfs(l);
27574
+ const dag = edges.map(
27575
+ ({ 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 }
27576
+ );
27577
+ const outDag = /* @__PURE__ */ new Map();
27578
+ const inDeg = /* @__PURE__ */ new Map();
27579
+ for (const l of labels) {
27580
+ outDag.set(l, []);
27581
+ inDeg.set(l, 0);
27582
+ }
27583
+ for (const d of dag) {
27584
+ outDag.get(d.from).push(d.to);
27585
+ inDeg.set(d.to, (inDeg.get(d.to) ?? 0) + 1);
27586
+ }
27587
+ const rank = /* @__PURE__ */ new Map();
27588
+ for (const l of labels) rank.set(l, 0);
27589
+ const queue = labels.filter((l) => (inDeg.get(l) ?? 0) === 0);
27590
+ const indeg2 = new Map(inDeg);
27591
+ const topo = [];
27592
+ while (queue.length) {
27593
+ const u = queue.shift();
27594
+ topo.push(u);
27595
+ for (const v of outDag.get(u)) {
27596
+ rank.set(v, Math.max(rank.get(v), rank.get(u) + 1));
27597
+ indeg2.set(v, indeg2.get(v) - 1);
27598
+ if (indeg2.get(v) === 0) queue.push(v);
27599
+ }
27600
+ }
27601
+ if (topo.length !== labels.length) return [];
27602
+ const maxRank = Math.max(...labels.map((l) => rank.get(l)));
27603
+ const node = /* @__PURE__ */ new Map();
27604
+ for (const n of parsed.nodes) {
27605
+ const s = sizes.get(n.label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
27606
+ node.set(n.label, {
27607
+ id: n.label,
27608
+ dummy: false,
27609
+ rank: rank.get(n.label),
27610
+ cross: 0,
27611
+ thick: isTB ? s.width : s.height,
27612
+ depth: isTB ? s.height : s.width,
27613
+ realW: s.width,
27614
+ realH: s.height
27615
+ });
27616
+ }
27617
+ const segs = [];
27618
+ const chains = [];
27619
+ const backEdges = [];
27620
+ for (const d of dag) {
27621
+ if (d.rev) {
27622
+ backEdges.push({ edgeIdx: d.idx, src: d.to, tgt: d.from });
27623
+ continue;
27624
+ }
27625
+ const r0 = rank.get(d.from);
27626
+ const r1 = rank.get(d.to);
27627
+ const dagChain = [d.from];
27628
+ let prev = d.from;
27629
+ for (let r = r0 + 1; r < r1; r++) {
27630
+ const id = `__d${d.idx}_${r}`;
27631
+ node.set(id, {
27632
+ id,
27633
+ dummy: true,
27634
+ rank: r,
27635
+ cross: 0,
27636
+ thick: DUMMY_THICK,
27637
+ depth: 0,
27638
+ realW: 0,
27639
+ realH: 0
27640
+ });
27641
+ segs.push({ a: prev, b: id });
27642
+ dagChain.push(id);
27643
+ prev = id;
27644
+ }
27645
+ segs.push({ a: prev, b: d.to });
27646
+ dagChain.push(d.to);
27647
+ chains.push({
27648
+ edgeIdx: d.idx,
27649
+ chain: d.rev ? dagChain.slice().reverse() : dagChain
27650
+ });
27651
+ }
27652
+ const ranks = Array.from({ length: maxRank + 1 }, () => []);
27653
+ for (const n of node.values()) ranks[n.rank].push(n.id);
27654
+ const up = /* @__PURE__ */ new Map();
27655
+ const down = /* @__PURE__ */ new Map();
27656
+ for (const id of node.keys()) {
27657
+ up.set(id, []);
27658
+ down.set(id, []);
27659
+ }
27660
+ for (const s of segs) {
27661
+ down.get(s.a).push(s.b);
27662
+ up.get(s.b).push(s.a);
27663
+ }
27664
+ const orderOf = /* @__PURE__ */ new Map();
27665
+ const applyOrder = (ord) => {
27666
+ for (const id of node.keys()) orderOf.set(id, ord.get(id));
27667
+ };
27668
+ const gapCrossings = (r) => {
27669
+ const lower = ranks[r + 1];
27670
+ if (!lower) return 0;
27671
+ const es = [];
27672
+ for (const a of ranks[r]) {
27673
+ const pa = orderOf.get(a);
27674
+ for (const b of down.get(a)) es.push({ p: pa, q: orderOf.get(b) });
27675
+ }
27676
+ let c = 0;
27677
+ for (let i = 0; i < es.length; i++)
27678
+ for (let j = i + 1; j < es.length; j++) {
27679
+ const A = es[i], B = es[j];
27680
+ if ((A.p - B.p) * (A.q - B.q) < 0) c++;
27681
+ }
27682
+ return c;
27683
+ };
27684
+ const totalCrossings = () => {
27685
+ let c = 0;
27686
+ for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
27687
+ return c;
27688
+ };
27689
+ const median = (vals) => {
27690
+ if (vals.length === 0) return -1;
27691
+ vals.sort((x, y) => x - y);
27692
+ const m = Math.floor(vals.length / 2);
27693
+ if (vals.length % 2 === 1) return vals[m];
27694
+ if (vals.length === 2) return (vals[0] + vals[1]) / 2;
27695
+ const left = vals[m - 1] - vals[0];
27696
+ const right = vals[vals.length - 1] - vals[m];
27697
+ return left + right === 0 ? (vals[m - 1] + vals[m]) / 2 : (vals[m - 1] * right + vals[m] * left) / (left + right);
27698
+ };
27699
+ const wmedianRank = (r, useUp) => {
27700
+ const ids = ranks[r];
27701
+ const neigh = useUp ? up : down;
27702
+ const med = /* @__PURE__ */ new Map();
27703
+ for (const id of ids)
27704
+ med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
27705
+ const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
27706
+ const out = new Array(ids.length).fill(null);
27707
+ for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
27708
+ let mi = 0;
27709
+ for (let slot = 0; slot < out.length; slot++)
27710
+ if (out[slot] === null) out[slot] = movable[mi++];
27711
+ const final = out;
27712
+ final.forEach((id, i) => orderOf.set(id, i));
27713
+ ranks[r] = final;
27714
+ };
27715
+ const transpose = () => {
27716
+ let improved = true;
27717
+ let guard = 0;
27718
+ while (improved && guard++ < 12) {
27719
+ improved = false;
27720
+ for (let r = 0; r <= maxRank; r++) {
27721
+ const ids = ranks[r];
27722
+ for (let i = 0; i < ids.length - 1; i++) {
27723
+ const before = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
27724
+ const a = ids[i], b = ids[i + 1];
27725
+ ids[i] = b;
27726
+ ids[i + 1] = a;
27727
+ orderOf.set(b, i);
27728
+ orderOf.set(a, i + 1);
27729
+ const after = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
27730
+ if (after < before) {
27731
+ improved = true;
27732
+ } else {
27733
+ ids[i] = a;
27734
+ ids[i + 1] = b;
27735
+ orderOf.set(a, i);
27736
+ orderOf.set(b, i + 1);
27737
+ }
27738
+ }
27739
+ }
27740
+ }
27741
+ };
27742
+ const initOrder = (seed) => {
27743
+ const r = seed === 0 ? null : rng(seed);
27744
+ for (let rr = 0; rr <= maxRank; rr++) {
27745
+ const ids = ranks[rr].slice();
27746
+ if (r) {
27747
+ for (let i = ids.length - 1; i > 0; i--) {
27748
+ const j = Math.floor(r() * (i + 1));
27749
+ [ids[i], ids[j]] = [ids[j], ids[i]];
27750
+ }
27751
+ }
27752
+ ranks[rr] = ids;
27753
+ ids.forEach((id, i) => orderOf.set(id, i));
27754
+ }
27755
+ };
27756
+ const found = [];
27757
+ for (let s = 0; s < restarts; s++) {
27758
+ initOrder(s);
27759
+ let best = totalCrossings();
27760
+ let bestSnap = new Map(orderOf);
27761
+ for (let iter = 0; iter < 8; iter++) {
27762
+ const down1 = iter % 2 === 0;
27763
+ if (down1) for (let r = 1; r <= maxRank; r++) wmedianRank(r, true);
27764
+ else for (let r = maxRank - 1; r >= 0; r--) wmedianRank(r, false);
27765
+ transpose();
27766
+ const c = totalCrossings();
27767
+ if (c < best) {
27768
+ best = c;
27769
+ bestSnap = new Map(orderOf);
27770
+ }
27771
+ if (c === 0) break;
27772
+ }
27773
+ applyOrder(bestSnap);
27774
+ for (let r = 0; r <= maxRank; r++)
27775
+ ranks[r].sort((a, b) => orderOf.get(a) - orderOf.get(b));
27776
+ const sig = ranks.map((r) => r.join(",")).join("|");
27777
+ if (!found.some((f) => f.sig === sig))
27778
+ found.push({ sig, cross: best, order: new Map(bestSnap) });
27779
+ }
27780
+ found.sort((a, b) => a.cross - b.cross);
27781
+ const keep = found.slice(0, keepK);
27782
+ if (keep.length === 0) return [];
27783
+ const results = [];
27784
+ for (const cand of keep) {
27785
+ applyOrder(cand.order);
27786
+ const rk = Array.from({ length: maxRank + 1 }, () => []);
27787
+ for (const n of node.values()) rk[n.rank].push(n.id);
27788
+ for (let r = 0; r <= maxRank; r++)
27789
+ rk[r].sort((a, b) => cand.order.get(a) - cand.order.get(b));
27790
+ results.push(realize(rk));
27791
+ }
27792
+ return results;
27793
+ function realize(rk) {
27794
+ const bandDepth = rk.map(
27795
+ (ids) => Math.max(0, ...ids.map((id) => node.get(id).depth))
27796
+ );
27797
+ const bandCenter = [];
27798
+ let acc = MARGIN3;
27799
+ for (let r = 0; r <= maxRank; r++) {
27800
+ bandCenter[r] = acc + bandDepth[r] / 2;
27801
+ acc += bandDepth[r] + ranksep;
27802
+ }
27803
+ for (let r = 0; r <= maxRank; r++) {
27804
+ let x = MARGIN3;
27805
+ for (const id of rk[r]) {
27806
+ const n = node.get(id);
27807
+ n.cross = x + n.thick / 2;
27808
+ x += n.thick + nodesep;
27809
+ }
27810
+ }
27811
+ const ITER = 18;
27812
+ for (let it = 0; it < ITER; it++) {
27813
+ const downPass = it % 2 === 0;
27814
+ const order = downPass ? Array.from({ length: maxRank + 1 }, (_, i) => i) : Array.from({ length: maxRank + 1 }, (_, i) => maxRank - i);
27815
+ for (const r of order) {
27816
+ const ids = rk[r];
27817
+ const desired = ids.map((id) => {
27818
+ const n = node.get(id);
27819
+ let sw = 0, sx2 = 0;
27820
+ for (const nb of up.get(id)) {
27821
+ const w = weight(n, node.get(nb));
27822
+ sw += w;
27823
+ sx2 += w * node.get(nb).cross;
27824
+ }
27825
+ for (const nb of down.get(id)) {
27826
+ const w = weight(n, node.get(nb));
27827
+ sw += w;
27828
+ sx2 += w * node.get(nb).cross;
27829
+ }
27830
+ return sw > 0 ? sx2 / sw : n.cross;
27831
+ });
27832
+ placeWithSeparation(ids, desired);
27833
+ }
27834
+ }
27835
+ let minC = Infinity;
27836
+ for (const n of node.values()) minC = Math.min(minC, n.cross - n.thick / 2);
27837
+ const shift = MARGIN3 - minC;
27838
+ for (const n of node.values()) n.cross += shift;
27839
+ const coord = (n) => isTB ? { x: n.cross, y: bandCenter[n.rank] } : { x: bandCenter[n.rank], y: n.cross };
27840
+ const layoutNodes = parsed.nodes.map((pn) => {
27841
+ const n = node.get(pn.label);
27842
+ const c = coord(n);
27843
+ return {
27844
+ label: pn.label,
27845
+ x: c.x,
27846
+ y: c.y,
27847
+ width: n.realW,
27848
+ height: n.realH
27849
+ };
27850
+ });
27851
+ const chainById = /* @__PURE__ */ new Map();
27852
+ for (const ch of chains) chainById.set(ch.edgeIdx, ch.chain);
27853
+ let minCross = Infinity, maxCross = -Infinity;
27854
+ for (const n of node.values()) {
27855
+ if (n.dummy) continue;
27856
+ minCross = Math.min(minCross, n.cross - n.thick / 2);
27857
+ maxCross = Math.max(maxCross, n.cross + n.thick / 2);
27858
+ }
27859
+ const GAP = 34;
27860
+ const LANE = 28;
27861
+ const sideOf = (b) => {
27862
+ if (backEdgeSide === "left") return -1;
27863
+ if (backEdgeSide === "right") return 1;
27864
+ const d = node.get(b.src).cross - node.get(b.tgt).cross;
27865
+ return d >= 0 ? 1 : -1;
27866
+ };
27867
+ const backSorted = backEdges.map((b) => ({
27868
+ ...b,
27869
+ side: sideOf(b),
27870
+ span: Math.abs(node.get(b.src).rank - node.get(b.tgt).rank)
27871
+ })).sort((a, b) => a.side - b.side || a.span - b.span);
27872
+ const sideCounts = /* @__PURE__ */ new Map();
27873
+ for (const b of backSorted)
27874
+ sideCounts.set(b.side, (sideCounts.get(b.side) ?? 0) + 1);
27875
+ const laneCounter = /* @__PURE__ */ new Map();
27876
+ const backPoints = /* @__PURE__ */ new Map();
27877
+ const faceLim = (depth) => Math.max(0, depth / 2 - 6);
27878
+ const hubIndex = /* @__PURE__ */ new Map();
27879
+ const hubSize = /* @__PURE__ */ new Map();
27880
+ {
27881
+ const counter = /* @__PURE__ */ new Map();
27882
+ for (const b of backSorted) {
27883
+ const key = `${b.side}|${b.tgt}`;
27884
+ const idx = counter.get(key) ?? 0;
27885
+ counter.set(key, idx + 1);
27886
+ hubIndex.set(b.edgeIdx, idx);
27887
+ }
27888
+ for (const [key, v] of counter) hubSize.set(key, v);
27889
+ }
27890
+ for (const b of backSorted) {
27891
+ const s = b.side;
27892
+ const k = laneCounter.get(s) ?? 0;
27893
+ laneCounter.set(s, k + 1);
27894
+ const K = sideCounts.get(s);
27895
+ const src = node.get(b.src);
27896
+ const tgt = node.get(b.tgt);
27897
+ const laneC = s > 0 ? maxCross + GAP + k * LANE : minCross - GAP - k * LANE;
27898
+ const srcCtr = bandCenter[src.rank];
27899
+ const tgtCtr = bandCenter[tgt.rank];
27900
+ const frac = K > 1 ? (k + 1) / (K + 1) : 0;
27901
+ const srcR = srcCtr + frac * faceLim(src.depth);
27902
+ const tgtR = tgtCtr - frac * faceLim(tgt.depth);
27903
+ const m = (c, r) => isTB ? { x: c, y: r } : { x: r, y: c };
27904
+ const off = 10;
27905
+ const srcEdgeC = src.cross + s * (src.thick / 2 + off);
27906
+ const tgtEdgeC = tgt.cross + s * (tgt.thick / 2 + off);
27907
+ const hubK = hubIndex.get(b.edgeIdx);
27908
+ const hubN = hubSize.get(`${s}|${b.tgt}`);
27909
+ if (hubN > 1 && hubK > 0) {
27910
+ const tgtTop = tgtCtr - (tgt.depth / 2 + GAP + (hubK - 1) * LANE);
27911
+ const entryCross = tgt.cross + s * faceLim(tgt.thick) * (hubK / hubN);
27912
+ backPoints.set(b.edgeIdx, [
27913
+ m(src.cross, srcCtr),
27914
+ m(srcEdgeC, srcR),
27915
+ m(laneC, srcR),
27916
+ m(laneC, tgtTop),
27917
+ m(entryCross, tgtTop),
27918
+ m(tgt.cross, tgtCtr)
27919
+ ]);
27920
+ } else {
27921
+ backPoints.set(b.edgeIdx, [
27922
+ m(src.cross, srcCtr),
27923
+ m(srcEdgeC, srcR),
27924
+ m(laneC, srcR),
27925
+ m(laneC, tgtR),
27926
+ m(tgtEdgeC, tgtR),
27927
+ m(tgt.cross, tgtCtr)
27928
+ ]);
27929
+ }
27930
+ }
27931
+ const layoutEdges = [];
27932
+ for (let i = 0; i < parsed.edges.length; i++) {
27933
+ const e = parsed.edges[i];
27934
+ if (!labelSet.has(e.source) || !labelSet.has(e.target)) continue;
27935
+ let points;
27936
+ if (e.source === e.target) {
27937
+ const c = coord(node.get(e.source));
27938
+ points = [c, c];
27939
+ } else if (backPoints.has(i)) {
27940
+ points = backPoints.get(i);
27941
+ } else {
27942
+ const chain = chainById.get(i);
27943
+ if (!chain) continue;
27944
+ points = chain.map((id) => coord(node.get(id)));
27945
+ }
27946
+ layoutEdges.push({
27947
+ source: e.source,
27948
+ target: e.target,
27949
+ ...e.label !== void 0 && { label: e.label },
27950
+ bidirectional: e.bidirectional,
27951
+ lineNumber: e.lineNumber,
27952
+ points,
27953
+ yOffset: 0,
27954
+ parallelCount: 1,
27955
+ metadata: e.metadata
27956
+ });
27957
+ }
27958
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
27959
+ for (const n of layoutNodes) {
27960
+ minX = Math.min(minX, n.x - n.width / 2);
27961
+ minY = Math.min(minY, n.y - n.height / 2);
27962
+ maxX = Math.max(maxX, n.x + n.width / 2);
27963
+ maxY = Math.max(maxY, n.y + n.height / 2);
27964
+ }
27965
+ for (const e of layoutEdges)
27966
+ for (const p of e.points) {
27967
+ minX = Math.min(minX, p.x);
27968
+ minY = Math.min(minY, p.y);
27969
+ maxX = Math.max(maxX, p.x);
27970
+ maxY = Math.max(maxY, p.y);
27971
+ }
27972
+ const sx = MARGIN3 - minX;
27973
+ const sy = MARGIN3 - minY;
27974
+ const shifted = sx !== 0 || sy !== 0 ? {
27975
+ nodes: layoutNodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })),
27976
+ edges: layoutEdges.map((e) => ({
27977
+ ...e,
27978
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
27979
+ }))
27980
+ } : { nodes: layoutNodes, edges: layoutEdges };
27981
+ return {
27982
+ nodes: shifted.nodes,
27983
+ edges: shifted.edges,
27984
+ groups: [],
27985
+ width: maxX + sx + MARGIN3,
27986
+ height: maxY + sy + MARGIN3
27987
+ };
27988
+ }
27989
+ function weight(a, b) {
27990
+ if (a.dummy && b.dummy) return W_DUMMY_DUMMY;
27991
+ if (a.dummy || b.dummy) return W_REAL_DUMMY;
27992
+ return W_REAL_REAL;
27993
+ }
27994
+ function placeWithSeparation(ids, desired) {
27995
+ const n = ids.length;
27996
+ if (n === 0) return;
27997
+ const half = ids.map((id) => node.get(id).thick / 2);
27998
+ const off = [0];
27999
+ for (let i = 1; i < n; i++)
28000
+ off[i] = off[i - 1] + half[i - 1] + nodesep + half[i];
28001
+ const d = desired.map((v, i) => v - off[i]);
28002
+ const blocks = [];
28003
+ for (let i = 0; i < n; i++) {
28004
+ let b = { pos: d[i], count: 1, sumOffset: d[i], first: i };
28005
+ while (blocks.length && blocks[blocks.length - 1].pos >= b.pos) {
28006
+ const prev = blocks.pop();
28007
+ const count = prev.count + b.count;
28008
+ const sumOffset = prev.sumOffset + b.sumOffset;
28009
+ b = { pos: sumOffset / count, count, sumOffset, first: prev.first };
28010
+ }
28011
+ blocks.push(b);
28012
+ }
28013
+ const xs = new Array(n);
28014
+ for (const b of blocks)
28015
+ for (let k = 0; k < b.count; k++) xs[b.first + k] = b.pos;
28016
+ for (let i = 0; i < n; i++) node.get(ids[i]).cross = xs[i] + off[i];
28017
+ }
28018
+ }
28019
+ var NODESEP, RANKSEP, DUMMY_THICK, MARGIN3, W_REAL_REAL, W_REAL_DUMMY, W_DUMMY_DUMMY;
28020
+ var init_layout_layered = __esm({
28021
+ "src/boxes-and-lines/layout-layered.ts"() {
28022
+ "use strict";
28023
+ init_layout5();
28024
+ NODESEP = 50;
28025
+ RANKSEP = 60;
28026
+ DUMMY_THICK = 10;
28027
+ MARGIN3 = 40;
28028
+ W_REAL_REAL = 1;
28029
+ W_REAL_DUMMY = 2;
28030
+ W_DUMMY_DUMMY = 8;
28031
+ }
28032
+ });
28033
+
28034
+ // src/boxes-and-lines/layout-search.ts
28035
+ var layout_search_exports = {};
28036
+ __export(layout_search_exports, {
28037
+ countEdgeNearMiss: () => countEdgeNearMiss,
28038
+ countEdgeNodePierces: () => countEdgeNodePierces,
28039
+ countEdgeOverlaps: () => countEdgeOverlaps,
28040
+ countGroupOverlaps: () => countGroupOverlaps,
28041
+ countSplineCrossings: () => countSplineCrossings,
28042
+ deroutePierces: () => deroutePierces,
28043
+ detectEdgeNodePierces: () => detectEdgeNodePierces,
28044
+ detectEdgeOverlaps: () => detectEdgeOverlaps,
28045
+ detectGroupOverlaps: () => detectGroupOverlaps,
28046
+ layoutBoxesAndLinesSearch: () => layoutBoxesAndLinesSearch,
28047
+ separateGroupBands: () => separateGroupBands
28048
+ });
28049
+ function rng2(s) {
28050
+ return () => {
28051
+ s |= 0;
28052
+ s = s + 1831565813 | 0;
28053
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
28054
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
28055
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
28056
+ };
28057
+ }
28058
+ function shuffle(a, r) {
28059
+ const x = a.slice();
28060
+ for (let i = x.length - 1; i > 0; i--) {
28061
+ const j = Math.floor(r() * (i + 1));
28062
+ [x[i], x[j]] = [x[j], x[i]];
28063
+ }
28064
+ return x;
28065
+ }
28066
+ function flatten(d) {
28067
+ const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
28068
+ const pts = [];
28069
+ let i = 0, cx = 0, cy = 0, cmd = "";
28070
+ const num = () => parseFloat(toks[i++]);
28071
+ const samp = (p0, c1, c2, p1) => {
28072
+ for (let t = 0; t <= 1; t += 0.12) {
28073
+ const u = 1 - t;
28074
+ if (c2)
28075
+ pts.push({
28076
+ x: u * u * u * p0.x + 3 * u * u * t * c1.x + 3 * u * t * t * c2.x + t * t * t * p1.x,
28077
+ y: u * u * u * p0.y + 3 * u * u * t * c1.y + 3 * u * t * t * c2.y + t * t * t * p1.y
28078
+ });
28079
+ else
28080
+ pts.push({
28081
+ x: u * u * p0.x + 2 * u * t * c1.x + t * t * p1.x,
28082
+ y: u * u * p0.y + 2 * u * t * c1.y + t * t * p1.y
28083
+ });
28084
+ }
28085
+ };
28086
+ while (i < toks.length) {
28087
+ const tk = toks[i];
28088
+ if (/[MLQC]/i.test(tk)) {
28089
+ cmd = tk;
28090
+ i++;
28091
+ }
28092
+ if (cmd === "M" || cmd === "L") {
28093
+ const x = num(), y = num();
28094
+ pts.push({ x, y });
28095
+ cx = x;
28096
+ cy = y;
28097
+ } else if (cmd === "Q") {
28098
+ const c1 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
28099
+ samp({ x: cx, y: cy }, c1, null, p1);
28100
+ cx = p1.x;
28101
+ cy = p1.y;
28102
+ } else if (cmd === "C") {
28103
+ const c1 = { x: num(), y: num() }, c2 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
28104
+ samp({ x: cx, y: cy }, c1, c2, p1);
28105
+ cx = p1.x;
28106
+ cy = p1.y;
28107
+ } else i++;
28108
+ }
28109
+ return pts;
28110
+ }
28111
+ function segPoint(p1, p2, p3, p4) {
28112
+ const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
28113
+ if (Math.abs(den) < 1e-9) return null;
28114
+ 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;
28115
+ 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;
28116
+ }
28117
+ function countSplineCrossings(layout) {
28118
+ const center = /* @__PURE__ */ new Map();
28119
+ for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
28120
+ for (const g of layout.groups)
28121
+ if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
28122
+ const polys = layout.edges.map((e) => {
28123
+ const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
28124
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28125
+ for (const p of pts) {
28126
+ if (p.x < x0) x0 = p.x;
28127
+ if (p.x > x1) x1 = p.x;
28128
+ if (p.y < y0) y0 = p.y;
28129
+ if (p.y > y1) y1 = p.y;
28130
+ }
28131
+ return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
28132
+ });
28133
+ const R = 34;
28134
+ let total = 0;
28135
+ for (let a = 0; a < polys.length; a++)
28136
+ for (let b = a + 1; b < polys.length; b++) {
28137
+ const A = polys[a], B = polys[b];
28138
+ if (A.pts.length < 2 || B.pts.length < 2) continue;
28139
+ if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
28140
+ const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
28141
+ const hits = [];
28142
+ for (let i = 1; i < A.pts.length; i++)
28143
+ for (let j = 1; j < B.pts.length; j++) {
28144
+ const p = segPoint(
28145
+ A.pts[i - 1],
28146
+ A.pts[i],
28147
+ B.pts[j - 1],
28148
+ B.pts[j]
28149
+ );
28150
+ if (!p) continue;
28151
+ if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
28152
+ continue;
28153
+ if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
28154
+ hits.push(p);
28155
+ }
28156
+ total += hits.length;
28157
+ }
28158
+ return total;
28159
+ }
28160
+ function pointSegDist(p, a, b) {
28161
+ const dx = b.x - a.x, dy = b.y - a.y;
28162
+ const len2 = dx * dx + dy * dy;
28163
+ if (len2 < 1e-9) return Math.hypot(p.x - a.x, p.y - a.y);
28164
+ let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
28165
+ t = Math.max(0, Math.min(1, t));
28166
+ return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));
28167
+ }
28168
+ function distToPoly(p, poly) {
28169
+ let m = Infinity;
28170
+ for (let i = 1; i < poly.length; i++)
28171
+ m = Math.min(m, pointSegDist(p, poly[i - 1], poly[i]));
28172
+ return m;
28173
+ }
28174
+ function pointRectDist(p, r) {
28175
+ const dx = Math.max(r.x - r.w / 2 - p.x, 0, p.x - (r.x + r.w / 2));
28176
+ const dy = Math.max(r.y - r.h / 2 - p.y, 0, p.y - (r.y + r.h / 2));
28177
+ return Math.hypot(dx, dy);
28178
+ }
28179
+ function detectEdgeOverlaps(layout, opts) {
28180
+ const dist = opts?.dist ?? 8;
28181
+ const minLen = opts?.minLen ?? 16;
28182
+ const nodeClear = opts?.nodeClear ?? 12;
28183
+ const rect = /* @__PURE__ */ new Map();
28184
+ for (const n of layout.nodes)
28185
+ rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
28186
+ for (const g of layout.groups)
28187
+ if (g.collapsed)
28188
+ rect.set("__group_" + g.label, {
28189
+ x: g.x,
28190
+ y: g.y,
28191
+ w: g.width,
28192
+ h: g.height
28193
+ });
28194
+ const polys = layout.edges.map((e) => {
28195
+ const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
28196
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28197
+ for (const p of pts) {
28198
+ if (p.x < x0) x0 = p.x;
28199
+ if (p.x > x1) x1 = p.x;
28200
+ if (p.y < y0) y0 = p.y;
28201
+ if (p.y > y1) y1 = p.y;
28202
+ }
28203
+ return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
28204
+ });
28205
+ const runs = [];
28206
+ for (let a = 0; a < polys.length; a++)
28207
+ for (let b = a + 1; b < polys.length; b++) {
28208
+ const A = polys[a], B = polys[b];
28209
+ if (A.pts.length < 2 || B.pts.length < 2) continue;
28210
+ if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
28211
+ continue;
28212
+ const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
28213
+ let run2 = [];
28214
+ let runLen = 0;
28215
+ const flush = () => {
28216
+ if (runLen >= minLen && run2.length >= 2)
28217
+ runs.push({
28218
+ mid: run2[Math.floor(run2.length / 2)],
28219
+ length: runLen,
28220
+ pts: run2.slice()
28221
+ });
28222
+ run2 = [];
28223
+ runLen = 0;
28224
+ };
28225
+ for (const p of A.pts) {
28226
+ const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
28227
+ const covered = !nearShared && distToPoly(p, B.pts) < dist;
28228
+ if (covered) {
28229
+ if (run2.length)
28230
+ runLen += Math.hypot(
28231
+ p.x - run2[run2.length - 1].x,
28232
+ p.y - run2[run2.length - 1].y
28233
+ );
28234
+ run2.push(p);
28235
+ } else flush();
28236
+ }
28237
+ flush();
28238
+ }
28239
+ return runs;
28240
+ }
28241
+ function countEdgeOverlaps(layout, opts) {
28242
+ return detectEdgeOverlaps(layout, opts).length;
28243
+ }
28244
+ function detectEdgeNodePierces(layout, opts) {
28245
+ const inset = opts?.inset ?? 6;
28246
+ const minPts = opts?.minPts ?? 2;
28247
+ const rects = [];
28248
+ for (const n of layout.nodes)
28249
+ rects.push({ key: n.label, x: n.x, y: n.y, w: n.width, h: n.height });
28250
+ for (const g of layout.groups)
28251
+ if (g.collapsed)
28252
+ rects.push({
28253
+ key: "__group_" + g.label,
28254
+ x: g.x,
28255
+ y: g.y,
28256
+ w: g.width,
28257
+ h: g.height
28258
+ });
28259
+ const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
28260
+ const out = [];
28261
+ layout.edges.forEach((e, idx) => {
28262
+ if (e.points.length < 2) return;
28263
+ const poly = flatten(splineGen(e.points) ?? "");
28264
+ for (const r of rects) {
28265
+ if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
28266
+ continue;
28267
+ const hits = poly.filter((p) => inside(p, r));
28268
+ if (hits.length >= minPts)
28269
+ out.push({ edgeIdx: idx, node: r.key, pts: hits });
28270
+ }
28271
+ });
28272
+ return out;
28273
+ }
28274
+ function countEdgeNodePierces(layout, opts) {
28275
+ return detectEdgeNodePierces(layout, opts).length;
28276
+ }
28277
+ function deroutePierces(layout) {
28278
+ const pierces = detectEdgeNodePierces(layout);
28279
+ if (!pierces.length) return layout;
28280
+ const rect = /* @__PURE__ */ new Map();
28281
+ for (const n of layout.nodes)
28282
+ rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
28283
+ for (const g of layout.groups)
28284
+ if (g.collapsed)
28285
+ rect.set("__group_" + g.label, {
28286
+ x: g.x,
28287
+ y: g.y,
28288
+ w: g.width,
28289
+ h: g.height
28290
+ });
28291
+ const byEdge = /* @__PURE__ */ new Map();
28292
+ for (const p of pierces) {
28293
+ const arr = byEdge.get(p.edgeIdx);
28294
+ if (arr) arr.push(p.node);
28295
+ else byEdge.set(p.edgeIdx, [p.node]);
28296
+ }
28297
+ const edges = layout.edges.map((e, idx) => {
28298
+ const nodes = byEdge.get(idx);
28299
+ if (!nodes) return e;
28300
+ let pts = e.points.map((p) => ({ x: p.x, y: p.y }));
28301
+ for (const label of nodes) {
28302
+ const r = rect.get(label);
28303
+ if (r) pts = detourAround(pts, r);
28304
+ }
28305
+ return { ...e, points: pts };
28306
+ });
28307
+ return { ...layout, edges };
28308
+ }
28309
+ function detourAround(pts, r) {
28310
+ if (pts.length < 2) return pts;
28311
+ const c = { x: r.x, y: r.y };
28312
+ let bestSeg = -1;
28313
+ let bestT = 0;
28314
+ let bestD = Infinity;
28315
+ let bestPt = pts[0];
28316
+ for (let i = 0; i < pts.length - 1; i++) {
28317
+ const a2 = pts[i], b2 = pts[i + 1];
28318
+ const dx = b2.x - a2.x, dy = b2.y - a2.y;
28319
+ const len2 = dx * dx + dy * dy || 1e-9;
28320
+ let t = ((c.x - a2.x) * dx + (c.y - a2.y) * dy) / len2;
28321
+ t = Math.max(0, Math.min(1, t));
28322
+ const q = { x: a2.x + t * dx, y: a2.y + t * dy };
28323
+ const d = Math.hypot(q.x - c.x, q.y - c.y);
28324
+ if (d < bestD) {
28325
+ bestD = d;
28326
+ bestSeg = i;
28327
+ bestT = t;
28328
+ bestPt = q;
28329
+ }
28330
+ }
28331
+ if (bestSeg < 0) return pts;
28332
+ let nx = bestPt.x - c.x;
28333
+ let ny = bestPt.y - c.y;
28334
+ if (Math.hypot(nx, ny) < 1) {
28335
+ if (r.w <= r.h) {
28336
+ nx = 1;
28337
+ ny = 0;
28338
+ } else {
28339
+ nx = 0;
28340
+ ny = 1;
28341
+ }
28342
+ }
28343
+ const nlen = Math.hypot(nx, ny) || 1;
28344
+ nx /= nlen;
28345
+ ny /= nlen;
28346
+ const a = pts[bestSeg], b = pts[bestSeg + 1];
28347
+ let ex = b.x - a.x, ey = b.y - a.y;
28348
+ const elen = Math.hypot(ex, ey) || 1;
28349
+ ex /= elen;
28350
+ ey /= elen;
28351
+ const clear = Math.hypot(r.w, r.h) / 2 + 18;
28352
+ const hw = Math.hypot(r.w, r.h) / 2;
28353
+ void bestT;
28354
+ const before = {
28355
+ x: bestPt.x - ex * hw + nx * clear * 0.5,
28356
+ y: bestPt.y - ey * hw + ny * clear * 0.5
28357
+ };
28358
+ const peak = { x: c.x + nx * clear, y: c.y + ny * clear };
28359
+ const after = {
28360
+ x: bestPt.x + ex * hw + nx * clear * 0.5,
28361
+ y: bestPt.y + ey * hw + ny * clear * 0.5
28362
+ };
28363
+ const out = pts.slice(0, bestSeg + 1);
28364
+ out.push(before, peak, after);
28365
+ out.push(...pts.slice(bestSeg + 1));
28366
+ return out;
28367
+ }
28368
+ function countEdgeNearMiss(layout, opts) {
28369
+ const near = opts?.near ?? 14;
28370
+ const tight = opts?.tight ?? 8;
28371
+ const minLen = opts?.minLen ?? 18;
28372
+ const close = detectEdgeOverlaps(layout, { dist: near, minLen }).length;
28373
+ const over = detectEdgeOverlaps(layout, { dist: tight, minLen }).length;
28374
+ return Math.max(0, close - over);
28375
+ }
28376
+ function detectGroupOverlaps(layout, opts) {
28377
+ const margin = opts?.margin ?? 4;
28378
+ const raw = layout.groups.map((g) => ({
28379
+ label: g.label,
28380
+ l: g.x - g.width / 2,
28381
+ r: g.x + g.width / 2,
28382
+ t: g.y - g.height / 2,
28383
+ b: g.y + g.height / 2
28384
+ }));
28385
+ const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
28386
+ const rend = raw.map((a, i) => {
28387
+ const parent = raw.some((b, j) => j !== i && contains(a, b));
28388
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
28389
+ });
28390
+ const hit = /* @__PURE__ */ new Set();
28391
+ for (let i = 0; i < raw.length; i++)
28392
+ for (let j = i + 1; j < raw.length; j++) {
28393
+ if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
28394
+ const a = rend[i], b = rend[j];
28395
+ const dx = Math.max(a.l - b.r, b.l - a.r);
28396
+ const dy = Math.max(a.t - b.b, b.t - a.b);
28397
+ if (Math.max(dx, dy) < margin) {
28398
+ hit.add(raw[i].label);
28399
+ hit.add(raw[j].label);
28400
+ }
28401
+ }
28402
+ return [...hit];
28403
+ }
28404
+ function countGroupOverlaps(layout, opts) {
28405
+ const margin = opts?.margin ?? 4;
28406
+ const raw = layout.groups.map((g) => ({
28407
+ l: g.x - g.width / 2,
28408
+ r: g.x + g.width / 2,
28409
+ t: g.y - g.height / 2,
28410
+ b: g.y + g.height / 2
28411
+ }));
28412
+ const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
28413
+ const rend = raw.map((a, i) => {
28414
+ const parent = raw.some((b, j) => j !== i && contains(a, b));
28415
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
28416
+ });
28417
+ let count = 0;
28418
+ for (let i = 0; i < raw.length; i++)
28419
+ for (let j = i + 1; j < raw.length; j++) {
28420
+ if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
28421
+ const a = rend[i], b = rend[j];
28422
+ const dx = Math.max(a.l - b.r, b.l - a.r);
28423
+ const dy = Math.max(a.t - b.b, b.t - a.b);
28424
+ if (Math.max(dx, dy) < margin) count++;
28425
+ }
28426
+ return count;
28427
+ }
28428
+ function separateGroupBands(layout, parsed) {
28429
+ if (layout.groups.some((g) => g.collapsed)) return layout;
28430
+ if (countGroupOverlaps(layout) === 0) return layout;
28431
+ const parentOf = /* @__PURE__ */ new Map();
28432
+ for (const g of parsed.groups) parentOf.set(g.label, g.parentGroup);
28433
+ const topOf = (label) => {
28434
+ let cur = label;
28435
+ const seen = /* @__PURE__ */ new Set();
28436
+ for (; ; ) {
28437
+ const p = parentOf.get(cur);
28438
+ if (!p || seen.has(p)) return cur;
28439
+ seen.add(cur);
28440
+ cur = p;
28441
+ }
28442
+ };
28443
+ const topLabels = parsed.groups.filter((g) => !g.parentGroup).map((g) => g.label);
28444
+ if (topLabels.length < 2) return layout;
28445
+ const nodeTop = /* @__PURE__ */ new Map();
28446
+ for (const g of parsed.groups)
28447
+ for (const child of g.children)
28448
+ if (!parsed.groups.some((gg) => gg.label === child))
28449
+ nodeTop.set(child, topOf(g.label));
28450
+ const axis = parsed.direction === "TB" ? "x" : "y";
28451
+ const boxByLabel = new Map(layout.groups.map((g) => [g.label, g]));
28452
+ const bands = topLabels.map((label) => {
28453
+ const g = boxByLabel.get(label);
28454
+ const half2 = (axis === "y" ? g.height : g.width) / 2;
28455
+ let lo = (axis === "y" ? g.y : g.x) - half2;
28456
+ const hi = (axis === "y" ? g.y : g.x) + half2;
28457
+ const isParent = parsed.groups.some((c) => c.parentGroup === label);
28458
+ if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE2;
28459
+ return { label, lo, hi, c: (lo + hi) / 2 };
28460
+ });
28461
+ bands.sort((a, b) => a.c - b.c);
28462
+ const GAP = 16;
28463
+ const half = bands.map((b) => (b.hi - b.lo) / 2);
28464
+ const off = [0];
28465
+ for (let i = 1; i < bands.length; i++)
28466
+ off[i] = off[i - 1] + half[i - 1] + GAP + half[i];
28467
+ const desired = bands.map((b, i) => b.c - off[i]);
28468
+ const blocks = [];
28469
+ for (let i = 0; i < bands.length; i++) {
28470
+ let blk = { pos: desired[i], count: 1, sum: desired[i], first: i };
28471
+ while (blocks.length && blocks[blocks.length - 1].pos >= blk.pos) {
28472
+ const prev = blocks.pop();
28473
+ const count = prev.count + blk.count;
28474
+ const sum = prev.sum + blk.sum;
28475
+ blk = { pos: sum / count, count, sum, first: prev.first };
28476
+ }
28477
+ blocks.push(blk);
28478
+ }
28479
+ const newC = new Array(bands.length);
28480
+ for (const blk of blocks)
28481
+ for (let k = 0; k < blk.count; k++)
28482
+ newC[blk.first + k] = blk.pos + off[blk.first + k];
28483
+ const delta = /* @__PURE__ */ new Map();
28484
+ let moved = false;
28485
+ bands.forEach((b, i) => {
28486
+ const d = newC[i] - b.c;
28487
+ delta.set(b.label, d);
28488
+ if (Math.abs(d) > 0.5) moved = true;
28489
+ });
28490
+ if (!moved) return layout;
28491
+ const nodeDelta = (label) => {
28492
+ const top = nodeTop.get(label);
28493
+ return top ? delta.get(top) ?? 0 : 0;
28494
+ };
28495
+ const shift = (p, d) => axis === "y" ? { x: p.x, y: p.y + d } : { x: p.x + d, y: p.y };
28496
+ const nodes = layout.nodes.map((n) => {
28497
+ const d = nodeDelta(n.label);
28498
+ return d ? { ...n, ...axis === "y" ? { y: n.y + d } : { x: n.x + d } } : n;
28499
+ });
28500
+ const groups = layout.groups.map((g) => {
28501
+ const d = delta.get(topOf(g.label)) ?? 0;
28502
+ return d ? { ...g, ...axis === "y" ? { y: g.y + d } : { x: g.x + d } } : g;
28503
+ });
28504
+ const edges = layout.edges.map((e) => {
28505
+ const ds = nodeDelta(e.source);
28506
+ const dt = nodeDelta(e.target);
28507
+ if (!ds && !dt) return e;
28508
+ const N = e.points.length;
28509
+ const points = e.points.map((p, i) => {
28510
+ const f = N > 1 ? i / (N - 1) : 0;
28511
+ return shift(p, ds * (1 - f) + dt * f);
28512
+ });
28513
+ return { ...e, points };
28514
+ });
28515
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
28516
+ const acc = (x, y) => {
28517
+ if (x < minX) minX = x;
28518
+ if (x > maxX) maxX = x;
28519
+ if (y < minY) minY = y;
28520
+ if (y > maxY) maxY = y;
28521
+ };
28522
+ for (const n of nodes) {
28523
+ acc(n.x - n.width / 2, n.y - n.height / 2);
28524
+ acc(n.x + n.width / 2, n.y + n.height / 2);
28525
+ }
28526
+ for (const g of groups) {
28527
+ acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE2);
28528
+ acc(g.x + g.width / 2, g.y + g.height / 2);
28529
+ }
28530
+ for (const e of edges) for (const p of e.points) acc(p.x, p.y);
28531
+ const M = 40;
28532
+ const sx = M - minX, sy = M - minY;
28533
+ const reshift = sx !== 0 || sy !== 0;
28534
+ return {
28535
+ nodes: reshift ? nodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })) : nodes,
28536
+ groups: reshift ? groups.map((g) => ({ ...g, x: g.x + sx, y: g.y + sy })) : groups,
28537
+ edges: reshift ? edges.map((e) => ({
28538
+ ...e,
28539
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
28540
+ })) : edges,
28541
+ width: maxX - minX + 2 * M,
28542
+ height: maxY - minY + 2 * M
28543
+ };
28544
+ }
28545
+ function segCross(p1, p2, p3, p4) {
28546
+ const d1x = p2.x - p1.x, d1y = p2.y - p1.y, d2x = p4.x - p3.x, d2y = p4.y - p3.y;
28547
+ const den = d1x * d2y - d1y * d2x;
28548
+ if (Math.abs(den) < 1e-9) return false;
28549
+ const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / den;
28550
+ const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / den;
28551
+ return t > 1e-3 && t < 0.999 && s > 1e-3 && s < 0.999;
28552
+ }
28553
+ function countCrossingsFast(layout) {
28554
+ const E = layout.edges.filter((e) => e.points.length >= 2);
28555
+ const bb = E.map((e) => {
28556
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28557
+ for (const p of e.points) {
28558
+ if (p.x < x0) x0 = p.x;
28559
+ if (p.x > x1) x1 = p.x;
28560
+ if (p.y < y0) y0 = p.y;
28561
+ if (p.y > y1) y1 = p.y;
28562
+ }
28563
+ return { x0, y0, x1, y1 };
28564
+ });
28565
+ let count = 0;
28566
+ for (let i = 0; i < E.length; i++)
28567
+ for (let j = i + 1; j < E.length; j++) {
28568
+ const A = E[i], B = E[j];
28569
+ if (A.source === B.source || A.source === B.target || A.target === B.source || A.target === B.target)
28570
+ continue;
28571
+ const a = bb[i], b = bb[j];
28572
+ if (a.x1 < b.x0 || b.x1 < a.x0 || a.y1 < b.y0 || b.y1 < a.y0) continue;
28573
+ const pa = A.points, pb = B.points;
28574
+ let hit = false;
28575
+ for (let ai = 0; ai < pa.length - 1 && !hit; ai++)
28576
+ for (let bi = 0; bi < pb.length - 1; bi++) {
28577
+ if (segCross(pa[ai], pa[ai + 1], pb[bi], pb[bi + 1])) {
28578
+ hit = true;
28579
+ break;
28580
+ }
28581
+ }
28582
+ if (hit) count++;
28583
+ }
28584
+ return count;
28585
+ }
28586
+ function meanDrift(layout, prev) {
28587
+ if (!prev?.size) return 0;
28588
+ let sum = 0, n = 0;
28589
+ for (const node of layout.nodes) {
28590
+ const p = prev.get(node.label);
28591
+ if (p) {
28592
+ sum += Math.hypot(node.x - p.x, node.y - p.y);
28593
+ n++;
28594
+ }
28595
+ }
28596
+ return n ? sum / n : 0;
28597
+ }
28598
+ function edgeLength(layout) {
28599
+ let total = 0;
28600
+ for (const e of layout.edges)
28601
+ for (let i = 1; i < e.points.length; i++)
28602
+ total += Math.hypot(
28603
+ e.points[i].x - e.points[i - 1].x,
28604
+ e.points[i].y - e.points[i - 1].y
28605
+ );
28606
+ return total;
28607
+ }
28608
+ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
28609
+ const hideDescriptions = opts?.hideDescriptions ?? false;
28610
+ const collapsedGroupLabels = /* @__PURE__ */ new Set();
28611
+ if (collapseInfo) {
28612
+ const missing = /* @__PURE__ */ new Set();
28613
+ for (const og of collapseInfo.originalGroups)
28614
+ if (!parsed.groups.some((g) => g.label === og.label))
28615
+ missing.add(og.label);
28616
+ for (const label of missing) {
28617
+ const og = collapseInfo.originalGroups.find((g) => g.label === label);
28618
+ const parent = og?.parentGroup;
28619
+ if (!parent || !missing.has(parent)) collapsedGroupLabels.add(label);
28620
+ }
28621
+ }
28622
+ const sizes = /* @__PURE__ */ new Map();
28623
+ let maxDescH = 0;
28624
+ for (const node of parsed.nodes) {
28625
+ const s = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
28626
+ sizes.set(node.label, s);
28627
+ if (!hideDescriptions && node.description && node.description.length > 0)
28628
+ maxDescH = Math.max(maxDescH, s.height);
28629
+ }
28630
+ if (maxDescH > 0) {
28631
+ for (const node of parsed.nodes)
28632
+ if (node.description && node.description.length > 0) {
28633
+ const s = sizes.get(node.label);
28634
+ sizes.set(node.label, { width: s.width, height: maxDescH });
28635
+ }
28636
+ }
28637
+ const gid = (label) => `__group_${label}`;
28638
+ const rankdir = parsed.direction === "TB" ? "TB" : "LR";
28639
+ function place(cfg) {
28640
+ const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
28641
+ const ord = (a) => r ? shuffle(a, r) : a.slice();
28642
+ const g = new import_dagre4.default.graphlib.Graph({ compound: true, multigraph: true });
28643
+ g.setGraph({
28644
+ rankdir,
28645
+ ranker: cfg.ranker,
28646
+ nodesep: cfg.nodesep,
28647
+ ranksep: cfg.ranksep,
28648
+ edgesep: 20,
28649
+ marginx: 40,
28650
+ marginy: 40
28651
+ });
28652
+ g.setDefaultEdgeLabel(() => ({}));
28653
+ for (const grp of ord(parsed.groups))
28654
+ g.setNode(gid(grp.label), { label: grp.label });
28655
+ for (const node of ord(parsed.nodes)) {
28656
+ const s = sizes.get(node.label);
28657
+ g.setNode(node.label, { width: s.width, height: s.height });
28658
+ }
28659
+ for (const label of collapsedGroupLabels)
28660
+ g.setNode(gid(label), { width: NODE_WIDTH, height: NODE_HEIGHT });
28661
+ for (const grp of parsed.groups) {
28662
+ if (grp.parentGroup && g.hasNode(gid(grp.parentGroup)))
28663
+ g.setParent(gid(grp.label), gid(grp.parentGroup));
28664
+ for (const c of ord(grp.children)) {
28665
+ if (g.hasNode(c)) g.setParent(c, gid(grp.label));
28666
+ }
28667
+ }
28668
+ if (collapseInfo)
28669
+ for (const label of collapsedGroupLabels) {
28670
+ const og = collapseInfo.originalGroups.find((x) => x.label === label);
28671
+ if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && g.hasNode(gid(og.parentGroup)))
28672
+ g.setParent(gid(label), gid(og.parentGroup));
28673
+ }
28674
+ for (const e of ord(parsed.edges))
28675
+ if (g.hasNode(e.source) && g.hasNode(e.target))
28676
+ g.setEdge(e.source, e.target, {});
28677
+ import_dagre4.default.layout(g);
28678
+ const nodes = parsed.nodes.map((n2) => {
28679
+ const p = g.node(n2.label);
28680
+ return {
28681
+ label: n2.label,
28682
+ x: p.x,
28683
+ y: p.y,
28684
+ width: p.width,
28685
+ height: p.height
28686
+ };
28687
+ });
28688
+ const groups = parsed.groups.map(
28689
+ (grp) => {
28690
+ const p = g.node(gid(grp.label));
28691
+ return {
28692
+ label: grp.label,
28693
+ lineNumber: grp.lineNumber,
28694
+ x: p.x,
28695
+ y: p.y,
28696
+ width: p.width,
28697
+ height: p.height,
28698
+ collapsed: false,
28699
+ childCount: grp.children.length
28700
+ };
28701
+ }
28702
+ );
28703
+ for (const label of collapsedGroupLabels) {
28704
+ const p = g.node(gid(label));
28705
+ groups.push({
28706
+ label,
28707
+ lineNumber: 0,
28708
+ x: p.x,
28709
+ y: p.y,
28710
+ width: p.width,
28711
+ height: p.height,
28712
+ collapsed: true,
28713
+ childCount: collapseInfo?.collapsedChildCounts.get(label) ?? 0
28714
+ });
28715
+ }
28716
+ const edges = parsed.edges.filter((e) => g.hasEdge(e.source, e.target)).map((e) => {
28717
+ const ed = g.edge(e.source, e.target);
28718
+ return {
28719
+ source: e.source,
28720
+ target: e.target,
28721
+ ...e.label !== void 0 && { label: e.label },
28722
+ bidirectional: e.bidirectional,
28723
+ lineNumber: e.lineNumber,
28724
+ points: ed?.points ?? [],
28725
+ yOffset: 0,
28726
+ parallelCount: 1,
28727
+ metadata: e.metadata
28728
+ };
28729
+ });
28730
+ const gg = g.graph();
28731
+ return {
28732
+ nodes,
28733
+ edges,
28734
+ groups,
28735
+ width: gg.width ?? 800,
28736
+ height: gg.height ?? 600
28737
+ };
28738
+ }
28739
+ const n = parsed.nodes.length;
28740
+ const seedCount = opts?.seeds ?? (n <= 12 ? 80 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
28741
+ const REFINE_K = opts?.refineK ?? 6;
28742
+ const lambda = opts?.lambda ?? DEFAULT_LAMBDA;
28743
+ const prev = opts?.previousPositions;
28744
+ const RANKERS = ["network-simplex", "tight-tree", "longest-path"];
28745
+ const SPACINGS = [
28746
+ { nodesep: 50, ranksep: 60 },
28747
+ { nodesep: 34, ranksep: 46 },
28748
+ { nodesep: 66, ranksep: 82 }
28749
+ ];
28750
+ const configs = [];
28751
+ for (const ranker of RANKERS)
28752
+ for (const sp of SPACINGS) configs.push({ ranker, ...sp });
28753
+ for (let s = 0; s < seedCount; s++)
28754
+ configs.push({
28755
+ ranker: "network-simplex",
28756
+ nodesep: 50,
28757
+ ranksep: 60,
28758
+ seed: s
28759
+ });
28760
+ const badness = (lay, floor) => {
28761
+ const x = countSplineCrossings(lay);
28762
+ if (x > floor) return Infinity;
28763
+ return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
28764
+ };
28765
+ const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
28766
+ const pool = [];
28767
+ for (const cfg of configs) {
28768
+ try {
28769
+ pool.push(place(cfg));
28770
+ } catch {
28771
+ }
28772
+ }
28773
+ if (!pool.length)
28774
+ return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
28775
+ let layered = [];
28776
+ try {
28777
+ layered = layeredCandidates(parsed, sizes);
28778
+ } catch {
28779
+ }
28780
+ pool.sort(
28781
+ (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
28782
+ );
28783
+ const refineK = Math.min(REFINE_K, pool.length);
28784
+ let best = pool[0];
28785
+ let bestObj = Infinity;
28786
+ let bestBad = Infinity;
28787
+ const consider = (lay) => {
28788
+ const bad = badness(lay, bestBad);
28789
+ if (bad === Infinity) return;
28790
+ const sc = objective(lay, bad);
28791
+ if (sc < bestObj) {
28792
+ bestObj = sc;
28793
+ bestBad = bad;
28794
+ best = lay;
28795
+ }
28796
+ };
28797
+ for (const lay of pool.slice(0, refineK)) consider(lay);
28798
+ if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
28799
+ const extra = [];
28800
+ for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
28801
+ try {
28802
+ extra.push(
28803
+ place({
28804
+ ranker: "network-simplex",
28805
+ nodesep: 50,
28806
+ ranksep: 60,
28807
+ seed: s
28808
+ })
28809
+ );
28810
+ } catch {
28811
+ }
28812
+ }
28813
+ extra.sort(
28814
+ (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
28815
+ );
28816
+ for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
28817
+ }
28818
+ for (const lay of layered) {
28819
+ const variants = [lay];
28820
+ const dp = deroutePierces(lay);
28821
+ if (dp !== lay) variants.push(dp);
28822
+ for (const v of variants) {
28823
+ const bad = badness(v, bestBad - 1);
28824
+ if (bad < bestBad) {
28825
+ bestBad = bad;
28826
+ best = v;
28827
+ }
28828
+ }
28829
+ }
28830
+ if (bestBad > 0) {
28831
+ const rerouted = deroutePierces(best);
28832
+ if (rerouted !== best && badness(rerouted, bestBad - 1) < bestBad)
28833
+ best = rerouted;
28834
+ }
28835
+ if (bestBad > 0 && countGroupOverlaps(best) > 0) {
28836
+ const separated = separateGroupBands(best, parsed);
28837
+ if (separated !== best && badness(separated, bestBad - 1) < bestBad)
28838
+ best = separated;
28839
+ }
28840
+ return best;
28841
+ }
28842
+ var import_dagre4, import_d3_shape, DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
28843
+ var init_layout_search = __esm({
28844
+ "src/boxes-and-lines/layout-search.ts"() {
28845
+ "use strict";
28846
+ import_dagre4 = __toESM(require("@dagrejs/dagre"), 1);
28847
+ import_d3_shape = require("d3-shape");
28848
+ init_layout5();
28849
+ init_layout_layered();
28850
+ DEFAULT_LAMBDA = 4;
28851
+ ESCALATE_THRESHOLD = 4;
28852
+ ESCALATE_MAX_N = 45;
28853
+ ESCALATE_SEEDS = 18;
28854
+ ESCALATE_REFINE = 10;
28855
+ splineGen = (0, import_d3_shape.line)().x((d) => d.x).y((d) => d.y).curve(import_d3_shape.curveBasis);
28856
+ GROUP_LABEL_ZONE2 = 32;
28857
+ }
28858
+ });
28859
+
27530
28860
  // src/boxes-and-lines/layout.ts
27531
28861
  var layout_exports5 = {};
27532
28862
  __export(layout_exports5, {
28863
+ NODE_HEIGHT: () => NODE_HEIGHT,
28864
+ NODE_WIDTH: () => NODE_WIDTH,
28865
+ computeNodeSize: () => computeNodeSize,
27533
28866
  layoutBoxesAndLines: () => layoutBoxesAndLines
27534
28867
  });
27535
28868
  function splitCamelCase2(word) {
@@ -27602,417 +28935,21 @@ function computeNodeSize(node, reserveValueRow) {
27602
28935
  const totalHeight = labelHeight + SEPARATOR_GAP5 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
27603
28936
  return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
27604
28937
  }
27605
- function getElk() {
27606
- if (!elkInstance) elkInstance = new import_elk_bundled.default();
27607
- return elkInstance;
27608
- }
27609
- function baseOptions() {
27610
- return {
27611
- "elk.algorithm": "layered",
27612
- // INCLUDE_CHILDREN lets ELK route edges across container boundaries.
27613
- "elk.hierarchyHandling": "INCLUDE_CHILDREN",
27614
- "elk.edgeRouting": "ORTHOGONAL",
27615
- "elk.layered.unnecessaryBendpoints": "true",
27616
- // Let edges leave from top/bottom of nodes (not just the flow-direction
27617
- // sides) when it reduces crossings.
27618
- "elk.layered.allowNonFlowPortsToSwitchSides": "true"
27619
- };
27620
- }
27621
- function bkBaseline() {
27622
- return {
27623
- ...baseOptions(),
27624
- "elk.layered.nodePlacement.strategy": "BRANDES_KOEPF",
27625
- "elk.layered.nodePlacement.bk.fixedAlignment": "BALANCED",
27626
- "elk.layered.nodePlacement.bk.edgeStraightening": "IMPROVE_STRAIGHTNESS",
27627
- "elk.layered.compaction.connectedComponents": "true",
27628
- "elk.layered.spacing.nodeNodeBetweenLayers": "90",
27629
- "elk.spacing.nodeNode": "55",
27630
- "elk.spacing.edgeNode": "55",
27631
- "elk.spacing.edgeEdge": "18"
27632
- };
27633
- }
27634
- function getVariants() {
27635
- const bk = bkBaseline();
27636
- return [
27637
- {
27638
- name: "bk-baseline",
27639
- options: {
27640
- ...bk,
27641
- "elk.layered.crossingMinimization.greedySwitch.type": "ONE_SIDED"
27642
- }
27643
- },
27644
- {
27645
- name: "bk-aggressive",
27646
- options: {
27647
- ...bk,
27648
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27649
- "elk.layered.thoroughness": "50"
27650
- }
27651
- },
27652
- {
27653
- name: "bk-wide",
27654
- options: {
27655
- ...bk,
27656
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27657
- "elk.layered.thoroughness": "50",
27658
- "elk.spacing.nodeNode": "70",
27659
- "elk.spacing.edgeNode": "75",
27660
- "elk.spacing.edgeEdge": "22",
27661
- "elk.layered.spacing.nodeNodeBetweenLayers": "120"
27662
- }
27663
- },
27664
- {
27665
- name: "longest-path",
27666
- options: {
27667
- ...bk,
27668
- "elk.layered.layering.strategy": "LONGEST_PATH",
27669
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27670
- "elk.layered.thoroughness": "50"
27671
- }
27672
- },
27673
- {
27674
- name: "bounded-width",
27675
- options: {
27676
- ...bk,
27677
- "elk.layered.layering.strategy": "COFFMAN_GRAHAM",
27678
- "elk.layered.layering.coffmanGraham.layerBound": "3",
27679
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27680
- "elk.layered.thoroughness": "50"
27681
- }
27682
- }
27683
- ];
27684
- }
27685
- function countCrossings(edges) {
27686
- let count = 0;
27687
- for (let i = 0; i < edges.length; i++) {
27688
- const edgeI = edges[i];
27689
- const a = edgeI.points;
27690
- if (a.length < 2) continue;
27691
- for (let j = i + 1; j < edges.length; j++) {
27692
- const edgeJ = edges[j];
27693
- const b = edgeJ.points;
27694
- if (b.length < 2) continue;
27695
- if (edgeI.source === edgeJ.source) continue;
27696
- if (edgeI.source === edgeJ.target) continue;
27697
- if (edgeI.target === edgeJ.source) continue;
27698
- if (edgeI.target === edgeJ.target) continue;
27699
- for (let ai = 0; ai < a.length - 1; ai++) {
27700
- for (let bi = 0; bi < b.length - 1; bi++) {
27701
- if (segmentsCross(a[ai], a[ai + 1], b[bi], b[bi + 1])) count++;
27702
- }
27703
- }
27704
- }
27705
- }
27706
- return count;
27707
- }
27708
- function segmentsCross(p1, p2, p3, p4) {
27709
- const d1x = p2.x - p1.x;
27710
- const d1y = p2.y - p1.y;
27711
- const d2x = p4.x - p3.x;
27712
- const d2y = p4.y - p3.y;
27713
- const denom = d1x * d2y - d1y * d2x;
27714
- if (Math.abs(denom) < 1e-9) return false;
27715
- const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / denom;
27716
- const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / denom;
27717
- const EPS = 1e-3;
27718
- return t > EPS && t < 1 - EPS && s > EPS && s < 1 - EPS;
27719
- }
27720
- function countTotalBends(edges) {
27721
- let bends = 0;
27722
- for (const e of edges) bends += Math.max(0, e.points.length - 2);
27723
- return bends;
27724
- }
27725
- function scoreLayout(layout) {
27726
- return {
27727
- crossings: countCrossings(layout.edges),
27728
- bends: countTotalBends(layout.edges),
27729
- area: layout.width * layout.height
27730
- };
27731
- }
27732
- function cmpScore(a, b) {
27733
- const aBucket = a.crossings <= CROSSINGS_FORGIVENESS ? 0 : a.crossings;
27734
- const bBucket = b.crossings <= CROSSINGS_FORGIVENESS ? 0 : b.crossings;
27735
- if (aBucket !== bBucket) return aBucket - bBucket;
27736
- if (a.area !== b.area) return a.area - b.area;
27737
- return a.bends - b.bends;
27738
- }
27739
28938
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
27740
- const hideDescriptions = layoutOptions?.hideDescriptions ?? false;
27741
- const direction = parsed.direction === "TB" ? "DOWN" : "RIGHT";
27742
- const collapsedGroupLabels = /* @__PURE__ */ new Set();
27743
- if (collapseInfo) {
27744
- const missingGroups = /* @__PURE__ */ new Set();
27745
- for (const og of collapseInfo.originalGroups) {
27746
- if (!parsed.groups.some((g) => g.label === og.label)) {
27747
- missingGroups.add(og.label);
27748
- }
27749
- }
27750
- for (const label of missingGroups) {
27751
- const og = collapseInfo.originalGroups.find((g) => g.label === label);
27752
- const parentLabel = og?.parentGroup;
27753
- if (!parentLabel || !missingGroups.has(parentLabel)) {
27754
- collapsedGroupLabels.add(label);
27755
- }
27756
- }
27757
- }
27758
- const nodeSizes = /* @__PURE__ */ new Map();
27759
- let maxDescHeight = 0;
27760
- for (const node of parsed.nodes) {
27761
- const size = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
27762
- nodeSizes.set(node.label, size);
27763
- if (!hideDescriptions && node.description && node.description.length > 0) {
27764
- maxDescHeight = Math.max(maxDescHeight, size.height);
27765
- }
27766
- }
27767
- if (maxDescHeight > 0) {
27768
- for (const node of parsed.nodes) {
27769
- if (node.description && node.description.length > 0) {
27770
- const size = nodeSizes.get(node.label);
27771
- nodeSizes.set(node.label, { width: size.width, height: maxDescHeight });
27772
- }
27773
- }
27774
- }
27775
- const expandedGroupSet = new Set(parsed.groups.map((g) => g.label));
27776
- const gid = (label) => `__group_${label}`;
27777
- function buildGraph() {
27778
- const nodeById = /* @__PURE__ */ new Map();
27779
- const parentOf = /* @__PURE__ */ new Map();
27780
- for (const node of parsed.nodes) {
27781
- const size = nodeSizes.get(node.label);
27782
- nodeById.set(node.label, {
27783
- id: node.label,
27784
- width: size.width,
27785
- height: size.height,
27786
- labels: [{ text: node.label }]
27787
- });
27788
- }
27789
- for (const group of parsed.groups) {
27790
- nodeById.set(gid(group.label), {
27791
- id: gid(group.label),
27792
- labels: [{ text: group.label }],
27793
- layoutOptions: {
27794
- "elk.padding": `[top=${CONTAINER_PAD_TOP2},left=${CONTAINER_PAD_X3},bottom=${CONTAINER_PAD_BOTTOM3},right=${CONTAINER_PAD_X3}]`,
27795
- // Suggest square-ish containers — has limited effect with
27796
- // INCLUDE_CHILDREN but doesn't hurt.
27797
- "elk.aspectRatio": "1.4"
27798
- },
27799
- children: [],
27800
- edges: []
27801
- });
27802
- }
27803
- for (const label of collapsedGroupLabels) {
27804
- nodeById.set(gid(label), {
27805
- id: gid(label),
27806
- width: NODE_WIDTH,
27807
- height: NODE_HEIGHT,
27808
- labels: [{ text: label }]
27809
- });
27810
- }
27811
- for (const group of parsed.groups) {
27812
- if (group.parentGroup && nodeById.has(gid(group.parentGroup))) {
27813
- parentOf.set(gid(group.label), gid(group.parentGroup));
27814
- }
27815
- }
27816
- if (collapseInfo) {
27817
- for (const label of collapsedGroupLabels) {
27818
- const og = collapseInfo.originalGroups.find((g) => g.label === label);
27819
- if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && nodeById.has(gid(og.parentGroup))) {
27820
- parentOf.set(gid(label), gid(og.parentGroup));
27821
- }
27822
- }
27823
- }
27824
- for (const group of parsed.groups) {
27825
- for (const child of group.children) {
27826
- if (expandedGroupSet.has(child)) continue;
27827
- if (nodeById.has(child)) {
27828
- parentOf.set(child, gid(group.label));
27829
- }
27830
- }
27831
- }
27832
- const roots = [];
27833
- for (const [id, node] of nodeById) {
27834
- const parentId = parentOf.get(id);
27835
- if (parentId) {
27836
- const parent = nodeById.get(parentId);
27837
- parent.children = parent.children ?? [];
27838
- parent.children.push(node);
27839
- } else {
27840
- roots.push(node);
27841
- }
27842
- }
27843
- const rootEdges = [];
27844
- for (let i = 0; i < parsed.edges.length; i++) {
27845
- const edge = parsed.edges[i];
27846
- if (!nodeById.has(edge.source) || !nodeById.has(edge.target)) continue;
27847
- rootEdges.push({
27848
- id: `e${i}`,
27849
- sources: [edge.source],
27850
- targets: [edge.target]
27851
- });
27852
- }
27853
- return { roots, rootEdges };
27854
- }
27855
- async function runVariant(variant) {
27856
- const { roots, rootEdges } = buildGraph();
27857
- const elkRoot = {
27858
- id: "root",
27859
- layoutOptions: {
27860
- ...variant.options,
27861
- "elk.direction": direction,
27862
- "elk.padding": `[top=${MARGIN3},left=${MARGIN3},bottom=${MARGIN3},right=${MARGIN3}]`
27863
- },
27864
- children: roots,
27865
- edges: rootEdges
27866
- };
27867
- const result = await getElk().layout(elkRoot);
27868
- return extractLayout(result);
27869
- }
27870
- function extractLayout(result) {
27871
- const layoutNodes = [];
27872
- const layoutGroups = [];
27873
- const allEdges = [];
27874
- const containerAbs = /* @__PURE__ */ new Map();
27875
- function walk(n, offsetX, offsetY, isRoot) {
27876
- const nx = (n.x ?? 0) + offsetX;
27877
- const ny = (n.y ?? 0) + offsetY;
27878
- const nw = n.width ?? 0;
27879
- const nh = n.height ?? 0;
27880
- if (isRoot) {
27881
- containerAbs.set("root", { x: nx, y: ny });
27882
- } else {
27883
- const isGroup = n.id.startsWith("__group_");
27884
- if (isGroup) {
27885
- const label = n.id.slice("__group_".length);
27886
- const collapsed = collapsedGroupLabels.has(label);
27887
- const og = collapseInfo?.originalGroups.find(
27888
- (g) => g.label === label
27889
- );
27890
- const pg = parsed.groups.find((g) => g.label === label);
27891
- const childCount = collapsed ? collapseInfo?.collapsedChildCounts.get(label) ?? 0 : void 0;
27892
- layoutGroups.push({
27893
- label,
27894
- lineNumber: pg?.lineNumber ?? og?.lineNumber ?? 0,
27895
- x: nx + nw / 2,
27896
- y: ny + nh / 2,
27897
- width: nw,
27898
- height: nh,
27899
- collapsed,
27900
- ...childCount !== void 0 && { childCount }
27901
- });
27902
- if (!collapsed) containerAbs.set(n.id, { x: nx, y: ny });
27903
- } else {
27904
- layoutNodes.push({
27905
- label: n.id,
27906
- x: nx + nw / 2,
27907
- y: ny + nh / 2,
27908
- width: nw,
27909
- height: nh
27910
- });
27911
- }
27912
- }
27913
- if (n.edges) for (const e of n.edges) allEdges.push(e);
27914
- if (n.children) for (const c of n.children) walk(c, nx, ny, false);
27915
- }
27916
- walk(result, 0, 0, true);
27917
- const edgeYOffsets = new Array(parsed.edges.length).fill(0);
27918
- const edgeParallelCounts = new Array(parsed.edges.length).fill(1);
27919
- const parallelGroups = /* @__PURE__ */ new Map();
27920
- for (let i = 0; i < parsed.edges.length; i++) {
27921
- const edge = parsed.edges[i];
27922
- const [a, b] = edge.source < edge.target ? [edge.source, edge.target] : [edge.target, edge.source];
27923
- const key = `${a}\0${b}`;
27924
- if (!parallelGroups.has(key)) parallelGroups.set(key, []);
27925
- parallelGroups.get(key).push(i);
27926
- }
27927
- for (const group of parallelGroups.values()) {
27928
- const capped = group.slice(0, MAX_PARALLEL_EDGES);
27929
- for (const idx of group.slice(MAX_PARALLEL_EDGES)) {
27930
- edgeParallelCounts[idx] = 0;
27931
- }
27932
- if (capped.length < 2) continue;
27933
- for (let j = 0; j < capped.length; j++) {
27934
- const cappedJ = capped[j];
27935
- edgeYOffsets[cappedJ] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
27936
- edgeParallelCounts[cappedJ] = capped.length;
27937
- }
27938
- }
27939
- const edgeById = /* @__PURE__ */ new Map();
27940
- for (const e of allEdges) edgeById.set(e.id, e);
27941
- const layoutEdges = [];
27942
- for (let i = 0; i < parsed.edges.length; i++) {
27943
- const edge = parsed.edges[i];
27944
- if (edgeParallelCounts[i] === 0) continue;
27945
- const elkEdge = edgeById.get(`e${i}`);
27946
- if (!elkEdge?.sections || elkEdge.sections.length === 0) continue;
27947
- const container = elkEdge.container ?? "root";
27948
- const off = containerAbs.get(container) ?? { x: 0, y: 0 };
27949
- const s = elkEdge.sections[0];
27950
- const points = [
27951
- { x: s.startPoint.x + off.x, y: s.startPoint.y + off.y },
27952
- ...(s.bendPoints ?? []).map((p) => ({
27953
- x: p.x + off.x,
27954
- y: p.y + off.y
27955
- })),
27956
- { x: s.endPoint.x + off.x, y: s.endPoint.y + off.y }
27957
- ];
27958
- let labelX;
27959
- let labelY;
27960
- if (edge.label && points.length >= 2) {
27961
- const mid = Math.floor(points.length / 2);
27962
- const midPoint = points[mid];
27963
- labelX = midPoint.x;
27964
- labelY = midPoint.y - 10;
27965
- }
27966
- layoutEdges.push({
27967
- source: edge.source,
27968
- target: edge.target,
27969
- ...edge.label !== void 0 && { label: edge.label },
27970
- bidirectional: edge.bidirectional,
27971
- lineNumber: edge.lineNumber,
27972
- points,
27973
- ...labelX !== void 0 && { labelX },
27974
- ...labelY !== void 0 && { labelY },
27975
- // In-bounds — i < parsed.edges.length, arrays sized to that length.
27976
- yOffset: edgeYOffsets[i],
27977
- parallelCount: edgeParallelCounts[i],
27978
- metadata: edge.metadata,
27979
- deferred: true
27980
- });
27981
- }
27982
- let maxX = 0;
27983
- let maxY = 0;
27984
- for (const node of layoutNodes) {
27985
- maxX = Math.max(maxX, node.x + node.width / 2);
27986
- maxY = Math.max(maxY, node.y + node.height / 2);
27987
- }
27988
- for (const group of layoutGroups) {
27989
- maxX = Math.max(maxX, group.x + group.width / 2);
27990
- maxY = Math.max(maxY, group.y + group.height / 2);
28939
+ const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
28940
+ const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
28941
+ ...layoutOptions?.hideDescriptions !== void 0 && {
28942
+ hideDescriptions: layoutOptions.hideDescriptions
28943
+ },
28944
+ ...layoutOptions?.previousPositions !== void 0 && {
28945
+ previousPositions: layoutOptions.previousPositions
27991
28946
  }
27992
- return {
27993
- nodes: layoutNodes,
27994
- edges: layoutEdges,
27995
- groups: layoutGroups,
27996
- width: maxX + MARGIN3,
27997
- height: maxY + MARGIN3
27998
- };
27999
- }
28000
- const N = parsed.nodes.length + parsed.groups.length;
28001
- const E = parsed.edges.length;
28002
- const trivial = N < 8 && E < 10;
28003
- const variants = trivial ? [getVariants()[1]] : getVariants();
28004
- const results = await Promise.all(variants.map((v) => runVariant(v)));
28005
- let best = results[0];
28006
- let bestScore = scoreLayout(best);
28007
- for (let i = 1; i < results.length; i++) {
28008
- const resultI = results[i];
28009
- const s = scoreLayout(resultI);
28010
- if (cmpScore(s, bestScore) < 0) {
28011
- best = resultI;
28012
- bestScore = s;
28013
- }
28014
- }
28015
- return attachNotes(best, parsed, layoutOptions?.collapsedNotes);
28947
+ });
28948
+ return attachNotes(
28949
+ applyParallelEdgeOffsets(searched),
28950
+ parsed,
28951
+ layoutOptions?.collapsedNotes
28952
+ );
28016
28953
  }
28017
28954
  function attachNotes(layout, parsed, collapsedNotes) {
28018
28955
  const notesSuppressed = parsed.options?.["no-notes"] === "on";
@@ -28090,21 +29027,47 @@ function attachNotes(layout, parsed, collapsedNotes) {
28090
29027
  nodes: finalNodes,
28091
29028
  edges: finalEdges,
28092
29029
  groups: finalGroups,
28093
- width: bbMaxX + shiftX + MARGIN3,
28094
- height: bbMaxY + shiftY + MARGIN3
29030
+ width: bbMaxX + shiftX + MARGIN4,
29031
+ height: bbMaxY + shiftY + MARGIN4
29032
+ };
29033
+ }
29034
+ function applyParallelEdgeOffsets(layout) {
29035
+ const groups = /* @__PURE__ */ new Map();
29036
+ layout.edges.forEach((e, i) => {
29037
+ const [a, b] = e.source < e.target ? [e.source, e.target] : [e.target, e.source];
29038
+ const key = `${a}\0${b}`;
29039
+ const arr = groups.get(key);
29040
+ if (arr) arr.push(i);
29041
+ else groups.set(key, [i]);
29042
+ });
29043
+ if ([...groups.values()].every((g) => g.length < 2)) return layout;
29044
+ const yOffset = new Array(layout.edges.length).fill(0);
29045
+ const count = new Array(layout.edges.length).fill(1);
29046
+ for (const idxs of groups.values()) {
29047
+ const capped = idxs.slice(0, MAX_PARALLEL_EDGES);
29048
+ for (const drop of idxs.slice(MAX_PARALLEL_EDGES)) count[drop] = 0;
29049
+ if (capped.length < 2) continue;
29050
+ capped.forEach((idx, j) => {
29051
+ yOffset[idx] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
29052
+ count[idx] = capped.length;
29053
+ });
29054
+ }
29055
+ return {
29056
+ ...layout,
29057
+ edges: layout.edges.map((e, i) => ({
29058
+ ...e,
29059
+ yOffset: yOffset[i],
29060
+ parallelCount: count[i]
29061
+ }))
28095
29062
  };
28096
29063
  }
28097
- 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;
29064
+ 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;
28098
29065
  var init_layout5 = __esm({
28099
29066
  "src/boxes-and-lines/layout.ts"() {
28100
29067
  "use strict";
28101
- import_elk_bundled = __toESM(require("elkjs/lib/elk.bundled.js"), 1);
28102
29068
  init_text_measure();
28103
29069
  init_notes();
28104
- MARGIN3 = 40;
28105
- CONTAINER_PAD_X3 = 30;
28106
- CONTAINER_PAD_TOP2 = 40;
28107
- CONTAINER_PAD_BOTTOM3 = 24;
29070
+ MARGIN4 = 40;
28108
29071
  MAX_PARALLEL_EDGES = 5;
28109
29072
  PARALLEL_SPACING = 22;
28110
29073
  PHI = 1.618;
@@ -28121,8 +29084,6 @@ var init_layout5 = __esm({
28121
29084
  LABEL_PAD = 12;
28122
29085
  VALUE_ROW_FONT = 11;
28123
29086
  VALUE_ROW_H = SEPARATOR_GAP5 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
28124
- elkInstance = null;
28125
- CROSSINGS_FORGIVENESS = 1;
28126
29087
  }
28127
29088
  });
28128
29089
 
@@ -28277,7 +29238,7 @@ function layoutMindmap(parsed, _palette, options) {
28277
29238
  leafWidth: ctx.structural(LEAF_WIDTH),
28278
29239
  hGap: ctx.aesthetic(H_GAP2),
28279
29240
  vGap: ctx.aesthetic(V_GAP2),
28280
- margin: ctx.aesthetic(MARGIN4),
29241
+ margin: ctx.aesthetic(MARGIN5),
28281
29242
  multiRootGap: ctx.aesthetic(MULTI_ROOT_GAP)
28282
29243
  };
28283
29244
  populateDepthCache(roots);
@@ -28655,7 +29616,7 @@ function populateDepthCache(roots) {
28655
29616
  };
28656
29617
  walk(roots, 0);
28657
29618
  }
28658
- 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;
29619
+ 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;
28659
29620
  var init_layout6 = __esm({
28660
29621
  "src/mindmap/layout.ts"() {
28661
29622
  "use strict";
@@ -28671,7 +29632,7 @@ var init_layout6 = __esm({
28671
29632
  NODE_V_PAD = 10;
28672
29633
  H_GAP2 = 40;
28673
29634
  V_GAP2 = 12;
28674
- MARGIN4 = 40;
29635
+ MARGIN5 = 40;
28675
29636
  MULTI_ROOT_GAP = 60;
28676
29637
  nodeDepthCache = /* @__PURE__ */ new Map();
28677
29638
  }
@@ -30370,7 +31331,7 @@ function layoutC4Context(parsed, activeTagGroup) {
30370
31331
  }
30371
31332
  const contextRels = rollUpContextRelationships(parsed);
30372
31333
  const spacing = computeAdaptiveSpacing(contextRels);
30373
- const g = new import_dagre4.default.graphlib.Graph();
31334
+ const g = new import_dagre5.default.graphlib.Graph();
30374
31335
  g.setGraph({
30375
31336
  rankdir: "TB",
30376
31337
  nodesep: spacing.nodesep,
@@ -30391,7 +31352,7 @@ function layoutC4Context(parsed, activeTagGroup) {
30391
31352
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
30392
31353
  }
30393
31354
  }
30394
- import_dagre4.default.layout(g);
31355
+ import_dagre5.default.layout(g);
30395
31356
  reduceCrossings(
30396
31357
  g,
30397
31358
  validRels.map((r) => ({ source: r.sourceName, target: r.targetName }))
@@ -30458,8 +31419,8 @@ function layoutC4Context(parsed, activeTagGroup) {
30458
31419
  }
30459
31420
  }
30460
31421
  if (nodes.length > 0) {
30461
- const shiftX = MARGIN5 - minX;
30462
- const shiftY = MARGIN5 - minY;
31422
+ const shiftX = MARGIN6 - minX;
31423
+ const shiftY = MARGIN6 - minY;
30463
31424
  for (const node of nodes) {
30464
31425
  node.x += shiftX;
30465
31426
  node.y += shiftY;
@@ -30471,12 +31432,12 @@ function layoutC4Context(parsed, activeTagGroup) {
30471
31432
  }
30472
31433
  }
30473
31434
  }
30474
- let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN5 * 2 : 0;
30475
- let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN5 * 2 : 0;
31435
+ let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN6 * 2 : 0;
31436
+ let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN6 * 2 : 0;
30476
31437
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
30477
31438
  if (legendGroups.length > 0) {
30478
- const legendY = totalHeight + MARGIN5;
30479
- let legendX = MARGIN5;
31439
+ const legendY = totalHeight + MARGIN6;
31440
+ let legendX = MARGIN6;
30480
31441
  for (const lg of legendGroups) {
30481
31442
  lg.x = legendX;
30482
31443
  lg.y = legendY;
@@ -30569,7 +31530,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30569
31530
  }
30570
31531
  }
30571
31532
  const hasGroups = elementToGroup.size > 0;
30572
- const g = hasGroups ? new import_dagre4.default.graphlib.Graph({ compound: true }) : new import_dagre4.default.graphlib.Graph();
31533
+ const g = hasGroups ? new import_dagre5.default.graphlib.Graph({ compound: true }) : new import_dagre5.default.graphlib.Graph();
30573
31534
  g.setDefaultEdgeLabel(() => ({}));
30574
31535
  if (hasGroups) {
30575
31536
  const seenGroups = /* @__PURE__ */ new Set();
@@ -30647,7 +31608,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30647
31608
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
30648
31609
  }
30649
31610
  }
30650
- import_dagre4.default.layout(g);
31611
+ import_dagre5.default.layout(g);
30651
31612
  const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
30652
31613
  reduceCrossings(
30653
31614
  g,
@@ -30808,8 +31769,8 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30808
31769
  if (pt.y > maxY) maxY = pt.y;
30809
31770
  }
30810
31771
  }
30811
- const shiftX = MARGIN5 - minX;
30812
- const shiftY = MARGIN5 - minY;
31772
+ const shiftX = MARGIN6 - minX;
31773
+ const shiftY = MARGIN6 - minY;
30813
31774
  for (const node of nodes) {
30814
31775
  node.x += shiftX;
30815
31776
  node.y += shiftY;
@@ -30826,12 +31787,12 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30826
31787
  pt.y += shiftY;
30827
31788
  }
30828
31789
  }
30829
- let totalWidth = maxX - minX + MARGIN5 * 2;
30830
- let totalHeight = maxY - minY + MARGIN5 * 2;
31790
+ let totalWidth = maxX - minX + MARGIN6 * 2;
31791
+ let totalHeight = maxY - minY + MARGIN6 * 2;
30831
31792
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
30832
31793
  if (legendGroups.length > 0) {
30833
- const legendY = totalHeight + MARGIN5;
30834
- let legendX = MARGIN5;
31794
+ const legendY = totalHeight + MARGIN6;
31795
+ let legendX = MARGIN6;
30835
31796
  for (const lg of legendGroups) {
30836
31797
  lg.x = legendX;
30837
31798
  lg.y = legendY;
@@ -30976,7 +31937,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
30976
31937
  }
30977
31938
  }
30978
31939
  const hasGroups = elementToGroup.size > 0;
30979
- const g = hasGroups ? new import_dagre4.default.graphlib.Graph({ compound: true }) : new import_dagre4.default.graphlib.Graph();
31940
+ const g = hasGroups ? new import_dagre5.default.graphlib.Graph({ compound: true }) : new import_dagre5.default.graphlib.Graph();
30980
31941
  g.setDefaultEdgeLabel(() => ({}));
30981
31942
  if (hasGroups) {
30982
31943
  const seenGroups = /* @__PURE__ */ new Set();
@@ -31060,7 +32021,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31060
32021
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
31061
32022
  }
31062
32023
  }
31063
- import_dagre4.default.layout(g);
32024
+ import_dagre5.default.layout(g);
31064
32025
  const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
31065
32026
  reduceCrossings(
31066
32027
  g,
@@ -31223,8 +32184,8 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31223
32184
  if (pt.y > maxY) maxY = pt.y;
31224
32185
  }
31225
32186
  }
31226
- const shiftX = MARGIN5 - minX;
31227
- const shiftY = MARGIN5 - minY;
32187
+ const shiftX = MARGIN6 - minX;
32188
+ const shiftY = MARGIN6 - minY;
31228
32189
  for (const node of nodes) {
31229
32190
  node.x += shiftX;
31230
32191
  node.y += shiftY;
@@ -31241,12 +32202,12 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31241
32202
  pt.y += shiftY;
31242
32203
  }
31243
32204
  }
31244
- let totalWidth = maxX - minX + MARGIN5 * 2;
31245
- let totalHeight = maxY - minY + MARGIN5 * 2;
32205
+ let totalWidth = maxX - minX + MARGIN6 * 2;
32206
+ let totalHeight = maxY - minY + MARGIN6 * 2;
31246
32207
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
31247
32208
  if (legendGroups.length > 0) {
31248
- const legendY = totalHeight + MARGIN5;
31249
- let legendX = MARGIN5;
32209
+ const legendY = totalHeight + MARGIN6;
32210
+ let legendX = MARGIN6;
31250
32211
  for (const lg of legendGroups) {
31251
32212
  lg.x = legendX;
31252
32213
  lg.y = legendY;
@@ -31352,7 +32313,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31352
32313
  for (const r of refEntries) {
31353
32314
  nameToElement.set(r.element.name, r.element);
31354
32315
  }
31355
- const g = new import_dagre4.default.graphlib.Graph({ compound: true });
32316
+ const g = new import_dagre5.default.graphlib.Graph({ compound: true });
31356
32317
  g.setDefaultEdgeLabel(() => ({}));
31357
32318
  for (const [infraId] of infraIds) {
31358
32319
  g.setNode(infraId, {});
@@ -31396,7 +32357,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31396
32357
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
31397
32358
  }
31398
32359
  }
31399
- import_dagre4.default.layout(g);
32360
+ import_dagre5.default.layout(g);
31400
32361
  const nodeInfraMap = /* @__PURE__ */ new Map();
31401
32362
  for (const r of refEntries) nodeInfraMap.set(r.element.name, r.infraId);
31402
32363
  reduceCrossings(
@@ -31534,8 +32495,8 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31534
32495
  if (pt.y > maxY) maxY = pt.y;
31535
32496
  }
31536
32497
  }
31537
- const shiftX = MARGIN5 - minX;
31538
- const shiftY = MARGIN5 - minY;
32498
+ const shiftX = MARGIN6 - minX;
32499
+ const shiftY = MARGIN6 - minY;
31539
32500
  for (const node of nodes) {
31540
32501
  node.x += shiftX;
31541
32502
  node.y += shiftY;
@@ -31550,12 +32511,12 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31550
32511
  pt.y += shiftY;
31551
32512
  }
31552
32513
  }
31553
- let totalWidth = maxX - minX + MARGIN5 * 2;
31554
- let totalHeight = maxY - minY + MARGIN5 * 2;
32514
+ let totalWidth = maxX - minX + MARGIN6 * 2;
32515
+ let totalHeight = maxY - minY + MARGIN6 * 2;
31555
32516
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
31556
32517
  if (legendGroups.length > 0) {
31557
- const legendY = totalHeight + MARGIN5;
31558
- let legendX = MARGIN5;
32518
+ const legendY = totalHeight + MARGIN6;
32519
+ let legendX = MARGIN6;
31559
32520
  for (const lg of legendGroups) {
31560
32521
  lg.x = legendX;
31561
32522
  lg.y = legendY;
@@ -31575,11 +32536,11 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31575
32536
  height: totalHeight
31576
32537
  };
31577
32538
  }
31578
- 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;
32539
+ 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;
31579
32540
  var init_layout8 = __esm({
31580
32541
  "src/c4/layout.ts"() {
31581
32542
  "use strict";
31582
- import_dagre4 = __toESM(require("@dagrejs/dagre"), 1);
32543
+ import_dagre5 = __toESM(require("@dagrejs/dagre"), 1);
31583
32544
  init_legend_constants();
31584
32545
  init_text_measure();
31585
32546
  gNode = (g, name) => g.node(name);
@@ -31596,7 +32557,7 @@ var init_layout8 = __esm({
31596
32557
  CARD_H_PAD3 = 20;
31597
32558
  META_LINE_HEIGHT5 = 16;
31598
32559
  META_FONT_SIZE5 = 11;
31599
- MARGIN5 = 40;
32560
+ MARGIN6 = 40;
31600
32561
  BOUNDARY_PAD = 40;
31601
32562
  GROUP_BOUNDARY_PAD = 24;
31602
32563
  EDGE_NODE_COLLISION_WEIGHT = 5e3;
@@ -32664,7 +33625,7 @@ function layoutGraph(graph, options) {
32664
33625
  if (allNodes.length === 0) {
32665
33626
  return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
32666
33627
  }
32667
- const g = new import_dagre5.default.graphlib.Graph({ compound: true });
33628
+ const g = new import_dagre6.default.graphlib.Graph({ compound: true });
32668
33629
  g.setGraph({
32669
33630
  rankdir: graph.direction,
32670
33631
  nodesep: 50,
@@ -32715,7 +33676,7 @@ function layoutGraph(graph, options) {
32715
33676
  label: edge.label ?? ""
32716
33677
  });
32717
33678
  }
32718
- import_dagre5.default.layout(g);
33679
+ import_dagre6.default.layout(g);
32719
33680
  const collapsedGroupIds = collapsedChildCounts ? new Set(collapsedChildCounts.keys()) : /* @__PURE__ */ new Set();
32720
33681
  const basePositioned = allNodes.map((node) => {
32721
33682
  const pos = g.node(node.id);
@@ -32911,11 +33872,11 @@ function layoutGraph(graph, options) {
32911
33872
  height: totalHeight
32912
33873
  };
32913
33874
  }
32914
- var import_dagre5, GROUP_PADDING;
33875
+ var import_dagre6, GROUP_PADDING;
32915
33876
  var init_layout9 = __esm({
32916
33877
  "src/graph/layout.ts"() {
32917
33878
  "use strict";
32918
- import_dagre5 = __toESM(require("@dagrejs/dagre"), 1);
33879
+ import_dagre6 = __toESM(require("@dagrejs/dagre"), 1);
32919
33880
  init_notes2();
32920
33881
  init_note_box();
32921
33882
  init_notes();
@@ -34721,7 +35682,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
34721
35682
  };
34722
35683
  }
34723
35684
  const isLR = computed.direction !== "TB";
34724
- const g = new import_dagre6.default.graphlib.Graph();
35685
+ const g = new import_dagre7.default.graphlib.Graph();
34725
35686
  g.setGraph({
34726
35687
  rankdir: computed.direction === "TB" ? "TB" : "LR",
34727
35688
  nodesep: isLR ? 70 : 60,
@@ -34772,7 +35733,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
34772
35733
  g.setEdge(edge.sourceId, edge.targetId, { label: edge.label });
34773
35734
  }
34774
35735
  }
34775
- import_dagre6.default.layout(g);
35736
+ import_dagre7.default.layout(g);
34776
35737
  const layoutNodes = computed.nodes.map(
34777
35738
  (node) => {
34778
35739
  const pos = g.node(node.id);
@@ -34944,11 +35905,11 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
34944
35905
  height: totalHeight
34945
35906
  };
34946
35907
  }
34947
- 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;
35908
+ 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;
34948
35909
  var init_layout10 = __esm({
34949
35910
  "src/infra/layout.ts"() {
34950
35911
  "use strict";
34951
- import_dagre6 = __toESM(require("@dagrejs/dagre"), 1);
35912
+ import_dagre7 = __toESM(require("@dagrejs/dagre"), 1);
34952
35913
  init_text_measure();
34953
35914
  MIN_NODE_WIDTH2 = 140;
34954
35915
  NODE_HEADER_HEIGHT = 28;
@@ -36515,17 +37476,17 @@ function mulberry32(seed) {
36515
37476
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
36516
37477
  };
36517
37478
  }
36518
- function standardNormal(rng) {
37479
+ function standardNormal(rng3) {
36519
37480
  let u = 0;
36520
37481
  let v = 0;
36521
- while (u === 0) u = rng();
36522
- while (v === 0) v = rng();
37482
+ while (u === 0) u = rng3();
37483
+ while (v === 0) v = rng3();
36523
37484
  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
36524
37485
  }
36525
- function gamma(shape, rng) {
37486
+ function gamma(shape, rng3) {
36526
37487
  if (shape < 1) {
36527
- const u = rng();
36528
- return gamma(shape + 1, rng) * Math.pow(u, 1 / shape);
37488
+ const u = rng3();
37489
+ return gamma(shape + 1, rng3) * Math.pow(u, 1 / shape);
36529
37490
  }
36530
37491
  const d = shape - 1 / 3;
36531
37492
  const c = 1 / Math.sqrt(9 * d);
@@ -36533,29 +37494,29 @@ function gamma(shape, rng) {
36533
37494
  let x;
36534
37495
  let v;
36535
37496
  do {
36536
- x = standardNormal(rng);
37497
+ x = standardNormal(rng3);
36537
37498
  v = 1 + c * x;
36538
37499
  } while (v <= 0);
36539
37500
  v = v * v * v;
36540
- const u = rng();
37501
+ const u = rng3();
36541
37502
  if (u < 1 - 0.0331 * x * x * x * x) return d * v;
36542
37503
  if (Math.log(u) < x * x / 2 + d * (1 - v + Math.log(v))) return d * v;
36543
37504
  }
36544
37505
  }
36545
- function sampleBeta(alpha, beta, rng) {
36546
- const x = gamma(alpha, rng);
36547
- const y = gamma(beta, rng);
37506
+ function sampleBeta(alpha, beta, rng3) {
37507
+ const x = gamma(alpha, rng3);
37508
+ const y = gamma(beta, rng3);
36548
37509
  return x / (x + y);
36549
37510
  }
36550
- function sampleBetaPert(o, m, p, rng) {
37511
+ function sampleBetaPert(o, m, p, rng3) {
36551
37512
  if (o === p) return m;
36552
37513
  const range = p - o;
36553
37514
  const alpha = 1 + 4 * (m - o) / range;
36554
37515
  const beta = 1 + 4 * (p - m) / range;
36555
- return o + sampleBeta(alpha, beta, rng) * range;
37516
+ return o + sampleBeta(alpha, beta, rng3) * range;
36556
37517
  }
36557
37518
  function simulate(resolved, expanded, _predecessors, _successors, topo, terminals, poisoned, opts) {
36558
- const rng = mulberry32(opts.seed);
37519
+ const rng3 = mulberry32(opts.seed);
36559
37520
  const expById = /* @__PURE__ */ new Map();
36560
37521
  for (const e of expanded) expById.set(e.id, e);
36561
37522
  const completionTimes = new Array(opts.trials);
@@ -36578,7 +37539,7 @@ function simulate(resolved, expanded, _predecessors, _successors, topo, terminal
36578
37539
  } else if (exp.o === exp.p) {
36579
37540
  duration = exp.m;
36580
37541
  } else {
36581
- duration = sampleBetaPert(exp.o, exp.m, exp.p, rng);
37542
+ duration = sampleBetaPert(exp.o, exp.m, exp.p, rng3);
36582
37543
  }
36583
37544
  let esLower = 0;
36584
37545
  let efLower = -Infinity;
@@ -37684,7 +38645,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
37684
38645
  }
37685
38646
  }
37686
38647
  const dagreId = (id) => memberToGroup.get(id) ?? id;
37687
- const g = new import_dagre7.default.graphlib.Graph();
38648
+ const g = new import_dagre8.default.graphlib.Graph();
37688
38649
  g.setGraph({
37689
38650
  rankdir: resolved.options.direction,
37690
38651
  nodesep: 50,
@@ -37723,7 +38684,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
37723
38684
  seenEdges.add(k);
37724
38685
  g.setEdge(src, tgt, {});
37725
38686
  }
37726
- import_dagre7.default.layout(g);
38687
+ import_dagre8.default.layout(g);
37727
38688
  const swimApplied = applySwimLanes(
37728
38689
  g,
37729
38690
  resolved,
@@ -38069,7 +39030,7 @@ function reduceCrossings2(g, direction) {
38069
39030
  buckets.get(key).push(id);
38070
39031
  }
38071
39032
  const edges = g.edges().map((e) => ({ v: e.v, w: e.w }));
38072
- const countCrossings2 = () => {
39033
+ const countCrossings = () => {
38073
39034
  let total = 0;
38074
39035
  for (let i = 0; i < edges.length; i++) {
38075
39036
  const a = edges[i];
@@ -38082,13 +39043,13 @@ function reduceCrossings2(g, direction) {
38082
39043
  const b1 = g.node(b.v);
38083
39044
  const b2 = g.node(b.w);
38084
39045
  if (!b1 || !b2) continue;
38085
- if (segmentsCross2(a1, a2, b1, b2)) total++;
39046
+ if (segmentsCross(a1, a2, b1, b2)) total++;
38086
39047
  }
38087
39048
  }
38088
39049
  return total;
38089
39050
  };
38090
39051
  const MAX_ITER = 8;
38091
- let baseline = countCrossings2();
39052
+ let baseline = countCrossings();
38092
39053
  if (baseline === 0) return;
38093
39054
  for (let iter = 0; iter < MAX_ITER; iter++) {
38094
39055
  let improved = false;
@@ -38106,7 +39067,7 @@ function reduceCrossings2(g, direction) {
38106
39067
  const bv = bn[slotAxis];
38107
39068
  an[slotAxis] = bv;
38108
39069
  bn[slotAxis] = av;
38109
- const after = countCrossings2();
39070
+ const after = countCrossings();
38110
39071
  if (after < baseline) {
38111
39072
  baseline = after;
38112
39073
  improved = true;
@@ -38127,7 +39088,7 @@ function reduceCrossings2(g, direction) {
38127
39088
  data.points = smoothEdge(src, tgt, direction);
38128
39089
  }
38129
39090
  }
38130
- function segmentsCross2(a1, a2, b1, b2) {
39091
+ function segmentsCross(a1, a2, b1, b2) {
38131
39092
  const ccw = (p, q, r) => (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x);
38132
39093
  const d1 = ccw(b1, b2, a1);
38133
39094
  const d2 = ccw(b1, b2, a2);
@@ -38135,11 +39096,11 @@ function segmentsCross2(a1, a2, b1, b2) {
38135
39096
  const d4 = ccw(a1, a2, b2);
38136
39097
  return (d1 > 0 && d2 < 0 || d1 < 0 && d2 > 0) && (d32 > 0 && d4 < 0 || d32 < 0 && d4 > 0);
38137
39098
  }
38138
- 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;
39099
+ 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;
38139
39100
  var init_layout11 = __esm({
38140
39101
  "src/pert/layout.ts"() {
38141
39102
  "use strict";
38142
- import_dagre7 = __toESM(require("@dagrejs/dagre"), 1);
39103
+ import_dagre8 = __toESM(require("@dagrejs/dagre"), 1);
38143
39104
  init_internal();
38144
39105
  init_text_measure();
38145
39106
  DEFAULT_NODE_HEIGHT = 90;
@@ -47669,10 +48630,10 @@ function tierBand(maxSpanDeg) {
47669
48630
  }
47670
48631
  function labelBudget(width, height, band) {
47671
48632
  const bandCap = {
47672
- world: 6,
47673
- continental: 5,
47674
- regional: 4,
47675
- local: 3
48633
+ world: 7,
48634
+ continental: 6,
48635
+ regional: 5,
48636
+ local: 4
47676
48637
  };
47677
48638
  const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
47678
48639
  return Math.max(0, Math.min(area2, bandCap[band]));
@@ -47805,8 +48766,11 @@ function placeContextLabels(args) {
47805
48766
  color: waterColor,
47806
48767
  fontSize: FONT,
47807
48768
  // water names keep the base font (no footprint to scale on)
47808
- // Water before any country (×1000), then by tier, then kind, then name.
47809
- sort: tier * 10 + KIND_ORDER[kind]
48769
+ // Orientation-value bands (lower = earlier): MAJOR water (oceans + major
48770
+ // seas, tier ≤ 1) leads at `tier*10+kind` (0..~16); MINOR water (tier ≥ 2:
48771
+ // smaller seas, bays, gulfs, straits) drops to band 2 (2000+) — BELOW the
48772
+ // country band (1000+), so a big country outranks a minor basin.
48773
+ sort: (tier <= 1 ? 0 : 2e3) + tier * 10 + KIND_ORDER[kind]
47810
48774
  });
47811
48775
  }
47812
48776
  const ranked = countries.map((c) => {
@@ -47819,7 +48783,7 @@ function placeContextLabels(args) {
47819
48783
  let ci = 0;
47820
48784
  for (const r of ranked) {
47821
48785
  const { c, w, h, area: area2 } = r;
47822
- if (w > width * 0.66 || h > height * 0.66) continue;
48786
+ if (!c.curatedAnchor && w > width * 0.66 && h > height * 0.66) continue;
47823
48787
  if (!insideViewport(c.anchor, width, height)) continue;
47824
48788
  const sizeFrac = Math.sqrt(area2) / canvasLinear;
47825
48789
  const t = Math.min(
@@ -47844,15 +48808,23 @@ function placeContextLabels(args) {
47844
48808
  letterSpacing: 0,
47845
48809
  color,
47846
48810
  fontSize,
47847
- // Always after every water body (+1e6); larger area = earlier.
47848
- sort: 1e6 + ci++
48811
+ // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
48812
+ // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
48813
+ // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
48814
+ // Campeche) yet never displaces an ocean. Larger area = earlier within the
48815
+ // band (`ci` is the area-desc rank index).
48816
+ sort: 1e3 + ci++
47849
48817
  });
47850
48818
  }
47851
48819
  candidates.sort((a, b) => a.sort - b.sort);
47852
48820
  const placed = [];
47853
48821
  const placedRects = [];
48822
+ const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
48823
+ const waterCap = budget - Math.min(2, countryCount);
48824
+ let waterPlaced = 0;
47854
48825
  for (const cand of candidates) {
47855
48826
  if (placed.length >= budget) break;
48827
+ if (cand.italic && waterPlaced >= waterCap) continue;
47856
48828
  const rect = rectAround(
47857
48829
  cand.cx,
47858
48830
  cand.cy,
@@ -47879,6 +48851,7 @@ function placeContextLabels(args) {
47879
48851
  if (collides(rect)) continue;
47880
48852
  if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
47881
48853
  placedRects.push(rect);
48854
+ if (cand.italic) waterPlaced++;
47882
48855
  placed.push({
47883
48856
  x: cand.cx,
47884
48857
  y: cand.cy,
@@ -49841,16 +50814,16 @@ function layoutMap(resolved, data, size, opts) {
49841
50814
  countryCandidates.push({
49842
50815
  name: f.properties?.name ?? iso,
49843
50816
  bbox: [x0, y0, x1, y1],
49844
- anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
50817
+ anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
50818
+ curatedAnchor: !!anchorLngLat
49845
50819
  });
49846
50820
  }
49847
50821
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
49848
50822
  (id) => id.startsWith("US-")
49849
50823
  );
49850
50824
  if (usLayer && framedStateContainers) {
49851
- const containerSet = new Set(resolved.poiFrameContainers);
49852
50825
  for (const [iso, f] of usLayer) {
49853
- if (containerSet.has(iso) || regionById.has(iso)) continue;
50826
+ if (regionById.has(iso)) continue;
49854
50827
  const viewF = cullFeatureToView(f);
49855
50828
  if (!viewF) continue;
49856
50829
  const b = path.bounds(viewF);
@@ -49980,8 +50953,15 @@ var init_layout15 = __esm({
49980
50953
  W_MAX = 8;
49981
50954
  FONT2 = 11;
49982
50955
  WORLD_LABEL_ANCHORS = {
49983
- US: [-98.5, 39.5]
50956
+ US: [-98.5, 39.5],
49984
50957
  // CONUS geographic centre (near Lebanon, Kansas)
50958
+ // Russia crosses the antimeridian (Chukotka at ~170°W), so on a non-global
50959
+ // (e.g. Europe) projection its geometry smears across the whole frame and the
50960
+ // area-weighted centroid lands mid-map (over Europe) — useless as a label
50961
+ // anchor. Pin it to European Russia (~Volga) so a Europe view labels visible
50962
+ // western Russia on its eastern margin; on a world view this still sits over
50963
+ // Russian land. (See the curated-anchor smear-gate bypass in context-labels.)
50964
+ RU: [45, 58]
49985
50965
  };
49986
50966
  MAX_CLUSTER_EXTENT_FACTOR = 0.18;
49987
50967
  MAX_COLUMN_ROWS = 7;
@@ -60706,7 +61686,7 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
60706
61686
 
60707
61687
  // src/auto/index.ts
60708
61688
  init_safe_href();
60709
- var VERSION = "0.27.0";
61689
+ var VERSION = "0.28.0";
60710
61690
  var DEFAULTS = {
60711
61691
  theme: "auto",
60712
61692
  palette: "slate",