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.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  /**
52   * This module represents an interactive configurator and launcher for
53   * {@link y.layout.hierarchic.HierarchicLayouter}
54   * and {@link y.layout.hierarchic.HierarchicGroupLayouter}.
55   * It is similar to HierarchicLayoutModule found in the yFiles package y.module.
56   *
57   */
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     //cleanup BFSLayerer key if present
428     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; //nothing to do
465     }
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