alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (Order2.java)

This example Java source code file (Order2.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

awt, curve, decreasing, geometry, increasing, order2, string, tfory, util, xfort, xfory, yfort

The Order2.java Java example source code

/*
 * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.awt.geom;

import java.awt.geom.Rectangle2D;
import java.awt.geom.PathIterator;
import java.awt.geom.QuadCurve2D;
import java.util.Vector;

final class Order2 extends Curve {
    private double x0;
    private double y0;
    private double cx0;
    private double cy0;
    private double x1;
    private double y1;
    private double xmin;
    private double xmax;

    private double xcoeff0;
    private double xcoeff1;
    private double xcoeff2;
    private double ycoeff0;
    private double ycoeff1;
    private double ycoeff2;

    public static void insert(Vector curves, double tmp[],
                              double x0, double y0,
                              double cx0, double cy0,
                              double x1, double y1,
                              int direction)
    {
        int numparams = getHorizontalParams(y0, cy0, y1, tmp);
        if (numparams == 0) {
            // We are using addInstance here to avoid inserting horisontal
            // segments
            addInstance(curves, x0, y0, cx0, cy0, x1, y1, direction);
            return;
        }
        // assert(numparams == 1);
        double t = tmp[0];
        tmp[0] = x0;  tmp[1] = y0;
        tmp[2] = cx0; tmp[3] = cy0;
        tmp[4] = x1;  tmp[5] = y1;
        split(tmp, 0, t);
        int i0 = (direction == INCREASING)? 0 : 4;
        int i1 = 4 - i0;
        addInstance(curves, tmp[i0], tmp[i0 + 1], tmp[i0 + 2], tmp[i0 + 3],
                    tmp[i0 + 4], tmp[i0 + 5], direction);
        addInstance(curves, tmp[i1], tmp[i1 + 1], tmp[i1 + 2], tmp[i1 + 3],
                    tmp[i1 + 4], tmp[i1 + 5], direction);
    }

    public static void addInstance(Vector curves,
                                   double x0, double y0,
                                   double cx0, double cy0,
                                   double x1, double y1,
                                   int direction) {
        if (y0 > y1) {
            curves.add(new Order2(x1, y1, cx0, cy0, x0, y0, -direction));
        } else if (y1 > y0) {
            curves.add(new Order2(x0, y0, cx0, cy0, x1, y1, direction));
        }
    }

    /*
     * Return the count of the number of horizontal sections of the
     * specified quadratic Bezier curve.  Put the parameters for the
     * horizontal sections into the specified <code>ret array.
     * <p>
     * If we examine the parametric equation in t, we have:
     *     Py(t) = C0*(1-t)^2 + 2*CP*t*(1-t) + C1*t^2
     *           = C0 - 2*C0*t + C0*t^2 + 2*CP*t - 2*CP*t^2 + C1*t^2
     *           = C0 + (2*CP - 2*C0)*t + (C0 - 2*CP + C1)*t^2
     *     Py(t) = (C0 - 2*CP + C1)*t^2 + (2*CP - 2*C0)*t + (C0)
     * If we take the derivative, we get:
     *     Py(t) = At^2 + Bt + C
     *     dPy(t) = 2At + B = 0
     *     2*(C0 - 2*CP + C1)t + 2*(CP - C0) = 0
     *     2*(C0 - 2*CP + C1)t = 2*(C0 - CP)
     *     t = 2*(C0 - CP) / 2*(C0 - 2*CP + C1)
     *     t = (C0 - CP) / (C0 - CP + C1 - CP)
     * Note that this method will return 0 if the equation is a line,
     * which is either always horizontal or never horizontal.
     * Completely horizontal curves need to be eliminated by other
     * means outside of this method.
     */
    public static int getHorizontalParams(double c0, double cp, double c1,
                                          double ret[]) {
        if (c0 <= cp && cp <= c1) {
            return 0;
        }
        c0 -= cp;
        c1 -= cp;
        double denom = c0 + c1;
        // If denom == 0 then cp == (c0+c1)/2 and we have a line.
        if (denom == 0) {
            return 0;
        }
        double t = c0 / denom;
        // No splits at t==0 and t==1
        if (t <= 0 || t >= 1) {
            return 0;
        }
        ret[0] = t;
        return 1;
    }

    /*
     * Split the quadratic Bezier stored at coords[pos...pos+5] representing
     * the paramtric range [0..1] into two subcurves representing the
     * parametric subranges [0..t] and [t..1].  Store the results back
     * into the array at coords[pos...pos+5] and coords[pos+4...pos+9].
     */
    public static void split(double coords[], int pos, double t) {
        double x0, y0, cx, cy, x1, y1;
        coords[pos+8] = x1 = coords[pos+4];
        coords[pos+9] = y1 = coords[pos+5];
        cx = coords[pos+2];
        cy = coords[pos+3];
        x1 = cx + (x1 - cx) * t;
        y1 = cy + (y1 - cy) * t;
        x0 = coords[pos+0];
        y0 = coords[pos+1];
        x0 = x0 + (cx - x0) * t;
        y0 = y0 + (cy - y0) * t;
        cx = x0 + (x1 - x0) * t;
        cy = y0 + (y1 - y0) * t;
        coords[pos+2] = x0;
        coords[pos+3] = y0;
        coords[pos+4] = cx;
        coords[pos+5] = cy;
        coords[pos+6] = x1;
        coords[pos+7] = y1;
    }

    public Order2(double x0, double y0,
                  double cx0, double cy0,
                  double x1, double y1,
                  int direction)
    {
        super(direction);
        // REMIND: Better accuracy in the root finding methods would
        //  ensure that cy0 is in range.  As it stands, it is never
        //  more than "1 mantissa bit" out of range...
        if (cy0 < y0) {
            cy0 = y0;
        } else if (cy0 > y1) {
            cy0 = y1;
        }
        this.x0 = x0;
        this.y0 = y0;
        this.cx0 = cx0;
        this.cy0 = cy0;
        this.x1 = x1;
        this.y1 = y1;
        xmin = Math.min(Math.min(x0, x1), cx0);
        xmax = Math.max(Math.max(x0, x1), cx0);
        xcoeff0 = x0;
        xcoeff1 = cx0 + cx0 - x0 - x0;
        xcoeff2 = x0 - cx0 - cx0 + x1;
        ycoeff0 = y0;
        ycoeff1 = cy0 + cy0 - y0 - y0;
        ycoeff2 = y0 - cy0 - cy0 + y1;
    }

    public int getOrder() {
        return 2;
    }

    public double getXTop() {
        return x0;
    }

    public double getYTop() {
        return y0;
    }

    public double getXBot() {
        return x1;
    }

    public double getYBot() {
        return y1;
    }

    public double getXMin() {
        return xmin;
    }

    public double getXMax() {
        return xmax;
    }

    public double getX0() {
        return (direction == INCREASING) ? x0 : x1;
    }

    public double getY0() {
        return (direction == INCREASING) ? y0 : y1;
    }

    public double getCX0() {
        return cx0;
    }

    public double getCY0() {
        return cy0;
    }

    public double getX1() {
        return (direction == DECREASING) ? x0 : x1;
    }

    public double getY1() {
        return (direction == DECREASING) ? y0 : y1;
    }

    public double XforY(double y) {
        if (y <= y0) {
            return x0;
        }
        if (y >= y1) {
            return x1;
        }
        return XforT(TforY(y));
    }

    public double TforY(double y) {
        if (y <= y0) {
            return 0;
        }
        if (y >= y1) {
            return 1;
        }
        return TforY(y, ycoeff0, ycoeff1, ycoeff2);
    }

    public static double TforY(double y,
                               double ycoeff0, double ycoeff1, double ycoeff2)
    {
        // The caller should have already eliminated y values
        // outside of the y0 to y1 range.
        ycoeff0 -= y;
        if (ycoeff2 == 0.0) {
            // The quadratic parabola has degenerated to a line.
            // ycoeff1 should not be 0.0 since we have already eliminated
            // totally horizontal lines, but if it is, then we will generate
            // infinity here for the root, which will not be in the [0,1]
            // range so we will pass to the failure code below.
            double root = -ycoeff0 / ycoeff1;
            if (root >= 0 && root <= 1) {
                return root;
            }
        } else {
            // From Numerical Recipes, 5.6, Quadratic and Cubic Equations
            double d = ycoeff1 * ycoeff1 - 4.0 * ycoeff2 * ycoeff0;
            // If d < 0.0, then there are no roots
            if (d >= 0.0) {
                d = Math.sqrt(d);
                // For accuracy, calculate one root using:
                //     (-ycoeff1 +/- d) / 2ycoeff2
                // and the other using:
                //     2ycoeff0 / (-ycoeff1 +/- d)
                // Choose the sign of the +/- so that ycoeff1+d
                // gets larger in magnitude
                if (ycoeff1 < 0.0) {
                    d = -d;
                }
                double q = (ycoeff1 + d) / -2.0;
                // We already tested ycoeff2 for being 0 above
                double root = q / ycoeff2;
                if (root >= 0 && root <= 1) {
                    return root;
                }
                if (q != 0.0) {
                    root = ycoeff0 / q;
                    if (root >= 0 && root <= 1) {
                        return root;
                    }
                }
            }
        }
        /* We failed to find a root in [0,1].  What could have gone wrong?
         * First, remember that these curves are constructed to be monotonic
         * in Y and totally horizontal curves have already been eliminated.
         * Now keep in mind that the Y coefficients of the polynomial form
         * of the curve are calculated from the Y coordinates which define
         * our curve.  They should theoretically define the same curve,
         * but they can be off by a couple of bits of precision after the
         * math is done and so can represent a slightly modified curve.
         * This is normally not an issue except when we have solutions near
         * the endpoints.  Since the answers we get from solving the polynomial
         * may be off by a few bits that means that they could lie just a
         * few bits of precision outside the [0,1] range.
         *
         * Another problem could be that while the parametric curve defined
         * by the Y coordinates has a local minima or maxima at or just
         * outside of the endpoints, the polynomial form might express
         * that same min/max just inside of and just shy of the Y coordinate
         * of that endpoint.  In that case, if we solve for a Y coordinate
         * at or near that endpoint, we may be solving for a Y coordinate
         * that is below that minima or above that maxima and we would find
         * no solutions at all.
         *
         * In either case, we can assume that y is so near one of the
         * endpoints that we can just collapse it onto the nearest endpoint
         * without losing more than a couple of bits of precision.
         */
        // First calculate the midpoint between y0 and y1 and choose to
        // return either 0.0 or 1.0 depending on whether y is above
        // or below the midpoint...
        // Note that we subtracted y from ycoeff0 above so both y0 and y1
        // will be "relative to y" so we are really just looking at where
        // zero falls with respect to the "relative midpoint" here.
        double y0 = ycoeff0;
        double y1 = ycoeff0 + ycoeff1 + ycoeff2;
        return (0 < (y0 + y1) / 2) ? 0.0 : 1.0;
    }

    public double XforT(double t) {
        return (xcoeff2 * t + xcoeff1) * t + xcoeff0;
    }

    public double YforT(double t) {
        return (ycoeff2 * t + ycoeff1) * t + ycoeff0;
    }

    public double dXforT(double t, int deriv) {
        switch (deriv) {
        case 0:
            return (xcoeff2 * t + xcoeff1) * t + xcoeff0;
        case 1:
            return 2 * xcoeff2 * t + xcoeff1;
        case 2:
            return 2 * xcoeff2;
        default:
            return 0;
        }
    }

    public double dYforT(double t, int deriv) {
        switch (deriv) {
        case 0:
            return (ycoeff2 * t + ycoeff1) * t + ycoeff0;
        case 1:
            return 2 * ycoeff2 * t + ycoeff1;
        case 2:
            return 2 * ycoeff2;
        default:
            return 0;
        }
    }

    public double nextVertical(double t0, double t1) {
        double t = -xcoeff1 / (2 * xcoeff2);
        if (t > t0 && t < t1) {
            return t;
        }
        return t1;
    }

    public void enlarge(Rectangle2D r) {
        r.add(x0, y0);
        double t = -xcoeff1 / (2 * xcoeff2);
        if (t > 0 && t < 1) {
            r.add(XforT(t), YforT(t));
        }
        r.add(x1, y1);
    }

    public Curve getSubCurve(double ystart, double yend, int dir) {
        double t0, t1;
        if (ystart <= y0) {
            if (yend >= y1) {
                return getWithDirection(dir);
            }
            t0 = 0;
        } else {
            t0 = TforY(ystart, ycoeff0, ycoeff1, ycoeff2);
        }
        if (yend >= y1) {
            t1 = 1;
        } else {
            t1 = TforY(yend, ycoeff0, ycoeff1, ycoeff2);
        }
        double eqn[] = new double[10];
        eqn[0] = x0;
        eqn[1] = y0;
        eqn[2] = cx0;
        eqn[3] = cy0;
        eqn[4] = x1;
        eqn[5] = y1;
        if (t1 < 1) {
            split(eqn, 0, t1);
        }
        int i;
        if (t0 <= 0) {
            i = 0;
        } else {
            split(eqn, 0, t0 / t1);
            i = 4;
        }
        return new Order2(eqn[i+0], ystart,
                          eqn[i+2], eqn[i+3],
                          eqn[i+4], yend,
                          dir);
    }

    public Curve getReversedCurve() {
        return new Order2(x0, y0, cx0, cy0, x1, y1, -direction);
    }

    public int getSegment(double coords[]) {
        coords[0] = cx0;
        coords[1] = cy0;
        if (direction == INCREASING) {
            coords[2] = x1;
            coords[3] = y1;
        } else {
            coords[2] = x0;
            coords[3] = y0;
        }
        return PathIterator.SEG_QUADTO;
    }

    public String controlPointString() {
        return ("("+round(cx0)+", "+round(cy0)+"), ");
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java Order2.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.