use crate::tui::markdown::mermaid::model::Edge;
pub(super) fn order_ranks(by_rank: &mut [Vec<usize>], edges: &[Edge], ranks: &[usize]) {
let n = ranks.len();
if by_rank.len() < 2 || n < 3 {
return;
}
let mut parents: Vec<Vec<usize>> = vec![Vec::new(); n];
let mut children: Vec<Vec<usize>> = vec![Vec::new(); n];
for e in edges {
if e.from != e.to && ranks[e.to] > ranks[e.from] {
parents[e.to].push(e.from);
children[e.from].push(e.to);
}
}
let mut pos = vec![0usize; n];
let set_pos = |by_rank: &[Vec<usize>], pos: &mut Vec<usize>| {
for row in by_rank {
for (i, &v) in row.iter().enumerate() {
pos[v] = i;
}
}
};
set_pos(by_rank, &mut pos);
let mut best: Vec<Vec<usize>> = by_rank.to_vec();
let mut best_crossings = count_crossings(edges, ranks, &pos);
if best_crossings == 0 {
return;
}
for it in 0..8 {
if it % 2 == 0 {
for row in by_rank.iter_mut().skip(1) {
sort_by_barycenter(row, &parents, &pos);
for (i, &v) in row.iter().enumerate() {
pos[v] = i;
}
}
} else {
let last = by_rank.len() - 1;
for row in by_rank[..last].iter_mut().rev() {
sort_by_barycenter(row, &children, &pos);
for (i, &v) in row.iter().enumerate() {
pos[v] = i;
}
}
}
let crossings = count_crossings(edges, ranks, &pos);
if crossings < best_crossings {
best_crossings = crossings;
best = by_rank.to_vec();
}
if best_crossings == 0 {
break;
}
}
for (row, b) in by_rank.iter_mut().zip(best) {
*row = b;
}
}
fn sort_by_barycenter(row: &mut [usize], neigh: &[Vec<usize>], pos: &[usize]) {
let mut keyed: Vec<(f64, usize)> = row
.iter()
.map(|&v| {
let key = if neigh[v].is_empty() {
pos[v] as f64
} else {
neigh[v].iter().map(|&u| pos[u] as f64).sum::<f64>() / neigh[v].len() as f64
};
(key, v)
})
.collect();
keyed.sort_by(|a, b| a.0.total_cmp(&b.0));
for (slot, (_, v)) in row.iter_mut().zip(keyed) {
*slot = v;
}
}
fn count_crossings(edges: &[Edge], ranks: &[usize], pos: &[usize]) -> usize {
let adjacent: Vec<(usize, usize, usize)> = edges
.iter()
.filter(|e| e.from != e.to && ranks[e.to] == ranks[e.from] + 1)
.map(|e| (ranks[e.from], pos[e.from], pos[e.to]))
.collect();
let mut crossings = 0;
for (i, a) in adjacent.iter().enumerate() {
for b in &adjacent[i + 1..] {
if a.0 == b.0 && ((a.1 < b.1 && a.2 > b.2) || (a.1 > b.1 && a.2 < b.2)) {
crossings += 1;
}
}
}
crossings
}
pub(super) fn assign_positions(
by_rank: &[Vec<usize>],
size: &[usize],
sep: usize,
edges: &[Edge],
ranks: &[usize],
) -> Vec<usize> {
let n = size.len();
let mut parents: Vec<Vec<usize>> = vec![Vec::new(); n];
let mut children: Vec<Vec<usize>> = vec![Vec::new(); n];
for e in edges {
if e.from != e.to && ranks[e.to] > ranks[e.from] {
parents[e.to].push(e.from);
children[e.from].push(e.to);
}
}
let mut pos = vec![0f64; n];
for row in by_rank {
let mut x = 0f64;
for &v in row {
let half = size[v] as f64 / 2.0;
x += half;
pos[v] = x;
x += half + sep as f64;
}
}
for it in 0..10 {
if it % 2 == 0 {
for row in by_rank.iter() {
relax_rank(row, &parents, &mut pos, size, sep);
}
} else {
for row in by_rank.iter().rev() {
relax_rank(row, &children, &mut pos, size, sep);
}
}
}
let min_left = (0..n)
.map(|v| pos[v] - size[v] as f64 / 2.0)
.fold(f64::INFINITY, f64::min);
let min_left = if min_left.is_finite() { min_left } else { 0.0 };
(0..n)
.map(|v| (pos[v] - min_left).round().max(0.0) as usize)
.collect()
}
fn relax_rank(nodes: &[usize], neigh: &[Vec<usize>], pos: &mut [f64], size: &[usize], sep: usize) {
let n = nodes.len();
if n == 0 {
return;
}
let desired: Vec<f64> = nodes
.iter()
.map(|&v| {
if neigh[v].is_empty() {
pos[v]
} else {
neigh[v].iter().map(|&u| pos[u]).sum::<f64>() / neigh[v].len() as f64
}
})
.collect();
let half = |i: usize| size[nodes[i]] as f64 / 2.0;
let mut left = vec![0f64; n];
let mut right = vec![0f64; n];
for i in 0..n {
left[i] = if i == 0 {
desired[i]
} else {
desired[i].max(left[i - 1] + half(i - 1) + sep as f64 + half(i))
};
}
for i in (0..n).rev() {
right[i] = if i == n - 1 {
desired[i]
} else {
desired[i].min(right[i + 1] - half(i + 1) - sep as f64 - half(i))
};
}
for i in 0..n {
pos[nodes[i]] = (left[i] + right[i]) / 2.0;
}
for i in 1..n {
let min_p = pos[nodes[i - 1]] + half(i - 1) + sep as f64 + half(i);
if pos[nodes[i]] < min_p {
pos[nodes[i]] = min_p;
}
}
}