@diagrammo/dgmo 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/advanced.cjs CHANGED
@@ -27666,9 +27666,1342 @@ var init_renderer6 = __esm({
27666
27666
  }
27667
27667
  });
27668
27668
 
27669
+ // src/boxes-and-lines/layout-layered.ts
27670
+ function rng(s) {
27671
+ return () => {
27672
+ s |= 0;
27673
+ s = s + 1831565813 | 0;
27674
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
27675
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
27676
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
27677
+ };
27678
+ }
27679
+ function layeredCandidates(parsed, sizes, opts) {
27680
+ if (parsed.groups.length > 0) return [];
27681
+ const nCount = parsed.nodes.length;
27682
+ if (nCount < 3 || nCount > 40) return [];
27683
+ const isTB = parsed.direction === "TB";
27684
+ const nodesep = opts?.nodesep ?? NODESEP;
27685
+ const ranksep = opts?.ranksep ?? RANKSEP;
27686
+ const backEdgeSide = opts?.backEdgeSide ?? "nearest";
27687
+ const restarts = opts?.restarts ?? (nCount <= 14 ? 28 : nCount <= 25 ? 14 : 6);
27688
+ const keepK = opts?.keepK ?? 3;
27689
+ const labels = parsed.nodes.map((n) => n.label);
27690
+ const labelSet = new Set(labels);
27691
+ const edges = parsed.edges.map((e, i) => ({ e, i })).filter(
27692
+ ({ e }) => labelSet.has(e.source) && labelSet.has(e.target) && e.source !== e.target
27693
+ );
27694
+ if (edges.length === 0) return [];
27695
+ const adj = /* @__PURE__ */ new Map();
27696
+ for (const l of labels) adj.set(l, []);
27697
+ for (const { e, i } of edges)
27698
+ adj.get(e.source).push({ to: e.target, idx: i });
27699
+ const reversed = /* @__PURE__ */ new Set();
27700
+ const state = /* @__PURE__ */ new Map();
27701
+ for (const l of labels) state.set(l, 0);
27702
+ const dfs = (u) => {
27703
+ state.set(u, 1);
27704
+ for (const { to, idx } of adj.get(u)) {
27705
+ const st = state.get(to);
27706
+ if (st === 1)
27707
+ reversed.add(idx);
27708
+ else if (st === 0) dfs(to);
27709
+ }
27710
+ state.set(u, 2);
27711
+ };
27712
+ for (const l of labels) if (state.get(l) === 0) dfs(l);
27713
+ const dag = edges.map(
27714
+ ({ 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 }
27715
+ );
27716
+ const outDag = /* @__PURE__ */ new Map();
27717
+ const inDeg = /* @__PURE__ */ new Map();
27718
+ for (const l of labels) {
27719
+ outDag.set(l, []);
27720
+ inDeg.set(l, 0);
27721
+ }
27722
+ for (const d of dag) {
27723
+ outDag.get(d.from).push(d.to);
27724
+ inDeg.set(d.to, (inDeg.get(d.to) ?? 0) + 1);
27725
+ }
27726
+ const rank = /* @__PURE__ */ new Map();
27727
+ for (const l of labels) rank.set(l, 0);
27728
+ const queue = labels.filter((l) => (inDeg.get(l) ?? 0) === 0);
27729
+ const indeg2 = new Map(inDeg);
27730
+ const topo = [];
27731
+ while (queue.length) {
27732
+ const u = queue.shift();
27733
+ topo.push(u);
27734
+ for (const v of outDag.get(u)) {
27735
+ rank.set(v, Math.max(rank.get(v), rank.get(u) + 1));
27736
+ indeg2.set(v, indeg2.get(v) - 1);
27737
+ if (indeg2.get(v) === 0) queue.push(v);
27738
+ }
27739
+ }
27740
+ if (topo.length !== labels.length) return [];
27741
+ const maxRank = Math.max(...labels.map((l) => rank.get(l)));
27742
+ const node = /* @__PURE__ */ new Map();
27743
+ for (const n of parsed.nodes) {
27744
+ const s = sizes.get(n.label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
27745
+ node.set(n.label, {
27746
+ id: n.label,
27747
+ dummy: false,
27748
+ rank: rank.get(n.label),
27749
+ cross: 0,
27750
+ thick: isTB ? s.width : s.height,
27751
+ depth: isTB ? s.height : s.width,
27752
+ realW: s.width,
27753
+ realH: s.height
27754
+ });
27755
+ }
27756
+ const segs = [];
27757
+ const chains = [];
27758
+ const backEdges = [];
27759
+ for (const d of dag) {
27760
+ if (d.rev) {
27761
+ backEdges.push({ edgeIdx: d.idx, src: d.to, tgt: d.from });
27762
+ continue;
27763
+ }
27764
+ const r0 = rank.get(d.from);
27765
+ const r1 = rank.get(d.to);
27766
+ const dagChain = [d.from];
27767
+ let prev = d.from;
27768
+ for (let r = r0 + 1; r < r1; r++) {
27769
+ const id = `__d${d.idx}_${r}`;
27770
+ node.set(id, {
27771
+ id,
27772
+ dummy: true,
27773
+ rank: r,
27774
+ cross: 0,
27775
+ thick: DUMMY_THICK,
27776
+ depth: 0,
27777
+ realW: 0,
27778
+ realH: 0
27779
+ });
27780
+ segs.push({ a: prev, b: id });
27781
+ dagChain.push(id);
27782
+ prev = id;
27783
+ }
27784
+ segs.push({ a: prev, b: d.to });
27785
+ dagChain.push(d.to);
27786
+ chains.push({
27787
+ edgeIdx: d.idx,
27788
+ chain: d.rev ? dagChain.slice().reverse() : dagChain
27789
+ });
27790
+ }
27791
+ const ranks = Array.from({ length: maxRank + 1 }, () => []);
27792
+ for (const n of node.values()) ranks[n.rank].push(n.id);
27793
+ const up = /* @__PURE__ */ new Map();
27794
+ const down = /* @__PURE__ */ new Map();
27795
+ for (const id of node.keys()) {
27796
+ up.set(id, []);
27797
+ down.set(id, []);
27798
+ }
27799
+ for (const s of segs) {
27800
+ down.get(s.a).push(s.b);
27801
+ up.get(s.b).push(s.a);
27802
+ }
27803
+ const orderOf = /* @__PURE__ */ new Map();
27804
+ const applyOrder = (ord) => {
27805
+ for (const id of node.keys()) orderOf.set(id, ord.get(id));
27806
+ };
27807
+ const gapCrossings = (r) => {
27808
+ const lower = ranks[r + 1];
27809
+ if (!lower) return 0;
27810
+ const es = [];
27811
+ for (const a of ranks[r]) {
27812
+ const pa = orderOf.get(a);
27813
+ for (const b of down.get(a)) es.push({ p: pa, q: orderOf.get(b) });
27814
+ }
27815
+ let c = 0;
27816
+ for (let i = 0; i < es.length; i++)
27817
+ for (let j = i + 1; j < es.length; j++) {
27818
+ const A = es[i], B = es[j];
27819
+ if ((A.p - B.p) * (A.q - B.q) < 0) c++;
27820
+ }
27821
+ return c;
27822
+ };
27823
+ const totalCrossings = () => {
27824
+ let c = 0;
27825
+ for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
27826
+ return c;
27827
+ };
27828
+ const median = (vals) => {
27829
+ if (vals.length === 0) return -1;
27830
+ vals.sort((x, y) => x - y);
27831
+ const m = Math.floor(vals.length / 2);
27832
+ if (vals.length % 2 === 1) return vals[m];
27833
+ if (vals.length === 2) return (vals[0] + vals[1]) / 2;
27834
+ const left = vals[m - 1] - vals[0];
27835
+ const right = vals[vals.length - 1] - vals[m];
27836
+ return left + right === 0 ? (vals[m - 1] + vals[m]) / 2 : (vals[m - 1] * right + vals[m] * left) / (left + right);
27837
+ };
27838
+ const wmedianRank = (r, useUp) => {
27839
+ const ids = ranks[r];
27840
+ const neigh = useUp ? up : down;
27841
+ const med = /* @__PURE__ */ new Map();
27842
+ for (const id of ids)
27843
+ med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
27844
+ const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
27845
+ const out = new Array(ids.length).fill(null);
27846
+ for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
27847
+ let mi = 0;
27848
+ for (let slot = 0; slot < out.length; slot++)
27849
+ if (out[slot] === null) out[slot] = movable[mi++];
27850
+ const final = out;
27851
+ final.forEach((id, i) => orderOf.set(id, i));
27852
+ ranks[r] = final;
27853
+ };
27854
+ const transpose = () => {
27855
+ let improved = true;
27856
+ let guard = 0;
27857
+ while (improved && guard++ < 12) {
27858
+ improved = false;
27859
+ for (let r = 0; r <= maxRank; r++) {
27860
+ const ids = ranks[r];
27861
+ for (let i = 0; i < ids.length - 1; i++) {
27862
+ const before = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
27863
+ const a = ids[i], b = ids[i + 1];
27864
+ ids[i] = b;
27865
+ ids[i + 1] = a;
27866
+ orderOf.set(b, i);
27867
+ orderOf.set(a, i + 1);
27868
+ const after = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
27869
+ if (after < before) {
27870
+ improved = true;
27871
+ } else {
27872
+ ids[i] = a;
27873
+ ids[i + 1] = b;
27874
+ orderOf.set(a, i);
27875
+ orderOf.set(b, i + 1);
27876
+ }
27877
+ }
27878
+ }
27879
+ }
27880
+ };
27881
+ const initOrder = (seed) => {
27882
+ const r = seed === 0 ? null : rng(seed);
27883
+ for (let rr = 0; rr <= maxRank; rr++) {
27884
+ const ids = ranks[rr].slice();
27885
+ if (r) {
27886
+ for (let i = ids.length - 1; i > 0; i--) {
27887
+ const j = Math.floor(r() * (i + 1));
27888
+ [ids[i], ids[j]] = [ids[j], ids[i]];
27889
+ }
27890
+ }
27891
+ ranks[rr] = ids;
27892
+ ids.forEach((id, i) => orderOf.set(id, i));
27893
+ }
27894
+ };
27895
+ const found = [];
27896
+ for (let s = 0; s < restarts; s++) {
27897
+ initOrder(s);
27898
+ let best = totalCrossings();
27899
+ let bestSnap = new Map(orderOf);
27900
+ for (let iter = 0; iter < 8; iter++) {
27901
+ const down1 = iter % 2 === 0;
27902
+ if (down1) for (let r = 1; r <= maxRank; r++) wmedianRank(r, true);
27903
+ else for (let r = maxRank - 1; r >= 0; r--) wmedianRank(r, false);
27904
+ transpose();
27905
+ const c = totalCrossings();
27906
+ if (c < best) {
27907
+ best = c;
27908
+ bestSnap = new Map(orderOf);
27909
+ }
27910
+ if (c === 0) break;
27911
+ }
27912
+ applyOrder(bestSnap);
27913
+ for (let r = 0; r <= maxRank; r++)
27914
+ ranks[r].sort((a, b) => orderOf.get(a) - orderOf.get(b));
27915
+ const sig = ranks.map((r) => r.join(",")).join("|");
27916
+ if (!found.some((f) => f.sig === sig))
27917
+ found.push({ sig, cross: best, order: new Map(bestSnap) });
27918
+ }
27919
+ found.sort((a, b) => a.cross - b.cross);
27920
+ const keep = found.slice(0, keepK);
27921
+ if (keep.length === 0) return [];
27922
+ const results = [];
27923
+ for (const cand of keep) {
27924
+ applyOrder(cand.order);
27925
+ const rk = Array.from({ length: maxRank + 1 }, () => []);
27926
+ for (const n of node.values()) rk[n.rank].push(n.id);
27927
+ for (let r = 0; r <= maxRank; r++)
27928
+ rk[r].sort((a, b) => cand.order.get(a) - cand.order.get(b));
27929
+ results.push(realize(rk));
27930
+ }
27931
+ return results;
27932
+ function realize(rk) {
27933
+ const bandDepth = rk.map(
27934
+ (ids) => Math.max(0, ...ids.map((id) => node.get(id).depth))
27935
+ );
27936
+ const bandCenter = [];
27937
+ let acc = MARGIN3;
27938
+ for (let r = 0; r <= maxRank; r++) {
27939
+ bandCenter[r] = acc + bandDepth[r] / 2;
27940
+ acc += bandDepth[r] + ranksep;
27941
+ }
27942
+ for (let r = 0; r <= maxRank; r++) {
27943
+ let x = MARGIN3;
27944
+ for (const id of rk[r]) {
27945
+ const n = node.get(id);
27946
+ n.cross = x + n.thick / 2;
27947
+ x += n.thick + nodesep;
27948
+ }
27949
+ }
27950
+ const ITER = 18;
27951
+ for (let it = 0; it < ITER; it++) {
27952
+ const downPass = it % 2 === 0;
27953
+ const order = downPass ? Array.from({ length: maxRank + 1 }, (_, i) => i) : Array.from({ length: maxRank + 1 }, (_, i) => maxRank - i);
27954
+ for (const r of order) {
27955
+ const ids = rk[r];
27956
+ const desired = ids.map((id) => {
27957
+ const n = node.get(id);
27958
+ let sw = 0, sx2 = 0;
27959
+ for (const nb of up.get(id)) {
27960
+ const w = weight(n, node.get(nb));
27961
+ sw += w;
27962
+ sx2 += w * node.get(nb).cross;
27963
+ }
27964
+ for (const nb of down.get(id)) {
27965
+ const w = weight(n, node.get(nb));
27966
+ sw += w;
27967
+ sx2 += w * node.get(nb).cross;
27968
+ }
27969
+ return sw > 0 ? sx2 / sw : n.cross;
27970
+ });
27971
+ placeWithSeparation(ids, desired);
27972
+ }
27973
+ }
27974
+ let minC = Infinity;
27975
+ for (const n of node.values()) minC = Math.min(minC, n.cross - n.thick / 2);
27976
+ const shift = MARGIN3 - minC;
27977
+ for (const n of node.values()) n.cross += shift;
27978
+ const coord = (n) => isTB ? { x: n.cross, y: bandCenter[n.rank] } : { x: bandCenter[n.rank], y: n.cross };
27979
+ const layoutNodes = parsed.nodes.map((pn) => {
27980
+ const n = node.get(pn.label);
27981
+ const c = coord(n);
27982
+ return {
27983
+ label: pn.label,
27984
+ x: c.x,
27985
+ y: c.y,
27986
+ width: n.realW,
27987
+ height: n.realH
27988
+ };
27989
+ });
27990
+ const chainById = /* @__PURE__ */ new Map();
27991
+ for (const ch of chains) chainById.set(ch.edgeIdx, ch.chain);
27992
+ let minCross = Infinity, maxCross = -Infinity;
27993
+ for (const n of node.values()) {
27994
+ if (n.dummy) continue;
27995
+ minCross = Math.min(minCross, n.cross - n.thick / 2);
27996
+ maxCross = Math.max(maxCross, n.cross + n.thick / 2);
27997
+ }
27998
+ const GAP = 34;
27999
+ const LANE = 28;
28000
+ const sideOf = (b) => {
28001
+ if (backEdgeSide === "left") return -1;
28002
+ if (backEdgeSide === "right") return 1;
28003
+ const d = node.get(b.src).cross - node.get(b.tgt).cross;
28004
+ return d >= 0 ? 1 : -1;
28005
+ };
28006
+ const backSorted = backEdges.map((b) => ({
28007
+ ...b,
28008
+ side: sideOf(b),
28009
+ span: Math.abs(node.get(b.src).rank - node.get(b.tgt).rank)
28010
+ })).sort((a, b) => a.side - b.side || a.span - b.span);
28011
+ const sideCounts = /* @__PURE__ */ new Map();
28012
+ for (const b of backSorted)
28013
+ sideCounts.set(b.side, (sideCounts.get(b.side) ?? 0) + 1);
28014
+ const laneCounter = /* @__PURE__ */ new Map();
28015
+ const backPoints = /* @__PURE__ */ new Map();
28016
+ const faceLim = (depth) => Math.max(0, depth / 2 - 6);
28017
+ const hubIndex = /* @__PURE__ */ new Map();
28018
+ const hubSize = /* @__PURE__ */ new Map();
28019
+ {
28020
+ const counter = /* @__PURE__ */ new Map();
28021
+ for (const b of backSorted) {
28022
+ const key = `${b.side}|${b.tgt}`;
28023
+ const idx = counter.get(key) ?? 0;
28024
+ counter.set(key, idx + 1);
28025
+ hubIndex.set(b.edgeIdx, idx);
28026
+ }
28027
+ for (const [key, v] of counter) hubSize.set(key, v);
28028
+ }
28029
+ for (const b of backSorted) {
28030
+ const s = b.side;
28031
+ const k = laneCounter.get(s) ?? 0;
28032
+ laneCounter.set(s, k + 1);
28033
+ const K = sideCounts.get(s);
28034
+ const src = node.get(b.src);
28035
+ const tgt = node.get(b.tgt);
28036
+ const laneC = s > 0 ? maxCross + GAP + k * LANE : minCross - GAP - k * LANE;
28037
+ const srcCtr = bandCenter[src.rank];
28038
+ const tgtCtr = bandCenter[tgt.rank];
28039
+ const frac = K > 1 ? (k + 1) / (K + 1) : 0;
28040
+ const srcR = srcCtr + frac * faceLim(src.depth);
28041
+ const tgtR = tgtCtr - frac * faceLim(tgt.depth);
28042
+ const m = (c, r) => isTB ? { x: c, y: r } : { x: r, y: c };
28043
+ const off = 10;
28044
+ const srcEdgeC = src.cross + s * (src.thick / 2 + off);
28045
+ const tgtEdgeC = tgt.cross + s * (tgt.thick / 2 + off);
28046
+ const hubK = hubIndex.get(b.edgeIdx);
28047
+ const hubN = hubSize.get(`${s}|${b.tgt}`);
28048
+ if (hubN > 1 && hubK > 0) {
28049
+ const tgtTop = tgtCtr - (tgt.depth / 2 + GAP + (hubK - 1) * LANE);
28050
+ const entryCross = tgt.cross + s * faceLim(tgt.thick) * (hubK / hubN);
28051
+ backPoints.set(b.edgeIdx, [
28052
+ m(src.cross, srcCtr),
28053
+ m(srcEdgeC, srcR),
28054
+ m(laneC, srcR),
28055
+ m(laneC, tgtTop),
28056
+ m(entryCross, tgtTop),
28057
+ m(tgt.cross, tgtCtr)
28058
+ ]);
28059
+ } else {
28060
+ backPoints.set(b.edgeIdx, [
28061
+ m(src.cross, srcCtr),
28062
+ m(srcEdgeC, srcR),
28063
+ m(laneC, srcR),
28064
+ m(laneC, tgtR),
28065
+ m(tgtEdgeC, tgtR),
28066
+ m(tgt.cross, tgtCtr)
28067
+ ]);
28068
+ }
28069
+ }
28070
+ const layoutEdges = [];
28071
+ for (let i = 0; i < parsed.edges.length; i++) {
28072
+ const e = parsed.edges[i];
28073
+ if (!labelSet.has(e.source) || !labelSet.has(e.target)) continue;
28074
+ let points;
28075
+ if (e.source === e.target) {
28076
+ const c = coord(node.get(e.source));
28077
+ points = [c, c];
28078
+ } else if (backPoints.has(i)) {
28079
+ points = backPoints.get(i);
28080
+ } else {
28081
+ const chain = chainById.get(i);
28082
+ if (!chain) continue;
28083
+ points = chain.map((id) => coord(node.get(id)));
28084
+ }
28085
+ layoutEdges.push({
28086
+ source: e.source,
28087
+ target: e.target,
28088
+ ...e.label !== void 0 && { label: e.label },
28089
+ bidirectional: e.bidirectional,
28090
+ lineNumber: e.lineNumber,
28091
+ points,
28092
+ yOffset: 0,
28093
+ parallelCount: 1,
28094
+ metadata: e.metadata
28095
+ });
28096
+ }
28097
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
28098
+ for (const n of layoutNodes) {
28099
+ minX = Math.min(minX, n.x - n.width / 2);
28100
+ minY = Math.min(minY, n.y - n.height / 2);
28101
+ maxX = Math.max(maxX, n.x + n.width / 2);
28102
+ maxY = Math.max(maxY, n.y + n.height / 2);
28103
+ }
28104
+ for (const e of layoutEdges)
28105
+ for (const p of e.points) {
28106
+ minX = Math.min(minX, p.x);
28107
+ minY = Math.min(minY, p.y);
28108
+ maxX = Math.max(maxX, p.x);
28109
+ maxY = Math.max(maxY, p.y);
28110
+ }
28111
+ const sx = MARGIN3 - minX;
28112
+ const sy = MARGIN3 - minY;
28113
+ const shifted = sx !== 0 || sy !== 0 ? {
28114
+ nodes: layoutNodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })),
28115
+ edges: layoutEdges.map((e) => ({
28116
+ ...e,
28117
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
28118
+ }))
28119
+ } : { nodes: layoutNodes, edges: layoutEdges };
28120
+ return {
28121
+ nodes: shifted.nodes,
28122
+ edges: shifted.edges,
28123
+ groups: [],
28124
+ width: maxX + sx + MARGIN3,
28125
+ height: maxY + sy + MARGIN3
28126
+ };
28127
+ }
28128
+ function weight(a, b) {
28129
+ if (a.dummy && b.dummy) return W_DUMMY_DUMMY;
28130
+ if (a.dummy || b.dummy) return W_REAL_DUMMY;
28131
+ return W_REAL_REAL;
28132
+ }
28133
+ function placeWithSeparation(ids, desired) {
28134
+ const n = ids.length;
28135
+ if (n === 0) return;
28136
+ const half = ids.map((id) => node.get(id).thick / 2);
28137
+ const off = [0];
28138
+ for (let i = 1; i < n; i++)
28139
+ off[i] = off[i - 1] + half[i - 1] + nodesep + half[i];
28140
+ const d = desired.map((v, i) => v - off[i]);
28141
+ const blocks = [];
28142
+ for (let i = 0; i < n; i++) {
28143
+ let b = { pos: d[i], count: 1, sumOffset: d[i], first: i };
28144
+ while (blocks.length && blocks[blocks.length - 1].pos >= b.pos) {
28145
+ const prev = blocks.pop();
28146
+ const count = prev.count + b.count;
28147
+ const sumOffset = prev.sumOffset + b.sumOffset;
28148
+ b = { pos: sumOffset / count, count, sumOffset, first: prev.first };
28149
+ }
28150
+ blocks.push(b);
28151
+ }
28152
+ const xs = new Array(n);
28153
+ for (const b of blocks)
28154
+ for (let k = 0; k < b.count; k++) xs[b.first + k] = b.pos;
28155
+ for (let i = 0; i < n; i++) node.get(ids[i]).cross = xs[i] + off[i];
28156
+ }
28157
+ }
28158
+ var NODESEP, RANKSEP, DUMMY_THICK, MARGIN3, W_REAL_REAL, W_REAL_DUMMY, W_DUMMY_DUMMY;
28159
+ var init_layout_layered = __esm({
28160
+ "src/boxes-and-lines/layout-layered.ts"() {
28161
+ "use strict";
28162
+ init_layout5();
28163
+ NODESEP = 50;
28164
+ RANKSEP = 60;
28165
+ DUMMY_THICK = 10;
28166
+ MARGIN3 = 40;
28167
+ W_REAL_REAL = 1;
28168
+ W_REAL_DUMMY = 2;
28169
+ W_DUMMY_DUMMY = 8;
28170
+ }
28171
+ });
28172
+
28173
+ // src/boxes-and-lines/layout-search.ts
28174
+ var layout_search_exports = {};
28175
+ __export(layout_search_exports, {
28176
+ countEdgeNearMiss: () => countEdgeNearMiss,
28177
+ countEdgeNodePierces: () => countEdgeNodePierces,
28178
+ countEdgeOverlaps: () => countEdgeOverlaps,
28179
+ countGroupOverlaps: () => countGroupOverlaps,
28180
+ countSplineCrossings: () => countSplineCrossings,
28181
+ deroutePierces: () => deroutePierces,
28182
+ detectEdgeNodePierces: () => detectEdgeNodePierces,
28183
+ detectEdgeOverlaps: () => detectEdgeOverlaps,
28184
+ detectGroupOverlaps: () => detectGroupOverlaps,
28185
+ layoutBoxesAndLinesSearch: () => layoutBoxesAndLinesSearch,
28186
+ separateGroupBands: () => separateGroupBands
28187
+ });
28188
+ function rng2(s) {
28189
+ return () => {
28190
+ s |= 0;
28191
+ s = s + 1831565813 | 0;
28192
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
28193
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
28194
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
28195
+ };
28196
+ }
28197
+ function shuffle(a, r) {
28198
+ const x = a.slice();
28199
+ for (let i = x.length - 1; i > 0; i--) {
28200
+ const j = Math.floor(r() * (i + 1));
28201
+ [x[i], x[j]] = [x[j], x[i]];
28202
+ }
28203
+ return x;
28204
+ }
28205
+ function flatten(d) {
28206
+ const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
28207
+ const pts = [];
28208
+ let i = 0, cx = 0, cy = 0, cmd = "";
28209
+ const num = () => parseFloat(toks[i++]);
28210
+ const samp = (p0, c1, c2, p1) => {
28211
+ for (let t = 0; t <= 1; t += 0.12) {
28212
+ const u = 1 - t;
28213
+ if (c2)
28214
+ pts.push({
28215
+ x: u * u * u * p0.x + 3 * u * u * t * c1.x + 3 * u * t * t * c2.x + t * t * t * p1.x,
28216
+ y: u * u * u * p0.y + 3 * u * u * t * c1.y + 3 * u * t * t * c2.y + t * t * t * p1.y
28217
+ });
28218
+ else
28219
+ pts.push({
28220
+ x: u * u * p0.x + 2 * u * t * c1.x + t * t * p1.x,
28221
+ y: u * u * p0.y + 2 * u * t * c1.y + t * t * p1.y
28222
+ });
28223
+ }
28224
+ };
28225
+ while (i < toks.length) {
28226
+ const tk = toks[i];
28227
+ if (/[MLQC]/i.test(tk)) {
28228
+ cmd = tk;
28229
+ i++;
28230
+ }
28231
+ if (cmd === "M" || cmd === "L") {
28232
+ const x = num(), y = num();
28233
+ pts.push({ x, y });
28234
+ cx = x;
28235
+ cy = y;
28236
+ } else if (cmd === "Q") {
28237
+ const c1 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
28238
+ samp({ x: cx, y: cy }, c1, null, p1);
28239
+ cx = p1.x;
28240
+ cy = p1.y;
28241
+ } else if (cmd === "C") {
28242
+ const c1 = { x: num(), y: num() }, c2 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
28243
+ samp({ x: cx, y: cy }, c1, c2, p1);
28244
+ cx = p1.x;
28245
+ cy = p1.y;
28246
+ } else i++;
28247
+ }
28248
+ return pts;
28249
+ }
28250
+ function segPoint(p1, p2, p3, p4) {
28251
+ const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
28252
+ if (Math.abs(den) < 1e-9) return null;
28253
+ 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;
28254
+ 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;
28255
+ }
28256
+ function countSplineCrossings(layout) {
28257
+ const center = /* @__PURE__ */ new Map();
28258
+ for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
28259
+ for (const g of layout.groups)
28260
+ if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
28261
+ const polys = layout.edges.map((e) => {
28262
+ const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
28263
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28264
+ for (const p of pts) {
28265
+ if (p.x < x0) x0 = p.x;
28266
+ if (p.x > x1) x1 = p.x;
28267
+ if (p.y < y0) y0 = p.y;
28268
+ if (p.y > y1) y1 = p.y;
28269
+ }
28270
+ return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
28271
+ });
28272
+ const R = 34;
28273
+ let total = 0;
28274
+ for (let a = 0; a < polys.length; a++)
28275
+ for (let b = a + 1; b < polys.length; b++) {
28276
+ const A = polys[a], B = polys[b];
28277
+ if (A.pts.length < 2 || B.pts.length < 2) continue;
28278
+ if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
28279
+ const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
28280
+ const hits = [];
28281
+ for (let i = 1; i < A.pts.length; i++)
28282
+ for (let j = 1; j < B.pts.length; j++) {
28283
+ const p = segPoint(
28284
+ A.pts[i - 1],
28285
+ A.pts[i],
28286
+ B.pts[j - 1],
28287
+ B.pts[j]
28288
+ );
28289
+ if (!p) continue;
28290
+ if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
28291
+ continue;
28292
+ if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
28293
+ hits.push(p);
28294
+ }
28295
+ total += hits.length;
28296
+ }
28297
+ return total;
28298
+ }
28299
+ function pointSegDist(p, a, b) {
28300
+ const dx = b.x - a.x, dy = b.y - a.y;
28301
+ const len2 = dx * dx + dy * dy;
28302
+ if (len2 < 1e-9) return Math.hypot(p.x - a.x, p.y - a.y);
28303
+ let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
28304
+ t = Math.max(0, Math.min(1, t));
28305
+ return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));
28306
+ }
28307
+ function distToPoly(p, poly) {
28308
+ let m = Infinity;
28309
+ for (let i = 1; i < poly.length; i++)
28310
+ m = Math.min(m, pointSegDist(p, poly[i - 1], poly[i]));
28311
+ return m;
28312
+ }
28313
+ function pointRectDist(p, r) {
28314
+ const dx = Math.max(r.x - r.w / 2 - p.x, 0, p.x - (r.x + r.w / 2));
28315
+ const dy = Math.max(r.y - r.h / 2 - p.y, 0, p.y - (r.y + r.h / 2));
28316
+ return Math.hypot(dx, dy);
28317
+ }
28318
+ function detectEdgeOverlaps(layout, opts) {
28319
+ const dist = opts?.dist ?? 8;
28320
+ const minLen = opts?.minLen ?? 16;
28321
+ const nodeClear = opts?.nodeClear ?? 12;
28322
+ const rect = /* @__PURE__ */ new Map();
28323
+ for (const n of layout.nodes)
28324
+ rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
28325
+ for (const g of layout.groups)
28326
+ if (g.collapsed)
28327
+ rect.set("__group_" + g.label, {
28328
+ x: g.x,
28329
+ y: g.y,
28330
+ w: g.width,
28331
+ h: g.height
28332
+ });
28333
+ const polys = layout.edges.map((e) => {
28334
+ const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
28335
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28336
+ for (const p of pts) {
28337
+ if (p.x < x0) x0 = p.x;
28338
+ if (p.x > x1) x1 = p.x;
28339
+ if (p.y < y0) y0 = p.y;
28340
+ if (p.y > y1) y1 = p.y;
28341
+ }
28342
+ return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
28343
+ });
28344
+ const runs = [];
28345
+ for (let a = 0; a < polys.length; a++)
28346
+ for (let b = a + 1; b < polys.length; b++) {
28347
+ const A = polys[a], B = polys[b];
28348
+ if (A.pts.length < 2 || B.pts.length < 2) continue;
28349
+ if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
28350
+ continue;
28351
+ const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
28352
+ let run = [];
28353
+ let runLen = 0;
28354
+ const flush = () => {
28355
+ if (runLen >= minLen && run.length >= 2)
28356
+ runs.push({
28357
+ mid: run[Math.floor(run.length / 2)],
28358
+ length: runLen,
28359
+ pts: run.slice()
28360
+ });
28361
+ run = [];
28362
+ runLen = 0;
28363
+ };
28364
+ for (const p of A.pts) {
28365
+ const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
28366
+ const covered = !nearShared && distToPoly(p, B.pts) < dist;
28367
+ if (covered) {
28368
+ if (run.length)
28369
+ runLen += Math.hypot(
28370
+ p.x - run[run.length - 1].x,
28371
+ p.y - run[run.length - 1].y
28372
+ );
28373
+ run.push(p);
28374
+ } else flush();
28375
+ }
28376
+ flush();
28377
+ }
28378
+ return runs;
28379
+ }
28380
+ function countEdgeOverlaps(layout, opts) {
28381
+ return detectEdgeOverlaps(layout, opts).length;
28382
+ }
28383
+ function detectEdgeNodePierces(layout, opts) {
28384
+ const inset = opts?.inset ?? 6;
28385
+ const minPts = opts?.minPts ?? 2;
28386
+ const rects = [];
28387
+ for (const n of layout.nodes)
28388
+ rects.push({ key: n.label, x: n.x, y: n.y, w: n.width, h: n.height });
28389
+ for (const g of layout.groups)
28390
+ if (g.collapsed)
28391
+ rects.push({
28392
+ key: "__group_" + g.label,
28393
+ x: g.x,
28394
+ y: g.y,
28395
+ w: g.width,
28396
+ h: g.height
28397
+ });
28398
+ const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
28399
+ const out = [];
28400
+ layout.edges.forEach((e, idx) => {
28401
+ if (e.points.length < 2) return;
28402
+ const poly = flatten(splineGen(e.points) ?? "");
28403
+ for (const r of rects) {
28404
+ if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
28405
+ continue;
28406
+ const hits = poly.filter((p) => inside(p, r));
28407
+ if (hits.length >= minPts)
28408
+ out.push({ edgeIdx: idx, node: r.key, pts: hits });
28409
+ }
28410
+ });
28411
+ return out;
28412
+ }
28413
+ function countEdgeNodePierces(layout, opts) {
28414
+ return detectEdgeNodePierces(layout, opts).length;
28415
+ }
28416
+ function deroutePierces(layout) {
28417
+ const pierces = detectEdgeNodePierces(layout);
28418
+ if (!pierces.length) return layout;
28419
+ const rect = /* @__PURE__ */ new Map();
28420
+ for (const n of layout.nodes)
28421
+ rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
28422
+ for (const g of layout.groups)
28423
+ if (g.collapsed)
28424
+ rect.set("__group_" + g.label, {
28425
+ x: g.x,
28426
+ y: g.y,
28427
+ w: g.width,
28428
+ h: g.height
28429
+ });
28430
+ const byEdge = /* @__PURE__ */ new Map();
28431
+ for (const p of pierces) {
28432
+ const arr = byEdge.get(p.edgeIdx);
28433
+ if (arr) arr.push(p.node);
28434
+ else byEdge.set(p.edgeIdx, [p.node]);
28435
+ }
28436
+ const edges = layout.edges.map((e, idx) => {
28437
+ const nodes = byEdge.get(idx);
28438
+ if (!nodes) return e;
28439
+ let pts = e.points.map((p) => ({ x: p.x, y: p.y }));
28440
+ for (const label of nodes) {
28441
+ const r = rect.get(label);
28442
+ if (r) pts = detourAround(pts, r);
28443
+ }
28444
+ return { ...e, points: pts };
28445
+ });
28446
+ return { ...layout, edges };
28447
+ }
28448
+ function detourAround(pts, r) {
28449
+ if (pts.length < 2) return pts;
28450
+ const c = { x: r.x, y: r.y };
28451
+ let bestSeg = -1;
28452
+ let bestT = 0;
28453
+ let bestD = Infinity;
28454
+ let bestPt = pts[0];
28455
+ for (let i = 0; i < pts.length - 1; i++) {
28456
+ const a2 = pts[i], b2 = pts[i + 1];
28457
+ const dx = b2.x - a2.x, dy = b2.y - a2.y;
28458
+ const len2 = dx * dx + dy * dy || 1e-9;
28459
+ let t = ((c.x - a2.x) * dx + (c.y - a2.y) * dy) / len2;
28460
+ t = Math.max(0, Math.min(1, t));
28461
+ const q = { x: a2.x + t * dx, y: a2.y + t * dy };
28462
+ const d = Math.hypot(q.x - c.x, q.y - c.y);
28463
+ if (d < bestD) {
28464
+ bestD = d;
28465
+ bestSeg = i;
28466
+ bestT = t;
28467
+ bestPt = q;
28468
+ }
28469
+ }
28470
+ if (bestSeg < 0) return pts;
28471
+ let nx = bestPt.x - c.x;
28472
+ let ny = bestPt.y - c.y;
28473
+ if (Math.hypot(nx, ny) < 1) {
28474
+ if (r.w <= r.h) {
28475
+ nx = 1;
28476
+ ny = 0;
28477
+ } else {
28478
+ nx = 0;
28479
+ ny = 1;
28480
+ }
28481
+ }
28482
+ const nlen = Math.hypot(nx, ny) || 1;
28483
+ nx /= nlen;
28484
+ ny /= nlen;
28485
+ const a = pts[bestSeg], b = pts[bestSeg + 1];
28486
+ let ex = b.x - a.x, ey = b.y - a.y;
28487
+ const elen = Math.hypot(ex, ey) || 1;
28488
+ ex /= elen;
28489
+ ey /= elen;
28490
+ const clear = Math.hypot(r.w, r.h) / 2 + 18;
28491
+ const hw = Math.hypot(r.w, r.h) / 2;
28492
+ void bestT;
28493
+ const before = {
28494
+ x: bestPt.x - ex * hw + nx * clear * 0.5,
28495
+ y: bestPt.y - ey * hw + ny * clear * 0.5
28496
+ };
28497
+ const peak = { x: c.x + nx * clear, y: c.y + ny * clear };
28498
+ const after = {
28499
+ x: bestPt.x + ex * hw + nx * clear * 0.5,
28500
+ y: bestPt.y + ey * hw + ny * clear * 0.5
28501
+ };
28502
+ const out = pts.slice(0, bestSeg + 1);
28503
+ out.push(before, peak, after);
28504
+ out.push(...pts.slice(bestSeg + 1));
28505
+ return out;
28506
+ }
28507
+ function countEdgeNearMiss(layout, opts) {
28508
+ const near = opts?.near ?? 14;
28509
+ const tight = opts?.tight ?? 8;
28510
+ const minLen = opts?.minLen ?? 18;
28511
+ const close = detectEdgeOverlaps(layout, { dist: near, minLen }).length;
28512
+ const over = detectEdgeOverlaps(layout, { dist: tight, minLen }).length;
28513
+ return Math.max(0, close - over);
28514
+ }
28515
+ function detectGroupOverlaps(layout, opts) {
28516
+ const margin = opts?.margin ?? 4;
28517
+ const raw = layout.groups.map((g) => ({
28518
+ label: g.label,
28519
+ l: g.x - g.width / 2,
28520
+ r: g.x + g.width / 2,
28521
+ t: g.y - g.height / 2,
28522
+ b: g.y + g.height / 2
28523
+ }));
28524
+ const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
28525
+ const rend = raw.map((a, i) => {
28526
+ const parent = raw.some((b, j) => j !== i && contains(a, b));
28527
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
28528
+ });
28529
+ const hit = /* @__PURE__ */ new Set();
28530
+ for (let i = 0; i < raw.length; i++)
28531
+ for (let j = i + 1; j < raw.length; j++) {
28532
+ if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
28533
+ const a = rend[i], b = rend[j];
28534
+ const dx = Math.max(a.l - b.r, b.l - a.r);
28535
+ const dy = Math.max(a.t - b.b, b.t - a.b);
28536
+ if (Math.max(dx, dy) < margin) {
28537
+ hit.add(raw[i].label);
28538
+ hit.add(raw[j].label);
28539
+ }
28540
+ }
28541
+ return [...hit];
28542
+ }
28543
+ function countGroupOverlaps(layout, opts) {
28544
+ const margin = opts?.margin ?? 4;
28545
+ const raw = layout.groups.map((g) => ({
28546
+ l: g.x - g.width / 2,
28547
+ r: g.x + g.width / 2,
28548
+ t: g.y - g.height / 2,
28549
+ b: g.y + g.height / 2
28550
+ }));
28551
+ const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
28552
+ const rend = raw.map((a, i) => {
28553
+ const parent = raw.some((b, j) => j !== i && contains(a, b));
28554
+ return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
28555
+ });
28556
+ let count = 0;
28557
+ for (let i = 0; i < raw.length; i++)
28558
+ for (let j = i + 1; j < raw.length; j++) {
28559
+ if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
28560
+ const a = rend[i], b = rend[j];
28561
+ const dx = Math.max(a.l - b.r, b.l - a.r);
28562
+ const dy = Math.max(a.t - b.b, b.t - a.b);
28563
+ if (Math.max(dx, dy) < margin) count++;
28564
+ }
28565
+ return count;
28566
+ }
28567
+ function separateGroupBands(layout, parsed) {
28568
+ if (layout.groups.some((g) => g.collapsed)) return layout;
28569
+ if (countGroupOverlaps(layout) === 0) return layout;
28570
+ const parentOf = /* @__PURE__ */ new Map();
28571
+ for (const g of parsed.groups) parentOf.set(g.label, g.parentGroup);
28572
+ const topOf = (label) => {
28573
+ let cur = label;
28574
+ const seen = /* @__PURE__ */ new Set();
28575
+ for (; ; ) {
28576
+ const p = parentOf.get(cur);
28577
+ if (!p || seen.has(p)) return cur;
28578
+ seen.add(cur);
28579
+ cur = p;
28580
+ }
28581
+ };
28582
+ const topLabels = parsed.groups.filter((g) => !g.parentGroup).map((g) => g.label);
28583
+ if (topLabels.length < 2) return layout;
28584
+ const nodeTop = /* @__PURE__ */ new Map();
28585
+ for (const g of parsed.groups)
28586
+ for (const child of g.children)
28587
+ if (!parsed.groups.some((gg) => gg.label === child))
28588
+ nodeTop.set(child, topOf(g.label));
28589
+ const axis = parsed.direction === "TB" ? "x" : "y";
28590
+ const boxByLabel = new Map(layout.groups.map((g) => [g.label, g]));
28591
+ const bands = topLabels.map((label) => {
28592
+ const g = boxByLabel.get(label);
28593
+ const half2 = (axis === "y" ? g.height : g.width) / 2;
28594
+ let lo = (axis === "y" ? g.y : g.x) - half2;
28595
+ const hi = (axis === "y" ? g.y : g.x) + half2;
28596
+ const isParent = parsed.groups.some((c) => c.parentGroup === label);
28597
+ if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE2;
28598
+ return { label, lo, hi, c: (lo + hi) / 2 };
28599
+ });
28600
+ bands.sort((a, b) => a.c - b.c);
28601
+ const GAP = 16;
28602
+ const half = bands.map((b) => (b.hi - b.lo) / 2);
28603
+ const off = [0];
28604
+ for (let i = 1; i < bands.length; i++)
28605
+ off[i] = off[i - 1] + half[i - 1] + GAP + half[i];
28606
+ const desired = bands.map((b, i) => b.c - off[i]);
28607
+ const blocks = [];
28608
+ for (let i = 0; i < bands.length; i++) {
28609
+ let blk = { pos: desired[i], count: 1, sum: desired[i], first: i };
28610
+ while (blocks.length && blocks[blocks.length - 1].pos >= blk.pos) {
28611
+ const prev = blocks.pop();
28612
+ const count = prev.count + blk.count;
28613
+ const sum = prev.sum + blk.sum;
28614
+ blk = { pos: sum / count, count, sum, first: prev.first };
28615
+ }
28616
+ blocks.push(blk);
28617
+ }
28618
+ const newC = new Array(bands.length);
28619
+ for (const blk of blocks)
28620
+ for (let k = 0; k < blk.count; k++)
28621
+ newC[blk.first + k] = blk.pos + off[blk.first + k];
28622
+ const delta = /* @__PURE__ */ new Map();
28623
+ let moved = false;
28624
+ bands.forEach((b, i) => {
28625
+ const d = newC[i] - b.c;
28626
+ delta.set(b.label, d);
28627
+ if (Math.abs(d) > 0.5) moved = true;
28628
+ });
28629
+ if (!moved) return layout;
28630
+ const nodeDelta = (label) => {
28631
+ const top = nodeTop.get(label);
28632
+ return top ? delta.get(top) ?? 0 : 0;
28633
+ };
28634
+ const shift = (p, d) => axis === "y" ? { x: p.x, y: p.y + d } : { x: p.x + d, y: p.y };
28635
+ const nodes = layout.nodes.map((n) => {
28636
+ const d = nodeDelta(n.label);
28637
+ return d ? { ...n, ...axis === "y" ? { y: n.y + d } : { x: n.x + d } } : n;
28638
+ });
28639
+ const groups = layout.groups.map((g) => {
28640
+ const d = delta.get(topOf(g.label)) ?? 0;
28641
+ return d ? { ...g, ...axis === "y" ? { y: g.y + d } : { x: g.x + d } } : g;
28642
+ });
28643
+ const edges = layout.edges.map((e) => {
28644
+ const ds = nodeDelta(e.source);
28645
+ const dt = nodeDelta(e.target);
28646
+ if (!ds && !dt) return e;
28647
+ const N = e.points.length;
28648
+ const points = e.points.map((p, i) => {
28649
+ const f = N > 1 ? i / (N - 1) : 0;
28650
+ return shift(p, ds * (1 - f) + dt * f);
28651
+ });
28652
+ return { ...e, points };
28653
+ });
28654
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
28655
+ const acc = (x, y) => {
28656
+ if (x < minX) minX = x;
28657
+ if (x > maxX) maxX = x;
28658
+ if (y < minY) minY = y;
28659
+ if (y > maxY) maxY = y;
28660
+ };
28661
+ for (const n of nodes) {
28662
+ acc(n.x - n.width / 2, n.y - n.height / 2);
28663
+ acc(n.x + n.width / 2, n.y + n.height / 2);
28664
+ }
28665
+ for (const g of groups) {
28666
+ acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE2);
28667
+ acc(g.x + g.width / 2, g.y + g.height / 2);
28668
+ }
28669
+ for (const e of edges) for (const p of e.points) acc(p.x, p.y);
28670
+ const M = 40;
28671
+ const sx = M - minX, sy = M - minY;
28672
+ const reshift = sx !== 0 || sy !== 0;
28673
+ return {
28674
+ nodes: reshift ? nodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })) : nodes,
28675
+ groups: reshift ? groups.map((g) => ({ ...g, x: g.x + sx, y: g.y + sy })) : groups,
28676
+ edges: reshift ? edges.map((e) => ({
28677
+ ...e,
28678
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
28679
+ })) : edges,
28680
+ width: maxX - minX + 2 * M,
28681
+ height: maxY - minY + 2 * M
28682
+ };
28683
+ }
28684
+ function segCross(p1, p2, p3, p4) {
28685
+ const d1x = p2.x - p1.x, d1y = p2.y - p1.y, d2x = p4.x - p3.x, d2y = p4.y - p3.y;
28686
+ const den = d1x * d2y - d1y * d2x;
28687
+ if (Math.abs(den) < 1e-9) return false;
28688
+ const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / den;
28689
+ const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / den;
28690
+ return t > 1e-3 && t < 0.999 && s > 1e-3 && s < 0.999;
28691
+ }
28692
+ function countCrossingsFast(layout) {
28693
+ const E = layout.edges.filter((e) => e.points.length >= 2);
28694
+ const bb = E.map((e) => {
28695
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
28696
+ for (const p of e.points) {
28697
+ if (p.x < x0) x0 = p.x;
28698
+ if (p.x > x1) x1 = p.x;
28699
+ if (p.y < y0) y0 = p.y;
28700
+ if (p.y > y1) y1 = p.y;
28701
+ }
28702
+ return { x0, y0, x1, y1 };
28703
+ });
28704
+ let count = 0;
28705
+ for (let i = 0; i < E.length; i++)
28706
+ for (let j = i + 1; j < E.length; j++) {
28707
+ const A = E[i], B = E[j];
28708
+ if (A.source === B.source || A.source === B.target || A.target === B.source || A.target === B.target)
28709
+ continue;
28710
+ const a = bb[i], b = bb[j];
28711
+ if (a.x1 < b.x0 || b.x1 < a.x0 || a.y1 < b.y0 || b.y1 < a.y0) continue;
28712
+ const pa = A.points, pb = B.points;
28713
+ let hit = false;
28714
+ for (let ai = 0; ai < pa.length - 1 && !hit; ai++)
28715
+ for (let bi = 0; bi < pb.length - 1; bi++) {
28716
+ if (segCross(pa[ai], pa[ai + 1], pb[bi], pb[bi + 1])) {
28717
+ hit = true;
28718
+ break;
28719
+ }
28720
+ }
28721
+ if (hit) count++;
28722
+ }
28723
+ return count;
28724
+ }
28725
+ function meanDrift(layout, prev) {
28726
+ if (!prev?.size) return 0;
28727
+ let sum = 0, n = 0;
28728
+ for (const node of layout.nodes) {
28729
+ const p = prev.get(node.label);
28730
+ if (p) {
28731
+ sum += Math.hypot(node.x - p.x, node.y - p.y);
28732
+ n++;
28733
+ }
28734
+ }
28735
+ return n ? sum / n : 0;
28736
+ }
28737
+ function edgeLength(layout) {
28738
+ let total = 0;
28739
+ for (const e of layout.edges)
28740
+ for (let i = 1; i < e.points.length; i++)
28741
+ total += Math.hypot(
28742
+ e.points[i].x - e.points[i - 1].x,
28743
+ e.points[i].y - e.points[i - 1].y
28744
+ );
28745
+ return total;
28746
+ }
28747
+ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
28748
+ const hideDescriptions = opts?.hideDescriptions ?? false;
28749
+ const collapsedGroupLabels = /* @__PURE__ */ new Set();
28750
+ if (collapseInfo) {
28751
+ const missing = /* @__PURE__ */ new Set();
28752
+ for (const og of collapseInfo.originalGroups)
28753
+ if (!parsed.groups.some((g) => g.label === og.label))
28754
+ missing.add(og.label);
28755
+ for (const label of missing) {
28756
+ const og = collapseInfo.originalGroups.find((g) => g.label === label);
28757
+ const parent = og?.parentGroup;
28758
+ if (!parent || !missing.has(parent)) collapsedGroupLabels.add(label);
28759
+ }
28760
+ }
28761
+ const sizes = /* @__PURE__ */ new Map();
28762
+ let maxDescH = 0;
28763
+ for (const node of parsed.nodes) {
28764
+ const s = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
28765
+ sizes.set(node.label, s);
28766
+ if (!hideDescriptions && node.description && node.description.length > 0)
28767
+ maxDescH = Math.max(maxDescH, s.height);
28768
+ }
28769
+ if (maxDescH > 0) {
28770
+ for (const node of parsed.nodes)
28771
+ if (node.description && node.description.length > 0) {
28772
+ const s = sizes.get(node.label);
28773
+ sizes.set(node.label, { width: s.width, height: maxDescH });
28774
+ }
28775
+ }
28776
+ const gid = (label) => `__group_${label}`;
28777
+ const rankdir = parsed.direction === "TB" ? "TB" : "LR";
28778
+ function place(cfg) {
28779
+ const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
28780
+ const ord = (a) => r ? shuffle(a, r) : a.slice();
28781
+ const g = new import_dagre4.default.graphlib.Graph({ compound: true, multigraph: true });
28782
+ g.setGraph({
28783
+ rankdir,
28784
+ ranker: cfg.ranker,
28785
+ nodesep: cfg.nodesep,
28786
+ ranksep: cfg.ranksep,
28787
+ edgesep: 20,
28788
+ marginx: 40,
28789
+ marginy: 40
28790
+ });
28791
+ g.setDefaultEdgeLabel(() => ({}));
28792
+ for (const grp of ord(parsed.groups))
28793
+ g.setNode(gid(grp.label), { label: grp.label });
28794
+ for (const node of ord(parsed.nodes)) {
28795
+ const s = sizes.get(node.label);
28796
+ g.setNode(node.label, { width: s.width, height: s.height });
28797
+ }
28798
+ for (const label of collapsedGroupLabels)
28799
+ g.setNode(gid(label), { width: NODE_WIDTH, height: NODE_HEIGHT });
28800
+ for (const grp of parsed.groups) {
28801
+ if (grp.parentGroup && g.hasNode(gid(grp.parentGroup)))
28802
+ g.setParent(gid(grp.label), gid(grp.parentGroup));
28803
+ for (const c of ord(grp.children)) {
28804
+ if (g.hasNode(c)) g.setParent(c, gid(grp.label));
28805
+ }
28806
+ }
28807
+ if (collapseInfo)
28808
+ for (const label of collapsedGroupLabels) {
28809
+ const og = collapseInfo.originalGroups.find((x) => x.label === label);
28810
+ if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && g.hasNode(gid(og.parentGroup)))
28811
+ g.setParent(gid(label), gid(og.parentGroup));
28812
+ }
28813
+ for (const e of ord(parsed.edges))
28814
+ if (g.hasNode(e.source) && g.hasNode(e.target))
28815
+ g.setEdge(e.source, e.target, {});
28816
+ import_dagre4.default.layout(g);
28817
+ const nodes = parsed.nodes.map((n2) => {
28818
+ const p = g.node(n2.label);
28819
+ return {
28820
+ label: n2.label,
28821
+ x: p.x,
28822
+ y: p.y,
28823
+ width: p.width,
28824
+ height: p.height
28825
+ };
28826
+ });
28827
+ const groups = parsed.groups.map(
28828
+ (grp) => {
28829
+ const p = g.node(gid(grp.label));
28830
+ return {
28831
+ label: grp.label,
28832
+ lineNumber: grp.lineNumber,
28833
+ x: p.x,
28834
+ y: p.y,
28835
+ width: p.width,
28836
+ height: p.height,
28837
+ collapsed: false,
28838
+ childCount: grp.children.length
28839
+ };
28840
+ }
28841
+ );
28842
+ for (const label of collapsedGroupLabels) {
28843
+ const p = g.node(gid(label));
28844
+ groups.push({
28845
+ label,
28846
+ lineNumber: 0,
28847
+ x: p.x,
28848
+ y: p.y,
28849
+ width: p.width,
28850
+ height: p.height,
28851
+ collapsed: true,
28852
+ childCount: collapseInfo?.collapsedChildCounts.get(label) ?? 0
28853
+ });
28854
+ }
28855
+ const edges = parsed.edges.filter((e) => g.hasEdge(e.source, e.target)).map((e) => {
28856
+ const ed = g.edge(e.source, e.target);
28857
+ return {
28858
+ source: e.source,
28859
+ target: e.target,
28860
+ ...e.label !== void 0 && { label: e.label },
28861
+ bidirectional: e.bidirectional,
28862
+ lineNumber: e.lineNumber,
28863
+ points: ed?.points ?? [],
28864
+ yOffset: 0,
28865
+ parallelCount: 1,
28866
+ metadata: e.metadata
28867
+ };
28868
+ });
28869
+ const gg = g.graph();
28870
+ return {
28871
+ nodes,
28872
+ edges,
28873
+ groups,
28874
+ width: gg.width ?? 800,
28875
+ height: gg.height ?? 600
28876
+ };
28877
+ }
28878
+ const n = parsed.nodes.length;
28879
+ const seedCount = opts?.seeds ?? (n <= 12 ? 80 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
28880
+ const REFINE_K = opts?.refineK ?? 6;
28881
+ const lambda = opts?.lambda ?? DEFAULT_LAMBDA;
28882
+ const prev = opts?.previousPositions;
28883
+ const RANKERS = ["network-simplex", "tight-tree", "longest-path"];
28884
+ const SPACINGS = [
28885
+ { nodesep: 50, ranksep: 60 },
28886
+ { nodesep: 34, ranksep: 46 },
28887
+ { nodesep: 66, ranksep: 82 }
28888
+ ];
28889
+ const configs = [];
28890
+ for (const ranker of RANKERS)
28891
+ for (const sp of SPACINGS) configs.push({ ranker, ...sp });
28892
+ for (let s = 0; s < seedCount; s++)
28893
+ configs.push({
28894
+ ranker: "network-simplex",
28895
+ nodesep: 50,
28896
+ ranksep: 60,
28897
+ seed: s
28898
+ });
28899
+ const badness = (lay, floor) => {
28900
+ const x = countSplineCrossings(lay);
28901
+ if (x > floor) return Infinity;
28902
+ return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
28903
+ };
28904
+ const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
28905
+ const pool = [];
28906
+ for (const cfg of configs) {
28907
+ try {
28908
+ pool.push(place(cfg));
28909
+ } catch {
28910
+ }
28911
+ }
28912
+ if (!pool.length)
28913
+ return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
28914
+ let layered = [];
28915
+ try {
28916
+ layered = layeredCandidates(parsed, sizes);
28917
+ } catch {
28918
+ }
28919
+ pool.sort(
28920
+ (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
28921
+ );
28922
+ const refineK = Math.min(REFINE_K, pool.length);
28923
+ let best = pool[0];
28924
+ let bestObj = Infinity;
28925
+ let bestBad = Infinity;
28926
+ const consider = (lay) => {
28927
+ const bad = badness(lay, bestBad);
28928
+ if (bad === Infinity) return;
28929
+ const sc = objective(lay, bad);
28930
+ if (sc < bestObj) {
28931
+ bestObj = sc;
28932
+ bestBad = bad;
28933
+ best = lay;
28934
+ }
28935
+ };
28936
+ for (const lay of pool.slice(0, refineK)) consider(lay);
28937
+ if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
28938
+ const extra = [];
28939
+ for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
28940
+ try {
28941
+ extra.push(
28942
+ place({
28943
+ ranker: "network-simplex",
28944
+ nodesep: 50,
28945
+ ranksep: 60,
28946
+ seed: s
28947
+ })
28948
+ );
28949
+ } catch {
28950
+ }
28951
+ }
28952
+ extra.sort(
28953
+ (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
28954
+ );
28955
+ for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
28956
+ }
28957
+ for (const lay of layered) {
28958
+ const variants = [lay];
28959
+ const dp = deroutePierces(lay);
28960
+ if (dp !== lay) variants.push(dp);
28961
+ for (const v of variants) {
28962
+ const bad = badness(v, bestBad - 1);
28963
+ if (bad < bestBad) {
28964
+ bestBad = bad;
28965
+ best = v;
28966
+ }
28967
+ }
28968
+ }
28969
+ if (bestBad > 0) {
28970
+ const rerouted = deroutePierces(best);
28971
+ if (rerouted !== best && badness(rerouted, bestBad - 1) < bestBad)
28972
+ best = rerouted;
28973
+ }
28974
+ if (bestBad > 0 && countGroupOverlaps(best) > 0) {
28975
+ const separated = separateGroupBands(best, parsed);
28976
+ if (separated !== best && badness(separated, bestBad - 1) < bestBad)
28977
+ best = separated;
28978
+ }
28979
+ return best;
28980
+ }
28981
+ var import_dagre4, import_d3_shape, DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
28982
+ var init_layout_search = __esm({
28983
+ "src/boxes-and-lines/layout-search.ts"() {
28984
+ "use strict";
28985
+ import_dagre4 = __toESM(require("@dagrejs/dagre"), 1);
28986
+ import_d3_shape = require("d3-shape");
28987
+ init_layout5();
28988
+ init_layout_layered();
28989
+ DEFAULT_LAMBDA = 4;
28990
+ ESCALATE_THRESHOLD = 4;
28991
+ ESCALATE_MAX_N = 45;
28992
+ ESCALATE_SEEDS = 18;
28993
+ ESCALATE_REFINE = 10;
28994
+ splineGen = (0, import_d3_shape.line)().x((d) => d.x).y((d) => d.y).curve(import_d3_shape.curveBasis);
28995
+ GROUP_LABEL_ZONE2 = 32;
28996
+ }
28997
+ });
28998
+
27669
28999
  // src/boxes-and-lines/layout.ts
27670
29000
  var layout_exports5 = {};
27671
29001
  __export(layout_exports5, {
29002
+ NODE_HEIGHT: () => NODE_HEIGHT,
29003
+ NODE_WIDTH: () => NODE_WIDTH,
29004
+ computeNodeSize: () => computeNodeSize,
27672
29005
  layoutBoxesAndLines: () => layoutBoxesAndLines
27673
29006
  });
27674
29007
  function splitCamelCase2(word) {
@@ -27741,417 +29074,21 @@ function computeNodeSize(node, reserveValueRow) {
27741
29074
  const totalHeight = labelHeight + SEPARATOR_GAP5 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
27742
29075
  return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
27743
29076
  }
27744
- function getElk() {
27745
- if (!elkInstance) elkInstance = new import_elk_bundled.default();
27746
- return elkInstance;
27747
- }
27748
- function baseOptions() {
27749
- return {
27750
- "elk.algorithm": "layered",
27751
- // INCLUDE_CHILDREN lets ELK route edges across container boundaries.
27752
- "elk.hierarchyHandling": "INCLUDE_CHILDREN",
27753
- "elk.edgeRouting": "ORTHOGONAL",
27754
- "elk.layered.unnecessaryBendpoints": "true",
27755
- // Let edges leave from top/bottom of nodes (not just the flow-direction
27756
- // sides) when it reduces crossings.
27757
- "elk.layered.allowNonFlowPortsToSwitchSides": "true"
27758
- };
27759
- }
27760
- function bkBaseline() {
27761
- return {
27762
- ...baseOptions(),
27763
- "elk.layered.nodePlacement.strategy": "BRANDES_KOEPF",
27764
- "elk.layered.nodePlacement.bk.fixedAlignment": "BALANCED",
27765
- "elk.layered.nodePlacement.bk.edgeStraightening": "IMPROVE_STRAIGHTNESS",
27766
- "elk.layered.compaction.connectedComponents": "true",
27767
- "elk.layered.spacing.nodeNodeBetweenLayers": "90",
27768
- "elk.spacing.nodeNode": "55",
27769
- "elk.spacing.edgeNode": "55",
27770
- "elk.spacing.edgeEdge": "18"
27771
- };
27772
- }
27773
- function getVariants() {
27774
- const bk = bkBaseline();
27775
- return [
27776
- {
27777
- name: "bk-baseline",
27778
- options: {
27779
- ...bk,
27780
- "elk.layered.crossingMinimization.greedySwitch.type": "ONE_SIDED"
27781
- }
27782
- },
27783
- {
27784
- name: "bk-aggressive",
27785
- options: {
27786
- ...bk,
27787
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27788
- "elk.layered.thoroughness": "50"
27789
- }
27790
- },
27791
- {
27792
- name: "bk-wide",
27793
- options: {
27794
- ...bk,
27795
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27796
- "elk.layered.thoroughness": "50",
27797
- "elk.spacing.nodeNode": "70",
27798
- "elk.spacing.edgeNode": "75",
27799
- "elk.spacing.edgeEdge": "22",
27800
- "elk.layered.spacing.nodeNodeBetweenLayers": "120"
27801
- }
27802
- },
27803
- {
27804
- name: "longest-path",
27805
- options: {
27806
- ...bk,
27807
- "elk.layered.layering.strategy": "LONGEST_PATH",
27808
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27809
- "elk.layered.thoroughness": "50"
27810
- }
27811
- },
27812
- {
27813
- name: "bounded-width",
27814
- options: {
27815
- ...bk,
27816
- "elk.layered.layering.strategy": "COFFMAN_GRAHAM",
27817
- "elk.layered.layering.coffmanGraham.layerBound": "3",
27818
- "elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
27819
- "elk.layered.thoroughness": "50"
27820
- }
27821
- }
27822
- ];
27823
- }
27824
- function countCrossings(edges) {
27825
- let count = 0;
27826
- for (let i = 0; i < edges.length; i++) {
27827
- const edgeI = edges[i];
27828
- const a = edgeI.points;
27829
- if (a.length < 2) continue;
27830
- for (let j = i + 1; j < edges.length; j++) {
27831
- const edgeJ = edges[j];
27832
- const b = edgeJ.points;
27833
- if (b.length < 2) continue;
27834
- if (edgeI.source === edgeJ.source) continue;
27835
- if (edgeI.source === edgeJ.target) continue;
27836
- if (edgeI.target === edgeJ.source) continue;
27837
- if (edgeI.target === edgeJ.target) continue;
27838
- for (let ai = 0; ai < a.length - 1; ai++) {
27839
- for (let bi = 0; bi < b.length - 1; bi++) {
27840
- if (segmentsCross(a[ai], a[ai + 1], b[bi], b[bi + 1])) count++;
27841
- }
27842
- }
27843
- }
27844
- }
27845
- return count;
27846
- }
27847
- function segmentsCross(p1, p2, p3, p4) {
27848
- const d1x = p2.x - p1.x;
27849
- const d1y = p2.y - p1.y;
27850
- const d2x = p4.x - p3.x;
27851
- const d2y = p4.y - p3.y;
27852
- const denom = d1x * d2y - d1y * d2x;
27853
- if (Math.abs(denom) < 1e-9) return false;
27854
- const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / denom;
27855
- const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / denom;
27856
- const EPS = 1e-3;
27857
- return t > EPS && t < 1 - EPS && s > EPS && s < 1 - EPS;
27858
- }
27859
- function countTotalBends(edges) {
27860
- let bends = 0;
27861
- for (const e of edges) bends += Math.max(0, e.points.length - 2);
27862
- return bends;
27863
- }
27864
- function scoreLayout(layout) {
27865
- return {
27866
- crossings: countCrossings(layout.edges),
27867
- bends: countTotalBends(layout.edges),
27868
- area: layout.width * layout.height
27869
- };
27870
- }
27871
- function cmpScore(a, b) {
27872
- const aBucket = a.crossings <= CROSSINGS_FORGIVENESS ? 0 : a.crossings;
27873
- const bBucket = b.crossings <= CROSSINGS_FORGIVENESS ? 0 : b.crossings;
27874
- if (aBucket !== bBucket) return aBucket - bBucket;
27875
- if (a.area !== b.area) return a.area - b.area;
27876
- return a.bends - b.bends;
27877
- }
27878
29077
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
27879
- const hideDescriptions = layoutOptions?.hideDescriptions ?? false;
27880
- const direction = parsed.direction === "TB" ? "DOWN" : "RIGHT";
27881
- const collapsedGroupLabels = /* @__PURE__ */ new Set();
27882
- if (collapseInfo) {
27883
- const missingGroups = /* @__PURE__ */ new Set();
27884
- for (const og of collapseInfo.originalGroups) {
27885
- if (!parsed.groups.some((g) => g.label === og.label)) {
27886
- missingGroups.add(og.label);
27887
- }
27888
- }
27889
- for (const label of missingGroups) {
27890
- const og = collapseInfo.originalGroups.find((g) => g.label === label);
27891
- const parentLabel = og?.parentGroup;
27892
- if (!parentLabel || !missingGroups.has(parentLabel)) {
27893
- collapsedGroupLabels.add(label);
27894
- }
27895
- }
27896
- }
27897
- const nodeSizes = /* @__PURE__ */ new Map();
27898
- let maxDescHeight = 0;
27899
- for (const node of parsed.nodes) {
27900
- const size = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
27901
- nodeSizes.set(node.label, size);
27902
- if (!hideDescriptions && node.description && node.description.length > 0) {
27903
- maxDescHeight = Math.max(maxDescHeight, size.height);
27904
- }
27905
- }
27906
- if (maxDescHeight > 0) {
27907
- for (const node of parsed.nodes) {
27908
- if (node.description && node.description.length > 0) {
27909
- const size = nodeSizes.get(node.label);
27910
- nodeSizes.set(node.label, { width: size.width, height: maxDescHeight });
27911
- }
27912
- }
27913
- }
27914
- const expandedGroupSet = new Set(parsed.groups.map((g) => g.label));
27915
- const gid = (label) => `__group_${label}`;
27916
- function buildGraph() {
27917
- const nodeById = /* @__PURE__ */ new Map();
27918
- const parentOf = /* @__PURE__ */ new Map();
27919
- for (const node of parsed.nodes) {
27920
- const size = nodeSizes.get(node.label);
27921
- nodeById.set(node.label, {
27922
- id: node.label,
27923
- width: size.width,
27924
- height: size.height,
27925
- labels: [{ text: node.label }]
27926
- });
27927
- }
27928
- for (const group of parsed.groups) {
27929
- nodeById.set(gid(group.label), {
27930
- id: gid(group.label),
27931
- labels: [{ text: group.label }],
27932
- layoutOptions: {
27933
- "elk.padding": `[top=${CONTAINER_PAD_TOP2},left=${CONTAINER_PAD_X3},bottom=${CONTAINER_PAD_BOTTOM3},right=${CONTAINER_PAD_X3}]`,
27934
- // Suggest square-ish containers — has limited effect with
27935
- // INCLUDE_CHILDREN but doesn't hurt.
27936
- "elk.aspectRatio": "1.4"
27937
- },
27938
- children: [],
27939
- edges: []
27940
- });
27941
- }
27942
- for (const label of collapsedGroupLabels) {
27943
- nodeById.set(gid(label), {
27944
- id: gid(label),
27945
- width: NODE_WIDTH,
27946
- height: NODE_HEIGHT,
27947
- labels: [{ text: label }]
27948
- });
27949
- }
27950
- for (const group of parsed.groups) {
27951
- if (group.parentGroup && nodeById.has(gid(group.parentGroup))) {
27952
- parentOf.set(gid(group.label), gid(group.parentGroup));
27953
- }
27954
- }
27955
- if (collapseInfo) {
27956
- for (const label of collapsedGroupLabels) {
27957
- const og = collapseInfo.originalGroups.find((g) => g.label === label);
27958
- if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && nodeById.has(gid(og.parentGroup))) {
27959
- parentOf.set(gid(label), gid(og.parentGroup));
27960
- }
27961
- }
27962
- }
27963
- for (const group of parsed.groups) {
27964
- for (const child of group.children) {
27965
- if (expandedGroupSet.has(child)) continue;
27966
- if (nodeById.has(child)) {
27967
- parentOf.set(child, gid(group.label));
27968
- }
27969
- }
27970
- }
27971
- const roots = [];
27972
- for (const [id, node] of nodeById) {
27973
- const parentId = parentOf.get(id);
27974
- if (parentId) {
27975
- const parent = nodeById.get(parentId);
27976
- parent.children = parent.children ?? [];
27977
- parent.children.push(node);
27978
- } else {
27979
- roots.push(node);
27980
- }
27981
- }
27982
- const rootEdges = [];
27983
- for (let i = 0; i < parsed.edges.length; i++) {
27984
- const edge = parsed.edges[i];
27985
- if (!nodeById.has(edge.source) || !nodeById.has(edge.target)) continue;
27986
- rootEdges.push({
27987
- id: `e${i}`,
27988
- sources: [edge.source],
27989
- targets: [edge.target]
27990
- });
27991
- }
27992
- return { roots, rootEdges };
27993
- }
27994
- async function runVariant(variant) {
27995
- const { roots, rootEdges } = buildGraph();
27996
- const elkRoot = {
27997
- id: "root",
27998
- layoutOptions: {
27999
- ...variant.options,
28000
- "elk.direction": direction,
28001
- "elk.padding": `[top=${MARGIN3},left=${MARGIN3},bottom=${MARGIN3},right=${MARGIN3}]`
28002
- },
28003
- children: roots,
28004
- edges: rootEdges
28005
- };
28006
- const result = await getElk().layout(elkRoot);
28007
- return extractLayout(result);
28008
- }
28009
- function extractLayout(result) {
28010
- const layoutNodes = [];
28011
- const layoutGroups = [];
28012
- const allEdges = [];
28013
- const containerAbs = /* @__PURE__ */ new Map();
28014
- function walk(n, offsetX, offsetY, isRoot) {
28015
- const nx = (n.x ?? 0) + offsetX;
28016
- const ny = (n.y ?? 0) + offsetY;
28017
- const nw = n.width ?? 0;
28018
- const nh = n.height ?? 0;
28019
- if (isRoot) {
28020
- containerAbs.set("root", { x: nx, y: ny });
28021
- } else {
28022
- const isGroup = n.id.startsWith("__group_");
28023
- if (isGroup) {
28024
- const label = n.id.slice("__group_".length);
28025
- const collapsed = collapsedGroupLabels.has(label);
28026
- const og = collapseInfo?.originalGroups.find(
28027
- (g) => g.label === label
28028
- );
28029
- const pg = parsed.groups.find((g) => g.label === label);
28030
- const childCount = collapsed ? collapseInfo?.collapsedChildCounts.get(label) ?? 0 : void 0;
28031
- layoutGroups.push({
28032
- label,
28033
- lineNumber: pg?.lineNumber ?? og?.lineNumber ?? 0,
28034
- x: nx + nw / 2,
28035
- y: ny + nh / 2,
28036
- width: nw,
28037
- height: nh,
28038
- collapsed,
28039
- ...childCount !== void 0 && { childCount }
28040
- });
28041
- if (!collapsed) containerAbs.set(n.id, { x: nx, y: ny });
28042
- } else {
28043
- layoutNodes.push({
28044
- label: n.id,
28045
- x: nx + nw / 2,
28046
- y: ny + nh / 2,
28047
- width: nw,
28048
- height: nh
28049
- });
28050
- }
28051
- }
28052
- if (n.edges) for (const e of n.edges) allEdges.push(e);
28053
- if (n.children) for (const c of n.children) walk(c, nx, ny, false);
28054
- }
28055
- walk(result, 0, 0, true);
28056
- const edgeYOffsets = new Array(parsed.edges.length).fill(0);
28057
- const edgeParallelCounts = new Array(parsed.edges.length).fill(1);
28058
- const parallelGroups = /* @__PURE__ */ new Map();
28059
- for (let i = 0; i < parsed.edges.length; i++) {
28060
- const edge = parsed.edges[i];
28061
- const [a, b] = edge.source < edge.target ? [edge.source, edge.target] : [edge.target, edge.source];
28062
- const key = `${a}\0${b}`;
28063
- if (!parallelGroups.has(key)) parallelGroups.set(key, []);
28064
- parallelGroups.get(key).push(i);
28065
- }
28066
- for (const group of parallelGroups.values()) {
28067
- const capped = group.slice(0, MAX_PARALLEL_EDGES);
28068
- for (const idx of group.slice(MAX_PARALLEL_EDGES)) {
28069
- edgeParallelCounts[idx] = 0;
28070
- }
28071
- if (capped.length < 2) continue;
28072
- for (let j = 0; j < capped.length; j++) {
28073
- const cappedJ = capped[j];
28074
- edgeYOffsets[cappedJ] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
28075
- edgeParallelCounts[cappedJ] = capped.length;
28076
- }
28077
- }
28078
- const edgeById = /* @__PURE__ */ new Map();
28079
- for (const e of allEdges) edgeById.set(e.id, e);
28080
- const layoutEdges = [];
28081
- for (let i = 0; i < parsed.edges.length; i++) {
28082
- const edge = parsed.edges[i];
28083
- if (edgeParallelCounts[i] === 0) continue;
28084
- const elkEdge = edgeById.get(`e${i}`);
28085
- if (!elkEdge?.sections || elkEdge.sections.length === 0) continue;
28086
- const container = elkEdge.container ?? "root";
28087
- const off = containerAbs.get(container) ?? { x: 0, y: 0 };
28088
- const s = elkEdge.sections[0];
28089
- const points = [
28090
- { x: s.startPoint.x + off.x, y: s.startPoint.y + off.y },
28091
- ...(s.bendPoints ?? []).map((p) => ({
28092
- x: p.x + off.x,
28093
- y: p.y + off.y
28094
- })),
28095
- { x: s.endPoint.x + off.x, y: s.endPoint.y + off.y }
28096
- ];
28097
- let labelX;
28098
- let labelY;
28099
- if (edge.label && points.length >= 2) {
28100
- const mid = Math.floor(points.length / 2);
28101
- const midPoint = points[mid];
28102
- labelX = midPoint.x;
28103
- labelY = midPoint.y - 10;
28104
- }
28105
- layoutEdges.push({
28106
- source: edge.source,
28107
- target: edge.target,
28108
- ...edge.label !== void 0 && { label: edge.label },
28109
- bidirectional: edge.bidirectional,
28110
- lineNumber: edge.lineNumber,
28111
- points,
28112
- ...labelX !== void 0 && { labelX },
28113
- ...labelY !== void 0 && { labelY },
28114
- // In-bounds — i < parsed.edges.length, arrays sized to that length.
28115
- yOffset: edgeYOffsets[i],
28116
- parallelCount: edgeParallelCounts[i],
28117
- metadata: edge.metadata,
28118
- deferred: true
28119
- });
28120
- }
28121
- let maxX = 0;
28122
- let maxY = 0;
28123
- for (const node of layoutNodes) {
28124
- maxX = Math.max(maxX, node.x + node.width / 2);
28125
- maxY = Math.max(maxY, node.y + node.height / 2);
28126
- }
28127
- for (const group of layoutGroups) {
28128
- maxX = Math.max(maxX, group.x + group.width / 2);
28129
- maxY = Math.max(maxY, group.y + group.height / 2);
29078
+ const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
29079
+ const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
29080
+ ...layoutOptions?.hideDescriptions !== void 0 && {
29081
+ hideDescriptions: layoutOptions.hideDescriptions
29082
+ },
29083
+ ...layoutOptions?.previousPositions !== void 0 && {
29084
+ previousPositions: layoutOptions.previousPositions
28130
29085
  }
28131
- return {
28132
- nodes: layoutNodes,
28133
- edges: layoutEdges,
28134
- groups: layoutGroups,
28135
- width: maxX + MARGIN3,
28136
- height: maxY + MARGIN3
28137
- };
28138
- }
28139
- const N = parsed.nodes.length + parsed.groups.length;
28140
- const E = parsed.edges.length;
28141
- const trivial = N < 8 && E < 10;
28142
- const variants = trivial ? [getVariants()[1]] : getVariants();
28143
- const results = await Promise.all(variants.map((v) => runVariant(v)));
28144
- let best = results[0];
28145
- let bestScore = scoreLayout(best);
28146
- for (let i = 1; i < results.length; i++) {
28147
- const resultI = results[i];
28148
- const s = scoreLayout(resultI);
28149
- if (cmpScore(s, bestScore) < 0) {
28150
- best = resultI;
28151
- bestScore = s;
28152
- }
28153
- }
28154
- return attachNotes(best, parsed, layoutOptions?.collapsedNotes);
29086
+ });
29087
+ return attachNotes(
29088
+ applyParallelEdgeOffsets(searched),
29089
+ parsed,
29090
+ layoutOptions?.collapsedNotes
29091
+ );
28155
29092
  }
28156
29093
  function attachNotes(layout, parsed, collapsedNotes) {
28157
29094
  const notesSuppressed = parsed.options?.["no-notes"] === "on";
@@ -28229,21 +29166,47 @@ function attachNotes(layout, parsed, collapsedNotes) {
28229
29166
  nodes: finalNodes,
28230
29167
  edges: finalEdges,
28231
29168
  groups: finalGroups,
28232
- width: bbMaxX + shiftX + MARGIN3,
28233
- height: bbMaxY + shiftY + MARGIN3
29169
+ width: bbMaxX + shiftX + MARGIN4,
29170
+ height: bbMaxY + shiftY + MARGIN4
29171
+ };
29172
+ }
29173
+ function applyParallelEdgeOffsets(layout) {
29174
+ const groups = /* @__PURE__ */ new Map();
29175
+ layout.edges.forEach((e, i) => {
29176
+ const [a, b] = e.source < e.target ? [e.source, e.target] : [e.target, e.source];
29177
+ const key = `${a}\0${b}`;
29178
+ const arr = groups.get(key);
29179
+ if (arr) arr.push(i);
29180
+ else groups.set(key, [i]);
29181
+ });
29182
+ if ([...groups.values()].every((g) => g.length < 2)) return layout;
29183
+ const yOffset = new Array(layout.edges.length).fill(0);
29184
+ const count = new Array(layout.edges.length).fill(1);
29185
+ for (const idxs of groups.values()) {
29186
+ const capped = idxs.slice(0, MAX_PARALLEL_EDGES);
29187
+ for (const drop of idxs.slice(MAX_PARALLEL_EDGES)) count[drop] = 0;
29188
+ if (capped.length < 2) continue;
29189
+ capped.forEach((idx, j) => {
29190
+ yOffset[idx] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
29191
+ count[idx] = capped.length;
29192
+ });
29193
+ }
29194
+ return {
29195
+ ...layout,
29196
+ edges: layout.edges.map((e, i) => ({
29197
+ ...e,
29198
+ yOffset: yOffset[i],
29199
+ parallelCount: count[i]
29200
+ }))
28234
29201
  };
28235
29202
  }
28236
- 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;
29203
+ 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;
28237
29204
  var init_layout5 = __esm({
28238
29205
  "src/boxes-and-lines/layout.ts"() {
28239
29206
  "use strict";
28240
- import_elk_bundled = __toESM(require("elkjs/lib/elk.bundled.js"), 1);
28241
29207
  init_text_measure();
28242
29208
  init_notes();
28243
- MARGIN3 = 40;
28244
- CONTAINER_PAD_X3 = 30;
28245
- CONTAINER_PAD_TOP2 = 40;
28246
- CONTAINER_PAD_BOTTOM3 = 24;
29209
+ MARGIN4 = 40;
28247
29210
  MAX_PARALLEL_EDGES = 5;
28248
29211
  PARALLEL_SPACING = 22;
28249
29212
  PHI = 1.618;
@@ -28260,8 +29223,6 @@ var init_layout5 = __esm({
28260
29223
  LABEL_PAD = 12;
28261
29224
  VALUE_ROW_FONT = 11;
28262
29225
  VALUE_ROW_H = SEPARATOR_GAP5 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
28263
- elkInstance = null;
28264
- CROSSINGS_FORGIVENESS = 1;
28265
29226
  }
28266
29227
  });
28267
29228
 
@@ -28416,7 +29377,7 @@ function layoutMindmap(parsed, _palette, options) {
28416
29377
  leafWidth: ctx.structural(LEAF_WIDTH),
28417
29378
  hGap: ctx.aesthetic(H_GAP2),
28418
29379
  vGap: ctx.aesthetic(V_GAP2),
28419
- margin: ctx.aesthetic(MARGIN4),
29380
+ margin: ctx.aesthetic(MARGIN5),
28420
29381
  multiRootGap: ctx.aesthetic(MULTI_ROOT_GAP)
28421
29382
  };
28422
29383
  populateDepthCache(roots);
@@ -28794,7 +29755,7 @@ function populateDepthCache(roots) {
28794
29755
  };
28795
29756
  walk(roots, 0);
28796
29757
  }
28797
- 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;
29758
+ 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;
28798
29759
  var init_layout6 = __esm({
28799
29760
  "src/mindmap/layout.ts"() {
28800
29761
  "use strict";
@@ -28810,7 +29771,7 @@ var init_layout6 = __esm({
28810
29771
  NODE_V_PAD = 10;
28811
29772
  H_GAP2 = 40;
28812
29773
  V_GAP2 = 12;
28813
- MARGIN4 = 40;
29774
+ MARGIN5 = 40;
28814
29775
  MULTI_ROOT_GAP = 60;
28815
29776
  nodeDepthCache = /* @__PURE__ */ new Map();
28816
29777
  }
@@ -30509,7 +31470,7 @@ function layoutC4Context(parsed, activeTagGroup) {
30509
31470
  }
30510
31471
  const contextRels = rollUpContextRelationships(parsed);
30511
31472
  const spacing = computeAdaptiveSpacing(contextRels);
30512
- const g = new import_dagre4.default.graphlib.Graph();
31473
+ const g = new import_dagre5.default.graphlib.Graph();
30513
31474
  g.setGraph({
30514
31475
  rankdir: "TB",
30515
31476
  nodesep: spacing.nodesep,
@@ -30530,7 +31491,7 @@ function layoutC4Context(parsed, activeTagGroup) {
30530
31491
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
30531
31492
  }
30532
31493
  }
30533
- import_dagre4.default.layout(g);
31494
+ import_dagre5.default.layout(g);
30534
31495
  reduceCrossings(
30535
31496
  g,
30536
31497
  validRels.map((r) => ({ source: r.sourceName, target: r.targetName }))
@@ -30597,8 +31558,8 @@ function layoutC4Context(parsed, activeTagGroup) {
30597
31558
  }
30598
31559
  }
30599
31560
  if (nodes.length > 0) {
30600
- const shiftX = MARGIN5 - minX;
30601
- const shiftY = MARGIN5 - minY;
31561
+ const shiftX = MARGIN6 - minX;
31562
+ const shiftY = MARGIN6 - minY;
30602
31563
  for (const node of nodes) {
30603
31564
  node.x += shiftX;
30604
31565
  node.y += shiftY;
@@ -30610,12 +31571,12 @@ function layoutC4Context(parsed, activeTagGroup) {
30610
31571
  }
30611
31572
  }
30612
31573
  }
30613
- let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN5 * 2 : 0;
30614
- let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN5 * 2 : 0;
31574
+ let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN6 * 2 : 0;
31575
+ let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN6 * 2 : 0;
30615
31576
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
30616
31577
  if (legendGroups.length > 0) {
30617
- const legendY = totalHeight + MARGIN5;
30618
- let legendX = MARGIN5;
31578
+ const legendY = totalHeight + MARGIN6;
31579
+ let legendX = MARGIN6;
30619
31580
  for (const lg of legendGroups) {
30620
31581
  lg.x = legendX;
30621
31582
  lg.y = legendY;
@@ -30708,7 +31669,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30708
31669
  }
30709
31670
  }
30710
31671
  const hasGroups = elementToGroup.size > 0;
30711
- const g = hasGroups ? new import_dagre4.default.graphlib.Graph({ compound: true }) : new import_dagre4.default.graphlib.Graph();
31672
+ const g = hasGroups ? new import_dagre5.default.graphlib.Graph({ compound: true }) : new import_dagre5.default.graphlib.Graph();
30712
31673
  g.setDefaultEdgeLabel(() => ({}));
30713
31674
  if (hasGroups) {
30714
31675
  const seenGroups = /* @__PURE__ */ new Set();
@@ -30786,7 +31747,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30786
31747
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
30787
31748
  }
30788
31749
  }
30789
- import_dagre4.default.layout(g);
31750
+ import_dagre5.default.layout(g);
30790
31751
  const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
30791
31752
  reduceCrossings(
30792
31753
  g,
@@ -30947,8 +31908,8 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30947
31908
  if (pt.y > maxY) maxY = pt.y;
30948
31909
  }
30949
31910
  }
30950
- const shiftX = MARGIN5 - minX;
30951
- const shiftY = MARGIN5 - minY;
31911
+ const shiftX = MARGIN6 - minX;
31912
+ const shiftY = MARGIN6 - minY;
30952
31913
  for (const node of nodes) {
30953
31914
  node.x += shiftX;
30954
31915
  node.y += shiftY;
@@ -30965,12 +31926,12 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
30965
31926
  pt.y += shiftY;
30966
31927
  }
30967
31928
  }
30968
- let totalWidth = maxX - minX + MARGIN5 * 2;
30969
- let totalHeight = maxY - minY + MARGIN5 * 2;
31929
+ let totalWidth = maxX - minX + MARGIN6 * 2;
31930
+ let totalHeight = maxY - minY + MARGIN6 * 2;
30970
31931
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
30971
31932
  if (legendGroups.length > 0) {
30972
- const legendY = totalHeight + MARGIN5;
30973
- let legendX = MARGIN5;
31933
+ const legendY = totalHeight + MARGIN6;
31934
+ let legendX = MARGIN6;
30974
31935
  for (const lg of legendGroups) {
30975
31936
  lg.x = legendX;
30976
31937
  lg.y = legendY;
@@ -31115,7 +32076,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31115
32076
  }
31116
32077
  }
31117
32078
  const hasGroups = elementToGroup.size > 0;
31118
- const g = hasGroups ? new import_dagre4.default.graphlib.Graph({ compound: true }) : new import_dagre4.default.graphlib.Graph();
32079
+ const g = hasGroups ? new import_dagre5.default.graphlib.Graph({ compound: true }) : new import_dagre5.default.graphlib.Graph();
31119
32080
  g.setDefaultEdgeLabel(() => ({}));
31120
32081
  if (hasGroups) {
31121
32082
  const seenGroups = /* @__PURE__ */ new Set();
@@ -31199,7 +32160,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31199
32160
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
31200
32161
  }
31201
32162
  }
31202
- import_dagre4.default.layout(g);
32163
+ import_dagre5.default.layout(g);
31203
32164
  const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
31204
32165
  reduceCrossings(
31205
32166
  g,
@@ -31362,8 +32323,8 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31362
32323
  if (pt.y > maxY) maxY = pt.y;
31363
32324
  }
31364
32325
  }
31365
- const shiftX = MARGIN5 - minX;
31366
- const shiftY = MARGIN5 - minY;
32326
+ const shiftX = MARGIN6 - minX;
32327
+ const shiftY = MARGIN6 - minY;
31367
32328
  for (const node of nodes) {
31368
32329
  node.x += shiftX;
31369
32330
  node.y += shiftY;
@@ -31380,12 +32341,12 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
31380
32341
  pt.y += shiftY;
31381
32342
  }
31382
32343
  }
31383
- let totalWidth = maxX - minX + MARGIN5 * 2;
31384
- let totalHeight = maxY - minY + MARGIN5 * 2;
32344
+ let totalWidth = maxX - minX + MARGIN6 * 2;
32345
+ let totalHeight = maxY - minY + MARGIN6 * 2;
31385
32346
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
31386
32347
  if (legendGroups.length > 0) {
31387
- const legendY = totalHeight + MARGIN5;
31388
- let legendX = MARGIN5;
32348
+ const legendY = totalHeight + MARGIN6;
32349
+ let legendX = MARGIN6;
31389
32350
  for (const lg of legendGroups) {
31390
32351
  lg.x = legendX;
31391
32352
  lg.y = legendY;
@@ -31491,7 +32452,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31491
32452
  for (const r of refEntries) {
31492
32453
  nameToElement.set(r.element.name, r.element);
31493
32454
  }
31494
- const g = new import_dagre4.default.graphlib.Graph({ compound: true });
32455
+ const g = new import_dagre5.default.graphlib.Graph({ compound: true });
31495
32456
  g.setDefaultEdgeLabel(() => ({}));
31496
32457
  for (const [infraId] of infraIds) {
31497
32458
  g.setNode(infraId, {});
@@ -31535,7 +32496,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31535
32496
  g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
31536
32497
  }
31537
32498
  }
31538
- import_dagre4.default.layout(g);
32499
+ import_dagre5.default.layout(g);
31539
32500
  const nodeInfraMap = /* @__PURE__ */ new Map();
31540
32501
  for (const r of refEntries) nodeInfraMap.set(r.element.name, r.infraId);
31541
32502
  reduceCrossings(
@@ -31673,8 +32634,8 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31673
32634
  if (pt.y > maxY) maxY = pt.y;
31674
32635
  }
31675
32636
  }
31676
- const shiftX = MARGIN5 - minX;
31677
- const shiftY = MARGIN5 - minY;
32637
+ const shiftX = MARGIN6 - minX;
32638
+ const shiftY = MARGIN6 - minY;
31678
32639
  for (const node of nodes) {
31679
32640
  node.x += shiftX;
31680
32641
  node.y += shiftY;
@@ -31689,12 +32650,12 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31689
32650
  pt.y += shiftY;
31690
32651
  }
31691
32652
  }
31692
- let totalWidth = maxX - minX + MARGIN5 * 2;
31693
- let totalHeight = maxY - minY + MARGIN5 * 2;
32653
+ let totalWidth = maxX - minX + MARGIN6 * 2;
32654
+ let totalHeight = maxY - minY + MARGIN6 * 2;
31694
32655
  const legendGroups = computeLegendGroups3(parsed.tagGroups);
31695
32656
  if (legendGroups.length > 0) {
31696
- const legendY = totalHeight + MARGIN5;
31697
- let legendX = MARGIN5;
32657
+ const legendY = totalHeight + MARGIN6;
32658
+ let legendX = MARGIN6;
31698
32659
  for (const lg of legendGroups) {
31699
32660
  lg.x = legendX;
31700
32661
  lg.y = legendY;
@@ -31714,11 +32675,11 @@ function layoutC4Deployment(parsed, activeTagGroup) {
31714
32675
  height: totalHeight
31715
32676
  };
31716
32677
  }
31717
- 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;
32678
+ 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;
31718
32679
  var init_layout8 = __esm({
31719
32680
  "src/c4/layout.ts"() {
31720
32681
  "use strict";
31721
- import_dagre4 = __toESM(require("@dagrejs/dagre"), 1);
32682
+ import_dagre5 = __toESM(require("@dagrejs/dagre"), 1);
31722
32683
  init_legend_constants();
31723
32684
  init_text_measure();
31724
32685
  gNode = (g, name) => g.node(name);
@@ -31735,7 +32696,7 @@ var init_layout8 = __esm({
31735
32696
  CARD_H_PAD3 = 20;
31736
32697
  META_LINE_HEIGHT5 = 16;
31737
32698
  META_FONT_SIZE5 = 11;
31738
- MARGIN5 = 40;
32699
+ MARGIN6 = 40;
31739
32700
  BOUNDARY_PAD = 40;
31740
32701
  GROUP_BOUNDARY_PAD = 24;
31741
32702
  EDGE_NODE_COLLISION_WEIGHT = 5e3;
@@ -32803,7 +33764,7 @@ function layoutGraph(graph, options) {
32803
33764
  if (allNodes.length === 0) {
32804
33765
  return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
32805
33766
  }
32806
- const g = new import_dagre5.default.graphlib.Graph({ compound: true });
33767
+ const g = new import_dagre6.default.graphlib.Graph({ compound: true });
32807
33768
  g.setGraph({
32808
33769
  rankdir: graph.direction,
32809
33770
  nodesep: 50,
@@ -32854,7 +33815,7 @@ function layoutGraph(graph, options) {
32854
33815
  label: edge.label ?? ""
32855
33816
  });
32856
33817
  }
32857
- import_dagre5.default.layout(g);
33818
+ import_dagre6.default.layout(g);
32858
33819
  const collapsedGroupIds = collapsedChildCounts ? new Set(collapsedChildCounts.keys()) : /* @__PURE__ */ new Set();
32859
33820
  const basePositioned = allNodes.map((node) => {
32860
33821
  const pos = g.node(node.id);
@@ -33050,11 +34011,11 @@ function layoutGraph(graph, options) {
33050
34011
  height: totalHeight
33051
34012
  };
33052
34013
  }
33053
- var import_dagre5, GROUP_PADDING;
34014
+ var import_dagre6, GROUP_PADDING;
33054
34015
  var init_layout9 = __esm({
33055
34016
  "src/graph/layout.ts"() {
33056
34017
  "use strict";
33057
- import_dagre5 = __toESM(require("@dagrejs/dagre"), 1);
34018
+ import_dagre6 = __toESM(require("@dagrejs/dagre"), 1);
33058
34019
  init_notes2();
33059
34020
  init_note_box();
33060
34021
  init_notes();
@@ -34860,7 +35821,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
34860
35821
  };
34861
35822
  }
34862
35823
  const isLR = computed.direction !== "TB";
34863
- const g = new import_dagre6.default.graphlib.Graph();
35824
+ const g = new import_dagre7.default.graphlib.Graph();
34864
35825
  g.setGraph({
34865
35826
  rankdir: computed.direction === "TB" ? "TB" : "LR",
34866
35827
  nodesep: isLR ? 70 : 60,
@@ -34911,7 +35872,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
34911
35872
  g.setEdge(edge.sourceId, edge.targetId, { label: edge.label });
34912
35873
  }
34913
35874
  }
34914
- import_dagre6.default.layout(g);
35875
+ import_dagre7.default.layout(g);
34915
35876
  const layoutNodes = computed.nodes.map(
34916
35877
  (node) => {
34917
35878
  const pos = g.node(node.id);
@@ -35083,11 +36044,11 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
35083
36044
  height: totalHeight
35084
36045
  };
35085
36046
  }
35086
- 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;
36047
+ 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;
35087
36048
  var init_layout10 = __esm({
35088
36049
  "src/infra/layout.ts"() {
35089
36050
  "use strict";
35090
- import_dagre6 = __toESM(require("@dagrejs/dagre"), 1);
36051
+ import_dagre7 = __toESM(require("@dagrejs/dagre"), 1);
35091
36052
  init_text_measure();
35092
36053
  MIN_NODE_WIDTH2 = 140;
35093
36054
  NODE_HEADER_HEIGHT = 28;
@@ -36654,17 +37615,17 @@ function mulberry32(seed) {
36654
37615
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
36655
37616
  };
36656
37617
  }
36657
- function standardNormal(rng) {
37618
+ function standardNormal(rng3) {
36658
37619
  let u = 0;
36659
37620
  let v = 0;
36660
- while (u === 0) u = rng();
36661
- while (v === 0) v = rng();
37621
+ while (u === 0) u = rng3();
37622
+ while (v === 0) v = rng3();
36662
37623
  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
36663
37624
  }
36664
- function gamma(shape, rng) {
37625
+ function gamma(shape, rng3) {
36665
37626
  if (shape < 1) {
36666
- const u = rng();
36667
- return gamma(shape + 1, rng) * Math.pow(u, 1 / shape);
37627
+ const u = rng3();
37628
+ return gamma(shape + 1, rng3) * Math.pow(u, 1 / shape);
36668
37629
  }
36669
37630
  const d = shape - 1 / 3;
36670
37631
  const c = 1 / Math.sqrt(9 * d);
@@ -36672,29 +37633,29 @@ function gamma(shape, rng) {
36672
37633
  let x;
36673
37634
  let v;
36674
37635
  do {
36675
- x = standardNormal(rng);
37636
+ x = standardNormal(rng3);
36676
37637
  v = 1 + c * x;
36677
37638
  } while (v <= 0);
36678
37639
  v = v * v * v;
36679
- const u = rng();
37640
+ const u = rng3();
36680
37641
  if (u < 1 - 0.0331 * x * x * x * x) return d * v;
36681
37642
  if (Math.log(u) < x * x / 2 + d * (1 - v + Math.log(v))) return d * v;
36682
37643
  }
36683
37644
  }
36684
- function sampleBeta(alpha, beta, rng) {
36685
- const x = gamma(alpha, rng);
36686
- const y = gamma(beta, rng);
37645
+ function sampleBeta(alpha, beta, rng3) {
37646
+ const x = gamma(alpha, rng3);
37647
+ const y = gamma(beta, rng3);
36687
37648
  return x / (x + y);
36688
37649
  }
36689
- function sampleBetaPert(o, m, p, rng) {
37650
+ function sampleBetaPert(o, m, p, rng3) {
36690
37651
  if (o === p) return m;
36691
37652
  const range = p - o;
36692
37653
  const alpha = 1 + 4 * (m - o) / range;
36693
37654
  const beta = 1 + 4 * (p - m) / range;
36694
- return o + sampleBeta(alpha, beta, rng) * range;
37655
+ return o + sampleBeta(alpha, beta, rng3) * range;
36695
37656
  }
36696
37657
  function simulate(resolved, expanded, _predecessors, _successors, topo, terminals, poisoned, opts) {
36697
- const rng = mulberry32(opts.seed);
37658
+ const rng3 = mulberry32(opts.seed);
36698
37659
  const expById = /* @__PURE__ */ new Map();
36699
37660
  for (const e of expanded) expById.set(e.id, e);
36700
37661
  const completionTimes = new Array(opts.trials);
@@ -36717,7 +37678,7 @@ function simulate(resolved, expanded, _predecessors, _successors, topo, terminal
36717
37678
  } else if (exp.o === exp.p) {
36718
37679
  duration = exp.m;
36719
37680
  } else {
36720
- duration = sampleBetaPert(exp.o, exp.m, exp.p, rng);
37681
+ duration = sampleBetaPert(exp.o, exp.m, exp.p, rng3);
36721
37682
  }
36722
37683
  let esLower = 0;
36723
37684
  let efLower = -Infinity;
@@ -37826,7 +38787,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
37826
38787
  }
37827
38788
  }
37828
38789
  const dagreId = (id) => memberToGroup.get(id) ?? id;
37829
- const g = new import_dagre7.default.graphlib.Graph();
38790
+ const g = new import_dagre8.default.graphlib.Graph();
37830
38791
  g.setGraph({
37831
38792
  rankdir: resolved.options.direction,
37832
38793
  nodesep: 50,
@@ -37865,7 +38826,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
37865
38826
  seenEdges.add(k);
37866
38827
  g.setEdge(src, tgt, {});
37867
38828
  }
37868
- import_dagre7.default.layout(g);
38829
+ import_dagre8.default.layout(g);
37869
38830
  const swimApplied = applySwimLanes(
37870
38831
  g,
37871
38832
  resolved,
@@ -38211,7 +39172,7 @@ function reduceCrossings2(g, direction) {
38211
39172
  buckets.get(key).push(id);
38212
39173
  }
38213
39174
  const edges = g.edges().map((e) => ({ v: e.v, w: e.w }));
38214
- const countCrossings2 = () => {
39175
+ const countCrossings = () => {
38215
39176
  let total = 0;
38216
39177
  for (let i = 0; i < edges.length; i++) {
38217
39178
  const a = edges[i];
@@ -38224,13 +39185,13 @@ function reduceCrossings2(g, direction) {
38224
39185
  const b1 = g.node(b.v);
38225
39186
  const b2 = g.node(b.w);
38226
39187
  if (!b1 || !b2) continue;
38227
- if (segmentsCross2(a1, a2, b1, b2)) total++;
39188
+ if (segmentsCross(a1, a2, b1, b2)) total++;
38228
39189
  }
38229
39190
  }
38230
39191
  return total;
38231
39192
  };
38232
39193
  const MAX_ITER = 8;
38233
- let baseline = countCrossings2();
39194
+ let baseline = countCrossings();
38234
39195
  if (baseline === 0) return;
38235
39196
  for (let iter = 0; iter < MAX_ITER; iter++) {
38236
39197
  let improved = false;
@@ -38248,7 +39209,7 @@ function reduceCrossings2(g, direction) {
38248
39209
  const bv = bn[slotAxis];
38249
39210
  an[slotAxis] = bv;
38250
39211
  bn[slotAxis] = av;
38251
- const after = countCrossings2();
39212
+ const after = countCrossings();
38252
39213
  if (after < baseline) {
38253
39214
  baseline = after;
38254
39215
  improved = true;
@@ -38269,7 +39230,7 @@ function reduceCrossings2(g, direction) {
38269
39230
  data.points = smoothEdge(src, tgt, direction);
38270
39231
  }
38271
39232
  }
38272
- function segmentsCross2(a1, a2, b1, b2) {
39233
+ function segmentsCross(a1, a2, b1, b2) {
38273
39234
  const ccw = (p, q, r) => (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x);
38274
39235
  const d1 = ccw(b1, b2, a1);
38275
39236
  const d2 = ccw(b1, b2, a2);
@@ -38277,11 +39238,11 @@ function segmentsCross2(a1, a2, b1, b2) {
38277
39238
  const d4 = ccw(a1, a2, b2);
38278
39239
  return (d1 > 0 && d2 < 0 || d1 < 0 && d2 > 0) && (d32 > 0 && d4 < 0 || d32 < 0 && d4 > 0);
38279
39240
  }
38280
- 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;
39241
+ 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;
38281
39242
  var init_layout11 = __esm({
38282
39243
  "src/pert/layout.ts"() {
38283
39244
  "use strict";
38284
- import_dagre7 = __toESM(require("@dagrejs/dagre"), 1);
39245
+ import_dagre8 = __toESM(require("@dagrejs/dagre"), 1);
38285
39246
  init_internal();
38286
39247
  init_text_measure();
38287
39248
  DEFAULT_NODE_HEIGHT = 90;
@@ -48031,10 +48992,10 @@ function tierBand(maxSpanDeg) {
48031
48992
  }
48032
48993
  function labelBudget(width, height, band) {
48033
48994
  const bandCap = {
48034
- world: 6,
48035
- continental: 5,
48036
- regional: 4,
48037
- local: 3
48995
+ world: 7,
48996
+ continental: 6,
48997
+ regional: 5,
48998
+ local: 4
48038
48999
  };
48039
49000
  const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
48040
49001
  return Math.max(0, Math.min(area2, bandCap[band]));
@@ -48167,8 +49128,11 @@ function placeContextLabels(args) {
48167
49128
  color: waterColor,
48168
49129
  fontSize: FONT,
48169
49130
  // water names keep the base font (no footprint to scale on)
48170
- // Water before any country (×1000), then by tier, then kind, then name.
48171
- sort: tier * 10 + KIND_ORDER[kind]
49131
+ // Orientation-value bands (lower = earlier): MAJOR water (oceans + major
49132
+ // seas, tier ≤ 1) leads at `tier*10+kind` (0..~16); MINOR water (tier ≥ 2:
49133
+ // smaller seas, bays, gulfs, straits) drops to band 2 (2000+) — BELOW the
49134
+ // country band (1000+), so a big country outranks a minor basin.
49135
+ sort: (tier <= 1 ? 0 : 2e3) + tier * 10 + KIND_ORDER[kind]
48172
49136
  });
48173
49137
  }
48174
49138
  const ranked = countries.map((c) => {
@@ -48181,7 +49145,7 @@ function placeContextLabels(args) {
48181
49145
  let ci = 0;
48182
49146
  for (const r of ranked) {
48183
49147
  const { c, w, h, area: area2 } = r;
48184
- if (w > width * 0.66 || h > height * 0.66) continue;
49148
+ if (!c.curatedAnchor && w > width * 0.66 && h > height * 0.66) continue;
48185
49149
  if (!insideViewport(c.anchor, width, height)) continue;
48186
49150
  const sizeFrac = Math.sqrt(area2) / canvasLinear;
48187
49151
  const t = Math.min(
@@ -48206,15 +49170,23 @@ function placeContextLabels(args) {
48206
49170
  letterSpacing: 0,
48207
49171
  color,
48208
49172
  fontSize,
48209
- // Always after every water body (+1e6); larger area = earlier.
48210
- sort: 1e6 + ci++
49173
+ // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
49174
+ // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
49175
+ // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
49176
+ // Campeche) yet never displaces an ocean. Larger area = earlier within the
49177
+ // band (`ci` is the area-desc rank index).
49178
+ sort: 1e3 + ci++
48211
49179
  });
48212
49180
  }
48213
49181
  candidates.sort((a, b) => a.sort - b.sort);
48214
49182
  const placed = [];
48215
49183
  const placedRects = [];
49184
+ const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
49185
+ const waterCap = budget - Math.min(2, countryCount);
49186
+ let waterPlaced = 0;
48216
49187
  for (const cand of candidates) {
48217
49188
  if (placed.length >= budget) break;
49189
+ if (cand.italic && waterPlaced >= waterCap) continue;
48218
49190
  const rect = rectAround(
48219
49191
  cand.cx,
48220
49192
  cand.cy,
@@ -48241,6 +49213,7 @@ function placeContextLabels(args) {
48241
49213
  if (collides(rect)) continue;
48242
49214
  if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
48243
49215
  placedRects.push(rect);
49216
+ if (cand.italic) waterPlaced++;
48244
49217
  placed.push({
48245
49218
  x: cand.cx,
48246
49219
  y: cand.cy,
@@ -50203,16 +51176,16 @@ function layoutMap(resolved, data, size, opts) {
50203
51176
  countryCandidates.push({
50204
51177
  name: f.properties?.name ?? iso,
50205
51178
  bbox: [x0, y0, x1, y1],
50206
- anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
51179
+ anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
51180
+ curatedAnchor: !!anchorLngLat
50207
51181
  });
50208
51182
  }
50209
51183
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
50210
51184
  (id) => id.startsWith("US-")
50211
51185
  );
50212
51186
  if (usLayer && framedStateContainers) {
50213
- const containerSet = new Set(resolved.poiFrameContainers);
50214
51187
  for (const [iso, f] of usLayer) {
50215
- if (containerSet.has(iso) || regionById.has(iso)) continue;
51188
+ if (regionById.has(iso)) continue;
50216
51189
  const viewF = cullFeatureToView(f);
50217
51190
  if (!viewF) continue;
50218
51191
  const b = path.bounds(viewF);
@@ -50342,8 +51315,15 @@ var init_layout15 = __esm({
50342
51315
  W_MAX = 8;
50343
51316
  FONT2 = 11;
50344
51317
  WORLD_LABEL_ANCHORS = {
50345
- US: [-98.5, 39.5]
51318
+ US: [-98.5, 39.5],
50346
51319
  // CONUS geographic centre (near Lebanon, Kansas)
51320
+ // Russia crosses the antimeridian (Chukotka at ~170°W), so on a non-global
51321
+ // (e.g. Europe) projection its geometry smears across the whole frame and the
51322
+ // area-weighted centroid lands mid-map (over Europe) — useless as a label
51323
+ // anchor. Pin it to European Russia (~Volga) so a Europe view labels visible
51324
+ // western Russia on its eastern margin; on a world view this still sits over
51325
+ // Russian land. (See the curated-anchor smear-gate bypass in context-labels.)
51326
+ RU: [45, 58]
50347
51327
  };
50348
51328
  MAX_CLUSTER_EXTENT_FACTOR = 0.18;
50349
51329
  MAX_COLUMN_ROWS = 7;