]> granicus.if.org Git - graphviz/commitdiff
Tweak the new spline routing code to fix the problems seen in the
authorerg <devnull@localhost>
Fri, 15 Jul 2005 19:25:16 +0000 (19:25 +0000)
committererg <devnull@localhost>
Fri, 15 Jul 2005 19:25:16 +0000 (19:25 +0000)
examples given by Bill Zissimopoulos <graphviz-interest@billz.fastmail.fm>
(See mailing list posting on 14 July 2005.)

lib/common/shapes.c
lib/common/splines.c
lib/common/types.h

index cf775218c3bb8033a7be7a6478c21b75b27f79a2..e33f8a8137de27a2500968628e21b861f5c715ca 100644 (file)
@@ -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);
     }
 
index 92e03f84e73e803a61a75c00e7ce44c081f56b89..d21fe9e253fae632d2e96177e0f132008a2d9981 100644 (file)
@@ -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;
        }
     }
index bd8d4566f81348efad2e441438dd200def4e42aa..2062c47ff3c37ad80008a876eaaf28e9c0799ed7 100644 (file)
@@ -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 {