diff --git a/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/p5edges/OrthogonalEdgeRouter.java b/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/p5edges/OrthogonalEdgeRouter.java
index 61a81ffde2..4d82ab66ad 100644
--- a/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/p5edges/OrthogonalEdgeRouter.java
+++ b/plugins/org.eclipse.elk.alg.layered/src/org/eclipse/elk/alg/layered/p5edges/OrthogonalEdgeRouter.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2010, 2015 Kiel University and others.
+ * Copyright (c) 2010, 2020 Kiel University and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
@@ -50,10 +50,6 @@
*
Postcondition:each node is assigned a horizontal coordinate;
* the bend points of each edge are set; the width of the whole graph is set
*
- *
- * @author msp
- * @author cds
- * @author jjc
*/
public final class OrthogonalEdgeRouter implements ILayoutPhase {
@@ -262,18 +258,22 @@ public void process(final LGraph layeredGraph, final IElkProgressMonitor monitor
PolylineEdgeRouter.PRED_EXTERNAL_WEST_OR_EAST_PORT);
if (slotsCount > 0) {
- // The space between each pair of edge segments, and between nodes and edges
- double increment =
- edgeNodeSpacing + (slotsCount - 1) * edgeEdgeSpacing;
+ // Compute routing area's width
+ double routingWidth = (slotsCount - 1) * edgeEdgeSpacing;
+
+ if (leftLayer != null) {
+ routingWidth += edgeNodeSpacing;
+ }
+
if (rightLayer != null) {
- increment += edgeNodeSpacing;
+ routingWidth += edgeNodeSpacing;
}
// If we are between two layers, make sure their minimal spacing is preserved
- if (increment < nodeNodeSpacing && !isLeftLayerExternal && !isRightLayerExternal) {
- increment = nodeNodeSpacing;
+ if (routingWidth < nodeNodeSpacing && !isLeftLayerExternal && !isRightLayerExternal) {
+ routingWidth = nodeNodeSpacing;
}
- xpos += increment;
+ xpos += routingWidth;
} else if (!isLeftLayerExternal && !isRightLayerExternal) {
// If all edges are straight, use the usual spacing
xpos += nodeNodeSpacing;
diff --git a/test/org.eclipse.elk.alg.layered.test/src/org/eclipse/elk/alg/layered/issues/Issue734Test.java b/test/org.eclipse.elk.alg.layered.test/src/org/eclipse/elk/alg/layered/issues/Issue734Test.java
new file mode 100644
index 0000000000..09a5360f9e
--- /dev/null
+++ b/test/org.eclipse.elk.alg.layered.test/src/org/eclipse/elk/alg/layered/issues/Issue734Test.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Kiel University and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.elk.alg.layered.issues;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.DoubleSummaryStatistics;
+import java.util.List;
+
+import org.eclipse.elk.alg.layered.intermediate.greedyswitch.TestGraphCreator;
+import org.eclipse.elk.alg.layered.options.LayeredOptions;
+import org.eclipse.elk.alg.test.framework.LayoutTestRunner;
+import org.eclipse.elk.alg.test.framework.annotations.Algorithm;
+import org.eclipse.elk.alg.test.framework.annotations.DefaultConfiguration;
+import org.eclipse.elk.alg.test.framework.annotations.GraphResourceProvider;
+import org.eclipse.elk.alg.test.framework.io.AbstractResourcePath;
+import org.eclipse.elk.alg.test.framework.io.FileNameFilter;
+import org.eclipse.elk.alg.test.framework.io.ModelResourcePath;
+import org.eclipse.elk.core.util.ElkUtil;
+import org.eclipse.elk.graph.ElkEdge;
+import org.eclipse.elk.graph.ElkNode;
+import org.eclipse.emf.common.util.EList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import com.google.common.collect.Lists;
+
+@RunWith(LayoutTestRunner.class)
+@Algorithm(LayeredOptions.ALGORITHM_ID)
+public class Issue734Test extends TestGraphCreator {
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Sources
+
+ @GraphResourceProvider
+ public List testGraphs() {
+ return Lists.newArrayList(
+ new ModelResourcePath("tickets/layered/734_nodeEdgeSpacingFlat.elkt"),
+ new ModelResourcePath("tickets/layered/734_nodeEdgeSpacingFlatMoreComplex.elkt"),
+ new ModelResourcePath("tickets/layered/734_nodeEdgeSpacingHierarchical.elkt"));
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Tests
+
+ @Test
+ public void testOrthogonalEdges(final ElkNode graph) {
+ // Compute the area occupied by nodes on the top level
+ CoordinateSpan nodeSpan = computeNodeCoordinateSpan(graph.getChildren());
+ CoordinateSpan edgeSpan = computeEdgeCoordinateSpan(graph.getContainedEdges());
+
+ // If the edge is routed properly, the edge span should have the same distance to the node span on both
+ // sides. In other words, their respective centres should be aligned
+ double nodeSpanCenter = (nodeSpan.min + nodeSpan.max) / 2;
+ double edgeSpanCenter = (edgeSpan.min + edgeSpan.max) / 2;
+
+ assertEquals(nodeSpanCenter, edgeSpanCenter, 0.5);
+ }
+
+ private CoordinateSpan computeNodeCoordinateSpan(EList nodes) {
+ CoordinateSpan span = new CoordinateSpan();
+
+ span.min = nodes.stream()
+ .mapToDouble(node -> node.getX())
+ .min()
+ .orElse(0);
+ span.max = nodes.stream()
+ .mapToDouble(node -> node.getX() + node.getWidth())
+ .max()
+ .orElse(0);
+
+ return span;
+ }
+
+ private CoordinateSpan computeEdgeCoordinateSpan(EList containedEdges) {
+ DoubleSummaryStatistics stats = containedEdges.stream()
+ .flatMap(edge -> edge.getSections().stream())
+ .map(section -> ElkUtil.createVectorChain(section))
+ .flatMap(chain -> chain.stream())
+ .mapToDouble(vec -> vec.x)
+ .summaryStatistics();
+ return new CoordinateSpan(stats.getMin(), stats.getMax());
+ }
+
+ private static class CoordinateSpan {
+ private double min = Double.POSITIVE_INFINITY;
+ private double max = Double.NEGATIVE_INFINITY;
+
+ public CoordinateSpan() {
+ }
+
+ public CoordinateSpan(double min, double max) {
+ this.min = min;
+ this.max = max;
+ }
+ }
+
+}