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.awt.Shape; 29 import java.awt.Rectangle; 30 import java.util.Arrays; 31 import java.io.Serializable; 32 import sun.awt.geom.Curve; 33 34 import static java.lang.Math.abs; 35 import static java.lang.Math.max; 36 import static java.lang.Math.ulp; 37 38 /** 39 * The <code>CubicCurve2D</code> class defines a cubic parametric curve 40 * segment in {@code (x,y)} coordinate space. 41 * <p> 42 * This class is only the abstract superclass for all objects which 43 * store a 2D cubic curve segment. 44 * The actual storage representation of the coordinates is left to 45 * the subclass. 46 * 47 * @author Jim Graham 48 * @since 1.2 49 */ 50 public abstract class CubicCurve2D implements Shape, Cloneable { 51 52 /** 53 * A cubic parametric curve segment specified with 54 * {@code float} coordinates. 55 * @since 1.2 56 */ 57 public static class Float extends CubicCurve2D implements Serializable { 58 /** 59 * The X coordinate of the start point 60 * of the cubic curve segment. 61 * @since 1.2 62 * @serial 63 */ 64 public float x1; 65 66 /** 67 * The Y coordinate of the start point 68 * of the cubic curve segment. 69 * @since 1.2 70 * @serial 71 */ 72 public float y1; 73 74 /** 75 * The X coordinate of the first control point 76 * of the cubic curve segment. 77 * @since 1.2 78 * @serial 79 */ 80 public float ctrlx1; 81 82 /** 83 * The Y coordinate of the first control point 84 * of the cubic curve segment. 85 * @since 1.2 86 * @serial 87 */ 88 public float ctrly1; 89 90 /** 91 * The X coordinate of the second control point 92 * of the cubic curve segment. 93 * @since 1.2 94 * @serial 95 */ 96 public float ctrlx2; 97 98 /** 99 * The Y coordinate of the second control point 100 * of the cubic curve segment. 101 * @since 1.2 102 * @serial 103 */ 104 public float ctrly2; 105 106 /** 107 * The X coordinate of the end point 108 * of the cubic curve segment. 109 * @since 1.2 110 * @serial 111 */ 112 public float x2; 113 114 /** 115 * The Y coordinate of the end point 116 * of the cubic curve segment. 117 * @since 1.2 118 * @serial 119 */ 120 public float y2; 121 122 /** 123 * Constructs and initializes a CubicCurve with coordinates 124 * (0, 0, 0, 0, 0, 0, 0, 0). 125 * @since 1.2 126 */ 127 public Float() { 128 } 129 130 /** 131 * Constructs and initializes a {@code CubicCurve2D} from 132 * the specified {@code float} coordinates. 133 * 134 * @param x1 the X coordinate for the start point 135 * of the resulting {@code CubicCurve2D} 136 * @param y1 the Y coordinate for the start point 137 * of the resulting {@code CubicCurve2D} 138 * @param ctrlx1 the X coordinate for the first control point 139 * of the resulting {@code CubicCurve2D} 140 * @param ctrly1 the Y coordinate for the first control point 141 * of the resulting {@code CubicCurve2D} 142 * @param ctrlx2 the X coordinate for the second control point 143 * of the resulting {@code CubicCurve2D} 144 * @param ctrly2 the Y coordinate for the second control point 145 * of the resulting {@code CubicCurve2D} 146 * @param x2 the X coordinate for the end point 147 * of the resulting {@code CubicCurve2D} 148 * @param y2 the Y coordinate for the end point 149 * of the resulting {@code CubicCurve2D} 150 * @since 1.2 151 */ 152 public Float(float x1, float y1, 153 float ctrlx1, float ctrly1, 154 float ctrlx2, float ctrly2, 155 float x2, float y2) 156 { 157 setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2); 158 } 159 160 /** 161 * {@inheritDoc} 162 * @since 1.2 163 */ 164 public double getX1() { 165 return (double) x1; 166 } 167 168 /** 169 * {@inheritDoc} 170 * @since 1.2 171 */ 172 public double getY1() { 173 return (double) y1; 174 } 175 176 /** 177 * {@inheritDoc} 178 * @since 1.2 179 */ 180 public Point2D getP1() { 181 return new Point2D.Float(x1, y1); 182 } 183 184 /** 185 * {@inheritDoc} 186 * @since 1.2 187 */ 188 public double getCtrlX1() { 189 return (double) ctrlx1; 190 } 191 192 /** 193 * {@inheritDoc} 194 * @since 1.2 195 */ 196 public double getCtrlY1() { 197 return (double) ctrly1; 198 } 199 200 /** 201 * {@inheritDoc} 202 * @since 1.2 203 */ 204 public Point2D getCtrlP1() { 205 return new Point2D.Float(ctrlx1, ctrly1); 206 } 207 208 /** 209 * {@inheritDoc} 210 * @since 1.2 211 */ 212 public double getCtrlX2() { 213 return (double) ctrlx2; 214 } 215 216 /** 217 * {@inheritDoc} 218 * @since 1.2 219 */ 220 public double getCtrlY2() { 221 return (double) ctrly2; 222 } 223 224 /** 225 * {@inheritDoc} 226 * @since 1.2 227 */ 228 public Point2D getCtrlP2() { 229 return new Point2D.Float(ctrlx2, ctrly2); 230 } 231 232 /** 233 * {@inheritDoc} 234 * @since 1.2 235 */ 236 public double getX2() { 237 return (double) x2; 238 } 239 240 /** 241 * {@inheritDoc} 242 * @since 1.2 243 */ 244 public double getY2() { 245 return (double) y2; 246 } 247 248 /** 249 * {@inheritDoc} 250 * @since 1.2 251 */ 252 public Point2D getP2() { 253 return new Point2D.Float(x2, y2); 254 } 255 256 /** 257 * {@inheritDoc} 258 * @since 1.2 259 */ 260 public void setCurve(double x1, double y1, 261 double ctrlx1, double ctrly1, 262 double ctrlx2, double ctrly2, 263 double x2, double y2) 264 { 265 this.x1 = (float) x1; 266 this.y1 = (float) y1; 267 this.ctrlx1 = (float) ctrlx1; 268 this.ctrly1 = (float) ctrly1; 269 this.ctrlx2 = (float) ctrlx2; 270 this.ctrly2 = (float) ctrly2; 271 this.x2 = (float) x2; 272 this.y2 = (float) y2; 273 } 274 275 /** 276 * Sets the location of the end points and control points 277 * of this curve to the specified {@code float} coordinates. 278 * 279 * @param x1 the X coordinate used to set the start point 280 * of this {@code CubicCurve2D} 281 * @param y1 the Y coordinate used to set the start point 282 * of this {@code CubicCurve2D} 283 * @param ctrlx1 the X coordinate used to set the first control point 284 * of this {@code CubicCurve2D} 285 * @param ctrly1 the Y coordinate used to set the first control point 286 * of this {@code CubicCurve2D} 287 * @param ctrlx2 the X coordinate used to set the second control point 288 * of this {@code CubicCurve2D} 289 * @param ctrly2 the Y coordinate used to set the second control point 290 * of this {@code CubicCurve2D} 291 * @param x2 the X coordinate used to set the end point 292 * of this {@code CubicCurve2D} 293 * @param y2 the Y coordinate used to set the end point 294 * of this {@code CubicCurve2D} 295 * @since 1.2 296 */ 297 public void setCurve(float x1, float y1, 298 float ctrlx1, float ctrly1, 299 float ctrlx2, float ctrly2, 300 float x2, float y2) 301 { 302 this.x1 = x1; 303 this.y1 = y1; 304 this.ctrlx1 = ctrlx1; 305 this.ctrly1 = ctrly1; 306 this.ctrlx2 = ctrlx2; 307 this.ctrly2 = ctrly2; 308 this.x2 = x2; 309 this.y2 = y2; 310 } 311 312 /** 313 * {@inheritDoc} 314 * @since 1.2 315 */ 316 public Rectangle2D getBounds2D() { 317 float left = Math.min(Math.min(x1, x2), 318 Math.min(ctrlx1, ctrlx2)); 319 float top = Math.min(Math.min(y1, y2), 320 Math.min(ctrly1, ctrly2)); 321 float right = Math.max(Math.max(x1, x2), 322 Math.max(ctrlx1, ctrlx2)); 323 float bottom = Math.max(Math.max(y1, y2), 324 Math.max(ctrly1, ctrly2)); 325 return new Rectangle2D.Float(left, top, 326 right - left, bottom - top); 327 } 328 329 /* 330 * JDK 1.6 serialVersionUID 331 */ 332 private static final long serialVersionUID = -1272015596714244385L; 333 } 334 335 /** 336 * A cubic parametric curve segment specified with 337 * {@code double} coordinates. 338 * @since 1.2 339 */ 340 public static class Double extends CubicCurve2D implements Serializable { 341 /** 342 * The X coordinate of the start point 343 * of the cubic curve segment. 344 * @since 1.2 345 * @serial 346 */ 347 public double x1; 348 349 /** 350 * The Y coordinate of the start point 351 * of the cubic curve segment. 352 * @since 1.2 353 * @serial 354 */ 355 public double y1; 356 357 /** 358 * The X coordinate of the first control point 359 * of the cubic curve segment. 360 * @since 1.2 361 * @serial 362 */ 363 public double ctrlx1; 364 365 /** 366 * The Y coordinate of the first control point 367 * of the cubic curve segment. 368 * @since 1.2 369 * @serial 370 */ 371 public double ctrly1; 372 373 /** 374 * The X coordinate of the second control point 375 * of the cubic curve segment. 376 * @since 1.2 377 * @serial 378 */ 379 public double ctrlx2; 380 381 /** 382 * The Y coordinate of the second control point 383 * of the cubic curve segment. 384 * @since 1.2 385 * @serial 386 */ 387 public double ctrly2; 388 389 /** 390 * The X coordinate of the end point 391 * of the cubic curve segment. 392 * @since 1.2 393 * @serial 394 */ 395 public double x2; 396 397 /** 398 * The Y coordinate of the end point 399 * of the cubic curve segment. 400 * @since 1.2 401 * @serial 402 */ 403 public double y2; 404 405 /** 406 * Constructs and initializes a CubicCurve with coordinates 407 * (0, 0, 0, 0, 0, 0, 0, 0). 408 * @since 1.2 409 */ 410 public Double() { 411 } 412 413 /** 414 * Constructs and initializes a {@code CubicCurve2D} from 415 * the specified {@code double} coordinates. 416 * 417 * @param x1 the X coordinate for the start point 418 * of the resulting {@code CubicCurve2D} 419 * @param y1 the Y coordinate for the start point 420 * of the resulting {@code CubicCurve2D} 421 * @param ctrlx1 the X coordinate for the first control point 422 * of the resulting {@code CubicCurve2D} 423 * @param ctrly1 the Y coordinate for the first control point 424 * of the resulting {@code CubicCurve2D} 425 * @param ctrlx2 the X coordinate for the second control point 426 * of the resulting {@code CubicCurve2D} 427 * @param ctrly2 the Y coordinate for the second control point 428 * of the resulting {@code CubicCurve2D} 429 * @param x2 the X coordinate for the end point 430 * of the resulting {@code CubicCurve2D} 431 * @param y2 the Y coordinate for the end point 432 * of the resulting {@code CubicCurve2D} 433 * @since 1.2 434 */ 435 public Double(double x1, double y1, 436 double ctrlx1, double ctrly1, 437 double ctrlx2, double ctrly2, 438 double x2, double y2) 439 { 440 setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2); 441 } 442 443 /** 444 * {@inheritDoc} 445 * @since 1.2 446 */ 447 public double getX1() { 448 return x1; 449 } 450 451 /** 452 * {@inheritDoc} 453 * @since 1.2 454 */ 455 public double getY1() { 456 return y1; 457 } 458 459 /** 460 * {@inheritDoc} 461 * @since 1.2 462 */ 463 public Point2D getP1() { 464 return new Point2D.Double(x1, y1); 465 } 466 467 /** 468 * {@inheritDoc} 469 * @since 1.2 470 */ 471 public double getCtrlX1() { 472 return ctrlx1; 473 } 474 475 /** 476 * {@inheritDoc} 477 * @since 1.2 478 */ 479 public double getCtrlY1() { 480 return ctrly1; 481 } 482 483 /** 484 * {@inheritDoc} 485 * @since 1.2 486 */ 487 public Point2D getCtrlP1() { 488 return new Point2D.Double(ctrlx1, ctrly1); 489 } 490 491 /** 492 * {@inheritDoc} 493 * @since 1.2 494 */ 495 public double getCtrlX2() { 496 return ctrlx2; 497 } 498 499 /** 500 * {@inheritDoc} 501 * @since 1.2 502 */ 503 public double getCtrlY2() { 504 return ctrly2; 505 } 506 507 /** 508 * {@inheritDoc} 509 * @since 1.2 510 */ 511 public Point2D getCtrlP2() { 512 return new Point2D.Double(ctrlx2, ctrly2); 513 } 514 515 /** 516 * {@inheritDoc} 517 * @since 1.2 518 */ 519 public double getX2() { 520 return x2; 521 } 522 523 /** 524 * {@inheritDoc} 525 * @since 1.2 526 */ 527 public double getY2() { 528 return y2; 529 } 530 531 /** 532 * {@inheritDoc} 533 * @since 1.2 534 */ 535 public Point2D getP2() { 536 return new Point2D.Double(x2, y2); 537 } 538 539 /** 540 * {@inheritDoc} 541 * @since 1.2 542 */ 543 public void setCurve(double x1, double y1, 544 double ctrlx1, double ctrly1, 545 double ctrlx2, double ctrly2, 546 double x2, double y2) 547 { 548 this.x1 = x1; 549 this.y1 = y1; 550 this.ctrlx1 = ctrlx1; 551 this.ctrly1 = ctrly1; 552 this.ctrlx2 = ctrlx2; 553 this.ctrly2 = ctrly2; 554 this.x2 = x2; 555 this.y2 = y2; 556 } 557 558 /** 559 * {@inheritDoc} 560 * @since 1.2 561 */ 562 public Rectangle2D getBounds2D() { 563 double left = Math.min(Math.min(x1, x2), 564 Math.min(ctrlx1, ctrlx2)); 565 double top = Math.min(Math.min(y1, y2), 566 Math.min(ctrly1, ctrly2)); 567 double right = Math.max(Math.max(x1, x2), 568 Math.max(ctrlx1, ctrlx2)); 569 double bottom = Math.max(Math.max(y1, y2), 570 Math.max(ctrly1, ctrly2)); 571 return new Rectangle2D.Double(left, top, 572 right - left, bottom - top); 573 } 574 575 /* 576 * JDK 1.6 serialVersionUID 577 */ 578 private static final long serialVersionUID = -4202960122839707295L; 579 } 580 581 /** 582 * This is an abstract class that cannot be instantiated directly. 583 * Type-specific implementation subclasses are available for 584 * instantiation and provide a number of formats for storing 585 * the information necessary to satisfy the various accessor 586 * methods below. 587 * 588 * @see java.awt.geom.CubicCurve2D.Float 589 * @see java.awt.geom.CubicCurve2D.Double 590 * @since 1.2 591 */ 592 protected CubicCurve2D() { 593 } 594 595 /** 596 * Returns the X coordinate of the start point in double precision. 597 * @return the X coordinate of the start point of the 598 * {@code CubicCurve2D}. 599 * @since 1.2 600 */ 601 public abstract double getX1(); 602 603 /** 604 * Returns the Y coordinate of the start point in double precision. 605 * @return the Y coordinate of the start point of the 606 * {@code CubicCurve2D}. 607 * @since 1.2 608 */ 609 public abstract double getY1(); 610 611 /** 612 * Returns the start point. 613 * @return a {@code Point2D} that is the start point of 614 * the {@code CubicCurve2D}. 615 * @since 1.2 616 */ 617 public abstract Point2D getP1(); 618 619 /** 620 * Returns the X coordinate of the first control point in double precision. 621 * @return the X coordinate of the first control point of the 622 * {@code CubicCurve2D}. 623 * @since 1.2 624 */ 625 public abstract double getCtrlX1(); 626 627 /** 628 * Returns the Y coordinate of the first control point in double precision. 629 * @return the Y coordinate of the first control point of the 630 * {@code CubicCurve2D}. 631 * @since 1.2 632 */ 633 public abstract double getCtrlY1(); 634 635 /** 636 * Returns the first control point. 637 * @return a {@code Point2D} that is the first control point of 638 * the {@code CubicCurve2D}. 639 * @since 1.2 640 */ 641 public abstract Point2D getCtrlP1(); 642 643 /** 644 * Returns the X coordinate of the second control point 645 * in double precision. 646 * @return the X coordinate of the second control point of the 647 * {@code CubicCurve2D}. 648 * @since 1.2 649 */ 650 public abstract double getCtrlX2(); 651 652 /** 653 * Returns the Y coordinate of the second control point 654 * in double precision. 655 * @return the Y coordinate of the second control point of the 656 * {@code CubicCurve2D}. 657 * @since 1.2 658 */ 659 public abstract double getCtrlY2(); 660 661 /** 662 * Returns the second control point. 663 * @return a {@code Point2D} that is the second control point of 664 * the {@code CubicCurve2D}. 665 * @since 1.2 666 */ 667 public abstract Point2D getCtrlP2(); 668 669 /** 670 * Returns the X coordinate of the end point in double precision. 671 * @return the X coordinate of the end point of the 672 * {@code CubicCurve2D}. 673 * @since 1.2 674 */ 675 public abstract double getX2(); 676 677 /** 678 * Returns the Y coordinate of the end point in double precision. 679 * @return the Y coordinate of the end point of the 680 * {@code CubicCurve2D}. 681 * @since 1.2 682 */ 683 public abstract double getY2(); 684 685 /** 686 * Returns the end point. 687 * @return a {@code Point2D} that is the end point of 688 * the {@code CubicCurve2D}. 689 * @since 1.2 690 */ 691 public abstract Point2D getP2(); 692 693 /** 694 * Sets the location of the end points and control points of this curve 695 * to the specified double coordinates. 696 * 697 * @param x1 the X coordinate used to set the start point 698 * of this {@code CubicCurve2D} 699 * @param y1 the Y coordinate used to set the start point 700 * of this {@code CubicCurve2D} 701 * @param ctrlx1 the X coordinate used to set the first control point 702 * of this {@code CubicCurve2D} 703 * @param ctrly1 the Y coordinate used to set the first control point 704 * of this {@code CubicCurve2D} 705 * @param ctrlx2 the X coordinate used to set the second control point 706 * of this {@code CubicCurve2D} 707 * @param ctrly2 the Y coordinate used to set the second control point 708 * of this {@code CubicCurve2D} 709 * @param x2 the X coordinate used to set the end point 710 * of this {@code CubicCurve2D} 711 * @param y2 the Y coordinate used to set the end point 712 * of this {@code CubicCurve2D} 713 * @since 1.2 714 */ 715 public abstract void setCurve(double x1, double y1, 716 double ctrlx1, double ctrly1, 717 double ctrlx2, double ctrly2, 718 double x2, double y2); 719 720 /** 721 * Sets the location of the end points and control points of this curve 722 * to the double coordinates at the specified offset in the specified 723 * array. 724 * @param coords a double array containing coordinates 725 * @param offset the index of <code>coords</code> from which to begin 726 * setting the end points and control points of this curve 727 * to the coordinates contained in <code>coords</code> 728 * @since 1.2 729 */ 730 public void setCurve(double[] coords, int offset) { 731 setCurve(coords[offset + 0], coords[offset + 1], 732 coords[offset + 2], coords[offset + 3], 733 coords[offset + 4], coords[offset + 5], 734 coords[offset + 6], coords[offset + 7]); 735 } 736 737 /** 738 * Sets the location of the end points and control points of this curve 739 * to the specified <code>Point2D</code> coordinates. 740 * @param p1 the first specified <code>Point2D</code> used to set the 741 * start point of this curve 742 * @param cp1 the second specified <code>Point2D</code> used to set the 743 * first control point of this curve 744 * @param cp2 the third specified <code>Point2D</code> used to set the 745 * second control point of this curve 746 * @param p2 the fourth specified <code>Point2D</code> used to set the 747 * end point of this curve 748 * @since 1.2 749 */ 750 public void setCurve(Point2D p1, Point2D cp1, Point2D cp2, Point2D p2) { 751 setCurve(p1.getX(), p1.getY(), cp1.getX(), cp1.getY(), 752 cp2.getX(), cp2.getY(), p2.getX(), p2.getY()); 753 } 754 755 /** 756 * Sets the location of the end points and control points of this curve 757 * to the coordinates of the <code>Point2D</code> objects at the specified 758 * offset in the specified array. 759 * @param pts an array of <code>Point2D</code> objects 760 * @param offset the index of <code>pts</code> from which to begin setting 761 * the end points and control points of this curve to the 762 * points contained in <code>pts</code> 763 * @since 1.2 764 */ 765 public void setCurve(Point2D[] pts, int offset) { 766 setCurve(pts[offset + 0].getX(), pts[offset + 0].getY(), 767 pts[offset + 1].getX(), pts[offset + 1].getY(), 768 pts[offset + 2].getX(), pts[offset + 2].getY(), 769 pts[offset + 3].getX(), pts[offset + 3].getY()); 770 } 771 772 /** 773 * Sets the location of the end points and control points of this curve 774 * to the same as those in the specified <code>CubicCurve2D</code>. 775 * @param c the specified <code>CubicCurve2D</code> 776 * @since 1.2 777 */ 778 public void setCurve(CubicCurve2D c) { 779 setCurve(c.getX1(), c.getY1(), c.getCtrlX1(), c.getCtrlY1(), 780 c.getCtrlX2(), c.getCtrlY2(), c.getX2(), c.getY2()); 781 } 782 783 /** 784 * Returns the square of the flatness of the cubic curve specified 785 * by the indicated control points. The flatness is the maximum distance 786 * of a control point from the line connecting the end points. 787 * 788 * @param x1 the X coordinate that specifies the start point 789 * of a {@code CubicCurve2D} 790 * @param y1 the Y coordinate that specifies the start point 791 * of a {@code CubicCurve2D} 792 * @param ctrlx1 the X coordinate that specifies the first control point 793 * of a {@code CubicCurve2D} 794 * @param ctrly1 the Y coordinate that specifies the first control point 795 * of a {@code CubicCurve2D} 796 * @param ctrlx2 the X coordinate that specifies the second control point 797 * of a {@code CubicCurve2D} 798 * @param ctrly2 the Y coordinate that specifies the second control point 799 * of a {@code CubicCurve2D} 800 * @param x2 the X coordinate that specifies the end point 801 * of a {@code CubicCurve2D} 802 * @param y2 the Y coordinate that specifies the end point 803 * of a {@code CubicCurve2D} 804 * @return the square of the flatness of the {@code CubicCurve2D} 805 * represented by the specified coordinates. 806 * @since 1.2 807 */ 808 public static double getFlatnessSq(double x1, double y1, 809 double ctrlx1, double ctrly1, 810 double ctrlx2, double ctrly2, 811 double x2, double y2) { 812 return Math.max(Line2D.ptSegDistSq(x1, y1, x2, y2, ctrlx1, ctrly1), 813 Line2D.ptSegDistSq(x1, y1, x2, y2, ctrlx2, ctrly2)); 814 815 } 816 817 /** 818 * Returns the flatness of the cubic curve specified 819 * by the indicated control points. The flatness is the maximum distance 820 * of a control point from the line connecting the end points. 821 * 822 * @param x1 the X coordinate that specifies the start point 823 * of a {@code CubicCurve2D} 824 * @param y1 the Y coordinate that specifies the start point 825 * of a {@code CubicCurve2D} 826 * @param ctrlx1 the X coordinate that specifies the first control point 827 * of a {@code CubicCurve2D} 828 * @param ctrly1 the Y coordinate that specifies the first control point 829 * of a {@code CubicCurve2D} 830 * @param ctrlx2 the X coordinate that specifies the second control point 831 * of a {@code CubicCurve2D} 832 * @param ctrly2 the Y coordinate that specifies the second control point 833 * of a {@code CubicCurve2D} 834 * @param x2 the X coordinate that specifies the end point 835 * of a {@code CubicCurve2D} 836 * @param y2 the Y coordinate that specifies the end point 837 * of a {@code CubicCurve2D} 838 * @return the flatness of the {@code CubicCurve2D} 839 * represented by the specified coordinates. 840 * @since 1.2 841 */ 842 public static double getFlatness(double x1, double y1, 843 double ctrlx1, double ctrly1, 844 double ctrlx2, double ctrly2, 845 double x2, double y2) { 846 return Math.sqrt(getFlatnessSq(x1, y1, ctrlx1, ctrly1, 847 ctrlx2, ctrly2, x2, y2)); 848 } 849 850 /** 851 * Returns the square of the flatness of the cubic curve specified 852 * by the control points stored in the indicated array at the 853 * indicated index. The flatness is the maximum distance 854 * of a control point from the line connecting the end points. 855 * @param coords an array containing coordinates 856 * @param offset the index of <code>coords</code> from which to begin 857 * getting the end points and control points of the curve 858 * @return the square of the flatness of the <code>CubicCurve2D</code> 859 * specified by the coordinates in <code>coords</code> at 860 * the specified offset. 861 * @since 1.2 862 */ 863 public static double getFlatnessSq(double coords[], int offset) { 864 return getFlatnessSq(coords[offset + 0], coords[offset + 1], 865 coords[offset + 2], coords[offset + 3], 866 coords[offset + 4], coords[offset + 5], 867 coords[offset + 6], coords[offset + 7]); 868 } 869 870 /** 871 * Returns the flatness of the cubic curve specified 872 * by the control points stored in the indicated array at the 873 * indicated index. The flatness is the maximum distance 874 * of a control point from the line connecting the end points. 875 * @param coords an array containing coordinates 876 * @param offset the index of <code>coords</code> from which to begin 877 * getting the end points and control points of the curve 878 * @return the flatness of the <code>CubicCurve2D</code> 879 * specified by the coordinates in <code>coords</code> at 880 * the specified offset. 881 * @since 1.2 882 */ 883 public static double getFlatness(double coords[], int offset) { 884 return getFlatness(coords[offset + 0], coords[offset + 1], 885 coords[offset + 2], coords[offset + 3], 886 coords[offset + 4], coords[offset + 5], 887 coords[offset + 6], coords[offset + 7]); 888 } 889 890 /** 891 * Returns the square of the flatness of this curve. The flatness is the 892 * maximum distance of a control point from the line connecting the 893 * end points. 894 * @return the square of the flatness of this curve. 895 * @since 1.2 896 */ 897 public double getFlatnessSq() { 898 return getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(), 899 getCtrlX2(), getCtrlY2(), getX2(), getY2()); 900 } 901 902 /** 903 * Returns the flatness of this curve. The flatness is the 904 * maximum distance of a control point from the line connecting the 905 * end points. 906 * @return the flatness of this curve. 907 * @since 1.2 908 */ 909 public double getFlatness() { 910 return getFlatness(getX1(), getY1(), getCtrlX1(), getCtrlY1(), 911 getCtrlX2(), getCtrlY2(), getX2(), getY2()); 912 } 913 914 /** 915 * Subdivides this cubic curve and stores the resulting two 916 * subdivided curves into the left and right curve parameters. 917 * Either or both of the left and right objects may be the same 918 * as this object or null. 919 * @param left the cubic curve object for storing for the left or 920 * first half of the subdivided curve 921 * @param right the cubic curve object for storing for the right or 922 * second half of the subdivided curve 923 * @since 1.2 924 */ 925 public void subdivide(CubicCurve2D left, CubicCurve2D right) { 926 subdivide(this, left, right); 927 } 928 929 /** 930 * Subdivides the cubic curve specified by the <code>src</code> parameter 931 * and stores the resulting two subdivided curves into the 932 * <code>left</code> and <code>right</code> curve parameters. 933 * Either or both of the <code>left</code> and <code>right</code> objects 934 * may be the same as the <code>src</code> object or <code>null</code>. 935 * @param src the cubic curve to be subdivided 936 * @param left the cubic curve object for storing the left or 937 * first half of the subdivided curve 938 * @param right the cubic curve object for storing the right or 939 * second half of the subdivided curve 940 * @since 1.2 941 */ 942 public static void subdivide(CubicCurve2D src, 943 CubicCurve2D left, 944 CubicCurve2D right) { 945 double x1 = src.getX1(); 946 double y1 = src.getY1(); 947 double ctrlx1 = src.getCtrlX1(); 948 double ctrly1 = src.getCtrlY1(); 949 double ctrlx2 = src.getCtrlX2(); 950 double ctrly2 = src.getCtrlY2(); 951 double x2 = src.getX2(); 952 double y2 = src.getY2(); 953 double centerx = (ctrlx1 + ctrlx2) / 2.0; 954 double centery = (ctrly1 + ctrly2) / 2.0; 955 ctrlx1 = (x1 + ctrlx1) / 2.0; 956 ctrly1 = (y1 + ctrly1) / 2.0; 957 ctrlx2 = (x2 + ctrlx2) / 2.0; 958 ctrly2 = (y2 + ctrly2) / 2.0; 959 double ctrlx12 = (ctrlx1 + centerx) / 2.0; 960 double ctrly12 = (ctrly1 + centery) / 2.0; 961 double ctrlx21 = (ctrlx2 + centerx) / 2.0; 962 double ctrly21 = (ctrly2 + centery) / 2.0; 963 centerx = (ctrlx12 + ctrlx21) / 2.0; 964 centery = (ctrly12 + ctrly21) / 2.0; 965 if (left != null) { 966 left.setCurve(x1, y1, ctrlx1, ctrly1, 967 ctrlx12, ctrly12, centerx, centery); 968 } 969 if (right != null) { 970 right.setCurve(centerx, centery, ctrlx21, ctrly21, 971 ctrlx2, ctrly2, x2, y2); 972 } 973 } 974 975 /** 976 * Subdivides the cubic curve specified by the coordinates 977 * stored in the <code>src</code> array at indices <code>srcoff</code> 978 * through (<code>srcoff</code> + 7) and stores the 979 * resulting two subdivided curves into the two result arrays at the 980 * corresponding indices. 981 * Either or both of the <code>left</code> and <code>right</code> 982 * arrays may be <code>null</code> or a reference to the same array 983 * as the <code>src</code> array. 984 * Note that the last point in the first subdivided curve is the 985 * same as the first point in the second subdivided curve. Thus, 986 * it is possible to pass the same array for <code>left</code> 987 * and <code>right</code> and to use offsets, such as <code>rightoff</code> 988 * equals (<code>leftoff</code> + 6), in order 989 * to avoid allocating extra storage for this common point. 990 * @param src the array holding the coordinates for the source curve 991 * @param srcoff the offset into the array of the beginning of the 992 * the 6 source coordinates 993 * @param left the array for storing the coordinates for the first 994 * half of the subdivided curve 995 * @param leftoff the offset into the array of the beginning of the 996 * the 6 left coordinates 997 * @param right the array for storing the coordinates for the second 998 * half of the subdivided curve 999 * @param rightoff the offset into the array of the beginning of the 1000 * the 6 right coordinates 1001 * @since 1.2 1002 */ 1003 public static void subdivide(double src[], int srcoff, 1004 double left[], int leftoff, 1005 double right[], int rightoff) { 1006 double x1 = src[srcoff + 0]; 1007 double y1 = src[srcoff + 1]; 1008 double ctrlx1 = src[srcoff + 2]; 1009 double ctrly1 = src[srcoff + 3]; 1010 double ctrlx2 = src[srcoff + 4]; 1011 double ctrly2 = src[srcoff + 5]; 1012 double x2 = src[srcoff + 6]; 1013 double y2 = src[srcoff + 7]; 1014 if (left != null) { 1015 left[leftoff + 0] = x1; 1016 left[leftoff + 1] = y1; 1017 } 1018 if (right != null) { 1019 right[rightoff + 6] = x2; 1020 right[rightoff + 7] = y2; 1021 } 1022 x1 = (x1 + ctrlx1) / 2.0; 1023 y1 = (y1 + ctrly1) / 2.0; 1024 x2 = (x2 + ctrlx2) / 2.0; 1025 y2 = (y2 + ctrly2) / 2.0; 1026 double centerx = (ctrlx1 + ctrlx2) / 2.0; 1027 double centery = (ctrly1 + ctrly2) / 2.0; 1028 ctrlx1 = (x1 + centerx) / 2.0; 1029 ctrly1 = (y1 + centery) / 2.0; 1030 ctrlx2 = (x2 + centerx) / 2.0; 1031 ctrly2 = (y2 + centery) / 2.0; 1032 centerx = (ctrlx1 + ctrlx2) / 2.0; 1033 centery = (ctrly1 + ctrly2) / 2.0; 1034 if (left != null) { 1035 left[leftoff + 2] = x1; 1036 left[leftoff + 3] = y1; 1037 left[leftoff + 4] = ctrlx1; 1038 left[leftoff + 5] = ctrly1; 1039 left[leftoff + 6] = centerx; 1040 left[leftoff + 7] = centery; 1041 } 1042 if (right != null) { 1043 right[rightoff + 0] = centerx; 1044 right[rightoff + 1] = centery; 1045 right[rightoff + 2] = ctrlx2; 1046 right[rightoff + 3] = ctrly2; 1047 right[rightoff + 4] = x2; 1048 right[rightoff + 5] = y2; 1049 } 1050 } 1051 1052 /** 1053 * Solves the cubic whose coefficients are in the <code>eqn</code> 1054 * array and places the non-complex roots back into the same array, 1055 * returning the number of roots. The solved cubic is represented 1056 * by the equation: 1057 * <pre> 1058 * eqn = {c, b, a, d} 1059 * dx^3 + ax^2 + bx + c = 0 1060 * </pre> 1061 * A return value of -1 is used to distinguish a constant equation 1062 * that might be always 0 or never 0 from an equation that has no 1063 * zeroes. 1064 * @param eqn an array containing coefficients for a cubic 1065 * @return the number of roots, or -1 if the equation is a constant. 1066 * @since 1.2 1067 */ 1068 public static int solveCubic(double eqn[]) { 1069 return solveCubic(eqn, eqn); 1070 } 1071 1072 /** 1073 * Solve the cubic whose coefficients are in the <code>eqn</code> 1074 * array and place the non-complex roots into the <code>res</code> 1075 * array, returning the number of roots. 1076 * The cubic solved is represented by the equation: 1077 * eqn = {c, b, a, d} 1078 * dx^3 + ax^2 + bx + c = 0 1079 * A return value of -1 is used to distinguish a constant equation, 1080 * which may be always 0 or never 0, from an equation which has no 1081 * zeroes. 1082 * @param eqn the specified array of coefficients to use to solve 1083 * the cubic equation 1084 * @param res the array that contains the non-complex roots 1085 * resulting from the solution of the cubic equation 1086 * @return the number of roots, or -1 if the equation is a constant 1087 * @since 1.3 1088 */ 1089 public static int solveCubic(double eqn[], double res[]) { 1090 // From Graphics Gems: 1091 // http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c 1092 final double d = eqn[3]; 1093 if (d == 0) { 1094 return QuadCurve2D.solveQuadratic(eqn, res); 1095 } 1096 1097 /* normal form: x^3 + Ax^2 + Bx + C = 0 */ 1098 final double A = eqn[2] / d; 1099 final double B = eqn[1] / d; 1100 final double C = eqn[0] / d; 1101 1102 1103 // substitute x = y - A/3 to eliminate quadratic term: 1104 // x^3 +Px + Q = 0 1105 // 1106 // Since we actually need P/3 and Q/2 for all of the 1107 // calculations that follow, we will calculate 1108 // p = P/3 1109 // q = Q/2 1110 // instead and use those values for simplicity of the code. 1111 double sq_A = A * A; 1112 double p = 1.0/3 * (-1.0/3 * sq_A + B); 1113 double q = 1.0/2 * (2.0/27 * A * sq_A - 1.0/3 * A * B + C); 1114 1115 /* use Cardano's formula */ 1116 1117 double cb_p = p * p * p; 1118 double D = q * q + cb_p; 1119 1120 final double sub = 1.0/3 * A; 1121 1122 int num; 1123 if (D < 0) { /* Casus irreducibilis: three real solutions */ 1124 // see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method 1125 double phi = 1.0/3 * Math.acos(-q / Math.sqrt(-cb_p)); 1126 double t = 2 * Math.sqrt(-p); 1127 1128 if (res == eqn) { 1129 eqn = Arrays.copyOf(eqn, 4); 1130 } 1131 1132 res[ 0 ] = ( t * Math.cos(phi)); 1133 res[ 1 ] = (-t * Math.cos(phi + Math.PI / 3)); 1134 res[ 2 ] = (-t * Math.cos(phi - Math.PI / 3)); 1135 num = 3; 1136 1137 for (int i = 0; i < num; ++i) { 1138 res[ i ] -= sub; 1139 } 1140 1141 } else { 1142 // Please see the comment in fixRoots marked 'XXX' before changing 1143 // any of the code in this case. 1144 double sqrt_D = Math.sqrt(D); 1145 double u = Math.cbrt(sqrt_D - q); 1146 double v = - Math.cbrt(sqrt_D + q); 1147 double uv = u+v; 1148 1149 num = 1; 1150 1151 double err = 1200000000*ulp(abs(uv) + abs(sub)); 1152 if (iszero(D, err) || within(u, v, err)) { 1153 if (res == eqn) { 1154 eqn = Arrays.copyOf(eqn, 4); 1155 } 1156 res[1] = -(uv / 2) - sub; 1157 num = 2; 1158 } 1159 // this must be done after the potential Arrays.copyOf 1160 res[ 0 ] = uv - sub; 1161 } 1162 1163 if (num > 1) { // num == 3 || num == 2 1164 num = fixRoots(eqn, res, num); 1165 } 1166 if (num > 2 && (res[2] == res[1] || res[2] == res[0])) { 1167 num--; 1168 } 1169 if (num > 1 && res[1] == res[0]) { 1170 res[1] = res[--num]; // Copies res[2] to res[1] if needed 1171 } 1172 return num; 1173 } 1174 1175 // preconditions: eqn != res && eqn[3] != 0 && num > 1 1176 // This method tries to improve the accuracy of the roots of eqn (which 1177 // should be in res). It also might eliminate roots in res if it decideds 1178 // that they're not real roots. It will not check for roots that the 1179 // computation of res might have missed, so this method should only be 1180 // used when the roots in res have been computed using an algorithm 1181 // that never underestimates the number of roots (such as solveCubic above) 1182 private static int fixRoots(double[] eqn, double[] res, int num) { 1183 double[] intervals = {eqn[1], 2*eqn[2], 3*eqn[3]}; 1184 int critCount = QuadCurve2D.solveQuadratic(intervals, intervals); 1185 if (critCount == 2 && intervals[0] == intervals[1]) { 1186 critCount--; 1187 } 1188 if (critCount == 2 && intervals[0] > intervals[1]) { 1189 double tmp = intervals[0]; 1190 intervals[0] = intervals[1]; 1191 intervals[1] = tmp; 1192 } 1193 1194 // below we use critCount to possibly filter out roots that shouldn't 1195 // have been computed. We require that eqn[3] != 0, so eqn is a proper 1196 // cubic, which means that its limits at -/+inf are -/+inf or +/-inf. 1197 // Therefore, if critCount==2, the curve is shaped like a sideways S, 1198 // and it could have 1-3 roots. If critCount==0 it is monotonic, and 1199 // if critCount==1 it is monotonic with a single point where it is 1200 // flat. In the last 2 cases there can only be 1 root. So in cases 1201 // where num > 1 but critCount < 2, we eliminate all roots in res 1202 // except one. 1203 1204 if (num == 3) { 1205 double xe = getRootUpperBound(eqn); 1206 double x0 = -xe; 1207 1208 Arrays.sort(res, 0, num); 1209 if (critCount == 2) { 1210 // this just tries to improve the accuracy of the computed 1211 // roots using Newton's method. 1212 res[0] = refineRootWithHint(eqn, x0, intervals[0], res[0]); 1213 res[1] = refineRootWithHint(eqn, intervals[0], intervals[1], res[1]); 1214 res[2] = refineRootWithHint(eqn, intervals[1], xe, res[2]); 1215 return 3; 1216 } else if (critCount == 1) { 1217 // we only need fx0 and fxe for the sign of the polynomial 1218 // at -inf and +inf respectively, so we don't need to do 1219 // fx0 = solveEqn(eqn, 3, x0); fxe = solveEqn(eqn, 3, xe) 1220 double fxe = eqn[3]; 1221 double fx0 = -fxe; 1222 1223 double x1 = intervals[0]; 1224 double fx1 = solveEqn(eqn, 3, x1); 1225 1226 // if critCount == 1 or critCount == 0, but num == 3 then 1227 // something has gone wrong. This branch and the one below 1228 // would ideally never execute, but if they do we can't know 1229 // which of the computed roots is closest to the real root; 1230 // therefore, we can't use refineRootWithHint. But even if 1231 // we did know, being here most likely means that the 1232 // curve is very flat close to two of the computed roots 1233 // (or maybe even all three). This might make Newton's method 1234 // fail altogether, which would be a pain to detect and fix. 1235 // This is why we use a very stable bisection method. 1236 if (oppositeSigns(fx0, fx1)) { 1237 res[0] = bisectRootWithHint(eqn, x0, x1, res[0]); 1238 } else if (oppositeSigns(fx1, fxe)) { 1239 res[0] = bisectRootWithHint(eqn, x1, xe, res[2]); 1240 } else /* fx1 must be 0 */ { 1241 res[0] = x1; 1242 } 1243 // return 1 1244 } else if (critCount == 0) { 1245 res[0] = bisectRootWithHint(eqn, x0, xe, res[1]); 1246 // return 1 1247 } 1248 } else if (num == 2 && critCount == 2) { 1249 // XXX: here we assume that res[0] has better accuracy than res[1]. 1250 // This is true because this method is only used from solveCubic 1251 // which puts in res[0] the root that it would compute anyway even 1252 // if num==1. If this method is ever used from any other method, or 1253 // if the solveCubic implementation changes, this assumption should 1254 // be reevaluated, and the choice of goodRoot might have to become 1255 // goodRoot = (abs(eqn'(res[0])) > abs(eqn'(res[1]))) ? res[0] : res[1] 1256 // where eqn' is the derivative of eqn. 1257 double goodRoot = res[0]; 1258 double badRoot = res[1]; 1259 double x1 = intervals[0]; 1260 double x2 = intervals[1]; 1261 // If a cubic curve really has 2 roots, one of those roots must be 1262 // at a critical point. That can't be goodRoot, so we compute x to 1263 // be the farthest critical point from goodRoot. If there are two 1264 // roots, x must be the second one, so we evaluate eqn at x, and if 1265 // it is zero (or close enough) we put x in res[1] (or badRoot, if 1266 // |solveEqn(eqn, 3, badRoot)| < |solveEqn(eqn, 3, x)| but this 1267 // shouldn't happen often). 1268 double x = abs(x1 - goodRoot) > abs(x2 - goodRoot) ? x1 : x2; 1269 double fx = solveEqn(eqn, 3, x); 1270 1271 if (iszero(fx, 10000000*ulp(x))) { 1272 double badRootVal = solveEqn(eqn, 3, badRoot); 1273 res[1] = abs(badRootVal) < abs(fx) ? badRoot : x; 1274 return 2; 1275 } 1276 } // else there can only be one root - goodRoot, and it is already in res[0] 1277 1278 return 1; 1279 } 1280 1281 // use newton's method. 1282 private static double refineRootWithHint(double[] eqn, double min, double max, double t) { 1283 if (!inInterval(t, min, max)) { 1284 return t; 1285 } 1286 double[] deriv = {eqn[1], 2*eqn[2], 3*eqn[3]}; 1287 double origt = t; 1288 for (int i = 0; i < 3; i++) { 1289 double slope = solveEqn(deriv, 2, t); 1290 double y = solveEqn(eqn, 3, t); 1291 double delta = - (y / slope); 1292 double newt = t + delta; 1293 1294 if (slope == 0 || y == 0 || t == newt) { 1295 break; 1296 } 1297 1298 t = newt; 1299 } 1300 if (within(t, origt, 1000*ulp(origt)) && inInterval(t, min, max)) { 1301 return t; 1302 } 1303 return origt; 1304 } 1305 1306 private static double bisectRootWithHint(double[] eqn, double x0, double xe, double hint) { 1307 double delta1 = Math.min(abs(hint - x0) / 64, 0.0625); 1308 double delta2 = Math.min(abs(hint - xe) / 64, 0.0625); 1309 double x02 = hint - delta1; 1310 double xe2 = hint + delta2; 1311 double fx02 = solveEqn(eqn, 3, x02); 1312 double fxe2 = solveEqn(eqn, 3, xe2); 1313 while (oppositeSigns(fx02, fxe2)) { 1314 if (x02 >= xe2) { 1315 return x02; 1316 } 1317 x0 = x02; 1318 xe = xe2; 1319 delta1 /= 64; 1320 delta2 /= 64; 1321 x02 = hint - delta1; 1322 xe2 = hint + delta2; 1323 fx02 = solveEqn(eqn, 3, x02); 1324 fxe2 = solveEqn(eqn, 3, xe2); 1325 } 1326 if (fx02 == 0) { 1327 return x02; 1328 } 1329 if (fxe2 == 0) { 1330 return xe2; 1331 } 1332 1333 return bisectRoot(eqn, x0, xe); 1334 } 1335 1336 private static double bisectRoot(double[] eqn, double x0, double xe) { 1337 double fx0 = solveEqn(eqn, 3, x0); 1338 double m = x0 + (xe - x0) / 2; 1339 while (m != x0 && m != xe) { 1340 double fm = solveEqn(eqn, 3, m); 1341 if (fm == 0) { 1342 return m; 1343 } 1344 if (oppositeSigns(fx0, fm)) { 1345 xe = m; 1346 } else { 1347 fx0 = fm; 1348 x0 = m; 1349 } 1350 m = x0 + (xe-x0)/2; 1351 } 1352 return m; 1353 } 1354 1355 private static boolean inInterval(double t, double min, double max) { 1356 return min <= t && t <= max; 1357 } 1358 1359 private static boolean within(double x, double y, double err) { 1360 double d = y - x; 1361 return (d <= err && d >= -err); 1362 } 1363 1364 private static boolean iszero(double x, double err) { 1365 return within(x, 0, err); 1366 } 1367 1368 private static boolean oppositeSigns(double x1, double x2) { 1369 return (x1 < 0 && x2 > 0) || (x1 > 0 && x2 < 0); 1370 } 1371 1372 private static double solveEqn(double eqn[], int order, double t) { 1373 double v = eqn[order]; 1374 while (--order >= 0) { 1375 v = v * t + eqn[order]; 1376 } 1377 return v; 1378 } 1379 1380 /* 1381 * Computes M+1 where M is an upper bound for all the roots in of eqn. 1382 * See: http://en.wikipedia.org/wiki/Sturm%27s_theorem#Applications. 1383 * The above link doesn't contain a proof, but I [dlila] proved it myself 1384 * so the result is reliable. The proof isn't difficult, but it's a bit 1385 * long to include here. 1386 * Precondition: eqn must represent a cubic polynomial 1387 */ 1388 private static double getRootUpperBound(double[] eqn) { 1389 double d = eqn[3]; 1390 double a = eqn[2]; 1391 double b = eqn[1]; 1392 double c = eqn[0]; 1393 1394 double M = 1 + max(max(abs(a), abs(b)), abs(c)) / abs(d); 1395 M += ulp(M) + 1; 1396 return M; 1397 } 1398 1399 1400 /** 1401 * {@inheritDoc} 1402 * @since 1.2 1403 */ 1404 public boolean contains(double x, double y) { 1405 if (!(x * 0.0 + y * 0.0 == 0.0)) { 1406 /* Either x or y was infinite or NaN. 1407 * A NaN always produces a negative response to any test 1408 * and Infinity values cannot be "inside" any path so 1409 * they should return false as well. 1410 */ 1411 return false; 1412 } 1413 // We count the "Y" crossings to determine if the point is 1414 // inside the curve bounded by its closing line. 1415 double x1 = getX1(); 1416 double y1 = getY1(); 1417 double x2 = getX2(); 1418 double y2 = getY2(); 1419 int crossings = 1420 (Curve.pointCrossingsForLine(x, y, x1, y1, x2, y2) + 1421 Curve.pointCrossingsForCubic(x, y, 1422 x1, y1, 1423 getCtrlX1(), getCtrlY1(), 1424 getCtrlX2(), getCtrlY2(), 1425 x2, y2, 0)); 1426 return ((crossings & 1) == 1); 1427 } 1428 1429 /** 1430 * {@inheritDoc} 1431 * @since 1.2 1432 */ 1433 public boolean contains(Point2D p) { 1434 return contains(p.getX(), p.getY()); 1435 } 1436 1437 /** 1438 * {@inheritDoc} 1439 * @since 1.2 1440 */ 1441 public boolean intersects(double x, double y, double w, double h) { 1442 // Trivially reject non-existant rectangles 1443 if (w <= 0 || h <= 0) { 1444 return false; 1445 } 1446 1447 int numCrossings = rectCrossings(x, y, w, h); 1448 // the intended return value is 1449 // numCrossings != 0 || numCrossings == Curve.RECT_INTERSECTS 1450 // but if (numCrossings != 0) numCrossings == INTERSECTS won't matter 1451 // and if !(numCrossings != 0) then numCrossings == 0, so 1452 // numCrossings != RECT_INTERSECT 1453 return numCrossings != 0; 1454 } 1455 1456 /** 1457 * {@inheritDoc} 1458 * @since 1.2 1459 */ 1460 public boolean intersects(Rectangle2D r) { 1461 return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); 1462 } 1463 1464 /** 1465 * {@inheritDoc} 1466 * @since 1.2 1467 */ 1468 public boolean contains(double x, double y, double w, double h) { 1469 if (w <= 0 || h <= 0) { 1470 return false; 1471 } 1472 1473 int numCrossings = rectCrossings(x, y, w, h); 1474 return !(numCrossings == 0 || numCrossings == Curve.RECT_INTERSECTS); 1475 } 1476 1477 private int rectCrossings(double x, double y, double w, double h) { 1478 int crossings = 0; 1479 if (!(getX1() == getX2() && getY1() == getY2())) { 1480 crossings = Curve.rectCrossingsForLine(crossings, 1481 x, y, 1482 x+w, y+h, 1483 getX1(), getY1(), 1484 getX2(), getY2()); 1485 if (crossings == Curve.RECT_INTERSECTS) { 1486 return crossings; 1487 } 1488 } 1489 // we call this with the curve's direction reversed, because we wanted 1490 // to call rectCrossingsForLine first, because it's cheaper. 1491 return Curve.rectCrossingsForCubic(crossings, 1492 x, y, 1493 x+w, y+h, 1494 getX2(), getY2(), 1495 getCtrlX2(), getCtrlY2(), 1496 getCtrlX1(), getCtrlY1(), 1497 getX1(), getY1(), 0); 1498 } 1499 1500 /** 1501 * {@inheritDoc} 1502 * @since 1.2 1503 */ 1504 public boolean contains(Rectangle2D r) { 1505 return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); 1506 } 1507 1508 /** 1509 * {@inheritDoc} 1510 * @since 1.2 1511 */ 1512 public Rectangle getBounds() { 1513 return getBounds2D().getBounds(); 1514 } 1515 1516 /** 1517 * Returns an iteration object that defines the boundary of the 1518 * shape. 1519 * The iterator for this class is not multi-threaded safe, 1520 * which means that this <code>CubicCurve2D</code> class does not 1521 * guarantee that modifications to the geometry of this 1522 * <code>CubicCurve2D</code> object do not affect any iterations of 1523 * that geometry that are already in process. 1524 * @param at an optional <code>AffineTransform</code> to be applied to the 1525 * coordinates as they are returned in the iteration, or <code>null</code> 1526 * if untransformed coordinates are desired 1527 * @return the <code>PathIterator</code> object that returns the 1528 * geometry of the outline of this <code>CubicCurve2D</code>, one 1529 * segment at a time. 1530 * @since 1.2 1531 */ 1532 public PathIterator getPathIterator(AffineTransform at) { 1533 return new CubicIterator(this, at); 1534 } 1535 1536 /** 1537 * Return an iteration object that defines the boundary of the 1538 * flattened shape. 1539 * The iterator for this class is not multi-threaded safe, 1540 * which means that this <code>CubicCurve2D</code> class does not 1541 * guarantee that modifications to the geometry of this 1542 * <code>CubicCurve2D</code> object do not affect any iterations of 1543 * that geometry that are already in process. 1544 * @param at an optional <code>AffineTransform</code> to be applied to the 1545 * coordinates as they are returned in the iteration, or <code>null</code> 1546 * if untransformed coordinates are desired 1547 * @param flatness the maximum amount that the control points 1548 * for a given curve can vary from colinear before a subdivided 1549 * curve is replaced by a straight line connecting the end points 1550 * @return the <code>PathIterator</code> object that returns the 1551 * geometry of the outline of this <code>CubicCurve2D</code>, 1552 * one segment at a time. 1553 * @since 1.2 1554 */ 1555 public PathIterator getPathIterator(AffineTransform at, double flatness) { 1556 return new FlatteningPathIterator(getPathIterator(at), flatness); 1557 } 1558 1559 /** 1560 * Creates a new object of the same class as this object. 1561 * 1562 * @return a clone of this instance. 1563 * @exception OutOfMemoryError if there is not enough memory. 1564 * @see java.lang.Cloneable 1565 * @since 1.2 1566 */ 1567 public Object clone() { 1568 try { 1569 return super.clone(); 1570 } catch (CloneNotSupportedException e) { 1571 // this shouldn't happen, since we are Cloneable 1572 throw new InternalError(); 1573 } 1574 } 1575 }