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