Skip to content

Commit cb30294

Browse files
committed
Added draw_dot_graph function.
1 parent 9d2e062 commit cb30294

File tree

9 files changed

+492
-5
lines changed

9 files changed

+492
-5
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ __history/
88
*.cbp
99
*.png
1010
.vscode
11+
.clangd
1112
build
1213

1314
tests/bin

example/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
add_subdirectory(from_readme)
1+
add_subdirectory(from_readme)
2+
add_subdirectory(dot_graph)

example/dot_graph/CMakeLists.txt

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
add_executable(dotgraph main.cpp)
2+
target_link_libraries(dotgraph interval-tree)
3+
4+
set_target_properties(dotgraph
5+
PROPERTIES
6+
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/interval_tree/examples"
7+
)

example/dot_graph/main.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <interval-tree/dot_graph.hpp>
2+
#include <interval-tree/interval_tree.hpp>
3+
4+
int main()
5+
{
6+
using namespace lib_interval_tree;
7+
interval_tree_t<int> tree;
8+
9+
tree.insert(make_safe_interval<int>(21, 16)); // make_safe_interval swaps low and high if not in right order.
10+
tree.insert({8, 9});
11+
tree.insert({25, 30});
12+
tree.insert({5, 8});
13+
tree.insert({15, 23});
14+
tree.insert({17, 19});
15+
tree.insert({26, 26});
16+
tree.insert({0, 3});
17+
tree.insert({6, 10});
18+
tree.insert({19, 20});
19+
20+
draw_dot_graph(std::cout, tree);
21+
}

example/from_readme/main.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// #include <interval-tree/draw.hpp> // to draw tree. this is not header only anymore.
22
#include <interval-tree/interval_tree.hpp>
33

4+
#include <iostream>
5+
46
int main()
57
{
68
using namespace lib_interval_tree;

include/interval-tree/dot_graph.hpp

+218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
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+
}

include/interval-tree/interval_tree.hpp

-4
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,10 @@
66
#include "feature_test.hpp"
77

88
#include <string>
9-
#include <memory>
10-
#include <cstdio>
119
#include <stdexcept>
1210
#include <iterator>
1311
#include <type_traits>
1412

15-
#include <iostream>
16-
1713
namespace lib_interval_tree
1814
{
1915
// ############################################################################################################

0 commit comments

Comments
 (0)