/*
* $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.ai;
import java.util.Iterator;
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.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.math.geometry.d2.ai.Segment2ai.BresenhamLineIterator;
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
*/
public interface Rectangle2ai<
ST extends Shape2ai<?, ?, IE, P, V, B>,
IT extends Rectangle2ai<?, ?, IE, P, V, B>,
IE extends PathElement2ai,
P extends Point2D<? super P, ? super V>,
V extends Vector2D<? super V, ? super P>,
B extends Rectangle2ai<?, ?, IE, P, V, B>>
extends RectangularShape2ai<ST, IT, IE, P, V, B> {
/** 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(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int 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 a rectangle is intersecting a segment.
*
* <p>The intersection test is partly based on the Cohen-Sutherland
* classification of the segment.
* This classification permits to detect the base cases;
* and to run a clipping-like algorithm for the intersection
* detection.
*
* @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 segment.
* @param y3 is the first point of the segment.
* @param x4 is the second point of the segment.
* @param y4 is the second point of the segment.
* @return <code>true</code> if the two shapes are intersecting; otherwise
* <code>false</code>
*/
@Pure
@SuppressWarnings("checkstyle:cyclomaticcomplexity")
static boolean intersectsRectangleSegment(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
assert x1 <= x2 : AssertMessages.lowerEqualParameters(0, x1, 2, x2);
assert y1 <= y2 : AssertMessages.lowerEqualParameters(1, y1, 3, y2);
int c1 = MathUtil.getCohenSutherlandCode(x3, y3, x1, y1, x2, y2);
final int c2 = MathUtil.getCohenSutherlandCode(x4, y4, x1, y1, x2, y2);
if (c1 == MathConstants.COHEN_SUTHERLAND_INSIDE || c2 == MathConstants.COHEN_SUTHERLAND_INSIDE) {
return true;
}
if ((c1 & c2) != 0) {
return false;
}
int sx1 = x3;
int sy1 = y3;
final int sx2 = x4;
final int sy2 = y4;
// Only for internal use
final Point2D<?, ?> pts = new InnerComputationPoint2ai();
final BresenhamLineIterator<InnerComputationPoint2ai, InnerComputationVector2ai> iterator =
new BresenhamLineIterator<>(
InnerComputationGeomFactory.SINGLETON, sx1, sy1, sx2, sy2);
while (iterator.hasNext() && c1 != MathConstants.COHEN_SUTHERLAND_INSIDE
&& c2 != MathConstants.COHEN_SUTHERLAND_INSIDE && (c1 & c2) == 0) {
if ((c1 & MathConstants.COHEN_SUTHERLAND_TOP) != 0) {
do {
iterator.next(pts);
sy1 = pts.iy();
}
while (iterator.hasNext() && sy1 != y2);
if (sy1 != y2) {
return false;
}
sx1 = pts.ix();
} else if ((c1 & MathConstants.COHEN_SUTHERLAND_BOTTOM) != 0) {
do {
iterator.next(pts);
sy1 = pts.iy();
}
while (iterator.hasNext() && sy1 != y1);
if (sy1 != y1) {
return false;
}
sx1 = pts.ix();
} else if ((c1 & MathConstants.COHEN_SUTHERLAND_RIGHT) != 0) {
do {
iterator.next(pts);
sx1 = pts.ix();
}
while (iterator.hasNext() && sx1 != x2);
if (sx1 != x2) {
return false;
}
sy1 = pts.iy();
} else {
do {
iterator.next(pts);
sx1 = pts.ix();
}
while (iterator.hasNext() && sx1 != x1);
if (sx1 != x1) {
return false;
}
sy1 = pts.iy();
}
c1 = MathUtil.getCohenSutherlandCode(sx1, sy1, x1, y1, x2, y2);
}
return c1 == MathConstants.COHEN_SUTHERLAND_INSIDE || c2 == MathConstants.COHEN_SUTHERLAND_INSIDE;
}
/** 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(int, int, int, int, int, int, int, int, Point2D)}
*/
@Deprecated
@SuppressWarnings("checkstyle:parameternumber")
static void computeClosestPointRectangleRectangle(
int rx1, int ry1, int rmaxx1, int rmaxy1,
int rx2, int ry2, int rmaxx2, int 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(
int rx1, int ry1, int rmaxx1, int rmaxy1,
int rx2, int ry2, int rmaxx2, int 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, ry2, 7, rmaxy2);
final int px;
final int cx = (rx2 + rmaxx2) / 2;
if (cx <= rx1) {
px = rx1;
} else if (cx >= rmaxx1) {
px = rmaxx1;
} else {
px = cx;
}
final int py;
final int cy = (ry2 + rmaxy2) / 2;
if (cy <= ry1) {
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(int, int, int, int, int, int, int, int, Point2D)}
*/
@Deprecated
@SuppressWarnings("checkstyle:parameternumber")
static void computeClosestPointRectangleSegment(
int rx, int ry, int rmaxx, int rmaxy,
int sx1, int sy1, int sx2, int 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(
int rx, int ry, int rmaxx, int rmaxy,
int sx1, int sy1, int sx2, int 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 InnerComputationPoint2ai();
final int zone = Rectangle2ai.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 closest point on the rectangle from the given point.
*
* @param minx is the x-coordinate of the lowest coordinate of the rectangle.
* @param miny is the y-coordinate of the lowest coordinate of the rectangle.
* @param maxx is the x-coordinate of the highest coordinate of the rectangle.
* @param maxy is the y-coordinate of the highest coordinate of the rectangle.
* @param px is the x-coordinate of the point.
* @param py is the y-coordinate of the point.
* @param result the closest point.
* @deprecated since 13.0, see {@link #findsClosestPointRectanglePoint(int, int, int, int, int, int, Point2D)}
*/
@Deprecated
static void computeClosestPointRectanglePoint(int minx, int miny, int maxx, int maxy, int px, int py, Point2D<?, ?> result) {
findsClosestPointRectanglePoint(minx, miny, maxx, maxy, px, py, result);
}
/** Compute the closest point on the rectangle from the given point.
*
* @param minx is the x-coordinate of the lowest coordinate of the rectangle.
* @param miny is the y-coordinate of the lowest coordinate of the rectangle.
* @param maxx is the x-coordinate of the highest coordinate of the rectangle.
* @param maxy is the y-coordinate of the highest coordinate of the rectangle.
* @param px is the x-coordinate of the point.
* @param py is the y-coordinate of the point.
* @param result the closest point.
*/
@SuppressWarnings("checkstyle:magicnumber")
static void findsClosestPointRectanglePoint(int minx, int miny, int maxx, int maxy, int px, int py, Point2D<?, ?> result) {
assert minx <= maxx : AssertMessages.lowerEqualParameters(0, minx, 2, maxx);
assert miny <= maxy : AssertMessages.lowerEqualParameters(1, miny, 3, maxy);
assert result != null : AssertMessages.notNullParameter(6);
final int x;
int same = 0;
if (px < minx) {
x = minx;
} else if (px > maxx) {
x = maxx;
} else {
x = px;
++same;
}
final int y;
if (py < miny) {
y = miny;
} else if (py > maxy) {
y = maxy;
} else {
y = py;
++same;
}
if (same == 2) {
result.set(px, py);
} else {
result.set(x, y);
}
}
/** Compute the farthest point on the rectangle from the given point.
*
* @param minx is the x-coordinate of the lowest coordinate of the rectangle.
* @param miny is the y-coordinate of the lowest coordinate of the rectangle.
* @param maxx is the x-coordinate of the highest coordinate of the rectangle.
* @param maxy is the y-coordinate of the highest coordinate of the rectangle.
* @param px is the x-coordinate of the point.
* @param py is the y-coordinate of the point.
* @param result the farthest point.
* @deprecated since 13.0, see {@link #findsFarthestPointRectanglePoint(int, int, int, int, int, int, Point2D)}
*/
@Deprecated
static void computeFarthestPoint(int minx, int miny, int maxx, int maxy, int px, int py, Point2D<?, ?> result) {
findsFarthestPointRectanglePoint(minx, miny, maxx, maxy, px, py, result);
}
/** Compute the farthest point on the rectangle from the given point.
*
* @param minx is the x-coordinate of the lowest coordinate of the rectangle.
* @param miny is the y-coordinate of the lowest coordinate of the rectangle.
* @param maxx is the x-coordinate of the highest coordinate of the rectangle.
* @param maxy is the y-coordinate of the highest coordinate of the rectangle.
* @param px is the x-coordinate of the point.
* @param py is the y-coordinate of the point.
* @param result the farthest point.
*/
@SuppressWarnings("checkstyle:magicnumber")
static void findsFarthestPointRectanglePoint(int minx, int miny, int maxx, int maxy, int px, int py, Point2D<?, ?> result) {
assert minx <= maxx : AssertMessages.lowerEqualParameters(0, minx, 2, maxx);
assert miny <= maxy : AssertMessages.lowerEqualParameters(1, miny, 3, maxy);
assert result != null : AssertMessages.notNullParameter(6);
final int x;
if (px <= ((minx + maxx) / 2)) {
x = maxx;
} else {
x = minx;
}
final int y;
if (py <= ((miny + maxy) / 2)) {
y = maxy;
} else {
y = miny;
}
result.set(x, y);
}
/** 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(int, int, int, int,
* int, int, int, int, int, int, Point2D, Point2D)}
*/
@Deprecated
@SuppressWarnings("checkstyle:parameternumber")
static int reduceCohenSutherlandZoneRectangleSegment(int rx1, int ry1, int rx2, int ry2,
int sx1, int sy1, int sx2, int 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(int rx1, int ry1, int rx2, int ry2,
int sx1, int sy1, int sx2, int 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);
int segmentX1 = sx1;
int segmentY1 = sy1;
int segmentX2 = sx2;
int 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;
int x = 0;
int 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);
}
}
}
}
@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();
}
@Pure
@Override
default boolean intersects(Rectangle2ai<?, ?, ?, ?, ?, ?> 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(Circle2ai<?, ?, ?, ?, ?, ?> circle) {
assert circle != null : AssertMessages.notNullParameter();
return Circle2ai.intersectsCircleRectangle(
circle.getX(), circle.getY(),
circle.getRadius(),
getMinX(), getMinY(),
getMaxX(), getMaxY());
}
@Pure
@Override
default boolean intersects(Segment2ai<?, ?, ?, ?, ?, ?> 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(PathIterator2ai<?> iterator) {
assert iterator != null : AssertMessages.notNullParameter();
final int mask = iterator.getWindingRule() == PathWindingRule.NON_ZERO ? -1 : 2;
final int crossings = Path2ai.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(MultiShape2ai<?, ?, ?, ?, ?, ?, ?> multishape) {
assert multishape != null : AssertMessages.notNullParameter();
return multishape.intersects(this);
}
@Pure
@Override
default boolean contains(int x, int y) {
return x >= getMinX() && x <= this.getMaxX() && y >= getMinY() && y <= getMaxY();
}
@Pure
@Override
default boolean contains(Rectangle2ai<?, ?, ?, ?, ?, ?> box) {
assert box != null : AssertMessages.notNullParameter();
return box.getMinX() >= getMinX() && box.getMaxX() <= getMaxX()
&& box.getMinY() >= getMinY() && box.getMaxY() <= getMaxY();
}
@Override
default void set(IT shape) {
assert shape != null : AssertMessages.notNullParameter();
setFromCorners(shape.getMinX(), shape.getMinY(), shape.getMaxX(), shape.getMaxY());
}
@Pure
@Override
default P getClosestPointTo(Point2D<?, ?> pt) {
assert pt != null : AssertMessages.notNullParameter();
final P point = getGeomFactory().newPoint();
findsClosestPointRectanglePoint(getMinX(), getMinY(), getMaxX(), getMaxY(), pt.ix(), pt.iy(), point);
return point;
}
@Override
default P getClosestPointTo(Rectangle2ai<?, ?, ?, ?, ?, ?> 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(Circle2ai<?, ?, ?, ?, ?, ?> circle) {
assert circle != null : AssertMessages.notNullParameter();
return getClosestPointTo(circle.getCenter());
}
@Override
default P getClosestPointTo(Segment2ai<?, ?, ?, ?, ?, ?> 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(Path2ai<?, ?, ?, ?, ?, ?> path) {
assert path != null : AssertMessages.notNullParameter();
final P point = getGeomFactory().newPoint();
Path2ai.findsClosestPointPathIteratorPathIterator(getPathIterator(), path.getPathIterator(), point);
return point;
}
@Pure
@Override
default P getFarthestPointTo(Point2D<?, ?> pt) {
assert pt != null : AssertMessages.notNullParameter();
final P point = getGeomFactory().newPoint();
findsFarthestPointRectanglePoint(getMinX(), getMinY(), getMaxX(), getMaxY(), pt.ix(), pt.iy(), point);
return point;
}
@Pure
@Override
default double getDistanceSquared(Point2D<?, ?> pt) {
assert pt != null : AssertMessages.notNullParameter();
final int dx;
if (pt.ix() < getMinX()) {
dx = getMinX() - pt.ix();
} else if (pt.ix() > getMaxX()) {
dx = pt.ix() - getMaxX();
} else {
dx = 0;
}
final int dy;
if (pt.iy() < getMinY()) {
dy = getMinY() - pt.iy();
} else if (pt.iy() > getMaxY()) {
dy = pt.iy() - getMaxY();
} else {
dy = 0;
}
return dx * dx + dy * dy;
}
@Pure
@Override
default double getDistanceL1(Point2D<?, ?> pt) {
assert pt != null : AssertMessages.notNullParameter();
final int dx;
if (pt.ix() < getMinX()) {
dx = getMinX() - pt.ix();
} else if (pt.ix() > getMaxX()) {
dx = pt.ix() - getMaxX();
} else {
dx = 0;
}
final int dy;
if (pt.iy() < getMinY()) {
dy = getMinY() - pt.iy();
} else if (pt.iy() > getMaxY()) {
dy = pt.iy() - getMaxY();
} else {
dy = 0;
}
return dx + dy;
}
@Pure
@Override
default double getDistanceLinf(Point2D<?, ?> pt) {
assert pt != null : AssertMessages.notNullParameter();
final int dx;
if (pt.ix() < getMinX()) {
dx = getMinX() - pt.ix();
} else if (pt.ix() > getMaxX()) {
dx = pt.ix() - getMaxX();
} else {
dx = 0;
}
final int dy;
if (pt.iy() < getMinY()) {
dy = getMinY() - pt.iy();
} else if (pt.iy() > getMaxY()) {
dy = pt.iy() - getMaxY();
} else {
dy = 0;
}
return Math.max(dx, dy);
}
@Pure
@Override
default Iterator<P> getPointIterator() {
return getPointIterator(Side.TOP);
}
/** Replies the points on the bounds of the rectangle.
*
* @param startingBorder is the first border to reply.
* @return the points on the bounds of the rectangle.
*/
@Pure
default Iterator<P> getPointIterator(Side startingBorder) {
assert startingBorder != null : AssertMessages.notNullParameter();
return new RectangleSideIterator<>(this, startingBorder);
}
@Override
default PathIterator2ai<IE> getPathIterator(Transform2D transform) {
if (transform == null || transform.isIdentity()) {
return new RectanglePathIterator<>(this);
}
return new TransformedRectanglePathIterator<>(this, transform);
}
/** 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(RectangularShape2ai)
*/
@Pure
default B createUnion(RectangularShape2ai<?, ?, ?, ?, ?, ?> 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(RectangularShape2ai)
*/
@Pure
default B createIntersection(RectangularShape2ai<?, ?, ?, ?, ?, ?> rect) {
assert rect != null : AssertMessages.notNullParameter();
final B rr = getGeomFactory().newBox();
final int x1 = Math.max(getMinX(), rect.getMinX());
final int y1 = Math.max(getMinY(), rect.getMinY());
final int x2 = Math.min(getMaxX(), rect.getMaxX());
final int 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(RectangularShape2ai)
*/
default void setUnion(RectangularShape2ai<?, ?, ?, ?, ?, ?> 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(RectangularShape2ai)
* @see #clear()
*/
default void setIntersection(RectangularShape2ai<?, ?, ?, ?, ?, ?> rect) {
assert rect != null : AssertMessages.notNullParameter();
final int x1 = Math.max(getMinX(), rect.getMinX());
final int y1 = Math.max(getMinY(), rect.getMinY());
final int x2 = Math.min(getMaxX(), rect.getMaxX());
final int y2 = Math.min(getMaxY(), rect.getMaxY());
if (x1 <= x2 && y1 <= y2) {
setFromCorners(x1, y1, x2, y2);
} else {
clear();
}
}
/** Sides of a rectangle.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
*/
enum Side {
/** Top.
*/
TOP,
/** Right.
*/
RIGHT,
/** Bottom.
*/
BOTTOM,
/** Left.
*/
LEFT;
}
/** Iterates on points on the sides of a rectangle.
*
* @param <P> type of the points.
* @param <V> type of the vectors.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
*/
class RectangleSideIterator<P extends Point2D<? super P, ? super V>,
V extends Vector2D<? super V, ? super P>> implements Iterator<P> {
private final GeomFactory2ai<?, P, V, ?> factory;
private final int x0;
private final int y0;
private final int x1;
private final int y1;
private final Side firstSide;
private Side currentSide;
private int index;
/**
* @param rectangle is the rectangle to iterate.
* @param firstSide the first side to iterate on.
*/
RectangleSideIterator(Rectangle2ai<?, ?, ?, P, V, ?> rectangle, Side firstSide) {
assert rectangle != null : AssertMessages.notNullParameter(0);
assert firstSide != null : AssertMessages.notNullParameter(1);
this.factory = rectangle.getGeomFactory();
this.firstSide = firstSide;
this.x0 = rectangle.getMinX();
this.y0 = rectangle.getMinY();
this.x1 = rectangle.getMaxX();
this.y1 = rectangle.getMaxY();
this.currentSide = (this.x1 > this.x0 && this.y1 > this.y0) ? this.firstSide : null;
this.index = 0;
}
@Pure
@Override
public boolean hasNext() {
return this.currentSide != null;
}
@Override
@SuppressWarnings("checkstyle:npathcomplexity")
public P next() {
final int x;
final int y;
switch (this.currentSide) {
case TOP:
x = this.x0 + this.index;
y = this.y0;
break;
case RIGHT:
x = this.x1;
y = this.y0 + this.index + 1;
break;
case BOTTOM:
x = this.x1 - this.index - 1;
y = this.y1;
break;
case LEFT:
x = this.x0;
y = this.y1 - this.index - 1;
break;
default:
throw new NoSuchElementException();
}
++this.index;
Side newSide = null;
switch (this.currentSide) {
case TOP:
if (x >= this.x1) {
newSide = Side.RIGHT;
this.index = 0;
}
break;
case RIGHT:
if (y >= this.y1) {
newSide = Side.BOTTOM;
this.index = 0;
}
break;
case BOTTOM:
if (x <= this.x0) {
newSide = Side.LEFT;
this.index = 0;
}
break;
case LEFT:
if (y <= this.y0 + 1) {
newSide = Side.TOP;
this.index = 0;
}
break;
default:
throw new NoSuchElementException();
}
if (newSide != null) {
this.currentSide = (this.firstSide == newSide) ? null : newSide;
}
return this.factory.newPoint(x, y);
}
}
/** Iterator on the path elements of the rectangle.
*
* @param <E> the type of the path elements.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
*/
@SuppressWarnings("checkstyle:magicnumber")
class RectanglePathIterator<E extends PathElement2ai> implements PathIterator2ai<E> {
private final Rectangle2ai<?, ?, E, ?, ?, ?> rectangle;
private int x1;
private int y1;
private int x2;
private int y2;
private int index;
/**
* @param rectangle is the rectangle to iterate.
*/
RectanglePathIterator(Rectangle2ai<?, ?, E, ?, ?, ?> rectangle) {
assert rectangle != null : AssertMessages.notNullParameter();
this.rectangle = rectangle;
this.x1 = rectangle.getMinX();
this.y1 = rectangle.getMinY();
this.x2 = rectangle.getMaxX();
this.y2 = rectangle.getMaxY();
}
@Override
public PathIterator2ai<E> restartIterations() {
return new RectanglePathIterator<>(this.rectangle);
}
@Pure
@Override
public boolean hasNext() {
return this.index <= 4;
}
@Override
public E 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();
}
}
@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 isMultiParts() {
return false;
}
@Override
public boolean isPolygon() {
return true;
}
@Override
public GeomFactory2ai<E, ?, ?, ?> getGeomFactory() {
return this.rectangle.getGeomFactory();
}
}
/** Iterator on the path elements of the rectangle.
*
* @param <E> the type of the path elements.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 13.0
*/
@SuppressWarnings("checkstyle:magicnumber")
class TransformedRectanglePathIterator<E extends PathElement2ai> implements PathIterator2ai<E> {
private final Rectangle2ai<?, ?, E, ?, ?, ?> rectangle;
private final Transform2D transform;
private int x1;
private int y1;
private int x2;
private int y2;
private int index;
private Point2D<?, ?> move;
private Point2D<?, ?> p1;
private Point2D<?, ?> p2;
/**
* @param rectangle is the rectangle to iterate.
* @param transform the transformation to apply on the rectangle.
*/
TransformedRectanglePathIterator(Rectangle2ai<?, ?, E, ?, ?, ?> rectangle, Transform2D transform) {
assert rectangle != null : AssertMessages.notNullParameter(0);
assert transform != null : AssertMessages.notNullParameter(1);
this.rectangle = rectangle;
this.transform = transform;
this.move = new InnerComputationPoint2ai();
this.p1 = new InnerComputationPoint2ai();
this.p2 = new InnerComputationPoint2ai();
this.x1 = rectangle.getMinX();
this.y1 = rectangle.getMinY();
this.x2 = rectangle.getMaxX();
this.y2 = rectangle.getMaxY();
}
@Override
public PathIterator2ai<E> restartIterations() {
return new TransformedRectanglePathIterator<>(this.rectangle, this.transform);
}
@Pure
@Override
public boolean hasNext() {
return this.index <= 4;
}
@Override
public E next() {
final int idx = this.index;
++this.index;
switch (idx) {
case 0:
this.p2.set(this.x1, this.y1);
if (this.transform != null) {
this.transform.transform(this.p2);
}
this.move.set(this.p2);
return this.rectangle.getGeomFactory().newMovePathElement(
this.p2.ix(), this.p2.iy());
case 1:
this.p1.set(this.p2);
this.p2.set(this.x2, this.y1);
if (this.transform != null) {
this.transform.transform(this.p2);
}
return this.rectangle.getGeomFactory().newLinePathElement(
this.p1.ix(), this.p1.iy(),
this.p2.ix(), this.p2.iy());
case 2:
this.p1.set(this.p2);
this.p2.set(this.x2, this.y2);
if (this.transform != null) {
this.transform.transform(this.p2);
}
return this.rectangle.getGeomFactory().newLinePathElement(
this.p1.ix(), this.p1.iy(),
this.p2.ix(), this.p2.iy());
case 3:
this.p1.set(this.p2);
this.p2.set(this.x1, this.y2);
if (this.transform != null) {
this.transform.transform(this.p2);
}
return this.rectangle.getGeomFactory().newLinePathElement(
this.p1.ix(), this.p1.iy(),
this.p2.ix(), this.p2.iy());
case 4:
return this.rectangle.getGeomFactory().newClosePathElement(
this.p2.ix(), this.p2.iy(),
this.move.ix(), this.move.iy());
default:
throw new NoSuchElementException();
}
}
@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 isMultiParts() {
return false;
}
@Override
public boolean isPolygon() {
return true;
}
@Override
public GeomFactory2ai<E, ?, ?, ?> getGeomFactory() {
return this.rectangle.getGeomFactory();
}
}
}