Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Barbin2014-11-18 09:18:39 +0000
committerFlorian Barbin2014-12-01 16:05:12 +0000
commitdeae56838017dae692745c2579845ad934832575 (patch)
treedd45775d33200d9a4f03d3e3f07d6d27a997dd5f
parent7f1e40da9ac5f691b33104fe1aafe7b952a197c8 (diff)
downloadorg.eclipse.sirius-deae56838017dae692745c2579845ad934832575.tar.gz
org.eclipse.sirius-deae56838017dae692745c2579845ad934832575.tar.xz
org.eclipse.sirius-deae56838017dae692745c2579845ad934832575.zip
[448739] Improve the centering for straight and 2 segments edges.
* Some cases with straight rectilinear edges or two segments rectilinear edges were not correctly handled if the two edge ends were centered. see Comment 12 [1] * Externalize the centered algorithm. (to use with the rectilinear router) [1] https://bugs.eclipse.org/bugs/show_bug.cgi?id=448739#c12 Bug: 448739 Change-Id: I7e8ff468893d6e767b23313db2af84d2fcf71c90 Signed-off-by: Florian Barbin <florian.barbin@obeo.fr>
-rw-r--r--plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/internal/operation/CenterEdgeEndModelChangeOperation.java117
-rw-r--r--plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/tools/internal/routers/RectilinearEdgeUtil.java278
2 files changed, 312 insertions, 83 deletions
diff --git a/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/internal/operation/CenterEdgeEndModelChangeOperation.java b/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/internal/operation/CenterEdgeEndModelChangeOperation.java
index 8dc42e9c95..ae0301169b 100644
--- a/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/internal/operation/CenterEdgeEndModelChangeOperation.java
+++ b/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/internal/operation/CenterEdgeEndModelChangeOperation.java
@@ -18,7 +18,6 @@ import java.util.List;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.ConnectionLayer;
import org.eclipse.draw2d.ConnectionRouter;
-import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
@@ -54,6 +53,7 @@ import org.eclipse.sirius.diagram.ui.business.internal.operation.AbstractModelCh
import org.eclipse.sirius.diagram.ui.graphical.figures.CanonicalRectilinearRouter;
import org.eclipse.sirius.diagram.ui.internal.edit.parts.DEdgeEditPart;
import org.eclipse.sirius.diagram.ui.internal.refresh.GMFHelper;
+import org.eclipse.sirius.diagram.ui.tools.internal.routers.RectilinearEdgeUtil;
import org.eclipse.sirius.diagram.ui.tools.internal.util.GMFNotationUtilities;
import org.eclipse.sirius.ext.base.Option;
import org.eclipse.sirius.ext.base.Options;
@@ -69,6 +69,7 @@ import org.eclipse.ui.IEditorPart;
* @author Florian Barbin
*
*/
+@SuppressWarnings("restriction")
public class CenterEdgeEndModelChangeOperation extends AbstractModelChangeOperation<Void> {
private Edge edge;
@@ -91,6 +92,8 @@ public class CenterEdgeEndModelChangeOperation extends AbstractModelChangeOperat
private Dimension targetFigureSize;
+ private static final String CENTERED_ANCHOR = GMFNotationUtilities.getTerminalString(0.5d, 0.5d);
+
/**
* Constructor to use the connectionEditPart to compute the new edge
* bendpoints.
@@ -347,8 +350,6 @@ public class CenterEdgeEndModelChangeOperation extends AbstractModelChangeOperat
*/
private void handleRectilinearCase(CenteringStyle center, Rectangle sourceBounds, Rectangle targetBounds, PointList existingPointList) {
- int sourceAnchorOrientation = PositionConstants.NONE;
- int targetAnchorOrientation = PositionConstants.NONE;
PointList rectilinear = null;
// If the connection is available (the edge already exist) we retrieve
@@ -356,9 +357,6 @@ public class CenterEdgeEndModelChangeOperation extends AbstractModelChangeOperat
if (connection != null) {
rectilinear = getRectilinearPointListFromConnection();
- sourceAnchorOrientation = computeSourceOrientation(rectilinear);
- targetAnchorOrientation = computeTargetOrientation(rectilinear);
-
} else {
rectilinear = existingPointList.getCopy();
@@ -373,16 +371,15 @@ public class CenterEdgeEndModelChangeOperation extends AbstractModelChangeOperat
CanonicalRectilinearRouter rectilinearRouter = new CanonicalRectilinearRouter();
rectilinearRouter.routeLine(edge, 0, rectilinear, sourceBounds, targetBounds);
-
- sourceAnchorOrientation = computeSourceOrientation(rectilinear);
- targetAnchorOrientation = computeTargetOrientation(rectilinear);
}
if (rectilinear.size() >= 2) {
+ RectilinearEdgeUtil.centerEdgeEnds(rectilinear, newSourceAnchorAbsoluteLocation, newTargetAnchorAbsoluteLocation, center);
+
if (center == CenteringStyle.BOTH || center == CenteringStyle.SOURCE) {
- handleSourceRectilinearRoutingStyle(sourceBounds, rectilinear, sourceAnchorOrientation);
+ centerSourceAnchor();
}
if (center == CenteringStyle.BOTH || center == CenteringStyle.TARGET) {
- handleTargetRectilinearRoutingStyle(targetBounds, rectilinear, targetAnchorOrientation);
+ centerTargetAnchor();
}
existingPointList.removeAllPoints();
@@ -390,20 +387,6 @@ public class CenterEdgeEndModelChangeOperation extends AbstractModelChangeOperat
}
}
- private int computeSourceOrientation(PointList rectilinear) {
- if (rectilinear.size() >= 2) {
- return (rectilinear.getPoint(0).x() == rectilinear.getPoint(1).x()) ? PositionConstants.VERTICAL : PositionConstants.HORIZONTAL;
- }
- return PositionConstants.NONE;
- }
-
- private int computeTargetOrientation(PointList rectilinear) {
- if (rectilinear.size() >= 2) {
- return (rectilinear.getPoint(rectilinear.size() - 1).x() == rectilinear.getPoint(rectilinear.size() - 2).x()) ? PositionConstants.VERTICAL : PositionConstants.HORIZONTAL;
- }
- return PositionConstants.NONE;
- }
-
private void computePointListByIntersections(PointList rectilinear, Rectangle sourceBounds, Rectangle targetBounds) {
Option<Point> sourceConnectionPoint = GraphicalHelper.getIntersection(existingSourceAnchorAbsoluteLocation, existingTargetAnchorAbsoluteLocation, sourceBounds, false);
Option<Point> targetConnectionPoint = GraphicalHelper.getIntersection(existingSourceAnchorAbsoluteLocation, existingTargetAnchorAbsoluteLocation, targetBounds, false);
@@ -414,57 +397,6 @@ public class CenterEdgeEndModelChangeOperation extends AbstractModelChangeOperat
}
}
- private void handleSourceRectilinearRoutingStyle(Rectangle figureBounds, PointList rectilinear, int sourceAnchorRelativeLocation) {
-
- Point newConnectionPoint = rectilinear.getFirstPoint().getCopy();
- Point secondFromSrc = rectilinear.getPoint(1);
- switch (sourceAnchorRelativeLocation) {
- case PositionConstants.HORIZONTAL:
- newConnectionPoint.setY(newSourceAnchorAbsoluteLocation.y());
- secondFromSrc.setY(newSourceAnchorAbsoluteLocation.y());
- break;
-
- case PositionConstants.VERTICAL:
- newConnectionPoint.setX(newSourceAnchorAbsoluteLocation.x());
- secondFromSrc.setX(newSourceAnchorAbsoluteLocation.x());
- break;
-
- default:
- // this case should not happen.
- break;
- }
-
- rectilinear.setPoint(newConnectionPoint, 0);
- rectilinear.setPoint(secondFromSrc, 1);
- centerSourceAnchor();
-
- }
-
- private void handleTargetRectilinearRoutingStyle(Rectangle figureBounds, PointList rectilinear, int targetAnchorRelativeLocation) {
-
- Point newConnectionPoint = rectilinear.getLastPoint().getCopy();
- Point secondFromTgt = rectilinear.getPoint(rectilinear.size() - 2);
- switch (targetAnchorRelativeLocation) {
- case PositionConstants.HORIZONTAL:
- newConnectionPoint.setY(newTargetAnchorAbsoluteLocation.y());
- secondFromTgt.setY(newTargetAnchorAbsoluteLocation.y());
- break;
-
- case PositionConstants.VERTICAL:
- newConnectionPoint.setX(newTargetAnchorAbsoluteLocation.x());
- secondFromTgt.setX(newTargetAnchorAbsoluteLocation.x());
- break;
-
- default:
- // this case should not happen.
- break;
- }
-
- rectilinear.setPoint(newConnectionPoint, rectilinear.size() - 1);
- rectilinear.setPoint(secondFromTgt, rectilinear.size() - 2);
- centerTargetAnchor();
- }
-
private void computeAndSetNewAnchorsAbsoluteCoordinates(Rectangle sourceBounds, Rectangle targetBounds, CenteringStyle center) {
newSourceAnchorAbsoluteLocation = existingSourceAnchorAbsoluteLocation;
@@ -501,6 +433,7 @@ public class CenterEdgeEndModelChangeOperation extends AbstractModelChangeOperat
if (option.some()) {
pointList = option.get();
} else {
+ @SuppressWarnings("unchecked")
List<RelativeBendpoint> relativeBendpoints = bendpoints.getPoints();
for (int i = 0; i < relativeBendpoints.size(); i++) {
float weight = i / ((float) relativeBendpoints.size() - 1);
@@ -531,18 +464,36 @@ public class CenterEdgeEndModelChangeOperation extends AbstractModelChangeOperat
* Set the source anchor at (0.5, 0.5).
*/
private void centerSourceAnchor() {
- IdentityAnchor a = NotationFactory.eINSTANCE.createIdentityAnchor();
- a.setId(GMFNotationUtilities.getTerminalString(0.5d, 0.5d));
- edge.setSourceAnchor(a);
+ Anchor currentAnchor = edge.getSourceAnchor();
+ IdentityAnchor newAnchor = setOrCreateNewAnchorIfNeeded(currentAnchor);
+ if (newAnchor != currentAnchor) {
+ edge.setSourceAnchor(newAnchor);
+ }
+
}
/**
* Set the target anchor at (0.5, 0.5).
*/
private void centerTargetAnchor() {
- IdentityAnchor a = NotationFactory.eINSTANCE.createIdentityAnchor();
- a.setId(GMFNotationUtilities.getTerminalString(0.5d, 0.5d));
- edge.setTargetAnchor(a);
+ Anchor currentAnchor = edge.getTargetAnchor();
+ IdentityAnchor newAnchor = setOrCreateNewAnchorIfNeeded(currentAnchor);
+ if (newAnchor != currentAnchor) {
+ edge.setTargetAnchor(newAnchor);
+ }
+ }
+
+ private IdentityAnchor setOrCreateNewAnchorIfNeeded(Anchor anchor) {
+ if (anchor instanceof IdentityAnchor) {
+ if (!((IdentityAnchor) anchor).getId().equals(CENTERED_ANCHOR)) {
+ ((IdentityAnchor) anchor).setId(CENTERED_ANCHOR);
+ }
+ return (IdentityAnchor) anchor;
+ } else {
+ IdentityAnchor a = NotationFactory.eINSTANCE.createIdentityAnchor();
+ a.setId(CENTERED_ANCHOR);
+ return a;
+ }
}
/**
@@ -608,6 +559,7 @@ public class CenterEdgeEndModelChangeOperation extends AbstractModelChangeOperat
/**
* We try to retrieve the edge connection figure.
*/
+ @SuppressWarnings("rawtypes")
private void setConnectionIfNull() {
if (connection != null || !useFigure) {
return;
@@ -653,7 +605,6 @@ public class CenterEdgeEndModelChangeOperation extends AbstractModelChangeOperat
*
* @return a rectilinear PointList.
*/
- @SuppressWarnings("restriction")
private PointList getRectilinearPointListFromConnection() {
ConnectionRouter oldConnectionRouter = connection.getConnectionRouter();
boolean needToRetrieveOldRouter = false;
diff --git a/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/tools/internal/routers/RectilinearEdgeUtil.java b/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/tools/internal/routers/RectilinearEdgeUtil.java
new file mode 100644
index 0000000000..d301259427
--- /dev/null
+++ b/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/tools/internal/routers/RectilinearEdgeUtil.java
@@ -0,0 +1,278 @@
+/*******************************************************************************
+ * Copyright (c) 2014 THALES GLOBAL SERVICES and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Obeo - initial API and implementation
+ * IBM Corporation - removeRedundantPoints(PointList) method
+ *******************************************************************************/
+
+package org.eclipse.sirius.diagram.ui.tools.internal.routers;
+
+import org.eclipse.draw2d.PositionConstants;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.gmf.runtime.draw2d.ui.geometry.LineSeg;
+import org.eclipse.sirius.diagram.description.CenteringStyle;
+
+/**
+ * Utility class for rectilinear edges. Handles edge centering.
+ *
+ * @author Florian Barbin
+ *
+ */
+public final class RectilinearEdgeUtil {
+
+ /**
+ * The minimal distance between an edge connection and the first bendpoint
+ * to add (if we need to add bendpoints).
+ */
+ private static final int MINIMAL_SEGMENT_SIZE = 20;
+
+ private RectilinearEdgeUtil() {
+ // private constructor.
+ }
+
+ /**
+ * Centers the edge ends of the given rectilinear point list.
+ *
+ * @param line
+ * the point list (in absolute coordinates). We assume this list
+ * is rectilinear.
+ * @param srcRefPoint
+ * the source anchor absolute location. Must be centered if the
+ * centering style is BOTH or SOURCE.
+ * @param tgtRefPoint
+ * the target anchor absolute location. Must be centered if the
+ * centering style is BOTH or TARGET.
+ * @param centeringStyle
+ * the centering style.
+ */
+ public static void centerEdgeEnds(PointList line, Point srcRefPoint, Point tgtRefPoint, CenteringStyle centeringStyle) {
+ if (CenteringStyle.BOTH == centeringStyle || CenteringStyle.SOURCE == centeringStyle) {
+
+ addPointsIfNeeded(line, srcRefPoint, true);
+ alignSegmentTowardAnchor(line, srcRefPoint, true);
+ }
+ if (CenteringStyle.BOTH == centeringStyle || CenteringStyle.TARGET == centeringStyle) {
+
+ addPointsIfNeeded(line, tgtRefPoint, false);
+ alignSegmentTowardAnchor(line, tgtRefPoint, false);
+ }
+ removeRedundantPoints(line);
+ }
+
+ /**
+ * Adds transitional bendpoints in the cases where centering one segment
+ * changes the other segment orientation.
+ *
+ * @param line
+ * the rectilinear edge point list.
+ * @param absoluteAnchorCoordinates
+ * the anchor absolute coordinates where the segment should be
+ * aligned
+ * @param isFirst
+ * true if it concerns the source or false for the target.
+ */
+ private static void addPointsIfNeeded(PointList line, Point absoluteAnchorCoordinates, boolean isFirst) {
+ if (line.size() == 2) {
+ addPointForStraightEdge(line);
+ } else if (line.size() == 3 && isOppositeEndWillBeAffected(line, absoluteAnchorCoordinates, isFirst)) {
+ addPointForLEdge(line, isFirst);
+ }
+ }
+
+ /**
+ * For straight edges, we need to had 2 bendpoints at the middle of the line
+ * to center only one segment at time.
+ *
+ * @param line
+ * the current 2 points list. This point list will be modified by
+ * adding two points in the middle.
+ */
+ private static void addPointForStraightEdge(PointList line) {
+ Point pointToInsert = null;
+ int segmentOrientation = line.getFirstPoint().x == line.getLastPoint().x ? PositionConstants.VERTICAL : PositionConstants.HORIZONTAL;
+ if (segmentOrientation == PositionConstants.VERTICAL) {
+ int xValue = line.getFirstPoint().x;
+ int delta = (line.getFirstPoint().y - line.getLastPoint().y) / 2;
+ pointToInsert = new Point(xValue, line.getFirstPoint().y - delta);
+ } else {
+ int yValue = line.getFirstPoint().y;
+ int delta = (line.getFirstPoint().x - line.getLastPoint().x) / 2;
+ pointToInsert = new Point(line.getFirstPoint().x - delta, yValue);
+ }
+ line.insertPoint(pointToInsert, 1);
+ line.insertPoint(pointToInsert.getCopy(), 1);
+
+ }
+
+ /**
+ * In the case of an edge with 3 bendpoints (the edge forms a "L"), shifting
+ * one segment could change the other segment connection point.
+ *
+ * @param newLine
+ * the edge point list.
+ * @param absoluteAnchorCoordinates
+ * the coordinate of the anchor we want to align the segment.
+ * @param isFirst
+ * true if want to move the source segment. Otherwise false.
+ * @return True if the perpendicular segment will change of shape face.
+ */
+ private static boolean isOppositeEndWillBeAffected(PointList line, Point absoluteAnchorCoordinates, boolean isFirst) {
+ LineSeg oppositeSegment = getSegment(line, !isFirst);
+ int origTermDelta = 0;
+ int origNewLocDelta = 0;
+
+ if (oppositeSegment.isVertical()) {
+ origTermDelta = oppositeSegment.getOrigin().y - oppositeSegment.getTerminus().y;
+ origNewLocDelta = oppositeSegment.getOrigin().y - absoluteAnchorCoordinates.y;
+ } else {
+ origTermDelta = oppositeSegment.getOrigin().x - oppositeSegment.getTerminus().x;
+ origNewLocDelta = oppositeSegment.getOrigin().x - absoluteAnchorCoordinates.x;
+
+ }
+
+ return (origTermDelta > 0) != (origNewLocDelta > 0);
+
+ }
+
+ /**
+ * Adds two bendpoints on the segment we want to align with the anchor. That
+ * avoids to break the other segment when centering this one. (in case of
+ * edge with 3 bendpoints that means two perpendicular segments).
+ *
+ * @param line
+ * the point list to add new bendpoints.
+ * @param isFirst
+ * true if the first segment is concerned (from the source),
+ * false for the second one (the last from the target).
+ */
+ private static void addPointForLEdge(PointList line, boolean isFirst) {
+ LineSeg segment = getSegment(line, isFirst);
+ Point origin = segment.getOrigin();
+ Point terminus = segment.getTerminus();
+ Point newPoint = origin.getCopy();
+ if (segment.isHorizontal()) {
+ int newPointX;
+ if (origin.x > terminus.x) {
+ newPointX = origin.x - MINIMAL_SEGMENT_SIZE;
+ } else {
+ newPointX = origin.x + MINIMAL_SEGMENT_SIZE;
+ }
+ newPoint.setX(newPointX);
+
+ } else {
+
+ int newPointY;
+ if (origin.y > terminus.y) {
+ newPointY = origin.y - MINIMAL_SEGMENT_SIZE;
+ } else {
+ newPointY = origin.y + MINIMAL_SEGMENT_SIZE;
+ }
+ newPoint.setY(newPointY);
+ }
+ if (isFirst) {
+ line.insertPoint(newPoint, 1);
+ line.insertPoint(newPoint.getCopy(), 1);
+ } else {
+
+ line.insertPoint(newPoint, line.size() - 1);
+ line.insertPoint(newPoint.getCopy(), line.size() - 1);
+ }
+
+ }
+
+ /**
+ * Aligns the given segment toward anchor.
+ *
+ * @param line
+ * the edge point list.
+ * @param absoluteAnchorCoordinates
+ * the anchor coordinates.
+ * @param isFirst
+ * true to align the first segment (from the source), false to
+ * align the last one (from the target).
+ */
+ private static void alignSegmentTowardAnchor(PointList line, Point absoluteAnchorCoordinates, boolean isFirst) {
+ LineSeg segment = getSegment(line, isFirst);
+ Point origin = segment.getOrigin();
+ Point terminus = segment.getTerminus();
+ if (segment.isVertical()) {
+ origin.setX(absoluteAnchorCoordinates.x());
+ terminus.setX(absoluteAnchorCoordinates.x());
+ } else {
+ origin.setY(absoluteAnchorCoordinates.y());
+ terminus.setY(absoluteAnchorCoordinates.y());
+ }
+ if (isFirst) {
+ line.setPoint(origin, 0);
+ line.setPoint(terminus, 1);
+ } else {
+ line.setPoint(origin, line.size() - 1);
+ line.setPoint(terminus, line.size() - 2);
+ }
+ }
+
+ /**
+ * Gives the first or the last edge segment from the line.
+ *
+ * @param line
+ * the edge point list.
+ * @param isFirst
+ * true to get the first segment, false to get the last one.
+ * @return the segment. The origin point is always the one connected to the
+ * shape. For the first segment, the origin is the first point, for
+ * the last segment, the origin is the last point.
+ */
+ private static LineSeg getSegment(PointList line, boolean isFirst) {
+ if (isFirst) {
+ return new LineSeg(line.getFirstPoint(), line.getPoint(1));
+ } else {
+ return new LineSeg(line.getLastPoint(), line.getPoint(line.size() - 2));
+ }
+ }
+
+ /**
+ * From
+ * org.eclipse.gmf.runtime.draw2d.ui.internal.routers.RectilinearRouter.
+ * removeRedundantPoints(PointList) Iterates through points of a polyline
+ * and does the following: if 3 points lie on the same line the middle point
+ * is removed
+ *
+ * @param line
+ * polyline's points
+ */
+ private static boolean removeRedundantPoints(PointList line) {
+ int initialNumberOfPoints = line.size();
+ if (line.size() > 2) {
+ PointList newLine = new PointList(line.size());
+ newLine.addPoint(line.removePoint(0));
+ while (line.size() >= 2) {
+ Point p0 = newLine.getLastPoint();
+ Point p1 = line.getPoint(0);
+ Point p2 = line.getPoint(1);
+ if (p0.x == p1.x && p0.x == p2.x) {
+ // Have two vertical segments in a row
+ // get rid of the point between
+ line.removePoint(0);
+ } else if (p0.y == p1.y && p0.y == p2.y) {
+ // Have two horizontal segments in a row
+ // get rid of the point between
+ line.removePoint(0);
+ } else {
+ newLine.addPoint(line.removePoint(0));
+ }
+ }
+ while (line.size() > 0) {
+ newLine.addPoint(line.removePoint(0));
+ }
+ line.removeAllPoints();
+ line.addAll(newLine);
+ }
+ return line.size() != initialNumberOfPoints;
+ }
+}

Back to the top