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.base.Edge;
20  import y.layout.Layouter;
21  import y.layout.router.ChannelEdgeRouter;
22  import y.layout.router.OrthogonalPatternEdgeRouter;
23  import y.layout.router.OrthogonalSegmentDistributionStage;
24  import y.option.ConstraintManager;
25  import y.option.DoubleOptionItem;
26  import y.option.OptionGroup;
27  import y.option.OptionHandler;
28  import y.util.DataProviderAdapter;
29  import y.view.Graph2D;
30  
31  /**
32   * This module represents an interactive configurator and launcher for
33   * {@link y.layout.router.ChannelEdgeRouter}.
34   *
35   *
36   * @see <a href="http://docs.yworks.com/yfiles/doc/developers-guide/channel_edge_router.html#channel_edge_router">Section Channel Edge Routing</a> in the yFiles for Java Developer's Guide
37   */
38  public class ChannelEdgeRouterModule extends LayoutModule {
39    private static final String NAME = "CHANNEL_EDGE_ROUTER";
40  
41    private ChannelEdgeRouter router;
42    private static final String PATHFINDER = "PATHFINDER";
43  
44    private static final String SCOPE = "SCOPE";
45    private static final String SCOPE_AT_SELECTED_NODES = "SCOPE_AT_SELECTED_NODES";
46    private static final String SCOPE_SELECTED_EDGES = "SCOPE_SELECTED_EDGES";
47    private static final String LAYOUT_OPTIONS = "LAYOUT_OPTIONS";
48    private static final String SCOPE_ALL_EDGES = "SCOPE_ALL_EDGES";
49    private static final String COST = "COST";
50    private static final String EDGE_CROSSING_COST = "EDGE_CROSSING_COST";
51    private static final String NODE_CROSSING_COST = "NODE_CROSSING_COST";
52    private static final String BEND_COST = "BEND_COST_FACTOR";
53    private static final String MINIMUM_DISTANCE = "MINIMUM_DISTANCE";
54    private static final String ACTIVATE_GRID_ROUTING = "ACTIVATE_GRID_ROUTING";
55    private static final String GRID_SPACING = "GRID_SPACING";
56    private static final String ORTHOGONAL_PATTERN_PATH_FINDER = "ORTHOGONAL_PATTERN_PATH_FINDER";
57    private static final String ORTHOGONAL_SHORTESTPATH_PATH_FINDER = "ORTHOGONAL_SHORTESTPATH_PATH_FINDER";
58  
59  
60    /**
61     * Creates a new Instance of this Module.
62     */
63    public ChannelEdgeRouterModule() {
64      super(NAME, "yFiles Layout Team", "Routes edges orthogonally.");
65      setPortIntersectionCalculatorEnabled(true);
66    }
67  
68  
69    protected void init() {
70      instantiateRouter();
71      configure(router);
72  
73      final Graph2D graph = getGraph2D();
74      OptionHandler oh = getOptionHandler();
75  
76      //set affected edges
77      if (oh.get(SCOPE).equals(SCOPE_ALL_EDGES)) {
78        graph.addDataProvider(ChannelEdgeRouter.AFFECTED_EDGES, new DataProviderAdapter() {
79          public boolean getBool(Object dataHolder) {
80            return true;
81          }
82        });
83      } else if (oh.get(SCOPE).equals(SCOPE_SELECTED_EDGES)) {
84        graph.addDataProvider(ChannelEdgeRouter.AFFECTED_EDGES, new DataProviderAdapter() {
85          public boolean getBool(Object dataHolder) {
86            return graph.isSelected((Edge) dataHolder);
87          }
88        });
89      } else {
90        graph.addDataProvider(ChannelEdgeRouter.AFFECTED_EDGES, new DataProviderAdapter() {
91          public boolean getBool(Object dataHolder) {
92            return graph.isSelected(((Edge) dataHolder).source()) || graph.isSelected(((Edge) dataHolder).target());
93          }
94        });
95      }
96    }
97  
98    //////////////////////////////////////////////////////////////////////////////
99    //// Own stuff
100   //////////////////////////////////////////////////////////////////////////////
101   private void instantiateRouter() {
102     if (router != null) {
103       return;
104     }
105     router = new ChannelEdgeRouter();
106   }
107 
108   /**
109    * Configures an instance of ChannelEdgeRouter. The values provided by this module's option handler are being used for
110    * this purpose.
111    *
112    * @param layouter the layouter to be configured.
113    */
114   public void configure(Layouter layouter) {
115     if (layouter instanceof ChannelEdgeRouter) {
116       ChannelEdgeRouter edgeRouter = (ChannelEdgeRouter) layouter;
117       OptionHandler oh = getOptionHandler();
118 
119 
120       Layouter pathFinder;
121       if (oh.get(PATHFINDER).equals(ORTHOGONAL_PATTERN_PATH_FINDER)) {
122         OrthogonalPatternEdgeRouter orthogonalPatternEdgeRouter = new OrthogonalPatternEdgeRouter();
123         orthogonalPatternEdgeRouter.setAffectedEdgesDPKey(ChannelEdgeRouter.AFFECTED_EDGES);
124         orthogonalPatternEdgeRouter.setMinimumDistance(oh.getDouble(MINIMUM_DISTANCE));
125 
126         orthogonalPatternEdgeRouter.setGridRoutingEnabled(oh.getBool(ACTIVATE_GRID_ROUTING));
127         orthogonalPatternEdgeRouter.setGridWidth(oh.getDouble(GRID_SPACING));
128 
129         orthogonalPatternEdgeRouter.setBendCost(oh.getDouble(BEND_COST));
130         orthogonalPatternEdgeRouter.setEdgeCrossingCost(oh.getDouble(EDGE_CROSSING_COST));
131         orthogonalPatternEdgeRouter.setNodeCrossingCost(oh.getDouble(NODE_CROSSING_COST));
132 
133         //disable edge overlap costs when Edge distribution will run afterwards anyway
134         orthogonalPatternEdgeRouter.setEdgeOverlapCost(0.0);
135         pathFinder = orthogonalPatternEdgeRouter;
136       } else {
137         ChannelEdgeRouter.OrthogonalShortestPathPathFinder orthogonalShortestPathPathFinder = new ChannelEdgeRouter.OrthogonalShortestPathPathFinder();
138         orthogonalShortestPathPathFinder.setAffectedEdgesDPKey(ChannelEdgeRouter.AFFECTED_EDGES);
139         orthogonalShortestPathPathFinder.setMinimumDistance((int) oh.getDouble(MINIMUM_DISTANCE));
140 
141         orthogonalShortestPathPathFinder.setGridRoutingEnabled(oh.getBool(ACTIVATE_GRID_ROUTING));
142         orthogonalShortestPathPathFinder.setGridSpacing((int) oh.getDouble(GRID_SPACING));
143 
144         orthogonalShortestPathPathFinder.setCrossingCost(oh.getDouble(EDGE_CROSSING_COST));
145         pathFinder = orthogonalShortestPathPathFinder;
146       }
147       edgeRouter.setPathFinderStrategy(pathFinder);
148 
149       OrthogonalSegmentDistributionStage segmentDistributionStage = new OrthogonalSegmentDistributionStage();
150       segmentDistributionStage.setAffectedEdgesDPKey(ChannelEdgeRouter.AFFECTED_EDGES);
151       segmentDistributionStage.setPreferredDistance(oh.getDouble(MINIMUM_DISTANCE));
152       segmentDistributionStage.setGridEnabled(oh.getBool(ACTIVATE_GRID_ROUTING));
153       segmentDistributionStage.setGridWidth(oh.getDouble(GRID_SPACING));
154 
155       edgeRouter.setEdgeDistributionStrategy(segmentDistributionStage);
156     } else {
157       throw new IllegalArgumentException("argument must be of type y.layout.router.ChannelEdgeRouter");
158     }
159   }
160 
161   /**
162    * Initializes the option handler of this module with the properties of the given router.
163    *
164    * @param layouter an instance of {@link y.layout.router.ChannelEdgeRouter}.
165    */
166   public void initOptionHandler(Layouter layouter) {
167     OptionHandler oh = getOptionHandler();
168     initOptionHandler(oh, layouter);
169   }
170 
171   void initOptionHandler(OptionHandler oh, Layouter layouter) {
172     oh.clear();
173     if (layouter == null || ! (layouter instanceof ChannelEdgeRouter) ) {
174       layouter = new ChannelEdgeRouter();
175     }
176     ChannelEdgeRouter cer = (ChannelEdgeRouter) layouter;
177 
178     OptionGroup og = new OptionGroup();
179     og.setAttribute(OptionGroup.ATTRIBUTE_TITLE, LAYOUT_OPTIONS);
180     String[] pathFinderEnum = {ORTHOGONAL_PATTERN_PATH_FINDER, ORTHOGONAL_SHORTESTPATH_PATH_FINDER};
181 
182     if(cer.getPathFinderStrategy() instanceof OrthogonalPatternEdgeRouter){
183       OrthogonalPatternEdgeRouter oper = (OrthogonalPatternEdgeRouter) cer.getPathFinderStrategy();
184       og.addItem(oh.addEnum(PATHFINDER, pathFinderEnum, 0));
185 
186       String[] affectedEnum = {SCOPE_ALL_EDGES, SCOPE_SELECTED_EDGES, SCOPE_AT_SELECTED_NODES};
187       og.addItem(oh.addEnum(SCOPE, affectedEnum, 0));
188 
189       og.addItem(oh.addDouble(MINIMUM_DISTANCE, oper.getMinimumDistance()));
190       og.addItem(oh.addBool(ACTIVATE_GRID_ROUTING, oper.isGridRoutingEnabled()));
191       og.addItem(oh.addDouble(GRID_SPACING, oper.getGridWidth()));
192       oh.getItem(GRID_SPACING)
193         .setAttribute(DoubleOptionItem.ATTRIBUTE_MIN_VALUE, new Double(2.0));
194 
195       ConstraintManager cm = new ConstraintManager(oh);
196       cm.setEnabledOnValueEquals(ACTIVATE_GRID_ROUTING, Boolean.TRUE, GRID_SPACING);
197 
198       og = new OptionGroup();
199       og.setAttribute(OptionGroup.ATTRIBUTE_TITLE, COST);
200       og.addItem(oh.addDouble(BEND_COST, oper.getBendCost()));
201       cm.setEnabledOnValueEquals(PATHFINDER, ORTHOGONAL_PATTERN_PATH_FINDER, BEND_COST);
202       og.addItem(oh.addDouble(EDGE_CROSSING_COST, oper.getEdgeCrossingCost()));
203       cm.setEnabledOnValueEquals(PATHFINDER, ORTHOGONAL_PATTERN_PATH_FINDER, EDGE_CROSSING_COST);
204       og.addItem(oh.addDouble(NODE_CROSSING_COST, oper.getNodeCrossingCost()));
205       cm.setEnabledOnValueEquals(PATHFINDER, ORTHOGONAL_PATTERN_PATH_FINDER, NODE_CROSSING_COST);
206     } else if(cer.getPathFinderStrategy() instanceof ChannelEdgeRouter.OrthogonalShortestPathPathFinder){
207       ChannelEdgeRouter.OrthogonalShortestPathPathFinder osppf =
208           (ChannelEdgeRouter.OrthogonalShortestPathPathFinder) cer.getPathFinderStrategy();
209       og.addItem(oh.addEnum(PATHFINDER, pathFinderEnum, 1));
210 
211       String[] affectedEnum = {SCOPE_ALL_EDGES, SCOPE_SELECTED_EDGES, SCOPE_AT_SELECTED_NODES};
212       og.addItem(oh.addEnum(SCOPE, affectedEnum, 0));
213 
214       og.addItem(oh.addDouble(MINIMUM_DISTANCE, osppf.getMinimumDistance()));
215       oh.getItem(MINIMUM_DISTANCE)
216         .setAttribute(DoubleOptionItem.ATTRIBUTE_MIN_VALUE, new Double(4.0));
217       og.addItem(oh.addBool(ACTIVATE_GRID_ROUTING, osppf.isGridRoutingEnabled()));
218       og.addItem(oh.addDouble(GRID_SPACING, osppf.getGridSpacing()));
219       oh.getItem(GRID_SPACING)
220         .setAttribute(DoubleOptionItem.ATTRIBUTE_MIN_VALUE, new Double(2.0));
221 
222       ConstraintManager cm = new ConstraintManager(oh);
223       cm.setEnabledOnValueEquals(ACTIVATE_GRID_ROUTING, Boolean.TRUE, GRID_SPACING);
224 
225       og = new OptionGroup();
226       og.setAttribute(OptionGroup.ATTRIBUTE_TITLE, COST);
227       og.addItem(oh.addDouble(BEND_COST, 1));
228       cm.setEnabledOnValueEquals(PATHFINDER, ORTHOGONAL_PATTERN_PATH_FINDER, BEND_COST);
229       og.addItem(oh.addDouble(EDGE_CROSSING_COST, 5));
230       cm.setEnabledOnValueEquals(PATHFINDER, ORTHOGONAL_PATTERN_PATH_FINDER, EDGE_CROSSING_COST);
231       og.addItem(oh.addDouble(NODE_CROSSING_COST, 50));
232       cm.setEnabledOnValueEquals(PATHFINDER, ORTHOGONAL_PATTERN_PATH_FINDER, NODE_CROSSING_COST);
233     } else { //use other settings
234       og.addItem(oh.addEnum(PATHFINDER, pathFinderEnum, 0));
235 
236       String[] affectedEnum = {SCOPE_ALL_EDGES, SCOPE_SELECTED_EDGES, SCOPE_AT_SELECTED_NODES};
237       og.addItem(oh.addEnum(SCOPE, affectedEnum, 0));
238 
239       og.addItem(oh.addDouble(MINIMUM_DISTANCE, 10.0));
240       og.addItem(oh.addBool(ACTIVATE_GRID_ROUTING, true));
241       og.addItem(oh.addDouble(GRID_SPACING, 20.0));
242       ConstraintManager cm = new ConstraintManager(oh);
243       cm.setEnabledOnValueEquals(ACTIVATE_GRID_ROUTING, Boolean.TRUE, GRID_SPACING);
244 
245       og = new OptionGroup();
246       og.setAttribute(OptionGroup.ATTRIBUTE_TITLE, COST);
247       og.addItem(oh.addDouble(BEND_COST, 1.0));
248       cm.setEnabledOnValueEquals(PATHFINDER, ORTHOGONAL_PATTERN_PATH_FINDER, BEND_COST);
249       og.addItem(oh.addDouble(EDGE_CROSSING_COST, 5.0));
250       cm.setEnabledOnValueEquals(PATHFINDER, ORTHOGONAL_PATTERN_PATH_FINDER, EDGE_CROSSING_COST);
251       og.addItem(oh.addDouble(NODE_CROSSING_COST, 50.0));
252       cm.setEnabledOnValueEquals(PATHFINDER, ORTHOGONAL_PATTERN_PATH_FINDER, NODE_CROSSING_COST);
253     }
254   }
255 
256   protected void dispose() {
257     router = null;
258   }
259 
260   /**
261    * Creates and initializes the Option Handler so that a convenient way for manipulating the parameters is at the
262    * user's hand.
263    */
264   protected OptionHandler createOptionHandler() {
265     OptionHandler oh = new OptionHandler(getModuleName());
266     initOptionHandler(oh, null);
267     return oh;
268   }
269 
270   protected void mainrun() {
271     launchLayouter(router);
272   }
273 }
274