/* * $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.generic.PathWindingRule; import org.arakhne.afc.math.generic.Point2D; import org.arakhne.afc.math.matrix.Transform2D; /** 2D rectangle with integer coordinates. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @deprecated see {@link org.arakhne.afc.math.geometry.d2.i.Rectangle2i} */ @Deprecated @SuppressWarnings("all") public class Rectangle2i extends AbstractRectangularShape2i<Rectangle2i> { private static final long serialVersionUID = 9061018868216880896L; /** Replies if two rectangles are intersecting. * * @param x1 is the first corner of the first rectangle. * @param y1 is the first corner of the first rectangle. * @param x2 is the second corner of the first rectangle. * @param y2 is the second corner of the first rectangle. * @param x3 is the first corner of the second rectangle. * @param y3 is the first corner of the second rectangle. * @param x4 is the second corner of the second rectangle. * @param y4 is the second corner of the second rectangle. * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ public static boolean intersectsRectangleRectangle(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { assert(x1<=x2); assert(y1<=y2); assert(x3<=x4); assert(y3<=y4); return x2 > x3 && x1 < x4 && y2 > y3 && y1 < y4; } private static int code(int x, int y, int minx, int miny, int maxx, int maxy) { int code = 0; if (x<minx) code |= 0x8; if (x>maxx) code |= 0x4; if (y<miny) code |= 0x2; if (y>maxy) code |= 0x1; return code; } /** Replies if a rectangle is intersecting a segment. * <p> * The intersection test is partly based on the Cohen-Sutherland * classification of the segment. * This classification permits to detect the base cases; * and to run a clipping-like algorithm for the intersection * detection. * * @param x1 is the first corner of the rectangle. * @param y1 is the first corner of the rectangle. * @param x2 is the second corner of the rectangle. * @param y2 is the second corner of the rectangle. * @param x3 is the first point of the segment. * @param y3 is the first point of the segment. * @param x4 is the second point of the segment. * @param y4 is the second point of the segment. * @return <code>true</code> if the two shapes are intersecting; otherwise * <code>false</code> */ public static boolean intersectsRectangleSegment(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { int c1 = code(x3, y3, x1, y1, x2, y2); int c2 = code(x4, y4, x1, y1, x2, y2); if (c1==0x0 || c2==0x0) return true; if ((c1&c2)!=0x0) return false; int sx1 = x3; int sy1 = y3; int sx2 = x4; int sy2 = y4; Point2i pts = new Point2i(); Segment2i.LineIterator iterator = new Segment2i.LineIterator(sx1, sy1, sx2, sy2); while (iterator.hasNext() && c1!=0x0 && c2!=0x0 && (c1&c2)==0) { if ((c1&0x1)!=0) { do { iterator.next(pts); sy1 = pts.y(); } while (iterator.hasNext() && sy1!=y2); if (sy1!=y2) return false; sx1 = pts.x(); } else if ((c1&0x2)!=0) { do { iterator.next(pts); sy1 = pts.y(); } while (iterator.hasNext() && sy1!=y1); if (sy1!=y1) return false; sx1 = pts.x(); } else if ((c1&0x4)!=0) { do { iterator.next(pts); sx1 = pts.x(); } while (iterator.hasNext() && sx1!=x2); if (sx1!=x2) return false; sy1 = pts.y(); } else { do { iterator.next(pts); sx1 = pts.x(); } while (iterator.hasNext() && sx1!=x1); if (sx1!=x1) return false; sy1 = pts.y(); } c1 = code(sx1, sy1, x1, y1, x2, y2); } return c1==0x0 || c2==0x0; } /** Compute the closest point on the rectangle from the given point. * * @param minx is the x-coordinate of the lowest coordinate of the rectangle. * @param miny is the y-coordinate of the lowest coordinate of the rectangle. * @param maxx is the x-coordinate of the highest coordinate of the rectangle. * @param maxy is the y-coordinate of the highest coordinate of the rectangle. * @param px is the x-coordinate of the point. * @param py is the y-coordinate of the point. * @return the closest point. */ public static Point2i computeClosestPoint(int minx, int miny, int maxx, int maxy, int px, int py) { int x; int same = 0; if (px<minx) { x = minx; } else if (px>maxx) { x = maxx; } else { x = px; ++same; } int y; if (py<miny) { y = miny; } else if (py>maxy) { y = maxy; } else { y = py; ++same; } if (same==2) { return new Point2i(px,py); } return new Point2i(x,y); } /** */ public Rectangle2i() { // } /** * @param min is the min corner of the rectangle. * @param max is the max corner of the rectangle. */ public Rectangle2i(Point2i min, Point2i max) { setFromCorners(min.x(), min.y(), max.x(), max.y()); } /** * @param r */ public Rectangle2i(Rectangle2i r) { super(r); } /** * @param x * @param y * @param width * @param height */ public Rectangle2i(int x, int y, int width, int height) { setFromCorners(x, y, x+width, y+height); } @Override public void set(Shape2i s) { s.toBoundingBox(this); } /** {@inheritDoc} */ @Override public Rectangle2i toBoundingBox() { return this; } /** {@inheritDoc} */ @Override public float distanceSquared(Point2D p) { int dx; if (p.x()<this.minx) { dx = this.minx - p.x(); } else if (p.x()>this.maxx) { dx = p.x() - this.maxx; } else { dx = 0; } int dy; if (p.y()<this.miny) { dy = this.miny - p.y(); } else if (p.y()>this.maxy) { dy = p.y() - this.maxy; } else { dy = 0; } return dx*dx+dy*dy; } /** {@inheritDoc} */ @Override public float distanceL1(Point2D p) { int dx; if (p.x()<this.minx) { dx = this.minx - p.x(); } else if (p.x()>this.maxx) { dx = p.x() - this.maxx; } else { dx = 0; } int dy; if (p.y()<this.miny) { dy = this.miny - p.y(); } else if (p.y()>this.maxy) { dy = p.y() - this.maxy; } else { dy = 0; } return dx + dy; } /** {@inheritDoc} */ @Override public float distanceLinf(Point2D p) { int dx; if (p.x()<this.minx) { dx = this.minx - p.x(); } else if (p.x()>this.maxx) { dx = p.x() - this.maxx; } else { dx = 0; } int dy; if (p.y()<this.miny) { dy = this.miny - p.y(); } else if (p.y()>this.maxy) { dy = p.y() - this.maxy; } else { dy = 0; } return Math.max(dx, dy); } /** {@inheritDoc} */ @Override public Point2i getClosestPointTo(Point2D p) { return computeClosestPoint(this.minx, this.miny, this.maxx, this.maxy, p.x(), p.y()); } @Override public boolean intersects(Rectangle2i s) { return intersectsRectangleRectangle( getMinX(), getMinY(), getMaxX(), getMaxY(), s.getMinX(), s.getMinY(), s.getMaxX(), s.getMaxY()); } @Override public boolean intersects(Circle2i s) { return Circle2i.intersectsCircleRectangle( s.getX(), s.getY(), s.getRadius(), getMinX(), getMinY(), getMaxX(), getMaxY()); } @Override public boolean intersects(Segment2i s) { return intersectsRectangleSegment( this.minx, this.miny, this.maxx, this.maxy, s.getX1(), s.getY1(), s.getX2(), s.getY2()); } @Override public PathIterator2i getPathIterator(Transform2D transform) { if (transform==null) { return new CopyPathIterator( getMinX(), getMinY(), getMaxX(), getMaxY()); } return new TransformPathIterator( getMinX(), getMinY(), getMaxX(), getMaxY(), transform); } @Override public boolean contains(int x, int y) { return x>=this.minx && x<=this.maxx && y>=this.miny && y<=this.maxy; } @Override public boolean contains(Rectangle2i r) { return r.getMinX()>=getMinX() && r.getMaxX()<=getMaxX() && r.getMinY()>=getMinY() && r.getMaxY()<=getMaxY(); } /** Replies the points on the bounds of the rectangle starting from * the top border. * * @return the points on the bounds of the rectangle. */ @Override public Iterator<Point2i> getPointIterator() { return getPointIterator(Side.TOP); } /** Replies the points on the bounds of the rectangle. * * @param startingBorder is the first border to reply. * @return the points on the bounds of the rectangle. */ public Iterator<Point2i> getPointIterator(Side startingBorder) { return new RectangleSideIterator(this.minx, this.miny, this.maxx, this.maxy, startingBorder); } /** Iterator on the path elements of the rectangle. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ private static class CopyPathIterator implements PathIterator2i { 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 */ public CopyPathIterator(int x1, int y1, int x2, int y2) { this.x1 = Math.min(x1, x2); this.y1 = Math.min(y1, y2); this.x2 = Math.max(x1, x2); this.y2 = Math.max(y1, y2); if (Math.abs(this.x1-this.x2)<=0 || Math.abs(this.y1-this.y2)<=0) { this.index = 6; } } @Override public boolean hasNext() { return this.index<=5; } @Override public PathElement2i next() { int idx = this.index; ++this.index; switch(idx) { case 0: return new PathElement2i.MovePathElement2i( this.x1, this.y1); case 1: return new PathElement2i.LinePathElement2i( this.x1, this.y1, this.x2, this.y1); case 2: return new PathElement2i.LinePathElement2i( this.x2, this.y1, this.x2, this.y2); case 3: return new PathElement2i.LinePathElement2i( this.x2, this.y2, this.x1, this.y2); case 4: return new PathElement2i.LinePathElement2i( this.x1, this.y2, this.x1, this.y1); case 5: return new PathElement2i.ClosePathElement2i( this.x1, this.y1, this.x1, this.y1); 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; } } /** Iterator on the path elements of the rectangle. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ private static class TransformPathIterator implements PathIterator2i { private final Transform2D transform; private final int x1; private final int y1; private final int x2; private final int y2; private int index = 0; private final Point2D p1 = new Point2i(); private final Point2D p2 = new Point2i(); /** * @param x1 * @param y1 * @param x2 * @param y2 * @param transform */ public TransformPathIterator(int x1, int y1, int x2, int y2, Transform2D transform) { this.transform = transform; this.x1 = Math.min(x1, x2); this.y1 = Math.min(y1, y2); this.x2 = Math.max(x1, x2); this.y2 = Math.max(y1, y2); if (Math.abs(this.x1-this.x2)<=0 || Math.abs(this.y1-this.y2)<=0) { this.index = 6; } } @Override public boolean hasNext() { return this.index<=5; } @Override public PathElement2i next() { 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.y1); 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()); case 2: 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()); case 3: this.p1.set(this.p2); this.p2.set(this.x1, 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()); case 4: this.p1.set(this.p2); this.p2.set(this.x1, this.y1); 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()); case 5: return new PathElement2i.ClosePathElement2i( this.p2.x(), this.p2.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; } } /** Sides of a rectangle. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ public static enum Side { /** Top. */ TOP, /** Right. */ RIGHT, /** Bottom. */ BOTTOM, /** Left. */ LEFT; } /** Iterates on points on the sides of a rectangle. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ private static class RectangleSideIterator implements Iterator<Point2i> { private final int x0; private final int y0; private final int x1; private final int y1; private final Side firstSide; private Side currentSide; private int i; /** * @param x1 * @param y1 * @param x2 * @param y2 * @param firstSide */ public RectangleSideIterator(int x1, int y1, int x2, int y2, Side firstSide) { assert(x1<=x2 && y1<=y2); this.firstSide = firstSide; this.x0 = x1; this.y0 = y1; this.x1 = x2; this.y1 = y2; this.currentSide = (x2>x1 && y2>y1) ? this.firstSide : null; this.i = 0; } /** * {@inheritDoc} */ @Override public boolean hasNext() { return this.currentSide!=null; } /** * {@inheritDoc} */ @Override public Point2i next() { int x, y; switch(this.currentSide) { case TOP: x = this.x0+this.i; y = this.y0; break; case RIGHT: x = this.x1; y = this.y0+this.i+1; break; case BOTTOM: x = this.x1-this.i-1; y = this.y1; break; case LEFT: x = this.x0; y = this.y1-this.i-1; break; default: throw new NoSuchElementException(); } ++ this.i; Side newSide = null; switch(this.currentSide) { case TOP: if (x>=this.x1) { newSide = Side.RIGHT; this.i = 0; } break; case RIGHT: if (y>=this.y1) { newSide = Side.BOTTOM; this.i = 0; } break; case BOTTOM: if (x<=this.x0) { newSide = Side.LEFT; this.i = 0; } break; case LEFT: if (y<=this.y0+1) { newSide = Side.TOP; this.i = 0; } break; default: throw new NoSuchElementException(); } if (newSide!=null) { this.currentSide = (this.firstSide==newSide) ? null : newSide; } return new Point2i(x,y); } /** * {@inheritDoc} */ @Override public void remove() { throw new UnsupportedOperationException(); } } // class RectangleIterator }