use super::types::*;
use crate::graph::{Graph, GraphOptions};
#[cfg(test)]
use std::collections::BTreeMap;
pub(crate) fn add_dummy_node(
g: &mut Graph<NodeLabel, EdgeLabel>,
dummy_type: &str,
mut attrs: NodeLabel,
prefix: &str,
) -> String {
let mut v = prefix.to_string();
while g.has_node(&v) {
v = crate::util::unique_id(prefix);
}
attrs.dummy = Some(dummy_type.to_string());
g.set_node(v.clone(), Some(attrs));
v
}
pub(crate) fn simplify(g: &Graph<NodeLabel, EdgeLabel>) -> Graph<NodeLabel, EdgeLabel> {
let mut simplified = Graph::new();
for v in g.nodes() {
if let Some(label) = g.node(&v) {
simplified.set_node(v, Some(label.clone()));
} else {
simplified.set_node(v, None);
}
}
for e in g.edges() {
let existing = simplified.edge(&e.v, &e.w, None);
let mut weight = existing.map_or(0, |l: &EdgeLabel| l.weight);
let mut minlen = existing.map_or(1, |l: &EdgeLabel| l.minlen);
if let Some(label) = g.edge(&e.v, &e.w, e.name.as_deref()) {
weight += label.weight;
minlen = minlen.max(label.minlen);
}
let label = EdgeLabel {
weight,
minlen,
..EdgeLabel::default()
};
simplified.set_edge(e.v.clone(), e.w.clone(), Some(label), None);
}
simplified
}
pub(crate) fn as_non_compound_graph(
g: &Graph<NodeLabel, EdgeLabel>,
) -> Graph<NodeLabel, EdgeLabel> {
let mut simplified = Graph::with_options(GraphOptions {
multigraph: g.is_multigraph(),
..Default::default()
});
for v in g.nodes() {
if g.is_compound() && !g.children(Some(&v)).is_empty() {
continue;
}
if let Some(label) = g.node(&v) {
simplified.set_node(v, Some(label.clone()));
} else {
simplified.set_node(v, None);
}
}
for e in g.edges() {
for endpoint in [&e.v, &e.w] {
if !simplified.has_node(endpoint) {
simplified.set_node(endpoint.clone(), Some(NodeLabel::default()));
}
}
if let Some(label) = g.edge(&e.v, &e.w, e.name.as_deref()) {
simplified.set_edge(
e.v.clone(),
e.w.clone(),
Some(label.clone()),
e.name.as_deref(),
);
}
}
simplified
}
pub(crate) fn intersect_node(rect: &NodeLabel, point: &Point) -> Point {
let cx = rect.x.unwrap_or(0.0);
let cy = rect.y.unwrap_or(0.0);
match rect.shape.as_deref() {
Some("diamond") => {
let w2 = rect.width / 2.0;
let h2 = rect.height / 2.0;
let verts = [
Point { x: cx, y: cy - h2 },
Point { x: cx + w2, y: cy },
Point { x: cx, y: cy + h2 },
Point { x: cx - w2, y: cy },
];
super::intersect::intersect_polygon(&verts, &Point { x: cx, y: cy }, point)
}
Some("stateStart" | "stateEnd" | "state_start" | "state_end" | "start" | "end") => {
super::intersect::intersect_ellipse(cx, cy, rect.width / 2.0, rect.height / 2.0, point)
}
_ => super::intersect::intersect_rect(cx, cy, rect.width, rect.height, point),
}
}
#[cfg(test)]
pub(crate) fn intersect_rect(rect: &NodeLabel, point: &Point) -> Point {
super::intersect::intersect_rect(
rect.x.unwrap_or(0.0),
rect.y.unwrap_or(0.0),
rect.width,
rect.height,
point,
)
}
pub(crate) fn build_layer_matrix(g: &Graph<NodeLabel, EdgeLabel>) -> Vec<Vec<String>> {
let max_r = max_rank(g);
if max_r < 0 {
return vec![];
}
let mut layers: Vec<Vec<String>> = vec![vec![]; (max_r + 1) as usize];
for v in g.nodes() {
if let Some(node) = g.node(&v)
&& let Some(rank) = node.rank
{
let r = rank as usize;
if r < layers.len() {
let order = node.order.unwrap_or(0);
if order >= layers[r].len() {
layers[r].resize(order + 1, String::new());
}
layers[r][order] = v;
}
}
}
layers
}
pub(crate) fn max_rank(g: &Graph<NodeLabel, EdgeLabel>) -> i32 {
g.nodes()
.iter()
.filter_map(|v| g.node(v).and_then(|n| n.rank))
.max()
.unwrap_or(-1)
}
pub(crate) fn normalize_ranks(g: &mut Graph<NodeLabel, EdgeLabel>) {
let min_rank = g
.nodes()
.iter()
.filter_map(|v| g.node(v).and_then(|n| n.rank))
.min();
if let Some(min) = min_rank
&& min != 0
{
for v in g.nodes() {
if let Some(node) = g.node_mut(&v)
&& let Some(ref mut rank) = node.rank
{
*rank -= min;
}
}
}
}
pub(crate) fn remove_empty_ranks(g: &mut Graph<NodeLabel, EdgeLabel>) {
let ranks: Vec<i32> = g
.nodes()
.iter()
.filter_map(|v| g.node(v).and_then(|n| n.rank))
.collect();
if ranks.is_empty() {
return;
}
let offset = *ranks.iter().min().unwrap();
let max_r = *ranks.iter().max().unwrap();
let len = (max_r - offset + 1) as usize;
let mut layers: Vec<Vec<String>> = vec![vec![]; len];
for v in g.nodes() {
if let Some(node) = g.node(&v)
&& let Some(rank) = node.rank
{
layers[(rank - offset) as usize].push(v);
}
}
let node_rank_factor = g
.graph_label::<GraphLabel>()
.and_then(|gl| gl.node_rank_factor)
.map(|f| f as i32)
.unwrap_or(1);
let mut delta: i32 = 0;
for (i, layer) in layers.iter().enumerate() {
if layer.is_empty() {
if node_rank_factor > 0 && (i as i32) % node_rank_factor != 0 {
delta -= 1;
}
} else if delta != 0 {
for v in layer {
if let Some(node) = g.node_mut(v)
&& let Some(ref mut rank) = node.rank
{
*rank += delta;
}
}
}
}
}
pub(crate) fn add_border_node(
g: &mut Graph<NodeLabel, EdgeLabel>,
prefix: &str,
rank: Option<i32>,
order: Option<usize>,
) -> String {
let node = NodeLabel {
rank,
order,
..NodeLabel::default()
};
add_dummy_node(g, "border", node, prefix)
}
#[cfg(test)]
pub(crate) fn successor_weights(
g: &Graph<NodeLabel, EdgeLabel>,
) -> BTreeMap<String, BTreeMap<String, i32>> {
let mut result = BTreeMap::new();
for v in g.nodes() {
let mut sucs = BTreeMap::new();
if let Some(edges) = g.out_edges(&v, None) {
for e in edges {
let w = g
.edge(&e.v, &e.w, e.name.as_deref())
.map_or(1, |l| l.weight);
*sucs.entry(e.w.clone()).or_insert(0) += w;
}
}
result.insert(v, sucs);
}
result
}
#[cfg(test)]
pub(crate) fn predecessor_weights(
g: &Graph<NodeLabel, EdgeLabel>,
) -> BTreeMap<String, BTreeMap<String, i32>> {
let mut result = BTreeMap::new();
for v in g.nodes() {
let mut preds = BTreeMap::new();
if let Some(edges) = g.in_edges(&v, None) {
for e in edges {
let w = g
.edge(&e.v, &e.w, e.name.as_deref())
.map_or(1, |l| l.weight);
*preds.entry(e.v.clone()).or_insert(0) += w;
}
}
result.insert(v, preds);
}
result
}