use disposition_input_model::process::{ProcessDiagram, Processes};
use disposition_ir_model::{
node::NodeId,
process::{
ProcessStepEdges, ProcessStepGraph, ProcessStepGraphEdge, ProcessStepGraphs,
ProcessStepLane, ProcessStepPlacement, ProcessStepRanks,
},
};
use disposition_model_common::{Map, Set};
#[derive(Clone, Copy, Debug)]
pub struct ProcessStepGraphCalculator;
impl ProcessStepGraphCalculator {
pub fn calculate<'id>(
processes: &Processes<'id>,
process_step_ranks: &ProcessStepRanks<'id>,
process_step_edges: &ProcessStepEdges<'id>,
) -> ProcessStepGraphs<'id> {
processes
.iter()
.filter_map(|(process_id, process_diagram)| {
if process_diagram.steps.is_empty() {
return None;
}
let process_node_id = NodeId::from(process_id.as_ref().clone());
let graph = Self::process_graph_build(
process_diagram,
process_step_ranks,
process_step_edges,
);
Some((process_node_id, graph))
})
.collect()
}
fn process_graph_build<'id>(
process_diagram: &ProcessDiagram<'id>,
process_step_ranks: &ProcessStepRanks<'id>,
process_step_edges: &ProcessStepEdges<'id>,
) -> ProcessStepGraph<'id> {
let steps_decl: Vec<NodeId<'id>> = process_diagram
.steps
.keys()
.map(|step_id| NodeId::from(step_id.as_ref().clone()))
.collect();
let step_set: Set<NodeId<'id>> = steps_decl.iter().cloned().collect();
let decl_index: Map<NodeId<'id>, usize> = steps_decl
.iter()
.enumerate()
.map(|(index, node_id)| (node_id.clone(), index))
.collect();
let rank_of =
|node_id: &NodeId<'id>| process_step_ranks.get(node_id).copied().unwrap_or_default();
let mut rows = steps_decl.clone();
rows.sort_by_key(|node_id| rank_of(node_id));
let row_of: Map<NodeId<'id>, u32> = rows
.iter()
.enumerate()
.map(|(row, node_id)| (node_id.clone(), row as u32))
.collect();
let mut out_targets: Map<NodeId<'id>, Vec<NodeId<'id>>> = Map::new();
for edge in process_step_edges.iter() {
if step_set.contains(&edge.from) && step_set.contains(&edge.to) {
out_targets
.entry(edge.from.clone())
.or_default()
.push(edge.to.clone());
}
}
for targets in out_targets.values_mut() {
targets.sort_by(|node_id_a, node_id_b| {
let row_a = row_of.get(node_id_a).copied().unwrap_or(0);
let row_b = row_of.get(node_id_b).copied().unwrap_or(0);
row_a.cmp(&row_b).then_with(|| {
let decl_a = decl_index.get(node_id_a).copied().unwrap_or(0);
let decl_b = decl_index.get(node_id_b).copied().unwrap_or(0);
decl_a.cmp(&decl_b)
})
});
}
let mut active_lanes: Vec<Option<NodeId<'id>>> = Vec::new();
let mut step_placements: Map<NodeId<'id>, ProcessStepPlacement> = Map::new();
let mut edges: Vec<ProcessStepGraphEdge<'id>> = Vec::new();
let mut max_lane: u32 = 0;
for (row, step) in rows.iter().enumerate() {
let row = row as u32;
let incoming_lanes: Vec<usize> = active_lanes
.iter()
.enumerate()
.filter_map(|(lane, target)| {
target
.as_ref()
.filter(|target| *target == step)
.map(|_| lane)
})
.collect();
let lane = incoming_lanes
.first()
.copied()
.unwrap_or_else(|| Self::lane_first_free(&mut active_lanes));
for incoming_lane in &incoming_lanes {
active_lanes[*incoming_lane] = None;
}
step_placements.insert(
step.clone(),
ProcessStepPlacement::new(row, ProcessStepLane::new(lane as u32)),
);
max_lane = max_lane.max(lane as u32);
if let Some(targets) = out_targets.get(step) {
for (index, target) in targets.iter().enumerate() {
let is_back_edge = row_of.get(target).copied().unwrap_or(0) <= row;
let edge_lane = if index == 0 || is_back_edge {
lane
} else {
Self::lane_first_free(&mut active_lanes)
};
if !is_back_edge {
active_lanes[edge_lane] = Some(target.clone());
}
edges.push(ProcessStepGraphEdge::new(
step.clone(),
target.clone(),
ProcessStepLane::new(edge_lane as u32),
));
max_lane = max_lane.max(edge_lane as u32);
}
}
}
ProcessStepGraph {
lane_count: max_lane + 1,
step_placements,
edges,
}
}
fn lane_first_free(active_lanes: &mut Vec<Option<NodeId<'_>>>) -> usize {
if let Some(index) = active_lanes.iter().position(Option::is_none) {
index
} else {
active_lanes.push(None);
active_lanes.len() - 1
}
}
}