Vcsn  2.0
Be Rational
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
dot.hh
Go to the documentation of this file.
1 #ifndef VCSN_ALGOS_DOT_HH
2 # define VCSN_ALGOS_DOT_HH
3 
4 # include <algorithm>
5 # include <iostream>
6 # include <sstream>
7 
8 # include <vcsn/dyn/fwd.hh>
9 # include <vcsn/dyn/automaton.hh>
10 # include <vcsn/algos/grail.hh>
11 # include <vcsn/algos/accessible.hh> // useful_states
12 # include <vcsn/misc/iostream.hh>
13 # include <vcsn/misc/set.hh>
15 
16 namespace vcsn
17 {
18 
19  namespace detail
20  {
21  /*-------------------------.
22  | dot(automaton, stream). |
23  `-------------------------*/
24 
28  template <typename Aut>
29  class dotter: public outputter<Aut>
30  {
31  private:
33  using typename super_t::automaton_t;
34  using typename super_t::state_t;
35  using typename super_t::transition_t;
36  using typename super_t::weightset_t;
37  using typename super_t::weight_t;
38 
39  using super_t::aut_;
40  using super_t::finals_;
42  using super_t::initials_;
43  using super_t::os_;
44  using super_t::ws_;
45 
46  using super_t::super_t;
47 
48  // Dot, by default, uses the X11 color naming scheme, whose "gray"
49  // is really light (it looks almost blue in some cases).
50  const char* gray = "color = DimGray";
51 
52  public:
53  dotter(const automaton_t& aut, std::ostream& out,
54  bool dot2tex = false)
55  : super_t(aut, out)
56  , dot2tex_(dot2tex)
57  {
59  bos_.push(out);
60  }
61 
63  std::ostream& operator()()
64  {
66  print_states_();
69  return os_;
70  }
71 
72  private:
75  {
76  bos_ <<
77  "digraph\n"
78  "{\n"
79  " vcsn_context = \"" << aut_->context().vname() << "\"\n"
80  " rankdir = LR\n"
81  " edge ["
82  << (dot2tex_
83  ? "texmode = math, lblstyle = auto"
84  : "arrowhead = vee, arrowsize = .6")
85  << "]\n";
86 
87  if (dot2tex_)
88  bos_ <<
89  " d2toptions = \"--format tikz --tikzedgelabels"
90  " --graphstyle=automaton --crop --nominsize --autosize\"\n"
91  " d2tdocpreamble = \""
92  " \\usepackage{amssymb}"
93  " \\usetikzlibrary{arrows.meta, automata, bending}"
94  " \\tikzstyle{automaton}=[shorten >=1pt, pos=.4,"
95  " >={Stealth[bend,round]}, initial text=]"
96  " \\tikzstyle{named}=[rectangle, rounded corners]"
97  " \\tikzstyle{initial}=[initial by arrow]"
98  " \\tikzstyle{accepting}=[accepting by arrow]"
99  " \"\n";
100  }
101 
104  {
105  bos_ << '}';
106  }
107 
112  bool format_(const std::string& sep,
113  const std::string& kind, const weight_t& w)
114  {
115  if (ws_.is_zero(w))
116  return false;
117  else
118  {
119  bos_ << sep << kind;
120  if (ws_.show_one() || !ws_.is_one(w))
121  {
122  bos_ << ", " << kind << " text={";
123  ws_.print(w, bos_) << '}';
124  }
125  return true;
126  }
127  }
128 
130  void
132  {
133  aut_->print_state(s, bos_);
134  bool has_attributes = false;
135  if (dot2tex_)
136  {
137  has_attributes = true;
138  bos_ << " [";
139  std::string style;
140  std::string sep;
141  std::string close;
142  // I hate this piece of code. There must be means to be
143  // better looking...
144  if (aut_->state_has_name(s))
145  {
146  bos_ << "label = \"";
147  enable_();
148  aut_->print_state_name(s, bos_, "latex");
149  disable_();
150  static bool debug = getenv("VCSN_DEBUG");
151  if (debug)
152  bos_ << " (" << s << ')';
153  bos_ << "\", style = \"named";
154  sep = ", ";
155  close = "\"";
156  }
157  else
158  sep = "style = \"state, ";
159  if (format_(sep, "initial", aut_->get_initial_weight(s)))
160  {
161  sep = ", ";
162  close = "\"";
163  }
164  if (format_(sep, "accepting", aut_->get_final_weight(s)))
165  close = "\"";
166  bos_ << close;
167  }
168  else
169  {
170  // Dot format.
171  if (aut_->state_has_name(s))
172  {
173  has_attributes = true;
174  bos_ << " [label = \"";
175  enable_();
176  aut_->print_state_name(s, bos_, "text");
177  disable_();
178  static bool debug = getenv("VCSN_DEBUG");
179  if (debug)
180  bos_ << " (" << s << ')';
181  bos_ << "\", shape = box";
182  }
183  }
184  if (!has(useful_, s))
185  {
186  if (has_attributes)
187  bos_ << ", ";
188  else
189  {
190  bos_ << " [";
191  has_attributes = true;
192  }
193  bos_ << gray;
194  }
195  if (has_attributes)
196  bos_ << ']';
197  }
198 
201  {
202  if (!dot2tex_)
203  {
204  // Output the pre-initial and post-final states.
205  if (!aut_->initial_transitions().empty()
206  || !aut_->final_transitions().empty())
207  {
208  bos_ <<
209  " {\n"
210  " node [shape = point, width = 0]\n";
211  for (auto s : initials_())
212  {
213  bos_ << " I";
214  aut_->print_state(s, bos_);
215  bos_ << '\n';
216  }
217  for (auto s : finals_())
218  {
219  bos_ << " F";
220  aut_->print_state(s, bos_);
221  bos_ << '\n';
222  }
223  bos_ << " }\n";
224  }
225  }
226 
227  // Output all the states to make "print | read" idempotent.
228  //
229  // Put the useless ones in gray. This does not work:
230  //
231  // { 0 1 2 }
232  // { node [color = gray] 2 }
233  //
234  // because 2 was already "declared", and dot does not associate
235  // "color = gray" to it.
236  //
237  // Set the width to something nicer than the default and shape
238  // to rounded. Useless for circle, but useful for shape =
239  // box, and simpler to set it once for all.
240  if (!aut_->states().empty())
241  {
242  bos_ << " {\n"
243  << " node ["
244  << (dot2tex_
245  ? "texmode = math, style = state"
246  : "shape = circle, style = rounded, width = 0.5")
247  << "]\n";
248  for (auto s : aut_->states())
249  {
250  bos_ << " ";
251  print_state_(s);
252  bos_ << '\n';
253  }
254  bos_ << " }\n";
255  }
256  }
257 
260  {
261  for (auto src : dot2tex_ ? aut_->states() : aut_->all_states())
262  {
263  // Sort by destination state.
264  std::set<state_t> ds;
265  if (dot2tex_)
266  for (auto t: aut_->out(src))
267  ds.insert(aut_->dst_of(t));
268  else
269  for (auto t: aut_->all_out(src))
270  ds.insert(aut_->dst_of(t));
271  for (auto dst: ds)
272  {
273  bos_ << " ";
274  if (src == aut_->pre())
275  {
276  bos_ << 'I';
277  aut_->print_state(dst, bos_);
278  bos_ << " -> ";
279  aut_->print_state(dst, bos_);
280  }
281  else if (dst == aut_->post())
282  {
283  aut_->print_state(src, bos_);
284  bos_ << " -> ";
285  bos_ << 'F';
286  aut_->print_state(src, bos_);
287  }
288  else
289  {
290  aut_->print_state(src, bos_);
291  bos_ << " -> ";
292  aut_->print_state(dst, bos_);
293  }
294 
295  std::ostringstream o;
296  print_entry_(src, dst, o,
297  dot2tex_ ? "latex" : "text");
298  bool useless = !has(useful_, src) || !has(useful_, dst);
299  if (!o.str().empty() || useless)
300  {
301  bos_ << " [";
302  const char* sep = "";
303  if (!o.str().empty())
304  {
305  enable_();
306  bos_ << "label = \"" << o.str() << "\"";
307  disable_();
308  sep = ", ";
309  }
310  if (useless)
311  bos_ << sep << gray;
312  bos_ << ']';
313  }
314  bos_ << '\n';
315  }
316  }
317  }
318 
320  void enable_()
321  {
322  boost::iostreams::flush(bos_);
323  bos_.component<detail::backslashify_output_filter>(0)->enable();
324  }
325 
327  void disable_()
328  {
329  boost::iostreams::flush(bos_);
330  bos_.component<detail::backslashify_output_filter>(0)->disable();
331  }
332 
334  bool dot2tex_ = false;
336  std::unordered_set<state_t_of<Aut>> useful_ = useful_states(aut_);
338  detail::io::filtering_ostream bos_;
339  };
340  }
341 
342  template <typename Aut>
343  std::ostream&
344  dot(const Aut& aut, std::ostream& out, bool dot2tex = false)
345  {
346  detail::dotter<Aut> dot(aut, out, dot2tex);
347  return dot();
348  }
349 
350  namespace dyn
351  {
352  namespace detail
353  {
355  template <typename Aut, typename Ostream, typename Bool>
356  std::ostream& dot(const automaton& aut, std::ostream& out,
357  bool dot2tex)
358  {
359  return dot(aut->as<Aut>(), out, dot2tex);
360  }
361 
363  (const automaton& aut, std::ostream& out,
364  bool dot2tex) -> std::ostream&);
365  }
366  }
367 }
368 
369 #endif // !VCSN_ALGOS_DOT_HH
void disable_()
Disable the escaping of backslashes.
Definition: dot.hh:327
bool format_(const std::string &sep, const std::string &kind, const weight_t &w)
Format a TikZ attribute.
Definition: dot.hh:112
REGISTER_DECLARE(accessible,(const automaton &) -> automaton)
std::shared_ptr< detail::automaton_base > automaton
Definition: automaton.hh:71
std::ostream & print_entry_(state_t src, state_t dst, std::ostream &os, const std::string &fmt="text")
The labels and weights of transitions from src to dst.
Definition: grail.hh:73
states_t< Aut > useful_states(const Aut &a)
Definition: accessible.hh:69
void enable_()
Enable the escaping of backslashes.
Definition: dot.hh:320
const weightset_t & ws_
Short-hand to the weightset.
Definition: grail.hh:147
states_t initials_()
The list of initial states, sorted.
Definition: grail.hh:121
weight_t_of< automaton_t > weight_t
Definition: grail.hh:47
std::ostream & dot(const automaton &aut, std::ostream &out, bool dot2tex)
Bridge.
Definition: dot.hh:356
std::unordered_set< state_t_of< Aut > > useful_
Useful states.
Definition: dot.hh:336
std::ostream & os_
Output stream.
Definition: grail.hh:143
transition_t_of< automaton_t > transition_t
Definition: grail.hh:45
Factor common bits in automaton formatting.
Definition: grail.hh:27
dotter(const automaton_t &aut, std::ostream &out, bool dot2tex=false)
Definition: dot.hh:53
bool dot2tex_
Whether to format for dot2tex.
Definition: dot.hh:334
std::ostream & operator()()
Print the automaton on the stream.
Definition: dot.hh:63
void print_epilogue_()
Finish the dot graph.
Definition: dot.hh:103
void print_prologue_()
Start the dot graph.
Definition: dot.hh:74
void print_state_(state_t s)
Pretty-print state s for both dot and dot2tex.
Definition: dot.hh:131
weightset_t_of< automaton_t > weightset_t
Definition: grail.hh:46
states_t finals_()
The list of final states, sorted.
Definition: grail.hh:131
Format an automaton into Dot.
Definition: dot.hh:29
const char * gray
Definition: dot.hh:50
const automaton_t & aut_
The automaton we have to output.
Definition: grail.hh:141
state_t_of< automaton_t > state_t
Definition: grail.hh:40
detail::io::filtering_ostream bos_
The output stream, with a backslashify filter.
Definition: dot.hh:338
std::ostream & dot(const Aut &aut, std::ostream &out, bool dot2tex=false)
Definition: dot.hh:344
bool has(const std::map< Key, Value, Compare, Alloc > &s, const Key &e)
Definition: map.hh:35
void print_transitions_()
Print all the transitions.
Definition: dot.hh:259
void print_states_()
Print the states.
Definition: dot.hh:200