Created
February 7, 2017 07:26
-
-
Save afshin-hoseini/513a82271f683811c6a04abeeba508d0 to your computer and use it in GitHub Desktop.
Geometry functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * (C) 2004 - Geotechnical Software Services | |
| * | |
| * This code is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * 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 Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this program; if not, write to the Free | |
| * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | |
| * MA 02111-1307, USA. | |
| */ | |
| package no.geosoft.cc.geometry; | |
| /** | |
| * Collection of geometry utility methods. All methods are static. | |
| * | |
| * @author <a href="mailto:[email protected]">GeoSoft</a> | |
| */ | |
| public final class Geometry | |
| { | |
| /** | |
| * Return true if c is between a and b. | |
| */ | |
| private static boolean isBetween (int a, int b, int c) | |
| { | |
| return b > a ? c >= a && c <= b : c >= b && c <= a; | |
| } | |
| /** | |
| * Return true if c is between a and b. | |
| */ | |
| private static boolean isBetween (double a, double b, double c) | |
| { | |
| return b > a ? c >= a && c <= b : c >= b && c <= a; | |
| } | |
| /** | |
| * 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); | |
| } | |
| /** | |
| * Return smallest of four numbers. | |
| * | |
| * @param a First number to find smallest among. | |
| * @param b Second number to find smallest among. | |
| * @param c Third number to find smallest among. | |
| * @param d Fourth number to find smallest among. | |
| * @return Smallest of a, b, c and d. | |
| */ | |
| private static double min (double a, double b, double c, double d) | |
| { | |
| return Math.min (Math.min (a, b), Math.min (c, d)); | |
| } | |
| /** | |
| * Return largest of four numbers. | |
| * | |
| * @param a First number to find largest among. | |
| * @param b Second number to find largest among. | |
| * @param c Third number to find largest among. | |
| * @param d Fourth number to find largest among. | |
| * @return Largest of a, b, c and d. | |
| */ | |
| private static double max (double a, double b, double c, double d) | |
| { | |
| return Math.max (Math.max (a, b), Math.max (c, d)); | |
| } | |
| /** | |
| * Check if a specified point is inside a specified rectangle. | |
| * | |
| * @param x0, y0, x1, y1 Upper left and lower right corner of rectangle | |
| * (inclusive) | |
| * @param x,y Point to check. | |
| * @return True if the point is inside the rectangle, | |
| * false otherwise. | |
| */ | |
| public static boolean isPointInsideRectangle (int x0, int y0, int x1, int y1, | |
| int x, int y) | |
| { | |
| return x >= x0 && x < x1 && y >= y0 && y < y1; | |
| } | |
| /** | |
| * Check if a given point is inside a given (complex) polygon. | |
| * | |
| * @param x, y Polygon. | |
| * @param pointX, pointY Point to check. | |
| * @return True if the given point is inside the polygon, false otherwise. | |
| */ | |
| public static boolean isPointInsidePolygon (double[] x, double[] y, | |
| double pointX, double pointY) | |
| { | |
| boolean isInside = false; | |
| int nPoints = x.length; | |
| int j = 0; | |
| for (int i = 0; i < nPoints; i++) { | |
| j++; | |
| if (j == nPoints) j = 0; | |
| if (y[i] < pointY && y[j] >= pointY || y[j] < pointY && y[i] >= pointY) { | |
| if (x[i] + (pointY - y[i]) / (y[j] - y[i]) * (x[j] - x[i]) < pointX) { | |
| isInside = !isInside; | |
| } | |
| } | |
| } | |
| return isInside; | |
| } | |
| /** | |
| * Check if a given point is inside a given polygon. Integer domain. | |
| * | |
| * @param x, y Polygon. | |
| * @param pointX, pointY Point to check. | |
| * @return True if the given point is inside the polygon, false otherwise. | |
| */ | |
| public static boolean isPointInsidePolygon (int[] x, int[] y, | |
| int pointX, int pointY) | |
| { | |
| boolean isInside = false; | |
| int nPoints = x.length; | |
| int j = 0; | |
| for (int i = 0; i < nPoints; i++) { | |
| j++; | |
| if (j == nPoints) j = 0; | |
| if (y[i] < pointY && y[j] >= pointY || y[j] < pointY && y[i] >= pointY) { | |
| if (x[i] + (double) (pointY - y[i]) / (double) (y[j] - y[i]) * | |
| (x[j] - x[i]) < pointX) { | |
| isInside = !isInside; | |
| } | |
| } | |
| } | |
| return isInside; | |
| } | |
| /** | |
| * Find the point on the line p0,p1 [x,y,z] a given fraction from p0. | |
| * Fraction of 0.0 whould give back p0, 1.0 give back p1, 0.5 returns | |
| * midpoint of line p0,p1 and so on. F | |
| * raction can be >1 and it can be negative to return any point on the | |
| * line specified by p0,p1. | |
| * | |
| * @param p0 First coordinale of line [x,y,z]. | |
| * @param p0 Second coordinale of line [x,y,z]. | |
| * @param fractionFromP0 Point we are looking for coordinates of | |
| * @param p Coordinate of point we are looking for | |
| */ | |
| public static double[] computePointOnLine (double[] p0, double[] p1, | |
| double fractionFromP0) | |
| { | |
| double[] p = new double[3]; | |
| p[0] = p0[0] + fractionFromP0 * (p1[0] - p0[0]); | |
| p[1] = p0[1] + fractionFromP0 * (p1[1] - p0[1]); | |
| p[2] = p0[2] + fractionFromP0 * (p1[2] - p0[2]); | |
| return p; | |
| } | |
| /** | |
| * Find the point on the line defined by x0,y0,x1,y1 a given fraction | |
| * from x0,y0. 2D version of method above.. | |
| * | |
| * @param x0, y0 First point defining the line | |
| * @param x1, y1 Second point defining the line | |
| * @param fractionFrom0 Distance from (x0,y0) | |
| * @return x, y Coordinate of point we are looking for | |
| */ | |
| public static double[] computePointOnLine (double x0, double y0, | |
| double x1, double y1, | |
| double fractionFrom0) | |
| { | |
| double[] p0 = {x0, y0, 0.0}; | |
| double[] p1 = {x1, y1, 0.0}; | |
| double[] p = Geometry.computePointOnLine (p0, p1, fractionFrom0); | |
| double[] r = {p[0], p[1]}; | |
| return r; | |
| } | |
| /** | |
| * Extend a given line segment to a specified length. | |
| * | |
| * @param p0, p1 Line segment to extend [x,y,z]. | |
| * @param toLength Length of new line segment. | |
| * @param anchor Specifies the fixed point during extension. | |
| * If anchor is 0.0, p0 is fixed and p1 is adjusted. | |
| * If anchor is 1.0, p1 is fixed and p0 is adjusted. | |
| * If anchor is 0.5, the line is adjusted equally in each | |
| * direction and so on. | |
| */ | |
| public static void extendLine (double[] p0, double[] p1, double toLength, | |
| double anchor) | |
| { | |
| double[] p = Geometry.computePointOnLine (p0, p1, anchor); | |
| double length0 = toLength * anchor; | |
| double length1 = toLength * (1.0 - anchor); | |
| Geometry.extendLine (p, p0, length0); | |
| Geometry.extendLine (p, p1, length1); | |
| } | |
| /** | |
| * Extend a given line segment to a given length and holding the first | |
| * point of the line as fixed. | |
| * | |
| * @param p0, p1 Line segment to extend. p0 is fixed during extension | |
| * @param length Length of new line segment. | |
| */ | |
| public static void extendLine (double[] p0, double[] p1, double toLength) | |
| { | |
| double oldLength = Geometry.length (p0, p1); | |
| double lengthFraction = oldLength != 0.0 ? toLength / oldLength : 0.0; | |
| p1[0] = p0[0] + (p1[0] - p0[0]) * lengthFraction; | |
| p1[1] = p0[1] + (p1[1] - p0[1]) * lengthFraction; | |
| p1[2] = p0[2] + (p1[2] - p0[2]) * lengthFraction; | |
| } | |
| /** | |
| * 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 = Geometry.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 Geometry.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 += Geometry.length (x[i], y[i], x[i+1], y[i+1]); | |
| // Add last leg if this is a polygon | |
| if (isClosed && nPoints > 1) | |
| length += Geometry.length (x[nPoints-1], y[nPoints-1], x[0], y[0]); | |
| return length; | |
| } | |
| /** | |
| * Return distance bwetween the line defined by (x0,y0) and (x1,y1) | |
| * and the point (x,y). | |
| * Ref: http://astronomy.swin.edu.au/pbourke/geometry/pointline/ | |
| * The 3D case should be similar. | |
| * | |
| * @param x0, y0 First point of line. | |
| * @param x1, y1 Second point of line. | |
| * @param x, y, Point to consider. | |
| * @return Distance from x,y down to the (extended) line defined | |
| * by x0, y0, x1, y1. | |
| */ | |
| public static double distance (int x0, int y0, int x1, int y1, | |
| int x, int y) | |
| { | |
| // If x0,y0,x1,y1 is same point, we return distance to that point | |
| double length = Geometry.length (x0, y0, x1, y1); | |
| if (length == 0.0) | |
| return Geometry.length (x0, y0, x, y); | |
| // If u is [0,1] then (xp,yp) is on the line segment (x0,y0),(x1,y1). | |
| double u = ((x - x0) * (x1 - x0) + (y - y0) * (y1 - y0)) / | |
| (length * length); | |
| // This is the intersection point of the normal. | |
| // TODO: Might consider returning this as well. | |
| double xp = x0 + u * (x1 - x0); | |
| double yp = y0 + u * (y1 - y0); | |
| length = Geometry.length (xp, yp, x, y); | |
| return length; | |
| } | |
| /** | |
| * Find the angle between twree 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 = Geometry.createVector (p0, p1); | |
| double[] v1 = Geometry.createVector (p0, p2); | |
| double dotProduct = Geometry.computeDotProduct (v0, v1); | |
| double length1 = Geometry.length (v0); | |
| double length2 = Geometry.length (v1); | |
| double denominator = length1 * length2; | |
| double product = denominator != 0.0 ? dotProduct / denominator : 0.0; | |
| double angle = Math.acos (product); | |
| return angle; | |
| } | |
| /** | |
| * 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]; | |
| } | |
| /** | |
| * Compute the cross product (a vector) of two vectors. | |
| * | |
| * @param v0, v1 Vectors to compute cross product between [x,y,z]. | |
| * @param crossProduct Cross product of specified vectors [x,y,z]. | |
| */ | |
| public static double[] computeCrossProduct (double[] v0, double[] v1) | |
| { | |
| double crossProduct[] = new double[3]; | |
| crossProduct[0] = v0[1] * v1[2] - v0[2] * v1[1]; | |
| crossProduct[1] = v0[2] * v1[0] - v0[0] * v1[2]; | |
| crossProduct[2] = v0[0] * v1[1] - v0[1] * v1[0]; | |
| return crossProduct; | |
| } | |
| /** | |
| * 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; | |
| } | |
| /** | |
| * Check if two points are on the same side of a given line. | |
| * Algorithm from Sedgewick page 350. | |
| * | |
| * @param x0, y0, x1, y1 The line. | |
| * @param px0, py0 First point. | |
| * @param px1, py1 Second point. | |
| * @return <0 if points on opposite sides. | |
| * =0 if one of the points is exactly on the line | |
| * >0 if points on same side. | |
| */ | |
| private static int sameSide (double x0, double y0, double x1, double y1, | |
| double px0, double py0, double px1, double py1) | |
| { | |
| int sameSide = 0; | |
| double dx = x1 - x0; | |
| double dy = y1 - y0; | |
| double dx1 = px0 - x0; | |
| double dy1 = py0 - y0; | |
| double dx2 = px1 - x1; | |
| double dy2 = py1 - y1; | |
| // Cross product of the vector from the endpoint of the line to the point | |
| double c1 = dx * dy1 - dy * dx1; | |
| double c2 = dx * dy2 - dy * dx2; | |
| if (c1 != 0 && c2 != 0) | |
| sameSide = c1 < 0 != c2 < 0 ? -1 : 1; | |
| else if (dx == 0 && dx1 == 0 && dx2 == 0) | |
| sameSide = !isBetween (y0, y1, py0) && !isBetween (y0, y1, py1) ? 1 : 0; | |
| else if (dy == 0 && dy1 == 0 && dy2 == 0) | |
| sameSide = !isBetween (x0, x1, px0) && !isBetween (x0, x1, px1) ? 1 : 0; | |
| return sameSide; | |
| } | |
| /** | |
| * Check if two points are on the same side of a given line. Integer domain. | |
| * | |
| * @param x0, y0, x1, y1 The line. | |
| * @param px0, py0 First point. | |
| * @param px1, py1 Second point. | |
| * @return <0 if points on opposite sides. | |
| * =0 if one of the points is exactly on the line | |
| * >0 if points on same side. | |
| */ | |
| private static int sameSide (int x0, int y0, int x1, int y1, | |
| int px0, int py0, int px1, int py1) | |
| { | |
| return sameSide ((double) x0, (double) y0, (double) x1, (double) y1, | |
| (double) px0, (double) py0, (double) px1, (double) py1); | |
| } | |
| /** | |
| * Check if two line segments intersects. Integer domain. | |
| * | |
| * @param x0, y0, x1, y1 End points of first line to check. | |
| * @param x2, yy, x3, y3 End points of second line to check. | |
| * @return True if the two lines intersects. | |
| */ | |
| public static boolean isLineIntersectingLine (int x0, int y0, int x1, int y1, | |
| int x2, int y2, int x3, int y3) | |
| { | |
| int s1 = Geometry.sameSide (x0, y0, x1, y1, x2, y2, x3, y3); | |
| int s2 = Geometry.sameSide (x2, y2, x3, y3, x0, y0, x1, y1); | |
| return s1 <= 0 && s2 <= 0; | |
| } | |
| /** | |
| * Check if a specified line intersects a specified rectangle. | |
| * Integer domain. | |
| * | |
| * @param lx0, ly0 1st end point of line | |
| * @param ly1, ly1 2nd end point of line | |
| * @param x0, y0, x1, y1 Upper left and lower right corner of rectangle | |
| * (inclusive). | |
| * @return True if the line intersects the rectangle, | |
| * false otherwise. | |
| */ | |
| public static boolean isLineIntersectingRectangle (int lx0, int ly0, | |
| int lx1, int ly1, | |
| int x0, int y0, | |
| int x1, int y1) | |
| { | |
| // Is one of the line endpoints inside the rectangle | |
| if (Geometry.isPointInsideRectangle (x0, y0, x1, y1, lx0, ly0) || | |
| Geometry.isPointInsideRectangle (x0, y0, x1, y1, lx1, ly1)) | |
| return true; | |
| // If it intersects it goes through. Need to check three sides only. | |
| // Check against top rectangle line | |
| if (Geometry.isLineIntersectingLine (lx0, ly0, lx1, ly1, | |
| x0, y0, x1, y0)) | |
| return true; | |
| // Check against left rectangle line | |
| if (Geometry.isLineIntersectingLine (lx0, ly0, lx1, ly1, | |
| x0, y0, x0, y1)) | |
| return true; | |
| // Check against bottom rectangle line | |
| if (Geometry.isLineIntersectingLine (lx0, ly0, lx1, ly1, | |
| x0, y1, x1, y1)) | |
| return true; | |
| return false; | |
| } | |
| /** | |
| * Check if a specified polyline intersects a specified rectangle. | |
| * Integer domain. | |
| * | |
| * @param x, y Polyline to check. | |
| * @param x0, y0, x1, y1 Upper left and lower left corner of rectangle | |
| * (inclusive). | |
| * @return True if the polyline intersects the rectangle, | |
| * false otherwise. | |
| */ | |
| public static boolean isPolylineIntersectingRectangle (int[] x, int[] y, | |
| int x0, int y0, | |
| int x1, int y1) | |
| { | |
| if (x.length == 0) return false; | |
| if (Geometry.isPointInsideRectangle (x[0], y[0], x0, y0, x1, y1)) | |
| return true; | |
| else if (x.length == 1) | |
| return false; | |
| for (int i = 1; i < x.length; i++) { | |
| if (x[i-1] != x[i] || y[i-1] != y[i]) | |
| if (Geometry.isLineIntersectingRectangle (x[i-1], y[i-1], | |
| x[i], y[i], | |
| x0, y0, x1, y1)) | |
| return true; | |
| } | |
| return false; | |
| } | |
| /** | |
| * Check if a specified polygon intersects a specified rectangle. | |
| * Integer domain. | |
| * | |
| * @param x X coordinates of polyline. | |
| * @param y Y coordinates of polyline. | |
| * @param x0 X of upper left corner of rectangle. | |
| * @param y0 Y of upper left corner of rectangle. | |
| * @param x1 X of lower right corner of rectangle. | |
| * @param y1 Y of lower right corner of rectangle. | |
| * @return True if the polyline intersects the rectangle, false otherwise. | |
| */ | |
| public static boolean isPolygonIntersectingRectangle (int[] x, int[] y, | |
| int x0, int y0, | |
| int x1, int y1) | |
| { | |
| int n = x.length; | |
| if (n == 0) | |
| return false; | |
| if (n == 1) | |
| return Geometry.isPointInsideRectangle (x0, y0, x1, y1, x[0], y[0]); | |
| // | |
| // If the polyline constituting the polygon intersects the rectangle | |
| // the polygon does too. | |
| // | |
| if (Geometry.isPolylineIntersectingRectangle (x, y, x0, y0, x1, y1)) | |
| return true; | |
| // Check last leg as well | |
| if (Geometry.isLineIntersectingRectangle (x[n-2], y[n-2], x[n-1], y[n-1], | |
| x0, y0, x1, y1)) | |
| return true; | |
| // | |
| // The rectangle and polygon are now completely including each other | |
| // or separate. | |
| // | |
| if (Geometry.isPointInsidePolygon (x, y, x0, y0) || | |
| Geometry.isPointInsideRectangle (x0, y0, x1, y1, x[0], y[0])) | |
| return true; | |
| // Separate | |
| return false; | |
| } | |
| /** | |
| * Compute the area of the specfied polygon. | |
| * | |
| * @param x X coordinates of polygon. | |
| * @param y Y coordinates of polygon. | |
| * @return Area of specified polygon. | |
| */ | |
| public static double computePolygonArea (double[] x, double[] y) | |
| { | |
| int n = x.length; | |
| double area = 0.0; | |
| for (int i = 0; i < n - 1; i++) | |
| area += (x[i] * y[i+1]) - (x[i+1] * y[i]); | |
| area += (x[n-1] * y[0]) - (x[0] * y[n-1]); | |
| area *= 0.5; | |
| return area; | |
| } | |
| /** | |
| * Compute the area of the specfied polygon. | |
| * | |
| * @param xy Geometry of polygon [x,y,...] | |
| * @return Area of specified polygon. | |
| */ | |
| public static double computePolygonArea (double[] xy) | |
| { | |
| int n = xy.length; | |
| double area = 0.0; | |
| for (int i = 0; i < n - 2; i += 2) | |
| area += (xy[i] * xy[i+3]) - (xy[i+2] * xy[i+1]); | |
| area += (xy[xy.length-2] * xy[1]) - (xy[0] * xy[xy.length-1]); | |
| area *= 0.5; | |
| return area; | |
| } | |
| /** | |
| * Compute centorid (center of gravity) of specified polygon. | |
| * | |
| * @param x X coordinates of polygon. | |
| * @param y Y coordinates of polygon. | |
| * @return Centroid [x,y] of specified polygon. | |
| */ | |
| public static double[] computePolygonCentroid (double[] x, double[] y) | |
| { | |
| double cx = 0.0; | |
| double cy = 0.0; | |
| int n = x.length; | |
| for (int i = 0; i < n - 1; i++) { | |
| double a = x[i] * y[i+1] - x[i+1] * y[i]; | |
| cx += (x[i] + x[i+1]) * a; | |
| cy += (y[i] + y[i+1]) * a; | |
| } | |
| double a = x[n-1] * y[0] - x[0] * y[n-1]; | |
| cx += (x[n-1] + x[0]) * a; | |
| cy += (y[n-1] + y[0]) * a; | |
| double area = Geometry.computePolygonArea (x, y); | |
| cx /= 6 * area; | |
| cy /= 6 * area; | |
| return new double[] {cx, cy}; | |
| } | |
| /** | |
| * Find the 3D extent of a polyline. | |
| * | |
| * @param x X coordinates of polyline. | |
| * @param y Y coordinates of polyline. | |
| * @param z Z coordinates of polyline. | |
| * May be null if this is a 2D case. | |
| * @param xExtent Will upon return contain [xMin,xMax]. | |
| * @param yExtent Will upon return contain [xMin,xMax]. | |
| * @param zExtent Will upon return contain [xMin,xMax]. Unused (may be | |
| * set to null) if z is null. | |
| */ | |
| public static void findPolygonExtent (double[] x, double[] y, double[] z, | |
| double[] xExtent, | |
| double[] yExtent, | |
| double[] zExtent) | |
| { | |
| double xMin = +Double.MAX_VALUE; | |
| double xMax = -Double.MAX_VALUE; | |
| double yMin = +Double.MAX_VALUE; | |
| double yMax = -Double.MAX_VALUE; | |
| double zMin = +Double.MAX_VALUE; | |
| double zMax = -Double.MAX_VALUE; | |
| for (int i = 0; i < x.length; i++) { | |
| if (x[i] < xMin) xMin = x[i]; | |
| if (x[i] > xMax) xMax = x[i]; | |
| if (y[i] < yMin) yMin = y[i]; | |
| if (y[i] > yMax) yMax = y[i]; | |
| if (z != null) { | |
| if (z[i] < zMin) zMin = z[i]; | |
| if (z[i] > zMax) zMax = z[i]; | |
| } | |
| } | |
| xExtent[0] = xMin; | |
| xExtent[1] = xMax; | |
| yExtent[0] = yMin; | |
| yExtent[1] = yMax; | |
| if (z != null) { | |
| zExtent[0] = zMin; | |
| zExtent[1] = zMax; | |
| } | |
| } | |
| /** | |
| * Find the extent of a polygon. | |
| * | |
| * @param x X coordinates of polygon. | |
| * @param y Y coordinates of polygon. | |
| * @param xExtent Will upon return contain [xMin, xMax] | |
| * @param yExtent Will upon return contain [yMin, yMax] | |
| */ | |
| public static void findPolygonExtent (int[] x, int[] y, | |
| int[] xExtent, // xMin, xMax | |
| int[] yExtent) // yMin, yMax | |
| { | |
| int xMin = + Integer.MAX_VALUE; | |
| int xMax = - Integer.MAX_VALUE; | |
| int yMin = + Integer.MAX_VALUE; | |
| int yMax = - Integer.MAX_VALUE; | |
| for (int i = 0; i < x.length; i++) { | |
| if (x[i] < xMin) xMin = x[i]; | |
| if (x[i] > xMax) xMax = x[i]; | |
| if (y[i] < yMin) yMin = y[i]; | |
| if (y[i] > yMax) yMax = y[i]; | |
| } | |
| xExtent[0] = xMin; | |
| xExtent[1] = xMax; | |
| yExtent[0] = yMin; | |
| yExtent[1] = yMax; | |
| } | |
| /** | |
| * Compute the intersection between two line segments, or two lines | |
| * of infinite length. | |
| * | |
| * @param x0 X coordinate first end point first line segment. | |
| * @param y0 Y coordinate first end point first line segment. | |
| * @param x1 X coordinate second end point first line segment. | |
| * @param y1 Y coordinate second end point first line segment. | |
| * @param x2 X coordinate first end point second line segment. | |
| * @param y2 Y coordinate first end point second line segment. | |
| * @param x3 X coordinate second end point second line segment. | |
| * @param y3 Y coordinate second end point second line segment. | |
| * @param intersection[2] Preallocated by caller to double[2] | |
| * @return -1 if lines are parallel (x,y unset), | |
| * -2 if lines are parallel and overlapping (x, y center) | |
| * 0 if intesrection outside segments (x,y set) | |
| * +1 if segments intersect (x,y set) | |
| */ | |
| public static int findLineSegmentIntersection (double x0, double y0, | |
| double x1, double y1, | |
| double x2, double y2, | |
| double x3, double y3, | |
| double[] intersection) | |
| { | |
| // TODO: Make limit depend on input domain | |
| final double LIMIT = 1e-5; | |
| final double INFINITY = 1e10; | |
| double x, y; | |
| // | |
| // Convert the lines to the form y = ax + b | |
| // | |
| // Slope of the two lines | |
| double a0 = Geometry.equals (x0, x1, LIMIT) ? | |
| INFINITY : (y0 - y1) / (x0 - x1); | |
| double a1 = Geometry.equals (x2, x3, LIMIT) ? | |
| INFINITY : (y2 - y3) / (x2 - x3); | |
| double b0 = y0 - a0 * x0; | |
| double b1 = y2 - a1 * x2; | |
| // Check if lines are parallel | |
| if (Geometry.equals (a0, a1)) { | |
| if (!Geometry.equals (b0, b1)) | |
| return -1; // Parallell non-overlapping | |
| else { | |
| if (Geometry.equals (x0, x1)) { | |
| if (Math.min (y0, y1) < Math.max (y2, y3) || | |
| Math.max (y0, y1) > Math.min (y2, y3)) { | |
| double twoMiddle = y0 + y1 + y2 + y3 - | |
| Geometry.min (y0, y1, y2, y3) - | |
| Geometry.max (y0, y1, y2, y3); | |
| y = (twoMiddle) / 2.0; | |
| x = (y - b0) / a0; | |
| } | |
| else return -1; // Parallell non-overlapping | |
| } | |
| else { | |
| if (Math.min (x0, x1) < Math.max (x2, x3) || | |
| Math.max (x0, x1) > Math.min (x2, x3)) { | |
| double twoMiddle = x0 + x1 + x2 + x3 - | |
| Geometry.min (x0, x1, x2, x3) - | |
| Geometry.max (x0, x1, x2, x3); | |
| x = (twoMiddle) / 2.0; | |
| y = a0 * x + b0; | |
| } | |
| else return -1; | |
| } | |
| intersection[0] = x; | |
| intersection[1] = y; | |
| return -2; | |
| } | |
| } | |
| // Find correct intersection point | |
| if (Geometry.equals (a0, INFINITY)) { | |
| x = x0; | |
| y = a1 * x + b1; | |
| } | |
| else if (Geometry.equals (a1, INFINITY)) { | |
| x = x2; | |
| y = a0 * x + b0; | |
| } | |
| else { | |
| x = - (b0 - b1) / (a0 - a1); | |
| y = a0 * x + b0; | |
| } | |
| intersection[0] = x; | |
| intersection[1] = y; | |
| // Then check if intersection is within line segments | |
| double distanceFrom1; | |
| if (Geometry.equals (x0, x1)) { | |
| if (y0 < y1) | |
| distanceFrom1 = y < y0 ? Geometry.length (x, y, x0, y0) : | |
| y > y1 ? Geometry.length (x, y, x1, y1) : 0.0; | |
| else | |
| distanceFrom1 = y < y1 ? Geometry.length (x, y, x1, y1) : | |
| y > y0 ? Geometry.length (x, y, x0, y0) : 0.0; | |
| } | |
| else { | |
| if (x0 < x1) | |
| distanceFrom1 = x < x0 ? Geometry.length (x, y, x0, y0) : | |
| x > x1 ? Geometry.length (x, y, x1, y1) : 0.0; | |
| else | |
| distanceFrom1 = x < x1 ? Geometry.length (x, y, x1, y1) : | |
| x > x0 ? Geometry.length (x, y, x0, y0) : 0.0; | |
| } | |
| double distanceFrom2; | |
| if (Geometry.equals (x2, x3)) { | |
| if (y2 < y3) | |
| distanceFrom2 = y < y2 ? Geometry.length (x, y, x2, y2) : | |
| y > y3 ? Geometry.length (x, y, x3, y3) : 0.0; | |
| else | |
| distanceFrom2 = y < y3 ? Geometry.length (x, y, x3, y3) : | |
| y > y2 ? Geometry.length (x, y, x2, y2) : 0.0; | |
| } | |
| else { | |
| if (x2 < x3) | |
| distanceFrom2 = x < x2 ? Geometry.length (x, y, x2, y2) : | |
| x > x3 ? Geometry.length (x, y, x3, y3) : 0.0; | |
| else | |
| distanceFrom2 = x < x3 ? Geometry.length (x, y, x3, y3) : | |
| x > x2 ? Geometry.length (x, y, x2, y2) : 0.0; | |
| } | |
| return Geometry.equals (distanceFrom1, 0.0) && | |
| Geometry.equals (distanceFrom2, 0.0) ? 1 : 0; | |
| } | |
| /** | |
| * Find the intersections between a polygon and a straight line. | |
| * | |
| * NOTE: This method is only guaranteed to work if the polygon | |
| * is first preprocessed so that "unneccesary" vertices are removed | |
| * (i.e vertices on the straight line between its neighbours). | |
| * | |
| * @param x X coordinates of polygon. | |
| * @param y Y coordinates of polygon. | |
| * @param x0 X first end point of line. | |
| * @param x0 Y first end point of line. | |
| * @param x0 X second end point of line. | |
| * @param x0 Y second end point of line. | |
| * @return Intersections [x,y,x,y...]. | |
| */ | |
| public static double[] findLinePolygonIntersections (double[] x, double[] y, | |
| double x0, double y0, | |
| double x1, double y1) | |
| { | |
| double x2, y2, x3, y3; | |
| double xi, yi; | |
| int nPoints = x.length; | |
| int nIntersections = 0; | |
| double[] intersections = new double[24]; // Result vector x,y,x,y,... | |
| double[] intersection = new double[2]; // Any given intersection x,y | |
| for (int i = 0; i < nPoints; i++) { | |
| int next = i == nPoints - 1 ? 0 : i + 1; | |
| // The line segment of the polyline to check | |
| x2 = x[i]; | |
| y2 = y[i]; | |
| x3 = x[next]; | |
| y3 = y[next]; | |
| boolean isIntersecting = false; | |
| // Ignore segments of zero length | |
| if (Geometry.equals (x2, x3) && Geometry.equals (y2, y3)) | |
| continue; | |
| int type = Geometry.findLineSegmentIntersection (x0, y0, x1, y1, | |
| x2, y2, x3, y3, | |
| intersection); | |
| if (type == -2) { // Overlapping | |
| int p1 = i == 0 ? nPoints - 1 : i - 1; | |
| int p2 = next == nPoints - 1 ? 0 : next + 1; | |
| int side = Geometry.sameSide (x0, y0, x1, y1, | |
| x[p1], y[p1], x[p2], y[p2]); | |
| if (side < 0) | |
| isIntersecting = true; | |
| } | |
| else if (type == 1) | |
| isIntersecting = true; | |
| // Add the intersection point | |
| if (isIntersecting) { | |
| // Reallocate if necessary | |
| if (nIntersections << 1 == intersections.length) { | |
| double[] newArray = new double[nIntersections << 2]; | |
| System.arraycopy (intersections, 0, newArray, 0, | |
| intersections.length); | |
| intersections = newArray; | |
| } | |
| // Then add | |
| intersections[nIntersections << 1 + 0] = intersection[0]; | |
| intersections[nIntersections << 1 + 1] = intersection[1]; | |
| nIntersections++; | |
| } | |
| } | |
| if (nIntersections == 0) return null; | |
| // Reallocate result so array match number of intersections | |
| double[] finalArray = new double[nIntersections << 2]; | |
| System.arraycopy (intersections, 0, finalArray, 0, finalArray.length); | |
| return finalArray; | |
| } | |
| /** | |
| * 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 = Geometry.computeAngle (p0, p1, p2); | |
| // dx / dy | |
| double dx = Geometry.length (x0, y0, x[1], y[1]); | |
| double dy = Geometry.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 (!Geometry.equals (axisAngle, Math.PI/2.0, 0.1) && | |
| !Geometry.equals (axisAngle, Math.PI, 0.1) && | |
| !Geometry.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; | |
| 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; | |
| } | |
| /** | |
| * Create geometry for a circle. Integer domain. | |
| * | |
| * @param x0 X center of circle. | |
| * @param y0 Y center of circle. | |
| * @param radius Radius of circle. | |
| * @return Geometry of circle [x,y,...] | |
| */ | |
| public static int[] createCircle (int x0, int y0, int radius) | |
| { | |
| return createEllipse (x0, y0, radius, radius); | |
| } | |
| /** | |
| * Create geometry for a circle. Floating point domain. | |
| * | |
| * @param x0 X center of circle. | |
| * @param y0 Y center of circle. | |
| * @param radius Radius of circle. | |
| * @return Geometry of circle [x,y,...] | |
| */ | |
| public static double[] createCircle (double x0, double y0, double radius) | |
| { | |
| return createEllipse (x0, y0, radius, radius); | |
| } | |
| /** | |
| * Create the geometry of a sector of an ellipse. | |
| * | |
| * @param x0 X coordinate of center of ellipse. | |
| * @param y0 Y coordinate of center of ellipse. | |
| * @param dx X radius of ellipse. | |
| * @param dy Y radius of ellipse. | |
| * @param angle0 First angle of sector (in radians). | |
| * @param angle1 Second angle of sector (in radians). | |
| * @return Geometry of secor [x,y,...] | |
| */ | |
| public static int[] createSector (int x0, int y0, int dx, int dy, | |
| double angle0, double angle1) | |
| { | |
| // Determine a sensible number of points for arc | |
| double angleSpan = Math.abs (angle1 - angle0); | |
| double arcDistance = Math.max (dx, dy) * angleSpan; | |
| int nPoints = (int) Math.round (arcDistance / 15); | |
| double angleStep = angleSpan / (nPoints - 1); | |
| int[] xy = new int[nPoints*2 + 4]; | |
| int index = 0; | |
| for (int i = 0; i < nPoints; i++) { | |
| double angle = angle0 + angleStep * i; | |
| double x = dx * Math.cos (angle); | |
| double y = dy * Math.sin (angle); | |
| xy[index+0] = x0 + (int) Math.round (x); | |
| xy[index+1] = y0 - (int) Math.round (y); | |
| index += 2; | |
| } | |
| // Start and end geometry at center of ellipse to make it a closed polygon | |
| xy[nPoints*2 + 0] = x0; | |
| xy[nPoints*2 + 1] = y0; | |
| xy[nPoints*2 + 2] = xy[0]; | |
| xy[nPoints*2 + 3] = xy[1]; | |
| return xy; | |
| } | |
| /** | |
| * Create the geometry of a sector of a circle. | |
| * | |
| * @param x0 X coordinate of center of ellipse. | |
| * @param y0 Y coordinate of center of ellipse. | |
| * @param dx X radius of ellipse. | |
| * @param dy Y radius of ellipse. | |
| * @param angle0 First angle of sector (in radians). | |
| * @param angle1 Second angle of sector (in radians). | |
| * @return Geometry of secor [x,y,...] | |
| */ | |
| public static int[] createSector (int x0, int y0, int radius, | |
| double angle0, double angle1) | |
| { | |
| return createSector (x0, y0, radius, radius, angle0, angle1); | |
| } | |
| /** | |
| * Create the geometry of an arrow. The arrow is positioned at the | |
| * end (last point) of the specified polyline, as follows: | |
| * | |
| * 0,4--, | |
| * \ --, | |
| * \ --, | |
| * \ --, | |
| * \ --, | |
| * -------------------------3-----------1 | |
| * / --' | |
| * / --' | |
| * / --' | |
| * / --' | |
| * 2--' | |
| * | |
| * @param x X coordinates of polyline of where arrow is positioned | |
| * in the end. Must contain at least two points. | |
| * @param y Y coordinates of polyline of where arrow is positioned | |
| * in the end. | |
| * @param length Length along the main axis from point 1 to the | |
| * projection of point 0. | |
| * @param angle Angle between the main axis and the line 1,0 | |
| * (and 1,2) in radians. | |
| * @param inset Specification of point 3 [0.0-1.0], 1.0 will put | |
| * point 3 at distance length from 1, 0.0 will put it | |
| * at point 1. | |
| * @return Array of the five coordinates [x,y,...]. | |
| */ | |
| public static int[] createArrow (int[] x, int[] y, | |
| double length, double angle, double inset) | |
| { | |
| int[] arrow = new int[10]; | |
| int x0 = x[x.length - 1]; | |
| int y0 = y[y.length - 1]; | |
| arrow[2] = x0; | |
| arrow[3] = y0; | |
| // Find position of interior of the arrow along the polyline | |
| int[] pos1 = new int[2]; | |
| Geometry.findPolygonPosition (x, y, length, pos1); | |
| // Angles | |
| double dx = x0 - pos1[0]; | |
| double dy = y0 - pos1[1]; | |
| // Polyline angle | |
| double v = dx == 0.0 ? Math.PI / 2.0 : Math.atan (Math.abs (dy / dx)); | |
| v = dx > 0.0 && dy <= 0.0 ? Math.PI + v : | |
| dx > 0.0 && dy >= 0.0 ? Math.PI - v : | |
| dx <= 0.0 && dy < 0.0 ? -v : | |
| dx <= 0.0 && dy > 0.0 ? +v : 0.0; | |
| double v0 = v + angle; | |
| double v1 = v - angle; | |
| double edgeLength = length / Math.cos (angle); | |
| arrow[0] = x0 + (int) Math.round (edgeLength * Math.cos (v0)); | |
| arrow[1] = y0 - (int) Math.round (edgeLength * Math.sin (v0)); | |
| arrow[4] = x0 + (int) Math.round (edgeLength * Math.cos (v1)); | |
| arrow[5] = y0 - (int) Math.round (edgeLength * Math.sin (v1)); | |
| double c1 = inset * length; | |
| arrow[6] = x0 + (int) Math.round (c1 * Math.cos (v)); | |
| arrow[7] = y0 - (int) Math.round (c1 * Math.sin (v)); | |
| // Close polygon | |
| arrow[8] = arrow[0]; | |
| arrow[9] = arrow[1]; | |
| return arrow; | |
| } | |
| /** | |
| * Create geometry for an arrow along the specified line and with | |
| * tip at x1,y1. See general method above. | |
| * | |
| * @param x0 X first end point of line. | |
| * @param y0 Y first end point of line. | |
| * @param x1 X second end point of line. | |
| * @param y1 Y second end point of line. | |
| * @param length Length along the main axis from point 1 to the | |
| * projection of point 0. | |
| * @param angle Angle between the main axis and the line 1,0 | |
| * (and 1.2) | |
| * @param inset Specification of point 3 [0.0-1.0], 1.0 will put | |
| * point 3 at distance length from 1, 0.0 will put it | |
| * at point 1. | |
| * @return Array of the four coordinates [x,y,...]. | |
| */ | |
| public static int[] createArrow (int x0, int y0, int x1, int y1, | |
| double length, double angle, double inset) | |
| { | |
| int[] x = {x0, x1}; | |
| int[] y = {y0, y1}; | |
| return createArrow (x, y, length, angle, inset); | |
| } | |
| /** | |
| * Create geometry for a rectangle. Returns a closed polygon; first | |
| * and last points matches. Integer domain. | |
| * | |
| * @param x0 X corner of rectangle. | |
| * @param y0 Y corner of rectangle. | |
| * @param width Width (may be negative to indicate leftwards direction) | |
| * @param height Height (may be negative to indicaten upwards direction) | |
| */ | |
| public static int[] createRectangle (int x0, int y0, int width, int height) | |
| { | |
| return new int[] {x0, y0, | |
| x0 + (width - 1), y0, | |
| x0 + (width - 1), y0 + (height - 1), | |
| x0, y0 + (height - 1), | |
| x0, y0}; | |
| } | |
| /** | |
| * Create geometry for a rectangle. Returns a closed polygon; first | |
| * and last points matches. Floating point domain. | |
| * | |
| * @param x0 X corner of rectangle. | |
| * @param y0 Y corner of rectangle. | |
| * @param width Width (may be negative to indicate leftwards direction) | |
| * @param height Height (may be negative to indicaten upwards direction) | |
| */ | |
| public static double[] createRectangle (double x0, double y0, | |
| double width, double height) | |
| { | |
| return new double[] {x0, y0, | |
| x0 + width, y0, | |
| x0 + width, y0 + height, | |
| x0, y0 + height, | |
| x0, y0}; | |
| } | |
| /** | |
| * Create geometry of a star. Integer domain. | |
| * | |
| * @param x0 X center of star. | |
| * @param y0 Y center of star. | |
| * @param innerRadius Inner radis of arms. | |
| * @param outerRadius Outer radius of arms. | |
| * @param nArms Number of arms. | |
| * @return Geometry of star [x,y,x,y,...]. | |
| */ | |
| public static int[] createStar (int x0, int y0, | |
| int innerRadius, int outerRadius, | |
| int nArms) | |
| { | |
| int nPoints = nArms * 2 + 1; | |
| int[] xy = new int[nPoints * 2]; | |
| double angleStep = 2.0 * Math.PI / nArms / 2.0; | |
| for (int i = 0; i < nArms * 2; i++) { | |
| double angle = i * angleStep; | |
| double radius = (i % 2) == 0 ? innerRadius : outerRadius; | |
| double x = x0 + radius * Math.cos (angle); | |
| double y = y0 + radius * Math.sin (angle); | |
| xy[i*2 + 0] = (int) Math.round (x); | |
| xy[i*2 + 1] = (int) Math.round (y); | |
| } | |
| // Close polygon | |
| xy[nPoints*2 - 2] = xy[0]; | |
| xy[nPoints*2 - 1] = xy[1]; | |
| return xy; | |
| } | |
| /** | |
| * Create geometry of a star. Floating point domain. | |
| * | |
| * @param x0 X center of star. | |
| * @param y0 Y center of star. | |
| * @param innerRadius Inner radis of arms. | |
| * @param outerRadius Outer radius of arms. | |
| * @param nArms Number of arms. | |
| * @return Geometry of star [x,y,x,y,...]. | |
| */ | |
| public static double[] createStar (double x0, double y0, | |
| double innerRadius, double outerRadius, | |
| int nArms) | |
| { | |
| int nPoints = nArms * 2 + 1; | |
| double[] xy = new double[nPoints * 2]; | |
| double angleStep = 2.0 * Math.PI / nArms / 2.0; | |
| for (int i = 0; i < nArms * 2; i++) { | |
| double angle = i * angleStep; | |
| double radius = (i % 2) == 0 ? innerRadius : outerRadius; | |
| xy[i*2 + 0] = x0 + radius * Math.cos (angle); | |
| xy[i*2 + 1] = y0 + radius * Math.sin (angle); | |
| } | |
| // Close polygon | |
| xy[nPoints*2 - 2] = xy[0]; | |
| xy[nPoints*2 - 1] = xy[1]; | |
| return xy; | |
| } | |
| /** | |
| * Return the x,y position at distance "length" into the given polyline. | |
| * | |
| * @param x X coordinates of polyline | |
| * @param y Y coordinates of polyline | |
| * @param length Requested position | |
| * @param position Preallocated to int[2] | |
| * @return True if point is within polyline, false otherwise | |
| */ | |
| public static boolean findPolygonPosition (int[] x, int[] y, | |
| double length, | |
| int[] position) | |
| { | |
| if (length < 0) return false; | |
| double accumulatedLength = 0.0; | |
| for (int i = 1; i < x.length; i++) { | |
| double legLength = Geometry.length (x[i-1], y[i-1], x[i], y[i]); | |
| if (legLength + accumulatedLength >= length) { | |
| double part = length - accumulatedLength; | |
| double fraction = part / legLength; | |
| position[0] = (int) Math.round (x[i-1] + fraction * (x[i] - x[i-1])); | |
| position[1] = (int) Math.round (y[i-1] + fraction * (y[i] - y[i-1])); | |
| return true; | |
| } | |
| accumulatedLength += legLength; | |
| } | |
| // Length is longer than polyline | |
| return false; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment