1
28 package demo.layout.module;
29
30 import y.module.LayoutModule;
31 import y.module.YModule;
32
33 import y.base.Edge;
34 import y.base.EdgeCursor;
35 import y.layout.CanonicMultiStageLayouter;
36 import y.layout.ComponentLayouter;
37 import y.layout.LabelLayoutConstants;
38 import y.layout.LabelRanking;
39 import y.layout.PreferredPlacementDescriptor;
40 import y.layout.grouping.FixedGroupLayoutStage;
41 import y.layout.labeling.GreedyMISLabeling;
42 import y.layout.orthogonal.EdgeLayoutDescriptor;
43 import y.layout.orthogonal.OrthogonalGroupLayouter;
44 import y.layout.orthogonal.OrthogonalLayouter;
45 import y.option.ConstraintManager;
46 import y.option.EnumOptionItem;
47 import y.option.IntOptionItem;
48 import y.option.OptionHandler;
49 import y.option.ConstraintManager.Condition;
50 import y.option.OptionItem;
51 import y.view.EdgeLabel;
52 import y.view.EdgeRealizer;
53 import y.view.Graph2D;
54 import y.view.hierarchy.HierarchyManager;
55
56
57
68 public class OrthogonalLayoutModule extends LayoutModule {
69 protected static final String MODULE_ORTHOGONAL = "ORTHOGONAL_LAYOUTER";
71
72 protected static final String SECTION_LAYOUT = "LAYOUT";
74 protected static final String ITEM_STYLE = "STYLE";
76 protected static final String VALUE_NORMAL = "NORMAL";
77 protected static final String VALUE_NORMAL_TREE = "NORMAL_TREE";
78 protected static final String VALUE_UNIFORM_NODES = "UNIFORM_NODES";
79 protected static final String VALUE_BOX_NODES = "BOX_NODES";
80 protected static final String VALUE_MIXED = "MIXED";
81 protected static final String VALUE_FIXED_MIXED = "FIXED_MIXED";
82 protected static final String VALUE_FIXED_BOX_NODES = "FIXED_BOX_NODES";
83 protected static final String ITEM_GRID = "GRID";
84 protected static final String ITEM_LENGTH_REDUCTION = "LENGTH_REDUCTION";
85 protected static final String ITEM_USE_EXISTING_DRAWING_AS_SKETCH = "USE_EXISTING_DRAWING_AS_SKETCH";
86 protected static final String ITEM_CROSSING_POSTPROCESSING = "CROSSING_POSTPROCESSING";
87 protected static final String ITEM_PERCEIVED_BENDS_POSTPROCESSING = "PERCEIVED_BENDS_POSTPROCESSING";
88 protected static final String ITEM_ALIGN_DEGREE_ONE_NODES = "ALIGN_DEGREE_ONE_NODES";
89 protected static final String ITEM_USE_RANDOMIZATION = "USE_RANDOMIZATION";
90 protected static final String ITEM_USE_FACE_MAXIMIZATION = "USE_FACE_MAXIMIZATION";
91 protected static final String ITEM_ROUTE_MULTI_EDGES_IN_PARALLEL = "ROUTE_MULTI_EDGES_IN_PARALLEL";
92 protected static final String ITEM_MINIMUM_FIRST_SEGMENT_LENGTH = "MINIMUM_FIRST_SEGMENT_LENGTH";
93 protected static final String ITEM_MINIMUM_LAST_SEGMENT_LENGTH = "MINIMUM_LAST_SEGMENT_LENGTH";
94 protected static final String ITEM_MINIMUM_SEGMENT_LENGTH = "MINIMUM_SEGMENT_LENGTH";
95
96 protected static final String SECTION_LABELING = "LABELING";
98 protected static final String ITEM_EDGE_LABELING = "EDGE_LABELING";
100 protected static final String VALUE_NONE = "NONE";
101 protected static final String VALUE_INTEGRATED = "INTEGRATED";
102 protected static final String VALUE_GENERIC = "GENERIC";
103 protected static final String ITEM_EDGE_LABEL_MODEL = "EDGE_LABEL_MODEL";
104 protected static final String VALUE_BEST = "BEST";
105 protected static final String VALUE_AS_IS = "AS_IS";
106 protected static final String VALUE_CENTER_SLIDER = "CENTER_SLIDER";
107 protected static final String VALUE_SIDE_SLIDER = "SIDE_SLIDER";
108 protected static final String VALUE_FREE = "FREE";
109 protected static final String ITEM_CONSIDER_NODE_LABELS = "CONSIDER_NODE_LABELS";
110
111 protected static final String SECTION_GROUPING = "GROUPING";
113 protected static final String ITEM_GROUP_POLICY = "GROUP_LAYOUT_POLICY";
115 protected static final String VALUE_LAYOUT_GROUPS = "LAYOUT_GROUPS";
116 protected static final String VALUE_FIX_GROUPS = "FIX_GROUPS";
117 protected static final String VALUE_IGNORE_GROUPS = "IGNORE_GROUPS";
118 protected static final String ITEM_GROUP_LAYOUT_QUALITY = "GROUP_LAYOUT_QUALITY";
119
120
123 public OrthogonalLayoutModule() {
124 super(MODULE_ORTHOGONAL);
125 setPortIntersectionCalculatorEnabled(true);
126 }
127
128
132 protected OptionHandler createOptionHandler() {
133 final OptionHandler options = new OptionHandler(getModuleName());
134 final ConstraintManager optionConstraints = new ConstraintManager(options);
135
136 options.useSection(SECTION_LAYOUT);
138 options.addEnum(ITEM_STYLE, new String[]{
140 VALUE_NORMAL,
141 VALUE_NORMAL_TREE,
142 VALUE_UNIFORM_NODES,
143 VALUE_BOX_NODES,
144 VALUE_MIXED,
145 VALUE_FIXED_MIXED,
146 VALUE_FIXED_BOX_NODES
147 }, 0);
148 options.addInt(ITEM_GRID,25)
149 .setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(1));
150 options.addBool(ITEM_LENGTH_REDUCTION, true);
151 options.addBool(ITEM_USE_EXISTING_DRAWING_AS_SKETCH, false);
152 options.addBool(ITEM_CROSSING_POSTPROCESSING, true);
153 options.addBool(ITEM_PERCEIVED_BENDS_POSTPROCESSING, true);
154 options.addBool(ITEM_ALIGN_DEGREE_ONE_NODES, true);
155 options.addBool(ITEM_USE_RANDOMIZATION, true);
156 options.addBool(ITEM_USE_FACE_MAXIMIZATION,false);
157 options.addBool(ITEM_ROUTE_MULTI_EDGES_IN_PARALLEL, false);
158 options.addDouble(ITEM_MINIMUM_FIRST_SEGMENT_LENGTH, 10);
159 options.addDouble(ITEM_MINIMUM_LAST_SEGMENT_LENGTH, 10);
160 options.addDouble(ITEM_MINIMUM_SEGMENT_LENGTH, 10);
161
162 options.useSection(SECTION_LABELING);
164 options.addEnum(ITEM_EDGE_LABELING, new String[]{
166 VALUE_NONE,
167 VALUE_INTEGRATED,
168 VALUE_GENERIC
169 }, 0);
170 options.addEnum(ITEM_EDGE_LABEL_MODEL, new String[]{
171 VALUE_BEST,
172 VALUE_AS_IS,
173 VALUE_CENTER_SLIDER,
174 VALUE_SIDE_SLIDER,
175 VALUE_FREE
176 }, 0);
177 options.addBool(ITEM_CONSIDER_NODE_LABELS, false);
178 optionConstraints.setEnabledOnValueEquals(ITEM_EDGE_LABELING, VALUE_NONE, ITEM_EDGE_LABEL_MODEL, true);
180 final Condition c =
181 optionConstraints.createConditionValueEquals(ITEM_USE_EXISTING_DRAWING_AS_SKETCH, Boolean.FALSE);
182 optionConstraints.setEnabledOnCondition(c, options.getItem(ITEM_CROSSING_POSTPROCESSING));
183 optionConstraints.setEnabledOnCondition(c, options.getItem(ITEM_PERCEIVED_BENDS_POSTPROCESSING));
184 optionConstraints.setEnabledOnCondition(c, options.getItem(ITEM_ALIGN_DEGREE_ONE_NODES));
185 optionConstraints.setEnabledOnCondition(c, options.getItem(ITEM_STYLE));
186 optionConstraints.setEnabledOnCondition(c, options.getItem(ITEM_USE_RANDOMIZATION));
187
188 options.useSection(SECTION_GROUPING);
190 final EnumOptionItem itemGroupPolicy = options.addEnum(ITEM_GROUP_POLICY, new String[]{
192 VALUE_LAYOUT_GROUPS,
193 VALUE_FIX_GROUPS,
194 VALUE_IGNORE_GROUPS
195 }, 0);
196 final OptionItem itemGroupLayoutQuality = options.addDouble(ITEM_GROUP_LAYOUT_QUALITY, 1.0, 0.0, 1.0);
197 optionConstraints.setEnabledOnValueEquals(itemGroupPolicy, VALUE_LAYOUT_GROUPS, itemGroupLayoutQuality);
199
200 return options;
201 }
202
203
207 protected void mainrun() {
208 final CanonicMultiStageLayouter canonic;
209
210 final OptionHandler options = getOptionHandler();
211
212 if (HierarchyManager.containsGroupNodes(getGraph2D())
213 && !options.get(ITEM_GROUP_POLICY).equals(VALUE_IGNORE_GROUPS)
214 && !options.get(ITEM_GROUP_POLICY).equals(VALUE_FIX_GROUPS)) {
215 final OrthogonalGroupLayouter orthogonalGroup = new OrthogonalGroupLayouter();
216 configure(orthogonalGroup, options);
217 canonic = orthogonalGroup;
218 } else {
219 final OrthogonalLayouter orthogonal = new OrthogonalLayouter();
220 configure(orthogonal, options);
221 canonic = orthogonal;
222 }
223
224 final Graph2D graph = getGraph2D();
225 prepareGraph(graph, options);
226
227 launchLayouter(canonic);
228 }
229
230
236 private void prepareGraph(final Graph2D graph, final OptionHandler options) {
237 final boolean normalStyle = VALUE_NORMAL.equals(options.getString(ITEM_STYLE));
238 final String el = options.getString(ITEM_EDGE_LABELING);
239 if (el.equals(VALUE_GENERIC) || (el.equals(VALUE_INTEGRATED) && normalStyle)) {
240 setupEdgeLabelModel(graph, el, options.getString(ITEM_EDGE_LABEL_MODEL));
241 }
242 }
243
244
253 protected void configure(final OrthogonalLayouter orthogonal, final OptionHandler options) {
254 ((ComponentLayouter) orthogonal.getComponentLayouter()).setStyle(ComponentLayouter.STYLE_MULTI_ROWS);
255
256
260 final String styleValue = options.getString(ITEM_STYLE);
261 if (VALUE_NORMAL_TREE.equals(styleValue)) {
262 orthogonal.setLayoutStyle(OrthogonalLayouter.NORMAL_TREE_STYLE);
263 } else if (VALUE_UNIFORM_NODES.equals(styleValue)) {
264 orthogonal.setLayoutStyle(OrthogonalLayouter.UNIFORM_STYLE);
265 } else if (VALUE_BOX_NODES.equals(styleValue)) {
266 orthogonal.setLayoutStyle(OrthogonalLayouter.BOX_STYLE);
267 } else if (VALUE_MIXED.equals(styleValue)) {
268 orthogonal.setLayoutStyle(OrthogonalLayouter.MIXED_STYLE);
269 } else if (VALUE_FIXED_MIXED.equals(styleValue)) {
270 orthogonal.setLayoutStyle(OrthogonalLayouter.FIXED_MIXED_STYLE);
271 } else if (VALUE_FIXED_BOX_NODES.equals(styleValue)) {
272 orthogonal.setLayoutStyle(OrthogonalLayouter.FIXED_BOX_STYLE);
273 } else {
274 orthogonal.setLayoutStyle(OrthogonalLayouter.NORMAL_STYLE);
276 }
277
278 final EdgeLayoutDescriptor layoutDescriptor = orthogonal.getEdgeLayoutDescriptor();
279 layoutDescriptor.setMinimumFirstSegmentLength(options.getDouble(ITEM_MINIMUM_FIRST_SEGMENT_LENGTH));
280 layoutDescriptor.setMinimumLastSegmentLength(options.getDouble(ITEM_MINIMUM_LAST_SEGMENT_LENGTH));
281 layoutDescriptor.setMinimumSegmentLength(options.getDouble(ITEM_MINIMUM_SEGMENT_LENGTH));
282
283 orthogonal.setGrid(options.getInt(ITEM_GRID));
284 orthogonal.setUseLengthReduction(
285 options.getBool(ITEM_LENGTH_REDUCTION));
286 orthogonal.setUseCrossingPostprocessing(
287 options.getBool(ITEM_CROSSING_POSTPROCESSING));
288 orthogonal.setPerceivedBendsOptimizationEnabled(
289 options.getBool(ITEM_PERCEIVED_BENDS_POSTPROCESSING));
290 orthogonal.setAlignDegreeOneNodesEnabled(options.getBool(ITEM_ALIGN_DEGREE_ONE_NODES));
291 orthogonal.setUseRandomization(
292 options.getBool(ITEM_USE_RANDOMIZATION));
293 orthogonal.setUseFaceMaximization(
294 options.getBool(ITEM_USE_FACE_MAXIMIZATION));
295 orthogonal.setUseSketchDrawing(options.getBool(ITEM_USE_EXISTING_DRAWING_AS_SKETCH));
296 orthogonal.setParallelEdgeLayouterEnabled(options.getBool(ITEM_ROUTE_MULTI_EDGES_IN_PARALLEL));
297
298
299
303 final boolean normalStyle = (orthogonal.getLayoutStyle() == OrthogonalLayouter.NORMAL_STYLE);
304 final String el = options.getString(ITEM_EDGE_LABELING);
305 orthogonal.setIntegratedEdgeLabelingEnabled(el.equals(VALUE_INTEGRATED) && normalStyle);
306 orthogonal.setConsiderNodeLabelsEnabled(options.getBool(ITEM_CONSIDER_NODE_LABELS) && normalStyle);
307
308 if (HierarchyManager.containsGroupNodes(getGraph2D()) && !options.get(ITEM_GROUP_POLICY).equals(VALUE_IGNORE_GROUPS)) {
309 if (options.get(ITEM_GROUP_POLICY).equals(VALUE_FIX_GROUPS)) {
310 final FixedGroupLayoutStage fgl = new FixedGroupLayoutStage();
311 fgl.setInterEdgeRoutingStyle(FixedGroupLayoutStage.ROUTING_STYLE_ORTHOGONAL);
312 orthogonal.prependStage(fgl);
313 }
314 }
315
316 if (options.getString(ITEM_EDGE_LABELING).equals(VALUE_GENERIC)) {
317 final GreedyMISLabeling la = new GreedyMISLabeling();
318 la.setPlaceNodeLabels(false);
319 la.setPlaceEdgeLabels(true);
320 la.setAutoFlippingEnabled(true);
321 la.setProfitModel(new LabelRanking());
322
323 orthogonal.appendStage(la);
330 }
331 }
332
333
339 protected void configure(final OrthogonalGroupLayouter orthogonalGroup, final OptionHandler options) {
340 final boolean normalStyle = (VALUE_NORMAL.equals(options.getString(ITEM_STYLE)));
341 final String el = options.getString(ITEM_EDGE_LABELING);
342
343 ((ComponentLayouter) orthogonalGroup.getComponentLayouter()).setStyle(ComponentLayouter.STYLE_MULTI_ROWS);
344 final EdgeLayoutDescriptor descriptor = orthogonalGroup.getEdgeLayoutDescriptor();
345 descriptor.setMinimumFirstSegmentLength(options.getDouble(ITEM_MINIMUM_FIRST_SEGMENT_LENGTH));
346 descriptor.setMinimumLastSegmentLength(options.getDouble(ITEM_MINIMUM_LAST_SEGMENT_LENGTH));
347 descriptor.setMinimumSegmentLength(options.getDouble(ITEM_MINIMUM_SEGMENT_LENGTH));
348 orthogonalGroup.setIntegratedEdgeLabelingEnabled(el.equals(VALUE_INTEGRATED) && normalStyle);
349 orthogonalGroup.setConsiderNodeLabelsEnabled(options.getBool(ITEM_CONSIDER_NODE_LABELS) && normalStyle);
350 orthogonalGroup.setAlignDegreeOneNodesEnabled(options.getBool(ITEM_ALIGN_DEGREE_ONE_NODES));
351 orthogonalGroup.setPerceivedBendsOptimizationEnabled(options.getBool(ITEM_PERCEIVED_BENDS_POSTPROCESSING));
352
353 orthogonalGroup.setGrid(options.getInt(ITEM_GRID));
354 orthogonalGroup.setLayoutQuality(options.getDouble(ITEM_GROUP_LAYOUT_QUALITY));
355 orthogonalGroup.setParallelEdgeLayouterEnabled(options.getBool(ITEM_ROUTE_MULTI_EDGES_IN_PARALLEL));
356 }
357
358 private static void setupEdgeLabelModel(final Graph2D graph, final String edgeLabeling, final String edgeLabelModel) {
359 if (edgeLabeling.equals(VALUE_NONE) || edgeLabelModel.equals(VALUE_AS_IS)) {
360 return; }
362
363 byte model = EdgeLabel.SIDE_SLIDER;
364 if (edgeLabelModel.equals(VALUE_CENTER_SLIDER)) {
365 model = EdgeLabel.CENTER_SLIDER;
366 } else if (edgeLabelModel.equals(VALUE_FREE) || edgeLabelModel.equals(VALUE_BEST)) {
367 model = EdgeLabel.FREE;
368 }
369
370 for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
371 final Edge e = ec.edge();
372 EdgeRealizer er = graph.getRealizer(e);
373 for (int i = 0; i < er.labelCount(); i++) {
374 final EdgeLabel el = er.getLabel(i);
375 el.setModel(model);
376 final byte prefOnSide = el.getPreferredPlacementDescriptor().getSideOfEdge();
377 if (model == EdgeLabel.CENTER_SLIDER && prefOnSide != LabelLayoutConstants.PLACE_ON_EDGE) {
378 setPreferredSide(el, LabelLayoutConstants.PLACE_ON_EDGE);
379 } else if (model == EdgeLabel.SIDE_SLIDER && prefOnSide == LabelLayoutConstants.PLACE_ON_EDGE) {
380 setPreferredSide(el, LabelLayoutConstants.PLACE_RIGHT_OF_EDGE);
381 }
382 }
383 }
384 }
385
386 private static void setPreferredSide(final EdgeLabel el, final byte preferredSide) {
387 final PreferredPlacementDescriptor oldDescriptor =
388 el.getPreferredPlacementDescriptor();
389 if (oldDescriptor.getSideOfEdge() != preferredSide) {
390 final PreferredPlacementDescriptor newDescriptor =
391 new PreferredPlacementDescriptor(oldDescriptor);
392 newDescriptor.setSideOfEdge(preferredSide);
393 el.setPreferredPlacementDescriptor(newDescriptor);
394 }
395 }
396 }
397