Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/java2d/marlin/Curve.java
38918 views
/*1* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d.marlin;2627import java.util.Iterator;2829final class Curve {3031float ax, ay, bx, by, cx, cy, dx, dy;32float dax, day, dbx, dby;33// shared iterator instance34private final BreakPtrIterator iterator = new BreakPtrIterator();3536Curve() {37}3839void set(float[] points, int type) {40switch(type) {41case 8:42set(points[0], points[1],43points[2], points[3],44points[4], points[5],45points[6], points[7]);46return;47case 6:48set(points[0], points[1],49points[2], points[3],50points[4], points[5]);51return;52default:53throw new InternalError("Curves can only be cubic or quadratic");54}55}5657void set(float x1, float y1,58float x2, float y2,59float x3, float y3,60float x4, float y4)61{62ax = 3f * (x2 - x3) + x4 - x1;63ay = 3f * (y2 - y3) + y4 - y1;64bx = 3f * (x1 - 2f * x2 + x3);65by = 3f * (y1 - 2f * y2 + y3);66cx = 3f * (x2 - x1);67cy = 3f * (y2 - y1);68dx = x1;69dy = y1;70dax = 3f * ax; day = 3f * ay;71dbx = 2f * bx; dby = 2f * by;72}7374void set(float x1, float y1,75float x2, float y2,76float x3, float y3)77{78ax = 0f; ay = 0f;79bx = x1 - 2f * x2 + x3;80by = y1 - 2f * y2 + y3;81cx = 2f * (x2 - x1);82cy = 2f * (y2 - y1);83dx = x1;84dy = y1;85dax = 0f; day = 0f;86dbx = 2f * bx; dby = 2f * by;87}8889float xat(float t) {90return t * (t * (t * ax + bx) + cx) + dx;91}92float yat(float t) {93return t * (t * (t * ay + by) + cy) + dy;94}9596float dxat(float t) {97return t * (t * dax + dbx) + cx;98}99100float dyat(float t) {101return t * (t * day + dby) + cy;102}103104int dxRoots(float[] roots, int off) {105return Helpers.quadraticRoots(dax, dbx, cx, roots, off);106}107108int dyRoots(float[] roots, int off) {109return Helpers.quadraticRoots(day, dby, cy, roots, off);110}111112int infPoints(float[] pts, int off) {113// inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0114// Fortunately, this turns out to be quadratic, so there are at115// most 2 inflection points.116final float a = dax * dby - dbx * day;117final float b = 2f * (cy * dax - day * cx);118final float c = cy * dbx - cx * dby;119120return Helpers.quadraticRoots(a, b, c, pts, off);121}122123// finds points where the first and second derivative are124// perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where125// * is a dot product). Unfortunately, we have to solve a cubic.126private int perpendiculardfddf(float[] pts, int off) {127assert pts.length >= off + 4;128129// these are the coefficients of some multiple of g(t) (not g(t),130// because the roots of a polynomial are not changed after multiplication131// by a constant, and this way we save a few multiplications).132final float a = 2f * (dax*dax + day*day);133final float b = 3f * (dax*dbx + day*dby);134final float c = 2f * (dax*cx + day*cy) + dbx*dbx + dby*dby;135final float d = dbx*cx + dby*cy;136return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0f, 1f);137}138139// Tries to find the roots of the function ROC(t)-w in [0, 1). It uses140// a variant of the false position algorithm to find the roots. False141// position requires that 2 initial values x0,x1 be given, and that the142// function must have opposite signs at those values. To find such143// values, we need the local extrema of the ROC function, for which we144// need the roots of its derivative; however, it's harder to find the145// roots of the derivative in this case than it is to find the roots146// of the original function. So, we find all points where this curve's147// first and second derivative are perpendicular, and we pretend these148// are our local extrema. There are at most 3 of these, so we will check149// at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection150// points, so roc-w can have at least 6 roots. This shouldn't be a151// problem for what we're trying to do (draw a nice looking curve).152int rootsOfROCMinusW(float[] roots, int off, final float w, final float err) {153// no OOB exception, because by now off<=6, and roots.length >= 10154assert off <= 6 && roots.length >= 10;155int ret = off;156int numPerpdfddf = perpendiculardfddf(roots, off);157float t0 = 0, ft0 = ROCsq(t0) - w*w;158roots[off + numPerpdfddf] = 1f; // always check interval end points159numPerpdfddf++;160for (int i = off; i < off + numPerpdfddf; i++) {161float t1 = roots[i], ft1 = ROCsq(t1) - w*w;162if (ft0 == 0f) {163roots[ret++] = t0;164} else if (ft1 * ft0 < 0f) { // have opposite signs165// (ROC(t)^2 == w^2) == (ROC(t) == w) is true because166// ROC(t) >= 0 for all t.167roots[ret++] = falsePositionROCsqMinusX(t0, t1, w*w, err);168}169t0 = t1;170ft0 = ft1;171}172173return ret - off;174}175176private static float eliminateInf(float x) {177return (x == Float.POSITIVE_INFINITY ? Float.MAX_VALUE :178(x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x));179}180181// A slight modification of the false position algorithm on wikipedia.182// This only works for the ROCsq-x functions. It might be nice to have183// the function as an argument, but that would be awkward in java6.184// TODO: It is something to consider for java8 (or whenever lambda185// expressions make it into the language), depending on how closures186// and turn out. Same goes for the newton's method187// algorithm in Helpers.java188private float falsePositionROCsqMinusX(float x0, float x1,189final float x, final float err)190{191final int iterLimit = 100;192int side = 0;193float t = x1, ft = eliminateInf(ROCsq(t) - x);194float s = x0, fs = eliminateInf(ROCsq(s) - x);195float r = s, fr;196for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {197r = (fs * t - ft * s) / (fs - ft);198fr = ROCsq(r) - x;199if (sameSign(fr, ft)) {200ft = fr; t = r;201if (side < 0) {202fs /= (1 << (-side));203side--;204} else {205side = -1;206}207} else if (fr * fs > 0) {208fs = fr; s = r;209if (side > 0) {210ft /= (1 << side);211side++;212} else {213side = 1;214}215} else {216break;217}218}219return r;220}221222private static boolean sameSign(float x, float y) {223// another way is to test if x*y > 0. This is bad for small x, y.224return (x < 0f && y < 0f) || (x > 0f && y > 0f);225}226227// returns the radius of curvature squared at t of this curve228// see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)229private float ROCsq(final float t) {230// dx=xat(t) and dy=yat(t). These calls have been inlined for efficiency231final float dx = t * (t * dax + dbx) + cx;232final float dy = t * (t * day + dby) + cy;233final float ddx = 2f * dax * t + dbx;234final float ddy = 2f * day * t + dby;235final float dx2dy2 = dx*dx + dy*dy;236final float ddx2ddy2 = ddx*ddx + ddy*ddy;237final float ddxdxddydy = ddx*dx + ddy*dy;238return dx2dy2*((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy));239}240241// curve to be broken should be in pts242// this will change the contents of pts but not Ts243// TODO: There's no reason for Ts to be an array. All we need is a sequence244// of t values at which to subdivide. An array statisfies this condition,245// but is unnecessarily restrictive. Ts should be an Iterator<Float> instead.246// Doing this will also make dashing easier, since we could easily make247// LengthIterator an Iterator<Float> and feed it to this function to simplify248// the loop in Dasher.somethingTo.249BreakPtrIterator breakPtsAtTs(final float[] pts, final int type,250final float[] Ts, final int numTs)251{252assert pts.length >= 2*type && numTs <= Ts.length;253254// initialize shared iterator:255iterator.init(pts, type, Ts, numTs);256257return iterator;258}259260static final class BreakPtrIterator {261private int nextCurveIdx;262private int curCurveOff;263private float prevT;264private float[] pts;265private int type;266private float[] ts;267private int numTs;268269void init(final float[] pts, final int type,270final float[] ts, final int numTs) {271this.pts = pts;272this.type = type;273this.ts = ts;274this.numTs = numTs;275276nextCurveIdx = 0;277curCurveOff = 0;278prevT = 0f;279}280281public boolean hasNext() {282return nextCurveIdx <= numTs;283}284285public int next() {286int ret;287if (nextCurveIdx < numTs) {288float curT = ts[nextCurveIdx];289float splitT = (curT - prevT) / (1f - prevT);290Helpers.subdivideAt(splitT,291pts, curCurveOff,292pts, 0,293pts, type, type);294prevT = curT;295ret = 0;296curCurveOff = type;297} else {298ret = curCurveOff;299}300nextCurveIdx++;301return ret;302}303}304}305306307308