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.layout.radial;
29  
30  import demo.view.DemoBase;
31  import y.base.Edge;
32  import y.base.EdgeCursor;
33  import y.base.NodeMap;
34  import y.layout.radial.RadialLayouter;
35  import demo.layout.module.RadialLayoutModule;
36  import y.option.OptionHandler;
37  import y.util.DataProviderAdapter;
38  import y.view.Arrow;
39  import y.view.Bend;
40  import y.view.BendCursor;
41  import y.view.BezierEdgeRealizer;
42  import y.view.EdgeRealizer;
43  import y.view.Graph2D;
44  import y.view.PolyLineEdgeRealizer;
45  import y.view.QuadCurveEdgeRealizer;
46  import y.view.SplineEdgeRealizer;
47  
48  import javax.swing.AbstractAction;
49  import javax.swing.AbstractButton;
50  import javax.swing.JComboBox;
51  import javax.swing.JToggleButton;
52  import javax.swing.JToolBar;
53  import java.awt.EventQueue;
54  import java.awt.event.ActionEvent;
55  import java.awt.event.ActionListener;
56  import java.net.URL;
57  import java.util.Locale;
58  
59  /**
60   * Showcases the {@link RadialLayouter} and the effect of its configuration settings.
61   * <p>
62   * The demo offers to calculate a radial layout for a default or customized graph using different settings
63   * of its layout module.
64   * </p>
65   * <p>
66   * The circles the nodes are placed on as well as their sectors are visualized on demand. A combo box offers different
67   * {@link EdgeRealizer edge realizers} suitable for a radial layout.
68   * </p>
69   *
70   */
71  public class RadialLayouterDemo extends DemoBase {
72  
73    /* The layout module to configure the RadialLayouter.*/
74    private RadialLayoutModule module;
75  
76    /* A node map the RadialLayouter fills with RadialLayouter.NodeInfo objects which are used by the SectorDrawable.*/
77    private NodeMap nodeInfoMap;
78  
79    /* The SectorDrawable to visualize the circles and/or sectors the nodes are placed on/in.*/
80    private SectorDrawable sectorDrawable;
81  
82    /* The edge realizers offered by the edge realizer combo box.*/
83    private EdgeRealizer[] edgeRealizers;
84  
85    public RadialLayouterDemo() {
86      // load and layout sample graph
87      loadGraph("resource/radial.graphml");
88      doLayout();
89    }
90  
91    protected void initialize() {
92      super.initialize();
93  
94      initializeEdgeRealizer();
95  
96      // add node map to hold NodeInfo data crated by the RadialLayouter
97      nodeInfoMap = view.getGraph2D().createNodeMap();
98      view.getGraph2D().addDataProvider(RadialLayouter.NODE_INFO_DPKEY, nodeInfoMap);
99      // add drawable for the circles and sectors and only draw circles initially
100     sectorDrawable = new SectorDrawable(view, nodeInfoMap);
101     view.addBackgroundDrawable(sectorDrawable);
102   }
103 
104   private void initializeEdgeRealizer() {
105     //use a delta arrow to make edge directions clear
106     SplineEdgeRealizer splineEdgeRealizer = new SplineEdgeRealizer();
107     splineEdgeRealizer.setArrow(Arrow.DELTA);
108 
109     QuadCurveEdgeRealizer quadCurveEdgeRealizer = new QuadCurveEdgeRealizer();
110     quadCurveEdgeRealizer.setArrow(Arrow.DELTA);
111 
112     BezierEdgeRealizer bezierEdgeRealizer = new BezierEdgeRealizer();
113     bezierEdgeRealizer.setArrow(Arrow.DELTA);
114 
115     PolyLineEdgeRealizer polyLineEdgeRealizer = new PolyLineEdgeRealizer();
116     polyLineEdgeRealizer.setArrow(Arrow.DELTA);
117 
118     view.getGraph2D().setDefaultEdgeRealizer(polyLineEdgeRealizer);
119     edgeRealizers = new EdgeRealizer[]{splineEdgeRealizer, quadCurveEdgeRealizer, bezierEdgeRealizer, polyLineEdgeRealizer};
120   }
121 
122   /**
123    * Creates a toolbar for configuring and running the radial layouter.
124    */
125   protected JToolBar createToolBar() {
126     module = new RadialLayoutModule();
127 
128     final JToolBar toolBar = super.createToolBar();
129     toolBar.addSeparator();
130     toolBar.add(createActionControl(new AbstractAction(
131             "Layout", SHARED_LAYOUT_ICON) {
132       public void actionPerformed(final ActionEvent e) {
133         doLayout();
134       }
135     }));
136 
137     toolBar.addSeparator(TOOLBAR_SMALL_SEPARATOR);
138     toolBar.add(createActionControl(new AbstractAction(
139             "Settings...", getIconResource("resource/properties.png")) {
140       public void actionPerformed(final ActionEvent e) {
141         final OptionHandler settings = module.getOptionHandler();
142         if (settings != null) {
143           final ActionListener listener = new ActionListener() {
144             public void actionPerformed(ActionEvent e) {
145               doLayout();
146             }
147           };
148 
149           OptionSupport.showDialog(settings, listener, false, view.getFrame());
150         }
151       }
152     }));
153 
154     toolBar.addSeparator();
155     final JToggleButton showCirclesBtn = new JToggleButton(new AbstractAction("Show Circles") {
156       public void actionPerformed(ActionEvent e) {
157         sectorDrawable.setDrawingCircles(((AbstractButton) e.getSource()).isSelected());
158         view.updateView();
159       }
160     });
161     showCirclesBtn.setSelected(sectorDrawable.isDrawingCircles());
162     toolBar.add(showCirclesBtn);
163     toolBar.addSeparator(TOOLBAR_SMALL_SEPARATOR);
164     final JToggleButton showSectorsBtn = new JToggleButton(new AbstractAction("Show Sectors") {
165       public void actionPerformed(ActionEvent e) {
166         sectorDrawable.setDrawingSectors(((AbstractButton) e.getSource()).isSelected());
167         view.updateView();
168       }
169     });
170     showSectorsBtn.setSelected(sectorDrawable.isDrawingSectors());
171     toolBar.add(showSectorsBtn);
172     toolBar.addSeparator();
173 
174     final JComboBox comboBox = new JComboBox(new Object[]{"Spline Routing", "Quad Curve Routing", "Bezier Routing", "Polygonal Routing"});
175     comboBox.setMaximumSize(comboBox.getPreferredSize());
176     comboBox.setSelectedIndex(3);
177     comboBox.addActionListener(new ActionListener() {
178       public void actionPerformed(ActionEvent e) {
179         EdgeRealizer selectedRealizer = edgeRealizers[comboBox.getSelectedIndex()];
180         if (view.getGraph2D().getDefaultEdgeRealizer() != selectedRealizer) {
181           view.getGraph2D().setDefaultEdgeRealizer(selectedRealizer);
182           updateEdgeRealizers();
183         }
184       }
185     });
186     toolBar.add(comboBox);
187 
188     return toolBar;
189   }
190 
191   private void doLayout() {
192     module.start(view.getGraph2D());
193     sectorDrawable.updateSectors();
194     view.fitContent();
195     view.updateView();
196   }
197 
198   protected void loadGraph(final URL resource) {
199     super.loadGraph(resource);
200 
201     // clear sector drawable as it is outdated
202     sectorDrawable.updateSectors();
203 
204     updateEdgeRealizers();
205   }
206 
207   /**
208    * Update all edges to use (a copy of) the default edge realizer.
209    * <p>
210    * Existing bends of transferred to the new realizers.
211    * </p>
212    */
213   private void updateEdgeRealizers() {
214     final Graph2D graph2D = view.getGraph2D();
215 
216     for (EdgeCursor ec = graph2D.edges(); ec.ok(); ec.next()) {
217       Edge edge = ec.edge();
218       EdgeRealizer oldRealizer = graph2D.getRealizer(edge);
219       EdgeRealizer newRealizer = graph2D.getDefaultEdgeRealizer().createCopy();
220       for (BendCursor bc = oldRealizer.bends(); bc.ok(); bc.next()) {
221         Bend bend = bc.bend();
222         newRealizer.addPoint(bend.getX(), bend.getY());
223       }
224       graph2D.setRealizer(edge, newRealizer);
225     }
226     view.updateView();
227   }
228 
229   public static void main( String[] args ) {
230     EventQueue.invokeLater(new Runnable() {
231       public void run() {
232         Locale.setDefault(Locale.ENGLISH);
233         initLnF();
234         new RadialLayouterDemo().start();
235       }
236     });
237   }
238 }
239