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.layout.LayoutOrientation;
34  import y.layout.router.OrganicEdgeRouter;
35  import y.layout.router.StraightLineEdgeRouter;
36  import y.layout.router.polyline.EdgeRouter;
37  import y.layout.seriesparallel.DefaultPortAssignment;
38  import y.layout.seriesparallel.EdgeLayoutDescriptor;
39  import y.layout.seriesparallel.SeriesParallelLayouter;
40  import y.option.ConstraintManager;
41  import y.option.OptionGroup;
42  import y.option.OptionHandler;
43  import y.option.OptionItem;
44  
45  /**
46   * This module represents an interactive configurator and launcher for {@link y.layout.seriesparallel.SeriesParallelLayouter}.
47   *
48   */
49  public class SeriesParallelLayoutModule extends LayoutModule {
50  
51    //// Module 'Series-Parallel Layout'
52    protected static final String MODULE_SERIES_PARALLEL = "SERIES_PARALLEL";
53  
54    //// Section 'General'
55    protected static final String SECTION_GENERAL = "GENERAL";
56    // Section 'General' items
57    protected static final String ITEM_ORIENTATION = "ORIENTATION";
58    protected static final String VALUE_RIGHT_TO_LEFT = "RIGHT_TO_LEFT";
59    protected static final String VALUE_BOTTOM_TO_TOP = "BOTTOM_TO_TOP";
60    protected static final String VALUE_LEFT_TO_RIGHT = "LEFT_TO_RIGHT";
61    protected static final String VALUE_TOP_TO_BOTTOM = "TOP_TO_BOTTOM";
62    protected static final String ITEM_VERTICAL_ALIGNMENT = "VERTICAL_ALIGNMENT";
63    protected static final String VALUE_ALIGNMENT_TOP = "ALIGNMENT_TOP";
64    protected static final String VALUE_ALIGNMENT_CENTER = "ALIGNMENT_CENTER";
65    protected static final String VALUE_ALIGNMENT_BOTTOM = "ALIGNMENT_BOTTOM";
66    protected static final String ITEM_FROM_SKETCH_MODE = "FROM_SKETCH_MODE";
67    protected static final String ITEM_ROUTING_STYLE_FOR_NON_SERIES_PARALLEL_EDGES = "ROUTING_STYLE_FOR_NON_SERIES_PARALLEL_EDGES";
68    protected static final String VALUE_ROUTE_ORGANIC = "ROUTE_ORGANIC";
69    protected static final String VALUE_ROUTE_ORTHOGONAL = "ROUTE_ORTHOGONAL";
70    protected static final String VALUE_ROUTE_STRAIGHTLINE = "ROUTE_STRAIGHTLINE";
71    protected static final String TITLE_MINIMUM_DISTANCES = "MINIMUM_DISTANCES";
72    protected static final String ITEM_NODE_TO_NODE_DISTANCE = "NODE_TO_NODE_DISTANCE";
73    protected static final String ITEM_NODE_TO_EDGE_DISTANCE = "NODE_TO_EDGE_DISTANCE";
74    protected static final String ITEM_EDGE_TO_EDGE_DISTANCE = "EDGE_TO_EDGE_DISTANCE";
75    protected static final String TITLE_LABELING = "LABELING";
76    protected static final String ITEM_CONSIDER_NODE_LABELS = "CONSIDER_NODE_LABELS";
77    protected static final String ITEM_INTEGRATED_EDGE_LABELING = "INTEGRATED_EDGE_LABELING";
78  
79  
80    //// Section 'Edge Settings'
81    protected static final String SECTION_EDGE_SETTINGS = "EDGE_SETTINGS";
82    //// Section 'Edge Settings' items
83    protected static final String ITEM_PORT_STYLE = "PORT_STYLE";
84    protected static final String VALUE_CENTER_PORTS = "CENTER_PORTS";
85    protected static final String VALUE_DISTRIBUTED_PORTS = "DISTRIBUTED_PORTS";
86    protected static final String ITEM_MINIMUM_FIRST_SEGMENT_LENGTH = "MINIMUM_FIRST_SEGMENT_LENGTH";
87    protected static final String ITEM_MINIMUM_LAST_SEGMENT_LENGTH = "MINIMUM_LAST_SEGMENT_LENGTH";
88    protected static final String ITEM_MINIMUM_EDGE_LENGTH = "MINIMUM_EDGE_LENGTH";
89    protected static final String ITEM_ROUTING_STYLE = "ROUTING_STYLE";
90    protected static final String VALUE_ROUTING_STYLE_ORTHOGONAL = "ROUTING_STYLE_ORTHOGONAL";
91    protected static final String VALUE_ROUTING_STYLE_OCTILINEAR = "ROUTING_STYLE_OCTILINEAR";
92    protected static final String VALUE_ROUTING_STYLE_POLYLINE = "ROUTING_STYLE_POLYLINE";
93    protected static final String ITEM_PREFERRED_OCTILINEAR_SEGMENT_LENGTH = "PREFERRED_OCTILINEAR_SEGMENT_LENGTH";
94    protected static final String ITEM_POLYLINE_DISTANCE = "POLYLINE_DISTANCE";
95    protected static final String ITEM_MINIMUM_SLOPE = "MINIMUM_SLOPE";
96    protected static final String ITEM_ROUTE_IN_FLOW = "ROUTE_IN_FLOW";
97  
98    /**
99     * Creates an instance of this module.
100    */
101   public SeriesParallelLayoutModule() {
102     super(MODULE_SERIES_PARALLEL);
103   }
104 
105   /**
106    * Factory method responsible for creating and initializing the OptionHandler for this module.
107    */
108   protected OptionHandler createOptionHandler() {
109     final OptionHandler options = new OptionHandler(getModuleName());
110 
111     final SeriesParallelLayouter defaults = new SeriesParallelLayouter();
112     final EdgeLayoutDescriptor eld = defaults.getDefaultEdgeLayoutDescriptor();
113 
114     //// Section 'General'
115     options.useSection(SECTION_GENERAL);
116     // Populate section
117     options.addEnum(ITEM_ORIENTATION, new Object[]{
118             VALUE_TOP_TO_BOTTOM,
119             VALUE_LEFT_TO_RIGHT,
120             VALUE_BOTTOM_TO_TOP,
121             VALUE_RIGHT_TO_LEFT,
122     }, 0);
123     options.addEnum(ITEM_VERTICAL_ALIGNMENT, new String[]{
124             VALUE_ALIGNMENT_TOP,
125             VALUE_ALIGNMENT_CENTER,
126             VALUE_ALIGNMENT_BOTTOM,
127     }, 1);
128     options.addBool(ITEM_FROM_SKETCH_MODE, defaults.isFromSketchModeEnabled());
129     options.addEnum(ITEM_ROUTING_STYLE_FOR_NON_SERIES_PARALLEL_EDGES, new String[]{
130             VALUE_ROUTE_ORGANIC,
131             VALUE_ROUTE_ORTHOGONAL,
132             VALUE_ROUTE_STRAIGHTLINE
133     }, 0);
134 
135     // Group 'Minimum Distances'
136     final OptionGroup minDistGroup = new OptionGroup();
137     minDistGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_MINIMUM_DISTANCES);
138     // Populate group
139     minDistGroup.addItem(options.addDouble(ITEM_NODE_TO_NODE_DISTANCE, 30.0d));
140     minDistGroup.addItem(options.addDouble(ITEM_NODE_TO_EDGE_DISTANCE, 15.0d));
141     minDistGroup.addItem(options.addDouble(ITEM_EDGE_TO_EDGE_DISTANCE, 15.0d));
142 
143     // Group 'Labeling'
144     final OptionGroup labelingGroup = new OptionGroup();
145     // Populate group
146     labelingGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_LABELING);
147     labelingGroup.addItem(options.addBool(ITEM_CONSIDER_NODE_LABELS, true));
148     labelingGroup.addItem(options.addBool(ITEM_INTEGRATED_EDGE_LABELING, true));
149 
150     //// Section 'Edge Settings'
151     options.useSection(SECTION_EDGE_SETTINGS);
152     // Populate section
153     options.addEnum(ITEM_PORT_STYLE, new String[]{
154             VALUE_CENTER_PORTS,
155             VALUE_DISTRIBUTED_PORTS,
156     }, 0);
157     options.addDouble(ITEM_MINIMUM_FIRST_SEGMENT_LENGTH, eld.getMinimumFirstSegmentLength());
158     options.addDouble(ITEM_MINIMUM_LAST_SEGMENT_LENGTH, eld.getMinimumLastSegmentLength());
159     options.addDouble(ITEM_MINIMUM_EDGE_LENGTH, 20.0d);
160     final OptionItem itemRoutingStyle = options.addEnum(ITEM_ROUTING_STYLE, new String[]{
161             VALUE_ROUTING_STYLE_ORTHOGONAL,
162             VALUE_ROUTING_STYLE_OCTILINEAR,
163             VALUE_ROUTING_STYLE_POLYLINE,
164     }, 0);
165     final OptionItem itemSegmentLength = options.addDouble(ITEM_PREFERRED_OCTILINEAR_SEGMENT_LENGTH, defaults.getPreferredOctilinearSegmentLength());
166     final OptionItem itemDistance = options.addDouble(ITEM_POLYLINE_DISTANCE, defaults.getMinimumPolylineSegmentLength());
167     final OptionItem itemMinimumSlope = options.addDouble(ITEM_MINIMUM_SLOPE, defaults.getMinimumSlope(), 0, 5);
168     options.addBool(ITEM_ROUTE_IN_FLOW, true);
169     // Enable/disable items depending on specific values
170     final ConstraintManager optionConstraints = new ConstraintManager(options);
171     optionConstraints.setEnabledOnValueEquals(itemRoutingStyle, VALUE_ROUTING_STYLE_OCTILINEAR, itemSegmentLength);
172     optionConstraints.setEnabledOnValueEquals(itemRoutingStyle, VALUE_ROUTING_STYLE_POLYLINE, itemDistance);
173     optionConstraints.setEnabledOnValueEquals(itemRoutingStyle, VALUE_ROUTING_STYLE_POLYLINE, itemMinimumSlope);
174 
175     return options;
176   }
177 
178   /**
179    * Main module execution routine which configures and launches the series-parallel layouter.
180    */
181   protected void mainrun() {
182     final SeriesParallelLayouter series = new SeriesParallelLayouter();
183 
184     final OptionHandler options = getOptionHandler();
185 
186     configure(series, options);
187 
188     launchLayouter(series);
189   }
190 
191   /**
192    * Configures the module's layout algorithm according to the given options.
193    * @param series the <code>SeriesParallelLayouter</code> to be configured
194    * @param options the layout options to set
195    */
196   protected void configure( final SeriesParallelLayouter series, final OptionHandler options ) {
197     series.setGeneralGraphHandlingEnabled(true);
198 
199     final Object orientation = options.get(ITEM_ORIENTATION);
200     if (VALUE_LEFT_TO_RIGHT.equals(orientation)) {
201       series.setLayoutOrientation(LayoutOrientation.LEFT_TO_RIGHT);
202     } else if (VALUE_BOTTOM_TO_TOP.equals(orientation)) {
203       series.setLayoutOrientation(LayoutOrientation.BOTTOM_TO_TOP);
204     } else if (VALUE_RIGHT_TO_LEFT.equals(orientation)) {
205       series.setLayoutOrientation(LayoutOrientation.RIGHT_TO_LEFT);
206     } else {
207       series.setLayoutOrientation(LayoutOrientation.TOP_TO_BOTTOM);
208     }
209 
210     final String verticalAlignment = options.getString(ITEM_VERTICAL_ALIGNMENT);
211     if (VALUE_ALIGNMENT_TOP.equals(verticalAlignment)) {
212       series.setVerticalAlignment(0);
213     } else if (VALUE_ALIGNMENT_CENTER.equals(verticalAlignment)) {
214       series.setVerticalAlignment(0.5);
215     } else { // VALUE_ALIGNMENT_BOTTOM
216       series.setVerticalAlignment(1);
217     }
218 
219     final String routingStyle = options.getString(ITEM_ROUTING_STYLE);
220     if (VALUE_ROUTING_STYLE_ORTHOGONAL.equals(routingStyle)) {
221       series.setRoutingStyle(SeriesParallelLayouter.ROUTING_STYLE_ORTHOGONAL);
222     } else if (VALUE_ROUTING_STYLE_OCTILINEAR.equals(routingStyle)) {
223       series.setRoutingStyle(SeriesParallelLayouter.ROUTING_STYLE_OCTILINEAR);
224       series.setPreferredOctilinearSegmentLength(options.getDouble(ITEM_PREFERRED_OCTILINEAR_SEGMENT_LENGTH));
225     } else { // VALUE_ROUTING_STYLE_POLYLINE
226       series.setRoutingStyle(SeriesParallelLayouter.ROUTING_STYLE_POLYLINE);
227       series.setMinimumPolylineSegmentLength(options.getDouble(ITEM_POLYLINE_DISTANCE));
228       series.setMinimumSlope(options.getDouble(ITEM_MINIMUM_SLOPE));
229     }
230 
231     final DefaultPortAssignment defaultPortAssignment = (DefaultPortAssignment) series.getDefaultPortAssignment();
232     if (options.getBool(ITEM_ROUTE_IN_FLOW)) {
233       defaultPortAssignment.setForkStyle(DefaultPortAssignment.FORK_STYLE_OUTSIDE_NODE);
234     } else {
235       defaultPortAssignment.setForkStyle(DefaultPortAssignment.FORK_STYLE_AT_NODE);
236     }
237 
238     series.setFromSketchModeEnabled(options.getBool(ITEM_FROM_SKETCH_MODE));
239 
240     final Object nonSeriesParallelRoutingStyle = options.get(ITEM_ROUTING_STYLE_FOR_NON_SERIES_PARALLEL_EDGES);
241     if (VALUE_ROUTE_ORGANIC.equals(nonSeriesParallelRoutingStyle)) {
242       final OrganicEdgeRouter organic = new OrganicEdgeRouter();
243       series.setNonSeriesParallelEdgeRouter(organic);
244       series.setNonSeriesParallelEdgesDpKey(OrganicEdgeRouter.ROUTE_EDGE_DPKEY);
245     } else if (VALUE_ROUTE_ORTHOGONAL.equals(nonSeriesParallelRoutingStyle)) {
246       final EdgeRouter orthogonal = new EdgeRouter();
247       orthogonal.setReroutingEnabled(true);
248       orthogonal.setSphereOfAction(EdgeRouter.ROUTE_SELECTED_EDGES);
249       series.setNonSeriesParallelEdgeRouter(orthogonal);
250       series.setNonSeriesParallelEdgesDpKey(orthogonal.getSelectedEdgesDpKey());
251     } else if (VALUE_ROUTE_STRAIGHTLINE.equals(nonSeriesParallelRoutingStyle)) {
252       final StraightLineEdgeRouter straightLine = new StraightLineEdgeRouter();
253       straightLine.setSphereOfAction(StraightLineEdgeRouter.ROUTE_SELECTED_EDGES);
254       series.setNonSeriesParallelEdgeRouter(straightLine);
255       series.setNonSeriesParallelEdgesDpKey(straightLine.getSelectedEdgesDpKey());
256     }
257 
258     series.setMinimumNodeToNodeDistance(options.getDouble(ITEM_NODE_TO_NODE_DISTANCE));
259     series.setMinimumNodeToEdgeDistance(options.getDouble(ITEM_NODE_TO_EDGE_DISTANCE));
260     series.setMinimumEdgeToEdgeDistance(options.getDouble(ITEM_EDGE_TO_EDGE_DISTANCE));
261 
262     final Object portStyle = options.get(ITEM_PORT_STYLE);
263     if (VALUE_CENTER_PORTS.equals(portStyle)) {
264       defaultPortAssignment.setMode(DefaultPortAssignment.PORT_ASSIGNMENT_MODE_CENTER);
265     } else {
266       defaultPortAssignment.setMode(DefaultPortAssignment.PORT_ASSIGNMENT_MODE_DISTRIBUTED);
267     }
268     final EdgeLayoutDescriptor eld = series.getDefaultEdgeLayoutDescriptor();
269     eld.setMinimumFirstSegmentLength(options.getDouble(ITEM_MINIMUM_FIRST_SEGMENT_LENGTH));
270     eld.setMinimumLastSegmentLength(options.getDouble(ITEM_MINIMUM_LAST_SEGMENT_LENGTH));
271     eld.setMinimumLength(options.getDouble(ITEM_MINIMUM_EDGE_LENGTH));
272 
273     series.setConsiderNodeLabelsEnabled(options.getBool(ITEM_CONSIDER_NODE_LABELS));
274     series.setIntegratedEdgeLabelingEnabled(options.getBool(ITEM_INTEGRATED_EDGE_LABELING));
275   }
276 }
277