/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/circogen/blockpath.c

Go to the documentation of this file.
00001 /* $Id: blockpath.c,v 1.4 2008/03/03 23:01:51 ellson Exp $ $Revision: 1.4 $ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /**********************************************************
00005 *      This software is part of the graphviz package      *
00006 *                http://www.graphviz.org/                 *
00007 *                                                         *
00008 *            Copyright (c) 1994-2004 AT&T Corp.           *
00009 *                and is licensed under the                *
00010 *            Common Public License, Version 1.0           *
00011 *                      by AT&T Corp.                      *
00012 *                                                         *
00013 *        Information and Software Systems Research        *
00014 *              AT&T Research, Florham Park NJ             *
00015 **********************************************************/
00016 
00017 
00018 #include        "blockpath.h"
00019 #include        "edgelist.h"
00020 #include        "nodeset.h"
00021 #include        "deglist.h"
00022 
00023 /* The code below lays out a single block on a circle.
00024  */
00025 
00026 /* We use the unused fields order and to_orig in cloned nodes and edges */
00027 #define ORIGE(e)  (ED_to_orig(e))
00028 
00029 /* clone_graph:
00030  * Create two copies of the argument graph
00031  * One is a subgraph, the other is an actual copy since we will be
00032  * adding edges to it.
00033  */
00034 static Agraph_t *clone_graph(Agraph_t * ing, Agraph_t ** xg)
00035 {
00036     Agraph_t *clone;
00037     Agraph_t *xclone;
00038     Agnode_t *n;
00039     Agnode_t *xn;
00040     Agnode_t *xh;
00041     Agedge_t *e;
00042     Agedge_t *xe;
00043     char gname[SMALLBUF];
00044     static int id = 0;
00045 
00046     sprintf(gname, "_clone_%d", id++);
00047     clone = agsubg(ing, gname);
00048     sprintf(gname, "_clone_%d", id++);
00049     xclone = agopen(gname, ing->kind);
00050 
00051     for (n = agfstnode(ing); n; n = agnxtnode(ing, n)) {
00052         aginsert(clone, n);
00053         xn = agnode(xclone, n->name);
00054         CLONE(n) = xn;
00055     }
00056 
00057     for (n = agfstnode(ing); n; n = agnxtnode(ing, n)) {
00058         xn = CLONE(n);
00059         for (e = agfstout(ing, n); e; e = agnxtout(ing, e)) {
00060             aginsert(clone, e);
00061             xh = CLONE(e->head);
00062             xe = agedge(xclone, xn, xh);
00063             ORIGE(xe) = e;
00064             DEGREE(xn) += 1;
00065             DEGREE(xh) += 1;
00066         }
00067     }
00068     *xg = xclone;
00069 #ifdef OLD
00070     clone = agopen("clone", root->kind);
00071 
00072     for (n = agfstnode(root); n; n = agnxtnode(root, n)) {
00073         cn = agnode(clone, n->name);
00074         ND_alg(cn) = DATA(n);
00075         BCDONE(cn) = 0;
00076     }
00077 
00078     for (n = agfstnode(root); n; n = agnxtnode(root, n)) {
00079         Agnode_t *t = agnode(clone, n);
00080         for (e = agfstout(root, n); e; e = agnxtout(root, e)) {
00081             Agnode_t *h = agnode(clone, e->head->name);
00082             agedge(clone, t, h);
00083         }
00084     }
00085 #endif
00086     return clone;
00087 }
00088 
00089 /* fillList:
00090  * Add nodes to deg_list, which stores them by degree.
00091  */
00092 static deglist_t *getList(Agraph_t * g)
00093 {
00094     deglist_t *dl = mkDeglist();
00095     Agnode_t *n;
00096 
00097     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00098         insertDeglist(dl, n);
00099     }
00100     return dl;
00101 }
00102 
00103 /* find_pair_edges:
00104  */
00105 static void find_pair_edges(Agraph_t * g, Agnode_t * n, Agraph_t * outg)
00106 {
00107     Agnode_t **neighbors_with;
00108     Agnode_t **neighbors_without;
00109 
00110     Agedge_t *e;
00111     Agedge_t *ep;
00112     Agedge_t *ex;
00113     Agnode_t *n1;
00114     Agnode_t *n2;
00115     int has_pair_edge;
00116     int diff;
00117     int has_pair_count = 0;
00118     int no_pair_count = 0;
00119     int node_degree;
00120     int edge_cnt = 0;
00121 
00122     node_degree = DEGREE(n);
00123     neighbors_with = N_GNEW(node_degree, Agnode_t *);
00124     neighbors_without = N_GNEW(node_degree, Agnode_t *);
00125 
00126     for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
00127         n1 = e->head;
00128         if (n1 == n)
00129             n1 = e->tail;
00130         has_pair_edge = 0;
00131         for (ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
00132             if (ep == e)
00133                 continue;
00134             n2 = ep->head;
00135             if (n2 == n)
00136                 n2 = ep->tail;
00137             ex = agfindedge(g, n1, n2);
00138             if (ex) {
00139                 has_pair_edge = 1;
00140                 if (n1 < n2) {  /* count edge only once */
00141                     edge_cnt++;
00142                     if (ORIGE(ex)) {
00143                         agdelete(outg, ORIGE(ex));
00144                         ORIGE(ex) = 0;  /* delete only once */
00145                     }
00146                 }
00147             }
00148         }
00149         if (has_pair_edge) {
00150             neighbors_with[has_pair_count] = n1;
00151             has_pair_count++;
00152         } else {
00153             neighbors_without[no_pair_count] = n1;
00154             no_pair_count++;
00155         }
00156     }
00157 
00158     diff = node_degree - 1 - edge_cnt;
00159     if (diff > 0) {
00160         int mark;
00161         Agnode_t *hp;
00162         Agnode_t *tp;
00163 
00164         if (diff < no_pair_count) {
00165             for (mark = 0; mark < no_pair_count; mark += 2) {
00166                 if ((mark + 1) >= no_pair_count)
00167                     break;
00168                 tp = neighbors_without[mark];
00169                 hp = neighbors_without[mark + 1];
00170                 agedge(g, tp, hp);
00171                 DEGREE(tp)++;
00172                 DEGREE(hp)++;
00173                 diff--;
00174             }
00175 
00176             mark = 2;
00177             while (diff > 0) {
00178                 tp = neighbors_without[0];
00179                 hp = neighbors_without[mark];
00180                 agedge(g, tp, hp);
00181                 DEGREE(tp)++;
00182                 DEGREE(hp)++;
00183                 mark++;
00184                 diff--;
00185             }
00186         }
00187 
00188         else if (diff == no_pair_count) {
00189             tp = neighbors_with[0];
00190             for (mark = 0; mark < no_pair_count; mark++) {
00191                 hp = neighbors_without[mark];
00192                 agedge(g, tp, hp);
00193                 DEGREE(tp)++;
00194                 DEGREE(hp)++;
00195             }
00196         }
00197     }
00198 
00199     free(neighbors_without);
00200     free(neighbors_with);
00201 }
00202 
00203 /* remove_pair_edges:
00204  * Create layout skeleton of ing.
00205  * Why is returned graph connected?
00206  */
00207 static Agraph_t *remove_pair_edges(Agraph_t * ing)
00208 {
00209     int counter = 0;
00210     int nodeCount;
00211     Agraph_t *outg;
00212     Agraph_t *g;
00213     deglist_t *dl;
00214     Agnode_t *currnode, *adjNode;
00215     Agedge_t *e;
00216 
00217     outg = clone_graph(ing, &g);
00218     nodeCount = agnnodes(g);
00219     dl = getList(g);
00220 
00221     while (counter < (nodeCount - 3)) {
00222         currnode = firstDeglist(dl);
00223 
00224         /* Remove all adjacent nodes since they have to be reinserted */
00225         for (e = agfstedge(g, currnode); e; e = agnxtedge(g, e, currnode)) {
00226             adjNode = e->head;
00227             if (currnode == adjNode)
00228                 adjNode = e->tail;
00229             removeDeglist(dl, adjNode);
00230         }
00231 
00232         find_pair_edges(g, currnode, outg);
00233 
00234         for (e = agfstedge(g, currnode); e; e = agnxtedge(g, e, currnode)) {
00235             adjNode = e->head;
00236             if (currnode == adjNode)
00237                 adjNode = e->tail;
00238 
00239             DEGREE(adjNode)--;
00240             insertDeglist(dl, adjNode);
00241         }
00242 
00243         agdelete(g, currnode);
00244 
00245         counter++;
00246     }
00247 
00248     agclose(g);
00249     freeDeglist(dl);
00250     return outg;
00251 }
00252 
00253 static void
00254 measure_distance(Agnode_t * n, Agnode_t * ancestor, int dist,
00255                  Agnode_t * change)
00256 {
00257     Agnode_t *parent;
00258 
00259     parent = TPARENT(ancestor);
00260     if (parent == NULL)
00261         return;
00262 
00263     dist++;
00264 
00265     /* check parent to see if it has other leaf paths at greater distance
00266        than the context node.
00267        set the path/distance of the leaf at this ancestor node */
00268 
00269     if (DISTONE(parent) == 0) {
00270         LEAFONE(parent) = n;
00271         DISTONE(parent) = dist;
00272     } else if (dist > DISTONE(parent)) {
00273         if (LEAFONE(parent) != change) {
00274             if (!DISTTWO(parent) || (LEAFTWO(parent) != change))
00275                 change = LEAFONE(parent);
00276             LEAFTWO(parent) = LEAFONE(parent);
00277             DISTTWO(parent) = DISTONE(parent);
00278         }
00279         LEAFONE(parent) = n;
00280         DISTONE(parent) = dist;
00281     } else if (dist > DISTTWO(parent)) {
00282         LEAFTWO(parent) = n;
00283         DISTTWO(parent) = dist;
00284         return;
00285     } else
00286         return;
00287 
00288     measure_distance(n, parent, dist, change);
00289 }
00290 
00291 /* find_longest_path:
00292  * Find and return longest path in tree.
00293  */
00294 static nodelist_t *find_longest_path(Agraph_t * tree)
00295 {
00296     Agnode_t *n;
00297     Agedge_t *e;
00298     Agnode_t *common = 0;
00299     nodelist_t *path;
00300     nodelist_t *endPath;
00301     int maxlength = 0;
00302     int length;
00303 
00304     if (agnnodes(tree) == 1) {
00305         path = mkNodelist();
00306         n = agfstnode(tree);
00307         appendNodelist(path, NULL, n);
00308         SET_ONPATH(n);
00309         return path;
00310     }
00311 
00312     for (n = agfstnode(tree); n; n = agnxtnode(tree, n)) {
00313         int count = 0;
00314         for (e = agfstedge(tree, n); e; e = agnxtedge(tree, e, n)) {
00315             count++;
00316         }
00317         if (count == 1)
00318             measure_distance(n, n, 0, NULL);
00319     }
00320 
00321     /* find the branch node rooted at the longest path */
00322     for (n = agfstnode(tree); n; n = agnxtnode(tree, n)) {
00323         length = DISTONE(n) + DISTTWO(n);
00324         if (length > maxlength) {
00325             common = n;
00326             maxlength = length;
00327         }
00328     }
00329 
00330     path = mkNodelist();
00331     for (n = LEAFONE(common); n != common; n = TPARENT(n)) {
00332         appendNodelist(path, NULL, n);
00333         SET_ONPATH(n);
00334     }
00335     appendNodelist(path, NULL, common);
00336     SET_ONPATH(common);
00337 
00338     if (DISTTWO(common)) {      /* 2nd path might be empty */
00339         endPath = mkNodelist();
00340         for (n = LEAFTWO(common); n != common; n = TPARENT(n)) {
00341             appendNodelist(endPath, NULL, n);
00342             SET_ONPATH(n);
00343         }
00344         reverseAppend(path, endPath);
00345     }
00346 
00347     return path;
00348 }
00349 
00350 /* dfs:
00351  * Simple depth first search, adding traversed edges to tree.
00352  */
00353 static void dfs(Agraph_t * g, Agnode_t * n, Agraph_t * tree)
00354 {
00355     Agedge_t *e;
00356     Agnode_t *neighbor;
00357 
00358     SET_VISITED(n);
00359     for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
00360         neighbor = e->head;
00361         if (neighbor == n)
00362             neighbor = e->tail;
00363 
00364         if (!VISITED(neighbor)) {
00365             /* add the edge to the dfs tree */
00366             aginsert(tree, e);
00367             TPARENT(neighbor) = n;
00368             dfs(g, neighbor, tree);
00369         }
00370     }
00371 }
00372 
00373 /* spanning_tree:
00374  * Construct spanning forest of g as subgraph
00375  */
00376 static Agraph_t *spanning_tree(Agraph_t * g)
00377 {
00378     Agnode_t *n;
00379     Agraph_t *tree;
00380     char gname[SMALLBUF];
00381     static int id = 0;
00382 
00383     sprintf(gname, "_span_%d", id++);
00384     tree = agsubg(g, gname);
00385     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00386         aginsert(tree, n);
00387         DISTONE(n) = 0;
00388         DISTTWO(n) = 0;
00389         UNSET_VISITED(n);
00390     }
00391 
00392     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00393         if (!VISITED(n)) {
00394             TPARENT(n) = NULL;
00395             dfs(g, n, tree);
00396         }
00397     }
00398 
00399     return tree;
00400 }
00401 
00402 /* block_graph:
00403  * Add induced edges.
00404  */
00405 static void block_graph(Agraph_t * g, block_t * sn)
00406 {
00407     Agnode_t *n;
00408     Agedge_t *e;
00409     Agraph_t *subg = sn->sub_graph;
00410 
00411     for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
00412         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00413             if (BLOCK(e->head) == sn)
00414                 aginsert(subg, e);
00415         }
00416     }
00417 }
00418 
00419 static int count_all_crossings(nodelist_t * list, Agraph_t * subg)
00420 {
00421     nodelistitem_t *item;
00422     edgelist *openEdgeList = init_edgelist();
00423     Agnode_t *n;
00424     Agedge_t *e;
00425     int crossings = 0;
00426     int order = 1;
00427 
00428     for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
00429         for (e = agfstout(subg, n); e; e = agnxtout(subg, e)) {
00430             EDGEORDER(e) = 0;
00431         }
00432     }
00433 
00434     for (item = list->first; item; item = item->next) {
00435         n = item->curr;
00436 
00437         for (e = agfstedge(subg, n); e; e = agnxtedge(subg, e, n)) {
00438             if (EDGEORDER(e) > 0) {
00439                 edgelistitem *eitem;
00440                 Agedge_t *ep;
00441 
00442                 for (eitem = (edgelistitem *) dtfirst(openEdgeList); eitem;
00443                      eitem =
00444                      (edgelistitem *) dtnext(openEdgeList, eitem)) {
00445                     ep = eitem->edge;
00446                     if (EDGEORDER(ep) > EDGEORDER(e)) {
00447                         if ((ep->head != n) && (ep->tail != n))
00448                             crossings++;
00449                     }
00450                 }
00451                 remove_edge(openEdgeList, e);
00452             }
00453         }
00454 
00455         for (e = agfstedge(subg, n); e; e = agnxtedge(subg, e, n)) {
00456             if (EDGEORDER(e) == 0) {
00457                 EDGEORDER(e) = order;
00458                 add_edge(openEdgeList, e);
00459             }
00460         }
00461         order++;
00462     }
00463 
00464     free_edgelist(openEdgeList);
00465     return crossings;
00466 }
00467 
00468 #define CROSS_ITER 10
00469 
00470 /* reduce:
00471  * Attempt to reduce edge crossings by moving nodes.
00472  * Original crossing count is in cnt; final count is returned there.
00473  * list is the original list; return the best list found.
00474  */
00475 static nodelist_t *reduce(nodelist_t * list, Agraph_t * subg, int *cnt)
00476 {
00477     Agnode_t *curnode;
00478     Agedge_t *e;
00479     Agnode_t *neighbor;
00480     nodelist_t *listCopy;
00481     int crossings, j, newCrossings;
00482 
00483     crossings = *cnt;
00484     for (curnode = agfstnode(subg); curnode;
00485          curnode = agnxtnode(subg, curnode)) {
00486         /*  move curnode next to its neighbors */
00487         for (e = agfstedge(subg, curnode); e;
00488              e = agnxtedge(subg, e, curnode)) {
00489             neighbor = e->tail;
00490             if (neighbor == curnode)
00491                 neighbor = e->head;
00492 
00493             for (j = 0; j < 2; j++) {
00494                 listCopy = cloneNodelist(list);
00495                 insertNodelist(list, curnode, neighbor, j);
00496                 newCrossings = count_all_crossings(list, subg);
00497                 if (newCrossings < crossings) {
00498                     crossings = newCrossings;
00499                     freeNodelist(listCopy);
00500                     if (crossings == 0) {
00501                         *cnt = 0;
00502                         return list;
00503                     }
00504                 } else {
00505                     freeNodelist(list);
00506                     list = listCopy;
00507                 }
00508             }
00509         }
00510     }
00511     *cnt = crossings;
00512     return list;
00513 }
00514 
00515 static nodelist_t *reduce_edge_crossings(nodelist_t * list,
00516                                          Agraph_t * subg)
00517 {
00518     int i, crossings, origCrossings;
00519 
00520     crossings = count_all_crossings(list, subg);
00521     if (crossings == 0)
00522         return list;
00523 
00524     for (i = 0; i < CROSS_ITER; i++) {
00525         origCrossings = crossings;
00526         list = reduce(list, subg, &crossings);
00527         /* return if no crossings or no improvement */
00528         if ((origCrossings == crossings) || (crossings == 0))
00529             return list;
00530     }
00531     return list;
00532 }
00533 
00534 /* largest_nodesize:
00535  * Return max dimension of nodes on list
00536  */
00537 static double largest_nodesize(nodelist_t * list)
00538 {
00539     Agnode_t *n;
00540     nodelistitem_t *item;
00541     double size = 0;
00542 
00543     for (item = list->first; item; item = item->next) {
00544         n = ORIGN(item->curr);
00545         if (ND_width(n) > size)
00546             size = ND_width(n);
00547         if (ND_height(n) > size)
00548             size = ND_height(n);
00549     }
00550     return size;
00551 }
00552 
00553 /* place_node:
00554  * Add n to list. By construction, n is not in list at start.
00555  */
00556 static void place_node(Agraph_t * g, Agnode_t * n, nodelist_t * list)
00557 {
00558     Agedge_t *e;
00559     int placed = 0;
00560     nodelist_t *neighbors = mkNodelist();
00561     nodelistitem_t *one, *two;
00562 
00563     for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00564         appendNodelist(neighbors, NULL, e->head);
00565         SET_NEIGHBOR(e->head);
00566     }
00567     for (e = agfstin(g, n); e; e = agnxtin(g, e)) {
00568         appendNodelist(neighbors, NULL, e->tail);
00569         SET_NEIGHBOR(e->tail);
00570     }
00571 
00572     /* Look for 2 neighbors consecutive on list */
00573     if (sizeNodelist(neighbors) >= 2) {
00574         for (one = list->first; one; one = one->next) {
00575             if (one == list->last)
00576                 two = list->first;
00577             else
00578                 two = one->next;
00579 
00580             if (NEIGHBOR(one->curr) && NEIGHBOR(two->curr)) {
00581                 appendNodelist(list, one, n);
00582                 placed = 1;
00583                 break;
00584             }
00585         }
00586     }
00587 
00588     /* Find any neighbor on list */
00589     if (!placed && sizeNodelist(neighbors) > 0) {
00590         for (one = list->first; one; one = one->next) {
00591             if (NEIGHBOR(one->curr)) {
00592                 appendNodelist(list, one, n);
00593                 placed = 1;
00594                 break;
00595             }
00596         }
00597     }
00598 
00599     if (!placed)
00600         appendNodelist(list, NULL, n);
00601 
00602     for (one = neighbors->first; one; one = one->next)
00603         UNSET_NEIGHBOR(one->curr);
00604     freeNodelist(neighbors);
00605 }
00606 
00607 /* place_residual_nodes:
00608  * Add nodes not in list to list.
00609  */
00610 static void place_residual_nodes(Agraph_t * g, nodelist_t * list)
00611 {
00612     Agnode_t *n;
00613 
00614     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00615         if (!ONPATH(n))
00616             place_node(g, n, list);
00617     }
00618 }
00619 
00620 nodelist_t *layout_block(Agraph_t * g, block_t * sn, double min_dist)
00621 {
00622     Agnode_t *n;
00623     Agraph_t *copyG, *tree, *subg;
00624     nodelist_t *longest_path;
00625     nodelistitem_t *item;
00626     int N, k;
00627     double theta, radius, largest_node;
00628     largest_node = 0;
00629 
00630     subg = sn->sub_graph;
00631     block_graph(g, sn);         /* add induced edges */
00632 
00633     copyG = remove_pair_edges(subg);
00634 
00635     tree = spanning_tree(copyG);
00636     longest_path = find_longest_path(tree);
00637     place_residual_nodes(subg, longest_path);
00638     /* at this point, longest_path is a list of all nodes in the block */
00639 
00640     /* apply crossing reduction algorithms here */
00641     longest_path = reduce_edge_crossings(longest_path, subg);
00642 
00643     N = sizeNodelist(longest_path);
00644     largest_node = largest_nodesize(longest_path);
00645     /* N*(min_dist+largest_node) is roughly circumference of required circle */
00646     if (N == 1)
00647         radius = 0;
00648     else
00649         radius = (N * (min_dist + largest_node)) / (2 * M_PI);
00650 
00651     for (item = longest_path->first; item; item = item->next) {
00652         n = item->curr;
00653         if (ISPARENT(n)) {
00654             /* QUESTION: Why is only one parent realigned? */
00655             realignNodelist(longest_path, item);
00656             break;
00657         }
00658     }
00659 
00660     k = 0;
00661     for (item = longest_path->first; item; item = item->next) {
00662         n = item->curr;
00663         POSITION(n) = k;
00664         PSI(n) = 0.0;
00665         theta = k * ((2.0 * M_PI) / N);
00666 
00667         ND_pos(n)[0] = radius * cos(theta);
00668         ND_pos(n)[1] = radius * sin(theta);
00669 
00670         k++;
00671     }
00672 
00673     if (N == 1)
00674         sn->radius = largest_node / 2;
00675     else
00676         sn->radius = radius;
00677     sn->rad0 = sn->radius;
00678 
00679     /* initialize parent pos */
00680     sn->parent_pos = -1;
00681 
00682     agclose(copyG);
00683     return longest_path;
00684 }
00685 
00686 #ifdef DEBUG
00687 void prTree(Agraph_t * g)
00688 {
00689     Agnode_t *n;
00690 
00691     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00692         if (TPARENT(n))
00693             fprintf(stderr, "%s -> %s\n", n->name, TPARENT(n)->name);
00694     }
00695 }
00696 #endif

Generated on Mon Mar 31 19:03:23 2008 for Graphviz by  doxygen 1.5.1