1   /****************************************************************************
2    * This demo file is part of yFiles for Java 2.14.
3    * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4    * 72070 Tuebingen, Germany. All rights reserved.
5    * 
6    * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7    * of demo files in source code or binary form, with or without
8    * modification, is not permitted.
9    * 
10   * Owners of a valid software license for a yFiles for Java version that this
11   * demo is shipped with are allowed to use the demo source code as basis
12   * for their own yFiles for Java powered applications. Use of such programs is
13   * governed by the rights and conditions as set out in the yFiles for Java
14   * license agreement.
15   * 
16   * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19   * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21   * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   *
27   ***************************************************************************/
28  package demo.layout.module;
29  
30  import y.module.LayoutModule;
31  import y.module.YModule;
32  
33  import y.base.DataMap;
34  import y.base.Edge;
35  import y.base.EdgeCursor;
36  import y.base.Node;
37  import y.base.NodeCursor;
38  import y.base.NodeMap;
39  import y.geom.YPoint;
40  import y.layout.LabelLayoutConstants;
41  import y.layout.LabelRanking;
42  import y.layout.OrientationLayouter;
43  import y.layout.PreferredPlacementDescriptor;
44  import y.layout.hierarchic.AsIsLayerer;
45  import y.layout.hierarchic.BFSLayerer;
46  import y.layout.hierarchic.IncrementalHierarchicLayouter;
47  import y.layout.hierarchic.incremental.EdgeLayoutDescriptor;
48  import y.layout.hierarchic.incremental.HierarchicLayouter;
49  import y.layout.hierarchic.incremental.IncrementalHintsFactory;
50  import y.layout.hierarchic.incremental.NodeLayoutDescriptor;
51  import y.layout.hierarchic.incremental.OldLayererWrapper;
52  import y.layout.hierarchic.incremental.RoutingStyle;
53  import y.layout.hierarchic.incremental.SimplexNodePlacer;
54  import y.layout.hierarchic.incremental.TopLevelGroupToSwimlaneStage;
55  import y.layout.labeling.GreedyMISLabeling;
56  import y.option.ConstraintManager;
57  import y.option.ConstraintManager.Condition;
58  import y.option.DoubleOptionItem;
59  import y.option.EnumOptionItem;
60  import y.option.OptionGroup;
61  import y.option.OptionHandler;
62  import y.option.OptionItem;
63  import y.util.DataProviderAdapter;
64  import y.util.Maps;
65  import y.view.Arrow;
66  import y.view.EdgeLabel;
67  import y.view.EdgeRealizer;
68  import y.view.Graph2D;
69  import y.view.Selections;
70  import y.view.hierarchy.HierarchyManager;
71  
72  /**
73   * This module represents an interactive configurator and launcher for
74   * {@link y.layout.hierarchic.IncrementalHierarchicLayouter}.
75   *
76   *
77   * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/incremental_hierarchical_layouter#incremental_hierarchical_stages" target="_blank">Section Applicable Layout Stages</a> in the yFiles for Java Developer's Guide
78   * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/layout_advanced_features#layout_advanced_features" target="_blank">Section Advanced Layout Concepts</a> in the yFiles for Java Developer's Guide
79   */
80  public class IncrementalHierarchicLayoutModule extends LayoutModule {
81    //// Module 'Incremental Hierarchic Layout'
82    protected static final String MODULE_INCREMENTAL_HIERARCHIC = "INCREMENTAL_HIERARCHIC";
83  
84    //// Section 'General'
85    protected static final String SECTION_GENERAL = "GENERAL";
86    // Section 'General' items
87    protected static final String TITLE_INTERACTION = "INTERACTION";
88    protected static final String ITEM_SELECTED_ELEMENTS_INCREMENTALLY = "SELECTED_ELEMENTS_INCREMENTALLY";
89    protected static final String ITEM_USE_DRAWING_AS_SKETCH = "USE_DRAWING_AS_SKETCH";
90    protected static final String ITEM_ORIENTATION = "ORIENTATION";
91    protected static final String VALUE_TOP_TO_BOTTOM = "TOP_TO_BOTTOM";
92    protected static final String VALUE_LEFT_TO_RIGHT = "LEFT_TO_RIGHT";
93    protected static final String VALUE_BOTTOM_TO_TOP = "BOTTOM_TO_TOP";
94    protected static final String VALUE_RIGHT_TO_LEFT = "RIGHT_TO_LEFT";
95    protected static final String ITEM_LAYOUT_COMPONENTS_SEPARATELY = "LAYOUT_COMPONENTS_SEPARATELY";
96    protected static final String ITEM_SYMMETRIC_PLACEMENT = "SYMMETRIC_PLACEMENT";
97    protected static final String ITEM_MAXIMAL_DURATION = "MAXIMAL_DURATION";
98    protected static final String TITLE_MINIMUM_DISTANCES = "MINIMUM_DISTANCES";
99    protected static final String ITEM_NODE_TO_NODE_DISTANCE = "NODE_TO_NODE_DISTANCE";
100   protected static final String ITEM_NODE_TO_EDGE_DISTANCE = "NODE_TO_EDGE_DISTANCE";
101   protected static final String ITEM_EDGE_TO_EDGE_DISTANCE = "EDGE_TO_EDGE_DISTANCE";
102   protected static final String ITEM_MINIMUM_LAYER_DISTANCE = "MINIMUM_LAYER_DISTANCE";
103 
104   //// Section 'Edges'
105   protected static final String SECTION_EDGE_SETTINGS = "EDGE_SETTINGS";
106   //-
107   protected static final String ITEM_EDGE_ROUTING = "EDGE_ROUTING";
108   protected static final String VALUE_EDGE_ROUTING_ORTHOGONAL = "EDGE_ROUTING_ORTHOGONAL";
109   protected static final String VALUE_EDGE_ROUTING_POLYLINE = "EDGE_ROUTING_POLYLINE";
110   protected static final String VALUE_EDGE_ROUTING_OCTILINEAR = "EDGE_ROUTING_OCTILINEAR";
111   protected static final String ITEM_BACKLOOP_ROUTING = "BACKLOOP_ROUTING";
112   protected static final String ITEM_BACKLOOP_ROUTING_SELFLOOPS = "BACKLOOP_ROUTING_SELFLOOPS";
113   protected static final String ITEM_AUTOMATIC_EDGE_GROUPING_ENABLED = "AUTOMATIC_EDGE_GROUPING_ENABLED";
114   protected static final String ITEM_MINIMUM_FIRST_SEGMENT_LENGTH = "MINIMUM_FIRST_SEGMENT_LENGTH";
115   protected static final String ITEM_MINIMUM_LAST_SEGMENT_LENGTH = "MINIMUM_LAST_SEGMENT_LENGTH";
116   protected static final String ITEM_MINIMUM_EDGE_LENGTH = "MINIMUM_EDGE_LENGTH";
117   protected static final String ITEM_MINIMUM_EDGE_DISTANCE = "MINIMUM_EDGE_DISTANCE";
118   protected static final String ITEM_MINIMUM_SLOPE = "MINIMUM_SLOPE";
119   protected static final String ITEM_PC_OPTIMIZATION_ENABLED = "PC_OPTIMIZATION_ENABLED";
120   protected static final String ITEM_EDGE_STRAIGHTENING_OPTIMIZATION_ENABLED = "EDGE_STRAIGHTENING_OPTIMIZATION_ENABLED";
121   protected static final String ITEM_RECURSIVE_EDGE_ROUTING = "RECURSIVE_EDGE_ROUTING";
122   protected static final String VALUE_RECURSIVE_EDGE_ROUTING_OFF = "RECURSIVE_EDGE_ROUTING_OFF";
123   protected static final String VALUE_RECURSIVE_EDGE_ROUTING_DIRECTED = "RECURSIVE_EDGE_ROUTING_DIRECTED";
124   protected static final String VALUE_RECURSIVE_EDGE_ROUTING_UNDIRECTED = "RECURSIVE_EDGE_ROUTING_UNDIRECTED";
125   protected static final String ITEM_CONSIDER_EDGE_THICKNESS = "CONSIDER_EDGE_THICKNESS";
126   protected static final String ITEM_CONSIDER_EDGE_DIRECTION = "CONSIDER_EDGE_DIRECTION";
127 
128   //// Section 'Layers'
129   protected static final String SECTION_RANKS = "RANKS";
130   // Section 'Layers' items
131   protected static final String ITEM_RANKING_POLICY = "RANKING_POLICY";
132   protected static final String VALUE_HIERARCHICAL_OPTIMAL = "HIERARCHICAL_OPTIMAL";
133   protected static final String VALUE_HIERARCHICAL_TIGHT_TREE_HEURISTIC = "HIERARCHICAL_TIGHT_TREE_HEURISTIC";
134   protected static final String VALUE_BFS_LAYERS = "BFS_LAYERS";
135   protected static final String VALUE_FROM_SKETCH = "FROM_SKETCH";
136   protected static final String VALUE_HIERARCHICAL_TOPMOST = "HIERARCHICAL_TOPMOST";
137   protected static final String ITEM_LAYER_ALIGNMENT = "LAYER_ALIGNMENT";
138   protected static final String VALUE_TOP = "TOP";
139   protected static final String VALUE_CENTER = "CENTER";
140   protected static final String VALUE_BOTTOM = "BOTTOM";
141   protected static final String ITEM_COMPONENT_ARRANGEMENT_POLICY = "COMPONENT_ARRANGEMENT_POLICY";
142   protected static final String VALUE_POLICY_COMPACT = "POLICY_COMPACT";
143   protected static final String VALUE_POLICY_TOPMOST = "POLICY_TOPMOST";
144   protected static final String TITLE_FROM_SKETCH_PROPERTIES = "FROM_SKETCH_PROPERTIES";
145   protected static final String ITEM_SCALE = "SCALE";
146   protected static final String ITEM_HALO = "HALO";
147   protected static final String ITEM_MINIMUM_SIZE = "MINIMUM_SIZE";
148   protected static final String ITEM_MAXIMUM_SIZE = "MAXIMUM_SIZE";
149   protected static final String ITEM_NODE_COMPACTION_ENABLED = "NODE_COMPACTION_ENABLED";
150 
151   //// Section 'Labeling'
152   protected static final String SECTION_LABELING = "LABELING";
153   // Section 'Labeling' items
154   protected static final String TITLE_NODE_PROPERTIES = "NODE_PROPERTIES";
155   protected static final String ITEM_CONSIDER_NODE_LABELS = "CONSIDER_NODE_LABELS";
156   protected static final String TITLE_EDGE_PROPERTIES = "EDGE_PROPERTIES";
157   protected static final String ITEM_EDGE_LABELING = "EDGE_LABELING";
158   protected static final String VALUE_EDGE_LABELING_NONE = "EDGE_LABELING_NONE";
159   protected static final String VALUE_EDGE_LABELING_GENERIC = "EDGE_LABELING_GENERIC";
160   protected static final String VALUE_EDGE_LABELING_HIERARCHIC = "EDGE_LABELING_HIERARCHIC";
161   protected static final String ITEM_EDGE_LABEL_MODEL = "EDGE_LABEL_MODEL";
162   protected static final String VALUE_EDGE_LABEL_MODEL_BEST = "EDGE_LABEL_MODEL_BEST";
163   protected static final String VALUE_EDGE_LABEL_MODEL_AS_IS = "EDGE_LABEL_MODEL_AS_IS";
164   protected static final String VALUE_EDGE_LABEL_MODEL_CENTER_SLIDER = "EDGE_LABEL_MODEL_CENTER_SLIDER";
165   protected static final String VALUE_EDGE_LABEL_MODEL_SIDE_SLIDER = "EDGE_LABEL_MODEL_SIDE_SLIDER";
166   protected static final String VALUE_EDGE_LABEL_MODEL_FREE = "EDGE_LABEL_MODEL_FREE";
167   protected static final String ITEM_COMPACT_EDGE_LABEL_PLACEMENT = "COMPACT_EDGE_LABEL_PLACEMENT";
168 
169   //// Section 'Grouping'
170   protected static final String SECTION_GROUPING = "GROUPING";
171   // Section 'Grouping' items
172   protected static final String ITEM_GROUP_LAYERING_STRATEGY = "GROUP_LAYERING_STRATEGY";
173   protected static final String VALUE_GLOBAL_LAYERING = "GLOBAL_LAYERING";
174   protected static final String VALUE_RECURSIVE_LAYERING = "RECURSIVE_LAYERING";
175   protected static final String ITEM_GROUP_ENABLE_COMPACTION = "GROUP_ENABLE_COMPACTION";
176   protected static final String ITEM_GROUP_ALIGNMENT = "GROUP_ALIGNMENT";
177   protected static final String VALUE_GROUP_ALIGN_TOP = "GROUP_ALIGN_TOP";
178   protected static final String VALUE_GROUP_ALIGN_CENTER = "GROUP_ALIGN_CENTER";
179   protected static final String VALUE_GROUP_ALIGN_BOTTOM = "GROUP_ALIGN_BOTTOM";
180   protected static final String ITEM_GROUP_HORIZONTAL_COMPACTION = "GROUP_HORIZONTAL_COMPACTION";
181   protected static final String VALUE_GROUP_HORIZONTAL_COMPACTION_NONE = "GROUP_HORIZONTAL_COMPACTION_NONE";
182   protected static final String VALUE_GROUP_HORIZONTAL_COMPACTION_MAX = "GROUP_HORIZONTAL_COMPACTION_MAX";
183   
184   //// Section 'Swimlanes'
185   protected static final String SECTION_SWIMLANES = "SWIMLANES";
186   // Section 'Swimlanes' items
187   protected static final String ITEM_TREAT_ROOT_GROUPS_AS_SWIMLANES = "TREAT_ROOT_GROUPS_AS_SWIMLANES";
188   protected static final String ITEM_USE_ORDER_FROM_SKETCH = "USE_ORDER_FROM_SKETCH";
189   protected static final String ITEM_SWIMLANE_SPACING = "SWIMLANE_SPACING";
190 
191   //// Section 'Grid'
192   private static final String SECTION_GRID = "GRID";
193   // Section 'Grid' items
194   private static final String ITEM_GRID_ENABLED = "GRID_ENABLED";
195   private static final String ITEM_GRID_SPACING = "GRID_SPACING";
196   private static final String ITEM_GRID_PORT_ASSIGNMENT = "GRID_PORT_ASSIGNMENT";
197   private static final String VALUE_GRID_PORT_ASSIGNMENT_DEFAULT = "GRID_PORT_ASSIGNMENT_DEFAULT";
198   private static final String VALUE_GRID_PORT_ASSIGNMENT_ON_GRID = "GRID_PORT_ASSIGNMENT_ON_GRID";
199   private static final String VALUE_GRID_PORT_ASSIGNMENT_ON_SUBGRID = "GRID_PORT_ASSIGNMENT_ON_SUBGRID";
200   
201   /**
202    * Creates an instance of this module.
203    */
204   public IncrementalHierarchicLayoutModule() {
205     super(MODULE_INCREMENTAL_HIERARCHIC);
206     setPortIntersectionCalculatorEnabled(true);
207   }
208 
209   /**
210    * Creates an OptionHandler and adds the option items used by this module.
211    * @return the created <code>OptionHandler</code> providing module related options
212    */
213   protected OptionHandler createOptionHandler() {
214     final OptionHandler options = new OptionHandler(getModuleName());
215     final ConstraintManager optionConstraints = new ConstraintManager(options);
216 
217     //// Section 'General'
218     options.useSection(SECTION_GENERAL);
219     // Group 'Interactive Settings'
220     final OptionGroup interactionGroup = new OptionGroup();
221     interactionGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_INTERACTION);
222     // Populate group
223     final OptionItem itemSelectedElementsIncrementally = interactionGroup.addItem(
224         options.addBool(ITEM_SELECTED_ELEMENTS_INCREMENTALLY, false));
225     final OptionItem itemUseDrawingAsSketch = interactionGroup.addItem(
226         options.addBool(ITEM_USE_DRAWING_AS_SKETCH, false));
227 
228     // General settings
229     // Populate section
230     options.addEnum(ITEM_ORIENTATION, new String[]{
231         VALUE_TOP_TO_BOTTOM,
232         VALUE_LEFT_TO_RIGHT,
233         VALUE_BOTTOM_TO_TOP,
234         VALUE_RIGHT_TO_LEFT
235     }, 0);
236     options.addBool(ITEM_LAYOUT_COMPONENTS_SEPARATELY, false);
237     final OptionItem itemSymmetricPlacement = options.addBool(ITEM_SYMMETRIC_PLACEMENT, true);
238     options.addInt(ITEM_MAXIMAL_DURATION, 5);
239 
240     // Group 'Minimum Distances'
241     final OptionGroup distancesGroup = new OptionGroup();
242     distancesGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_MINIMUM_DISTANCES);
243     // Populate group
244     distancesGroup.addItem(options.addDouble(ITEM_NODE_TO_NODE_DISTANCE, 30.0d));
245     distancesGroup.addItem(options.addDouble(ITEM_NODE_TO_EDGE_DISTANCE, 15.0d));
246     distancesGroup.addItem(options.addDouble(ITEM_EDGE_TO_EDGE_DISTANCE, 15.0d));
247     distancesGroup.addItem(options.addDouble(ITEM_MINIMUM_LAYER_DISTANCE, 10.0d));
248 
249     //// Section 'Edges'
250     options.useSection(SECTION_EDGE_SETTINGS);
251     // Populate section
252     final EnumOptionItem itemEdgeRouting = options.addEnum(ITEM_EDGE_ROUTING, new String[]{
253         VALUE_EDGE_ROUTING_ORTHOGONAL,
254         VALUE_EDGE_ROUTING_POLYLINE,
255         VALUE_EDGE_ROUTING_OCTILINEAR
256     }, 0);
257     final OptionItem itemBackloopRouting = options.addBool(ITEM_BACKLOOP_ROUTING, false);
258     final OptionItem itemBackloopRoutingSelfloops = options.addBool(ITEM_BACKLOOP_ROUTING_SELFLOOPS, false);
259     options.addBool(ITEM_AUTOMATIC_EDGE_GROUPING_ENABLED, false);
260     options.addDouble(ITEM_MINIMUM_FIRST_SEGMENT_LENGTH, 10.0d);
261     options.addDouble(ITEM_MINIMUM_LAST_SEGMENT_LENGTH, 15.0d);
262     options.addDouble(ITEM_MINIMUM_EDGE_LENGTH, 20.0d);
263     options.addDouble(ITEM_MINIMUM_EDGE_DISTANCE, 15.0d);
264     options.addBool(ITEM_PC_OPTIMIZATION_ENABLED, false);
265     final OptionItem itemEdgeStraighteningOptimization =
266         options.addBool(ITEM_EDGE_STRAIGHTENING_OPTIMIZATION_ENABLED, false);
267     final OptionItem itemMinimumSlope = options.addDouble(ITEM_MINIMUM_SLOPE, 0.25d, 0.0d, 5.0d, 2);
268     final OptionItem recursiveEdgeRouting = options.addEnum(ITEM_RECURSIVE_EDGE_ROUTING, new String[]{
269             VALUE_RECURSIVE_EDGE_ROUTING_OFF,
270             VALUE_RECURSIVE_EDGE_ROUTING_DIRECTED,
271             VALUE_RECURSIVE_EDGE_ROUTING_UNDIRECTED
272     }, 0);
273     options.addBool(ITEM_CONSIDER_EDGE_THICKNESS, true);
274     options.addBool(ITEM_CONSIDER_EDGE_DIRECTION, true);
275 
276     // disable recursive edges if from-sketch mode or incremental mode is enabled
277     final Condition constraint =
278             optionConstraints.createConditionValueEquals(itemUseDrawingAsSketch, Boolean.FALSE).and(
279                     optionConstraints.createConditionValueEquals(itemSelectedElementsIncrementally, Boolean.FALSE));
280     optionConstraints.setEnabledOnCondition(constraint, recursiveEdgeRouting);
281     
282     // Enable items on specific settings
283     optionConstraints.setEnabledOnValueEquals(itemSymmetricPlacement, Boolean.FALSE,
284         itemEdgeStraighteningOptimization);
285     optionConstraints.setEnabledOnValueEquals(itemBackloopRouting, Boolean.TRUE, itemBackloopRoutingSelfloops);
286     optionConstraints.setEnabledOnValueEquals(itemEdgeRouting, VALUE_EDGE_ROUTING_POLYLINE, itemMinimumSlope);
287  
288     //// Section 'Layers'
289     options.useSection(SECTION_RANKS);
290     // Populate section
291     final EnumOptionItem itemRankingPolicy = options.addEnum(ITEM_RANKING_POLICY, new String[]{
292         VALUE_HIERARCHICAL_OPTIMAL,
293         VALUE_HIERARCHICAL_TIGHT_TREE_HEURISTIC,
294         VALUE_BFS_LAYERS,
295         VALUE_FROM_SKETCH,
296         VALUE_HIERARCHICAL_TOPMOST
297     }, 0);
298     options.addEnum(ITEM_LAYER_ALIGNMENT, new String[]{
299         VALUE_TOP,
300         VALUE_CENTER,
301         VALUE_BOTTOM
302     }, 1);
303     options.addBool(ITEM_NODE_COMPACTION_ENABLED, false);
304     options.addEnum(ITEM_COMPONENT_ARRANGEMENT_POLICY, new String[]{
305         VALUE_POLICY_COMPACT,
306         VALUE_POLICY_TOPMOST
307     }, 1);
308     
309     // Group 'From Sketch Settings'
310     final OptionGroup sketchPropertyGroup = new OptionGroup();
311     sketchPropertyGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_FROM_SKETCH_PROPERTIES);
312     // Populate group
313     sketchPropertyGroup.addItem(options.addDouble(ITEM_SCALE, 1.0d, 0.0d, 5.0d, 1));
314     sketchPropertyGroup.addItem(options.addDouble(ITEM_HALO, 0.0d));
315     final OptionItem itemMinimumSize = sketchPropertyGroup.addItem(options.addDouble(ITEM_MINIMUM_SIZE, 0.0d));
316     itemMinimumSize.setAttribute(DoubleOptionItem.ATTRIBUTE_MIN_VALUE, new Double(0.0));
317     final OptionItem itemMaximumSize = sketchPropertyGroup.addItem(options.addDouble(ITEM_MAXIMUM_SIZE, 1000.0d));
318     itemMaximumSize.setAttribute(DoubleOptionItem.ATTRIBUTE_MIN_VALUE, new Double(0.0));
319     // Enable/disable items depending on specific values
320     Condition c =
321         optionConstraints.createConditionValueEquals(itemUseDrawingAsSketch, Boolean.FALSE).and(
322             optionConstraints.createConditionValueEquals(itemSelectedElementsIncrementally, Boolean.FALSE));
323     optionConstraints.setEnabledOnCondition(c, itemRankingPolicy);
324     c = c.inverse().or(optionConstraints.createConditionValueEquals(itemRankingPolicy, VALUE_FROM_SKETCH));
325     optionConstraints.setEnabledOnCondition(c, sketchPropertyGroup);
326 
327     //// Section 'Labeling'
328     options.useSection(SECTION_LABELING);
329     // Group 'Node settings'
330     final OptionGroup nodePropertyGroup = new OptionGroup();
331     nodePropertyGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_NODE_PROPERTIES);
332     // Populate group
333     nodePropertyGroup.addItem(options.addBool(ITEM_CONSIDER_NODE_LABELS, true));
334     
335     // Group 'Edge settings'
336     final OptionGroup edgePropertyGroup = new OptionGroup();
337     edgePropertyGroup.setAttribute(OptionGroup.ATTRIBUTE_TITLE, TITLE_EDGE_PROPERTIES);
338     // Populate group
339     final EnumOptionItem itemEdgeLabeling = options.addEnum(ITEM_EDGE_LABELING, new String[]{
340         VALUE_EDGE_LABELING_NONE,
341         VALUE_EDGE_LABELING_GENERIC,
342         VALUE_EDGE_LABELING_HIERARCHIC
343     }, 0);
344     edgePropertyGroup.addItem(itemEdgeLabeling);
345     final OptionItem itemEdgeLabelModel = edgePropertyGroup.addItem(
346         options.addEnum(ITEM_EDGE_LABEL_MODEL, new String[]{
347             VALUE_EDGE_LABEL_MODEL_BEST,
348             VALUE_EDGE_LABEL_MODEL_AS_IS,
349             VALUE_EDGE_LABEL_MODEL_CENTER_SLIDER,
350             VALUE_EDGE_LABEL_MODEL_SIDE_SLIDER,
351             VALUE_EDGE_LABEL_MODEL_FREE,
352         }, 0));
353     final OptionItem itemEdgeLabelPlacement = options.addBool(ITEM_COMPACT_EDGE_LABEL_PLACEMENT, true);
354     // Enable/disable items depending on specific values
355     optionConstraints.setEnabledOnValueEquals(itemEdgeLabeling, VALUE_EDGE_LABELING_NONE, itemEdgeLabelModel, true);
356     optionConstraints.setEnabledOnValueEquals(itemEdgeLabeling, VALUE_EDGE_LABELING_HIERARCHIC,
357         edgePropertyGroup.addItem(itemEdgeLabelPlacement));
358 
359     //// Section 'Grouping'
360     options.useSection(SECTION_GROUPING);
361     // Populate section
362     final EnumOptionItem itemGroupLayeringStrategy = options.addEnum(ITEM_GROUP_LAYERING_STRATEGY, new String[]{
363         VALUE_GLOBAL_LAYERING,
364         VALUE_RECURSIVE_LAYERING
365     }, 0);
366     final OptionItem itemGroupEnableCompaction = options.addBool(ITEM_GROUP_ENABLE_COMPACTION, true);
367     final EnumOptionItem itemGroupAlignment = options.addEnum(ITEM_GROUP_ALIGNMENT, new String[]{
368         VALUE_GROUP_ALIGN_TOP,
369         VALUE_GROUP_ALIGN_CENTER,
370         VALUE_GROUP_ALIGN_BOTTOM
371     }, 0);
372     options.addEnum(ITEM_GROUP_HORIZONTAL_COMPACTION, new String[]{
373         VALUE_GROUP_HORIZONTAL_COMPACTION_NONE,
374         VALUE_GROUP_HORIZONTAL_COMPACTION_MAX
375     }, 1);
376     // Enable/disable items depending on specific values
377     optionConstraints.setEnabledOnValueEquals(itemUseDrawingAsSketch, Boolean.FALSE, itemGroupLayeringStrategy);
378     optionConstraints.setEnabledOnValueEquals(itemGroupLayeringStrategy, VALUE_RECURSIVE_LAYERING, itemGroupEnableCompaction);
379     optionConstraints.setEnabledOnValueEquals(itemGroupLayeringStrategy, VALUE_RECURSIVE_LAYERING, itemGroupAlignment);
380     optionConstraints.setEnabledOnCondition(
381         optionConstraints.createConditionValueEquals(itemGroupLayeringStrategy, VALUE_RECURSIVE_LAYERING).and(
382             optionConstraints.createConditionValueEquals(itemGroupEnableCompaction, Boolean.TRUE).inverse()
383         ), itemGroupAlignment);
384     optionConstraints.setEnabledOnCondition(
385         optionConstraints.createConditionValueEquals(itemGroupLayeringStrategy, VALUE_RECURSIVE_LAYERING).and(
386             optionConstraints.createConditionValueEquals(itemUseDrawingAsSketch, Boolean.FALSE)
387         ), itemGroupEnableCompaction);
388     
389     //// Section 'Swimlanes'
390     options.useSection(SECTION_SWIMLANES);
391     // Populate section
392     final OptionItem swimlaneOption = options.addBool(ITEM_TREAT_ROOT_GROUPS_AS_SWIMLANES, false);
393     final OptionItem fromSketchOption = options.addBool(ITEM_USE_ORDER_FROM_SKETCH, false);
394     final OptionItem spacingOption = options.addDouble(ITEM_SWIMLANE_SPACING, 0.0d);
395     spacingOption.setAttribute(DoubleOptionItem.ATTRIBUTE_MIN_VALUE, new Double(0.0));
396     // Enable/disable items depending on specific values
397     optionConstraints.setEnabledOnValueEquals(swimlaneOption, Boolean.TRUE, fromSketchOption);
398     optionConstraints.setEnabledOnValueEquals(swimlaneOption, Boolean.TRUE, spacingOption);
399 
400     //// Section 'Grid'
401     options.useSection(SECTION_GRID);
402     // Populate section
403     final OptionItem grid = options.addBool(ITEM_GRID_ENABLED, false);
404     final OptionItem gridSpacing = options.addDouble(ITEM_GRID_SPACING, 10, 1, 100, 1);
405     final OptionItem portAssignment = options.addEnum(ITEM_GRID_PORT_ASSIGNMENT, new String[]{
406         VALUE_GRID_PORT_ASSIGNMENT_DEFAULT,
407         VALUE_GRID_PORT_ASSIGNMENT_ON_GRID,
408         VALUE_GRID_PORT_ASSIGNMENT_ON_SUBGRID,
409     }, 0);
410     optionConstraints.setEnabledOnValueEquals(grid, Boolean.TRUE, gridSpacing);
411     optionConstraints.setEnabledOnValueEquals(grid, Boolean.TRUE, portAssignment);
412 
413     return options;
414   }
415 
416   /**
417    * Main module execution routine.
418    * Launches the module's underlying algorithm on the module's graph based on user options.
419    */
420   protected void mainrun() {
421     final IncrementalHierarchicLayouter hierarchic = new IncrementalHierarchicLayouter();
422     
423     final OptionHandler options = getOptionHandler();
424     configure(hierarchic, options);
425 
426     final Graph2D graph = getGraph2D();
427 
428     final boolean incrementalLayout = options.getBool(ITEM_SELECTED_ELEMENTS_INCREMENTALLY);
429     final boolean selectedElements = graph.selectedEdges().ok() || graph.selectedNodes().ok();
430     if (incrementalLayout && selectedElements) {
431       // mark incremental elements if required
432       // create storage for both nodes and edges
433       final DataMap incrementalElements = Maps.createHashedDataMap();
434       
435       final IncrementalHintsFactory ihf = hierarchic.createIncrementalHintsFactory();
436 
437       final HierarchyManager hm = graph.getHierarchyManager();
438       for (NodeCursor nc = graph.selectedNodes(); nc.ok(); nc.next()) {
439         final Node n = nc.node();
440         if (hm != null && hm.isGroupNode(n)) {
441           incrementalElements.set(n, ihf.createIncrementalGroupHint(n));
442         } else {
443           incrementalElements.set(n, ihf.createLayerIncrementallyHint(n));
444         }
445       }
446 
447       for (EdgeCursor ec = graph.selectedEdges(); ec.ok(); ec.next()) {
448         incrementalElements.set(ec.edge(), ihf.createSequenceIncrementallyHint(ec.edge()));
449       }
450       // backup existing data providers to prevent loss of user settings
451       backupDataProvider(graph, IncrementalHierarchicLayouter.INCREMENTAL_HINTS_DPKEY);
452       graph.addDataProvider(IncrementalHierarchicLayouter.INCREMENTAL_HINTS_DPKEY, incrementalElements);
453     }
454 
455     final boolean gridEnabled = options.getBool(ITEM_GRID_ENABLED);
456     if (gridEnabled) {
457       // backup existing data providers to prevent loss of user settings
458       backupDataProvider(graph, HierarchicLayouter.NODE_LAYOUT_DESCRIPTOR_DPKEY);
459 
460       final String portAssignment = options.getString(ITEM_GRID_PORT_ASSIGNMENT);
461       final byte gridPortAssignment;
462       if (VALUE_GRID_PORT_ASSIGNMENT_ON_GRID.equals(portAssignment)) {
463         gridPortAssignment = NodeLayoutDescriptor.PORT_ASSIGNMENT_ON_GRID;
464       } else if (VALUE_GRID_PORT_ASSIGNMENT_ON_SUBGRID.equals(portAssignment)) {
465         gridPortAssignment = NodeLayoutDescriptor.PORT_ASSIGNMENT_ON_SUBGRID;
466       } else {
467         gridPortAssignment = NodeLayoutDescriptor.PORT_ASSIGNMENT_DEFAULT;
468       }
469 
470       final NodeLayoutDescriptor nld = hierarchic.getNodeLayoutDescriptor();
471       graph.addDataProvider(HierarchicLayouter.NODE_LAYOUT_DESCRIPTOR_DPKEY, new DataProviderAdapter() {
472         public Object get(Object dataHolder) {
473           if (dataHolder instanceof Node) {
474             final Node node = (Node) dataHolder;
475             // copy descriptor to keep all settings for this node
476             final NodeLayoutDescriptor descriptor = new NodeLayoutDescriptor();
477             descriptor.setLayerAlignment(nld.getLayerAlignment());
478             descriptor.setMinimumDistance(nld.getMinimumDistance());
479             descriptor.setMinimumLayerHeight(nld.getMinimumLayerHeight());
480             descriptor.setNodeLabelMode(nld.getNodeLabelMode());
481             // anchor nodes on grid according to their alignment within the layer
482             descriptor.setGridReference(new YPoint(0.0, (nld.getLayerAlignment() - 0.5) * graph.getHeight(node)));
483             descriptor.setPortAssignment(gridPortAssignment);
484             return descriptor;
485           }
486           return null;
487         }
488       });
489     }
490     
491     //if recursive routing is enabled, mark the folder nodes
492     final boolean recursiveRouting = options.get(ITEM_RECURSIVE_EDGE_ROUTING) != VALUE_RECURSIVE_EDGE_ROUTING_OFF;
493     if (recursiveRouting) {
494       //backup existing data provider
495       backupDataProvider(graph, IncrementalHierarchicLayouter.FOLDER_NODES_DPKEY);
496       final NodeMap node2IsFolder = Maps.createHashedNodeMap();
497       final HierarchyManager hm = graph.getHierarchyManager();
498       for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
499         final Node node = nc.node();
500         if (hm.isFolderNode(node)) {
501           node2IsFolder.setBool(node, true);
502         }
503       }
504       graph.addDataProvider(IncrementalHierarchicLayouter.FOLDER_NODES_DPKEY, node2IsFolder);
505     }
506 
507     prepareGraph(graph, options);
508     try {
509       // launch layouter in buffered mode
510       launchLayouter(hierarchic);
511     } finally {
512       restoreGraph(graph, options);
513 
514       if (gridEnabled) {
515         // remove the data providers set by this module by restoring the initial state
516         restoreDataProvider(graph, HierarchicLayouter.NODE_LAYOUT_DESCRIPTOR_DPKEY);
517       }
518 
519       if (incrementalLayout && selectedElements) {
520         // remove the data providers set by this module by restoring the initial state
521         restoreDataProvider(graph, IncrementalHierarchicLayouter.INCREMENTAL_HINTS_DPKEY);
522       }
523       
524       if (recursiveRouting) {
525         // remove the data providers set by this module by restoring the initial state
526         restoreDataProvider(graph, IncrementalHierarchicLayouter.FOLDER_NODES_DPKEY);
527       }
528     }
529   }
530 
531   /**
532    * Prepares a <code>graph</code> depending on the given options for the
533    * module's layout algorithm.
534    * <br>
535    * Additional resources created by this method have to be freed up by calling
536    * {@link #restoreGraph(y.view.Graph2D, y.option.OptionHandler)} after
537    * layout calculation.  
538    * @param graph the graph to be prepared
539    * @param options the options for the module's layout algorithm
540    */
541   protected void prepareGraph(final Graph2D graph, final OptionHandler options) {
542     if (options.getString(ITEM_RANKING_POLICY).equals(VALUE_BFS_LAYERS)) {
543       // backup existing data providers to prevent loss of user settings
544       backupDataProvider(graph, BFSLayerer.CORE_NODES);
545       graph.addDataProvider(BFSLayerer.CORE_NODES, Selections.createSelectionNodeMap(graph));
546     }
547     if (options.getBool(ITEM_CONSIDER_EDGE_THICKNESS)) {
548       // backup existing data providers to prevent loss of user settings
549       backupDataProvider(graph, IncrementalHierarchicLayouter.EDGE_THICKNESS_DPKEY);
550       graph.addDataProvider(
551               IncrementalHierarchicLayouter.EDGE_THICKNESS_DPKEY,
552               new EdgeWidthProvider(graph));
553     }
554     if (options.getBool(ITEM_CONSIDER_EDGE_DIRECTION)) {
555       // backup existing data providers to prevent loss of user settings
556       backupDataProvider(graph, IncrementalHierarchicLayouter.EDGE_DIRECTEDNESS_DPKEY);
557       graph.addDataProvider(
558           IncrementalHierarchicLayouter.EDGE_DIRECTEDNESS_DPKEY,
559           new EdgeDirectednessProvider(graph));
560     }
561     final String el = options.getString(ITEM_EDGE_LABELING);
562     if (!el.equals(VALUE_EDGE_LABELING_NONE)) {
563       setupEdgeLabelModel(graph, el, options.getString(ITEM_EDGE_LABEL_MODEL));
564     }
565   }
566 
567   /**
568    * Restores the given <code>graph</code> by freeing up resources created by
569    * {@link #prepareGraph(y.view.Graph2D, y.option.OptionHandler)}.
570    * @param graph the graph for which <code>prepareGraph</code> has been called
571    * @param options the options for the module's layout algorithm
572    */
573   protected void restoreGraph(final Graph2D graph, final OptionHandler options) {
574     if (options.getBool(ITEM_CONSIDER_EDGE_THICKNESS)) {
575       // remove the data providers set by this module by restoring the initial state
576       restoreDataProvider(graph, IncrementalHierarchicLayouter.EDGE_THICKNESS_DPKEY);
577     }
578     if (options.getBool(ITEM_CONSIDER_EDGE_DIRECTION)) {
579       // remove the data providers set by this module by restoring the initial state
580       restoreDataProvider(graph, IncrementalHierarchicLayouter.EDGE_DIRECTEDNESS_DPKEY);
581     }
582     if (options.getString(ITEM_RANKING_POLICY).equals(VALUE_BFS_LAYERS)) {
583       // remove the data providers set by this module by restoring the initial state
584       restoreDataProvider(graph, BFSLayerer.CORE_NODES);
585     }
586   }
587 
588   /**
589    * Configures the module's layout algorithm according to the given options.
590    * <p>
591    * Important: This method does also depend on the <code>Graph2D</code>
592    * of this module in addition to the method's parameters.
593    * </p>
594    * @param hierarchic the <code>IncrementalHierarchicLayouter</code> to be configured
595    * @param options the layout options to set
596    */
597   protected void configure(final IncrementalHierarchicLayouter hierarchic, final OptionHandler options) {
598     final Graph2D graph = getGraph2D();
599 
600     boolean incrementalLayout = options.getBool(ITEM_SELECTED_ELEMENTS_INCREMENTALLY);
601     boolean selectedElements = graph.selectedEdges().ok() || graph.selectedNodes().ok();
602     // configure the mode
603     final boolean isIncrementalModeEnabled = (incrementalLayout && selectedElements) || options.getBool(ITEM_USE_DRAWING_AS_SKETCH);
604     if (isIncrementalModeEnabled) {
605       hierarchic.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_INCREMENTAL);
606     } else {
607       hierarchic.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_FROM_SCRATCH);
608     }
609     
610     // cast to implementation simplex
611     final SimplexNodePlacer np = (SimplexNodePlacer) hierarchic.getNodePlacer();
612     np.setBaryCenterModeEnabled(options.getBool(ITEM_SYMMETRIC_PLACEMENT));
613     np.setEdgeStraighteningOptimizationEnabled(
614         options.getBool(ITEM_EDGE_STRAIGHTENING_OPTIMIZATION_ENABLED));
615 
616     if (VALUE_GROUP_HORIZONTAL_COMPACTION_NONE.equals(options.getString(ITEM_GROUP_HORIZONTAL_COMPACTION))) {
617       np.setGroupCompactionStrategy(SimplexNodePlacer.GROUP_COMPACTION_NONE);
618     } else if (VALUE_GROUP_HORIZONTAL_COMPACTION_MAX.equals(options.getString(ITEM_GROUP_HORIZONTAL_COMPACTION))) {
619       np.setGroupCompactionStrategy(SimplexNodePlacer.GROUP_COMPACTION_MAX);
620     }
621 
622     hierarchic.setComponentLayouterEnabled(options.getBool(ITEM_LAYOUT_COMPONENTS_SEPARATELY));
623     hierarchic.setMinimumLayerDistance(options.getDouble(ITEM_MINIMUM_LAYER_DISTANCE));
624     hierarchic.setNodeToEdgeDistance(options.getDouble(ITEM_NODE_TO_EDGE_DISTANCE));
625     hierarchic.setNodeToNodeDistance(options.getDouble(ITEM_NODE_TO_NODE_DISTANCE));
626     hierarchic.setEdgeToEdgeDistance(options.getDouble(ITEM_EDGE_TO_EDGE_DISTANCE));
627     hierarchic.setAutomaticEdgeGroupingEnabled(options.getBool(ITEM_AUTOMATIC_EDGE_GROUPING_ENABLED));
628 
629     final EdgeLayoutDescriptor eld = hierarchic.getEdgeLayoutDescriptor();
630     if (options.get(ITEM_EDGE_ROUTING) == VALUE_EDGE_ROUTING_OCTILINEAR) {
631       eld.setRoutingStyle(new RoutingStyle(RoutingStyle.EDGE_STYLE_OCTILINEAR));
632     } else if (options.get(ITEM_EDGE_ROUTING) == VALUE_EDGE_ROUTING_POLYLINE) {
633       eld.setRoutingStyle(new RoutingStyle(RoutingStyle.EDGE_STYLE_POLYLINE));
634     } else {
635       eld.setRoutingStyle(new RoutingStyle(RoutingStyle.EDGE_STYLE_ORTHOGONAL));
636     }
637 
638     eld.setMinimumFirstSegmentLength(options.getDouble(ITEM_MINIMUM_FIRST_SEGMENT_LENGTH));
639     eld.setMinimumLastSegmentLength(options.getDouble(ITEM_MINIMUM_LAST_SEGMENT_LENGTH));
640     eld.setMinimumDistance(options.getDouble(ITEM_MINIMUM_EDGE_DISTANCE));
641     eld.setMinimumLength(options.getDouble(ITEM_MINIMUM_EDGE_LENGTH));
642     eld.setMinimumSlope(options.getDouble(ITEM_MINIMUM_SLOPE));
643     eld.setSourcePortOptimizationEnabled(options.getBool(ITEM_PC_OPTIMIZATION_ENABLED));
644     eld.setTargetPortOptimizationEnabled(options.getBool(ITEM_PC_OPTIMIZATION_ENABLED));
645 
646     if (!isIncrementalModeEnabled && options.get(ITEM_RECURSIVE_EDGE_ROUTING) == VALUE_RECURSIVE_EDGE_ROUTING_DIRECTED) {
647       eld.setRecursiveEdgeStyle(EdgeLayoutDescriptor.RECURSIVE_EDGE_STYLE_DIRECTED);
648     } else if (!isIncrementalModeEnabled && options.get(ITEM_RECURSIVE_EDGE_ROUTING) == VALUE_RECURSIVE_EDGE_ROUTING_UNDIRECTED) {
649       eld.setRecursiveEdgeStyle(EdgeLayoutDescriptor.RECURSIVE_EDGE_STYLE_UNDIRECTED);
650     } else {
651       eld.setRecursiveEdgeStyle(EdgeLayoutDescriptor.RECURSIVE_EDGE_STYLE_OFF);
652     }
653 
654     final NodeLayoutDescriptor nld = hierarchic.getNodeLayoutDescriptor();
655     nld.setMinimumDistance(Math.min(hierarchic.getNodeToNodeDistance(), hierarchic.getNodeToEdgeDistance()));
656     nld.setMinimumLayerHeight(0);
657     if (options.get(ITEM_LAYER_ALIGNMENT).equals(VALUE_TOP)) {
658       nld.setLayerAlignment(0.0);
659     } else if (options.get(ITEM_LAYER_ALIGNMENT).equals(VALUE_CENTER)) {
660       nld.setLayerAlignment(0.5);
661     } else if (options.get(ITEM_LAYER_ALIGNMENT).equals(VALUE_BOTTOM)) {
662       nld.setLayerAlignment(1.0);
663     }
664 
665     if (options.get(ITEM_ORIENTATION).equals(VALUE_TOP_TO_BOTTOM)) {
666       hierarchic.setLayoutOrientation(OrientationLayouter.TOP_TO_BOTTOM);
667     } else if (options.get(ITEM_ORIENTATION).equals(VALUE_LEFT_TO_RIGHT)) {
668       hierarchic.setLayoutOrientation(OrientationLayouter.LEFT_TO_RIGHT);
669     } else if (options.get(ITEM_ORIENTATION).equals(VALUE_BOTTOM_TO_TOP)) {
670       hierarchic.setLayoutOrientation(OrientationLayouter.BOTTOM_TO_TOP);
671     } else if (options.get(ITEM_ORIENTATION).equals(VALUE_RIGHT_TO_LEFT)) {
672       hierarchic.setLayoutOrientation(OrientationLayouter.RIGHT_TO_LEFT);
673     }
674 
675     configureLabeling(hierarchic, options);
676 
677     if (options.getBool(ITEM_CONSIDER_NODE_LABELS)) {
678       hierarchic.setConsiderNodeLabelsEnabled(true);
679       hierarchic.getNodeLayoutDescriptor().setNodeLabelMode(NodeLayoutDescriptor.NODE_LABEL_MODE_CONSIDER_FOR_DRAWING);
680     } else {
681       hierarchic.setConsiderNodeLabelsEnabled(false);
682     }
683 
684     final String rp = options.getString(ITEM_RANKING_POLICY);
685     if (rp.equals(VALUE_FROM_SKETCH)) {
686       hierarchic.setFromScratchLayeringStrategy(IncrementalHierarchicLayouter.LAYERING_STRATEGY_FROM_SKETCH);
687     } else if (rp.equals(VALUE_HIERARCHICAL_OPTIMAL)) {
688       hierarchic.setFromScratchLayeringStrategy(IncrementalHierarchicLayouter.LAYERING_STRATEGY_HIERARCHICAL_OPTIMAL);
689     } else if (rp.equals(VALUE_HIERARCHICAL_TIGHT_TREE_HEURISTIC)) {
690       hierarchic.setFromScratchLayeringStrategy(IncrementalHierarchicLayouter.LAYERING_STRATEGY_HIERARCHICAL_TIGHT_TREE);
691     } else if (rp.equals(VALUE_HIERARCHICAL_TOPMOST)) {
692       hierarchic.setFromScratchLayeringStrategy(IncrementalHierarchicLayouter.LAYERING_STRATEGY_HIERARCHICAL_TOPMOST);
693     } else if (rp.equals(VALUE_BFS_LAYERS)) {
694       hierarchic.setFromScratchLayeringStrategy(IncrementalHierarchicLayouter.LAYERING_STRATEGY_BFS);
695     }
696 
697     if (options.getString(ITEM_COMPONENT_ARRANGEMENT_POLICY).equals(VALUE_POLICY_COMPACT)) {
698       hierarchic.setComponentArrangementPolicy(IncrementalHierarchicLayouter.COMPONENT_ARRANGEMENT_COMPACT);
699     } else {
700       hierarchic.setComponentArrangementPolicy(IncrementalHierarchicLayouter.COMPONENT_ARRANGEMENT_TOPMOST);
701     }
702 
703     ((SimplexNodePlacer) hierarchic.getNodePlacer()).setNodeCompactionEnabled(options.getBool(ITEM_NODE_COMPACTION_ENABLED));
704 
705     //configure AsIsLayerer
706     Object layerer = (hierarchic.getLayoutMode() == IncrementalHierarchicLayouter.LAYOUT_MODE_FROM_SCRATCH) ?
707         hierarchic.getFromScratchLayerer() : hierarchic.getFixedElementsLayerer();
708     if (layerer instanceof OldLayererWrapper) {
709       layerer = ((OldLayererWrapper) layerer).getOldLayerer();
710     }
711     if (layerer instanceof AsIsLayerer) {
712       AsIsLayerer ail = (AsIsLayerer) layerer;
713       ail.setNodeHalo(options.getDouble(ITEM_HALO));
714       ail.setNodeScalingFactor(options.getDouble(ITEM_SCALE));
715       ail.setMinimumNodeSize(options.getDouble(ITEM_MINIMUM_SIZE));
716       ail.setMaximumNodeSize(options.getDouble(ITEM_MAXIMUM_SIZE));
717     }
718 
719     if (!options.getBool(ITEM_USE_DRAWING_AS_SKETCH) &&
720         options.getString(ITEM_GROUP_LAYERING_STRATEGY).equals(VALUE_RECURSIVE_LAYERING)) {
721       byte alignmentPolicy = IncrementalHierarchicLayouter.POLICY_ALIGN_GROUPS_TOP;
722       if (options.getString(ITEM_GROUP_ALIGNMENT).equals(VALUE_GROUP_ALIGN_CENTER)) {
723         alignmentPolicy = IncrementalHierarchicLayouter.POLICY_ALIGN_GROUPS_CENTER;
724       } else if (options.getString(ITEM_GROUP_ALIGNMENT).equals(VALUE_GROUP_ALIGN_BOTTOM)) {
725         alignmentPolicy = IncrementalHierarchicLayouter.POLICY_ALIGN_GROUPS_BOTTOM;
726       }
727       hierarchic.setGroupCompactionEnabled(options.getBool(ITEM_GROUP_ENABLE_COMPACTION));
728       hierarchic.setGroupAlignmentPolicy(alignmentPolicy);
729       hierarchic.setRecursiveGroupLayeringEnabled(true);
730     } else {
731       hierarchic.setRecursiveGroupLayeringEnabled(false);
732     }
733 
734     if (options.getBool(SECTION_SWIMLANES, ITEM_TREAT_ROOT_GROUPS_AS_SWIMLANES)) {
735       final TopLevelGroupToSwimlaneStage stage = new TopLevelGroupToSwimlaneStage();
736       stage.setFromSketchSwimlaneOrderingEnabled(options.getBool(SECTION_SWIMLANES, ITEM_USE_ORDER_FROM_SKETCH));
737       stage.setSpacing(options.getDouble(SECTION_SWIMLANES, ITEM_SWIMLANE_SPACING));
738       hierarchic.appendStage(stage);
739     }
740 
741     hierarchic.setBackloopRoutingEnabled(options.getBool(ITEM_BACKLOOP_ROUTING));
742     hierarchic.setBackloopRoutingForSelfloopsEnabled(options.getBool(ITEM_BACKLOOP_ROUTING_SELFLOOPS));
743     hierarchic.setMaximalDuration(options.getInt(ITEM_MAXIMAL_DURATION) * 1000);
744 
745 
746     final boolean gridEnabled = options.getBool(ITEM_GRID_ENABLED);
747     if (gridEnabled) {
748       hierarchic.setGridSpacing(options.getDouble(ITEM_GRID_SPACING));
749     }
750   }
751 
752   private void configureLabeling(final IncrementalHierarchicLayouter hierarchic, final OptionHandler options) {
753     final String el = options.getString(ITEM_EDGE_LABELING);
754     if (!el.equals(VALUE_EDGE_LABELING_NONE)) {
755       if (el.equals(VALUE_EDGE_LABELING_GENERIC)) {
756         final GreedyMISLabeling la = new GreedyMISLabeling();
757         la.setPlaceNodeLabels(false);
758         la.setPlaceEdgeLabels(true);
759         la.setAutoFlippingEnabled(true);
760         la.setProfitModel(new LabelRanking());
761         //cannot be set as label layouter (see note on method setConsiderNodeLabelsEnabled of IncrementalHierarchicLayouter)
762         hierarchic.prependStage(la);
763       } else if (el.equals(VALUE_EDGE_LABELING_HIERARCHIC)) {
764         final boolean compactEdgeLabelPlacement = options.getBool(ITEM_COMPACT_EDGE_LABEL_PLACEMENT);
765         if (hierarchic.getNodePlacer() instanceof SimplexNodePlacer) {
766           ((SimplexNodePlacer) hierarchic.getNodePlacer()).setLabelCompactionEnabled(compactEdgeLabelPlacement);
767         }
768         hierarchic.setIntegratedEdgeLabelingEnabled(true);
769       }
770     } else {
771       hierarchic.setIntegratedEdgeLabelingEnabled(false);
772     }
773   }
774 
775   private void setupEdgeLabelModel(final Graph2D graph, final String edgeLabeling, String edgeLabelModel) {
776     if (edgeLabeling.equals(VALUE_EDGE_LABELING_NONE) || edgeLabelModel.equals(VALUE_EDGE_LABEL_MODEL_AS_IS)) {
777       return; //nothing to do
778     }
779 
780     if (edgeLabelModel.equals(VALUE_EDGE_LABEL_MODEL_BEST)) {
781       if (edgeLabeling.equals(VALUE_EDGE_LABELING_GENERIC)) {
782         edgeLabelModel = VALUE_EDGE_LABEL_MODEL_SIDE_SLIDER;
783       } else if (edgeLabeling.equals(VALUE_EDGE_LABELING_HIERARCHIC)) {
784         edgeLabelModel = VALUE_EDGE_LABEL_MODEL_FREE;
785       }
786     }
787 
788     byte model = EdgeLabel.SIDE_SLIDER;
789     byte preferredSide = LabelLayoutConstants.PLACE_RIGHT_OF_EDGE;
790     if (edgeLabelModel.equals(VALUE_EDGE_LABEL_MODEL_CENTER_SLIDER)) {
791       model = EdgeLabel.CENTER_SLIDER;
792       preferredSide = LabelLayoutConstants.PLACE_ON_EDGE;
793     } else if (edgeLabelModel.equals(VALUE_EDGE_LABEL_MODEL_FREE)) {
794       model = EdgeLabel.FREE;
795       preferredSide = LabelLayoutConstants.PLACE_ON_EDGE;
796     }
797 
798     assignLabelModel(graph, model, preferredSide, !edgeLabelModel.equals(VALUE_EDGE_LABEL_MODEL_FREE));
799   }
800 
801   private void assignLabelModel(
802       final Graph2D graph,
803       final byte model,
804       final byte preferredSide,
805       final boolean setPreferredSize
806   ) {
807     for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
808       final Edge e = ec.edge();
809       EdgeRealizer er = graph.getRealizer(e);
810       for (int i = 0; i < er.labelCount(); i++) {
811         final EdgeLabel el = er.getLabel(i);
812         el.setModel(model);
813         if (setPreferredSize) {
814           setPreferredSide(el, preferredSide);
815         }
816       }
817     }
818   }
819 
820 
821   private void setPreferredSide(final EdgeLabel el, final byte preferredSide) {
822     final PreferredPlacementDescriptor oldDescriptor =
823             el.getPreferredPlacementDescriptor();
824     if (oldDescriptor.getSideOfEdge() != preferredSide) {
825       final PreferredPlacementDescriptor newDescriptor =
826               new PreferredPlacementDescriptor(oldDescriptor);
827       newDescriptor.setSideOfEdge(preferredSide);
828       el.setPreferredPlacementDescriptor(newDescriptor);
829     }
830   }
831 
832 
833   private static final class EdgeDirectednessProvider extends DataProviderAdapter {
834     final Graph2D graph;
835 
836     EdgeDirectednessProvider(final Graph2D graph) {
837       this.graph = graph;
838     }
839 
840     public double getDouble(final Object dataHolder) {
841       try {
842         final EdgeRealizer realizer = graph.getRealizer((Edge) dataHolder);
843         if (realizer.getSourceArrow() == Arrow.NONE && realizer.getTargetArrow() != Arrow.NONE) {
844           //edge has some arrow at the target and none at the source -> consider as directed
845           return 1;
846         } else if (realizer.getTargetArrow() == Arrow.NONE && realizer.getSourceArrow() != Arrow.NONE) {
847           //edge has some arrow at the source and none at the target -> consider as reversed directed
848           return -1;
849         } else {
850           //edge has no arrows at all or arrows on both ends -> consider as undirected
851           return 0;
852         }
853       } catch (Exception e) {
854         //by default all edges are considered to be directed from source to target
855         return 1;
856       }
857     }
858   }
859 
860   private static final class EdgeWidthProvider extends DataProviderAdapter {
861     final Graph2D graph;
862 
863     EdgeWidthProvider( final Graph2D graph ) {
864       this.graph = graph;
865     }
866 
867     public double getDouble( final Object dataHolder ) {
868       try {
869         final EdgeRealizer er = graph.getRealizer((Edge) dataHolder);
870         return er.getLineType().getLineWidth();
871       } catch (Exception e) {
872         return 0;
873       }
874     }
875   }
876 }
877