Routing multiple edges between two nodes in a parallel fashion
Tips & TricksSummary
This article provides a demo that shows how to use ParallelEdgeLayouter to route multiple edges between two nodes.
Description
The following demo code will route multiple edges between two nodes as soon as one drags the adjacent node or creates a new edge. It is meant to show the usage of ParallelEdgeLayouter
The demo source code looks like this:
package demo.view.advanced;
import demo.view.DemoBase;
import y.base.Edge;
import y.base.EdgeCursor;
import y.base.EdgeList;
import y.base.NodeList;
import y.base.Node;
import y.layout.LayoutTool;
import y.layout.ParallelEdgeLayouter;
import y.util.GraphHider;
import y.view.Arrow;
import y.view.EditMode;
import y.view.MoveSelectionMode;
import y.view.CreateEdgeMode;
import y.view.Graph2D;
import javax.swing.JToolBar;
import javax.swing.AbstractAction;
import java.awt.event.ActionEvent;
/**
* This class creates a simple graph editor with its own MoveSelectionMode and CreateEdgeMode. Whenever nodes are moved
* all adjacent edges that have the same source and target nodes are routed parallel using {@link ParallelEdgeLayouter}.
* Whenever an edge is created all edges connecting from/to the same target/source node of this edge will be routed
* parallel using {@link ParallelEdgeLayouter}.
*/
public class ParallelEdgeLayouterDemo extends DemoBase {
private ParallelEdgeLayouter parallelEdeLayouter;
private final int LINE_DISTANCE = 5;
public ParallelEdgeLayouterDemo() {
Graph2D graph = view.getGraph2D();
graph.getDefaultEdgeRealizer().setTargetArrow(Arrow.STANDARD);
//create parallel edge layouter
parallelEdeLayouter = new ParallelEdgeLayouter();
//set a line distance between parallel edges
parallelEdeLayouter.setLineDistance(LINE_DISTANCE);
// join end points of parallel edges
parallelEdeLayouter.setJoinEndsEnabled(true);
fillGraphInitially(graph);
}
private void fillGraphInitially(Graph2D graph) {
//create some nodes
Node node1 = graph.createNode(100, 100, "1");
Node node2 = graph.createNode(100, 200, "2");
Node node3 = graph.createNode(200, 200, "3");
//create some parallel edges
for (int i = 0; i < 4; i++) {
if (i % 2 == 0) {
graph.createEdge(node1, node2);
graph.createEdge(node2, node3);
} else {
graph.createEdge(node2, node1);
graph.createEdge(node3, node2);
}
}
view.fitContent();
view.updateView();
}
protected JToolBar createToolBar() {
JToolBar toolbar = super.createToolBar();
//add an action to reset all edge paths.
toolbar.add(new AbstractAction("Reset Edge Paths") {
public void actionPerformed(ActionEvent e) {
LayoutTool.resetPaths(view.getGraph2D());
view.updateView();
}
});
return toolbar;
}
protected EditMode createEditMode() {
EditMode editMode = super.createEditMode();
//set our own MoveSelectionMode and CreateEdgeMode
editMode.setCreateEdgeMode(new ParallelEdgeCreateEdgeMode());
editMode.setMoveSelectionMode(new ParallelEdgeMoveSelectionMode());
return editMode;
}
/**
* Routes the edges of the given list using {@link y.layout.ParallelEdgeLayouter}.
*
* @param affectedEdges a list of edges that shall be routed in a parallel fashion
*/
private void routeParallelEdges(EdgeList affectedEdges) {
Graph2D graph = view.getGraph2D();
//GraphHider is needed to hide all edges that shall not be routed by ParallelEdgeLayouter
GraphHider hider = new GraphHider(graph);
for (EdgeCursor edgeCursor = graph.edges(); edgeCursor.ok(); edgeCursor.next()) {
Edge edge = edgeCursor.edge();
if (affectedEdges.contains(edge)) {
//remove bends from affected edges
LayoutTool.resetPath(graph, edge);
} else {
//hide unaffected edges from layouter
hider.hide(edge);
}
}
//do the layout
parallelEdeLayouter.doLayout(graph);
//unhide unaffected edges
hider.unhideAll();
}
/**
* A ViewMode that handles edge creations. It will reroute all edges that have the same source and target node as the
* newly created edge using {@link y.layout.ParallelEdgeLayouter}
*/
class ParallelEdgeCreateEdgeMode extends CreateEdgeMode {
private EdgeList affectedEdges;
public ParallelEdgeCreateEdgeMode() {
affectedEdges = new EdgeList();
}
protected void edgeCreated(Edge edge) {
super.edgeCreated(edge);
//collect all edges from the created edge's source to the created edge's target node, these must be rerouted
for (EdgeCursor edgeCursor = edge.source().outEdges(); edgeCursor.ok(); edgeCursor.next()) {
Edge outEdge = edgeCursor.edge();
if (outEdge.target() == edge.target()) {
affectedEdges.add(outEdge);
}
}
//collect all edges from the created edge's target to the created edge's source node, these must be rerouted
for (EdgeCursor edgeCursor = edge.source().inEdges(); edgeCursor.ok(); edgeCursor.next()) {
Edge inEdge = edgeCursor.edge();
if (inEdge.source() == edge.target()) {
affectedEdges.add(inEdge);
}
}
routeParallelEdges(affectedEdges);
affectedEdges.clear();
}
}
/**
* A ViewMode that handles selection movement. It will reroute edges between selected and unselected nodes using
* {@link y.layout.ParallelEdgeLayouter} when the selected nodes are moved.
*/
class ParallelEdgeMoveSelectionMode extends MoveSelectionMode {
private EdgeList affectedEdges;
public ParallelEdgeMoveSelectionMode() {
affectedEdges = new EdgeList();
}
protected void selectionOnMove(double dx, double dy, double x, double y) {
super.selectionOnMove(dx, dy, x, y);
routeParallelEdges(affectedEdges);
}
protected void selectionMoveStarted(double x, double y) {
super.selectionMoveStarted(x, y);
//fill list with edges that shall be routed
for (EdgeCursor edgeCursor = getGraph2D().edges(); edgeCursor.ok(); edgeCursor.next()) {
Edge edge = edgeCursor.edge();
NodeList movedNodes = getNodesToBeMoved();
//if the source or the target of an edge is moved and the counterpart is not moved, we want to reroute the edge
//if the source *and* target is moved, we do *not* want to reroute the edge
if (movedNodes.contains(edge.source()) != movedNodes.contains(edge.target())) {
affectedEdges.add(edge);
}
}
}
protected void selectionMovedAction(double dx, double dy, double x, double y) {
super.selectionMovedAction(dx, dy, x, y);
routeParallelEdges(affectedEdges);
affectedEdges.clear();
}
}
public static void main(String[] args) {
initLnF();
ParallelEdgeLayouterDemo demo = new ParallelEdgeLayouterDemo();
demo.start("ParallelEdgelayouter Demo");
}
}
Resources
Categories this article belongs to:
yFiles for Java > yFiles Layout > Automatic Graph Layout
Applies to:
yFiles for Java 2: 2.5, 2.6, 2.7, 2.8, 2.9, 2.10, 2.11, 2.12, 2.13, 2.14, 2.15, 2.16, 2.17, 2.18
Keywords:
ParallelEdgeLayouter - multiple - route - edge - router