Collection of geometry utility methods : Geometry « 2D Graphics GUI « Java






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;
  }
}


           
         
    
    
    
  








Related examples in the same category

1.Unions Rectangle2D
2.Interpolates points given in the 2D plane
3.Returns distance between two sets of coords
4.Returns distance between 3D set of coords
5.Returns closest point on segment to point
6.Calculate Angle From
7.Returns distance to segment
8.Hexagon demo
9.Implements an Vector in 3D space.
10.Implementation of the 4 dimensional vector.
11.Quaternion
12.Circle shape
13.Geometry Utilities
14.This is a Polygon that allows the user to flip and swap the points along it's axis.
15.Fast trigonometric operationsFast trigonometric operations
16.A class to represent a latitude and longitude
17.An undirected graph that keeps track of connected components (groups).
18.Generates n logarithmically-spaced points between d1 and d2 using the provided base.
19.Returns a dimension where width and height are inside the bounds of the maxWidth and maxHeight parameters