/*
* $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.MathUtil;
import org.arakhne.afc.math.generic.PathWindingRule;
import org.arakhne.afc.math.generic.Point2D;
import org.arakhne.afc.math.geometry.d2.afp.Segment2afp;
import org.arakhne.afc.math.geometry.d2.d.Segment2d;
import org.arakhne.afc.math.matrix.Transform2D;
import org.arakhne.afc.vmutil.ReflectionUtil;
/** 2D line segment with floating-point coordinates.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @deprecated see {@link Segment2d}
*/
@Deprecated
@SuppressWarnings("all")
public class Segment2f extends AbstractShape2f<Segment2f> {
private static final long serialVersionUID = -82425036308183925L;
/**
* Calculates the number of times the line from (x0,y0) to (x1,y1)
* crosses the ray extending to the right from (px,py).
* If the point lies on the line, then no crossings are recorded.
* +1 is returned for a crossing where the Y coordinate is increasing
* -1 is returned for a crossing where the Y coordinate is decreasing
*
* @param px is the reference point to test.
* @param py is the reference point to test.
* @param x0 is the first point of the line.
* @param y0 is the first point of the line.
* @param x1 is the second point of the line.
* @param y1 is the secondpoint of the line.
* @return the crossing.
*/
public static int computeCrossingsFromPoint(
float px, float py,
float x0, float y0,
float x1, float y1) {
// Copied from AWT API
if (py < y0 && py < y1) return 0;
if (py >= y0 && py >= y1) return 0;
// assert(y0 != y1);
if (px >= x0 && px >= x1) return 0;
if (px < x0 && px < x1) return (y0 < y1) ? 1 : -1;
float xintercept = x0 + (py - y0) * (x1 - x0) / (y1 - y0);
if (px >= xintercept) return 0;
return (y0 < y1) ? 1 : -1;
}
/**
* Calculates the number of times the line from (x0,y0) to (x1,y1)
* crosses the ray extending to the right from (px,py).
* If the point lies on the line, then no crossings are recorded.
* +1 is returned for a crossing where the Y coordinate is increasing
* -1 is returned for a crossing where the Y coordinate is decreasing
* <p>
* This function differs from {@link #computeCrossingsFromPoint(float, float, float, float, float, float)}.
* The equality test is not used in this function.
*
* @param px is the reference point to test.
* @param py is the reference point to test.
* @param x0 is the first point of the line.
* @param y0 is the first point of the line.
* @param x1 is the second point of the line.
* @param y1 is the secondpoint of the line.
* @return the crossing.
*/
private static int computeCrossingsFromPoint1(
float px, float py,
float x0, float y0,
float x1, float y1) {
// Copied from AWT API
if (py < y0 && py < y1) return 0;
if (py > y0 && py > y1) return 0;
// assert(y0 != y1);
if (px > x0 && px > x1) return 0;
if (px < x0 && px < x1) return (y0 < y1) ? 1 : -1;
float xintercept = x0 + (py - y0) * (x1 - x0) / (y1 - y0);
if (px > xintercept) return 0;
return (y0 < y1) ? 1 : -1;
}
/**
* Calculates the number of times the line from (x0,y0) to (x1,y1)
* crosses the segment (sx0,sy0) to (sx1,sy1) extending to the right.
*
* @param crossings is the initial value for the number of crossings.
* @param sx1 is the first point of the segment to extend.
* @param sy1 is the first point of the segment to extend.
* @param sx2 is the second point of the segment to extend.
* @param sy2 is the second point of the segment to extend.
* @param x0 is the first point of the line.
* @param y0 is the first point of the line.
* @param x1 is the second point of the line.
* @param y1 is the secondpoint of the line.
* @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}
*/
public static int computeCrossingsFromSegment(
int crossings,
float sx1, float sy1,
float sx2, float sy2,
float x0, float y0,
float x1, float y1) {
int numCrosses = crossings;
float xmin = Math.min(sx1, sx2);
float xmax = Math.max(sx1, sx2);
float ymin = Math.min(sy1, sy2);
float ymax = Math.max(sy1, sy2);
if (y0<=ymin && y1<=ymin) return numCrosses;
if (y0>=ymax && y1>=ymax) return numCrosses;
if (x0<=xmin && x1<=xmin) return numCrosses;
if (x0>=xmax && x1>=xmax) {
// The line is entirely at the right of the shadow
if (y0<y1) {
if (y0<=ymin) ++numCrosses;
if (y1>=ymax) ++numCrosses;
}
else {
if (y1<=ymin) --numCrosses;
if (y0>=ymax) --numCrosses;
}
}
else if (intersectsSegmentSegmentWithoutEnds(x0, y0, x1, y1, sx1, sy1, sx2, sy2)) {
return MathConstants.SHAPE_INTERSECTS;
}
else {
int side1, side2;
if (sy1<=sy2) {
side1 = Segment2afp.findsSideLinePoint(sx1, sy1, sx2, sy2, x0, y0, 0);
side2 = Segment2afp.findsSideLinePoint(sx1, sy1, sx2, sy2, x1, y1, 0);
}
else {
side1 = Segment2afp.findsSideLinePoint(sx2, sy2, sx1, sy1, x0, y0, 0);
side2 = Segment2afp.findsSideLinePoint(sx2, sy2, sx1, sy1, x1, y1, 0);
}
if (side1>0 || side2>0) {
int n1 = computeCrossingsFromPoint(sx1, sy1, x0, y0, x1, y1);
int n2;
if (n1!=0) {
n2 = computeCrossingsFromPoint1(sx2, sy2, x0, y0, x1, y1);
}
else {
n2 = computeCrossingsFromPoint(sx2, sy2, x0, y0, x1, y1);
}
numCrosses += n1;
numCrosses += n2;
}
}
return numCrosses;
}
/**
* Calculates the number of times the line from (x0,y0) to (x1,y1)
* crosses the ellipse (ex0,ey0) to (ex1,ey1) extending to the right.
*
* @param crossings is the initial value for the number of crossings.
* @param ex is the first corner of the ellipse to extend.
* @param ey is the first corner of the ellipse to extend.
* @param ew is the width of the ellipse to extend.
* @param eh is the height of the ellipse to extend.
* @param x0 is the first point of the line.
* @param y0 is the first point of the line.
* @param x1 is the second point of the line.
* @param y1 is the secondpoint of the line.
* @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}.
*/
public static int computeCrossingsFromEllipse(
int crossings,
float ex, float ey,
float ew, float eh,
float x0, float y0,
float x1, float y1) {
int numCrosses = crossings;
float xmin = ex;
float ymin = ey;
float xmax = ex + ew;
float ymax = ey + eh;
if (y0<=ymin && y1<=ymin) return numCrosses;
if (y0>=ymax && y1>=ymax) return numCrosses;
if (x0<=xmin && x1<=xmin) return numCrosses;
if (x0>=xmax && x1>=xmax) {
// The line is entirely at the right of the shadow
if (y0<y1) {
if (y0<=ymin) ++numCrosses;
if (y1>=ymax) ++numCrosses;
}
else {
if (y1<=ymin) --numCrosses;
if (y0>=ymax) --numCrosses;
}
}
else if (Ellipse2f.intersectsEllipseSegment(
xmin, ymin, xmax-xmin, ymax-ymin,
x0, y0, x1, y1)) {
return MathConstants.SHAPE_INTERSECTS;
}
else {
float xcenter = (xmin+xmax)/2f;
numCrosses += computeCrossingsFromPoint(xcenter, ymin, x0, y0, x1, y1);
numCrosses += computeCrossingsFromPoint(xcenter, ymax, x0, y0, x1, y1);
}
return numCrosses;
}
/**
* Calculates the number of times the line from (x0,y0) to (x1,y1)
* crosses the ellipse (ex0,ey0) to (ex1,ey1) extending to the right.
*
* @param crossings is the initial value for the number of crossings.
* @param cx is the center of the circle to extend.
* @param cy is the center of the circle to extend.
* @param radius is the radius of the circle to extend.
* @param x0 is the first point of the line.
* @param y0 is the first point of the line.
* @param x1 is the second point of the line.
* @param y1 is the secondpoint of the line.
* @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}.
*/
public static int computeCrossingsFromCircle(
int crossings,
float cx, float cy,
float radius,
float x0, float y0,
float x1, float y1) {
int numCrosses = crossings;
float xmin = cx - Math.abs(radius);
float ymin = cy - Math.abs(radius);
float ymax = cy + Math.abs(radius);
if (y0<=ymin && y1<=ymin) return numCrosses;
if (y0>=ymax && y1>=ymax) return numCrosses;
if (x0<=xmin && x1<=xmin) return numCrosses;
if (x0>=cx+radius && x1>=cx+radius) {
// The line is entirely at the right of the shadow
if (y0<y1) {
if (y0<=ymin) ++numCrosses;
if (y1>=ymax) ++numCrosses;
}
else {
if (y1<=ymin) --numCrosses;
if (y0>=ymax) --numCrosses;
}
}
else if (Circle2f.intersectsCircleSegment(
cx, cy, radius,
x0, y0, x1, y1)) {
return MathConstants.SHAPE_INTERSECTS;
}
else {
numCrosses += computeCrossingsFromPoint(cx, ymin, x0, y0, x1, y1);
numCrosses += computeCrossingsFromPoint(cx, ymax, x0, y0, x1, y1);
}
return numCrosses;
}
/**
* Accumulate the number of times the line crosses the shadow
* extending to the right of the rectangle. See the comment
* for the {@link MathConstants#SHAPE_INTERSECTS} constant for more complete details.
*
* @param crossings is the initial value for the number of crossings.
* @param rxmin is the first corner of the rectangle.
* @param rymin is the first corner of the rectangle.
* @param rxmax is the second corner of the rectangle.
* @param rymax is the second corner of the rectangle.
* @param x0 is the first point of the line.
* @param y0 is the first point of the line.
* @param x1 is the second point of the line.
* @param y1 is the secondpoint of the line.
* @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}.
*/
public static int computeCrossingsFromRect(
int crossings,
float rxmin, float rymin,
float rxmax, float rymax,
float x0, float y0,
float x1, float y1) {
int numCrosses = crossings;
if (y0 >= rymax && y1 >= rymax) return numCrosses;
if (y0 <= rymin && y1 <= rymin) return numCrosses;
if (x0 <= rxmin && x1 <= rxmin) return numCrosses;
if (x0 >= rxmax && x1 >= rxmax) {
// Line is entirely to the right of the rect
// and the vertical ranges of the two overlap by a non-empty amount
// Thus, this line segment is partially in the "right-shadow"
// Path may have done a complete crossing
// Or path may have entered or exited the right-shadow
if (y0 < y1) {
// y-increasing line segment...
// We know that y0 < rymax and y1 > rymin
if (y0 <= rymin) ++numCrosses;
if (y1 >= rymax) ++numCrosses;
}
else if (y1 < y0) {
// y-decreasing line segment...
// We know that y1 < rymax and y0 > rymin
if (y1 <= rymin) --numCrosses;
if (y0 >= rymax) --numCrosses;
}
return numCrosses;
}
// Remaining case:
// Both x and y ranges overlap by a non-empty amount
// First do trivial INTERSECTS rejection of the cases
// where one of the endpoints is inside the rectangle.
if ((x0 > rxmin && x0 < rxmax && y0 > rymin && y0 < rymax) ||
(x1 > rxmin && x1 < rxmax && y1 > rymin && y1 < rymax)) {
return MathConstants.SHAPE_INTERSECTS;
}
// Otherwise calculate the y intercepts and see where
// they fall with respect to the rectangle
float xi0 = x0;
if (y0 < rymin) {
xi0 += ((rymin - y0) * (x1 - x0) / (y1 - y0));
}
else if (y0 > rymax) {
xi0 += ((rymax - y0) * (x1 - x0) / (y1 - y0));
}
float xi1 = x1;
if (y1 < rymin) {
xi1 += ((rymin - y1) * (x0 - x1) / (y0 - y1));
}
else if (y1 > rymax) {
xi1 += ((rymax - y1) * (x0 - x1) / (y0 - y1));
}
if (xi0 <= rxmin && xi1 <= rxmin) return numCrosses;
if (xi0 >= rxmax && xi1 >= rxmax) {
if (y0 < y1) {
// y-increasing line segment...
// We know that y0 < rymax and y1 > rymin
if (y0 <= rymin) ++numCrosses;
if (y1 >= rymax) ++numCrosses;
}
else if (y1 < y0) {
// y-decreasing line segment...
// We know that y1 < rymax and y0 > rymin
if (y1 <= rymin) --numCrosses;
if (y0 >= rymax) --numCrosses;
}
return numCrosses;
}
return MathConstants.SHAPE_INTERSECTS;
}
/** Replies if two lines are intersecting.
*
* @param x1 is the first point of the first line.
* @param y1 is the first point of the first line.
* @param x2 is the second point of the first line.
* @param y2 is the second point of the first line.
* @param x3 is the first point of the second line.
* @param y3 is the first point of the second line.
* @param x4 is the second point of the second line.
* @param y4 is the second point of the second line.
* @return <code>true</code> if the two shapes are intersecting; otherwise
* <code>false</code>
*/
public static boolean intersectsLineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
if (Segment2afp.isParallelLines(x1, y1, x2, y2, x3, y3, x4, y4)) {
return org.arakhne.afc.math.geometry.d2.Point2D.isCollinearPoints(x1, y1, x2, y2, x3, y3);
}
return true;
}
/** Replies if a segment and a line are intersecting.
*
* @param x1 is the first point of the first segment.
* @param y1 is the first point of the first segment.
* @param x2 is the second point of the first segment.
* @param y2 is the second point of the first segment.
* @param x3 is the first point of the second line.
* @param y3 is the first point of the second line.
* @param x4 is the second point of the second line.
* @param y4 is the second point of the second line.
* @return <code>true</code> if the two shapes are intersecting; otherwise
* <code>false</code>
*/
public static boolean intersectsSegmentLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
return (Segment2afp.findsSideLinePoint(x3, y3, x4, y4, x1, y1, 0) *
Segment2afp.findsSideLinePoint(x3, y3, x4, y4, x2, y2, 0)) <= 0;
}
private static boolean intersectsSSWE(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
float vx1, vy1, vx2a, vy2a, vx2b, vy2b, f1, f2, sign;
vx1 = x2 - x1;
vy1 = y2 - y1;
// Based on CCW. It is different than MathUtil.ccw(); because
// this small algorithm is computing a ccw of 0 for colinear points.
vx2a = x3 - x1;
vy2a = y3 - y1;
f1 = vx2a * vy1 - vy2a * vx1;
vx2b = x4 - x1;
vy2b = y4 - y1;
f2 = vx2b * vy1 - vy2b * vx1;
// s = f1 * f2
//
// f1 f2 s intersect
// -1 -1 1 F
// -1 0 0 ON SEGMENT?
// -1 1 -1 T
// 0 -1 0 ON SEGMENT?
// 0 0 0 SEGMENT INTERSECTION?
// 0 1 1 ON SEGMENT?
// 1 -1 -1 T
// 1 0 0 ON SEGMENT?
// 1 1 1 F
sign = f1 * f2;
if (sign<0f) return true;
if (sign>0f) return false;
float squaredLength = vx1*vx1 + vy1*vy1;
if (f1==0f && f2==0f) {
// Projection of the point on the segment line:
// <0 -> means before first point
// >1 -> means after second point
// otherwhise on the segment.
f1 = (vx2a * vx1 + vy2a * vy1) / squaredLength;
f2 = (vx2b * vx1 + vy2b * vy1) / squaredLength;
// No intersection when:
// f1<0 && f2<0 or
// f1>1 && f2>1
return (f1>=0f || f2>=0) && (f1<=1f || f2<=1f);
}
if (f1==0f) {
// Projection of the point on the segment line:
// <0 -> means before first point
// >1 -> means after second point
// otherwhise on the segment.
f1 = (vx2a * vx1 + vy2a * vy1) / squaredLength;
// No intersection when:
// f1<=0 && f2<=0 or
// f1>=1 && f2>=1
return (f1>=0f && f1<=1f);
}
if (f2==0f) {
// Projection of the point on the segment line:
// <0 -> means before first point
// >1 -> means after second point
// otherwhise on the segment.
f2 = (vx2b * vx1 + vy2b * vy1) / squaredLength;
// No intersection when:
// f1<0 && f2<0 or
// f1>1 && f2>1
return (f2>=0 && f2<=1f);
}
return false;
}
private static boolean intersectsSSWoE(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
float vx1, vy1, vx2a, vy2a, vx2b, vy2b, f1, f2, sign;
vx1 = x2 - x1;
vy1 = y2 - y1;
vx2a = x3 - x1;
vy2a = y3 - y1;
f1 = vx2a * vy1 - vy2a * vx1;
vx2b = x4 - x1;
vy2b = y4 - y1;
f2 = vx2b * vy1 - vy2b * vx1;
// s = f1 * f2
//
// f1 f2 s intersect
// -1 -1 1 F
// -1 0 0 F
// -1 1 -1 T
// 0 -1 0 F
// 0 0 0 SEGMENT INTERSECTION?
// 0 1 1 F
// 1 -1 -1 T
// 1 0 0 F
// 1 1 1 F
sign = f1 * f2;
if (sign<0f) return true;
if (sign>0f) return false;
if (f1==0f && f2==0f) {
// Projection of the point on the segment line:
// <0 -> means before first point
// >1 -> means after second point
// otherwhise on the segment.
float squaredLength = vx1*vx1 + vy1*vy1;
f1 = (vx2a * vx1 + vy2a * vy1) / squaredLength;
f2 = (vx2b * vx1 + vy2b * vy1) / squaredLength;
// No intersection when:
// f1<=0 && f2<=0 or
// f1>=1 && f2>=1
return (f1>0f || f2>0) && (f1<1f || f2<1f);
}
return false;
}
/** Replies if two segments are intersecting.
* This function considers that the ends of
* the segments are not intersecting.
* To include the ends of the segments in the intersection ranges, see
* {@link #intersectsSegmentSegmentWithEnds(float, float, float, float, float, float, float, float)}.
*
* @param x1 is the first point of the first segment.
* @param y1 is the first point of the first segment.
* @param x2 is the second point of the first segment.
* @param y2 is the second point of the first segment.
* @param x3 is the first point of the second segment.
* @param y3 is the first point of the second segment.
* @param x4 is the second point of the second segment.
* @param y4 is the second point of the second segment.
* @return <code>true</code> if the two shapes are intersecting; otherwise
* <code>false</code>
* @see #intersectsSegmentSegmentWithEnds(float, float, float, float, float, float, float, float)
*/
public static boolean intersectsSegmentSegmentWithoutEnds(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
boolean r;
r = intersectsSSWoE(x1, y1, x2, y2, x3, y3, x4, y4);
if (!r) return r;
return intersectsSSWoE(x3, y3, x4, y4, x1, y1, x2, y2);
}
/** Replies if two segments are intersecting.
* This function considers that the ends of
* the segments are intersecting.
* To ignore the ends of the segments, see
* {@link #intersectsSegmentSegmentWithoutEnds(float, float, float, float, float, float, float, float)}.
*
* @param x1 is the first point of the first segment.
* @param y1 is the first point of the first segment.
* @param x2 is the second point of the first segment.
* @param y2 is the second point of the first segment.
* @param x3 is the first point of the second segment.
* @param y3 is the first point of the second segment.
* @param x4 is the second point of the second segment.
* @param y4 is the second point of the second segment.
* @return <code>true</code> if the two shapes are intersecting; otherwise
* <code>false</code>
* @see #intersectsSegmentSegmentWithoutEnds(float, float, float, float, float, float, float, float)
*/
public static boolean intersectsSegmentSegmentWithEnds(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
boolean r;
r = intersectsSSWE(x1, y1, x2, y2, x3, y3, x4, y4);
if (!r) return r;
return intersectsSSWE(x3, y3, x4, y4, x1, y1, x2, y2);
}
/** X-coordinate of the first point. */
protected float ax = 0f;
/** Y-coordinate of the first point. */
protected float ay = 0f;
/** X-coordinate of the second point. */
protected float bx = 0f;
/** Y-coordinate of the second point. */
protected float by = 0f;
/**
*/
public Segment2f() {
//
}
/**
* @param a
* @param b
*/
public Segment2f(Point2D a, Point2D b) {
set(a, b);
}
/**
* @param s
*/
public Segment2f(Segment2f s) {
this.ax = s.ax;
this.ay = s.ay;
this.bx = s.bx;
this.by = s.by;
}
/**
* @param x1
* @param y1
* @param x2
* @param y2
*/
public Segment2f(float x1, float y1, float x2, float y2) {
set(x1, y1, x2, y2);
}
@Override
public void clear() {
this.ax = this.ay = this.bx = this.by = 0f;
}
/**
* Replies if this segment is empty.
* The segment is empty when the two
* points are equal.
*
* @return <code>true</code> if the two points are
* equal.
*/
@Override
public boolean isEmpty() {
return this.ax==this.bx && this.ay==this.by;
}
/** Change the line.
*
* @param x1
* @param y1
* @param x2
* @param y2
*/
public void set(float x1, float y1, float x2, float y2) {
this.ax = x1;
this.ay = y1;
this.bx = x2;
this.by = y2;
}
/** Change the line.
*
* @param a
* @param b
*/
public void set(Point2D a, Point2D b) {
this.ax = a.getX();
this.ay = a.getY();
this.bx = b.getX();
this.by = b.getY();
}
@Override
public void set(Shape2f s) {
Rectangle2f r = s.toBoundingBox();
this.ax = r.getMinX();
this.ay = r.getMinY();
this.bx = r.getMaxX();
this.by = r.getMaxY();
}
/** Replies the X of the first point.
*
* @return the x of the first point.
*/
public float getX1() {
return this.ax;
}
/** Replies the Y of the first point.
*
* @return the y of the first point.
*/
public float getY1() {
return this.ay;
}
/** Replies the X of the second point.
*
* @return the x of the second point.
*/
public float getX2() {
return this.bx;
}
/** Replies the Y of the second point.
*
* @return the y of the second point.
*/
public float getY2() {
return this.by;
}
/** Replies the first point.
*
* @return the first point.
*/
public Point2D getP1() {
return new Point2f(this.ax, this.ay);
}
/** Replies the second point.
*
* @return the second point.
*/
public Point2D getP2() {
return new Point2f(this.bx, this.by);
}
/** {@inheritDoc}
*/
@Override
public Rectangle2f toBoundingBox() {
Rectangle2f r = new Rectangle2f();
r.setFromCorners(
this.ax,
this.ay,
this.bx,
this.by);
return r;
}
/** {@inheritDoc}
*/
@Override
public void toBoundingBox(Rectangle2f box) {
box.setFromCorners(
this.ax,
this.ay,
this.bx,
this.by);
}
/** {@inheritDoc}
*/
@Override
public float distanceSquared(Point2D p) {
return (float) Segment2afp.calculatesDistanceSquaredSegmentPoint(
this.ax, this.ay,
this.bx, this.by,
p.getX(), p.getY());
}
/** {@inheritDoc}
*/
@Override
public float distanceL1(Point2D p) {
float ratio = (float) Segment2afp.findsProjectedPointPointLine(p.getX(), p.getY(), this.ax, this.ay, this.bx, this.by);
ratio = MathUtil.clamp(ratio, 0f, 1f);
Vector2f v = new Vector2f(this.bx, this.by);
v.sub(this.ax, this.ay);
v.scale(ratio);
return Math.abs(this.ax + v.getX() - p.getX())
+ Math.abs(this.ay + v.getY() - p.getY());
}
/** {@inheritDoc}
*/
@Override
public float distanceLinf(Point2D p) {
float ratio = (float) Segment2afp.findsProjectedPointPointLine(p.getX(), p.getY(), this.ax, this.ay, this.bx, this.by);
ratio = MathUtil.clamp(ratio, 0f, 1f);
Vector2f v = new Vector2f(this.bx, this.by);
v.sub(this.ax, this.ay);
v.scale(ratio);
return Math.max(
Math.abs(this.ax + v.getX() - p.getX()),
Math.abs(this.ay + v.getY() - p.getY()));
}
/** {@inheritDoc}
* <p>
* This function uses the equal-to-zero test with the error EPSILON.
*
* @see MathUtil#isEpsilonZero(double)
*/
@Override
public boolean contains(float x, float y) {
return MathUtil.isEpsilonZero(Segment2afp.calculatesDistanceSquaredSegmentPoint(
this.ax, this.ay,
this.bx, this.by,
x, y));
}
/** {@inheritDoc}
*/
@Override
public boolean contains(Rectangle2f r) {
return contains(r.getMinX(), r.getMinY())
&& contains(r.getMaxX(), r.getMaxY());
}
/** {@inheritDoc}
*/
@Override
public Point2f getClosestPointTo(Point2D p) {
float ratio = (float) Segment2afp.findsProjectedPointPointLine(p.getX(), p.getY(),
this.ax, this.ay,
this.bx, this.by);
if (ratio<=0f) return new Point2f(this.ax, this.ay);
if (ratio>=1f) return new Point2f(this.bx, this.by);
return new Point2f(
this.ax + (this.bx - this.ax) * ratio,
this.ay + (this.by - this.ay) * ratio);
}
@Override
public void translate(float dx, float dy) {
this.ax += dx;
this.ay += dy;
this.bx += dx;
this.by += dy;
}
@Override
public PathIterator2f getPathIterator(Transform2D transform) {
return new SegmentPathIterator(
getX1(), getY1(), getX2(), getY2(),
transform);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Segment2f) {
Segment2f rr2d = (Segment2f) obj;
return ((getX1() == rr2d.getX1()) &&
(getY1() == rr2d.getY1()) &&
(getX2() == rr2d.getX2()) &&
(getY2() == rr2d.getY2()));
}
return false;
}
@Override
public int hashCode() {
long bits = 1L;
bits = 31L * bits + floatToIntBits(getX1());
bits = 31L * bits + floatToIntBits(getY1());
bits = 31L * bits + floatToIntBits(getX2());
bits = 31L * bits + floatToIntBits(getY2());
return (int) (bits ^ (bits >> 32));
}
/** Transform the current segment.
* This function changes the current segment.
*
* @param transform is the affine transformation to apply.
* @see #createTransformedShape(Transform2D)
*/
public void transform(Transform2D transform) {
Point2f p = new Point2f(this.ax, this.ay);
transform.transform(p);
this.ax = p.getX();
this.ay = p.getY();
p.set(this.bx, this.by);
transform.transform(p);
this.bx = p.getX();
this.by = p.getY();
}
@Override
public Shape2f createTransformedShape(Transform2D transform) {
Point2D p1 = transform.transform(this.ax, this.ay);
Point2D p2 = transform.transform(this.bx, this.by);
return new Segment2f(p1, p2);
}
@Override
public boolean intersects(Rectangle2f s) {
return Rectangle2f.intersectsRectangleSegment(
s.getMinX(), s.getMinY(),
s.getMaxX(), s.getMaxY(),
getX1(), getY1(),
getX2(), getY2());
}
@Override
public boolean intersects(Ellipse2f s) {
return Ellipse2f.intersectsEllipseSegment(
s.getMinX(), s.getMinY(),
s.getWidth(), s.getHeight(),
getX1(), getY1(),
getX2(), getY2());
}
@Override
public boolean intersects(Circle2f s) {
return Circle2f.intersectsCircleSegment(
s.getX(), s.getY(),
s.getRadius(),
getX1(), getY1(),
getX2(), getY2());
}
@Override
public boolean intersects(Segment2f s) {
return intersectsSegmentSegmentWithoutEnds(
getX1(), getY1(),
getX2(), getY2(),
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.computeCrossingsFromSegment(
0,
s,
getX1(), getY1(), getX2(), getY2(),
false);
return (crossings == MathConstants.SHAPE_INTERSECTS ||
(crossings & mask) != 0);
}
@Override
public String toString() {
return ReflectionUtil.toString(this);
}
/** Iterator on the path elements of the segment.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private static class SegmentPathIterator implements PathIterator2f {
private final Point2D p1 = new Point2f();
private final Point2D p2 = new Point2f();
private final Transform2D transform;
private final float x1;
private final float y1;
private final float x2;
private final float y2;
private int index = 0;
/**
* @param x1
* @param y1
* @param x2
* @param y2
* @param transform
*/
public SegmentPathIterator(float x1, float y1, float x2, float y2, Transform2D transform) {
this.transform = transform;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
if (this.x1==this.x2 && this.y1==this.y2) {
this.index = 2;
}
}
@Override
public boolean hasNext() {
return this.index<=1;
}
@Override
public PathElement2f next() {
if (this.index>1) throw new NoSuchElementException();
int idx = this.index;
++this.index;
switch(idx) {
case 0:
this.p2.set(this.x1, this.y1);
if (this.transform!=null) {
this.transform.transform(this.p2);
}
return new PathElement2f.MovePathElement2f(
this.p2.getX(), this.p2.getY());
case 1:
this.p1.set(this.p2);
this.p2.set(this.x2, this.y2);
if (this.transform!=null) {
this.transform.transform(this.p2);
}
return new PathElement2f.LinePathElement2f(
this.p1.getX(), this.p1.getY(),
this.p2.getX(), this.p2.getY());
default:
throw new NoSuchElementException();
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public PathWindingRule getWindingRule() {
return PathWindingRule.NON_ZERO;
}
@Override
public boolean isPolyline() {
return true;
}
}
}