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, 0.0), size (0.0, 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, 0.0), size (0.0, 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