| /***************************************************************************** |
| * Copyright (c) 2014-15 CEA LIST, Montages AG 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: |
| * Michael Golubev (Montages) - Initial API and implementation |
| * |
| *****************************************************************************/ |
| package org.eclipse.gmf.tooling.runtime.linklf; |
| |
| import java.util.Collection; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| import org.eclipse.draw2d.PositionConstants; |
| import org.eclipse.draw2d.geometry.Point; |
| import org.eclipse.draw2d.geometry.PrecisionPoint; |
| import org.eclipse.draw2d.geometry.PrecisionRectangle; |
| import org.eclipse.draw2d.geometry.Rectangle; |
| import org.eclipse.gef.EditPartViewer; |
| import org.eclipse.gmf.runtime.draw2d.ui.figures.FigureUtilities; |
| import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure; |
| import org.eclipse.gmf.runtime.gef.ui.figures.SlidableAnchor; |
| |
| /** |
| * Extends {@link SlidableAnchor} with ability to snap to the intersection |
| * points between the host anchorable bounds and the active diagram grid. |
| * |
| * @since 3.3 |
| */ |
| public class SlidableSnapToGridAnchor extends SlidableAnchor { |
| |
| private EditPartViewer myGridProvider; |
| |
| public SlidableSnapToGridAnchor(NodeFigure f, PrecisionPoint p) { |
| super(f, p); |
| } |
| |
| public void setEditPartViewer(EditPartViewer viewer) { |
| myGridProvider = viewer; |
| } |
| |
| @Override |
| public Point getOrthogonalLocation(Point orthoReference) { |
| Point result = getOrthogonalLocationNoSnap(orthoReference); |
| if (result != null) { |
| Rectangle gridSpecAbs = getAbsoluteGridSpec(); |
| LinkedList<Point> onVerticalGrid = new LinkedList<Point>(); |
| LinkedList<Point> onHorizontalGrid = new LinkedList<Point>(); |
| computeAnchorablePointsOnGrid(gridSpecAbs, onVerticalGrid, |
| onHorizontalGrid); |
| |
| if (!onHorizontalGrid.isEmpty() || !onVerticalGrid.isEmpty()) { |
| return pickClosestPointToSet(result, onHorizontalGrid, |
| onVerticalGrid); |
| } |
| } |
| return result; |
| } |
| |
| private boolean myGetLocationReentryLock = false; |
| |
| @Override |
| public Point getLocation(Point reference) { |
| Rectangle gridSpecAbs = getAbsoluteGridSpec(); |
| if (gridSpecAbs != null && !myGetLocationReentryLock) { |
| myGetLocationReentryLock = true; |
| try { |
| List<Point> onVerticalGrid = new LinkedList<Point>(); |
| List<Point> onHorizontalGrid = new LinkedList<Point>(); |
| |
| computeAnchorablePointsOnGrid(gridSpecAbs, onVerticalGrid, |
| onHorizontalGrid); |
| |
| if (!onVerticalGrid.isEmpty() || !onVerticalGrid.isEmpty()) { |
| return pickClosestPointToSet(getReferencePoint(), |
| onHorizontalGrid, onVerticalGrid); |
| } |
| } finally { |
| myGetLocationReentryLock = false; |
| } |
| } |
| |
| Point result = super.getLocation(reference); |
| return result; |
| } |
| |
| /** |
| * @see bug #451604, we don't want anchor to move even if the resulting line |
| * is close to straight |
| * <p/> |
| * So we will block the normalization for instances of this class. |
| */ |
| @Override |
| protected Point normalizeToStraightlineTolerance(Point foreignReference, |
| Point ownReference, int tolerance) { |
| final int NO_TOLERANCE = 0; |
| return super.normalizeToStraightlineTolerance(foreignReference, |
| ownReference, NO_TOLERANCE); |
| } |
| |
| @Override |
| public String toString() { |
| return "SSTGA:" + "terminal: " + getTerminal() + ", terminalLoc: " |
| + getReferencePoint() + ", box: " + getBox(); |
| } |
| |
| /** |
| * Returns the position of the closest edge of the rectangle closest to the |
| * point |
| * |
| * @param p |
| * the point |
| * @param r |
| * the rectangle |
| * @return position of the closest edge |
| * @deprecated copy-pasted from super class, [GMFRT] make protected |
| */ |
| @Deprecated |
| public static int getClosestSide2(Point p, Rectangle r) { |
| double diff = Math.abs(r.preciseX() + r.preciseWidth() - p.preciseX()); |
| int side = PositionConstants.RIGHT; |
| double currentDiff = Math.abs(r.preciseX() - p.preciseX()); |
| if (currentDiff < diff) { |
| diff = currentDiff; |
| side = PositionConstants.LEFT; |
| } |
| currentDiff = Math.abs(r.preciseY() + r.preciseHeight() - p.preciseY()); |
| if (currentDiff < diff) { |
| diff = currentDiff; |
| side = PositionConstants.BOTTOM; |
| } |
| currentDiff = Math.abs(r.preciseY() - p.preciseY()); |
| if (currentDiff < diff) { |
| diff = currentDiff; |
| side = PositionConstants.TOP; |
| } |
| return side; |
| } |
| |
| /** |
| * If grid provider had been set up and has grid enabled then returns active |
| * grid specification in absolute coordinates. Otherwise returns null. |
| * |
| * @return <code>null</code> if no active grid or grid provider had not been |
| * set up. |
| */ |
| protected Rectangle getAbsoluteGridSpec() { |
| return myGridProvider == null ? null : DiagramGridSpec |
| .getAbsoluteGridSpec(myGridProvider); |
| } |
| |
| protected static Point pickClosestPointToSet(Point source, |
| Collection<? extends Point> set1, Collection<? extends Point> set2) { |
| double bestDistSquared = Double.MAX_VALUE; |
| Point result = null; |
| for (Point next : set1) { |
| double nextDistSquared = source.getDistance(next); |
| if (nextDistSquared < bestDistSquared) { |
| result = next; |
| bestDistSquared = nextDistSquared; |
| } |
| } |
| for (Point next : set2) { |
| double nextDistSquared = source.getDistance(next); |
| if (nextDistSquared < bestDistSquared) { |
| result = next; |
| bestDistSquared = nextDistSquared; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Computes possible anchorable points on grid for given gridSpec. Results |
| * are pushed into the given lists, separately for points computed for |
| * vertical and horizontal grid. |
| * <p/> |
| * As results of this method depends only on the owner location and the |
| * gridspec, they may be cached to improve performance. |
| * |
| * @param gridSpecAbs |
| * absolute grid specification, if <code>null</code> then method |
| * does nothing |
| * @param onVerticalGridLocs |
| * output list to store points of vertical grid |
| * @param onHorizontalGridLocs |
| * output list to store points on horizontal grid |
| */ |
| protected void computeAnchorablePointsOnGrid(Rectangle gridSpecAbs, |
| List<Point> onVerticalGridLocs, List<Point> onHorizontalGridLocs) { |
| if (gridSpecAbs == null) { |
| return; |
| } |
| double gridX = gridSpecAbs.preciseWidth(); |
| double gridY = gridSpecAbs.preciseWidth(); |
| Point gridOrigin = gridSpecAbs.getLocation(); |
| |
| PrecisionRectangle bounds = new PrecisionRectangle( |
| FigureUtilities.getAnchorableFigureBounds(getOwner())); |
| getOwner().translateToAbsolute(bounds); |
| |
| Point notOnGrid = bounds.getCenter(); |
| |
| PrecisionPoint fakeRefAbove = new PrecisionPoint(notOnGrid); |
| PrecisionPoint fakeRefBelow = new PrecisionPoint(notOnGrid); |
| fakeRefAbove |
| .setPreciseY(bounds.preciseY() - bounds.preciseHeight() / 2); |
| fakeRefBelow.setPreciseY(bounds.preciseY() + bounds.preciseHeight() / 2 |
| + bounds.preciseHeight()); |
| |
| double reminderX = Math.IEEEremainder( |
| notOnGrid.preciseX() - gridOrigin.preciseX(), gridX); |
| if (reminderX < 0) { |
| reminderX += gridX; |
| } |
| for (double nextX = notOnGrid.preciseX() - reminderX; nextX >= bounds |
| .preciseX(); nextX -= gridX) { |
| fakeRefAbove.setPreciseX(nextX); |
| fakeRefBelow.setPreciseX(nextX); |
| Point onGridForAboveRef = getOrthogonalLocationNoSnap(fakeRefAbove); |
| Point onGridForBelowRef = getOrthogonalLocationNoSnap(fakeRefBelow); |
| onVerticalGridLocs.add(onGridForAboveRef); |
| if (!onGridForBelowRef.equals(onGridForAboveRef)) { |
| onVerticalGridLocs.add(onGridForBelowRef); |
| } |
| } |
| for (double nextX = gridX + notOnGrid.preciseX() - reminderX; nextX <= bounds |
| .preciseX() + bounds.preciseWidth(); nextX += gridX) { |
| fakeRefAbove.setPreciseX(nextX); |
| fakeRefBelow.setPreciseX(nextX); |
| Point onGridForAboveRef = getOrthogonalLocationNoSnap(fakeRefAbove); |
| Point onGridForBelowRef = getOrthogonalLocationNoSnap(fakeRefBelow); |
| onVerticalGridLocs.add(onGridForAboveRef); |
| if (!onGridForBelowRef.equals(onGridForAboveRef)) { |
| onVerticalGridLocs.add(onGridForBelowRef); |
| } |
| } |
| |
| PrecisionPoint fakeRefLeft = new PrecisionPoint(notOnGrid); |
| PrecisionPoint fakeRefRight = new PrecisionPoint(notOnGrid); |
| fakeRefLeft.setPreciseX(bounds.preciseX() - bounds.preciseWidth() / 2); |
| fakeRefRight.setPreciseX(bounds.preciseX() + bounds.preciseWidth() |
| + bounds.preciseWidth() / 2); |
| double reminderY = Math.IEEEremainder( |
| notOnGrid.preciseY() - gridOrigin.preciseY(), gridY); |
| if (reminderY < 0) { |
| reminderY += gridY; |
| } |
| for (double nextY = notOnGrid.preciseY() - reminderY; nextY >= bounds |
| .preciseY(); nextY -= gridY) { |
| fakeRefLeft.setPreciseY(nextY); |
| fakeRefRight.setPreciseY(nextY); |
| Point onGridForLeftRef = getOrthogonalLocationNoSnap(fakeRefLeft); |
| Point onGridForRightRef = getOrthogonalLocationNoSnap(fakeRefRight); |
| onHorizontalGridLocs.add(onGridForLeftRef); |
| if (!onGridForLeftRef.equals(onGridForRightRef)) { |
| onHorizontalGridLocs.add(onGridForRightRef); |
| } |
| } |
| for (double nextY = gridY + notOnGrid.preciseY() - reminderY; nextY <= bounds |
| .preciseY() + bounds.preciseHeight(); nextY += gridY) { |
| fakeRefLeft.setPreciseY(nextY); |
| fakeRefRight.setPreciseY(nextY); |
| Point onGridForLeftRef = getOrthogonalLocationNoSnap(fakeRefLeft); |
| Point onGridForRightRef = getOrthogonalLocationNoSnap(fakeRefRight); |
| onHorizontalGridLocs.add(onGridForLeftRef); |
| if (!onGridForLeftRef.equals(onGridForRightRef)) { |
| onHorizontalGridLocs.add(onGridForRightRef); |
| } |
| } |
| |
| // System.err.println("Found: on horizontal grid: " + |
| // onHorizontalGridLocs); |
| // System.err.println("Found: on vertical grid: " + onVerticalGridLocs); |
| } |
| |
| protected Point getOrthogonalLocationNoSnap(Point refPoint) { |
| return super.getOrthogonalLocation(refPoint); |
| } |
| |
| } |