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.router;
29  
30  import demo.view.DemoBase;
31  import y.base.DataProvider;
32  import y.base.Edge;
33  import y.base.EdgeCursor;
34  import y.base.EdgeMap;
35  import y.base.Node;
36  import y.base.NodeCursor;
37  import y.layout.Layouter;
38  import y.layout.PortConstraintConfigurator;
39  import y.layout.PortConstraintKeys;
40  import y.layout.router.ChannelEdgeRouter;
41  import y.layout.router.OrthogonalEdgeRouter;
42  import y.layout.router.polyline.EdgeRouter;
43  import demo.layout.module.ChannelEdgeRouterModule;
44  import demo.layout.module.OrthogonalEdgeRouterModule;
45  import demo.layout.module.PolylineEdgeRouterModule;
46  import y.module.YModule;
47  import y.option.OptionHandler;
48  import y.util.DataProviderAdapter;
49  import y.view.Bend;
50  import y.view.BendCursor;
51  import y.view.BendList;
52  import y.view.CreateEdgeMode;
53  import y.view.EdgeRealizer;
54  import y.view.EditMode;
55  import y.view.Graph2D;
56  import y.view.HotSpotMode;
57  import y.view.MovePortMode;
58  import y.view.PolyLineEdgeRealizer;
59  import y.view.Port;
60  import y.view.PortAssignmentMoveSelectionMode;
61  import y.view.Selections;
62  
63  import javax.swing.AbstractAction;
64  import javax.swing.JComboBox;
65  import javax.swing.JToolBar;
66  import java.awt.EventQueue;
67  import java.awt.event.ActionEvent;
68  import java.awt.event.ActionListener;
69  import java.util.HashSet;
70  import java.util.Locale;
71  import java.util.Set;
72  
73  
74  /**
75   * A demo that shows some of the capabilities of the yFiles Orthogonal Edge Router implementations.
76   * <br> The following aspects of using the edge routers are demonstrated.
77   * <ol>
78   *   <li>
79   *     How to use OrthogonalEdgeRouterModule, ChannelEdgeRouterModule or PolylineEdgeRouterModule
80   *     as a convenient means to launch and configure the edge routers.
81   *   </li>
82   *   <li>
83   *     How to modify the yFiles EditMode in order to trigger the orthogonal edge router whenever
84   *     <ul>
85   *       <li>new edges get created</li>
86   *       <li>nodes get resized</li>
87   *       <li>selected nodes will be moved</li>
88   *     </ul>
89   *   </li>
90   *   <li>
91   *     How to specify port constraints for the edge router. With the help of port constraints it
92   *     is possible to tell the orthogonal edge router on which side of a node or on which exact
93   *     coordinate a start or endpoint of an edge should connect to a node.
94   *   </li>
95   * </ol>
96   * <br>
97   * Usage: Create nodes. Create edges crossing other nodes. The edges will be routed immediately.
98   * To reroute all edges use the toolbar button "Route Edges".
99   *
100  * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/polyline_edge_router" target="_blank">Section Polyline Edge Routing</a> in the yFiles for Java Developer's Guide
101  * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/orthogonal_edge_router" target="_blank">Section Orthogonal Edge Routing</a> in the yFiles for Java Developer's Guide
102  * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/channel_edge_router" target="_blank">Section Channel Edge Routing</a> in the yFiles for Java Developer's Guide
103  */
104 public class EdgeRouterDemo extends DemoBase {
105   RouterStrategy strategy;
106   PortAssignmentMoveSelectionMode paMode;
107 
108   // three available strategies
109   private OrthogonalEdgeRouterStrategy orthogonalEdgeRouterStrategy;
110   private ChannelEdgeRouterStrategy channelEdgeRouterStrategy;
111   private PolyLineEdgeRouterStrategy polylineEdgeRouterStrategy;
112 
113   public EdgeRouterDemo() {
114     Graph2D graph = view.getGraph2D();
115     EdgeMap sourcePortMap = graph.createEdgeMap();
116     EdgeMap targetPortMap = graph.createEdgeMap();
117     graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, sourcePortMap);
118     graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, targetPortMap);
119     paMode.setSpc(sourcePortMap);
120     paMode.setTpc(targetPortMap);
121     
122     createInitialGraph();
123   }
124 
125   protected void initialize() {
126     channelEdgeRouterStrategy = new ChannelEdgeRouterStrategy();
127     orthogonalEdgeRouterStrategy = new OrthogonalEdgeRouterStrategy();
128     polylineEdgeRouterStrategy = new PolyLineEdgeRouterStrategy();
129   }
130 
131   protected void createInitialGraph() {
132     Graph2D graph = view.getGraph2D();
133     graph.createEdge(graph.createNode(100,100,"1"), graph.createEdge(graph.createNode(200,200,"2"), graph.createNode(300,100,"3")).source());
134   }
135 
136   /**
137    * Returns ViewActionDemo toolbar plus actions to trigger some layout algorithms
138    */
139   protected JToolBar createToolBar() {
140     final JComboBox comboBox = new JComboBox(new Object[]{"Polyline Edge Router", "Orthogonal Edge Router", "Channel Edge Router"});
141     comboBox.setMaximumSize(comboBox.getPreferredSize());
142     comboBox.addActionListener(new ActionListener() {
143       public void actionPerformed(ActionEvent e) {
144         switch (comboBox.getSelectedIndex()) {
145           case 0:
146             strategy = polylineEdgeRouterStrategy;
147             setSmoothedBends(false);
148             break;
149           case 1:
150             strategy = orthogonalEdgeRouterStrategy;
151             setSmoothedBends(true);
152             break;
153           case 2:
154             strategy = channelEdgeRouterStrategy;
155             setSmoothedBends(true);
156         }
157         strategy.getModule().start(view.getGraph2D());
158       }
159     });
160     strategy = polylineEdgeRouterStrategy;
161 
162     JToolBar toolBar = super.createToolBar();
163     toolBar.addSeparator();
164     toolBar.add(createActionControl(new LayoutAction()));
165     toolBar.addSeparator(TOOLBAR_SMALL_SEPARATOR);
166     toolBar.add(comboBox);
167     toolBar.addSeparator(TOOLBAR_SMALL_SEPARATOR);
168     toolBar.add(createActionControl(new OptionAction()));
169 
170     return toolBar;
171   }
172 
173   private void setSmoothedBends(boolean smoothBends) {
174     Graph2D graph = view.getGraph2D();
175 
176     for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
177       final Edge edge = ec.edge();
178       EdgeRealizer realizer = graph.getRealizer(edge);
179       ((PolyLineEdgeRealizer) realizer).setSmoothedBends(smoothBends);
180     }
181 
182     // also adjust the default edge realizer for newly created edges
183     ((PolyLineEdgeRealizer) graph.getDefaultEdgeRealizer()).setSmoothedBends(smoothBends);
184   }
185 
186 
187   /**
188    * Provides configuration options for the edge router.
189    */
190   class OptionAction extends AbstractAction {
191     OptionAction() {
192       super("Settings...", getIconResource("resource/properties.png"));
193     }
194 
195     public void actionPerformed(ActionEvent e) {
196       OptionSupport.showDialog(strategy.getModule(), view.getGraph2D(), false, view.getFrame());
197     }
198   }
199 
200   /**
201    * Launches the Orthogonal Edge Router.
202    */
203   class LayoutAction extends AbstractAction {
204     LayoutAction() {
205       super("Route Edges", SHARED_LAYOUT_ICON);
206     }
207 
208     public void actionPerformed(ActionEvent e) {
209       strategy.getModule().start(view.getGraph2D());
210     }
211   }
212 
213   /**
214    * Adds a specially configured EditMode that will automatically route all newly created edges orthogonally. The
215    * orthogonal edge router will also be activated on some edges, when nodes get resized or a node selection gets
216    * moved.
217    */
218   protected void registerViewModes() {
219     EditMode mode = new EditMode();
220     mode.setMoveSelectionMode(paMode = new MyMoveSelectionMode());
221     mode.setCreateEdgeMode(new MyCreateEdgeMode());
222     mode.setHotSpotMode(new MyHotSpotMode());
223     mode.setMovePortMode(new MyMovePortMode());
224     view.addViewMode(mode);
225   }
226 
227   /** A special mode for creating edges. */
228   class MyCreateEdgeMode extends CreateEdgeMode {
229     MyCreateEdgeMode() {
230       super();
231       allowSelfloopCreation(false);
232     }
233 
234     protected void edgeCreated(final Edge e) {
235       final Graph2D graph = view.getGraph2D();
236 
237       strategy.routeEdge(e);
238 
239       graph.updateViews();
240     }
241   }
242 
243 
244   /** A special mode for resizing nodes. */
245   class MyHotSpotMode extends HotSpotMode {
246     public void mouseReleasedLeft(double x, double y) {
247       super.mouseReleasedLeft(x, y);
248 
249       final Graph2D graph = view.getGraph2D();
250 
251       DataProvider selectedNodes = Selections.createSelectionDataProvider(graph);
252       strategy.rerouteAdjacentEdges(selectedNodes, graph);
253       graph.updateViews();
254     }
255   }
256 
257   /** A special mode for moving a selection of the graph. */
258   class MyMoveSelectionMode extends PortAssignmentMoveSelectionMode {
259 
260     MyMoveSelectionMode() {
261       super(null, null);
262     }
263 
264     private boolean routeEdgesOnMove = true;
265 
266     protected BendList getBendsToBeMoved() {
267       BendList bends = super.getBendsToBeMoved();
268 
269       //add all bends from edges, whose source and target nodes are selected, since they will not be routed. 
270       for (NodeCursor nodeCursor = getGraph2D().selectedNodes(); nodeCursor.ok(); nodeCursor.next()) {
271         Node node = nodeCursor.node();
272         for(EdgeCursor edgeCursor = node.outEdges(); edgeCursor.ok(); edgeCursor.next()) {
273           Edge edge = edgeCursor.edge();
274           if(getGraph2D().isSelected(edge.target())){
275             for(BendCursor bendCursor = getGraph2D().getRealizer(edge).bends(); bendCursor.ok(); bendCursor.next()){
276               Bend bend = bendCursor.bend();
277               bends.add(bend);
278             }
279           }
280         }
281       }
282       return bends;
283     }
284 
285     protected void selectionOnMove(double dx, double dy, double x, double y) {
286       super.selectionOnMove(dx, dy, x, y);
287       if (routeEdgesOnMove) {
288         routeEdgesToSelection(false);
289       }
290     }
291 
292     protected void selectionMovedAction(double dx, double dy, double x, double y) {
293       super.selectionMovedAction(dx, dy, x, y);
294       routeEdgesToSelection(true);
295     }
296 
297     void routeEdgesToSelection(boolean includeBends) {
298       final Graph2D graph = view.getGraph2D();
299       if (graph.selectedNodes().ok() || (includeBends && graph.selectedBends().ok())) {
300         strategy.routeEdgesToSelection(graph);
301         graph.updateViews();
302       }
303     }
304   }
305 
306   class MyMovePortMode extends MovePortMode {
307     MyMovePortMode() {
308       setChangeEdgeEnabled(true);
309       setIndicatingTargetNode(true);
310     }
311 
312     protected void portMoved(Port port, double x, double y) {
313       super.portMoved(port, x, y);
314       final Edge edge = port.getOwner().getEdge();
315       strategy.routeEdge(edge);
316     }
317   }
318 
319 
320   /** Launches this demo. */
321   public static void main(String[] args) {
322     EventQueue.invokeLater(new Runnable() {
323       public void run() {
324         Locale.setDefault(Locale.ENGLISH);
325         initLnF();
326         (new EdgeRouterDemo()).start("Orthogonal Edge Router Demo");
327       }
328     });
329   }
330 
331   abstract static class RouterStrategy {
332     abstract YModule getModule();
333 
334     abstract void routeEdge(final Edge e);
335 
336     abstract void rerouteAdjacentEdges(final DataProvider selectedNodes, final Graph2D graph);
337 
338     abstract void routeEdgesToSelection(final Graph2D graph);
339 
340     abstract void route(final Graph2D graph);
341 
342     protected void routeEdge(final Edge e, final Graph2D graph) {
343       EdgeMap spc = (EdgeMap) graph.getDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY);
344       EdgeMap tpc = (EdgeMap) graph.getDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY);
345 
346       PortConstraintConfigurator pcc = new PortConstraintConfigurator();
347       if (spc != null && tpc != null) {
348         spc.set(e, pcc.createPortConstraintFromSketch(graph, e, true, false));
349         tpc.set(e, pcc.createPortConstraintFromSketch(graph, e, false, false));
350         route(graph);
351         spc.set(e, null);
352         tpc.set(e, null);
353       } else {
354         route(graph);
355       }
356     }
357 
358     protected void routeEdgesToSelection(final Graph2D graph, final Object affectedEdgesKey) {
359       final Set selectedEdges = new HashSet();
360       for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
361         final Edge edge = ec.edge();
362         if (graph.isSelected(edge.source()) ^ graph.isSelected(edge.target())) {
363           selectedEdges.add(edge);
364           continue;
365         }
366         for (BendCursor bc = graph.selectedBends(); bc.ok(); bc.next()) {
367           final Bend bend = (Bend) bc.current();
368           if (bend.getEdge() == edge) {
369             selectedEdges.add(edge);
370             break;
371           }
372         }
373       }
374       graph.addDataProvider(affectedEdgesKey, new DataProviderAdapter() {
375         public boolean getBool(Object dataHolder) {
376           return selectedEdges.contains(dataHolder);
377         }
378       });
379       route(graph);
380       graph.removeDataProvider(affectedEdgesKey);
381     }
382 
383     protected void routeEdge(final Edge e, final Graph2D graph, final Object selectedEdgesKey) {
384       graph.addDataProvider(selectedEdgesKey, new DataProviderAdapter() {
385         public boolean getBool(Object o) {
386           return e == o;
387         }
388       });
389       routeEdge(e, graph);
390       graph.removeDataProvider(selectedEdgesKey);
391     }
392   }
393 
394   static class OrthogonalEdgeRouterStrategy extends RouterStrategy {
395     private final MyOrthogonalEdgeRouterModule module;
396 
397     public OrthogonalEdgeRouterStrategy() {
398       module = new MyOrthogonalEdgeRouterModule();
399       // disables the routing animation
400       module.setMorphingEnabled(false);
401     }
402     
403     public YModule getModule() {
404       return module;
405     }
406 
407     public void routeEdge(final Edge e) {
408       final Graph2D graph = (Graph2D) e.getGraph();
409       module.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_SELECTED_EDGES);
410       routeEdge(e, graph, Layouter.SELECTED_EDGES);
411       module.resetSphereOfAction();
412     }
413 
414     public void rerouteAdjacentEdges(final DataProvider selectedNodes, final Graph2D graph) {
415       module.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_EDGES_AT_SELECTED_NODES);
416       graph.addDataProvider(Layouter.SELECTED_NODES, selectedNodes);
417       this.route(graph);
418       graph.removeDataProvider(Layouter.SELECTED_NODES);
419       module.resetSphereOfAction();
420     }
421 
422     public void routeEdgesToSelection(final Graph2D graph) {
423       module.setSphereOfAction(OrthogonalEdgeRouter.ROUTE_SELECTED_EDGES);
424       routeEdgesToSelection(graph, Layouter.SELECTED_EDGES);
425       module.resetSphereOfAction();
426     }
427 
428     void route(final Graph2D graph) {
429       module.start(graph);
430     }
431   }
432 
433   /**
434    *  Overwrites (if necessary) the scope (or sphere of action) of the routing
435    *  algorithm.
436    */
437   private static class MyOrthogonalEdgeRouterModule extends OrthogonalEdgeRouterModule {
438     private final byte UNDEFINED = -1;
439     private byte sphereOfAction = UNDEFINED;
440 
441     protected void configure(final OrthogonalEdgeRouter orthogonal, final OptionHandler options) {
442       super.configure(orthogonal, options);
443 
444       if (sphereOfAction != UNDEFINED) {
445         orthogonal.setSphereOfAction(sphereOfAction);
446       }
447     }
448 
449     public void setSphereOfAction(final byte sphereOfAction) {
450       this.sphereOfAction = sphereOfAction;
451     }
452 
453     public void resetSphereOfAction() {
454       sphereOfAction = UNDEFINED;
455     }
456   }
457 
458   static class ChannelEdgeRouterStrategy extends RouterStrategy {
459     private final ChannelEdgeRouterModule module;
460     
461     public ChannelEdgeRouterStrategy() {
462       module = new ChannelEdgeRouterModule();
463       // disables the routing animation
464       module.setMorphingEnabled(false);
465     }
466 
467     public YModule getModule() {
468       return module;
469     }
470 
471     public void routeEdge(final Edge e) {
472       final Graph2D graph = (Graph2D) e.getGraph();
473       routeEdge(e, graph, ChannelEdgeRouter.AFFECTED_EDGES);
474     }
475 
476     public void rerouteAdjacentEdges(final DataProvider selectedNodes, final Graph2D graph) {
477       graph.addDataProvider(ChannelEdgeRouter.AFFECTED_EDGES, new DataProviderAdapter() {
478         public boolean getBool(Object dataHolder) {
479           return selectedNodes.getBool((((Edge) dataHolder).source())) || selectedNodes.getBool(((Edge) dataHolder).target());
480         }
481       });
482       this.route(graph);
483       graph.removeDataProvider(ChannelEdgeRouter.AFFECTED_EDGES);
484     }
485 
486     public void routeEdgesToSelection(final Graph2D graph) {
487       routeEdgesToSelection(graph, ChannelEdgeRouter.AFFECTED_EDGES);
488     }
489 
490     void route(final Graph2D graph) {
491       module.start(graph);
492     }
493   }
494 
495   static class PolyLineEdgeRouterStrategy extends RouterStrategy {
496     private final MyPolylineEdgeRouterModule module;
497 
498     PolyLineEdgeRouterStrategy() {
499       module = new MyPolylineEdgeRouterModule();
500       // disables routing animation
501       module.setMorphingEnabled(false);
502     }
503 
504     YModule getModule() {
505       return module;
506     }
507 
508     void routeEdge(final Edge e) {
509       final Graph2D graph = (Graph2D) e.getGraph();
510       module.setSphereOfAction(EdgeRouter.ROUTE_SELECTED_EDGES);
511       routeEdge(e, graph, Layouter.SELECTED_EDGES);
512       module.resetSphereOfAction();
513     }
514 
515     void rerouteAdjacentEdges(final DataProvider selectedNodes, final Graph2D graph) {
516       module.setSphereOfAction(EdgeRouter.ROUTE_SELECTED_EDGES);
517       graph.addDataProvider(Layouter.SELECTED_EDGES, new DataProviderAdapter() {
518         public boolean getBool(Object dataHolder) {
519           return selectedNodes.getBool(((Edge) dataHolder).source())
520               || selectedNodes.getBool(((Edge) dataHolder).target());
521         }
522       });
523       this.route(graph);
524       graph.removeDataProvider(Layouter.SELECTED_EDGES);
525       module.resetSphereOfAction();
526     }
527 
528     void routeEdgesToSelection(final Graph2D graph) {
529       module.setSphereOfAction(EdgeRouter.ROUTE_SELECTED_EDGES);
530       routeEdgesToSelection(graph, Layouter.SELECTED_EDGES);
531       module.resetSphereOfAction();
532     }
533 
534     void route(final Graph2D graph) {
535       module.start(graph);
536     }
537   }
538 
539   /**
540    * Sets custom initial option values and (if necessary) overwrites the scope
541    * (or sphere of action) for the routing algorithm.
542    */
543   private static class MyPolylineEdgeRouterModule extends PolylineEdgeRouterModule {
544     private static final byte UNDEFINED = -1;
545     private byte sphereOfAction = UNDEFINED;
546 
547     protected OptionHandler createOptionHandler() {
548       final OptionHandler options = super.createOptionHandler();
549       options.set(ITEM_ENABLE_POLYLINE_ROUTING, Boolean.TRUE);
550       return options;
551     }
552 
553     protected void configure(EdgeRouter router, OptionHandler options) {
554       super.configure(router, options);
555 
556       if (sphereOfAction != UNDEFINED) {
557         router.setSphereOfAction(EdgeRouter.ROUTE_SELECTED_EDGES);
558       }
559     }
560 
561     public void setSphereOfAction(final byte sphereOfAction) {
562       this.sphereOfAction = sphereOfAction;
563     }
564 
565     public void resetSphereOfAction() {
566       sphereOfAction = UNDEFINED;
567     }
568   }
569 }
570 
571 
572       
573