1   /****************************************************************************
2    * This demo file is part of yFiles for Java 2.14.
3    * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4    * 72070 Tuebingen, Germany. All rights reserved.
5    * 
6    * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7    * of demo files in source code or binary form, with or without
8    * modification, is not permitted.
9    * 
10   * Owners of a valid software license for a yFiles for Java version that this
11   * demo is shipped with are allowed to use the demo source code as basis
12   * for their own yFiles for Java powered applications. Use of such programs is
13   * governed by the rights and conditions as set out in the yFiles for Java
14   * license agreement.
15   * 
16   * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19   * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21   * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   *
27   ***************************************************************************/
28  package demo.view.viewmode;
29  
30  import demo.view.DemoBase;
31  import demo.view.DemoDefaults;
32  
33  import y.anim.AnimationFactory;
34  import y.anim.AnimationObject;
35  import y.anim.AnimationPlayer;
36  import y.base.Node;
37  import y.base.NodeMap;
38  import y.view.EditMode;
39  import y.view.Graph2DViewActions;
40  import y.view.Graph2DViewRepaintManager;
41  import y.view.HitInfo;
42  import y.view.NodeRealizer;
43  import y.view.ShapeNodeRealizer;
44  import y.view.ViewAnimationFactory;
45  import y.view.ViewMode;
46  import y.view.AutoDragViewMode;
47  import y.view.DefaultGraph2DRenderer;
48  import y.util.DefaultMutableValue2D;
49  import y.util.Value2D;
50  import y.view.YRenderingHints;
51  
52  import javax.swing.ActionMap;
53  import javax.swing.InputMap;
54  import javax.swing.JComponent;
55  import java.awt.Dimension;
56  import java.awt.EventQueue;
57  import java.util.Locale;
58  
59  
60  /**
61   * Demonstrates how to create a custom <code>ViewMode</code> that uses yFiles'
62   * Animation Framework to produce a roll over effect for nodes under the mouse
63   * cursor.
64   *
65   */
66  public class RollOverEffectDemo extends DemoBase {
67  
68    public RollOverEffectDemo() {
69      final DefaultGraph2DRenderer g2dr = new DefaultGraph2DRenderer();
70      g2dr.setDrawEdgesFirst(true);
71      view.setGraph2DRenderer(g2dr);
72      view.setPreferredSize(new Dimension(800, 600));
73      view.getRenderingHints().put(ShapeNodeRealizer.KEY_SLOPPY_RECT_PAINTING,
74          ShapeNodeRealizer.VALUE_SLOPPY_RECT_PAINTING_OFF);
75      view.getRenderingHints().put(YRenderingHints.KEY_SLOPPY_POLYLINE_PAINTING,
76          YRenderingHints.VALUE_SLOPPY_POLYLINE_PAINTING_OFF);
77      loadInitialGraph();
78    }
79  
80    protected void configureDefaultRealizers() {
81      // painting shadows is expensive and therefore not well suited for animations
82      DemoDefaults.registerDefaultNodeConfiguration(false);
83      DemoDefaults.configureDefaultRealizers(view);
84    }
85  
86    /**
87     * Overwritten to register a roll over effect producing view mode.
88     */
89    protected void registerViewModes() {
90      final EditMode editMode = createEditMode();
91      if (editMode != null) {
92        view.addViewMode(editMode);
93      }
94      view.addViewMode(new AutoDragViewMode());
95      view.addViewMode(new RollOverViewMode());
96  
97      // disable label editing shortcut
98      final Graph2DViewActions actions = new Graph2DViewActions(view);
99      ActionMap amap = view.getCanvasComponent().getActionMap();
100     amap.remove(Graph2DViewActions.EDIT_LABEL);
101     InputMap imap = actions.createDefaultInputMap(amap);
102     view.getCanvasComponent().setInputMap(JComponent.WHEN_FOCUSED, imap);
103   }
104 
105   /**
106    * Loads a sample graph.
107    */
108   protected void loadInitialGraph() {
109     loadGraph("resource/rollover.graphml");
110   }
111 
112 
113   public static void main(String[] args) {
114     EventQueue.invokeLater(new Runnable() {
115       public void run() {
116         Locale.setDefault(Locale.ENGLISH);
117         initLnF();
118         (new RollOverEffectDemo()).start();
119       }
120     });
121   }
122 
123   /**
124    * A <code>ViewMode</code> that produces a roll over effect for nodes
125    * under the mouse cursor.
126    */
127   private static final class RollOverViewMode extends ViewMode {
128     /** Animation state constant */
129     private static final int NONE = 0;
130     /** Animation state constant */
131     private static final int MARKED = 1;
132     /** Animation state constant */
133     private static final int UNMARK = 2;
134 
135 
136     /** Preferred duration for roll over effect animations */
137     private static final int PREFERRED_DURATION = 350;
138 
139     /** Scale factor for the roll over effect animations */
140     private static final Value2D SCALE_FACTOR =
141             DefaultMutableValue2D.create(3, 3);
142 
143 
144     /** Stores the last node that was marked with the roll over effect */
145     private Node lastHitNode;
146     /** Stores the original size of nodes */
147     private NodeMap size;
148     /** Stores the animation state of nodes */
149     private NodeMap state;
150 
151     private ViewAnimationFactory factory;
152     private AnimationPlayer player;
153 
154     /**
155      * Triggers a rollover effect for the first node at the specified location.
156      */
157     public void mouseMoved( final double x, final double y ) {
158       final HitInfo hi = getHitInfo(x, y);
159       if (hi.hasHitNodes()) {
160         final Node node = (Node) hi.hitNodes().current();
161         if (node != lastHitNode) {
162           unmark(lastHitNode);
163         }
164         if (state.getInt(node) == NONE) {
165           mark(node);
166           lastHitNode = node;
167         }
168       } else {
169         unmark(lastHitNode);
170         lastHitNode = null;
171       }
172     }
173 
174     /**
175      * Overwritten to initialize/dispose this <code>ViewMode</code>'s
176      * helper data.
177      */
178     public void activate( final boolean b ) {
179       if (b) {
180         factory = new ViewAnimationFactory(new Graph2DViewRepaintManager(view));
181         player = factory.createConfiguredPlayer();
182         size = view.getGraph2D().createNodeMap();
183         state = view.getGraph2D().createNodeMap();
184       } else {
185         view.getGraph2D().disposeNodeMap(state);
186         view.getGraph2D().disposeNodeMap(size);
187         state = null;
188         size = null;
189         player = null;
190         factory = null;
191       }
192       super.activate(b);
193     }
194 
195     /**
196      * Overwritten to take only nodes into account for hit testing.
197      */
198     protected HitInfo getHitInfo( final double x, final double y ) {
199       final HitInfo hi = DemoBase.checkNodeHit(view, x, y);
200       setLastHitInfo(hi);
201       return hi;
202     }
203 
204     /**
205      * Triggers a <em>mark</em> animation for the specified node.
206      * Sets the animation state of the given node to <em>MARKED</em>.
207      */
208     protected void mark( final Node node ) {
209       // only start a mark animation if no other animation is playing
210       // for the given node
211       if (state.getInt(node) == NONE) {
212         state.setInt(node, MARKED);
213 
214         final NodeRealizer nr = getGraph2D().getRealizer(node);
215         size.set(node, DefaultMutableValue2D.create(nr.getWidth(), nr.getHeight()));
216         final AnimationObject ao = factory.scale(
217                 nr,
218                 SCALE_FACTOR,
219                 ViewAnimationFactory.APPLY_EFFECT,
220                 PREFERRED_DURATION);
221         player.animate(AnimationFactory.createEasedAnimation(ao));
222       }
223     }
224 
225     /**
226      * Triggers an <em>unmark</em> animation for the specified node.
227      * Sets the animation state of the given node to <em>UNMARKED</em>.
228      */
229     protected void unmark( final Node node ) {
230       if (node == null) {
231         return;
232       }
233 
234       // only start an unmark animation if the node is currently marked
235       // (or in the process of being marked)
236       if (state.getInt(node) == MARKED) {
237         state.setInt(node, UNMARK);
238 
239         final Value2D oldSize = (Value2D) size.get(node);
240         final NodeRealizer nr = getGraph2D().getRealizer(node);
241         final AnimationObject ao = factory.resize(
242                 nr,
243                 oldSize,
244                 ViewAnimationFactory.APPLY_EFFECT,
245                 PREFERRED_DURATION);
246         final AnimationObject eao = AnimationFactory.createEasedAnimation(ao);
247         player.animate(new Reset(eao, node, nr, oldSize));
248       }
249     }
250 
251     /**
252      * Custom animation object that resets node size and state upon disposal.
253      */
254     private final class Reset implements AnimationObject {
255       private AnimationObject ao;
256       private final Node node;
257       private final NodeRealizer nr;
258       private final Value2D oldSize;
259 
260       Reset(
261               final AnimationObject ao,
262               final Node node,
263               final NodeRealizer nr,
264               final Value2D size
265       ) {
266         this.ao = ao;
267         this.node = node;
268         this.nr = nr;
269         this.oldSize = size;
270       }
271 
272       public void initAnimation() {
273         ao.initAnimation();
274       }
275 
276       public void calcFrame( final double time ) {
277         ao.calcFrame(time);
278       }
279 
280       /**
281        * Resets the target node to its original size and its animation state
282        * to <em>NONE</em>.
283        */
284       public void disposeAnimation() {
285         ao.disposeAnimation();
286         nr.setSize(oldSize.getX(), oldSize.getY());
287         size.set(node, null);
288         state.setInt(node, NONE);
289       }
290 
291       public long preferredDuration() {
292         return ao.preferredDuration();
293       }
294     }
295   }
296 }
297