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</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  */
  44 public 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 }
--- EOF ---