@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/auto.mjs
CHANGED
|
@@ -27543,12 +27543,1344 @@ var init_renderer6 = __esm({
|
|
|
27543
27543
|
}
|
|
27544
27544
|
});
|
|
27545
27545
|
|
|
27546
|
+
// src/boxes-and-lines/layout-layered.ts
|
|
27547
|
+
function rng(s) {
|
|
27548
|
+
return () => {
|
|
27549
|
+
s |= 0;
|
|
27550
|
+
s = s + 1831565813 | 0;
|
|
27551
|
+
let t = Math.imul(s ^ s >>> 15, 1 | s);
|
|
27552
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
27553
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
27554
|
+
};
|
|
27555
|
+
}
|
|
27556
|
+
function layeredCandidates(parsed, sizes, opts) {
|
|
27557
|
+
if (parsed.groups.length > 0) return [];
|
|
27558
|
+
const nCount = parsed.nodes.length;
|
|
27559
|
+
if (nCount < 3 || nCount > 40) return [];
|
|
27560
|
+
const isTB = parsed.direction === "TB";
|
|
27561
|
+
const nodesep = opts?.nodesep ?? NODESEP;
|
|
27562
|
+
const ranksep = opts?.ranksep ?? RANKSEP;
|
|
27563
|
+
const backEdgeSide = opts?.backEdgeSide ?? "nearest";
|
|
27564
|
+
const restarts = opts?.restarts ?? (nCount <= 14 ? 28 : nCount <= 25 ? 14 : 6);
|
|
27565
|
+
const keepK = opts?.keepK ?? 3;
|
|
27566
|
+
const labels = parsed.nodes.map((n) => n.label);
|
|
27567
|
+
const labelSet = new Set(labels);
|
|
27568
|
+
const edges = parsed.edges.map((e, i) => ({ e, i })).filter(
|
|
27569
|
+
({ e }) => labelSet.has(e.source) && labelSet.has(e.target) && e.source !== e.target
|
|
27570
|
+
);
|
|
27571
|
+
if (edges.length === 0) return [];
|
|
27572
|
+
const adj = /* @__PURE__ */ new Map();
|
|
27573
|
+
for (const l of labels) adj.set(l, []);
|
|
27574
|
+
for (const { e, i } of edges)
|
|
27575
|
+
adj.get(e.source).push({ to: e.target, idx: i });
|
|
27576
|
+
const reversed = /* @__PURE__ */ new Set();
|
|
27577
|
+
const state = /* @__PURE__ */ new Map();
|
|
27578
|
+
for (const l of labels) state.set(l, 0);
|
|
27579
|
+
const dfs = (u) => {
|
|
27580
|
+
state.set(u, 1);
|
|
27581
|
+
for (const { to, idx } of adj.get(u)) {
|
|
27582
|
+
const st = state.get(to);
|
|
27583
|
+
if (st === 1)
|
|
27584
|
+
reversed.add(idx);
|
|
27585
|
+
else if (st === 0) dfs(to);
|
|
27586
|
+
}
|
|
27587
|
+
state.set(u, 2);
|
|
27588
|
+
};
|
|
27589
|
+
for (const l of labels) if (state.get(l) === 0) dfs(l);
|
|
27590
|
+
const dag = edges.map(
|
|
27591
|
+
({ 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 }
|
|
27592
|
+
);
|
|
27593
|
+
const outDag = /* @__PURE__ */ new Map();
|
|
27594
|
+
const inDeg = /* @__PURE__ */ new Map();
|
|
27595
|
+
for (const l of labels) {
|
|
27596
|
+
outDag.set(l, []);
|
|
27597
|
+
inDeg.set(l, 0);
|
|
27598
|
+
}
|
|
27599
|
+
for (const d of dag) {
|
|
27600
|
+
outDag.get(d.from).push(d.to);
|
|
27601
|
+
inDeg.set(d.to, (inDeg.get(d.to) ?? 0) + 1);
|
|
27602
|
+
}
|
|
27603
|
+
const rank = /* @__PURE__ */ new Map();
|
|
27604
|
+
for (const l of labels) rank.set(l, 0);
|
|
27605
|
+
const queue = labels.filter((l) => (inDeg.get(l) ?? 0) === 0);
|
|
27606
|
+
const indeg2 = new Map(inDeg);
|
|
27607
|
+
const topo = [];
|
|
27608
|
+
while (queue.length) {
|
|
27609
|
+
const u = queue.shift();
|
|
27610
|
+
topo.push(u);
|
|
27611
|
+
for (const v of outDag.get(u)) {
|
|
27612
|
+
rank.set(v, Math.max(rank.get(v), rank.get(u) + 1));
|
|
27613
|
+
indeg2.set(v, indeg2.get(v) - 1);
|
|
27614
|
+
if (indeg2.get(v) === 0) queue.push(v);
|
|
27615
|
+
}
|
|
27616
|
+
}
|
|
27617
|
+
if (topo.length !== labels.length) return [];
|
|
27618
|
+
const maxRank = Math.max(...labels.map((l) => rank.get(l)));
|
|
27619
|
+
const node = /* @__PURE__ */ new Map();
|
|
27620
|
+
for (const n of parsed.nodes) {
|
|
27621
|
+
const s = sizes.get(n.label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
|
|
27622
|
+
node.set(n.label, {
|
|
27623
|
+
id: n.label,
|
|
27624
|
+
dummy: false,
|
|
27625
|
+
rank: rank.get(n.label),
|
|
27626
|
+
cross: 0,
|
|
27627
|
+
thick: isTB ? s.width : s.height,
|
|
27628
|
+
depth: isTB ? s.height : s.width,
|
|
27629
|
+
realW: s.width,
|
|
27630
|
+
realH: s.height
|
|
27631
|
+
});
|
|
27632
|
+
}
|
|
27633
|
+
const segs = [];
|
|
27634
|
+
const chains = [];
|
|
27635
|
+
const backEdges = [];
|
|
27636
|
+
for (const d of dag) {
|
|
27637
|
+
if (d.rev) {
|
|
27638
|
+
backEdges.push({ edgeIdx: d.idx, src: d.to, tgt: d.from });
|
|
27639
|
+
continue;
|
|
27640
|
+
}
|
|
27641
|
+
const r0 = rank.get(d.from);
|
|
27642
|
+
const r1 = rank.get(d.to);
|
|
27643
|
+
const dagChain = [d.from];
|
|
27644
|
+
let prev = d.from;
|
|
27645
|
+
for (let r = r0 + 1; r < r1; r++) {
|
|
27646
|
+
const id = `__d${d.idx}_${r}`;
|
|
27647
|
+
node.set(id, {
|
|
27648
|
+
id,
|
|
27649
|
+
dummy: true,
|
|
27650
|
+
rank: r,
|
|
27651
|
+
cross: 0,
|
|
27652
|
+
thick: DUMMY_THICK,
|
|
27653
|
+
depth: 0,
|
|
27654
|
+
realW: 0,
|
|
27655
|
+
realH: 0
|
|
27656
|
+
});
|
|
27657
|
+
segs.push({ a: prev, b: id });
|
|
27658
|
+
dagChain.push(id);
|
|
27659
|
+
prev = id;
|
|
27660
|
+
}
|
|
27661
|
+
segs.push({ a: prev, b: d.to });
|
|
27662
|
+
dagChain.push(d.to);
|
|
27663
|
+
chains.push({
|
|
27664
|
+
edgeIdx: d.idx,
|
|
27665
|
+
chain: d.rev ? dagChain.slice().reverse() : dagChain
|
|
27666
|
+
});
|
|
27667
|
+
}
|
|
27668
|
+
const ranks = Array.from({ length: maxRank + 1 }, () => []);
|
|
27669
|
+
for (const n of node.values()) ranks[n.rank].push(n.id);
|
|
27670
|
+
const up = /* @__PURE__ */ new Map();
|
|
27671
|
+
const down = /* @__PURE__ */ new Map();
|
|
27672
|
+
for (const id of node.keys()) {
|
|
27673
|
+
up.set(id, []);
|
|
27674
|
+
down.set(id, []);
|
|
27675
|
+
}
|
|
27676
|
+
for (const s of segs) {
|
|
27677
|
+
down.get(s.a).push(s.b);
|
|
27678
|
+
up.get(s.b).push(s.a);
|
|
27679
|
+
}
|
|
27680
|
+
const orderOf = /* @__PURE__ */ new Map();
|
|
27681
|
+
const applyOrder = (ord) => {
|
|
27682
|
+
for (const id of node.keys()) orderOf.set(id, ord.get(id));
|
|
27683
|
+
};
|
|
27684
|
+
const gapCrossings = (r) => {
|
|
27685
|
+
const lower = ranks[r + 1];
|
|
27686
|
+
if (!lower) return 0;
|
|
27687
|
+
const es = [];
|
|
27688
|
+
for (const a of ranks[r]) {
|
|
27689
|
+
const pa = orderOf.get(a);
|
|
27690
|
+
for (const b of down.get(a)) es.push({ p: pa, q: orderOf.get(b) });
|
|
27691
|
+
}
|
|
27692
|
+
let c = 0;
|
|
27693
|
+
for (let i = 0; i < es.length; i++)
|
|
27694
|
+
for (let j = i + 1; j < es.length; j++) {
|
|
27695
|
+
const A = es[i], B = es[j];
|
|
27696
|
+
if ((A.p - B.p) * (A.q - B.q) < 0) c++;
|
|
27697
|
+
}
|
|
27698
|
+
return c;
|
|
27699
|
+
};
|
|
27700
|
+
const totalCrossings = () => {
|
|
27701
|
+
let c = 0;
|
|
27702
|
+
for (let r = 0; r < maxRank; r++) c += gapCrossings(r);
|
|
27703
|
+
return c;
|
|
27704
|
+
};
|
|
27705
|
+
const median = (vals) => {
|
|
27706
|
+
if (vals.length === 0) return -1;
|
|
27707
|
+
vals.sort((x, y) => x - y);
|
|
27708
|
+
const m = Math.floor(vals.length / 2);
|
|
27709
|
+
if (vals.length % 2 === 1) return vals[m];
|
|
27710
|
+
if (vals.length === 2) return (vals[0] + vals[1]) / 2;
|
|
27711
|
+
const left = vals[m - 1] - vals[0];
|
|
27712
|
+
const right = vals[vals.length - 1] - vals[m];
|
|
27713
|
+
return left + right === 0 ? (vals[m - 1] + vals[m]) / 2 : (vals[m - 1] * right + vals[m] * left) / (left + right);
|
|
27714
|
+
};
|
|
27715
|
+
const wmedianRank = (r, useUp) => {
|
|
27716
|
+
const ids = ranks[r];
|
|
27717
|
+
const neigh = useUp ? up : down;
|
|
27718
|
+
const med = /* @__PURE__ */ new Map();
|
|
27719
|
+
for (const id of ids)
|
|
27720
|
+
med.set(id, median(neigh.get(id).map((x) => orderOf.get(x))));
|
|
27721
|
+
const movable = ids.filter((id) => med.get(id) >= 0).sort((a, b) => med.get(a) - med.get(b));
|
|
27722
|
+
const out = new Array(ids.length).fill(null);
|
|
27723
|
+
for (const id of ids) if (med.get(id) < 0) out[orderOf.get(id)] = id;
|
|
27724
|
+
let mi = 0;
|
|
27725
|
+
for (let slot = 0; slot < out.length; slot++)
|
|
27726
|
+
if (out[slot] === null) out[slot] = movable[mi++];
|
|
27727
|
+
const final = out;
|
|
27728
|
+
final.forEach((id, i) => orderOf.set(id, i));
|
|
27729
|
+
ranks[r] = final;
|
|
27730
|
+
};
|
|
27731
|
+
const transpose = () => {
|
|
27732
|
+
let improved = true;
|
|
27733
|
+
let guard = 0;
|
|
27734
|
+
while (improved && guard++ < 12) {
|
|
27735
|
+
improved = false;
|
|
27736
|
+
for (let r = 0; r <= maxRank; r++) {
|
|
27737
|
+
const ids = ranks[r];
|
|
27738
|
+
for (let i = 0; i < ids.length - 1; i++) {
|
|
27739
|
+
const before = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
|
|
27740
|
+
const a = ids[i], b = ids[i + 1];
|
|
27741
|
+
ids[i] = b;
|
|
27742
|
+
ids[i + 1] = a;
|
|
27743
|
+
orderOf.set(b, i);
|
|
27744
|
+
orderOf.set(a, i + 1);
|
|
27745
|
+
const after = (r > 0 ? gapCrossings(r - 1) : 0) + (r < maxRank ? gapCrossings(r) : 0);
|
|
27746
|
+
if (after < before) {
|
|
27747
|
+
improved = true;
|
|
27748
|
+
} else {
|
|
27749
|
+
ids[i] = a;
|
|
27750
|
+
ids[i + 1] = b;
|
|
27751
|
+
orderOf.set(a, i);
|
|
27752
|
+
orderOf.set(b, i + 1);
|
|
27753
|
+
}
|
|
27754
|
+
}
|
|
27755
|
+
}
|
|
27756
|
+
}
|
|
27757
|
+
};
|
|
27758
|
+
const initOrder = (seed) => {
|
|
27759
|
+
const r = seed === 0 ? null : rng(seed);
|
|
27760
|
+
for (let rr = 0; rr <= maxRank; rr++) {
|
|
27761
|
+
const ids = ranks[rr].slice();
|
|
27762
|
+
if (r) {
|
|
27763
|
+
for (let i = ids.length - 1; i > 0; i--) {
|
|
27764
|
+
const j = Math.floor(r() * (i + 1));
|
|
27765
|
+
[ids[i], ids[j]] = [ids[j], ids[i]];
|
|
27766
|
+
}
|
|
27767
|
+
}
|
|
27768
|
+
ranks[rr] = ids;
|
|
27769
|
+
ids.forEach((id, i) => orderOf.set(id, i));
|
|
27770
|
+
}
|
|
27771
|
+
};
|
|
27772
|
+
const found = [];
|
|
27773
|
+
for (let s = 0; s < restarts; s++) {
|
|
27774
|
+
initOrder(s);
|
|
27775
|
+
let best = totalCrossings();
|
|
27776
|
+
let bestSnap = new Map(orderOf);
|
|
27777
|
+
for (let iter = 0; iter < 8; iter++) {
|
|
27778
|
+
const down1 = iter % 2 === 0;
|
|
27779
|
+
if (down1) for (let r = 1; r <= maxRank; r++) wmedianRank(r, true);
|
|
27780
|
+
else for (let r = maxRank - 1; r >= 0; r--) wmedianRank(r, false);
|
|
27781
|
+
transpose();
|
|
27782
|
+
const c = totalCrossings();
|
|
27783
|
+
if (c < best) {
|
|
27784
|
+
best = c;
|
|
27785
|
+
bestSnap = new Map(orderOf);
|
|
27786
|
+
}
|
|
27787
|
+
if (c === 0) break;
|
|
27788
|
+
}
|
|
27789
|
+
applyOrder(bestSnap);
|
|
27790
|
+
for (let r = 0; r <= maxRank; r++)
|
|
27791
|
+
ranks[r].sort((a, b) => orderOf.get(a) - orderOf.get(b));
|
|
27792
|
+
const sig = ranks.map((r) => r.join(",")).join("|");
|
|
27793
|
+
if (!found.some((f) => f.sig === sig))
|
|
27794
|
+
found.push({ sig, cross: best, order: new Map(bestSnap) });
|
|
27795
|
+
}
|
|
27796
|
+
found.sort((a, b) => a.cross - b.cross);
|
|
27797
|
+
const keep = found.slice(0, keepK);
|
|
27798
|
+
if (keep.length === 0) return [];
|
|
27799
|
+
const results = [];
|
|
27800
|
+
for (const cand of keep) {
|
|
27801
|
+
applyOrder(cand.order);
|
|
27802
|
+
const rk = Array.from({ length: maxRank + 1 }, () => []);
|
|
27803
|
+
for (const n of node.values()) rk[n.rank].push(n.id);
|
|
27804
|
+
for (let r = 0; r <= maxRank; r++)
|
|
27805
|
+
rk[r].sort((a, b) => cand.order.get(a) - cand.order.get(b));
|
|
27806
|
+
results.push(realize(rk));
|
|
27807
|
+
}
|
|
27808
|
+
return results;
|
|
27809
|
+
function realize(rk) {
|
|
27810
|
+
const bandDepth = rk.map(
|
|
27811
|
+
(ids) => Math.max(0, ...ids.map((id) => node.get(id).depth))
|
|
27812
|
+
);
|
|
27813
|
+
const bandCenter = [];
|
|
27814
|
+
let acc = MARGIN3;
|
|
27815
|
+
for (let r = 0; r <= maxRank; r++) {
|
|
27816
|
+
bandCenter[r] = acc + bandDepth[r] / 2;
|
|
27817
|
+
acc += bandDepth[r] + ranksep;
|
|
27818
|
+
}
|
|
27819
|
+
for (let r = 0; r <= maxRank; r++) {
|
|
27820
|
+
let x = MARGIN3;
|
|
27821
|
+
for (const id of rk[r]) {
|
|
27822
|
+
const n = node.get(id);
|
|
27823
|
+
n.cross = x + n.thick / 2;
|
|
27824
|
+
x += n.thick + nodesep;
|
|
27825
|
+
}
|
|
27826
|
+
}
|
|
27827
|
+
const ITER = 18;
|
|
27828
|
+
for (let it = 0; it < ITER; it++) {
|
|
27829
|
+
const downPass = it % 2 === 0;
|
|
27830
|
+
const order = downPass ? Array.from({ length: maxRank + 1 }, (_, i) => i) : Array.from({ length: maxRank + 1 }, (_, i) => maxRank - i);
|
|
27831
|
+
for (const r of order) {
|
|
27832
|
+
const ids = rk[r];
|
|
27833
|
+
const desired = ids.map((id) => {
|
|
27834
|
+
const n = node.get(id);
|
|
27835
|
+
let sw = 0, sx2 = 0;
|
|
27836
|
+
for (const nb of up.get(id)) {
|
|
27837
|
+
const w = weight(n, node.get(nb));
|
|
27838
|
+
sw += w;
|
|
27839
|
+
sx2 += w * node.get(nb).cross;
|
|
27840
|
+
}
|
|
27841
|
+
for (const nb of down.get(id)) {
|
|
27842
|
+
const w = weight(n, node.get(nb));
|
|
27843
|
+
sw += w;
|
|
27844
|
+
sx2 += w * node.get(nb).cross;
|
|
27845
|
+
}
|
|
27846
|
+
return sw > 0 ? sx2 / sw : n.cross;
|
|
27847
|
+
});
|
|
27848
|
+
placeWithSeparation(ids, desired);
|
|
27849
|
+
}
|
|
27850
|
+
}
|
|
27851
|
+
let minC = Infinity;
|
|
27852
|
+
for (const n of node.values()) minC = Math.min(minC, n.cross - n.thick / 2);
|
|
27853
|
+
const shift = MARGIN3 - minC;
|
|
27854
|
+
for (const n of node.values()) n.cross += shift;
|
|
27855
|
+
const coord = (n) => isTB ? { x: n.cross, y: bandCenter[n.rank] } : { x: bandCenter[n.rank], y: n.cross };
|
|
27856
|
+
const layoutNodes = parsed.nodes.map((pn) => {
|
|
27857
|
+
const n = node.get(pn.label);
|
|
27858
|
+
const c = coord(n);
|
|
27859
|
+
return {
|
|
27860
|
+
label: pn.label,
|
|
27861
|
+
x: c.x,
|
|
27862
|
+
y: c.y,
|
|
27863
|
+
width: n.realW,
|
|
27864
|
+
height: n.realH
|
|
27865
|
+
};
|
|
27866
|
+
});
|
|
27867
|
+
const chainById = /* @__PURE__ */ new Map();
|
|
27868
|
+
for (const ch of chains) chainById.set(ch.edgeIdx, ch.chain);
|
|
27869
|
+
let minCross = Infinity, maxCross = -Infinity;
|
|
27870
|
+
for (const n of node.values()) {
|
|
27871
|
+
if (n.dummy) continue;
|
|
27872
|
+
minCross = Math.min(minCross, n.cross - n.thick / 2);
|
|
27873
|
+
maxCross = Math.max(maxCross, n.cross + n.thick / 2);
|
|
27874
|
+
}
|
|
27875
|
+
const GAP = 34;
|
|
27876
|
+
const LANE = 28;
|
|
27877
|
+
const sideOf = (b) => {
|
|
27878
|
+
if (backEdgeSide === "left") return -1;
|
|
27879
|
+
if (backEdgeSide === "right") return 1;
|
|
27880
|
+
const d = node.get(b.src).cross - node.get(b.tgt).cross;
|
|
27881
|
+
return d >= 0 ? 1 : -1;
|
|
27882
|
+
};
|
|
27883
|
+
const backSorted = backEdges.map((b) => ({
|
|
27884
|
+
...b,
|
|
27885
|
+
side: sideOf(b),
|
|
27886
|
+
span: Math.abs(node.get(b.src).rank - node.get(b.tgt).rank)
|
|
27887
|
+
})).sort((a, b) => a.side - b.side || a.span - b.span);
|
|
27888
|
+
const sideCounts = /* @__PURE__ */ new Map();
|
|
27889
|
+
for (const b of backSorted)
|
|
27890
|
+
sideCounts.set(b.side, (sideCounts.get(b.side) ?? 0) + 1);
|
|
27891
|
+
const laneCounter = /* @__PURE__ */ new Map();
|
|
27892
|
+
const backPoints = /* @__PURE__ */ new Map();
|
|
27893
|
+
const faceLim = (depth) => Math.max(0, depth / 2 - 6);
|
|
27894
|
+
const hubIndex = /* @__PURE__ */ new Map();
|
|
27895
|
+
const hubSize = /* @__PURE__ */ new Map();
|
|
27896
|
+
{
|
|
27897
|
+
const counter = /* @__PURE__ */ new Map();
|
|
27898
|
+
for (const b of backSorted) {
|
|
27899
|
+
const key = `${b.side}|${b.tgt}`;
|
|
27900
|
+
const idx = counter.get(key) ?? 0;
|
|
27901
|
+
counter.set(key, idx + 1);
|
|
27902
|
+
hubIndex.set(b.edgeIdx, idx);
|
|
27903
|
+
}
|
|
27904
|
+
for (const [key, v] of counter) hubSize.set(key, v);
|
|
27905
|
+
}
|
|
27906
|
+
for (const b of backSorted) {
|
|
27907
|
+
const s = b.side;
|
|
27908
|
+
const k = laneCounter.get(s) ?? 0;
|
|
27909
|
+
laneCounter.set(s, k + 1);
|
|
27910
|
+
const K = sideCounts.get(s);
|
|
27911
|
+
const src = node.get(b.src);
|
|
27912
|
+
const tgt = node.get(b.tgt);
|
|
27913
|
+
const laneC = s > 0 ? maxCross + GAP + k * LANE : minCross - GAP - k * LANE;
|
|
27914
|
+
const srcCtr = bandCenter[src.rank];
|
|
27915
|
+
const tgtCtr = bandCenter[tgt.rank];
|
|
27916
|
+
const frac = K > 1 ? (k + 1) / (K + 1) : 0;
|
|
27917
|
+
const srcR = srcCtr + frac * faceLim(src.depth);
|
|
27918
|
+
const tgtR = tgtCtr - frac * faceLim(tgt.depth);
|
|
27919
|
+
const m = (c, r) => isTB ? { x: c, y: r } : { x: r, y: c };
|
|
27920
|
+
const off = 10;
|
|
27921
|
+
const srcEdgeC = src.cross + s * (src.thick / 2 + off);
|
|
27922
|
+
const tgtEdgeC = tgt.cross + s * (tgt.thick / 2 + off);
|
|
27923
|
+
const hubK = hubIndex.get(b.edgeIdx);
|
|
27924
|
+
const hubN = hubSize.get(`${s}|${b.tgt}`);
|
|
27925
|
+
if (hubN > 1 && hubK > 0) {
|
|
27926
|
+
const tgtTop = tgtCtr - (tgt.depth / 2 + GAP + (hubK - 1) * LANE);
|
|
27927
|
+
const entryCross = tgt.cross + s * faceLim(tgt.thick) * (hubK / hubN);
|
|
27928
|
+
backPoints.set(b.edgeIdx, [
|
|
27929
|
+
m(src.cross, srcCtr),
|
|
27930
|
+
m(srcEdgeC, srcR),
|
|
27931
|
+
m(laneC, srcR),
|
|
27932
|
+
m(laneC, tgtTop),
|
|
27933
|
+
m(entryCross, tgtTop),
|
|
27934
|
+
m(tgt.cross, tgtCtr)
|
|
27935
|
+
]);
|
|
27936
|
+
} else {
|
|
27937
|
+
backPoints.set(b.edgeIdx, [
|
|
27938
|
+
m(src.cross, srcCtr),
|
|
27939
|
+
m(srcEdgeC, srcR),
|
|
27940
|
+
m(laneC, srcR),
|
|
27941
|
+
m(laneC, tgtR),
|
|
27942
|
+
m(tgtEdgeC, tgtR),
|
|
27943
|
+
m(tgt.cross, tgtCtr)
|
|
27944
|
+
]);
|
|
27945
|
+
}
|
|
27946
|
+
}
|
|
27947
|
+
const layoutEdges = [];
|
|
27948
|
+
for (let i = 0; i < parsed.edges.length; i++) {
|
|
27949
|
+
const e = parsed.edges[i];
|
|
27950
|
+
if (!labelSet.has(e.source) || !labelSet.has(e.target)) continue;
|
|
27951
|
+
let points;
|
|
27952
|
+
if (e.source === e.target) {
|
|
27953
|
+
const c = coord(node.get(e.source));
|
|
27954
|
+
points = [c, c];
|
|
27955
|
+
} else if (backPoints.has(i)) {
|
|
27956
|
+
points = backPoints.get(i);
|
|
27957
|
+
} else {
|
|
27958
|
+
const chain = chainById.get(i);
|
|
27959
|
+
if (!chain) continue;
|
|
27960
|
+
points = chain.map((id) => coord(node.get(id)));
|
|
27961
|
+
}
|
|
27962
|
+
layoutEdges.push({
|
|
27963
|
+
source: e.source,
|
|
27964
|
+
target: e.target,
|
|
27965
|
+
...e.label !== void 0 && { label: e.label },
|
|
27966
|
+
bidirectional: e.bidirectional,
|
|
27967
|
+
lineNumber: e.lineNumber,
|
|
27968
|
+
points,
|
|
27969
|
+
yOffset: 0,
|
|
27970
|
+
parallelCount: 1,
|
|
27971
|
+
metadata: e.metadata
|
|
27972
|
+
});
|
|
27973
|
+
}
|
|
27974
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
27975
|
+
for (const n of layoutNodes) {
|
|
27976
|
+
minX = Math.min(minX, n.x - n.width / 2);
|
|
27977
|
+
minY = Math.min(minY, n.y - n.height / 2);
|
|
27978
|
+
maxX = Math.max(maxX, n.x + n.width / 2);
|
|
27979
|
+
maxY = Math.max(maxY, n.y + n.height / 2);
|
|
27980
|
+
}
|
|
27981
|
+
for (const e of layoutEdges)
|
|
27982
|
+
for (const p of e.points) {
|
|
27983
|
+
minX = Math.min(minX, p.x);
|
|
27984
|
+
minY = Math.min(minY, p.y);
|
|
27985
|
+
maxX = Math.max(maxX, p.x);
|
|
27986
|
+
maxY = Math.max(maxY, p.y);
|
|
27987
|
+
}
|
|
27988
|
+
const sx = MARGIN3 - minX;
|
|
27989
|
+
const sy = MARGIN3 - minY;
|
|
27990
|
+
const shifted = sx !== 0 || sy !== 0 ? {
|
|
27991
|
+
nodes: layoutNodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })),
|
|
27992
|
+
edges: layoutEdges.map((e) => ({
|
|
27993
|
+
...e,
|
|
27994
|
+
points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
|
|
27995
|
+
}))
|
|
27996
|
+
} : { nodes: layoutNodes, edges: layoutEdges };
|
|
27997
|
+
return {
|
|
27998
|
+
nodes: shifted.nodes,
|
|
27999
|
+
edges: shifted.edges,
|
|
28000
|
+
groups: [],
|
|
28001
|
+
width: maxX + sx + MARGIN3,
|
|
28002
|
+
height: maxY + sy + MARGIN3
|
|
28003
|
+
};
|
|
28004
|
+
}
|
|
28005
|
+
function weight(a, b) {
|
|
28006
|
+
if (a.dummy && b.dummy) return W_DUMMY_DUMMY;
|
|
28007
|
+
if (a.dummy || b.dummy) return W_REAL_DUMMY;
|
|
28008
|
+
return W_REAL_REAL;
|
|
28009
|
+
}
|
|
28010
|
+
function placeWithSeparation(ids, desired) {
|
|
28011
|
+
const n = ids.length;
|
|
28012
|
+
if (n === 0) return;
|
|
28013
|
+
const half = ids.map((id) => node.get(id).thick / 2);
|
|
28014
|
+
const off = [0];
|
|
28015
|
+
for (let i = 1; i < n; i++)
|
|
28016
|
+
off[i] = off[i - 1] + half[i - 1] + nodesep + half[i];
|
|
28017
|
+
const d = desired.map((v, i) => v - off[i]);
|
|
28018
|
+
const blocks = [];
|
|
28019
|
+
for (let i = 0; i < n; i++) {
|
|
28020
|
+
let b = { pos: d[i], count: 1, sumOffset: d[i], first: i };
|
|
28021
|
+
while (blocks.length && blocks[blocks.length - 1].pos >= b.pos) {
|
|
28022
|
+
const prev = blocks.pop();
|
|
28023
|
+
const count = prev.count + b.count;
|
|
28024
|
+
const sumOffset = prev.sumOffset + b.sumOffset;
|
|
28025
|
+
b = { pos: sumOffset / count, count, sumOffset, first: prev.first };
|
|
28026
|
+
}
|
|
28027
|
+
blocks.push(b);
|
|
28028
|
+
}
|
|
28029
|
+
const xs = new Array(n);
|
|
28030
|
+
for (const b of blocks)
|
|
28031
|
+
for (let k = 0; k < b.count; k++) xs[b.first + k] = b.pos;
|
|
28032
|
+
for (let i = 0; i < n; i++) node.get(ids[i]).cross = xs[i] + off[i];
|
|
28033
|
+
}
|
|
28034
|
+
}
|
|
28035
|
+
var NODESEP, RANKSEP, DUMMY_THICK, MARGIN3, W_REAL_REAL, W_REAL_DUMMY, W_DUMMY_DUMMY;
|
|
28036
|
+
var init_layout_layered = __esm({
|
|
28037
|
+
"src/boxes-and-lines/layout-layered.ts"() {
|
|
28038
|
+
"use strict";
|
|
28039
|
+
init_layout5();
|
|
28040
|
+
NODESEP = 50;
|
|
28041
|
+
RANKSEP = 60;
|
|
28042
|
+
DUMMY_THICK = 10;
|
|
28043
|
+
MARGIN3 = 40;
|
|
28044
|
+
W_REAL_REAL = 1;
|
|
28045
|
+
W_REAL_DUMMY = 2;
|
|
28046
|
+
W_DUMMY_DUMMY = 8;
|
|
28047
|
+
}
|
|
28048
|
+
});
|
|
28049
|
+
|
|
28050
|
+
// src/boxes-and-lines/layout-search.ts
|
|
28051
|
+
var layout_search_exports = {};
|
|
28052
|
+
__export(layout_search_exports, {
|
|
28053
|
+
countEdgeNearMiss: () => countEdgeNearMiss,
|
|
28054
|
+
countEdgeNodePierces: () => countEdgeNodePierces,
|
|
28055
|
+
countEdgeOverlaps: () => countEdgeOverlaps,
|
|
28056
|
+
countGroupOverlaps: () => countGroupOverlaps,
|
|
28057
|
+
countSplineCrossings: () => countSplineCrossings,
|
|
28058
|
+
deroutePierces: () => deroutePierces,
|
|
28059
|
+
detectEdgeNodePierces: () => detectEdgeNodePierces,
|
|
28060
|
+
detectEdgeOverlaps: () => detectEdgeOverlaps,
|
|
28061
|
+
detectGroupOverlaps: () => detectGroupOverlaps,
|
|
28062
|
+
layoutBoxesAndLinesSearch: () => layoutBoxesAndLinesSearch,
|
|
28063
|
+
separateGroupBands: () => separateGroupBands
|
|
28064
|
+
});
|
|
28065
|
+
import dagre4 from "@dagrejs/dagre";
|
|
28066
|
+
import { line as d3line, curveBasis as curveBasis5 } from "d3-shape";
|
|
28067
|
+
function rng2(s) {
|
|
28068
|
+
return () => {
|
|
28069
|
+
s |= 0;
|
|
28070
|
+
s = s + 1831565813 | 0;
|
|
28071
|
+
let t = Math.imul(s ^ s >>> 15, 1 | s);
|
|
28072
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
28073
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
28074
|
+
};
|
|
28075
|
+
}
|
|
28076
|
+
function shuffle(a, r) {
|
|
28077
|
+
const x = a.slice();
|
|
28078
|
+
for (let i = x.length - 1; i > 0; i--) {
|
|
28079
|
+
const j = Math.floor(r() * (i + 1));
|
|
28080
|
+
[x[i], x[j]] = [x[j], x[i]];
|
|
28081
|
+
}
|
|
28082
|
+
return x;
|
|
28083
|
+
}
|
|
28084
|
+
function flatten(d) {
|
|
28085
|
+
const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
|
|
28086
|
+
const pts = [];
|
|
28087
|
+
let i = 0, cx = 0, cy = 0, cmd = "";
|
|
28088
|
+
const num = () => parseFloat(toks[i++]);
|
|
28089
|
+
const samp = (p0, c1, c2, p1) => {
|
|
28090
|
+
for (let t = 0; t <= 1; t += 0.12) {
|
|
28091
|
+
const u = 1 - t;
|
|
28092
|
+
if (c2)
|
|
28093
|
+
pts.push({
|
|
28094
|
+
x: u * u * u * p0.x + 3 * u * u * t * c1.x + 3 * u * t * t * c2.x + t * t * t * p1.x,
|
|
28095
|
+
y: u * u * u * p0.y + 3 * u * u * t * c1.y + 3 * u * t * t * c2.y + t * t * t * p1.y
|
|
28096
|
+
});
|
|
28097
|
+
else
|
|
28098
|
+
pts.push({
|
|
28099
|
+
x: u * u * p0.x + 2 * u * t * c1.x + t * t * p1.x,
|
|
28100
|
+
y: u * u * p0.y + 2 * u * t * c1.y + t * t * p1.y
|
|
28101
|
+
});
|
|
28102
|
+
}
|
|
28103
|
+
};
|
|
28104
|
+
while (i < toks.length) {
|
|
28105
|
+
const tk = toks[i];
|
|
28106
|
+
if (/[MLQC]/i.test(tk)) {
|
|
28107
|
+
cmd = tk;
|
|
28108
|
+
i++;
|
|
28109
|
+
}
|
|
28110
|
+
if (cmd === "M" || cmd === "L") {
|
|
28111
|
+
const x = num(), y = num();
|
|
28112
|
+
pts.push({ x, y });
|
|
28113
|
+
cx = x;
|
|
28114
|
+
cy = y;
|
|
28115
|
+
} else if (cmd === "Q") {
|
|
28116
|
+
const c1 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
|
|
28117
|
+
samp({ x: cx, y: cy }, c1, null, p1);
|
|
28118
|
+
cx = p1.x;
|
|
28119
|
+
cy = p1.y;
|
|
28120
|
+
} else if (cmd === "C") {
|
|
28121
|
+
const c1 = { x: num(), y: num() }, c2 = { x: num(), y: num() }, p1 = { x: num(), y: num() };
|
|
28122
|
+
samp({ x: cx, y: cy }, c1, c2, p1);
|
|
28123
|
+
cx = p1.x;
|
|
28124
|
+
cy = p1.y;
|
|
28125
|
+
} else i++;
|
|
28126
|
+
}
|
|
28127
|
+
return pts;
|
|
28128
|
+
}
|
|
28129
|
+
function segPoint(p1, p2, p3, p4) {
|
|
28130
|
+
const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
|
|
28131
|
+
if (Math.abs(den) < 1e-9) return null;
|
|
28132
|
+
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;
|
|
28133
|
+
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;
|
|
28134
|
+
}
|
|
28135
|
+
function countSplineCrossings(layout) {
|
|
28136
|
+
const center = /* @__PURE__ */ new Map();
|
|
28137
|
+
for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
|
|
28138
|
+
for (const g of layout.groups)
|
|
28139
|
+
if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
|
|
28140
|
+
const polys = layout.edges.map((e) => {
|
|
28141
|
+
const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
|
|
28142
|
+
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
28143
|
+
for (const p of pts) {
|
|
28144
|
+
if (p.x < x0) x0 = p.x;
|
|
28145
|
+
if (p.x > x1) x1 = p.x;
|
|
28146
|
+
if (p.y < y0) y0 = p.y;
|
|
28147
|
+
if (p.y > y1) y1 = p.y;
|
|
28148
|
+
}
|
|
28149
|
+
return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
|
|
28150
|
+
});
|
|
28151
|
+
const R = 34;
|
|
28152
|
+
let total = 0;
|
|
28153
|
+
for (let a = 0; a < polys.length; a++)
|
|
28154
|
+
for (let b = a + 1; b < polys.length; b++) {
|
|
28155
|
+
const A = polys[a], B = polys[b];
|
|
28156
|
+
if (A.pts.length < 2 || B.pts.length < 2) continue;
|
|
28157
|
+
if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
|
|
28158
|
+
const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
|
|
28159
|
+
const hits = [];
|
|
28160
|
+
for (let i = 1; i < A.pts.length; i++)
|
|
28161
|
+
for (let j = 1; j < B.pts.length; j++) {
|
|
28162
|
+
const p = segPoint(
|
|
28163
|
+
A.pts[i - 1],
|
|
28164
|
+
A.pts[i],
|
|
28165
|
+
B.pts[j - 1],
|
|
28166
|
+
B.pts[j]
|
|
28167
|
+
);
|
|
28168
|
+
if (!p) continue;
|
|
28169
|
+
if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
|
|
28170
|
+
continue;
|
|
28171
|
+
if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
|
|
28172
|
+
hits.push(p);
|
|
28173
|
+
}
|
|
28174
|
+
total += hits.length;
|
|
28175
|
+
}
|
|
28176
|
+
return total;
|
|
28177
|
+
}
|
|
28178
|
+
function pointSegDist(p, a, b) {
|
|
28179
|
+
const dx = b.x - a.x, dy = b.y - a.y;
|
|
28180
|
+
const len2 = dx * dx + dy * dy;
|
|
28181
|
+
if (len2 < 1e-9) return Math.hypot(p.x - a.x, p.y - a.y);
|
|
28182
|
+
let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
|
|
28183
|
+
t = Math.max(0, Math.min(1, t));
|
|
28184
|
+
return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));
|
|
28185
|
+
}
|
|
28186
|
+
function distToPoly(p, poly) {
|
|
28187
|
+
let m = Infinity;
|
|
28188
|
+
for (let i = 1; i < poly.length; i++)
|
|
28189
|
+
m = Math.min(m, pointSegDist(p, poly[i - 1], poly[i]));
|
|
28190
|
+
return m;
|
|
28191
|
+
}
|
|
28192
|
+
function pointRectDist(p, r) {
|
|
28193
|
+
const dx = Math.max(r.x - r.w / 2 - p.x, 0, p.x - (r.x + r.w / 2));
|
|
28194
|
+
const dy = Math.max(r.y - r.h / 2 - p.y, 0, p.y - (r.y + r.h / 2));
|
|
28195
|
+
return Math.hypot(dx, dy);
|
|
28196
|
+
}
|
|
28197
|
+
function detectEdgeOverlaps(layout, opts) {
|
|
28198
|
+
const dist = opts?.dist ?? 8;
|
|
28199
|
+
const minLen = opts?.minLen ?? 16;
|
|
28200
|
+
const nodeClear = opts?.nodeClear ?? 12;
|
|
28201
|
+
const rect = /* @__PURE__ */ new Map();
|
|
28202
|
+
for (const n of layout.nodes)
|
|
28203
|
+
rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
|
|
28204
|
+
for (const g of layout.groups)
|
|
28205
|
+
if (g.collapsed)
|
|
28206
|
+
rect.set("__group_" + g.label, {
|
|
28207
|
+
x: g.x,
|
|
28208
|
+
y: g.y,
|
|
28209
|
+
w: g.width,
|
|
28210
|
+
h: g.height
|
|
28211
|
+
});
|
|
28212
|
+
const polys = layout.edges.map((e) => {
|
|
28213
|
+
const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
|
|
28214
|
+
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
28215
|
+
for (const p of pts) {
|
|
28216
|
+
if (p.x < x0) x0 = p.x;
|
|
28217
|
+
if (p.x > x1) x1 = p.x;
|
|
28218
|
+
if (p.y < y0) y0 = p.y;
|
|
28219
|
+
if (p.y > y1) y1 = p.y;
|
|
28220
|
+
}
|
|
28221
|
+
return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
|
|
28222
|
+
});
|
|
28223
|
+
const runs = [];
|
|
28224
|
+
for (let a = 0; a < polys.length; a++)
|
|
28225
|
+
for (let b = a + 1; b < polys.length; b++) {
|
|
28226
|
+
const A = polys[a], B = polys[b];
|
|
28227
|
+
if (A.pts.length < 2 || B.pts.length < 2) continue;
|
|
28228
|
+
if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
|
|
28229
|
+
continue;
|
|
28230
|
+
const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
|
|
28231
|
+
let run2 = [];
|
|
28232
|
+
let runLen = 0;
|
|
28233
|
+
const flush = () => {
|
|
28234
|
+
if (runLen >= minLen && run2.length >= 2)
|
|
28235
|
+
runs.push({
|
|
28236
|
+
mid: run2[Math.floor(run2.length / 2)],
|
|
28237
|
+
length: runLen,
|
|
28238
|
+
pts: run2.slice()
|
|
28239
|
+
});
|
|
28240
|
+
run2 = [];
|
|
28241
|
+
runLen = 0;
|
|
28242
|
+
};
|
|
28243
|
+
for (const p of A.pts) {
|
|
28244
|
+
const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
|
|
28245
|
+
const covered = !nearShared && distToPoly(p, B.pts) < dist;
|
|
28246
|
+
if (covered) {
|
|
28247
|
+
if (run2.length)
|
|
28248
|
+
runLen += Math.hypot(
|
|
28249
|
+
p.x - run2[run2.length - 1].x,
|
|
28250
|
+
p.y - run2[run2.length - 1].y
|
|
28251
|
+
);
|
|
28252
|
+
run2.push(p);
|
|
28253
|
+
} else flush();
|
|
28254
|
+
}
|
|
28255
|
+
flush();
|
|
28256
|
+
}
|
|
28257
|
+
return runs;
|
|
28258
|
+
}
|
|
28259
|
+
function countEdgeOverlaps(layout, opts) {
|
|
28260
|
+
return detectEdgeOverlaps(layout, opts).length;
|
|
28261
|
+
}
|
|
28262
|
+
function detectEdgeNodePierces(layout, opts) {
|
|
28263
|
+
const inset = opts?.inset ?? 6;
|
|
28264
|
+
const minPts = opts?.minPts ?? 2;
|
|
28265
|
+
const rects = [];
|
|
28266
|
+
for (const n of layout.nodes)
|
|
28267
|
+
rects.push({ key: n.label, x: n.x, y: n.y, w: n.width, h: n.height });
|
|
28268
|
+
for (const g of layout.groups)
|
|
28269
|
+
if (g.collapsed)
|
|
28270
|
+
rects.push({
|
|
28271
|
+
key: "__group_" + g.label,
|
|
28272
|
+
x: g.x,
|
|
28273
|
+
y: g.y,
|
|
28274
|
+
w: g.width,
|
|
28275
|
+
h: g.height
|
|
28276
|
+
});
|
|
28277
|
+
const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
|
|
28278
|
+
const out = [];
|
|
28279
|
+
layout.edges.forEach((e, idx) => {
|
|
28280
|
+
if (e.points.length < 2) return;
|
|
28281
|
+
const poly = flatten(splineGen(e.points) ?? "");
|
|
28282
|
+
for (const r of rects) {
|
|
28283
|
+
if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
|
|
28284
|
+
continue;
|
|
28285
|
+
const hits = poly.filter((p) => inside(p, r));
|
|
28286
|
+
if (hits.length >= minPts)
|
|
28287
|
+
out.push({ edgeIdx: idx, node: r.key, pts: hits });
|
|
28288
|
+
}
|
|
28289
|
+
});
|
|
28290
|
+
return out;
|
|
28291
|
+
}
|
|
28292
|
+
function countEdgeNodePierces(layout, opts) {
|
|
28293
|
+
return detectEdgeNodePierces(layout, opts).length;
|
|
28294
|
+
}
|
|
28295
|
+
function deroutePierces(layout) {
|
|
28296
|
+
const pierces = detectEdgeNodePierces(layout);
|
|
28297
|
+
if (!pierces.length) return layout;
|
|
28298
|
+
const rect = /* @__PURE__ */ new Map();
|
|
28299
|
+
for (const n of layout.nodes)
|
|
28300
|
+
rect.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
|
|
28301
|
+
for (const g of layout.groups)
|
|
28302
|
+
if (g.collapsed)
|
|
28303
|
+
rect.set("__group_" + g.label, {
|
|
28304
|
+
x: g.x,
|
|
28305
|
+
y: g.y,
|
|
28306
|
+
w: g.width,
|
|
28307
|
+
h: g.height
|
|
28308
|
+
});
|
|
28309
|
+
const byEdge = /* @__PURE__ */ new Map();
|
|
28310
|
+
for (const p of pierces) {
|
|
28311
|
+
const arr = byEdge.get(p.edgeIdx);
|
|
28312
|
+
if (arr) arr.push(p.node);
|
|
28313
|
+
else byEdge.set(p.edgeIdx, [p.node]);
|
|
28314
|
+
}
|
|
28315
|
+
const edges = layout.edges.map((e, idx) => {
|
|
28316
|
+
const nodes = byEdge.get(idx);
|
|
28317
|
+
if (!nodes) return e;
|
|
28318
|
+
let pts = e.points.map((p) => ({ x: p.x, y: p.y }));
|
|
28319
|
+
for (const label of nodes) {
|
|
28320
|
+
const r = rect.get(label);
|
|
28321
|
+
if (r) pts = detourAround(pts, r);
|
|
28322
|
+
}
|
|
28323
|
+
return { ...e, points: pts };
|
|
28324
|
+
});
|
|
28325
|
+
return { ...layout, edges };
|
|
28326
|
+
}
|
|
28327
|
+
function detourAround(pts, r) {
|
|
28328
|
+
if (pts.length < 2) return pts;
|
|
28329
|
+
const c = { x: r.x, y: r.y };
|
|
28330
|
+
let bestSeg = -1;
|
|
28331
|
+
let bestT = 0;
|
|
28332
|
+
let bestD = Infinity;
|
|
28333
|
+
let bestPt = pts[0];
|
|
28334
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
28335
|
+
const a2 = pts[i], b2 = pts[i + 1];
|
|
28336
|
+
const dx = b2.x - a2.x, dy = b2.y - a2.y;
|
|
28337
|
+
const len2 = dx * dx + dy * dy || 1e-9;
|
|
28338
|
+
let t = ((c.x - a2.x) * dx + (c.y - a2.y) * dy) / len2;
|
|
28339
|
+
t = Math.max(0, Math.min(1, t));
|
|
28340
|
+
const q = { x: a2.x + t * dx, y: a2.y + t * dy };
|
|
28341
|
+
const d = Math.hypot(q.x - c.x, q.y - c.y);
|
|
28342
|
+
if (d < bestD) {
|
|
28343
|
+
bestD = d;
|
|
28344
|
+
bestSeg = i;
|
|
28345
|
+
bestT = t;
|
|
28346
|
+
bestPt = q;
|
|
28347
|
+
}
|
|
28348
|
+
}
|
|
28349
|
+
if (bestSeg < 0) return pts;
|
|
28350
|
+
let nx = bestPt.x - c.x;
|
|
28351
|
+
let ny = bestPt.y - c.y;
|
|
28352
|
+
if (Math.hypot(nx, ny) < 1) {
|
|
28353
|
+
if (r.w <= r.h) {
|
|
28354
|
+
nx = 1;
|
|
28355
|
+
ny = 0;
|
|
28356
|
+
} else {
|
|
28357
|
+
nx = 0;
|
|
28358
|
+
ny = 1;
|
|
28359
|
+
}
|
|
28360
|
+
}
|
|
28361
|
+
const nlen = Math.hypot(nx, ny) || 1;
|
|
28362
|
+
nx /= nlen;
|
|
28363
|
+
ny /= nlen;
|
|
28364
|
+
const a = pts[bestSeg], b = pts[bestSeg + 1];
|
|
28365
|
+
let ex = b.x - a.x, ey = b.y - a.y;
|
|
28366
|
+
const elen = Math.hypot(ex, ey) || 1;
|
|
28367
|
+
ex /= elen;
|
|
28368
|
+
ey /= elen;
|
|
28369
|
+
const clear = Math.hypot(r.w, r.h) / 2 + 18;
|
|
28370
|
+
const hw = Math.hypot(r.w, r.h) / 2;
|
|
28371
|
+
void bestT;
|
|
28372
|
+
const before = {
|
|
28373
|
+
x: bestPt.x - ex * hw + nx * clear * 0.5,
|
|
28374
|
+
y: bestPt.y - ey * hw + ny * clear * 0.5
|
|
28375
|
+
};
|
|
28376
|
+
const peak = { x: c.x + nx * clear, y: c.y + ny * clear };
|
|
28377
|
+
const after = {
|
|
28378
|
+
x: bestPt.x + ex * hw + nx * clear * 0.5,
|
|
28379
|
+
y: bestPt.y + ey * hw + ny * clear * 0.5
|
|
28380
|
+
};
|
|
28381
|
+
const out = pts.slice(0, bestSeg + 1);
|
|
28382
|
+
out.push(before, peak, after);
|
|
28383
|
+
out.push(...pts.slice(bestSeg + 1));
|
|
28384
|
+
return out;
|
|
28385
|
+
}
|
|
28386
|
+
function countEdgeNearMiss(layout, opts) {
|
|
28387
|
+
const near = opts?.near ?? 14;
|
|
28388
|
+
const tight = opts?.tight ?? 8;
|
|
28389
|
+
const minLen = opts?.minLen ?? 18;
|
|
28390
|
+
const close = detectEdgeOverlaps(layout, { dist: near, minLen }).length;
|
|
28391
|
+
const over = detectEdgeOverlaps(layout, { dist: tight, minLen }).length;
|
|
28392
|
+
return Math.max(0, close - over);
|
|
28393
|
+
}
|
|
28394
|
+
function detectGroupOverlaps(layout, opts) {
|
|
28395
|
+
const margin = opts?.margin ?? 4;
|
|
28396
|
+
const raw = layout.groups.map((g) => ({
|
|
28397
|
+
label: g.label,
|
|
28398
|
+
l: g.x - g.width / 2,
|
|
28399
|
+
r: g.x + g.width / 2,
|
|
28400
|
+
t: g.y - g.height / 2,
|
|
28401
|
+
b: g.y + g.height / 2
|
|
28402
|
+
}));
|
|
28403
|
+
const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
|
|
28404
|
+
const rend = raw.map((a, i) => {
|
|
28405
|
+
const parent = raw.some((b, j) => j !== i && contains(a, b));
|
|
28406
|
+
return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
|
|
28407
|
+
});
|
|
28408
|
+
const hit = /* @__PURE__ */ new Set();
|
|
28409
|
+
for (let i = 0; i < raw.length; i++)
|
|
28410
|
+
for (let j = i + 1; j < raw.length; j++) {
|
|
28411
|
+
if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
|
|
28412
|
+
const a = rend[i], b = rend[j];
|
|
28413
|
+
const dx = Math.max(a.l - b.r, b.l - a.r);
|
|
28414
|
+
const dy = Math.max(a.t - b.b, b.t - a.b);
|
|
28415
|
+
if (Math.max(dx, dy) < margin) {
|
|
28416
|
+
hit.add(raw[i].label);
|
|
28417
|
+
hit.add(raw[j].label);
|
|
28418
|
+
}
|
|
28419
|
+
}
|
|
28420
|
+
return [...hit];
|
|
28421
|
+
}
|
|
28422
|
+
function countGroupOverlaps(layout, opts) {
|
|
28423
|
+
const margin = opts?.margin ?? 4;
|
|
28424
|
+
const raw = layout.groups.map((g) => ({
|
|
28425
|
+
l: g.x - g.width / 2,
|
|
28426
|
+
r: g.x + g.width / 2,
|
|
28427
|
+
t: g.y - g.height / 2,
|
|
28428
|
+
b: g.y + g.height / 2
|
|
28429
|
+
}));
|
|
28430
|
+
const contains = (outer, inner) => outer.l <= inner.l + margin && outer.r >= inner.r - margin && outer.t <= inner.t + margin && outer.b >= inner.b - margin;
|
|
28431
|
+
const rend = raw.map((a, i) => {
|
|
28432
|
+
const parent = raw.some((b, j) => j !== i && contains(a, b));
|
|
28433
|
+
return parent ? { ...a, t: a.t - GROUP_LABEL_ZONE2 } : a;
|
|
28434
|
+
});
|
|
28435
|
+
let count = 0;
|
|
28436
|
+
for (let i = 0; i < raw.length; i++)
|
|
28437
|
+
for (let j = i + 1; j < raw.length; j++) {
|
|
28438
|
+
if (contains(raw[i], raw[j]) || contains(raw[j], raw[i])) continue;
|
|
28439
|
+
const a = rend[i], b = rend[j];
|
|
28440
|
+
const dx = Math.max(a.l - b.r, b.l - a.r);
|
|
28441
|
+
const dy = Math.max(a.t - b.b, b.t - a.b);
|
|
28442
|
+
if (Math.max(dx, dy) < margin) count++;
|
|
28443
|
+
}
|
|
28444
|
+
return count;
|
|
28445
|
+
}
|
|
28446
|
+
function separateGroupBands(layout, parsed) {
|
|
28447
|
+
if (layout.groups.some((g) => g.collapsed)) return layout;
|
|
28448
|
+
if (countGroupOverlaps(layout) === 0) return layout;
|
|
28449
|
+
const parentOf = /* @__PURE__ */ new Map();
|
|
28450
|
+
for (const g of parsed.groups) parentOf.set(g.label, g.parentGroup);
|
|
28451
|
+
const topOf = (label) => {
|
|
28452
|
+
let cur = label;
|
|
28453
|
+
const seen = /* @__PURE__ */ new Set();
|
|
28454
|
+
for (; ; ) {
|
|
28455
|
+
const p = parentOf.get(cur);
|
|
28456
|
+
if (!p || seen.has(p)) return cur;
|
|
28457
|
+
seen.add(cur);
|
|
28458
|
+
cur = p;
|
|
28459
|
+
}
|
|
28460
|
+
};
|
|
28461
|
+
const topLabels = parsed.groups.filter((g) => !g.parentGroup).map((g) => g.label);
|
|
28462
|
+
if (topLabels.length < 2) return layout;
|
|
28463
|
+
const nodeTop = /* @__PURE__ */ new Map();
|
|
28464
|
+
for (const g of parsed.groups)
|
|
28465
|
+
for (const child of g.children)
|
|
28466
|
+
if (!parsed.groups.some((gg) => gg.label === child))
|
|
28467
|
+
nodeTop.set(child, topOf(g.label));
|
|
28468
|
+
const axis = parsed.direction === "TB" ? "x" : "y";
|
|
28469
|
+
const boxByLabel = new Map(layout.groups.map((g) => [g.label, g]));
|
|
28470
|
+
const bands = topLabels.map((label) => {
|
|
28471
|
+
const g = boxByLabel.get(label);
|
|
28472
|
+
const half2 = (axis === "y" ? g.height : g.width) / 2;
|
|
28473
|
+
let lo = (axis === "y" ? g.y : g.x) - half2;
|
|
28474
|
+
const hi = (axis === "y" ? g.y : g.x) + half2;
|
|
28475
|
+
const isParent = parsed.groups.some((c) => c.parentGroup === label);
|
|
28476
|
+
if (axis === "y" && isParent) lo -= GROUP_LABEL_ZONE2;
|
|
28477
|
+
return { label, lo, hi, c: (lo + hi) / 2 };
|
|
28478
|
+
});
|
|
28479
|
+
bands.sort((a, b) => a.c - b.c);
|
|
28480
|
+
const GAP = 16;
|
|
28481
|
+
const half = bands.map((b) => (b.hi - b.lo) / 2);
|
|
28482
|
+
const off = [0];
|
|
28483
|
+
for (let i = 1; i < bands.length; i++)
|
|
28484
|
+
off[i] = off[i - 1] + half[i - 1] + GAP + half[i];
|
|
28485
|
+
const desired = bands.map((b, i) => b.c - off[i]);
|
|
28486
|
+
const blocks = [];
|
|
28487
|
+
for (let i = 0; i < bands.length; i++) {
|
|
28488
|
+
let blk = { pos: desired[i], count: 1, sum: desired[i], first: i };
|
|
28489
|
+
while (blocks.length && blocks[blocks.length - 1].pos >= blk.pos) {
|
|
28490
|
+
const prev = blocks.pop();
|
|
28491
|
+
const count = prev.count + blk.count;
|
|
28492
|
+
const sum = prev.sum + blk.sum;
|
|
28493
|
+
blk = { pos: sum / count, count, sum, first: prev.first };
|
|
28494
|
+
}
|
|
28495
|
+
blocks.push(blk);
|
|
28496
|
+
}
|
|
28497
|
+
const newC = new Array(bands.length);
|
|
28498
|
+
for (const blk of blocks)
|
|
28499
|
+
for (let k = 0; k < blk.count; k++)
|
|
28500
|
+
newC[blk.first + k] = blk.pos + off[blk.first + k];
|
|
28501
|
+
const delta = /* @__PURE__ */ new Map();
|
|
28502
|
+
let moved = false;
|
|
28503
|
+
bands.forEach((b, i) => {
|
|
28504
|
+
const d = newC[i] - b.c;
|
|
28505
|
+
delta.set(b.label, d);
|
|
28506
|
+
if (Math.abs(d) > 0.5) moved = true;
|
|
28507
|
+
});
|
|
28508
|
+
if (!moved) return layout;
|
|
28509
|
+
const nodeDelta = (label) => {
|
|
28510
|
+
const top = nodeTop.get(label);
|
|
28511
|
+
return top ? delta.get(top) ?? 0 : 0;
|
|
28512
|
+
};
|
|
28513
|
+
const shift = (p, d) => axis === "y" ? { x: p.x, y: p.y + d } : { x: p.x + d, y: p.y };
|
|
28514
|
+
const nodes = layout.nodes.map((n) => {
|
|
28515
|
+
const d = nodeDelta(n.label);
|
|
28516
|
+
return d ? { ...n, ...axis === "y" ? { y: n.y + d } : { x: n.x + d } } : n;
|
|
28517
|
+
});
|
|
28518
|
+
const groups = layout.groups.map((g) => {
|
|
28519
|
+
const d = delta.get(topOf(g.label)) ?? 0;
|
|
28520
|
+
return d ? { ...g, ...axis === "y" ? { y: g.y + d } : { x: g.x + d } } : g;
|
|
28521
|
+
});
|
|
28522
|
+
const edges = layout.edges.map((e) => {
|
|
28523
|
+
const ds = nodeDelta(e.source);
|
|
28524
|
+
const dt = nodeDelta(e.target);
|
|
28525
|
+
if (!ds && !dt) return e;
|
|
28526
|
+
const N = e.points.length;
|
|
28527
|
+
const points = e.points.map((p, i) => {
|
|
28528
|
+
const f = N > 1 ? i / (N - 1) : 0;
|
|
28529
|
+
return shift(p, ds * (1 - f) + dt * f);
|
|
28530
|
+
});
|
|
28531
|
+
return { ...e, points };
|
|
28532
|
+
});
|
|
28533
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
28534
|
+
const acc = (x, y) => {
|
|
28535
|
+
if (x < minX) minX = x;
|
|
28536
|
+
if (x > maxX) maxX = x;
|
|
28537
|
+
if (y < minY) minY = y;
|
|
28538
|
+
if (y > maxY) maxY = y;
|
|
28539
|
+
};
|
|
28540
|
+
for (const n of nodes) {
|
|
28541
|
+
acc(n.x - n.width / 2, n.y - n.height / 2);
|
|
28542
|
+
acc(n.x + n.width / 2, n.y + n.height / 2);
|
|
28543
|
+
}
|
|
28544
|
+
for (const g of groups) {
|
|
28545
|
+
acc(g.x - g.width / 2, g.y - g.height / 2 - GROUP_LABEL_ZONE2);
|
|
28546
|
+
acc(g.x + g.width / 2, g.y + g.height / 2);
|
|
28547
|
+
}
|
|
28548
|
+
for (const e of edges) for (const p of e.points) acc(p.x, p.y);
|
|
28549
|
+
const M = 40;
|
|
28550
|
+
const sx = M - minX, sy = M - minY;
|
|
28551
|
+
const reshift = sx !== 0 || sy !== 0;
|
|
28552
|
+
return {
|
|
28553
|
+
nodes: reshift ? nodes.map((n) => ({ ...n, x: n.x + sx, y: n.y + sy })) : nodes,
|
|
28554
|
+
groups: reshift ? groups.map((g) => ({ ...g, x: g.x + sx, y: g.y + sy })) : groups,
|
|
28555
|
+
edges: reshift ? edges.map((e) => ({
|
|
28556
|
+
...e,
|
|
28557
|
+
points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
|
|
28558
|
+
})) : edges,
|
|
28559
|
+
width: maxX - minX + 2 * M,
|
|
28560
|
+
height: maxY - minY + 2 * M
|
|
28561
|
+
};
|
|
28562
|
+
}
|
|
28563
|
+
function segCross(p1, p2, p3, p4) {
|
|
28564
|
+
const d1x = p2.x - p1.x, d1y = p2.y - p1.y, d2x = p4.x - p3.x, d2y = p4.y - p3.y;
|
|
28565
|
+
const den = d1x * d2y - d1y * d2x;
|
|
28566
|
+
if (Math.abs(den) < 1e-9) return false;
|
|
28567
|
+
const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / den;
|
|
28568
|
+
const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / den;
|
|
28569
|
+
return t > 1e-3 && t < 0.999 && s > 1e-3 && s < 0.999;
|
|
28570
|
+
}
|
|
28571
|
+
function countCrossingsFast(layout) {
|
|
28572
|
+
const E = layout.edges.filter((e) => e.points.length >= 2);
|
|
28573
|
+
const bb = E.map((e) => {
|
|
28574
|
+
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
28575
|
+
for (const p of e.points) {
|
|
28576
|
+
if (p.x < x0) x0 = p.x;
|
|
28577
|
+
if (p.x > x1) x1 = p.x;
|
|
28578
|
+
if (p.y < y0) y0 = p.y;
|
|
28579
|
+
if (p.y > y1) y1 = p.y;
|
|
28580
|
+
}
|
|
28581
|
+
return { x0, y0, x1, y1 };
|
|
28582
|
+
});
|
|
28583
|
+
let count = 0;
|
|
28584
|
+
for (let i = 0; i < E.length; i++)
|
|
28585
|
+
for (let j = i + 1; j < E.length; j++) {
|
|
28586
|
+
const A = E[i], B = E[j];
|
|
28587
|
+
if (A.source === B.source || A.source === B.target || A.target === B.source || A.target === B.target)
|
|
28588
|
+
continue;
|
|
28589
|
+
const a = bb[i], b = bb[j];
|
|
28590
|
+
if (a.x1 < b.x0 || b.x1 < a.x0 || a.y1 < b.y0 || b.y1 < a.y0) continue;
|
|
28591
|
+
const pa = A.points, pb = B.points;
|
|
28592
|
+
let hit = false;
|
|
28593
|
+
for (let ai = 0; ai < pa.length - 1 && !hit; ai++)
|
|
28594
|
+
for (let bi = 0; bi < pb.length - 1; bi++) {
|
|
28595
|
+
if (segCross(pa[ai], pa[ai + 1], pb[bi], pb[bi + 1])) {
|
|
28596
|
+
hit = true;
|
|
28597
|
+
break;
|
|
28598
|
+
}
|
|
28599
|
+
}
|
|
28600
|
+
if (hit) count++;
|
|
28601
|
+
}
|
|
28602
|
+
return count;
|
|
28603
|
+
}
|
|
28604
|
+
function meanDrift(layout, prev) {
|
|
28605
|
+
if (!prev?.size) return 0;
|
|
28606
|
+
let sum = 0, n = 0;
|
|
28607
|
+
for (const node of layout.nodes) {
|
|
28608
|
+
const p = prev.get(node.label);
|
|
28609
|
+
if (p) {
|
|
28610
|
+
sum += Math.hypot(node.x - p.x, node.y - p.y);
|
|
28611
|
+
n++;
|
|
28612
|
+
}
|
|
28613
|
+
}
|
|
28614
|
+
return n ? sum / n : 0;
|
|
28615
|
+
}
|
|
28616
|
+
function edgeLength(layout) {
|
|
28617
|
+
let total = 0;
|
|
28618
|
+
for (const e of layout.edges)
|
|
28619
|
+
for (let i = 1; i < e.points.length; i++)
|
|
28620
|
+
total += Math.hypot(
|
|
28621
|
+
e.points[i].x - e.points[i - 1].x,
|
|
28622
|
+
e.points[i].y - e.points[i - 1].y
|
|
28623
|
+
);
|
|
28624
|
+
return total;
|
|
28625
|
+
}
|
|
28626
|
+
function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
|
|
28627
|
+
const hideDescriptions = opts?.hideDescriptions ?? false;
|
|
28628
|
+
const collapsedGroupLabels = /* @__PURE__ */ new Set();
|
|
28629
|
+
if (collapseInfo) {
|
|
28630
|
+
const missing = /* @__PURE__ */ new Set();
|
|
28631
|
+
for (const og of collapseInfo.originalGroups)
|
|
28632
|
+
if (!parsed.groups.some((g) => g.label === og.label))
|
|
28633
|
+
missing.add(og.label);
|
|
28634
|
+
for (const label of missing) {
|
|
28635
|
+
const og = collapseInfo.originalGroups.find((g) => g.label === label);
|
|
28636
|
+
const parent = og?.parentGroup;
|
|
28637
|
+
if (!parent || !missing.has(parent)) collapsedGroupLabels.add(label);
|
|
28638
|
+
}
|
|
28639
|
+
}
|
|
28640
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
28641
|
+
let maxDescH = 0;
|
|
28642
|
+
for (const node of parsed.nodes) {
|
|
28643
|
+
const s = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
|
|
28644
|
+
sizes.set(node.label, s);
|
|
28645
|
+
if (!hideDescriptions && node.description && node.description.length > 0)
|
|
28646
|
+
maxDescH = Math.max(maxDescH, s.height);
|
|
28647
|
+
}
|
|
28648
|
+
if (maxDescH > 0) {
|
|
28649
|
+
for (const node of parsed.nodes)
|
|
28650
|
+
if (node.description && node.description.length > 0) {
|
|
28651
|
+
const s = sizes.get(node.label);
|
|
28652
|
+
sizes.set(node.label, { width: s.width, height: maxDescH });
|
|
28653
|
+
}
|
|
28654
|
+
}
|
|
28655
|
+
const gid = (label) => `__group_${label}`;
|
|
28656
|
+
const rankdir = parsed.direction === "TB" ? "TB" : "LR";
|
|
28657
|
+
function place(cfg) {
|
|
28658
|
+
const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
|
|
28659
|
+
const ord = (a) => r ? shuffle(a, r) : a.slice();
|
|
28660
|
+
const g = new dagre4.graphlib.Graph({ compound: true, multigraph: true });
|
|
28661
|
+
g.setGraph({
|
|
28662
|
+
rankdir,
|
|
28663
|
+
ranker: cfg.ranker,
|
|
28664
|
+
nodesep: cfg.nodesep,
|
|
28665
|
+
ranksep: cfg.ranksep,
|
|
28666
|
+
edgesep: 20,
|
|
28667
|
+
marginx: 40,
|
|
28668
|
+
marginy: 40
|
|
28669
|
+
});
|
|
28670
|
+
g.setDefaultEdgeLabel(() => ({}));
|
|
28671
|
+
for (const grp of ord(parsed.groups))
|
|
28672
|
+
g.setNode(gid(grp.label), { label: grp.label });
|
|
28673
|
+
for (const node of ord(parsed.nodes)) {
|
|
28674
|
+
const s = sizes.get(node.label);
|
|
28675
|
+
g.setNode(node.label, { width: s.width, height: s.height });
|
|
28676
|
+
}
|
|
28677
|
+
for (const label of collapsedGroupLabels)
|
|
28678
|
+
g.setNode(gid(label), { width: NODE_WIDTH, height: NODE_HEIGHT });
|
|
28679
|
+
for (const grp of parsed.groups) {
|
|
28680
|
+
if (grp.parentGroup && g.hasNode(gid(grp.parentGroup)))
|
|
28681
|
+
g.setParent(gid(grp.label), gid(grp.parentGroup));
|
|
28682
|
+
for (const c of ord(grp.children)) {
|
|
28683
|
+
if (g.hasNode(c)) g.setParent(c, gid(grp.label));
|
|
28684
|
+
}
|
|
28685
|
+
}
|
|
28686
|
+
if (collapseInfo)
|
|
28687
|
+
for (const label of collapsedGroupLabels) {
|
|
28688
|
+
const og = collapseInfo.originalGroups.find((x) => x.label === label);
|
|
28689
|
+
if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && g.hasNode(gid(og.parentGroup)))
|
|
28690
|
+
g.setParent(gid(label), gid(og.parentGroup));
|
|
28691
|
+
}
|
|
28692
|
+
for (const e of ord(parsed.edges))
|
|
28693
|
+
if (g.hasNode(e.source) && g.hasNode(e.target))
|
|
28694
|
+
g.setEdge(e.source, e.target, {});
|
|
28695
|
+
dagre4.layout(g);
|
|
28696
|
+
const nodes = parsed.nodes.map((n2) => {
|
|
28697
|
+
const p = g.node(n2.label);
|
|
28698
|
+
return {
|
|
28699
|
+
label: n2.label,
|
|
28700
|
+
x: p.x,
|
|
28701
|
+
y: p.y,
|
|
28702
|
+
width: p.width,
|
|
28703
|
+
height: p.height
|
|
28704
|
+
};
|
|
28705
|
+
});
|
|
28706
|
+
const groups = parsed.groups.map(
|
|
28707
|
+
(grp) => {
|
|
28708
|
+
const p = g.node(gid(grp.label));
|
|
28709
|
+
return {
|
|
28710
|
+
label: grp.label,
|
|
28711
|
+
lineNumber: grp.lineNumber,
|
|
28712
|
+
x: p.x,
|
|
28713
|
+
y: p.y,
|
|
28714
|
+
width: p.width,
|
|
28715
|
+
height: p.height,
|
|
28716
|
+
collapsed: false,
|
|
28717
|
+
childCount: grp.children.length
|
|
28718
|
+
};
|
|
28719
|
+
}
|
|
28720
|
+
);
|
|
28721
|
+
for (const label of collapsedGroupLabels) {
|
|
28722
|
+
const p = g.node(gid(label));
|
|
28723
|
+
groups.push({
|
|
28724
|
+
label,
|
|
28725
|
+
lineNumber: 0,
|
|
28726
|
+
x: p.x,
|
|
28727
|
+
y: p.y,
|
|
28728
|
+
width: p.width,
|
|
28729
|
+
height: p.height,
|
|
28730
|
+
collapsed: true,
|
|
28731
|
+
childCount: collapseInfo?.collapsedChildCounts.get(label) ?? 0
|
|
28732
|
+
});
|
|
28733
|
+
}
|
|
28734
|
+
const edges = parsed.edges.filter((e) => g.hasEdge(e.source, e.target)).map((e) => {
|
|
28735
|
+
const ed = g.edge(e.source, e.target);
|
|
28736
|
+
return {
|
|
28737
|
+
source: e.source,
|
|
28738
|
+
target: e.target,
|
|
28739
|
+
...e.label !== void 0 && { label: e.label },
|
|
28740
|
+
bidirectional: e.bidirectional,
|
|
28741
|
+
lineNumber: e.lineNumber,
|
|
28742
|
+
points: ed?.points ?? [],
|
|
28743
|
+
yOffset: 0,
|
|
28744
|
+
parallelCount: 1,
|
|
28745
|
+
metadata: e.metadata
|
|
28746
|
+
};
|
|
28747
|
+
});
|
|
28748
|
+
const gg = g.graph();
|
|
28749
|
+
return {
|
|
28750
|
+
nodes,
|
|
28751
|
+
edges,
|
|
28752
|
+
groups,
|
|
28753
|
+
width: gg.width ?? 800,
|
|
28754
|
+
height: gg.height ?? 600
|
|
28755
|
+
};
|
|
28756
|
+
}
|
|
28757
|
+
const n = parsed.nodes.length;
|
|
28758
|
+
const seedCount = opts?.seeds ?? (n <= 12 ? 80 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
|
|
28759
|
+
const REFINE_K = opts?.refineK ?? 6;
|
|
28760
|
+
const lambda = opts?.lambda ?? DEFAULT_LAMBDA;
|
|
28761
|
+
const prev = opts?.previousPositions;
|
|
28762
|
+
const RANKERS = ["network-simplex", "tight-tree", "longest-path"];
|
|
28763
|
+
const SPACINGS = [
|
|
28764
|
+
{ nodesep: 50, ranksep: 60 },
|
|
28765
|
+
{ nodesep: 34, ranksep: 46 },
|
|
28766
|
+
{ nodesep: 66, ranksep: 82 }
|
|
28767
|
+
];
|
|
28768
|
+
const configs = [];
|
|
28769
|
+
for (const ranker of RANKERS)
|
|
28770
|
+
for (const sp of SPACINGS) configs.push({ ranker, ...sp });
|
|
28771
|
+
for (let s = 0; s < seedCount; s++)
|
|
28772
|
+
configs.push({
|
|
28773
|
+
ranker: "network-simplex",
|
|
28774
|
+
nodesep: 50,
|
|
28775
|
+
ranksep: 60,
|
|
28776
|
+
seed: s
|
|
28777
|
+
});
|
|
28778
|
+
const badness = (lay, floor) => {
|
|
28779
|
+
const x = countSplineCrossings(lay);
|
|
28780
|
+
if (x > floor) return Infinity;
|
|
28781
|
+
return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
|
|
28782
|
+
};
|
|
28783
|
+
const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
|
|
28784
|
+
const pool = [];
|
|
28785
|
+
for (const cfg of configs) {
|
|
28786
|
+
try {
|
|
28787
|
+
pool.push(place(cfg));
|
|
28788
|
+
} catch {
|
|
28789
|
+
}
|
|
28790
|
+
}
|
|
28791
|
+
if (!pool.length)
|
|
28792
|
+
return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
|
|
28793
|
+
let layered = [];
|
|
28794
|
+
try {
|
|
28795
|
+
layered = layeredCandidates(parsed, sizes);
|
|
28796
|
+
} catch {
|
|
28797
|
+
}
|
|
28798
|
+
pool.sort(
|
|
28799
|
+
(a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
|
|
28800
|
+
);
|
|
28801
|
+
const refineK = Math.min(REFINE_K, pool.length);
|
|
28802
|
+
let best = pool[0];
|
|
28803
|
+
let bestObj = Infinity;
|
|
28804
|
+
let bestBad = Infinity;
|
|
28805
|
+
const consider = (lay) => {
|
|
28806
|
+
const bad = badness(lay, bestBad);
|
|
28807
|
+
if (bad === Infinity) return;
|
|
28808
|
+
const sc = objective(lay, bad);
|
|
28809
|
+
if (sc < bestObj) {
|
|
28810
|
+
bestObj = sc;
|
|
28811
|
+
bestBad = bad;
|
|
28812
|
+
best = lay;
|
|
28813
|
+
}
|
|
28814
|
+
};
|
|
28815
|
+
for (const lay of pool.slice(0, refineK)) consider(lay);
|
|
28816
|
+
if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
|
|
28817
|
+
const extra = [];
|
|
28818
|
+
for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
|
|
28819
|
+
try {
|
|
28820
|
+
extra.push(
|
|
28821
|
+
place({
|
|
28822
|
+
ranker: "network-simplex",
|
|
28823
|
+
nodesep: 50,
|
|
28824
|
+
ranksep: 60,
|
|
28825
|
+
seed: s
|
|
28826
|
+
})
|
|
28827
|
+
);
|
|
28828
|
+
} catch {
|
|
28829
|
+
}
|
|
28830
|
+
}
|
|
28831
|
+
extra.sort(
|
|
28832
|
+
(a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
|
|
28833
|
+
);
|
|
28834
|
+
for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
|
|
28835
|
+
}
|
|
28836
|
+
for (const lay of layered) {
|
|
28837
|
+
const variants = [lay];
|
|
28838
|
+
const dp = deroutePierces(lay);
|
|
28839
|
+
if (dp !== lay) variants.push(dp);
|
|
28840
|
+
for (const v of variants) {
|
|
28841
|
+
const bad = badness(v, bestBad - 1);
|
|
28842
|
+
if (bad < bestBad) {
|
|
28843
|
+
bestBad = bad;
|
|
28844
|
+
best = v;
|
|
28845
|
+
}
|
|
28846
|
+
}
|
|
28847
|
+
}
|
|
28848
|
+
if (bestBad > 0) {
|
|
28849
|
+
const rerouted = deroutePierces(best);
|
|
28850
|
+
if (rerouted !== best && badness(rerouted, bestBad - 1) < bestBad)
|
|
28851
|
+
best = rerouted;
|
|
28852
|
+
}
|
|
28853
|
+
if (bestBad > 0 && countGroupOverlaps(best) > 0) {
|
|
28854
|
+
const separated = separateGroupBands(best, parsed);
|
|
28855
|
+
if (separated !== best && badness(separated, bestBad - 1) < bestBad)
|
|
28856
|
+
best = separated;
|
|
28857
|
+
}
|
|
28858
|
+
return best;
|
|
28859
|
+
}
|
|
28860
|
+
var DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
|
|
28861
|
+
var init_layout_search = __esm({
|
|
28862
|
+
"src/boxes-and-lines/layout-search.ts"() {
|
|
28863
|
+
"use strict";
|
|
28864
|
+
init_layout5();
|
|
28865
|
+
init_layout_layered();
|
|
28866
|
+
DEFAULT_LAMBDA = 4;
|
|
28867
|
+
ESCALATE_THRESHOLD = 4;
|
|
28868
|
+
ESCALATE_MAX_N = 45;
|
|
28869
|
+
ESCALATE_SEEDS = 18;
|
|
28870
|
+
ESCALATE_REFINE = 10;
|
|
28871
|
+
splineGen = d3line().x((d) => d.x).y((d) => d.y).curve(curveBasis5);
|
|
28872
|
+
GROUP_LABEL_ZONE2 = 32;
|
|
28873
|
+
}
|
|
28874
|
+
});
|
|
28875
|
+
|
|
27546
28876
|
// src/boxes-and-lines/layout.ts
|
|
27547
28877
|
var layout_exports5 = {};
|
|
27548
28878
|
__export(layout_exports5, {
|
|
28879
|
+
NODE_HEIGHT: () => NODE_HEIGHT,
|
|
28880
|
+
NODE_WIDTH: () => NODE_WIDTH,
|
|
28881
|
+
computeNodeSize: () => computeNodeSize,
|
|
27549
28882
|
layoutBoxesAndLines: () => layoutBoxesAndLines
|
|
27550
28883
|
});
|
|
27551
|
-
import ELK from "elkjs/lib/elk.bundled.js";
|
|
27552
28884
|
function splitCamelCase2(word) {
|
|
27553
28885
|
const parts = [];
|
|
27554
28886
|
let start = 0;
|
|
@@ -27619,417 +28951,21 @@ function computeNodeSize(node, reserveValueRow) {
|
|
|
27619
28951
|
const totalHeight = labelHeight + SEPARATOR_GAP5 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
|
|
27620
28952
|
return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
|
|
27621
28953
|
}
|
|
27622
|
-
function getElk() {
|
|
27623
|
-
if (!elkInstance) elkInstance = new ELK();
|
|
27624
|
-
return elkInstance;
|
|
27625
|
-
}
|
|
27626
|
-
function baseOptions() {
|
|
27627
|
-
return {
|
|
27628
|
-
"elk.algorithm": "layered",
|
|
27629
|
-
// INCLUDE_CHILDREN lets ELK route edges across container boundaries.
|
|
27630
|
-
"elk.hierarchyHandling": "INCLUDE_CHILDREN",
|
|
27631
|
-
"elk.edgeRouting": "ORTHOGONAL",
|
|
27632
|
-
"elk.layered.unnecessaryBendpoints": "true",
|
|
27633
|
-
// Let edges leave from top/bottom of nodes (not just the flow-direction
|
|
27634
|
-
// sides) when it reduces crossings.
|
|
27635
|
-
"elk.layered.allowNonFlowPortsToSwitchSides": "true"
|
|
27636
|
-
};
|
|
27637
|
-
}
|
|
27638
|
-
function bkBaseline() {
|
|
27639
|
-
return {
|
|
27640
|
-
...baseOptions(),
|
|
27641
|
-
"elk.layered.nodePlacement.strategy": "BRANDES_KOEPF",
|
|
27642
|
-
"elk.layered.nodePlacement.bk.fixedAlignment": "BALANCED",
|
|
27643
|
-
"elk.layered.nodePlacement.bk.edgeStraightening": "IMPROVE_STRAIGHTNESS",
|
|
27644
|
-
"elk.layered.compaction.connectedComponents": "true",
|
|
27645
|
-
"elk.layered.spacing.nodeNodeBetweenLayers": "90",
|
|
27646
|
-
"elk.spacing.nodeNode": "55",
|
|
27647
|
-
"elk.spacing.edgeNode": "55",
|
|
27648
|
-
"elk.spacing.edgeEdge": "18"
|
|
27649
|
-
};
|
|
27650
|
-
}
|
|
27651
|
-
function getVariants() {
|
|
27652
|
-
const bk = bkBaseline();
|
|
27653
|
-
return [
|
|
27654
|
-
{
|
|
27655
|
-
name: "bk-baseline",
|
|
27656
|
-
options: {
|
|
27657
|
-
...bk,
|
|
27658
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "ONE_SIDED"
|
|
27659
|
-
}
|
|
27660
|
-
},
|
|
27661
|
-
{
|
|
27662
|
-
name: "bk-aggressive",
|
|
27663
|
-
options: {
|
|
27664
|
-
...bk,
|
|
27665
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
|
|
27666
|
-
"elk.layered.thoroughness": "50"
|
|
27667
|
-
}
|
|
27668
|
-
},
|
|
27669
|
-
{
|
|
27670
|
-
name: "bk-wide",
|
|
27671
|
-
options: {
|
|
27672
|
-
...bk,
|
|
27673
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
|
|
27674
|
-
"elk.layered.thoroughness": "50",
|
|
27675
|
-
"elk.spacing.nodeNode": "70",
|
|
27676
|
-
"elk.spacing.edgeNode": "75",
|
|
27677
|
-
"elk.spacing.edgeEdge": "22",
|
|
27678
|
-
"elk.layered.spacing.nodeNodeBetweenLayers": "120"
|
|
27679
|
-
}
|
|
27680
|
-
},
|
|
27681
|
-
{
|
|
27682
|
-
name: "longest-path",
|
|
27683
|
-
options: {
|
|
27684
|
-
...bk,
|
|
27685
|
-
"elk.layered.layering.strategy": "LONGEST_PATH",
|
|
27686
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
|
|
27687
|
-
"elk.layered.thoroughness": "50"
|
|
27688
|
-
}
|
|
27689
|
-
},
|
|
27690
|
-
{
|
|
27691
|
-
name: "bounded-width",
|
|
27692
|
-
options: {
|
|
27693
|
-
...bk,
|
|
27694
|
-
"elk.layered.layering.strategy": "COFFMAN_GRAHAM",
|
|
27695
|
-
"elk.layered.layering.coffmanGraham.layerBound": "3",
|
|
27696
|
-
"elk.layered.crossingMinimization.greedySwitch.type": "TWO_SIDED",
|
|
27697
|
-
"elk.layered.thoroughness": "50"
|
|
27698
|
-
}
|
|
27699
|
-
}
|
|
27700
|
-
];
|
|
27701
|
-
}
|
|
27702
|
-
function countCrossings(edges) {
|
|
27703
|
-
let count = 0;
|
|
27704
|
-
for (let i = 0; i < edges.length; i++) {
|
|
27705
|
-
const edgeI = edges[i];
|
|
27706
|
-
const a = edgeI.points;
|
|
27707
|
-
if (a.length < 2) continue;
|
|
27708
|
-
for (let j = i + 1; j < edges.length; j++) {
|
|
27709
|
-
const edgeJ = edges[j];
|
|
27710
|
-
const b = edgeJ.points;
|
|
27711
|
-
if (b.length < 2) continue;
|
|
27712
|
-
if (edgeI.source === edgeJ.source) continue;
|
|
27713
|
-
if (edgeI.source === edgeJ.target) continue;
|
|
27714
|
-
if (edgeI.target === edgeJ.source) continue;
|
|
27715
|
-
if (edgeI.target === edgeJ.target) continue;
|
|
27716
|
-
for (let ai = 0; ai < a.length - 1; ai++) {
|
|
27717
|
-
for (let bi = 0; bi < b.length - 1; bi++) {
|
|
27718
|
-
if (segmentsCross(a[ai], a[ai + 1], b[bi], b[bi + 1])) count++;
|
|
27719
|
-
}
|
|
27720
|
-
}
|
|
27721
|
-
}
|
|
27722
|
-
}
|
|
27723
|
-
return count;
|
|
27724
|
-
}
|
|
27725
|
-
function segmentsCross(p1, p2, p3, p4) {
|
|
27726
|
-
const d1x = p2.x - p1.x;
|
|
27727
|
-
const d1y = p2.y - p1.y;
|
|
27728
|
-
const d2x = p4.x - p3.x;
|
|
27729
|
-
const d2y = p4.y - p3.y;
|
|
27730
|
-
const denom = d1x * d2y - d1y * d2x;
|
|
27731
|
-
if (Math.abs(denom) < 1e-9) return false;
|
|
27732
|
-
const t = ((p3.x - p1.x) * d2y - (p3.y - p1.y) * d2x) / denom;
|
|
27733
|
-
const s = ((p3.x - p1.x) * d1y - (p3.y - p1.y) * d1x) / denom;
|
|
27734
|
-
const EPS = 1e-3;
|
|
27735
|
-
return t > EPS && t < 1 - EPS && s > EPS && s < 1 - EPS;
|
|
27736
|
-
}
|
|
27737
|
-
function countTotalBends(edges) {
|
|
27738
|
-
let bends = 0;
|
|
27739
|
-
for (const e of edges) bends += Math.max(0, e.points.length - 2);
|
|
27740
|
-
return bends;
|
|
27741
|
-
}
|
|
27742
|
-
function scoreLayout(layout) {
|
|
27743
|
-
return {
|
|
27744
|
-
crossings: countCrossings(layout.edges),
|
|
27745
|
-
bends: countTotalBends(layout.edges),
|
|
27746
|
-
area: layout.width * layout.height
|
|
27747
|
-
};
|
|
27748
|
-
}
|
|
27749
|
-
function cmpScore(a, b) {
|
|
27750
|
-
const aBucket = a.crossings <= CROSSINGS_FORGIVENESS ? 0 : a.crossings;
|
|
27751
|
-
const bBucket = b.crossings <= CROSSINGS_FORGIVENESS ? 0 : b.crossings;
|
|
27752
|
-
if (aBucket !== bBucket) return aBucket - bBucket;
|
|
27753
|
-
if (a.area !== b.area) return a.area - b.area;
|
|
27754
|
-
return a.bends - b.bends;
|
|
27755
|
-
}
|
|
27756
28954
|
async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
|
|
27757
|
-
const
|
|
27758
|
-
const
|
|
27759
|
-
|
|
27760
|
-
|
|
27761
|
-
|
|
27762
|
-
|
|
27763
|
-
|
|
27764
|
-
missingGroups.add(og.label);
|
|
27765
|
-
}
|
|
27766
|
-
}
|
|
27767
|
-
for (const label of missingGroups) {
|
|
27768
|
-
const og = collapseInfo.originalGroups.find((g) => g.label === label);
|
|
27769
|
-
const parentLabel = og?.parentGroup;
|
|
27770
|
-
if (!parentLabel || !missingGroups.has(parentLabel)) {
|
|
27771
|
-
collapsedGroupLabels.add(label);
|
|
27772
|
-
}
|
|
27773
|
-
}
|
|
27774
|
-
}
|
|
27775
|
-
const nodeSizes = /* @__PURE__ */ new Map();
|
|
27776
|
-
let maxDescHeight = 0;
|
|
27777
|
-
for (const node of parsed.nodes) {
|
|
27778
|
-
const size = hideDescriptions ? { width: NODE_WIDTH, height: NODE_HEIGHT } : computeNodeSize(node, parsed.showValues === true);
|
|
27779
|
-
nodeSizes.set(node.label, size);
|
|
27780
|
-
if (!hideDescriptions && node.description && node.description.length > 0) {
|
|
27781
|
-
maxDescHeight = Math.max(maxDescHeight, size.height);
|
|
27782
|
-
}
|
|
27783
|
-
}
|
|
27784
|
-
if (maxDescHeight > 0) {
|
|
27785
|
-
for (const node of parsed.nodes) {
|
|
27786
|
-
if (node.description && node.description.length > 0) {
|
|
27787
|
-
const size = nodeSizes.get(node.label);
|
|
27788
|
-
nodeSizes.set(node.label, { width: size.width, height: maxDescHeight });
|
|
27789
|
-
}
|
|
27790
|
-
}
|
|
27791
|
-
}
|
|
27792
|
-
const expandedGroupSet = new Set(parsed.groups.map((g) => g.label));
|
|
27793
|
-
const gid = (label) => `__group_${label}`;
|
|
27794
|
-
function buildGraph() {
|
|
27795
|
-
const nodeById = /* @__PURE__ */ new Map();
|
|
27796
|
-
const parentOf = /* @__PURE__ */ new Map();
|
|
27797
|
-
for (const node of parsed.nodes) {
|
|
27798
|
-
const size = nodeSizes.get(node.label);
|
|
27799
|
-
nodeById.set(node.label, {
|
|
27800
|
-
id: node.label,
|
|
27801
|
-
width: size.width,
|
|
27802
|
-
height: size.height,
|
|
27803
|
-
labels: [{ text: node.label }]
|
|
27804
|
-
});
|
|
27805
|
-
}
|
|
27806
|
-
for (const group of parsed.groups) {
|
|
27807
|
-
nodeById.set(gid(group.label), {
|
|
27808
|
-
id: gid(group.label),
|
|
27809
|
-
labels: [{ text: group.label }],
|
|
27810
|
-
layoutOptions: {
|
|
27811
|
-
"elk.padding": `[top=${CONTAINER_PAD_TOP2},left=${CONTAINER_PAD_X3},bottom=${CONTAINER_PAD_BOTTOM3},right=${CONTAINER_PAD_X3}]`,
|
|
27812
|
-
// Suggest square-ish containers — has limited effect with
|
|
27813
|
-
// INCLUDE_CHILDREN but doesn't hurt.
|
|
27814
|
-
"elk.aspectRatio": "1.4"
|
|
27815
|
-
},
|
|
27816
|
-
children: [],
|
|
27817
|
-
edges: []
|
|
27818
|
-
});
|
|
27819
|
-
}
|
|
27820
|
-
for (const label of collapsedGroupLabels) {
|
|
27821
|
-
nodeById.set(gid(label), {
|
|
27822
|
-
id: gid(label),
|
|
27823
|
-
width: NODE_WIDTH,
|
|
27824
|
-
height: NODE_HEIGHT,
|
|
27825
|
-
labels: [{ text: label }]
|
|
27826
|
-
});
|
|
27827
|
-
}
|
|
27828
|
-
for (const group of parsed.groups) {
|
|
27829
|
-
if (group.parentGroup && nodeById.has(gid(group.parentGroup))) {
|
|
27830
|
-
parentOf.set(gid(group.label), gid(group.parentGroup));
|
|
27831
|
-
}
|
|
27832
|
-
}
|
|
27833
|
-
if (collapseInfo) {
|
|
27834
|
-
for (const label of collapsedGroupLabels) {
|
|
27835
|
-
const og = collapseInfo.originalGroups.find((g) => g.label === label);
|
|
27836
|
-
if (og?.parentGroup && !collapsedGroupLabels.has(og.parentGroup) && nodeById.has(gid(og.parentGroup))) {
|
|
27837
|
-
parentOf.set(gid(label), gid(og.parentGroup));
|
|
27838
|
-
}
|
|
27839
|
-
}
|
|
27840
|
-
}
|
|
27841
|
-
for (const group of parsed.groups) {
|
|
27842
|
-
for (const child of group.children) {
|
|
27843
|
-
if (expandedGroupSet.has(child)) continue;
|
|
27844
|
-
if (nodeById.has(child)) {
|
|
27845
|
-
parentOf.set(child, gid(group.label));
|
|
27846
|
-
}
|
|
27847
|
-
}
|
|
27848
|
-
}
|
|
27849
|
-
const roots = [];
|
|
27850
|
-
for (const [id, node] of nodeById) {
|
|
27851
|
-
const parentId = parentOf.get(id);
|
|
27852
|
-
if (parentId) {
|
|
27853
|
-
const parent = nodeById.get(parentId);
|
|
27854
|
-
parent.children = parent.children ?? [];
|
|
27855
|
-
parent.children.push(node);
|
|
27856
|
-
} else {
|
|
27857
|
-
roots.push(node);
|
|
27858
|
-
}
|
|
27859
|
-
}
|
|
27860
|
-
const rootEdges = [];
|
|
27861
|
-
for (let i = 0; i < parsed.edges.length; i++) {
|
|
27862
|
-
const edge = parsed.edges[i];
|
|
27863
|
-
if (!nodeById.has(edge.source) || !nodeById.has(edge.target)) continue;
|
|
27864
|
-
rootEdges.push({
|
|
27865
|
-
id: `e${i}`,
|
|
27866
|
-
sources: [edge.source],
|
|
27867
|
-
targets: [edge.target]
|
|
27868
|
-
});
|
|
27869
|
-
}
|
|
27870
|
-
return { roots, rootEdges };
|
|
27871
|
-
}
|
|
27872
|
-
async function runVariant(variant) {
|
|
27873
|
-
const { roots, rootEdges } = buildGraph();
|
|
27874
|
-
const elkRoot = {
|
|
27875
|
-
id: "root",
|
|
27876
|
-
layoutOptions: {
|
|
27877
|
-
...variant.options,
|
|
27878
|
-
"elk.direction": direction,
|
|
27879
|
-
"elk.padding": `[top=${MARGIN3},left=${MARGIN3},bottom=${MARGIN3},right=${MARGIN3}]`
|
|
27880
|
-
},
|
|
27881
|
-
children: roots,
|
|
27882
|
-
edges: rootEdges
|
|
27883
|
-
};
|
|
27884
|
-
const result = await getElk().layout(elkRoot);
|
|
27885
|
-
return extractLayout(result);
|
|
27886
|
-
}
|
|
27887
|
-
function extractLayout(result) {
|
|
27888
|
-
const layoutNodes = [];
|
|
27889
|
-
const layoutGroups = [];
|
|
27890
|
-
const allEdges = [];
|
|
27891
|
-
const containerAbs = /* @__PURE__ */ new Map();
|
|
27892
|
-
function walk(n, offsetX, offsetY, isRoot) {
|
|
27893
|
-
const nx = (n.x ?? 0) + offsetX;
|
|
27894
|
-
const ny = (n.y ?? 0) + offsetY;
|
|
27895
|
-
const nw = n.width ?? 0;
|
|
27896
|
-
const nh = n.height ?? 0;
|
|
27897
|
-
if (isRoot) {
|
|
27898
|
-
containerAbs.set("root", { x: nx, y: ny });
|
|
27899
|
-
} else {
|
|
27900
|
-
const isGroup = n.id.startsWith("__group_");
|
|
27901
|
-
if (isGroup) {
|
|
27902
|
-
const label = n.id.slice("__group_".length);
|
|
27903
|
-
const collapsed = collapsedGroupLabels.has(label);
|
|
27904
|
-
const og = collapseInfo?.originalGroups.find(
|
|
27905
|
-
(g) => g.label === label
|
|
27906
|
-
);
|
|
27907
|
-
const pg = parsed.groups.find((g) => g.label === label);
|
|
27908
|
-
const childCount = collapsed ? collapseInfo?.collapsedChildCounts.get(label) ?? 0 : void 0;
|
|
27909
|
-
layoutGroups.push({
|
|
27910
|
-
label,
|
|
27911
|
-
lineNumber: pg?.lineNumber ?? og?.lineNumber ?? 0,
|
|
27912
|
-
x: nx + nw / 2,
|
|
27913
|
-
y: ny + nh / 2,
|
|
27914
|
-
width: nw,
|
|
27915
|
-
height: nh,
|
|
27916
|
-
collapsed,
|
|
27917
|
-
...childCount !== void 0 && { childCount }
|
|
27918
|
-
});
|
|
27919
|
-
if (!collapsed) containerAbs.set(n.id, { x: nx, y: ny });
|
|
27920
|
-
} else {
|
|
27921
|
-
layoutNodes.push({
|
|
27922
|
-
label: n.id,
|
|
27923
|
-
x: nx + nw / 2,
|
|
27924
|
-
y: ny + nh / 2,
|
|
27925
|
-
width: nw,
|
|
27926
|
-
height: nh
|
|
27927
|
-
});
|
|
27928
|
-
}
|
|
27929
|
-
}
|
|
27930
|
-
if (n.edges) for (const e of n.edges) allEdges.push(e);
|
|
27931
|
-
if (n.children) for (const c of n.children) walk(c, nx, ny, false);
|
|
27932
|
-
}
|
|
27933
|
-
walk(result, 0, 0, true);
|
|
27934
|
-
const edgeYOffsets = new Array(parsed.edges.length).fill(0);
|
|
27935
|
-
const edgeParallelCounts = new Array(parsed.edges.length).fill(1);
|
|
27936
|
-
const parallelGroups = /* @__PURE__ */ new Map();
|
|
27937
|
-
for (let i = 0; i < parsed.edges.length; i++) {
|
|
27938
|
-
const edge = parsed.edges[i];
|
|
27939
|
-
const [a, b] = edge.source < edge.target ? [edge.source, edge.target] : [edge.target, edge.source];
|
|
27940
|
-
const key = `${a}\0${b}`;
|
|
27941
|
-
if (!parallelGroups.has(key)) parallelGroups.set(key, []);
|
|
27942
|
-
parallelGroups.get(key).push(i);
|
|
27943
|
-
}
|
|
27944
|
-
for (const group of parallelGroups.values()) {
|
|
27945
|
-
const capped = group.slice(0, MAX_PARALLEL_EDGES);
|
|
27946
|
-
for (const idx of group.slice(MAX_PARALLEL_EDGES)) {
|
|
27947
|
-
edgeParallelCounts[idx] = 0;
|
|
27948
|
-
}
|
|
27949
|
-
if (capped.length < 2) continue;
|
|
27950
|
-
for (let j = 0; j < capped.length; j++) {
|
|
27951
|
-
const cappedJ = capped[j];
|
|
27952
|
-
edgeYOffsets[cappedJ] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
|
|
27953
|
-
edgeParallelCounts[cappedJ] = capped.length;
|
|
27954
|
-
}
|
|
27955
|
-
}
|
|
27956
|
-
const edgeById = /* @__PURE__ */ new Map();
|
|
27957
|
-
for (const e of allEdges) edgeById.set(e.id, e);
|
|
27958
|
-
const layoutEdges = [];
|
|
27959
|
-
for (let i = 0; i < parsed.edges.length; i++) {
|
|
27960
|
-
const edge = parsed.edges[i];
|
|
27961
|
-
if (edgeParallelCounts[i] === 0) continue;
|
|
27962
|
-
const elkEdge = edgeById.get(`e${i}`);
|
|
27963
|
-
if (!elkEdge?.sections || elkEdge.sections.length === 0) continue;
|
|
27964
|
-
const container = elkEdge.container ?? "root";
|
|
27965
|
-
const off = containerAbs.get(container) ?? { x: 0, y: 0 };
|
|
27966
|
-
const s = elkEdge.sections[0];
|
|
27967
|
-
const points = [
|
|
27968
|
-
{ x: s.startPoint.x + off.x, y: s.startPoint.y + off.y },
|
|
27969
|
-
...(s.bendPoints ?? []).map((p) => ({
|
|
27970
|
-
x: p.x + off.x,
|
|
27971
|
-
y: p.y + off.y
|
|
27972
|
-
})),
|
|
27973
|
-
{ x: s.endPoint.x + off.x, y: s.endPoint.y + off.y }
|
|
27974
|
-
];
|
|
27975
|
-
let labelX;
|
|
27976
|
-
let labelY;
|
|
27977
|
-
if (edge.label && points.length >= 2) {
|
|
27978
|
-
const mid = Math.floor(points.length / 2);
|
|
27979
|
-
const midPoint = points[mid];
|
|
27980
|
-
labelX = midPoint.x;
|
|
27981
|
-
labelY = midPoint.y - 10;
|
|
27982
|
-
}
|
|
27983
|
-
layoutEdges.push({
|
|
27984
|
-
source: edge.source,
|
|
27985
|
-
target: edge.target,
|
|
27986
|
-
...edge.label !== void 0 && { label: edge.label },
|
|
27987
|
-
bidirectional: edge.bidirectional,
|
|
27988
|
-
lineNumber: edge.lineNumber,
|
|
27989
|
-
points,
|
|
27990
|
-
...labelX !== void 0 && { labelX },
|
|
27991
|
-
...labelY !== void 0 && { labelY },
|
|
27992
|
-
// In-bounds — i < parsed.edges.length, arrays sized to that length.
|
|
27993
|
-
yOffset: edgeYOffsets[i],
|
|
27994
|
-
parallelCount: edgeParallelCounts[i],
|
|
27995
|
-
metadata: edge.metadata,
|
|
27996
|
-
deferred: true
|
|
27997
|
-
});
|
|
27998
|
-
}
|
|
27999
|
-
let maxX = 0;
|
|
28000
|
-
let maxY = 0;
|
|
28001
|
-
for (const node of layoutNodes) {
|
|
28002
|
-
maxX = Math.max(maxX, node.x + node.width / 2);
|
|
28003
|
-
maxY = Math.max(maxY, node.y + node.height / 2);
|
|
28004
|
-
}
|
|
28005
|
-
for (const group of layoutGroups) {
|
|
28006
|
-
maxX = Math.max(maxX, group.x + group.width / 2);
|
|
28007
|
-
maxY = Math.max(maxY, group.y + group.height / 2);
|
|
28955
|
+
const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
|
|
28956
|
+
const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
|
|
28957
|
+
...layoutOptions?.hideDescriptions !== void 0 && {
|
|
28958
|
+
hideDescriptions: layoutOptions.hideDescriptions
|
|
28959
|
+
},
|
|
28960
|
+
...layoutOptions?.previousPositions !== void 0 && {
|
|
28961
|
+
previousPositions: layoutOptions.previousPositions
|
|
28008
28962
|
}
|
|
28009
|
-
|
|
28010
|
-
|
|
28011
|
-
|
|
28012
|
-
|
|
28013
|
-
|
|
28014
|
-
|
|
28015
|
-
};
|
|
28016
|
-
}
|
|
28017
|
-
const N = parsed.nodes.length + parsed.groups.length;
|
|
28018
|
-
const E = parsed.edges.length;
|
|
28019
|
-
const trivial = N < 8 && E < 10;
|
|
28020
|
-
const variants = trivial ? [getVariants()[1]] : getVariants();
|
|
28021
|
-
const results = await Promise.all(variants.map((v) => runVariant(v)));
|
|
28022
|
-
let best = results[0];
|
|
28023
|
-
let bestScore = scoreLayout(best);
|
|
28024
|
-
for (let i = 1; i < results.length; i++) {
|
|
28025
|
-
const resultI = results[i];
|
|
28026
|
-
const s = scoreLayout(resultI);
|
|
28027
|
-
if (cmpScore(s, bestScore) < 0) {
|
|
28028
|
-
best = resultI;
|
|
28029
|
-
bestScore = s;
|
|
28030
|
-
}
|
|
28031
|
-
}
|
|
28032
|
-
return attachNotes(best, parsed, layoutOptions?.collapsedNotes);
|
|
28963
|
+
});
|
|
28964
|
+
return attachNotes(
|
|
28965
|
+
applyParallelEdgeOffsets(searched),
|
|
28966
|
+
parsed,
|
|
28967
|
+
layoutOptions?.collapsedNotes
|
|
28968
|
+
);
|
|
28033
28969
|
}
|
|
28034
28970
|
function attachNotes(layout, parsed, collapsedNotes) {
|
|
28035
28971
|
const notesSuppressed = parsed.options?.["no-notes"] === "on";
|
|
@@ -28107,20 +29043,47 @@ function attachNotes(layout, parsed, collapsedNotes) {
|
|
|
28107
29043
|
nodes: finalNodes,
|
|
28108
29044
|
edges: finalEdges,
|
|
28109
29045
|
groups: finalGroups,
|
|
28110
|
-
width: bbMaxX + shiftX +
|
|
28111
|
-
height: bbMaxY + shiftY +
|
|
29046
|
+
width: bbMaxX + shiftX + MARGIN4,
|
|
29047
|
+
height: bbMaxY + shiftY + MARGIN4
|
|
29048
|
+
};
|
|
29049
|
+
}
|
|
29050
|
+
function applyParallelEdgeOffsets(layout) {
|
|
29051
|
+
const groups = /* @__PURE__ */ new Map();
|
|
29052
|
+
layout.edges.forEach((e, i) => {
|
|
29053
|
+
const [a, b] = e.source < e.target ? [e.source, e.target] : [e.target, e.source];
|
|
29054
|
+
const key = `${a}\0${b}`;
|
|
29055
|
+
const arr = groups.get(key);
|
|
29056
|
+
if (arr) arr.push(i);
|
|
29057
|
+
else groups.set(key, [i]);
|
|
29058
|
+
});
|
|
29059
|
+
if ([...groups.values()].every((g) => g.length < 2)) return layout;
|
|
29060
|
+
const yOffset = new Array(layout.edges.length).fill(0);
|
|
29061
|
+
const count = new Array(layout.edges.length).fill(1);
|
|
29062
|
+
for (const idxs of groups.values()) {
|
|
29063
|
+
const capped = idxs.slice(0, MAX_PARALLEL_EDGES);
|
|
29064
|
+
for (const drop of idxs.slice(MAX_PARALLEL_EDGES)) count[drop] = 0;
|
|
29065
|
+
if (capped.length < 2) continue;
|
|
29066
|
+
capped.forEach((idx, j) => {
|
|
29067
|
+
yOffset[idx] = (j - (capped.length - 1) / 2) * PARALLEL_SPACING;
|
|
29068
|
+
count[idx] = capped.length;
|
|
29069
|
+
});
|
|
29070
|
+
}
|
|
29071
|
+
return {
|
|
29072
|
+
...layout,
|
|
29073
|
+
edges: layout.edges.map((e, i) => ({
|
|
29074
|
+
...e,
|
|
29075
|
+
yOffset: yOffset[i],
|
|
29076
|
+
parallelCount: count[i]
|
|
29077
|
+
}))
|
|
28112
29078
|
};
|
|
28113
29079
|
}
|
|
28114
|
-
var
|
|
29080
|
+
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;
|
|
28115
29081
|
var init_layout5 = __esm({
|
|
28116
29082
|
"src/boxes-and-lines/layout.ts"() {
|
|
28117
29083
|
"use strict";
|
|
28118
29084
|
init_text_measure();
|
|
28119
29085
|
init_notes();
|
|
28120
|
-
|
|
28121
|
-
CONTAINER_PAD_X3 = 30;
|
|
28122
|
-
CONTAINER_PAD_TOP2 = 40;
|
|
28123
|
-
CONTAINER_PAD_BOTTOM3 = 24;
|
|
29086
|
+
MARGIN4 = 40;
|
|
28124
29087
|
MAX_PARALLEL_EDGES = 5;
|
|
28125
29088
|
PARALLEL_SPACING = 22;
|
|
28126
29089
|
PHI = 1.618;
|
|
@@ -28137,8 +29100,6 @@ var init_layout5 = __esm({
|
|
|
28137
29100
|
LABEL_PAD = 12;
|
|
28138
29101
|
VALUE_ROW_FONT = 11;
|
|
28139
29102
|
VALUE_ROW_H = SEPARATOR_GAP5 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
|
|
28140
|
-
elkInstance = null;
|
|
28141
|
-
CROSSINGS_FORGIVENESS = 1;
|
|
28142
29103
|
}
|
|
28143
29104
|
});
|
|
28144
29105
|
|
|
@@ -28293,7 +29254,7 @@ function layoutMindmap(parsed, _palette, options) {
|
|
|
28293
29254
|
leafWidth: ctx.structural(LEAF_WIDTH),
|
|
28294
29255
|
hGap: ctx.aesthetic(H_GAP2),
|
|
28295
29256
|
vGap: ctx.aesthetic(V_GAP2),
|
|
28296
|
-
margin: ctx.aesthetic(
|
|
29257
|
+
margin: ctx.aesthetic(MARGIN5),
|
|
28297
29258
|
multiRootGap: ctx.aesthetic(MULTI_ROOT_GAP)
|
|
28298
29259
|
};
|
|
28299
29260
|
populateDepthCache(roots);
|
|
@@ -28671,7 +29632,7 @@ function populateDepthCache(roots) {
|
|
|
28671
29632
|
};
|
|
28672
29633
|
walk(roots, 0);
|
|
28673
29634
|
}
|
|
28674
|
-
var ROOT_WIDTH, DEPTH1_WIDTH, LEAF_WIDTH, SINGLE_LABEL_HEIGHT, LABEL_LINE_HEIGHT2, DESC_LINE_HEIGHT3, NODE_V_PAD, H_GAP2, V_GAP2,
|
|
29635
|
+
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;
|
|
28675
29636
|
var init_layout6 = __esm({
|
|
28676
29637
|
"src/mindmap/layout.ts"() {
|
|
28677
29638
|
"use strict";
|
|
@@ -28687,7 +29648,7 @@ var init_layout6 = __esm({
|
|
|
28687
29648
|
NODE_V_PAD = 10;
|
|
28688
29649
|
H_GAP2 = 40;
|
|
28689
29650
|
V_GAP2 = 12;
|
|
28690
|
-
|
|
29651
|
+
MARGIN5 = 40;
|
|
28691
29652
|
MULTI_ROOT_GAP = 60;
|
|
28692
29653
|
nodeDepthCache = /* @__PURE__ */ new Map();
|
|
28693
29654
|
}
|
|
@@ -29962,7 +30923,7 @@ __export(layout_exports8, {
|
|
|
29962
30923
|
layoutC4Deployment: () => layoutC4Deployment,
|
|
29963
30924
|
rollUpContextRelationships: () => rollUpContextRelationships
|
|
29964
30925
|
});
|
|
29965
|
-
import
|
|
30926
|
+
import dagre5 from "@dagrejs/dagre";
|
|
29966
30927
|
function computeEdgePenalty(edgeList, nodePositions, degrees, nodeGeometry) {
|
|
29967
30928
|
let penalty = 0;
|
|
29968
30929
|
for (const edge of edgeList) {
|
|
@@ -30387,7 +31348,7 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
30387
31348
|
}
|
|
30388
31349
|
const contextRels = rollUpContextRelationships(parsed);
|
|
30389
31350
|
const spacing = computeAdaptiveSpacing(contextRels);
|
|
30390
|
-
const g = new
|
|
31351
|
+
const g = new dagre5.graphlib.Graph();
|
|
30391
31352
|
g.setGraph({
|
|
30392
31353
|
rankdir: "TB",
|
|
30393
31354
|
nodesep: spacing.nodesep,
|
|
@@ -30408,7 +31369,7 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
30408
31369
|
g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
|
|
30409
31370
|
}
|
|
30410
31371
|
}
|
|
30411
|
-
|
|
31372
|
+
dagre5.layout(g);
|
|
30412
31373
|
reduceCrossings(
|
|
30413
31374
|
g,
|
|
30414
31375
|
validRels.map((r) => ({ source: r.sourceName, target: r.targetName }))
|
|
@@ -30475,8 +31436,8 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
30475
31436
|
}
|
|
30476
31437
|
}
|
|
30477
31438
|
if (nodes.length > 0) {
|
|
30478
|
-
const shiftX =
|
|
30479
|
-
const shiftY =
|
|
31439
|
+
const shiftX = MARGIN6 - minX;
|
|
31440
|
+
const shiftY = MARGIN6 - minY;
|
|
30480
31441
|
for (const node of nodes) {
|
|
30481
31442
|
node.x += shiftX;
|
|
30482
31443
|
node.y += shiftY;
|
|
@@ -30488,12 +31449,12 @@ function layoutC4Context(parsed, activeTagGroup) {
|
|
|
30488
31449
|
}
|
|
30489
31450
|
}
|
|
30490
31451
|
}
|
|
30491
|
-
let totalWidth = nodes.length > 0 ? maxX - minX +
|
|
30492
|
-
let totalHeight = nodes.length > 0 ? maxY - minY +
|
|
31452
|
+
let totalWidth = nodes.length > 0 ? maxX - minX + MARGIN6 * 2 : 0;
|
|
31453
|
+
let totalHeight = nodes.length > 0 ? maxY - minY + MARGIN6 * 2 : 0;
|
|
30493
31454
|
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
30494
31455
|
if (legendGroups.length > 0) {
|
|
30495
|
-
const legendY = totalHeight +
|
|
30496
|
-
let legendX =
|
|
31456
|
+
const legendY = totalHeight + MARGIN6;
|
|
31457
|
+
let legendX = MARGIN6;
|
|
30497
31458
|
for (const lg of legendGroups) {
|
|
30498
31459
|
lg.x = legendX;
|
|
30499
31460
|
lg.y = legendY;
|
|
@@ -30586,7 +31547,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
30586
31547
|
}
|
|
30587
31548
|
}
|
|
30588
31549
|
const hasGroups = elementToGroup.size > 0;
|
|
30589
|
-
const g = hasGroups ? new
|
|
31550
|
+
const g = hasGroups ? new dagre5.graphlib.Graph({ compound: true }) : new dagre5.graphlib.Graph();
|
|
30590
31551
|
g.setDefaultEdgeLabel(() => ({}));
|
|
30591
31552
|
if (hasGroups) {
|
|
30592
31553
|
const seenGroups = /* @__PURE__ */ new Set();
|
|
@@ -30664,7 +31625,7 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
30664
31625
|
g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
|
|
30665
31626
|
}
|
|
30666
31627
|
}
|
|
30667
|
-
|
|
31628
|
+
dagre5.layout(g);
|
|
30668
31629
|
const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
|
|
30669
31630
|
reduceCrossings(
|
|
30670
31631
|
g,
|
|
@@ -30825,8 +31786,8 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
30825
31786
|
if (pt.y > maxY) maxY = pt.y;
|
|
30826
31787
|
}
|
|
30827
31788
|
}
|
|
30828
|
-
const shiftX =
|
|
30829
|
-
const shiftY =
|
|
31789
|
+
const shiftX = MARGIN6 - minX;
|
|
31790
|
+
const shiftY = MARGIN6 - minY;
|
|
30830
31791
|
for (const node of nodes) {
|
|
30831
31792
|
node.x += shiftX;
|
|
30832
31793
|
node.y += shiftY;
|
|
@@ -30843,12 +31804,12 @@ function layoutC4Containers(parsed, systemName, activeTagGroup) {
|
|
|
30843
31804
|
pt.y += shiftY;
|
|
30844
31805
|
}
|
|
30845
31806
|
}
|
|
30846
|
-
let totalWidth = maxX - minX +
|
|
30847
|
-
let totalHeight = maxY - minY +
|
|
31807
|
+
let totalWidth = maxX - minX + MARGIN6 * 2;
|
|
31808
|
+
let totalHeight = maxY - minY + MARGIN6 * 2;
|
|
30848
31809
|
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
30849
31810
|
if (legendGroups.length > 0) {
|
|
30850
|
-
const legendY = totalHeight +
|
|
30851
|
-
let legendX =
|
|
31811
|
+
const legendY = totalHeight + MARGIN6;
|
|
31812
|
+
let legendX = MARGIN6;
|
|
30852
31813
|
for (const lg of legendGroups) {
|
|
30853
31814
|
lg.x = legendX;
|
|
30854
31815
|
lg.y = legendY;
|
|
@@ -30993,7 +31954,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
30993
31954
|
}
|
|
30994
31955
|
}
|
|
30995
31956
|
const hasGroups = elementToGroup.size > 0;
|
|
30996
|
-
const g = hasGroups ? new
|
|
31957
|
+
const g = hasGroups ? new dagre5.graphlib.Graph({ compound: true }) : new dagre5.graphlib.Graph();
|
|
30997
31958
|
g.setDefaultEdgeLabel(() => ({}));
|
|
30998
31959
|
if (hasGroups) {
|
|
30999
31960
|
const seenGroups = /* @__PURE__ */ new Set();
|
|
@@ -31077,7 +32038,7 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
31077
32038
|
g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
|
|
31078
32039
|
}
|
|
31079
32040
|
}
|
|
31080
|
-
|
|
32041
|
+
dagre5.layout(g);
|
|
31081
32042
|
const nodeGroupMap = hasGroups ? new Map([...elementToGroup.entries()].map(([k, v]) => [k, v.name])) : void 0;
|
|
31082
32043
|
reduceCrossings(
|
|
31083
32044
|
g,
|
|
@@ -31240,8 +32201,8 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
31240
32201
|
if (pt.y > maxY) maxY = pt.y;
|
|
31241
32202
|
}
|
|
31242
32203
|
}
|
|
31243
|
-
const shiftX =
|
|
31244
|
-
const shiftY =
|
|
32204
|
+
const shiftX = MARGIN6 - minX;
|
|
32205
|
+
const shiftY = MARGIN6 - minY;
|
|
31245
32206
|
for (const node of nodes) {
|
|
31246
32207
|
node.x += shiftX;
|
|
31247
32208
|
node.y += shiftY;
|
|
@@ -31258,12 +32219,12 @@ function layoutC4Components(parsed, systemName, containerName, activeTagGroup) {
|
|
|
31258
32219
|
pt.y += shiftY;
|
|
31259
32220
|
}
|
|
31260
32221
|
}
|
|
31261
|
-
let totalWidth = maxX - minX +
|
|
31262
|
-
let totalHeight = maxY - minY +
|
|
32222
|
+
let totalWidth = maxX - minX + MARGIN6 * 2;
|
|
32223
|
+
let totalHeight = maxY - minY + MARGIN6 * 2;
|
|
31263
32224
|
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
31264
32225
|
if (legendGroups.length > 0) {
|
|
31265
|
-
const legendY = totalHeight +
|
|
31266
|
-
let legendX =
|
|
32226
|
+
const legendY = totalHeight + MARGIN6;
|
|
32227
|
+
let legendX = MARGIN6;
|
|
31267
32228
|
for (const lg of legendGroups) {
|
|
31268
32229
|
lg.x = legendX;
|
|
31269
32230
|
lg.y = legendY;
|
|
@@ -31369,7 +32330,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31369
32330
|
for (const r of refEntries) {
|
|
31370
32331
|
nameToElement.set(r.element.name, r.element);
|
|
31371
32332
|
}
|
|
31372
|
-
const g = new
|
|
32333
|
+
const g = new dagre5.graphlib.Graph({ compound: true });
|
|
31373
32334
|
g.setDefaultEdgeLabel(() => ({}));
|
|
31374
32335
|
for (const [infraId] of infraIds) {
|
|
31375
32336
|
g.setNode(infraId, {});
|
|
@@ -31413,7 +32374,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31413
32374
|
g.setEdge(rel.sourceName, rel.targetName, { label: rel.label ?? "" });
|
|
31414
32375
|
}
|
|
31415
32376
|
}
|
|
31416
|
-
|
|
32377
|
+
dagre5.layout(g);
|
|
31417
32378
|
const nodeInfraMap = /* @__PURE__ */ new Map();
|
|
31418
32379
|
for (const r of refEntries) nodeInfraMap.set(r.element.name, r.infraId);
|
|
31419
32380
|
reduceCrossings(
|
|
@@ -31551,8 +32512,8 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31551
32512
|
if (pt.y > maxY) maxY = pt.y;
|
|
31552
32513
|
}
|
|
31553
32514
|
}
|
|
31554
|
-
const shiftX =
|
|
31555
|
-
const shiftY =
|
|
32515
|
+
const shiftX = MARGIN6 - minX;
|
|
32516
|
+
const shiftY = MARGIN6 - minY;
|
|
31556
32517
|
for (const node of nodes) {
|
|
31557
32518
|
node.x += shiftX;
|
|
31558
32519
|
node.y += shiftY;
|
|
@@ -31567,12 +32528,12 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31567
32528
|
pt.y += shiftY;
|
|
31568
32529
|
}
|
|
31569
32530
|
}
|
|
31570
|
-
let totalWidth = maxX - minX +
|
|
31571
|
-
let totalHeight = maxY - minY +
|
|
32531
|
+
let totalWidth = maxX - minX + MARGIN6 * 2;
|
|
32532
|
+
let totalHeight = maxY - minY + MARGIN6 * 2;
|
|
31572
32533
|
const legendGroups = computeLegendGroups3(parsed.tagGroups);
|
|
31573
32534
|
if (legendGroups.length > 0) {
|
|
31574
|
-
const legendY = totalHeight +
|
|
31575
|
-
let legendX =
|
|
32535
|
+
const legendY = totalHeight + MARGIN6;
|
|
32536
|
+
let legendX = MARGIN6;
|
|
31576
32537
|
for (const lg of legendGroups) {
|
|
31577
32538
|
lg.x = legendX;
|
|
31578
32539
|
lg.y = legendY;
|
|
@@ -31592,7 +32553,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
|
|
|
31592
32553
|
height: totalHeight
|
|
31593
32554
|
};
|
|
31594
32555
|
}
|
|
31595
|
-
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,
|
|
32556
|
+
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;
|
|
31596
32557
|
var init_layout8 = __esm({
|
|
31597
32558
|
"src/c4/layout.ts"() {
|
|
31598
32559
|
"use strict";
|
|
@@ -31612,7 +32573,7 @@ var init_layout8 = __esm({
|
|
|
31612
32573
|
CARD_H_PAD3 = 20;
|
|
31613
32574
|
META_LINE_HEIGHT5 = 16;
|
|
31614
32575
|
META_FONT_SIZE5 = 11;
|
|
31615
|
-
|
|
32576
|
+
MARGIN6 = 40;
|
|
31616
32577
|
BOUNDARY_PAD = 40;
|
|
31617
32578
|
GROUP_BOUNDARY_PAD = 24;
|
|
31618
32579
|
EDGE_NODE_COLLISION_WEIGHT = 5e3;
|
|
@@ -32647,7 +33608,7 @@ var layout_exports9 = {};
|
|
|
32647
33608
|
__export(layout_exports9, {
|
|
32648
33609
|
layoutGraph: () => layoutGraph
|
|
32649
33610
|
});
|
|
32650
|
-
import
|
|
33611
|
+
import dagre6 from "@dagrejs/dagre";
|
|
32651
33612
|
function computeNodeWidth(label, shape) {
|
|
32652
33613
|
if (shape === "pseudostate") return 24;
|
|
32653
33614
|
const base = Math.max(120, label.length * 9 + 40);
|
|
@@ -32681,7 +33642,7 @@ function layoutGraph(graph, options) {
|
|
|
32681
33642
|
if (allNodes.length === 0) {
|
|
32682
33643
|
return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
|
|
32683
33644
|
}
|
|
32684
|
-
const g = new
|
|
33645
|
+
const g = new dagre6.graphlib.Graph({ compound: true });
|
|
32685
33646
|
g.setGraph({
|
|
32686
33647
|
rankdir: graph.direction,
|
|
32687
33648
|
nodesep: 50,
|
|
@@ -32732,7 +33693,7 @@ function layoutGraph(graph, options) {
|
|
|
32732
33693
|
label: edge.label ?? ""
|
|
32733
33694
|
});
|
|
32734
33695
|
}
|
|
32735
|
-
|
|
33696
|
+
dagre6.layout(g);
|
|
32736
33697
|
const collapsedGroupIds = collapsedChildCounts ? new Set(collapsedChildCounts.keys()) : /* @__PURE__ */ new Set();
|
|
32737
33698
|
const basePositioned = allNodes.map((node) => {
|
|
32738
33699
|
const pos = g.node(node.id);
|
|
@@ -34436,7 +35397,7 @@ __export(layout_exports10, {
|
|
|
34436
35397
|
layoutInfra: () => layoutInfra,
|
|
34437
35398
|
separateGroups: () => separateGroups
|
|
34438
35399
|
});
|
|
34439
|
-
import
|
|
35400
|
+
import dagre7 from "@dagrejs/dagre";
|
|
34440
35401
|
function countDisplayProps(node, expanded, options) {
|
|
34441
35402
|
if (!expanded) return 0;
|
|
34442
35403
|
let count = node.properties.filter((p) => DISPLAY_KEYS.has(p.key)).length;
|
|
@@ -34738,7 +35699,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
34738
35699
|
};
|
|
34739
35700
|
}
|
|
34740
35701
|
const isLR = computed.direction !== "TB";
|
|
34741
|
-
const g = new
|
|
35702
|
+
const g = new dagre7.graphlib.Graph();
|
|
34742
35703
|
g.setGraph({
|
|
34743
35704
|
rankdir: computed.direction === "TB" ? "TB" : "LR",
|
|
34744
35705
|
nodesep: isLR ? 70 : 60,
|
|
@@ -34789,7 +35750,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
|
|
|
34789
35750
|
g.setEdge(edge.sourceId, edge.targetId, { label: edge.label });
|
|
34790
35751
|
}
|
|
34791
35752
|
}
|
|
34792
|
-
|
|
35753
|
+
dagre7.layout(g);
|
|
34793
35754
|
const layoutNodes = computed.nodes.map(
|
|
34794
35755
|
(node) => {
|
|
34795
35756
|
const pos = g.node(node.id);
|
|
@@ -36531,17 +37492,17 @@ function mulberry32(seed) {
|
|
|
36531
37492
|
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
36532
37493
|
};
|
|
36533
37494
|
}
|
|
36534
|
-
function standardNormal(
|
|
37495
|
+
function standardNormal(rng3) {
|
|
36535
37496
|
let u = 0;
|
|
36536
37497
|
let v = 0;
|
|
36537
|
-
while (u === 0) u =
|
|
36538
|
-
while (v === 0) v =
|
|
37498
|
+
while (u === 0) u = rng3();
|
|
37499
|
+
while (v === 0) v = rng3();
|
|
36539
37500
|
return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
|
|
36540
37501
|
}
|
|
36541
|
-
function gamma(shape,
|
|
37502
|
+
function gamma(shape, rng3) {
|
|
36542
37503
|
if (shape < 1) {
|
|
36543
|
-
const u =
|
|
36544
|
-
return gamma(shape + 1,
|
|
37504
|
+
const u = rng3();
|
|
37505
|
+
return gamma(shape + 1, rng3) * Math.pow(u, 1 / shape);
|
|
36545
37506
|
}
|
|
36546
37507
|
const d = shape - 1 / 3;
|
|
36547
37508
|
const c = 1 / Math.sqrt(9 * d);
|
|
@@ -36549,29 +37510,29 @@ function gamma(shape, rng) {
|
|
|
36549
37510
|
let x;
|
|
36550
37511
|
let v;
|
|
36551
37512
|
do {
|
|
36552
|
-
x = standardNormal(
|
|
37513
|
+
x = standardNormal(rng3);
|
|
36553
37514
|
v = 1 + c * x;
|
|
36554
37515
|
} while (v <= 0);
|
|
36555
37516
|
v = v * v * v;
|
|
36556
|
-
const u =
|
|
37517
|
+
const u = rng3();
|
|
36557
37518
|
if (u < 1 - 0.0331 * x * x * x * x) return d * v;
|
|
36558
37519
|
if (Math.log(u) < x * x / 2 + d * (1 - v + Math.log(v))) return d * v;
|
|
36559
37520
|
}
|
|
36560
37521
|
}
|
|
36561
|
-
function sampleBeta(alpha, beta,
|
|
36562
|
-
const x = gamma(alpha,
|
|
36563
|
-
const y = gamma(beta,
|
|
37522
|
+
function sampleBeta(alpha, beta, rng3) {
|
|
37523
|
+
const x = gamma(alpha, rng3);
|
|
37524
|
+
const y = gamma(beta, rng3);
|
|
36564
37525
|
return x / (x + y);
|
|
36565
37526
|
}
|
|
36566
|
-
function sampleBetaPert(o, m, p,
|
|
37527
|
+
function sampleBetaPert(o, m, p, rng3) {
|
|
36567
37528
|
if (o === p) return m;
|
|
36568
37529
|
const range = p - o;
|
|
36569
37530
|
const alpha = 1 + 4 * (m - o) / range;
|
|
36570
37531
|
const beta = 1 + 4 * (p - m) / range;
|
|
36571
|
-
return o + sampleBeta(alpha, beta,
|
|
37532
|
+
return o + sampleBeta(alpha, beta, rng3) * range;
|
|
36572
37533
|
}
|
|
36573
37534
|
function simulate(resolved, expanded, _predecessors, _successors, topo, terminals, poisoned, opts) {
|
|
36574
|
-
const
|
|
37535
|
+
const rng3 = mulberry32(opts.seed);
|
|
36575
37536
|
const expById = /* @__PURE__ */ new Map();
|
|
36576
37537
|
for (const e of expanded) expById.set(e.id, e);
|
|
36577
37538
|
const completionTimes = new Array(opts.trials);
|
|
@@ -36594,7 +37555,7 @@ function simulate(resolved, expanded, _predecessors, _successors, topo, terminal
|
|
|
36594
37555
|
} else if (exp.o === exp.p) {
|
|
36595
37556
|
duration = exp.m;
|
|
36596
37557
|
} else {
|
|
36597
|
-
duration = sampleBetaPert(exp.o, exp.m, exp.p,
|
|
37558
|
+
duration = sampleBetaPert(exp.o, exp.m, exp.p, rng3);
|
|
36598
37559
|
}
|
|
36599
37560
|
let esLower = 0;
|
|
36600
37561
|
let efLower = -Infinity;
|
|
@@ -37583,7 +38544,7 @@ __export(layout_exports11, {
|
|
|
37583
38544
|
layoutPert: () => layoutPert,
|
|
37584
38545
|
relayoutPert: () => relayoutPert
|
|
37585
38546
|
});
|
|
37586
|
-
import
|
|
38547
|
+
import dagre8 from "@dagrejs/dagre";
|
|
37587
38548
|
function computeNodeSizing(resolved) {
|
|
37588
38549
|
const unit = resolved.options.timeUnit;
|
|
37589
38550
|
const sprintMode = resolved.options.sprintMode;
|
|
@@ -37701,7 +38662,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
|
|
|
37701
38662
|
}
|
|
37702
38663
|
}
|
|
37703
38664
|
const dagreId = (id) => memberToGroup.get(id) ?? id;
|
|
37704
|
-
const g = new
|
|
38665
|
+
const g = new dagre8.graphlib.Graph();
|
|
37705
38666
|
g.setGraph({
|
|
37706
38667
|
rankdir: resolved.options.direction,
|
|
37707
38668
|
nodesep: 50,
|
|
@@ -37740,7 +38701,7 @@ function relayoutPert(resolved, overrides, collapsedGroupIds = /* @__PURE__ */ n
|
|
|
37740
38701
|
seenEdges.add(k);
|
|
37741
38702
|
g.setEdge(src, tgt, {});
|
|
37742
38703
|
}
|
|
37743
|
-
|
|
38704
|
+
dagre8.layout(g);
|
|
37744
38705
|
const swimApplied = applySwimLanes(
|
|
37745
38706
|
g,
|
|
37746
38707
|
resolved,
|
|
@@ -38086,7 +39047,7 @@ function reduceCrossings2(g, direction) {
|
|
|
38086
39047
|
buckets.get(key).push(id);
|
|
38087
39048
|
}
|
|
38088
39049
|
const edges = g.edges().map((e) => ({ v: e.v, w: e.w }));
|
|
38089
|
-
const
|
|
39050
|
+
const countCrossings = () => {
|
|
38090
39051
|
let total = 0;
|
|
38091
39052
|
for (let i = 0; i < edges.length; i++) {
|
|
38092
39053
|
const a = edges[i];
|
|
@@ -38099,13 +39060,13 @@ function reduceCrossings2(g, direction) {
|
|
|
38099
39060
|
const b1 = g.node(b.v);
|
|
38100
39061
|
const b2 = g.node(b.w);
|
|
38101
39062
|
if (!b1 || !b2) continue;
|
|
38102
|
-
if (
|
|
39063
|
+
if (segmentsCross(a1, a2, b1, b2)) total++;
|
|
38103
39064
|
}
|
|
38104
39065
|
}
|
|
38105
39066
|
return total;
|
|
38106
39067
|
};
|
|
38107
39068
|
const MAX_ITER = 8;
|
|
38108
|
-
let baseline =
|
|
39069
|
+
let baseline = countCrossings();
|
|
38109
39070
|
if (baseline === 0) return;
|
|
38110
39071
|
for (let iter = 0; iter < MAX_ITER; iter++) {
|
|
38111
39072
|
let improved = false;
|
|
@@ -38123,7 +39084,7 @@ function reduceCrossings2(g, direction) {
|
|
|
38123
39084
|
const bv = bn[slotAxis];
|
|
38124
39085
|
an[slotAxis] = bv;
|
|
38125
39086
|
bn[slotAxis] = av;
|
|
38126
|
-
const after =
|
|
39087
|
+
const after = countCrossings();
|
|
38127
39088
|
if (after < baseline) {
|
|
38128
39089
|
baseline = after;
|
|
38129
39090
|
improved = true;
|
|
@@ -38144,7 +39105,7 @@ function reduceCrossings2(g, direction) {
|
|
|
38144
39105
|
data.points = smoothEdge(src, tgt, direction);
|
|
38145
39106
|
}
|
|
38146
39107
|
}
|
|
38147
|
-
function
|
|
39108
|
+
function segmentsCross(a1, a2, b1, b2) {
|
|
38148
39109
|
const ccw = (p, q, r) => (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x);
|
|
38149
39110
|
const d1 = ccw(b1, b2, a1);
|
|
38150
39111
|
const d2 = ccw(b1, b2, a2);
|
|
@@ -47685,10 +48646,10 @@ function tierBand(maxSpanDeg) {
|
|
|
47685
48646
|
}
|
|
47686
48647
|
function labelBudget(width, height, band) {
|
|
47687
48648
|
const bandCap = {
|
|
47688
|
-
world:
|
|
47689
|
-
continental:
|
|
47690
|
-
regional:
|
|
47691
|
-
local:
|
|
48649
|
+
world: 7,
|
|
48650
|
+
continental: 6,
|
|
48651
|
+
regional: 5,
|
|
48652
|
+
local: 4
|
|
47692
48653
|
};
|
|
47693
48654
|
const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
|
|
47694
48655
|
return Math.max(0, Math.min(area2, bandCap[band]));
|
|
@@ -47821,8 +48782,11 @@ function placeContextLabels(args) {
|
|
|
47821
48782
|
color: waterColor,
|
|
47822
48783
|
fontSize: FONT,
|
|
47823
48784
|
// water names keep the base font (no footprint to scale on)
|
|
47824
|
-
//
|
|
47825
|
-
|
|
48785
|
+
// Orientation-value bands (lower = earlier): MAJOR water (oceans + major
|
|
48786
|
+
// seas, tier ≤ 1) leads at `tier*10+kind` (0..~16); MINOR water (tier ≥ 2:
|
|
48787
|
+
// smaller seas, bays, gulfs, straits) drops to band 2 (2000+) — BELOW the
|
|
48788
|
+
// country band (1000+), so a big country outranks a minor basin.
|
|
48789
|
+
sort: (tier <= 1 ? 0 : 2e3) + tier * 10 + KIND_ORDER[kind]
|
|
47826
48790
|
});
|
|
47827
48791
|
}
|
|
47828
48792
|
const ranked = countries.map((c) => {
|
|
@@ -47835,7 +48799,7 @@ function placeContextLabels(args) {
|
|
|
47835
48799
|
let ci = 0;
|
|
47836
48800
|
for (const r of ranked) {
|
|
47837
48801
|
const { c, w, h, area: area2 } = r;
|
|
47838
|
-
if (w > width * 0.66
|
|
48802
|
+
if (!c.curatedAnchor && w > width * 0.66 && h > height * 0.66) continue;
|
|
47839
48803
|
if (!insideViewport(c.anchor, width, height)) continue;
|
|
47840
48804
|
const sizeFrac = Math.sqrt(area2) / canvasLinear;
|
|
47841
48805
|
const t = Math.min(
|
|
@@ -47860,15 +48824,23 @@ function placeContextLabels(args) {
|
|
|
47860
48824
|
letterSpacing: 0,
|
|
47861
48825
|
color,
|
|
47862
48826
|
fontSize,
|
|
47863
|
-
//
|
|
47864
|
-
|
|
48827
|
+
// Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
|
|
48828
|
+
// below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
|
|
48829
|
+
// (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
|
|
48830
|
+
// Campeche) yet never displaces an ocean. Larger area = earlier within the
|
|
48831
|
+
// band (`ci` is the area-desc rank index).
|
|
48832
|
+
sort: 1e3 + ci++
|
|
47865
48833
|
});
|
|
47866
48834
|
}
|
|
47867
48835
|
candidates.sort((a, b) => a.sort - b.sort);
|
|
47868
48836
|
const placed = [];
|
|
47869
48837
|
const placedRects = [];
|
|
48838
|
+
const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
|
|
48839
|
+
const waterCap = budget - Math.min(2, countryCount);
|
|
48840
|
+
let waterPlaced = 0;
|
|
47870
48841
|
for (const cand of candidates) {
|
|
47871
48842
|
if (placed.length >= budget) break;
|
|
48843
|
+
if (cand.italic && waterPlaced >= waterCap) continue;
|
|
47872
48844
|
const rect = rectAround(
|
|
47873
48845
|
cand.cx,
|
|
47874
48846
|
cand.cy,
|
|
@@ -47895,6 +48867,7 @@ function placeContextLabels(args) {
|
|
|
47895
48867
|
if (collides(rect)) continue;
|
|
47896
48868
|
if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
|
|
47897
48869
|
placedRects.push(rect);
|
|
48870
|
+
if (cand.italic) waterPlaced++;
|
|
47898
48871
|
placed.push({
|
|
47899
48872
|
x: cand.cx,
|
|
47900
48873
|
y: cand.cy,
|
|
@@ -49868,16 +50841,16 @@ function layoutMap(resolved, data, size, opts) {
|
|
|
49868
50841
|
countryCandidates.push({
|
|
49869
50842
|
name: f.properties?.name ?? iso,
|
|
49870
50843
|
bbox: [x0, y0, x1, y1],
|
|
49871
|
-
anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null
|
|
50844
|
+
anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
|
|
50845
|
+
curatedAnchor: !!anchorLngLat
|
|
49872
50846
|
});
|
|
49873
50847
|
}
|
|
49874
50848
|
const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
|
|
49875
50849
|
(id) => id.startsWith("US-")
|
|
49876
50850
|
);
|
|
49877
50851
|
if (usLayer && framedStateContainers) {
|
|
49878
|
-
const containerSet = new Set(resolved.poiFrameContainers);
|
|
49879
50852
|
for (const [iso, f] of usLayer) {
|
|
49880
|
-
if (
|
|
50853
|
+
if (regionById.has(iso)) continue;
|
|
49881
50854
|
const viewF = cullFeatureToView(f);
|
|
49882
50855
|
if (!viewF) continue;
|
|
49883
50856
|
const b = path.bounds(viewF);
|
|
@@ -50005,8 +50978,15 @@ var init_layout15 = __esm({
|
|
|
50005
50978
|
W_MAX = 8;
|
|
50006
50979
|
FONT2 = 11;
|
|
50007
50980
|
WORLD_LABEL_ANCHORS = {
|
|
50008
|
-
US: [-98.5, 39.5]
|
|
50981
|
+
US: [-98.5, 39.5],
|
|
50009
50982
|
// CONUS geographic centre (near Lebanon, Kansas)
|
|
50983
|
+
// Russia crosses the antimeridian (Chukotka at ~170°W), so on a non-global
|
|
50984
|
+
// (e.g. Europe) projection its geometry smears across the whole frame and the
|
|
50985
|
+
// area-weighted centroid lands mid-map (over Europe) — useless as a label
|
|
50986
|
+
// anchor. Pin it to European Russia (~Volga) so a Europe view labels visible
|
|
50987
|
+
// western Russia on its eastern margin; on a world view this still sits over
|
|
50988
|
+
// Russian land. (See the curated-anchor smear-gate bypass in context-labels.)
|
|
50989
|
+
RU: [45, 58]
|
|
50010
50990
|
};
|
|
50011
50991
|
MAX_CLUSTER_EXTENT_FACTOR = 0.18;
|
|
50012
50992
|
MAX_COLUMN_ROWS = 7;
|
|
@@ -60716,7 +61696,7 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
|
|
|
60716
61696
|
|
|
60717
61697
|
// src/auto/index.ts
|
|
60718
61698
|
init_safe_href();
|
|
60719
|
-
var VERSION = "0.
|
|
61699
|
+
var VERSION = "0.28.0";
|
|
60720
61700
|
var DEFAULTS = {
|
|
60721
61701
|
theme: "auto",
|
|
60722
61702
|
palette: "slate",
|