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