public Vector2D tangent(double t) { // format between min and max admissible values t = min(max(0, t), abs(angleExtent)); // compute tangent vector depending on position if (angleExtent < 0) { // need to invert vector for indirect arcs return ellipse.tangent(startAngle - t).times(-1); } else { return ellipse.tangent(startAngle + t); } }
/** Returns the curvature of the ellipse arc. Curvature is negative if the arc is indirect. */ public double curvature(double t) { // convert position to angle if (angleExtent < 0) t = startAngle - t; else t = startAngle + t; double kappa = ellipse.curvature(t); return this.isDirect() ? kappa : -kappa; }
/* * (non-Javadoc) * * @see math.geom2d.Shape2D#signedDistance(math.geom2d.Point2D) */ public double signedDistance(double x, double y) { boolean direct = angleExtent >= 0; double dist = distance(x, y); Point2D point = new Point2D(x, y); boolean inside = ellipse.isInside(point); if (inside) return angleExtent > 0 ? -dist : dist; Point2D p1 = point(startAngle); double endAngle = startAngle + angleExtent; Point2D p2 = point(endAngle); boolean onLeft = (new StraightLine2D(p1, p2)).isInside(point); if (direct && !onLeft) return dist; if (!direct && onLeft) return -dist; Ray2D ray = new Ray2D(p1, -sin(startAngle), cos(startAngle)); boolean left1 = ray.isInside(point); if (direct && !left1) return dist; if (!direct && left1) return -dist; ray = new Ray2D(p2, -sin(endAngle), cos(endAngle)); boolean left2 = ray.isInside(point); if (direct && !left2) return dist; if (!direct && left2) return -dist; if (direct) return -dist; else return dist; }
/* * (non-Javadoc) * * @see math.geom2d.OrientedCurve2D#windingAngle(math.geom2d.Point2D) */ public double windingAngle(Point2D point) { Point2D p1 = point(0); Point2D p2 = point(abs(angleExtent)); // compute angle of point with extreme points double angle1 = Angle2D.horizontalAngle(point, p1); double angle2 = Angle2D.horizontalAngle(point, p2); // test on which 'side' of the arc the point lie boolean b1 = (new StraightLine2D(p1, p2)).isInside(point); boolean b2 = ellipse.isInside(point); if (angleExtent > 0) { if (b1 || b2) { // inside of ellipse arc if (angle2 > angle1) return angle2 - angle1; else return 2 * PI - angle1 + angle2; } else { // outside of ellipse arc if (angle2 > angle1) return angle2 - angle1 - 2 * PI; else return angle2 - angle1; } } else { if (!b1 || b2) { if (angle1 > angle2) return angle2 - angle1; else return angle2 - angle1 - 2 * PI; } else { if (angle1 > angle2) return angle2 - angle1 + 2 * PI; else return angle2 - angle1; } } }
/* * (non-Javadoc) * * @see math.geom2d.Curve2D#intersections(math.geom2d.LinearShape2D) */ public Collection<Point2D> intersections(LinearShape2D line) { // check point contained in it ArrayList<Point2D> array = new ArrayList<Point2D>(); for (Point2D point : ellipse.intersections(line)) if (contains(point)) array.add(point); return array; }
/* * (non-Javadoc) * * @see math.geom2d.Curve2D#position(math.geom2d.Point2D) */ public double position(Point2D point) { double angle = Angle2D.horizontalAngle(ellipse.center(), point); if (this.containsAngle(angle)) if (angleExtent > 0) return Angle2D.formatAngle(angle - startAngle); else return Angle2D.formatAngle(startAngle - angle); // If the point is not contained in the arc, return NaN. return Double.NaN; }
/* * (non-Javadoc) * * @see math.geom2d.Curve2D#point(double, math.geom2d.Point2D) */ public Point2D point(double t) { // check bounds t = max(t, 0); t = min(t, abs(angleExtent)); // convert position to angle if (angleExtent < 0) t = startAngle - t; else t = startAngle + t; // return corresponding point return ellipse.point(t); }
@Override public String toString() { Point2D center = ellipse.center(); return String.format( Locale.US, "EllipseArc2D(%7.2f,%7.2f,%7.2f,%7.2f,%7.5f,%7.5f,%7.5f)", center.x(), center.y(), ellipse.r1, ellipse.r2, ellipse.theta, startAngle, angleExtent); }
public double project(Point2D point) { double angle = ellipse.project(point); // Case of an angle contained in the ellipse arc if (this.containsAngle(angle)) { if (angleExtent > 0) return Angle2D.formatAngle(angle - startAngle); else return Angle2D.formatAngle(startAngle - angle); } // return either 0 or T1, depending on which extremity is closer. double d1 = this.firstPoint().distance(point); double d2 = this.lastPoint().distance(point); return d1 < d2 ? 0 : abs(angleExtent); }
/* * (non-Javadoc) * * @see math.geom2d.Shape2D#transform(math.geom2d.AffineTransform2D) */ public EllipseArc2D transform(AffineTransform2D trans) { // transform supporting ellipse Ellipse2D ell = ellipse.transform(trans); // ensure ellipse is direct if (!ell.isDirect()) ell = ell.reverse(); // Compute position of end points on the transformed ellipse double startPos = ell.project(this.firstPoint().transform(trans)); double endPos = ell.project(this.lastPoint().transform(trans)); // Compute the new arc boolean direct = !(angleExtent > 0 ^ trans.isDirect()); return new EllipseArc2D(ell, startPos, endPos, direct); }
public Box2D boundingBox() { // first get ending points Point2D p0 = firstPoint(); Point2D p1 = lastPoint(); // get coordinate of ending points double x0 = p0.x(); double y0 = p0.y(); double x1 = p1.x(); double y1 = p1.y(); // initialize min and max coords double xmin = min(x0, x1); double xmax = max(x0, x1); double ymin = min(y0, y1); double ymax = max(y0, y1); // precomputes some values Point2D center = ellipse.center(); double xc = center.x(); double yc = center.y(); double endAngle = startAngle + angleExtent; boolean direct = angleExtent >= 0; // check cases arc contains one maximum if (Angle2D.containsAngle(startAngle, endAngle, PI / 2 + ellipse.theta, direct)) ymax = max(ymax, yc + ellipse.r1); if (Angle2D.containsAngle(startAngle, endAngle, 3 * PI / 2 + ellipse.theta, direct)) ymin = min(ymin, yc - ellipse.r1); if (Angle2D.containsAngle(startAngle, endAngle, ellipse.theta, direct)) xmax = max(xmax, xc + ellipse.r2); if (Angle2D.containsAngle(startAngle, endAngle, PI + ellipse.theta, direct)) xmin = min(xmin, xc - ellipse.r2); // return a bounding with computed limits return new Box2D(xmin, xmax, ymin, ymax); }