algograph/algorithm/graphviz.rs
1//! Visualize tagged graphs in the graphviz format.
2use crate::{graph::*, tagged::Edge as _Edge};
3use ahash::RandomState;
4use std::collections::HashMap;
5
6/**
7 * Provides graphviz labels for vertices.
8 *
9 * See [DumpInGraphviz] for details.
10 */
11pub trait GraphvizLabelForVertex {
12 /**
13 * Returns a string for graphviz name and an optional label.
14 */
15 fn label(&self) -> (String, Option<String>);
16}
17
18/**
19 * Provides graphviz labels for edges.
20 *
21 * See [DumpInGraphviz] for details.
22 */
23pub trait GraphvizLabelForEdge: crate::tagged::Edge {
24 /**
25 * Returns an optional label.
26 */
27 fn label(&self) -> Option<String>;
28}
29
30/**
31 * Dumps a directed/undirected graph into graphviz format.
32 *
33 * # Examples
34 *
35 * ```rust
36 * use algograph::{
37 * algorithm::graphviz::*,
38 * graph::*,
39 * tagged::*,
40 * };
41 *
42 * #[derive(Debug, Clone, Hash, PartialEq, Eq)]
43 * enum Shape {
44 * Default,
45 * Rectangle,
46 * }
47 *
48 * #[derive(Debug, Clone, Hash, PartialEq, Eq)]
49 * struct ShapedVertex {
50 * key: usize,
51 * shape: Shape,
52 * }
53 *
54 * impl GraphvizLabelForVertex for ShapedVertex {
55 * fn label(&self) -> (String, Option<String>) {
56 * let name = format!("{}", self.key);
57 * let label = match self.shape {
58 * Shape::Rectangle => Some("shape=rectangle".to_owned()),
59 * _ => None,
60 * };
61 * (name, label)
62 * }
63 * }
64 *
65 * #[derive(Debug, Clone, Hash, PartialEq, Eq)]
66 * enum Color {
67 * Default,
68 * Red,
69 * }
70 *
71 * #[derive(Debug, Clone, Hash, PartialEq, Eq)]
72 * struct ColoredEdge {
73 * src: VertexId,
74 * snk: VertexId,
75 * color: Color,
76 * }
77 *
78 * impl algograph::tagged::Edge for ColoredEdge {
79 * fn source(&self) -> VertexId {
80 * self.src
81 * }
82 * fn sink(&self) -> VertexId {
83 * self.snk
84 * }
85 * }
86 *
87 * impl GraphvizLabelForEdge for ColoredEdge {
88 * fn label(&self) -> Option<String> {
89 * match self.color {
90 * Color::Red => Some("color=red".to_owned()),
91 * _ => None,
92 * }
93 * }
94 * }
95 *
96 * // for a directed graph
97 * let mut dg = NaiveTaggedGraph::<ShapedVertex, ColoredEdge>::new();
98 * let v0 = dg.overwrite_vertex(ShapedVertex {
99 * key: 0,
100 * shape: Shape::Default,
101 * });
102 * let v1 = dg.overwrite_vertex(ShapedVertex {
103 * key: 1,
104 * shape: Shape::Rectangle,
105 * });
106 * let _ = dg.add_edge(ColoredEdge {
107 * src: v0,
108 * snk: v1,
109 * color: Color::Red,
110 * });
111 * let _ = dg.add_edge(ColoredEdge {
112 * src: v0,
113 * snk: v0,
114 * color: Color::Default,
115 * });
116 * let trial = {
117 * let mut buf = vec![];
118 * dg.dump_in_graphviz(&mut buf, "trial").unwrap();
119 * String::from_utf8(buf).unwrap()
120 * };
121 * assert_eq!(
122 * trial,
123 * r#"digraph trial {
124 * 0 ;
125 * 1 [shape=rectangle] ;
126 * 0 -> 1 [color=red] ;
127 * 0 -> 0 ;
128 * }
129 * "#);
130 *
131 * // for an undirected graph
132 * let mut udg =
133 * NaiveTaggedGraph::<ShapedVertex, ColoredEdge, undirected::TreeBackedGraph>::new();
134 * let v0 = udg.overwrite_vertex(ShapedVertex {
135 * key: 0,
136 * shape: Shape::Default,
137 * });
138 * let v1 = udg.overwrite_vertex(ShapedVertex {
139 * key: 1,
140 * shape: Shape::Rectangle,
141 * });
142 * let _ = udg.add_edge(ColoredEdge {
143 * src: v0,
144 * snk: v1,
145 * color: Color::Red,
146 * });
147 * let _ = udg.add_edge(ColoredEdge {
148 * src: v0,
149 * snk: v0,
150 * color: Color::Default,
151 * });
152 * let trial = {
153 * let mut buf = vec![];
154 * udg.dump_in_graphviz(&mut buf, "trial").unwrap();
155 * String::from_utf8(buf).unwrap()
156 * };
157 * println!("{}", trial);
158 * assert_eq!(
159 * trial,
160 * r#"graph trial {
161 * 0 ;
162 * 1 [shape=rectangle] ;
163 * 0 -- 1 [color=red] ;
164 * 0 -- 0 ;
165 * }
166 * "#
167 * );
168 * ```
169 */
170pub trait DumpInGraphviz
171where
172 Self: crate::tagged::QueryableTaggedGraph + DirectedOrNot,
173 Self::LowerGraph: QueryableGraph,
174 Self::Vertex: GraphvizLabelForVertex,
175 Self::Edge: GraphvizLabelForEdge,
176{
177 /**
178 * Dumps a directed/undirected graph to a `std::io::Write` object in the graphviz format.
179 *
180 */
181 fn dump_in_graphviz<W>(&self, out: &mut W, graph_name: &str) -> std::io::Result<()>
182 where
183 W: std::io::Write,
184 {
185 if Self::DIRECTED_OR_NOT {
186 writeln!(out, "digraph {} {{", graph_name)?;
187 } else {
188 writeln!(out, "graph {} {{", graph_name)?;
189 }
190 let mut vkey = HashMap::with_hasher(RandomState::new());
191 for (vid, vert) in self.iter_vertices() {
192 let (key, label) = vert.label();
193 if let Some(label) = label {
194 writeln!(out, " {} [{}] ;", key, label)?;
195 } else {
196 writeln!(out, " {} ;", key)?;
197 }
198 vkey.insert(vid, key);
199 }
200 let dir = if Self::DIRECTED_OR_NOT { "->" } else { "--" };
201 for (_, e) in self.iter_edges() {
202 let src = vkey.get(&e.source()).unwrap();
203 let snk = vkey.get(&e.sink()).unwrap();
204 if let Some(label) = e.label() {
205 writeln!(out, " {} {} {} [{}] ;", src, dir, snk, label)?;
206 } else {
207 writeln!(out, " {} {} {} ;", src, dir, snk)?;
208 }
209 }
210 writeln!(out, "}}")?;
211 Ok(())
212 }
213}
214
215impl<G> DumpInGraphviz for G
216where
217 G: crate::tagged::QueryableTaggedGraph + DirectedOrNot,
218 G::LowerGraph: QueryableGraph,
219 G::Vertex: GraphvizLabelForVertex,
220 G::Edge: GraphvizLabelForEdge,
221{
222}