]> granicus.if.org Git - graphviz/commitdiff
add new test_edge_node_overlap_polygon_node_shapes test
authorMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Sun, 28 Aug 2022 19:58:57 +0000 (21:58 +0200)
committerMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Tue, 20 Sep 2022 15:53:06 +0000 (17:53 +0200)
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.

tests/CMakeLists.txt
tests/test_edge_node_overlap_polygon_node_shapes.cpp [new file with mode: 0644]
tests/test_edge_node_overlap_utilities.cpp
tests/test_edge_node_overlap_utilities.h
tests/test_utilities.cpp
tests/test_utilities.h

index 1efc556be74dabfa52ee0bc88d3b13f66a29e784..82a6a208f9c80abaea1353088748d8fb7cd10e36 100644 (file)
@@ -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 (file)
index 0000000..5e6d358
--- /dev/null
@@ -0,0 +1,25 @@
+#include <catch2/catch.hpp>
+#include <fmt/format.h>
+
+#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});
+}
index 6e218d2131cc924e13e50c3a69556a749af560e1..b9bc46926f92574882c7933029cd16555eb15574 100644 (file)
@@ -1,4 +1,6 @@
 #include <string>
+#include <string_view>
+#include <unordered_set>
 
 #include <catch2/catch.hpp>
 #include <cmath>
 #include "test_utilities.h"
 #include <cgraph/unreachable.h>
 
+/// return union of unordered sets of string views
+std::unordered_set<std::string_view>
+union_(const std::unordered_set<std::string_view> &a,
+       const std::unordered_set<std::string_view> &b,
+       const std::unordered_set<std::string_view> &c = {}) {
+  std::unordered_set<std::string_view> ret = a;
+  ret.insert(b.begin(), b.end());
+  ret.insert(c.begin(), c.end());
+
+  return ret;
+}
+
+static const std::unordered_set<std::string_view>
+    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<std::string_view>
+    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<std::string_view> &
+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<std::string_view> shapes_with_concave_top = {
+    "folder", "tab", "promoter", "rpromoter", "rarrow", "larrow", "lpromoter",
+};
+
+static const std::unordered_set<std::string_view> shapes_with_concave_bottom = {
+    "star", "rpromoter", "rarrow", "larrow", "lpromoter"};
+
+static const std::unordered_set<std::string_view> shapes_with_concave_left = {
+    "component"};
+
+static const std::unordered_set<std::string_view>
+    shapes_with_left_extreme_not_centered = {
+        "egg",           "triangle", "invtriangle", "trapezium", "invtrapezium",
+        "parallelogram", "pentagon", "septagon",    "star"};
+
+static const std::unordered_set<std::string_view>
+    shapes_with_right_extreme_not_centered = {
+        "egg",           "triangle", "invtriangle", "trapezium", "invtrapezium",
+        "parallelogram", "pentagon", "septagon",    "star"};
+
+static const std::unordered_set<std::string_view>
+    shapes_with_invisible_descent = {"plain"};
+
+static const std::unordered_set<std::string_view>
+    shapes_with_invisible_left_extension = {"plain"};
+
+static const std::unordered_set<std::string_view>
+    shapes_with_invisible_right_extension = {"plain"};
+
+static const std::unordered_set<std::string_view>
+    shapes_not_to_check_for_overlap_at_top = shapes_with_concave_top;
+
+static const std::unordered_set<std::string_view>
+    shapes_not_to_check_for_overlap_at_bottom =
+        union_(shapes_with_concave_bottom, shapes_with_invisible_descent);
+
+static const std::unordered_set<std::string_view>
+    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<std::string_view>
+    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<std::string_view> &
+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<std::string_view> &
+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);
+      }
     }
   }
 
index 41f8287268744fa4e6d2dbacb27ebde8f36c5a02..1caa0c353be4dff18210a2903132b1ca52eebe7e 100644 (file)
@@ -1,4 +1,5 @@
 #include <string>
+#include <string_view>
 
 #include "svg_analyzer.h"
 
index 7cc2bf4585f373a4dccf8d617835055174ac7673..49b2b9b50fde05fd0a473b7820559fe6cd3d336d 100644 (file)
@@ -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<std::string_view> primitive_polygon_arrow_shapes = {
     "crow", "diamond", "inv", "normal", "vee"};
 
@@ -195,6 +199,10 @@ const std::unordered_set<std::string_view>
 
 const std::unordered_set<std::string_view> all_rank_directions = {"TB", "BT",
                                                                   "LR", "RL"};
+
+const std::unordered_set<std::string_view> 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) {
index 4a7cddd7c73e113520117ab92588aa48ddad8558..7382fba7b2beb443130a2d46e74418f6122d3965 100644 (file)
@@ -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<std::string_view>
     primitive_polygon_arrow_shapes;
@@ -38,6 +40,9 @@ extern const std::unordered_set<std::string_view>
 /// rank directions
 extern const std::unordered_set<std::string_view> all_rank_directions;
 
+/// edge directions
+extern const std::unordered_set<std::string_view> all_edge_directions;
+
 /// misc utilities
 
 /// get the base name of the test case file without file extension