1#[cfg(feature = "generic")]
11pub mod generic;
12
13use crate::graph::DAG;
14use alloc::{vec, vec::Vec};
15
16impl<'a> DAG<'a> {
17 pub(crate) fn calculate_levels(&self) -> Vec<(usize, usize)> {
22 let mut levels = vec![0usize; self.nodes.len()];
23 let mut changed = true;
24
25 while changed {
26 changed = false;
27 for &(from, to) in &self.edges {
28 if let Some(from_idx) = self.node_index(from) {
30 if let Some(to_idx) = self.node_index(to) {
31 let new_level = levels[from_idx] + 1;
32 if new_level > levels[to_idx] {
33 levels[to_idx] = new_level;
34 changed = true;
35 }
36 }
37 }
38 }
39 }
40
41 levels.into_iter().enumerate().collect()
42 }
43
44 pub(crate) fn calculate_levels_for_subgraph(
46 &self,
47 subgraph_indices: &[usize],
48 ) -> Vec<(usize, usize)> {
49 let subgraph_node_ids: Vec<usize> = subgraph_indices
50 .iter()
51 .map(|&idx| self.nodes[idx].0)
52 .collect();
53
54 let mut levels = vec![0usize; self.nodes.len()];
55 let mut changed = true;
56
57 while changed {
58 changed = false;
59 for &(from, to) in &self.edges {
60 if !subgraph_node_ids.contains(&from) || !subgraph_node_ids.contains(&to) {
62 continue;
63 }
64
65 if let Some(from_idx) = self.node_index(from) {
67 if let Some(to_idx) = self.node_index(to) {
68 let new_level = levels[from_idx] + 1;
69 if new_level > levels[to_idx] {
70 levels[to_idx] = new_level;
71 changed = true;
72 }
73 }
74 }
75 }
76 }
77
78 subgraph_indices
79 .iter()
80 .map(|&idx| (idx, levels[idx]))
81 .collect()
82 }
83
84 pub(crate) fn reduce_crossings(&self, levels: &mut [Vec<usize>], max_level: usize) {
89 for _ in 0..4 {
91 for level_idx in 1..=max_level {
93 let (prev_levels, rest) = levels.split_at_mut(level_idx);
95 let parent_level = &prev_levels[level_idx - 1];
96 self.order_by_median_parents(&mut rest[0], parent_level);
97 }
98
99 for level_idx in (0..max_level).rev() {
101 let (left, right) = levels.split_at_mut(level_idx + 1);
103 let child_level = &right[0];
104 self.order_by_median_children(&mut left[level_idx], child_level);
105 }
106 }
107 }
108
109 fn order_by_median_parents(&self, level_nodes: &mut Vec<usize>, parent_level: &[usize]) {
111 let mut node_medians: Vec<(usize, f32)> = Vec::new();
112
113 for (pos, &idx) in level_nodes.iter().enumerate() {
114 let node_id = self.nodes[idx].0;
115 let parents = self.get_parents(node_id);
116
117 if parents.is_empty() {
118 node_medians.push((idx, pos as f32));
119 } else {
120 let mut parent_positions: Vec<usize> = parents
122 .iter()
123 .filter_map(|&p_id| parent_level.iter().position(|&i| self.nodes[i].0 == p_id))
124 .collect();
125 parent_positions.sort_unstable();
126
127 let median = if parent_positions.is_empty() {
128 pos as f32
129 } else if parent_positions.len() % 2 == 1 {
130 parent_positions[parent_positions.len() / 2] as f32
131 } else {
132 let mid = parent_positions.len() / 2;
133 (parent_positions[mid - 1] + parent_positions[mid]) as f32 / 2.0
134 };
135
136 node_medians.push((idx, median));
137 }
138 }
139
140 node_medians.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
142 *level_nodes = node_medians.iter().map(|(idx, _)| *idx).collect();
143 }
144
145 fn order_by_median_children(&self, level_nodes: &mut Vec<usize>, child_level: &[usize]) {
147 let mut node_medians: Vec<(usize, f32)> = Vec::new();
148
149 for (pos, &idx) in level_nodes.iter().enumerate() {
150 let node_id = self.nodes[idx].0;
151 let children = self.get_children(node_id);
152
153 if children.is_empty() {
154 node_medians.push((idx, pos as f32));
155 } else {
156 let mut child_positions: Vec<usize> = children
158 .iter()
159 .filter_map(|&c_id| child_level.iter().position(|&i| self.nodes[i].0 == c_id))
160 .collect();
161 child_positions.sort_unstable();
162
163 let median = if child_positions.is_empty() {
164 pos as f32
165 } else if child_positions.len() % 2 == 1 {
166 child_positions[child_positions.len() / 2] as f32
167 } else {
168 let mid = child_positions.len() / 2;
169 (child_positions[mid - 1] + child_positions[mid]) as f32 / 2.0
170 };
171
172 node_medians.push((idx, median));
173 }
174 }
175
176 node_medians.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
177 *level_nodes = node_medians.iter().map(|(idx, _)| *idx).collect();
178 }
179
180 pub(crate) fn assign_x_coordinates(
185 &self,
186 levels: &mut [Vec<usize>],
187 max_level: usize,
188 ) -> Vec<usize> {
189 let mut x_coords = vec![0usize; self.nodes.len()];
190
191 for level_nodes in levels.iter() {
193 let mut x = 0;
194 for &idx in level_nodes.iter() {
195 x_coords[idx] = x;
196 let width = self.get_node_width(idx);
197 x += width + 3;
198 }
199 }
200
201 for _ in 0..2 {
204 for level_idx in 1..=max_level {
206 for &idx in &levels[level_idx] {
207 let node_id = self.nodes[idx].0;
208 let parents = self.get_parents(node_id);
209
210 if !parents.is_empty() {
211 let mut parent_centers: Vec<usize> = Vec::new();
212 for &p_id in &parents {
213 if let Some(p_idx) = self.node_index(p_id) {
215 let width = self.get_node_width(p_idx); parent_centers.push(x_coords[p_idx] + width / 2);
217 }
218 }
219
220 if !parent_centers.is_empty() {
221 parent_centers.sort_unstable();
222 let median = parent_centers[parent_centers.len() / 2];
223 let width = self.get_node_width(idx);
224 let target = median.saturating_sub(width / 2);
226 x_coords[idx] = (x_coords[idx] + target) / 2;
227 }
228 }
229 }
230
231 self.compact_level(&mut x_coords, &mut levels[level_idx]);
233 }
234 }
235
236 x_coords
237 }
238
239 pub(crate) fn compact_level(&self, x_coords: &mut [usize], level_nodes: &mut Vec<usize>) {
241 if level_nodes.is_empty() {
242 return;
243 }
244
245 let mut sorted: Vec<_> = level_nodes
247 .iter()
248 .map(|&idx| (x_coords[idx], idx))
249 .collect();
250 sorted.sort_by_key(|(x, _)| *x);
251
252 level_nodes.clear();
254 let mut x = 0;
255 for (_, idx) in sorted {
256 level_nodes.push(idx);
257 x_coords[idx] = x;
258 let width = self.get_node_width(idx);
259 x += width + 3;
260 }
261 }
262
263 pub(crate) fn calculate_canvas_dimensions(
267 &self,
268 levels: &[Vec<usize>],
269 x_coords: &[usize],
270 ) -> (Vec<usize>, usize) {
271 let mut level_widths = Vec::new();
272 let mut max_width = 0;
273
274 for level_nodes in levels {
275 if level_nodes.is_empty() {
276 level_widths.push(0);
277 continue;
278 }
279
280 let min_x = level_nodes
281 .iter()
282 .map(|&idx| x_coords[idx])
283 .min()
284 .unwrap_or(0);
285 let max_node_idx = level_nodes
286 .iter()
287 .max_by_key(|&&idx| x_coords[idx])
288 .unwrap();
289 let width = self.get_node_width(*max_node_idx);
290 let level_width = (x_coords[*max_node_idx] - min_x) + width;
291
292 level_widths.push(level_width);
293 max_width = max_width.max(level_width);
294 }
295
296 (level_widths, max_width)
297 }
298
299 pub(crate) fn find_subgraphs(&self) -> Vec<Vec<usize>> {
301 let mut visited = vec![false; self.nodes.len()];
302 let mut subgraphs = Vec::new();
303
304 for i in 0..self.nodes.len() {
305 if !visited[i] {
306 let mut subgraph = Vec::new();
307 self.collect_connected(i, &mut visited, &mut subgraph);
308 subgraphs.push(subgraph);
309 }
310 }
311
312 subgraphs
313 }
314
315 fn collect_connected(&self, start_idx: usize, visited: &mut [bool], subgraph: &mut Vec<usize>) {
317 if visited[start_idx] {
318 return;
319 }
320
321 visited[start_idx] = true;
322 subgraph.push(start_idx);
323
324 let node_id = self.nodes[start_idx].0;
325
326 for &(from, to) in &self.edges {
328 if from == node_id {
329 if let Some(child_idx) = self.node_index(to) {
331 self.collect_connected(child_idx, visited, subgraph);
332 }
333 }
334 if to == node_id {
335 if let Some(parent_idx) = self.node_index(from) {
337 self.collect_connected(parent_idx, visited, subgraph);
338 }
339 }
340 }
341 }
342
343 pub(crate) fn is_subgraph_simple_chain(&self, subgraph_indices: &[usize]) -> bool {
345 for &idx in subgraph_indices {
346 let node_id = self.nodes[idx].0;
347 let parents = self.get_parents(node_id);
348 let children = self.get_children(node_id);
349
350 if parents.len() > 1 || children.len() > 1 {
351 return false;
352 }
353 }
354 true
355 }
356}
357
358#[cfg(test)]
359mod tests {
360 use crate::graph::DAG;
361
362 #[test]
363 fn test_calculate_levels() {
364 let dag = DAG::from_edges(&[(1, "A"), (2, "B"), (3, "C")], &[(1, 2), (2, 3)]);
365
366 let levels = dag.calculate_levels();
367
368 let level_map: std::collections::HashMap<_, _> = levels
370 .into_iter()
371 .map(|(idx, level)| (dag.nodes[idx].0, level))
372 .collect();
373
374 assert_eq!(level_map[&1], 0); assert_eq!(level_map[&2], 1);
376 assert_eq!(level_map[&3], 2);
377 }
378
379 #[test]
380 fn test_diamond_layout() {
381 let dag = DAG::from_edges(
382 &[(1, "Top"), (2, "Left"), (3, "Right"), (4, "Bottom")],
383 &[(1, 2), (1, 3), (2, 4), (3, 4)],
384 );
385
386 let levels = dag.calculate_levels();
387 let level_map: std::collections::HashMap<_, _> = levels
388 .into_iter()
389 .map(|(idx, level)| (dag.nodes[idx].0, level))
390 .collect();
391
392 assert_eq!(level_map[&1], 0); assert_eq!(level_map[&2], 1); assert_eq!(level_map[&3], 1);
395 assert_eq!(level_map[&4], 2); }
397}