/*
* $Id$
* This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc
*
* Copyright (c) 2000-2012 Stephane GALLAND.
* Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
* Universite de Technologie de Belfort-Montbeliard.
* Copyright (c) 2013-2016 The original authors, and other authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.arakhne.afc.math.continous.object2d;
import java.util.NoSuchElementException;
import org.arakhne.afc.math.MathConstants;
import org.arakhne.afc.math.generic.PathWindingRule;
import org.arakhne.afc.math.generic.Point2D;
import org.arakhne.afc.math.geometry.d2.afp.Circle2afp;
import org.arakhne.afc.math.geometry.d2.afp.Segment2afp;
import org.arakhne.afc.math.geometry.d2.d.Circle2d;
import org.arakhne.afc.math.matrix.Transform2D;
import org.arakhne.afc.vmutil.ReflectionUtil;
/** 2D circle with floating-point points.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @deprecated see {@link Circle2d}
*/
@Deprecated
@SuppressWarnings("all")
public class Circle2f extends AbstractShape2f<Circle2f> {
private static final long serialVersionUID = -5535463117356287850L;
/**
* ArcIterator.btan(Math.PI/2)
*/
static final float CTRL_VAL = 0.5522847498307933f;
/**
* ctrlpts contains the control points for a set of 4 cubic
* bezier curves that approximate a circle of radius 0.5
* centered at 0.5, 0.5
*/
static final float PCV = 0.5f + CTRL_VAL * 0.5f;
/**
* ctrlpts contains the control points for a set of 4 cubic
* bezier curves that approximate a circle of radius 0.5
* centered at 0.5, 0.5
*/
static final float NCV = 0.5f - CTRL_VAL * 0.5f;
/**
* ctrlpts contains the control points for a set of 4 cubic
* bezier curves that approximate a circle of radius 0.5
* centered at 0.5, 0.5
*/
static float CTRL_PTS[][] = {
{ 1.0f, PCV, PCV, 1.0f, 0.5f, 1.0f },
{ NCV, 1.0f, 0.0f, PCV, 0.0f, 0.5f },
{ 0.0f, NCV, NCV, 0.0f, 0.5f, 0.0f },
{ PCV, 0.0f, 1.0f, NCV, 1.0f, 0.5f }
};
/** Replies if a rectangle is inside in the circle.
*
* @param cx is the center of the circle.
* @param cy is the center of the circle.
* @param radius is the radius of the circle.
* @param rx is the lowest corner of the rectangle.
* @param ry is the lowest corner of the rectangle.
* @param rwidth is the width of the rectangle.
* @param rheight is the height of the rectangle.
* @return <code>true</code> if the given rectangle is inside the circle;
* otherwise <code>false</code>.
*/
public static boolean containsCircleRectangle(float cx, float cy, float radius, float rx, float ry, float rwidth, float rheight) {
float rcx = (rx + rwidth/2f);
float rcy = (ry + rheight/2f);
float farX;
if (cx<=rcx) farX = rx + rwidth;
else farX = rx;
float farY;
if (cy<=rcy) farY = ry + rheight;
else farY = ry;
return Circle2afp.containsCirclePoint(cx, cy, radius, farX, farY);
}
/** Replies if two circles are intersecting.
*
* @param x1 is the center of the first circle
* @param y1 is the center of the first circle
* @param radius1 is the radius of the first circle
* @param x2 is the center of the second circle
* @param y2 is the center of the second circle
* @param radius2 is the radius of the second circle
* @return <code>true</code> if the two shapes are intersecting; otherwise
* <code>false</code>
*/
public static boolean intersectsCircleCircle(float x1, float y1, float radius1, float x2, float y2, float radius2) {
float r = radius1+radius2;
return org.arakhne.afc.math.geometry.d2.Point2D.getDistanceSquaredPointPoint(x1, y1, x2, y2) < (r*r);
}
/** Replies if a circle and a rectangle are intersecting.
*
* @param x1 is the center of the circle
* @param y1 is the center of the circle
* @param radius is the radius of the circle
* @param x2 is the first corner of the rectangle.
* @param y2 is the first corner of the rectangle.
* @param x3 is the second corner of the rectangle.
* @param y3 is the second corner of the rectangle.
* @return <code>true</code> if the two shapes are intersecting; otherwise
* <code>false</code>
*/
public static boolean intersectsCircleRectangle(float x1, float y1, float radius, float x2, float y2, float x3, float y3) {
float dx;
if (x1<x2) {
dx = x2 - x1;
}
else if (x1>x3) {
dx = x1 - x3;
}
else {
dx = 0f;
}
float dy;
if (y1<y2) {
dy = y2 - y1;
}
else if (y1>y3) {
dy = y1 - y3;
}
else {
dy = 0f;
}
return (dx*dx+dy*dy) < (radius*radius);
}
/** Replies if a circle and a line are intersecting.
*
* @param x1 is the center of the circle
* @param y1 is the center of the circle
* @param radius is the radius of the circle
* @param x2 is the first point of the line.
* @param y2 is the first point of the line.
* @param x3 is the second point of the line.
* @param y3 is the second point of the line.
* @return <code>true</code> if the two shapes are intersecting; otherwise
* <code>false</code>
*/
public static boolean intersectsCircleLine(float x1, float y1, float radius, float x2, float y2, float x3, float y3) {
float d = (float) Segment2afp.calculatesDistanceLinePoint(x2, y2, x3, y3, x1, y1);
return d<(radius*radius);
}
/** Replies if a circle and a segment are intersecting.
*
* @param x1 is the center of the circle
* @param y1 is the center of the circle
* @param radius is the radius of the circle
* @param x2 is the first point of the segment.
* @param y2 is the first point of the segment.
* @param x3 is the second point of the segment.
* @param y3 is the second point of the segment.
* @return <code>true</code> if the two shapes are intersecting; otherwise
* <code>false</code>
*/
public static boolean intersectsCircleSegment(float x1, float y1, float radius, float x2, float y2, float x3, float y3) {
float d = (float) Segment2afp.calculatesDistanceSegmentPoint(x2, y2, x3, y3, x1, y1);
return d<(radius*radius);
}
/** X-coordinate of the circle center. */
protected float cx = 0f;
/** Y-coordinate of the circle center. */
protected float cy = 0f;
/** Radius of the circle center (must be always positive). */
protected float radius = 0f;
/**
*/
public Circle2f() {
//
}
/**
* @param center
* @param radius
*/
public Circle2f(Point2D center, float radius) {
set(center, radius);
}
/**
* @param x
* @param y
* @param radius
*/
public Circle2f(float x, float y, float radius) {
set(x, y, radius);
}
/** Construct a circle from a circle.
* @param c
*/
public Circle2f(Circle2f c) {
this.cx = c.cx;
this.cy = c.cy;
this.radius = c.radius;
}
@Override
public void clear() {
this.cx = this.cy = 0f;
this.radius = 0f;
}
/** Replies if the circle is empty.
* The circle is empty when the radius is nul.
*
* @return <code>true</code> if the radius is nul;
* otherwise <code>false</code>.
*/
@Override
public boolean isEmpty() {
return this.radius<=+0f;
}
/** Change the frame of the circle.
*
* @param x
* @param y
* @param radius
*/
public void set(float x, float y, float radius) {
this.cx = x;
this.cy = y;
this.radius = Math.abs(radius);
}
/** Change the frame of te circle.
*
* @param center
* @param radius
*/
public void set(Point2D center, float radius) {
this.cx = center.getX();
this.cy = center.getY();
this.radius = Math.abs(radius);
}
@Override
public void set(Shape2f s) {
Rectangle2f r = s.toBoundingBox();
this.cx = r.getCenterX();
this.cy = r.getCenterY();
this.radius = Math.min(r.getWidth(), r.getHeight()) / 2f;
}
/** Replies the center X.
*
* @return the center x.
*/
public float getX() {
return this.cx;
}
/** Replies the center y.
*
* @return the center y.
*/
public float getY() {
return this.cy;
}
/** Replies the center.
*
* @return a copy of the center.
*/
public Point2f getCenter() {
return new Point2f(this.cx, this.cy);
}
/** Replies the center.
*
* @param center
*/
public void setCenter(Point2D center) {
this.cx = center.getX();
this.cy = center.getY();
}
/** Replies the center.
*
* @param x
* @param y
*/
public void setCenter(float x, float y) {
this.cx = x;
this.cy = y;
}
/** Replies the radius.
*
* @return the radius.
*/
public float getRadius() {
return this.radius;
}
/** Set the radius.
*
* @param radius is the radius.
*/
public void setRadius(float radius) {
this.radius = Math.abs(radius);
}
/** {@inheritDoc}
*/
@Override
public Rectangle2f toBoundingBox() {
Rectangle2f r = new Rectangle2f();
r.setFromCorners(
this.cx-this.radius,
this.cy-this.radius,
this.cx+this.radius,
this.cy+this.radius);
return r;
}
/** {@inheritDoc}
*/
@Override
public void toBoundingBox(Rectangle2f box) {
box.setFromCorners(this.cx-this.radius,
this.cy-this.radius,
this.cx+this.radius,
this.cy+this.radius);
}
/** {@inheritDoc}
*/
@Override
public float distance(Point2D p) {
float d = (float) org.arakhne.afc.math.geometry.d2.Point2D.getDistancePointPoint(getX(), getY(), p.getX(), p.getY()) - getRadius();
return Math.max(0f, d);
}
/** {@inheritDoc}
*/
@Override
public float distanceSquared(Point2D p) {
float d = distance(p);
return d * d;
}
/** {@inheritDoc}
*/
@Override
public float distanceL1(Point2D p) {
Point2D r = getClosestPointTo(p);
return r.distanceL1(p);
}
/** {@inheritDoc}
*/
@Override
public float distanceLinf(Point2D p) {
Point2D r = getClosestPointTo(p);
return r.distanceLinf(p);
}
/** {@inheritDoc}
*/
@Override
public boolean contains(float x, float y) {
return Circle2afp.containsCirclePoint(getX(), getY(), getRadius(), x, y);
}
@Override
public boolean contains(Rectangle2f r) {
return containsCircleRectangle(getX(), getY(), getRadius(),
r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
}
/** {@inheritDoc}
*/
@Override
public Point2f getClosestPointTo(Point2D p) {
Vector2f v = new Vector2f(p);
v.sub(this.cx, this.cy);
float l = v.lengthSquared();
if (l<=(this.radius*this.radius)) {
if (p instanceof Point2f) return (Point2f)p;
return new Point2f(p);
}
float s = this.radius/(float)Math.sqrt(l);
v.scale(s);
return new Point2f(this.cx + v.getX(), this.cy + v.getY());
}
@Override
public void translate(float dx, float dy) {
this.cx += dx;
this.cy += dy;
}
@Override
public PathIterator2f getPathIterator(Transform2D transform) {
if (transform==null)
return new CopyPathIterator(getX(), getY(), getRadius());
return new TransformPathIterator(getX(), getY(), getRadius(), transform);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Circle2f) {
Circle2f rr2d = (Circle2f) obj;
return ((getX() == rr2d.getX()) &&
(getY() == rr2d.getY()) &&
(getRadius() == rr2d.getRadius()));
}
return false;
}
@Override
public int hashCode() {
long bits = 1L;
bits = 31L * bits + floatToIntBits(getX());
bits = 31L * bits + floatToIntBits(getY());
bits = 31L * bits + floatToIntBits(getRadius());
return (int) (bits ^ (bits >> 32));
}
@Override
public boolean intersects(Rectangle2f s) {
return intersectsCircleRectangle(
getX(), getY(), getRadius(),
s.getMinX(), s.getMinY(),
s.getMaxX(), s.getMaxY());
}
@Override
public boolean intersects(Ellipse2f s) {
return Ellipse2f.intersectsEllipseEllipse(
getX()-getRadius(), getY()-getRadius(),
getX()+getRadius(), getY()+getRadius(),
s.getMinX(), s.getMinY(),
s.getMaxX(), s.getMaxY());
}
@Override
public boolean intersects(Circle2f s) {
return intersectsCircleCircle(
getX(), getY(), getRadius(),
s.getX(), s.getY(), s.getRadius());
}
@Override
public boolean intersects(Segment2f s) {
return intersectsCircleSegment(
getX(), getY(), getRadius(),
s.getX1(), s.getY1(),
s.getX2(), s.getY2());
}
@Override
public boolean intersects(Path2f s) {
return intersects(s.getPathIterator((float) MathConstants.SPLINE_APPROXIMATION_RATIO));
}
@Override
public boolean intersects(PathIterator2f s) {
int mask = (s.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 2);
int crossings = Path2f.computeCrossingsFromCircle(
0,
s,
getX(), getY(), getRadius(),
false, true);
return (crossings == MathConstants.SHAPE_INTERSECTS ||
(crossings & mask) != 0);
}
@Override
public String toString() {
return ReflectionUtil.toString(this);
}
/** Iterator on the path elements of the circle.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private static class CopyPathIterator implements PathIterator2f {
private final float x;
private final float y;
private final float r;
private int index = 0;
private float movex, movey;
private float lastx, lasty;
/**
* @param x
* @param y
* @param r
*/
public CopyPathIterator(float x, float y, float r) {
this.r = Math.max(0f, r);
this.x = x - this.r;
this.y = y - this.r;
if (this.r<=0f) {
this.index = 6;
}
}
@Override
public boolean hasNext() {
return this.index<=5;
}
@Override
public PathElement2f next() {
if (this.index>5) throw new NoSuchElementException();
int idx = this.index;
++this.index;
if (idx==0) {
float dr = 2f * this.r;
float ctrls[] = CTRL_PTS[3];
this.movex = (this.x + ctrls[4] * dr);
this.movey = (this.y + ctrls[5] * dr);
this.lastx = this.movex;
this.lasty = this.movey;
return new PathElement2f.MovePathElement2f(
this.lastx, this.lasty);
}
else if (idx<5) {
float dr = 2f * this.r;
float ctrls[] = CTRL_PTS[idx - 1];
float ppx = this.lastx;
float ppy = this.lasty;
this.lastx = (this.x + ctrls[4] * dr);
this.lasty = (this.y + ctrls[5] * dr);
return new PathElement2f.CurvePathElement2f(
ppx, ppy,
(this.x + ctrls[0] * dr),
(this.y + ctrls[1] * dr),
(this.x + ctrls[2] * dr),
(this.y + ctrls[3] * dr),
this.lastx, this.lasty);
}
float ppx = this.lastx;
float ppy = this.lasty;
this.lastx = this.movex;
this.lasty = this.movey;
return new PathElement2f.ClosePathElement2f(
ppx, ppy,
this.lastx, this.lasty);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public PathWindingRule getWindingRule() {
return PathWindingRule.NON_ZERO;
}
@Override
public boolean isPolyline() {
return false;
}
}
/** Iterator on the path elements of the circle.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private static class TransformPathIterator implements PathIterator2f {
private final Point2D p1 = new Point2f();
private final Point2D p2 = new Point2f();
private final Point2D ptmp1 = new Point2f();
private final Point2D ptmp2 = new Point2f();
private final Transform2D transform;
private final float x;
private final float y;
private final float r;
private int index = 0;
private float movex, movey;
/**
* @param x
* @param y
* @param r
* @param transform
*/
public TransformPathIterator(float x, float y, float r, Transform2D transform) {
assert(transform!=null);
this.transform = transform;
this.r = Math.max(0f, r);
this.x = x - this.r;
this.y = y - this.r;
if (this.r<=0f) {
this.index = 6;
}
}
@Override
public boolean hasNext() {
return this.index<=5;
}
@Override
public PathElement2f next() {
if (this.index>5) throw new NoSuchElementException();
int idx = this.index;
++this.index;
if (idx==0) {
float dr = 2f * this.r;
float ctrls[] = CTRL_PTS[3];
this.movex = (this.x + ctrls[4] * dr);
this.movey = (this.y + ctrls[5] * dr);
this.p2.set(this.movex, this.movey);
this.transform.transform(this.p2);
return new PathElement2f.MovePathElement2f(
this.p2.getX(), this.p2.getY());
}
else if (idx<5) {
float dr = 2f * this.r;
float ctrls[] = CTRL_PTS[idx - 1];
this.p1.set(this.p2);
this.p2.set(
(this.x + ctrls[4] * dr),
(this.y + ctrls[5] * dr));
this.transform.transform(this.p2);
this.ptmp1.set(
(this.x + ctrls[0] * dr),
(this.y + ctrls[1] * dr));
this.transform.transform(this.ptmp1);
this.ptmp2.set(
(this.x + ctrls[2] * dr),
(this.y + ctrls[3] * dr));
this.transform.transform(this.ptmp2);
return new PathElement2f.CurvePathElement2f(
this.p1.getX(), this.p1.getY(),
this.ptmp1.getX(), this.ptmp1.getY(),
this.ptmp2.getX(), this.ptmp2.getY(),
this.p2.getX(), this.p2.getY());
}
this.p1.set(this.p2);
this.p2.set(this.movex, this.movey);
this.transform.transform(this.p2);
return new PathElement2f.ClosePathElement2f(
this.p1.getX(), this.p1.getY(),
this.p2.getX(), this.p2.getY());
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public PathWindingRule getWindingRule() {
return PathWindingRule.NON_ZERO;
}
@Override
public boolean isPolyline() {
return false;
}
}
}