From: Magnus Jacobsson Date: Sun, 28 Aug 2022 19:58:57 +0000 (+0200) Subject: add new test_edge_node_overlap_polygon_node_shapes test X-Git-Tag: 6.0.2~34^2~10 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=224da27425bdcf5bf9a0e20277aa834483cebd34;p=graphviz add new test_edge_node_overlap_polygon_node_shapes test An upcoming commit series will add separate fixes for different parts of the overlap problems. Having separate test cases for checking maximum or minimum overlap will make it easier to see that the fixes have the intended effect. --- diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1efc556be..82a6a208f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -89,6 +89,7 @@ CREATE_TEST(GVContext_render_svg) CREATE_TEST(GVLayout_construction) CREATE_TEST(GVLayout_render) CREATE_TEST(edge_node_overlap_all_node_shapes) +CREATE_TEST(edge_node_overlap_polygon_node_shapes) CREATE_TEST(edge_node_overlap_simple) CREATE_TEST(max_edge_node_overlap_simple) CREATE_TEST(min_edge_node_overlap_simple) diff --git a/tests/test_edge_node_overlap_polygon_node_shapes.cpp b/tests/test_edge_node_overlap_polygon_node_shapes.cpp new file mode 100644 index 000000000..5e6d35848 --- /dev/null +++ b/tests/test_edge_node_overlap_polygon_node_shapes.cpp @@ -0,0 +1,25 @@ +#include +#include + +#include "test_edge_node_overlap_utilities.h" +#include "test_utilities.h" + +TEST_CASE( + "Overlap polygon node shapes", + "[!shouldfail] Test that an edge connected to a polygon based node touches " + "that node and does not overlap it too much, regardless of the node " + "shape") { + + const auto shape = GENERATE(from_range(node_shapes_consisting_of_polygon)); + INFO(fmt::format("Node shape: {}", shape)); + + const graph_options graph_options = { + .node_shape = shape, + .node_penwidth = 2, + .edge_penwidth = 2, + }; + + const auto filename_base = fmt::format("{}_{}", AUTO_NAME(), shape); + + test_edge_node_overlap(graph_options, {}, {.filename_base = filename_base}); +} diff --git a/tests/test_edge_node_overlap_utilities.cpp b/tests/test_edge_node_overlap_utilities.cpp index 6e218d213..b9bc46926 100644 --- a/tests/test_edge_node_overlap_utilities.cpp +++ b/tests/test_edge_node_overlap_utilities.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include @@ -9,6 +11,133 @@ #include "test_utilities.h" #include +/// return union of unordered sets of string views +std::unordered_set +union_(const std::unordered_set &a, + const std::unordered_set &b, + const std::unordered_set &c = {}) { + std::unordered_set ret = a; + ret.insert(b.begin(), b.end()); + ret.insert(c.begin(), c.end()); + + return ret; +} + +static const std::unordered_set + shapes_not_meeting_edge_vertically = { + "plaintext", // + "plain", // + "none", // + "promoter", // + "cds", // + "terminator", // + "utr", // + "primersite", // + "restrictionsite", // + "fivepoverhang", // + "threepoverhang", // + "noverhang", // + "assembly", // + "signature", // + "insulator", // + "ribosite", // + "rnastab", // + "proteasesite", // + "proteinstab", // +}; + +static const std::unordered_set + shapes_not_meeting_edge_horizontally = { + "plaintext", // has space around the label as if it was a box shape + "none", // has space around the label as if it was a box shape +}; + +static const std::unordered_set & +shapes_not_meeting_edge(const std::string_view rankdir) { + if (rankdir == "TB" || rankdir == "BT") { + return shapes_not_meeting_edge_vertically; + } else if (rankdir == "LR" || rankdir == "RL") { + return shapes_not_meeting_edge_horizontally; + } + UNREACHABLE(); +} + +static const std::unordered_set shapes_with_concave_top = { + "folder", "tab", "promoter", "rpromoter", "rarrow", "larrow", "lpromoter", +}; + +static const std::unordered_set shapes_with_concave_bottom = { + "star", "rpromoter", "rarrow", "larrow", "lpromoter"}; + +static const std::unordered_set shapes_with_concave_left = { + "component"}; + +static const std::unordered_set + shapes_with_left_extreme_not_centered = { + "egg", "triangle", "invtriangle", "trapezium", "invtrapezium", + "parallelogram", "pentagon", "septagon", "star"}; + +static const std::unordered_set + shapes_with_right_extreme_not_centered = { + "egg", "triangle", "invtriangle", "trapezium", "invtrapezium", + "parallelogram", "pentagon", "septagon", "star"}; + +static const std::unordered_set + shapes_with_invisible_descent = {"plain"}; + +static const std::unordered_set + shapes_with_invisible_left_extension = {"plain"}; + +static const std::unordered_set + shapes_with_invisible_right_extension = {"plain"}; + +static const std::unordered_set + shapes_not_to_check_for_overlap_at_top = shapes_with_concave_top; + +static const std::unordered_set + shapes_not_to_check_for_overlap_at_bottom = + union_(shapes_with_concave_bottom, shapes_with_invisible_descent); + +static const std::unordered_set + shapes_not_to_check_for_overlap_at_left_side = + union_(shapes_with_left_extreme_not_centered, shapes_with_concave_left, + shapes_with_invisible_left_extension); + +static const std::unordered_set + shapes_not_to_check_for_overlap_at_right_side = + union_(shapes_with_right_extreme_not_centered, + shapes_with_invisible_right_extension); + +static const std::unordered_set & +shapes_not_to_check_for_max_overlap_at_edge_head( + const std::string_view rankdir) { + if (rankdir == "TB") { + return shapes_not_to_check_for_overlap_at_top; + } else if (rankdir == "BT") { + return shapes_not_to_check_for_overlap_at_bottom; + } else if (rankdir == "LR") { + return shapes_not_to_check_for_overlap_at_left_side; + } else if (rankdir == "RL") { + return shapes_not_to_check_for_overlap_at_right_side; + } + UNREACHABLE(); +} + +const std::unordered_set & +shapes_not_to_check_for_max_overlap_at_edge_tail( + const std::string_view rankdir) { + if (rankdir == "TB") { + return shapes_not_to_check_for_overlap_at_bottom; + } else if (rankdir == "BT") { + return shapes_not_to_check_for_overlap_at_top; + } else if (rankdir == "LR") { + return shapes_not_to_check_for_overlap_at_right_side; + } else if (rankdir == "RL") { + return shapes_not_to_check_for_overlap_at_left_side; + } + UNREACHABLE(); +} + /// return the overlap in the rank direction from an intersection rectangle static double overlap_in_rank_direction(SVG::SVGRect intersection, const std::string_view rankdir) { @@ -21,12 +150,35 @@ static double overlap_in_rank_direction(SVG::SVGRect intersection, UNREACHABLE(); } +static bool skip_max_check_at_head_node(std::string_view rankdir, + std::string_view node_shape) { + return shapes_not_to_check_for_max_overlap_at_edge_head(rankdir).contains( + node_shape); +} + +static bool skip_max_check_at_tail_node(std::string_view rankdir, + std::string_view node_shape) { + return shapes_not_to_check_for_max_overlap_at_edge_tail(rankdir).contains( + node_shape); +} + +static bool skip_min_check_at_head_node(std::string_view rankdir, + std::string_view node_shape) { + return shapes_not_meeting_edge(rankdir).contains(node_shape); +} + +static bool skip_min_check_at_tail_node(std::string_view rankdir, + std::string_view node_shape) { + return shapes_not_meeting_edge(rankdir).contains(node_shape); +} + /// check overlap between the edge and the nodes static bool check_analyzed_svg(SVGAnalyzer &svg_analyzer, const graph_options &graph_options, const check_options &check_options) { const auto rankdir = graph_options.rankdir; + const auto node_shape = graph_options.node_shape; REQUIRE(svg_analyzer.graphs().size() == 1); auto &recreated_graph = svg_analyzer.graphs().back(); @@ -60,16 +212,20 @@ static bool check_analyzed_svg(SVGAnalyzer &svg_analyzer, // check maximum head node and edge overlap if (check_options.check_max_edge_node_overlap) { - DO_CHECK(head_node_edge_overlap <= - check_options.max_node_edge_overlap + - check_options.svg_rounding_error * 2); + if (!skip_max_check_at_head_node(rankdir, node_shape)) { + DO_CHECK(head_node_edge_overlap <= + check_options.max_node_edge_overlap + + check_options.svg_rounding_error * 2); + } } // check minimum head node and edge overlap if (check_options.check_min_edge_node_overlap) { - DO_CHECK(head_node_edge_overlap >= - check_options.min_node_edge_overlap - - check_options.svg_rounding_error * 2); + if (!skip_min_check_at_head_node(rankdir, node_shape)) { + DO_CHECK(head_node_edge_overlap >= + check_options.min_node_edge_overlap - + check_options.svg_rounding_error * 2); + } } } @@ -85,16 +241,20 @@ static bool check_analyzed_svg(SVGAnalyzer &svg_analyzer, // check maximum tail node and edge overlap if (check_options.check_max_edge_node_overlap) { - DO_CHECK(tail_node_edge_overlap <= - check_options.max_node_edge_overlap + - check_options.svg_rounding_error * 2); + if (!skip_max_check_at_tail_node(rankdir, node_shape)) { + DO_CHECK(tail_node_edge_overlap <= + check_options.max_node_edge_overlap + + check_options.svg_rounding_error * 2); + } } // check minimum overlap at edge tail if (check_options.check_min_edge_node_overlap) { - DO_CHECK(tail_node_edge_overlap >= - check_options.min_node_edge_overlap - - check_options.svg_rounding_error * 2); + if (!skip_min_check_at_tail_node(rankdir, node_shape)) { + DO_CHECK(tail_node_edge_overlap >= + check_options.min_node_edge_overlap - + check_options.svg_rounding_error * 2); + } } } diff --git a/tests/test_edge_node_overlap_utilities.h b/tests/test_edge_node_overlap_utilities.h index 41f828726..1caa0c353 100644 --- a/tests/test_edge_node_overlap_utilities.h +++ b/tests/test_edge_node_overlap_utilities.h @@ -1,4 +1,5 @@ #include +#include #include "svg_analyzer.h" diff --git a/tests/test_utilities.cpp b/tests/test_utilities.cpp index 7cc2bf458..49b2b9b50 100644 --- a/tests/test_utilities.cpp +++ b/tests/test_utilities.cpp @@ -179,6 +179,10 @@ bool contains_multiple_shapes_with_different_fill( contains_polyline_shape(shape); } +bool is_polygon_shape(const std::string_view shape) { + return node_shapes_consisting_of_polygon.contains(shape); +} + const std::unordered_set primitive_polygon_arrow_shapes = { "crow", "diamond", "inv", "normal", "vee"}; @@ -195,6 +199,10 @@ const std::unordered_set const std::unordered_set all_rank_directions = {"TB", "BT", "LR", "RL"}; + +const std::unordered_set all_edge_directions = { + "forward", "back", "both", "none"}; + void write_to_file(const std::filesystem::path &directory, const std::filesystem::path &filename, const std::string_view text) { diff --git a/tests/test_utilities.h b/tests/test_utilities.h index 4a7cddd7c..7382fba7b 100644 --- a/tests/test_utilities.h +++ b/tests/test_utilities.h @@ -26,6 +26,8 @@ bool contains_multiple_shapes_with_different_fill(std::string_view shape); bool contains_polygon_shape(std::string_view shape); bool contains_polyline_shape(std::string_view shape); +bool is_polygon_shape(std::string_view shape); + /// arrow shapes extern const std::unordered_set primitive_polygon_arrow_shapes; @@ -38,6 +40,9 @@ extern const std::unordered_set /// rank directions extern const std::unordered_set all_rank_directions; +/// edge directions +extern const std::unordered_set all_edge_directions; + /// misc utilities /// get the base name of the test case file without file extension