Return the geometry of an ellipse based on its four top points. - Java java.lang

Java examples for java.lang:Math Geometry

Description

Return the geometry of an ellipse based on its four top points.

Demo Code



public class Main{
    /**/*from   w  w w.j a  va  2 s .  c  o  m*/
     * Return the geometry of an ellipse based on its four top points. Integer
     * domain. The method use the generic createEllipse() method for the main
     * task, and then transforms this according to any rotation or skew defined
     * by the given top points.
     *
     * @param x X array of four top points of ellipse.
     * @param y Y array of four top points of ellipse.
     * @return Geometry of ellipse [x,y,x,y...].
     */
    public static int[] createEllipse(int[] x, int[] y) {
        // Center of ellipse
        int x0 = (x[0] + x[2]) / 2;
        int y0 = (y[0] + y[2]) / 2;

        // Angle between axis define skew
        double[] p0 = { (double) x0, (double) y0, 0.0 };
        double[] p1 = { (double) x[0], (double) y[0], 0.0 };
        double[] p2 = { (double) x[1], (double) y[1], 0.0 };

        double axisAngle = GeometryUtils.computeAngle(p0, p1, p2);

        // dx / dy  
        double dx = GeometryUtils.length(x0, y0, x[1], y[1]);
        double dy = GeometryUtils.length(x0, y0, x[0], y[0])
                * Math.sin(axisAngle);

        // Create geometry for unrotated / unsheared ellipse
        int[] ellipse = createEllipse(x0, y0, (int) Math.round(dx),
                (int) Math.round(dy));
        int nPoints = ellipse.length / 2;

        // Shear if neccessary. If angle is close to 90 there is no shear.
        // If angle is close to 0 or 180 shear is infinite, and we set
        // it to zero as well.
        if (!GeometryUtils.equals(axisAngle, Math.PI / 2.0, 0.1)
                && !GeometryUtils.equals(axisAngle, Math.PI, 0.1)
                && !GeometryUtils.equals(axisAngle, 0.0, 0.1)) {
            double xShear = 1.0 / Math.tan(axisAngle);
            for (int i = 0; i < nPoints; i++) {
                ellipse[i * 2 + 0] += Math.round((ellipse[i * 2 + 1] - y0)
                        * xShear);
            }
        }

        // Rotate
        int ddx = x[1] - x0;
        int ddy = y0 - y[1];

        double angle;
        if (ddx == 0 && ddy == 0) {
            angle = 0.0;
        } else if (ddx == 0) {
            angle = Math.PI / 2.0;
        } else {
            angle = Math.atan((double) ddy / (double) ddx);
        }

        double cosAngle = Math.cos(angle);
        double sinAngle = Math.sin(angle);

        for (int i = 0; i < nPoints; i++) {
            int xr = (int) Math.round(x0 + (ellipse[i * 2 + 0] - x0)
                    * cosAngle - (ellipse[i * 2 + 1] - y0) * sinAngle);
            int yr = (int) Math.round(y0 - (ellipse[i * 2 + 1] - y0)
                    * cosAngle - (ellipse[i * 2 + 0] - x0) * sinAngle);

            ellipse[i * 2 + 0] = xr;
            ellipse[i * 2 + 1] = yr;
        }

        return ellipse;
    }
    /**
     * Create the geometry for an unrotated, unskewed ellipse. Integer domain.
     *
     * @param x0 X center of ellipse.
     * @param y0 Y center of ellipse.
     * @param dx X ellipse radius.
     * @param dy Y ellipse radius.
     * @return Ellipse geometry [x,y,x,y,...].
     */
    public static int[] createEllipse(int x0, int y0, int dx, int dy) {
        // Make sure deltas are positive
        dx = Math.abs(dx);
        dy = Math.abs(dy);

        // This is an approximate number of points we need to make a smooth
        // surface on a quater of the ellipse
        int nPoints = dx > dy ? dx : dy;
        nPoints /= 2;
        if (nPoints < 1) {
            nPoints = 1;
        }

        // Allocate arrays for holding the complete set of vertices around
        // the ellipse. Note that this is a complete ellipse: First and last
        // point coincide.
        int[] ellipse = new int[nPoints * 8 + 2];

        // Compute some intermediate results to save time in the inner loop
        int dxdy = dx * dy;
        int dx2 = dx * dx;
        int dy2 = dy * dy;

        // Handcode the entries in the four "corner" points of the ellipse,
        // i.e. at point 0, 90, 180, 270 and 360 degrees
        ellipse[nPoints * 0 + 0] = x0 + dx;
        ellipse[nPoints * 0 + 1] = y0;

        ellipse[nPoints * 8 + 0] = x0 + dx;
        ellipse[nPoints * 8 + 1] = y0;

        ellipse[nPoints * 2 + 0] = x0;
        ellipse[nPoints * 2 + 1] = y0 - dy;

        ellipse[nPoints * 4 + 0] = x0 - dx;
        ellipse[nPoints * 4 + 1] = y0;

        ellipse[nPoints * 6 + 0] = x0;
        ellipse[nPoints * 6 + 1] = y0 + dy;

        // Find the angle step
        double angleStep = nPoints > 0 ? Math.PI / 2.0 / nPoints : 0.0;

        // Loop over angles from 0 to 90. The rest of the ellipse can be derrived
        // from this first quadrant. For each angle set the four corresponding
        // ellipse points.
        double a = 0.0;
        for (int i = 1; i < nPoints; i++) {
            a += angleStep;

            double t = Math.tan(a);

            double x = (double) dxdy / Math.sqrt(t * t * dx2 + dy2);
            double y = x * t + 0.5;

            int xi = (int) (x + 0.5);
            int yi = (int) (y + 0.5);

            ellipse[(nPoints * 0 + i) * 2 + 0] = x0 + xi;
            ellipse[(nPoints * 2 - i) * 2 + 0] = x0 - xi;
            ellipse[(nPoints * 2 + i) * 2 + 0] = x0 - xi;
            ellipse[(nPoints * 4 - i) * 2 + 0] = x0 + xi;

            ellipse[(nPoints * 0 + i) * 2 + 1] = y0 - yi;
            ellipse[(nPoints * 2 - i) * 2 + 1] = y0 - yi;
            ellipse[(nPoints * 2 + i) * 2 + 1] = y0 + yi;
            ellipse[(nPoints * 4 - i) * 2 + 1] = y0 + yi;
        }

        return ellipse;
    }
    /**
     * Create the geometry for an unrotated, unskewed ellipse. Floating point
     * domain.
     *
     * @param x0 X center of ellipse.
     * @param y0 Y center of ellipse.
     * @param dx X ellipse radius.
     * @param dy Y ellipse radius.
     * @return Ellipse geometry [x,y,x,y,...].
     */
    public static double[] createEllipse(double x0, double y0, double dx,
            double dy) {
        // Make sure deltas are positive
        dx = Math.abs(dx);
        dy = Math.abs(dy);

        // As we don't know the resolution of the appliance of the ellipse
        // we create one vertex per 2nd degree. The nPoints variable holds
        // number of points in a quater of the ellipse.
        int nPoints = 45;

        // Allocate arrays for holding the complete set of vertices around
        // the ellipse. Note that this is a complete ellipse: First and last
        // point coincide.
        double[] ellipse = new double[nPoints * 8 + 2];

        // Compute some intermediate results to save time in the inner loop
        double dxdy = dx * dy;
        double dx2 = dx * dx;
        double dy2 = dy * dy;

        // Handcode the entries in the four "corner" points of the ellipse,
        // i.e. at point 0, 90, 180, 270 and 360 degrees
        ellipse[nPoints * 0 + 0] = x0 + dx;
        ellipse[nPoints * 0 + 1] = y0;

        ellipse[nPoints * 8 + 0] = x0 + dx;
        ellipse[nPoints * 8 + 1] = y0;

        ellipse[nPoints * 2 + 0] = x0;
        ellipse[nPoints * 2 + 1] = y0 - dy;

        ellipse[nPoints * 4 + 0] = x0 - dx;
        ellipse[nPoints * 4 + 1] = y0;

        ellipse[nPoints * 6 + 0] = x0;
        ellipse[nPoints * 6 + 1] = y0 + dy;

        // Find the angle step
        double angleStep = nPoints > 0 ? Math.PI / 2.0 / nPoints : 0.0;

        // Loop over angles from 0 to 90. The rest of the ellipse can be derrived
        // from this first quadrant. For each angle set the four corresponding
        // ellipse points.
        double a = 0.0;
        for (int i = 1; i < nPoints; i++) {
            a += angleStep;

            double t = Math.tan(a);

            double x = (double) dxdy / Math.sqrt(t * t * dx2 + dy2);
            double y = x * t + 0.5;

            ellipse[(nPoints * 0 + i) * 2 + 0] = x0 + x;
            ellipse[(nPoints * 2 - i) * 2 + 0] = x0 - x;
            ellipse[(nPoints * 2 + i) * 2 + 0] = x0 - x;
            ellipse[(nPoints * 4 - i) * 2 + 0] = x0 + x;

            ellipse[(nPoints * 0 + i) * 2 + 1] = y0 - y;
            ellipse[(nPoints * 2 - i) * 2 + 1] = y0 - y;
            ellipse[(nPoints * 2 + i) * 2 + 1] = y0 + y;
            ellipse[(nPoints * 4 - i) * 2 + 1] = y0 + y;
        }

        return ellipse;
    }
    /**
     * Find the angle between three points. P0 is center point
     *
     * @param p0, p1, p2 Three points finding angle between [x,y,z].
     * @return Angle (in radians) between given points.
     */
    public static double computeAngle(double[] p0, double[] p1, double[] p2) {
        double[] v0 = GeometryUtils.createVector(p0, p1);
        double[] v1 = GeometryUtils.createVector(p0, p2);

        double dotProduct = GeometryUtils.computeDotProduct(v0, v1);

        double length1 = GeometryUtils.length(v0);
        double length2 = GeometryUtils.length(v1);

        double denominator = length1 * length2;

        double product = denominator != 0.0 ? dotProduct / denominator
                : 0.0;

        double angle = Math.acos(product);

        return angle;
    }
    /**
     * Return the length of a vector.
     *
     * @param v Vector to compute length of [x,y,z].
     * @return Length of vector.
     */
    public static double length(double[] v) {
        return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
    }
    /**
     * Compute distance between two points.
     *
     * @param p0, p1 Points to compute distance between [x,y,z].
     * @return Distance between points.
     */
    public static double length(double[] p0, double[] p1) {
        double[] v = GeometryUtils.createVector(p0, p1);
        return length(v);
    }
    /**
     * Compute the length of the line from (x0,y0) to (x1,y1)
     *
     * @param x0, y0 First line end point.
     * @param x1, y1 Second line end point.
     * @return Length of line from (x0,y0) to (x1,y1).
     */
    public static double length(int x0, int y0, int x1, int y1) {
        return GeometryUtils.length((double) x0, (double) y0, (double) x1,
                (double) y1);
    }
    /**
     * Compute the length of the line from (x0,y0) to (x1,y1)
     *
     * @param x0, y0 First line end point.
     * @param x1, y1 Second line end point.
     * @return Length of line from (x0,y0) to (x1,y1).
     */
    public static double length(double x0, double y0, double x1, double y1) {
        double dx = x1 - x0;
        double dy = y1 - y0;

        return Math.sqrt(dx * dx + dy * dy);
    }
    /**
     * Compute the length of a polyline.
     *
     * @param x, y Arrays of x,y coordinates
     * @param nPoints Number of elements in the above.
     * @param isClosed True if this is a closed polygon, false otherwise
     * @return Length of polyline defined by x, y and nPoints.
     */
    public static double length(int[] x, int[] y, boolean isClosed) {
        double length = 0.0;

        int nPoints = x.length;
        for (int i = 0; i < nPoints - 1; i++) {
            length += GeometryUtils.length(x[i], y[i], x[i + 1], y[i + 1]);
        }

        // Add last leg if this is a polygon
        if (isClosed && nPoints > 1) {
            length += GeometryUtils.length(x[nPoints - 1], y[nPoints - 1],
                    x[0], y[0]);
        }

        return length;
    }
    /**
     * Check if two double precision numbers are "equal", i.e. close enough to a
     * given limit.
     *
     * @param a First number to check
     * @param b Second number to check
     * @param limit The definition of "equal".
     * @return True if the twho numbers are "equal", false otherwise
     */
    private static boolean equals(double a, double b, double limit) {
        return Math.abs(a - b) < limit;
    }
    /**
     * Check if two double precision numbers are "equal", i.e. close enough to a
     * prespecified limit.
     *
     * @param a First number to check
     * @param b Second number to check
     * @return True if the twho numbers are "equal", false otherwise
     */
    private static boolean equals(double a, double b) {
        return equals(a, b, 1.0e-5);
    }
    /**
     * Construct the vector specified by two points.
     *
     * @param p0, p1 Points the construct vector between [x,y,z].
     * @return v Vector from p0 to p1 [x,y,z].
     */
    public static double[] createVector(double[] p0, double[] p1) {
        double v[] = { p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2] };
        return v;
    }
    /**
     * Compute the dot product (a scalar) between two vectors.
     *
     * @param v0, v1 Vectors to compute dot product between [x,y,z].
     * @return Dot product of given vectors.
     */
    public static double computeDotProduct(double[] v0, double[] v1) {
        return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2];
    }
}

Related Tutorials