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.base.Node;
20  import y.layout.CanonicMultiStageLayouter;
21  import y.layout.LayoutOrientation;
22  import y.layout.OrientationLayouter;
23  import y.layout.router.OrganicEdgeRouter;
24  import y.layout.router.OrthogonalEdgeRouter;
25  import y.layout.tree.ARTreeLayouter;
26  import y.layout.tree.BalloonLayouter;
27  import y.layout.tree.HVTreeLayouter;
28  import y.layout.tree.TreeLayouter;
29  import y.layout.tree.TreeReductionStage;
30  import y.option.ConstraintManager;
31  import y.option.DefaultEditorFactory;
32  import y.option.OptionHandler;
33  import y.option.OptionItem;
34  import y.util.DataProviderAdapter;
35  import y.view.Graph2D;
36  import y.view.Graph2DView;
37  
38  import java.awt.Dimension;
39  
40  /**
41   * This module represents an interactive configurator and launcher for {@link y.layout.tree.TreeLayouter}, {@link
42   * y.layout.tree.BalloonLayouter}, {@link y.layout.tree.ARTreeLayouter} and {@link y.layout.tree.HVTreeLayouter}.
43   *
44   *
45   * @see <a href="http://docs.yworks.com/yfiles/doc/developers-guide/tree_layouter.html#tree_layouter">Section Tree Layout</a> in the yFiles for Java Developer's Guide
46   */
47  public class TreeLayoutModule extends LayoutModule {
48  
49    private static final String LAYOUT_STYLE = "LAYOUT_STYLE";
50    private static final String PREFERRED_CHILD_WEDGE = "PREFERRED_CHILD_WEDGE";
51    private static final String DIRECTED_ROOT = "DIRECTED_ROOT";
52    private static final String LEFT_TO_RIGHT = "LEFT_TO_RIGHT";
53    private static final String ALLOW_OVERLAPS = "ALLOW_OVERLAPS";
54    private static final String GENERAL = "GENERAL";
55  
56    private static final String ALLOW_NON_TREE_EDGES = "ALLOW_NON_TREES";
57    private static final String ROUTING_STYLE_FOR_NON_TREE_EDGES = "ROUTING_STYLE_FOR_NON_TREE_EDGES";
58    private static final String ROUTE_ORGANIC = "ROUTE_ORGANIC";
59    private static final String ROUTE_ORTHOGONAL = "ROUTE_ORTHOGONAL";
60    private static final String ROUTE_STRAIGHTLINE = "ROUTE_STRAIGHTLINE";
61  
62    private static final String COMPACTNESS_FACTOR = "COMPACTNESS_FACTOR";
63    private static final String MINIMAL_NODE_DISTANCE = "MINIMAL_NODE_DISTANCE";
64    private static final String ACT_ON_SELECTION_ONLY = "ACT_ON_SELECTION_ONLY";
65    private static final String BOTTOM_TO_TOP = "BOTTOM_TO_TOP";
66    private static final String BALLOON = "BALLOON";
67    private static final String MINIMAL_LAYER_DISTANCE = "MINIMAL_LAYER_DISTANCE";
68    private static final String ORIENTATION = "ORIENTATION";
69    private static final String PREFERRED_ROOT_WEDGE = "PREFERRED_ROOT_WEDGE";
70    private static final String RIGHT_TO_LEFT = "RIGHT_TO_LEFT";
71    private static final String HV = "HV";
72    private static final String VERTICAL_SPACE = "VERTICAL_SPACE";
73    private static final String AR = "AR";
74    private static final String HORIZONTAL_SPACE = "HORIZONTAL_SPACE";
75    private static final String TREE = "TREE";
76    private static final String TOP_TO_BOTTOM = "TOP_TO_BOTTOM";
77    private static final String MINIMAL_EDGE_LENGTH = "MINIMAL_EDGE_LENGTH";
78    private static final String ROOT_NODE_POLICY = "ROOT_NODE_POLICY";
79    private static final String CENTER_ROOT = "CENTER_ROOT";
80    private static final String WEIGHTED_CENTER_ROOT = "WEIGHTED_CENTER_ROOT";
81  
82    private static final String BEND_DISTANCE = "BEND_DISTANCE";
83    private static final String ASPECT_RATIO = "ASPECT_RATIO";
84    private static final String USE_VIEW_ASPECT_RATIO = "USE_VIEW_ASPECT_RATIO";
85  
86    private static final String DIRECTED = "DIRECTED";
87    private static final String ORTHOGONAL_EDGE_ROUTING = "ORTHOGONAL_EDGE_ROUTING";
88  
89    private static final String CHILD_PLACEMENT_POLICY = "CHILD_PLACEMENT_POLICY";
90    private static final String SIBLINGS_ON_SAME_LAYER = "SIBLINGS_ON_SAME_LAYER";
91    private static final String ALL_LEAVES_ON_SAME_LAYER = "ALL_LEAVES_ON_SAME_LAYER";
92    private static final String LEAVES_STACKED = "LEAVES_STACKED";
93    private static final String LEAVES_STACKED_LEFT = "LEAVES_STACKED_LEFT";
94    private static final String LEAVES_STACKED_RIGHT = "LEAVES_STACKED_RIGHT";
95    private static final String[] enumLeafLayoutPolicy = {
96            SIBLINGS_ON_SAME_LAYER,
97            ALL_LEAVES_ON_SAME_LAYER,
98            LEAVES_STACKED,
99            LEAVES_STACKED_LEFT,
100           LEAVES_STACKED_RIGHT,
101   };
102 
103   private static final String ENFORCE_GLOBAL_LAYERING = "ENFORCE_GLOBAL_LAYERING";
104 
105   private static final String INTEGRATED_EDGE_LABELING = "INTEGRATED_EDGE_LABELING";
106   private static final String INTEGRATED_NODE_LABELING = "INTEGRATED_NODE_LABELING";
107 
108   private static final String VERTICAL_ALIGNMENT = "VERTICAL_ALIGNMENT";
109   private static final String BUS_ALIGNMENT = "BUS_ALIGNMENT";
110 
111   private static final String BALLOON_FROM_SKETCH = "FROM_SKETCH";
112 
113   private static final String[] enumRoute = {ROUTE_ORGANIC, ROUTE_ORTHOGONAL, ROUTE_STRAIGHTLINE};
114 
115   private static final String[] enumStyle = {DIRECTED, BALLOON, HV, AR};
116   private static final String[] enumOrient = {TOP_TO_BOTTOM, LEFT_TO_RIGHT,
117       BOTTOM_TO_TOP, RIGHT_TO_LEFT};
118   private static final String[] enumRoot = {DIRECTED_ROOT, CENTER_ROOT, WEIGHTED_CENTER_ROOT};
119 
120   private static final String PORT_STYLE = "PORT_STYLE";
121   private static final String NODE_CENTER_PORTS = "NODE_CENTER";
122   private static final String BORDER_CENTER_PORTS = "BORDER_CENTER";
123   private static final String BORDER_DISTRIBUTED_PORTS = "BORDER_DISTRIBUTED";
124 
125   private static final String[] enumPortStyle = {
126       NODE_CENTER_PORTS,
127       BORDER_CENTER_PORTS,
128       BORDER_DISTRIBUTED_PORTS
129   };
130 
131 
132   public TreeLayoutModule() {
133     super(TREE, "yFiles Layout Team", "A layouter for tree structures");
134     setPortIntersectionCalculatorEnabled(true);
135   }
136 
137 
138   /** module support */
139   public OptionHandler createOptionHandler() {
140     OptionHandler op = new OptionHandler(getModuleName());
141 
142     op.useSection(GENERAL);
143     op.addEnum(LAYOUT_STYLE, enumStyle, 0);
144 
145     op.addBool(ALLOW_NON_TREE_EDGES, false);
146     op.addEnum(ROUTING_STYLE_FOR_NON_TREE_EDGES, enumRoute, 0);
147     ConstraintManager cm = new ConstraintManager(op);
148     cm.setEnabledOnValueEquals(ALLOW_NON_TREE_EDGES, Boolean.TRUE, ROUTING_STYLE_FOR_NON_TREE_EDGES);
149 
150     op.addBool(ACT_ON_SELECTION_ONLY, false);
151 
152     op.useSection(DIRECTED);
153     TreeLayouter treeLayouter = new TreeLayouter();
154     op.addInt(MINIMAL_NODE_DISTANCE,
155         (int) treeLayouter.getMinimalNodeDistance(), 1, 100);
156     op.addInt(MINIMAL_LAYER_DISTANCE,
157         (int) treeLayouter.getMinimalLayerDistance(), 10, 300);
158     op.addEnum(ORIENTATION, enumOrient, 0);
159     op.addEnum(PORT_STYLE, enumPortStyle, 0);
160 
161     op.addBool(INTEGRATED_NODE_LABELING, false);
162     op.addBool(INTEGRATED_EDGE_LABELING, false);
163 
164     OptionItem edgeRoutingOption = op.addBool(ORTHOGONAL_EDGE_ROUTING, false);
165     OptionItem busAlignmentOption = op.addDouble(BUS_ALIGNMENT, 0.5, 0, 1);
166     OptionItem optionItem = op.addDouble(VERTICAL_ALIGNMENT, 0.5, 0, 1);
167 
168     op.addEnum(CHILD_PLACEMENT_POLICY, enumLeafLayoutPolicy, 0);
169     op.addBool(ENFORCE_GLOBAL_LAYERING, false);
170 
171     busAlignmentOption.setAttribute(DefaultEditorFactory.ATTRIBUTE_MIN_VALUE_LABEL_TEXT, "TOP");
172     busAlignmentOption.setAttribute(DefaultEditorFactory.ATTRIBUTE_MAX_VALUE_LABEL_TEXT, "BOTTOM");
173     new ConstraintManager(op).setEnabledOnValueEquals(edgeRoutingOption, Boolean.TRUE, busAlignmentOption);
174 
175     optionItem.setAttribute(DefaultEditorFactory.ATTRIBUTE_MIN_VALUE_LABEL_TEXT, "TOP");
176     optionItem.setAttribute(DefaultEditorFactory.ATTRIBUTE_MAX_VALUE_LABEL_TEXT, "BOTTOM");
177 
178     op.useSection(BALLOON);
179     BalloonLayouter balloonLayouter = new BalloonLayouter();
180     op.addEnum(ROOT_NODE_POLICY, enumRoot, 0);
181     op.addInt(PREFERRED_CHILD_WEDGE, balloonLayouter.getPreferredChildWedge(), 1, 359);
182     op.addInt(PREFERRED_ROOT_WEDGE, balloonLayouter.getPreferredRootWedge(), 1, 360);
183     op.addInt(MINIMAL_EDGE_LENGTH, balloonLayouter.getMinimalEdgeLength(), 10, 400);
184     op.addDouble(COMPACTNESS_FACTOR, balloonLayouter.getCompactnessFactor(), 0.1, 0.9);
185     op.addBool(ALLOW_OVERLAPS, balloonLayouter.getAllowOverlaps());
186     op.addBool(BALLOON_FROM_SKETCH, balloonLayouter.isFromSketchModeEnabled());
187 
188     op.useSection(HV);
189     HVTreeLayouter hv = new HVTreeLayouter();
190     op.addInt(HORIZONTAL_SPACE, (int) hv.getHorizontalSpace());
191     op.addInt(VERTICAL_SPACE, (int) hv.getVerticalSpace());
192 
193     op.useSection(AR);
194     ARTreeLayouter ar = new ARTreeLayouter();
195     op.addInt(HORIZONTAL_SPACE, (int) ar.getHorizontalSpace());
196     op.addInt(VERTICAL_SPACE, (int) ar.getVerticalSpace());
197     op.addInt(BEND_DISTANCE, (int) ar.getBendDistance());
198     op.addBool(USE_VIEW_ASPECT_RATIO, true);
199     op.addDouble(ASPECT_RATIO, ar.getAspectRatio());
200     cm.setEnabledOnValueEquals(USE_VIEW_ASPECT_RATIO, Boolean.FALSE, ASPECT_RATIO);
201 
202     return op;
203   }
204 
205 
206 
207 
208   public void mainrun() {
209     CanonicMultiStageLayouter layouter = null;
210     Graph2D graph = getGraph2D();
211 
212     OptionHandler op = getOptionHandler();
213     String style = op.getString(LAYOUT_STYLE);
214 
215     if (style.equals(DIRECTED)) {
216       TreeLayouter tree = new TreeLayouter();
217 
218       tree.setMinimalNodeDistance(op.getInt(DIRECTED, MINIMAL_NODE_DISTANCE));
219       tree.setMinimalLayerDistance(op.getInt(DIRECTED, MINIMAL_LAYER_DISTANCE));
220 
221       OrientationLayouter ol = (OrientationLayouter) tree.getOrientationLayouter();
222       if (op.getString(ORIENTATION).equals(TOP_TO_BOTTOM)) {
223         ol.setOrientation(LayoutOrientation.TOP_TO_BOTTOM);
224       } else if (op.getString(ORIENTATION).equals(BOTTOM_TO_TOP)) {
225         ol.setOrientation(LayoutOrientation.BOTTOM_TO_TOP);
226       } else if (op.getString(ORIENTATION).equals(RIGHT_TO_LEFT)) {
227         ol.setOrientation(LayoutOrientation.RIGHT_TO_LEFT);
228       } else {
229         ol.setOrientation(LayoutOrientation.LEFT_TO_RIGHT);
230       }
231 
232       if (op.getBool(ORTHOGONAL_EDGE_ROUTING)) {
233         tree.setLayoutStyle(TreeLayouter.ORTHOGONAL_STYLE);
234       } else {
235         tree.setLayoutStyle(TreeLayouter.PLAIN_STYLE);
236       }
237 
238       final String leafLayotPolicyStr = op.getString(CHILD_PLACEMENT_POLICY);
239       if (SIBLINGS_ON_SAME_LAYER.equals(leafLayotPolicyStr)) {
240         tree.setChildPlacementPolicy(TreeLayouter.CHILD_PLACEMENT_POLICY_SIBLINGS_ON_SAME_LAYER);
241       } else if (LEAVES_STACKED_LEFT.equals(leafLayotPolicyStr)) {
242         tree.setChildPlacementPolicy(TreeLayouter.CHILD_PLACEMENT_POLICY_LEAVES_STACKED_LEFT);
243       } else if (LEAVES_STACKED_RIGHT.equals(leafLayotPolicyStr)) {
244         tree.setChildPlacementPolicy(TreeLayouter.CHILD_PLACEMENT_POLICY_LEAVES_STACKED_RIGHT);
245       } else if (LEAVES_STACKED.equals(leafLayotPolicyStr)) {
246         tree.setChildPlacementPolicy(TreeLayouter.CHILD_PLACEMENT_POLICY_LEAVES_STACKED);
247       } else if (ALL_LEAVES_ON_SAME_LAYER.equals(leafLayotPolicyStr)) {
248         tree.setChildPlacementPolicy(TreeLayouter.CHILD_PLACEMENT_POLICY_ALL_LEAVES_ON_SAME_LAYER);
249       }
250 
251       if (op.getBool(ENFORCE_GLOBAL_LAYERING)) {
252         tree.setEnforceGlobalLayering(true);
253       } else {
254         tree.setEnforceGlobalLayering(false);
255       }
256 
257       if (op.getString(PORT_STYLE).equals(NODE_CENTER_PORTS)) {
258         tree.setPortStyle(TreeLayouter.NODE_CENTER_PORTS);
259       } else if (op.getString(PORT_STYLE).equals(BORDER_CENTER_PORTS)) {
260         tree.setPortStyle(TreeLayouter.BORDER_CENTER_PORTS);
261       } else if (op.getString(PORT_STYLE).equals(BORDER_DISTRIBUTED_PORTS)) {
262         tree.setPortStyle(TreeLayouter.BORDER_DISTRIBUTED_PORTS);
263       }
264 
265       tree.setIntegratedNodeLabelingEnabled(op.getBool(INTEGRATED_NODE_LABELING));
266       tree.setIntegratedEdgeLabelingEnabled(op.getBool(INTEGRATED_EDGE_LABELING));
267 
268       tree.setVerticalAlignment(op.getDouble(VERTICAL_ALIGNMENT));
269       tree.setBusAlignment(op.getDouble(BUS_ALIGNMENT));
270 
271       layouter = tree;
272     } else if (style.equals(BALLOON)) {
273       BalloonLayouter balloon = new BalloonLayouter();
274 
275       if (op.get(ROOT_NODE_POLICY).equals(enumRoot[0])) {
276         balloon.setRootNodePolicy(BalloonLayouter.DIRECTED_ROOT);
277       } else if (op.get(ROOT_NODE_POLICY).equals(enumRoot[1])) {
278         balloon.setRootNodePolicy(BalloonLayouter.CENTER_ROOT);
279       } else {
280         balloon.setRootNodePolicy(BalloonLayouter.WEIGHTED_CENTER_ROOT);
281       }
282 
283       balloon.setPreferredChildWedge(op.getInt(PREFERRED_CHILD_WEDGE));
284       balloon.setPreferredRootWedge(op.getInt(PREFERRED_ROOT_WEDGE));
285       balloon.setMinimalEdgeLength(op.getInt(BALLOON, MINIMAL_EDGE_LENGTH));
286       balloon.setCompactnessFactor(op.getDouble(COMPACTNESS_FACTOR));
287       balloon.setAllowOverlaps(op.getBool(ALLOW_OVERLAPS));
288       balloon.setFromSketchModeEnabled(op.getBool(BALLOON_FROM_SKETCH));
289       layouter = balloon;
290     } else if (style.equals(HV)) {
291       HVTreeLayouter hv = new HVTreeLayouter();
292       DataProviderAdapter dp = new DataProviderAdapter() {
293         public Object get(Object node) {
294           if (getGraph2D().isSelected((Node) node)) {
295             return HVTreeLayouter.VERTICAL_SUBTREE;
296           } else {
297             return HVTreeLayouter.HORIZONTAL_SUBTREE;
298           }
299         }
300       };
301 
302       graph.addDataProvider(HVTreeLayouter.SUBTREE_ORIENTATION, dp);
303 
304       hv.setHorizontalSpace(op.getInt(HV, HORIZONTAL_SPACE));
305       hv.setVerticalSpace(op.getInt(HV, VERTICAL_SPACE));
306 
307       layouter = hv;
308     } else if (style.equals(AR)) {
309       ARTreeLayouter ar = new ARTreeLayouter();
310 
311       DataProviderAdapter dp = new DataProviderAdapter() {
312         public Object get(Object node) {
313           if (getGraph2D().isSelected((Node) node)) {
314             return ARTreeLayouter.ROUTING_HORIZONTAL;
315           } else {
316             return ARTreeLayouter.ROUTING_VERTICAL;
317           }
318         }
319       };
320 
321       if (op.getBool(USE_VIEW_ASPECT_RATIO)) {
322         Graph2DView view = getGraph2DView();
323         if (view != null) {
324           Dimension dim = view.getSize();
325           ar.setAspectRatio(dim.getWidth() / (double) dim.getHeight());
326         } else {
327           ar.setAspectRatio(1);
328         }
329       } else {
330         ar.setAspectRatio(op.getDouble(ASPECT_RATIO));
331       }
332       ar.setHorizontalSpace(op.getInt(AR, HORIZONTAL_SPACE));
333       ar.setVerticalSpace(op.getInt(AR, VERTICAL_SPACE));
334       ar.setBendDistance(op.getInt(AR, BEND_DISTANCE));
335 
336       graph.addDataProvider(ARTreeLayouter.ROUTING_POLICY, dp);
337       layouter = ar;
338     }
339 
340     layouter.setSubgraphLayouterEnabled(op.getBool(ACT_ON_SELECTION_ONLY));
341 
342     //configure tree reduction state and non-tree edge routing
343     TreeReductionStage trs = null;
344     if (op.getBool(ALLOW_NON_TREE_EDGES)) {
345       trs = new TreeReductionStage();
346       layouter.appendStage(trs);
347       if (ROUTE_ORGANIC.equals(op.get(ROUTING_STYLE_FOR_NON_TREE_EDGES))) {
348         OrganicEdgeRouter organic = new OrganicEdgeRouter();
349         trs.setNonTreeEdgeRouter(organic);
350         trs.setNonTreeEdgeSelectionKey(OrganicEdgeRouter.ROUTE_EDGE_DPKEY);
351       }
352       if (ROUTE_ORTHOGONAL.equals(op.get(ROUTING_STYLE_FOR_NON_TREE_EDGES))) {
353         OrthogonalEdgeRouter orthogonal = new OrthogonalEdgeRouter();
354         orthogonal.setCrossingCost(1.0);
355         orthogonal.setReroutingEnabled(true);
356         orthogonal.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_SELECTED_EDGES);
357 
358         trs.setNonTreeEdgeSelectionKey(orthogonal.getSelectedEdgesDpKey());
359         trs.setNonTreeEdgeRouter(orthogonal);
360       }
361       if (ROUTE_STRAIGHTLINE.equals(op.get(ROUTING_STYLE_FOR_NON_TREE_EDGES))) {
362         trs.setNonTreeEdgeRouter(trs.createStraightlineRouter());
363       }
364     }
365 
366 
367     try {
368       launchLayouter(layouter);
369     } finally {
370       // make sure the DataProviders will always be unregistered
371       graph.removeDataProvider(ARTreeLayouter.ROUTING_POLICY);
372       graph.removeDataProvider(HVTreeLayouter.SUBTREE_ORIENTATION);
373       if (trs != null) {
374         layouter.removeStage(trs);
375       }
376     }
377   }
378 }