1 package flare.vis.operator.layout
3 import flare.animate.Transitioner;
4 import flare.util.Property;
5 import flare.vis.data.EdgeSprite;
6 import flare.vis.data.NodeSprite;
9 * Layout that places items in a dendrogram for displaying the results of a
10 * hierarchical clustering. This class computes a dendrogram layout and sets
11 * edge control points to create "U" shaped dendrogram branches. It is
12 * common, though by no means required, to hide the node instances in a
15 * <p>To determine the height of dendrogram branches, a distance property
16 * can be provided. The values of this property will directly determine
17 * node heights by laying out the depth axis using a linear scale of
18 * distance values. The distance property should be set for all non-leaf
19 * nodes in the tree. Typically, leaf nodes have a distance of zero,
20 * resulting in an aligned list of leaf nodes.</p>
22 public class DendrogramLayout extends Layout
24 // TODO: support axes, too
26 private var _orient:String = Orientation.TOP_TO_BOTTOM; // the orientation of the tree
27 private var _dp:Property;
28 private var _t:Transitioner; // temp variable for transitioner access
30 private var _leafCount:int;
31 private var _leafIndex:int = 0;
32 private var _maxDist:Number;
33 private var _b1:Number;
34 private var _db:Number;
35 private var _d1:Number;
36 private var _dd:Number;
38 /** Data property to use as the distance field for
39 * determining the height values of dendrogram branches. */
40 public function get distanceProperty():String { return _dp.name; }
41 public function set distanceProperty(dp:String):void {
45 /** The orientation of the dendrogram */
46 public function get orientation():String { return _orient; }
47 public function set orientation(o:String):void { _orient = o; }
50 * Creates a new DendrogramLayout.
51 * @param distField data property to use as the distance field for
52 * determining the height values of dendrogram branches
53 * @param orientation the orientation of the dendrogram
55 public function DendrogramLayout(distField:String=null,
56 orientation:String=Orientation.TOP_TO_BOTTOM)
58 _dp = distField==null ? null : Property.$(distField);
59 _orient = orientation;
63 public override function operate(t:Transitioner=null):void
65 _t = (t == null ? Transitioner.DEFAULT : t);
67 layout(visualization.tree.root);
71 * Initialize the layout.
73 private function init():void
75 var root:NodeSprite = visualization.tree.root;
76 _leafCount = visualization.tree.countLeaves();
78 _maxDist = _dp!=null ? _dp.getValue(root) : computeHeights(root);
81 case Orientation.TOP_TO_BOTTOM:
82 _b1 = layoutBounds.left;
83 _db = layoutBounds.width;
84 _d1 = layoutBounds.bottom;
85 _dd = -layoutBounds.height;
87 case Orientation.BOTTOM_TO_TOP:
88 _b1 = layoutBounds.left;
89 _db = layoutBounds.width;
90 _d1 = layoutBounds.top;
91 _dd = layoutBounds.height;
93 case Orientation.LEFT_TO_RIGHT:
94 _b1 = layoutBounds.top;
95 _db = layoutBounds.height;
96 _d1 = layoutBounds.right;
97 _dd = -layoutBounds.width;
99 case Orientation.RIGHT_TO_LEFT:
100 _b1 = layoutBounds.top;
101 _db = layoutBounds.height;
102 _d1 = layoutBounds.left;
103 _dd = layoutBounds.width;
108 private function computeHeights(n:NodeSprite):int
111 for (var i:int=0; i<n.childDegree; ++i) {
112 n.u = Math.max(n.u, 1 + computeHeights(n.getChildNode(i)));
117 private function layout(n:NodeSprite):Number
119 var d:Number = _dp!=null ? _dp.getValue(n) : n.u;
120 d = _d1 + _dd * (d / _maxDist);
122 if (n.childDegree > 0) {
123 var b:Number = 0, bc:Number;
124 for (var i:int=0; i<n.childDegree; ++i) {
125 var c:NodeSprite = n.getChildNode(i);
127 layoutEdge(c.parentEdge, bc, d);
131 var step:Number = 1.0 / _leafCount;
132 b = _b1 + _db * step * (0.5 + _leafIndex++);
138 private function layoutNode(n:NodeSprite, b:Number, d:Number):void
140 var o:Object = _t.$(n);
141 if (Orientation.isVertical(_orient)) {
148 private function layoutEdge(e:EdgeSprite, b:Number, d:Number):void
150 var vert:Boolean = Orientation.isVertical(_orient);
151 var o:Object = _t.$(e);
152 if (e.points == null) {
153 var s:NodeSprite = e.source;
154 var t:NodeSprite = e.target;
155 e.points = [(s.x+t.x)/2, (s.y+t.y)/2];
157 _t.$(e).points = vert ? [b, d] : [d, b];
160 } // end of class DendrogramLayout