/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/neatogen/neatosplines.c

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

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