Graphviz  2.29.20120523.0446
lib/neatogen/adjust.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 /* adjust.c
00015  * Routines for repositioning nodes after initial layout in
00016  * order to reduce/remove node overlaps.
00017  */
00018 
00019 #include "neato.h"
00020 #include "agxbuf.h"
00021 #include "utils.h"
00022 #include "ctype.h"
00023 #include "voronoi.h"
00024 #include "info.h"
00025 #include "edges.h"
00026 #include "site.h"
00027 #include "heap.h"
00028 #include "hedges.h"
00029 #include "digcola.h"
00030 #if ((defined(HAVE_GTS) || defined(HAVE_TRIANGLE)) && defined(SFDP))
00031 #include "overlap.h"
00032 #endif
00033 #ifdef IPSEPCOLA
00034 #include "csolve_VPSC.h"
00035 #include "quad_prog_vpsc.h"
00036 #endif
00037 
00038 #define SEPFACT         0.8  /* default esep/sep */
00039 
00040 static double margin = 0.05;    /* Create initial bounding box by adding
00041                                  * margin * dimension around box enclosing
00042                                  * nodes.
00043                                  */
00044 static double incr = 0.05;      /* Increase bounding box by adding
00045                                  * incr * dimension around box.
00046                                  */
00047 static int iterations = -1;     /* Number of iterations */
00048 static int useIter = 0;         /* Use specified number of iterations */
00049 
00050 static int doAll = 0;           /* Move all nodes, regardless of overlap */
00051 static Site **sites;            /* Array of pointers to sites; used in qsort */
00052 static Site **endSite;          /* Sentinel on sites array */
00053 static Point nw, ne, sw, se;    /* Corners of clipping window */
00054 
00055 static Site **nextSite;
00056 
00057 static void setBoundBox(Point * ll, Point * ur)
00058 {
00059     pxmin = ll->x;
00060     pxmax = ur->x;
00061     pymin = ll->y;
00062     pymax = ur->y;
00063     nw.x = sw.x = pxmin;
00064     ne.x = se.x = pxmax;
00065     nw.y = ne.y = pymax;
00066     sw.y = se.y = pymin;
00067 }
00068 
00069  /* freeNodes:
00070   * Free node resources.
00071   */
00072 static void freeNodes(void)
00073 {
00074     int i;
00075     Info_t *ip = nodeInfo;
00076 
00077     for (i = 0; i < nsites; i++) {
00078         breakPoly(&ip->poly);
00079         ip++;
00080     }
00081     polyFree();
00082     infoinit();                 /* Free vertices */
00083     free(nodeInfo);
00084 }
00085 
00086 /* chkBoundBox:
00087  *   Compute extremes of graph, then set up bounding box.
00088  *   If user supplied a bounding box, use that;
00089  *   else if "window" is a graph attribute, use that; 
00090  *   otherwise, define bounding box as a percentage expansion of
00091  *   graph extremes.
00092  *   In the first two cases, check that graph fits in bounding box.
00093  */
00094 static void chkBoundBox(Agraph_t * graph)
00095 {
00096     char *marg;
00097     Point ll, ur;
00098     int i;
00099     double x, y;
00100     double xmin, xmax, ymin, ymax;
00101     double xmn, xmx, ymn, ymx;
00102     double ydelta, xdelta;
00103     Info_t *ip;
00104     Poly *pp;
00105     /* int          cnt; */
00106 
00107     ip = nodeInfo;
00108     pp = &ip->poly;
00109     x = ip->site.coord.x;
00110     y = ip->site.coord.y;
00111     xmin = pp->origin.x + x;
00112     ymin = pp->origin.y + y;
00113     xmax = pp->corner.x + x;
00114     ymax = pp->corner.y + y;
00115     for (i = 1; i < nsites; i++) {
00116         ip++;
00117         pp = &ip->poly;
00118         x = ip->site.coord.x;
00119         y = ip->site.coord.y;
00120         xmn = pp->origin.x + x;
00121         ymn = pp->origin.y + y;
00122         xmx = pp->corner.x + x;
00123         ymx = pp->corner.y + y;
00124         if (xmn < xmin)
00125             xmin = xmn;
00126         if (ymn < ymin)
00127             ymin = ymn;
00128         if (xmx > xmax)
00129             xmax = xmx;
00130         if (ymx > ymax)
00131             ymax = ymx;
00132     }
00133 
00134     marg = agget(graph, "voro_margin");
00135     if (marg && (*marg != '\0')) {
00136         margin = atof(marg);
00137     }
00138     ydelta = margin * (ymax - ymin);
00139     xdelta = margin * (xmax - xmin);
00140     ll.x = xmin - xdelta;
00141     ll.y = ymin - ydelta;
00142     ur.x = xmax + xdelta;
00143     ur.y = ymax + ydelta;
00144 
00145     setBoundBox(&ll, &ur);
00146 }
00147 
00148  /* makeInfo:
00149   * For each node in the graph, create a Info data structure 
00150   */
00151 static int makeInfo(Agraph_t * graph)
00152 {
00153     Agnode_t *node;
00154     int i;
00155     Info_t *ip;
00156     expand_t pmargin;
00157     int (*polyf)(Poly *, Agnode_t *, float, float);
00158 
00159     nsites = agnnodes(graph);
00160     geominit();
00161 
00162     nodeInfo = N_GNEW(nsites, Info_t);
00163 
00164     node = agfstnode(graph);
00165     ip = nodeInfo;
00166 
00167     pmargin = sepFactor (graph);
00168 
00169     if (pmargin.doAdd) {
00170         polyf = makeAddPoly;
00171         /* we need inches for makeAddPoly */
00172         pmargin.x = PS2INCH(pmargin.x);
00173         pmargin.y = PS2INCH(pmargin.y);
00174     }
00175         
00176     else polyf = makePoly;
00177     for (i = 0; i < nsites; i++) {
00178         ip->site.coord.x = ND_pos(node)[0];
00179         ip->site.coord.y = ND_pos(node)[1];
00180 
00181         if (polyf(&ip->poly, node, pmargin.x, pmargin.y)) {
00182             free (nodeInfo);
00183             nodeInfo = NULL;
00184             return 1;
00185         }
00186 
00187         ip->site.sitenbr = i;
00188         ip->site.refcnt = 1;
00189         ip->node = node;
00190         ip->verts = NULL;
00191         node = agnxtnode(graph, node);
00192         ip++;
00193     }
00194     return 0;
00195 }
00196 
00197 /* sort sites on y, then x, coord */
00198 static int scomp(const void *S1, const void *S2)
00199 {
00200     Site *s1, *s2;
00201 
00202     s1 = *(Site **) S1;
00203     s2 = *(Site **) S2;
00204     if (s1->coord.y < s2->coord.y)
00205         return (-1);
00206     if (s1->coord.y > s2->coord.y)
00207         return (1);
00208     if (s1->coord.x < s2->coord.x)
00209         return (-1);
00210     if (s1->coord.x > s2->coord.x)
00211         return (1);
00212     return (0);
00213 }
00214 
00215  /* sortSites:
00216   * Fill array of pointer to sites and sort the sites using scomp
00217   */
00218 static void sortSites(void)
00219 {
00220     int i;
00221     Site **sp;
00222     Info_t *ip;
00223 
00224     if (sites == 0) {
00225         sites = N_GNEW(nsites, Site *);
00226         endSite = sites + nsites;
00227     }
00228 
00229     sp = sites;
00230     ip = nodeInfo;
00231     infoinit();
00232     for (i = 0; i < nsites; i++) {
00233         *sp++ = &(ip->site);
00234         ip->verts = NULL;
00235         ip->site.refcnt = 1;
00236         ip++;
00237     }
00238 
00239     qsort(sites, nsites, sizeof(Site *), scomp);
00240 
00241     /* Reset site index for nextOne */
00242     nextSite = sites;
00243 
00244 }
00245 
00246 static void geomUpdate(int doSort)
00247 {
00248     int i;
00249 
00250     if (doSort)
00251         sortSites();
00252 
00253     /* compute ranges */
00254     xmin = sites[0]->coord.x;
00255     xmax = sites[0]->coord.x;
00256     for (i = 1; i < nsites; i++) {
00257         if (sites[i]->coord.x < xmin)
00258             xmin = sites[i]->coord.x;
00259         if (sites[i]->coord.x > xmax)
00260             xmax = sites[i]->coord.x;
00261     }
00262     ymin = sites[0]->coord.y;
00263     ymax = sites[nsites - 1]->coord.y;
00264 
00265     deltay = ymax - ymin;
00266     deltax = xmax - xmin;
00267 }
00268 
00269 static Site *nextOne(void)
00270 {
00271     Site *s;
00272 
00273     if (nextSite < endSite) {
00274         s = *nextSite++;
00275         return (s);
00276     } else
00277         return ((Site *) NULL);
00278 }
00279 
00280 /* rmEquality:
00281  * Check for nodes with identical positions and tweak
00282  * the positions.
00283  */
00284 static void rmEquality(void)
00285 {
00286     int i, cnt;
00287     Site **ip;
00288     Site **jp;
00289     Site **kp;
00290     double xdel;
00291 
00292     sortSites();
00293     ip = sites;
00294 
00295     while (ip < endSite) {
00296         jp = ip + 1;
00297         if ((jp >= endSite) ||
00298             ((*jp)->coord.x != (*ip)->coord.x) ||
00299             ((*jp)->coord.y != (*ip)->coord.y)) {
00300             ip = jp;
00301             continue;
00302         }
00303 
00304         /* Find first node kp with position different from ip */
00305         cnt = 2;
00306         kp = jp + 1;
00307         while ((kp < endSite) &&
00308                ((*kp)->coord.x == (*ip)->coord.x) &&
00309                ((*kp)->coord.y == (*ip)->coord.y)) {
00310             cnt++;
00311             jp = kp;
00312             kp = jp + 1;
00313         }
00314 
00315         /* If next node exists and is on the same line */
00316         if ((kp < endSite) && ((*kp)->coord.y == (*ip)->coord.y)) {
00317             xdel = ((*kp)->coord.x - (*ip)->coord.x) / cnt;
00318             i = 1;
00319             for (jp = ip + 1; jp < kp; jp++) {
00320                 (*jp)->coord.x += i * xdel;
00321                 i++;
00322             }
00323         } else {                /* nothing is to the right */
00324             Info_t *info;
00325             for (jp = ip + 1; jp < kp; ip++, jp++) {
00326                 info = nodeInfo + (*ip)->sitenbr;
00327                 xdel = info->poly.corner.x - info->poly.origin.x;
00328                 info = nodeInfo + (*jp)->sitenbr;
00329                 xdel += info->poly.corner.x - info->poly.origin.x;
00330                 (*jp)->coord.x = (*ip)->coord.x + xdel / 2;
00331             }
00332         }
00333         ip = kp;
00334     }
00335 }
00336 
00337 /* countOverlap:
00338  * Count number of node-node overlaps at iteration iter.
00339  */
00340 static int countOverlap(int iter)
00341 {
00342     int count = 0;
00343     int i, j;
00344     Info_t *ip = nodeInfo;
00345     Info_t *jp;
00346 
00347     for (i = 0; i < nsites; i++)
00348         nodeInfo[i].overlaps = 0;
00349 
00350     for (i = 0; i < nsites - 1; i++) {
00351         jp = ip + 1;
00352         for (j = i + 1; j < nsites; j++) {
00353             if (polyOverlap
00354                 (ip->site.coord, &ip->poly, jp->site.coord, &jp->poly)) {
00355                 count++;
00356                 ip->overlaps = 1;
00357                 jp->overlaps = 1;
00358             }
00359             jp++;
00360         }
00361         ip++;
00362     }
00363 
00364     if (Verbose > 1)
00365         fprintf(stderr, "overlap [%d] : %d\n", iter, count);
00366     return count;
00367 }
00368 
00369 static void increaseBoundBox(void)
00370 {
00371     double ydelta, xdelta;
00372     Point ll, ur;
00373 
00374     ur.x = pxmax;
00375     ur.y = pymax;
00376     ll.x = pxmin;
00377     ll.y = pymin;
00378 
00379     ydelta = incr * (ur.y - ll.y);
00380     xdelta = incr * (ur.x - ll.x);
00381 
00382     ur.x += xdelta;
00383     ur.y += ydelta;
00384     ll.x -= xdelta;
00385     ll.y -= ydelta;
00386 
00387     setBoundBox(&ll, &ur);
00388 }
00389 
00390  /* areaOf:
00391   * Area of triangle whose vertices are a,b,c
00392   */
00393 static double areaOf(Point a, Point b, Point c)
00394 {
00395     double area;
00396 
00397     area =
00398         (double) (fabs
00399                   (a.x * (b.y - c.y) + b.x * (c.y - a.y) +
00400                    c.x * (a.y - b.y)) / 2);
00401     return area;
00402 }
00403 
00404  /* centroidOf:
00405   * Compute centroid of triangle with vertices a, b, c.
00406   * Return coordinates in x and y.
00407   */
00408 static void centroidOf(Point a, Point b, Point c, double *x, double *y)
00409 {
00410     *x = (a.x + b.x + c.x) / 3;
00411     *y = (a.y + b.y + c.y) / 3;
00412 }
00413 
00414  /* newpos;
00415   * The new position is the centroid of the
00416   * voronoi polygon. This is the weighted sum of the
00417   * centroids of a triangulation, normalized to the
00418   * total area.
00419   */
00420 static void newpos(Info_t * ip)
00421 {
00422     PtItem *anchor = ip->verts;
00423     PtItem *p, *q;
00424     double totalArea = 0.0;
00425     double cx = 0.0;
00426     double cy = 0.0;
00427     double x;
00428     double y;
00429     double area;
00430 
00431     p = anchor->next;
00432     q = p->next;
00433     while (q != NULL) {
00434         area = areaOf(anchor->p, p->p, q->p);
00435         centroidOf(anchor->p, p->p, q->p, &x, &y);
00436         cx = cx + area * x;
00437         cy = cy + area * y;
00438         totalArea = totalArea + area;
00439         p = q;
00440         q = q->next;
00441     }
00442 
00443     ip->site.coord.x = cx / totalArea;
00444     ip->site.coord.y = cy / totalArea;
00445 }
00446 
00447  /* addCorners:
00448   * Add corners of clipping window to appropriate sites.
00449   * A site gets a corner if it is the closest site to that corner.
00450   */
00451 static void addCorners(void)
00452 {
00453     Info_t *ip = nodeInfo;
00454     Info_t *sws = ip;
00455     Info_t *nws = ip;
00456     Info_t *ses = ip;
00457     Info_t *nes = ip;
00458     double swd = dist_2(&ip->site.coord, &sw);
00459     double nwd = dist_2(&ip->site.coord, &nw);
00460     double sed = dist_2(&ip->site.coord, &se);
00461     double ned = dist_2(&ip->site.coord, &ne);
00462     double d;
00463     int i;
00464 
00465     ip++;
00466     for (i = 1; i < nsites; i++) {
00467         d = dist_2(&ip->site.coord, &sw);
00468         if (d < swd) {
00469             swd = d;
00470             sws = ip;
00471         }
00472         d = dist_2(&ip->site.coord, &se);
00473         if (d < sed) {
00474             sed = d;
00475             ses = ip;
00476         }
00477         d = dist_2(&ip->site.coord, &nw);
00478         if (d < nwd) {
00479             nwd = d;
00480             nws = ip;
00481         }
00482         d = dist_2(&ip->site.coord, &ne);
00483         if (d < ned) {
00484             ned = d;
00485             nes = ip;
00486         }
00487         ip++;
00488     }
00489 
00490     addVertex(&sws->site, sw.x, sw.y);
00491     addVertex(&ses->site, se.x, se.y);
00492     addVertex(&nws->site, nw.x, nw.y);
00493     addVertex(&nes->site, ne.x, ne.y);
00494 }
00495 
00496  /* newPos:
00497   * Calculate the new position of a site as the centroid
00498   * of its voronoi polygon, if it overlaps other nodes.
00499   * The polygons are finite by being clipped to the clipping
00500   * window.
00501   * We first add the corner of the clipping windows to the
00502   * vertex lists of the appropriate sites.
00503   */
00504 static void newPos(void)
00505 {
00506     int i;
00507     Info_t *ip = nodeInfo;
00508 
00509     addCorners();
00510     for (i = 0; i < nsites; i++) {
00511         if (doAll || ip->overlaps)
00512             newpos(ip);
00513         ip++;
00514     }
00515 }
00516 
00517 /* cleanup:
00518  * Cleanup voronoi memory.
00519  * Note that PQcleanup and ELcleanup rely on the number
00520  * of sites, so should at least be reset everytime we use
00521  * vAdjust.
00522  * This could be optimized, over multiple components or
00523  * even multiple graphs, but probably not worth it.
00524  */
00525 static void cleanup(void)
00526 {
00527     PQcleanup();
00528     ELcleanup();
00529     siteinit();                 /* free memory */
00530     edgeinit();                 /* free memory */
00531 }
00532 
00533 static int vAdjust(void)
00534 {
00535     int iterCnt = 0;
00536     int overlapCnt = 0;
00537     int badLevel = 0;
00538     int increaseCnt = 0;
00539     int cnt;
00540 
00541     if (!useIter || (iterations > 0))
00542         overlapCnt = countOverlap(iterCnt);
00543 
00544     if ((overlapCnt == 0) || (iterations == 0))
00545         return 0;
00546 
00547     rmEquality();
00548     geomUpdate(0);
00549     voronoi(0, nextOne);
00550     while (1) {
00551         newPos();
00552         iterCnt++;
00553 
00554         if (useIter && (iterCnt == iterations))
00555             break;
00556         cnt = countOverlap(iterCnt);
00557         if (cnt == 0)
00558             break;
00559         if (cnt >= overlapCnt)
00560             badLevel++;
00561         else
00562             badLevel = 0;
00563         overlapCnt = cnt;
00564 
00565         switch (badLevel) {
00566         case 0:
00567             doAll = 1;
00568             break;
00569 /*
00570       case 1:
00571         doAll = 1;
00572         break;
00573 */
00574         default:
00575             doAll = 1;
00576             increaseCnt++;
00577             increaseBoundBox();
00578             break;
00579         }
00580 
00581         geomUpdate(1);
00582         voronoi(0, nextOne);
00583     }
00584 
00585     if (Verbose) {
00586         fprintf(stderr, "Number of iterations = %d\n", iterCnt);
00587         fprintf(stderr, "Number of increases = %d\n", increaseCnt);
00588     }
00589 
00590     cleanup();
00591     return 1;
00592 }
00593 
00594 static double rePos(Point c)
00595 {
00596     int i;
00597     Info_t *ip = nodeInfo;
00598     double f = 1.0 + incr;
00599 
00600     for (i = 0; i < nsites; i++) {
00601         /* ip->site.coord.x = f*(ip->site.coord.x - c.x) + c.x; */
00602         /* ip->site.coord.y = f*(ip->site.coord.y - c.y) + c.y; */
00603         ip->site.coord.x = f * ip->site.coord.x;
00604         ip->site.coord.y = f * ip->site.coord.y;
00605         ip++;
00606     }
00607     return f;
00608 }
00609 
00610 static int sAdjust(void)
00611 {
00612     int iterCnt = 0;
00613     int overlapCnt = 0;
00614     int cnt;
00615     Point center;
00616     /* double sc; */
00617 
00618     if (!useIter || (iterations > 0))
00619         overlapCnt = countOverlap(iterCnt);
00620 
00621     if ((overlapCnt == 0) || (iterations == 0))
00622         return 0;
00623 
00624     rmEquality();
00625     center.x = (pxmin + pxmax) / 2.0;
00626     center.y = (pymin + pymax) / 2.0;
00627     while (1) {
00628         /* sc = */ rePos(center);
00629         iterCnt++;
00630 
00631         if (useIter && (iterCnt == iterations))
00632             break;
00633         cnt = countOverlap(iterCnt);
00634         if (cnt == 0)
00635             break;
00636     }
00637 
00638     if (Verbose) {
00639         fprintf(stderr, "Number of iterations = %d\n", iterCnt);
00640     }
00641 
00642     return 1;
00643 }
00644 
00645  /* updateGraph:
00646   * Enter new node positions into the graph
00647   */
00648 static void updateGraph(Agraph_t * graph)
00649 {
00650     /* Agnode_t*    node; */
00651     int i;
00652     Info_t *ip;
00653     /* char         pos[100]; */
00654 
00655     ip = nodeInfo;
00656     for (i = 0; i < nsites; i++) {
00657         ND_pos(ip->node)[0] = ip->site.coord.x;
00658         ND_pos(ip->node)[1] = ip->site.coord.y;
00659         ip++;
00660     }
00661 }
00662 
00663 #define ELS "|edgelabel|"
00664 #define ELSN (sizeof(ELS)-1)
00665   /* Return true if node name starts with ELS */
00666 #define IS_LNODE(n) (!strncmp(agnameof(n),ELS,ELSN))
00667 
00668 /* getSizes:
00669  * Set up array of half sizes in inches.
00670  */
00671 double *getSizes(Agraph_t * g, pointf pad, int* n_elabels, int** elabels)
00672 {
00673     Agnode_t *n;
00674     real *sizes = N_GNEW(2 * agnnodes(g), real);
00675     int i, nedge_nodes;
00676     int* elabs;
00677 
00678     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00679         if (elabels && IS_LNODE(n)) nedge_nodes++;
00680 
00681         i = ND_id(n);
00682         sizes[i * 2] = ND_width(n) * .5 + pad.x;
00683         sizes[i * 2 + 1] = ND_height(n) * .5 + pad.y;
00684     }
00685 
00686     if (elabels && nedge_nodes) {
00687         elabs = N_GNEW(nedge_nodes, int);
00688         nedge_nodes = 0;
00689         for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00690             if (IS_LNODE(n))
00691                 elabs[nedge_nodes++] = ND_id(n);
00692         }
00693         *elabels = elabs;
00694         *n_elabels = nedge_nodes;
00695     }
00696 
00697     return sizes;
00698 }
00699 
00700 /* makeMatrix:
00701  * Assumes g is connected and simple, i.e., we can have a->b and b->a
00702  * but not a->b and a->b
00703  */
00704 SparseMatrix makeMatrix(Agraph_t* g, int dim, SparseMatrix *D)
00705 {
00706     SparseMatrix A = 0;
00707     Agnode_t *n;
00708     Agedge_t *e;
00709     Agsym_t *sym;
00710     int nnodes;
00711     int nedges;
00712     int i, row;
00713     int *I;
00714     int *J;
00715     real *val;
00716     real v;
00717     int type = MATRIX_TYPE_REAL;
00718     Agsym_t* symD = NULL;
00719     real* valD = NULL;
00720 
00721     if (!g)
00722         return NULL;
00723     nnodes = agnnodes(g);
00724     nedges = agnedges(g);
00725 
00726     /* Assign node ids */
00727     i = 0;
00728     for (n = agfstnode(g); n; n = agnxtnode(g, n))
00729         ND_id(n) = i++;
00730 
00731     I = N_GNEW(nedges, int);
00732     J = N_GNEW(nedges, int);
00733     val = N_GNEW(nedges, real);
00734 
00735     sym = agfindedgeattr(g, "weight");
00736     if (D) {
00737         symD = agfindedgeattr(g, "len");
00738         valD = N_NEW(nedges, real);
00739     }
00740 
00741     i = 0;
00742     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00743         row = ND_id(n);
00744         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00745             I[i] = row;
00746             J[i] = ND_id(aghead(e));
00747 #ifndef WITH_CGRAPH
00748             if (!sym || (sscanf(agxget(e, sym->index), "%lf", &v) != 1))
00749 #else
00750             if (!sym || (sscanf(agxget(e, sym), "%lf", &v) != 1))
00751 #endif
00752                 v = 1;
00753             val[i] = v;
00754         /* edge length */
00755             if (symD) {
00756 #ifndef WITH_CGRAPH
00757                 if (sscanf (agxget (e, symD->index), "%lf", &v) != 1) v = 1;
00758 #else
00759                 if (sscanf (agxget (e, symD), "%lf", &v) != 1) v = 1;
00760 #endif
00761                 valD[i] = v;
00762             }
00763             i++;
00764         }
00765     }
00766 
00767     A = SparseMatrix_from_coordinate_arrays(nedges, nnodes, nnodes, I, J,
00768                                             val, type);
00769 
00770     if (D) *D = SparseMatrix_from_coordinate_arrays(nedges, nnodes, nnodes, I, J, valD, type);
00771 
00772     free(I);
00773     free(J);
00774     free(val);
00775     if (valD) free (valD);
00776 
00777     return A;
00778 }
00779 
00780 #if ((defined(HAVE_GTS) || defined(HAVE_TRIANGLE)) && defined(SFDP))
00781 static int
00782 fdpAdjust (graph_t* g, adjust_data* am)
00783 {
00784     SparseMatrix A0 = makeMatrix(g, Ndim, NULL);
00785     SparseMatrix A = A0;
00786     real *sizes;
00787     real *pos = N_NEW(Ndim * agnnodes(g), real);
00788     Agnode_t *n;
00789     int flag, i;
00790     expand_t sep = sepFactor(g);
00791     pointf pad;
00792 
00793     if (sep.doAdd) {
00794         pad.x = PS2INCH(sep.x);
00795         pad.y = PS2INCH(sep.y);
00796     } else {
00797         pad.x = PS2INCH(DFLT_MARGIN);
00798         pad.y = PS2INCH(DFLT_MARGIN);
00799     }
00800     sizes = getSizes(g, pad, NULL, NULL);
00801 
00802     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00803         real* npos = pos + (Ndim * ND_id(n));
00804         for (i = 0; i < Ndim; i++) {
00805             npos[i] = ND_pos(n)[i];
00806         }
00807     }
00808 
00809     if (!SparseMatrix_is_symmetric(A, FALSE)
00810         || A->type != MATRIX_TYPE_REAL) {
00811         A = SparseMatrix_get_real_adjacency_matrix_symmetrized(A);
00812     } else {
00813         A = SparseMatrix_remove_diagonal(A);
00814     }
00815 
00816     remove_overlap(Ndim, A, pos, sizes, am->value, am->scaling, 
00817         ELSCHEME_NONE, 0, NULL, NULL, &flag);
00818 
00819     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00820         real *npos = pos + (Ndim * ND_id(n));
00821         for (i = 0; i < Ndim; i++) {
00822             ND_pos(n)[i] = npos[i];
00823         }
00824     }
00825 
00826     free(sizes);
00827     free(pos);
00828     if (A != A0)
00829         SparseMatrix_delete(A);
00830     SparseMatrix_delete (A0);
00831 
00832     return flag;
00833 }
00834 #endif
00835 
00836 #ifdef IPSEPCOLA
00837 static int
00838 vpscAdjust(graph_t* G)
00839 {
00840     int dim = 2;
00841     int nnodes = agnnodes(G);
00842     ipsep_options opt;
00843     pointf* nsize = N_GNEW(nnodes, pointf);
00844     float** coords = N_GNEW(dim, float*);
00845     float* f_storage = N_GNEW(dim * nnodes, float);
00846     int i, j;
00847     Agnode_t* v;
00848     expand_t margin;
00849 
00850     for (i = 0; i < dim; i++) {
00851         coords[i] = f_storage + i * nnodes;
00852     }
00853 
00854     j = 0;
00855     for (v = agfstnode(G); v; v = agnxtnode(G, v)) {
00856         for (i = 0; i < dim; i++) {
00857             coords[i][j] =  (float) (ND_pos(v)[i]);
00858         }
00859         nsize[j].x = ND_width(v);
00860         nsize[j].y = ND_height(v);
00861         j++;
00862     }
00863 
00864     opt.diredges = 0;
00865     opt.edge_gap = 0;
00866     opt.noverlap = 2;
00867     opt.clusters = NEW(cluster_data);
00868     margin = sepFactor (G);
00869         /* Multiply by 2 since opt.gap is the gap size, not the margin */
00870     if (margin.doAdd) {
00871         opt.gap.x = 2.0*PS2INCH(margin.x);
00872         opt.gap.y = 2.0*PS2INCH(margin.y);
00873     }
00874     else {
00875         opt.gap.x = opt.gap.y = 2.0*PS2INCH(DFLT_MARGIN);
00876     }
00877     opt.nsize = nsize;
00878 
00879     removeoverlaps(nnodes, coords, &opt);
00880 
00881     j = 0;
00882     for (v = agfstnode(G); v; v = agnxtnode(G, v)) {
00883         for (i = 0; i < dim; i++) {
00884             ND_pos(v)[i] = coords[i][j];
00885         }
00886         j++;
00887     }
00888 
00889     free (opt.clusters);
00890     free (f_storage);
00891     free (coords);
00892     free (nsize);
00893     return 0;
00894 }
00895 #endif
00896 
00897 /* normalize:
00898  * If normalize is set, move first node to origin, then
00899  * rotate graph so that first edge is horizontal.
00900  * FIX: Generalize to allow rotation determined by graph shape.
00901  */
00902 void normalize(graph_t * g)
00903 {
00904     node_t *v;
00905     edge_t *e;
00906 
00907     double theta;
00908     pointf p;
00909 
00910     if (!mapbool(agget(g, "normalize")))
00911         return;
00912 
00913     v = agfstnode(g);
00914     p.x = ND_pos(v)[0];
00915     p.y = ND_pos(v)[1];
00916     for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
00917         ND_pos(v)[0] -= p.x;
00918         ND_pos(v)[1] -= p.y;
00919     }
00920 
00921     e = NULL;
00922     for (v = agfstnode(g); v; v = agnxtnode(g, v))
00923         if ((e = agfstout(g, v)))
00924             break;
00925     if (e == NULL)
00926         return;
00927 
00928     theta = -atan2(ND_pos(aghead(e))[1] - ND_pos(agtail(e))[1],
00929                    ND_pos(aghead(e))[0] - ND_pos(agtail(e))[0]);
00930 
00931     for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
00932         p.x = ND_pos(v)[0];
00933         p.y = ND_pos(v)[1];
00934         ND_pos(v)[0] = p.x * cos(theta) - p.y * sin(theta);
00935         ND_pos(v)[1] = p.x * sin(theta) + p.y * cos(theta);
00936     }
00937 }
00938 
00939 typedef struct {
00940     adjust_mode mode;
00941     char *attrib;
00942     int len;
00943     char *print;
00944 } lookup_t;
00945 
00946 #define STRLEN(s) ((sizeof(s)-1)/sizeof(char))
00947 #define ITEM(i,s,v) {i, s, STRLEN(s), v}
00948 
00949 /* Translation table from overlap values to algorithms.
00950  * adjustMode[0] corresponds to overlap=true
00951  * adjustMode[1] corresponds to overlap=false
00952  */
00953 static lookup_t adjustMode[] = {
00954     ITEM(AM_NONE, "", "none"),
00955 #if ((defined(HAVE_GTS) || defined(HAVE_TRIANGLE)) && defined(SFDP))
00956     ITEM(AM_PRISM, "prism", "prism"),
00957 #endif
00958     ITEM(AM_VOR, "voronoi", "Voronoi"),
00959     ITEM(AM_NSCALE, "scale", "scaling"),
00960     ITEM(AM_COMPRESS, "compress", "compress"),
00961     ITEM(AM_VPSC, "vpsc", "vpsc"),
00962     ITEM(AM_IPSEP, "ipsep", "ipsep"),
00963     ITEM(AM_SCALE, "oscale", "old scaling"),
00964     ITEM(AM_SCALEXY, "scalexy", "x and y scaling"),
00965     ITEM(AM_ORTHO, "ortho", "orthogonal constraints"),
00966     ITEM(AM_ORTHO_YX, "ortho_yx", "orthogonal constraints"),
00967     ITEM(AM_ORTHOXY, "orthoxy", "xy orthogonal constraints"),
00968     ITEM(AM_ORTHOYX, "orthoyx", "yx orthogonal constraints"),
00969     ITEM(AM_PORTHO, "portho", "pseudo-orthogonal constraints"),
00970     ITEM(AM_PORTHO_YX, "portho_yx", "pseudo-orthogonal constraints"),
00971     ITEM(AM_PORTHOXY, "porthoxy", "xy pseudo-orthogonal constraints"),
00972     ITEM(AM_PORTHOYX, "porthoyx", "yx pseudo-orthogonal constraints"),
00973 #if !((defined(HAVE_GTS) || defined(HAVE_TRIANGLE)) && defined(SFDP))
00974     ITEM(AM_PRISM, "prism", 0),
00975 #endif
00976     {AM_NONE, 0, 0, 0}
00977 };
00978 
00979     
00980 /* setPrismValues:
00981  * Initialize and set prism values
00982  */
00983 static void
00984 setPrismValues (Agraph_t* g, char* s, adjust_data* dp)
00985 {
00986     int v;
00987 
00988     if ((sscanf (s, "%d", &v) > 0) && (v >= 0))
00989         dp->value = v;
00990     else
00991         dp->value = 1000;
00992     dp->scaling = late_double(g, agfindgraphattr(g, "overlap_scaling"), -4.0, -1.e10);
00993 }
00994 
00995 /* getAdjustMode:
00996  * Convert string value to internal value of adjustment mode.
00997  * Assume s != NULL.
00998  */
00999 static adjust_data *getAdjustMode(Agraph_t* g, char *s, adjust_data* dp)
01000 {
01001     lookup_t *ap = adjustMode + 1;
01002     if (*s == '\0') {
01003         dp->mode = adjustMode[0].mode;
01004         dp->print = adjustMode[0].print;
01005     }
01006     else {
01007         while (ap->attrib) {
01008             if (!strncasecmp(s, ap->attrib, ap->len)) {
01009                 if (ap->print == NULL) {
01010                     agerr (AGWARN, "Overlap value \"%s\" unsupported - ignored\n", ap->attrib);
01011                     ap = &adjustMode[1];
01012                 }
01013                 dp->mode = ap->mode;
01014                 dp->print = ap->print;
01015                 if (ap->mode == AM_PRISM)
01016                     setPrismValues (g, s + ap->len, dp);
01017                 break;
01018             }
01019             ap++;
01020         }
01021         if (ap->attrib == NULL ) {
01022             if (mapbool(s)) {
01023                 dp->mode = adjustMode[0].mode;
01024                 dp->print = adjustMode[0].print;
01025             }
01026             else {
01027                 dp->mode = adjustMode[1].mode;
01028                 dp->print = adjustMode[1].print;
01029             }
01030             if (dp->mode == AM_PRISM)
01031                 setPrismValues (g, "", dp);
01032         }
01033     }
01034     return dp;
01035 }
01036 
01037 adjust_data *graphAdjustMode(graph_t *G, adjust_data* dp, char* dflt)
01038 {
01039     char* am = agget(G, "overlap");
01040     return (getAdjustMode (G, am ? am : (dflt ? dflt : ""), dp));
01041 }
01042 
01043 /* removeOverlapWith:
01044  * Use adjust_data to determine if and how to remove
01045  * node overlaps.
01046  * Return non-zero if nodes are moved.
01047  */
01048 int 
01049 removeOverlapWith (graph_t * G, adjust_data* am)
01050 {
01051     int ret;
01052 
01053     if (agnnodes(G) < 2)
01054         return 0;
01055 
01056     normalize(G);
01057 
01058     if (am->mode == AM_NONE)
01059         return 0;
01060 
01061     if (Verbose)
01062         fprintf(stderr, "Adjusting %s using %s\n", agnameof(G), am->print);
01063 
01064     if (am->mode > AM_SCALE) {
01065 /* start_timer(); */
01066         switch (am->mode) {
01067         case AM_NSCALE:
01068             ret = scAdjust(G, 1);
01069             break;
01070         case AM_SCALEXY:
01071             ret = scAdjust(G, 0);
01072             break;
01073         case AM_PUSH:
01074             /* scanAdjust (G, 1); */
01075             break;
01076         case AM_PUSHPULL:
01077             /* scanAdjust (G, 0); */
01078             break;
01079         case AM_PORTHO_YX:
01080         case AM_PORTHO:
01081         case AM_PORTHOXY:
01082         case AM_PORTHOYX:
01083         case AM_ORTHO_YX:
01084         case AM_ORTHO:
01085         case AM_ORTHOXY:
01086         case AM_ORTHOYX:
01087             cAdjust(G, am->mode);
01088             break;
01089         case AM_COMPRESS:
01090             ret = scAdjust(G, -1);
01091             break;
01092 #if ((defined(HAVE_GTS) || defined(HAVE_TRIANGLE)) && defined(SFDP))
01093         case AM_PRISM:
01094             ret = fdpAdjust(G, am);
01095             break;
01096 #endif
01097 #ifdef IPSEPCOLA
01098         case AM_IPSEP:
01099             return 0;   /* handled during layout */
01100             break;
01101         case AM_VPSC:
01102             ret = vpscAdjust(G);
01103             break;
01104 #endif
01105         default:                /* to silence warnings */
01106             if ((am->mode != AM_VOR) && (am->mode != AM_SCALE))
01107                 agerr(AGWARN, "Unhandled adjust option %s\n", am->print);
01108             break;
01109         }
01110 /* fprintf (stderr, "%s %.4f sec\n", am->print, elapsed_sec()); */
01111         return ret;
01112     }
01113 
01114     /* create main array */
01115 /* start_timer(); */
01116     if (makeInfo(G)) {
01117         freeNodes();
01118         free(sites);
01119         sites = 0;
01120         return 0;
01121     }
01122 
01123     /* establish and verify bounding box */
01124     chkBoundBox(G);
01125 
01126     if (am->mode == AM_SCALE)
01127         ret = sAdjust();
01128     else
01129         ret = vAdjust();
01130 
01131     if (ret)
01132         updateGraph(G);
01133 
01134     freeNodes();
01135     free(sites);
01136     sites = 0;
01137 /* fprintf (stderr, "%s %.4f sec\n", am->print, elapsed_sec()); */
01138 
01139     return ret;
01140 }
01141 
01142 /* removeOverlapAs:
01143  * Use flag value to determine if and how to remove
01144  * node overlaps.
01145  */
01146 int 
01147 removeOverlapAs(graph_t * G, char* flag)
01148 {
01149     adjust_data am;
01150 
01151     if (agnnodes(G) < 2)
01152         return 0;
01153     if (flag == NULL)
01154         return 0;
01155 
01156     getAdjustMode(G, flag, &am);
01157     return removeOverlapWith (G, &am);
01158 }
01159 
01160 /* adjustNodes:
01161  * Remove node overlap relying on graph's overlap attribute.
01162  * Return non-zero if graph has changed.
01163  */
01164 int adjustNodes(graph_t * G)
01165 {
01166     return (removeOverlapAs(G, agget(G, "overlap")));
01167 }
01168 
01169 /* parseFactor:
01170  * Convert "sep" attribute into expand_t.
01171  * Input "+x,y" becomes {x,y,true}
01172  * Input "x,y" becomes {1 + x/sepfact,1 + y/sepfact,false}
01173  * Return 1 on success, 0 on failure
01174  */
01175 static int
01176 parseFactor (char* s, expand_t* pp, float sepfact)
01177 {
01178     int i;
01179     float x, y;
01180 
01181     while (isspace(*s)) s++;
01182     if (*s == '+') {
01183         s++;
01184         pp->doAdd = 1;
01185     }
01186     else pp->doAdd = 0;
01187 
01188     if ((i = sscanf(s, "%f,%f", &x, &y))) {
01189         if (i == 1) y = x;
01190         if (pp->doAdd) {
01191             pp->x = x/sepfact;
01192             pp->y = y/sepfact;
01193         }
01194         else {
01195             pp->x = 1.0 + x/sepfact;
01196             pp->y = 1.0 + y/sepfact;
01197         }
01198         return 1;
01199     }
01200     else return 0;
01201 }
01202 
01203 /* sepFactor:
01204  */
01205 expand_t
01206 sepFactor(graph_t* g)
01207 {
01208     expand_t pmargin;
01209     char*  marg;
01210 
01211     if ((marg = agget(g, "sep")) && parseFactor(marg, &pmargin, 1.0)) {
01212     }
01213     else if ((marg = agget(g, "esep")) && parseFactor(marg, &pmargin, SEPFACT)) {
01214     }
01215     else { /* default */
01216         pmargin.x = pmargin.y = DFLT_MARGIN;
01217         pmargin.doAdd = 1;
01218     }
01219     if (Verbose)
01220         fprintf (stderr, "Node separation: add=%d (%f,%f)\n",
01221             pmargin.doAdd, pmargin.x, pmargin.y);
01222     return pmargin;
01223 }
01224 
01225 /* esepFactor:
01226  * This value should be smaller than the sep value used to expand
01227  * nodes during adjustment. If not, when the adjustment pass produces
01228  * a fairly tight layout, the spline code will find that some nodes
01229  * still overlap.
01230  */
01231 expand_t
01232 esepFactor(graph_t* g)
01233 {
01234     expand_t pmargin;
01235     char*  marg;
01236 
01237     if ((marg = agget(g, "esep")) && parseFactor(marg, &pmargin, 1.0)) {
01238     }
01239     else if ((marg = agget(g, "sep")) && parseFactor(marg, &pmargin, 1.0/SEPFACT)) {
01240     }
01241     else {
01242         pmargin.x = pmargin.y = SEPFACT*DFLT_MARGIN;
01243         pmargin.doAdd = 1;
01244     }
01245     if (Verbose)
01246         fprintf (stderr, "Edge separation: add=%d (%f,%f)\n",
01247             pmargin.doAdd, pmargin.x, pmargin.y);
01248     return pmargin;
01249 }