1use std::collections::{BTreeMap, HashMap};
20
21use log::info;
22use petgraph::stable_graph::{EdgeIndex, NodeIndex, StableDiGraph};
23
24mod config;
26pub mod types;
27mod util;
28
29pub use config::{Config, CrossingMinimization, RankingType, VERTEX_SPACING_DEFAULT};
31#[allow(unused_imports)]
33use p0_cycle_removal as p0;
34use p1_layering as p1;
35use p2_reduce_crossings as p2;
36use p3_calculate_coordinates as p3;
37
38mod p0_cycle_removal;
41mod p1_layering;
42mod p2_reduce_crossings;
43mod p3_calculate_coordinates;
44
45#[derive(Debug, Clone, Copy, PartialEq)]
49pub struct Vertex {
50 pub input_node_idx: Option<NodeIndex>,
51 sort_bias: i32,
57 size: (f64, f64),
58 rank: i32,
59 pos: usize,
60 low: u32,
61 lim: u32,
62 parent: Option<NodeIndex>,
63 is_tree_vertex: bool,
64 is_dummy: bool,
65 root: NodeIndex,
66 align: NodeIndex,
67 shift: f64,
68 sink: NodeIndex,
69 block_max_vertex_width: f64,
70}
71
72impl Vertex {
73 pub fn new(original_id: NodeIndex) -> Self {
74 Self {
75 input_node_idx: Some(original_id),
76 ..Default::default()
77 }
78 }
79
80 pub fn new_dummy() -> Self {
81 Self {
82 input_node_idx: None,
83 is_dummy: true,
84 ..Default::default()
85 }
86 }
87
88 pub fn with_sort_bias(mut self, sort_bias: i32) -> Self {
89 self.sort_bias = sort_bias;
90 self
91 }
92
93 pub fn set_sort_bias(&mut self, sort_bias: i32) {
94 self.sort_bias = sort_bias;
95 }
96
97 pub fn sort_bias(&self) -> i32 {
98 self.sort_bias
99 }
100
101 pub fn set_size(&mut self, (width, height): (u64, u64), vertex_spacing: f64) {
102 self.size = (
107 (height.max(1) as f64 + vertex_spacing),
108 (width.max(1) as f64 + vertex_spacing),
109 );
110 }
111
112 pub fn get_size(&self, vertex_spacing: f64) -> (u64, u64) {
113 let (width, height) = self.size;
115 (
117 (height - vertex_spacing).round() as u64,
118 (width - vertex_spacing).round() as u64,
119 )
120 }
121
122 pub fn is_dummy(&self) -> bool {
123 self.is_dummy
124 }
125
126 pub fn get_rank(&self) -> i32 {
127 self.rank
128 }
129}
130
131impl Default for Vertex {
132 fn default() -> Self {
133 Self {
134 input_node_idx: None,
135 sort_bias: 0,
136 size: (1.0, 1.0),
137 rank: 0,
138 pos: 0,
139 low: 0,
140 lim: 0,
141 parent: None,
142 is_tree_vertex: false,
143 is_dummy: false,
144 root: 0.into(),
145 align: 0.into(),
146 shift: f64::INFINITY,
147 sink: 0.into(),
148 block_max_vertex_width: 0.0,
149 }
150 }
151}
152
153#[derive(Clone, Copy, Debug)]
154pub struct Edge {
155 pub weight: i32,
156 cut_value: Option<i32>,
157 is_tree_edge: bool,
158 has_type_1_conflict: bool,
159 pub input_node_idx_pair: Option<(NodeIndex, NodeIndex)>,
160}
161
162impl Default for Edge {
163 fn default() -> Self {
164 Self {
165 weight: 1,
166 cut_value: None,
167 is_tree_edge: false,
168 has_type_1_conflict: false,
169 input_node_idx_pair: None,
170 }
171 }
172}
173
174impl Edge {
175 pub fn with_label(self, (tail, head): (NodeIndex, NodeIndex)) -> Self {
176 Self {
177 input_node_idx_pair: Some((tail, head)),
178 ..self
179 }
180 }
181}
182
183pub fn run_sugiyama_algorithm(
186 vertex_graph: &mut StableDiGraph<Vertex, Edge>,
187 config: &Config,
188) -> Vec<Vec<NodeIndex>> {
189 info!(target: "layouting", "Start building layout");
190 info!(target: "layouting", "Configuration is: {:?}", config);
191
192 info!(target: "layouting", "Skipping phase 0: Cycle Removal");
194 info!(target: "layouting", "Executing phase 1: Ranking");
199 p1::rank(
200 vertex_graph,
201 config.minimum_length as i32,
202 config.ranking_type,
203 );
204
205 info!(target: "layouting", "Executing phase 2: Crossing Reduction");
207 info!(target: "layouting",
208 "dummy vertex size: {:?}, heuristic for crossing minimization: {:?}, using transpose: {}",
209 config.dummy_size,
210 config.c_minimization,
211 config.transpose
212 );
213
214 let _dropped_edges = p2::insert_dummy_vertices(
215 vertex_graph,
216 config.minimum_length as i32,
217 config.dummy_size,
218 );
219
220 let mut layers = p2::ordering(vertex_graph, config.c_minimization, config.transpose);
221 if !config.dummy_vertices {
222 p2::remove_dummy_vertices(vertex_graph, &mut layers);
223 }
224
225 layers
226}
227
228pub fn assign_coordinates(
231 layers: &mut [Vec<NodeIndex>],
232 vertex_graph: &mut StableDiGraph<Vertex, Edge>,
233) -> Vec<(NodeIndex, (i64, i64))> {
234 info!(target: "layouting", "Executing phase 3: Coordinate Calculation on a vertex graph with {} nodes and {} edges", vertex_graph.node_count(), vertex_graph.edge_count());
236 let mut layouts = p3::create_layouts(vertex_graph, layers);
237 p3::align_to_smallest_width_layout(&mut layouts);
238
239 let mut x_coordinates = p3::calculate_relative_coords(layouts);
241 let min = x_coordinates
242 .iter()
243 .min_by(|a, b| a.1.total_cmp(&b.1))
244 .unwrap()
245 .1;
246 for (_, c) in &mut x_coordinates {
247 *c -= min;
248 }
249
250 let mut rank_to_max_height = BTreeMap::<i32, f64>::new();
252 for vertex in vertex_graph.node_weights() {
253 let max = rank_to_max_height.entry(vertex.rank).or_default();
254 *max = max.max(vertex.size.1);
255 }
256
257 let mut rank_to_y_offset = HashMap::new();
258 let mut current_rank_top_offset = *rank_to_max_height.iter().next().unwrap().1 * -0.5;
259 for (rank, max_height) in rank_to_max_height {
260 rank_to_y_offset.insert(rank, current_rank_top_offset + max_height * 0.5);
261 current_rank_top_offset += max_height;
262 }
263
264 x_coordinates
268 .into_iter()
269 .map(|(v, x)| {
270 (
271 v,
272 (x, *rank_to_y_offset.get(&vertex_graph[v].rank).unwrap()),
273 )
274 })
275 .map(|(i, (x, y))| (i, (y.floor() as i64, x.floor() as i64)))
277 .collect::<Vec<(NodeIndex, (i64, i64))>>()
278}
279
280fn slack(graph: &StableDiGraph<Vertex, Edge>, edge: EdgeIndex, minimum_length: i32) -> i32 {
281 let (tail, head) = graph.edge_endpoints(edge).unwrap();
282 graph[head].rank - graph[tail].rank - minimum_length
283}