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