001    /*
002     * File: GRectangle.java
003     * ---------------------
004     * This file implements the GRectangle class, which is a real-valued version
005     * of the java.awt.GRectangle class.
006     *
007     * Copyright 2004 by the ACM Java Task Force
008     * This work is licensed under the Creative Commons
009     * Attribution-NonCommercial-NoDerivs License.
010     * To view a copy of this license, visit
011     *
012     *      http://creativecommons.org/licenses/by-nc-nd/2.0/
013     *
014     * or send a letter to
015     *
016     *      Creative Commons
017     *      559 Nathan Abbott Way
018     *      Stanford, CA  94305
019     *      USA
020     *
021     * This source code is released as part of the first public draft
022     * of the ACM Java Task Force report, dated February 1, 2005.
023     * The classes and methods implemented by these source files
024     * remains under development and is subject to change.
025     */
026    
027    package acm.graphics;
028    
029    import java.awt.*;
030    
031    /* Class: GRectangle */
032    /**
033     * This class is a double-precision version of the <code>Rectangle</code> class
034     * in <code>java.awt</code>.
035     */
036    public class GRectangle {
037    
038    /* Constructor: GRectangle() */
039    /**
040     * Constructs a new empty <code>GRectangle</code>.
041     *
042     * @usage rect = new GRectangle();
043     */
044        public GRectangle() {
045            this(0, 0, 0, 0);
046        }
047    
048    /* Constructor: GRectangle(x, y, width, height) */
049    /**
050     * Constructs a new <code>GRectangle</code> with the specified coordinates and size.
051     *
052     * @usage rect = new GRectangle(x, y, width, height);
053     * @param x The x-coordinate of the rectangle
054     * @param y The y-coordinate of the rectangle
055     * @param width The width of the rectangle
056     * @param height The height of the rectangle
057     */
058        public GRectangle(double x, double y, double width, double height) {
059            xc = x;
060            yc = y;
061            myWidth = width;
062            myHeight = height;
063        }
064    
065    /* Constructor: GRectangle(width, height) */
066    /**
067     * Constructs a <code>GRectangle</code> at the origin with the specified width and height.
068     *
069     * @usage rect = new GRectangle(width, height);
070     * @param width The width of the rectangle
071     * @param height The height of the rectangle
072     */
073        public GRectangle(double width, double height) {
074            this(0, 0, width, height);
075        }
076    
077    /* Constructor: GRectangle(pt, size) */
078    /**
079     * Constructs a new <code>GRectangle</code> with the specified location and size.
080     *
081     * @usage rect = new GRectangle(pt, size);
082     * @param pt The location of the upper left corner of the rectangle
083     * @param size The dimensions of the rectangle
084     */
085        public GRectangle(GPoint pt, GDimension size) {
086            this(pt.getX(), pt.getY(), size.getWidth(), size.getHeight());
087        }
088    
089    /* Constructor: GRectangle(pt) */
090    /**
091     * Constructs a new empty <code>GRectangle</code> at the specified location.
092     *
093     * @usage rect = new GRectangle(pt);
094     * @param pt The location of the upper left corner of the rectangle
095     */
096        public GRectangle(GPoint pt) {
097            this(pt.getX(), pt.getY(), 0, 0);
098        }
099    
100    /* Constructor: GRectangle(size) */
101    /**
102     * Constructs a new <code>GRectangle</code> at the origin with the specified size.
103     *
104     * @usage rect = new GRectangle(size);
105     * @param size The dimensions of the rectangle
106     */
107        public GRectangle(GDimension size) {
108            this(0, 0, size.getWidth(), size.getHeight());
109        }
110    
111    /* Constructor: GRectangle(r) */
112    /**
113     * Constructs a new <code>GRectangle</code> from an existing one.
114     *
115     * @usage rect = new GRectangle(r);
116     * @param r The original rectangle
117     */
118        public GRectangle(GRectangle r) {
119            this(r.xc, r.yc, r.myWidth, r.myHeight);
120        }
121    
122    /* Method: getX() */
123    /**
124     * Returns the x coordinate of this <code>GRectangle</code>.
125     *
126     * @usage x = rect.getX();
127     * @return The x coordinate of this <code>GRectangle</code>
128     */
129        public double getX() {
130            return xc;
131        }
132    
133    /* Method: getY() */
134    /**
135     * Returns the y coordinate of this <code>GRectangle</code>.
136     *
137     * @usage y = rect.getY();
138     * @return The y coordinate of this <code>GRectangle</code>
139     */
140        public double getY() {
141            return yc;
142        }
143    
144    /* Method: getWidth() */
145    /**
146     * Returns the width of this <code>GDimension</code>.
147     *
148     * @usage width = rect.getWidth();
149     * @return The width of this <code>GDimension</code>
150     */
151        public double getWidth() {
152            return myWidth;
153        }
154    
155    /* Method: getHeight() */
156    /**
157     * Returns the height of this <code>GDimension</code>.
158     *
159     * @usage height = rect.getHeight();
160     * @return The height of this <code>GDimension</code>
161     */
162        public double getHeight() {
163            return myHeight;
164        }
165    
166    /* Method: setBounds(x, y, width, height) */
167    /**
168     * Sets the components of a <code>GRectangle</code> from the specified values.
169     *
170     * @usage rect.setBounds(x, y, width, height);
171     * @param x The x-coordinate of the rectangle
172     * @param y The y-coordinate of the rectangle
173     * @param width The width of the rectangle
174     * @param height The height of the rectangle
175     */
176        public void setBounds(double x, double y, double width, double height) {
177            xc = x;
178            yc = y;
179            myWidth = width;
180            myHeight = height;
181        }
182    
183    /* Method: setBounds(pt, size) */
184    /**
185     * Sets the components of a <code>GRectangle</code> from the specified location and size.
186     *
187     * @usage rect.setBounds(pt, size);
188     * @param pt The location of the upper left corner of the rectangle
189     * @param size The dimensions of the rectangle
190     */
191        public void setBounds(GPoint pt, GDimension size) {
192            setBounds(pt.getX(), pt.getY(), size.getWidth(), size.getHeight());
193        }
194    
195    /* Method: setBounds(bounds) */
196    /**
197     * Sets the bounds of one <code>GRectangle</code> equal to that of another.
198     *
199     * @usage rect.setBounds(bounds);
200     * @param bounds A <code>GRectangle</code> specifying the new bounds
201     */
202        public void setBounds(GRectangle bounds) {
203            setBounds(bounds.xc, bounds.yc, bounds.myWidth, bounds.myHeight);
204        }
205    
206    /* Method: getBounds() */
207    /**
208     * Returns a new <code>GRectangle</code> whose bounds are the same as this one.
209     *
210     * @usage r = rect.getBounds();
211     * @return A new rectangle with the same bounds
212     */
213        public GRectangle getBounds() {
214            return new GRectangle(this);
215        }
216    
217    /* Method: setLocation(x, y) */
218    /**
219     * Sets the location of the <code>GRectangle</code> to the specified <code>x</code>
220     * and <code>y</code> values.
221     *
222     * @usage rect.setLocation(x, y);
223     * @param x The new x-coordinate for the rectangle
224     * @param y The new y-coordinate for the rectangle
225     */
226        public void setLocation(double x, double y) {
227            xc = x;
228            yc = y;
229        }
230    
231    /* Method: setLocation(pt) */
232    /**
233     * Sets the location of the <code>GRectangle</code> to the specified point.
234     *
235     * @usage rect.setLocation(pt);
236     * @param pt The new location for the rectangle
237     */
238        public void setLocation(GPoint pt) {
239            setLocation(pt.getX(), pt.getY());
240        }
241    
242    /* Method: getLocation() */
243    /**
244     * Returns a new <code>GPoint</code> with the location of the rectangle.
245     *
246     * @usage pt = rect.getLocation();
247     * @return The location of the rectangle as a <code>GPoint</code>
248     */
249        public GPoint getLocation() {
250            return new GPoint(xc, yc);
251        }
252    
253    /* Method: translate(dx, dy) */
254    /**
255     * Adjusts the coordinates of a rectangle by the specified <code>dx</code> and
256     * <code>dy</code> offsets.
257     *
258     * @usage rect.translate(dx, dy);
259     * @param dx The change in the x direction (positive is rightward)
260     * @param dy The change in the y direction (positive is downward)
261     */
262        public void translate(double dx, double dy) {
263            xc += dx;
264            yc += dy;
265        }
266    
267    /* Method: setSize(width, height) */
268    /**
269     * Sets the size of the <code>GRectangle</code> to the specified values.
270     *
271     * @usage rect.setSize(width, height);
272     * @param width The new width of the rectangle
273     * @param height The new height of the rectangle
274     */
275        public void setSize(double width, double height) {
276            myWidth = width;
277            myHeight = height;
278        }
279    
280    /* Method: setSize(size) */
281    /**
282     * Sets the size of the <code>GRectangle</code> to the specified dimension.
283     *
284     * @usage rect.setSize(size);
285     * @param size The new dimensions of the rectangle
286     */
287        public void setSize(GDimension size) {
288            setSize(size.getWidth(), size.getHeight());
289        }
290    
291    /* Method: getSize() */
292    /**
293     * Returns a new <code>GDimension</code> object with the size of the <code>GRectangle</code>.
294     * @usage size = rect.getSize();
295     * @return The size of the rectangle as a <code>GDimension</code>
296     */
297        public GDimension getSize() {
298            return new GDimension(myWidth, myHeight);
299        }
300    
301    /* Method: grow(dx, dy) */
302    /**
303     * Adjusts the edges of a rectangle by the specified <code>dx</code> and <code>dy</code>
304     * offsets along each of its borders.
305     *
306     * @usage rect.grow(dx, dy);
307     * @param dx The offset to extend each of the left and right borders
308     * @param dy The offset to extend each of the top and bottom borders
309     */
310        public void grow(double dx, double dy) {
311            xc -= dx;
312            yc -= dy;
313            myWidth += 2 * dx;
314            myHeight += 2 * dy;
315        }
316    
317    /* Method: isEmpty() */
318    /**
319     * Returns <code>true</code> if the rectangle is empty.
320     *
321     * @usage if (rect.isEmpty()) . . .
322     * @return <code>true</code> if the rectangle is empty, and <code>false</code> otherwise
323     */
324        public boolean isEmpty() {
325            return myWidth <= 0 || myHeight <= 0;
326        }
327    
328    /* Method: contains(x, y) */
329    /**
330     * Returns <code>true</code> if the <code>GRectangle</code> contains the specified point.
331     *
332     * @usage if (rect.contains(x, y)) . . .
333     * @param x The x-coordinate of the point being tested
334     * @param y The y-coordinate of the point being tested
335     * @return <code>true</code> if the rectangle contains (<code>x</code>,&nbsp;<code>y</code>),
336     *         and <code>false</code> otherwise
337     */
338        public boolean contains(double x, double y) {
339            return x >= xc && y >= yc && x < xc + myWidth && y < yc + myHeight;
340        }
341    
342    /* Method: contains(pt) */
343    /**
344     * Returns <code>true</code> if the <code>GRectangle</code> contains the specified point.
345     *
346     * @usage if (rect.contains(pt)) . . .
347     * @param pt The point being tested
348     * @return <code>true</code> if the rectangle contains <code>pt</code>,
349     *         and <code>false</code> otherwise
350     */
351        public boolean contains(GPoint pt) {
352            return contains(pt.getX(), pt.getY());
353        }
354    
355    /* Method: intersects(r) */
356    /**
357     * Returns <code>true</code> if <code>r1</code> and <code>r2</code> have a nonempty
358     * intersection.
359     *
360     * @usage if (r1.intersects(r2)) . . .
361     * @param r2 A second rectangle
362     * @return <code>true</code> if the two rectangles intersect, and <code>false</code> otherwise
363     */
364        public boolean intersects(GRectangle r) {
365            if (xc > r.xc + r.myWidth) return false;
366            if (yc > r.yc + r.myHeight) return false;
367            if (r.xc > xc + myWidth) return false;
368            if (r.yc > yc + myHeight) return false;
369            return true;
370        }
371    
372    /* Method: intersection(r) */
373    /**
374     * Returns the largest rectangle that is contained in both
375     * <code>r1</code> and <code>r2</code>.
376     *
377     * @usage r3 = r1.intersection(r2);
378     * @param r2 A second rectangle
379     * @return The intersection of this rectangle and <code>r2</code>
380     */
381        public GRectangle intersection(GRectangle r) {
382            double x1 = Math.max(xc, r.xc);
383            double y1 = Math.max(yc, r.yc);
384            double x2 = Math.min(xc + myWidth, r.xc + r.myWidth);
385            double y2 = Math.min(yc + myHeight, r.yc + r.myHeight);
386            return new GRectangle(x1, y1, x2 - x1, y2 - y1);
387        }
388    
389    /* Method: union(r) */
390    /**
391     * Returns the smallest rectangle that contains both
392     * <code>r1</code> and <code>r2</code>.
393     *
394     * @usage r3 = r1.union(r2);
395     * @param r2 A second rectangle
396     * @return The union of this rectangle and <code>r2</code>
397     */
398        public GRectangle union(GRectangle r) {
399            if (isEmpty()) return new GRectangle(r);
400            if (r.isEmpty()) return new GRectangle(this);
401            double x1 = Math.min(xc, r.xc);
402            double y1 = Math.min(yc, r.yc);
403            double x2 = Math.max(xc + myWidth, r.xc + r.myWidth);
404            double y2 = Math.max(yc + myHeight, r.yc + r.myHeight);
405            return new GRectangle(x1, y1, x2 - x1, y2 - y1);
406        }
407    
408    /* Method: add(r) */
409    /**
410     * Adjusts the bounds of the current <code>GRectangle</code> so that it contains
411     * the rectangle represented by the argument.
412     *
413     * @usage rect.add(r);
414     * @param r A new rectangle to include in this one
415     */
416        public void add(GRectangle r) {
417            if (r.isEmpty()) return;
418            if (isEmpty()) {
419                setBounds(r);
420                return;
421            }
422            double x2 = Math.max(xc + myWidth, r.xc + r.myWidth);
423            double y2 = Math.max(yc + myHeight, r.yc + r.myHeight);
424            xc = Math.min(r.xc, xc);
425            yc = Math.min(r.yc, yc);
426            myWidth = x2 - xc;
427            myHeight = y2 - yc;
428        }
429    
430    /* Method: add(x, y) */
431    /**
432     * Adds the specified point to the rectangle.
433     *
434     * @usage rect.add(x, y);
435     * @param x The x coordinate of the new point
436     * @param y The y coordinate of the new point
437     */
438        public void add(double x, double y) {
439            if (isEmpty()) {
440                setBounds(x, y, 0, 0);
441                return;
442            }
443            double x2 = Math.max(x + myWidth, x);
444            double y2 = Math.max(y + myHeight, y);
445            xc = Math.min(x, xc);
446            yc = Math.min(y, yc);
447            myWidth = x2 - xc;
448            myHeight = y2 - yc;
449        }
450    
451    /* Method: toRectangle() */
452    /**
453     * Converts this <code>GRectangle</code> to the nearest integer-based <code>Rectangle</code>.
454     *
455     * @usage size = dim.toRectangle();
456     * @return The closest integer-based <code>Rectangle</code>
457     */
458        public Rectangle toRectangle() {
459            return new Rectangle((int) Math.round(xc), (int) Math.round(yc),
460                                 (int) Math.round(myWidth), (int) Math.round(myHeight));
461        }
462    
463    /* Method: hashCode() */
464    /**
465     * Returns an integer hash code for the rectangle.  The hash code for a
466     * <code>GRectangle</code> is constructed from the hash codes from the
467     * <code>float</code> values of the coordinates, which are the ones used in the
468     * <code>equals</code> method.
469     *
470     * @usage hash = rect.hashCode();
471     * @return The hash code for this rectangle
472     * @noshow
473     */
474        public int hashCode() {
475            return new Float((float) xc).hashCode() ^ new Float((float) yc).hashCode()
476                  ^ new Float((float) myWidth).hashCode() ^ new Float((float) myHeight).hashCode();
477        }
478    
479    /* Method: equals(obj) */
480    /**
481     * Tests whether two <code>GRectangle</code> objects are equal.
482     * Because floating-point values are inexact, this method checks for
483     * equality by comparing the <code>float</code> values (rather than the
484     * <code>double</code> values) of the coordinates.
485     *
486     * @usage if (rect.equals(obj)) . . .
487     * @param obj Any object
488     * @return <code>true</code> if the <code>obj</code> is a <code>GRectangle</code>
489     *         equal to this one, and <code>false</code> otherwise
490     * @noshow
491     */
492        public boolean equals(Object obj) {
493            if (!(obj instanceof GRectangle)) return false;
494            GRectangle r = (GRectangle) obj;
495            if ((float) xc != (float) r.xc) return false;
496            if ((float) yc != (float) r.yc) return false;
497            if ((float) myWidth != (float) r.myWidth) return false;
498            if ((float) myHeight != (float) r.myHeight) return false;
499            return true;
500        }
501    
502    /* Method: toString() */
503    /**
504     * Converts this <code>GRectangle</code> to its string representation.
505     *
506     * @usage str = rect.toString();
507     * @return A string representation of this rectangle
508     * @noshow
509     */
510        public String toString() {
511            return "[" + (float) xc + ", " + (float) yc + ", "
512                          + (float) myWidth + "x" + (float) myHeight + "]";
513        }
514    
515    /* Private instance variables */
516    
517        private double xc;
518        private double yc;
519        private double myWidth;
520        private double myHeight;
521    }