1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.9. 
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2000-2011 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.layout.module;
15  
16  import y.module.LayoutModule;
17  import y.module.YModule;
18  
19  import y.layout.ComponentLayouter;
20  import y.layout.Layouter;
21  import y.layout.PartitionLayouter;
22  import y.layout.grouping.GroupNodeHider;
23  import y.layout.orthogonal.CompactOrthogonalLayouter;
24  import y.layout.orthogonal.OrthogonalLayouter;
25  import y.layout.router.ChannelEdgeRouter;
26  import y.layout.router.OrthogonalPatternEdgeRouter;
27  import y.option.ConstraintManager;
28  import y.option.IntOptionItem;
29  import y.option.OptionGroup;
30  import y.option.OptionHandler;
31  
32  import java.awt.Dimension;
33  
34  /**
35   * This module represents an interactive configurator and launcher for
36   * {@link y.layout.orthogonal.CompactOrthogonalLayouter}.
37   *
38   *
39   * @see <a href="http://docs.yworks.com/yfiles/doc/developers-guide/compact_orthogonal_layouter.html#compact_orthogonal_layouter">Section Compact Orthogonal Layout</a> in the yFiles for Java Developer's Guide
40   */
41  public class CompactOrthogonalLayoutModule extends LayoutModule {
42    private static final String NAME = "COMPACT_ORTHOGONAL";
43  
44    private static final String ORTHOGONAL_LAYOUT_STYLE = "ORTHOGONAL_LAYOUT_STYLE";
45    private static final String GRID = "GRID";
46  
47    private static final String NORMAL = "NORMAL";
48    private static final String NORMAL_TREE = "NORMAL_TREE";
49    private static final String FIXED_MIXED = "FIXED_MIXED";
50    private static final String FIXED_BOX_NODES = "FIXED_BOX_NODES";
51  
52    private static final String ASPECT_RATIO = "ASPECT_RATIO";
53    private static final String USE_VIEW_ASPECT_RATIO = "USE_VIEW_ASPECT_RATIO";
54  
55    private static final String PLACEMENT_STRATEGY = "PLACEMENT_STRATEGY";
56    private static final String STYLE_ROWS = "STYLE_ROWS";
57    private static final String STYLE_PACKED_COMPACT_RECTANGLE = "STYLE_PACKED_COMPACT_RECTANGLE";
58  
59    // ChannelInterEdgeRouter stuff
60    private static final String PATH_FINDER = "PATH_FINDER";
61    private static final String ORTHOGONAL_PATTERN_PATH_FINDER = "ORTHOGONAL_PATTERN_PATH_FINDER";
62    private static final String ORTHOGONAL_SHORTESTPATH_PATH_FINDER = "ORTHOGONAL_SHORTESTPATH_PATH_FINDER";
63    private static final String INTER_EDGE_ROUTER = "INTER_EDGE_ROUTER";
64    private static final String ROUTE_ALL_EDGES = "ROUTE_ALL_EDGES";
65  
66    // ChannelEdgeRouter stuff
67    private static final String MINIMUM_DISTANCE = "MINIMUM_DISTANCE";
68    private static final String CENTER_TO_SPACE_RATIO = "SPACE_DRIVEN_VS_CENTER_DRIVEN_SEARCH";
69    private static final String EDGE_CROSSING_COST = "EDGE_CROSSING_COST";
70    private static final String NODE_CROSSING_COST = "NODE_CROSSING_COST";
71    private static final String BEND_COST = "BEND_COST";
72  
73    // for the option handler
74    private static final String[] COMPONENT_STYLE_ENUM = {
75      STYLE_ROWS,
76      STYLE_PACKED_COMPACT_RECTANGLE
77    };
78    private static final String[] STYLE_ENUM = {
79      NORMAL, NORMAL_TREE, FIXED_MIXED, FIXED_BOX_NODES
80    };
81    private static final String[] PATH_FINDER_ENUM = {
82      ORTHOGONAL_PATTERN_PATH_FINDER,
83      ORTHOGONAL_SHORTESTPATH_PATH_FINDER
84    };
85  
86  
87    public CompactOrthogonalLayoutModule() {
88      super (NAME,"yFiles Layout Team",
89             "Compact Orthogonal Layouter");
90      setPortIntersectionCalculatorEnabled(true);
91    }
92  
93    public OptionHandler createOptionHandler() {
94      OptionHandler op = new OptionHandler(getModuleName());
95      ConstraintManager cm =  new ConstraintManager(op);
96      OptionGroup og;
97  
98  
99      // use an instance of the layouter as a defaults provider
100     CompactOrthogonalLayouter layouter = new CompactOrthogonalLayouter();
101     prepare(layouter);
102 
103     OrthogonalLayouter cl = (OrthogonalLayouter) layouter.getCoreLayouter();
104 
105     int styleIndex = 0;
106     switch (cl.getLayoutStyle()) {
107       case OrthogonalLayouter.NORMAL_STYLE:
108         styleIndex = 0;
109         break;
110       case OrthogonalLayouter.NORMAL_TREE_STYLE:
111         styleIndex = 1;
112         break;
113       case OrthogonalLayouter.FIXED_MIXED_STYLE:
114         styleIndex = 2;
115         break;
116       case OrthogonalLayouter.FIXED_BOX_STYLE:
117         styleIndex = 3;
118         break;
119     }
120     op.addEnum(ORTHOGONAL_LAYOUT_STYLE, STYLE_ENUM, styleIndex);
121 
122 
123     PartitionLayouter.ComponentPartitionPlacer cpp = (PartitionLayouter.ComponentPartitionPlacer) layouter.getPartitionPlacer();
124 
125     int compStyleIndex = 0;
126     switch (cpp.getComponentLayouter().getStyle()) {
127       case ComponentLayouter.STYLE_ROWS:
128         compStyleIndex = 0;
129         break;
130       case ComponentLayouter.STYLE_PACKED_COMPACT_RECTANGLE:
131         compStyleIndex = 1;
132         break;
133     }
134     op.addEnum(PLACEMENT_STRATEGY, COMPONENT_STYLE_ENUM, compStyleIndex);
135 
136     op.addBool(USE_VIEW_ASPECT_RATIO, true);
137     op.addDouble(ASPECT_RATIO, layouter.getAspectRatio());
138     cm.setEnabledOnValueEquals(USE_VIEW_ASPECT_RATIO, Boolean.FALSE, ASPECT_RATIO);
139 
140     op.addInt(GRID, layouter.getGridSpacing())
141       .setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(1));
142 
143 
144     // ChannelInterEdgeRouter stuff
145     og = new OptionGroup();
146     og.setAttribute(OptionGroup.ATTRIBUTE_TITLE, INTER_EDGE_ROUTER);
147 
148     og.addItem(op.addEnum(PATH_FINDER, PATH_FINDER_ENUM, 1));
149 
150     PartitionLayouter.ChannelInterEdgeRouter cier = (PartitionLayouter.ChannelInterEdgeRouter)layouter.getInterEdgeRouter();
151     og.addItem(op.addBool(ROUTE_ALL_EDGES, !cier.isRouteInterEdgesOnly()));
152 
153     // ChannelEdgeRouter stuff
154     OrthogonalPatternEdgeRouter oper = new OrthogonalPatternEdgeRouter();
155     ChannelEdgeRouter.OrthogonalShortestPathPathFinder osppf = new ChannelEdgeRouter.OrthogonalShortestPathPathFinder();
156 
157     // path finding strategy properties
158     og.addItem(op.addDouble(BEND_COST, oper.getBendCost()));
159     og.addItem(op.addDouble(NODE_CROSSING_COST, oper.getNodeCrossingCost()));
160     og.addItem(op.addInt(MINIMUM_DISTANCE, osppf.getMinimumDistance()));
161     op.getItem(MINIMUM_DISTANCE)
162       .setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(4));
163     og.addItem(op.addDouble(EDGE_CROSSING_COST, osppf.getCrossingCost()));
164     og.addItem(op.addDouble(CENTER_TO_SPACE_RATIO, osppf.getCenterToSpaceRatio(), 0, 1));
165 
166     cm.setEnabledOnValueEquals(PATH_FINDER, ORTHOGONAL_PATTERN_PATH_FINDER, BEND_COST);
167     cm.setEnabledOnValueEquals(PATH_FINDER, ORTHOGONAL_PATTERN_PATH_FINDER, NODE_CROSSING_COST);
168     cm.setEnabledOnValueEquals(PATH_FINDER, ORTHOGONAL_SHORTESTPATH_PATH_FINDER, CENTER_TO_SPACE_RATIO);
169     return op;
170   }
171 
172 
173   private void prepare( CompactOrthogonalLayouter layouter ) {
174     PartitionLayouter.InterEdgeRouter ier = layouter.getInterEdgeRouter();
175     if (!(ier instanceof PartitionLayouter.ChannelInterEdgeRouter)) {
176       ier = new PartitionLayouter.ChannelInterEdgeRouter();
177       layouter.setInterEdgeRouter(ier);
178     }
179 
180     PartitionLayouter.PartitionPlacer pp = layouter.getPartitionPlacer();
181     if (!(pp instanceof PartitionLayouter.ComponentPartitionPlacer)) {
182       pp = new PartitionLayouter.ComponentPartitionPlacer();
183       layouter.setPartitionPlacer(pp);
184     }
185     Layouter cl = layouter.getCoreLayouter();
186     if (!(cl instanceof OrthogonalLayouter)) {
187       cl = new OrthogonalLayouter();
188       layouter.setCoreLayouter(cl);
189     }
190   }
191 
192   private void applyOptions( OptionHandler oh, PartitionLayouter.ChannelInterEdgeRouter router ) {
193     router.setRouteInterEdgesOnly(!oh.getBool(ROUTE_ALL_EDGES));
194     if (oh.getEnum(PATH_FINDER) == 0) {
195       OrthogonalPatternEdgeRouter oper = new OrthogonalPatternEdgeRouter();
196       oper.setMinimumDistance(oh.getInt(MINIMUM_DISTANCE));
197       oper.setEdgeCrossingCost(oh.getDouble(EDGE_CROSSING_COST));
198       oper.setNodeCrossingCost(oh.getDouble(NODE_CROSSING_COST));
199       oper.setBendCost(oh.getDouble(BEND_COST));
200       router.getChannelEdgeRouter().setPathFinderStrategy(oper);
201     } else {
202       ChannelEdgeRouter.OrthogonalShortestPathPathFinder osppf = new ChannelEdgeRouter.OrthogonalShortestPathPathFinder();
203       osppf.setMinimumDistance(oh.getInt(MINIMUM_DISTANCE));
204       osppf.setCrossingCost(oh.getDouble(EDGE_CROSSING_COST));
205       osppf.setCenterToSpaceRatio(oh.getDouble(CENTER_TO_SPACE_RATIO));
206       router.getChannelEdgeRouter().setPathFinderStrategy(osppf);
207     }
208   }
209 
210   private void applyOptions( OptionHandler oh, PartitionLayouter.ComponentPartitionPlacer placer ) {
211     if (STYLE_PACKED_COMPACT_RECTANGLE.equals(oh.get(PLACEMENT_STRATEGY))) {
212       placer.getComponentLayouter().setStyle(ComponentLayouter.STYLE_PACKED_COMPACT_RECTANGLE);
213     }
214     else if (STYLE_ROWS.equals(oh.get(PLACEMENT_STRATEGY))) {
215       placer.getComponentLayouter().setStyle(ComponentLayouter.STYLE_ROWS);
216     }
217   }
218 
219   private void applyOptions( OptionHandler oh, OrthogonalLayouter layouter ) {
220     switch (OptionHandler.getIndex(STYLE_ENUM, oh.getString(ORTHOGONAL_LAYOUT_STYLE))) {
221       default:
222       case 0:
223         layouter.setLayoutStyle(OrthogonalLayouter.NORMAL_STYLE);
224         break;
225       case 1:
226         layouter.setLayoutStyle(OrthogonalLayouter.NORMAL_TREE_STYLE);
227         break;
228       case 2:
229         layouter.setLayoutStyle(OrthogonalLayouter.FIXED_MIXED_STYLE);
230         break;
231       case 3:
232         layouter.setLayoutStyle(OrthogonalLayouter.FIXED_BOX_STYLE);
233         break;
234     }
235   }
236 
237   private void applyOptions( OptionHandler oh, CompactOrthogonalLayouter layouter ) {
238     layouter.setGridSpacing(oh.getInt(GRID));
239 
240     final double ar;
241     if (oh.getBool(USE_VIEW_ASPECT_RATIO) && getGraph2DView() != null) {
242       final Dimension dim = getGraph2DView().getSize();
243       ar = dim.getWidth()/dim.getHeight();
244     } else {
245       ar = oh.getDouble(ASPECT_RATIO);
246     }
247 
248     // this needs to be done as a final step since it will reconfigure
249     // layout stages which support aspect ratio accordingly
250     layouter.setAspectRatio(ar);
251   }
252 
253   public void mainrun() {
254     final OptionHandler op = getOptionHandler();
255 
256     CompactOrthogonalLayouter compactOrthogonal = new CompactOrthogonalLayouter();
257     prepare(compactOrthogonal);
258 
259     PartitionLayouter.ChannelInterEdgeRouter router = (PartitionLayouter.ChannelInterEdgeRouter) compactOrthogonal.getInterEdgeRouter();
260     applyOptions(op, router);
261 
262     PartitionLayouter.ComponentPartitionPlacer placer = (PartitionLayouter.ComponentPartitionPlacer) compactOrthogonal.getPartitionPlacer();
263     applyOptions(op, placer);
264 
265     OrthogonalLayouter orthogonalCore = (OrthogonalLayouter) compactOrthogonal.getCoreLayouter();
266     applyOptions(op, orthogonalCore);
267 
268     applyOptions(op, compactOrthogonal);
269 
270 
271     // launch layouter in buffered mode
272     GroupNodeHider groupNodeHider = new GroupNodeHider(compactOrthogonal);
273     groupNodeHider.setHidingEmptyGroupNodes(false);
274     launchLayouter(groupNodeHider);
275   }
276 }
277