Frames | No Frames |
1: /* CubicCurve2D.java -- represents a parameterized cubic curve in 2-D space 2: Copyright (C) 2002, 2003, 2004 Free Software Foundation 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package java.awt.geom; 39: 40: import java.awt.Rectangle; 41: import java.awt.Shape; 42: import java.util.NoSuchElementException; 43: 44: 45: /** 46: * A two-dimensional curve that is parameterized with a cubic 47: * function. 48: * 49: * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" 50: * alt="A drawing of a CubicCurve2D" /> 51: * 52: * @author Eric Blake (ebb9@email.byu.edu) 53: * @author Graydon Hoare (graydon@redhat.com) 54: * @author Sascha Brawer (brawer@dandelis.ch) 55: * @author Sven de Marothy (sven@physto.se) 56: * 57: * @since 1.2 58: */ 59: public abstract class CubicCurve2D implements Shape, Cloneable 60: { 61: private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0; 62: private static final double EPSILON = 1E-10; 63: 64: /** 65: * Constructs a new CubicCurve2D. Typical users will want to 66: * construct instances of a subclass, such as {@link 67: * CubicCurve2D.Float} or {@link CubicCurve2D.Double}. 68: */ 69: protected CubicCurve2D() 70: { 71: } 72: 73: /** 74: * Returns the <i>x</i> coordinate of the curve’s start 75: * point. 76: */ 77: public abstract double getX1(); 78: 79: /** 80: * Returns the <i>y</i> coordinate of the curve’s start 81: * point. 82: */ 83: public abstract double getY1(); 84: 85: /** 86: * Returns the curve’s start point. 87: */ 88: public abstract Point2D getP1(); 89: 90: /** 91: * Returns the <i>x</i> coordinate of the curve’s first 92: * control point. 93: */ 94: public abstract double getCtrlX1(); 95: 96: /** 97: * Returns the <i>y</i> coordinate of the curve’s first 98: * control point. 99: */ 100: public abstract double getCtrlY1(); 101: 102: /** 103: * Returns the curve’s first control point. 104: */ 105: public abstract Point2D getCtrlP1(); 106: 107: /** 108: * Returns the <i>x</i> coordinate of the curve’s second 109: * control point. 110: */ 111: public abstract double getCtrlX2(); 112: 113: /** 114: * Returns the <i>y</i> coordinate of the curve’s second 115: * control point. 116: */ 117: public abstract double getCtrlY2(); 118: 119: /** 120: * Returns the curve’s second control point. 121: */ 122: public abstract Point2D getCtrlP2(); 123: 124: /** 125: * Returns the <i>x</i> coordinate of the curve’s end 126: * point. 127: */ 128: public abstract double getX2(); 129: 130: /** 131: * Returns the <i>y</i> coordinate of the curve’s end 132: * point. 133: */ 134: public abstract double getY2(); 135: 136: /** 137: * Returns the curve’s end point. 138: */ 139: public abstract Point2D getP2(); 140: 141: /** 142: * Changes the curve geometry, separately specifying each coordinate 143: * value. 144: * 145: * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" 146: * alt="A drawing of a CubicCurve2D" /> 147: * 148: * @param x1 the <i>x</i> coordinate of the curve’s new start 149: * point. 150: * 151: * @param y1 the <i>y</i> coordinate of the curve’s new start 152: * point. 153: * 154: * @param cx1 the <i>x</i> coordinate of the curve’s new 155: * first control point. 156: * 157: * @param cy1 the <i>y</i> coordinate of the curve’s new 158: * first control point. 159: * 160: * @param cx2 the <i>x</i> coordinate of the curve’s new 161: * second control point. 162: * 163: * @param cy2 the <i>y</i> coordinate of the curve’s new 164: * second control point. 165: * 166: * @param x2 the <i>x</i> coordinate of the curve’s new end 167: * point. 168: * 169: * @param y2 the <i>y</i> coordinate of the curve’s new end 170: * point. 171: */ 172: public abstract void setCurve(double x1, double y1, double cx1, double cy1, 173: double cx2, double cy2, double x2, double y2); 174: 175: /** 176: * Changes the curve geometry, specifying coordinate values in an 177: * array. 178: * 179: * @param coords an array containing the new coordinate values. The 180: * <i>x</i> coordinate of the new start point is located at 181: * <code>coords[offset]</code>, its <i>y</i> coordinate at 182: * <code>coords[offset + 1]</code>. The <i>x</i> coordinate of the 183: * new first control point is located at <code>coords[offset + 184: * 2]</code>, its <i>y</i> coordinate at <code>coords[offset + 185: * 3]</code>. The <i>x</i> coordinate of the new second control 186: * point is located at <code>coords[offset + 4]</code>, its <i>y</i> 187: * coordinate at <code>coords[offset + 5]</code>. The <i>x</i> 188: * coordinate of the new end point is located at <code>coords[offset 189: * + 6]</code>, its <i>y</i> coordinate at <code>coords[offset + 190: * 7]</code>. 191: * 192: * @param offset the offset of the first coordinate value in 193: * <code>coords</code>. 194: */ 195: public void setCurve(double[] coords, int offset) 196: { 197: setCurve(coords[offset++], coords[offset++], coords[offset++], 198: coords[offset++], coords[offset++], coords[offset++], 199: coords[offset++], coords[offset++]); 200: } 201: 202: /** 203: * Changes the curve geometry, specifying coordinate values in 204: * separate Point objects. 205: * 206: * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" 207: * alt="A drawing of a CubicCurve2D" /> 208: * 209: * <p>The curve does not keep any reference to the passed point 210: * objects. Therefore, a later change to <code>p1</code>, 211: * <code>c1</code>, <code>c2</code> or <code>p2</code> will not 212: * affect the curve geometry. 213: * 214: * @param p1 the new start point. 215: * @param c1 the new first control point. 216: * @param c2 the new second control point. 217: * @param p2 the new end point. 218: */ 219: public void setCurve(Point2D p1, Point2D c1, Point2D c2, Point2D p2) 220: { 221: setCurve(p1.getX(), p1.getY(), c1.getX(), c1.getY(), c2.getX(), c2.getY(), 222: p2.getX(), p2.getY()); 223: } 224: 225: /** 226: * Changes the curve geometry, specifying coordinate values in an 227: * array of Point objects. 228: * 229: * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" 230: * alt="A drawing of a CubicCurve2D" /> 231: * 232: * <p>The curve does not keep references to the passed point 233: * objects. Therefore, a later change to the <code>pts</code> array 234: * or any of its elements will not affect the curve geometry. 235: * 236: * @param pts an array containing the points. The new start point 237: * is located at <code>pts[offset]</code>, the new first control 238: * point at <code>pts[offset + 1]</code>, the new second control 239: * point at <code>pts[offset + 2]</code>, and the new end point 240: * at <code>pts[offset + 3]</code>. 241: * 242: * @param offset the offset of the start point in <code>pts</code>. 243: */ 244: public void setCurve(Point2D[] pts, int offset) 245: { 246: setCurve(pts[offset].getX(), pts[offset++].getY(), pts[offset].getX(), 247: pts[offset++].getY(), pts[offset].getX(), pts[offset++].getY(), 248: pts[offset].getX(), pts[offset++].getY()); 249: } 250: 251: /** 252: * Changes the curve geometry to that of another curve. 253: * 254: * @param c the curve whose coordinates will be copied. 255: */ 256: public void setCurve(CubicCurve2D c) 257: { 258: setCurve(c.getX1(), c.getY1(), c.getCtrlX1(), c.getCtrlY1(), 259: c.getCtrlX2(), c.getCtrlY2(), c.getX2(), c.getY2()); 260: } 261: 262: /** 263: * Calculates the squared flatness of a cubic curve, directly 264: * specifying each coordinate value. The flatness is the maximal 265: * distance of a control point to the line between start and end 266: * point. 267: * 268: * <p><img src="doc-files/CubicCurve2D-4.png" width="350" height="180" 269: * alt="A drawing that illustrates the flatness" /> 270: * 271: * <p>In the above drawing, the straight line connecting start point 272: * P1 and end point P2 is depicted in gray. In comparison to C1, 273: * control point C2 is father away from the gray line. Therefore, 274: * the result will be the square of the distance between C2 and the 275: * gray line, i.e. the squared length of the red line. 276: * 277: * @param x1 the <i>x</i> coordinate of the start point P1. 278: * @param y1 the <i>y</i> coordinate of the start point P1. 279: * @param cx1 the <i>x</i> coordinate of the first control point C1. 280: * @param cy1 the <i>y</i> coordinate of the first control point C1. 281: * @param cx2 the <i>x</i> coordinate of the second control point C2. 282: * @param cy2 the <i>y</i> coordinate of the second control point C2. 283: * @param x2 the <i>x</i> coordinate of the end point P2. 284: * @param y2 the <i>y</i> coordinate of the end point P2. 285: */ 286: public static double getFlatnessSq(double x1, double y1, double cx1, 287: double cy1, double cx2, double cy2, 288: double x2, double y2) 289: { 290: return Math.max(Line2D.ptSegDistSq(x1, y1, x2, y2, cx1, cy1), 291: Line2D.ptSegDistSq(x1, y1, x2, y2, cx2, cy2)); 292: } 293: 294: /** 295: * Calculates the flatness of a cubic curve, directly specifying 296: * each coordinate value. The flatness is the maximal distance of a 297: * control point to the line between start and end point. 298: * 299: * <p><img src="doc-files/CubicCurve2D-4.png" width="350" height="180" 300: * alt="A drawing that illustrates the flatness" /> 301: * 302: * <p>In the above drawing, the straight line connecting start point 303: * P1 and end point P2 is depicted in gray. In comparison to C1, 304: * control point C2 is father away from the gray line. Therefore, 305: * the result will be the distance between C2 and the gray line, 306: * i.e. the length of the red line. 307: * 308: * @param x1 the <i>x</i> coordinate of the start point P1. 309: * @param y1 the <i>y</i> coordinate of the start point P1. 310: * @param cx1 the <i>x</i> coordinate of the first control point C1. 311: * @param cy1 the <i>y</i> coordinate of the first control point C1. 312: * @param cx2 the <i>x</i> coordinate of the second control point C2. 313: * @param cy2 the <i>y</i> coordinate of the second control point C2. 314: * @param x2 the <i>x</i> coordinate of the end point P2. 315: * @param y2 the <i>y</i> coordinate of the end point P2. 316: */ 317: public static double getFlatness(double x1, double y1, double cx1, 318: double cy1, double cx2, double cy2, 319: double x2, double y2) 320: { 321: return Math.sqrt(getFlatnessSq(x1, y1, cx1, cy1, cx2, cy2, x2, y2)); 322: } 323: 324: /** 325: * Calculates the squared flatness of a cubic curve, specifying the 326: * coordinate values in an array. The flatness is the maximal 327: * distance of a control point to the line between start and end 328: * point. 329: * 330: * <p><img src="doc-files/CubicCurve2D-4.png" width="350" height="180" 331: * alt="A drawing that illustrates the flatness" /> 332: * 333: * <p>In the above drawing, the straight line connecting start point 334: * P1 and end point P2 is depicted in gray. In comparison to C1, 335: * control point C2 is father away from the gray line. Therefore, 336: * the result will be the square of the distance between C2 and the 337: * gray line, i.e. the squared length of the red line. 338: * 339: * @param coords an array containing the coordinate values. The 340: * <i>x</i> coordinate of the start point P1 is located at 341: * <code>coords[offset]</code>, its <i>y</i> coordinate at 342: * <code>coords[offset + 1]</code>. The <i>x</i> coordinate of the 343: * first control point C1 is located at <code>coords[offset + 344: * 2]</code>, its <i>y</i> coordinate at <code>coords[offset + 345: * 3]</code>. The <i>x</i> coordinate of the second control point C2 346: * is located at <code>coords[offset + 4]</code>, its <i>y</i> 347: * coordinate at <code>coords[offset + 5]</code>. The <i>x</i> 348: * coordinate of the end point P2 is located at <code>coords[offset 349: * + 6]</code>, its <i>y</i> coordinate at <code>coords[offset + 350: * 7]</code>. 351: * 352: * @param offset the offset of the first coordinate value in 353: * <code>coords</code>. 354: */ 355: public static double getFlatnessSq(double[] coords, int offset) 356: { 357: return getFlatnessSq(coords[offset++], coords[offset++], coords[offset++], 358: coords[offset++], coords[offset++], coords[offset++], 359: coords[offset++], coords[offset++]); 360: } 361: 362: /** 363: * Calculates the flatness of a cubic curve, specifying the 364: * coordinate values in an array. The flatness is the maximal 365: * distance of a control point to the line between start and end 366: * point. 367: * 368: * <p><img src="doc-files/CubicCurve2D-4.png" width="350" height="180" 369: * alt="A drawing that illustrates the flatness" /> 370: * 371: * <p>In the above drawing, the straight line connecting start point 372: * P1 and end point P2 is depicted in gray. In comparison to C1, 373: * control point C2 is father away from the gray line. Therefore, 374: * the result will be the distance between C2 and the gray line, 375: * i.e. the length of the red line. 376: * 377: * @param coords an array containing the coordinate values. The 378: * <i>x</i> coordinate of the start point P1 is located at 379: * <code>coords[offset]</code>, its <i>y</i> coordinate at 380: * <code>coords[offset + 1]</code>. The <i>x</i> coordinate of the 381: * first control point C1 is located at <code>coords[offset + 382: * 2]</code>, its <i>y</i> coordinate at <code>coords[offset + 383: * 3]</code>. The <i>x</i> coordinate of the second control point C2 384: * is located at <code>coords[offset + 4]</code>, its <i>y</i> 385: * coordinate at <code>coords[offset + 5]</code>. The <i>x</i> 386: * coordinate of the end point P2 is located at <code>coords[offset 387: * + 6]</code>, its <i>y</i> coordinate at <code>coords[offset + 388: * 7]</code>. 389: * 390: * @param offset the offset of the first coordinate value in 391: * <code>coords</code>. 392: */ 393: public static double getFlatness(double[] coords, int offset) 394: { 395: return Math.sqrt(getFlatnessSq(coords[offset++], coords[offset++], 396: coords[offset++], coords[offset++], 397: coords[offset++], coords[offset++], 398: coords[offset++], coords[offset++])); 399: } 400: 401: /** 402: * Calculates the squared flatness of this curve. The flatness is 403: * the maximal distance of a control point to the line between start 404: * and end point. 405: * 406: * <p><img src="doc-files/CubicCurve2D-4.png" width="350" height="180" 407: * alt="A drawing that illustrates the flatness" /> 408: * 409: * <p>In the above drawing, the straight line connecting start point 410: * P1 and end point P2 is depicted in gray. In comparison to C1, 411: * control point C2 is father away from the gray line. Therefore, 412: * the result will be the square of the distance between C2 and the 413: * gray line, i.e. the squared length of the red line. 414: */ 415: public double getFlatnessSq() 416: { 417: return getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(), 418: getCtrlX2(), getCtrlY2(), getX2(), getY2()); 419: } 420: 421: /** 422: * Calculates the flatness of this curve. The flatness is the 423: * maximal distance of a control point to the line between start and 424: * end point. 425: * 426: * <p><img src="doc-files/CubicCurve2D-4.png" width="350" height="180" 427: * alt="A drawing that illustrates the flatness" /> 428: * 429: * <p>In the above drawing, the straight line connecting start point 430: * P1 and end point P2 is depicted in gray. In comparison to C1, 431: * control point C2 is father away from the gray line. Therefore, 432: * the result will be the distance between C2 and the gray line, 433: * i.e. the length of the red line. 434: */ 435: public double getFlatness() 436: { 437: return Math.sqrt(getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(), 438: getCtrlX2(), getCtrlY2(), getX2(), getY2())); 439: } 440: 441: /** 442: * Subdivides this curve into two halves. 443: * 444: * <p><img src="doc-files/CubicCurve2D-3.png" width="700" 445: * height="180" alt="A drawing that illustrates the effects of 446: * subdividing a CubicCurve2D" /> 447: * 448: * @param left a curve whose geometry will be set to the left half 449: * of this curve, or <code>null</code> if the caller is not 450: * interested in the left half. 451: * 452: * @param right a curve whose geometry will be set to the right half 453: * of this curve, or <code>null</code> if the caller is not 454: * interested in the right half. 455: */ 456: public void subdivide(CubicCurve2D left, CubicCurve2D right) 457: { 458: // Use empty slots at end to share single array. 459: double[] d = new double[] 460: { 461: getX1(), getY1(), getCtrlX1(), getCtrlY1(), getCtrlX2(), 462: getCtrlY2(), getX2(), getY2(), 0, 0, 0, 0, 0, 0 463: }; 464: subdivide(d, 0, d, 0, d, 6); 465: if (left != null) 466: left.setCurve(d, 0); 467: if (right != null) 468: right.setCurve(d, 6); 469: } 470: 471: /** 472: * Subdivides a cubic curve into two halves. 473: * 474: * <p><img src="doc-files/CubicCurve2D-3.png" width="700" 475: * height="180" alt="A drawing that illustrates the effects of 476: * subdividing a CubicCurve2D" /> 477: * 478: * @param src the curve to be subdivided. 479: * 480: * @param left a curve whose geometry will be set to the left half 481: * of <code>src</code>, or <code>null</code> if the caller is not 482: * interested in the left half. 483: * 484: * @param right a curve whose geometry will be set to the right half 485: * of <code>src</code>, or <code>null</code> if the caller is not 486: * interested in the right half. 487: */ 488: public static void subdivide(CubicCurve2D src, CubicCurve2D left, 489: CubicCurve2D right) 490: { 491: src.subdivide(left, right); 492: } 493: 494: /** 495: * Subdivides a cubic curve into two halves, passing all coordinates 496: * in an array. 497: * 498: * <p><img src="doc-files/CubicCurve2D-3.png" width="700" 499: * height="180" alt="A drawing that illustrates the effects of 500: * subdividing a CubicCurve2D" /> 501: * 502: * <p>The left end point and the right start point will always be 503: * identical. Memory-concious programmers thus may want to pass the 504: * same array for both <code>left</code> and <code>right</code>, and 505: * set <code>rightOff</code> to <code>leftOff + 6</code>. 506: * 507: * @param src an array containing the coordinates of the curve to be 508: * subdivided. The <i>x</i> coordinate of the start point P1 is 509: * located at <code>src[srcOff]</code>, its <i>y</i> at 510: * <code>src[srcOff + 1]</code>. The <i>x</i> coordinate of the 511: * first control point C1 is located at <code>src[srcOff + 512: * 2]</code>, its <i>y</i> at <code>src[srcOff + 3]</code>. The 513: * <i>x</i> coordinate of the second control point C2 is located at 514: * <code>src[srcOff + 4]</code>, its <i>y</i> at <code>src[srcOff + 515: * 5]</code>. The <i>x</i> coordinate of the end point is located at 516: * <code>src[srcOff + 6]</code>, its <i>y</i> at <code>src[srcOff + 517: * 7]</code>. 518: * 519: * @param srcOff an offset into <code>src</code>, specifying 520: * the index of the start point’s <i>x</i> coordinate. 521: * 522: * @param left an array that will receive the coordinates of the 523: * left half of <code>src</code>. It is acceptable to pass 524: * <code>src</code>. A caller who is not interested in the left half 525: * can pass <code>null</code>. 526: * 527: * @param leftOff an offset into <code>left</code>, specifying the 528: * index where the start point’s <i>x</i> coordinate will be 529: * stored. 530: * 531: * @param right an array that will receive the coordinates of the 532: * right half of <code>src</code>. It is acceptable to pass 533: * <code>src</code> or <code>left</code>. A caller who is not 534: * interested in the right half can pass <code>null</code>. 535: * 536: * @param rightOff an offset into <code>right</code>, specifying the 537: * index where the start point’s <i>x</i> coordinate will be 538: * stored. 539: */ 540: public static void subdivide(double[] src, int srcOff, double[] left, 541: int leftOff, double[] right, int rightOff) 542: { 543: // To understand this code, please have a look at the image 544: // "CubicCurve2D-3.png" in the sub-directory "doc-files". 545: double src_C1_x; 546: double src_C1_y; 547: double src_C2_x; 548: double src_C2_y; 549: double left_P1_x; 550: double left_P1_y; 551: double left_C1_x; 552: double left_C1_y; 553: double left_C2_x; 554: double left_C2_y; 555: double right_C1_x; 556: double right_C1_y; 557: double right_C2_x; 558: double right_C2_y; 559: double right_P2_x; 560: double right_P2_y; 561: double Mid_x; // Mid = left.P2 = right.P1 562: double Mid_y; // Mid = left.P2 = right.P1 563: 564: left_P1_x = src[srcOff]; 565: left_P1_y = src[srcOff + 1]; 566: src_C1_x = src[srcOff + 2]; 567: src_C1_y = src[srcOff + 3]; 568: src_C2_x = src[srcOff + 4]; 569: src_C2_y = src[srcOff + 5]; 570: right_P2_x = src[srcOff + 6]; 571: right_P2_y = src[srcOff + 7]; 572: 573: left_C1_x = (left_P1_x + src_C1_x) / 2; 574: left_C1_y = (left_P1_y + src_C1_y) / 2; 575: right_C2_x = (right_P2_x + src_C2_x) / 2; 576: right_C2_y = (right_P2_y + src_C2_y) / 2; 577: Mid_x = (src_C1_x + src_C2_x) / 2; 578: Mid_y = (src_C1_y + src_C2_y) / 2; 579: left_C2_x = (left_C1_x + Mid_x) / 2; 580: left_C2_y = (left_C1_y + Mid_y) / 2; 581: right_C1_x = (Mid_x + right_C2_x) / 2; 582: right_C1_y = (Mid_y + right_C2_y) / 2; 583: Mid_x = (left_C2_x + right_C1_x) / 2; 584: Mid_y = (left_C2_y + right_C1_y) / 2; 585: 586: if (left != null) 587: { 588: left[leftOff] = left_P1_x; 589: left[leftOff + 1] = left_P1_y; 590: left[leftOff + 2] = left_C1_x; 591: left[leftOff + 3] = left_C1_y; 592: left[leftOff + 4] = left_C2_x; 593: left[leftOff + 5] = left_C2_y; 594: left[leftOff + 6] = Mid_x; 595: left[leftOff + 7] = Mid_y; 596: } 597: 598: if (right != null) 599: { 600: right[rightOff] = Mid_x; 601: right[rightOff + 1] = Mid_y; 602: right[rightOff + 2] = right_C1_x; 603: right[rightOff + 3] = right_C1_y; 604: right[rightOff + 4] = right_C2_x; 605: right[rightOff + 5] = right_C2_y; 606: right[rightOff + 6] = right_P2_x; 607: right[rightOff + 7] = right_P2_y; 608: } 609: } 610: 611: /** 612: * Finds the non-complex roots of a cubic equation, placing the 613: * results into the same array as the equation coefficients. The 614: * following equation is being solved: 615: * 616: * <blockquote><code>eqn[3]</code> · <i>x</i><sup>3</sup> 617: * + <code>eqn[2]</code> · <i>x</i><sup>2</sup> 618: * + <code>eqn[1]</code> · <i>x</i> 619: * + <code>eqn[0]</code> 620: * = 0 621: * </blockquote> 622: * 623: * <p>For some background about solving cubic equations, see the 624: * article <a 625: * href="http://planetmath.org/encyclopedia/CubicFormula.html" 626: * >“Cubic Formula”</a> in <a 627: * href="http://planetmath.org/" >PlanetMath</a>. For an extensive 628: * library of numerical algorithms written in the C programming 629: * language, see the <a href= "http://www.gnu.org/software/gsl/">GNU 630: * Scientific Library</a>, from which this implementation was 631: * adapted. 632: * 633: * @param eqn an array with the coefficients of the equation. When 634: * this procedure has returned, <code>eqn</code> will contain the 635: * non-complex solutions of the equation, in no particular order. 636: * 637: * @return the number of non-complex solutions. A result of 0 638: * indicates that the equation has no non-complex solutions. A 639: * result of -1 indicates that the equation is constant (i.e., 640: * always or never zero). 641: * 642: * @see #solveCubic(double[], double[]) 643: * @see QuadCurve2D#solveQuadratic(double[],double[]) 644: * 645: * @author Brian Gough (bjg@network-theory.com) 646: * (original C implementation in the <a href= 647: * "http://www.gnu.org/software/gsl/">GNU Scientific Library</a>) 648: * 649: * @author Sascha Brawer (brawer@dandelis.ch) 650: * (adaptation to Java) 651: */ 652: public static int solveCubic(double[] eqn) 653: { 654: return solveCubic(eqn, eqn); 655: } 656: 657: /** 658: * Finds the non-complex roots of a cubic equation. The following 659: * equation is being solved: 660: * 661: * <blockquote><code>eqn[3]</code> · <i>x</i><sup>3</sup> 662: * + <code>eqn[2]</code> · <i>x</i><sup>2</sup> 663: * + <code>eqn[1]</code> · <i>x</i> 664: * + <code>eqn[0]</code> 665: * = 0 666: * </blockquote> 667: * 668: * <p>For some background about solving cubic equations, see the 669: * article <a 670: * href="http://planetmath.org/encyclopedia/CubicFormula.html" 671: * >“Cubic Formula”</a> in <a 672: * href="http://planetmath.org/" >PlanetMath</a>. For an extensive 673: * library of numerical algorithms written in the C programming 674: * language, see the <a href= "http://www.gnu.org/software/gsl/">GNU 675: * Scientific Library</a>, from which this implementation was 676: * adapted. 677: * 678: * @see QuadCurve2D#solveQuadratic(double[],double[]) 679: * 680: * @param eqn an array with the coefficients of the equation. 681: * 682: * @param res an array into which the non-complex roots will be 683: * stored. The results may be in an arbitrary order. It is safe to 684: * pass the same array object reference for both <code>eqn</code> 685: * and <code>res</code>. 686: * 687: * @return the number of non-complex solutions. A result of 0 688: * indicates that the equation has no non-complex solutions. A 689: * result of -1 indicates that the equation is constant (i.e., 690: * always or never zero). 691: * 692: * @author Brian Gough (bjg@network-theory.com) 693: * (original C implementation in the <a href= 694: * "http://www.gnu.org/software/gsl/">GNU Scientific Library</a>) 695: * 696: * @author Sascha Brawer (brawer@dandelis.ch) 697: * (adaptation to Java) 698: */ 699: public static int solveCubic(double[] eqn, double[] res) 700: { 701: // Adapted from poly/solve_cubic.c in the GNU Scientific Library 702: // (GSL), revision 1.7 of 2003-07-26. For the original source, see 703: // http://www.gnu.org/software/gsl/ 704: // 705: // Brian Gough, the author of that code, has granted the 706: // permission to use it in GNU Classpath under the GNU Classpath 707: // license, and has assigned the copyright to the Free Software 708: // Foundation. 709: // 710: // The Java implementation is very similar to the GSL code, but 711: // not a strict one-to-one copy. For example, GSL would sort the 712: // result. 713: 714: double a; 715: double b; 716: double c; 717: double q; 718: double r; 719: double Q; 720: double R; 721: double c3; 722: double Q3; 723: double R2; 724: double CR2; 725: double CQ3; 726: 727: // If the cubic coefficient is zero, we have a quadratic equation. 728: c3 = eqn[3]; 729: if (c3 == 0) 730: return QuadCurve2D.solveQuadratic(eqn, res); 731: 732: // Divide the equation by the cubic coefficient. 733: c = eqn[0] / c3; 734: b = eqn[1] / c3; 735: a = eqn[2] / c3; 736: 737: // We now need to solve x^3 + ax^2 + bx + c = 0. 738: q = a * a - 3 * b; 739: r = 2 * a * a * a - 9 * a * b + 27 * c; 740: 741: Q = q / 9; 742: R = r / 54; 743: 744: Q3 = Q * Q * Q; 745: R2 = R * R; 746: 747: CR2 = 729 * r * r; 748: CQ3 = 2916 * q * q * q; 749: 750: if (R == 0 && Q == 0) 751: { 752: // The GNU Scientific Library would return three identical 753: // solutions in this case. 754: res[0] = -a / 3; 755: return 1; 756: } 757: 758: if (CR2 == CQ3) 759: { 760: /* this test is actually R2 == Q3, written in a form suitable 761: for exact computation with integers */ 762: /* Due to finite precision some double roots may be missed, and 763: considered to be a pair of complex roots z = x +/- epsilon i 764: close to the real axis. */ 765: double sqrtQ = Math.sqrt(Q); 766: 767: if (R > 0) 768: { 769: res[0] = -2 * sqrtQ - a / 3; 770: res[1] = sqrtQ - a / 3; 771: } 772: else 773: { 774: res[0] = -sqrtQ - a / 3; 775: res[1] = 2 * sqrtQ - a / 3; 776: } 777: return 2; 778: } 779: 780: if (CR2 < CQ3) /* equivalent to R2 < Q3 */ 781: { 782: double sqrtQ = Math.sqrt(Q); 783: double sqrtQ3 = sqrtQ * sqrtQ * sqrtQ; 784: double theta = Math.acos(R / sqrtQ3); 785: double norm = -2 * sqrtQ; 786: res[0] = norm * Math.cos(theta / 3) - a / 3; 787: res[1] = norm * Math.cos((theta + 2.0 * Math.PI) / 3) - a / 3; 788: res[2] = norm * Math.cos((theta - 2.0 * Math.PI) / 3) - a / 3; 789: 790: // The GNU Scientific Library sorts the results. We don't. 791: return 3; 792: } 793: 794: double sgnR = (R >= 0 ? 1 : -1); 795: double A = -sgnR * Math.pow(Math.abs(R) + Math.sqrt(R2 - Q3), 1.0 / 3.0); 796: double B = Q / A; 797: res[0] = A + B - a / 3; 798: return 1; 799: } 800: 801: /** 802: * Determines whether a position lies inside the area bounded 803: * by the curve and the straight line connecting its end points. 804: * 805: * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" 806: * alt="A drawing of the area spanned by the curve" /> 807: * 808: * <p>The above drawing illustrates in which area points are 809: * considered “inside” a CubicCurve2D. 810: */ 811: public boolean contains(double x, double y) 812: { 813: if (! getBounds2D().contains(x, y)) 814: return false; 815: 816: return ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0); 817: } 818: 819: /** 820: * Determines whether a point lies inside the area bounded 821: * by the curve and the straight line connecting its end points. 822: * 823: * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" 824: * alt="A drawing of the area spanned by the curve" /> 825: * 826: * <p>The above drawing illustrates in which area points are 827: * considered “inside” a CubicCurve2D. 828: */ 829: public boolean contains(Point2D p) 830: { 831: return contains(p.getX(), p.getY()); 832: } 833: 834: /** 835: * Determines whether any part of a rectangle is inside the area bounded 836: * by the curve and the straight line connecting its end points. 837: * 838: * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" 839: * alt="A drawing of the area spanned by the curve" /> 840: * 841: * <p>The above drawing illustrates in which area points are 842: * considered “inside” in a CubicCurve2D. 843: * @see #contains(double, double) 844: */ 845: public boolean intersects(double x, double y, double w, double h) 846: { 847: if (! getBounds2D().contains(x, y, w, h)) 848: return false; 849: 850: /* Does any edge intersect? */ 851: if (getAxisIntersections(x, y, true, w) != 0 /* top */ 852: || getAxisIntersections(x, y + h, true, w) != 0 /* bottom */ 853: || getAxisIntersections(x + w, y, false, h) != 0 /* right */ 854: || getAxisIntersections(x, y, false, h) != 0) /* left */ 855: return true; 856: 857: /* No intersections, is any point inside? */ 858: if ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0) 859: return true; 860: 861: return false; 862: } 863: 864: /** 865: * Determines whether any part of a Rectangle2D is inside the area bounded 866: * by the curve and the straight line connecting its end points. 867: * @see #intersects(double, double, double, double) 868: */ 869: public boolean intersects(Rectangle2D r) 870: { 871: return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); 872: } 873: 874: /** 875: * Determine whether a rectangle is entirely inside the area that is bounded 876: * by the curve and the straight line connecting its end points. 877: * 878: * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" 879: * alt="A drawing of the area spanned by the curve" /> 880: * 881: * <p>The above drawing illustrates in which area points are 882: * considered “inside” a CubicCurve2D. 883: * @see #contains(double, double) 884: */ 885: public boolean contains(double x, double y, double w, double h) 886: { 887: if (! getBounds2D().intersects(x, y, w, h)) 888: return false; 889: 890: /* Does any edge intersect? */ 891: if (getAxisIntersections(x, y, true, w) != 0 /* top */ 892: || getAxisIntersections(x, y + h, true, w) != 0 /* bottom */ 893: || getAxisIntersections(x + w, y, false, h) != 0 /* right */ 894: || getAxisIntersections(x, y, false, h) != 0) /* left */ 895: return false; 896: 897: /* No intersections, is any point inside? */ 898: if ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0) 899: return true; 900: 901: return false; 902: } 903: 904: /** 905: * Determine whether a Rectangle2D is entirely inside the area that is 906: * bounded by the curve and the straight line connecting its end points. 907: * 908: * <p><img src="doc-files/CubicCurve2D-5.png" width="350" height="180" 909: * alt="A drawing of the area spanned by the curve" /> 910: * 911: * <p>The above drawing illustrates in which area points are 912: * considered “inside” a CubicCurve2D. 913: * @see #contains(double, double) 914: */ 915: public boolean contains(Rectangle2D r) 916: { 917: return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); 918: } 919: 920: /** 921: * Determines the smallest rectangle that encloses the 922: * curve’s start, end and control points. 923: */ 924: public Rectangle getBounds() 925: { 926: return getBounds2D().getBounds(); 927: } 928: 929: public PathIterator getPathIterator(final AffineTransform at) 930: { 931: return new PathIterator() 932: { 933: /** Current coordinate. */ 934: private int current = 0; 935: 936: public int getWindingRule() 937: { 938: return WIND_NON_ZERO; 939: } 940: 941: public boolean isDone() 942: { 943: return current >= 2; 944: } 945: 946: public void next() 947: { 948: current++; 949: } 950: 951: public int currentSegment(float[] coords) 952: { 953: int result; 954: switch (current) 955: { 956: case 0: 957: coords[0] = (float) getX1(); 958: coords[1] = (float) getY1(); 959: result = SEG_MOVETO; 960: break; 961: case 1: 962: coords[0] = (float) getCtrlX1(); 963: coords[1] = (float) getCtrlY1(); 964: coords[2] = (float) getCtrlX2(); 965: coords[3] = (float) getCtrlY2(); 966: coords[4] = (float) getX2(); 967: coords[5] = (float) getY2(); 968: result = SEG_CUBICTO; 969: break; 970: default: 971: throw new NoSuchElementException("cubic iterator out of bounds"); 972: } 973: if (at != null) 974: at.transform(coords, 0, coords, 0, 3); 975: return result; 976: } 977: 978: public int currentSegment(double[] coords) 979: { 980: int result; 981: switch (current) 982: { 983: case 0: 984: coords[0] = getX1(); 985: coords[1] = getY1(); 986: result = SEG_MOVETO; 987: break; 988: case 1: 989: coords[0] = getCtrlX1(); 990: coords[1] = getCtrlY1(); 991: coords[2] = getCtrlX2(); 992: coords[3] = getCtrlY2(); 993: coords[4] = getX2(); 994: coords[5] = getY2(); 995: result = SEG_CUBICTO; 996: break; 997: default: 998: throw new NoSuchElementException("cubic iterator out of bounds"); 999: } 1000: if (at != null) 1001: at.transform(coords, 0, coords, 0, 3); 1002: return result; 1003: } 1004: }; 1005: } 1006: 1007: public PathIterator getPathIterator(AffineTransform at, double flatness) 1008: { 1009: return new FlatteningPathIterator(getPathIterator(at), flatness); 1010: } 1011: 1012: /** 1013: * Create a new curve with the same contents as this one. 1014: * 1015: * @return the clone. 1016: */ 1017: public Object clone() 1018: { 1019: try 1020: { 1021: return super.clone(); 1022: } 1023: catch (CloneNotSupportedException e) 1024: { 1025: throw (Error) new InternalError().initCause(e); // Impossible 1026: } 1027: } 1028: 1029: /** 1030: * Helper method used by contains() and intersects() methods, that 1031: * returns the number of curve/line intersections on a given axis 1032: * extending from a certain point. 1033: * 1034: * @param x x coordinate of the origin point 1035: * @param y y coordinate of the origin point 1036: * @param useYaxis axis used, if true the positive Y axis is used, 1037: * false uses the positive X axis. 1038: * 1039: * This is an implementation of the line-crossings algorithm, 1040: * Detailed in an article on Eric Haines' page: 1041: * http://www.acm.org/tog/editors/erich/ptinpoly/ 1042: * 1043: * A special-case not adressed in this code is self-intersections 1044: * of the curve, e.g. if the axis intersects the self-itersection, 1045: * the degenerate roots of the polynomial will erroneously count as 1046: * a single intersection of the curve, and not two. 1047: */ 1048: private int getAxisIntersections(double x, double y, boolean useYaxis, 1049: double distance) 1050: { 1051: int nCrossings = 0; 1052: double a0; 1053: double a1; 1054: double a2; 1055: double a3; 1056: double b0; 1057: double b1; 1058: double b2; 1059: double b3; 1060: double[] r = new double[4]; 1061: int nRoots; 1062: 1063: a0 = a3 = 0.0; 1064: 1065: if (useYaxis) 1066: { 1067: a0 = getY1() - y; 1068: a1 = getCtrlY1() - y; 1069: a2 = getCtrlY2() - y; 1070: a3 = getY2() - y; 1071: b0 = getX1() - x; 1072: b1 = getCtrlX1() - x; 1073: b2 = getCtrlX2() - x; 1074: b3 = getX2() - x; 1075: } 1076: else 1077: { 1078: a0 = getX1() - x; 1079: a1 = getCtrlX1() - x; 1080: a2 = getCtrlX2() - x; 1081: a3 = getX2() - x; 1082: b0 = getY1() - y; 1083: b1 = getCtrlY1() - y; 1084: b2 = getCtrlY2() - y; 1085: b3 = getY2() - y; 1086: } 1087: 1088: /* If the axis intersects a start/endpoint, shift it up by some small 1089: amount to guarantee the line is 'inside' 1090: If this is not done, bad behaviour may result for points on that axis.*/ 1091: if (a0 == 0.0 || a3 == 0.0) 1092: { 1093: double small = getFlatness() * EPSILON; 1094: if (a0 == 0.0) 1095: a0 -= small; 1096: if (a3 == 0.0) 1097: a3 -= small; 1098: } 1099: 1100: if (useYaxis) 1101: { 1102: if (Line2D.linesIntersect(b0, a0, b3, a3, EPSILON, 0.0, distance, 0.0)) 1103: nCrossings++; 1104: } 1105: else 1106: { 1107: if (Line2D.linesIntersect(a0, b0, a3, b3, 0.0, EPSILON, 0.0, distance)) 1108: nCrossings++; 1109: } 1110: 1111: r[0] = a0; 1112: r[1] = 3 * (a1 - a0); 1113: r[2] = 3 * (a2 + a0 - 2 * a1); 1114: r[3] = a3 - 3 * a2 + 3 * a1 - a0; 1115: 1116: if ((nRoots = solveCubic(r)) != 0) 1117: for (int i = 0; i < nRoots; i++) 1118: { 1119: double t = r[i]; 1120: if (t >= 0.0 && t <= 1.0) 1121: { 1122: double crossing = -(t * t * t) * (b0 - 3 * b1 + 3 * b2 - b3) 1123: + 3 * t * t * (b0 - 2 * b1 + b2) 1124: + 3 * t * (b1 - b0) + b0; 1125: if (crossing > 0.0 && crossing <= distance) 1126: nCrossings++; 1127: } 1128: } 1129: 1130: return (nCrossings); 1131: } 1132: 1133: /** 1134: * A two-dimensional curve that is parameterized with a cubic 1135: * function and stores coordinate values in double-precision 1136: * floating-point format. 1137: * 1138: * @see CubicCurve2D.Float 1139: * 1140: * @author Eric Blake (ebb9@email.byu.edu) 1141: * @author Sascha Brawer (brawer@dandelis.ch) 1142: */ 1143: public static class Double extends CubicCurve2D 1144: { 1145: /** 1146: * The <i>x</i> coordinate of the curve’s start point. 1147: */ 1148: public double x1; 1149: 1150: /** 1151: * The <i>y</i> coordinate of the curve’s start point. 1152: */ 1153: public double y1; 1154: 1155: /** 1156: * The <i>x</i> coordinate of the curve’s first control point. 1157: */ 1158: public double ctrlx1; 1159: 1160: /** 1161: * The <i>y</i> coordinate of the curve’s first control point. 1162: */ 1163: public double ctrly1; 1164: 1165: /** 1166: * The <i>x</i> coordinate of the curve’s second control point. 1167: */ 1168: public double ctrlx2; 1169: 1170: /** 1171: * The <i>y</i> coordinate of the curve’s second control point. 1172: */ 1173: public double ctrly2; 1174: 1175: /** 1176: * The <i>x</i> coordinate of the curve’s end point. 1177: */ 1178: public double x2; 1179: 1180: /** 1181: * The <i>y</i> coordinate of the curve’s end point. 1182: */ 1183: public double y2; 1184: 1185: /** 1186: * Constructs a new CubicCurve2D that stores its coordinate values 1187: * in double-precision floating-point format. All points are 1188: * initially at position (0, 0). 1189: */ 1190: public Double() 1191: { 1192: } 1193: 1194: /** 1195: * Constructs a new CubicCurve2D that stores its coordinate values 1196: * in double-precision floating-point format, specifying the 1197: * initial position of each point. 1198: * 1199: * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" 1200: * alt="A drawing of a CubicCurve2D" /> 1201: * 1202: * @param x1 the <i>x</i> coordinate of the curve’s start 1203: * point. 1204: * 1205: * @param y1 the <i>y</i> coordinate of the curve’s start 1206: * point. 1207: * 1208: * @param cx1 the <i>x</i> coordinate of the curve’s first 1209: * control point. 1210: * 1211: * @param cy1 the <i>y</i> coordinate of the curve’s first 1212: * control point. 1213: * 1214: * @param cx2 the <i>x</i> coordinate of the curve’s second 1215: * control point. 1216: * 1217: * @param cy2 the <i>y</i> coordinate of the curve’s second 1218: * control point. 1219: * 1220: * @param x2 the <i>x</i> coordinate of the curve’s end 1221: * point. 1222: * 1223: * @param y2 the <i>y</i> coordinate of the curve’s end 1224: * point. 1225: */ 1226: public Double(double x1, double y1, double cx1, double cy1, double cx2, 1227: double cy2, double x2, double y2) 1228: { 1229: this.x1 = x1; 1230: this.y1 = y1; 1231: ctrlx1 = cx1; 1232: ctrly1 = cy1; 1233: ctrlx2 = cx2; 1234: ctrly2 = cy2; 1235: this.x2 = x2; 1236: this.y2 = y2; 1237: } 1238: 1239: /** 1240: * Returns the <i>x</i> coordinate of the curve’s start 1241: * point. 1242: */ 1243: public double getX1() 1244: { 1245: return x1; 1246: } 1247: 1248: /** 1249: * Returns the <i>y</i> coordinate of the curve’s start 1250: * point. 1251: */ 1252: public double getY1() 1253: { 1254: return y1; 1255: } 1256: 1257: /** 1258: * Returns the curve’s start point. 1259: */ 1260: public Point2D getP1() 1261: { 1262: return new Point2D.Double(x1, y1); 1263: } 1264: 1265: /** 1266: * Returns the <i>x</i> coordinate of the curve’s first 1267: * control point. 1268: */ 1269: public double getCtrlX1() 1270: { 1271: return ctrlx1; 1272: } 1273: 1274: /** 1275: * Returns the <i>y</i> coordinate of the curve’s first 1276: * control point. 1277: */ 1278: public double getCtrlY1() 1279: { 1280: return ctrly1; 1281: } 1282: 1283: /** 1284: * Returns the curve’s first control point. 1285: */ 1286: public Point2D getCtrlP1() 1287: { 1288: return new Point2D.Double(ctrlx1, ctrly1); 1289: } 1290: 1291: /** 1292: * Returns the <i>x</i> coordinate of the curve’s second 1293: * control point. 1294: */ 1295: public double getCtrlX2() 1296: { 1297: return ctrlx2; 1298: } 1299: 1300: /** 1301: * Returns the <i>y</i> coordinate of the curve’s second 1302: * control point. 1303: */ 1304: public double getCtrlY2() 1305: { 1306: return ctrly2; 1307: } 1308: 1309: /** 1310: * Returns the curve’s second control point. 1311: */ 1312: public Point2D getCtrlP2() 1313: { 1314: return new Point2D.Double(ctrlx2, ctrly2); 1315: } 1316: 1317: /** 1318: * Returns the <i>x</i> coordinate of the curve’s end 1319: * point. 1320: */ 1321: public double getX2() 1322: { 1323: return x2; 1324: } 1325: 1326: /** 1327: * Returns the <i>y</i> coordinate of the curve’s end 1328: * point. 1329: */ 1330: public double getY2() 1331: { 1332: return y2; 1333: } 1334: 1335: /** 1336: * Returns the curve’s end point. 1337: */ 1338: public Point2D getP2() 1339: { 1340: return new Point2D.Double(x2, y2); 1341: } 1342: 1343: /** 1344: * Changes the curve geometry, separately specifying each coordinate 1345: * value. 1346: * 1347: * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" 1348: * alt="A drawing of a CubicCurve2D" /> 1349: * 1350: * @param x1 the <i>x</i> coordinate of the curve’s new start 1351: * point. 1352: * 1353: * @param y1 the <i>y</i> coordinate of the curve’s new start 1354: * point. 1355: * 1356: * @param cx1 the <i>x</i> coordinate of the curve’s new 1357: * first control point. 1358: * 1359: * @param cy1 the <i>y</i> coordinate of the curve’s new 1360: * first control point. 1361: * 1362: * @param cx2 the <i>x</i> coordinate of the curve’s new 1363: * second control point. 1364: * 1365: * @param cy2 the <i>y</i> coordinate of the curve’s new 1366: * second control point. 1367: * 1368: * @param x2 the <i>x</i> coordinate of the curve’s new end 1369: * point. 1370: * 1371: * @param y2 the <i>y</i> coordinate of the curve’s new end 1372: * point. 1373: */ 1374: public void setCurve(double x1, double y1, double cx1, double cy1, 1375: double cx2, double cy2, double x2, double y2) 1376: { 1377: this.x1 = x1; 1378: this.y1 = y1; 1379: ctrlx1 = cx1; 1380: ctrly1 = cy1; 1381: ctrlx2 = cx2; 1382: ctrly2 = cy2; 1383: this.x2 = x2; 1384: this.y2 = y2; 1385: } 1386: 1387: /** 1388: * Determines the smallest rectangle that encloses the 1389: * curve’s start, end and control points. As the 1390: * illustration below shows, the invisible control points may cause 1391: * the bounds to be much larger than the area that is actually 1392: * covered by the curve. 1393: * 1394: * <p><img src="doc-files/CubicCurve2D-2.png" width="350" height="180" 1395: * alt="An illustration of the bounds of a CubicCurve2D" /> 1396: */ 1397: public Rectangle2D getBounds2D() 1398: { 1399: double nx1 = Math.min(Math.min(x1, ctrlx1), Math.min(ctrlx2, x2)); 1400: double ny1 = Math.min(Math.min(y1, ctrly1), Math.min(ctrly2, y2)); 1401: double nx2 = Math.max(Math.max(x1, ctrlx1), Math.max(ctrlx2, x2)); 1402: double ny2 = Math.max(Math.max(y1, ctrly1), Math.max(ctrly2, y2)); 1403: return new Rectangle2D.Double(nx1, ny1, nx2 - nx1, ny2 - ny1); 1404: } 1405: } 1406: 1407: /** 1408: * A two-dimensional curve that is parameterized with a cubic 1409: * function and stores coordinate values in single-precision 1410: * floating-point format. 1411: * 1412: * @see CubicCurve2D.Float 1413: * 1414: * @author Eric Blake (ebb9@email.byu.edu) 1415: * @author Sascha Brawer (brawer@dandelis.ch) 1416: */ 1417: public static class Float extends CubicCurve2D 1418: { 1419: /** 1420: * The <i>x</i> coordinate of the curve’s start point. 1421: */ 1422: public float x1; 1423: 1424: /** 1425: * The <i>y</i> coordinate of the curve’s start point. 1426: */ 1427: public float y1; 1428: 1429: /** 1430: * The <i>x</i> coordinate of the curve’s first control point. 1431: */ 1432: public float ctrlx1; 1433: 1434: /** 1435: * The <i>y</i> coordinate of the curve’s first control point. 1436: */ 1437: public float ctrly1; 1438: 1439: /** 1440: * The <i>x</i> coordinate of the curve’s second control point. 1441: */ 1442: public float ctrlx2; 1443: 1444: /** 1445: * The <i>y</i> coordinate of the curve’s second control point. 1446: */ 1447: public float ctrly2; 1448: 1449: /** 1450: * The <i>x</i> coordinate of the curve’s end point. 1451: */ 1452: public float x2; 1453: 1454: /** 1455: * The <i>y</i> coordinate of the curve’s end point. 1456: */ 1457: public float y2; 1458: 1459: /** 1460: * Constructs a new CubicCurve2D that stores its coordinate values 1461: * in single-precision floating-point format. All points are 1462: * initially at position (0, 0). 1463: */ 1464: public Float() 1465: { 1466: } 1467: 1468: /** 1469: * Constructs a new CubicCurve2D that stores its coordinate values 1470: * in single-precision floating-point format, specifying the 1471: * initial position of each point. 1472: * 1473: * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" 1474: * alt="A drawing of a CubicCurve2D" /> 1475: * 1476: * @param x1 the <i>x</i> coordinate of the curve’s start 1477: * point. 1478: * 1479: * @param y1 the <i>y</i> coordinate of the curve’s start 1480: * point. 1481: * 1482: * @param cx1 the <i>x</i> coordinate of the curve’s first 1483: * control point. 1484: * 1485: * @param cy1 the <i>y</i> coordinate of the curve’s first 1486: * control point. 1487: * 1488: * @param cx2 the <i>x</i> coordinate of the curve’s second 1489: * control point. 1490: * 1491: * @param cy2 the <i>y</i> coordinate of the curve’s second 1492: * control point. 1493: * 1494: * @param x2 the <i>x</i> coordinate of the curve’s end 1495: * point. 1496: * 1497: * @param y2 the <i>y</i> coordinate of the curve’s end 1498: * point. 1499: */ 1500: public Float(float x1, float y1, float cx1, float cy1, float cx2, 1501: float cy2, float x2, float y2) 1502: { 1503: this.x1 = x1; 1504: this.y1 = y1; 1505: ctrlx1 = cx1; 1506: ctrly1 = cy1; 1507: ctrlx2 = cx2; 1508: ctrly2 = cy2; 1509: this.x2 = x2; 1510: this.y2 = y2; 1511: } 1512: 1513: /** 1514: * Returns the <i>x</i> coordinate of the curve’s start 1515: * point. 1516: */ 1517: public double getX1() 1518: { 1519: return x1; 1520: } 1521: 1522: /** 1523: * Returns the <i>y</i> coordinate of the curve’s start 1524: * point. 1525: */ 1526: public double getY1() 1527: { 1528: return y1; 1529: } 1530: 1531: /** 1532: * Returns the curve’s start point. 1533: */ 1534: public Point2D getP1() 1535: { 1536: return new Point2D.Float(x1, y1); 1537: } 1538: 1539: /** 1540: * Returns the <i>x</i> coordinate of the curve’s first 1541: * control point. 1542: */ 1543: public double getCtrlX1() 1544: { 1545: return ctrlx1; 1546: } 1547: 1548: /** 1549: * Returns the <i>y</i> coordinate of the curve’s first 1550: * control point. 1551: */ 1552: public double getCtrlY1() 1553: { 1554: return ctrly1; 1555: } 1556: 1557: /** 1558: * Returns the curve’s first control point. 1559: */ 1560: public Point2D getCtrlP1() 1561: { 1562: return new Point2D.Float(ctrlx1, ctrly1); 1563: } 1564: 1565: /** 1566: * Returns the <i>s</i> coordinate of the curve’s second 1567: * control point. 1568: */ 1569: public double getCtrlX2() 1570: { 1571: return ctrlx2; 1572: } 1573: 1574: /** 1575: * Returns the <i>y</i> coordinate of the curve’s second 1576: * control point. 1577: */ 1578: public double getCtrlY2() 1579: { 1580: return ctrly2; 1581: } 1582: 1583: /** 1584: * Returns the curve’s second control point. 1585: */ 1586: public Point2D getCtrlP2() 1587: { 1588: return new Point2D.Float(ctrlx2, ctrly2); 1589: } 1590: 1591: /** 1592: * Returns the <i>x</i> coordinate of the curve’s end 1593: * point. 1594: */ 1595: public double getX2() 1596: { 1597: return x2; 1598: } 1599: 1600: /** 1601: * Returns the <i>y</i> coordinate of the curve’s end 1602: * point. 1603: */ 1604: public double getY2() 1605: { 1606: return y2; 1607: } 1608: 1609: /** 1610: * Returns the curve’s end point. 1611: */ 1612: public Point2D getP2() 1613: { 1614: return new Point2D.Float(x2, y2); 1615: } 1616: 1617: /** 1618: * Changes the curve geometry, separately specifying each coordinate 1619: * value as a double-precision floating-point number. 1620: * 1621: * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" 1622: * alt="A drawing of a CubicCurve2D" /> 1623: * 1624: * @param x1 the <i>x</i> coordinate of the curve’s new start 1625: * point. 1626: * 1627: * @param y1 the <i>y</i> coordinate of the curve’s new start 1628: * point. 1629: * 1630: * @param cx1 the <i>x</i> coordinate of the curve’s new 1631: * first control point. 1632: * 1633: * @param cy1 the <i>y</i> coordinate of the curve’s new 1634: * first control point. 1635: * 1636: * @param cx2 the <i>x</i> coordinate of the curve’s new 1637: * second control point. 1638: * 1639: * @param cy2 the <i>y</i> coordinate of the curve’s new 1640: * second control point. 1641: * 1642: * @param x2 the <i>x</i> coordinate of the curve’s new end 1643: * point. 1644: * 1645: * @param y2 the <i>y</i> coordinate of the curve’s new end 1646: * point. 1647: */ 1648: public void setCurve(double x1, double y1, double cx1, double cy1, 1649: double cx2, double cy2, double x2, double y2) 1650: { 1651: this.x1 = (float) x1; 1652: this.y1 = (float) y1; 1653: ctrlx1 = (float) cx1; 1654: ctrly1 = (float) cy1; 1655: ctrlx2 = (float) cx2; 1656: ctrly2 = (float) cy2; 1657: this.x2 = (float) x2; 1658: this.y2 = (float) y2; 1659: } 1660: 1661: /** 1662: * Changes the curve geometry, separately specifying each coordinate 1663: * value as a single-precision floating-point number. 1664: * 1665: * <p><img src="doc-files/CubicCurve2D-1.png" width="350" height="180" 1666: * alt="A drawing of a CubicCurve2D" /> 1667: * 1668: * @param x1 the <i>x</i> coordinate of the curve’s new start 1669: * point. 1670: * 1671: * @param y1 the <i>y</i> coordinate of the curve’s new start 1672: * point. 1673: * 1674: * @param cx1 the <i>x</i> coordinate of the curve’s new 1675: * first control point. 1676: * 1677: * @param cy1 the <i>y</i> coordinate of the curve’s new 1678: * first control point. 1679: * 1680: * @param cx2 the <i>x</i> coordinate of the curve’s new 1681: * second control point. 1682: * 1683: * @param cy2 the <i>y</i> coordinate of the curve’s new 1684: * second control point. 1685: * 1686: * @param x2 the <i>x</i> coordinate of the curve’s new end 1687: * point. 1688: * 1689: * @param y2 the <i>y</i> coordinate of the curve’s new end 1690: * point. 1691: */ 1692: public void setCurve(float x1, float y1, float cx1, float cy1, float cx2, 1693: float cy2, float x2, float y2) 1694: { 1695: this.x1 = x1; 1696: this.y1 = y1; 1697: ctrlx1 = cx1; 1698: ctrly1 = cy1; 1699: ctrlx2 = cx2; 1700: ctrly2 = cy2; 1701: this.x2 = x2; 1702: this.y2 = y2; 1703: } 1704: 1705: /** 1706: * Determines the smallest rectangle that encloses the 1707: * curve’s start, end and control points. As the 1708: * illustration below shows, the invisible control points may cause 1709: * the bounds to be much larger than the area that is actually 1710: * covered by the curve. 1711: * 1712: * <p><img src="doc-files/CubicCurve2D-2.png" width="350" height="180" 1713: * alt="An illustration of the bounds of a CubicCurve2D" /> 1714: */ 1715: public Rectangle2D getBounds2D() 1716: { 1717: float nx1 = Math.min(Math.min(x1, ctrlx1), Math.min(ctrlx2, x2)); 1718: float ny1 = Math.min(Math.min(y1, ctrly1), Math.min(ctrly2, y2)); 1719: float nx2 = Math.max(Math.max(x1, ctrlx1), Math.max(ctrlx2, x2)); 1720: float ny2 = Math.max(Math.max(y1, ctrly1), Math.max(ctrly2, y2)); 1721: return new Rectangle2D.Float(nx1, ny1, nx2 - nx1, ny2 - ny1); 1722: } 1723: } 1724: }