use crate::graph::Graph;
use crate::layout::types::{EdgeLabel, NodeLabel};
use super::barycenter::{self, BarycenterEntry};
use super::resolve_conflicts;
use super::sort;
#[derive(Debug, Clone)]
pub(crate) struct SubgraphResult {
pub vs: Vec<String>,
pub barycenter: Option<f64>,
pub weight: Option<i32>,
}
pub(crate) fn sort_subgraph(
g: &Graph<NodeLabel, EdgeLabel>,
v: &str,
cg: &Graph<(), ()>,
bias_right: bool,
) -> SubgraphResult {
let mut movable: Vec<String> = g.children(Some(v));
let node = g.node(v);
let bl: Option<String> = node.and_then(|n| n.border_left.first().cloned());
let br: Option<String> = node.and_then(|n| n.border_right.first().cloned());
if bl.is_some() {
movable.retain(|w| Some(w.as_str()) != bl.as_deref() && Some(w.as_str()) != br.as_deref());
}
let mut barycenters = barycenter::barycenter(g, &movable);
let mut subgraph_results: std::collections::BTreeMap<String, SubgraphResult> =
std::collections::BTreeMap::new();
for entry in &mut barycenters {
let children = g.children(Some(&entry.v));
if !children.is_empty() {
let sub_result = sort_subgraph(g, &entry.v, cg, bias_right);
subgraph_results.insert(entry.v.clone(), sub_result.clone());
if sub_result.barycenter.is_some() {
merge_barycenters(entry, &sub_result);
}
}
}
let mut entries = resolve_conflicts::resolve_conflicts(&barycenters, cg);
for entry in &mut entries {
entry.vs = entry
.vs
.iter()
.flat_map(|node_v| {
if let Some(sub) = subgraph_results.get(node_v) {
sub.vs.clone()
} else {
vec![node_v.clone()]
}
})
.collect();
}
let mut result = sort::sort(&entries, bias_right);
if let (Some(bl_v), Some(br_v)) = (&bl, &br) {
let mut final_vs = vec![bl_v.clone()];
final_vs.extend(result.vs);
final_vs.push(br_v.clone());
result.vs = final_vs;
let bl_preds = g.predecessors(bl_v).unwrap_or_default();
if !bl_preds.is_empty() {
let bl_pred_order = g.node(&bl_preds[0]).and_then(|n| n.order).unwrap_or(0) as f64;
let br_preds = g.predecessors(br_v).unwrap_or_default();
let br_pred_order = if !br_preds.is_empty() {
g.node(&br_preds[0]).and_then(|n| n.order).unwrap_or(0) as f64
} else {
0.0
};
if result.barycenter.is_none() {
result.barycenter = Some(0.0);
result.weight = Some(0);
}
let bc = result.barycenter.unwrap();
let w = result.weight.unwrap();
result.barycenter =
Some((bc * w as f64 + bl_pred_order + br_pred_order) / (w + 2) as f64);
result.weight = Some(w + 2);
}
}
SubgraphResult {
vs: result.vs,
barycenter: result.barycenter,
weight: result.weight,
}
}
fn merge_barycenters(target: &mut BarycenterEntry, other: &SubgraphResult) {
if let (Some(t_bc), Some(t_w)) = (target.barycenter, target.weight) {
let o_bc = other.barycenter.unwrap_or(0.0);
let o_w = other.weight.unwrap_or(0);
let total_w = t_w + o_w;
if total_w > 0 {
target.barycenter = Some((t_bc * t_w as f64 + o_bc * o_w as f64) / total_w as f64);
target.weight = Some(total_w);
}
} else {
target.barycenter = other.barycenter;
target.weight = other.weight;
}
}