0N/A/*
2362N/A * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.awt.geom;
0N/A
0N/Aimport java.awt.geom.Rectangle2D;
0N/Aimport java.awt.geom.PathIterator;
0N/Aimport java.awt.geom.QuadCurve2D;
0N/Aimport java.util.Vector;
0N/A
0N/Afinal class Order2 extends Curve {
0N/A private double x0;
0N/A private double y0;
0N/A private double cx0;
0N/A private double cy0;
0N/A private double x1;
0N/A private double y1;
0N/A private double xmin;
0N/A private double xmax;
0N/A
0N/A private double xcoeff0;
0N/A private double xcoeff1;
0N/A private double xcoeff2;
0N/A private double ycoeff0;
0N/A private double ycoeff1;
0N/A private double ycoeff2;
0N/A
0N/A public static void insert(Vector curves, double tmp[],
0N/A double x0, double y0,
0N/A double cx0, double cy0,
0N/A double x1, double y1,
0N/A int direction)
0N/A {
0N/A int numparams = getHorizontalParams(y0, cy0, y1, tmp);
0N/A if (numparams == 0) {
0N/A // We are using addInstance here to avoid inserting horisontal
0N/A // segments
0N/A addInstance(curves, x0, y0, cx0, cy0, x1, y1, direction);
0N/A return;
0N/A }
0N/A // assert(numparams == 1);
0N/A double t = tmp[0];
0N/A tmp[0] = x0; tmp[1] = y0;
0N/A tmp[2] = cx0; tmp[3] = cy0;
0N/A tmp[4] = x1; tmp[5] = y1;
0N/A split(tmp, 0, t);
0N/A int i0 = (direction == INCREASING)? 0 : 4;
0N/A int i1 = 4 - i0;
0N/A addInstance(curves, tmp[i0], tmp[i0 + 1], tmp[i0 + 2], tmp[i0 + 3],
0N/A tmp[i0 + 4], tmp[i0 + 5], direction);
0N/A addInstance(curves, tmp[i1], tmp[i1 + 1], tmp[i1 + 2], tmp[i1 + 3],
0N/A tmp[i1 + 4], tmp[i1 + 5], direction);
0N/A }
0N/A
0N/A public static void addInstance(Vector curves,
0N/A double x0, double y0,
0N/A double cx0, double cy0,
0N/A double x1, double y1,
0N/A int direction) {
0N/A if (y0 > y1) {
0N/A curves.add(new Order2(x1, y1, cx0, cy0, x0, y0, -direction));
0N/A } else if (y1 > y0) {
0N/A curves.add(new Order2(x0, y0, cx0, cy0, x1, y1, direction));
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Return the count of the number of horizontal sections of the
0N/A * specified quadratic Bezier curve. Put the parameters for the
0N/A * horizontal sections into the specified <code>ret</code> array.
0N/A * <p>
0N/A * If we examine the parametric equation in t, we have:
0N/A * Py(t) = C0*(1-t)^2 + 2*CP*t*(1-t) + C1*t^2
0N/A * = C0 - 2*C0*t + C0*t^2 + 2*CP*t - 2*CP*t^2 + C1*t^2
0N/A * = C0 + (2*CP - 2*C0)*t + (C0 - 2*CP + C1)*t^2
0N/A * Py(t) = (C0 - 2*CP + C1)*t^2 + (2*CP - 2*C0)*t + (C0)
0N/A * If we take the derivative, we get:
0N/A * Py(t) = At^2 + Bt + C
0N/A * dPy(t) = 2At + B = 0
0N/A * 2*(C0 - 2*CP + C1)t + 2*(CP - C0) = 0
0N/A * 2*(C0 - 2*CP + C1)t = 2*(C0 - CP)
0N/A * t = 2*(C0 - CP) / 2*(C0 - 2*CP + C1)
0N/A * t = (C0 - CP) / (C0 - CP + C1 - CP)
0N/A * Note that this method will return 0 if the equation is a line,
0N/A * which is either always horizontal or never horizontal.
0N/A * Completely horizontal curves need to be eliminated by other
0N/A * means outside of this method.
0N/A */
0N/A public static int getHorizontalParams(double c0, double cp, double c1,
0N/A double ret[]) {
0N/A if (c0 <= cp && cp <= c1) {
0N/A return 0;
0N/A }
0N/A c0 -= cp;
0N/A c1 -= cp;
0N/A double denom = c0 + c1;
0N/A // If denom == 0 then cp == (c0+c1)/2 and we have a line.
0N/A if (denom == 0) {
0N/A return 0;
0N/A }
0N/A double t = c0 / denom;
0N/A // No splits at t==0 and t==1
0N/A if (t <= 0 || t >= 1) {
0N/A return 0;
0N/A }
0N/A ret[0] = t;
0N/A return 1;
0N/A }
0N/A
0N/A /*
0N/A * Split the quadratic Bezier stored at coords[pos...pos+5] representing
0N/A * the paramtric range [0..1] into two subcurves representing the
0N/A * parametric subranges [0..t] and [t..1]. Store the results back
0N/A * into the array at coords[pos...pos+5] and coords[pos+4...pos+9].
0N/A */
0N/A public static void split(double coords[], int pos, double t) {
0N/A double x0, y0, cx, cy, x1, y1;
0N/A coords[pos+8] = x1 = coords[pos+4];
0N/A coords[pos+9] = y1 = coords[pos+5];
0N/A cx = coords[pos+2];
0N/A cy = coords[pos+3];
0N/A x1 = cx + (x1 - cx) * t;
0N/A y1 = cy + (y1 - cy) * t;
0N/A x0 = coords[pos+0];
0N/A y0 = coords[pos+1];
0N/A x0 = x0 + (cx - x0) * t;
0N/A y0 = y0 + (cy - y0) * t;
0N/A cx = x0 + (x1 - x0) * t;
0N/A cy = y0 + (y1 - y0) * t;
0N/A coords[pos+2] = x0;
0N/A coords[pos+3] = y0;
0N/A coords[pos+4] = cx;
0N/A coords[pos+5] = cy;
0N/A coords[pos+6] = x1;
0N/A coords[pos+7] = y1;
0N/A }
0N/A
0N/A public Order2(double x0, double y0,
0N/A double cx0, double cy0,
0N/A double x1, double y1,
0N/A int direction)
0N/A {
0N/A super(direction);
0N/A // REMIND: Better accuracy in the root finding methods would
0N/A // ensure that cy0 is in range. As it stands, it is never
0N/A // more than "1 mantissa bit" out of range...
0N/A if (cy0 < y0) {
0N/A cy0 = y0;
0N/A } else if (cy0 > y1) {
0N/A cy0 = y1;
0N/A }
0N/A this.x0 = x0;
0N/A this.y0 = y0;
0N/A this.cx0 = cx0;
0N/A this.cy0 = cy0;
0N/A this.x1 = x1;
0N/A this.y1 = y1;
0N/A xmin = Math.min(Math.min(x0, x1), cx0);
0N/A xmax = Math.max(Math.max(x0, x1), cx0);
0N/A xcoeff0 = x0;
0N/A xcoeff1 = cx0 + cx0 - x0 - x0;
0N/A xcoeff2 = x0 - cx0 - cx0 + x1;
0N/A ycoeff0 = y0;
0N/A ycoeff1 = cy0 + cy0 - y0 - y0;
0N/A ycoeff2 = y0 - cy0 - cy0 + y1;
0N/A }
0N/A
0N/A public int getOrder() {
0N/A return 2;
0N/A }
0N/A
0N/A public double getXTop() {
0N/A return x0;
0N/A }
0N/A
0N/A public double getYTop() {
0N/A return y0;
0N/A }
0N/A
0N/A public double getXBot() {
0N/A return x1;
0N/A }
0N/A
0N/A public double getYBot() {
0N/A return y1;
0N/A }
0N/A
0N/A public double getXMin() {
0N/A return xmin;
0N/A }
0N/A
0N/A public double getXMax() {
0N/A return xmax;
0N/A }
0N/A
0N/A public double getX0() {
0N/A return (direction == INCREASING) ? x0 : x1;
0N/A }
0N/A
0N/A public double getY0() {
0N/A return (direction == INCREASING) ? y0 : y1;
0N/A }
0N/A
0N/A public double getCX0() {
0N/A return cx0;
0N/A }
0N/A
0N/A public double getCY0() {
0N/A return cy0;
0N/A }
0N/A
0N/A public double getX1() {
0N/A return (direction == DECREASING) ? x0 : x1;
0N/A }
0N/A
0N/A public double getY1() {
0N/A return (direction == DECREASING) ? y0 : y1;
0N/A }
0N/A
0N/A public double XforY(double y) {
0N/A if (y <= y0) {
0N/A return x0;
0N/A }
0N/A if (y >= y1) {
0N/A return x1;
0N/A }
0N/A return XforT(TforY(y));
0N/A }
0N/A
0N/A public double TforY(double y) {
0N/A if (y <= y0) {
0N/A return 0;
0N/A }
0N/A if (y >= y1) {
0N/A return 1;
0N/A }
0N/A return TforY(y, ycoeff0, ycoeff1, ycoeff2);
0N/A }
0N/A
0N/A public static double TforY(double y,
0N/A double ycoeff0, double ycoeff1, double ycoeff2)
0N/A {
0N/A // The caller should have already eliminated y values
0N/A // outside of the y0 to y1 range.
0N/A ycoeff0 -= y;
0N/A if (ycoeff2 == 0.0) {
0N/A // The quadratic parabola has degenerated to a line.
0N/A // ycoeff1 should not be 0.0 since we have already eliminated
0N/A // totally horizontal lines, but if it is, then we will generate
0N/A // infinity here for the root, which will not be in the [0,1]
0N/A // range so we will pass to the failure code below.
0N/A double root = -ycoeff0 / ycoeff1;
0N/A if (root >= 0 && root <= 1) {
0N/A return root;
0N/A }
0N/A } else {
0N/A // From Numerical Recipes, 5.6, Quadratic and Cubic Equations
0N/A double d = ycoeff1 * ycoeff1 - 4.0 * ycoeff2 * ycoeff0;
0N/A // If d < 0.0, then there are no roots
0N/A if (d >= 0.0) {
0N/A d = Math.sqrt(d);
0N/A // For accuracy, calculate one root using:
0N/A // (-ycoeff1 +/- d) / 2ycoeff2
0N/A // and the other using:
0N/A // 2ycoeff0 / (-ycoeff1 +/- d)
0N/A // Choose the sign of the +/- so that ycoeff1+d
0N/A // gets larger in magnitude
0N/A if (ycoeff1 < 0.0) {
0N/A d = -d;
0N/A }
0N/A double q = (ycoeff1 + d) / -2.0;
0N/A // We already tested ycoeff2 for being 0 above
0N/A double root = q / ycoeff2;
0N/A if (root >= 0 && root <= 1) {
0N/A return root;
0N/A }
0N/A if (q != 0.0) {
0N/A root = ycoeff0 / q;
0N/A if (root >= 0 && root <= 1) {
0N/A return root;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A /* We failed to find a root in [0,1]. What could have gone wrong?
0N/A * First, remember that these curves are constructed to be monotonic
0N/A * in Y and totally horizontal curves have already been eliminated.
0N/A * Now keep in mind that the Y coefficients of the polynomial form
0N/A * of the curve are calculated from the Y coordinates which define
0N/A * our curve. They should theoretically define the same curve,
0N/A * but they can be off by a couple of bits of precision after the
0N/A * math is done and so can represent a slightly modified curve.
0N/A * This is normally not an issue except when we have solutions near
0N/A * the endpoints. Since the answers we get from solving the polynomial
0N/A * may be off by a few bits that means that they could lie just a
0N/A * few bits of precision outside the [0,1] range.
0N/A *
0N/A * Another problem could be that while the parametric curve defined
0N/A * by the Y coordinates has a local minima or maxima at or just
0N/A * outside of the endpoints, the polynomial form might express
0N/A * that same min/max just inside of and just shy of the Y coordinate
0N/A * of that endpoint. In that case, if we solve for a Y coordinate
0N/A * at or near that endpoint, we may be solving for a Y coordinate
0N/A * that is below that minima or above that maxima and we would find
0N/A * no solutions at all.
0N/A *
0N/A * In either case, we can assume that y is so near one of the
0N/A * endpoints that we can just collapse it onto the nearest endpoint
0N/A * without losing more than a couple of bits of precision.
0N/A */
0N/A // First calculate the midpoint between y0 and y1 and choose to
0N/A // return either 0.0 or 1.0 depending on whether y is above
0N/A // or below the midpoint...
0N/A // Note that we subtracted y from ycoeff0 above so both y0 and y1
0N/A // will be "relative to y" so we are really just looking at where
0N/A // zero falls with respect to the "relative midpoint" here.
0N/A double y0 = ycoeff0;
0N/A double y1 = ycoeff0 + ycoeff1 + ycoeff2;
0N/A return (0 < (y0 + y1) / 2) ? 0.0 : 1.0;
0N/A }
0N/A
0N/A public double XforT(double t) {
0N/A return (xcoeff2 * t + xcoeff1) * t + xcoeff0;
0N/A }
0N/A
0N/A public double YforT(double t) {
0N/A return (ycoeff2 * t + ycoeff1) * t + ycoeff0;
0N/A }
0N/A
0N/A public double dXforT(double t, int deriv) {
0N/A switch (deriv) {
0N/A case 0:
0N/A return (xcoeff2 * t + xcoeff1) * t + xcoeff0;
0N/A case 1:
0N/A return 2 * xcoeff2 * t + xcoeff1;
0N/A case 2:
0N/A return 2 * xcoeff2;
0N/A default:
0N/A return 0;
0N/A }
0N/A }
0N/A
0N/A public double dYforT(double t, int deriv) {
0N/A switch (deriv) {
0N/A case 0:
0N/A return (ycoeff2 * t + ycoeff1) * t + ycoeff0;
0N/A case 1:
0N/A return 2 * ycoeff2 * t + ycoeff1;
0N/A case 2:
0N/A return 2 * ycoeff2;
0N/A default:
0N/A return 0;
0N/A }
0N/A }
0N/A
0N/A public double nextVertical(double t0, double t1) {
0N/A double t = -xcoeff1 / (2 * xcoeff2);
0N/A if (t > t0 && t < t1) {
0N/A return t;
0N/A }
0N/A return t1;
0N/A }
0N/A
0N/A public void enlarge(Rectangle2D r) {
0N/A r.add(x0, y0);
0N/A double t = -xcoeff1 / (2 * xcoeff2);
0N/A if (t > 0 && t < 1) {
0N/A r.add(XforT(t), YforT(t));
0N/A }
0N/A r.add(x1, y1);
0N/A }
0N/A
0N/A public Curve getSubCurve(double ystart, double yend, int dir) {
0N/A double t0, t1;
0N/A if (ystart <= y0) {
0N/A if (yend >= y1) {
0N/A return getWithDirection(dir);
0N/A }
0N/A t0 = 0;
0N/A } else {
0N/A t0 = TforY(ystart, ycoeff0, ycoeff1, ycoeff2);
0N/A }
0N/A if (yend >= y1) {
0N/A t1 = 1;
0N/A } else {
0N/A t1 = TforY(yend, ycoeff0, ycoeff1, ycoeff2);
0N/A }
0N/A double eqn[] = new double[10];
0N/A eqn[0] = x0;
0N/A eqn[1] = y0;
0N/A eqn[2] = cx0;
0N/A eqn[3] = cy0;
0N/A eqn[4] = x1;
0N/A eqn[5] = y1;
0N/A if (t1 < 1) {
0N/A split(eqn, 0, t1);
0N/A }
0N/A int i;
0N/A if (t0 <= 0) {
0N/A i = 0;
0N/A } else {
0N/A split(eqn, 0, t0 / t1);
0N/A i = 4;
0N/A }
0N/A return new Order2(eqn[i+0], ystart,
0N/A eqn[i+2], eqn[i+3],
0N/A eqn[i+4], yend,
0N/A dir);
0N/A }
0N/A
0N/A public Curve getReversedCurve() {
0N/A return new Order2(x0, y0, cx0, cy0, x1, y1, -direction);
0N/A }
0N/A
0N/A public int getSegment(double coords[]) {
0N/A coords[0] = cx0;
0N/A coords[1] = cy0;
0N/A if (direction == INCREASING) {
0N/A coords[2] = x1;
0N/A coords[3] = y1;
0N/A } else {
0N/A coords[2] = x0;
0N/A coords[3] = y0;
0N/A }
0N/A return PathIterator.SEG_QUADTO;
0N/A }
0N/A
0N/A public String controlPointString() {
0N/A return ("("+round(cx0)+", "+round(cy0)+"), ");
0N/A }
0N/A}