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