1 /* 2 * Copyright (c) 1997, 2011, 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 * <CODE>Arc2D</CODE> is the abstract superclass for all objects that 32 * store a 2D arc defined by a framing rectangle, 33 * start angle, angular extent (length of the arc), and a closure type 34 * (<CODE>OPEN</CODE>, <CODE>CHORD</CODE>, or <CODE>PIE</CODE>). 35 * <p> 36 * <a name="inscribes"> 37 * The arc is a partial section of a full ellipse which 38 * inscribes the framing rectangle of its parent</a>{@link RectangularShape}. 39 * 40 * <a name="angles"> 41 * The angles are specified relative to the non-square 42 * framing rectangle such that 45 degrees always falls on the line from 43 * the center of the ellipse to the upper right corner of the framing 44 * rectangle. 45 * As a result, if the framing rectangle is noticeably longer along one 46 * axis than the other, the angles to the start and end of the arc segment 47 * will be skewed farther along the longer axis of the frame. 48 * </a> 49 * <p> 50 * The actual storage representation of the coordinates is left to 51 * the subclass. 52 * 53 * @author Jim Graham 54 * @since 1.2 55 */ 56 public abstract class Arc2D extends RectangularShape { 57 58 /** 59 * The closure type for an open arc with no path segments 60 * connecting the two ends of the arc segment. 61 * @since 1.2 62 */ 63 public final static int OPEN = 0; 64 65 /** 66 * The closure type for an arc closed by drawing a straight 67 * line segment from the start of the arc segment to the end of the 68 * arc segment. 69 * @since 1.2 70 */ 71 public final static int CHORD = 1; 72 73 /** 74 * The closure type for an arc closed by drawing straight line 75 * segments from the start of the arc segment to the center 76 * of the full ellipse and from that point to the end of the arc segment. 77 * @since 1.2 78 */ 79 public final static int PIE = 2; 80 81 /** 82 * This class defines an arc specified in {@code float} precision. 83 * @since 1.2 84 */ 85 public static class Float extends Arc2D implements Serializable { 86 /** 87 * The X coordinate of the upper-left corner of the framing 88 * rectangle of the arc. 89 * @since 1.2 90 * @serial 91 */ 92 public float x; 93 94 /** 95 * The Y coordinate of the upper-left corner of the framing 96 * rectangle of the arc. 97 * @since 1.2 98 * @serial 99 */ 100 public float y; 101 102 /** 103 * The overall width of the full ellipse of which this arc is 104 * a partial section (not considering the 105 * angular extents). 106 * @since 1.2 107 * @serial 108 */ 109 public float width; 110 111 /** 112 * The overall height of the full ellipse of which this arc is 113 * a partial section (not considering the 114 * angular extents). 115 * @since 1.2 116 * @serial 117 */ 118 public float height; 119 120 /** 121 * The starting angle of the arc in degrees. 122 * @since 1.2 123 * @serial 124 */ 125 public float start; 126 127 /** 128 * The angular extent of the arc in degrees. 129 * @since 1.2 130 * @serial 131 */ 132 public float extent; 133 134 /** 135 * Constructs a new OPEN arc, initialized to location (0, 0), 136 * size (0, 0), angular extents (start = 0, extent = 0). 137 * @since 1.2 138 */ 139 public Float() { 140 super(OPEN); 141 } 142 143 /** 144 * Constructs a new arc, initialized to location (0, 0), 145 * size (0, 0), angular extents (start = 0, extent = 0), and 146 * the specified closure type. 147 * 148 * @param type The closure type for the arc: 149 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 150 * @since 1.2 151 */ 152 public Float(int type) { 153 super(type); 154 } 155 156 /** 157 * Constructs a new arc, initialized to the specified location, 158 * size, angular extents, and closure type. 159 * 160 * @param x The X coordinate of the upper-left corner of 161 * the arc's framing rectangle. 162 * @param y The Y coordinate of the upper-left corner of 163 * the arc's framing rectangle. 164 * @param w The overall width of the full ellipse of which 165 * this arc is a partial section. 166 * @param h The overall height of the full ellipse of which this 167 * arc is a partial section. 168 * @param start The starting angle of the arc in degrees. 169 * @param extent The angular extent of the arc in degrees. 170 * @param type The closure type for the arc: 171 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 172 * @since 1.2 173 */ 174 public Float(float x, float y, float w, float h, 175 float start, float extent, int type) { 176 super(type); 177 this.x = x; 178 this.y = y; 179 this.width = w; 180 this.height = h; 181 this.start = start; 182 this.extent = extent; 183 } 184 185 /** 186 * Constructs a new arc, initialized to the specified location, 187 * size, angular extents, and closure type. 188 * 189 * @param ellipseBounds The framing rectangle that defines the 190 * outer boundary of the full ellipse of which this arc is a 191 * partial section. 192 * @param start The starting angle of the arc in degrees. 193 * @param extent The angular extent of the arc in degrees. 194 * @param type The closure type for the arc: 195 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 196 * @since 1.2 197 */ 198 public Float(Rectangle2D ellipseBounds, 199 float start, float extent, int type) { 200 super(type); 201 this.x = (float) ellipseBounds.getX(); 202 this.y = (float) ellipseBounds.getY(); 203 this.width = (float) ellipseBounds.getWidth(); 204 this.height = (float) ellipseBounds.getHeight(); 205 this.start = start; 206 this.extent = extent; 207 } 208 209 /** 210 * {@inheritDoc} 211 * Note that the arc 212 * <a href="Arc2D.html#inscribes">partially inscribes</a> 213 * the framing rectangle of this {@code RectangularShape}. 214 * 215 * @since 1.2 216 */ 217 public double getX() { 218 return (double) x; 219 } 220 221 /** 222 * {@inheritDoc} 223 * Note that the arc 224 * <a href="Arc2D.html#inscribes">partially inscribes</a> 225 * the framing rectangle of this {@code RectangularShape}. 226 * 227 * @since 1.2 228 */ 229 public double getY() { 230 return (double) y; 231 } 232 233 /** 234 * {@inheritDoc} 235 * Note that the arc 236 * <a href="Arc2D.html#inscribes">partially inscribes</a> 237 * the framing rectangle of this {@code RectangularShape}. 238 * 239 * @since 1.2 240 */ 241 public double getWidth() { 242 return (double) width; 243 } 244 245 /** 246 * {@inheritDoc} 247 * Note that the arc 248 * <a href="Arc2D.html#inscribes">partially inscribes</a> 249 * the framing rectangle of this {@code RectangularShape}. 250 * 251 * @since 1.2 252 */ 253 public double getHeight() { 254 return (double) height; 255 } 256 257 /** 258 * {@inheritDoc} 259 * @since 1.2 260 */ 261 public double getAngleStart() { 262 return (double) start; 263 } 264 265 /** 266 * {@inheritDoc} 267 * @since 1.2 268 */ 269 public double getAngleExtent() { 270 return (double) extent; 271 } 272 273 /** 274 * {@inheritDoc} 275 * @since 1.2 276 */ 277 public boolean isEmpty() { 278 return (width <= 0.0 || height <= 0.0); 279 } 280 281 /** 282 * {@inheritDoc} 283 * @since 1.2 284 */ 285 public void setArc(double x, double y, double w, double h, 286 double angSt, double angExt, int closure) { 287 this.setArcType(closure); 288 this.x = (float) x; 289 this.y = (float) y; 290 this.width = (float) w; 291 this.height = (float) h; 292 this.start = (float) angSt; 293 this.extent = (float) angExt; 294 } 295 296 /** 297 * {@inheritDoc} 298 * @since 1.2 299 */ 300 public void setAngleStart(double angSt) { 301 this.start = (float) angSt; 302 } 303 304 /** 305 * {@inheritDoc} 306 * @since 1.2 307 */ 308 public void setAngleExtent(double angExt) { 309 this.extent = (float) angExt; 310 } 311 312 /** 313 * {@inheritDoc} 314 * @since 1.2 315 */ 316 protected Rectangle2D makeBounds(double x, double y, 317 double w, double h) { 318 return new Rectangle2D.Float((float) x, (float) y, 319 (float) w, (float) h); 320 } 321 322 /* 323 * JDK 1.6 serialVersionUID 324 */ 325 private static final long serialVersionUID = 9130893014586380278L; 326 327 /** 328 * Writes the default serializable fields to the 329 * <code>ObjectOutputStream</code> followed by a byte 330 * indicating the arc type of this <code>Arc2D</code> 331 * instance. 332 * 333 * @serialData 334 * <ol> 335 * <li>The default serializable fields. 336 * <li> 337 * followed by a <code>byte</code> indicating the arc type 338 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 339 * </ol> 340 */ 341 private void writeObject(java.io.ObjectOutputStream s) 342 throws java.io.IOException 343 { 344 s.defaultWriteObject(); 345 346 s.writeByte(getArcType()); 347 } 348 349 /** 350 * Reads the default serializable fields from the 351 * <code>ObjectInputStream</code> followed by a byte 352 * indicating the arc type of this <code>Arc2D</code> 353 * instance. 354 * 355 * @serialData 356 * <ol> 357 * <li>The default serializable fields. 358 * <li> 359 * followed by a <code>byte</code> indicating the arc type 360 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 361 * </ol> 362 */ 363 private void readObject(java.io.ObjectInputStream s) 364 throws java.lang.ClassNotFoundException, java.io.IOException 365 { 366 s.defaultReadObject(); 367 368 try { 369 setArcType(s.readByte()); 370 } catch (IllegalArgumentException iae) { 371 throw new java.io.InvalidObjectException(iae.getMessage()); 372 } 373 } 374 } 375 376 /** 377 * This class defines an arc specified in {@code double} precision. 378 * @since 1.2 379 */ 380 public static class Double extends Arc2D implements Serializable { 381 /** 382 * The X coordinate of the upper-left corner of the framing 383 * rectangle of the arc. 384 * @since 1.2 385 * @serial 386 */ 387 public double x; 388 389 /** 390 * The Y coordinate of the upper-left corner of the framing 391 * rectangle of the arc. 392 * @since 1.2 393 * @serial 394 */ 395 public double y; 396 397 /** 398 * The overall width of the full ellipse of which this arc is 399 * a partial section (not considering the angular extents). 400 * @since 1.2 401 * @serial 402 */ 403 public double width; 404 405 /** 406 * The overall height of the full ellipse of which this arc is 407 * a partial section (not considering the angular extents). 408 * @since 1.2 409 * @serial 410 */ 411 public double height; 412 413 /** 414 * The starting angle of the arc in degrees. 415 * @since 1.2 416 * @serial 417 */ 418 public double start; 419 420 /** 421 * The angular extent of the arc in degrees. 422 * @since 1.2 423 * @serial 424 */ 425 public double extent; 426 427 /** 428 * Constructs a new OPEN arc, initialized to location (0, 0), 429 * size (0, 0), angular extents (start = 0, extent = 0). 430 * @since 1.2 431 */ 432 public Double() { 433 super(OPEN); 434 } 435 436 /** 437 * Constructs a new arc, initialized to location (0, 0), 438 * size (0, 0), angular extents (start = 0, extent = 0), and 439 * the specified closure type. 440 * 441 * @param type The closure type for the arc: 442 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 443 * @since 1.2 444 */ 445 public Double(int type) { 446 super(type); 447 } 448 449 /** 450 * Constructs a new arc, initialized to the specified location, 451 * size, angular extents, and closure type. 452 * 453 * @param x The X coordinate of the upper-left corner 454 * of the arc's framing rectangle. 455 * @param y The Y coordinate of the upper-left corner 456 * of the arc's framing rectangle. 457 * @param w The overall width of the full ellipse of which this 458 * arc is a partial section. 459 * @param h The overall height of the full ellipse of which this 460 * arc is a partial section. 461 * @param start The starting angle of the arc in degrees. 462 * @param extent The angular extent of the arc in degrees. 463 * @param type The closure type for the arc: 464 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 465 * @since 1.2 466 */ 467 public Double(double x, double y, double w, double h, 468 double start, double extent, int type) { 469 super(type); 470 this.x = x; 471 this.y = y; 472 this.width = w; 473 this.height = h; 474 this.start = start; 475 this.extent = extent; 476 } 477 478 /** 479 * Constructs a new arc, initialized to the specified location, 480 * size, angular extents, and closure type. 481 * 482 * @param ellipseBounds The framing rectangle that defines the 483 * outer boundary of the full ellipse of which this arc is a 484 * partial section. 485 * @param start The starting angle of the arc in degrees. 486 * @param extent The angular extent of the arc in degrees. 487 * @param type The closure type for the arc: 488 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 489 * @since 1.2 490 */ 491 public Double(Rectangle2D ellipseBounds, 492 double start, double extent, int type) { 493 super(type); 494 this.x = ellipseBounds.getX(); 495 this.y = ellipseBounds.getY(); 496 this.width = ellipseBounds.getWidth(); 497 this.height = ellipseBounds.getHeight(); 498 this.start = start; 499 this.extent = extent; 500 } 501 502 /** 503 * {@inheritDoc} 504 * Note that the arc 505 * <a href="Arc2D.html#inscribes">partially inscribes</a> 506 * the framing rectangle of this {@code RectangularShape}. 507 * 508 * @since 1.2 509 */ 510 public double getX() { 511 return x; 512 } 513 514 /** 515 * {@inheritDoc} 516 * Note that the arc 517 * <a href="Arc2D.html#inscribes">partially inscribes</a> 518 * the framing rectangle of this {@code RectangularShape}. 519 * 520 * @since 1.2 521 */ 522 public double getY() { 523 return y; 524 } 525 526 /** 527 * {@inheritDoc} 528 * Note that the arc 529 * <a href="Arc2D.html#inscribes">partially inscribes</a> 530 * the framing rectangle of this {@code RectangularShape}. 531 * 532 * @since 1.2 533 */ 534 public double getWidth() { 535 return width; 536 } 537 538 /** 539 * {@inheritDoc} 540 * Note that the arc 541 * <a href="Arc2D.html#inscribes">partially inscribes</a> 542 * the framing rectangle of this {@code RectangularShape}. 543 * 544 * @since 1.2 545 */ 546 public double getHeight() { 547 return height; 548 } 549 550 /** 551 * {@inheritDoc} 552 * @since 1.2 553 */ 554 public double getAngleStart() { 555 return start; 556 } 557 558 /** 559 * {@inheritDoc} 560 * @since 1.2 561 */ 562 public double getAngleExtent() { 563 return extent; 564 } 565 566 /** 567 * {@inheritDoc} 568 * @since 1.2 569 */ 570 public boolean isEmpty() { 571 return (width <= 0.0 || height <= 0.0); 572 } 573 574 /** 575 * {@inheritDoc} 576 * @since 1.2 577 */ 578 public void setArc(double x, double y, double w, double h, 579 double angSt, double angExt, int closure) { 580 this.setArcType(closure); 581 this.x = x; 582 this.y = y; 583 this.width = w; 584 this.height = h; 585 this.start = angSt; 586 this.extent = angExt; 587 } 588 589 /** 590 * {@inheritDoc} 591 * @since 1.2 592 */ 593 public void setAngleStart(double angSt) { 594 this.start = angSt; 595 } 596 597 /** 598 * {@inheritDoc} 599 * @since 1.2 600 */ 601 public void setAngleExtent(double angExt) { 602 this.extent = angExt; 603 } 604 605 /** 606 * {@inheritDoc} 607 * @since 1.2 608 */ 609 protected Rectangle2D makeBounds(double x, double y, 610 double w, double h) { 611 return new Rectangle2D.Double(x, y, w, h); 612 } 613 614 /* 615 * JDK 1.6 serialVersionUID 616 */ 617 private static final long serialVersionUID = 728264085846882001L; 618 619 /** 620 * Writes the default serializable fields to the 621 * <code>ObjectOutputStream</code> followed by a byte 622 * indicating the arc type of this <code>Arc2D</code> 623 * instance. 624 * 625 * @serialData 626 * <ol> 627 * <li>The default serializable fields. 628 * <li> 629 * followed by a <code>byte</code> indicating the arc type 630 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 631 * </ol> 632 */ 633 private void writeObject(java.io.ObjectOutputStream s) 634 throws java.io.IOException 635 { 636 s.defaultWriteObject(); 637 638 s.writeByte(getArcType()); 639 } 640 641 /** 642 * Reads the default serializable fields from the 643 * <code>ObjectInputStream</code> followed by a byte 644 * indicating the arc type of this <code>Arc2D</code> 645 * instance. 646 * 647 * @serialData 648 * <ol> 649 * <li>The default serializable fields. 650 * <li> 651 * followed by a <code>byte</code> indicating the arc type 652 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 653 * </ol> 654 */ 655 private void readObject(java.io.ObjectInputStream s) 656 throws java.lang.ClassNotFoundException, java.io.IOException 657 { 658 s.defaultReadObject(); 659 660 try { 661 setArcType(s.readByte()); 662 } catch (IllegalArgumentException iae) { 663 throw new java.io.InvalidObjectException(iae.getMessage()); 664 } 665 } 666 } 667 668 private int type; 669 670 /** 671 * This is an abstract class that cannot be instantiated directly. 672 * Type-specific implementation subclasses are available for 673 * instantiation and provide a number of formats for storing 674 * the information necessary to satisfy the various accessor 675 * methods below. 676 * <p> 677 * This constructor creates an object with a default closure 678 * type of {@link #OPEN}. It is provided only to enable 679 * serialization of subclasses. 680 * 681 * @see java.awt.geom.Arc2D.Float 682 * @see java.awt.geom.Arc2D.Double 683 */ 684 protected Arc2D() { 685 this(OPEN); 686 } 687 688 /** 689 * This is an abstract class that cannot be instantiated directly. 690 * Type-specific implementation subclasses are available for 691 * instantiation and provide a number of formats for storing 692 * the information necessary to satisfy the various accessor 693 * methods below. 694 * 695 * @param type The closure type of this arc: 696 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 697 * @see java.awt.geom.Arc2D.Float 698 * @see java.awt.geom.Arc2D.Double 699 * @since 1.2 700 */ 701 protected Arc2D(int type) { 702 setArcType(type); 703 } 704 705 /** 706 * Returns the starting angle of the arc. 707 * 708 * @return A double value that represents the starting angle 709 * of the arc in degrees. 710 * @see #setAngleStart 711 * @since 1.2 712 */ 713 public abstract double getAngleStart(); 714 715 /** 716 * Returns the angular extent of the arc. 717 * 718 * @return A double value that represents the angular extent 719 * of the arc in degrees. 720 * @see #setAngleExtent 721 * @since 1.2 722 */ 723 public abstract double getAngleExtent(); 724 725 /** 726 * Returns the arc closure type of the arc: {@link #OPEN}, 727 * {@link #CHORD}, or {@link #PIE}. 728 * @return One of the integer constant closure types defined 729 * in this class. 730 * @see #setArcType 731 * @since 1.2 732 */ 733 public int getArcType() { 734 return type; 735 } 736 737 /** 738 * Returns the starting point of the arc. This point is the 739 * intersection of the ray from the center defined by the 740 * starting angle and the elliptical boundary of the arc. 741 * 742 * @return A <CODE>Point2D</CODE> object representing the 743 * x,y coordinates of the starting point of the arc. 744 * @since 1.2 745 */ 746 public Point2D getStartPoint() { 747 double angle = Math.toRadians(-getAngleStart()); 748 double x = getX() + (Math.cos(angle) * 0.5 + 0.5) * getWidth(); 749 double y = getY() + (Math.sin(angle) * 0.5 + 0.5) * getHeight(); 750 return new Point2D.Double(x, y); 751 } 752 753 /** 754 * Returns the ending point of the arc. This point is the 755 * intersection of the ray from the center defined by the 756 * starting angle plus the angular extent of the arc and the 757 * elliptical boundary of the arc. 758 * 759 * @return A <CODE>Point2D</CODE> object representing the 760 * x,y coordinates of the ending point of the arc. 761 * @since 1.2 762 */ 763 public Point2D getEndPoint() { 764 double angle = Math.toRadians(-getAngleStart() - getAngleExtent()); 765 double x = getX() + (Math.cos(angle) * 0.5 + 0.5) * getWidth(); 766 double y = getY() + (Math.sin(angle) * 0.5 + 0.5) * getHeight(); 767 return new Point2D.Double(x, y); 768 } 769 770 /** 771 * Sets the location, size, angular extents, and closure type of 772 * this arc to the specified double values. 773 * 774 * @param x The X coordinate of the upper-left corner of the arc. 775 * @param y The Y coordinate of the upper-left corner of the arc. 776 * @param w The overall width of the full ellipse of which 777 * this arc is a partial section. 778 * @param h The overall height of the full ellipse of which 779 * this arc is a partial section. 780 * @param angSt The starting angle of the arc in degrees. 781 * @param angExt The angular extent of the arc in degrees. 782 * @param closure The closure type for the arc: 783 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 784 * @since 1.2 785 */ 786 public abstract void setArc(double x, double y, double w, double h, 787 double angSt, double angExt, int closure); 788 789 /** 790 * Sets the location, size, angular extents, and closure type of 791 * this arc to the specified values. 792 * 793 * @param loc The <CODE>Point2D</CODE> representing the coordinates of 794 * the upper-left corner of the arc. 795 * @param size The <CODE>Dimension2D</CODE> representing the width 796 * and height of the full ellipse of which this arc is 797 * a partial section. 798 * @param angSt The starting angle of the arc in degrees. 799 * @param angExt The angular extent of the arc in degrees. 800 * @param closure The closure type for the arc: 801 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 802 * @since 1.2 803 */ 804 public void setArc(Point2D loc, Dimension2D size, 805 double angSt, double angExt, int closure) { 806 setArc(loc.getX(), loc.getY(), size.getWidth(), size.getHeight(), 807 angSt, angExt, closure); 808 } 809 810 /** 811 * Sets the location, size, angular extents, and closure type of 812 * this arc to the specified values. 813 * 814 * @param rect The framing rectangle that defines the 815 * outer boundary of the full ellipse of which this arc is a 816 * partial section. 817 * @param angSt The starting angle of the arc in degrees. 818 * @param angExt The angular extent of the arc in degrees. 819 * @param closure The closure type for the arc: 820 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 821 * @since 1.2 822 */ 823 public void setArc(Rectangle2D rect, double angSt, double angExt, 824 int closure) { 825 setArc(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight(), 826 angSt, angExt, closure); 827 } 828 829 /** 830 * Sets this arc to be the same as the specified arc. 831 * 832 * @param a The <CODE>Arc2D</CODE> to use to set the arc's values. 833 * @since 1.2 834 */ 835 public void setArc(Arc2D a) { 836 setArc(a.getX(), a.getY(), a.getWidth(), a.getHeight(), 837 a.getAngleStart(), a.getAngleExtent(), a.type); 838 } 839 840 /** 841 * Sets the position, bounds, angular extents, and closure type of 842 * this arc to the specified values. The arc is defined by a center 843 * point and a radius rather than a framing rectangle for the full ellipse. 844 * 845 * @param x The X coordinate of the center of the arc. 846 * @param y The Y coordinate of the center of the arc. 847 * @param radius The radius of the arc. 848 * @param angSt The starting angle of the arc in degrees. 849 * @param angExt The angular extent of the arc in degrees. 850 * @param closure The closure type for the arc: 851 * {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. 852 * @since 1.2 853 */ 854 public void setArcByCenter(double x, double y, double radius, 855 double angSt, double angExt, int closure) { 856 setArc(x - radius, y - radius, radius * 2.0, radius * 2.0, 857 angSt, angExt, closure); 858 } 859 860 /** 861 * Sets the position, bounds, and angular extents of this arc to the 862 * specified value. The starting angle of the arc is tangent to the 863 * line specified by points (p1, p2), the ending angle is tangent to 864 * the line specified by points (p2, p3), and the arc has the 865 * specified radius. 866 * 867 * @param p1 The first point that defines the arc. The starting 868 * angle of the arc is tangent to the line specified by points (p1, p2). 869 * @param p2 The second point that defines the arc. The starting 870 * angle of the arc is tangent to the line specified by points (p1, p2). 871 * The ending angle of the arc is tangent to the line specified by 872 * points (p2, p3). 873 * @param p3 The third point that defines the arc. The ending angle 874 * of the arc is tangent to the line specified by points (p2, p3). 875 * @param radius The radius of the arc. 876 * @since 1.2 877 */ 878 public void setArcByTangent(Point2D p1, Point2D p2, Point2D p3, 879 double radius) { 880 double ang1 = Math.atan2(p1.getY() - p2.getY(), 881 p1.getX() - p2.getX()); 882 double ang2 = Math.atan2(p3.getY() - p2.getY(), 883 p3.getX() - p2.getX()); 884 double diff = ang2 - ang1; 885 if (diff > Math.PI) { 886 ang2 -= Math.PI * 2.0; 887 } else if (diff < -Math.PI) { 888 ang2 += Math.PI * 2.0; 889 } 890 double bisect = (ang1 + ang2) / 2.0; 891 double theta = Math.abs(ang2 - bisect); 892 double dist = radius / Math.sin(theta); 893 double x = p2.getX() + dist * Math.cos(bisect); 894 double y = p2.getY() + dist * Math.sin(bisect); 895 // REMIND: This needs some work... 896 if (ang1 < ang2) { 897 ang1 -= Math.PI / 2.0; 898 ang2 += Math.PI / 2.0; 899 } else { 900 ang1 += Math.PI / 2.0; 901 ang2 -= Math.PI / 2.0; 902 } 903 ang1 = Math.toDegrees(-ang1); 904 ang2 = Math.toDegrees(-ang2); 905 diff = ang2 - ang1; 906 if (diff < 0) { 907 diff += 360; 908 } else { 909 diff -= 360; 910 } 911 setArcByCenter(x, y, radius, ang1, diff, type); 912 } 913 914 /** 915 * Sets the starting angle of this arc to the specified double 916 * value. 917 * 918 * @param angSt The starting angle of the arc in degrees. 919 * @see #getAngleStart 920 * @since 1.2 921 */ 922 public abstract void setAngleStart(double angSt); 923 924 /** 925 * Sets the angular extent of this arc to the specified double 926 * value. 927 * 928 * @param angExt The angular extent of the arc in degrees. 929 * @see #getAngleExtent 930 * @since 1.2 931 */ 932 public abstract void setAngleExtent(double angExt); 933 934 /** 935 * Sets the starting angle of this arc to the angle that the 936 * specified point defines relative to the center of this arc. 937 * The angular extent of the arc will remain the same. 938 * 939 * @param p The <CODE>Point2D</CODE> that defines the starting angle. 940 * @see #getAngleStart 941 * @since 1.2 942 */ 943 public void setAngleStart(Point2D p) { 944 // Bias the dx and dy by the height and width of the oval. 945 double dx = getHeight() * (p.getX() - getCenterX()); 946 double dy = getWidth() * (p.getY() - getCenterY()); 947 setAngleStart(-Math.toDegrees(Math.atan2(dy, dx))); 948 } 949 950 /** 951 * Sets the starting angle and angular extent of this arc using two 952 * sets of coordinates. The first set of coordinates is used to 953 * determine the angle of the starting point relative to the arc's 954 * center. The second set of coordinates is used to determine the 955 * angle of the end point relative to the arc's center. 956 * The arc will always be non-empty and extend counterclockwise 957 * from the first point around to the second point. 958 * 959 * @param x1 The X coordinate of the arc's starting point. 960 * @param y1 The Y coordinate of the arc's starting point. 961 * @param x2 The X coordinate of the arc's ending point. 962 * @param y2 The Y coordinate of the arc's ending point. 963 * @since 1.2 964 */ 965 public void setAngles(double x1, double y1, double x2, double y2) { 966 double x = getCenterX(); 967 double y = getCenterY(); 968 double w = getWidth(); 969 double h = getHeight(); 970 // Note: reversing the Y equations negates the angle to adjust 971 // for the upside down coordinate system. 972 // Also we should bias atans by the height and width of the oval. 973 double ang1 = Math.atan2(w * (y - y1), h * (x1 - x)); 974 double ang2 = Math.atan2(w * (y - y2), h * (x2 - x)); 975 ang2 -= ang1; 976 if (ang2 <= 0.0) { 977 ang2 += Math.PI * 2.0; 978 } 979 setAngleStart(Math.toDegrees(ang1)); 980 setAngleExtent(Math.toDegrees(ang2)); 981 } 982 983 /** 984 * Sets the starting angle and angular extent of this arc using 985 * two points. The first point is used to determine the angle of 986 * the starting point relative to the arc's center. 987 * The second point is used to determine the angle of the end point 988 * relative to the arc's center. 989 * The arc will always be non-empty and extend counterclockwise 990 * from the first point around to the second point. 991 * 992 * @param p1 The <CODE>Point2D</CODE> that defines the arc's 993 * starting point. 994 * @param p2 The <CODE>Point2D</CODE> that defines the arc's 995 * ending point. 996 * @since 1.2 997 */ 998 public void setAngles(Point2D p1, Point2D p2) { 999 setAngles(p1.getX(), p1.getY(), p2.getX(), p2.getY()); 1000 } 1001 1002 /** 1003 * Sets the closure type of this arc to the specified value: 1004 * <CODE>OPEN</CODE>, <CODE>CHORD</CODE>, or <CODE>PIE</CODE>. 1005 * 1006 * @param type The integer constant that represents the closure 1007 * type of this arc: {@link #OPEN}, {@link #CHORD}, or 1008 * {@link #PIE}. 1009 * 1010 * @throws IllegalArgumentException if <code>type</code> is not 1011 * 0, 1, or 2.+ 1012 * @see #getArcType 1013 * @since 1.2 1014 */ 1015 public void setArcType(int type) { 1016 if (type < OPEN || type > PIE) { 1017 throw new IllegalArgumentException("invalid type for Arc: "+type); 1018 } 1019 this.type = type; 1020 } 1021 1022 /** 1023 * {@inheritDoc} 1024 * Note that the arc 1025 * <a href="Arc2D.html#inscribes">partially inscribes</a> 1026 * the framing rectangle of this {@code RectangularShape}. 1027 * 1028 * @since 1.2 1029 */ 1030 public void setFrame(double x, double y, double w, double h) { 1031 setArc(x, y, w, h, getAngleStart(), getAngleExtent(), type); 1032 } 1033 1034 /** 1035 * Returns the high-precision framing rectangle of the arc. The framing 1036 * rectangle contains only the part of this <code>Arc2D</code> that is 1037 * in between the starting and ending angles and contains the pie 1038 * wedge, if this <code>Arc2D</code> has a <code>PIE</code> closure type. 1039 * <p> 1040 * This method differs from the 1041 * {@link RectangularShape#getBounds() getBounds} in that the 1042 * <code>getBounds</code> method only returns the bounds of the 1043 * enclosing ellipse of this <code>Arc2D</code> without considering 1044 * the starting and ending angles of this <code>Arc2D</code>. 1045 * 1046 * @return the <CODE>Rectangle2D</CODE> that represents the arc's 1047 * framing rectangle. 1048 * @since 1.2 1049 */ 1050 public Rectangle2D getBounds2D() { 1051 if (isEmpty()) { 1052 return makeBounds(getX(), getY(), getWidth(), getHeight()); 1053 } 1054 double x1, y1, x2, y2; 1055 if (getArcType() == PIE) { 1056 x1 = y1 = x2 = y2 = 0.0; 1057 } else { 1058 x1 = y1 = 1.0; 1059 x2 = y2 = -1.0; 1060 } 1061 double angle = 0.0; 1062 for (int i = 0; i < 6; i++) { 1063 if (i < 4) { 1064 // 0-3 are the four quadrants 1065 angle += 90.0; 1066 if (!containsAngle(angle)) { 1067 continue; 1068 } 1069 } else if (i == 4) { 1070 // 4 is start angle 1071 angle = getAngleStart(); 1072 } else { 1073 // 5 is end angle 1074 angle += getAngleExtent(); 1075 } 1076 double rads = Math.toRadians(-angle); 1077 double xe = Math.cos(rads); 1078 double ye = Math.sin(rads); 1079 x1 = Math.min(x1, xe); 1080 y1 = Math.min(y1, ye); 1081 x2 = Math.max(x2, xe); 1082 y2 = Math.max(y2, ye); 1083 } 1084 double w = getWidth(); 1085 double h = getHeight(); 1086 x2 = (x2 - x1) * 0.5 * w; 1087 y2 = (y2 - y1) * 0.5 * h; 1088 x1 = getX() + (x1 * 0.5 + 0.5) * w; 1089 y1 = getY() + (y1 * 0.5 + 0.5) * h; 1090 return makeBounds(x1, y1, x2, y2); 1091 } 1092 1093 /** 1094 * Constructs a <code>Rectangle2D</code> of the appropriate precision 1095 * to hold the parameters calculated to be the framing rectangle 1096 * of this arc. 1097 * 1098 * @param x The X coordinate of the upper-left corner of the 1099 * framing rectangle. 1100 * @param y The Y coordinate of the upper-left corner of the 1101 * framing rectangle. 1102 * @param w The width of the framing rectangle. 1103 * @param h The height of the framing rectangle. 1104 * @return a <code>Rectangle2D</code> that is the framing rectangle 1105 * of this arc. 1106 * @since 1.2 1107 */ 1108 protected abstract Rectangle2D makeBounds(double x, double y, 1109 double w, double h); 1110 1111 /* 1112 * Normalizes the specified angle into the range -180 to 180. 1113 */ 1114 static double normalizeDegrees(double angle) { 1115 if (angle > 180.0) { 1116 if (angle <= (180.0 + 360.0)) { 1117 angle = angle - 360.0; 1118 } else { 1119 angle = Math.IEEEremainder(angle, 360.0); 1120 // IEEEremainder can return -180 here for some input values... 1121 if (angle == -180.0) { 1122 angle = 180.0; 1123 } 1124 } 1125 } else if (angle <= -180.0) { 1126 if (angle > (-180.0 - 360.0)) { 1127 angle = angle + 360.0; 1128 } else { 1129 angle = Math.IEEEremainder(angle, 360.0); 1130 // IEEEremainder can return -180 here for some input values... 1131 if (angle == -180.0) { 1132 angle = 180.0; 1133 } 1134 } 1135 } 1136 return angle; 1137 } 1138 1139 /** 1140 * Determines whether or not the specified angle is within the 1141 * angular extents of the arc. 1142 * 1143 * @param angle The angle to test. 1144 * 1145 * @return <CODE>true</CODE> if the arc contains the angle, 1146 * <CODE>false</CODE> if the arc doesn't contain the angle. 1147 * @since 1.2 1148 */ 1149 public boolean containsAngle(double angle) { 1150 double angExt = getAngleExtent(); 1151 boolean backwards = (angExt < 0.0); 1152 if (backwards) { 1153 angExt = -angExt; 1154 } 1155 if (angExt >= 360.0) { 1156 return true; 1157 } 1158 angle = normalizeDegrees(angle) - normalizeDegrees(getAngleStart()); 1159 if (backwards) { 1160 angle = -angle; 1161 } 1162 if (angle < 0.0) { 1163 angle += 360.0; 1164 } 1165 1166 1167 return (angle >= 0.0) && (angle < angExt); 1168 } 1169 1170 /** 1171 * Determines whether or not the specified point is inside the boundary 1172 * of the arc. 1173 * 1174 * @param x The X coordinate of the point to test. 1175 * @param y The Y coordinate of the point to test. 1176 * 1177 * @return <CODE>true</CODE> if the point lies within the bound of 1178 * the arc, <CODE>false</CODE> if the point lies outside of the 1179 * arc's bounds. 1180 * @since 1.2 1181 */ 1182 public boolean contains(double x, double y) { 1183 // Normalize the coordinates compared to the ellipse 1184 // having a center at 0,0 and a radius of 0.5. 1185 double ellw = getWidth(); 1186 if (ellw <= 0.0) { 1187 return false; 1188 } 1189 double normx = (x - getX()) / ellw - 0.5; 1190 double ellh = getHeight(); 1191 if (ellh <= 0.0) { 1192 return false; 1193 } 1194 double normy = (y - getY()) / ellh - 0.5; 1195 double distSq = (normx * normx + normy * normy); 1196 if (distSq >= 0.25) { 1197 return false; 1198 } 1199 double angExt = Math.abs(getAngleExtent()); 1200 if (angExt >= 360.0) { 1201 return true; 1202 } 1203 boolean inarc = containsAngle(-Math.toDegrees(Math.atan2(normy, 1204 normx))); 1205 if (type == PIE) { 1206 return inarc; 1207 } 1208 // CHORD and OPEN behave the same way 1209 if (inarc) { 1210 if (angExt >= 180.0) { 1211 return true; 1212 } 1213 // point must be outside the "pie triangle" 1214 } else { 1215 if (angExt <= 180.0) { 1216 return false; 1217 } 1218 // point must be inside the "pie triangle" 1219 } 1220 // The point is inside the pie triangle iff it is on the same 1221 // side of the line connecting the ends of the arc as the center. 1222 double angle = Math.toRadians(-getAngleStart()); 1223 double x1 = Math.cos(angle); 1224 double y1 = Math.sin(angle); 1225 angle += Math.toRadians(-getAngleExtent()); 1226 double x2 = Math.cos(angle); 1227 double y2 = Math.sin(angle); 1228 boolean inside = (Line2D.relativeCCW(x1, y1, x2, y2, 2*normx, 2*normy) * 1229 Line2D.relativeCCW(x1, y1, x2, y2, 0, 0) >= 0); 1230 return inarc ? !inside : inside; 1231 } 1232 1233 /** 1234 * Determines whether or not the interior of the arc intersects 1235 * the interior of the specified rectangle. 1236 * 1237 * @param x The X coordinate of the rectangle's upper-left corner. 1238 * @param y The Y coordinate of the rectangle's upper-left corner. 1239 * @param w The width of the rectangle. 1240 * @param h The height of the rectangle. 1241 * 1242 * @return <CODE>true</CODE> if the arc intersects the rectangle, 1243 * <CODE>false</CODE> if the arc doesn't intersect the rectangle. 1244 * @since 1.2 1245 */ 1246 public boolean intersects(double x, double y, double w, double h) { 1247 1248 double aw = getWidth(); 1249 double ah = getHeight(); 1250 1251 if ( w <= 0 || h <= 0 || aw <= 0 || ah <= 0 ) { 1252 return false; 1253 } 1254 double ext = getAngleExtent(); 1255 if (ext == 0) { 1256 return false; 1257 } 1258 1259 double ax = getX(); 1260 double ay = getY(); 1261 double axw = ax + aw; 1262 double ayh = ay + ah; 1263 double xw = x + w; 1264 double yh = y + h; 1265 1266 // check bbox 1267 if (x >= axw || y >= ayh || xw <= ax || yh <= ay) { 1268 return false; 1269 } 1270 1271 // extract necessary data 1272 double axc = getCenterX(); 1273 double ayc = getCenterY(); 1274 Point2D sp = getStartPoint(); 1275 Point2D ep = getEndPoint(); 1276 double sx = sp.getX(); 1277 double sy = sp.getY(); 1278 double ex = ep.getX(); 1279 double ey = ep.getY(); 1280 1281 /* 1282 * Try to catch rectangles that intersect arc in areas 1283 * outside of rectagle with left top corner coordinates 1284 * (min(center x, start point x, end point x), 1285 * min(center y, start point y, end point y)) 1286 * and rigth bottom corner coordinates 1287 * (max(center x, start point x, end point x), 1288 * max(center y, start point y, end point y)). 1289 * So we'll check axis segments outside of rectangle above. 1290 */ 1291 if (ayc >= y && ayc <= yh) { // 0 and 180 1292 if ((sx < xw && ex < xw && axc < xw && 1293 axw > x && containsAngle(0)) || 1294 (sx > x && ex > x && axc > x && 1295 ax < xw && containsAngle(180))) { 1296 return true; 1297 } 1298 } 1299 if (axc >= x && axc <= xw) { // 90 and 270 1300 if ((sy > y && ey > y && ayc > y && 1301 ay < yh && containsAngle(90)) || 1302 (sy < yh && ey < yh && ayc < yh && 1303 ayh > y && containsAngle(270))) { 1304 return true; 1305 } 1306 } 1307 1308 /* 1309 * For PIE we should check intersection with pie slices; 1310 * also we should do the same for arcs with extent is greater 1311 * than 180, because we should cover case of rectangle, which 1312 * situated between center of arc and chord, but does not 1313 * intersect the chord. 1314 */ 1315 Rectangle2D rect = new Rectangle2D.Double(x, y, w, h); 1316 if (type == PIE || Math.abs(ext) > 180) { 1317 // for PIE: try to find intersections with pie slices 1318 if (rect.intersectsLine(axc, ayc, sx, sy) || 1319 rect.intersectsLine(axc, ayc, ex, ey)) { 1320 return true; 1321 } 1322 } else { 1323 // for CHORD and OPEN: try to find intersections with chord 1324 if (rect.intersectsLine(sx, sy, ex, ey)) { 1325 return true; 1326 } 1327 } 1328 1329 // finally check the rectangle corners inside the arc 1330 if (contains(x, y) || contains(x + w, y) || 1331 contains(x, y + h) || contains(x + w, y + h)) { 1332 return true; 1333 } 1334 1335 return false; 1336 } 1337 1338 /** 1339 * Determines whether or not the interior of the arc entirely contains 1340 * the specified rectangle. 1341 * 1342 * @param x The X coordinate of the rectangle's upper-left corner. 1343 * @param y The Y coordinate of the rectangle's upper-left corner. 1344 * @param w The width of the rectangle. 1345 * @param h The height of the rectangle. 1346 * 1347 * @return <CODE>true</CODE> if the arc contains the rectangle, 1348 * <CODE>false</CODE> if the arc doesn't contain the rectangle. 1349 * @since 1.2 1350 */ 1351 public boolean contains(double x, double y, double w, double h) { 1352 return contains(x, y, w, h, null); 1353 } 1354 1355 /** 1356 * Determines whether or not the interior of the arc entirely contains 1357 * the specified rectangle. 1358 * 1359 * @param r The <CODE>Rectangle2D</CODE> to test. 1360 * 1361 * @return <CODE>true</CODE> if the arc contains the rectangle, 1362 * <CODE>false</CODE> if the arc doesn't contain the rectangle. 1363 * @since 1.2 1364 */ 1365 public boolean contains(Rectangle2D r) { 1366 return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight(), r); 1367 } 1368 1369 private boolean contains(double x, double y, double w, double h, 1370 Rectangle2D origrect) { 1371 if (!(contains(x, y) && 1372 contains(x + w, y) && 1373 contains(x, y + h) && 1374 contains(x + w, y + h))) { 1375 return false; 1376 } 1377 // If the shape is convex then we have done all the testing 1378 // we need. Only PIE arcs can be concave and then only if 1379 // the angular extents are greater than 180 degrees. 1380 if (type != PIE || Math.abs(getAngleExtent()) <= 180.0) { 1381 return true; 1382 } 1383 // For a PIE shape we have an additional test for the case where 1384 // the angular extents are greater than 180 degrees and all four 1385 // rectangular corners are inside the shape but one of the 1386 // rectangle edges spans across the "missing wedge" of the arc. 1387 // We can test for this case by checking if the rectangle intersects 1388 // either of the pie angle segments. 1389 if (origrect == null) { 1390 origrect = new Rectangle2D.Double(x, y, w, h); 1391 } 1392 double halfW = getWidth() / 2.0; 1393 double halfH = getHeight() / 2.0; 1394 double xc = getX() + halfW; 1395 double yc = getY() + halfH; 1396 double angle = Math.toRadians(-getAngleStart()); 1397 double xe = xc + halfW * Math.cos(angle); 1398 double ye = yc + halfH * Math.sin(angle); 1399 if (origrect.intersectsLine(xc, yc, xe, ye)) { 1400 return false; 1401 } 1402 angle += Math.toRadians(-getAngleExtent()); 1403 xe = xc + halfW * Math.cos(angle); 1404 ye = yc + halfH * Math.sin(angle); 1405 return !origrect.intersectsLine(xc, yc, xe, ye); 1406 } 1407 1408 /** 1409 * Returns an iteration object that defines the boundary of the 1410 * arc. 1411 * This iterator is multithread safe. 1412 * <code>Arc2D</code> guarantees that 1413 * modifications to the geometry of the arc 1414 * do not affect any iterations of that geometry that 1415 * are already in process. 1416 * 1417 * @param at an optional <CODE>AffineTransform</CODE> to be applied 1418 * to the coordinates as they are returned in the iteration, or null 1419 * if the untransformed coordinates are desired. 1420 * 1421 * @return A <CODE>PathIterator</CODE> that defines the arc's boundary. 1422 * @since 1.2 1423 */ 1424 public PathIterator getPathIterator(AffineTransform at) { 1425 return new ArcIterator(this, at); 1426 } 1427 1428 /** 1429 * Returns the hashcode for this <code>Arc2D</code>. 1430 * @return the hashcode for this <code>Arc2D</code>. 1431 * @since 1.6 1432 */ 1433 public int hashCode() { 1434 long bits = java.lang.Double.doubleToLongBits(getX()); 1435 bits += java.lang.Double.doubleToLongBits(getY()) * 37; 1436 bits += java.lang.Double.doubleToLongBits(getWidth()) * 43; 1437 bits += java.lang.Double.doubleToLongBits(getHeight()) * 47; 1438 bits += java.lang.Double.doubleToLongBits(getAngleStart()) * 53; 1439 bits += java.lang.Double.doubleToLongBits(getAngleExtent()) * 59; 1440 bits += getArcType() * 61; 1441 return (((int) bits) ^ ((int) (bits >> 32))); 1442 } 1443 1444 /** 1445 * Determines whether or not the specified <code>Object</code> is 1446 * equal to this <code>Arc2D</code>. The specified 1447 * <code>Object</code> is equal to this <code>Arc2D</code> 1448 * if it is an instance of <code>Arc2D</code> and if its 1449 * location, size, arc extents and type are the same as this 1450 * <code>Arc2D</code>. 1451 * @param obj an <code>Object</code> to be compared with this 1452 * <code>Arc2D</code>. 1453 * @return <code>true</code> if <code>obj</code> is an instance 1454 * of <code>Arc2D</code> and has the same values; 1455 * <code>false</code> otherwise. 1456 * @since 1.6 1457 */ 1458 public boolean equals(Object obj) { 1459 if (obj == this) { 1460 return true; 1461 } 1462 if (obj instanceof Arc2D) { 1463 Arc2D a2d = (Arc2D) obj; 1464 return ((getX() == a2d.getX()) && 1465 (getY() == a2d.getY()) && 1466 (getWidth() == a2d.getWidth()) && 1467 (getHeight() == a2d.getHeight()) && 1468 (getAngleStart() == a2d.getAngleStart()) && 1469 (getAngleExtent() == a2d.getAngleExtent()) && 1470 (getArcType() == a2d.getArcType())); 1471 } 1472 return false; 1473 } 1474 }