/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/fdpgen/layout.c

Go to the documentation of this file.
00001 /* $Id: layout.c,v 1.21 2008/03/03 23:01:51 ellson Exp $ $Revision: 1.21 $ */
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 /* layout.c:
00019  * Written by Emden R. Gansner
00020  *
00021  * This module provides the main bookkeeping for the fdp layout.
00022  * In particular, it handles the recursion and the creation of
00023  * ports and auxiliary graphs.
00024  * 
00025  * TODO : can we use ports to aid in layout of edges? Note that
00026  * at present, they are deleted.
00027  *
00028  *   Can we delay all repositioning of nodes until evalPositions, so
00029  * finalCC only sets the bounding boxes?
00030  *
00031  * Make sure multiple edges have an effect.
00032  */
00033 
00034 /* uses PRIVATE interface */
00035 #define FDP_PRIVATE 1
00036 
00037 #ifdef HAVE_CONFIG_H
00038 #include "config.h"
00039 #endif
00040 #ifdef HAVE_LIMITS_H
00041 #include <limits.h>
00042 #else
00043 #ifdef HAVE_VALUES_H
00044 #include <values.h>
00045 #endif
00046 #endif
00047 #include <tlayout.h>
00048 #include <neatoprocs.h>
00049 #include <adjust.h>
00050 #include <comp.h>
00051 #include <pack.h>
00052 #include <assert.h>
00053 #include <clusteredges.h>
00054 #include <dbg.h>
00055 
00056 typedef struct {
00057     graph_t*  rootg;  /* logical root; graph passed in to fdp_layout */
00058     attrsym_t *G_coord;
00059     attrsym_t *G_width;
00060     attrsym_t *G_height;
00061     int gid;
00062     pack_info pack;
00063 } layout_info;
00064 
00065 #define NEW_EDGE(e) (ED_to_virt(e) == 0)
00066 
00067 /* finalCC:
00068  * Set graph bounding box given list of connected
00069  * components, each with its bounding box set.
00070  * If c_cnt > 1, then pts != NULL and gives translations for components.
00071  * Add margin about whole graph unless isRoot is true.
00072  * Reposition nodes based on final position of
00073  * node's connected component.
00074  * Also, entire layout is translated to origin.
00075  */
00076 static void
00077 finalCC(graph_t * g, int c_cnt, graph_t ** cc, point * pts, graph_t * rg,
00078         layout_info* infop)
00079 {
00080     attrsym_t * G_width = infop->G_width;
00081     attrsym_t * G_height = infop->G_height;
00082     graph_t *cg;
00083     box b, bb;
00084     boxf bbf;
00085     point pt;
00086     int margin;
00087     graph_t **cp = cc;
00088     point *pp = pts;
00089     int isRoot = (rg == infop->rootg);
00090     int isEmpty = 0;
00091 
00092     /* compute graph bounding box in points */
00093     if (c_cnt) {
00094         cg = *cp++;
00095         bb = GD_bb(cg);
00096         if (c_cnt > 1) {
00097             pt = *pp++;
00098             bb.LL.x += pt.x;
00099             bb.LL.y += pt.y;
00100             bb.UR.x += pt.x;
00101             bb.UR.y += pt.y;
00102             while ((cg = *cp++)) {
00103                 b = GD_bb(cg);
00104                 pt = *pp++;
00105                 b.LL.x += pt.x;
00106                 b.LL.y += pt.y;
00107                 b.UR.x += pt.x;
00108                 b.UR.y += pt.y;
00109                 bb.LL.x = MIN(bb.LL.x, b.LL.x);
00110                 bb.LL.y = MIN(bb.LL.y, b.LL.y);
00111                 bb.UR.x = MAX(bb.UR.x, b.UR.x);
00112                 bb.UR.y = MAX(bb.UR.y, b.UR.y);
00113             }
00114         }
00115     } else {                    /* empty graph */
00116         bb.LL.x = 0;
00117         bb.LL.y = 0;
00118         bb.UR.x = late_int(rg, G_width, POINTS(DEFAULT_NODEWIDTH), 3);
00119         bb.UR.y = late_int(rg, G_height, POINTS(DEFAULT_NODEHEIGHT), 3);
00120         isEmpty = 1;
00121     }
00122 
00123     if (GD_label(rg)) {
00124         point p;
00125         int d;
00126 
00127         isEmpty = 0;
00128         PF2P(GD_label(rg)->dimen, p);
00129         d = p.x - (bb.UR.x - bb.LL.x);
00130         if (d > 0) {            /* height of label added below */
00131             d /= 2;
00132             bb.LL.x -= d;
00133             bb.UR.x += d;
00134         }
00135     }
00136 
00137     if (isRoot || isEmpty)
00138         margin = 0;
00139     else
00140         margin = CL_OFFSET;
00141     pt.x = -bb.LL.x + margin;
00142     pt.y = -bb.LL.y + margin + GD_border(rg)[BOTTOM_IX].y;
00143     bb.LL.x = 0;
00144     bb.LL.y = 0;
00145     bb.UR.x += pt.x + margin;
00146     bb.UR.y += pt.y + margin + GD_border(rg)[TOP_IX].y;
00147 
00148     /* translate nodes */
00149     if (c_cnt) {
00150         cp = cc;
00151         pp = pts;
00152         while ((cg = *cp++)) {
00153             point p;
00154             node_t *n;
00155             pointf del;
00156 
00157             if (pp) {
00158                 p = *pp++;
00159                 p.x += pt.x;
00160                 p.y += pt.y;
00161             } else {
00162                 p = pt;
00163             }
00164             del = cvt2ptf(p);
00165             for (n = agfstnode(cg); n; n = agnxtnode(cg, n)) {
00166                 ND_pos(n)[0] += del.x;
00167                 ND_pos(n)[1] += del.y;
00168             }
00169         }
00170     }
00171 
00172     bbf.LL = cvt2ptf(bb.LL);
00173     bbf.UR = cvt2ptf(bb.UR);
00174     BB(g) = bbf;
00175 
00176 }
00177 
00178 /* mkDeriveNode:
00179  * Constructor for a node in a derived graph.
00180  * Allocates dndata.
00181  */
00182 static node_t *mkDeriveNode(graph_t * dg, char *name)
00183 {
00184     node_t *dn;
00185 
00186     dn = agnode(dg, name);
00187     ND_alg(dn) = (void *) NEW(dndata);  /* free in freeDeriveNode */
00188     ND_pos(dn) = N_GNEW(GD_ndim(dg), double);
00189     /* fprintf (stderr, "Creating %s\n", dn->name); */
00190     return dn;
00191 }
00192 
00193 static void freeDeriveNode(node_t * n)
00194 {
00195     free(ND_alg(n));
00196     free(ND_pos(n));
00197 }
00198 
00199 static void freeGData(graph_t * g)
00200 {
00201     free(GD_alg(g));
00202 }
00203 
00204 static void freeDerivedGraph(graph_t * g, graph_t ** cc)
00205 {
00206     graph_t *cg;
00207     node_t *dn;
00208     node_t *dnxt;
00209     edge_t *e;
00210 
00211     while ((cg = *cc++)) {
00212         freeGData(cg);
00213     }
00214     if (PORTS(g))
00215         free(PORTS(g));
00216     freeGData(g);
00217     for (dn = agfstnode(g); dn; dn = dnxt) {
00218         dnxt = agnxtnode(g, dn);
00219         for (e = agfstout(g, dn); e; e = agnxtout(g, e)) {
00220             free (ED_to_virt(e));
00221         }
00222         freeDeriveNode(dn);
00223     }
00224     agclose(g);
00225 }
00226 
00227 /* evalPositions:
00228  * The input is laid out, but node coordinates
00229  * are relative to smallest containing cluster.
00230  * Walk through all nodes and clusters, translating
00231  * the positions to absolute coordinates.
00232  * Assume that when called, g's bounding box is
00233  * in absolute coordinates and that box of root graph
00234  * has LL at origin.
00235  */
00236 static void evalPositions(graph_t * g, graph_t* rootg)
00237 {
00238     int i;
00239     graph_t *subg;
00240     node_t *n;
00241     boxf bb;
00242     boxf sbb;
00243 
00244     bb = BB(g);
00245 
00246     /* translate nodes in g */
00247     if (g != rootg) {
00248         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00249             if (PARENT(n) != g)
00250                 continue;
00251             ND_pos(n)[0] += bb.LL.x;
00252             ND_pos(n)[1] += bb.LL.y;
00253         }
00254     }
00255 
00256     /* translate top-level clusters and recurse */
00257     for (i = 1; i <= GD_n_cluster(g); i++) {
00258         subg = GD_clust(g)[i];
00259         if (g != rootg) {
00260             sbb = BB(subg);
00261             sbb.LL.x += bb.LL.x;
00262             sbb.LL.y += bb.LL.y;
00263             sbb.UR.x += bb.LL.x;
00264             sbb.UR.y += bb.LL.y;
00265             BB(subg) = sbb;
00266         }
00267         evalPositions(subg, rootg);
00268     }
00269 }
00270 
00271 #define CL_CHUNK 10
00272 
00273 typedef struct {
00274     graph_t **cl;
00275     int sz;
00276     int cnt;
00277 } clist_t;
00278 
00279 static void initCList(clist_t * clist)
00280 {
00281     clist->cl = 0;
00282     clist->sz = 0;
00283     clist->cnt = 0;
00284 }
00285 
00286 /* addCluster:
00287  * Append a new cluster to the list.
00288  * NOTE: cl[0] is empty. The clusters are in cl[1..cnt].
00289  * Normally, we increase the array when cnt == sz.
00290  * The test for cnt > sz is necessary for the first time.
00291  */
00292 static void addCluster(clist_t * clist, graph_t * subg)
00293 {
00294     clist->cnt++;
00295     if (clist->cnt >= clist->sz) {
00296         clist->sz += CL_CHUNK;
00297         clist->cl = RALLOC(clist->sz, clist->cl, graph_t *);
00298     }
00299     clist->cl[clist->cnt] = subg;
00300 }
00301 
00302 #define BSZ 1000
00303 
00304 /* portName:
00305  * Generate a name for a port.
00306  * We use the name of the subgraph and names of the nodes on the edge,
00307  * if possible. Otherwise, we use the ids of the nodes.
00308  * This is for debugging. For production, just use edge id and some
00309  * id for the graph. Note that all the graphs are subgraphs of the
00310  * root graph.
00311  */
00312 static char *portName(graph_t * g, bport_t * p)
00313 {
00314     edge_t *e = p->e;
00315     node_t *h = e->head;
00316     node_t *t = e->tail;
00317     static char buf[BSZ + 1];
00318     int len = 8;
00319 
00320     len += strlen(g->name) + strlen(h->name) + strlen(t->name);
00321     if (len >= BSZ)
00322         sprintf(buf, "_port_%s_%s_%s_%d", g->name, t->name, h->name,
00323                 e->id);
00324     else
00325         sprintf(buf, "_port_%s_(%d)_(%d)_%d", g->name, ND_id(t), ND_id(h),
00326                 e->id);
00327     return buf;
00328 }
00329 
00330 /* chkPos:
00331  * If cluster has coords attribute, use to supply initial position
00332  * of derived node.
00333  * Only called if G_coord is defined.
00334  * We also look at the parent graph's G_coord attribute. If this
00335  * is identical to the child graph, we have to assume the child
00336  * inherited it.
00337  */
00338 static void chkPos(graph_t* g, node_t* n, layout_info* infop, boxf* bbp)
00339 {
00340     char *p;
00341     char *pp;
00342     boxf bb;
00343     char c;
00344     graph_t *parent;
00345     attrsym_t *G_coord = infop->G_coord;
00346 
00347     p = agxget(g, G_coord->index);
00348     if (p[0]) {
00349         if (g != infop->rootg) {
00350             parent =
00351                 agusergraph((agfstin(g->meta_node->graph, g->meta_node))->
00352                             tail);
00353             pp = agxget(parent, G_coord->index);
00354             if ((pp == p) || !strcmp(p, pp))
00355                 return;
00356         }
00357         c = '\0';
00358         if (sscanf(p, "%lf,%lf,%lf,%lf%c",
00359                    &bb.LL.x, &bb.LL.y, &bb.UR.x, &bb.UR.y, &c) >= 4) {
00360             if (PSinputscale > 0.0) {
00361                 bb.LL.x /= PSinputscale;
00362                 bb.LL.y /= PSinputscale;
00363                 bb.UR.x /= PSinputscale;
00364                 bb.UR.y /= PSinputscale;
00365             }
00366             if (c == '!')
00367                 ND_pinned(n) = P_PIN;
00368             else if (c == '?')
00369                 ND_pinned(n) = P_FIX;
00370             else
00371                 ND_pinned(n) = P_SET;
00372             *bbp = bb;
00373         } else
00374             agerr(AGWARN, "graph %s, coord %s, expected four doubles\n",
00375                   g->name, p);
00376     }
00377 }
00378 
00379 /* addEdge:
00380  * Add real edge e to its image de in the derived graph.
00381  * We use the to_virt and count fields to store the list.
00382  */
00383 static void addEdge(edge_t * de, edge_t * e)
00384 {
00385     short cnt = ED_count(de);
00386     edge_t **el;
00387 
00388     el = (edge_t **) (ED_to_virt(de));
00389     el = ALLOC(cnt + 1, el, edge_t *);
00390     el[cnt] = e;
00391     ED_to_virt(de) = (edge_t *) el;
00392     ED_count(de)++;
00393 }
00394 
00395 /* copyAttr:
00396  * Copy given attribute from g to dg.
00397  */
00398 static void
00399 copyAttr (graph_t* g, graph_t* dg, char* attr)
00400 {
00401     char*     ov_val;
00402     Agsym_t*  ov;
00403 
00404     if ((ov = agfindattr(g, attr))) {
00405         ov_val = agxget(g,ov->index);
00406         ov = agfindattr(dg, attr);
00407         if (ov)
00408             agxset (dg, ov->index, ov_val);
00409         else
00410             agraphattr(dg, attr, ov_val);
00411     }
00412 }
00413 
00414 /* deriveGraph:
00415  * Create derived graph of g by collapsing clusters into
00416  * nodes. An edge is created between nodes if there is
00417  * an edge between two nodes in the clusters of the base graph.
00418  * Such edges record all corresponding real edges.
00419  * In addition, we add a node and edge for each port.
00420  */
00421 static graph_t *deriveGraph(graph_t * g, layout_info * infop)
00422 {
00423     graph_t *dg;
00424     node_t *dn;
00425     graph_t *subg;
00426     char name[100];
00427     bport_t *pp;
00428     node_t *n;
00429     edge_t *de;
00430     int i, id = 0;
00431 
00432     sprintf(name, "_dg_%d", infop->gid++);
00433     if (Verbose >= 2)
00434         fprintf(stderr, "derive graph %s of %s\n", name, g->name);
00435     dg = agopen(name, AGRAPHSTRICT);
00436     GD_alg(dg) = (void *) NEW(gdata);   /* freed in freeDeriveGraph */
00437 #ifdef DEBUG
00438     GORIG(dg) = g;
00439 #endif
00440     GD_ndim(dg) = GD_ndim(g);
00441 
00442     /* Copy attributes from g.
00443      */
00444     copyAttr(g,dg,"overlap");
00445     copyAttr(g,dg,"sep");
00446     copyAttr(g,dg,"K");
00447 
00448     /* create derived nodes from clusters */
00449     for (i = 1; i <= GD_n_cluster(g); i++) {
00450         boxf fix_bb = {{ MAXDOUBLE, MAXDOUBLE },{ -MAXDOUBLE, -MAXDOUBLE }};
00451         subg = GD_clust(g)[i];
00452 
00453         do_graph_label(subg);
00454         dn = mkDeriveNode(dg, subg->name);
00455         ND_clust(dn) = subg;
00456         ND_id(dn) = id++;
00457         if (infop->G_coord)
00458                 chkPos(subg, dn, infop, &fix_bb);
00459         for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
00460             DNODE(n) = dn;
00461 #ifdef UNIMPLEMENTED
00462 /* This code starts the implementation of supporting pinned nodes
00463  * within clusters. This needs more work. In particular, we may need
00464  * a separate notion of pinning related to contained nodes, which will
00465  * allow the cluster itself to wiggle.
00466  */
00467             if (ND_pinned(n)) {
00468                 fix_bb.LL.x = MIN(fix_bb.LL.x, ND_pos(n)[0]);
00469                 fix_bb.LL.y = MIN(fix_bb.LL.y, ND_pos(n)[1]);
00470                 fix_bb.UR.x = MAX(fix_bb.UR.x, ND_pos(n)[0]);
00471                 fix_bb.UR.y = MAX(fix_bb.UR.y, ND_pos(n)[1]);
00472                 ND_pinned(dn) = MAX(ND_pinned(dn), ND_pinned(n));
00473             }
00474 #endif
00475         }
00476         if (ND_pinned(dn)) {
00477             ND_pos(dn)[0] = (fix_bb.LL.x + fix_bb.UR.x) / 2;
00478             ND_pos(dn)[1] = (fix_bb.LL.y + fix_bb.UR.y) / 2;
00479         }
00480     }
00481 
00482     /* create derived nodes from remaining nodes */
00483     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00484         if (!DNODE(n)) {
00485             if (PARENT(n) && (PARENT(n) != GPARENT(g))) {
00486                 agerr (AGERR, "node \"%s\" is contained in two non-comparable clusters \"%s\" and \"%s\"\n", n->name, g->name, PARENT(n)->name);
00487                 exit (1);
00488             }
00489             PARENT(n) = g;
00490             if (IS_CLUST_NODE(n))
00491                 continue;
00492             dn = mkDeriveNode(dg, n->name);
00493             DNODE(n) = dn;
00494             ND_id(dn) = id++;
00495             ND_width(dn) = ND_width(n);
00496             ND_height(dn) = ND_height(n);
00497             ND_xsize(dn) = ND_xsize(n);
00498             ND_ysize(dn) = ND_ysize(n);
00499             ND_shape(dn) = ND_shape(n);
00500             ND_shape_info(dn) = ND_shape_info(n);
00501             if (ND_pinned(n)) {
00502                 ND_pos(dn)[0] = ND_pos(n)[0];
00503                 ND_pos(dn)[1] = ND_pos(n)[1];
00504                 ND_pinned(dn) = ND_pinned(n);
00505             }
00506             ANODE(dn) = n;
00507         }
00508     }
00509 
00510     /* add edges */
00511     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00512         edge_t *e;
00513         node_t *hd;
00514         node_t *tl = DNODE(n);
00515         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00516             hd = DNODE(e->head);
00517             if (hd == tl)
00518                 continue;
00519             if (hd > tl)
00520                 de = agedge(dg, tl, hd);
00521             else
00522                 de = agedge(dg, hd, tl);
00523             ED_dist(de) = ED_dist(e);
00524             ED_factor(de) = ED_factor(e);
00525             /* fprintf (stderr, "edge %s -- %s\n", tl->name, hd->name); */
00526             WDEG(hd)++;
00527             WDEG(tl)++;
00528             if (NEW_EDGE(de)) {
00529                 DEG(hd)++;
00530                 DEG(tl)++;
00531             }
00532             addEdge(de, e);
00533         }
00534     }
00535 
00536     /* transform ports */
00537     if ((pp = PORTS(g))) {
00538         bport_t *pq;
00539         node_t *m;
00540         int sz = NPORTS(g);
00541 
00542         /* freed in freeDeriveGraph */
00543         PORTS(dg) = pq = N_NEW(sz + 1, bport_t);
00544         sz = 0;
00545         while (pp->e) {
00546             m = DNODE(pp->n);
00547             /* Create port in derived graph only if hooks to internal node */
00548             if (m) {
00549                 dn = mkDeriveNode(dg, portName(g, pp));
00550                 sz++;
00551                 ND_id(dn) = id++;
00552                 if (dn > m)
00553                     de = agedge(dg, m, dn);
00554                 else
00555                     de = agedge(dg, dn, m);
00556                 ED_dist(de) = ED_dist(pp->e);
00557                 ED_factor(de) = ED_factor(pp->e);
00558                 addEdge(de, pp->e);
00559                 WDEG(dn)++;
00560                 WDEG(m)++;
00561                 DEG(dn)++;      /* ports are unique, so this will be the first and */
00562                 DEG(m)++;       /* only time the edge is touched. */
00563                 pq->n = dn;
00564                 pq->alpha = pp->alpha;
00565                 pq->e = de;
00566                 pq++;
00567             }
00568             pp++;
00569         }
00570         NPORTS(dg) = sz;
00571     }
00572 
00573     return dg;
00574 }
00575 
00576 typedef struct {
00577     edge_t *e;
00578     double alpha;
00579     double dist2;
00580 } erec;
00581 
00582 /* ecmp:
00583  * Sort edges by angle, then distance.
00584  */
00585 static int ecmp(const void *v1, const void *v2)
00586 {
00587     erec *e1 = (erec *) v1;
00588     erec *e2 = (erec *) v2;
00589     if (e1->alpha > e2->alpha)
00590         return 1;
00591     else if (e1->alpha < e2->alpha)
00592         return -1;
00593     else if (e1->dist2 > e2->dist2)
00594         return 1;
00595     else if (e1->dist2 < e2->dist2)
00596         return -1;
00597     else
00598         return 0;
00599 }
00600 
00601 #define ANG (M_PI/90)           /* Maximum angular change: 2 degrees */
00602 
00603 /* getEdgeList:
00604  * Generate list of edges in derived graph g using
00605  * node n. The list is in counterclockwise order.
00606  * This, of course, assumes we have an initial layout for g.
00607  */
00608 static erec *getEdgeList(node_t * n, graph_t * g)
00609 {
00610     erec *erecs;
00611     int deg = DEG(n);
00612     int i;
00613     double dx, dy;
00614     edge_t *e;
00615     node_t *m;
00616 
00617     /* freed in expandCluster */
00618     erecs = N_NEW(deg + 1, erec);
00619     i = 0;
00620     for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
00621         if (e->head == n)
00622             m = e->tail;
00623         else
00624             m = e->head;
00625         dx = ND_pos(m)[0] - ND_pos(n)[0];
00626         dy = ND_pos(m)[1] - ND_pos(n)[1];
00627         erecs[i].e = e;
00628         erecs[i].alpha = atan2(dy, dx);
00629         erecs[i].dist2 = dx * dx + dy * dy;
00630         i++;
00631     }
00632     assert(i == deg);
00633     qsort(erecs, deg, sizeof(erec), ecmp);
00634 
00635     /* ensure no two angles are equal */
00636     if (deg >= 2) {
00637         int j;
00638         double a, inc, delta, bnd;
00639 
00640         i = 0;
00641         while (i < deg - 1) {
00642             a = erecs[i].alpha;
00643             j = i + 1;
00644             while ((j < deg) && (erecs[j].alpha == a))
00645                 j++;
00646             if (j == i + 1)
00647                 i = j;
00648             else {
00649                 if (j == deg)
00650                     bnd = M_PI; /* all values equal up to end */
00651                 else
00652                     bnd = erecs[j].alpha;
00653                 delta = (bnd - a) / (j - i);
00654                 if (delta > ANG)
00655                     delta = ANG;
00656                 inc = 0;
00657                 for (; i < j; i++) {
00658                     erecs[i].alpha += inc;
00659                     inc += delta;
00660                 }
00661             }
00662         }
00663     }
00664 
00665     return erecs;
00666 }
00667 
00668 /* genPorts:
00669  * Given list of edges with node n in derived graph, add corresponding
00670  * ports to port list pp, starting at index idx. Return next index.
00671  * If an edge in the derived graph corresponds to multiple real edges,
00672  * add them in order if address of n is smaller than other node address.
00673  * Otherwise, reverse order.
00674  * Attach angles. The value bnd gives next angle after er->alpha.
00675  */
00676 static int
00677 genPorts(node_t * n, erec * er, bport_t * pp, int idx, double bnd)
00678 {
00679     node_t *other;
00680     int cnt;
00681     edge_t *e = er->e;
00682     edge_t *el;
00683     edge_t **ep;
00684     double angle, delta;
00685     int i, j, inc;
00686 
00687     cnt = ED_count(e);
00688 
00689     if (e->head == n)
00690         other = e->tail;
00691     else
00692         other = e->head;
00693 
00694     delta = (bnd - er->alpha) / cnt;
00695     angle = er->alpha;
00696     if (delta > ANG)
00697         delta = ANG;
00698 
00699     if (n < other) {
00700         i = idx;
00701         inc = 1;
00702     } else {
00703         i = idx + cnt - 1;
00704         inc = -1;
00705         angle += delta * (cnt - 1);
00706         delta = -delta;
00707     }
00708 
00709     ep = (edge_t **) (el = ED_to_virt(e));
00710     for (j = 0; j < ED_count(e); j++, ep++) {
00711         el = *ep;
00712         pp[i].e = el;
00713         pp[i].n = (DNODE(el->tail) == n ? el->tail : el->head);
00714         pp[i].alpha = angle;
00715         i += inc;
00716         angle += delta;
00717     }
00718     return (idx + cnt);
00719 }
00720 
00721 /* expandCluster;
00722  * Given positioned derived graph cg with node n which corresponds
00723  * to a cluster, generate a graph containing the interior of the
00724  * cluster, plus port information induced by the layout of cg.
00725  * Basically, we use the cluster subgraph to which n corresponds,
00726  * attached with port information.
00727  */
00728 static graph_t *expandCluster(node_t * n, graph_t * cg)
00729 {
00730     erec *es;
00731     erec *ep;
00732     erec *next;
00733     graph_t *sg = ND_clust(n);
00734     bport_t *pp;
00735     int sz = WDEG(n);
00736     int idx = 0;
00737     double bnd;
00738 
00739     if (sz != 0) {
00740         /* freed in cleanup_subgs */
00741         pp = N_NEW(sz + 1, bport_t);
00742 
00743         /* create sorted list of edges of n */
00744         es = ep = getEdgeList(n, cg);
00745 
00746         /* generate ports from edges */
00747         while (ep->e) {
00748             next = ep + 1;
00749             if (next->e)
00750                 bnd = next->alpha;
00751             else
00752                 bnd = 2 * M_PI + es->alpha;
00753             idx = genPorts(n, ep, pp, idx, bnd);
00754             ep = next;
00755         }
00756         assert(idx == sz);
00757 
00758         PORTS(sg) = pp;
00759         NPORTS(sg) = sz;
00760         free(es);
00761     }
00762     return sg;
00763 }
00764 
00765 /* setClustNodes:
00766  * At present, cluster nodes are not assigned a position during layout,
00767  * but positioned in the center of its associated cluster. Because the
00768  * dummy edge associated with a cluster node may not occur at a sufficient
00769  * level of cluster, the edge may not be used during layout and we cannot
00770  * therefore rely find these nodes via ports.
00771  *
00772  * In this implementation, we just do a linear pass over all nodes in the
00773  * root graph. At some point, we may use a better method, like having each
00774  * cluster contain its list of cluster nodes, or have the graph keep a list.
00775  * 
00776  * As nodes, we need to assign cluster nodes the coordinates in the
00777  * coordinates of its cluster p. Note that p's bbox is in its parent's
00778  * coordinates. 
00779  * 
00780  * If routing, we may decide to place on cluster boundary,
00781  * and use polyline.
00782  */
00783 static void 
00784 setClustNodes(graph_t* root)
00785 {
00786     boxf bb;
00787     graph_t* p;
00788     pointf ctr;
00789     node_t *n;
00790     double w, h;
00791     int h2, w2, h_i;
00792     pointf *vertices;
00793 
00794     for (n = agfstnode(root); n; n = agnxtnode(root, n)) {
00795         if (!IS_CLUST_NODE(n)) continue;
00796 
00797         p = PARENT(n);
00798         bb = BB(p);  /* bbox in parent cluster's coordinates */
00799         w = bb.UR.x - bb.LL.x;
00800         h = bb.UR.y - bb.LL.y;
00801         ctr.x = w / 2.0;
00802         ctr.y = h / 2.0;
00803         w2 = POINTS(w / 2.0);
00804         h2 = POINTS(h / 2.0);
00805         h_i = POINTS(h);
00806         ND_pos(n)[0] = ctr.x;
00807         ND_pos(n)[1] = ctr.y;
00808         ND_width(n) = w;
00809         ND_height(n) = h;
00810         ND_xsize(n) = POINTS(w);
00811         ND_lw_i(n) = ND_rw_i(n) = w2;
00812         ND_ht_i(n) = ND_ysize(n) = h_i;
00813 
00814         vertices = ((polygon_t *) ND_shape_info(n))->vertices;
00815         vertices[0].x = ND_rw_i(n);
00816         vertices[0].y = h2;
00817         vertices[1].x = -ND_lw_i(n);
00818         vertices[1].y = h2;
00819         vertices[2].x = -ND_lw_i(n);
00820         vertices[2].y = -h2;
00821         vertices[3].x = ND_rw_i(n);
00822         vertices[3].y = -h2;
00823     }
00824 }
00825 
00826 /* layout:
00827  * Given g with ports:
00828  *  Derive g' from g by reducing clusters to points (deriveGraph)
00829  *  Compute connected components of g' (findCComp)
00830  *  For each cc of g': 
00831  *    Layout cc (tLayout)
00832  *    For each node n in cc of g' <-> cluster c in g:
00833  *      Add ports based on layout of cc to get c' (expandCluster)
00834  *      Layout c' (recursion)
00835  *    Remove ports from cc
00836  *    Expand nodes of cc to reflect size of c'  (xLayout)
00837  *  Pack connected components to get layout of g (putGraphs)
00838  *  Translate layout so that bounding box of layout + margin 
00839  *  has the origin as LL corner. 
00840  *  Set position of top level clusters and real nodes.
00841  *  Set bounding box of graph
00842  * 
00843  * TODO:
00844  * 
00845  * Possibly should modify so that only do connected components
00846  * on top-level derived graph. Unconnected parts of a cluster
00847  * could just rattle within cluster boundaries. This may mix
00848  * up components but give a tighter packing.
00849  * 
00850  * Add edges per components to get better packing, rather than
00851  * wait until the end.
00852  */
00853 /* static */ void layout(graph_t * g, layout_info * infop)
00854 {
00855     point *pts = NULL;
00856     graph_t *dg;
00857     node_t *dn;
00858     node_t *n;
00859     graph_t *cg;
00860     graph_t *sg;
00861     graph_t **cc;
00862     graph_t **pg;
00863     int c_cnt;
00864     int pinned;
00865     xparams xpms;
00866 
00867 #ifdef DEBUG
00868     incInd();
00869 #endif
00870     if (Verbose) {
00871 #ifdef DEBUG
00872         prIndent();
00873 #endif
00874         fprintf (stderr, "layout %s\n", g->name);
00875     }
00876     /* initialize derived node pointers */
00877     for (n = agfstnode(g); n; n = agnxtnode(g, n))
00878         DNODE(n) = 0;
00879 
00880     dg = deriveGraph(g, infop);
00881     cc = pg = findCComp(dg, &c_cnt, &pinned);
00882 
00883     while ((cg = *pg++)) {
00884         fdp_tLayout(cg, &xpms);
00885         for (n = agfstnode(cg); n; n = agnxtnode(cg, n)) {
00886             if (ND_clust(n)) {
00887                 point pt;
00888                 sg = expandCluster(n, cg);      /* attach ports to sg */
00889                 layout(sg, infop);
00890                 /* bb.LL == origin */
00891                 ND_width(n) = BB(sg).UR.x;
00892                 ND_height(n) = BB(sg).UR.y;
00893                 pt = cvt2pt(BB(sg).UR);
00894                 ND_xsize(n) = pt.x;
00895                 ND_ysize(n) = pt.y;
00896             } else if (IS_PORT(n))
00897                 agdelete(cg, n);        /* remove ports from component */
00898         }
00899 
00900         /* Remove overlaps */
00901         if (agnnodes(cg) >= 2) {
00902             if (g == infop->rootg)
00903                 normalize (cg);
00904             fdp_xLayout(cg, &xpms);
00905         }
00906         /* set bounding box but don't use ports */
00907         /* setBB (cg); */
00908     }
00909 
00910     /* At this point, each connected component has its nodes correctly
00911      * positioned. If we have multiple components, we pack them together.
00912      * All nodes will be moved to their new positions.
00913      * NOTE: packGraphs uses nodes in components, so if port nodes are
00914      * not removed, it won't work.
00915      */
00916     /* Handle special cases well: no ports to real internal nodes
00917      *   Place cluster edges separately, after layout.
00918      * How to combine parts, especially with disparate components?
00919      */
00920     if (c_cnt > 1) {
00921         boolean *bp;
00922         if (pinned) {
00923             bp = N_NEW(c_cnt, boolean);
00924             bp[0] = TRUE;
00925         } else
00926             bp = 0;
00927         infop->pack.fixed = bp;
00928         pts = putGraphs(c_cnt, cc, NULL, &infop->pack);
00929         if (bp)
00930             free(bp);
00931     } else {
00932         pts = NULL;
00933         if (c_cnt == 1)
00934             compute_bb(cc[0]);
00935     }
00936 
00937     /* set bounding box of dg and reposition nodes */
00938     finalCC(dg, c_cnt, cc, pts, g, infop);
00939     free (pts);
00940 
00941     /* record positions from derived graph to input graph */
00942     /* At present, this does not record port node info */
00943     /* In fact, as noted above, we have removed port nodes */
00944     for (dn = agfstnode(dg); dn; dn = agnxtnode(dg, dn)) {
00945         if ((sg = ND_clust(dn))) {
00946             BB(sg).LL.x = ND_pos(dn)[0] - ND_width(dn) / 2;
00947             BB(sg).LL.y = ND_pos(dn)[1] - ND_height(dn) / 2;
00948             BB(sg).UR.x = BB(sg).LL.x + ND_width(dn);
00949             BB(sg).UR.y = BB(sg).LL.y + ND_height(dn);
00950         } else if ((n = ANODE(dn))) {
00951             ND_pos(n)[0] = ND_pos(dn)[0];
00952             ND_pos(n)[1] = ND_pos(dn)[1];
00953         }
00954     }
00955     BB(g) = BB(dg);
00956 #ifdef DEBUG
00957     if (g == infop->rootg)
00958         dump(g, 1, 0);
00959 #endif
00960 
00961     /* clean up temp graphs */
00962     freeDerivedGraph(dg, cc);
00963     free(cc);
00964     if (Verbose) {
00965 #ifdef DEBUG
00966         prIndent ();
00967 #endif
00968         fprintf (stderr, "end %s\n", g->name);
00969     }
00970 #ifdef DEBUG
00971     decInd();
00972 #endif
00973 }
00974 
00975 /* setBB;
00976  * Set point box g->bb from inch box BB(g).
00977  */
00978 static void setBB(graph_t * g)
00979 {
00980     int i;
00981     GD_bb(g).LL = cvt2pt(BB(g).LL);
00982     GD_bb(g).UR = cvt2pt(BB(g).UR);
00983     for (i = 1; i <= GD_n_cluster(g); i++) {
00984         setBB(GD_clust(g)[i]);
00985     }
00986 }
00987 
00988 /* init_info:
00989  * Initialize graph-dependent information and
00990  * state variable.s
00991  */
00992 void init_info(graph_t * g, layout_info * infop)
00993 {
00994     infop->G_coord = agfindattr(g, "coords");
00995     infop->G_width = agfindattr(g, "width");
00996     infop->G_height = agfindattr(g, "height");
00997     infop->rootg = g;
00998     infop->gid = 0;
00999     infop->pack.margin = getPack(g, CL_OFFSET / 2, CL_OFFSET / 2);
01000     infop->pack.doSplines = 0;
01001     infop->pack.mode = getPackMode(g, l_node);
01002 }
01003 
01004 /* mkClusters:
01005  * Attach list of immediate child clusters.
01006  * NB: By convention, the indexing starts at 1.
01007  * If pclist is NULL, the graph is the root graph or a cluster
01008  * If pclist is non-NULL, we are recursively scanning a non-cluster
01009  * subgraph for cluster children.
01010  */
01011 static void
01012 mkClusters (graph_t * g, clist_t* pclist, graph_t* parent)
01013 {
01014     node_t*  mn;
01015     edge_t*  me;
01016     graph_t* mg;
01017     graph_t* subg;
01018     clist_t  list;
01019     clist_t* clist;
01020 
01021     if (pclist == NULL) {
01022         clist = &list;
01023         initCList(clist);
01024     }
01025     else
01026         clist = pclist;
01027     mg = g->meta_node->graph;
01028     for (me = agfstout(mg, g->meta_node); me; me = agnxtout(mg, me)) {
01029         mn = me->head;
01030         subg = agusergraph(mn);
01031         if (!strncmp(subg->name, "cluster", 7)) {
01032             GD_alg(subg) = (void *) NEW(gdata); /* freed in cleanup_subgs */
01033             GD_ndim(subg) = GD_ndim(parent);
01034             LEVEL(subg) = LEVEL(parent) + 1;
01035             GPARENT(subg) = parent;
01036             addCluster(clist, subg);
01037             mkClusters(subg, NULL, subg);
01038         }
01039         else {
01040             mkClusters(subg, clist, parent);
01041         }
01042     }
01043     if (pclist == NULL) {
01044         GD_n_cluster(g) = list.cnt;
01045         if (list.cnt)
01046             GD_clust(g) = RALLOC(list.cnt + 1, list.cl, graph_t*);
01047     }
01048 }
01049 
01050 void fdp_init_graph(Agraph_t * g)
01051 {
01052     setEdgeType (g, ET_LINE);
01053     GD_alg(g) = (void *) NEW(gdata);    /* freed in cleanup_graph */
01054     g->u.ndim = late_int(g, agfindattr(g, "dim"), 2, 2);
01055     Ndim = g->u.ndim = MIN(g->u.ndim, MAXDIM);
01056 
01057     mkClusters (g, NULL, g);
01058     fdp_initParams(g);
01059     fdp_init_node_edge(g);
01060 }
01061 
01062 void fdpLayout(graph_t * g)
01063 {
01064     layout_info info;
01065 
01066     init_info(g, &info);
01067     layout(g, &info);
01068     setClustNodes(g);
01069     evalPositions(g,g);
01070 
01071     /* Set bbox info for g and all clusters. This is needed for
01072      * spline drawing. We already know the graph bbox is at the origin.
01073      * (We pass the origin to spline_edges0. This really isn't true,
01074      * as the algorithm has done many translations.)
01075      * On return from spline drawing, all bounding boxes should be correct.
01076      */
01077     setBB(g);
01078 }
01079 
01080 static void
01081 fdpSplines (graph_t * g)
01082 {
01083     int trySplines = 0;
01084     int et = EDGE_TYPE(g);
01085 
01086     if (et != ET_LINE) {
01087         if (et == ET_COMPOUND) {
01088             trySplines = splineEdges(g, compoundEdges, ET_SPLINE);
01089             /* When doing the edges again, accept edges done by compoundEdges */
01090             if (trySplines)
01091                 Nop = 2;
01092         }
01093         if (trySplines || (et == ET_SPLINE)) {
01094             if (HAS_CLUST_EDGE(g)) {
01095                 agerr(AGWARN,
01096                       "splines and cluster edges not supported - using line segments\n");
01097             } else {
01098                 spline_edges1(g, ET_SPLINE);
01099             }
01100         }
01101     }
01102     if (State < GVSPLINES)
01103         spline_edges1(g, ET_LINE);
01104 }
01105 
01106 void fdp_layout(graph_t * g)
01107 {
01108     fdp_init_graph(g);
01109     fdpLayout(g);
01110     neato_set_aspect(g);
01111 
01112     if (EDGE_TYPE(g) != ET_NONE) fdpSplines (g); 
01113 
01114     dotneato_postprocess(g);
01115 }

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