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