Skip to content

Commit 4ef8728

Browse files
authored
Merge pull request #45 from 5cript/feat/dotfile
Feat/dotfile
2 parents 9d2e062 + ef19698 commit 4ef8728

File tree

10 files changed

+593
-6
lines changed

10 files changed

+593
-6
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

README.md

+71-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ int main()
4242
tree.insert({19, 20});
4343

4444
tree.deoverlap();
45-
45+
4646
for (auto const& i : tree)
4747
{
4848
std::cout << "[" << i.low() << ", " << i.high() << "]\n";
@@ -61,10 +61,80 @@ Create a build folder, navigate there, run cmake and build the tree-tests target
6161
You might have to adapt the linker line for gtest, if you built it yourself and didn't install it into your system.
6262
If you want to generate the pretty drawings, install cairo, pull the submodule and pass INT_TREE_DRAW_EXAMPLES=on to the cmake command line to generate a drawings/make_drawings executeable.
6363

64+
## Draw Dot Graph
65+
This draws a dot graph of the tree:
66+
```c++
67+
#include <interval-tree/interval_tree.hpp>
68+
#include <interval-tree/dot_graph.hpp>
69+
70+
int main()
71+
{
72+
using namespace lib_interval_tree;
73+
interval_tree_t<int> tree;
74+
75+
tree.insert(make_safe_interval<int>(21, 16)); // make_safe_interval swaps low and high if not in right order.
76+
tree.insert({8, 9});
77+
tree.insert({25, 30});
78+
tree.insert({5, 8});
79+
tree.insert({15, 23});
80+
tree.insert({17, 19});
81+
tree.insert({26, 26});
82+
tree.insert({0, 3});
83+
tree.insert({6, 10});
84+
tree.insert({19, 20});
85+
86+
draw_dot_graph(
87+
std::cout,
88+
tree,
89+
{
90+
// digraph or graph?
91+
.digraph = true,
92+
93+
// graph name
94+
.name = "G",
95+
96+
// extra node attributes
97+
.extra_node_attributes = {"color=red"},
98+
99+
// extra graph statements
100+
.extra_statements = {"rankdir=LR"},
101+
102+
// put space after comma of interval label? (a,b) vs (a, b)
103+
.space_after_comma = false,
104+
105+
// left brace override enabled if not 0, otherwise determined from interval kind
106+
.left_brace = '\0',
107+
108+
// right brace override enabled if not 0, otherwise determined from interval kind
109+
.right_brace = '\0',
110+
111+
// edge attributes
112+
.edge_attributes = {"color=blue"},
113+
114+
// indent characters
115+
.indent = "\t",
116+
}
117+
);
118+
}
119+
```
120+
64121
## Free Functions
65122
### interval<NumericT, Kind> make_safe_interval(NumericT border1, NumericT border2)
66123
Creates an interval where the borders are sorted so the lower border is the first one.
67124

125+
### draw_dot_graph(std::ostream& os, interval_tree_t<Interval> const& tree, DrawOptions const& options)
126+
Draws a dot graph of the interval tree to the output stream.
127+
Options are:
128+
- digraph: bool
129+
- name: std::string
130+
- extra_node_attributes: std::vector<std::string>
131+
- extra_statements: std::vector<std::string>
132+
- space_after_comma: bool
133+
- left_brace: std::optional<std::string>
134+
- right_brace: std::optional<std::string>
135+
- edge_attributes: std::vector<std::string>
136+
- indent: std::string
137+
68138
## Members of IntervalTree<Interval>
69139

70140
- [Members of IntervalTree<Interval>](#members-of-intervaltreeinterval)

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

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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(
21+
std::cout,
22+
tree,
23+
{
24+
// digraph or graph?
25+
.digraph = true,
26+
27+
// graph name
28+
.name = "G",
29+
30+
// extra node attributes
31+
.extra_node_attributes = {"color=red"},
32+
33+
// extra graph statements
34+
.extra_statements = {"rankdir=LR"},
35+
36+
// put space after comma of interval label? (a,b) vs (a, b)
37+
.space_after_comma = false,
38+
39+
// left brace override, otherwise determined from interval kind
40+
.left_brace = '\0',
41+
42+
// right brace override, otherwise determined from interval kind
43+
.right_brace = '\0',
44+
45+
// edge attributes
46+
.edge_attributes = {"color=blue"},
47+
48+
// indent characters
49+
.indent = "\t",
50+
}
51+
);
52+
}

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

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

0 commit comments

Comments
 (0)