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.realizer;
29  
30  import demo.view.DemoBase;
31  import demo.view.DemoDefaults;
32  
33  import y.base.EdgeCursor;
34  import y.view.Arrow;
35  import y.view.BendList;
36  import y.view.BridgeCalculator;
37  import y.view.DefaultGraph2DRenderer;
38  import y.view.EdgeRealizer;
39  import y.view.GenericEdgePainter;
40  import y.view.GenericEdgeRealizer;
41  import y.view.Graph2D;
42  
43  import javax.swing.AbstractAction;
44  import javax.swing.Action;
45  import javax.swing.JButton;
46  import javax.swing.JToolBar;
47  
48  import java.awt.Graphics2D;
49  import java.awt.EventQueue;
50  import java.awt.event.ActionEvent;
51  import java.awt.geom.GeneralPath;
52  import java.awt.geom.PathIterator;
53  import java.util.Locale;
54  import java.util.Map;
55  
56  /**
57   * This class demonstrates how to utilize {@link y.view.BridgeCalculator} to draw bridges/gaps for crossing edges with
58   * custom {@link EdgeRealizer}s.
59   * It demonstrates how to wrap a {@link y.view.GenericEdgeRealizer.Painter} implementation of a customized
60   * {@link y.view.GenericEdgeRealizer} and use the current {@link BridgeCalculator} instance
61   * from the {@link DefaultGraph2DRenderer}
62   * to incorporate the calculation of bridges into the rendering.
63   *
64   * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/mvc_view#renderer" target="_blank">Section View Implementations</a> in the yFiles for Java Developer's Guide
65   */
66  public class BridgeEdgeRealizerDemo extends DemoBase {
67    BridgeCalculator bridgeCalculator;
68  
69    public BridgeEdgeRealizerDemo() {
70      super();
71    
72      loadGraph("resource/bridgeEdgeRealizer.graphml");
73      DemoDefaults.applyRealizerDefaults(view.getGraph2D(), true, true);
74    }
75  
76    protected void configureDefaultRealizers() {
77      super.configureDefaultRealizers();
78      // get the factory to register our own styles
79      GenericEdgeRealizer.Factory factory = GenericEdgeRealizer.getFactory();
80  
81      // Retrieve a map that holds the default GenericEdgeRealizer configuration.
82      // The implementations contained therein can be replaced one by one in order 
83      // to create custom configurations... 
84      Map implementationsMap = factory.createDefaultConfigurationMap();
85  
86      // notice that the painter instance is wrapped using BridgedEdgePainter
87      // which modifies the GeneralPath instance and provides the necessary BridgeCalculatorHandler
88      // if the BridgeCalculator's mode should be set to two pass rendering (modes other than
89      // CROSSING_MODE_ORDER_INDUCED)
90      final BridgedEdgePainter painter = new BridgedEdgePainter(
91          new GenericEdgePainter(), BridgeCalculator.CROSSING_STYLE_GAP);
92      implementationsMap.put(GenericEdgeRealizer.Painter.class, painter);
93      // used only when the bridgeCalculator is set to two pass rendering - otherwise not needed
94      implementationsMap.put(GenericEdgeRealizer.BridgeCalculatorHandler.class, painter);
95  
96      // finally add the configuration to the factory
97      factory.addConfiguration("bridgetype1", implementationsMap);
98  
99      // and another style
100     final BridgedEdgePainter painter2 = new BridgedEdgePainter(
101         new GenericEdgePainter(), BridgeCalculator.CROSSING_STYLE_ARC);
102     implementationsMap.put(GenericEdgeRealizer.Painter.class, painter2);
103     // used only when the bridgeCalculator is set to two pass rendering - otherwise not needed
104     implementationsMap.put(GenericEdgeRealizer.BridgeCalculatorHandler.class, painter2);
105 
106     // finally add the configuration to the factory
107     factory.addConfiguration("bridgetype2", implementationsMap);
108 
109     // Create a default EdgeRealizer
110     GenericEdgeRealizer ger = new GenericEdgeRealizer();
111 
112     // initialize the default edge realizer to the type we just registered...
113     ger.setConfiguration("bridgetype1");
114     ger.setTargetArrow(Arrow.STANDARD);
115 
116     // set the realizer...
117     final Graph2D graph = view.getGraph2D();
118     graph.setDefaultEdgeRealizer(ger);
119 
120     // set an appropriate graph2drenderer that resets the bridge calculator initially for each painting
121     bridgeCalculator = new BridgeCalculator();
122     // optionally set a different crossing mode
123     // (triggers usage of BridgeCalculatorHandler implementation)
124     // bridgeCalculator.setCrossingMode(BridgeCalculator.CROSSING_MODE_HORIZONTAL_CROSSES_VERTICAL);
125     ((DefaultGraph2DRenderer) view.getGraph2DRenderer()).setBridgeCalculator(bridgeCalculator);
126   }
127 
128   protected JToolBar createToolBar() {
129     final AbstractAction assignGapStyleAction = new AbstractAction("Gap Style") {
130       public void actionPerformed(ActionEvent e) {
131         setBridgeType("bridgetype1");
132       }
133     };
134     assignGapStyleAction.putValue(Action.SHORT_DESCRIPTION, "Draw 'Gap' style bridges on the selected edges");
135     assignGapStyleAction.putValue(Action.SMALL_ICON, getIconResource("resource/bridge_gap.png"));
136 
137     final AbstractAction assignArcStyleAction = new AbstractAction("Arc Style") {
138       public void actionPerformed(ActionEvent e) {
139         setBridgeType("bridgetype2");
140       }
141     };
142     assignArcStyleAction.putValue(Action.SHORT_DESCRIPTION, "Draw 'Arc' style bridges on the selected edges");
143     assignArcStyleAction.putValue(Action.SMALL_ICON, getIconResource("resource/bridge_arc.png"));
144 
145     final JToolBar toolBar = super.createToolBar();
146     toolBar.addSeparator();
147     toolBar.add(new JButton(assignGapStyleAction));
148     toolBar.addSeparator(TOOLBAR_SMALL_SEPARATOR);
149     toolBar.add(new JButton(assignArcStyleAction));
150     return toolBar;
151   }
152 
153   private void setBridgeType(String bridgeType) {
154     final Graph2D graph = view.getGraph2D();
155     // fire event to mark start of bridge type change for undo/redo
156     graph.firePreEvent();
157     // backup realizers to be able to restore the former edge realizer on undo
158     graph.backupRealizers();
159     try {
160       EdgeCursor ec = graph.selectedEdges();
161       if (ec.size() == 0) {
162         ec = graph.edges();
163       }
164       for (; ec.ok(); ec.next()) {
165         if (graph.getRealizer(ec.edge()) instanceof GenericEdgeRealizer) {
166           ((GenericEdgeRealizer) graph.getRealizer(ec.edge())).setConfiguration(bridgeType);
167         }
168       }
169 
170       graph.setDefaultEdgeRealizer(new GenericEdgeRealizer(bridgeType));
171       graph.updateViews();
172     } finally {
173       // fire event to mark end of bridge type change for undo/redo
174       graph.firePostEvent();
175     }
176   }
177 
178   /**
179    * Wrapping GenericEdgeRealizer.Painter implementation that modifies the given
180    * GeneralPath to incorporate bridges. Then delegates the actual painting to
181    * the given instance.
182    */
183   static final class BridgedEdgePainter implements GenericEdgeRealizer.Painter, GenericEdgeRealizer.BridgeCalculatorHandler {
184     private final GenericEdgeRealizer.Painter painter;
185     private final short bridgeStyle;
186 
187     BridgedEdgePainter(GenericEdgeRealizer.Painter painter, short bridgeStyle) {
188       this.painter = painter;
189       this.bridgeStyle = bridgeStyle;
190     }
191 
192     public void paint(EdgeRealizer context, BendList bends, GeneralPath path, Graphics2D gfx, boolean selected) {
193       // modify the GeneralPath
194       BridgeCalculator bridgeCalculator = DefaultGraph2DRenderer.getBridgeCalculator(context, gfx);
195       if (bridgeCalculator != null) {
196         GeneralPath p = new GeneralPath();
197         // remember old style
198         final short crossingStyle = bridgeCalculator.getCrossingStyle();
199         try {
200           bridgeCalculator.setCrossingStyle(bridgeStyle);
201           PathIterator pathIterator = bridgeCalculator.insertBridges(path.getPathIterator(null, 1.0d));
202           p.append(pathIterator, true);
203           // and delegate the painting
204           painter.paint(context, bends, p, gfx, selected);
205         } finally {
206           bridgeCalculator.setCrossingStyle(crossingStyle);
207         }
208       } else {
209         painter.paint(context, bends, path, gfx, selected);
210       }
211     }
212 
213     public void paintSloppy(EdgeRealizer context, BendList bends, GeneralPath path, Graphics2D gfx, boolean selected) {
214       painter.paintSloppy(context, bends, path, gfx, selected);
215     }
216 
217     // necessary for two-pass rendering only - the obstacles produced by this realizer have to be
218     // registered with the BridgeCalculator
219     public void registerObstacles(EdgeRealizer context, BendList bends, GeneralPath path, BridgeCalculator calculator) {
220       calculator.registerObstacles(path.getPathIterator(null));
221     }
222   }
223 
224 
225   public static void main(String[] args) {
226     EventQueue.invokeLater(new Runnable() {
227       public void run() {
228         Locale.setDefault(Locale.ENGLISH);
229         initLnF();
230         new BridgeEdgeRealizerDemo().start("Bridge EdgeRealizer Demo");
231       }
232     });
233   }
234 }
235