/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/common/splines.c

Go to the documentation of this file.
00001 /* $Id: splines.c,v 1.26 2008/03/03 23:01:51 ellson Exp $ $Revision: 1.26 $ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /**********************************************************
00005 *      This software is part of the graphviz package      *
00006 *                http://www.graphviz.org/                 *
00007 *                                                         *
00008 *            Copyright (c) 1994-2004 AT&T Corp.           *
00009 *                and is licensed under the                *
00010 *            Common Public License, Version 1.0           *
00011 *                      by AT&T Corp.                      *
00012 *                                                         *
00013 *        Information and Software Systems Research        *
00014 *              AT&T Research, Florham Park NJ             *
00015 **********************************************************/
00016 
00017 
00018 /* Functions related to creating a spline and attaching it to
00019  * an edge, starting from a list of control points.
00020  */
00021 
00022 #include "render.h"
00023 
00024 #ifdef DEBUG
00025 static int debugleveln(edge_t* e, int i)
00026 {
00027     return (GD_showboxes(e->head->graph) == i ||
00028             GD_showboxes(e->tail->graph) == i ||
00029             ED_showboxes(e) == i ||
00030             ND_showboxes(e->head) == i ||
00031             ND_showboxes(e->tail) == i);
00032 }
00033 
00034 static void showPoints(point ps[], int pn)
00035 {
00036     char buf[BUFSIZ];
00037     int newcnt = Show_cnt + pn + 3;
00038     int bi, li;
00039 
00040     Show_boxes = ALLOC(newcnt+2,Show_boxes,char*);
00041     li = Show_cnt+1;
00042     Show_boxes[li++] = strdup ("%% self list");
00043     Show_boxes[li++] = strdup ("dbgstart");
00044     for (bi = 0; bi < pn; bi++) {
00045         sprintf(buf, "%d %d point", ps[bi].x, ps[bi].y);
00046         Show_boxes[li++] = strdup (buf);
00047     }
00048     Show_boxes[li++] = strdup ("grestore");
00049 
00050     Show_cnt = newcnt;
00051     Show_boxes[Show_cnt+1] = NULL;
00052 }
00053 #endif
00054 
00055 /* arrow_clip:
00056  * Clip arrow to node boundary.
00057  * The real work is done elsewhere. Here we get the real edge,
00058  * check that the edge has arrowheads, and that an endpoint
00059  * isn't a merge point where several parts of an edge meet.
00060  * (e.g., with edge concentrators).
00061  */
00062 static void
00063 arrow_clip(edge_t * fe, node_t * hn,
00064            point * ps, int *startp, int *endp,
00065            bezier * spl, splineInfo * info)
00066 {
00067     edge_t *e;
00068     int i, j, sflag, eflag;
00069 
00070     for (e = fe; ED_to_orig(e); e = ED_to_orig(e));
00071 
00072     j = info->swapEnds(e);
00073     arrow_flags(e, &sflag, &eflag);
00074     if (info->splineMerge(hn))
00075         eflag = ARR_NONE;
00076     if (info->splineMerge(fe->tail))
00077         sflag = ARR_NONE;
00078     if (j) {
00079         i = sflag;
00080         sflag = eflag;
00081         eflag = i;
00082     }
00083     /* swap the two ends */
00084     if (sflag)
00085         *startp =
00086             arrowStartClip(e, ps, *startp, *endp, spl, sflag);
00087     if (eflag)
00088         *endp =
00089             arrowEndClip(e, ps, *startp, *endp, spl, eflag);
00090 }
00091 
00092 /* bezier_clip
00093  * Clip bezier to shape using binary search.
00094  * The details of the shape are passed in the inside_context;
00095  * The function providing the inside test is passed as a parameter.
00096  * left_inside specifies that sp[0] is inside the node, 
00097  * else sp[3] is taken as inside.
00098  * The points p are in node coordinates.
00099  */
00100 void bezier_clip(inside_t * inside_context,
00101                  boolean(*inside) (inside_t * inside_context, pointf p),
00102                  pointf * sp, boolean left_inside)
00103 {
00104     pointf seg[4], best[4], pt, opt, *left, *right;
00105     double low, high, t, *idir, *odir;
00106     boolean found;
00107     int i;
00108 
00109     if (left_inside) {
00110         left = NULL;
00111         right = seg;
00112         pt = sp[0];
00113         idir = &low;
00114         odir = &high;
00115     } else {
00116         left = seg;
00117         right = NULL;
00118         pt = sp[3];
00119         idir = &high;
00120         odir = &low;
00121     }
00122     found = FALSE;
00123     low = 0.0;
00124     high = 1.0;
00125     do {
00126         opt = pt;
00127         t = (high + low) / 2.0;
00128         pt = Bezier(sp, 3, t, left, right);
00129         if (inside(inside_context, pt)) {
00130             *idir = t;
00131         } else {
00132             for (i = 0; i < 4; i++)
00133                 best[i] = seg[i];
00134             found = TRUE;
00135             *odir = t;
00136         }
00137     } while (ABS(opt.x - pt.x) > .5 || ABS(opt.y - pt.y) > .5);
00138     if (found)
00139         for (i = 0; i < 4; i++)
00140             sp[i] = best[i];
00141     else
00142         for (i = 0; i < 4; i++)
00143             sp[i] = seg[i];
00144 }
00145 
00146 /* shape_clip0:
00147  * Clip Bezier to node shape using binary search.
00148  * left_inside specifies that curve[0] is inside the node, else
00149  * curve[3] is taken as inside.
00150  * Assumes ND_shape(n) and ND_shape(n)->fns->insidefn are non-NULL.
00151  * See note on shape_clip.
00152  */
00153 static void
00154 shape_clip0(inside_t * inside_context, node_t * n, point curve[4],
00155             boolean left_inside)
00156 {
00157     int i, save_real_size;
00158     pointf c[4];
00159 
00160     save_real_size = ND_rw_i(n);
00161     for (i = 0; i < 4; i++) {
00162         c[i].x = curve[i].x - ND_coord_i(n).x;
00163         c[i].y = curve[i].y - ND_coord_i(n).y;
00164     }
00165 
00166     bezier_clip(inside_context, ND_shape(n)->fns->insidefn, c,
00167                 left_inside);
00168 
00169     for (i = 0; i < 4; i++) {
00170         curve[i].x = ROUND(c[i].x + ND_coord_i(n).x);
00171         curve[i].y = ROUND(c[i].y + ND_coord_i(n).y);
00172     }
00173     ND_rw_i(n) = save_real_size;
00174 }
00175 
00176 /* shape_clip:
00177  * Clip Bezier to node shape
00178  * Uses curve[0] to determine which which side is inside the node. 
00179  * NOTE: This test is bad. It is possible for previous call to
00180  * shape_clip to produce a Bezier with curve[0] moved to the boundary
00181  * for which insidefn(curve[0]) is true. Thus, if the new Bezier is
00182  * fed back to shape_clip, it will again assume left_inside is true.
00183  * To be safe, shape_clip0 should guarantee that the computed boundary
00184  * point fails insidefn.
00185  * The edge e is used to provide a port box. If NULL, the spline is
00186  * clipped to the node shape.
00187  */
00188 void shape_clip(node_t * n, point curve[4])
00189 {
00190     int save_real_size;
00191     boolean left_inside;
00192     pointf c;
00193     inside_t inside_context;
00194 
00195     if (ND_shape(n) == NULL || ND_shape(n)->fns->insidefn == NULL)
00196         return;
00197 
00198     inside_context.s.n = n;
00199     inside_context.s.bp = NULL;
00200     save_real_size = ND_rw_i(n);
00201     c.x = curve[0].x - ND_coord_i(n).x;
00202     c.y = curve[0].y - ND_coord_i(n).y;
00203     left_inside = ND_shape(n)->fns->insidefn(&inside_context, c);
00204     ND_rw_i(n) = save_real_size;
00205     shape_clip0(&inside_context, n, curve, left_inside);
00206 }
00207 
00208 /* new_spline:
00209  * Create and attach a new bezier of size sz to the edge d
00210  */
00211 bezier *new_spline(edge_t * e, int sz)
00212 {
00213     bezier *rv;
00214 
00215     while (ED_edge_type(e) != NORMAL)
00216         e = ED_to_orig(e);
00217     if (ED_spl(e) == NULL)
00218         ED_spl(e) = NEW(splines);
00219     ED_spl(e)->list = ALLOC(ED_spl(e)->size + 1, ED_spl(e)->list, bezier);
00220     rv = &(ED_spl(e)->list[ED_spl(e)->size++]);
00221     rv->list = N_NEW(sz, point);
00222     rv->size = sz;
00223     rv->sflag = rv->eflag = FALSE;
00224     return rv;
00225 }
00226 
00227 /* update_bb:
00228  * Update the bounding box of g based on the addition of
00229  * point p.
00230  */
00231 void update_bb(graph_t * g, point pt)
00232 {
00233     if (pt.x > GD_bb(g).UR.x)
00234         GD_bb(g).UR.x = pt.x;
00235     if (pt.y > GD_bb(g).UR.y)
00236         GD_bb(g).UR.y = pt.y;
00237     if (pt.x < GD_bb(g).LL.x)
00238         GD_bb(g).LL.x = pt.x;
00239     if (pt.y < GD_bb(g).LL.y)
00240         GD_bb(g).LL.y = pt.y;
00241 }
00242 
00243 /* clip_and_install:
00244  * Given a raw spline (pn control points in ps), representing
00245  * a path from edge fe->tail ending in node hn, clip the ends to
00246  * the node boundaries and attach the resulting spline to the
00247  * edge.
00248  */
00249 void
00250 clip_and_install(edge_t * fe, node_t * hn, point * ps, int pn,
00251                  splineInfo * info)
00252 {
00253     pointf p2;
00254     bezier *newspl;
00255     node_t *tn;
00256     int start, end, i, clipTail, clipHead;
00257     graph_t *g;
00258     edge_t *orig;
00259     box* tbox;
00260     box* hbox;
00261     inside_t inside_context;
00262 
00263     tn = fe->tail;
00264     g = tn->graph;
00265     newspl = new_spline(fe, pn);
00266 
00267     for (orig = fe; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig));
00268 
00269     /* may be a reversed flat edge */
00270     if ((tn->u.rank == hn->u.rank) && (tn->u.order > hn->u.order)) {
00271         node_t *tmp;
00272         tmp = hn;
00273         hn = tn;
00274         tn = tmp;
00275     }
00276     if (tn == orig->tail) {
00277         clipTail = ED_tail_port(orig).clip;
00278         clipHead = ED_head_port(orig).clip;
00279         tbox = ED_tail_port(orig).bp;
00280         hbox = ED_head_port(orig).bp;
00281     }
00282     else { /* fe and orig are reversed */
00283         clipTail = ED_head_port(orig).clip;
00284         clipHead = ED_tail_port(orig).clip;
00285         hbox = ED_tail_port(orig).bp;
00286         tbox = ED_head_port(orig).bp;
00287     }
00288 
00289     /* spline may be interior to node */
00290     if(clipTail && ND_shape(tn) && ND_shape(tn)->fns->insidefn) {
00291         inside_context.s.n = tn;
00292         inside_context.s.bp = tbox;
00293         for (start = 0; start < pn - 4; start += 3) {
00294             p2.x = ps[start + 3].x - ND_coord_i(tn).x;
00295             p2.y = ps[start + 3].y - ND_coord_i(tn).y;
00296             if (ND_shape(tn)->fns->insidefn(&inside_context, p2) == FALSE)
00297                 break;
00298         }
00299         shape_clip0(&inside_context, tn, &ps[start], TRUE);
00300     } else
00301         start = 0;
00302     if(clipHead && ND_shape(hn) && ND_shape(hn)->fns->insidefn) {
00303         inside_context.s.n = hn;
00304         inside_context.s.bp = hbox;
00305         for (end = pn - 4; end > 0; end -= 3) {
00306             p2.x = ps[end].x - ND_coord_i(hn).x;
00307             p2.y = ps[end].y - ND_coord_i(hn).y;
00308             if (ND_shape(hn)->fns->insidefn(&inside_context, p2) == FALSE)
00309                 break;
00310         }
00311         shape_clip0(&inside_context, hn, &ps[end], FALSE);
00312     } else
00313         end = pn - 4;
00314     for (; start < pn - 4; start += 3)
00315         if (ps[start].x != ps[start + 3].x
00316             || ps[start].y != ps[start + 3].y)
00317             break;
00318     for (; end > 0; end -= 3)
00319         if (ps[end].x != ps[end + 3].x || ps[end].y != ps[end + 3].y)
00320             break;
00321     arrow_clip(fe, hn, ps, &start, &end, newspl, info);
00322     for (i = start; i < end + 4; i++) {
00323         point pt;
00324         pt = newspl->list[i - start] = ps[i];
00325         update_bb(g, pt);
00326     }
00327     newspl->size = end - start + 4;
00328 }
00329 
00330 static double 
00331 conc_slope(node_t* n)
00332 {
00333     double s_in, s_out, m_in, m_out;
00334     int cnt_in, cnt_out;
00335     pointf p;
00336     edge_t *e;
00337 
00338     s_in = s_out = 0.0;
00339     for (cnt_in = 0; (e = ND_in(n).list[cnt_in]); cnt_in++)
00340         s_in += ND_coord_i(e->tail).x;
00341     for (cnt_out = 0; (e = ND_out(n).list[cnt_out]); cnt_out++)
00342         s_out += ND_coord_i(e->head).x;
00343     p.x = ND_coord_i(n).x - (s_in / cnt_in);
00344     p.y = ND_coord_i(n).y - ND_coord_i(ND_in(n).list[0]->tail).y;
00345     m_in = atan2(p.y, p.x);
00346     p.x = (s_out / cnt_out) - ND_coord_i(n).x;
00347     p.y = ND_coord_i(ND_out(n).list[0]->head).y - ND_coord_i(n).y;
00348     m_out = atan2(p.y, p.x);
00349     return ((m_in + m_out) / 2.0);
00350 }
00351 
00352 void add_box(path * P, box b)
00353 {
00354     if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
00355         P->boxes[P->nbox++] = b;
00356 }
00357 
00358 /* beginpath:
00359  * Set up boxes near the tail node.
00360  * For regular nodes, the result should be a list of continguous rectangles 
00361  * such that the last one ends has the smallest LL.y and its LL.y is above
00362  * the bottom of the rank (rank.ht1).
00363  * 
00364  * For flat edges, we assume endp->sidemask has been set. For regular
00365  * edges, we set this, but it doesn't appear to be needed any more.
00366  * 
00367  * In many cases, we tweak the x or y coordinate of P->start.p by 1.
00368  * This is because of a problem in the path routing code. If the starting
00369  * point actually lies on the polygon, in some cases, the router gets
00370  * confused and routes the path outside the polygon. So, the offset ensures
00371  * the starting point is in the polygon.
00372  *
00373  * FIX: Creating the initial boxes only really works for rankdir=TB and
00374  * rankdir=LR. For the others, we rely on compassPort flipping the side
00375  * and then assume that the node shape has top-bottom symmetry. Since we
00376  * at present only put compass points on the bounding box, this works.
00377  * If we attempt to implement compass points on actual node perimeters,
00378  * something major will probably be necessary. Doing the coordinate
00379  * flip (postprocess) before spline routing will be too disruptive. The
00380  * correct solution is probably to have beginpath/endpath create the
00381  * boxes assuming an inverted node. Note that compassPort already does
00382  * some flipping. Even better would be to allow the *_path function
00383  * to provide a polygon.
00384  *
00385  * The extra space provided by FUDGE-2 prevents the edge from getting
00386  * too close the side of the node.
00387  */
00388 #define FUDGE 2
00389 
00390 void
00391 beginpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge)
00392 {
00393     int side, mask;
00394     node_t *n;
00395     int (*pboxfn) (node_t*, port*, int, box*, int*);
00396 
00397     n = e->tail;
00398 
00399     if (ND_shape(n))
00400         pboxfn = ND_shape(n)->fns->pboxfn;
00401     else
00402         pboxfn = NULL;
00403     P->start.p = add_points(ND_coord_i(n), ED_tail_port(e).p);
00404     P->ulpp = P->urpp = P->llpp = P->lrpp = NULL;
00405     if (merge) {
00406         /*P->start.theta = - M_PI / 2; */
00407         P->start.theta = conc_slope(e->tail);
00408         P->start.constrained = TRUE;
00409     } else {
00410         if (ED_tail_port(e).constrained) {
00411             P->start.theta = ED_tail_port(e).theta;
00412             P->start.constrained = TRUE;
00413         } else
00414             P->start.constrained = FALSE;
00415     }
00416     P->nbox = 0;
00417     P->data = (void *) e;
00418     endp->np = P->start.p;
00419     if ((et == REGULAREDGE) && (ND_node_type(n) == NORMAL) && ((side = ED_tail_port(e).side))) {
00420         edge_t* orig;
00421         box b0, b = endp->nb;
00422         if (side & TOP) {
00423             endp->sidemask = TOP;
00424             if (P->start.p.x < ND_coord_i(n).x) { /* go left */
00425                 b0.LL.x = b.LL.x - 1;
00426                 /* b0.LL.y = ND_coord_i(n).y + ND_ht_i(n)/2; */
00427                 b0.LL.y = P->start.p.y;
00428                 b0.UR.x = b.UR.x;
00429                 b0.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2 + GD_ranksep(n->graph)/2;
00430                 b.UR.x = ND_coord_i(n).x - ND_lw_i(n) - (FUDGE-2);
00431                 b.UR.y = b0.LL.y;
00432                 b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2;
00433                 b.LL.x -= 1;
00434                 endp->boxes[0] = b0;
00435                 endp->boxes[1] = b;
00436             }
00437             else {
00438                 b0.LL.x = b.LL.x;
00439                 b0.LL.y = P->start.p.y;
00440                 /* b0.LL.y = ND_coord_i(n).y + ND_ht_i(n)/2; */
00441                 b0.UR.x = b.UR.x+1;
00442                 b0.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2 + GD_ranksep(n->graph)/2;
00443                 b.LL.x = ND_coord_i(n).x + ND_rw_i(n) + (FUDGE-2);
00444                 b.UR.y = b0.LL.y;
00445                 b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2;
00446                 b.UR.x += 1;
00447                 endp->boxes[0] = b0;
00448                 endp->boxes[1] = b;
00449             } 
00450             P->start.p.y += 1;
00451             endp->boxn = 2;
00452         }
00453         else if (side & BOTTOM) {
00454             endp->sidemask = BOTTOM;
00455             b.UR.y = MAX(b.UR.y,P->start.p.y);
00456             endp->boxes[0] = b;
00457             endp->boxn = 1;
00458             P->start.p.y -= 1;
00459         }
00460         else if (side & LEFT) {
00461             endp->sidemask = LEFT;
00462             b.UR.x = P->start.p.x;
00463             b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2;
00464             b.UR.y = P->start.p.y;
00465             endp->boxes[0] = b;
00466             endp->boxn = 1;
00467             P->start.p.x -= 1;
00468         }
00469         else {
00470             endp->sidemask = RIGHT;
00471             b.LL.x = P->start.p.x;
00472             b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2;
00473             b.UR.y = P->start.p.y;
00474             endp->boxes[0] = b;
00475             endp->boxn = 1;
00476             P->start.p.x += 1;
00477         }
00478         for (orig = e; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig));
00479         if (n == orig->tail)
00480             ED_tail_port(orig).clip = FALSE;
00481         else
00482             ED_head_port(orig).clip = FALSE;
00483         return;
00484     }
00485     if ((et == FLATEDGE) && ((side = ED_tail_port(e).side))) {
00486         box b0, b = endp->nb;
00487         edge_t* orig;
00488         if (side & TOP) {
00489             b.LL.y = MIN(b.LL.y,P->end.p.y);
00490             endp->boxes[0] = b;
00491             endp->boxn = 1;
00492         }
00493         else if (side & BOTTOM) {
00494             if (endp->sidemask == TOP) {
00495                 b0.UR.y = ND_coord_i(n).y - ND_ht_i(n)/2;
00496                 b0.UR.x = b.UR.x+1;
00497                 b0.LL.x = P->start.p.x;
00498                 b0.LL.y = b0.UR.y - GD_ranksep(n->graph)/2;
00499                 b.LL.x = ND_coord_i(n).x + ND_rw_i(n) + (FUDGE-2);
00500                 b.LL.y = b0.UR.y;
00501                 b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2;
00502                 b.UR.x += 1;
00503                 endp->boxes[0] = b0;
00504                 endp->boxes[1] = b;
00505                 endp->boxn = 2;
00506             }
00507             else {
00508                 b.UR.y = MAX(b.UR.y,P->start.p.y);
00509                 endp->boxes[0] = b;
00510                 endp->boxn = 1;
00511             }
00512         }
00513         else if (side & LEFT) {
00514             b.UR.x = P->start.p.x+1;
00515             if (endp->sidemask == TOP) {
00516                 b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2;
00517                 b.LL.y = P->start.p.y-1;
00518             }
00519             else {
00520                 b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2;
00521                 b.UR.y = P->start.p.y+1;
00522             }
00523             endp->boxes[0] = b;
00524             endp->boxn = 1;
00525         }
00526         else {
00527             b.LL.x = P->start.p.x;
00528             if (endp->sidemask == TOP) {
00529                 b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2;
00530                 b.LL.y = P->start.p.y;
00531             }
00532             else {
00533                 b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2;
00534                 b.UR.y = P->start.p.y+1;
00535             }
00536             endp->boxes[0] = b;
00537             endp->boxn = 1;
00538         }
00539         for (orig = e; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig));
00540         if (n == orig->tail)
00541             ED_tail_port(orig).clip = FALSE;
00542         else
00543             ED_head_port(orig).clip = FALSE;
00544         endp->sidemask = side;
00545         return;
00546     }
00547 
00548     if (et == REGULAREDGE) side = BOTTOM;
00549     else side = endp->sidemask;  /* for flat edges */
00550     if (pboxfn
00551         && (mask = (*pboxfn) (n, &ED_tail_port(e), side, &endp->boxes[0], &endp->boxn)))
00552         endp->sidemask = mask;
00553     else {
00554         endp->boxes[0] = endp->nb;
00555         endp->boxn = 1;
00556 
00557         switch (et) {
00558         case SELFEDGE:
00559         /* moving the box UR.y by + 1 avoids colinearity between
00560            port point and box that confuses Proutespline().  it's
00561            a bug in Proutespline() but this is the easiest fix. */
00562             assert(0);  /* at present, we don't use beginpath for selfedges */
00563             endp->boxes[0].UR.y = P->start.p.y - 1;
00564             endp->sidemask = BOTTOM;
00565             break;
00566         case FLATEDGE:
00567             if (endp->sidemask == TOP)
00568                 endp->boxes[0].LL.y = P->start.p.y;
00569             else
00570                 endp->boxes[0].UR.y = P->start.p.y;
00571             break;
00572         case REGULAREDGE:
00573             endp->boxes[0].UR.y = P->start.p.y;
00574             endp->sidemask = BOTTOM;
00575             P->start.p.y -= 1;
00576             break;
00577         }    
00578     }    
00579 }
00580 
00581 void endpath(path * P, edge_t * e, int et, pathend_t * endp, boolean merge)
00582 {
00583     int side, mask;
00584     node_t *n;
00585     int (*pboxfn) (node_t* n, port*, int, box*, int*);
00586 
00587     n = e->head;
00588 
00589     if (ND_shape(n))
00590         pboxfn = ND_shape(n)->fns->pboxfn;
00591     else
00592         pboxfn = NULL;
00593     P->end.p = add_points(ND_coord_i(n), ED_head_port(e).p);
00594     if (merge) {
00595         /*P->end.theta = M_PI / 2; */
00596         P->end.theta = conc_slope(e->head) + M_PI;
00597         assert(P->end.theta < 2 * M_PI);
00598         P->end.constrained = TRUE;
00599     } else {
00600         if (ED_head_port(e).constrained) {
00601             P->end.theta = ED_head_port(e).theta;
00602             P->end.constrained = TRUE;
00603         } else
00604             P->end.constrained = FALSE;
00605     }
00606     endp->np = P->end.p;
00607     if ((et == REGULAREDGE) && (ND_node_type(n) == NORMAL) && ((side = ED_head_port(e).side))) {
00608         edge_t* orig;
00609         box b0, b = endp->nb;
00610         if (side & TOP) {
00611             endp->sidemask = TOP;
00612             b.LL.y = MIN(b.LL.y,P->end.p.y);
00613             endp->boxes[0] = b;
00614             endp->boxn = 1;
00615             P->start.p.y += 1;
00616         }
00617         else if (side & BOTTOM) {
00618             endp->sidemask = BOTTOM;
00619             if (P->end.p.x < ND_coord_i(n).x) { /* go left */
00620                 b0.LL.x = b.LL.x-1;
00621                 /* b0.UR.y = ND_coord_i(n).y - ND_ht_i(n)/2; */
00622                 b0.UR.y = P->end.p.y;
00623                 b0.UR.x = b.UR.x;
00624                 b0.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2 - GD_ranksep(n->graph)/2;
00625                 b.UR.x = ND_coord_i(n).x - ND_lw_i(n) - (FUDGE-2);
00626                 b.LL.y = b0.UR.y;
00627                 b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2;
00628                 b.LL.x -= 1;
00629                 endp->boxes[0] = b0;
00630                 endp->boxes[1] = b;
00631             }
00632             else {
00633                 b0.LL.x = b.LL.x;
00634                 b0.UR.y = P->end.p.y;
00635                 /* b0.UR.y = ND_coord_i(n).y - ND_ht_i(n)/2; */
00636                 b0.UR.x = b.UR.x+1;
00637                 b0.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2 - GD_ranksep(n->graph)/2;
00638                 b.LL.x = ND_coord_i(n).x + ND_rw_i(n) + (FUDGE-2);
00639                 b.LL.y = b0.UR.y;
00640                 b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2;
00641                 b.UR.x += 1;
00642                 endp->boxes[0] = b0;
00643                 endp->boxes[1] = b;
00644             } 
00645             endp->boxn = 2;
00646             P->end.p.y -= 1;
00647         }
00648         else if (side & LEFT) {
00649             endp->sidemask = LEFT;
00650             b.UR.x = P->end.p.x;
00651             b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2;
00652             b.LL.y = P->end.p.y;
00653             endp->boxes[0] = b;
00654             endp->boxn = 1;
00655             P->start.p.x -= 1;
00656         }
00657         else {
00658             endp->sidemask = RIGHT;
00659             b.LL.x = P->end.p.x;
00660             b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2;
00661             b.LL.y = P->end.p.y;
00662             endp->boxes[0] = b;
00663             endp->boxn = 1;
00664             P->start.p.x -= 1;
00665         }
00666         for (orig = e; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig));
00667         if (n == orig->head)
00668             ED_head_port(orig).clip = FALSE;
00669         else
00670             ED_tail_port(orig).clip = FALSE;
00671         endp->sidemask = side;
00672         return;
00673     }
00674 
00675     if ((et == FLATEDGE) && ((side = ED_head_port(e).side))) {
00676         edge_t* orig;
00677         box b0, b = endp->nb;
00678         switch (side) {
00679         case LEFT:
00680             b.UR.x = P->end.p.x;
00681             if (endp->sidemask == TOP) {
00682                 b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2;
00683                 b.LL.y = P->end.p.y;
00684             }
00685             else {
00686                 b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2;
00687                 b.UR.y = P->end.p.y;
00688             }
00689             endp->boxes[0] = b;
00690             endp->boxn = 1;
00691             break;
00692         case RIGHT:
00693             b.LL.x = P->end.p.x-1;
00694             if (endp->sidemask == TOP) {
00695                 b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2;
00696                 b.LL.y = P->end.p.y-1;
00697             }
00698             else {
00699                 b.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2;
00700                 b.UR.y = P->end.p.y;
00701             }
00702             endp->boxes[0] = b;
00703             endp->boxn = 1;
00704             break;
00705         case TOP:
00706             b.LL.y = MIN(b.LL.y,P->end.p.y);
00707             endp->boxes[0] = b;
00708             endp->boxn = 1;
00709             break;
00710         case BOTTOM:
00711             if (endp->sidemask == TOP) {
00712                 b0.LL.x = b.LL.x-1;
00713                 b0.UR.y = ND_coord_i(n).y - ND_ht_i(n)/2;
00714                 b0.UR.x = P->end.p.x;
00715                 b0.LL.y = b0.UR.y - GD_ranksep(n->graph)/2;
00716                 b.UR.x = ND_coord_i(n).x - ND_lw_i(n) - 2;
00717                 b.LL.y = b0.UR.y;
00718                 b.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2;
00719                 b.LL.x -= 1;
00720                 endp->boxes[0] = b0;
00721                 endp->boxes[1] = b;
00722                 endp->boxn = 2;
00723             }
00724             else {
00725                 b.UR.y = MAX(b.UR.y,P->start.p.y);
00726                 endp->boxes[0] = b;
00727                 endp->boxn = 1;
00728             }
00729             break;
00730         }
00731         for (orig = e; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig));
00732         if (n == orig->head)
00733             ED_head_port(orig).clip = FALSE;
00734         else
00735             ED_tail_port(orig).clip = FALSE;
00736         endp->sidemask = side;
00737         return;
00738     }
00739 
00740     if (et == REGULAREDGE) side = TOP;
00741     else side = endp->sidemask;  /* for flat edges */
00742     if (pboxfn
00743         && (mask = (*pboxfn) (n, &ED_head_port(e), side, &endp->boxes[0], &endp->boxn)))
00744         endp->sidemask = mask;
00745     else {
00746         endp->boxes[0] = endp->nb;
00747         endp->boxn = 1;
00748         switch (et) {
00749         case SELFEDGE:
00750             /* offset of -1 is symmetric w.r.t. beginpath() 
00751              * FIXME: is any of this right?  what if self-edge 
00752              * doesn't connect from BOTTOM to TOP??? */
00753             assert(0);  /* at present, we don't use endpath for selfedges */
00754             endp->boxes[0].LL.y = P->end.p.y + 1;
00755             endp->sidemask = TOP;
00756             break;
00757         case FLATEDGE:
00758             if (endp->sidemask == TOP)
00759                 endp->boxes[0].LL.y = P->start.p.y;
00760             else
00761                 endp->boxes[0].UR.y = P->start.p.y;
00762             break;
00763         case REGULAREDGE:
00764             endp->boxes[0].LL.y = P->end.p.y;
00765             endp->sidemask = TOP;
00766             P->start.p.y += 1;
00767             break;
00768         }
00769     }
00770 }
00771 
00772 #ifdef OLD
00773 /* self edges */
00774 #define ANYW  0                 /* could go either way */
00775 
00776 static int selfsidemap[16][3] = {
00777     {BOTTOM, BOTTOM, ANYW},
00778     {TOP, TOP, ANYW},
00779     {RIGHT, RIGHT, ANYW},
00780     {LEFT, LEFT, ANYW},
00781     {BOTTOM, LEFT, CCW},
00782     {LEFT, BOTTOM, CW},
00783     {TOP, RIGHT, CW},
00784     {RIGHT, TOP, CCW},
00785     {TOP, LEFT, CCW},
00786     {LEFT, TOP, CW},
00787     {BOTTOM, RIGHT, CCW},
00788     {RIGHT, BOTTOM, CW},
00789     {BOTTOM, TOP, CCW},
00790     {TOP, BOTTOM, CW},
00791     {LEFT, RIGHT, CCW},
00792     {RIGHT, LEFT, CW},
00793 };
00794 
00795 static void
00796 chooseselfsides(pathend_t * tendp, pathend_t * hendp,
00797                 int *tsidep, int *hsidep, int *dirp)
00798 {
00799     int i;
00800 
00801     for (i = 0; i < 16; i++)
00802         if ((selfsidemap[i][0] & tendp->sidemask) &&
00803             (selfsidemap[i][1] & hendp->sidemask))
00804             break;
00805     if (i == 16)
00806         abort();
00807     *tsidep = selfsidemap[i][0], *hsidep = selfsidemap[i][1];
00808     *dirp = selfsidemap[i][2];
00809     if (*dirp == ANYW) {        /* ANYW can appear when tside == hside */
00810         switch (*tsidep) {
00811         case BOTTOM:
00812             *dirp = (tendp->np.x < hendp->np.x) ? CCW : CW;
00813             break;
00814         case RIGHT:
00815             *dirp = (tendp->np.y < hendp->np.y) ? CCW : CW;
00816             break;
00817         case TOP:
00818             *dirp = (tendp->np.x > hendp->np.x) ? CCW : CW;
00819             break;
00820         case LEFT:
00821             *dirp = (tendp->np.y > hendp->np.y) ? CCW : CW;
00822             break;
00823         }
00824     }
00825 }
00826 
00827 static box makeselfend(box b, int side, int dir, int dx, int dy)
00828 {
00829     box eb = { {0, 0}, {0, 0} };
00830 
00831     switch (side) {
00832     case BOTTOM:
00833         eb = boxof(b.LL.x, b.LL.y - dy, b.UR.x, b.LL.y);
00834         (dir == CCW) ? (eb.UR.x += dx / 2) : (eb.LL.x -= dx / 2);
00835         break;
00836     case RIGHT:
00837         eb = boxof(b.UR.x, b.LL.y, b.UR.x + dx, b.UR.y);
00838         (dir == CCW) ? (eb.UR.y += dy / 2) : (eb.LL.y -= dy / 2);
00839         break;
00840     case TOP:
00841         eb = boxof(b.LL.x, b.UR.y, b.UR.x, b.UR.y + dy);
00842         (dir == CCW) ? (eb.LL.x -= dx / 2) : (eb.UR.x += dx / 2);
00843         break;
00844     case LEFT:
00845         eb = boxof(b.LL.x - dx, b.LL.y, b.LL.x, b.UR.y);
00846         (dir == CCW) ? (eb.LL.y -= dy / 2) : (eb.UR.y += dy / 2);
00847         break;
00848     }
00849     return eb;
00850 }
00851 
00852 static box
00853 makeselfcomponent(box nb, int side, int dx, int dy, int w, int h)
00854 {
00855     box b = { {0, 0}, {0, 0} };
00856 
00857     switch (side) {
00858     case BOTTOM:
00859         b.LL.x = nb.LL.x - dx - w, b.LL.y = nb.LL.y - dy - h;
00860         b.UR.x = nb.UR.x + dx + w, b.UR.y = b.LL.y + h;
00861         break;
00862     case RIGHT:
00863         b.LL.x = nb.UR.x + dx, b.LL.y = nb.LL.y - dy;
00864         b.UR.x = b.LL.x + w, b.UR.y = nb.UR.y + dy;
00865         break;
00866     case TOP:
00867         b.LL.x = nb.LL.x - dx - w, b.LL.y = nb.UR.y + dy;
00868         b.UR.x = nb.UR.x + dx + w, b.UR.y = b.LL.y + h;
00869         break;
00870     case LEFT:
00871         b.LL.x = nb.LL.x - dx - w, b.LL.y = nb.LL.y - dy;
00872         b.UR.x = b.LL.x + w, b.UR.y = nb.UR.y + dy;
00873         break;
00874     }
00875     return b;
00876 }
00877 
00878 static void
00879 adjustselfends(box * tbp, box * hbp, point p, int side, int dir)
00880 {
00881     switch (side) {
00882     case BOTTOM:
00883         if (dir == CCW) {
00884             tbp->LL.x -= (tbp->UR.x - p.x), tbp->UR.x = p.x;
00885             hbp->UR.x += (p.x - hbp->LL.x), hbp->LL.x = p.x;
00886         } else {
00887             tbp->UR.x -= (tbp->LL.x - p.x), tbp->LL.x = p.x;
00888             hbp->LL.x += (p.x - hbp->UR.x), hbp->UR.x = p.x;
00889         }
00890         break;
00891     case RIGHT:
00892         if (dir == CCW) {
00893             tbp->LL.y -= (tbp->UR.y - p.y), tbp->UR.y = p.y;
00894             hbp->UR.y += (p.y - hbp->LL.y), hbp->LL.y = p.y;
00895         } else {
00896             tbp->UR.y -= (tbp->LL.y - p.y), tbp->LL.y = p.y;
00897             hbp->LL.y += (p.y - hbp->UR.y), hbp->UR.y = p.y;
00898         }
00899         break;
00900     case TOP:
00901         if (dir == CW) {
00902             tbp->LL.x -= (tbp->UR.x - p.x), tbp->UR.x = p.x;
00903             hbp->UR.x += (p.x - hbp->LL.x), hbp->LL.x = p.x;
00904         } else {
00905             tbp->UR.x -= (tbp->LL.x - p.x), tbp->LL.x = p.x;
00906             hbp->LL.x += (p.x - hbp->UR.x), hbp->UR.x = p.x;
00907         }
00908         break;
00909     case LEFT:
00910         if (dir == CW) {
00911             tbp->LL.y -= (tbp->UR.y - p.y), tbp->UR.y = p.y;
00912             hbp->UR.y += (p.y - hbp->LL.y), hbp->LL.y = p.y;
00913         } else {
00914             tbp->UR.y -= (tbp->LL.y - p.y), tbp->LL.y = p.y;
00915             hbp->LL.y += (p.y - hbp->UR.y), hbp->UR.y = p.y;
00916         }
00917         break;
00918     }
00919 }
00920 
00921 static void
00922 completeselfpath(path * P, pathend_t * tendp, pathend_t * hendp,
00923                  int tside, int hside, int dir, int dx, int dy, int w,
00924                  int h)
00925 {
00926     int i, side;
00927     box boxes[4];               /* can't have more than 6 boxes */
00928     box tb, hb;
00929     int boxn;
00930 
00931     tb = makeselfend(tendp->boxes[tendp->boxn - 1], tside, dir, dx, dy);
00932     hb = makeselfend(hendp->boxes[hendp->boxn - 1],
00933                      hside, OTHERDIR(dir), dx, dy);
00934 
00935     if (tside == hside && tendp->np.x == hendp->np.x &&
00936         tendp->np.y == hendp->np.y)
00937         adjustselfends(&tb, &hb, tendp->np, tside, dir);
00938 
00939     boxn = 0;
00940     for (side = tside;; side = NEXTSIDE(side, dir)) {
00941         boxes[boxn++] = makeselfcomponent(tendp->nb, side, dx, dy, w, h);
00942         if (side == hside)
00943             break;
00944     }
00945     for (i = 0; i < tendp->boxn; i++)
00946         add_box(P, tendp->boxes[i]);
00947     add_box(P, tb);
00948     for (i = 0; i < boxn; i++)
00949         add_box(P, boxes[i]);
00950     add_box(P, hb);
00951     for (i = hendp->boxn - 1; i >= 0; i--)
00952         add_box(P, hendp->boxes[i]);
00953 }
00954 #endif
00955 
00956 static void
00957 selfBottom (edge_t* edges[], int ind, int cnt, int sizex, int stepy,
00958           splineInfo* sinfo) 
00959 {
00960     int hy, ty, sgn;
00961     point tp, hp;
00962     node_t *n;
00963     edge_t *e;
00964     int i, stepx, dx, dy;
00965     double width, height; 
00966     point points[1000];
00967     int pointn;
00968     point np;
00969 
00970     e = edges[ind];
00971     n = e->tail;
00972 
00973     stepx = (sizex / 2) / cnt;
00974     stepx = MAX(stepx,2);
00975     pointn = 0;
00976     np = ND_coord_i(n);
00977     tp = ED_tail_port(e).p;
00978     tp.x += np.x;
00979     tp.y += np.y;
00980     hp = ED_head_port(e).p;
00981     hp.x += np.x;
00982     hp.y += np.y;
00983     if (tp.x >= hp.x) sgn = 1;
00984     else sgn = -1;
00985     dy = ND_ht_i(n)/2, dx = 0;
00986     ty = MIN(dy, 3*(tp.y + dy - np.y));
00987     hy = MIN(dy, 3*(hp.y + dy - np.y));
00988     for (i = 0; i < cnt; i++) {
00989         e = edges[ind++];
00990         dy += stepy, ty += stepy, hy += stepy, dx += sgn*stepx;
00991         pointn = 0;
00992         points[pointn++] = tp;
00993         points[pointn++] = pointof(tp.x + dx, tp.y - ty / 3);
00994         points[pointn++] = pointof(tp.x + dx, np.y - dy);
00995         points[pointn++] = pointof((tp.x+hp.x)/2, np.y - dy);
00996         points[pointn++] = pointof(hp.x - dx, np.y - dy);
00997         points[pointn++] = pointof(hp.x - dx, hp.y - hy / 3);
00998         points[pointn++] = hp;
00999         if (ED_label(e)) {
01000         if (GD_flip(e->tail->graph)) {
01001             width = ED_label(e)->dimen.y;
01002             height = ED_label(e)->dimen.x;
01003         } else {
01004             width = ED_label(e)->dimen.x;
01005             height = ED_label(e)->dimen.y;
01006         }
01007         ED_label(e)->p.y = ND_coord_i(n).y - dy - height / 2.0;
01008         ED_label(e)->p.x = ND_coord_i(n).x;
01009         ED_label(e)->set = TRUE;
01010         if (height > stepy)
01011             dy += height - stepy;
01012         if (dx + stepx < width)
01013             dx += width - stepx;
01014         }
01015         clip_and_install(e, e->head, points, pointn, sinfo);
01016 #ifdef DEBUG
01017         if (debugleveln(e,1))
01018             showPoints (points, pointn);
01019 #endif
01020     }
01021 }
01022 
01023 
01024 static void
01025 selfTop (edge_t* edges[], int ind, int cnt, int sizex, int stepy,
01026            splineInfo* sinfo) 
01027 {
01028     int hy, ty, sgn;
01029     point tp, hp;
01030     node_t *n;
01031     edge_t *e;
01032     int i, stepx, dx, dy;
01033     double width, height; 
01034     point points[1000];
01035     int pointn;
01036     point np;
01037 
01038     e = edges[ind];
01039     n = e->tail;
01040 
01041     stepx = (sizex / 2) / cnt;
01042     stepx = MAX(stepx, 2);
01043     pointn = 0;
01044     np = ND_coord_i(n);
01045     tp  = ED_tail_port(e).p;
01046     tp.x += np.x;
01047     tp.y += np.y;
01048     hp  = ED_head_port(e).p;
01049     hp.x += np.x;
01050     hp.y += np.y;
01051     if (tp.x >= hp.x) sgn = 1;
01052     else sgn = -1;
01053     dy = ND_ht_i(n)/2, dx = 0;
01054     ty = MIN(dy, 3*(np.y + dy - tp.y));
01055     hy = MIN(dy, 3*(np.y + dy - hp.y));
01056     for (i = 0; i < cnt; i++) {
01057         e = edges[ind++];
01058         dy += stepy, ty += stepy, hy += stepy, dx += sgn*stepx;
01059         pointn = 0;
01060         points[pointn++] = tp;
01061         points[pointn++] = pointof(tp.x + dx, tp.y + ty / 3);
01062         points[pointn++] = pointof(tp.x + dx, np.y + dy);
01063         points[pointn++] = pointof((tp.x+hp.x)/2, np.y + dy);
01064         points[pointn++] = pointof(hp.x - dx, np.y + dy);
01065         points[pointn++] = pointof(hp.x - dx, hp.y + hy / 3);
01066         points[pointn++] = hp;
01067         if (ED_label(e)) {
01068             if (GD_flip(e->tail->graph)) {
01069                 width = ED_label(e)->dimen.y;
01070                 height = ED_label(e)->dimen.x;
01071             } else {
01072                 width = ED_label(e)->dimen.x;
01073                 height = ED_label(e)->dimen.y;
01074             }
01075             ED_label(e)->p.y = ND_coord_i(n).y + dy + height / 2.0;
01076             ED_label(e)->p.x = ND_coord_i(n).x;
01077             ED_label(e)->set = TRUE;
01078             if (height > stepy)
01079                 dy += height - stepy;
01080             if (dx + stepx < width)
01081                 dx += width - stepx;
01082         }
01083         clip_and_install(e, e->head, points, pointn, sinfo);
01084 #ifdef DEBUG
01085         if (debugleveln(e,1))
01086             showPoints (points, pointn);
01087 #endif
01088     }
01089     return;
01090 }
01091 
01092 static void
01093 selfRight (edge_t* edges[], int ind, int cnt, int stepx, int sizey,
01094            splineInfo* sinfo) 
01095 {
01096     int hx, tx, sgn;
01097     point tp, hp;
01098     node_t *n;
01099     edge_t *e;
01100     int i, stepy, dx, dy;
01101     double width, height; 
01102     point points[1000];
01103     int pointn;
01104     point np;
01105 
01106     e = edges[ind];
01107     n = e->tail;
01108 
01109     stepy = (sizey / 2) / cnt;
01110     stepy = MAX(stepy, 2);
01111     pointn = 0;
01112     np = ND_coord_i(n);
01113     tp  = ED_tail_port(e).p;
01114     tp.x += np.x;
01115     tp.y += np.y;
01116     hp  = ED_head_port(e).p;
01117     hp.x += np.x;
01118     hp.y += np.y;
01119     if (tp.y >= hp.y) sgn = 1;
01120     else sgn = -1;
01121     dx = ND_rw_i(n), dy = 0;
01122     tx = MIN(dx, 3*(np.x + dx - tp.x));
01123     hx = MIN(dx, 3*(np.x + dx - hp.x));
01124     for (i = 0; i < cnt; i++) {
01125         e = edges[ind++];
01126         dx += stepx, tx += stepx, hx += stepx, dy += sgn*stepy;
01127         pointn = 0;
01128         points[pointn++] = tp;
01129         points[pointn++] = pointof(tp.x + tx / 3, tp.y + dy);
01130         points[pointn++] = pointof(np.x + dx, tp.y + dy);
01131         points[pointn++] = pointof(np.x + dx, (tp.y+hp.y)/2);
01132         points[pointn++] = pointof(np.x + dx, hp.y - dy);
01133         points[pointn++] = pointof(hp.x + hx / 3, hp.y - dy);
01134         points[pointn++] = hp;
01135         if (ED_label(e)) {
01136             if (GD_flip(e->tail->graph)) {
01137                 width = ED_label(e)->dimen.y;
01138                 height = ED_label(e)->dimen.x;
01139             } else {
01140                 width = ED_label(e)->dimen.x;
01141                 height = ED_label(e)->dimen.y;
01142             }
01143             ED_label(e)->p.x = ND_coord_i(n).x + dx + width / 2.0;
01144             ED_label(e)->p.y = ND_coord_i(n).y;
01145             ED_label(e)->set = TRUE;
01146             if (width > stepx)
01147                 dx += width - stepx;
01148             if (dy + stepy < height)
01149                 dy += height - stepy;
01150         }
01151         clip_and_install(e, e->head, points, pointn, sinfo);
01152 #ifdef DEBUG
01153         if (debugleveln(e,1))
01154             showPoints (points, pointn);
01155 #endif
01156     }
01157     return;
01158 }
01159 
01160 static void
01161 selfLeft (edge_t* edges[], int ind, int cnt, int stepx, int sizey,
01162           splineInfo* sinfo) 
01163 {
01164     int hx, tx, sgn;
01165     point tp, hp;
01166     node_t *n;
01167     edge_t *e;
01168     int i, stepy, dx, dy;
01169     double width, height; 
01170     point points[1000];
01171     int pointn;
01172     point np;
01173 
01174     e = edges[ind];
01175     n = e->tail;
01176 
01177     stepy = (sizey / 2) / cnt;
01178     stepy = MAX(stepy,2);
01179     pointn = 0;
01180     np = ND_coord_i(n);
01181     tp = ED_tail_port(e).p;
01182     tp.x += np.x;
01183     tp.y += np.y;
01184     hp = ED_head_port(e).p;
01185     hp.x += np.x;
01186     hp.y += np.y;
01187     if (tp.y >= hp.y) sgn = 1;
01188     else sgn = -1;
01189     dx = ND_lw_i(n), dy = 0;
01190     tx = MIN(dx, 3*(tp.x + dx - np.x));
01191     hx = MIN(dx, 3*(hp.x + dx - np.x));
01192     for (i = 0; i < cnt; i++) {
01193         e = edges[ind++];
01194         dx += stepx, tx += stepx, hx += stepx, dy += sgn*stepy;
01195         pointn = 0;
01196         points[pointn++] = tp;
01197         points[pointn++] = pointof(tp.x - tx / 3, tp.y + dy);
01198         points[pointn++] = pointof(np.x - dx, tp.y + dy);
01199         points[pointn++] = pointof(np.x - dx, (tp.y+hp.y)/2);
01200         points[pointn++] = pointof(np.x - dx, hp.y - dy);
01201         points[pointn++] = pointof(hp.x - hx / 3, hp.y - dy);
01202         points[pointn++] = hp;
01203         if (ED_label(e)) {
01204         if (GD_flip(e->tail->graph)) {
01205             width = ED_label(e)->dimen.y;
01206             height = ED_label(e)->dimen.x;
01207         } else {
01208             width = ED_label(e)->dimen.x;
01209             height = ED_label(e)->dimen.y;
01210         }
01211         ED_label(e)->p.x = ND_coord_i(n).x - dx - width / 2.0;
01212         ED_label(e)->p.y = ND_coord_i(n).y;
01213         ED_label(e)->set = TRUE;
01214         if (width > stepx)
01215             dx += width - stepx;
01216         if (dy + stepy < height)
01217             dy += height - stepy;
01218         }
01219         clip_and_install(e, e->head, points, pointn, sinfo);
01220 #ifdef DEBUG
01221         if (debugleveln(e,1))
01222             showPoints (points, pointn);
01223 #endif
01224     }
01225 }
01226 
01227 /* selfRightSpace:
01228  * Assume e is self-edge.
01229  * Return extra space necessary on the right for this edge.
01230  * If the edge does not go on the right, return 0.
01231  * NOTE: the actual space is determined dynamically by GD_nodesep,
01232  * so using the constant SELF_EDGE_SIZE is going to be wrong.
01233  * Fortunately, the default nodesep is the same as SELF_EDGE_SIZE.
01234  */
01235 int
01236 selfRightSpace (edge_t* e)
01237 {
01238     int sw;
01239     double label_width;
01240     textlabel_t* l = ED_label(e);
01241 
01242     if (((!ED_tail_port(e).defined) && (!ED_head_port(e).defined)) ||
01243         (!(ED_tail_port(e).side & LEFT) && 
01244          !(ED_head_port(e).side & LEFT) &&
01245           ((ED_tail_port(e).side != ED_head_port(e).side) || 
01246           (!(ED_tail_port(e).side & (TOP|BOTTOM)))))) {
01247         sw = SELF_EDGE_SIZE;
01248         if (l) {
01249             label_width = GD_flip(e->head->graph) ? l->dimen.y : l->dimen.x;
01250             sw += label_width;
01251         }
01252     }
01253     else sw = 0;
01254     return sw;
01255 }
01256 
01257 /* makeSelfEdge:
01258  * The routing is biased towards the right side because this is what
01259  * dot supports, and leaves room for.
01260  * FIX: With this bias, labels tend to be placed on top of each other.
01261  * Perhaps for self-edges, the label should be centered.
01262  */
01263 void
01264 makeSelfEdge(path * P, edge_t * edges[], int ind, int cnt, int sizex,
01265              int sizey, splineInfo * sinfo)
01266 {
01267     edge_t *e;
01268 #ifdef OLD
01269     node_t *n;
01270     point *ps, np;
01271     pathend_t tend, hend;
01272     int i, j, maxx, stepy, dx, dy, tside, hside, dir, pn;
01273     double width, height;
01274     point points[1000];
01275     int pointn;
01276 #endif
01277 
01278     e = edges[ind];
01279 
01280     /* self edge without ports or
01281      * self edge with all ports inside, on the right, or at most 1 on top 
01282      * and at most 1 on bottom 
01283      */
01284     if (((!ED_tail_port(e).defined) && (!ED_head_port(e).defined)) ||
01285         (!(ED_tail_port(e).side & LEFT) && 
01286          !(ED_head_port(e).side & LEFT) &&
01287           ((ED_tail_port(e).side != ED_head_port(e).side) || 
01288           (!(ED_tail_port(e).side & (TOP|BOTTOM)))))) {
01289         selfRight(edges, ind, cnt, sizex, sizey, sinfo);
01290     }
01291 
01292     /* self edge with port on left side */
01293     else if ((ED_tail_port(e).side & LEFT) || (ED_head_port(e).side & LEFT)) {
01294 
01295         /* handle L-R specially */
01296         if ((ED_tail_port(e).side & RIGHT) || (ED_head_port(e).side & RIGHT)) {
01297             selfTop(edges, ind, cnt, sizex, sizey, sinfo);
01298         }
01299         else {
01300             selfLeft(edges, ind, cnt, sizex, sizey, sinfo);
01301         }
01302     }
01303 
01304     /* self edge with both ports on top side */
01305     else if (ED_tail_port(e).side & TOP) {
01306         selfTop(edges, ind, cnt, sizex, sizey, sinfo);
01307     }
01308     else if (ED_tail_port(e).side & BOTTOM) {
01309         selfBottom(edges, ind, cnt, sizex, sizey, sinfo);
01310     }
01311 
01312     else assert(0);
01313 
01314 #ifdef OLD
01315     tend.nb =
01316         boxof(ND_coord_i(n).x - ND_lw_i(n),
01317               ND_coord_i(n).y - ND_ht_i(n) / 2,
01318               ND_coord_i(n).x + ND_rw_i(n),
01319               ND_coord_i(n).y + ND_ht_i(n) / 2);
01320     hend.nb = tend.nb;
01321     stepy = stepx / 2;
01322     dx = 0, dy = 0;
01323     for (i = 0; i < cnt; i++) {
01324         e = edges[ind++];
01325         dx += stepx, dy += stepy;
01326 
01327         /* tail setup */
01328         beginpath(P, e, SELFEDGE, &tend, sinfo->splineMerge(e->tail));
01329 
01330         /* head setup */
01331         endpath(P, e, SELFEDGE, &hend, sinfo->splineMerge(e->head));
01332 
01333         chooseselfsides(&tend, &hend, &tside, &hside, &dir);
01334         completeselfpath(P, &tend, &hend, tside, hside, dir,
01335                          dx, dy, stepx, stepx);
01336 
01337         ps = routesplines(P, &pn);
01338         if (pn == 0)
01339             return;             /* will result in a lost edge */
01340         if (ED_label(e)) {
01341             /* FIXME: labels only right for BOTTOM -> TOP edges */
01342             for (j = 0, maxx = ND_coord_i(n).x; j < P->nbox; j++)
01343                 if (P->boxes[j].UR.x > maxx)
01344                     maxx = P->boxes[j].UR.x;
01345             if (GD_flip(e->tail->graph))
01346                 width = ED_label(e)->dimen.y;
01347             else
01348                 width = ED_label(e)->dimen.x;
01349             ED_label(e)->p.x = maxx + width / 2.0;
01350             ED_label(e)->p.y = ND_coord_i(n).y;
01351             ED_label(e)->set = TRUE;
01352             if (width > stepx)
01353                 dx += width - stepx;
01354         }
01355         clip_and_install(e, e->head, ps, pn, sinfo);
01356     }
01357 #endif
01358 }
01359 
01360 /* vladimir */
01361 void place_portlabel(edge_t * e, boolean head_p)
01362 /* place the {head,tail}label (depending on HEAD_P) of edge E */
01363 /* N.B. Assume edges are normalized, so tail is at spl->list[0].list[0]
01364  * and head is at spl->list[spl->size-l].list[bez->size-1]
01365  */
01366 {
01367     textlabel_t *l;
01368     splines *spl;
01369     bezier *bez;
01370     double dist, angle;
01371     point p;
01372     pointf c[4], pf;
01373     int i;
01374 
01375     if (ED_edge_type(e) == IGNORED)
01376         return;
01377     l = head_p ? ED_head_label(e) : ED_tail_label(e);
01378     spl = getsplinepoints(e);
01379     if (!head_p) {
01380         bez = &spl->list[0];
01381         if (bez->sflag) {
01382             p = bez->sp;
01383             P2PF(bez->list[0], pf);
01384         } else {
01385             p = bez->list[0];
01386             for (i = 0; i < 4; i++)
01387                 P2PF(bez->list[i], c[i]);
01388             pf = Bezier(c, 3, 0.1, NULL, NULL);
01389         }
01390     } else {
01391         bez = &spl->list[spl->size - 1];
01392         if (bez->eflag) {
01393             p = bez->ep;
01394             P2PF(bez->list[bez->size - 1], pf);
01395         } else {
01396             p = bez->list[bez->size - 1];
01397             for (i = 0; i < 4; i++)
01398                 P2PF(bez->list[bez->size - 4 + i], c[i]);
01399             pf = Bezier(c, 3, 0.9, NULL, NULL);
01400         }
01401     }
01402     angle = atan2(pf.y - p.y, pf.x - p.x) +
01403         RADIANS(late_double(e, E_labelangle, PORT_LABEL_ANGLE, -180.0));
01404     dist = PORT_LABEL_DISTANCE * late_double(e, E_labeldistance, 1.0, 0.0);
01405     l->p.x = p.x + ROUND(dist * cos(angle));
01406     l->p.y = p.y + ROUND(dist * sin(angle));
01407     l->set = TRUE;
01408 }
01409 
01410 splines *getsplinepoints(edge_t * e)
01411 {
01412     edge_t *le;
01413     splines *sp;
01414 
01415     for (le = e; !(sp = ED_spl(le)) && ED_edge_type(le) != NORMAL;
01416          le = ED_to_orig(le));
01417     if (sp == NULL)
01418         abort();
01419     return sp;
01420 }

Generated on Mon Mar 31 19:03:25 2008 for Graphviz by  doxygen 1.5.1