1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.9. 
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-2011 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.layout.module;
15  
16  import y.module.LayoutModule;
17  import y.module.YModule;
18  
19  import y.layout.Layouter;
20  import y.layout.router.EdgeGroupRouterStage;
21  import y.layout.router.GroupNodeRouterStage;
22  import y.layout.router.OrthogonalEdgeRouter;
23  import y.layout.router.PatchRouterStage;
24  import y.layout.router.ReducedSphereOfActionStage;
25  import y.option.ConstraintManager;
26  import y.option.IntOptionItem;
27  import y.option.OptionGroup;
28  import y.option.OptionHandler;
29  
30  /**
31   * This module represents an interactive configurator and launcher for
32   * {@link y.layout.router.OrthogonalEdgeRouter}.
33   *
34   *
35   * @see <a href="http://docs.yworks.com/yfiles/doc/developers-guide/orthogonal_edge_router.html#orthogonal_edge_router">Section Orthogonal Edge Routing</a> in the yFiles for Java Developer's Guide
36   */
37  public class OrthogonalEdgeRouterModule extends LayoutModule
38  {
39    private static final String NAME  = "ORTHOGONAL_EDGE_ROUTER";
40    private static final String SCOPE = "SCOPE";
41    private static final String SCOPE_ALL_EDGES = "ALL_EDGES";
42    private static final String SCOPE_SELECTED_EDGES = "SELECTED_EDGES";
43    private static final String SCOPE_AT_SELECTED_NODES = "AT_SELECTED_NODES";
44    private static final String MINIMUM_DISTANCE_TO_EDGE = "MINIMUM_DISTANCE_TO_EDGE";
45    private static final String USE_CUSTOM_MINIMUM_DISTANCE_TO_NODE = "USE_CUSTOM_MINIMUM_DISTANCE_TO_NODE";
46    private static final String CUSTOM_MINIMUM_DISTANCE_TO_NODE = "CUSTOM_MINIMUM_DISTANCE_TO_NODE";
47    private static final String SPACE_DRIVEN_VS_CENTER_DRIVEN_SEARCH = "SPACE_DRIVEN_VS_CENTER_DRIVEN_SEARCH";
48    private static final String LOCAL_CROSSING_MINIMIZATION = "LOCAL_CROSSING_MINIMIZATION";
49    private static final String GRID_SPACING = "GRID_SPACING";
50    private static final String ROUTE_ON_GRID = "ROUTE_ON_GRID";
51    private static final String CROSSING_COST     = "CROSSING_COST";
52    private static final String REROUTING_ENABLED = "REROUTING_ENABLED";
53    private static final String MONOTONIC_NONE = "MONOTONIC_NONE";
54    private static final String MONOTONIC_VERTICAL = "MONOTONIC_VERTICAL";
55    private static final String MONOTONIC_HORIZONTAL = "MONOTONIC_HORIZONTAL";
56    private static final String MONOTONIC_BOTH = "MONOTONIC_BOTH";
57    private static final String MONOTONIC_RESTRICTION = "MONOTONIC_RESTRICTION";
58    private static final String ENFORCE_MONOTONIC_RESTRICTIONS = "ENFORCE_MONOTONIC_RESTRICTIONS";
59    private static final String CONSIDER_NODE_LABELS = "CONSIDER_NODE_LABELS";
60  
61    private static final String LAYOUT_OPTIONS = "LAYOUT_OPTIONS";
62    private static final String CROSSING_MINIMIZATION = "CROSSING_MINIMIZATION";
63    private static final Object[] monotonyFlagEnum = {
64        MONOTONIC_NONE,
65        MONOTONIC_HORIZONTAL,
66        MONOTONIC_VERTICAL,
67        MONOTONIC_BOTH};
68  
69  
70    //////////////////////////////////////////////////////////////////////////////
71    //// Own stuff
72    //////////////////////////////////////////////////////////////////////////////
73    private OrthogonalEdgeRouter router;
74  
75    //////////////////////////////////////////////////////////////////////////////
76    //// Construction
77    //////////////////////////////////////////////////////////////////////////////
78    public OrthogonalEdgeRouterModule()
79    {
80      super(NAME, "yFiles Layout Team", "Routes edges orthogonally.");
81      setPortIntersectionCalculatorEnabled(true);
82    }
83    
84    //////////////////////////////////////////////////////////////////////////////
85    //// Implementation for abstract class y.module.YModule
86    //////////////////////////////////////////////////////////////////////////////
87    protected void init(){ instantiateRouter(); }
88    
89    protected void mainrun()
90    {  
91      // launch layouter in buffered mode
92      launchLayouter(
93          new EdgeGroupRouterStage(
94              new GroupNodeRouterStage(
95                  new ReducedSphereOfActionStage(
96                      new PatchRouterStage(router)))));
97    }
98    
99    protected void dispose(){ router = null; }
100   
101   /**
102    * Creates and initializes the Option Handler so that a convenient way for
103    * manipulating the parameters is at the user's hand.
104    */
105   protected OptionHandler createOptionHandler()
106   {
107     OptionHandler oh = new OptionHandler(getModuleName());  
108     initOptionHandler(oh, null);
109     return oh;
110   }
111   
112   void initOptionHandler(OptionHandler oh, Layouter layouter)
113   {
114     oh.clear();
115     if(layouter == null) layouter = new OrthogonalEdgeRouter();
116     OrthogonalEdgeRouter router = (OrthogonalEdgeRouter)layouter;
117 
118     OptionGroup og = new OptionGroup();
119     og.setAttribute(OptionGroup.ATTRIBUTE_TITLE, LAYOUT_OPTIONS);
120     String[] enums = { SCOPE_ALL_EDGES, SCOPE_SELECTED_EDGES, SCOPE_AT_SELECTED_NODES };
121     if(router.getSphereOfAction() == OrthogonalEdgeRouter.ROUTE_ALL_EDGES)
122       og.addItem(oh.addEnum(SCOPE, enums, 0));
123     else if(router.getSphereOfAction() == OrthogonalEdgeRouter.ROUTE_EDGES_AT_SELECTED_NODES)
124       og.addItem(oh.addEnum(SCOPE, enums, 1));
125     else
126       og.addItem(oh.addEnum(SCOPE, enums, 2));
127     og.addItem(oh.addEnum(MONOTONIC_RESTRICTION, monotonyFlagEnum, 0));
128     og.addItem(oh.addBool(ENFORCE_MONOTONIC_RESTRICTIONS, router.isEnforceMonotonicPathRestrictions()));
129     
130     // The value given for 'minimum distance' denotes a halo to the left and
131     // right of an edge segment.
132     og.addItem(oh.addInt(MINIMUM_DISTANCE_TO_EDGE, router.getMinimumDistance()));
133     oh.getItem(MINIMUM_DISTANCE_TO_EDGE)
134       .setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(4));
135     og.addItem(oh.addBool(USE_CUSTOM_MINIMUM_DISTANCE_TO_NODE, !router.getCoupledDistances()));
136     og.addItem(oh.addInt(CUSTOM_MINIMUM_DISTANCE_TO_NODE, router.getMinimumDistanceToNode()));
137     oh.getItem(CUSTOM_MINIMUM_DISTANCE_TO_NODE)
138       .setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(2));
139     og.addItem(oh.addBool(ROUTE_ON_GRID, router.isGridRoutingEnabled()));
140     og.addItem(oh.addInt(GRID_SPACING, router.getGridSpacing()));
141     oh.getItem(GRID_SPACING)
142       .setAttribute(IntOptionItem.ATTRIBUTE_MIN_VALUE, new Integer(2));
143     og.addItem(oh.addDouble(SPACE_DRIVEN_VS_CENTER_DRIVEN_SEARCH,
144                             router.getCenterToSpaceRatio(), 0.0, 1.0));
145     og.addItem(oh.addBool(CONSIDER_NODE_LABELS, router.isConsiderNodeLabelsEnabled()));
146     
147     og = new OptionGroup();
148     og.setAttribute(OptionGroup.ATTRIBUTE_TITLE, CROSSING_MINIMIZATION);
149     og.addItem(oh.addBool(LOCAL_CROSSING_MINIMIZATION,
150                           router.isLocalCrossingMinimizationEnabled()));
151     
152     og.addItem(oh.addDouble(CROSSING_COST, router.getCrossingCost()));
153     og.addItem(oh.addBool(REROUTING_ENABLED, router.isReroutingEnabled()));
154   
155     ConstraintManager cm = new ConstraintManager(oh);
156     cm.setEnabledOnValueEquals(ROUTE_ON_GRID, Boolean.TRUE, GRID_SPACING);
157     cm.setEnabledOnValueEquals(USE_CUSTOM_MINIMUM_DISTANCE_TO_NODE,
158                                Boolean.TRUE,
159                                CUSTOM_MINIMUM_DISTANCE_TO_NODE);
160   }
161   
162   /**
163    * Initializes the option handler of this module with the 
164    * properties of the given router. 
165    * @param layouter an instance of {@link y.layout.router.OrthogonalEdgeRouter}.
166    */
167   public void initOptionHandler(Layouter layouter)
168   {
169     OptionHandler oh = getOptionHandler();
170     initOptionHandler(oh, layouter);
171   }
172   
173   /**
174    * Configures an instance of OrthogonalEdgeRouter. The values provided by
175    * this module's option handler are being used for this purpose.
176    */
177   public void configure(Layouter layouter)
178   {
179     if (layouter instanceof OrthogonalEdgeRouter)
180     {
181       OrthogonalEdgeRouter router = (OrthogonalEdgeRouter)layouter;
182       OptionHandler oh = getOptionHandler();
183       
184       String choice = oh.getString(SCOPE);
185       if (choice.equals(SCOPE_AT_SELECTED_NODES))
186         router.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_EDGES_AT_SELECTED_NODES);
187       else if (choice.equals(SCOPE_SELECTED_EDGES))
188         router.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_SELECTED_EDGES);
189       else
190         router.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_ALL_EDGES);
191       
192       router.setMinimumDistance(oh.getInt(MINIMUM_DISTANCE_TO_EDGE));
193       router.setCoupledDistances(!oh.getBool(USE_CUSTOM_MINIMUM_DISTANCE_TO_NODE));
194       router.setMinimumDistanceToNode(oh.getInt(CUSTOM_MINIMUM_DISTANCE_TO_NODE));
195       
196       router.setGridRoutingEnabled(oh.getBool(ROUTE_ON_GRID));
197       router.setGridSpacing(oh.getInt(GRID_SPACING));
198       
199       router.setCenterToSpaceRatio(oh.getDouble(SPACE_DRIVEN_VS_CENTER_DRIVEN_SEARCH));
200       
201       router.setLocalCrossingMinimizationEnabled(oh.getBool(LOCAL_CROSSING_MINIMIZATION));
202       if(MONOTONIC_BOTH.equals(oh.getString(MONOTONIC_RESTRICTION))) {
203         router.setMonotonicPathRestriction(OrthogonalEdgeRouter.MONOTONIC_BOTH);
204       } else if(MONOTONIC_HORIZONTAL.equals(oh.getString(MONOTONIC_RESTRICTION))) {
205         router.setMonotonicPathRestriction(OrthogonalEdgeRouter.MONOTONIC_HORIZONTAL);
206       } else if(MONOTONIC_VERTICAL.equals(oh.getString(MONOTONIC_RESTRICTION))) {
207         router.setMonotonicPathRestriction(OrthogonalEdgeRouter.MONOTONIC_VERTICAL);
208       } else {
209         router.setMonotonicPathRestriction(OrthogonalEdgeRouter.MONOTONIC_NONE);
210       }
211       router.setEnforceMonotonicPathRestrictions(oh.getBool(ENFORCE_MONOTONIC_RESTRICTIONS));
212 
213       router.setConsiderNodeLabelsEnabled(oh.getBool(CONSIDER_NODE_LABELS));
214       router.setCrossingCost(oh.getDouble(CROSSING_COST));
215       router.setReroutingEnabled(oh.getBool(REROUTING_ENABLED));
216       
217       // Further, non-public options.
218       //router.setInnerPortsEnabled(true);
219     }
220     else
221     {
222       throw new IllegalArgumentException("argument must be of type y.layout.router.OrthogonalEdgeRouter");
223     }
224   }
225   
226   //////////////////////////////////////////////////////////////////////////////
227   //// Own stuff
228   //////////////////////////////////////////////////////////////////////////////
229   private void instantiateRouter()
230   {
231     if (router != null)
232       return;
233     router = new OrthogonalEdgeRouter();
234     configure(router);
235   }
236 }
237