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