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

Go to the documentation of this file.
00001 /* $Id: adjust.c,v 1.14 2008/02/05 04:37:16 erg Exp $ $Revision: 1.14 $ */
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 /* adjust.c
00018  * Routines for repositioning nodes after initial layout in
00019  * order to reduce/remove node overlaps.
00020  */
00021 
00022 #include "neato.h"
00023 #include "agxbuf.h"
00024 #include "utils.h"
00025 #include "voronoi.h"
00026 #include "info.h"
00027 #include "edges.h"
00028 #include "site.h"
00029 #include "heap.h"
00030 #include "hedges.h"
00031 #include "digcola.h"
00032 #ifdef IPSEPCOLA
00033 #include <csolve_VPSC.h>
00034 #include "quad_prog_vpsc.h"
00035 #endif
00036 
00037 static double margin = 0.05;    /* Create initial bounding box by adding
00038                                  * margin * dimension around box enclosing
00039                                  * nodes.
00040                                  */
00041 static double incr = 0.05;      /* Increase bounding box by adding
00042                                  * incr * dimension around box.
00043                                  */
00044 static double pmargin = 5.0 / POINTS_PER_INCH;  /* Margin around polygons, in inches */
00045 static int iterations = -1;     /* Number of iterations */
00046 static int useIter = 0;         /* Use specified number of iterations */
00047 
00048 static int doAll = 0;           /* Move all nodes, regardless of overlap */
00049 static Site **sites;            /* Array of pointers to sites; used in qsort */
00050 static Site **endSite;          /* Sentinel on sites array */
00051 static Point nw, ne, sw, se;    /* Corners of clipping window */
00052 
00053 static Site **nextSite;
00054 
00055 static void setBoundBox(Point * ll, Point * ur)
00056 {
00057     pxmin = ll->x;
00058     pxmax = ur->x;
00059     pymin = ll->y;
00060     pymax = ur->y;
00061     nw.x = sw.x = pxmin;
00062     ne.x = se.x = pxmax;
00063     nw.y = ne.y = pymax;
00064     sw.y = se.y = pymin;
00065 }
00066 
00067  /* freeNodes:
00068   * Free node resources.
00069   */
00070 static void freeNodes(void)
00071 {
00072     int i;
00073     Info_t *ip = nodeInfo;
00074 
00075     for (i = 0; i < nsites; i++) {
00076         breakPoly(&ip->poly);
00077         ip++;
00078     }
00079     polyFree();
00080     infoinit();                 /* Free vertices */
00081     free(nodeInfo);
00082 }
00083 
00084 /* chkBoundBox:
00085  *   Compute extremes of graph, then set up bounding box.
00086  *   If user supplied a bounding box, use that;
00087  *   else if "window" is a graph attribute, use that; 
00088  *   otherwise, define bounding box as a percentage expansion of
00089  *   graph extremes.
00090  *   In the first two cases, check that graph fits in bounding box.
00091  */
00092 static void chkBoundBox(Agraph_t * graph)
00093 {
00094     char *marg;
00095     Point ll, ur;
00096     int i;
00097     double x, y;
00098     double xmin, xmax, ymin, ymax;
00099     double xmn, xmx, ymn, ymx;
00100     double ydelta, xdelta;
00101     Info_t *ip;
00102     Poly *pp;
00103     /* int          cnt; */
00104 
00105     ip = nodeInfo;
00106     pp = &ip->poly;
00107     x = ip->site.coord.x;
00108     y = ip->site.coord.y;
00109     xmin = pp->origin.x + x;
00110     ymin = pp->origin.y + y;
00111     xmax = pp->corner.x + x;
00112     ymax = pp->corner.y + y;
00113     for (i = 1; i < nsites; i++) {
00114         ip++;
00115         pp = &ip->poly;
00116         x = ip->site.coord.x;
00117         y = ip->site.coord.y;
00118         xmn = pp->origin.x + x;
00119         ymn = pp->origin.y + y;
00120         xmx = pp->corner.x + x;
00121         ymx = pp->corner.y + y;
00122         if (xmn < xmin)
00123             xmin = xmn;
00124         if (ymn < ymin)
00125             ymin = ymn;
00126         if (xmx > xmax)
00127             xmax = xmx;
00128         if (ymx > ymax)
00129             ymax = ymx;
00130     }
00131 
00132     marg = agget(graph, "voro_margin");
00133     if (marg && (*marg != '\0')) {
00134         margin = atof(marg);
00135     }
00136     ydelta = margin * (ymax - ymin);
00137     xdelta = margin * (xmax - xmin);
00138     ll.x = xmin - xdelta;
00139     ll.y = ymin - ydelta;
00140     ur.x = xmax + xdelta;
00141     ur.y = ymax + ydelta;
00142 
00143     setBoundBox(&ll, &ur);
00144 }
00145 
00146  /* makeInfo:
00147   * For each node in the graph, create a Info data structure 
00148   */
00149 static void makeInfo(Agraph_t * graph)
00150 {
00151     Agnode_t *node;
00152     int i;
00153     Info_t *ip;
00154 
00155     nsites = agnnodes(graph);
00156     geominit();
00157 
00158     nodeInfo = N_GNEW(nsites, Info_t);
00159 
00160     node = agfstnode(graph);
00161     ip = nodeInfo;
00162 
00163     pmargin = expFactor (graph);
00164     for (i = 0; i < nsites; i++) {
00165         ip->site.coord.x = ND_pos(node)[0];
00166         ip->site.coord.y = ND_pos(node)[1];
00167 
00168         makePoly(&ip->poly, node, pmargin);
00169 
00170         ip->site.sitenbr = i;
00171         ip->site.refcnt = 1;
00172         ip->node = node;
00173         ip->verts = NULL;
00174         node = agnxtnode(graph, node);
00175         ip++;
00176     }
00177 }
00178 
00179 /* sort sites on y, then x, coord */
00180 static int scomp(const void *S1, const void *S2)
00181 {
00182     Site *s1, *s2;
00183 
00184     s1 = *(Site **) S1;
00185     s2 = *(Site **) S2;
00186     if (s1->coord.y < s2->coord.y)
00187         return (-1);
00188     if (s1->coord.y > s2->coord.y)
00189         return (1);
00190     if (s1->coord.x < s2->coord.x)
00191         return (-1);
00192     if (s1->coord.x > s2->coord.x)
00193         return (1);
00194     return (0);
00195 }
00196 
00197  /* sortSites:
00198   * Fill array of pointer to sites and sort the sites using scomp
00199   */
00200 static void sortSites(void)
00201 {
00202     int i;
00203     Site **sp;
00204     Info_t *ip;
00205 
00206     if (sites == 0) {
00207         sites = N_GNEW(nsites, Site *);
00208         endSite = sites + nsites;
00209     }
00210 
00211     sp = sites;
00212     ip = nodeInfo;
00213     infoinit();
00214     for (i = 0; i < nsites; i++) {
00215         *sp++ = &(ip->site);
00216         ip->verts = NULL;
00217         ip->site.refcnt = 1;
00218         ip++;
00219     }
00220 
00221     qsort(sites, nsites, sizeof(Site *), scomp);
00222 
00223     /* Reset site index for nextOne */
00224     nextSite = sites;
00225 
00226 }
00227 
00228 static void geomUpdate(int doSort)
00229 {
00230     int i;
00231 
00232     if (doSort)
00233         sortSites();
00234 
00235     /* compute ranges */
00236     xmin = sites[0]->coord.x;
00237     xmax = sites[0]->coord.x;
00238     for (i = 1; i < nsites; i++) {
00239         if (sites[i]->coord.x < xmin)
00240             xmin = sites[i]->coord.x;
00241         if (sites[i]->coord.x > xmax)
00242             xmax = sites[i]->coord.x;
00243     }
00244     ymin = sites[0]->coord.y;
00245     ymax = sites[nsites - 1]->coord.y;
00246 
00247     deltay = ymax - ymin;
00248     deltax = xmax - xmin;
00249 }
00250 
00251 static Site *nextOne(void)
00252 {
00253     Site *s;
00254 
00255     if (nextSite < endSite) {
00256         s = *nextSite++;
00257         return (s);
00258     } else
00259         return ((Site *) NULL);
00260 }
00261 
00262 /* rmEquality:
00263  * Check for nodes with identical positions and tweak
00264  * the positions.
00265  */
00266 static void rmEquality(void)
00267 {
00268     int i, cnt;
00269     Site **ip;
00270     Site **jp;
00271     Site **kp;
00272     double xdel;
00273 
00274     sortSites();
00275     ip = sites;
00276 
00277     while (ip < endSite) {
00278         jp = ip + 1;
00279         if ((jp >= endSite) ||
00280             ((*jp)->coord.x != (*ip)->coord.x) ||
00281             ((*jp)->coord.y != (*ip)->coord.y)) {
00282             ip = jp;
00283             continue;
00284         }
00285 
00286         /* Find first node kp with position different from ip */
00287         cnt = 2;
00288         kp = jp + 1;
00289         while ((kp < endSite) &&
00290                ((*kp)->coord.x == (*ip)->coord.x) &&
00291                ((*kp)->coord.y == (*ip)->coord.y)) {
00292             cnt++;
00293             jp = kp;
00294             kp = jp + 1;
00295         }
00296 
00297         /* If next node exists and is on the same line */
00298         if ((kp < endSite) && ((*kp)->coord.y == (*ip)->coord.y)) {
00299             xdel = ((*kp)->coord.x - (*ip)->coord.x) / cnt;
00300             i = 1;
00301             for (jp = ip + 1; jp < kp; jp++) {
00302                 (*jp)->coord.x += i * xdel;
00303                 i++;
00304             }
00305         } else {                /* nothing is to the right */
00306             Info_t *info;
00307             for (jp = ip + 1; jp < kp; ip++, jp++) {
00308                 info = nodeInfo + (*ip)->sitenbr;
00309                 xdel = info->poly.corner.x - info->poly.origin.x;
00310                 info = nodeInfo + (*jp)->sitenbr;
00311                 xdel += info->poly.corner.x - info->poly.origin.x;
00312                 (*jp)->coord.x = (*ip)->coord.x + xdel / 2;
00313             }
00314         }
00315         ip = kp;
00316     }
00317 }
00318 
00319 /* countOverlap:
00320  * Count number of node-node overlaps at iteration iter.
00321  */
00322 static int countOverlap(int iter)
00323 {
00324     int count = 0;
00325     int i, j;
00326     Info_t *ip = nodeInfo;
00327     Info_t *jp;
00328 
00329     for (i = 0; i < nsites; i++)
00330         nodeInfo[i].overlaps = 0;
00331 
00332     for (i = 0; i < nsites - 1; i++) {
00333         jp = ip + 1;
00334         for (j = i + 1; j < nsites; j++) {
00335             if (polyOverlap
00336                 (ip->site.coord, &ip->poly, jp->site.coord, &jp->poly)) {
00337                 count++;
00338                 ip->overlaps = 1;
00339                 jp->overlaps = 1;
00340             }
00341             jp++;
00342         }
00343         ip++;
00344     }
00345 
00346     if (Verbose > 1)
00347         fprintf(stderr, "overlap [%d] : %d\n", iter, count);
00348     return count;
00349 }
00350 
00351 static void increaseBoundBox(void)
00352 {
00353     double ydelta, xdelta;
00354     Point ll, ur;
00355 
00356     ur.x = pxmax;
00357     ur.y = pymax;
00358     ll.x = pxmin;
00359     ll.y = pymin;
00360 
00361     ydelta = incr * (ur.y - ll.y);
00362     xdelta = incr * (ur.x - ll.x);
00363 
00364     ur.x += xdelta;
00365     ur.y += ydelta;
00366     ll.x -= xdelta;
00367     ll.y -= ydelta;
00368 
00369     setBoundBox(&ll, &ur);
00370 }
00371 
00372  /* areaOf:
00373   * Area of triangle whose vertices are a,b,c
00374   */
00375 static double areaOf(Point a, Point b, Point c)
00376 {
00377     double area;
00378 
00379     area =
00380         (double) (fabs
00381                   (a.x * (b.y - c.y) + b.x * (c.y - a.y) +
00382                    c.x * (a.y - b.y)) / 2);
00383     return area;
00384 }
00385 
00386  /* centroidOf:
00387   * Compute centroid of triangle with vertices a, b, c.
00388   * Return coordinates in x and y.
00389   */
00390 static void centroidOf(Point a, Point b, Point c, double *x, double *y)
00391 {
00392     *x = (a.x + b.x + c.x) / 3;
00393     *y = (a.y + b.y + c.y) / 3;
00394 }
00395 
00396  /* newpos;
00397   * The new position is the centroid of the
00398   * voronoi polygon. This is the weighted sum of the
00399   * centroids of a triangulation, normalized to the
00400   * total area.
00401   */
00402 static void newpos(Info_t * ip)
00403 {
00404     PtItem *anchor = ip->verts;
00405     PtItem *p, *q;
00406     double totalArea = 0.0;
00407     double cx = 0.0;
00408     double cy = 0.0;
00409     double x;
00410     double y;
00411     double area;
00412 
00413     p = anchor->next;
00414     q = p->next;
00415     while (q != NULL) {
00416         area = areaOf(anchor->p, p->p, q->p);
00417         centroidOf(anchor->p, p->p, q->p, &x, &y);
00418         cx = cx + area * x;
00419         cy = cy + area * y;
00420         totalArea = totalArea + area;
00421         p = q;
00422         q = q->next;
00423     }
00424 
00425     ip->site.coord.x = cx / totalArea;
00426     ip->site.coord.y = cy / totalArea;
00427 }
00428 
00429  /* addCorners:
00430   * Add corners of clipping window to appropriate sites.
00431   * A site gets a corner if it is the closest site to that corner.
00432   */
00433 static void addCorners(void)
00434 {
00435     Info_t *ip = nodeInfo;
00436     Info_t *sws = ip;
00437     Info_t *nws = ip;
00438     Info_t *ses = ip;
00439     Info_t *nes = ip;
00440     double swd = dist_2(&ip->site.coord, &sw);
00441     double nwd = dist_2(&ip->site.coord, &nw);
00442     double sed = dist_2(&ip->site.coord, &se);
00443     double ned = dist_2(&ip->site.coord, &ne);
00444     double d;
00445     int i;
00446 
00447     ip++;
00448     for (i = 1; i < nsites; i++) {
00449         d = dist_2(&ip->site.coord, &sw);
00450         if (d < swd) {
00451             swd = d;
00452             sws = ip;
00453         }
00454         d = dist_2(&ip->site.coord, &se);
00455         if (d < sed) {
00456             sed = d;
00457             ses = ip;
00458         }
00459         d = dist_2(&ip->site.coord, &nw);
00460         if (d < nwd) {
00461             nwd = d;
00462             nws = ip;
00463         }
00464         d = dist_2(&ip->site.coord, &ne);
00465         if (d < ned) {
00466             ned = d;
00467             nes = ip;
00468         }
00469         ip++;
00470     }
00471 
00472     addVertex(&sws->site, sw.x, sw.y);
00473     addVertex(&ses->site, se.x, se.y);
00474     addVertex(&nws->site, nw.x, nw.y);
00475     addVertex(&nes->site, ne.x, ne.y);
00476 }
00477 
00478  /* newPos:
00479   * Calculate the new position of a site as the centroid
00480   * of its voronoi polygon, if it overlaps other nodes.
00481   * The polygons are finite by being clipped to the clipping
00482   * window.
00483   * We first add the corner of the clipping windows to the
00484   * vertex lists of the appropriate sites.
00485   */
00486 static void newPos(void)
00487 {
00488     int i;
00489     Info_t *ip = nodeInfo;
00490 
00491     addCorners();
00492     for (i = 0; i < nsites; i++) {
00493         if (doAll || ip->overlaps)
00494             newpos(ip);
00495         ip++;
00496     }
00497 }
00498 
00499 /* cleanup:
00500  * Cleanup voronoi memory.
00501  * Note that PQcleanup and ELcleanup rely on the number
00502  * of sites, so should at least be reset everytime we use
00503  * vAdjust.
00504  * This could be optimized, over multiple components or
00505  * even multiple graphs, but probably not worth it.
00506  */
00507 static void cleanup(void)
00508 {
00509     PQcleanup();
00510     ELcleanup();
00511     siteinit();                 /* free memory */
00512     edgeinit();                 /* free memory */
00513 }
00514 
00515 static int vAdjust(void)
00516 {
00517     int iterCnt = 0;
00518     int overlapCnt = 0;
00519     int badLevel = 0;
00520     int increaseCnt = 0;
00521     int cnt;
00522 
00523     if (!useIter || (iterations > 0))
00524         overlapCnt = countOverlap(iterCnt);
00525 
00526     if ((overlapCnt == 0) || (iterations == 0))
00527         return 0;
00528 
00529     rmEquality();
00530     geomUpdate(0);
00531     voronoi(0, nextOne);
00532     while (1) {
00533         newPos();
00534         iterCnt++;
00535 
00536         if (useIter && (iterCnt == iterations))
00537             break;
00538         cnt = countOverlap(iterCnt);
00539         if (cnt == 0)
00540             break;
00541         if (cnt >= overlapCnt)
00542             badLevel++;
00543         else
00544             badLevel = 0;
00545         overlapCnt = cnt;
00546 
00547         switch (badLevel) {
00548         case 0:
00549             doAll = 1;
00550             break;
00551 /*
00552       case 1:
00553         doAll = 1;
00554         break;
00555 */
00556         default:
00557             doAll = 1;
00558             increaseCnt++;
00559             increaseBoundBox();
00560             break;
00561         }
00562 
00563         geomUpdate(1);
00564         voronoi(0, nextOne);
00565     }
00566 
00567     if (Verbose) {
00568         fprintf(stderr, "Number of iterations = %d\n", iterCnt);
00569         fprintf(stderr, "Number of increases = %d\n", increaseCnt);
00570     }
00571 
00572     cleanup();
00573     return 1;
00574 }
00575 
00576 static double rePos(Point c)
00577 {
00578     int i;
00579     Info_t *ip = nodeInfo;
00580     double f = 1.0 + incr;
00581 
00582     for (i = 0; i < nsites; i++) {
00583         /* ip->site.coord.x = f*(ip->site.coord.x - c.x) + c.x; */
00584         /* ip->site.coord.y = f*(ip->site.coord.y - c.y) + c.y; */
00585         ip->site.coord.x = f * ip->site.coord.x;
00586         ip->site.coord.y = f * ip->site.coord.y;
00587         ip++;
00588     }
00589     return f;
00590 }
00591 
00592 static int sAdjust(void)
00593 {
00594     int iterCnt = 0;
00595     int overlapCnt = 0;
00596     int cnt;
00597     Point center;
00598     /* double sc; */
00599 
00600     if (!useIter || (iterations > 0))
00601         overlapCnt = countOverlap(iterCnt);
00602 
00603     if ((overlapCnt == 0) || (iterations == 0))
00604         return 0;
00605 
00606     rmEquality();
00607     center.x = (pxmin + pxmax) / 2.0;
00608     center.y = (pymin + pymax) / 2.0;
00609     while (1) {
00610         /* sc = */ rePos(center);
00611         iterCnt++;
00612 
00613         if (useIter && (iterCnt == iterations))
00614             break;
00615         cnt = countOverlap(iterCnt);
00616         if (cnt == 0)
00617             break;
00618     }
00619 
00620     if (Verbose) {
00621         fprintf(stderr, "Number of iterations = %d\n", iterCnt);
00622     }
00623 
00624     return 1;
00625 }
00626 
00627  /* updateGraph:
00628   * Enter new node positions into the graph
00629   */
00630 static void updateGraph(Agraph_t * graph)
00631 {
00632     /* Agnode_t*    node; */
00633     int i;
00634     Info_t *ip;
00635     /* char         pos[100]; */
00636 
00637     ip = nodeInfo;
00638     for (i = 0; i < nsites; i++) {
00639         ND_pos(ip->node)[0] = ip->site.coord.x;
00640         ND_pos(ip->node)[1] = ip->site.coord.y;
00641         ip++;
00642     }
00643 }
00644 
00645 #ifdef IPSEPCOLA
00646 static int
00647 vpscAdjust(graph_t* G)
00648 {
00649     int dim = 2;
00650     int nnodes = agnnodes(G);
00651     ipsep_options opt;
00652     pointf* nsize = N_GNEW(nnodes, pointf);
00653     float** coords = N_GNEW(dim, float*);
00654     float* f_storage = N_GNEW(dim * nnodes, float);
00655     int i, j;
00656     Agnode_t* v;
00657     char* str;
00658 
00659     for (i = 0; i < dim; i++) {
00660         coords[i] = f_storage + i * nnodes;
00661     }
00662 
00663     j = 0;
00664     for (v = agfstnode(G); v; v = agnxtnode(G, v)) {
00665         for (i = 0; i < dim; i++) {
00666             coords[i][j] =  (float) (ND_pos(v)[i]);
00667         }
00668         nsize[j].x = ND_width(v);
00669         nsize[j].y = ND_height(v);
00670         j++;
00671     }
00672 
00673     opt.diredges = 0;
00674     opt.edge_gap = 0;
00675     opt.noverlap = 2;
00676     opt.clusters = NEW(cluster_data);
00677     if ((str = agget(G, "sep")) && 
00678         (i = sscanf(str, "%lf,%lf", &opt.gap.x, &opt.gap.y))) {
00679             if (i == 1) opt.gap.y = opt.gap.x;
00680             if(Verbose)
00681                 fprintf(stderr,"gap=%f,%f\n",opt.gap.x,opt.gap.y);
00682     }
00683     else opt.gap.x = opt.gap.y = PS2INCH(10);
00684     opt.nsize = nsize;
00685 
00686     removeoverlaps(nnodes, coords, &opt);
00687 
00688     j = 0;
00689     for (v = agfstnode(G); v; v = agnxtnode(G, v)) {
00690         for (i = 0; i < dim; i++) {
00691             ND_pos(v)[i] = coords[i][j];
00692         }
00693         j++;
00694     }
00695 
00696     free (opt.clusters);
00697     free (f_storage);
00698     free (coords);
00699     free (nsize);
00700     return 0;
00701 }
00702 #endif
00703 
00704 /* normalize:
00705  * If normalize is set, move first node to origin, then
00706  * rotate graph so that first edge is horizontal.
00707  * FIX: Generalize to allow rotation determined by graph shape.
00708  */
00709 void normalize(graph_t * g)
00710 {
00711     node_t *v;
00712     edge_t *e;
00713 
00714     double theta;
00715     pointf p;
00716 
00717     if (!mapbool(agget(g, "normalize")))
00718         return;
00719 
00720     v = agfstnode(g);
00721     p.x = ND_pos(v)[0];
00722     p.y = ND_pos(v)[1];
00723     for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
00724         ND_pos(v)[0] -= p.x;
00725         ND_pos(v)[1] -= p.y;
00726     }
00727 
00728     e = NULL;
00729     for (v = agfstnode(g); v; v = agnxtnode(g, v))
00730         if ((e = agfstout(g, v)))
00731             break;
00732     if (e == NULL)
00733         return;
00734 
00735     theta = -atan2(ND_pos(e->head)[1] - ND_pos(e->tail)[1],
00736                    ND_pos(e->head)[0] - ND_pos(e->tail)[0]);
00737 
00738     for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
00739         p.x = ND_pos(v)[0];
00740         p.y = ND_pos(v)[1];
00741         ND_pos(v)[0] = p.x * cos(theta) - p.y * sin(theta);
00742         ND_pos(v)[1] = p.x * sin(theta) + p.y * cos(theta);
00743     }
00744 }
00745 
00746 static adjust_data adjustMode[] = {
00747     {AM_NONE, "", ""},
00748     {AM_VOR, "", "Voronoi"},
00749     {AM_SCALE, "oscale", "old scaling"},
00750     {AM_NSCALE, "scale", "scaling"},
00751     {AM_SCALEXY, "scalexy", "x and y scaling"},
00752     /* {AM_PUSH, "push", "push scan adjust"}, */
00753     /* {AM_PUSHPULL, "pushpull", "push-pull scan adjust"}, */
00754     {AM_ORTHO, "ortho", "orthogonal constraints"},
00755     {AM_ORTHO_YX, "ortho_yx", "orthogonal constraints"},
00756     {AM_ORTHOXY, "orthoxy", "xy orthogonal constraints"},
00757     {AM_ORTHOYX, "orthoyx", "yx orthogonal constraints"},
00758     {AM_PORTHO, "portho", "pseudo-orthogonal constraints"},
00759     {AM_PORTHO_YX, "portho_yx", "pseudo-orthogonal constraints"},
00760     {AM_PORTHOXY, "porthoxy", "xy pseudo-orthogonal constraints"},
00761     {AM_PORTHOYX, "porthoyx", "yx pseudo-orthogonal constraints"},
00762     {AM_COMPRESS, "compress", "compress"},
00763     {AM_VPSC, "vpsc", "vpsc"},
00764     {AM_IPSEP, "ipsep", "ipsep"},
00765     {AM_NONE, 0, 0}
00766 };
00767 
00768 /* getAdjustMode:
00769  * Convert string value to internal value of adjustment mode.
00770  * Assume s != NULL.
00771  */
00772 static adjust_data *getAdjustMode(char *s)
00773 {
00774     adjust_data *ap = adjustMode + 2;
00775     if (*s == '\0') return adjustMode;
00776     while (ap->attrib) {
00777         if (!strcasecmp(s, ap->attrib))
00778             return ap;
00779         ap++;
00780     }
00781     if (mapbool(s))
00782         return adjustMode;
00783     else
00784         return adjustMode + 1;
00785 }
00786 
00787 adjust_data *graphAdjustMode(graph_t *G)
00788 {
00789     char* am = agget(G, "overlap");
00790     return (getAdjustMode (am ? am : ""));
00791 }
00792 
00793 /* removeOverlapAs:
00794  * Use flag value to determine if and how to remove
00795  * node overlaps.
00796  */
00797 int 
00798 removeOverlapAs(graph_t * G, char* flag)
00799 {
00800     /* int          userWindow = 0; */
00801     int ret = 0;
00802     /* extern void  scanAdjust(graph_t*, int); */
00803 
00804     adjust_data *am;
00805 
00806     if (agnnodes(G) < 2)
00807         return 0;
00808     if (flag == NULL)
00809         return 0;
00810 
00811     am = getAdjustMode(flag);
00812     if (am->mode == AM_NONE)
00813         return 0;
00814 
00815     if (Verbose)
00816         fprintf(stderr, "Adjusting %s using %s\n", G->name, am->print);
00817 
00818     if (am->mode > AM_SCALE) {
00819 /* start_timer(); */
00820         switch (am->mode) {
00821         case AM_NSCALE:
00822             ret = scAdjust(G, 1);
00823             break;
00824         case AM_SCALEXY:
00825             ret = scAdjust(G, 0);
00826             break;
00827         case AM_PUSH:
00828             /* scanAdjust (G, 1); */
00829             break;
00830         case AM_PUSHPULL:
00831             /* scanAdjust (G, 0); */
00832             break;
00833         case AM_PORTHO_YX:
00834         case AM_PORTHO:
00835         case AM_PORTHOXY:
00836         case AM_PORTHOYX:
00837         case AM_ORTHO_YX:
00838         case AM_ORTHO:
00839         case AM_ORTHOXY:
00840         case AM_ORTHOYX:
00841             cAdjust(G, am->mode);
00842             break;
00843         case AM_COMPRESS:
00844             ret = scAdjust(G, -1);
00845             break;
00846 #ifdef IPSEPCOLA
00847         case AM_VPSC:
00848             ret = vpscAdjust(G);
00849             break;
00850 #endif
00851         default:                /* to silence warnings */
00852             break;
00853         }
00854 /* fprintf (stderr, "%s %.4f sec\n", am->attrib, elapsed_sec()); */
00855         return ret;
00856     }
00857 
00858     /* create main array */
00859 /* start_timer(); */
00860     makeInfo(G);
00861 
00862     /* establish and verify bounding box */
00863     chkBoundBox(G);
00864 
00865     if (am->mode == AM_SCALE)
00866         ret = sAdjust();
00867     else
00868         ret = vAdjust();
00869 
00870     if (ret)
00871         updateGraph(G);
00872 
00873     freeNodes();
00874     free(sites);
00875     sites = 0;
00876 /* fprintf (stderr, "old scale %.4f sec\n", elapsed_sec()); */
00877 
00878     return ret;
00879 }
00880 
00881 /* removeOverlap:
00882  */
00883 int 
00884 removeOverlap(graph_t * G)
00885 {
00886     return (removeOverlapAs(G, agget(G, "overlap")));
00887 }
00888 
00889 /* adjustNodes:
00890  * Remove node overlap relying on graph's overlap attribute.
00891  * Return non-zero if graph has changed.
00892  */
00893 int adjustNodes(graph_t * G)
00894 {
00895     if (agnnodes(G) < 2)
00896         return 0;
00897     normalize(G);
00898     return removeOverlap (G);
00899 }
00900 
00901 /* expFactor:
00902  * Return factor by which to scale up nodes.
00903  */
00904 double 
00905 expFactor(graph_t* g)
00906 {
00907     double pmargin;
00908     char*  marg;
00909 
00910     if ((marg = agget(g, "sep")))
00911         pmargin = 1.0 + atof(marg);
00912     else if ((marg = agget(g, "esep")))
00913         pmargin = 1.0 + atof(marg)/SEPFACT;
00914     else
00915         pmargin = 1.1;
00916     return pmargin;
00917 }
00918 

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