/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/dotgen/dotsplines.c

Go to the documentation of this file.
00001 /* $Id: dotsplines.c,v 1.42 2008/03/03 23:01:51 ellson Exp $ $Revision: 1.42 $ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /**********************************************************
00005 *      This software is part of the graphviz package      *
00006 *                http://www.graphviz.org/                 *
00007 *                                                         *
00008 *            Copyright (c) 1994-2004 AT&T Corp.           *
00009 *                and is licensed under the                *
00010 *            Common Public License, Version 1.0           *
00011 *                      by AT&T Corp.                      *
00012 *                                                         *
00013 *        Information and Software Systems Research        *
00014 *              AT&T Research, Florham Park NJ             *
00015 **********************************************************/
00016 
00017 
00018 /*
00019  * set edge splines.
00020  */
00021 
00022 #include "dot.h"
00023 
00024 #define NSUB    9               /* number of subdivisions, re-aiming splines */
00025 #define CHUNK   128             /* in building list of edges */
00026 
00027 #define MINW 16                 /* minimum width of a box in the edge path */
00028 #define HALFMINW 8
00029 
00030 #define FWDEDGE    16
00031 #define BWDEDGE    32
00032 
00033 #define MAINGRAPH  64
00034 #define AUXGRAPH  128
00035 #define GRAPHTYPEMASK   192     /* the OR of the above */
00036 
00037 #define MAKEFWDEDGE(new, old) { \
00038         edge_t *newp; \
00039         newp = new; \
00040         *newp = *old; \
00041         newp->tail = old->head; \
00042         newp->head = old->tail; \
00043         ED_tail_port(newp) = ED_head_port(old); \
00044         ED_head_port(newp) = ED_tail_port(old); \
00045         ED_edge_type(newp) = VIRTUAL; \
00046         ED_to_orig(newp) = old; \
00047 }
00048 
00049 #define P2PF(p, pf) (pf.x = p.x, pf.y = p.y)
00050 
00051 #ifdef OBSOLETE
00052 static int flatsidemap[16][6] = {
00053     {BOTTOM, BOTTOM, BOTTOM, CCW, CCW, FALSE},
00054     {TOP,    TOP,    TOP,    CW,  CW,  FALSE},
00055     {RIGHT,  LEFT,   BOTTOM, CW,  CW,  TRUE},
00056     {BOTTOM, TOP,    RIGHT,  CCW, CW,  TRUE},
00057     {TOP,    BOTTOM, RIGHT,  CW,  CCW, TRUE},
00058     {RIGHT,  TOP,    RIGHT,  CCW, CW,  TRUE},
00059     {RIGHT,  BOTTOM, RIGHT,  CW,  CCW, TRUE},
00060     {TOP,    LEFT,   TOP,    CW,  CCW, TRUE},
00061     {BOTTOM, LEFT,   BOTTOM, CCW, CW,  TRUE},
00062     {RIGHT,  RIGHT,  BOTTOM, CW,  CCW, TRUE},
00063     {LEFT,   LEFT,   BOTTOM, CCW, CW,  TRUE},
00064     {LEFT,   BOTTOM, BOTTOM, CCW, CCW, FALSE},
00065     {TOP,    RIGHT,  TOP,    CW,  CW,  FALSE},
00066     {LEFT,   TOP,    TOP,    CW,  CW,  FALSE},
00067     {BOTTOM, RIGHT,  BOTTOM, CCW, CCW, FALSE},
00068     {LEFT,   RIGHT,  BOTTOM, CCW, CCW, FALSE},
00069 };
00070 #endif
00071 
00072 #define AVG(a, b) ((a + b) / 2)
00073 
00074 static box boxes[1000];
00075 typedef struct {
00076     int LeftBound, RightBound, Splinesep, Multisep;
00077     box* Rank_box;
00078 } spline_info_t;
00079 
00080 static void adjustregularpath(path *, int, int);
00081 static Agedge_t *bot_bound(Agedge_t *, int);
00082 static boolean pathscross(Agnode_t *, Agnode_t *, Agedge_t *, Agedge_t *);
00083 #ifdef OBSOLETE
00084 static void chooseflatsides(pathend_t *, pathend_t *, int *, int *, int *,
00085                             int *, int *, int *);
00086 static void completeflatpath(path *, pathend_t *, pathend_t *,
00087                              box *, box *, int, int);
00088 static box makeflatend(box, int, int, box);
00089 static box makeflatcomponent(box, box, int, int, int, int, int);
00090 #endif
00091 static Agraph_t *cl_bound(Agnode_t *, Agnode_t *);
00092 static int cl_vninside(Agraph_t *, Agnode_t *);
00093 static void completeregularpath(path *, Agedge_t *, Agedge_t *,
00094                                 pathend_t *, pathend_t *, box *, int, int);
00095 static int edgecmp(Agedge_t **, Agedge_t **);
00096 static void make_flat_edge(spline_info_t*, path *, Agedge_t **, int, int, int);
00097 static void make_regular_edge(spline_info_t*, path *, Agedge_t **, int, int, int);
00098 static box makeregularend(box, int, int);
00099 static box maximal_bbox(spline_info_t*, Agnode_t *, Agedge_t *, Agedge_t *);
00100 static Agnode_t *neighbor(Agnode_t *, Agedge_t *, Agedge_t *, int);
00101 static void place_vnlabel(Agnode_t *);
00102 static box rank_box(spline_info_t* sp, Agraph_t *, int);
00103 static void recover_slack(Agedge_t *, path *);
00104 static void resize_vn(Agnode_t *, int, int, int);
00105 static void setflags(Agedge_t *, int, int, int);
00106 static int straight_len(Agnode_t *);
00107 static Agedge_t *straight_path(Agedge_t *, int, point *, int *);
00108 static Agedge_t *top_bound(Agedge_t *, int);
00109 
00110 #define GROWEDGES (edges = ALLOC (n_edges + CHUNK, edges, edge_t*))
00111 
00112 static edge_t*
00113 getmainedge(edge_t * e)
00114 {
00115     edge_t *le = e;
00116     while (ED_to_virt(le))
00117         le = ED_to_virt(le);
00118     while (ED_to_orig(le))
00119         le = ED_to_orig(le);
00120     return le;
00121 }
00122 
00123 static boolean spline_merge(node_t * n)
00124 {
00125     return ((ND_node_type(n) == VIRTUAL)
00126             && ((ND_in(n).size > 1) || (ND_out(n).size > 1)));
00127 }
00128 
00129 static boolean swap_ends_p(edge_t * e)
00130 {
00131     while (ED_to_orig(e))
00132         e = ED_to_orig(e);
00133     if (ND_rank(e->head) > ND_rank(e->tail))
00134         return FALSE;
00135     if (ND_rank(e->head) < ND_rank(e->tail))
00136         return TRUE;
00137     if (ND_order(e->head) >= ND_order(e->tail))
00138         return FALSE;
00139     return TRUE;
00140 }
00141 
00142 static splineInfo sinfo = { swap_ends_p, spline_merge };
00143 
00144 int portcmp(port p0, port p1)
00145 {
00146     int rv;
00147     if (p1.defined == FALSE)
00148         return (p0.defined ? 1 : 0);
00149     if (p0.defined == FALSE)
00150         return -1;
00151     rv = p0.p.x - p1.p.x;
00152     if (rv == 0)
00153         rv = p0.p.y - p1.p.y;
00154     return rv;
00155 }
00156 
00157 /* swap_bezier:
00158  */
00159 static void swap_bezier(bezier * old, bezier * new)
00160 {
00161     point *list;
00162     point *lp;
00163     point *olp;
00164     int i, sz;
00165 
00166     sz = old->size;
00167     list = N_GNEW(sz, point);
00168     lp = list;
00169     olp = old->list + (sz - 1);
00170     for (i = 0; i < sz; i++) {  /* reverse list of points */
00171         *lp++ = *olp--;
00172     }
00173 
00174     new->list = list;
00175     new->size = sz;
00176     new->sflag = old->eflag;
00177     new->eflag = old->sflag;
00178     new->sp = old->ep;
00179     new->ep = old->sp;
00180 }
00181 
00182 /* swap_spline:
00183  */
00184 static void swap_spline(splines * s)
00185 {
00186     bezier *list;
00187     bezier *lp;
00188     bezier *olp;
00189     int i, sz;
00190 
00191     sz = s->size;
00192     list = N_GNEW(sz, bezier);
00193     lp = list;
00194     olp = s->list + (sz - 1);
00195     for (i = 0; i < sz; i++) {  /* reverse and swap list of beziers */
00196         swap_bezier(olp--, lp++);
00197     }
00198 
00199     /* free old structures */
00200     for (i = 0; i < sz; i++)
00201         free(s->list[i].list);
00202     free(s->list);
00203 
00204     s->list = list;
00205 }
00206 
00207 /* edge_normalize:
00208  * Some back edges are reversed during layout and the reversed edge
00209  * is used to compute the spline. We would like to guarantee that
00210  * the order of control points always goes from tail to head, so
00211  * we reverse them if necessary.
00212  */
00213 static void edge_normalize(graph_t * g)
00214 {
00215     edge_t *e;
00216     node_t *n;
00217 
00218     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00219         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00220             if (sinfo.swapEnds(e) && ED_spl(e))
00221                 swap_spline(ED_spl(e));
00222         }
00223     }
00224 }
00225 
00226 /* _dot_splines:
00227  * Main spline routing code.
00228  * The normalize parameter allows this function to be called by the
00229  * recursive call in make_flat_edge without normalization occurring,
00230  * so that the edge will only be normalized once in the top level call
00231  * of dot_splines.
00232  */
00233 static void _dot_splines(graph_t * g, int normalize)
00234 {
00235     int i, j, k, n_nodes, n_edges, ind, cnt;
00236     node_t *n;
00237     edge_t fwdedgea, fwdedgeb;
00238     edge_t *e, *e0, *e1, *ea, *eb, *le0, *le1, **edges;
00239     path *P;
00240     spline_info_t sd;
00241     int et = EDGE_TYPE(g->root);
00242 
00243     if (et == ET_NONE) return; 
00244 
00245     mark_lowclusters(g);
00246     routesplinesinit();
00247     P = NEW(path);
00248     /* FlatHeight = 2 * GD_nodesep(g); */
00249     sd.Splinesep = GD_nodesep(g) / 4;
00250     sd.Multisep = GD_nodesep(g);
00251     edges = N_NEW(CHUNK, edge_t *);
00252 
00253     /* compute boundaries and list of splines */
00254     sd.LeftBound = sd.RightBound = 0;
00255     n_edges = n_nodes = 0;
00256     for (i = GD_minrank(g); i <= GD_maxrank(g); i++) {
00257         n_nodes += GD_rank(g)[i].n;
00258         if ((n = GD_rank(g)[i].v[0]))
00259             sd.LeftBound = MIN(sd.LeftBound, (ND_coord_i(n).x - ND_lw_i(n)));
00260         if (GD_rank(g)[i].n && (n = GD_rank(g)[i].v[GD_rank(g)[i].n - 1]))
00261             sd.RightBound = MAX(sd.RightBound, (ND_coord_i(n).x + ND_rw_i(n)));
00262         sd.LeftBound -= MINW;
00263         sd.RightBound += MINW;
00264 
00265         for (j = 0; j < GD_rank(g)[i].n; j++) {
00266             n = GD_rank(g)[i].v[j];
00267                 /* if n is the label of a flat edge, copy its position to
00268                  * the label.
00269                  */
00270             if (ND_alg(n)) {
00271                 edge_t* fe = (edge_t*)ND_alg(n);
00272                 assert (ED_label(fe));
00273                 ED_label(fe)->p = ND_coord_i(n);
00274             }
00275             if ((ND_node_type(n) != NORMAL) &&
00276                 (sinfo.splineMerge(n) == FALSE))
00277                 continue;
00278             for (k = 0; (e = ND_out(n).list[k]); k++) {
00279                 if ((ED_edge_type(e) == FLATORDER)
00280                     || (ED_edge_type(e) == IGNORED))
00281                     continue;
00282                 setflags(e, REGULAREDGE, FWDEDGE, MAINGRAPH);
00283                 edges[n_edges++] = e;
00284                 if (n_edges % CHUNK == 0)
00285                     GROWEDGES;
00286             }
00287             if (ND_flat_out(n).list)
00288                 for (k = 0; (e = ND_flat_out(n).list[k]); k++) {
00289                     setflags(e, FLATEDGE, 0, AUXGRAPH);
00290                     edges[n_edges++] = e;
00291                     if (n_edges % CHUNK == 0)
00292                         GROWEDGES;
00293                 }
00294             if (ND_other(n).list) {
00295                 /* In position, each node has its rw stored in mval and,
00296                  * if a node is part of a loop, rw may be increased to
00297                  * reflect the loops and associated labels. We restore
00298                  * the original value here. 
00299                  */
00300                 if (ND_node_type(n) == NORMAL) {
00301                     int tmp = ND_rw_i(n);
00302                     ND_rw_i(n) = ND_mval(n);
00303                     ND_mval(n) = tmp;
00304                 }
00305                 for (k = 0; (e = ND_other(n).list[k]); k++) {
00306                     setflags(e, 0, 0, AUXGRAPH);
00307                     edges[n_edges++] = e;
00308                     if (n_edges % CHUNK == 0)
00309                         GROWEDGES;
00310                 }
00311             }
00312         }
00313     }
00314 
00315     /* Sort so that equivalent edges are contiguous. 
00316      * Equivalence should basically mean that 2 edges have the
00317      * same set {(tailnode,tailport),(headnode,headport)}, or
00318      * alternatively, the edges would be routed identically if
00319      * routed separately.
00320      */
00321     qsort((char *) &edges[0], n_edges, sizeof(edges[0]),
00322           (qsort_cmpf) edgecmp);
00323 
00324     /* FIXME: just how many boxes can there be? */
00325     P->boxes = N_NEW(n_nodes + 20 * 2 * NSUB, box);
00326     sd.Rank_box = N_NEW(i, box);
00327 
00328     if (et == ET_LINE) {
00329     /* place regular edge labels */
00330         for (n = GD_nlist(g); n; n = ND_next(n)) {
00331             if ((ND_node_type(n) == VIRTUAL) && (ND_label(n))) {
00332                 place_vnlabel(n);
00333             }
00334         }
00335     }
00336 
00337     for (i = 0; i < n_edges;) {
00338         ind = i;
00339         le0 = getmainedge((e0 = edges[i++]));
00340         ea = (ED_tail_port(e0).defined
00341               || ED_head_port(e0).defined) ? e0 : le0;
00342         if (ED_tree_index(ea) & BWDEDGE) {
00343             MAKEFWDEDGE(&fwdedgea, ea);
00344             ea = &fwdedgea;
00345         }
00346         for (cnt = 1; i < n_edges; cnt++, i++) {
00347             if (le0 != (le1 = getmainedge((e1 = edges[i]))))
00348                 break;
00349             if (ED_adjacent(e0)) continue; /* all flat adjacent edges at once */
00350             eb = (ED_tail_port(e1).defined
00351                   || ED_head_port(e1).defined) ? e1 : le1;
00352             if (ED_tree_index(eb) & BWDEDGE) {
00353                 MAKEFWDEDGE(&fwdedgeb, eb);
00354                 eb = &fwdedgeb;
00355             }
00356             if (portcmp(ED_tail_port(ea), ED_tail_port(eb)))
00357                 break;
00358             if (portcmp(ED_head_port(ea), ED_head_port(eb)))
00359                 break;
00360             if ((ED_tree_index(e0) & EDGETYPEMASK) == FLATEDGE
00361                 && ED_label(e0) != ED_label(e1))
00362                 break;
00363             if (ED_tree_index(edges[i]) & MAINGRAPH)    /* Aha! -C is on */
00364                 break;
00365         }
00366 
00367         if (e0->tail == e0->head) {
00368             int b, sizey, r;
00369             n = e0->tail;
00370             r = ND_rank(n);
00371             if (r == GD_maxrank(g)) {
00372                 if (r > 0)
00373                     sizey = ND_coord_i(GD_rank(g)[r-1].v[0]).y - ND_coord_i(n).y;
00374                 else
00375                     sizey = ND_ht_i(n);
00376             }
00377             else if (r == GD_minrank(g)) {
00378                 sizey = ND_coord_i(n).y - ND_coord_i(GD_rank(g)[r+1].v[0]).y;
00379             }
00380             else {
00381                 int upy = ND_coord_i(GD_rank(g)[r-1].v[0]).y - ND_coord_i(n).y;
00382                 int dwny = ND_coord_i(n).y - ND_coord_i(GD_rank(g)[r+1].v[0]).y;
00383                 sizey = MIN(upy, dwny);
00384             }
00385             makeSelfEdge(P, edges, ind, cnt, sd.Multisep, sizey/2, &sinfo);
00386             for (b = 0; b < cnt; b++) {
00387                 e = edges[ind+b];
00388                 if (ED_label(e))
00389                     updateBB(g, ED_label(e));
00390             }
00391         }
00392         else if (ND_rank(e0->tail) == ND_rank(e0->head)) {
00393             make_flat_edge(&sd, P, edges, ind, cnt, et);
00394         }
00395         else
00396             make_regular_edge(&sd, P, edges, ind, cnt, et);
00397     }
00398 
00399     /* place regular edge labels */
00400     for (n = GD_nlist(g); n; n = ND_next(n)) {
00401         if ((ND_node_type(n) == VIRTUAL) && (ND_label(n))) {
00402             place_vnlabel(n);
00403             updateBB(g, ND_label(n));
00404         }
00405     }
00406 
00407     /* normalize splines so they always go from tail to head */
00408     /* place_portlabel relies on this being done first */
00409     if (normalize)
00410         edge_normalize(g);
00411 
00412     /* vladimir: place port labels */
00413     /* FIX: head and tail labels are not part of cluster bbox */
00414     if (E_headlabel || E_taillabel) {
00415         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00416             if (E_headlabel) {
00417                 for (e = agfstin(g, n); e; e = agnxtin(g, e))
00418                     if (ED_head_label(e)) {
00419                         place_portlabel(e, TRUE);
00420                         updateBB(g, ED_head_label(e));
00421                     }
00422             }
00423             if (E_taillabel) {
00424                 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00425                     if (ED_tail_label(e)) {
00426                         place_portlabel(e, FALSE);
00427                         updateBB(g, ED_tail_label(e));
00428                     }
00429                 }
00430             }
00431         }
00432     }
00433     /* end vladimir */
00434 
00435     free(edges);
00436     free(P->boxes);
00437     free(P);
00438     free(sd.Rank_box);
00439     routesplinesterm();
00440     State = GVSPLINES;
00441 }
00442 
00443 /* dot_splines:
00444  * If the splines attribute is defined but equal to "", skip edge routing.
00445  */
00446 void dot_splines(graph_t * g)
00447 {
00448     _dot_splines (g, 1);
00449 }
00450 
00451 /* place_vnlabel:
00452  * assign position of an edge label from its virtual node
00453  * This is for regular edges only.
00454  */
00455 static void 
00456 place_vnlabel(node_t * n)
00457 {
00458     pointf dimen;
00459     double width;
00460     edge_t *e;
00461     if (ND_in(n).size == 0)
00462         return;                 /* skip flat edge labels here */
00463     for (e = ND_out(n).list[0]; ED_edge_type(e) != NORMAL;
00464          e = ED_to_orig(e));
00465     dimen = ED_label(e)->dimen;
00466     width = GD_flip(n->graph) ? dimen.y : dimen.x;
00467     ED_label(e)->p.x = ND_coord_i(n).x + width / 2.0;
00468     ED_label(e)->p.y = ND_coord_i(n).y;
00469 }
00470 
00471 static void 
00472 setflags(edge_t *e, int hint1, int hint2, int f3)
00473 {
00474     int f1, f2;
00475     if (hint1 != 0)
00476         f1 = hint1;
00477     else {
00478         if (e->tail == e->head)
00479             if (ED_tail_port(e).defined || ED_head_port(e).defined)
00480                 f1 = SELFWPEDGE;
00481             else
00482                 f1 = SELFNPEDGE;
00483         else if (ND_rank(e->tail) == ND_rank(e->head))
00484             f1 = FLATEDGE;
00485         else
00486             f1 = REGULAREDGE;
00487     }
00488     if (hint2 != 0)
00489         f2 = hint2;
00490     else {
00491         if (f1 == REGULAREDGE)
00492             f2 = (ND_rank(e->tail) < ND_rank(e->head)) ? FWDEDGE : BWDEDGE;
00493         else if (f1 == FLATEDGE)
00494             f2 = (ND_order(e->tail) < ND_order(e->head)) ?
00495                 FWDEDGE : BWDEDGE;
00496         else                    /* f1 == SELF*EDGE */
00497             f2 = FWDEDGE;
00498     }
00499     ED_tree_index(e) = (f1 | f2 | f3);
00500 }
00501 
00502 /* edgecmp:
00503  * lexicographically order edges by
00504  *  - edge type
00505  *  - |rank difference of nodes|
00506  *  - |x difference of nodes|
00507  *  - id of witness edge for equivalence class
00508  *  - port comparison
00509  *  - graph type
00510  *  - labels if flat edges
00511  *  - edge id
00512  */
00513 static int edgecmp(edge_t** ptr0, edge_t** ptr1)
00514 {
00515     edge_t fwdedgea, fwdedgeb, *e0, *e1, *ea, *eb, *le0, *le1;
00516     int et0, et1, v0, v1, rv;
00517 
00518     e0 = (edge_t *) * ptr0;
00519     e1 = (edge_t *) * ptr1;
00520     et0 = ED_tree_index(e0) & EDGETYPEMASK;
00521     et1 = ED_tree_index(e1) & EDGETYPEMASK;
00522     if (et0 != et1)
00523         return (et1 - et0);
00524     le0 = getmainedge(e0);
00525     le1 = getmainedge(e1);
00526     v0 = ND_rank(le0->tail) - ND_rank(le0->head), v0 = ABS(v0);
00527     v1 = ND_rank(le1->tail) - ND_rank(le1->head), v1 = ABS(v1);
00528     if (v0 != v1)
00529         return (v0 - v1);
00530     v0 = ND_coord_i(le0->tail).x - ND_coord_i(le0->head).x, v0 = ABS(v0);
00531     v1 = ND_coord_i(le1->tail).x - ND_coord_i(le1->head).x, v1 = ABS(v1);
00532     if (v0 != v1)
00533         return (v0 - v1);
00534     /* This provides a cheap test for edges having the same set of endpoints.
00535      */
00536     if (le0->id != le1->id)
00537         return (le0->id - le1->id);
00538     ea = (ED_tail_port(e0).defined || ED_head_port(e0).defined) ? e0 : le0;
00539     if (ED_tree_index(ea) & BWDEDGE) {
00540         MAKEFWDEDGE(&fwdedgea, ea);
00541         ea = &fwdedgea;
00542     }
00543     eb = (ED_tail_port(e1).defined || ED_head_port(e1).defined) ? e1 : le1;
00544     if (ED_tree_index(eb) & BWDEDGE) {
00545         MAKEFWDEDGE(&fwdedgeb, eb);
00546         eb = &fwdedgeb;
00547     }
00548     if ((rv = portcmp(ED_tail_port(ea), ED_tail_port(eb))))
00549         return rv;
00550     if ((rv = portcmp(ED_head_port(ea), ED_head_port(eb))))
00551         return rv;
00552     v0 = ED_tree_index(e0) & GRAPHTYPEMASK;
00553     v1 = ED_tree_index(e1) & GRAPHTYPEMASK;
00554     if (v0 != v1)
00555         return (v0 - v1);
00556     if (et0 == FLATEDGE && ED_label(e0) != ED_label(e1))
00557         return (int) (ED_label(e0) - ED_label(e1));
00558     return (e0->id - e1->id);
00559 }
00560 
00561 #if 0
00562 /* fledgecmp:
00563  * Sort edges by mid y value of ports.
00564  * If this is the same, and all y values are the same,
00565  * check if one segment lies within the other.
00566  */
00567 static int 
00568 fledgecmp(edge_t** ptr0, edge_t** ptr1)
00569 {
00570     edge_t *e0, *e1;
00571     point tp0, tp1, hp0, hp1;
00572     int y0, y1;
00573 
00574     e0 = *ptr0;
00575     e1 = *ptr1;
00576     tp0 = ED_tail_port(e0).p;
00577     hp0 = ED_head_port(e0).p;
00578     tp1 = ED_tail_port(e1).p;
00579     hp1 = ED_head_port(e1).p;
00580     y0 = (tp0.y + hp0.y)/2;
00581     y1 = (tp1.y + hp1.y)/2;
00582     if (y0 != y1) return (y0-y1);
00583     if ((tp0.y == hp0.y) && (tp1.y == hp1.y)) {
00584         if ((tp0.x <= tp1.x) && (hp0.x >= hp1.x)) {
00585             if (tp0.y <= 0) return -1;
00586             else return 1;
00587         }
00588         else if ((tp0.x >= tp1.x) && (hp0.x <= hp1.x)) {
00589             if (tp0.y <= 0) return 1;
00590             else return -1;
00591         }
00592     }
00593     return (e0->id - e1->id);
00594 
00595 }
00596 
00597 #define LABEL_SPACE 8
00598 
00599 /* setFlatAdjPos:
00600  * Create middle boxes for routing using ordered list of edges going from
00601  * bottom to top.
00602  * Also, set label positions.
00603  */
00604 static void
00605 setFlatAdjPos (edge_t** edges, int n_edges, int flip, box* boxes, edge_t* e0)
00606 {
00607     int r, i, x, boxw, availht;
00608     edge_t* e;
00609     double  y, wd, ht, totalht = 0;
00610     textlabel_t* lbl;
00611     node_t *tn, *hn;
00612     graph_t* g;
00613 
00614 assert(0);
00615     tn = e0->tail, hn = e0->head;
00616     g = tn->graph;
00617     x = (ND_coord_i(tn).x + ND_coord_i(hn).x)/2;
00618     y = ND_coord_i(tn).y;
00619     r = ND_rank(tn);
00620     availht = GD_rank(g)[r].ht2 + GD_rank(g)[r].ht1 + GD_ranksep(g);
00621     boxw = (ND_coord_i(hn).x - ND_coord_i(tn).x - ND_rw_i(tn) - ND_lw_i(hn))/3;
00622     for (i = 0; i < n_edges; i++) {
00623         if (!((lbl = ED_label(e)))) continue;
00624         if (flip) {
00625             ht = lbl->dimen.x;
00626             wd = lbl->dimen.y;
00627         }
00628         else {
00629             ht = lbl->dimen.y; 
00630             wd = lbl->dimen.x; 
00631         }
00632         totalht += ht;
00633         boxw = MAX(boxw, wd);
00634     }
00635     for (i = 0; i < n_edges; i++) {
00636         e = edges[i];
00637         lbl = ED_label(e);
00638         if (GD_flip(g)) ht = lbl->dimen.x;
00639         else ht = lbl->dimen.y; 
00640         lbl->p.x = x;
00641         lbl->p.y = ROUND(y - ht/2);
00642         y -= ht + LABEL_SPACE;
00643     }
00644 }
00645 #endif
00646  
00647 /* cloneGraph:
00648  */
00649 static struct {
00650     attrsym_t* E_constr;
00651     attrsym_t* E_samehead;
00652     attrsym_t* E_sametail;
00653     attrsym_t* E_weight;
00654     attrsym_t* E_minlen;
00655     attrsym_t* N_group;
00656     int        State;
00657 } attr_state;
00658 
00659 /* cloneGraph:
00660  * Create clone graph. It stores the global Agsyms, to be
00661  * restored in cleanupCloneGraph. The graph uses the main
00662  * graph's settings for certain geometry parameters, and
00663  * declares all node and edge attributes used in the original
00664  * graph.
00665  */
00666 static graph_t*
00667 cloneGraph (graph_t* g)
00668 {
00669     Agsym_t* sym;
00670     graph_t* auxg;
00671     Agsym_t **list;
00672     
00673     auxg = agopen ("auxg", AG_IS_DIRECTED(g)?AGDIGRAPH:AGRAPH);
00674     agraphattr(auxg, "rank", "");
00675     GD_drawing(auxg) = NEW(layout_t);
00676     GD_drawing(auxg)->quantum = GD_drawing(g)->quantum; 
00677     GD_drawing(auxg)->dpi = GD_drawing(g)->dpi;
00678 
00679     GD_charset(auxg) = GD_charset (g);
00680     if (GD_flip(g))
00681         SET_RANKDIR(auxg, RANKDIR_TB);
00682     else
00683         SET_RANKDIR(auxg, RANKDIR_LR);
00684     GD_nodesep(auxg) = GD_nodesep(g);
00685     GD_ranksep(auxg) = GD_ranksep(g);
00686 
00687     list = g->root->univ->nodeattr->list;
00688     while ((sym = *list++)) {
00689         agnodeattr (auxg, sym->name, sym->value);
00690     }
00691 
00692     list = g->root->univ->edgeattr->list;
00693     while ((sym = *list++)) {
00694         agedgeattr (auxg, sym->name, sym->value);
00695     }
00696 
00697     attr_state.E_constr = E_constr;
00698     attr_state.E_samehead = E_samehead;
00699     attr_state.E_sametail = E_sametail;
00700     attr_state.E_weight = E_weight;
00701     attr_state.E_minlen = E_minlen;
00702     attr_state.N_group = N_group;
00703     attr_state.State = State;
00704     E_constr = NULL;
00705     E_samehead = agfindattr(auxg->proto->e, "samehead");
00706     E_sametail = agfindattr(auxg->proto->e, "sametail");
00707     E_weight = agfindattr(auxg->proto->e, "weight");
00708     if (!E_weight)
00709         E_weight = agedgeattr (auxg, "weight", "");
00710     E_minlen = NULL;
00711     N_group = NULL;
00712 
00713     return auxg;
00714 }
00715 
00716 /* cleanupCloneGraph:
00717  */
00718 static void
00719 cleanupCloneGraph (graph_t* g)
00720 {
00721     /* restore main graph syms */
00722     E_constr = attr_state.E_constr;
00723     E_samehead = attr_state.E_samehead;
00724     E_sametail = attr_state.E_sametail;
00725     E_weight = attr_state.E_weight;
00726     E_minlen = attr_state.E_minlen;
00727     N_group = attr_state.N_group;
00728     State = attr_state.State;
00729 
00730     dot_cleanup(g);
00731     agclose(g);
00732 }
00733 
00734 /* cloneNode:
00735  * If flipped is true, original graph has rankdir=LR or RL.
00736  * In this case, records change shape, so we wrap a record node's
00737  * label in "{...}" to prevent this.
00738  */
00739 static node_t*
00740 cloneNode (graph_t* g, node_t* orign, int flipped)
00741 {
00742     node_t* n = agnode(g, orign->name);
00743     agcopyattr (orign, n);
00744     if (shapeOf(orign) == SH_RECORD) {
00745         int lbllen = strlen(ND_label(orign)->text);
00746         char* buf = N_GNEW(lbllen+3,char);
00747         sprintf (buf, "{%s}", ND_label(orign)->text);
00748         agset (n, "label", buf);
00749     }
00750 
00751     return n;
00752 }
00753 
00754 /* cloneEdge:
00755  */
00756 static edge_t*
00757 cloneEdge (graph_t* g, node_t* tn, node_t* hn, edge_t* orig)
00758 {
00759     edge_t* e = agedge(g, tn, hn);
00760     for (; ED_edge_type(orig) != NORMAL; orig = ED_to_orig(orig));
00761     agcopyattr (orig, e);
00762 
00763     return e;
00764 }
00765 
00766 /* transform:
00767  * Rotate, if necessary, then translate points.
00768  */
00769 static point
00770 transform (point p, point del, int flip)
00771 {
00772     if (flip) {
00773         int i = p.x;
00774         p.x = p.y;
00775         p.y = -i;
00776     }
00777     return add_points(p, del);
00778 }
00779 
00780 /* makeSimpleFlat:
00781  */
00782 static void
00783 makeSimpleFlat (node_t* tn, node_t* hn, edge_t** edges, int ind, int cnt, int et)
00784 {
00785     edge_t* e = edges[ind];
00786     point points[10];
00787     int i, pointn, stepy = (cnt > 1) ? ND_ht_i(tn) / (cnt - 1) : 0;
00788     point tp = add_points(ND_coord_i(tn), ED_tail_port(e).p);
00789     point hp = add_points(ND_coord_i(hn), ED_head_port(e).p);
00790     int dy = tp.y - ((cnt > 1) ? ND_ht_i(tn) / 2 : 0);
00791     for (i = 0; i < cnt; i++) {
00792         e = edges[ind + i];
00793         pointn = 0;
00794         if ((et == ET_SPLINE) || (et == ET_LINE)) {
00795             points[pointn++] = tp;
00796             points[pointn++] = pointof((2 * tp.x + hp.x) / 3, dy);
00797             points[pointn++] = pointof((2 * hp.x + tp.x) / 3, dy);
00798             points[pointn++] = hp;
00799         }
00800         else {
00801             points[pointn++] = tp;
00802             points[pointn++] = tp;
00803             points[pointn++] = pointof((2 * tp.x + hp.x) / 3, dy);
00804             points[pointn++] = pointof((2 * tp.x + hp.x) / 3, dy);
00805             points[pointn++] = pointof((2 * tp.x + hp.x) / 3, dy);
00806             points[pointn++] = pointof((2 * hp.x + tp.x) / 3, dy);
00807             points[pointn++] = pointof((2 * hp.x + tp.x) / 3, dy);
00808             points[pointn++] = pointof((2 * hp.x + tp.x) / 3, dy);
00809             points[pointn++] = hp;
00810             points[pointn++] = hp;
00811         }
00812         dy += stepy;
00813         clip_and_install(e, e->head, points, pointn, &sinfo);
00814     }
00815 }
00816 
00817 /* make_flat_adj_edges:
00818  * In the simple case, with no labels or ports, this creates a simple
00819  * spindle of splines.
00820  * Otherwise, we run dot recursively on the 2 nodes and the edges, 
00821  * essentially using rankdir=LR, to get the needed spline info.
00822  */
00823 static void
00824 make_flat_adj_edges(path* P, edge_t** edges, int ind, int cnt, edge_t* e0,
00825                     int et)
00826 {
00827     node_t* n;
00828     node_t *tn, *hn;
00829     edge_t* e;
00830     int labels = 0, ports = 0;
00831     graph_t* g;
00832     graph_t* auxg;
00833     graph_t* subg;
00834     node_t *auxt, *auxh;
00835     edge_t* auxe;
00836     int     i, j, midx, midy, leftx, rightx;
00837     point   del;
00838     edge_t* hvye = NULL;
00839 
00840     g = e0->tail->graph;
00841     tn = e0->tail, hn = e0->head;
00842     for (i = 0; i < cnt; i++) {
00843         e = edges[ind + i];
00844         if (ND_label(e)) labels++;
00845         if (ED_tail_port(e).defined || ED_head_port(e).defined) ports = 1;
00846     }
00847 
00848     /* flat edges without ports and labels can go straight left to right */
00849     if ((labels == 0) && (ports == 0)) {
00850         makeSimpleFlat (tn, hn, edges, ind, cnt, et);
00851         return;
00852     }
00853 
00854     auxg = cloneGraph (g);
00855     subg = agsubg (auxg, "xxx");
00856     agset (subg, "rank", "source");
00857     rightx = ND_coord_i(hn).x;
00858     leftx = ND_coord_i(tn).x;
00859     if (GD_flip(g)) {
00860         node_t* n;
00861         n = tn;
00862         tn = hn;
00863         hn = n;
00864     }
00865     auxt = cloneNode(subg, tn, GD_flip(g)); 
00866     auxh = cloneNode(auxg, hn, GD_flip(g)); 
00867     for (i = 0; i < cnt; i++) {
00868         e = edges[ind + i];
00869         if (e->tail == tn)
00870             auxe = cloneEdge (auxg, auxt, auxh, e);
00871         else
00872             auxe = cloneEdge (auxg, auxh, auxt, e);
00873         ED_alg(e) = auxe;
00874         if (!hvye && !ED_tail_port(e0).defined && !ED_head_port(e0).defined) {
00875             hvye = auxe;
00876             ED_alg(hvye) = e;
00877         }
00878     }
00879     if (!hvye) {
00880         hvye = agedge (auxg, auxt, auxh);
00881     }
00882     agxset (hvye, E_weight->index, "10000");
00883     GD_gvc(auxg) = GD_gvc(g);
00884     setEdgeType (auxg, et);
00885     dot_init_node_edge(auxg);
00886 
00887     dot_rank(auxg);
00888     dot_mincross(auxg);
00889     dot_position(auxg);
00890     
00891     /* reposition */
00892     midx = (ND_coord_i(tn).x - ND_rw_i(tn) + ND_coord_i(hn).x + ND_lw_i(hn))/2;
00893     midy = (ND_coord_i(auxt).x + ND_coord_i(auxh).x)/2;
00894     for (n = GD_nlist(auxg); n; n = ND_next(n)) {
00895         if (n == auxt) {
00896             ND_coord_i(n).y = rightx;
00897             ND_coord_i(n).x = midy;
00898         }
00899         else if (n == auxh) {
00900             ND_coord_i(n).y = leftx;
00901             ND_coord_i(n).x = midy;
00902         }
00903         else ND_coord_i(n).y = midx;
00904     }
00905     dot_sameports(auxg);
00906     _dot_splines(auxg, 0);
00907     dotneato_postprocess(auxg);
00908 
00909        /* copy splines */
00910     if (GD_flip(g)) {
00911         del.x = ND_coord_i(tn).x - ND_coord_i(auxt).y;
00912         del.y = ND_coord_i(tn).y + ND_coord_i(auxt).x;
00913     }
00914     else {
00915         del.x = ND_coord_i(tn).x - ND_coord_i(auxt).x;
00916         del.y = ND_coord_i(tn).y - ND_coord_i(auxt).y;
00917     }
00918     for (i = 0; i < cnt; i++) {
00919         bezier* auxbz;
00920         bezier* bz;
00921 
00922         e = edges[ind + i];
00923         auxe = (edge_t*)ED_alg(e);
00924         if ((auxe == hvye) & !ED_alg(auxe)) continue; /* pseudo-edge */
00925         auxbz = ED_spl(auxe)->list;
00926         bz = new_spline(e, auxbz->size);
00927         if (GD_flip(g)) {
00928             bz->sflag = auxbz->eflag;
00929             bz->sp = transform(auxbz->ep, del, 1);
00930             bz->eflag = auxbz->sflag;
00931             bz->ep = transform(auxbz->sp, del, 1);
00932         }
00933         else {
00934             bz->sflag = auxbz->sflag;
00935             bz->sp = transform(auxbz->sp, del, 0);
00936             bz->eflag = auxbz->eflag;
00937             bz->ep = transform(auxbz->ep, del, 0);
00938         }
00939         for (j = 0; j <  auxbz->size; j++) {
00940             point pt;
00941             pt = bz->list[j] = transform(auxbz->list[j], del, GD_flip(g));
00942             update_bb(g, pt);
00943         }
00944         if (ED_label(e)) {
00945             ED_label(e)->p = transform(ED_label(auxe)->p, del, GD_flip(g));
00946             updateBB(g, ED_label(e));
00947         }
00948     }
00949 
00950     cleanupCloneGraph (auxg);
00951 }
00952 
00953 /* makeFlatEnd;
00954  */
00955 static void
00956 makeFlatEnd (spline_info_t* sp, path* P, node_t* n, edge_t* e, pathend_t* endp,
00957              boolean isBegin)
00958 {
00959     box b;
00960     graph_t* g = n->graph;
00961 
00962     b = endp->nb = maximal_bbox(sp, n, NULL, e);
00963     endp->sidemask = TOP;
00964     if (isBegin) beginpath(P, e, FLATEDGE, endp, FALSE);
00965     else endpath(P, e, FLATEDGE, endp, FALSE);
00966     b.UR.y = endp->boxes[endp->boxn - 1].UR.y;
00967     b.LL.y = endp->boxes[endp->boxn - 1].LL.y;
00968     b = makeregularend(b, TOP, ND_coord_i(n).y + GD_rank(g)[ND_rank(n)].ht2);
00969     if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
00970         endp->boxes[endp->boxn++] = b;
00971 }
00972 /* makeBottomFlatEnd;
00973  */
00974 static void
00975 makeBottomFlatEnd (spline_info_t* sp, path* P, node_t* n, edge_t* e, 
00976         pathend_t* endp, boolean isBegin)
00977 {
00978     box b;
00979     graph_t* g = n->graph;
00980 
00981     b = endp->nb = maximal_bbox(sp, n, NULL, e);
00982     endp->sidemask = BOTTOM;
00983     if (isBegin) beginpath(P, e, FLATEDGE, endp, FALSE);
00984     else endpath(P, e, FLATEDGE, endp, FALSE);
00985     b.UR.y = endp->boxes[endp->boxn - 1].UR.y;
00986     b.LL.y = endp->boxes[endp->boxn - 1].LL.y;
00987     b = makeregularend(b, BOTTOM, ND_coord_i(n).y - GD_rank(g)[ND_rank(n)].ht2);
00988     if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
00989         endp->boxes[endp->boxn++] = b;
00990 }
00991 
00992 
00993 /* make_flat_labeled_edge:
00994  */
00995 static void
00996 make_flat_labeled_edge(spline_info_t* sp, path* P, edge_t* e, int et)
00997 {
00998     graph_t *g;
00999     node_t *tn, *hn, *ln;
01000     point *ps;
01001     pathend_t tend, hend;
01002     box lb;
01003     int boxn, i, pn, ydelta;
01004     edge_t *f;
01005     point points[7];
01006 
01007     g = e->tail->graph;
01008     tn = e->tail;
01009     hn = e->head;
01010 
01011     for (f = ED_to_virt(e); ED_to_virt(f); f = ED_to_virt(f));
01012     ln = f->tail;
01013     ED_label(e)->p = ND_coord_i(ln);
01014 
01015     if (et == ET_LINE) {
01016         point startp, endp, lp;
01017         startp = add_points(ND_coord_i(tn), ED_tail_port(e).p);
01018         endp = add_points(ND_coord_i(hn), ED_head_port(e).p);
01019         lp = ED_label(e)->p;
01020         lp.y -= (ED_label(e)->dimen.y)/2.0;
01021         points[1] = points[0] = startp;
01022         points[2] = points[3] = points[4] = lp;
01023         points[5] = points[6] = endp;
01024         ps = points;
01025         pn = 7;
01026     }
01027     else {
01028         lb.LL.x = ND_coord_i(ln).x - ND_lw_i(ln);
01029         lb.UR.x = ND_coord_i(ln).x + ND_rw_i(ln);
01030         lb.UR.y = ND_coord_i(ln).y + ND_ht_i(ln)/2;
01031         ydelta = ND_coord_i(ln).y - GD_rank(g)[ND_rank(tn)].ht1 -
01032                 ND_coord_i(tn).y + GD_rank(g)[ND_rank(tn)].ht2;
01033         ydelta /= 6;
01034         lb.LL.y = lb.UR.y - MAX(5,ydelta); 
01035 
01036         boxn = 0;
01037         makeFlatEnd (sp, P, tn, e, &tend, TRUE);
01038         makeFlatEnd (sp, P, hn, e, &hend, FALSE);
01039 
01040         boxes[boxn].LL.x = tend.boxes[tend.boxn - 1].LL.x; 
01041         boxes[boxn].LL.y = tend.boxes[tend.boxn - 1].UR.y; 
01042         boxes[boxn].UR.x = lb.LL.x;
01043         boxes[boxn].UR.y = lb.LL.y;
01044         boxn++;
01045         boxes[boxn].LL.x = tend.boxes[tend.boxn - 1].LL.x; 
01046         boxes[boxn].LL.y = lb.LL.y;
01047         boxes[boxn].UR.x = hend.boxes[hend.boxn - 1].UR.x;
01048         boxes[boxn].UR.y = lb.UR.y;
01049         boxn++;
01050         boxes[boxn].LL.x = lb.UR.x;
01051         boxes[boxn].UR.y = lb.LL.y;
01052         boxes[boxn].LL.y = hend.boxes[hend.boxn - 1].UR.y; 
01053         boxes[boxn].UR.x = hend.boxes[hend.boxn - 1].UR.x;
01054         boxn++;
01055 
01056         for (i = 0; i < tend.boxn; i++) add_box(P, tend.boxes[i]);
01057         for (i = 0; i < boxn; i++) add_box(P, boxes[i]);
01058         for (i = hend.boxn - 1; i >= 0; i--) add_box(P, hend.boxes[i]);
01059 
01060         if (et == ET_SPLINE) ps = routesplines(P, &pn);
01061         else ps = routepolylines(P, &pn);
01062         if (pn == 0) return;
01063     }
01064     clip_and_install(e, e->head, ps, pn, &sinfo);
01065 }
01066 
01067 /* make_flat_bottom_edges:
01068  */
01069 static void
01070 make_flat_bottom_edges(spline_info_t* sp, path * P, edge_t ** edges, int 
01071         ind, int cnt, edge_t* e, int splines)
01072 {
01073     node_t *tn, *hn;
01074     int j, i, stepx, stepy, vspace, r;
01075     rank_t* nextr;
01076     int pn;
01077     point *ps;
01078     pathend_t tend, hend;
01079     graph_t* g;
01080 
01081     tn = e->tail, hn = e->head;
01082     g = tn->graph;
01083     r = ND_rank(tn);
01084     if (r < GD_maxrank(g)) {
01085         nextr = GD_rank(g) + (r+1);
01086         vspace = ND_coord_i(tn).y - GD_rank(g)[r].pht1 -
01087                 (ND_coord_i(nextr->v[0]).y + nextr->pht2);
01088     }
01089     else {
01090         vspace = GD_ranksep(g);
01091     }
01092     stepx = sp->Multisep / (cnt+1); 
01093     stepy = vspace / (cnt+1);
01094 
01095     makeBottomFlatEnd (sp, P, tn, e, &tend, TRUE);
01096     makeBottomFlatEnd (sp, P, hn, e, &hend, FALSE);
01097 
01098     for (i = 0; i < cnt; i++) {
01099         int boxn;
01100         box b;
01101         e = edges[ind + i];
01102         boxn = 0;
01103 
01104         b = tend.boxes[tend.boxn - 1];
01105         boxes[boxn].LL.x = b.LL.x; 
01106         boxes[boxn].UR.y = b.LL.y; 
01107         boxes[boxn].UR.x = b.UR.x + (i + 1) * stepx;
01108         boxes[boxn].LL.y = b.LL.y - (i + 1) * stepy;
01109         boxn++;
01110         boxes[boxn].LL.x = tend.boxes[tend.boxn - 1].LL.x; 
01111         boxes[boxn].UR.y = boxes[boxn-1].LL.y;
01112         boxes[boxn].UR.x = hend.boxes[hend.boxn - 1].UR.x;
01113         boxes[boxn].LL.y = boxes[boxn].UR.y - stepy;
01114         boxn++;
01115         b = hend.boxes[hend.boxn - 1];
01116         boxes[boxn].UR.x = b.UR.x;
01117         boxes[boxn].UR.y = b.LL.y;
01118         boxes[boxn].LL.x = b.LL.x - (i + 1) * stepx;
01119         boxes[boxn].LL.y = boxes[boxn-1].UR.y;
01120         boxn++;
01121 
01122         for (j = 0; j < tend.boxn; j++) add_box(P, tend.boxes[j]);
01123         for (j = 0; j < boxn; j++) add_box(P, boxes[j]);
01124         for (j = hend.boxn - 1; j >= 0; j--) add_box(P, hend.boxes[j]);
01125 
01126         if (splines) ps = routesplines(P, &pn);
01127         else ps = routepolylines(P, &pn);
01128         if (pn == 0)
01129             return;
01130         clip_and_install(e, e->head, ps, pn, &sinfo);
01131         P->nbox = 0;
01132     }
01133 }
01134 
01135 /* make_flat_edge:
01136  * Construct flat edges edges[ind...ind+cnt-1]
01137  * There are 4 main cases:
01138  *  - all edges between a and b where a and b are adjacent 
01139  *  - one labeled edge
01140  *  - all non-labeled edges with identical ports between non-adjacent a and b 
01141  *     = connecting bottom to bottom/left/right - route along bottom
01142  *     = the rest - route along top
01143  */
01144 static void
01145 make_flat_edge(spline_info_t* sp, path * P, edge_t ** edges, int ind, int cnt, int et)
01146 {
01147     node_t *tn, *hn;
01148     edge_t fwdedge, *e;
01149     int j, i, stepx, stepy, vspace, r;
01150     int tside, hside, pn;
01151     point *ps;
01152     pathend_t tend, hend;
01153     graph_t* g;
01154 
01155     /* Get sample edge; normalize to go from left to right */
01156     e = edges[ind];
01157     if (ED_tree_index(e) & BWDEDGE) {
01158         MAKEFWDEDGE(&fwdedge, e);
01159         e = &fwdedge;
01160     }
01161     if (ED_adjacent(edges[ind])) {
01162         make_flat_adj_edges (P, edges, ind, cnt, e, et);
01163         return;
01164     }
01165     if (ED_label(e)) {  /* edges with labels aren't multi-edges */
01166         make_flat_labeled_edge (sp, P, e, et);
01167         return;
01168     }
01169 
01170     if (et == ET_LINE) {
01171         makeSimpleFlat (e->tail, e->head, edges, ind, cnt, et);
01172         return;
01173     }
01174 
01175     tside = ED_tail_port(e).side;
01176     hside = ED_head_port(e).side;
01177     if (((tside == BOTTOM) && (hside != TOP)) ||
01178         ((hside == BOTTOM) && (tside != TOP))) {
01179         make_flat_bottom_edges (sp, P, edges, ind, cnt, e, et == ET_SPLINE);
01180         return;
01181     }
01182 
01183     tn = e->tail, hn = e->head;
01184     g = tn->graph;
01185     r = ND_rank(tn);
01186     if (r > 0) {
01187         rank_t* prevr;
01188         if (GD_has_labels(g) & EDGE_LABEL)
01189             prevr = GD_rank(g) + (r-2);
01190         else
01191             prevr = GD_rank(g) + (r-1);
01192         vspace = ND_coord_i(prevr->v[0]).y - prevr->ht1 - ND_coord_i(tn).y - GD_rank(g)[r].ht2;
01193     }
01194     else {
01195         vspace = GD_ranksep(g);
01196     }
01197     stepx = sp->Multisep / (cnt+1); 
01198     stepy = vspace / (cnt+1);
01199 
01200     makeFlatEnd (sp, P, tn, e, &tend, TRUE);
01201     makeFlatEnd (sp, P, hn, e, &hend, FALSE);
01202 
01203     for (i = 0; i < cnt; i++) {
01204         int boxn;
01205         box b;
01206         e = edges[ind + i];
01207         boxn = 0;
01208 
01209         b = tend.boxes[tend.boxn - 1];
01210         boxes[boxn].LL.x = b.LL.x; 
01211         boxes[boxn].LL.y = b.UR.y; 
01212         boxes[boxn].UR.x = b.UR.x + (i + 1) * stepx;
01213         boxes[boxn].UR.y = b.UR.y + (i + 1) * stepy;
01214         boxn++;
01215         boxes[boxn].LL.x = tend.boxes[tend.boxn - 1].LL.x; 
01216         boxes[boxn].LL.y = boxes[boxn-1].UR.y;
01217         boxes[boxn].UR.x = hend.boxes[hend.boxn - 1].UR.x;
01218         boxes[boxn].UR.y = boxes[boxn].LL.y + stepy;
01219         boxn++;
01220         b = hend.boxes[hend.boxn - 1];
01221         boxes[boxn].UR.x = b.UR.x;
01222         boxes[boxn].LL.y = b.UR.y;
01223         boxes[boxn].LL.x = b.LL.x - (i + 1) * stepx;
01224         boxes[boxn].UR.y = boxes[boxn-1].LL.y;
01225         boxn++;
01226 
01227         for (j = 0; j < tend.boxn; j++) add_box(P, tend.boxes[j]);
01228         for (j = 0; j < boxn; j++) add_box(P, boxes[j]);
01229         for (j = hend.boxn - 1; j >= 0; j--) add_box(P, hend.boxes[j]);
01230 
01231         if (et == ET_SPLINE) ps = routesplines(P, &pn);
01232         else ps = routepolylines(P, &pn);
01233         if (pn == 0)
01234             return;
01235         clip_and_install(e, e->head, ps, pn, &sinfo);
01236         P->nbox = 0;
01237     }
01238 }
01239 
01240 /* ccw:
01241  * Return true if p3 is to left of ray p1->p2
01242  */
01243 static int
01244 leftOf (point p1, point p2, point p3)
01245 {
01246     int d;
01247 
01248     d = ((p1.y - p2.y) * (p3.x - p2.x)) -
01249         ((p3.y - p2.y) * (p1.x - p2.x));
01250     return (d > 0);
01251 }
01252 
01253 /* makeLineEdge:
01254  * Create an edge as line segment. We guarantee that the points
01255  * are always drawn downwards. This means that for flipped edges,
01256  * we draw from the head to the tail. The routine returns the
01257  * end node of the edge in *hp. The points are stored in the
01258  * given array of points, and the number of points is returned.
01259  *
01260  * If the edge has a label, the edge is draw as two segments, with
01261  * the bend near the label.
01262  *
01263  * If the endpoints are on adjacent ranks, revert to usual code by
01264  * returning 0.
01265  * This is done because the usual code handles the interaction of
01266  * multiple edges better.
01267  */
01268 static int 
01269 makeLineEdge(edge_t* fe, point* points, node_t** hp)
01270 {
01271     int delr, pn;
01272     node_t* hn;
01273     node_t* tn;
01274     edge_t* e = fe;
01275     point startp, endp, lp;
01276     pointf dimen;
01277     double width, height;
01278 
01279     while (ED_edge_type(e) != NORMAL)
01280         e = ED_to_orig(e);
01281     hn = e->head;
01282     tn = e->tail;
01283     delr = ABS(ND_rank(hn)-ND_rank(tn));
01284     if ((delr == 1) || ((delr == 2) && (GD_has_labels(hn->graph) & EDGE_LABEL)))
01285         return 0;
01286     if (fe->tail == e->tail) {
01287         *hp = hn;
01288         startp = add_points(ND_coord_i(tn), ED_tail_port(e).p);
01289         endp = add_points(ND_coord_i(hn), ED_head_port(e).p);
01290     }
01291     else {
01292         *hp = tn; 
01293         startp = add_points(ND_coord_i(hn), ED_head_port(e).p);
01294         endp = add_points(ND_coord_i(tn), ED_tail_port(e).p);
01295     }
01296 
01297     if (ED_label(e)) {
01298         dimen = ED_label(e)->dimen;
01299         if (GD_flip(hn->graph)) {
01300             width = dimen.y;
01301             height = dimen.x;
01302         }
01303         else {
01304             width = dimen.x;
01305             height = dimen.y;
01306         }
01307 
01308         lp = ED_label(e)->p;
01309         if (leftOf (endp,startp,lp)) {
01310             lp.x += width/2.0;
01311             lp.y -= height/2.0;
01312         }    
01313         else {
01314             lp.x -= width/2.0;
01315             lp.y += height/2.0;
01316         }
01317 
01318         points[1] = points[0] = startp;
01319         points[2] = points[3] = points[4] = lp;
01320         points[5] = points[6] = endp;
01321         pn = 7;
01322     }
01323     else {
01324         points[1] = points[0] = startp;
01325         points[3] = points[2] = endp;
01326         pn = 4;
01327     }
01328 
01329     return pn;
01330 }
01331 
01332 /* make_regular_edge:
01333  */
01334 static void
01335 make_regular_edge(spline_info_t* sp, path * P, edge_t ** edges, int ind, int cnt, int et)
01336 {
01337     graph_t *g;
01338     node_t *tn, *hn;
01339     edge_t fwdedgea, fwdedgeb, fwdedge, *e, *fe, *le, *segfirst;
01340     point *ps;
01341     pathend_t tend, hend;
01342     box b;
01343     int boxn, sl, si, smode, i, j, dx, pn, hackflag, longedge;
01344     point points[1000], points2[1000];
01345     int pointn;
01346 
01347     sl = 0;
01348     e = edges[ind];
01349     g = e->tail->graph;
01350     hackflag = FALSE;
01351     if (ABS(ND_rank(e->tail) - ND_rank(e->head)) > 1) {
01352         fwdedgea = *e;
01353         if (ED_tree_index(e) & BWDEDGE) {
01354             MAKEFWDEDGE(&fwdedgeb, e);
01355             fwdedgea.tail = e->head;
01356             fwdedgea.u.tail_port = ED_head_port(e);
01357         } else {
01358             fwdedgeb = *e;
01359             fwdedgea.tail = e->tail;
01360         }
01361         le = getmainedge(e);
01362         while (ED_to_virt(le))
01363             le = ED_to_virt(le);
01364         fwdedgea.head = le->head;
01365         fwdedgea.u.head_port.defined = FALSE;
01366         fwdedgea.u.edge_type = VIRTUAL;
01367         fwdedgea.u.head_port.p.x = fwdedgea.u.head_port.p.y = 0;
01368         fwdedgea.u.to_orig = e;
01369         e = &fwdedgea;
01370         hackflag = TRUE;
01371     } else {
01372         if (ED_tree_index(e) & BWDEDGE) {
01373             MAKEFWDEDGE(&fwdedgea, e);
01374             e = &fwdedgea;
01375         }
01376     }
01377     fe = e;
01378 
01379     /* compute the spline points for the edge */
01380 
01381     if ((et == ET_LINE) && (pointn = makeLineEdge (fe, points, &hn))) {
01382     }
01383     else {
01384         int splines = et == ET_SPLINE;
01385         boxn = 0;
01386         pointn = 0;
01387         segfirst = e;
01388         tn = e->tail;
01389         hn = e->head;
01390         b = tend.nb = maximal_bbox(sp, tn, NULL, e);
01391         beginpath(P, e, REGULAREDGE, &tend, spline_merge(e->tail));
01392         b.UR.y = tend.boxes[tend.boxn - 1].UR.y;
01393         b.LL.y = tend.boxes[tend.boxn - 1].LL.y;
01394         b = makeregularend(b, BOTTOM,
01395                    ND_coord_i(tn).y - GD_rank(tn->graph)[ND_rank(tn)].ht1);
01396         if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
01397             tend.boxes[tend.boxn++] = b;
01398         longedge = 0;
01399         smode = FALSE, si = -1;
01400         while (ND_node_type(hn) == VIRTUAL && !sinfo.splineMerge(hn)) {
01401             longedge = 1;
01402             boxes[boxn++] = rank_box(sp, g, ND_rank(tn));
01403             if (!smode
01404                 && ((sl = straight_len(hn)) >=
01405                 ((GD_has_labels(g) & EDGE_LABEL) ? 4 + 1 : 2 + 1))) {
01406                 smode = TRUE;
01407                 si = 1, sl -= 2;
01408             }
01409             if (!smode || si > 0) {
01410                 si--;
01411                 boxes[boxn++] = maximal_bbox(sp, hn, e, ND_out(hn).list[0]);
01412                 e = ND_out(hn).list[0];
01413                 tn = e->tail;
01414                 hn = e->head;
01415                 continue;
01416             }
01417             hend.nb = maximal_bbox(sp, hn, e, ND_out(hn).list[0]);
01418             endpath(P, e, REGULAREDGE, &hend, spline_merge(e->head));
01419             b = makeregularend(hend.boxes[hend.boxn - 1], TOP,
01420                        ND_coord_i(hn).y + GD_rank(hn->graph)[ND_rank(hn)].ht2);
01421             if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
01422                 hend.boxes[hend.boxn++] = b;
01423             P->end.theta = M_PI / 2, P->end.constrained = TRUE;
01424             completeregularpath(P, segfirst, e, &tend, &hend, boxes, boxn, 1);
01425             if (splines) ps = routesplines(P, &pn);
01426             else {
01427                 ps = routepolylines (P, &pn);
01428                 if ((et == ET_LINE) && (pn > 4)) {
01429                     ps[1] = ps[0];
01430                     ps[3] = ps[2] = ps[pn-1];
01431                     pn = 4;
01432                 }
01433             }
01434             if (pn == 0)
01435                 return;
01436             for (i = 0; i < pn; i++)
01437                 points[pointn++] = ps[i];
01438             e = straight_path(ND_out(hn).list[0], sl, points, &pointn);
01439             recover_slack(segfirst, P);
01440             segfirst = e;
01441             tn = e->tail;
01442             hn = e->head;
01443             boxn = 0;
01444             tend.nb = maximal_bbox(sp, tn, ND_in(tn).list[0], e);
01445             beginpath(P, e, REGULAREDGE, &tend, spline_merge(e->tail));
01446             b = makeregularend(tend.boxes[tend.boxn - 1], BOTTOM,
01447                        ND_coord_i(tn).y - GD_rank(tn->graph)[ND_rank(tn)].ht1);
01448             if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
01449                 tend.boxes[tend.boxn++] = b;
01450             P->start.theta = -M_PI / 2, P->start.constrained = TRUE;
01451             smode = FALSE;
01452         }
01453         boxes[boxn++] = rank_box(sp, g, ND_rank(tn));
01454         b = hend.nb = maximal_bbox(sp, hn, e, NULL);
01455         endpath(P, hackflag ? &fwdedgeb : e, REGULAREDGE, &hend,
01456                 spline_merge(e->head));
01457         b.UR.y = hend.boxes[hend.boxn - 1].UR.y;
01458         b.LL.y = hend.boxes[hend.boxn - 1].LL.y;
01459         b = makeregularend(b, TOP,
01460                    ND_coord_i(hn).y + GD_rank(hn->graph)[ND_rank(hn)].ht2);
01461         if (b.LL.x < b.UR.x && b.LL.y < b.UR.y)
01462             hend.boxes[hend.boxn++] = b;
01463         completeregularpath(P, segfirst, e, &tend, &hend, boxes, boxn,
01464                         longedge);
01465         if (splines) ps = routesplines(P, &pn);
01466         else ps = routepolylines (P, &pn);
01467         if ((et == ET_LINE) && (pn > 4)) {
01468             /* Here we have used the polyline case to handle
01469              * an edge between two nodes on adjacent ranks. If the
01470              * results really is a polyline, straighten it.
01471              */
01472             ps[1] = ps[0];
01473             ps[3] = ps[2] = ps[pn-1];
01474             pn = 4;
01475         }
01476         if (pn == 0)
01477             return;
01478         for (i = 0; i < pn; i++)
01479             points[pointn++] = ps[i];
01480         recover_slack(segfirst, P);
01481         hn = hackflag ? fwdedgeb.head : e->head;
01482     }
01483 
01484     /* make copies of the spline points, one per multi-edge */
01485 
01486     if (cnt == 1) {
01487         clip_and_install(fe, hn, points, pointn, &sinfo);
01488         return;
01489     }
01490     dx = sp->Multisep * (cnt - 1) / 2;
01491     for (i = 1; i < pointn - 1; i++)
01492         points[i].x -= dx;
01493     for (i = 0; i < pointn; i++)
01494         points2[i] = points[i];
01495     clip_and_install(fe, hn, points2, pointn, &sinfo);
01496     for (j = 1; j < cnt; j++) {
01497         e = edges[ind + j];
01498         if (ED_tree_index(e) & BWDEDGE) {
01499             MAKEFWDEDGE(&fwdedge, e);
01500             e = &fwdedge;
01501         }
01502         for (i = 1; i < pointn - 1; i++)
01503             points[i].x += sp->Multisep;
01504         for (i = 0; i < pointn; i++)
01505             points2[i] = points[i];
01506         clip_and_install(e, e->head, points2, pointn, &sinfo);
01507     }
01508 }
01509 
01510 /* flat edges */
01511 
01512 #ifdef OBSOLETE
01513 static void 
01514 chooseflatsides(pathend_t* tendp, pathend_t *hendp,
01515                 int* tsidep, int* hsidep, int* msidep, int* tdirp, 
01516                 int* hdirp, int* crossp)
01517 {
01518     int i;
01519 
01520     for (i = 0; i < 16; i++)
01521         if ((flatsidemap[i][0] & tendp->sidemask) &&
01522             (flatsidemap[i][1] & hendp->sidemask))
01523             break;
01524     if (i == 16)
01525         abort();
01526     *tsidep = flatsidemap[i][0], *hsidep = flatsidemap[i][1];
01527     *msidep = flatsidemap[i][2];
01528     *tdirp = flatsidemap[i][3], *hdirp = flatsidemap[i][4];
01529     *crossp = flatsidemap[i][5];
01530 }
01531 
01532 static void
01533 completeflatpath(path * P,
01534                  pathend_t * tendp, pathend_t * hendp,
01535                  int tside, int hside, int mside, int tdir, int hdir,
01536                  box * arg_lb, box * arg_rb, int w, int h)
01537 {
01538     int i, side, boxn;
01539     box boxes[8];
01540     box tb, hb;
01541     box lb, rb;
01542     lb = *arg_lb;
01543     rb = *arg_rb;
01544 
01545     tb = makeflatend(tendp->boxes[tendp->boxn - 1], tside, tdir, lb);
01546     hb = makeflatend(hendp->boxes[hendp->boxn - 1], hside, OTHERDIR(hdir),
01547                      rb);
01548 
01549     boxn = 0;
01550     for (side = tside;; side = NEXTSIDE(side, tdir)) {
01551         boxes[boxn++] = makeflatcomponent(lb, rb, side,
01552                                           (side == mside) ? 0 : -1, tdir,
01553                                           w, h);
01554         if (side == mside)
01555             break;
01556     }
01557     if (mside == RIGHT)
01558         mside = LEFT;
01559     if (mside != hside) {
01560         for (side = NEXTSIDE(mside, hdir);; side = NEXTSIDE(side, hdir)) {
01561             boxes[boxn++] = makeflatcomponent(lb, rb, side, 1, hdir, w, h);
01562             if (side == hside)
01563                 break;
01564         }
01565     }
01566 
01567     for (i = 0; i < tendp->boxn; i++)
01568         add_box(P, tendp->boxes[i]);
01569     if (tb.LL.x != tb.UR.x && tb.LL.y != tb.UR.y)
01570         add_box(P, tb);
01571     for (i = 0; i < boxn; i++)
01572         add_box(P, boxes[i]);
01573     if (hb.LL.x != hb.UR.x && hb.LL.y != hb.UR.y)
01574         add_box(P, hb);
01575     for (i = hendp->boxn - 1; i >= 0; i--)
01576         add_box(P, hendp->boxes[i]);
01577 }
01578 
01579 static box 
01580 makeflatend(box b, int side, int dir, box bb)
01581 {
01582     box eb = { {0, 0}, {0, 0} };
01583 
01584     switch (side) {
01585     case BOTTOM:
01586         eb = boxof(b.LL.x, bb.LL.y, b.UR.x, b.LL.y);
01587         if (dir == CCW)
01588             eb.UR.x += (bb.UR.x - b.UR.x) / 2;
01589         else
01590             eb.LL.x -= (b.LL.x - bb.LL.x) / 2;
01591         break;
01592     case RIGHT:
01593         eb = boxof(b.UR.x, b.LL.y, bb.UR.x, b.UR.y);
01594         if (dir == CCW)
01595             eb.UR.y += (bb.UR.y - b.UR.y) / 2;
01596         else
01597             eb.LL.y -= (b.LL.y - bb.LL.y) / 2;
01598         break;
01599     case TOP:
01600         eb = boxof(b.LL.x, b.UR.y, b.UR.x, bb.UR.y);
01601         if (dir == CCW)
01602             eb.LL.x -= (b.LL.x - bb.LL.x) / 2;
01603         else
01604             eb.UR.x += (bb.UR.x - b.UR.x) / 2;
01605         break;
01606     case LEFT:
01607         eb = boxof(bb.LL.x, b.LL.y, b.LL.x, b.UR.y);
01608         if (dir == CCW)
01609             eb.LL.y -= (bb.UR.y - b.UR.y) / 2;
01610         else
01611             eb.UR.y += (b.LL.y - bb.LL.y) / 2;
01612         break;
01613     }
01614     return eb;
01615 }
01616 
01617 static box makeflatcomponent(lb, rb, side, mode, dir, w, h)
01618 box lb, rb;
01619 int side, mode, dir, w, h;
01620 {
01621     box b = { {0, 0}, {0, 0} };
01622 
01623     /* mode == -1 means use left box, 1 means use right box
01624        and 0 means use mostly the left box */
01625 
01626     switch (side) {
01627     case BOTTOM:
01628         b.LL.x = lb.LL.x - w, b.UR.x = rb.UR.x + w;
01629         if (mode <= 0)
01630             b.LL.y = lb.LL.y - h, b.UR.y = lb.LL.y;
01631         else
01632             b.LL.y = rb.LL.y - h, b.UR.y = rb.LL.y;
01633         break;
01634     case RIGHT:
01635         if (mode == -1) {
01636             b.LL.x = lb.UR.x, b.UR.x = lb.UR.x + w;
01637             b.LL.y = lb.LL.y, b.UR.y = lb.UR.y;
01638         } else if (mode == 0) {
01639             b.LL.x = lb.UR.x, b.UR.x = lb.UR.x + w;
01640             if (dir == CCW)
01641                 b.LL.y = lb.LL.y, b.UR.y = rb.UR.y;
01642             else
01643                 b.LL.y = rb.LL.y, b.UR.y = lb.UR.y;
01644         } else {
01645             b.LL.x = rb.UR.x, b.UR.x = rb.UR.x + w;
01646             b.LL.y = rb.LL.y, b.UR.y = rb.UR.y;
01647         }
01648         break;
01649     case TOP:
01650         b.LL.x = lb.LL.x - w, b.UR.x = rb.UR.x + w;
01651         if (mode <= 0)
01652             b.LL.y = lb.UR.y, b.UR.y = lb.UR.y + h;
01653         else
01654             b.LL.y = rb.UR.y, b.UR.y = rb.UR.y + h;
01655         break;
01656     case LEFT:
01657         if (mode == -1) {
01658             b.LL.x = lb.LL.x - w, b.UR.x = lb.LL.x;
01659             b.LL.y = lb.LL.y, b.UR.y = lb.UR.y;
01660         } else if (mode == 0) {
01661             b.LL.x = lb.LL.x - w, b.UR.x = lb.LL.x;
01662             if (dir == CCW)
01663                 b.LL.y = lb.LL.y, b.UR.y = rb.UR.y;
01664             else
01665                 b.LL.y = rb.LL.y, b.UR.y = lb.UR.y;
01666         } else {
01667             b.LL.x = rb.LL.x - w, b.UR.x = rb.LL.x;
01668             b.LL.y = rb.LL.y, b.UR.y = rb.UR.y;
01669         }
01670         break;
01671     }
01672     return b;
01673 }
01674 static void
01675 completeflatpath(path* P, pathend_t* tendp, pathend_t* hendp,
01676                  box* lbp, box* rbp, int w, int h)
01677 {
01678     int i;
01679     box wbox;
01680     box tb, hb;
01681     box lb, rb;
01682     lb = *lbp;
01683     rb = *rbp;
01684 
01685     tb = makeflatend(tendp->boxes[tendp->boxn - 1], TOP, CW, lb);
01686     hb = makeflatend(hendp->boxes[hendp->boxn - 1], TOP, CCW, rb);
01687 
01688     wbox = makeflatcomponent(lb, rb, TOP, 0, CW, w, h);
01689 
01690     for (i = 0; i < tendp->boxn; i++)
01691         add_box(P, tendp->boxes[i]);
01692     add_box(P, tb);
01693     add_box(P, wbox);
01694     for (i = hendp->boxn - 1; i >= 0; i--)
01695         add_box(P, hendp->boxes[i]);
01696 }
01697 #endif
01698 
01699 /* regular edges */
01700 
01701 #define DONT_WANT_ANY_ENDPOINT_PATH_REFINEMENT
01702 #ifdef DONT_WANT_ANY_ENDPOINT_PATH_REFINEMENT
01703 static void
01704 completeregularpath(path * P, edge_t * first, edge_t * last,
01705                     pathend_t * tendp, pathend_t * hendp, box * boxes,
01706                     int boxn, int flag)
01707 {
01708     edge_t *uleft, *uright, *lleft, *lright;
01709     int i, fb, lb;
01710     splines *spl;
01711     point *pp;
01712     int pn;
01713 
01714     fb = lb = -1;
01715     uleft = uright = NULL;
01716     uleft = top_bound(first, -1), uright = top_bound(first, 1);
01717     if (uleft) {
01718         spl = getsplinepoints(uleft);
01719         pp = spl->list[0].list, pn = spl->list[0].size;
01720         P->ulpp = &pp[0];
01721     }
01722     if (uright) {
01723         spl = getsplinepoints(uright);
01724         pp = spl->list[0].list, pn = spl->list[0].size;
01725         P->urpp = &pp[0];
01726     }
01727     lleft = lright = NULL;
01728     lleft = bot_bound(last, -1), lright = bot_bound(last, 1);
01729     if (lleft) {
01730         spl = getsplinepoints(lleft);
01731         pp = spl->list[spl->size - 1].list, pn =
01732             spl->list[spl->size - 1].size;
01733         P->llpp = &pp[pn - 1];
01734     }
01735     if (lright) {
01736         spl = getsplinepoints(lright);
01737         pp = spl->list[spl->size - 1].list, pn =
01738             spl->list[spl->size - 1].size;
01739         P->lrpp = &pp[pn - 1];
01740     }
01741     for (i = 0; i < tendp->boxn; i++)
01742         add_box(P, tendp->boxes[i]);
01743     fb = P->nbox + 1;
01744     lb = fb + boxn - 3;
01745     for (i = 0; i < boxn; i++)
01746         add_box(P, boxes[i]);
01747     for (i = hendp->boxn - 1; i >= 0; i--)
01748         add_box(P, hendp->boxes[i]);
01749     adjustregularpath(P, fb, lb);
01750 }
01751 #else
01752 void refineregularends(edge_t * left, edge_t * right, pathend_t * endp,
01753                        int dir, box b, box * boxes, int *boxnp);
01754 
01755 /* box subdivision is obsolete, I think... ek */
01756 static void
01757 completeregularpath(path * P, edge_t * first, edge_t * last,
01758                     pathend_t * tendp, pathend_t * hendp, box * boxes,
01759                     int boxn, int flag)
01760 {
01761     edge_t *uleft, *uright, *lleft, *lright;
01762     box uboxes[NSUB], lboxes[NSUB];
01763     box b;
01764     int uboxn, lboxn, i, y, fb, lb;
01765 
01766     fb = lb = -1;
01767     uleft = uright = NULL;
01768     if (flag || ND_rank(first->tail) + 1 != ND_rank(last->head))
01769         uleft = top_bound(first, -1), uright = top_bound(first, 1);
01770     refineregularends(uleft, uright, tendp, 1, boxes[0], uboxes, &uboxn);
01771     lleft = lright = NULL;
01772     if (flag || ND_rank(first->tail) + 1 != ND_rank(last->head))
01773         lleft = bot_bound(last, -1), lright = bot_bound(last, 1);
01774     refineregularends(lleft, lright, hendp, -1, boxes[boxn - 1], lboxes,
01775                       &lboxn);
01776     for (i = 0; i < tendp->boxn; i++)
01777         add_box(P, tendp->boxes[i]);
01778     if (ND_rank(first->tail) + 1 == ND_rank(last->head)) {
01779         if ((!uleft && !uright) && (lleft || lright)) {
01780             b = boxes[0];
01781             y = b.UR.y - b.LL.y;
01782             for (i = 0; i < NSUB; i++) {
01783                 uboxes[i] = b;
01784                 uboxes[i].UR.y = b.UR.y - y * i / NSUB;
01785                 uboxes[i].LL.y = b.UR.y - y * (i + 1) / NSUB;
01786             }
01787             uboxn = NSUB;
01788         } else if ((uleft || uright) && (!lleft && !lright)) {
01789             b = boxes[boxn - 1];
01790             y = b.UR.y - b.LL.y;
01791             for (i = 0; i < NSUB; i++) {
01792                 lboxes[i] = b;
01793                 lboxes[i].UR.y = b.UR.y - y * i / NSUB;
01794                 lboxes[i].LL.y = b.UR.y - y * (i + 1) / NSUB;
01795             }
01796             lboxn = NSUB;
01797         }
01798         for (i = 0; i < uboxn; i++) {
01799             uboxes[i].LL.x = MAX(uboxes[i].LL.x, lboxes[i].LL.x);
01800             uboxes[i].UR.x = MIN(uboxes[i].UR.x, lboxes[i].UR.x);
01801         }
01802         for (i = 0; i < uboxn; i++)
01803             add_box(P, uboxes[i]);
01804     } else {
01805         for (i = 0; i < uboxn; i++)
01806             add_box(P, uboxes[i]);
01807         fb = P->nbox;
01808         lb = fb + boxn - 3;
01809         for (i = 1; i < boxn - 1; i++)
01810             add_box(P, boxes[i]);
01811         for (i = 0; i < lboxn; i++)
01812             add_box(P, lboxes[i]);
01813     }
01814     for (i = hendp->boxn - 1; i >= 0; i--)
01815         add_box(P, hendp->boxes[i]);
01816     adjustregularpath(P, fb, lb);
01817 }
01818 #endif
01819 
01820 /* makeregularend:
01821  * Add box to fill between node and interrank space. Needed because
01822  * nodes in a given rank can differ in height.
01823  * for now, regular edges always go from top to bottom 
01824  */
01825 static box makeregularend(box b, int side, int y)
01826 {
01827     box newb;
01828     switch (side) {
01829     case BOTTOM:
01830         newb = boxof(b.LL.x, y, b.UR.x, b.LL.y);
01831         break;
01832     case TOP:
01833         newb = boxof(b.LL.x, b.UR.y, b.UR.x, y);
01834         break;
01835     }
01836     return newb;
01837 }
01838 
01839 #ifndef DONT_WANT_ANY_ENDPOINT_PATH_REFINEMENT
01840 void refineregularends(left, right, endp, dir, b, boxes, boxnp)
01841 edge_t *left, *right;
01842 pathend_t *endp;
01843 int dir;
01844 box b;
01845 box *boxes;
01846 int *boxnp;
01847 {
01848     splines *lspls, *rspls;
01849     point pp, cp;
01850     box eb;
01851     box *bp;
01852     int y, i, j, k;
01853     int nsub;
01854 
01855     y = b.UR.y - b.LL.y;
01856     if ((y == 1) || (!left && !right)) {
01857         boxes[0] = b;
01858         *boxnp = 1;
01859         return;
01860     }
01861     nsub = MIN(NSUB, y);
01862     for (i = 0; i < nsub; i++) {
01863         boxes[i] = b;
01864         boxes[i].UR.y = b.UR.y - y * i / nsub;
01865         boxes[i].LL.y = b.UR.y - y * (i + 1) / nsub;
01866         if (boxes[i].UR.y == boxes[i].LL.y)
01867             abort();
01868     }
01869     *boxnp = nsub;
01870     /* only break big boxes */
01871     for (j = 0; j < endp->boxn; j++) {
01872         eb = endp->boxes[j];
01873         y = eb.UR.y - eb.LL.y;
01874 #ifdef STEVE_AND_LEFTY_GRASPING_AT_STRAWS
01875         if (y < 15)
01876             continue;
01877 #else
01878         if (y < nsub)
01879             continue;
01880 #endif
01881         for (k = endp->boxn - 1; k > j; k--)
01882             endp->boxes[k + (nsub - 1)] = endp->boxes[k];
01883         for (i = 0; i < nsub; i++) {
01884             bp = &endp->boxes[j + ((dir == 1) ? i : (nsub - i - 1))];
01885             *bp = eb;
01886             bp->UR.y = eb.UR.y - y * i / nsub;
01887             bp->LL.y = eb.UR.y - y * (i + 1) / nsub;
01888             if (bp->UR.y == bp->LL.y)
01889                 abort();
01890         }
01891         endp->boxn += (nsub - 1);
01892         j += nsub - 1;
01893     }
01894     if (left) {
01895         lspls = getsplinepoints(left);
01896         pp = spline_at_y(lspls, boxes[0].UR.y);
01897         for (i = 0; i < nsub; i++) {
01898             cp = spline_at_y(lspls, boxes[i].LL.y);
01899             /*boxes[i].LL.x = AVG (pp.x, cp.x); */
01900             boxes[i].LL.x = MAX(pp.x, cp.x);
01901             pp = cp;
01902         }
01903         pp = spline_at_y(lspls, (dir == 1) ?
01904                          endp->boxes[1].UR.y : endp->boxes[1].LL.y);
01905         for (i = 1; i < endp->boxn; i++) {
01906             cp = spline_at_y(lspls, (dir == 1) ?
01907                              endp->boxes[i].LL.y : endp->boxes[i].UR.y);
01908             endp->boxes[i].LL.x = MIN(endp->nb.UR.x, MAX(pp.x, cp.x));
01909             pp = cp;
01910         }
01911         i = (dir == 1) ? 0 : *boxnp - 1;
01912         if (boxes[i].LL.x > endp->boxes[endp->boxn - 1].UR.x - MINW)
01913             boxes[i].LL.x = endp->boxes[endp->boxn - 1].UR.x - MINW;
01914     }
01915     if (right) {
01916         rspls = getsplinepoints(right);
01917         pp = spline_at_y(rspls, boxes[0].UR.y);
01918         for (i = 0; i < nsub; i++) {
01919             cp = spline_at_y(rspls, boxes[i].LL.y);
01920             /*boxes[i].UR.x = AVG (pp.x, cp.x); */
01921             boxes[i].UR.x = AVG(pp.x, cp.x);
01922             pp = cp;
01923         }
01924         pp = spline_at_y(rspls, (dir == 1) ?
01925                          endp->boxes[1].UR.y : endp->boxes[1].LL.y);
01926         for (i = 1; i < endp->boxn; i++) {
01927             cp = spline_at_y(rspls, (dir == 1) ?
01928                              endp->boxes[i].LL.y : endp->boxes[i].UR.y);
01929             endp->boxes[i].UR.x = MAX(endp->nb.LL.x, AVG(pp.x, cp.x));
01930             pp = cp;
01931         }
01932         i = (dir == 1) ? 0 : *boxnp - 1;
01933         if (boxes[i].UR.x < endp->boxes[endp->boxn - 1].LL.x + MINW)
01934             boxes[i].UR.x = endp->boxes[endp->boxn - 1].LL.x + MINW;
01935     }
01936 }
01937 #endif
01938 
01939 /* adjustregularpath:
01940  * make sure the path is wide enough.
01941  * the % 2 was so that in rank boxes would only be grown if
01942  * they were == 0 while inter-rank boxes could be stretched to a min
01943  * width.
01944  * The list of boxes has three parts: tail boxes, path boxes, and head
01945  * boxes. (Note that because of back edges, the tail boxes might actually
01946  * belong to the head node, and vice versa.) fb is the index of the
01947  * first interrank path box and lb is the last interrank path box.
01948  * If fb > lb, there are none.
01949  *
01950  * The second for loop was added by ek long ago, and apparently is intended
01951  * to guarantee an overlap between adjacent boxes of at least MINW.
01952  * It doesn't do this, and the ifdef'ed part has the potential of moving 
01953  * a box within a node for more complex paths.
01954  */
01955 static void adjustregularpath(path * P, int fb, int lb)
01956 {
01957     box *bp1, *bp2;
01958     int i, x;
01959 
01960     for (i = fb-1; i < lb+1; i++) {
01961         bp1 = &P->boxes[i];
01962         if ((i - fb) % 2 == 0) {
01963             if (bp1->LL.x >= bp1->UR.x) {
01964                 x = (bp1->LL.x + bp1->UR.x) / 2;
01965                 bp1->LL.x = x - HALFMINW, bp1->UR.x = x + HALFMINW;
01966             }
01967         } else {
01968             if (bp1->LL.x + MINW > bp1->UR.x) {
01969                 x = (bp1->LL.x + bp1->UR.x) / 2;
01970                 bp1->LL.x = x - HALFMINW, bp1->UR.x = x + HALFMINW;
01971             }
01972         }
01973     }
01974     for (i = 0; i < P->nbox - 1; i++) {
01975         bp1 = &P->boxes[i], bp2 = &P->boxes[i + 1];
01976         if (i >= fb && i <= lb && (i - fb) % 2 == 0) {
01977             if (bp1->LL.x + MINW > bp2->UR.x)
01978                 bp2->UR.x = bp1->LL.x + MINW;
01979             if (bp1->UR.x - MINW < bp2->LL.x)
01980                 bp2->LL.x = bp1->UR.x - MINW;
01981         } else if (i + 1 >= fb && i < lb && (i + 1 - fb) % 2 == 0) {
01982             if (bp1->LL.x + MINW > bp2->UR.x)
01983                 bp1->LL.x = bp2->UR.x - MINW;
01984             if (bp1->UR.x - MINW < bp2->LL.x)
01985                 bp1->UR.x = bp2->LL.x + MINW;
01986         } 
01987 #ifdef OLD
01988         else {
01989             if (bp1->LL.x + MINW > bp2->UR.x) {
01990                 x = (bp1->LL.x + bp2->UR.x) / 2;
01991                 bp1->LL.x = x - HALFMINW;
01992                 bp2->UR.x = x + HALFMINW;
01993             }
01994             if (bp1->UR.x - MINW < bp2->LL.x) {
01995                 x = (bp1->UR.x + bp2->LL.x) / 2;
01996                 bp1->UR.x = x + HALFMINW;
01997                 bp2->LL.x = x - HALFMINW;
01998             }
01999         }
02000 #endif
02001     }
02002 }
02003 
02004 static box rank_box(spline_info_t* sp, graph_t * g, int r)
02005 {
02006     box b;
02007     node_t /* *right0, *right1, */  * left0, *left1;
02008 
02009     b = sp->Rank_box[r];
02010     if (b.LL.x == b.UR.x) {
02011         left0 = GD_rank(g)[r].v[0];
02012         /* right0 = GD_rank(g)[r].v[GD_rank(g)[r].n - 1]; */
02013         left1 = GD_rank(g)[r + 1].v[0];
02014         /* right1 = GD_rank(g)[r + 1].v[GD_rank(g)[r + 1].n - 1]; */
02015         b.LL.x = sp->LeftBound;
02016         b.LL.y = ND_coord_i(left1).y + GD_rank(g)[r + 1].ht2;
02017         b.UR.x = sp->RightBound;
02018         b.UR.y = ND_coord_i(left0).y - GD_rank(g)[r].ht1;
02019         sp->Rank_box[r] = b;
02020     }
02021     return b;
02022 }
02023 
02024 /* returns count of vertically aligned edges starting at n */
02025 static int straight_len(node_t * n)
02026 {
02027     int cnt = 0;
02028     node_t *v;
02029 
02030     v = n;
02031     while (1) {
02032         v = ND_out(v).list[0]->head;
02033         if (ND_node_type(v) != VIRTUAL)
02034             break;
02035         if ((ND_out(v).size != 1) || (ND_in(v).size != 1))
02036             break;
02037         if (ND_coord_i(v).x != ND_coord_i(n).x)
02038             break;
02039         cnt++;
02040     }
02041     return cnt;
02042 }
02043 
02044 static edge_t *straight_path(edge_t * e, int cnt, point * plist, int *np)
02045 {
02046     int n = *np;
02047     edge_t *f = e;
02048 
02049     while (cnt--)
02050         f = ND_out(f->head).list[0];
02051     plist[(*np)++] = plist[n - 1];
02052     plist[(*np)++] = plist[n - 1];
02053     plist[(*np)] = ND_coord_i(f->tail); /* will be overwritten by next spline */
02054     return f;
02055 }
02056 
02057 static void recover_slack(edge_t * e, path * p)
02058 {
02059     int b;
02060     node_t *vn;
02061 
02062     b = 0;                      /* skip first rank box */
02063     for (vn = e->head;
02064          ND_node_type(vn) == VIRTUAL && !sinfo.splineMerge(vn);
02065          vn = ND_out(vn).list[0]->head) {
02066         while ((b < p->nbox) && (p->boxes[b].LL.y > ND_coord_i(vn).y))
02067             b++;
02068         if (b >= p->nbox)
02069             break;
02070         if (p->boxes[b].UR.y < ND_coord_i(vn).y)
02071             continue;
02072         if (ND_label(vn))
02073             resize_vn(vn, p->boxes[b].LL.x, p->boxes[b].UR.x,
02074                       p->boxes[b].UR.x + ND_rw_i(vn));
02075         else
02076             resize_vn(vn, p->boxes[b].LL.x, (p->boxes[b].LL.x +
02077                                              p->boxes[b].UR.x) / 2,
02078                       p->boxes[b].UR.x);
02079     }
02080 }
02081 
02082 static void resize_vn(vn, lx, cx, rx)
02083 node_t *vn;
02084 int lx, cx, rx;
02085 {
02086     ND_coord_i(vn).x = cx;
02087     ND_lw_i(vn) = cx - lx, ND_rw_i(vn) = rx - cx;
02088 }
02089 
02090 /* side > 0 means right. side < 0 means left */
02091 static edge_t *top_bound(edge_t * e, int side)
02092 {
02093     edge_t *f, *ans = NULL;
02094     int i;
02095 
02096     for (i = 0; (f = ND_out(e->tail).list[i]); i++) {
02097 #if 0                           /* were we out of our minds? */
02098         if (ED_tail_port(e).p.x != ED_tail_port(f).p.x)
02099             continue;
02100 #endif
02101         if (side * (ND_order(f->head) - ND_order(e->head)) <= 0)
02102             continue;
02103         if ((ED_spl(f) == NULL)
02104             && ((ED_to_orig(f) == NULL) || (ED_to_orig(f)->u.spl == NULL)))
02105             continue;
02106         if ((ans == NULL)
02107             || (side * (ND_order(ans->head) - ND_order(f->head)) > 0))
02108             ans = f;
02109     }
02110     return ans;
02111 }
02112 
02113 static edge_t *bot_bound(edge_t * e, int side)
02114 {
02115     edge_t *f, *ans = NULL;
02116     int i;
02117 
02118     for (i = 0; (f = ND_in(e->head).list[i]); i++) {
02119 #if 0                           /* same here */
02120         if (ED_head_port(e).p.x != ED_head_port(f).p.x)
02121             continue;
02122 #endif
02123         if (side * (ND_order(f->tail) - ND_order(e->tail)) <= 0)
02124             continue;
02125         if ((ED_spl(f) == NULL)
02126             && ((ED_to_orig(f) == NULL) || (ED_to_orig(f)->u.spl == NULL)))
02127             continue;
02128         if ((ans == NULL)
02129             || (side * (ND_order(ans->tail) - ND_order(f->tail)) > 0))
02130             ans = f;
02131     }
02132     return ans;
02133 }
02134 
02135 point closest(splines * spl, point p)
02136 {
02137     int i, j, k, besti, bestj;
02138     double bestdist2, d2, dlow2, dhigh2; /* squares of distance */
02139     double low, high, t;
02140     pointf c[4], pt2, pt;
02141     point rv;
02142     bezier bz;
02143 
02144     besti = bestj = -1;
02145     bestdist2 = 1e+38;
02146     P2PF(p, pt);
02147     for (i = 0; i < spl->size; i++) {
02148         bz = spl->list[i];
02149         for (j = 0; j < bz.size; j++) {
02150             pointf b;
02151 
02152             b.x = bz.list[j].x;
02153             b.y = bz.list[j].y;
02154             d2 = DIST2(b, pt);
02155             if ((bestj == -1) || (d2 < bestdist2)) {
02156                 besti = i;
02157                 bestj = j;
02158                 bestdist2 = d2;
02159             }
02160         }
02161     }
02162 
02163     bz = spl->list[besti];
02164     j = bestj / 3;
02165     if (j >= spl->size)
02166         j--;
02167     for (k = 0; k < 4; k++) {
02168         c[k].x = bz.list[j + k].x;
02169         c[k].y = bz.list[j + k].y;
02170     }
02171     low = 0.0;
02172     high = 1.0;
02173     dlow2 = DIST2(c[0], pt);
02174     dhigh2 = DIST2(c[3], pt);
02175     do {
02176         t = (low + high) / 2.0;
02177         pt2 = Bezier(c, 3, t, NULL, NULL);
02178         if (fabs(dlow2 - dhigh2) < 1.0)
02179             break;
02180         if (fabs(high - low) < .00001)
02181             break;
02182         if (dlow2 < dhigh2) {
02183             high = t;
02184             dhigh2 = DIST2(pt2, pt);
02185         } else {
02186             low = t;
02187             dlow2 = DIST2(pt2, pt);
02188         }
02189     } while (1);
02190     PF2P(pt2, rv);
02191     return rv;
02192 }
02193 
02194 /* common routines */
02195 
02196 static int cl_vninside(graph_t * cl, node_t * n)
02197 {
02198     return (BETWEEN(GD_bb(cl).LL.x, ND_coord_i(n).x, GD_bb(cl).UR.x) &&
02199             BETWEEN(GD_bb(cl).LL.y, ND_coord_i(n).y, GD_bb(cl).UR.y));
02200 }
02201 
02202 /* returns the cluster of (adj) that interferes with n,
02203  */
02204 static graph_t *cl_bound(n, adj)
02205 node_t *n, *adj;
02206 {
02207     graph_t *rv, *cl, *tcl, *hcl;
02208     edge_t *orig;
02209 
02210     rv = NULL;
02211     if (ND_node_type(n) == NORMAL)
02212         tcl = hcl = ND_clust(n);
02213     else {
02214         orig = ND_out(n).list[0]->u.to_orig;
02215         tcl = ND_clust(orig->tail);
02216         hcl = ND_clust(orig->head);
02217     }
02218     if (ND_node_type(adj) == NORMAL) {
02219         cl = ND_clust(adj);
02220         if (cl && (cl != tcl) && (cl != hcl))
02221             rv = cl;
02222     } else {
02223         orig = ED_to_orig(ND_out(adj).list[0]);
02224         cl = ND_clust(orig->tail);
02225         if (cl && (cl != tcl) && (cl != hcl) && cl_vninside(cl, adj))
02226             rv = cl;
02227         else {
02228             cl = ND_clust(orig->head);
02229             if (cl && (cl != tcl) && (cl != hcl) && cl_vninside(cl, adj))
02230                 rv = cl;
02231         }
02232     }
02233     return rv;
02234 }
02235 
02236 /* maximal_bbox:
02237  * Return an initial bounding box to be used for building the
02238  * beginning or ending of the path of boxes.
02239  * Height reflects height of tallest node on rank.
02240  * The extra space provided by FUDGE allows begin/endpath to create a box
02241  * FUDGE-2 away from the node, so the routing can avoid the node and the
02242  * box is at least 2 wide.
02243  */
02244 #define FUDGE 4
02245 
02246 static box maximal_bbox(spline_info_t* sp, node_t* vn, edge_t* ie, edge_t* oe)
02247 {
02248     int nb, b;
02249     graph_t *g = vn->graph, *left_cl, *right_cl;
02250     node_t *left, *right;
02251     box rv;
02252 
02253     left_cl = right_cl = NULL;
02254 
02255     /* give this node all the available space up to its neighbors */
02256     b = ND_coord_i(vn).x - ND_lw_i(vn) - FUDGE;
02257     if ((left = neighbor(vn, ie, oe, -1))) {
02258         if ((left_cl = cl_bound(vn, left)))
02259             nb = GD_bb(left_cl).UR.x + sp->Splinesep;
02260         else {
02261             nb = ND_coord_i(left).x + ND_mval(left);
02262             if (ND_node_type(left) == NORMAL)
02263                 nb += GD_nodesep(g) / 2;
02264             else
02265                 nb += sp->Splinesep;
02266         }
02267         if (nb < b)
02268             b = nb;
02269         rv.LL.x = b;
02270     } else
02271         rv.LL.x = MIN(b, sp->LeftBound);
02272 
02273     /* we have to leave room for our own label! */
02274     if ((ND_node_type(vn) == VIRTUAL) && (ND_label(vn)))
02275         b = ND_coord_i(vn).x + 10;
02276     else
02277         b = ND_coord_i(vn).x + ND_rw_i(vn) + FUDGE;
02278     if ((right = neighbor(vn, ie, oe, 1))) {
02279         if ((right_cl = cl_bound(vn, right)))
02280             nb = GD_bb(right_cl).LL.x - sp->Splinesep;
02281         else {
02282             nb = ND_coord_i(right).x - ND_lw_i(right);
02283             if (ND_node_type(right) == NORMAL)
02284                 nb -= GD_nodesep(g) / 2;
02285             else
02286                 nb -= sp->Splinesep;
02287         }
02288         if (nb > b)
02289             b = nb;
02290         rv.UR.x = b;
02291     } else
02292         rv.UR.x = MAX(b, sp->RightBound);
02293 
02294     if ((ND_node_type(vn) == VIRTUAL) && (ND_label(vn)))
02295         rv.UR.x -= ND_rw_i(vn);
02296 
02297     rv.LL.y = ND_coord_i(vn).y - GD_rank(g)[ND_rank(vn)].ht1;
02298     rv.UR.y = ND_coord_i(vn).y + GD_rank(g)[ND_rank(vn)].ht2;
02299     return rv;
02300 }
02301 
02302 static node_t *neighbor(vn, ie, oe, dir)
02303 node_t *vn;
02304 edge_t *ie, *oe;
02305 int dir;
02306 {
02307     int i;
02308     node_t *n, *rv = NULL;
02309     rank_t *rank = &(GD_rank(vn->graph)[ND_rank(vn)]);
02310 
02311     for (i = ND_order(vn) + dir; ((i >= 0) && (i < rank->n)); i += dir) {
02312         n = rank->v[i];
02313         if ((ND_node_type(n) == VIRTUAL) && (ND_label(n))) {
02314             rv = n;
02315             break;
02316         }
02317         if (ND_node_type(n) == NORMAL) {
02318             rv = n;
02319             break;
02320         }
02321         if (pathscross(n, vn, ie, oe) == FALSE) {
02322             rv = n;
02323             break;
02324         }
02325     }
02326     return rv;
02327 }
02328 
02329 static boolean pathscross(n0, n1, ie1, oe1)
02330 node_t *n0, *n1;
02331 edge_t *ie1, *oe1;
02332 {
02333     edge_t *e0, *e1;
02334     node_t *na, *nb;
02335     int order, cnt;
02336 
02337     order = (ND_order(n0) > ND_order(n1));
02338     if ((ND_out(n0).size != 1) && (ND_out(n0).size != 1))
02339         return FALSE;
02340     e1 = oe1;
02341     if (ND_out(n0).size == 1 && e1) {
02342         e0 = ND_out(n0).list[0];
02343         for (cnt = 0; cnt < 2; cnt++) {
02344             if ((na = e0->head) == (nb = e1->head))
02345                 break;
02346             if (order != (ND_order(na) > ND_order(nb)))
02347                 return TRUE;
02348             if ((ND_out(na).size != 1) || (ND_node_type(na) == NORMAL))
02349                 break;
02350             e0 = ND_out(na).list[0];
02351             if ((ND_out(nb).size != 1) || (ND_node_type(nb) == NORMAL))
02352                 break;
02353             e1 = ND_out(nb).list[0];
02354         }
02355     }
02356     e1 = ie1;
02357     if (ND_in(n0).size == 1 && e1) {
02358         e0 = ND_in(n0).list[0];
02359         for (cnt = 0; cnt < 2; cnt++) {
02360             if ((na = e0->tail) == (nb = e1->tail))
02361                 break;
02362             if (order != (ND_order(na) > ND_order(nb)))
02363                 return TRUE;
02364             if ((ND_in(na).size != 1) || (ND_node_type(na) == NORMAL))
02365                 break;
02366             e0 = ND_in(na).list[0];
02367             if ((ND_in(nb).size != 1) || (ND_node_type(nb) == NORMAL))
02368                 break;
02369             e1 = ND_in(nb).list[0];
02370         }
02371     }
02372     return FALSE;
02373 }
02374 
02375 #ifdef DEBUG
02376 void showpath(path * p)
02377 {
02378     int i;
02379     point LL, UR;
02380 
02381     fprintf(stderr, "%%!PS\n");
02382     for (i = 0; i < p->nbox; i++) {
02383         LL = p->boxes[i].LL;
02384         UR = p->boxes[i].UR;
02385         fprintf(stderr,
02386                 "newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath stroke\n",
02387                 LL.x, LL.y, UR.x, LL.y, UR.x, UR.y, LL.x, UR.y);
02388     }
02389     fprintf(stderr, "showpage\n");
02390 }
02391 #endif

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