@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.js
CHANGED
|
@@ -27682,12 +27682,1344 @@ var init_renderer6 = __esm({
|
|
|
27682
27682
|
}
|
|
27683
27683
|
});
|
|
27684
27684
|
|
|
27685
|
+
// src/boxes-and-lines/layout-layered.ts
|
|
27686
|
+
function rng(s) {
|
|
27687
|
+
return () => {
|
|
27688
|
+
s |= 0;
|
|
27689
|
+
s = s + 1831565813 | 0;
|
|
27690
|
+
let t = Math.imul(s ^ s >>> 15, 1 | s);
|
|
27691
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
27692
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
27693
|
+
};
|
|
27694
|
+
}
|
|
27695
|
+
function layeredCandidates(parsed, sizes, opts) {
|
|
27696
|
+
if (parsed.groups.length > 0) return [];
|
|
27697
|
+
const nCount = parsed.nodes.length;
|
|
27698
|
+
if (nCount < 3 || nCount > 40) return [];
|
|
27699
|
+
const isTB = parsed.direction === "TB";
|
|
27700
|
+
const nodesep = opts?.nodesep ?? NODESEP;
|
|
27701
|
+
const ranksep = opts?.ranksep ?? RANKSEP;
|
|
27702
|
+
const backEdgeSide = opts?.backEdgeSide ?? "nearest";
|
|
27703
|
+
const restarts = opts?.restarts ?? (nCount <= 14 ? 28 : nCount <= 25 ? 14 : 6);
|
|
27704
|
+
const keepK = opts?.keepK ?? 3;
|
|
27705
|
+
const labels = parsed.nodes.map((n) => n.label);
|
|
27706
|
+
const labelSet = new Set(labels);
|
|
27707
|
+
const edges = parsed.edges.map((e, i) => ({ e, i })).filter(
|
|
27708
|
+
({ e }) => labelSet.has(e.source) && labelSet.has(e.target) && e.source !== e.target
|
|
27709
|
+
);
|
|
27710
|
+
if (edges.length === 0) return [];
|
|
27711
|
+
const adj = /* @__PURE__ */ new Map();
|
|
27712
|
+
for (const l of labels) adj.set(l, []);
|
|
27713
|
+
for (const { e, i } of edges)
|
|
27714
|
+
adj.get(e.source).push({ to: e.target, idx: i });
|
|
27715
|
+
const reversed = /* @__PURE__ */ new Set();
|
|
27716
|
+
const state = /* @__PURE__ */ new Map();
|
|
27717
|
+
for (const l of labels) state.set(l, 0);
|
|
27718
|
+
const dfs = (u) => {
|
|
27719
|
+
state.set(u, 1);
|
|
27720
|
+
for (const { to, idx } of adj.get(u)) {
|
|
27721
|
+
const st = state.get(to);
|
|
27722
|
+
if (st === 1)
|
|
27723
|
+
reversed.add(idx);
|
|
27724
|
+
else if (st === 0) dfs(to);
|
|
27725
|
+
}
|
|
27726
|
+
state.set(u, 2);
|
|
27727
|
+
};
|
|
27728
|
+
for (const l of labels) if (state.get(l) === 0) dfs(l);
|
|
27729
|
+
const dag = edges.map(
|
|
27730
|
+
({ 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 }
|
|
27731
|
+
);
|
|
27732
|
+
const outDag = /* @__PURE__ */ new Map();
|
|
27733
|
+
const inDeg = /* @__PURE__ */ new Map();
|
|
27734
|
+
for (const l of labels) {
|
|
27735
|
+
outDag.set(l, []);
|
|
27736
|
+
inDeg.set(l, 0);
|
|
27737
|
+
}
|
|
27738
|
+
for (const d of dag) {
|
|
27739
|
+
outDag.get(d.from).push(d.to);
|
|
27740
|
+
inDeg.set(d.to, (inDeg.get(d.to) ?? 0) + 1);
|
|
27741
|
+
}
|
|
27742
|
+
const rank = /* @__PURE__ */ new Map();
|
|
27743
|
+
for (const l of labels) rank.set(l, 0);
|
|
27744
|
+
const queue = labels.filter((l) => (inDeg.get(l) ?? 0) === 0);
|
|
27745
|
+
const indeg2 = new Map(inDeg);
|
|
27746
|
+
const topo = [];
|
|
27747
|
+
while (queue.length) {
|
|
27748
|
+
const u = queue.shift();
|
|
27749
|
+
topo.push(u);
|
|
27750
|
+
for (const v of outDag.get(u)) {
|
|
27751
|
+
rank.set(v, Math.max(rank.get(v), rank.get(u) + 1));
|
|
27752
|
+
indeg2.set(v, indeg2.get(v) - 1);
|
|
27753
|
+
if (indeg2.get(v) === 0) queue.push(v);
|
|
27754
|
+
}
|
|
27755
|
+
}
|
|
27756
|
+
if (topo.length !== labels.length) return [];
|
|
27757
|
+
const maxRank = Math.max(...labels.map((l) => rank.get(l)));
|
|
27758
|
+
const node = /* @__PURE__ */ new Map();
|
|
27759
|
+
for (const n of parsed.nodes) {
|
|
27760
|
+
const s = sizes.get(n.label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
|
|
27761
|
+
node.set(n.label, {
|
|
27762
|
+
id: n.label,
|
|
27763
|
+
dummy: false,
|
|
27764
|
+
rank: rank.get(n.label),
|
|
27765
|
+
cross: 0,
|
|
27766
|
+
thick: isTB ? s.width : s.height,
|
|
27767
|
+
depth: isTB ? s.height : s.width,
|
|
27768
|
+
realW: s.width,
|
|
27769
|
+
realH: s.height
|
|
27770
|
+
});
|
|
27771
|
+
}
|
|
27772
|
+
const segs = [];
|
|
27773
|
+
const chains = [];
|
|
27774
|
+
const backEdges = [];
|
|
27775
|
+
for (const d of dag) {
|
|
27776
|
+
if (d.rev) {
|
|
27777
|
+
backEdges.push({ edgeIdx: d.idx, src: d.to, tgt: d.from });
|
|
27778
|
+
continue;
|
|
27779
|
+
}
|
|
27780
|
+
const r0 = rank.get(d.from);
|
|
27781
|
+
const r1 = rank.get(d.to);
|
|
27782
|
+
const dagChain = [d.from];
|
|
27783
|
+
let prev = d.from;
|
|
27784
|
+
for (let r = r0 + 1; r < r1; r++) {
|
|
27785
|
+
const id = `__d${d.idx}_${r}`;
|
|
27786
|
+
node.set(id, {
|
|
27787
|
+
id,
|
|
27788
|
+
dummy: true,
|
|
27789
|
+
rank: r,
|
|
27790
|
+
cross: 0,
|
|
27791
|
+
thick: DUMMY_THICK,
|
|
27792
|
+
depth: 0,
|
|
27793
|
+
realW: 0,
|
|
27794
|
+
realH: 0
|
|
27795
|
+
});
|
|
27796
|
+
segs.push({ a: prev, b: id });
|
|
27797
|
+
dagChain.push(id);
|
|
27798
|
+
prev = id;
|
|
27799
|
+
}
|
|
27800
|
+
segs.push({ a: prev, b: d.to });
|
|
27801
|
+
dagChain.push(d.to);
|
|
27802
|
+
chains.push({
|
|
27803
|
+
edgeIdx: d.idx,
|
|
27804
|
+
chain: d.rev ? dagChain.slice().reverse() : dagChain
|
|
27805
|
+
});
|
|
27806
|
+
}
|
|
27807
|
+
const ranks = Array.from({ length: maxRank + 1 }, () => []);
|
|
27808
|
+
for (const n of node.values()) ranks[n.rank].push(n.id);
|
|
27809
|
+
const up = /* @__PURE__ */ new Map();
|
|
27810
|
+
const down = /* @__PURE__ */ new Map();
|
|
27811
|
+
for (const id of node.keys()) {
|
|
27812
|
+
up.set(id, []);
|
|
27813
|
+
down.set(id, []);
|
|
27814
|
+
}
|
|
27815
|
+
for (const s of segs) {
|
|
27816
|
+
down.get(s.a).push(s.b);
|
|
27817
|
+
up.get(s.b).push(s.a);
|
|
27818
|
+
}
|
|
27819
|
+
const orderOf = /* @__PURE__ */ new Map();
|
|
27820
|
+
const applyOrder = (ord) => {
|
|
27821
|
+
for (const id of node.keys()) orderOf.set(id, ord.get(id));
|
|
27822
|
+
};
|
|
27823
|
+
const gapCrossings = (r) => {
|
|
27824
|
+
const lower = ranks[r + 1];
|
|
27825
|
+
if (!lower) return 0;
|
|
27826
|
+
const es = [];
|
|
27827
|
+
for (const a of ranks[r]) {
|
|
27828
|
+
const pa = orderOf.get(a);
|
|
27829
|
+
for (const b of down.get(a)) es.push({ p: pa, q: orderOf.get(b) });
|
|
27830
|
+
}
|
|
27831
|
+
let c = 0;
|
|
27832
|
+
for (let i = 0; i < es.length; i++)
|
|
27833
|
+
for (let j = i + 1; j < es.length; j++) {
|
|
27834
|
+
const A = es[i], B = es[j];
|
|
27835
|
+
if ((A.p - B.p) * (A.q - B.q) < 0) c++;
|
|
27836
|
+
}
|
|
27837
|
+
return c;
|
|
27838
|
+
};
|
|
27839
|
+
const totalCrossings = () => {
|
|
27840
|
+
let c = 0;
|
|
27841
|
+
for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
|
|
27842
|
+
return c;
|
|
27843
|
+
};
|
|
27844
|
+
const median = (vals) => {
|
|
27845
|
+
if (vals.length === 0) return -1;
|
|
27846
|
+
vals.sort((x, y) => x - y);
|
|
27847
|
+
const m = Math.floor(vals.length / 2);
|
|
27848
|
+
if (vals.length % 2 === 1) return vals[m];
|
|
27849
|
+
if (vals.length === 2) return (vals[0] + vals[1]) / 2;
|
|
27850
|
+
const left = vals[m - 1] - vals[0];
|
|
27851
|
+
const right = vals[vals.length - 1] - vals[m];
|
|
27852
|
+
return left + right === 0 ? (vals[m - 1] + vals[m]) / 2 : (vals[m - 1] * right + vals[m] * left) / (left + right);
|
|
27853
|
+
};
|
|
27854
|
+
const wmedianRank = (r, useUp) => {
|
|
27855
|
+
const ids = ranks[r];
|
|
27856
|
+
const neigh = useUp ? up : down;
|
|
27857
|
+
const med = /* @__PURE__ */ new Map();
|
|
27858
|
+
for (const id of ids)
|
|
27859
|
+
med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
|
|
27860
|
+
const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
|
|
27861
|
+
const out = new Array(ids.length).fill(null);
|
|
27862
|
+
for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
|
|
27863
|
+
let mi = 0;
|
|
27864
|
+
for (let slot = 0; slot < out.length; slot++)
|
|
27865
|
+
if (out[slot] === null) out[slot] = movable[mi++];
|
|
27866
|
+
const final = out;
|
|
27867
|
+
final.forEach((id, i) => orderOf.set(id, i));
|
|
27868
|
+
ranks[r] = final;
|
|
27869
|
+
};
|
|
27870
|
+
const transpose = () => {
|
|
27871
|
+
let improved = true;
|
|
27872
|
+
let guard = 0;
|
|
27873
|
+
while (improved && guard++ < 12) {
|
|
27874
|
+
improved = false;
|
|
27875
|
+
for (let r = 0; r <= maxRank; r++) {
|
|
27876
|
+
const ids = ranks[r];
|
|
27877
|
+
for (let i = 0; i < ids.length - 1; i++) {
|
|
27878
|
+
const before = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
|
|
27879
|
+
const a = ids[i], b = ids[i + 1];
|
|
27880
|
+
ids[i] = b;
|
|
27881
|
+
ids[i + 1] = a;
|
|
27882
|
+
orderOf.set(b, i);
|
|
27883
|
+
orderOf.set(a, i + 1);
|
|
27884
|
+
const after = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
|
|
27885
|
+
if (after < before) {
|
|
27886
|
+
improved = true;
|
|
27887
|
+
} else {
|
|
27888
|
+
ids[i] = a;
|
|
27889
|
+
ids[i + 1] = b;
|
|
27890
|
+
orderOf.set(a, i);
|
|
27891
|
+
orderOf.set(b, i + 1);
|
|
27892
|
+
}
|
|
27893
|
+
}
|
|
27894
|
+
}
|
|
27895
|
+
}
|
|
27896
|
+
};
|
|
27897
|
+
const initOrder = (seed) => {
|
|
27898
|
+
const r = seed === 0 ? null : rng(seed);
|
|
27899
|
+
for (let rr = 0; rr <= maxRank; rr++) {
|
|
27900
|
+
const ids = ranks[rr].slice();
|
|
27901
|
+
if (r) {
|
|
27902
|
+
for (let i = ids.length - 1; i > 0; i--) {
|
|
27903
|
+
const j = Math.floor(r() * (i + 1));
|
|
27904
|
+
[ids[i], ids[j]] = [ids[j], ids[i]];
|
|
27905
|
+
}
|
|
27906
|
+
}
|
|
27907
|
+
ranks[rr] = ids;
|
|
27908
|
+
ids.forEach((id, i) => orderOf.set(id, i));
|
|
27909
|
+
}
|
|
27910
|
+
};
|
|
27911
|
+
const found = [];
|
|
27912
|
+
for (let s = 0; s < restarts; s++) {
|
|
27913
|
+
initOrder(s);
|
|
27914
|
+
let best = totalCrossings();
|
|
27915
|
+
let bestSnap = new Map(orderOf);
|
|
27916
|
+
for (let iter = 0; iter < 8; iter++) {
|
|
27917
|
+
const down1 = iter % 2 === 0;
|
|
27918
|
+
if (down1) for (let r = 1; r <= maxRank; r++) wmedianRank(r, true);
|
|
27919
|
+
else for (let r = maxRank - 1; r >= 0; r--) wmedianRank(r, false);
|
|
27920
|
+
transpose();
|
|
27921
|
+
const c = totalCrossings();
|
|
27922
|
+
if (c < best) {
|
|
27923
|
+
best = c;
|
|
27924
|
+
bestSnap = new Map(orderOf);
|
|
27925
|
+
}
|
|
27926
|
+
if (c === 0) break;
|
|
27927
|
+
}
|
|
27928
|
+
applyOrder(bestSnap);
|
|
27929
|
+
for (let r = 0; r <= maxRank; r++)
|
|
27930
|
+
ranks[r].sort((a, b) => orderOf.get(a) - orderOf.get(b));
|
|
27931
|
+
const sig = ranks.map((r) => r.join(",")).join("|");
|
|
27932
|
+
if (!found.some((f) => f.sig === sig))
|
|
27933
|
+
found.push({ sig, cross: best, order: new Map(bestSnap) });
|
|
27934
|
+
}
|
|
27935
|
+
found.sort((a, b) => a.cross - b.cross);
|
|
27936
|
+
const keep = found.slice(0, keepK);
|
|
27937
|
+
if (keep.length === 0) return [];
|
|
27938
|
+
const results = [];
|
|
27939
|
+
for (const cand of keep) {
|
|
27940
|
+
applyOrder(cand.order);
|
|
27941
|
+
const rk = Array.from({ length: maxRank + 1 }, () => []);
|
|
27942
|
+
for (const n of node.values()) rk[n.rank].push(n.id);
|
|
27943
|
+
for (let r = 0; r <= maxRank; r++)
|
|
27944
|
+
rk[r].sort((a, b) => cand.order.get(a) - cand.order.get(b));
|
|
27945
|
+
results.push(realize(rk));
|
|
27946
|
+
}
|
|
27947
|
+
return results;
|
|
27948
|
+
function realize(rk) {
|
|
27949
|
+
const bandDepth = rk.map(
|
|
27950
|
+
(ids) => Math.max(0, ...ids.map((id) => node.get(id).depth))
|
|
27951
|
+
);
|
|
27952
|
+
const bandCenter = [];
|
|
27953
|
+
let acc = MARGIN3;
|
|
27954
|
+
for (let r = 0; r <= maxRank; r++) {
|
|
27955
|
+
bandCenter[r] = acc + bandDepth[r] / 2;
|
|
27956
|
+
acc += bandDepth[r] + ranksep;
|
|
27957
|
+
}
|
|
27958
|
+
for (let r = 0; r <= maxRank; r++) {
|
|
27959
|
+
let x = MARGIN3;
|
|
27960
|
+
for (const id of rk[r]) {
|
|
27961
|
+
const n = node.get(id);
|
|
27962
|
+
n.cross = x + n.thick / 2;
|
|
27963
|
+
x += n.thick + nodesep;
|
|
27964
|
+
}
|
|
27965
|
+
}
|
|
27966
|
+
const ITER = 18;
|
|
27967
|
+
for (let it = 0; it < ITER; it++) {
|
|
27968
|
+
const downPass = it % 2 === 0;
|
|
27969
|
+
const order = downPass ? Array.from({ length: maxRank + 1 }, (_, i) => i) : Array.from({ length: maxRank + 1 }, (_, i) => maxRank - i);
|
|
27970
|
+
for (const r of order) {
|
|
27971
|
+
const ids = rk[r];
|
|
27972
|
+
const desired = ids.map((id) => {
|
|
27973
|
+
const n = node.get(id);
|
|
27974
|
+
let sw = 0, sx2 = 0;
|
|
27975
|
+
for (const nb of up.get(id)) {
|
|
27976
|
+
const w = weight(n, node.get(nb));
|
|
27977
|
+
sw += w;
|
|
27978
|
+
sx2 += w * node.get(nb).cross;
|
|
27979
|
+
}
|
|
27980
|
+
for (const nb of down.get(id)) {
|
|
27981
|
+
const w = weight(n, node.get(nb));
|
|
27982
|
+
sw += w;
|
|
27983
|
+
sx2 += w * node.get(nb).cross;
|
|
27984
|
+
}
|
|
27985
|
+
return sw > 0 ? sx2 / sw : n.cross;
|
|
27986
|
+
});
|
|
27987
|
+
placeWithSeparation(ids, desired);
|
|
27988
|
+
}
|
|
27989
|
+
}
|
|
27990
|
+
let minC = Infinity;
|
|
27991
|
+
for (const n of node.values()) minC = Math.min(minC, n.cross - n.thick / 2);
|
|
27992
|
+
const shift = MARGIN3 - minC;
|
|
27993
|
+
for (const n of node.values()) n.cross += shift;
|
|
27994
|
+
const coord = (n) => isTB ? { x: n.cross, y: bandCenter[n.rank] } : { x: bandCenter[n.rank], y: n.cross };
|
|
27995
|
+
const layoutNodes = parsed.nodes.map((pn) => {
|
|
27996
|
+
const n = node.get(pn.label);
|
|
27997
|
+
const c = coord(n);
|
|
27998
|
+
return {
|
|
27999
|
+
label: pn.label,
|
|
28000
|
+
x: c.x,
|
|
28001
|
+
y: c.y,
|
|
28002
|
+
width: n.realW,
|
|
28003
|
+
height: n.realH
|
|
28004
|
+
};
|
|
28005
|
+
});
|
|
28006
|
+
const chainById = /* @__PURE__ */ new Map();
|
|
28007
|
+
for (const ch of chains) chainById.set(ch.edgeIdx, ch.chain);
|
|
28008
|
+
let minCross = Infinity, maxCross = -Infinity;
|
|
28009
|
+
for (const n of node.values()) {
|
|
28010
|
+
if (n.dummy) continue;
|
|
28011
|
+
minCross = Math.min(minCross, n.cross - n.thick / 2);
|
|
28012
|
+
maxCross = Math.max(maxCross, n.cross + n.thick / 2);
|
|
28013
|
+
}
|
|
28014
|
+
const GAP = 34;
|
|
28015
|
+
const LANE = 28;
|
|
28016
|
+
const sideOf = (b) => {
|
|
28017
|
+
if (backEdgeSide === "left") return -1;
|
|
28018
|
+
if (backEdgeSide === "right") return 1;
|
|
28019
|
+
const d = node.get(b.src).cross - node.get(b.tgt).cross;
|
|
28020
|
+
return d >= 0 ? 1 : -1;
|
|
28021
|
+
};
|
|
28022
|
+
const backSorted = backEdges.map((b) => ({
|
|
28023
|
+
...b,
|
|
28024
|
+
side: sideOf(b),
|
|
28025
|
+
span: Math.abs(node.get(b.src).rank - node.get(b.tgt).rank)
|
|
28026
|
+
})).sort((a, b) => a.side - b.side || a.span - b.span);
|
|
28027
|
+
const sideCounts = /* @__PURE__ */ new Map();
|
|
28028
|
+
for (const b of backSorted)
|
|
28029
|
+
sideCounts.set(b.side, (sideCounts.get(b.side) ?? 0) + 1);
|
|
28030
|
+
const laneCounter = /* @__PURE__ */ new Map();
|
|
28031
|
+
const backPoints = /* @__PURE__ */ new Map();
|
|
28032
|
+
const faceLim = (depth) => Math.max(0, depth / 2 - 6);
|
|
28033
|
+
const hubIndex = /* @__PURE__ */ new Map();
|
|
28034
|
+
const hubSize = /* @__PURE__ */ new Map();
|
|
28035
|
+
{
|
|
28036
|
+
const counter = /* @__PURE__ */ new Map();
|
|
28037
|
+
for (const b of backSorted) {
|
|
28038
|
+
const key = `${b.side}|${b.tgt}`;
|
|
28039
|
+
const idx = counter.get(key) ?? 0;
|
|
28040
|
+
counter.set(key, idx + 1);
|
|
28041
|
+
hubIndex.set(b.edgeIdx, idx);
|
|
28042
|
+
}
|
|
28043
|
+
for (const [key, v] of counter) hubSize.set(key, v);
|
|
28044
|
+
}
|
|
28045
|
+
for (const b of backSorted) {
|
|
28046
|
+
const s = b.side;
|
|
28047
|
+
const k = laneCounter.get(s) ?? 0;
|
|
28048
|
+
laneCounter.set(s, k + 1);
|
|
28049
|
+
const K = sideCounts.get(s);
|
|
28050
|
+
const src = node.get(b.src);
|
|
28051
|
+
const tgt = node.get(b.tgt);
|
|
28052
|
+
const laneC = s > 0 ? maxCross + GAP + k * LANE : minCross - GAP - k * LANE;
|
|
28053
|
+
const srcCtr = bandCenter[src.rank];
|
|
28054
|
+
const tgtCtr = bandCenter[tgt.rank];
|
|
28055
|
+
const frac = K > 1 ? (k + 1) / (K + 1) : 0;
|
|
28056
|
+
const srcR = srcCtr + frac * faceLim(src.depth);
|
|
28057
|
+
const tgtR = tgtCtr - frac * faceLim(tgt.depth);
|
|
28058
|
+
const m = (c, r) => isTB ? { x: c, y: r } : { x: r, y: c };
|
|
28059
|
+
const off = 10;
|
|
28060
|
+
const srcEdgeC = src.cross + s * (src.thick / 2 + off);
|
|
28061
|
+
const tgtEdgeC = tgt.cross + s * (tgt.thick / 2 + off);
|
|
28062
|
+
const hubK = hubIndex.get(b.edgeIdx);
|
|
28063
|
+
const hubN = hubSize.get(`${s}|${b.tgt}`);
|
|
28064
|
+
if (hubN > 1 && hubK > 0) {
|
|
28065
|
+
const tgtTop = tgtCtr - (tgt.depth / 2 + GAP + (hubK - 1) * LANE);
|
|
28066
|
+
const entryCross = tgt.cross + s * faceLim(tgt.thick) * (hubK / hubN);
|
|
28067
|
+
backPoints.set(b.edgeIdx, [
|
|
28068
|
+
m(src.cross, srcCtr),
|
|
28069
|
+
m(srcEdgeC, srcR),
|
|
28070
|
+
m(laneC, srcR),
|
|
28071
|
+
m(laneC, tgtTop),
|
|
28072
|
+
m(entryCross, tgtTop),
|
|
28073
|
+
m(tgt.cross, tgtCtr)
|
|
28074
|
+
]);
|
|
28075
|
+
} else {
|
|
28076
|
+
backPoints.set(b.edgeIdx, [
|
|
28077
|
+
m(src.cross, srcCtr),
|
|
28078
|
+
m(srcEdgeC, srcR),
|
|
28079
|
+
m(laneC, srcR),
|
|
28080
|
+
m(laneC, tgtR),
|
|
28081
|
+
m(tgtEdgeC, tgtR),
|
|
28082
|
+
m(tgt.cross, tgtCtr)
|
|
28083
|
+
]);
|
|
28084
|
+
}
|
|
28085
|
+
}
|
|
28086
|
+
const layoutEdges = [];
|
|
28087
|
+
for (let i = 0; i < parsed.edges.length; i++) {
|
|
28088
|
+
const e = parsed.edges[i];
|
|
28089
|
+
if (!labelSet.has(e.source) || !labelSet.has(e.target)) continue;
|
|
28090
|
+
let points;
|
|
28091
|
+
if (e.source === e.target) {
|
|
28092
|
+
const c = coord(node.get(e.source));
|
|
28093
|
+
points = [c, c];
|
|
28094
|
+
} else if (backPoints.has(i)) {
|
|
28095
|
+
points = backPoints.get(i);
|
|
28096
|
+
} else {
|
|
28097
|
+
const chain = chainById.get(i);
|
|
28098
|
+
if (!chain) continue;
|
|
28099
|
+
points = chain.map((id) => coord(node.get(id)));
|
|
28100
|
+
}
|
|
28101
|
+
layoutEdges.push({
|
|
28102
|
+
source: e.source,
|
|
28103
|
+
target: e.target,
|
|
28104
|
+
...e.label !== void 0 && { label: e.label },
|
|
28105
|
+
bidirectional: e.bidirectional,
|
|
28106
|
+
lineNumber: e.lineNumber,
|
|
28107
|
+
points,
|
|
28108
|
+
yOffset: 0,
|
|
28109
|
+
parallelCount: 1,
|
|
28110
|
+
metadata: e.metadata
|
|
28111
|
+
});
|
|
28112
|
+
}
|
|
28113
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
28114
|
+
for (const n of layoutNodes) {
|
|
28115
|
+
minX = Math.min(minX, n.x - n.width / 2);
|
|
28116
|
+
minY = Math.min(minY, n.y - n.height / 2);
|
|
28117
|
+
maxX = Math.max(maxX, n.x + n.width / 2);
|
|
28118
|
+
maxY = Math.max(maxY, n.y + n.height / 2);
|
|
28119
|
+
}
|
|
28120
|
+
for (const e of layoutEdges)
|
|
28121
|
+
for (const p of e.points) {
|
|
28122
|
+
minX = Math.min(minX, p.x);
|
|
28123
|
+
minY = Math.min(minY, p.y);
|
|
28124
|
+
maxX = Math.max(maxX, p.x);
|
|
28125
|
+
maxY = Math.max(maxY, p.y);
|
|
28126
|
+
}
|
|
28127
|
+
const sx = MARGIN3 - minX;
|
|
28128
|
+
const sy = MARGIN3 - minY;
|
|
28129
|
+
const shifted = sx !== 0 || sy !== 0 ? {
|
|
28130
|
+
nodes: layoutNodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })),
|
|
28131
|
+
edges: layoutEdges.map((e) => ({
|
|
28132
|
+
...e,
|
|
28133
|
+
points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
|
|
28134
|
+
}))
|
|
28135
|
+
} : { nodes: layoutNodes, edges: layoutEdges };
|
|
28136
|
+
return {
|
|
28137
|
+
nodes: shifted.nodes,
|
|
28138
|
+
edges: shifted.edges,
|
|
28139
|
+
groups: [],
|
|
28140
|
+
width: maxX + sx + MARGIN3,
|
|
28141
|
+
height: maxY + sy + MARGIN3
|
|
28142
|
+
};
|
|
28143
|
+
}
|
|
28144
|
+
function weight(a, b) {
|
|
28145
|
+
if (a.dummy && b.dummy) return W_DUMMY_DUMMY;
|
|
28146
|
+
if (a.dummy || b.dummy) return W_REAL_DUMMY;
|
|
28147
|
+
return W_REAL_REAL;
|
|
28148
|
+
}
|
|
28149
|
+
function placeWithSeparation(ids, desired) {
|
|
28150
|
+
const n = ids.length;
|
|
28151
|
+
if (n === 0) return;
|
|
28152
|
+
const half = ids.map((id) => node.get(id).thick / 2);
|
|
28153
|
+
const off = [0];
|
|
28154
|
+
for (let i = 1; i < n; i++)
|
|
28155
|
+
off[i] = off[i - 1] + half[i - 1] + nodesep + half[i];
|
|
28156
|
+
const d = desired.map((v, i) => v - off[i]);
|
|
28157
|
+
const blocks = [];
|
|
28158
|
+
for (let i = 0; i < n; i++) {
|
|
28159
|
+
let b = { pos: d[i], count: 1, sumOffset: d[i], first: i };
|
|
28160
|
+
while (blocks.length && blocks[blocks.length - 1].pos >= b.pos) {
|
|
28161
|
+
const prev = blocks.pop();
|
|
28162
|
+
const count = prev.count + b.count;
|
|
28163
|
+
const sumOffset = prev.sumOffset + b.sumOffset;
|
|
28164
|
+
b = { pos: sumOffset / count, count, sumOffset, first: prev.first };
|
|
28165
|
+
}
|
|
28166
|
+
blocks.push(b);
|
|
28167
|
+
}
|
|
28168
|
+
const xs = new Array(n);
|
|
28169
|
+
for (const b of blocks)
|
|
28170
|
+
for (let k = 0; k < b.count; k++) xs[b.first + k] = b.pos;
|
|
28171
|
+
for (let i = 0; i < n; i++) node.get(ids[i]).cross = xs[i] + off[i];
|
|
28172
|
+
}
|
|
28173
|
+
}
|
|
28174
|
+
var NODESEP, RANKSEP, DUMMY_THICK, MARGIN3, W_REAL_REAL, W_REAL_DUMMY, W_DUMMY_DUMMY;
|
|
28175
|
+
var init_layout_layered = __esm({
|
|
28176
|
+
"src/boxes-and-lines/layout-layered.ts"() {
|
|
28177
|
+
"use strict";
|
|
28178
|
+
init_layout5();
|
|
28179
|
+
NODESEP = 50;
|
|
28180
|
+
RANKSEP = 60;
|
|
28181
|
+
DUMMY_THICK = 10;
|
|
28182
|
+
MARGIN3 = 40;
|
|
28183
|
+
W_REAL_REAL = 1;
|
|
28184
|
+
W_REAL_DUMMY = 2;
|
|
28185
|
+
W_DUMMY_DUMMY = 8;
|
|
28186
|
+
}
|
|
28187
|
+
});
|
|
28188
|
+
|
|
28189
|
+
// src/boxes-and-lines/layout-search.ts
|
|
28190
|
+
var layout_search_exports = {};
|
|
28191
|
+
__export(layout_search_exports, {
|
|
28192
|
+
countEdgeNearMiss: () => countEdgeNearMiss,
|
|
28193
|
+
countEdgeNodePierces: () => countEdgeNodePierces,
|
|
28194
|
+
countEdgeOverlaps: () => countEdgeOverlaps,
|
|
28195
|
+
countGroupOverlaps: () => countGroupOverlaps,
|
|
28196
|
+
countSplineCrossings: () => countSplineCrossings,
|
|
28197
|
+
deroutePierces: () => deroutePierces,
|
|
28198
|
+
detectEdgeNodePierces: () => detectEdgeNodePierces,
|
|
28199
|
+
detectEdgeOverlaps: () => detectEdgeOverlaps,
|
|
28200
|
+
detectGroupOverlaps: () => detectGroupOverlaps,
|
|
28201
|
+
layoutBoxesAndLinesSearch: () => layoutBoxesAndLinesSearch,
|
|
28202
|
+
separateGroupBands: () => separateGroupBands
|
|
28203
|
+
});
|
|
28204
|
+
import dagre4 from "@dagrejs/dagre";
|
|
28205
|
+
import { line as d3line, curveBasis as curveBasis5 } from "d3-shape";
|
|
28206
|
+
function rng2(s) {
|
|
28207
|
+
return () => {
|
|
28208
|
+
s |= 0;
|
|
28209
|
+
s = s + 1831565813 | 0;
|
|
28210
|
+
let t = Math.imul(s ^ s >>> 15, 1 | s);
|
|
28211
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
28212
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
28213
|
+
};
|
|
28214
|
+
}
|
|
28215
|
+
function shuffle(a, r) {
|
|
28216
|
+
const x = a.slice();
|
|
28217
|
+
for (let i = x.length - 1; i > 0; i--) {
|
|
28218
|
+
const j = Math.floor(r() * (i + 1));
|
|
28219
|
+
[x[i], x[j]] = [x[j], x[i]];
|
|
28220
|
+
}
|
|
28221
|
+
return x;
|
|
28222
|
+
}
|
|
28223
|
+
function flatten(d) {
|
|
28224
|
+
const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
|
|
28225
|
+
const pts = [];
|
|
28226
|
+
let i = 0, cx = 0, cy = 0, cmd = "";
|
|
28227
|
+
const num = () => parseFloat(toks[i++]);
|
|
28228
|
+
const samp = (p0, c1, c2, p1) => {
|
|
28229
|
+
for (let t = 0; t <= 1; t += 0.12) {
|
|
28230
|
+
const u = 1 - t;
|
|
28231
|
+
if (c2)
|
|
28232
|
+
pts.push({
|
|
28233
|
+
x: u * u * u * p0.x + 3 * u * u * t * c1.x + 3 * u * t * t * c2.x + t * t * t * p1.x,
|
|
28234
|
+
y: u * u * u * p0.y + 3 * u * u * t * c1.y + 3 * u * t * t * c2.y + t * t * t * p1.y
|
|
28235
|
+
});
|
|
28236
|
+
else
|
|
28237
|
+
pts.push({
|
|
28238
|
+
x: u * u * p0.x + 2 * u * t * c1.x + t * t * p1.x,
|
|
28239
|
+
y: u * u * p0.y + 2 * u * t * c1.y + t * t * p1.y
|
|
28240
|
+
});
|
|
28241
|
+
}
|
|
28242
|
+
};
|
|
28243
|
+
while (i < toks.length) {
|
|
28244
|
+
const tk = toks[i];
|
|
28245
|
+
if (/[MLQC]/i.test(tk)) {
|
|
28246
|
+
cmd = tk;
|
|
28247
|
+
i++;
|
|
28248
|
+
}
|
|
28249
|
+
if (cmd === "M" || cmd === "L") {
|
|
28250
|
+
const x = num(), y = num();
|
|
28251
|
+
pts.push({ x, y });
|
|
28252
|
+
cx = x;
|
|
28253
|
+
cy = y;
|
|
28254
|
+
} else if (cmd === "Q") {
|
|
28255
|
+
const c1 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
|
|
28256
|
+
samp({ x: cx, y: cy }, c1, null, p1);
|
|
28257
|
+
cx = p1.x;
|
|
28258
|
+
cy = p1.y;
|
|
28259
|
+
} else if (cmd === "C") {
|
|
28260
|
+
const c1 = { x: num(), y: num() }, c2 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
|
|
28261
|
+
samp({ x: cx, y: cy }, c1, c2, p1);
|
|
28262
|
+
cx = p1.x;
|
|
28263
|
+
cy = p1.y;
|
|
28264
|
+
} else i++;
|
|
28265
|
+
}
|
|
28266
|
+
return pts;
|
|
28267
|
+
}
|
|
28268
|
+
function segPoint(p1, p2, p3, p4) {
|
|
28269
|
+
const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
|
|
28270
|
+
if (Math.abs(den) < 1e-9) return null;
|
|
28271
|
+
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;
|
|
28272
|
+
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;
|
|
28273
|
+
}
|
|
28274
|
+
function countSplineCrossings(layout) {
|
|
28275
|
+
const center = /* @__PURE__ */ new Map();
|
|
28276
|
+
for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
|
|
28277
|
+
for (const g of layout.groups)
|
|
28278
|
+
if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
|
|
28279
|
+
const polys = layout.edges.map((e) => {
|
|
28280
|
+
const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
|
|
28281
|
+
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
28282
|
+
for (const p of pts) {
|
|
28283
|
+
if (p.x < x0) x0 = p.x;
|
|
28284
|
+
if (p.x > x1) x1 = p.x;
|
|
28285
|
+
if (p.y < y0) y0 = p.y;
|
|
28286
|
+
if (p.y > y1) y1 = p.y;
|
|
28287
|
+
}
|
|
28288
|
+
return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
|
|
28289
|
+
});
|
|
28290
|
+
const R = 34;
|
|
28291
|
+
let total = 0;
|
|
28292
|
+
for (let a = 0; a < polys.length; a++)
|
|
28293
|
+
for (let b = a + 1; b < polys.length; b++) {
|
|
28294
|
+
const A = polys[a], B = polys[b];
|
|
28295
|
+
if (A.pts.length < 2 || B.pts.length < 2) continue;
|
|
28296
|
+
if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
|
|
28297
|
+
const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
|
|
28298
|
+
const hits = [];
|
|
28299
|
+
for (let i = 1; i < A.pts.length; i++)
|
|
28300
|
+
for (let j = 1; j < B.pts.length; j++) {
|
|
28301
|
+
const p = segPoint(
|
|
28302
|
+
A.pts[i - 1],
|
|
28303
|
+
A.pts[i],
|
|
28304
|
+
B.pts[j - 1],
|
|
28305
|
+
B.pts[j]
|
|
28306
|
+
);
|
|
28307
|
+
if (!p) continue;
|
|
28308
|
+
if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
|
|
28309
|
+
continue;
|
|
28310
|
+
if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
|
|
28311
|
+
hits.push(p);
|
|
28312
|
+
}
|
|
28313
|
+
total += hits.length;
|
|
28314
|
+
}
|
|
28315
|
+
return total;
|
|
28316
|
+
}
|
|
28317
|
+
function pointSegDist(p, a, b) {
|
|
28318
|
+
const dx = b.x - a.x, dy = b.y - a.y;
|
|
28319
|
+
const len2 = dx * dx + dy * dy;
|
|
28320
|
+
if (len2 < 1e-9) return Math.hypot(p.x - a.x, p.y - a.y);
|
|
28321
|
+
let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
|
|
28322
|
+
t = Math.max(0, Math.min(1, t));
|
|
28323
|
+
return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));
|
|
28324
|
+
}
|
|
28325
|
+
function distToPoly(p, poly) {
|
|
28326
|
+
let m = Infinity;
|
|
28327
|
+
for (let i = 1; i < poly.length; i++)
|
|
28328
|
+
m = Math.min(m, pointSegDist(p, poly[i - 1], poly[i]));
|
|
28329
|
+
return m;
|
|
28330
|
+
}
|
|
28331
|
+
function pointRectDist(p, r) {
|
|
28332
|
+
const dx = Math.max(r.x - r.w / 2 - p.x, 0, p.x - (r.x + r.w / 2));
|
|
28333
|
+
const dy = Math.max(r.y - r.h / 2 - p.y, 0, p.y - (r.y + r.h / 2));
|
|
28334
|
+
return Math.hypot(dx, dy);
|
|
28335
|
+
}
|
|
28336
|
+
function detectEdgeOverlaps(layout, opts) {
|
|
28337
|
+
const dist = opts?.dist ?? 8;
|
|
28338
|
+
const minLen = opts?.minLen ?? 16;
|
|
28339
|
+
const nodeClear = opts?.nodeClear ?? 12;
|
|
28340
|
+
const rect = /* @__PURE__ */ new Map();
|
|
28341
|
+
for (const n of layout.nodes)
|
|
28342
|
+
rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
|
|
28343
|
+
for (const g of layout.groups)
|
|
28344
|
+
if (g.collapsed)
|
|
28345
|
+
rect.set("__group_" + g.label, {
|
|
28346
|
+
x: g.x,
|
|
28347
|
+
y: g.y,
|
|
28348
|
+
w: g.width,
|
|
28349
|
+
h: g.height
|
|
28350
|
+
});
|
|
28351
|
+
const polys = layout.edges.map((e) => {
|
|
28352
|
+
const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
|
|
28353
|
+
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
28354
|
+
for (const p of pts) {
|
|
28355
|
+
if (p.x < x0) x0 = p.x;
|
|
28356
|
+
if (p.x > x1) x1 = p.x;
|
|
28357
|
+
if (p.y < y0) y0 = p.y;
|
|
28358
|
+
if (p.y > y1) y1 = p.y;
|
|
28359
|
+
}
|
|
28360
|
+
return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
|
|
28361
|
+
});
|
|
28362
|
+
const runs = [];
|
|
28363
|
+
for (let a = 0; a < polys.length; a++)
|
|
28364
|
+
for (let b = a + 1; b < polys.length; b++) {
|
|
28365
|
+
const A = polys[a], B = polys[b];
|
|
28366
|
+
if (A.pts.length < 2 || B.pts.length < 2) continue;
|
|
28367
|
+
if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
|
|
28368
|
+
continue;
|
|
28369
|
+
const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
|
|
28370
|
+
let run = [];
|
|
28371
|
+
let runLen = 0;
|
|
28372
|
+
const flush = () => {
|
|
28373
|
+
if (runLen >= minLen && run.length >= 2)
|
|
28374
|
+
runs.push({
|
|
28375
|
+
mid: run[Math.floor(run.length / 2)],
|
|
28376
|
+
length: runLen,
|
|
28377
|
+
pts: run.slice()
|
|
28378
|
+
});
|
|
28379
|
+
run = [];
|
|
28380
|
+
runLen = 0;
|
|
28381
|
+
};
|
|
28382
|
+
for (const p of A.pts) {
|
|
28383
|
+
const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
|
|
28384
|
+
const covered = !nearShared && distToPoly(p, B.pts) < dist;
|
|
28385
|
+
if (covered) {
|
|
28386
|
+
if (run.length)
|
|
28387
|
+
runLen += Math.hypot(
|
|
28388
|
+
p.x - run[run.length - 1].x,
|
|
28389
|
+
p.y - run[run.length - 1].y
|
|
28390
|
+
);
|
|
28391
|
+
run.push(p);
|
|
28392
|
+
} else flush();
|
|
28393
|
+
}
|
|
28394
|
+
flush();
|
|
28395
|
+
}
|
|
28396
|
+
return runs;
|
|
28397
|
+
}
|
|
28398
|
+
function countEdgeOverlaps(layout, opts) {
|
|
28399
|
+
return detectEdgeOverlaps(layout, opts).length;
|
|
28400
|
+
}
|
|
28401
|
+
function detectEdgeNodePierces(layout, opts) {
|
|
28402
|
+
const inset = opts?.inset ?? 6;
|
|
28403
|
+
const minPts = opts?.minPts ?? 2;
|
|
28404
|
+
const rects = [];
|
|
28405
|
+
for (const n of layout.nodes)
|
|
28406
|
+
rects.push({ key: n.label, x: n.x, y: n.y, w: n.width, h: n.height });
|
|
28407
|
+
for (const g of layout.groups)
|
|
28408
|
+
if (g.collapsed)
|
|
28409
|
+
rects.push({
|
|
28410
|
+
key: "__group_" + g.label,
|
|
28411
|
+
x: g.x,
|
|
28412
|
+
y: g.y,
|
|
28413
|
+
w: g.width,
|
|
28414
|
+
h: g.height
|
|
28415
|
+
});
|
|
28416
|
+
const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
|
|
28417
|
+
const out = [];
|
|
28418
|
+
layout.edges.forEach((e, idx) => {
|
|
28419
|
+
if (e.points.length < 2) return;
|
|
28420
|
+
const poly = flatten(splineGen(e.points) ?? "");
|
|
28421
|
+
for (const r of rects) {
|
|
28422
|
+
if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
|
|
28423
|
+
continue;
|
|
28424
|
+
const hits = poly.filter((p) => inside(p, r));
|
|
28425
|
+
if (hits.length >= minPts)
|
|
28426
|
+
out.push({ edgeIdx: idx, node: r.key, pts: hits });
|
|
28427
|
+
}
|
|
28428
|
+
});
|
|
28429
|
+
return out;
|
|
28430
|
+
}
|
|
28431
|
+
function countEdgeNodePierces(layout, opts) {
|
|
28432
|
+
return detectEdgeNodePierces(layout, opts).length;
|
|
28433
|
+
}
|
|
28434
|
+
function deroutePierces(layout) {
|
|
28435
|
+
const pierces = detectEdgeNodePierces(layout);
|
|
28436
|
+
if (!pierces.length) return layout;
|
|
28437
|
+
const rect = /* @__PURE__ */ new Map();
|
|
28438
|
+
for (const n of layout.nodes)
|
|
28439
|
+
rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
|
|
28440
|
+
for (const g of layout.groups)
|
|
28441
|
+
if (g.collapsed)
|
|
28442
|
+
rect.set("__group_" + g.label, {
|
|
28443
|
+
x: g.x,
|
|
28444
|
+
y: g.y,
|
|
28445
|
+
w: g.width,
|
|
28446
|
+
h: g.height
|
|
28447
|
+
});
|
|
28448
|
+
const byEdge = /* @__PURE__ */ new Map();
|
|
28449
|
+
for (const p of pierces) {
|
|
28450
|
+
const arr = byEdge.get(p.edgeIdx);
|
|
28451
|
+
if (arr) arr.push(p.node);
|
|
28452
|
+
else byEdge.set(p.edgeIdx, [p.node]);
|
|
28453
|
+
}
|
|
28454
|
+
const edges = layout.edges.map((e, idx) => {
|
|
28455
|
+
const nodes = byEdge.get(idx);
|
|
28456
|
+
if (!nodes) return e;
|
|
28457
|
+
let pts = e.points.map((p) => ({ x: p.x, y: p.y }));
|
|
28458
|
+
for (const label of nodes) {
|
|
28459
|
+
const r = rect.get(label);
|
|
28460
|
+
if (r) pts = detourAround(pts, r);
|
|
28461
|
+
}
|
|
28462
|
+
return { ...e, points: pts };
|
|
28463
|
+
});
|
|
28464
|
+
return { ...layout, edges };
|
|
28465
|
+
}
|
|
28466
|
+
function detourAround(pts, r) {
|
|
28467
|
+
if (pts.length < 2) return pts;
|
|
28468
|
+
const c = { x: r.x, y: r.y };
|
|
28469
|
+
let bestSeg = -1;
|
|
28470
|
+
let bestT = 0;
|
|
28471
|
+
let bestD = Infinity;
|
|
28472
|
+
let bestPt = pts[0];
|
|
28473
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
28474
|
+
const a2 = pts[i], b2 = pts[i + 1];
|
|
28475
|
+
const dx = b2.x - a2.x, dy = b2.y - a2.y;
|
|
28476
|
+
const len2 = dx * dx + dy * dy || 1e-9;
|
|
28477
|
+
let t = ((c.x - a2.x) * dx + (c.y - a2.y) * dy) / len2;
|
|
28478
|
+
t = Math.max(0, Math.min(1, t));
|
|
28479
|
+
const q = { x: a2.x + t * dx, y: a2.y + t * dy };
|
|
28480
|
+
const d = Math.hypot(q.x - c.x, q.y - c.y);
|
|
28481
|
+
if (d < bestD) {
|
|
28482
|
+
bestD = d;
|
|
28483
|
+
bestSeg = i;
|
|
28484
|
+
bestT = t;
|
|
28485
|
+
bestPt = q;
|
|
28486
|
+
}
|
|
28487
|
+
}
|
|
28488
|
+
if (bestSeg < 0) return pts;
|
|
28489
|
+
let nx = bestPt.x - c.x;
|
|
28490
|
+
let ny = bestPt.y - c.y;
|
|
28491
|
+
if (Math.hypot(nx, ny) < 1) {
|
|
28492
|
+
if (r.w <= r.h) {
|
|
28493
|
+
nx = 1;
|
|
28494
|
+
ny = 0;
|
|
28495
|
+
} else {
|
|
28496
|
+
nx = 0;
|
|
28497
|
+
ny = 1;
|
|
28498
|
+
}
|
|
28499
|
+
}
|
|
28500
|
+
const nlen = Math.hypot(nx, ny) || 1;
|
|
28501
|
+
nx /= nlen;
|
|
28502
|
+
ny /= nlen;
|
|
28503
|
+
const a = pts[bestSeg], b = pts[bestSeg + 1];
|
|
28504
|
+
let ex = b.x - a.x, ey = b.y - a.y;
|
|
28505
|
+
const elen = Math.hypot(ex, ey) || 1;
|
|
28506
|
+
ex /= elen;
|
|
28507
|
+
ey /= elen;
|
|
28508
|
+
const clear = Math.hypot(r.w, r.h) / 2 + 18;
|
|
28509
|
+
const hw = Math.hypot(r.w, r.h) / 2;
|
|
28510
|
+
void bestT;
|
|
28511
|
+
const before = {
|
|
28512
|
+
x: bestPt.x - ex * hw + nx * clear * 0.5,
|
|
28513
|
+
y: bestPt.y - ey * hw + ny * clear * 0.5
|
|
28514
|
+
};
|
|
28515
|
+
const peak = { x: c.x + nx * clear, y: c.y + ny * clear };
|
|
28516
|
+
const after = {
|
|
28517
|
+
x: bestPt.x + ex * hw + nx * clear * 0.5,
|
|
28518
|
+
y: bestPt.y + ey * hw + ny * clear * 0.5
|
|
28519
|
+
};
|
|
28520
|
+
const out = pts.slice(0, bestSeg + 1);
|
|
28521
|
+
out.push(before, peak, after);
|
|
28522
|
+
out.push(...pts.slice(bestSeg + 1));
|
|
28523
|
+
return out;
|
|
28524
|
+
}
|
|
28525
|
+
function countEdgeNearMiss(layout, opts) {
|
|
28526
|
+
const near = opts?.near ?? 14;
|
|
28527
|
+
const tight = opts?.tight ?? 8;
|
|
28528
|
+
const minLen = opts?.minLen ?? 18;
|
|
28529
|
+
const close = detectEdgeOverlaps(layout, { dist: near, minLen }).length;
|
|
28530
|
+
const over = detectEdgeOverlaps(layout, { dist: tight, minLen }).length;
|
|
28531
|
+
return Math.max(0, close - over);
|
|
28532
|
+
}
|
|
28533
|
+
function detectGroupOverlaps(layout, opts) {
|
|
28534
|
+
const margin = opts?.margin ?? 4;
|
|
28535
|
+
const raw = layout.groups.map((g) => ({
|
|
28536
|
+
label: g.label,
|
|
28537
|
+
l: g.x - g.width / 2,
|
|
28538
|
+
r: g.x + g.width / 2,
|
|
28539
|
+
t: g.y - g.height / 2,
|
|
28540
|
+
b: g.y + g.height / 2
|
|
28541
|
+
}));
|
|
28542
|
+
const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
|
|
28543
|
+
const rend = raw.map((a, i) => {
|
|
28544
|
+
const parent = raw.some((b, j) => j !== i && contains(a, b));
|
|
28545
|
+
return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
|
|
28546
|
+
});
|
|
28547
|
+
const hit = /* @__PURE__ */ new Set();
|
|
28548
|
+
for (let i = 0; i < raw.length; i++)
|
|
28549
|
+
for (let j = i + 1; j < raw.length; j++) {
|
|
28550
|
+
if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
|
|
28551
|
+
const a = rend[i], b = rend[j];
|
|
28552
|
+
const dx = Math.max(a.l - b.r, b.l - a.r);
|
|
28553
|
+
const dy = Math.max(a.t - b.b, b.t - a.b);
|
|
28554
|
+
if (Math.max(dx, dy) < margin) {
|
|
28555
|
+
hit.add(raw[i].label);
|
|
28556
|
+
hit.add(raw[j].label);
|
|
28557
|
+
}
|
|
28558
|
+
}
|
|
28559
|
+
return [...hit];
|
|
28560
|
+
}
|
|
28561
|
+
function countGroupOverlaps(layout, opts) {
|
|
28562
|
+
const margin = opts?.margin ?? 4;
|
|
28563
|
+
const raw = layout.groups.map((g) => ({
|
|
28564
|
+
l: g.x - g.width / 2,
|
|
28565
|
+
r: g.x + g.width / 2,
|
|
28566
|
+
t: g.y - g.height / 2,
|
|
28567
|
+
b: g.y + g.height / 2
|
|
28568
|
+
}));
|
|
28569
|
+
const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
|
|
28570
|
+
const rend = raw.map((a, i) => {
|
|
28571
|
+
const parent = raw.some((b, j) => j !== i && contains(a, b));
|
|
28572
|
+
return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
|
|
28573
|
+
});
|
|
28574
|
+
let count = 0;
|
|
28575
|
+
for (let i = 0; i < raw.length; i++)
|
|
28576
|
+
for (let j = i + 1; j < raw.length; j++) {
|
|
28577
|
+
if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
|
|
28578
|
+
const a = rend[i], b = rend[j];
|
|
28579
|
+
const dx = Math.max(a.l - b.r, b.l - a.r);
|
|
28580
|
+
const dy = Math.max(a.t - b.b, b.t - a.b);
|
|
28581
|
+
if (Math.max(dx, dy) < margin) count++;
|
|
28582
|
+
}
|
|
28583
|
+
return count;
|
|
28584
|
+
}
|
|
28585
|
+
function separateGroupBands(layout, parsed) {
|
|
28586
|
+
if (layout.groups.some((g) => g.collapsed)) return layout;
|
|
28587
|
+
if (countGroupOverlaps(layout) === 0) return layout;
|
|
28588
|
+
const parentOf = /* @__PURE__ */ new Map();
|
|
28589
|
+
for (const g of parsed.groups) parentOf.set(g.label, g.parentGroup);
|
|
28590
|
+
const topOf = (label) => {
|
|
28591
|
+
let cur = label;
|
|
28592
|
+
const seen = /* @__PURE__ */ new Set();
|
|
28593
|
+
for (; ; ) {
|
|
28594
|
+
const p = parentOf.get(cur);
|
|
28595
|
+
if (!p || seen.has(p)) return cur;
|
|
28596
|
+
seen.add(cur);
|
|
28597
|
+
cur = p;
|
|
28598
|
+
}
|
|
28599
|
+
};
|
|
28600
|
+
const topLabels = parsed.groups.filter((g) => !g.parentGroup).map((g) => g.label);
|
|
28601
|
+
if (topLabels.length < 2) return layout;
|
|
28602
|
+
const nodeTop = /* @__PURE__ */ new Map();
|
|
28603
|
+
for (const g of parsed.groups)
|
|
28604
|
+
for (const child of g.children)
|
|
28605
|
+
if (!parsed.groups.some((gg) => gg.label === child))
|
|
28606
|
+
nodeTop.set(child, topOf(g.label));
|
|
28607
|
+
const axis = parsed.direction === "TB" ? "x" : "y";
|
|
28608
|
+
const boxByLabel = new Map(layout.groups.map((g) => [g.label, g]));
|
|
28609
|
+
const bands = topLabels.map((label) => {
|
|
28610
|
+
const g = boxByLabel.get(label);
|
|
28611
|
+
const half2 = (axis === "y" ? g.height : g.width) / 2;
|
|
28612
|
+
let lo = (axis === "y" ? g.y : g.x) - half2;
|
|
28613
|
+
const hi = (axis === "y" ? g.y : g.x) + half2;
|
|
28614
|
+
const isParent = parsed.groups.some((c) => c.parentGroup === label);
|
|
28615
|
+
if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE2;
|
|
28616
|
+
return { label, lo, hi, c: (lo + hi) / 2 };
|
|
28617
|
+
});
|
|
28618
|
+
bands.sort((a, b) => a.c - b.c);
|
|
28619
|
+
const GAP = 16;
|
|
28620
|
+
const half = bands.map((b) => (b.hi - b.lo) / 2);
|
|
28621
|
+
const off = [0];
|
|
28622
|
+
for (let i = 1; i < bands.length; i++)
|
|
28623
|
+
off[i] = off[i - 1] + half[i - 1] + GAP + half[i];
|
|
28624
|
+
const desired = bands.map((b, i) => b.c - off[i]);
|
|
28625
|
+
const blocks = [];
|
|
28626
|
+
for (let i = 0; i < bands.length; i++) {
|
|
28627
|
+
let blk = { pos: desired[i], count: 1, sum: desired[i], first: i };
|
|
28628
|
+
while (blocks.length && blocks[blocks.length - 1].pos >= blk.pos) {
|
|
28629
|
+
const prev = blocks.pop();
|
|
28630
|
+
const count = prev.count + blk.count;
|
|
28631
|
+
const sum = prev.sum + blk.sum;
|
|
28632
|
+
blk = { pos: sum / count, count, sum, first: prev.first };
|
|
28633
|
+
}
|
|
28634
|
+
blocks.push(blk);
|
|
28635
|
+
}
|
|
28636
|
+
const newC = new Array(bands.length);
|
|
28637
|
+
for (const blk of blocks)
|
|
28638
|
+
for (let k = 0; k < blk.count; k++)
|
|
28639
|
+
newC[blk.first + k] = blk.pos + off[blk.first + k];
|
|
28640
|
+
const delta = /* @__PURE__ */ new Map();
|
|
28641
|
+
let moved = false;
|
|
28642
|
+
bands.forEach((b, i) => {
|
|
28643
|
+
const d = newC[i] - b.c;
|
|
28644
|
+
delta.set(b.label, d);
|
|
28645
|
+
if (Math.abs(d) > 0.5) moved = true;
|
|
28646
|
+
});
|
|
28647
|
+
if (!moved) return layout;
|
|
28648
|
+
const nodeDelta = (label) => {
|
|
28649
|
+
const top = nodeTop.get(label);
|
|
28650
|
+
return top ? delta.get(top) ?? 0 : 0;
|
|
28651
|
+
};
|
|
28652
|
+
const shift = (p, d) => axis === "y" ? { x: p.x, y: p.y + d } : { x: p.x + d, y: p.y };
|
|
28653
|
+
const nodes = layout.nodes.map((n) => {
|
|
28654
|
+
const d = nodeDelta(n.label);
|
|
28655
|
+
return d ? { ...n, ...axis === "y" ? { y: n.y + d } : { x: n.x + d } } : n;
|
|
28656
|
+
});
|
|
28657
|
+
const groups = layout.groups.map((g) => {
|
|
28658
|
+
const d = delta.get(topOf(g.label)) ?? 0;
|
|
28659
|
+
return d ? { ...g, ...axis === "y" ? { y: g.y + d } : { x: g.x + d } } : g;
|
|
28660
|
+
});
|
|
28661
|
+
const edges = layout.edges.map((e) => {
|
|
28662
|
+
const ds = nodeDelta(e.source);
|
|
28663
|
+
const dt = nodeDelta(e.target);
|
|
28664
|
+
if (!ds && !dt) return e;
|
|
28665
|
+
const N = e.points.length;
|
|
28666
|
+
const points = e.points.map((p, i) => {
|
|
28667
|
+
const f = N > 1 ? i / (N - 1) : 0;
|
|
28668
|
+
return shift(p, ds * (1 - f) + dt * f);
|
|
28669
|
+
});
|
|
28670
|
+
return { ...e, points };
|
|
28671
|
+
});
|
|
28672
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
28673
|
+
const acc = (x, y) => {
|
|
28674
|
+
if (x < minX) minX = x;
|
|
28675
|
+
if (x > maxX) maxX = x;
|
|
28676
|
+
if (y < minY) minY = y;
|
|
28677
|
+
if (y > maxY) maxY = y;
|
|
28678
|
+
};
|
|
28679
|
+
for (const n of nodes) {
|
|
28680
|
+
acc(n.x - n.width / 2, n.y - n.height / 2);
|
|
28681
|
+
acc(n.x + n.width / 2, n.y + n.height / 2);
|
|
28682
|
+
}
|
|
28683
|
+
for (const g of groups) {
|
|
28684
|
+
acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE2);
|
|
28685
|
+
acc(g.x + g.width / 2, g.y + g.height / 2);
|
|
28686
|
+
}
|
|
28687
|
+
for (const e of edges) for (const p of e.points) acc(p.x, p.y);
|
|
28688
|
+
const M = 40;
|
|
28689
|
+
const sx = M - minX, sy = M - minY;
|
|
28690
|
+
const reshift = sx !== 0 || sy !== 0;
|
|
28691
|
+
return {
|
|
28692
|
+
nodes: reshift ? nodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })) : nodes,
|
|
28693
|
+
groups: reshift ? groups.map((g) => ({ ...g, x: g.x + sx, y: g.y + sy })) : groups,
|
|
28694
|
+
edges: reshift ? edges.map((e) => ({
|
|
28695
|
+
...e,
|
|
28696
|
+
points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
|
|
28697
|
+
})) : edges,
|
|
28698
|
+
width: maxX - minX + 2 * M,
|
|
28699
|
+
height: maxY - minY + 2 * M
|
|
28700
|
+
};
|
|
28701
|
+
}
|
|
28702
|
+
function segCross(p1, p2, p3, p4) {
|
|
28703
|
+
const d1x = p2.x - p1.x, d1y = p2.y - p1.y, d2x = p4.x - p3.x, d2y = p4.y - p3.y;
|
|
28704
|
+
const den = d1x * d2y - d1y * d2x;
|
|
28705
|
+
if (Math.abs(den) < 1e-9) return false;
|
|
28706
|
+
const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / den;
|
|
28707
|
+
const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / den;
|
|
28708
|
+
return t > 1e-3 && t < 0.999 && s > 1e-3 && s < 0.999;
|
|
28709
|
+
}
|
|
28710
|
+
function countCrossingsFast(layout) {
|
|
28711
|
+
const E = layout.edges.filter((e) => e.points.length >= 2);
|
|
28712
|
+
const bb = E.map((e) => {
|
|
28713
|
+
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
28714
|
+
for (const p of e.points) {
|
|
28715
|
+
if (p.x < x0) x0 = p.x;
|
|
28716
|
+
if (p.x > x1) x1 = p.x;
|
|
28717
|
+
if (p.y < y0) y0 = p.y;
|
|
28718
|
+
if (p.y > y1) y1 = p.y;
|
|
28719
|
+
}
|
|
28720
|
+
return { x0, y0, x1, y1 };
|
|
28721
|
+
});
|
|
28722
|
+
let count = 0;
|
|
28723
|
+
for (let i = 0; i < E.length; i++)
|
|
28724
|
+
for (let j = i + 1; j < E.length; j++) {
|
|
28725
|
+
const A = E[i], B = E[j];
|
|
28726
|
+
if (A.source === B.source || A.source === B.target || A.target === B.source || A.target === B.target)
|
|
28727
|
+
continue;
|
|
28728
|
+
const a = bb[i], b = bb[j];
|
|
28729
|
+
if (a.x1 < b.x0 || b.x1 < a.x0 || a.y1 < b.y0 || b.y1 < a.y0) continue;
|
|
28730
|
+
const pa = A.points, pb = B.points;
|
|
28731
|
+
let hit = false;
|
|
28732
|
+
for (let ai = 0; ai < pa.length - 1 && !hit; ai++)
|
|
28733
|
+
for (let bi = 0; bi < pb.length - 1; bi++) {
|
|
28734
|
+
if (segCross(pa[ai], pa[ai + 1], pb[bi], pb[bi + 1])) {
|
|
28735
|
+
hit = true;
|
|
28736
|
+
break;
|
|
28737
|
+
}
|
|
28738
|
+
}
|
|
28739
|
+
if (hit) count++;
|
|
28740
|
+
}
|
|
28741
|
+
return count;
|
|
28742
|
+
}
|
|
28743
|
+
function meanDrift(layout, prev) {
|
|
28744
|
+
if (!prev?.size) return 0;
|
|
28745
|
+
let sum = 0, n = 0;
|
|
28746
|
+
for (const node of layout.nodes) {
|
|
28747
|
+
const p = prev.get(node.label);
|
|
28748
|
+
if (p) {
|
|
28749
|
+
sum += Math.hypot(node.x - p.x, node.y - p.y);
|
|
28750
|
+
n++;
|
|
28751
|
+
}
|
|
28752
|
+
}
|
|
28753
|
+
return n ? sum / n : 0;
|
|
28754
|
+
}
|
|
28755
|
+
function edgeLength(layout) {
|
|
28756
|
+
let total = 0;
|
|
28757
|
+
for (const e of layout.edges)
|
|
28758
|
+
for (let i = 1; i < e.points.length; i++)
|
|
28759
|
+
total += Math.hypot(
|
|
28760
|
+
e.points[i].x - e.points[i - 1].x,
|
|
28761
|
+
e.points[i].y - e.points[i - 1].y
|
|
28762
|
+
);
|
|
28763
|
+
return total;
|
|
28764
|
+
}
|
|
28765
|
+
function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
|
|
28766
|
+
const hideDescriptions = opts?.hideDescriptions ?? false;
|
|
28767
|
+
const collapsedGroupLabels = /* @__PURE__ */ new Set();
|
|
28768
|
+
if (collapseInfo) {
|
|
28769
|
+
const missing = /* @__PURE__ */ new Set();
|
|
28770
|
+
for (const og of collapseInfo.originalGroups)
|
|
28771
|
+
if (!parsed.groups.some((g) => g.label === og.label))
|
|
28772
|
+
missing.add(og.label);
|
|
28773
|
+
for (const label of missing) {
|
|
28774
|
+
const og = collapseInfo.originalGroups.find((g) => g.label === label);
|
|
28775
|
+
const parent = og?.parentGroup;
|
|
28776
|
+
if (!parent || !missing.has(parent)) collapsedGroupLabels.add(label);
|
|
28777
|
+
}
|
|
28778
|
+
}
|
|
28779
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
28780
|
+
let maxDescH = 0;
|
|
28781
|
+
for (const node of parsed.nodes) {
|
|
28782
|
+
const s = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
|
|
28783
|
+
sizes.set(node.label, s);
|
|
28784
|
+
if (!hideDescriptions && node.description && node.description.length > 0)
|
|
28785
|
+
maxDescH = Math.max(maxDescH, s.height);
|
|
28786
|
+
}
|
|
28787
|
+
if (maxDescH > 0) {
|
|
28788
|
+
for (const node of parsed.nodes)
|
|
28789
|
+
if (node.description && node.description.length > 0) {
|
|
28790
|
+
const s = sizes.get(node.label);
|
|
28791
|
+
sizes.set(node.label, { width: s.width, height: maxDescH });
|
|
28792
|
+
}
|
|
28793
|
+
}
|
|
28794
|
+
const gid = (label) => `__group_${label}`;
|
|
28795
|
+
const rankdir = parsed.direction === "TB" ? "TB" : "LR";
|
|
28796
|
+
function place(cfg) {
|
|
28797
|
+
const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
|
|
28798
|
+
const ord = (a) => r ? shuffle(a, r) : a.slice();
|
|
28799
|
+
const g = new dagre4.graphlib.Graph({ compound: true, multigraph: true });
|
|
28800
|
+
g.setGraph({
|
|
28801
|
+
rankdir,
|
|
28802
|
+
ranker: cfg.ranker,
|
|
28803
|
+
nodesep: cfg.nodesep,
|
|
28804
|
+
ranksep: cfg.ranksep,
|
|
28805
|
+
edgesep: 20,
|
|
28806
|
+
marginx: 40,
|
|
28807
|
+
marginy: 40
|
|
28808
|
+
});
|
|
28809
|
+
g.setDefaultEdgeLabel(() => ({}));
|
|
28810
|
+
for (const grp of ord(parsed.groups))
|
|
28811
|
+
g.setNode(gid(grp.label), { label: grp.label });
|
|
28812
|
+
for (const node of ord(parsed.nodes)) {
|
|
28813
|
+
const s = sizes.get(node.label);
|
|
28814
|
+
g.setNode(node.label, { width: s.width, height: s.height });
|
|
28815
|
+
}
|
|
28816
|
+
for (const label of collapsedGroupLabels)
|
|
28817
|
+
g.setNode(gid(label), { width: NODE_WIDTH, height: NODE_HEIGHT });
|
|
28818
|
+
for (const grp of parsed.groups) {
|
|
28819
|
+
if (grp.parentGroup && g.hasNode(gid(grp.parentGroup)))
|
|
28820
|
+
g.setParent(gid(grp.label), gid(grp.parentGroup));
|
|
28821
|
+
for (const c of ord(grp.children)) {
|
|
28822
|
+
if (g.hasNode(c)) g.setParent(c, gid(grp.label));
|
|
28823
|
+
}
|
|
28824
|
+
}
|
|
28825
|
+
if (collapseInfo)
|
|
28826
|
+
for (const label of collapsedGroupLabels) {
|
|
28827
|
+
const og = collapseInfo.originalGroups.find((x) => x.label === label);
|
|
28828
|
+
if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && g.hasNode(gid(og.parentGroup)))
|
|
28829
|
+
g.setParent(gid(label), gid(og.parentGroup));
|
|
28830
|
+
}
|
|
28831
|
+
for (const e of ord(parsed.edges))
|
|
28832
|
+
if (g.hasNode(e.source) && g.hasNode(e.target))
|
|
28833
|
+
g.setEdge(e.source, e.target, {});
|
|
28834
|
+
dagre4.layout(g);
|
|
28835
|
+
const nodes = parsed.nodes.map((n2) => {
|
|
28836
|
+
const p = g.node(n2.label);
|
|
28837
|
+
return {
|
|
28838
|
+
label: n2.label,
|
|
28839
|
+
x: p.x,
|
|
28840
|
+
y: p.y,
|
|
28841
|
+
width: p.width,
|
|
28842
|
+
height: p.height
|
|
28843
|
+
};
|
|
28844
|
+
});
|
|
28845
|
+
const groups = parsed.groups.map(
|
|
28846
|
+
(grp) => {
|
|
28847
|
+
const p = g.node(gid(grp.label));
|
|
28848
|
+
return {
|
|
28849
|
+
label: grp.label,
|
|
28850
|
+
lineNumber: grp.lineNumber,
|
|
28851
|
+
x: p.x,
|
|
28852
|
+
y: p.y,
|
|
28853
|
+
width: p.width,
|
|
28854
|
+
height: p.height,
|
|
28855
|
+
collapsed: false,
|
|
28856
|
+
childCount: grp.children.length
|
|
28857
|
+
};
|
|
28858
|
+
}
|
|
28859
|
+
);
|
|
28860
|
+
for (const label of collapsedGroupLabels) {
|
|
28861
|
+
const p = g.node(gid(label));
|
|
28862
|
+
groups.push({
|
|
28863
|
+
label,
|
|
28864
|
+
lineNumber: 0,
|
|
28865
|
+
x: p.x,
|
|
28866
|
+
y: p.y,
|
|
28867
|
+
width: p.width,
|
|
28868
|
+
height: p.height,
|
|
28869
|
+
collapsed: true,
|
|
28870
|
+
childCount: collapseInfo?.collapsedChildCounts.get(label) ?? 0
|
|
28871
|
+
});
|
|
28872
|
+
}
|
|
28873
|
+
const edges = parsed.edges.filter((e) => g.hasEdge(e.source, e.target)).map((e) => {
|
|
28874
|
+
const ed = g.edge(e.source, e.target);
|
|
28875
|
+
return {
|
|
28876
|
+
source: e.source,
|
|
28877
|
+
target: e.target,
|
|
28878
|
+
...e.label !== void 0 && { label: e.label },
|
|
28879
|
+
bidirectional: e.bidirectional,
|
|
28880
|
+
lineNumber: e.lineNumber,
|
|
28881
|
+
points: ed?.points ?? [],
|
|
28882
|
+
yOffset: 0,
|
|
28883
|
+
parallelCount: 1,
|
|
28884
|
+
metadata: e.metadata
|
|
28885
|
+
};
|
|
28886
|
+
});
|
|
28887
|
+
const gg = g.graph();
|
|
28888
|
+
return {
|
|
28889
|
+
nodes,
|
|
28890
|
+
edges,
|
|
28891
|
+
groups,
|
|
28892
|
+
width: gg.width ?? 800,
|
|
28893
|
+
height: gg.height ?? 600
|
|
28894
|
+
};
|
|
28895
|
+
}
|
|
28896
|
+
const n = parsed.nodes.length;
|
|
28897
|
+
const seedCount = opts?.seeds ?? (n <= 12 ? 80 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
|
|
28898
|
+
const REFINE_K = opts?.refineK ?? 6;
|
|
28899
|
+
const lambda = opts?.lambda ?? DEFAULT_LAMBDA;
|
|
28900
|
+
const prev = opts?.previousPositions;
|
|
28901
|
+
const RANKERS = ["network-simplex", "tight-tree", "longest-path"];
|
|
28902
|
+
const SPACINGS = [
|
|
28903
|
+
{ nodesep: 50, ranksep: 60 },
|
|
28904
|
+
{ nodesep: 34, ranksep: 46 },
|
|
28905
|
+
{ nodesep: 66, ranksep: 82 }
|
|
28906
|
+
];
|
|
28907
|
+
const configs = [];
|
|
28908
|
+
for (const ranker of RANKERS)
|
|
28909
|
+
for (const sp of SPACINGS) configs.push({ ranker, ...sp });
|
|
28910
|
+
for (let s = 0; s < seedCount; s++)
|
|
28911
|
+
configs.push({
|
|
28912
|
+
ranker: "network-simplex",
|
|
28913
|
+
nodesep: 50,
|
|
28914
|
+
ranksep: 60,
|
|
28915
|
+
seed: s
|
|
28916
|
+
});
|
|
28917
|
+
const badness = (lay, floor) => {
|
|
28918
|
+
const x = countSplineCrossings(lay);
|
|
28919
|
+
if (x > floor) return Infinity;
|
|
28920
|
+
return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
|
|
28921
|
+
};
|
|
28922
|
+
const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
|
|
28923
|
+
const pool = [];
|
|
28924
|
+
for (const cfg of configs) {
|
|
28925
|
+
try {
|
|
28926
|
+
pool.push(place(cfg));
|
|
28927
|
+
} catch {
|
|
28928
|
+
}
|
|
28929
|
+
}
|
|
28930
|
+
if (!pool.length)
|
|
28931
|
+
return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
|
|
28932
|
+
let layered = [];
|
|
28933
|
+
try {
|
|
28934
|
+
layered = layeredCandidates(parsed, sizes);
|
|
28935
|
+
} catch {
|
|
28936
|
+
}
|
|
28937
|
+
pool.sort(
|
|
28938
|
+
(a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
|
|
28939
|
+
);
|
|
28940
|
+
const refineK = Math.min(REFINE_K, pool.length);
|
|
28941
|
+
let best = pool[0];
|
|
28942
|
+
let bestObj = Infinity;
|
|
28943
|
+
let bestBad = Infinity;
|
|
28944
|
+
const consider = (lay) => {
|
|
28945
|
+
const bad = badness(lay, bestBad);
|
|
28946
|
+
if (bad === Infinity) return;
|
|
28947
|
+
const sc = objective(lay, bad);
|
|
28948
|
+
if (sc < bestObj) {
|
|
28949
|
+
bestObj = sc;
|
|
28950
|
+
bestBad = bad;
|
|
28951
|
+
best = lay;
|
|
28952
|
+
}
|
|
28953
|
+
};
|
|
28954
|
+
for (const lay of pool.slice(0, refineK)) consider(lay);
|
|
28955
|
+
if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
|
|
28956
|
+
const extra = [];
|
|
28957
|
+
for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
|
|
28958
|
+
try {
|
|
28959
|
+
extra.push(
|
|
28960
|
+
place({
|
|
28961
|
+
ranker: "network-simplex",
|
|
28962
|
+
nodesep: 50,
|
|
28963
|
+
ranksep: 60,
|
|
28964
|
+
seed: s
|
|
28965
|
+
})
|
|
28966
|
+
);
|
|
28967
|
+
} catch {
|
|
28968
|
+
}
|
|
28969
|
+
}
|
|
28970
|
+
extra.sort(
|
|
28971
|
+
(a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
|
|
28972
|
+
);
|
|
28973
|
+
for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
|
|
28974
|
+
}
|
|
28975
|
+
for (const lay of layered) {
|
|
28976
|
+
const variants = [lay];
|
|
28977
|
+
const dp = deroutePierces(lay);
|
|
28978
|
+
if (dp !== lay) variants.push(dp);
|
|
28979
|
+
for (const v of variants) {
|
|
28980
|
+
const bad = badness(v, bestBad - 1);
|
|
28981
|
+
if (bad < bestBad) {
|
|
28982
|
+
bestBad = bad;
|
|
28983
|
+
best = v;
|
|
28984
|
+
}
|
|
28985
|
+
}
|
|
28986
|
+
}
|
|
28987
|
+
if (bestBad > 0) {
|
|
28988
|
+
const rerouted = deroutePierces(best);
|
|
28989
|
+
if (rerouted !== best && badness(rerouted, bestBad - 1) < bestBad)
|
|
28990
|
+
best = rerouted;
|
|
28991
|
+
}
|
|
28992
|
+
if (bestBad > 0 && countGroupOverlaps(best) > 0) {
|
|
28993
|
+
const separated = separateGroupBands(best, parsed);
|
|
28994
|
+
if (separated !== best && badness(separated, bestBad - 1) < bestBad)
|
|
28995
|
+
best = separated;
|
|
28996
|
+
}
|
|
28997
|
+
return best;
|
|
28998
|
+
}
|
|
28999
|
+
var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
|
|
29000
|
+
var init_layout_search = __esm({
|
|
29001
|
+
"src/boxes-and-lines/layout-search.ts"() {
|
|
29002
|
+
"use strict";
|
|
29003
|
+
init_layout5();
|
|
29004
|
+
init_layout_layered();
|
|
29005
|
+
DEFAULT_LAMBDA = 4;
|
|
29006
|
+
ESCALATE_THRESHOLD = 4;
|
|
29007
|
+
ESCALATE_MAX_N = 45;
|
|
29008
|
+
ESCALATE_SEEDS = 18;
|
|
29009
|
+
ESCALATE_REFINE = 10;
|
|
29010
|
+
splineGen = d3line().x((d) => d.x).y((d) => d.y).curve(curveBasis5);
|
|
29011
|
+
GROUP_LABEL_ZONE2 = 32;
|
|
29012
|
+
}
|
|
29013
|
+
});
|
|
29014
|
+
|
|
27685
29015
|
// src/boxes-and-lines/layout.ts
|
|
27686
29016
|
var layout_exports5 = {};
|
|
27687
29017
|
__export(layout_exports5, {
|
|
29018
|
+
NODE_HEIGHT: () => NODE_HEIGHT,
|
|
29019
|
+
NODE_WIDTH: () => NODE_WIDTH,
|
|
29020
|
+
computeNodeSize: () => computeNodeSize,
|
|
27688
29021
|
layoutBoxesAndLines: () => layoutBoxesAndLines
|
|
27689
29022
|
});
|
|
27690
|
-
import ELK from "elkjs/lib/elk.bundled.js";
|
|
27691
29023
|
function splitCamelCase2(word) {
|
|
27692
29024
|
const parts = [];
|
|
27693
29025
|
let start = 0;
|
|
@@ -27758,417 +29090,21 @@ function computeNodeSize(node, reserveValueRow) {
|
|
|
27758
29090
|
const totalHeight = labelHeight + SEPARATOR_GAP5 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
|
|
27759
29091
|
return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
|
|
27760
29092
|
}
|
|
27761
|
-
function getElk() {
|
|
27762
|
-
if (!elkInstance) elkInstance = new ELK();
|
|
27763
|
-
return elkInstance;
|
|
27764
|
-
}
|
|
27765
|
-
function baseOptions() {
|
|
27766
|
-
return {
|
|
27767
|
-
"elk.algorithm": "layered",
|
|
27768
|
-
// INCLUDE_CHILDREN lets ELK route edges across container boundaries.
|
|
27769
|
-
"elk.hierarchyHandling": "INCLUDE_CHILDREN",
|
|
27770
|
-
"elk.edgeRouting": "ORTHOGONAL",
|
|
27771
|
-
"elk.layered.unnecessaryBendpoints": "true",
|
|
27772
|
-
// Let edges leave from top/bottom of nodes (not just the flow-direction
|
|
27773
|
-
// sides) when it reduces crossings.
|
|
27774
|
-
"elk.layered.allowNonFlowPortsToSwitchSides": "true"
|
|
27775
|
-
};
|
|
27776
|
-
}
|
|
27777
|
-
function bkBaseline() {
|
|
27778
|
-
return {
|
|
27779
|
-
...baseOptions(),
|
|
27780
|
-
"elk.layered.nodePlacement.strategy": "BRANDES_KOEPF",
|
|
27781
|
-
"elk.layered.nodePlacement.bk.fixedAlignment": "BALANCED",
|
|
27782
|
-
"elk.layered.nodePlacement.bk.edgeStraightening": "IMPROVE_STRAIGHTNESS",
|
|
27783
|
-
"elk.layered.compaction.connectedComponents": "true",
|
|
27784
|
-
"elk.layered.spacing.nodeNodeBetweenLayers": "90",
|
|
27785
|
-
"elk.spacing.nodeNode": "55",
|
|
27786
|
-
"elk.spacing.edgeNode": "55",
|
|
27787
|
-
"elk.spacing.edgeEdge": "18"
|
|
27788
|
-
};
|
|
27789
|
-
}
|
|
27790
|
-
function getVariants() {
|
|
27791
|
-
const bk = bkBaseline();
|
|
27792
|
-
return [
|
|
27793
|
-
{
|
|
27794
|
-
name: "bk-baseline",
|
|
27795
|
-
options: {
|
|
27796
|
-
...bk,
|
|
27797
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "ONE_SIDED"
|
|
27798
|
-
}
|
|
27799
|
-
},
|
|
27800
|
-
{
|
|
27801
|
-
name: "bk-aggressive",
|
|
27802
|
-
options: {
|
|
27803
|
-
...bk,
|
|
27804
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
|
|
27805
|
-
"elk.layered.thoroughness": "50"
|
|
27806
|
-
}
|
|
27807
|
-
},
|
|
27808
|
-
{
|
|
27809
|
-
name: "bk-wide",
|
|
27810
|
-
options: {
|
|
27811
|
-
...bk,
|
|
27812
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
|
|
27813
|
-
"elk.layered.thoroughness": "50",
|
|
27814
|
-
"elk.spacing.nodeNode": "70",
|
|
27815
|
-
"elk.spacing.edgeNode": "75",
|
|
27816
|
-
"elk.spacing.edgeEdge": "22",
|
|
27817
|
-
"elk.layered.spacing.nodeNodeBetweenLayers": "120"
|
|
27818
|
-
}
|
|
27819
|
-
},
|
|
27820
|
-
{
|
|
27821
|
-
name: "longest-path",
|
|
27822
|
-
options: {
|
|
27823
|
-
...bk,
|
|
27824
|
-
"elk.layered.layering.strategy": "LONGEST_PATH",
|
|
27825
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
|
|
27826
|
-
"elk.layered.thoroughness": "50"
|
|
27827
|
-
}
|
|
27828
|
-
},
|
|
27829
|
-
{
|
|
27830
|
-
name: "bounded-width",
|
|
27831
|
-
options: {
|
|
27832
|
-
...bk,
|
|
27833
|
-
"elk.layered.layering.strategy": "COFFMAN_GRAHAM",
|
|
27834
|
-
"elk.layered.layering.coffmanGraham.layerBound": "3",
|
|
27835
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
|
|
27836
|
-
"elk.layered.thoroughness": "50"
|
|
27837
|
-
}
|
|
27838
|
-
}
|
|
27839
|
-
];
|
|
27840
|
-
}
|
|
27841
|
-
function countCrossings(edges) {
|
|
27842
|
-
let count = 0;
|
|
27843
|
-
for (let i = 0; i < edges.length; i++) {
|
|
27844
|
-
const edgeI = edges[i];
|
|
27845
|
-
const a = edgeI.points;
|
|
27846
|
-
if (a.length < 2) continue;
|
|
27847
|
-
for (let j = i + 1; j < edges.length; j++) {
|
|
27848
|
-
const edgeJ = edges[j];
|
|
27849
|
-
const b = edgeJ.points;
|
|
27850
|
-
if (b.length < 2) continue;
|
|
27851
|
-
if (edgeI.source === edgeJ.source) continue;
|
|
27852
|
-
if (edgeI.source === edgeJ.target) continue;
|
|
27853
|
-
if (edgeI.target === edgeJ.source) continue;
|
|
27854
|
-
if (edgeI.target === edgeJ.target) continue;
|
|
27855
|
-
for (let ai = 0; ai < a.length - 1; ai++) {
|
|
27856
|
-
for (let bi = 0; bi < b.length - 1; bi++) {
|
|
27857
|
-
if (segmentsCross(a[ai], a[ai + 1], b[bi], b[bi + 1])) count++;
|
|
27858
|
-
}
|
|
27859
|
-
}
|
|
27860
|
-
}
|
|
27861
|
-
}
|
|
27862
|
-
return count;
|
|
27863
|
-
}
|
|
27864
|
-
function segmentsCross(p1, p2, p3, p4) {
|
|
27865
|
-
const d1x = p2.x - p1.x;
|
|
27866
|
-
const d1y = p2.y - p1.y;
|
|
27867
|
-
const d2x = p4.x - p3.x;
|
|
27868
|
-
const d2y = p4.y - p3.y;
|
|
27869
|
-
const denom = d1x * d2y - d1y * d2x;
|
|
27870
|
-
if (Math.abs(denom) < 1e-9) return false;
|
|
27871
|
-
const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / denom;
|
|
27872
|
-
const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / denom;
|
|
27873
|
-
const EPS = 1e-3;
|
|
27874
|
-
return t > EPS && t < 1 - EPS && s > EPS && s < 1 - EPS;
|
|
27875
|
-
}
|
|
27876
|
-
function countTotalBends(edges) {
|
|
27877
|
-
let bends = 0;
|
|
27878
|
-
for (const e of edges) bends += Math.max(0, e.points.length - 2);
|
|
27879
|
-
return bends;
|
|
27880
|
-
}
|
|
27881
|
-
function scoreLayout(layout) {
|
|
27882
|
-
return {
|
|
27883
|
-
crossings: countCrossings(layout.edges),
|
|
27884
|
-
bends: countTotalBends(layout.edges),
|
|
27885
|
-
area: layout.width * layout.height
|
|
27886
|
-
};
|
|
27887
|
-
}
|
|
27888
|
-
function cmpScore(a, b) {
|
|
27889
|
-
const aBucket = a.crossings <= CROSSINGS_FORGIVENESS ? 0 : a.crossings;
|
|
27890
|
-
const bBucket = b.crossings <= CROSSINGS_FORGIVENESS ? 0 : b.crossings;
|
|
27891
|
-
if (aBucket !== bBucket) return aBucket - bBucket;
|
|
27892
|
-
if (a.area !== b.area) return a.area - b.area;
|
|
27893
|
-
return a.bends - b.bends;
|
|
27894
|
-
}
|
|
27895
29093
|
async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
|
|
27896
|
-
const
|
|
27897
|
-
const
|
|
27898
|
-
|
|
27899
|
-
|
|
27900
|
-
|
|
27901
|
-
|
|
27902
|
-
|
|
27903
|
-
missingGroups.add(og.label);
|
|
27904
|
-
}
|
|
27905
|
-
}
|
|
27906
|
-
for (const label of missingGroups) {
|
|
27907
|
-
const og = collapseInfo.originalGroups.find((g) => g.label === label);
|
|
27908
|
-
const parentLabel = og?.parentGroup;
|
|
27909
|
-
if (!parentLabel || !missingGroups.has(parentLabel)) {
|
|
27910
|
-
collapsedGroupLabels.add(label);
|
|
27911
|
-
}
|
|
27912
|
-
}
|
|
27913
|
-
}
|
|
27914
|
-
const nodeSizes = /* @__PURE__ */ new Map();
|
|
27915
|
-
let maxDescHeight = 0;
|
|
27916
|
-
for (const node of parsed.nodes) {
|
|
27917
|
-
const size = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
|
|
27918
|
-
nodeSizes.set(node.label, size);
|
|
27919
|
-
if (!hideDescriptions && node.description && node.description.length > 0) {
|
|
27920
|
-
maxDescHeight = Math.max(maxDescHeight, size.height);
|
|
27921
|
-
}
|
|
27922
|
-
}
|
|
27923
|
-
if (maxDescHeight > 0) {
|
|
27924
|
-
for (const node of parsed.nodes) {
|
|
27925
|
-
if (node.description && node.description.length > 0) {
|
|
27926
|
-
const size = nodeSizes.get(node.label);
|
|
27927
|
-
nodeSizes.set(node.label, { width: size.width, height: maxDescHeight });
|
|
27928
|
-
}
|
|
27929
|
-
}
|
|
27930
|
-
}
|
|
27931
|
-
const expandedGroupSet = new Set(parsed.groups.map((g) => g.label));
|
|
27932
|
-
const gid = (label) => `__group_${label}`;
|
|
27933
|
-
function buildGraph() {
|
|
27934
|
-
const nodeById = /* @__PURE__ */ new Map();
|
|
27935
|
-
const parentOf = /* @__PURE__ */ new Map();
|
|
27936
|
-
for (const node of parsed.nodes) {
|
|
27937
|
-
const size = nodeSizes.get(node.label);
|
|
27938
|
-
nodeById.set(node.label, {
|
|
27939
|
-
id: node.label,
|
|
27940
|
-
width: size.width,
|
|
27941
|
-
height: size.height,
|
|
27942
|
-
labels: [{ text: node.label }]
|
|
27943
|
-
});
|
|
27944
|
-
}
|
|
27945
|
-
for (const group of parsed.groups) {
|
|
27946
|
-
nodeById.set(gid(group.label), {
|
|
27947
|
-
id: gid(group.label),
|
|
27948
|
-
labels: [{ text: group.label }],
|
|
27949
|
-
layoutOptions: {
|
|
27950
|
-
"elk.padding": `[top=${CONTAINER_PAD_TOP2},left=${CONTAINER_PAD_X3},bottom=${CONTAINER_PAD_BOTTOM3},right=${CONTAINER_PAD_X3}]`,
|
|
27951
|
-
// Suggest square-ish containers — has limited effect with
|
|
27952
|
-
// INCLUDE_CHILDREN but doesn't hurt.
|
|
27953
|
-
"elk.aspectRatio": "1.4"
|
|
27954
|
-
},
|
|
27955
|
-
children: [],
|
|
27956
|
-
edges: []
|
|
27957
|
-
});
|
|
27958
|
-
}
|
|
27959
|
-
for (const label of collapsedGroupLabels) {
|
|
27960
|
-
nodeById.set(gid(label), {
|
|
27961
|
-
id: gid(label),
|
|
27962
|
-
width: NODE_WIDTH,
|
|
27963
|
-
height: NODE_HEIGHT,
|
|
27964
|
-
labels: [{ text: label }]
|
|
27965
|
-
});
|
|
27966
|
-
}
|
|
27967
|
-
for (const group of parsed.groups) {
|
|
27968
|
-
if (group.parentGroup && nodeById.has(gid(group.parentGroup))) {
|
|
27969
|
-
parentOf.set(gid(group.label), gid(group.parentGroup));
|
|
27970
|
-
}
|
|
27971
|
-
}
|
|
27972
|
-
if (collapseInfo) {
|
|
27973
|
-
for (const label of collapsedGroupLabels) {
|
|
27974
|
-
const og = collapseInfo.originalGroups.find((g) => g.label === label);
|
|
27975
|
-
if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && nodeById.has(gid(og.parentGroup))) {
|
|
27976
|
-
parentOf.set(gid(label), gid(og.parentGroup));
|
|
27977
|
-
}
|
|
27978
|
-
}
|
|
27979
|
-
}
|
|
27980
|
-
for (const group of parsed.groups) {
|
|
27981
|
-
for (const child of group.children) {
|
|
27982
|
-
if (expandedGroupSet.has(child)) continue;
|
|
27983
|
-
if (nodeById.has(child)) {
|
|
27984
|
-
parentOf.set(child, gid(group.label));
|
|
27985
|
-
}
|
|
27986
|
-
}
|
|
27987
|
-
}
|
|
27988
|
-
const roots = [];
|
|
27989
|
-
for (const [id, node] of nodeById) {
|
|
27990
|
-
const parentId = parentOf.get(id);
|
|
27991
|
-
if (parentId) {
|
|
27992
|
-
const parent = nodeById.get(parentId);
|
|
27993
|
-
parent.children = parent.children ?? [];
|
|
27994
|
-
parent.children.push(node);
|
|
27995
|
-
} else {
|
|
27996
|
-
roots.push(node);
|
|
27997
|
-
}
|
|
27998
|
-
}
|
|
27999
|
-
const rootEdges = [];
|
|
28000
|
-
for (let i = 0; i < parsed.edges.length; i++) {
|
|
28001
|
-
const edge = parsed.edges[i];
|
|
28002
|
-
if (!nodeById.has(edge.source) || !nodeById.has(edge.target)) continue;
|
|
28003
|
-
rootEdges.push({
|
|
28004
|
-
id: `e${i}`,
|
|
28005
|
-
sources: [edge.source],
|
|
28006
|
-
targets: [edge.target]
|
|
28007
|
-
});
|
|
28008
|
-
}
|
|
28009
|
-
return { roots, rootEdges };
|
|
28010
|
-
}
|
|
28011
|
-
async function runVariant(variant) {
|
|
28012
|
-
const { roots, rootEdges } = buildGraph();
|
|
28013
|
-
const elkRoot = {
|
|
28014
|
-
id: "root",
|
|
28015
|
-
layoutOptions: {
|
|
28016
|
-
...variant.options,
|
|
28017
|
-
"elk.direction": direction,
|
|
28018
|
-
"elk.padding": `[top=${MARGIN3},left=${MARGIN3},bottom=${MARGIN3},right=${MARGIN3}]`
|
|
28019
|
-
},
|
|
28020
|
-
children: roots,
|
|
28021
|
-
edges: rootEdges
|
|
28022
|
-
};
|
|
28023
|
-
const result = await getElk().layout(elkRoot);
|
|
28024
|
-
return extractLayout(result);
|
|
28025
|
-
}
|
|
28026
|
-
function extractLayout(result) {
|
|
28027
|
-
const layoutNodes = [];
|
|
28028
|
-
const layoutGroups = [];
|
|
28029
|
-
const allEdges = [];
|
|
28030
|
-
const containerAbs = /* @__PURE__ */ new Map();
|
|
28031
|
-
function walk(n, offsetX, offsetY, isRoot) {
|
|
28032
|
-
const nx = (n.x ?? 0) + offsetX;
|
|
28033
|
-
const ny = (n.y ?? 0) + offsetY;
|
|
28034
|
-
const nw = n.width ?? 0;
|
|
28035
|
-
const nh = n.height ?? 0;
|
|
28036
|
-
if (isRoot) {
|
|
28037
|
-
containerAbs.set("root", { x: nx, y: ny });
|
|
28038
|
-
} else {
|
|
28039
|
-
const isGroup = n.id.startsWith("__group_");
|
|
28040
|
-
if (isGroup) {
|
|
28041
|
-
const label = n.id.slice("__group_".length);
|
|
28042
|
-
const collapsed = collapsedGroupLabels.has(label);
|
|
28043
|
-
const og = collapseInfo?.originalGroups.find(
|
|
28044
|
-
(g) => g.label === label
|
|
28045
|
-
);
|
|
28046
|
-
const pg = parsed.groups.find((g) => g.label === label);
|
|
28047
|
-
const childCount = collapsed ? collapseInfo?.collapsedChildCounts.get(label) ?? 0 : void 0;
|
|
28048
|
-
layoutGroups.push({
|
|
28049
|
-
label,
|
|
28050
|
-
lineNumber: pg?.lineNumber ?? og?.lineNumber ?? 0,
|
|
28051
|
-
x: nx + nw / 2,
|
|
28052
|
-
y: ny + nh / 2,
|
|
28053
|
-
width: nw,
|
|
28054
|
-
height: nh,
|
|
28055
|
-
collapsed,
|
|
28056
|
-
...childCount !== void 0 && { childCount }
|
|
28057
|
-
});
|
|
28058
|
-
if (!collapsed) containerAbs.set(n.id, { x: nx, y: ny });
|
|
28059
|
-
} else {
|
|
28060
|
-
layoutNodes.push({
|
|
28061
|
-
label: n.id,
|
|
28062
|
-
x: nx + nw / 2,
|
|
28063
|
-
y: ny + nh / 2,
|
|
28064
|
-
width: nw,
|
|
28065
|
-
height: nh
|
|
28066
|
-
});
|
|
28067
|
-
}
|
|
28068
|
-
}
|
|
28069
|
-
if (n.edges) for (const e of n.edges) allEdges.push(e);
|
|
28070
|
-
if (n.children) for (const c of n.children) walk(c, nx, ny, false);
|
|
28071
|
-
}
|
|
28072
|
-
walk(result, 0, 0, true);
|
|
28073
|
-
const edgeYOffsets = new Array(parsed.edges.length).fill(0);
|
|
28074
|
-
const edgeParallelCounts = new Array(parsed.edges.length).fill(1);
|
|
28075
|
-
const parallelGroups = /* @__PURE__ */ new Map();
|
|
28076
|
-
for (let i = 0; i < parsed.edges.length; i++) {
|
|
28077
|
-
const edge = parsed.edges[i];
|
|
28078
|
-
const [a, b] = edge.source < edge.target ? [edge.source, edge.target] : [edge.target, edge.source];
|
|
28079
|
-
const key = `${a}\0${b}`;
|
|
28080
|
-
if (!parallelGroups.has(key)) parallelGroups.set(key, []);
|
|
28081
|
-
parallelGroups.get(key).push(i);
|
|
28082
|
-
}
|
|
28083
|
-
for (const group of parallelGroups.values()) {
|
|
28084
|
-
const capped = group.slice(0, MAX_PARALLEL_EDGES);
|
|
28085
|
-
for (const idx of group.slice(MAX_PARALLEL_EDGES)) {
|
|
28086
|
-
edgeParallelCounts[idx] = 0;
|
|
28087
|
-
}
|
|
28088
|
-
if (capped.length < 2) continue;
|
|
28089
|
-
for (let j = 0; j < capped.length; j++) {
|
|
28090
|
-
const cappedJ = capped[j];
|
|
28091
|
-
edgeYOffsets[cappedJ] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
|
|
28092
|
-
edgeParallelCounts[cappedJ] = capped.length;
|
|
28093
|
-
}
|
|
28094
|
-
}
|
|
28095
|
-
const edgeById = /* @__PURE__ */ new Map();
|
|
28096
|
-
for (const e of allEdges) edgeById.set(e.id, e);
|
|
28097
|
-
const layoutEdges = [];
|
|
28098
|
-
for (let i = 0; i < parsed.edges.length; i++) {
|
|
28099
|
-
const edge = parsed.edges[i];
|
|
28100
|
-
if (edgeParallelCounts[i] === 0) continue;
|
|
28101
|
-
const elkEdge = edgeById.get(`e${i}`);
|
|
28102
|
-
if (!elkEdge?.sections || elkEdge.sections.length === 0) continue;
|
|
28103
|
-
const container = elkEdge.container ?? "root";
|
|
28104
|
-
const off = containerAbs.get(container) ?? { x: 0, y: 0 };
|
|
28105
|
-
const s = elkEdge.sections[0];
|
|
28106
|
-
const points = [
|
|
28107
|
-
{ x: s.startPoint.x + off.x, y: s.startPoint.y + off.y },
|
|
28108
|
-
...(s.bendPoints ?? []).map((p) => ({
|
|
28109
|
-
x: p.x + off.x,
|
|
28110
|
-
y: p.y + off.y
|
|
28111
|
-
})),
|
|
28112
|
-
{ x: s.endPoint.x + off.x, y: s.endPoint.y + off.y }
|
|
28113
|
-
];
|
|
28114
|
-
let labelX;
|
|
28115
|
-
let labelY;
|
|
28116
|
-
if (edge.label && points.length >= 2) {
|
|
28117
|
-
const mid = Math.floor(points.length / 2);
|
|
28118
|
-
const midPoint = points[mid];
|
|
28119
|
-
labelX = midPoint.x;
|
|
28120
|
-
labelY = midPoint.y - 10;
|
|
28121
|
-
}
|
|
28122
|
-
layoutEdges.push({
|
|
28123
|
-
source: edge.source,
|
|
28124
|
-
target: edge.target,
|
|
28125
|
-
...edge.label !== void 0 && { label: edge.label },
|
|
28126
|
-
bidirectional: edge.bidirectional,
|
|
28127
|
-
lineNumber: edge.lineNumber,
|
|
28128
|
-
points,
|
|
28129
|
-
...labelX !== void 0 && { labelX },
|
|
28130
|
-
...labelY !== void 0 && { labelY },
|
|
28131
|
-
// In-bounds — i < parsed.edges.length, arrays sized to that length.
|
|
28132
|
-
yOffset: edgeYOffsets[i],
|
|
28133
|
-
parallelCount: edgeParallelCounts[i],
|
|
28134
|
-
metadata: edge.metadata,
|
|
28135
|
-
deferred: true
|
|
28136
|
-
});
|
|
28137
|
-
}
|
|
28138
|
-
let maxX = 0;
|
|
28139
|
-
let maxY = 0;
|
|
28140
|
-
for (const node of layoutNodes) {
|
|
28141
|
-
maxX = Math.max(maxX, node.x + node.width / 2);
|
|
28142
|
-
maxY = Math.max(maxY, node.y + node.height / 2);
|
|
28143
|
-
}
|
|
28144
|
-
for (const group of layoutGroups) {
|
|
28145
|
-
maxX = Math.max(maxX, group.x + group.width / 2);
|
|
28146
|
-
maxY = Math.max(maxY, group.y + group.height / 2);
|
|
29094
|
+
const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
|
|
29095
|
+
const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
|
|
29096
|
+
...layoutOptions?.hideDescriptions !== void 0 && {
|
|
29097
|
+
hideDescriptions: layoutOptions.hideDescriptions
|
|
29098
|
+
},
|
|
29099
|
+
...layoutOptions?.previousPositions !== void 0 && {
|
|
29100
|
+
previousPositions: layoutOptions.previousPositions
|
|
28147
29101
|
}
|
|
28148
|
-
|
|
28149
|
-
|
|
28150
|
-
|
|
28151
|
-
|
|
28152
|
-
|
|
28153
|
-
|
|
28154
|
-
};
|
|
28155
|
-
}
|
|
28156
|
-
const N = parsed.nodes.length + parsed.groups.length;
|
|
28157
|
-
const E = parsed.edges.length;
|
|
28158
|
-
const trivial = N < 8 && E < 10;
|
|
28159
|
-
const variants = trivial ? [getVariants()[1]] : getVariants();
|
|
28160
|
-
const results = await Promise.all(variants.map((v) => runVariant(v)));
|
|
28161
|
-
let best = results[0];
|
|
28162
|
-
let bestScore = scoreLayout(best);
|
|
28163
|
-
for (let i = 1; i < results.length; i++) {
|
|
28164
|
-
const resultI = results[i];
|
|
28165
|
-
const s = scoreLayout(resultI);
|
|
28166
|
-
if (cmpScore(s, bestScore) < 0) {
|
|
28167
|
-
best = resultI;
|
|
28168
|
-
bestScore = s;
|
|
28169
|
-
}
|
|
28170
|
-
}
|
|
28171
|
-
return attachNotes(best, parsed, layoutOptions?.collapsedNotes);
|
|
29102
|
+
});
|
|
29103
|
+
return attachNotes(
|
|
29104
|
+
applyParallelEdgeOffsets(searched),
|
|
29105
|
+
parsed,
|
|
29106
|
+
layoutOptions?.collapsedNotes
|
|
29107
|
+
);
|
|
28172
29108
|
}
|
|
28173
29109
|
function attachNotes(layout, parsed, collapsedNotes) {
|
|
28174
29110
|
const notesSuppressed = parsed.options?.["no-notes"] === "on";
|
|
@@ -28246,20 +29182,47 @@ function attachNotes(layout, parsed, collapsedNotes) {
|
|
|
28246
29182
|
nodes: finalNodes,
|
|
28247
29183
|
edges: finalEdges,
|
|
28248
29184
|
groups: finalGroups,
|
|
28249
|
-
width: bbMaxX + shiftX +
|
|
28250
|
-
height: bbMaxY + shiftY +
|
|
29185
|
+
width: bbMaxX + shiftX + MARGIN4,
|
|
29186
|
+
height: bbMaxY + shiftY + MARGIN4
|
|
29187
|
+
};
|
|
29188
|
+
}
|
|
29189
|
+
function applyParallelEdgeOffsets(layout) {
|
|
29190
|
+
const groups = /* @__PURE__ */ new Map();
|
|
29191
|
+
layout.edges.forEach((e, i) => {
|
|
29192
|
+
const [a, b] = e.source < e.target ? [e.source, e.target] : [e.target, e.source];
|
|
29193
|
+
const key = `${a}\0${b}`;
|
|
29194
|
+
const arr = groups.get(key);
|
|
29195
|
+
if (arr) arr.push(i);
|
|
29196
|
+
else groups.set(key, [i]);
|
|
29197
|
+
});
|
|
29198
|
+
if ([...groups.values()].every((g) => g.length < 2)) return layout;
|
|
29199
|
+
const yOffset = new Array(layout.edges.length).fill(0);
|
|
29200
|
+
const count = new Array(layout.edges.length).fill(1);
|
|
29201
|
+
for (const idxs of groups.values()) {
|
|
29202
|
+
const capped = idxs.slice(0, MAX_PARALLEL_EDGES);
|
|
29203
|
+
for (const drop of idxs.slice(MAX_PARALLEL_EDGES)) count[drop] = 0;
|
|
29204
|
+
if (capped.length < 2) continue;
|
|
29205
|
+
capped.forEach((idx, j) => {
|
|
29206
|
+
yOffset[idx] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
|
|
29207
|
+
count[idx] = capped.length;
|
|
29208
|
+
});
|
|
29209
|
+
}
|
|
29210
|
+
return {
|
|
29211
|
+
...layout,
|
|
29212
|
+
edges: layout.edges.map((e, i) => ({
|
|
29213
|
+
...e,
|
|
29214
|
+
yOffset: yOffset[i],
|
|
29215
|
+
parallelCount: count[i]
|
|
29216
|
+
}))
|
|
28251
29217
|
};
|
|
28252
29218
|
}
|
|
28253
|
-
var
|
|
29219
|
+
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;
|
|
28254
29220
|
var init_layout5 = __esm({
|
|
28255
29221
|
"src/boxes-and-lines/layout.ts"() {
|
|
28256
29222
|
"use strict";
|
|
28257
29223
|
init_text_measure();
|
|
28258
29224
|
init_notes();
|
|
28259
|
-
|
|
28260
|
-
CONTAINER_PAD_X3 = 30;
|
|
28261
|
-
CONTAINER_PAD_TOP2 = 40;
|
|
28262
|
-
CONTAINER_PAD_BOTTOM3 = 24;
|
|
29225
|
+
MARGIN4 = 40;
|
|
28263
29226
|
MAX_PARALLEL_EDGES = 5;
|
|
28264
29227
|
PARALLEL_SPACING = 22;
|
|
28265
29228
|
PHI = 1.618;
|
|
@@ -28276,8 +29239,6 @@ var init_layout5 = __esm({
|
|
|
28276
29239
|
LABEL_PAD = 12;
|
|
28277
29240
|
VALUE_ROW_FONT = 11;
|
|
28278
29241
|
VALUE_ROW_H = SEPARATOR_GAP5 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
|
|
28279
|
-
elkInstance = null;
|
|
28280
|
-
CROSSINGS_FORGIVENESS = 1;
|
|
28281
29242
|
}
|
|
28282
29243
|
});
|
|
28283
29244
|
|
|
@@ -28432,7 +29393,7 @@ function layoutMindmap(parsed, _palette, options) {
|
|
|
28432
29393
|
leafWidth: ctx.structural(LEAF_WIDTH),
|
|
28433
29394
|
hGap: ctx.aesthetic(H_GAP2),
|
|
28434
29395
|
vGap: ctx.aesthetic(V_GAP2),
|
|
28435
|
-
margin: ctx.aesthetic(
|
|
29396
|
+
margin: ctx.aesthetic(MARGIN5),
|
|
28436
29397
|
multiRootGap: ctx.aesthetic(MULTI_ROOT_GAP)
|
|
28437
29398
|
};
|
|
28438
29399
|
populateDepthCache(roots);
|
|
@@ -28810,7 +29771,7 @@ function populateDepthCache(roots) {
|
|
|
28810
29771
|
};
|
|
28811
29772
|
walk(roots, 0);
|
|
28812
29773
|
}
|
|
28813
|
-
var ROOT_WIDTH, DEPTH1_WIDTH, LEAF_WIDTH, SINGLE_LABEL_HEIGHT, LABEL_LINE_HEIGHT2, DESC_LINE_HEIGHT3, NODE_V_PAD, H_GAP2, V_GAP2,
|
|
29774
|
+
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;
|
|
28814
29775
|
var init_layout6 = __esm({
|
|
28815
29776
|
"src/mindmap/layout.ts"() {
|
|
28816
29777
|
"use strict";
|
|
@@ -28826,7 +29787,7 @@ var init_layout6 = __esm({
|
|
|
28826
29787
|
NODE_V_PAD = 10;
|
|
28827
29788
|
H_GAP2 = 40;
|
|
28828
29789
|
V_GAP2 = 12;
|
|
28829
|
-
|
|
29790
|
+
MARGIN5 = 40;
|
|
28830
29791
|
MULTI_ROOT_GAP = 60;
|
|
28831
29792
|
nodeDepthCache = /* @__PURE__ */ new Map();
|
|
28832
29793
|
}
|
|
@@ -30101,7 +31062,7 @@ __export(layout_exports8, {
|
|
|
30101
31062
|
layoutC4Deployment: () => layoutC4Deployment,
|
|
30102
31063
|
rollUpContextRelationships: () => rollUpContextRelationships
|
|
30103
31064
|
});
|
|
30104
|
-
import
|
|
31065
|
+
import dagre5 from "@dagrejs/dagre";
|
|
30105
31066
|
function computeEdgePenalty(edgeList, nodePositions, degrees, nodeGeometry) {
|
|
30106
31067
|
let penalty = 0;
|
|
30107
31068
|
for (const edge of edgeList) {
|
|
@@ -30526,7 +31487,7 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
30526
31487
|
}
|
|
30527
31488
|
const contextRels = rollUpContextRelationships(parsed);
|
|
30528
31489
|
const spacing = computeAdaptiveSpacing(contextRels);
|
|
30529
|
-
const g = new
|
|
31490
|
+
const g = new dagre5.graphlib.Graph();
|
|
30530
31491
|
g.setGraph({
|
|
30531
31492
|
rankdir: "TB",
|
|
30532
31493
|
nodesep: spacing.nodesep,
|
|
@@ -30547,7 +31508,7 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
30547
31508
|
g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
|
|
30548
31509
|
}
|
|
30549
31510
|
}
|
|
30550
|
-
|
|
31511
|
+
dagre5.layout(g);
|
|
30551
31512
|
reduceCrossings(
|
|
30552
31513
|
g,
|
|
30553
31514
|
validRels.map((r) => ({ source: r.sourceName, target: r.targetName }))
|
|
@@ -30614,8 +31575,8 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
30614
31575
|
}
|
|
30615
31576
|
}
|
|
30616
31577
|
if (nodes.length > 0) {
|
|
30617
|
-
const shiftX =
|
|
30618
|
-
const shiftY =
|
|
31578
|
+
const shiftX = MARGIN6 - minX;
|
|
31579
|
+
const shiftY = MARGIN6 - minY;
|
|
30619
31580
|
for (const node of nodes) {
|
|
30620
31581
|
node.x += shiftX;
|
|
30621
31582
|
node.y += shiftY;
|
|
@@ -30627,12 +31588,12 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
30627
31588
|
}
|
|
30628
31589
|
}
|
|
30629
31590
|
}
|
|
30630
|
-
let totalWidth = nodes.length > 0 ? maxX - minX +
|
|
30631
|
-
let totalHeight = nodes.length > 0 ? maxY - minY +
|
|
31591
|
+
let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN6 * 2 : 0;
|
|
31592
|
+
let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN6 * 2 : 0;
|
|
30632
31593
|
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
30633
31594
|
if (legendGroups.length > 0) {
|
|
30634
|
-
const legendY = totalHeight +
|
|
30635
|
-
let legendX =
|
|
31595
|
+
const legendY = totalHeight + MARGIN6;
|
|
31596
|
+
let legendX = MARGIN6;
|
|
30636
31597
|
for (const lg of legendGroups) {
|
|
30637
31598
|
lg.x = legendX;
|
|
30638
31599
|
lg.y = legendY;
|
|
@@ -30725,7 +31686,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
30725
31686
|
}
|
|
30726
31687
|
}
|
|
30727
31688
|
const hasGroups = elementToGroup.size > 0;
|
|
30728
|
-
const g = hasGroups ? new
|
|
31689
|
+
const g = hasGroups ? new dagre5.graphlib.Graph({ compound: true }) : new dagre5.graphlib.Graph();
|
|
30729
31690
|
g.setDefaultEdgeLabel(() => ({}));
|
|
30730
31691
|
if (hasGroups) {
|
|
30731
31692
|
const seenGroups = /* @__PURE__ */ new Set();
|
|
@@ -30803,7 +31764,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
30803
31764
|
g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
|
|
30804
31765
|
}
|
|
30805
31766
|
}
|
|
30806
|
-
|
|
31767
|
+
dagre5.layout(g);
|
|
30807
31768
|
const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
|
|
30808
31769
|
reduceCrossings(
|
|
30809
31770
|
g,
|
|
@@ -30964,8 +31925,8 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
30964
31925
|
if (pt.y > maxY) maxY = pt.y;
|
|
30965
31926
|
}
|
|
30966
31927
|
}
|
|
30967
|
-
const shiftX =
|
|
30968
|
-
const shiftY =
|
|
31928
|
+
const shiftX = MARGIN6 - minX;
|
|
31929
|
+
const shiftY = MARGIN6 - minY;
|
|
30969
31930
|
for (const node of nodes) {
|
|
30970
31931
|
node.x += shiftX;
|
|
30971
31932
|
node.y += shiftY;
|
|
@@ -30982,12 +31943,12 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
30982
31943
|
pt.y += shiftY;
|
|
30983
31944
|
}
|
|
30984
31945
|
}
|
|
30985
|
-
let totalWidth = maxX - minX +
|
|
30986
|
-
let totalHeight = maxY - minY +
|
|
31946
|
+
let totalWidth = maxX - minX + MARGIN6 * 2;
|
|
31947
|
+
let totalHeight = maxY - minY + MARGIN6 * 2;
|
|
30987
31948
|
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
30988
31949
|
if (legendGroups.length > 0) {
|
|
30989
|
-
const legendY = totalHeight +
|
|
30990
|
-
let legendX =
|
|
31950
|
+
const legendY = totalHeight + MARGIN6;
|
|
31951
|
+
let legendX = MARGIN6;
|
|
30991
31952
|
for (const lg of legendGroups) {
|
|
30992
31953
|
lg.x = legendX;
|
|
30993
31954
|
lg.y = legendY;
|
|
@@ -31132,7 +32093,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
31132
32093
|
}
|
|
31133
32094
|
}
|
|
31134
32095
|
const hasGroups = elementToGroup.size > 0;
|
|
31135
|
-
const g = hasGroups ? new
|
|
32096
|
+
const g = hasGroups ? new dagre5.graphlib.Graph({ compound: true }) : new dagre5.graphlib.Graph();
|
|
31136
32097
|
g.setDefaultEdgeLabel(() => ({}));
|
|
31137
32098
|
if (hasGroups) {
|
|
31138
32099
|
const seenGroups = /* @__PURE__ */ new Set();
|
|
@@ -31216,7 +32177,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
31216
32177
|
g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
|
|
31217
32178
|
}
|
|
31218
32179
|
}
|
|
31219
|
-
|
|
32180
|
+
dagre5.layout(g);
|
|
31220
32181
|
const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
|
|
31221
32182
|
reduceCrossings(
|
|
31222
32183
|
g,
|
|
@@ -31379,8 +32340,8 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
31379
32340
|
if (pt.y > maxY) maxY = pt.y;
|
|
31380
32341
|
}
|
|
31381
32342
|
}
|
|
31382
|
-
const shiftX =
|
|
31383
|
-
const shiftY =
|
|
32343
|
+
const shiftX = MARGIN6 - minX;
|
|
32344
|
+
const shiftY = MARGIN6 - minY;
|
|
31384
32345
|
for (const node of nodes) {
|
|
31385
32346
|
node.x += shiftX;
|
|
31386
32347
|
node.y += shiftY;
|
|
@@ -31397,12 +32358,12 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
31397
32358
|
pt.y += shiftY;
|
|
31398
32359
|
}
|
|
31399
32360
|
}
|
|
31400
|
-
let totalWidth = maxX - minX +
|
|
31401
|
-
let totalHeight = maxY - minY +
|
|
32361
|
+
let totalWidth = maxX - minX + MARGIN6 * 2;
|
|
32362
|
+
let totalHeight = maxY - minY + MARGIN6 * 2;
|
|
31402
32363
|
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
31403
32364
|
if (legendGroups.length > 0) {
|
|
31404
|
-
const legendY = totalHeight +
|
|
31405
|
-
let legendX =
|
|
32365
|
+
const legendY = totalHeight + MARGIN6;
|
|
32366
|
+
let legendX = MARGIN6;
|
|
31406
32367
|
for (const lg of legendGroups) {
|
|
31407
32368
|
lg.x = legendX;
|
|
31408
32369
|
lg.y = legendY;
|
|
@@ -31508,7 +32469,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31508
32469
|
for (const r of refEntries) {
|
|
31509
32470
|
nameToElement.set(r.element.name, r.element);
|
|
31510
32471
|
}
|
|
31511
|
-
const g = new
|
|
32472
|
+
const g = new dagre5.graphlib.Graph({ compound: true });
|
|
31512
32473
|
g.setDefaultEdgeLabel(() => ({}));
|
|
31513
32474
|
for (const [infraId] of infraIds) {
|
|
31514
32475
|
g.setNode(infraId, {});
|
|
@@ -31552,7 +32513,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31552
32513
|
g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
|
|
31553
32514
|
}
|
|
31554
32515
|
}
|
|
31555
|
-
|
|
32516
|
+
dagre5.layout(g);
|
|
31556
32517
|
const nodeInfraMap = /* @__PURE__ */ new Map();
|
|
31557
32518
|
for (const r of refEntries) nodeInfraMap.set(r.element.name, r.infraId);
|
|
31558
32519
|
reduceCrossings(
|
|
@@ -31690,8 +32651,8 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31690
32651
|
if (pt.y > maxY) maxY = pt.y;
|
|
31691
32652
|
}
|
|
31692
32653
|
}
|
|
31693
|
-
const shiftX =
|
|
31694
|
-
const shiftY =
|
|
32654
|
+
const shiftX = MARGIN6 - minX;
|
|
32655
|
+
const shiftY = MARGIN6 - minY;
|
|
31695
32656
|
for (const node of nodes) {
|
|
31696
32657
|
node.x += shiftX;
|
|
31697
32658
|
node.y += shiftY;
|
|
@@ -31706,12 +32667,12 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31706
32667
|
pt.y += shiftY;
|
|
31707
32668
|
}
|
|
31708
32669
|
}
|
|
31709
|
-
let totalWidth = maxX - minX +
|
|
31710
|
-
let totalHeight = maxY - minY +
|
|
32670
|
+
let totalWidth = maxX - minX + MARGIN6 * 2;
|
|
32671
|
+
let totalHeight = maxY - minY + MARGIN6 * 2;
|
|
31711
32672
|
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
31712
32673
|
if (legendGroups.length > 0) {
|
|
31713
|
-
const legendY = totalHeight +
|
|
31714
|
-
let legendX =
|
|
32674
|
+
const legendY = totalHeight + MARGIN6;
|
|
32675
|
+
let legendX = MARGIN6;
|
|
31715
32676
|
for (const lg of legendGroups) {
|
|
31716
32677
|
lg.x = legendX;
|
|
31717
32678
|
lg.y = legendY;
|
|
@@ -31731,7 +32692,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31731
32692
|
height: totalHeight
|
|
31732
32693
|
};
|
|
31733
32694
|
}
|
|
31734
|
-
var 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,
|
|
32695
|
+
var 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;
|
|
31735
32696
|
var init_layout8 = __esm({
|
|
31736
32697
|
"src/c4/layout.ts"() {
|
|
31737
32698
|
"use strict";
|
|
@@ -31751,7 +32712,7 @@ var init_layout8 = __esm({
|
|
|
31751
32712
|
CARD_H_PAD3 = 20;
|
|
31752
32713
|
META_LINE_HEIGHT5 = 16;
|
|
31753
32714
|
META_FONT_SIZE5 = 11;
|
|
31754
|
-
|
|
32715
|
+
MARGIN6 = 40;
|
|
31755
32716
|
BOUNDARY_PAD = 40;
|
|
31756
32717
|
GROUP_BOUNDARY_PAD = 24;
|
|
31757
32718
|
EDGE_NODE_COLLISION_WEIGHT = 5e3;
|
|
@@ -32786,7 +33747,7 @@ var layout_exports9 = {};
|
|
|
32786
33747
|
__export(layout_exports9, {
|
|
32787
33748
|
layoutGraph: () => layoutGraph
|
|
32788
33749
|
});
|
|
32789
|
-
import
|
|
33750
|
+
import dagre6 from "@dagrejs/dagre";
|
|
32790
33751
|
function computeNodeWidth(label, shape) {
|
|
32791
33752
|
if (shape === "pseudostate") return 24;
|
|
32792
33753
|
const base = Math.max(120, label.length * 9 + 40);
|
|
@@ -32820,7 +33781,7 @@ function layoutGraph(graph, options) {
|
|
|
32820
33781
|
if (allNodes.length === 0) {
|
|
32821
33782
|
return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
|
|
32822
33783
|
}
|
|
32823
|
-
const g = new
|
|
33784
|
+
const g = new dagre6.graphlib.Graph({ compound: true });
|
|
32824
33785
|
g.setGraph({
|
|
32825
33786
|
rankdir: graph.direction,
|
|
32826
33787
|
nodesep: 50,
|
|
@@ -32871,7 +33832,7 @@ function layoutGraph(graph, options) {
|
|
|
32871
33832
|
label: edge.label ?? ""
|
|
32872
33833
|
});
|
|
32873
33834
|
}
|
|
32874
|
-
|
|
33835
|
+
dagre6.layout(g);
|
|
32875
33836
|
const collapsedGroupIds = collapsedChildCounts ? new Set(collapsedChildCounts.keys()) : /* @__PURE__ */ new Set();
|
|
32876
33837
|
const basePositioned = allNodes.map((node) => {
|
|
32877
33838
|
const pos = g.node(node.id);
|
|
@@ -34575,7 +35536,7 @@ __export(layout_exports10, {
|
|
|
34575
35536
|
layoutInfra: () => layoutInfra,
|
|
34576
35537
|
separateGroups: () => separateGroups
|
|
34577
35538
|
});
|
|
34578
|
-
import
|
|
35539
|
+
import dagre7 from "@dagrejs/dagre";
|
|
34579
35540
|
function countDisplayProps(node, expanded, options) {
|
|
34580
35541
|
if (!expanded) return 0;
|
|
34581
35542
|
let count = node.properties.filter((p) => DISPLAY_KEYS.has(p.key)).length;
|
|
@@ -34877,7 +35838,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
34877
35838
|
};
|
|
34878
35839
|
}
|
|
34879
35840
|
const isLR = computed.direction !== "TB";
|
|
34880
|
-
const g = new
|
|
35841
|
+
const g = new dagre7.graphlib.Graph();
|
|
34881
35842
|
g.setGraph({
|
|
34882
35843
|
rankdir: computed.direction === "TB" ? "TB" : "LR",
|
|
34883
35844
|
nodesep: isLR ? 70 : 60,
|
|
@@ -34928,7 +35889,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
34928
35889
|
g.setEdge(edge.sourceId, edge.targetId, { label: edge.label });
|
|
34929
35890
|
}
|
|
34930
35891
|
}
|
|
34931
|
-
|
|
35892
|
+
dagre7.layout(g);
|
|
34932
35893
|
const layoutNodes = computed.nodes.map(
|
|
34933
35894
|
(node) => {
|
|
34934
35895
|
const pos = g.node(node.id);
|
|
@@ -36670,17 +37631,17 @@ function mulberry32(seed) {
|
|
|
36670
37631
|
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
36671
37632
|
};
|
|
36672
37633
|
}
|
|
36673
|
-
function standardNormal(
|
|
37634
|
+
function standardNormal(rng3) {
|
|
36674
37635
|
let u = 0;
|
|
36675
37636
|
let v = 0;
|
|
36676
|
-
while (u === 0) u =
|
|
36677
|
-
while (v === 0) v =
|
|
37637
|
+
while (u === 0) u = rng3();
|
|
37638
|
+
while (v === 0) v = rng3();
|
|
36678
37639
|
return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
|
|
36679
37640
|
}
|
|
36680
|
-
function gamma(shape,
|
|
37641
|
+
function gamma(shape, rng3) {
|
|
36681
37642
|
if (shape < 1) {
|
|
36682
|
-
const u =
|
|
36683
|
-
return gamma(shape + 1,
|
|
37643
|
+
const u = rng3();
|
|
37644
|
+
return gamma(shape + 1, rng3) * Math.pow(u, 1 / shape);
|
|
36684
37645
|
}
|
|
36685
37646
|
const d = shape - 1 / 3;
|
|
36686
37647
|
const c = 1 / Math.sqrt(9 * d);
|
|
@@ -36688,29 +37649,29 @@ function gamma(shape, rng) {
|
|
|
36688
37649
|
let x;
|
|
36689
37650
|
let v;
|
|
36690
37651
|
do {
|
|
36691
|
-
x = standardNormal(
|
|
37652
|
+
x = standardNormal(rng3);
|
|
36692
37653
|
v = 1 + c * x;
|
|
36693
37654
|
} while (v <= 0);
|
|
36694
37655
|
v = v * v * v;
|
|
36695
|
-
const u =
|
|
37656
|
+
const u = rng3();
|
|
36696
37657
|
if (u < 1 - 0.0331 * x * x * x * x) return d * v;
|
|
36697
37658
|
if (Math.log(u) < x * x / 2 + d * (1 - v + Math.log(v))) return d * v;
|
|
36698
37659
|
}
|
|
36699
37660
|
}
|
|
36700
|
-
function sampleBeta(alpha, beta,
|
|
36701
|
-
const x = gamma(alpha,
|
|
36702
|
-
const y = gamma(beta,
|
|
37661
|
+
function sampleBeta(alpha, beta, rng3) {
|
|
37662
|
+
const x = gamma(alpha, rng3);
|
|
37663
|
+
const y = gamma(beta, rng3);
|
|
36703
37664
|
return x / (x + y);
|
|
36704
37665
|
}
|
|
36705
|
-
function sampleBetaPert(o, m, p,
|
|
37666
|
+
function sampleBetaPert(o, m, p, rng3) {
|
|
36706
37667
|
if (o === p) return m;
|
|
36707
37668
|
const range = p - o;
|
|
36708
37669
|
const alpha = 1 + 4 * (m - o) / range;
|
|
36709
37670
|
const beta = 1 + 4 * (p - m) / range;
|
|
36710
|
-
return o + sampleBeta(alpha, beta,
|
|
37671
|
+
return o + sampleBeta(alpha, beta, rng3) * range;
|
|
36711
37672
|
}
|
|
36712
37673
|
function simulate(resolved, expanded, _predecessors, _successors, topo, terminals, poisoned, opts) {
|
|
36713
|
-
const
|
|
37674
|
+
const rng3 = mulberry32(opts.seed);
|
|
36714
37675
|
const expById = /* @__PURE__ */ new Map();
|
|
36715
37676
|
for (const e of expanded) expById.set(e.id, e);
|
|
36716
37677
|
const completionTimes = new Array(opts.trials);
|
|
@@ -36733,7 +37694,7 @@ function simulate(resolved, expanded, _predecessors, _successors, topo, terminal
|
|
|
36733
37694
|
} else if (exp.o === exp.p) {
|
|
36734
37695
|
duration = exp.m;
|
|
36735
37696
|
} else {
|
|
36736
|
-
duration = sampleBetaPert(exp.o, exp.m, exp.p,
|
|
37697
|
+
duration = sampleBetaPert(exp.o, exp.m, exp.p, rng3);
|
|
36737
37698
|
}
|
|
36738
37699
|
let esLower = 0;
|
|
36739
37700
|
let efLower = -Infinity;
|
|
@@ -37725,7 +38686,7 @@ __export(layout_exports11, {
|
|
|
37725
38686
|
layoutPert: () => layoutPert,
|
|
37726
38687
|
relayoutPert: () => relayoutPert
|
|
37727
38688
|
});
|
|
37728
|
-
import
|
|
38689
|
+
import dagre8 from "@dagrejs/dagre";
|
|
37729
38690
|
function computeNodeSizing(resolved) {
|
|
37730
38691
|
const unit = resolved.options.timeUnit;
|
|
37731
38692
|
const sprintMode = resolved.options.sprintMode;
|
|
@@ -37843,7 +38804,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
|
|
|
37843
38804
|
}
|
|
37844
38805
|
}
|
|
37845
38806
|
const dagreId = (id) => memberToGroup.get(id) ?? id;
|
|
37846
|
-
const g = new
|
|
38807
|
+
const g = new dagre8.graphlib.Graph();
|
|
37847
38808
|
g.setGraph({
|
|
37848
38809
|
rankdir: resolved.options.direction,
|
|
37849
38810
|
nodesep: 50,
|
|
@@ -37882,7 +38843,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
|
|
|
37882
38843
|
seenEdges.add(k);
|
|
37883
38844
|
g.setEdge(src, tgt, {});
|
|
37884
38845
|
}
|
|
37885
|
-
|
|
38846
|
+
dagre8.layout(g);
|
|
37886
38847
|
const swimApplied = applySwimLanes(
|
|
37887
38848
|
g,
|
|
37888
38849
|
resolved,
|
|
@@ -38228,7 +39189,7 @@ function reduceCrossings2(g, direction) {
|
|
|
38228
39189
|
buckets.get(key).push(id);
|
|
38229
39190
|
}
|
|
38230
39191
|
const edges = g.edges().map((e) => ({ v: e.v, w: e.w }));
|
|
38231
|
-
const
|
|
39192
|
+
const countCrossings = () => {
|
|
38232
39193
|
let total = 0;
|
|
38233
39194
|
for (let i = 0; i < edges.length; i++) {
|
|
38234
39195
|
const a = edges[i];
|
|
@@ -38241,13 +39202,13 @@ function reduceCrossings2(g, direction) {
|
|
|
38241
39202
|
const b1 = g.node(b.v);
|
|
38242
39203
|
const b2 = g.node(b.w);
|
|
38243
39204
|
if (!b1 || !b2) continue;
|
|
38244
|
-
if (
|
|
39205
|
+
if (segmentsCross(a1, a2, b1, b2)) total++;
|
|
38245
39206
|
}
|
|
38246
39207
|
}
|
|
38247
39208
|
return total;
|
|
38248
39209
|
};
|
|
38249
39210
|
const MAX_ITER = 8;
|
|
38250
|
-
let baseline =
|
|
39211
|
+
let baseline = countCrossings();
|
|
38251
39212
|
if (baseline === 0) return;
|
|
38252
39213
|
for (let iter = 0; iter < MAX_ITER; iter++) {
|
|
38253
39214
|
let improved = false;
|
|
@@ -38265,7 +39226,7 @@ function reduceCrossings2(g, direction) {
|
|
|
38265
39226
|
const bv = bn[slotAxis];
|
|
38266
39227
|
an[slotAxis] = bv;
|
|
38267
39228
|
bn[slotAxis] = av;
|
|
38268
|
-
const after =
|
|
39229
|
+
const after = countCrossings();
|
|
38269
39230
|
if (after < baseline) {
|
|
38270
39231
|
baseline = after;
|
|
38271
39232
|
improved = true;
|
|
@@ -38286,7 +39247,7 @@ function reduceCrossings2(g, direction) {
|
|
|
38286
39247
|
data.points = smoothEdge(src, tgt, direction);
|
|
38287
39248
|
}
|
|
38288
39249
|
}
|
|
38289
|
-
function
|
|
39250
|
+
function segmentsCross(a1, a2, b1, b2) {
|
|
38290
39251
|
const ccw = (p, q, r) => (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x);
|
|
38291
39252
|
const d1 = ccw(b1, b2, a1);
|
|
38292
39253
|
const d2 = ccw(b1, b2, a2);
|
|
@@ -48047,10 +49008,10 @@ function tierBand(maxSpanDeg) {
|
|
|
48047
49008
|
}
|
|
48048
49009
|
function labelBudget(width, height, band) {
|
|
48049
49010
|
const bandCap = {
|
|
48050
|
-
world:
|
|
48051
|
-
continental:
|
|
48052
|
-
regional:
|
|
48053
|
-
local:
|
|
49011
|
+
world: 7,
|
|
49012
|
+
continental: 6,
|
|
49013
|
+
regional: 5,
|
|
49014
|
+
local: 4
|
|
48054
49015
|
};
|
|
48055
49016
|
const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
|
|
48056
49017
|
return Math.max(0, Math.min(area2, bandCap[band]));
|
|
@@ -48183,8 +49144,11 @@ function placeContextLabels(args) {
|
|
|
48183
49144
|
color: waterColor,
|
|
48184
49145
|
fontSize: FONT,
|
|
48185
49146
|
// water names keep the base font (no footprint to scale on)
|
|
48186
|
-
//
|
|
48187
|
-
|
|
49147
|
+
// Orientation-value bands (lower = earlier): MAJOR water (oceans + major
|
|
49148
|
+
// seas, tier ≤ 1) leads at `tier*10+kind` (0..~16); MINOR water (tier ≥ 2:
|
|
49149
|
+
// smaller seas, bays, gulfs, straits) drops to band 2 (2000+) — BELOW the
|
|
49150
|
+
// country band (1000+), so a big country outranks a minor basin.
|
|
49151
|
+
sort: (tier <= 1 ? 0 : 2e3) + tier * 10 + KIND_ORDER[kind]
|
|
48188
49152
|
});
|
|
48189
49153
|
}
|
|
48190
49154
|
const ranked = countries.map((c) => {
|
|
@@ -48197,7 +49161,7 @@ function placeContextLabels(args) {
|
|
|
48197
49161
|
let ci = 0;
|
|
48198
49162
|
for (const r of ranked) {
|
|
48199
49163
|
const { c, w, h, area: area2 } = r;
|
|
48200
|
-
if (w > width * 0.66
|
|
49164
|
+
if (!c.curatedAnchor && w > width * 0.66 && h > height * 0.66) continue;
|
|
48201
49165
|
if (!insideViewport(c.anchor, width, height)) continue;
|
|
48202
49166
|
const sizeFrac = Math.sqrt(area2) / canvasLinear;
|
|
48203
49167
|
const t = Math.min(
|
|
@@ -48222,15 +49186,23 @@ function placeContextLabels(args) {
|
|
|
48222
49186
|
letterSpacing: 0,
|
|
48223
49187
|
color,
|
|
48224
49188
|
fontSize,
|
|
48225
|
-
//
|
|
48226
|
-
|
|
49189
|
+
// Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
|
|
49190
|
+
// below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
|
|
49191
|
+
// (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
|
|
49192
|
+
// Campeche) yet never displaces an ocean. Larger area = earlier within the
|
|
49193
|
+
// band (`ci` is the area-desc rank index).
|
|
49194
|
+
sort: 1e3 + ci++
|
|
48227
49195
|
});
|
|
48228
49196
|
}
|
|
48229
49197
|
candidates.sort((a, b) => a.sort - b.sort);
|
|
48230
49198
|
const placed = [];
|
|
48231
49199
|
const placedRects = [];
|
|
49200
|
+
const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
|
|
49201
|
+
const waterCap = budget - Math.min(2, countryCount);
|
|
49202
|
+
let waterPlaced = 0;
|
|
48232
49203
|
for (const cand of candidates) {
|
|
48233
49204
|
if (placed.length >= budget) break;
|
|
49205
|
+
if (cand.italic && waterPlaced >= waterCap) continue;
|
|
48234
49206
|
const rect = rectAround(
|
|
48235
49207
|
cand.cx,
|
|
48236
49208
|
cand.cy,
|
|
@@ -48257,6 +49229,7 @@ function placeContextLabels(args) {
|
|
|
48257
49229
|
if (collides(rect)) continue;
|
|
48258
49230
|
if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
|
|
48259
49231
|
placedRects.push(rect);
|
|
49232
|
+
if (cand.italic) waterPlaced++;
|
|
48260
49233
|
placed.push({
|
|
48261
49234
|
x: cand.cx,
|
|
48262
49235
|
y: cand.cy,
|
|
@@ -50230,16 +51203,16 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
50230
51203
|
countryCandidates.push({
|
|
50231
51204
|
name: f.properties?.name ?? iso,
|
|
50232
51205
|
bbox: [x0, y0, x1, y1],
|
|
50233
|
-
anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
|
|
51206
|
+
anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
|
|
51207
|
+
curatedAnchor: !!anchorLngLat
|
|
50234
51208
|
});
|
|
50235
51209
|
}
|
|
50236
51210
|
const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
|
|
50237
51211
|
(id) => id.startsWith("US-")
|
|
50238
51212
|
);
|
|
50239
51213
|
if (usLayer && framedStateContainers) {
|
|
50240
|
-
const containerSet = new Set(resolved.poiFrameContainers);
|
|
50241
51214
|
for (const [iso, f] of usLayer) {
|
|
50242
|
-
if (
|
|
51215
|
+
if (regionById.has(iso)) continue;
|
|
50243
51216
|
const viewF = cullFeatureToView(f);
|
|
50244
51217
|
if (!viewF) continue;
|
|
50245
51218
|
const b = path.bounds(viewF);
|
|
@@ -50367,8 +51340,15 @@ var init_layout15 = __esm({
|
|
|
50367
51340
|
W_MAX = 8;
|
|
50368
51341
|
FONT2 = 11;
|
|
50369
51342
|
WORLD_LABEL_ANCHORS = {
|
|
50370
|
-
US: [-98.5, 39.5]
|
|
51343
|
+
US: [-98.5, 39.5],
|
|
50371
51344
|
// CONUS geographic centre (near Lebanon, Kansas)
|
|
51345
|
+
// Russia crosses the antimeridian (Chukotka at ~170°W), so on a non-global
|
|
51346
|
+
// (e.g. Europe) projection its geometry smears across the whole frame and the
|
|
51347
|
+
// area-weighted centroid lands mid-map (over Europe) — useless as a label
|
|
51348
|
+
// anchor. Pin it to European Russia (~Volga) so a Europe view labels visible
|
|
51349
|
+
// western Russia on its eastern margin; on a world view this still sits over
|
|
51350
|
+
// Russian land. (See the curated-anchor smear-gate bypass in context-labels.)
|
|
51351
|
+
RU: [45, 58]
|
|
50372
51352
|
};
|
|
50373
51353
|
MAX_CLUSTER_EXTENT_FACTOR = 0.18;
|
|
50374
51354
|
MAX_COLUMN_ROWS = 7;
|