From: Magnus Jacobsson Date: Mon, 11 Apr 2022 18:06:26 +0000 (+0200) Subject: shapes: poly_init/poly_inside: fix clipping of edge at node outline X-Git-Tag: 7.0.0~21^2~9 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b574f6d31e240e7f92a2c3fa5eb221a25a715253;p=graphviz shapes: poly_init/poly_inside: fix clipping of edge at node outline Take node penwidth into account when doing the clipping. This causes the clipping to occur at the visible node outline instead of at the "ideal" bounding box which assumes a zero node penwidth. For polygon shapes, this fixes the second part of arrow not respecting penwidth. Similar fixes need to be applied to fix the second part for other node shapes. The test_max_edge_node_overlap_simple test now succeeds, so the expectancy that it should fail is removed. The test_min_edge_node_overlap_simple and test_min_edge_node_overlap_node_shapes tests now fail since there's now a small gap between the edge and the node, so they're temporarily set to be expected to fail. This gap will be removed in an upcoming commit in this series. Towards https://gitlab.com/graphviz/graphviz/-/issues/372. --- diff --git a/lib/common/const.h b/lib/common/const.h index b14b99e64..9031f5f76 100644 --- a/lib/common/const.h +++ b/lib/common/const.h @@ -73,6 +73,8 @@ #define DEFAULT_NODEWIDTH 0.75 #define MIN_NODEWIDTH 0.01 #define DEFAULT_NODESHAPE "ellipse" +#define DEFAULT_NODEPENWIDTH 1.0 +#define MIN_NODEPENWIDTH 0.0 #define NODENAME_ESC "\\N" diff --git a/lib/common/shapes.c b/lib/common/shapes.c index e156945dc..0f595308f 100644 --- a/lib/common/shapes.c +++ b/lib/common/shapes.c @@ -1840,6 +1840,7 @@ bool isPolygon(node_t * n) static void poly_init(node_t * n) { pointf dimen, min_bb, bb; + pointf outline_bb; point imagesize; pointf *vertices; char *p, *sfile, *fxd; @@ -2061,6 +2062,8 @@ static void poly_init(node_t * n) ND_label(n)->space.y = dimen.y + temp; } + const double penwidth = late_int(n, N_penwidth, DEFAULT_NODEPENWIDTH, MIN_NODEPENWIDTH); + outp = peripheries; if (peripheries < 1) outp = 1; @@ -2087,6 +2090,7 @@ static void poly_init(node_t * n) bb.x = 2. * P.x; bb.y = 2. * P.y; } + outline_bb = bb; } else { /* @@ -2102,6 +2106,12 @@ static void poly_init(node_t * n) * the current segments, and outside by GAP distance, intersect. */ + if (peripheries >= 1 && penwidth > 0) { + // allocate extra vertices representing the outline, i.e., the outermost + // periphery with penwidth taken into account + ++outp; + } + vertices = N_NEW(outp * sides, pointf); if (ND_shape(n)->polygon->vertices) { poly_desc_t* pd = (poly_desc_t*)ND_shape(n)->polygon->vertices; @@ -2168,6 +2178,8 @@ static void poly_init(node_t * n) ymax *= 2.; bb.x = MAX(width, xmax); bb.y = MAX(height, ymax); + outline_bb = bb; + scalex = bb.x / xmax; scaley = bb.y / ymax; @@ -2178,7 +2190,7 @@ static void poly_init(node_t * n) vertices[i] = P; } - if (peripheries > 1) { + if (outp > 1) { pointf Q = vertices[(sides - 1)]; pointf R = vertices[0]; beta = atan2(R.y - Q.y, R.x - Q.x); @@ -2207,11 +2219,20 @@ static void poly_init(node_t * n) Q.y += sinx; vertices[i + j * sides] = Q; } + if (outp > peripheries) { + // add an outline at half the penwidth outside the outermost periphery + Q.x += cosx * penwidth / 2 / GAP; + Q.y += sinx * penwidth / 2 / GAP; + vertices[i + peripheries * sides] = Q; + } } for (i = 0; i < sides; i++) { pointf P = vertices[i + (peripheries - 1) * sides]; bb.x = MAX(2. * fabs(P.x), bb.x); bb.y = MAX(2. * fabs(P.y), bb.y); + pointf Q = vertices[i + (outp - 1) * sides]; + outline_bb.x = MAX(2. * fabs(Q.x), outline_bb.x); + outline_bb.y = MAX(2. * fabs(Q.y), outline_bb.y); } } } @@ -2227,9 +2248,13 @@ static void poly_init(node_t * n) /* set width and height to reflect label and shape */ ND_width(n) = PS2INCH(MAX(dimen.x,bb.x)); ND_height(n) = PS2INCH(MAX(dimen.y,bb.y)); + ND_outline_width(n) = PS2INCH(MAX(dimen.x, outline_bb.x)); + ND_outline_height(n) = PS2INCH(MAX(dimen.y, outline_bb.y)); } else { ND_width(n) = PS2INCH(bb.x); ND_height(n) = PS2INCH(bb.y); + ND_outline_width(n) = PS2INCH(outline_bb.x); + ND_outline_height(n) = PS2INCH(outline_bb.y); } ND_shape_info(n) = poly; } @@ -2283,6 +2308,8 @@ static bool poly_inside(inside_t * inside_context, pointf p) if (n != lastn) { double n_width, n_height; + double n_outline_width; + double n_outline_height; poly = ND_shape_info(n); vertex = poly->vertices; sides = poly->sides; @@ -2291,6 +2318,8 @@ static bool poly_inside(inside_t * inside_context, pointf p) boxf bb = polyBB(poly); n_width = bb.UR.x - bb.LL.x; n_height = bb.UR.y - bb.LL.y; + n_outline_width = n_width; + n_outline_height = n_height; /* get point and node size adjusted for rankdir=LR */ if (GD_flip(agraphof(n))) { ysize = n_width; @@ -2310,6 +2339,8 @@ static bool poly_inside(inside_t * inside_context, pointf p) } n_width = POINTS(ND_width(n)); n_height = POINTS(ND_height(n)); + n_outline_width = INCH2PS(ND_outline_width(n)); + n_outline_height = INCH2PS(ND_outline_height(n)); } /* scale */ @@ -2319,13 +2350,19 @@ static bool poly_inside(inside_t * inside_context, pointf p) ysize = 1.0; scalex = n_width / xsize; scaley = n_height / ysize; - box_URx = n_width / 2.0; - box_URy = n_height / 2.0; + box_URx = n_outline_width / 2.0; + box_URy = n_outline_height / 2.0; - /* index to outer-periphery */ - outp = (poly->peripheries - 1) * sides; - if (outp < 0) - outp = 0; + const double penwidth = late_int(n, N_penwidth, DEFAULT_NODEPENWIDTH, MIN_NODEPENWIDTH); + if (poly->peripheries >= 1 && penwidth > 0) { + /* index to outline, i.e., the outer-periphery with penwidth taken into account */ + outp = (poly->peripheries + 1 - 1) * sides; + } else { + /* index to outer-periphery */ + outp = (poly->peripheries - 1) * sides; + if (outp < 0) + outp = 0; + } lastn = n; } diff --git a/tests/test_max_edge_node_overlap_simple.cpp b/tests/test_max_edge_node_overlap_simple.cpp index a632ff893..8231bba7a 100644 --- a/tests/test_max_edge_node_overlap_simple.cpp +++ b/tests/test_max_edge_node_overlap_simple.cpp @@ -6,8 +6,7 @@ #include "test_utilities.h" TEST_CASE("Maximum edge node overlap", - "[!shouldfail] An edge connected to a node shall not overlap that " - "node too much") { + "An edge connected to a node shall not overlap that node too much") { const graph_options graph_options = { .node_shape = "polygon", diff --git a/tests/test_min_edge_node_overlap_polygon_node_shapes.cpp b/tests/test_min_edge_node_overlap_polygon_node_shapes.cpp index db12e5b9c..ec85dd035 100644 --- a/tests/test_min_edge_node_overlap_polygon_node_shapes.cpp +++ b/tests/test_min_edge_node_overlap_polygon_node_shapes.cpp @@ -5,8 +5,8 @@ #include "test_utilities.h" TEST_CASE("Minimum edge and node overlap for polygon node shapes", - "Test that an edge connected to a polygon based node touches that " - "node, regardless of the node shape") { + "[!shouldfail] Test that an edge connected to a polygon based node " + "touches that node, regardless of the node shape") { const auto shape = GENERATE(from_range(node_shapes_consisting_of_polygon)); INFO("Node shape: " << shape); diff --git a/tests/test_min_edge_node_overlap_simple.cpp b/tests/test_min_edge_node_overlap_simple.cpp index 6b13ce913..a436f91cd 100644 --- a/tests/test_min_edge_node_overlap_simple.cpp +++ b/tests/test_min_edge_node_overlap_simple.cpp @@ -6,7 +6,7 @@ #include "test_utilities.h" TEST_CASE("Minimum edge node overlap", - "An edge connected to a node shall touch that node") { + "[!shouldfail] An edge connected to a node shall touch that node") { const graph_options graph_options = { .node_shape = "polygon",