]> git.mjollnir.org Git - moodle.git/blob
6566f714dd8c8ee8e3f40d4dc40c2925c36f664e
[moodle.git] /
1 package flare.vis.operator.filter
2 {
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;
10
11         /**
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.
22          * 
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>
29          */
30         public class FisheyeTreeFilter extends Operator
31         {
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;
36                 
37                 private var _divisor:Number;
38                 private var _root:NodeSprite;
39                 private var _t:Transitioner;
40                 
41                 /**
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
47                  */             
48                 public function FisheyeTreeFilter(focusNodes:Array, distance:int=1) {
49                         this.focusNodes = focusNodes;
50                         this.distance = distance;
51                 }
52                 
53                 /** @inheritDoc */
54                 public override function operate(t:Transitioner=null):void
55                 {
56                         _t = (t==null ? Transitioner.DEFAULT : t);
57                         var tree:Tree = visualization.tree;
58                         _divisor = tree.nodes.size;
59                 _root = tree.root;
60         
61                 // mark the items
62                 visualization.data.visit(function(ds:DataSprite):void {
63                         ds.props.doi = -Number.MAX_VALUE;
64                 }, Data.ALL);
65                 
66                 // compute the fisheye over nodes
67                 for each (var fn:NodeSprite in focusNodes) {
68                         visitFocus(fn, null);
69                 }
70                 visitFocus(_root, null);
71         
72                 // mark unreached items
73                 visualization.data.visit(function(ds:DataSprite):void {
74                         if (ds.props.doi == -Number.MAX_VALUE)
75                                 setVisibility(ds, false);
76                 }, Data.ALL);
77                 }
78                 
79                 /**
80                  * Set the visibility of a data sprite.
81                  */
82                 private function setVisibility(ds:DataSprite, visible:Boolean):void
83                 {
84                         var alpha:Number = visible ? 1 : 0;
85                         var obj:Object = _t.$(ds);
86                                 
87                         obj.alpha = alpha;
88                         if (ds is NodeSprite)
89                                 (ds as NodeSprite).expanded = (ds.props.doi > -distance);
90                         if (_t.immediate) {
91                                 ds.visible = visible;
92                         } else {
93                                 obj.visible = visible;
94                         }
95                 }
96                 
97                 /**
98              * Visit a focus node.
99              */
100             private function visitFocus(n:NodeSprite, c:NodeSprite):void
101             {
102                 if (n.props.doi <= -1 ) {
103                     visit(n, c, 0, 0);
104                     if (distance > 0) visitDescendants(n, c);
105                     visitAncestors(n);
106                 }
107             }
108             
109             /**
110              * Visit a specific node and update its degree-of-interest.
111              */
112             private function visit(n:NodeSprite, c:NodeSprite, doi:int, ldist:int):void
113             {
114                 setVisibility(n, true);
115                 var localDOI:Number = -ldist / Math.min(1000.0, _divisor);
116                 n.props.doi = doi + localDOI;
117                 
118                 if (c != null) {
119                         var e:EdgeSprite = c.parentEdge;
120                         e.props.doi = c.props.doi;
121                         setVisibility(e, true);
122                 }
123             }
124             
125             /**
126              * Visit tree ancestors and their other descendants.
127              */
128             private function visitAncestors(n:NodeSprite):void
129             {
130                 if (n == _root) return;
131                 visitFocus(n.parentNode, n);
132             }
133             
134             /**
135              * Traverse tree descendents.
136              */
137             private function visitDescendants(p:NodeSprite, skip:NodeSprite):void
138             {
139                 var lidx:int = (skip == null ? 0 : skip.parentIndex);
140                 
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;
145                         
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);
149                 }
150             }
151
152                 
153         } // end of class FisheyeTreeFilter
154 }