|
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 /* layout.c: 00016 * Written by Emden R. Gansner 00017 * 00018 * This module provides the main bookkeeping for the fdp layout. 00019 * In particular, it handles the recursion and the creation of 00020 * ports and auxiliary graphs. 00021 * 00022 * TODO : can we use ports to aid in layout of edges? Note that 00023 * at present, they are deleted. 00024 * 00025 * Can we delay all repositioning of nodes until evalPositions, so 00026 * finalCC only sets the bounding boxes? 00027 * 00028 * Make sure multiple edges have an effect. 00029 */ 00030 00031 /* uses PRIVATE interface */ 00032 #define FDP_PRIVATE 1 00033 00034 #ifdef HAVE_CONFIG_H 00035 #include "config.h" 00036 #endif 00037 #ifdef HAVE_LIMITS_H 00038 #include <limits.h> 00039 #else 00040 #ifdef HAVE_VALUES_H 00041 #include <values.h> 00042 #endif 00043 #endif 00044 #include <assert.h> 00045 #include "tlayout.h" 00046 #include "neatoprocs.h" 00047 #include "adjust.h" 00048 #include "comp.h" 00049 #include "pack.h" 00050 #include "clusteredges.h" 00051 #include "dbg.h" 00052 #include <setjmp.h> 00053 00054 static jmp_buf jbuf; 00055 00056 typedef struct { 00057 graph_t* rootg; /* logical root; graph passed in to fdp_layout */ 00058 attrsym_t *G_coord; 00059 attrsym_t *G_width; 00060 attrsym_t *G_height; 00061 int gid; 00062 pack_info pack; 00063 } layout_info; 00064 00065 typedef struct { 00066 edge_t *e; 00067 double alpha; 00068 double dist2; 00069 } erec; 00070 00071 #define NEW_EDGE(e) (ED_to_virt(e) == 0) 00072 00073 /* finalCC: 00074 * Set graph bounding box given list of connected 00075 * components, each with its bounding box set. 00076 * If c_cnt > 1, then pts != NULL and gives translations for components. 00077 * Add margin about whole graph unless isRoot is true. 00078 * Reposition nodes based on final position of 00079 * node's connected component. 00080 * Also, entire layout is translated to origin. 00081 */ 00082 static void 00083 finalCC(graph_t * g, int c_cnt, graph_t ** cc, point * pts, graph_t * rg, 00084 layout_info* infop) 00085 { 00086 attrsym_t * G_width = infop->G_width; 00087 attrsym_t * G_height = infop->G_height; 00088 graph_t *cg; 00089 box b, bb; 00090 boxf bbf; 00091 point pt; 00092 int margin; 00093 graph_t **cp = cc; 00094 point *pp = pts; 00095 int isRoot = (rg == infop->rootg); 00096 int isEmpty = 0; 00097 00098 /* compute graph bounding box in points */ 00099 if (c_cnt) { 00100 cg = *cp++; 00101 BF2B(GD_bb(cg), bb); 00102 if (c_cnt > 1) { 00103 pt = *pp++; 00104 bb.LL.x += pt.x; 00105 bb.LL.y += pt.y; 00106 bb.UR.x += pt.x; 00107 bb.UR.y += pt.y; 00108 while ((cg = *cp++)) { 00109 BF2B(GD_bb(cg), b); 00110 pt = *pp++; 00111 b.LL.x += pt.x; 00112 b.LL.y += pt.y; 00113 b.UR.x += pt.x; 00114 b.UR.y += pt.y; 00115 bb.LL.x = MIN(bb.LL.x, b.LL.x); 00116 bb.LL.y = MIN(bb.LL.y, b.LL.y); 00117 bb.UR.x = MAX(bb.UR.x, b.UR.x); 00118 bb.UR.y = MAX(bb.UR.y, b.UR.y); 00119 } 00120 } 00121 } else { /* empty graph */ 00122 bb.LL.x = 0; 00123 bb.LL.y = 0; 00124 bb.UR.x = late_int(rg, G_width, POINTS(DEFAULT_NODEWIDTH), 3); 00125 bb.UR.y = late_int(rg, G_height, POINTS(DEFAULT_NODEHEIGHT), 3); 00126 isEmpty = 1; 00127 } 00128 00129 if (GD_label(rg)) { 00130 point p; 00131 int d; 00132 00133 isEmpty = 0; 00134 PF2P(GD_label(rg)->dimen, p); 00135 d = p.x - (bb.UR.x - bb.LL.x); 00136 if (d > 0) { /* height of label added below */ 00137 d /= 2; 00138 bb.LL.x -= d; 00139 bb.UR.x += d; 00140 } 00141 } 00142 00143 if (isRoot || isEmpty) 00144 margin = 0; 00145 else 00146 margin = late_int (g, G_margin, CL_OFFSET, 0); 00147 pt.x = -bb.LL.x + margin; 00148 pt.y = -bb.LL.y + margin + GD_border(rg)[BOTTOM_IX].y; 00149 bb.LL.x = 0; 00150 bb.LL.y = 0; 00151 bb.UR.x += pt.x + margin; 00152 bb.UR.y += pt.y + margin + GD_border(rg)[TOP_IX].y; 00153 00154 /* translate nodes */ 00155 if (c_cnt) { 00156 cp = cc; 00157 pp = pts; 00158 while ((cg = *cp++)) { 00159 point p; 00160 node_t *n; 00161 pointf del; 00162 00163 if (pp) { 00164 p = *pp++; 00165 p.x += pt.x; 00166 p.y += pt.y; 00167 } else { 00168 p = pt; 00169 } 00170 del.x = PS2INCH(p.x); 00171 del.y = PS2INCH(p.y); 00172 for (n = agfstnode(cg); n; n = agnxtnode(cg, n)) { 00173 ND_pos(n)[0] += del.x; 00174 ND_pos(n)[1] += del.y; 00175 } 00176 } 00177 } 00178 00179 bbf.LL.x = PS2INCH(bb.LL.x); 00180 bbf.LL.y = PS2INCH(bb.LL.y); 00181 bbf.UR.x = PS2INCH(bb.UR.x); 00182 bbf.UR.y = PS2INCH(bb.UR.y); 00183 BB(g) = bbf; 00184 00185 } 00186 00187 /* mkDeriveNode: 00188 * Constructor for a node in a derived graph. 00189 * Allocates dndata. 00190 */ 00191 static node_t *mkDeriveNode(graph_t * dg, char *name) 00192 { 00193 node_t *dn; 00194 00195 #ifndef WITH_CGRAPH 00196 dn = agnode(dg, name); 00197 #else /* WITH_CGRAPH */ 00198 dn = agnode(dg, name,1); 00199 agbindrec(dn, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE); //node custom data 00200 #endif /* WITH_CGRAPH */ 00201 ND_alg(dn) = (void *) NEW(dndata); /* free in freeDeriveNode */ 00202 ND_pos(dn) = N_GNEW(GD_ndim(dg), double); 00203 /* fprintf (stderr, "Creating %s\n", dn->name); */ 00204 return dn; 00205 } 00206 00207 static void freeDeriveNode(node_t * n) 00208 { 00209 free(ND_alg(n)); 00210 free(ND_pos(n)); 00211 #ifdef WITH_CGRAPH 00212 agdelrec(n, "Agnodeinfo_t"); 00213 #endif 00214 } 00215 00216 static void freeGData(graph_t * g) 00217 { 00218 free(GD_alg(g)); 00219 } 00220 00221 static void freeDerivedGraph(graph_t * g, graph_t ** cc) 00222 { 00223 graph_t *cg; 00224 node_t *dn; 00225 node_t *dnxt; 00226 edge_t *e; 00227 00228 while ((cg = *cc++)) { 00229 freeGData(cg); 00230 #ifdef WITH_CGRAPH 00231 agdelrec(cg, "Agraphinfo_t"); 00232 #endif 00233 } 00234 if (PORTS(g)) 00235 free(PORTS(g)); 00236 freeGData(g); 00237 #ifdef WITH_CGRAPH 00238 agdelrec(g, "Agraphinfo_t"); 00239 #endif 00240 for (dn = agfstnode(g); dn; dn = dnxt) { 00241 dnxt = agnxtnode(g, dn); 00242 for (e = agfstout(g, dn); e; e = agnxtout(g, e)) { 00243 free (ED_to_virt(e)); 00244 #ifdef WITH_CGRAPH 00245 agdelrec(e, "Agedgeinfo_t"); 00246 #endif 00247 } 00248 freeDeriveNode(dn); 00249 } 00250 agclose(g); 00251 } 00252 00253 /* evalPositions: 00254 * The input is laid out, but node coordinates 00255 * are relative to smallest containing cluster. 00256 * Walk through all nodes and clusters, translating 00257 * the positions to absolute coordinates. 00258 * Assume that when called, g's bounding box is 00259 * in absolute coordinates and that box of root graph 00260 * has LL at origin. 00261 */ 00262 static void evalPositions(graph_t * g, graph_t* rootg) 00263 { 00264 int i; 00265 graph_t *subg; 00266 node_t *n; 00267 boxf bb; 00268 boxf sbb; 00269 00270 bb = BB(g); 00271 00272 /* translate nodes in g */ 00273 if (g != rootg) { 00274 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00275 if (PARENT(n) != g) 00276 continue; 00277 ND_pos(n)[0] += bb.LL.x; 00278 ND_pos(n)[1] += bb.LL.y; 00279 } 00280 } 00281 00282 /* translate top-level clusters and recurse */ 00283 for (i = 1; i <= GD_n_cluster(g); i++) { 00284 subg = GD_clust(g)[i]; 00285 if (g != rootg) { 00286 sbb = BB(subg); 00287 sbb.LL.x += bb.LL.x; 00288 sbb.LL.y += bb.LL.y; 00289 sbb.UR.x += bb.LL.x; 00290 sbb.UR.y += bb.LL.y; 00291 BB(subg) = sbb; 00292 } 00293 evalPositions(subg, rootg); 00294 } 00295 } 00296 00297 #define CL_CHUNK 10 00298 00299 typedef struct { 00300 graph_t **cl; 00301 int sz; 00302 int cnt; 00303 } clist_t; 00304 00305 static void initCList(clist_t * clist) 00306 { 00307 clist->cl = 0; 00308 clist->sz = 0; 00309 clist->cnt = 0; 00310 } 00311 00312 /* addCluster: 00313 * Append a new cluster to the list. 00314 * NOTE: cl[0] is empty. The clusters are in cl[1..cnt]. 00315 * Normally, we increase the array when cnt == sz. 00316 * The test for cnt > sz is necessary for the first time. 00317 */ 00318 static void addCluster(clist_t * clist, graph_t * subg) 00319 { 00320 clist->cnt++; 00321 if (clist->cnt >= clist->sz) { 00322 clist->sz += CL_CHUNK; 00323 clist->cl = RALLOC(clist->sz, clist->cl, graph_t *); 00324 } 00325 clist->cl[clist->cnt] = subg; 00326 } 00327 00328 #define BSZ 1000 00329 00330 /* portName: 00331 * Generate a name for a port. 00332 * We use the name of the subgraph and names of the nodes on the edge, 00333 * if possible. Otherwise, we use the ids of the nodes. 00334 * This is for debugging. For production, just use edge id and some 00335 * id for the graph. Note that all the graphs are subgraphs of the 00336 * root graph. 00337 */ 00338 static char *portName(graph_t * g, bport_t * p) 00339 { 00340 edge_t *e = p->e; 00341 node_t *h = aghead(e); 00342 node_t *t = agtail(e); 00343 static char buf[BSZ + 1]; 00344 int len = 8; 00345 00346 len += strlen(agnameof(g)) + strlen(agnameof(h)) + strlen(agnameof(t)); 00347 if (len >= BSZ) 00348 sprintf(buf, "_port_%s_%s_%s_%ld", agnameof(g), agnameof(t), agnameof(h), 00349 (unsigned long)AGSEQ(e)); 00350 else 00351 sprintf(buf, "_port_%s_(%d)_(%d)_%ld",agnameof(g), ND_id(t), ND_id(h), 00352 (unsigned long)AGSEQ(e)); 00353 return buf; 00354 } 00355 00356 /* chkPos: 00357 * If cluster has coords attribute, use to supply initial position 00358 * of derived node. 00359 * Only called if G_coord is defined. 00360 * We also look at the parent graph's G_coord attribute. If this 00361 * is identical to the child graph, we have to assume the child 00362 * inherited it. 00363 */ 00364 static void chkPos(graph_t* g, node_t* n, layout_info* infop, boxf* bbp) 00365 { 00366 char *p; 00367 char *pp; 00368 boxf bb; 00369 char c; 00370 graph_t *parent; 00371 attrsym_t *G_coord = infop->G_coord; 00372 00373 #ifndef WITH_CGRAPH 00374 p = agxget(g, G_coord->index); 00375 #else /* WITH_CGRAPH */ 00376 p = agxget(g, G_coord); 00377 #endif /* WITH_CGRAPH */ 00378 if (p[0]) { 00379 if (g != infop->rootg) { 00380 #ifndef WITH_CGRAPH 00381 parent = agusergraph((agfstin(g->meta_node->graph, g->meta_node))->tail); 00382 pp = agxget(parent, G_coord->index); 00383 #else /* WITH_CGRAPH */ 00384 parent =agparent(g); 00385 pp = agxget(parent, G_coord); 00386 #endif /* WITH_CGRAPH */ 00387 if ((pp == p) || !strcmp(p, pp)) 00388 return; 00389 } 00390 c = '\0'; 00391 if (sscanf(p, "%lf,%lf,%lf,%lf%c", 00392 &bb.LL.x, &bb.LL.y, &bb.UR.x, &bb.UR.y, &c) >= 4) { 00393 if (PSinputscale > 0.0) { 00394 bb.LL.x /= PSinputscale; 00395 bb.LL.y /= PSinputscale; 00396 bb.UR.x /= PSinputscale; 00397 bb.UR.y /= PSinputscale; 00398 } 00399 if (c == '!') 00400 ND_pinned(n) = P_PIN; 00401 else if (c == '?') 00402 ND_pinned(n) = P_FIX; 00403 else 00404 ND_pinned(n) = P_SET; 00405 *bbp = bb; 00406 } else 00407 agerr(AGWARN, "graph %s, coord %s, expected four doubles\n", 00408 agnameof(g), p); 00409 } 00410 } 00411 00412 /* addEdge: 00413 * Add real edge e to its image de in the derived graph. 00414 * We use the to_virt and count fields to store the list. 00415 */ 00416 static void addEdge(edge_t * de, edge_t * e) 00417 { 00418 short cnt = ED_count(de); 00419 edge_t **el; 00420 00421 el = (edge_t **) (ED_to_virt(de)); 00422 el = ALLOC(cnt + 1, el, edge_t *); 00423 el[cnt] = e; 00424 ED_to_virt(de) = (edge_t *) el; 00425 ED_count(de)++; 00426 } 00427 00428 /* copyAttr: 00429 * Copy given attribute from g to dg. 00430 */ 00431 static void 00432 copyAttr (graph_t* g, graph_t* dg, char* attr) 00433 { 00434 char* ov_val; 00435 Agsym_t* ov; 00436 00437 #ifndef WITH_CGRAPH 00438 if ((ov = agfindattr(g, attr))) { 00439 ov_val = agxget(g,ov->index); 00440 ov = agfindattr(dg, attr); 00441 #else /* WITH_CGRAPH */ 00442 if ((ov = agattr(g,AGRAPH, attr, NULL))) { 00443 ov_val = agxget(g,ov); 00444 ov = agattr(dg,AGRAPH, attr, NULL); 00445 #endif /* WITH_CGRAPH */ 00446 if (ov) 00447 #ifndef WITH_CGRAPH 00448 agxset (dg, ov->index, ov_val); 00449 #else /* WITH_CGRAPH */ 00450 agxset (dg, ov, ov_val); 00451 #endif /* WITH_CGRAPH */ 00452 else 00453 #ifndef WITH_CGRAPH 00454 agraphattr(dg, attr, ov_val); 00455 #else /* WITH_CGRAPH */ 00456 agattr(dg, AGRAPH, attr, ov_val); 00457 #endif /* WITH_CGRAPH */ 00458 } 00459 } 00460 00461 /* deriveGraph: 00462 * Create derived graph of g by collapsing clusters into 00463 * nodes. An edge is created between nodes if there is 00464 * an edge between two nodes in the clusters of the base graph. 00465 * Such edges record all corresponding real edges. 00466 * In addition, we add a node and edge for each port. 00467 */ 00468 static graph_t *deriveGraph(graph_t * g, layout_info * infop) 00469 { 00470 graph_t *dg; 00471 node_t *dn; 00472 graph_t *subg; 00473 char name[100]; 00474 bport_t *pp; 00475 node_t *n; 00476 edge_t *de; 00477 int i, id = 0; 00478 00479 sprintf(name, "_dg_%d", infop->gid++); 00480 if (Verbose >= 2) 00481 #ifndef WITH_CGRAPH 00482 fprintf(stderr, "derive graph %s of %s\n", name, g->name); 00483 #else /* WITH_CGRAPH */ 00484 fprintf(stderr, "derive graph %s of %s\n", name, agnameof(g)); 00485 #endif 00486 00487 #ifndef WITH_CGRAPH 00488 dg = agopen(name, AGRAPHSTRICT); 00489 #else /* WITH_CGRAPH */ 00490 dg = agopen("derived", Agstrictdirected,NIL(Agdisc_t *)); 00491 agbindrec(dg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE); 00492 #endif /* WITH_CGRAPH */ 00493 GD_alg(dg) = (void *) NEW(gdata); /* freed in freeDeriveGraph */ 00494 #ifdef DEBUG 00495 GORIG(dg) = g; 00496 #endif 00497 GD_ndim(dg) = GD_ndim(g); 00498 00499 /* Copy attributes from g. 00500 */ 00501 copyAttr(g,dg,"overlap"); 00502 copyAttr(g,dg,"sep"); 00503 copyAttr(g,dg,"K"); 00504 00505 /* create derived nodes from clusters */ 00506 for (i = 1; i <= GD_n_cluster(g); i++) { 00507 boxf fix_bb = {{ MAXDOUBLE, MAXDOUBLE },{ -MAXDOUBLE, -MAXDOUBLE }}; 00508 subg = GD_clust(g)[i]; 00509 00510 do_graph_label(subg); 00511 dn = mkDeriveNode(dg, agnameof(subg)); 00512 ND_clust(dn) = subg; 00513 ND_id(dn) = id++; 00514 if (infop->G_coord) 00515 chkPos(subg, dn, infop, &fix_bb); 00516 for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) { 00517 DNODE(n) = dn; 00518 #ifdef UNIMPLEMENTED 00519 /* This code starts the implementation of supporting pinned nodes 00520 * within clusters. This needs more work. In particular, we may need 00521 * a separate notion of pinning related to contained nodes, which will 00522 * allow the cluster itself to wiggle. 00523 */ 00524 if (ND_pinned(n)) { 00525 fix_bb.LL.x = MIN(fix_bb.LL.x, ND_pos(n)[0]); 00526 fix_bb.LL.y = MIN(fix_bb.LL.y, ND_pos(n)[1]); 00527 fix_bb.UR.x = MAX(fix_bb.UR.x, ND_pos(n)[0]); 00528 fix_bb.UR.y = MAX(fix_bb.UR.y, ND_pos(n)[1]); 00529 ND_pinned(dn) = MAX(ND_pinned(dn), ND_pinned(n)); 00530 } 00531 #endif 00532 } 00533 if (ND_pinned(dn)) { 00534 ND_pos(dn)[0] = (fix_bb.LL.x + fix_bb.UR.x) / 2; 00535 ND_pos(dn)[1] = (fix_bb.LL.y + fix_bb.UR.y) / 2; 00536 } 00537 } 00538 00539 /* create derived nodes from remaining nodes */ 00540 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00541 if (!DNODE(n)) { 00542 if (PARENT(n) && (PARENT(n) != GPARENT(g))) { 00543 agerr (AGERR, "node \"%s\" is contained in two non-comparable clusters \"%s\" and \"%s\"\n", agnameof(n), agnameof(g), agnameof(PARENT(n))); 00544 longjmp (jbuf, 1); 00545 } 00546 PARENT(n) = g; 00547 if (IS_CLUST_NODE(n)) 00548 continue; 00549 dn = mkDeriveNode(dg, agnameof(n)); 00550 DNODE(n) = dn; 00551 ND_id(dn) = id++; 00552 ND_width(dn) = ND_width(n); 00553 ND_height(dn) = ND_height(n); 00554 ND_lw(dn) = ND_lw(n); 00555 ND_rw(dn) = ND_rw(n); 00556 ND_ht(dn) = ND_ht(n); 00557 ND_shape(dn) = ND_shape(n); 00558 ND_shape_info(dn) = ND_shape_info(n); 00559 if (ND_pinned(n)) { 00560 ND_pos(dn)[0] = ND_pos(n)[0]; 00561 ND_pos(dn)[1] = ND_pos(n)[1]; 00562 ND_pinned(dn) = ND_pinned(n); 00563 } 00564 ANODE(dn) = n; 00565 } 00566 } 00567 00568 /* add edges */ 00569 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00570 edge_t *e; 00571 node_t *hd; 00572 node_t *tl = DNODE(n); 00573 for (e = agfstout(g, n); e; e = agnxtout(g, e)) { 00574 hd = DNODE(aghead(e)); 00575 if (hd == tl) 00576 continue; 00577 if (hd > tl) 00578 #ifndef WITH_CGRAPH 00579 de = agedge(dg, tl, hd); 00580 #else /* WITH_CGRAPH */ 00581 de = agedge(dg, tl, hd, NULL,1); 00582 #endif /* WITH_CGRAPH */ 00583 else 00584 #ifndef WITH_CGRAPH 00585 de = agedge(dg, hd, tl); 00586 #else /* WITH_CGRAPH */ 00587 de = agedge(dg, hd, tl, NULL,1); 00588 agbindrec(de, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE); 00589 #endif /* WITH_CGRAPH */ 00590 ED_dist(de) = ED_dist(e); 00591 ED_factor(de) = ED_factor(e); 00592 /* fprintf (stderr, "edge %s -- %s\n", tl->name, hd->name); */ 00593 WDEG(hd)++; 00594 WDEG(tl)++; 00595 if (NEW_EDGE(de)) { 00596 DEG(hd)++; 00597 DEG(tl)++; 00598 } 00599 addEdge(de, e); 00600 } 00601 } 00602 00603 /* transform ports */ 00604 if ((pp = PORTS(g))) { 00605 bport_t *pq; 00606 node_t *m; 00607 int sz = NPORTS(g); 00608 00609 /* freed in freeDeriveGraph */ 00610 PORTS(dg) = pq = N_NEW(sz + 1, bport_t); 00611 sz = 0; 00612 while (pp->e) { 00613 m = DNODE(pp->n); 00614 /* Create port in derived graph only if hooks to internal node */ 00615 if (m) { 00616 dn = mkDeriveNode(dg, portName(g, pp)); 00617 sz++; 00618 ND_id(dn) = id++; 00619 if (dn > m) 00620 #ifndef WITH_CGRAPH 00621 de = agedge(dg, m, dn); 00622 #else /* WITH_CGRAPH */ 00623 de = agedge(dg, m, dn, NULL,1); 00624 #endif /* WITH_CGRAPH */ 00625 else 00626 #ifndef WITH_CGRAPH 00627 de = agedge(dg, dn, m); 00628 #else /* WITH_CGRAPH */ 00629 de = agedge(dg, dn, m, NULL,1); 00630 agbindrec(de, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE); 00631 #endif /* WITH_CGRAPH */ 00632 ED_dist(de) = ED_dist(pp->e); 00633 ED_factor(de) = ED_factor(pp->e); 00634 addEdge(de, pp->e); 00635 WDEG(dn)++; 00636 WDEG(m)++; 00637 DEG(dn)++; /* ports are unique, so this will be the first and */ 00638 DEG(m)++; /* only time the edge is touched. */ 00639 pq->n = dn; 00640 pq->alpha = pp->alpha; 00641 pq->e = de; 00642 pq++; 00643 } 00644 pp++; 00645 } 00646 NPORTS(dg) = sz; 00647 } 00648 00649 return dg; 00650 } 00651 00652 /* ecmp: 00653 * Sort edges by angle, then distance. 00654 */ 00655 static int ecmp(const void *v1, const void *v2) 00656 { 00657 erec *e1 = (erec *) v1; 00658 erec *e2 = (erec *) v2; 00659 if (e1->alpha > e2->alpha) 00660 return 1; 00661 else if (e1->alpha < e2->alpha) 00662 return -1; 00663 else if (e1->dist2 > e2->dist2) 00664 return 1; 00665 else if (e1->dist2 < e2->dist2) 00666 return -1; 00667 else 00668 return 0; 00669 } 00670 00671 #define ANG (M_PI/90) /* Maximum angular change: 2 degrees */ 00672 00673 /* getEdgeList: 00674 * Generate list of edges in derived graph g using 00675 * node n. The list is in counterclockwise order. 00676 * This, of course, assumes we have an initial layout for g. 00677 */ 00678 static erec *getEdgeList(node_t * n, graph_t * g) 00679 { 00680 erec *erecs; 00681 int deg = DEG(n); 00682 int i; 00683 double dx, dy; 00684 edge_t *e; 00685 node_t *m; 00686 00687 /* freed in expandCluster */ 00688 erecs = N_NEW(deg + 1, erec); 00689 i = 0; 00690 for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) { 00691 if (aghead(e) == n) 00692 m = agtail(e); 00693 else 00694 m = aghead(e); 00695 dx = ND_pos(m)[0] - ND_pos(n)[0]; 00696 dy = ND_pos(m)[1] - ND_pos(n)[1]; 00697 erecs[i].e = e; 00698 erecs[i].alpha = atan2(dy, dx); 00699 erecs[i].dist2 = dx * dx + dy * dy; 00700 i++; 00701 } 00702 assert(i == deg); 00703 qsort(erecs, deg, sizeof(erec), ecmp); 00704 00705 /* ensure no two angles are equal */ 00706 if (deg >= 2) { 00707 int j; 00708 double a, inc, delta, bnd; 00709 00710 i = 0; 00711 while (i < deg - 1) { 00712 a = erecs[i].alpha; 00713 j = i + 1; 00714 while ((j < deg) && (erecs[j].alpha == a)) 00715 j++; 00716 if (j == i + 1) 00717 i = j; 00718 else { 00719 if (j == deg) 00720 bnd = M_PI; /* all values equal up to end */ 00721 else 00722 bnd = erecs[j].alpha; 00723 delta = (bnd - a) / (j - i); 00724 if (delta > ANG) 00725 delta = ANG; 00726 inc = 0; 00727 for (; i < j; i++) { 00728 erecs[i].alpha += inc; 00729 inc += delta; 00730 } 00731 } 00732 } 00733 } 00734 00735 return erecs; 00736 } 00737 00738 /* genPorts: 00739 * Given list of edges with node n in derived graph, add corresponding 00740 * ports to port list pp, starting at index idx. Return next index. 00741 * If an edge in the derived graph corresponds to multiple real edges, 00742 * add them in order if address of n is smaller than other node address. 00743 * Otherwise, reverse order. 00744 * Attach angles. The value bnd gives next angle after er->alpha. 00745 */ 00746 static int 00747 genPorts(node_t * n, erec * er, bport_t * pp, int idx, double bnd) 00748 { 00749 node_t *other; 00750 int cnt; 00751 edge_t *e = er->e; 00752 edge_t *el; 00753 edge_t **ep; 00754 double angle, delta; 00755 int i, j, inc; 00756 00757 cnt = ED_count(e); 00758 00759 if (aghead(e) == n) 00760 other = agtail(e); 00761 else 00762 other = aghead(e); 00763 00764 delta = (bnd - er->alpha) / cnt; 00765 angle = er->alpha; 00766 if (delta > ANG) 00767 delta = ANG; 00768 00769 if (n < other) { 00770 i = idx; 00771 inc = 1; 00772 } else { 00773 i = idx + cnt - 1; 00774 inc = -1; 00775 angle += delta * (cnt - 1); 00776 delta = -delta; 00777 } 00778 00779 ep = (edge_t **) (el = ED_to_virt(e)); 00780 for (j = 0; j < ED_count(e); j++, ep++) { 00781 el = *ep; 00782 pp[i].e = el; 00783 pp[i].n = (DNODE(agtail(el)) == n ? agtail(el) : aghead(el)); 00784 pp[i].alpha = angle; 00785 i += inc; 00786 angle += delta; 00787 } 00788 return (idx + cnt); 00789 } 00790 00791 /* expandCluster; 00792 * Given positioned derived graph cg with node n which corresponds 00793 * to a cluster, generate a graph containing the interior of the 00794 * cluster, plus port information induced by the layout of cg. 00795 * Basically, we use the cluster subgraph to which n corresponds, 00796 * attached with port information. 00797 */ 00798 static graph_t *expandCluster(node_t * n, graph_t * cg) 00799 { 00800 erec *es; 00801 erec *ep; 00802 erec *next; 00803 graph_t *sg = ND_clust(n); 00804 bport_t *pp; 00805 int sz = WDEG(n); 00806 int idx = 0; 00807 double bnd; 00808 00809 if (sz != 0) { 00810 /* freed in cleanup_subgs */ 00811 pp = N_NEW(sz + 1, bport_t); 00812 00813 /* create sorted list of edges of n */ 00814 es = ep = getEdgeList(n, cg); 00815 00816 /* generate ports from edges */ 00817 while (ep->e) { 00818 next = ep + 1; 00819 if (next->e) 00820 bnd = next->alpha; 00821 else 00822 bnd = 2 * M_PI + es->alpha; 00823 idx = genPorts(n, ep, pp, idx, bnd); 00824 ep = next; 00825 } 00826 assert(idx == sz); 00827 00828 PORTS(sg) = pp; 00829 NPORTS(sg) = sz; 00830 free(es); 00831 } 00832 return sg; 00833 } 00834 00835 /* setClustNodes: 00836 * At present, cluster nodes are not assigned a position during layout, 00837 * but positioned in the center of its associated cluster. Because the 00838 * dummy edge associated with a cluster node may not occur at a sufficient 00839 * level of cluster, the edge may not be used during layout and we cannot 00840 * therefore rely find these nodes via ports. 00841 * 00842 * In this implementation, we just do a linear pass over all nodes in the 00843 * root graph. At some point, we may use a better method, like having each 00844 * cluster contain its list of cluster nodes, or have the graph keep a list. 00845 * 00846 * As nodes, we need to assign cluster nodes the coordinates in the 00847 * coordinates of its cluster p. Note that p's bbox is in its parent's 00848 * coordinates. 00849 * 00850 * If routing, we may decide to place on cluster boundary, 00851 * and use polyline. 00852 */ 00853 static void 00854 setClustNodes(graph_t* root) 00855 { 00856 boxf bb; 00857 graph_t* p; 00858 pointf ctr; 00859 node_t *n; 00860 double w, h, h_pts; 00861 double h2, w2; 00862 pointf *vertices; 00863 00864 for (n = agfstnode(root); n; n = agnxtnode(root, n)) { 00865 if (!IS_CLUST_NODE(n)) continue; 00866 00867 p = PARENT(n); 00868 bb = BB(p); /* bbox in parent cluster's coordinates */ 00869 w = bb.UR.x - bb.LL.x; 00870 h = bb.UR.y - bb.LL.y; 00871 ctr.x = w / 2.0; 00872 ctr.y = h / 2.0; 00873 w2 = INCH2PS(w / 2.0); 00874 h2 = INCH2PS(h / 2.0); 00875 h_pts = INCH2PS(h); 00876 ND_pos(n)[0] = ctr.x; 00877 ND_pos(n)[1] = ctr.y; 00878 ND_width(n) = w; 00879 ND_height(n) = h; 00880 /* ND_xsize(n) = POINTS(w); */ 00881 ND_lw(n) = ND_rw(n) = w2; 00882 ND_ht(n) = h_pts; 00883 00884 vertices = ((polygon_t *) ND_shape_info(n))->vertices; 00885 vertices[0].x = ND_rw(n); 00886 vertices[0].y = h2; 00887 vertices[1].x = -ND_lw(n); 00888 vertices[1].y = h2; 00889 vertices[2].x = -ND_lw(n); 00890 vertices[2].y = -h2; 00891 vertices[3].x = ND_rw(n); 00892 vertices[3].y = -h2; 00893 } 00894 } 00895 00896 /* layout: 00897 * Given g with ports: 00898 * Derive g' from g by reducing clusters to points (deriveGraph) 00899 * Compute connected components of g' (findCComp) 00900 * For each cc of g': 00901 * Layout cc (tLayout) 00902 * For each node n in cc of g' <-> cluster c in g: 00903 * Add ports based on layout of cc to get c' (expandCluster) 00904 * Layout c' (recursion) 00905 * Remove ports from cc 00906 * Expand nodes of cc to reflect size of c' (xLayout) 00907 * Pack connected components to get layout of g (putGraphs) 00908 * Translate layout so that bounding box of layout + margin 00909 * has the origin as LL corner. 00910 * Set position of top level clusters and real nodes. 00911 * Set bounding box of graph 00912 * 00913 * TODO: 00914 * 00915 * Possibly should modify so that only do connected components 00916 * on top-level derived graph. Unconnected parts of a cluster 00917 * could just rattle within cluster boundaries. This may mix 00918 * up components but give a tighter packing. 00919 * 00920 * Add edges per components to get better packing, rather than 00921 * wait until the end. 00922 */ 00923 static 00924 void layout(graph_t * g, layout_info * infop) 00925 { 00926 point *pts = NULL; 00927 graph_t *dg; 00928 node_t *dn; 00929 node_t *n; 00930 graph_t *cg; 00931 graph_t *sg; 00932 graph_t **cc; 00933 graph_t **pg; 00934 int c_cnt; 00935 int pinned; 00936 xparams xpms; 00937 00938 #ifdef DEBUG 00939 incInd(); 00940 #endif 00941 if (Verbose) { 00942 #ifdef DEBUG 00943 prIndent(); 00944 #endif 00945 fprintf (stderr, "layout %s\n", agnameof(g)); 00946 } 00947 /* initialize derived node pointers */ 00948 for (n = agfstnode(g); n; n = agnxtnode(g, n)) 00949 DNODE(n) = 0; 00950 00951 dg = deriveGraph(g, infop); 00952 cc = pg = findCComp(dg, &c_cnt, &pinned); 00953 00954 while ((cg = *pg++)) { 00955 fdp_tLayout(cg, &xpms); 00956 for (n = agfstnode(cg); n; n = agnxtnode(cg, n)) { 00957 if (ND_clust(n)) { 00958 pointf pt; 00959 sg = expandCluster(n, cg); /* attach ports to sg */ 00960 layout(sg, infop); 00961 /* bb.LL == origin */ 00962 ND_width(n) = BB(sg).UR.x; 00963 ND_height(n) = BB(sg).UR.y; 00964 pt.x = POINTS_PER_INCH * BB(sg).UR.x; 00965 pt.y = POINTS_PER_INCH * BB(sg).UR.y; 00966 ND_rw(n) = ND_lw(n) = pt.x/2; 00967 ND_ht(n) = pt.y; 00968 } else if (IS_PORT(n)) 00969 agdelete(cg, n); /* remove ports from component */ 00970 } 00971 00972 /* Remove overlaps */ 00973 if (agnnodes(cg) >= 2) { 00974 if (g == infop->rootg) 00975 normalize (cg); 00976 fdp_xLayout(cg, &xpms); 00977 } 00978 /* set bounding box but don't use ports */ 00979 /* setBB (cg); */ 00980 } 00981 00982 /* At this point, each connected component has its nodes correctly 00983 * positioned. If we have multiple components, we pack them together. 00984 * All nodes will be moved to their new positions. 00985 * NOTE: packGraphs uses nodes in components, so if port nodes are 00986 * not removed, it won't work. 00987 */ 00988 /* Handle special cases well: no ports to real internal nodes 00989 * Place cluster edges separately, after layout. 00990 * How to combine parts, especially with disparate components? 00991 */ 00992 if (c_cnt > 1) { 00993 boolean *bp; 00994 if (pinned) { 00995 bp = N_NEW(c_cnt, boolean); 00996 bp[0] = TRUE; 00997 } else 00998 bp = 0; 00999 infop->pack.fixed = bp; 01000 pts = putGraphs(c_cnt, cc, NULL, &infop->pack); 01001 if (bp) 01002 free(bp); 01003 } else { 01004 pts = NULL; 01005 if (c_cnt == 1) 01006 compute_bb(cc[0]); 01007 } 01008 01009 /* set bounding box of dg and reposition nodes */ 01010 finalCC(dg, c_cnt, cc, pts, g, infop); 01011 free (pts); 01012 01013 /* record positions from derived graph to input graph */ 01014 /* At present, this does not record port node info */ 01015 /* In fact, as noted above, we have removed port nodes */ 01016 for (dn = agfstnode(dg); dn; dn = agnxtnode(dg, dn)) { 01017 if ((sg = ND_clust(dn))) { 01018 BB(sg).LL.x = ND_pos(dn)[0] - ND_width(dn) / 2; 01019 BB(sg).LL.y = ND_pos(dn)[1] - ND_height(dn) / 2; 01020 BB(sg).UR.x = BB(sg).LL.x + ND_width(dn); 01021 BB(sg).UR.y = BB(sg).LL.y + ND_height(dn); 01022 } else if ((n = ANODE(dn))) { 01023 ND_pos(n)[0] = ND_pos(dn)[0]; 01024 ND_pos(n)[1] = ND_pos(dn)[1]; 01025 } 01026 } 01027 BB(g) = BB(dg); 01028 #ifdef DEBUG 01029 if (g == infop->rootg) 01030 dump(g, 1, 0); 01031 #endif 01032 01033 /* clean up temp graphs */ 01034 freeDerivedGraph(dg, cc); 01035 free(cc); 01036 if (Verbose) { 01037 #ifdef DEBUG 01038 prIndent (); 01039 #endif 01040 fprintf (stderr, "end %s\n", agnameof(g)); 01041 } 01042 #ifdef DEBUG 01043 decInd(); 01044 #endif 01045 } 01046 01047 /* setBB; 01048 * Set point box g->bb from inch box BB(g). 01049 */ 01050 static void setBB(graph_t * g) 01051 { 01052 int i; 01053 boxf bb; 01054 01055 bb.LL.x = POINTS_PER_INCH * BB(g).LL.x; 01056 bb.LL.y = POINTS_PER_INCH * BB(g).LL.y; 01057 bb.UR.x = POINTS_PER_INCH * BB(g).UR.x; 01058 bb.UR.y = POINTS_PER_INCH * BB(g).UR.y; 01059 GD_bb(g) = bb; 01060 for (i = 1; i <= GD_n_cluster(g); i++) { 01061 setBB(GD_clust(g)[i]); 01062 } 01063 } 01064 01065 /* init_info: 01066 * Initialize graph-dependent information and 01067 * state variable.s 01068 */ 01069 void init_info(graph_t * g, layout_info * infop) 01070 { 01071 #ifndef WITH_CGRAPH 01072 infop->G_coord = agfindattr(g, "coords"); 01073 infop->G_width = agfindattr(g, "width"); 01074 infop->G_height = agfindattr(g, "height"); 01075 #else /* WITH_CGRAPH */ 01076 infop->G_coord = agattr(g,AGRAPH, "coords", NULL); 01077 infop->G_width = agattr(g,AGRAPH, "width", NULL); 01078 infop->G_height = agattr(g, AGRAPH,"height", NULL); 01079 #endif /* WITH_CGRAPH */ 01080 infop->rootg = g; 01081 infop->gid = 0; 01082 infop->pack.mode = getPackInfo(g, l_node, CL_OFFSET / 2, &(infop->pack)); 01083 } 01084 01085 /* mkClusters: 01086 * Attach list of immediate child clusters. 01087 * NB: By convention, the indexing starts at 1. 01088 * If pclist is NULL, the graph is the root graph or a cluster 01089 * If pclist is non-NULL, we are recursively scanning a non-cluster 01090 * subgraph for cluster children. 01091 */ 01092 static void 01093 mkClusters (graph_t * g, clist_t* pclist, graph_t* parent) 01094 { 01095 #ifndef WITH_CGRAPH 01096 node_t* mn; 01097 edge_t* me; 01098 graph_t* mg; 01099 #endif /* WITH_CGRAPH */ 01100 graph_t* subg; 01101 clist_t list; 01102 clist_t* clist; 01103 01104 if (pclist == NULL) { 01105 clist = &list; 01106 initCList(clist); 01107 } 01108 else 01109 clist = pclist; 01110 #ifndef WITH_CGRAPH 01111 mg = g->meta_node->graph; 01112 for (me = agfstout(mg, g->meta_node); me; me = agnxtout(mg, me)) { 01113 mn = me->head; 01114 subg = agusergraph(mn); 01115 if (!strncmp(subg->name, "cluster", 7)) { 01116 #else /* WITH_CGRAPH */ 01117 01118 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) 01119 { 01120 if (!strncmp(agnameof(subg), "cluster", 7)) { 01121 agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE); 01122 #endif /* WITH_CGRAPH */ 01123 GD_alg(subg) = (void *) NEW(gdata); /* freed in cleanup_subgs */ 01124 GD_ndim(subg) = GD_ndim(parent); 01125 LEVEL(subg) = LEVEL(parent) + 1; 01126 GPARENT(subg) = parent; 01127 addCluster(clist, subg); 01128 mkClusters(subg, NULL, subg); 01129 } 01130 else { 01131 mkClusters(subg, clist, parent); 01132 } 01133 } 01134 if (pclist == NULL) { 01135 GD_n_cluster(g) = list.cnt; 01136 if (list.cnt) 01137 GD_clust(g) = RALLOC(list.cnt + 1, list.cl, graph_t*); 01138 } 01139 } 01140 01141 void fdp_init_graph(Agraph_t * g) 01142 { 01143 setEdgeType (g, ET_LINE); 01144 GD_alg(g) = (void *) NEW(gdata); /* freed in cleanup_graph */ 01145 #ifndef WITH_CGRAPH 01146 g->u.ndim = late_int(g, agfindattr(g, "dim"), 2, 2); 01147 Ndim = g->u.ndim = MIN(g->u.ndim, MAXDIM); 01148 #else /* WITH_CGRAPH */ 01149 GD_ndim(g) = late_int(g, agattr(g,AGRAPH, "dim", NULL), 2, 2); 01150 Ndim = GD_ndim(g) = MIN(GD_ndim(g), MAXDIM); 01151 #endif /* WITH_CGRAPH */ 01152 01153 mkClusters (g, NULL, g); 01154 fdp_initParams(g); 01155 fdp_init_node_edge(g); 01156 } 01157 01158 void fdpLayout(graph_t * g) 01159 { 01160 layout_info info; 01161 01162 init_info(g, &info); 01163 layout(g, &info); 01164 setClustNodes(g); 01165 evalPositions(g,g); 01166 01167 /* Set bbox info for g and all clusters. This is needed for 01168 * spline drawing. We already know the graph bbox is at the origin. 01169 * (We pass the origin to spline_edges0. This really isn't true, 01170 * as the algorithm has done many translations.) 01171 * On return from spline drawing, all bounding boxes should be correct. 01172 */ 01173 setBB(g); 01174 } 01175 01176 static void 01177 fdpSplines (graph_t * g) 01178 { 01179 int trySplines = 0; 01180 int et = EDGE_TYPE(g); 01181 01182 if (et != ET_LINE) { 01183 if (et == ET_COMPOUND) { 01184 trySplines = splineEdges(g, compoundEdges, ET_SPLINE); 01185 /* When doing the edges again, accept edges done by compoundEdges */ 01186 if (trySplines) 01187 Nop = 2; 01188 } 01189 if (trySplines || (et != ET_COMPOUND)) { 01190 if (HAS_CLUST_EDGE(g)) { 01191 agerr(AGWARN, 01192 "splines and cluster edges not supported - using line segments\n"); 01193 } else { 01194 spline_edges1(g, et); 01195 } 01196 } 01197 Nop = 0; 01198 } 01199 if (State < GVSPLINES) 01200 spline_edges1(g, ET_LINE); 01201 } 01202 01203 void fdp_layout(graph_t * g) 01204 { 01205 /* Agnode_t* n; */ 01206 01207 fdp_init_graph(g); 01208 if (setjmp(jbuf)) { 01209 return; 01210 } 01211 fdpLayout(g); 01212 #if 0 01213 /* free ND_alg field so it can be used in spline routing */ 01214 if ((n = agfstnode(g))) 01215 free(ND_alg(n)); 01216 #endif 01217 neato_set_aspect(g); 01218 01219 if (EDGE_TYPE(g) != ET_NONE) fdpSplines (g); 01220 01221 dotneato_postprocess(g); 01222 }
1.7.5