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.Edge;
34  import y.layout.Layouter;
35  import y.layout.circular.CircularLayouter;
36  import y.layout.hierarchic.IncrementalHierarchicLayouter;
37  import y.layout.organic.SmartOrganicLayouter;
38  import y.layout.orthogonal.OrthogonalLayouter;
39  import y.layout.partial.PartialLayouter;
40  import y.option.OptionHandler;
41  import y.util.DataProviderAdapter;
42  import y.view.Arrow;
43  import y.view.EdgeRealizer;
44  import y.view.Graph2D;
45  import y.view.Graph2DLayoutExecutor;
46  import y.view.Selections;
47  import y.view.tabular.TableLayoutConfigurator;
48  
49  /**
50   * This module represents an interactive configurator and launcher for
51   * {@link y.layout.partial.PartialLayouter}.
52   *
53   *
54   * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/partial_layout#partial_layout" target="_blank">Section Partial Layout </a> in the yFiles for Java Developer's Guide
55   */
56  public class PartialLayoutModule extends LayoutModule {
57    //// Module 'Partial Layout'
58    protected static final String MODULE_PARTIAL = "PARTIAL";
59    
60    //// Section 'General'
61    protected static final String SECTION_GENERAL = "GENERAL";
62    // Section 'General' items
63    protected static final String ITEM_ROUTING_TO_SUBGRAPH = "ROUTING_TO_SUBGRAPH";
64    protected static final String VALUE_ROUTING_TO_SUBGRAPH_AUTO = "ROUTING_TO_SUBGRAPH_AUTO";
65    protected static final String VALUE_ROUTING_TO_SUBGRAPH_STRAIGHT_LINE = "ROUTING_TO_SUBGRAPH_STRAIGHT_LINE";
66    protected static final String VALUE_ROUTING_TO_SUBGRAPH_POLYLINE = "ROUTING_TO_SUBGRAPH_POLYLINE";
67    protected static final String VALUE_ROUTING_TO_SUBGRAPH_ORTHOGONALLY = "ROUTING_TO_SUBGRAPH_ORTHOGONALLY";
68    protected static final String VALUE_ROUTING_TO_SUBGRAPH_ORGANIC = "ROUTING_TO_SUBGRAPH_ORGANIC";
69    protected static final String ITEM_MODE_COMPONENT_ASSIGNMENT = "MODE_COMPONENT_ASSIGNMENT";
70    protected static final String VALUE_MODE_COMPONENT_CONNECTED = "MODE_COMPONENT_CONNECTED";
71    protected static final String VALUE_MODE_COMPONENT_SINGLE = "MODE_COMPONENT_SINGLE";
72    protected static final String VALUE_MODE_COMPONENT_CLUSTERING = "MODE_COMPONENT_CLUSTERING";
73    protected static final String VALUE_MODE_COMPONENT_CUSTOMIZED = "MODE_COMPONENT_CUSTOMIZED";
74    protected static final String ITEM_SUBGRAPH_LAYOUTER = "SUBGRAPH_LAYOUTER";
75    protected static final String VALUE_SUBGRAPH_LAYOUTER_IHL = "SUBGRAPH_LAYOUTER_IHL";
76    protected static final String VALUE_SUBGRAPH_LAYOUTER_ORGANIC = "SUBGRAPH_LAYOUTER_ORGANIC";
77    protected static final String VALUE_SUBGRAPH_LAYOUTER_CIRCULAR = "SUBGRAPH_LAYOUTER_CIRCULAR";
78    protected static final String VALUE_SUBGRAPH_LAYOUTER_ORTHOGONAL = "SUBGRAPH_LAYOUTER_ORTHOGONAL";
79    protected static final String VALUE_SUBGRAPH_LAYOUTER_NO_LAYOUT = "SUBGRAPH_LAYOUTER_NO_LAYOUT";
80    protected static final String ITEM_SUBGRAPH_POSITION_STRATEGY = "SUBGRAPH_POSITION_STRATEGY";
81    protected static final String VALUE_SUBGRAPH_POSITIONING_STRATEGY_BARYCENTER = "SUBGRAPH_POSITION_STRATEGY_BARYCENTER";
82    protected static final String VALUE_SUBGRAPH_POSITIONING_STRATEGY_FROM_SKETCH = "SUBGRAPH_POSITION_STRATEGY_FROM_SKETCH";
83    protected static final String ITEM_MIN_NODE_DIST = "MIN_NODE_DIST";
84    protected static final String ITEM_ORIENTATION_MAIN_GRAPH = "ORIENTATION_MAIN_GRAPH";
85    protected static final String VALUE_ORIENTATION_MAIN_GRAPH_AUTO_DETECT = "ORIENTATION_MAIN_GRAPH_AUTO_DETECT";
86    protected static final String VALUE_ORIENTATION_MAIN_GRAPH_TOP_TO_DOWN = "ORIENTATION_MAIN_GRAPH_TOP_TO_DOWN";
87    protected static final String VALUE_ORIENTATION_MAIN_GRAPH_DOWN_TO_TOP = "ORIENTATION_MAIN_GRAPH_DOWN_TO_TOP";
88    protected static final String VALUE_ORIENTATION_MAIN_GRAPH_LEFT_TO_RIGHT = "ORIENTATION_MAIN_GRAPH_LEFT_TO_RIGHT";
89    protected static final String VALUE_ORIENTATION_MAIN_GRAPH_RIGHT_TO_LEFT = "ORIENTATION_MAIN_GRAPH_RIGHT_TO_LEFT";
90    protected static final String VALUE_ORIENTATION_MAIN_GRAPH_NONE = "ORIENTATION_MAIN_GRAPH_NONE";
91    protected static final String ITEM_CONSIDER_SNAPLINES = "CONSIDER_SNAPLINES";
92    protected static final String ITEM_CONSIDER_EDGE_DIRECTION = "CONSIDER_EDGE_DIRECTION";
93  
94    /**
95     * Creates an instance of this module.
96     */
97    public PartialLayoutModule() {
98      super(MODULE_PARTIAL);
99    }
100 
101   /**
102    * Creates an OptionHandler and adds the option items used by this module.
103    * @return the created <code>OptionHandler</code> providing module related options
104    */
105   protected OptionHandler createOptionHandler() {
106     final OptionHandler options = new OptionHandler(getModuleName());
107     
108     //// Section 'General'
109     options.useSection(SECTION_GENERAL);
110     // Populate items
111     options.addEnum(ITEM_ROUTING_TO_SUBGRAPH, new String[]{
112         VALUE_ROUTING_TO_SUBGRAPH_AUTO,
113         VALUE_ROUTING_TO_SUBGRAPH_STRAIGHT_LINE,
114         VALUE_ROUTING_TO_SUBGRAPH_POLYLINE,
115         VALUE_ROUTING_TO_SUBGRAPH_ORTHOGONALLY,
116         VALUE_ROUTING_TO_SUBGRAPH_ORGANIC
117     }, 0);
118     options.addEnum(ITEM_MODE_COMPONENT_ASSIGNMENT, new String[]{
119         VALUE_MODE_COMPONENT_CONNECTED,
120         VALUE_MODE_COMPONENT_SINGLE,
121         VALUE_MODE_COMPONENT_CLUSTERING,
122         VALUE_MODE_COMPONENT_CUSTOMIZED
123     }, 0);
124     options.addEnum(ITEM_SUBGRAPH_LAYOUTER, new String[]{
125         VALUE_SUBGRAPH_LAYOUTER_IHL,
126         VALUE_SUBGRAPH_LAYOUTER_ORGANIC,
127         VALUE_SUBGRAPH_LAYOUTER_CIRCULAR,
128         VALUE_SUBGRAPH_LAYOUTER_ORTHOGONAL,
129         VALUE_SUBGRAPH_LAYOUTER_NO_LAYOUT
130     }, 0);
131     options.addEnum(ITEM_SUBGRAPH_POSITION_STRATEGY, new String[]{
132         VALUE_SUBGRAPH_POSITIONING_STRATEGY_BARYCENTER,
133         VALUE_SUBGRAPH_POSITIONING_STRATEGY_FROM_SKETCH
134     }, 0);
135     options.addInt(ITEM_MIN_NODE_DIST, 30, 1, 100);
136     options.addEnum(ITEM_ORIENTATION_MAIN_GRAPH, new String[]{
137         VALUE_ORIENTATION_MAIN_GRAPH_AUTO_DETECT,
138         VALUE_ORIENTATION_MAIN_GRAPH_TOP_TO_DOWN,
139         VALUE_ORIENTATION_MAIN_GRAPH_DOWN_TO_TOP,
140         VALUE_ORIENTATION_MAIN_GRAPH_LEFT_TO_RIGHT,
141         VALUE_ORIENTATION_MAIN_GRAPH_RIGHT_TO_LEFT,
142         VALUE_ORIENTATION_MAIN_GRAPH_NONE
143     }, 0);
144     options.addBool(ITEM_CONSIDER_SNAPLINES, true);
145     options.addBool(ITEM_CONSIDER_EDGE_DIRECTION, false);
146     
147     return options;
148   }
149 
150   /**
151    * Main module execution routine.
152    * Launches the module's underlying algorithm on the module's graph based on user options.
153    */
154   protected void mainrun() {
155     final Graph2D graph = getGraph2D();
156     if (graph.selectedNodes().size() + graph.selectedEdges().size() == 0) {
157       return; //nothing to do
158     }
159 
160     final PartialLayouter partial = new PartialLayouter();
161 
162     final OptionHandler options = getOptionHandler();
163     configure(partial, options);
164 
165     final Graph2DLayoutExecutor layoutExecutor = getLayoutExecutor();
166     final TableLayoutConfigurator tableLayoutConf = layoutExecutor.getTableLayoutConfigurator();
167     final boolean wasConfTableNodeRealizer = layoutExecutor.isConfiguringTableNodeRealizers();
168     final boolean wasHorizontalLayoutConf = tableLayoutConf.isHorizontalLayoutConfiguration();
169     layoutExecutor.setConfiguringTableNodeRealizers(true);
170     tableLayoutConf.setHorizontalLayoutConfiguration(isHorizontalLayoutConf(options));
171     
172     prepareGraph(graph, options);
173     try {
174       launchLayouter(partial);
175     } finally {
176       restoreGraph(graph, options);
177 
178       layoutExecutor.setConfiguringTableNodeRealizers(wasConfTableNodeRealizer);
179       tableLayoutConf.setHorizontalLayoutConfiguration(wasHorizontalLayoutConf);
180     }
181   }
182 
183   /**
184    * Prepares a <code>graph</code> depending on the given options for the
185    * module's layout algorithm.
186    * <br>
187    * Additional resources created by this method have to be freed up by calling
188    * {@link #restoreGraph(y.view.Graph2D, y.option.OptionHandler)} after
189    * layout calculation.  
190    * @param graph the graph to be prepared
191    * @param options the options for the module's layout algorithm
192    */
193   protected void prepareGraph(final Graph2D graph, OptionHandler options) {
194     // backup existing data providers to prevent loss of user settings
195     backupDataProvider(graph, PartialLayouter.PARTIAL_NODES_DPKEY);
196     backupDataProvider(graph, PartialLayouter.PARTIAL_EDGES_DPKEY);
197     //register dp for selected nodes/edges
198     graph.addDataProvider(PartialLayouter.PARTIAL_NODES_DPKEY, Selections.createSelectionNodeMap(graph));
199     graph.addDataProvider(PartialLayouter.PARTIAL_EDGES_DPKEY, Selections.createSelectionEdgeMap(graph));
200 
201     if (options.getBool(ITEM_CONSIDER_EDGE_DIRECTION)
202         && graph.getDataProvider(PartialLayouter.DIRECTED_EDGES_DPKEY) == null) {
203       graph.addDataProvider(PartialLayouter.DIRECTED_EDGES_DPKEY, new ModuleDataProvider() {
204         public boolean getBool(Object dataHolder) {
205           if (!(dataHolder instanceof Edge)) {
206             return false;
207           }
208 
209           final EdgeRealizer realizer = graph.getRealizer((Edge) dataHolder);
210           // directed => exactly one endpoint has an arrow
211           return (realizer.getSourceArrow() == Arrow.NONE) == (realizer.getTargetArrow() != Arrow.NONE); 
212         }
213       });
214     }
215   }
216 
217   /**
218    * Restores the given <code>graph</code> by freeing up resources created by
219    * {@link #prepareGraph(y.view.Graph2D, y.option.OptionHandler)}.
220    * @param graph the graph for which <code>prepareGraph</code> has been called
221    * @param options the options for the module's layout algorithm
222    */
223   protected void restoreGraph(final Graph2D graph, final OptionHandler options) {
224     // remove the data providers set by this module by restoring the initial state
225     if (graph.getDataProvider(PartialLayouter.DIRECTED_EDGES_DPKEY) instanceof ModuleDataProvider) {
226       // so options.getBool(ITEM_CONSIDER_EDGE_DIRECTION) is also true 
227       graph.removeDataProvider(PartialLayouter.DIRECTED_EDGES_DPKEY);
228     }
229 
230     restoreDataProvider(graph, PartialLayouter.PARTIAL_NODES_DPKEY);
231     restoreDataProvider(graph, PartialLayouter.PARTIAL_EDGES_DPKEY);
232   }
233 
234   /**
235    * Configures the module's layout algorithm according to the given options.
236    * @param partial the <code>PartialLayouter</code> to be configured
237    * @param options the layout options to set
238    */
239   protected void configure(final PartialLayouter partial, final OptionHandler options) {
240     partial.setMinimalNodeDistance(options.getInt(ITEM_MIN_NODE_DIST));
241     partial.setConsiderNodeAlignment(options.getBool(ITEM_CONSIDER_SNAPLINES));
242 
243     final byte subgraphPositioningStrategy =
244         subgraphPositioningStrategyAsByte(options.getString(ITEM_SUBGRAPH_POSITION_STRATEGY));
245     partial.setPositioningStrategy(subgraphPositioningStrategy);
246 
247     final byte componentAssignment = componentAssignmentAsByte(options.getString(ITEM_MODE_COMPONENT_ASSIGNMENT));
248     partial.setComponentAssignmentStrategy(componentAssignment);
249 
250     final byte mainGraphOrientation = graphOrientationAsByte(options.getString(ITEM_ORIENTATION_MAIN_GRAPH));
251     partial.setLayoutOrientation(mainGraphOrientation);
252 
253     final byte routingToSubGraph = routingToSubGraphAsByte(options.getString(ITEM_ROUTING_TO_SUBGRAPH));
254     partial.setEdgeRoutingStrategy(routingToSubGraph);
255 
256     partial.setCoreLayouter(defineSubgraphLayouter(options, routingToSubGraph));
257   }
258 
259   private boolean isHorizontalLayoutConf(OptionHandler options) {
260     final byte mainGraphOrientation = graphOrientationAsByte(options.getString(ITEM_ORIENTATION_MAIN_GRAPH));
261     return (mainGraphOrientation == PartialLayouter.ORIENTATION_LEFT_TO_RIGHT)
262         || (mainGraphOrientation == PartialLayouter.ORIENTATION_RIGHT_TO_LEFT);
263   }
264 
265   
266   private Layouter defineSubgraphLayouter(final OptionHandler options, final byte routingToSubGraph) {
267     final String subgraphLayouterString = options.getString(ITEM_SUBGRAPH_LAYOUTER);
268     if (VALUE_SUBGRAPH_LAYOUTER_IHL.equals(subgraphLayouterString)) {
269       final IncrementalHierarchicLayouter ihl = new IncrementalHierarchicLayouter();
270       ihl.setIntegratedEdgeLabelingEnabled(true);
271       if (PartialLayouter.EDGE_ROUTING_STRATEGY_ORTHOGONAL == routingToSubGraph) {
272         ihl.setOrthogonallyRouted(true);
273       } else {
274         ihl.setOrthogonallyRouted(false);
275       }
276       return ihl;
277     } else if (VALUE_SUBGRAPH_LAYOUTER_ORGANIC.equals(subgraphLayouterString)) {
278       final SmartOrganicLayouter sol = new SmartOrganicLayouter();
279       sol.setDeterministic(true);
280       return sol;
281     } else if (VALUE_SUBGRAPH_LAYOUTER_CIRCULAR.equals(subgraphLayouterString)) {
282       return new CircularLayouter();
283     } else if (VALUE_SUBGRAPH_LAYOUTER_ORTHOGONAL.equals(subgraphLayouterString)) {
284       return new OrthogonalLayouter();
285     } else {
286       // else if VALUE_MODE_COMPONENT_SINGLE.equals(options.getString(ITEM_MODE_COMPONENT_ASSIGNMENT)
287       // or VALUE_SUBGRAPH_LAYOUTER_ORTHOGONAL.equals(subgraphLayouterString)
288       // or VALUE_SUBGRAPH_LAYOUTER_NO_LAYOUT.equals(subgraphLayouterString)
289       
290       // trivial case: no subgraph layouter
291       return null;
292     }
293   }
294 
295   /** Determines the routing of inter edges (main graph to subgraph). */
296   private static byte routingToSubGraphAsByte(final String routingToSubGraphString) {
297     if (VALUE_ROUTING_TO_SUBGRAPH_ORTHOGONALLY.equals(routingToSubGraphString)) {
298       return PartialLayouter.EDGE_ROUTING_STRATEGY_ORTHOGONAL;
299     } else if (VALUE_ROUTING_TO_SUBGRAPH_AUTO.equals(routingToSubGraphString)) {
300       return PartialLayouter.EDGE_ROUTING_STRATEGY_AUTOMATIC;
301     } else if (VALUE_ROUTING_TO_SUBGRAPH_ORGANIC.equals(routingToSubGraphString)) {
302       return PartialLayouter.EDGE_ROUTING_STRATEGY_ORGANIC;
303     } else if (VALUE_ROUTING_TO_SUBGRAPH_POLYLINE.equals(routingToSubGraphString)) {
304       return PartialLayouter.EDGE_ROUTING_STRATEGY_OCTILINEAR;
305     } else {
306       // else if VALUE_ROUTING_TO_SUBGRAPH_STRAIGHT_LINE.equals(routingToSubGraphString)
307       return PartialLayouter.EDGE_ROUTING_STRATEGY_STRAIGHTLINE;
308     }
309   }
310 
311   /** Determines the subgraph position strategy. */
312   private static byte subgraphPositioningStrategyAsByte(final String subgraphPositioningStrategyString) {
313     if (VALUE_SUBGRAPH_POSITIONING_STRATEGY_FROM_SKETCH.equals(subgraphPositioningStrategyString)) {
314       return PartialLayouter.SUBGRAPH_POSITIONING_STRATEGY_FROM_SKETCH;
315     } else {
316       return PartialLayouter.SUBGRAPH_POSITIONING_STRATEGY_BARYCENTER;
317     }
318   }
319 
320   /** Determines component by: {Clustering, Connected graph, Particular}. */
321   private static byte componentAssignmentAsByte(final String componentAssignmentString) {
322     if (VALUE_MODE_COMPONENT_SINGLE.equals(componentAssignmentString)) {
323       return PartialLayouter.COMPONENT_ASSIGNMENT_STRATEGY_SINGLE;
324     } else if (VALUE_MODE_COMPONENT_CONNECTED.equals(componentAssignmentString)) {
325       return PartialLayouter.COMPONENT_ASSIGNMENT_STRATEGY_CONNECTED;
326     } else if (VALUE_MODE_COMPONENT_CLUSTERING.equals(componentAssignmentString)) {
327       return PartialLayouter.COMPONENT_ASSIGNMENT_STRATEGY_CLUSTERING;
328     } else {
329       return PartialLayouter.COMPONENT_ASSIGNMENT_STRATEGY_CUSTOMIZED;
330     }
331   }
332 
333   /** Determines the main graph Orientation. */
334   private static byte graphOrientationAsByte(final String graphOrientationString) {
335     if (VALUE_ORIENTATION_MAIN_GRAPH_AUTO_DETECT.equals(graphOrientationString)) {
336       return PartialLayouter.ORIENTATION_AUTO_DETECTION;
337     } else if (VALUE_ORIENTATION_MAIN_GRAPH_TOP_TO_DOWN.equals(graphOrientationString)) {
338       return PartialLayouter.ORIENTATION_TOP_TO_BOTTOM;
339     } else if (VALUE_ORIENTATION_MAIN_GRAPH_DOWN_TO_TOP.equals(graphOrientationString)) {
340       return PartialLayouter.ORIENTATION_BOTTOM_TO_TOP;
341     } else if (VALUE_ORIENTATION_MAIN_GRAPH_LEFT_TO_RIGHT.equals(graphOrientationString)) {
342       return PartialLayouter.ORIENTATION_LEFT_TO_RIGHT;
343     } else if (VALUE_ORIENTATION_MAIN_GRAPH_RIGHT_TO_LEFT.equals(graphOrientationString)) {
344       return PartialLayouter.ORIENTATION_RIGHT_TO_LEFT;
345     } else if (VALUE_ORIENTATION_MAIN_GRAPH_NONE.equals(graphOrientationString)) {
346       return PartialLayouter.ORIENTATION_NONE;
347     } else {
348       return PartialLayouter.ORIENTATION_AUTO_DETECTION;
349     }
350   }
351 
352   private class ModuleDataProvider extends DataProviderAdapter {
353   }
354 }
355