Source for java.awt.geom.RoundRectangle2D

   1: /* RoundRectangle2D.java -- represents a rectangle with rounded corners
   2:    Copyright (C) 2000, 2002, 2003, 2004 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: import java.util.NoSuchElementException;
  41: 
  42: 
  43: /** This class implements a rectangle with rounded corners.
  44:  * @author Tom Tromey (tromey@cygnus.com)
  45:  * @date December 3, 2000
  46:  */
  47: public abstract class RoundRectangle2D extends RectangularShape
  48: {
  49:   /** Return the arc height of this round rectangle.  */
  50:   public abstract double getArcHeight();
  51: 
  52:   /** Return the arc width of this round rectangle.  */
  53:   public abstract double getArcWidth();
  54: 
  55:   /** Set the values of this round rectangle
  56:    * @param x The x coordinate
  57:    * @param y The y coordinate
  58:    * @param w The width
  59:    * @param h The height
  60:    * @param arcWidth The arc width
  61:    * @param arcHeight The arc height
  62:    */
  63:   public abstract void setRoundRect(double x, double y, double w, double h,
  64:                                     double arcWidth, double arcHeight);
  65: 
  66:   /** Create a RoundRectangle2D.  This is protected because this class
  67:    * is abstract and cannot be instantiated.
  68:    */
  69:   protected RoundRectangle2D()
  70:   {
  71:   }
  72: 
  73:   /** Return true if this object contains the specified point.
  74:    * @param x The x coordinate
  75:    * @param y The y coordinate
  76:    */
  77:   public boolean contains(double x, double y)
  78:   {
  79:     double mx = getX();
  80:     double mw = getWidth();
  81:     if (x < mx || x >= mx + mw)
  82:       return false;
  83:     double my = getY();
  84:     double mh = getHeight();
  85:     if (y < my || y >= my + mh)
  86:       return false;
  87: 
  88:     // Now check to see if the point is in range of an arc.
  89:     double dy = Math.min(Math.abs(my - y), Math.abs(my + mh - y));
  90:     double dx = Math.min(Math.abs(mx - x), Math.abs(mx + mw - x));
  91: 
  92:     // The arc dimensions are that of the corresponding ellipse
  93:     // thus a 90 degree segment is half of that.
  94:     double aw = getArcWidth() / 2.0;
  95:     double ah = getArcHeight() / 2.0;
  96:     if (dx > aw || dy > ah)
  97:       return true;
  98: 
  99:     // At this point DX represents the distance from the nearest edge
 100:     // of the rectangle.  But we want to transform it to represent the
 101:     // scaled distance from the center of the ellipse that forms the
 102:     // arc.  Hence this code:
 103:     dy = (ah - dy) / ah;
 104:     dx = (aw - dx) / aw;
 105: 
 106:     return dx * dx + dy * dy <= 1.0;
 107:   }
 108: 
 109:   /** Return true if this object contains the specified rectangle
 110:    * @param x The x coordinate
 111:    * @param y The y coordinate
 112:    * @param w The width
 113:    * @param h The height
 114:    */
 115:   public boolean contains(double x, double y, double w, double h)
 116:   {
 117:     // We have to check all four points here (for ordinary rectangles
 118:     // we can just check opposing corners).
 119:     return (contains(x, y) && contains(x, y + h) && contains(x + w, y + h)
 120:            && contains(x + w, y));
 121:   }
 122: 
 123:   /** Return a new path iterator which iterates over this rectangle.
 124:    * @param at An affine transform to apply to the object
 125:    */
 126:   public PathIterator getPathIterator(final AffineTransform at)
 127:   {
 128:     final double minx = getX();
 129:     final double miny = getY();
 130:     final double maxx = minx + getWidth();
 131:     final double maxy = miny + getHeight();
 132:     final double arcwidth = getArcWidth();
 133:     final double archeight = getArcHeight();
 134:     return new PathIterator()
 135:       {
 136:     /** We iterate counterclockwise around the rectangle, starting in the
 137:      * upper right.  This variable tracks our current point, which
 138:      * can be on either side of a given corner.  */
 139:     private int current = 0;
 140: 
 141:     /** Child path iterator, used for corners.  */
 142:     private PathIterator corner;
 143: 
 144:     /** This is used when rendering the corners.  We re-use the arc
 145:      * for each corner.  */
 146:     private Arc2D arc = new Arc2D.Double();
 147: 
 148:     /** Temporary array used by getPoint.  */
 149:     private double[] temp = new double[2];
 150: 
 151:     public int getWindingRule()
 152:     {
 153:       return WIND_NON_ZERO;
 154:     }
 155: 
 156:     public boolean isDone()
 157:     {
 158:       return current > 9;
 159:     }
 160: 
 161:     private void getPoint(int val)
 162:     {
 163:       switch (val)
 164:         {
 165:         case 0:
 166:         case 8:
 167:           temp[0] = maxx;
 168:           temp[1] = miny + archeight;
 169:           break;
 170:         case 7:
 171:           temp[0] = maxx;
 172:           temp[1] = maxy - archeight;
 173:           break;
 174:         case 6:
 175:           temp[0] = maxx - arcwidth;
 176:           temp[1] = maxy;
 177:           break;
 178:         case 5:
 179:           temp[0] = minx + arcwidth;
 180:           temp[1] = maxy;
 181:           break;
 182:         case 4:
 183:           temp[0] = minx;
 184:           temp[1] = maxy - archeight;
 185:           break;
 186:         case 3:
 187:           temp[0] = minx;
 188:           temp[1] = miny + archeight;
 189:           break;
 190:         case 2:
 191:           temp[0] = minx + arcwidth;
 192:           temp[1] = miny;
 193:           break;
 194:         case 1:
 195:           temp[0] = maxx - arcwidth;
 196:           temp[1] = miny;
 197:           break;
 198:         }
 199:     }
 200: 
 201:     public void next()
 202:     {
 203:       if (current >= 8)
 204:         ++current;
 205:       else if (corner != null)
 206:         {
 207:           // We're iterating through the corner.  Work on the child
 208:           // iterator; if it finishes, reset and move to the next
 209:           // point along the rectangle.
 210:           corner.next();
 211:           if (corner.isDone())
 212:             {
 213:           corner = null;
 214:           ++current;
 215:             }
 216:         }
 217:       else
 218:         {
 219:           // Make an arc between this point on the rectangle and
 220:           // the next one, and then iterate over this arc.
 221:           getPoint(current);
 222:           double x1 = temp[0];
 223:           double y1 = temp[1];
 224:           getPoint(current + 1);
 225:           Rectangle2D.Double r = new Rectangle2D.Double(Math.min(x1,
 226:                                                                  temp[0]),
 227:                                                         Math.min(y1,
 228:                                                                  temp[1]),
 229:                                                         Math.abs(x1
 230:                                                                  - temp[0]),
 231:                                                         Math.abs(y1
 232:                                                                  - temp[1]));
 233:           arc.setArc(r, (current >> 1) * 90.0, 90.0, Arc2D.OPEN);
 234:           corner = arc.getPathIterator(at);
 235:         }
 236:     }
 237: 
 238:     public int currentSegment(float[] coords)
 239:     {
 240:       if (corner != null)
 241:         {
 242:           int r = corner.currentSegment(coords);
 243:           if (r == SEG_MOVETO)
 244:         r = SEG_LINETO;
 245:           return r;
 246:         }
 247: 
 248:       if (current < 9)
 249:         {
 250:           getPoint(current);
 251:           coords[0] = (float) temp[0];
 252:           coords[1] = (float) temp[1];
 253:         }
 254:       else if (current == 9)
 255:         return SEG_CLOSE;
 256:       else
 257:         throw new NoSuchElementException("rect iterator out of bounds");
 258: 
 259:       if (at != null)
 260:         at.transform(coords, 0, coords, 0, 1);
 261:       return current == 0 ? SEG_MOVETO : SEG_LINETO;
 262:     }
 263: 
 264:     public int currentSegment(double[] coords)
 265:     {
 266:       if (corner != null)
 267:         {
 268:           int r = corner.currentSegment(coords);
 269:           if (r == SEG_MOVETO)
 270:         r = SEG_LINETO;
 271:           return r;
 272:         }
 273: 
 274:       if (current < 9)
 275:         {
 276:           getPoint(current);
 277:           coords[0] = temp[0];
 278:           coords[1] = temp[1];
 279:         }
 280:       else if (current == 9)
 281:         return SEG_CLOSE;
 282:       else
 283:         throw new NoSuchElementException("rect iterator out of bounds");
 284: 
 285:       if (at != null)
 286:         at.transform(coords, 0, coords, 0, 1);
 287:       return current == 0 ? SEG_MOVETO : SEG_LINETO;
 288:     }
 289:       };
 290:   }
 291: 
 292:   /** Return true if the given rectangle intersects this shape.
 293:    * @param x The x coordinate
 294:    * @param y The y coordinate
 295:    * @param w The width
 296:    * @param h The height
 297:    */
 298:   public boolean intersects(double x, double y, double w, double h)
 299:   {
 300:     // Check if any corner is within the rectangle
 301:     return (contains(x, y) || contains(x, y + h) || contains(x + w, y + h)
 302:            || contains(x + w, y));
 303:   }
 304: 
 305:   /** Set the boundary of this round rectangle.
 306:    * @param x The x coordinate
 307:    * @param y The y coordinate
 308:    * @param w The width
 309:    * @param h The height
 310:    */
 311:   public void setFrame(double x, double y, double w, double h)
 312:   {
 313:     // This is a bit lame.
 314:     setRoundRect(x, y, w, h, getArcWidth(), getArcHeight());
 315:   }
 316: 
 317:   /** Set the values of this round rectangle to be the same as those
 318:    * of the argument.
 319:    * @param rr The round rectangle to copy
 320:    */
 321:   public void setRoundRect(RoundRectangle2D rr)
 322:   {
 323:     setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(),
 324:                  rr.getArcWidth(), rr.getArcHeight());
 325:   }
 326: 
 327:   /** A subclass of RoundRectangle which keeps its parameters as
 328:    * doubles.  */
 329:   public static class Double extends RoundRectangle2D
 330:   {
 331:     /** The height of the corner arc.  */
 332:     public double archeight;
 333: 
 334:     /** The width of the corner arc.  */
 335:     public double arcwidth;
 336: 
 337:     /** The x coordinate of this object.  */
 338:     public double x;
 339: 
 340:     /** The y coordinate of this object.  */
 341:     public double y;
 342: 
 343:     /** The width of this object.  */
 344:     public double width;
 345: 
 346:     /** The height of this object.  */
 347:     public double height;
 348: 
 349:     /** Construct a new instance, with all parameters set to 0.  */
 350:     public Double()
 351:     {
 352:     }
 353: 
 354:     /** Construct a new instance with the given arguments.
 355:      * @param x The x coordinate
 356:      * @param y The y coordinate
 357:      * @param w The width
 358:      * @param h The height
 359:      * @param arcWidth The arc width
 360:      * @param arcHeight The arc height
 361:      */
 362:     public Double(double x, double y, double w, double h, double arcWidth,
 363:                   double arcHeight)
 364:     {
 365:       this.x = x;
 366:       this.y = y;
 367:       this.width = w;
 368:       this.height = h;
 369:       this.arcwidth = arcWidth;
 370:       this.archeight = arcHeight;
 371:     }
 372: 
 373:     public double getArcHeight()
 374:     {
 375:       return archeight;
 376:     }
 377: 
 378:     public double getArcWidth()
 379:     {
 380:       return arcwidth;
 381:     }
 382: 
 383:     public Rectangle2D getBounds2D()
 384:     {
 385:       return new Rectangle2D.Double(x, y, width, height);
 386:     }
 387: 
 388:     public double getX()
 389:     {
 390:       return x;
 391:     }
 392: 
 393:     public double getY()
 394:     {
 395:       return y;
 396:     }
 397: 
 398:     public double getWidth()
 399:     {
 400:       return width;
 401:     }
 402: 
 403:     public double getHeight()
 404:     {
 405:       return height;
 406:     }
 407: 
 408:     public boolean isEmpty()
 409:     {
 410:       return width <= 0 || height <= 0;
 411:     }
 412: 
 413:     public void setRoundRect(double x, double y, double w, double h,
 414:                              double arcWidth, double arcHeight)
 415:     {
 416:       this.x = x;
 417:       this.y = y;
 418:       this.width = w;
 419:       this.height = h;
 420:       this.arcwidth = arcWidth;
 421:       this.archeight = arcHeight;
 422:     }
 423:   } // class Double
 424: 
 425:   /** A subclass of RoundRectangle which keeps its parameters as
 426:    * floats.  */
 427:   public static class Float extends RoundRectangle2D
 428:   {
 429:     /** The height of the corner arc.  */
 430:     public float archeight;
 431: 
 432:     /** The width of the corner arc.  */
 433:     public float arcwidth;
 434: 
 435:     /** The x coordinate of this object.  */
 436:     public float x;
 437: 
 438:     /** The y coordinate of this object.  */
 439:     public float y;
 440: 
 441:     /** The width of this object.  */
 442:     public float width;
 443: 
 444:     /** The height of this object.  */
 445:     public float height;
 446: 
 447:     /** Construct a new instance, with all parameters set to 0.  */
 448:     public Float()
 449:     {
 450:     }
 451: 
 452:     /** Construct a new instance with the given arguments.
 453:      * @param x The x coordinate
 454:      * @param y The y coordinate
 455:      * @param w The width
 456:      * @param h The height
 457:      * @param arcWidth The arc width
 458:      * @param arcHeight The arc height
 459:      */
 460:     public Float(float x, float y, float w, float h, float arcWidth,
 461:                  float arcHeight)
 462:     {
 463:       this.x = x;
 464:       this.y = y;
 465:       this.width = w;
 466:       this.height = h;
 467:       this.arcwidth = arcWidth;
 468:       this.archeight = arcHeight;
 469:     }
 470: 
 471:     public double getArcHeight()
 472:     {
 473:       return archeight;
 474:     }
 475: 
 476:     public double getArcWidth()
 477:     {
 478:       return arcwidth;
 479:     }
 480: 
 481:     public Rectangle2D getBounds2D()
 482:     {
 483:       return new Rectangle2D.Float(x, y, width, height);
 484:     }
 485: 
 486:     public double getX()
 487:     {
 488:       return x;
 489:     }
 490: 
 491:     public double getY()
 492:     {
 493:       return y;
 494:     }
 495: 
 496:     public double getWidth()
 497:     {
 498:       return width;
 499:     }
 500: 
 501:     public double getHeight()
 502:     {
 503:       return height;
 504:     }
 505: 
 506:     public boolean isEmpty()
 507:     {
 508:       return width <= 0 || height <= 0;
 509:     }
 510: 
 511:     public void setRoundRect(float x, float y, float w, float h,
 512:                              float arcWidth, float arcHeight)
 513:     {
 514:       this.x = x;
 515:       this.y = y;
 516:       this.width = w;
 517:       this.height = h;
 518:       this.arcwidth = arcWidth;
 519:       this.archeight = arcHeight;
 520:     }
 521: 
 522:     public void setRoundRect(double x, double y, double w, double h,
 523:                              double arcWidth, double arcHeight)
 524:     {
 525:       this.x = (float) x;
 526:       this.y = (float) y;
 527:       this.width = (float) w;
 528:       this.height = (float) h;
 529:       this.arcwidth = (float) arcWidth;
 530:       this.archeight = (float) arcHeight;
 531:     }
 532:   } // class Float
 533: } // class RoundRectangle2D