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.view.layout.router;
15  
16  import demo.view.DemoBase;
17  import y.base.DataProvider;
18  import y.base.Edge;
19  import y.base.EdgeCursor;
20  import y.base.EdgeMap;
21  import y.base.Node;
22  import y.base.NodeCursor;
23  import y.layout.LayoutGraph;
24  import y.layout.Layouter;
25  import y.layout.PortConstraintConfigurator;
26  import y.layout.PortConstraintKeys;
27  import y.layout.router.ChannelEdgeRouter;
28  import y.layout.router.OrthogonalEdgeRouter;
29  import y.module.ChannelEdgeRouterModule;
30  import y.module.OrthogonalEdgeRouterModule;
31  import y.module.PortConstraintModule;
32  import y.module.YModule;
33  import y.option.OptionHandler;
34  import y.util.DataProviderAdapter;
35  import y.view.Arrow;
36  import y.view.Bend;
37  import y.view.BendCursor;
38  import y.view.BendList;
39  import y.view.CreateEdgeMode;
40  import y.view.EditMode;
41  import y.view.Graph2D;
42  import y.view.HotSpotMode;
43  import y.view.PolyLineEdgeRealizer;
44  import y.view.PortAssignmentMoveSelectionMode;
45  import y.view.Selections;
46  
47  import javax.swing.AbstractAction;
48  import javax.swing.JComboBox;
49  import javax.swing.JToolBar;
50  import java.awt.event.ActionEvent;
51  import java.awt.event.ActionListener;
52  
53  
54  /**
55   * A demo that shows some of the capabilities of the yFiles Orthogonal Edge Router implementations.
56   * <br> The following aspects of using
57   * the edge routers are demonstrated. <ol> <li>How to use OrthogonalEdgeRouterModule or
58   * ChannelEdgeRouterModule as a convenient means to launch and
59   * configure the edge routers.</li> <li>How to modify the yFiles EditMode in order to trigger the orthogonal edge router
60   * whenever <ul> <li>new edges get created</li> <li>nodes get resized</li> <li>selected nodes will be moved</li>
61   * </ul></li> <li>How to specify port constraints for the edge router. With the help of port constraints it is possible
62   * to tell the orthogonal edge router on which side of a node or on which exact coordinate a start or endpoint of an
63   * edge should connect to a node.</li> </ol>
64   */
65  public class EdgeRouterDemo extends DemoBase {
66    RouterStrategy strategy;
67    PortAssignmentMoveSelectionMode paMode;
68  
69    // two available strategies
70    private ChannelEdgeRouterStrategy channelEdgeRouterStrategy = new ChannelEdgeRouterStrategy();
71    private OrthogonalEdgeRouterStrategy orthogonalEdgeRouterStrategy = new OrthogonalEdgeRouterStrategy();
72  
73    public EdgeRouterDemo() {
74      strategy = orthogonalEdgeRouterStrategy;
75  
76      PolyLineEdgeRealizer er = (PolyLineEdgeRealizer) view.getGraph2D().getDefaultEdgeRealizer();
77      er.setTargetArrow(Arrow.STANDARD);
78      er.setSmoothedBends(true);
79      Graph2D graph = view.getGraph2D();
80      EdgeMap sourcePortMap = graph.createEdgeMap();
81      EdgeMap targetPortMap = graph.createEdgeMap();
82      graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, sourcePortMap);
83      graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, targetPortMap);
84      paMode.setSpc(sourcePortMap);
85      paMode.setTpc(targetPortMap);
86    }
87  
88    /** Returns ViewActionDemo toolbar plus actions to trigger some layout algorithms */
89    protected JToolBar createToolBar() {
90      JToolBar toolBar = super.createToolBar();
91      toolBar.addSeparator();
92      toolBar.add(new ConfigurePortConstraints());
93      toolBar.add(new LayoutAction());
94      toolBar.add(new OptionAction());
95      final JComboBox comboBox = new JComboBox(new Object[]{"Orthogonal Edge Router", "Channel Edge Router"});
96      comboBox.addActionListener(new ActionListener() {
97        public void actionPerformed(ActionEvent e) {
98          switch (comboBox.getSelectedIndex()) {
99            default:
100           case 0:
101             strategy = orthogonalEdgeRouterStrategy;
102             break;
103           case 1:
104             strategy = channelEdgeRouterStrategy;
105             break;
106         }
107       }
108     });
109     toolBar.add(comboBox);
110     return toolBar;
111   }
112 
113 
114   /** Provides configuration options  for the edge router */
115   class OptionAction extends AbstractAction {
116     OptionAction() {
117       super("Router Options...");
118     }
119 
120     public void actionPerformed(ActionEvent e) {
121       //display the option handler
122       OptionHandler op = strategy.getModule().getOptionHandler();
123       if (op != null) {
124         op.showEditor();
125       }
126     }
127   }
128 
129   /** Launches the Orthogonal Edge Router */
130   class LayoutAction extends AbstractAction {
131     LayoutAction() {
132       super("Route Edges");
133     }
134 
135     public void actionPerformed(ActionEvent e) {
136       //update preferredEdgeLengthData before launching the module
137       Graph2D graph = view.getGraph2D();
138 
139       //start the module
140       strategy.getModule().start(graph);
141     }
142   }
143 
144   /**
145    * Configuration Utility for Port Constraints.  With the help of port constraints it is possible to tell the
146    * orthogonal edge router on which side of a node or on which exact coordinate a start or endpoint of an edge should
147    * connect to a node.
148    */
149   class ConfigurePortConstraints extends AbstractAction {
150     YModule module;
151 
152     ConfigurePortConstraints() {
153       super("Port Constraints...");
154       module = new PortConstraintModule();
155     }
156 
157     public void actionPerformed(ActionEvent e) {
158       //display the option handler
159       OptionHandler op = module.getOptionHandler();
160       if (op != null) {
161         if (!op.showEditor()) {
162           return;
163         }
164       }
165       module.start(view.getGraph2D());
166     }
167   }
168 
169   /**
170    * Adds a specially configured EditMode that will automatically route all newly created edges orthogonally. The
171    * orthogonal edge router will also be activated on some edges, when nodes get resized or a node selection gets
172    * moved.
173    */
174   protected void registerViewModes() {
175     EditMode mode = new EditMode();
176     mode.setMoveSelectionMode(paMode = new MyMoveSelectionMode());
177     mode.setCreateEdgeMode(new MyCreateEdgeMode());
178     mode.setHotSpotMode(new MyHotSpotMode());
179     view.addViewMode(mode);
180   }
181 
182   /** A special mode for creating edges. */
183   class MyCreateEdgeMode extends CreateEdgeMode {
184     MyCreateEdgeMode() {
185       super();
186       allowSelfloopCreation(false);
187     }
188 
189     protected void edgeCreated(final Edge e) {
190       final Graph2D graph = view.getGraph2D();
191 
192       strategy.routeNewEdge(e);
193 
194 
195       graph.updateViews();
196     }
197   }
198 
199 
200   /** A special mode for resizing nodes. */
201   class MyHotSpotMode extends HotSpotMode {
202     public void mouseReleasedLeft(double x, double y) {
203       super.mouseReleasedLeft(x, y);
204 
205       final Graph2D graph = view.getGraph2D();
206 
207       DataProvider selectedNodes = Selections.createSelectionDataProvider(graph);
208       strategy.rerouteAdjacentEdges(selectedNodes, graph);
209       graph.updateViews();
210     }
211   }
212 
213   /** A special mode for moving a selection of the graph. */
214   class MyMoveSelectionMode extends PortAssignmentMoveSelectionMode {
215 
216     MyMoveSelectionMode() {
217       super(null, null);
218     }
219 
220     private boolean routeEdgesOnMove = true;
221 
222     protected BendList getBendsToBeMoved() {
223       BendList bends = super.getBendsToBeMoved();
224 
225       //add all bends from edges, whose source and target nodes are selected, since they will not be routed. 
226       for (NodeCursor nodeCursor = getGraph2D().selectedNodes(); nodeCursor.ok(); nodeCursor.next()) {
227         Node node = nodeCursor.node();
228         for(EdgeCursor edgeCursor = node.outEdges(); edgeCursor.ok(); edgeCursor.next()) {
229           Edge edge = edgeCursor.edge();
230           if(getGraph2D().isSelected(edge.target())){
231             for(BendCursor bendCursor = getGraph2D().getRealizer(edge).bends(); bendCursor.ok(); bendCursor.next()){
232               Bend bend = bendCursor.bend();
233               bends.add(bend);
234             }
235           }
236         }
237       }
238       return bends;
239     }
240 
241     protected void selectionOnMove(double dx, double dy, double x, double y) {
242       super.selectionOnMove(dx, dy, x, y);
243       if (routeEdgesOnMove) {
244         routeEdgesToSelection();
245       }
246     }
247 
248     protected void selectionMovedAction(double dx, double dy, double x, double y) {
249       super.selectionMovedAction(dx, dy, x, y);
250       routeEdgesToSelection();
251     }
252 
253     void routeEdgesToSelection() {
254       final Graph2D graph = view.getGraph2D();
255       if (graph.selectedNodes().ok()) {
256         strategy.routeEdgesToSelection(graph);
257         graph.updateViews();
258       }
259     }
260   }
261 
262   /** Launches this demo. */
263   public static void main(String[] args) {
264     initLnF();
265 
266     EdgeRouterDemo demo = new EdgeRouterDemo();
267 
268     demo.start("Orthogonal Edge Router Demo");
269   }
270 
271   abstract static class RouterStrategy {
272     abstract YModule getModule();
273 
274     abstract void routeNewEdge(Edge e);
275 
276     abstract void rerouteAdjacentEdges(DataProvider selectedNodes, LayoutGraph graph);
277 
278     abstract void routeEdgesToSelection(Graph2D graph);
279 
280     abstract void route(Layouter router, LayoutGraph graph);
281 
282     protected void routeNewEdge(Layouter router, final Edge e, Graph2D graph) {
283       EdgeMap spc = (EdgeMap) graph.getDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY);
284       EdgeMap tpc = (EdgeMap) graph.getDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY);
285 
286       PortConstraintConfigurator pcc = new PortConstraintConfigurator();
287       if (spc != null && tpc != null) {
288         spc.set(e, pcc.createPortConstraintFromSketch(graph, e, true, false));
289         tpc.set(e, pcc.createPortConstraintFromSketch(graph, e, false, false));
290         route(router, graph);
291         spc.set(e, null);
292         tpc.set(e, null);
293       } else {
294         route(router, graph);
295       }
296     }
297 
298     protected void routeEdgesToSelection(final Graph2D graph, Layouter router, Object affectedEdgesKey) {
299       graph.addDataProvider(affectedEdgesKey, new DataProviderAdapter() {
300         public boolean getBool(Object dataHolder) {
301           return graph.isSelected(((Edge) dataHolder).source()) ^ graph.isSelected(((Edge) dataHolder).target());
302         }
303       });
304       route(router, graph);
305       graph.removeDataProvider(affectedEdgesKey);
306     }
307 
308     protected void routeNewEdge(final Edge e, Graph2D graph, Layouter router, Object selectedEdgesKey) {
309       DataProvider activeEdges = new DataProviderAdapter() {
310         public boolean getBool(Object o) {
311           return e == o;
312         }
313       };
314       graph.addDataProvider(selectedEdgesKey, activeEdges);
315       routeNewEdge(router, e, graph);
316       graph.removeDataProvider(selectedEdgesKey);
317     }
318   }
319 
320   static class OrthogonalEdgeRouterStrategy extends RouterStrategy {
321     private OrthogonalEdgeRouterModule module = new OrthogonalEdgeRouterModule();
322 
323     public YModule getModule() {
324       return module;
325     }
326 
327     public void routeNewEdge(final Edge e) {
328       Graph2D graph = (Graph2D) e.getGraph();
329       OrthogonalEdgeRouter router = new OrthogonalEdgeRouter();
330       module.configure(router);
331       router.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_SELECTED_EDGES);
332       routeNewEdge(e, graph, router, Layouter.SELECTED_EDGES);
333     }
334 
335     public void rerouteAdjacentEdges(DataProvider selectedNodes, LayoutGraph graph) {
336       OrthogonalEdgeRouter router = new OrthogonalEdgeRouter();
337       module.configure(router);
338       router.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_EDGES_AT_SELECTED_NODES);
339       graph.addDataProvider(Layouter.SELECTED_NODES, selectedNodes);
340       this.route(router, graph);
341       graph.removeDataProvider(Layouter.SELECTED_NODES);
342     }
343 
344     public void routeEdgesToSelection(final Graph2D graph) {
345       OrthogonalEdgeRouter router = new OrthogonalEdgeRouter();
346       module.configure(router);
347       router.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_SELECTED_EDGES);
348       routeEdgesToSelection(graph, router, Layouter.SELECTED_EDGES);
349     }
350 
351     void route(Layouter router, LayoutGraph graph) {
352       router.doLayout(graph);
353     }
354   }
355 
356   static class ChannelEdgeRouterStrategy extends RouterStrategy {
357     private ChannelEdgeRouterModule module = new ChannelEdgeRouterModule();
358 
359     public YModule getModule() {
360       return module;
361     }
362 
363     public void routeNewEdge(final Edge e) {
364       final Graph2D graph = (Graph2D) e.getGraph();
365       ChannelEdgeRouter router = new ChannelEdgeRouter();
366       module.configure(router);
367       routeNewEdge(e, graph, router, ChannelEdgeRouter.AFFECTED_EDGES);
368     }
369 
370     public void rerouteAdjacentEdges(final DataProvider selectedNodes, LayoutGraph graph) {
371       ChannelEdgeRouter router = new ChannelEdgeRouter();
372       module.configure(router);
373       graph.addDataProvider(ChannelEdgeRouter.AFFECTED_EDGES, new DataProviderAdapter() {
374         public boolean getBool(Object dataHolder) {
375           return selectedNodes.getBool((((Edge) dataHolder).source())) || selectedNodes.getBool(((Edge) dataHolder).target());
376         }
377       });
378       this.route(router, graph);
379       graph.removeDataProvider(ChannelEdgeRouter.AFFECTED_EDGES);
380     }
381 
382     public void routeEdgesToSelection(final Graph2D graph) {
383       ChannelEdgeRouter router = new ChannelEdgeRouter();
384       module.configure(router);
385       routeEdgesToSelection(graph, router, ChannelEdgeRouter.AFFECTED_EDGES);
386     }
387 
388     void route(Layouter router, LayoutGraph graph) {
389       router.doLayout(graph);
390     }
391   }
392 }
393 
394 
395       
396