1 package flare.vis.operator.filter
3 import flare.animate.Transitioner;
4 import flare.vis.data.Data;
5 import flare.vis.data.DataSprite;
6 import flare.vis.data.EdgeSprite;
7 import flare.vis.data.NodeSprite;
8 import flare.vis.data.Tree;
9 import flare.vis.operator.Operator;
12 * Filter operator that computes a fisheye degree-of-interest function over
13 * a tree structure. Visibility and DOI (degree-of-interest) values are set
14 * for the nodes and edges in the structure. This function includes a set
15 * of focus nodes, and includes neighbors only in a limited window around
16 * these foci. The size of this window is determined by this operator's
17 * <code>distance</code> property. All ancestors of a focus up to the root
18 * of the tree are considered foci as well. By convention, DOI values start
19 * at zero for focus nodes, with decreasing negative numbers for each hop
20 * away from a focus. The DOI values computed by this filter are stored in
21 * the <code>DataSprite.props.doi</code> property.
23 * <p>This form of filtering was described by George Furnas as early as 1981.
24 * For more information about Furnas' fisheye view calculation and DOI values,
25 * take a look at G.W. Furnas, "The FISHEYE View: A New Look at Structured
26 * Files," Bell Laboratories Tech. Report, Murray Hill, New Jersey, 1981.
27 * Available online at <a href="http://citeseer.nj.nec.com/furnas81fisheye.html">
28 * http://citeseer.nj.nec.com/furnas81fisheye.html</a>.</p>
30 public class FisheyeTreeFilter extends Operator
32 /** An array of focal NodeSprites. */
33 public var focusNodes:/*NodeSprite*/Array;
34 /** Graph distance within within which items wll be visible. */
35 public var distance:int;
37 private var _divisor:Number;
38 private var _root:NodeSprite;
39 private var _t:Transitioner;
42 * Creates a new FisheyeTreeFilter
43 * @param focusNodes focusNodes an array of focal NodeSprites. Graph
44 * distance is measured as the minimum number of edge-hops to one of
45 * these nodes or their ancestors up to the root.
46 * @param distance graph distance within which items will be visible
48 public function FisheyeTreeFilter(focusNodes:Array, distance:int=1) {
49 this.focusNodes = focusNodes;
50 this.distance = distance;
54 public override function operate(t:Transitioner=null):void
56 _t = (t==null ? Transitioner.DEFAULT : t);
57 var tree:Tree = visualization.tree;
58 _divisor = tree.nodes.size;
62 visualization.data.visit(function(ds:DataSprite):void {
63 ds.props.doi = -Number.MAX_VALUE;
66 // compute the fisheye over nodes
67 for each (var fn:NodeSprite in focusNodes) {
70 visitFocus(_root, null);
72 // mark unreached items
73 visualization.data.visit(function(ds:DataSprite):void {
74 if (ds.props.doi == -Number.MAX_VALUE)
75 setVisibility(ds, false);
80 * Set the visibility of a data sprite.
82 private function setVisibility(ds:DataSprite, visible:Boolean):void
84 var alpha:Number = visible ? 1 : 0;
85 var obj:Object = _t.$(ds);
89 (ds as NodeSprite).expanded = (ds.props.doi > -distance);
93 obj.visible = visible;
100 private function visitFocus(n:NodeSprite, c:NodeSprite):void
102 if (n.props.doi <= -1 ) {
104 if (distance > 0) visitDescendants(n, c);
110 * Visit a specific node and update its degree-of-interest.
112 private function visit(n:NodeSprite, c:NodeSprite, doi:int, ldist:int):void
114 setVisibility(n, true);
115 var localDOI:Number = -ldist / Math.min(1000.0, _divisor);
116 n.props.doi = doi + localDOI;
119 var e:EdgeSprite = c.parentEdge;
120 e.props.doi = c.props.doi;
121 setVisibility(e, true);
126 * Visit tree ancestors and their other descendants.
128 private function visitAncestors(n:NodeSprite):void
130 if (n == _root) return;
131 visitFocus(n.parentNode, n);
135 * Traverse tree descendents.
137 private function visitDescendants(p:NodeSprite, skip:NodeSprite):void
139 var lidx:int = (skip == null ? 0 : skip.parentIndex);
141 p.expanded = p.childDegree > 0;
142 for (var i:int=0; i<p.childDegree; ++i) {
143 var c:NodeSprite = p.getChildNode(i);
144 if (c == skip) continue;
146 var doi:int = int(p.props.doi - 1);
147 visit(c, c, doi, Math.abs(lidx-i));
148 if (doi > -distance) visitDescendants(c, null);
153 } // end of class FisheyeTreeFilter