|
Graphviz 2.29.20120208.0545
|
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 "blocktree.h" 00016 00017 static void addNode(block_t * bp, Agnode_t * n) 00018 { 00019 #ifndef WITH_CGRAPH 00020 aginsert(bp->sub_graph, n); 00021 #else /* WITH_CGRAPH */ 00022 agsubnode(bp->sub_graph, n,1); 00023 #endif /* WITH_CGRAPH */ 00024 BLOCK(n) = bp; 00025 } 00026 00027 static Agraph_t *makeBlockGraph(Agraph_t * g, circ_state * state) 00028 { 00029 char name[SMALLBUF]; 00030 Agraph_t *subg; 00031 00032 sprintf(name, "_block_%d", state->blockCount++); 00033 #ifndef WITH_CGRAPH 00034 subg = agsubg(g, name); 00035 #else /* WITH_CGRAPH */ 00036 subg = agsubg(g, name,1); 00037 agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE); //node custom data 00038 #endif /* WITH_CGRAPH */ 00039 return subg; 00040 } 00041 00042 static block_t *makeBlock(Agraph_t * g, circ_state * state) 00043 { 00044 Agraph_t *subg = makeBlockGraph(g, state); 00045 block_t *bp = mkBlock(subg); 00046 00047 return bp; 00048 } 00049 00050 typedef struct { 00051 Agedge_t *top; 00052 int sz; 00053 } estack; 00054 00055 static void 00056 push (estack* s, Agedge_t* e) 00057 { 00058 ENEXT(e) = s->top; 00059 s->top = e; 00060 s->sz += 1; 00061 } 00062 00063 static Agedge_t* 00064 pop (estack* s) 00065 { 00066 Agedge_t *top = s->top; 00067 00068 if (top) { 00069 assert(s->sz > 0); 00070 s->top = ENEXT(top); 00071 s->sz -= 1; 00072 } else { 00073 assert(0); 00074 } 00075 00076 return top; 00077 } 00078 00079 00080 /* dfs: 00081 * 00082 * Current scheme adds articulation point to first non-trivial child 00083 * block. If none exists, it will be added to its parent's block, if 00084 * non-trivial, or else given its own block. 00085 * 00086 * FIX: 00087 * This should be modified to: 00088 * - allow user to specify which block gets a node, perhaps on per-node basis. 00089 * - if an articulation point is not used in one of its non-trivial blocks, 00090 * dummy edges should be added to preserve biconnectivity 00091 * - turn on user-supplied blocks. 00092 * - Post-process to move articulation point to largest block 00093 */ 00094 static void dfs(Agraph_t * g, Agnode_t * u, circ_state * state, int isRoot, estack* stk) 00095 { 00096 Agedge_t *e; 00097 Agnode_t *v; 00098 00099 LOWVAL(u) = VAL(u) = state->orderCount++; 00100 for (e = agfstedge(g, u); e; e = agnxtedge(g, e, u)) { 00101 v = aghead (e); 00102 if (v == u) { 00103 v = agtail(e); 00104 if (!EDGEORDER(e)) EDGEORDER(e) = -1; 00105 } 00106 else { 00107 if (!EDGEORDER(e)) EDGEORDER(e) = 1; 00108 } 00109 00110 if (VAL(v) == 0) { /* Since VAL(root) == 0, it gets treated as artificial cut point */ 00111 PARENT(v) = u; 00112 push(stk, e); 00113 dfs(g, v, state, 0, stk); 00114 LOWVAL(u) = MIN(LOWVAL(u), LOWVAL(v)); 00115 if (LOWVAL(v) >= VAL(u)) { /* u is an articulation point */ 00116 block_t *block = NULL; 00117 Agnode_t *np; 00118 Agedge_t *ep; 00119 do { 00120 ep = pop(stk); 00121 if (EDGEORDER(ep) == 1) 00122 np = aghead (ep); 00123 else 00124 np = agtail (ep); 00125 if (!BLOCK(np)) { 00126 if (!block) 00127 block = makeBlock(g, state); 00128 addNode(block, np); 00129 } 00130 } while (ep != e); 00131 if (block) { /* If block != NULL, it's not empty */ 00132 if (!BLOCK(u) && blockSize (block) > 1) 00133 addNode(block, u); 00134 if (isRoot && (BLOCK(u) == block)) 00135 insertBlock(&state->bl, block); 00136 else 00137 appendBlock(&state->bl, block); 00138 } 00139 } 00140 } else if (PARENT(u) != v) { 00141 LOWVAL(u) = MIN(LOWVAL(u), VAL(v)); 00142 } 00143 } 00144 if (isRoot && !BLOCK(u)) { 00145 block_t *block = makeBlock(g, state); 00146 addNode(block, u); 00147 insertBlock(&state->bl, block); 00148 } 00149 } 00150 00151 00152 /* find_blocks: 00153 */ 00154 static void find_blocks(Agraph_t * g, circ_state * state) 00155 { 00156 Agnode_t *n; 00157 Agnode_t *root = NULL; 00158 estack stk; 00159 00160 /* check to see if there is a node which is set to be the root 00161 */ 00162 if (state->rootname) { 00163 root = agfindnode(g, state->rootname); 00164 } 00165 if (!root && state->N_root) { 00166 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00167 if (late_bool(ORIGN(n), state->N_root, 0)) { 00168 root = n; 00169 break; 00170 } 00171 } 00172 } 00173 00174 if (!root) 00175 root = agfstnode(g); 00176 if (Verbose) 00177 fprintf (stderr, "root = %s\n", agnameof(root)); 00178 stk.sz = 0; 00179 stk.top = NULL; 00180 dfs(g, root, state, 1, &stk); 00181 00182 } 00183 00184 /* create_block_tree: 00185 * Construct block tree by peeling nodes from block list in state. 00186 * When done, return root. The block list is empty 00187 * FIX: use largest block as root 00188 */ 00189 block_t *createBlocktree(Agraph_t * g, circ_state * state) 00190 { 00191 block_t *bp; 00192 block_t *next; 00193 block_t *root; 00194 int min; 00195 /* int ordercnt; */ 00196 00197 find_blocks(g, state); 00198 00199 bp = state->bl.first; /* if root chosen, will be first */ 00200 /* Otherwise, just pick first as root */ 00201 root = bp; 00202 00203 /* Find node with minimum VAL value to find parent block */ 00204 /* FIX: Should be some way to avoid search below. */ 00205 /* ordercnt = state->orderCount; */ 00206 for (bp = bp->next; bp; bp = next) { 00207 Agnode_t *n; 00208 Agnode_t *parent; 00209 Agnode_t *child; 00210 Agraph_t *subg = bp->sub_graph; 00211 00212 child = n = agfstnode(subg); 00213 00214 min = VAL(n); 00215 parent = PARENT(n); 00216 for (n = agnxtnode(subg, n); n; n = agnxtnode(subg, n)) { 00217 if (VAL(n) < min) { 00218 child = n; 00219 min = VAL(n); 00220 parent = PARENT(n); 00221 } 00222 } 00223 SET_PARENT(parent); 00224 CHILD(bp) = child; 00225 next = bp->next; /* save next since list insertion destroys it */ 00226 appendBlock(&(BLOCK(parent)->children), bp); 00227 } 00228 initBlocklist(&state->bl); /* zero out list */ 00229 return root; 00230 } 00231 00232 void freeBlocktree(block_t * bp) 00233 { 00234 block_t *child; 00235 block_t *next; 00236 00237 for (child = bp->children.first; child; child = next) { 00238 next = child->next; 00239 freeBlocktree(child); 00240 } 00241 00242 freeBlock(bp); 00243 } 00244 00245 #ifdef DEBUG 00246 static void indent(int i) 00247 { 00248 while (i--) 00249 fputs(" ", stderr); 00250 } 00251 00252 void print_blocktree(block_t * sn, int depth) 00253 { 00254 block_t *child; 00255 Agnode_t *n; 00256 Agraph_t *g; 00257 00258 indent(depth); 00259 g = sn->sub_graph; 00260 fprintf(stderr, "%s:", g->name); 00261 for (n = agfstnode(g); n; n = agnxtnode(g, n)) { 00262 fprintf(stderr, " %s", n->name); 00263 } 00264 fputs("\n", stderr); 00265 00266 depth++; 00267 for (child = sn->children.first; child; child = child->next) { 00268 print_blocktree(child, depth); 00269 } 00270 } 00271 00272 #endif
1.7.4