1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17/**
18 * @author Denis M. Kishenko
19 * @version $Revision$
20 */
21
22package java.awt;
23
24import java.awt.Point;
25import java.awt.Rectangle;
26import java.awt.Shape;
27import java.awt.geom.AffineTransform;
28import java.awt.geom.PathIterator;
29import java.awt.geom.Point2D;
30import java.awt.geom.Rectangle2D;
31import java.io.Serializable;
32import java.util.NoSuchElementException;
33
34import org.apache.harmony.awt.gl.*;
35import org.apache.harmony.awt.internal.nls.Messages;
36
37/**
38 * The Polygon class defines an closed area specified by n vertices and n edges.
39 * The coordinates of the vertices are specified by x, y arrays. The edges are
40 * the line segments from the point (x[i], y[i]) to the point (x[i+1], y[i+1]),
41 * for -1 < i < (n-1) plus the line segment from the point (x[n-1], y[n-1]) to
42 * the point (x[0], y[0]) point. The Polygon is empty if the number of vertices
43 * is zero.
44 *
45 * @since Android 1.0
46 */
47public class Polygon implements Shape, Serializable {
48
49    /**
50     * The Constant serialVersionUID.
51     */
52    private static final long serialVersionUID = -6460061437900069969L;
53
54    /**
55     * The points buffer capacity.
56     */
57    private static final int BUFFER_CAPACITY = 4;
58
59    /**
60     * The number of Polygon vertices.
61     */
62    public int npoints;
63
64    /**
65     * The array of X coordinates of the vertices.
66     */
67    public int[] xpoints;
68
69    /**
70     * The array of Y coordinates of the vertices.
71     */
72    public int[] ypoints;
73
74    /**
75     * The smallest Rectangle that completely contains this Polygon.
76     */
77    protected Rectangle bounds;
78
79    /*
80     * Polygon path iterator
81     */
82    /**
83     * The internal Class Iterator.
84     */
85    class Iterator implements PathIterator {
86
87        /**
88         * The source Polygon object.
89         */
90        public Polygon p;
91
92        /**
93         * The path iterator transformation.
94         */
95        public AffineTransform t;
96
97        /**
98         * The current segment index.
99         */
100        public int index;
101
102        /**
103         * Constructs a new Polygon.Iterator for the given polygon and
104         * transformation
105         *
106         * @param at
107         *            the AffineTransform object to apply rectangle path.
108         * @param p
109         *            the p.
110         */
111        public Iterator(AffineTransform at, Polygon p) {
112            this.p = p;
113            this.t = at;
114            if (p.npoints == 0) {
115                index = 1;
116            }
117        }
118
119        public int getWindingRule() {
120            return WIND_EVEN_ODD;
121        }
122
123        public boolean isDone() {
124            return index > p.npoints;
125        }
126
127        public void next() {
128            index++;
129        }
130
131        public int currentSegment(double[] coords) {
132            if (isDone()) {
133                // awt.110=Iterator out of bounds
134                throw new NoSuchElementException(Messages.getString("awt.110")); //$NON-NLS-1$
135            }
136            if (index == p.npoints) {
137                return SEG_CLOSE;
138            }
139            coords[0] = p.xpoints[index];
140            coords[1] = p.ypoints[index];
141            if (t != null) {
142                t.transform(coords, 0, coords, 0, 1);
143            }
144            return index == 0 ? SEG_MOVETO : SEG_LINETO;
145        }
146
147        public int currentSegment(float[] coords) {
148            if (isDone()) {
149                // awt.110=Iterator out of bounds
150                throw new NoSuchElementException(Messages.getString("awt.110")); //$NON-NLS-1$
151            }
152            if (index == p.npoints) {
153                return SEG_CLOSE;
154            }
155            coords[0] = p.xpoints[index];
156            coords[1] = p.ypoints[index];
157            if (t != null) {
158                t.transform(coords, 0, coords, 0, 1);
159            }
160            return index == 0 ? SEG_MOVETO : SEG_LINETO;
161        }
162    }
163
164    /**
165     * Instantiates a new empty polygon.
166     */
167    public Polygon() {
168        xpoints = new int[BUFFER_CAPACITY];
169        ypoints = new int[BUFFER_CAPACITY];
170    }
171
172    /**
173     * Instantiates a new polygon with the specified number of vertices, and the
174     * given arrays of x, y vertex coordinates. The length of each coordinate
175     * array may not be less than the specified number of vertices but may be
176     * greater. Only the first n elements are used from each coordinate array.
177     *
178     * @param xpoints
179     *            the array of X vertex coordinates.
180     * @param ypoints
181     *            the array of Y vertex coordinates.
182     * @param npoints
183     *            the number vertices of the polygon.
184     * @throws IndexOutOfBoundsException
185     *             if the length of xpoints or ypoints is less than n.
186     * @throws NegativeArraySizeException
187     *             if n is negative.
188     */
189    public Polygon(int[] xpoints, int[] ypoints, int npoints) {
190        if (npoints > xpoints.length || npoints > ypoints.length) {
191            // awt.111=Parameter npoints is greater than array length
192            throw new IndexOutOfBoundsException(Messages.getString("awt.111")); //$NON-NLS-1$
193        }
194        if (npoints < 0) {
195            // awt.112=Negative number of points
196            throw new NegativeArraySizeException(Messages.getString("awt.112")); //$NON-NLS-1$
197        }
198        this.npoints = npoints;
199        this.xpoints = new int[npoints];
200        this.ypoints = new int[npoints];
201        System.arraycopy(xpoints, 0, this.xpoints, 0, npoints);
202        System.arraycopy(ypoints, 0, this.ypoints, 0, npoints);
203    }
204
205    /**
206     * Resets the current Polygon to an empty Polygon. More precisely, the
207     * number of Polygon vertices is set to zero, but x, y coordinates arrays
208     * are not affected.
209     */
210    public void reset() {
211        npoints = 0;
212        bounds = null;
213    }
214
215    /**
216     * Invalidates the data that depends on the vertex coordinates. This method
217     * should be called after direct manipulations of the x, y vertex
218     * coordinates arrays to avoid unpredictable results of methods which rely
219     * on the bounding box.
220     */
221    public void invalidate() {
222        bounds = null;
223    }
224
225    /**
226     * Adds the point to the Polygon and updates the bounding box accordingly.
227     *
228     * @param px
229     *            the X coordinate of the added vertex.
230     * @param py
231     *            the Y coordinate of the added vertex.
232     */
233    public void addPoint(int px, int py) {
234        if (npoints == xpoints.length) {
235            int[] tmp;
236
237            tmp = new int[xpoints.length + BUFFER_CAPACITY];
238            System.arraycopy(xpoints, 0, tmp, 0, xpoints.length);
239            xpoints = tmp;
240
241            tmp = new int[ypoints.length + BUFFER_CAPACITY];
242            System.arraycopy(ypoints, 0, tmp, 0, ypoints.length);
243            ypoints = tmp;
244        }
245
246        xpoints[npoints] = px;
247        ypoints[npoints] = py;
248        npoints++;
249
250        if (bounds != null) {
251            bounds.setFrameFromDiagonal(Math.min(bounds.getMinX(), px), Math.min(bounds.getMinY(),
252                    py), Math.max(bounds.getMaxX(), px), Math.max(bounds.getMaxY(), py));
253        }
254    }
255
256    /**
257     * Gets the bounding rectangle of the Polygon. The bounding rectangle is the
258     * smallest rectangle which contains the Polygon.
259     *
260     * @return the bounding rectangle of the Polygon.
261     * @see java.awt.Shape#getBounds()
262     */
263    public Rectangle getBounds() {
264        if (bounds != null) {
265            return bounds;
266        }
267        if (npoints == 0) {
268            return new Rectangle();
269        }
270
271        int bx1 = xpoints[0];
272        int by1 = ypoints[0];
273        int bx2 = bx1;
274        int by2 = by1;
275
276        for (int i = 1; i < npoints; i++) {
277            int x = xpoints[i];
278            int y = ypoints[i];
279            if (x < bx1) {
280                bx1 = x;
281            } else if (x > bx2) {
282                bx2 = x;
283            }
284            if (y < by1) {
285                by1 = y;
286            } else if (y > by2) {
287                by2 = y;
288            }
289        }
290
291        return bounds = new Rectangle(bx1, by1, bx2 - bx1, by2 - by1);
292    }
293
294    /**
295     * Gets the bounding rectangle of the Polygon. The bounding rectangle is the
296     * smallest rectangle which contains the Polygon.
297     *
298     * @return the bounding rectangle of the Polygon.
299     * @deprecated Use getBounds() method.
300     */
301    @Deprecated
302    public Rectangle getBoundingBox() {
303        return getBounds();
304    }
305
306    /**
307     * Gets the Rectangle2D which represents Polygon bounds. The bounding
308     * rectangle is the smallest rectangle which contains the Polygon.
309     *
310     * @return the bounding rectangle of the Polygon.
311     * @see java.awt.Shape#getBounds2D()
312     */
313    public Rectangle2D getBounds2D() {
314        return getBounds().getBounds2D();
315    }
316
317    /**
318     * Translates all vertices of Polygon the specified distances along X, Y
319     * axis.
320     *
321     * @param mx
322     *            the distance to translate horizontally.
323     * @param my
324     *            the distance to translate vertically.
325     */
326    public void translate(int mx, int my) {
327        for (int i = 0; i < npoints; i++) {
328            xpoints[i] += mx;
329            ypoints[i] += my;
330        }
331        if (bounds != null) {
332            bounds.translate(mx, my);
333        }
334    }
335
336    /**
337     * Checks whether or not the point given by the coordinates x, y lies inside
338     * the Polygon.
339     *
340     * @param x
341     *            the X coordinate of the point to check.
342     * @param y
343     *            the Y coordinate of the point to check.
344     * @return true, if the specified point lies inside the Polygon, false
345     *         otherwise.
346     * @deprecated Use contains(int, int) method.
347     */
348    @Deprecated
349    public boolean inside(int x, int y) {
350        return contains((double)x, (double)y);
351    }
352
353    /**
354     * Checks whether or not the point given by the coordinates x, y lies inside
355     * the Polygon.
356     *
357     * @param x
358     *            the X coordinate of the point to check.
359     * @param y
360     *            the Y coordinate of the point to check.
361     * @return true, if the specified point lies inside the Polygon, false
362     *         otherwise.
363     */
364    public boolean contains(int x, int y) {
365        return contains((double)x, (double)y);
366    }
367
368    /**
369     * Checks whether or not the point with specified double coordinates lies
370     * inside the Polygon.
371     *
372     * @param x
373     *            the X coordinate of the point to check.
374     * @param y
375     *            the Y coordinate of the point to check.
376     * @return true, if the point given by the double coordinates lies inside
377     *         the Polygon, false otherwise.
378     * @see java.awt.Shape#contains(double, double)
379     */
380    public boolean contains(double x, double y) {
381        return Crossing.isInsideEvenOdd(Crossing.crossShape(this, x, y));
382    }
383
384    /**
385     * Checks whether or not the rectangle determined by the parameters [x, y,
386     * width, height] lies inside the Polygon.
387     *
388     * @param x
389     *            the X coordinate of the rectangles's left upper corner as a
390     *            double.
391     * @param y
392     *            the Y coordinate of the rectangles's left upper corner as a
393     *            double.
394     * @param width
395     *            the width of rectangle as a double.
396     * @param height
397     *            the height of rectangle as a double.
398     * @return true, if the specified rectangle lies inside the Polygon, false
399     *         otherwise.
400     * @see java.awt.Shape#contains(double, double, double, double)
401     */
402    public boolean contains(double x, double y, double width, double height) {
403        int cross = Crossing.intersectShape(this, x, y, width, height);
404        return cross != Crossing.CROSSING && Crossing.isInsideEvenOdd(cross);
405    }
406
407    /**
408     * Checks whether or not the rectangle determined by the parameters [x, y,
409     * width, height] intersects the interior of the Polygon.
410     *
411     * @param x
412     *            the X coordinate of the rectangles's left upper corner as a
413     *            double.
414     * @param y
415     *            the Y coordinate of the rectangles's left upper corner as a
416     *            double.
417     * @param width
418     *            the width of rectangle as a double.
419     * @param height
420     *            the height of rectangle as a double.
421     * @return true, if the specified rectangle intersects the interior of the
422     *         Polygon, false otherwise.
423     * @see java.awt.Shape#intersects(double, double, double, double)
424     */
425    public boolean intersects(double x, double y, double width, double height) {
426        int cross = Crossing.intersectShape(this, x, y, width, height);
427        return cross == Crossing.CROSSING || Crossing.isInsideEvenOdd(cross);
428    }
429
430    /**
431     * Checks whether or not the specified rectangle lies inside the Polygon.
432     *
433     * @param rect
434     *            the Rectangle2D object.
435     * @return true, if the specified rectangle lies inside the Polygon, false
436     *         otherwise.
437     * @see java.awt.Shape#contains(java.awt.geom.Rectangle2D)
438     */
439    public boolean contains(Rectangle2D rect) {
440        return contains(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
441    }
442
443    /**
444     * Checks whether or not the specified Point lies inside the Polygon.
445     *
446     * @param point
447     *            the Point object.
448     * @return true, if the specified Point lies inside the Polygon, false
449     *         otherwise.
450     */
451    public boolean contains(Point point) {
452        return contains(point.getX(), point.getY());
453    }
454
455    /**
456     * Checks whether or not the specified Point2D lies inside the Polygon.
457     *
458     * @param point
459     *            the Point2D object.
460     * @return true, if the specified Point2D lies inside the Polygon, false
461     *         otherwise.
462     * @see java.awt.Shape#contains(java.awt.geom.Point2D)
463     */
464    public boolean contains(Point2D point) {
465        return contains(point.getX(), point.getY());
466    }
467
468    /**
469     * Checks whether or not the interior of rectangle specified by the
470     * Rectangle2D object intersects the interior of the Polygon.
471     *
472     * @param rect
473     *            the Rectangle2D object.
474     * @return true, if the Rectangle2D intersects the interior of the Polygon,
475     *         false otherwise.
476     * @see java.awt.Shape#intersects(java.awt.geom.Rectangle2D)
477     */
478    public boolean intersects(Rectangle2D rect) {
479        return intersects(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
480    }
481
482    /**
483     * Gets the PathIterator object which gives the coordinates of the polygon,
484     * transformed according to the specified AffineTransform.
485     *
486     * @param t
487     *            the specified AffineTransform object or null.
488     * @return PathIterator object for the Polygon.
489     * @see java.awt.Shape#getPathIterator(java.awt.geom.AffineTransform)
490     */
491    public PathIterator getPathIterator(AffineTransform t) {
492        return new Iterator(t, this);
493    }
494
495    /**
496     * Gets the PathIterator object which gives the coordinates of the polygon,
497     * transformed according to the specified AffineTransform. The flatness
498     * parameter is ignored.
499     *
500     * @param t
501     *            the specified AffineTransform object or null.
502     * @param flatness
503     *            the maximum number of the control points for a given curve
504     *            which varies from colinear before a subdivided curve is
505     *            replaced by a straight line connecting the endpoints. This
506     *            parameter is ignored for the Polygon class.
507     * @return PathIterator object for the Polygon.
508     * @see java.awt.Shape#getPathIterator(java.awt.geom.AffineTransform,
509     *      double)
510     */
511    public PathIterator getPathIterator(AffineTransform t, double flatness) {
512        return new Iterator(t, this);
513    }
514
515}
516