Graphviz  2.31.20130618.0446
lib/dotgen/flat.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 #include        "dot.h"
00016 
00017 
00018 static node_t *make_vn_slot(graph_t * g, int r, int pos)
00019 {
00020     int i;
00021     node_t **v, *n;
00022 
00023     v = GD_rank(g)[r].v =
00024         ALLOC(GD_rank(g)[r].n + 2, GD_rank(g)[r].v, node_t *);
00025     for (i = GD_rank(g)[r].n; i > pos; i--) {
00026         v[i] = v[i - 1];
00027         ND_order(v[i])++;
00028     }
00029     n = v[pos] = virtual_node(g);
00030     ND_order(n) = pos;
00031     ND_rank(n) = r;
00032     v[++(GD_rank(g)[r].n)] = NULL;
00033     return v[pos];
00034 }
00035 
00036 #define         HLB     0       /* hard left bound */
00037 #define         HRB             1       /* hard right bound */
00038 #define         SLB             2       /* soft left bound */
00039 #define         SRB             3       /* soft right bound */
00040 
00041 static void findlr(node_t * u, node_t * v, int *lp, int *rp)
00042 {
00043     int l, r;
00044     l = ND_order(u);
00045     r = ND_order(v);
00046     if (l > r) {
00047         int t = l;
00048         l = r;
00049         r = t;
00050     }
00051     *lp = l;
00052     *rp = r;
00053 }
00054 
00055 static void setbounds(node_t * v, int *bounds, int lpos, int rpos)
00056 {
00057     int i, l, r, ord;
00058     edge_t *f;
00059 
00060     if (ND_node_type(v) == VIRTUAL) {
00061         ord = ND_order(v);
00062         if (ND_in(v).size == 0) {       /* flat */
00063             assert(ND_out(v).size == 2);
00064             findlr(aghead(ND_out(v).list[0]), aghead(ND_out(v).list[1]), &l,
00065                    &r);
00066             /* the other flat edge could be to the left or right */
00067             if (r <= lpos)
00068                 bounds[SLB] = bounds[HLB] = ord;
00069             else if (l >= rpos)
00070                 bounds[SRB] = bounds[HRB] = ord;
00071             /* could be spanning this one */
00072             else if ((l < lpos) && (r > rpos)); /* ignore */
00073             /* must have intersecting ranges */
00074             else {
00075                 if ((l < lpos) || ((l == lpos) && (r < rpos)))
00076                     bounds[SLB] = ord;
00077                 if ((r > rpos) || ((r == rpos) && (l > lpos)))
00078                     bounds[SRB] = ord;
00079             }
00080         } else {                /* forward */
00081             boolean onleft, onright;
00082             onleft = onright = FALSE;
00083             for (i = 0; (f = ND_out(v).list[i]); i++) {
00084                 if (ND_order(aghead(f)) <= lpos) {
00085                     onleft = TRUE;
00086                     continue;
00087                 }
00088                 if (ND_order(aghead(f)) >= rpos) {
00089                     onright = TRUE;
00090                     continue;
00091                 }
00092             }
00093             if (onleft && (onright == FALSE))
00094                 bounds[HLB] = ord + 1;
00095             if (onright && (onleft == FALSE))
00096                 bounds[HRB] = ord - 1;
00097         }
00098     }
00099 }
00100 
00101 static int flat_limits(graph_t * g, edge_t * e)
00102 {
00103     int lnode, rnode, r, bounds[4], lpos, rpos, pos;
00104     node_t **rank;
00105 
00106     r = ND_rank(agtail(e)) - 1;
00107     rank = GD_rank(g)[r].v;
00108     lnode = 0;
00109     rnode = GD_rank(g)[r].n - 1;
00110     bounds[HLB] = bounds[SLB] = lnode - 1;
00111     bounds[HRB] = bounds[SRB] = rnode + 1;
00112     findlr(agtail(e), aghead(e), &lpos, &rpos);
00113     while (lnode <= rnode) {
00114         setbounds(rank[lnode], bounds, lpos, rpos);
00115         if (lnode != rnode)
00116             setbounds(rank[rnode], bounds, lpos, rpos);
00117         lnode++;
00118         rnode--;
00119         if (bounds[HRB] - bounds[HLB] <= 1)
00120             break;
00121     }
00122     if (bounds[HLB] <= bounds[HRB])
00123         pos = (bounds[HLB] + bounds[HRB] + 1) / 2;
00124     else
00125         pos = (bounds[SLB] + bounds[SRB] + 1) / 2;
00126     return pos;
00127 }
00128 
00129 /* flat_node:
00130  * Create virtual node representing edge label between
00131  * actual ends of edge e. 
00132  * This node is characterized by being virtual and having a non-NULL
00133  * ND_alg pointing to e.
00134  */
00135 static void 
00136 flat_node(edge_t * e)
00137 {
00138     int r, place, ypos, h2;
00139     graph_t *g;
00140     node_t *n, *vn;
00141     edge_t *ve;
00142     pointf dimen;
00143 
00144     if (ED_label(e) == NULL)
00145         return;
00146     g = agraphof(agtail(e));
00147     r = ND_rank(agtail(e));
00148 
00149     place = flat_limits(g, e);
00150     /* grab ypos = LL.y of label box before make_vn_slot() */
00151     if ((n = GD_rank(g)[r - 1].v[0]))
00152         ypos = ND_coord(n).y - GD_rank(g)[r - 1].ht1;
00153     else {
00154         n = GD_rank(g)[r].v[0];
00155         ypos = ND_coord(n).y + GD_rank(g)[r].ht2 + GD_ranksep(g);
00156     }
00157     vn = make_vn_slot(g, r - 1, place);
00158     dimen = ED_label(e)->dimen;
00159     if (GD_flip(g)) {
00160         double f = dimen.x;
00161         dimen.x = dimen.y;
00162         dimen.y = f;
00163     }
00164     ND_ht(vn) = dimen.y;
00165     h2 = ND_ht(vn) / 2;
00166     ND_lw(vn) = ND_rw(vn) = dimen.x / 2;
00167     ND_label(vn) = ED_label(e);
00168     ND_coord(vn).y = ypos + h2;
00169     ve = virtual_edge(vn, agtail(e), e);        /* was NULL? */
00170     ED_tail_port(ve).p.x = -ND_lw(vn);
00171     ED_head_port(ve).p.x = ND_rw(agtail(e));
00172     ED_edge_type(ve) = FLATORDER;
00173     ve = virtual_edge(vn, aghead(e), e);
00174     ED_tail_port(ve).p.x = ND_rw(vn);
00175     ED_head_port(ve).p.x = ND_lw(aghead(e));
00176     ED_edge_type(ve) = FLATORDER;
00177     /* another assumed symmetry of ht1/ht2 of a label node */
00178     if (GD_rank(g)[r - 1].ht1 < h2)
00179         GD_rank(g)[r - 1].ht1 = h2;
00180     if (GD_rank(g)[r - 1].ht2 < h2)
00181         GD_rank(g)[r - 1].ht2 = h2;
00182     ND_alg(vn) = e;
00183 }
00184 
00185 static void abomination(graph_t * g)
00186 {
00187     int r;
00188     rank_t *rptr;
00189 
00190     assert(GD_minrank(g) == 0);
00191     /* 3 = one for new rank, one for sentinel, one for off-by-one */
00192     r = GD_maxrank(g) + 3;
00193     rptr = ALLOC(r, GD_rank(g), rank_t);
00194     GD_rank(g) = rptr + 1;
00195     for (r = GD_maxrank(g); r >= 0; r--)
00196         GD_rank(g)[r] = GD_rank(g)[r - 1];
00197     GD_rank(g)[r].n = GD_rank(g)[r].an = 0;
00198     GD_rank(g)[r].v = GD_rank(g)[r].av = N_NEW(2, node_t *);
00199     GD_rank(g)[r].flat = NULL;
00200     GD_rank(g)[r].ht1 = GD_rank(g)[r].ht2 = 1;
00201     GD_rank(g)[r].pht1 = GD_rank(g)[r].pht2 = 1;
00202     GD_minrank(g)--;
00203 }
00204 
00205 /* checkFlatAdjacent:
00206  * Check if tn and hn are adjacent. 
00207  * If so, set adjacent bit on all related edges.
00208  * Assume e is flat.
00209  */
00210 static void
00211 checkFlatAdjacent (edge_t* e)
00212 {
00213     node_t* tn = agtail(e);
00214     node_t* hn = aghead(e);
00215     int i, lo, hi;
00216     node_t* n;
00217     rank_t *rank;
00218 
00219     if (ND_order(tn) < ND_order(hn)) {
00220         lo = ND_order(tn);
00221         hi = ND_order(hn);
00222     }
00223     else {
00224         lo = ND_order(hn);
00225         hi = ND_order(tn);
00226     }
00227     rank = &(GD_rank(agraphof(tn))[ND_rank(tn)]);
00228     for (i = lo + 1; i < hi; i++) {
00229         n = rank->v[i];
00230         if ((ND_node_type(n) == VIRTUAL && ND_label(n)) || 
00231              ND_node_type(n) == NORMAL)
00232             break;
00233     }
00234     if (i == hi) {  /* adjacent edge */
00235         do {
00236             ED_adjacent(e) = 1;
00237             e = ED_to_virt(e);
00238         } while (e); 
00239     }
00240 }
00241  
00242 /* flat_edges:
00243  * Process flat edges.
00244  * First, mark flat edges as having adjacent endpoints or not.
00245  *
00246  * Second, if there are edge labels, nodes are placed on ranks 0,2,4,...
00247  * If we have a labeled flat edge on rank 0, add a rank -1.
00248  *
00249  * Finally, create label information. Add a virtual label node in the 
00250  * previous rank for each labeled, non-adjacent flat edge. If this is 
00251  * done for any edge, return true, so that main code will reset y coords.
00252  * For labeled adjacent flat edges, store label width in representative edge.
00253  * FIX: We should take into account any extra height needed for the latter
00254  * labels.
00255  * 
00256  * We leave equivalent flat edges in ND_other. Their ED_virt field should
00257  * still point to the class representative.
00258  */
00259 int 
00260 flat_edges(graph_t * g)
00261 {
00262     int i, j, reset = FALSE;
00263     node_t *n;
00264     edge_t *e;
00265     int found = FALSE;
00266 
00267     for (n = GD_nlist(g); n; n = ND_next(n)) {
00268         if (ND_flat_out(n).list) {
00269             for (j = 0; (e = ND_flat_out(n).list[j]); j++) {
00270                 checkFlatAdjacent (e);
00271             }
00272         }
00273         for (j = 0; j < ND_other(n).size; j++) {
00274             e = ND_other(n).list[j];
00275             if (ND_rank(aghead(e)) == ND_rank(agtail(e)))
00276                 checkFlatAdjacent (e);
00277         }
00278     }
00279 
00280     if ((GD_rank(g)[0].flat) || (GD_n_cluster(g) > 0)) {
00281         for (i = 0; (n = GD_rank(g)[0].v[i]); i++) {
00282             for (j = 0; (e = ND_flat_in(n).list[j]); j++) {
00283                 if ((ED_label(e)) && !ED_adjacent(e)) {
00284                     abomination(g);
00285                     found = TRUE;
00286                     break;
00287                 }
00288             }
00289             if (found)
00290                 break;
00291         }
00292     }
00293 
00294     rec_save_vlists(g);
00295     for (n = GD_nlist(g); n; n = ND_next(n)) {
00296           /* if n is the tail of any flat edge, one will be in flat_out */
00297         if (ND_flat_out(n).list) {
00298             for (i = 0; (e = ND_flat_out(n).list[i]); i++) {
00299                 if (ED_label(e)) {
00300                     if (ED_adjacent(e)) {
00301                         if (GD_flip(g)) ED_dist(e) = ED_label(e)->dimen.y;
00302                         else ED_dist(e) = ED_label(e)->dimen.x; 
00303                     }
00304                     else {
00305                         reset = TRUE;
00306                         flat_node(e);
00307                     }
00308                 }
00309             }
00310                 /* look for other flat edges with labels */
00311             for (j = 0; j < ND_other(n).size; j++) {
00312                 edge_t* le;
00313                 e = ND_other(n).list[j];
00314                 if (ND_rank(agtail(e)) != ND_rank(aghead(e))) continue;
00315                 if (agtail(e) == aghead(e)) continue; /* skip loops */
00316                 le = e;
00317                 while (ED_to_virt(le)) le = ED_to_virt(le);
00318                 ED_adjacent(e) = ED_adjacent(le); 
00319                 if (ED_label(e)) {
00320                     if (ED_adjacent(e)) {
00321                         double lw;
00322                         if (GD_flip(g)) lw = ED_label(e)->dimen.y;
00323                         else lw = ED_label(e)->dimen.x; 
00324                         ED_dist(le) = MAX(lw,ED_dist(le));
00325                     }
00326                     else {
00327                         reset = TRUE;
00328                         flat_node(e);
00329                     }
00330                 }
00331             }
00332         }
00333     }
00334     if (reset)
00335         rec_reset_vlists(g);
00336     return reset;
00337 }