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