use super::graph_prep::{lay_out_by_piece, PreparedGraph};
use super::Mat;
use crate::matrix::graph::{bfs, far_end};
const SWEEPS: usize = 12;
#[must_use]
pub fn sugiyama_layout_2d_on_graph(
n: usize,
edges: &[(usize, usize, f32)],
root: Option<usize>,
) -> Mat {
let graph = PreparedGraph::new(n, edges);
let root = root.filter(|&r| r < n);
let first = root.or((n > 0).then_some(0));
lay_out_by_piece(
&graph,
|piece, at_first| {
let from = match (root, at_first) {
(Some(_), Some(r)) => r,
_ => far_end(piece, far_end(piece, 0)),
};
layered(piece, from)
},
first,
)
}
fn layered(graph: &PreparedGraph, from: usize) -> Mat {
let (order, _, level) = bfs(graph, from);
let n_layers = order.last().map_or(0, |&v| level[v] + 1);
let mut layers: Vec<Vec<usize>> = vec![Vec::new(); n_layers];
for i in 0..graph.n() {
layers[level[i]].push(i);
}
order_layers(graph, &mut layers);
let mut xy = Mat::zeros(graph.n(), 2);
for (l, ly) in layers.iter().enumerate() {
let mid = (ly.len() as f32 - 1.0) / 2.0;
for (k, &i) in ly.iter().enumerate() {
xy[(i, 0)] = k as f32 - mid;
xy[(i, 1)] = -(l as f32);
}
}
xy
}
fn order_layers(graph: &PreparedGraph, layers: &mut [Vec<usize>]) {
let mut place = vec![0.0_f32; graph.n()];
let set_places = |ly: &[usize], place: &mut [f32]| {
for (k, &i) in ly.iter().enumerate() {
place[i] = k as f32;
}
};
for ly in layers.iter() {
set_places(ly, &mut place);
}
let mut in_layer = vec![usize::MAX; graph.n()];
for (l, ly) in layers.iter().enumerate() {
for &i in ly {
in_layer[i] = l;
}
}
let reorder = |ly: &mut Vec<usize>, above: usize, place: &mut [f32]| {
let key: Vec<f32> = ly
.iter()
.map(|&i| {
let there: Vec<f32> = graph
.neighbour_ids(i)
.filter(|&j| in_layer[j] == above)
.map(|j| place[j])
.collect();
if there.is_empty() {
place[i]
} else {
there.iter().sum::<f32>() / there.len() as f32
}
})
.collect();
let mut idx: Vec<usize> = (0..ly.len()).collect();
idx.sort_by(|&a, &b| key[a].total_cmp(&key[b]));
*ly = idx.iter().map(|&k| ly[k]).collect();
set_places(ly, place);
};
for _ in 0..SWEEPS {
for (l, ly) in layers.iter_mut().enumerate().skip(1) {
reorder(ly, l - 1, &mut place);
}
}
}
#[cfg(test)]
mod tests;