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