use crate::{
graph::{Graph, edge::Edge, node::Node},
triskel::layout::{EdgeLayoutData, LayoutGraph, NodeLayoutData},
};
pub(crate) fn break_cycles(graph: &mut LayoutGraph, root: usize) {
let mut ids: Vec<usize> = graph.nodes().map(|n| n.id()).collect();
ids.sort_unstable();
let mut color = vec![0u8; graph_capacity(graph)];
let mut back_edges: Vec<usize> = Vec::new();
if color.get(root) == Some(&0) {
visit(graph, root, &mut color, &mut back_edges);
}
for start in ids {
if color[start] == 0 {
visit(graph, start, &mut color, &mut back_edges);
}
}
for edge_id in back_edges {
let edge = graph.get_edge(edge_id).unwrap();
let source = edge.from_id(); let target = edge.to_id(); let gadget = EdgeLayoutData {
reversed: true,
orig: edge.orig,
port_start: edge.port_start,
port_end: edge.port_end,
..Default::default()
};
let a_prime = graph.make_node(virtual_node()); let b_prime = graph.make_node(virtual_node()); graph.remove_edge(edge_id);
graph.make_edge(a_prime, target, gadget); graph.make_edge(source, b_prime, gadget); graph.make_edge(a_prime, b_prime, gadget); }
}
fn virtual_node() -> NodeLayoutData {
NodeLayoutData {
width: 0.0,
height: 0.0,
is_dummy: true,
..Default::default()
}
}
fn visit(graph: &LayoutGraph, node: usize, color: &mut [u8], back_edges: &mut Vec<usize>) {
color[node] = 1;
let mut children: Vec<(usize, usize)> = graph
.get_node(node)
.unwrap()
.children()
.map(|c| (c.edge_id(), c.node_id()))
.collect();
children.sort_unstable();
for (edge_id, child) in children {
if child == node {
continue; }
match color[child] {
0 => visit(graph, child, color, back_edges),
1 => back_edges.push(edge_id), _ => {}
}
}
color[node] = 2;
}
fn graph_capacity(graph: &LayoutGraph) -> usize {
graph.nodes().map(|n| n.id()).max().map_or(0, |m| m + 1)
}