Graphviz  2.29.20120523.0446
lib/dotgen/conc.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  *      build edge_t concentrators for parallel edges with a common endpoint
00017  */
00018 
00019 #include        "dot.h"
00020 #include        <setjmp.h>
00021 
00022 #define         UP              0
00023 #define         DOWN    1
00024 
00025 static jmp_buf jbuf;
00026 
00027 static boolean samedir(edge_t * e, edge_t * f)
00028 {
00029     edge_t *e0, *f0;
00030 
00031     for (e0 = e; ED_edge_type(e0) != NORMAL; e0 = ED_to_orig(e0));
00032     for (f0 = f; ED_edge_type(f0) != NORMAL; f0 = ED_to_orig(f0));
00033     if (ED_conc_opp_flag(e0))
00034         return FALSE;
00035     if (ED_conc_opp_flag(f0))
00036         return FALSE;
00037     return ((ND_rank(agtail(f0)) - ND_rank(aghead(f0)))
00038             * (ND_rank(agtail(e0)) - ND_rank(aghead(e0))) > 0);
00039 }
00040 
00041 static boolean downcandidate(node_t * v)
00042 {
00043     return ((ND_node_type(v) == VIRTUAL) && (ND_in(v).size == 1)
00044             && (ND_out(v).size == 1) && (ND_label(v) == NULL));
00045 }
00046 
00047 static boolean bothdowncandidates(node_t * u, node_t * v)
00048 {
00049     edge_t *e, *f;
00050     e = ND_in(u).list[0];
00051     f = ND_in(v).list[0];
00052     if (downcandidate(v) && (agtail(e) == agtail(f))) {
00053         return samedir(e, f)
00054             && (portcmp(ED_tail_port(e), ED_tail_port(f)) == 0);
00055     }
00056     return FALSE;
00057 }
00058 
00059 static boolean upcandidate(node_t * v)
00060 {
00061     return ((ND_node_type(v) == VIRTUAL) && (ND_out(v).size == 1)
00062             && (ND_in(v).size == 1) && (ND_label(v) == NULL));
00063 }
00064 
00065 static boolean bothupcandidates(node_t * u, node_t * v)
00066 {
00067     edge_t *e, *f;
00068     e = ND_out(u).list[0];
00069     f = ND_out(v).list[0];
00070     if (upcandidate(v) && (aghead(e) == aghead(f))) {
00071         return samedir(e, f)
00072             && (portcmp(ED_head_port(e), ED_head_port(f)) == 0);
00073     }
00074     return FALSE;
00075 }
00076 
00077 static void mergevirtual(graph_t * g, int r, int lpos, int rpos, int dir)
00078 {
00079     int i, k;
00080     node_t *left, *right;
00081     edge_t *e, *f, *e0;
00082 
00083     left = GD_rank(g)[r].v[lpos];
00084     /* merge all right nodes into the leftmost one */
00085     for (i = lpos + 1; i <= rpos; i++) {
00086         right = GD_rank(g)[r].v[i];
00087         if (dir == DOWN) {
00088             while ((e = ND_out(right).list[0])) {
00089                 for (k = 0; (f = ND_out(left).list[k]); k++)
00090                     if (aghead(f) == aghead(e))
00091                         break;
00092                 if (f == NULL)
00093                     f = virtual_edge(left, aghead(e), e);
00094                 while ((e0 = ND_in(right).list[0])) {
00095                     merge_oneway(e0, f);
00096                     /*ED_weight(f) += ED_weight(e0); */
00097                     delete_fast_edge(e0);
00098                 }
00099                 delete_fast_edge(e);
00100             }
00101         } else {
00102             while ((e = ND_in(right).list[0])) {
00103                 for (k = 0; (f = ND_in(left).list[k]); k++)
00104                     if (agtail(f) == agtail(e))
00105                         break;
00106                 if (f == NULL)
00107                     f = virtual_edge(agtail(e), left, e);
00108                 while ((e0 = ND_out(right).list[0])) {
00109                     merge_oneway(e0, f);
00110                     delete_fast_edge(e0);
00111                 }
00112                 delete_fast_edge(e);
00113             }
00114         }
00115         assert(ND_in(right).size + ND_out(right).size == 0);
00116         delete_fast_node(g, right);
00117     }
00118     k = lpos + 1;
00119     i = rpos + 1;
00120     while (i < GD_rank(g)[r].n) {
00121         node_t *n;
00122         n = GD_rank(g)[r].v[k] = GD_rank(g)[r].v[i];
00123         ND_order(n) = k;
00124         k++;
00125         i++;
00126     }
00127     GD_rank(g)[r].n = k;
00128     GD_rank(g)[r].v[k] = NULL;
00129 }
00130 
00131 static void infuse(graph_t * g, node_t * n)
00132 {
00133     node_t *lead;
00134 
00135     lead = GD_rankleader(g)[ND_rank(n)];
00136     if ((lead == NULL) || (ND_order(lead) > ND_order(n)))
00137         GD_rankleader(g)[ND_rank(n)] = n;
00138 }
00139 
00140 static void rebuild_vlists(graph_t * g)
00141 {
00142     int c, i, r, maxi;
00143     node_t *n, *lead;
00144     edge_t *e, *rep;
00145 
00146     for (r = GD_minrank(g); r <= GD_maxrank(g); r++)
00147         GD_rankleader(g)[r] = NULL;
00148     dot_scan_ranks(g);
00149     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00150         infuse(g, n);
00151         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00152             for (rep = e; ED_to_virt(rep); rep = ED_to_virt(rep));
00153             while (ND_rank(aghead(rep)) < ND_rank(aghead(e))) {
00154                 infuse(g, aghead(rep));
00155                 rep = ND_out(aghead(rep)).list[0];
00156             }
00157         }
00158     }
00159 
00160     for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
00161         lead = GD_rankleader(g)[r];
00162         if (GD_rank(agroot(g))[r].v[ND_order(lead)] != lead) {
00163             agerr(AGERR, "rebuiltd_vlists: rank lead %s not in order %d of rank %d\n", 
00164                 agnameof(lead), ND_order(lead), r);
00165             longjmp(jbuf, 1);
00166         }
00167         GD_rank(g)[r].v =
00168             GD_rank(agroot(g))[r].v + ND_order((GD_rankleader(g)[r]));
00169         maxi = -1;
00170         for (i = 0; i < GD_rank(g)[r].n; i++) {
00171             if ((n = GD_rank(g)[r].v[i]) == NULL)
00172                 break;
00173             if (ND_node_type(n) == NORMAL) {
00174                 if (agcontains(g, n))
00175                     maxi = i;
00176                 else
00177                     break;
00178             } else {
00179                 edge_t *e;
00180                 for (e = ND_in(n).list[0]; e && ED_to_orig(e);
00181                      e = ED_to_orig(e));
00182                 if (e && (agcontains(g, agtail(e)))
00183                     && agcontains(g, aghead(e)))
00184                     maxi = i;
00185             }
00186         }
00187         if (maxi == -1)
00188             agerr(AGWARN, "degenerate concentrated rank %s,%d\n", agnameof(g),
00189                   r);
00190         GD_rank(g)[r].n = maxi + 1;
00191     }
00192 
00193     for (c = 1; c <= GD_n_cluster(g); c++)
00194         rebuild_vlists(GD_clust(g)[c]);
00195 }
00196 
00197 void dot_concentrate(graph_t * g)
00198 {
00199     int c, r, leftpos, rightpos;
00200     node_t *left, *right;
00201 
00202     if (GD_maxrank(g) - GD_minrank(g) <= 1)
00203         return;
00204     /* this is the downward looking pass. r is a candidate rank. */
00205     for (r = 1; GD_rank(g)[r + 1].n; r++) {
00206         for (leftpos = 0; leftpos < GD_rank(g)[r].n; leftpos++) {
00207             left = GD_rank(g)[r].v[leftpos];
00208             if (downcandidate(left) == FALSE)
00209                 continue;
00210             for (rightpos = leftpos + 1; rightpos < GD_rank(g)[r].n;
00211                  rightpos++) {
00212                 right = GD_rank(g)[r].v[rightpos];
00213                 if (bothdowncandidates(left, right) == FALSE)
00214                     break;
00215             }
00216             if (rightpos - leftpos > 1)
00217                 mergevirtual(g, r, leftpos, rightpos - 1, DOWN);
00218         }
00219     }
00220     /* this is the corresponding upward pass */
00221     while (r > 0) {
00222         for (leftpos = 0; leftpos < GD_rank(g)[r].n; leftpos++) {
00223             left = GD_rank(g)[r].v[leftpos];
00224             if (upcandidate(left) == FALSE)
00225                 continue;
00226             for (rightpos = leftpos + 1; rightpos < GD_rank(g)[r].n;
00227                  rightpos++) {
00228                 right = GD_rank(g)[r].v[rightpos];
00229                 if (bothupcandidates(left, right) == FALSE)
00230                     break;
00231             }
00232             if (rightpos - leftpos > 1)
00233                 mergevirtual(g, r, leftpos, rightpos - 1, UP);
00234         }
00235         r--;
00236     }
00237     if (setjmp(jbuf)) {
00238         agerr(AGPREV, "concentrate=true may not work correctly.\n");
00239         return;
00240     }
00241     for (c = 1; c <= GD_n_cluster(g); c++)
00242         rebuild_vlists(GD_clust(g)[c]);
00243 }