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