|
Graphviz
2.29.20120523.0446
|
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 }
1.7.5