ere_core/visualization/
latex_graph.rs1pub fn escape_latex(text: String) -> String {
2 return text
3 .chars()
4 .map(|c| match c {
5 '\\' => r"{\textbackslash}".to_string(),
6 '&' => r"\&".to_string(),
7 '%' => r"\%".to_string(),
8 '$' => r"\$".to_string(),
9 '#' => r"\#".to_string(),
10 '_' => r"\_".to_string(),
11 '{' => r"\{".to_string(),
12 '}' => r"\}".to_string(),
13 '~' => r"{\textasciitilde}".to_string(),
14 '^' => r"{\textasciicircum}".to_string(),
15 c => c.to_string(),
16 })
17 .collect();
18}
19
20pub struct LatexGraphTransition {
21 pub(crate) to: usize,
22 pub(crate) label: String,
24}
25
26pub struct LatexGraphState {
27 pub(crate) label: String,
29 pub(crate) transitions: Vec<LatexGraphTransition>,
30 pub(crate) initial: bool,
31 pub(crate) accept: bool,
32}
33
34pub struct LatexGraph {
36 pub(crate) states: Vec<LatexGraphState>,
37}
38impl LatexGraph {
39 pub fn to_tikz(&self, include_doc: bool) -> String {
44 let mut text_parts: Vec<String> = Vec::new();
45 if include_doc {
46 text_parts.push(
47 "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{automata, positioning}\n\\begin{document}\n"
48 .into(),
49 );
50 }
51 text_parts.push("\\begin{tikzpicture}[node distance=2cm, auto]\n".into());
52
53 let mut transition_parts = Vec::new();
54
55 for (i, state) in self.states.iter().enumerate() {
56 let mut modifiers = String::new();
57 if state.initial {
58 modifiers += ", initial";
59 }
60 if state.accept {
61 modifiers += ", accepting"
62 }
63 if i == 0 {
64 text_parts.push(format!("\\node[state{modifiers}](q0){{$q_0$}};\n"));
65 } else {
66 text_parts.push(format!(
67 "\\node[state{modifiers}, right of=q{}](q{i}){{$q_{{{}}}$}};\n",
68 i - 1,
69 state.label
70 ));
71 }
72
73 for LatexGraphTransition { to, label } in &state.transitions {
74 let bend = match to.cmp(&i) {
75 std::cmp::Ordering::Less => "[bend left] ",
76 std::cmp::Ordering::Equal => "[loop below]",
77 std::cmp::Ordering::Greater => "[bend left] ",
78 };
79 transition_parts.push(format!(
80 "\\path[->] (q{i}) edge {bend} node {{{label}}} (q{to});\n",
81 ));
82 }
83 }
84 text_parts.extend_from_slice(&transition_parts);
85
86 text_parts.push("\\end{tikzpicture}\n".into());
87 if include_doc {
88 text_parts.push("\\end{document}\n".into());
89 }
90 return text_parts.into_iter().collect();
91 }
92}