From: erg Date: Fri, 15 Jul 2005 19:25:16 +0000 (+0000) Subject: Tweak the new spline routing code to fix the problems seen in the X-Git-Tag: LAST_LIBGRAPH~32^2~7431 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3c8acb596e5b48630c69b3e9856e1ab5c145360e;p=graphviz Tweak the new spline routing code to fix the problems seen in the examples given by Bill Zissimopoulos (See mailing list posting on 14 July 2005.) --- diff --git a/lib/common/shapes.c b/lib/common/shapes.c index cf775218c..e33f8a813 100644 --- a/lib/common/shapes.c +++ b/lib/common/shapes.c @@ -105,6 +105,8 @@ static polygon_t p_Msquare = { TRUE, 1, 4, 0., 0., 0., DIAGONALS }; static polygon_t p_Mcircle = { TRUE, 1, 1, 0., 0., 0., DIAGONALS | AUXLABELS }; +#define IS_BOX(n) (ND_shape(n)->polygon == &p_box) + /* * every shape has these functions: * @@ -982,14 +984,6 @@ static double invflip_angle (double angle, int rankdir) return angle; } -/* would like = 0, but spline router croaks - * Basically, if the point is actually on a box, the curve fitter - * may, at some point, compute a curve which lies entirely outside - * the polygon except at the two endpoints. Thus, by guaranteeing that - * the point lies in the interior, such bad cases can't happen. - */ -#define MARGIN 1 - /* compassPoint: * Compute compass points for non-trivial shapes. * It finds where the ray ((0,0),(x,y)) hits the boundary and @@ -1070,7 +1064,7 @@ compassPort(node_t* n, box* bp, port* pp, char* compass, int sides, inside_t* ic if (compass && *compass) { switch (*compass++) { case 'e': - p.x = b.UR.x + MARGIN; + p.x = b.UR.x; theta = 0.0; constrain = 1; defined = TRUE; @@ -1078,26 +1072,28 @@ compassPort(node_t* n, box* bp, port* pp, char* compass, int sides, inside_t* ic side = sides & RIGHT; break; case 's': - p.y = b.LL.y - MARGIN; + p.y = b.LL.y; constrain = 1; clip = FALSE; - side = sides & BOTTOM; switch (*compass) { case '\0': theta = -PI * 0.5; defined = TRUE; + side = sides & BOTTOM; break; case 'e': theta = -PI * 0.25; defined = TRUE; if (ictxt) p = compassPoint (ictxt, -MAXINT, MAXINT); - else p.x = b.UR.x + MARGIN; + else p.x = b.UR.x; + side = sides & (BOTTOM | RIGHT); break; case 'w': theta = -PI * 0.75; defined = TRUE; if (ictxt) p = compassPoint (ictxt, -MAXINT, -MAXINT); - else p.x = b.LL.x - MARGIN; + else p.x = b.LL.x; + side = sides & (BOTTOM | LEFT); break; default: p.y = ctr.y; @@ -1108,7 +1104,7 @@ compassPort(node_t* n, box* bp, port* pp, char* compass, int sides, inside_t* ic } break; case 'w': - p.x = b.LL.x - MARGIN; + p.x = b.LL.x; theta = PI; constrain = 1; defined = TRUE; @@ -1116,26 +1112,28 @@ compassPort(node_t* n, box* bp, port* pp, char* compass, int sides, inside_t* ic side = sides & LEFT; break; case 'n': - p.y = b.UR.y + MARGIN; + p.y = b.UR.y; constrain = 1; clip = FALSE; - side = sides & TOP; switch (*compass) { case '\0': defined = TRUE; theta = PI * 0.5; + side = sides & TOP; break; case 'e': defined = TRUE; theta = PI * 0.25; if (ictxt) p = compassPoint (ictxt, MAXINT, MAXINT); - else p.x = b.UR.x + MARGIN; + else p.x = b.UR.x; + side = sides & (TOP | RIGHT); break; case 'w': defined = TRUE; theta = PI * 0.75; if (ictxt) p = compassPoint (ictxt, MAXINT, -MAXINT); - else p.x = b.LL.x - MARGIN; + else p.x = b.LL.x; + side = sides & (TOP | LEFT); break; default: p.y = ctr.y; @@ -1187,11 +1185,16 @@ static port poly_port(node_t * n, char *portname, char *compass) } } else { - inside_t ictxt; + inside_t* ictxtp; + inside_t ictxt; - ictxt.s.n = n; - ictxt.s.bp = NULL; - if (compassPort(n, NULL, &rv, portname, sides, &ictxt)) + if (IS_BOX(n)) ictxtp = NULL; + else { + ictxt.s.n = n; + ictxt.s.bp = NULL; + ictxtp = &ictxt; + } + if (compassPort(n, NULL, &rv, portname, sides, ictxtp)) unrecognized(n, portname); } diff --git a/lib/common/splines.c b/lib/common/splines.c index 92e03f84e..d21fe9e25 100644 --- a/lib/common/splines.c +++ b/lib/common/splines.c @@ -360,6 +360,15 @@ void add_box(path * P, box b) * such that the last one ends has the smallest LL.y and its LL.y is above * the bottom of the rank (rank.ht1). * + * For flat edges, we assume endp->sidemask has been set. For regular + * edges, we set this, but it doesn't appear to be needed any more. + * + * In many cases, we tweak the x or y coordinate of P->start.p by 1. + * This is because of a problem in the path routing code. If the starting + * point actually lies on the polygon, in some cases, the router gets + * confused and routes the path outside the polygon. So, the offset ensures + * the starting point is in the polygon. + * * FIX: Creating the initial boxes only really works for rankdir=TB and * rankdir=LR. For the others, we rely on compassPort flipping the side * and then assume that the node shape has top-bottom symmetry. Since we @@ -404,27 +413,13 @@ beginpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) if ((et == REGULAREDGE) && (ND_node_type(n) == NORMAL) && ((side = ED_tail_port(e).side))) { edge_t* orig; box b0, b = endp->nb; - switch (side) { - case LEFT: - b.UR.x = P->start.p.x; - b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2; - b.UR.y = P->start.p.y; - endp->boxes[0] = b; - endp->boxn = 1; - break; - case RIGHT: - b.LL.x = P->start.p.x; - b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2; - b.UR.y = P->start.p.y; - endp->boxes[0] = b; - endp->boxn = 1; - break; - case TOP: - if (ND_coord_i(e->head).x < 2*ND_coord_i(n).x - endp->np.x) { + if (side & TOP) { + endp->sidemask = TOP; + if (P->start.p.x < ND_coord_i(n).x) { /* go left */ b0.LL.x = b.LL.x - 1; /* b0.LL.y = ND_coord_i(n).y + ND_ht_i(n)/2; */ b0.LL.y = P->start.p.y; - b0.UR.x = P->start.p.x; + b0.UR.x = b.UR.x; b0.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2 + GD_ranksep(n->graph)/2; b.UR.x = ND_coord_i(n).x - ND_lw_i(n) - 2; b.UR.y = b0.LL.y; @@ -434,7 +429,7 @@ beginpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) endp->boxes[1] = b; } else { - b0.LL.x = P->start.p.x; + b0.LL.x = b.LL.x; b0.LL.y = P->start.p.y; /* b0.LL.y = ND_coord_i(n).y + ND_ht_i(n)/2; */ b0.UR.x = b.UR.x+1; @@ -446,27 +441,70 @@ beginpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) endp->boxes[0] = b0; endp->boxes[1] = b; } + P->start.p.y += 1; endp->boxn = 2; - break; - case BOTTOM: + } + else if (side & BOTTOM) { + endp->sidemask = BOTTOM; b.UR.y = MAX(b.UR.y,P->start.p.y); endp->boxes[0] = b; endp->boxn = 1; - break; + P->start.p.y -= 1; + } + else if (side & LEFT) { + endp->sidemask = LEFT; + b.UR.x = P->start.p.x; + b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2; + b.UR.y = P->start.p.y; + endp->boxes[0] = b; + endp->boxn = 1; + P->start.p.x -= 1; + } + else { + endp->sidemask = RIGHT; + b.LL.x = P->start.p.x; + b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2; + b.UR.y = P->start.p.y; + endp->boxes[0] = b; + endp->boxn = 1; + P->start.p.x += 1; } for (orig = e; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig)); if (n == orig->tail) ED_tail_port(orig).clip = FALSE; else ED_head_port(orig).clip = FALSE; - endp->sidemask = side; return; } if ((et == FLATEDGE) && ((side = ED_tail_port(e).side))) { box b0, b = endp->nb; edge_t* orig; - switch (side) { - case LEFT: + if (side & TOP) { + b.LL.y = MIN(b.LL.y,P->end.p.y); + endp->boxes[0] = b; + endp->boxn = 1; + } + else if (side & BOTTOM) { + if (endp->sidemask == TOP) { + b0.UR.y = ND_coord_i(n).y - ND_ht_i(n)/2; + b0.UR.x = b.UR.x+1; + b0.LL.x = P->start.p.x; + b0.LL.y = b0.UR.y - GD_ranksep(n->graph)/2; + b.LL.x = ND_coord_i(n).x + ND_rw_i(n) + 2; + b.LL.y = b0.UR.y; + b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2; + b.UR.x += 1; + endp->boxes[0] = b0; + endp->boxes[1] = b; + endp->boxn = 2; + } + else { + b.UR.y = MAX(b.UR.y,P->start.p.y); + endp->boxes[0] = b; + endp->boxn = 1; + } + } + else if (side & LEFT) { b.UR.x = P->start.p.x+1; if (endp->sidemask == TOP) { b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2; @@ -478,8 +516,8 @@ beginpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) } endp->boxes[0] = b; endp->boxn = 1; - break; - case RIGHT: + } + else { b.LL.x = P->start.p.x; if (endp->sidemask == TOP) { b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2; @@ -491,32 +529,6 @@ beginpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) } endp->boxes[0] = b; endp->boxn = 1; - break; - case TOP: - b.LL.y = MIN(b.LL.y,P->end.p.y); - endp->boxes[0] = b; - endp->boxn = 1; - break; - case BOTTOM: - if (endp->sidemask == TOP) { - b0.UR.y = ND_coord_i(n).y - ND_ht_i(n)/2; - b0.UR.x = b.UR.x+1; - b0.LL.x = P->start.p.x; - b0.LL.y = b0.UR.y - GD_ranksep(n->graph)/2; - b.LL.x = ND_coord_i(n).x + ND_rw_i(n) + 2; - b.LL.y = b0.UR.y; - b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2; - b.UR.x += 1; - endp->boxes[0] = b0; - endp->boxes[1] = b; - endp->boxn = 2; - } - else { - b.UR.y = MAX(b.UR.y,P->start.p.y); - endp->boxes[0] = b; - endp->boxn = 1; - } - break; } for (orig = e; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig)); if (n == orig->tail) @@ -554,6 +566,7 @@ beginpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) case REGULAREDGE: endp->boxes[0].UR.y = P->start.p.y; endp->sidemask = BOTTOM; + P->start.p.y -= 1; break; } } @@ -588,27 +601,20 @@ void endpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) if ((et == REGULAREDGE) && (ND_node_type(n) == NORMAL) && ((side = ED_head_port(e).side))) { edge_t* orig; box b0, b = endp->nb; - switch (side) { - case LEFT: - b.UR.x = P->end.p.x; - b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2; - b.LL.y = P->end.p.y; - endp->boxes[0] = b; - endp->boxn = 1; - break; - case RIGHT: - b.LL.x = P->end.p.x; - b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2; - b.LL.y = P->end.p.y; + if (side & TOP) { + endp->sidemask = TOP; + b.LL.y = MIN(b.LL.y,P->end.p.y); endp->boxes[0] = b; endp->boxn = 1; - break; - case BOTTOM: - if (ND_coord_i(e->tail).x < 2*ND_coord_i(n).x - endp->np.x) { + P->start.p.y += 1; + } + else if (side & BOTTOM) { + endp->sidemask = BOTTOM; + if (P->end.p.x < ND_coord_i(n).x) { /* go left */ b0.LL.x = b.LL.x-1; /* b0.UR.y = ND_coord_i(n).y - ND_ht_i(n)/2; */ b0.UR.y = P->end.p.y; - b0.UR.x = P->end.p.x; + b0.UR.x = b.UR.x; b0.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2 - GD_ranksep(n->graph)/2; b.UR.x = ND_coord_i(n).x - ND_lw_i(n) - 2; b.LL.y = b0.UR.y; @@ -618,7 +624,7 @@ void endpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) endp->boxes[1] = b; } else { - b0.LL.x = P->end.p.x; + b0.LL.x = b.LL.x; b0.UR.y = P->end.p.y; /* b0.UR.y = ND_coord_i(n).y - ND_ht_i(n)/2; */ b0.UR.x = b.UR.x+1; @@ -631,12 +637,25 @@ void endpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) endp->boxes[1] = b; } endp->boxn = 2; - break; - case TOP: - b.LL.y = MIN(b.LL.y,P->end.p.y); + P->end.p.y -= 1; + } + else if (side & LEFT) { + endp->sidemask = LEFT; + b.UR.x = P->end.p.x; + b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2; + b.LL.y = P->end.p.y; endp->boxes[0] = b; endp->boxn = 1; - break; + P->start.p.x -= 1; + } + else { + endp->sidemask = RIGHT; + b.LL.x = P->end.p.x; + b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2; + b.LL.y = P->end.p.y; + endp->boxes[0] = b; + endp->boxn = 1; + P->start.p.x -= 1; } for (orig = e; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig)); if (n == orig->head) @@ -737,6 +756,7 @@ void endpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge) case REGULAREDGE: endp->boxes[0].LL.y = P->end.p.y; endp->sidemask = TOP; + P->start.p.y += 1; break; } } diff --git a/lib/common/types.h b/lib/common/types.h index bd8d4566f..2062c47ff 100644 --- a/lib/common/types.h +++ b/lib/common/types.h @@ -78,7 +78,10 @@ extern "C" { boolean constrained; /* if true, constraints such as theta are set */ boolean clip; /* if true, clip end to node/port shape */ unsigned char order; /* for mincross */ - unsigned char side; /* if port is on perimeter of node */ + unsigned char side; /* if port is on perimeter of node, this + * contains the bitwise OR of the sides (TOP, + * BOTTOM, etc.) it is on. + */ } port; typedef struct {