| /******************************************************************************* |
| * Copyright (c) 2010 itemis AG (http://www.itemis.de) |
| * 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: |
| * itemis AG - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.draw2d; |
| |
| import org.eclipse.draw2d.geometry.Dimension; |
| import org.eclipse.draw2d.geometry.Point; |
| import org.eclipse.draw2d.geometry.PrecisionPoint; |
| import org.eclipse.draw2d.geometry.Rectangle; |
| |
| /** |
| * Anchor for rounded rectangles which is always on a line between the center |
| * and the reference point. |
| * |
| * @author Benjamin Schwertfeger (benjamin.schwertfeger@itemis.de) |
| * @since 3.8 |
| */ |
| public class RoundedRectangleAnchor extends ChopboxAnchor { |
| |
| private static final int LEFT = 1; |
| |
| private static final int MIDDLE = 2; |
| |
| private static final int RIGHT = 4; |
| |
| private static final int TOP = 8; |
| |
| private static final int CENTER = 16; |
| |
| private static final int BOTTOM = 32; |
| |
| private final Dimension dimension; |
| |
| /** |
| * Rounded Rectangle getCornerDimension should be public #302836 then |
| * Rounded Rectangle would be sufficient. |
| */ |
| public RoundedRectangleAnchor(final RoundedRectangle figure) { |
| super(figure); |
| dimension = null; |
| } |
| |
| /** |
| * Rounded Rectangle getCornerDimension should be public #302836 then |
| * Rounded Rectangle would be sufficient. |
| */ |
| public RoundedRectangleAnchor(final Figure figure, final Dimension corners) { |
| super(figure); |
| dimension = corners; |
| } |
| |
| /** |
| * Calculates the position with ChopboxAnchor#getLocation() and if the |
| * anchor is not at the rounded corners, the result is returned. If the |
| * anchor point should be at a corner, the rectangle for the ellipse is |
| * determined and ellipseAnchorGetLocation returns the two intersection |
| * points between the line from calculated anchor point and the center of |
| * the rounded rectangle. |
| * |
| * @return The anchor location |
| */ |
| public Point getLocation(final Point ref) { |
| Dimension corner = dimension; |
| if (getOwner() instanceof RoundedRectangle) { |
| corner = ((RoundedRectangle) getOwner()).getCornerDimensions(); |
| } |
| final Point location = super.getLocation(ref); |
| final Rectangle r = Rectangle.getSINGLETON(); |
| r.setBounds(getOwner().getBounds()); |
| r.translate(-1, -1); |
| r.resize(1, 1); |
| getOwner().translateToAbsolute(r); |
| final int yTop = r.y + corner.height / 2; |
| final int yBottom = r.y + r.height - corner.height / 2; |
| final int xLeft = r.x + corner.width / 2; |
| final int xRight = r.x + r.width - corner.width / 2; |
| int pos = 0; |
| if (location.x < xLeft) { |
| pos = LEFT; |
| } else if (location.x > xRight) { |
| pos = RIGHT; |
| } else { |
| pos = MIDDLE; |
| } |
| if (location.y < yTop) { |
| pos |= TOP; |
| } else if (location.y > yBottom) { |
| pos |= BOTTOM; |
| } else { |
| pos += CENTER; |
| } |
| switch (pos) { |
| case TOP | MIDDLE: |
| case CENTER | LEFT: |
| case CENTER | RIGHT: |
| case BOTTOM | MIDDLE: |
| return new Point(location.x, location.y); |
| case TOP | LEFT: |
| return ellipseAnchorGetLocation(location, new Rectangle(r.x, r.y, |
| corner.width, corner.height), getOwner().getBounds() |
| .getCenter())[0]; |
| case TOP | RIGHT: |
| return ellipseAnchorGetLocation(location, |
| new Rectangle(r.x + r.width - corner.width, r.y, |
| corner.width, corner.height), getOwner() |
| .getBounds().getCenter())[1]; |
| case CENTER | MIDDLE: |
| // default for reference inside Figure |
| return new Point(r.x, r.y + r.height / 2); |
| case BOTTOM | LEFT: |
| return ellipseAnchorGetLocation(location, new Rectangle(r.x, r.y |
| + r.height - corner.height, corner.width, corner.height), |
| getOwner().getBounds().getCenter())[0]; |
| case BOTTOM | RIGHT: |
| return ellipseAnchorGetLocation(location, new Rectangle(r.x |
| + r.width - corner.width, r.y + r.height - corner.height, |
| corner.width, corner.height), getOwner().getBounds() |
| .getCenter())[1]; |
| default: |
| throw new IllegalStateException( |
| "Calculation of RoundedRectangleAnchor missed. Rect: " + r //$NON-NLS-1$ |
| + " Point: " + location); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Calculation of intersections points of one ellipse, represented by r, and |
| * the line between ref and c. |
| * |
| * @param reference |
| * reference point for line end (end of the line) |
| * @param r |
| * the rectangle of the ellipse, where the intersection points |
| * are wanted for |
| * @param center |
| * center of the figure (start of the line) |
| * @return Two intersection points of circle with the line. They could be |
| * equal, if the line only tangents. Returns null, if no |
| * intersection was found. |
| */ |
| private static Point[] ellipseAnchorGetLocation(final Point ref, |
| final Rectangle r, Point c) { |
| |
| // Move the coordinates so that the center of ellipse is in the origin. |
| final PrecisionPoint reference = new PrecisionPoint(r.getCenter() |
| .negate().translate(ref)); |
| final PrecisionPoint center = new PrecisionPoint(r.getCenter().negate() |
| .translate(c)); |
| // Transform the coordinate axis, to make the ellipse a circle with |
| // radius 1. |
| final double referenceX = reference.preciseX() * 2.0 / r.width; |
| final double referenceY = reference.preciseY() * 2.0 / r.height; |
| final double centerX = center.preciseX() * 2.0 / r.width; |
| final double centerY = center.preciseY() * 2.0 / r.height; |
| |
| // the line is y=a*x+b detemine a and b |
| final double a = (referenceY - centerY) / (referenceX - centerX); |
| final double b = centerY - (centerX * a); |
| |
| // circle is x^2+y^2=1. With the line this leads to |
| // |
| // x_{1/2} = +-Sqrt( (1-b*b)/((a*a+1)^2) + (a*a*b*b)/(a*a+1) ) - |
| // (a*b)/a*a+1 |
| // |
| // y = a*x+b |
| final double bSqr = Math.pow(b, 2); |
| final double aSqr = Math.pow(a, 2); |
| final double xSqrt = Math.sqrt((1 - bSqr) / (aSqr + 1) + (aSqr * bSqr) |
| / (Math.pow(aSqr + 1, 2))); |
| if (xSqrt == Double.NaN) { |
| // no intersection found |
| return null; |
| } |
| final double x1 = -xSqrt - (a * b) / (Math.pow(a, 2) + 1); |
| final double x2 = +xSqrt - (a * b) / (Math.pow(a, 2) + 1); |
| final double y1 = a * x1 + b; |
| final double y2 = a * x2 + b; |
| final Point p1 = new PrecisionPoint(x1 * r.width / 2.0, y1 * r.height |
| / 2.0); |
| final Point p2 = new PrecisionPoint(x2 * r.width / 2.0, y2 * r.height |
| / 2.0); |
| return new Point[] { r.getCenter().translate(p1), |
| r.getCenter().translate(p2) }; |
| } |
| } |