Graphviz  2.31.20130618.0446
lib/neatogen/neatosplines.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 #ifdef HAVE_CONFIG_H
00016 #include "config.h"
00017 #endif
00018 
00019 #include "neato.h"
00020 #include "adjust.h"
00021 #include "pathplan.h"
00022 #include "vispath.h"
00023 #include "multispline.h"
00024 #ifndef HAVE_DRAND48
00025 extern double drand48(void);
00026 #endif
00027 
00028 #ifdef ORTHO
00029 #include <ortho.h>
00030 #endif
00031 
00032 extern void printvis(vconfig_t * cp);
00033 extern int in_poly(Ppoly_t argpoly, Ppoint_t q);
00034 
00035 
00036 static boolean spline_merge(node_t * n)
00037 {
00038     return FALSE;
00039 }
00040 
00041 static boolean swap_ends_p(edge_t * e)
00042 {
00043     return FALSE;
00044 }
00045 
00046 static splineInfo sinfo = { swap_ends_p, spline_merge };
00047 
00048 static void
00049 make_barriers(Ppoly_t ** poly, int npoly, int pp, int qp,
00050               Pedge_t ** barriers, int *n_barriers)
00051 {
00052     int i, j, k, n, b;
00053     Pedge_t *bar;
00054 
00055     n = 0;
00056     for (i = 0; i < npoly; i++) {
00057         if (i == pp)
00058             continue;
00059         if (i == qp)
00060             continue;
00061         n = n + poly[i]->pn;
00062     }
00063     bar = N_GNEW(n, Pedge_t);
00064     b = 0;
00065     for (i = 0; i < npoly; i++) {
00066         if (i == pp)
00067             continue;
00068         if (i == qp)
00069             continue;
00070         for (j = 0; j < poly[i]->pn; j++) {
00071             k = j + 1;
00072             if (k >= poly[i]->pn)
00073                 k = 0;
00074             bar[b].a = poly[i]->ps[j];
00075             bar[b].b = poly[i]->ps[k];
00076             b++;
00077         }
00078     }
00079     assert(b == n);
00080     *barriers = bar;
00081     *n_barriers = n;
00082 }
00083 
00084 /* genPt:
00085  */
00086 static Ppoint_t genPt(double x, double y, pointf c)
00087 {
00088     Ppoint_t p;
00089 
00090     p.x = x + c.x;
00091     p.y = y + c.y;
00092     return p;
00093 }
00094 
00095 
00096 /* recPt:
00097  */
00098 static Ppoint_t recPt(double x, double y, pointf c, expand_t* m)
00099 {
00100     Ppoint_t p;
00101 
00102     p.x = (x * m->x) + c.x;
00103     p.y = (y * m->y) + c.y;
00104     return p;
00105 }
00106 
00107 typedef struct {
00108     node_t *n1;
00109     pointf p1;
00110     node_t *n2;
00111     pointf p2;
00112 } edgeinfo;
00113 typedef struct {
00114     Dtlink_t link;
00115     edgeinfo id;
00116     edge_t *e;
00117 } edgeitem;
00118 
00119 static void *newitem(Dt_t * d, edgeitem * obj, Dtdisc_t * disc)
00120 {
00121     edgeitem *newp;
00122 
00123     NOTUSED(disc);
00124     newp = NEW(edgeitem);
00125     newp->id = obj->id;
00126     newp->e = obj->e;
00127     ED_count(newp->e) = 1;
00128 
00129     return newp;
00130 }
00131 
00132 static void freeitem(Dt_t * d, edgeitem * obj, Dtdisc_t * disc)
00133 {
00134     free(obj);
00135 }
00136 
00137 static int
00138 cmpitems(Dt_t * d, edgeinfo * key1, edgeinfo * key2, Dtdisc_t * disc)
00139 {
00140     int x;
00141 
00142     if (key1->n1 > key2->n1)
00143         return 1;
00144     if (key1->n1 < key2->n1)
00145         return -1;
00146     if (key1->n2 > key2->n2)
00147         return 1;
00148     if (key1->n2 < key2->n2)
00149         return -1;
00150 
00151     if ((x = key1->p1.x - key2->p1.x))
00152         return x;
00153     if ((x = key1->p1.y - key2->p1.y))
00154         return x;
00155     if ((x = key1->p2.x - key2->p2.x))
00156         return x;
00157     return (key1->p2.y - key2->p2.y);
00158 }
00159 
00160 Dtdisc_t edgeItemDisc = {
00161     offsetof(edgeitem, id),
00162     sizeof(edgeinfo),
00163     offsetof(edgeitem, link),
00164     (Dtmake_f) newitem,
00165     (Dtfree_f) freeitem,
00166     (Dtcompar_f) cmpitems,
00167     0,
00168     0,
00169     0
00170 };
00171 
00172 /* equivEdge:
00173  * See if we have already encountered an edge between the same
00174  * node:port pairs. If so, return the earlier edge. If not, 
00175  * this edge is added to map and returned.
00176  * We first have to canonicalize the key fields using a lexicographic
00177  * ordering.
00178  */
00179 static edge_t *equivEdge(Dt_t * map, edge_t * e)
00180 {
00181     edgeinfo test;
00182     edgeitem dummy;
00183     edgeitem *ip;
00184 
00185     if (agtail(e) < aghead(e)) {
00186         test.n1 = agtail(e);
00187         test.p1 = ED_tail_port(e).p;
00188         test.n2 = aghead(e);
00189         test.p2 = ED_head_port(e).p;
00190     } else if (agtail(e) > aghead(e)) {
00191         test.n2 = agtail(e);
00192         test.p2 = ED_tail_port(e).p;
00193         test.n1 = aghead(e);
00194         test.p1 = ED_head_port(e).p;
00195     } else {
00196         pointf hp = ED_head_port(e).p;
00197         pointf tp = ED_tail_port(e).p;
00198         if (tp.x < hp.x) {
00199             test.p1 = tp;
00200             test.p2 = hp;
00201         } else if (tp.x > hp.x) {
00202             test.p1 = hp;
00203             test.p2 = tp;
00204         } else if (tp.y < hp.y) {
00205             test.p1 = tp;
00206             test.p2 = hp;
00207         } else if (tp.y > hp.y) {
00208             test.p1 = hp;
00209             test.p2 = tp;
00210         } else {
00211             test.p1 = test.p2 = tp;
00212         }
00213         test.n2 = test.n1 = agtail(e);
00214     }
00215     dummy.id = test;
00216     dummy.e = e;
00217     ip = dtinsert(map, &dummy);
00218     return ip->e;
00219 }
00220 
00221 
00222 /* makeSelfArcs:
00223  * Generate loops. We use the library routine makeSelfEdge
00224  * which also places the labels.
00225  * We have to handle port labels here.
00226  * as well as update the bbox from edge labels.
00227  */
00228 void makeSelfArcs(path * P, edge_t * e, int stepx)
00229 {
00230     int cnt = ED_count(e);
00231 
00232     if ((cnt == 1) || Concentrate) {
00233         edge_t *edges1[1];
00234         edges1[0] = e;
00235         makeSelfEdge(P, edges1, 0, 1, stepx, stepx, &sinfo);
00236         if (ED_label(e))
00237             updateBB(agraphof(agtail(e)), ED_label(e));
00238         makePortLabels(e);
00239     } else {
00240         int i;
00241         edge_t **edges = N_GNEW(cnt, edge_t *);
00242         for (i = 0; i < cnt; i++) {
00243             edges[i] = e;
00244             e = ED_to_virt(e);
00245         }
00246         makeSelfEdge(P, edges, 0, cnt, stepx, stepx, &sinfo);
00247         for (i = 0; i < cnt; i++) {
00248             e = edges[i];
00249             if (ED_label(e))
00250                 updateBB(agraphof(agtail(e)), ED_label(e));
00251             makePortLabels(e);
00252         }
00253         free(edges);
00254     }
00255 }
00256 
00257 /* makeObstacle:
00258  * Given a node, return an obstacle reflecting the
00259  * node's geometry. pmargin specifies how much space to allow
00260  * around the polygon. 
00261  * Returns the constructed polygon on success, NULL on failure.
00262  * Failure means the node shape is not supported. 
00263  *
00264  * The polygon has its vertices in CW order.
00265  * 
00266  */
00267 Ppoly_t *makeObstacle(node_t * n, expand_t* pmargin)
00268 {
00269     Ppoly_t *obs;
00270     polygon_t *poly;
00271     double adj = 0.0;
00272     int j, sides;
00273     pointf polyp;
00274     boxf b;
00275     pointf pt;
00276     field_t *fld;
00277     epsf_t *desc;
00278 
00279     switch (shapeOf(n)) {
00280     case SH_POLY:
00281     case SH_POINT:
00282         obs = NEW(Ppoly_t);
00283         poly = (polygon_t *) ND_shape_info(n);
00284         if (poly->sides >= 3) {
00285             sides = poly->sides;
00286         } else {                /* ellipse */
00287             sides = 8;
00288             adj = drand48() * .01;
00289         }
00290         obs->pn = sides;
00291         obs->ps = N_NEW(sides, Ppoint_t);
00292         /* assuming polys are in CCW order, and pathplan needs CW */
00293         for (j = 0; j < sides; j++) {
00294             double xmargin = 0.0, ymargin = 0.0;
00295             if (poly->sides >= 3) {
00296                 if (pmargin->doAdd) {
00297                     if (poly->sides == 4) {
00298                         switch (j) {
00299                         case 0 :
00300                             xmargin = pmargin->x;
00301                             ymargin = pmargin->y;
00302                             break;
00303                         case 1 :
00304                             xmargin = -pmargin->x;
00305                             ymargin = pmargin->y;
00306                             break;
00307                         case 2 :
00308                             xmargin = -pmargin->x;
00309                             ymargin = -pmargin->y;
00310                             break;
00311                         case 3 :
00312                             xmargin = pmargin->x;
00313                             ymargin = -pmargin->y;
00314                             break;
00315                         }
00316                         polyp.x = poly->vertices[j].x + xmargin;
00317                         polyp.y = poly->vertices[j].y + ymargin;
00318                     }
00319                     else {
00320                         double h = LEN(poly->vertices[j].x,poly->vertices[j].y);
00321                         polyp.x = poly->vertices[j].x * (1.0 + pmargin->x/h);
00322                         polyp.y = poly->vertices[j].y * (1.0 + pmargin->y/h);
00323                     }
00324                 }
00325                 else {
00326                     polyp.x = poly->vertices[j].x * pmargin->x;
00327                     polyp.y = poly->vertices[j].y * pmargin->y;
00328                 }
00329             } else {
00330                 double c, s;
00331                 c = cos(2.0 * M_PI * j / sides + adj);
00332                 s = sin(2.0 * M_PI * j / sides + adj);
00333                 if (pmargin->doAdd) {
00334                     polyp.x =  c*(ND_lw(n)+ND_rw(n)+pmargin->x) / 2.0;
00335                     polyp.y =  s*(ND_ht(n)+pmargin->y) / 2.0;
00336                 }
00337                 else {
00338                     polyp.x = pmargin->x * c * (ND_lw(n) + ND_rw(n)) / 2.0;
00339                     polyp.y = pmargin->y * s * ND_ht(n) / 2.0;
00340                 }
00341             }
00342             obs->ps[sides - j - 1].x = polyp.x + ND_coord(n).x;
00343             obs->ps[sides - j - 1].y = polyp.y + ND_coord(n).y;
00344         }
00345         break;
00346     case SH_RECORD:
00347         fld = (field_t *) ND_shape_info(n);
00348         b = fld->b;
00349         obs = NEW(Ppoly_t);
00350         obs->pn = 4;
00351         obs->ps = N_NEW(4, Ppoint_t);
00352         /* CW order */
00353         pt = ND_coord(n);
00354         if (pmargin->doAdd) {
00355             obs->ps[0] = genPt(b.LL.x-pmargin->x, b.LL.y-pmargin->y, pt);
00356             obs->ps[1] = genPt(b.LL.x-pmargin->x, b.UR.y+pmargin->y, pt);
00357             obs->ps[2] = genPt(b.UR.x+pmargin->x, b.UR.y+pmargin->y, pt);
00358             obs->ps[3] = genPt(b.UR.x+pmargin->x, b.LL.y-pmargin->y, pt);
00359         }
00360         else {
00361             obs->ps[0] = recPt(b.LL.x, b.LL.y, pt, pmargin);
00362             obs->ps[1] = recPt(b.LL.x, b.UR.y, pt, pmargin);
00363             obs->ps[2] = recPt(b.UR.x, b.UR.y, pt, pmargin);
00364             obs->ps[3] = recPt(b.UR.x, b.LL.y, pt, pmargin);
00365         }
00366         break;
00367     case SH_EPSF:
00368         desc = (epsf_t *) (ND_shape_info(n));
00369         obs = NEW(Ppoly_t);
00370         obs->pn = 4;
00371         obs->ps = N_NEW(4, Ppoint_t);
00372         /* CW order */
00373         pt = ND_coord(n);
00374         if (pmargin->doAdd) {
00375             obs->ps[0] = genPt(-ND_lw(n)-pmargin->x, -ND_ht(n)-pmargin->y,pt);
00376             obs->ps[1] = genPt(-ND_lw(n)-pmargin->x, ND_ht(n)+pmargin->y,pt);
00377             obs->ps[2] = genPt(ND_rw(n)+pmargin->x, ND_ht(n)+pmargin->y,pt);
00378             obs->ps[3] = genPt(ND_rw(n)+pmargin->x, -ND_ht(n)-pmargin->y,pt);
00379         }
00380         else {
00381             obs->ps[0] = recPt(-ND_lw(n), -ND_ht(n), pt, pmargin);
00382             obs->ps[1] = recPt(-ND_lw(n), ND_ht(n), pt, pmargin);
00383             obs->ps[2] = recPt(ND_rw(n), ND_ht(n), pt, pmargin);
00384             obs->ps[3] = recPt(ND_rw(n), -ND_ht(n), pt, pmargin);
00385         }
00386         break;
00387     default:
00388         obs = NULL;
00389         break;
00390     }
00391     return obs;
00392 }
00393 
00394 /* getPath
00395  * Construct the shortest path from one endpoint of e to the other.
00396  * The obstacles and their number are given by obs and npoly.
00397  * vconfig is a precomputed data structure to help in the computation.
00398  * If chkPts is true, the function finds the polygons, if any, containing
00399  * the endpoints and tells the shortest path computation to ignore them. 
00400  * Assumes this info is set in ND_lim, usually from _spline_edges.
00401  * Returns the shortest path.
00402  */
00403 Ppolyline_t
00404 getPath(edge_t * e, vconfig_t * vconfig, int chkPts, Ppoly_t ** obs,
00405         int npoly)
00406 {
00407     Ppolyline_t line;
00408     int pp, qp;
00409     Ppoint_t p, q;
00410 
00411     p = add_pointf(ND_coord(agtail(e)), ED_tail_port(e).p);
00412     q = add_pointf(ND_coord(aghead(e)), ED_head_port(e).p);
00413 
00414     /* determine the polygons (if any) that contain the endpoints */
00415     pp = qp = POLYID_NONE;
00416     if (chkPts) {
00417         pp = ND_lim(agtail(e));
00418         qp = ND_lim(aghead(e));
00419 /*
00420         for (i = 0; i < npoly; i++) {
00421             if ((pp == POLYID_NONE) && in_poly(*obs[i], p))
00422                 pp = i;
00423             if ((qp == POLYID_NONE) && in_poly(*obs[i], q))
00424                 qp = i;
00425         }
00426 */
00427     }
00428     Pobspath(vconfig, p, pp, q, qp, &line);
00429     return line;
00430 }
00431 
00432 /* makePolyline:
00433  */
00434 static void
00435 makePolyline(graph_t* g, edge_t * e)
00436 {
00437     Ppolyline_t spl, line = ED_path(e);
00438     Ppoint_t p0, q0;
00439 
00440     p0 = line.ps[0];
00441     q0 = line.ps[line.pn - 1];
00442     make_polyline (line, &spl);
00443     if (Verbose > 1)
00444         fprintf(stderr, "polyline %s %s\n", agnameof(agtail(e)), agnameof(aghead(e)));
00445     clip_and_install(e, aghead(e), spl.ps, spl.pn, &sinfo);
00446     addEdgeLabels(g, e, p0, q0);
00447 }
00448 
00449 /* makeSpline:
00450  * Construct a spline connecting the endpoints of e, avoiding the npoly
00451  * obstacles obs.
00452  * The resultant spline is attached to the edge, the positions of any 
00453  * edge labels are computed, and the graph's bounding box is recomputed.
00454  * 
00455  * If chkPts is true, the function checks if one or both of the endpoints 
00456  * is on or inside one of the obstacles and, if so, tells the shortest path
00457  * computation to ignore them. 
00458  */
00459 void makeSpline(graph_t* g, edge_t * e, Ppoly_t ** obs, int npoly, boolean chkPts)
00460 {
00461     Ppolyline_t line, spline;
00462     Pvector_t slopes[2];
00463     int i, n_barriers;
00464     int pp, qp;
00465     Ppoint_t p, q;
00466     Pedge_t *barriers;
00467 
00468     line = ED_path(e);
00469     p = line.ps[0];
00470     q = line.ps[line.pn - 1];
00471     /* determine the polygons (if any) that contain the endpoints */
00472     pp = qp = POLYID_NONE;
00473     if (chkPts)
00474         for (i = 0; i < npoly; i++) {
00475             if ((pp == POLYID_NONE) && in_poly(*obs[i], p))
00476                 pp = i;
00477             if ((qp == POLYID_NONE) && in_poly(*obs[i], q))
00478                 qp = i;
00479         }
00480 
00481     make_barriers(obs, npoly, pp, qp, &barriers, &n_barriers);
00482     slopes[0].x = slopes[0].y = 0.0;
00483     slopes[1].x = slopes[1].y = 0.0;
00484     if (Proutespline(barriers, n_barriers, line, slopes, &spline) < 0) {
00485         agerr (AGERR, "makeSpline: failed to make spline edge (%s,%s)\n", agnameof(agtail(e)), agnameof(aghead(e)));
00486         return;
00487     }
00488 
00489     /* north why did you ever use int coords */
00490     if (Verbose > 1)
00491         fprintf(stderr, "spline %s %s\n", agnameof(agtail(e)), agnameof(aghead(e)));
00492     clip_and_install(e, aghead(e), spline.ps, spline.pn, &sinfo);
00493     free(barriers);
00494     addEdgeLabels(g, e, p, q);
00495 }
00496 
00497   /* True if either head or tail has a port on its boundary */
00498 #define BOUNDARY_PORT(e) ((ED_tail_port(e).side)||(ED_head_port(e).side))
00499 
00500 /* _spline_edges:
00501  * Basic default routine for creating edges.
00502  * If splines are requested, we construct the obstacles.
00503  * If not, or nodes overlap, the function reverts to line segments.
00504  * NOTE: intra-cluster edges are not constrained to
00505  * remain in the cluster's bounding box and, conversely, a cluster's box
00506  * is not altered to reflect intra-cluster edges.
00507  * If Nop > 1 and the spline exists, it is just copied.
00508  * NOTE: if edgetype = ET_NONE, we shouldn't be here.
00509  */
00510 static int _spline_edges(graph_t * g, expand_t* pmargin, int edgetype)
00511 {
00512     node_t *n;
00513     edge_t *e;
00514     edge_t *e0;
00515     Ppoly_t **obs = 0;
00516     Ppoly_t *obp;
00517     int cnt, i = 0, npoly;
00518     vconfig_t *vconfig = 0;
00519     path *P = NULL;
00520     int useEdges = (Nop > 1);
00521     router_t* rtr = 0;
00522     int legal = 0;
00523 
00524     /* build configuration */
00525     if (edgetype >= ET_PLINE) {
00526         obs = N_NEW(agnnodes(g), Ppoly_t *);
00527         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00528             obp = makeObstacle(n, pmargin);
00529             if (obp) {
00530                 ND_lim(n) = i; 
00531                 obs[i++] = obp;
00532             }
00533             else
00534                 ND_lim(n) = POLYID_NONE; 
00535         }
00536     } else {
00537         obs = 0;
00538     }
00539     npoly = i;
00540     if (obs) {
00541         if ((legal = Plegal_arrangement(obs, npoly))) {
00542             if (edgetype != ET_ORTHO) vconfig = Pobsopen(obs, npoly);
00543         }
00544         else if (Verbose)
00545             fprintf(stderr,
00546                 "nodes touch - falling back to straight line edges\n");
00547     }
00548 
00549     /* route edges  */
00550     if (Verbose)
00551         fprintf(stderr, "Creating edges using %s\n",
00552             (legal && (edgetype == ET_ORTHO)) ? "orthogonal lines" :
00553             (vconfig ? (edgetype == ET_SPLINE ? "splines" : "polylines") : 
00554                 "line segments"));
00555     if (vconfig) {
00556         /* path-finding pass */
00557         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00558             for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00559                 ED_path(e) = getPath(e, vconfig, TRUE, obs, npoly);
00560             }
00561         }
00562     }
00563 #ifdef ORTHO
00564     else if (legal && (edgetype == ET_ORTHO)) {
00565         orthoEdges (g, 0);
00566         useEdges = 1;
00567     }
00568 #endif
00569 
00570     /* spline-drawing pass */
00571     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00572         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00573 /* fprintf (stderr, "%s -- %s %d\n", agnameof(agtail(e)), agnameof(aghead(e)), ED_count(e)); */
00574             node_t *head = aghead(e);
00575             if (useEdges && ED_spl(e)) {
00576                 addEdgeLabels(g, e,
00577                               add_pointf(ND_coord(n), ED_tail_port(e).p),
00578                               add_pointf(ND_coord(head), ED_head_port(e).p));
00579             } 
00580             else if (ED_count(e) == 0) continue;  /* only do representative */
00581             else if (n == head) {    /* self arc */
00582                 if (!P) {
00583                     P = NEW(path);
00584                     P->boxes = N_NEW(agnnodes(g) + 20 * 2 * 9, boxf);
00585                 }
00586                 makeSelfArcs(P, e, GD_nodesep(g->root));
00587             } else if (vconfig) { /* ET_SPLINE or ET_PLINE */
00588 #ifdef HAVE_GTS
00589                 if ((ED_count(e) > 1) || BOUNDARY_PORT(e)) {
00590                     int fail = 0;
00591                     if ((ED_path(e).pn == 2) && !BOUNDARY_PORT(e))
00592                              /* if a straight line can connect the ends */
00593                         makeStraightEdge(g, e, edgetype, &sinfo);
00594                     else { 
00595                         if (!rtr) rtr = mkRouter (obs, npoly);
00596                         fail = makeMultiSpline(g, e, rtr, edgetype == ET_PLINE);
00597                     } 
00598                     if (!fail) continue;
00599                 }
00600                 /* We can probably remove this branch and just use
00601                  * makeMultiSpline. It can also catch the makeStraightEdge
00602                  * case. We could then eliminate all of the vconfig stuff.
00603                  */
00604 #endif
00605                 cnt = ED_count(e);
00606                 if (Concentrate) cnt = 1; /* only do representative */
00607                 e0 = e;
00608                 for (i = 0; i < cnt; i++) {
00609                     if (edgetype == ET_SPLINE)
00610                         makeSpline(g, e0, obs, npoly, TRUE);
00611                     else
00612                         makePolyline(g, e0);
00613                     e0 = ED_to_virt(e0);
00614                 }
00615             } else {
00616                 makeStraightEdge(g, e, edgetype, &sinfo);
00617             }
00618         }
00619     }
00620 
00621 #ifdef HAVE_GTS
00622     if (rtr)
00623         freeRouter (rtr);
00624 #endif
00625 
00626     if (vconfig)
00627         Pobsclose (vconfig);
00628     if (P) {
00629         free(P->boxes);
00630         free(P);
00631     }
00632     if (obs) {
00633         for (i=0; i < npoly; i++)
00634             free (obs[i]);
00635         free (obs);
00636     }
00637     return 0;
00638 }
00639 
00640 /* splineEdges:
00641  * Main wrapper code for generating edges.
00642  * Sets desired separation. 
00643  * Coalesces equivalent edges (edges * with the same endpoints). 
00644  * It then calls the edge generating function, and marks the
00645  * spline phase complete.
00646  * Returns 0 on success.
00647  *
00648  * The edge function is given the graph, the separation to be added
00649  * around obstacles, and the type of edge. It must guarantee 
00650  * that all bounding boxes are current; in particular, the bounding box of 
00651  * g must reflect the addition of the edges.
00652  */
00653 int
00654 splineEdges(graph_t * g, int (*edgefn) (graph_t *, expand_t*, int),
00655             int edgetype)
00656 {
00657     node_t *n;
00658     edge_t *e;
00659     expand_t margin;
00660     Dt_t *map;
00661 
00662     margin = esepFactor (g);
00663 
00664     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00665         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00666             resolvePorts (e);
00667         }
00668     }
00669 
00670     /* find equivalent edges */
00671     map = dtopen(&edgeItemDisc, Dtoset);
00672     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00673         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00674             edge_t *leader = equivEdge(map, e);
00675             if (leader != e) {
00676                 ED_count(leader)++;
00677                 ED_to_virt(e) = ED_to_virt(leader);
00678                 ED_to_virt(leader) = e;
00679             }
00680         }
00681     }
00682     dtclose(map);
00683 
00684     if (edgefn(g, &margin, edgetype))
00685         return 1;
00686 
00687     State = GVSPLINES;
00688     return 0;
00689 }
00690 
00691 /* spline_edges1:
00692  * Construct edges using default algorithm and given splines value.
00693  * Return 0 on success.
00694  */
00695 int spline_edges1(graph_t * g, int edgetype)
00696 {
00697     return splineEdges(g, _spline_edges, edgetype);
00698 }
00699 
00700 /* spline_edges0:
00701  * Sets the graph's aspect ratio.
00702  * Check splines attribute and construct edges using default algorithm.
00703  * If the splines attribute is defined but equal to "", skip edge routing.
00704  * 
00705  * Assumes u.bb for has been computed for g and all clusters
00706  * (not just top-level clusters), and  that GD_bb(g).LL is at the origin.
00707  *
00708  * This last criterion is, I believe, mainly to simplify the code
00709  * in neato_set_aspect. It would be good to remove this constraint,
00710  * as this would allow nodes pinned on input to have the same coordinates
00711  * when output in dot or plain format.
00712  *
00713  */
00714 void spline_edges0(graph_t * g)
00715 {
00716     int et = EDGE_TYPE (g);
00717     neato_set_aspect(g);
00718     if (et == ET_NONE) return;
00719 #ifndef ORTHO
00720     if (et == ET_ORTHO) {
00721         agerr (AGWARN, "Orthogonal edges not yet supported\n");
00722         et = ET_PLINE; 
00723         GD_flags(g->root) &= ~ET_ORTHO;
00724         GD_flags(g->root) |= ET_PLINE;
00725     }
00726 #endif
00727     spline_edges1(g, et);
00728 }
00729 
00730 /* shiftClusters:
00731  */
00732 static void
00733 shiftClusters (graph_t * g, pointf offset)
00734 {
00735     int i;
00736 
00737     for (i = 1; i <= GD_n_cluster(g); i++) {
00738         shiftClusters (GD_clust(g)[i], offset);
00739     }
00740 
00741     GD_bb(g).UR.x -= offset.x;
00742     GD_bb(g).UR.y -= offset.y;
00743     GD_bb(g).LL.x -= offset.x;
00744     GD_bb(g).LL.y -= offset.y;
00745 }
00746 
00747 /* spline_edges:
00748  * Compute bounding box, translate graph to origin,
00749  * then construct all edges.
00750  */
00751 void spline_edges(graph_t * g)
00752 {
00753     node_t *n;
00754     pointf offset;
00755 
00756     compute_bb(g);
00757     offset.x = PS2INCH(GD_bb(g).LL.x);
00758     offset.y = PS2INCH(GD_bb(g).LL.y);
00759     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00760         ND_pos(n)[0] -= offset.x;
00761         ND_pos(n)[1] -= offset.y;
00762     }
00763         
00764     shiftClusters (g, GD_bb(g).LL);
00765     spline_edges0(g);
00766 }
00767 
00768 /* scaleEdge:
00769  * Scale edge by given factor.
00770  * Assume ED_spl != NULL.
00771  */
00772 static void scaleEdge(edge_t * e, double xf, double yf)
00773 {
00774     int i, j;
00775     pointf *pt;
00776     bezier *bez;
00777     pointf delh, delt;
00778 
00779     delh.x = POINTS_PER_INCH * (ND_pos(aghead(e))[0] * (xf - 1.0));
00780     delh.y = POINTS_PER_INCH * (ND_pos(aghead(e))[1] * (yf - 1.0));
00781     delt.x = POINTS_PER_INCH * (ND_pos(agtail(e))[0] * (xf - 1.0));
00782     delt.y = POINTS_PER_INCH * (ND_pos(agtail(e))[1] * (yf - 1.0));
00783 
00784     bez = ED_spl(e)->list;
00785     for (i = 0; i < ED_spl(e)->size; i++) {
00786         pt = bez->list;
00787         for (j = 0; j < bez->size; j++) {
00788             if ((i == 0) && (j == 0)) {
00789                 pt->x += delt.x;
00790                 pt->y += delt.y;
00791             }
00792             else if ((i == ED_spl(e)->size-1) && (j == bez->size-1)) {
00793                 pt->x += delh.x;
00794                 pt->y += delh.y;
00795             }
00796             else {
00797                 pt->x *= xf;
00798                 pt->y *= yf;
00799             }
00800             pt++;
00801         }
00802         if (bez->sflag) {
00803             bez->sp.x += delt.x;
00804             bez->sp.y += delt.y;
00805         }
00806         if (bez->eflag) {
00807             bez->ep.x += delh.x;
00808             bez->ep.y += delh.y;
00809         }
00810         bez++;
00811     }
00812 
00813     if (ED_label(e) && ED_label(e)->set) {
00814         ED_label(e)->pos.x *= xf;
00815         ED_label(e)->pos.y *= yf;
00816     }
00817     if (ED_head_label(e) && ED_head_label(e)->set) {
00818         ED_head_label(e)->pos.x += delh.x;
00819         ED_head_label(e)->pos.y += delh.y;
00820     }
00821     if (ED_tail_label(e) && ED_tail_label(e)->set) {
00822         ED_tail_label(e)->pos.x += delt.x;
00823         ED_tail_label(e)->pos.y += delt.y;
00824     }
00825 }
00826 
00827 /* scaleBB:
00828  * Scale bounding box of clusters of g by given factors.
00829  */
00830 static void scaleBB(graph_t * g, double xf, double yf)
00831 {
00832     int i;
00833 
00834     GD_bb(g).UR.x *= xf;
00835     GD_bb(g).UR.y *= yf;
00836     GD_bb(g).LL.x *= xf;
00837     GD_bb(g).LL.y *= yf;
00838 
00839     if (GD_label(g) && GD_label(g)->set) {
00840         GD_label(g)->pos.x *= xf;
00841         GD_label(g)->pos.y *= yf;
00842     }
00843 
00844     for (i = 1; i <= GD_n_cluster(g); i++)
00845         scaleBB(GD_clust(g)[i], xf, yf);
00846 }
00847 
00848 /* _neato_set_aspect;
00849  * Assume all bounding boxes are correct and
00850  * that GD_bb(g).LL is at origin.
00851  * Also assume g is the root graph
00852  */
00853 static void _neato_set_aspect(graph_t * g)
00854 {
00855     /* int          i; */
00856     double xf, yf, actual, desired;
00857     node_t *n;
00858 
00859     /* compute_bb(g); */
00860     if (GD_drawing(g)->ratio_kind) {
00861         /* normalize */
00862         assert(ROUND(GD_bb(g).LL.x) == 0);
00863         assert(ROUND(GD_bb(g).LL.y) == 0);
00864         if (GD_flip(g)) {
00865             double t = GD_bb(g).UR.x;
00866             GD_bb(g).UR.x = GD_bb(g).UR.y;
00867             GD_bb(g).UR.y = t;
00868         }
00869         if (GD_drawing(g)->ratio_kind == R_FILL) {
00870             /* fill is weird because both X and Y can stretch */
00871             if (GD_drawing(g)->size.x <= 0)
00872                 return;
00873             xf = (double) GD_drawing(g)->size.x / GD_bb(g).UR.x;
00874             yf = (double) GD_drawing(g)->size.y / GD_bb(g).UR.y;
00875             /* handle case where one or more dimensions is too big */
00876             if ((xf < 1.0) || (yf < 1.0)) {
00877                 if (xf < yf) {
00878                     yf = yf / xf;
00879                     xf = 1.0;
00880                 } else {
00881                     xf = xf / yf;
00882                     yf = 1.0;
00883                 }
00884             }
00885         } else if (GD_drawing(g)->ratio_kind == R_EXPAND) {
00886             if (GD_drawing(g)->size.x <= 0)
00887                 return;
00888             xf = (double) GD_drawing(g)->size.x / GD_bb(g).UR.x;
00889             yf = (double) GD_drawing(g)->size.y / GD_bb(g).UR.y;
00890             if ((xf > 1.0) && (yf > 1.0)) {
00891                 double scale = MIN(xf, yf);
00892                 xf = yf = scale;
00893             } else
00894                 return;
00895         } else if (GD_drawing(g)->ratio_kind == R_VALUE) {
00896             desired = GD_drawing(g)->ratio;
00897             actual = (GD_bb(g).UR.y) / (GD_bb(g).UR.x);
00898             if (actual < desired) {
00899                 yf = desired / actual;
00900                 xf = 1.0;
00901             } else {
00902                 xf = actual / desired;
00903                 yf = 1.0;
00904             }
00905         } else
00906             return;
00907         if (GD_flip(g)) {
00908             double t = xf;
00909             xf = yf;
00910             yf = t;
00911         }
00912 
00913         if (Nop > 1) {
00914             edge_t *e;
00915             for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00916                 for (e = agfstout(g, n); e; e = agnxtout(g, e))
00917                     if (ED_spl(e))
00918                         scaleEdge(e, xf, yf);
00919             }
00920         }
00921         /* Not relying on neato_nlist here allows us not to have to 
00922          * allocate it in the root graph and the connected components. 
00923          */
00924         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00925             ND_pos(n)[0] = ND_pos(n)[0] * xf;
00926             ND_pos(n)[1] = ND_pos(n)[1] * yf;
00927         }
00928         scaleBB(g, xf, yf);
00929     }
00930 }
00931 
00932 /* neato_set_aspect:
00933  * Sets aspect ratio if necessary; real work done in _neato_set_aspect;
00934  * This also copies the internal layout coordinates (ND_pos) to the 
00935  * external ones (ND_coord).
00936  */
00937 void neato_set_aspect(graph_t * g)
00938 {
00939     node_t *n;
00940 
00941         /* setting aspect ratio only makes sense on root graph */
00942     if (g->root == g)
00943         _neato_set_aspect(g);
00944     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00945         ND_coord(n).x = POINTS_PER_INCH * (ND_pos(n)[0]);
00946         ND_coord(n).y = POINTS_PER_INCH * (ND_pos(n)[1]);
00947     }
00948 }
00949