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

Go to the documentation of this file.
00001 /* $Id: neatoinit.c,v 1.36 2008/03/03 23:01:51 ellson Exp $ $Revision: 1.36 $ */
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 <time.h>
00023 #ifndef WIN32
00024 #include <unistd.h>
00025 #endif
00026 #include "neato.h"
00027 #include "pack.h"
00028 #include "stress.h"
00029 #ifdef DIGCOLA
00030 #include "digcola.h"
00031 #endif
00032 #include "kkutils.h"
00033 #include "pointset.h"
00034 #include <ctype.h>
00035 
00036 #ifndef HAVE_SRAND48
00037 #define srand48 srand
00038 #endif
00039 
00040 static attrsym_t *N_pos;
00041 static int Pack;                /* If >= 0, layout components separately and pack together
00042                                  * The value of Pack gives margins around graphs.
00043                                  */
00044 static char *cc_pfx = "_neato_cc";
00045 
00046 void neato_nodesize(node_t * n, boolean flip)
00047 {
00048     int w;
00049 
00050     w = ND_xsize(n) = POINTS(ND_width(n));
00051     ND_lw_i(n) = ND_rw_i(n) = w / 2;
00052     ND_ht_i(n) = ND_ysize(n) = POINTS(ND_height(n));
00053 }
00054 
00055 void neato_init_node(node_t * n)
00056 {
00057     common_init_node(n);
00058     ND_pos(n) = N_NEW(GD_ndim(n->graph), double);
00059     neato_nodesize(n, GD_flip(n->graph));
00060 }
00061 
00062 void neato_init_edge(edge_t * e)
00063 {
00064     common_init_edge(e);
00065 
00066     ED_factor(e) = late_double(e, E_weight, 1.0, 1.0);
00067 }
00068 
00069 int user_pos(attrsym_t * posptr, attrsym_t * pinptr, node_t * np, int nG)
00070 {
00071     double *pvec;
00072     char *p, c;
00073     double z;
00074 
00075     if (posptr == NULL)
00076         return FALSE;
00077     pvec = ND_pos(np);
00078     p = agxget(np, posptr->index);
00079     if (p[0]) {
00080         c = '\0';
00081         if ((Ndim >= 3) && 
00082             (sscanf(p, "%lf,%lf,%lf%c", pvec, pvec+1, pvec+2, &c) >= 3)){
00083             ND_pinned(np) = P_SET;
00084             if (PSinputscale > 0.0) {
00085                 int i;
00086                 for (i = 0; i < Ndim; i++)
00087                     pvec[i] = pvec[i] / PSinputscale;
00088             }
00089             if (Ndim > 3)
00090                 jitter_d(np, nG, 3);
00091             if ((c == '!')
00092                 || (pinptr && mapbool(agxget(np, pinptr->index))))
00093                 ND_pinned(np) = P_PIN;
00094             return TRUE;
00095         }
00096         else if (sscanf(p, "%lf,%lf%c", pvec, pvec + 1, &c) >= 2) {
00097             ND_pinned(np) = P_SET;
00098             if (PSinputscale > 0.0) {
00099                 int i;
00100                 for (i = 0; i < Ndim; i++)
00101                     pvec[i] = pvec[i] / PSinputscale;
00102             }
00103             if (Ndim > 2) {
00104                 if (N_z && (p = agxget(np, N_z->index)) && 
00105                            (sscanf(p,"%lf",&z) == 1)) { 
00106                     if (PSinputscale > 0.0) {
00107                         pvec[2] = z / PSinputscale;
00108                     }
00109                     else
00110                         pvec[2] = z;
00111                     jitter_d(np, nG, 3);
00112                 }
00113                 else
00114                     jitter3d(np, nG);
00115             }
00116             if ((c == '!')
00117                 || (pinptr && mapbool(agxget(np, pinptr->index))))
00118                 ND_pinned(np) = P_PIN;
00119             return TRUE;
00120         } else
00121             agerr(AGERR, "node %s, position %s, expected two doubles\n",
00122                   np->name, p);
00123     }
00124     return FALSE;
00125 }
00126 
00127 void neato_init_node_edge(graph_t * g)
00128 {
00129     node_t *n;
00130     edge_t *e;
00131     int nG = agnnodes(g);
00132     attrsym_t *N_pin;
00133 
00134     N_pos = agfindattr(g->proto->n, "pos");
00135     N_pin = agfindattr(g->proto->n, "pin");
00136 
00137     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00138         neato_init_node(n);
00139         user_pos(N_pos, N_pin, n, nG);  /* set user position if given */
00140     }
00141     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00142         for (e = agfstout(g, n); e; e = agnxtout(g, e))
00143             neato_init_edge(e);
00144     }
00145 }
00146 
00147 void neato_cleanup_node(node_t * n)
00148 {
00149     if (ND_shape(n)) {
00150         ND_shape(n)->fns->freefn(n);
00151     }
00152     free(ND_pos(n));
00153     free_label(ND_label(n));
00154     memset(&(n->u), 0, sizeof(Agnodeinfo_t));
00155 }
00156 
00157 void neato_free_splines(edge_t * e)
00158 {
00159     int i;
00160     if (ED_spl(e)) {
00161         for (i = 0; i < ED_spl(e)->size; i++)
00162             free(ED_spl(e)->list[i].list);
00163         free(ED_spl(e)->list);
00164         free(ED_spl(e));
00165     }
00166     ED_spl(e) = NULL;
00167 }
00168 
00169 void neato_cleanup_edge(edge_t * e)
00170 {
00171     neato_free_splines(e);
00172     free_label(ED_label(e));
00173     memset(&(e->u), 0, sizeof(Agedgeinfo_t));
00174 }
00175 
00176 void neato_cleanup_graph(graph_t * g)
00177 {
00178     if (Nop || (Pack < 0))
00179         free_scan_graph(g);
00180     if (g != g->root) memset(&(g->u), 0, sizeof(Agraphinfo_t));
00181 }
00182 
00183 void neato_cleanup(graph_t * g)
00184 {
00185     node_t *n;
00186     edge_t *e;
00187 
00188     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00189         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00190             neato_cleanup_edge(e);
00191         }
00192         neato_cleanup_node(n);
00193     }
00194     neato_cleanup_graph(g);
00195 }
00196 
00197 static int numFields(unsigned char *pos)
00198 {
00199     int cnt = 0;
00200     unsigned char c;
00201 
00202     do {
00203         while (isspace(*pos))
00204             pos++;              /* skip white space */
00205         if ((c = *pos)) { /* skip token */
00206             cnt++;
00207             while ((c = *pos) && !isspace(c) && (c != ';'))
00208                 pos++;
00209         }
00210     } while (isspace(c));
00211     return cnt;
00212 }
00213 
00214 static void set_elabel(edge_t * e, textlabel_t * l, char *name)
00215 {
00216     double x, y;
00217     point pt;
00218     char *lp;
00219     lp = agget(e, name);
00220     if (lp && (sscanf(lp, "%lf,%lf", &x, &y) == 2)) {
00221         pt.x = (int) (x);
00222         pt.y = (int) (y);
00223         l->p = pt;
00224         l->set = TRUE;
00225     }
00226 }
00227 
00228 #ifdef IPSEPCOLA
00229 static cluster_data* cluster_map(graph_t *mastergraph, graph_t *g)
00230 {
00231     /* search meta-graph to find clusters */
00232     graph_t *mg, *subg;
00233     node_t *mm, *mn;
00234     node_t *n;
00235     edge_t *me;
00236      /* array of arrays of node indices in each cluster */
00237     int **cs,*cn;
00238     int i,j,nclusters=0;
00239     boolean* assigned = N_NEW(agnnodes(g), boolean);
00240     cluster_data *cdata = GNEW(cluster_data);
00241 
00242     cdata->ntoplevel = agnnodes(g);
00243     mm = mastergraph->meta_node;
00244     mg = mm->graph;
00245     for (me = agfstout(mg, mm); me; me = agnxtout(mg, me)) {
00246         mn = me->head;
00247         subg = agusergraph(mn);
00248         if (!strncmp(subg->name, "cluster", 7)) {
00249             nclusters++;
00250         }
00251     }
00252     cdata->nvars=0;
00253     cdata->nclusters = nclusters;
00254     cs = cdata->clusters = N_GNEW(nclusters,int*);
00255     cn = cdata->clustersizes = N_GNEW(nclusters,int);
00256     /* fprintf(stderr,"search %d clusters...\n",nclusters); */
00257     for (me = agfstout(mg, mm); me; me = agnxtout(mg, me)) {
00258         mn = me->head;
00259         subg = agusergraph(mn);
00260         /* clusters are processed by separate calls to ordered_edges */
00261         if (!strncmp(subg->name, "cluster", 7)) {
00262             int *c;
00263 
00264             *cn = agnnodes(subg);
00265             cdata->nvars += *cn;
00266             c = *cs++ = N_GNEW(*cn++,int);
00267             /* fprintf(stderr,"Cluster with %d nodes...\n",agnnodes(subg)); */
00268             for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
00269                 node_t *gn;
00270                 int ind = 0;
00271                 for (gn = agfstnode(g); gn; gn = agnxtnode(g, gn)) {
00272                     if(gn->id==n->id) break;
00273                     ind++;
00274                 }
00275                 /* fprintf(stderr,"  node=%s, id=%d, ind=%d\n",n->name,n->id,ind); */
00276                 *c++=ind;
00277                 assigned[ind]=TRUE;
00278                 cdata->ntoplevel--;
00279             }
00280         }
00281     }
00282     cdata->bb=N_GNEW(cdata->nclusters,boxf);
00283     cdata->toplevel=N_GNEW(cdata->ntoplevel,int);
00284     for(i=j=0;i<agnnodes(g);i++) {
00285         if(!assigned[i]) {
00286             cdata->toplevel[j++]=i;
00287         }
00288     }
00289     assert(cdata->ntoplevel==agnnodes(g)-cdata->nvars);
00290     free (assigned);
00291     return cdata;
00292 }
00293 
00294 static void freeClusterData(cluster_data *c) {
00295     if(c->nclusters>0) {
00296         free(c->clusters[0]);
00297         free(c->clusters);
00298         free(c->clustersizes);
00299         free(c->toplevel);
00300         free(c->bb);
00301     }
00302     free(c);
00303 }
00304 #endif
00305 
00306 /* user_spline:
00307  * Attempt to use already existing pos info for spline
00308  * Return 1 if successful, 0 otherwise.
00309  * Assume E_pos != NULL and ED_spl(e) == NULL.
00310  */
00311 static int user_spline(attrsym_t * E_pos, edge_t * e)
00312 {
00313     char *pos;
00314     int i, n, npts, nc;
00315     point *ps = 0;
00316     point *pp;
00317     double x, y;
00318     int sflag = 0, eflag = 0;
00319     point sp = { 0, 0 }, ep = {
00320     0, 0};
00321     bezier *newspl;
00322     int more = 1;
00323     int stype, etype;
00324 
00325     pos = agxget(e, E_pos->index);
00326     if (*pos == '\0')
00327         return 0;
00328 
00329     arrow_flags(e, &stype, &etype);
00330     do {
00331         /* check for s head */
00332         i = sscanf(pos, "s,%lf,%lf%n", &x, &y, &nc);
00333         if (i == 2) {
00334             sflag = 1;
00335             pos = pos + nc;
00336             sp.x = (int) (x);
00337             sp.y = (int) (y);
00338         }
00339 
00340         /* check for e head */
00341         i = sscanf(pos, " e,%lf,%lf%n", &x, &y, &nc);
00342         if (i == 2) {
00343             eflag = 1;
00344             pos = pos + nc;
00345             ep.x = (int) (x);
00346             ep.y = (int) (y);
00347         }
00348 
00349         npts = numFields((unsigned char *) pos);        /* count potential points */
00350         n = npts;
00351         if ((n < 4) || (n % 3 != 1)) {
00352             neato_free_splines(e);
00353             return 0;
00354         }
00355         ps = ALLOC(n, 0, point);
00356         pp = ps;
00357         while (n) {
00358             i = sscanf(pos, "%lf,%lf%n", &x, &y, &nc);
00359             if (i < 2) {
00360                 free(ps);
00361                 neato_free_splines(e);
00362                 return 0;
00363             }
00364             pos = pos + nc;
00365             pp->x = (int) (x);
00366             pp->y = (int) (y);
00367             pp++;
00368             n--;
00369         }
00370         while (isspace(*pos)) pos++;
00371         if (*pos == '\0')
00372             more = 0;
00373         else
00374             pos++;
00375 
00376         /* parsed successfully; create spline */
00377         newspl = new_spline(e, npts);
00378         if (sflag) {
00379             newspl->sflag = stype;
00380             newspl->sp = sp;
00381         }
00382         if (eflag) {
00383             newspl->eflag = etype;
00384             newspl->ep = ep;
00385         }
00386         for (i = 0; i < npts; i++) {
00387             newspl->list[i] = ps[i];
00388         }
00389         free(ps);
00390     } while (more);
00391 
00392     if (ED_label(e))
00393         set_elabel(e, ED_label(e), "lp");
00394     if (ED_head_label(e))
00395         set_elabel(e, ED_head_label(e), "head_lp");
00396     if (ED_tail_label(e))
00397         set_elabel(e, ED_tail_label(e), "tail_lp");
00398 
00399     return 1;
00400 }
00401 
00402 /* Nop can be:
00403  *  0 - do full layout
00404  *  1 - assume initial node positions, do (optional) adjust and all splines
00405  *  2 - assume final node and edges positions, do nothing except compute
00406  *      missing splines
00407  */
00408 
00409  /* Indicates the amount of edges with position information */
00410 typedef enum { NoEdges, SomeEdges, AllEdges } pos_edge;
00411 
00412 /* nop_init_edges:
00413  * Check edges for position info.
00414  * If position info exists, check for edge label positions.
00415  * Return number of edges with position info.
00416  */
00417 static pos_edge nop_init_edges(Agraph_t * g)
00418 {
00419     node_t *n;
00420     edge_t *e;
00421     int nedges = 0;
00422     attrsym_t *E_pos = agfindattr(g->proto->e, "pos");
00423 
00424     if (!E_pos || (Nop < 2))
00425         return NoEdges;
00426 
00427     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00428         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00429             if (user_spline(E_pos, e)) {
00430                 nedges++;
00431             }
00432         }
00433     }
00434     if (nedges) {
00435         if (nedges == agnedges(g))
00436             return AllEdges;
00437         else
00438             return SomeEdges;
00439     } else
00440         return NoEdges;
00441 }
00442 
00443 /* chkBB:
00444  * Scans for a correct bb attribute. If available, sets it
00445  * in the graph and returns 1.
00446  */
00447 #define BS "%d,%d,%d,%d"
00448 
00449 static int chkBB(Agraph_t * g, attrsym_t * G_bb)
00450 {
00451     char *s;
00452     box bb;
00453 
00454     s = agxget(g, G_bb->index);
00455     if (sscanf(s, BS, &bb.LL.x, &bb.LL.y, &bb.UR.x, &bb.UR.y) == 4) {
00456         if (bb.LL.y > bb.UR.y) {
00457         /* If the LL.y coordinate is bigger than the UR.y coordinate,
00458          * we assume the input was produced using -y, so we normalize
00459          * the bb. 
00460          */
00461             int tmp = bb.LL.y;
00462             bb.LL.y = bb.UR.y;
00463             bb.UR.y = tmp;
00464         }
00465         GD_bb(g) = bb;
00466         return 1;
00467     } else
00468         return 0;
00469 }
00470 
00471 static void add_cluster(Agraph_t * g, Agraph_t * subg)
00472 {
00473     int cno;
00474     cno = ++(GD_n_cluster(g));
00475     GD_clust(g) = ZALLOC(cno + 1, GD_clust(g), graph_t *, GD_n_cluster(g));
00476     GD_clust(g)[cno] = subg;
00477     do_graph_label(subg);
00478 }
00479 
00480 
00481 static void nop_init_graphs(Agraph_t *, attrsym_t *, attrsym_t *);
00482 
00483 /* dfs:
00484  */
00485 static void
00486 dfs(node_t * mn, Agraph_t * g, attrsym_t * G_lp, attrsym_t * G_bb)
00487 {
00488     graph_t *subg;
00489 
00490     subg = agusergraph(mn);
00491     if (!strncmp(subg->name, "cluster", 7) && chkBB(subg, G_bb)) {
00492         add_cluster(g, subg);
00493         nop_init_graphs(subg, G_lp, G_bb);
00494     } else {
00495         graph_t *mg = g->meta_node->graph;
00496         edge_t *me;
00497         for (me = agfstout(mg, mn); me; me = agnxtout(mg, me)) {
00498             dfs(me->head, g, G_lp, G_bb);
00499         }
00500     }
00501 }
00502 
00503 /* nop_init_graphs:
00504  * Read in clusters and graph label info.
00505  * A subgraph is a cluster if its name starts with "cluster" and
00506  * it has a valid bb.
00507  */
00508 static void
00509 nop_init_graphs(Agraph_t * g, attrsym_t * G_lp, attrsym_t * G_bb)
00510 {
00511     graph_t *mg;
00512     edge_t *me;
00513     char *s;
00514     point p;
00515 
00516     if (GD_label(g) && G_lp) {
00517         s = agxget(g, G_lp->index);
00518         if (sscanf(s, "%d,%d", &p.x, &p.y) == 2) {
00519             GD_label(g)->set = TRUE;
00520             GD_label(g)->p = p;
00521         }
00522     }
00523 
00524     if (!G_bb)
00525         return;
00526     mg = g->meta_node->graph;
00527     for (me = agfstout(mg, g->meta_node); me; me = agnxtout(mg, me)) {
00528         dfs(me->head, g, G_lp, G_bb);
00529     }
00530 }
00531 
00532 /* translateE:
00533  * Translate edge by offset.
00534  * Assume ED_spl(e) != NULL
00535  */
00536 static void translateE(edge_t * e, point offset)
00537 {
00538     int i, j;
00539     point *pt;
00540     bezier *bez;
00541 
00542     bez = ED_spl(e)->list;
00543     for (i = 0; i < ED_spl(e)->size; i++) {
00544         pt = bez->list;
00545         for (j = 0; j < bez->size; j++) {
00546             pt->x -= offset.x;
00547             pt->y -= offset.y;
00548             pt++;
00549         }
00550         if (bez->sflag) {
00551             bez->sp.x -= offset.x;
00552             bez->sp.y -= offset.y;
00553         }
00554         if (bez->eflag) {
00555             bez->ep.x -= offset.x;
00556             bez->ep.y -= offset.y;
00557         }
00558         bez++;
00559     }
00560 
00561     if (ED_label(e) && ED_label(e)->set) {
00562         ED_label(e)->p.x -= offset.x;
00563         ED_label(e)->p.y -= offset.y;
00564     }
00565     if (ED_head_label(e) && ED_head_label(e)->set) {
00566         ED_head_label(e)->p.x -= offset.x;
00567         ED_head_label(e)->p.y -= offset.y;
00568     }
00569     if (ED_tail_label(e) && ED_tail_label(e)->set) {
00570         ED_tail_label(e)->p.x -= offset.x;
00571         ED_tail_label(e)->p.y -= offset.y;
00572     }
00573 }
00574 
00575 /* translateG:
00576  */
00577 static void translateG(Agraph_t * g, point offset)
00578 {
00579     int i;
00580 
00581     GD_bb(g).UR.x -= offset.x;
00582     GD_bb(g).UR.y -= offset.y;
00583     GD_bb(g).LL.x -= offset.x;
00584     GD_bb(g).LL.y -= offset.y;
00585 
00586     if (GD_label(g) && GD_label(g)->set) {
00587         GD_label(g)->p.x -= offset.x;
00588         GD_label(g)->p.y -= offset.y;
00589     }
00590 
00591     for (i = 1; i <= GD_n_cluster(g); i++)
00592         translateG(GD_clust(g)[i], offset);
00593 }
00594 
00595 /* translate:
00596  */
00597 static void translate(Agraph_t * g, pos_edge posEdges)
00598 {
00599     node_t *n;
00600     edge_t *e;
00601     pointf offset;
00602 
00603     offset = cvt2ptf(GD_bb(g).LL);
00604     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00605         ND_pos(n)[0] -= offset.x;
00606         ND_pos(n)[1] -= offset.y;
00607     }
00608     if (posEdges != NoEdges) {
00609         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00610             for (e = agfstout(g, n); e; e = agnxtout(g, e))
00611                 if (ED_spl(e))
00612                     translateE(e, GD_bb(g).LL);
00613         }
00614     }
00615     translateG(g, GD_bb(g).LL);
00616 }
00617 
00618 /* init_nop:
00619  * This assumes all nodes have been positioned.
00620  * It also assumes none of the * relevant fields in A*info_t have been set.
00621  * The input may provide additional position information for
00622  * clusters, edges and labels. If certain position information
00623  * is missing, init_nop will use a standard neato technique to
00624  * supply it.
00625  */
00626 int init_nop(Agraph_t * g, int adjust)
00627 {
00628     int i;
00629     node_t *np;
00630     pos_edge posEdges;          /* How many edges have spline info */
00631     attrsym_t *G_lp = agfindattr(g, "lp");
00632     attrsym_t *G_bb = agfindattr(g, "bb");
00633 
00634     /* If G_bb not defined, define it */
00635     if (!G_bb)
00636         G_bb = agraphattr(g, "bb", "");
00637 
00638     scan_graph(g);              /* mainly to set up GD_neato_nlist */
00639     for (i = 0; (np = GD_neato_nlist(g)[i]); i++) {
00640         if (!hasPos(np) && strncmp(np->name, "cluster", 7)) {
00641             agerr(AGERR, "node %s in graph %s has no position\n",
00642                   np->name, g->name);
00643             return 1;
00644         }
00645     }
00646     nop_init_graphs(g, G_lp, G_bb);
00647     posEdges = nop_init_edges(g);
00648 
00649     if (adjust && Nop == 1)
00650         adjustNodes(g);
00651 
00652     /* If g does not have a good "bb" attribute, compute it. */
00653     if (!chkBB(g, G_bb))
00654         compute_bb(g);
00655 
00656     /* At this point, all bounding boxes should be correctly defined.
00657      * If necessary, we translate the graph to the origin.
00658      */
00659     if (adjust && (GD_bb(g).LL.x || GD_bb(g).LL.y))
00660         translate(g, posEdges);
00661 
00662     if (!adjust) {
00663         node_t *n;
00664         State = GVSPLINES;
00665         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00666             ND_coord_i(n).x = POINTS(ND_pos(n)[0]);
00667             ND_coord_i(n).y = POINTS(ND_pos(n)[1]);
00668         }
00669     }
00670     else if (posEdges != AllEdges)
00671         spline_edges0(g);
00672     else {
00673         State = GVSPLINES;
00674         neato_set_aspect(g);
00675     }
00676     return 0;
00677 }
00678 
00679 void neato_init_graphn(Agraph_t * g, int dfltdim)
00680 {
00681     setEdgeType (g, ET_LINE);
00682     GD_ndim(g->root) = late_int(g, agfindattr(g, "dim"), dfltdim, 2);
00683     Ndim = GD_ndim(g->root) = MIN(GD_ndim(g->root), MAXDIM);
00684     neato_init_node_edge(g);
00685 }
00686 
00687 void neato_init_graph(Agraph_t * g)
00688 {
00689     neato_init_graphn(g, 2);
00690 }
00691 
00692 static int neatoModel(graph_t * g)
00693 {
00694     char *p = agget(g, "model");
00695     char c;
00696 
00697     if (!p || (!(c = *p)))
00698         return MODEL_SHORTPATH;
00699     if ((c == 'c') && streq(p, "circuit"))
00700         return MODEL_CIRCUIT;
00701     if (c == 's') {
00702         if (streq(p, "subset"))
00703             return MODEL_SUBSET;
00704         else if (streq(p, "shortpath"))
00705             return MODEL_SHORTPATH;
00706     }
00707     agerr(AGWARN,
00708           "Unknown value %s for attribute \"model\" in graph %s - ignored\n",
00709           p, g->name);
00710     return MODEL_SHORTPATH;
00711 }
00712 
00713 /* neatoMode:
00714  */
00715 static int neatoMode(graph_t * g)
00716 {
00717     char *str;
00718     int mode = MODE_MAJOR;      /* default mode */
00719 
00720     str = agget(g, "mode");
00721     if (str && *str) {
00722         if (streq(str, "KK"))
00723             mode = MODE_KK;
00724         else if (streq(str, "major"))
00725             mode = MODE_MAJOR;
00726 #ifdef DIGCOLA
00727         else if (streq(str, "hier"))
00728             mode = MODE_HIER;
00729 #ifdef IPSEPCOLA
00730         else if (streq(str, "ipsep"))
00731             mode = MODE_IPSEP;
00732 #endif
00733 #endif
00734         else
00735             agerr(AGWARN,
00736                   "Illegal value %s for attribute \"mode\" in graph %s - ignored\n",
00737                   str, g->name);
00738     }
00739 
00740     return mode;
00741 }
00742 
00743 /* checkEdge:
00744  *
00745  */
00746 static int checkEdge(PointMap * pm, edge_t * ep, int idx)
00747 {
00748     int i = ND_id(ep->tail);
00749     int j = ND_id(ep->head);
00750     int tmp;
00751 
00752     if (i > j) {
00753         tmp = i;
00754         i = j;
00755         j = tmp;
00756     }
00757     return insertPM(pm, i, j, idx);
00758 }
00759 
00760 #ifdef DIGCOLA
00761 /* dfsCycle:
00762  * dfs for breaking cycles in vtxdata
00763  */
00764 static void
00765 dfsCycle (vtx_data* graph, int i,int mode)
00766 {
00767     node_t *np, *hp;
00768     int j, e, f;
00769     /* if mode is IPSEP make it an in-edge
00770      * at both ends, so that an edge constraint won't be generated!
00771      */
00772     double x = (mode==MODE_IPSEP?-1.0:1.0);
00773 
00774     np = graph[i].np;
00775     ND_mark(np) = TRUE;
00776     ND_onstack(np) = TRUE;
00777     for (e = 1; e < graph[i].nedges; e++) {
00778         if (graph[i].edists[e] == 1.0) continue;  /* in edge */
00779         j = graph[i].edges[e];
00780         hp = graph[j].np;
00781         if (ND_onstack(hp)) {  /* back edge: reverse it */
00782             graph[i].edists[e] = x;
00783             for (f = 1; (f < graph[j].nedges) &&(graph[j].edges[f] != i); f++) ;
00784             assert (f < graph[j].nedges);
00785             graph[j].edists[f] = -1.0;
00786         }
00787         else if (ND_mark(hp) == FALSE) dfsCycle(graph, j, mode);
00788 
00789     }
00790     ND_onstack(np) = FALSE;
00791 }
00792 
00793 /* acyclic:
00794  * Do a dfs of the vtx_data, looking for cycles, reversing edges.
00795  */
00796 static void
00797 acyclic (vtx_data* graph, int nv, int mode)
00798 {
00799     int i;
00800     node_t* np;
00801 
00802     for (i = 0; i < nv; i++) {
00803         np = graph[i].np;
00804         ND_mark(np) = FALSE;
00805         ND_onstack(np) = FALSE;
00806     }
00807     for (i = 0; i < nv; i++) {
00808         if (ND_mark(graph[i].np)) continue;
00809         dfsCycle (graph, i, mode);      
00810     }
00811 
00812 }
00813 #endif
00814 
00815 /* makeGraphData:
00816  * Create sparse graph representation via arrays.
00817  * Each node is represented by a vtx_data.
00818  * The index of each neighbor is stored in the edges array;
00819  * the corresponding edge lengths and weights go on ewgts and eweights.
00820  * We do not allocate the latter 2 if the graph does not use them.
00821  * By convention, graph[i].edges[0] == i.
00822  * The values graph[i].ewgts[0] and graph[i].eweights[0] are left undefined.
00823  *
00824  * In constructing graph from g, we neglect loops. We track multiedges (ignoring
00825  * direction). Edge weights are additive; the final edge length is the max.
00826  *
00827  * If direction is used, we set the edists field, -1 for tail, +1 for head. 
00828  * graph[i].edists[0] is left undefined. If multiedges exist, the direction
00829  * of the first one encountered is used. Finally, a pass is made to guarantee
00830  * the graph is acyclic.
00831  *
00832  */
00833 static vtx_data *makeGraphData(graph_t * g, int nv, int *nedges, int mode, int model)
00834 {
00835     vtx_data *graph;
00836     int ne = agnedges(g);       /* upper bound */
00837     int *edges;
00838     float *ewgts = NULL;
00839     node_t *np;
00840     edge_t *ep;
00841     float *eweights = NULL;
00842 #ifdef DIGCOLA
00843     float *edists = NULL;
00844 #endif
00845     int haveLen;
00846     int haveWt;
00847     int haveDir;
00848     PointMap *ps = newPM();
00849     int i, i_nedges, idx;
00850 
00851     /* lengths and weights unused in reweight model */
00852     if (model == MODEL_SUBSET) {
00853         haveLen = FALSE;
00854         haveWt = FALSE;
00855     } else {
00856         haveLen = (agindex(g->root->proto->e, "len") >= 0);
00857         haveWt = (E_weight != 0);
00858     }
00859     if (mode == MODE_HIER || mode == MODE_IPSEP)
00860         haveDir = TRUE;
00861     else
00862         haveDir = FALSE;
00863 
00864     graph = N_GNEW(nv, vtx_data);
00865     edges = N_GNEW(2 * ne + nv, int);   /* reserve space for self loops */
00866     if (haveLen || haveDir)
00867         ewgts = N_GNEW(2 * ne + nv, float);
00868     if (haveWt)
00869         eweights = N_GNEW(2 * ne + nv, float);
00870 #ifdef DIGCOLA
00871     if (haveDir)
00872         edists = N_GNEW(2*ne+nv,float);
00873 #endif
00874 
00875     i = 0;
00876     ne = 0;
00877     for (np = agfstnode(g); np; np = agnxtnode(g, np)) {
00878         int j = 1;              /* index of neighbors */
00879         clearPM(ps);
00880         assert(ND_id(np) == i);
00881         graph[i].np = np;
00882         graph[i].edges = edges++;       /* reserve space for the self loop */
00883         if (haveLen || haveDir)
00884             graph[i].ewgts = ewgts++;
00885         else
00886             graph[i].ewgts = NULL;
00887         if (haveWt)
00888             graph[i].eweights = eweights++;
00889         else
00890             graph[i].eweights = NULL;
00891 #ifdef DIGCOLA
00892         if (haveDir) {
00893             graph[i].edists = edists++;
00894         }
00895         else
00896             graph[i].edists = NULL;
00897 #endif
00898         i_nedges = 1;           /* one for the self */
00899 
00900         for (ep = agfstedge(g, np); ep; ep = agnxtedge(g, ep, np)) {
00901             if (ep->head == ep->tail)
00902                 continue;       /* ignore loops */
00903             idx = checkEdge(ps, ep, j);
00904             if (idx != j) {     /* seen before */
00905                 if (haveWt)
00906                     graph[i].eweights[idx] += ED_factor(ep);
00907                 if (haveLen) {
00908                     int curlen = graph[i].ewgts[idx];
00909                     graph[i].ewgts[idx] = MAX(ED_dist(ep), curlen);
00910                 }
00911             } else {
00912                 node_t *vp = (((ep->tail) == np) ? ep->head : ep->tail);
00913                 ne++;
00914                 j++;
00915 
00916                 *edges++ = ND_id(vp);
00917                 if (haveWt)
00918                     *eweights++ = ED_factor(ep);
00919                 if (haveLen)
00920                     *ewgts++ = ED_dist(ep);
00921                 else if (haveDir)
00922                     *ewgts++ = 1.0;
00923 #ifdef DIGCOLA
00924                 if (haveDir) {
00925                     char *s = agget(ep,"dir");
00926                     if(s&&!strncmp(s,"none",4)) {
00927                         *edists++ = 0;
00928                     } else {
00929                         *edists++ = (np == ep->head ? 1.0 : -1.0);
00930                     }
00931                 }
00932 #endif
00933                 i_nedges++;
00934             }
00935         }
00936 
00937         graph[i].nedges = i_nedges;
00938         graph[i].edges[0] = i;
00939 #ifdef USE_STYLES
00940         graph[i].styles = NULL;
00941 #endif
00942         i++;
00943     }
00944 #ifdef DIGCOLA
00945     if (haveDir) {
00946     /* Make graph acyclic */
00947         acyclic (graph, nv, mode);
00948     }
00949 #endif
00950 
00951     ne /= 2;                    /* every edge is counted twice */
00952 
00953     /* If necessary, release extra memory. */
00954     if (ne != agnedges(g)) {
00955         edges = RALLOC(2 * ne + nv, graph[0].edges, int);
00956         if (haveLen)
00957             ewgts = RALLOC(2 * ne + nv, graph[0].ewgts, float);
00958         if (haveWt)
00959             eweights = RALLOC(2 * ne + nv, graph[0].eweights, float);
00960 
00961         for (i = 0; i < nv; i++) {
00962             int sz = graph[i].nedges;
00963             graph[i].edges = edges;
00964             edges += sz;
00965             if (haveLen) {
00966                 graph[i].ewgts = ewgts;
00967                 ewgts += sz;
00968             }
00969             if (haveWt) {
00970                 graph[i].eweights = eweights;
00971                 eweights += sz;
00972             }
00973         }
00974     }
00975 
00976     *nedges = ne;
00977     freePM(ps);
00978     return graph;
00979 }
00980 
00981 static void freeGraphData(vtx_data * graph)
00982 {
00983     if (graph != NULL) {
00984         if (graph[0].edges != NULL)
00985             free(graph[0].edges);
00986         if (graph[0].ewgts != NULL)
00987             free(graph[0].ewgts);
00988 #ifdef UNIMPLEMENTED
00989         if (graph[0].elens != NULL)
00990             free(graph[0].elens);
00991 #endif
00992 #ifdef USE_STYLES
00993         if (graph[0].styles != NULL)
00994             free(graph[0].styles);
00995 #endif
00996         free(graph);
00997     }
00998 }
00999 
01000 static void initRegular(graph_t * G, int nG)
01001 {
01002     double a, da;
01003     node_t *np;
01004 
01005     a = 0.0;
01006     da = (2 * M_PI) / nG;
01007     for (np = agfstnode(G); np; np = agnxtnode(G, np)) {
01008         ND_pos(np)[0] = nG * Spring_coeff * cos(a);
01009         ND_pos(np)[1] = nG * Spring_coeff * sin(a);
01010         ND_pinned(np) = P_SET;
01011         a = a + da;
01012         if (Ndim > 2)
01013             jitter3d(np, nG);
01014     }
01015 }
01016 
01017 #define SLEN(s) (sizeof(s)-1)
01018 #define SMART   "self"
01019 #define REGULAR "regular"
01020 #define RANDOM  "random"
01021 
01022 /* setSeed:
01023  * Analyze "start" attribute. If unset, return dflt.
01024  * If it begins with self, regular, or random, return set init to same,
01025  * else set init to dflt.
01026  * If init is random, look for value integer suffix to use a seed; if not
01027  * found, use time to set seed and store seed in graph.
01028  * Return seed in seedp.
01029  * Return init.
01030  */
01031 int
01032 setSeed (graph_t * G, int dflt, long* seedp)
01033 {
01034     char smallbuf[32];
01035     char *p = agget(G, "start");
01036     int init = dflt;
01037 
01038     if (!p || (*p == '\0')) return dflt;
01039     if (isalpha(*(unsigned char *)p)) {
01040         if (!strncmp(p, SMART, SLEN(SMART))) {
01041             init = INIT_SELF;
01042             p += SLEN(SMART);
01043         } else if (!strncmp(p, REGULAR, SLEN(REGULAR))) {
01044             init = INIT_REGULAR;
01045             p += SLEN(REGULAR);
01046         } else if (!strncmp(p, RANDOM, SLEN(RANDOM))) {
01047             init = INIT_RANDOM;
01048             p += SLEN(RANDOM);
01049         }
01050         else init = dflt;
01051     }
01052     else if (isdigit(*(unsigned char *)p)) {
01053         init = INIT_RANDOM;
01054     }
01055     
01056     if (init == INIT_RANDOM) {
01057         long seed;
01058         /* Check for seed value */
01059         if (!isdigit(*(unsigned char *)p) || sscanf(p, "%ld", &seed) < 1) {
01060 #ifdef MSWIN32
01061             seed = (unsigned) time(NULL);
01062 #else
01063             seed = (unsigned) getpid() ^ (unsigned) time(NULL);
01064 #endif
01065             sprintf(smallbuf, "%ld", seed);
01066             agset(G, "start", smallbuf);
01067         }
01068         *seedp = seed;
01069     }
01070     return init;
01071 }
01072 
01073 /* checkStart:
01074  * Analyzes start attribute, setting seed.
01075  * If set,
01076  *   If start is regular, places nodes and returns INIT_REGULAR.
01077  *   If start is self, returns INIT_SELF.
01078  *   If start is random, returns INIT_RANDOM
01079  *   Set RNG seed
01080  * else return default
01081  * 
01082  */
01083 int checkStart(graph_t * G, int nG, int dflt)
01084 {
01085     long seed;
01086     int init;
01087 
01088     seed = 1;
01089     init = setSeed (G, dflt, &seed); 
01090     if (N_pos && (init != INIT_RANDOM)) {
01091         agerr(AGWARN, "node positions are ignored unless start=random\n");
01092     }
01093     if (init == INIT_REGULAR) initRegular(G, nG);
01094     srand48(seed);
01095     return init;
01096 }
01097 
01098 #ifdef UNUSED
01099 void dumpData(graph_t * g, vtx_data * gp, int nv, int ne)
01100 {
01101     node_t *v;
01102     int i, j, n;
01103 
01104     fprintf(stderr, "n %d e %d\n", nv, ne);
01105     for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
01106         fprintf(stderr, "\"%s\" %d\n", v->name, ND_id(v));
01107     }
01108     for (i = 0; i < nv; i++) {
01109         n = gp[i].nedges;
01110         fprintf(stderr, "[%d] %d\n", i, n);
01111         for (j = 0; j < n; j++) {
01112             fprintf(stderr, "  %3d", gp[i].edges[j]);
01113         }
01114         fputs("\n", stderr);
01115         if (gp[i].ewgts)
01116             for (j = 0; j < n; j++) {
01117                 fprintf(stderr, "  %3f", gp[i].ewgts[j]);
01118             }
01119         fputs("\n", stderr);
01120 
01121     }
01122 }
01123 #endif
01124 
01125 /* majorization:
01126  * Solve stress using majorization.
01127  * Old neato attributes to incorporate:
01128  *  weight?
01129  * model will be MODE_MAJOR, MODE_HIER or MODE_IPSEP
01130  */
01131 static void
01132 majorization(graph_t *mg, graph_t * g, int nv, int mode, int model, int dim, int steps)
01133 {
01134     double **coords;
01135     int ne;
01136     int i;
01137     node_t *v;
01138     vtx_data *gp;
01139 
01140     int init;
01141 
01142     init = checkStart(g, nv, (mode == MODE_HIER ? INIT_SELF : INIT_RANDOM));
01143     coords = N_GNEW(dim, double *);
01144     coords[0] = N_GNEW(nv * dim, double);
01145     for (i = 1; i < Ndim; i++) {
01146         coords[i] = coords[0] + i * nv;
01147     }
01148     if (Verbose) {
01149         fprintf(stderr, "model %d smart_init %d iterations %d tol %f\n",
01150                 model, (init == INIT_SELF), MaxIter, Epsilon);
01151         fprintf(stderr, "convert graph: ");
01152         start_timer();
01153     }
01154     gp = makeGraphData(g, nv, &ne, mode, model);
01155 
01156     if (Verbose) {
01157         fprintf(stderr, "%d nodes %.2f sec\n", nv, elapsed_sec());
01158     }
01159 
01160 #ifdef DIGCOLA
01161     if (mode != MODE_MAJOR) {
01162         double lgap = late_double(g, agfindattr(g, "levelsgap"), 0.0, -MAXDOUBLE);
01163         if (mode == MODE_HIER) {        
01164             stress_majorization_with_hierarchy(gp, nv, ne, coords, Ndim,
01165                        (init == INIT_SELF), model, MaxIter, lgap);
01166         } 
01167 #ifdef IPSEPCOLA
01168         else {
01169             char* str;
01170             adjust_data* am;
01171             ipsep_options opt;
01172             pointf nsize[nv];
01173             cluster_data *cs = (cluster_data*)cluster_map(mg,g);
01174             opt.edge_gap = lgap;
01175 #ifdef MOSEK
01176             opt.mosek = 0;
01177 #endif /* MOSEK */
01178             opt.nsize = nsize;
01179             opt.clusters = cs;
01180             str = agget(g, "diredgeconstraints");
01181             if (mapbool(str)) {
01182                 opt.diredges = 1;
01183                 if(Verbose)
01184                     fprintf(stderr,"Generating Edge Constraints...\n");
01185             } else if (str && !strncasecmp(str,"hier",4)) {
01186                 opt.diredges = 2;
01187                 if(Verbose)
01188                     fprintf(stderr,"Generating DiG-CoLa Edge Constraints...\n");
01189             }
01190             else opt.diredges = 0;
01191             am = graphAdjustMode (g);
01192             if (am->mode == AM_IPSEP) {
01193                 opt.noverlap = 1;
01194                 if(Verbose)
01195                     fprintf(stderr,"Generating Non-overlap Constraints...\n");
01196             } else if (am->mode == AM_VPSC) {
01197                 opt.noverlap = 2;
01198                 if(Verbose)
01199                     fprintf(stderr,"Removing overlaps as postprocess...\n");
01200             }  
01201             else opt.noverlap = 0;
01202 #ifdef MOSEK
01203             str = agget(g, "mosek");
01204             if(str && !strncmp(str,"true",4)) {
01205                 opt.mosek = 1;
01206                 if(Verbose)
01207                     fprintf(stderr,"Using Mosek for constraint optimization...\n");
01208             }
01209 #endif /* MOSEK */
01210             if ((str = agget(g, "sep")) && 
01211                 (i = sscanf(str, "%lf,%lf", &opt.gap.x, &opt.gap.y))) {
01212                     if (i == 1) opt.gap.y = opt.gap.x;
01213                     if(Verbose)
01214                         fprintf(stderr,"gap=%f,%f\n",opt.gap.x,opt.gap.y);
01215             }
01216             else opt.gap.x = opt.gap.y = 0;
01217             for (i=0, v = agfstnode(g); v; v = agnxtnode(g, v),i++) {
01218                 nsize[i].x = ND_width(v);
01219                 nsize[i].y = ND_height(v);
01220             }
01221 
01222             stress_majorization_cola(gp, nv, ne, coords, Ndim, model, MaxIter, &opt);
01223             freeClusterData(cs);
01224         }
01225 #endif
01226     }
01227     else
01228 #endif
01229         stress_majorization_kD_mkernel(gp, nv, ne, coords, Ndim,
01230                                    (init == INIT_SELF), model, MaxIter);
01231 
01232     /* store positions back in nodes */
01233     for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
01234         int idx = ND_id(v);
01235         int i;
01236         for (i = 0; i < Ndim; i++) {
01237             ND_pos(v)[i] = coords[i][idx];
01238         }
01239     }
01240     freeGraphData(gp);
01241     free(coords[0]);
01242     free(coords);
01243 }
01244 
01245 static void subset_model(Agraph_t * G, int nG)
01246 {
01247     int i, j, ne;
01248     DistType **Dij;
01249     vtx_data *gp;
01250 
01251     gp = makeGraphData(G, nG, &ne, MODE_KK, MODEL_SUBSET);
01252     Dij = compute_apsp_artifical_weights(gp, nG);
01253     for (i = 0; i < nG; i++) {
01254         for (j = 0; j < nG; j++) {
01255             GD_dist(G)[i][j] = Dij[i][j];
01256         }
01257     }
01258     free(Dij[0]);
01259     free(Dij);
01260     freeGraphData(gp);
01261 }
01262 
01263 /* kkNeato:
01264  * Solve using gradient descent a la Kamada-Kawai.
01265  */
01266 static void kkNeato(Agraph_t * g, int nG, int model)
01267 {
01268     if (model == MODEL_SUBSET) {
01269         subset_model(g, nG);
01270     } else if (model == MODEL_CIRCUIT) {
01271         if (!circuit_model(g, nG)) {
01272             agerr(AGWARN,
01273                   "graph %s is disconnected. Hence, the circuit model\n",
01274                   g->name);
01275             agerr(AGPREV,
01276                   "is undefined. Reverting to the shortest path model.\n");
01277             agerr(AGPREV,
01278                   "Alternatively, consider running neato using -Gpack=true or decomposing\n");
01279             agerr(AGPREV, "the graph into connected components.\n");
01280             shortest_path(g, nG);
01281         }
01282     } else
01283         shortest_path(g, nG);
01284     initial_positions(g, nG);
01285     diffeq_model(g, nG);
01286     if (Verbose) {
01287         fprintf(stderr, "Solving model %d iterations %d tol %f\n",
01288                 model, MaxIter, Epsilon);
01289         start_timer();
01290     }
01291     solve_model(g, nG);
01292 }
01293 
01294 /* neatoLayout:
01295  * Use stress optimization to layout a single component
01296  */
01297 void neatoLayout(Agraph_t * mg, Agraph_t * g, int layoutMode, int layoutModel)
01298 {
01299     int nG;
01300     char *str;
01301 
01302     if ((str = agget(g, "maxiter")))
01303         MaxIter = atoi(str);
01304     else if (layoutMode == MODE_MAJOR)
01305         MaxIter = DFLT_ITERATIONS;
01306     else
01307         MaxIter = 100 * agnnodes(g);
01308 
01309     nG = scan_graph_mode(g, layoutMode);
01310     if ((nG < 2) || (MaxIter <=0))
01311         return;
01312     if (layoutMode)
01313         majorization(mg, g, nG, layoutMode, layoutModel, Ndim, MaxIter);
01314     else
01315         kkNeato(g, nG, layoutModel);
01316 }
01317 
01318 /* addZ;
01319  * If dimension == 3 and z attribute is declared, 
01320  * attach z value to nodes if not defined.
01321  */
01322 static void
01323 addZ (Agraph_t* g)
01324 {
01325     node_t* n;
01326     char    buf[BUFSIZ];
01327 
01328     if ((Ndim >= 3) && N_z) { 
01329         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
01330             sprintf(buf, "%d", POINTS(ND_pos(n)[2]));
01331             agxset(n, N_z->index, buf);
01332         }
01333     }
01334 }
01335 
01336 /* neato_layout:
01337  */
01338 void neato_layout(Agraph_t * g)
01339 {
01340     int layoutMode;
01341     int model;
01342     pack_mode mode;
01343 
01344     if (Nop) {
01345         int save = PSinputscale;
01346         int ret;
01347         PSinputscale = POINTS_PER_INCH;
01348         neato_init_graph(g);
01349         addZ (g);
01350         ret = init_nop(g, 1);
01351         PSinputscale = save;
01352         if (ret) {
01353             agerr(AGPREV, "as required by the -n flag\n");
01354             exit(1);
01355         }
01356     } else {
01357         neato_init_graph(g);
01358         layoutMode = neatoMode(g);
01359         model = neatoModel(g);
01360         mode = getPackMode(g, l_undef);
01361         Pack = getPack(g, -1, CL_OFFSET);
01362         /* pack if just packmode defined. */
01363         if (mode == l_undef) {
01364             /* If the user has not indicated packing but we are
01365              * using the new neato, turn packing on.
01366              */
01367             if ((Pack < 0) && layoutMode)
01368                 Pack = CL_OFFSET;
01369             mode = l_node;
01370         } else if (Pack < 0)
01371             Pack = CL_OFFSET;
01372         if (Pack >= 0) {
01373             graph_t *gc;
01374             graph_t **cc;
01375             int n_cc;
01376             int i;
01377             pack_info pinfo;
01378             boolean pin;
01379 
01380             cc = pccomps(g, &n_cc, cc_pfx, &pin);
01381 
01382             for (i = 0; i < n_cc; i++) {
01383                 gc = cc[i];
01384                 nodeInduce(gc);
01385                 neatoLayout(g, gc, layoutMode, model);
01386                 adjustNodes(gc);
01387             }
01388             if (n_cc > 1) {
01389                 boolean *bp;
01390                 if (pin) {
01391                     bp = N_NEW(n_cc, boolean);
01392                     bp[0] = TRUE;
01393                 } else
01394                     bp = 0;
01395                 pinfo.margin = Pack;
01396                 pinfo.doSplines = 0;
01397                 pinfo.mode = mode;
01398                 pinfo.fixed = bp;
01399                 packGraphs(n_cc, cc, 0, &pinfo);
01400                 if (bp)
01401                     free(bp);
01402             }
01403             compute_bb(g);
01404             addZ (g);
01405             spline_edges(g);
01406 
01407             /* cleanup and remove component subgraphs */
01408             for (i = 0; i < n_cc; i++) {
01409                 gc = cc[i];
01410                 free_scan_graph(gc);
01411                 agdelete(g, gc);
01412             }
01413             free (cc);
01414 #ifdef IPSEPCOLA
01415             {
01416                 graph_t *mg, *subg;
01417                 node_t *mm, *mn;
01418                 edge_t *me;
01419                 mm = g->meta_node;
01420                 mg = mm->graph;
01421                 for (me = agfstout(mg, mm); me; me = agnxtout(mg, me)) {
01422                     mn = me->head;
01423                     subg = agusergraph(mn);
01424                     if (!strncmp(subg->name, "cluster", 7)) {
01425                         add_cluster(g,subg);
01426                         compute_bb(subg);
01427                     }
01428                 }
01429             }
01430 #endif
01431         } else {
01432             neatoLayout(g, g, layoutMode, model);
01433             adjustNodes(g);
01434             addZ (g);
01435             spline_edges(g);
01436         }
01437     }
01438     dotneato_postprocess(g);
01439 }

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