1 /*
2  * Copyright (c) 1997, 2006, Oracle and/or its affiliates. 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package java.awt.geom;
27 
28 import java.io.Serializable;
29 
30 /**
31  * The {@code RoundRectangle2D} 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  */
44 public abstract class RoundRectangle2D extends RectangularShape {
45 
46     /**
47      * The {@code Float} class defines a rectangle with rounded
48      * corners all specified in {@code float} 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}.
56          * @since 1.2
57          * @serial
58          */
59         public float x;
60 
61         /**
62          * The Y coordinate of this {@code RoundRectangle2D}.
63          * @since 1.2
64          * @serial
65          */
66         public float y;
67 
68         /**
69          * The width of this {@code RoundRectangle2D}.
70          * @since 1.2
71          * @serial
72          */
73         public float width;
74 
75         /**
76          * The height of this {@code RoundRectangle2D}.
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}, 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          */
Float()102         public Float() {
103         }
104 
105         /**
106          * Constructs and initializes a {@code RoundRectangle2D}
107          * from the specified {@code float} coordinates.
108          *
109          * @param x the X coordinate of the newly
110          *          constructed {@code RoundRectangle2D}
111          * @param y the Y coordinate of the newly
112          *          constructed {@code RoundRectangle2D}
113          * @param w the width to which to set the newly
114          *          constructed {@code RoundRectangle2D}
115          * @param h the height to which to set the newly
116          *          constructed {@code RoundRectangle2D}
117          * @param arcw the width of the arc to use to round off the
118          *             corners of the newly constructed
119          *             {@code RoundRectangle2D}
120          * @param arch the height of the arc to use to round off the
121          *             corners of the newly constructed
122          *             {@code RoundRectangle2D}
123          * @since 1.2
124          */
Float(float x, float y, float w, float h, float arcw, float arch)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          */
getX()135         public double getX() {
136             return (double) x;
137         }
138 
139         /**
140          * {@inheritDoc}
141          * @since 1.2
142          */
getY()143         public double getY() {
144             return (double) y;
145         }
146 
147         /**
148          * {@inheritDoc}
149          * @since 1.2
150          */
getWidth()151         public double getWidth() {
152             return (double) width;
153         }
154 
155         /**
156          * {@inheritDoc}
157          * @since 1.2
158          */
getHeight()159         public double getHeight() {
160             return (double) height;
161         }
162 
163         /**
164          * {@inheritDoc}
165          * @since 1.2
166          */
getArcWidth()167         public double getArcWidth() {
168             return (double) arcwidth;
169         }
170 
171         /**
172          * {@inheritDoc}
173          * @since 1.2
174          */
getArcHeight()175         public double getArcHeight() {
176             return (double) archeight;
177         }
178 
179         /**
180          * {@inheritDoc}
181          * @since 1.2
182          */
isEmpty()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} to the specified
190          * {@code float} values.
191          *
192          * @param x the X coordinate to which to set the
193          *          location of this {@code RoundRectangle2D}
194          * @param y the Y coordinate to which to set the
195          *          location of this {@code RoundRectangle2D}
196          * @param w the width to which to set this
197          *          {@code RoundRectangle2D}
198          * @param h the height to which to set this
199          *          {@code RoundRectangle2D}
200          * @param arcw the width to which to set the arc of this
201          *             {@code RoundRectangle2D}
202          * @param arch the height to which to set the arc of this
203          *             {@code RoundRectangle2D}
204          * @since 1.2
205          */
setRoundRect(float x, float y, float w, float h, float arcw, float arch)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          */
setRoundRect(double x, double y, double w, double h, double arcw, double arch)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          */
setRoundRect(RoundRectangle2D rr)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          */
getBounds2D()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} class defines a rectangle with rounded
261      * corners all specified in {@code double} 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}.
269          * @since 1.2
270          * @serial
271          */
272         public double x;
273 
274         /**
275          * The Y coordinate of this {@code RoundRectangle2D}.
276          * @since 1.2
277          * @serial
278          */
279         public double y;
280 
281         /**
282          * The width of this {@code RoundRectangle2D}.
283          * @since 1.2
284          * @serial
285          */
286         public double width;
287 
288         /**
289          * The height of this {@code RoundRectangle2D}.
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}, 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          */
Double()315         public Double() {
316         }
317 
318         /**
319          * Constructs and initializes a {@code RoundRectangle2D}
320          * from the specified {@code double} coordinates.
321          *
322          * @param x the X coordinate of the newly
323          *          constructed {@code RoundRectangle2D}
324          * @param y the Y coordinate of the newly
325          *          constructed {@code RoundRectangle2D}
326          * @param w the width to which to set the newly
327          *          constructed {@code RoundRectangle2D}
328          * @param h the height to which to set the newly
329          *          constructed {@code RoundRectangle2D}
330          * @param arcw the width of the arc to use to round off the
331          *             corners of the newly constructed
332          *             {@code RoundRectangle2D}
333          * @param arch the height of the arc to use to round off the
334          *             corners of the newly constructed
335          *             {@code RoundRectangle2D}
336          * @since 1.2
337          */
Double(double x, double y, double w, double h, double arcw, double arch)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          */
getX()348         public double getX() {
349             return x;
350         }
351 
352         /**
353          * {@inheritDoc}
354          * @since 1.2
355          */
getY()356         public double getY() {
357             return y;
358         }
359 
360         /**
361          * {@inheritDoc}
362          * @since 1.2
363          */
getWidth()364         public double getWidth() {
365             return width;
366         }
367 
368         /**
369          * {@inheritDoc}
370          * @since 1.2
371          */
getHeight()372         public double getHeight() {
373             return height;
374         }
375 
376         /**
377          * {@inheritDoc}
378          * @since 1.2
379          */
getArcWidth()380         public double getArcWidth() {
381             return arcwidth;
382         }
383 
384         /**
385          * {@inheritDoc}
386          * @since 1.2
387          */
getArcHeight()388         public double getArcHeight() {
389             return archeight;
390         }
391 
392         /**
393          * {@inheritDoc}
394          * @since 1.2
395          */
isEmpty()396         public boolean isEmpty() {
397             return (width <= 0.0f) || (height <= 0.0f);
398         }
399 
400         /**
401          * {@inheritDoc}
402          * @since 1.2
403          */
setRoundRect(double x, double y, double w, double h, double arcw, double arch)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          */
setRoundRect(RoundRectangle2D rr)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          */
getBounds2D()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      */
RoundRectangle2D()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}.
460      * @since 1.2
461      */
getArcWidth()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}.
468      * @since 1.2
469      */
getArcHeight()470     public abstract double getArcHeight();
471 
472     /**
473      * Sets the location, size, and corner radii of this
474      * {@code RoundRectangle2D} to the specified
475      * {@code double} values.
476      *
477      * @param x the X coordinate to which to set the
478      *          location of this {@code RoundRectangle2D}
479      * @param y the Y coordinate to which to set the
480      *          location of this {@code RoundRectangle2D}
481      * @param w the width to which to set this
482      *          {@code RoundRectangle2D}
483      * @param h the height to which to set this
484      *          {@code RoundRectangle2D}
485      * @param arcWidth the width to which to set the arc of this
486      *                 {@code RoundRectangle2D}
487      * @param arcHeight the height to which to set the arc of this
488      *                  {@code RoundRectangle2D}
489      * @since 1.2
490      */
setRoundRect(double x, double y, double w, double h, double arcWidth, double arcHeight)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} to be the same as the
496      * specified {@code RoundRectangle2D}.
497      * @param rr the specified {@code RoundRectangle2D}
498      * @since 1.2
499      */
setRoundRect(RoundRectangle2D rr)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      */
setFrame(double x, double y, double w, double h)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      */
contains(double x, double y)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 
classify(double coord, double left, double right, double arcsize)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      */
intersects(double x, double y, double w, double h)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      */
contains(double x, double y, double w, double h)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}.
620      * The iterator for this class is multi-threaded safe, which means
621      * that this {@code RoundRectangle2D} class guarantees that
622      * modifications to the geometry of this {@code RoundRectangle2D}
623      * object do not affect any iterations of that geometry that
624      * are already in process.
625      * @param at an optional {@code AffineTransform} to be applied to
626      * the coordinates as they are returned in the iteration, or
627      * {@code null} if untransformed coordinates are desired
628      * @return    the {@code PathIterator} object that returns the
629      *          geometry of the outline of this
630      *          {@code RoundRectangle2D}, one segment at a time.
631      * @since 1.2
632      */
getPathIterator(AffineTransform at)633     public PathIterator getPathIterator(AffineTransform at) {
634         return new RoundRectIterator(this, at);
635     }
636 
637     /**
638      * Returns the hashcode for this {@code RoundRectangle2D}.
639      * @return the hashcode for this {@code RoundRectangle2D}.
640      * @since 1.6
641      */
hashCode()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} is
654      * equal to this {@code RoundRectangle2D}.  The specified
655      * {@code Object} is equal to this {@code RoundRectangle2D}
656      * if it is an instance of {@code RoundRectangle2D} and if its
657      * location, size, and corner arc dimensions are the same as this
658      * {@code RoundRectangle2D}.
659      * @param obj  an {@code Object} to be compared with this
660      *             {@code RoundRectangle2D}.
661      * @return  {@code true} if {@code obj} is an instance
662      *          of {@code RoundRectangle2D} and has the same values;
663      *          {@code false} otherwise.
664      * @since 1.6
665      */
equals(Object obj)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 }
682