Graphviz  2.29.20120524.0446
lib/dotgen/position.c
Go to the documentation of this file.
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 }