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>, <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 }