Skip to content

Feat/dotfile #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ __history/
*.cbp
*.png
.vscode
.clangd
build

tests/bin
Expand Down
72 changes: 71 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ int main()
tree.insert({19, 20});

tree.deoverlap();

for (auto const& i : tree)
{
std::cout << "[" << i.low() << ", " << i.high() << "]\n";
Expand All @@ -61,10 +61,80 @@ Create a build folder, navigate there, run cmake and build the tree-tests target
You might have to adapt the linker line for gtest, if you built it yourself and didn't install it into your system.
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.

## Draw Dot Graph
This draws a dot graph of the tree:
```c++
#include <interval-tree/interval_tree.hpp>
#include <interval-tree/dot_graph.hpp>

int main()
{
using namespace lib_interval_tree;
interval_tree_t<int> tree;

tree.insert(make_safe_interval<int>(21, 16)); // make_safe_interval swaps low and high if not in right order.
tree.insert({8, 9});
tree.insert({25, 30});
tree.insert({5, 8});
tree.insert({15, 23});
tree.insert({17, 19});
tree.insert({26, 26});
tree.insert({0, 3});
tree.insert({6, 10});
tree.insert({19, 20});

draw_dot_graph(
std::cout,
tree,
{
// digraph or graph?
.digraph = true,

// graph name
.name = "G",

// extra node attributes
.extra_node_attributes = {"color=red"},

// extra graph statements
.extra_statements = {"rankdir=LR"},

// put space after comma of interval label? (a,b) vs (a, b)
.space_after_comma = false,

// left brace override enabled if not 0, otherwise determined from interval kind
.left_brace = '\0',

// right brace override enabled if not 0, otherwise determined from interval kind
.right_brace = '\0',

// edge attributes
.edge_attributes = {"color=blue"},

// indent characters
.indent = "\t",
}
);
}
```

## Free Functions
### interval<NumericT, Kind> make_safe_interval(NumericT border1, NumericT border2)
Creates an interval where the borders are sorted so the lower border is the first one.

### draw_dot_graph(std::ostream& os, interval_tree_t<Interval> const& tree, DrawOptions const& options)
Draws a dot graph of the interval tree to the output stream.
Options are:
- digraph: bool
- name: std::string
- extra_node_attributes: std::vector<std::string>
- extra_statements: std::vector<std::string>
- space_after_comma: bool
- left_brace: std::optional<std::string>
- right_brace: std::optional<std::string>
- edge_attributes: std::vector<std::string>
- indent: std::string

## Members of IntervalTree<Interval>

- [Members of IntervalTree<Interval>](#members-of-intervaltreeinterval)
Expand Down
3 changes: 2 additions & 1 deletion example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
add_subdirectory(from_readme)
add_subdirectory(from_readme)
add_subdirectory(dot_graph)
7 changes: 7 additions & 0 deletions example/dot_graph/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
add_executable(dotgraph main.cpp)
target_link_libraries(dotgraph interval-tree)

set_target_properties(dotgraph
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/interval_tree/examples"
)
52 changes: 52 additions & 0 deletions example/dot_graph/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include <interval-tree/dot_graph.hpp>
#include <interval-tree/interval_tree.hpp>

int main()
{
using namespace lib_interval_tree;
interval_tree_t<int> tree;

tree.insert(make_safe_interval<int>(21, 16)); // make_safe_interval swaps low and high if not in right order.
tree.insert({8, 9});
tree.insert({25, 30});
tree.insert({5, 8});
tree.insert({15, 23});
tree.insert({17, 19});
tree.insert({26, 26});
tree.insert({0, 3});
tree.insert({6, 10});
tree.insert({19, 20});

draw_dot_graph(
std::cout,
tree,
{
// digraph or graph?
.digraph = true,

// graph name
.name = "G",

// extra node attributes
.extra_node_attributes = {"color=red"},

// extra graph statements
.extra_statements = {"rankdir=LR"},

// put space after comma of interval label? (a,b) vs (a, b)
.space_after_comma = false,

// left brace override, otherwise determined from interval kind
.left_brace = '\0',

// right brace override, otherwise determined from interval kind
.right_brace = '\0',

// edge attributes
.edge_attributes = {"color=blue"},

// indent characters
.indent = "\t",
}
);
}
2 changes: 2 additions & 0 deletions example/from_readme/main.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// #include <interval-tree/draw.hpp> // to draw tree. this is not header only anymore.
#include <interval-tree/interval_tree.hpp>

#include <iostream>

int main()
{
using namespace lib_interval_tree;
Expand Down
217 changes: 217 additions & 0 deletions include/interval-tree/dot_graph.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
#pragma once

#include "interval_types.hpp"

#include <iostream>
#include <string>
#include <vector>
#include <utility>

namespace lib_interval_tree
{
struct dot_graph_draw_settings
{
bool digraph = true;
std::string name = "G";
std::vector<std::string> extra_node_attributes = {};
std::vector<std::string> extra_statements = {};
bool space_after_comma = false;
char left_brace = '\0';
char right_brace = '\0';
std::vector<std::string> edge_attributes = {};
std::string indent = "\t";
};

namespace detail
{
template <typename TreeT>
class graph_painter
{
public:
graph_painter(std::ostream& stream, TreeT const& tree, dot_graph_draw_settings settings)
: stream_{stream}
, tree_{tree}
, settings_{std::move(settings)}
, node_id_{"a"}
, left_brace_{}
, right_brace_{}
{
using ival_type = typename TreeT::interval_type;

const auto determine_brace = []() {
if (std::is_same<typename ival_type::interval_kind, closed>::value)
return "[]";
else if (std::is_same<typename ival_type::interval_kind, left_open>::value)
return "(]";
else if (std::is_same<typename ival_type::interval_kind, right_open>::value)
return "[)";
else if (std::is_same<typename ival_type::interval_kind, open>::value)
return "()";
else if (std::is_same<typename ival_type::interval_kind, closed_adjacent>::value)
return "[]";
else
return "[]";
};

if (settings_.left_brace != '\0')
left_brace_ = settings_.left_brace;
else
left_brace_ = determine_brace()[0];

if (settings_.right_brace != '\0')
right_brace_ = settings_.right_brace;
else
right_brace_ = determine_brace()[1];
}

void make_header()
{
stream_ << (settings_.digraph ? "digraph" : "graph") << " " << settings_.name << " {\n";
for (auto const& statement : settings_.extra_statements)
{
stream_ << settings_.indent << statement << ";\n";
}
}

template <typename T>
void make_label(T const& ival)
{
#if __cplusplus >= 201703L
if constexpr (std::is_same<typename T::interval_kind, dynamic>::value)
{
stream_ << (ival.left_border() == interval_border::open ? '(' : '[') << ival.low()
<< (settings_.space_after_comma ? ", " : ",") << ival.high()
<< (ival.right_border() == interval_border::open ? ')' : ']');
}
else
{
stream_ << left_brace_ << ival.low() << (settings_.space_after_comma ? ", " : ",") << ival.high()
<< right_brace_;
}
#else
stream_ << left_brace_ << ival.low() << (settings_.space_after_comma ? ", " : ",") << ival.high()
<< right_brace_;
#endif
}

template <typename interval_type>
void specify_node(interval_type const& ival)
{
stream_ << settings_.indent << node_id_ << " [label=\"";
increment_node_id();
make_label(ival);
stream_ << "\"";
if (!settings_.extra_node_attributes.empty())
{
for (auto const& attr : settings_.extra_node_attributes)
{
stream_ << ", " << attr;
}
}
stream_ << "];\n";
}

template <typename iterator_type>
void specify_all_nodes(iterator_type const& node)
{
specify_node(*node);
if (node.left() != tree_.end())
specify_all_nodes(node.left());
if (node.right() != tree_.end())
specify_all_nodes(node.right());
}

void specify_edge(std::string const& from, std::string const& to)
{
stream_ << settings_.indent << from << (settings_.digraph ? " -> " : " -- ") << to;
if (!settings_.edge_attributes.empty())
{
stream_ << " [";
for (auto iter = settings_.edge_attributes.begin(); iter != settings_.edge_attributes.end(); ++iter)
{
stream_ << *iter;
if (iter + 1 != settings_.edge_attributes.end())
stream_ << ", ";
}
stream_ << "]";
}
stream_ << ";\n";
}

template <typename iterator_type>
void specify_all_edges(iterator_type const& node)
{
auto previous_id = node_id_;
if (node.left() != tree_.end())
{
increment_node_id();
specify_edge(previous_id, node_id_);
specify_all_edges(node.left());
}
if (node.right() != tree_.end())
{
increment_node_id();
specify_edge(previous_id, node_id_);
specify_all_edges(node.right());
}
}

void close()
{
stream_ << "}";
}

void reset_node_id()
{
node_id_ = "a";
}

private:
void increment_node_id()
{
const auto character = node_id_.begin();
for (auto iter = character; iter != node_id_.end(); ++iter)
{
if (*iter == 'z')
{
*iter = 'a';
if (iter + 1 == node_id_.end())
{
node_id_ += 'a';
break;
}
}
else
{
++*iter;
break;
}
}
}

private:
std::ostream& stream_;
TreeT const& tree_;
dot_graph_draw_settings settings_;
std::string node_id_;
char left_brace_;
char right_brace_;
};
}

template <typename TreeT>
void draw_dot_graph(std::ostream& stream, TreeT const& tree, dot_graph_draw_settings const& settings = {})
{
detail::graph_painter<TreeT> painter{stream, tree, settings};
painter.make_header();
if (tree.empty())
{
painter.close();
return;
}
painter.specify_all_nodes(tree.root());
painter.reset_node_id();
painter.specify_all_edges(tree.root());
painter.close();
}
}
Loading