|
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 /* 00016 * position(g): set ND_coord(n) (x and y) for all nodes n of g, using GD_rank(g). 00017 * (the graph may be modified by merging certain edges with a common endpoint.) 00018 * the coordinates are computed by constructing and ranking an auxiliary graph. 00019 * then leaf nodes are inserted in the fast graph. cluster boundary nodes are 00020 * created and correctly separated. 00021 */ 00022 00023 #include "dot.h" 00024 #include "aspect.h" 00025 00026 static int nsiter2(graph_t * g); 00027 static void create_aux_edges(graph_t * g); 00028 static void remove_aux_edges(graph_t * g); 00029 static void set_xcoords(graph_t * g); 00030 static void set_ycoords(graph_t * g); 00031 static void set_aspect(graph_t * g, aspect_t* ); 00032 static void expand_leaves(graph_t * g); 00033 static void make_lrvn(graph_t * g); 00034 static void contain_nodes(graph_t * g); 00035 static boolean idealsize(graph_t * g, double); 00036 00037 #ifdef DEBUG 00038 static void 00039 dumpNS (graph_t * g) 00040 { 00041 node_t* n = GD_nlist(g); 00042 elist el; 00043 edge_t* e; 00044 int i; 00045 00046 while (n) { 00047 el = ND_out(n); 00048 for (i = 0; i < el.size; i++) { 00049 e = el.list[i]; 00050 fprintf (stderr, "%s(%x) -> %s(%x) : %d\n", agtail(e)->name,agtail(e), aghead(e)->name, aghead(e), 00051 ED_minlen(e)); 00052 } 00053 n = ND_next(n); 00054 } 00055 } 00056 #endif 00057 00058 static double 00059 largeMinlen (double l) 00060 { 00061 agerr (AGERR, "Edge length %f larger than maximum %u allowed.\nCheck for overwide node(s).\n", l, USHRT_MAX); 00062 return (double)USHRT_MAX; 00063 } 00064 00065 /* connectGraph: 00066 * When source and/or sink nodes are defined, it is possible that 00067 * after the auxiliary edges are added, the graph may still have 2 or 00068 * 3 components. To fix this, we put trivial constraints connecting the 00069 * first items of each rank. 00070 */ 00071 static void 00072 connectGraph (graph_t* g) 00073 { 00074 int i, j, r, found; 00075 node_t* tp; 00076 node_t* hp; 00077 node_t* sn; 00078 edge_t* e; 00079 rank_t* rp; 00080 00081 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { 00082 rp = GD_rank(g)+r; 00083 found =FALSE; 00084 tp = NULL; 00085 for (i = 0; i < rp->n; i++) { 00086 tp = rp->v[i]; 00087 if (ND_save_out(tp).list) { 00088 for (j = 0; (e = ND_save_out(tp).list[j]); j++) { 00089 if ((ND_rank(aghead(e)) > r) || (ND_rank(agtail(e)) > r)) { 00090 found = TRUE; 00091 break; 00092 } 00093 } 00094 if (found) break; 00095 } 00096 if (ND_save_in(tp).list) { 00097 for (j = 0; (e = ND_save_in(tp).list[j]); j++) { 00098 if ((ND_rank(agtail(e)) > r) || (ND_rank(aghead(e)) > r)) { 00099 found = TRUE; 00100 break; 00101 } 00102 } 00103 if (found) break; 00104 } 00105 } 00106 if (found || !tp) continue; 00107 tp = rp->v[0]; 00108 if (r < GD_maxrank(g)) hp = (rp+1)->v[0]; 00109 else hp = (rp-1)->v[0]; 00110 assert (hp); 00111 sn = virtual_node(g); 00112 ND_node_type(sn) = SLACKNODE; 00113 make_aux_edge(sn, tp, 0, 0); 00114 make_aux_edge(sn, hp, 0, 0); 00115 ND_rank(sn) = MIN(ND_rank(tp), ND_rank(hp)); 00116 } 00117 } 00118 00119 void dot_position(graph_t * g, aspect_t* asp) 00120 { 00121 if (GD_nlist(g) == NULL) 00122 return; /* ignore empty graph */ 00123 mark_lowclusters(g); /* we could remove from splines.c now */ 00124 set_ycoords(g); 00125 if (Concentrate) 00126 dot_concentrate(g); 00127 expand_leaves(g); 00128 if (flat_edges(g)) 00129 set_ycoords(g); 00130 create_aux_edges(g); 00131 if (rank(g, 2, nsiter2(g))) { /* LR balance == 2 */ 00132 connectGraph (g); 00133 assert(rank(g, 2, nsiter2(g)) == 0); 00134 } 00135 set_xcoords(g); 00136 set_aspect(g, asp); 00137 remove_aux_edges(g); /* must come after set_aspect since we now 00138 * use GD_ln and GD_rn for bbox width. 00139 */ 00140 } 00141 00142 static int nsiter2(graph_t * g) 00143 { 00144 int maxiter = INT_MAX; 00145 char *s; 00146 00147 if ((s = agget(g, "nslimit"))) 00148 maxiter = atof(s) * agnnodes(g); 00149 return maxiter; 00150 } 00151 00152 static int go(node_t * u, node_t * v) 00153 { 00154 int i; 00155 edge_t *e; 00156 00157 if (u == v) 00158 return TRUE; 00159 for (i = 0; (e = ND_out(u).list[i]); i++) { 00160 if (go(aghead(e), v)) 00161 return TRUE; 00162 } 00163 return FALSE; 00164 } 00165 00166 static int canreach(node_t * u, node_t * v) 00167 { 00168 return go(u, v); 00169 } 00170 00171 edge_t *make_aux_edge(node_t * u, node_t * v, double len, int wt) 00172 { 00173 edge_t *e; 00174 00175 #ifndef WITH_CGRAPH 00176 e = NEW(edge_t); 00177 #else 00178 Agedgepair_t* e2 = NEW(Agedgepair_t); 00179 AGTYPE(&(e2->in)) = AGINEDGE; 00180 AGTYPE(&(e2->out)) = AGOUTEDGE; 00181 e2->out.base.data = (Agrec_t*)NEW(Agedgeinfo_t); 00182 e = &(e2->out); 00183 #endif /* WITH_CGRAPH */ 00184 00185 agtail(e) = u; 00186 aghead(e) = v; 00187 if (len > USHRT_MAX) 00188 len = largeMinlen (len); 00189 ED_minlen(e) = ROUND(len); 00190 ED_weight(e) = wt; 00191 fast_edge(e); 00192 return e; 00193 } 00194 00195 static void allocate_aux_edges(graph_t * g) 00196 { 00197 int i, j, n_in; 00198 node_t *n; 00199 00200 /* allocate space for aux edge lists */ 00201 for (n = GD_nlist(g); n; n = ND_next(n)) { 00202 ND_save_in(n) = ND_in(n); 00203 ND_save_out(n) = ND_out(n); 00204 for (i = 0; ND_out(n).list[i]; i++); 00205 for (j = 0; ND_in(n).list[j]; j++); 00206 n_in = i + j; 00207 alloc_elist(n_in + 3, ND_in(n)); 00208 alloc_elist(3, ND_out(n)); 00209 } 00210 } 00211 00212 /* make_LR_constraints: 00213 */ 00214 static void 00215 make_LR_constraints(graph_t * g) 00216 { 00217 int i, j, k; 00218 int sw; /* self width */ 00219 int m0, m1; 00220 double width; 00221 int sep[2]; 00222 int nodesep; /* separation between nodes on same rank */ 00223 edge_t *e, *e0, *e1, *ff; 00224 node_t *u, *v, *t0, *h0; 00225 rank_t *rank = GD_rank(g); 00226 00227 /* Use smaller separation on odd ranks if g has edge labels */ 00228 if (GD_has_labels(g) & EDGE_LABEL) { 00229 sep[0] = GD_nodesep(g); 00230 sep[1] = 5; 00231 } 00232 else { 00233 sep[1] = sep[0] = GD_nodesep(g); 00234 } 00235 /* make edges to constrain left-to-right ordering */ 00236 for (i = GD_minrank(g); i <= GD_maxrank(g); i++) { 00237 double last; 00238 last = ND_rank(rank[i].v[0]) = 0; 00239 nodesep = sep[i & 1]; 00240 for (j = 0; j < rank[i].n; j++) { 00241 u = rank[i].v[j]; 00242 ND_mval(u) = ND_rw(u); /* keep it somewhere safe */ 00243 if (ND_other(u).size > 0) { /* compute self size */ 00244 /* FIX: dot assumes all self-edges go to the right. This 00245 * is no longer true, though makeSelfEdge still attempts to 00246 * put as many as reasonable on the right. The dot code 00247 * should be modified to allow a box reflecting the placement 00248 * of all self-edges, and use that to reposition the nodes. 00249 * Note that this would not only affect left and right 00250 * positioning but may also affect interrank spacing. 00251 */ 00252 sw = 0; 00253 for (k = 0; (e = ND_other(u).list[k]); k++) { 00254 if (agtail(e) == aghead(e)) { 00255 sw += selfRightSpace (e); 00256 } 00257 } 00258 ND_rw(u) += sw; /* increment to include self edges */ 00259 } 00260 v = rank[i].v[j + 1]; 00261 if (v) { 00262 width = ND_rw(u) + ND_lw(v) + nodesep; 00263 e0 = make_aux_edge(u, v, width, 0); 00264 last = (ND_rank(v) = last + width); 00265 } 00266 00267 /* constraints from labels of flat edges on previous rank */ 00268 if ((e = (edge_t*)ND_alg(u))) { 00269 e0 = ND_save_out(u).list[0]; 00270 e1 = ND_save_out(u).list[1]; 00271 if (ND_order(aghead(e0)) > ND_order(aghead(e1))) { 00272 ff = e0; 00273 e0 = e1; 00274 e1 = ff; 00275 } 00276 m0 = (ED_minlen(e) * GD_nodesep(g)) / 2; 00277 m1 = m0 + ND_rw(aghead(e0)) + ND_lw(agtail(e0)); 00278 /* these guards are needed because the flat edges 00279 * work very poorly with cluster layout */ 00280 if (canreach(agtail(e0), aghead(e0)) == FALSE) 00281 make_aux_edge(aghead(e0), agtail(e0), m1, 00282 ED_weight(e)); 00283 m1 = m0 + ND_rw(agtail(e1)) + ND_lw(aghead(e1)); 00284 if (canreach(aghead(e1), agtail(e1)) == FALSE) 00285 make_aux_edge(agtail(e1), aghead(e1), m1, 00286 ED_weight(e)); 00287 } 00288 00289 /* position flat edge endpoints */ 00290 for (k = 0; k < ND_flat_out(u).size; k++) { 00291 e = ND_flat_out(u).list[k]; 00292 if (ND_order(agtail(e)) < ND_order(aghead(e))) { 00293 t0 = agtail(e); 00294 h0 = aghead(e); 00295 } else { 00296 t0 = aghead(e); 00297 h0 = agtail(e); 00298 } 00299 00300 width = ND_rw(t0) + ND_lw(h0); 00301 m0 = ED_minlen(e) * GD_nodesep(g) + width; 00302 00303 if ((e0 = find_fast_edge(t0, h0))) { 00304 /* flat edge between adjacent neighbors 00305 * ED_dist contains the largest label width. 00306 */ 00307 m0 = MAX(m0, width + GD_nodesep(g) + ROUND(ED_dist(e))); 00308 if (m0 > USHRT_MAX) 00309 m0 = largeMinlen (m0); 00310 ED_minlen(e0) = MAX(ED_minlen(e0), m0); 00311 } 00312 else if (!ED_label(e)) { 00313 /* unlabeled flat edge between non-neighbors 00314 * ED_minlen(e) is max of ED_minlen of all equivalent 00315 * edges. 00316 */ 00317 make_aux_edge(t0, h0, m0, ED_weight(e)); 00318 } 00319 /* labeled flat edges between non-neighbors have already 00320 * been constrained by the label above. 00321 */ 00322 } 00323 } 00324 } 00325 } 00326 00327 /* make_edge_pairs: make virtual edge pairs corresponding to input edges */ 00328 static void make_edge_pairs(graph_t * g) 00329 { 00330 int i, m0, m1; 00331 node_t *n, *sn; 00332 edge_t *e; 00333 00334 for (n = GD_nlist(g); n; n = ND_next(n)) { 00335 if (ND_save_out(n).list) 00336 for (i = 0; (e = ND_save_out(n).list[i]); i++) { 00337 sn = virtual_node(g); 00338 ND_node_type(sn) = SLACKNODE; 00339 m0 = (ED_head_port(e).p.x - ED_tail_port(e).p.x); 00340 if (m0 > 0) 00341 m1 = 0; 00342 else { 00343 m1 = -m0; 00344 m0 = 0; 00345 } 00346 #ifdef NOTDEF 00347 /* was trying to improve LR balance */ 00348 if ((ND_save_out(n).size % 2 == 0) 00349 && (i == ND_save_out(n).size / 2 - 1)) { 00350 node_t *u = ND_save_out(n).list[i]->head; 00351 node_t *v = ND_save_out(n).list[i + 1]->head; 00352 double width = ND_rw(u) + ND_lw(v) + GD_nodesep(g); 00353 m0 = width / 2 - 1; 00354 } 00355 #endif 00356 make_aux_edge(sn, agtail(e), m0 + 1, ED_weight(e)); 00357 make_aux_edge(sn, aghead(e), m1 + 1, ED_weight(e)); 00358 ND_rank(sn) = 00359 MIN(ND_rank(agtail(e)) - m0 - 1, 00360 ND_rank(aghead(e)) - m1 - 1); 00361 } 00362 } 00363 } 00364 00365 static void contain_clustnodes(graph_t * g) 00366 { 00367 int c; 00368 edge_t *e; 00369 00370 if (g != agroot(g)) { 00371 contain_nodes(g); 00372 if ((e = find_fast_edge(GD_ln(g),GD_rn(g)))) /* maybe from lrvn()?*/ 00373 ED_weight(e) += 128; 00374 else 00375 make_aux_edge(GD_ln(g), GD_rn(g), 1, 128); /* clust compaction edge */ 00376 } 00377 for (c = 1; c <= GD_n_cluster(g); c++) 00378 contain_clustnodes(GD_clust(g)[c]); 00379 } 00380 00381 static int vnode_not_related_to(graph_t * g, node_t * v) 00382 { 00383 edge_t *e; 00384 00385 if (ND_node_type(v) != VIRTUAL) 00386 return FALSE; 00387 for (e = ND_save_out(v).list[0]; ED_to_orig(e); e = ED_to_orig(e)); 00388 if (agcontains(g, agtail(e))) 00389 return FALSE; 00390 if (agcontains(g, aghead(e))) 00391 return FALSE; 00392 return TRUE; 00393 } 00394 00395 /* keepout_othernodes: 00396 * Guarantee nodes outside the cluster g are placed outside of it. 00397 * This is done by adding constraints to make sure such nodes have 00398 * a gap of margin from the left or right bounding box node ln or rn. 00399 * 00400 * We could probably reduce some of these constraints by checking if 00401 * the node is in a cluster, since elsewhere we make constrain a 00402 * separate between clusters. Also, we should be able to skip the 00403 * first loop if g is the root graph. 00404 */ 00405 static void keepout_othernodes(graph_t * g) 00406 { 00407 int i, c, r, margin; 00408 node_t *u, *v; 00409 00410 margin = late_int (g, G_margin, CL_OFFSET, 0); 00411 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { 00412 if (GD_rank(g)[r].n == 0) 00413 continue; 00414 v = GD_rank(g)[r].v[0]; 00415 if (v == NULL) 00416 continue; 00417 for (i = ND_order(v) - 1; i >= 0; i--) { 00418 u = GD_rank(agroot(g))[r].v[i]; 00419 /* can't use "is_a_vnode_of" because elists are swapped */ 00420 if ((ND_node_type(u) == NORMAL) || vnode_not_related_to(g, u)) { 00421 make_aux_edge(u, GD_ln(g), margin + ND_rw(u), 0); 00422 break; 00423 } 00424 } 00425 for (i = ND_order(v) + GD_rank(g)[r].n; i < GD_rank(agroot(g))[r].n; 00426 i++) { 00427 u = GD_rank(agroot(g))[r].v[i]; 00428 if ((ND_node_type(u) == NORMAL) || vnode_not_related_to(g, u)) { 00429 make_aux_edge(GD_rn(g), u, margin + ND_lw(u), 0); 00430 break; 00431 } 00432 } 00433 } 00434 00435 for (c = 1; c <= GD_n_cluster(g); c++) 00436 keepout_othernodes(GD_clust(g)[c]); 00437 } 00438 00439 /* contain_subclust: 00440 * Make sure boxes of subclusters of g are offset from the 00441 * box of g. This is done by a constraint between the left and 00442 * right bounding box nodes ln and rn of g and a subcluster. 00443 * The gap needs to include any left or right labels. 00444 */ 00445 static void contain_subclust(graph_t * g) 00446 { 00447 int margin, c; 00448 graph_t *subg; 00449 00450 margin = late_int (g, G_margin, CL_OFFSET, 0); 00451 make_lrvn(g); 00452 for (c = 1; c <= GD_n_cluster(g); c++) { 00453 subg = GD_clust(g)[c]; 00454 make_lrvn(subg); 00455 make_aux_edge(GD_ln(g), GD_ln(subg), 00456 margin + GD_border(g)[LEFT_IX].x, 0); 00457 make_aux_edge(GD_rn(subg), GD_rn(g), 00458 margin + GD_border(g)[RIGHT_IX].x, 0); 00459 contain_subclust(subg); 00460 } 00461 } 00462 00463 /* separate_subclust: 00464 * Guarantee space between subcluster of g. 00465 * This is done by adding a constraint between the right bbox node rn 00466 * of the left cluster and the left bbox node ln of the right cluster. 00467 * This is only done if the two clusters overlap in some rank. 00468 */ 00469 static void separate_subclust(graph_t * g) 00470 { 00471 int i, j, margin; 00472 graph_t *low, *high; 00473 graph_t *left, *right; 00474 00475 margin = late_int (g, G_margin, CL_OFFSET, 0); 00476 for (i = 1; i <= GD_n_cluster(g); i++) 00477 make_lrvn(GD_clust(g)[i]); 00478 for (i = 1; i <= GD_n_cluster(g); i++) { 00479 for (j = i + 1; j <= GD_n_cluster(g); j++) { 00480 low = GD_clust(g)[i]; 00481 high = GD_clust(g)[j]; 00482 if (GD_minrank(low) > GD_minrank(high)) { 00483 graph_t *temp = low; 00484 low = high; 00485 high = temp; 00486 } 00487 if (GD_maxrank(low) < GD_minrank(high)) 00488 continue; 00489 if (ND_order(GD_rank(low)[GD_minrank(high)].v[0]) 00490 < ND_order(GD_rank(high)[GD_minrank(high)].v[0])) { 00491 left = low; 00492 right = high; 00493 } else { 00494 left = high; 00495 right = low; 00496 } 00497 make_aux_edge(GD_rn(left), GD_ln(right), margin, 0); 00498 } 00499 separate_subclust(GD_clust(g)[i]); 00500 } 00501 } 00502 00503 /* pos_clusters: create constraints for: 00504 * node containment in clusters, 00505 * cluster containment in clusters, 00506 * separation of sibling clusters. 00507 */ 00508 static void pos_clusters(graph_t * g) 00509 { 00510 if (GD_n_cluster(g) > 0) { 00511 contain_clustnodes(g); 00512 keepout_othernodes(g); 00513 contain_subclust(g); 00514 separate_subclust(g); 00515 } 00516 } 00517 00518 static void compress_graph(graph_t * g) 00519 { 00520 double x; 00521 pointf p; 00522 00523 if (GD_drawing(g)->ratio_kind != R_COMPRESS) 00524 return; 00525 p = GD_drawing(g)->size; 00526 if (p.x * p.y <= 1) 00527 return; 00528 contain_nodes(g); 00529 if (GD_flip(g) == FALSE) 00530 x = p.x; 00531 else 00532 x = p.y; 00533 00534 /* Guard against huge size attribute since max. edge length is USHRT_MAX 00535 * A warning might be called for. Also, one could check that the graph 00536 * already fits GD_drawing(g)->size and return immediately. 00537 */ 00538 x = MIN(x,USHRT_MAX); 00539 make_aux_edge(GD_ln(g), GD_rn(g), x, 1000); 00540 } 00541 00542 static void create_aux_edges(graph_t * g) 00543 { 00544 allocate_aux_edges(g); 00545 make_LR_constraints(g); 00546 make_edge_pairs(g); 00547 pos_clusters(g); 00548 compress_graph(g); 00549 } 00550 00551 static void remove_aux_edges(graph_t * g) 00552 { 00553 int i; 00554 node_t *n, *nnext, *nprev; 00555 edge_t *e; 00556 00557 for (n = GD_nlist(g); n; n = ND_next(n)) { 00558 for (i = 0; (e = ND_out(n).list[i]); i++) { 00559 #ifdef WITH_CGRAPH 00560 free(e->base.data); 00561 #endif 00562 free(e); 00563 } 00564 free_list(ND_out(n)); 00565 free_list(ND_in(n)); 00566 ND_out(n) = ND_save_out(n); 00567 ND_in(n) = ND_save_in(n); 00568 } 00569 /* cannot be merged with previous loop */ 00570 nprev = NULL; 00571 for (n = GD_nlist(g); n; n = nnext) { 00572 nnext = ND_next(n); 00573 if (ND_node_type(n) == SLACKNODE) { 00574 if (nprev) 00575 ND_next(nprev) = nnext; 00576 else 00577 GD_nlist(g) = nnext; 00578 #ifdef WITH_CGRAPH 00579 free(n->base.data); 00580 #endif 00581 free(n); 00582 } else 00583 nprev = n; 00584 } 00585 ND_prev(GD_nlist(g)) = NULL; 00586 } 00587 00588 /* set_xcoords: 00589 * Set x coords of nodes. 00590 */ 00591 static void 00592 set_xcoords(graph_t * g) 00593 { 00594 int i, j; 00595 node_t *v; 00596 rank_t *rank = GD_rank(g); 00597 00598 for (i = GD_minrank(g); i <= GD_maxrank(g); i++) { 00599 for (j = 0; j < rank[i].n; j++) { 00600 v = rank[i].v[j]; 00601 ND_coord(v).x = ND_rank(v); 00602 ND_rank(v) = i; 00603 } 00604 } 00605 } 00606 00607 /* adjustEqual: 00608 * Expand cluster g vertically by delta, assuming ranks 00609 * are equally spaced. We first try to split delta evenly 00610 * using any available space at the top and bottom. If there 00611 * is not enough, we have to widen the space between the ranks. 00612 * We divide delta equally within the ranks of g plus its ht1 00613 * and ht2. To preserve equality of ranks, we add this space 00614 * between every pair of ranks. 00615 * 00616 * There is probably some way to add less than delta, by using 00617 * whatever available space there is at top and bottom, but for 00618 * now, trying to figure that out seems more trouble than it is worth. 00619 */ 00620 static void adjustEqual(graph_t * g, int delta) 00621 { 00622 int r, avail, half, deltop, delbottom; 00623 graph_t *root = agroot(g); 00624 rank_t *rank = GD_rank(root); 00625 int maxr = GD_maxrank(g); 00626 int minr = GD_minrank(g); 00627 00628 deltop = rank[minr].ht2 - GD_ht2(g); 00629 delbottom = rank[maxr].ht1 - GD_ht1(g); 00630 avail = deltop + delbottom; 00631 if (avail >= delta) { 00632 half = (delta+1) / 2; 00633 if (deltop <= delbottom) { 00634 if (half <= deltop) { 00635 GD_ht2(g) += half; 00636 GD_ht1(g) += (delta - half); 00637 } 00638 else { 00639 GD_ht2(g) += deltop; 00640 GD_ht1(g) += (delta - deltop); 00641 } 00642 } 00643 else { 00644 if (half <= delbottom) { 00645 GD_ht1(g) += half; 00646 GD_ht2(g) += (delta - half); 00647 } 00648 else { 00649 GD_ht1(g) += delbottom; 00650 GD_ht2(g) += (delta - delbottom); 00651 } 00652 } 00653 } 00654 else { 00655 int gaps = maxr - minr + 2; 00656 int yoff = (delta + (gaps - 1)) / gaps; 00657 int y = yoff; 00658 for (r = GD_maxrank(root) - 1; r >= GD_minrank(root); r--) { 00659 if (rank[r].n > 0) 00660 ND_coord(rank[r].v[0]).y += y; 00661 y += yoff; 00662 } 00663 GD_ht2(g) += yoff; 00664 GD_ht1(g) += yoff; 00665 } 00666 } 00667 00668 /* adjustSimple: 00669 * Expand cluster height by delta, adding half to top 00670 * and half to bottom. If the bottom expansion exceeds the 00671 * ht1 of the rank, shift the ranks in the cluster up. 00672 * If the top expansion, including any shift from the bottom 00673 * expansion, exceeds to ht2 of the rank, shift the ranks above 00674 * the cluster up. 00675 */ 00676 static void adjustSimple(graph_t * g, int delta) 00677 { 00678 int r, bottom, deltop, delbottom; 00679 graph_t *root = agroot(g); 00680 rank_t *rank = GD_rank(root); 00681 int maxr = GD_maxrank(g); 00682 int minr = GD_minrank(g); 00683 00684 bottom = (delta+1) / 2; 00685 delbottom = GD_ht1(g) + bottom - rank[maxr].ht1; 00686 if (delbottom > 0) { 00687 for (r = maxr; r >= minr; r--) { 00688 if (rank[r].n > 0) 00689 ND_coord(rank[r].v[0]).y += delbottom; 00690 } 00691 deltop = GD_ht2(g) + (delta-bottom) + delbottom - rank[minr].ht2; 00692 } 00693 else 00694 deltop = GD_ht2(g) + (delta-bottom) - rank[minr].ht2; 00695 if (deltop > 0) { 00696 for (r = minr-1; r >= GD_minrank(root); r--) { 00697 if (rank[r].n > 0) 00698 ND_coord(rank[r].v[0]).y += deltop; 00699 } 00700 } 00701 GD_ht2(g) += (delta - bottom); 00702 GD_ht1(g) += bottom; 00703 } 00704 00705 /* adjustRanks: 00706 * Recursively adjust ranks to take into account 00707 * wide cluster labels when rankdir=LR. 00708 * We divide the extra space between the top and bottom. 00709 * Adjust the ht1 and ht2 values in the process. 00710 */ 00711 static void adjustRanks(graph_t * g, int equal) 00712 { 00713 int lht; /* label height */ 00714 int rht; /* height between top and bottom ranks */ 00715 int delta, maxr, minr, margin; 00716 int c, ht1, ht2; 00717 00718 rank_t *rank = GD_rank(agroot(g)); 00719 margin = late_int (g, G_margin, CL_OFFSET, 0); 00720 00721 ht1 = GD_ht1(g); 00722 ht2 = GD_ht2(g); 00723 00724 for (c = 1; c <= GD_n_cluster(g); c++) { 00725 graph_t *subg = GD_clust(g)[c]; 00726 adjustRanks(subg, equal); 00727 if (GD_maxrank(subg) == GD_maxrank(g)) 00728 ht1 = MAX(ht1, GD_ht1(subg) + margin); 00729 if (GD_minrank(subg) == GD_minrank(g)) 00730 ht2 = MAX(ht2, GD_ht2(subg) + margin); 00731 } 00732 00733 GD_ht1(g) = ht1; 00734 GD_ht2(g) = ht2; 00735 00736 if ((g != agroot(g)) && GD_label(g)) { 00737 lht = MAX(GD_border(g)[LEFT_IX].y, GD_border(g)[RIGHT_IX].y); 00738 maxr = GD_maxrank(g); 00739 minr = GD_minrank(g); 00740 rht = ND_coord(rank[minr].v[0]).y - ND_coord(rank[maxr].v[0]).y; 00741 delta = lht - (rht + ht1 + ht2); 00742 if (delta > 0) { 00743 if (equal) 00744 adjustEqual(g, delta); 00745 else 00746 adjustSimple(g, delta); 00747 } 00748 } 00749 00750 /* update the global ranks */ 00751 if (g != agroot(g)) { 00752 rank[GD_minrank(g)].ht2 = MAX(rank[GD_minrank(g)].ht2, GD_ht2(g)); 00753 rank[GD_maxrank(g)].ht1 = MAX(rank[GD_maxrank(g)].ht1, GD_ht1(g)); 00754 } 00755 } 00756 00757 /* clust_ht: 00758 * recursively compute cluster ht requirements. assumes GD_ht1(subg) and ht2 00759 * are computed from primitive nodes only. updates ht1 and ht2 to reflect 00760 * cluster nesting and labels. also maintains global rank ht1 and ht2. 00761 * Return true if some cluster has a label. 00762 */ 00763 static int clust_ht(Agraph_t * g) 00764 { 00765 int c, ht1, ht2; 00766 graph_t *subg; 00767 rank_t *rank = GD_rank(agroot(g)); 00768 int margin, haveClustLabel = 0; 00769 00770 if (g == agroot(g)) 00771 margin = CL_OFFSET; 00772 else 00773 margin = late_int (g, G_margin, CL_OFFSET, 0); 00774 00775 ht1 = GD_ht1(g); 00776 ht2 = GD_ht2(g); 00777 00778 /* account for sub-clusters */ 00779 for (c = 1; c <= GD_n_cluster(g); c++) { 00780 subg = GD_clust(g)[c]; 00781 haveClustLabel |= clust_ht(subg); 00782 if (GD_maxrank(subg) == GD_maxrank(g)) 00783 ht1 = MAX(ht1, GD_ht1(subg) + margin); 00784 if (GD_minrank(subg) == GD_minrank(g)) 00785 ht2 = MAX(ht2, GD_ht2(subg) + margin); 00786 } 00787 00788 /* account for a possible cluster label in clusters */ 00789 /* room for root graph label is handled in dotneato_postprocess */ 00790 if ((g != agroot(g)) && GD_label(g)) { 00791 haveClustLabel = 1; 00792 if (!GD_flip(agroot(g))) { 00793 ht1 += GD_border(g)[BOTTOM_IX].y; 00794 ht2 += GD_border(g)[TOP_IX].y; 00795 } 00796 } 00797 GD_ht1(g) = ht1; 00798 GD_ht2(g) = ht2; 00799 00800 /* update the global ranks */ 00801 if (g != agroot(g)) { 00802 rank[GD_minrank(g)].ht2 = MAX(rank[GD_minrank(g)].ht2, ht2); 00803 rank[GD_maxrank(g)].ht1 = MAX(rank[GD_maxrank(g)].ht1, ht1); 00804 } 00805 00806 return haveClustLabel; 00807 } 00808 00809 /* set y coordinates of nodes, a rank at a time */ 00810 static void set_ycoords(graph_t * g) 00811 { 00812 int i, j, r, ht2, maxht, delta, d0, d1; 00813 node_t *n; 00814 edge_t *e; 00815 rank_t *rank = GD_rank(g); 00816 graph_t *clust; 00817 int lbl; 00818 00819 ht2 = maxht = 0; 00820 00821 /* scan ranks for tallest nodes. */ 00822 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { 00823 for (i = 0; i < rank[r].n; i++) { 00824 n = rank[r].v[i]; 00825 00826 /* assumes symmetry, ht1 = ht2 */ 00827 ht2 = (ROUND(ND_ht(n)) + 1) / 2; 00828 00829 00830 /* have to look for high self-edge labels, too */ 00831 if (ND_other(n).list) 00832 for (j = 0; (e = ND_other(n).list[j]); j++) { 00833 if (agtail(e) == aghead(e)) { 00834 if (ED_label(e)) 00835 ht2 = MAX(ht2, ED_label(e)->dimen.y / 2); 00836 } 00837 } 00838 00839 /* update global rank ht */ 00840 if (rank[r].pht2 < ht2) 00841 rank[r].pht2 = rank[r].ht2 = ht2; 00842 if (rank[r].pht1 < ht2) 00843 rank[r].pht1 = rank[r].ht1 = ht2; 00844 00845 /* update nearest enclosing cluster rank ht */ 00846 if ((clust = ND_clust(n))) { 00847 int yoff = (clust == g ? 0 : late_int (clust, G_margin, CL_OFFSET, 0)); 00848 if (ND_rank(n) == GD_minrank(clust)) 00849 GD_ht2(clust) = MAX(GD_ht2(clust), ht2 + yoff); 00850 if (ND_rank(n) == GD_maxrank(clust)) 00851 GD_ht1(clust) = MAX(GD_ht1(clust), ht2 + yoff); 00852 } 00853 } 00854 } 00855 00856 /* scan sub-clusters */ 00857 lbl = clust_ht(g); 00858 00859 /* make the initial assignment of ycoords to leftmost nodes by ranks */ 00860 maxht = 0; 00861 r = GD_maxrank(g); 00862 (ND_coord(rank[r].v[0])).y = rank[r].ht1; 00863 while (--r >= GD_minrank(g)) { 00864 d0 = rank[r + 1].pht2 + rank[r].pht1 + GD_ranksep(g); /* prim node sep */ 00865 d1 = rank[r + 1].ht2 + rank[r].ht1 + CL_OFFSET; /* cluster sep */ 00866 delta = MAX(d0, d1); 00867 if (rank[r].n > 0) /* this may reflect some problem */ 00868 (ND_coord(rank[r].v[0])).y = (ND_coord(rank[r + 1].v[0])).y + delta; 00869 #ifdef DEBUG 00870 else 00871 fprintf(stderr, "dot set_ycoords: rank %d is empty\n", 00872 rank[r].n); 00873 #endif 00874 maxht = MAX(maxht, delta); 00875 } 00876 00877 /* re-assign if ranks are equally spaced */ 00878 if (GD_exact_ranksep(g)) { 00879 for (r = GD_maxrank(g) - 1; r >= GD_minrank(g); r--) 00880 if (rank[r].n > 0) /* this may reflect the same problem :-() */ 00881 (ND_coord(rank[r].v[0])).y= 00882 (ND_coord(rank[r + 1].v[0])).y + maxht; 00883 } 00884 00885 if (lbl && GD_flip(g)) 00886 adjustRanks(g, GD_exact_ranksep(g)); 00887 00888 /* copy ycoord assignment from leftmost nodes to others */ 00889 for (n = GD_nlist(g); n; n = ND_next(n)) 00890 ND_coord(n).y = (ND_coord(rank[ND_rank(n)].v[0])).y; 00891 } 00892 00893 /* dot_compute_bb: 00894 * Compute bounding box of g. 00895 * The x limits of clusters are given by the x positions of ln and rn. 00896 * This information is stored in the rank field, since it was calculated 00897 * using network simplex. 00898 * For the root graph, we don't enforce all the constraints on lr and 00899 * rn, so we traverse the nodes and subclusters. 00900 */ 00901 static void dot_compute_bb(graph_t * g, graph_t * root) 00902 { 00903 int r, c, x, offset; 00904 node_t *v; 00905 pointf LL, UR; 00906 00907 if (g == agroot(g)) { 00908 LL.x = (double)(INT_MAX); 00909 UR.x = (double)(-INT_MAX); 00910 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { 00911 int rnkn = GD_rank(g)[r].n; 00912 if (rnkn == 0) 00913 continue; 00914 if ((v = GD_rank(g)[r].v[0]) == NULL) 00915 continue; 00916 for (c = 1; (ND_node_type(v) != NORMAL) && c < rnkn; c++) 00917 v = GD_rank(g)[r].v[c]; 00918 if (ND_node_type(v) == NORMAL) { 00919 x = ND_coord(v).x - ND_lw(v); 00920 LL.x = MIN(LL.x, x); 00921 } 00922 else continue; 00923 /* At this point, we know the rank contains a NORMAL node */ 00924 v = GD_rank(g)[r].v[rnkn - 1]; 00925 for (c = rnkn-2; ND_node_type(v) != NORMAL; c--) 00926 v = GD_rank(g)[r].v[c]; 00927 x = ND_coord(v).x + ND_rw(v); 00928 UR.x = MAX(UR.x, x); 00929 } 00930 offset = CL_OFFSET; 00931 for (c = 1; c <= GD_n_cluster(g); c++) { 00932 x = (double)(GD_bb(GD_clust(g)[c]).LL.x - offset); 00933 LL.x = MIN(LL.x, x); 00934 x = (double)(GD_bb(GD_clust(g)[c]).UR.x + offset); 00935 UR.x = MAX(UR.x, x); 00936 } 00937 } else { 00938 LL.x = (double)(ND_rank(GD_ln(g))); 00939 UR.x = (double)(ND_rank(GD_rn(g))); 00940 } 00941 LL.y = ND_coord(GD_rank(root)[GD_maxrank(g)].v[0]).y - GD_ht1(g); 00942 UR.y = ND_coord(GD_rank(root)[GD_minrank(g)].v[0]).y + GD_ht2(g); 00943 GD_bb(g).LL = LL; 00944 GD_bb(g).UR = UR; 00945 } 00946 00947 static void rec_bb(graph_t * g, graph_t * root) 00948 { 00949 int c; 00950 for (c = 1; c <= GD_n_cluster(g); c++) 00951 rec_bb(GD_clust(g)[c], root); 00952 dot_compute_bb(g, root); 00953 } 00954 00955 /* scale_bb: 00956 * Recursively rescale all bounding boxes using scale factors 00957 * xf and yf. We assume all the bboxes have been computed. 00958 */ 00959 static void scale_bb(graph_t * g, graph_t * root, double xf, double yf) 00960 { 00961 int c; 00962 00963 for (c = 1; c <= GD_n_cluster(g); c++) 00964 scale_bb(GD_clust(g)[c], root, xf, yf); 00965 GD_bb(g).LL.x *= xf; 00966 GD_bb(g).LL.y *= yf; 00967 GD_bb(g).UR.x *= xf; 00968 GD_bb(g).UR.y *= yf; 00969 } 00970 00971 /* adjustAspectRatio: 00972 */ 00973 static void adjustAspectRatio (graph_t* g, aspect_t* asp) 00974 { 00975 double AR = (GD_bb(g).UR.x - GD_bb(g).LL.x)/(GD_bb(g).UR.y - GD_bb(g).LL.y); 00976 if (Verbose) { 00977 fprintf(stderr, "AR=%0.4lf\t Area= %0.4lf\t", AR, (double)(GD_bb(g).UR.x - GD_bb(g).LL.x)*(GD_bb(g).UR.y - GD_bb(g).LL.y)/10000.0); 00978 fprintf(stderr, "Dummy=%d\n", countDummyNodes(g)); 00979 } 00980 if (AR > 1.1*asp->targetAR) { 00981 asp->nextIter = (int)(asp->targetAR * (double)(asp->curIterations - asp->prevIterations)/(AR)); 00982 } 00983 else if (AR <= 0.8 * asp->targetAR) { 00984 asp->nextIter = -1; 00985 if (Verbose) 00986 fprintf(stderr, "Going to apply another expansion.\n"); 00987 } 00988 else { 00989 asp->nextIter = 0; 00990 } 00991 if (Verbose) 00992 fprintf(stderr, "next#iter=%d\n", asp->nextIter); 00993 } 00994 00995 /* set_aspect: 00996 * Set bounding boxes and, if ratio is set, rescale graph. 00997 * Note that if some dimension shrinks, there may be problems 00998 * with labels. 00999 */ 01000 static void set_aspect(graph_t * g, aspect_t* asp) 01001 { 01002 double xf = 0.0, yf = 0.0, actual, desired; 01003 node_t *n; 01004 boolean scale_it, filled; 01005 point sz; 01006 01007 rec_bb(g, g); 01008 if ((GD_maxrank(g) > 0) && (GD_drawing(g)->ratio_kind)) { 01009 sz.x = GD_bb(g).UR.x - GD_bb(g).LL.x; 01010 sz.y = GD_bb(g).UR.y - GD_bb(g).LL.y; /* normalize */ 01011 if (GD_flip(g)) { 01012 int t = sz.x; 01013 sz.x = sz.y; 01014 sz.y = t; 01015 } 01016 scale_it = TRUE; 01017 if (GD_drawing(g)->ratio_kind == R_AUTO) 01018 filled = idealsize(g, .5); 01019 else 01020 filled = (GD_drawing(g)->ratio_kind == R_FILL); 01021 if (filled) { 01022 /* fill is weird because both X and Y can stretch */ 01023 if (GD_drawing(g)->size.x <= 0) 01024 scale_it = FALSE; 01025 else { 01026 xf = (double) GD_drawing(g)->size.x / (double) sz.x; 01027 yf = (double) GD_drawing(g)->size.y / (double) sz.y; 01028 if ((xf < 1.0) || (yf < 1.0)) { 01029 if (xf < yf) { 01030 yf = yf / xf; 01031 xf = 1.0; 01032 } else { 01033 xf = xf / yf; 01034 yf = 1.0; 01035 } 01036 } 01037 } 01038 } else if (GD_drawing(g)->ratio_kind == R_EXPAND) { 01039 if (GD_drawing(g)->size.x <= 0) 01040 scale_it = FALSE; 01041 else { 01042 xf = (double) GD_drawing(g)->size.x / 01043 (double) GD_bb(g).UR.x; 01044 yf = (double) GD_drawing(g)->size.y / 01045 (double) GD_bb(g).UR.y; 01046 if ((xf > 1.0) && (yf > 1.0)) { 01047 double scale = MIN(xf, yf); 01048 xf = yf = scale; 01049 } else 01050 scale_it = FALSE; 01051 } 01052 } else if (GD_drawing(g)->ratio_kind == R_VALUE) { 01053 desired = GD_drawing(g)->ratio; 01054 actual = ((double) sz.y) / ((double) sz.x); 01055 if (actual < desired) { 01056 yf = desired / actual; 01057 xf = 1.0; 01058 } else { 01059 xf = actual / desired; 01060 yf = 1.0; 01061 } 01062 } else 01063 scale_it = FALSE; 01064 if (scale_it) { 01065 if (GD_flip(g)) { 01066 double t = xf; 01067 xf = yf; 01068 yf = t; 01069 } 01070 for (n = GD_nlist(g); n; n = ND_next(n)) { 01071 ND_coord(n).x = ROUND(ND_coord(n).x * xf); 01072 ND_coord(n).y = ROUND(ND_coord(n).y * yf); 01073 } 01074 scale_bb(g, g, xf, yf); 01075 } 01076 } 01077 01078 if (asp) adjustAspectRatio (g, asp); 01079 } 01080 01081 static point resize_leaf(node_t * leaf, point lbound) 01082 { 01083 gv_nodesize(leaf, GD_flip(agraphof(leaf))); 01084 ND_coord(leaf).y = lbound.y; 01085 ND_coord(leaf).x = lbound.x + ND_lw(leaf); 01086 lbound.x = lbound.x + ND_lw(leaf) + ND_rw(leaf) + GD_nodesep(agraphof(leaf)); 01087 return lbound; 01088 } 01089 01090 static point place_leaf(node_t * leaf, point lbound, int order) 01091 { 01092 node_t *leader; 01093 graph_t *g = agraphof(leaf); 01094 01095 leader = UF_find(leaf); 01096 if (leaf != leader) 01097 fast_nodeapp(leader, leaf); 01098 ND_order(leaf) = order; 01099 ND_rank(leaf) = ND_rank(leader); 01100 GD_rank(g)[ND_rank(leaf)].v[ND_order(leaf)] = leaf; 01101 return resize_leaf(leaf, lbound); 01102 } 01103 01104 /* make space for the leaf nodes of each rank */ 01105 static void make_leafslots(graph_t * g) 01106 { 01107 int i, j, r; 01108 node_t *v; 01109 01110 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { 01111 j = 0; 01112 for (i = 0; i < GD_rank(g)[r].n; i++) { 01113 v = GD_rank(g)[r].v[i]; 01114 ND_order(v) = j; 01115 if (ND_ranktype(v) == LEAFSET) 01116 j = j + ND_UF_size(v); 01117 else 01118 j++; 01119 } 01120 if (j <= GD_rank(g)[r].n) 01121 continue; 01122 GD_rank(g)[r].v = ALLOC(j + 1, GD_rank(g)[r].v, node_t *); 01123 for (i = GD_rank(g)[r].n - 1; i >= 0; i--) { 01124 v = GD_rank(g)[r].v[i]; 01125 GD_rank(g)[r].v[ND_order(v)] = v; 01126 } 01127 GD_rank(g)[r].n = j; 01128 GD_rank(g)[r].v[j] = NULL; 01129 } 01130 } 01131 01132 static void do_leaves(graph_t * g, node_t * leader) 01133 { 01134 int j; 01135 point lbound; 01136 node_t *n; 01137 edge_t *e; 01138 01139 if (ND_UF_size(leader) <= 1) 01140 return; 01141 lbound.x = ND_coord(leader).x - ND_lw(leader); 01142 lbound.y = ND_coord(leader).y; 01143 lbound = resize_leaf(leader, lbound); 01144 if (ND_out(leader).size > 0) { /* in-edge leaves */ 01145 n = aghead(ND_out(leader).list[0]); 01146 j = ND_order(leader) + 1; 01147 for (e = agfstin(g, n); e; e = agnxtin(g, e)) { 01148 #ifndef WITH_CGRAPH 01149 edge_t *e1 = e; 01150 #else 01151 edge_t *e1 = AGMKOUT(e); 01152 #endif 01153 if ((agtail(e1) != leader) && (UF_find(agtail(e1)) == leader)) { 01154 lbound = place_leaf(agtail(e1), lbound, j++); 01155 unmerge_oneway(e1); 01156 elist_append(e1, ND_in(aghead(e1))); 01157 } 01158 } 01159 } else { /* out edge leaves */ 01160 n = agtail(ND_in(leader).list[0]); 01161 j = ND_order(leader) + 1; 01162 for (e = agfstout(g, n); e; e = agnxtout(g, e)) { 01163 if ((aghead(e) != leader) && (UF_find(aghead(e)) == leader)) { 01164 lbound = place_leaf(aghead(e), lbound, j++); 01165 unmerge_oneway(e); 01166 elist_append(e, ND_out(agtail(e))); 01167 } 01168 } 01169 } 01170 } 01171 01172 int ports_eq(edge_t * e, edge_t * f) 01173 { 01174 return ((ED_head_port(e).defined == ED_head_port(f).defined) 01175 && (((ED_head_port(e).p.x == ED_head_port(f).p.x) && 01176 (ED_head_port(e).p.y == ED_head_port(f).p.y)) 01177 || (ED_head_port(e).defined == FALSE)) 01178 && (((ED_tail_port(e).p.x == ED_tail_port(f).p.x) && 01179 (ED_tail_port(e).p.y == ED_tail_port(f).p.y)) 01180 || (ED_tail_port(e).defined == FALSE)) 01181 ); 01182 } 01183 01184 static void expand_leaves(graph_t * g) 01185 { 01186 int i, d; 01187 node_t *n; 01188 edge_t *e, *f; 01189 01190 make_leafslots(g); 01191 for (n = GD_nlist(g); n; n = ND_next(n)) { 01192 if (ND_inleaf(n)) 01193 do_leaves(g, ND_inleaf(n)); 01194 if (ND_outleaf(n)) 01195 do_leaves(g, ND_outleaf(n)); 01196 if (ND_other(n).list) 01197 for (i = 0; (e = ND_other(n).list[i]); i++) { 01198 if ((d = ND_rank(aghead(e)) - ND_rank(aghead(e))) == 0) 01199 continue; 01200 f = ED_to_orig(e); 01201 if (ports_eq(e, f) == FALSE) { 01202 zapinlist(&(ND_other(n)), e); 01203 if (d == 1) 01204 fast_edge(e); 01205 /*else unitize(e); ### */ 01206 i--; 01207 } 01208 } 01209 } 01210 } 01211 01212 /* make_lrvn: 01213 * Add left and right slacknodes to a cluster which 01214 * are used in the LP to constrain nodes not in g but 01215 * sharing its ranks to be to the left or right of g 01216 * by a specified amount. 01217 * The slacknodes ln and rn give the x position of the 01218 * left and right side of the cluster's bounding box. In 01219 * particular, any cluster labels on the left or right side 01220 * are inside. 01221 * If a cluster has a label, and we have rankdir!=LR, we make 01222 * sure the cluster is wide enough for the label. Note that 01223 * if the label is wider than the cluster, the nodes in the 01224 * cluster may not be centered. 01225 */ 01226 static void make_lrvn(graph_t * g) 01227 { 01228 node_t *ln, *rn; 01229 01230 if (GD_ln(g)) 01231 return; 01232 ln = virtual_node(agroot(g)); 01233 ND_node_type(ln) = SLACKNODE; 01234 rn = virtual_node(agroot(g)); 01235 ND_node_type(rn) = SLACKNODE; 01236 01237 if (GD_label(g) && (g != agroot(g)) && !GD_flip(agroot(g))) { 01238 int w = MAX(GD_border(g)[BOTTOM_IX].x, GD_border(g)[TOP_IX].x); 01239 make_aux_edge(ln, rn, w, 0); 01240 } 01241 01242 GD_ln(g) = ln; 01243 GD_rn(g) = rn; 01244 } 01245 01246 /* contain_nodes: 01247 * make left and right bounding box virtual nodes ln and rn 01248 * constrain interior nodes 01249 */ 01250 static void contain_nodes(graph_t * g) 01251 { 01252 int margin, r; 01253 node_t *ln, *rn, *v; 01254 01255 margin = late_int (g, G_margin, CL_OFFSET, 0); 01256 make_lrvn(g); 01257 ln = GD_ln(g); 01258 rn = GD_rn(g); 01259 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) { 01260 if (GD_rank(g)[r].n == 0) 01261 continue; 01262 v = GD_rank(g)[r].v[0]; 01263 if (v == NULL) { 01264 agerr(AGERR, "contain_nodes clust %s rank %d missing node\n", 01265 agnameof(g), r); 01266 continue; 01267 } 01268 make_aux_edge(ln, v, 01269 ND_lw(v) + margin + GD_border(g)[LEFT_IX].x, 0); 01270 v = GD_rank(g)[r].v[GD_rank(g)[r].n - 1]; 01271 make_aux_edge(v, rn, 01272 ND_rw(v) + margin + GD_border(g)[RIGHT_IX].x, 0); 01273 } 01274 } 01275 01276 /* idealsize: 01277 * set g->drawing->size to a reasonable default. 01278 * returns a boolean to indicate if drawing is to 01279 * be scaled and filled */ 01280 static boolean idealsize(graph_t * g, double minallowed) 01281 { 01282 double xf, yf, f, R; 01283 pointf b, relpage, margin; 01284 01285 /* try for one page */ 01286 relpage = GD_drawing(g)->page; 01287 if (relpage.x < 0.001 || relpage.y < 0.001) 01288 return FALSE; /* no page was specified */ 01289 margin = GD_drawing(g)->margin; 01290 relpage = sub_pointf(relpage, margin); 01291 relpage = sub_pointf(relpage, margin); 01292 b.x = GD_bb(g).UR.x; 01293 b.y = GD_bb(g).UR.y; 01294 xf = relpage.x / b.x; 01295 yf = relpage.y / b.y; 01296 if ((xf >= 1.0) && (yf >= 1.0)) 01297 return FALSE; /* fits on one page */ 01298 01299 f = MIN(xf, yf); 01300 xf = yf = MAX(f, minallowed); 01301 01302 R = ceil((xf * b.x) / relpage.x); 01303 xf = ((R * relpage.x) / b.x); 01304 R = ceil((yf * b.y) / relpage.y); 01305 yf = ((R * relpage.y) / b.y); 01306 GD_drawing(g)->size.x = b.x * xf; 01307 GD_drawing(g)->size.y = b.y * yf; 01308 return TRUE; 01309 }
1.7.5