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.geom;
23
24import java.util.NoSuchElementException;
25
26import org.apache.harmony.awt.internal.nls.Messages;
27
28/**
29 * The Class Ellipse2D describes an ellipse defined by a rectangular area in
30 * which it is inscribed.
31 *
32 * @since Android 1.0
33 */
34public abstract class Ellipse2D extends RectangularShape {
35
36    /**
37     * The Class Float is the subclass of Ellipse2D that has all of its data
38     * values stored with float-level precision.
39     *
40     * @since Android 1.0
41     */
42    public static class Float extends Ellipse2D {
43
44        /**
45         * The x coordinate of the upper left corner of the ellipse's bounding
46         * rectangle.
47         */
48        public float x;
49
50        /**
51         * The y coordinate of the upper left corner of the ellipse's bounding
52         * rectangle.
53         */
54        public float y;
55
56        /**
57         * The width of the ellipse's bounding rectangle.
58         */
59        public float width;
60
61        /**
62         * The height of the ellipse's bounding rectangle.
63         */
64        public float height;
65
66        /**
67         * Instantiates a new float-valued Ellipse2D.
68         */
69        public Float() {
70        }
71
72        /**
73         * Instantiates a new float-valued Ellipse2D with the specified data.
74         *
75         * @param x
76         *            the x coordinate of the upper left corner of the ellipse's
77         *            bounding rectangle.
78         * @param y
79         *            the y coordinate of the upper left corner of the ellipse's
80         *            bounding rectangle.
81         * @param width
82         *            the width of the ellipse's bounding rectangle.
83         * @param height
84         *            the height of the ellipse's bounding rectangle.
85         */
86        public Float(float x, float y, float width, float height) {
87            setFrame(x, y, width, height);
88        }
89
90        @Override
91        public double getX() {
92            return x;
93        }
94
95        @Override
96        public double getY() {
97            return y;
98        }
99
100        @Override
101        public double getWidth() {
102            return width;
103        }
104
105        @Override
106        public double getHeight() {
107            return height;
108        }
109
110        @Override
111        public boolean isEmpty() {
112            return width <= 0.0 || height <= 0.0;
113        }
114
115        /**
116         * Sets the data of the ellipse's bounding rectangle.
117         *
118         * @param x
119         *            the x coordinate of the upper left corner of the ellipse's
120         *            bounding rectangle.
121         * @param y
122         *            the y coordinate of the upper left corner of the ellipse's
123         *            bounding rectangle.
124         * @param width
125         *            the width of the ellipse's bounding rectangle.
126         * @param height
127         *            the height of the ellipse's bounding rectangle.
128         */
129        public void setFrame(float x, float y, float width, float height) {
130            this.x = x;
131            this.y = y;
132            this.width = width;
133            this.height = height;
134        }
135
136        @Override
137        public void setFrame(double x, double y, double width, double height) {
138            this.x = (float)x;
139            this.y = (float)y;
140            this.width = (float)width;
141            this.height = (float)height;
142        }
143
144        public Rectangle2D getBounds2D() {
145            return new Rectangle2D.Float(x, y, width, height);
146        }
147    }
148
149    /**
150     * The Class Double is the subclass of Ellipse2D that has all of its data
151     * values stored with double-level precision.
152     *
153     * @since Android 1.0
154     */
155    public static class Double extends Ellipse2D {
156
157        /**
158         * The x coordinate of the upper left corner of the ellipse's bounding
159         * rectangle.
160         */
161        public double x;
162
163        /**
164         * The y coordinate of the upper left corner of the ellipse's bounding
165         * rectangle.
166         */
167        public double y;
168
169        /**
170         * The width of the ellipse's bounding rectangle.
171         */
172        public double width;
173
174        /**
175         * The height of the ellipse's bounding rectangle.
176         */
177        public double height;
178
179        /**
180         * Instantiates a new double-valued Ellipse2D.
181         */
182        public Double() {
183        }
184
185        /**
186         * Instantiates a new double-valued Ellipse2D with the specified data.
187         *
188         * @param x
189         *            the x coordinate of the upper left corner of the ellipse's
190         *            bounding rectangle.
191         * @param y
192         *            the y coordinate of the upper left corner of the ellipse's
193         *            bounding rectangle.
194         * @param width
195         *            the width of the ellipse's bounding rectangle.
196         * @param height
197         *            the height of the ellipse's bounding rectangle.
198         */
199        public Double(double x, double y, double width, double height) {
200            setFrame(x, y, width, height);
201        }
202
203        @Override
204        public double getX() {
205            return x;
206        }
207
208        @Override
209        public double getY() {
210            return y;
211        }
212
213        @Override
214        public double getWidth() {
215            return width;
216        }
217
218        @Override
219        public double getHeight() {
220            return height;
221        }
222
223        @Override
224        public boolean isEmpty() {
225            return width <= 0.0 || height <= 0.0;
226        }
227
228        @Override
229        public void setFrame(double x, double y, double width, double height) {
230            this.x = x;
231            this.y = y;
232            this.width = width;
233            this.height = height;
234        }
235
236        public Rectangle2D getBounds2D() {
237            return new Rectangle2D.Double(x, y, width, height);
238        }
239    }
240
241    /*
242     * Ellipse2D path iterator
243     */
244    /**
245     * The subclass of PathIterator to traverse an Ellipse2D.
246     */
247    class Iterator implements PathIterator {
248
249        /*
250         * Ellipse is subdivided into four quarters by x and y axis. Each part
251         * approximated by cubic Bezier curve. Arc in first quarter is started
252         * in (a, 0) and finished in (0, b) points. Control points for cubic
253         * curve wiil be (a, 0), (a, m), (n, b) and (0, b) where n and m are
254         * calculated based on requirement Bezier curve in point 0.5 should lay
255         * on the arc.
256         */
257
258        /**
259         * The coefficient to calculate control points of Bezier curves.
260         */
261        final double u = 2.0 / 3.0 * (Math.sqrt(2.0) - 1.0);
262
263        /**
264         * The points coordinates calculation table.
265         */
266        final double points[][] = {
267                {
268                        1.0, 0.5 + u, 0.5 + u, 1.0, 0.5, 1.0
269                }, {
270                        0.5 - u, 1.0, 0.0, 0.5 + u, 0.0, 0.5
271                }, {
272                        0.0, 0.5 - u, 0.5 - u, 0.0, 0.5, 0.0
273                }, {
274                        0.5 + u, 0.0, 1.0, 0.5 - u, 1.0, 0.5
275                }
276        };
277
278        /**
279         * The x coordinate of left-upper corner of the ellipse bounds.
280         */
281        double x;
282
283        /**
284         * The y coordinate of left-upper corner of the ellipse bounds.
285         */
286        double y;
287
288        /**
289         * The width of the ellipse bounds.
290         */
291        double width;
292
293        /**
294         * The height of the ellipse bounds.
295         */
296        double height;
297
298        /**
299         * The path iterator transformation.
300         */
301        AffineTransform t;
302
303        /**
304         * The current segment index.
305         */
306        int index;
307
308        /**
309         * Constructs a new Ellipse2D.Iterator for given ellipse and
310         * transformation
311         *
312         * @param e
313         *            the source Ellipse2D object.
314         * @param t
315         *            the affine transformation object.
316         */
317        Iterator(Ellipse2D e, AffineTransform t) {
318            this.x = e.getX();
319            this.y = e.getY();
320            this.width = e.getWidth();
321            this.height = e.getHeight();
322            this.t = t;
323            if (width < 0.0 || height < 0.0) {
324                index = 6;
325            }
326        }
327
328        public int getWindingRule() {
329            return WIND_NON_ZERO;
330        }
331
332        public boolean isDone() {
333            return index > 5;
334        }
335
336        public void next() {
337            index++;
338        }
339
340        public int currentSegment(double[] coords) {
341            if (isDone()) {
342                // awt.4B=Iterator out of bounds
343                throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$
344            }
345            if (index == 5) {
346                return SEG_CLOSE;
347            }
348            int type;
349            int count;
350            if (index == 0) {
351                type = SEG_MOVETO;
352                count = 1;
353                double p[] = points[3];
354                coords[0] = x + p[4] * width;
355                coords[1] = y + p[5] * height;
356            } else {
357                type = SEG_CUBICTO;
358                count = 3;
359                double p[] = points[index - 1];
360                int j = 0;
361                for (int i = 0; i < 3; i++) {
362                    coords[j] = x + p[j++] * width;
363                    coords[j] = y + p[j++] * height;
364                }
365            }
366            if (t != null) {
367                t.transform(coords, 0, coords, 0, count);
368            }
369            return type;
370        }
371
372        public int currentSegment(float[] coords) {
373            if (isDone()) {
374                // awt.4B=Iterator out of bounds
375                throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$
376            }
377            if (index == 5) {
378                return SEG_CLOSE;
379            }
380            int type;
381            int count;
382            if (index == 0) {
383                type = SEG_MOVETO;
384                count = 1;
385                double p[] = points[3];
386                coords[0] = (float)(x + p[4] * width);
387                coords[1] = (float)(y + p[5] * height);
388            } else {
389                type = SEG_CUBICTO;
390                count = 3;
391                int j = 0;
392                double p[] = points[index - 1];
393                for (int i = 0; i < 3; i++) {
394                    coords[j] = (float)(x + p[j++] * width);
395                    coords[j] = (float)(y + p[j++] * height);
396                }
397            }
398            if (t != null) {
399                t.transform(coords, 0, coords, 0, count);
400            }
401            return type;
402        }
403
404    }
405
406    /**
407     * Instantiates a new Ellipse2D.
408     */
409    protected Ellipse2D() {
410    }
411
412    public boolean contains(double px, double py) {
413        if (isEmpty()) {
414            return false;
415        }
416
417        double a = (px - getX()) / getWidth() - 0.5;
418        double b = (py - getY()) / getHeight() - 0.5;
419
420        return a * a + b * b < 0.25;
421    }
422
423    public boolean intersects(double rx, double ry, double rw, double rh) {
424        if (isEmpty() || rw <= 0.0 || rh <= 0.0) {
425            return false;
426        }
427
428        double cx = getX() + getWidth() / 2.0;
429        double cy = getY() + getHeight() / 2.0;
430
431        double rx1 = rx;
432        double ry1 = ry;
433        double rx2 = rx + rw;
434        double ry2 = ry + rh;
435
436        double nx = cx < rx1 ? rx1 : (cx > rx2 ? rx2 : cx);
437        double ny = cy < ry1 ? ry1 : (cy > ry2 ? ry2 : cy);
438
439        return contains(nx, ny);
440    }
441
442    public boolean contains(double rx, double ry, double rw, double rh) {
443        if (isEmpty() || rw <= 0.0 || rh <= 0.0) {
444            return false;
445        }
446
447        double rx1 = rx;
448        double ry1 = ry;
449        double rx2 = rx + rw;
450        double ry2 = ry + rh;
451
452        return contains(rx1, ry1) && contains(rx2, ry1) && contains(rx2, ry2) && contains(rx1, ry2);
453    }
454
455    public PathIterator getPathIterator(AffineTransform at) {
456        return new Iterator(this, at);
457    }
458}
459