/*******************************************************************************
* Copyright (c) 2011, 2016 itemis 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:
* Alexander Nyßen (itemis AG) - initial API and implementation
* Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
*
*******************************************************************************/
package org.eclipse.gef.geometry.planar;
import org.eclipse.gef.geometry.euclidean.Angle;
import org.eclipse.gef.geometry.internal.utils.PrecisionUtils;
/**
* Represents the geometric shape of an {@link Arc}, which is defined by its
* enclosing framing {@link Rectangle}, a start {@link Angle} (relative to the
* x-axis), and an angular extend in counter-clockwise (CCW) direction.
*
* @author anyssen
* @author mwienand
*
*/
public final class Arc extends AbstractArcBasedGeometry<Arc, PolyBezier>
implements ICurve {
private static final long serialVersionUID = 1L;
/**
* Constructs a new {@link Arc} of the given values. An {@link Arc} is cut
* out of an {@link Ellipse}. The start {@link Angle} is the
* counter-clockwise (CCW) {@link Angle} to the x-axis at which the
* {@link Arc} begins. The angular extent is the CCW {@link Angle} that
* spans the {@link Arc}, i.e. the resulting end {@link Angle} of the
* {@link Arc} is the sum of the start {@link Angle} and the angular extent.
*
* @param r
* the {@link AbstractRectangleBasedGeometry} describing the
* bounds of the {@link Ellipse} of which the {@link Arc} is cut
* out
* @param startAngle
* the CCW {@link Angle} at which the {@link Arc} begins
* @param angularExtent
* the CCW {@link Angle} that spans the {@link Arc}
*/
public Arc(AbstractRectangleBasedGeometry<?, ?> r, Angle startAngle,
Angle angularExtent) {
super(r.x, r.y, r.width, r.height, startAngle, angularExtent);
}
/**
* Constructs a new {@link Arc} of the given values. An {@link Arc} is cut
* out of an {@link Ellipse}. The start {@link Angle} is the
* counter-clockwise (CCW) {@link Angle} to the x-axis at which the
* {@link Arc} begins. The angular extent is the CCW {@link Angle} that
* spans the {@link Arc}, i.e. the resulting end {@link Angle} of the
* {@link Arc} is the sum of the start {@link Angle} and the angular extent.
*
* @param x
* the x coordinate of the bounds of the {@link Ellipse} of which
* the {@link Arc} is cut out
* @param y
* the y coordinate of the bounds of the {@link Ellipse} of which
* the {@link Arc} is cut out
* @param width
* the width of the bounds of the {@link Ellipse} of which the
* {@link Arc} is cut out
* @param height
* the height of the bounds of the {@link Ellipse} of which the
* {@link Arc} is cut out
* @param startAngle
* the CCW {@link Angle} at which the {@link Arc} begins
* @param angularExtent
* the CCW {@link Angle} that spans the {@link Arc}
*/
public Arc(double x, double y, double width, double height,
Angle startAngle, Angle angularExtent) {
super(x, y, width, height, startAngle, angularExtent);
}
@Override
public boolean contains(Point p) {
for (CubicCurve c : computeBezierApproximation()) {
if (c.contains(p)) {
return true;
}
}
return false;
}
/*
* TODO: Construct Arc from PolyBezier to round out their relation. (Arc
* returns PolyBezier objects if it is rotated.)
*/
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof Arc)) {
return false;
}
Arc o = (Arc) obj;
return PrecisionUtils.equal(x, o.x) && PrecisionUtils.equal(y, o.y)
&& PrecisionUtils.equal(width, o.width)
&& PrecisionUtils.equal(height, o.height)
&& angularExtent.equals(o.angularExtent)
&& startAngle.equals(o.startAngle);
}
@Override
public Rectangle getBounds() {
return new PolyBezier(toBezier()).getBounds();
}
@Override
public Arc getCopy() {
return new Arc(x, y, width, height, startAngle, angularExtent);
}
@Override
public Point[] getIntersections(ICurve c) {
return CurveUtils.getIntersections(this, c);
}
@Override
public ICurve[] getOverlaps(ICurve c) {
return CurveUtils.getOverlaps(this, c);
}
@Override
public Point getProjection(Point reference) {
double minDistance = 0;
Point minProjection = null;
for (BezierCurve bc : toBezier()) {
Point projection = bc.getProjection(reference);
double distance = projection.getDistance(reference);
if (minProjection == null || distance < minDistance) {
minProjection = projection;
minDistance = distance;
}
}
return minProjection;
}
@Override
public PolyBezier getRotatedCCW(Angle angle) {
return getRotatedCCW(angle, getCenter());
}
@Override
public PolyBezier getRotatedCCW(Angle angle, double cx, double cy) {
return new PolyBezier(computeBezierApproximation()).rotateCCW(angle, cx,
cy);
}
@Override
public PolyBezier getRotatedCCW(Angle angle, Point center) {
return new PolyBezier(computeBezierApproximation()).rotateCCW(angle,
center);
}
@Override
public PolyBezier getRotatedCW(Angle angle) {
return getRotatedCW(angle, getCenter());
}
@Override
public PolyBezier getRotatedCW(Angle angle, double cx, double cy) {
return new PolyBezier(computeBezierApproximation()).rotateCW(angle, cx,
cy);
}
@Override
public PolyBezier getRotatedCW(Angle angle, Point center) {
return new PolyBezier(computeBezierApproximation()).rotateCW(angle,
center);
}
@Override
public boolean intersects(ICurve c) {
return CurveUtils.getIntersections(this, c).length > 0;
}
@Override
public boolean overlaps(ICurve c) {
for (BezierCurve seg1 : computeBezierApproximation()) {
if (seg1.overlaps(c)) {
return true;
}
}
return false;
}
@Override
public CubicCurve[] toBezier() {
return computeBezierApproximation();
}
@Override
public String toString() {
return "Arc(" + "x = " + x + ", y = " + y + ", width = " + width
+ ", height = " + height + ", startAngle = " + startAngle.deg()
+ ", angularExtend = " + angularExtent.deg() + ")";
}
}