Mega Code Archive

 
Categories / Java / 2D Graphics GUI
 

Collection of geometry utility methods

/*  * (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:jacob.dreyer@geosoft.no">Jacob Dreyer</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;   } }