1
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
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
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 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 graph.removeDataProvider(ARTreeLayouter.ROUTING_POLICY);
372 graph.removeDataProvider(HVTreeLayouter.SUBTREE_ORIENTATION);
373 if (trs != null) {
374 layouter.removeStage(trs);
375 }
376 }
377 }
378 }