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