From cb3029425488f0a35f823a9e23b59d1ee455f97d Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Thu, 9 Jan 2025 11:56:34 +0100 Subject: [PATCH 1/3] Added draw_dot_graph function. --- .gitignore | 1 + example/CMakeLists.txt | 3 +- example/dot_graph/CMakeLists.txt | 7 + example/dot_graph/main.cpp | 21 +++ example/from_readme/main.cpp | 2 + include/interval-tree/dot_graph.hpp | 218 +++++++++++++++++++++ include/interval-tree/interval_tree.hpp | 4 - tests/dot_draw_tests.hpp | 240 ++++++++++++++++++++++++ tests/tests.cpp | 1 + 9 files changed, 492 insertions(+), 5 deletions(-) create mode 100644 example/dot_graph/CMakeLists.txt create mode 100644 example/dot_graph/main.cpp create mode 100644 include/interval-tree/dot_graph.hpp create mode 100644 tests/dot_draw_tests.hpp diff --git a/.gitignore b/.gitignore index eb2c885..7bb5cfe 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ __history/ *.cbp *.png .vscode +.clangd build tests/bin diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 8af7d72..72fd136 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1 +1,2 @@ -add_subdirectory(from_readme) \ No newline at end of file +add_subdirectory(from_readme) +add_subdirectory(dot_graph) \ No newline at end of file diff --git a/example/dot_graph/CMakeLists.txt b/example/dot_graph/CMakeLists.txt new file mode 100644 index 0000000..fa19a1e --- /dev/null +++ b/example/dot_graph/CMakeLists.txt @@ -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" +) \ No newline at end of file diff --git a/example/dot_graph/main.cpp b/example/dot_graph/main.cpp new file mode 100644 index 0000000..634eb86 --- /dev/null +++ b/example/dot_graph/main.cpp @@ -0,0 +1,21 @@ +#include +#include + +int main() +{ + using namespace lib_interval_tree; + interval_tree_t tree; + + tree.insert(make_safe_interval(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); +} \ No newline at end of file diff --git a/example/from_readme/main.cpp b/example/from_readme/main.cpp index 17d5659..063f364 100644 --- a/example/from_readme/main.cpp +++ b/example/from_readme/main.cpp @@ -1,6 +1,8 @@ // #include // to draw tree. this is not header only anymore. #include +#include + int main() { using namespace lib_interval_tree; diff --git a/include/interval-tree/dot_graph.hpp b/include/interval-tree/dot_graph.hpp new file mode 100644 index 0000000..0bb4b9d --- /dev/null +++ b/include/interval-tree/dot_graph.hpp @@ -0,0 +1,218 @@ +#pragma once + +#include "interval_types.hpp" + +#include +#include +#include +#include +#include + +namespace lib_interval_tree +{ + struct dot_graph_draw_settings + { + bool digraph = true; + std::string name = "G"; + std::vector extra_node_attributes = {}; + std::vector extra_statements = {}; + bool space_after_comma = false; + std::optional left_brace = std::nullopt; + std::optional right_brace = std::nullopt; + std::vector edge_attributes = {}; + std::string indent = "\t"; + }; + + namespace detail + { + template + 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_v) + return "[]"; + else if (std::is_same_v) + return "(]"; + else if (std::is_same_v) + return "[)"; + else if (std::is_same_v) + return "()"; + else if (std::is_same_v) + return "[]"; + else + return "[]"; + }; + + if (settings_.left_brace) + left_brace_ = *settings_.left_brace; + else + left_brace_ = determine_brace()[0]; + + if (settings_.right_brace) + 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 + void make_label(T const& ival) + { +#if __cplusplus >= 201703L + if constexpr (std::is_same_v) + { + 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 + 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 + 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 + 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 + void draw_dot_graph(std::ostream& stream, TreeT const& tree, dot_graph_draw_settings const& settings = {}) + { + detail::graph_painter 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(); + } +} \ No newline at end of file diff --git a/include/interval-tree/interval_tree.hpp b/include/interval-tree/interval_tree.hpp index 2b283c5..c239d77 100644 --- a/include/interval-tree/interval_tree.hpp +++ b/include/interval-tree/interval_tree.hpp @@ -6,14 +6,10 @@ #include "feature_test.hpp" #include -#include -#include #include #include #include -#include - namespace lib_interval_tree { // ############################################################################################################ diff --git a/tests/dot_draw_tests.hpp b/tests/dot_draw_tests.hpp new file mode 100644 index 0000000..a7bdc39 --- /dev/null +++ b/tests/dot_draw_tests.hpp @@ -0,0 +1,240 @@ +#pragma once + +#include +#include + +class DotDrawTests : public ::testing::Test +{ + public: + + protected: +}; + +TEST_F(DotDrawTests, WillDrawEmptyTree) +{ + lib_interval_tree::interval_tree_t tree; + std::stringstream ss; + lib_interval_tree::draw_dot_graph(ss, tree); + EXPECT_EQ(ss.str(), "digraph G {\n}"); +} + +TEST_F(DotDrawTests, DrawsSingleNode) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + std::stringstream ss; + lib_interval_tree::draw_dot_graph(ss, tree); + EXPECT_EQ(ss.str(), "digraph G {\n\ta [label=\"[0,10]\"];\n}"); +} + +TEST_F(DotDrawTests, DrawsNodesWithEdge) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + tree.insert({10, 20}); + std::stringstream ss; + lib_interval_tree::draw_dot_graph(ss, tree); + EXPECT_EQ(ss.str(), "digraph G {\n\ta [label=\"[0,10]\"];\n\tb [label=\"[10,20]\"];\n\ta -> b;\n}"); +} + +TEST_F(DotDrawTests, SpaceAfterCommaIsSetIfEnabled) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + std::stringstream ss; + lib_interval_tree::dot_graph_draw_settings settings; + settings.space_after_comma = true; + lib_interval_tree::draw_dot_graph(ss, tree, settings); + EXPECT_EQ(ss.str(), "digraph G {\n\ta [label=\"[0, 10]\"];\n}"); +} + +TEST_F(DotDrawTests, GraphIsUndirectedIfSpecifiedAsSuch) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + tree.insert({10, 20}); + std::stringstream ss; + lib_interval_tree::dot_graph_draw_settings settings; + settings.digraph = false; + lib_interval_tree::draw_dot_graph(ss, tree, settings); + EXPECT_EQ(ss.str(), "graph G {\n\ta [label=\"[0,10]\"];\n\tb [label=\"[10,20]\"];\n\ta -- b;\n}"); +} + +TEST_F(DotDrawTests, CanSpecifyExtraNodeAttributes) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + std::stringstream ss; + lib_interval_tree::dot_graph_draw_settings settings; + settings.extra_node_attributes = {"color=red"}; + lib_interval_tree::draw_dot_graph(ss, tree, settings); + EXPECT_EQ(ss.str(), "digraph G {\n\ta [label=\"[0,10]\", color=red];\n}"); +} + +TEST_F(DotDrawTests, GraphNameIsSet) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + std::stringstream ss; + lib_interval_tree::dot_graph_draw_settings settings; + settings.name = "MyGraph"; + lib_interval_tree::draw_dot_graph(ss, tree, settings); + EXPECT_EQ(ss.str(), "digraph MyGraph {\n\ta [label=\"[0,10]\"];\n}"); +} + +TEST_F(DotDrawTests, CanOverrideLeftBrace) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + std::stringstream ss; + lib_interval_tree::dot_graph_draw_settings settings; + settings.left_brace = '('; + lib_interval_tree::draw_dot_graph(ss, tree, settings); + EXPECT_EQ(ss.str(), "digraph G {\n\ta [label=\"(0,10]\"];\n}"); +} + +TEST_F(DotDrawTests, CanOverrideRightBrace) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + std::stringstream ss; + lib_interval_tree::dot_graph_draw_settings settings; + settings.right_brace = ')'; + lib_interval_tree::draw_dot_graph(ss, tree, settings); + EXPECT_EQ(ss.str(), "digraph G {\n\ta [label=\"[0,10)\"];\n}"); +} + +TEST_F(DotDrawTests, CanSpecifyExtraStatements) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + std::stringstream ss; + lib_interval_tree::dot_graph_draw_settings settings; + settings.extra_statements = {"rankdir=LR"}; + lib_interval_tree::draw_dot_graph(ss, tree, settings); + EXPECT_EQ(ss.str(), "digraph G {\n\trankdir=LR;\n\ta [label=\"[0,10]\"];\n}"); +} + +TEST_F(DotDrawTests, CanSpecifyExtraEdgeAttributes) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + tree.insert({10, 20}); + std::stringstream ss; + lib_interval_tree::dot_graph_draw_settings settings; + settings.edge_attributes = {"color=blue"}; + lib_interval_tree::draw_dot_graph(ss, tree, settings); + EXPECT_EQ(ss.str(), "digraph G {\n\ta [label=\"[0,10]\"];\n\tb [label=\"[10,20]\"];\n\ta -> b [color=blue];\n}"); +} + +TEST_F(DotDrawTests, IndentIsSet) +{ + lib_interval_tree::interval_tree_t tree; + tree.insert({0, 10}); + std::stringstream ss; + lib_interval_tree::dot_graph_draw_settings settings; + settings.indent = "XXX"; + lib_interval_tree::draw_dot_graph(ss, tree, settings); + EXPECT_EQ(ss.str(), "digraph G {\nXXXa [label=\"[0,10]\"];\n}"); +} + +TEST_F(DotDrawTests, CanDrawTreeWithOpenIntervalType) +{ + lib_interval_tree::interval_tree> tree; + tree.insert({0, 10}); + tree.insert({10, 20}); + std::stringstream ss; + lib_interval_tree::draw_dot_graph(ss, tree); + EXPECT_EQ(ss.str(), "digraph G {\n\ta [label=\"(0,10)\"];\n\tb [label=\"(10,20)\"];\n\ta -> b;\n}"); +} + +TEST_F(DotDrawTests, CanDrawTreeWithLeftOpenIntervalType) +{ + lib_interval_tree::interval_tree> tree; + tree.insert({0, 10}); + tree.insert({10, 20}); + std::stringstream ss; + lib_interval_tree::draw_dot_graph(ss, tree); + EXPECT_EQ(ss.str(), "digraph G {\n\ta [label=\"(0,10]\"];\n\tb [label=\"(10,20]\"];\n\ta -> b;\n}"); +} + +TEST_F(DotDrawTests, CanDrawTreeWithRightOpenIntervalType) +{ + lib_interval_tree::interval_tree> tree; + tree.insert({0, 10}); + tree.insert({10, 20}); + std::stringstream ss; + lib_interval_tree::draw_dot_graph(ss, tree); + EXPECT_EQ(ss.str(), "digraph G {\n\ta [label=\"[0,10)\"];\n\tb [label=\"[10,20)\"];\n\ta -> b;\n}"); +} + +TEST_F(DotDrawTests, CanDrawTreeWithClosedAdjacentIntervalType) +{ + lib_interval_tree::interval_tree> tree; + tree.insert({0, 10}); + tree.insert({10, 20}); + std::stringstream ss; + lib_interval_tree::draw_dot_graph(ss, tree); + EXPECT_EQ(ss.str(), "digraph G {\n\ta [label=\"[0,10]\"];\n\tb [label=\"[10,20]\"];\n\ta -> b;\n}"); +} + +#if __cplusplus >= 201703L +TEST_F(DotDrawTests, CanDrawTreeWithDynamicIntervalType) +{ + lib_interval_tree::interval_tree> tree; + tree.insert({0, 10, lib_interval_tree::interval_border::closed, lib_interval_tree::interval_border::open}); + tree.insert({10, 20, lib_interval_tree::interval_border::open, lib_interval_tree::interval_border::closed_adjacent} + ); + std::stringstream ss; + lib_interval_tree::draw_dot_graph(ss, tree); + EXPECT_EQ(ss.str(), "digraph G {\n\ta [label=\"[0,10)\"];\n\tb [label=\"(10,20]\"];\n\ta -> b;\n}"); +} +#endif + +TEST_F(DotDrawTests, CanDrawLargerTree) +{ + using namespace lib_interval_tree; + interval_tree_t tree; + + tree.insert(make_safe_interval(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}); + + std::stringstream ss; + + lib_interval_tree::dot_graph_draw_settings settings; + settings.indent = ""; + + draw_dot_graph(ss, tree, settings); + EXPECT_EQ( + ss.str(), + R"(digraph G { +a [label="[16,21]"]; +b [label="[8,9]"]; +c [label="[5,8]"]; +d [label="[0,3]"]; +e [label="[6,10]"]; +f [label="[15,23]"]; +g [label="[25,30]"]; +h [label="[17,19]"]; +i [label="[19,20]"]; +j [label="[26,26]"]; +a -> b; +b -> c; +c -> d; +c -> e; +b -> f; +a -> g; +g -> h; +h -> i; +g -> j; +})" + ); +} \ No newline at end of file diff --git a/tests/tests.cpp b/tests/tests.cpp index de6ab1a..93a925c 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -15,6 +15,7 @@ #include "iteration_tests.hpp" #include "hook_tests.hpp" #include "custom_interval_tests.hpp" +#include "dot_draw_tests.hpp" int main(int argc, char** argv) { From 35ed3a974bf8f0cee9e79814c0f45fda68f263fe Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Thu, 9 Jan 2025 12:05:12 +0100 Subject: [PATCH 2/3] Documented new graph drawing. --- README.md | 72 +++++++++++++++++++++++++++++++++++++- example/dot_graph/main.cpp | 33 ++++++++++++++++- 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ea62691..a5b0ddc 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ int main() tree.insert({19, 20}); tree.deoverlap(); - + for (auto const& i : tree) { 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 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 +#include + +int main() +{ + using namespace lib_interval_tree; + interval_tree_t tree; + + tree.insert(make_safe_interval(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 = std::nullopt, + + // right brace override, otherwise determined from interval kind + .right_brace = std::nullopt, + + // edge attributes + .edge_attributes = {"color=blue"}, + + // indent characters + .indent = "\t", + } + ); +} +``` + ## Free Functions ### interval 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 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 +- extra_statements: std::vector +- space_after_comma: bool +- left_brace: std::optional +- right_brace: std::optional +- edge_attributes: std::vector +- indent: std::string + ## Members of IntervalTree - [Members of IntervalTree](#members-of-intervaltreeinterval) diff --git a/example/dot_graph/main.cpp b/example/dot_graph/main.cpp index 634eb86..305db0f 100644 --- a/example/dot_graph/main.cpp +++ b/example/dot_graph/main.cpp @@ -17,5 +17,36 @@ int main() tree.insert({6, 10}); tree.insert({19, 20}); - draw_dot_graph(std::cout, tree); + 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 = std::nullopt, + + // right brace override, otherwise determined from interval kind + .right_brace = std::nullopt, + + // edge attributes + .edge_attributes = {"color=blue"}, + + // indent characters + .indent = "\t", + } + ); } \ No newline at end of file From ef196987481a96aacf8ee3345598e67483afc4ff Mon Sep 17 00:00:00 2001 From: Tim Ebbeke Date: Thu, 9 Jan 2025 12:10:35 +0100 Subject: [PATCH 3/3] Removed C++17 and C++20 code. --- README.md | 8 ++++---- example/dot_graph/main.cpp | 4 ++-- include/interval-tree/dot_graph.hpp | 27 +++++++++++++-------------- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index a5b0ddc..e09e669 100644 --- a/README.md +++ b/README.md @@ -102,11 +102,11 @@ int main() // 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 = std::nullopt, + // left brace override enabled if not 0, otherwise determined from interval kind + .left_brace = '\0', - // right brace override, otherwise determined from interval kind - .right_brace = std::nullopt, + // right brace override enabled if not 0, otherwise determined from interval kind + .right_brace = '\0', // edge attributes .edge_attributes = {"color=blue"}, diff --git a/example/dot_graph/main.cpp b/example/dot_graph/main.cpp index 305db0f..c4350be 100644 --- a/example/dot_graph/main.cpp +++ b/example/dot_graph/main.cpp @@ -37,10 +37,10 @@ int main() .space_after_comma = false, // left brace override, otherwise determined from interval kind - .left_brace = std::nullopt, + .left_brace = '\0', // right brace override, otherwise determined from interval kind - .right_brace = std::nullopt, + .right_brace = '\0', // edge attributes .edge_attributes = {"color=blue"}, diff --git a/include/interval-tree/dot_graph.hpp b/include/interval-tree/dot_graph.hpp index 0bb4b9d..5533fac 100644 --- a/include/interval-tree/dot_graph.hpp +++ b/include/interval-tree/dot_graph.hpp @@ -4,7 +4,6 @@ #include #include -#include #include #include @@ -17,8 +16,8 @@ namespace lib_interval_tree std::vector extra_node_attributes = {}; std::vector extra_statements = {}; bool space_after_comma = false; - std::optional left_brace = std::nullopt; - std::optional right_brace = std::nullopt; + char left_brace = '\0'; + char right_brace = '\0'; std::vector edge_attributes = {}; std::string indent = "\t"; }; @@ -40,27 +39,27 @@ namespace lib_interval_tree using ival_type = typename TreeT::interval_type; const auto determine_brace = []() { - if (std::is_same_v) + if (std::is_same::value) return "[]"; - else if (std::is_same_v) + else if (std::is_same::value) return "(]"; - else if (std::is_same_v) + else if (std::is_same::value) return "[)"; - else if (std::is_same_v) + else if (std::is_same::value) return "()"; - else if (std::is_same_v) + else if (std::is_same::value) return "[]"; else return "[]"; }; - if (settings_.left_brace) - left_brace_ = *settings_.left_brace; + if (settings_.left_brace != '\0') + left_brace_ = settings_.left_brace; else left_brace_ = determine_brace()[0]; - if (settings_.right_brace) - right_brace_ = *settings_.right_brace; + if (settings_.right_brace != '\0') + right_brace_ = settings_.right_brace; else right_brace_ = determine_brace()[1]; } @@ -78,7 +77,7 @@ namespace lib_interval_tree void make_label(T const& ival) { #if __cplusplus >= 201703L - if constexpr (std::is_same_v) + if constexpr (std::is_same::value) { stream_ << (ival.left_border() == interval_border::open ? '(' : '[') << ival.low() << (settings_.space_after_comma ? ", " : ",") << ival.high() @@ -203,7 +202,7 @@ namespace lib_interval_tree template void draw_dot_graph(std::ostream& stream, TreeT const& tree, dot_graph_draw_settings const& settings = {}) { - detail::graph_painter painter{stream, tree, settings}; + detail::graph_painter painter{stream, tree, settings}; painter.make_header(); if (tree.empty()) {