jdk/src/java.desktop/share/classes/sun/java2d/marlin/Curve.java
changeset 34417 57a3863abbb4
child 39519 21bfc4452441
equal deleted inserted replaced
34416:68c0d866db5d 34417:57a3863abbb4
       
     1 /*
       
     2  * Copyright (c) 2007, 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 sun.java2d.marlin;
       
    27 
       
    28 import java.util.Iterator;
       
    29 
       
    30 final class Curve {
       
    31 
       
    32     float ax, ay, bx, by, cx, cy, dx, dy;
       
    33     float dax, day, dbx, dby;
       
    34     // shared iterator instance
       
    35     private final BreakPtrIterator iterator = new BreakPtrIterator();
       
    36 
       
    37     Curve() {
       
    38     }
       
    39 
       
    40     void set(float[] points, int type) {
       
    41         switch(type) {
       
    42         case 8:
       
    43             set(points[0], points[1],
       
    44                 points[2], points[3],
       
    45                 points[4], points[5],
       
    46                 points[6], points[7]);
       
    47             return;
       
    48         case 6:
       
    49             set(points[0], points[1],
       
    50                 points[2], points[3],
       
    51                 points[4], points[5]);
       
    52             return;
       
    53         default:
       
    54             throw new InternalError("Curves can only be cubic or quadratic");
       
    55         }
       
    56     }
       
    57 
       
    58     void set(float x1, float y1,
       
    59              float x2, float y2,
       
    60              float x3, float y3,
       
    61              float x4, float y4)
       
    62     {
       
    63         ax = 3f * (x2 - x3) + x4 - x1;
       
    64         ay = 3f * (y2 - y3) + y4 - y1;
       
    65         bx = 3f * (x1 - 2f * x2 + x3);
       
    66         by = 3f * (y1 - 2f * y2 + y3);
       
    67         cx = 3f * (x2 - x1);
       
    68         cy = 3f * (y2 - y1);
       
    69         dx = x1;
       
    70         dy = y1;
       
    71         dax = 3f * ax; day = 3f * ay;
       
    72         dbx = 2f * bx; dby = 2f * by;
       
    73     }
       
    74 
       
    75     void set(float x1, float y1,
       
    76              float x2, float y2,
       
    77              float x3, float y3)
       
    78     {
       
    79         ax = 0f; ay = 0f;
       
    80         bx = x1 - 2f * x2 + x3;
       
    81         by = y1 - 2f * y2 + y3;
       
    82         cx = 2f * (x2 - x1);
       
    83         cy = 2f * (y2 - y1);
       
    84         dx = x1;
       
    85         dy = y1;
       
    86         dax = 0f; day = 0f;
       
    87         dbx = 2f * bx; dby = 2f * by;
       
    88     }
       
    89 
       
    90     float xat(float t) {
       
    91         return t * (t * (t * ax + bx) + cx) + dx;
       
    92     }
       
    93     float yat(float t) {
       
    94         return t * (t * (t * ay + by) + cy) + dy;
       
    95     }
       
    96 
       
    97     float dxat(float t) {
       
    98         return t * (t * dax + dbx) + cx;
       
    99     }
       
   100 
       
   101     float dyat(float t) {
       
   102         return t * (t * day + dby) + cy;
       
   103     }
       
   104 
       
   105     int dxRoots(float[] roots, int off) {
       
   106         return Helpers.quadraticRoots(dax, dbx, cx, roots, off);
       
   107     }
       
   108 
       
   109     int dyRoots(float[] roots, int off) {
       
   110         return Helpers.quadraticRoots(day, dby, cy, roots, off);
       
   111     }
       
   112 
       
   113     int infPoints(float[] pts, int off) {
       
   114         // inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0
       
   115         // Fortunately, this turns out to be quadratic, so there are at
       
   116         // most 2 inflection points.
       
   117         final float a = dax * dby - dbx * day;
       
   118         final float b = 2f * (cy * dax - day * cx);
       
   119         final float c = cy * dbx - cx * dby;
       
   120 
       
   121         return Helpers.quadraticRoots(a, b, c, pts, off);
       
   122     }
       
   123 
       
   124     // finds points where the first and second derivative are
       
   125     // perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
       
   126     // * is a dot product). Unfortunately, we have to solve a cubic.
       
   127     private int perpendiculardfddf(float[] pts, int off) {
       
   128         assert pts.length >= off + 4;
       
   129 
       
   130         // these are the coefficients of some multiple of g(t) (not g(t),
       
   131         // because the roots of a polynomial are not changed after multiplication
       
   132         // by a constant, and this way we save a few multiplications).
       
   133         final float a = 2f * (dax*dax + day*day);
       
   134         final float b = 3f * (dax*dbx + day*dby);
       
   135         final float c = 2f * (dax*cx + day*cy) + dbx*dbx + dby*dby;
       
   136         final float d = dbx*cx + dby*cy;
       
   137         return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0f, 1f);
       
   138     }
       
   139 
       
   140     // Tries to find the roots of the function ROC(t)-w in [0, 1). It uses
       
   141     // a variant of the false position algorithm to find the roots. False
       
   142     // position requires that 2 initial values x0,x1 be given, and that the
       
   143     // function must have opposite signs at those values. To find such
       
   144     // values, we need the local extrema of the ROC function, for which we
       
   145     // need the roots of its derivative; however, it's harder to find the
       
   146     // roots of the derivative in this case than it is to find the roots
       
   147     // of the original function. So, we find all points where this curve's
       
   148     // first and second derivative are perpendicular, and we pretend these
       
   149     // are our local extrema. There are at most 3 of these, so we will check
       
   150     // at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection
       
   151     // points, so roc-w can have at least 6 roots. This shouldn't be a
       
   152     // problem for what we're trying to do (draw a nice looking curve).
       
   153     int rootsOfROCMinusW(float[] roots, int off, final float w, final float err) {
       
   154         // no OOB exception, because by now off<=6, and roots.length >= 10
       
   155         assert off <= 6 && roots.length >= 10;
       
   156         int ret = off;
       
   157         int numPerpdfddf = perpendiculardfddf(roots, off);
       
   158         float t0 = 0, ft0 = ROCsq(t0) - w*w;
       
   159         roots[off + numPerpdfddf] = 1f; // always check interval end points
       
   160         numPerpdfddf++;
       
   161         for (int i = off; i < off + numPerpdfddf; i++) {
       
   162             float t1 = roots[i], ft1 = ROCsq(t1) - w*w;
       
   163             if (ft0 == 0f) {
       
   164                 roots[ret++] = t0;
       
   165             } else if (ft1 * ft0 < 0f) { // have opposite signs
       
   166                 // (ROC(t)^2 == w^2) == (ROC(t) == w) is true because
       
   167                 // ROC(t) >= 0 for all t.
       
   168                 roots[ret++] = falsePositionROCsqMinusX(t0, t1, w*w, err);
       
   169             }
       
   170             t0 = t1;
       
   171             ft0 = ft1;
       
   172         }
       
   173 
       
   174         return ret - off;
       
   175     }
       
   176 
       
   177     private static float eliminateInf(float x) {
       
   178         return (x == Float.POSITIVE_INFINITY ? Float.MAX_VALUE :
       
   179             (x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x));
       
   180     }
       
   181 
       
   182     // A slight modification of the false position algorithm on wikipedia.
       
   183     // This only works for the ROCsq-x functions. It might be nice to have
       
   184     // the function as an argument, but that would be awkward in java6.
       
   185     // TODO: It is something to consider for java8 (or whenever lambda
       
   186     // expressions make it into the language), depending on how closures
       
   187     // and turn out. Same goes for the newton's method
       
   188     // algorithm in Helpers.java
       
   189     private float falsePositionROCsqMinusX(float x0, float x1,
       
   190                                            final float x, final float err)
       
   191     {
       
   192         final int iterLimit = 100;
       
   193         int side = 0;
       
   194         float t = x1, ft = eliminateInf(ROCsq(t) - x);
       
   195         float s = x0, fs = eliminateInf(ROCsq(s) - x);
       
   196         float r = s, fr;
       
   197         for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
       
   198             r = (fs * t - ft * s) / (fs - ft);
       
   199             fr = ROCsq(r) - x;
       
   200             if (sameSign(fr, ft)) {
       
   201                 ft = fr; t = r;
       
   202                 if (side < 0) {
       
   203                     fs /= (1 << (-side));
       
   204                     side--;
       
   205                 } else {
       
   206                     side = -1;
       
   207                 }
       
   208             } else if (fr * fs > 0) {
       
   209                 fs = fr; s = r;
       
   210                 if (side > 0) {
       
   211                     ft /= (1 << side);
       
   212                     side++;
       
   213                 } else {
       
   214                     side = 1;
       
   215                 }
       
   216             } else {
       
   217                 break;
       
   218             }
       
   219         }
       
   220         return r;
       
   221     }
       
   222 
       
   223     private static boolean sameSign(float x, float y) {
       
   224         // another way is to test if x*y > 0. This is bad for small x, y.
       
   225         return (x < 0f && y < 0f) || (x > 0f && y > 0f);
       
   226     }
       
   227 
       
   228     // returns the radius of curvature squared at t of this curve
       
   229     // see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)
       
   230     private float ROCsq(final float t) {
       
   231         // dx=xat(t) and dy=yat(t). These calls have been inlined for efficiency
       
   232         final float dx = t * (t * dax + dbx) + cx;
       
   233         final float dy = t * (t * day + dby) + cy;
       
   234         final float ddx = 2f * dax * t + dbx;
       
   235         final float ddy = 2f * day * t + dby;
       
   236         final float dx2dy2 = dx*dx + dy*dy;
       
   237         final float ddx2ddy2 = ddx*ddx + ddy*ddy;
       
   238         final float ddxdxddydy = ddx*dx + ddy*dy;
       
   239         return dx2dy2*((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy));
       
   240     }
       
   241 
       
   242     // curve to be broken should be in pts
       
   243     // this will change the contents of pts but not Ts
       
   244     // TODO: There's no reason for Ts to be an array. All we need is a sequence
       
   245     // of t values at which to subdivide. An array statisfies this condition,
       
   246     // but is unnecessarily restrictive. Ts should be an Iterator<Float> instead.
       
   247     // Doing this will also make dashing easier, since we could easily make
       
   248     // LengthIterator an Iterator<Float> and feed it to this function to simplify
       
   249     // the loop in Dasher.somethingTo.
       
   250     BreakPtrIterator breakPtsAtTs(final float[] pts, final int type,
       
   251                                   final float[] Ts, final int numTs)
       
   252     {
       
   253         assert pts.length >= 2*type && numTs <= Ts.length;
       
   254 
       
   255         // initialize shared iterator:
       
   256         iterator.init(pts, type, Ts, numTs);
       
   257 
       
   258         return iterator;
       
   259     }
       
   260 
       
   261     static final class BreakPtrIterator {
       
   262         private int nextCurveIdx;
       
   263         private int curCurveOff;
       
   264         private float prevT;
       
   265         private float[] pts;
       
   266         private int type;
       
   267         private float[] ts;
       
   268         private int numTs;
       
   269 
       
   270         void init(final float[] pts, final int type,
       
   271                   final float[] ts, final int numTs) {
       
   272             this.pts = pts;
       
   273             this.type = type;
       
   274             this.ts = ts;
       
   275             this.numTs = numTs;
       
   276 
       
   277             nextCurveIdx = 0;
       
   278             curCurveOff = 0;
       
   279             prevT = 0f;
       
   280         }
       
   281 
       
   282         public boolean hasNext() {
       
   283             return nextCurveIdx <= numTs;
       
   284         }
       
   285 
       
   286         public int next() {
       
   287             int ret;
       
   288             if (nextCurveIdx < numTs) {
       
   289                 float curT = ts[nextCurveIdx];
       
   290                 float splitT = (curT - prevT) / (1f - prevT);
       
   291                 Helpers.subdivideAt(splitT,
       
   292                                     pts, curCurveOff,
       
   293                                     pts, 0,
       
   294                                     pts, type, type);
       
   295                 prevT = curT;
       
   296                 ret = 0;
       
   297                 curCurveOff = type;
       
   298             } else {
       
   299                 ret = curCurveOff;
       
   300             }
       
   301             nextCurveIdx++;
       
   302             return ret;
       
   303         }
       
   304     }
       
   305 }
       
   306