/*
* $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.geometry.d2.afp;
import java.util.NoSuchElementException;
import org.eclipse.xtext.xbase.lib.Inline;
import org.eclipse.xtext.xbase.lib.Pure;
import org.arakhne.afc.math.MathConstants;
import org.arakhne.afc.math.MathUtil;
import org.arakhne.afc.math.Unefficient;
import org.arakhne.afc.math.geometry.CrossingComputationType;
import org.arakhne.afc.math.geometry.PathWindingRule;
import org.arakhne.afc.math.geometry.d2.Point2D;
import org.arakhne.afc.math.geometry.d2.Shape2D;
import org.arakhne.afc.math.geometry.d2.Transform2D;
import org.arakhne.afc.math.geometry.d2.Vector2D;
import org.arakhne.afc.vmutil.asserts.AssertMessages;
/**
* Fonctional interface that represented a 2D segment/line on a plane.
*
* @param <ST> is the type of the general implementation.
* @param <IT> is the type of the implementation of this shape.
* @param <IE> is the type of the path elements.
* @param <P> is the type of the points.
* @param <V> is the type of the vectors.
* @param <B> is the type of the bounding boxes.
* @author $Author: sgalland$
* @author $Author: hjaffali$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@SuppressWarnings("checkstyle:methodcount")
public interface Segment2afp<
ST extends Shape2afp<?, ?, IE, P, V, B>,
IT extends Segment2afp<?, ?, IE, P, V, B>,
IE extends PathElement2afp,
P extends Point2D<? super P, ? super V>,
V extends Vector2D<? super V, ? super P>,
B extends Rectangle2afp<?, ?, IE, P, V, B>>
extends Shape2afp<ST, IT, IE, P, V, B> {
/** Iterator on the path elements of the segment.
*
* @param <T> the type of the path elements.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
*/
class SegmentPathIterator<T extends PathElement2afp> implements PathIterator2afp<T> {
private int index;
private final Point2D<?, ?> p1;
private final Point2D<?, ?> p2;
private final Segment2afp<?, ?, T, ?, ?, ?> segment;
private final Transform2D transform;
private final double x1;
private final double x2;
private final double y1;
private final double y2;
/**
* @param segment the iterated segment.
* @param transform the transformation, or <code>null</code>.
*/
public SegmentPathIterator(Segment2afp<?, ?, T, ?, ?, ?> segment, Transform2D transform) {
assert segment != null : AssertMessages.notNullParameter();
this.segment = segment;
this.p1 = new InnerComputationPoint2afp();
this.p2 = new InnerComputationPoint2afp();
this.transform = (transform == null || transform.isIdentity()) ? null : transform;
this.x1 = segment.getX1();
this.y1 = segment.getY1();
this.x2 = segment.getX2();
this.y2 = segment.getY2();
if (this.x1 == this.x2 && this.y1 == this.y2) {
this.index = 2;
}
}
@Override
public GeomFactory2afp<T, ?, ?, ?> getGeomFactory() {
return this.segment.getGeomFactory();
}
@Pure
@Override
public PathWindingRule getWindingRule() {
return PathWindingRule.NON_ZERO;
}
@Pure
@Override
public boolean hasNext() {
return this.index <= 1;
}
@Pure
@Override
public boolean isCurved() {
return false;
}
@Pure
@Override
public boolean isMultiParts() {
return false;
}
@Pure
@Override
public boolean isPolygon() {
return false;
}
@Pure
@Override
public boolean isPolyline() {
return true;
}
@Override
public T next() {
if (this.index > 1) {
throw new NoSuchElementException();
}
final 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 this.segment.getGeomFactory().newMovePathElement(
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 this.segment.getGeomFactory().newLinePathElement(
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 PathIterator2afp<T> restartIterations() {
return new SegmentPathIterator<>(this.segment, this.transform);
}
}
/** Result of the intersection between segments in a context where a single
* test is not enough.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
* @see Segment2afp#findsUncertainIntersectionSegmentSegmentWithEnds(double,
* double, double, double, double, double, double, double)
* @see Segment2afp#findsUncertainIntersectionSegmentSegmentWithoutEnds(double,
* double, double, double, double, double, double, double)
*/
enum UncertainIntersection {
/** No intersection, certainly.
*/
NO {
@Override
public boolean booleanValue() {
return false;
}
},
/** Intersection, uncertainly.
*/
PERHAPS {
@Override
public boolean booleanValue() {
return true;
}
};
/** Replies the UncertainIntersection for the given boolean.
*
* @param value the boolean value.
* @return the UncertainIntersection.
*/
public static UncertainIntersection fromBoolean(boolean value) {
if (value) {
return UncertainIntersection.PERHAPS;
}
return UncertainIntersection.NO;
}
/** Replies the boolean representation of the constant.
*
* @return the boolean representation.
*/
public abstract boolean booleanValue();
}
/**
* Replies the relative counterclockwise (CCW) of a segment against a point. Returns an indicator of where
* the specified point {@code (px, py)} lies with respect to the line segment from {@code (x1, y1)}
* to {@code (x2, y2)}. The return value can be either 1, -1, or 0 and indicates in which
* direction the specified line must pivot around its first end point, {@code (x1, y1)}, in
* order to point at the specified point {@code (px, py)}.
* In other words, given three point P1, P2, and P, is the segments (P1-P2-P) a counterclockwise turn?
*
* <p>In opposite to {@link #findsSideLinePoint(double, double, double, double, double, double, double)},
* this function tries to classifies the point if it is colinear to the segment.
* The classification is explained below.
*
* <p>A return value of 1 indicates that the line segment must turn in the direction that takes the
* positive X axis towards the negative Y axis. In the default coordinate system used by Java 2D,
* this direction is counterclockwise.
*
* <p>A return value of -1 indicates that the line segment must turn in the direction that takes the
* positive X axis towards the positive Y axis. In the default coordinate system, this
* direction is clockwise.
*
* <p>A return value of 0 indicates that the point lies exactly on the line segment.
* Note that an indicator value of 0 is rare and not useful for determining colinearity
* because of floating point rounding issues.
*
* <p>If the point is colinear with the line segment, but not between the end points, then the value will be
* -1 if the point lies "beyond {@code (x1, y1)}" or 1 if the point lies "beyond {@code (x2, y2)}".
*
* @param x1
* the X coordinate of the start point of the specified line segment
* @param y1
* the Y coordinate of the start point of the specified line segment
* @param x2
* the X coordinate of the end point of the specified line segment
* @param y2
* the Y coordinate of the end point of the specified line segment
* @param px
* the X coordinate of the specified point to be compared with the specified line segment
* @param py
* the Y coordinate of the specified point to be compared with the specified line segment
* @param epsilon approximation of the tests for equality to zero.
* @return an integer that indicates the position of the third specified coordinates with
* respect to the line segment formed by the first two specified coordinates.
* @see #calculatesRelativeDistanceLinePoint(double, double, double, double, double, double)
* @see #findsSideLinePoint(double, double, double, double, double, double, double)
*/
@Pure
static int ccw(double x1, double y1, double x2, double y2, double px, double py, double epsilon) {
final double x21 = x2 - x1;
final double y21 = y2 - y1;
double xp1 = px - x1;
double yp1 = py - y1;
double ccw = xp1 * y21 - yp1 * x21;
if (MathUtil.isEpsilonZero(ccw, epsilon)) {
// The point is colinear, classify based on which side of
// the segment the point falls on. We can calculate a
// relative value using the projection of px, py onto the
// segment - a negative value indicates the point projects
// outside of the segment in the direction of the particular
// endpoint used as the origin for the projection.
ccw = xp1 * x21 + yp1 * y21;
if (ccw > 0.) {
// Reverse the projection to be relative to the original x2, y2
// x2 and y2 are simply negated.
// px and py need to have (x2 - x1) or (y2 - y1) subtracted
// from them (based on the original values)
// Since we really want to get a positive answer when the
// point is "beyond (x2, y2)", then we want to calculate
// the inverse anyway - thus we leave x2 & y2 negated.
xp1 -= x21;
yp1 -= y21;
ccw = xp1 * x21 + yp1 * y21;
if (ccw < 0) {
ccw = 0.;
}
}
}
return (ccw < 0.) ? -1 : ((ccw > 0.) ? 1 : 0);
}
/** Replies the point on the segment that is closest to the given point.
*
* @param ax is the x coordinate of the first point of the segment.
* @param ay is the y coordinate of the first point of the segment.
* @param bx is the x coordinate of the second point of the segment.
* @param by is the y coordinate of the second point of the segment.
* @param px is the x coordinate of the point.
* @param py is the y coordinate of the point.
* @param result the is point on the shape.
* @deprecated since 13.0, see {@link #findsClosestPointSegmentPoint(double, double, double, double,
* double, double, Point2D)}
*/
@Deprecated
static void computeClosestPointToPoint(
double ax, double ay, double bx, double by, double px, double py,
Point2D<?, ?> result) {
findsClosestPointSegmentPoint(ax, ay, bx, by, px, py, result);
}
/** Replies the point on the segment that is closest to the given point.
*
* @param ax is the x coordinate of the first point of the segment.
* @param ay is the y coordinate of the first point of the segment.
* @param bx is the x coordinate of the second point of the segment.
* @param by is the y coordinate of the second point of the segment.
* @param px is the x coordinate of the point.
* @param py is the y coordinate of the point.
* @param result the is point on the shape.
*/
@SuppressWarnings("checkstyle:magicnumber")
static void findsClosestPointSegmentPoint(
double ax, double ay, double bx, double by, double px, double py,
Point2D<?, ?> result) {
assert result != null : AssertMessages.notNullParameter(6);
final double ratio = Segment2afp.findsProjectedPointPointLine(px, py, ax, ay, bx, by);
if (ratio <= 0.) {
result.set(ax, ay);
} else if (ratio >= 1.) {
result.set(bx, by);
} else {
result.set(
ax + (bx - ax) * ratio,
ay + (by - ay) * ratio);
}
}
/** Replies the point on the segment that is closest to the rectangle.
*
* @param sx1 is the x coordinate of the first point of the segment.
* @param sy1 is the y coordinate of the first point of the segment.
* @param sx2 is the x coordinate of the second point of the segment.
* @param sy2 is the y coordinate of the second point of the segment.
* @param rx is the x coordinate of the rectangle.
* @param ry is the y coordinate of the rectangle.
* @param rwidth is the width of the rectangle.
* @param rheight is the height of the rectangle.
* @param result the is point on the segment.
* @deprecated since 13.0, see {@link #findsClosestPointSegmentRectangle(double, double, double,
* double, double, double, double, double, Point2D)}
*/
@Deprecated
@SuppressWarnings("checkstyle:parameternumber")
static void computeClosestPointToRectangle(double sx1, double sy1, double sx2, double sy2,
double rx, double ry, double rwidth, double rheight, Point2D<?, ?> result) {
findsClosestPointSegmentRectangle(sx1, sy1, sx2, sy2, rx, ry, rwidth, rheight, result);
}
/** Replies the point on the segment that is closest to the rectangle.
*
* @param sx1 is the x coordinate of the first point of the segment.
* @param sy1 is the y coordinate of the first point of the segment.
* @param sx2 is the x coordinate of the second point of the segment.
* @param sy2 is the y coordinate of the second point of the segment.
* @param rx is the x coordinate of the rectangle.
* @param ry is the y coordinate of the rectangle.
* @param rwidth is the width of the rectangle.
* @param rheight is the height of the rectangle.
* @param result the is point on the segment.
*/
@SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"})
static void findsClosestPointSegmentRectangle(double sx1, double sy1, double sx2, double sy2,
double rx, double ry, double rwidth, double rheight, Point2D<?, ?> result) {
assert rwidth >= 0. : AssertMessages.positiveOrZeroParameter(6);
assert rheight >= 0. : AssertMessages.positiveOrZeroParameter(7);
final double rmaxx = rx + rwidth;
final double rmaxy = ry + rheight;
final int code1 = MathUtil.getCohenSutherlandCode(sx1, sy1, rx, ry, rmaxx, rmaxy);
final int code2 = MathUtil.getCohenSutherlandCode(sx2, sy2, rx, ry, rmaxx, rmaxy);
final Point2D<?, ?> tmp1 = new InnerComputationPoint2afp();
final Point2D<?, ?> tmp2 = new InnerComputationPoint2afp();
final int zone = Rectangle2afp.reducesCohenSutherlandZoneRectangleSegment(
rx, ry, rmaxx, rmaxy,
sx1, sy1, sx2, sy2,
code1, code2,
tmp1, tmp2);
if ((zone & MathConstants.COHEN_SUTHERLAND_LEFT) != 0) {
findsClosestPointSegmentSegment(
sx1, sy1, sx2, sy2,
rx, ry, rx, rmaxy, result);
} else if ((zone & MathConstants.COHEN_SUTHERLAND_RIGHT) != 0) {
findsClosestPointSegmentSegment(
sx1, sy1, sx2, sy2,
rmaxx, ry, rmaxx, rmaxy, result);
} else if ((zone & MathConstants.COHEN_SUTHERLAND_BOTTOM) != 0) {
findsClosestPointSegmentSegment(
sx1, sy1, sx2, sy2,
rx, ry, rmaxx, ry, result);
} else if ((zone & MathConstants.COHEN_SUTHERLAND_TOP) != 0) {
findsClosestPointSegmentSegment(
sx1, sy1, sx2, sy2,
rx, rmaxy, rmaxx, rmaxy, result);
} else {
findsClosestPointSegmentPoint(
tmp1.getX(), tmp1.getY(), tmp2.getX(), tmp2.getY(),
(rx + rmaxx) / 2., (ry + rmaxy) / 2., result);
}
}
/** Replies the point on the first segment that is closest to the second segment.
*
* @param s1x1 is the x coordinate of the first point of the first segment.
* @param s1y1 is the y coordinate of the first point of the first segment.
* @param s1x2 is the x coordinate of the second point of the first segment.
* @param s1y2 is the y coordinate of the second point of the first segment.
* @param s2x1 is the x coordinate of the first point of the second segment.
* @param s2y1 is the y coordinate of the first point of the second segment.
* @param s2x2 is the x coordinate of the second point of the second segment.
* @param s2y2 is the y coordinate of the second point of the second segment.
* @param result the is point on the shape.
* @return the square distance between the segments.
* @deprecated since 13.0, see {@link #findsClosestPointSegmentSegment(double, double, double,
* double, double, double, double, double, Point2D)}
*/
@Deprecated
@SuppressWarnings("checkstyle:parameternumber")
static double computeClosestPointToSegment(
double s1x1, double s1y1, double s1x2, double s1y2,
double s2x1, double s2y1, double s2x2, double s2y2,
Point2D<?, ?> result) {
return findsClosestPointSegmentSegment(s1x1, s1y1, s1x2, s1y2, s2x1, s2y1, s2x2, s2y2, result);
}
/** Replies the point on the first segment that is closest to the second segment.
*
* @param s1x1 is the x coordinate of the first point of the first segment.
* @param s1y1 is the y coordinate of the first point of the first segment.
* @param s1x2 is the x coordinate of the second point of the first segment.
* @param s1y2 is the y coordinate of the second point of the first segment.
* @param s2x1 is the x coordinate of the first point of the second segment.
* @param s2y1 is the y coordinate of the first point of the second segment.
* @param s2x2 is the x coordinate of the second point of the second segment.
* @param s2y2 is the y coordinate of the second point of the second segment.
* @param resultOnFirstSegment the point on the first segment.
* @param resultOnSecondSegment the point on the second segment.
* @return the square distance between the segments.
* @deprecated since 13.0, see {@link #findsClosestPointSegmentSegment(double, double, double,
* double, double, double, double, double, Point2D, Point2D)}
*/
@Deprecated
@SuppressWarnings("checkstyle:parameternumber")
static double computeClosestPointToSegment(
double s1x1, double s1y1, double s1x2, double s1y2,
double s2x1, double s2y1, double s2x2, double s2y2,
Point2D<?, ?> resultOnFirstSegment, Point2D<?, ?> resultOnSecondSegment) {
return findsClosestPointSegmentSegment(s1x1, s1y1, s1x2, s1y2, s2x1, s2y1, s2x2, s2y2,
resultOnFirstSegment, resultOnSecondSegment);
}
/** Replies the point on the first segment that is closest to the second segment.
*
* @param s1x1 is the x coordinate of the first point of the first segment.
* @param s1y1 is the y coordinate of the first point of the first segment.
* @param s1x2 is the x coordinate of the second point of the first segment.
* @param s1y2 is the y coordinate of the second point of the first segment.
* @param s2x1 is the x coordinate of the first point of the second segment.
* @param s2y1 is the y coordinate of the first point of the second segment.
* @param s2x2 is the x coordinate of the second point of the second segment.
* @param s2y2 is the y coordinate of the second point of the second segment.
* @param result the is point on the shape.
* @return the square distance between the segments.
*/
@SuppressWarnings({"checkstyle:parameternumber", "checkstyle:npathcomplexity"})
static double findsClosestPointSegmentSegment(
double s1x1, double s1y1, double s1x2, double s1y2,
double s2x1, double s2y1, double s2x2, double s2y2,
Point2D<?, ?> result) {
return findsClosestPointSegmentSegment(
s1x1, s1y1, s1x2, s1y2, s2x1, s2y1, s2x2, s2y2,
result, null);
}
/** Replies the point on the first segment that is closest to the second segment.
*
* @param s1x1 is the x coordinate of the first point of the first segment.
* @param s1y1 is the y coordinate of the first point of the first segment.
* @param s1x2 is the x coordinate of the second point of the first segment.
* @param s1y2 is the y coordinate of the second point of the first segment.
* @param s2x1 is the x coordinate of the first point of the second segment.
* @param s2y1 is the y coordinate of the first point of the second segment.
* @param s2x2 is the x coordinate of the second point of the second segment.
* @param s2y2 is the y coordinate of the second point of the second segment.
* @param resultOnFirstSegment the point on the first segment.
* @param resultOnSecondSegment the point on the second segment.
* @return the square distance between the segments.
*/
@SuppressWarnings({"checkstyle:parameternumber", "checkstyle:npathcomplexity"})
static double findsClosestPointSegmentSegment(
double s1x1, double s1y1, double s1x2, double s1y2,
double s2x1, double s2y1, double s2x2, double s2y2,
Point2D<?, ?> resultOnFirstSegment, Point2D<?, ?> resultOnSecondSegment) {
final double ux = s1x2 - s1x1;
final double uy = s1y2 - s1y1;
final double vx = s2x2 - s2x1;
final double vy = s2y2 - s2y1;
final double wx = s1x1 - s2x1;
final double wy = s1y1 - s2y1;
final double a = Vector2D.dotProduct(ux, uy, ux, uy);
final double b = Vector2D.dotProduct(ux, uy, vx, vy);
final double c = Vector2D.dotProduct(vx, vy, vx, vy);
final double d = Vector2D.dotProduct(ux, uy, wx, wy);
final double e = Vector2D.dotProduct(vx, vy, wx, wy);
final double bigD = a * c - b * b;
double svD = bigD;
double tvD = bigD;
double svN;
double tvN;
// compute the line parameters of the two closest points
if (MathUtil.isEpsilonZero(bigD)) {
// the lines are almost parallel
// force using point P0 on segment S1
svN = 0.;
// to prevent possible division by 0.0 later
svD = 1.;
tvN = e;
tvD = c;
} else {
// get the closest points on the infinite lines
svN = b * e - c * d;
tvN = a * e - b * d;
if (svN < 0.) {
// sc < 0 => the s=0 edge is visible
svN = 0.;
tvN = e;
tvD = c;
} else if (svN > svD) {
// sc > 1 => the s=1 edge is visible
svN = svD;
tvN = e + b;
tvD = c;
}
}
if (tvN < 0.) {
// tc < 0 => the t=0 edge is visible
tvN = 0.;
// recompute sc for this edge
if (-d < 0.) {
svN = 0.0;
} else if (-d > a) {
svN = svD;
} else {
svN = -d;
svD = a;
}
} else if (tvN > tvD) {
// tc > 1 => the t=1 edge is visible
tvN = tvD;
// recompute sc for this edge
if ((-d + b) < 0.) {
svN = 0;
} else if ((-d + b) > a) {
svN = svD;
} else {
svN = -d + b;
svD = a;
}
}
// finally do the division to get sc and tc
final double sc = MathUtil.isEpsilonZero(svN) ? 0. : (svN / svD);
final double tc = MathUtil.isEpsilonZero(tvN) ? 0. : (tvN / tvD);
// get the difference of the two closest points
// = S1(sc) - S2(tc)
final double dPx = wx + (sc * ux) - (tc * vx);
final double dPy = wy + (sc * uy) - (tc * vy);
if (resultOnFirstSegment != null) {
resultOnFirstSegment.set(s1x1 + sc * ux, s1y1 + sc * uy);
}
if (resultOnSecondSegment != null) {
resultOnSecondSegment.set(s2x1 + tc * vx, s2y1 + tc * vy);
}
return dPx * dPx + dPy * dPy;
}
/**
* Calculates the number of times the line from (x0, y0) to (x1, y1)
* crosses the circle (cx, cy) with radius 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}.
* @deprecated since 13.0, see {@link #calculatesCrossingsCircleShadowSegment(int, double,
* double, double, double, double, double, double)}
*/
@Deprecated
@Pure
static int computeCrossingsFromCircle(
int crossings,
double cx, double cy,
double radius,
double x0, double y0,
double x1, double y1) {
return calculatesCrossingsCircleShadowSegment(crossings, cx, cy, radius, x0, y0, x1, y1);
}
/**
* Calculates the number of times the line from (x0, y0) to (x1, y1)
* crosses the circle (cx, cy) with radius 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}.
*/
@Pure
static int calculatesCrossingsCircleShadowSegment(
int crossings,
double cx, double cy,
double radius,
double x0, double y0,
double x1, double y1) {
assert radius >= 0. : AssertMessages.positiveOrZeroParameter(3);
int numCrosses = crossings;
final double xmin = cx - Math.abs(radius);
final double ymin = cy - Math.abs(radius);
final double 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 (Circle2afp.intersectsCircleSegment(
cx, cy, radius,
x0, y0, x1, y1)) {
return MathConstants.SHAPE_INTERSECTS;
} else {
numCrosses += calculatesCrossingsPointShadowSegment(cx, ymin, x0, y0, x1, y1);
numCrosses += calculatesCrossingsPointShadowSegment(cx, 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 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}.
* @deprecated since 13.0, see {@link #calculatesCrossingsEllipseShadowSegment(int, double, double,
* double, double, double, double, double, double)}
*/
@Pure
@Deprecated
@SuppressWarnings("checkstyle:parameternumber")
static int computeCrossingsFromEllipse(
int crossings,
double ex, double ey,
double ew, double eh,
double x0, double y0,
double x1, double y1) {
return calculatesCrossingsEllipseShadowSegment(crossings, ex, ey, ew, eh, x0, y0, x1, y1);
}
/**
* 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}.
*/
@Pure
@SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"})
static int calculatesCrossingsEllipseShadowSegment(
int crossings,
double ex, double ey,
double ew, double eh,
double x0, double y0,
double x1, double y1) {
assert ew >= 0. : AssertMessages.positiveOrZeroParameter(3);
assert eh >= 0 : AssertMessages.positiveOrZeroParameter(4);
int numCrosses = crossings;
final double xmin = ex;
final double ymin = ey;
final double xmax = ex + ew;
final double 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 (Ellipse2afp.intersectsEllipseSegment(
xmin, ymin, xmax - xmin, ymax - ymin,
x0, y0, x1, y1, true)) {
return MathConstants.SHAPE_INTERSECTS;
} else {
final double xcenter = (xmin + xmax) / 2.;
numCrosses += calculatesCrossingsPointShadowSegment(xcenter, ymin, x0, y0, x1, y1);
numCrosses += calculatesCrossingsPointShadowSegment(xcenter, ymax, x0, y0, x1, y1);
}
return numCrosses;
}
/**
* 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 #calculatesCrossingsPointShadowSegmentWithoutEquality(double,
* double, double, double, double, double)}.
* The equality test is 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.
* @deprecated since 13.0, see {@link #calculatesCrossingsPointShadowSegment(double, double, double, double, double, double)}
*/
@Deprecated
@Pure
static int computeCrossingsFromPoint(
double px, double py,
double x0, double y0,
double x1, double y1) {
return calculatesCrossingsPointShadowSegment(px, py, x0, y0, x1, y1);
}
/**
* 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 #calculatesCrossingsPointShadowSegmentWithoutEquality(double,
* double, double, double, double, double)}.
* The equality test is 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.
*/
@Pure
static int calculatesCrossingsPointShadowSegment(
double px, double py,
double x0, double y0,
double x1, double 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;
}
final double 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 #calculatesCrossingsPointShadowSegment(double,
* double, double, double, double, double)}.
* 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.
* @deprecated since 13.0, see {@link #calculatesCrossingsPointShadowSegmentWithoutEquality(double, double,
* double, double, double, double)}
*/
@Deprecated
@Pure
static int computeCrossingsFromPointWithoutEquality(
double px, double py,
double x0, double y0,
double x1, double y1) {
return calculatesCrossingsPointShadowSegmentWithoutEquality(px, py, x0, y0, x1, y1);
}
/**
* 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 #calculatesCrossingsPointShadowSegment(double,
* double, double, double, double, double)}.
* 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.
*/
@Pure
static int calculatesCrossingsPointShadowSegmentWithoutEquality(
double px, double py,
double x0, double y0,
double x1, double 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;
}
final double xintercept = x0 + (py - y0) * (x1 - x0) / (y1 - y0);
if (px > xintercept) {
return 0;
}
return (y0 < y1) ? 1 : -1;
}
/**
* 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}.
* @deprecated since 13.0, see {@link #calculatesCrossingsRectangleShadowSegment(int, double, double,
* double, double, double, double, double, double)}
*/
@Deprecated
@Pure
@SuppressWarnings("checkstyle:parameternumber")
static int computeCrossingsFromRect(
int crossings,
double rxmin, double rymin,
double rxmax, double rymax,
double x0, double y0,
double x1, double y1) {
return calculatesCrossingsRectangleShadowSegment(crossings, rxmin, rymin, rxmax, rymax, x0, y0, x1, y1);
}
/**
* 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}.
*/
@Pure
@SuppressWarnings({"checkstyle:parameternumber", "checkstyle:cyclomaticcomplexity",
"checkstyle:npathcomplexity", "checkstyle:returncount",
"checkstyle:booleanexpressioncomplexity", "checkstyle:magicnumber"})
static int calculatesCrossingsRectangleShadowSegment(
int crossings,
double rxmin, double rymin,
double rxmax, double rymax,
double x0, double y0,
double x1, double y1) {
assert rxmin <= rxmax : AssertMessages.lowerEqualParameters(1, rxmin, 3, rxmax);
assert rymin <= rymax : AssertMessages.lowerEqualParameters(2, rymin, 4, rymax);
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
double xi0 = x0;
if (y0 < rymin) {
xi0 += (rymin - y0) * (x1 - x0) / (y1 - y0);
} else if (y0 > rymax) {
xi0 += (rymax - y0) * (x1 - x0) / (y1 - y0);
}
double 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;
}
/**
* Accumulate the number of times the line crosses the shadow
* extending to the right of the round 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 arcWidth is the width of the rectangle arcs.
* @param arcHeight is the height of the rectangle arcs.
* @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}.
* @deprecated since 13.0, see {@link #calculatesCrossingsRoundRectangleShadowSegment(int, double,
* double, double, double, double, double, double, double, double, double)}
*/
@Deprecated
@Pure
@SuppressWarnings("checkstyle:parameternumber")
static int computeCrossingsFromRoundRect(
int crossings,
double rxmin, double rymin,
double rxmax, double rymax,
double arcWidth, double arcHeight,
double x0, double y0,
double x1, double y1) {
return calculatesCrossingsRoundRectangleShadowSegment(crossings, rxmin, rymin, rxmax, rymax,
arcWidth, arcHeight, x0, y0, x1, y1);
}
/**
* Accumulate the number of times the line crosses the shadow
* extending to the right of the round 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 arcWidth is the width of the rectangle arcs.
* @param arcHeight is the height of the rectangle arcs.
* @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}.
*/
@Pure
@SuppressWarnings({"checkstyle:parameternumber", "checkstyle:cyclomaticcomplexity",
"checkstyle:npathcomplexity", "checkstyle:magicnumber"})
static int calculatesCrossingsRoundRectangleShadowSegment(
int crossings,
double rxmin, double rymin,
double rxmax, double rymax,
double arcWidth, double arcHeight,
double x0, double y0,
double x1, double y1) {
assert rxmin <= rxmax : AssertMessages.lowerEqualParameters(1, rxmin, 3, rxmax);
assert rymin <= rymax : AssertMessages.lowerEqualParameters(2, rymin, 4, rymax);
assert arcWidth >= 0. && arcWidth <= (rxmax - rxmin) / 2.
: AssertMessages.outsideRangeInclusiveParameter(5, arcWidth, 0, (rxmax - rxmin) / 2.);
assert arcHeight >= 0. && arcHeight <= (rymax - rymin) / 2.
: AssertMessages.outsideRangeInclusiveParameter(6, arcHeight, 0, (rymax - rymin) / 2.);
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.
if (RoundRectangle2afp.intersectsRoundRectangleSegment(
rxmin, rymin, rxmax, rymax, arcWidth, arcHeight,
x0, y0, x1, y1)) {
return MathConstants.SHAPE_INTERSECTS;
}
final double x = rxmax - arcWidth;
numCrosses += calculatesCrossingsPointShadowSegment(x, rymin, x0, y0, x1, y1);
numCrosses += calculatesCrossingsPointShadowSegment(x, rymax, x0, y0, x1, y1);
return numCrosses;
}
/**
* 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}
* @deprecated since 13.0, see {@link #calculatesCrossingsSegmentShadowSegment(int,
* double, double, double, double, double, double, double, double)}
*/
@Deprecated
@Pure
@SuppressWarnings("checkstyle:parameternumber")
static int computeCrossingsFromSegment(
int crossings,
double sx1, double sy1,
double sx2, double sy2,
double x0, double y0,
double x1, double y1) {
return calculatesCrossingsSegmentShadowSegment(crossings, sx1, sy1, sx2, sy2, x0, y0, x1, y1);
}
/**
* 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}
*/
@Pure
@SuppressWarnings({"checkstyle:parameternumber", "checkstyle:cyclomaticcomplexity",
"checkstyle:npathcomplexity"})
static int calculatesCrossingsSegmentShadowSegment(
int crossings,
double sx1, double sy1,
double sx2, double sy2,
double x0, double y0,
double x1, double y1) {
int numCrosses = crossings;
final double xmin = Math.min(sx1, sx2);
final double xmax = Math.max(sx1, sx2);
final double ymin = Math.min(sy1, sy2);
final double 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 (intersectsSegmentSegmentWithEnds(x0, y0, x1, y1, sx1, sy1, sx2, sy2)) {
return MathConstants.SHAPE_INTERSECTS;
} else {
final int side1;
final int side2;
if (sy1 <= sy2) {
side1 = findsSideLinePoint(sx1, sy1, sx2, sy2, x0, y0, 0.);
side2 = findsSideLinePoint(sx1, sy1, sx2, sy2, x1, y1, 0.);
} else {
side1 = findsSideLinePoint(sx2, sy2, sx1, sy1, x0, y0, 0.);
side2 = findsSideLinePoint(sx2, sy2, sx1, sy1, x1, y1, 0.);
}
if (side1 > 0 || side2 > 0) {
final int n1 = calculatesCrossingsPointShadowSegment(sx1, sy1, x0, y0, x1, y1);
final int n2;
if (n1 != 0) {
n2 = calculatesCrossingsPointShadowSegmentWithoutEquality(sx2, sy2, x0, y0, x1, y1);
} else {
n2 = calculatesCrossingsPointShadowSegment(sx2, sy2, x0, y0, x1, y1);
}
numCrosses += n1;
numCrosses += n2;
}
}
return numCrosses;
}
/**
* Accumulate the number of times the line crosses the shadow
* extending to the right of the triangle. 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 tx1 is the first point of the triangle.
* @param ty1 is the first point of the triangle.
* @param tx2 is the second point of the triangle.
* @param ty2 is the second point of the triangle.
* @param tx3 is the third point of the triangle.
* @param ty3 is the third point of the triangle.
* @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}.
* @deprecated since 13.0, see {@link #calculatesCrossingsTriangleShadowSegment(int,
* double, double, double, double, double, double, double, double, double, double)}
*/
@Deprecated
@Pure
@SuppressWarnings("checkstyle:parameternumber")
static int computeCrossingsFromTriangle(
int crossings,
double tx1, double ty1,
double tx2, double ty2,
double tx3, double ty3,
double x0, double y0,
double x1, double y1) {
return calculatesCrossingsTriangleShadowSegment(crossings, tx1, ty1, tx2, ty2, tx3, ty3, x0, y0, x1, y1);
}
/**
* Accumulate the number of times the line crosses the shadow
* extending to the right of the triangle. 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 tx1 is the first point of the triangle.
* @param ty1 is the first point of the triangle.
* @param tx2 is the second point of the triangle.
* @param ty2 is the second point of the triangle.
* @param tx3 is the third point of the triangle.
* @param ty3 is the third point of the triangle.
* @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}.
*/
@Pure
@SuppressWarnings({"checkstyle:parameternumber", "checkstyle:cyclomaticcomplexity",
"checkstyle:npathcomplexity"})
static int calculatesCrossingsTriangleShadowSegment(
int crossings,
double tx1, double ty1,
double tx2, double ty2,
double tx3, double ty3,
double x0, double y0,
double x1, double y1) {
int numCrosses = crossings;
double xmin = tx1;
double xmax = tx1;
if (tx2 < xmin) {
xmin = tx2;
}
if (tx2 > xmax) {
xmax = tx2;
}
double ymin = ty1;
double ymax = ty1;
double x4ymin = tx1;
double x4ymax = tx1;
if (ty2 == ymin) {
x4ymin = Math.max(tx2, x4ymin);
} else if (ty2 < ymin) {
ymin = ty2;
x4ymin = tx2;
}
if (ty2 == ymax) {
x4ymax = Math.max(tx2, x4ymax);
} else if (ty2 > ymax) {
ymax = ty2;
x4ymax = tx2;
}
if (tx3 < xmin) {
xmin = tx3;
}
if (tx3 > xmax) {
xmax = tx3;
}
if (ty3 == ymin) {
x4ymin = Math.max(tx3, x4ymin);
} else if (ty3 < ymin) {
ymin = ty3;
x4ymin = tx3;
}
if (ty3 == ymax) {
x4ymax = Math.max(tx3, x4ymax);
} else if (ty3 > ymax) {
ymax = ty3;
x4ymax = tx3;
}
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 (Triangle2afp.intersectsTriangleSegment(
tx1, ty1, tx2, ty2, tx3, ty3,
x0, y0, x1, y1)) {
return MathConstants.SHAPE_INTERSECTS;
} else {
numCrosses += calculatesCrossingsPointShadowSegment(x4ymin, ymin, x0, y0, x1, y1);
numCrosses += calculatesCrossingsPointShadowSegment(x4ymax, ymax, x0, y0, x1, y1);
}
return numCrosses;
}
/** Compute the distance between a point and a line.
*
* @param x1 horizontal position of the first point of the line.
* @param y1 vertical position of the first point of the line.
* @param x2 horizontal position of the second point of the line.
* @param y2 vertical position of the second point of the line.
* @param px horizontal position of the point.
* @param py vertical position of the point.
* @return the distance beetween the point and the line.
* @see #calculatesDistanceSquaredLinePoint(double, double, double, double, double, double)
* @see #calculatesRelativeDistanceLinePoint(double, double, double, double, double, double)
* @deprecated since 13.0, see {@link #calculatesDistanceLinePoint(double, double, double, double, double, double)}
*/
@Deprecated
@Pure
static double computeDistanceLinePoint(double x1, double y1, double x2, double y2, double px, double py) {
return calculatesDistanceLinePoint(x1, y1, x2, y2, px, py);
}
/** Compute the distance between a point and a line.
*
* @param x1 horizontal position of the first point of the line.
* @param y1 vertical position of the first point of the line.
* @param x2 horizontal position of the second point of the line.
* @param y2 vertical position of the second point of the line.
* @param px horizontal position of the point.
* @param py vertical position of the point.
* @return the distance beetween the point and the line.
* @see #calculatesDistanceSquaredLinePoint(double, double, double, double, double, double)
* @see #calculatesRelativeDistanceLinePoint(double, double, double, double, double, double)
*/
@Pure
static double calculatesDistanceLinePoint(double x1, double y1, double x2, double y2, double px, double py) {
final double x21 = x2 - x1;
final double y21 = y2 - y1;
final double denomenator = x21 * x21 + y21 * y21;
if (denomenator == 0.) {
return Point2D.getDistancePointPoint(px, py, x1, y1);
}
final double factor = ((y1 - py) * x21 - (x1 - px) * y21) / denomenator;
return Math.abs(factor) * Math.sqrt(denomenator);
}
/** Compute the distance between a point and a segment.
*
* @param x1 horizontal position of the first point of the segment.
* @param y1 vertical position of the first point of the segment.
* @param x2 horizontal position of the second point of the segment.
* @param y2 vertical position of the second point of the segment.
* @param px horizontal position of the point.
* @param py vertical position of the point.
* @return the distance beetween the point and the segment.
* @deprecated since 13.0, see {@link #calculatesDistanceSegmentPoint(double, double, double, double, double, double)}
*/
@Deprecated
@Pure
static double computeDistanceSegmentPoint(double x1, double y1, double x2, double y2, double px, double py) {
return calculatesDistanceSegmentPoint(x1, y1, x2, y2, px, py);
}
/** Compute the distance between a point and a segment.
*
* @param x1 horizontal position of the first point of the segment.
* @param y1 vertical position of the first point of the segment.
* @param x2 horizontal position of the second point of the segment.
* @param y2 vertical position of the second point of the segment.
* @param px horizontal position of the point.
* @param py vertical position of the point.
* @return the distance beetween the point and the segment.
*/
@Pure
static double calculatesDistanceSegmentPoint(double x1, double y1, double x2, double y2, double px, double py) {
final double x21 = x2 - x1;
final double y21 = y2 - y1;
final double denomenator = x21 * x21 + y21 * y21;
if (denomenator == 0.) {
return Point2D.getDistancePointPoint(px, py, x1, y1);
}
final double xp1 = px - x1;
final double yp1 = py - y1;
final double numerator = xp1 * x21 + yp1 * y21;
final double ratio = numerator / denomenator;
if (ratio <= 0.) {
return Math.sqrt(xp1 * xp1 + yp1 * yp1);
}
if (ratio >= 1.) {
final double xp2 = px - x2;
final double yp2 = py - y2;
return Math.sqrt(xp2 * xp2 + yp2 * yp2);
}
final double factor = (xp1 * y21 - yp1 * x21) / denomenator;
return Math.abs(factor) * Math.sqrt(denomenator);
}
/** Compute the distance between a point and a line.
*
* @param x1 horizontal position of the first point of the line.
* @param y1 vertical position of the first point of the line.
* @param x2 horizontal position of the second point of the line.
* @param y2 vertical position of the second point of the line.
* @param px horizontal position of the point.
* @param py vertical position of the point.
* @return the distance beetween the point and the line.
* @see #calculatesDistanceLinePoint(double, double, double, double, double, double)
* @deprecated since 13.0, see {@link #calculatesDistanceSquaredLinePoint(double, double, double,
* double, double, double)}
*/
@Deprecated
@Pure
static double computeDistanceSquaredLinePoint(double x1, double y1, double x2, double y2, double px, double py) {
return calculatesDistanceSquaredLinePoint(x1, y1, x2, y2, px, py);
}
/** Compute the distance between a point and a line.
*
* @param x1 horizontal position of the first point of the line.
* @param y1 vertical position of the first point of the line.
* @param x2 horizontal position of the second point of the line.
* @param y2 vertical position of the second point of the line.
* @param px horizontal position of the point.
* @param py vertical position of the point.
* @return the distance beetween the point and the line.
* @see #calculatesDistanceLinePoint(double, double, double, double, double, double)
*/
@Pure
static double calculatesDistanceSquaredLinePoint(double x1, double y1, double x2, double y2, double px, double py) {
final double x21 = x2 - x1;
final double y21 = y2 - y1;
final double denomenator = x21 * x21 + y21 * y21;
if (denomenator == 0.) {
return Point2D.getDistanceSquaredPointPoint(px, py, x1, y1);
}
final double s = ((y1 - py) * x21 - (x1 - px) * y21) / denomenator;
return (s * s) * Math.abs(denomenator);
}
/** Compute the square distance between a point and a segment.
*
* @param x1 horizontal position of the first point of the segment.
* @param y1 vertical position of the first point of the segment.
* @param x2 horizontal position of the second point of the segment.
* @param y2 vertical position of the second point of the segment.
* @param px horizontal position of the point.
* @param py vertical position of the point.
* @return the distance beetween the point and the segment.
* @deprecated since 13.0, see {@link #calculatesDistanceSquaredSegmentPoint(double,
* double, double, double, double, double)}
*/
@Deprecated
@Pure
static double computeDistanceSquaredSegmentPoint(double x1, double y1, double x2, double y2, double px, double py) {
return calculatesDistanceSquaredSegmentPoint(x1, y1, x2, y2, px, py);
}
/** Compute the square distance between a point and a segment.
*
* @param x1 horizontal position of the first point of the segment.
* @param y1 vertical position of the first point of the segment.
* @param x2 horizontal position of the second point of the segment.
* @param y2 vertical position of the second point of the segment.
* @param px horizontal position of the point.
* @param py vertical position of the point.
* @return the distance beetween the point and the segment.
*/
@Pure
static double calculatesDistanceSquaredSegmentPoint(double x1, double y1, double x2, double y2, double px, double py) {
final double x21 = x2 - x1;
final double y21 = y2 - y1;
final double denomenator = x21 * x21 + y21 * y21;
if (denomenator == 0.) {
return Point2D.getDistanceSquaredPointPoint(px, py, x1, y1);
}
final double xp1 = px - x1;
final double yp1 = py - y1;
final double numerator = xp1 * x21 + yp1 * y21;
final double ratio = numerator / denomenator;
if (ratio <= 0.) {
return Math.abs(xp1 * xp1 + yp1 * yp1);
}
if (ratio >= 1.) {
final double xp2 = px - x2;
final double yp2 = py - y2;
return Math.abs(xp2 * xp2 + yp2 * yp2);
}
final double factor = (xp1 * y21 - yp1 * x21) / denomenator;
return (factor * factor) * Math.abs(denomenator);
}
/** Replies the distance between the two segments.
*
* @param s1x1 x coordinate of the first point of the first triangle.
* @param s1y1 y coordinate of the first point of the first triangle.
* @param s1x2 x coordinate of the second point of the first triangle.
* @param s1y2 y coordinate of the second point of the first triangle.
* @param s2x1 x coordinate of the first point of the second triangle.
* @param s2y1 y coordinate of the first point of the second triangle.
* @param s2x2 x coordinate of the second point of the second triangle.
* @param s2y2 y coordinate of the second point of the second triangle.
* @return the distance between the two segments.
* @deprecated since 13.0, see {@link #calculatesDistanceSquaredSegmentSegment(double,
* double, double, double, double, double, double, double)}
*/
@Deprecated
@Pure
@Inline(value = "Segment2afp.computeClosestPointToSegment(($1), ($2), ($3), ($4), ($5), ($6), ($7), ($8), null)",
imported = {Segment2afp.class})
static double computeDistanceSquaredSegmentSegment(double s1x1, double s1y1, double s1x2, double s1y2,
double s2x1, double s2y1, double s2x2, double s2y2) {
return calculatesDistanceSquaredSegmentSegment(s1x1, s1y1, s1x2, s1y2, s2x1, s2y1, s2x2, s2y2);
}
/** Replies the distance between the two segments.
*
* @param s1x1 x coordinate of the first point of the first triangle.
* @param s1y1 y coordinate of the first point of the first triangle.
* @param s1x2 x coordinate of the second point of the first triangle.
* @param s1y2 y coordinate of the second point of the first triangle.
* @param s2x1 x coordinate of the first point of the second triangle.
* @param s2y1 y coordinate of the first point of the second triangle.
* @param s2x2 x coordinate of the second point of the second triangle.
* @param s2y2 y coordinate of the second point of the second triangle.
* @return the distance between the two segments.
*/
@Pure
@Inline(value = "Segment2afp.computeClosestPointToSegment(($1), ($2), ($3), ($4), ($5), ($6), ($7), ($8), null)",
imported = {Segment2afp.class})
static double calculatesDistanceSquaredSegmentSegment(double s1x1, double s1y1, double s1x2, double s1y2,
double s2x1, double s2y1, double s2x2, double s2y2) {
return findsClosestPointSegmentSegment(s1x1, s1y1, s1x2, s1y2, s2x1, s2y1, s2x2, s2y2, null);
}
/** Replies the point on the segment that is farthest to the given point.
*
* @param ax is the x coordinate of the first point of the segment.
* @param ay is the y coordinate of the first point of the segment.
* @param bx is the x coordinate of the second point of the segment.
* @param by is the y coordinate of the second point of the segment.
* @param px is the x coordinate of the point.
* @param py is the y coordinate of the point.
* @param result the farthest point on the shape.
* @deprecated since 13.0, see {@link #findsFarthestPointSegmentPoint(double,
* double, double, double, double, double, Point2D)}
*/
@Deprecated
@SuppressWarnings("checkstyle:magicnumber")
static void computeFarthestPointToPoint(
double ax, double ay, double bx, double by, double px, double py, Point2D<?, ?> result) {
findsFarthestPointSegmentPoint(ax, ay, bx, by, px, py, result);
}
/** Replies the point on the segment that is farthest to the given point.
*
* @param ax is the x coordinate of the first point of the segment.
* @param ay is the y coordinate of the first point of the segment.
* @param bx is the x coordinate of the second point of the segment.
* @param by is the y coordinate of the second point of the segment.
* @param px is the x coordinate of the point.
* @param py is the y coordinate of the point.
* @param result the farthest point on the shape.
*/
@SuppressWarnings("checkstyle:magicnumber")
static void findsFarthestPointSegmentPoint(
double ax, double ay, double bx, double by, double px, double py, Point2D<?, ?> result) {
assert result != null : AssertMessages.notNullParameter(6);
final double xpa = px - ax;
final double ypa = py - ay;
final double xpb = px - bx;
final double ypb = py - by;
if ((xpa * xpa + ypa * ypa) >= (xpb * xpb + ypb * ypb)) {
result.set(ax, ay);
} else {
result.set(bx, by);
}
}
/** Replies the point on the segment that is farthest to the rectangle.
*
* @param sx1 is the x coordinate of the first point of the segment.
* @param sy1 is the y coordinate of the first point of the segment.
* @param sx2 is the x coordinate of the second point of the segment.
* @param sy2 is the y coordinate of the second point of the segment.
* @param rx is the x coordinate of the rectangle.
* @param ry is the y coordinate of the rectangle.
* @param rwidth is the width of the rectangle.
* @param rheight is the height of the rectangle.
* @param result the is point on the segment.
* @deprecated since 13.0, see {@link #findsFarthestPointSegmentRectangle(double,
* double, double, double, double, double, double, double, Point2D)}
*/
@Deprecated
@SuppressWarnings("checkstyle:parameternumber")
static void computeFarthestPointToRectangle(double sx1, double sy1, double sx2, double sy2,
double rx, double ry, double rwidth, double rheight, Point2D<?, ?> result) {
findsFarthestPointSegmentRectangle(sx1, sy1, sx2, sy2, rx, ry, rwidth, rheight, result);
}
/** Replies the point on the segment that is farthest to the rectangle.
*
* @param sx1 is the x coordinate of the first point of the segment.
* @param sy1 is the y coordinate of the first point of the segment.
* @param sx2 is the x coordinate of the second point of the segment.
* @param sy2 is the y coordinate of the second point of the segment.
* @param rx is the x coordinate of the rectangle.
* @param ry is the y coordinate of the rectangle.
* @param rwidth is the width of the rectangle.
* @param rheight is the height of the rectangle.
* @param result the is point on the segment.
*/
@SuppressWarnings("checkstyle:parameternumber")
static void findsFarthestPointSegmentRectangle(double sx1, double sy1, double sx2, double sy2,
double rx, double ry, double rwidth, double rheight, Point2D<?, ?> result) {
final double rmaxx = rx + rwidth;
final double rmaxy = ry + rheight;
final int code1 = MathUtil.getCohenSutherlandCode(sx1, sy1, rx, ry, rmaxx, rmaxy);
final int code2 = MathUtil.getCohenSutherlandCode(sx2, sy2, rx, ry, rmaxx, rmaxy);
final Point2D<?, ?> tmp1 = new InnerComputationPoint2afp();
final Point2D<?, ?> tmp2 = new InnerComputationPoint2afp();
final int zone;
if (code1 != code2) {
zone = Rectangle2afp.reducesCohenSutherlandZoneRectangleSegment(
rx, ry, rmaxx, rmaxy,
sx1, sy1, sx2, sy2,
code1, code2,
tmp1, tmp2);
} else {
zone = code1;
tmp1.set(sx1, sy1);
tmp2.set(sx2, sy2);
}
if ((zone & MathConstants.COHEN_SUTHERLAND_LEFT) != 0) {
findsFarthestPointSegmentSegment(
sx1, sy1, sx2, sy2,
rx, ry, rx, rmaxy, result);
} else if ((zone & MathConstants.COHEN_SUTHERLAND_RIGHT) != 0) {
findsFarthestPointSegmentSegment(
sx1, sy1, sx2, sy2,
rmaxx, ry, rmaxx, rmaxy, result);
} else if ((zone & MathConstants.COHEN_SUTHERLAND_BOTTOM) != 0) {
findsFarthestPointSegmentSegment(
sx1, sy1, sx2, sy2,
rx, ry, rmaxx, ry, result);
} else if ((zone & MathConstants.COHEN_SUTHERLAND_TOP) != 0) {
findsFarthestPointSegmentSegment(
sx1, sy1, sx2, sy2,
rx, rmaxy, rmaxx, rmaxy, result);
} else {
final double dist1 = Point2D.getDistanceSquaredPointPoint(tmp1.getX(), tmp1.getY(), sx1, sy1);
final double dist2 = Point2D.getDistanceSquaredPointPoint(tmp2.getX(), tmp2.getY(), sx2, sy2);
if (dist1 >= dist2) {
result.set(sx1, sy1);
} else {
result.set(sx2, sy2);
}
}
}
/** Replies the point on the first segment that is farthest to the second segment.
*
* @param s1x1 is the x coordinate of the first point of the first segment.
* @param s1y1 is the y coordinate of the first point of the first segment.
* @param s1x2 is the x coordinate of the second point of the first segment.
* @param s1y2 is the y coordinate of the second point of the first segment.
* @param s2x1 is the x coordinate of the first point of the second segment.
* @param s2y1 is the y coordinate of the first point of the second segment.
* @param s2x2 is the x coordinate of the second point of the second segment.
* @param s2y2 is the y coordinate of the second point of the second segment.
* @param result the is point on the shape.
* @return the minimal square distance between the point on the first segment and any point on the second segment.
* @deprecated since 13.0, see {@link #findsFarthestPointSegmentSegment(double,
* double, double, double, double, double, double, double, Point2D)}
*/
@Deprecated
@SuppressWarnings("checkstyle:parameternumber")
static double computeFarthestPointToSegment(
double s1x1, double s1y1, double s1x2, double s1y2,
double s2x1, double s2y1, double s2x2, double s2y2,
Point2D<?, ?> result) {
return findsFarthestPointSegmentSegment(s1x1, s1y1, s1x2, s1y2, s2x1, s2y1, s2x2, s2y2, result);
}
/** Replies the point on the first segment that is farthest to the second segment.
*
* @param s1x1 is the x coordinate of the first point of the first segment.
* @param s1y1 is the y coordinate of the first point of the first segment.
* @param s1x2 is the x coordinate of the second point of the first segment.
* @param s1y2 is the y coordinate of the second point of the first segment.
* @param s2x1 is the x coordinate of the first point of the second segment.
* @param s2y1 is the y coordinate of the first point of the second segment.
* @param s2x2 is the x coordinate of the second point of the second segment.
* @param s2y2 is the y coordinate of the second point of the second segment.
* @param result the is point on the shape.
* @return the minimal square distance between the point on the first segment and any point on the second segment.
*/
@SuppressWarnings({"checkstyle:parameternumber", "checkstyle:npathcomplexity", "checkstyle:magicnumber"})
static double findsFarthestPointSegmentSegment(
double s1x1, double s1y1, double s1x2, double s1y2,
double s2x1, double s2y1, double s2x2, double s2y2,
Point2D<?, ?> result) {
final double ux = s1x2 - s1x1;
final double uy = s1y2 - s1y1;
final double vx = s2x2 - s2x1;
final double vy = s2y2 - s2y1;
final double wx = s1x1 - s2x1;
final double wy = s1y1 - s2y1;
final double a = Vector2D.dotProduct(ux, uy, ux, uy);
final double b = Vector2D.dotProduct(ux, uy, vx, vy);
final double c = Vector2D.dotProduct(vx, vy, vx, vy);
final double d = Vector2D.dotProduct(ux, uy, wx, wy);
final double e = Vector2D.dotProduct(vx, vy, wx, wy);
final double bigD = a * c - b * b;
double svD = bigD;
double tvD = bigD;
double svN;
double tvN;
// compute the line parameters of the two closest points
if (MathUtil.isEpsilonZero(bigD)) {
// the lines are almost parallel
// force using point P0 on segment S1
svN = 0.;
// to prevent possible division by 0.0 later
svD = 1.;
tvN = e;
tvD = c;
} else {
// get the closest points on the infinite lines
svN = b * e - c * d;
tvN = a * e - b * d;
if (svN < 0.) {
// sc < 0 => the s=0 edge is visible
svN = 0.;
tvN = e;
tvD = c;
} else if (svN > svD) {
// sc > 1 => the s=1 edge is visible
svN = svD;
tvN = e + b;
tvD = c;
}
}
if (tvN < 0.) {
// tc < 0 => the t=0 edge is visible
tvN = 0.;
// recompute sc for this edge
if (-d < 0.) {
svN = 0.0;
} else if (-d > a) {
svN = svD;
} else {
svN = -d;
svD = a;
}
} else if (tvN > tvD) {
// tc > 1 => the t=1 edge is visible
tvN = tvD;
// recompute sc for this edge
if ((-d + b) < 0.) {
svN = 0;
} else if ((-d + b) > a) {
svN = svD;
} else {
svN = -d + b;
svD = a;
}
}
final double sc = MathUtil.isEpsilonZero(svN) ? 0. : (svN / svD);
final double tc = MathUtil.isEpsilonZero(tvN) ? 0. : (tvN / tvD);
if (result != null) {
if (sc <= .5) {
result.set(s1x2, s1y2);
} else {
result.set(s1x1, s1y1);
}
}
// get the difference of the two closest points
final double dPx = wx + (sc * ux) - (tc * vx);
final double dPy = wy + (sc * uy) - (tc * vy);
return dPx * dPx + dPy * dPy;
}
/** Compute the intersection of two lines specified
* by the specified points and vectors.
*
* @param x1 horizontal position of the first point of the line.
* @param y1 vertical position of the first point of the line.
* @param x2 horizontal position of the second point of the line.
* @param y2 vertical position of the second point of the line.
* @param x3 horizontal position of the first point of the line.
* @param y3 vertical position of the first point of the line.
* @param x4 horizontal position of the second point of the line.
* @param y4 vertical position of the second point of the line.
* @param result the intersection point.
* @return <code>true</code> if there is an intersection.
* @deprecated since 13.0, see {@link #findsLineLineIntersection(double,
* double, double, double, double, double, double, double, Point2D)}
*/
@Deprecated
@SuppressWarnings("checkstyle:parameternumber")
static boolean computeLineLineIntersection(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4,
Point2D<?, ?> result) {
return findsLineLineIntersection(x1, y1, x2, y2, x3, y3, x4, y4, result);
}
/** Compute the intersection of two lines specified
* by the specified points and vectors.
*
* @param x1 horizontal position of the first point of the line.
* @param y1 vertical position of the first point of the line.
* @param x2 horizontal position of the second point of the line.
* @param y2 vertical position of the second point of the line.
* @param x3 horizontal position of the first point of the line.
* @param y3 vertical position of the first point of the line.
* @param x4 horizontal position of the second point of the line.
* @param y4 vertical position of the second point of the line.
* @param result the intersection point.
* @return <code>true</code> if there is an intersection.
*/
@SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"})
static boolean findsLineLineIntersection(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4,
Point2D<?, ?> result) {
assert result != null : AssertMessages.notNullParameter(8);
final double x21 = x2 - x1;
final double x43 = x4 - x3;
final double y21 = y2 - y1;
final double y43 = y4 - y3;
final double denom = y43 * x21 - x43 * y21;
if (denom == 0.) {
return false;
}
final double x13 = x1 - x3;
final double y13 = y1 - y3;
double intersectionFactor1 = x43 * y13 - y43 * x13;
final double intersectionFactor2 = x21 * y13 - y21 * x13;
if (intersectionFactor1 == intersectionFactor2) {
return false;
}
intersectionFactor1 = intersectionFactor1 / denom;
result.set(
x1 + intersectionFactor1 * x21,
y1 + intersectionFactor1 * y21);
return true;
}
/**
* Replies the position factory for the intersection point between two lines.
*
* <p>Let line equations for L1 and L2:<br>
* <code>L1: P1 + factor1 * (P2-P1)</code><br>
* <code>L2: P3 + factor2 * (P4-P3)</code><br>
* If lines are intersecting, then<br>
* <code>P1 + factor1 * (P2-P1) = P3 + factor2 * (P4-P3)</code>
*
* <p>This function computes and replies <code>factor1</code>.
*
* @param x1
* is the X coordinate of the first point of the first line.
* @param y1
* is the Y coordinate of the first point of the first line.
* @param x2
* is the X coordinate of the second point of the first line.
* @param y2
* is the Y coordinate of the second point of the first line.
* @param x3
* is the X coordinate of the first point of the second line.
* @param y3
* is the Y coordinate of the first point of the second line.
* @param x4
* is the X coordinate of the second point of the second line.
* @param y4
* is the Y coordinate of the second point of the second line.
* @return <code>factor1</code> or {@link Double#NaN} if no intersection.
* @deprecated since 13.0, see {@link #calculatesLineLineIntersectionFactor(double,
* double, double, double, double, double, double, double)}
*/
@Deprecated
@Pure
static double computeLineLineIntersectionFactor(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4) {
return calculatesLineLineIntersectionFactor(x1, y1, x2, y2, x3, y3, x4, y4);
}
/**
* Replies the position factory for the intersection point between two lines.
*
* <p>Let line equations for L1 and L2:<br>
* <code>L1: P1 + factor1 * (P2-P1)</code><br>
* <code>L2: P3 + factor2 * (P4-P3)</code><br>
* If lines are intersecting, then<br>
* <code>P1 + factor1 * (P2-P1) = P3 + factor2 * (P4-P3)</code>
*
* <p>This function computes and replies <code>factor1</code>.
*
* @param x1
* is the X coordinate of the first point of the first line.
* @param y1
* is the Y coordinate of the first point of the first line.
* @param x2
* is the X coordinate of the second point of the first line.
* @param y2
* is the Y coordinate of the second point of the first line.
* @param x3
* is the X coordinate of the first point of the second line.
* @param y3
* is the Y coordinate of the first point of the second line.
* @param x4
* is the X coordinate of the second point of the second line.
* @param y4
* is the Y coordinate of the second point of the second line.
* @return <code>factor1</code> or {@link Double#NaN} if no intersection.
*/
@Pure
static double calculatesLineLineIntersectionFactor(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4) {
final double vx1 = x2 - x1;
final double vy1 = y2 - y1;
final double vx2 = x4 - x3;
final double vy2 = y4 - y3;
// determinant is zero when parallel = det(L1, L2)
final double det = Vector2D.perpProduct(vx1, vy1, vx2, vy2);
if (det == 0.) {
return Double.NaN;
}
// Given line equations:
// Pa = P1 + ua (P2-P1), and
// Pb = P3 + ub (P4-P3)
// and
// V = (P1-P3)
// then
// ua = det(L2, V) / det(L1, L2)
// ub = det(L1, V) / det(L1, L2)
return Vector2D.perpProduct(vx2, vy2, x1 - x3, y1 - y3) / det;
}
/**
* Replies the projection a point on a segment.
*
* @param px
* is the coordiante of the point to project
* @param py
* is the coordiante of the point to project
* @param s1x
* is the x-coordinate of the first line point.
* @param s1y
* is the y-coordinate of the first line point.
* @param s2x
* is the x-coordinate of the second line point.
* @param s2y
* is the y-coordinate of the second line point.
* @return the projection of the specified point on the line. If
* equal to {@code 0}, the projection is equal to the first segment point.
* If equal to {@code 1}, the projection is equal to the second segment point.
* If inside {@code ]0;1[}, the projection is between the two segment points.
* If inside {@code ]-inf;0[}, the projection is outside on the side of the
* first segment point. If inside {@code ]1;+inf[}, the projection is
* outside on the side of the second segment point.
* @deprecated since 13.0, see {@link #findsProjectedPointPointLine(double, double, double, double, double, double)}
*/
@Deprecated
@Pure
static double computeProjectedPointOnLine(double px, double py, double s1x, double s1y, double s2x, double s2y) {
return findsProjectedPointPointLine(px, py, s1x, s1y, s2x, s2y);
}
/**
* Replies the projection a point on a segment.
*
* @param px
* is the coordiante of the point to project
* @param py
* is the coordiante of the point to project
* @param s1x
* is the x-coordinate of the first line point.
* @param s1y
* is the y-coordinate of the first line point.
* @param s2x
* is the x-coordinate of the second line point.
* @param s2y
* is the y-coordinate of the second line point.
* @return the projection of the specified point on the line. If
* equal to {@code 0}, the projection is equal to the first segment point.
* If equal to {@code 1}, the projection is equal to the second segment point.
* If inside {@code ]0;1[}, the projection is between the two segment points.
* If inside {@code ]-inf;0[}, the projection is outside on the side of the
* first segment point. If inside {@code ]1;+inf[}, the projection is
* outside on the side of the second segment point.
*/
@Pure
static double findsProjectedPointPointLine(double px, double py, double s1x, double s1y, double s2x, double s2y) {
final double vx = s2x - s1x;
final double vy = s2y - s1y;
final double numerator = (px - s1x) * vx + (py - s1y) * vy;
final double denomenator = vx * vx + vy * vy;
return numerator / denomenator;
}
/**
* Replies the relative distance from the given point to the given line.
* The replied distance may be negative, depending on which side of
* the line the point is.
*
* @param x1
* the X coordinate of the start point of the specified line segment
* @param y1
* the Y coordinate of the start point of the specified line segment
* @param x2
* the X coordinate of the end point of the specified line segment
* @param y2
* the Y coordinate of the end point of the specified line segment
* @param px
* the X coordinate of the specified point to be compared with the specified line segment
* @param py
* the Y coordinate of the specified point to be compared with the specified line segment
* @return the positive or negative distance from the point to the line
* @see #ccw(double, double, double, double, double, double, double)
* @see #findsSideLinePoint(double, double, double, double, double, double, double)
* @deprecated since 13.0, see {@link #calculatesRelativeDistanceLinePoint(double, double, double,
* double, double, double)}
*/
@Deprecated
@Pure
static double computeRelativeDistanceLinePoint(double x1, double y1, double x2, double y2, double px, double py) {
return calculatesRelativeDistanceLinePoint(x1, y1, x2, y2, px, py);
}
/**
* Replies the relative distance from the given point to the given line.
* The replied distance may be negative, depending on which side of
* the line the point is.
*
* @param x1
* the X coordinate of the start point of the specified line segment
* @param y1
* the Y coordinate of the start point of the specified line segment
* @param x2
* the X coordinate of the end point of the specified line segment
* @param y2
* the Y coordinate of the end point of the specified line segment
* @param px
* the X coordinate of the specified point to be compared with the specified line segment
* @param py
* the Y coordinate of the specified point to be compared with the specified line segment
* @return the positive or negative distance from the point to the line
* @see #ccw(double, double, double, double, double, double, double)
* @see #findsSideLinePoint(double, double, double, double, double, double, double)
*/
@Pure
static double calculatesRelativeDistanceLinePoint(double x1, double y1, double x2, double y2, double px, double py) {
final double x21 = x2 - x1;
final double y21 = y2 - y1;
final double denomenator = x21 * x21 + y21 * y21;
if (denomenator == 0.) {
return Point2D.getDistancePointPoint(px, py, x1, y1);
}
final double factor = ((y1 - py) * x21 - (x1 - px) * y21) / denomenator;
return factor * Math.sqrt(denomenator);
}
/**
* Replies the intersection point between two segments.
*
* @param x1
* is the X coordinate of the first point of the first segment.
* @param y1
* is the Y coordinate of the first point of the first segment.
* @param x2
* is the X coordinate of the second point of the first segment.
* @param y2
* is the Y coordinate of the second point of the first segment.
* @param x3
* is the X coordinate of the first point of the second segment.
* @param y3
* is the Y coordinate of the first point of the second segment.
* @param x4
* is the X coordinate of the second point of the second segment.
* @param y4
* is the Y coordinate of the second point of the second segment.
* @param result the intersection point.
* @return <code>true</code> if an intersection exists.
* @deprecated since 13.0, se {@link #findsSegmentSegmentIntersection(double,
* double, double, double, double, double, double, double, Point2D)}
*/
@Deprecated
@SuppressWarnings("checkstyle:parameternumber")
static boolean computeSegmentSegmentIntersection(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4,
Point2D<?, ?> result) {
return findsSegmentSegmentIntersection(x1, y1, x2, y2, x3, y3, x4, y4, result);
}
/**
* Replies the intersection point between two segments.
*
* @param x1
* is the X coordinate of the first point of the first segment.
* @param y1
* is the Y coordinate of the first point of the first segment.
* @param x2
* is the X coordinate of the second point of the first segment.
* @param y2
* is the Y coordinate of the second point of the first segment.
* @param x3
* is the X coordinate of the first point of the second segment.
* @param y3
* is the Y coordinate of the first point of the second segment.
* @param x4
* is the X coordinate of the second point of the second segment.
* @param y4
* is the Y coordinate of the second point of the second segment.
* @param result the intersection point.
* @return <code>true</code> if an intersection exists.
*/
@SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"})
static boolean findsSegmentSegmentIntersection(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4,
Point2D<?, ?> result) {
assert result != null : AssertMessages.notNullParameter(8);
final double m = calculatesSegmentSegmentIntersectionFactor(x1, y1, x2, y2, x3, y3, x4, y4);
if (Double.isNaN(m)) {
return false;
}
result.set(x1 + m * (x2 - x1), y1 + m * (y2 - y1));
return true;
}
/**
* Replies one position factor for the intersection point between two lines.
*
* <p>Let line equations for L1 and L2:<br>
* <code>L1: P1 + factor1 * (P2-P1)</code><br>
* <code>L2: P3 + factor2 * (P4-P3)</code><br>
* If lines are intersecting, then<br>
* <code>P1 + factor1 * (P2-P1) = P3 + factor2 * (P4-P3)</code>
*
* <p>This function computes and replies <code>factor1</code>.
*
* @param x1
* is the X coordinate of the first point of the first segment.
* @param y1
* is the Y coordinate of the first point of the first segment.
* @param x2
* is the X coordinate of the second point of the first segment.
* @param y2
* is the Y coordinate of the second point of the first segment.
* @param x3
* is the X coordinate of the first point of the second segment.
* @param y3
* is the Y coordinate of the first point of the second segment.
* @param x4
* is the X coordinate of the second point of the second segment.
* @param y4
* is the Y coordinate of the second point of the second segment.
* @return <code>factor1</code> or {@link Double#NaN} if no intersection.
* @deprecated since 13.0, see {@link #calculatesSegmentSegmentIntersectionFactor(double,
* double, double, double, double, double, double, double)}
*/
@Deprecated
@Pure
static double computeSegmentSegmentIntersectionFactor(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4) {
return calculatesSegmentSegmentIntersectionFactor(x1, y1, x2, y2, x3, y3, x4, y4);
}
/**
* Replies one position factor for the intersection point between two lines.
*
* <p>Let line equations for L1 and L2:<br>
* <code>L1: P1 + factor1 * (P2-P1)</code><br>
* <code>L2: P3 + factor2 * (P4-P3)</code><br>
* If lines are intersecting, then<br>
* <code>P1 + factor1 * (P2-P1) = P3 + factor2 * (P4-P3)</code>
*
* <p>This function computes and replies <code>factor1</code>.
*
* @param x1
* is the X coordinate of the first point of the first segment.
* @param y1
* is the Y coordinate of the first point of the first segment.
* @param x2
* is the X coordinate of the second point of the first segment.
* @param y2
* is the Y coordinate of the second point of the first segment.
* @param x3
* is the X coordinate of the first point of the second segment.
* @param y3
* is the Y coordinate of the first point of the second segment.
* @param x4
* is the X coordinate of the second point of the second segment.
* @param y4
* is the Y coordinate of the second point of the second segment.
* @return <code>factor1</code> or {@link Double#NaN} if no intersection.
*/
@Pure
static double calculatesSegmentSegmentIntersectionFactor(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4) {
final double vx1 = x2 - x1;
final double vy1 = y2 - y1;
final double vx2 = x4 - x3;
final double vy2 = y4 - y3;
// determinant is zero when parallel = det(L1, L2)
final double det = Vector2D.perpProduct(vx1, vy1, vx2, vy2);
if (det == 0.) {
return Double.NaN;
}
// Given line equations:
// Pa = P1 + ua (P2-P1), and
// Pb = P3 + ub (P4-P3)
// and
// V = (P1-P3)
// then
// ua = det(L2, V) / det(L1, L2)
// ub = det(L1, V) / det(L1, L2)
final double vx3 = x1 - x3;
final double vy3 = y1 - y3;
double intersectionFactor = Vector2D.perpProduct(vx1, vy1, vx3, vy3) / det;
if (intersectionFactor < 0. || intersectionFactor > 1.) {
return Double.NaN;
}
intersectionFactor = Vector2D.perpProduct(vx2, vy2, vx3, vy3) / det;
return (intersectionFactor < 0. || intersectionFactor > 1.) ? Double.NaN : intersectionFactor;
}
/**
* Replies on which side of a line the given point is located.
*
* <p>A return value of 1 indicates that the line segment must turn in the direction
* that takes the positive X axis towards the negative Y axis. In the default
* coordinate system used by Java 2D, this direction is counterclockwise.
*
* <p>A return value of -1 indicates that the line segment must turn in the direction that
* takes the positive X axis towards the positive Y axis. In the default coordinate system,
* this direction is clockwise.
*
* <p>A return value of 0 indicates that the point lies exactly on the line segment. Note that
* an indicator value of 0 is rare and not useful for determining colinearity because of
* floating point rounding issues.
*
* <p>This function uses the equal-to-zero test with the error {@link Math#ulp(double)}.
*
* <p>In opposite of {@link #ccw(double, double, double, double, double, double, double)},
* this function does not try to classify the point if it is colinear
* to the segment. If the point is colinear, O is always returns.
*
* @param x1
* the X coordinate of the start point of the specified line segment
* @param y1
* the Y coordinate of the start point of the specified line segment
* @param x2
* the X coordinate of the end point of the specified line segment
* @param y2
* the Y coordinate of the end point of the specified line segment
* @param px
* the X coordinate of the specified point to be compared with the specified line segment
* @param py
* the Y coordinate of the specified point to be compared with the specified line segment
* @param epsilon approximate epsilon.
* @return an integer that indicates the position of the third specified coordinates with
* respect to the line segment formed by the first two specified coordinates.
* @see #calculatesRelativeDistanceLinePoint(double, double, double, double, double, double)
* @see MathUtil#isEpsilonZero(double)
* @see #ccw(double, double, double, double, double, double, double)
* @deprecated since 13.0, see {@link #findsSideLinePoint(double, double, double, double, double, double, double)}
*/
@Deprecated
@Pure
static int computeSideLinePoint(double x1, double y1, double x2, double y2, double px, double py, double epsilon) {
return findsSideLinePoint(x1, y1, x2, y2, px, py, epsilon);
}
/**
}
* Replies on which side of a line the given point is located.
*
* <p>A return value of 1 indicates that the line segment must turn in the direction
* that takes the positive X axis towards the negative Y axis. In the default
* coordinate system used by Java 2D, this direction is counterclockwise.
*
* <p>A return value of -1 indicates that the line segment must turn in the direction that
* takes the positive X axis towards the positive Y axis. In the default coordinate system,
* this direction is clockwise.
*
* <p>A return value of 0 indicates that the point lies exactly on the line segment. Note that
* an indicator value of 0 is rare and not useful for determining colinearity because of
* floating point rounding issues.
*
* <p>This function uses the equal-to-zero test with the error {@link Math#ulp(double)}.
*
* <p>In opposite of {@link #ccw(double, double, double, double, double, double, double)},
* this function does not try to classify the point if it is colinear
* to the segment. If the point is colinear, O is always returns.
*
* @param x1
* the X coordinate of the start point of the specified line segment
* @param y1
* the Y coordinate of the start point of the specified line segment
* @param x2
* the X coordinate of the end point of the specified line segment
* @param y2
* the Y coordinate of the end point of the specified line segment
* @param px
* the X coordinate of the specified point to be compared with the specified line segment
* @param py
* the Y coordinate of the specified point to be compared with the specified line segment
* @param epsilon approximate epsilon.
* @return an integer that indicates the position of the third specified coordinates with
* respect to the line segment formed by the first two specified coordinates.
* @see #calculatesRelativeDistanceLinePoint(double, double, double, double, double, double)
* @see MathUtil#isEpsilonZero(double)
* @see #ccw(double, double, double, double, double, double, double)
*/
@Pure
static int findsSideLinePoint(double x1, double y1, double x2, double y2, double px, double py, double epsilon) {
final double x21 = x2 - x1;
final double y21 = y2 - y1;
final double xp1 = px - x1;
final double yp1 = py - y1;
double side = xp1 * y21 - yp1 * x21;
if (side != 0. && MathUtil.isEpsilonZero(side, epsilon)) {
side = 0.;
}
return (side < 0) ? -1 : ((side > 0) ? 1 : 0);
}
/** Do an intersection test of two segments for ensuring that the answer of "no intersect" is safe.
*
* <p>If the function replies {@link UncertainIntersection#NO}, we are sure that the two given segments are not
* intersecting. If the function replies {@link UncertainIntersection#PERHAPS}, the two segments may intersects
* (uncertain answer).
*
* <p>This function considers that the ends of the segments 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 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 the type of intersection.
* @see #intersectsSegmentSegmentWithEnds(double, double, double, double, double, double, double, double)
* @deprecated since 13.0, see {@link #findsUncertainIntersectionSegmentSegmentWithEnds(double,
* double, double, double, double, double, double, double)}
*/
@Deprecated
@Pure
static UncertainIntersection getNoSegmentSegmentWithEndsIntersection(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4) {
return findsUncertainIntersectionSegmentSegmentWithEnds(x1, y1, x2, y2, x3, y3, x4, y4);
}
/** Do an intersection test of two segments for ensuring that the answer of "no intersect" is safe.
*
* <p>If the function replies {@link UncertainIntersection#NO}, we are sure that the two given segments are not
* intersecting. If the function replies {@link UncertainIntersection#PERHAPS}, the two segments may intersects
* (uncertain answer).
*
* <p>This function considers that the ends of the segments 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 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 the type of intersection.
* @see #intersectsSegmentSegmentWithEnds(double, double, double, double, double, double, double, double)
*/
@Pure
static UncertainIntersection findsUncertainIntersectionSegmentSegmentWithEnds(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4) {
final double vx1 = x2 - x1;
final double 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.
final double vx2a = x3 - x1;
final double vy2a = y3 - y1;
double f1 = vx2a * vy1 - vy2a * vx1;
final double vx2b = x4 - x1;
final double vy2b = y4 - y1;
double 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
final double sign = f1 * f2;
if (sign < 0) {
return UncertainIntersection.PERHAPS;
}
if (sign > 0) {
return UncertainIntersection.NO;
}
final double squaredLength = vx1 * vx1 + vy1 * vy1;
if (f1 == 0. && f2 == 0.) {
// 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 UncertainIntersection.fromBoolean((f1 >= 0. || f2 >= 0.) && (f1 <= 1. || f2 <= 1.));
}
if (f1 == 0) {
// 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 UncertainIntersection.fromBoolean(f1 >= 0. && f1 <= 1.);
}
if (f2 == 0) {
// 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 UncertainIntersection.fromBoolean(f2 >= 0. && f2 <= 1.);
}
return UncertainIntersection.NO;
}
/** Do an intersection test of two segments for ensuring that the answer of "no intersect" is safe.
* If the function replies <code>true</code>, it may
* This function does not consider that the ends of
* the segments 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 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 the type of intersection.
* @see #intersectsSegmentSegmentWithoutEnds(double, double, double, double, double, double, double, double)
* @deprecated since 13.0, see {@link #findsUncertainIntersectionSegmentSegmentWithoutEnds(double,
* double, double, double, double, double, double, double)}
*/
@Deprecated
@Pure
static UncertainIntersection getNoSegmentSegmentWithoutEndsIntersection(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4) {
return findsUncertainIntersectionSegmentSegmentWithoutEnds(x1, y1, x2, y2, x3, y3, x4, y4);
}
/** Do an intersection test of two segments for ensuring that the answer of "no intersect" is safe.
* If the function replies <code>true</code>, it may
* This function does not consider that the ends of
* the segments 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 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 the type of intersection.
* @see #intersectsSegmentSegmentWithoutEnds(double, double, double, double, double, double, double, double)
*/
@Pure
static UncertainIntersection findsUncertainIntersectionSegmentSegmentWithoutEnds(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4) {
final double vx1 = x2 - x1;
final double vy1 = y2 - y1;
final double vx2a = x3 - x1;
final double vy2a = y3 - y1;
double f1 = vx2a * vy1 - vy2a * vx1;
final double vx2b = x4 - x1;
final double vy2b = y4 - y1;
double 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
final double sign = f1 * f2;
if (sign < 0) {
return UncertainIntersection.PERHAPS;
}
if (sign > 0) {
return UncertainIntersection.NO;
}
if (f1 == 0. && f2 == 0.) {
// Projection of the point on the segment line:
// <0 -> means before first point
// >1 -> means after second point
// otherwhise on the segment.
final double 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 UncertainIntersection.fromBoolean((f1 > 0. || f2 > 0.) && (f1 < 1. || f2 < 1.));
}
return UncertainIntersection.NO;
}
/** Compute the interpolate point between the two points.
*
* @param p1x x coordinate of the first point.
* @param p1y y coordinate of the first point.
* @param p2x x coordinate of the second point.
* @param p2y y coordinate of the second point.
* @param factor is between 0 and 1; 0 for p1, and 1 for p2.
* @param result the interpolate point.
* @deprecated since 13.0, see {@link #interpolates(double, double, double, double, double, Point2D)}
*/
@Deprecated
static void interpolate(double p1x, double p1y, double p2x, double p2y, double factor, Point2D<?, ?> result) {
interpolates(p1x, p1y, p2x, p2y, factor, result);
}
/** Compute the interpolate point between the two points.
*
* @param p1x x coordinate of the first point.
* @param p1y y coordinate of the first point.
* @param p2x x coordinate of the second point.
* @param p2y y coordinate of the second point.
* @param factor is between 0 and 1; 0 for p1, and 1 for p2.
* @param result the interpolate point.
*/
@SuppressWarnings("checkstyle:magicnumber")
static void interpolates(double p1x, double p1y, double p2x, double p2y, double factor, Point2D<?, ?> result) {
assert result != null : AssertMessages.notNullParameter(5);
assert factor >= 0. && factor <= 1. : AssertMessages.outsideRangeInclusiveParameter(4, factor, 0, 1);
final double vx = p2x - p1x;
final double vy = p2y - p1y;
result.set(
p1x + factor * vx,
p1y + factor * vy);
}
/** 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>
*/
@Pure
static boolean intersectsLineLine(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
if (isParallelLines(x1, y1, x2, y2, x3, y3, x4, y4)) {
return 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>
*/
@Pure
static boolean intersectsSegmentLine(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
return (findsSideLinePoint(x3, y3, x4, y4, x1, y1, Double.NaN)
* findsSideLinePoint(x3, y3, x4, y4, x2, y2, Double.NaN)) <= 0;
}
/** 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(double, double, double, double, double, double, double, double)}.
*
* @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(double, double, double, double, double, double, double, double)
*/
@Pure
static boolean intersectsSegmentSegmentWithEnds(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4) {
final UncertainIntersection result = findsUncertainIntersectionSegmentSegmentWithEnds(x1, y1, x2, y2, x3, y3, x4, y4);
if (!result.booleanValue()) {
return result.booleanValue();
}
return findsUncertainIntersectionSegmentSegmentWithEnds(x3, y3, x4, y4, x1, y1, x2, y2).booleanValue();
}
/** 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(double, double, double, double, double, double, double, double)}.
*
* @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(double, double, double, double, double, double, double, double)
*/
@Pure
static boolean intersectsSegmentSegmentWithoutEnds(double x1, double y1, double x2, double y2,
double x3, double y3, double x4, double y4) {
final UncertainIntersection result = findsUncertainIntersectionSegmentSegmentWithoutEnds(x1, y1, x2, y2, x3, y3, x4, y4);
if (!result.booleanValue()) {
return result.booleanValue();
}
return findsUncertainIntersectionSegmentSegmentWithoutEnds(x3, y3, x4, y4, x1, y1, x2, y2).booleanValue();
}
/**
* Replies if two lines are colinear.
*
* <p>The given two lines are described respectivaly by two points, i.e. {@code (x1, y1)}
* and {@code (x2, y2)} for the first line, and {@code (x3, y3)} and {@code (x4, y4)} for the second line.
*
* <p>If you are interested to test if the two lines are parallel, see
* {@link #isParallelLines(double, double, double, double, double, double, double, double)}.
*
* @param x1
* is the X coordinate of the first point of the first line.
* @param y1
* is the Y coordinate of the first point of the first line.
* @param x2
* is the X coordinate of the second point of the first line.
* @param y2
* is the Y coordinate of the second point of the first line.
* @param x3
* is the X coordinate of the first point of the second line.
* @param y3
* is the Y coordinate of the first point of the second line.
* @param x4
* is the X coordinate of the second point of the second line.
* @param y4
* is the Y coordinate of the second point of the second line.
* @return <code>true</code> if the two given lines are collinear.
* @see #isParallelLines(double, double, double, double, double, double, double, double)
* @see Point2D#isCollinearPoints(double, double, double, double, double, double)
*/
@Pure
static boolean isCollinearLines(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
return isParallelLines(x1, y1, x2, y2, x3, y3, x4, y4)
&& Point2D.isCollinearPoints(x1, y1, x2, y2, x3, y3);
}
/**
* Replies if two lines are parallel.
*
* <p>The given two lines are described respectivaly by two points, i.e. {@code (x1, y1)}
* and {@code (x2, y2)} for the first line, and {@code (x3, y3)} and {@code (x4, y4)} for the second line.
*
* <p>If you are interested to test if the two lines are colinear, see
* {@link #isCollinearLines(double, double, double, double, double, double, double, double)}.
*
* @param x1
* is the X coordinate of the first point of the first line.
* @param y1
* is the Y coordinate of the first point of the first line.
* @param x2
* is the X coordinate of the second point of the first line.
* @param y2
* is the Y coordinate of the second point of the first line.
* @param x3
* is the X coordinate of the first point of the second line.
* @param y3
* is the Y coordinate of the first point of the second line.
* @param x4
* is the X coordinate of the second point of the second line.
* @param y4
* is the Y coordinate of the second point of the second line.
* @return <code>true</code> if the two given lines are parallel.
* @see #isCollinearLines(double, double, double, double, double, double, double, double)
*/
@Pure
static boolean isParallelLines(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
return Vector2D.isCollinearVectors(x2 - x1, y2 - y1, x4 - x3, y4 - y3);
}
/** Replies if a point is closed to a line.
*
* @param x1 horizontal location of the first-line begining.
* @param y1 vertical location of the first-line ending.
* @param x2 horizontal location of the second-line begining.
* @param y2 vertical location of the second-line ending.
* @param x horizontal location of the point.
* @param y vertical location of the point.
* @param hitDistance is the maximal hitting distance.
* @return <code>true</code> if the point and the
* line have closed locations.
*/
@Pure
@SuppressWarnings("checkstyle:magicnumber")
static boolean isPointClosedToLine(double x1, double y1,
double x2, double y2,
double x, double y, double hitDistance) {
assert hitDistance >= 0. : AssertMessages.positiveOrZeroParameter(6);
return calculatesDistanceLinePoint(x1, y1, x2, y2, x, y) < hitDistance;
}
/** Replies if a point is closed to a segment.
*
* @param x1 horizontal location of the first-segment begining.
* @param y1 vertical location of the first-segment ending.
* @param x2 horizontal location of the second-segment begining.
* @param y2 vertical location of the second-segment ending.
* @param x horizontal location of the point.
* @param y vertical location of the point.
* @param hitDistance is the maximal hitting distance.
* @return <code>true</code> if the point and the
* line have closed locations.
*/
@Pure
@SuppressWarnings("checkstyle:magicnumber")
static boolean isPointClosedToSegment(double x1, double y1,
double x2, double y2,
double x, double y, double hitDistance) {
assert hitDistance >= 0. : AssertMessages.positiveOrZeroParameter(6);
return calculatesDistanceSegmentPoint(x1, y1, x2, y2, x, y) < hitDistance;
}
@Override
default void clear() {
set(0, 0, 0, 0);
}
/** Clip the segment against the clipping rectangle
* according to the <a href="http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm">Cohen-Sutherland algorithm</a>.
*
* @param rxmin is the min of the coordinates of the rectangle.
* @param rymin is the min of the coordinates of the rectangle.
* @param rxmax is the max of the coordinates of the rectangle.
* @param rymax is the max of the coordinates of the rectangle.
* @return <code>true</code> if the segment has an intersection with the
* rectangle and the segment was clipped; <code>false</code> if the segment
* does not intersect the rectangle.
*/
@Pure
default boolean clipToRectangle(double rxmin, double rymin, double rxmax, double rymax) {
assert rxmin <= rxmax : AssertMessages.lowerEqualParameters(0, rxmin, 2, rxmax);
assert rymin <= rymax : AssertMessages.lowerEqualParameters(1, rymin, 3, rymax);
double x0 = getX1();
double y0 = getY1();
double x1 = getX2();
double y1 = getY2();
int code1 = MathUtil.getCohenSutherlandCode(x0, y0, rxmin, rymin, rxmax, rymax);
int code2 = MathUtil.getCohenSutherlandCode(x1, y1, rxmin, rymin, rxmax, rymax);
boolean accept = false;
boolean cont = true;
double x = 0;
double y = 0;
while (cont) {
if ((code1 | code2) == 0) {
// Bitwise OR is 0. Trivially accept and get out of loop
accept = true;
cont = false;
} else if ((code1 & code2) != 0) {
// Bitwise AND is not 0. Trivially reject and get out of loop
cont = false;
} else {
// failed both tests, so calculate the line segment to clip
// from an outside point to an intersection with clip edge
// At least one endpoint is outside the clip rectangle; pick it.
int code3 = code1 != 0 ? code1 : code2;
// Now find the intersection point;
// use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
if ((code3 & MathConstants.COHEN_SUTHERLAND_TOP) != 0) {
// point is above the clip rectangle
x = x0 + (x1 - x0) * (rymax - y0) / (y1 - y0);
y = rymax;
} else if ((code3 & MathConstants.COHEN_SUTHERLAND_BOTTOM) != 0) {
// point is below the clip rectangle
x = x0 + (x1 - x0) * (rymin - y0) / (y1 - y0);
y = rymin;
} else if ((code3 & MathConstants.COHEN_SUTHERLAND_RIGHT) != 0) {
// point is to the right of clip rectangle
y = y0 + (y1 - y0) * (rxmax - x0) / (x1 - x0);
x = rxmax;
} else if ((code3 & MathConstants.COHEN_SUTHERLAND_LEFT) != 0) {
// point is to the left of clip rectangle
y = y0 + (y1 - y0) * (rxmin - x0) / (x1 - x0);
x = rxmin;
} else {
code3 = 0;
}
if (code3 != 0) {
// Now we move outside point to intersection point to clip
// and get ready for next pass.
if (code3 == code1) {
x0 = x;
y0 = y;
code1 = MathUtil.getCohenSutherlandCode(x0, y0, rxmin, rymin, rxmax, rymax);
} else {
x1 = x;
y1 = y;
code2 = MathUtil.getCohenSutherlandCode(x1, y1, rxmin, rymin, rxmax, rymax);
}
}
}
}
if (accept) {
set(x0, y0, x1, y1);
}
return accept;
}
/** {@inheritDoc}
*
* <p>This function uses the equal-to-zero test with the error {@link Math#ulp(double)}.
*
* @see MathUtil#isEpsilonZero(double)
*/
@Override
default boolean contains(double x, double y) {
return MathUtil.isEpsilonZero(
calculatesDistanceSquaredSegmentPoint(
getX1(), getY1(),
getX2(), getY2(),
x, y));
}
@Pure
@Override
default boolean contains(Rectangle2afp<?, ?, ?, ?, ?, ?> rectangle) {
return false;
}
@Pure
@Override
default boolean contains(Shape2D<?, ?, ?, ?, ?, ?> shape) {
return false;
}
@Pure
@Override
default boolean equalsToShape(IT shape) {
if (shape == null) {
return false;
}
if (shape == this) {
return true;
}
return getX1() == shape.getX1()
&& getY1() == shape.getY1()
&& getX2() == shape.getX2()
&& getY2() == shape.getY2();
}
@Pure
@Override
default P getClosestPointTo(Circle2afp<?, ?, ?, ?, ?, ?> circle) {
assert circle != null : AssertMessages.notNullParameter();
return getClosestPointTo(circle.getCenter());
}
@Override
@Unefficient
default P getClosestPointTo(Ellipse2afp<?, ?, ?, ?, ?, ?> ellipse) {
assert ellipse != null : AssertMessages.notNullParameter();
final P point = getGeomFactory().newPoint();
Path2afp.findsClosestPointPathIteratorPathIterator(getPathIterator(), ellipse.getPathIterator(), point);
return point;
}
@Override
default P getClosestPointTo(OrientedRectangle2afp<?, ?, ?, ?, ?, ?> orientedRectngle) {
assert orientedRectngle != null : AssertMessages.notNullParameter();
// Change of basis to the local basis of the oriented rectangle
final double cx = orientedRectngle.getCenterX();
final double cy = orientedRectngle.getCenterY();
final double sx1 = getX1() - cx;
final double sy1 = getY1() - cy;
final double sx2 = getX2() - cx;
final double sy2 = getY2() - cy;
final double rx = orientedRectngle.getFirstAxisX();
final double ry = orientedRectngle.getFirstAxisY();
double x1 = OrientedRectangle2afp.findsVectorProjectionRAxisVector(rx, ry, sx1, sy1);
double y1 = OrientedRectangle2afp.findsVectorProjectionSAxisVector(rx, ry, sx1, sy1);
final double x2 = OrientedRectangle2afp.findsVectorProjectionRAxisVector(rx, ry, sx2, sy2);
final double y2 = OrientedRectangle2afp.findsVectorProjectionSAxisVector(rx, ry, sx2, sy2);
final double extent1 = orientedRectngle.getFirstAxisExtent();
final double extent2 = orientedRectngle.getSecondAxisExtent();
final P point = getGeomFactory().newPoint();
findsClosestPointSegmentRectangle(x1, y1, x2, y2, -extent1, -extent2, 2. * extent1, 2. * extent2, point);
// Invert change of basis
x1 = cx + point.getX() * rx - point.getY() * ry;
y1 = cy + point.getX() * ry + point.getY() * rx;
point.set(x1, y1);
return point;
}
@Override
default P getClosestPointTo(Parallelogram2afp<?, ?, ?, ?, ?, ?> parallelogram) {
assert parallelogram != null : AssertMessages.notNullParameter();
// Change of basis to the local basis of the oriented rectangle
final double cx = parallelogram.getCenterX();
final double cy = parallelogram.getCenterY();
final double sx1 = getX1() - cx;
final double sy1 = getY1() - cy;
final double sx2 = getX2() - cx;
final double sy2 = getY2() - cy;
final double rx = parallelogram.getFirstAxisX();
final double ry = parallelogram.getFirstAxisY();
final double sx = parallelogram.getSecondAxisX();
final double sy = parallelogram.getSecondAxisY();
final double extent1 = parallelogram.getFirstAxisExtent();
final double extent2 = parallelogram.getSecondAxisExtent();
final P point = getGeomFactory().newPoint();
//
final double x1 = Parallelogram2afp.findsVectorProjectionRAxisPoint(rx, ry, sx, sy, sx1, sy1);
final double y1 = Parallelogram2afp.findsVectorProjectionSAxisVector(rx, ry, sx, sy, sx1, sy1);
final double x2 = Parallelogram2afp.findsVectorProjectionRAxisPoint(rx, ry, sx, sy, sx2, sy2);
final double y2 = Parallelogram2afp.findsVectorProjectionSAxisVector(rx, ry, sx, sy, sx2, sy2);
final int code1 = MathUtil.getCohenSutherlandCode(x1, y1, -extent1, -extent2, extent1, extent2);
final int code2 = MathUtil.getCohenSutherlandCode(x2, y2, -extent1, -extent2, extent1, extent2);
final Point2D<?, ?> tmp1 = new InnerComputationPoint2afp();
final Point2D<?, ?> tmp2 = new InnerComputationPoint2afp();
final int zone = Rectangle2afp.reducesCohenSutherlandZoneRectangleSegment(
-extent1, -extent2, extent1, extent2,
x1, y1, x2, y2,
code1, code2,
tmp1, tmp2);
if ((zone & MathConstants.COHEN_SUTHERLAND_LEFT) != 0) {
findsClosestPointSegmentSegment(
sx1, sy1, sx2, sy2,
-rx * extent1 + sx * extent2, -ry * extent1 + sy * extent2,
-rx * extent1 - sx * extent2, -ry * extent1 - sy * extent2,
point);
} else if ((zone & MathConstants.COHEN_SUTHERLAND_RIGHT) != 0) {
findsClosestPointSegmentSegment(
sx1, sy1, sx2, sy2,
rx * extent1 + sx * extent2, ry * extent1 + sy * extent2,
rx * extent1 - sx * extent2, ry * extent1 - sy * extent2,
point);
} else if ((zone & MathConstants.COHEN_SUTHERLAND_BOTTOM) != 0) {
findsClosestPointSegmentSegment(
sx1, sy1, sx2, sy2,
-rx * extent1 - sx * extent2, -ry * extent1 - sy * extent2,
rx * extent1 - sx * extent2, ry * extent1 - sy * extent2,
point);
} else if ((zone & MathConstants.COHEN_SUTHERLAND_TOP) != 0) {
findsClosestPointSegmentSegment(
sx1, sy1, sx2, sy2,
-rx * extent1 + sx * extent2, -ry * extent1 + sy * extent2,
rx * extent1 + sx * extent2, ry * extent1 + sy * extent2,
point);
} else {
findsClosestPointSegmentPoint(
tmp1.getX(), tmp1.getY(), tmp2.getX(), tmp2.getY(),
0, 0,
point);
}
// Invert change of basis
point.add(cx, cy);
return point;
}
@Override
@Unefficient
default P getClosestPointTo(Path2afp<?, ?, ?, ?, ?, ?> path) {
assert path != null : AssertMessages.notNullParameter();
final P point = getGeomFactory().newPoint();
Path2afp.findsClosestPointPathIteratorPathIterator(getPathIterator(), path.getPathIterator(), point);
return point;
}
@Pure
@Override
default P getClosestPointTo(Point2D<?, ?> pt) {
assert pt != null : AssertMessages.notNullParameter();
final P point = getGeomFactory().newPoint();
Segment2afp.findsClosestPointSegmentPoint(
getX1(), getY1(),
getX2(), getY2(),
pt.getX(), pt.getY(),
point);
return point;
}
@Pure
@Override
default P getClosestPointTo(Rectangle2afp<?, ?, ?, ?, ?, ?> rectangle) {
assert rectangle != null : AssertMessages.notNullParameter();
final P point = getGeomFactory().newPoint();
findsClosestPointSegmentRectangle(getX1(), getY1(), getX2(), getY2(),
rectangle.getMinX(), rectangle.getMinY(), rectangle.getWidth(), rectangle.getHeight(), point);
return point;
}
@Override
@Unefficient
default P getClosestPointTo(RoundRectangle2afp<?, ?, ?, ?, ?, ?> roundRectangle) {
assert roundRectangle != null : AssertMessages.notNullParameter();
final P point = getGeomFactory().newPoint();
Path2afp.findsClosestPointPathIteratorPathIterator(getPathIterator(), roundRectangle.getPathIterator(), point);
return point;
}
@Pure
@Override
default P getClosestPointTo(Segment2afp<?, ?, ?, ?, ?, ?> segment) {
assert segment != null : AssertMessages.notNullParameter();
final P point = getGeomFactory().newPoint();
findsClosestPointSegmentSegment(getX1(), getY1(), getX2(), getY2(),
segment.getX1(), segment.getY1(), segment.getX2(), segment.getY2(), point);
return point;
}
@Pure
@Override
@SuppressWarnings("checkstyle:magicnumber")
default P getClosestPointTo(Triangle2afp<?, ?, ?, ?, ?, ?> triangle) {
assert triangle != null : AssertMessages.notNullParameter();
final double[] segments = new double[] {
triangle.getX1(), triangle.getY1(), triangle.getX2(), triangle.getY2(),
triangle.getX2(), triangle.getY2(), triangle.getX3(), triangle.getY3(),
triangle.getX3(), triangle.getY3(), triangle.getX1(), triangle.getY1(),
};
final P point = getGeomFactory().newPoint();
final P tmp = getGeomFactory().newPoint();
double minDistance = Double.POSITIVE_INFINITY;
final double ox1 = getX1();
final double oy1 = getY1();
final double ox2 = getX2();
final double oy2 = getY2();
for (int i = 0; i < segments.length; i += 4) {
final double x1 = segments[i];
final double y1 = segments[i + 1];
final double x2 = segments[i + 2];
final double y2 = segments[i + 3];
final double distance = findsClosestPointSegmentSegment(ox1, oy1, ox2, oy2, x1, y1, x2, y2, tmp);
if (distance < minDistance) {
minDistance = distance;
point.set(tmp);
}
}
return point;
}
@Pure
@Override
default double getDistanceL1(Point2D<?, ?> pt) {
assert pt != null : AssertMessages.notNullParameter();
double ratio = findsProjectedPointPointLine(pt.getX(), pt.getY(), getX1(), getY1(), getX2(), getY2());
ratio = MathUtil.clamp(ratio, 0, 1);
final double vx = (getX2() - getX1()) * ratio;
final double vy = (getY2() - getY1()) * ratio;
return Math.abs(getX1() + vx - pt.getX())
+ Math.abs(getY1() + vy - pt.getY());
}
@Pure
@Override
default double getDistanceLinf(Point2D<?, ?> pt) {
assert pt != null : AssertMessages.notNullParameter();
double ratio = findsProjectedPointPointLine(pt.getX(), pt.getY(), getX1(), getY1(), getX2(), getY2());
ratio = MathUtil.clamp(ratio, 0, 1);
final double vx = (getX2() - getX1()) * ratio;
final double vy = (getY2() - getY1()) * ratio;
return Math.max(
Math.abs(this.getX1() + vx - pt.getX()),
Math.abs(this.getY1() + vy - pt.getY()));
}
@Pure
@Override
default double getDistanceSquared(Point2D<?, ?> pt) {
assert pt != null : AssertMessages.notNullParameter();
return calculatesDistanceSquaredSegmentPoint(
getX1(), getY1(),
getX2(), getY2(),
pt.getX(), pt.getY());
}
@Override
@Pure
default double getDistanceSquared(Segment2afp<?, ?, ?, ?, ?, ?> segment) {
assert segment != null : AssertMessages.notNullParameter();
return calculatesDistanceSquaredSegmentSegment(getX1(), getY1(), getX2(), getY2(),
segment.getX1(), segment.getY1(), segment.getX2(), segment.getY2());
}
@Pure
@Override
default P getFarthestPointTo(Point2D<?, ?> pt) {
assert pt != null : AssertMessages.notNullParameter();
final P point = getGeomFactory().newPoint();
Segment2afp.findsFarthestPointSegmentPoint(
getX1(), getY1(),
getX2(), getY2(),
pt.getX(), pt.getY(),
point);
return point;
}
/** Replies the length of the segment.
*
* @return the length.
*/
@Pure
default double getLength() {
return Point2D.getDistancePointPoint(getX1(), getY1(), getX2(), getY2());
}
/** Replies the squared length of the segment.
*
* @return the squared length.
*/
@Pure
default double getLengthSquared() {
return Point2D.getDistanceSquaredPointPoint(getX1(), getY1(), getX2(), getY2());
}
/** Replies the first point.
*
* @return the first point.
*/
@Pure
default P getP1() {
return getGeomFactory().newPoint(getX1(), getY1());
}
/** Replies the second point.
*
* @return the second point.
*/
@Pure
default P getP2() {
return getGeomFactory().newPoint(getX2(), getY2());
}
@Pure
@Override
default PathIterator2afp<IE> getPathIterator(Transform2D transform) {
return new SegmentPathIterator<>(this, transform);
}
/** Replies the X of the first point.
*
* @return the x of the first point.
*/
@Pure
double getX1();
/** Replies the X of the second point.
*
* @return the x of the second point.
*/
@Pure
double getX2();
/** Replies the Y of the first point.
*
* @return the y of the first point.
*/
@Pure
double getY1();
/** Replies the Y of the second point.
*
* @return the y of the second point.
*/
@Pure
double getY2();
@Pure
@Override
default boolean intersects(Circle2afp<?, ?, ?, ?, ?, ?> circle) {
assert circle != null : AssertMessages.notNullParameter();
return Circle2afp.intersectsCircleSegment(
circle.getX(), circle.getY(),
circle.getRadius(),
getX1(), getY1(),
getX2(), getY2());
}
@Pure
@Override
default boolean intersects(Ellipse2afp<?, ?, ?, ?, ?, ?> ellipse) {
assert ellipse != null : AssertMessages.notNullParameter();
return Ellipse2afp.intersectsEllipseSegment(
ellipse.getMinX(), ellipse.getMinY(),
ellipse.getWidth(), ellipse.getHeight(),
getX1(), getY1(),
getX2(), getY2(), false);
}
@Pure
@Override
default boolean intersects(MultiShape2afp<?, ?, ?, ?, ?, ?, ?> multishape) {
assert multishape != null : AssertMessages.notNullParameter();
return multishape.intersects(this);
}
@Pure
@Override
default boolean intersects(OrientedRectangle2afp<?, ?, ?, ?, ?, ?> orientedRectangle) {
assert orientedRectangle != null : AssertMessages.notNullParameter();
return OrientedRectangle2afp.intersectsOrientedRectangleSegment(
orientedRectangle.getCenterX(), orientedRectangle.getCenterY(),
orientedRectangle.getFirstAxisX(), orientedRectangle.getFirstAxisY(), orientedRectangle.getFirstAxisExtent(),
orientedRectangle.getSecondAxisExtent(),
getX1(), getY1(), getX2(), getY2());
}
@Pure
@Override
default boolean intersects(Parallelogram2afp<?, ?, ?, ?, ?, ?> parallelogram) {
assert parallelogram != null : AssertMessages.notNullParameter();
return Parallelogram2afp.intersectsParallelogramSegment(
parallelogram.getCenterX(), parallelogram.getCenterY(),
parallelogram.getFirstAxisX(), parallelogram.getFirstAxisY(), parallelogram.getFirstAxisExtent(),
parallelogram.getSecondAxisX(), parallelogram.getSecondAxisY(), parallelogram.getSecondAxisExtent(),
getX1(), getY1(), getX2(), getY2());
}
@Pure
@Override
default boolean intersects(PathIterator2afp<?> iterator) {
assert iterator != null : AssertMessages.notNullParameter();
final int mask = iterator.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 2;
final int crossings = Path2afp.calculatesCrossingsPathIteratorSegmentShadow(
0,
iterator,
getX1(), getY1(), getX2(), getY2(),
CrossingComputationType.SIMPLE_INTERSECTION_WHEN_NOT_POLYGON);
return crossings == MathConstants.SHAPE_INTERSECTS
|| (crossings & mask) != 0;
}
@Pure
@Override
default boolean intersects(Rectangle2afp<?, ?, ?, ?, ?, ?> rectangle) {
assert rectangle != null : AssertMessages.notNullParameter();
return Rectangle2afp.intersectsRectangleSegment(
rectangle.getMinX(), rectangle.getMinY(),
rectangle.getMaxX(), rectangle.getMaxY(),
getX1(), getY1(),
getX2(), getY2());
}
@Pure
@Override
default boolean intersects(RoundRectangle2afp<?, ?, ?, ?, ?, ?> roundRectangle) {
assert roundRectangle != null : AssertMessages.notNullParameter();
return RoundRectangle2afp.intersectsRoundRectangleSegment(
roundRectangle.getMinX(), roundRectangle.getMinY(), roundRectangle.getMaxX(),
roundRectangle.getMaxY(), roundRectangle.getArcWidth(), roundRectangle.getArcHeight(),
getX1(), getY1(), getX2(), getY2());
}
@Pure
@Override
default boolean intersects(Segment2afp<?, ?, ?, ?, ?, ?> segment) {
assert segment != null : AssertMessages.notNullParameter();
return intersectsSegmentSegmentWithEnds(
getX1(), getY1(),
getX2(), getY2(),
segment.getX1(), segment.getY1(),
segment.getX2(), segment.getY2());
}
@Pure
@Override
default boolean intersects(Triangle2afp<?, ?, ?, ?, ?, ?> triangle) {
assert triangle != null : AssertMessages.notNullParameter();
return Triangle2afp.intersectsTriangleSegment(
triangle.getX1(), triangle.getY1(),
triangle.getX2(), triangle.getY2(),
triangle.getX3(), triangle.getY3(),
getX1(), getY1(),
getX2(), getY2());
}
@Override
default boolean isEmpty() {
return getX1() == getX2() && getY1() == getY2();
}
/** Change the line.
*
* @param x1 x coordinate of the first point.
* @param y1 y coordinate of the first point.
* @param x2 x coordinate of the second point.
* @param y2 y coordinate of the second point.
*/
// No default implementation for ensuring atomic change.
void set(double x1, double y1, double x2, double y2);
@Override
default void set(IT shape) {
assert shape != null : AssertMessages.notNullParameter();
set(shape.getX1(), shape.getY1(), shape.getX2(), shape.getY2());
}
/** Change the line.
*
* @param firstPoint the first point.
* @param secondPoint the second point.
*/
default void set(Point2D<?, ?> firstPoint, Point2D<?, ?> secondPoint) {
assert firstPoint != null : AssertMessages.notNullParameter(0);
assert secondPoint != null : AssertMessages.notNullParameter(1);
set(firstPoint.getX(), firstPoint.getY(), secondPoint.getX(), secondPoint.getY());
}
/** Change the first point.
*
* @param x x coordinate of the first point.
* @param y y coordinate of the first point.
*/
default void setP1(double x, double y) {
set(x, y, getX2(), getY2());
}
/** Change the first point.
*
* @param pt the first point.
*/
default void setP1(Point2D<?, ?> pt) {
assert pt != null : AssertMessages.notNullParameter();
set(pt.getX(), pt.getY(), getX2(), getY2());
}
/** Change the second point.
*
* @param x x coordinate of the second point.
* @param y y coordinate of the second point.
*/
default void setP2(double x, double y) {
set(getX1(), getY1(), x, y);
}
/** Change the second point.
*
* @param pt the second point.
*/
default void setP2(Point2D<?, ?> pt) {
assert pt != null : AssertMessages.notNullParameter();
set(getX1(), getY1(), pt.getX(), pt.getY());
}
/** Sets a new value in the X of the first point.
*
* @param x the new value double x
*/
void setX1(double x);
/**Sets a new value in the X of the second point.
*
* @param x the new value double x
*/
void setX2(double x);
/**Sets a new value in the Y of the first point.
*
* @param y the new value double y
*/
void setY1(double y);
/**Sets a new value in the Y of the second point.
*
* @param y the new value double y
*/
void setY2(double y);
@Pure
@Override
default void toBoundingBox(B box) {
assert box != null : AssertMessages.notNullParameter();
box.setFromCorners(
this.getX1(),
this.getY1(),
this.getX2(),
this.getY2());
}
/** Transform the current segment.
* This function changes the current segment.
*
* @param transform is the affine transformation to apply.
* @see #createTransformedShape
*/
default void transform(Transform2D transform) {
assert transform != null : AssertMessages.notNullParameter();
final Point2D<?, ?> p = new InnerComputationPoint2afp(getX1(), getY1());
transform.transform(p);
final double x1 = p.getX();
final double y1 = p.getY();
p.set(getX2(), getY2());
transform.transform(p);
set(x1, y1, p.getX(), p.getY());
}
@Override
default void translate(double dx, double dy) {
set(getX1() + dx, getY1() + dy, getX2() + dx, getY2() + dy);
}
}