Source for java.awt.geom.RoundRectangle2D

   1: /* RoundRectangle2D.java -- represents a rectangle with rounded corners
   2:    Copyright (C) 2000, 2002, 2003, 2004, 2006, Free Software Foundation
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package java.awt.geom;
  39: 
  40: 
  41: 
  42: /** This class implements a rectangle with rounded corners.
  43:  * @author Tom Tromey (tromey@cygnus.com)
  44:  * @date December 3, 2000
  45:  */
  46: public abstract class RoundRectangle2D extends RectangularShape
  47: {
  48:   /**
  49:    * Return the arc height of this round rectangle.  The arc height and width
  50:    * control the roundness of the corners of the rectangle.
  51:    *
  52:    * @return The arc height.
  53:    *
  54:    * @see #getArcWidth()
  55:    */
  56:   public abstract double getArcHeight();
  57: 
  58:   /**
  59:    * Return the arc width of this round rectangle.  The arc width and height
  60:    * control the roundness of the corners of the rectangle.
  61:    *
  62:    * @return The arc width.
  63:    *
  64:    * @see #getArcHeight()
  65:    */
  66:   public abstract double getArcWidth();
  67: 
  68:   /**
  69:    * Set the values of this round rectangle.
  70:    *
  71:    * @param x The x coordinate
  72:    * @param y The y coordinate
  73:    * @param w The width
  74:    * @param h The height
  75:    * @param arcWidth The arc width
  76:    * @param arcHeight The arc height
  77:    */
  78:   public abstract void setRoundRect(double x, double y, double w, double h,
  79:                                     double arcWidth, double arcHeight);
  80: 
  81:   /**
  82:    * Create a RoundRectangle2D.  This is protected because this class
  83:    * is abstract and cannot be instantiated.
  84:    */
  85:   protected RoundRectangle2D()
  86:   {
  87:   }
  88: 
  89:   /**
  90:    * Return true if this object contains the specified point.
  91:    * @param x The x coordinate
  92:    * @param y The y coordinate
  93:    */
  94:   public boolean contains(double x, double y)
  95:   {
  96:     double mx = getX();
  97:     double mw = getWidth();
  98:     if (x < mx || x >= mx + mw)
  99:       return false;
 100:     double my = getY();
 101:     double mh = getHeight();
 102:     if (y < my || y >= my + mh)
 103:       return false;
 104: 
 105:     // Now check to see if the point is in range of an arc.
 106:     double dy = Math.min(Math.abs(my - y), Math.abs(my + mh - y));
 107:     double dx = Math.min(Math.abs(mx - x), Math.abs(mx + mw - x));
 108: 
 109:     // The arc dimensions are that of the corresponding ellipse
 110:     // thus a 90 degree segment is half of that.
 111:     double aw = getArcWidth() / 2.0;
 112:     double ah = getArcHeight() / 2.0;
 113:     if (dx > aw || dy > ah)
 114:       return true;
 115: 
 116:     // At this point DX represents the distance from the nearest edge
 117:     // of the rectangle.  But we want to transform it to represent the
 118:     // scaled distance from the center of the ellipse that forms the
 119:     // arc.  Hence this code:
 120:     dy = (ah - dy) / ah;
 121:     dx = (aw - dx) / aw;
 122: 
 123:     return dx * dx + dy * dy <= 1.0;
 124:   }
 125: 
 126:   /**
 127:    * Return true if this object contains the specified rectangle
 128:    * @param x The x coordinate
 129:    * @param y The y coordinate
 130:    * @param w The width
 131:    * @param h The height
 132:    */
 133:   public boolean contains(double x, double y, double w, double h)
 134:   {
 135:     // We have to check all four points here (for ordinary rectangles
 136:     // we can just check opposing corners).
 137:     return (contains(x, y) && contains(x, y + h) && contains(x + w, y + h)
 138:            && contains(x + w, y));
 139:   }
 140: 
 141:   /**
 142:    * Return a new path iterator which iterates over this rectangle.
 143:    *
 144:    * @param at An affine transform to apply to the object
 145:    */
 146:   public PathIterator getPathIterator(final AffineTransform at)
 147:   {
 148:     double arcW = Math.min(getArcWidth(), getWidth());
 149:     double arcH = Math.min(getArcHeight(), getHeight());
 150: 
 151:     // check for special cases...
 152:     if (arcW <= 0 || arcH <= 0)
 153:       {
 154:         Rectangle2D r = new Rectangle2D.Double(getX(), getY(), getWidth(),
 155:                 getHeight());
 156:         return r.getPathIterator(at);
 157:       }
 158:     else if (arcW >= getWidth() && arcH >= getHeight())
 159:       {
 160:         Ellipse2D e = new Ellipse2D.Double(getX(), getY(), getWidth(),
 161:                 getHeight());
 162:         return e.getPathIterator(at);
 163:       }
 164: 
 165:     // otherwise return the standard case...
 166:     return new PathIterator()
 167:       {
 168:         double x = getX();
 169:         double y = getY();
 170:         double w = getWidth();
 171:         double h = getHeight();
 172:         double arcW = Math.min(getArcWidth(), w);
 173:         double arcH = Math.min(getArcHeight(), h);
 174:         Arc2D.Double arc = new Arc2D.Double();
 175:         PathIterator corner;
 176:         int step = -1;
 177: 
 178:         public int currentSegment(double[] coords)
 179:         {
 180:           if (corner != null) // steps 1, 3, 5 and 7
 181:           {
 182:             int r = corner.currentSegment(coords);
 183:             if (r == SEG_MOVETO)
 184:               r = SEG_LINETO;
 185:             return r;
 186:           }
 187:           if (step == -1)
 188:           {
 189:             // move to the start position
 190:             coords[0] = x + w - arcW / 2;
 191:             coords[1] = y;
 192:           }
 193:           else if (step == 0)
 194:           {
 195:             // top line
 196:             coords[0] = x + arcW / 2;
 197:             coords[1] = y;
 198:           }
 199:           else if (step == 2)
 200:           {
 201:             // left line
 202:             coords[0] = x;
 203:             coords[1] = y + h - arcH / 2;
 204:           }
 205:           else if (step == 4)
 206:           {
 207:             // bottom line
 208:             coords[0] = x + w - arcW / 2;
 209:             coords[1] = y + h;
 210:           }
 211:           else if (step == 6)
 212:           {
 213:             // right line
 214:               coords[0] = x + w;
 215:               coords[1] = y + arcH / 2;
 216:           }
 217:           if (at != null)
 218:             at.transform(coords, 0, coords, 0, 1);
 219:           return step == -1 ? SEG_MOVETO : SEG_LINETO;
 220:         }
 221: 
 222:         public int currentSegment(float[] coords) {
 223:           if (corner != null) // steps 1, 3, 5 and 7
 224:           {
 225:             int r = corner.currentSegment(coords);
 226:             if (r == SEG_MOVETO)
 227:               r = SEG_LINETO;
 228:             return r;
 229:           }
 230:           if (step == -1)
 231:           {
 232:             // move to the start position
 233:             coords[0] = (float) (x + w - arcW / 2);
 234:             coords[1] = (float) y;
 235:           }
 236:           else if (step == 0)
 237:           {
 238:             // top line
 239:             coords[0] = (float) (x + arcW / 2);
 240:             coords[1] = (float) y;
 241:           }
 242:           else if (step == 2)
 243:           {
 244:             // left line
 245:             coords[0] = (float) x;
 246:             coords[1] = (float) (y + h - arcH / 2);
 247:           }
 248:           else if (step == 4)
 249:           {
 250:             // bottom line
 251:             coords[0] = (float) (x + w - arcW / 2);
 252:             coords[1] = (float) (y + h);
 253:           }
 254:           else if (step == 6)
 255:           {
 256:             // right line
 257:             coords[0] = (float) (x + w);
 258:             coords[1] = (float) (y + arcH / 2);
 259:           }
 260:         if (at != null)
 261:           at.transform(coords, 0, coords, 0, 1);
 262:         return step == -1 ? SEG_MOVETO : SEG_LINETO;
 263:       }
 264: 
 265:       public int getWindingRule() {
 266:         return WIND_NON_ZERO;
 267:       }
 268: 
 269:       public boolean isDone() {
 270:         return step >= 8;
 271:       }
 272: 
 273:       public void next()
 274:       {
 275:         if (corner != null)
 276:           {
 277:             corner.next();
 278:             if (corner.isDone())
 279:               {
 280:                 corner = null;
 281:                 step++;
 282:               }
 283:           }
 284:         else
 285:           {
 286:             step++;
 287:             if (step == 1)
 288:               {
 289:                 // create top left corner
 290:                 arc.setArc(x, y, arcW, arcH, 90, 90, Arc2D.OPEN);
 291:                 corner = arc.getPathIterator(at);
 292:               }
 293:             else if (step == 3)
 294:               {
 295:                 // create bottom left corner
 296:                 arc.setArc(x, y + h - arcH, arcW, arcH, 180, 90,
 297:                         Arc2D.OPEN);
 298:                 corner = arc.getPathIterator(at);
 299:               }
 300:             else if (step == 5)
 301:               {
 302:                 // create bottom right corner
 303:                 arc.setArc(x + w - arcW, y + h - arcH, arcW, arcH, 270, 90,
 304:                         Arc2D.OPEN);
 305:                 corner = arc.getPathIterator(at);
 306:               }
 307:             else if (step == 7)
 308:               {
 309:                 // create top right corner
 310:                 arc.setArc(x + w - arcW, y, arcW, arcH, 0, 90, Arc2D.OPEN);
 311:                 corner = arc.getPathIterator(at);
 312:               }
 313:           }
 314:       }
 315:     };
 316:   }
 317: 
 318:   /**
 319:    * Return true if the given rectangle intersects this shape.
 320:    * @param x The x coordinate
 321:    * @param y The y coordinate
 322:    * @param w The width
 323:    * @param h The height
 324:    */
 325:   public boolean intersects(double x, double y, double w, double h)
 326:   {
 327:     // Check if any corner is within the rectangle
 328:     return (contains(x, y) || contains(x, y + h) || contains(x + w, y + h)
 329:            || contains(x + w, y));
 330:   }
 331: 
 332:   /**
 333:    * Set the boundary of this round rectangle.
 334:    * @param x The x coordinate
 335:    * @param y The y coordinate
 336:    * @param w The width
 337:    * @param h The height
 338:    */
 339:   public void setFrame(double x, double y, double w, double h)
 340:   {
 341:     // This is a bit lame.
 342:     setRoundRect(x, y, w, h, getArcWidth(), getArcHeight());
 343:   }
 344: 
 345:   /**
 346:    * Set the values of this round rectangle to be the same as those
 347:    * of the argument.
 348:    * @param rr The round rectangle to copy
 349:    */
 350:   public void setRoundRect(RoundRectangle2D rr)
 351:   {
 352:     setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(),
 353:                  rr.getArcWidth(), rr.getArcHeight());
 354:   }
 355: 
 356:   /**
 357:    * A subclass of RoundRectangle which keeps its parameters as
 358:    * doubles.
 359:    */
 360:   public static class Double extends RoundRectangle2D
 361:   {
 362:     /** The height of the corner arc.  */
 363:     public double archeight;
 364: 
 365:     /** The width of the corner arc.  */
 366:     public double arcwidth;
 367: 
 368:     /** The x coordinate of this object.  */
 369:     public double x;
 370: 
 371:     /** The y coordinate of this object.  */
 372:     public double y;
 373: 
 374:     /** The width of this object.  */
 375:     public double width;
 376: 
 377:     /** The height of this object.  */
 378:     public double height;
 379: 
 380:     /**
 381:      * Construct a new instance, with all parameters set to 0.
 382:      */
 383:     public Double()
 384:     {
 385:     }
 386: 
 387:     /**
 388:      * Construct a new instance with the given arguments.
 389:      * @param x The x coordinate
 390:      * @param y The y coordinate
 391:      * @param w The width
 392:      * @param h The height
 393:      * @param arcWidth The arc width
 394:      * @param arcHeight The arc height
 395:      */
 396:     public Double(double x, double y, double w, double h, double arcWidth,
 397:                   double arcHeight)
 398:     {
 399:       this.x = x;
 400:       this.y = y;
 401:       this.width = w;
 402:       this.height = h;
 403:       this.arcwidth = arcWidth;
 404:       this.archeight = arcHeight;
 405:     }
 406: 
 407:     public double getArcHeight()
 408:     {
 409:       return archeight;
 410:     }
 411: 
 412:     public double getArcWidth()
 413:     {
 414:       return arcwidth;
 415:     }
 416: 
 417:     public Rectangle2D getBounds2D()
 418:     {
 419:       return new Rectangle2D.Double(x, y, width, height);
 420:     }
 421: 
 422:     public double getX()
 423:     {
 424:       return x;
 425:     }
 426: 
 427:     public double getY()
 428:     {
 429:       return y;
 430:     }
 431: 
 432:     public double getWidth()
 433:     {
 434:       return width;
 435:     }
 436: 
 437:     public double getHeight()
 438:     {
 439:       return height;
 440:     }
 441: 
 442:     public boolean isEmpty()
 443:     {
 444:       return width <= 0 || height <= 0;
 445:     }
 446: 
 447:     public void setRoundRect(double x, double y, double w, double h,
 448:                              double arcWidth, double arcHeight)
 449:     {
 450:       this.x = x;
 451:       this.y = y;
 452:       this.width = w;
 453:       this.height = h;
 454:       this.arcwidth = arcWidth;
 455:       this.archeight = arcHeight;
 456:     }
 457:   } // class Double
 458: 
 459:   /**
 460:    * A subclass of RoundRectangle which keeps its parameters as
 461:    * floats.
 462:    */
 463:   public static class Float extends RoundRectangle2D
 464:   {
 465:     /** The height of the corner arc.  */
 466:     public float archeight;
 467: 
 468:     /** The width of the corner arc.  */
 469:     public float arcwidth;
 470: 
 471:     /** The x coordinate of this object.  */
 472:     public float x;
 473: 
 474:     /** The y coordinate of this object.  */
 475:     public float y;
 476: 
 477:     /** The width of this object.  */
 478:     public float width;
 479: 
 480:     /** The height of this object.  */
 481:     public float height;
 482: 
 483:     /**
 484:      * Construct a new instance, with all parameters set to 0.
 485:      */
 486:     public Float()
 487:     {
 488:     }
 489: 
 490:     /**
 491:      * Construct a new instance with the given arguments.
 492:      * @param x The x coordinate
 493:      * @param y The y coordinate
 494:      * @param w The width
 495:      * @param h The height
 496:      * @param arcWidth The arc width
 497:      * @param arcHeight The arc height
 498:      */
 499:     public Float(float x, float y, float w, float h, float arcWidth,
 500:                  float arcHeight)
 501:     {
 502:       this.x = x;
 503:       this.y = y;
 504:       this.width = w;
 505:       this.height = h;
 506:       this.arcwidth = arcWidth;
 507:       this.archeight = arcHeight;
 508:     }
 509: 
 510:     public double getArcHeight()
 511:     {
 512:       return archeight;
 513:     }
 514: 
 515:     public double getArcWidth()
 516:     {
 517:       return arcwidth;
 518:     }
 519: 
 520:     public Rectangle2D getBounds2D()
 521:     {
 522:       return new Rectangle2D.Float(x, y, width, height);
 523:     }
 524: 
 525:     public double getX()
 526:     {
 527:       return x;
 528:     }
 529: 
 530:     public double getY()
 531:     {
 532:       return y;
 533:     }
 534: 
 535:     public double getWidth()
 536:     {
 537:       return width;
 538:     }
 539: 
 540:     public double getHeight()
 541:     {
 542:       return height;
 543:     }
 544: 
 545:     public boolean isEmpty()
 546:     {
 547:       return width <= 0 || height <= 0;
 548:     }
 549: 
 550:     /**
 551:      * Sets the dimensions for this rounded rectangle.
 552:      *
 553:      * @param x  the x-coordinate of the top left corner.
 554:      * @param y  the y-coordinate of the top left corner.
 555:      * @param w  the width of the rectangle.
 556:      * @param h  the height of the rectangle.
 557:      * @param arcWidth  the arc width.
 558:      * @param arcHeight  the arc height.
 559:      *
 560:      * @see #setRoundRect(double, double, double, double, double, double)
 561:      */
 562:     public void setRoundRect(float x, float y, float w, float h,
 563:                              float arcWidth, float arcHeight)
 564:     {
 565:       this.x = x;
 566:       this.y = y;
 567:       this.width = w;
 568:       this.height = h;
 569:       this.arcwidth = arcWidth;
 570:       this.archeight = arcHeight;
 571:     }
 572: 
 573:     public void setRoundRect(double x, double y, double w, double h,
 574:                              double arcWidth, double arcHeight)
 575:     {
 576:       this.x = (float) x;
 577:       this.y = (float) y;
 578:       this.width = (float) w;
 579:       this.height = (float) h;
 580:       this.arcwidth = (float) arcWidth;
 581:       this.archeight = (float) arcHeight;
 582:     }
 583:   } // class Float
 584: } // class RoundRectangle2D