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

Go to the documentation of this file.
00001 /* $Id: circpos.c,v 1.3 2008/03/03 23:01:51 ellson Exp $ $Revision: 1.3 $ */
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 #ifdef HAVE_CONFIG_H
00019 #include "config.h"
00020 #endif
00021 
00022 /* TODO:
00023  * If cut point is in exactly 2 blocks, expand block circles to overlap
00024  * especially in the case where one block is the sole child of the other.
00025  */
00026 
00027 #include        "blockpath.h"
00028 
00029 #define         LEN(x,y) (sqrt((x)*(x) + (y)*(y)))
00030 
00031 /* getRotation:
00032  * The function determines how much the block should be rotated
00033  * for best positioning with parent, assuming its center is at x and y
00034  * relative to the parent.
00035  * angle gives the angle of the new position, i.e., tan(angle) = y/x.
00036  * If sn has 2 nodes, we arrange the line of the 2 normal to angle.
00037  * If sn has 1 node, parent_pos has already been set to the 
00038  * correct angle assuming no rotation.
00039  * Otherwise, we find the node in sn connected to the parent and rotate
00040  * the block so that it is closer or at least visible to its node in the
00041  * parent.
00042  *
00043  * For COALESCED blocks, if neighbor is in left half plane, 
00044  * use unCOALESCED case.
00045  * Else let theta be angle, R = LEN(x,y), pho the radius of actual 
00046  * child block, phi be angle of neighbor in actual child block,
00047  * and r the distance from center of coalesced block to center of 
00048  * actual block. Then, the angle to rotate the coalesced block to
00049  * that the edge from the parent is tangent to the neighbor on the
00050  * actual child block circle is
00051  *    alpha = theta + M_PI/2 - phi - arcsin((l/R)*(sin B))
00052  * where l = r - rho/(cos phi) and beta = M_PI/2 + phi.
00053  * Thus, 
00054  *    alpha = theta + M_PI/2 - phi - arcsin((l/R)*(cos phi))
00055  */
00056 static double
00057 getRotation(block_t * sn, Agraph_t * g, double x, double y, double theta)
00058 {
00059     double mindist;
00060     Agraph_t *subg;
00061     /* Agedge_t* e; */
00062     Agnode_t *n, *closest_node, *neighbor;
00063     nodelist_t *list;
00064     double len, newX, newY;
00065     int count;
00066 
00067     subg = sn->sub_graph;
00068 #ifdef OLD
00069     parent = sn->parent;
00070 #endif
00071 
00072     list = sn->circle_list;
00073 
00074     if (sn->parent_pos >= 0) {
00075         theta += M_PI - sn->parent_pos;
00076         if (theta < 0)
00077             theta += 2 * M_PI;
00078 
00079         return theta;
00080     }
00081 
00082     count = sizeNodelist(list);
00083     if (count == 2) {
00084         return (theta - M_PI / 2.0);
00085     }
00086 
00087     /* Find node in block connected to block's parent */
00088     neighbor = CHILD(sn);
00089 #ifdef OLD
00090     for (e = agfstedge(g, parent); e; e = agnxtedge(g, e, parent)) {
00091         n = e->head;
00092         if (n == parent)
00093             n = e->tail;
00094 
00095         if ((BLOCK(n) == sn) && (PARENT(n) == parent)) {
00096             neighbor = n;
00097             break;
00098         }
00099     }
00100 #endif
00101     newX = ND_pos(neighbor)[0] + x;
00102     newY = ND_pos(neighbor)[1] + y;
00103     mindist = LEN(newX, newY);
00104     closest_node = neighbor;
00105 
00106     for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
00107         if (n == neighbor)
00108             continue;
00109 
00110         newX = ND_pos(n)[0] + x;
00111         newY = ND_pos(n)[1] + y;
00112 
00113         len = LEN(newX, newY);
00114         if (len < mindist) {
00115             mindist = len;
00116             closest_node = n;
00117         }
00118     }
00119 
00120     /* if((neighbor != closest_node) && !ISPARENT(neighbor)) { */
00121     if (neighbor != closest_node) {
00122         double rho = sn->rad0;
00123         double r = sn->radius - rho;
00124         double n_x = ND_pos(neighbor)[0];
00125         if (COALESCED(sn) && (-r < n_x)) {
00126             double R = LEN(x, y);
00127             double n_y = ND_pos(neighbor)[1];
00128             double phi = atan2(n_y, n_x + r);
00129             double l = r - rho / (cos(phi));
00130 
00131             theta += M_PI / 2.0 - phi - asin((l / R) * (cos(phi)));
00132         } else {                /* Origin still at center of this block */
00133             double phi = atan2(ND_pos(neighbor)[1], ND_pos(neighbor)[0]);
00134             theta += M_PI - phi - PSI(neighbor);
00135             if (theta > 2 * M_PI)
00136                 theta -= 2 * M_PI;
00137         }
00138     } else
00139         theta = 0;
00140     return theta;
00141 }
00142 
00143 
00144 /* applyDelta:
00145  * Recursively apply rotation rotate followed by translation (x,y)
00146  * to block sn and its children.
00147  */
00148 static void applyDelta(block_t * sn, double x, double y, double rotate)
00149 {
00150     block_t *child;
00151     Agraph_t *subg;
00152     Agnode_t *n;
00153 
00154     subg = sn->sub_graph;
00155 
00156     for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
00157         double X, Y;
00158 
00159         if (rotate != 0) {
00160             double tmpX, tmpY;
00161             double cosR, sinR;
00162 
00163             tmpX = ND_pos(n)[0];
00164             tmpY = ND_pos(n)[1];
00165             cosR = cos(rotate);
00166             sinR = sin(rotate);
00167 
00168             X = tmpX * cosR - tmpY * sinR;
00169             Y = tmpX * sinR + tmpY * cosR;
00170         } else {
00171             X = ND_pos(n)[0];
00172             Y = ND_pos(n)[1];
00173         }
00174 
00175         /* translate */
00176         ND_pos(n)[0] = X + x;
00177         ND_pos(n)[1] = Y + y;
00178     }
00179 
00180     for (child = sn->children.first; child; child = child->next)
00181         applyDelta(child, x, y, rotate);
00182 }
00183 
00184 /* firstangle and lastangle give the range of child angles.
00185  * These are set and used only when a block has just 1 node.
00186  * And are used to give the center angle between the two extremes.
00187  * The parent will then be attached at PI - center angle (parent_pos).
00188  * If this block has no children, this is PI. Otherwise, doParent will
00189  * be called once with the blocks node. firstangle will be 0, with
00190  * succeeding angles increasing. 
00191  * position can always return the center angle - PI, since the block
00192  * must have children and if the block has 1 node, the limits will be
00193  * correctly set. If the block has more than 1 node, the value is
00194  * unused.
00195  */
00196 typedef struct {
00197     double radius;              /* Basic radius of block */
00198     double subtreeR;            /* Max of subtree radii */
00199     double nodeAngle;           /* Angle allocated to each node in block */
00200     double firstAngle;          /* Smallest child angle when block has 1 node */
00201     double lastAngle;           /* Largest child angle when block has 1 node */
00202     block_t *cp;                /* Children of block */
00203     node_t *neighbor;           /* Node connected to parent block, if any */
00204 } posstate;
00205 
00206 /* doParent:
00207  * Attach child blocks belonging to n.
00208  * children of n are placed in theta +/- stp->nodeAngle/2 unless
00209  * length = 1, in which case they go from 0 to
00210  * childCount*(incidentAngle + mindistAngle/2).
00211  * If length is 1, keeps track of minimum and maximum child angle.
00212  */
00213 static double
00214 doParent(Agraph_t * g, double theta, Agnode_t * n,
00215          int length, double min_dist, posstate * stp)
00216 {
00217     block_t *child;
00218     double mindistance;
00219     double childAngle;          /* angle of child */
00220     double deltaX, deltaY;
00221     double snRadius = stp->subtreeR;    /* max subtree radius */
00222     double firstAngle = stp->firstAngle;
00223     double lastAngle = stp->lastAngle;
00224     double rotateAngle;
00225     double incidentAngle;
00226 
00227     int childCount = 0;         /* No. of blocks that are children of n */
00228     double maxRadius = 0;       /* Max. radius of children */
00229     double childRadius;         /* Radius of circle on which children are placed */
00230     double diameter = 0;        /* sum of child diameters */
00231     double mindistAngle;        /* angle to move min_dist at childRadius */
00232     int cnt, midChild;
00233     double midAngle = 0;
00234 
00235     for (child = stp->cp; child; child = child->next) {
00236         if (BLK_PARENT(child) == n) {
00237             childCount++;
00238             if (maxRadius < child->radius) {
00239                 maxRadius = child->radius;
00240             }
00241             diameter += 2 * child->radius + min_dist;
00242         }
00243     }
00244 
00245     if (length == 1)
00246         childAngle = 0;
00247     else
00248         childAngle = theta - stp->nodeAngle / 2;
00249 
00250     childRadius = length * diameter / (2 * M_PI);
00251 
00252     /* FIX: If the parent block stp has only 1 child, we should probably
00253      * also set childRadius to mindistance. In this case, can 1 prove that
00254      * childRadius < mindistance? Probably not, since we can increase length
00255      * arbitrarily.
00256      */
00257     mindistance = stp->radius + min_dist + maxRadius;
00258     if (childRadius < mindistance)
00259         childRadius = mindistance;
00260 
00261     if ((childRadius + maxRadius) > snRadius)
00262         snRadius = childRadius + maxRadius;
00263 
00264     mindistAngle = min_dist / childRadius;
00265 
00266     cnt = 0;
00267     midChild = (childCount + 1) / 2;
00268     for (child = stp->cp; child; child = child->next) {
00269         if (BLK_PARENT(child) != n)
00270             continue;
00271         if (sizeNodelist(child->circle_list) <= 0)
00272             continue;
00273 
00274         incidentAngle = child->radius / childRadius;
00275         if (length == 1) {
00276             if (childAngle != 0)
00277                 childAngle += incidentAngle;
00278 
00279             if (firstAngle < 0)
00280                 firstAngle = childAngle;
00281 
00282             lastAngle = childAngle;
00283         } else {
00284             if (childCount == 1) {
00285                 childAngle = theta;
00286             } else {
00287                 childAngle += incidentAngle + mindistAngle / 2;
00288             }
00289         }
00290 
00291         deltaX = childRadius * cos(childAngle);
00292         deltaY = childRadius * sin(childAngle);
00293 
00294         /* first apply the delta to the immediate child and see if we need
00295          * to rotate it for better edge link                                            
00296          * should return the theta value if there was a rotation else zero
00297          */
00298 
00299         rotateAngle = getRotation(child, g, deltaX, deltaY, childAngle);
00300         applyDelta(child, deltaX, deltaY, rotateAngle);
00301 
00302         if (length == 1) {
00303             childAngle += incidentAngle + mindistAngle;
00304         } else {
00305             childAngle += incidentAngle + mindistAngle / 2;
00306         }
00307         cnt++;
00308         if (cnt == midChild)
00309             midAngle = childAngle;
00310     }
00311 
00312     if ((length > 1) && (n == stp->neighbor)) {
00313         PSI(n) = midAngle;
00314     }
00315 
00316     stp->subtreeR = snRadius;
00317     stp->firstAngle = firstAngle;
00318     stp->lastAngle = lastAngle;
00319     return maxRadius;
00320 }
00321 
00322 /* position:
00323  * Assume childCount > 0
00324  */
00325 static double
00326 position(Agraph_t * g, int childCount, int length, nodelist_t * path,
00327          block_t * sn, double min_dist)
00328 {
00329     nodelistitem_t *item;
00330     Agnode_t *n;
00331     posstate state;
00332     int counter = 0;
00333     double maxRadius = 0.0;
00334     double angle;
00335     double theta = 0.0;
00336 
00337     state.cp = sn->children.first;
00338     state.subtreeR = sn->radius;
00339     state.radius = sn->radius;
00340     state.neighbor = CHILD(sn);
00341     state.nodeAngle = 2 * M_PI / length;
00342     state.firstAngle = -1;
00343     state.lastAngle = -1;
00344 
00345     for (item = path->first; item; item = item->next) {
00346         n = item->curr;
00347 
00348         if (length != 1) {
00349             theta = counter * state.nodeAngle;
00350             counter++;
00351         }
00352 
00353         if (ISPARENT(n))
00354             maxRadius = doParent(g, theta, n, length, min_dist, &state);
00355     }
00356 
00357     /* If block has only 1 child, to save space, we coalesce it with the
00358      * child. Instead of having final radius sn->radius + max child radius,
00359      * we have half that. However, the origin of the block is no longer in
00360      * the center of the block, so we cannot do a simple rotation to get
00361      * the neighbor node next to the parent block in getRotate.
00362      */
00363     if (childCount == 1) {
00364         applyDelta(sn, -(maxRadius + min_dist / 2), 0, 0);
00365         sn->radius += min_dist / 2 + maxRadius;
00366         SET_COALESCED(sn);
00367     } else
00368         sn->radius = state.subtreeR;
00369 
00370     angle = (state.firstAngle + state.lastAngle) / 2.0 - M_PI;
00371     return angle;
00372 }
00373 
00374 /* doBlock:
00375  * Set positions of block sn and its child blocks.
00376  */
00377 static void doBlock(Agraph_t * g, block_t * sn, double min_dist)
00378 {
00379     block_t *child;
00380     nodelist_t *longest_path;
00381     int childCount, length;
00382     double centerAngle = M_PI;
00383 
00384     /* layout child subtrees */
00385     childCount = 0;
00386     for (child = sn->children.first; child; child = child->next) {
00387         doBlock(g, child, min_dist);
00388         childCount++;
00389     }
00390 
00391     /* layout this block */
00392     longest_path = layout_block(g, sn, min_dist);
00393     sn->circle_list = longest_path;
00394     length = sizeNodelist(longest_path);        /* path contains everything in block */
00395 
00396     /* attach children */
00397     if (childCount > 0)
00398         centerAngle =
00399             position(g, childCount, length, longest_path, sn, min_dist);
00400 
00401     if ((length == 1) && (BLK_PARENT(sn))) {
00402         sn->parent_pos = centerAngle;
00403         if (sn->parent_pos < 0)
00404             sn->parent_pos += 2 * M_PI;
00405     }
00406 }
00407 
00408 void circPos(Agraph_t * g, block_t * sn, circ_state * state)
00409 {
00410     doBlock(g, sn, state->min_dist);
00411 }

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