/* * $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.Pure; import org.arakhne.afc.math.MathConstants; 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.Transform2D; import org.arakhne.afc.math.geometry.d2.Vector2D; import org.arakhne.afc.vmutil.asserts.AssertMessages; /** Fonctional interface that represented a 2D triangle 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: fozgul$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ @SuppressWarnings("checkstyle:methodcount") public interface Triangle2afp< ST extends Shape2afp<?, ?, IE, P, V, B>, IT extends Triangle2afp<?, ?, 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> { /** Replies the closest feature of the triangle to the given point. * * @param tx1 x coordinate of the first point of the triangle. * @param ty1 y coordinate of the first point of the triangle. * @param tx2 x coordinate of the second point of the triangle. * @param ty2 y coordinate of the second point of the triangle. * @param tx3 x coordinate of the third point of the triangle. * @param ty3 y coordinate of the third point of the triangle. * @param px x coordinate of the reference point. * @param py y coordinate of the reference point. * @return the closest triangle feature to the reference point. * @deprecated since 13.0, see {@link #findsClosestFeatureTrianglePoint(double, double, * double, double, double, double, double, double)} */ @Deprecated static TriangleFeature getClosestFeatureTrianglePoint(double tx1, double ty1, double tx2, double ty2, double tx3, double ty3, double px, double py) { return findsClosestFeatureTrianglePoint(tx1, ty1, tx2, ty2, tx3, ty3, px, py); } /** Replies the closest feature of the triangle to the given point. * * @param tx1 x coordinate of the first point of the triangle. * @param ty1 y coordinate of the first point of the triangle. * @param tx2 x coordinate of the second point of the triangle. * @param ty2 y coordinate of the second point of the triangle. * @param tx3 x coordinate of the third point of the triangle. * @param ty3 y coordinate of the third point of the triangle. * @param px x coordinate of the reference point. * @param py y coordinate of the reference point. * @return the closest triangle feature to the reference point. */ @SuppressWarnings({"checkstyle:returncount", "checkstyle:npathcomplexity"}) static TriangleFeature findsClosestFeatureTrianglePoint(double tx1, double ty1, double tx2, double ty2, double tx3, double ty3, double px, double py) { final double apx = px - tx1; final double apy = py - ty1; final double abx = tx2 - tx1; final double aby = ty2 - ty1; double d1 = Vector2D.dotProduct(abx, aby, apx, apy); if (d1 < 0.) { final double acx = tx3 - tx1; final double acy = ty3 - ty1; d1 = Vector2D.dotProduct(acx, acy, apx, apy); if (d1 < 0.) { return TriangleFeature.FIRST_CORNER; } final double d2 = Vector2D.dotProduct(acx, acy, acx, acy); if (d1 > d2) { return TriangleFeature.THIRD_CORNER; } return TriangleFeature.THIRD_SEGMENT; } final double bpx = px - tx2; final double bpy = py - ty2; final double bcx = tx3 - tx2; final double bcy = ty3 - ty2; final double d2 = Vector2D.dotProduct(bcx, bcy, bpx, bpy); if (d2 < 0.) { final double d3 = Vector2D.dotProduct(abx, aby, abx, aby); if (d1 > d3) { return TriangleFeature.SECOND_CORNER; } return TriangleFeature.FIRST_SEGMENT; } final double cpx = px - tx3; final double cpy = py - ty3; final double cax = tx1 - tx1; final double cay = ty1 - ty1; final double d3 = Vector2D.dotProduct(cax, cay, cpx, cpy); if (d3 < 0.) { final double d4 = Vector2D.dotProduct(bcx, bcy, bcx, bcy); if (d2 > d4) { return TriangleFeature.THIRD_CORNER; } return TriangleFeature.SECOND_SEGMENT; } double d4 = Vector2D.dotProduct(abx, aby, abx, aby); if (d1 > d4) { return TriangleFeature.SECOND_SEGMENT; } d4 = Vector2D.perpProduct(cax, cay, cpx, cpy); if (d4 < 0.) { return TriangleFeature.THIRD_SEGMENT; } return TriangleFeature.INSIDE; } /** * Replies if three points of a triangle are defined in a counter-clockwise order. * * @param x1 * is the X coordinate of the first point * @param y1 * is the Y coordinate of the first point * @param x2 * is the X coordinate of the second point * @param y2 * is the Y coordinate of the second point * @param x3 * is the X coordinate of the third point * @param y3 * is the Y coordinate of the third point * @return <code>true</code> if the three given points are defined in a counter-clockwise order. * @deprecated since 13.0, see {@link #isCCW(double, double, double, double, double, double)} */ @Deprecated @Pure static boolean isCCWOrderDefinition(double x1, double y1, double x2, double y2, double x3, double y3) { return isCCW(x1, y1, x2, y2, x3, y3); } /** * Replies if three points of a triangle are defined in a counter-clockwise order. * * @param x1 * is the X coordinate of the first point * @param y1 * is the Y coordinate of the first point * @param x2 * is the X coordinate of the second point * @param y2 * is the Y coordinate of the second point * @param x3 * is the X coordinate of the third point * @param y3 * is the Y coordinate of the third point * @return <code>true</code> if the three given points are defined in a counter-clockwise order. */ @Pure static boolean isCCW(double x1, double y1, double x2, double y2, double x3, double y3) { return Vector2D.perpProduct(x2 - x1, y2 - y1, x3 - x1, y3 - y1) >= 0.; } /** Replies if the points of the triangle are defined in a counter-clockwise order. * * @return <code>true</code> if the triangle points are defined in a counter-clockwise order. */ boolean isCCW(); /** * Replies if the given point is inside the given triangle. * * @param tx1 x coordinate of the first point of the triangle. * @param ty1 y coordinate of the first point of the triangle. * @param tx2 x coordinate of the second point of the triangle. * @param ty2 y coordinate of the second point of the triangle. * @param tx3 x coordinate of the third point of the triangle. * @param ty3 y coordinate of the third point of the triangle. * @param px is the point to test. * @param py is the point to test. * @return <code>true</code> if the point is inside the triangle; * <code>false</code> if not. */ @Pure static boolean containsTrianglePoint(double tx1, double ty1, double tx2, double ty2, double tx3, double ty3, double px, double py) { // Barycentric algorithm final double ty23 = ty2 - ty3; final double tx13 = tx1 - tx3; final double tx32 = tx3 - tx2; final double ty13 = ty1 - ty3; final double denominator = ty23 * tx13 + tx32 * ty13; if (denominator == 0.) { return false; } final double px3 = px - tx3; final double py3 = py - ty3; final double a = (ty23 * px3 + tx32 * py3) / denominator; final double b = (-ty13 * px3 + tx13 * py3) / denominator; final double c = 1. - a - b; return 0. <= a && a <= 1. && 0. <= b && b <= 1. && 0. <= c && c <= 1.; } /** * Replies if the given point is inside the given triangle. * * @param tx1 x coordinate of the first point of the triangle. * @param ty1 y coordinate of the first point of the triangle. * @param tx2 x coordinate of the second point of the triangle. * @param ty2 y coordinate of the second point of the triangle. * @param tx3 x coordinate of the third point of the triangle. * @param ty3 y coordinate of the third point of the triangle. * @param rx is the x coordinate of the rectangle. * @param ry is the y coordinate of the rectangle. * @param rwidth the width of the rectangle. * @param rheight the height of the rectangle. * @return <code>true</code> if the rectangle is inside the triangle; * <code>false</code> if not. */ @Pure @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"}) static boolean containsTriangleRectangle(double tx1, double ty1, double tx2, double ty2, double tx3, double ty3, double rx, double ry, double rwidth, double rheight) { assert rwidth >= 0. : AssertMessages.positiveOrZeroParameter(8); assert rheight >= 0. : AssertMessages.positiveOrZeroParameter(9); return containsTrianglePoint(tx1, ty1, tx2, ty2, tx3, ty3, rx, ry) && containsTrianglePoint(tx1, ty1, tx2, ty2, tx3, ty3, rx + rwidth, ry) && containsTrianglePoint(tx1, ty1, tx2, ty2, tx3, ty3, rx + rwidth, ry + rheight) && containsTrianglePoint(tx1, ty1, tx2, ty2, tx3, ty3, rx, ry + rheight); } /** * Replies the closest point to the given point inside the given triangle. * * @param tx1 x coordinate of the first point of the triangle. * @param ty1 y coordinate of the first point of the triangle. * @param tx2 x coordinate of the second point of the triangle. * @param ty2 y coordinate of the second point of the triangle. * @param tx3 x coordinate of the third point of the triangle. * @param ty3 y coordinate of the third point of the triangle. * @param px is the point to test. * @param py is the point to test. * @param closest the closest point. * @param farthest the farthest point. * @deprecated since 13.0, {@link #findsClosestFarthestPointsTrianglePoint(double, double, double, double, * double, double, double, double, Point2D, Point2D)} */ @Deprecated @SuppressWarnings("checkstyle:parameternumber") static void computeClosestFarthestPoints(double tx1, double ty1, double tx2, double ty2, double tx3, double ty3, double px, double py, Point2D<?, ?> closest, Point2D<?, ?> farthest) { findsClosestFarthestPointsTrianglePoint(tx1, ty1, tx2, ty2, tx3, ty3, px, py, closest, farthest); } /** * Replies the closest point to the given point inside the given triangle. * * @param tx1 x coordinate of the first point of the triangle. * @param ty1 y coordinate of the first point of the triangle. * @param tx2 x coordinate of the second point of the triangle. * @param ty2 y coordinate of the second point of the triangle. * @param tx3 x coordinate of the third point of the triangle. * @param ty3 y coordinate of the third point of the triangle. * @param px is the point to test. * @param py is the point to test. * @param closest the closest point. * @param farthest the farthest point. */ @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"}) static void findsClosestFarthestPointsTrianglePoint(double tx1, double ty1, double tx2, double ty2, double tx3, double ty3, double px, double py, Point2D<?, ?> closest, Point2D<?, ?> farthest) { assert closest != null || farthest != null : AssertMessages.oneNotNullParameter(8, 9); if (closest != null) { final double side1 = Vector2D.perpProduct(tx2 - tx1, ty2 - ty1, px - tx1, py - ty1); final double side2 = Vector2D.perpProduct(tx3 - tx2, ty3 - ty2, px - tx2, py - ty2); final double side3 = Vector2D.perpProduct(tx1 - tx3, ty1 - ty3, px - tx3, py - ty3); if (side1 <= 0) { if (side2 <= 0) { closest.set(tx2, ty2); } else if (side3 <= 0) { closest.set(tx1, ty1); } else { Segment2afp.findsClosestPointSegmentPoint(tx1, ty1, tx2, ty2, px, py, closest); } } else if (side2 <= 0) { if (side3 <= 0) { closest.set(tx3, ty3); } else { Segment2afp.findsClosestPointSegmentPoint(tx2, ty2, tx3, ty3, px, py, closest); } } else if (side3 <= 0) { Segment2afp.findsClosestPointSegmentPoint(tx3, ty3, tx1, ty1, px, py, closest); } else { closest.set(px, py); } } if (farthest != null) { double dist; double x = tx1; double y = ty1; double max = Math.pow(tx1 - px, 2) + Math.pow(ty1 - py, 2); dist = Math.pow(tx2 - px, 2) + Math.pow(ty2 - py, 2); if (dist > max) { max = dist; x = tx2; y = ty2; } dist = Math.pow(tx3 - px, 2) + Math.pow(ty3 - py, 2); if (dist > max) { x = tx3; y = ty3; } farthest.set(x, y); } } /** * Replies the squared distance from the given triangle to the given point. * * <p>Caution: The points of the triangle must be defined in a CCW order. * * @param tx1 x coordinate of the first point of the triangle. * @param ty1 y coordinate of the first point of the triangle. * @param tx2 x coordinate of the second point of the triangle. * @param ty2 y coordinate of the second point of the triangle. * @param tx3 x coordinate of the third point of the triangle. * @param ty3 y coordinate of the third point of the triangle. * @param px is the point. * @param py is the point. * @return the squared distance from the triangle to the point. * @see #isCCW(double, double, double, double, double, double) * @deprecated since 13.0, see {@link #calculatesSquaredDistanceTrianglePoint(double, double, * double, double, double, double, double, double)} */ @Deprecated @Pure @SuppressWarnings("checkstyle:parameternumber") static double getSquaredDistanceTrianglePoint(double tx1, double ty1, double tx2, double ty2, double tx3, double ty3, double px, double py) { return calculatesSquaredDistanceTrianglePoint(tx1, ty1, tx2, ty2, tx3, ty3, px, py); } /** * Replies the squared distance from the given triangle to the given point. * * <p>Caution: The points of the triangle must be defined in a CCW order. * * @param tx1 x coordinate of the first point of the triangle. * @param ty1 y coordinate of the first point of the triangle. * @param tx2 x coordinate of the second point of the triangle. * @param ty2 y coordinate of the second point of the triangle. * @param tx3 x coordinate of the third point of the triangle. * @param ty3 y coordinate of the third point of the triangle. * @param px is the point. * @param py is the point. * @return the squared distance from the triangle to the point. * @see #isCCW(double, double, double, double, double, double) */ @Pure @SuppressWarnings("checkstyle:parameternumber") static double calculatesSquaredDistanceTrianglePoint(double tx1, double ty1, double tx2, double ty2, double tx3, double ty3, double px, double py) { final double bx; final double by; final double cx; final double cy; if (isCCW(tx1, ty1, tx2, ty2, tx3, ty3)) { bx = tx2; by = ty2; cx = tx3; cy = ty3; } else { bx = tx3; by = ty3; cx = tx2; cy = ty2; } final double side1 = Vector2D.perpProduct(bx - tx1, by - ty1, px - tx1, py - ty1); final double side2 = Vector2D.perpProduct(cx - bx, cy - by, px - bx, py - by); final double side3 = Vector2D.perpProduct(tx1 - cx, ty1 - cy, px - cx, py - cy); if (side1 < 0) { if (side2 < 0) { return Point2D.getDistanceSquaredPointPoint(px, py, tx2, ty2); } if (side3 < 0) { return Point2D.getDistanceSquaredPointPoint(px, py, tx1, ty1); } return Segment2afp.calculatesDistanceSquaredSegmentPoint(tx1, ty1, tx2, ty2, px, py); } if (side2 < 0) { if (side3 < 0) { return Point2D.getDistanceSquaredPointPoint(px, py, tx3, ty3); } return Segment2afp.calculatesDistanceSquaredSegmentPoint(tx2, ty2, tx3, ty3, px, py); } if (side3 < 0) { return Segment2afp.calculatesDistanceSquaredSegmentPoint(tx3, ty3, tx1, ty1, px, py); } return 0.; } /** Replies if a triangle and a circle are intersecting. * * @param tx1 x coordinate of the first point of the triangle. * @param ty1 y coordinate of the first point of the triangle. * @param tx2 x coordinate of the second point of the triangle. * @param ty2 y coordinate of the second point of the triangle. * @param tx3 x coordinate of the third point of the triangle. * @param ty3 y coordinate of the third point of the triangle. * @param cx is the center of the circle * @param cy is the center of the circle * @param cradius is the radius of the circle * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ @Pure @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"}) static boolean intersectsTriangleCircle(double tx1, double ty1, double tx2, double ty2, double tx3, double ty3, double cx, double cy, double cradius) { assert cradius >= 0 : AssertMessages.positiveOrZeroParameter(8); final double distance = calculatesSquaredDistanceTrianglePoint( tx1, ty1, tx2, ty2, tx3, ty3, cx, cy); return distance < cradius * cradius; } /** Replies if a triangle and an ellipse are intersecting. * * @param tx1 x coordinate of the first point of the triangle. * @param ty1 y coordinate of the first point of the triangle. * @param tx2 x coordinate of the second point of the triangle. * @param ty2 y coordinate of the second point of the triangle. * @param tx3 x coordinate of the third point of the triangle. * @param ty3 y coordinate of the third point of the triangle. * @param ex is the position of the ellipse * @param ey is the position of the ellipse * @param ewidth is the width of the ellipse * @param eheight is the height of the ellipse * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ @Pure @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"}) static boolean intersectsTriangleEllipse(double tx1, double ty1, double tx2, double ty2, double tx3, double ty3, double ex, double ey, double ewidth, double eheight) { assert ewidth >= 0 : AssertMessages.positiveOrZeroParameter(8); assert eheight >= 0 : AssertMessages.positiveOrZeroParameter(9); final double a = ewidth / 2.; final double b = eheight / 2.; final double centerX = ex + a; final double centerY = ey + b; final double x1 = (tx1 - centerX) / a; final double y1 = (ty1 - centerY) / b; final double x2 = (tx2 - centerX) / a; final double y2 = (ty2 - centerY) / b; final double x3 = (tx3 - centerX) / a; final double y3 = (ty3 - centerY) / b; final double distance = calculatesSquaredDistanceTrianglePoint( x1, y1, x2, y2, x3, y3, 0, 0); return distance < 1.; } /** Replies if a triangle and a segment are intersecting. * * @param tx1 x coordinate of the first point of the triangle. * @param ty1 y coordinate of the first point of the triangle. * @param tx2 x coordinate of the second point of the triangle. * @param ty2 y coordinate of the second point of the triangle. * @param tx3 x coordinate of the third point of the triangle. * @param ty3 y coordinate of the third point of the triangle. * @param sx1 x coordinate of the first point of the segment. * @param sy1 y coordinate of the first point of the segment. * @param sx2 x coordinate of the second point of the segment. * @param sy2 y coordinate of the second point of the segment. * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ @Pure @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:npathcomplexity"}) static boolean intersectsTriangleSegment(double tx1, double ty1, double tx2, double ty2, double tx3, double ty3, double sx1, double sy1, double sx2, double sy2) { // Separated axis theory on 4 axis (3 directions of the triangle, 1 direction of the segment) double vx; double vy; double min1; double max1; double min2; double max2; double a; final double[] coordinates = new double[] { tx2 - tx1, ty2 - ty1, tx3 - tx2, ty3 - ty3, tx1 - tx3, ty1 - ty3, sx2 - sx1, sy2 - sy1, }; for (int i = 0; i < coordinates.length; i += 2) { vx = coordinates[i]; vy = coordinates[i + 1]; min1 = Vector2D.perpProduct(vx, vy, tx1, ty1); max1 = min1; a = Vector2D.perpProduct(vx, vy, tx2, ty2); if (a < min1) { min1 = a; } if (a > max1) { max1 = a; } a = Vector2D.perpProduct(vx, vy, tx3, ty3); if (a < min1) { min1 = a; } if (a > max1) { max1 = a; } min2 = Vector2D.perpProduct(vx, vy, sx1, sy1); max2 = min2; a = Vector2D.perpProduct(vx, vy, sx2, sy2); if (a < min2) { min2 = a; } if (a > max2) { max2 = a; } if (max1 <= min2 || max2 <= min1) { return false; } } return true; } /** Replies if two triangles are intersecting. * The first triangle must be CCW ordered. * * @param t1x1 x coordinate of the first point of the first triangle. * @param t1y1 y coordinate of the first point of the first triangle. * @param t1x2 x coordinate of the second point of the first triangle. * @param t1y2 y coordinate of the second point of the first triangle. * @param t1x3 x coordinate of the third point of the first triangle. * @param t1y3 y coordinate of the third point of the first triangle. * @param t2x1 x coordinate of the first point of the second triangle. * @param t2y1 y coordinate of the first point of the second triangle. * @param t2x2 x coordinate of the second point of the second triangle. * @param t2y2 y coordinate of the second point of the second triangle. * @param t2x3 x coordinate of the third point of the second triangle. * @param t2y3 y coordinate of the third point of the second triangle. * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ @Pure @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"}) static boolean intersectsTriangleTriangle(double t1x1, double t1y1, double t1x2, double t1y2, double t1x3, double t1y3, double t2x1, double t2y1, double t2x2, double t2y2, double t2x3, double t2y3) { assert isCCW(t1x1, t1y1, t1x2, t1y2, t1x3, t1y3) : AssertMessages.ccwParameters(0, 1, 2, 3, 4, 5); final double[] coordinates = new double[] { t1x2 - t1x1, t1x1, t1y2 - t1y1, t1y1, t2x2 - t2x1, t2x1, t2y2 - t2y1, t2y1, t1x3 - t1x2, t1x2, t1y3 - t1y2, t1y2, t2x3 - t2x2, t2x2, t2y3 - t2y2, t2y2, t1x1 - t1x3, t1x3, t1y1 - t1y3, t1y3, t2x1 - t2x3, t2x3, t2y1 - t2y3, t2y3, }; for (int i = 0; i < coordinates.length; i += 8) { final double a = coordinates[i]; double ox = coordinates[i + 1]; final double b = coordinates[i + 2]; double oy = coordinates[i + 3]; if ((Vector2D.perpProduct(a, b, t2x1 - ox, t2y1 - oy) <= 0.) && (Vector2D.perpProduct(a, b, t2x2 - ox, t2y2 - oy) <= 0.) && (Vector2D.perpProduct(a, b, t2x3 - ox, t2y3 - oy) <= 0.)) { return false; } final double c = coordinates[i + 4]; ox = coordinates[i + 5]; final double d = coordinates[i + 6]; oy = coordinates[i + 7]; if ((Vector2D.perpProduct(c, d, t1x1 - ox, t1y1 - oy) <= 0.) && (Vector2D.perpProduct(c, d, t1x2 - ox, t1y2 - oy) <= 0.) && (Vector2D.perpProduct(c, d, t1x3 - ox, t1y3 - oy) <= 0.)) { return false; } } return true; } /** Replies if a triangle and a rectangle are intersecting. * * @param tx1 x coordinate of the first point of the triangle. * @param ty1 y coordinate of the first point of the triangle. * @param tx2 x coordinate of the second point of the triangle. * @param ty2 y coordinate of the second point of the triangle. * @param tx3 x coordinate of the third point of the triangle. * @param ty3 y coordinate of the third point of the triangle. * @param rx x coordinate of the minimum corner of the rectangle. * @param ry y coordinate of the minimum corner of the rectangle. * @param rwidth width of the rectangle. * @param rheight height of the rectangle. * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ @Pure @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"}) static boolean intersectsTriangleRectangle(double tx1, double ty1, double tx2, double ty2, double tx3, double ty3, double rx, double ry, double rwidth, double rheight) { assert rwidth >= 0. : AssertMessages.positiveOrZeroParameter(8); assert rheight >= 0. : AssertMessages.positiveOrZeroParameter(9); // Test triangle segment intersection with the rectangle final double rx2 = rx + rwidth; final double ry2 = ry + rheight; if (Rectangle2afp.intersectsRectangleSegment(rx, ry, rx2, ry2, tx1, ty1, tx2, ty2) || Rectangle2afp.intersectsRectangleSegment(rx, ry, rx2, ry2, tx2, ty2, tx3, ty3) || Rectangle2afp.intersectsRectangleSegment(rx, ry, rx2, ry2, tx3, ty3, tx1, ty1)) { return true; } // Triangle may enclose the rectangle. return containsTriangleRectangle(tx1, ty1, tx2, ty2, tx3, ty3, rx, ry, rwidth, rheight); } @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() && getX3() == shape.getX3() && getY3() == shape.getY3(); } /** Replies the first point X. * * @return the first point x. */ @Pure double getX1(); /** Replies the first point Y. * * @return the first point y. */ @Pure double getY1(); /** Replies the second point X. * * @return the second point x. */ @Pure double getX2(); /** Replies the second point Y. * * @return the second point y. */ @Pure double getY2(); /** Replies the third point X. * * @return the third point x. */ @Pure double getX3(); /** Replies the third point Y. * * @return the third point y. */ @Pure double getY3(); /** Replies the first point. * * @return a copy of the first point. */ @Pure default P getP1() { return getGeomFactory().newPoint(getX1(), getY1()); } /** Replies the second point. * * @return a copy of the second point. */ @Pure default P getP2() { return getGeomFactory().newPoint(getX2(), getY2()); } /** Replies the third point. * * @return a copy of the third point. */ @Pure default P getP3() { return getGeomFactory().newPoint(getX3(), getY3()); } /** Change the first point. * * @param point the point. */ default void setP1(Point2D<?, ?> point) { assert point != null : AssertMessages.notNullParameter(); setX1(point.getX()); setY1(point.getY()); } /** Change the first point. * * @param x x coordinate of the point. * @param y y coordinate of the point. */ default void setP1(double x, double y) { setX1(x); setY1(y); } /** Change the second point. * * @param point the point. */ default void setP2(Point2D<?, ?> point) { assert point != null : AssertMessages.notNullParameter(); setX2(point.getX()); setY2(point.getY()); } /** Change the second point. * * @param x x coordinate of the point. * @param y y coordinate of the point. */ default void setP2(double x, double y) { setX2(x); setY2(y); } /** Change the third point. * * @param point the point. */ default void setP3(Point2D<?, ?> point) { assert point != null : AssertMessages.notNullParameter(); setX3(point.getX()); setY3(point.getY()); } /** Change the third point. * * @param x x coordinate of the point. * @param y y coordinate of the point. */ default void setP3(double x, double y) { setX3(x); setY3(y); } /** Change the x coordinate of the first point. * * @param x x coordinate of the point. */ void setX1(double x); /** Change the y coordinate of the first point. * * @param y y coordinate of the point. */ void setY1(double y); /** Change the x coordinate of the second point. * * @param x x coordinate of the point. */ void setX2(double x); /** Change the y coordinate of the second point. * * @param y y coordinate of the point. */ void setY2(double y); /** Change the x coordinate of the third point. * * @param x x coordinate of the point. */ void setX3(double x); /** Change the y coordinate of the third point. * * @param y y coordinate of the point. */ void setY3(double y); /** Change the triangle. * * @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. * @param x3 x coordinate of the third point. * @param y3 y coordinate of the third point. */ void set(double x1, double y1, double x2, double y2, double x3, double y3); @Override default void set(IT shape) { assert shape != null : AssertMessages.notNullParameter(); set(shape.getX1(), shape.getY1(), shape.getX2(), shape.getY2(), shape.getX3(), shape.getY3()); } @Override default void clear() { set(0, 0, 0, 0, 0, 0); } @Override @SuppressWarnings("checkstyle:npathcomplexity") default void toBoundingBox(B box) { assert box != null : AssertMessages.notNullParameter(); double minx = getX1(); double maxx = minx; if (getX2() < minx) { minx = getX2(); } if (getX2() > maxx) { maxx = getX2(); } double miny = getY1(); double maxy = miny; if (getY2() < miny) { miny = getY2(); } if (getY2() > maxy) { maxy = getY2(); } if (getX3() < minx) { minx = getX3(); } if (getX3() > maxx) { maxx = getX3(); } if (getY3() < miny) { miny = getY3(); } if (getY3() > maxy) { maxy = getY3(); } box.setFromCorners(minx, miny, maxx, maxy); } @Override default boolean isEmpty() { final double x = getX1(); final double y = getY1(); return x == getX2() && x == getX3() && y == getY2() && y == getY3(); } @Pure @Override default double getDistanceSquared(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); return calculatesSquaredDistanceTrianglePoint(getX1(), getY1(), getX2(), getY2(), getX3(), getY3(), pt.getX(), pt.getY()); } @Pure @Override default double getDistanceL1(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final Point2D<?, ?> r = getClosestPointTo(pt); return r.getDistanceL1(pt); } @Pure @Override default double getDistanceLinf(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final Point2D<?, ?> r = getClosestPointTo(pt); return r.getDistanceLinf(pt); } @Pure @Override default boolean contains(double x, double y) { return containsTrianglePoint(getX1(), getY1(), getX2(), getY2(), getX3(), getY3(), x, y); } @Override default boolean contains(Rectangle2afp<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); return containsTriangleRectangle(getX1(), getY1(), getX2(), getY2(), getX3(), getY3(), rectangle.getMinX(), rectangle.getMinY(), rectangle.getWidth(), rectangle.getHeight()); } @Override default void translate(double dx, double dy) { setP1(getX1() + dx, getY1() + dy); setP2(getX2() + dx, getY2() + dy); setP3(getX3() + dx, getY3() + dy); } @Pure @Override default boolean intersects(Rectangle2afp<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); return intersectsTriangleRectangle(getX1(), getY1(), getX2(), getY2(), getX3(), getY3(), rectangle.getMinX(), rectangle.getMinY(), rectangle.getWidth(), rectangle.getHeight()); } @Pure @Override default boolean intersects(Ellipse2afp<?, ?, ?, ?, ?, ?> ellipse) { assert ellipse != null : AssertMessages.notNullParameter(); return intersectsTriangleEllipse(getX1(), getY1(), getX2(), getY2(), getX3(), getY3(), ellipse.getMinX(), ellipse.getMinY(), ellipse.getWidth(), ellipse.getHeight()); } @Pure @Override default boolean intersects(Circle2afp<?, ?, ?, ?, ?, ?> circle) { assert circle != null : AssertMessages.notNullParameter(); return intersectsTriangleCircle(getX1(), getY1(), getX2(), getY2(), getX3(), getY3(), circle.getX(), circle.getY(), circle.getRadius()); } @Pure @Override default boolean intersects(Segment2afp<?, ?, ?, ?, ?, ?> segment) { assert segment != null : AssertMessages.notNullParameter(); return intersectsTriangleSegment(getX1(), getY1(), getX2(), getY2(), getX3(), getY3(), segment.getX1(), segment.getY1(), segment.getX2(), segment.getY2()); } @Pure @Override default boolean intersects(Triangle2afp<?, ?, ?, ?, ?, ?> triangle) { assert triangle != null : AssertMessages.notNullParameter(); if (isCCW()) { return intersectsTriangleTriangle(getX1(), getY1(), getX2(), getY2(), getX3(), getY3(), triangle.getX1(), triangle.getY1(), triangle.getX2(), triangle.getY2(), triangle.getX3(), triangle.getY3()); } return intersectsTriangleTriangle(getX1(), getY1(), getX3(), getY3(), getX2(), getY2(), triangle.getX1(), triangle.getY1(), triangle.getX2(), triangle.getY2(), triangle.getX3(), triangle.getY3()); } @Pure @Override default boolean intersects(OrientedRectangle2afp<?, ?, ?, ?, ?, ?> sorientedRectangle) { assert sorientedRectangle != null : AssertMessages.notNullParameter(); return OrientedRectangle2afp.intersectsOrientedRectangleTriangle( sorientedRectangle.getCenterX(), sorientedRectangle.getCenterY(), sorientedRectangle.getFirstAxisX(), sorientedRectangle.getFirstAxisY(), sorientedRectangle.getFirstAxisExtent(), sorientedRectangle.getSecondAxisExtent(), getX1(), getY1(), getX2(), getY2(), getX3(), getY3()); } @Pure @Override default boolean intersects(Parallelogram2afp<?, ?, ?, ?, ?, ?> parallelogram) { assert parallelogram != null : AssertMessages.notNullParameter(); return Parallelogram2afp.intersectsParallelogramTriangle( parallelogram.getCenterX(), parallelogram.getCenterY(), parallelogram.getFirstAxisX(), parallelogram.getFirstAxisY(), parallelogram.getFirstAxisExtent(), parallelogram.getSecondAxisX(), parallelogram.getSecondAxisY(), parallelogram.getSecondAxisExtent(), getX1(), getY1(), getX2(), getY2(), getX3(), getY3()); } @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.calculatesCrossingsPathIteratorTriangleShadow( 0, iterator, getX1(), getY1(), getX2(), getY2(), getX3(), getY3(), CrossingComputationType.SIMPLE_INTERSECTION_WHEN_NOT_POLYGON); return crossings == MathConstants.SHAPE_INTERSECTS || (crossings & mask) != 0; } @Pure @Override default boolean intersects(RoundRectangle2afp<?, ?, ?, ?, ?, ?> roundRectangle) { assert roundRectangle != null : AssertMessages.notNullParameter(); return roundRectangle.intersects(getPathIterator()); } @Pure @Override default boolean intersects(MultiShape2afp<?, ?, ?, ?, ?, ?, ?> multishape) { assert multishape != null : AssertMessages.notNullParameter(); return multishape.intersects(this); } @Pure @Override default P getClosestPointTo(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); findsClosestFarthestPointsTrianglePoint(getX1(), getY1(), getX2(), getY2(), getX3(), getY3(), pt.getX(), pt.getY(), point, null); return point; } @Pure @Override default P getClosestPointTo(Circle2afp<?, ?, ?, ?, ?, ?> circle) { 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 @Unefficient default P getClosestPointTo(Rectangle2afp<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); Path2afp.findsClosestPointPathIteratorPathIterator(getPathIterator(), rectangle.getPathIterator(), point); return point; } @Override @Unefficient default P getClosestPointTo(Segment2afp<?, ?, ?, ?, ?, ?> segment) { assert segment != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); Path2afp.findsClosestPointPathIteratorPathIterator(getPathIterator(), segment.getPathIterator(), point); return point; } @Override @Unefficient default P getClosestPointTo(Triangle2afp<?, ?, ?, ?, ?, ?> triangle) { assert triangle != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); Path2afp.findsClosestPointPathIteratorPathIterator(getPathIterator(), triangle.getPathIterator(), point); return point; } @Override @Unefficient default P getClosestPointTo(OrientedRectangle2afp<?, ?, ?, ?, ?, ?> orientedRectangle) { assert orientedRectangle != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); Path2afp.findsClosestPointPathIteratorPathIterator(getPathIterator(), orientedRectangle.getPathIterator(), point); return point; } @Override @Unefficient default P getClosestPointTo(Parallelogram2afp<?, ?, ?, ?, ?, ?> parallelogram) { assert parallelogram != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); Path2afp.findsClosestPointPathIteratorPathIterator(getPathIterator(), parallelogram.getPathIterator(), 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; } @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 getFarthestPointTo(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); findsClosestFarthestPointsTrianglePoint(getX1(), getY1(), getX2(), getY2(), getX3(), getY3(), pt.getX(), pt.getY(), null, point); return point; } @Pure @Override default PathIterator2afp<IE> getPathIterator(Transform2D transform) { if (transform == null || transform.isIdentity()) { return new TrianglePathIterator<>(this); } return new TransformedTrianglePathIterator<>(this, transform); } /** Abstract iterator on the path elements of the triangle. * * @param <T> the type of the path elements. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ abstract class AbstractTrianglePathIterator<T extends PathElement2afp> implements PathIterator2afp<T> { /** Number of path elements. */ protected static final int NUMBER_ELEMENTS = 3; /** The iterated shape. */ protected final Triangle2afp<?, ?, T, ?, ?, ?> triangle; /** * @param triangle the iterated shape. */ public AbstractTrianglePathIterator(Triangle2afp<?, ?, T, ?, ?, ?> triangle) { assert triangle != null : AssertMessages.notNullParameter(); this.triangle = triangle; } @Override public GeomFactory2afp<T, ?, ?, ?> getGeomFactory() { return this.triangle.getGeomFactory(); } @Override public void remove() { throw new UnsupportedOperationException(); } @Pure @Override public PathWindingRule getWindingRule() { return PathWindingRule.NON_ZERO; } @Pure @Override public boolean isPolyline() { return false; } @Override public boolean isCurved() { return false; } @Override public boolean isPolygon() { return true; } @Pure @Override public boolean isMultiParts() { return false; } } /** Iterator on the path elements of the triangle. * * @param <T> the type of the path elements. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ class TrianglePathIterator<T extends PathElement2afp> extends AbstractTrianglePathIterator<T> { private double x1; private double y1; private double x2; private double y2; private double x3; private double y3; private int index; private double movex; private double movey; private double lastx; private double lasty; /** * @param triangle the triangle to iterate on. */ public TrianglePathIterator(Triangle2afp<?, ?, T, ?, ?, ?> triangle) { super(triangle); if (triangle.isEmpty()) { this.index = NUMBER_ELEMENTS; } else { this.x1 = triangle.getX1(); this.y1 = triangle.getY1(); this.x2 = triangle.getX2(); this.y2 = triangle.getY2(); this.x3 = triangle.getX3(); this.y3 = triangle.getY3(); this.index = -1; } } @Override public PathIterator2afp<T> restartIterations() { return new TrianglePathIterator<>(this.triangle); } @Pure @Override public boolean hasNext() { return this.index < NUMBER_ELEMENTS; } @Override public T next() { if (this.index >= NUMBER_ELEMENTS) { throw new NoSuchElementException(); } final int idx = this.index; ++this.index; if (idx < 0) { this.movex = this.x1; this.movey = this.y1; this.lastx = this.movex; this.lasty = this.movey; return getGeomFactory().newMovePathElement( this.lastx, this.lasty); } final double ppx = this.lastx; final double ppy = this.lasty; switch (idx) { case 0: this.lastx = this.x2; this.lasty = this.y2; return getGeomFactory().newLinePathElement( ppx, ppy, this.lastx, this.lasty); case 1: this.lastx = this.x3; this.lasty = this.y3; return getGeomFactory().newLinePathElement( ppx, ppy, this.lastx, this.lasty); default: return getGeomFactory().newClosePathElement( this.lastx, this.lasty, this.movex, this.movey); } } } /** Iterator on the path elements of the circle. * * @param <T> the type of the path elements. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ class TransformedTrianglePathIterator<T extends PathElement2afp> extends AbstractTrianglePathIterator<T> { private final Transform2D transform; private final Point2D<?, ?> tmpPoint; private double x1; private double y1; private double x2; private double y2; private double x3; private double y3; private int index; private double movex; private double movey; private double lastx; private double lasty; /** * @param triangle the iterated triangle. * @param transform the transformation to apply. */ public TransformedTrianglePathIterator(Triangle2afp<?, ?, T, ?, ?, ?> triangle, Transform2D transform) { super(triangle); assert transform != null : AssertMessages.notNullParameter(1); this.transform = transform; if (triangle.isEmpty()) { this.index = NUMBER_ELEMENTS; this.tmpPoint = null; } else { this.tmpPoint = new InnerComputationPoint2afp(); this.x1 = triangle.getX1(); this.y1 = triangle.getY1(); this.x2 = triangle.getX2(); this.y2 = triangle.getY2(); this.x3 = triangle.getX3(); this.y3 = triangle.getY3(); this.index = -1; } } @Override public PathIterator2afp<T> restartIterations() { return new TransformedTrianglePathIterator<>(this.triangle, this.transform); } @Pure @Override public boolean hasNext() { return this.index < NUMBER_ELEMENTS; } @Override public T next() { if (this.index >= NUMBER_ELEMENTS) { throw new NoSuchElementException(); } final int idx = this.index; ++this.index; if (idx < 0) { this.tmpPoint.set(this.x1, this.y1); this.transform.transform(this.tmpPoint); this.movex = this.tmpPoint.getX(); this.movey = this.tmpPoint.getY(); this.lastx = this.movex; this.lasty = this.movey; return getGeomFactory().newMovePathElement( this.lastx, this.lasty); } final double ppx = this.lastx; final double ppy = this.lasty; switch (idx) { case 0: this.tmpPoint.set(this.x2, this.y2); this.transform.transform(this.tmpPoint); this.lastx = this.tmpPoint.getX(); this.lasty = this.tmpPoint.getY(); return getGeomFactory().newLinePathElement( ppx, ppy, this.lastx, this.lasty); case 1: this.tmpPoint.set(this.x3, this.y3); this.transform.transform(this.tmpPoint); this.lastx = this.tmpPoint.getX(); this.lasty = this.tmpPoint.getY(); return getGeomFactory().newLinePathElement( ppx, ppy, this.lastx, this.lasty); default: return getGeomFactory().newClosePathElement( this.lastx, this.lasty, this.movex, this.movey); } } } /** Features of a triangle. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ enum TriangleFeature { /** Inside of the triangle. */ INSIDE, /** The first triangle point. */ FIRST_CORNER, /** The second triangle point. */ SECOND_CORNER, /** The third triangle point. */ THIRD_CORNER, /** The first triangle segment. */ FIRST_SEGMENT, /** The second triangle segment. */ SECOND_SEGMENT, /** The third triangle segment. */ THIRD_SEGMENT; } }