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