1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.6. 
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-2008 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
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  /**
40   * This module represents an interactive configurator and launcher for
41   * {@link y.layout.tree.TreeLayouter}, {@link y.layout.tree.BalloonLayouter}, {@link y.layout.tree.ARTreeLayouter} and {@link y.layout.tree.HVTreeLayouter}.
42   *
43   */
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   /** module support */
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     //configure tree reduction state and non-tree edge routing
308     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     // initialize potential grouping information
332     GroupLayoutConfigurator glc = new GroupLayoutConfigurator(graph);
333 
334     try {
335       // register grouping relevant DataProviders
336       glc.prepareAll();
337       // launch layouter in buffered mode
338       launchLayouter(layouter);
339     } finally {
340       // make sure the DataProviders will always be unregistered
341       glc.restoreAll();
342       graph.removeDataProvider(ARTreeLayouter.ROUTING_POLICY);
343       graph.removeDataProvider(HVTreeLayouter.SUBTREE_ORIENTATION);
344       if(trs != null) layouter.removeStage(trs);
345     }
346   }
347 }