1 /*
   2  * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.awt.geom;
  27 
  28 import java.awt.geom.Rectangle2D;
  29 import java.awt.geom.PathIterator;
  30 import java.awt.geom.QuadCurve2D;
  31 import java.util.Vector;
  32 
  33 final class Order3 extends Curve {
  34     private double x0;
  35     private double y0;
  36     private double cx0;
  37     private double cy0;
  38     private double cx1;
  39     private double cy1;
  40     private double x1;
  41     private double y1;
  42 
  43     private double xmin;
  44     private double xmax;
  45 
  46     private double xcoeff0;
  47     private double xcoeff1;
  48     private double xcoeff2;
  49     private double xcoeff3;
  50 
  51     private double ycoeff0;
  52     private double ycoeff1;
  53     private double ycoeff2;
  54     private double ycoeff3;
  55 
  56     public static void insert(Vector curves, double tmp[],
  57                               double x0, double y0,
  58                               double cx0, double cy0,
  59                               double cx1, double cy1,
  60                               double x1, double y1,
  61                               int direction)
  62     {
  63         int numparams = getHorizontalParams(y0, cy0, cy1, y1, tmp);
  64         if (numparams == 0) {
  65             // We are using addInstance here to avoid inserting horisontal
  66             // segments
  67             addInstance(curves, x0, y0, cx0, cy0, cx1, cy1, x1, y1, direction);
  68             return;
  69         }
  70         // Store coordinates for splitting at tmp[3..10]
  71         tmp[3] = x0;  tmp[4]  = y0;
  72         tmp[5] = cx0; tmp[6]  = cy0;
  73         tmp[7] = cx1; tmp[8]  = cy1;
  74         tmp[9] = x1;  tmp[10] = y1;
  75         double t = tmp[0];
  76         if (numparams > 1 && t > tmp[1]) {
  77             // Perform a "2 element sort"...
  78             tmp[0] = tmp[1];
  79             tmp[1] = t;
  80             t = tmp[0];
  81         }
  82         split(tmp, 3, t);
  83         if (numparams > 1) {
  84             // Recalculate tmp[1] relative to the range [tmp[0]...1]
  85             t = (tmp[1] - t) / (1 - t);
  86             split(tmp, 9, t);
  87         }
  88         int index = 3;
  89         if (direction == DECREASING) {
  90             index += numparams * 6;
  91         }
  92         while (numparams >= 0) {
  93             addInstance(curves,
  94                         tmp[index + 0], tmp[index + 1],
  95                         tmp[index + 2], tmp[index + 3],
  96                         tmp[index + 4], tmp[index + 5],
  97                         tmp[index + 6], tmp[index + 7],
  98                         direction);
  99             numparams--;
 100             if (direction == INCREASING) {
 101                 index += 6;
 102             } else {
 103                 index -= 6;
 104             }
 105         }
 106     }
 107 
 108     public static void addInstance(Vector curves,
 109                                    double x0, double y0,
 110                                    double cx0, double cy0,
 111                                    double cx1, double cy1,
 112                                    double x1, double y1,
 113                                    int direction) {
 114         if (y0 > y1) {
 115             curves.add(new Order3(x1, y1, cx1, cy1, cx0, cy0, x0, y0,
 116                                   -direction));
 117         } else if (y1 > y0) {
 118             curves.add(new Order3(x0, y0, cx0, cy0, cx1, cy1, x1, y1,
 119                                   direction));
 120         }
 121     }
 122 
 123     /*
 124      * Return the count of the number of horizontal sections of the
 125      * specified cubic Bezier curve.  Put the parameters for the
 126      * horizontal sections into the specified <code>ret</code> array.
 127      * <p>
 128      * If we examine the parametric equation in t, we have:
 129      *   Py(t) = C0(1-t)^3 + 3CP0 t(1-t)^2 + 3CP1 t^2(1-t) + C1 t^3
 130      *         = C0 - 3C0t + 3C0t^2 - C0t^3 +
 131      *           3CP0t - 6CP0t^2 + 3CP0t^3 +
 132      *           3CP1t^2 - 3CP1t^3 +
 133      *           C1t^3
 134      *   Py(t) = (C1 - 3CP1 + 3CP0 - C0) t^3 +
 135      *           (3C0 - 6CP0 + 3CP1) t^2 +
 136      *           (3CP0 - 3C0) t +
 137      *           (C0)
 138      * If we take the derivative, we get:
 139      *   Py(t) = Dt^3 + At^2 + Bt + C
 140      *   dPy(t) = 3Dt^2 + 2At + B = 0
 141      *        0 = 3*(C1 - 3*CP1 + 3*CP0 - C0)t^2
 142      *          + 2*(3*CP1 - 6*CP0 + 3*C0)t
 143      *          + (3*CP0 - 3*C0)
 144      *        0 = 3*(C1 - 3*CP1 + 3*CP0 - C0)t^2
 145      *          + 3*2*(CP1 - 2*CP0 + C0)t
 146      *          + 3*(CP0 - C0)
 147      *        0 = (C1 - CP1 - CP1 - CP1 + CP0 + CP0 + CP0 - C0)t^2
 148      *          + 2*(CP1 - CP0 - CP0 + C0)t
 149      *          + (CP0 - C0)
 150      *        0 = (C1 - CP1 + CP0 - CP1 + CP0 - CP1 + CP0 - C0)t^2
 151      *          + 2*(CP1 - CP0 - CP0 + C0)t
 152      *          + (CP0 - C0)
 153      *        0 = ((C1 - CP1) - (CP1 - CP0) - (CP1 - CP0) + (CP0 - C0))t^2
 154      *          + 2*((CP1 - CP0) - (CP0 - C0))t
 155      *          + (CP0 - C0)
 156      * Note that this method will return 0 if the equation is a line,
 157      * which is either always horizontal or never horizontal.
 158      * Completely horizontal curves need to be eliminated by other
 159      * means outside of this method.
 160      */
 161     public static int getHorizontalParams(double c0, double cp0,
 162                                           double cp1, double c1,
 163                                           double ret[]) {
 164         if (c0 <= cp0 && cp0 <= cp1 && cp1 <= c1) {
 165             return 0;
 166         }
 167         c1 -= cp1;
 168         cp1 -= cp0;
 169         cp0 -= c0;
 170         ret[0] = cp0;
 171         ret[1] = (cp1 - cp0) * 2;
 172         ret[2] = (c1 - cp1 - cp1 + cp0);
 173         int numroots = QuadCurve2D.solveQuadratic(ret, ret);
 174         int j = 0;
 175         for (int i = 0; i < numroots; i++) {
 176             double t = ret[i];
 177             // No splits at t==0 and t==1
 178             if (t > 0 && t < 1) {
 179                 if (j < i) {
 180                     ret[j] = t;
 181                 }
 182                 j++;
 183             }
 184         }
 185         return j;
 186     }
 187 
 188     /*
 189      * Split the cubic Bezier stored at coords[pos...pos+7] representing
 190      * the parametric range [0..1] into two subcurves representing the
 191      * parametric subranges [0..t] and [t..1].  Store the results back
 192      * into the array at coords[pos...pos+7] and coords[pos+6...pos+13].
 193      */
 194     public static void split(double coords[], int pos, double t) {
 195         double x0, y0, cx0, cy0, cx1, cy1, x1, y1;
 196         coords[pos+12] = x1 = coords[pos+6];
 197         coords[pos+13] = y1 = coords[pos+7];
 198         cx1 = coords[pos+4];
 199         cy1 = coords[pos+5];
 200         x1 = cx1 + (x1 - cx1) * t;
 201         y1 = cy1 + (y1 - cy1) * t;
 202         x0 = coords[pos+0];
 203         y0 = coords[pos+1];
 204         cx0 = coords[pos+2];
 205         cy0 = coords[pos+3];
 206         x0 = x0 + (cx0 - x0) * t;
 207         y0 = y0 + (cy0 - y0) * t;
 208         cx0 = cx0 + (cx1 - cx0) * t;
 209         cy0 = cy0 + (cy1 - cy0) * t;
 210         cx1 = cx0 + (x1 - cx0) * t;
 211         cy1 = cy0 + (y1 - cy0) * t;
 212         cx0 = x0 + (cx0 - x0) * t;
 213         cy0 = y0 + (cy0 - y0) * t;
 214         coords[pos+2] = x0;
 215         coords[pos+3] = y0;
 216         coords[pos+4] = cx0;
 217         coords[pos+5] = cy0;
 218         coords[pos+6] = cx0 + (cx1 - cx0) * t;
 219         coords[pos+7] = cy0 + (cy1 - cy0) * t;
 220         coords[pos+8] = cx1;
 221         coords[pos+9] = cy1;
 222         coords[pos+10] = x1;
 223         coords[pos+11] = y1;
 224     }
 225 
 226     public Order3(double x0, double y0,
 227                   double cx0, double cy0,
 228                   double cx1, double cy1,
 229                   double x1, double y1,
 230                   int direction)
 231     {
 232         super(direction);
 233         // REMIND: Better accuracy in the root finding methods would
 234         //  ensure that cys are in range.  As it stands, they are never
 235         //  more than "1 mantissa bit" out of range...
 236         if (cy0 < y0) cy0 = y0;
 237         if (cy1 > y1) cy1 = y1;
 238         this.x0 = x0;
 239         this.y0 = y0;
 240         this.cx0 = cx0;
 241         this.cy0 = cy0;
 242         this.cx1 = cx1;
 243         this.cy1 = cy1;
 244         this.x1 = x1;
 245         this.y1 = y1;
 246         xmin = Math.min(Math.min(x0, x1), Math.min(cx0, cx1));
 247         xmax = Math.max(Math.max(x0, x1), Math.max(cx0, cx1));
 248         xcoeff0 = x0;
 249         xcoeff1 = (cx0 - x0) * 3.0;
 250         xcoeff2 = (cx1 - cx0 - cx0 + x0) * 3.0;
 251         xcoeff3 = x1 - (cx1 - cx0) * 3.0 - x0;
 252         ycoeff0 = y0;
 253         ycoeff1 = (cy0 - y0) * 3.0;
 254         ycoeff2 = (cy1 - cy0 - cy0 + y0) * 3.0;
 255         ycoeff3 = y1 - (cy1 - cy0) * 3.0 - y0;
 256         YforT1 = YforT2 = YforT3 = y0;
 257     }
 258 
 259     public int getOrder() {
 260         return 3;
 261     }
 262 
 263     public double getXTop() {
 264         return x0;
 265     }
 266 
 267     public double getYTop() {
 268         return y0;
 269     }
 270 
 271     public double getXBot() {
 272         return x1;
 273     }
 274 
 275     public double getYBot() {
 276         return y1;
 277     }
 278 
 279     public double getXMin() {
 280         return xmin;
 281     }
 282 
 283     public double getXMax() {
 284         return xmax;
 285     }
 286 
 287     public double getX0() {
 288         return (direction == INCREASING) ? x0 : x1;
 289     }
 290 
 291     public double getY0() {
 292         return (direction == INCREASING) ? y0 : y1;
 293     }
 294 
 295     public double getCX0() {
 296         return (direction == INCREASING) ? cx0 : cx1;
 297     }
 298 
 299     public double getCY0() {
 300         return (direction == INCREASING) ? cy0 : cy1;
 301     }
 302 
 303     public double getCX1() {
 304         return (direction == DECREASING) ? cx0 : cx1;
 305     }
 306 
 307     public double getCY1() {
 308         return (direction == DECREASING) ? cy0 : cy1;
 309     }
 310 
 311     public double getX1() {
 312         return (direction == DECREASING) ? x0 : x1;
 313     }
 314 
 315     public double getY1() {
 316         return (direction == DECREASING) ? y0 : y1;
 317     }
 318 
 319     private double TforY1;
 320     private double YforT1;
 321     private double TforY2;
 322     private double YforT2;
 323     private double TforY3;
 324     private double YforT3;
 325 
 326     /*
 327      * Solve the cubic whose coefficients are in the a,b,c,d fields and
 328      * return the first root in the range [0, 1].
 329      * The cubic solved is represented by the equation:
 330      *     x^3 + (ycoeff2)x^2 + (ycoeff1)x + (ycoeff0) = y
 331      * @return the first valid root (in the range [0, 1])
 332      */
 333     public double TforY(double y) {
 334         if (y <= y0) return 0;
 335         if (y >= y1) return 1;
 336         if (y == YforT1) return TforY1;
 337         if (y == YforT2) return TforY2;
 338         if (y == YforT3) return TforY3;
 339         // From Numerical Recipes, 5.6, Quadratic and Cubic Equations
 340         if (ycoeff3 == 0.0) {
 341             // The cubic degenerated to quadratic (or line or ...).
 342             return Order2.TforY(y, ycoeff0, ycoeff1, ycoeff2);
 343         }
 344         double a = ycoeff2 / ycoeff3;
 345         double b = ycoeff1 / ycoeff3;
 346         double c = (ycoeff0 - y) / ycoeff3;
 347         int roots = 0;
 348         double Q = (a * a - 3.0 * b) / 9.0;
 349         double R = (2.0 * a * a * a - 9.0 * a * b + 27.0 * c) / 54.0;
 350         double R2 = R * R;
 351         double Q3 = Q * Q * Q;
 352         double a_3 = a / 3.0;
 353         double t;
 354         if (R2 < Q3) {
 355             double theta = Math.acos(R / Math.sqrt(Q3));
 356             Q = -2.0 * Math.sqrt(Q);
 357             t = refine(a, b, c, y, Q * Math.cos(theta / 3.0) - a_3);
 358             if (t < 0) {
 359                 t = refine(a, b, c, y,
 360                            Q * Math.cos((theta + Math.PI * 2.0)/ 3.0) - a_3);
 361             }
 362             if (t < 0) {
 363                 t = refine(a, b, c, y,
 364                            Q * Math.cos((theta - Math.PI * 2.0)/ 3.0) - a_3);
 365             }
 366         } else {
 367             boolean neg = (R < 0.0);
 368             double S = Math.sqrt(R2 - Q3);
 369             if (neg) {
 370                 R = -R;
 371             }
 372             double A = Math.pow(R + S, 1.0 / 3.0);
 373             if (!neg) {
 374                 A = -A;
 375             }
 376             double B = (A == 0.0) ? 0.0 : (Q / A);
 377             t = refine(a, b, c, y, (A + B) - a_3);
 378         }
 379         if (t < 0) {
 380             //throw new InternalError("bad t");
 381             double t0 = 0;
 382             double t1 = 1;
 383             while (true) {
 384                 t = (t0 + t1) / 2;
 385                 if (t == t0 || t == t1) {
 386                     break;
 387                 }
 388                 double yt = YforT(t);
 389                 if (yt < y) {
 390                     t0 = t;
 391                 } else if (yt > y) {
 392                     t1 = t;
 393                 } else {
 394                     break;
 395                 }
 396             }
 397         }
 398         if (t >= 0) {
 399             TforY3 = TforY2;
 400             YforT3 = YforT2;
 401             TforY2 = TforY1;
 402             YforT2 = YforT1;
 403             TforY1 = t;
 404             YforT1 = y;
 405         }
 406         return t;
 407     }
 408 
 409     public double refine(double a, double b, double c,
 410                          double target, double t)
 411     {
 412         if (t < -0.1 || t > 1.1) {
 413             return -1;
 414         }
 415         double y = YforT(t);
 416         double t0, t1;
 417         if (y < target) {
 418             t0 = t;
 419             t1 = 1;
 420         } else {
 421             t0 = 0;
 422             t1 = t;
 423         }
 424         double origt = t;
 425         double origy = y;
 426         boolean useslope = true;
 427         while (y != target) {
 428             if (!useslope) {
 429                 double t2 = (t0 + t1) / 2;
 430                 if (t2 == t0 || t2 == t1) {
 431                     break;
 432                 }
 433                 t = t2;
 434             } else {
 435                 double slope = dYforT(t, 1);
 436                 if (slope == 0) {
 437                     useslope = false;
 438                     continue;
 439                 }
 440                 double t2 = t + ((target - y) / slope);
 441                 if (t2 == t || t2 <= t0 || t2 >= t1) {
 442                     useslope = false;
 443                     continue;
 444                 }
 445                 t = t2;
 446             }
 447             y = YforT(t);
 448             if (y < target) {
 449                 t0 = t;
 450             } else if (y > target) {
 451                 t1 = t;
 452             } else {
 453                 break;
 454             }
 455         }
 456         boolean verbose = false;
 457         if (false && t >= 0 && t <= 1) {
 458             y = YforT(t);
 459             long tdiff = diffbits(t, origt);
 460             long ydiff = diffbits(y, origy);
 461             long yerr = diffbits(y, target);
 462             if (yerr > 0 || (verbose && tdiff > 0)) {
 463                 System.out.println("target was y = "+target);
 464                 System.out.println("original was y = "+origy+", t = "+origt);
 465                 System.out.println("final was y = "+y+", t = "+t);
 466                 System.out.println("t diff is "+tdiff);
 467                 System.out.println("y diff is "+ydiff);
 468                 System.out.println("y error is "+yerr);
 469                 double tlow = prev(t);
 470                 double ylow = YforT(tlow);
 471                 double thi = next(t);
 472                 double yhi = YforT(thi);
 473                 if (Math.abs(target - ylow) < Math.abs(target - y) ||
 474                     Math.abs(target - yhi) < Math.abs(target - y))
 475                 {
 476                     System.out.println("adjacent y's = ["+ylow+", "+yhi+"]");
 477                 }
 478             }
 479         }
 480         return (t > 1) ? -1 : t;
 481     }
 482 
 483     public double XforY(double y) {
 484         if (y <= y0) {
 485             return x0;
 486         }
 487         if (y >= y1) {
 488             return x1;
 489         }
 490         return XforT(TforY(y));
 491     }
 492 
 493     public double XforT(double t) {
 494         return (((xcoeff3 * t) + xcoeff2) * t + xcoeff1) * t + xcoeff0;
 495     }
 496 
 497     public double YforT(double t) {
 498         return (((ycoeff3 * t) + ycoeff2) * t + ycoeff1) * t + ycoeff0;
 499     }
 500 
 501     public double dXforT(double t, int deriv) {
 502         switch (deriv) {
 503         case 0:
 504             return (((xcoeff3 * t) + xcoeff2) * t + xcoeff1) * t + xcoeff0;
 505         case 1:
 506             return ((3 * xcoeff3 * t) + 2 * xcoeff2) * t + xcoeff1;
 507         case 2:
 508             return (6 * xcoeff3 * t) + 2 * xcoeff2;
 509         case 3:
 510             return 6 * xcoeff3;
 511         default:
 512             return 0;
 513         }
 514     }
 515 
 516     public double dYforT(double t, int deriv) {
 517         switch (deriv) {
 518         case 0:
 519             return (((ycoeff3 * t) + ycoeff2) * t + ycoeff1) * t + ycoeff0;
 520         case 1:
 521             return ((3 * ycoeff3 * t) + 2 * ycoeff2) * t + ycoeff1;
 522         case 2:
 523             return (6 * ycoeff3 * t) + 2 * ycoeff2;
 524         case 3:
 525             return 6 * ycoeff3;
 526         default:
 527             return 0;
 528         }
 529     }
 530 
 531     public double nextVertical(double t0, double t1) {
 532         double eqn[] = {xcoeff1, 2 * xcoeff2, 3 * xcoeff3};
 533         int numroots = QuadCurve2D.solveQuadratic(eqn, eqn);
 534         for (int i = 0; i < numroots; i++) {
 535             if (eqn[i] > t0 && eqn[i] < t1) {
 536                 t1 = eqn[i];
 537             }
 538         }
 539         return t1;
 540     }
 541 
 542     public void enlarge(Rectangle2D r) {
 543         r.add(x0, y0);
 544         double eqn[] = {xcoeff1, 2 * xcoeff2, 3 * xcoeff3};
 545         int numroots = QuadCurve2D.solveQuadratic(eqn, eqn);
 546         for (int i = 0; i < numroots; i++) {
 547             double t = eqn[i];
 548             if (t > 0 && t < 1) {
 549                 r.add(XforT(t), YforT(t));
 550             }
 551         }
 552         r.add(x1, y1);
 553     }
 554 
 555     public Curve getSubCurve(double ystart, double yend, int dir) {
 556         if (ystart <= y0 && yend >= y1) {
 557             return getWithDirection(dir);
 558         }
 559         double eqn[] = new double[14];
 560         double t0, t1;
 561         t0 = TforY(ystart);
 562         t1 = TforY(yend);
 563         eqn[0] = x0;
 564         eqn[1] = y0;
 565         eqn[2] = cx0;
 566         eqn[3] = cy0;
 567         eqn[4] = cx1;
 568         eqn[5] = cy1;
 569         eqn[6] = x1;
 570         eqn[7] = y1;
 571         if (t0 > t1) {
 572             /* This happens in only rare cases where ystart is
 573              * very near yend and solving for the yend root ends
 574              * up stepping slightly lower in t than solving for
 575              * the ystart root.
 576              * Ideally we might want to skip this tiny little
 577              * segment and just fudge the surrounding coordinates
 578              * to bridge the gap left behind, but there is no way
 579              * to do that from here.  Higher levels could
 580              * potentially eliminate these tiny "fixup" segments,
 581              * but not without a lot of extra work on the code that
 582              * coalesces chains of curves into subpaths.  The
 583              * simplest solution for now is to just reorder the t
 584              * values and chop out a miniscule curve piece.
 585              */
 586             double t = t0;
 587             t0 = t1;
 588             t1 = t;
 589         }
 590         if (t1 < 1) {
 591             split(eqn, 0, t1);
 592         }
 593         int i;
 594         if (t0 <= 0) {
 595             i = 0;
 596         } else {
 597             split(eqn, 0, t0 / t1);
 598             i = 6;
 599         }
 600         return new Order3(eqn[i+0], ystart,
 601                           eqn[i+2], eqn[i+3],
 602                           eqn[i+4], eqn[i+5],
 603                           eqn[i+6], yend,
 604                           dir);
 605     }
 606 
 607     public Curve getReversedCurve() {
 608         return new Order3(x0, y0, cx0, cy0, cx1, cy1, x1, y1, -direction);
 609     }
 610 
 611     public int getSegment(double coords[]) {
 612         if (direction == INCREASING) {
 613             coords[0] = cx0;
 614             coords[1] = cy0;
 615             coords[2] = cx1;
 616             coords[3] = cy1;
 617             coords[4] = x1;
 618             coords[5] = y1;
 619         } else {
 620             coords[0] = cx1;
 621             coords[1] = cy1;
 622             coords[2] = cx0;
 623             coords[3] = cy0;
 624             coords[4] = x0;
 625             coords[5] = y0;
 626         }
 627         return PathIterator.SEG_CUBICTO;
 628     }
 629 
 630     public String controlPointString() {
 631         return (("("+round(getCX0())+", "+round(getCY0())+"), ")+
 632                 ("("+round(getCX1())+", "+round(getCY1())+"), "));
 633     }
 634 }