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