/* * $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.Inline; import org.eclipse.xtext.xbase.lib.Pure; import org.arakhne.afc.math.MathConstants; import org.arakhne.afc.math.MathUtil; import org.arakhne.afc.math.Unefficient; import org.arakhne.afc.math.geometry.CrossingComputationType; import org.arakhne.afc.math.geometry.PathWindingRule; import org.arakhne.afc.math.geometry.d2.GeomFactory2D; import org.arakhne.afc.math.geometry.d2.Point2D; import org.arakhne.afc.math.geometry.d2.Shape2D; import org.arakhne.afc.math.geometry.d2.Transform2D; import org.arakhne.afc.math.geometry.d2.Vector2D; import org.arakhne.afc.math.geometry.d2.afp.Segment2afp; import org.arakhne.afc.util.OutputParameter; import org.arakhne.afc.vmutil.asserts.AssertMessages; /** Fonctional interface that represented a 2D segment/line on a plane. * * @param <ST> is the type of the general implementation. * @param <IT> is the type of the implementation of this shape. * @param <IE> is the type of the path elements. * @param <P> is the type of the points. * @param <V> is the type of the vectors. * @param <B> is the type of the bounding boxes. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ @SuppressWarnings("checkstyle:methodcount") public interface Segment2ai< ST extends Shape2ai<?, ?, IE, P, V, B>, IT extends Segment2ai<?, ?, 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 Shape2ai<ST, IT, IE, P, V, B> { /** Replies the closest point in a circle to a point. * * @param ax is the x-coordinate of the first point of the segment * @param ay is the y-coordinate of the first point of the segment * @param bx is the x-coordinate of the second point of the segment * @param by is the y-coordinate of the second point of the segment * @param px is the x-coordinate of the point * @param py is the x-coordinate of the point * @param result the closest point in the segment to the point. * @deprecated since 13.0, see {@link #findsClosestPointSegmentPoint(int, int, int, int, int, int, Point2D)} */ @Deprecated static void computeClosestPointToPoint(int ax, int ay, int bx, int by, int px, int py, Point2D<?, ?> result) { findsClosestPointSegmentPoint(ax, ay, bx, by, px, py, result); } /** Replies the closest point in a circle to a point. * * @param ax is the x-coordinate of the first point of the segment * @param ay is the y-coordinate of the first point of the segment * @param bx is the x-coordinate of the second point of the segment * @param by is the y-coordinate of the second point of the segment * @param px is the x-coordinate of the point * @param py is the x-coordinate of the point * @param result the closest point in the segment to the point. */ @SuppressWarnings("checkstyle:magicnumber") static void findsClosestPointSegmentPoint(int ax, int ay, int bx, int by, int px, int py, Point2D<?, ?> result) { assert result != null : AssertMessages.notNullParameter(6); // Special case // 0 1 2 3 4 5 6 7 8 9 10 // 5) | | | | | | | | | | |X| // 4) | | |O| | | | | |X|X| | // 3) | | | | | | |X|X| | | | // 2) | | | | |X|X| | | | | | // 1) | | |X|X| | | | | | | | // 0) |X|X| | | | | | | | | | // // The closest point to point O is (4;2) even // if the distance is increasing between (2;1) // and (4;2). The algo must take this special // case into account. int minDist = Integer.MAX_VALUE; result.set(ax, ay); // Only for internal use final InnerComputationPoint2ai cp = new InnerComputationPoint2ai(); final BresenhamLineIterator<InnerComputationPoint2ai, InnerComputationVector2ai> iterator = new BresenhamLineIterator<>( InnerComputationGeomFactory.SINGLETON, ax, ay, bx, by); while (iterator.hasNext()) { iterator.next(cp); final int a = Math.abs(px - cp.ix()); final int b = Math.abs(py - cp.iy()); final int d = a * a + b * b; if (d == 0) { // We are sure that the closest point was found result.set(cp); return; } if (d < minDist) { minDist = d; result.set(cp); } } } /** Replies the point on the segment that is closest to the rectangle. * * @param sx1 is the x coordinate of the first point of the segment. * @param sy1 is the y coordinate of the first point of the segment. * @param sx2 is the x coordinate of the second point of the segment. * @param sy2 is the y coordinate of the second point of the segment. * @param rx is the x coordinate of the rectangle. * @param ry is the y coordinate of the rectangle. * @param rwidth is the width of the rectangle. * @param rheight is the height of the rectangle. * @param result the is point on the segment. * @return the square distance between the segments. * @deprecated since 13.0, see {@link #findsClosestPointSegmentRectangle(int, int, int, int, int, int, int, int, Point2D)} */ @Deprecated @SuppressWarnings("checkstyle:parameternumber") static double computeClosestPointToRectangle(int sx1, int sy1, int sx2, int sy2, int rx, int ry, int rwidth, int rheight, Point2D<?, ?> result) { return findsClosestPointSegmentRectangle(sx1, sy1, sx2, sy2, rx, ry, rwidth, rheight, result); } /** Replies the point on the segment that is closest to the rectangle. * * @param sx1 is the x coordinate of the first point of the segment. * @param sy1 is the y coordinate of the first point of the segment. * @param sx2 is the x coordinate of the second point of the segment. * @param sy2 is the y coordinate of the second point of the segment. * @param rx is the x coordinate of the rectangle. * @param ry is the y coordinate of the rectangle. * @param rwidth is the width of the rectangle. * @param rheight is the height of the rectangle. * @param result the is point on the segment. * @return the square distance between the segments. */ @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:magicnumber"}) static double findsClosestPointSegmentRectangle(int sx1, int sy1, int sx2, int sy2, int rx, int ry, int rwidth, int rheight, Point2D<?, ?> result) { assert rwidth >= 0. : AssertMessages.positiveOrZeroParameter(6); assert rheight >= 0. : AssertMessages.positiveOrZeroParameter(7); final int rmaxx = rx + rwidth; final int rmaxy = ry + rheight; final int code1 = MathUtil.getCohenSutherlandCode(sx1, sy1, rx, ry, rmaxx, rmaxy); final int code2 = MathUtil.getCohenSutherlandCode(sx2, sy2, rx, ry, rmaxx, rmaxy); final Point2D<?, ?> tmp1 = new InnerComputationPoint2ai(); final Point2D<?, ?> tmp2 = new InnerComputationPoint2ai(); final int zone = Rectangle2ai.reducesCohenSutherlandZoneRectangleSegment( rx, ry, rmaxx, rmaxy, sx1, sy1, sx2, sy2, code1, code2, tmp1, tmp2); if ((zone & MathConstants.COHEN_SUTHERLAND_LEFT) != 0) { return findsClosestPointsSegmentSegment( sx1, sy1, sx2, sy2, rx, ry, rx, rmaxy, result, null); } if ((zone & MathConstants.COHEN_SUTHERLAND_RIGHT) != 0) { return findsClosestPointsSegmentSegment( sx1, sy1, sx2, sy2, rmaxx, ry, rmaxx, rmaxy, result, null); } if ((zone & MathConstants.COHEN_SUTHERLAND_BOTTOM) != 0) { return findsClosestPointsSegmentSegment( sx1, sy1, sx2, sy2, rx, ry, rmaxx, ry, result, null); } if ((zone & MathConstants.COHEN_SUTHERLAND_TOP) != 0) { return findsClosestPointsSegmentSegment( sx1, sy1, sx2, sy2, rx, rmaxy, rmaxx, rmaxy, result, null); } if (result != null) { findsClosestPointSegmentPoint( tmp1.ix(), tmp1.iy(), tmp2.ix(), tmp2.iy(), (rx + rmaxx) / 2, (ry + rmaxy) / 2, result); } return 0; } /** Replies the point on the first segment that is closest to the second segment. * * @param s1x1 is the x coordinate of the first point of the first segment. * @param s1y1 is the y coordinate of the first point of the first segment. * @param s1x2 is the x coordinate of the second point of the first segment. * @param s1y2 is the y coordinate of the second point of the first segment. * @param s2x1 is the x coordinate of the first point of the second segment. * @param s2y1 is the y coordinate of the first point of the second segment. * @param s2x2 is the x coordinate of the second point of the second segment. * @param s2y2 is the y coordinate of the second point of the second segment. * @param result the is point on the shape. * @return the square distance between the segments. * @deprecated since 13.0, see {@link #findsClosestPointSegmentSegment(int, int, int, int, int, int, int, int, Point2D)} */ @Deprecated @SuppressWarnings("checkstyle:parameternumber") static double computeClosestPointToSegment( int s1x1, int s1y1, int s1x2, int s1y2, int s2x1, int s2y1, int s2x2, int s2y2, Point2D<?, ?> result) { return findsClosestPointSegmentSegment(s1x1, s1y1, s1x2, s1y2, s2x1, s2y1, s2x2, s2y2, result); } /** Replies the point on the first segment that is closest to the second segment. * * @param s1x1 is the x coordinate of the first point of the first segment. * @param s1y1 is the y coordinate of the first point of the first segment. * @param s1x2 is the x coordinate of the second point of the first segment. * @param s1y2 is the y coordinate of the second point of the first segment. * @param s2x1 is the x coordinate of the first point of the second segment. * @param s2y1 is the y coordinate of the first point of the second segment. * @param s2x2 is the x coordinate of the second point of the second segment. * @param s2y2 is the y coordinate of the second point of the second segment. * @param resultOnFirstSegment the point on the first segment. * @param resultOnSecondSegment the point on the second segment. * @return the square distance between the segments. * @deprecated since 13.0, see {@link #findsClosestPointsSegmentSegment(int, int, int, * int, int, int, int, int, Point2D, Point2D)} */ @SuppressWarnings("checkstyle:parameternumber") @Deprecated static double computeClosestPointToSegment( int s1x1, int s1y1, int s1x2, int s1y2, int s2x1, int s2y1, int s2x2, int s2y2, Point2D<?, ?> resultOnFirstSegment, Point2D<?, ?> resultOnSecondSegment) { return findsClosestPointsSegmentSegment(s1x1, s1y1, s1x2, s1y2, s2x1, s2y1, s2x2, s2y2, resultOnFirstSegment, resultOnSecondSegment); } /** Replies the point on the first segment that is closest to the second segment. * * @param s1x1 is the x coordinate of the first point of the first segment. * @param s1y1 is the y coordinate of the first point of the first segment. * @param s1x2 is the x coordinate of the second point of the first segment. * @param s1y2 is the y coordinate of the second point of the first segment. * @param s2x1 is the x coordinate of the first point of the second segment. * @param s2y1 is the y coordinate of the first point of the second segment. * @param s2x2 is the x coordinate of the second point of the second segment. * @param s2y2 is the y coordinate of the second point of the second segment. * @param result the is point on the shape. * @return the square distance between the segments. */ @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:npathcomplexity"}) static double findsClosestPointSegmentSegment( int s1x1, int s1y1, int s1x2, int s1y2, int s2x1, int s2y1, int s2x2, int s2y2, Point2D<?, ?> result) { return findsClosestPointsSegmentSegment( s1x1, s1y1, s1x2, s1y2, s2x1, s2y1, s2x2, s2y2, result, null); } /** Replies the point on the first segment that is closest to the second segment. * * @param s1x1 is the x coordinate of the first point of the first segment. * @param s1y1 is the y coordinate of the first point of the first segment. * @param s1x2 is the x coordinate of the second point of the first segment. * @param s1y2 is the y coordinate of the second point of the first segment. * @param s2x1 is the x coordinate of the first point of the second segment. * @param s2y1 is the y coordinate of the first point of the second segment. * @param s2x2 is the x coordinate of the second point of the second segment. * @param s2y2 is the y coordinate of the second point of the second segment. * @param resultOnFirstSegment the point on the first segment. * @param resultOnSecondSegment the point on the second segment. * @return the square distance between the segments. */ @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:npathcomplexity", "checkstyle:cyclomaticcomplexity"}) @Unefficient static double findsClosestPointsSegmentSegment( int s1x1, int s1y1, int s1x2, int s1y2, int s2x1, int s2y1, int s2x2, int s2y2, Point2D<?, ?> resultOnFirstSegment, Point2D<?, ?> resultOnSecondSegment) { final Point2D<?, ?> c1 = new InnerComputationPoint2ai(); final Point2D<?, ?> c2 = new InnerComputationPoint2ai(); Segment2afp.findsClosestPointSegmentSegment(s1x1, s1y1, s1x2, s1y2, s2x1, s2y1, s2x2, s2y2, c1, c2); final int a = c1.ix(); final int b = c1.iy(); final int c = c2.ix(); final int d = c2.iy(); findsClosestPointSegmentPoint(s1x1, s1y1, s1x2, s1y2, c, d, c1); findsClosestPointSegmentPoint(s2x1, s2y1, s2x2, s2y2, a, b, c2); if (resultOnFirstSegment != null) { resultOnFirstSegment.set(c1); } if (resultOnSecondSegment != null) { resultOnSecondSegment.set(c2); } final double distance = c1.getDistanceSquared(c2); if (distance > 0. && distance <= 1.) { final int side1 = findsSideLinePoint(s1x1, s1y1, s1x2, s1y2, s2x1, s2y1); final int side2 = findsSideLinePoint(s1x1, s1y1, s1x2, s1y2, s2x2, s2y2); final int side3 = findsSideLinePoint(s2x1, s2y1, s2x2, s2y2, s1x1, s1y1); final int side4 = findsSideLinePoint(s2x1, s2y1, s2x2, s2y2, s1x2, s1y2); if (side1 == -side2 && side3 == -side4) { // Segment are intersecting return 0; } } // General case return distance; } /** Replies the farthest point on a segment to a point. * * @param ax is the x-coordinate of the first point of the segment * @param ay is the y-coordinate of the first point of the segment * @param bx is the x-coordinate of the second point of the segment * @param by is the y-coordinate of the second point of the segment * @param px is the x-coordinate of the point * @param py is the x-coordinate of the point * @param result the farthest point in the segment to the point. * @deprecated since 13.0, see {@link #findsFarthestPointSegmentPoint(int, int, int, int, int, int, Point2D)} */ @Deprecated static void computeFarthestPointToPoint(int ax, int ay, int bx, int by, int px, int py, Point2D<?, ?> result) { findsFarthestPointSegmentPoint(ax, ay, bx, by, px, py, result); } /** Replies the farthest point on a segment to a point. * * @param ax is the x-coordinate of the first point of the segment * @param ay is the y-coordinate of the first point of the segment * @param bx is the x-coordinate of the second point of the segment * @param by is the y-coordinate of the second point of the segment * @param px is the x-coordinate of the point * @param py is the x-coordinate of the point * @param result the farthest point in the segment to the point. */ @SuppressWarnings("checkstyle:magicnumber") static void findsFarthestPointSegmentPoint(int ax, int ay, int bx, int by, int px, int py, Point2D<?, ?> result) { assert result != null : AssertMessages.notNullParameter(6); final int v1x = px - ax; final int v1y = py - ay; final int v2x = px - bx; final int v2y = py - by; if ((v1x * v1x + v1y * v1y) >= (v2x * v2x + v2y * v2y)) { result.set(ax, ay); } else { result.set(bx, by); } } /** * Replies on which side of a line the given point is located. * * <p>A return value of 1 indicates that the line segment must turn in the direction * that takes the positive X axis towards the negative Y axis. In the default * coordinate system used by Java 2D, this direction is counterclockwise. * * <p>A return value of -1 indicates that the line segment must turn in the direction that takes the * positive X axis towards the positive Y axis. In the default coordinate system by Java 2D, this direction is clockwise. * * <p>A return value of 0 indicates that the point lies exactly on the line segment. * * @param x1 * the X coordinate of the start point of the specified line segment * @param y1 * the Y coordinate of the start point of the specified line segment * @param x2 * the X coordinate of the end point of the specified line segment * @param y2 * the Y coordinate of the end point of the specified line segment * @param px * the X coordinate of the specified point to be compared with the specified line segment * @param py * the Y coordinate of the specified point to be compared with the specified line segment * @return an integer that indicates the position of the third specified coordinates with respect to * the line segment formed by the first two specified coordinates. * @see MathUtil#isEpsilonZero(double) * @deprecated since 13.0, see {@link #findsSideLinePoint(int, int, int, int, int, int)} */ @Deprecated @Pure static int computeSideLinePoint(int x1, int y1, int x2, int y2, int px, int py) { return findsSideLinePoint(x1, y1, x2, y2, px, py); } /** * Replies on which side of a line the given point is located. * * <p>A return value of 1 indicates that the line segment must turn in the direction * that takes the positive X axis towards the negative Y axis. In the default * coordinate system used by Java 2D, this direction is counterclockwise. * * <p>A return value of -1 indicates that the line segment must turn in the direction that takes the * positive X axis towards the positive Y axis. In the default coordinate system by Java 2D, this direction is clockwise. * * <p>A return value of 0 indicates that the point lies exactly on the line segment. * * @param x1 * the X coordinate of the start point of the specified line segment * @param y1 * the Y coordinate of the start point of the specified line segment * @param x2 * the X coordinate of the end point of the specified line segment * @param y2 * the Y coordinate of the end point of the specified line segment * @param px * the X coordinate of the specified point to be compared with the specified line segment * @param py * the Y coordinate of the specified point to be compared with the specified line segment * @return an integer that indicates the position of the third specified coordinates with respect to * the line segment formed by the first two specified coordinates. * @see MathUtil#isEpsilonZero(double) */ @Pure static int findsSideLinePoint(int x1, int y1, int x2, int y2, int px, int py) { final int x21 = x2 - x1; final int y21 = y2 - y1; final int xp1 = px - x1; final int yp1 = py - y1; final int side = xp1 * y21 - yp1 * x21; return (side < 0) ? -1 : ((side > 0) ? 1 : 0); } /** * Calculates the number of times the line from (x0, y0) to (x1, y1) * crosses the ellipse (ex0, ey0) to (ex1, ey1) extending to the right. * * <p>When the line (x0;y0) to (x1;y1) is crossing one of the up or * bottom borders of the shadow of the circle, the crossings * count is increased or decreased, depending if the line is * going down or up, respectively. * In the following figure, the circle is represented. * The "shadow" is the projection of the circle on the right. * The red lines represent the up and bottom borders. * * <p><a href="doc-files/crossing_circle.png"><img alt="" src="doc-files/crossing_circle.png" width="300"></a> * * @param crossings is the initial value for the number of crossings. * @param cx is the center of the circle to extend. * @param cy is the center of the circle to extend. * @param radius is the radius of the circle to extend. * @param x0 is the first point of the line. * @param y0 is the first point of the line. * @param x1 is the second point of the line. * @param y1 is the secondpoint of the line. * @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}. * @deprecated since 13.0, see {@link #calculatesCrossingsCircleShadowSegment(int, int, int, int, int, int, int, int)} */ @Pure @Deprecated static int computeCrossingsFromCircle( int crossings, int cx, int cy, int radius, int x0, int y0, int x1, int y1) { return calculatesCrossingsCircleShadowSegment(crossings, cx, cy, radius, x0, y0, x1, y1); } /** * Calculates the number of times the line from (x0, y0) to (x1, y1) * crosses the ellipse (ex0, ey0) to (ex1, ey1) extending to the right. * * <p>When the line (x0;y0) to (x1;y1) is crossing one of the up or * bottom borders of the shadow of the circle, the crossings * count is increased or decreased, depending if the line is * going down or up, respectively. * In the following figure, the circle is represented. * The "shadow" is the projection of the circle on the right. * The red lines represent the up and bottom borders. * * <p><a href="doc-files/crossing_circle.png"><img alt="" src="doc-files/crossing_circle.png" width="300"></a> * * @param crossings is the initial value for the number of crossings. * @param cx is the center of the circle to extend. * @param cy is the center of the circle to extend. * @param radius is the radius of the circle to extend. * @param x0 is the first point of the line. * @param y0 is the first point of the line. * @param x1 is the second point of the line. * @param y1 is the secondpoint of the line. * @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}. */ @Pure static int calculatesCrossingsCircleShadowSegment( int crossings, int cx, int cy, int radius, int x0, int y0, int x1, int y1) { assert radius >= 0 : AssertMessages.positiveOrZeroParameter(3); int numCrosses = crossings; final int xmin = cx - Math.abs(radius); final int xmax = cx + Math.abs(radius); final int ymin = cy - Math.abs(radius); final int ymax = cy + Math.abs(radius); // The line is entirely on the top or on the bottom of the shadow if (y0 < ymin && y1 < ymin) { return numCrosses; } if (y0 > ymax && y1 > ymax) { return numCrosses; } // The line is entierly on the left of the shadow. if (x0 < xmin && x1 < xmin) { return numCrosses; } if (x0 > xmax && x1 > xmax) { // The line is entirely at the right of the center of the shadow. // We may use the standard "rectangle" crossing computation if (y0 < y1) { if (y0 < ymin) { ++numCrosses; } if (y1 > ymax) { ++numCrosses; } } else { if (y1 < ymin) { --numCrosses; } if (y0 > ymax) { --numCrosses; } } } else if (Circle2ai.intersectsCircleSegment( cx, cy, radius, x0, y0, x1, y1)) { return MathConstants.SHAPE_INTERSECTS; } else { numCrosses = calculatesCrossingsAndXPointShadowSegment(numCrosses, cx, ymin, x0, y0, x1, y1, true, false, null); numCrosses = calculatesCrossingsAndXPointShadowSegment(numCrosses, cx, ymax, x0, y0, x1, y1, false, true, null); } return numCrosses; } /** * Calculates the number of times the line from (x0, y0) to (x1, y1) * crosses the segment (sx0, sy0) to (sx1, sy1) extending to the right. * * <p>When the line (x0;y0) to (x1;y1) is crossing one of the up or * bottom borders of the shadow of the segment, the crossings * count is increased or decreased, depending if the line is * going down or up, respectively. * In the following figure, the segment is represented. * The "shadow" is the projection of the segment on the right. * The red lines represent the up and bottom borders. * * <p><a href="doc-files/crossing_segment.png"><img alt="" src="doc-files/crossing_segment.png" width="300"></a> * * @param crossings is the initial value for the number of crossings. * @param sx1 is the first point of the segment to extend. * @param sy1 is the first point of the segment to extend. * @param sx2 is the second point of the segment to extend. * @param sy2 is the second point of the segment to extend. * @param x0 is the first point of the line. * @param y0 is the first point of the line. * @param x1 is the second point of the line. * @param y1 is the secondpoint of the line. * @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}. * @deprecated since 13.0, see {@link #calculatesCrossingsSegmentShadowSegment(int, int, int, * int, int, int, int, int, int)} */ @Pure @Deprecated @SuppressWarnings("checkstyle:parameternumber") static int computeCrossingsFromSegment( int crossings, int sx1, int sy1, int sx2, int sy2, int x0, int y0, int x1, int y1) { return calculatesCrossingsSegmentShadowSegment(crossings, sx1, sy1, sx2, sy2, x0, y0, x1, y1); } /** * Calculates the number of times the line from (x0, y0) to (x1, y1) * crosses the segment (sx0, sy0) to (sx1, sy1) extending to the right. * * <p>When the line (x0;y0) to (x1;y1) is crossing one of the up or * bottom borders of the shadow of the segment, the crossings * count is increased or decreased, depending if the line is * going down or up, respectively. * In the following figure, the segment is represented. * The "shadow" is the projection of the segment on the right. * The red lines represent the up and bottom borders. * * <p><a href="doc-files/crossing_segment.png"><img alt="" src="doc-files/crossing_segment.png" width="300"></a> * * @param crossings is the initial value for the number of crossings. * @param sx1 is the first point of the segment to extend. * @param sy1 is the first point of the segment to extend. * @param sx2 is the second point of the segment to extend. * @param sy2 is the second point of the segment to extend. * @param x0 is the first point of the line. * @param y0 is the first point of the line. * @param x1 is the second point of the line. * @param y1 is the secondpoint of the line. * @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}. */ @Pure @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:cyclomaticcomplexity", "checkstyle:npathcomplexity"}) static int calculatesCrossingsSegmentShadowSegment( int crossings, int sx1, int sy1, int sx2, int sy2, int x0, int y0, int x1, int y1) { /* CAUTION: * -------- * In the comments of this function, it is assumed that y0<=y1, * to simplify the explanations. * The source code is supporting y0<=y1 and y0>y1. */ int numCrosses = crossings; final int xmin = Math.min(sx1, sx2); final int xmax = Math.max(sx1, sx2); final int ymin = Math.min(sy1, sy2); final int ymax = Math.max(sy1, sy2); // The line is entirely below or up to the shadow of the segment if (y0 < ymin && y1 < ymin) { return numCrosses; } if (y0 > ymax && y1 > ymax) { return numCrosses; } // The line is entirely at te left of the segment if (x0 < xmin && x1 < xmin) { return numCrosses; } if (x0 > xmax && x1 > xmax) { // The line is entirely at the right of the shadow if (y0 < y1) { if (y0 < ymin) { ++numCrosses; } if (y1 > ymax) { ++numCrosses; } } else { if (y1 < ymin) { --numCrosses; } if (y0 > ymax) { --numCrosses; } } } else if (intersectsSegmentSegment(x0, y0, x1, y1, sx1, sy1, sx2, sy2, true, true, null)) { return MathConstants.SHAPE_INTERSECTS; } else { // The line is intersectly partly the bounding rectangle of the segment. // We must determine on which side of the segment the points of the line are. // If side1 is positive, the first point of the line is on the side of the shadow, relatively to the segment // If it is negative, the first point is on the opposite side of the shadow, relatively to the segment. // If it is nul, the point is on line colinear to the segment. // Same for side2 and the second point of the line. final int side1; final int side2; final boolean firstIsTop = sy1 <= sy2; if (firstIsTop) { side1 = findsSideLinePoint(sx1, sy1, sx2, sy2, x0, y0); side2 = findsSideLinePoint(sx1, sy1, sx2, sy2, x1, y1); } else { side1 = findsSideLinePoint(sx2, sy2, sx1, sy1, x0, y0); side2 = findsSideLinePoint(sx2, sy2, sx1, sy1, x1, y1); } if (side1 >= 0 || side2 >= 0) { // At least one point is on the side of the shadow. // Now we compute the intersection with the up and bottom borders. // Intersection is obtained by computed the crossing value from // the two points of the segment. final int n1 = calculatesCrossingsAndXPointShadowSegment(0, sx1, sy1, x0, y0, x1, y1, firstIsTop, !firstIsTop, null); final int n2 = calculatesCrossingsAndXPointShadowSegment(0, sx2, sy2, x0, y0, x1, y1, !firstIsTop, firstIsTop, null); // The total crossing value must be updated with the border's crossing values. numCrosses += n1 + n2; } } return numCrosses; } /** * Accumulate the number of times the line crosses the shadow * extending to the right of the rectangle. * * <p>When the line (x0;y0) to (x1;y1) is intersecting the rectangle, * the value {@link MathConstants#SHAPE_INTERSECTS} is returned. * When the line (x0;y0) to (x1;y1) is crossing one of the up or * bottom borders of the shadow of the rectangle, the crossings * count is increased or decreased, depending if the line is * going down or up, respectively. * In the following figure, the rectangle is represented. * The "shadow" is the projection of the rectangle on the right. * The red lines represent the up and bottom borders. * * <p><a href="doc-files/crossing_rect.png"><img alt="" src="doc-files/crossing_rect.png" width="300"></a> * * @param crossings is the initial value for the number of crossings. * @param rxmin is the first corner of the rectangle. * @param rymin is the first corner of the rectangle. * @param rxmax is the second corner of the rectangle. * @param rymax is the second corner of the rectangle. * @param x0 is the first point of the line. * @param y0 is the first point of the line. * @param x1 is the second point of the line. * @param y1 is the secondpoint of the line. * @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}. * @deprecated since 13.0, see {@link #calculatesCrossingsRectangleShadowSegment(int, int, int, * int, int, int, int, int, int)} */ @Deprecated @Pure @SuppressWarnings("checkstyle:parameternumber") static int computeCrossingsFromRect( int crossings, int rxmin, int rymin, int rxmax, int rymax, int x0, int y0, int x1, int y1) { return calculatesCrossingsRectangleShadowSegment(crossings, rxmin, rymin, rxmax, rymax, x0, y0, x1, y1); } /** * Accumulate the number of times the line crosses the shadow * extending to the right of the rectangle. * * <p>When the line (x0;y0) to (x1;y1) is intersecting the rectangle, * the value {@link MathConstants#SHAPE_INTERSECTS} is returned. * When the line (x0;y0) to (x1;y1) is crossing one of the up or * bottom borders of the shadow of the rectangle, the crossings * count is increased or decreased, depending if the line is * going down or up, respectively. * In the following figure, the rectangle is represented. * The "shadow" is the projection of the rectangle on the right. * The red lines represent the up and bottom borders. * * <p><a href="doc-files/crossing_rect.png"><img alt="" src="doc-files/crossing_rect.png" width="300"></a> * * @param crossings is the initial value for the number of crossings. * @param rxmin is the first corner of the rectangle. * @param rymin is the first corner of the rectangle. * @param rxmax is the second corner of the rectangle. * @param rymax is the second corner of the rectangle. * @param x0 is the first point of the line. * @param y0 is the first point of the line. * @param x1 is the second point of the line. * @param y1 is the secondpoint of the line. * @return the crossing, or {@link MathConstants#SHAPE_INTERSECTS}. */ @Pure @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:cyclomaticcomplexity", "checkstyle:npathcomplexity", "checkstyle:returncount", "checkstyle:booleanexpressioncomplexity", "checkstyle:nestedifdepth"}) static int calculatesCrossingsRectangleShadowSegment( int crossings, int rxmin, int rymin, int rxmax, int rymax, int x0, int y0, int x1, int y1) { int numCrosses = crossings; // The line is horizontal, only SHAPE_INTERSECT may be replies if (y0 == y1) { if (y0 >= rymin && y0 <= rymax && (x0 >= rxmin || x1 >= rxmin) && (x0 <= rxmax || x1 <= rxmax)) { return MathConstants.SHAPE_INTERSECTS; } return crossings; } // The line is entirely at the top or at the bottom of the rectangle if (y0 > rymax && y1 > rymax) { return numCrosses; } if (y0 < rymin && y1 < rymin) { return numCrosses; } // The line is entirely on the left of the rectangle if (x0 < rxmin && x1 < rxmin) { return numCrosses; } if (x0 > rxmax && x1 > rxmax) { // Line is entirely to the right of the rect // and the vertical ranges of the two overlap by a non-empty amount // Thus, this line segment is partially in the "right-shadow" // Path may have done a complete crossing // Or path may have entered or exited the right-shadow if (y0 < y1) { // y-increasing line segment... // We know that y0 < rymax and y1 > rymin if (y0 < rymin) { ++numCrosses; } if (y1 > rymax) { ++numCrosses; } } else if (y1 < y0) { // y-decreasing line segment... // We know that y1 < rymax and y0 > rymin if (y1 < rymin) { --numCrosses; } if (y0 > rymax) { --numCrosses; } } } else { // Remaining case: // Both x and y ranges overlap by a non-empty amount // First do trivial INTERSECTS rejection of the cases // where one of the endpoints is inside the rectangle. if ((x0 >= rxmin && x0 <= rxmax && y0 >= rymin && y0 <= rymax) || (x1 >= rxmin && x1 <= rxmax && y1 >= rymin && y1 <= rymax)) { return MathConstants.SHAPE_INTERSECTS; } // Otherwise calculate the y intercepts and see where // they fall with respect to the rectangle final BresenhamLineIterator<InnerComputationPoint2ai, InnerComputationVector2ai> iterator; final int ymaxline; if (y0 <= y1) { iterator = new BresenhamLineIterator<>( InnerComputationGeomFactory.SINGLETON, x0, y0, x1, y1); ymaxline = y1; } else { iterator = new BresenhamLineIterator<>( InnerComputationGeomFactory.SINGLETON, x1, y1, x0, y0); ymaxline = y0; } final InnerComputationPoint2ai p = new InnerComputationPoint2ai(); Integer xintercept1 = null; Integer xintercept2 = null; boolean cont = true; while (iterator.hasNext() && cont) { iterator.next(p); if (p.iy() == rymin && (xintercept1 == null || xintercept1.intValue() > p.ix())) { xintercept1 = Integer.valueOf(p.ix()); } if (p.iy() == rymax && (xintercept2 == null || xintercept2.intValue() > p.ix())) { xintercept2 = Integer.valueOf(p.ix()); } cont = p.iy() <= ymaxline; } if (xintercept1 != null && xintercept2 != null) { if (xintercept1.intValue() < rxmin && xintercept2.intValue() < rxmin) { // the intersection points are entirely on the left } else if (xintercept1.intValue() > rxmax && xintercept2.intValue() > rxmax) { // the intersection points are entirely on the right if (y0 < y1) { // y-increasing line segment... // We know that y0 < rymax and y1 > rymin if (y0 <= rymin) { ++numCrosses; } if (y1 >= rymax) { ++numCrosses; } } else if (y1 < y0) { // y-decreasing line segment... // We know that y1 < rymax and y0 > rymin if (y1 <= rymin) { --numCrosses; } if (y0 >= rymax) { --numCrosses; } } } else { return MathConstants.SHAPE_INTERSECTS; } } else if (xintercept1 != null) { // Only the top line of the rectangle is intersecting the segment if (xintercept1.intValue() < rxmin) { // the intersection point is at entirely on the left } else if (xintercept1.intValue() > rxmax) { if (y0 < y1) { // y-increasing line segment... // We know that y0 < rymax and y1 > rymin if (y0 <= rymin) { ++numCrosses; } } else if (y1 < y0 && y1 <= rymin) { // y-decreasing line segment... // We know that y1 < rymax and y0 > rymin --numCrosses; } } else { return MathConstants.SHAPE_INTERSECTS; } } else if (xintercept2 != null) { // Only the bottom line of the rectangle is intersecting the segment if (xintercept2.intValue() < rxmin) { // the intersection point is at entirely on the left } else if (xintercept2.intValue() > rxmax) { if (y0 < y1) { // y-increasing line segment... // We know that y0 < rymax and y1 > rymin if (y0 <= rymax) { ++numCrosses; } } else if (y1 < y0 && y1 <= rymax) { // y-decreasing line segment... // We know that y1 < rymax and y0 > rymin --numCrosses; } } else { return MathConstants.SHAPE_INTERSECTS; } } } return numCrosses; } /** * Calculates the number of times the line from (x0, y0) to (x1, y1) * crosses the up/bottom borders of the ray extending to the right from (px, py). * +x is returned for a crossing where the Y coordinate is increasing. * -x is returned for a crossing where the Y coordinate is decreasing. * x is the number of border crossed by the lines. * * <p>The borders of the segment are the two side limits between the cells covered by the segment * and the adjacents cells (not covered by the segment). * In the following figure, the point (px;py) is represented. * The "shadow line" is the projection of (px;py) on the right. * The red lines represent the up and bottom borders. * * <p><a href="doc-files/crossing_point.png"><img alt="" src="doc-files/crossing_point.png" width="300"></a> * * @param crossing is the initial value of the crossing. * @param px is the reference point to test. * @param py is the reference point to test. * @param x0 is the first point of the line. * @param y0 is the first point of the line. * @param x1 is the second point of the line. * @param y1 is the secondpoint of the line. * @return the crossing, {@link MathConstants#SHAPE_INTERSECTS} * @deprecated since 13.0, see {@link #calculatesCrossingsPointShadowSegment(int, int, int, int, int, int, int)} */ @Pure @Deprecated static int computeCrossingsFromPoint( int crossing, int px, int py, int x0, int y0, int x1, int y1) { return calculatesCrossingsPointShadowSegment(crossing, px, py, x0, y0, x1, y1); } /** * Calculates the number of times the line from (x0, y0) to (x1, y1) * crosses the up/bottom borders of the ray extending to the right from (px, py). * +x is returned for a crossing where the Y coordinate is increasing. * -x is returned for a crossing where the Y coordinate is decreasing. * x is the number of border crossed by the lines. * * <p>The borders of the segment are the two side limits between the cells covered by the segment * and the adjacents cells (not covered by the segment). * In the following figure, the point (px;py) is represented. * The "shadow line" is the projection of (px;py) on the right. * The red lines represent the up and bottom borders. * * <p><a href="doc-files/crossing_point.png"><img alt="" src="doc-files/crossing_point.png" width="300"></a> * * @param crossing is the initial value of the crossing. * @param px is the reference point to test. * @param py is the reference point to test. * @param x0 is the first point of the line. * @param y0 is the first point of the line. * @param x1 is the second point of the line. * @param y1 is the secondpoint of the line. * @param enableTopBorder indicates if the top border must be enabled in the crossing computation. * @param enableBottomBorder indicates if the bottom border must be enabled in the crossing computation. * @return the crossing; or {@link MathConstants#SHAPE_INTERSECTS} if the segment is on the point. * @deprecated see {@link #calculatesCrossingsAndXPointShadowSegment(int, int, int, int, int, int, int, boolean, * boolean, OutputParameter)} */ @Pure @Inline("Segment2ai.computeCrossingsAndXFromPoint($1,$2,$3,$4,$5,$6,$7,$8,$9,null)") @Deprecated @SuppressWarnings("checkstyle:parameternumber") static int computeCrossingsFromPoint( int crossing, int px, int py, int x0, int y0, int x1, int y1, boolean enableTopBorder, boolean enableBottomBorder) { return calculatesCrossingsAndXPointShadowSegment(crossing, px, py, x0, y0, x1, y1, enableTopBorder, enableBottomBorder, null); } /** * Calculates the number of times the line from (x0, y0) to (x1, y1) * crosses the up/bottom borders of the ray extending to the right from (px, py). * +x is returned for a crossing where the Y coordinate is increasing. * -x is returned for a crossing where the Y coordinate is decreasing. * x is the number of border crossed by the lines. * * <p>The borders of the segment are the two side limits between the cells covered by the segment * and the adjacents cells (not covered by the segment). * In the following figure, the point (px;py) is represented. * The "shadow line" is the projection of (px;py) on the right. * The red lines represent the up and bottom borders. * * <p><a href="doc-files/crossing_point.png"><img alt="" src="doc-files/crossing_point.png" width="300"></a> * * @param crossing is the initial value of the crossing. * @param px is the reference point to test. * @param py is the reference point to test. * @param x0 is the first point of the line. * @param y0 is the first point of the line. * @param x1 is the second point of the line. * @param y1 is the secondpoint of the line. * @return the crossing, {@link MathConstants#SHAPE_INTERSECTS} */ @Pure static int calculatesCrossingsPointShadowSegment( int crossing, int px, int py, int x0, int y0, int x1, int y1) { return calculatesCrossingsAndXPointShadowSegment(crossing, px, py, x0, y0, x1, y1, true, true, null); } /** * Calculates the number of times the line from (x0, y0) to (x1, y1) * crosses the up/bottom borders of the ray extending to the right from (px, py). * +x is returned for a crossing where the Y coordinate is increasing. * -x is returned for a crossing where the Y coordinate is decreasing. * x is the number of border crossed by the lines. * * <p>The borders of the segment are the two side limits between the cells covered by the segment * and the adjacents cells (not covered by the segment). * In the following figure, the point (px;py) is represented. * The "shadow line" is the projection of (px;py) on the right. * The red lines represent the up and bottom borders. * * <p><a href="doc-files/crossing_point.png"><img alt="" src="doc-files/crossing_point.png" width="300"></a> * * @param crossing is the initial value of the crossing. * @param px is the reference point to test. * @param py is the reference point to test. * @param x0 is the first point of the line. * @param y0 is the first point of the line. * @param x1 is the second point of the line. * @param y1 is the secondpoint of the line. * @param enableTopBorder indicates if the top border must be enabled in the crossing computation. * @param enableBottomBorder indicates if the bottom border must be enabled in the crossing computation. * @param xCoordinate output parameter for the x coordinate that is intersecting. * @return the crossing; or {@link MathConstants#SHAPE_INTERSECTS} if the segment is on the point. * @deprecated since 13.0, see {@link #calculatesCrossingsAndXPointShadowSegment(int, int, int, * int, int, int, int, boolean, boolean, OutputParameter)} */ @Deprecated @SuppressWarnings("checkstyle:parameternumber") static int computeCrossingsAndXFromPoint( int crossing, int px, int py, int x0, int y0, int x1, int y1, boolean enableTopBorder, boolean enableBottomBorder, OutputParameter<Integer> xCoordinate) { return calculatesCrossingsAndXPointShadowSegment(crossing, px, py, x0, y0, x1, y1, enableTopBorder, enableBottomBorder, xCoordinate); } /** * Calculates the number of times the line from (x0, y0) to (x1, y1) * crosses the up/bottom borders of the ray extending to the right from (px, py). * +x is returned for a crossing where the Y coordinate is increasing. * -x is returned for a crossing where the Y coordinate is decreasing. * x is the number of border crossed by the lines. * * <p>The borders of the segment are the two side limits between the cells covered by the segment * and the adjacents cells (not covered by the segment). * In the following figure, the point (px;py) is represented. * The "shadow line" is the projection of (px;py) on the right. * The red lines represent the up and bottom borders. * * <p><a href="doc-files/crossing_point.png"><img alt="" src="doc-files/crossing_point.png" width="300"></a> * * @param crossing is the initial value of the crossing. * @param px is the reference point to test. * @param py is the reference point to test. * @param x0 is the first point of the line. * @param y0 is the first point of the line. * @param x1 is the second point of the line. * @param y1 is the secondpoint of the line. * @param enableTopBorder indicates if the top border must be enabled in the crossing computation. * @param enableBottomBorder indicates if the bottom border must be enabled in the crossing computation. * @param xCoordinate output parameter for the x coordinate that is intersecting. * @return the crossing; or {@link MathConstants#SHAPE_INTERSECTS} if the segment is on the point. */ @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:cyclomaticcomplexity", "checkstyle:npathcomplexity"}) static int calculatesCrossingsAndXPointShadowSegment( int crossing, int px, int py, int x0, int y0, int x1, int y1, boolean enableTopBorder, boolean enableBottomBorder, OutputParameter<Integer> xCoordinate) { // The line is horizontal, impossible to intersect the borders. if (y0 == y1) { return crossing; } // The line does cross the shadow line if (py < y0 && py < y1) { return crossing; } if (py > y0 && py > y1) { return crossing; } // The line is entirely on the left of the point if (px > x0 && px > x1) { return crossing; } // General case: try to detect crossing final BresenhamLineIterator<InnerComputationPoint2ai, InnerComputationVector2ai> iterator = new BresenhamLineIterator<>( InnerComputationGeomFactory.SINGLETON, x0, y0, x1, y1); // Only for internal use. final Point2D<?, ?> p = new InnerComputationPoint2ai(); while (iterator.hasNext()) { iterator.next(p); if (p.iy() == py) { if (p.ix() == px) { return MathConstants.SHAPE_INTERSECTS; } if (p.ix() > px) { // Found an intersection int numCrosses = crossing; if (y0 <= y1) { if (y0 < py && enableTopBorder) { ++numCrosses; } if (y1 > py && enableBottomBorder) { ++numCrosses; } } else { if (y0 > py && enableBottomBorder) { --numCrosses; } if (y1 < py && enableTopBorder) { --numCrosses; } } if (xCoordinate != null) { xCoordinate.set(Integer.valueOf(p.ix())); } return numCrosses; } } } return crossing; } /** Replies if two segments are intersecting. * This function determines if the segments' lines * are intersecting because using the pixel-based test. * This function uses the pixels of the segments that are * computed according to a Bresenham line algorithm. * * @param x1 is the first point of the first segment. * @param y1 is the first point of the first segment. * @param x2 is the second point of the first segment. * @param y2 is the second point of the first segment. * @param x3 is the first point of the second segment. * @param y3 is the first point of the second segment. * @param x4 is the second point of the second segment. * @param y4 is the second point of the second segment. * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ @Pure static boolean intersectsSegmentSegment(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { final int side1 = findsSideLinePoint(x1, y1, x2, y2, x3, y3); final int side2 = findsSideLinePoint(x1, y1, x2, y2, x4, y4); if ((side1 * side2) <= 0) { return findsIntersectionTypeSegmentSegment(x1, y1, x2, y2, x3, y3, x4, y4, true, true, null) != 0; } return false; } /** Replies if two segments are intersecting pixel per pixel. * This function does not determine if the segments' lines * are intersecting because using the pixel-based test. * This function uses the pixels of the segments that are * computed according to a Bresenham line algorithm. * * @param x1 is the first point of the first segment. * @param y1 is the first point of the first segment. * @param x2 is the second point of the first segment. * @param y2 is the second point of the first segment. * @param x3 is the first point of the second segment. * @param y3 is the first point of the second segment. * @param x4 is the second point of the second segment. * @param y4 is the second point of the second segment. * @param enableThirdPoint indicates if the intersection on the third point is computed. * @param enableFourthPoint indicates if the intersection on the fourth point is computed. * @param intersectionPoint are the coordinates of the intersection, if exist. * @return <code>true</code> if the two segments are intersecting; otherwise * <code>false</code> */ @SuppressWarnings("checkstyle:parameternumber") static boolean intersectsSegmentSegment(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, boolean enableThirdPoint, boolean enableFourthPoint, Point2D<?, ?> intersectionPoint) { return findsIntersectionTypeSegmentSegment(x1, y1, x2, y2, x3, y3, x4, y4, enableThirdPoint, enableFourthPoint, intersectionPoint) != 0; } /** Replies if two segments are intersecting pixel per pixel. * This function does not determine if the segments' lines * are intersecting because using the pixel-based test. * This function uses the pixels of the segments that are * computed according to a Bresenham line algorithm. * * @param x1 is the first point of the first segment. * @param y1 is the first point of the first segment. * @param x2 is the second point of the first segment. * @param y2 is the second point of the first segment. * @param x3 is the first point of the second segment. * @param y3 is the first point of the second segment. * @param x4 is the second point of the second segment. * @param y4 is the second point of the second segment. * @param enableThirdPoint indicates if the intersection on the third point is computed. * @param enableFourthPoint indicates if the intersection on the fourth point is computed. * @param intersectionPoint are the coordinates of the intersection, if exist. * @return an integer value; if <code>0</code> the two segments are not intersecting; * <code>1</code> if the two segments are intersecting and the segment 2 has pixels on both * sides of the segment 1; <code>2</code> if the segments are intersecting and the segment 2 * is only in one side of the segment 1. * @deprecated since 13.0, see {@link #findsIntersectionTypeSegmentSegment(int, int, int, int, * int, int, int, int, boolean, boolean, Point2D)} */ @Deprecated @SuppressWarnings("checkstyle:parameternumber") static int computeIntersectionTypeSegmentSegment(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, boolean enableThirdPoint, boolean enableFourthPoint, Point2D<?, ?> intersectionPoint) { return findsIntersectionTypeSegmentSegment(x1, y1, x2, y2, x3, y3, x4, y4, enableThirdPoint, enableFourthPoint, intersectionPoint); } /** Replies if two segments are intersecting pixel per pixel. * This function does not determine if the segments' lines * are intersecting because using the pixel-based test. * This function uses the pixels of the segments that are * computed according to a Bresenham line algorithm. * * @param x1 is the first point of the first segment. * @param y1 is the first point of the first segment. * @param x2 is the second point of the first segment. * @param y2 is the second point of the first segment. * @param x3 is the first point of the second segment. * @param y3 is the first point of the second segment. * @param x4 is the second point of the second segment. * @param y4 is the second point of the second segment. * @param enableThirdPoint indicates if the intersection on the third point is computed. * @param enableFourthPoint indicates if the intersection on the fourth point is computed. * @param intersectionPoint are the coordinates of the intersection, if exist. * @return an integer value; if <code>0</code> the two segments are not intersecting; * <code>1</code> if the two segments are intersecting and the segment 2 has pixels on both * sides of the segment 1; <code>2</code> if the segments are intersecting and the segment 2 * is only in one side of the segment 1. */ @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:cyclomaticcomplexity", "checkstyle:npathcomplexity"}) static int findsIntersectionTypeSegmentSegment(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, boolean enableThirdPoint, boolean enableFourthPoint, Point2D<?, ?> intersectionPoint) { final BresenhamLineIterator<InnerComputationPoint2ai, InnerComputationVector2ai> it1; if (x1 < x2) { it1 = new BresenhamLineIterator<>( InnerComputationGeomFactory.SINGLETON, x1, y1, x2, y2); } else { it1 = new BresenhamLineIterator<>( InnerComputationGeomFactory.SINGLETON, x2, y2, x1, y1); } final BresenhamLineIterator<InnerComputationPoint2ai, InnerComputationVector2ai> it2; if (x3 < x4) { it2 = new BresenhamLineIterator<>( InnerComputationGeomFactory.SINGLETON, x3, y3, x4, y4); } else { it2 = new BresenhamLineIterator<>( InnerComputationGeomFactory.SINGLETON, x4, y4, x3, y3); } if (it1.hasNext() && it2.hasNext()) { // Only for internal use final Point2D<?, ?> p1 = new InnerComputationPoint2ai(); // Only for internal use final Point2D<?, ?> p2 = new InnerComputationPoint2ai(); boolean isFirstPointOfSecondSegment = true; it1.next(p1); it2.next(p2); do { if (p1.ix() < p2.ix()) { while (it1.hasNext() && p1.ix() < p2.ix()) { it1.next(p1); } } else if (p2.ix() < p1.ix()) { while (it2.hasNext() && p2.ix() < p1.ix()) { it2.next(p2); isFirstPointOfSecondSegment = false; } } final int x = p1.ix(); int min1 = p1.iy(); int max1 = p1.iy(); int min2 = isFirstPointOfSecondSegment && !enableThirdPoint ? Integer.MAX_VALUE : p2.iy(); int max2 = isFirstPointOfSecondSegment && !enableThirdPoint ? Integer.MIN_VALUE : p2.iy(); while (it1.hasNext()) { it1.next(p1); if (p1.ix() == x) { if (p1.iy() < min1) { min1 = p1.iy(); } if (p1.iy() > max1) { max1 = p1.iy(); } } else { break; } } while (it2.hasNext()) { it2.next(p2); isFirstPointOfSecondSegment = false; if (p2.ix() == x) { if (p2.iy() < min2) { min2 = p2.iy(); } if (p2.iy() > max2) { max2 = p2.iy(); } } else { break; } } if (max2 >= min1 && max1 >= min2) { if (intersectionPoint != null) { intersectionPoint.set(x, Math.max(min1, min2)); } return !isFirstPointOfSecondSegment && (it2.hasNext()) ? 1 : 2; } } while (it1.hasNext() && it2.hasNext()); if (enableFourthPoint && p1.equals(p2)) { if (intersectionPoint != null) { intersectionPoint.set(p1); } return !isFirstPointOfSecondSegment && (it2.hasNext()) ? 1 : 2; } } return 0; } @Pure @Override default boolean equalsToShape(IT shape) { if (shape == null) { return false; } if (shape == this) { return true; } return getX1() == shape.getX1() && getY1() == shape.getY1() && getX2() == shape.getX2() && getY2() == shape.getY2(); } @Override default void clear() { set(0, 0, 0, 0); } @Pure @Override default boolean isEmpty() { return getX1() == getX2() && getY1() == getY2(); } /** Change the line. * * @param x1 x coordinate of the first point. * @param y1 y coordinate of the first point. * @param x2 x coordinate of the second point. * @param y2 y coordinate of the second point. */ void set(int x1, int y1, int x2, int y2); /** Change the line. * * @param firstPoint the first point. * @param secondPoint the second point. */ default void set(Point2D<?, ?> firstPoint, Point2D<?, ?> secondPoint) { assert firstPoint != null : AssertMessages.notNullParameter(0); assert secondPoint != null : AssertMessages.notNullParameter(1); set(firstPoint.ix(), firstPoint.iy(), secondPoint.ix(), secondPoint.iy()); } @Override default void set(IT shape) { assert shape != null : AssertMessages.notNullParameter(); set(shape.getX1(), shape.getY1(), shape.getX2(), shape.getY2()); } /** Replies the X of the first point. * * @return the x of the first point. */ @Pure int getX1(); /** Replies the Y of the first point. * * @return the y of the first point. */ @Pure int getY1(); /** Replies the X of the second point. * * @return the x of the second point. */ @Pure int getX2(); /** Replies the Y of the second point. * * @return the y of the second point. */ @Pure int getY2(); /** Change the X of the first point. * * @param x the x of the first point. */ @Pure void setX1(int x); /** Change the Y of the first point. * * @param y the y of the first point. */ @Pure void setY1(int y); /** Change the X of the second point. * * @param x the x of the second point. */ @Pure void setX2(int x); /** Change the Y of the second point. * * @param y the y of the second point. */ @Pure void setY2(int y); /** Replies the first point. * * @return the first point. */ @Pure default P getP1() { return getGeomFactory().newPoint(getX1(), getY1()); } /** Replies the second point. * * @return the second point. */ @Pure default P getP2() { return getGeomFactory().newPoint(getX2(), getY2()); } /** Change the first point. * * @param point the first point. */ @Pure default void setP1(Point2D<?, ?> point) { assert point != null : AssertMessages.notNullParameter(); set(point.ix(), point.iy(), getX2(), getY2()); } /** Change the first point. * * @param x x coordinate of the first point. * @param y y coordinate of the first point. */ @Pure default void setP1(int x, int y) { set(x, y, getX2(), getY2()); } /** Change the second point. * * @param point the second point. */ @Pure default void setP2(Point2D<?, ?> point) { assert point != null : AssertMessages.notNullParameter(); set(getX1(), getY1(), point.ix(), point.iy()); } /** Change the second point. * * @param x x coordinate the second point. * @param y y coordinate the second point. */ @Pure default void setP2(int x, int y) { set(getX1(), getY1(), x, y); } @Override @Pure default void toBoundingBox(B box) { assert box != null : AssertMessages.notNullParameter(); box.setFromCorners(getX1(), getY1(), getX2(), getY2()); } @Pure @Override default double getDistanceSquared(Segment2ai<?, ?, ?, ?, ?, ?> segment) { assert segment != null : AssertMessages.notNullParameter(); return findsClosestPointsSegmentSegment(getX1(), getY1(), getX2(), getY2(), segment.getX1(), segment.getY1(), segment.getX2(), segment.getY2(), null, null); } @Pure @Override default double getDistanceSquared(Rectangle2ai<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); return findsClosestPointSegmentRectangle(getX1(), getY1(), getX2(), getY2(), rectangle.getMinX(), rectangle.getMinY(), rectangle.getWidth(), rectangle.getHeight(), null); } @Pure @Override default double getDistanceSquared(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final P closestPoint = getClosestPointTo(pt); return closestPoint.getDistanceSquared(pt); } @Pure @Override default double getDistanceL1(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final P closestPoint = getClosestPointTo(pt); return closestPoint.getDistanceL1(pt); } @Pure @Override default double getDistanceLinf(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final P closestPoint = getClosestPointTo(pt); return closestPoint.getDistanceLinf(pt); } @Pure @Override default boolean contains(int x, int y) { final int ax = getX1(); final int ay = getY1(); final int bx = getX2(); final int by = getY2(); if (x >= ax && x <= bx && y >= ay && y <= by) { if (ax == bx || ay == by) { return true; } int minDist = Integer.MAX_VALUE; final Point2D<?, ?> p = new InnerComputationPoint2ai(); final BresenhamLineIterator<InnerComputationPoint2ai, InnerComputationVector2ai> iterator = new BresenhamLineIterator<>(InnerComputationGeomFactory.SINGLETON, ax, ay, bx, by); while (iterator.hasNext()) { iterator.next(p); final int a = Math.abs(x - p.ix()); final int b = Math.abs(y - p.iy()); final int d = a * a + b * b; if (d == 0) { return true; } if (d > minDist) { return false; } minDist = d; } } return false; } @Pure @Override default boolean contains(Rectangle2ai<?, ?, ?, ?, ?, ?> rectangle) { return false; } @Pure @Override default boolean contains(Shape2D<?, ?, ?, ?, ?, ?> shape) { return false; } @Pure @Override default P getClosestPointTo(Point2D<?, ?> pt) { assert pt != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); findsClosestPointSegmentPoint(getX1(), getY1(), getX2(), getY2(), pt.ix(), pt.iy(), 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(); findsClosestPointsSegmentSegment(getX1(), getY1(), getX2(), getY2(), segment.getX1(), segment.getY1(), segment.getX2(), segment.getY2(), point, null); return point; } @Override default P getClosestPointTo(Rectangle2ai<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); final P point = getGeomFactory().newPoint(); findsClosestPointSegmentRectangle(getX1(), getY1(), getX2(), getY2(), rectangle.getMinX(), rectangle.getMinY(), rectangle.getWidth(), rectangle.getHeight(), point); return point; } @Override 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(); findsFarthestPointSegmentPoint(getX1(), getY1(), getX2(), getY2(), pt.ix(), pt.iy(), point); return point; } @Override default void translate(int dx, int dy) { set(getX1() + dx, getY1() + dy, getX2() + dx, getY2() + dy); } @Pure @Override default PathIterator2ai<IE> getPathIterator(Transform2D transform) { if (transform == null || transform.isIdentity()) { return new SegmentPathIterator<>(this); } return new TransformedSegmentPathIterator<>(this, transform); } /** Replies an iterator on the points of the segment. * * <p>The Bresenham line algorithm is an algorithm which determines which points in * an n-dimensional raster should be plotted in order to form a close * approximation to a straight line between two given points. It is * commonly used to draw lines on a computer screen, as it uses only * integer addition, subtraction and bit shifting, all of which are * very cheap operations in standard computer architectures. It is one of the * earliest algorithms developed in the field of computer graphics. A minor extension * to the original algorithm also deals with drawing circles. * * <p>While algorithms such as Wu's algorithm are also frequently used in modern * computer graphics because they can support antialiasing, the speed and * simplicity of Bresenham's line algorithm mean that it is still important. * The algorithm is used in hardware such as plotters and in the graphics * chips of modern graphics cards. It can also be found in many software * graphics libraries. Because the algorithm is very simple, it is often * implemented in either the firmware or the hardware of modern graphics cards. * * @return an iterator on the points along the Bresenham line. */ @Pure @Override default Iterator<P> getPointIterator() { return new BresenhamLineIterator<>(getGeomFactory(), getX1(), getY1(), getX2(), getY2()); } /** Transform the current segment. * This function changes the current segment. * * @param transform is the affine transformation to apply. * @see #createTransformedShape(Transform2D) */ default void transform(Transform2D transform) { assert transform != null : AssertMessages.notNullParameter(); final P p = getGeomFactory().newPoint(getX1(), getY1()); transform.transform(p); setP1(p); p.set(getX2(), getY2()); transform.transform(p); setP2(p); } /** Clip the segment against the clipping rectangle * according to the <a href="http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm">Cohen-Sutherland algorithm</a>. * * @param rxmin is the min of the coordinates of the rectangle. * @param rymin is the min of the coordinates of the rectangle. * @param rxmax is the max of the coordinates of the rectangle. * @param rymax is the max of the coordinates of the rectangle. * @return <code>true</code> if the segment has an intersection with the * rectangle and the segment was clipped; <code>false</code> if the segment * does not intersect the rectangle. */ default boolean clipToRectangle(int rxmin, int rymin, int rxmax, int rymax) { assert rxmin <= rxmax : AssertMessages.lowerEqualParameters(0, rxmin, 2, rxmax); assert rymin <= rymax : AssertMessages.lowerEqualParameters(1, rymin, 3, rymax); int x0 = getX1(); int y0 = getY1(); int x1 = getX2(); int y1 = getY2(); int code1 = MathUtil.getCohenSutherlandCode(x0, y0, rxmin, rymin, rxmax, rymax); int code2 = MathUtil.getCohenSutherlandCode(x1, y1, rxmin, rymin, rxmax, rymax); boolean accept = false; boolean cont = true; int x = 0; int y = 0; while (cont) { if ((code1 | code2) == 0) { // Bitwise OR is 0. Trivially accept and get out of loop accept = true; cont = false; } else if ((code1 & code2) != 0) { // Bitwise AND is not 0. Trivially reject and get out of loop cont = false; } else { // failed both tests, so calculate the line segment to clip // from an outside point to an intersection with clip edge // At least one endpoint is outside the clip rectangle; pick it. int code3 = code1 != 0 ? code1 : code2; // Now find the intersection point; // use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0) if ((code3 & MathConstants.COHEN_SUTHERLAND_TOP) != 0) { // point is above the clip rectangle x = x0 + (x1 - x0) * (rymax - y0) / (y1 - y0); y = rymax; } else if ((code3 & MathConstants.COHEN_SUTHERLAND_BOTTOM) != 0) { // point is below the clip rectangle x = x0 + (x1 - x0) * (rymin - y0) / (y1 - y0); y = rymin; } else if ((code3 & MathConstants.COHEN_SUTHERLAND_RIGHT) != 0) { // point is to the right of clip rectangle y = y0 + (y1 - y0) * (rxmax - x0) / (x1 - x0); x = rxmax; } else if ((code3 & MathConstants.COHEN_SUTHERLAND_LEFT) != 0) { // point is to the left of clip rectangle y = y0 + (y1 - y0) * (rxmin - x0) / (x1 - x0); x = rxmin; } else { code3 = 0; } if (code3 != 0) { // Now we move outside point to intersection point to clip // and get ready for next pass. if (code3 == code1) { x0 = x; y0 = y; code1 = MathUtil.getCohenSutherlandCode(x0, y0, rxmin, rymin, rxmax, rymax); } else { x1 = x; y1 = y; code2 = MathUtil.getCohenSutherlandCode(x1, y1, rxmin, rymin, rxmax, rymax); } } } } if (accept) { set(x0, y0, x1, y1); } return accept; } @Pure @Override default boolean intersects(Rectangle2ai<?, ?, ?, ?, ?, ?> rectangle) { assert rectangle != null : AssertMessages.notNullParameter(); return Rectangle2ai.intersectsRectangleSegment( rectangle.getMinX(), rectangle.getMinY(), rectangle.getMaxX(), rectangle.getMaxY(), getX1(), getY1(), getX2(), getY2()); } @Pure @Override default boolean intersects(Circle2ai<?, ?, ?, ?, ?, ?> circle) { assert circle != null : AssertMessages.notNullParameter(); return Circle2ai.intersectsCircleSegment( circle.getX(), circle.getY(), circle.getRadius(), getX1(), getY1(), getX2(), getY2()); } @Pure @Override default boolean intersects(Segment2ai<?, ?, ?, ?, ?, ?> segment) { assert segment != null : AssertMessages.notNullParameter(); return intersectsSegmentSegment( getX1(), getY1(), getX2(), getY2(), 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.calculatesCrossingsPathIteratorSegmentShadow( 0, iterator, getX1(), getY1(), getX2(), getY2(), CrossingComputationType.SIMPLE_INTERSECTION_WHEN_NOT_POLYGON); return crossings == MathConstants.SHAPE_INTERSECTS || (crossings & mask) != 0; } @Pure @Override default boolean intersects(MultiShape2ai<?, ?, ?, ?, ?, ?, ?> multishape) { assert multishape != null : AssertMessages.notNullParameter(); return multishape.intersects(this); } /** The Bresenham line algorithm is an algorithm which determines which points in * an n-dimensional raster should be plotted in order to form a close * approximation to a straight line between two given points. It is * commonly used to draw lines on a computer screen, as it uses only * integer addition, subtraction and bit shifting, all of which are * very cheap operations in standard computer architectures. It is one of the * earliest algorithms developed in the field of computer graphics. A minor extension * to the original algorithm also deals with drawing circles. * * <p>While algorithms such as Wu's algorithm are also frequently used in modern * computer graphics because they can support antialiasing, the speed and * simplicity of Bresenham's line algorithm mean that it is still important. * The algorithm is used in hardware such as plotters and in the graphics * chips of modern graphics cards. It can also be found in many software * graphics libraries. Because the algorithm is very simple, it is often * implemented in either the firmware or the hardware of modern graphics cards. * * @param <P> the type of the points. * @param <V> the type of the vectors. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ class BresenhamLineIterator<P extends Point2D<? super P, ? super V>, V extends Vector2D<? super V, ? super P>> implements Iterator<P> { private final GeomFactory2D<V, P> factory; private final boolean steep; private final int ystep; private final int xstep; private final int deltax; private final int deltay; private final int x1; private int y; private int x; private int error; /** * @param factory the point factory. * @param x0 is the x-coordinate of the first point of the Bresenham line. * @param y0 is the y-coordinate of the first point of the Bresenham line. * @param x1 is the x-coordinate of the last point of the Bresenham line. * @param y1 is the y-coordinate of the last point of the Bresenham line. */ public BresenhamLineIterator(GeomFactory2D<V, P> factory, int x0, int y0, int x1, int y1) { assert factory != null : AssertMessages.notNullParameter(0); this.factory = factory; int localx0 = x0; int localy0 = y0; int localx1 = x1; int localy1 = y1; this.steep = Math.abs(localy1 - localy0) > Math.abs(localx1 - localx0); int swapv; if (this.steep) { //swap(x0, y0); swapv = localx0; localx0 = localy0; localy0 = swapv; //swap(x1, y1); swapv = localx1; localx1 = localy1; localy1 = swapv; } /*if (localx0 > localx1) { //swap(x0, x1); swapv = localx0; localx0 = localx1; localx1 = swapv; //swap(y0, y1); swapv = localy0; localy0 = localy1; localy1 = swapv; }*/ this.deltax = Math.abs(localx1 - localx0); this.deltay = Math.abs(localy1 - localy0); this.error = this.deltax / 2; this.y = localy0; if (localx0 < localx1) { this.xstep = 1; } else { this.xstep = -1; } if (localy0 < localy1) { this.ystep = 1; } else { this.ystep = -1; } this.x1 = localx1; this.x = localx0; } @Pure @Override public boolean hasNext() { return ((this.xstep > 0) && (this.x <= this.x1)) || ((this.xstep < 0) && (this.x1 <= this.x)); } /** Replies the next point in the given parameter. * * @param pt the output point. */ public void next(Point2D<?, ?> pt) { if (this.steep) { pt.set(this.y, this.x); } else { pt.set(this.x, this.y); } this.error = this.error - this.deltay; if (this.error < 0) { this.y = this.y + this.ystep; this.error = this.error + this.deltax; } this.x += this.xstep; } @Pure @Override public P next() { final P p = this.factory.newPoint(); next(p); return p; } @Override public void remove() { throw new UnsupportedOperationException(); } } /** Abstract iterator on the path elements of the segment. * * @param <IE> is the type of the path elements. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ abstract class AbstractSegmentPathIterator<IE extends PathElement2ai> implements PathIterator2ai<IE> { /** Element. */ protected final Segment2ai<?, ?, IE, ?, ?, ?> segment; /** * @param segment the element. */ public AbstractSegmentPathIterator(Segment2ai<?, ?, IE, ?, ?, ?> segment) { assert segment != null : AssertMessages.notNullParameter(); this.segment = segment; } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public PathWindingRule getWindingRule() { return PathWindingRule.NON_ZERO; } @Pure @Override public boolean isPolyline() { return true; } @Override public boolean isCurved() { return false; } @Override public boolean isMultiParts() { return false; } @Override public boolean isPolygon() { return false; } @Override public GeomFactory2ai<IE, ?, ?, ?> getGeomFactory() { return this.segment.getGeomFactory(); } } /** Iterator on the path elements of the segment. * * @param <IE> is the type of the path elements. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ class TransformedSegmentPathIterator<IE extends PathElement2ai> extends AbstractSegmentPathIterator<IE> { private final Transform2D transform; private Point2D<?, ?> p1; private Point2D<?, ?> p2; private int x1; private int y1; private int x2; private int y2; private int index; /** * @param segment the segment to iterate on. * @param transform the transformation to apply. */ public TransformedSegmentPathIterator(Segment2ai<?, ?, IE, ?, ?, ?> segment, Transform2D transform) { super(segment); assert transform != null : AssertMessages.notNullParameter(1); this.transform = transform; this.p1 = new InnerComputationPoint2ai(); this.p2 = new InnerComputationPoint2ai(); this.x1 = segment.getX1(); this.y1 = segment.getY1(); this.x2 = segment.getX2(); this.y2 = segment.getY2(); } @Override public PathIterator2ai<IE> restartIterations() { return new TransformedSegmentPathIterator<>(this.segment, this.transform); } @Pure @Override public boolean hasNext() { return this.index <= 1; } @Override public IE next() { if (this.index > 1) { throw new NoSuchElementException(); } final int idx = this.index; ++this.index; switch (idx) { case 0: this.p2.set(this.x1, this.y1); if (this.transform != null) { this.transform.transform(this.p2); } return getGeomFactory().newMovePathElement( this.p2.ix(), this.p2.iy()); case 1: this.p1.set(this.p2); this.p2.set(this.x2, this.y2); if (this.transform != null) { this.transform.transform(this.p2); } return getGeomFactory().newLinePathElement( this.p1.ix(), this.p1.iy(), this.p2.ix(), this.p2.iy()); default: throw new NoSuchElementException(); } } } /** Iterator on the path elements of the segment. * * @param <IE> is the type of the path elements. * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ class SegmentPathIterator<IE extends PathElement2ai> extends AbstractSegmentPathIterator<IE> { private int x1; private int y1; private int x2; private int y2; private int index; /** * @param segment the segment to iterate on. */ public SegmentPathIterator(Segment2ai<?, ?, IE, ?, ?, ?> segment) { super(segment); this.x1 = segment.getX1(); this.y1 = segment.getY1(); this.x2 = segment.getX2(); this.y2 = segment.getY2(); } @Override public PathIterator2ai<IE> restartIterations() { return new SegmentPathIterator<>(this.segment); } @Pure @Override public boolean hasNext() { return this.index <= 1; } @Override public IE next() { if (this.index > 1) { throw new NoSuchElementException(); } final int idx = this.index; ++this.index; switch (idx) { case 0: return getGeomFactory().newMovePathElement(this.x1, this.y1); case 1: return getGeomFactory().newLinePathElement( this.x1, this.y1, this.x2, this.y2); default: throw new NoSuchElementException(); } } } }