/* * $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.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.Transform2D; import org.arakhne.afc.math.geometry.d2.Vector2D; import org.arakhne.afc.vmutil.asserts.AssertMessages; /** Fonctional interface that represented a 2D rectangle 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$ * @since 13.0 */ @SuppressWarnings("checkstyle:methodcount") public interface Rectangle2afp< ST extends Shape2afp<?, ?, IE, P, V, B>, IT extends Rectangle2afp<?, ?, 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 RectangularShape2afp<ST, IT, IE, P, V, B>, OrientedRectangle2afp<ST, IT, IE, P, V, B> { /** Compute the point on the rectangle that is the closest to the given point. * * @param rx the minimum x coordinate of the rectangle. * @param ry the minimum y coordinate of the rectangle. * @param rmaxx the maximum x coordinate of the rectangle. * @param rmaxy the maximum y coordinate of the rectangle. * @param px the x coordinate of the point. * @param py the y coordinate of the point. * @param closest is set with the closest point on the rectangle. * @deprecated since 13.0, see {@link #findsClosestPointRectanglePoint(double, * double, double, double, double, double, Point2D)} */ @Deprecated static void computeClosestPointRectanglePoint( double rx, double ry, double rmaxx, double rmaxy, double px, double py, Point2D<?, ?> closest) { findsClosestPointRectanglePoint(rx, ry, rmaxx, rmaxy, px, py, closest); } /** Compute the point on the rectangle that is the closest to the given point. * * @param rx the minimum x coordinate of the rectangle. * @param ry the minimum y coordinate of the rectangle. * @param rmaxx the maximum x coordinate of the rectangle. * @param rmaxy the maximum y coordinate of the rectangle. * @param px the x coordinate of the point. * @param py the y coordinate of the point. * @param closest is set with the closest point on the rectangle. */ static void findsClosestPointRectanglePoint( double rx, double ry, double rmaxx, double rmaxy, double px, double py, Point2D<?, ?> closest) { assert rmaxx >= rx : AssertMessages.lowerEqualParameters(0, rx, 2, rmaxx); assert rmaxy >= ry : AssertMessages.lowerEqualParameters(1, ry, 3, rmaxy); final double x; if (px < rx) { x = rx; } else if (px > rmaxx) { x = rmaxx; } else { x = px; } final double y; if (py < ry) { y = ry; } else if (py > rmaxy) { y = rmaxy; } else { y = py; } closest.set(x, y); } /** Compute the point on the first rectangle that is the closest to the second rectangle. * * @param rx1 the minimum x coordinate of the first rectangle. * @param ry1 the minimum y coordinate of the first rectangle. * @param rmaxx1 the maximum x coordinate of the first rectangle. * @param rmaxy1 the maximum y coordinate of the first rectangle. * @param rx2 the minimum x coordinate of the second rectangle. * @param ry2 the minimum y coordinate of the second rectangle. * @param rmaxx2 the maximum x coordinate of the second rectangle. * @param rmaxy2 the maximum y coordinate of the second rectangle. * @param closest is set with the closest point on the first rectangle. * @deprecated since 13.0, see {@link #findsClosestPointRectangleRectangle(double, * double, double, double, double, double, double, double, Point2D)} */ @Deprecated @SuppressWarnings("checkstyle:parameternumber") static void computeClosestPointRectangleRectangle( double rx1, double ry1, double rmaxx1, double rmaxy1, double rx2, double ry2, double rmaxx2, double rmaxy2, Point2D<?, ?> closest) { findsClosestPointRectangleRectangle(rx1, ry1, rmaxx1, rmaxy1, rx2, ry2, rmaxx2, rmaxy2, closest); } /** Compute the point on the first rectangle that is the closest to the second rectangle. * * @param rx1 the minimum x coordinate of the first rectangle. * @param ry1 the minimum y coordinate of the first rectangle. * @param rmaxx1 the maximum x coordinate of the first rectangle. * @param rmaxy1 the maximum y coordinate of the first rectangle. * @param rx2 the minimum x coordinate of the second rectangle. * @param ry2 the minimum y coordinate of the second rectangle. * @param rmaxx2 the maximum x coordinate of the second rectangle. * @param rmaxy2 the maximum y coordinate of the second rectangle. * @param closest is set with the closest point on the first rectangle. */ @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"}) static void findsClosestPointRectangleRectangle( double rx1, double ry1, double rmaxx1, double rmaxy1, double rx2, double ry2, double rmaxx2, double rmaxy2, Point2D<?, ?> closest) { assert rmaxx1 >= rx1 : AssertMessages.lowerEqualParameters(0, rx1, 2, rmaxx1); assert rmaxy1 >= ry1 : AssertMessages.lowerEqualParameters(1, ry1, 3, rmaxy1); assert rmaxx2 >= rx2 : AssertMessages.lowerEqualParameters(4, rx2, 6, rmaxx2); assert rmaxy2 >= ry2 : AssertMessages.lowerEqualParameters(5, rx2, 7, rmaxx2); final double px; final double cx = (rx2 + rmaxx2) / 2.; if (cx <= rx1) { px = rx1; } else if (cx >= rmaxx1) { px = rmaxx1; } else { px = cx; } final double py; final double cy = (ry2 + rmaxy2) / 2.; if (cy <= rx1) { py = ry1; } else if (cy >= rmaxy1) { py = rmaxy1; } else { py = cy; } closest.set(px, py); } /** Compute the point on the rectangle that is the closest to the segment. * * @param rx the minimum x coordinate of the rectangle. * @param ry the minimum y coordinate of the rectangle. * @param rmaxx the maximum x coordinate of the rectangle. * @param rmaxy the maximum y coordinate of the rectangle. * @param sx1 the x coordinate of the first point of the segment. * @param sy1 the y coordinate of the first point of the segment. * @param sx2 the x coordinate of the second point of the segment. * @param sy2 the y coordinate of the second point of the segment. * @param closest is set with the closest point on the rectangle. * @deprecated since 13.0, see {@link #findsClosestPointRectangleSegment(double, * double, double, double, double, double, double, double, Point2D)} */ @Deprecated @SuppressWarnings("checkstyle:parameternumber") static void computeClosestPointRectangleSegment( double rx, double ry, double rmaxx, double rmaxy, double sx1, double sy1, double sx2, double sy2, Point2D<?, ?> closest) { findsClosestPointRectangleSegment(rx, ry, rmaxx, rmaxy, sx1, sy1, sx2, sy2, closest); } /** Compute the point on the rectangle that is the closest to the segment. * * @param rx the minimum x coordinate of the rectangle. * @param ry the minimum y coordinate of the rectangle. * @param rmaxx the maximum x coordinate of the rectangle. * @param rmaxy the maximum y coordinate of the rectangle. * @param sx1 the x coordinate of the first point of the segment. * @param sy1 the y coordinate of the first point of the segment. * @param sx2 the x coordinate of the second point of the segment. * @param sy2 the y coordinate of the second point of the segment. * @param closest is set with the closest point on the rectangle. */ @SuppressWarnings("checkstyle:parameternumber") static void findsClosestPointRectangleSegment( double rx, double ry, double rmaxx, double rmaxy, double sx1, double sy1, double sx2, double sy2, Point2D<?, ?> closest) { assert rmaxx >= rx : AssertMessages.lowerEqualParameters(0, rx, 2, rmaxx); assert rmaxy >= ry : AssertMessages.lowerEqualParameters(1, ry, 3, rmaxy); 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 int zone = Rectangle2afp.reducesCohenSutherlandZoneRectangleSegment( rx, ry, rmaxx, rmaxy, sx1, sy1, sx2, sy2, code1, code2, tmp1, null); final double closex; final double closey; if ((zone & MathConstants.COHEN_SUTHERLAND_LEFT) != 0) { closex = rx; if (sx1 >= sx2) { closey = MathUtil.clamp(sy1, ry, rmaxy); } else { closey = MathUtil.clamp(sy2, ry, rmaxy); } } else if ((zone & MathConstants.COHEN_SUTHERLAND_RIGHT) != 0) { closex = rmaxx; if (sx1 <= sx2) { closey = MathUtil.clamp(sy1, ry, rmaxy); } else { closey = MathUtil.clamp(sy2, ry, rmaxy); } } else if ((zone & MathConstants.COHEN_SUTHERLAND_BOTTOM) != 0) { closey = ry; if (sy1 >= sy2) { closex = MathUtil.clamp(sx1, rx, rmaxx); } else { closex = MathUtil.clamp(sx2, rx, rmaxx); } } else if ((zone & MathConstants.COHEN_SUTHERLAND_TOP) != 0) { closey = rmaxy; if (sy1 <= sy2) { closex = MathUtil.clamp(sx1, rx, rmaxx); } else { closex = MathUtil.clamp(sx2, rx, rmaxx); } } else { closex = tmp1.getX(); closey = tmp1.getY(); } closest.set(closex, closey); } /** Compute the point on the rectangle that is the closest to the parallelogram. * * @param rx the minimum x coordinate of the rectangle. * @param ry the minimum y coordinate of the rectangle. * @param rmaxx the maximum x coordinate of the rectangle. * @param rmaxy the maximum y coordinate of the rectangle. * @param centerX the x coordinate of the center of the parallelogram. * @param centerY the y coordinate of the center of the parallelogram. * @param axis1X the x coordinate of the first axis of the parallelogram. * @param axis1Y the y coordinate of the first axis of the parallelogram. * @param axis1Extent the size of the parallelogram along its first axis. * @param axis2X the x coordinate of the second axis of the parallelogram. * @param axis2Y the y coordinate of the secondaxis of the parallelogram. * @param axis2Extent the size of the parallelogram along its second axis. * @param closest is set with the closest point on the first rectangle. * @deprecated since 13.0, see {@link #findsClosestPointRectangleParallelogram(double, * double, double, double, double, double, double, double, double, double, double, * double, Point2D)} */ @Deprecated @SuppressWarnings("checkstyle:parameternumber") static void computeClosestPointRectangleParallelogram( double rx, double ry, double rmaxx, double rmaxy, double centerX, double centerY, double axis1X, double axis1Y, double axis1Extent, double axis2X, double axis2Y, double axis2Extent, Point2D<?, ?> closest) { findsClosestPointRectangleParallelogram(rx, ry, rmaxx, rmaxy, centerX, centerY, axis1X, axis1Y, axis1Extent, axis2X, axis2Y, axis2Extent, closest); } /** Compute the point on the rectangle that is the closest to the parallelogram. * * @param rx the minimum x coordinate of the rectangle. * @param ry the minimum y coordinate of the rectangle. * @param rmaxx the maximum x coordinate of the rectangle. * @param rmaxy the maximum y coordinate of the rectangle. * @param centerX the x coordinate of the center of the parallelogram. * @param centerY the y coordinate of the center of the parallelogram. * @param axis1X the x coordinate of the first axis of the parallelogram. * @param axis1Y the y coordinate of the first axis of the parallelogram. * @param axis1Extent the size of the parallelogram along its first axis. * @param axis2X the x coordinate of the second axis of the parallelogram. * @param axis2Y the y coordinate of the secondaxis of the parallelogram. * @param axis2Extent the size of the parallelogram along its second axis. * @param closest is set with the closest point on the first rectangle. */ @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"}) static void findsClosestPointRectangleParallelogram( double rx, double ry, double rmaxx, double rmaxy, double centerX, double centerY, double axis1X, double axis1Y, double axis1Extent, double axis2X, double axis2Y, double axis2Extent, Point2D<?, ?> closest) { assert rmaxx >= rx : AssertMessages.lowerEqualParameters(0, rx, 2, rmaxx); assert rmaxy >= ry : AssertMessages.lowerEqualParameters(1, ry, 3, rmaxy); assert Vector2D.isUnitVector(axis1X, axis1Y) : AssertMessages.normalizedParameters(6, 7); assert axis1Extent >= 0. : AssertMessages.positiveOrZeroParameter(8); assert Vector2D.isUnitVector(axis2X, axis2Y) : AssertMessages.normalizedParameters(9, 10); assert axis2Extent >= 0. : AssertMessages.positiveOrZeroParameter(11); final double a1x = axis1X * axis1Extent; final double a1y = axis1Y * axis1Extent; final double a2x = axis2X * axis2Extent; final double a2y = axis2Y * axis2Extent; final Point2D<?, ?> point = new InnerComputationPoint2afp(); double x1 = centerX + a1x + a2x; double y1 = centerY + a1y + a2y; double min = Double.POSITIVE_INFINITY; final double[] segments = new double[] { centerX - a1x + a2x, centerY - a1y + a2y, centerX - a1x - a2x, centerY - a1y - a2y, centerX + a1x - a2x, centerY + a1y - a2y, x1, y1, }; for (int i = 0; i < segments.length; i += 2) { final double x2 = segments[i]; final double y2 = segments[i + 1]; findsClosestPointRectangleSegment(rx, ry, rmaxx, rmaxy, x1, y1, x2, y2, point); final double dist = Segment2afp.calculatesDistanceSquaredSegmentPoint(x1, y1, x2, y2, point.getX(), point.getY()); if (dist <= 0.) { closest.set(point); return; } if (dist < min) { min = dist; closest.set(point); } x1 = x2; y1 = y2; } } /** Update the given Cohen-Sutherland code that corresponds to the given segment in order * to obtain a segment restricted to a single Cohen-Sutherland zone. * This function is at the heart of the * <a href="http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm">Cohen-Sutherland algorithm</a>. * * <p>The result of this function may be: <ul> * <li>the code for a single zone, or</li> * <li>the code that corresponds to a single column, or </li> * <li>the code that corresponds to a single row.</li> * </ul> * * @param rx1 is the first corner of the rectangle. * @param ry1 is the first corner of the rectangle. * @param rx2 is the second corner of the rectangle. * @param ry2 is the second corner of the rectangle. * @param sx1 is the first point of the segment. * @param sy1 is the first point of the segment. * @param sx2 is the second point of the segment. * @param sy2 is the second point of the segment. * @param codePoint1 the Cohen-Sutherland code for the first point of the segment. * @param codePoint2 the Cohen-Sutherland code for the second point of the segment. * @param newSegmentP1 is set with the new coordinates of the segment first point. If <code>null</code>, * this parameter is ignored. * @param newSegmentP2 is set with the new coordinates of the segment second point. If <code>null</code>, * this parameter is ignored. * @return the rectricted Cohen-Sutherland zone. * @deprecated since 13.0, see {@link #reducesCohenSutherlandZoneRectangleSegment(double, double, double, * double, double, double, double, double, int, int, Point2D, Point2D)} */ @Deprecated @SuppressWarnings("checkstyle:parameternumber") static int reduceCohenSutherlandZoneRectangleSegment(double rx1, double ry1, double rx2, double ry2, double sx1, double sy1, double sx2, double sy2, int codePoint1, int codePoint2, Point2D<?, ?> newSegmentP1, Point2D<?, ?> newSegmentP2) { return reducesCohenSutherlandZoneRectangleSegment(rx1, ry1, rx2, ry2, sx1, sy1, sx2, sy2, codePoint1, codePoint2, newSegmentP1, newSegmentP2); } /** Update the given Cohen-Sutherland code that corresponds to the given segment in order * to obtain a segment restricted to a single Cohen-Sutherland zone. * This function is at the heart of the * <a href="http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm">Cohen-Sutherland algorithm</a>. * * <p>The result of this function may be: <ul> * <li>the code for a single zone, or</li> * <li>the code that corresponds to a single column, or </li> * <li>the code that corresponds to a single row.</li> * </ul> * * @param rx1 is the first corner of the rectangle. * @param ry1 is the first corner of the rectangle. * @param rx2 is the second corner of the rectangle. * @param ry2 is the second corner of the rectangle. * @param sx1 is the first point of the segment. * @param sy1 is the first point of the segment. * @param sx2 is the second point of the segment. * @param sy2 is the second point of the segment. * @param codePoint1 the Cohen-Sutherland code for the first point of the segment. * @param codePoint2 the Cohen-Sutherland code for the second point of the segment. * @param newSegmentP1 is set with the new coordinates of the segment first point. If <code>null</code>, * this parameter is ignored. * @param newSegmentP2 is set with the new coordinates of the segment second point. If <code>null</code>, * this parameter is ignored. * @return the rectricted Cohen-Sutherland zone. */ @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:npathcomplexity", "checkstyle:magicnumber"}) static int reducesCohenSutherlandZoneRectangleSegment(double rx1, double ry1, double rx2, double ry2, double sx1, double sy1, double sx2, double sy2, int codePoint1, int codePoint2, Point2D<?, ?> newSegmentP1, Point2D<?, ?> newSegmentP2) { assert rx1 <= rx2 : AssertMessages.lowerEqualParameters(0, rx1, 2, rx2); assert ry1 <= ry2 : AssertMessages.lowerEqualParameters(1, ry1, 3, ry2); assert codePoint1 == MathUtil.getCohenSutherlandCode(sx1, sy1, rx1, ry1, rx2, ry2) : AssertMessages.invalidValue(8); assert codePoint2 == MathUtil.getCohenSutherlandCode(sx2, sy2, rx1, ry1, rx2, ry2) : AssertMessages.invalidValue(9); double segmentX1 = sx1; double segmentY1 = sy1; double segmentX2 = sx2; double segmentY2 = sy2; int code1 = codePoint1; int code2 = codePoint2; while (true) { if ((code1 | code2) == 0) { // Bitwise OR is 0. Trivially accept and get out of loop if (newSegmentP1 != null) { newSegmentP1.set(segmentX1, segmentY1); } if (newSegmentP2 != null) { newSegmentP2.set(segmentX2, segmentY2); } return 0; } if ((code1 & code2) != 0) { // Bitwise AND is not 0. Trivially reject and get out of loop if (newSegmentP1 != null) { newSegmentP1.set(segmentX1, segmentY1); } if (newSegmentP2 != null) { newSegmentP2.set(segmentX2, segmentY2); } return code1 & code2; } // failed both tests, so calculate the line segment intersection // At least one endpoint is outside the clip rectangle; pick it. int code3 = (code1 != 0) ? code1 : code2; double x = 0; double y = 0; // 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 = segmentX1 + (segmentX2 - segmentX1) * (ry2 - segmentY1) / (segmentY2 - segmentY1); y = ry2; } else if ((code3 & MathConstants.COHEN_SUTHERLAND_BOTTOM) != 0) { // point is below the clip rectangle x = segmentX1 + (segmentX2 - segmentX1) * (ry1 - segmentY1) / (segmentY2 - segmentY1); y = ry1; } else if ((code3 & MathConstants.COHEN_SUTHERLAND_RIGHT) != 0) { // point is to the right of clip rectangle y = segmentY1 + (segmentY2 - segmentY1) * (rx2 - segmentX1) / (segmentX2 - segmentX1); x = rx2; } else if ((code3 & MathConstants.COHEN_SUTHERLAND_LEFT) != 0) { // point is to the left of clip rectangle y = segmentY1 + (segmentY2 - segmentY1) * (rx1 - segmentX1) / (segmentX2 - segmentX1); x = rx1; } 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) { segmentX1 = x; segmentY1 = y; code1 = MathUtil.getCohenSutherlandCode(segmentX1, segmentY1, rx1, ry1, rx2, ry2); } else { segmentX2 = x; segmentY2 = y; code2 = MathUtil.getCohenSutherlandCode(segmentX2, segmentY2, rx1, ry1, rx2, ry2); } } } } /** Compute the square distance between a rectangle and a point. * * @param rx the minimum x coordinate of the rectangle. * @param ry the minimum y coordinate of the rectangle. * @param rmaxx the maximum x coordinate of the rectangle. * @param rmaxy the maximum y coordinate of the rectangle. * @param px the x coordinate of the point. * @param py the y coordinate of the point. * @return the square distance. * @deprecated since 13.0, see {@link #calculatesDistanceSquaredRectanglePoint(double, * double, double, double, double, double)} */ @Deprecated @Pure static double computeDistanceSquaredRectanglePoint(double rx, double ry, double rmaxx, double rmaxy, double px, double py) { return calculatesDistanceSquaredRectanglePoint(rx, ry, rmaxx, rmaxy, px, py); } /** Compute the square distance between a rectangle and a point. * * @param rx the minimum x coordinate of the rectangle. * @param ry the minimum y coordinate of the rectangle. * @param rmaxx the maximum x coordinate of the rectangle. * @param rmaxy the maximum y coordinate of the rectangle. * @param px the x coordinate of the point. * @param py the y coordinate of the point. * @return the square distance. */ @Pure static double calculatesDistanceSquaredRectanglePoint(double rx, double ry, double rmaxx, double rmaxy, double px, double py) { assert rmaxx >= rx : AssertMessages.lowerEqualParameters(0, rx, 2, rmaxx); assert rmaxy >= ry : AssertMessages.lowerEqualParameters(1, ry, 3, rmaxy); final double dx; if (px < rx) { dx = rx - px; } else if (px > rmaxx) { dx = px - rmaxx; } else { dx = 0; } final double dy; if (py < ry) { dy = ry - py; } else if (py > rmaxy) { dy = py - rmaxy; } else { dy = 0; } return dx * dx + dy * dy; } /** Replies if two rectangles are intersecting. * * @param x1 is the first corner of the first rectangle. * @param y1 is the first corner of the first rectangle. * @param x2 is the second corner of the first rectangle. * @param y2 is the second corner of the first rectangle. * @param x3 is the first corner of the second rectangle. * @param y3 is the first corner of the second rectangle. * @param x4 is the second corner of the second rectangle. * @param y4 is the second corner of the second rectangle. * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ @Pure @SuppressWarnings("checkstyle:magicnumber") static boolean intersectsRectangleRectangle(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { assert x1 <= x2 : AssertMessages.lowerEqualParameters(0, x1, 2, x2); assert y1 <= y2 : AssertMessages.lowerEqualParameters(1, y1, 3, y2); assert x3 <= x4 : AssertMessages.lowerEqualParameters(4, x3, 6, x4); assert y3 <= y4 : AssertMessages.lowerEqualParameters(5, y3, 7, y4); return x2 > x3 && x1 < x4 && y2 > y3 && y1 < y4; } /** Replies if two rectangles are intersecting. * * @param x1 is the first corner of the rectangle. * @param y1 is the first corner of the rectangle. * @param x2 is the second corner of the rectangle. * @param y2 is the second corner of the rectangle. * @param x3 is the first point of the line. * @param y3 is the first point of the line. * @param x4 is the second point of the line. * @param y4 is the second point of the line. * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ @Pure static boolean intersectsRectangleLine(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { assert x1 <= x2 : AssertMessages.lowerEqualParameters(0, x1, 2, x2); assert y1 <= y2 : AssertMessages.lowerEqualParameters(1, y1, 3, y2); final int a = Segment2afp.ccw(x3, y3, x4, y4, x1, y1, 0.); int b = Segment2afp.ccw(x3, y3, x4, y4, x2, y1, 0.); if (a != b && b != 0) { return true; } b = Segment2afp.ccw(x3, y3, x4, y4, x2, y2, 0.); if (a != b && b != 0) { return true; } b = Segment2afp.ccw(x3, y3, x4, y4, x1, y2, 0.); return a != b && b != 0; } /** Replies if the rectangle is intersecting the segment. * * @param rx1 is the first corner of the rectangle. * @param ry1 is the first corner of the rectangle. * @param rx2 is the second corner of the rectangle. * @param ry2 is the second corner of the rectangle. * @param sx1 is the first point of the segment. * @param sy1 is the first point of the segment. * @param sx2 is the second point of the segment. * @param sy2 is the second point of the segment. * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ @Pure @SuppressWarnings("checkstyle:npathcomplexity") static boolean intersectsRectangleSegment(double rx1, double ry1, double rx2, double ry2, double sx1, double sy1, double sx2, double sy2) { assert rx1 <= rx2 : AssertMessages.lowerEqualParameters(0, rx1, 2, rx2); assert ry1 <= ry2 : AssertMessages.lowerEqualParameters(1, ry1, 3, ry2); double segmentX1 = sx1; double segmentY1 = sy1; double segmentX2 = sx2; double segmentY2 = sy2; int code1 = MathUtil.getCohenSutherlandCode(segmentX1, segmentY1, rx1, ry1, rx2, ry2); int code2 = MathUtil.getCohenSutherlandCode(segmentX2, segmentY2, rx1, ry1, rx2, ry2); while (true) { if ((code1 | code2) == 0) { // Bitwise OR is 0. Trivially accept and get out of loop // Special case: if the segment is empty, it is on the border => no intersection. return segmentX1 != segmentX2 || segmentY1 != segmentY2; } if ((code1 & code2) != 0) { // Bitwise AND is not 0. Trivially reject and get out of loop return false; } // failed both tests, so calculate the line segment intersection // At least one endpoint is outside the clip rectangle; pick it. int code3 = (code1 != 0) ? code1 : code2; double x = 0; double y = 0; // 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 = segmentX1 + (segmentX2 - segmentX1) * (ry2 - segmentY1) / (segmentY2 - segmentY1); y = ry2; } else if ((code3 & MathConstants.COHEN_SUTHERLAND_BOTTOM) != 0) { // point is below the clip rectangle x = segmentX1 + (segmentX2 - segmentX1) * (ry1 - segmentY1) / (segmentY2 - segmentY1); y = ry1; } else if ((code3 & MathConstants.COHEN_SUTHERLAND_RIGHT) != 0) { // point is to the right of clip rectangle y = segmentY1 + (segmentY2 - segmentY1) * (rx2 - segmentX1) / (segmentX2 - segmentX1); x = rx2; } else if ((code3 & MathConstants.COHEN_SUTHERLAND_LEFT) != 0) { // point is to the left of clip rectangle y = segmentY1 + (segmentY2 - segmentY1) * (rx1 - segmentX1) / (segmentX2 - segmentX1); x = rx1; } 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) { segmentX1 = x; segmentY1 = y; code1 = MathUtil.getCohenSutherlandCode(segmentX1, segmentY1, rx1, ry1, rx2, ry2); } else { segmentX2 = x; segmentY2 = y; code2 = MathUtil.getCohenSutherlandCode(segmentX2, segmentY2, rx1, ry1, rx2, ry2); } } } } /** Replies if a rectangle is inside in the rectangle. * * @param enclosingX1 is the lowest corner of the enclosing-candidate rectangle. * @param enclosingY1 is the lowest corner of the enclosing-candidate rectangle. * @param enclosingX2 is the uppest corner of the enclosing-candidate rectangle. * @param enclosingY2 is the uppest corner of the enclosing-candidate rectangle. * @param innerX1 is the lowest corner of the inner-candidate rectangle. * @param innerY1 is the lowest corner of the inner-candidate rectangle. * @param innerX2 is the uppest corner of the inner-candidate rectangle. * @param innerY2 is the uppest corner of the inner-candidate rectangle. * @return <code>true</code> if the given rectangle is inside the ellipse; * otherwise <code>false</code>. */ @Pure @SuppressWarnings("checkstyle:magicnumber") static boolean containsRectangleRectangle(double enclosingX1, double enclosingY1, double enclosingX2, double enclosingY2, double innerX1, double innerY1, double innerX2, double innerY2) { assert enclosingX1 <= enclosingX2 : AssertMessages.lowerEqualParameters(0, enclosingX1, 2, enclosingX2); assert enclosingY1 <= enclosingY2 : AssertMessages.lowerEqualParameters(1, enclosingY1, 3, enclosingY2); assert innerX1 <= innerX2 : AssertMessages.lowerEqualParameters(4, innerX1, 6, innerX2); assert innerY1 <= innerY2 : AssertMessages.lowerEqualParameters(5, innerY1, 7, innerY2); return innerX1 >= enclosingX1 && innerY1 >= enclosingY1 && innerX2 <= enclosingX2 && innerY2 <= enclosingY2; } /** Replies if a point is inside in the rectangle. * * @param rx1 is the lowest corner of the rectangle. * @param ry1 is the lowest corner of the rectangle. * @param rx2 is the uppest corner of the rectangle. * @param ry2 is the uppest corner of the rectangle. * @param px is the point. * @param py is the point. * @return <code>true</code> if the given point is inside the rectangle; * otherwise <code>false</code>. */ @Pure static boolean containsRectanglePoint( double rx1, double ry1, double rx2, double ry2, double px, double py) { assert rx1 <= rx2 : AssertMessages.lowerEqualParameters(0, rx1, 2, rx2); assert ry1 <= ry2 : AssertMessages.lowerEqualParameters(1, ry1, 3, ry2); return (px >= rx1 && px <= rx2) && (py >= ry1 && py <= ry2); } @Pure @Override default boolean equalsToShape(IT shape) { if (shape == null) { return false; } if (shape == this) { return true; } return getMinX() == shape.getMinX() && getMinY() == shape.getMinY() && getMaxX() == shape.getMaxX() && getMaxY() == shape.getMaxY(); } @Override default void set(IT shape) { assert shape != null : AssertMessages.notNullParameter(); setFromCorners(shape.getMinX(), shape.getMinY(), shape.getMaxX(), shape.getMaxY()); } /** {@inheritDoc} * * <p>The rectangle is always aligned on the global axes. * It means that the rectangle is set to the enclosing box related to the given parameters. */ @Override @SuppressWarnings("checkstyle:magicnumber") default void set(double centerX, double centerY, double axis1x, double axis1y, double axis1Extent, double axis2Extent) { assert Vector2D.isUnitVector(axis1x, axis1y) : AssertMessages.normalizedParameters(2, 3); assert axis1Extent >= 0 : AssertMessages.positiveOrZeroParameter(4); assert axis2Extent >= 0 : AssertMessages.positiveOrZeroParameter(5); final double mx = Math.max(Math.abs(axis1x * axis1Extent), Math.abs(-axis1y * axis2Extent)); final double my = Math.max(Math.abs(axis1y * axis1Extent), Math.abs(axis1x * axis2Extent)); final double vx = OrientedRectangle2afp.findsVectorProjectionRAxisVector(1, 0, mx, my); final double vy = OrientedRectangle2afp.findsVectorProjectionSAxisVector(1, 0, mx, my); set(centerX - vx, centerY - vy, vx * 2, vy * 2); } @Pure @Override default double getDistanceSquared(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final double dx; if (pt.getX() < getMinX()) { dx = getMinX() - pt.getX(); } else if (pt.getX() > getMaxX()) { dx = pt.getX() - getMaxX(); } else { dx = 0f; } final double dy; if (pt.getY() < getMinY()) { dy = getMinY() - pt.getY(); } else if (pt.getY() > getMaxY()) { dy = pt.getY() - getMaxY(); } else { dy = 0f; } return dx * dx + dy * dy; } @Override default double getDistanceSquared(Rectangle2afp<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); final double dx; if (rectangle.getMaxX() <= getMinX()) { dx = getMinX() - rectangle.getMaxX(); } else if (rectangle.getMinX() >= getMaxX()) { dx = rectangle.getMinX() - getMaxX(); } else { dx = 0; } final double dy; if (rectangle.getMaxY() <= getMinY()) { dy = getMinY() - rectangle.getMaxY(); } else if (rectangle.getMinY() >= getMaxY()) { dy = rectangle.getMinY() - getMaxY(); } else { dy = 0; } return dx * dx + dy * dy; } @Pure @Override default double getDistanceL1(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final double dx; if (pt.getX() < getMinX()) { dx = getMinX() - pt.getX(); } else if (pt.getX() > getMaxX()) { dx = pt.getX() - getMaxX(); } else { dx = 0f; } final double dy; if (pt.getY() < getMinY()) { dy = getMinY() - pt.getY(); } else if (pt.getY() > getMaxY()) { dy = pt.getY() - getMaxY(); } else { dy = 0f; } return dx + dy; } @Pure @Override default double getDistanceLinf(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final double dx; if (pt.getX() < getMinX()) { dx = getMinX() - pt.getX(); } else if (pt.getX() > getMaxX()) { dx = pt.getX() - getMaxX(); } else { dx = 0f; } final double dy; if (pt.getY() < getMinY()) { dy = getMinY() - pt.getY(); } else if (pt.getY() > getMaxY()) { dy = pt.getY() - getMaxY(); } else { dy = 0f; } return Math.max(dx, dy); } @Pure @Override default boolean contains(double x, double y) { return (x >= getMinX() && x <= getMaxX()) && (y >= getMinY() && y <= getMaxY()); } @Pure @Override default boolean contains(Rectangle2afp<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); return containsRectangleRectangle( getMinX(), getMinY(), getMaxX(), getMaxY(), rectangle.getMinX(), rectangle.getMinY(), rectangle.getMaxX(), rectangle.getMaxY()); } /** Add the given coordinate in the rectangle. * * <p>The corners of the rectangles are moved to * enclosed the given coordinate. * * @param pt the point. */ default void add(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); add(pt.getX(), pt.getY()); } /** Add the given coordinate in the rectangle. * * <p>The corners of the rectangles are moved for * enclosing the given coordinate. * * @param x x coordinate of the point. * @param y y coordinate of the point. */ default void add(double x, double y) { if (x < getMinX()) { setMinX(x); } else if (x > getMaxX()) { setMaxX(x); } if (y < getMinY()) { setMinY(y); } else if (y > getMaxY()) { setMaxY(y); } } /** Compute and replies the union of this rectangle and the given rectangle. * This function does not change this rectangle. * * <p>It is equivalent to (where <code>ur</code> is the union): * <pre><code> * Rectangle2f ur = new Rectangle2f(this); * ur.setUnion(r); * </code></pre> * * @param rect the rectangular shape. * @return the union of this rectangle and the given rectangle. * @see #setUnion(RectangularShape2afp) */ @Pure default B createUnion(RectangularShape2afp<?, ?, ?, ?, ?, ?> rect) { assert rect != null : AssertMessages.notNullParameter(); final B rr = getGeomFactory().newBox(); rr.setFromCorners(getMinX(), getMinY(), getMaxX(), getMaxY()); rr.setUnion(rect); return rr; } /** Compute and replies the intersection of this rectangle and the given rectangle. * This function does not change this rectangle. * * <p>It is equivalent to (where <code>ir</code> is the intersection): * <pre><code> * Rectangle2f ir = new Rectangle2f(this); * ir.setIntersection(r); * </code></pre> * * @param rect the rectangular shape. * @return the union of this rectangle and the given rectangle. * @see #setIntersection(RectangularShape2afp) */ @Pure default B createIntersection(RectangularShape2afp<?, ?, ?, ?, ?, ?> rect) { assert rect != null : AssertMessages.notNullParameter(); final B rr = getGeomFactory().newBox(); final double x1 = Math.max(getMinX(), rect.getMinX()); final double y1 = Math.max(getMinY(), rect.getMinY()); final double x2 = Math.min(getMaxX(), rect.getMaxX()); final double y2 = Math.min(getMaxY(), rect.getMaxY()); if (x1 <= x2 && y1 <= y2) { rr.setFromCorners(x1, y1, x2, y2); } else { rr.clear(); } return rr; } /** Compute the union of this rectangle and the given rectangle and * change this rectangle with the result of the union. * * @param rect the rectangular shape. * @see #createUnion(RectangularShape2afp) */ default void setUnion(RectangularShape2afp<?, ?, ?, ?, ?, ?> rect) { assert rect != null : AssertMessages.notNullParameter(); setFromCorners( Math.min(getMinX(), rect.getMinX()), Math.min(getMinY(), rect.getMinY()), Math.max(getMaxX(), rect.getMaxX()), Math.max(getMaxY(), rect.getMaxY())); } /** Compute the intersection of this rectangle and the given rectangle. * This function changes this rectangle. * * <p>If there is no intersection, this rectangle is cleared. * * @param rect the rectangular shape. * @see #createIntersection(RectangularShape2afp) * @see #clear() */ default void setIntersection(RectangularShape2afp<?, ?, ?, ?, ?, ?> rect) { assert rect != null : AssertMessages.notNullParameter(); final double x1 = Math.max(getMinX(), rect.getMinX()); final double y1 = Math.max(getMinY(), rect.getMinY()); final double x2 = Math.min(getMaxX(), rect.getMaxX()); final double y2 = Math.min(getMaxY(), rect.getMaxY()); if (x1 <= x2 && y1 <= y2) { setFromCorners(x1, y1, x2, y2); } else { clear(); } } @Pure @Override default boolean intersects(Rectangle2afp<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); return intersectsRectangleRectangle( getMinX(), getMinY(), getMaxX(), getMaxY(), rectangle.getMinX(), rectangle.getMinY(), rectangle.getMaxX(), rectangle.getMaxY()); } @Pure @Override default boolean intersects(Ellipse2afp<?, ?, ?, ?, ?, ?> ellipse) { assert ellipse != null : AssertMessages.notNullParameter(); return Ellipse2afp.intersectsEllipseRectangle( ellipse.getMinX(), ellipse.getMinY(), ellipse.getWidth(), ellipse.getHeight(), getMinX(), getMinY(), getMaxX(), getMaxY()); } @Pure @Override default boolean intersects(Circle2afp<?, ?, ?, ?, ?, ?> circle) { assert circle != null : AssertMessages.notNullParameter(); return Circle2afp.intersectsCircleRectangle( circle.getX(), circle.getY(), circle.getRadius(), getMinX(), getMinY(), getMaxX(), getMaxY()); } @Pure @Override default boolean intersects(Segment2afp<?, ?, ?, ?, ?, ?> segment) { assert segment != null : AssertMessages.notNullParameter(); return intersectsRectangleSegment( getMinX(), getMinY(), getMaxX(), getMaxY(), segment.getX1(), segment.getY1(), segment.getX2(), segment.getY2()); } @Pure @Override default boolean intersects(OrientedRectangle2afp<?, ?, ?, ?, ?, ?> orientedRectangle) { assert orientedRectangle != null : AssertMessages.notNullParameter(); return OrientedRectangle2afp.intersectsOrientedRectangleRectangle( orientedRectangle.getCenterX(), orientedRectangle.getCenterY(), orientedRectangle.getFirstAxisX(), orientedRectangle.getFirstAxisY(), orientedRectangle.getFirstAxisExtent(), orientedRectangle.getSecondAxisExtent(), getMinX(), getMinY(), getWidth(), getHeight()); } @Pure @Override default boolean intersects(Parallelogram2afp<?, ?, ?, ?, ?, ?> parallelogram) { assert parallelogram != null : AssertMessages.notNullParameter(); return Parallelogram2afp.intersectsParallelogramRectangle( parallelogram.getCenterX(), parallelogram.getCenterY(), parallelogram.getFirstAxisX(), parallelogram.getFirstAxisY(), parallelogram.getFirstAxisExtent(), parallelogram.getSecondAxisX(), parallelogram.getSecondAxisY(), parallelogram.getSecondAxisExtent(), getMinX(), getMinY(), getWidth(), getHeight()); } @Pure @Override default boolean intersects(RoundRectangle2afp<?, ?, ?, ?, ?, ?> roundRectangle) { assert roundRectangle != null : AssertMessages.notNullParameter(); return RoundRectangle2afp.intersectsRoundRectangleRectangle( roundRectangle.getMinX(), roundRectangle.getMinY(), roundRectangle.getMaxX(), roundRectangle.getMaxY(), roundRectangle.getArcWidth(), roundRectangle.getArcHeight(), getMinX(), getMinY(), getMaxX(), getMaxY()); } @Override default boolean intersects(Triangle2afp<?, ?, ?, ?, ?, ?> triangle) { assert triangle != null : AssertMessages.notNullParameter(); return Triangle2afp.intersectsTriangleRectangle( triangle.getX1(), triangle.getY1(), triangle.getX2(), triangle.getY2(), triangle.getX3(), triangle.getY3(), getMinX(), getMinY(), getWidth(), getHeight()); } @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.calculatesCrossingsPathIteratorRectangleShadow( 0, iterator, getMinX(), getMinY(), getMaxX(), getMaxY(), CrossingComputationType.SIMPLE_INTERSECTION_WHEN_NOT_POLYGON); return crossings == MathConstants.SHAPE_INTERSECTS || (crossings & mask) != 0; } @Pure @Override default boolean intersects(MultiShape2afp<?, ?, ?, ?, ?, ?, ?> multishape) { assert multishape != null : AssertMessages.notNullParameter(); return multishape.intersects(this); } /** Move this rectangle to avoid collision * with the reference rectangle. * * @param reference is the rectangle to avoid collision with. * @param result the displacement vector. */ default void avoidCollisionWith(Rectangle2afp<?, ?, ?, ?, ?, ?> reference, Vector2D<?, ?> result) { assert reference != null : AssertMessages.notNullParameter(); assert result != null : AssertMessages.notNullParameter(); final double dx1 = reference.getMaxX() - getMinX(); final double dx2 = getMaxX() - reference.getMinX(); final double dy1 = reference.getMaxY() - getMinY(); final double dy2 = getMaxY() - reference.getMinY(); final double absdx1 = Math.abs(dx1); final double absdx2 = Math.abs(dx2); final double absdy1 = Math.abs(dy1); final double absdy2 = Math.abs(dy2); final double dx; final double dy; if (dx1 >= 0 && absdx1 <= absdx2 && absdx1 <= absdy1 && absdx1 <= absdy2) { // Move according to dx1 dx = dx1; dy = 0; } else if (dx2 >= 0 && absdx2 <= absdx1 && absdx2 <= absdy1 && absdx2 <= absdy2) { // Move according to dx2 dx = -dx2; dy = 0; } else if (dy1 >= 0 && absdy1 <= absdx1 && absdy1 <= absdx2 && absdy1 <= absdy2) { // Move according to dy1 dx = 0; dy = dy1; } else { // Move according to dy2 dx = 0; dy = -dy2; } set( getMinX() + dx, getMinY() + dy, getWidth(), getHeight()); result.set(dx, dy); } /** Move this rectangle to avoid collision * with the reference rectangle. * * @param reference is the rectangle to avoid collision with. * @param displacementDirection is the direction of the allowed displacement (it is an input). * This vector is set according to the result before returning. * @param result the displacement vector. */ default void avoidCollisionWith(Rectangle2afp<?, ?, ?, ?, ?, ?> reference, Vector2D<?, ?> displacementDirection, Vector2D<?, ?> result) { assert reference != null : AssertMessages.notNullParameter(0); assert result != null : AssertMessages.notNullParameter(2); if (displacementDirection == null || displacementDirection.getLengthSquared() == 0) { avoidCollisionWith(reference, result); return; } final double dx1 = reference.getMaxX() - getMinX(); final double dx2 = reference.getMinX() - getMaxX(); final double dy1 = reference.getMaxY() - getMinY(); final double dy2 = reference.getMinY() - getMaxY(); final double absdx1 = Math.abs(dx1); final double absdx2 = Math.abs(dx2); final double absdy1 = Math.abs(dy1); final double absdy2 = Math.abs(dy2); final double dx; final double dy; if (displacementDirection.getX() < 0) { dx = -Math.min(absdx1, absdx2); } else { dx = Math.min(absdx1, absdx2); } if (displacementDirection.getY() < 0) { dy = -Math.min(absdy1, absdy2); } else { dy = Math.min(absdy1, absdy2); } set( getMinX() + dx, getMinY() + dy, getWidth(), getHeight()); displacementDirection.set(dx, dy); result.set(dx, dy); } @Pure @Override default P getClosestPointTo(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final double x; int same = 0; if (pt.getX() < getMinX()) { x = getMinX(); } else if (pt.getX() > getMaxX()) { x = getMaxX(); } else { x = pt.getX(); ++same; } final double y; if (pt.getY() < getMinY()) { y = getMinY(); } else if (pt.getY() > getMaxY()) { y = getMaxY(); } else { y = pt.getY(); ++same; } if (same == 2) { return getGeomFactory().convertToPoint(pt); } return getGeomFactory().newPoint(x, y); } @Override default P getClosestPointTo(Circle2afp<?, ?, ?, ?, ?, ?> circle) { assert circle != null : AssertMessages.notNullParameter(); return getClosestPointTo(circle.getCenter()); } @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 default P getClosestPointTo(Path2afp<?, ?, ?, ?, ?, ?> path) { assert path != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); Path2afp.findsClosestPointPathIteratorPathIterator(getPathIterator(), path.getPathIterator(), point); return point; } @Override default P getClosestPointTo(Rectangle2afp<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); findsClosestPointRectangleRectangle(getMinX(), getMinY(), getMaxX(), getMaxY(), rectangle.getMinX(), rectangle.getMinY(), rectangle.getMaxX(), rectangle.getMaxY(), point); return point; } @Override default P getClosestPointTo(Segment2afp<?, ?, ?, ?, ?, ?> segment) { assert segment != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); findsClosestPointRectangleSegment( getMinX(), getMinY(), getMaxX(), getMaxY(), segment.getX1(), segment.getY1(), segment.getX2(), segment.getY2(), point); return point; } @Override default P getClosestPointTo(Ellipse2afp<?, ?, ?, ?, ?, ?> ellipse) { assert ellipse != null : AssertMessages.notNullParameter(); final double cx = ellipse.getCenterX(); final double cy = ellipse.getCenterY(); final double sx = ellipse.getHorizontalRadius(); final double sy = ellipse.getVerticalRadius(); // Scale the rectangle final double rx = (getMinX() - cx) / sx; final double ry = (getMinY() - cy) / sy; final double rwidth = getWidth() / sx; final double rheight = getHeight() / sy; // Compute the closest point final P closest = getGeomFactory().newPoint(); findsClosestPointRectanglePoint(rx, ry, rx + rwidth, ry + rheight, 0, 0, closest); // Invert scale closest.set(closest.getX() * sx + cx, closest.getY() * sy + cy); return closest; } @Override default P getClosestPointTo(RoundRectangle2afp<?, ?, ?, ?, ?, ?> roundRectangle) { assert roundRectangle != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); findsClosestPointRectangleRectangle(getMinX(), getMinY(), getMaxX(), getMaxY(), roundRectangle.getMinX(), roundRectangle.getMinY(), roundRectangle.getMaxX(), roundRectangle.getMaxY(), point); return point; } @Override default P getClosestPointTo(Parallelogram2afp<?, ?, ?, ?, ?, ?> parallelogram) { assert parallelogram != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); findsClosestPointRectangleParallelogram( getMinX(), getMinY(), getMaxX(), getMaxY(), parallelogram.getCenterX(), parallelogram.getCenterY(), parallelogram.getFirstAxisX(), parallelogram.getFirstAxisY(), parallelogram.getFirstAxisExtent(), parallelogram.getSecondAxisX(), parallelogram.getSecondAxisY(), parallelogram.getSecondAxisExtent(), point); return point; } @Override default P getClosestPointTo(OrientedRectangle2afp<?, ?, ?, ?, ?, ?> orientedRectangle) { assert orientedRectangle != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); findsClosestPointRectangleParallelogram( getMinX(), getMinY(), getMaxX(), getMaxY(), orientedRectangle.getCenterX(), orientedRectangle.getCenterY(), orientedRectangle.getFirstAxisX(), orientedRectangle.getFirstAxisY(), orientedRectangle.getFirstAxisExtent(), orientedRectangle.getSecondAxisX(), orientedRectangle.getSecondAxisY(), orientedRectangle.getSecondAxisExtent(), point); return point; } @Pure @Override default P getFarthestPointTo(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final double x; if (pt.getX() <= getCenterX()) { x = getMaxX(); } else { x = getMinX(); } final double y; if (pt.getY() <= getCenterY()) { y = getMaxY(); } else { y = getMinY(); } return getGeomFactory().newPoint(x, y); } @Pure @Override default PathIterator2afp<IE> getPathIterator(Transform2D transform) { if (transform == null || transform.isIdentity()) { return new RectanglePathIterator<>(this); } return new TransformedRectanglePathIterator<>(this, transform); } @Override default P getCenter() { return getGeomFactory().newPoint(getCenterX(), getCenterY()); } @Override default void setCenter(double cx, double cy) { final double demiWidth = getWidth() / 2.; final double demiHeight = getHeight() / 2.; setMinX(cx - demiWidth); setMinY(cy - demiHeight); setMaxX(cx + demiWidth); setMaxY(cy + demiHeight); } @Override default void setCenterX(double cx) { final double demiWidth = getWidth() / 2.; setMinX(cx - demiWidth); setMaxX(cx + demiWidth); } @Override default void setCenterY(double cy) { final double demiHeight = getHeight() / 2.; setMinY(cy - demiHeight); setMaxY(cy + demiHeight); } @Override default V getFirstAxis() { return getGeomFactory().newVector(getFirstAxisX(), getFirstAxisY()); } @Override default double getFirstAxisX() { return 1.; } @Override default double getFirstAxisY() { return 0.; } @Override default V getSecondAxis() { return getGeomFactory().newVector(getSecondAxisX(), getSecondAxisY()); } @Override default double getSecondAxisX() { return 0.; } @Override default double getSecondAxisY() { return 1.; } @Override default double getFirstAxisExtent() { return getWidth() / 2.; } @Override default void setFirstAxisExtent(double extent) { final double x = getCenterX(); setMinX(x - extent); setMaxX(x + extent); } @Override default double getSecondAxisExtent() { return getHeight() / 2.; } @Override default void setSecondAxisExtent(double extent) { final double y = getCenterY(); setMinY(y - extent); setMaxY(y + extent); } /** {@inheritDoc} * * <p>The rectangle is always aligned on the global axes. * It means that the rectangle is set to the enclosing box related to the given parameters. */ @Override default void setFirstAxis(double x, double y, double extent) { assert Vector2D.isUnitVector(x, y) : AssertMessages.normalizedParameters(0, 1); assert extent >= 0. : AssertMessages.positiveOrZeroParameter(2); set(getCenterX(), getCenterY(), x, y, extent, getSecondAxisExtent()); } /** {@inheritDoc} * * <p>The rectangle is always aligned on the global axes. * It means that the rectangle is set to the enclosing box related to the given parameters. */ @Override default void setSecondAxis(double x, double y, double extent) { assert Vector2D.isUnitVector(x, y) : AssertMessages.normalizedParameters(0, 1); assert extent >= 0. : AssertMessages.positiveOrZeroParameter(2); set(getCenterX(), getCenterY(), y, -x, getFirstAxisExtent(), extent); } // // Avoid multiple inheritance error // @Override default void clear() { RectangularShape2afp.super.clear(); } @Override default double getCenterX() { return RectangularShape2afp.super.getCenterX(); } @Override default double getCenterY() { return RectangularShape2afp.super.getCenterY(); } @Override default void translate(double dx, double dy) { RectangularShape2afp.super.translate(dx, dy); } @Override default void toBoundingBox(B box) { RectangularShape2afp.super.toBoundingBox(box); } @Override default boolean isEmpty() { return RectangularShape2afp.super.isEmpty(); } /** Iterator on the path elements of the rectangle. * * @param <T> the type of the path elements. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ @SuppressWarnings("checkstyle:magicnumber") class RectanglePathIterator<T extends PathElement2afp> implements PathIterator2afp<T> { private final Rectangle2afp<?, ?, T, ?, ?, ?> rectangle; private double x1; private double y1; private double x2; private double y2; private int index; /** * @param rectangle the iterated rectangle. */ public RectanglePathIterator(Rectangle2afp<?, ?, T, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); this.rectangle = rectangle; if (rectangle.isEmpty()) { this.index = 5; } else { this.x1 = rectangle.getMinX(); this.x2 = rectangle.getMaxX(); this.y1 = rectangle.getMinY(); this.y2 = rectangle.getMaxY(); } } @Override public PathIterator2afp<T> restartIterations() { return new RectanglePathIterator<>(this.rectangle); } @Pure @Override public boolean hasNext() { return this.index <= 4; } @Override public T next() { final int idx = this.index; ++this.index; switch (idx) { case 0: return this.rectangle.getGeomFactory().newMovePathElement( this.x1, this.y1); case 1: return this.rectangle.getGeomFactory().newLinePathElement( this.x1, this.y1, this.x2, this.y1); case 2: return this.rectangle.getGeomFactory().newLinePathElement( this.x2, this.y1, this.x2, this.y2); case 3: return this.rectangle.getGeomFactory().newLinePathElement( this.x2, this.y2, this.x1, this.y2); case 4: return this.rectangle.getGeomFactory().newClosePathElement( this.x1, this.y2, this.x1, this.y1); default: throw new NoSuchElementException(); } } @Override public void remove() { throw new UnsupportedOperationException(); } @Pure @Override public PathWindingRule getWindingRule() { return PathWindingRule.NON_ZERO; } @Pure @Override public boolean isPolyline() { return false; } @Pure @Override public boolean isCurved() { return false; } @Pure @Override public boolean isPolygon() { return true; } @Pure @Override public boolean isMultiParts() { return false; } @Override public GeomFactory2afp<T, ?, ?, ?> getGeomFactory() { return this.rectangle.getGeomFactory(); } } /** Iterator on the path elements of the rectangle. * * @param <T> the type of the path elements. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ @SuppressWarnings("checkstyle:magicnumber") class TransformedRectanglePathIterator<T extends PathElement2afp> implements PathIterator2afp<T> { private final Transform2D transform; private final Rectangle2afp<?, ?, T, ?, ?, ?> rectangle; private Point2D<?, ?> p1; private Point2D<?, ?> p2; private double x1; private double y1; private double x2; private double y2; private int index; /** * @param rectangle the iterated rectangle. * @param transform the transformation. */ public TransformedRectanglePathIterator(Rectangle2afp<?, ?, T, ?, ?, ?> rectangle, Transform2D transform) { assert rectangle != null : AssertMessages.notNullParameter(0); assert transform != null : AssertMessages.notNullParameter(1); this.rectangle = rectangle; this.transform = transform; if (rectangle.isEmpty()) { this.index = 5; } else { this.index = 0; this.p1 = new InnerComputationPoint2afp(); this.p2 = new InnerComputationPoint2afp(); this.x1 = rectangle.getMinX(); this.x2 = rectangle.getMaxX(); this.y1 = rectangle.getMinY(); this.y2 = rectangle.getMaxY(); } } @Override public PathIterator2afp<T> restartIterations() { return new TransformedRectanglePathIterator<>(this.rectangle, this.transform); } @Pure @Override public boolean hasNext() { return this.index <= 4; } @Override public T next() { final int idx = this.index; ++this.index; switch (idx) { case 0: this.p2.set(this.x1, this.y1); this.transform.transform(this.p2); return this.rectangle.getGeomFactory().newMovePathElement( this.p2.getX(), this.p2.getY()); case 1: this.p1.set(this.p2); this.p2.set(this.x2, this.y1); this.transform.transform(this.p2); return this.rectangle.getGeomFactory().newLinePathElement( this.p1.getX(), this.p1.getY(), this.p2.getX(), this.p2.getY()); case 2: this.p1.set(this.p2); this.p2.set(this.x2, this.y2); this.transform.transform(this.p2); return this.rectangle.getGeomFactory().newLinePathElement( this.p1.getX(), this.p1.getY(), this.p2.getX(), this.p2.getY()); case 3: this.p1.set(this.p2); this.p2.set(this.x1, this.y2); this.transform.transform(this.p2); return this.rectangle.getGeomFactory().newLinePathElement( this.p1.getX(), this.p1.getY(), this.p2.getX(), this.p2.getY()); case 4: this.p1.set(this.p2); this.p2.set(this.x1, this.y1); this.transform.transform(this.p2); return this.rectangle.getGeomFactory().newClosePathElement( this.p1.getX(), this.p1.getY(), this.p2.getX(), this.p2.getY()); default: throw new NoSuchElementException(); } } @Override public void remove() { throw new UnsupportedOperationException(); } @Pure @Override public PathWindingRule getWindingRule() { return PathWindingRule.NON_ZERO; } @Pure @Override public boolean isPolyline() { return false; } @Pure @Override public boolean isCurved() { return false; } @Pure @Override public boolean isPolygon() { return true; } @Pure @Override public boolean isMultiParts() { return false; } @Override public GeomFactory2afp<T, ?, ?, ?> getGeomFactory() { return this.rectangle.getGeomFactory(); } } }