1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.6. 
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2000-2008 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.view.layout.organic;
15  
16  import y.algo.Bfs;
17  import y.anim.AnimationFactory;
18  import y.anim.AnimationObject;
19  import y.base.Edge;
20  import y.base.EdgeCursor;
21  import y.base.EdgeList;
22  import y.base.EdgeMap;
23  import y.base.ListCell;
24  import y.base.Node;
25  import y.base.NodeCursor;
26  import y.base.NodeList;
27  import y.base.NodeMap;
28  import y.base.YList;
29  import y.geom.YPoint;
30  import y.util.DefaultMutableValue2D;
31  import y.util.Maps;
32  import y.view.Drawable;
33  import y.view.EdgeRealizer;
34  import y.view.Graph2D;
35  import y.view.NodeRealizer;
36  import y.view.ViewAnimationFactory;
37  
38  import javax.swing.SwingUtilities;
39  
40  /**
41   * This demo is an extension of {@link demo.view.layout.organic.NavigationDemo}.
42   * <br>
43   * In this demos changing the visible subgraph is now accompanied by animated
44   * fade-in and fade-out effects for apprearing and disappearing nodes and edges.
45   * Additionally, the initial positions of nodes that newly appear in the graph
46   * are set to the position of their already visible parent node.
47   **/
48  public class AnimatedNavigationDemo extends NavigationDemo {
49    private NodeMap hiddenNodesMap;
50    private EdgeMap hiddenEdgesMap;
51  
52    public static void main( String[] args ) {
53      initLnF();
54      SwingUtilities.invokeLater( new Runnable() {
55        public void run() {
56          final AnimatedNavigationDemo navigationDemo = new AnimatedNavigationDemo();
57          navigationDemo.start( "Animated Navigation Demo" );
58          navigationDemo.moveFirstNodeToCenter();
59        }
60      } );
61    }
62  
63    public AnimatedNavigationDemo() {
64      // remember which nodes are hidden
65      hiddenNodesMap = view.getGraph2D().createNodeMap();
66      hiddenEdgesMap = view.getGraph2D().createEdgeMap();
67    }
68  
69    /**
70     * This list contains the history of the center nodes.
71     */
72    private YList history = new YList();
73  
74    protected void moveToCenter( final Node newCenterNode, boolean animated ) {
75      if ( ! SwingUtilities.isEventDispatchThread() ) {
76        throw new IllegalStateException( "not in dispatch thread" );
77      }
78      this.centerNode = newCenterNode;
79  
80      // The new centered node is "pinned" It will no longer be moved by the layouter.
81      layouter.setInertia(newCenterNode, 1);
82      if (history.size() < 4) {
83        history.addFirst( newCenterNode );
84      } else {
85        ListCell lastCell = history.lastCell();
86        // The "older" centered nodes are moveable again.
87        layouter.setInertia( ( Node ) lastCell.getInfo(), 0);
88        history.removeCell(lastCell);
89        lastCell.setInfo(newCenterNode);
90        history.addFirstCell(lastCell);
91      }
92  
93      //The elements that will change state
94      NodeList fadeInNodes = new NodeList();
95      NodeList fadeOutNodes = new NodeList();
96      EdgeList fadeInEdges = new EdgeList();
97      EdgeList fadeOutEdges = new EdgeList();
98  
99      // the elements that will be hidden finally
100     final EdgeList hiddenEdges = new EdgeList();
101     final NodeList hiddenNodes = new NodeList();
102 
103     final Graph2D graph = view.getGraph2D();
104 
105     // unhide the whole graph to perform the calculation
106     graphHider.unhideAll();
107 
108     // do a Bfs run
109     // prepare a NodeMap
110     final int[] data = new int[graph.N()];
111     NodeMap layerMap = Maps.createIndexNodeMap(data);
112     // calculate the first 4 layers
113     NodeList[] layerLists = Bfs.getLayers(graph, new NodeList(newCenterNode), false, layerMap, 4);
114 
115     // get the new nodes to display
116     for (int i = 0; i < Math.min(layerLists.length, 3); i++){
117       for (NodeCursor nc = layerLists[i].nodes(); nc.ok(); nc.next()){
118         final Node node = nc.node();
119         final boolean wasHidden = hiddenNodesMap.getBool(node);
120         if (wasHidden) {
121           fadeInNodes.add(node);
122         }
123       }
124     }
125 
126     // update the visibility marker and add the nodes to hide
127     for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()){
128       final Node node = nc.node();
129       final boolean wasHidden = hiddenNodesMap.getBool(node);
130       final int layer = layerMap.getInt(node);
131       if (layer >= 0 && layer < 3) {
132         // should be visible
133         if (wasHidden) {
134           hiddenNodesMap.setBool(node, false);
135         }
136       } else {
137         hiddenNodes.add(node);
138         // should be invisible
139         if (!wasHidden) {
140           fadeOutNodes.add(node);
141           hiddenNodesMap.setBool(node, true);
142         }
143       }
144     }
145 
146     // update the visibility of the edges and sort out which ones to hide and which ones to insert
147     for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
148       final Edge edge = ec.edge();
149       final boolean wasHidden = hiddenEdgesMap.getBool(edge);
150       final boolean shouldBeHidden = hiddenNodesMap.getBool(edge.source()) || hiddenNodesMap.getBool(edge.target());
151       if (shouldBeHidden) {
152         hiddenEdges.add(edge);
153         if (!wasHidden) {
154           fadeOutEdges.add(edge);
155           hiddenEdgesMap.setBool(edge, true);
156         }
157       } else {
158         if (wasHidden) {
159           fadeInEdges.add(edge);
160           hiddenEdgesMap.setBool(edge, false);
161         }
162       }
163     }
164 
165 
166 
167     //calculate the camera movement.
168     double x;
169     double y;
170 
171     //If we have valid informations from the layouter, use them.
172     YPoint location = layouter.getCenter( newCenterNode );
173     if ( location != null ) {
174       x = location.getX();
175       y = location.getY();
176     } else { //Fallback: Use the informations from the realizers (e.g. at the beginning)
177       NodeRealizer realizer = view.getGraph2D().getRealizer( newCenterNode );
178       x = realizer.getX();
179       y = realizer.getY();
180     }
181 
182     if (animated ) {
183       // now perform the animations
184       // ... for camera
185       animateCamera(x, y);
186 
187       // for the elements that go away....
188       fadeOutEdges(fadeOutEdges);
189       fadeOutNodes(fadeOutNodes);
190 
191       // and for the new elements
192       fadeInEdges(fadeInEdges);
193       fadeInNodes(graph, layerMap, fadeInNodes);
194     } else {
195       view.setCenter(x, y);
196     }
197 
198     //Hide the edges that have been marked
199     graphHider.hide( hiddenEdges );
200 
201     //Now hide the nodes (and edges) that shall be hidden
202     graphHider.hide( hiddenNodes );
203 
204     // make sure the layout algorithm starts its work
205     ( ( NavigationDemo )this ).layouter.syncStructure();
206     ( ( NavigationDemo )this ).layouter.wakeUp();
207   }
208 
209   private void animateCamera(double x, double y) {
210     //An AnimationObject representing the movement of the camera is created
211     AnimationObject animationObject = factory.moveCamera( DefaultMutableValue2D.create( x, y ), PREFERRED_DURATION );
212     //The movement is eased (in and out)
213     AnimationObject easedAnimation = AnimationFactory.createEasedAnimation( animationObject, 0.15, 0.25 );
214     animationPlayer.animate( easedAnimation );
215   }
216 
217   private void fadeOutNodes(NodeList fadeOutNodes) {
218     for (NodeCursor nc = fadeOutNodes.nodes(); nc.ok(); nc.next()) {
219       final Node node = nc.node();
220       //Fade out visible node
221       NodeRealizer realizer = view.getGraph2D().getRealizer( node );
222       final Drawable nodeDrawable = ViewAnimationFactory.createDrawable( realizer );
223       animationPlayer.animate( factory.fadeOut( nodeDrawable, PREFERRED_DURATION ) ); //<--------                                              FADE OUT
224     }
225   }
226 
227   private void fadeInNodes(Graph2D graph, NodeMap layerMap, NodeList fadeInNodes) {
228     for (NodeCursor nc = fadeInNodes.nodes(); nc.ok(); nc.next()) {
229       final Node node = nc.node();
230 
231       // calculate the new position for the node
232       final int myLayer = layerMap.getInt(node);
233       double posX = 0;
234       double posY = 0;
235 
236       // determine the barycenter of all parent neighbour nodes
237       int count = 0;
238       for (NodeCursor nc2 = node.neighbors(); nc2.ok(); nc2.next()) {
239         final Node neighbour = nc2.node();
240         if (layerMap.getInt(neighbour) < myLayer){
241           count++;
242           posX += graph.getCenterX(neighbour);
243           posY += graph.getCenterY(neighbour);
244         }
245       }
246 
247       // get the realizer
248       NodeRealizer nodeRealizer = view.getGraph2D().getRealizer( node );
249 
250       // update its position
251       if (count > 0) {
252         posX /= count;
253         posY /= count;
254         //copy the coords of the parent...
255         //.. to the realizer...
256         nodeRealizer.setCenter( posX, posY);
257         //... and to the data structure
258         layouter.setCenter( node, posX, posY );
259       }
260 
261       //Fade the node in
262       animationPlayer.animate( factory.fadeIn( nodeRealizer, PREFERRED_DURATION ) ); //<-------- FADE IN
263     }
264   }
265 
266   private void fadeOutEdges(EdgeList fadeOutEdges) {
267     for (EdgeCursor ec = fadeOutEdges.edges(); ec.ok(); ec.next()){
268       final Edge edge = ec.edge();
269       EdgeRealizer realizer = view.getGraph2D().getRealizer( edge );
270       final Drawable edgeDrawable = ViewAnimationFactory.createDrawable( realizer );
271       animationPlayer.animate( factory.fadeOut( edgeDrawable, PREFERRED_DURATION ) );//<--------                                               FADE OUT
272     }
273   }
274 
275   private void fadeInEdges(EdgeList fadeInEdges) {
276     for (EdgeCursor ec = fadeInEdges.edges(); ec.ok(); ec.next()){
277       final Edge edge = ec.edge();
278       EdgeRealizer edgeRealizer = view.getGraph2D().getRealizer( edge );
279       animationPlayer.animate( factory.fadeIn( edgeRealizer, PREFERRED_DURATION ) );  //<-------- FADE IN
280     }
281   }
282 }
283