/* * $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.discrete.object2d; import java.util.Iterator; import java.util.NoSuchElementException; import org.arakhne.afc.math.MathConstants; import org.arakhne.afc.math.generic.PathWindingRule; import org.arakhne.afc.math.generic.Point2D; import org.arakhne.afc.math.geometry.d2.ai.Segment2ai; import org.arakhne.afc.math.matrix.Transform2D; import org.arakhne.afc.vmutil.ReflectionUtil; /** 2D line segment with integer coordinates. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @deprecated see {@link org.arakhne.afc.math.geometry.d2.i.Segment2i} */ @Deprecated @SuppressWarnings("all") public class Segment2i extends AbstractShape2i<Segment2i> { /** * 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. * <center> * <a href="doc-files/crossing_circle.png"><img alt="" src="doc-files/crossing_circle.png" width="300"></a> * </center> * * @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}. */ public static int computeCrossingsFromCircle( int crossings, int cx, int cy, int radius, int x0, int y0, int x1, int y1) { int numCrosses = crossings; int xmin = cx - Math.abs(radius); int xmax = cx + Math.abs(radius); int ymin = cy - Math.abs(radius); 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 (Circle2i.intersectsCircleSegment( cx, cy, radius, x0, y0, x1, y1)) { return MathConstants.SHAPE_INTERSECTS; } else { numCrosses = computeCrossingsFromPoint(numCrosses, cx, ymin, x0, y0, x1, y1, true, false); numCrosses = computeCrossingsFromPoint(numCrosses, cx, ymax, x0, y0, x1, y1, false, true); } 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. * <center> * <a href="doc-files/crossing_segment.png"><img alt="" src="doc-files/crossing_segment.png" width="300"></a> * </center> * * @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}. */ public static int computeCrossingsFromSegment( int crossings, int sx1, int sy1, int sx2, int sy2, int x0, int y0, int x1, int y1) { /* CAUTION: * -------- * In the comment of this function, it is assumed that y0<=y1, * to simplify the explanations. * The source code is handled y0<=y1 and y0>y1. */ int numCrosses = crossings; int xmin = Math.min(sx1, sx2); int xmax = Math.max(sx1, sx2); int ymin = Math.min(sy1, sy2); 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. int side1, side2; boolean firstIsTop = (sy1<=sy2); if (firstIsTop) { side1 = Segment2ai.findsSideLinePoint(sx1, sy1, sx2, sy2, x0, y0); side2 = Segment2ai.findsSideLinePoint(sx1, sy1, sx2, sy2, x1, y1); } else { side1 = Segment2ai.findsSideLinePoint(sx2, sy2, sx1, sy1, x0, y0); side2 = Segment2ai.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. int n1, n2; n1 = computeCrossingsFromPoint(0, sx1, sy1, x0, y0, x1, y1, firstIsTop, !firstIsTop); n2 = computeCrossingsFromPoint(0, sx2, sy2, x0, y0, x1, y1, !firstIsTop, firstIsTop); // 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. * <center> * <a href="doc-files/crossing_rect.png"><img alt="" src="doc-files/crossing_rect.png" width="300"></a> * </center> * * @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}. */ public static int computeCrossingsFromRect( 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 LineIterator iterator; int ymaxline; if (y0<=y1) { iterator = new LineIterator(x0, y0, x1, y1); ymaxline = y1; } else { iterator = new LineIterator(x1, y1, x0, y0); ymaxline = y0; } Point2i p = new Point2i(); Integer xintercept1 = null; Integer xintercept2 = null; boolean cont = true; while (iterator.hasNext() && cont) { iterator.next(p); if (p.y()==rymin && (xintercept1==null || xintercept1>p.x())) { xintercept1 = p.x(); } if (p.y()==rymax && (xintercept2==null || xintercept2>p.x())) { xintercept2 = p.x(); } cont = (p.y()<=ymaxline); } if (xintercept1!=null && xintercept2!=null) { if (xintercept1<rxmin && xintercept2<rxmin) { // the intersection points are entirely on the left } else if (xintercept1>rxmax && xintercept2>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<rxmin) { // the intersection point is at entirely on the left } else if (xintercept1>rxmax) { if (y0 < y1) { // y-increasing line segment... // We know that y0 < rymax and y1 > rymin if (y0 <= rymin) ++numCrosses; } else if (y1 < y0) { // y-decreasing line segment... // We know that y1 < rymax and y0 > rymin if (y1 <= rymin) --numCrosses; } } else { return MathConstants.SHAPE_INTERSECTS; } } else if (xintercept2!=null) { // Only the bottom line of the rectangle is intersecting the segment if (xintercept2<rxmin) { // the intersection point is at entirely on the left } else if (xintercept2>rxmax) { if (y0 < y1) { // y-increasing line segment... // We know that y0 < rymax and y1 > rymin if (y0 <= rymax) ++numCrosses; } else if (y1 < y0) { // y-decreasing line segment... // We know that y1 < rymax and y0 > rymin if (y1 <= rymax) --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. * <center> * <a href="doc-files/crossing_point.png"><img alt="" src="doc-files/crossing_point.png" width="300"></a> * </center> * * @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} */ public static int computeCrossingsFromPoint( int crossing, int px, int py, int x0, int y0, int x1, int y1) { return computeCrossingsFromPoint(crossing, px, py, x0, y0, x1, y1, true, true); } /** * 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. * <center> * <a href="doc-files/crossing_point.png"><img alt="" src="doc-files/crossing_point.png" width="300"></a> * </center> * * @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. */ public static int computeCrossingsFromPoint( int crossing, int px, int py, int x0, int y0, int x1, int y1, boolean enableTopBorder, boolean enableBottomBorder) { // 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 LineIterator iterator = new LineIterator(x0, y0, x1, y1); Point2i p = new Point2i(); while (iterator.hasNext()) { iterator.next(p); if (p.y()==py) { if (p.x()==px) return MathConstants.SHAPE_INTERSECTS; if (p.x()>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; } 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> */ public static boolean intersectsSegmentSegment(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { int side1 = Segment2ai.findsSideLinePoint(x1, y1, x2, y2, x3, y3); int side2 = Segment2ai.findsSideLinePoint(x1, y1, x2, y2, x4, y4); if ((side1*side2)<=0) { return intersectsSegmentSegment1(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> */ public 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 intersectsSegmentSegment1(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. */ static int intersectsSegmentSegment1(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, boolean enableThirdPoint, boolean enableFourthPoint, Point2D intersectionPoint) { LineIterator it1; if (x1<x2) { it1 = new LineIterator(x1, y1, x2, y2); } else { it1 = new LineIterator(x2, y2, x1, y1); } LineIterator it2; if (x3<x4) { it2 = new LineIterator(x3, y3, x4, y4); } else { it2 = new LineIterator(x4, y4, x3, y3); } if (it1.hasNext() && it2.hasNext()) { Point2i p1 = new Point2i(); Point2i p2 = new Point2i(); boolean isFirstPointOfSecondSegment = true; it1.next(p1); it2.next(p2); do { if (p1.x()<p2.x()) { while (it1.hasNext() && p1.x()<p2.x()) { it1.next(p1); } } else if (p2.x()<p1.x()) { while (it2.hasNext() && p2.x()<p1.x()) { it2.next(p2); isFirstPointOfSecondSegment = false; } } int x = p1.x(); int min1 = p1.y(); int max1 = p1.y(); int min2 = isFirstPointOfSecondSegment && !enableThirdPoint ? Integer.MAX_VALUE : p2.y(); int max2 = isFirstPointOfSecondSegment && !enableThirdPoint ? Integer.MIN_VALUE : p2.y(); while (it1.hasNext()) { it1.next(p1); if (p1.x()==x) { if (p1.y()<min1) min1 = p1.y(); if (p1.y()>max1) max1 = p1.y(); } else { break; } } while (it2.hasNext()) { it2.next(p2); isFirstPointOfSecondSegment = false; if (p2.x()==x) { if (p2.y()<min2) min2 = p2.y(); if (p2.y()>max2) max2 = p2.y(); } 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; } private static final long serialVersionUID = -82425036308183925L; /** X-coordinate of the first point. */ protected int ax = 0; /** Y-coordinate of the first point. */ protected int ay = 0; /** X-coordinate of the second point. */ protected int bx = 0; /** Y-coordinate of the second point. */ protected int by = 0; /** */ public Segment2i() { // } /** * @param a * @param b */ public Segment2i(Point2D a, Point2D b) { set(a, b); } /** * @param x1 * @param y1 * @param x2 * @param y2 */ public Segment2i(int x1, int y1, int x2, int y2) { set(x1, y1, x2, y2); } /** * @param s */ public Segment2i(Segment2i s) { this.ax = s.ax; this.ay = s.ay; this.bx = s.bx; this.by = s.by; } @Override public void clear() { this.ax = this.ay = this.bx = this.by = 0; } /** * Replies if this segment is empty. * The segment is empty when the two * points are equal. * * @return <code>true</code> if the two points are * equal. */ @Override public boolean isEmpty() { return this.ax==this.bx && this.ay==this.by; } /** Change the line. * * @param x1 * @param y1 * @param x2 * @param y2 */ public void set(int x1, int y1, int x2, int y2) { this.ax = x1; this.ay = y1; this.bx = x2; this.by = y2; } /** Change the line. * * @param a * @param b */ public void set(Point2D a, Point2D b) { this.ax = a.x(); this.ay = a.y(); this.bx = b.x(); this.by = b.y(); } @Override public void set(Shape2i s) { Rectangle2i r = s.toBoundingBox(); this.ax = r.getMinX(); this.ay = r.getMinY(); this.bx = r.getMaxX(); this.by = r.getMaxY(); } /** Replies the X of the first point. * * @return the x of the first point. */ public int getX1() { return this.ax; } /** Replies the Y of the first point. * * @return the y of the first point. */ public int getY1() { return this.ay; } /** Replies the X of the second point. * * @return the x of the second point. */ public int getX2() { return this.bx; } /** Replies the Y of the second point. * * @return the y of the second point. */ public int getY2() { return this.by; } /** Replies the first point. * * @return the first point. */ public Point2D getP1() { return new Point2i(this.ax, this.ay); } /** Replies the second point. * * @return the second point. */ public Point2D getP2() { return new Point2i(this.bx, this.by); } @Override public Rectangle2i toBoundingBox() { Rectangle2i r = new Rectangle2i(); r.setFromCorners( this.ax, this.ay, this.bx, this.by); return r; } @Override public void toBoundingBox(Rectangle2i box) { box.setFromCorners( this.ax, this.ay, this.bx, this.by); } /** {@inheritDoc} */ @Override public float distanceSquared(Point2D p) { Point2D closestPoint = getClosestPointTo(p); return closestPoint.distanceSquared(p); } /** {@inheritDoc} */ @Override public float distanceL1(Point2D p) { Point2D closestPoint = getClosestPointTo(p); return closestPoint.distanceL1(p); } /** {@inheritDoc} */ @Override public float distanceLinf(Point2D p) { Point2D closestPoint = getClosestPointTo(p); return closestPoint.distanceLinf(p); } /** {@inheritDoc} */ @Override public boolean contains(int x, int y) { if (x>=this.ax && x<=this.bx && y>=this.ay && y<=this.by) { if (this.ax==this.bx || this.ay==this.by) { return true; } int minDist = Integer.MAX_VALUE; int d; int a,b; Point2i p = new Point2i(); LineIterator iterator = new LineIterator(this.ax, this.ay, this.bx, this.by); while (iterator.hasNext()) { iterator.next(p); a = Math.abs(x-p.x()); b = Math.abs(y-p.y()); d = a*a + b*b ; if (d==0) return true; if (d>minDist) { return false; } minDist = d; } } return false; } /** {@inheritDoc} */ @Override public boolean contains(Rectangle2i r) { return r.isEmpty() && contains(r.getMinX(), r.getMinY()); } /** {@inheritDoc} */ @Override public Point2i getClosestPointTo(Point2D p) { return computeClosestPointTo(this.ax, this.ay, this.bx, this.by, p.x(), p.y()); } /** 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 * @return the closest point in the segment to the point. */ public static Point2i computeClosestPointTo(int ax, int ay, int bx, int by, int px, int py) { // 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; int d; int a,b; boolean oneBestFound = false; Point2i solution = new Point2i(ax, ay); Point2i cp = new Point2i(); LineIterator iterator = new LineIterator(ax, ay, bx, by); while (iterator.hasNext()) { iterator.next(cp); a = Math.abs(px-cp.x()); b = Math.abs(py-cp.y()); d = a*a + b*b ; if (d==0) { // We are sure that the closest point was found return cp; } if (d>minDist) { // here we have found a good candidate, but // but due to the rasterization the optimal solution // may be one pixel after the already found. // See the special case configuration at the beginning // of this function. if (oneBestFound) return solution; oneBestFound = true; } else { minDist = d; solution.set(cp); // here we have found a good candidate, but // but due to the rasterization the optimal solution // may be one pixel after the already found. // See the special case configuration at the beginning // of this function. if (oneBestFound) return solution; } } return solution; } @Override public void translate(int dx, int dy) { this.ax += dx; this.ay += dy; this.bx += dx; this.by += dy; } @Override public PathIterator2i getPathIterator(Transform2D transform) { return new SegmentPathIterator( this.ax, this.ay, this.bx, this.by, 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. */ @Override public Iterator<Point2i> getPointIterator() { return new LineIterator(this.ax, this.ay, this.bx, this.by); } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof Segment2i) { Segment2i rr2d = (Segment2i) obj; return ((this.ax == rr2d.getX1()) && (this.ay == rr2d.getY1()) && (this.bx == rr2d.getX2()) && (this.by == rr2d.getY2())); } return false; } @Override public int hashCode() { long bits = 1L; bits = 31L * bits + this.ax; bits = 31L * bits + this.ay; bits = 31L * bits + this.bx; bits = 31L * bits + this.by; return (int) (bits ^ (bits >> 32)); } /** Transform the current segment. * This function changes the current segment. * * @param transform is the affine transformation to apply. * @see #createTransformedShape(Transform2D) */ public void transform(Transform2D transform) { Point2i p = new Point2i(this.ax, this.ay); transform.transform(p); this.ax = p.x(); this.ay = p.y(); p.set(this.bx, this.by); transform.transform(p); this.bx = p.x(); this.by = p.y(); } @Override public Shape2i createTransformedShape(Transform2D transform) { Point2D p1 = transform.transform(this.ax, this.ay); Point2D p2 = transform.transform(this.bx, this.by); return new Segment2i(p1, p2); } @Override public boolean intersects(Rectangle2i s) { return Rectangle2i.intersectsRectangleSegment( s.getMinX(), s.getMinY(), s.getMaxX(), s.getMaxY(), getX1(), getY1(), getX2(), getY2()); } @Override public boolean intersects(Circle2i s) { return Circle2i.intersectsCircleSegment( s.getX(), s.getY(), s.getRadius(), getX1(), getY1(), getX2(), getY2()); } @Override public boolean intersects(Segment2i s) { return intersectsSegmentSegment( getX1(), getY1(), getX2(), getY2(), s.getX1(), s.getY1(), s.getX2(), s.getY2()); } @Override public String toString() { return ReflectionUtil.toString(this); } /** Iterator on the path elements of the segment. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ private static class SegmentPathIterator implements PathIterator2i { private final Point2D p1 = new Point2i(); private final Point2D p2 = new Point2i(); private final Transform2D transform; private final int x1; private final int y1; private final int x2; private final int y2; private int index = 0; /** * @param x1 * @param y1 * @param x2 * @param y2 * @param transform */ public SegmentPathIterator(int x1, int y1, int x2, int y2, Transform2D transform) { this.transform = transform; this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; if (this.x1==this.x2 && this.y1==this.y2) { this.index = 2; } } @Override public boolean hasNext() { return this.index<=1; } @Override public PathElement2i next() { if (this.index>1) throw new NoSuchElementException(); 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 new PathElement2i.MovePathElement2i( this.p2.x(), this.p2.y()); case 1: this.p1.set(this.p2); this.p2.set(this.x2, this.y2); if (this.transform!=null) { this.transform.transform(this.p2); } return new PathElement2i.LinePathElement2i( this.p1.x(), this.p1.y(), this.p2.x(), this.p2.y()); default: throw new NoSuchElementException(); } } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public PathWindingRule getWindingRule() { return PathWindingRule.NON_ZERO; } @Override public boolean isPolyline() { return false; } } /** 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. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ static class LineIterator implements Iterator<Point2i> { 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, x; private int error; /** * @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 LineIterator(int x0, int y0, int x1, int y1) { int _x0 = x0; int _y0 = y0; int _x1 = x1; int _y1 = y1; this.steep = Math.abs(_y1 - _y0) > Math.abs(_x1 - _x0); int swapv; if (this.steep) { //swap(x0, y0); swapv = _x0; _x0 = _y0; _y0 = swapv; //swap(x1, y1); swapv = _x1; _x1 = _y1; _y1 = swapv; } /*if (_x0 > _x1) { //swap(x0, x1); swapv = _x0; _x0 = _x1; _x1 = swapv; //swap(y0, y1); swapv = _y0; _y0 = _y1; _y1 = swapv; }*/ this.deltax = Math.abs(_x1 - _x0); this.deltay = Math.abs(_y1 - _y0); this.error = this.deltax / 2; this.y = _y0; if (_x0 < _x1) this.xstep = 1; else this.xstep = -1; if (_y0 < _y1) this.ystep = 1; else this.ystep = -1; this.x1 = _x1; this.x = _x0; } /** * {@inheritDoc} */ @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 p */ public void next(Point2i p) { if (this.steep) { p.set(this.y, this.x); } else { p.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; } /** * {@inheritDoc} */ @Override public Point2i next() { Point2i p = new Point2i(); next(p); return p; } /** * {@inheritDoc} */ @Override public void remove() { throw new UnsupportedOperationException(); } } // class LineIterator }