1 /* ----------------------------------------------------------------------------
3 * Purpose : dynamic tree program based on Sven Moen's algorithm
4 * ----------------------------------------------------------------------------
14 /* ------------------------------------------------------------------------- */
15 /* Global Variables */
16 /* ------------------------------------------------------------------------- */
18 static int NumLines = 0;
19 static int NumNodes = 0;
22 /* ----------------------------------------------------------------------------
24 * MakeLine() allocates the memory required for a Polyline and
25 * initializes the fields of a Polyline to the arguments. The
26 * newly-allocated Polyline is returned by the function.
28 * ----------------------------------------------------------------------------
32 MakeLine(short dx, short dy, Polyline *line)
34 Polyline *new_line = (Polyline *) malloc(sizeof(Polyline));
36 NASSERT(new_line, "could not allocate memory for polyline");
41 new_line->link = line;
46 /* ----------------------------------------------------------------------------
48 * MakeNode() allocates the memory required for a tree node, and
49 * zeros out all the fields in the Node. It returns a pointer to the
50 * tree node upon success, and NULL upon failure.
52 * ----------------------------------------------------------------------------
58 Tree *node = (Tree *) malloc(sizeof(Tree));
60 NASSERT(node, "could not allocate memory for node");
66 memset((char *) node, 0, sizeof(Tree));
71 /* ----------------------------------------------------------------------------
75 * ----------------------------------------------------------------------------
79 MakeBridge(Polyline *line1, int x1, int y1, Polyline *line2, int x2, int y2)
84 dx = x2 + line2->dx - x1;
91 r = MakeLine(dx, dy, line2->link);
92 line1->link = MakeLine(0, y2 + line2->dy - dy - y1, r);
97 /* ----------------------------------------------------------------------------
99 * Offset() computes the necessary offset that prevents two line segments
100 * from intersecting each other. This is the "heart" of the merge step
101 * that computes how two subtree contours should be separated.
103 * The code is taken directly from Sven Moen's paper, with changes in
104 * some variable names to give more meaning:
106 * - px,py indicate the x- and y-coordinates of the point on the longer
107 * segment if the previous Offset() call had two unequal segments
109 * - lx,ly indicate the dx and dy values of the "lower" line segment
111 * - ux,uy indicate the dx and dy values of the "upper" line segment
113 * ----------------------------------------------------------------------------
117 Offset(int px, int py, int lx, int ly, int ux, int uy)
121 if (ux <= px || px+lx <= 0)
142 d = uy - (py + s/lx);
144 else if (ux > px+lx) {
156 /* ----------------------------------------------------------------------------
160 * ----------------------------------------------------------------------------
164 Merge(Polygon *c1, Polygon *c2)
167 Polyline *lower, *upper, *bridge;
171 /* compare lower part of upper child's contour
172 * with upper part of lower child's contour
174 upper = c1->lower.head;
175 lower = c2->upper.head;
177 while (lower && upper) {
178 d = Offset(x, y, lower->dx, lower->dy, upper->dx, upper->dy);
182 if (x + lower->dx <= upper->dx) {
195 bridge = MakeBridge(c1->upper.tail, 0, 0, lower, x, y);
196 c1->upper.tail = (bridge->link) ? c2->upper.tail : bridge;
197 c1->lower.tail = c2->lower.tail;
200 bridge = MakeBridge(c2->lower.tail, x, y, upper, 0, 0);
202 c1->lower.tail = bridge;
204 c1->lower.head = c2->lower.head;
209 /* ----------------------------------------------------------------------------
211 * DetachParent() reverses the effects of AttachParent by removing
212 * the four line segments that connect the subtree contour to the
213 * node specified by 'tree'.
215 * ----------------------------------------------------------------------------
219 DetachParent(Tree *tree)
221 free(tree->contour.upper.head->link);
222 free(tree->contour.upper.head);
223 tree->contour.upper.head = NULL;
224 tree->contour.upper.tail = NULL;
226 free(tree->contour.lower.head->link);
227 free(tree->contour.lower.head);
228 tree->contour.lower.head = NULL;
229 tree->contour.lower.tail = NULL;
234 /* ----------------------------------------------------------------------------
237 * This function also establishes the position of the first child
238 * The code follows Sven Moen's version, with slight modification to
239 * support varying borders at different levels.
241 * ----------------------------------------------------------------------------
245 AttachParent(Tree *tree, int h)
250 x = tree->border + (TreeParentDistance * 2) +
251 (TreeParentDistance - tree->width);
253 x = tree->border + TreeParentDistance;
254 y2 = (h - tree->height)/2 - tree->border;
255 y1 = y2 + tree->height + (2 * tree->border) - h;
256 tree->child->offset.x = x + tree->width;
257 tree->child->offset.y = y1;
258 tree->contour.upper.head = MakeLine(tree->width, 0,
260 tree->contour.upper.head));
261 tree->contour.lower.head = MakeLine(tree->width, 0,
263 tree->contour.lower.head));
266 /* ----------------------------------------------------------------------------
269 * The tree passed to Split() must have at least 1 child, because
270 * it doesn't make sense to split a leaf (there are no bridges)
272 * ----------------------------------------------------------------------------
281 FOREACH_CHILD(child, tree) {
282 if ((link = child->contour.upper.tail->link)) {
285 child->contour.upper.tail->link = NULL;
288 if ((link = child->contour.lower.tail->link)) {
292 child->contour.lower.tail->link = NULL;
297 /* ----------------------------------------------------------------------------
299 * Join() merges all subtree contours of the given tree and returns the
300 * height of the entire tree contour.
302 * ----------------------------------------------------------------------------
311 /* to start, set the parent's contour and height
312 * to contour and height of first child
315 tree->contour = child->contour;
316 sum = h = child->height + (2 * child->border);
318 /* extend contour to include contours of all children of parent */
319 for (child = child->sibling ; child ; child = child->sibling) {
320 d = Merge(&tree->contour, &child->contour);
321 child->offset.y = d + h;
323 h = child->height + (2 * child->border);
324 /* keep cumulative heights of subtree contours */
330 /* ----------------------------------------------------------------------------
332 * RuboutLeaf() accepts a single node (leaf) and removes its contour.
333 * The memory associated with the contour is deallocated.
335 * ----------------------------------------------------------------------------
339 RuboutLeaf(Tree *tree)
341 free(tree->contour.upper.head);
342 free(tree->contour.lower.tail);
343 free(tree->contour.lower.head);
344 tree->contour.upper.head = NULL;
345 tree->contour.upper.tail = NULL;
346 tree->contour.lower.head = NULL;
347 tree->contour.lower.tail = NULL;
351 /* ----------------------------------------------------------------------------
353 * LayoutLeaf() accepts a single node (leaf) and forms its contour. This
354 * function assumes that the width, height, and border fields of the
355 * node have been assigned meaningful values.
357 * ----------------------------------------------------------------------------
361 LayoutLeaf(Tree *tree)
363 tree->node_height = 0;
364 tree->border = TreeBorderSize;
366 tree->contour.upper.tail = MakeLine(tree->width + 2 * tree->border, 0,
368 tree->contour.upper.head = tree->contour.upper.tail;
370 tree->contour.lower.tail = MakeLine(0, -tree->height - 2 * tree->border,
372 tree->contour.lower.head = MakeLine(tree->width + 2 * tree->border, 0,
373 tree->contour.lower.tail);
377 /* ----------------------------------------------------------------------------
379 * LayoutTree() traverses the given tree (in depth-first order), and forms
380 * subtree or leaf contours at each node as needed. Each node's contour is
381 * stored in its "contour" field. Elision is also supported by generating
382 * the contour for both the expanded and collapsed node. This routine
383 * also computes the tree height of each node in the tree, so that variable
384 * density layout can be demonstrated.
386 * ----------------------------------------------------------------------------
390 LayoutTree(Tree *tree)
395 FOREACH_CHILD(child, tree) {
398 if (child->elision) { /* support elision */
399 child->old_contour = child->contour;
407 FOREACH_CHILD(child, tree)
408 height = MAX(child->node_height, height);
409 tree->node_height = height + 1;
411 if (TreeLayoutDensity == Fixed)
412 tree->border = TreeBorderSize;
415 (int) (TreeBorderSize * (tree->node_height * DENSITY_FACTOR));
417 AttachParent(tree, Join(tree));
423 /* ------------------------------------------------------------------------- */
432 HiliteNode(tree, New);
433 tree->on_path = TRUE;
434 StatusMsg("Unzip: follow parent links up to root", 0);
445 /* draw entire contour; do it only for root, because the last
446 * frame drawn in this function will have already drawn the
447 * contour for the most recently split subtree.
450 if (tree->parent == NULL) {
452 DrawTreeContour(tree, New, CONTOUR_COLOR, FALSE, FALSE, FALSE);
453 DrawTree(TheTree, New);
455 StatusMsg("Unzip: disassemble entire contour", 0);
462 /* draw contour as it would appear after DetachParent() */
466 DrawTreeContour(tree, New, CONTOUR_COLOR, TRUE,
467 FALSE, FALSE, FALSE);
469 DrawTreeContour(tree, New, CONTOUR_COLOR, TRUE,
471 DrawTree(TheTree, New);
473 StatusMsg("Unzip: detach parent", 0);
484 /* mark other subtree contours as split, and */
485 /* draw only the contour on path in full */
486 FOREACH_CHILD(child, tree) {
490 DrawTreeContour(child, New, CONTOUR_COLOR,
491 FALSE, FALSE, FALSE);
493 DrawTree(TheTree, New);
495 StatusMsg("Unzip: split tree", 0);
502 RuboutLeaf(tree); /* leaf node */
505 /* ------------------------------------------------------------------------- */
511 AttachParent(tree, Join(tree));
519 /* ----------------------------------------------------------------------------
521 * Insert() adds the specified child to parent, just after the specified
522 * sibling. If 'sibling' is Null, the child is added as the first child.
524 * ----------------------------------------------------------------------------
528 Insert(Tree *parent, Tree *child, Tree *sibling)
531 child->parent = parent;
533 child->sibling = sibling->sibling;
534 sibling->sibling = child;
537 child->sibling = parent->child;
538 parent->child = child;
546 /* ----------------------------------------------------------------------------
548 * Delete() traverses the specified tree and frees all storage
549 * allocated to the subtree, including contours and bridges.
550 * If the tree had a preceding sibling, the preceding sibling is
551 * modified to point to the tree's succeeding sibling, if any.
553 * ----------------------------------------------------------------------------
559 Tree *sibling = NULL;
560 Tree *parent, *child;
563 parent = tree->parent;
565 FOREACH_CHILD(child, parent)
566 if (child->sibling == tree) {
572 sibling->sibling = tree->sibling;
574 parent->child = tree->sibling;
576 DeleteTree(tree, FALSE);
580 /* ----------------------------------------------------------------------------
582 * DeleteTree() is the recursive function that supports Delete().
583 * If 'contour' is True, then only the contours are recursively deleted.
584 * This flag should be True when you are regenerating a tree's layout
585 * and still want to preserve the nodes. Since contours would be deleted
586 * only due to a change in sibling or level distance, each node's border
587 * value is updated with the current value of TreeBorderSize;
589 * ----------------------------------------------------------------------------
593 DeleteTree(Tree *tree, int contour)
599 tree->contour = tree->old_contour;
600 tree->old_contour.upper.head = NULL; /* flag to note 'NULL' contour */
603 if (!IS_LEAF(tree)) {
608 /* This macro makes a child->sibling reference
609 after the child has been deleted, so don't
610 use it. - kkm@kis.ru, 4/9/1998 */
611 FOREACH_CHILD(child,tree)
612 DeleteTree(child, contour);
617 Tree* next = child->sibling;
618 DeleteTree (child, contour);
627 tree->border = TreeBorderSize;
629 free(tree->label.text);
636 /* ----------------------------------------------------------------------------
639 * This function should be called after tree layout.
641 * ----------------------------------------------------------------------------
645 ComputeTreeSize(Tree *tree,
646 int *width, int *height,
647 int *x_offset, int *y_offset)
649 Polyline *contour, *tail;
650 int upper_min_y = 0, lower_max_y = 0;
651 int upper_abs_y = 0, lower_abs_y = 0;
654 /* do upper contour */
655 contour = tree->contour.upper.head;
656 tail = tree->contour.upper.tail;
658 if ((contour->dy + upper_abs_y) < upper_min_y)
659 upper_min_y = contour->dy + upper_abs_y;
660 upper_abs_y += contour->dy;
664 contour = contour->link;
667 /* do lower contour */
668 contour = tree->contour.lower.head;
669 tail = tree->contour.lower.tail;
671 if ((contour->dy + lower_abs_y) > lower_max_y)
672 lower_max_y = contour->dy + lower_abs_y;
673 lower_abs_y += contour->dy;
678 contour = contour->link;
682 *height = lower_max_y - upper_min_y +
683 (tree->height + (2 * tree->border)) + 1;
685 *x_offset = tree->border;
687 *y_offset = - upper_min_y + tree->border;
690 /* ----------------------------------------------------------------------------
694 * ----------------------------------------------------------------------------
698 PetrifyTree(Tree *tree, int x, int y)
700 tree->old_pos = tree->pos; /* used by AnimateTree */
702 /* fix position of each node */
703 tree->pos.x = x + tree->offset.x;
704 tree->pos.y = y + tree->offset.y;
707 PetrifyTree(tree->child, tree->pos.x, tree->pos.y);
708 ComputeSubTreeExtent(tree); /* for benefit of interface picking */
711 PetrifyTree(tree->sibling, tree->pos.x, tree->pos.y);