Copyright (C) 1992, Digital Equipment Corporation
All rights reserved.
See the file COPYRIGHT for a full description.
by Steve Glassman and Stephen Harrison
Last modified on Sat Jul 18 15:35:20 PDT 1992 by harrison
modified on Wed May 20 01:44:58 1992 by steveg
<*PRAGMA LL*>
MODULE RadialTree;
A TreeView provides the basic structure for a tree. A tree must be
further sub-typed to provide more appropriate or efficient
representations for specific kinds of trees
IMPORT GenericTree, Math, MG, R2, R2Box;
CONST
Pi = FLOAT(Math.Pi, LONGREAL);
TwoPi = 2.0D0 * Pi;
TYPE
GV = GenericTree.V;
GT = GenericTree.GenericTree;
GST = GenericTree.SubTree;
REVEAL
(* parent of a Tree must be a Tree. angle field is set if child is a
tree *)
V = GV BRANDED OBJECT END;
T = GT BRANDED OBJECT
depth := 0;
angle := 0.0D0;
radius := 0.0;
OVERRIDES
init := Init;
calculateSize := Size;
translate := Translate;
END;
calculate the maximum child radius, then the actual radius of node.
Must be MAX(2 * max child radius + node's graphic's radius, max child
radius / sin(2 * pi/num children)) latter is the chord distance between
children
PROCEDURE Size (node: T; v: GV) =
VAR
maxChildDiameter, chordalDiameter := 0.0;
diameter: REAL;
ch := node.succ(v, NIL);
bounds := node.graphic.appearance.boundingBox(node.graphic, v);
size := R2Box.Size(bounds);
BEGIN
node.radius := MAX(size[0], size[1]) / 2.0;
IF node.numChildren > 0 THEN
WHILE ch # NIL DO
maxChildDiameter := MAX(maxChildDiameter, ch.width);
ch := node.succ(v, ch);
END;
IF node.numChildren > 2 THEN
chordalDiameter :=
maxChildDiameter / FLOAT(
Math.sin(Pi / FLOAT(node.numChildren, LONGREAL)));
END;
node.radius :=
node.radius + MAX(node.dxChildren + maxChildDiameter / 2.0,
chordalDiameter / 2.0);
END;
diameter := 2.0 * node.radius + maxChildDiameter;
node.width := diameter;
node.height := diameter;
END Size;
center the node at north - diameter/2, west + diameter
PROCEDURE Translate (node: T; v: GV; north, west: REAL) =
VAR
ppos := GenericTree.ParentPos(node.parent, v);
xCenter := ppos[0] + west + node.width / 2.0;
yCenter := ppos[1] + north - node.height / 2.0;
ch := node.succ(v, NIL);
pos := MG.PosLocked(node, v);
BEGIN
IF node.parent = NIL THEN
node.depth := 0;
ELSE
node.depth := NARROW(node.parent, T).depth + 1;
END;
IF GenericTree.LinearAnimation(
v, R2.T{xCenter - pos[0], yCenter - pos[1]}, node) THEN
VAR
alpha := TwoPi / FLOAT(MAX(2, node.numChildren), LONGREAL);
theta := node.angle;
BEGIN
IF node.depth # 0 THEN
theta := theta + Pi + alpha / 2.0D0;
IF theta > TwoPi THEN theta := theta - TwoPi END;
END;
WHILE ch # NIL DO
TYPECASE ch OF
| T (treeChild) => treeChild.angle := theta;
ELSE
END;
ch.translate(
v, node.radius * FLOAT(Math.sin(theta)) + ch.height / 2.0,
node.radius * FLOAT(Math.cos(theta)) - ch.width / 2.0);
ch := node.succ(v, ch);
theta := theta + alpha;
END;
END;
END;
END Translate;
PROCEDURE Init (t: T; v: GV; graphic: MG.T): GST =
BEGIN
EVAL GT.init(t, v, graphic);
WITH diameter = MAX(t.width, t.height),
dChildren = MAX(t.dxChildren, t.dyChildren) DO
t.width := diameter;
t.height := diameter;
t.dxChildren := dChildren;
t.dyChildren := dChildren;
END;
RETURN t;
END Init;
BEGIN
END RadialTree.