Skip to main content

ggplot_rs/
graph_layout.rs

1//! Layered (Sugiyama-style) layout of a directed graph of boxes — nodes in
2//! layers (stages) along one axis, ordered across it to keep crossings low.
3//!
4//! The steps: cycles broken by a DFS in input order; ranks by longest path
5//! (a pinned layer wins; sources pulled up to just before their nearest
6//! successor); a dummy vertex per layer a long edge passes; barycenter sweeps
7//! plus adjacent transposition, the fewest crossings kept (counted per layer
8//! pair with a Fenwick tree); a weighted least-squares (isotonic) placement
9//! along each layer; ports spread along a box's side; optional swimlanes as
10//! bands. A graph whose every node is pinned in both axes is drawn on a grid
11//! where it is placed, with straight edges.
12//!
13//! Node sizes come from the caller (the box contents are the drawing's
14//! business); the result is in px with the origin at the top-left margin.
15//! Moved here from anofox-visualization's `::DIAGRAM` (sipemu/ggplot-rs#497,
16//! DataZooDE/anofox-visualization#1153).
17
18/// Which way the layers run.
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
20pub enum Direction {
21    /// Layers left to right (the default).
22    #[default]
23    LeftRight,
24    /// Layers top to bottom.
25    TopBottom,
26}
27
28/// A node: its box size, its swimlane, and optional pins (a layer, and a
29/// position across it; both pinned on every node → the grid layout).
30#[derive(Clone, Debug, Default)]
31pub struct GraphNode {
32    pub w: f64,
33    pub h: f64,
34    /// Index of the node's swimlane (< [`LayeredParams::lanes`]), if any.
35    pub lane: Option<usize>,
36    /// Pinned layer and pinned position (grid units in the explicit layout).
37    pub pin: (Option<f64>, Option<f64>),
38}
39
40/// A directed edge; `label_w` is the width (px) of its label, if it has one:
41/// left to right, the gap between two layers is at least as wide as the
42/// labels that cross it.
43#[derive(Clone, Debug, Default)]
44pub struct GraphEdge {
45    pub from: usize,
46    pub to: usize,
47    pub label_w: Option<f64>,
48}
49
50/// Spacing and options; the defaults are anofox-visualization's diagram.
51#[derive(Clone, Debug)]
52pub struct LayeredParams {
53    pub direction: Direction,
54    /// Number of swimlanes (nodes carry the index).
55    pub lanes: usize,
56    pub margin: f64,
57    /// Arrowhead length (an edge label's gap leaves room for two).
58    pub arrow: f64,
59    /// A self-loop's reach beyond its box.
60    pub loop_size: f64,
61    /// Extra space between swimlanes, and a lane's label strip.
62    pub lane_gap: f64,
63    pub lane_head: f64,
64}
65
66impl Default for LayeredParams {
67    fn default() -> Self {
68        LayeredParams {
69            direction: Direction::LeftRight,
70            lanes: 0,
71            margin: 10.0,
72            arrow: 7.0,
73            loop_size: 16.0,
74            lane_gap: 22.0,
75            lane_head: 16.0,
76        }
77    }
78}
79
80/// A placed node: centre, size, layer, and rank in reading order (layer by
81/// layer, then along it).
82#[derive(Clone, Debug, PartialEq)]
83pub struct PlacedNode {
84    pub cx: f64,
85    pub cy: f64,
86    pub w: f64,
87    pub h: f64,
88    pub layer: usize,
89    pub order: usize,
90}
91
92/// The laid-out graph.
93#[derive(Clone, Debug, PartialEq)]
94pub struct GraphLayout {
95    pub w: f64,
96    pub h: f64,
97    pub nodes: Vec<PlacedNode>,
98    /// Per edge (input order): its points, source → target.
99    pub edges: Vec<Vec<(f64, f64)>>,
100    /// Edges drawn against the layer order (they close a cycle).
101    pub back: Vec<bool>,
102    pub layers: usize,
103    /// Swimlane bands: (lane, x0, y0, x1, y1).
104    pub bands: Vec<(usize, f64, f64, f64, f64)>,
105}
106
107/// Lay out `nodes` and `edges` in layers (see the module docs).
108pub fn layered(nodes: &[GraphNode], edges: &[GraphEdge], p: &LayeredParams) -> GraphLayout {
109    let explicit =
110        !nodes.is_empty() && nodes.iter().all(|n| n.pin.0.is_some() && n.pin.1.is_some());
111    if explicit {
112        layout_explicit(nodes, edges, p)
113    } else {
114        layout(nodes, edges, p)
115    }
116}
117
118/// A DAG edge: `(u, v, edge index, reversed)`.
119type DagEdge = (usize, usize, usize, bool);
120
121/// The cycle-free orientation (a DFS in input order reverses every edge
122/// that closes a cycle), and the self-loops (edge indices).
123fn break_cycles(n: usize, edges: &[GraphEdge]) -> (Vec<DagEdge>, Vec<usize>) {
124    let mut out: Vec<Vec<(usize, usize)>> = vec![Vec::new(); n];
125    let mut loops = Vec::new();
126    for (k, e) in edges.iter().enumerate() {
127        if e.from == e.to {
128            loops.push(k);
129        } else {
130            out[e.from].push((e.to, k));
131        }
132    }
133    let mut state = vec![0u8; n]; // 0 new, 1 on the DFS stack, 2 done
134    let mut rev = vec![false; edges.len()];
135    for s in 0..n {
136        if state[s] != 0 {
137            continue;
138        }
139        state[s] = 1;
140        let mut stack = vec![(s, 0usize)];
141        while let Some(top) = stack.last_mut() {
142            let v = top.0;
143            if top.1 < out[v].len() {
144                let (w, k) = out[v][top.1];
145                top.1 += 1;
146                match state[w] {
147                    0 => {
148                        state[w] = 1;
149                        stack.push((w, 0));
150                    }
151                    1 => rev[k] = true,
152                    _ => {}
153                }
154            } else {
155                state[v] = 2;
156                stack.pop();
157            }
158        }
159    }
160    let dag = edges
161        .iter()
162        .enumerate()
163        .filter(|(_, e)| e.from != e.to)
164        .map(|(k, e)| {
165            if rev[k] {
166                (e.to, e.from, k, true)
167            } else {
168                (e.from, e.to, k, false)
169            }
170        })
171        .collect();
172    (dag, loops)
173}
174
175/// `keys` ordered by `(primary, secondary, index)`, as the indices.
176fn ordered(mut keys: Vec<(f64, f64, usize)>) -> Vec<usize> {
177    keys.sort_unstable_by(|a, b| {
178        a.0.total_cmp(&b.0)
179            .then(a.1.total_cmp(&b.1))
180            .then(a.2.cmp(&b.2))
181    });
182    keys.into_iter().map(|k| k.2).collect()
183}
184
185/// Crossings between two adjacent layers (Barth–Mutzel–Jünger: the edges by
186/// their upper end, inversions of the lower ends counted with a Fenwick tree).
187fn layer_crossings(upper: &[usize], succ: &[Vec<usize>], pos: &[usize], lower_len: usize) -> u64 {
188    let mut ends: Vec<(usize, usize)> = Vec::new();
189    for &u in upper {
190        for &v in &succ[u] {
191            ends.push((pos[u], pos[v]));
192        }
193    }
194    let lower: Vec<usize> = ordered(
195        ends.iter()
196            .enumerate()
197            .map(|(i, &(u, v))| (u as f64, v as f64, i))
198            .collect(),
199    )
200    .into_iter()
201    .map(|i| ends[i].1)
202    .collect();
203    let mut tree = vec![0u64; lower_len + 1];
204    let mut c = 0u64;
205    for (seen, &p) in lower.iter().enumerate() {
206        let mut i = p + 1;
207        let mut le = 0u64;
208        while i > 0 {
209            le += tree[i];
210            i -= 1 << i.trailing_zeros();
211        }
212        c += seen as u64 - le;
213        let mut i = p + 1;
214        while i <= lower_len {
215            tree[i] += 1;
216            i += 1 << i.trailing_zeros();
217        }
218    }
219    c
220}
221
222/// Isotonic placement: positions as close as possible (weighted least
223/// squares) to `want`, in the given order, at least `gap[i]` after the
224/// previous one.
225fn place(want: &[f64], weight: &[f64], gap: &[f64]) -> Vec<f64> {
226    let n = want.len();
227    let mut off = vec![0.0; n];
228    for i in 1..n {
229        off[i] = off[i - 1] + gap[i];
230    }
231    let mut blocks: Vec<(f64, f64, usize)> = Vec::new();
232    for i in 0..n {
233        blocks.push((weight[i] * (want[i] - off[i]), weight[i], 1));
234        while blocks.len() >= 2 {
235            let (b, a) = (blocks[blocks.len() - 1], blocks[blocks.len() - 2]);
236            if a.0 / a.1 > b.0 / b.1 {
237                blocks.pop();
238                let last = blocks.last_mut().expect("two blocks");
239                *last = (a.0 + b.0, a.1 + b.1, a.2 + b.2);
240            } else {
241                break;
242            }
243        }
244    }
245    let mut out = Vec::with_capacity(n);
246    for (s, w, c) in blocks {
247        for _ in 0..c {
248            let i = out.len();
249            out.push(s / w + off[i]);
250        }
251    }
252    out
253}
254
255/// Where a line from a box's centre towards `to` leaves the box.
256fn exit(p: &PlacedNode, to: (f64, f64)) -> (f64, f64) {
257    let (dx, dy) = (to.0 - p.cx, to.1 - p.cy);
258    let sx = if dx != 0.0 {
259        p.w / 2.0 / dx.abs()
260    } else {
261        f64::INFINITY
262    };
263    let sy = if dy != 0.0 {
264        p.h / 2.0 / dy.abs()
265    } else {
266        f64::INFINITY
267    };
268    let k = sx.min(sy);
269    if k.is_finite() {
270        (p.cx + dx * k, p.cy + dy * k)
271    } else {
272        (p.cx, p.cy)
273    }
274}
275
276fn straight(pn: &[PlacedNode], u: usize, v: usize) -> Vec<(f64, f64)> {
277    let (a, b) = (&pn[u], &pn[v]);
278    vec![exit(a, (b.cx, b.cy)), exit(b, (a.cx, a.cy))]
279}
280
281/// A self-loop over (left to right) or beside (top to bottom) its box.
282fn self_loop(p: &PlacedNode, lr: bool, reach: f64) -> Vec<(f64, f64)> {
283    if lr {
284        let top = p.cy - p.h / 2.0;
285        vec![
286            (p.cx + p.w / 4.0, top),
287            (p.cx + p.w / 4.0, top - reach),
288            (p.cx - p.w / 4.0, top - reach),
289            (p.cx - p.w / 4.0, top),
290        ]
291    } else {
292        let right = p.cx + p.w / 2.0;
293        vec![
294            (right, p.cy - p.h / 4.0),
295            (right + reach, p.cy - p.h / 4.0),
296            (right + reach, p.cy + p.h / 4.0),
297            (right, p.cy + p.h / 4.0),
298        ]
299    }
300}
301
302/// Every node pinned in both axes: a grid of the widest / tallest box.
303fn layout_explicit(nodes: &[GraphNode], edges: &[GraphEdge], p: &LayeredParams) -> GraphLayout {
304    let sx = nodes.iter().map(|b| b.w).fold(0.0, f64::max) + 48.0;
305    let sy = nodes.iter().map(|b| b.h).fold(0.0, f64::max) + 24.0;
306    let pin = |v: usize| (nodes[v].pin.0.unwrap_or(0.0), nodes[v].pin.1.unwrap_or(0.0));
307    let mut xs: Vec<f64> = (0..nodes.len()).map(|v| pin(v).0).collect();
308    xs.sort_by(f64::total_cmp);
309    xs.dedup();
310    let reading = ordered((0..nodes.len()).map(|v| (pin(v).0, pin(v).1, v)).collect());
311    let mut pn: Vec<PlacedNode> = nodes
312        .iter()
313        .enumerate()
314        .map(|(v, b)| PlacedNode {
315            cx: pin(v).0 * sx,
316            cy: pin(v).1 * sy,
317            w: b.w,
318            h: b.h,
319            layer: xs.iter().position(|&x| x == pin(v).0).unwrap_or(0),
320            order: 0,
321        })
322        .collect();
323    for (i, &v) in reading.iter().enumerate() {
324        pn[v].order = i;
325    }
326    let lr = p.direction == Direction::LeftRight;
327    let pts = edges
328        .iter()
329        .map(|e| {
330            if e.from == e.to {
331                self_loop(&pn[e.from], lr, p.loop_size)
332            } else {
333                straight(&pn, e.from, e.to)
334            }
335        })
336        .collect();
337    finish(
338        pn,
339        pts,
340        vec![false; edges.len()],
341        xs.len().max(1),
342        Vec::new(),
343        p.margin,
344    )
345}
346
347fn layout(nodes: &[GraphNode], edges: &[GraphEdge], p: &LayeredParams) -> GraphLayout {
348    let n = nodes.len();
349    let (dag, loops) = break_cycles(n, edges);
350    let lr = p.direction == Direction::LeftRight;
351    let along = |b: &GraphNode| if lr { b.w } else { b.h };
352    let across = |b: &GraphNode| if lr { b.h } else { b.w };
353    let (rank_gap, node_gap) = if lr { (64.0, 20.0) } else { (48.0, 28.0) };
354    let (lane_gap, lane_head) = (p.lane_gap, p.lane_head);
355
356    // ranks: longest path in topological order (input order first), then
357    // sources pulled up to just before their nearest successor
358    let mut preds: Vec<Vec<usize>> = vec![Vec::new(); n];
359    let mut succs: Vec<Vec<usize>> = vec![Vec::new(); n];
360    for &(u, v, _, _) in &dag {
361        preds[v].push(u);
362        succs[u].push(v);
363    }
364    let mut indeg: Vec<usize> = preds.iter().map(Vec::len).collect();
365    let mut done = vec![false; n];
366    let mut topo = Vec::with_capacity(n);
367    while let Some(v) = (0..n).find(|&v| !done[v] && indeg[v] == 0) {
368        done[v] = true;
369        topo.push(v);
370        for &w in &succs[v] {
371            indeg[w] -= 1;
372        }
373    }
374    // a pinned layer wins over the longest path
375    let pinned = |v: usize| {
376        nodes[v]
377            .pin
378            .0
379            .map(|x| x.round().clamp(0.0, 1000.0) as usize)
380    };
381    let any_pin = (0..n).any(|v| pinned(v).is_some());
382    let mut rank = vec![0usize; n];
383    for &v in &topo {
384        rank[v] =
385            pinned(v).unwrap_or_else(|| preds[v].iter().map(|&q| rank[q] + 1).max().unwrap_or(0));
386    }
387    for &v in topo.iter().rev() {
388        if preds[v].is_empty() && pinned(v).is_none() {
389            if let Some(m) = succs[v].iter().map(|&s| rank[s]).min() {
390                rank[v] = rank[v].max(m.saturating_sub(1));
391            }
392        }
393    }
394    // drop empty ranks (pinned layers keep their numbers)
395    let mut used = vec![false; rank.iter().max().map_or(1, |m| m + 1)];
396    for &r in &rank {
397        used[r] = true;
398    }
399    let mut renumber = vec![0usize; used.len()];
400    let mut nl = 0;
401    for (r, u) in used.iter().enumerate() {
402        renumber[r] = nl;
403        nl += usize::from(*u || any_pin);
404    }
405    for r in &mut rank {
406        *r = renumber[*r];
407    }
408    let nl = nl.max(1);
409    // edges the pins make run within or against the layers: drawn straight
410    let (dag, free): (Vec<DagEdge>, Vec<DagEdge>) =
411        dag.into_iter().partition(|&(u, v, _, _)| rank[v] > rank[u]);
412
413    // vertices: the nodes, then one dummy per layer an edge passes
414    let mut vrank: Vec<usize> = rank.clone();
415    let mut vsucc: Vec<Vec<usize>> = vec![Vec::new(); n];
416    let mut vpred: Vec<Vec<usize>> = vec![Vec::new(); n];
417    let mut chains: Vec<Vec<usize>> = Vec::with_capacity(dag.len());
418    for &(u, v, _, _) in &dag {
419        let mut chain = vec![u];
420        let mut prev = u;
421        for r in rank[u] + 1..rank[v] {
422            let dmy = vrank.len();
423            vrank.push(r);
424            vsucc.push(Vec::new());
425            vpred.push(Vec::new());
426            vsucc[prev].push(dmy);
427            vpred[dmy].push(prev);
428            chain.push(dmy);
429            prev = dmy;
430        }
431        vsucc[prev].push(v);
432        vpred[v].push(prev);
433        chain.push(v);
434        chains.push(chain);
435    }
436    let nv = vrank.len();
437    let dummy = |v: usize| v >= n;
438    // a vertex's swimlane (unlaned nodes last; a dummy runs in its source's)
439    let mut vlane: Vec<usize> = (0..n).map(|v| nodes[v].lane.unwrap_or(p.lanes)).collect();
440    vlane.resize(nv, 0);
441    for ch in &chains {
442        for &dm in &ch[1..ch.len() - 1] {
443            vlane[dm] = vlane[ch[0]];
444        }
445    }
446    let laned = p.lanes > 0;
447
448    // initial order: DFS from the vertices by (rank, index)
449    let mut layers: Vec<Vec<usize>> = vec![Vec::new(); nl];
450    let mut seen = vec![false; nv];
451    for s in ordered((0..nv).map(|v| (vrank[v] as f64, 0.0, v)).collect()) {
452        if seen[s] {
453            continue;
454        }
455        let mut stack = vec![s];
456        while let Some(v) = stack.pop() {
457            if seen[v] {
458                continue;
459            }
460            seen[v] = true;
461            layers[vrank[v]].push(v);
462            for &w in vsucc[v].iter().rev() {
463                if !seen[w] {
464                    stack.push(w);
465                }
466            }
467        }
468    }
469    if laned {
470        for l in &mut layers {
471            *l = ordered(
472                l.iter()
473                    .enumerate()
474                    .map(|(i, &v)| (vlane[v] as f64, i as f64, v))
475                    .collect(),
476            );
477        }
478    }
479    let mut pos = vec![0usize; nv];
480    let index = |layers: &[Vec<usize>], pos: &mut [usize]| {
481        for l in layers {
482            for (i, &v) in l.iter().enumerate() {
483                pos[v] = i;
484            }
485        }
486    };
487    index(&layers, &mut pos);
488    let crossings = |layers: &[Vec<usize>], pos: &[usize]| -> u64 {
489        (0..layers.len().saturating_sub(1))
490            .map(|r| layer_crossings(&layers[r], &vsucc, pos, layers[r + 1].len()))
491            .sum()
492    };
493
494    // crossing reduction: barycenter sweeps + adjacent transposition, best kept
495    let mut best = layers.clone();
496    let mut best_c = crossings(&layers, &pos);
497    for it in 0..8 {
498        if best_c == 0 {
499            break;
500        }
501        let down = it % 2 == 0;
502        let rs: Vec<usize> = if down {
503            (1..nl).collect()
504        } else {
505            (0..nl.saturating_sub(1)).rev().collect()
506        };
507        for r in rs {
508            let nb = if down { &vpred } else { &vsucc };
509            let keyed: Vec<(f64, f64, usize)> = layers[r]
510                .iter()
511                .map(|&v| {
512                    let k = if nb[v].is_empty() {
513                        pos[v] as f64
514                    } else {
515                        nb[v].iter().map(|&w| pos[w] as f64).sum::<f64>() / nb[v].len() as f64
516                    };
517                    (vlane[v] as f64 * 1e6 + k, pos[v] as f64, v)
518                })
519                .collect();
520            layers[r] = ordered(keyed);
521            for (i, &v) in layers[r].iter().enumerate() {
522                pos[v] = i;
523            }
524        }
525        for _ in 0..4 {
526            let mut better = false;
527            for layer in layers.iter_mut() {
528                for i in 0..layer.len().saturating_sub(1) {
529                    let (a, b) = (layer[i], layer[i + 1]);
530                    if vlane[a] != vlane[b] {
531                        continue;
532                    }
533                    let pair = |x: usize, y: usize| -> usize {
534                        let c = |l: &[Vec<usize>]| {
535                            l[x].iter()
536                                .flat_map(|&q| l[y].iter().map(move |&r| (q, r)))
537                                .filter(|&(q, r)| pos[q] > pos[r])
538                                .count()
539                        };
540                        c(&vpred) + c(&vsucc)
541                    };
542                    if pair(b, a) < pair(a, b) {
543                        layer.swap(i, i + 1);
544                        pos[a] = i + 1;
545                        pos[b] = i;
546                        better = true;
547                    }
548                }
549            }
550            if !better {
551                break;
552            }
553        }
554        let c = crossings(&layers, &pos);
555        if c < best_c {
556            best_c = c;
557            best = layers.clone();
558        }
559    }
560    let layers = best;
561    index(&layers, &mut pos);
562
563    // positions across each layer
564    let size = |v: usize| if dummy(v) { 0.0 } else { across(&nodes[v]) };
565    let gap_between = |a: usize, b: usize| -> f64 {
566        let g = match (dummy(a), dummy(b)) {
567            (true, true) => 8.0,
568            (false, false) => node_gap,
569            _ => 12.0,
570        };
571        let lane = if vlane[a] != vlane[b] {
572            lane_gap + lane_head
573        } else {
574            0.0
575        };
576        size(a) / 2.0 + g + lane + size(b) / 2.0
577    };
578    let mut y = vec![0.0f64; nv];
579    for l in &layers {
580        let mut at = 0.0;
581        for (i, &v) in l.iter().enumerate() {
582            if i > 0 {
583                at += gap_between(l[i - 1], v);
584            }
585            y[v] = at;
586        }
587        let mid = at / 2.0;
588        for &v in l {
589            y[v] -= mid;
590        }
591    }
592    let relax = |y: &mut Vec<f64>, r: usize, up: bool, down: bool| {
593        let l = &layers[r];
594        if l.is_empty() {
595            return;
596        }
597        let want: Vec<f64> = l
598            .iter()
599            .map(|&v| {
600                let mut nb: Vec<usize> = Vec::new();
601                if up {
602                    nb.extend(&vpred[v]);
603                }
604                if down {
605                    nb.extend(&vsucc[v]);
606                }
607                if nb.is_empty() {
608                    y[v]
609                } else {
610                    nb.iter().map(|&w| y[w]).sum::<f64>() / nb.len() as f64
611                }
612            })
613            .collect();
614        let weight: Vec<f64> = l
615            .iter()
616            .map(|&v| if dummy(v) { 2.0 } else { 1.0 })
617            .collect();
618        let gaps: Vec<f64> = (0..l.len())
619            .map(|i| {
620                if i == 0 {
621                    0.0
622                } else {
623                    gap_between(l[i - 1], l[i])
624                }
625            })
626            .collect();
627        for (&v, q) in l.iter().zip(place(&want, &weight, &gaps)) {
628            y[v] = q;
629        }
630    };
631    for _ in 0..6 {
632        for r in 1..nl {
633            relax(&mut y, r, true, false);
634        }
635        for r in (0..nl.saturating_sub(1)).rev() {
636            relax(&mut y, r, false, true);
637        }
638    }
639    for r in 0..nl {
640        relax(&mut y, r, true, true);
641    }
642    // swimlanes: each lane shifted clear of the one before (across all layers)
643    let mut lane_span: Vec<(f64, f64)> = Vec::new();
644    if laned {
645        let nlanes = p.lanes + 1;
646        let span = |y: &[f64], l: usize| {
647            (0..nv)
648                .filter(|&v| vlane[v] == l)
649                .map(|v| (y[v] - size(v) / 2.0, y[v] + size(v) / 2.0))
650                .fold(None, |acc: Option<(f64, f64)>, (a, b)| match acc {
651                    None => Some((a, b)),
652                    Some((x, z)) => Some((x.min(a), z.max(b))),
653                })
654        };
655        let mut floor: Option<f64> = None;
656        for l in 0..nlanes {
657            let Some((lo, _)) = span(&y, l) else {
658                lane_span.push((f64::NAN, f64::NAN));
659                continue;
660            };
661            if let Some(f) = floor {
662                let need = f + lane_gap + lane_head - lo;
663                if need > 0.0 {
664                    for v in 0..nv {
665                        if vlane[v] == l {
666                            y[v] += need;
667                        }
668                    }
669                }
670            }
671            let (lo, hi) = span(&y, l).expect("lane has vertices");
672            lane_span.push((lo, hi));
673            floor = Some(hi);
674        }
675    }
676
677    // positions along the layer axis: each layer as thick as its widest node;
678    // left to right, a gap is at least as wide as the edge labels crossing it
679    let mut thick = vec![0.0f64; nl];
680    for v in 0..n {
681        thick[rank[v]] = thick[rank[v]].max(along(&nodes[v]));
682    }
683    let mut gap: Vec<f64> = vec![rank_gap; nl];
684    if lr {
685        for e in edges {
686            let (a, b) = (rank[e.from].min(rank[e.to]), rank[e.from].max(rank[e.to]));
687            if b != a + 1 {
688                continue;
689            }
690            if let Some(w) = e.label_w {
691                gap[a] = gap[a].max(w + 2.0 * p.arrow + 18.0);
692            }
693        }
694    }
695    let mut start_at = vec![0.0f64; nl];
696    for r in 1..nl {
697        start_at[r] = start_at[r - 1] + thick[r - 1] + gap[r - 1];
698    }
699    let centre = |r: usize| start_at[r] + thick[r] / 2.0;
700    let xy = |rk: f64, o: f64| if lr { (rk, o) } else { (o, rk) };
701
702    let mut pn: Vec<PlacedNode> = Vec::with_capacity(n);
703    for (v, b) in nodes.iter().enumerate() {
704        let (cx, cy) = xy(centre(rank[v]), y[v]);
705        pn.push(PlacedNode {
706            cx,
707            cy,
708            w: b.w,
709            h: b.h,
710            layer: rank[v],
711            order: 0,
712        });
713    }
714    let reading = ordered((0..n).map(|v| (rank[v] as f64, y[v], v)).collect());
715    for (i, &v) in reading.iter().enumerate() {
716        pn[v].order = i;
717    }
718
719    // ports: a node's outgoing (incoming) chains spread along its side,
720    // sorted by where they go (come from)
721    let half_along = |v: usize| if lr { pn[v].w / 2.0 } else { pn[v].h / 2.0 };
722    let side = |v: usize| if lr { pn[v].h } else { pn[v].w };
723    let mut out_ports: Vec<Vec<usize>> = vec![Vec::new(); n];
724    let mut in_ports: Vec<Vec<usize>> = vec![Vec::new(); n];
725    for (c, ch) in chains.iter().enumerate() {
726        out_ports[ch[0]].push(c);
727        in_ports[ch[ch.len() - 1]].push(c);
728    }
729    for ports in &mut out_ports {
730        *ports = ordered(ports.iter().map(|&c| (y[chains[c][1]], 0.0, c)).collect());
731    }
732    for ports in &mut in_ports {
733        *ports = ordered(
734            ports
735                .iter()
736                .map(|&c| (y[chains[c][chains[c].len() - 2]], 0.0, c))
737                .collect(),
738        );
739    }
740    let spread = |list: &[usize], c: usize, v: usize| -> f64 {
741        let m = list.len();
742        if m <= 1 {
743            return 0.0;
744        }
745        let k = list.iter().position(|&x| x == c).unwrap_or(0);
746        let span = (side(v) * 0.6).min(14.0 * (m - 1) as f64);
747        -span / 2.0 + span * k as f64 / (m - 1) as f64
748    };
749    let mut edge_pts: Vec<Vec<(f64, f64)>> = vec![Vec::new(); edges.len()];
750    let mut back = vec![false; edges.len()];
751    for (c, ch) in chains.iter().enumerate() {
752        let (u, v, k, rev) = dag[c];
753        let mut pts = Vec::with_capacity(ch.len() * 2);
754        pts.push(xy(
755            centre(rank[u]) + half_along(u),
756            y[u] + spread(&out_ports[u], c, u),
757        ));
758        for &dm in &ch[1..ch.len() - 1] {
759            let r = vrank[dm];
760            pts.push(xy(start_at[r], y[dm]));
761            pts.push(xy(start_at[r] + thick[r], y[dm]));
762        }
763        pts.push(xy(
764            centre(rank[v]) - half_along(v),
765            y[v] + spread(&in_ports[v], c, v),
766        ));
767        if rev {
768            pts.reverse();
769        }
770        edge_pts[k] = pts;
771        back[k] = rev;
772    }
773    for k in loops {
774        edge_pts[k] = self_loop(&pn[edges[k].from], lr, p.loop_size);
775    }
776    for &(u, v, k, rev) in &free {
777        let mut pts = straight(&pn, u, v);
778        if rev {
779            pts.reverse();
780        }
781        edge_pts[k] = pts;
782        back[k] = rev;
783    }
784    // swimlane bands across every layer, with the lane's label strip
785    let total = start_at[nl - 1] + thick[nl - 1];
786    let bands = lane_span
787        .iter()
788        .enumerate()
789        .filter(|(l, (lo, _))| *l < p.lanes && lo.is_finite())
790        .map(|(l, &(lo, hi))| {
791            let (a, b) = if lr {
792                xy(-12.0, lo - lane_head - 6.0)
793            } else {
794                xy(-lane_head - 10.0, lo - 8.0)
795            };
796            let (c, e) = xy(total + 12.0, hi + 8.0);
797            (l, a, b, c, e)
798        })
799        .collect();
800    finish(pn, edge_pts, back, nl, bands, p.margin)
801}
802
803/// Move a layout to the margin and round it to half pixels.
804fn finish(
805    mut pn: Vec<PlacedNode>,
806    mut edge_pts: Vec<Vec<(f64, f64)>>,
807    back: Vec<bool>,
808    layers: usize,
809    mut bands: Vec<(usize, f64, f64, f64, f64)>,
810    margin: f64,
811) -> GraphLayout {
812    let (mut x0, mut y0, mut x1, mut y1) = (f64::MAX, f64::MAX, f64::MIN, f64::MIN);
813    for q in &pn {
814        x0 = x0.min(q.cx - q.w / 2.0);
815        x1 = x1.max(q.cx + q.w / 2.0);
816        y0 = y0.min(q.cy - q.h / 2.0);
817        y1 = y1.max(q.cy + q.h / 2.0);
818    }
819    for &(px, py) in edge_pts.iter().flatten() {
820        x0 = x0.min(px);
821        x1 = x1.max(px);
822        y0 = y0.min(py);
823        y1 = y1.max(py);
824    }
825    for &(_, a, b, c, e) in &bands {
826        x0 = x0.min(a.min(c));
827        x1 = x1.max(a.max(c));
828        y0 = y0.min(b.min(e));
829        y1 = y1.max(b.max(e));
830    }
831    if pn.is_empty() {
832        (x0, y0, x1, y1) = (0.0, 0.0, 0.0, 0.0);
833    }
834    let r = |v: f64| (v * 2.0).round() / 2.0;
835    let (dx, dy) = (margin - x0, margin - y0);
836    for q in &mut pn {
837        q.cx = r(q.cx + dx);
838        q.cy = r(q.cy + dy);
839    }
840    for pts in &mut edge_pts {
841        for pt in pts.iter_mut() {
842            *pt = (r(pt.0 + dx), r(pt.1 + dy));
843        }
844    }
845    for b in &mut bands {
846        *b = (b.0, r(b.1 + dx), r(b.2 + dy), r(b.3 + dx), r(b.4 + dy));
847    }
848    GraphLayout {
849        w: r(x1 - x0 + 2.0 * margin),
850        h: r(y1 - y0 + 2.0 * margin),
851        nodes: pn,
852        edges: edge_pts,
853        back,
854        layers,
855        bands,
856    }
857}
858
859#[cfg(test)]
860mod tests {
861    use super::*;
862
863    fn node(w: f64, h: f64) -> GraphNode {
864        GraphNode {
865            w,
866            h,
867            ..Default::default()
868        }
869    }
870    fn edge(from: usize, to: usize) -> GraphEdge {
871        GraphEdge {
872            from,
873            to,
874            label_w: None,
875        }
876    }
877
878    #[test]
879    fn a_chain_runs_left_to_right_in_layers() {
880        let l = layered(
881            &[node(100.0, 40.0), node(100.0, 40.0), node(100.0, 40.0)],
882            &[edge(0, 1), edge(1, 2)],
883            &LayeredParams::default(),
884        );
885        assert_eq!(l.layers, 3);
886        assert!(l.nodes[0].cx < l.nodes[1].cx && l.nodes[1].cx < l.nodes[2].cx);
887        assert_eq!(l.nodes[0].cy, l.nodes[1].cy);
888        // an edge leaves its source's right side and enters its target's left
889        assert_eq!(l.edges[0][0].0, l.nodes[0].cx + 50.0);
890        assert_eq!(l.edges[0].last().unwrap().0, l.nodes[1].cx - 50.0);
891    }
892
893    #[test]
894    fn a_cycle_is_broken_and_marked_back() {
895        let l = layered(
896            &[node(80.0, 30.0), node(80.0, 30.0)],
897            &[edge(0, 1), edge(1, 0)],
898            &LayeredParams::default(),
899        );
900        assert_eq!(l.back, vec![false, true]);
901        assert_eq!(l.layers, 2);
902    }
903
904    #[test]
905    fn crossings_are_removed_where_possible() {
906        // a→d, b→c drawn in input order would cross; the layout uncrosses them
907        let ns = vec![node(60.0, 30.0); 4];
908        let l = layered(&ns, &[edge(0, 3), edge(1, 2)], &LayeredParams::default());
909        let (a, b, c, d) = (&l.nodes[0], &l.nodes[1], &l.nodes[2], &l.nodes[3]);
910        assert_eq!(a.cy < b.cy, d.cy < c.cy, "{a:?} {b:?} {c:?} {d:?}");
911    }
912
913    #[test]
914    fn pins_and_the_explicit_grid() {
915        let mut ns = vec![node(60.0, 30.0); 3];
916        ns[2].pin.0 = Some(3.0);
917        let l = layered(&ns, &[edge(0, 1)], &LayeredParams::default());
918        assert_eq!(l.nodes[2].layer, 3, "a pinned layer keeps its number");
919        let grid: Vec<GraphNode> = (0..4)
920            .map(|i| GraphNode {
921                w: 60.0,
922                h: 30.0,
923                pin: (Some((i % 2) as f64), Some((i / 2) as f64)),
924                ..Default::default()
925            })
926            .collect();
927        let g = layered(&grid, &[edge(0, 3)], &LayeredParams::default());
928        assert_eq!(g.layers, 2);
929        assert_eq!(g.nodes[0].cy, g.nodes[1].cy);
930        assert_eq!(g.edges[0].len(), 2, "straight");
931    }
932
933    #[test]
934    fn lanes_become_bands_and_top_bottom_swaps_the_axes() {
935        let mut ns = vec![node(60.0, 30.0); 2];
936        ns[0].lane = Some(0);
937        ns[1].lane = Some(1);
938        let l = layered(
939            &ns,
940            &[edge(0, 1)],
941            &LayeredParams {
942                lanes: 2,
943                ..Default::default()
944            },
945        );
946        assert_eq!(l.bands.len(), 2);
947        let tb = layered(
948            &ns[..1],
949            &[],
950            &LayeredParams {
951                direction: Direction::TopBottom,
952                ..Default::default()
953            },
954        );
955        assert_eq!(tb.nodes.len(), 1);
956        let t = layered(
957            &[node(60.0, 30.0), node(60.0, 30.0)],
958            &[edge(0, 1)],
959            &LayeredParams {
960                direction: Direction::TopBottom,
961                ..Default::default()
962            },
963        );
964        assert!(t.nodes[0].cy < t.nodes[1].cy && t.nodes[0].cx == t.nodes[1].cx);
965    }
966
967    #[test]
968    fn edge_cases_loops_labels_ports_lanes_and_empty() {
969        let p = LayeredParams::default();
970        let empty = layered(&[], &[], &p);
971        assert!(empty.nodes.is_empty() && empty.layers == 1);
972        // self-loops left to right and top to bottom, in layers and on a grid
973        for dir in [Direction::LeftRight, Direction::TopBottom] {
974            let q = LayeredParams {
975                direction: dir,
976                ..Default::default()
977            };
978            let l = layered(
979                &[node(60.0, 30.0), node(60.0, 30.0)],
980                &[edge(0, 0), edge(0, 1)],
981                &q,
982            );
983            assert_eq!(l.edges[0].len(), 4, "a self-loop");
984            let g: Vec<GraphNode> = (0..2)
985                .map(|i| GraphNode {
986                    w: 60.0,
987                    h: 30.0,
988                    pin: (Some(i as f64), Some(0.0)),
989                    ..Default::default()
990                })
991                .collect();
992            let gl = layered(&g, &[edge(1, 1), edge(0, 1)], &q);
993            assert_eq!(gl.edges[0].len(), 4);
994        }
995        // an edge label widens its gap; a long edge gets dummies (bends)
996        let ns = vec![node(60.0, 30.0); 4];
997        let plain = layered(
998            &ns,
999            &[edge(0, 1), edge(1, 2), edge(0, 2), edge(0, 3), edge(2, 3)],
1000            &p,
1001        );
1002        let labelled = layered(
1003            &ns,
1004            &[
1005                GraphEdge {
1006                    from: 0,
1007                    to: 1,
1008                    label_w: Some(300.0),
1009                },
1010                edge(1, 2),
1011                edge(0, 2),
1012                edge(0, 3),
1013                edge(2, 3),
1014            ],
1015            &p,
1016        );
1017        assert!(
1018            labelled.nodes[1].cx - labelled.nodes[0].cx > plain.nodes[1].cx - plain.nodes[0].cx
1019        );
1020        assert!(
1021            plain.edges[3].len() > 2,
1022            "a long edge bends through its dummies"
1023        );
1024        // several edges leave one node: their ports spread along its side
1025        let starts: Vec<f64> = [0, 2, 3].iter().map(|&k| plain.edges[k][0].1).collect();
1026        assert!(starts.windows(2).any(|w| w[0] != w[1]), "{starts:?}");
1027        // a pinned layer against the flow: a straight edge drawn back
1028        let mut pn = vec![node(60.0, 30.0); 3];
1029        pn[0].pin.0 = Some(2.0);
1030        pn[1].pin.0 = Some(0.0);
1031        let back = layered(&pn, &[edge(0, 1), edge(1, 2)], &p);
1032        assert_eq!(back.edges[0].len(), 2);
1033        // swimlanes top to bottom, an unlaned node, crossings between lanes
1034        let mut lanes = vec![node(60.0, 30.0); 5];
1035        for (i, n) in lanes.iter_mut().enumerate().take(4) {
1036            n.lane = Some(i % 2);
1037        }
1038        let q = LayeredParams {
1039            direction: Direction::TopBottom,
1040            lanes: 2,
1041            ..Default::default()
1042        };
1043        let tb = layered(
1044            &lanes,
1045            &[edge(0, 3), edge(1, 2), edge(2, 4), edge(3, 4)],
1046            &q,
1047        );
1048        assert_eq!(tb.bands.len(), 2);
1049        assert!(tb.bands.iter().all(|b| b.3 > b.1 && b.4 > b.2));
1050    }
1051}