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