pub mod acyclic;
pub mod add_border_segments;
pub mod coordinate_system;
pub mod intersect;
pub mod nesting_graph;
pub mod normalize;
pub mod order;
pub mod parent_dummy_chains;
pub mod position;
pub mod rank;
pub mod types;
pub mod util;
use crate::graph::{Edge, Graph};
use types::*;
pub fn layout(g: &mut Graph<NodeLabel, EdgeLabel>, opts: Option<LayoutOptions>) {
let opts = opts.unwrap_or_default();
let gl = GraphLabel {
rankdir: opts.rankdir,
align: opts.align,
nodesep: opts.nodesep,
edgesep: opts.edgesep,
ranksep: opts.ranksep,
marginx: opts.marginx,
marginy: opts.marginy,
acyclicer: opts.acyclicer,
ranker: opts.ranker,
rank_align: opts.rank_align,
tie_keep_first: opts.tie_keep_first,
compound: g.is_compound(),
..GraphLabel::default()
};
g.set_graph_label(gl);
make_space_for_edge_labels(g);
remove_self_edges(g);
let acyclicer = g.graph_label::<GraphLabel>().and_then(|gl| gl.acyclicer);
acyclic::run(g, acyclicer);
let compound = g.graph_label::<GraphLabel>().is_some_and(|gl| gl.compound);
let nesting_root = nesting_graph::run(g);
{
let ranker = g
.graph_label::<GraphLabel>()
.map_or(Ranker::NetworkSimplex, |gl| gl.ranker);
let mut ncg = util::as_non_compound_graph(g);
rank::rank(&mut ncg, ranker);
for v in ncg.nodes() {
if let Some(node) = ncg.node(&v)
&& let Some(rank) = node.rank
&& let Some(gn) = g.node_mut(&v)
{
gn.rank = Some(rank);
}
}
}
if let Some(ref root) = nesting_root
&& let Some(gl) = g.graph_label_mut::<GraphLabel>()
{
gl.nesting_root = Some(root.clone());
}
inject_edge_label_proxies(g);
util::remove_empty_ranks(g);
{
let nesting_root = g
.graph_label::<GraphLabel>()
.and_then(|gl| gl.nesting_root.clone());
if let Some(ref root) = nesting_root {
nesting_graph::cleanup(g, root);
}
}
util::normalize_ranks(g);
assign_rank_min_max(g);
remove_edge_label_proxies(g);
let mut dummy_chains = Vec::new();
normalize::run(g, &mut dummy_chains);
if let Some(gl) = g.graph_label_mut::<GraphLabel>() {
gl.dummy_chains = dummy_chains.clone();
}
if compound {
parent_dummy_chains::parent_dummy_chains(g);
}
if compound {
add_border_segments::add_border_segments(g);
}
order::order(g);
insert_self_edges(g);
coordinate_system::adjust(g);
position::position(g);
position_self_edges(g);
remove_border_nodes(g);
let chains = g
.graph_label::<GraphLabel>()
.map(|gl| gl.dummy_chains.clone())
.unwrap_or_default();
normalize::undo(g, &chains);
fixup_edge_label_coords(g);
coordinate_system::undo(g);
translate_graph(g);
assign_node_intersects(g);
dedupe_adjacent_edge_points(g);
reverse_points_for_reversed_edges(g);
acyclic::undo(g);
}
fn make_space_for_edge_labels(g: &mut Graph<NodeLabel, EdgeLabel>) {
let (rankdir, ranksep) = {
let gl = g.graph_label::<GraphLabel>();
(
gl.map_or(RankDir::TB, |l| l.rankdir),
gl.map_or(50.0, |l| l.ranksep),
)
};
if let Some(gl) = g.graph_label_mut::<GraphLabel>() {
gl.ranksep = ranksep / 2.0;
}
for e in g.edges() {
if let Some(label) = g.edge_mut(&e.v, &e.w, e.name.as_deref()) {
label.minlen *= 2;
if label.labelpos != LabelPos::Center {
match rankdir {
RankDir::TB | RankDir::BT => {
label.width += label.label_offset;
}
RankDir::LR | RankDir::RL => {
label.height += label.label_offset;
}
}
}
}
}
}
fn remove_self_edges(g: &mut Graph<NodeLabel, EdgeLabel>) {
let self_edges: Vec<(Edge, EdgeLabel)> = g
.edges()
.into_iter()
.filter(|e| e.v == e.w)
.filter_map(|e| {
let label = g.edge(&e.v, &e.w, e.name.as_deref()).cloned()?;
Some((e, label))
})
.collect();
for (e, label) in self_edges {
g.remove_edge(&e.v, &e.w, e.name.as_deref());
let self_edge = SelfEdge {
e: e.clone(),
label,
};
if let Some(node) = g.node_mut(&e.v) {
node.self_edges.push(self_edge);
}
}
}
fn insert_self_edges(g: &mut Graph<NodeLabel, EdgeLabel>) {
let layers = util::build_layer_matrix(g);
for layer in &layers {
let mut order_shift = 0usize;
for (i, v) in layer.iter().enumerate() {
if v.is_empty() {
continue;
}
if let Some(node) = g.node_mut(v) {
node.order = Some(i + order_shift);
}
let self_edges: Vec<SelfEdge> =
g.node(v).map(|n| n.self_edges.clone()).unwrap_or_default();
let node_rank = g.node(v).and_then(|n| n.rank);
for se in &self_edges {
order_shift += 1;
let attrs = NodeLabel {
width: se.label.width,
height: se.label.height,
rank: node_rank,
order: Some(i + order_shift),
self_edge_data_e: Some(se.e.clone()),
self_edge_data_label: Some(se.label.clone()),
..NodeLabel::default()
};
util::add_dummy_node(g, "selfedge", attrs, "_se");
}
}
}
}
fn inject_edge_label_proxies(g: &mut Graph<NodeLabel, EdgeLabel>) {
let edges: Vec<Edge> = g.edges();
for e in edges {
let (v_rank, w_rank, has_label) = {
let label = match g.edge(&e.v, &e.w, e.name.as_deref()) {
Some(l) => l,
None => continue,
};
if label.width == 0.0 || label.height == 0.0 {
continue;
}
let vr = g.node(&e.v).and_then(|n| n.rank).unwrap_or(0);
let wr = g.node(&e.w).and_then(|n| n.rank).unwrap_or(0);
(vr, wr, true)
};
if !has_label {
continue;
}
let mid_rank = (w_rank - v_rank) / 2 + v_rank;
let attrs = NodeLabel {
rank: Some(mid_rank),
edge_obj: Some(crate::graph::Edge {
v: e.v.clone(),
w: e.w.clone(),
name: e.name.clone(),
}),
..NodeLabel::default()
};
util::add_dummy_node(g, "edge-proxy", attrs, "_ep");
}
}
fn remove_edge_label_proxies(g: &mut Graph<NodeLabel, EdgeLabel>) {
let proxy_nodes: Vec<String> = g
.nodes()
.into_iter()
.filter(|v| {
g.node(v)
.is_some_and(|n| n.dummy.as_deref() == Some("edge-proxy"))
})
.collect();
for v in proxy_nodes {
let rank = g.node(&v).and_then(|n| n.rank);
let edge_obj = g.node(&v).and_then(|n| n.edge_obj.clone());
if let Some(eo) = edge_obj
&& let Some(label) = g.edge_mut(&eo.v, &eo.w, eo.name.as_deref())
{
label.label_rank = rank.map(|r| r as f64);
}
g.remove_node(&v);
}
}
fn assign_rank_min_max(g: &mut Graph<NodeLabel, EdgeLabel>) {
let assignments: Vec<(String, i32, i32)> = g
.nodes()
.iter()
.filter_map(|v| {
let node = g.node(v)?;
let bt = node.border_top.as_ref()?;
let bb = node.border_bottom.as_ref()?;
let min_r = g.node(bt).and_then(|n| n.rank)?;
let max_r = g.node(bb).and_then(|n| n.rank)?;
Some((v.clone(), min_r, max_r))
})
.collect();
for (v, min_r, max_r) in assignments {
if let Some(node) = g.node_mut(&v) {
node.min_rank = Some(min_r);
node.max_rank = Some(max_r);
}
}
}
fn translate_graph(g: &mut Graph<NodeLabel, EdgeLabel>) {
let (marginx, marginy) = g
.graph_label::<GraphLabel>()
.map_or((0.0, 0.0), |gl| (gl.marginx, gl.marginy));
let mut min_x = f64::INFINITY;
let mut max_x = f64::NEG_INFINITY;
let mut min_y = f64::INFINITY;
let mut max_y = f64::NEG_INFINITY;
for v in g.nodes() {
if let Some(node) = g.node(&v)
&& let (Some(x), Some(y)) = (node.x, node.y)
{
let hw = node.width / 2.0;
let hh = node.height / 2.0;
min_x = min_x.min(x - hw);
max_x = max_x.max(x + hw);
min_y = min_y.min(y - hh);
max_y = max_y.max(y + hh);
}
}
for e in g.edges() {
if let Some(label) = g.edge(&e.v, &e.w, e.name.as_deref())
&& let (Some(x), Some(y)) = (label.x, label.y)
{
let hw = label.width / 2.0;
let hh = label.height / 2.0;
min_x = min_x.min(x - hw);
max_x = max_x.max(x + hw);
min_y = min_y.min(y - hh);
max_y = max_y.max(y + hh);
}
}
if min_x == f64::INFINITY {
return;
}
let dx = marginx - min_x;
let dy = marginy - min_y;
for v in g.nodes() {
if let Some(node) = g.node_mut(&v) {
if let Some(ref mut x) = node.x {
*x += dx;
}
if let Some(ref mut y) = node.y {
*y += dy;
}
}
}
for e in g.edges() {
if let Some(label) = g.edge_mut(&e.v, &e.w, e.name.as_deref()) {
if let Some(ref mut x) = label.x {
*x += dx;
}
if let Some(ref mut y) = label.y {
*y += dy;
}
for pt in &mut label.points {
pt.x += dx;
pt.y += dy;
}
}
}
let graph_width = max_x - min_x + 2.0 * marginx;
let graph_height = max_y - min_y + 2.0 * marginy;
if let Some(gl) = g.graph_label_mut::<GraphLabel>() {
gl.width = graph_width;
gl.height = graph_height;
}
}
fn assign_node_intersects(g: &mut Graph<NodeLabel, EdgeLabel>) {
for e in g.edges() {
let v_node = g.node(&e.v).cloned();
let w_node = g.node(&e.w).cloned();
let (src_point, tgt_point) = {
let label = match g.edge(&e.v, &e.w, e.name.as_deref()) {
Some(l) => l,
None => continue,
};
let first = label.points.first().cloned().unwrap_or_else(|| {
w_node.as_ref().map_or(Point::new(0.0, 0.0), |n| {
Point::new(n.x.unwrap_or(0.0), n.y.unwrap_or(0.0))
})
});
let last = label.points.last().cloned().unwrap_or_else(|| {
v_node.as_ref().map_or(Point::new(0.0, 0.0), |n| {
Point::new(n.x.unwrap_or(0.0), n.y.unwrap_or(0.0))
})
});
let src = v_node.as_ref().map(|n| util::intersect_node(n, &first));
let tgt = w_node.as_ref().map(|n| util::intersect_node(n, &last));
(src, tgt)
};
if let Some(label) = g.edge_mut(&e.v, &e.w, e.name.as_deref()) {
if let Some(pt) = src_point {
label.points.insert(0, pt);
}
if let Some(pt) = tgt_point {
label.points.push(pt);
}
}
}
}
fn fixup_edge_label_coords(g: &mut Graph<NodeLabel, EdgeLabel>) {
for e in g.edges() {
if let Some(label) = g.edge_mut(&e.v, &e.w, e.name.as_deref())
&& label.x.is_some()
{
if label.labelpos == LabelPos::Left || label.labelpos == LabelPos::Right {
label.width -= label.label_offset;
}
match label.labelpos {
LabelPos::Left => {
label.x = label.x.map(|x| x - label.width / 2.0 - label.label_offset);
}
LabelPos::Right => {
label.x = label.x.map(|x| x + label.width / 2.0 + label.label_offset);
}
LabelPos::Center => {
}
}
}
}
}
const DEDUPE_EPSILON: f64 = 1e-4;
fn dedupe_adjacent_edge_points(g: &mut Graph<NodeLabel, EdgeLabel>) {
let edges = g.edges();
for e in edges {
if let Some(label) = g.edge_mut(&e.v, &e.w, e.name.as_deref()) {
if label.points.len() < 2 {
continue;
}
label.points.dedup_by(|a, b| {
(a.x - b.x).abs() < DEDUPE_EPSILON && (a.y - b.y).abs() < DEDUPE_EPSILON
});
}
}
}
fn reverse_points_for_reversed_edges(g: &mut Graph<NodeLabel, EdgeLabel>) {
for e in g.edges() {
if let Some(label) = g.edge_mut(&e.v, &e.w, e.name.as_deref())
&& label.reversed
{
label.points.reverse();
}
}
}
fn remove_border_nodes(g: &mut Graph<NodeLabel, EdgeLabel>) {
let nodes: Vec<String> = g.nodes();
for v in &nodes {
let node = match g.node(v) {
Some(n) => n.clone(),
None => continue,
};
let last_bl = node.border_left.iter().rfind(|s| !s.is_empty());
let last_br = node.border_right.iter().rfind(|s| !s.is_empty());
let bt = node.border_top.as_ref();
let bb = node.border_bottom.as_ref();
let (Some(l_id), Some(r_id), Some(t_id), Some(b_id)) = (last_bl, last_br, bt, bb) else {
continue;
};
let l = g.node(l_id).cloned();
let r = g.node(r_id).cloned();
let t = g.node(t_id).cloned();
let b = g.node(b_id).cloned();
let (Some(l), Some(r), Some(t), Some(b)) = (l, r, t, b) else {
continue;
};
let (Some(lx), Some(rx), Some(ty), Some(by)) = (l.x, r.x, t.y, b.y) else {
continue;
};
let width = (rx - lx).abs();
let height = (by - ty).abs();
let cx = lx + width / 2.0;
let cy = ty + height / 2.0;
if let Some(node) = g.node_mut(v) {
node.width = width;
node.height = height;
node.x = Some(cx);
node.y = Some(cy);
}
}
let border_nodes: Vec<String> = g
.nodes()
.into_iter()
.filter(|v| {
g.node(v)
.is_some_and(|n| n.dummy.as_deref() == Some("border"))
})
.collect();
for v in border_nodes {
g.remove_node(&v);
}
}
fn position_self_edges(g: &mut Graph<NodeLabel, EdgeLabel>) {
let self_edge_nodes: Vec<String> = g
.nodes()
.into_iter()
.filter(|v| {
g.node(v)
.is_some_and(|n| n.dummy.as_deref() == Some("selfedge"))
})
.collect();
for v in self_edge_nodes {
let se_node = match g.node(&v).cloned() {
Some(n) => n,
None => continue,
};
let edge_desc = match se_node.self_edge_data_e.clone() {
Some(e) => e,
None => continue,
};
let se_label = se_node.self_edge_data_label.clone().unwrap_or_default();
let orig_node = match g.node(&edge_desc.v).cloned() {
Some(n) => n,
None => continue,
};
let x = orig_node.x.unwrap_or(0.0) + orig_node.width / 2.0;
let y = orig_node.y.unwrap_or(0.0);
let dx = se_node.x.unwrap_or(0.0) - x;
let dy = orig_node.height / 2.0;
g.set_edge(
edge_desc.v.clone(),
edge_desc.w.clone(),
Some(se_label.clone()),
edge_desc.name.as_deref(),
);
g.remove_node(&v);
if let Some(label) = g.edge_mut(&edge_desc.v, &edge_desc.w, edge_desc.name.as_deref()) {
label.points = vec![
Point::new(x + 2.0 * dx / 3.0, y - dy),
Point::new(x + 5.0 * dx / 6.0, y - dy),
Point::new(x + dx, y),
Point::new(x + 5.0 * dx / 6.0, y + dy),
Point::new(x + 2.0 * dx / 3.0, y + dy),
];
label.x = se_node.x;
label.y = se_node.y;
}
}
}
#[cfg(test)]
mod tests;