View Javadoc

1   /* File Rectangle2D.java 
2    *
3    * Project : Java Geometry Library
4    *
5    * ===========================================
6    * 
7    * This library is free software; you can redistribute it and/or modify it 
8    * under the terms of the GNU Lesser General Public License as published by
9    * the Free Software Foundation, either version 2.1 of the License, or (at
10   * your option) any later version.
11   *
12   * This library is distributed in the hope that it will be useful, but 
13   * WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
14   * or FITNESS FOR A PARTICULAR PURPOSE.
15   *
16   * See the GNU Lesser General Public License for more details.
17   *
18   * You should have received a copy of the GNU Lesser General Public License
19   * along with this library. if not, write to :
20   * The Free Software Foundation, Inc., 59 Temple Place, Suite 330,
21   * Boston, MA 02111-1307, USA.
22   */
23  
24  // package
25  
26  package math.geom2d.polygon;
27  
28  // Imports
29  import java.awt.Graphics2D;
30  import java.util.ArrayList;
31  import java.util.Collection;
32  import java.util.Iterator;
33  
34  import math.geom2d.AffineTransform2D;
35  import math.geom2d.Box2D;
36  import math.geom2d.Point2D;
37  import math.geom2d.circulinear.CirculinearBoundarySet2D;
38  import math.geom2d.circulinear.CirculinearCurve2DUtils;
39  import math.geom2d.circulinear.CirculinearDomain2D;
40  import math.geom2d.circulinear.GenericCirculinearDomain2D;
41  import math.geom2d.domain.Boundary2DUtils;
42  import math.geom2d.domain.Domain2D;
43  import math.geom2d.domain.GenericDomain2D;
44  import math.geom2d.line.LineSegment2D;
45  import math.geom2d.line.StraightLine2D;
46  import math.geom2d.transform.CircleInversion2D;
47  
48  /**
49   * Rectangle2D defines a rectangle rotated around its first corner.
50   */
51  public class Rectangle2D implements Polygon2D {
52  
53      // ===================================================================
54      // constants
55  
56      // ===================================================================
57      // class variables
58  
59      protected double x0;
60      protected double y0;
61      protected double w;
62      protected double h;
63      protected double theta;
64  
65      // ===================================================================
66      // constructors
67  
68      /** Main constructor */
69      public Rectangle2D(double x0, double y0, double w, double h, double theta) {
70          this.x0 = x0;
71          this.y0 = y0;
72          this.w = w;
73          this.h = h;
74          this.theta = theta;
75      }
76  
77      /** Empty contructor (size and position zero) */
78      public Rectangle2D() {
79          this(0, 0, 0, 0, 0);
80      }
81  
82      /** Constructor from awt, to allow easy construction from existing apps. */
83      public Rectangle2D(java.awt.geom.Rectangle2D rect) {
84          this.x0 = rect.getX();
85          this.y0 = rect.getY();
86          this.w = rect.getWidth();
87          this.h = rect.getHeight();
88          this.theta = 0;
89      }
90  
91      /** Main constructor */
92      public Rectangle2D(double x0, double y0, double w, double h) {
93          this.x0 = x0;
94          this.y0 = y0;
95          this.w = w;
96          this.h = h;
97          this.theta = 0;
98      }
99  
100     /** Main constructor */
101     public Rectangle2D(Point2D point, double w, double h, double theta) {
102         this.x0 = point.getX();
103         this.y0 = point.getY();
104         this.w = w;
105         this.h = h;
106         this.theta = theta;
107     }
108 
109     /** Main constructor */
110     public Rectangle2D(Point2D point, double w, double h) {
111         this.x0 = point.getX();
112         this.y0 = point.getY();
113         this.w = w;
114         this.h = h;
115         this.theta = 0;
116     }
117 
118     // ===================================================================
119     // accessors
120 
121     public double getX() {
122         return x0;
123     }
124 
125     public double getY() {
126         return y0;
127     }
128 
129     public double getWidth() {
130         return w;
131     }
132 
133     public double getHeight() {
134         return h;
135     }
136 
137     public double getTheta() {
138         return theta;
139     }
140 
141     // ===================================================================
142     // mutators
143 
144     /**
145      * Apply the characteristics of the given Rectangle to this object.
146      */
147     public void setRectangle(Rectangle2D rect) {
148         this.x0 = rect.x0;
149         this.y0 = rect.y0;
150         this.w = rect.w;
151         this.h = rect.h;
152         this.theta = rect.theta;
153     }
154 
155     /**
156      * Apply the characteristics of the given Rectangle to this object.
157      */
158     public void setRectangle(java.awt.geom.Rectangle2D rect) {
159         this.x0 = rect.getX();
160         this.y0 = rect.getY();
161         this.w = rect.getWidth();
162         this.h = rect.getHeight();
163         this.theta = 0;
164     }
165 
166     /**
167      * Apply the characteristics of the given Rectangle to this object.
168      */
169     public void setRectangle(double x, double y, double width, double height,
170             double theta) {
171         this.x0 = x;
172         this.y0 = y;
173         this.w = width;
174         this.h = height;
175         this.theta = theta;
176     }
177 
178     // ===================================================================
179     // methods inherited from interface AbstractPolygon2D
180 
181     /**
182      * Returns the vertices of the rectangle as a collection of points.
183      * 
184      * @return the vertices of the rectangle.
185      */
186     public Collection<Point2D> getVertices() {
187         AffineTransform2D rot = AffineTransform2D.createRotation(x0, y0, theta);
188         ArrayList<Point2D> array = new ArrayList<Point2D>(4);
189 
190         array.add(new Point2D(x0, y0).transform(rot));
191         array.add(new Point2D(x0+w, y0).transform(rot));
192         array.add(new Point2D(x0+w, y0+h).transform(rot));
193         array.add(new Point2D(x0, y0+h).transform(rot));
194 
195         return array;
196     }
197 
198     /**
199      * Returns the i-th vertex of the polygon.
200      * 
201      * @param i index of the vertex, between 0 and 3
202      */
203     public Point2D getVertex(int i) {
204         AffineTransform2D rot = AffineTransform2D.createRotation(x0, y0, theta);
205         switch (i) {
206         case 0:
207             return new Point2D(x0, y0).transform(rot);
208         case 1:
209             return new Point2D(x0+w, y0).transform(rot);
210         case 2:
211             return new Point2D(x0+w, y0+h).transform(rot);
212         case 3:
213             return new Point2D(x0, y0+h).transform(rot);
214         default:
215             throw new IndexOutOfBoundsException();
216         }
217     }
218 
219     /**
220      * Return the number of vertices of the rectangle, which is 4.
221      * 
222      * @since 0.6.3
223      */
224     public int getVertexNumber() {
225         return 4;
226     }
227 
228     public Collection<LineSegment2D> getEdges() {
229         ArrayList<LineSegment2D> edges = new ArrayList<LineSegment2D>(4);
230         double cot = Math.cos(theta);
231         double sit = Math.sin(theta);
232 
233         double x1 = w*cot+x0;
234         double y1 = w*sit+y0;
235         double x2 = w*cot-h*sit+x0;
236         double y2 = w*sit+h*cot+y0;
237         double x3 = -h*sit+x0;
238         double y3 = h*cot+y0;
239 
240         edges.add(new LineSegment2D(x0, y0, x1, y1));
241         edges.add(new LineSegment2D(x1, y1, x2, y2));
242         edges.add(new LineSegment2D(x2, y2, x3, y3));
243         edges.add(new LineSegment2D(x3, y3, x0, y0));
244         return edges;
245     }
246 
247     public int getEdgeNumber() {
248         return 4;
249     }
250 
251     /* (non-Javadoc)
252      * @see math.geom2d.polygon.Polygon2D#getRings()
253      */
254     public Collection<LinearRing2D> getRings() {
255         ArrayList<LinearRing2D> rings = new ArrayList<LinearRing2D>(1);
256         rings.add(new LinearRing2D(this.getVertices()));
257         return rings;
258     }
259 
260 	// ===================================================================
261     // methods inherited from Domain2D interface
262 
263 	/* (non-Javadoc)
264 	 * @see math.geom2d.circulinear.CirculinearDomain2D#transform(math.geom2d.transform.CircleInversion2D)
265 	 */
266 	public CirculinearDomain2D transform(CircleInversion2D inv) {
267 		return new GenericCirculinearDomain2D(
268 				this.getBoundary().transform(inv));
269 	}
270 
271 	/* (non-Javadoc)
272 	 * @see math.geom2d.circulinear.CirculinearShape2D#getBuffer(double)
273 	 */
274 	public CirculinearDomain2D getBuffer(double dist) {
275 		return CirculinearCurve2DUtils.computeBuffer(
276 				this.getBoundary(), dist);
277 	}
278 
279 	
280     // ===================================================================
281     // methods inherited from interface Domain2D
282 
283    public CirculinearBoundarySet2D<LinearRing2D> getBoundary() {
284         double cot = Math.cos(theta);
285         double sit = Math.sin(theta);
286         Point2D pts[] = new Point2D[4];
287         pts[0] = new Point2D(x0, y0);
288         pts[1] = new Point2D(w*cot+x0, w*sit+y0);
289         pts[2] = new Point2D(w*cot-h*sit+x0, w*sit+h*cot+y0);
290         pts[3] = new Point2D(-h*sit+x0, h*cot+y0);
291 
292         return new CirculinearBoundarySet2D<LinearRing2D>(
293         		new LinearRing2D(pts));
294     }
295 
296     public Polygon2D complement() {
297         double cot = Math.cos(theta);
298         double sit = Math.sin(theta);
299         Point2D pts[] = new Point2D[4];
300         pts[0] = new Point2D(x0, y0);
301         pts[1] = new Point2D(-h*sit+x0, h*cot+y0);
302         pts[2] = new Point2D(w*cot-h*sit+x0, w*sit+h*cot+y0);
303         pts[3] = new Point2D(w*cot+x0, w*sit+y0);
304 
305         return new SimplePolygon2D(pts);
306     }
307 
308     // ===================================================================
309     // methods inherited from Shape2D interface
310 
311     /** Always returns true, because a rectangle is always bounded. */
312     public boolean isBounded() {
313         return true;
314     }
315 
316     public boolean isEmpty() {
317         return false;
318     }
319 
320     public double getDistance(java.awt.geom.Point2D p) {
321         return Math.max(getSignedDistance(p.getX(), p.getY()), 0);
322     }
323 
324     public double getDistance(double x, double y) {
325         return Math.max(getSignedDistance(x, y), 0);
326     }
327 
328     /**
329      * Get the signed distance of the shape to the given point : this distance
330      * is positive if the point lies outside the shape, and is negative if the
331      * point lies inside the shape. In this case, absolute value of distance is
332      * equals to the distance to the border of the shape.
333      */
334     public double getSignedDistance(java.awt.geom.Point2D p) {
335         return getSignedDistance(p.getX(), p.getY());
336     }
337 
338     /**
339      * Get the signed distance of the shape to the given point : this distance
340      * is positive if the point lies outside the shape, and is negative if the
341      * point lies inside the shape. In this case, absolute value of distance is
342      * equals to the distance to the border of the shape.
343      */
344     public double getSignedDistance(double x, double y) {
345         double dist = getBoundary().getDistance(x, y);
346         if (contains(x, y))
347             return -dist;
348         else
349             return dist;
350     }
351 
352     /**
353      * Return the clipped polygon.
354      */
355     public Domain2D clip(Box2D box) {
356         return new GenericDomain2D(Boundary2DUtils.clipBoundary(this
357                 .getBoundary(), box));
358     }
359 
360     /**
361      * Return bounding box of the rectangle.
362      */
363     public Box2D getBoundingBox() {
364         double xmin = x0;
365         double xmax = x0;
366         double ymin = y0;
367         double ymax = y0;
368         double x, y;
369         double cot = Math.cos(theta);
370         double sit = Math.sin(theta);
371 
372         x = w*cot+x0;
373         y = w*sit+y0;
374         if (xmin>x)
375             xmin = x;
376         if (ymin>y)
377             ymin = y;
378         if (xmax<x)
379             xmax = x;
380         if (ymax<y)
381             ymax = y;
382 
383         x = w*cot-h*sit+x0;
384         y = w*sit+h*cot+y0;
385         if (xmin>x)
386             xmin = x;
387         if (ymin>y)
388             ymin = y;
389         if (xmax<x)
390             xmax = x;
391         if (ymax<y)
392             ymax = y;
393 
394         x = h*sit+x0;
395         y = h*cot+y0;
396         if (xmin>x)
397             xmin = x;
398         if (ymin>y)
399             ymin = y;
400         if (xmax<x)
401             xmax = x;
402         if (ymax<y)
403             ymax = y;
404 
405         return new Box2D(xmin, xmax, ymin, ymax);
406     }
407 
408     /**
409      * Return the new Polygon created by an affine transform of this polygon.
410      */
411     public SimplePolygon2D transform(AffineTransform2D trans) {
412         int nPoints = 4;
413         Point2D[] array = new Point2D[nPoints];
414         Point2D[] res = new Point2D[nPoints];
415         Iterator<Point2D> iter = this.getVertices().iterator();
416         for (int i = 0; i<nPoints; i++) {
417             array[i] = iter.next();
418             res[i] = new Point2D();
419         }
420 
421         trans.transform(array, res);
422         return new SimplePolygon2D(res);
423     }
424 
425     // ===================================================================
426     // methods inherited from Shape interface
427 
428     /**
429      * This method simply invoke ancestor method. It is redefined to avoid
430      * ambiguity with contains(Shape2D).
431      */
432     public boolean contains(java.awt.geom.Point2D point) {
433         return contains(point.getX(), point.getY());
434     }
435 
436     public boolean contains(double x, double y) {
437         double cot = Math.cos(theta);
438         double sit = Math.sin(theta);
439 
440         double x1 = w*cot+x0;
441         double y1 = w*sit+y0;
442         double x2 = w*cot-h*sit+x0;
443         double y2 = w*sit+h*cot+y0;
444         double x3 = -h*sit+x0;
445         double y3 = h*cot+y0;
446 
447         StraightLine2D line = new StraightLine2D(x0, y0, x1-x0, y1-y0);
448         if (line.getSignedDistance(x, y)>0)
449             return false;
450         line = new StraightLine2D(x1, y1, x2-x1, y2-y1);
451         if (line.getSignedDistance(x, y)>0)
452             return false;
453         line = new StraightLine2D(x2, y2, x3-x2, y3-y2);
454         // line.setPoints(x2, y2, x3, y3);
455         if (line.getSignedDistance(x, y)>0)
456             return false;
457         line = new StraightLine2D(x3, y3, x0-x3, y0-y3);
458         // line.setPoints(x3, y3, x0, y0);
459         if (line.getSignedDistance(x, y)>0)
460             return false;
461         return true;
462     }
463 
464     public void draw(Graphics2D g2) {
465         g2.draw(this.getBoundary().getGeneralPath());
466     }
467 
468     public void fill(Graphics2D g) {
469         g.fill(this.getBoundary().getGeneralPath());
470     }
471 
472     // ===================================================================
473     // methods inherited from Object interface
474 
475     /**
476      * Test if retangles are the same. We consider two rectangles are equals if
477      * their corners are the same. Then, we can have different origin and
478      * different angles, but equal rectangles.
479      */
480     @Override
481     public boolean equals(Object obj) {
482         // check class, and cast type
483         if (!(obj instanceof Rectangle2D))
484             return false;
485         Rectangle2D rect = (Rectangle2D) obj;
486 
487         // first get list of corners of the 2 rectangles.
488         // Iterator<Point2D> iter1 = this.getPoints();
489         // Point2D point;
490 
491         // check all 4 corners of the first rectangle
492         // while(iter1.hasNext()){
493         // point = (Point2D) iter1.next();
494         boolean ok;
495         for (Point2D point : this.getVertices()) {
496             ok = false;
497 
498             // compare with all 4 corners of second rectangle
499             // Iterator<Point2D> iter2 = rect.getPoints();
500             // while(iter2.hasNext())
501             // if(point.equals(iter2.next()))
502             // ok = true;
503             for (Point2D point2 : rect.getVertices())
504                 if (point.equals(point2)) {
505                     ok = true;
506                     break;
507                 }
508 
509             // if the point does not belong to the corners of the other
510             // rectangle,
511             // then the two rect are different
512             if (!ok)
513                 return false;
514         }
515 
516         // test ok for 4 corners, then the two rectangles are the same.
517         return true;
518     }
519 
520 }