A Node-Positioning Algorithm For General Trees: TR89-034 September, 1989
A Node-Positioning Algorithm For General Trees: TR89-034 September, 1989
John Q. Walker II
A TextLab Report
UNC is an Equal Opportunity/Affirmative Action Institution.
Abstract
Drawing a tree consists of two stages: determining the position of each node,
and actually rendering the individuals nodes and interconnecting branches.
The algorithm described in this paper is concerned with the first stage: given a
list of nodes. an indication of the hierarchical relationship among them. and
their shape and size, wrere should each node be positioned for optimal aes-
thetic effect?
This algorithm determines the positions of the nodes for any arbitrary general
tree. It is the most desirable positioning with respect to certain widely-
accepted heuristics. The positioning, specified in x, y coordinates, minimizes
the width of the tree. In a general tree, there is no limit on the number of off-
spring per node; this contrasts with binary and ternary trees, for example,
which are trees with a limit of 2 and 3 offspring per node. This algorithm oper-
ates in time O(N), where N is the number of nodes in the tree.
Previously, most tree drawings have been positioned by the sure hand of a
human graphic designer. Many computer-generated positionings have been
either trivial or contained irregularities. Earlier work by Wetherell and Shannon
(1979) and Tilford (1981), upon which this algorithm builds, failed to correctly
position the interior nodes of some trees. Radack (1988), also building on
Tilford's work, has solved this same problem with a different method which
makes four passes. The algorithm presented here correctly positions a tree's
nodes using only two passes. It also handles several practical considerations:
alternate orientations of the tree, variable node sizes, and out-of-bounds condi-
tions.
Keywords
Aesthetics, computer graphics, drawing methods, tree drawing, tree structures
Abstract ii
Contents
Introduction . . . . . . . . . . . .
What Is A General Tree?
Aesthetic Rules ..... . 2
Application Areas 3
The Algorithm 6
An Example . . ......... . 17
Nodes Visited in the First Traversal 17
Nodes Visited in the Second Traversal 20
Previous Work 24
Acknowledgements 27
References 28
Contents iii
Introduction
This algorithm addresses the problem of drawing tree structures. Trees are a
common method of representing a hierarchically-organized structure. In com-
puter science, trees are used in such areas as searching, compiling, and data-
base systems; in non-computer applications, they are commonly used to
construct organizational charts or to illustrate biological classifications. Visual
displays of trees show hierarchical relationships clearly; they are often more
useful than listings of trees in which hierarchical structure is obscured by a
linear arrangement of the information."
A key task in tree drawing is deciding where to place each node on the display
or output page. This task is accomplished by a node-positioning algorithm that
calculates the x and y coordinates for every node of the tree. A rendering
routine can then use these coordinates to draw the tree. A node-positioning
algorithm must address two key issues. First, the resulting drawing should be
aesthetically pleasing. Second, the positioning algorithm should make every
effort to conserve space. Each of these two issues can be handled
straightforwardly by itself, but taking them together poses some challenges.
Several algorithms for the positioning of general trees have been published; in
the works of Sweet" and Tilford". however, the authors describe anomalies
with their algorithms that can cause drawings with less-than-desirable results.
The algorithm presented here corrects the deficiencies in these algorithms and
produces the most desirable positioning for all general trees it is asked to posi-
tion. Radack7 has published a node-positioning algorithm that uses a different
solution technique, but which produces results identical to those presented
here.
Introduction i
This left-right distinction does not apply in a general tree; if a node has
a single offspring, the offspring is placed directly below its parent.
This algorithm positions a binary tree by ignoring the distinction above. That is,
it does not preserve left or right positioning of the offspring under the parent: if
a node has exactly one offspring, it is positioned directly below its parent.
Supowit and Reingold' noted that it is NP-hard to optimally position minimum-
width binary trees (while adhering to the distinction above) to within a factor of
less than about four percent.
Aesthetic Rules
In their paper, Wetherell and Shannon13 first described a set of aesthetic rules
against which a good positioning algorithm must be judged. Tilford" and
Supowit and Reingold' have expanded that list in an effort to produce better
algorithms.
Tidy drawings of trees occupy as little space as possible while satisfying certain
aesthetics:
1. Nodes at the same level of the tree should lie along a straight line, and the
straight lines defining the levels should be parallel. 13
In parse trees, one might want all leaves to lie on one horizontal
line; for that application Aesthetic 1 is not desirable. In this case,
though, the width of the placement is fixed and so the minimum
width placement problem for such parse trees is not interesting.
We therefore restrict our attention to the wide class of applications
for which Aesthetic 1 is desirable.'
2. A parent should be centered over its offspring. 13
3. A tree and its mirror image should produce drawings that are refiections of
one another; moreover, a subtree should be drawn the same way regard-
less of where it occurs in the tree. In some applications, one wishes to
examine large trees to find repeated patterns; the search for patterns is
facilitated by having isomorphic subtrees drawn isomorphicallyB
This implies that small subtrees should not appear arbitrarily positioned
among larger subtrees.
a. Small, interior subtrees should be spaced out evenly among larger sub-
trees {where the larger subtrees are adjacent at one or more levels).
b. Small subtrees at the far left or far right should be adjacent to larger
subtrees.
Introduction 2
Application Areas
In the past, general trees displayed on a computer screen or in print have had
one of the following characteristics:
With this algorithm, a computer can reliably generate tree drawings equivalent
to those done by a skilled human. Below are some of the applications that
often use tree-drawings.
Drawings of B-trees and 2-3 trees
• Structure editors that draw trees
Flow charts without loops
Visual LISP editors
Parse trees
Decision trees
• Hierarchical database models
• Hierarchically-organized file systems (for example, directories, sub-
directories, and files)
Depth-first spanning trees (graph theory)
• Organizational charts
• Table of contents in printed matter
• Biological classification
Introduction 3
How the Algorithm Works
This algorithm initially assumes the common practice among computer scien-
tists of drawing trees with the root at the top of the drawing.' Node-positioning
algorithms are concerned only with determining the x-coordinates of the nodes;
the y-coordinate of a node can easily be determined from its level in the tree,
due to Aesthetic 1 and the natural convention of a uniform vertical separation
between consecutive levels. "Changing the Orientation of the Root" on
page 21 presents a variation of the algorithm for altering the relationship of the
x- and y-coordinates.
Second is the concept of using two fields for the positioning of each node.
These two fields are:
• a preliminary x-coordinate, and
• a modifier field.
Two tree traversals are used to produce the final x-coordinate of a node. The
first traversal assigns the preliminary x-coordinate and modifier fields for each
node; the second traversal computes the final x-coordinate of each node by
summing the node's preliminary x-coordinate with the modifier fields of all of its
ancestors. This allows the simple moving of a large subtree and allows the
algorithm to operate in time O(N). For example, to move a subtree 4 units to
the right, increment both the preliminary x-coordinate and the modifier field of
the subtree's root by 4. As another example, the modifier field associated with
the apex node of the tree is used in determining the final position of all of its
descendants. (The term apex node is used here to distinguish the root of the
entire tree from the roots of individual internal subtrees.)
The first tree traversal is a postorder traversal, positioning the smallest sub-
trees (the leaves) first and recursively proceeding from left to right to build up
the position of larger and larger subtrees. Sibling nodes are always separated
from one another by at least a predefined minimal distance (the sibling sepa-
ration); adjacent subtrees are separated by at least a predefined subtree sepa-
ration. Subtrees of a node are formed independently and placed as close
together as these separation values allow.
As the tree walk moves from the leaves to the apex, it combines smaller sub-
trees and their root to form a larger subtree. For a given node, its subtrees are
positioned one-by-one, moving from left to right. Imagine that its newest
subtree has been drawn and cut out of paper along its contour. Superimpose
the new subtree atop its neighbor to the left, and move them apart until no two
When pushing a new, large subtree farther and farther to the right, a gap may
open between the large subtree and smaller subtrees that had been previously
positioned correctly, but now appear to be bunched on the left with an empty
area to their· right. This produces an undesirable appearance; this character-
istic of left-to-right gluing was the failing of the algorithms by Sweet, Wetherell
and Shannon, and Tilford.
Since the algorithm operates by making two recursive walks of the tree, several
variables are taken to be global for the sake of runtime efficiency. These vari-
ables are described below, alphabetically. All other variables are local to their
respective procedures and functions.
Variable Description
LeveiZeroPtr
The algorithm maintains a list of the previous node at each level,
that is, the adjacent neighbor to the left. Leve!ZeroPtr is a pointer
to the first entry in this list.
xTopAdjustment
A fixed distance used in the final walk of the tree to determine the
absolute x-coordinate of a node with respect to the apex node of
the tree.
yTopAdjustment
A fixed distance used in the final walk of the tree to determine the
absolute y-coordinate of a node with respect to the apex node of
the tree.
The following global values must be set before the algorithm is called; they are
not changed during the algorithm. They can be coded as constants.
·constant Description
Leve/Separation
The fixed distance between adjacent levels of the tree. Used in
determining they-coordinate of a node being positioned.
MaxDepth The maximum number of levels in the tree to be positioned. If all
levels are to be positioned, set this value to positive infinity (or an
appropriate numerical value).
SiblingSeparation
The minimum distance between adjacent siblings of the tree.
SubtreeSeparation
The minimum distance between adjacent subtrees of a tree. For
proper aesthetics, this value is normally somewhat larger than
SiblingSeparation.
The Algorithm 6
For each node, the algorithm uses nine different functions. These might be
stored in the memory allocated for each node, or they might be calculated for
each node, depending on the internal structure of your application.
Function Description
PARENT(Node)
The current node's hierarchical parent
FIRSTCH/LD(Node)
The current node's leftmost offspring
LEFTSIBL/NG(Node)
The current node's closest sibling node on the left.
RIGHTS!BLING(Node}
The current node's closest sibling node on the right
XCOORD(Node)
The current node's x-coordinate
YCOORD(Node}
The current node's y-coordinate
PRELIM(Node)
The current node's preliminary x-coordinate
MODIFIER(Node)
The current node's modifier value
LEFTNE!GHBOR(Node)
The current node's nearest neighbor to the left, at the same level
Upon entry to POSITIONTREE, the first four functions-the hierarchical
relationships-are required for each node. Also, XCOORD and YCOORD of the
apex node are required. Upon its successful completion, the algorithm sets the
XCOORD and YCOORD values for each node in the tree.
The Algorithm 7
function POSITIONTREE (Node): BOOLEAN;
begin
if Node I ¢ then
begin
(*Initialize the list of previous nodes at each level. *)
INITPREVNOOEL!ST;
Figure 1. Function POSITIONTREE. This function determines the coordinates for each
node in a tree. A pointer to the apex node of the tree is passed as input.
This assumes that the x and y coordinates of the apex node are set as
desired, since the tree underneath it will be positioned with respect to those
coordinates. Returns TRUE if no errors, otherwise returns FALSE.
The Algorithm 8
procedure FIRSTVIALK (Node, Leve 1):
begin
(*Set the pointer to the previous node at this level. *)
LEFTNEIGHBOR(Node) ~ GETPREVNODEATLEVEL(Level);
SETPREVNODEATLEVEL(Level, Node); (* This is now the previous. *)
fiODIFIER(Node) ~ B; (*Set the default modifier value. *)
if (ISLEAF(Node) or Level = l·laxDepth) then
begin
if HASLEFTSIBLING(Node) then
(* Determine the preliminary x-coordinate based on: *)
(* the preliminary x-coordinate of the left sibling, *)
(* the separation between sibling nodes, and *)
(* tne mean size of left sibling and current node. *)
PREUM(Node) • PREL!fi(LEFTSIBUNG(Node)) +
SiblingSeparation +
14EANNODES I ZE (LEFTS I BUIIG (!lode) , Node) ;
else
(*No sibling on the left to worry about. *)
PRELIM(Node) ~ G;
end;
else
(* This Node is not a leaf, so call this procedure *)
(* recursively for each of its offspring. *)
begin
Leftmost~ Rightmost~ FIRSTCHILD(Node);
FIRST\vALK(Leftmost, Level + 1);
while HASRIGHTSIBLING(Rightmost) do
begin
Rightmost • RIGHTSIBLING(Rightmost);
FIRSTVIALK(Rightmost, Level + 1);
end;
flidpoint ~ (PREL!fi(Leftmost) + PRELHI(Rightmost)) / 2;
if HASLEFTSIBLING(Node) then
begin
PRELHI(Node) ~ PRELIM(LEFTSIBLING(Node)) +
SiblingSeparation +
fiEANNODESIZE(LEFTSIBLHIG(Node), llode);
I·IODIFIER(Node) • PRELII-I(Node) - l·lidpoint;
APPORTION(Node, Level);
end;
else
PRELIM(Node) ~ Midpoint;
end;
end.
Figure 2. Procedure FIRSTVVALK. In this first postorder walk, every node of the tree is
assigned a preliminary x-coordinate (held in field PRELIM(Node)). In addition,
internal nodes are given modifiers, which will be used to move their offspring
to the right (held in field MODIFIER(Node)).
The Algorithm 9
function SECOND'!IALK (Node; Level, flodsum): BOOLEAN;
begin
if Level ~ l·laxDepth then
begin
xTemp ~ xTopAdj us tment + PREL!fl (Node) + flods urn;
yTemp ~ yTopAdjustment + (Level * LevelSeparation);
(* Check to see that xTemp and yTemp are of the proper *)
(* size for your application. *)
if CHECKEXTENTSRANGE(xTemp, yTemp) then
begin
XCOORD(Node) ~ xTemp;
YCOORD(Node) ~ yTemp;
if HASCHILD(fiode) then
(*Apply the flodifier value for this node to *)
(*all its offspring. *)
Result ~ SECONDHALK(FIRSTCHILD(IIode),
Level + 1,
flodsum + HODIFIER(Node));
if (Result= TRUE and
HASRIGHTSIBLING(Node)) then
Result ~ SECOND\~ALK(RIGHTSIBLING(flode),
Level + 1,
l·lodsum);
end;
else
(* Continuing would put the tree outside of the *)
(* drawable extents range. *)
Result ~ FALSE;
end;
else
(* We are at a level deeper than 1·1hat we want to draw. *)
Result ~ TRUE;
return Result;
end.
Figure 3. Function SECONDWALK. During a second preorder walk, each node is given
a final x-coordinate by summing its preliminary x-coordinate and the modifiers
of all the node's ancestors. The y-coordinate depends on the height of the
tree. If the actual position of an interior node is right of its preliminary place,
the subtree rooted at the node must be moved right to center the sons
around the father. Rather than immediately readjust all the nodes in the
subtree, each node remembers the distance to the provisional place in a mod-
ifier field (MODIFIER(Node)). In this second pass down the tree, modifiers are
accumulated and applied to every node. Returns TRUE if no errors, otherwise
returns FALSE.
The Algorithm 10
procedure APPORTION (Node, Level):
begin
Leftmost~ FIRSTCHILD(Node);
Neighbor~ LEFTNEIGHBOR(Leftmost);
CompareDepth ~ 1;
DepthToStop ~ MaxDepth- Level;
The Algorithm 11
if TempPtr f ¢ then
(*Apply portions to appropriate leftsibling *)
(* subtrees. *)
begin
Portion~ l~oveOistance j LeftSiblings;
TempPtr ~ Node;
while
TempPtr = AncestorNeighbor do
begin
PREWI(TempPtr) ~
PRELHI(TempPtr) + fioveOistance;
MOOIFIER(TempPtr) ~
l·iOOIFIER(TempPtr) + l·ioveDistance;
MoveDi stance ,... f·1ove0i stance -
Portion;
TempPtr ~ LEFTSIBLHIG(TempPtr);
end;
end;
else
(* Don't need to move anything--it needs to *)
(* be done by an ancestor because *)
(* AncestorNeighbor and AncestorLeftmost are *)
(* not siblings of each other. *)
return;
end; (* of MoveDistance > 0 *)
CompareDepth ~ CompareDepth + 1;
if ISLEAF(Leftmost) then
Leftmost <- GETLEFTfiOST(Node, 0, CompareDepth);
else
Leftmost~ FIRSTCHILD(Leftmost);
The Algorithm 12
function GETLEFTfiOST (Node, Level, Depth): NODE;
begin
if Level ~Depth then
return Node;
else if !SLEAF(Node) then
return ¢;
else begin
Rightmost~ FIRSTCHILD(Node);
Leftmost ~ GETLEFTt-IOST (Rightmost, Leve 1 + 1, Depth);
(* Do a postorder walk of the subtree below Node. *)
while (Leftmost = ¢ and
HASRIGHTS!BLING(Rightmost)) do
begin
Rightmost~ R!GHTSIBL!NG(Rightmost);
Leftmost ~ GETLEFTt-IOST(Rightmost, Level + 1, Depth);
end;
return Leftmost;
end;
end.
if LeftNode t ¢then
NodeSize ~ NodeSize + RIGHTSIZE(LeftNode);
if RightNode t ¢ then
NodeSize ~ NodeSize + LEFTS!ZE(RightNode);
return NodeSize;
end.
Figure 7. Function MEANNODESIZE. This function returns the mean size of the two
passed nodes. It adds the size of the right half of lefthand node to the left
half of righthand node. If all nodes are the same size, this is a trivial calcu-
lation.
The Algorithm 13
function CHECKEXTENTSRANGE (xValue, yValue): BOOLEAN;
begin
if (xValue is a valid value for the x-coordinate) and
(yValue is a valid value for the y-coordinate) then
return TRUE;
else
return FALSE;
end.
procedure INITPREVNODELIST:
begin
(* Start with the node at level 8--the apex of the tree. *)
TempPtr • LevelZeroPtr;
while TempPtr f ¢do
begin
PREVNODE(TempPtr) • ¢;
TempPtr • NEXTLEVEL(TempPtr);
end;
end.
Figure 9. Initialize the list of prev_ious nodes at each level. Three Jist-maintenance pro-
cedures, GETPREVNODEATLEVEL, SETPREVNODEATLEVEL, and
INITPREVNODELIST. maintain a singly-linked list. Each entry in the list corre-
sponds to the node previous to the current node at a given level (for example,
element 2 in the list corresponds to the node to the left of the current node at
level 2). If the maximum tree size is known beforehand, this Jist can be
replaced with a fixed-size array, and these procedures become trivial.
Each list element contains two fields: PREVNODE-the previous node at this
level, and NEXTLEVEL-a forward pointer to the next list element. The list is
does not need to be cleaned up between calls to POSITIONTREE, for perform-
ance.
The Algorithm 14
function GETPREVNOOEATLEVEL (Level): NODE;
begin
(* Start with the node at level 0--the apex of the tree. *)
TempPtr ~ LevelZeroPtr;
i ~ 0;
while TempPtr i ¢ do
begin
if i = Level then
return PREVNODE(TempPtr)
TempPtr ~ NEXTLEVEL(TempPtr);
if-i+l;
end;
(*Otherwise, there 11as no node at the specific level. *)
return ¢;
end.
Figure 10. Get the previous node at this level. See Figure 9.
The Algorithm 15
procedure SETPREVNOOEATLEVEL (Level, Node):
begin
(* Start with the node at level 6--the apex of the tree. *)
TempPtr ~ LevelZeroPtr;
i ~ 0;
while TempPtr ¢do r
begin
if i = Leve 1 then
begin
(*At this level, replace the existing list *)
(*element ~<ith the passed-in node. *)
PREVNODE(TempPtr) ~ Node;
return;
end;
else if NEXTLEVEL(TempPtr) = ¢ then
(*There isn•t a lfst element yet at this level, so *)
(* add one. The follo~<ing instructions prepare the *)
(* list element at the next level, not at this one. *)
begin
Ne~<Node ~ ALLOCATE_A_NODE;
PREVNODE(NewNode) ~ ¢;
NEXTLEVEL(NewNode) ~ ¢;
NEXTLEVEL(TempPtr) ~ NewNode;
end;
Figure 11. Set an element in the list. See Figure 9 on page 14. Function
"ALLOCATE_A_NODE" (not shown here) requests a pointer to a block of
memory, to be used to represent a node in the list.
The Algorithm 16
An Example
The operation of the algorithm during these two walks can be best illustrated
with an example. At least three levels are needed to illustrate its operation.
since a small subtree must be centered between larger sibling subtrees. The
following figure is an example tree positioned by this algorithm. Its fifteen
nodes have been lettered in the order that they are visited in the first postorder
traversal. For this example, the mean size of each node is 2 units and the
sibling separation and subtree separation values are the same: 4 units.
I
G
n
G
An Example 17
B is also a leaf with no left sibling.
PREWI(B) = 0
I·IODIFIER(B) = 0
C is the right sibling of node B. It is separated from it by the sibling sepa-
ration value plus the mean size of the two nodes.
PREWI (C) = 0 + 4 + 2 =6
I·IODIFIER(C) = 0
0 is the parent of nodes B and C, and the right sibling of node A. It is
separated from node A by the sibling separation value plus the mean
size of the two nodes. Its modifier is set so that when it is applied to
nodes B and C, they will appear centered underneath it. The modifier is
determined by taking PRELIM(D) and subtracting the mean of the
PRELIM(its mostly widely-separated offspring) values.
PRELIH(D) = 0 + 4 + 2 = 6
fiDDIFIER(D) = 6 - (0 + 6)/2 =3
E is the parent of nodes A and D. It is centered over nodes A and D.
PRELIH(E) = (0 + 6)/2 =3
MDDIFIER(E) = 8
F is a right siblfng of node E. It is separated from it by the sibling sepa-
ration value plus the mean size of the two nodes. That would place it
directly over node C. We can see now that node N's subtree will later
be placed much further to the right, leaving the spacing between nodes
E and F smaller, and hence different, than the spacing between nodes F
and N. ·When node N is finally positioned, the position of node F will be
adjusted. But for now,
PRELIM(F) = 3 + 4 + 2 =9
HODIFIER(F) = 8
G is a leaf with no left sibling.
PRELIM(G) = 0
MDDIFIER(G) = 0
H is a leaf with no left sibling.
PRELIM (H) = 0
MODIFIER(H) = 0
is the right sibling of node H. It is separated from it by the sibling sepa-
ration value plus the mean size of the two nodes.
PRELIM(!) = 0 + 4 + 2 =6
MODIFIER(!) = 0
J is the right sibling of node I. As above, it is separated by the standard
spacing from node I.
PREL!H(J) = 6 + 4 + 2 = 12
MODIFIER(J) = 0
An Example 18
K is the right sibling of node J.
PRELHI(K) = 12 + 4 + 2 = 18
I·IODIFIER(K) = 0
L is the right sibling of node K.
PRELIM(L) = 18 + 4 + 2 = 24
I·IOOIFIER(L) = 0
M is the parent of nodes H through L, and the right sibling of node G. It is
separated from node G by the sibling separation value plus the mean
size of the two nodes. Its modifier is set so that when it is applied to
nodes H through L, they will appear centered underneath it.
PRELH~ (M) = 0 + 4 + 2 = 6
I·IODIFIER(fl) = 6 - (0 + 24)/2 = -6
N is the parent of nodes G and M, and the right sibling of node F. It is
first of all given its standard positioning to the right of node F, with a
modifier that reflects the centering of its offspring beneath it.
PRELHI(N) = 9 + 4 + 2 = 15
I-IODIFIER(N) = 15 - (0 + 6)/2 = 12
Now we have to verify that node E's subtree and node N's subtree are
properly separated.
Moving down one level, the leftmost descendant of node N, node G, cur-
rently has a positioning of 0 + 12 = 12 (PRELIM(G) plus the
MODIFIER(N). its parent). The rightmost descendant of node E, node D
is positioned at 6 + 0 = 6 (PRELIM(D) plus the MODIFIER(E), its
parent). Their difference is 12 - 6 = 6, which is equal to the minimum
separation (subtree separation plus mean node size), so N's subtree
does not need to be moved, since there is no overlap at this level.
Moving down one more level, the leftmost descendant of node N is node
H. It is positioned at 0 + -6 + 12 = 6 (PRELIM(H) plus MODIFIER(M)
and MODIFIER(N)). The rightmost descendant of node E, node C, is
positioned at 6 + 3 + 0 = 9 (PRELIM(C) plus MODIFIER(D) and
MODIFIER(E)). Their difference is 6 - 9 = -3; it should be 6, the
minimum subtree separation plus the mean node size. Thus node N
and its subtree need to· be moved to the right a distance of 6- -3 = 9.
PRELIM(N) = 15 + 9 = 24
I·IODIFIER(N) = 12 + 9 = 21
This opens a gap of size 9 between sibling nodes E and N. This differ-
ence needs to be evenly distributed to all contained sibling nodes, and
node F is the only one. Node F is moved to the right a distance of 9/2
= 4.5.
PRELIM(F) = 9 + 4.5 = 13.5
I-IODIFIER(F) = 0 + 4.5 = 4.5
0 is the parent of nodes E, F, and N. It is positioned halfway between the
position of nodes E and N.
An Example 19
PREWI(O) = (3 + 24)/2 = 13.5
~IODIFIER(O) =
0
0 13.5
E 3+0=3
A 0+0+0=0
D 6+0+0=6
B 0+3+0+0=3
c 6+3+0+0=9
F 13.5 + 0 = 13.5
N 24 + 0 = 24
G 0 + 21 + 0 = 21
M 6 + 21 + 0 = 27
H 0 + -6 + 21 + 0 = 15
6 + -6 + 21 + 0 = 21
J 12 + -6 + 21 + 0 = 27
K 18 + -6 + 21 + 0 = 33
L 24 + -6 + 21 + 0 = 39
An Example 20
Changing the Orientation of the Root
The algorithm illustrates tree positioning where the apex of the tree is at the
top of the drawing. Some simple modifications allow other common posi-
tionings, such as where the root is on the left and the siblings are to its right.
Four such orientations of the root can be readily identified; these will be the
values taken by a new global constant, RootOrientation, to be set before the
algorithm is called.
NORTH root is at the top, as shown in the preceding algorithm
SOUTH root is at the bottom, its siblings are above it
EAST root is at the left, its siblings are to its right
WEST root is at the right, its siblings are to its left
Figure 13. Function POSITIONTREE. The final position of the tree's apex depends on
the RootOrientation value.
Figure 14. Function SECONDWALK. The values of xTemp and yTemp now depend on
the RootOrientation value.
Figure 15. Function MEANNODESIZE. This function now returns the mean width of the
two nodes if the RootOrientation is NORTH or SOUTH; if the RootOrientation
is EAST or WEST, it returns the mean height of the two nodes.
A
,--.,1_--,,
!
• 0
~---_J_ _ _ _ _ ci
I , ,1, ,1,
lA very wide subtreeJ HI J l
Figure 16. Sweet's illustration showing deficiencies in his algorithm. From page 96 of
Sweet.10
Previous Work 24
gluing order, because Algorithm 5.1 always puts a pair of subtrees as
close together as possible.
The problem arises when a tree has the general shape shown in Figure
5.2, in which two non-adjacent subtrees are large, and the intervening
ones are small enough that there is freedom in deciding where to place
them. Although this will not always lead via Algorithm 5.1 to a violation
of Aesthetic 4 [which states: "A tree and its mirror image should
produce drawings that are renections of one another; moreover, a
subtree should be drawn the same way regardless of where it occurs in
the tree."], it is clear that the small subtrees ought to be spaced out
evenly rather than bunched up on one side or the other.
Figure 1·l· A ~all ternary tree positioned (a) by Algorithm 5.1, with
left-to-right gluing; (b) by Algorithm 5.1, with right-to-left gluing;
and (e) ideally.
Figure 17. Examples of centering vs. two types of gluing. From page 37 of Tilford.ll
Figure 5.2. The general shape of a tree for which Algorithm 5.1
produce8&n unsatisfactorY positioning.
Figure 18. Tilford's illustration showing deficiencies in his algorithm. From page 38 of
Tilford."
Previous Work 25
Radack7 follows directly from Tilford's work, solving the left-to-right-gluing
problem by essentially running his algorithm twice. The first time. the subtrees
are agglutinated left to right; the second time, from right to left. A node is posi-
tioned at the average of the two assigned positions. Radack's algorithm posi-
tions the nodes in four passes.
The algorithms by Manning and Atallah' are examples of the class of algo-
rithms that do node positioning with a different set of aesthetic rules; their
primary goal was to highlight the symmetry inherent in hierarchical relation-
ships.
Previous Work 26
Acknowledgements
Thanks to Jane Munn, Jim Staton, and Dr. Bill Wright at IBM who reviewed an
earlier version of this paper. Bob Gibson and John Broughton have also given
me a lot of help.
Acknowledgements 27
References
[1] Knuth, D.E. Optimum Binary Search Trees. Acta Informatica 1 (1971)
14-25.
[3] Manning, J.B. and M.J. Atallah. Fast Detection and Display of Symmetry
in Trees. Congressus Numerantium 64 (November 1988) 159-169.
[8] Reingold, E.M. and J.S. Tilford. Tidier Drawings of Trees. IEEE Trans-
actions on Software Engineering SE-7, 2 (March 1981) 223-228.
[9] Supowit, K.J. and E.M. Reingold. The complexity of drawing trees nicely.
Acta Informatica 18, 4 (January 1983) 377-392.
[11] Tilford, J.S. Tree drawing algorithms. M.S. Thesis, Department of Com-
puter Science, University of Illinois, Urbana, IL, Report
UIUCDCS-R-81-1055, April 1981. Available as document UILU-ENG-81-1711
from the College of Engineering Document Center at the Univ. of Illinois.
[12] Vaucher, J.G. Pretty-Printing of Trees. Software- Practice and Experience
10 (1980) 553-561.
[13] Wetherell, C.S. and A. Shannon. Tidy Drawings of Trees. IEEE Trans-
actions on Software Engineering SE-5, 5 (September 1979) 514-520.
References 28
An Example Underlying Tree Structure
In this example, I use the internal tree notation described by Knuth [Reference
2, section 2.3.3] for a triply-linked tree. Each node consists of three pointers,
FATHER, LSON, and RLINK, and its information in field INFO. FATHER points to
the parent of the node. LSON points to the leftmost offspring of a node. RLINK
points to the right sibling of a node. Thus, if node T is the root of a binary tree,
the root of its left subtree is LSON(T) and the root of its right subtree is
RLINK(LSON(T)).
This node structure is illustrated below, using the syntax of the C programming
language.
struct position (
l*------------------------------------------------------------------------*1
/*This structure contains the node positioning information. 'I
/* I 1 ve used floating point values here; use integer values, if *I
/* necessary, but you may need to handle rounding errors. *J
1*------------------------------------------------------------------------*l
float x_coordinate; I* the value identified as XCOORO(Node) *I
float y_coordinate; /*the value identified as YCOORD(Node) *I
float preliminary; /*the value identified as PRELII·I(Node) *I
float modifier; /*the value identified as 1·10DIFIER(Node) *I
};
struct information. (
l*------------------------------------------------------------------------*1
/*This structure contains whatever node information your application 'I
I* requires. Here I show a fixed-length, 80-character label for the node. *I
I* If the sizes of the nodes differ, a node's width or height could be *I
I* included here (see function fiEANNODESIZE). *I
1*------------------------------------------------------------------------*l
char node_ label [80];
};
struct node {
struct node *father; /*pointer to the parent of this node */
struct node *lson; /*pointer to this node's leftmost offspring 'I
struct node *rlink; /*pointer to the right sibling of this node *I
struct node *left_neighbor; /*pointer to the adjacent node to the left. *I
struct position pas; J* positioning values, as defined above */
struct information info; /* node information, as defined above */
};