1 /*
   2  * Copyright (c) 1998, 2015, 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 com.sun.javafx.geom;
  27 
  28 import java.util.Vector;
  29 
  30 public abstract class Curve {
  31     public static final int INCREASING = 1;
  32     public static final int DECREASING = -1;
  33 
  34     protected int direction;
  35 
  36     public static void insertMove(Vector curves, double x, double y) {
  37         curves.add(new Order0(x, y));
  38     }
  39 
  40     public static void insertLine(Vector curves,
  41                                   double x0, double y0,
  42                                   double x1, double y1)
  43     {
  44         if (y0 < y1) {
  45             curves.add(new Order1(x0, y0,
  46                                   x1, y1,
  47                                   INCREASING));
  48         } else if (y0 > y1) {
  49             curves.add(new Order1(x1, y1,
  50                                   x0, y0,
  51                                   DECREASING));
  52         } else {
  53             // Do not add horizontal lines
  54         }
  55     }
  56 
  57     public static void insertQuad(Vector curves, double tmp[],
  58                                   double x0, double y0,
  59                                   double cx0, double cy0,
  60                                   double x1, double y1)
  61     {
  62         if (y0 > y1) {
  63             Order2.insert(curves, tmp,
  64                           x1, y1, cx0, cy0, x0, y0,
  65                           DECREASING);
  66         } else if (y0 == y1 && y0 == cy0) {
  67             // Do not add horizontal lines
  68             return;
  69         } else {
  70             Order2.insert(curves, tmp,
  71                           x0, y0, cx0, cy0, x1, y1,
  72                           INCREASING);
  73         }
  74     }
  75 
  76     public static void insertCubic(Vector curves, double tmp[],
  77                                    double x0, double y0,
  78                                    double cx0, double cy0,
  79                                    double cx1, double cy1,
  80                                    double x1, double y1)
  81     {
  82         if (y0 > y1) {
  83             Order3.insert(curves, tmp,
  84                           x1, y1, cx1, cy1, cx0, cy0, x0, y0,
  85                           DECREASING);
  86         } else if (y0 == y1 && y0 == cy0 && y0 == cy1) {
  87             // Do not add horizontal lines
  88             return;
  89         } else {
  90             Order3.insert(curves, tmp,
  91                           x0, y0, cx0, cy0, cx1, cy1, x1, y1,
  92                           INCREASING);
  93         }
  94     }
  95 
  96     public Curve(int direction) {
  97         this.direction = direction;
  98     }
  99 
 100     public final int getDirection() {
 101         return direction;
 102     }
 103 
 104     public final Curve getWithDirection(int direction) {
 105         return (this.direction == direction ? this : getReversedCurve());
 106     }
 107 
 108     public static double round(double v) {
 109         //return Math.rint(v*10)/10;
 110         return v;
 111     }
 112 
 113     public static int orderof(double x1, double x2) {
 114         if (x1 < x2) {
 115             return -1;
 116         }
 117         if (x1 > x2) {
 118             return 1;
 119         }
 120         return 0;
 121     }
 122 
 123     public static long signeddiffbits(double y1, double y2) {
 124         return (Double.doubleToLongBits(y1) - Double.doubleToLongBits(y2));
 125     }
 126     public static long diffbits(double y1, double y2) {
 127         return Math.abs(Double.doubleToLongBits(y1) -
 128                         Double.doubleToLongBits(y2));
 129     }
 130     public static double prev(double v) {
 131         return Double.longBitsToDouble(Double.doubleToLongBits(v)-1);
 132     }
 133     public static double next(double v) {
 134         return Double.longBitsToDouble(Double.doubleToLongBits(v)+1);
 135     }
 136 
 137     @Override
 138     public String toString() {
 139         return ("Curve["+
 140                 getOrder()+", "+
 141                 ("("+round(getX0())+", "+round(getY0())+"), ")+
 142                 controlPointString()+
 143                 ("("+round(getX1())+", "+round(getY1())+"), ")+
 144                 (direction == INCREASING ? "D" : "U")+
 145                 "]");
 146     }
 147 
 148     public String controlPointString() {
 149         return "";
 150     }
 151 
 152     public abstract int getOrder();
 153 
 154     public abstract double getXTop();
 155     public abstract double getYTop();
 156     public abstract double getXBot();
 157     public abstract double getYBot();
 158 
 159     public abstract double getXMin();
 160     public abstract double getXMax();
 161 
 162     public abstract double getX0();
 163     public abstract double getY0();
 164     public abstract double getX1();
 165     public abstract double getY1();
 166 
 167     public abstract double XforY(double y);
 168     public abstract double TforY(double y);
 169     public abstract double XforT(double t);
 170     public abstract double YforT(double t);
 171     public abstract double dXforT(double t, int deriv);
 172     public abstract double dYforT(double t, int deriv);
 173 
 174     public abstract double nextVertical(double t0, double t1);
 175 
 176     public int crossingsFor(double x, double y) {
 177         if (y >= getYTop() && y < getYBot()) {
 178             if (x < getXMax() && (x < getXMin() || x < XforY(y))) {
 179                 return 1;
 180             }
 181         }
 182         return 0;
 183     }
 184 
 185     public boolean accumulateCrossings(Crossings c) {
 186         double xhi = c.getXHi();
 187         if (getXMin() >= xhi) {
 188             return false;
 189         }
 190         double xlo = c.getXLo();
 191         double ylo = c.getYLo();
 192         double yhi = c.getYHi();
 193         double y0 = getYTop();
 194         double y1 = getYBot();
 195         double tstart, ystart, tend, yend;
 196         if (y0 < ylo) {
 197             if (y1 <= ylo) {
 198                 return false;
 199             }
 200             ystart = ylo;
 201             tstart = TforY(ylo);
 202         } else {
 203             if (y0 >= yhi) {
 204                 return false;
 205             }
 206             ystart = y0;
 207             tstart = 0;
 208         }
 209         if (y1 > yhi) {
 210             yend = yhi;
 211             tend = TforY(yhi);
 212         } else {
 213             yend = y1;
 214             tend = 1;
 215         }
 216         boolean hitLo = false;
 217         boolean hitHi = false;
 218         while (true) {
 219             double x = XforT(tstart);
 220             if (x < xhi) {
 221                 if (hitHi || x > xlo) {
 222                     return true;
 223                 }
 224                 hitLo = true;
 225             } else {
 226                 if (hitLo) {
 227                     return true;
 228                 }
 229                 hitHi = true;
 230             }
 231             if (tstart >= tend) {
 232                 break;
 233             }
 234             tstart = nextVertical(tstart, tend);
 235         }
 236         if (hitLo) {
 237             c.record(ystart, yend, direction);
 238         }
 239         return false;
 240     }
 241 
 242     public abstract void enlarge(RectBounds r);
 243 
 244     public Curve getSubCurve(double ystart, double yend) {
 245         return getSubCurve(ystart, yend, direction);
 246     }
 247 
 248     public abstract Curve getReversedCurve();
 249     public abstract Curve getSubCurve(double ystart, double yend, int dir);
 250 
 251     public int compareTo(Curve that, double yrange[]) {
 252         /*
 253         System.out.println(this+".compareTo("+that+")");
 254         System.out.println("target range = "+yrange[0]+"=>"+yrange[1]);
 255         */
 256         double y0 = yrange[0];
 257         double y1 = yrange[1];
 258         y1 = Math.min(Math.min(y1, this.getYBot()), that.getYBot());
 259         if (y1 <= yrange[0]) {
 260             System.err.println("this == "+this);
 261             System.err.println("that == "+that);
 262             System.out.println("target range = "+yrange[0]+"=>"+yrange[1]);
 263             throw new InternalError("backstepping from "+yrange[0]+" to "+y1);
 264         }
 265         yrange[1] = y1;
 266         if (this.getXMax() <= that.getXMin()) {
 267             if (this.getXMin() == that.getXMax()) {
 268                 return 0;
 269             }
 270             return -1;
 271         }
 272         if (this.getXMin() >= that.getXMax()) {
 273             return 1;
 274         }
 275         // Parameter s for thi(s) curve and t for tha(t) curve
 276         // [st]0 = parameters for top of current section of interest
 277         // [st]1 = parameters for bottom of valid range
 278         // [st]h = parameters for hypothesis point
 279         // [d][xy]s = valuations of thi(s) curve at sh
 280         // [d][xy]t = valuations of tha(t) curve at th
 281         double s0 = this.TforY(y0);
 282         double ys0 = this.YforT(s0);
 283         if (ys0 < y0) {
 284             s0 = refineTforY(s0, y0);
 285             ys0 = this.YforT(s0);
 286         }
 287         double s1 = this.TforY(y1);
 288         if (this.YforT(s1) < y0) {
 289             s1 = refineTforY(s1, y0);
 290             //System.out.println("s1 problem!");
 291         }
 292         double t0 = that.TforY(y0);
 293         double yt0 = that.YforT(t0);
 294         if (yt0 < y0) {
 295             t0 = that.refineTforY(t0, y0);
 296             yt0 = that.YforT(t0);
 297         }
 298         double t1 = that.TforY(y1);
 299         if (that.YforT(t1) < y0) {
 300             t1 = that.refineTforY(t1, y0);
 301             //System.out.println("t1 problem!");
 302         }
 303         double xs0 = this.XforT(s0);
 304         double xt0 = that.XforT(t0);
 305         double scale = Math.max(Math.abs(y0), Math.abs(y1));
 306         double ymin = Math.max(scale * 1E-14, 1E-300);
 307         if (fairlyClose(xs0, xt0)) {
 308             double bump = ymin;
 309             double maxbump = Math.min(ymin * 1E13, (y1 - y0) * .1);
 310             double y = y0 + bump;
 311             while (y <= y1) {
 312                 if (fairlyClose(this.XforY(y), that.XforY(y))) {
 313                     if ((bump *= 2) > maxbump) {
 314                         bump = maxbump;
 315                     }
 316                 } else {
 317                     y -= bump;
 318                     while (true) {
 319                         bump /= 2;
 320                         double newy = y + bump;
 321                         if (newy <= y) {
 322                             break;
 323                         }
 324                         if (fairlyClose(this.XforY(newy), that.XforY(newy))) {
 325                             y = newy;
 326                         }
 327                     }
 328                     break;
 329                 }
 330                 y += bump;
 331             }
 332             if (y > y0) {
 333                 if (y < y1) {
 334                     yrange[1] = y;
 335                 }
 336                 return 0;
 337             }
 338         }
 339         //double ymin = y1 * 1E-14;
 340         if (ymin <= 0) {
 341             System.out.println("ymin = "+ymin);
 342         }
 343         /*
 344         System.out.println("s range = "+s0+" to "+s1);
 345         System.out.println("t range = "+t0+" to "+t1);
 346         */
 347         while (s0 < s1 && t0 < t1) {
 348             double sh = this.nextVertical(s0, s1);
 349             double xsh = this.XforT(sh);
 350             double ysh = this.YforT(sh);
 351             double th = that.nextVertical(t0, t1);
 352             double xth = that.XforT(th);
 353             double yth = that.YforT(th);
 354             /*
 355             System.out.println("sh = "+sh);
 356             System.out.println("th = "+th);
 357             */
 358         try {
 359             if (findIntersect(that, yrange, ymin, 0, 0,
 360                               s0, xs0, ys0, sh, xsh, ysh,
 361                               t0, xt0, yt0, th, xth, yth)) {
 362                 break;
 363             }
 364         } catch (Throwable t) {
 365             System.err.println("Error: "+t);
 366             System.err.println("y range was "+yrange[0]+"=>"+yrange[1]);
 367             System.err.println("s y range is "+ys0+"=>"+ysh);
 368             System.err.println("t y range is "+yt0+"=>"+yth);
 369             System.err.println("ymin is "+ymin);
 370             return 0;
 371         }
 372             if (ysh < yth) {
 373                 if (ysh > yrange[0]) {
 374                     if (ysh < yrange[1]) {
 375                         yrange[1] = ysh;
 376                     }
 377                     break;
 378                 }
 379                 s0 = sh;
 380                 xs0 = xsh;
 381                 ys0 = ysh;
 382             } else {
 383                 if (yth > yrange[0]) {
 384                     if (yth < yrange[1]) {
 385                         yrange[1] = yth;
 386                     }
 387                     break;
 388                 }
 389                 t0 = th;
 390                 xt0 = xth;
 391                 yt0 = yth;
 392             }
 393         }
 394         double ymid = (yrange[0] + yrange[1]) / 2;
 395         /*
 396         System.out.println("final this["+s0+", "+sh+", "+s1+"]");
 397         System.out.println("final    y["+ys0+", "+ysh+"]");
 398         System.out.println("final that["+t0+", "+th+", "+t1+"]");
 399         System.out.println("final    y["+yt0+", "+yth+"]");
 400         System.out.println("final order = "+orderof(this.XforY(ymid),
 401                                                     that.XforY(ymid)));
 402         System.out.println("final range = "+yrange[0]+"=>"+yrange[1]);
 403         */
 404         /*
 405         System.out.println("final sx = "+this.XforY(ymid));
 406         System.out.println("final tx = "+that.XforY(ymid));
 407         System.out.println("final order = "+orderof(this.XforY(ymid),
 408                                                     that.XforY(ymid)));
 409         */
 410         return orderof(this.XforY(ymid), that.XforY(ymid));
 411     }
 412 
 413     public static final double TMIN = 1E-3;
 414 
 415     public boolean findIntersect(Curve that, double yrange[], double ymin,
 416                                  int slevel, int tlevel,
 417                                  double s0, double xs0, double ys0,
 418                                  double s1, double xs1, double ys1,
 419                                  double t0, double xt0, double yt0,
 420                                  double t1, double xt1, double yt1)
 421     {
 422         /*
 423         String pad = "        ";
 424         pad = pad+pad+pad+pad+pad;
 425         pad = pad+pad;
 426         System.out.println("----------------------------------------------");
 427         System.out.println(pad.substring(0, slevel)+ys0);
 428         System.out.println(pad.substring(0, slevel)+ys1);
 429         System.out.println(pad.substring(0, slevel)+(s1-s0));
 430         System.out.println("-------");
 431         System.out.println(pad.substring(0, tlevel)+yt0);
 432         System.out.println(pad.substring(0, tlevel)+yt1);
 433         System.out.println(pad.substring(0, tlevel)+(t1-t0));
 434         */
 435         if (ys0 > yt1 || yt0 > ys1) {
 436             return false;
 437         }
 438         if (Math.min(xs0, xs1) > Math.max(xt0, xt1) ||
 439             Math.max(xs0, xs1) < Math.min(xt0, xt1))
 440         {
 441             return false;
 442         }
 443         // Bounding boxes intersect - back off the larger of
 444         // the two subcurves by half until they stop intersecting
 445         // (or until they get small enough to switch to a more
 446         //  intensive algorithm).
 447         if (s1 - s0 > TMIN) {
 448             double s = (s0 + s1) / 2;
 449             double xs = this.XforT(s);
 450             double ys = this.YforT(s);
 451             if (s == s0 || s == s1) {
 452                 System.out.println("s0 = "+s0);
 453                 System.out.println("s1 = "+s1);
 454                 throw new InternalError("no s progress!");
 455             }
 456             if (t1 - t0 > TMIN) {
 457                 double t = (t0 + t1) / 2;
 458                 double xt = that.XforT(t);
 459                 double yt = that.YforT(t);
 460                 if (t == t0 || t == t1) {
 461                     System.out.println("t0 = "+t0);
 462                     System.out.println("t1 = "+t1);
 463                     throw new InternalError("no t progress!");
 464                 }
 465                 if (ys >= yt0 && yt >= ys0) {
 466                     if (findIntersect(that, yrange, ymin, slevel+1, tlevel+1,
 467                                       s0, xs0, ys0, s, xs, ys,
 468                                       t0, xt0, yt0, t, xt, yt)) {
 469                         return true;
 470                     }
 471                 }
 472                 if (ys >= yt) {
 473                     if (findIntersect(that, yrange, ymin, slevel+1, tlevel+1,
 474                                       s0, xs0, ys0, s, xs, ys,
 475                                       t, xt, yt, t1, xt1, yt1)) {
 476                         return true;
 477                     }
 478                 }
 479                 if (yt >= ys) {
 480                     if (findIntersect(that, yrange, ymin, slevel+1, tlevel+1,
 481                                       s, xs, ys, s1, xs1, ys1,
 482                                       t0, xt0, yt0, t, xt, yt)) {
 483                         return true;
 484                     }
 485                 }
 486                 if (ys1 >= yt && yt1 >= ys) {
 487                     if (findIntersect(that, yrange, ymin, slevel+1, tlevel+1,
 488                                       s, xs, ys, s1, xs1, ys1,
 489                                       t, xt, yt, t1, xt1, yt1)) {
 490                         return true;
 491                     }
 492                 }
 493             } else {
 494                 if (ys >= yt0) {
 495                     if (findIntersect(that, yrange, ymin, slevel+1, tlevel,
 496                                       s0, xs0, ys0, s, xs, ys,
 497                                       t0, xt0, yt0, t1, xt1, yt1)) {
 498                         return true;
 499                     }
 500                 }
 501                 if (yt1 >= ys) {
 502                     if (findIntersect(that, yrange, ymin, slevel+1, tlevel,
 503                                       s, xs, ys, s1, xs1, ys1,
 504                                       t0, xt0, yt0, t1, xt1, yt1)) {
 505                         return true;
 506                     }
 507                 }
 508             }
 509         } else if (t1 - t0 > TMIN) {
 510             double t = (t0 + t1) / 2;
 511             double xt = that.XforT(t);
 512             double yt = that.YforT(t);
 513             if (t == t0 || t == t1) {
 514                 System.out.println("t0 = "+t0);
 515                 System.out.println("t1 = "+t1);
 516                 throw new InternalError("no t progress!");
 517             }
 518             if (yt >= ys0) {
 519                 if (findIntersect(that, yrange, ymin, slevel, tlevel+1,
 520                                   s0, xs0, ys0, s1, xs1, ys1,
 521                                   t0, xt0, yt0, t, xt, yt)) {
 522                     return true;
 523                 }
 524             }
 525             if (ys1 >= yt) {
 526                 if (findIntersect(that, yrange, ymin, slevel, tlevel+1,
 527                                   s0, xs0, ys0, s1, xs1, ys1,
 528                                   t, xt, yt, t1, xt1, yt1)) {
 529                     return true;
 530                 }
 531             }
 532         } else {
 533             // No more subdivisions
 534             double xlk = xs1 - xs0;
 535             double ylk = ys1 - ys0;
 536             double xnm = xt1 - xt0;
 537             double ynm = yt1 - yt0;
 538             double xmk = xt0 - xs0;
 539             double ymk = yt0 - ys0;
 540             double det = xnm * ylk - ynm * xlk;
 541             if (det != 0) {
 542                 double detinv = 1 / det;
 543                 double s = (xnm * ymk - ynm * xmk) * detinv;
 544                 double t = (xlk * ymk - ylk * xmk) * detinv;
 545                 if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
 546                     s = s0 + s * (s1 - s0);
 547                     t = t0 + t * (t1 - t0);
 548                     if (s < 0 || s > 1 || t < 0 || t > 1) {
 549                         System.out.println("Uh oh!");
 550                     }
 551                     double y = (this.YforT(s) + that.YforT(t)) / 2;
 552                     if (y <= yrange[1] && y > yrange[0]) {
 553                         yrange[1] = y;
 554                         return true;
 555                     }
 556                 }
 557             }
 558             //System.out.println("Testing lines!");
 559         }
 560         return false;
 561     }
 562 
 563     public double refineTforY(double t0, double y0) {
 564         double t1 = 1;
 565         while (true) {
 566             double th = (t0 + t1) / 2;
 567             if (th == t0 || th == t1) {
 568                 return t1;
 569             }
 570             double y = YforT(th);
 571             if (y < y0) {
 572                 t0 = th;
 573             } else if (y > y0) {
 574                 t1 = th;
 575             } else {
 576                 return t1;
 577             }
 578         }
 579     }
 580 
 581     public boolean fairlyClose(double v1, double v2) {
 582         return (Math.abs(v1 - v2) <
 583                 Math.max(Math.abs(v1), Math.abs(v2)) * 1E-10);
 584     }
 585 
 586     public abstract int getSegment(float coords[]);
 587 }