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