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.ComponentLayouter;
34  import y.layout.Layouter;
35  import y.layout.PartitionLayouter;
36  import y.layout.PartitionLayouter.ComponentPartitionPlacer;
37  import y.layout.grouping.GroupNodeHider;
38  import y.layout.orthogonal.CompactOrthogonalLayouter;
39  import y.layout.orthogonal.OrthogonalLayouter;
40  import y.layout.router.ChannelEdgeRouter;
41  import y.layout.router.OrthogonalPatternEdgeRouter;
42  import y.layout.router.polyline.EdgeLayoutDescriptor;
43  import y.layout.router.polyline.EdgeRouter;
44  import y.layout.router.polyline.PenaltySettings;
45  import y.option.ConstraintManager;
46  import y.option.IntOptionItem;
47  import y.option.OptionGroup;
48  import y.option.OptionHandler;
49  import y.option.OptionItem;
50  import y.view.Graph2DView;
51  
52  import java.awt.Dimension;
53  
54  /**
55   * This module represents an interactive configurator and launcher for
56   * {@link y.layout.orthogonal.CompactOrthogonalLayouter}.
57   *
58   *
59   * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/compact_orthogonal_layouter#compact_orthogonal_layouter" target="_blank">Section Compact Orthogonal Layout</a> in the yFiles for Java Developer's Guide
60   */
61  public class CompactOrthogonalLayoutModule extends LayoutModule {
62    //// Module 'Compact Orthogonal Layout'
63    protected static final String MODULE_COMPACT_ORTHOGONAL = "COMPACT_ORTHOGONAL";
64  
65    //// Section 'default' items
66    protected static final String ITEM_ORTHOGONAL_LAYOUT_STYLE = "ORTHOGONAL_LAYOUT_STYLE";
67    protected static final String VALUE_NORMAL = "NORMAL";
68    protected static final String VALUE_NORMAL_TREE = "NORMAL_TREE";
69    protected static final String VALUE_FIXED_MIXED = "FIXED_MIXED";
70    protected static final String VALUE_FIXED_BOX_NODES = "FIXED_BOX_NODES";
71    protected static final String ITEM_PLACEMENT_STRATEGY = "PLACEMENT_STRATEGY";
72    protected static final String VALUE_STYLE_ROWS = "STYLE_ROWS";
73    protected static final String VALUE_STYLE_PACKED_COMPACT_RECTANGLE = "STYLE_PACKED_COMPACT_RECTANGLE";
74    protected static final String ITEM_USE_VIEW_ASPECT_RATIO = "USE_VIEW_ASPECT_RATIO";
75    protected static final String ITEM_ASPECT_RATIO = "ASPECT_RATIO";
76    protected static final String ITEM_GRID = "GRID";
77    protected static final String TITLE_INTER_EDGE_ROUTER = "INTER_EDGE_ROUTER";
78    protected static final String ITEM_EDGE_ROUTER = "EDGE_ROUTER";
79    protected static final String VALUE_EDGE_ROUTER_CHANNEL_FAST = "EDGE_ROUTER_CHANNEL_FAST";
80    protected static final String VALUE_EDGE_ROUTER_CHANNEL_HQ = "EDGE_ROUTER_CHANNEL_HQ";
81    //for orthogonal edge routing we take the PartitionLayouter.PolylineInterEdgeRouter
82    protected static final String VALUE_EDGE_ROUTER_ORTHOGONAL = "EDGE_ROUTER_ORTHOGONAL";
83    protected static final String ITEM_ROUTE_ALL_EDGES = "ROUTE_ALL_EDGES";
84    protected static final String ITEM_BEND_COST = "BEND_COST";
85    protected static final String ITEM_NODE_CROSSING_COST = "NODE_CROSSING_COST";
86    protected static final String ITEM_MINIMUM_DISTANCE = "MINIMUM_DISTANCE";
87    protected static final String ITEM_EDGE_CROSSING_COST = "EDGE_CROSSING_COST";
88    protected static final String ITEM_CENTER_TO_SPACE_RATIO = "SPACE_DRIVEN_VS_CENTER_DRIVEN_SEARCH";
89  
90    /**
91     * Creates an instance of this module.
92     */
93    public CompactOrthogonalLayoutModule() {
94      super(MODULE_COMPACT_ORTHOGONAL);
95      setPortIntersectionCalculatorEnabled(true);
96    }
97  
98    /**
99     * Creates an OptionHandler and adds the option items used by this module.
100    * @return the created <code>OptionHandler</code> providing module related options
101    */
102   protected OptionHandler createOptionHandler() {
103     final OptionHandler options = new OptionHandler(getModuleName());
104     final ConstraintManager optionConstraints = new ConstraintManager(options);
105     // Defaults provider
106     final CompactOrthogonalLayouter defaults = new CompactOrthogonalLayouter();
107     if (false) {
108         defaults.setInterEdgeRouter(new PartitionLayouter.PolylineInterEdgeRouter());
109     } else {
110         defaults.setInterEdgeRouter(new PartitionLayouter.ChannelInterEdgeRouter());
111     }
112     defaults.setPartitionPlacer(new ComponentPartitionPlacer());
113     defaults.setCoreLayouter(new OrthogonalLayouter());
114     final OrthogonalPatternEdgeRouter defaultsOper = new OrthogonalPatternEdgeRouter();
115     final ChannelEdgeRouter.OrthogonalShortestPathPathFinder defaultsPsppf =
116         new ChannelEdgeRouter.OrthogonalShortestPathPathFinder();
117 
118     // Populate default section
119     final int styleIndex;
120     switch (((OrthogonalLayouter) defaults.getCoreLayouter()).getLayoutStyle()) {
121       case OrthogonalLayouter.NORMAL_STYLE:
122       default:
123         styleIndex = 0;
124         break;
125       case OrthogonalLayouter.NORMAL_TREE_STYLE:
126         styleIndex = 1;
127         break;
128       case OrthogonalLayouter.FIXED_MIXED_STYLE:
129         styleIndex = 2;
130         break;
131       case OrthogonalLayouter.FIXED_BOX_STYLE:
132         styleIndex = 3;
133         break;
134     }
135     options.addEnum(ITEM_ORTHOGONAL_LAYOUT_STYLE, new String[]{
136         VALUE_NORMAL,
137         VALUE_NORMAL_TREE,
138         VALUE_FIXED_MIXED,
139         VALUE_FIXED_BOX_NODES
140     }, styleIndex);
141 
142     int compStyleIndex;
143     switch (((ComponentPartitionPlacer) defaults.getPartitionPlacer()).getComponentLayouter().getStyle()) {
144       case ComponentLayouter.STYLE_ROWS:
145       default:
146         compStyleIndex = 0;
147         break;
148       case ComponentLayouter.STYLE_PACKED_COMPACT_RECTANGLE:
149         compStyleIndex = 1;
150         break;
151     }
152     options.addEnum(ITEM_PLACEMENT_STRATEGY, new String[]{
153         VALUE_STYLE_ROWS,
154         VALUE_STYLE_PACKED_COMPACT_RECTANGLE
155     }, compStyleIndex);
156 
157     final OptionItem itemUseViewAspectRation = options.addBool(ITEM_USE_VIEW_ASPECT_RATIO, true);
158     final OptionItem itemAspectRatio = options.addDouble(ITEM_ASPECT_RATIO, defaults.getAspectRatio());
159     options.addInt(ITEM_GRID, defaults.getGridSpacing()).setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(1));
160     // Enable/disable items depending on specific values
161     optionConstraints.setEnabledOnValueEquals(itemUseViewAspectRation, Boolean.FALSE, itemAspectRatio);
162 
163     // Group 'Inter Edge Router'
164     final OptionGroup interEdgeGroup = new OptionGroup();
165     interEdgeGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_INTER_EDGE_ROUTER);
166     // Populate group
167     final OptionItem itemEdgeRouter = interEdgeGroup.addItem(
168         options.addEnum(ITEM_EDGE_ROUTER, new String[]{
169             VALUE_EDGE_ROUTER_CHANNEL_FAST,
170             VALUE_EDGE_ROUTER_CHANNEL_HQ,
171             VALUE_EDGE_ROUTER_ORTHOGONAL
172         }, 1));
173     interEdgeGroup.addItem(options.addBool(ITEM_ROUTE_ALL_EDGES,
174         !((PartitionLayouter.ChannelInterEdgeRouter) defaults.getInterEdgeRouter()).isRouteInterEdgesOnly()));
175 
176     // Path finding strategies
177     final OptionItem itemBendCost = interEdgeGroup.addItem(
178         options.addDouble(ITEM_BEND_COST, defaultsOper.getBendCost()));
179     final OptionItem itemNodeCrossingCost = interEdgeGroup.addItem(
180         options.addDouble(ITEM_NODE_CROSSING_COST, defaultsOper.getNodeCrossingCost()));
181     final OptionItem itemMinimumDistance =interEdgeGroup.addItem(
182         options.addInt(ITEM_MINIMUM_DISTANCE, defaultsPsppf.getMinimumDistance()));
183     itemMinimumDistance.setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(4));
184     interEdgeGroup.addItem(options.addDouble(ITEM_EDGE_CROSSING_COST, defaultsPsppf.getCrossingCost()));
185     final OptionItem itemCenterSpaceRatio = interEdgeGroup.addItem(
186         options.addDouble(ITEM_CENTER_TO_SPACE_RATIO, defaultsPsppf.getCenterToSpaceRatio(), 0, 1));
187 
188     // Enable/disable items depending on specific values
189     optionConstraints.setEnabledOnCondition(
190         optionConstraints.createConditionValueEquals(itemEdgeRouter, VALUE_EDGE_ROUTER_CHANNEL_FAST).or(
191             optionConstraints.createConditionValueEquals(itemEdgeRouter, VALUE_EDGE_ROUTER_ORTHOGONAL)
192         ), itemBendCost);
193     optionConstraints.setEnabledOnValueEquals(itemEdgeRouter, VALUE_EDGE_ROUTER_CHANNEL_FAST, itemNodeCrossingCost);
194     optionConstraints.setEnabledOnValueEquals(itemEdgeRouter, VALUE_EDGE_ROUTER_CHANNEL_HQ, itemCenterSpaceRatio);
195     
196     return options;
197   }
198 
199 
200   /**
201    * Main module execution routine.
202    * Launches the module's underlying algorithm on the module's graph based on user options.
203    */
204   protected void mainrun() {
205     final CompactOrthogonalLayouter orthogonal = new CompactOrthogonalLayouter();
206 
207     final OptionHandler options = getOptionHandler();
208     configure(orthogonal, options);
209 
210     // launch layouter in buffered mode
211     final GroupNodeHider gnh = new GroupNodeHider(orthogonal);
212     gnh.setHidingEmptyGroupNodes(false);
213     launchLayouter(gnh);
214   }
215 
216 
217 
218   /**
219    * Configures the module's layout algorithm according to the given options.
220    * <p>
221    * Important: This method does also depend on the <code>Graph2DView</code>
222    * of this module in addition to the method's parameters.
223    * </p>
224    * @param orthogonal the <code>CompactOrthogonalLayouter</code> to be configured
225    * @param options an <code>OptionHandler</code> providing the option-values referred to
226    */
227   protected void configure(final CompactOrthogonalLayouter orthogonal, final OptionHandler options) {
228     final boolean useOrthogonalEdgeRouter = VALUE_EDGE_ROUTER_ORTHOGONAL.equals(options.get(ITEM_EDGE_ROUTER));
229     final PartitionLayouter.InterEdgeRouter ier;
230     if (useOrthogonalEdgeRouter) {
231       ier = new PartitionLayouter.PolylineInterEdgeRouter();
232       orthogonal.setInterEdgeRouter(ier);
233     } else {
234       ier = new PartitionLayouter.ChannelInterEdgeRouter();
235       orthogonal.setInterEdgeRouter(ier);
236     }
237     configure(ier, options);
238 
239     final ComponentPartitionPlacer pp = new ComponentPartitionPlacer();
240     orthogonal.setPartitionPlacer(pp);
241     configure(pp, options);
242 
243     final OrthogonalLayouter cl = new OrthogonalLayouter();
244     orthogonal.setCoreLayouter(cl);
245     configure(cl, options);
246     
247     orthogonal.setGridSpacing(options.getInt(ITEM_GRID));
248 
249     final double ar;
250     final Graph2DView view = getGraph2DView();
251     if (options.getBool(ITEM_USE_VIEW_ASPECT_RATIO) && view != null) {
252       final Dimension dim = view.getSize();
253       ar = dim.getWidth()/dim.getHeight();
254     } else {
255       ar = options.getDouble(ITEM_ASPECT_RATIO);
256     }
257     // this needs to be done as a final step since it will reconfigure
258     // layout stages which support aspect ratio accordingly
259     orthogonal.setAspectRatio(ar);
260   }
261 
262   private void configure(final PartitionLayouter.InterEdgeRouter router, final OptionHandler options) {
263     if (router instanceof PartitionLayouter.PolylineInterEdgeRouter) {
264       final PartitionLayouter.PolylineInterEdgeRouter ier = (PartitionLayouter.PolylineInterEdgeRouter) router;
265       ier.setRouteInterEdgesOnly(!options.getBool(ITEM_ROUTE_ALL_EDGES));
266 
267       final EdgeRouter er = ier.getEdgeRouter();
268       er.setMinimalNodeToEdgeDistance(options.getInt(ITEM_MINIMUM_DISTANCE));
269       final EdgeLayoutDescriptor eld = er.getDefaultEdgeLayoutDescriptor();
270       eld.setMinimalEdgeToEdgeDistance(options.getInt(ITEM_MINIMUM_DISTANCE));
271       
272       final PenaltySettings ps = eld.getPenaltySettings();
273       ps.setEdgeCrossingPenalty((int) Math.ceil(options.getDouble(ITEM_EDGE_CROSSING_COST)));
274       ps.setBendPenalty((int) Math.ceil(options.getDouble(ITEM_BEND_COST)));
275     } else if (router instanceof PartitionLayouter.ChannelInterEdgeRouter) {
276       final PartitionLayouter.ChannelInterEdgeRouter ier = (PartitionLayouter.ChannelInterEdgeRouter) router;
277       ier.setRouteInterEdgesOnly(!options.getBool(ITEM_ROUTE_ALL_EDGES));
278       
279       if (VALUE_EDGE_ROUTER_CHANNEL_FAST.equals(options.getString(ITEM_EDGE_ROUTER))) {
280         final OrthogonalPatternEdgeRouter oper = new OrthogonalPatternEdgeRouter();
281         oper.setMinimumDistance(options.getInt(ITEM_MINIMUM_DISTANCE));
282         oper.setEdgeCrossingCost(options.getDouble(ITEM_EDGE_CROSSING_COST));
283         oper.setNodeCrossingCost(options.getDouble(ITEM_NODE_CROSSING_COST));
284         oper.setBendCost(options.getDouble(ITEM_BEND_COST));
285         ier.getChannelEdgeRouter().setPathFinderStrategy(oper);
286       } else {
287         final ChannelEdgeRouter.OrthogonalShortestPathPathFinder osppf = new ChannelEdgeRouter.OrthogonalShortestPathPathFinder();
288         osppf.setMinimumDistance(options.getInt(ITEM_MINIMUM_DISTANCE));
289         osppf.setCrossingCost(options.getDouble(ITEM_EDGE_CROSSING_COST));
290         osppf.setCenterToSpaceRatio(options.getDouble(ITEM_CENTER_TO_SPACE_RATIO));
291         ier.getChannelEdgeRouter().setPathFinderStrategy(osppf);
292       }
293     }
294   }
295 
296   private void configure(final PartitionLayouter.ComponentPartitionPlacer placer, final OptionHandler options) {
297     if (VALUE_STYLE_PACKED_COMPACT_RECTANGLE.equals(options.get(ITEM_PLACEMENT_STRATEGY))) {
298       placer.getComponentLayouter().setStyle(ComponentLayouter.STYLE_PACKED_COMPACT_RECTANGLE);
299     } else if (VALUE_STYLE_ROWS.equals(options.get(ITEM_PLACEMENT_STRATEGY))) {
300       placer.getComponentLayouter().setStyle(ComponentLayouter.STYLE_MULTI_ROWS);
301     }
302   }
303 
304   private void configure(final OrthogonalLayouter orthogonal, final OptionHandler options) {
305     final String ols = options.getString(ITEM_ORTHOGONAL_LAYOUT_STYLE);
306     if (VALUE_NORMAL_TREE.equals(ols)) {
307       orthogonal.setLayoutStyle(OrthogonalLayouter.NORMAL_TREE_STYLE);
308     } else if (VALUE_FIXED_MIXED.equals(ols)) {
309       orthogonal.setLayoutStyle(OrthogonalLayouter.FIXED_MIXED_STYLE);
310     } else if (VALUE_FIXED_BOX_NODES.equals(ols)) {
311       orthogonal.setLayoutStyle(OrthogonalLayouter.FIXED_BOX_STYLE);
312     } else {
313       // else if VALUE_NORMAL.equals(ols)
314       orthogonal.setLayoutStyle(OrthogonalLayouter.NORMAL_STYLE);
315     }
316   }
317 
318 }
319