blob: 4e37efe7ff03f51ba86530ffe8475822505f44b9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2017 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
*******************************************************************************/
package org.eclipse.sirius.diagram.ui.internal.edit.parts.locator;
import java.util.List;
import org.eclipse.draw2d.ConnectionLocator;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.PrecisionPoint;
import org.eclipse.draw2d.geometry.Straight;
import org.eclipse.draw2d.geometry.Transform;
import org.eclipse.draw2d.geometry.Vector;
import org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart;
import org.eclipse.gmf.runtime.diagram.ui.internal.util.LabelViewConstants;
import org.eclipse.gmf.runtime.draw2d.ui.geometry.LineSeg;
import org.eclipse.gmf.runtime.draw2d.ui.geometry.PointListUtilities;
import org.eclipse.gmf.runtime.notation.Bounds;
import org.eclipse.sirius.diagram.ui.business.internal.bracket.locators.BracketLabelLocator;
import org.eclipse.sirius.diagram.ui.internal.edit.parts.DEdgeBeginNameEditPart;
import org.eclipse.sirius.diagram.ui.internal.edit.parts.DEdgeEndNameEditPart;
import org.eclipse.sirius.diagram.ui.internal.edit.parts.DEdgeNameEditPart;
import org.eclipse.sirius.diagram.ui.part.SiriusVisualIDRegistry;
import org.eclipse.sirius.ext.base.Option;
import org.eclipse.sirius.ext.base.Options;
import com.google.common.base.Preconditions;
/**
* Utility class used to compute the position of a label according to its edge
* move (old points and new points list).
*
* @author <a href="mailto:laurent.fasani@obeo.fr">Laurent Fasani</a>
* @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a>
*/
public class EdgeLabelQuery {
private final static double DISTANCE_TOLERANCE = 0.001;
/** Status for {@link #getSameLineStatus(Vector, LineSeg)} method. */
private static final int NOT_ON_SAME_LINE = 0;
/** Status for {@link #getSameLineStatus(Vector, LineSeg)} method. */
private static final int ON_SAME_LINE_SAME_DIRECTION = 1;
/** Status for {@link #getSameLineStatus(Vector, LineSeg)} method. */
private static final int ON_SAME_LINE_OPPOSITE_DIRECTION = 2;
/** BendPoint list before the edge modification. */
private PointList oldBendPointList;
/** BendPoint list corresponding to the edge modification. */
private PointList newBendPointList;
/** The routing status of edge on which the label is. */
private boolean isEdgeWithObliqueRoutingStyle;
/** The old offset of the label */
private Point oldLabelOffset;
/**
* The keyPoint of the label (
* {@link org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart#getKeyPoint()}
* ).
*/
private Integer keyPoint;
private List<LineSeg> oldEdgeSegments;
private List<LineSeg> newEdgeSegments;
/**
* True if the parent's label is a bracketEdge (specific locator for center
* label and possibility to rotate the middle segment), false otherwise.
*/
private boolean isOnBracketEdge;
/** The size of the label. */
private Dimension labelSize;
/**
* Return the default snap back position according to the keyPoint of the
* label.
*
* @param keyPoint
* The keyPoint of the label (
* {@link org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart#getKeyPoint()}
* ).
* @return the default snap back position according to the keyPoint.
*/
public static Point getSnapBackPosition(Integer keyPoint) {
int percentage = getLocation(keyPoint);
Point snapBackPosition;
if (LabelViewConstants.SOURCE_LOCATION == percentage) {
snapBackPosition = LabelEditPart.getSnapBackPosition(SiriusVisualIDRegistry.getType(DEdgeBeginNameEditPart.VISUAL_ID));
} else if (LabelViewConstants.TARGET_LOCATION == percentage) {
snapBackPosition = LabelEditPart.getSnapBackPosition(SiriusVisualIDRegistry.getType(DEdgeEndNameEditPart.VISUAL_ID));
} else {
snapBackPosition = LabelEditPart.getSnapBackPosition(SiriusVisualIDRegistry.getType(DEdgeNameEditPart.VISUAL_ID));
}
return snapBackPosition;
}
/**
* Calculates the label offset from the reference point given the label
* center and a points list.<BR>
* This is another implementation of
* {@link org.eclipse.gmf.runtime.diagram.ui.internal.figures.LabelHelper#offsetFromRelativeCoordinate(org.eclipse.draw2d.IFigure, org.eclipse.draw2d.geometry.Rectangle, Point)}
*
* @param labelCenter
* the label center for the <code>IFigure</code> to calculate the
* offset for
* @param points
* the <code>PointList</code> of the edge that contains the
* label. The label offset is relative to this
* <code>PointList</code>.
* @param ref
* the <code>Point</code> that is the reference point that the
* offset is based on.
* @return a <code>Point</code> which represents a value offset from the
* <code>ref</code> point oriented based on the nearest line
* segment.
*/
public static Point offsetFromRelativeCoordinate(Point labelCenter, PointList points, Point ref) {
Vector fromAnchorToLabelCenterPointVector = new Vector(labelCenter.x - ref.x, labelCenter.y - ref.y);
@SuppressWarnings("rawtypes")
List lineSegments = PointListUtilities.getLineSegments(points);
LineSeg segmentContainingLabelAnchor = PointListUtilities.getNearestSegment(lineSegments, ref.x, ref.y);
Vector rotatedVector = getRotatedVector(fromAnchorToLabelCenterPointVector, segmentContainingLabelAnchor, false);
return new PrecisionPoint(rotatedVector.x, rotatedVector.y);
}
/**
* Calculates the relative coordinate that is equivalent to the offset from
* the reference point, that can be used to set the label center location.
* <BR>
*
* This is another implementation of
* {@link org.eclipse.gmf.runtime.diagram.ui.internal.figures.LabelHelper#relativeCoordinateFromOffset(org.eclipse.draw2d.IFigure, Point, Point)}
* . See bugzilla 476305 for more detail.
*
* @param points
* the <code>PointList</code> of the edge that contains the
* label. The label offset is relative to this
* <code>PointList</code>.
* @param ref
* a <code>Point</code> located on the parent which the offset
* value is relative to.
* @param offset
* a <code>Point</code> which represents a value offset from the
* <code>ref</code> point oriented based on the nearest line
* segment.
* @return a <code>Point</code> that is the relative coordinate of the label
* that can be used to set it's center location.
*/
public static Point relativeCenterCoordinateFromOffset(PointList points, Point ref, Point offset) {
Vector fromAnchorToLabelCenterPointVector = new Vector(offset.x, offset.y);
@SuppressWarnings("rawtypes")
List lineSegments = PointListUtilities.getLineSegments(points);
LineSeg segmentContainingLabelAnchor = PointListUtilities.getNearestSegment(lineSegments, ref.x, ref.y);
Vector rotatedVector = getRotatedVector(fromAnchorToLabelCenterPointVector, segmentContainingLabelAnchor, true);
return new PrecisionPoint(ref.x + rotatedVector.x, ref.y + rotatedVector.y);
}
/**
* Default constructor.
*
* @param oldBendPointList
* Bendpoint list before the edge modification
* @param newBendPointList
* Bendpoint list after the edge modification
* @param isEdgeWithObliqueRoutingStyle
* status of the edge from which to get the previous position of
* the bendpoints and from which to get the three labels
* @param oldLabelOffset
* The old offset.
* @param labelSize
* The size of the label
* @param keyPoint
* The keyPoint of the label (
* {@link org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart#getKeyPoint()}
* )
* @param isOnBracketEdge
* True if the parent's label is a bracketEdge (specific locator
* for center label and possibility to rotate the middle
* segment), false otherwise.
*/
// @SuppressWarnings("unchecked")
public EdgeLabelQuery(PointList oldBendPointList, PointList newBendPointList, boolean isEdgeWithObliqueRoutingStyle, Point oldLabelOffset, Dimension labelSize, Integer keyPoint,
boolean isOnBracketEdge) {
this.isEdgeWithObliqueRoutingStyle = isEdgeWithObliqueRoutingStyle;
this.oldBendPointList = oldBendPointList;
Preconditions.checkState(newBendPointList.size() > 0);
this.newBendPointList = newBendPointList;
this.oldLabelOffset = oldLabelOffset;
this.labelSize = labelSize;
this.keyPoint = keyPoint;
this.isOnBracketEdge = isOnBracketEdge;
// compute lineSegments from bendPoints
oldEdgeSegments = PointListUtilities.getLineSegments(oldBendPointList);
newEdgeSegments = PointListUtilities.getLineSegments(newBendPointList);
}
/**
* Calculate the new GMF label offset: the label offset defines the position
* of the label compared to labelAnchor point. <br>
* The new Label offset is computed taking into account:<br>
* <ul>
* <li>the label anchor point move (start, center or end)</li>
* <li>the orientation of the segment owning the label anchor. Indeed, the
* label offset is displayed by draw2D relatively to the direction of the
* edge segment including the anchor of the label</li>
* <li>the expected move of the label according to the functional
* specification</li>
* </ul>
* .
*
* @return the new offset of the label
*/
public Point calculateGMFLabelOffset() {
if (areBendpointsIdentical() && areSegmentsValid()) {
return oldLabelOffset;
} else {
int anchorPointRatio = getLocation(keyPoint);
Point oldAnchorPoint = getAnchorPoint(oldBendPointList, anchorPointRatio);
Point oldLabelCenter = relativeCenterCoordinateFromOffset(oldBendPointList, oldAnchorPoint, oldLabelOffset);
Point newAnchorPoint = getAnchorPoint(newBendPointList, anchorPointRatio);
Point newLabelCenter = calculateNewCenterLocation(oldLabelCenter, getStandardLabelCenterLocation(newBendPointList, keyPoint));
return offsetFromRelativeCoordinate(newLabelCenter, newBendPointList, newAnchorPoint);
}
}
private Point getAnchorPoint(PointList pointList, int anchorPointRatio) {
if (isOnBracketEdge && LabelViewConstants.MIDDLE_LOCATION == anchorPointRatio) {
return BracketLabelLocator.getReferencePoint(pointList);
} else {
return PointListUtilities.calculatePointRelativeToLine(pointList, 0, anchorPointRatio, true);
}
}
/**
* Check if all segments of new and old points are valid (no segment with
* same origin and terminus).
*
* @return true if segments are valid, false otherwise.
*/
private boolean areSegmentsValid() {
boolean areSegmentsValid = true;
for (LineSeg lineSeg : newEdgeSegments) {
if (lineSeg.getOrigin().equals(lineSeg.getTerminus())) {
areSegmentsValid = false;
break;
}
}
if (areSegmentsValid) {
for (LineSeg lineSeg : oldEdgeSegments) {
if (lineSeg.getOrigin().equals(lineSeg.getTerminus())) {
areSegmentsValid = false;
break;
}
}
}
return areSegmentsValid;
}
/**
* Check if the old points and the new one are the same.
*
* @return true if the old points are the same as the new, false otherwise.
*/
private boolean areBendpointsIdentical() {
boolean areBendpointsIdentical = true;
if (newBendPointList.size() == oldBendPointList.size()) {
for (int i = 0; i < newBendPointList.size(); i++) {
Point newPoint = newBendPointList.getPoint(i);
Point oldPoint = oldBendPointList.getPoint(i);
if (!newPoint.equals(oldPoint)) {
areBendpointsIdentical = false;
break;
}
}
} else {
areBendpointsIdentical = false;
}
return areBendpointsIdentical;
}
/**
* Calculate the new center location of the label according to functional
* specification.
*
* @param oldCenterLabel
* The old center location of the label.
* @param newDefaultLocation
* The standard center location according to the label keyPoint (
* {@link org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart#getKeyPoint()}
* and the default snap back position (
* {@link LabelEditPart#getSnapBackPosition(String)}.
* @return
*/
private Point calculateNewCenterLocation(Point oldCenterLabel, Point newDefaultLocation) {
Vector fromOldToNewCenterVector = null;
// Step 1 : Calculate old reference point (the nearest point on the edge
// from the center of the label).
LineSeg oldNearestSeg = PointListUtilities.getNearestSegment(oldEdgeSegments, oldCenterLabel.x, oldCenterLabel.y);
Point oldNearestPoint = oldNearestSeg.perpIntersect(oldCenterLabel.x, oldCenterLabel.y);
// Step 2 : Is there a new segment and an old segment on the same line?
// Case of segment increased or decreased (and eventually inverted)
Option<Vector> fromOldToNewRefPoint = getVectorFromOldToNewForSegmentsOnSameLine(oldNearestSeg, oldNearestPoint, oldCenterLabel);
if (fromOldToNewRefPoint.some()) {
// In this case the vector for the reference point is the same than
// for the label center.
fromOldToNewCenterVector = fromOldToNewRefPoint.get();
} else { // No identical segment line has been found
// RECTILINEAR and TREE routing
if (!isEdgeWithObliqueRoutingStyle) {
// Get projection of oldNearestPoint on newSegments along
// oldRefVector
LineSeg oldRefVectorIntoSegment = null;
if (oldCenterLabel.equals(oldNearestPoint)) {
// Get a segment perpendicular to oldRefSegment going
// through oldNearestPoint
oldRefVectorIntoSegment = new LineSeg(oldNearestPoint, new PrecisionPoint(oldNearestPoint.x + (oldNearestSeg.getOrigin().y - oldNearestSeg.getTerminus().y),
oldNearestPoint.y - (oldNearestSeg.getOrigin().x - oldNearestSeg.getTerminus().x)));
} else {
oldRefVectorIntoSegment = new LineSeg(oldCenterLabel, oldNearestPoint);
}
// Is there a new segment at the same index as old segment and
// with same axis? Case of rectilinear segment move.
fromOldToNewCenterVector = getVectorForSegmentMoveCase(oldNearestSeg, oldNearestPoint, oldCenterLabel);
if (fromOldToNewCenterVector == null && isOnBracketEdge) {
// Is there a new segment at the same index as old segment
// and with opposite axis? Case of change orientation of the
// bracket edge
fromOldToNewCenterVector = getVectorForBracketEdgeOrientationChangeCase(oldNearestSeg, oldNearestPoint, oldCenterLabel);
}
if (fromOldToNewCenterVector == null) {
for (LineSeg lineSeg : newEdgeSegments) {
PointList linesIntersections = oldRefVectorIntoSegment.getLinesIntersections(lineSeg);
// intersection should be, at more, one point
if (linesIntersections.size() == 1 && lineSeg.distanceToPoint(linesIntersections.getPoint(0).x, linesIntersections.getPoint(0).y) <= Math.sqrt(2)) {
Vector tempLabelMove = new Vector(linesIntersections.getPoint(0).x - oldNearestPoint.x, linesIntersections.getPoint(0).y - oldNearestPoint.y);
if (fromOldToNewCenterVector == null || tempLabelMove.getLength() < fromOldToNewCenterVector.getLength()) {
fromOldToNewCenterVector = tempLabelMove;
}
}
}
// Compare the minimalLabelMove with the default location.
// If the default location is nearer reset the labelMove.
Vector fromOldNearestPointToStandardLocation = new Vector(newDefaultLocation.x - oldNearestPoint.x, newDefaultLocation.y - oldNearestPoint.y);
if (fromOldToNewCenterVector == null || fromOldNearestPointToStandardLocation.getLength() < fromOldToNewCenterVector.getLength()) {
fromOldToNewCenterVector = null;
}
}
} else if (newEdgeSegments.size() == oldEdgeSegments.size()) {
// The newNearestSegment as the same index in
// newEdgeSegments than oldNearestSegment in oldEdgeSegments
LineSeg newRefSeg = newEdgeSegments.get(oldEdgeSegments.indexOf(oldNearestSeg));
// Keep ratio on segment for newRefPoint
double oldRatio = oldNearestSeg.projection(oldCenterLabel.x, oldCenterLabel.y);
Point newRefPoint = new PrecisionPoint(newRefSeg.getOrigin().x + oldRatio * (newRefSeg.getTerminus().x - newRefSeg.getOrigin().x),
newRefSeg.getOrigin().y + oldRatio * (newRefSeg.getTerminus().y - newRefSeg.getOrigin().y));
fromOldToNewCenterVector = new Vector(newRefPoint.x - oldNearestPoint.x, newRefPoint.y - oldNearestPoint.y);
}
}
if (fromOldToNewCenterVector == null) {
return newDefaultLocation;
} else {
return oldCenterLabel.getTranslated(fromOldToNewCenterVector.x, fromOldToNewCenterVector.y);
}
}
/**
* Check if we are in case of a rectilinear segment move: there is a new
* segment at the same index as old nearest segment and with the same axis.
* Return the corresponding vector from old to new center in this case, null
* otherwise.
*
* @param oldNearestSeg
* The segment that is the nearest from the center of the label
* in the old points list.
* @return the corresponding vector from old to new center in case of
* rectilinear segment move, null otherwise.
*/
private Vector getVectorForSegmentMoveCase(LineSeg oldNearestSeg, Point oldNearestPoint, Point oldCenterLabel) {
Vector fromOldToNewCenterVector = null;
if (newEdgeSegments.size() == oldEdgeSegments.size()) {
int index = oldEdgeSegments.indexOf(oldNearestSeg);
LineSeg newNearestSegment = newEdgeSegments.get(index);
if (oldNearestSeg.isHorizontal() == newNearestSegment.isHorizontal()) {
Vector oldVector = new Vector(oldNearestSeg.getTerminus().x - oldNearestSeg.getOrigin().x, oldNearestSeg.getTerminus().y - oldNearestSeg.getOrigin().y);
Vector newVector = new Vector(newNearestSegment.getTerminus().x - newNearestSegment.getOrigin().x, newNearestSegment.getTerminus().y - newNearestSegment.getOrigin().y);
boolean oppositeDirection = false;
if (oldVector.getLength() != 0 && newVector.getLength() != 0) {
oppositeDirection = oldVector.getAngle(newVector) == 180;
}
fromOldToNewCenterVector = applyOldRatioOnNewSegment(oldNearestSeg, oldNearestPoint, oldCenterLabel, newNearestSegment, oppositeDirection, false);
}
}
return fromOldToNewCenterVector;
}
/**
* Check if we are in case of a orientation segment change: there is a new
* segment at the same index as old nearest segment and with apposite axis.
* Return the corresponding vector from old to new center in this case, null
* otherwise.
*
* @param oldNearestSeg
* The segment that is the nearest from the center of the label
* in the old points list.
* @return the corresponding vector from old to new center in case of
* orientation segment change, null otherwise.
*/
private Vector getVectorForBracketEdgeOrientationChangeCase(LineSeg oldNearestSeg, Point oldNearestPoint, Point oldCenterLabel) {
Vector fromOldToNewCenterVector = null;
if (newEdgeSegments.size() == oldEdgeSegments.size()) {
int index = oldEdgeSegments.indexOf(oldNearestSeg);
LineSeg newNearestSegment = newEdgeSegments.get(index);
if (oldNearestSeg.isHorizontal() != newNearestSegment.isHorizontal()) {
Vector oldVector = new Vector(oldNearestSeg.getTerminus().x - oldNearestSeg.getOrigin().x, oldNearestSeg.getTerminus().y - oldNearestSeg.getOrigin().y);
Vector newVector = new Vector(newNearestSegment.getTerminus().x - newNearestSegment.getOrigin().x, newNearestSegment.getTerminus().y - newNearestSegment.getOrigin().y);
double angleInDegree = Math.toDegrees(angleBetween2Lines(oldVector, newVector));
fromOldToNewCenterVector = applyOldRatioOnNewOrthogonalSegment(oldNearestSeg, oldNearestPoint, oldCenterLabel, newNearestSegment, angleInDegree == 90 || angleInDegree == -270);
}
}
return fromOldToNewCenterVector;
}
private Option<Vector> getVectorFromOldToNewForSegmentsOnSameLine(LineSeg oldRefSeg, Point oldRefPoint, Point oldCenterLabel) {
Option<Vector> result = Options.newNone();
LineSeg newSegmentOnSameLineWithSameDirection = null;
LineSeg newSegmentOnSameLineWithOppositeDirection = null;
// Firstly, for points lists with same nb of segments, search if the
// vector, at
// the same index, is on the same line
int sameLineStatus = NOT_ON_SAME_LINE;
if (newEdgeSegments.size() == oldEdgeSegments.size()) {
LineSeg newSegAtSameIndex = newEdgeSegments.get(oldEdgeSegments.indexOf(oldRefSeg));
sameLineStatus = getSameLineStatus(oldRefSeg, newSegAtSameIndex);
if (ON_SAME_LINE_SAME_DIRECTION == sameLineStatus) {
newSegmentOnSameLineWithSameDirection = newSegAtSameIndex;
} else if (ON_SAME_LINE_OPPOSITE_DIRECTION == sameLineStatus) {
newSegmentOnSameLineWithOppositeDirection = newSegAtSameIndex;
}
}
if (NOT_ON_SAME_LINE == sameLineStatus) {
// If this is not the case search on all new segments.
for (LineSeg newSeg : newEdgeSegments) {
sameLineStatus = getSameLineStatus(oldRefSeg, newSeg);
if (ON_SAME_LINE_SAME_DIRECTION == sameLineStatus) {
newSegmentOnSameLineWithSameDirection = newSeg;
break;
} else if (ON_SAME_LINE_OPPOSITE_DIRECTION == sameLineStatus) {
newSegmentOnSameLineWithOppositeDirection = newSeg;
// Continue to search a potential segment in the same
// direction.
}
}
}
LineSeg newRefSeg = newSegmentOnSameLineWithSameDirection;
if (newRefSeg == null) {
newRefSeg = newSegmentOnSameLineWithOppositeDirection;
}
if (newRefSeg != null) {
result = Options.newSome(applyOldRatioOnNewSegment(oldRefSeg, oldRefPoint, oldCenterLabel, newRefSeg, newSegmentOnSameLineWithOppositeDirection != null, true));
}
return result;
}
/**
* Check if the <code>segment</code> is on the same line as the
* <code>referenceSegment</code> and if it is in the same direction or not.
*
* @param referenceSegment
* The reference segment.
* @param segment
* The segment to test
* @return one of these statuses {@link #NOT_ON_SAME_LINE},
* {@link #ON_SAME_LINE_SAME_DIRECTION} or
* {@link #ON_SAME_LINE_OPPOSITE_DIRECTION}.
*/
private int getSameLineStatus(LineSeg referenceSegment, LineSeg segment) {
int result = NOT_ON_SAME_LINE;
if (segment.length() != 0) {
Vector referenceVector = new Vector(referenceSegment.getTerminus().x - referenceSegment.getOrigin().x, referenceSegment.getTerminus().y - referenceSegment.getOrigin().y);
Vector vector = new Vector(segment.getTerminus().x - segment.getOrigin().x, segment.getTerminus().y - segment.getOrigin().y);
if (referenceVector.getLength() == 0 || vector.getLength() == 0) {
result = ON_SAME_LINE_SAME_DIRECTION;
} else {
double angle = referenceVector.getAngle(vector);
if (angle == 0 || angle == 180) {
Straight straight = new Straight(new PrecisionPoint(segment.getOrigin()), new PrecisionPoint(segment.getTerminus()));
double distToInfiniteLine = straight.getDistance(new Vector(referenceSegment.getOrigin().x, referenceSegment.getOrigin().y));
if (distToInfiniteLine < DISTANCE_TOLERANCE) {
if (angle == 180) {
result = ON_SAME_LINE_OPPOSITE_DIRECTION;
} else {
result = ON_SAME_LINE_SAME_DIRECTION;
}
}
}
}
}
return result;
}
private Vector applyOldRatioOnNewSegment(LineSeg oldRefSeg, Point oldRefPoint, Point oldCenterLabel, LineSeg newRefSeg, boolean oppositeDirection, boolean sameLine) {
Vector result;
double newRatio = newRefSeg.projection(oldCenterLabel.x, oldCenterLabel.y);
if (sameLine && newRatio >= 0 && newRatio <= 1) {
// If the orthogonal projection is inside segment (between 0 and
// 1), the reference point does not move.
result = new Vector(0, 0);
} else {
Point newRefPoint;
double oldRatio = oldRefSeg.projection(oldCenterLabel.x, oldCenterLabel.y);
if (!oppositeDirection) {
newRefPoint = new PrecisionPoint(newRefSeg.getOrigin().x + oldRatio * (newRefSeg.getTerminus().x - newRefSeg.getOrigin().x),
newRefSeg.getOrigin().y + oldRatio * (newRefSeg.getTerminus().y - newRefSeg.getOrigin().y));
} else {
newRefPoint = new PrecisionPoint(newRefSeg.getOrigin().x - oldRatio * (newRefSeg.getOrigin().x - newRefSeg.getTerminus().x),
newRefSeg.getOrigin().y - oldRatio * (newRefSeg.getOrigin().y - newRefSeg.getTerminus().y));
}
if (!sameLine && newRatio >= 0 && newRatio <= 1) {
// If the orthogonal projection is inside segment (between 0 and
// 1), we keep the oldRefPoint one axis
if (newRefSeg.isHorizontal()) {
newRefPoint.setX(oldRefPoint.x);
} else {
newRefPoint.setY(oldRefPoint.y);
}
}
Vector vectorFromOldToNewRefPoint = new Vector(newRefPoint.x - oldRefPoint.x, newRefPoint.y - oldRefPoint.y);
if (oldRatio >= 0 && oldRatio <= 1) {
// Keep ratio on segment for newRefPoint (if it was
// previously inside segment)
result = vectorFromOldToNewRefPoint;
} else {
// If the label is previously outside of the segment, we
// keep the shortest point (new or old one).
Point potentialNewCenter = oldCenterLabel.getTranslated(vectorFromOldToNewRefPoint.x, vectorFromOldToNewRefPoint.y);
if ((newRatio > 1 && newRatio < newRefSeg.projection(potentialNewCenter.x, potentialNewCenter.y))
|| (newRatio < 0 && newRatio > newRefSeg.projection(potentialNewCenter.x, potentialNewCenter.y))) {
result = new Vector(0, 0);
} else {
result = vectorFromOldToNewRefPoint;
}
}
}
return result;
}
private Vector applyOldRatioOnNewOrthogonalSegment(LineSeg oldRefSeg, Point oldRefPoint, Point oldCenterLabel, LineSeg newRefSeg, boolean is90Angle) {
double oldRatio = oldRefSeg.projection(oldCenterLabel.x, oldCenterLabel.y);
Transform rotateTransform = new Transform();
// Get the new reference point and the rotation to apply
Point newRefPoint;
if (is90Angle) {
// As GMF coordinates system is reversed (y positive orientation is
// from top to bottom), we reverse the rotation to apply
rotateTransform.setRotation(Math.toRadians(-90));
if (0 <= oldRatio && oldRatio <= 1) {
// Apply same ratio on the new segment to compute the new
// reference point
newRefPoint = new PrecisionPoint(newRefSeg.getOrigin().x - oldRatio * (newRefSeg.getOrigin().x - newRefSeg.getTerminus().x),
newRefSeg.getOrigin().y - oldRatio * (newRefSeg.getOrigin().y - newRefSeg.getTerminus().y));
} else if (oldRatio > 1) {
// Just apply the vector from old terminus to old reference
// point to the new terminus
Vector vectorFromOldTerminusToOldRefPoint = new Vector(oldRefPoint.x - oldRefSeg.getTerminus().x, oldRefPoint.y - oldRefSeg.getTerminus().y);
Point vectorFromNewTerminusToNewRefPoint = rotateTransform.getTransformed(vectorFromOldTerminusToOldRefPoint.toPoint());
newRefPoint = newRefSeg.getTerminus().getTranslated(vectorFromNewTerminusToNewRefPoint);
} else {
Vector vectorFromOldOriginToOldRefPoint = new Vector(oldRefPoint.x - oldRefSeg.getOrigin().x, oldRefPoint.y - oldRefSeg.getOrigin().y);
Point vectorFromNewOriginToNewRefPoint = rotateTransform.getTransformed(vectorFromOldOriginToOldRefPoint.toPoint());
newRefPoint = newRefSeg.getOrigin().getTranslated(vectorFromNewOriginToNewRefPoint);
}
} else {
// As GMF coordinates system is reversed (y positive orientation is
// from top to bottom), we reverse the rotation to apply
rotateTransform.setRotation(Math.toRadians(90));
if (0 <= oldRatio && oldRatio <= 1) {
// Apply same ratio on the new segment to compute the new
// reference point (but inverse the origin and the terminus)
newRefPoint = new PrecisionPoint(newRefSeg.getOrigin().x + oldRatio * (newRefSeg.getTerminus().x - newRefSeg.getOrigin().x),
newRefSeg.getOrigin().y + oldRatio * (newRefSeg.getTerminus().y - newRefSeg.getOrigin().y));
} else if (oldRatio > 1) {
// Just apply the vector from old terminus to old reference
// point to the new terminus
Vector vectorFromOldTerminusToOldRefPoint = new Vector(oldRefPoint.x - oldRefSeg.getTerminus().x, oldRefPoint.y - oldRefSeg.getTerminus().y);
Point vectorFromNewTerminusToNewRefPoint = rotateTransform.getTransformed(vectorFromOldTerminusToOldRefPoint.toPoint());
newRefPoint = newRefSeg.getTerminus().getTranslated(vectorFromNewTerminusToNewRefPoint);
} else {
Vector vectorFromOldOriginToOldRefPoint = new Vector(oldRefPoint.x - oldRefSeg.getOrigin().x, oldRefPoint.y - oldRefSeg.getOrigin().y);
Point vectorFromNewOriginToNewRefPoint = rotateTransform.getTransformed(vectorFromOldOriginToOldRefPoint.toPoint());
newRefPoint = newRefSeg.getOrigin().getTranslated(vectorFromNewOriginToNewRefPoint);
}
}
Vector vectorFromOldRefPointToOldCenterLabel = new Vector(oldCenterLabel.x - oldRefPoint.x, oldCenterLabel.y - oldRefPoint.y);
Point fromNewRefPointToNewCenterLabel = rotateTransform.getTransformed(vectorFromOldRefPointToOldCenterLabel.toPoint());
// Adjust the vector to apply according to orientation change (width
// becomes height and conversely)
if (newRefSeg.isHorizontal()) {
if (0 <= oldRatio && oldRatio <= 1) {
int invertedWidthHeight = labelSize.width - labelSize.height;
if (newRefSeg.getOrigin().x > newRefSeg.getTerminus().x) {
invertedWidthHeight = -invertedWidthHeight;
}
fromNewRefPointToNewCenterLabel.translate(0, invertedWidthHeight / 2);
} else if (oldRatio > 1) {
int invertedWidthHeight = labelSize.width - labelSize.height;
if (newRefSeg.getOrigin().x > newRefSeg.getTerminus().x) {
invertedWidthHeight = -invertedWidthHeight;
}
fromNewRefPointToNewCenterLabel.translate(invertedWidthHeight / 2, 0);
} else {
fromNewRefPointToNewCenterLabel.translate(0, 0);
int invertedWidthHeight = labelSize.width - labelSize.height;
if (newRefSeg.getOrigin().x < newRefSeg.getTerminus().x) {
invertedWidthHeight = -invertedWidthHeight;
}
fromNewRefPointToNewCenterLabel.translate(invertedWidthHeight / 2, 0);
}
} else {
if (0 <= oldRatio && oldRatio <= 1) {
int invertedWidthHeight = labelSize.width - labelSize.height;
if (newRefSeg.getOrigin().y > newRefSeg.getTerminus().y) {
invertedWidthHeight = -invertedWidthHeight;
}
fromNewRefPointToNewCenterLabel.translate(invertedWidthHeight / 2, 0);
} else if (oldRatio > 1) {
int invertedWidthHeight = labelSize.height - labelSize.width;
if (newRefSeg.getOrigin().y > newRefSeg.getTerminus().y) {
invertedWidthHeight = -invertedWidthHeight;
}
fromNewRefPointToNewCenterLabel.translate(0, invertedWidthHeight / 2);
} else {
int invertedWidthHeight = labelSize.height - labelSize.width;
if (newRefSeg.getOrigin().y < newRefSeg.getTerminus().y) {
invertedWidthHeight = -invertedWidthHeight;
}
fromNewRefPointToNewCenterLabel.translate(0, invertedWidthHeight / 2);
}
}
// Apply the rotated vector on new reference point to have the new
// center
Point newCenterLabel = newRefPoint.getTranslated(fromNewRefPointToNewCenterLabel);
// Compute the vector from old center to new center
return new Vector(newCenterLabel.x - oldCenterLabel.x, newCenterLabel.y - oldCenterLabel.y);
}
/**
* Get the rotated vector according to the segment orientation.
*
* @param vector
* vector to be rotated
* @param segment
* reference segment
* @param inverseRotation
* if true, inverse rotation
* @return the rotated Vector
*/
private static Vector getRotatedVector(Vector vector, LineSeg segment, boolean inverseRotation) {
Vector result = new Vector(vector.x, vector.y);
if (vector.x != 0 || vector.y != 0) {
double angle = angleBetween2Lines(new LineSeg(new Point(0, 0), new Point(1, 0)), segment) * (inverseRotation ? -1 : 1);
Transform rotateTransform = new Transform();
rotateTransform.setRotation(angle);
Point rotatedPoint = rotateTransform.getTransformed(vector.toPoint());
result.x = rotatedPoint.x;
result.y = rotatedPoint.y;
}
return result;
}
/**
* Get the signed angle between two segments.
*
* @param line1
* @param line2
* @return the signed angle in radian.
*/
private static double angleBetween2Lines(LineSeg line1, LineSeg line2) {
if (line1 == null || line2 == null) {
return 0;
}
double angle1 = Math.atan2(line1.getOrigin().y - line1.getTerminus().y, line1.getOrigin().x - line1.getTerminus().x);
double angle2 = Math.atan2(line2.getOrigin().y - line2.getTerminus().y, line2.getOrigin().x - line2.getTerminus().x);
return angle1 - angle2;
}
/**
* Get the signed angle between two vectors.
*
* @param vector1
* The first vector
* @param vector2
* The second vector
* @return the signed angle in radian.
*/
private static double angleBetween2Lines(Vector vector1, Vector vector2) {
if (vector1 == null || vector2 == null) {
return 0;
}
double angle1 = Math.atan2(vector1.y, vector1.x);
double angle2 = Math.atan2(vector2.y, vector2.x);
return angle1 - angle2;
}
/**
* Get the standard center location according to the label keyPoint (
* {@link org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart#getKeyPoint()}
* ) and the default snap back position (
* {@link LabelEditPart#getSnapBackPosition(String)}
*
* @param pointsList
* The points of the edge of the label.
* @param keyPoint
* The keyPoint of the label (
* {@link org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart#getKeyPoint()}
* ).
* @return The center of the label {@link Bounds} if this label is located
* by default.
*/
private Point getStandardLabelCenterLocation(PointList pointsList, Integer keyPoint) {
int percentage = getLocation(keyPoint);
Point newAnchorPoint = getAnchorPoint(pointsList, percentage);
Point snapBackPosition = getSnapBackPosition(keyPoint);
Point standardLabelCenter = newAnchorPoint.getTranslated(snapBackPosition);
return standardLabelCenter;
}
/**
* Get the location among {@link LabelViewConstants} constants where to
* relocate the label figure.
*
* @return the location among {@link LabelViewConstants} constants
*/
private static int getLocation(Integer keyPoint) {
int location = LabelViewConstants.MIDDLE_LOCATION;
switch (keyPoint) {
case ConnectionLocator.SOURCE:
location = LabelViewConstants.TARGET_LOCATION;
break;
case ConnectionLocator.TARGET:
location = LabelViewConstants.SOURCE_LOCATION;
break;
case ConnectionLocator.MIDDLE:
location = LabelViewConstants.MIDDLE_LOCATION;
break;
default:
location = LabelViewConstants.MIDDLE_LOCATION;
break;
}
return location;
}
}