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.viewmode;
15  
16  import demo.view.DemoBase;
17  
18  import y.anim.AnimationFactory;
19  import y.anim.AnimationObject;
20  import y.anim.AnimationPlayer;
21  import y.base.Node;
22  import y.base.NodeMap;
23  import y.view.EditMode;
24  import y.view.Graph2DViewRepaintManager;
25  import y.view.HitInfo;
26  import y.view.NodeRealizer;
27  import y.view.ViewAnimationFactory;
28  import y.view.ViewMode;
29  import y.view.AutoDragViewMode;
30  import y.view.DefaultGraph2DRenderer;
31  import y.util.DefaultMutableValue2D;
32  import y.util.Value2D;
33  
34  import java.awt.Dimension;
35  
36  
37  /**
38   * Demonstrates how to create a custom <code>ViewMode</code> that uses yFiles'
39   * Animation Framework to produce a roll over effect for nodes under the mouse
40   * cursor.
41   *
42   */
43  public class RollOverEffectDemo extends DemoBase {
44  
45    public RollOverEffectDemo() {
46      final DefaultGraph2DRenderer g2dr = new DefaultGraph2DRenderer();
47      g2dr.setDrawEdgesFirst(true);
48      view.setGraph2DRenderer(g2dr);
49      view.setPreferredSize(new Dimension(800, 600));
50      loadInitialGraph();
51    }
52  
53    /**
54     * Overwritten to register a roll over effect producing view mode.
55     */
56    protected void registerViewModes() {
57      final EditMode editMode = createEditMode();
58      if (editMode != null) {
59        view.addViewMode(editMode);
60      }
61      view.addViewMode(new AutoDragViewMode());
62      view.addViewMode(new RollOverViewMode());
63    }
64  
65    /**
66     * Loads a sample graph.
67     */
68    protected void loadInitialGraph() {
69      loadGraph("resource/sample.gml");
70    }
71  
72  
73    public static void main( String[] args ) {
74      initLnF();
75      (new RollOverEffectDemo()).start();
76    }
77  
78    /**
79     * A <code>ViewMode</code> that produces a roll over effect for nodes
80     * under the mouse cursor.
81     */
82    private static final class RollOverViewMode extends ViewMode {
83      /** Animation state constant */
84      private static final int NONE = 0;
85      /** Animation state constant */
86      private static final int MARKED = 1;
87      /** Animation state constant */
88      private static final int UNMARK = 2;
89  
90  
91      /** Preferred duration for roll over effect animations */
92      private static final int PREFERRED_DURATION = 350;
93  
94      /** Scale factor for the roll over effect animations */
95      private static final Value2D SCALE_FACTOR =
96              DefaultMutableValue2D.create(3, 3);
97  
98  
99      /** Stores the last node that was marked with the roll over effect */
100     private Node lastHitNode;
101     /** Stores the original size of nodes */
102     private NodeMap size;
103     /** Stores the animation state of nodes */
104     private NodeMap state;
105 
106     private ViewAnimationFactory factory;
107     private AnimationPlayer player;
108 
109     /**
110      * Triggers a rollover effect for the first node at the specified location.
111      */
112     public void mouseMoved( final double x, final double y ) {
113       final HitInfo hi = getHitInfo(x, y);
114       if (hi.hasHitNodes()) {
115         final Node node = (Node) hi.hitNodes().current();
116         if (node != lastHitNode) {
117           unmark(lastHitNode);
118         }
119         if (state.getInt(node) == NONE) {
120           mark(node);
121           lastHitNode = node;
122         }
123       } else {
124         unmark(lastHitNode);
125         lastHitNode = null;
126       }
127     }
128 
129     /**
130      * Overwritten to initialize/dispose this <code>ViewMode</code>'s
131      * helper data.
132      */
133     public void activate( final boolean b ) {
134       if (b) {
135         factory = new ViewAnimationFactory(new Graph2DViewRepaintManager(view));
136         player = factory.createConfiguredPlayer();
137         size = view.getGraph2D().createNodeMap();
138         state = view.getGraph2D().createNodeMap();
139       } else {
140         view.getGraph2D().disposeNodeMap(state);
141         view.getGraph2D().disposeNodeMap(size);
142         state = null;
143         size = null;
144         player = null;
145         factory = null;
146       }
147       super.activate(b);
148     }
149 
150     /**
151      * Overwritten to take only nodes into account for hit testing.
152      */
153     protected HitInfo getHitInfo( final double x, final double y ) {
154       final HitInfo hi = new HitInfo(view, x, y, true, HitInfo.NODE);
155       setLastHitInfo(hi);
156       return hi;
157     }
158 
159     /**
160      * Triggers a <em>mark</em> animation for the specified node.
161      * Sets the animation state of the given node to <em>MARKED</em>.
162      */
163     protected void mark( final Node node ) {
164       // only start a mark animation if no other animation is playing
165       // for the given node
166       if (state.getInt(node) == NONE) {
167         state.setInt(node, MARKED);
168 
169         final NodeRealizer nr = getGraph2D().getRealizer(node);
170         size.set(node, DefaultMutableValue2D.create(nr.getWidth(), nr.getHeight()));
171         final AnimationObject ao = factory.scale(
172                 nr,
173                 SCALE_FACTOR,
174                 ViewAnimationFactory.APPLY_EFFECT,
175                 PREFERRED_DURATION);
176         player.animate(AnimationFactory.createEasedAnimation(ao));
177       }
178     }
179 
180     /**
181      * Triggers an <em>unmark</em> animation for the specified node.
182      * Sets the animation state of the given node to <em>UNMARKED</em>.
183      */
184     protected void unmark( final Node node ) {
185       if (node == null) {
186         return;
187       }
188 
189       // only start an unmark animation if the node is currently marked
190       // (or in the process of being marked)
191       if (state.getInt(node) == MARKED) {
192         state.setInt(node, UNMARK);
193 
194         final Value2D oldSize = (Value2D) size.get(node);
195         final NodeRealizer nr = getGraph2D().getRealizer(node);
196         final AnimationObject ao = factory.resize(
197                 nr,
198                 oldSize,
199                 ViewAnimationFactory.APPLY_EFFECT,
200                 PREFERRED_DURATION);
201         final AnimationObject eao = AnimationFactory.createEasedAnimation(ao);
202         player.animate(new Reset(eao, node, nr, oldSize));
203       }
204     }
205 
206     /**
207      * Custom animation object that resets node size and state upon disposal.
208      */
209     private final class Reset implements AnimationObject {
210       private AnimationObject ao;
211       private final Node node;
212       private final NodeRealizer nr;
213       private final Value2D oldSize;
214 
215       Reset(
216               final AnimationObject ao,
217               final Node node,
218               final NodeRealizer nr,
219               final Value2D size
220       ) {
221         this.ao = ao;
222         this.node = node;
223         this.nr = nr;
224         this.oldSize = size;
225       }
226 
227       public void initAnimation() {
228         ao.initAnimation();
229       }
230 
231       public void calcFrame( final double time ) {
232         ao.calcFrame(time);
233       }
234 
235       /**
236        * Resets the target node to its original size and its animation state
237        * to <em>NONE</em>.
238        */
239       public void disposeAnimation() {
240         ao.disposeAnimation();
241         nr.setSize(oldSize.getX(), oldSize.getY());
242         size.set(node, null);
243         state.setInt(node, NONE);
244       }
245 
246       public long preferredDuration() {
247         return ao.preferredDuration();
248       }
249     }
250   }
251 }
252