/*******************************************************************************
* <copyright>
*
* Copyright (c) 2005, 2011 SAP AG.
* 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:
* SAP AG - initial API, implementation and documentation
* mwenz - Bug 336516 - Anchors with polyline GA caused NPE
* mgorning - Bug 355968 - Only ChopboxAnchors should compute the reference point as the center of its GA
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.ui.internal.util.draw2d;
import org.eclipse.draw2d.Ellipse;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Polygon;
import org.eclipse.draw2d.Polyline;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.PrecisionPoint;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.handles.HandleBounds;
import org.eclipse.graphiti.mm.pictograms.AdvancedAnchor;
import org.eclipse.graphiti.ui.internal.figures.GFEllipse;
import org.eclipse.graphiti.ui.internal.figures.GFEllipseDecoration;
import org.eclipse.graphiti.ui.internal.figures.GFPolygon;
import org.eclipse.graphiti.ui.internal.figures.GFPolyline;
/**
* A chopbox anchor supporting insets
*
* @noinstantiate This class is not intended to be instantiated by clients.
* @noextend This class is not intended to be subclassed by clients.
*/
public class GFChopboxAnchor extends ChopboxAnchorFixed {
public GFChopboxAnchor(IFigure figure) {
super(figure, null);
}
public GFChopboxAnchor(IFigure figure, AdvancedAnchor advancedAnchor) {
super(figure, advancedAnchor);
}
/**
* Gets the anchors associated figure's bounding box in absolute
* coordinates.
*
* @return a <code>Rectangle</code> that is the bounding box of the owner
* figure in absolute coordinates. also regards the indents
*/
@Override
protected Rectangle getBox() {
if (getOwner() instanceof HandleBounds) {
Rectangle copy = ((HandleBounds) getOwner()).getHandleBounds().getCopy();
if (getAdvancedAnchor() != null && getAdvancedAnchor().isUseAnchorLocationAsConnectionEndpoint()) {
int x = getAdvancedAnchor().getGraphicsAlgorithm().getX();
int y = getAdvancedAnchor().getGraphicsAlgorithm().getY();
copy.translate(-x, -y);
copy.setHeight(1);
copy.setWidth(1);
}
return copy;
}
return getOwner().getBounds().getCopy();
}
@Override
public Point getLocation(Point reference) {
if (getOwner() instanceof GFEllipse || getOwner() instanceof GFEllipseDecoration
|| getOwner() instanceof Ellipse) {
Rectangle r = Rectangle.SINGLETON;
r.setBounds(getBox()); // r.setBounds(getOwner().getBounds().getCropped(getOwner().getInsets()));
r.translate(-1, -1);
r.resize(1, 1);
getOwner().translateToAbsolute(r);
Point ref = r.getCenter().negate().translate(reference);
if (ref.x == 0)
return new Point(reference.x, (ref.y > 0) ? r.bottom() : r.y);
if (ref.y == 0)
return new Point((ref.x > 0) ? r.right() : r.x, reference.y);
float dx = (ref.x > 0) ? 0.5f : -0.5f;
float dy = (ref.y > 0) ? 0.5f : -0.5f;
// ref.x, ref.y, r.width, r.height != 0 => safe to proceed
float k = (float) (ref.y * r.width) / (ref.x * r.height);
k = k * k;
return r.getCenter().translate((int) (r.width * dx / Math.sqrt(1 + k)),
(int) (r.height * dy / Math.sqrt(1 + 1 / k)));
}
if (getOwner() instanceof GFPolygon || getOwner() instanceof Polygon || getOwner() instanceof GFPolyline
|| getOwner() instanceof Polyline) {
Point foreignReference = reference.getCopy();
// the midpoint
Point ownReference = getReferencePoint().getCopy();
// nice feature!
// ownReference = normalizeToStraightlineTolerance(foreignReference,
// ownReference, STRAIGHT_LINE_TOLERANCE);
Point location = getLocation(ownReference, foreignReference);
if (location == null || getBox().expand(1, 1).contains(foreignReference)
&& !getBox().shrink(1, 1).contains(foreignReference))
location = getLocation(getBox().getCenter(), foreignReference);
if (location == null) {
location = getBox().getCenter();
}
return location;
}
return super.getLocation(reference);
}
/**
* Calculates the location of the anchor depending on the anchors own
* reference and foreign reference points
*
* @param ownReference
* - the own reference of the anchor
* @param foreignReference
* - foreign reference that comes in
* @return the location of the anchor depending on the anchors own reference
* and foreign reference points
*/
protected Point getLocation(Point ownReference, Point foreignReference) {
PointList intersections = getIntersectionPoints(ownReference, foreignReference);
if (intersections != null && intersections.size() != 0) {
Point location = pickClosestPoint(intersections, foreignReference);
return location;
}
return null;
}
/**
* Assumption: Points in the <Code>PointList</Code> and <Code>Point</Code> p
* lie on the same line. Returns the <Code>Point</Code> from the
* <Code>PointList</Code> closest to
*
* @param p
* @param points
* - the list of points to select the result from
* @param p
* - the point to which the closest point must be found
* @return the <Code>Point</Code> from the <Code>PointList</Code> closest to
* @param p
*/
public static Point pickClosestPoint(PointList points, Point p) {
Point result = points.getFirstPoint();
for (int i = 1; i < points.size(); i++) {
Point temp = points.getPoint(i);
if (Math.abs(temp.x - p.x) < Math.abs(result.x - p.x))
result = temp;
else if (Math.abs(temp.y - p.y) < Math.abs(result.y - p.y))
result = temp;
}
return result;
}
/**
* Calculates intersection points of the figure and the line that passes
* through ownReference and foreignReference points
*
* @param ownReference
* the reference <code>Point</code> on or inside the shape that
* is being anchored to.
* @param foreignReference
* the outside reference <code>Point</code> point that is the
* terminal end of the line formed by the two parameters.
* @return intersection points of the figure and the line that passes
* through ownReference and foreignReference points
*/
protected PointList getIntersectionPoints(Point ownReference, Point foreignReference) {
final PointList polygon = getPolylinePoints();
return (new LineSeg(ownReference, foreignReference)).getLineIntersectionsWithLineSegs(polygon);
}
/**
* Returns the list of all the vertices of the figure. The created list must
* form a polygon, i.e. closed polyline, for figures hence the starting and
* ending points must be the same
*
* @return the <code>PointList</code> list of all the vertices of the
* figure.
*/
protected PointList getPolylinePoints() {
if (getOwner() instanceof GFPolyline) {
PointList polyList = ((GFPolyline) getOwner()).getPoints().getCopy();
polyList.addPoint(polyList.getFirstPoint());
getOwner().translateToAbsolute(polyList);
return polyList;
}
return null;
}
/**
* Calculates the relative location of the reference point with respect to
* the bounds of the figure. If point p is not inside of the figure's bounds
* then the point is mapped on the bounds and the point relative location is
* calculated
*
* @param p
* the <code>Point</code> that is relative coordinates of the
* point
* @return <Code>PrecisionPoint</Code>, i.e. the relative reference for
* <Code>SlidableAnchor</Code>
*/
static public PrecisionPoint getAnchorRelativeLocation(Point p, Rectangle bounds) {
PrecisionPoint relLocation;
Point temp = new Point(p);
if (p.x < bounds.x || p.x > bounds.x + bounds.width || p.y < bounds.y || p.y > bounds.y + bounds.height) {
if (p.x < bounds.x || p.x > bounds.x + bounds.width) {
temp.x = p.x < bounds.x ? bounds.x : bounds.x + bounds.width;
}
if (p.y < bounds.y || p.y > bounds.y + bounds.height) {
temp.y = p.y < bounds.y ? bounds.y : bounds.y + bounds.height;
}
relLocation = new PrecisionPoint((double) (temp.x - bounds.x) / bounds.width, (double) (temp.y - bounds.y)
/ bounds.height);
} else {
relLocation = new PrecisionPoint((double) (temp.x - bounds.x) / bounds.width, (double) (temp.y - bounds.y)
/ bounds.height);
}
return relLocation;
}
}