use matrixcode_core::workflow::WorkflowDef;
use std::collections::HashMap;
pub struct LayoutResult {
pub positions: HashMap<String, (usize, usize)>,
pub layers: Vec<Vec<String>>,
pub width: usize,
pub height: usize,
}
pub fn compute_layout(def: &WorkflowDef) -> LayoutResult {
let mut positions = HashMap::new();
let mut layers: Vec<Vec<String>> = Vec::new();
let mut visited: HashMap<String, bool> = HashMap::new();
let start_node = def
.nodes
.iter()
.find(|n| n.node_type == matrixcode_core::workflow::NodeType::Start)
.map(|n| n.id.clone());
if let Some(start) = start_node {
let mut current_layer = vec![start.clone()];
visited.insert(start.clone(), true);
while !current_layer.is_empty() {
let mut sorted_layer = current_layer.clone();
sorted_layer.sort();
layers.push(sorted_layer.clone());
for (col, node_id) in sorted_layer.iter().enumerate() {
positions.insert(node_id.clone(), (layers.len() - 1, col));
}
let mut next_layer = Vec::new();
for node_id in ¤t_layer {
for edge in &def.edges {
if &edge.from == node_id && !visited.contains_key(&edge.to) {
if def.nodes.iter().any(|n| n.id == edge.to) {
visited.insert(edge.to.clone(), true);
next_layer.push(edge.to.clone());
}
}
}
}
current_layer = next_layer;
}
for node in &def.nodes {
if !visited.contains_key(&node.id) {
if let Some(last_layer) = layers.last_mut() {
last_layer.push(node.id.clone());
let col = last_layer.len() - 1;
positions.insert(node.id.clone(), (layers.len() - 1, col));
} else {
layers.push(vec![node.id.clone()]);
positions.insert(node.id.clone(), (0, 0));
}
}
}
}
let height = layers.len();
let width = layers.iter().map(|l| l.len()).max().unwrap_or(0);
LayoutResult {
positions,
layers,
width,
height,
}
}
pub fn calculate_edge_path(
from_pos: (usize, usize),
to_pos: (usize, usize),
node_width: usize,
node_height: usize,
spacing_x: usize,
spacing_y: usize,
) -> Vec<(usize, usize)> {
let from_x = from_pos.1 * (node_width + spacing_x) + node_width / 2;
let from_y = from_pos.0 * (node_height + spacing_y) + node_height;
let to_x = to_pos.1 * (node_width + spacing_x) + node_width / 2;
let to_y = to_pos.0 * (node_height + spacing_y);
let mut points = Vec::new();
points.push((from_x, from_y));
if from_x == to_x {
for y in from_y + 1..to_y {
points.push((from_x, y));
}
} else {
let mid_y = (from_y + to_y) / 2;
for y in from_y + 1..mid_y {
points.push((from_x, y));
}
for x in from_x..to_x {
points.push((x, mid_y));
}
for y in mid_y..to_y {
points.push((to_x, y));
}
}
points.push((to_x, to_y));
points
}