/* file : ParabolaArc2D.java
*
* Project : geometry
*
* ===========================================
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. if not, write to :
* The Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Created on 02 May 2007
*
*/
package math.geom2d.conic;
import static java.lang.Math.PI;
import static java.lang.Math.max;
import static java.lang.Math.min;
import java.util.ArrayList;
import java.util.Collection;
import math.geom2d.*;
import math.geom2d.curve.*;
import math.geom2d.domain.SmoothOrientedCurve2D;
import math.geom2d.line.LinearShape2D;
import math.geom2d.line.StraightLine2D;
import math.geom2d.polygon.Polyline2D;
import math.utils.EqualUtils;
/**
* An arc of parabola, defined by a parent parabola, and two limits for the
* parametrization.
*
* @author dlegland
*/
public class ParabolaArc2D extends AbstractSmoothCurve2D
implements SmoothOrientedCurve2D, Cloneable {
// ==========================================================
// static constructor
public static ParabolaArc2D create(Parabola2D parabola, double t0, double t1) {
return new ParabolaArc2D(parabola, t0, t1);
}
// ==========================================================
// class variables
/**
* The parent parabola.
*/
protected Parabola2D parabola = new Parabola2D();
/**
* The lower bound if the parameterization for this arc.
*/
double t0 = 0;
/**
* The upper bound if the parameterization for this arc.
*/
double t1 = 1;
// ==========================================================
// constructor
public ParabolaArc2D(Parabola2D parabola, double t0, double t1) {
this.parabola = parabola;
this.t0 = t0;
this.t1 = t1;
}
// ==========================================================
// methods specific to ParabolaArc2D
public Parabola2D getParabola() {
return this.parabola;
}
// ==========================================================
// methods implementing the OrientedCurve2D interface
public double windingAngle(Point2D point) {
double angle0, angle1;
boolean direct = parabola.isDirect();
boolean inside = this.isInside(point);
if (Double.isInfinite(t0)) {
angle0 = parabola.getAngle() + (direct ? +1 : -1) * PI / 2;
} else {
angle0 = Angle2D.horizontalAngle(point, parabola.point(t0));
}
if (Double.isInfinite(t1)) {
angle1 = parabola.getAngle() + (direct ? +1 : -1) * PI / 2;
} else {
angle1 = Angle2D.horizontalAngle(point, parabola.point(t1));
}
if (inside) {
// turn CCW -> return positive angle
if (angle0 > angle1)
return 2 * PI - angle0 + angle1;
else
return angle1 - angle0;
} else {
// turn CW -> return negative angle
if (angle0 > angle1)
return angle1 - angle0;
else
return (angle1 - angle0) - 2 * PI;
}
}
public double signedDistance(Point2D p) {
return signedDistance(p.x(), p.y());
}
public double signedDistance(double x, double y) {
if (isInside(new Point2D(x, y)))
return -distance(x, y);
return -distance(x, y);
}
public boolean isInside(Point2D point) {
boolean direct = parabola.isDirect();
boolean inside = parabola.isInside(point);
if (inside && direct)
return true;
if (!inside && !direct)
return false;
double pos = parabola.project(point);
if (pos < t0) {
Point2D p0 = parabola.point(t0);
Vector2D v0 = parabola.tangent(t0);
StraightLine2D line0 = new StraightLine2D(p0, v0);
return line0.isInside(point);
}
if (pos > t1) {
Point2D p1 = parabola.point(t1);
Vector2D v1 = parabola.tangent(t1);
StraightLine2D line1 = new StraightLine2D(p1, v1);
return line1.isInside(point);
}
return !direct;
}
// ==========================================================
// methods implementing the SmoothCurve2D interface
public Vector2D tangent(double t) {
return parabola.tangent(t);
}
/**
* Returns the curvature of the parabola arc.
*/
public double curvature(double t) {
return parabola.curvature(t);
}
// ==========================================================
// methods implementing the ContinuousCurve2D interface
/** Returns false, by definition of a parabola arc */
public boolean isClosed() {
return false;
}
public Polyline2D asPolyline(int n) {
// Check that the curve is bounded
if (!this.isBounded())
throw new UnboundedShape2DException(this);
// compute start and increment values
double t0 = this.t0();
double dt = (this.t1() - t0) / n;
// allocate array of points, and compute each value.
Point2D[] points = new Point2D[n+1];
for(int i = 0; i < n+1;i++)
points[i] = this.point(t0 + i*dt);
return new Polyline2D(points);
}
// ====================================================================
// methods implementing the Curve2D interface
/**
* Returns the position of the first point of the parabola arc.
*/
public double t0() {
return t0;
}
/**
* @deprecated replaced by t0() (since 0.11.1).
*/
@Deprecated
public double getT0() {
return t0();
}
/**
* Returns the position of the last point of the parabola arc.
*/
public double t1() {
return t1;
}
/**
* @deprecated replaced by t1() (since 0.11.1).
*/
@Deprecated
public double getT1() {
return t1();
}
public Point2D point(double t) {
t = min(max(t, t0), t1);
return parabola.point(t);
}
public double position(Point2D point) {
if (!this.parabola.contains(point))
return Double.NaN;
double t = this.parabola.position(point);
if (t - t0 < -ACCURACY)
return Double.NaN;
if (t1 - t < ACCURACY)
return Double.NaN;
return t;
}
public double project(Point2D point) {
double t = this.parabola.project(point);
return min(max(t, t0), t1);
}
public Collection intersections(LinearShape2D line) {
Collection inters0 = this.parabola.intersections(line);
ArrayList inters = new ArrayList(2);
for (Point2D point : inters0) {
double pos = this.parabola.position(point);
if (pos > this.t0 && pos < this.t1)
inters.add(point);
}
return inters;
}
/**
* Returns the parabola arc which refers to the reversed parent parabola,
* and with inverted parameterization bounds.
*/
public ParabolaArc2D reverse() {
return new ParabolaArc2D(this.parabola.reverse(), -t1, -t0);
}
public ParabolaArc2D subCurve(double t0, double t1) {
if (t1 < t0)
return null;
t0 = max(this.t0, t0);
t1 = min(this.t1, t1);
return new ParabolaArc2D(parabola, t0, t1);
}
// ====================================================================
// methods implementing the Shape2D interface
public double distance(Point2D p) {
return distance(p.x(), p.y());
}
public double distance(double x, double y) {
// TODO Auto-generated method stub
return this.asPolyline(100).distance(x, y);
}
/**
* Returns true if the arc is bounded, i.e. if both limits are finite.
*/
public boolean isBounded() {
if (t0 == Double.NEGATIVE_INFINITY)
return false;
if (t1 == Double.POSITIVE_INFINITY)
return false;
return true;
}
/**
* Return true if t1,
* which contains only instances of ParabolaArc2D. If the parabola arc is
* not clipped, the result is an instance of CurveSet2D
* which contains 0 curves.
*/
public CurveSet2D extends ParabolaArc2D> clip(Box2D box) {
// Clip the curve
CurveSet2D set = Curves2D.clipSmoothCurve(this, box);
// Stores the result in appropriate structure
CurveArray2D result =
new CurveArray2D(set.size());
// convert the result
for (Curve2D curve : set.curves()) {
if (curve instanceof ParabolaArc2D)
result.add((ParabolaArc2D) curve);
}
return result;
}
public Box2D boundingBox() {
// TODO Auto-generated method stub
return this.asPolyline(100).boundingBox();
}
public ParabolaArc2D transform(AffineTransform2D trans) {
Parabola2D par = parabola.transform(trans);
// Compute position of end points on the transformed parabola
double startPos = Double.isInfinite(t0) ? Double.NEGATIVE_INFINITY
: par.project(this.firstPoint().transform(trans));
double endPos = Double.isInfinite(t1) ? Double.POSITIVE_INFINITY : par
.project(this.lastPoint().transform(trans));
// Compute the new arc
return new ParabolaArc2D(par, startPos, endPos);
}
// ====================================================================
// methods implementing the Shape interface
public boolean contains(double x, double y) {
// Check on parent parabola
if (!parabola.contains(x, y))
return false;
// Check if position of point is inside of bounds
double t = parabola.position(new Point2D(x, y));
if (t < this.t0)
return false;
if (t > this.t1)
return false;
return true;
}
public boolean contains(Point2D point) {
return contains(point.x(), point.y());
}
// ====================================================================
// Drawing methods
public java.awt.geom.GeneralPath appendPath(java.awt.geom.GeneralPath path) {
// Check curve is bounded
if (!this.isBounded())
throw new UnboundedShape2DException(this);
// Compute position and tangent at extremities
Point2D p1 = this.firstPoint();
Point2D p2 = this.lastPoint();
Vector2D v1 = this.tangent(this.t0);
Vector2D v2 = this.tangent(this.t1);
// Compute tangent lines at extremities
StraightLine2D line1 = new StraightLine2D(p1, v1);
StraightLine2D line2 = new StraightLine2D(p2, v2);
// Compute intersection point of tangent lines
Point2D pc = line1.intersection(line2);
// Use quadratic curve to represent (exactly) the parabola arc
path.quadTo(pc.x(), pc.y(), p2.x(), p2.y());
return path;
}
public java.awt.geom.GeneralPath getGeneralPath() {
if (!this.isBounded())
throw new UnboundedShape2DException(this);
return this.asPolyline(32).asGeneralPath();
}
// ===================================================================
// methods implementing the GeometricObject2D interface
/* (non-Javadoc)
* @see math.geom2d.GeometricObject2D#almostEquals(math.geom2d.GeometricObject2D, double)
*/
public boolean almostEquals(GeometricObject2D obj, double eps) {
if (this==obj)
return true;
if (!(obj instanceof ParabolaArc2D))
return false;
ParabolaArc2D arc = (ParabolaArc2D) obj;
if (!this.parabola.almostEquals(arc.parabola, eps))
return false;
if (Math.abs(this.t0 - arc.t0) > eps)
return false;
if (Math.abs(this.t1 - arc.t1) > eps)
return false;
return true;
}
// ====================================================================
// Methods inherited from object interface
@Override
public String toString() {
return String.format("ParabolaArc2D(%f,%f,%f,%f,%f,%f)",
parabola.xv, parabola.yv, parabola.a, parabola.theta, t0, t1);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ParabolaArc2D))
return false;
ParabolaArc2D that = (ParabolaArc2D) obj;
if (!this.parabola.equals(that.parabola))
return false;
if (!EqualUtils.areEqual(this.t0, that.t0))
return false;
if (!EqualUtils.areEqual(this.t1, that.t1))
return false;
return true;
}
/**
* @deprecated use copy constructor instead (0.11.2)
*/
@Deprecated
@Override
public ParabolaArc2D clone() {
return new ParabolaArc2D(parabola.clone(), t0, t1);
}
}
Ads help maintain this website.