1+ #pragma once
2+
3+ #include " interval_types.hpp"
4+
5+ #include < iostream>
6+ #include < string>
7+ #include < optional>
8+ #include < vector>
9+ #include < utility>
10+
11+ namespace lib_interval_tree
12+ {
13+ struct dot_graph_draw_settings
14+ {
15+ bool digraph = true ;
16+ std::string name = " G" ;
17+ std::vector<std::string> extra_node_attributes = {};
18+ std::vector<std::string> extra_statements = {};
19+ bool space_after_comma = false ;
20+ std::optional<char > left_brace = std::nullopt ;
21+ std::optional<char > right_brace = std::nullopt ;
22+ std::vector<std::string> edge_attributes = {};
23+ std::string indent = " \t " ;
24+ };
25+
26+ namespace detail
27+ {
28+ template <typename TreeT>
29+ class graph_painter
30+ {
31+ public:
32+ graph_painter (std::ostream& stream, TreeT const & tree, dot_graph_draw_settings settings)
33+ : stream_{stream}
34+ , tree_{tree}
35+ , settings_{std::move (settings)}
36+ , node_id_{" a" }
37+ , left_brace_{}
38+ , right_brace_{}
39+ {
40+ using ival_type = typename TreeT::interval_type;
41+
42+ const auto determine_brace = []() {
43+ if (std::is_same_v<typename ival_type::interval_kind, closed>)
44+ return " []" ;
45+ else if (std::is_same_v<typename ival_type::interval_kind, left_open>)
46+ return " (]" ;
47+ else if (std::is_same_v<typename ival_type::interval_kind, right_open>)
48+ return " [)" ;
49+ else if (std::is_same_v<typename ival_type::interval_kind, open>)
50+ return " ()" ;
51+ else if (std::is_same_v<typename ival_type::interval_kind, closed_adjacent>)
52+ return " []" ;
53+ else
54+ return " []" ;
55+ };
56+
57+ if (settings_.left_brace )
58+ left_brace_ = *settings_.left_brace ;
59+ else
60+ left_brace_ = determine_brace ()[0 ];
61+
62+ if (settings_.right_brace )
63+ right_brace_ = *settings_.right_brace ;
64+ else
65+ right_brace_ = determine_brace ()[1 ];
66+ }
67+
68+ void make_header ()
69+ {
70+ stream_ << (settings_.digraph ? " digraph" : " graph" ) << " " << settings_.name << " {\n " ;
71+ for (auto const & statement : settings_.extra_statements )
72+ {
73+ stream_ << settings_.indent << statement << " ;\n " ;
74+ }
75+ }
76+
77+ template <typename T>
78+ void make_label (T const & ival)
79+ {
80+ #if __cplusplus >= 201703L
81+ if constexpr (std::is_same_v<typename T::interval_kind, dynamic>)
82+ {
83+ stream_ << (ival.left_border () == interval_border::open ? ' (' : ' [' ) << ival.low ()
84+ << (settings_.space_after_comma ? " , " : " ," ) << ival.high ()
85+ << (ival.right_border () == interval_border::open ? ' )' : ' ]' );
86+ }
87+ else
88+ {
89+ stream_ << left_brace_ << ival.low () << (settings_.space_after_comma ? " , " : " ," ) << ival.high ()
90+ << right_brace_;
91+ }
92+ #else
93+ stream_ << left_brace_ << ival.low () << (settings_.space_after_comma ? " , " : " ," ) << ival.high ()
94+ << right_brace_;
95+ #endif
96+ }
97+
98+ template <typename interval_type>
99+ void specify_node (interval_type const & ival)
100+ {
101+ stream_ << settings_.indent << node_id_ << " [label=\" " ;
102+ increment_node_id ();
103+ make_label (ival);
104+ stream_ << " \" " ;
105+ if (!settings_.extra_node_attributes .empty ())
106+ {
107+ for (auto const & attr : settings_.extra_node_attributes )
108+ {
109+ stream_ << " , " << attr;
110+ }
111+ }
112+ stream_ << " ];\n " ;
113+ }
114+
115+ template <typename iterator_type>
116+ void specify_all_nodes (iterator_type const & node)
117+ {
118+ specify_node (*node);
119+ if (node.left () != tree_.end ())
120+ specify_all_nodes (node.left ());
121+ if (node.right () != tree_.end ())
122+ specify_all_nodes (node.right ());
123+ }
124+
125+ void specify_edge (std::string const & from, std::string const & to)
126+ {
127+ stream_ << settings_.indent << from << (settings_.digraph ? " -> " : " -- " ) << to;
128+ if (!settings_.edge_attributes .empty ())
129+ {
130+ stream_ << " [" ;
131+ for (auto iter = settings_.edge_attributes .begin (); iter != settings_.edge_attributes .end (); ++iter)
132+ {
133+ stream_ << *iter;
134+ if (iter + 1 != settings_.edge_attributes .end ())
135+ stream_ << " , " ;
136+ }
137+ stream_ << " ]" ;
138+ }
139+ stream_ << " ;\n " ;
140+ }
141+
142+ template <typename iterator_type>
143+ void specify_all_edges (iterator_type const & node)
144+ {
145+ auto previous_id = node_id_;
146+ if (node.left () != tree_.end ())
147+ {
148+ increment_node_id ();
149+ specify_edge (previous_id, node_id_);
150+ specify_all_edges (node.left ());
151+ }
152+ if (node.right () != tree_.end ())
153+ {
154+ increment_node_id ();
155+ specify_edge (previous_id, node_id_);
156+ specify_all_edges (node.right ());
157+ }
158+ }
159+
160+ void close ()
161+ {
162+ stream_ << " }" ;
163+ }
164+
165+ void reset_node_id ()
166+ {
167+ node_id_ = " a" ;
168+ }
169+
170+ private:
171+ void increment_node_id ()
172+ {
173+ const auto character = node_id_.begin ();
174+ for (auto iter = character; iter != node_id_.end (); ++iter)
175+ {
176+ if (*iter == ' z' )
177+ {
178+ *iter = ' a' ;
179+ if (iter + 1 == node_id_.end ())
180+ {
181+ node_id_ += ' a' ;
182+ break ;
183+ }
184+ }
185+ else
186+ {
187+ ++*iter;
188+ break ;
189+ }
190+ }
191+ }
192+
193+ private:
194+ std::ostream& stream_;
195+ TreeT const & tree_;
196+ dot_graph_draw_settings settings_;
197+ std::string node_id_;
198+ char left_brace_;
199+ char right_brace_;
200+ };
201+ }
202+
203+ template <typename TreeT>
204+ void draw_dot_graph (std::ostream& stream, TreeT const & tree, dot_graph_draw_settings const & settings = {})
205+ {
206+ detail::graph_painter painter{stream, tree, settings};
207+ painter.make_header ();
208+ if (tree.empty ())
209+ {
210+ painter.close ();
211+ return ;
212+ }
213+ painter.specify_all_nodes (tree.root ());
214+ painter.reset_node_id ();
215+ painter.specify_all_edges (tree.root ());
216+ painter.close ();
217+ }
218+ }
0 commit comments