| /******************************************************************************* |
| * 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; |
| } |
| } |