dagre_rust/layout/
mod.rs

1use graphlib_rust::{Graph, GraphOption};
2use crate::{GraphConfig, GraphEdge, GraphEdgePoint, GraphNode};
3use crate::layout::add_border_segments::add_border_segments;
4use crate::layout::order::order;
5use crate::layout::parent_dummy_chains::parent_dummy_chains;
6use crate::layout::rank::rank;
7use crate::layout::util::{as_non_compound_graph, intersect_rect, normalize_ranks, Rect, remove_empty_ranks, transfer_node_edge_labels};
8
9pub mod acyclic;
10pub mod nesting_graph;
11pub mod util;
12pub mod rank;
13pub mod normalize;
14pub mod parent_dummy_chains;
15pub mod add_border_segments;
16pub mod order;
17pub mod coordinate_system;
18pub mod position;
19
20const DEFAULT_RANK_SEP: f32 = 50.0;
21
22pub fn layout(g: &mut Graph<GraphConfig, GraphNode, GraphEdge>) {
23  let mut layout_graph = build_layout_graph(g);
24  run_layout(&mut layout_graph);
25  update_input_graph(g, &layout_graph);
26}
27
28pub fn update_input_graph(
29  input_graph: &mut Graph<GraphConfig, GraphNode, GraphEdge>,
30  layout_graph: &Graph<GraphConfig, GraphNode, GraphEdge>,
31) {
32  for v in input_graph.nodes() {
33    let input_label_ = input_graph.node_mut(&v);
34    let layout_label = layout_graph.node(&v).unwrap();
35
36    if let Some(input_label) = input_label_ {
37      input_label.x = layout_label.x;
38      input_label.y = layout_label.y;
39
40      if layout_graph.children(&v).len() > 0 {
41        input_label.width = layout_label.width;
42        input_label.height = layout_label.height;
43      }
44    }
45  }
46
47  for e in input_graph.edges() {
48    let input_label = input_graph.edge_mut_with_obj(&e).unwrap();
49    let layout_label = layout_graph.edge_with_obj(&e).unwrap();
50
51    input_label.points = layout_label.points.clone();
52    input_label.x = layout_label.x;
53    input_label.y = layout_label.y;
54  }
55
56  input_graph.graph_mut().width = layout_graph.graph().width;
57  input_graph.graph_mut().height = layout_graph.graph().height;
58}
59
60pub fn set_graph_label_default_values(graph_label: &mut GraphConfig) {
61  if graph_label.ranksep.is_none() {
62    graph_label.ranksep = Some(50.0);
63  }
64
65  if graph_label.edgesep.is_none() {
66    graph_label.edgesep = Some(20.0);
67  }
68
69  if graph_label.nodesep.is_none() {
70    graph_label.nodesep = Some(50.0);
71  }
72
73  if graph_label.rankdir.is_none() {
74    graph_label.rankdir = Some("tb".to_string());
75  }
76
77  if graph_label.marginx.is_none() {
78    graph_label.marginx = Some(0.0);
79  }
80
81  if graph_label.marginy.is_none() {
82    graph_label.marginy = Some(0.0);
83  }
84}
85
86pub fn set_edge_label_default_values(edge_label: &mut GraphEdge) {
87  if edge_label.minlen.is_none() {
88    edge_label.minlen = Some(1.0);
89  }
90
91  if edge_label.weight.is_none() {
92    edge_label.weight = Some(1.0);
93  }
94
95  if edge_label.width.is_none() {
96    edge_label.width = Some(0.0);
97  }
98
99  if edge_label.height.is_none() {
100    edge_label.height = Some(0.0);
101  }
102
103  if edge_label.labeloffset.is_none() {
104    edge_label.labeloffset = Some(10.0);
105  }
106
107  if edge_label.labelpos.is_none() {
108    edge_label.labelpos = Some("r".to_string());
109  }
110}
111
112/*
113 * Constructs a new graph from the input graph, which can be used for layout.
114 * This process copies only whitelisted attributes from the input graph to the
115 * layout graph. Thus this function serves as a good place to determine what
116 * attributes can influence layout.
117 */
118pub fn build_layout_graph(input_graph: &Graph<GraphConfig, GraphNode, GraphEdge>) -> Graph<GraphConfig, GraphNode, GraphEdge> {
119  let mut g: Graph<GraphConfig, GraphNode, GraphEdge> = Graph::new(Some(GraphOption {
120    directed: Some(true),
121    multigraph: Some(true),
122    compound: Some(true)
123  }));
124
125  let mut graph_label: GraphConfig = input_graph.graph().clone();
126  set_graph_label_default_values(&mut graph_label);
127  g.set_graph(graph_label);
128
129  for node_id in input_graph.nodes().iter() {
130    let _node = input_graph.node(node_id);
131    if _node.is_none() {
132      continue;
133    }
134    g.set_node(node_id.clone(), _node.cloned());
135    let _ = g.set_parent(node_id, input_graph.parent(node_id).cloned());
136  }
137
138  for edge_obj in input_graph.edges() {
139    let _edge = input_graph.edge_with_obj(&edge_obj);
140    if _edge.is_none() {
141      continue;
142    }
143    let mut edge_label = _edge.cloned().unwrap();
144    set_edge_label_default_values(&mut edge_label);
145    let _ = g.set_edge_with_obj(&edge_obj, Some(edge_label));
146  }
147
148  return g;
149}
150
151/*
152 * This idea comes from the Gansner paper: to account for edge labels in our
153 * layout we split each rank in half by doubling minlen and halving ranksep.
154 * Then we can place labels at these mid-points between nodes.
155 *
156 * We also add some minimal padding to the width to push the label for the edge
157 * away from the edge itself a bit.
158 */
159pub fn make_space_for_edge_labels(graph: &mut Graph<GraphConfig, GraphNode, GraphEdge>) {
160  let graph_config = graph.graph_mut();
161  graph_config.ranksep = Some(graph_config.ranksep.unwrap_or(DEFAULT_RANK_SEP) / 2.0);
162
163  // moving in nested block due to borrow checker
164  {
165    let graph_config = graph.graph().clone();
166    let edge_objs = graph.edges();
167    for edge_obj in edge_objs.into_iter() {
168      let _edge = graph.edge_mut_with_obj(&edge_obj);
169      if _edge.is_none() {
170        continue;
171      }
172      let edge = _edge.unwrap();
173
174      let minlen = edge.minlen.unwrap_or(1.0);
175      let labelpos = edge.labelpos.clone().unwrap_or("".to_string());
176      let labeloffset = edge.labeloffset.unwrap_or(10.0);
177      let rankdir = graph_config.rankdir.clone().unwrap_or("".to_string());
178
179      edge.minlen = Some(minlen * 2.0);
180      if labelpos != "c" {
181        if rankdir == "tb" || rankdir == "bt" {
182          edge.width = Some(edge.width.unwrap_or(0.0) + labeloffset);
183        } else {
184          edge.height = Some(edge.height.unwrap_or(0.0) + labeloffset);
185        }
186      }
187    }
188  }
189}
190
191/*
192 * Creates temporary dummy nodes that capture the rank in which each edge's
193 * label is going to, if it has one of non-zero width and height. We do this
194 * so that we can safely remove empty ranks while preserving balance for the
195 * label's position.
196 */
197pub fn inject_edge_label_proxies(g: &mut Graph<GraphConfig, GraphNode, GraphEdge>) {
198  let edges = g.edges();
199  for e in edges.into_iter() {
200    let edge_ = g.edge_with_obj(&e);
201    if let Some(edge) = edge_ {
202      if edge.width.clone().unwrap_or(0.0) > 0.0 && edge.height.clone().unwrap_or(0.0) > 0.0 {
203        let v = g.node(&e.v);
204        let w = g.node(&e.v);
205        let v_rank = v.cloned().unwrap_or(GraphNode::default()).rank.unwrap_or(0);
206        let w_rank = w.cloned().unwrap_or(GraphNode::default()).rank.unwrap_or(0);
207        let mut label = GraphNode::default();
208        label.rank = Some((w_rank - v_rank) / 2 + v_rank);
209        util::add_dummy_node(g, "edge-proxy".to_string(), label, "_ep".to_string());
210      }
211    }
212  }
213}
214
215pub fn assign_rank_min_max(g: &mut Graph<GraphConfig, GraphNode, GraphEdge>) {
216  let mut max_rank = 0;
217  let vs = g.nodes();
218  for v in vs.iter() {
219    let node_ = g.node(v);
220    if node_.is_none() {
221      continue;
222    }
223    let node = node_.unwrap();
224    if node.border_top.is_some() {
225      let border_top = node.border_top.clone().unwrap();
226      let border_bottom = node.border_bottom.clone().unwrap();
227      let _min_rank = g.node(&border_top).cloned().unwrap().rank.unwrap_or(0);
228      let _max_rank = g.node(&border_bottom).cloned().unwrap().rank.unwrap_or(0);
229
230      let _node = g.node_mut(v).unwrap();
231      _node.min_rank = Some(_min_rank);
232      _node.max_rank = Some(_max_rank.clone());
233      max_rank = std::cmp::max(max_rank, _max_rank);
234    }
235  }
236}
237
238enum GraphElement<'a> {
239  Node(&'a GraphNode),
240  Edge(&'a GraphEdge),
241}
242
243impl<'a> GraphElement<'a> {
244  fn x(&self) -> f32 {
245    match self {
246      GraphElement::Node(node) => node.x,
247      GraphElement::Edge(edge) => edge.x,
248    }
249  }
250
251  fn y(&self) -> f32 {
252    match self {
253      GraphElement::Node(node) => node.y,
254      GraphElement::Edge(edge) => edge.y,
255    }
256  }
257
258  fn width(&self) -> f32 {
259    match self {
260      GraphElement::Node(node) => node.width,
261      GraphElement::Edge(edge) => edge.width.unwrap_or(0.0),
262    }
263  }
264
265  fn height(&self) -> f32 {
266    match self {
267      GraphElement::Node(node) => node.height,
268      GraphElement::Edge(edge) => edge.height.unwrap_or(0.0),
269    }
270  }
271}
272
273pub fn translate_graph(g: &mut Graph<GraphConfig, GraphNode, GraphEdge>) {
274  let mut min_x = f64::INFINITY as f32;
275  let mut max_x: f32 = 0.0;
276  let mut min_y = f64::INFINITY as f32;
277  let mut max_y: f32 = 0.0;
278  let mut graph_label = g.graph().clone();
279  let margin_x = graph_label.marginx.unwrap_or(0.0);
280  let margin_y = graph_label.marginy.unwrap_or(0.0);
281
282  fn get_extremes(attrs: &GraphElement, min_x: &mut f32, max_x: &mut f32, min_y: &mut f32, max_y: &mut f32) {
283    let x = attrs.x();
284    let y = attrs.y();
285    let w = attrs.width();
286    let h = attrs.height();
287    *min_x = min_x.min(x - w / 2.0);
288    *max_x = max_x.max(x + w / 2.0);
289    *min_y = min_y.min(y - h / 2.0);
290    *max_y = max_y.max(y + h / 2.0);
291  }
292
293  for v in g.nodes() {
294    get_extremes(&GraphElement::Node(g.node(&v).unwrap()), &mut min_x, &mut max_x, &mut min_y, &mut max_y);
295  }
296
297  for e in g.edges() {
298    let edge = g.edge_with_obj(&e).unwrap();
299    if edge.x != 0.0 {
300      get_extremes(&GraphElement::Edge(edge), &mut min_x, &mut max_x, &mut min_y, &mut max_y);
301    }
302  }
303
304  min_x -= margin_x;
305  min_y -= margin_y;
306
307  for v in g.nodes() {
308    let mut node = g.node_mut(&v).unwrap();
309    node.x -= min_x;
310    node.y -= min_y;
311  }
312
313  for e in g.edges() {
314    let edge = g.edge_mut_with_obj(&e).unwrap();
315    if edge.points.is_some() {
316      for p in edge.points.as_mut().unwrap() {
317        p.x -= min_x;
318        p.y -= min_y;
319      }
320    }
321    if edge.x != 0.0 {
322      edge.x = min_x;
323    }
324
325    if edge.y != 0.0 {
326      edge.y = min_y;
327    }
328  }
329
330  graph_label.width = max_x - min_x + margin_x;
331  graph_label.height = max_y - min_y + margin_y;
332
333  g.set_graph(graph_label);
334}
335
336pub fn assign_node_intersects(g: &mut Graph<GraphConfig, GraphNode, GraphEdge>) {
337  for e in g.edges() {
338    let mut edge = g.edge_mut_with_obj(&e).cloned().unwrap();
339    let node_v = g.node(&e.v).cloned().unwrap();
340    let node_w = g.node(&e.w).cloned().unwrap();
341    let (p1, p2) = if edge.points.is_none() {
342      edge.points = Some(vec![]);
343      (GraphEdgePoint {
344        x: node_w.x,
345        y: node_w.y
346      }, GraphEdgePoint {
347        x: node_v.x,
348        y: node_v.y
349      })
350    } else {
351      let points = edge.points.clone().unwrap();
352      let r1 = GraphEdgePoint {
353        x: points[0].x,
354        y: points[0].y
355      };
356
357      let r2 = GraphEdgePoint {
358        x: points[points.len() - 1].x,
359        y: points[points.len() - 1].y
360      };
361
362      (r1, r2)
363    };
364
365    let points = edge.points.as_mut().unwrap();
366    points.insert(0, intersect_rect(&Rect {
367      x: node_v.x,
368      y: node_v.y,
369      width: node_v.width,
370      height: node_v.height
371    }, &p1));
372
373    points.push(intersect_rect(&Rect {
374      x: node_w.x,
375      y: node_w.y,
376      width: node_w.width,
377      height: node_w.height
378    }, &p2));
379
380    let _ = g.set_edge_with_obj(&e, Some(edge));
381  }
382}
383
384pub fn remove_edge_label_proxies(g: &mut Graph<GraphConfig, GraphNode, GraphEdge>) {
385  let vs = g.nodes();
386  for v in vs.iter() {
387    let node = g.node(v).unwrap();
388    if node.dummy.is_some() && node.dummy.clone().unwrap() == "edge-proxy" {
389      let rank = node.rank.unwrap_or(0);
390      let graph_edge_ = g.edge_mut_with_obj(&node.e.clone().unwrap());
391      if let Some(graph_edge) = graph_edge_ {
392        graph_edge.label_rank = Some(rank);
393      }
394      g.remove_node(v);
395    }
396  }
397}
398
399pub fn fixup_edge_label_coords(g: &mut Graph<GraphConfig, GraphNode, GraphEdge>) {
400  g.edges().iter().for_each(|e| {
401    let edge = g.edge_mut_with_obj(&e.to_owned()).unwrap();
402    if edge.x != 0.0 {
403      let labelpos = edge.labelpos.clone().unwrap_or("".to_string());
404      let labeloffset = edge.labeloffset.clone().unwrap_or(0.0);
405      if labelpos == "l" || labelpos == "r" {
406        edge.width = Some(edge.width.unwrap_or(0.0) - labeloffset);
407      }
408
409      if labelpos == "l" {
410        edge.x -= edge.width.clone().unwrap_or(0.0) / 2.0 + labeloffset;
411      } else if labelpos == "r" {
412        edge.x += edge.width.clone().unwrap_or(0.0) / 2.0 + labeloffset;
413      }
414    }
415  });
416}
417
418pub fn reverse_points_for_reversed_edges(g: &mut Graph<GraphConfig, GraphNode, GraphEdge>) {
419  for e in g.edges() {
420    let edge = g.edge_mut_with_obj(&e.to_owned()).unwrap();
421    if edge.reversed.clone().unwrap_or(false) {
422      if edge.points.is_some() {
423        let points = edge.points.as_mut().unwrap();
424        points.reverse();
425      }
426    }
427  }
428}
429
430pub fn remove_border_nodes(g: &mut Graph<GraphConfig, GraphNode, GraphEdge>) {
431  for v in g.nodes() {
432    if g.children(&v).len() > 0 {
433      let mut node = g.node(&v).cloned().unwrap();
434      let t = g.node(node.border_top.as_ref().unwrap()).cloned().unwrap();
435      let b = g.node(node.border_bottom.as_ref().unwrap()).cloned().unwrap();
436      // TODO: improve this after wasm implementation
437      let border_left = node.border_left.clone().unwrap();
438      let mut l_keys: Vec<i32> = border_left.keys().cloned().collect();
439      l_keys.sort();
440      let border_right = node.border_right.clone().unwrap();
441      let mut r_keys: Vec<i32> = border_right.keys().cloned().collect();
442      r_keys.sort();
443      let l = g.node(border_left.get(&l_keys[l_keys.len() - 1]).unwrap()).cloned().unwrap();
444      let r = g.node(border_right.get(&r_keys[r_keys.len() - 1]).unwrap()).cloned().unwrap();
445
446      node.width = (r.x - l.x).abs();
447      node.height = (b.y - t.y).abs();
448      node.x = l.x + node.width / 2.0;
449      node.y = l.y + node.height / 2.0;
450
451      g.set_node(v.clone(), Some(node));
452    }
453  }
454
455  g.nodes().iter().for_each(|v| {
456    let node = g.node(v).unwrap();
457    if node.dummy.is_some() && node.dummy.clone().unwrap() == "border" {
458      g.remove_node(v);
459    }
460  });
461}
462
463pub fn remove_self_edges(graph: &mut Graph<GraphConfig, GraphNode, GraphEdge>) {
464  let edge_objs = graph.edges();
465  for edge_obj in edge_objs.into_iter() {
466    if edge_obj.v == edge_obj.w {
467      let edge_label = graph.edge_with_obj(&edge_obj).cloned().unwrap();
468      let node = graph.node_mut(&edge_obj.v).unwrap();
469      node.self_edges.push((edge_obj.clone(), edge_label));
470      graph.remove_edge_with_obj(&edge_obj);
471    }
472  }
473}
474
475pub fn insert_self_edges(graph: &mut Graph<GraphConfig, GraphNode, GraphEdge>) {
476  let layers = util::build_layer_matrix(graph);
477  layers.iter().for_each(|layer| {
478    let mut order_shift = 0;
479    layer.iter().enumerate().for_each(|(i, v)| {
480      let node = graph.node_mut(v).unwrap();
481      node.order = Some(i + order_shift);
482      let rank = node.rank.clone();
483
484      let self_edges = node.self_edges.clone();
485      self_edges.into_iter().for_each(|(edge, graph_edge)| {
486        let mut _graph_node = GraphNode::default();
487        _graph_node.width = graph_edge.width.clone().unwrap_or(0.0);
488        _graph_node.height = graph_edge.height.clone().unwrap_or(0.0);
489        _graph_node.rank = rank.clone();
490        order_shift += 1;
491        _graph_node.order = Some(i + order_shift);
492        _graph_node.e = Some(edge.clone());
493        _graph_node.label = Some(graph_edge.clone());
494        util::add_dummy_node(graph, "selfedge".to_string(), _graph_node, "_se".to_string());
495      });
496    });
497  })
498}
499
500pub fn position_self_edges(g: &mut Graph<GraphConfig, GraphNode, GraphEdge>) {
501  for v in g.nodes() {
502    let node = g.node(&v).cloned().unwrap();
503    if node.dummy.unwrap_or("".to_string()) == "selfedge" {
504      let self_node = g.node(&node.e.as_ref().unwrap().v).unwrap();
505      let x = self_node.x + self_node.width / 2.0;
506      let y = self_node.y;
507      let dx = node.x - x;
508      let dy = self_node.height / 2.0;
509      let mut graph_edge = node.label.clone().unwrap();
510      graph_edge.points = Some(vec![
511        GraphEdgePoint { x: x + 2.0 * dx / 3.0, y: y - dy },
512        GraphEdgePoint { x: x + 2.0 * dx / 3.0, y: y - dy },
513        GraphEdgePoint { x: x + 5.0 * dx / 6.0, y: y - dy },
514        GraphEdgePoint { x: x +     dx        , y },
515        GraphEdgePoint { x: x + 5.0 * dx / 6.0, y: y + dy },
516        GraphEdgePoint { x: x + 2.0 * dx / 3.0, y: y + dy }
517      ]);
518      graph_edge.x = node.x;
519      graph_edge.y = node.y;
520      let _ = g.set_edge_with_obj(&node.e.unwrap(), Some(graph_edge));
521      g.remove_node(&v);
522    }
523  }
524}
525
526pub fn run_layout(graph: &mut Graph<GraphConfig, GraphNode, GraphEdge>) {
527  make_space_for_edge_labels(graph);
528  remove_self_edges(graph);
529  acyclic::run(graph);
530  nesting_graph::run(graph);
531  // calculating ranks
532  let mut nc_graph: Graph<GraphConfig, GraphNode, GraphEdge> = as_non_compound_graph(graph);
533  rank(&mut nc_graph);
534  transfer_node_edge_labels(&nc_graph, graph);
535  // done with calculating ranks
536  inject_edge_label_proxies(graph);
537  remove_empty_ranks(graph);
538  nesting_graph::cleanup(graph);
539  normalize_ranks(graph);
540  assign_rank_min_max(graph);
541  remove_edge_label_proxies(graph);
542  normalize::run(graph);
543  parent_dummy_chains(graph);
544  add_border_segments(graph);
545  order(graph);
546  insert_self_edges(graph);
547  coordinate_system::adjust(graph);
548  position::position(graph);
549  position_self_edges(graph);
550  remove_border_nodes(graph);
551  normalize::undo(graph);
552  fixup_edge_label_coords(graph);
553  coordinate_system::undo(graph);
554  translate_graph(graph);
555  assign_node_intersects(graph);
556  reverse_points_for_reversed_edges(graph);
557  acyclic::undo(graph);
558}