home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / java / awt / Polygon.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  18.0 KB  |  541 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)Polygon.java    1.32 98/10/22
  3.  *
  4.  * Copyright 1995-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14. package java.awt;
  15.  
  16. import java.awt.geom.AffineTransform;
  17. import java.awt.geom.PathIterator;
  18. import java.awt.geom.Point2D;
  19. import java.awt.geom.Rectangle2D;
  20. import java.awt.geom.Area;
  21.  
  22. /**
  23.  * The <code>Polygon</code> class encapsulates a description of a 
  24.  * closed, two-dimensional region within a coordinate space. This 
  25.  * region is bounded by an arbitrary number of line segments, each of 
  26.  * which is one side of the polygon. Internally, a polygon
  27.  * comprises of a list of (<i>x</i>, <i>y</i>) 
  28.  * coordinate pairs, where each pair defines a <i>vertex</i> of the 
  29.  * polygon, and two successive pairs are the endpoints of a 
  30.  * line that is a side of the polygon. The first and final
  31.  * pairs of (<i>x</i>, <i>y</i>) points are joined by a line segment 
  32.  * that closes the polygon.
  33.  *
  34.  * @version     1.26, 07/24/98
  35.  * @author     Sami Shaio
  36.  * @author      Herb Jellinek
  37.  * @since       JDK1.0
  38.  */
  39. public class Polygon implements Shape, java.io.Serializable {
  40.  
  41.     /**
  42.      * The total number of points.
  43.      * This value can be NULL.
  44.      *
  45.      * @serial
  46.      * @see #addPoint(int, int)
  47.      */
  48.     public int npoints = 0;
  49.  
  50.     /**
  51.      * The array of <i>x</i> coordinates. 
  52.      *
  53.      * @serial
  54.      * @see #addPoint(int, int)
  55.      */
  56.     public int xpoints[] = new int[4];
  57.  
  58.     /**
  59.      * The array of <i>y</i> coordinates. 
  60.      *
  61.      * @serial
  62.      * @see #addPoint(int, int)
  63.      */
  64.     public int ypoints[] = new int[4];
  65.     
  66.     /**
  67.      * Bounds of the polygon.
  68.      * This value can be NULL.
  69.      * Please see the javadoc comments getBounds().
  70.      * 
  71.      * @serial
  72.      * @see #getBoundingBox()
  73.      * @see #getBounds()
  74.      */
  75.     protected Rectangle bounds = null;
  76.     
  77.     /* 
  78.      * JDK 1.1 serialVersionUID 
  79.      */
  80.     private static final long serialVersionUID = -6460061437900069969L;
  81.  
  82.     /**
  83.      * Creates an empty polygon.
  84.      */
  85.     public Polygon() {
  86.     }
  87.  
  88.     /**
  89.      * Constructs and initializes a <code>Polygon</code> from the specified 
  90.      * parameters. 
  91.      * @param xpoints an array of <i>x</i> coordinates
  92.      * @param ypoints an array of <i>y</i> coordinates
  93.      * @param npoints the total number of points in the    
  94.      *                <code>Polygon</code>
  95.      * @exception  NegativeArraySizeException if the value of
  96.      *                       <code>npoints</code> is negative.
  97.      */
  98.     public Polygon(int xpoints[], int ypoints[], int npoints) {
  99.     this.npoints = npoints;
  100.     this.xpoints = new int[npoints];
  101.     this.ypoints = new int[npoints];
  102.     System.arraycopy(xpoints, 0, this.xpoints, 0, npoints);
  103.     System.arraycopy(ypoints, 0, this.ypoints, 0, npoints);    
  104.     }
  105.     
  106.     /**
  107.      * Translates the vertices of the <code>Polygon</code> by 
  108.      * <code>deltaX</code> along the x axis and by 
  109.      * <code>deltaY</code> along the y axis.
  110.      * @param deltaX the amount to translate along the <i>x</i> axis
  111.      * @param deltaY the amount to translate along the <i>y</i> axis
  112.      * @since JDK1.1
  113.      */
  114.     public void translate(int deltaX, int deltaY) {
  115.     for (int i = 0; i < npoints; i++) {
  116.         xpoints[i] += deltaX;
  117.         ypoints[i] += deltaY;
  118.     }
  119.     if (bounds != null) {
  120.         bounds.translate(deltaX, deltaY);
  121.     }
  122.     }
  123.  
  124.     /*
  125.      * Calculates the bounding box of the points passed to the constructor.
  126.      * Sets <code>bounds</code> to the result.
  127.      * @param xpoints[] array of <i>x</i> coordinates
  128.      * @param ypoints[] array of <i>y</i> coordinates
  129.      * @param npoints the total number of points
  130.      */
  131.     void calculateBounds(int xpoints[], int ypoints[], int npoints) {
  132.     int boundsMinX = Integer.MAX_VALUE;
  133.     int boundsMinY = Integer.MAX_VALUE;
  134.     int boundsMaxX = Integer.MIN_VALUE;
  135.     int boundsMaxY = Integer.MIN_VALUE;
  136.     
  137.     for (int i = 0; i < npoints; i++) {
  138.         int x = xpoints[i];
  139.         boundsMinX = Math.min(boundsMinX, x);
  140.         boundsMaxX = Math.max(boundsMaxX, x);
  141.         int y = ypoints[i];
  142.         boundsMinY = Math.min(boundsMinY, y);
  143.         boundsMaxY = Math.max(boundsMaxY, y);
  144.     }
  145.     bounds = new Rectangle(boundsMinX, boundsMinY,
  146.                    boundsMaxX - boundsMinX,
  147.                    boundsMaxY - boundsMinY);
  148.     }
  149.  
  150.     /*
  151.      * Resizes the bounding box to accomodate the specified coordinates.
  152.      * @param x, y the specified coordinates
  153.      */
  154.     void updateBounds(int x, int y) {
  155.     if (x < bounds.x) {
  156.         bounds.width = bounds.width + (bounds.x - x);
  157.         bounds.x = x;
  158.     }
  159.     else {
  160.         bounds.width = Math.max(bounds.width, x - bounds.x);
  161.         // bounds.x = bounds.x;
  162.     }
  163.  
  164.     if (y < bounds.y) {
  165.         bounds.height = bounds.height + (bounds.y - y);
  166.         bounds.y = y;
  167.     }
  168.     else {
  169.         bounds.height = Math.max(bounds.height, y - bounds.y);
  170.         // bounds.y = bounds.y;
  171.     }
  172.     }    
  173.  
  174.     /**
  175.      * Appends the specified coordinates to this <code>Polygon</code>. 
  176.      * <p>
  177.      * If an operation that calculates the bounding box of this     
  178.      * <code>Polygon</code> has already been performed, such as  
  179.      * <code>getBounds</code> or <code>contains</code>, then this 
  180.      * method updates the bounding box. 
  181.      * @param       x, y   the specified coordinates
  182.      * @see         java.awt.Polygon#getBounds
  183.      * @see         java.awt.Polygon#contains
  184.      */
  185.     public void addPoint(int x, int y) {
  186.     if (npoints == xpoints.length) {
  187.         int tmp[];
  188.  
  189.         tmp = new int[npoints * 2];
  190.         System.arraycopy(xpoints, 0, tmp, 0, npoints);
  191.         xpoints = tmp;
  192.  
  193.         tmp = new int[npoints * 2];
  194.         System.arraycopy(ypoints, 0, tmp, 0, npoints);
  195.         ypoints = tmp;
  196.     }
  197.     xpoints[npoints] = x;
  198.     ypoints[npoints] = y;
  199.     npoints++;
  200.     if (bounds != null) {
  201.         updateBounds(x, y);
  202.     }
  203.     }
  204.  
  205.     /**
  206.      * Gets the bounding box of this <code>Polygon</code>. 
  207.      * The bounding box is the smallest {@link Rectangle} whose
  208.      * sides are parallel to the x and y axes of the 
  209.      * coordinate space, and can completely contain the <code>Polygon</code>.
  210.      * @return a <code>Rectangle</code> that defines the bounds of this 
  211.      * <code>Polygon</code>.
  212.      * @since       JDK1.1
  213.      */
  214.     public Rectangle getBounds() {
  215.     return getBoundingBox();
  216.     }
  217.  
  218.     /**
  219.      * @deprecated As of JDK version 1.1,
  220.      * replaced by <code>getBounds()</code>.
  221.      */
  222.     public Rectangle getBoundingBox() {
  223.     if (bounds == null) {
  224.         calculateBounds(xpoints, ypoints, npoints);
  225.     }
  226.     return bounds;
  227.     }
  228.  
  229.     /**
  230.      * Determines whether the specified {@link Point} is inside this 
  231.      * <code>Polygon</code>.  Uses an even-odd insideness rule (also known
  232.      * as an alternating rule).
  233.      * @param p the specified <code>Point</code> to be tested
  234.      * @return <code>true</code> if the <code>Polygon</code> contains the
  235.      *             <code>Point</code>; <code>false</code> otherwise.
  236.      */
  237.     public boolean contains(Point p) {
  238.     return contains(p.x, p.y);
  239.     }
  240.  
  241.     /**
  242.      * Determines whether the specified coordinates are contained in this 
  243.      * <code>Polygon</code>.   
  244.      * <p>
  245.      * (The <code>contains</code> method is based on code by 
  246.      * Hanpeter van Vliet [hvvliet@inter.nl.net].) 
  247.      * @param x, y  the specified coordinates to be tested
  248.      * @return  <code>true</code> if this <code>Polygon</code> contains
  249.      *             the specified coordinates, (<i>x</i>, <i>y</i>);  
  250.      *             <code>false</code> otherwise.
  251.      * @since      JDK1.1
  252.      */
  253.     public boolean contains(int x, int y) {
  254.     return contains((double) x, (double) y);
  255.     }
  256.  
  257.     /**
  258.      * @deprecated As of JDK version 1.1,
  259.      * replaced by <code>contains(int, int)</code>.
  260.      */
  261.     public boolean inside(int x, int y) {
  262.     return contains((double) x, (double) y);
  263.     }
  264.  
  265.     /**
  266.      * Returns the high precision bounding box of the {@link Shape}.
  267.      * @return a {@link Rectangle2D} that precisely
  268.      *        bounds the <code>Shape</code>.
  269.      */
  270.     public Rectangle2D getBounds2D() {
  271.     Rectangle r = getBounds();
  272.     return new Rectangle2D.Float(r.x, r.y, r.width, r.height);
  273.     }
  274.  
  275.     /**
  276.      * Tests if the specified coordinates are inside the boundary of the 
  277.      * <code>Shape</code>.
  278.      * @param x, y the specified coordinates
  279.      * @return <code>true</code> if the <code>Shape</code> contains the
  280.      * specified coordinates; <code>false</code> otherwise.
  281.      */
  282.     public boolean contains(double x, double y) {
  283.         if (getBoundingBox().contains(x, y)) {
  284.             int hits = 0;
  285.             int ySave = 0;
  286.  
  287.             // Find a vertex that is not on the halfline
  288.             int i = 0;
  289.             while (i < npoints && ypoints[i] == y) {
  290.                 i++;
  291.         }
  292.  
  293.             // Walk the edges of the polygon
  294.             for (int n = 0; n < npoints; n++) {
  295.                 int j = (i + 1) % npoints;
  296.  
  297.                 int dx = xpoints[j] - xpoints[i];
  298.                 int dy = ypoints[j] - ypoints[i];
  299.  
  300.                 // Ignore horizontal edges completely
  301.                 if (dy != 0) {
  302.                     // Check to see if the edge intersects
  303.                     // the horizontal halfline through (x, y)
  304.                     double rx = x - xpoints[i];
  305.                     double ry = y - ypoints[i];
  306.  
  307.                     // Deal with edges starting or ending on the halfline
  308.                     if (ypoints[j] == y && xpoints[j] >= x) {
  309.                         ySave = ypoints[i];
  310.             }
  311.                     if (ypoints[i] == y && xpoints[i] >= x) {
  312.                         if ((ySave > y) != (ypoints[j] > y)) {
  313.                 hits--;
  314.             }
  315.             }
  316.  
  317.                     // Tally intersections with halfline
  318.                     double s = ry / dy;
  319.                     if (s >= 0.0 && s <= 1.0 && (s * dx) >= rx) {
  320.                         hits++;
  321.             }
  322.                 }
  323.                 i = j;
  324.             }
  325.  
  326.             // Inside if number of intersections odd
  327.             return (hits % 2) != 0;
  328.         }
  329.         return false;
  330.     }
  331.  
  332.     /**
  333.      * Tests if a specified {@link Point2D} is inside the boundary of this 
  334.      * <code>Polygon</code>.
  335.      * @param p a specified <code>Point2D</code>
  336.      * @return <code>true</code> if this <code>Polygon</code> contains the 
  337.      *         specified <code>Point2D</code>; <code>false</code>
  338.      *          otherwise.
  339.      */
  340.     public boolean contains(Point2D p) {
  341.     return contains(p.getX(), p.getY());
  342.     }
  343.  
  344.     /**
  345.      * Tests if the interior of this <code>Polygon</code> intersects the 
  346.      * interior of a specified set of rectangular coordinates.
  347.      * @param x, y the coordinates of the specified rectangular
  348.      *            shape's top-left corner
  349.      * @param w the width of the specified rectangular shape
  350.      * @param h the height of the specified rectangular shape
  351.      * @return <code>true</code> if the interior of this 
  352.      *            <code>Polygon</code> and the interior of the
  353.      *            specified set of rectangular 
  354.      *             coordinates intersect each other;
  355.      *            <code>false</code> otherwise.
  356.      */
  357.     public boolean intersects(double x, double y, double w, double h) {
  358.     return new Area(this).intersects(x, y, w, h);
  359.     }
  360.  
  361.     /**
  362.      * Tests if the interior of this <code>Polygon</code> intersects the
  363.      * interior of a specified <code>Rectangle2D</code>.
  364.      * @param r a specified <code>Rectangle2D</code>
  365.      * @return <code>true</code> if this <code>Polygon</code> and the
  366.      *             interior of the specified <code>Rectangle2D</code>
  367.      *             intersect each other; <code>false</code>
  368.      *             otherwise.
  369.      */
  370.     public boolean intersects(Rectangle2D r) {
  371.     return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
  372.     }
  373.  
  374.     /**
  375.      * Tests if the interior of this <code>Polygon</code> entirely
  376.      * contains the specified set of rectangular coordinates.
  377.      * @param x, y the coordinate of the top-left corner of the
  378.      *             specified set of rectangular coordinates
  379.      * @param w the width of the set of rectangular coordinates
  380.      * @param h the height of the set of rectangular coordinates
  381.      * @return <code>true</code> if this <code>Polygon</code> entirely
  382.      *             contains the specified set of rectangular
  383.      *             coordinates; <code>false</code> otherwise.
  384.      */
  385.     public boolean contains(double x, double y, double w, double h) {
  386.     return new Area(this).contains(x, y, w, h);
  387.     }
  388.  
  389.     /**
  390.      * Tests if the interior of this <code>Polygon</code> entirely
  391.      * contains the specified <code>Rectangle2D</code>.
  392.      * @param r the specified <code>Rectangle2D</code>
  393.      * @return <code>true</code> if this <code>Polygon</code> entirely
  394.      *             contains the specified <code>Rectangle2D</code>;
  395.      *            <code>false</code> otherwise.
  396.      */
  397.     public boolean contains(Rectangle2D r) {
  398.     return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
  399.     }
  400.  
  401.     /**
  402.      * Returns an iterator object that iterates along the boundary of this 
  403.      * <code>Polygon</code> and provides access to the geometry
  404.      * of the outline of this <code>Polygon</code>.  An optional
  405.      * {@link AffineTransform} can be specified so that the coordinates 
  406.      * returned in the iteration are transformed accordingly.
  407.      * @param at an optional <code>AffineTransform</code> to be applied to the
  408.      *         coordinates as they are returned in the iteration, or 
  409.      *        <code>null</code> if untransformed coordinates are desired
  410.      * @return a {@link PathIterator} object that provides access to the
  411.      *        geometry of this <code>Polygon</code>.      
  412.      */
  413.     public PathIterator getPathIterator(AffineTransform at) {
  414.     return new PolygonPathIterator(this, at);
  415.     }
  416.  
  417.     /**
  418.      * Returns an iterator object that iterates along the boundary of
  419.      * the <code>Shape</code> and provides access to the geometry of the 
  420.      * outline of the <code>Shape</code>.  Only SEG_MOVETO, SEG_LINETO, and 
  421.      * SEG_CLOSE point types are returned by the iterator.
  422.      * Since polygons are already flat, the <code>flatness</code> parameter
  423.      * is ignored.  An optional <code>AffineTransform</code> can be specified 
  424.      * in which case the coordinates returned in the iteration are transformed
  425.      * accordingly.
  426.      * @param at an optional <code>AffineTransform</code> to be applied to the
  427.      *         coordinates as they are returned in the iteration, or 
  428.      *        <code>null</code> if untransformed coordinates are desired
  429.      * @param flatness the maximum amount that the control points
  430.      *         for a given curve can vary from colinear before a subdivided
  431.      *        curve is replaced by a straight line connecting the 
  432.      *         endpoints.  Since polygons are already flat the
  433.      *         <code>flatness</code> parameter is ignored.
  434.      * @return a <code>PathIterator</code> object that provides access to the
  435.      *         <code>Shape</code> object's geometry.
  436.      */
  437.     public PathIterator getPathIterator(AffineTransform at, double flatness) {
  438.     return getPathIterator(at);
  439.     }
  440.  
  441.     class PolygonPathIterator implements PathIterator {
  442.     Polygon poly;
  443.     AffineTransform transform;
  444.     int index;
  445.  
  446.     public PolygonPathIterator(Polygon pg, AffineTransform at) {
  447.         poly = pg;
  448.         transform = at;
  449.     }
  450.  
  451.     /**
  452.      * Returns the winding rule for determining the interior of the
  453.      * path.
  454.          * @return an integer representing the current winding rule.
  455.      * @see PathIterator#WIND_NON_ZERO
  456.      */
  457.     public int getWindingRule() {
  458.         return WIND_EVEN_ODD;
  459.     }
  460.  
  461.     /**
  462.      * Tests if there are more points to read.
  463.      * @return <code>true</code> if there are more points to read;
  464.          *          <code>false</code> otherwise.
  465.      */
  466.     public boolean isDone() {
  467.         return index > poly.npoints;
  468.     }
  469.  
  470.     /**
  471.      * Moves the iterator forwards, along the primary direction of 
  472.          * traversal, to the next segment of the path when there are
  473.      * more points in that direction.
  474.      */
  475.     public void next() {
  476.         index++;
  477.     }
  478.  
  479.     /**
  480.      * Returns the coordinates and type of the current path segment in
  481.      * the iteration.
  482.      * The return value is the path segment type:
  483.      * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE.
  484.      * A <code>float</code> array of length 2 must be passed in and
  485.          * can be used to store the coordinates of the point(s).
  486.      * Each point is stored as a pair of <code>float</code> x, y
  487.          * coordinates.  SEG_MOVETO and SEG_LINETO types return one
  488.          * point, and SEG_CLOSE does not return any points.
  489.          * @param coords a <code>float</code> array that specifies the
  490.          * coordinates of the point(s)
  491.          * @return an integer representing the type and coordinates of the 
  492.          *         current path segment.
  493.      * @see PathIterator#SEG_MOVETO
  494.      * @see PathIterator#SEG_LINETO
  495.      * @see PathIterator#SEG_CLOSE
  496.      */
  497.     public int currentSegment(float[] coords) {
  498.         if (index >= poly.npoints) {
  499.         return SEG_CLOSE;
  500.         }
  501.         coords[0] = poly.xpoints[index];
  502.         coords[1] = poly.ypoints[index];
  503.         if (transform != null) {
  504.         transform.transform(coords, 0, coords, 0, 1);
  505.         }
  506.         return (index == 0 ? SEG_MOVETO : SEG_LINETO);
  507.     }
  508.  
  509.     /**
  510.      * Returns the coordinates and type of the current path segment in
  511.      * the iteration.
  512.      * The return value is the path segment type:
  513.      * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE.
  514.      * A <code>double</code> array of length 2 must be passed in and
  515.          * can be used to store the coordinates of the point(s).
  516.      * Each point is stored as a pair of <code>double</code> x, y
  517.          * coordinates.
  518.      * SEG_MOVETO and SEG_LINETO types return one point,
  519.      * and SEG_CLOSE does not return any points.
  520.          * @param coords a <code>double</code> array that specifies the
  521.          * coordinates of the point(s)
  522.          * @return an integer representing the type and coordinates of the 
  523.          *         current path segment.
  524.      * @see PathIterator#SEG_MOVETO
  525.      * @see PathIterator#SEG_LINETO
  526.      * @see PathIterator#SEG_CLOSE
  527.      */
  528.     public int currentSegment(double[] coords) {
  529.         if (index >= poly.npoints) {
  530.         return SEG_CLOSE;
  531.         }
  532.         coords[0] = poly.xpoints[index];
  533.         coords[1] = poly.ypoints[index];
  534.         if (transform != null) {
  535.         transform.transform(coords, 0, coords, 0, 1);
  536.         }
  537.         return (index == 0 ? SEG_MOVETO : SEG_LINETO);
  538.     }
  539.     }
  540. }
  541.