Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/java2d/pisces/Curve.java
38918 views
/*1* Copyright (c) 2007, 2011, 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.pisces;2627import java.util.Iterator;2829final class Curve {3031float ax, ay, bx, by, cx, cy, dx, dy;32float dax, day, dbx, dby;3334Curve() {35}3637void set(float[] points, int type) {38switch(type) {39case 8:40set(points[0], points[1],41points[2], points[3],42points[4], points[5],43points[6], points[7]);44break;45case 6:46set(points[0], points[1],47points[2], points[3],48points[4], points[5]);49break;50default:51throw new InternalError("Curves can only be cubic or quadratic");52}53}5455void set(float x1, float y1,56float x2, float y2,57float x3, float y3,58float x4, float y4)59{60ax = 3 * (x2 - x3) + x4 - x1;61ay = 3 * (y2 - y3) + y4 - y1;62bx = 3 * (x1 - 2 * x2 + x3);63by = 3 * (y1 - 2 * y2 + y3);64cx = 3 * (x2 - x1);65cy = 3 * (y2 - y1);66dx = x1;67dy = y1;68dax = 3 * ax; day = 3 * ay;69dbx = 2 * bx; dby = 2 * by;70}7172void set(float x1, float y1,73float x2, float y2,74float x3, float y3)75{76ax = ay = 0f;7778bx = x1 - 2 * x2 + x3;79by = y1 - 2 * y2 + y3;80cx = 2 * (x2 - x1);81cy = 2 * (y2 - y1);82dx = x1;83dy = y1;84dax = 0; day = 0;85dbx = 2 * bx; dby = 2 * by;86}8788float xat(float t) {89return t * (t * (t * ax + bx) + cx) + dx;90}91float yat(float t) {92return t * (t * (t * ay + by) + cy) + dy;93}9495float dxat(float t) {96return t * (t * dax + dbx) + cx;97}9899float dyat(float t) {100return t * (t * day + dby) + cy;101}102103int dxRoots(float[] roots, int off) {104return Helpers.quadraticRoots(dax, dbx, cx, roots, off);105}106107int dyRoots(float[] roots, int off) {108return Helpers.quadraticRoots(day, dby, cy, roots, off);109}110111int infPoints(float[] pts, int off) {112// inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0113// Fortunately, this turns out to be quadratic, so there are at114// most 2 inflection points.115final float a = dax * dby - dbx * day;116final float b = 2 * (cy * dax - day * cx);117final float c = cy * dbx - cx * dby;118119return Helpers.quadraticRoots(a, b, c, pts, off);120}121122// finds points where the first and second derivative are123// perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where124// * is a dot product). Unfortunately, we have to solve a cubic.125private int perpendiculardfddf(float[] pts, int off) {126assert pts.length >= off + 4;127128// these are the coefficients of some multiple of g(t) (not g(t),129// because the roots of a polynomial are not changed after multiplication130// by a constant, and this way we save a few multiplications).131final float a = 2*(dax*dax + day*day);132final float b = 3*(dax*dbx + day*dby);133final float c = 2*(dax*cx + day*cy) + dbx*dbx + dby*dby;134final float d = dbx*cx + dby*cy;135return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0f, 1f);136}137138// Tries to find the roots of the function ROC(t)-w in [0, 1). It uses139// a variant of the false position algorithm to find the roots. False140// position requires that 2 initial values x0,x1 be given, and that the141// function must have opposite signs at those values. To find such142// values, we need the local extrema of the ROC function, for which we143// need the roots of its derivative; however, it's harder to find the144// roots of the derivative in this case than it is to find the roots145// of the original function. So, we find all points where this curve's146// first and second derivative are perpendicular, and we pretend these147// are our local extrema. There are at most 3 of these, so we will check148// at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection149// points, so roc-w can have at least 6 roots. This shouldn't be a150// problem for what we're trying to do (draw a nice looking curve).151int rootsOfROCMinusW(float[] roots, int off, final float w, final float err) {152// no OOB exception, because by now off<=6, and roots.length >= 10153assert off <= 6 && roots.length >= 10;154int ret = off;155int numPerpdfddf = perpendiculardfddf(roots, off);156float t0 = 0, ft0 = ROCsq(t0) - w*w;157roots[off + numPerpdfddf] = 1f; // always check interval end points158numPerpdfddf++;159for (int i = off; i < off + numPerpdfddf; i++) {160float t1 = roots[i], ft1 = ROCsq(t1) - w*w;161if (ft0 == 0f) {162roots[ret++] = t0;163} else if (ft1 * ft0 < 0f) { // have opposite signs164// (ROC(t)^2 == w^2) == (ROC(t) == w) is true because165// ROC(t) >= 0 for all t.166roots[ret++] = falsePositionROCsqMinusX(t0, t1, w*w, err);167}168t0 = t1;169ft0 = ft1;170}171172return ret - off;173}174175private static float eliminateInf(float x) {176return (x == Float.POSITIVE_INFINITY ? Float.MAX_VALUE :177(x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x));178}179180// A slight modification of the false position algorithm on wikipedia.181// This only works for the ROCsq-x functions. It might be nice to have182// the function as an argument, but that would be awkward in java6.183// TODO: It is something to consider for java8 (or whenever lambda184// expressions make it into the language), depending on how closures185// and turn out. Same goes for the newton's method186// algorithm in Helpers.java187private float falsePositionROCsqMinusX(float x0, float x1,188final float x, final float err)189{190final int iterLimit = 100;191int side = 0;192float t = x1, ft = eliminateInf(ROCsq(t) - x);193float s = x0, fs = eliminateInf(ROCsq(s) - x);194float r = s, fr;195for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {196r = (fs * t - ft * s) / (fs - ft);197fr = ROCsq(r) - x;198if (sameSign(fr, ft)) {199ft = fr; t = r;200if (side < 0) {201fs /= (1 << (-side));202side--;203} else {204side = -1;205}206} else if (fr * fs > 0) {207fs = fr; s = r;208if (side > 0) {209ft /= (1 << side);210side++;211} else {212side = 1;213}214} else {215break;216}217}218return r;219}220221private static boolean sameSign(double x, double y) {222// another way is to test if x*y > 0. This is bad for small x, y.223return (x < 0 && y < 0) || (x > 0 && y > 0);224}225226// returns the radius of curvature squared at t of this curve227// see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)228private float ROCsq(final float t) {229// dx=xat(t) and dy=yat(t). These calls have been inlined for efficiency230final float dx = t * (t * dax + dbx) + cx;231final float dy = t * (t * day + dby) + cy;232final float ddx = 2 * dax * t + dbx;233final float ddy = 2 * day * t + dby;234final float dx2dy2 = dx*dx + dy*dy;235final float ddx2ddy2 = ddx*ddx + ddy*ddy;236final float ddxdxddydy = ddx*dx + ddy*dy;237return dx2dy2*((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy));238}239240// curve to be broken should be in pts241// this will change the contents of pts but not Ts242// TODO: There's no reason for Ts to be an array. All we need is a sequence243// of t values at which to subdivide. An array statisfies this condition,244// but is unnecessarily restrictive. Ts should be an Iterator<Float> instead.245// Doing this will also make dashing easier, since we could easily make246// LengthIterator an Iterator<Float> and feed it to this function to simplify247// the loop in Dasher.somethingTo.248static Iterator<Integer> breakPtsAtTs(final float[] pts, final int type,249final float[] Ts, final int numTs)250{251assert pts.length >= 2*type && numTs <= Ts.length;252return new Iterator<Integer>() {253// these prevent object creation and destruction during autoboxing.254// Because of this, the compiler should be able to completely255// eliminate the boxing costs.256final Integer i0 = 0;257final Integer itype = type;258int nextCurveIdx = 0;259Integer curCurveOff = i0;260float prevT = 0;261262@Override public boolean hasNext() {263return nextCurveIdx < numTs + 1;264}265266@Override public Integer next() {267Integer ret;268if (nextCurveIdx < numTs) {269float curT = Ts[nextCurveIdx];270float splitT = (curT - prevT) / (1 - prevT);271Helpers.subdivideAt(splitT,272pts, curCurveOff,273pts, 0,274pts, type, type);275prevT = curT;276ret = i0;277curCurveOff = itype;278} else {279ret = curCurveOff;280}281nextCurveIdx++;282return ret;283}284285@Override public void remove() {}286};287}288}289290291292