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