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.Edge;
20  import y.base.EdgeCursor;
21  import y.layout.LabelLayoutConstants;
22  import y.layout.LabelRanking;
23  import y.layout.LayoutStage;
24  import y.layout.Layouter;
25  import y.layout.grouping.FixedGroupLayoutStage;
26  import y.layout.labeling.GreedyMISLabeling;
27  import y.layout.orthogonal.EdgeLayoutDescriptor;
28  import y.layout.orthogonal.OrthogonalGroupLayouter;
29  import y.layout.orthogonal.OrthogonalLayouter;
30  import y.option.ConstraintManager;
31  import y.option.IntOptionItem;
32  import y.option.OptionHandler;
33  import y.view.EdgeLabel;
34  import y.view.EdgeRealizer;
35  import y.view.Graph2D;
36  import y.view.hierarchy.HierarchyManager;
37  
38  
39  /**
40   * This module represents an interactive configurator and launcher for
41   * {@link y.layout.orthogonal.OrthogonalLayouter}
42   * and {@link y.layout.orthogonal.OrthogonalGroupLayouter} respectively.
43   *
44   *
45   * @see <a href="http://docs.yworks.com/yfiles/doc/developers-guide/layout_advanced_features.html#layout_advanced_features">Section Advanced Layout Concepts</a> in the yFiles for Java Developer's Guide
46   * @see <a href="http://docs.yworks.com/yfiles/doc/developers-guide/layout_stages.html#layout_stages">Section Layout Stages</a> in the yFiles for Java Developer's Guide
47   * @see <a href="http://docs.yworks.com/yfiles/doc/developers-guide/orthogonal_group_layouter.html#orthogonal_group_layouter">Section Orthogonal Layout of Grouped Graphs</a> in the yFiles for Java Developer's Guide
48   * @see <a href="http://docs.yworks.com/yfiles/doc/developers-guide/orthogonal_layouter.html#orthogonal_layouter">Section Orthogonal Layout</a> in the yFiles for Java Developer's Guide
49   */
50  public class OrthogonalLayoutModule extends LayoutModule
51  {
52    private static final String ORTHOGONAL = "ORTHOGONAL_LAYOUTER";
53    private static final String GROUPING      = "GROUPING";
54    private static final String GROUP_POLICY  = "GROUP_LAYOUT_POLICY";
55    private static final String IGNORE_GROUPS = "IGNORE_GROUPS";
56    private static final String LAYOUT_GROUPS = "LAYOUT_GROUPS";
57    private static final String FIX_GROUPS    = "FIX_GROUPS";
58    private static final String GROUP_LAYOUT_QUALITY = "GROUP_LAYOUT_QUALITY";
59  
60    private static final String LENGTH_REDUCTION = "LENGTH_REDUCTION";
61    private static final String STYLE = "STYLE";
62    private static final String USE_RANDOMIZATION = "USE_RANDOMIZATION";
63    private static final String USE_FACE_MAXIMIZATION = "USE_FACE_MAXIMIZATION";
64    private static final String USE_EXISTING_DRAWING_AS_SKETCH = "USE_EXISTING_DRAWING_AS_SKETCH";
65    private static final String CROSSING_POSTPROCESSING = "CROSSING_POSTPROCESSING";
66    private static final String PERCEIVED_BENDS_POSTPROCESSING = "PERCEIVED_BENDS_POSTPROCESSING";
67    private static final String ROUTE_MULTI_EDGES_IN_PARALLEL = "ROUTE_MULTI_EDGES_IN_PARALLEL";
68    private static final String GRID = "GRID";
69    private static final String NORMAL = "NORMAL";
70    private static final String NORMAL_TREE = "NORMAL_TREE";
71    private static final String UNIFORM_NODES = "UNIFORM_NODES";
72    private static final String BOX_NODES = "BOX_NODES";
73    private static final String MIXED = "MIXED";
74    private static final String FIXED_MIXED = "FIXED_MIXED";
75    private static final String FIXED_BOX_NODES = "FIXED_BOX_NODES";
76    private static final String MINIMUM_FIRST_SEGMENT_LENGTH = "MINIMUM_FIRST_SEGMENT_LENGTH";
77    private static final String MINIMUM_LAST_SEGMENT_LENGTH = "MINIMUM_LAST_SEGMENT_LENGTH";
78    private static final String MINIMUM_SEGMENT_LENGTH = "MINIMUM_SEGMENT_LENGTH";
79  
80    private static final String LAYOUT = "LAYOUT";
81    private static final String EDGE_LABEL_MODEL = "EDGE_LABEL_MODEL";
82    private static final String EDGE_LABELING = "EDGE_LABELING";
83    private static final String LABELING = "LABELING";
84    private static final String GENERIC = "GENERIC";
85    private static final String NONE = "NONE";
86    private static final String INTEGRATED = "INTEGRATED";
87    private static final String FREE = "FREE";
88    private static final String SIDE_SLIDER = "SIDE_SLIDER";
89    private static final String CENTER_SLIDER = "CENTER_SLIDER";
90    private static final String AS_IS = "AS_IS";
91    private static final String BEST = "BEST";
92    private static final String CONSIDER_NODE_LABELS = "CONSIDER_NODE_LABELS";
93  
94    private final String[] styleEnum = {NORMAL, NORMAL_TREE, UNIFORM_NODES, BOX_NODES, MIXED, FIXED_MIXED, FIXED_BOX_NODES};
95  
96    private static final String[] edgeLabeling = {
97      NONE,
98      INTEGRATED,
99      GENERIC
100   };
101 
102   private static final String[] edgeLabelModel = {
103     BEST,
104     AS_IS,
105     CENTER_SLIDER,
106     SIDE_SLIDER,
107     FREE,
108   };
109 
110   public OrthogonalLayoutModule()
111   {
112     super (ORTHOGONAL,"yFiles Layout Team",
113            "Orthogonal Layouter");
114     setPortIntersectionCalculatorEnabled(true);
115   }
116 
117   public OptionHandler createOptionHandler()
118   {
119     OptionHandler op = new OptionHandler(getModuleName());
120     
121     ConstraintManager cm = new ConstraintManager( op );
122  
123     
124     op.useSection(LAYOUT);
125     op.addEnum(STYLE, styleEnum, 0);
126     op.addInt(GRID,25)
127       .setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(1));
128     op.addBool(LENGTH_REDUCTION, true);
129     op.addBool(USE_EXISTING_DRAWING_AS_SKETCH, false);
130     op.addBool(CROSSING_POSTPROCESSING, true);
131     op.addBool(PERCEIVED_BENDS_POSTPROCESSING, true);
132     op.addBool(USE_RANDOMIZATION, true);
133     op.addBool(USE_FACE_MAXIMIZATION,false);
134     op.addBool(ROUTE_MULTI_EDGES_IN_PARALLEL, false);
135     op.addDouble(MINIMUM_FIRST_SEGMENT_LENGTH, 10);
136     op.addDouble(MINIMUM_LAST_SEGMENT_LENGTH, 10);
137     op.addDouble(MINIMUM_SEGMENT_LENGTH, 10);
138     op.useSection(LABELING);
139     op.addEnum(EDGE_LABELING, edgeLabeling, 0);
140     op.addEnum(EDGE_LABEL_MODEL, edgeLabelModel, 0);
141     op.addBool(CONSIDER_NODE_LABELS, false);
142     
143     cm.setEnabledOnValueEquals( EDGE_LABELING, NONE, EDGE_LABEL_MODEL, true);    
144 
145     ConstraintManager.Condition c = cm.createConditionValueEquals(USE_EXISTING_DRAWING_AS_SKETCH, Boolean.FALSE);
146     cm.setEnabledOnCondition(c, op.getItem(CROSSING_POSTPROCESSING));
147     cm.setEnabledOnCondition(c, op.getItem(PERCEIVED_BENDS_POSTPROCESSING));
148     cm.setEnabledOnCondition(c, op.getItem(STYLE));
149     cm.setEnabledOnCondition(c, op.getItem(USE_RANDOMIZATION));
150 
151     
152     op.useSection(GROUPING);
153     String[] gEnum = { LAYOUT_GROUPS, FIX_GROUPS, IGNORE_GROUPS };
154     op.addEnum(GROUP_POLICY, gEnum, 0);
155     op.addDouble(GROUP_LAYOUT_QUALITY, 1.0, 0.0, 1.0);
156 
157     cm.setEnabledOnValueEquals( GROUP_POLICY, LAYOUT_GROUPS, GROUP_LAYOUT_QUALITY);
158     
159     return op;
160   }
161 
162   public void mainrun()
163   {
164     OptionHandler op = getOptionHandler();
165     
166     OrthogonalLayouter orthogonal = new OrthogonalLayouter();
167     
168     ////////////////////////////////////////////////////////////////////////////
169     // Layout
170     ////////////////////////////////////////////////////////////////////////////
171 
172     switch (OptionHandler.getIndex(styleEnum, op.getString(STYLE))){
173       default:
174       case 0:
175         orthogonal.setLayoutStyle(OrthogonalLayouter.NORMAL_STYLE);
176         break;
177       case 1:
178         orthogonal.setLayoutStyle(OrthogonalLayouter.NORMAL_TREE_STYLE);
179         break;
180       case 2:
181         orthogonal.setLayoutStyle(OrthogonalLayouter.UNIFORM_STYLE);
182         break;
183       case 3:
184         orthogonal.setLayoutStyle(OrthogonalLayouter.BOX_STYLE);
185         break;
186       case 4:
187         orthogonal.setLayoutStyle(OrthogonalLayouter.MIXED_STYLE);
188         break;
189       case 5:
190         orthogonal.setLayoutStyle(OrthogonalLayouter.FIXED_MIXED_STYLE);
191         break;
192       case 6:
193         orthogonal.setLayoutStyle(OrthogonalLayouter.FIXED_BOX_STYLE);
194         break;
195     }
196 
197     final EdgeLayoutDescriptor layoutDescriptor = orthogonal.getEdgeLayoutDescriptor();
198     layoutDescriptor.setMinimumFirstSegmentLength(op.getDouble(MINIMUM_FIRST_SEGMENT_LENGTH));
199     layoutDescriptor.setMinimumLastSegmentLength(op.getDouble(MINIMUM_LAST_SEGMENT_LENGTH));
200     layoutDescriptor.setMinimumSegmentLength(op.getDouble(MINIMUM_SEGMENT_LENGTH));
201 
202     orthogonal.setGrid(op.getInt(GRID));
203     orthogonal.setUseLengthReduction(
204       op.getBool(LENGTH_REDUCTION));
205     orthogonal.setUseCrossingPostprocessing(
206       op.getBool(CROSSING_POSTPROCESSING));
207     orthogonal.setPerceivedBendsOptimizationEnabled(
208       op.getBool(PERCEIVED_BENDS_POSTPROCESSING));
209     orthogonal.setUseRandomization(
210       op.getBool(USE_RANDOMIZATION));
211     orthogonal.setUseFaceMaximization(
212       op.getBool(USE_FACE_MAXIMIZATION));
213     orthogonal.setUseSketchDrawing(op.getBool(USE_EXISTING_DRAWING_AS_SKETCH));
214     orthogonal.setParallelEdgeLayouterEnabled(op.getBool(ROUTE_MULTI_EDGES_IN_PARALLEL));
215 
216     Graph2D graph = getGraph2D();    
217 
218     ////////////////////////////////////////////////////////////////////////////
219     // Labels
220     ////////////////////////////////////////////////////////////////////////////
221 
222     boolean normalStyle = (orthogonal.getLayoutStyle() == OrthogonalLayouter.NORMAL_STYLE);
223     String el = op.getString(EDGE_LABELING);
224     orthogonal.setIntegratedEdgeLabelingEnabled(el.equals(INTEGRATED) && normalStyle);
225     orthogonal.setConsiderNodeLabelsEnabled(op.getBool(CONSIDER_NODE_LABELS) && normalStyle);
226     if (el.equals(GENERIC) || (el.equals(INTEGRATED) && normalStyle)) {
227       setupEdgeLabelModel(el, op.getString(EDGE_LABEL_MODEL));               
228     } else if (!op.getBool(CONSIDER_NODE_LABELS) || !normalStyle) {
229       orthogonal.setLabelLayouterEnabled(false);
230     }    
231 
232     Layouter layouter = orthogonal;
233     LayoutStage preStage = null;
234     
235     if(HierarchyManager.containsGroupNodes(graph) && !op.get(GROUP_POLICY).equals(IGNORE_GROUPS))
236     {
237       FixedGroupLayoutStage fgl = null;
238       if(op.get(GROUP_POLICY).equals(FIX_GROUPS))
239       {
240         fgl = new FixedGroupLayoutStage();
241         fgl.setInterEdgeRoutingStyle(FixedGroupLayoutStage.ROUTING_STYLE_ORTHOGONAL);
242         orthogonal.prependStage(fgl);
243         preStage = fgl;
244       } else {
245         OrthogonalGroupLayouter ogl = new OrthogonalGroupLayouter();
246         final EdgeLayoutDescriptor descriptor = ogl.getEdgeLayoutDescriptor();
247         descriptor.setMinimumFirstSegmentLength(op.getDouble(MINIMUM_FIRST_SEGMENT_LENGTH));
248         descriptor.setMinimumLastSegmentLength(op.getDouble(MINIMUM_LAST_SEGMENT_LENGTH));
249         descriptor.setMinimumSegmentLength(op.getDouble(MINIMUM_SEGMENT_LENGTH));
250         ogl.setIntegratedEdgeLabelingEnabled(orthogonal.isIntegratedEdgeLabelingEnabled());
251         ogl.setConsiderNodeLabelsEnabled(orthogonal.isConsiderNodeLabelsEnabled());
252         ogl.setLabelLayouterEnabled(orthogonal.isLabelLayouterEnabled());        
253             
254         ogl.setGrid(op.getInt(GRID));
255         ogl.setLayoutQuality(op.getDouble(GROUP_LAYOUT_QUALITY));
256         ogl.setParallelEdgeLayouterEnabled(op.getBool(ROUTE_MULTI_EDGES_IN_PARALLEL));
257         layouter = ogl;
258       }
259 
260       try {
261         launchLayouter(layouter);
262       } finally {
263         if(preStage != null) {
264           orthogonal.removeStage(preStage);
265         }
266       }
267     }
268     else
269     {
270       launchLayouter(layouter);
271     }
272     if (el.equals(GENERIC)) {
273       GreedyMISLabeling la = new GreedyMISLabeling();
274       la.setPlaceNodeLabels(false);
275       la.setPlaceEdgeLabels(true);
276       la.setProfitModel(new LabelRanking());
277       la.doLayout(graph);
278     }    
279     getGraph2D().updateViews();
280   }
281 
282   void setupEdgeLabelModel(String edgeLabeling, String edgeLabelModel) {
283     if(edgeLabeling.equals(NONE) || edgeLabelModel.equals(AS_IS)) {
284       return; //nothing to do
285     }
286     
287     byte model = EdgeLabel.SIDE_SLIDER;
288     if (edgeLabelModel.equals(CENTER_SLIDER)) {
289       model = EdgeLabel.CENTER_SLIDER;
290     } else if (edgeLabelModel.equals(FREE) || edgeLabelModel.equals(BEST)) {
291       model = EdgeLabel.FREE;
292     }
293 //    else if (edgeLabelModel.equals(AUTO_ROTATION_ORTHOGONAL_SLIDER)) {
294 //      model = EdgeLabel.AUTO_ROTATED_ORTHOGONAL_SLIDER;
295 //    }
296 
297     Graph2D graph = getGraph2D();
298     for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
299       Edge e = ec.edge();
300       EdgeRealizer er = graph.getRealizer(e);
301       for (int i = 0; i < er.labelCount(); i++) {
302         EdgeLabel el = er.getLabel(i);
303         el.setModel(model);
304 //        el.setRotationAngle(0);
305         int prefAlongEdge = el.getPreferredPlacement() & LabelLayoutConstants.PLACEMENT_ALONG_EDGE_MASK;
306         int prefOnSide = el.getPreferredPlacement() & LabelLayoutConstants.PLACEMENT_ON_SIDE_OF_EDGE_MASK;
307         if (model == EdgeLabel.CENTER_SLIDER && prefOnSide != LabelLayoutConstants.PLACE_ON_EDGE) {
308           el.setPreferredPlacement((byte) (LabelLayoutConstants.PLACE_ON_EDGE | prefAlongEdge));
309         } else if(model == EdgeLabel.SIDE_SLIDER && prefOnSide == LabelLayoutConstants.PLACE_ON_EDGE) {
310           el.setPreferredPlacement((byte) (LabelLayoutConstants.PLACE_RIGHT_OF_EDGE | prefAlongEdge));
311         }
312       }
313     }
314   }
315 }