/*******************************************************************************
* Copyright (c) 2011, 2015 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.euclidean.Vector;
import org.eclipse.gef.geometry.internal.utils.PointListUtils;
/**
* Abstract superclass of geometries that are defined by means of a point list.
* <p>
* The type parameter <code>T</code> specifies the type of the inheriting class.
* This is to be able to return the correct type, so that a type cast is
* unnecessary.
* </p>
*
* @param <T>
* specifies the type of the inheriting class in order to avoid
* otherwise necessary type casts
*
* @author anyssen
* @author mwienand
*
*/
abstract class AbstractPointListBasedGeometry<T extends AbstractPointListBasedGeometry<?>>
extends AbstractGeometry
implements ITranslatable<T>, IScalable<T>, IRotatable<T> {
private static final long serialVersionUID = 1L;
/**
* The points constituting this {@link AbstractPointListBasedGeometry}.
*/
Point[] points;
/**
* Constructs a new {@link AbstractPointListBasedGeometry} from a
* even-numbered sequence of coordinates.
*
* @param coordinates
* an alternating, even-numbered sequence of x and y coordinates,
* representing the {@link Point}s from which the
* {@link AbstractPointListBasedGeometry} is to be created
* @see #AbstractPointListBasedGeometry(Point...)
*/
public AbstractPointListBasedGeometry(double... coordinates) {
points = new Point[coordinates.length / 2];
for (int i = 0; i < coordinates.length / 2; i++) {
points[i] = new Point(coordinates[i * 2], coordinates[i * 2 + 1]);
}
}
/**
* Constructs a new {@link AbstractPointListBasedGeometry} from the given
* sequence of {@link Point} s.
*
* @param points
* a sequence of points, from which the
* {@link AbstractPointListBasedGeometry} is to be created.
*/
public AbstractPointListBasedGeometry(Point... points) {
this.points = Point.getCopy(points);
}
@Override
public final Rectangle getBounds() {
return Point.getBounds(points);
}
/**
* Computes the centroid of this {@link AbstractPointListBasedGeometry}. The
* centroid is the "center of gravity", i.e. assuming a {@link Polygon} is
* spanned by the {@link Point}s of this
* {@link AbstractPointListBasedGeometry} and it is made of a material of
* constant density, then it is in a balanced state, if you put it on a pin
* that is placed exactly on its centroid.
*
* @return the center {@link Point} (or centroid) of this
* {@link AbstractPointListBasedGeometry}
*/
public Point getCentroid() {
return Point.getCentroid(points);
}
/**
* Returns a double array which represents the sequence of coordinates of
* the {@link Point}s that make up this
* {@link AbstractPointListBasedGeometry}.
*
* @return an array that alternately contains the x and y coordinates of
* this {@link AbstractPointListBasedGeometry}'s points
*/
public final double[] getCoordinates() {
return PointListUtils.toCoordinatesArray(points);
}
/**
* Returns a copy of the {@link Point}s that make up this
* {@link AbstractPointListBasedGeometry}.
*
* @return an array of {@link Point}s representing the {@link Point}s that
* make up this {@link AbstractPointListBasedGeometry}
*/
public final Point[] getPoints() {
return Point.getCopy(points);
}
@Override
public T getRotatedCCW(Angle alpha) {
return getRotatedCCW(alpha, getCentroid());
}
@Override
public T getRotatedCCW(Angle angle, double cx, double cy) {
return getRotatedCCW(angle, new Point(cx, cy));
}
@Override
@SuppressWarnings("unchecked")
public T getRotatedCCW(Angle alpha, Point center) {
return (T) ((T) getCopy()).rotateCCW(alpha, center);
}
@Override
public T getRotatedCW(Angle alpha) {
return getRotatedCW(alpha, getCentroid());
}
@Override
public T getRotatedCW(Angle angle, double cx, double cy) {
return getRotatedCW(angle, new Point(cx, cy));
}
@Override
@SuppressWarnings("unchecked")
public T getRotatedCW(Angle alpha, Point center) {
return (T) ((T) getCopy()).rotateCW(alpha, center);
}
@Override
@SuppressWarnings("unchecked")
public T getScaled(double factor) {
return (T) ((T) getCopy()).scale(factor);
}
@Override
@SuppressWarnings("unchecked")
public T getScaled(double factorX, double factorY) {
return (T) ((T) getCopy()).scale(factorX, factorY);
}
@Override
public T getScaled(double factor, double cx, double cy) {
return getScaled(factor, factor, new Point(cx, cy));
}
@Override
public T getScaled(double fx, double fy, double cx, double cy) {
return getScaled(fx, fy, new Point(cx, cy));
}
@Override
@SuppressWarnings("unchecked")
public T getScaled(double factorX, double factorY, Point center) {
return (T) ((T) getCopy()).scale(factorX, factorY, center);
}
@Override
@SuppressWarnings("unchecked")
public T getScaled(double factor, Point center) {
return (T) ((T) getCopy()).scale(factor, center);
}
@Override
@SuppressWarnings("unchecked")
public T getTranslated(double dx, double dy) {
return (T) ((T) getCopy()).translate(dx, dy);
}
@Override
@SuppressWarnings("unchecked")
public T getTranslated(Point pt) {
return (T) ((T) getCopy()).translate(pt);
}
/**
* Rotates this {@link AbstractPointListBasedGeometry} counter-clockwise
* (CCW) by the given {@link Angle} around its centroid (see
* {@link #getCentroid()}).
*
* @param alpha
* the rotation {@link Angle}
* @return <code>this</code> for convenience
* @see #rotateCCW(Angle, Point)
*/
public T rotateCCW(Angle alpha) {
return rotateCCW(alpha, getCentroid());
}
/**
* Rotates this {@link AbstractPointListBasedGeometry} counter-clockwise
* (CCW) by the given {@link Angle} around the {@link Point} specified by
* the passed-in x and y coordinates.
*
* @param alpha
* the rotation {@link Angle}
* @param cx
* the x coordinate of the {@link Point} to rotate around
* @param cy
* the y coordinate of the {@link Point} to rotate around
* @return <code>this</code> for convenience
* @see #rotateCCW(Angle, Point)
*/
public T rotateCCW(Angle alpha, double cx, double cy) {
return rotateCCW(alpha, new Point(cx, cy));
}
/**
* Rotates this {@link AbstractPointListBasedGeometry} counter-clockwise
* (CCW) by the given {@link Angle} around the given {@link Point}.
*
* The rotation is done by
* <ol>
* <li>translating this {@link AbstractPointListBasedGeometry} by the
* negated {@link Point} center</li>
* <li>rotating each {@link Point} of this
* {@link AbstractPointListBasedGeometry} counter-clockwise by the given
* {@link Angle}</li>
* <li>translating this {@link AbstractPointListBasedGeometry} back by the
* {@link Point} center</li>
* </ol>
*
* @param alpha
* the rotation {@link Angle}
* @param center
* the {@link Point} to rotate around
* @return <code>this</code> for convenience
*/
@SuppressWarnings("unchecked")
public T rotateCCW(Angle alpha, Point center) {
translate(center.getNegated());
for (Point p : points) {
Point np = new Vector(p).rotateCCW(alpha).toPoint();
p.x = np.x;
p.y = np.y;
}
translate(center);
return (T) this;
}
/**
* Rotates this {@link AbstractPointListBasedGeometry} clockwise (CW) by the
* given {@link Angle} around its centroid (see {@link #getCentroid()}).
*
* @param alpha
* the rotation {@link Angle}
* @return <code>this</code> for convenience
* @see #rotateCW(Angle, Point)
*/
public T rotateCW(Angle alpha) {
return rotateCW(alpha, getCentroid());
}
/**
* Rotates this {@link AbstractPointListBasedGeometry} clockwise (CW) by the
* given {@link Angle} around the {@link Point} specified by the passed-in x
* and y coordinates.
*
* @param alpha
* the rotation {@link Angle}
* @param cx
* the x coordinate of the {@link Point} to rotate around
* @param cy
* the y coordinate of the {@link Point} to rotate around
* @return <code>this</code> for convenience
* @see #rotateCW(Angle, Point)
*/
public T rotateCW(Angle alpha, double cx, double cy) {
return rotateCW(alpha, new Point(cx, cy));
}
/**
* Rotates this {@link AbstractPointListBasedGeometry} clockwise (CW) by the
* given {@link Angle} around the given {@link Point}.
*
* The rotation is done by
* <ol>
* <li>translating this {@link AbstractPointListBasedGeometry} by the
* negated {@link Point} center</li>
* <li>rotating each {@link Point} of this
* {@link AbstractPointListBasedGeometry} clockwise by the given
* {@link Angle}</li>
* <li>translating this {@link AbstractPointListBasedGeometry} back by the
* {@link Point} center</li>
* </ol>
*
* @param alpha
* the rotation {@link Angle}
* @param center
* the {@link Point} to rotate around
* @return <code>this</code> for convenience
*/
@SuppressWarnings("unchecked")
public T rotateCW(Angle alpha, Point center) {
translate(center.getNegated());
for (Point p : points) {
Point np = new Vector(p).rotateCW(alpha).toPoint();
p.x = np.x;
p.y = np.y;
}
translate(center);
return (T) this;
}
@Override
public T scale(double factor) {
return scale(factor, factor);
}
@Override
public T scale(double fx, double fy) {
return scale(fx, fy, getCentroid());
}
@Override
public T scale(double factor, double cx, double cy) {
return scale(factor, factor, new Point(cx, cy));
}
@Override
public T scale(double fx, double fy, double cx, double cy) {
return scale(fx, fy, new Point(cx, cy));
}
@Override
@SuppressWarnings("unchecked")
public T scale(double fx, double fy, Point center) {
for (Point p : points) {
Point np = p.getScaled(fx, fy, center);
p.x = np.x;
p.y = np.y;
}
return (T) this;
}
@Override
public T scale(double factor, Point center) {
return scale(factor, factor, center);
}
@Override
@SuppressWarnings("unchecked")
public T translate(double dx, double dy) {
Point.translate(points, dx, dy);
return (T) this;
}
@Override
public T translate(Point p) {
return translate(p.x, p.y);
}
}