Source for java.awt.geom.RoundRectangle2D

   1: /*
   2:  * @(#)RoundRectangle2D.java    1.18 03/12/19
   3:  *
   4:  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
   5:  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
   6:  */
   7: 
   8: package java.awt.geom;
   9: 
  10: /**
  11:  * The <code>RoundRectangle2D</code> class defines a rectangle with
  12:  * rounded corners defined by a location (x,&nbsp;y), a
  13:  * dimension (w&nbsp;x&nbsp;h), and the width and height of an arc 
  14:  * with which to round the corners.
  15:  * <p>
  16:  * This class is the abstract superclass for all objects that
  17:  * store a 2D rounded rectangle.
  18:  * The actual storage representation of the coordinates is left to
  19:  * the subclass.
  20:  *
  21:  * @version 1.18, 12/19/03
  22:  * @author    Jim Graham
  23:  */
  24:	  public abstract class RoundRectangle2D extends RectangularShape {
  25:    /**
  26:     * The <code>Float</code> class defines a rectangle with rounded
  27:     * corners all specified in <code>float</code> coordinates.
  28:     */
  29:	      public static class Float extends RoundRectangle2D {
  30:    /**
  31:     * The X coordinate of this <code>RoundRectangle2D</code>.
  32:     */
  33:    public float x;
  34:
  35:    /**
  36:         * The Y coordinate of this <code>RoundRectangle2D</code>.
  37:     */
  38:    public float y;
  39:
  40:    /**
  41:         * The width of this <code>RoundRectangle2D</code>.
  42:     */
  43:    public float width;
  44:
  45:    /**
  46:         * The height of this <code>RoundRectangle2D</code>.
  47:     */
  48:    public float height;
  49:
  50:    /**
  51:     * The width of the arc that rounds off the corners.
  52:     */
  53:    public float arcwidth;
  54:
  55:    /**
  56:     * The height of the arc that rounds off the corners.
  57:     */
  58:    public float archeight;
  59:
  60:    /**
  61:     * Constructs a new <code>RoundRectangle2D</code>, initialized to
  62:         * location (0.0,&nbsp;0), size (0.0,&nbsp;0.0), and corner arcs
  63:         * of radius 0.0.
  64:     */
  65:	      public Float() {
  66:    }
  67:
  68:    /**
  69:     * Constructs and initializes a <code>RoundRectangle2D</code> 
  70:         * from the specified coordinates.
  71:     * @param x,&nbsp;y the coordinates to which to set the newly
  72:         * constructed <code>RoundRectangle2D</code>
  73:     * @param w the width to which to set the newly
  74:         * constructed <code>RoundRectangle2D</code>
  75:     * @param h the height to which to set the newly
  76:         * constructed <code>RoundRectangle2D</code>
  77:         * @param arcw the width of the arc to use to round off the
  78:         * corners of the newly constructed <code>RoundRectangle2D</code>
  79:         * @param arch the height of the arc to use to round off the
  80:         * corners of the newly constructed <code>RoundRectangle2D</code>
  81:     */
  82:    public Float(float x, float y, float w, float h,
  83:	                     float arcw, float arch) {
  84:        setRoundRect(x, y, w, h, arcw, arch);
  85:    }
  86:
  87:    /**
  88:     * Returns the X coordinate of this <code>RoundRectangle2D</code>
  89:         * in <code>double</code> precision.
  90:         * @return the X coordinate of this <code>RoundRectangle2D</code>.
  91:     */
  92:	      public double getX() {
  93:        return (double) x;
  94:    }
  95:
  96:    /**
  97:     * Returns the Y coordinate of this <code>RoundRectangle2D</code>
  98:         * in <code>double</code> precision.
  99:         * @return the Y coordinate of this <code>RoundRectangle2D</code>.
 100:         */    
 101:	      public double getY() {
 102:        return (double) y;
 103:    }
 104:
 105:    /**
 106:     * Returns the width of this <code>RoundRectangle2D</code>
 107:         * in <code>double</code> precision.
 108:         * @return the width of this <code>RoundRectangle2D</code>.
 109:         */    
 110:	      public double getWidth() {
 111:        return (double) width;
 112:    }
 113:
 114:    /**
 115:         * Returns the height of this <code>RoundRectangle2D</code> 
 116:         * in <code>double</code> precision.
 117:         * @return the height of this <code>RoundRectangle2D</code>. 
 118:         */
 119:	      public double getHeight() {
 120:        return (double) height;
 121:    }
 122:
 123:    /**
 124:     * Returns the width of the arc that rounds off the corners.
 125:         * @return the width of the arc that rounds off the corners
 126:         * of this <code>RoundRectangle2D</code>.
 127:     */
 128:	      public double getArcWidth() {
 129:        return (double) arcwidth;
 130:    }
 131:
 132:    /**
 133:         * Returns the height of the arc that rounds off the corners.
 134:         * @return the height of the arc that rounds off the corners
 135:         * of this <code>RoundRectangle2D</code>. 
 136:         */
 137:	      public double getArcHeight() {
 138:        return (double) archeight;
 139:    }
 140:
 141:    /**
 142:     * Determines whether or not this <code>RoundRectangle2D</code>
 143:         * is empty.
 144:         * @return <code>true</code> if this <code>RoundRectangle2D</code>
 145:         * is empty; <code>false</code> othwerwise.
 146:     */
 147:	      public boolean isEmpty() {
 148:        return (width <= 0.0f) || (height <= 0.0f);
 149:    }
 150:
 151:    /**
 152:     * Sets the location, size, and arc radii of this 
 153:         * <code>RoundRectangle2D</code> to the
 154:     * specified <code>float</code> values.
 155:         * @param x,&nbsp;y the coordinates to which to set the
 156:         * location of this <code>RoundRectangle2D</code>
 157:         * @param w the width to which to set this
 158:         * <code>RoundRectangle2D</code>
 159:         * @param h the height to which to set this
 160:         * <code>RoundRectangle2D</code>
 161:         * @param arcw the width to which to set the arc of this
 162:         * <code>RoundRectangle2D</code>
 163:         * @param arch the height to which to set the arc of this
 164:         * <code>RoundRectangle2D</code>
 165:     */
 166:    public void setRoundRect(float x, float y, float w, float h,
 167:	                   float arcw, float arch) {
 168:        this.x = x;
 169:        this.y = y;
 170:        this.width = w;
 171:        this.height = h;
 172:        this.arcwidth = arcw;
 173:        this.archeight = arch;
 174:    }
 175:
 176:    /**
 177:     * Sets the location, size, and arc radii of this 
 178:         * <code>RoundRectangle2D</code> to the
 179:     * specified <code>double</code> values.
 180:         * @param x,&nbsp;y the coordinates to which to set the
 181:         * location of this <code>RoundRectangle2D</code>
 182:         * @param w the width to which to set this
 183:         * <code>RoundRectangle2D</code>
 184:         * @param h the height to which to set this
 185:         * <code>RoundRectangle2D</code>
 186:         * @param arcw the width to which to set the arc of this
 187:         * <code>RoundRectangle2D</code>
 188:         * @param arch the height to which to set the arc of this
 189:         * <code>RoundRectangle2D</code>    
 190:         */
 191:    public void setRoundRect(double x, double y, double w, double h,
 192:	                   double arcw, double arch) {
 193:        this.x = (float) x;
 194:        this.y = (float) y;
 195:        this.width = (float) w;
 196:        this.height = (float) h;
 197:        this.arcwidth = (float) arcw;
 198:        this.archeight = (float) arch;
 199:    }
 200:
 201:    /**
 202:     * Sets this <code>RoundRectangle2D</code> to be the same as the
 203:         * specified <code>RoundRectangle2D</code>.
 204:         * @param rr the specified <code>RoundRectangle2D</code>
 205:     */
 206:	      public void setRoundRect(RoundRectangle2D rr) {
 207:        this.x = (float) rr.getX();
 208:        this.y = (float) rr.getY();
 209:        this.width = (float) rr.getWidth();
 210:        this.height = (float) rr.getHeight();
 211:        this.arcwidth = (float) rr.getArcWidth();
 212:        this.archeight = (float) rr.getArcHeight();
 213:    }
 214:
 215:    /**
 216:     * Returns the high precision bounding box of this
 217:         * <code>RoundRectangle2D</code>.
 218:         * @return a {@link Rectangle2D} that is the bounding
 219:         * box of this <code>RoundRectangle2D</code>.
 220:     */
 221:	      public Rectangle2D getBounds2D() {
 222:        return new Rectangle2D.Float(x, y, width, height);
 223:    }
 224:    }
 225:
 226:    /**
 227:     * The <code>Double</code> class defines a rectangle with rounded
 228:     * corners all specified in <code>double</code> coordinates.
 229:     */
 230:	      public static class Double extends RoundRectangle2D {
 231:    /**
 232:         * The X coordinate of this <code>RoundRectangle2D</code>. 
 233:         */
 234:    public double x;
 235:
 236:    /**
 237:         * The Y coordinate of this <code>RoundRectangle2D</code>. 
 238:         */
 239:    public double y;
 240:
 241:    /**
 242:         * The width of this <code>RoundRectangle2D</code>.
 243:     */
 244:    public double width;
 245:
 246:    /**
 247:         * The height of this <code>RoundRectangle2D</code>.
 248:     */
 249:    public double height;
 250:
 251:    /**
 252:         * The width of the arc that rounds off the corners.
 253:     */
 254:    public double arcwidth;
 255:
 256:    /**
 257:         * The height of the arc that rounds off the corners.
 258:     */
 259:    public double archeight;
 260:
 261:    /**
 262:         * Constructs a new <code>RoundRectangle2D</code>, initialized to
 263:         * location (0.0,&nbsp;0), size (0.0,&nbsp;0.0), and corner arcs
 264:         * of radius 0.0.
 265:         */
 266:	      public Double() {
 267:    }
 268:
 269:    /**
 270:         * Constructs and initializes a <code>RoundRectangle2D</code>
 271:         * from the specified coordinates.
 272:         * @param x,&nbsp;y the coordinates to which to set the newly
 273:         * constructed <code>RoundRectangle2D</code>
 274:         * @param w the width to which to set the newly
 275:         * constructed <code>RoundRectangle2D</code>
 276:         * @param h the height to which to set the newly
 277:         * constructed <code>RoundRectangle2D</code>
 278:         * @param arcw the width of the arc to use to round off the
 279:         * corners of the newly constructed <code>RoundRectangle2D</code>
 280:         * @param arch the height of the arc to use to round off the 
 281:         * corners of the newly constructed <code>RoundRectangle2D</code>
 282:         */
 283:    public Double(double x, double y, double w, double h,
 284:	                double arcw, double arch) {
 285:        setRoundRect(x, y, w, h, arcw, arch);
 286:    }
 287:
 288:    /**
 289:         * Returns the X coordinate of this <code>RoundRectangle2D</code>
 290:         * in <code>double</code> precision.
 291:         * @return the X coordinate of this <code>RoundRectangle2D</code>.
 292:         */
 293:	      public double getX() {
 294:        return x;
 295:    }
 296:
 297:    /**
 298:         * Returns the Y coordinate of this <code>RoundRectangle2D</code>
 299:         * in <code>double</code> precision.
 300:         * @return the Y coordinate of this <code>RoundRectangle2D</code>.
 301:         */
 302:	      public double getY() {
 303:        return y;
 304:    }
 305:
 306:    /**
 307:         * Returns the width of this <code>RoundRectangle2D</code>
 308:         * in <code>double</code> precision.
 309:         * @return the width of this <code>RoundRectangle2D</code>.
 310:         */
 311:	      public double getWidth() {
 312:        return width;
 313:    }
 314:
 315:    /**
 316:         * Returns the height of this <code>RoundRectangle2D</code>
 317:         * in <code>double</code> precision.
 318:         * @return the height of this <code>RoundRectangle2D</code>.
 319:         */
 320:	      public double getHeight() {
 321:        return height;
 322:    }
 323:
 324:    /**
 325:         * Returns the width of the arc that rounds off the corners.
 326:         * @return the width of the arc that rounds off the corners
 327:         * of this <code>RoundRectangle2D</code>.
 328:         */
 329:	      public double getArcWidth() {
 330:        return arcwidth;
 331:    }
 332:
 333:    /**
 334:         * Returns the height of the arc that rounds off the corners.
 335:         * @return the height of the arc that rounds off the corners
 336:         * of this <code>RoundRectangle2D</code>.
 337:         */
 338:	      public double getArcHeight() {
 339:        return archeight;
 340:    }
 341:
 342:    /**
 343:         * Determines whether or not this <code>RoundRectangle2D</code>
 344:         * is empty.
 345:         * @return <code>true</code> if this <code>RoundRectangle2D</code>
 346:         * is empty; <code>false</code> othwerwise.    
 347:         */
 348:	      public boolean isEmpty() {
 349:        return (width <= 0.0f) || (height <= 0.0f);
 350:    }
 351:
 352:    /**
 353:         * Sets the location, size, and arc radii of this
 354:         * <code>RoundRectangle2D</code> to the
 355:         * specified <code>double</code> values.
 356:         * @param x,&nbsp;y the coordinates to which to set the
 357:         * location of this <code>RoundRectangle2D</code>
 358:         * @param w the width to which to set this
 359:         * <code>RoundRectangle2D</code>
 360:         * @param h the height to which to set this
 361:         * <code>RoundRectangle2D</code>
 362:         * @param arcw the width to which to set the arc of this
 363:         * <code>RoundRectangle2D</code>
 364:         * @param arch the height to which to set the arc of this
 365:         * <code>RoundRectangle2D</code>
 366:         */
 367:    public void setRoundRect(double x, double y, double w, double h,
 368:	                   double arcw, double arch) {
 369:        this.x = x;
 370:        this.y = y;
 371:        this.width = w;
 372:        this.height = h;
 373:        this.arcwidth = arcw;
 374:        this.archeight = arch;
 375:    }
 376:
 377:    /**
 378:         * Sets this <code>RoundRectangle2D</code> to be the same as the
 379:         * specified <code>RoundRectangle2D</code>.
 380:         * @param rr the specified <code>RoundRectangle2D</code>
 381:         */
 382:	      public void setRoundRect(RoundRectangle2D rr) {
 383:        this.x = rr.getX();
 384:        this.y = rr.getY();
 385:        this.width = rr.getWidth();
 386:        this.height = rr.getHeight();
 387:        this.arcwidth = rr.getArcWidth();
 388:        this.archeight = rr.getArcHeight();
 389:    }
 390:
 391:    /**
 392:         * Returns the high precision bounding box of this
 393:         * <code>RoundRectangle2D</code>.
 394:         * @return a {@link Rectangle2D} that is the bounding
 395:         * box of this <code>RoundRectangle2D</code>.
 396:     */
 397:	      public Rectangle2D getBounds2D() {
 398:        return new Rectangle2D.Double(x, y, width, height);
 399:    }
 400:    }
 401:
 402:    /**
 403:     * This is an abstract class that cannot be instantiated directly.
 404:     * Type-specific implementation subclasses are available for
 405:     * instantiation and provide a number of formats for storing
 406:     * the information necessary to satisfy the various accessor
 407:     * methods below.
 408:     *
 409:     * @see java.awt.geom.RoundRectangle2D.Float
 410:     * @see java.awt.geom.RoundRectangle2D.Double
 411:     */
 412:	      protected RoundRectangle2D() {
 413:    }
 414:
 415:    /**
 416:     * Gets the width of the arc that rounds off the corners.
 417:     * @return the width of the arc that rounds off the corners
 418:     * of this <code>RoundRectangle2D</code>.
 419:     */
 420:    public abstract double getArcWidth();
 421:
 422:    /**
 423:     * Gets the height of the arc that rounds off the corners.
 424:     * @return the height of the arc that rounds off the corners
 425:     * of this <code>RoundRectangle2D</code>.    
 426:     */
 427:    public abstract double getArcHeight();
 428:
 429:    /**
 430:     * Sets the location, size, and corner radii of this 
 431:     * <code>RoundRectangle2D</code> to the specified 
 432:     * <code>double</code> values.
 433:     * @param x,&nbsp;y the coordinates to which to set the
 434:     * location of this <code>RoundRectangle2D</code>
 435:     * @param w the width to which to set this
 436:     * <code>RoundRectangle2D</code>
 437:     * @param h the height to which to set this
 438:     * <code>RoundRectangle2D</code>
 439:     * @param arcWidth the width to which to set the arc of this
 440:     * <code>RoundRectangle2D</code>
 441:     * @param arcHeight the height to which to set the arc of this
 442:     * <code>RoundRectangle2D</code>
 443:     */
 444:    public abstract void setRoundRect(double x, double y, double w, double h,
 445:                      double arcWidth, double arcHeight);
 446:
 447:    /**
 448:     * Sets this <code>RoundRectangle2D</code> to be the same as the
 449:     * specified <code>RoundRectangle2D</code>.
 450:     * @param rr the specified <code>RoundRectangle2D</code>
 451:     */
 452:	      public void setRoundRect(RoundRectangle2D rr) {
 453:    setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(),
 454:             rr.getArcWidth(), rr.getArcHeight());
 455:    }
 456:
 457:    /**
 458:     * Sets the location and size of the outer bounds of this
 459:     * <code>RoundRectangle2D</code> to the specified rectangular values.
 460:     * @param x,&nbsp;y the coordinates to which to set the location
 461:     * of this <code>RoundRectangle2D</code>
 462:     * @param w the width to which to set this
 463:     * <code>RoundRectangle2D</code>
 464:     * @param h the height to which to set this
 465:     * <code>RoundRectangle2D</code>
 466:     */
 467:	      public void setFrame(double x, double y, double w, double h) {
 468:    setRoundRect(x, y, w, h, getArcWidth(), getArcHeight());
 469:    }
 470:
 471:    /**
 472:     * Tests if the specified coordinates are inside the boundary of
 473:     * this <code>RoundRectangle2D</code>.
 474:     * @param x,&nbsp;y the coordinates to test
 475:     * @return <code>true</code> if the specified coordinates are
 476:     * inside the boundary of this <code>RoundRectangle2D</code>;
 477:     * <code>false</code> otherwise.
 478:     */
 479:	      public boolean contains(double x, double y) {
 480:	      if (isEmpty()) {
 481:        return false;
 482:    }
 483:    double rrx0 = getX();
 484:    double rry0 = getY();
 485:    double rrx1 = rrx0 + getWidth();
 486:    double rry1 = rry0 + getHeight();
 487:    // Check for trivial rejection - point is outside bounding rectangle
 488:	      if (x < rrx0 || y < rry0 || x >= rrx1 || y >= rry1) {
 489:        return false;
 490:    }
 491:    double aw = Math.min(getWidth(), Math.abs(getArcWidth())) / 2.0;
 492:    double ah = Math.min(getHeight(), Math.abs(getArcHeight())) / 2.0;
 493:    // Check which corner point is in and do circular containment
 494:    // test - otherwise simple acceptance
 495:	      if (x >= (rrx0 += aw) && x < (rrx0 = rrx1 - aw)) {
 496:        return true;
 497:    }
 498:	      if (y >= (rry0 += ah) && y < (rry0 = rry1 - ah)) {
 499:        return true;
 500:    }
 501:    x = (x - rrx0) / aw;
 502:    y = (y - rry0) / ah;
 503:    return (x * x + y * y <= 1.0);
 504:    }
 505:
 506:    private int classify(double coord, double left, double right,
 507:	               double arcsize) {
 508:	      if (coord < left) {
 509:        return 0;
 510:    } else if (coord < left + arcsize) {
 511:        return 1;
 512:    } else if (coord < right - arcsize) {
 513:        return 2;
 514:    } else if (coord < right) {
 515:        return 3;
 516:    } else {
 517:        return 4;
 518:    }
 519:    }
 520:
 521:    /**
 522:     * Tests if the interior of this <code>RoundRectangle2D</code> 
 523:     * intersects the interior of a specified set of rectangular
 524:     * coordinates.
 525:     * @param x,&nbsp;y the coordinates of the upper left corner
 526:     * of the specified set of rectangular coordinates
 527:     * @param w the width of the specified set of rectangular
 528:     * coordinates
 529:     * @param h the height of the specified set of rectangular
 530:     * coordinates
 531:     * @return <code>true</code> if the interior of this
 532:     * <code>RoundRectangle2D</code> intersects the interior of the
 533:     * specified set of rectangular coordinates.
 534:     */
 535:	      public boolean intersects(double x, double y, double w, double h) {
 536:	      if (isEmpty() || w <= 0 || h <= 0) {
 537:        return false;
 538:    }
 539:    double rrx0 = getX();
 540:    double rry0 = getY();
 541:    double rrx1 = rrx0 + getWidth();
 542:    double rry1 = rry0 + getHeight();
 543:    // Check for trivial rejection - bounding rectangles do not intersect
 544:	      if (x + w <= rrx0 || x >= rrx1 || y + h <= rry0 || y >= rry1) {
 545:        return false;
 546:    }
 547:    double aw = Math.min(getWidth(), Math.abs(getArcWidth())) / 2.0;
 548:    double ah = Math.min(getHeight(), Math.abs(getArcHeight())) / 2.0;
 549:    int x0class = classify(x, rrx0, rrx1, aw);
 550:    int x1class = classify(x + w, rrx0, rrx1, aw);
 551:    int y0class = classify(y, rry0, rry1, ah);
 552:    int y1class = classify(y + h, rry0, rry1, ah);
 553:    // Trivially accept if any point is inside inner rectangle
 554:	      if (x0class == 2 || x1class == 2 || y0class == 2 || y1class == 2) {
 555:        return true;
 556:    }
 557:    // Trivially accept if either edge spans inner rectangle
 558:	      if ((x0class < 2 && x1class > 2) || (y0class < 2 && y1class > 2)) {
 559:        return true;
 560:    }
 561:    // Since neither edge spans the center, then one of the corners
 562:    // must be in one of the rounded edges.  We detect this case if
 563:    // a [xy]0class is 3 or a [xy]1class is 1.  One of those two cases
 564:    // must be true for each direction.
 565:    // We now find a "nearest point" to test for being inside a rounded
 566:    // corner.
 567:    x = (x1class == 1) ? (x = x + w - (rrx0 + aw)) : (x = x - (rrx1 - aw));
 568:    y = (y1class == 1) ? (y = y + h - (rry0 + ah)) : (y = y - (rry1 - ah));
 569:    x = x / aw;
 570:    y = y / ah;
 571:    return (x * x + y * y <= 1.0);
 572:    }
 573:
 574:    /**
 575:     * Tests if the interior of this <code>RoundRectangle2D</code>
 576:     * entirely contains the specified set of rectangular coordinates.
 577:     * @param x,&nbsp;y the coordinates of the specified set of
 578:     * rectangular coordinates
 579:     * @param w the width of the specified set of rectangular
 580:     * coordinates
 581:     * @param h the height of the specified set of rectangular 
 582:     * coordinates
 583:     * @return <code>true</code> if the interior of this
 584:     * <code>RoundRectangle2D</code> entirely contains the specified
 585:     * set of rectangular coordinates; <code>false</code> otherwise.
 586:     */
 587:	      public boolean contains(double x, double y, double w, double h) {
 588:	      if (isEmpty() || w <= 0 || h <= 0) {
 589:        return false;
 590:    }
 591:    return (contains(x, y) &&
 592:        contains(x + w, y) &&
 593:        contains(x, y + h) &&
 594:        contains(x + w, y + h));
 595:    }
 596:
 597:    /**
 598:     * Returns an iteration object that defines the boundary of this
 599:     * <code>RoundRectangle2D</code>.
 600:     * The iterator for this class is multi-threaded safe, which means
 601:     * that this <code>RoundRectangle2D</code> class guarantees that
 602:     * modifications to the geometry of this <code>RoundRectangle2D</code>
 603:     * object do not affect any iterations of that geometry that
 604:     * are already in process.
 605:     * @param at an optional <code>AffineTransform</code> to be applied to
 606:     * the coordinates as they are returned in the iteration, or
 607:     * <code>null</code> if untransformed coordinates are desired
 608:     * @return    the <code>PathIterator</code> object that returns the
 609:     *          geometry of the outline of this
 610:     *          <code>RoundRectangle2D</code>, one segment at a time.
 611:     */
 612:	      public PathIterator getPathIterator(AffineTransform at) {
 613:    return new RoundRectIterator(this, at);
 614:    }
 615:}