1 /* ----------------------------------------------------------------------------
\r
3 * Purpose : dynamic tree program based on Sven Moen's algorithm
\r
4 * ----------------------------------------------------------------------------
\r
15 /* ------------------------------------------------------------------------- */
\r
16 /* Global Variables */
\r
17 /* ------------------------------------------------------------------------- */
\r
23 /* ----------------------------------------------------------------------------
\r
25 * MakeLine() allocates the memory required for a TreePolyline and
\r
26 * initializes the fields of a TreePolyline to the arguments. The
\r
27 * newly-allocated Polyline is returned by the function.
\r
29 * ----------------------------------------------------------------------------
\r
33 MakeLine(dx, dy, link)
\r
40 new = (TreePolyline *) malloc(sizeof(TreePolyline));
\r
41 NASSERT(new, "could not allocate memory for polyline");
\r
51 /* ----------------------------------------------------------------------------
\r
53 * MakeNode() allocates the memory required for a tree node, and
\r
54 * zeros out all the fields in the Node. It returns a pointer to the
\r
55 * tree node upon success, and NULL upon failure.
\r
57 * ----------------------------------------------------------------------------
\r
65 node = (Tree *) malloc(sizeof(Tree));
\r
66 NASSERT(node, "could not allocate memory for node");
\r
72 #if defined(SYSV) || defined(WIN32)
\r
73 memset((char *) node, 0, sizeof(Tree));
\r
75 bzero((char *) node, sizeof(Tree));
\r
81 /* ----------------------------------------------------------------------------
\r
85 * ----------------------------------------------------------------------------
\r
89 MakeBridge(line1, x1, y1, line2, x2, y2)
\r
90 TreePolyline *line1, *line2;
\r
96 dx = x2 + line2->dx - x1;
\r
100 s = dx * line2->dy;
\r
101 dy = s / line2->dx;
\r
103 r = MakeLine(dx, dy, line2->link);
\r
104 line1->link = MakeLine(0, y2 + line2->dy - dy - y1, r);
\r
109 /* ----------------------------------------------------------------------------
\r
111 * Offset() computes the necessary offset that prevents two line segments
\r
112 * from intersecting each other. This is the "heart" of the merge step
\r
113 * that computes how two subtree contours should be separated.
\r
115 * The code is taken directly from Sven Moen's paper, with changes in
\r
116 * some variable names to give more meaning:
\r
118 * - px,py indicate the x- and y-coordinates of the point on the longer
\r
119 * segment if the previous Offset() call had two unequal segments
\r
121 * - lx,ly indicate the dx and dy values of the "lower" line segment
\r
123 * - ux,uy indicate the dx and dy values of the "upper" line segment
\r
125 * ----------------------------------------------------------------------------
\r
129 Offset(px, py, lx, ly, ux, uy)
\r
130 int px, py, lx, ly, ux, uy;
\r
134 if (ux <= px || px+lx <= 0)
\r
155 d = uy - (py + s/lx);
\r
157 else if (ux > px+lx) {
\r
159 d = s/ux - (py+ly);
\r
169 /* ----------------------------------------------------------------------------
\r
173 * ----------------------------------------------------------------------------
\r
178 TreePolygon *c1, *c2;
\r
180 int x, y, total, d;
\r
181 TreePolyline *lower, *upper, *bridge;
\r
185 /* compare lower part of upper child's contour
\r
186 * with upper part of lower child's contour
\r
188 upper = c1->lower.head;
\r
189 lower = c2->upper.head;
\r
191 while (lower && upper) {
\r
192 d = Offset(x, y, lower->dx, lower->dy, upper->dx, upper->dy);
\r
196 if (x + lower->dx <= upper->dx) {
\r
199 lower = lower->link;
\r
204 upper = upper->link;
\r
209 bridge = MakeBridge(c1->upper.tail, 0, 0, lower, x, y);
\r
210 c1->upper.tail = (bridge->link) ? c2->upper.tail : bridge;
\r
211 c1->lower.tail = c2->lower.tail;
\r
214 bridge = MakeBridge(c2->lower.tail, x, y, upper, 0, 0);
\r
215 if (!bridge->link)
\r
216 c1->lower.tail = bridge;
\r
218 c1->lower.head = c2->lower.head;
\r
223 /* ----------------------------------------------------------------------------
\r
225 * DetachParent() reverses the effects of AttachParent by removing
\r
226 * the four line segments that connect the subtree contour to the
\r
227 * node specified by 'tree'.
\r
229 * ----------------------------------------------------------------------------
\r
236 free(tree->contour.upper.head->link);
\r
237 free(tree->contour.upper.head);
\r
238 tree->contour.upper.head = NULL;
\r
239 tree->contour.upper.tail = NULL;
\r
241 free(tree->contour.lower.head->link);
\r
242 free(tree->contour.lower.head);
\r
243 tree->contour.lower.head = NULL;
\r
244 tree->contour.lower.tail = NULL;
\r
249 /* ----------------------------------------------------------------------------
\r
252 * This function also establishes the position of the first child
\r
253 * The code follows Sven Moen's version, with slight modification to
\r
254 * support varying borders at different levels.
\r
256 * ----------------------------------------------------------------------------
\r
260 AttachParent(tree, h)
\r
266 if (TreeAlignNodes)
\r
267 x = tree->border + (TreeParentDistance * 2) +
\r
268 (TreeParentDistance - tree->width);
\r
270 x = tree->border + TreeParentDistance;
\r
271 y2 = (h - tree->height)/2 - tree->border;
\r
272 y1 = y2 + tree->height + (2 * tree->border) - h;
\r
273 tree->child->offset.x = x + tree->width;
\r
274 tree->child->offset.y = y1;
\r
275 tree->contour.upper.head = MakeLine(tree->width, 0,
\r
277 tree->contour.upper.head));
\r
278 tree->contour.lower.head = MakeLine(tree->width, 0,
\r
280 tree->contour.lower.head));
\r
283 /* ----------------------------------------------------------------------------
\r
286 * The tree passed to Split() must have at least 1 child, because
\r
287 * it doesn't make sense to split a leaf (there are no bridges)
\r
289 * ----------------------------------------------------------------------------
\r
297 TreePolyline *link;
\r
299 FOREACH_CHILD(child, tree) {
\r
300 if (link = child->contour.upper.tail->link) {
\r
303 child->contour.upper.tail->link = NULL;
\r
306 if (link = child->contour.lower.tail->link) {
\r
310 child->contour.lower.tail->link = NULL;
\r
315 /* ----------------------------------------------------------------------------
\r
317 * Join() merges all subtree contours of the given tree and returns the
\r
318 * height of the entire tree contour.
\r
320 * ----------------------------------------------------------------------------
\r
330 /* to start, set the parent's contour and height
\r
331 * to contour and height of first child
\r
333 child = tree->child;
\r
334 tree->contour = child->contour;
\r
335 sum = h = child->height + (2 * child->border);
\r
337 /* extend contour to include contours of all children of parent */
\r
338 for (child = child->sibling ; child ; child = child->sibling) {
\r
339 d = Merge(&tree->contour, &child->contour);
\r
340 child->offset.y = d + h;
\r
341 child->offset.x = 0;
\r
342 h = child->height + (2 * child->border);
\r
343 /* keep cumulative heights of subtree contours */
\r
349 /* ----------------------------------------------------------------------------
\r
351 * RuboutLeaf() accepts a single node (leaf) and removes its contour.
\r
352 * The memory associated with the contour is deallocated.
\r
354 * ----------------------------------------------------------------------------
\r
361 free(tree->contour.upper.head);
\r
362 free(tree->contour.lower.tail);
\r
363 free(tree->contour.lower.head);
\r
364 tree->contour.upper.head = NULL;
\r
365 tree->contour.upper.tail = NULL;
\r
366 tree->contour.lower.head = NULL;
\r
367 tree->contour.lower.tail = NULL;
\r
371 /* ----------------------------------------------------------------------------
\r
373 * LayoutLeaf() accepts a single node (leaf) and forms its contour. This
\r
374 * function assumes that the width, height, and border fields of the
\r
375 * node have been assigned meaningful values.
\r
377 * ----------------------------------------------------------------------------
\r
384 tree->node_height = 0;
\r
385 tree->border = TreeBorderSize;
\r
387 tree->contour.upper.tail = MakeLine(tree->width + 2 * tree->border, 0,
\r
389 tree->contour.upper.head = tree->contour.upper.tail;
\r
391 tree->contour.lower.tail = MakeLine(0, -tree->height - 2 * tree->border,
\r
393 tree->contour.lower.head = MakeLine(tree->width + 2 * tree->border, 0,
\r
394 tree->contour.lower.tail);
\r
398 /* ----------------------------------------------------------------------------
\r
400 * LayoutTree() traverses the given tree (in depth-first order), and forms
\r
401 * subtree or leaf contours at each node as needed. Each node's contour is
\r
402 * stored in its "contour" field. Elision is also supported by generating
\r
403 * the contour for both the expanded and collapsed node. This routine
\r
404 * also computes the tree height of each node in the tree, so that variable
\r
405 * density layout can be demonstrated.
\r
407 * ----------------------------------------------------------------------------
\r
417 FOREACH_CHILD(child, tree) {
\r
420 if (child->elision) { /* support elision */
\r
421 child->old_contour = child->contour;
\r
429 FOREACH_CHILD(child, tree)
\r
430 height = MAX(child->node_height, height);
\r
431 tree->node_height = height + 1;
\r
433 if (TreeLayoutDensity == Fixed)
\r
434 tree->border = TreeBorderSize;
\r
437 (int) (TreeBorderSize * (tree->node_height * DENSITY_FACTOR));
\r
439 AttachParent(tree, Join(tree));
\r
445 /* ------------------------------------------------------------------------- */
\r
454 if (TreeShowSteps) {
\r
455 HiliteNode(tree, New);
\r
456 tree->on_path = TRUE;
\r
457 StatusMsg("Unzip: follow parent links up to root", 0);
\r
463 Unzip(tree->parent);
\r
468 /* draw entire contour; do it only for root, because the last
\r
469 * frame drawn in this function will have already drawn the
\r
470 * contour for the most recently split subtree.
\r
472 if (TreeShowSteps) {
\r
473 if (tree->parent == NULL) {
\r
475 DrawTreeContour(tree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);
\r
476 DrawTree(TheTree, New);
\r
478 StatusMsg("Unzip: disassemble entire contour", 0);
\r
485 /* draw contour as it would appear after DetachParent() */
\r
486 if (TreeShowSteps) {
\r
488 DrawTreeContour(tree, New, CONTOUR_COLOR, TRUE,
\r
489 FALSE, FALSE, FALSE);
\r
490 DrawTree(TheTree, New);
\r
492 StatusMsg("Unzip: detach parent", 0);
\r
497 DetachParent(tree);
\r
501 if (TreeShowSteps) {
\r
503 /* mark other subtree contours as split, and */
\r
504 /* draw only the contour on path in full */
\r
505 FOREACH_CHILD(child, tree) {
\r
506 if (!child->on_path)
\r
507 child->split = TRUE;
\r
509 DrawTreeContour(child, New, CONTOUR_COLOR,
\r
510 FALSE, FALSE, FALSE);
\r
512 DrawTree(TheTree, New);
\r
514 StatusMsg("Unzip: split tree", 0);
\r
521 RuboutLeaf(tree); /* leaf node */
\r
524 /* ------------------------------------------------------------------------- */
\r
531 AttachParent(tree, Join(tree));
\r
539 /* ----------------------------------------------------------------------------
\r
541 * Insert() adds the specified child to parent, just after the specified
\r
542 * sibling. If 'sibling' is Null, the child is added as the first child.
\r
544 * ----------------------------------------------------------------------------
\r
548 Insert(parent, child, sibling)
\r
549 Tree *parent, *child, *sibling;
\r
552 child->parent = parent;
\r
554 child->sibling = sibling->sibling;
\r
555 sibling->sibling = child;
\r
558 child->sibling = parent->child;
\r
559 parent->child = child;
\r
567 /* ----------------------------------------------------------------------------
\r
569 * Delete() traverses the specified tree and frees all storage
\r
570 * allocated to the subtree, including contours and bridges.
\r
571 * If the tree had a preceding sibling, the preceding sibling is
\r
572 * modified to point to the tree's succeeding sibling, if any.
\r
574 * ----------------------------------------------------------------------------
\r
581 Tree *sibling = NULL;
\r
582 Tree *parent, *child;
\r
585 parent = tree->parent;
\r
587 FOREACH_CHILD(child, parent)
\r
588 if (child->sibling == tree) {
\r
594 sibling->sibling = tree->sibling;
\r
596 parent->child = tree->sibling;
\r
598 DeleteTree(tree, FALSE);
\r
602 /* ----------------------------------------------------------------------------
\r
604 * DeleteTree() is the recursive function that supports Delete().
\r
605 * If 'contour' is True, then only the contours are recursively deleted.
\r
606 * This flag should be True when you are regenerating a tree's layout
\r
607 * and still want to preserve the nodes. Since contours would be deleted
\r
608 * only due to a change in sibling or level distance, each node's border
\r
609 * value is updated with the current value of TreeBorderSize;
\r
611 * ----------------------------------------------------------------------------
\r
615 DeleteTree(tree, contour)
\r
621 if (tree->elision) {
\r
623 tree->contour = tree->old_contour;
\r
624 tree->old_contour.upper.head = NULL; /* flag to note 'NULL' contour */
\r
627 if (!IS_LEAF(tree)) {
\r
628 DetachParent(tree);
\r
632 /* This macro is deadly here! -kkm */
\r
633 FOREACH_CHILD(child,tree)
\r
634 DeleteTree(child, contour);
\r
636 child = tree->child;
\r
639 Tree* next = child->sibling;
\r
640 DeleteTree (child, contour);
\r
649 tree->border = TreeBorderSize;
\r
651 free(tree->label.text);
\r
658 /* ----------------------------------------------------------------------------
\r
660 * ComputeTreeSize()
\r
661 * This function should be called after tree layout.
\r
663 * ----------------------------------------------------------------------------
\r
667 ComputeTreeSize(tree, width, height, x_offset, y_offset)
\r
669 int *width, *height;
\r
670 int *x_offset, *y_offset;
\r
672 TreePolyline *contour, *tail;
\r
673 int upper_min_y = 0, lower_max_y = 0;
\r
674 int upper_abs_y = 0, lower_abs_y = 0;
\r
677 /* do upper contour */
\r
678 contour = tree->contour.upper.head;
\r
679 tail = tree->contour.upper.tail;
\r
681 if ((contour->dy + upper_abs_y) < upper_min_y)
\r
682 upper_min_y = contour->dy + upper_abs_y;
\r
683 upper_abs_y += contour->dy;
\r
684 if (contour == tail)
\r
687 contour = contour->link;
\r
690 /* do lower contour */
\r
691 contour = tree->contour.lower.head;
\r
692 tail = tree->contour.lower.tail;
\r
694 if ((contour->dy + lower_abs_y) > lower_max_y)
\r
695 lower_max_y = contour->dy + lower_abs_y;
\r
696 lower_abs_y += contour->dy;
\r
698 if (contour == tail)
\r
701 contour = contour->link;
\r
705 *height = lower_max_y - upper_min_y +
\r
706 (tree->height + (2 * tree->border)) + 1;
\r
708 *x_offset = tree->border;
\r
710 *y_offset = - upper_min_y + tree->border;
\r
713 /* ----------------------------------------------------------------------------
\r
717 * ----------------------------------------------------------------------------
\r
721 PetrifyTree(tree, x, y)
\r
725 tree->old_pos = tree->pos; /* used by AnimateTree */
\r
727 /* fix position of each node */
\r
728 tree->pos.x = x + tree->offset.x;
\r
729 tree->pos.y = y + tree->offset.y;
\r
732 PetrifyTree(tree->child, tree->pos.x, tree->pos.y);
\r
733 ComputeSubTreeExtent(tree); /* for benefit of interface picking */
\r
736 PetrifyTree(tree->sibling, tree->pos.x, tree->pos.y);
\r