1 /* Rectangle2D.java -- generic rectangles in 2-D space
2 Copyright (C) 2000, 2001, 2002, 2004 Free Software Foundation
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package java
.awt
.geom
;
41 import java
.util
.NoSuchElementException
;
44 * This class describes a rectangle by a point (x,y) and dimension (w x h).
45 * The actual storage is left up to subclasses.
47 * <p>It is valid for a rectangle to have negative width or height; but it
48 * is considered to have no area or internal points. Therefore, the behavior
49 * in methods like <code>contains</code> or <code>intersects</code> is
50 * undefined unless the rectangle has positive width and height.
52 * @author Tom Tromey (tromey@cygnus.com)
53 * @author Eric Blake (ebb9@email.byu.edu)
55 * @status updated to 1.4
57 public abstract class Rectangle2D
extends RectangularShape
60 * The point lies left of the rectangle (p.x < r.x).
62 * @see #outcode(double, double)
64 public static final int OUT_LEFT
= 1;
67 * The point lies above the rectangle (p.y < r.y).
69 * @see #outcode(double, double)
71 public static final int OUT_TOP
= 2;
74 * The point lies right of the rectangle (p.x > r.maxX).
76 * @see #outcode(double, double)
78 public static final int OUT_RIGHT
= 4;
81 * The point lies below of the rectangle (p.y > r.maxY).
83 * @see #outcode(double, double)
85 public static final int OUT_BOTTOM
= 8;
88 * Default constructor.
90 protected Rectangle2D()
95 * Set the bounding box of this rectangle.
97 * @param x the new X coordinate
98 * @param y the new Y coordinate
99 * @param w the new width
100 * @param h the new height
102 public abstract void setRect(double x
, double y
, double w
, double h
);
105 * Set the bounding box of this rectangle from the given one.
107 * @param r rectangle to copy
108 * @throws NullPointerException if r is null
110 public void setRect(Rectangle2D r
)
112 setRect(r
.getX(), r
.getY(), r
.getWidth(), r
.getHeight());
116 * Tests if the specified line intersects the interior of this rectangle.
118 * @param x1 the first x coordinate of line segment
119 * @param y1 the first y coordinate of line segment
120 * @param x2 the second x coordinate of line segment
121 * @param y2 the second y coordinate of line segment
122 * @return true if the line intersects the rectangle
124 public boolean intersectsLine(double x1
, double y1
, double x2
, double y2
)
128 double w
= getWidth();
129 double h
= getHeight();
130 if (w
<= 0 || h
<= 0)
133 if (x1
>= x
&& x1
<= x
+ w
&& y1
>= y
&& y1
<= y
+ h
)
135 if (x2
>= x
&& x2
<= x
+ w
&& y2
>= y
&& y2
<= y
+ h
)
141 return (Line2D
.linesIntersect(x1
, y1
, x2
, y2
, x
, y
, x
, y3
)
142 || Line2D
.linesIntersect(x1
, y1
, x2
, y2
, x
, y3
, x3
, y3
)
143 || Line2D
.linesIntersect(x1
, y1
, x2
, y2
, x3
, y3
, x3
, y
)
144 || Line2D
.linesIntersect(x1
, y1
, x2
, y2
, x3
, y
, x
, y
));
148 * Tests if the specified line intersects the interior of this rectangle.
150 * @param l the line segment
151 * @return true if the line intersects the rectangle
152 * @throws NullPointerException if l is null
154 public boolean intersectsLine(Line2D l
)
156 return intersectsLine(l
.getX1(), l
.getY1(), l
.getX2(), l
.getY2());
160 * Determine where the point lies with respect to this rectangle. The
161 * result will be the binary OR of the appropriate bit masks.
163 * @param x the x coordinate to check
164 * @param y the y coordinate to check
165 * @return the binary OR of the result
171 public abstract int outcode(double x
, double y
);
174 * Determine where the point lies with respect to this rectangle. The
175 * result will be the binary OR of the appropriate bit masks.
177 * @param p the point to check
178 * @return the binary OR of the result
179 * @throws NullPointerException if p is null
185 public int outcode(Point2D p
)
187 return outcode(p
.getX(), p
.getY());
191 * Set the bounding box of this rectangle.
193 * @param x the new X coordinate
194 * @param y the new Y coordinate
195 * @param w the new width
196 * @param h the new height
198 public void setFrame(double x
, double y
, double w
, double h
)
204 * Returns the bounds of this rectangle. A pretty useless method, as this
205 * is already a rectangle.
207 * @return a copy of this rectangle
209 public Rectangle2D
getBounds2D()
211 return (Rectangle2D
) clone();
215 * Test if the given point is contained in the rectangle.
217 * @param x the x coordinate of the point
218 * @param y the y coordinate of the point
219 * @return true if (x,y) is in the rectangle
221 public boolean contains(double x
, double y
)
225 double w
= getWidth();
226 double h
= getHeight();
227 return w
> 0 && h
> 0 && x
>= mx
&& x
< mx
+ w
&& y
>= my
&& y
< my
+ h
;
231 * Tests if the given rectangle intersects this one. In other words, test if
232 * the two rectangles share at least one internal point.
234 * @param x the x coordinate of the other rectangle
235 * @param y the y coordinate of the other rectangle
236 * @param w the width of the other rectangle
237 * @param h the height of the other rectangle
238 * @return true if the rectangles intersect
240 public boolean intersects(double x
, double y
, double w
, double h
)
244 double mw
= getWidth();
245 double mh
= getHeight();
246 return w
> 0 && h
> 0 && mw
> 0 && mh
> 0
247 && x
< mx
+ mw
&& x
+ w
> mx
&& y
< my
+ mh
&& y
+ h
> my
;
251 * Tests if this rectangle contains the given one. In other words, test if
252 * this rectangle contains all points in the given one.
254 * @param x the x coordinate of the other rectangle
255 * @param y the y coordinate of the other rectangle
256 * @param w the width of the other rectangle
257 * @param h the height of the other rectangle
258 * @return true if this rectangle contains the other
260 public boolean contains(double x
, double y
, double w
, double h
)
264 double mw
= getWidth();
265 double mh
= getHeight();
266 return w
> 0 && h
> 0 && mw
> 0 && mh
> 0
267 && x
>= mx
&& x
+ w
<= mx
+ mw
&& y
>= my
&& y
+ h
<= my
+ mh
;
271 * Return a new rectangle which is the intersection of this and the given
272 * one. The result will be empty if there is no intersection.
274 * @param r the rectangle to be intersected
275 * @return the intersection
276 * @throws NullPointerException if r is null
278 public abstract Rectangle2D
createIntersection(Rectangle2D r
);
281 * Intersects a pair of rectangles, and places the result in the
282 * destination; this can be used to avoid object creation. This method
283 * even works when the destination is also a source, although you stand
284 * to lose the original data.
286 * @param src1 the first source
287 * @param src2 the second source
288 * @param dest the destination for the intersection
289 * @throws NullPointerException if any rectangle is null
291 public static void intersect(Rectangle2D src1
, Rectangle2D src2
,
294 double x
= Math
.max(src1
.getX(), src2
.getX());
295 double y
= Math
.max(src1
.getY(), src2
.getY());
296 double maxx
= Math
.min(src1
.getMaxX(), src2
.getMaxX());
297 double maxy
= Math
.min(src1
.getMaxY(), src2
.getMaxY());
298 dest
.setRect(x
, y
, maxx
- x
, maxy
- y
);
302 * Return a new rectangle which is the union of this and the given one.
304 * @param r the rectangle to be merged
306 * @throws NullPointerException if r is null
308 public abstract Rectangle2D
createUnion(Rectangle2D r
);
311 * Joins a pair of rectangles, and places the result in the destination;
312 * this can be used to avoid object creation. This method even works when
313 * the destination is also a source, although you stand to lose the
316 * @param src1 the first source
317 * @param src2 the second source
318 * @param dest the destination for the union
319 * @throws NullPointerException if any rectangle is null
321 public static void union(Rectangle2D src1
, Rectangle2D src2
,
324 double x
= Math
.min(src1
.getX(), src2
.getX());
325 double y
= Math
.min(src1
.getY(), src2
.getY());
326 double maxx
= Math
.max(src1
.getMaxX(), src2
.getMaxX());
327 double maxy
= Math
.max(src1
.getMaxY(), src2
.getMaxY());
328 dest
.setRect(x
, y
, maxx
- x
, maxy
- y
);
332 * Modifies this rectangle so that it represents the smallest rectangle
333 * that contains both the existing rectangle and the specified point.
334 * However, if the point falls on one of the two borders which are not
335 * inside the rectangle, a subsequent call to <code>contains</code> may
338 * @param newx the X coordinate of the point to add to this rectangle
339 * @param newy the Y coordinate of the point to add to this rectangle
341 public void add(double newx
, double newy
)
343 double minx
= Math
.min(getX(), newx
);
344 double maxx
= Math
.max(getMaxX(), newx
);
345 double miny
= Math
.min(getY(), newy
);
346 double maxy
= Math
.max(getMaxY(), newy
);
347 setRect(minx
, miny
, maxx
- minx
, maxy
- miny
);
351 * Modifies this rectangle so that it represents the smallest rectangle
352 * that contains both the existing rectangle and the specified point.
353 * However, if the point falls on one of the two borders which are not
354 * inside the rectangle, a subsequent call to <code>contains</code> may
357 * @param p the point to add to this rectangle
358 * @throws NullPointerException if p is null
360 public void add(Point2D p
)
362 add(p
.getX(), p
.getY());
366 * Modifies this rectangle so that it represents the smallest rectangle
367 * that contains both the existing rectangle and the specified rectangle.
369 * @param r the rectangle to add to this rectangle
370 * @throws NullPointerException if r is null
371 * @see #union(Rectangle2D, Rectangle2D, Rectangle2D)
373 public void add(Rectangle2D r
)
375 union(this, r
, this);
379 * Return an iterator along the shape boundary. If the optional transform
380 * is provided, the iterator is transformed accordingly. Each call returns
381 * a new object, independent from others in use. This iterator is thread
382 * safe; modifications to the rectangle do not affect the results of this
385 * @param at an optional transform to apply to the iterator
386 * @return a new iterator over the boundary
389 public PathIterator
getPathIterator(final AffineTransform at
)
391 final double minx
= getX();
392 final double miny
= getY();
393 final double maxx
= minx
+ getWidth();
394 final double maxy
= miny
+ getHeight();
395 return new PathIterator()
397 /** Current coordinate. */
398 private int current
= (maxx
<= minx
&& maxy
<= miny
) ?
6 : 0;
400 public int getWindingRule()
402 // A test program showed that Sun J2SE 1.3.1 and 1.4.1_01
403 // return WIND_NON_ZERO paths. While this does not really
404 // make any difference for rectangles (because they are not
405 // self-intersecting), it seems appropriate to behave
408 return WIND_NON_ZERO
;
411 public boolean isDone()
421 public int currentSegment(float[] coords
)
426 coords
[0] = (float) maxx
;
427 coords
[1] = (float) miny
;
430 coords
[0] = (float) maxx
;
431 coords
[1] = (float) maxy
;
434 coords
[0] = (float) minx
;
435 coords
[1] = (float) maxy
;
439 coords
[0] = (float) minx
;
440 coords
[1] = (float) miny
;
445 throw new NoSuchElementException("rect iterator out of bounds");
448 at
.transform(coords
, 0, coords
, 0, 1);
449 return current
== 0 ? SEG_MOVETO
: SEG_LINETO
;
452 public int currentSegment(double[] coords
)
476 throw new NoSuchElementException("rect iterator out of bounds");
479 at
.transform(coords
, 0, coords
, 0, 1);
480 return current
== 0 ? SEG_MOVETO
: SEG_LINETO
;
486 * Return an iterator along the shape boundary. If the optional transform
487 * is provided, the iterator is transformed accordingly. Each call returns
488 * a new object, independent from others in use. This iterator is thread
489 * safe; modifications to the rectangle do not affect the results of this
490 * path instance. As the rectangle is already flat, the flatness parameter
493 * @param at an optional transform to apply to the iterator
494 * @param flatness the maximum distance for deviation from the real boundary
495 * @return a new iterator over the boundary
498 public PathIterator
getPathIterator(AffineTransform at
, double flatness
)
500 return getPathIterator(at
);
504 * Return the hashcode for this rectangle. The formula is not documented, but
505 * appears to be the same as:
507 * long l = Double.doubleToLongBits(getX())
508 * + 37 * Double.doubleToLongBits(getY())
509 * + 43 * Double.doubleToLongBits(getWidth())
510 * + 47 * Double.doubleToLongBits(getHeight());
511 * return (int) ((l >> 32) ^ l);
514 * @return the hashcode
516 public int hashCode()
518 // Talk about a fun time reverse engineering this one!
519 long l
= java
.lang
.Double
.doubleToLongBits(getX())
520 + 37 * java
.lang
.Double
.doubleToLongBits(getY())
521 + 43 * java
.lang
.Double
.doubleToLongBits(getWidth())
522 + 47 * java
.lang
.Double
.doubleToLongBits(getHeight());
523 return (int) ((l
>> 32) ^ l
);
527 * Tests this rectangle for equality against the specified object. This
528 * will be true if an only if the specified object is an instance of
529 * Rectangle2D with the same coordinates and dimensions.
531 * @param obj the object to test against for equality
532 * @return true if the specified object is equal to this one
534 public boolean equals(Object obj
)
536 if (! (obj
instanceof Rectangle2D
))
538 Rectangle2D r
= (Rectangle2D
) obj
;
539 return r
.getX() == getX() && r
.getY() == getY()
540 && r
.getWidth() == getWidth() && r
.getHeight() == getHeight();
544 * This class defines a rectangle in <code>double</code> precision.
546 * @author Eric Blake (ebb9@email.byu.edu)
548 * @status updated to 1.4
550 public static class Double
extends Rectangle2D
552 /** The x coordinate of the lower left corner. */
555 /** The y coordinate of the lower left corner. */
558 /** The width of the rectangle. */
561 /** The height of the rectangle. */
562 public double height
;
565 * Create a rectangle at (0,0) with width 0 and height 0.
572 * Create a rectangle with the given values.
574 * @param x the x coordinate
575 * @param y the y coordinate
577 * @param h the height
579 public Double(double x
, double y
, double w
, double h
)
588 * Return the X coordinate.
590 * @return the value of x
598 * Return the Y coordinate.
600 * @return the value of y
610 * @return the value of width
612 public double getWidth()
620 * @return the value of height
622 public double getHeight()
628 * Test if the rectangle is empty.
630 * @return true if width or height is not positive
632 public boolean isEmpty()
634 return width
<= 0 || height
<= 0;
638 * Set the contents of this rectangle to those specified.
640 * @param x the x coordinate
641 * @param y the y coordinate
643 * @param h the height
645 public void setRect(double x
, double y
, double w
, double h
)
654 * Set the contents of this rectangle to those specified.
656 * @param r the rectangle to copy
657 * @throws NullPointerException if r is null
659 public void setRect(Rectangle2D r
)
663 width
= r
.getWidth();
664 height
= r
.getHeight();
668 * Determine where the point lies with respect to this rectangle. The
669 * result will be the binary OR of the appropriate bit masks.
671 * @param x the x coordinate to check
672 * @param y the y coordinate to check
673 * @return the binary OR of the result
680 public int outcode(double x
, double y
)
684 result
|= OUT_LEFT
| OUT_RIGHT
;
687 else if (x
> this.x
+ width
)
690 result
|= OUT_BOTTOM
| OUT_TOP
;
691 else if (y
< this.y
) // Remember that +y heads top-to-bottom.
693 else if (y
> this.y
+ height
)
694 result
|= OUT_BOTTOM
;
699 * Returns the bounds of this rectangle. A pretty useless method, as this
700 * is already a rectangle.
702 * @return a copy of this rectangle
704 public Rectangle2D
getBounds2D()
706 return new Double(x
, y
, width
, height
);
710 * Return a new rectangle which is the intersection of this and the given
711 * one. The result will be empty if there is no intersection.
713 * @param r the rectangle to be intersected
714 * @return the intersection
715 * @throws NullPointerException if r is null
717 public Rectangle2D
createIntersection(Rectangle2D r
)
719 Double res
= new Double();
720 intersect(this, r
, res
);
725 * Return a new rectangle which is the union of this and the given one.
727 * @param r the rectangle to be merged
729 * @throws NullPointerException if r is null
731 public Rectangle2D
createUnion(Rectangle2D r
)
733 Double res
= new Double();
739 * Returns a string representation of this rectangle. This is in the form
740 * <code>getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width
741 * + ",h=" + height + ']'</code>.
743 * @return a string representation of this rectangle
745 public String
toString()
747 return getClass().getName() + "[x=" + x
+ ",y=" + y
+ ",w=" + width
748 + ",h=" + height
+ ']';
753 * This class defines a rectangle in <code>float</code> precision.
755 * @author Eric Blake (ebb9@email.byu.edu)
757 * @status updated to 1.4
759 public static class Float
extends Rectangle2D
761 /** The x coordinate of the lower left corner. */
764 /** The y coordinate of the lower left corner. */
767 /** The width of the rectangle. */
770 /** The height of the rectangle. */
774 * Create a rectangle at (0,0) with width 0 and height 0.
781 * Create a rectangle with the given values.
783 * @param x the x coordinate
784 * @param y the y coordinate
786 * @param h the height
788 public Float(float x
, float y
, float w
, float h
)
797 * Create a rectangle with the given values.
799 * @param x the x coordinate
800 * @param y the y coordinate
802 * @param h the height
804 Float(double x
, double y
, double w
, double h
)
813 * Return the X coordinate.
815 * @return the value of x
823 * Return the Y coordinate.
825 * @return the value of y
835 * @return the value of width
837 public double getWidth()
845 * @return the value of height
847 public double getHeight()
853 * Test if the rectangle is empty.
855 * @return true if width or height is not positive
857 public boolean isEmpty()
859 return width
<= 0 || height
<= 0;
863 * Set the contents of this rectangle to those specified.
865 * @param x the x coordinate
866 * @param y the y coordinate
868 * @param h the height
870 public void setRect(float x
, float y
, float w
, float h
)
879 * Set the contents of this rectangle to those specified.
881 * @param x the x coordinate
882 * @param y the y coordinate
884 * @param h the height
886 public void setRect(double x
, double y
, double w
, double h
)
895 * Set the contents of this rectangle to those specified.
897 * @param r the rectangle to copy
898 * @throws NullPointerException if r is null
900 public void setRect(Rectangle2D r
)
902 x
= (float) r
.getX();
903 y
= (float) r
.getY();
904 width
= (float) r
.getWidth();
905 height
= (float) r
.getHeight();
909 * Determine where the point lies with respect to this rectangle. The
910 * result will be the binary OR of the appropriate bit masks.
912 * @param x the x coordinate to check
913 * @param y the y coordinate to check
914 * @return the binary OR of the result
921 public int outcode(double x
, double y
)
925 result
|= OUT_LEFT
| OUT_RIGHT
;
928 else if (x
> this.x
+ width
)
931 result
|= OUT_BOTTOM
| OUT_TOP
;
932 else if (y
< this.y
) // Remember that +y heads top-to-bottom.
934 else if (y
> this.y
+ height
)
935 result
|= OUT_BOTTOM
;
940 * Returns the bounds of this rectangle. A pretty useless method, as this
941 * is already a rectangle.
943 * @return a copy of this rectangle
945 public Rectangle2D
getBounds2D()
947 return new Float(x
, y
, width
, height
);
951 * Return a new rectangle which is the intersection of this and the given
952 * one. The result will be empty if there is no intersection.
954 * @param r the rectangle to be intersected
955 * @return the intersection
956 * @throws NullPointerException if r is null
958 public Rectangle2D
createIntersection(Rectangle2D r
)
960 Float res
= new Float();
961 intersect(this, r
, res
);
966 * Return a new rectangle which is the union of this and the given one.
968 * @param r the rectangle to be merged
970 * @throws NullPointerException if r is null
972 public Rectangle2D
createUnion(Rectangle2D r
)
974 Float res
= new Float();
980 * Returns a string representation of this rectangle. This is in the form
981 * <code>getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width
982 * + ",h=" + height + ']'</code>.
984 * @return a string representation of this rectangle
986 public String
toString()
988 return getClass().getName() + "[x=" + x
+ ",y=" + y
+ ",w=" + width
989 + ",h=" + height
+ ']';