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