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