|
1 | | -use std::{fmt, io::Write}; |
| 1 | +use std::{collections::HashMap, fmt, io::Write}; |
2 | 2 |
|
3 | 3 | use crate::EGraph; |
4 | 4 | use graphviz_rust::{ |
@@ -39,16 +39,23 @@ impl EGraph { |
39 | 39 | // and create mapping from each node ID to its class |
40 | 40 | let mut node_to_class = std::collections::HashMap::new(); |
41 | 41 | for (node_id, node) in &self.nodes { |
42 | | - let typ = self |
43 | | - .class_data |
44 | | - .get(&node.eclass) |
45 | | - .and_then(|data| data.typ.clone()); |
| 42 | + let class_data = self.class_data.get(&node.eclass); |
| 43 | + let typ = class_data.and_then(|data| data.typ.clone()); |
| 44 | + // extra if is none empty |
| 45 | + let extra = class_data.and_then(|data| { |
| 46 | + if data.extra.is_empty() { |
| 47 | + None |
| 48 | + } else { |
| 49 | + Some(data.extra.clone()) |
| 50 | + } |
| 51 | + }); |
46 | 52 | node_to_class.insert(node_id.clone(), node.eclass.clone()); |
47 | 53 | class_nodes |
48 | 54 | .entry(typ) |
49 | 55 | .or_insert_with(std::collections::HashMap::new) |
50 | 56 | .entry(node.eclass.clone()) |
51 | | - .or_insert_with(Vec::new) |
| 57 | + .or_insert_with(|| (extra, Vec::new())) |
| 58 | + .1 |
52 | 59 | .push((node_id.clone(), node)); |
53 | 60 | } |
54 | 61 | // 2. Start with configuration |
@@ -84,7 +91,7 @@ impl EGraph { |
84 | 91 | let next_color = (typ_colors.len() + INITIAL_COLOR) % N_COLORS; |
85 | 92 | let color = typ_colors.entry(typ).or_insert(next_color); |
86 | 93 | stmts.push(stmt!(attr!("fillcolor", color))); |
87 | | - for (class_id, nodes) in class_to_node { |
| 94 | + for (class_id, (extra, nodes)) in class_to_node { |
88 | 95 | let mut inner_stmts = vec![]; |
89 | 96 |
|
90 | 97 | // Add nodes |
@@ -113,14 +120,17 @@ impl EGraph { |
113 | 120 | let outer_subgraph_id = quote(&format!("outer_{subgraph_id}")); |
114 | 121 | let quoted_subgraph_id = quote(&subgraph_id); |
115 | 122 |
|
| 123 | + let mut inner_subgraph = subgraph!(quoted_subgraph_id; subgraph!("", inner_stmts)); |
| 124 | + if let Some(extra) = extra { |
| 125 | + inner_subgraph |
| 126 | + .add_stmt(stmt!(SubgraphAttributes::label(class_html_label(extra)))); |
| 127 | + } |
| 128 | + // Nest in empty sub-graph so that we can use rank=same |
| 129 | + // https://stackoverflow.com/a/55562026/907060 |
116 | 130 | let subgraph = subgraph!(outer_subgraph_id; |
117 | 131 | // Disable label for now, to reduce size |
118 | 132 | // NodeAttributes::label(subgraph_html_label(&typ)), |
119 | | - |
120 | | - // Nest in empty sub-graph so that we can use rank=same |
121 | | - // https://stackoverflow.com/a/55562026/907060 |
122 | | - subgraph!(quoted_subgraph_id; subgraph!("", inner_stmts)), |
123 | | - |
| 133 | + inner_subgraph, |
124 | 134 | // Make outer subgraph a cluster but make it invisible, so just used for padding |
125 | 135 | // https://forum.graphviz.org/t/how-to-add-space-between-clusters/1209/3 |
126 | 136 | SubgraphAttributes::style(quote("invis")), |
@@ -166,6 +176,19 @@ fn html_label(label: &str, n_args: usize) -> String { |
166 | 176 | ) |
167 | 177 | } |
168 | 178 |
|
| 179 | +fn class_html_label(extra: HashMap<String, String>) -> String { |
| 180 | + let mut rows = Vec::new(); |
| 181 | + for (key, value) in extra { |
| 182 | + rows.push(format!( |
| 183 | + "<TR><TD ALIGN=\"RIGHT\">{key}</TD><TD ALIGN=\"LEFT\">{value}</TD></TR>" |
| 184 | + )); |
| 185 | + } |
| 186 | + format!( |
| 187 | + "<<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"2\">{}</TABLE>>", |
| 188 | + rows.join("") |
| 189 | + ) |
| 190 | +} |
| 191 | + |
169 | 192 | /// Adds double quotes and escapes the quotes in the string |
170 | 193 | fn quote(s: &str) -> String { |
171 | 194 | format!("{s:?}") |
|
0 commit comments