1
14 package demo.module;
15
16 import y.base.DataProvider;
17 import y.base.Edge;
18 import y.base.EdgeCursor;
19
20 import y.layout.CompositeLayoutStage;
21 import y.layout.LabelLayoutConstants;
22 import y.layout.LabelLayoutDataRefinement;
23 import y.layout.LabelLayoutTranslator;
24 import y.layout.LabelRanking;
25 import y.layout.LayoutOrientation;
26 import y.layout.LayoutStage;
27 import y.layout.OrientationLayouter;
28 import y.layout.PortConstraint;
29 import y.layout.PortConstraintKeys;
30 import y.layout.grouping.FixedGroupLayoutStage;
31 import y.layout.grouping.GroupNodeHider;
32 import y.layout.hierarchic.BFSLayerer;
33 import y.layout.hierarchic.ClassicLayerSequencer;
34 import y.layout.hierarchic.HierarchicGroupLayouter;
35 import y.layout.hierarchic.HierarchicLayouter;
36 import y.layout.hierarchic.LayerSequencer;
37 import y.layout.labeling.GreedyMISLabeling;
38 import y.module.LayoutModule;
39
40 import y.view.EdgeLabel;
41 import y.view.EdgeRealizer;
42 import y.view.Graph2D;
43 import y.view.Selections;
44 import y.view.hierarchy.GroupLayoutConfigurator;
45 import y.view.hierarchy.HierarchyManager;
46
47 import y.option.OptionHandler;
48 import y.option.ConstraintManager;
49 import y.util.DataProviderAdapter;
50
51
58 public class HierarchicLayoutModule extends LayoutModule
59 {
60 private static final String EDGE_LABEL_MODEL = "EDGE_LABEL_MODEL";
61 private static final String EDGE_LABELING = "EDGE_LABELING";
62 private static final String LABELING = "LABELING";
63 private static final String REMOVE_FALSE_CROSSINGS = "REMOVE_FALSE_CROSSINGS";
64 private static final String USE_TRANSPOSITION = "USE_TRANSPOSITION";
65 private static final String WEIGHT_HEURISTIC = "WEIGHT_HEURISTIC";
66 private static final String NODE_ORDER = "NODE_ORDER";
67 private static final String RANDOMIZATION_ROUNDS = "RANDOMIZATION_ROUNDS";
68 private static final String RANKING_POLICY = "RANKING_POLICY";
69 private static final String NODE_RANK = "NODE_RANK";
70 private static final String ACT_ON_SELECTION_ONLY = "ACT_ON_SELECTION_ONLY";
71 private static final String BACKLOOP_ROUTING = "BACKLOOP_ROUTING";
72 private static final String EDGE_ROUTING = "EDGE_ROUTING";
73 private static final String NODE_PLACEMENT = "NODE_PLACEMENT";
74 private static final String ORIENTATION = "ORIENTATION";
75 private static final String MAXIMAL_DURATION = "MAXIMAL_DURATION";
76 private static final String MINIMAL_EDGE_DISTANCE = "MINIMAL_EDGE_DISTANCE";
77 private static final String MINIMAL_FIRST_SEGMENT_LENGTH = "MINIMAL_FIRST_SEGMENT_LENGTH";
78 private static final String MINIMAL_NODE_DISTANCE = "MINIMAL_NODE_DISTANCE";
79 private static final String MINIMAL_LAYER_DISTANCE = "MINIMAL_LAYER_DISTANCE";
80 private static final String LAYOUT = "LAYOUT";
81 private static final String HIERARCHIC = "HIERARCHIC";
82 private static final String FREE = "FREE";
83 private static final String SIDE_SLIDER = "SIDE_SLIDER";
84 private static final String CENTER_SLIDER = "CENTER_SLIDER";
85 private static final String AS_IS = "AS_IS";
86 private static final String BEST = "BEST";
87 private static final String GENERIC = "GENERIC";
88 private static final String NONE = "NONE";
89 private static final String ORTHOGONAL = "ORTHOGONAL";
90 private static final String POLYLINE = "POLYLINE";
91 private static final String TREE = "TREE";
92 private static final String LINEAR_SEGMENTS = "LINEAR_SEGMENTS";
93 private static final String PENDULUM = "PENDULUM";
94 private static final String MEDIAN = "MEDIAN";
95 private static final String BARYCENTER = "BARYCENTER";
96 private static final String SIMPLEX = "SIMPLEX";
97 private static final String MEDIAN_SIMPLEX = "MEDIAN_SIMPLEX";
98 private static final String TIGHT_TREE = "TIGHT_TREE";
99 private static final String DOWNSHIFT_NODES = "DOWNSHIFT_NODES";
100 private static final String NO_RERANKING = "NO_RERANKING";
101 private static final String BFS = "BFS";
102
103 private static final String RIGHT_TO_LEFT = "RIGHT_TO_LEFT";
104 private static final String BOTTOM_TO_TOP = "BOTTOM_TO_TOP";
105 private static final String LEFT_TO_RIGHT = "LEFT_TO_RIGHT";
106 private static final String TOP_TO_BOTTOM = "TOP_TO_BOTTOM";
107
108 private static final String GROUPING = "GROUPING";
109 private static final String GROUP_POLICY = "GROUP_LAYOUT_POLICY";
110 private static final String IGNORE_GROUPS = "IGNORE_GROUPS";
111 private static final String LAYOUT_GROUPS = "LAYOUT_GROUPS";
112 private static final String FIX_GROUPS = "FIX_GROUPS";
113 private static final String ENABLE_GLOBAL_SEQUENCING = "ENABLE_GLOBAL_SEQUENCING";
114
115
116 private static final String[] orientEnum = {
117 TOP_TO_BOTTOM,
118 LEFT_TO_RIGHT,
119 BOTTOM_TO_TOP,
120 RIGHT_TO_LEFT
121 };
122
123 private static final String[] topoLayerPolicy = {
124 NO_RERANKING,
125 DOWNSHIFT_NODES,
126 TIGHT_TREE,
127 SIMPLEX,
128 AS_IS,
129 BFS
130 };
131
132 private static final String[] weightHeuristic = {
133 BARYCENTER,
134 MEDIAN
135 };
136
137 private static final String[] layoutStyles = {
138 PENDULUM,
139 LINEAR_SEGMENTS,
140 POLYLINE,
141 TREE,
142 SIMPLEX,
143 MEDIAN_SIMPLEX,
144 };
145
146 private static final String[] routingStyles = {
147 POLYLINE,
148 ORTHOGONAL
149 };
150
151 private static final String[] edgeLabeling = {
152 NONE,
153 HIERARCHIC,
154 GENERIC
155 };
156
157 private static final String[] edgeLabelModel = {
158 BEST,
159 AS_IS,
160 CENTER_SLIDER,
161 SIDE_SLIDER,
162 FREE,
163 };
164
165
166 private HierarchicGroupLayouter hierarchic;
167
168 public HierarchicLayoutModule()
169 {
170 super (HIERARCHIC,"yFiles Layout Team",
171 "Sugiyama based layout");
172 setPortIntersectionCalculatorEnabled(true);
173 }
174
175 public OptionHandler createOptionHandler()
176 {
177 createHierarchic();
178
179 OptionHandler op = new OptionHandler(getModuleName());
180
181 op.useSection(LAYOUT);
182 op.addInt(MINIMAL_LAYER_DISTANCE, (int)hierarchic.getMinimalLayerDistance());
183 op.addInt(MINIMAL_NODE_DISTANCE, (int)hierarchic.getMinimalNodeDistance());
184 op.addInt(MINIMAL_EDGE_DISTANCE, (int)hierarchic.getMinimalEdgeDistance());
185 op.addInt(MINIMAL_FIRST_SEGMENT_LENGTH, (int)hierarchic.getMinimalFirstSegmentLength());
186 op.addInt(MAXIMAL_DURATION,5);
187 op.addEnum(ORIENTATION, orientEnum, 0);
188 op.addEnum(NODE_PLACEMENT,layoutStyles ,hierarchic.getLayoutStyle());
189 op.addEnum(EDGE_ROUTING ,routingStyles,hierarchic.getRoutingStyle());
190 op.addBool(BACKLOOP_ROUTING, false);
191 op.addBool(ACT_ON_SELECTION_ONLY,false);
192
193 op.useSection(NODE_RANK);
194 op.addEnum(RANKING_POLICY, topoLayerPolicy, 2);
195
196 op.useSection(NODE_ORDER);
197 ClassicLayerSequencer sequencer = new ClassicLayerSequencer();
198 op.addEnum(WEIGHT_HEURISTIC, weightHeuristic, sequencer.getWeightHeuristic());
199 op.addBool(USE_TRANSPOSITION, sequencer.getUseTransposition());
200 op.addBool(REMOVE_FALSE_CROSSINGS, hierarchic.getRemoveFalseCrossings());
201 op.addInt(RANDOMIZATION_ROUNDS, sequencer.getRandomizationRounds());
202
203 op.useSection(LABELING);
204 ConstraintManager cm = new ConstraintManager(op);
205 cm.setEnabledOnValueEquals(op.addEnum(EDGE_LABELING, edgeLabeling, 0), NONE,
206 op.addEnum(EDGE_LABEL_MODEL, edgeLabelModel, 0), true);
207
208 op.useSection(GROUPING);
209 String[] gEnum = { LAYOUT_GROUPS, FIX_GROUPS, IGNORE_GROUPS };
210 op.addEnum(GROUP_POLICY, gEnum, 0);
211 op.addBool(ENABLE_GLOBAL_SEQUENCING, true);
212 return op;
213 }
214
215 public void mainrun()
216 {
217 createHierarchic();
218
219 final Graph2D graph = getGraph2D();
220
221 OptionHandler op = getOptionHandler();
222 hierarchic.setRemoveFalseCrossings(op.getBool(REMOVE_FALSE_CROSSINGS));
223 hierarchic.setMaximalDuration(op.getInt(MAXIMAL_DURATION)*1000);
224 hierarchic.setMinimalNodeDistance(op.getInt(MINIMAL_NODE_DISTANCE));
225 hierarchic.setMinimalEdgeDistance(op.getInt(MINIMAL_EDGE_DISTANCE));
226 hierarchic.setMinimalFirstSegmentLength(op.getInt(MINIMAL_FIRST_SEGMENT_LENGTH));
227 hierarchic.setMinimalLayerDistance(op.getInt(MINIMAL_LAYER_DISTANCE));
228
229 final OrientationLayouter ol = (OrientationLayouter)hierarchic.getOrientationLayouter();
230 if(op.get(ORIENTATION).equals(TOP_TO_BOTTOM))
231 ol.setOrientation(OrientationLayouter.TOP_TO_BOTTOM);
232 else if(op.get(ORIENTATION).equals(LEFT_TO_RIGHT))
233 ol.setOrientation(OrientationLayouter.LEFT_TO_RIGHT);
234 else if(op.get(ORIENTATION).equals(BOTTOM_TO_TOP))
235 ol.setOrientation(OrientationLayouter.BOTTOM_TO_TOP);
236 else if(op.get(ORIENTATION).equals(RIGHT_TO_LEFT))
237 ol.setOrientation(OrientationLayouter.RIGHT_TO_LEFT);
238
239 if (hierarchic instanceof HierarchicGroupLayouter){
240 ((HierarchicGroupLayouter) hierarchic).setGlobalSequencingActive(op.getBool(GROUPING, ENABLE_GLOBAL_SEQUENCING));
241 }
242
243 String el = op.getString(EDGE_LABELING);
244 if(!el.equals(NONE))
245 {
246 setupEdgeLabelModel(el, op.getString(EDGE_LABEL_MODEL));
247 if(el.equals(GENERIC))
248 {
249 GreedyMISLabeling la = new GreedyMISLabeling();
250 la.setPlaceNodeLabels(false);
251 la.setPlaceEdgeLabels(true);
252 la.setProfitModel(new LabelRanking());
253 hierarchic.setLabelLayouter(la);
254 hierarchic.setLabelLayouterEnabled(true);
255 }
256 else if(el.equals(HIERARCHIC))
257 {
258 CompositeLayoutStage ll = new CompositeLayoutStage();
259 ll.appendStage(new LabelLayoutTranslator());
260 ll.appendStage(new LabelLayoutDataRefinement());
261 hierarchic.setLabelLayouter(ll);
262 hierarchic.setLabelLayouterEnabled(true);
263 }
264 }
265 else
266 {
267 hierarchic.setLabelLayouterEnabled(false);
268 }
269
270
271 String ls = op.getString(NODE_PLACEMENT);
272 if(ls.equals(PENDULUM))
273 hierarchic.setLayoutStyle(HierarchicLayouter.PENDULUM);
274 else if(ls.equals(POLYLINE))
275 hierarchic.setLayoutStyle(HierarchicLayouter.POLYLINE);
276 else if(ls.equals(LINEAR_SEGMENTS))
277 hierarchic.setLayoutStyle(HierarchicLayouter.LINEAR_SEGMENTS);
278 else if(ls.equals(TREE))
279 hierarchic.setLayoutStyle(HierarchicLayouter.TREE);
280 else if(ls.equals(SIMPLEX))
281 hierarchic.setLayoutStyle(HierarchicLayouter.SIMPLEX);
282 else if(ls.equals(MEDIAN_SIMPLEX))
283 hierarchic.setLayoutStyle(HierarchicLayouter.MEDIAN_SIMPLEX);
284 String rs = op.getString(EDGE_ROUTING);
285 if(rs.equals(POLYLINE))
286 hierarchic.setRoutingStyle(HierarchicLayouter.ROUTE_POLYLINE);
287 else if(rs.equals(ORTHOGONAL))
288 hierarchic.setRoutingStyle(HierarchicLayouter.ROUTE_ORTHOGONAL);
289
290
291 hierarchic.setSubgraphLayouterEnabled(op.getBool(ACT_ON_SELECTION_ONLY));
292
293 String rp = op.getString(RANKING_POLICY);
294
295 if(rp.equals(AS_IS))
296 hierarchic.setLayeringStrategy(HierarchicLayouter.LAYERING_FROM_SKETCH);
297 else if(rp.equals(SIMPLEX))
298 hierarchic.setLayeringStrategy(HierarchicLayouter.LAYERING_HIERARCHICAL_OPTIMAL);
299 else if(rp.equals(NO_RERANKING))
300 hierarchic.setLayeringStrategy(HierarchicLayouter.LAYERING_HIERARCHICAL_TOPMOST);
301 else if (rp.equals(DOWNSHIFT_NODES))
302 hierarchic.setLayeringStrategy(HierarchicLayouter.LAYERING_HIERARCHICAL_DOWNSHIFT);
303 else if (rp.equals(TIGHT_TREE))
304 hierarchic.setLayeringStrategy(HierarchicLayouter.LAYERING_HIERARCHICAL_TIGHT_TREE);
305 else if(rp.equals(BFS))
306 {
307 hierarchic.setLayeringStrategy(HierarchicLayouter.LAYERING_BFS);
308 getGraph2D().addDataProvider(BFSLayerer.CORE_NODES, Selections.createSelectionNodeMap(getGraph2D()));
309 }
310
311 String wh = op.getString(WEIGHT_HEURISTIC);
312
313 LayerSequencer layerSequencer = hierarchic.getLayerSequencer();
314 if (layerSequencer instanceof ClassicLayerSequencer){
315 ClassicLayerSequencer cls = (ClassicLayerSequencer)layerSequencer;
316 if(wh.equals(MEDIAN))
317 cls.setWeightHeuristic(ClassicLayerSequencer.MEDIAN_HEURISTIC);
318 else
319 cls.setWeightHeuristic(ClassicLayerSequencer.BARYCENTER_HEURISTIC);
320 cls.setUseTransposition(op.getBool(USE_TRANSPOSITION));
321 cls.setRandomizationRounds(op.getInt(NODE_ORDER, RANDOMIZATION_ROUNDS));
322 hierarchic.setLayerSequencer(cls);
323 }
324
325 DataProvider dp = null;
326
327 DataProvider oldSdp = graph.getDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY);
328 DataProvider oldTdp = graph.getDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY);
329
330 if(op.getBool(BACKLOOP_ROUTING))
331 {
332 PortConstraint spc = null, tpc = null;
333 switch(ol.getOrientation()) {
334 case LayoutOrientation.TOP_TO_BOTTOM:
335 spc = PortConstraint.create(PortConstraint.SOUTH);
336 tpc = PortConstraint.create(PortConstraint.NORTH);
337 break;
338 case LayoutOrientation.LEFT_TO_RIGHT:
339 spc = PortConstraint.create(PortConstraint.EAST);
340 tpc = PortConstraint.create(PortConstraint.WEST);
341 break;
342 case LayoutOrientation.RIGHT_TO_LEFT:
343 spc = PortConstraint.create(PortConstraint.WEST);
344 tpc = PortConstraint.create(PortConstraint.EAST);
345 break;
346 case LayoutOrientation.BOTTOM_TO_TOP:
347 spc = PortConstraint.create(PortConstraint.NORTH);
348 tpc = PortConstraint.create(PortConstraint.SOUTH);
349 break;
350 }
351 DataProvider sdp = new BackloopConstraintDP(spc, oldSdp);
352 DataProvider tdp = new BackloopConstraintDP(tpc, oldTdp);
353
354 if (oldSdp != null){
355 graph.removeDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY);
356 }
357 if (oldTdp != null){
358 graph.removeDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY);
359 }
360
361 graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY,sdp);
362 graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY,tdp);
363 }
364
365 if(HierarchyManager.containsGroupNodes(graph))
366 {
367 LayoutStage preStage = null;
368 GroupLayoutConfigurator glc = null;
369 if(op.get(GROUP_POLICY).equals(IGNORE_GROUPS))
370 {
371 preStage = new GroupNodeHider();
372 hierarchic.prependStage(preStage);
373 }
374 else
375 {
376 glc = new GroupLayoutConfigurator(graph);
377 glc.prepareAll();
378
379 if(op.get(GROUP_POLICY).equals(FIX_GROUPS))
380 {
381 FixedGroupLayoutStage fixedGroupLayoutStage = new FixedGroupLayoutStage();
382 if(op.get(EDGE_ROUTING).equals(ORTHOGONAL))
383 {
384 fixedGroupLayoutStage.setInterEdgeRoutingStyle(FixedGroupLayoutStage.ROUTING_STYLE_ORTHOGONAL);
385 }
386 preStage = fixedGroupLayoutStage;
387 hierarchic.prependStage(preStage);
388 }
389 }
390
391 try
392 {
393 launchLayouter(hierarchic);
394 }
395 finally
396 {
397 if(glc != null)
398 {
399 glc.restoreAll();
400 }
401 if(preStage != null)
402 {
403 hierarchic.removeStage(preStage);
404 }
405 }
406 }
407 else
408 {
409 launchLayouter(hierarchic);
410 }
411
412 if(op.getBool(BACKLOOP_ROUTING))
413 {
414 graph.removeDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY);
415 graph.removeDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY);
416 if (oldSdp != null){
417 graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, oldSdp);
418 }
419 if (oldTdp != null){
420 graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, oldTdp);
421 }
422 }
423 if (dp != null){
424 graph.removeDataProvider(ClassicLayerSequencer.GROUP_KEY);
425 }
426
427 graph.removeDataProvider(BFSLayerer.CORE_NODES);
429 }
430
431 static final class BackloopConstraintDP extends DataProviderAdapter
432 {
433 private PortConstraint pc;
434 private DataProvider delegate;
435 private static final PortConstraint anySide = PortConstraint.create(PortConstraint.ANY_SIDE);
436 BackloopConstraintDP(PortConstraint pc, DataProvider delegate)
437 {
438 this.pc = pc;
439 this.delegate = delegate;
440 }
441
442 public Object get(Object o)
443 {
444 if (delegate != null){
445 Object delegateResult = delegate.get(o);
446 if (delegateResult != null){
447 return delegateResult;
448 }
449 }
450 Edge e = (Edge)o;
451 if(e.isSelfLoop())
452 {
453 return anySide;
454 } else {
455 return pc;
456 }
457 }
458 }
459
460 void setupEdgeLabelModel(String edgeLabeling, String edgeLabelModel)
461 {
462 if(edgeLabeling.equals(NONE) || edgeLabelModel.equals(AS_IS))
463 {
464 return; }
466
467 if(edgeLabelModel.equals(BEST))
468 {
469 if(edgeLabeling.equals(GENERIC))
470 edgeLabelModel = SIDE_SLIDER;
471 else if(edgeLabeling.equals(HIERARCHIC))
472 edgeLabelModel = FREE;
473 }
474
475 byte model = EdgeLabel.SIDE_SLIDER;
476 int preferredSide = LabelLayoutConstants.PLACE_RIGHT_OF_EDGE;
477 if(edgeLabelModel.equals(CENTER_SLIDER))
478 {
479 model = EdgeLabel.CENTER_SLIDER;
480 preferredSide = LabelLayoutConstants.PLACE_ON_EDGE;
481 }
482 else if(edgeLabelModel.equals(FREE))
483 {
484 model = EdgeLabel.FREE;
485 preferredSide = LabelLayoutConstants.PLACE_ON_EDGE;
486 }
487
488 Graph2D graph = getGraph2D();
489 for(EdgeCursor ec = graph.edges(); ec.ok(); ec.next())
490 {
491 Edge e = ec.edge();
492 EdgeRealizer er = graph.getRealizer(e);
493 for(int i = 0; i < er.labelCount(); i++)
494 {
495 EdgeLabel el = er.getLabel(i);
496 el.setModel(model);
497 int prefAlongEdge = el.getPreferredPlacement() & LabelLayoutConstants.PLACEMENT_ALONG_EDGE_MASK;
498 el.setPreferredPlacement((byte)(preferredSide | prefAlongEdge));
499 }
500 }
501 }
502
503
504 public void dispose()
505 {
506 hierarchic = null;
507 }
508
509 private void createHierarchic()
510 {
511 if(hierarchic == null)
512 {
513 hierarchic = new HierarchicGroupLayouter();
514 }
515 }
516 }
517
518