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.module;
29  
30  import y.module.LayoutModule;
31  import y.module.YModule;
32  
33  import y.base.DataProvider;
34  import y.layout.BendConverter;
35  import y.layout.CompositeLayoutStage;
36  import y.layout.LayoutStage;
37  import y.layout.SequentialLayouter;
38  import y.layout.grouping.GroupNodeHider;
39  import y.layout.organic.RemoveOverlapsLayoutStage;
40  import y.layout.router.OrganicEdgeRouter;
41  import y.option.OptionGroup;
42  import y.option.OptionHandler;
43  import y.view.Graph2D;
44  import y.view.Selections;
45  
46  /**
47   * This module represents an interactive configurator and launcher for
48   * {@link y.layout.router.OrganicEdgeRouter}.
49   */
50  public class OrganicEdgeRouterModule extends LayoutModule {
51    //// Module 'Organic Edge Router'
52    protected static final String MODULE_ORGANIC_EDGE_ROUTER = "ORGANIC_EDGE_ROUTER";
53    
54    //// Section 'default' items
55    protected static final String TITLE_LAYOUT_OPTIONS = "LAYOUT_OPTIONS";
56    protected static final String ITEM_SELECTION_ONLY = "SELECTION_ONLY";
57    protected static final String ITEM_MINIMAL_NODE_DISTANCE = "MINIMAL_NODE_DISTANCE";
58    protected static final String ITEM_USE_BENDS = "USE_BENDS";
59    protected static final String ITEM_ROUTE_ONLY_NECESSARY = "ROUTE_ONLY_NECESSARY";
60    protected static final String ITEM_ALLOW_MOVING_NODES = "ALLOW_MOVING_NODES";
61  
62    /**
63     * Creates an instance of this module.
64     */
65    public OrganicEdgeRouterModule() {
66      super(MODULE_ORGANIC_EDGE_ROUTER);
67    }
68    
69    /**
70     * Creates an OptionHandler and adds the option items used by this module.
71     * @return the created <code>OptionHandler</code> providing module related options
72     */
73    protected OptionHandler createOptionHandler() {
74      final OptionHandler options = new OptionHandler(getModuleName());
75      // Defaults provider
76      final OrganicEdgeRouter defaults = new OrganicEdgeRouter();
77      
78      // Group 'Layout'
79      final OptionGroup layoutGroup = new OptionGroup();
80      layoutGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_LAYOUT_OPTIONS);
81      // Populate group
82      layoutGroup.addItem(options.addBool(ITEM_SELECTION_ONLY, false));
83      layoutGroup.addItem(options.addInt(ITEM_MINIMAL_NODE_DISTANCE, (int) defaults.getMinimalDistance(), 10, 300));
84      layoutGroup.addItem(options.addBool(ITEM_USE_BENDS, defaults.isUsingBends()));
85      layoutGroup.addItem(options.addBool(ITEM_ROUTE_ONLY_NECESSARY, !defaults.isRoutingAll()));
86      layoutGroup.addItem(options.addBool(ITEM_ALLOW_MOVING_NODES, false));
87      
88      return options;
89    }
90    
91    /**
92     * Main module execution routine.
93     * Launches the module's underlying algorithm on the module's graph based on user options.
94     */
95    protected void mainrun() {
96      final OrganicEdgeRouter organic = new OrganicEdgeRouter();
97      
98      final OptionHandler options = getOptionHandler();
99      configure(organic, options);
100     
101     final SequentialLayouter sequential = new SequentialLayouter();
102     if (options.getBool(ITEM_ALLOW_MOVING_NODES)) {
103       //if we are allowed to move nodes, we can improve the routing results by temporarily enlarging nodes and removing overlaps
104       //(this strategy ensures that there is enough space for the edges)
105       final CompositeLayoutStage cls = new CompositeLayoutStage();
106       cls.appendStage(organic.createNodeEnlargementStage());
107       cls.appendStage(new RemoveOverlapsLayoutStage(0));
108       sequential.appendLayouter(cls);
109 
110     }
111     if (organic.isUsingBends()) {
112       //we want to keep the original bends
113       final BendConverter bendConverter = new BendConverter();
114       bendConverter.setSelectedEdgesDpKey(OrganicEdgeRouter.ROUTE_EDGE_DPKEY);
115       bendConverter.setAdoptSelectionEnabled(options.getBool(ITEM_SELECTION_ONLY));
116       bendConverter.setCoreLayouter(organic);
117       sequential.appendLayouter(bendConverter);
118     } else {
119       sequential.appendLayouter(organic);
120     }
121 
122     final Graph2D graph = getGraph2D();
123     prepareGraph(graph, options);
124     try {
125       launchLayouter(new GroupNodeHider(sequential));
126     } finally {
127       restoreGraph(graph, options);
128     }
129   }
130 
131   /**
132    * Prepares a <code>graph</code> depending on the given options for the
133    * module's layout algorithm.
134    * <br>
135    * Additional resources created by this method have to be freed up by calling
136    * {@link #restoreGraph(y.view.Graph2D, y.option.OptionHandler)} after
137    * layout calculation.  
138    * @param graph the graph to be prepared
139    * @param options the options for the module's layout algorithm
140    */
141   protected void prepareGraph(final Graph2D graph, final OptionHandler options) {
142     // register grouping relevant DataProviders
143     if (options.getBool(ITEM_SELECTION_ONLY)) {
144       // backup existing data providers to prevent loss of user settings
145       final DataProvider selectedEdgesDP = Selections.createSelectionEdgeMap(graph);
146       backupDataProvider(graph, BendConverter.SCOPE_DPKEY);
147       graph.addDataProvider(BendConverter.SCOPE_DPKEY, selectedEdgesDP);
148       backupDataProvider(graph, OrganicEdgeRouter.ROUTE_EDGE_DPKEY);
149       graph.addDataProvider(OrganicEdgeRouter.ROUTE_EDGE_DPKEY, selectedEdgesDP);
150     }
151   }
152 
153   /**
154    * Restores the given <code>graph</code> by freeing up resources created by
155    * {@link #prepareGraph(y.view.Graph2D, y.option.OptionHandler)}.
156    * @param graph the graph for which <code>prepareGraph</code> has been called
157    * @param options the options for the module's layout algorithm
158    */
159   protected void restoreGraph(final Graph2D graph, final OptionHandler options) {
160     // unregister grouping relevant DataProviders
161     if (options.getBool(ITEM_SELECTION_ONLY)) {
162       // remove the data providers set by this module by restoring the initial state
163       restoreDataProvider(graph, OrganicEdgeRouter.ROUTE_EDGE_DPKEY);
164       restoreDataProvider(graph, BendConverter.SCOPE_DPKEY);
165     }
166   }
167 
168   /**
169    * Configures the module's layout algorithm according to the given options.
170    * @param organic the <code>OrganicEdgeRouter</code> to be configured
171    * @param options the layout options to set
172    */
173   protected void configure(final OrganicEdgeRouter organic, final OptionHandler options) {
174     organic.setMinimalDistance(options.getInt(ITEM_MINIMAL_NODE_DISTANCE));
175     organic.setUsingBends(options.getBool(ITEM_USE_BENDS));
176     organic.setRoutingAll(!options.getBool(ITEM_ROUTE_ONLY_NECESSARY));
177   }
178 }
179