001 /*
002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.awt.geom;
027
028 import java.io.Serializable;
029
030 /**
031 * The <code>RoundRectangle2D</code> class defines a rectangle with
032 * rounded corners defined by a location {@code (x,y)}, a
033 * dimension {@code (w x h)}, and the width and height of an arc
034 * with which to round the corners.
035 * <p>
036 * This class is the abstract superclass for all objects that
037 * store a 2D rounded rectangle.
038 * The actual storage representation of the coordinates is left to
039 * the subclass.
040 *
041 * @version 1.29, 05/05/07
042 * @author Jim Graham
043 * @since 1.2
044 */
045 public abstract class RoundRectangle2D extends RectangularShape {
046
047 /**
048 * The <code>Float</code> class defines a rectangle with rounded
049 * corners all specified in <code>float</code> coordinates.
050 * @since 1.2
051 */
052 public static class Float extends RoundRectangle2D implements
053 Serializable {
054 /**
055 * The X coordinate of this <code>RoundRectangle2D</code>.
056 * @since 1.2
057 * @serial
058 */
059 public float x;
060
061 /**
062 * The Y coordinate of this <code>RoundRectangle2D</code>.
063 * @since 1.2
064 * @serial
065 */
066 public float y;
067
068 /**
069 * The width of this <code>RoundRectangle2D</code>.
070 * @since 1.2
071 * @serial
072 */
073 public float width;
074
075 /**
076 * The height of this <code>RoundRectangle2D</code>.
077 * @since 1.2
078 * @serial
079 */
080 public float height;
081
082 /**
083 * The width of the arc that rounds off the corners.
084 * @since 1.2
085 * @serial
086 */
087 public float arcwidth;
088
089 /**
090 * The height of the arc that rounds off the corners.
091 * @since 1.2
092 * @serial
093 */
094 public float archeight;
095
096 /**
097 * Constructs a new <code>RoundRectangle2D</code>, initialized to
098 * location (0.0, 0.0), size (0.0, 0.0), and corner arcs
099 * of radius 0.0.
100 * @since 1.2
101 */
102 public Float() {
103 }
104
105 /**
106 * Constructs and initializes a <code>RoundRectangle2D</code>
107 * from the specified <code>float</code> coordinates.
108 *
109 * @param x the X coordinate of the newly
110 * constructed <code>RoundRectangle2D</code>
111 * @param y the Y coordinate of the newly
112 * constructed <code>RoundRectangle2D</code>
113 * @param w the width to which to set the newly
114 * constructed <code>RoundRectangle2D</code>
115 * @param h the height to which to set the newly
116 * constructed <code>RoundRectangle2D</code>
117 * @param arcw the width of the arc to use to round off the
118 * corners of the newly constructed
119 * <code>RoundRectangle2D</code>
120 * @param arch the height of the arc to use to round off the
121 * corners of the newly constructed
122 * <code>RoundRectangle2D</code>
123 * @since 1.2
124 */
125 public Float(float x, float y, float w, float h, float arcw,
126 float arch) {
127 setRoundRect(x, y, w, h, arcw, arch);
128 }
129
130 /**
131 * {@inheritDoc}
132 * @since 1.2
133 */
134 public double getX() {
135 return (double) x;
136 }
137
138 /**
139 * {@inheritDoc}
140 * @since 1.2
141 */
142 public double getY() {
143 return (double) y;
144 }
145
146 /**
147 * {@inheritDoc}
148 * @since 1.2
149 */
150 public double getWidth() {
151 return (double) width;
152 }
153
154 /**
155 * {@inheritDoc}
156 * @since 1.2
157 */
158 public double getHeight() {
159 return (double) height;
160 }
161
162 /**
163 * {@inheritDoc}
164 * @since 1.2
165 */
166 public double getArcWidth() {
167 return (double) arcwidth;
168 }
169
170 /**
171 * {@inheritDoc}
172 * @since 1.2
173 */
174 public double getArcHeight() {
175 return (double) archeight;
176 }
177
178 /**
179 * {@inheritDoc}
180 * @since 1.2
181 */
182 public boolean isEmpty() {
183 return (width <= 0.0f) || (height <= 0.0f);
184 }
185
186 /**
187 * Sets the location, size, and corner radii of this
188 * <code>RoundRectangle2D</code> to the specified
189 * <code>float</code> values.
190 *
191 * @param x the X coordinate to which to set the
192 * location of this <code>RoundRectangle2D</code>
193 * @param y the Y coordinate to which to set the
194 * location of this <code>RoundRectangle2D</code>
195 * @param w the width to which to set this
196 * <code>RoundRectangle2D</code>
197 * @param h the height to which to set this
198 * <code>RoundRectangle2D</code>
199 * @param arcw the width to which to set the arc of this
200 * <code>RoundRectangle2D</code>
201 * @param arch the height to which to set the arc of this
202 * <code>RoundRectangle2D</code>
203 * @since 1.2
204 */
205 public void setRoundRect(float x, float y, float w, float h,
206 float arcw, float arch) {
207 this .x = x;
208 this .y = y;
209 this .width = w;
210 this .height = h;
211 this .arcwidth = arcw;
212 this .archeight = arch;
213 }
214
215 /**
216 * {@inheritDoc}
217 * @since 1.2
218 */
219 public void setRoundRect(double x, double y, double w,
220 double h, double arcw, double arch) {
221 this .x = (float) x;
222 this .y = (float) y;
223 this .width = (float) w;
224 this .height = (float) h;
225 this .arcwidth = (float) arcw;
226 this .archeight = (float) arch;
227 }
228
229 /**
230 * {@inheritDoc}
231 * @since 1.2
232 */
233 public void setRoundRect(RoundRectangle2D rr) {
234 this .x = (float) rr.getX();
235 this .y = (float) rr.getY();
236 this .width = (float) rr.getWidth();
237 this .height = (float) rr.getHeight();
238 this .arcwidth = (float) rr.getArcWidth();
239 this .archeight = (float) rr.getArcHeight();
240 }
241
242 /**
243 * {@inheritDoc}
244 * @since 1.2
245 */
246 public Rectangle2D getBounds2D() {
247 return new Rectangle2D.Float(x, y, width, height);
248 }
249
250 /*
251 * JDK 1.6 serialVersionUID
252 */
253 private static final long serialVersionUID = -3423150618393866922L;
254 }
255
256 /**
257 * The <code>Double</code> class defines a rectangle with rounded
258 * corners all specified in <code>double</code> coordinates.
259 * @since 1.2
260 */
261 public static class Double extends RoundRectangle2D implements
262 Serializable {
263 /**
264 * The X coordinate of this <code>RoundRectangle2D</code>.
265 * @since 1.2
266 * @serial
267 */
268 public double x;
269
270 /**
271 * The Y coordinate of this <code>RoundRectangle2D</code>.
272 * @since 1.2
273 * @serial
274 */
275 public double y;
276
277 /**
278 * The width of this <code>RoundRectangle2D</code>.
279 * @since 1.2
280 * @serial
281 */
282 public double width;
283
284 /**
285 * The height of this <code>RoundRectangle2D</code>.
286 * @since 1.2
287 * @serial
288 */
289 public double height;
290
291 /**
292 * The width of the arc that rounds off the corners.
293 * @since 1.2
294 * @serial
295 */
296 public double arcwidth;
297
298 /**
299 * The height of the arc that rounds off the corners.
300 * @since 1.2
301 * @serial
302 */
303 public double archeight;
304
305 /**
306 * Constructs a new <code>RoundRectangle2D</code>, initialized to
307 * location (0.0, 0.0), size (0.0, 0.0), and corner arcs
308 * of radius 0.0.
309 * @since 1.2
310 */
311 public Double() {
312 }
313
314 /**
315 * Constructs and initializes a <code>RoundRectangle2D</code>
316 * from the specified <code>double</code> coordinates.
317 *
318 * @param x the X coordinate of the newly
319 * constructed <code>RoundRectangle2D</code>
320 * @param y the Y coordinate of the newly
321 * constructed <code>RoundRectangle2D</code>
322 * @param w the width to which to set the newly
323 * constructed <code>RoundRectangle2D</code>
324 * @param h the height to which to set the newly
325 * constructed <code>RoundRectangle2D</code>
326 * @param arcw the width of the arc to use to round off the
327 * corners of the newly constructed
328 * <code>RoundRectangle2D</code>
329 * @param arch the height of the arc to use to round off the
330 * corners of the newly constructed
331 * <code>RoundRectangle2D</code>
332 * @since 1.2
333 */
334 public Double(double x, double y, double w, double h,
335 double arcw, double arch) {
336 setRoundRect(x, y, w, h, arcw, arch);
337 }
338
339 /**
340 * {@inheritDoc}
341 * @since 1.2
342 */
343 public double getX() {
344 return x;
345 }
346
347 /**
348 * {@inheritDoc}
349 * @since 1.2
350 */
351 public double getY() {
352 return y;
353 }
354
355 /**
356 * {@inheritDoc}
357 * @since 1.2
358 */
359 public double getWidth() {
360 return width;
361 }
362
363 /**
364 * {@inheritDoc}
365 * @since 1.2
366 */
367 public double getHeight() {
368 return height;
369 }
370
371 /**
372 * {@inheritDoc}
373 * @since 1.2
374 */
375 public double getArcWidth() {
376 return arcwidth;
377 }
378
379 /**
380 * {@inheritDoc}
381 * @since 1.2
382 */
383 public double getArcHeight() {
384 return archeight;
385 }
386
387 /**
388 * {@inheritDoc}
389 * @since 1.2
390 */
391 public boolean isEmpty() {
392 return (width <= 0.0f) || (height <= 0.0f);
393 }
394
395 /**
396 * {@inheritDoc}
397 * @since 1.2
398 */
399 public void setRoundRect(double x, double y, double w,
400 double h, double arcw, double arch) {
401 this .x = x;
402 this .y = y;
403 this .width = w;
404 this .height = h;
405 this .arcwidth = arcw;
406 this .archeight = arch;
407 }
408
409 /**
410 * {@inheritDoc}
411 * @since 1.2
412 */
413 public void setRoundRect(RoundRectangle2D rr) {
414 this .x = rr.getX();
415 this .y = rr.getY();
416 this .width = rr.getWidth();
417 this .height = rr.getHeight();
418 this .arcwidth = rr.getArcWidth();
419 this .archeight = rr.getArcHeight();
420 }
421
422 /**
423 * {@inheritDoc}
424 * @since 1.2
425 */
426 public Rectangle2D getBounds2D() {
427 return new Rectangle2D.Double(x, y, width, height);
428 }
429
430 /*
431 * JDK 1.6 serialVersionUID
432 */
433 private static final long serialVersionUID = 1048939333485206117L;
434 }
435
436 /**
437 * This is an abstract class that cannot be instantiated directly.
438 * Type-specific implementation subclasses are available for
439 * instantiation and provide a number of formats for storing
440 * the information necessary to satisfy the various accessor
441 * methods below.
442 *
443 * @see java.awt.geom.RoundRectangle2D.Float
444 * @see java.awt.geom.RoundRectangle2D.Double
445 * @since 1.2
446 */
447 protected RoundRectangle2D() {
448 }
449
450 /**
451 * Gets the width of the arc that rounds off the corners.
452 * @return the width of the arc that rounds off the corners
453 * of this <code>RoundRectangle2D</code>.
454 * @since 1.2
455 */
456 public abstract double getArcWidth();
457
458 /**
459 * Gets the height of the arc that rounds off the corners.
460 * @return the height of the arc that rounds off the corners
461 * of this <code>RoundRectangle2D</code>.
462 * @since 1.2
463 */
464 public abstract double getArcHeight();
465
466 /**
467 * Sets the location, size, and corner radii of this
468 * <code>RoundRectangle2D</code> to the specified
469 * <code>double</code> values.
470 *
471 * @param x the X coordinate to which to set the
472 * location of this <code>RoundRectangle2D</code>
473 * @param y the Y coordinate to which to set the
474 * location of this <code>RoundRectangle2D</code>
475 * @param w the width to which to set this
476 * <code>RoundRectangle2D</code>
477 * @param h the height to which to set this
478 * <code>RoundRectangle2D</code>
479 * @param arcWidth the width to which to set the arc of this
480 * <code>RoundRectangle2D</code>
481 * @param arcHeight the height to which to set the arc of this
482 * <code>RoundRectangle2D</code>
483 * @since 1.2
484 */
485 public abstract void setRoundRect(double x, double y, double w,
486 double h, double arcWidth, double arcHeight);
487
488 /**
489 * Sets this <code>RoundRectangle2D</code> to be the same as the
490 * specified <code>RoundRectangle2D</code>.
491 * @param rr the specified <code>RoundRectangle2D</code>
492 * @since 1.2
493 */
494 public void setRoundRect(RoundRectangle2D rr) {
495 setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr
496 .getHeight(), rr.getArcWidth(), rr.getArcHeight());
497 }
498
499 /**
500 * {@inheritDoc}
501 * @since 1.2
502 */
503 public void setFrame(double x, double y, double w, double h) {
504 setRoundRect(x, y, w, h, getArcWidth(), getArcHeight());
505 }
506
507 /**
508 * {@inheritDoc}
509 * @since 1.2
510 */
511 public boolean contains(double x, double y) {
512 if (isEmpty()) {
513 return false;
514 }
515 double rrx0 = getX();
516 double rry0 = getY();
517 double rrx1 = rrx0 + getWidth();
518 double rry1 = rry0 + getHeight();
519 // Check for trivial rejection - point is outside bounding rectangle
520 if (x < rrx0 || y < rry0 || x >= rrx1 || y >= rry1) {
521 return false;
522 }
523 double aw = Math.min(getWidth(), Math.abs(getArcWidth())) / 2.0;
524 double ah = Math.min(getHeight(), Math.abs(getArcHeight())) / 2.0;
525 // Check which corner point is in and do circular containment
526 // test - otherwise simple acceptance
527 if (x >= (rrx0 += aw) && x < (rrx0 = rrx1 - aw)) {
528 return true;
529 }
530 if (y >= (rry0 += ah) && y < (rry0 = rry1 - ah)) {
531 return true;
532 }
533 x = (x - rrx0) / aw;
534 y = (y - rry0) / ah;
535 return (x * x + y * y <= 1.0);
536 }
537
538 private int classify(double coord, double left, double right,
539 double arcsize) {
540 if (coord < left) {
541 return 0;
542 } else if (coord < left + arcsize) {
543 return 1;
544 } else if (coord < right - arcsize) {
545 return 2;
546 } else if (coord < right) {
547 return 3;
548 } else {
549 return 4;
550 }
551 }
552
553 /**
554 * {@inheritDoc}
555 * @since 1.2
556 */
557 public boolean intersects(double x, double y, double w, double h) {
558 if (isEmpty() || w <= 0 || h <= 0) {
559 return false;
560 }
561 double rrx0 = getX();
562 double rry0 = getY();
563 double rrx1 = rrx0 + getWidth();
564 double rry1 = rry0 + getHeight();
565 // Check for trivial rejection - bounding rectangles do not intersect
566 if (x + w <= rrx0 || x >= rrx1 || y + h <= rry0 || y >= rry1) {
567 return false;
568 }
569 double aw = Math.min(getWidth(), Math.abs(getArcWidth())) / 2.0;
570 double ah = Math.min(getHeight(), Math.abs(getArcHeight())) / 2.0;
571 int x0class = classify(x, rrx0, rrx1, aw);
572 int x1class = classify(x + w, rrx0, rrx1, aw);
573 int y0class = classify(y, rry0, rry1, ah);
574 int y1class = classify(y + h, rry0, rry1, ah);
575 // Trivially accept if any point is inside inner rectangle
576 if (x0class == 2 || x1class == 2 || y0class == 2
577 || y1class == 2) {
578 return true;
579 }
580 // Trivially accept if either edge spans inner rectangle
581 if ((x0class < 2 && x1class > 2)
582 || (y0class < 2 && y1class > 2)) {
583 return true;
584 }
585 // Since neither edge spans the center, then one of the corners
586 // must be in one of the rounded edges. We detect this case if
587 // a [xy]0class is 3 or a [xy]1class is 1. One of those two cases
588 // must be true for each direction.
589 // We now find a "nearest point" to test for being inside a rounded
590 // corner.
591 x = (x1class == 1) ? (x = x + w - (rrx0 + aw)) : (x = x
592 - (rrx1 - aw));
593 y = (y1class == 1) ? (y = y + h - (rry0 + ah)) : (y = y
594 - (rry1 - ah));
595 x = x / aw;
596 y = y / ah;
597 return (x * x + y * y <= 1.0);
598 }
599
600 /**
601 * {@inheritDoc}
602 * @since 1.2
603 */
604 public boolean contains(double x, double y, double w, double h) {
605 if (isEmpty() || w <= 0 || h <= 0) {
606 return false;
607 }
608 return (contains(x, y) && contains(x + w, y)
609 && contains(x, y + h) && contains(x + w, y + h));
610 }
611
612 /**
613 * Returns an iteration object that defines the boundary of this
614 * <code>RoundRectangle2D</code>.
615 * The iterator for this class is multi-threaded safe, which means
616 * that this <code>RoundRectangle2D</code> class guarantees that
617 * modifications to the geometry of this <code>RoundRectangle2D</code>
618 * object do not affect any iterations of that geometry that
619 * are already in process.
620 * @param at an optional <code>AffineTransform</code> to be applied to
621 * the coordinates as they are returned in the iteration, or
622 * <code>null</code> if untransformed coordinates are desired
623 * @return the <code>PathIterator</code> object that returns the
624 * geometry of the outline of this
625 * <code>RoundRectangle2D</code>, one segment at a time.
626 * @since 1.2
627 */
628 public PathIterator getPathIterator(AffineTransform at) {
629 return new RoundRectIterator(this , at);
630 }
631
632 /**
633 * Returns the hashcode for this <code>RoundRectangle2D</code>.
634 * @return the hashcode for this <code>RoundRectangle2D</code>.
635 * @since 1.6
636 */
637 public int hashCode() {
638 long bits = java.lang.Double.doubleToLongBits(getX());
639 bits += java.lang.Double.doubleToLongBits(getY()) * 37;
640 bits += java.lang.Double.doubleToLongBits(getWidth()) * 43;
641 bits += java.lang.Double.doubleToLongBits(getHeight()) * 47;
642 bits += java.lang.Double.doubleToLongBits(getArcWidth()) * 53;
643 bits += java.lang.Double.doubleToLongBits(getArcHeight()) * 59;
644 return (((int) bits) ^ ((int) (bits >> 32)));
645 }
646
647 /**
648 * Determines whether or not the specified <code>Object</code> is
649 * equal to this <code>RoundRectangle2D</code>. The specified
650 * <code>Object</code> is equal to this <code>RoundRectangle2D</code>
651 * if it is an instance of <code>RoundRectangle2D</code> and if its
652 * location, size, and corner arc dimensions are the same as this
653 * <code>RoundRectangle2D</code>.
654 * @param obj an <code>Object</code> to be compared with this
655 * <code>RoundRectangle2D</code>.
656 * @return <code>true</code> if <code>obj</code> is an instance
657 * of <code>RoundRectangle2D</code> and has the same values;
658 * <code>false</code> otherwise.
659 * @since 1.6
660 */
661 public boolean equals(Object obj) {
662 if (obj == this ) {
663 return true;
664 }
665 if (obj instanceof RoundRectangle2D) {
666 RoundRectangle2D rr2d = (RoundRectangle2D) obj;
667 return ((getX() == rr2d.getX()) && (getY() == rr2d.getY())
668 && (getWidth() == rr2d.getWidth())
669 && (getHeight() == rr2d.getHeight())
670 && (getArcWidth() == rr2d.getArcWidth()) && (getArcHeight() == rr2d
671 .getArcHeight()));
672 }
673 return false;
674 }
675 }
|