@diagrammo/dgmo 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced.cjs +1482 -502
- package/dist/advanced.d.cts +6 -0
- package/dist/advanced.d.ts +6 -0
- package/dist/advanced.js +1479 -499
- package/dist/auto.cjs +1483 -503
- package/dist/auto.js +116 -139
- package/dist/auto.mjs +1480 -500
- package/dist/cli.cjs +168 -191
- package/dist/index.cjs +1482 -502
- package/dist/index.js +1479 -499
- package/dist/internal.cjs +1482 -502
- package/dist/internal.d.cts +6 -0
- package/dist/internal.d.ts +6 -0
- package/dist/internal.js +1479 -499
- package/package.json +7 -3
- package/src/boxes-and-lines/layout-layered.ts +722 -0
- package/src/boxes-and-lines/layout-search.ts +1200 -0
- package/src/boxes-and-lines/layout.ts +67 -556
- package/src/map/context-labels.ts +45 -14
- package/src/map/layout.ts +16 -6
package/dist/index.js
CHANGED
|
@@ -27618,12 +27618,1344 @@ var init_renderer6 = __esm({
|
|
|
27618
27618
|
}
|
|
27619
27619
|
});
|
|
27620
27620
|
|
|
27621
|
+
// src/boxes-and-lines/layout-layered.ts
|
|
27622
|
+
function rng(s) {
|
|
27623
|
+
return () => {
|
|
27624
|
+
s |= 0;
|
|
27625
|
+
s = s + 1831565813 | 0;
|
|
27626
|
+
let t = Math.imul(s ^ s >>> 15, 1 | s);
|
|
27627
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
27628
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
27629
|
+
};
|
|
27630
|
+
}
|
|
27631
|
+
function layeredCandidates(parsed, sizes, opts) {
|
|
27632
|
+
if (parsed.groups.length > 0) return [];
|
|
27633
|
+
const nCount = parsed.nodes.length;
|
|
27634
|
+
if (nCount < 3 || nCount > 40) return [];
|
|
27635
|
+
const isTB = parsed.direction === "TB";
|
|
27636
|
+
const nodesep = opts?.nodesep ?? NODESEP;
|
|
27637
|
+
const ranksep = opts?.ranksep ?? RANKSEP;
|
|
27638
|
+
const backEdgeSide = opts?.backEdgeSide ?? "nearest";
|
|
27639
|
+
const restarts = opts?.restarts ?? (nCount <= 14 ? 28 : nCount <= 25 ? 14 : 6);
|
|
27640
|
+
const keepK = opts?.keepK ?? 3;
|
|
27641
|
+
const labels = parsed.nodes.map((n) => n.label);
|
|
27642
|
+
const labelSet = new Set(labels);
|
|
27643
|
+
const edges = parsed.edges.map((e, i) => ({ e, i })).filter(
|
|
27644
|
+
({ e }) => labelSet.has(e.source) && labelSet.has(e.target) && e.source !== e.target
|
|
27645
|
+
);
|
|
27646
|
+
if (edges.length === 0) return [];
|
|
27647
|
+
const adj = /* @__PURE__ */ new Map();
|
|
27648
|
+
for (const l of labels) adj.set(l, []);
|
|
27649
|
+
for (const { e, i } of edges)
|
|
27650
|
+
adj.get(e.source).push({ to: e.target, idx: i });
|
|
27651
|
+
const reversed = /* @__PURE__ */ new Set();
|
|
27652
|
+
const state = /* @__PURE__ */ new Map();
|
|
27653
|
+
for (const l of labels) state.set(l, 0);
|
|
27654
|
+
const dfs = (u) => {
|
|
27655
|
+
state.set(u, 1);
|
|
27656
|
+
for (const { to, idx } of adj.get(u)) {
|
|
27657
|
+
const st = state.get(to);
|
|
27658
|
+
if (st === 1)
|
|
27659
|
+
reversed.add(idx);
|
|
27660
|
+
else if (st === 0) dfs(to);
|
|
27661
|
+
}
|
|
27662
|
+
state.set(u, 2);
|
|
27663
|
+
};
|
|
27664
|
+
for (const l of labels) if (state.get(l) === 0) dfs(l);
|
|
27665
|
+
const dag = edges.map(
|
|
27666
|
+
({ 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 }
|
|
27667
|
+
);
|
|
27668
|
+
const outDag = /* @__PURE__ */ new Map();
|
|
27669
|
+
const inDeg = /* @__PURE__ */ new Map();
|
|
27670
|
+
for (const l of labels) {
|
|
27671
|
+
outDag.set(l, []);
|
|
27672
|
+
inDeg.set(l, 0);
|
|
27673
|
+
}
|
|
27674
|
+
for (const d of dag) {
|
|
27675
|
+
outDag.get(d.from).push(d.to);
|
|
27676
|
+
inDeg.set(d.to, (inDeg.get(d.to) ?? 0) + 1);
|
|
27677
|
+
}
|
|
27678
|
+
const rank = /* @__PURE__ */ new Map();
|
|
27679
|
+
for (const l of labels) rank.set(l, 0);
|
|
27680
|
+
const queue = labels.filter((l) => (inDeg.get(l) ?? 0) === 0);
|
|
27681
|
+
const indeg2 = new Map(inDeg);
|
|
27682
|
+
const topo = [];
|
|
27683
|
+
while (queue.length) {
|
|
27684
|
+
const u = queue.shift();
|
|
27685
|
+
topo.push(u);
|
|
27686
|
+
for (const v of outDag.get(u)) {
|
|
27687
|
+
rank.set(v, Math.max(rank.get(v), rank.get(u) + 1));
|
|
27688
|
+
indeg2.set(v, indeg2.get(v) - 1);
|
|
27689
|
+
if (indeg2.get(v) === 0) queue.push(v);
|
|
27690
|
+
}
|
|
27691
|
+
}
|
|
27692
|
+
if (topo.length !== labels.length) return [];
|
|
27693
|
+
const maxRank = Math.max(...labels.map((l) => rank.get(l)));
|
|
27694
|
+
const node = /* @__PURE__ */ new Map();
|
|
27695
|
+
for (const n of parsed.nodes) {
|
|
27696
|
+
const s = sizes.get(n.label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
|
|
27697
|
+
node.set(n.label, {
|
|
27698
|
+
id: n.label,
|
|
27699
|
+
dummy: false,
|
|
27700
|
+
rank: rank.get(n.label),
|
|
27701
|
+
cross: 0,
|
|
27702
|
+
thick: isTB ? s.width : s.height,
|
|
27703
|
+
depth: isTB ? s.height : s.width,
|
|
27704
|
+
realW: s.width,
|
|
27705
|
+
realH: s.height
|
|
27706
|
+
});
|
|
27707
|
+
}
|
|
27708
|
+
const segs = [];
|
|
27709
|
+
const chains = [];
|
|
27710
|
+
const backEdges = [];
|
|
27711
|
+
for (const d of dag) {
|
|
27712
|
+
if (d.rev) {
|
|
27713
|
+
backEdges.push({ edgeIdx: d.idx, src: d.to, tgt: d.from });
|
|
27714
|
+
continue;
|
|
27715
|
+
}
|
|
27716
|
+
const r0 = rank.get(d.from);
|
|
27717
|
+
const r1 = rank.get(d.to);
|
|
27718
|
+
const dagChain = [d.from];
|
|
27719
|
+
let prev = d.from;
|
|
27720
|
+
for (let r = r0 + 1; r < r1; r++) {
|
|
27721
|
+
const id = `__d${d.idx}_${r}`;
|
|
27722
|
+
node.set(id, {
|
|
27723
|
+
id,
|
|
27724
|
+
dummy: true,
|
|
27725
|
+
rank: r,
|
|
27726
|
+
cross: 0,
|
|
27727
|
+
thick: DUMMY_THICK,
|
|
27728
|
+
depth: 0,
|
|
27729
|
+
realW: 0,
|
|
27730
|
+
realH: 0
|
|
27731
|
+
});
|
|
27732
|
+
segs.push({ a: prev, b: id });
|
|
27733
|
+
dagChain.push(id);
|
|
27734
|
+
prev = id;
|
|
27735
|
+
}
|
|
27736
|
+
segs.push({ a: prev, b: d.to });
|
|
27737
|
+
dagChain.push(d.to);
|
|
27738
|
+
chains.push({
|
|
27739
|
+
edgeIdx: d.idx,
|
|
27740
|
+
chain: d.rev ? dagChain.slice().reverse() : dagChain
|
|
27741
|
+
});
|
|
27742
|
+
}
|
|
27743
|
+
const ranks = Array.from({ length: maxRank + 1 }, () => []);
|
|
27744
|
+
for (const n of node.values()) ranks[n.rank].push(n.id);
|
|
27745
|
+
const up = /* @__PURE__ */ new Map();
|
|
27746
|
+
const down = /* @__PURE__ */ new Map();
|
|
27747
|
+
for (const id of node.keys()) {
|
|
27748
|
+
up.set(id, []);
|
|
27749
|
+
down.set(id, []);
|
|
27750
|
+
}
|
|
27751
|
+
for (const s of segs) {
|
|
27752
|
+
down.get(s.a).push(s.b);
|
|
27753
|
+
up.get(s.b).push(s.a);
|
|
27754
|
+
}
|
|
27755
|
+
const orderOf = /* @__PURE__ */ new Map();
|
|
27756
|
+
const applyOrder = (ord) => {
|
|
27757
|
+
for (const id of node.keys()) orderOf.set(id, ord.get(id));
|
|
27758
|
+
};
|
|
27759
|
+
const gapCrossings = (r) => {
|
|
27760
|
+
const lower = ranks[r + 1];
|
|
27761
|
+
if (!lower) return 0;
|
|
27762
|
+
const es = [];
|
|
27763
|
+
for (const a of ranks[r]) {
|
|
27764
|
+
const pa = orderOf.get(a);
|
|
27765
|
+
for (const b of down.get(a)) es.push({ p: pa, q: orderOf.get(b) });
|
|
27766
|
+
}
|
|
27767
|
+
let c = 0;
|
|
27768
|
+
for (let i = 0; i < es.length; i++)
|
|
27769
|
+
for (let j = i + 1; j < es.length; j++) {
|
|
27770
|
+
const A = es[i], B = es[j];
|
|
27771
|
+
if ((A.p - B.p) * (A.q - B.q) < 0) c++;
|
|
27772
|
+
}
|
|
27773
|
+
return c;
|
|
27774
|
+
};
|
|
27775
|
+
const totalCrossings = () => {
|
|
27776
|
+
let c = 0;
|
|
27777
|
+
for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
|
|
27778
|
+
return c;
|
|
27779
|
+
};
|
|
27780
|
+
const median = (vals) => {
|
|
27781
|
+
if (vals.length === 0) return -1;
|
|
27782
|
+
vals.sort((x, y) => x - y);
|
|
27783
|
+
const m = Math.floor(vals.length / 2);
|
|
27784
|
+
if (vals.length % 2 === 1) return vals[m];
|
|
27785
|
+
if (vals.length === 2) return (vals[0] + vals[1]) / 2;
|
|
27786
|
+
const left = vals[m - 1] - vals[0];
|
|
27787
|
+
const right = vals[vals.length - 1] - vals[m];
|
|
27788
|
+
return left + right === 0 ? (vals[m - 1] + vals[m]) / 2 : (vals[m - 1] * right + vals[m] * left) / (left + right);
|
|
27789
|
+
};
|
|
27790
|
+
const wmedianRank = (r, useUp) => {
|
|
27791
|
+
const ids = ranks[r];
|
|
27792
|
+
const neigh = useUp ? up : down;
|
|
27793
|
+
const med = /* @__PURE__ */ new Map();
|
|
27794
|
+
for (const id of ids)
|
|
27795
|
+
med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
|
|
27796
|
+
const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
|
|
27797
|
+
const out = new Array(ids.length).fill(null);
|
|
27798
|
+
for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
|
|
27799
|
+
let mi = 0;
|
|
27800
|
+
for (let slot = 0; slot < out.length; slot++)
|
|
27801
|
+
if (out[slot] === null) out[slot] = movable[mi++];
|
|
27802
|
+
const final = out;
|
|
27803
|
+
final.forEach((id, i) => orderOf.set(id, i));
|
|
27804
|
+
ranks[r] = final;
|
|
27805
|
+
};
|
|
27806
|
+
const transpose = () => {
|
|
27807
|
+
let improved = true;
|
|
27808
|
+
let guard = 0;
|
|
27809
|
+
while (improved && guard++ < 12) {
|
|
27810
|
+
improved = false;
|
|
27811
|
+
for (let r = 0; r <= maxRank; r++) {
|
|
27812
|
+
const ids = ranks[r];
|
|
27813
|
+
for (let i = 0; i < ids.length - 1; i++) {
|
|
27814
|
+
const before = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
|
|
27815
|
+
const a = ids[i], b = ids[i + 1];
|
|
27816
|
+
ids[i] = b;
|
|
27817
|
+
ids[i + 1] = a;
|
|
27818
|
+
orderOf.set(b, i);
|
|
27819
|
+
orderOf.set(a, i + 1);
|
|
27820
|
+
const after = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
|
|
27821
|
+
if (after < before) {
|
|
27822
|
+
improved = true;
|
|
27823
|
+
} else {
|
|
27824
|
+
ids[i] = a;
|
|
27825
|
+
ids[i + 1] = b;
|
|
27826
|
+
orderOf.set(a, i);
|
|
27827
|
+
orderOf.set(b, i + 1);
|
|
27828
|
+
}
|
|
27829
|
+
}
|
|
27830
|
+
}
|
|
27831
|
+
}
|
|
27832
|
+
};
|
|
27833
|
+
const initOrder = (seed) => {
|
|
27834
|
+
const r = seed === 0 ? null : rng(seed);
|
|
27835
|
+
for (let rr = 0; rr <= maxRank; rr++) {
|
|
27836
|
+
const ids = ranks[rr].slice();
|
|
27837
|
+
if (r) {
|
|
27838
|
+
for (let i = ids.length - 1; i > 0; i--) {
|
|
27839
|
+
const j = Math.floor(r() * (i + 1));
|
|
27840
|
+
[ids[i], ids[j]] = [ids[j], ids[i]];
|
|
27841
|
+
}
|
|
27842
|
+
}
|
|
27843
|
+
ranks[rr] = ids;
|
|
27844
|
+
ids.forEach((id, i) => orderOf.set(id, i));
|
|
27845
|
+
}
|
|
27846
|
+
};
|
|
27847
|
+
const found = [];
|
|
27848
|
+
for (let s = 0; s < restarts; s++) {
|
|
27849
|
+
initOrder(s);
|
|
27850
|
+
let best = totalCrossings();
|
|
27851
|
+
let bestSnap = new Map(orderOf);
|
|
27852
|
+
for (let iter = 0; iter < 8; iter++) {
|
|
27853
|
+
const down1 = iter % 2 === 0;
|
|
27854
|
+
if (down1) for (let r = 1; r <= maxRank; r++) wmedianRank(r, true);
|
|
27855
|
+
else for (let r = maxRank - 1; r >= 0; r--) wmedianRank(r, false);
|
|
27856
|
+
transpose();
|
|
27857
|
+
const c = totalCrossings();
|
|
27858
|
+
if (c < best) {
|
|
27859
|
+
best = c;
|
|
27860
|
+
bestSnap = new Map(orderOf);
|
|
27861
|
+
}
|
|
27862
|
+
if (c === 0) break;
|
|
27863
|
+
}
|
|
27864
|
+
applyOrder(bestSnap);
|
|
27865
|
+
for (let r = 0; r <= maxRank; r++)
|
|
27866
|
+
ranks[r].sort((a, b) => orderOf.get(a) - orderOf.get(b));
|
|
27867
|
+
const sig = ranks.map((r) => r.join(",")).join("|");
|
|
27868
|
+
if (!found.some((f) => f.sig === sig))
|
|
27869
|
+
found.push({ sig, cross: best, order: new Map(bestSnap) });
|
|
27870
|
+
}
|
|
27871
|
+
found.sort((a, b) => a.cross - b.cross);
|
|
27872
|
+
const keep = found.slice(0, keepK);
|
|
27873
|
+
if (keep.length === 0) return [];
|
|
27874
|
+
const results = [];
|
|
27875
|
+
for (const cand of keep) {
|
|
27876
|
+
applyOrder(cand.order);
|
|
27877
|
+
const rk = Array.from({ length: maxRank + 1 }, () => []);
|
|
27878
|
+
for (const n of node.values()) rk[n.rank].push(n.id);
|
|
27879
|
+
for (let r = 0; r <= maxRank; r++)
|
|
27880
|
+
rk[r].sort((a, b) => cand.order.get(a) - cand.order.get(b));
|
|
27881
|
+
results.push(realize(rk));
|
|
27882
|
+
}
|
|
27883
|
+
return results;
|
|
27884
|
+
function realize(rk) {
|
|
27885
|
+
const bandDepth = rk.map(
|
|
27886
|
+
(ids) => Math.max(0, ...ids.map((id) => node.get(id).depth))
|
|
27887
|
+
);
|
|
27888
|
+
const bandCenter = [];
|
|
27889
|
+
let acc = MARGIN3;
|
|
27890
|
+
for (let r = 0; r <= maxRank; r++) {
|
|
27891
|
+
bandCenter[r] = acc + bandDepth[r] / 2;
|
|
27892
|
+
acc += bandDepth[r] + ranksep;
|
|
27893
|
+
}
|
|
27894
|
+
for (let r = 0; r <= maxRank; r++) {
|
|
27895
|
+
let x = MARGIN3;
|
|
27896
|
+
for (const id of rk[r]) {
|
|
27897
|
+
const n = node.get(id);
|
|
27898
|
+
n.cross = x + n.thick / 2;
|
|
27899
|
+
x += n.thick + nodesep;
|
|
27900
|
+
}
|
|
27901
|
+
}
|
|
27902
|
+
const ITER = 18;
|
|
27903
|
+
for (let it = 0; it < ITER; it++) {
|
|
27904
|
+
const downPass = it % 2 === 0;
|
|
27905
|
+
const order = downPass ? Array.from({ length: maxRank + 1 }, (_, i) => i) : Array.from({ length: maxRank + 1 }, (_, i) => maxRank - i);
|
|
27906
|
+
for (const r of order) {
|
|
27907
|
+
const ids = rk[r];
|
|
27908
|
+
const desired = ids.map((id) => {
|
|
27909
|
+
const n = node.get(id);
|
|
27910
|
+
let sw = 0, sx2 = 0;
|
|
27911
|
+
for (const nb of up.get(id)) {
|
|
27912
|
+
const w = weight(n, node.get(nb));
|
|
27913
|
+
sw += w;
|
|
27914
|
+
sx2 += w * node.get(nb).cross;
|
|
27915
|
+
}
|
|
27916
|
+
for (const nb of down.get(id)) {
|
|
27917
|
+
const w = weight(n, node.get(nb));
|
|
27918
|
+
sw += w;
|
|
27919
|
+
sx2 += w * node.get(nb).cross;
|
|
27920
|
+
}
|
|
27921
|
+
return sw > 0 ? sx2 / sw : n.cross;
|
|
27922
|
+
});
|
|
27923
|
+
placeWithSeparation(ids, desired);
|
|
27924
|
+
}
|
|
27925
|
+
}
|
|
27926
|
+
let minC = Infinity;
|
|
27927
|
+
for (const n of node.values()) minC = Math.min(minC, n.cross - n.thick / 2);
|
|
27928
|
+
const shift = MARGIN3 - minC;
|
|
27929
|
+
for (const n of node.values()) n.cross += shift;
|
|
27930
|
+
const coord = (n) => isTB ? { x: n.cross, y: bandCenter[n.rank] } : { x: bandCenter[n.rank], y: n.cross };
|
|
27931
|
+
const layoutNodes = parsed.nodes.map((pn) => {
|
|
27932
|
+
const n = node.get(pn.label);
|
|
27933
|
+
const c = coord(n);
|
|
27934
|
+
return {
|
|
27935
|
+
label: pn.label,
|
|
27936
|
+
x: c.x,
|
|
27937
|
+
y: c.y,
|
|
27938
|
+
width: n.realW,
|
|
27939
|
+
height: n.realH
|
|
27940
|
+
};
|
|
27941
|
+
});
|
|
27942
|
+
const chainById = /* @__PURE__ */ new Map();
|
|
27943
|
+
for (const ch of chains) chainById.set(ch.edgeIdx, ch.chain);
|
|
27944
|
+
let minCross = Infinity, maxCross = -Infinity;
|
|
27945
|
+
for (const n of node.values()) {
|
|
27946
|
+
if (n.dummy) continue;
|
|
27947
|
+
minCross = Math.min(minCross, n.cross - n.thick / 2);
|
|
27948
|
+
maxCross = Math.max(maxCross, n.cross + n.thick / 2);
|
|
27949
|
+
}
|
|
27950
|
+
const GAP = 34;
|
|
27951
|
+
const LANE = 28;
|
|
27952
|
+
const sideOf = (b) => {
|
|
27953
|
+
if (backEdgeSide === "left") return -1;
|
|
27954
|
+
if (backEdgeSide === "right") return 1;
|
|
27955
|
+
const d = node.get(b.src).cross - node.get(b.tgt).cross;
|
|
27956
|
+
return d >= 0 ? 1 : -1;
|
|
27957
|
+
};
|
|
27958
|
+
const backSorted = backEdges.map((b) => ({
|
|
27959
|
+
...b,
|
|
27960
|
+
side: sideOf(b),
|
|
27961
|
+
span: Math.abs(node.get(b.src).rank - node.get(b.tgt).rank)
|
|
27962
|
+
})).sort((a, b) => a.side - b.side || a.span - b.span);
|
|
27963
|
+
const sideCounts = /* @__PURE__ */ new Map();
|
|
27964
|
+
for (const b of backSorted)
|
|
27965
|
+
sideCounts.set(b.side, (sideCounts.get(b.side) ?? 0) + 1);
|
|
27966
|
+
const laneCounter = /* @__PURE__ */ new Map();
|
|
27967
|
+
const backPoints = /* @__PURE__ */ new Map();
|
|
27968
|
+
const faceLim = (depth) => Math.max(0, depth / 2 - 6);
|
|
27969
|
+
const hubIndex = /* @__PURE__ */ new Map();
|
|
27970
|
+
const hubSize = /* @__PURE__ */ new Map();
|
|
27971
|
+
{
|
|
27972
|
+
const counter = /* @__PURE__ */ new Map();
|
|
27973
|
+
for (const b of backSorted) {
|
|
27974
|
+
const key = `${b.side}|${b.tgt}`;
|
|
27975
|
+
const idx = counter.get(key) ?? 0;
|
|
27976
|
+
counter.set(key, idx + 1);
|
|
27977
|
+
hubIndex.set(b.edgeIdx, idx);
|
|
27978
|
+
}
|
|
27979
|
+
for (const [key, v] of counter) hubSize.set(key, v);
|
|
27980
|
+
}
|
|
27981
|
+
for (const b of backSorted) {
|
|
27982
|
+
const s = b.side;
|
|
27983
|
+
const k = laneCounter.get(s) ?? 0;
|
|
27984
|
+
laneCounter.set(s, k + 1);
|
|
27985
|
+
const K = sideCounts.get(s);
|
|
27986
|
+
const src = node.get(b.src);
|
|
27987
|
+
const tgt = node.get(b.tgt);
|
|
27988
|
+
const laneC = s > 0 ? maxCross + GAP + k * LANE : minCross - GAP - k * LANE;
|
|
27989
|
+
const srcCtr = bandCenter[src.rank];
|
|
27990
|
+
const tgtCtr = bandCenter[tgt.rank];
|
|
27991
|
+
const frac = K > 1 ? (k + 1) / (K + 1) : 0;
|
|
27992
|
+
const srcR = srcCtr + frac * faceLim(src.depth);
|
|
27993
|
+
const tgtR = tgtCtr - frac * faceLim(tgt.depth);
|
|
27994
|
+
const m = (c, r) => isTB ? { x: c, y: r } : { x: r, y: c };
|
|
27995
|
+
const off = 10;
|
|
27996
|
+
const srcEdgeC = src.cross + s * (src.thick / 2 + off);
|
|
27997
|
+
const tgtEdgeC = tgt.cross + s * (tgt.thick / 2 + off);
|
|
27998
|
+
const hubK = hubIndex.get(b.edgeIdx);
|
|
27999
|
+
const hubN = hubSize.get(`${s}|${b.tgt}`);
|
|
28000
|
+
if (hubN > 1 && hubK > 0) {
|
|
28001
|
+
const tgtTop = tgtCtr - (tgt.depth / 2 + GAP + (hubK - 1) * LANE);
|
|
28002
|
+
const entryCross = tgt.cross + s * faceLim(tgt.thick) * (hubK / hubN);
|
|
28003
|
+
backPoints.set(b.edgeIdx, [
|
|
28004
|
+
m(src.cross, srcCtr),
|
|
28005
|
+
m(srcEdgeC, srcR),
|
|
28006
|
+
m(laneC, srcR),
|
|
28007
|
+
m(laneC, tgtTop),
|
|
28008
|
+
m(entryCross, tgtTop),
|
|
28009
|
+
m(tgt.cross, tgtCtr)
|
|
28010
|
+
]);
|
|
28011
|
+
} else {
|
|
28012
|
+
backPoints.set(b.edgeIdx, [
|
|
28013
|
+
m(src.cross, srcCtr),
|
|
28014
|
+
m(srcEdgeC, srcR),
|
|
28015
|
+
m(laneC, srcR),
|
|
28016
|
+
m(laneC, tgtR),
|
|
28017
|
+
m(tgtEdgeC, tgtR),
|
|
28018
|
+
m(tgt.cross, tgtCtr)
|
|
28019
|
+
]);
|
|
28020
|
+
}
|
|
28021
|
+
}
|
|
28022
|
+
const layoutEdges = [];
|
|
28023
|
+
for (let i = 0; i < parsed.edges.length; i++) {
|
|
28024
|
+
const e = parsed.edges[i];
|
|
28025
|
+
if (!labelSet.has(e.source) || !labelSet.has(e.target)) continue;
|
|
28026
|
+
let points;
|
|
28027
|
+
if (e.source === e.target) {
|
|
28028
|
+
const c = coord(node.get(e.source));
|
|
28029
|
+
points = [c, c];
|
|
28030
|
+
} else if (backPoints.has(i)) {
|
|
28031
|
+
points = backPoints.get(i);
|
|
28032
|
+
} else {
|
|
28033
|
+
const chain = chainById.get(i);
|
|
28034
|
+
if (!chain) continue;
|
|
28035
|
+
points = chain.map((id) => coord(node.get(id)));
|
|
28036
|
+
}
|
|
28037
|
+
layoutEdges.push({
|
|
28038
|
+
source: e.source,
|
|
28039
|
+
target: e.target,
|
|
28040
|
+
...e.label !== void 0 && { label: e.label },
|
|
28041
|
+
bidirectional: e.bidirectional,
|
|
28042
|
+
lineNumber: e.lineNumber,
|
|
28043
|
+
points,
|
|
28044
|
+
yOffset: 0,
|
|
28045
|
+
parallelCount: 1,
|
|
28046
|
+
metadata: e.metadata
|
|
28047
|
+
});
|
|
28048
|
+
}
|
|
28049
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
28050
|
+
for (const n of layoutNodes) {
|
|
28051
|
+
minX = Math.min(minX, n.x - n.width / 2);
|
|
28052
|
+
minY = Math.min(minY, n.y - n.height / 2);
|
|
28053
|
+
maxX = Math.max(maxX, n.x + n.width / 2);
|
|
28054
|
+
maxY = Math.max(maxY, n.y + n.height / 2);
|
|
28055
|
+
}
|
|
28056
|
+
for (const e of layoutEdges)
|
|
28057
|
+
for (const p of e.points) {
|
|
28058
|
+
minX = Math.min(minX, p.x);
|
|
28059
|
+
minY = Math.min(minY, p.y);
|
|
28060
|
+
maxX = Math.max(maxX, p.x);
|
|
28061
|
+
maxY = Math.max(maxY, p.y);
|
|
28062
|
+
}
|
|
28063
|
+
const sx = MARGIN3 - minX;
|
|
28064
|
+
const sy = MARGIN3 - minY;
|
|
28065
|
+
const shifted = sx !== 0 || sy !== 0 ? {
|
|
28066
|
+
nodes: layoutNodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })),
|
|
28067
|
+
edges: layoutEdges.map((e) => ({
|
|
28068
|
+
...e,
|
|
28069
|
+
points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
|
|
28070
|
+
}))
|
|
28071
|
+
} : { nodes: layoutNodes, edges: layoutEdges };
|
|
28072
|
+
return {
|
|
28073
|
+
nodes: shifted.nodes,
|
|
28074
|
+
edges: shifted.edges,
|
|
28075
|
+
groups: [],
|
|
28076
|
+
width: maxX + sx + MARGIN3,
|
|
28077
|
+
height: maxY + sy + MARGIN3
|
|
28078
|
+
};
|
|
28079
|
+
}
|
|
28080
|
+
function weight(a, b) {
|
|
28081
|
+
if (a.dummy && b.dummy) return W_DUMMY_DUMMY;
|
|
28082
|
+
if (a.dummy || b.dummy) return W_REAL_DUMMY;
|
|
28083
|
+
return W_REAL_REAL;
|
|
28084
|
+
}
|
|
28085
|
+
function placeWithSeparation(ids, desired) {
|
|
28086
|
+
const n = ids.length;
|
|
28087
|
+
if (n === 0) return;
|
|
28088
|
+
const half = ids.map((id) => node.get(id).thick / 2);
|
|
28089
|
+
const off = [0];
|
|
28090
|
+
for (let i = 1; i < n; i++)
|
|
28091
|
+
off[i] = off[i - 1] + half[i - 1] + nodesep + half[i];
|
|
28092
|
+
const d = desired.map((v, i) => v - off[i]);
|
|
28093
|
+
const blocks = [];
|
|
28094
|
+
for (let i = 0; i < n; i++) {
|
|
28095
|
+
let b = { pos: d[i], count: 1, sumOffset: d[i], first: i };
|
|
28096
|
+
while (blocks.length && blocks[blocks.length - 1].pos >= b.pos) {
|
|
28097
|
+
const prev = blocks.pop();
|
|
28098
|
+
const count = prev.count + b.count;
|
|
28099
|
+
const sumOffset = prev.sumOffset + b.sumOffset;
|
|
28100
|
+
b = { pos: sumOffset / count, count, sumOffset, first: prev.first };
|
|
28101
|
+
}
|
|
28102
|
+
blocks.push(b);
|
|
28103
|
+
}
|
|
28104
|
+
const xs = new Array(n);
|
|
28105
|
+
for (const b of blocks)
|
|
28106
|
+
for (let k = 0; k < b.count; k++) xs[b.first + k] = b.pos;
|
|
28107
|
+
for (let i = 0; i < n; i++) node.get(ids[i]).cross = xs[i] + off[i];
|
|
28108
|
+
}
|
|
28109
|
+
}
|
|
28110
|
+
var NODESEP, RANKSEP, DUMMY_THICK, MARGIN3, W_REAL_REAL, W_REAL_DUMMY, W_DUMMY_DUMMY;
|
|
28111
|
+
var init_layout_layered = __esm({
|
|
28112
|
+
"src/boxes-and-lines/layout-layered.ts"() {
|
|
28113
|
+
"use strict";
|
|
28114
|
+
init_layout5();
|
|
28115
|
+
NODESEP = 50;
|
|
28116
|
+
RANKSEP = 60;
|
|
28117
|
+
DUMMY_THICK = 10;
|
|
28118
|
+
MARGIN3 = 40;
|
|
28119
|
+
W_REAL_REAL = 1;
|
|
28120
|
+
W_REAL_DUMMY = 2;
|
|
28121
|
+
W_DUMMY_DUMMY = 8;
|
|
28122
|
+
}
|
|
28123
|
+
});
|
|
28124
|
+
|
|
28125
|
+
// src/boxes-and-lines/layout-search.ts
|
|
28126
|
+
var layout_search_exports = {};
|
|
28127
|
+
__export(layout_search_exports, {
|
|
28128
|
+
countEdgeNearMiss: () => countEdgeNearMiss,
|
|
28129
|
+
countEdgeNodePierces: () => countEdgeNodePierces,
|
|
28130
|
+
countEdgeOverlaps: () => countEdgeOverlaps,
|
|
28131
|
+
countGroupOverlaps: () => countGroupOverlaps,
|
|
28132
|
+
countSplineCrossings: () => countSplineCrossings,
|
|
28133
|
+
deroutePierces: () => deroutePierces,
|
|
28134
|
+
detectEdgeNodePierces: () => detectEdgeNodePierces,
|
|
28135
|
+
detectEdgeOverlaps: () => detectEdgeOverlaps,
|
|
28136
|
+
detectGroupOverlaps: () => detectGroupOverlaps,
|
|
28137
|
+
layoutBoxesAndLinesSearch: () => layoutBoxesAndLinesSearch,
|
|
28138
|
+
separateGroupBands: () => separateGroupBands
|
|
28139
|
+
});
|
|
28140
|
+
import dagre4 from "@dagrejs/dagre";
|
|
28141
|
+
import { line as d3line, curveBasis as curveBasis5 } from "d3-shape";
|
|
28142
|
+
function rng2(s) {
|
|
28143
|
+
return () => {
|
|
28144
|
+
s |= 0;
|
|
28145
|
+
s = s + 1831565813 | 0;
|
|
28146
|
+
let t = Math.imul(s ^ s >>> 15, 1 | s);
|
|
28147
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
28148
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
28149
|
+
};
|
|
28150
|
+
}
|
|
28151
|
+
function shuffle(a, r) {
|
|
28152
|
+
const x = a.slice();
|
|
28153
|
+
for (let i = x.length - 1; i > 0; i--) {
|
|
28154
|
+
const j = Math.floor(r() * (i + 1));
|
|
28155
|
+
[x[i], x[j]] = [x[j], x[i]];
|
|
28156
|
+
}
|
|
28157
|
+
return x;
|
|
28158
|
+
}
|
|
28159
|
+
function flatten(d) {
|
|
28160
|
+
const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
|
|
28161
|
+
const pts = [];
|
|
28162
|
+
let i = 0, cx = 0, cy = 0, cmd = "";
|
|
28163
|
+
const num = () => parseFloat(toks[i++]);
|
|
28164
|
+
const samp = (p0, c1, c2, p1) => {
|
|
28165
|
+
for (let t = 0; t <= 1; t += 0.12) {
|
|
28166
|
+
const u = 1 - t;
|
|
28167
|
+
if (c2)
|
|
28168
|
+
pts.push({
|
|
28169
|
+
x: u * u * u * p0.x + 3 * u * u * t * c1.x + 3 * u * t * t * c2.x + t * t * t * p1.x,
|
|
28170
|
+
y: u * u * u * p0.y + 3 * u * u * t * c1.y + 3 * u * t * t * c2.y + t * t * t * p1.y
|
|
28171
|
+
});
|
|
28172
|
+
else
|
|
28173
|
+
pts.push({
|
|
28174
|
+
x: u * u * p0.x + 2 * u * t * c1.x + t * t * p1.x,
|
|
28175
|
+
y: u * u * p0.y + 2 * u * t * c1.y + t * t * p1.y
|
|
28176
|
+
});
|
|
28177
|
+
}
|
|
28178
|
+
};
|
|
28179
|
+
while (i < toks.length) {
|
|
28180
|
+
const tk = toks[i];
|
|
28181
|
+
if (/[MLQC]/i.test(tk)) {
|
|
28182
|
+
cmd = tk;
|
|
28183
|
+
i++;
|
|
28184
|
+
}
|
|
28185
|
+
if (cmd === "M" || cmd === "L") {
|
|
28186
|
+
const x = num(), y = num();
|
|
28187
|
+
pts.push({ x, y });
|
|
28188
|
+
cx = x;
|
|
28189
|
+
cy = y;
|
|
28190
|
+
} else if (cmd === "Q") {
|
|
28191
|
+
const c1 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
|
|
28192
|
+
samp({ x: cx, y: cy }, c1, null, p1);
|
|
28193
|
+
cx = p1.x;
|
|
28194
|
+
cy = p1.y;
|
|
28195
|
+
} else if (cmd === "C") {
|
|
28196
|
+
const c1 = { x: num(), y: num() }, c2 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
|
|
28197
|
+
samp({ x: cx, y: cy }, c1, c2, p1);
|
|
28198
|
+
cx = p1.x;
|
|
28199
|
+
cy = p1.y;
|
|
28200
|
+
} else i++;
|
|
28201
|
+
}
|
|
28202
|
+
return pts;
|
|
28203
|
+
}
|
|
28204
|
+
function segPoint(p1, p2, p3, p4) {
|
|
28205
|
+
const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
|
|
28206
|
+
if (Math.abs(den) < 1e-9) return null;
|
|
28207
|
+
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;
|
|
28208
|
+
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;
|
|
28209
|
+
}
|
|
28210
|
+
function countSplineCrossings(layout) {
|
|
28211
|
+
const center = /* @__PURE__ */ new Map();
|
|
28212
|
+
for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
|
|
28213
|
+
for (const g of layout.groups)
|
|
28214
|
+
if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
|
|
28215
|
+
const polys = layout.edges.map((e) => {
|
|
28216
|
+
const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
|
|
28217
|
+
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
28218
|
+
for (const p of pts) {
|
|
28219
|
+
if (p.x < x0) x0 = p.x;
|
|
28220
|
+
if (p.x > x1) x1 = p.x;
|
|
28221
|
+
if (p.y < y0) y0 = p.y;
|
|
28222
|
+
if (p.y > y1) y1 = p.y;
|
|
28223
|
+
}
|
|
28224
|
+
return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
|
|
28225
|
+
});
|
|
28226
|
+
const R = 34;
|
|
28227
|
+
let total = 0;
|
|
28228
|
+
for (let a = 0; a < polys.length; a++)
|
|
28229
|
+
for (let b = a + 1; b < polys.length; b++) {
|
|
28230
|
+
const A = polys[a], B = polys[b];
|
|
28231
|
+
if (A.pts.length < 2 || B.pts.length < 2) continue;
|
|
28232
|
+
if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
|
|
28233
|
+
const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
|
|
28234
|
+
const hits = [];
|
|
28235
|
+
for (let i = 1; i < A.pts.length; i++)
|
|
28236
|
+
for (let j = 1; j < B.pts.length; j++) {
|
|
28237
|
+
const p = segPoint(
|
|
28238
|
+
A.pts[i - 1],
|
|
28239
|
+
A.pts[i],
|
|
28240
|
+
B.pts[j - 1],
|
|
28241
|
+
B.pts[j]
|
|
28242
|
+
);
|
|
28243
|
+
if (!p) continue;
|
|
28244
|
+
if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
|
|
28245
|
+
continue;
|
|
28246
|
+
if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
|
|
28247
|
+
hits.push(p);
|
|
28248
|
+
}
|
|
28249
|
+
total += hits.length;
|
|
28250
|
+
}
|
|
28251
|
+
return total;
|
|
28252
|
+
}
|
|
28253
|
+
function pointSegDist(p, a, b) {
|
|
28254
|
+
const dx = b.x - a.x, dy = b.y - a.y;
|
|
28255
|
+
const len2 = dx * dx + dy * dy;
|
|
28256
|
+
if (len2 < 1e-9) return Math.hypot(p.x - a.x, p.y - a.y);
|
|
28257
|
+
let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
|
|
28258
|
+
t = Math.max(0, Math.min(1, t));
|
|
28259
|
+
return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));
|
|
28260
|
+
}
|
|
28261
|
+
function distToPoly(p, poly) {
|
|
28262
|
+
let m = Infinity;
|
|
28263
|
+
for (let i = 1; i < poly.length; i++)
|
|
28264
|
+
m = Math.min(m, pointSegDist(p, poly[i - 1], poly[i]));
|
|
28265
|
+
return m;
|
|
28266
|
+
}
|
|
28267
|
+
function pointRectDist(p, r) {
|
|
28268
|
+
const dx = Math.max(r.x - r.w / 2 - p.x, 0, p.x - (r.x + r.w / 2));
|
|
28269
|
+
const dy = Math.max(r.y - r.h / 2 - p.y, 0, p.y - (r.y + r.h / 2));
|
|
28270
|
+
return Math.hypot(dx, dy);
|
|
28271
|
+
}
|
|
28272
|
+
function detectEdgeOverlaps(layout, opts) {
|
|
28273
|
+
const dist = opts?.dist ?? 8;
|
|
28274
|
+
const minLen = opts?.minLen ?? 16;
|
|
28275
|
+
const nodeClear = opts?.nodeClear ?? 12;
|
|
28276
|
+
const rect = /* @__PURE__ */ new Map();
|
|
28277
|
+
for (const n of layout.nodes)
|
|
28278
|
+
rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
|
|
28279
|
+
for (const g of layout.groups)
|
|
28280
|
+
if (g.collapsed)
|
|
28281
|
+
rect.set("__group_" + g.label, {
|
|
28282
|
+
x: g.x,
|
|
28283
|
+
y: g.y,
|
|
28284
|
+
w: g.width,
|
|
28285
|
+
h: g.height
|
|
28286
|
+
});
|
|
28287
|
+
const polys = layout.edges.map((e) => {
|
|
28288
|
+
const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
|
|
28289
|
+
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
28290
|
+
for (const p of pts) {
|
|
28291
|
+
if (p.x < x0) x0 = p.x;
|
|
28292
|
+
if (p.x > x1) x1 = p.x;
|
|
28293
|
+
if (p.y < y0) y0 = p.y;
|
|
28294
|
+
if (p.y > y1) y1 = p.y;
|
|
28295
|
+
}
|
|
28296
|
+
return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
|
|
28297
|
+
});
|
|
28298
|
+
const runs = [];
|
|
28299
|
+
for (let a = 0; a < polys.length; a++)
|
|
28300
|
+
for (let b = a + 1; b < polys.length; b++) {
|
|
28301
|
+
const A = polys[a], B = polys[b];
|
|
28302
|
+
if (A.pts.length < 2 || B.pts.length < 2) continue;
|
|
28303
|
+
if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
|
|
28304
|
+
continue;
|
|
28305
|
+
const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
|
|
28306
|
+
let run = [];
|
|
28307
|
+
let runLen = 0;
|
|
28308
|
+
const flush = () => {
|
|
28309
|
+
if (runLen >= minLen && run.length >= 2)
|
|
28310
|
+
runs.push({
|
|
28311
|
+
mid: run[Math.floor(run.length / 2)],
|
|
28312
|
+
length: runLen,
|
|
28313
|
+
pts: run.slice()
|
|
28314
|
+
});
|
|
28315
|
+
run = [];
|
|
28316
|
+
runLen = 0;
|
|
28317
|
+
};
|
|
28318
|
+
for (const p of A.pts) {
|
|
28319
|
+
const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
|
|
28320
|
+
const covered = !nearShared && distToPoly(p, B.pts) < dist;
|
|
28321
|
+
if (covered) {
|
|
28322
|
+
if (run.length)
|
|
28323
|
+
runLen += Math.hypot(
|
|
28324
|
+
p.x - run[run.length - 1].x,
|
|
28325
|
+
p.y - run[run.length - 1].y
|
|
28326
|
+
);
|
|
28327
|
+
run.push(p);
|
|
28328
|
+
} else flush();
|
|
28329
|
+
}
|
|
28330
|
+
flush();
|
|
28331
|
+
}
|
|
28332
|
+
return runs;
|
|
28333
|
+
}
|
|
28334
|
+
function countEdgeOverlaps(layout, opts) {
|
|
28335
|
+
return detectEdgeOverlaps(layout, opts).length;
|
|
28336
|
+
}
|
|
28337
|
+
function detectEdgeNodePierces(layout, opts) {
|
|
28338
|
+
const inset = opts?.inset ?? 6;
|
|
28339
|
+
const minPts = opts?.minPts ?? 2;
|
|
28340
|
+
const rects = [];
|
|
28341
|
+
for (const n of layout.nodes)
|
|
28342
|
+
rects.push({ key: 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
|
+
rects.push({
|
|
28346
|
+
key: "__group_" + g.label,
|
|
28347
|
+
x: g.x,
|
|
28348
|
+
y: g.y,
|
|
28349
|
+
w: g.width,
|
|
28350
|
+
h: g.height
|
|
28351
|
+
});
|
|
28352
|
+
const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
|
|
28353
|
+
const out = [];
|
|
28354
|
+
layout.edges.forEach((e, idx) => {
|
|
28355
|
+
if (e.points.length < 2) return;
|
|
28356
|
+
const poly = flatten(splineGen(e.points) ?? "");
|
|
28357
|
+
for (const r of rects) {
|
|
28358
|
+
if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
|
|
28359
|
+
continue;
|
|
28360
|
+
const hits = poly.filter((p) => inside(p, r));
|
|
28361
|
+
if (hits.length >= minPts)
|
|
28362
|
+
out.push({ edgeIdx: idx, node: r.key, pts: hits });
|
|
28363
|
+
}
|
|
28364
|
+
});
|
|
28365
|
+
return out;
|
|
28366
|
+
}
|
|
28367
|
+
function countEdgeNodePierces(layout, opts) {
|
|
28368
|
+
return detectEdgeNodePierces(layout, opts).length;
|
|
28369
|
+
}
|
|
28370
|
+
function deroutePierces(layout) {
|
|
28371
|
+
const pierces = detectEdgeNodePierces(layout);
|
|
28372
|
+
if (!pierces.length) return layout;
|
|
28373
|
+
const rect = /* @__PURE__ */ new Map();
|
|
28374
|
+
for (const n of layout.nodes)
|
|
28375
|
+
rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
|
|
28376
|
+
for (const g of layout.groups)
|
|
28377
|
+
if (g.collapsed)
|
|
28378
|
+
rect.set("__group_" + g.label, {
|
|
28379
|
+
x: g.x,
|
|
28380
|
+
y: g.y,
|
|
28381
|
+
w: g.width,
|
|
28382
|
+
h: g.height
|
|
28383
|
+
});
|
|
28384
|
+
const byEdge = /* @__PURE__ */ new Map();
|
|
28385
|
+
for (const p of pierces) {
|
|
28386
|
+
const arr = byEdge.get(p.edgeIdx);
|
|
28387
|
+
if (arr) arr.push(p.node);
|
|
28388
|
+
else byEdge.set(p.edgeIdx, [p.node]);
|
|
28389
|
+
}
|
|
28390
|
+
const edges = layout.edges.map((e, idx) => {
|
|
28391
|
+
const nodes = byEdge.get(idx);
|
|
28392
|
+
if (!nodes) return e;
|
|
28393
|
+
let pts = e.points.map((p) => ({ x: p.x, y: p.y }));
|
|
28394
|
+
for (const label of nodes) {
|
|
28395
|
+
const r = rect.get(label);
|
|
28396
|
+
if (r) pts = detourAround(pts, r);
|
|
28397
|
+
}
|
|
28398
|
+
return { ...e, points: pts };
|
|
28399
|
+
});
|
|
28400
|
+
return { ...layout, edges };
|
|
28401
|
+
}
|
|
28402
|
+
function detourAround(pts, r) {
|
|
28403
|
+
if (pts.length < 2) return pts;
|
|
28404
|
+
const c = { x: r.x, y: r.y };
|
|
28405
|
+
let bestSeg = -1;
|
|
28406
|
+
let bestT = 0;
|
|
28407
|
+
let bestD = Infinity;
|
|
28408
|
+
let bestPt = pts[0];
|
|
28409
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
28410
|
+
const a2 = pts[i], b2 = pts[i + 1];
|
|
28411
|
+
const dx = b2.x - a2.x, dy = b2.y - a2.y;
|
|
28412
|
+
const len2 = dx * dx + dy * dy || 1e-9;
|
|
28413
|
+
let t = ((c.x - a2.x) * dx + (c.y - a2.y) * dy) / len2;
|
|
28414
|
+
t = Math.max(0, Math.min(1, t));
|
|
28415
|
+
const q = { x: a2.x + t * dx, y: a2.y + t * dy };
|
|
28416
|
+
const d = Math.hypot(q.x - c.x, q.y - c.y);
|
|
28417
|
+
if (d < bestD) {
|
|
28418
|
+
bestD = d;
|
|
28419
|
+
bestSeg = i;
|
|
28420
|
+
bestT = t;
|
|
28421
|
+
bestPt = q;
|
|
28422
|
+
}
|
|
28423
|
+
}
|
|
28424
|
+
if (bestSeg < 0) return pts;
|
|
28425
|
+
let nx = bestPt.x - c.x;
|
|
28426
|
+
let ny = bestPt.y - c.y;
|
|
28427
|
+
if (Math.hypot(nx, ny) < 1) {
|
|
28428
|
+
if (r.w <= r.h) {
|
|
28429
|
+
nx = 1;
|
|
28430
|
+
ny = 0;
|
|
28431
|
+
} else {
|
|
28432
|
+
nx = 0;
|
|
28433
|
+
ny = 1;
|
|
28434
|
+
}
|
|
28435
|
+
}
|
|
28436
|
+
const nlen = Math.hypot(nx, ny) || 1;
|
|
28437
|
+
nx /= nlen;
|
|
28438
|
+
ny /= nlen;
|
|
28439
|
+
const a = pts[bestSeg], b = pts[bestSeg + 1];
|
|
28440
|
+
let ex = b.x - a.x, ey = b.y - a.y;
|
|
28441
|
+
const elen = Math.hypot(ex, ey) || 1;
|
|
28442
|
+
ex /= elen;
|
|
28443
|
+
ey /= elen;
|
|
28444
|
+
const clear = Math.hypot(r.w, r.h) / 2 + 18;
|
|
28445
|
+
const hw = Math.hypot(r.w, r.h) / 2;
|
|
28446
|
+
void bestT;
|
|
28447
|
+
const before = {
|
|
28448
|
+
x: bestPt.x - ex * hw + nx * clear * 0.5,
|
|
28449
|
+
y: bestPt.y - ey * hw + ny * clear * 0.5
|
|
28450
|
+
};
|
|
28451
|
+
const peak = { x: c.x + nx * clear, y: c.y + ny * clear };
|
|
28452
|
+
const after = {
|
|
28453
|
+
x: bestPt.x + ex * hw + nx * clear * 0.5,
|
|
28454
|
+
y: bestPt.y + ey * hw + ny * clear * 0.5
|
|
28455
|
+
};
|
|
28456
|
+
const out = pts.slice(0, bestSeg + 1);
|
|
28457
|
+
out.push(before, peak, after);
|
|
28458
|
+
out.push(...pts.slice(bestSeg + 1));
|
|
28459
|
+
return out;
|
|
28460
|
+
}
|
|
28461
|
+
function countEdgeNearMiss(layout, opts) {
|
|
28462
|
+
const near = opts?.near ?? 14;
|
|
28463
|
+
const tight = opts?.tight ?? 8;
|
|
28464
|
+
const minLen = opts?.minLen ?? 18;
|
|
28465
|
+
const close = detectEdgeOverlaps(layout, { dist: near, minLen }).length;
|
|
28466
|
+
const over = detectEdgeOverlaps(layout, { dist: tight, minLen }).length;
|
|
28467
|
+
return Math.max(0, close - over);
|
|
28468
|
+
}
|
|
28469
|
+
function detectGroupOverlaps(layout, opts) {
|
|
28470
|
+
const margin = opts?.margin ?? 4;
|
|
28471
|
+
const raw = layout.groups.map((g) => ({
|
|
28472
|
+
label: g.label,
|
|
28473
|
+
l: g.x - g.width / 2,
|
|
28474
|
+
r: g.x + g.width / 2,
|
|
28475
|
+
t: g.y - g.height / 2,
|
|
28476
|
+
b: g.y + g.height / 2
|
|
28477
|
+
}));
|
|
28478
|
+
const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
|
|
28479
|
+
const rend = raw.map((a, i) => {
|
|
28480
|
+
const parent = raw.some((b, j) => j !== i && contains(a, b));
|
|
28481
|
+
return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
|
|
28482
|
+
});
|
|
28483
|
+
const hit = /* @__PURE__ */ new Set();
|
|
28484
|
+
for (let i = 0; i < raw.length; i++)
|
|
28485
|
+
for (let j = i + 1; j < raw.length; j++) {
|
|
28486
|
+
if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
|
|
28487
|
+
const a = rend[i], b = rend[j];
|
|
28488
|
+
const dx = Math.max(a.l - b.r, b.l - a.r);
|
|
28489
|
+
const dy = Math.max(a.t - b.b, b.t - a.b);
|
|
28490
|
+
if (Math.max(dx, dy) < margin) {
|
|
28491
|
+
hit.add(raw[i].label);
|
|
28492
|
+
hit.add(raw[j].label);
|
|
28493
|
+
}
|
|
28494
|
+
}
|
|
28495
|
+
return [...hit];
|
|
28496
|
+
}
|
|
28497
|
+
function countGroupOverlaps(layout, opts) {
|
|
28498
|
+
const margin = opts?.margin ?? 4;
|
|
28499
|
+
const raw = layout.groups.map((g) => ({
|
|
28500
|
+
l: g.x - g.width / 2,
|
|
28501
|
+
r: g.x + g.width / 2,
|
|
28502
|
+
t: g.y - g.height / 2,
|
|
28503
|
+
b: g.y + g.height / 2
|
|
28504
|
+
}));
|
|
28505
|
+
const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
|
|
28506
|
+
const rend = raw.map((a, i) => {
|
|
28507
|
+
const parent = raw.some((b, j) => j !== i && contains(a, b));
|
|
28508
|
+
return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
|
|
28509
|
+
});
|
|
28510
|
+
let count = 0;
|
|
28511
|
+
for (let i = 0; i < raw.length; i++)
|
|
28512
|
+
for (let j = i + 1; j < raw.length; j++) {
|
|
28513
|
+
if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
|
|
28514
|
+
const a = rend[i], b = rend[j];
|
|
28515
|
+
const dx = Math.max(a.l - b.r, b.l - a.r);
|
|
28516
|
+
const dy = Math.max(a.t - b.b, b.t - a.b);
|
|
28517
|
+
if (Math.max(dx, dy) < margin) count++;
|
|
28518
|
+
}
|
|
28519
|
+
return count;
|
|
28520
|
+
}
|
|
28521
|
+
function separateGroupBands(layout, parsed) {
|
|
28522
|
+
if (layout.groups.some((g) => g.collapsed)) return layout;
|
|
28523
|
+
if (countGroupOverlaps(layout) === 0) return layout;
|
|
28524
|
+
const parentOf = /* @__PURE__ */ new Map();
|
|
28525
|
+
for (const g of parsed.groups) parentOf.set(g.label, g.parentGroup);
|
|
28526
|
+
const topOf = (label) => {
|
|
28527
|
+
let cur = label;
|
|
28528
|
+
const seen = /* @__PURE__ */ new Set();
|
|
28529
|
+
for (; ; ) {
|
|
28530
|
+
const p = parentOf.get(cur);
|
|
28531
|
+
if (!p || seen.has(p)) return cur;
|
|
28532
|
+
seen.add(cur);
|
|
28533
|
+
cur = p;
|
|
28534
|
+
}
|
|
28535
|
+
};
|
|
28536
|
+
const topLabels = parsed.groups.filter((g) => !g.parentGroup).map((g) => g.label);
|
|
28537
|
+
if (topLabels.length < 2) return layout;
|
|
28538
|
+
const nodeTop = /* @__PURE__ */ new Map();
|
|
28539
|
+
for (const g of parsed.groups)
|
|
28540
|
+
for (const child of g.children)
|
|
28541
|
+
if (!parsed.groups.some((gg) => gg.label === child))
|
|
28542
|
+
nodeTop.set(child, topOf(g.label));
|
|
28543
|
+
const axis = parsed.direction === "TB" ? "x" : "y";
|
|
28544
|
+
const boxByLabel = new Map(layout.groups.map((g) => [g.label, g]));
|
|
28545
|
+
const bands = topLabels.map((label) => {
|
|
28546
|
+
const g = boxByLabel.get(label);
|
|
28547
|
+
const half2 = (axis === "y" ? g.height : g.width) / 2;
|
|
28548
|
+
let lo = (axis === "y" ? g.y : g.x) - half2;
|
|
28549
|
+
const hi = (axis === "y" ? g.y : g.x) + half2;
|
|
28550
|
+
const isParent = parsed.groups.some((c) => c.parentGroup === label);
|
|
28551
|
+
if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE2;
|
|
28552
|
+
return { label, lo, hi, c: (lo + hi) / 2 };
|
|
28553
|
+
});
|
|
28554
|
+
bands.sort((a, b) => a.c - b.c);
|
|
28555
|
+
const GAP = 16;
|
|
28556
|
+
const half = bands.map((b) => (b.hi - b.lo) / 2);
|
|
28557
|
+
const off = [0];
|
|
28558
|
+
for (let i = 1; i < bands.length; i++)
|
|
28559
|
+
off[i] = off[i - 1] + half[i - 1] + GAP + half[i];
|
|
28560
|
+
const desired = bands.map((b, i) => b.c - off[i]);
|
|
28561
|
+
const blocks = [];
|
|
28562
|
+
for (let i = 0; i < bands.length; i++) {
|
|
28563
|
+
let blk = { pos: desired[i], count: 1, sum: desired[i], first: i };
|
|
28564
|
+
while (blocks.length && blocks[blocks.length - 1].pos >= blk.pos) {
|
|
28565
|
+
const prev = blocks.pop();
|
|
28566
|
+
const count = prev.count + blk.count;
|
|
28567
|
+
const sum = prev.sum + blk.sum;
|
|
28568
|
+
blk = { pos: sum / count, count, sum, first: prev.first };
|
|
28569
|
+
}
|
|
28570
|
+
blocks.push(blk);
|
|
28571
|
+
}
|
|
28572
|
+
const newC = new Array(bands.length);
|
|
28573
|
+
for (const blk of blocks)
|
|
28574
|
+
for (let k = 0; k < blk.count; k++)
|
|
28575
|
+
newC[blk.first + k] = blk.pos + off[blk.first + k];
|
|
28576
|
+
const delta = /* @__PURE__ */ new Map();
|
|
28577
|
+
let moved = false;
|
|
28578
|
+
bands.forEach((b, i) => {
|
|
28579
|
+
const d = newC[i] - b.c;
|
|
28580
|
+
delta.set(b.label, d);
|
|
28581
|
+
if (Math.abs(d) > 0.5) moved = true;
|
|
28582
|
+
});
|
|
28583
|
+
if (!moved) return layout;
|
|
28584
|
+
const nodeDelta = (label) => {
|
|
28585
|
+
const top = nodeTop.get(label);
|
|
28586
|
+
return top ? delta.get(top) ?? 0 : 0;
|
|
28587
|
+
};
|
|
28588
|
+
const shift = (p, d) => axis === "y" ? { x: p.x, y: p.y + d } : { x: p.x + d, y: p.y };
|
|
28589
|
+
const nodes = layout.nodes.map((n) => {
|
|
28590
|
+
const d = nodeDelta(n.label);
|
|
28591
|
+
return d ? { ...n, ...axis === "y" ? { y: n.y + d } : { x: n.x + d } } : n;
|
|
28592
|
+
});
|
|
28593
|
+
const groups = layout.groups.map((g) => {
|
|
28594
|
+
const d = delta.get(topOf(g.label)) ?? 0;
|
|
28595
|
+
return d ? { ...g, ...axis === "y" ? { y: g.y + d } : { x: g.x + d } } : g;
|
|
28596
|
+
});
|
|
28597
|
+
const edges = layout.edges.map((e) => {
|
|
28598
|
+
const ds = nodeDelta(e.source);
|
|
28599
|
+
const dt = nodeDelta(e.target);
|
|
28600
|
+
if (!ds && !dt) return e;
|
|
28601
|
+
const N = e.points.length;
|
|
28602
|
+
const points = e.points.map((p, i) => {
|
|
28603
|
+
const f = N > 1 ? i / (N - 1) : 0;
|
|
28604
|
+
return shift(p, ds * (1 - f) + dt * f);
|
|
28605
|
+
});
|
|
28606
|
+
return { ...e, points };
|
|
28607
|
+
});
|
|
28608
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
28609
|
+
const acc = (x, y) => {
|
|
28610
|
+
if (x < minX) minX = x;
|
|
28611
|
+
if (x > maxX) maxX = x;
|
|
28612
|
+
if (y < minY) minY = y;
|
|
28613
|
+
if (y > maxY) maxY = y;
|
|
28614
|
+
};
|
|
28615
|
+
for (const n of nodes) {
|
|
28616
|
+
acc(n.x - n.width / 2, n.y - n.height / 2);
|
|
28617
|
+
acc(n.x + n.width / 2, n.y + n.height / 2);
|
|
28618
|
+
}
|
|
28619
|
+
for (const g of groups) {
|
|
28620
|
+
acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE2);
|
|
28621
|
+
acc(g.x + g.width / 2, g.y + g.height / 2);
|
|
28622
|
+
}
|
|
28623
|
+
for (const e of edges) for (const p of e.points) acc(p.x, p.y);
|
|
28624
|
+
const M = 40;
|
|
28625
|
+
const sx = M - minX, sy = M - minY;
|
|
28626
|
+
const reshift = sx !== 0 || sy !== 0;
|
|
28627
|
+
return {
|
|
28628
|
+
nodes: reshift ? nodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })) : nodes,
|
|
28629
|
+
groups: reshift ? groups.map((g) => ({ ...g, x: g.x + sx, y: g.y + sy })) : groups,
|
|
28630
|
+
edges: reshift ? edges.map((e) => ({
|
|
28631
|
+
...e,
|
|
28632
|
+
points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
|
|
28633
|
+
})) : edges,
|
|
28634
|
+
width: maxX - minX + 2 * M,
|
|
28635
|
+
height: maxY - minY + 2 * M
|
|
28636
|
+
};
|
|
28637
|
+
}
|
|
28638
|
+
function segCross(p1, p2, p3, p4) {
|
|
28639
|
+
const d1x = p2.x - p1.x, d1y = p2.y - p1.y, d2x = p4.x - p3.x, d2y = p4.y - p3.y;
|
|
28640
|
+
const den = d1x * d2y - d1y * d2x;
|
|
28641
|
+
if (Math.abs(den) < 1e-9) return false;
|
|
28642
|
+
const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / den;
|
|
28643
|
+
const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / den;
|
|
28644
|
+
return t > 1e-3 && t < 0.999 && s > 1e-3 && s < 0.999;
|
|
28645
|
+
}
|
|
28646
|
+
function countCrossingsFast(layout) {
|
|
28647
|
+
const E = layout.edges.filter((e) => e.points.length >= 2);
|
|
28648
|
+
const bb = E.map((e) => {
|
|
28649
|
+
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
28650
|
+
for (const p of e.points) {
|
|
28651
|
+
if (p.x < x0) x0 = p.x;
|
|
28652
|
+
if (p.x > x1) x1 = p.x;
|
|
28653
|
+
if (p.y < y0) y0 = p.y;
|
|
28654
|
+
if (p.y > y1) y1 = p.y;
|
|
28655
|
+
}
|
|
28656
|
+
return { x0, y0, x1, y1 };
|
|
28657
|
+
});
|
|
28658
|
+
let count = 0;
|
|
28659
|
+
for (let i = 0; i < E.length; i++)
|
|
28660
|
+
for (let j = i + 1; j < E.length; j++) {
|
|
28661
|
+
const A = E[i], B = E[j];
|
|
28662
|
+
if (A.source === B.source || A.source === B.target || A.target === B.source || A.target === B.target)
|
|
28663
|
+
continue;
|
|
28664
|
+
const a = bb[i], b = bb[j];
|
|
28665
|
+
if (a.x1 < b.x0 || b.x1 < a.x0 || a.y1 < b.y0 || b.y1 < a.y0) continue;
|
|
28666
|
+
const pa = A.points, pb = B.points;
|
|
28667
|
+
let hit = false;
|
|
28668
|
+
for (let ai = 0; ai < pa.length - 1 && !hit; ai++)
|
|
28669
|
+
for (let bi = 0; bi < pb.length - 1; bi++) {
|
|
28670
|
+
if (segCross(pa[ai], pa[ai + 1], pb[bi], pb[bi + 1])) {
|
|
28671
|
+
hit = true;
|
|
28672
|
+
break;
|
|
28673
|
+
}
|
|
28674
|
+
}
|
|
28675
|
+
if (hit) count++;
|
|
28676
|
+
}
|
|
28677
|
+
return count;
|
|
28678
|
+
}
|
|
28679
|
+
function meanDrift(layout, prev) {
|
|
28680
|
+
if (!prev?.size) return 0;
|
|
28681
|
+
let sum = 0, n = 0;
|
|
28682
|
+
for (const node of layout.nodes) {
|
|
28683
|
+
const p = prev.get(node.label);
|
|
28684
|
+
if (p) {
|
|
28685
|
+
sum += Math.hypot(node.x - p.x, node.y - p.y);
|
|
28686
|
+
n++;
|
|
28687
|
+
}
|
|
28688
|
+
}
|
|
28689
|
+
return n ? sum / n : 0;
|
|
28690
|
+
}
|
|
28691
|
+
function edgeLength(layout) {
|
|
28692
|
+
let total = 0;
|
|
28693
|
+
for (const e of layout.edges)
|
|
28694
|
+
for (let i = 1; i < e.points.length; i++)
|
|
28695
|
+
total += Math.hypot(
|
|
28696
|
+
e.points[i].x - e.points[i - 1].x,
|
|
28697
|
+
e.points[i].y - e.points[i - 1].y
|
|
28698
|
+
);
|
|
28699
|
+
return total;
|
|
28700
|
+
}
|
|
28701
|
+
function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
|
|
28702
|
+
const hideDescriptions = opts?.hideDescriptions ?? false;
|
|
28703
|
+
const collapsedGroupLabels = /* @__PURE__ */ new Set();
|
|
28704
|
+
if (collapseInfo) {
|
|
28705
|
+
const missing = /* @__PURE__ */ new Set();
|
|
28706
|
+
for (const og of collapseInfo.originalGroups)
|
|
28707
|
+
if (!parsed.groups.some((g) => g.label === og.label))
|
|
28708
|
+
missing.add(og.label);
|
|
28709
|
+
for (const label of missing) {
|
|
28710
|
+
const og = collapseInfo.originalGroups.find((g) => g.label === label);
|
|
28711
|
+
const parent = og?.parentGroup;
|
|
28712
|
+
if (!parent || !missing.has(parent)) collapsedGroupLabels.add(label);
|
|
28713
|
+
}
|
|
28714
|
+
}
|
|
28715
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
28716
|
+
let maxDescH = 0;
|
|
28717
|
+
for (const node of parsed.nodes) {
|
|
28718
|
+
const s = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
|
|
28719
|
+
sizes.set(node.label, s);
|
|
28720
|
+
if (!hideDescriptions && node.description && node.description.length > 0)
|
|
28721
|
+
maxDescH = Math.max(maxDescH, s.height);
|
|
28722
|
+
}
|
|
28723
|
+
if (maxDescH > 0) {
|
|
28724
|
+
for (const node of parsed.nodes)
|
|
28725
|
+
if (node.description && node.description.length > 0) {
|
|
28726
|
+
const s = sizes.get(node.label);
|
|
28727
|
+
sizes.set(node.label, { width: s.width, height: maxDescH });
|
|
28728
|
+
}
|
|
28729
|
+
}
|
|
28730
|
+
const gid = (label) => `__group_${label}`;
|
|
28731
|
+
const rankdir = parsed.direction === "TB" ? "TB" : "LR";
|
|
28732
|
+
function place(cfg) {
|
|
28733
|
+
const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
|
|
28734
|
+
const ord = (a) => r ? shuffle(a, r) : a.slice();
|
|
28735
|
+
const g = new dagre4.graphlib.Graph({ compound: true, multigraph: true });
|
|
28736
|
+
g.setGraph({
|
|
28737
|
+
rankdir,
|
|
28738
|
+
ranker: cfg.ranker,
|
|
28739
|
+
nodesep: cfg.nodesep,
|
|
28740
|
+
ranksep: cfg.ranksep,
|
|
28741
|
+
edgesep: 20,
|
|
28742
|
+
marginx: 40,
|
|
28743
|
+
marginy: 40
|
|
28744
|
+
});
|
|
28745
|
+
g.setDefaultEdgeLabel(() => ({}));
|
|
28746
|
+
for (const grp of ord(parsed.groups))
|
|
28747
|
+
g.setNode(gid(grp.label), { label: grp.label });
|
|
28748
|
+
for (const node of ord(parsed.nodes)) {
|
|
28749
|
+
const s = sizes.get(node.label);
|
|
28750
|
+
g.setNode(node.label, { width: s.width, height: s.height });
|
|
28751
|
+
}
|
|
28752
|
+
for (const label of collapsedGroupLabels)
|
|
28753
|
+
g.setNode(gid(label), { width: NODE_WIDTH, height: NODE_HEIGHT });
|
|
28754
|
+
for (const grp of parsed.groups) {
|
|
28755
|
+
if (grp.parentGroup && g.hasNode(gid(grp.parentGroup)))
|
|
28756
|
+
g.setParent(gid(grp.label), gid(grp.parentGroup));
|
|
28757
|
+
for (const c of ord(grp.children)) {
|
|
28758
|
+
if (g.hasNode(c)) g.setParent(c, gid(grp.label));
|
|
28759
|
+
}
|
|
28760
|
+
}
|
|
28761
|
+
if (collapseInfo)
|
|
28762
|
+
for (const label of collapsedGroupLabels) {
|
|
28763
|
+
const og = collapseInfo.originalGroups.find((x) => x.label === label);
|
|
28764
|
+
if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && g.hasNode(gid(og.parentGroup)))
|
|
28765
|
+
g.setParent(gid(label), gid(og.parentGroup));
|
|
28766
|
+
}
|
|
28767
|
+
for (const e of ord(parsed.edges))
|
|
28768
|
+
if (g.hasNode(e.source) && g.hasNode(e.target))
|
|
28769
|
+
g.setEdge(e.source, e.target, {});
|
|
28770
|
+
dagre4.layout(g);
|
|
28771
|
+
const nodes = parsed.nodes.map((n2) => {
|
|
28772
|
+
const p = g.node(n2.label);
|
|
28773
|
+
return {
|
|
28774
|
+
label: n2.label,
|
|
28775
|
+
x: p.x,
|
|
28776
|
+
y: p.y,
|
|
28777
|
+
width: p.width,
|
|
28778
|
+
height: p.height
|
|
28779
|
+
};
|
|
28780
|
+
});
|
|
28781
|
+
const groups = parsed.groups.map(
|
|
28782
|
+
(grp) => {
|
|
28783
|
+
const p = g.node(gid(grp.label));
|
|
28784
|
+
return {
|
|
28785
|
+
label: grp.label,
|
|
28786
|
+
lineNumber: grp.lineNumber,
|
|
28787
|
+
x: p.x,
|
|
28788
|
+
y: p.y,
|
|
28789
|
+
width: p.width,
|
|
28790
|
+
height: p.height,
|
|
28791
|
+
collapsed: false,
|
|
28792
|
+
childCount: grp.children.length
|
|
28793
|
+
};
|
|
28794
|
+
}
|
|
28795
|
+
);
|
|
28796
|
+
for (const label of collapsedGroupLabels) {
|
|
28797
|
+
const p = g.node(gid(label));
|
|
28798
|
+
groups.push({
|
|
28799
|
+
label,
|
|
28800
|
+
lineNumber: 0,
|
|
28801
|
+
x: p.x,
|
|
28802
|
+
y: p.y,
|
|
28803
|
+
width: p.width,
|
|
28804
|
+
height: p.height,
|
|
28805
|
+
collapsed: true,
|
|
28806
|
+
childCount: collapseInfo?.collapsedChildCounts.get(label) ?? 0
|
|
28807
|
+
});
|
|
28808
|
+
}
|
|
28809
|
+
const edges = parsed.edges.filter((e) => g.hasEdge(e.source, e.target)).map((e) => {
|
|
28810
|
+
const ed = g.edge(e.source, e.target);
|
|
28811
|
+
return {
|
|
28812
|
+
source: e.source,
|
|
28813
|
+
target: e.target,
|
|
28814
|
+
...e.label !== void 0 && { label: e.label },
|
|
28815
|
+
bidirectional: e.bidirectional,
|
|
28816
|
+
lineNumber: e.lineNumber,
|
|
28817
|
+
points: ed?.points ?? [],
|
|
28818
|
+
yOffset: 0,
|
|
28819
|
+
parallelCount: 1,
|
|
28820
|
+
metadata: e.metadata
|
|
28821
|
+
};
|
|
28822
|
+
});
|
|
28823
|
+
const gg = g.graph();
|
|
28824
|
+
return {
|
|
28825
|
+
nodes,
|
|
28826
|
+
edges,
|
|
28827
|
+
groups,
|
|
28828
|
+
width: gg.width ?? 800,
|
|
28829
|
+
height: gg.height ?? 600
|
|
28830
|
+
};
|
|
28831
|
+
}
|
|
28832
|
+
const n = parsed.nodes.length;
|
|
28833
|
+
const seedCount = opts?.seeds ?? (n <= 12 ? 80 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
|
|
28834
|
+
const REFINE_K = opts?.refineK ?? 6;
|
|
28835
|
+
const lambda = opts?.lambda ?? DEFAULT_LAMBDA;
|
|
28836
|
+
const prev = opts?.previousPositions;
|
|
28837
|
+
const RANKERS = ["network-simplex", "tight-tree", "longest-path"];
|
|
28838
|
+
const SPACINGS = [
|
|
28839
|
+
{ nodesep: 50, ranksep: 60 },
|
|
28840
|
+
{ nodesep: 34, ranksep: 46 },
|
|
28841
|
+
{ nodesep: 66, ranksep: 82 }
|
|
28842
|
+
];
|
|
28843
|
+
const configs = [];
|
|
28844
|
+
for (const ranker of RANKERS)
|
|
28845
|
+
for (const sp of SPACINGS) configs.push({ ranker, ...sp });
|
|
28846
|
+
for (let s = 0; s < seedCount; s++)
|
|
28847
|
+
configs.push({
|
|
28848
|
+
ranker: "network-simplex",
|
|
28849
|
+
nodesep: 50,
|
|
28850
|
+
ranksep: 60,
|
|
28851
|
+
seed: s
|
|
28852
|
+
});
|
|
28853
|
+
const badness = (lay, floor) => {
|
|
28854
|
+
const x = countSplineCrossings(lay);
|
|
28855
|
+
if (x > floor) return Infinity;
|
|
28856
|
+
return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
|
|
28857
|
+
};
|
|
28858
|
+
const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
|
|
28859
|
+
const pool = [];
|
|
28860
|
+
for (const cfg of configs) {
|
|
28861
|
+
try {
|
|
28862
|
+
pool.push(place(cfg));
|
|
28863
|
+
} catch {
|
|
28864
|
+
}
|
|
28865
|
+
}
|
|
28866
|
+
if (!pool.length)
|
|
28867
|
+
return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
|
|
28868
|
+
let layered = [];
|
|
28869
|
+
try {
|
|
28870
|
+
layered = layeredCandidates(parsed, sizes);
|
|
28871
|
+
} catch {
|
|
28872
|
+
}
|
|
28873
|
+
pool.sort(
|
|
28874
|
+
(a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
|
|
28875
|
+
);
|
|
28876
|
+
const refineK = Math.min(REFINE_K, pool.length);
|
|
28877
|
+
let best = pool[0];
|
|
28878
|
+
let bestObj = Infinity;
|
|
28879
|
+
let bestBad = Infinity;
|
|
28880
|
+
const consider = (lay) => {
|
|
28881
|
+
const bad = badness(lay, bestBad);
|
|
28882
|
+
if (bad === Infinity) return;
|
|
28883
|
+
const sc = objective(lay, bad);
|
|
28884
|
+
if (sc < bestObj) {
|
|
28885
|
+
bestObj = sc;
|
|
28886
|
+
bestBad = bad;
|
|
28887
|
+
best = lay;
|
|
28888
|
+
}
|
|
28889
|
+
};
|
|
28890
|
+
for (const lay of pool.slice(0, refineK)) consider(lay);
|
|
28891
|
+
if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
|
|
28892
|
+
const extra = [];
|
|
28893
|
+
for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
|
|
28894
|
+
try {
|
|
28895
|
+
extra.push(
|
|
28896
|
+
place({
|
|
28897
|
+
ranker: "network-simplex",
|
|
28898
|
+
nodesep: 50,
|
|
28899
|
+
ranksep: 60,
|
|
28900
|
+
seed: s
|
|
28901
|
+
})
|
|
28902
|
+
);
|
|
28903
|
+
} catch {
|
|
28904
|
+
}
|
|
28905
|
+
}
|
|
28906
|
+
extra.sort(
|
|
28907
|
+
(a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
|
|
28908
|
+
);
|
|
28909
|
+
for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
|
|
28910
|
+
}
|
|
28911
|
+
for (const lay of layered) {
|
|
28912
|
+
const variants = [lay];
|
|
28913
|
+
const dp = deroutePierces(lay);
|
|
28914
|
+
if (dp !== lay) variants.push(dp);
|
|
28915
|
+
for (const v of variants) {
|
|
28916
|
+
const bad = badness(v, bestBad - 1);
|
|
28917
|
+
if (bad < bestBad) {
|
|
28918
|
+
bestBad = bad;
|
|
28919
|
+
best = v;
|
|
28920
|
+
}
|
|
28921
|
+
}
|
|
28922
|
+
}
|
|
28923
|
+
if (bestBad > 0) {
|
|
28924
|
+
const rerouted = deroutePierces(best);
|
|
28925
|
+
if (rerouted !== best && badness(rerouted, bestBad - 1) < bestBad)
|
|
28926
|
+
best = rerouted;
|
|
28927
|
+
}
|
|
28928
|
+
if (bestBad > 0 && countGroupOverlaps(best) > 0) {
|
|
28929
|
+
const separated = separateGroupBands(best, parsed);
|
|
28930
|
+
if (separated !== best && badness(separated, bestBad - 1) < bestBad)
|
|
28931
|
+
best = separated;
|
|
28932
|
+
}
|
|
28933
|
+
return best;
|
|
28934
|
+
}
|
|
28935
|
+
var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
|
|
28936
|
+
var init_layout_search = __esm({
|
|
28937
|
+
"src/boxes-and-lines/layout-search.ts"() {
|
|
28938
|
+
"use strict";
|
|
28939
|
+
init_layout5();
|
|
28940
|
+
init_layout_layered();
|
|
28941
|
+
DEFAULT_LAMBDA = 4;
|
|
28942
|
+
ESCALATE_THRESHOLD = 4;
|
|
28943
|
+
ESCALATE_MAX_N = 45;
|
|
28944
|
+
ESCALATE_SEEDS = 18;
|
|
28945
|
+
ESCALATE_REFINE = 10;
|
|
28946
|
+
splineGen = d3line().x((d) => d.x).y((d) => d.y).curve(curveBasis5);
|
|
28947
|
+
GROUP_LABEL_ZONE2 = 32;
|
|
28948
|
+
}
|
|
28949
|
+
});
|
|
28950
|
+
|
|
27621
28951
|
// src/boxes-and-lines/layout.ts
|
|
27622
28952
|
var layout_exports5 = {};
|
|
27623
28953
|
__export(layout_exports5, {
|
|
28954
|
+
NODE_HEIGHT: () => NODE_HEIGHT,
|
|
28955
|
+
NODE_WIDTH: () => NODE_WIDTH,
|
|
28956
|
+
computeNodeSize: () => computeNodeSize,
|
|
27624
28957
|
layoutBoxesAndLines: () => layoutBoxesAndLines
|
|
27625
28958
|
});
|
|
27626
|
-
import ELK from "elkjs/lib/elk.bundled.js";
|
|
27627
28959
|
function splitCamelCase2(word) {
|
|
27628
28960
|
const parts = [];
|
|
27629
28961
|
let start = 0;
|
|
@@ -27694,417 +29026,21 @@ function computeNodeSize(node, reserveValueRow) {
|
|
|
27694
29026
|
const totalHeight = labelHeight + SEPARATOR_GAP5 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
|
|
27695
29027
|
return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
|
|
27696
29028
|
}
|
|
27697
|
-
function getElk() {
|
|
27698
|
-
if (!elkInstance) elkInstance = new ELK();
|
|
27699
|
-
return elkInstance;
|
|
27700
|
-
}
|
|
27701
|
-
function baseOptions() {
|
|
27702
|
-
return {
|
|
27703
|
-
"elk.algorithm": "layered",
|
|
27704
|
-
// INCLUDE_CHILDREN lets ELK route edges across container boundaries.
|
|
27705
|
-
"elk.hierarchyHandling": "INCLUDE_CHILDREN",
|
|
27706
|
-
"elk.edgeRouting": "ORTHOGONAL",
|
|
27707
|
-
"elk.layered.unnecessaryBendpoints": "true",
|
|
27708
|
-
// Let edges leave from top/bottom of nodes (not just the flow-direction
|
|
27709
|
-
// sides) when it reduces crossings.
|
|
27710
|
-
"elk.layered.allowNonFlowPortsToSwitchSides": "true"
|
|
27711
|
-
};
|
|
27712
|
-
}
|
|
27713
|
-
function bkBaseline() {
|
|
27714
|
-
return {
|
|
27715
|
-
...baseOptions(),
|
|
27716
|
-
"elk.layered.nodePlacement.strategy": "BRANDES_KOEPF",
|
|
27717
|
-
"elk.layered.nodePlacement.bk.fixedAlignment": "BALANCED",
|
|
27718
|
-
"elk.layered.nodePlacement.bk.edgeStraightening": "IMPROVE_STRAIGHTNESS",
|
|
27719
|
-
"elk.layered.compaction.connectedComponents": "true",
|
|
27720
|
-
"elk.layered.spacing.nodeNodeBetweenLayers": "90",
|
|
27721
|
-
"elk.spacing.nodeNode": "55",
|
|
27722
|
-
"elk.spacing.edgeNode": "55",
|
|
27723
|
-
"elk.spacing.edgeEdge": "18"
|
|
27724
|
-
};
|
|
27725
|
-
}
|
|
27726
|
-
function getVariants() {
|
|
27727
|
-
const bk = bkBaseline();
|
|
27728
|
-
return [
|
|
27729
|
-
{
|
|
27730
|
-
name: "bk-baseline",
|
|
27731
|
-
options: {
|
|
27732
|
-
...bk,
|
|
27733
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "ONE_SIDED"
|
|
27734
|
-
}
|
|
27735
|
-
},
|
|
27736
|
-
{
|
|
27737
|
-
name: "bk-aggressive",
|
|
27738
|
-
options: {
|
|
27739
|
-
...bk,
|
|
27740
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
|
|
27741
|
-
"elk.layered.thoroughness": "50"
|
|
27742
|
-
}
|
|
27743
|
-
},
|
|
27744
|
-
{
|
|
27745
|
-
name: "bk-wide",
|
|
27746
|
-
options: {
|
|
27747
|
-
...bk,
|
|
27748
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
|
|
27749
|
-
"elk.layered.thoroughness": "50",
|
|
27750
|
-
"elk.spacing.nodeNode": "70",
|
|
27751
|
-
"elk.spacing.edgeNode": "75",
|
|
27752
|
-
"elk.spacing.edgeEdge": "22",
|
|
27753
|
-
"elk.layered.spacing.nodeNodeBetweenLayers": "120"
|
|
27754
|
-
}
|
|
27755
|
-
},
|
|
27756
|
-
{
|
|
27757
|
-
name: "longest-path",
|
|
27758
|
-
options: {
|
|
27759
|
-
...bk,
|
|
27760
|
-
"elk.layered.layering.strategy": "LONGEST_PATH",
|
|
27761
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
|
|
27762
|
-
"elk.layered.thoroughness": "50"
|
|
27763
|
-
}
|
|
27764
|
-
},
|
|
27765
|
-
{
|
|
27766
|
-
name: "bounded-width",
|
|
27767
|
-
options: {
|
|
27768
|
-
...bk,
|
|
27769
|
-
"elk.layered.layering.strategy": "COFFMAN_GRAHAM",
|
|
27770
|
-
"elk.layered.layering.coffmanGraham.layerBound": "3",
|
|
27771
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
|
|
27772
|
-
"elk.layered.thoroughness": "50"
|
|
27773
|
-
}
|
|
27774
|
-
}
|
|
27775
|
-
];
|
|
27776
|
-
}
|
|
27777
|
-
function countCrossings(edges) {
|
|
27778
|
-
let count = 0;
|
|
27779
|
-
for (let i = 0; i < edges.length; i++) {
|
|
27780
|
-
const edgeI = edges[i];
|
|
27781
|
-
const a = edgeI.points;
|
|
27782
|
-
if (a.length < 2) continue;
|
|
27783
|
-
for (let j = i + 1; j < edges.length; j++) {
|
|
27784
|
-
const edgeJ = edges[j];
|
|
27785
|
-
const b = edgeJ.points;
|
|
27786
|
-
if (b.length < 2) continue;
|
|
27787
|
-
if (edgeI.source === edgeJ.source) continue;
|
|
27788
|
-
if (edgeI.source === edgeJ.target) continue;
|
|
27789
|
-
if (edgeI.target === edgeJ.source) continue;
|
|
27790
|
-
if (edgeI.target === edgeJ.target) continue;
|
|
27791
|
-
for (let ai = 0; ai < a.length - 1; ai++) {
|
|
27792
|
-
for (let bi = 0; bi < b.length - 1; bi++) {
|
|
27793
|
-
if (segmentsCross(a[ai], a[ai + 1], b[bi], b[bi + 1])) count++;
|
|
27794
|
-
}
|
|
27795
|
-
}
|
|
27796
|
-
}
|
|
27797
|
-
}
|
|
27798
|
-
return count;
|
|
27799
|
-
}
|
|
27800
|
-
function segmentsCross(p1, p2, p3, p4) {
|
|
27801
|
-
const d1x = p2.x - p1.x;
|
|
27802
|
-
const d1y = p2.y - p1.y;
|
|
27803
|
-
const d2x = p4.x - p3.x;
|
|
27804
|
-
const d2y = p4.y - p3.y;
|
|
27805
|
-
const denom = d1x * d2y - d1y * d2x;
|
|
27806
|
-
if (Math.abs(denom) < 1e-9) return false;
|
|
27807
|
-
const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / denom;
|
|
27808
|
-
const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / denom;
|
|
27809
|
-
const EPS = 1e-3;
|
|
27810
|
-
return t > EPS && t < 1 - EPS && s > EPS && s < 1 - EPS;
|
|
27811
|
-
}
|
|
27812
|
-
function countTotalBends(edges) {
|
|
27813
|
-
let bends = 0;
|
|
27814
|
-
for (const e of edges) bends += Math.max(0, e.points.length - 2);
|
|
27815
|
-
return bends;
|
|
27816
|
-
}
|
|
27817
|
-
function scoreLayout(layout) {
|
|
27818
|
-
return {
|
|
27819
|
-
crossings: countCrossings(layout.edges),
|
|
27820
|
-
bends: countTotalBends(layout.edges),
|
|
27821
|
-
area: layout.width * layout.height
|
|
27822
|
-
};
|
|
27823
|
-
}
|
|
27824
|
-
function cmpScore(a, b) {
|
|
27825
|
-
const aBucket = a.crossings <= CROSSINGS_FORGIVENESS ? 0 : a.crossings;
|
|
27826
|
-
const bBucket = b.crossings <= CROSSINGS_FORGIVENESS ? 0 : b.crossings;
|
|
27827
|
-
if (aBucket !== bBucket) return aBucket - bBucket;
|
|
27828
|
-
if (a.area !== b.area) return a.area - b.area;
|
|
27829
|
-
return a.bends - b.bends;
|
|
27830
|
-
}
|
|
27831
29029
|
async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
|
|
27832
|
-
const
|
|
27833
|
-
const
|
|
27834
|
-
|
|
27835
|
-
|
|
27836
|
-
|
|
27837
|
-
|
|
27838
|
-
|
|
27839
|
-
missingGroups.add(og.label);
|
|
27840
|
-
}
|
|
27841
|
-
}
|
|
27842
|
-
for (const label of missingGroups) {
|
|
27843
|
-
const og = collapseInfo.originalGroups.find((g) => g.label === label);
|
|
27844
|
-
const parentLabel = og?.parentGroup;
|
|
27845
|
-
if (!parentLabel || !missingGroups.has(parentLabel)) {
|
|
27846
|
-
collapsedGroupLabels.add(label);
|
|
27847
|
-
}
|
|
27848
|
-
}
|
|
27849
|
-
}
|
|
27850
|
-
const nodeSizes = /* @__PURE__ */ new Map();
|
|
27851
|
-
let maxDescHeight = 0;
|
|
27852
|
-
for (const node of parsed.nodes) {
|
|
27853
|
-
const size = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
|
|
27854
|
-
nodeSizes.set(node.label, size);
|
|
27855
|
-
if (!hideDescriptions && node.description && node.description.length > 0) {
|
|
27856
|
-
maxDescHeight = Math.max(maxDescHeight, size.height);
|
|
27857
|
-
}
|
|
27858
|
-
}
|
|
27859
|
-
if (maxDescHeight > 0) {
|
|
27860
|
-
for (const node of parsed.nodes) {
|
|
27861
|
-
if (node.description && node.description.length > 0) {
|
|
27862
|
-
const size = nodeSizes.get(node.label);
|
|
27863
|
-
nodeSizes.set(node.label, { width: size.width, height: maxDescHeight });
|
|
27864
|
-
}
|
|
27865
|
-
}
|
|
27866
|
-
}
|
|
27867
|
-
const expandedGroupSet = new Set(parsed.groups.map((g) => g.label));
|
|
27868
|
-
const gid = (label) => `__group_${label}`;
|
|
27869
|
-
function buildGraph() {
|
|
27870
|
-
const nodeById = /* @__PURE__ */ new Map();
|
|
27871
|
-
const parentOf = /* @__PURE__ */ new Map();
|
|
27872
|
-
for (const node of parsed.nodes) {
|
|
27873
|
-
const size = nodeSizes.get(node.label);
|
|
27874
|
-
nodeById.set(node.label, {
|
|
27875
|
-
id: node.label,
|
|
27876
|
-
width: size.width,
|
|
27877
|
-
height: size.height,
|
|
27878
|
-
labels: [{ text: node.label }]
|
|
27879
|
-
});
|
|
27880
|
-
}
|
|
27881
|
-
for (const group of parsed.groups) {
|
|
27882
|
-
nodeById.set(gid(group.label), {
|
|
27883
|
-
id: gid(group.label),
|
|
27884
|
-
labels: [{ text: group.label }],
|
|
27885
|
-
layoutOptions: {
|
|
27886
|
-
"elk.padding": `[top=${CONTAINER_PAD_TOP2},left=${CONTAINER_PAD_X3},bottom=${CONTAINER_PAD_BOTTOM3},right=${CONTAINER_PAD_X3}]`,
|
|
27887
|
-
// Suggest square-ish containers — has limited effect with
|
|
27888
|
-
// INCLUDE_CHILDREN but doesn't hurt.
|
|
27889
|
-
"elk.aspectRatio": "1.4"
|
|
27890
|
-
},
|
|
27891
|
-
children: [],
|
|
27892
|
-
edges: []
|
|
27893
|
-
});
|
|
27894
|
-
}
|
|
27895
|
-
for (const label of collapsedGroupLabels) {
|
|
27896
|
-
nodeById.set(gid(label), {
|
|
27897
|
-
id: gid(label),
|
|
27898
|
-
width: NODE_WIDTH,
|
|
27899
|
-
height: NODE_HEIGHT,
|
|
27900
|
-
labels: [{ text: label }]
|
|
27901
|
-
});
|
|
27902
|
-
}
|
|
27903
|
-
for (const group of parsed.groups) {
|
|
27904
|
-
if (group.parentGroup && nodeById.has(gid(group.parentGroup))) {
|
|
27905
|
-
parentOf.set(gid(group.label), gid(group.parentGroup));
|
|
27906
|
-
}
|
|
27907
|
-
}
|
|
27908
|
-
if (collapseInfo) {
|
|
27909
|
-
for (const label of collapsedGroupLabels) {
|
|
27910
|
-
const og = collapseInfo.originalGroups.find((g) => g.label === label);
|
|
27911
|
-
if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && nodeById.has(gid(og.parentGroup))) {
|
|
27912
|
-
parentOf.set(gid(label), gid(og.parentGroup));
|
|
27913
|
-
}
|
|
27914
|
-
}
|
|
27915
|
-
}
|
|
27916
|
-
for (const group of parsed.groups) {
|
|
27917
|
-
for (const child of group.children) {
|
|
27918
|
-
if (expandedGroupSet.has(child)) continue;
|
|
27919
|
-
if (nodeById.has(child)) {
|
|
27920
|
-
parentOf.set(child, gid(group.label));
|
|
27921
|
-
}
|
|
27922
|
-
}
|
|
27923
|
-
}
|
|
27924
|
-
const roots = [];
|
|
27925
|
-
for (const [id, node] of nodeById) {
|
|
27926
|
-
const parentId = parentOf.get(id);
|
|
27927
|
-
if (parentId) {
|
|
27928
|
-
const parent = nodeById.get(parentId);
|
|
27929
|
-
parent.children = parent.children ?? [];
|
|
27930
|
-
parent.children.push(node);
|
|
27931
|
-
} else {
|
|
27932
|
-
roots.push(node);
|
|
27933
|
-
}
|
|
27934
|
-
}
|
|
27935
|
-
const rootEdges = [];
|
|
27936
|
-
for (let i = 0; i < parsed.edges.length; i++) {
|
|
27937
|
-
const edge = parsed.edges[i];
|
|
27938
|
-
if (!nodeById.has(edge.source) || !nodeById.has(edge.target)) continue;
|
|
27939
|
-
rootEdges.push({
|
|
27940
|
-
id: `e${i}`,
|
|
27941
|
-
sources: [edge.source],
|
|
27942
|
-
targets: [edge.target]
|
|
27943
|
-
});
|
|
27944
|
-
}
|
|
27945
|
-
return { roots, rootEdges };
|
|
27946
|
-
}
|
|
27947
|
-
async function runVariant(variant) {
|
|
27948
|
-
const { roots, rootEdges } = buildGraph();
|
|
27949
|
-
const elkRoot = {
|
|
27950
|
-
id: "root",
|
|
27951
|
-
layoutOptions: {
|
|
27952
|
-
...variant.options,
|
|
27953
|
-
"elk.direction": direction,
|
|
27954
|
-
"elk.padding": `[top=${MARGIN3},left=${MARGIN3},bottom=${MARGIN3},right=${MARGIN3}]`
|
|
27955
|
-
},
|
|
27956
|
-
children: roots,
|
|
27957
|
-
edges: rootEdges
|
|
27958
|
-
};
|
|
27959
|
-
const result = await getElk().layout(elkRoot);
|
|
27960
|
-
return extractLayout(result);
|
|
27961
|
-
}
|
|
27962
|
-
function extractLayout(result) {
|
|
27963
|
-
const layoutNodes = [];
|
|
27964
|
-
const layoutGroups = [];
|
|
27965
|
-
const allEdges = [];
|
|
27966
|
-
const containerAbs = /* @__PURE__ */ new Map();
|
|
27967
|
-
function walk(n, offsetX, offsetY, isRoot) {
|
|
27968
|
-
const nx = (n.x ?? 0) + offsetX;
|
|
27969
|
-
const ny = (n.y ?? 0) + offsetY;
|
|
27970
|
-
const nw = n.width ?? 0;
|
|
27971
|
-
const nh = n.height ?? 0;
|
|
27972
|
-
if (isRoot) {
|
|
27973
|
-
containerAbs.set("root", { x: nx, y: ny });
|
|
27974
|
-
} else {
|
|
27975
|
-
const isGroup = n.id.startsWith("__group_");
|
|
27976
|
-
if (isGroup) {
|
|
27977
|
-
const label = n.id.slice("__group_".length);
|
|
27978
|
-
const collapsed = collapsedGroupLabels.has(label);
|
|
27979
|
-
const og = collapseInfo?.originalGroups.find(
|
|
27980
|
-
(g) => g.label === label
|
|
27981
|
-
);
|
|
27982
|
-
const pg = parsed.groups.find((g) => g.label === label);
|
|
27983
|
-
const childCount = collapsed ? collapseInfo?.collapsedChildCounts.get(label) ?? 0 : void 0;
|
|
27984
|
-
layoutGroups.push({
|
|
27985
|
-
label,
|
|
27986
|
-
lineNumber: pg?.lineNumber ?? og?.lineNumber ?? 0,
|
|
27987
|
-
x: nx + nw / 2,
|
|
27988
|
-
y: ny + nh / 2,
|
|
27989
|
-
width: nw,
|
|
27990
|
-
height: nh,
|
|
27991
|
-
collapsed,
|
|
27992
|
-
...childCount !== void 0 && { childCount }
|
|
27993
|
-
});
|
|
27994
|
-
if (!collapsed) containerAbs.set(n.id, { x: nx, y: ny });
|
|
27995
|
-
} else {
|
|
27996
|
-
layoutNodes.push({
|
|
27997
|
-
label: n.id,
|
|
27998
|
-
x: nx + nw / 2,
|
|
27999
|
-
y: ny + nh / 2,
|
|
28000
|
-
width: nw,
|
|
28001
|
-
height: nh
|
|
28002
|
-
});
|
|
28003
|
-
}
|
|
28004
|
-
}
|
|
28005
|
-
if (n.edges) for (const e of n.edges) allEdges.push(e);
|
|
28006
|
-
if (n.children) for (const c of n.children) walk(c, nx, ny, false);
|
|
28007
|
-
}
|
|
28008
|
-
walk(result, 0, 0, true);
|
|
28009
|
-
const edgeYOffsets = new Array(parsed.edges.length).fill(0);
|
|
28010
|
-
const edgeParallelCounts = new Array(parsed.edges.length).fill(1);
|
|
28011
|
-
const parallelGroups = /* @__PURE__ */ new Map();
|
|
28012
|
-
for (let i = 0; i < parsed.edges.length; i++) {
|
|
28013
|
-
const edge = parsed.edges[i];
|
|
28014
|
-
const [a, b] = edge.source < edge.target ? [edge.source, edge.target] : [edge.target, edge.source];
|
|
28015
|
-
const key = `${a}\0${b}`;
|
|
28016
|
-
if (!parallelGroups.has(key)) parallelGroups.set(key, []);
|
|
28017
|
-
parallelGroups.get(key).push(i);
|
|
28018
|
-
}
|
|
28019
|
-
for (const group of parallelGroups.values()) {
|
|
28020
|
-
const capped = group.slice(0, MAX_PARALLEL_EDGES);
|
|
28021
|
-
for (const idx of group.slice(MAX_PARALLEL_EDGES)) {
|
|
28022
|
-
edgeParallelCounts[idx] = 0;
|
|
28023
|
-
}
|
|
28024
|
-
if (capped.length < 2) continue;
|
|
28025
|
-
for (let j = 0; j < capped.length; j++) {
|
|
28026
|
-
const cappedJ = capped[j];
|
|
28027
|
-
edgeYOffsets[cappedJ] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
|
|
28028
|
-
edgeParallelCounts[cappedJ] = capped.length;
|
|
28029
|
-
}
|
|
28030
|
-
}
|
|
28031
|
-
const edgeById = /* @__PURE__ */ new Map();
|
|
28032
|
-
for (const e of allEdges) edgeById.set(e.id, e);
|
|
28033
|
-
const layoutEdges = [];
|
|
28034
|
-
for (let i = 0; i < parsed.edges.length; i++) {
|
|
28035
|
-
const edge = parsed.edges[i];
|
|
28036
|
-
if (edgeParallelCounts[i] === 0) continue;
|
|
28037
|
-
const elkEdge = edgeById.get(`e${i}`);
|
|
28038
|
-
if (!elkEdge?.sections || elkEdge.sections.length === 0) continue;
|
|
28039
|
-
const container = elkEdge.container ?? "root";
|
|
28040
|
-
const off = containerAbs.get(container) ?? { x: 0, y: 0 };
|
|
28041
|
-
const s = elkEdge.sections[0];
|
|
28042
|
-
const points = [
|
|
28043
|
-
{ x: s.startPoint.x + off.x, y: s.startPoint.y + off.y },
|
|
28044
|
-
...(s.bendPoints ?? []).map((p) => ({
|
|
28045
|
-
x: p.x + off.x,
|
|
28046
|
-
y: p.y + off.y
|
|
28047
|
-
})),
|
|
28048
|
-
{ x: s.endPoint.x + off.x, y: s.endPoint.y + off.y }
|
|
28049
|
-
];
|
|
28050
|
-
let labelX;
|
|
28051
|
-
let labelY;
|
|
28052
|
-
if (edge.label && points.length >= 2) {
|
|
28053
|
-
const mid = Math.floor(points.length / 2);
|
|
28054
|
-
const midPoint = points[mid];
|
|
28055
|
-
labelX = midPoint.x;
|
|
28056
|
-
labelY = midPoint.y - 10;
|
|
28057
|
-
}
|
|
28058
|
-
layoutEdges.push({
|
|
28059
|
-
source: edge.source,
|
|
28060
|
-
target: edge.target,
|
|
28061
|
-
...edge.label !== void 0 && { label: edge.label },
|
|
28062
|
-
bidirectional: edge.bidirectional,
|
|
28063
|
-
lineNumber: edge.lineNumber,
|
|
28064
|
-
points,
|
|
28065
|
-
...labelX !== void 0 && { labelX },
|
|
28066
|
-
...labelY !== void 0 && { labelY },
|
|
28067
|
-
// In-bounds — i < parsed.edges.length, arrays sized to that length.
|
|
28068
|
-
yOffset: edgeYOffsets[i],
|
|
28069
|
-
parallelCount: edgeParallelCounts[i],
|
|
28070
|
-
metadata: edge.metadata,
|
|
28071
|
-
deferred: true
|
|
28072
|
-
});
|
|
28073
|
-
}
|
|
28074
|
-
let maxX = 0;
|
|
28075
|
-
let maxY = 0;
|
|
28076
|
-
for (const node of layoutNodes) {
|
|
28077
|
-
maxX = Math.max(maxX, node.x + node.width / 2);
|
|
28078
|
-
maxY = Math.max(maxY, node.y + node.height / 2);
|
|
28079
|
-
}
|
|
28080
|
-
for (const group of layoutGroups) {
|
|
28081
|
-
maxX = Math.max(maxX, group.x + group.width / 2);
|
|
28082
|
-
maxY = Math.max(maxY, group.y + group.height / 2);
|
|
29030
|
+
const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
|
|
29031
|
+
const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
|
|
29032
|
+
...layoutOptions?.hideDescriptions !== void 0 && {
|
|
29033
|
+
hideDescriptions: layoutOptions.hideDescriptions
|
|
29034
|
+
},
|
|
29035
|
+
...layoutOptions?.previousPositions !== void 0 && {
|
|
29036
|
+
previousPositions: layoutOptions.previousPositions
|
|
28083
29037
|
}
|
|
28084
|
-
|
|
28085
|
-
|
|
28086
|
-
|
|
28087
|
-
|
|
28088
|
-
|
|
28089
|
-
|
|
28090
|
-
};
|
|
28091
|
-
}
|
|
28092
|
-
const N = parsed.nodes.length + parsed.groups.length;
|
|
28093
|
-
const E = parsed.edges.length;
|
|
28094
|
-
const trivial = N < 8 && E < 10;
|
|
28095
|
-
const variants = trivial ? [getVariants()[1]] : getVariants();
|
|
28096
|
-
const results = await Promise.all(variants.map((v) => runVariant(v)));
|
|
28097
|
-
let best = results[0];
|
|
28098
|
-
let bestScore = scoreLayout(best);
|
|
28099
|
-
for (let i = 1; i < results.length; i++) {
|
|
28100
|
-
const resultI = results[i];
|
|
28101
|
-
const s = scoreLayout(resultI);
|
|
28102
|
-
if (cmpScore(s, bestScore) < 0) {
|
|
28103
|
-
best = resultI;
|
|
28104
|
-
bestScore = s;
|
|
28105
|
-
}
|
|
28106
|
-
}
|
|
28107
|
-
return attachNotes(best, parsed, layoutOptions?.collapsedNotes);
|
|
29038
|
+
});
|
|
29039
|
+
return attachNotes(
|
|
29040
|
+
applyParallelEdgeOffsets(searched),
|
|
29041
|
+
parsed,
|
|
29042
|
+
layoutOptions?.collapsedNotes
|
|
29043
|
+
);
|
|
28108
29044
|
}
|
|
28109
29045
|
function attachNotes(layout, parsed, collapsedNotes) {
|
|
28110
29046
|
const notesSuppressed = parsed.options?.["no-notes"] === "on";
|
|
@@ -28182,20 +29118,47 @@ function attachNotes(layout, parsed, collapsedNotes) {
|
|
|
28182
29118
|
nodes: finalNodes,
|
|
28183
29119
|
edges: finalEdges,
|
|
28184
29120
|
groups: finalGroups,
|
|
28185
|
-
width: bbMaxX + shiftX +
|
|
28186
|
-
height: bbMaxY + shiftY +
|
|
29121
|
+
width: bbMaxX + shiftX + MARGIN4,
|
|
29122
|
+
height: bbMaxY + shiftY + MARGIN4
|
|
29123
|
+
};
|
|
29124
|
+
}
|
|
29125
|
+
function applyParallelEdgeOffsets(layout) {
|
|
29126
|
+
const groups = /* @__PURE__ */ new Map();
|
|
29127
|
+
layout.edges.forEach((e, i) => {
|
|
29128
|
+
const [a, b] = e.source < e.target ? [e.source, e.target] : [e.target, e.source];
|
|
29129
|
+
const key = `${a}\0${b}`;
|
|
29130
|
+
const arr = groups.get(key);
|
|
29131
|
+
if (arr) arr.push(i);
|
|
29132
|
+
else groups.set(key, [i]);
|
|
29133
|
+
});
|
|
29134
|
+
if ([...groups.values()].every((g) => g.length < 2)) return layout;
|
|
29135
|
+
const yOffset = new Array(layout.edges.length).fill(0);
|
|
29136
|
+
const count = new Array(layout.edges.length).fill(1);
|
|
29137
|
+
for (const idxs of groups.values()) {
|
|
29138
|
+
const capped = idxs.slice(0, MAX_PARALLEL_EDGES);
|
|
29139
|
+
for (const drop of idxs.slice(MAX_PARALLEL_EDGES)) count[drop] = 0;
|
|
29140
|
+
if (capped.length < 2) continue;
|
|
29141
|
+
capped.forEach((idx, j) => {
|
|
29142
|
+
yOffset[idx] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
|
|
29143
|
+
count[idx] = capped.length;
|
|
29144
|
+
});
|
|
29145
|
+
}
|
|
29146
|
+
return {
|
|
29147
|
+
...layout,
|
|
29148
|
+
edges: layout.edges.map((e, i) => ({
|
|
29149
|
+
...e,
|
|
29150
|
+
yOffset: yOffset[i],
|
|
29151
|
+
parallelCount: count[i]
|
|
29152
|
+
}))
|
|
28187
29153
|
};
|
|
28188
29154
|
}
|
|
28189
|
-
var
|
|
29155
|
+
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;
|
|
28190
29156
|
var init_layout5 = __esm({
|
|
28191
29157
|
"src/boxes-and-lines/layout.ts"() {
|
|
28192
29158
|
"use strict";
|
|
28193
29159
|
init_text_measure();
|
|
28194
29160
|
init_notes();
|
|
28195
|
-
|
|
28196
|
-
CONTAINER_PAD_X3 = 30;
|
|
28197
|
-
CONTAINER_PAD_TOP2 = 40;
|
|
28198
|
-
CONTAINER_PAD_BOTTOM3 = 24;
|
|
29161
|
+
MARGIN4 = 40;
|
|
28199
29162
|
MAX_PARALLEL_EDGES = 5;
|
|
28200
29163
|
PARALLEL_SPACING = 22;
|
|
28201
29164
|
PHI = 1.618;
|
|
@@ -28212,8 +29175,6 @@ var init_layout5 = __esm({
|
|
|
28212
29175
|
LABEL_PAD = 12;
|
|
28213
29176
|
VALUE_ROW_FONT = 11;
|
|
28214
29177
|
VALUE_ROW_H = SEPARATOR_GAP5 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
|
|
28215
|
-
elkInstance = null;
|
|
28216
|
-
CROSSINGS_FORGIVENESS = 1;
|
|
28217
29178
|
}
|
|
28218
29179
|
});
|
|
28219
29180
|
|
|
@@ -28368,7 +29329,7 @@ function layoutMindmap(parsed, _palette, options) {
|
|
|
28368
29329
|
leafWidth: ctx.structural(LEAF_WIDTH),
|
|
28369
29330
|
hGap: ctx.aesthetic(H_GAP2),
|
|
28370
29331
|
vGap: ctx.aesthetic(V_GAP2),
|
|
28371
|
-
margin: ctx.aesthetic(
|
|
29332
|
+
margin: ctx.aesthetic(MARGIN5),
|
|
28372
29333
|
multiRootGap: ctx.aesthetic(MULTI_ROOT_GAP)
|
|
28373
29334
|
};
|
|
28374
29335
|
populateDepthCache(roots);
|
|
@@ -28746,7 +29707,7 @@ function populateDepthCache(roots) {
|
|
|
28746
29707
|
};
|
|
28747
29708
|
walk(roots, 0);
|
|
28748
29709
|
}
|
|
28749
|
-
var ROOT_WIDTH, DEPTH1_WIDTH, LEAF_WIDTH, SINGLE_LABEL_HEIGHT, LABEL_LINE_HEIGHT2, DESC_LINE_HEIGHT3, NODE_V_PAD, H_GAP2, V_GAP2,
|
|
29710
|
+
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;
|
|
28750
29711
|
var init_layout6 = __esm({
|
|
28751
29712
|
"src/mindmap/layout.ts"() {
|
|
28752
29713
|
"use strict";
|
|
@@ -28762,7 +29723,7 @@ var init_layout6 = __esm({
|
|
|
28762
29723
|
NODE_V_PAD = 10;
|
|
28763
29724
|
H_GAP2 = 40;
|
|
28764
29725
|
V_GAP2 = 12;
|
|
28765
|
-
|
|
29726
|
+
MARGIN5 = 40;
|
|
28766
29727
|
MULTI_ROOT_GAP = 60;
|
|
28767
29728
|
nodeDepthCache = /* @__PURE__ */ new Map();
|
|
28768
29729
|
}
|
|
@@ -30037,7 +30998,7 @@ __export(layout_exports8, {
|
|
|
30037
30998
|
layoutC4Deployment: () => layoutC4Deployment,
|
|
30038
30999
|
rollUpContextRelationships: () => rollUpContextRelationships
|
|
30039
31000
|
});
|
|
30040
|
-
import
|
|
31001
|
+
import dagre5 from "@dagrejs/dagre";
|
|
30041
31002
|
function computeEdgePenalty(edgeList, nodePositions, degrees, nodeGeometry) {
|
|
30042
31003
|
let penalty = 0;
|
|
30043
31004
|
for (const edge of edgeList) {
|
|
@@ -30462,7 +31423,7 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
30462
31423
|
}
|
|
30463
31424
|
const contextRels = rollUpContextRelationships(parsed);
|
|
30464
31425
|
const spacing = computeAdaptiveSpacing(contextRels);
|
|
30465
|
-
const g = new
|
|
31426
|
+
const g = new dagre5.graphlib.Graph();
|
|
30466
31427
|
g.setGraph({
|
|
30467
31428
|
rankdir: "TB",
|
|
30468
31429
|
nodesep: spacing.nodesep,
|
|
@@ -30483,7 +31444,7 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
30483
31444
|
g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
|
|
30484
31445
|
}
|
|
30485
31446
|
}
|
|
30486
|
-
|
|
31447
|
+
dagre5.layout(g);
|
|
30487
31448
|
reduceCrossings(
|
|
30488
31449
|
g,
|
|
30489
31450
|
validRels.map((r) => ({ source: r.sourceName, target: r.targetName }))
|
|
@@ -30550,8 +31511,8 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
30550
31511
|
}
|
|
30551
31512
|
}
|
|
30552
31513
|
if (nodes.length > 0) {
|
|
30553
|
-
const shiftX =
|
|
30554
|
-
const shiftY =
|
|
31514
|
+
const shiftX = MARGIN6 - minX;
|
|
31515
|
+
const shiftY = MARGIN6 - minY;
|
|
30555
31516
|
for (const node of nodes) {
|
|
30556
31517
|
node.x += shiftX;
|
|
30557
31518
|
node.y += shiftY;
|
|
@@ -30563,12 +31524,12 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
30563
31524
|
}
|
|
30564
31525
|
}
|
|
30565
31526
|
}
|
|
30566
|
-
let totalWidth = nodes.length > 0 ? maxX - minX +
|
|
30567
|
-
let totalHeight = nodes.length > 0 ? maxY - minY +
|
|
31527
|
+
let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN6 * 2 : 0;
|
|
31528
|
+
let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN6 * 2 : 0;
|
|
30568
31529
|
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
30569
31530
|
if (legendGroups.length > 0) {
|
|
30570
|
-
const legendY = totalHeight +
|
|
30571
|
-
let legendX =
|
|
31531
|
+
const legendY = totalHeight + MARGIN6;
|
|
31532
|
+
let legendX = MARGIN6;
|
|
30572
31533
|
for (const lg of legendGroups) {
|
|
30573
31534
|
lg.x = legendX;
|
|
30574
31535
|
lg.y = legendY;
|
|
@@ -30661,7 +31622,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
30661
31622
|
}
|
|
30662
31623
|
}
|
|
30663
31624
|
const hasGroups = elementToGroup.size > 0;
|
|
30664
|
-
const g = hasGroups ? new
|
|
31625
|
+
const g = hasGroups ? new dagre5.graphlib.Graph({ compound: true }) : new dagre5.graphlib.Graph();
|
|
30665
31626
|
g.setDefaultEdgeLabel(() => ({}));
|
|
30666
31627
|
if (hasGroups) {
|
|
30667
31628
|
const seenGroups = /* @__PURE__ */ new Set();
|
|
@@ -30739,7 +31700,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
30739
31700
|
g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
|
|
30740
31701
|
}
|
|
30741
31702
|
}
|
|
30742
|
-
|
|
31703
|
+
dagre5.layout(g);
|
|
30743
31704
|
const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
|
|
30744
31705
|
reduceCrossings(
|
|
30745
31706
|
g,
|
|
@@ -30900,8 +31861,8 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
30900
31861
|
if (pt.y > maxY) maxY = pt.y;
|
|
30901
31862
|
}
|
|
30902
31863
|
}
|
|
30903
|
-
const shiftX =
|
|
30904
|
-
const shiftY =
|
|
31864
|
+
const shiftX = MARGIN6 - minX;
|
|
31865
|
+
const shiftY = MARGIN6 - minY;
|
|
30905
31866
|
for (const node of nodes) {
|
|
30906
31867
|
node.x += shiftX;
|
|
30907
31868
|
node.y += shiftY;
|
|
@@ -30918,12 +31879,12 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
30918
31879
|
pt.y += shiftY;
|
|
30919
31880
|
}
|
|
30920
31881
|
}
|
|
30921
|
-
let totalWidth = maxX - minX +
|
|
30922
|
-
let totalHeight = maxY - minY +
|
|
31882
|
+
let totalWidth = maxX - minX + MARGIN6 * 2;
|
|
31883
|
+
let totalHeight = maxY - minY + MARGIN6 * 2;
|
|
30923
31884
|
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
30924
31885
|
if (legendGroups.length > 0) {
|
|
30925
|
-
const legendY = totalHeight +
|
|
30926
|
-
let legendX =
|
|
31886
|
+
const legendY = totalHeight + MARGIN6;
|
|
31887
|
+
let legendX = MARGIN6;
|
|
30927
31888
|
for (const lg of legendGroups) {
|
|
30928
31889
|
lg.x = legendX;
|
|
30929
31890
|
lg.y = legendY;
|
|
@@ -31068,7 +32029,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
31068
32029
|
}
|
|
31069
32030
|
}
|
|
31070
32031
|
const hasGroups = elementToGroup.size > 0;
|
|
31071
|
-
const g = hasGroups ? new
|
|
32032
|
+
const g = hasGroups ? new dagre5.graphlib.Graph({ compound: true }) : new dagre5.graphlib.Graph();
|
|
31072
32033
|
g.setDefaultEdgeLabel(() => ({}));
|
|
31073
32034
|
if (hasGroups) {
|
|
31074
32035
|
const seenGroups = /* @__PURE__ */ new Set();
|
|
@@ -31152,7 +32113,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
31152
32113
|
g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
|
|
31153
32114
|
}
|
|
31154
32115
|
}
|
|
31155
|
-
|
|
32116
|
+
dagre5.layout(g);
|
|
31156
32117
|
const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
|
|
31157
32118
|
reduceCrossings(
|
|
31158
32119
|
g,
|
|
@@ -31315,8 +32276,8 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
31315
32276
|
if (pt.y > maxY) maxY = pt.y;
|
|
31316
32277
|
}
|
|
31317
32278
|
}
|
|
31318
|
-
const shiftX =
|
|
31319
|
-
const shiftY =
|
|
32279
|
+
const shiftX = MARGIN6 - minX;
|
|
32280
|
+
const shiftY = MARGIN6 - minY;
|
|
31320
32281
|
for (const node of nodes) {
|
|
31321
32282
|
node.x += shiftX;
|
|
31322
32283
|
node.y += shiftY;
|
|
@@ -31333,12 +32294,12 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
31333
32294
|
pt.y += shiftY;
|
|
31334
32295
|
}
|
|
31335
32296
|
}
|
|
31336
|
-
let totalWidth = maxX - minX +
|
|
31337
|
-
let totalHeight = maxY - minY +
|
|
32297
|
+
let totalWidth = maxX - minX + MARGIN6 * 2;
|
|
32298
|
+
let totalHeight = maxY - minY + MARGIN6 * 2;
|
|
31338
32299
|
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
31339
32300
|
if (legendGroups.length > 0) {
|
|
31340
|
-
const legendY = totalHeight +
|
|
31341
|
-
let legendX =
|
|
32301
|
+
const legendY = totalHeight + MARGIN6;
|
|
32302
|
+
let legendX = MARGIN6;
|
|
31342
32303
|
for (const lg of legendGroups) {
|
|
31343
32304
|
lg.x = legendX;
|
|
31344
32305
|
lg.y = legendY;
|
|
@@ -31444,7 +32405,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31444
32405
|
for (const r of refEntries) {
|
|
31445
32406
|
nameToElement.set(r.element.name, r.element);
|
|
31446
32407
|
}
|
|
31447
|
-
const g = new
|
|
32408
|
+
const g = new dagre5.graphlib.Graph({ compound: true });
|
|
31448
32409
|
g.setDefaultEdgeLabel(() => ({}));
|
|
31449
32410
|
for (const [infraId] of infraIds) {
|
|
31450
32411
|
g.setNode(infraId, {});
|
|
@@ -31488,7 +32449,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31488
32449
|
g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
|
|
31489
32450
|
}
|
|
31490
32451
|
}
|
|
31491
|
-
|
|
32452
|
+
dagre5.layout(g);
|
|
31492
32453
|
const nodeInfraMap = /* @__PURE__ */ new Map();
|
|
31493
32454
|
for (const r of refEntries) nodeInfraMap.set(r.element.name, r.infraId);
|
|
31494
32455
|
reduceCrossings(
|
|
@@ -31626,8 +32587,8 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31626
32587
|
if (pt.y > maxY) maxY = pt.y;
|
|
31627
32588
|
}
|
|
31628
32589
|
}
|
|
31629
|
-
const shiftX =
|
|
31630
|
-
const shiftY =
|
|
32590
|
+
const shiftX = MARGIN6 - minX;
|
|
32591
|
+
const shiftY = MARGIN6 - minY;
|
|
31631
32592
|
for (const node of nodes) {
|
|
31632
32593
|
node.x += shiftX;
|
|
31633
32594
|
node.y += shiftY;
|
|
@@ -31642,12 +32603,12 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31642
32603
|
pt.y += shiftY;
|
|
31643
32604
|
}
|
|
31644
32605
|
}
|
|
31645
|
-
let totalWidth = maxX - minX +
|
|
31646
|
-
let totalHeight = maxY - minY +
|
|
32606
|
+
let totalWidth = maxX - minX + MARGIN6 * 2;
|
|
32607
|
+
let totalHeight = maxY - minY + MARGIN6 * 2;
|
|
31647
32608
|
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
31648
32609
|
if (legendGroups.length > 0) {
|
|
31649
|
-
const legendY = totalHeight +
|
|
31650
|
-
let legendX =
|
|
32610
|
+
const legendY = totalHeight + MARGIN6;
|
|
32611
|
+
let legendX = MARGIN6;
|
|
31651
32612
|
for (const lg of legendGroups) {
|
|
31652
32613
|
lg.x = legendX;
|
|
31653
32614
|
lg.y = legendY;
|
|
@@ -31667,7 +32628,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31667
32628
|
height: totalHeight
|
|
31668
32629
|
};
|
|
31669
32630
|
}
|
|
31670
|
-
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,
|
|
32631
|
+
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;
|
|
31671
32632
|
var init_layout8 = __esm({
|
|
31672
32633
|
"src/c4/layout.ts"() {
|
|
31673
32634
|
"use strict";
|
|
@@ -31687,7 +32648,7 @@ var init_layout8 = __esm({
|
|
|
31687
32648
|
CARD_H_PAD3 = 20;
|
|
31688
32649
|
META_LINE_HEIGHT5 = 16;
|
|
31689
32650
|
META_FONT_SIZE5 = 11;
|
|
31690
|
-
|
|
32651
|
+
MARGIN6 = 40;
|
|
31691
32652
|
BOUNDARY_PAD = 40;
|
|
31692
32653
|
GROUP_BOUNDARY_PAD = 24;
|
|
31693
32654
|
EDGE_NODE_COLLISION_WEIGHT = 5e3;
|
|
@@ -32722,7 +33683,7 @@ var layout_exports9 = {};
|
|
|
32722
33683
|
__export(layout_exports9, {
|
|
32723
33684
|
layoutGraph: () => layoutGraph
|
|
32724
33685
|
});
|
|
32725
|
-
import
|
|
33686
|
+
import dagre6 from "@dagrejs/dagre";
|
|
32726
33687
|
function computeNodeWidth(label, shape) {
|
|
32727
33688
|
if (shape === "pseudostate") return 24;
|
|
32728
33689
|
const base = Math.max(120, label.length * 9 + 40);
|
|
@@ -32756,7 +33717,7 @@ function layoutGraph(graph, options) {
|
|
|
32756
33717
|
if (allNodes.length === 0) {
|
|
32757
33718
|
return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
|
|
32758
33719
|
}
|
|
32759
|
-
const g = new
|
|
33720
|
+
const g = new dagre6.graphlib.Graph({ compound: true });
|
|
32760
33721
|
g.setGraph({
|
|
32761
33722
|
rankdir: graph.direction,
|
|
32762
33723
|
nodesep: 50,
|
|
@@ -32807,7 +33768,7 @@ function layoutGraph(graph, options) {
|
|
|
32807
33768
|
label: edge.label ?? ""
|
|
32808
33769
|
});
|
|
32809
33770
|
}
|
|
32810
|
-
|
|
33771
|
+
dagre6.layout(g);
|
|
32811
33772
|
const collapsedGroupIds = collapsedChildCounts ? new Set(collapsedChildCounts.keys()) : /* @__PURE__ */ new Set();
|
|
32812
33773
|
const basePositioned = allNodes.map((node) => {
|
|
32813
33774
|
const pos = g.node(node.id);
|
|
@@ -34511,7 +35472,7 @@ __export(layout_exports10, {
|
|
|
34511
35472
|
layoutInfra: () => layoutInfra,
|
|
34512
35473
|
separateGroups: () => separateGroups
|
|
34513
35474
|
});
|
|
34514
|
-
import
|
|
35475
|
+
import dagre7 from "@dagrejs/dagre";
|
|
34515
35476
|
function countDisplayProps(node, expanded, options) {
|
|
34516
35477
|
if (!expanded) return 0;
|
|
34517
35478
|
let count = node.properties.filter((p) => DISPLAY_KEYS.has(p.key)).length;
|
|
@@ -34813,7 +35774,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
34813
35774
|
};
|
|
34814
35775
|
}
|
|
34815
35776
|
const isLR = computed.direction !== "TB";
|
|
34816
|
-
const g = new
|
|
35777
|
+
const g = new dagre7.graphlib.Graph();
|
|
34817
35778
|
g.setGraph({
|
|
34818
35779
|
rankdir: computed.direction === "TB" ? "TB" : "LR",
|
|
34819
35780
|
nodesep: isLR ? 70 : 60,
|
|
@@ -34864,7 +35825,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
34864
35825
|
g.setEdge(edge.sourceId, edge.targetId, { label: edge.label });
|
|
34865
35826
|
}
|
|
34866
35827
|
}
|
|
34867
|
-
|
|
35828
|
+
dagre7.layout(g);
|
|
34868
35829
|
const layoutNodes = computed.nodes.map(
|
|
34869
35830
|
(node) => {
|
|
34870
35831
|
const pos = g.node(node.id);
|
|
@@ -36606,17 +37567,17 @@ function mulberry32(seed) {
|
|
|
36606
37567
|
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
36607
37568
|
};
|
|
36608
37569
|
}
|
|
36609
|
-
function standardNormal(
|
|
37570
|
+
function standardNormal(rng3) {
|
|
36610
37571
|
let u = 0;
|
|
36611
37572
|
let v = 0;
|
|
36612
|
-
while (u === 0) u =
|
|
36613
|
-
while (v === 0) v =
|
|
37573
|
+
while (u === 0) u = rng3();
|
|
37574
|
+
while (v === 0) v = rng3();
|
|
36614
37575
|
return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
|
|
36615
37576
|
}
|
|
36616
|
-
function gamma(shape,
|
|
37577
|
+
function gamma(shape, rng3) {
|
|
36617
37578
|
if (shape < 1) {
|
|
36618
|
-
const u =
|
|
36619
|
-
return gamma(shape + 1,
|
|
37579
|
+
const u = rng3();
|
|
37580
|
+
return gamma(shape + 1, rng3) * Math.pow(u, 1 / shape);
|
|
36620
37581
|
}
|
|
36621
37582
|
const d = shape - 1 / 3;
|
|
36622
37583
|
const c = 1 / Math.sqrt(9 * d);
|
|
@@ -36624,29 +37585,29 @@ function gamma(shape, rng) {
|
|
|
36624
37585
|
let x;
|
|
36625
37586
|
let v;
|
|
36626
37587
|
do {
|
|
36627
|
-
x = standardNormal(
|
|
37588
|
+
x = standardNormal(rng3);
|
|
36628
37589
|
v = 1 + c * x;
|
|
36629
37590
|
} while (v <= 0);
|
|
36630
37591
|
v = v * v * v;
|
|
36631
|
-
const u =
|
|
37592
|
+
const u = rng3();
|
|
36632
37593
|
if (u < 1 - 0.0331 * x * x * x * x) return d * v;
|
|
36633
37594
|
if (Math.log(u) < x * x / 2 + d * (1 - v + Math.log(v))) return d * v;
|
|
36634
37595
|
}
|
|
36635
37596
|
}
|
|
36636
|
-
function sampleBeta(alpha, beta,
|
|
36637
|
-
const x = gamma(alpha,
|
|
36638
|
-
const y = gamma(beta,
|
|
37597
|
+
function sampleBeta(alpha, beta, rng3) {
|
|
37598
|
+
const x = gamma(alpha, rng3);
|
|
37599
|
+
const y = gamma(beta, rng3);
|
|
36639
37600
|
return x / (x + y);
|
|
36640
37601
|
}
|
|
36641
|
-
function sampleBetaPert(o, m, p,
|
|
37602
|
+
function sampleBetaPert(o, m, p, rng3) {
|
|
36642
37603
|
if (o === p) return m;
|
|
36643
37604
|
const range = p - o;
|
|
36644
37605
|
const alpha = 1 + 4 * (m - o) / range;
|
|
36645
37606
|
const beta = 1 + 4 * (p - m) / range;
|
|
36646
|
-
return o + sampleBeta(alpha, beta,
|
|
37607
|
+
return o + sampleBeta(alpha, beta, rng3) * range;
|
|
36647
37608
|
}
|
|
36648
37609
|
function simulate(resolved, expanded, _predecessors, _successors, topo, terminals, poisoned, opts) {
|
|
36649
|
-
const
|
|
37610
|
+
const rng3 = mulberry32(opts.seed);
|
|
36650
37611
|
const expById = /* @__PURE__ */ new Map();
|
|
36651
37612
|
for (const e of expanded) expById.set(e.id, e);
|
|
36652
37613
|
const completionTimes = new Array(opts.trials);
|
|
@@ -36669,7 +37630,7 @@ function simulate(resolved, expanded, _predecessors, _successors, topo, terminal
|
|
|
36669
37630
|
} else if (exp.o === exp.p) {
|
|
36670
37631
|
duration = exp.m;
|
|
36671
37632
|
} else {
|
|
36672
|
-
duration = sampleBetaPert(exp.o, exp.m, exp.p,
|
|
37633
|
+
duration = sampleBetaPert(exp.o, exp.m, exp.p, rng3);
|
|
36673
37634
|
}
|
|
36674
37635
|
let esLower = 0;
|
|
36675
37636
|
let efLower = -Infinity;
|
|
@@ -37658,7 +38619,7 @@ __export(layout_exports11, {
|
|
|
37658
38619
|
layoutPert: () => layoutPert,
|
|
37659
38620
|
relayoutPert: () => relayoutPert
|
|
37660
38621
|
});
|
|
37661
|
-
import
|
|
38622
|
+
import dagre8 from "@dagrejs/dagre";
|
|
37662
38623
|
function computeNodeSizing(resolved) {
|
|
37663
38624
|
const unit = resolved.options.timeUnit;
|
|
37664
38625
|
const sprintMode = resolved.options.sprintMode;
|
|
@@ -37776,7 +38737,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
|
|
|
37776
38737
|
}
|
|
37777
38738
|
}
|
|
37778
38739
|
const dagreId = (id) => memberToGroup.get(id) ?? id;
|
|
37779
|
-
const g = new
|
|
38740
|
+
const g = new dagre8.graphlib.Graph();
|
|
37780
38741
|
g.setGraph({
|
|
37781
38742
|
rankdir: resolved.options.direction,
|
|
37782
38743
|
nodesep: 50,
|
|
@@ -37815,7 +38776,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
|
|
|
37815
38776
|
seenEdges.add(k);
|
|
37816
38777
|
g.setEdge(src, tgt, {});
|
|
37817
38778
|
}
|
|
37818
|
-
|
|
38779
|
+
dagre8.layout(g);
|
|
37819
38780
|
const swimApplied = applySwimLanes(
|
|
37820
38781
|
g,
|
|
37821
38782
|
resolved,
|
|
@@ -38161,7 +39122,7 @@ function reduceCrossings2(g, direction) {
|
|
|
38161
39122
|
buckets.get(key).push(id);
|
|
38162
39123
|
}
|
|
38163
39124
|
const edges = g.edges().map((e) => ({ v: e.v, w: e.w }));
|
|
38164
|
-
const
|
|
39125
|
+
const countCrossings = () => {
|
|
38165
39126
|
let total = 0;
|
|
38166
39127
|
for (let i = 0; i < edges.length; i++) {
|
|
38167
39128
|
const a = edges[i];
|
|
@@ -38174,13 +39135,13 @@ function reduceCrossings2(g, direction) {
|
|
|
38174
39135
|
const b1 = g.node(b.v);
|
|
38175
39136
|
const b2 = g.node(b.w);
|
|
38176
39137
|
if (!b1 || !b2) continue;
|
|
38177
|
-
if (
|
|
39138
|
+
if (segmentsCross(a1, a2, b1, b2)) total++;
|
|
38178
39139
|
}
|
|
38179
39140
|
}
|
|
38180
39141
|
return total;
|
|
38181
39142
|
};
|
|
38182
39143
|
const MAX_ITER = 8;
|
|
38183
|
-
let baseline =
|
|
39144
|
+
let baseline = countCrossings();
|
|
38184
39145
|
if (baseline === 0) return;
|
|
38185
39146
|
for (let iter = 0; iter < MAX_ITER; iter++) {
|
|
38186
39147
|
let improved = false;
|
|
@@ -38198,7 +39159,7 @@ function reduceCrossings2(g, direction) {
|
|
|
38198
39159
|
const bv = bn[slotAxis];
|
|
38199
39160
|
an[slotAxis] = bv;
|
|
38200
39161
|
bn[slotAxis] = av;
|
|
38201
|
-
const after =
|
|
39162
|
+
const after = countCrossings();
|
|
38202
39163
|
if (after < baseline) {
|
|
38203
39164
|
baseline = after;
|
|
38204
39165
|
improved = true;
|
|
@@ -38219,7 +39180,7 @@ function reduceCrossings2(g, direction) {
|
|
|
38219
39180
|
data.points = smoothEdge(src, tgt, direction);
|
|
38220
39181
|
}
|
|
38221
39182
|
}
|
|
38222
|
-
function
|
|
39183
|
+
function segmentsCross(a1, a2, b1, b2) {
|
|
38223
39184
|
const ccw = (p, q, r) => (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x);
|
|
38224
39185
|
const d1 = ccw(b1, b2, a1);
|
|
38225
39186
|
const d2 = ccw(b1, b2, a2);
|
|
@@ -47760,10 +48721,10 @@ function tierBand(maxSpanDeg) {
|
|
|
47760
48721
|
}
|
|
47761
48722
|
function labelBudget(width, height, band) {
|
|
47762
48723
|
const bandCap = {
|
|
47763
|
-
world:
|
|
47764
|
-
continental:
|
|
47765
|
-
regional:
|
|
47766
|
-
local:
|
|
48724
|
+
world: 7,
|
|
48725
|
+
continental: 6,
|
|
48726
|
+
regional: 5,
|
|
48727
|
+
local: 4
|
|
47767
48728
|
};
|
|
47768
48729
|
const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
|
|
47769
48730
|
return Math.max(0, Math.min(area2, bandCap[band]));
|
|
@@ -47896,8 +48857,11 @@ function placeContextLabels(args) {
|
|
|
47896
48857
|
color: waterColor,
|
|
47897
48858
|
fontSize: FONT,
|
|
47898
48859
|
// water names keep the base font (no footprint to scale on)
|
|
47899
|
-
//
|
|
47900
|
-
|
|
48860
|
+
// Orientation-value bands (lower = earlier): MAJOR water (oceans + major
|
|
48861
|
+
// seas, tier ≤ 1) leads at `tier*10+kind` (0..~16); MINOR water (tier ≥ 2:
|
|
48862
|
+
// smaller seas, bays, gulfs, straits) drops to band 2 (2000+) — BELOW the
|
|
48863
|
+
// country band (1000+), so a big country outranks a minor basin.
|
|
48864
|
+
sort: (tier <= 1 ? 0 : 2e3) + tier * 10 + KIND_ORDER[kind]
|
|
47901
48865
|
});
|
|
47902
48866
|
}
|
|
47903
48867
|
const ranked = countries.map((c) => {
|
|
@@ -47910,7 +48874,7 @@ function placeContextLabels(args) {
|
|
|
47910
48874
|
let ci = 0;
|
|
47911
48875
|
for (const r of ranked) {
|
|
47912
48876
|
const { c, w, h, area: area2 } = r;
|
|
47913
|
-
if (w > width * 0.66
|
|
48877
|
+
if (!c.curatedAnchor && w > width * 0.66 && h > height * 0.66) continue;
|
|
47914
48878
|
if (!insideViewport(c.anchor, width, height)) continue;
|
|
47915
48879
|
const sizeFrac = Math.sqrt(area2) / canvasLinear;
|
|
47916
48880
|
const t = Math.min(
|
|
@@ -47935,15 +48899,23 @@ function placeContextLabels(args) {
|
|
|
47935
48899
|
letterSpacing: 0,
|
|
47936
48900
|
color,
|
|
47937
48901
|
fontSize,
|
|
47938
|
-
//
|
|
47939
|
-
|
|
48902
|
+
// Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
|
|
48903
|
+
// below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
|
|
48904
|
+
// (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
|
|
48905
|
+
// Campeche) yet never displaces an ocean. Larger area = earlier within the
|
|
48906
|
+
// band (`ci` is the area-desc rank index).
|
|
48907
|
+
sort: 1e3 + ci++
|
|
47940
48908
|
});
|
|
47941
48909
|
}
|
|
47942
48910
|
candidates.sort((a, b) => a.sort - b.sort);
|
|
47943
48911
|
const placed = [];
|
|
47944
48912
|
const placedRects = [];
|
|
48913
|
+
const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
|
|
48914
|
+
const waterCap = budget - Math.min(2, countryCount);
|
|
48915
|
+
let waterPlaced = 0;
|
|
47945
48916
|
for (const cand of candidates) {
|
|
47946
48917
|
if (placed.length >= budget) break;
|
|
48918
|
+
if (cand.italic && waterPlaced >= waterCap) continue;
|
|
47947
48919
|
const rect = rectAround(
|
|
47948
48920
|
cand.cx,
|
|
47949
48921
|
cand.cy,
|
|
@@ -47970,6 +48942,7 @@ function placeContextLabels(args) {
|
|
|
47970
48942
|
if (collides(rect)) continue;
|
|
47971
48943
|
if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
|
|
47972
48944
|
placedRects.push(rect);
|
|
48945
|
+
if (cand.italic) waterPlaced++;
|
|
47973
48946
|
placed.push({
|
|
47974
48947
|
x: cand.cx,
|
|
47975
48948
|
y: cand.cy,
|
|
@@ -49943,16 +50916,16 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
49943
50916
|
countryCandidates.push({
|
|
49944
50917
|
name: f.properties?.name ?? iso,
|
|
49945
50918
|
bbox: [x0, y0, x1, y1],
|
|
49946
|
-
anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
|
|
50919
|
+
anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
|
|
50920
|
+
curatedAnchor: !!anchorLngLat
|
|
49947
50921
|
});
|
|
49948
50922
|
}
|
|
49949
50923
|
const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
|
|
49950
50924
|
(id) => id.startsWith("US-")
|
|
49951
50925
|
);
|
|
49952
50926
|
if (usLayer && framedStateContainers) {
|
|
49953
|
-
const containerSet = new Set(resolved.poiFrameContainers);
|
|
49954
50927
|
for (const [iso, f] of usLayer) {
|
|
49955
|
-
if (
|
|
50928
|
+
if (regionById.has(iso)) continue;
|
|
49956
50929
|
const viewF = cullFeatureToView(f);
|
|
49957
50930
|
if (!viewF) continue;
|
|
49958
50931
|
const b = path.bounds(viewF);
|
|
@@ -50080,8 +51053,15 @@ var init_layout15 = __esm({
|
|
|
50080
51053
|
W_MAX = 8;
|
|
50081
51054
|
FONT2 = 11;
|
|
50082
51055
|
WORLD_LABEL_ANCHORS = {
|
|
50083
|
-
US: [-98.5, 39.5]
|
|
51056
|
+
US: [-98.5, 39.5],
|
|
50084
51057
|
// CONUS geographic centre (near Lebanon, Kansas)
|
|
51058
|
+
// Russia crosses the antimeridian (Chukotka at ~170°W), so on a non-global
|
|
51059
|
+
// (e.g. Europe) projection its geometry smears across the whole frame and the
|
|
51060
|
+
// area-weighted centroid lands mid-map (over Europe) — useless as a label
|
|
51061
|
+
// anchor. Pin it to European Russia (~Volga) so a Europe view labels visible
|
|
51062
|
+
// western Russia on its eastern margin; on a world view this still sits over
|
|
51063
|
+
// Russian land. (See the curated-anchor smear-gate bypass in context-labels.)
|
|
51064
|
+
RU: [45, 58]
|
|
50085
51065
|
};
|
|
50086
51066
|
MAX_CLUSTER_EXTENT_FACTOR = 0.18;
|
|
50087
51067
|
MAX_COLUMN_ROWS = 7;
|