001    /*
002     * $Id: Star2D.java 3239 2009-02-01 20:35:49Z rah003 $
003     *
004     * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
005     *
006     * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
007     * Santa Clara, California 95054, U.S.A. All rights reserved.
008     *
009     * Copyright (c) 2006 Romain Guy <romain.guy@mac.com>
010     * All rights reserved.
011     *
012     * Redistribution and use in source and binary forms, with or without
013     * modification, are permitted provided that the following conditions
014     * are met:
015     * 1. Redistributions of source code must retain the above copyright
016     *    notice, this list of conditions and the following disclaimer.
017     * 2. Redistributions in binary form must reproduce the above copyright
018     *    notice, this list of conditions and the following disclaimer in the
019     *    documentation and/or other materials provided with the distribution.
020     * 3. The name of the author may not be used to endorse or promote products
021     *    derived from this software without specific prior written permission.
022     *
023     * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
024     * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
025     * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
026     * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
027     * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
028     * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
029     * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
030     * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
031     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
032     * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
033     */
034    
035    package org.jdesktop.swingx.geom;
036    
037    import java.awt.Rectangle;
038    import java.awt.Shape;
039    import java.awt.geom.AffineTransform;
040    import java.awt.geom.GeneralPath;
041    import java.awt.geom.PathIterator;
042    import java.awt.geom.Point2D;
043    import java.awt.geom.Rectangle2D;
044    
045    /**
046     * <p>This class provides a star shape. A star is defined by two radii and a
047     * number of branches. Each branch spans between the two radii. The inner
048     * radius is the distance between the center of the star and the origin of the
049     * branches. The outer radius is the distance between the center of the star
050     * and the tips of the branches.</p>
051     *
052     * @author Romain Guy <romain.guy@mac.com>
053     */
054    
055    public class Star2D implements Shape {
056        private Shape starShape;
057        private double x;
058        private double y;
059        private double innerRadius;
060        private double outerRadius;
061        private int branchesCount;
062    
063        /**
064         * <p>Creates a new star whose center is located at the specified
065         * <code>x</code> and <code>y</code> coordinates. The number of branches
066         * and their length can be specified.</p>
067         *
068         * @param x the location of the star center
069         * @param y the location of the star center
070         * @param innerRadius the distance between the center of the star and the
071         *   origin of the branches
072         * @param outerRadius the distance between the center of the star and the
073         *   tip of the branches
074         * @param branchesCount the number of branches in this star; must be &gt;= 3
075         * @throws IllegalArgumentException if <code>branchesCount<code> is < 3 or
076         *   if <code>innerRadius</code> is &gt;= <code>outerRadius</code>
077         */
078        public Star2D(double x, double y,
079                      double innerRadius, double outerRadius,
080                      int branchesCount) {
081            if (branchesCount < 3) {
082                throw new IllegalArgumentException("The number of branches must" +
083                                                   " be >= 3.");
084            } else if (innerRadius >= outerRadius) {
085                throw new IllegalArgumentException("The inner radius must be < " +
086                                                   "outer radius.");
087            }
088    
089            this.x = x;
090            this.y = y;
091            this.innerRadius = innerRadius;
092            this.outerRadius = outerRadius;
093            this.branchesCount = branchesCount;
094    
095            starShape = generateStar(x, y, innerRadius, outerRadius, branchesCount);
096        }
097    
098        private static Shape generateStar(double x, double y,
099                                          double innerRadius, double outerRadius,
100                                          int branchesCount) {
101            GeneralPath path = new GeneralPath();
102    
103            double outerAngleIncrement = 2 * Math.PI / branchesCount;
104    
105            double outerAngle = branchesCount % 2 == 0 ? 0.0 : -(Math.PI / 2.0);
106            double innerAngle = (outerAngleIncrement / 2.0) + outerAngle;
107    
108            float x1 = (float) (Math.cos(outerAngle) * outerRadius + x);
109            float y1 = (float) (Math.sin(outerAngle) * outerRadius + y);
110    
111            float x2 = (float) (Math.cos(innerAngle) * innerRadius + x);
112            float y2 = (float) (Math.sin(innerAngle) * innerRadius + y);
113    
114            path.moveTo(x1, y1);
115            path.lineTo(x2, y2);
116    
117            outerAngle += outerAngleIncrement;
118            innerAngle += outerAngleIncrement;
119    
120            for (int i = 1; i < branchesCount; i++) {
121                x1 = (float) (Math.cos(outerAngle) * outerRadius + x);
122                y1 = (float) (Math.sin(outerAngle) * outerRadius + y);
123    
124                path.lineTo(x1, y1);
125    
126                x2 = (float) (Math.cos(innerAngle) * innerRadius + x);
127                y2 = (float) (Math.sin(innerAngle) * innerRadius + y);
128    
129                path.lineTo(x2, y2);
130    
131                outerAngle += outerAngleIncrement;
132                innerAngle += outerAngleIncrement;
133            }
134    
135            path.closePath();
136            return path;
137        }
138    
139        /**
140         * <p>Sets the inner radius of the star, that is the distance between its
141         * center and the origin of the branches. The inner radius must always be
142         * lower than the outer radius.</p>
143         *
144         * @param innerRadius the distance between the center of the star and the
145         *   origin of the branches
146         * @throws IllegalArgumentException if the inner radius is &gt;= outer radius
147         */
148        public void setInnerRadius(double innerRadius) {
149            if (innerRadius >= outerRadius) {
150                throw new IllegalArgumentException("The inner radius must be <" +
151                                                   " outer radius.");
152            }
153    
154            this.innerRadius = innerRadius;
155            starShape = generateStar(getX(), getY(), innerRadius, getOuterRadius(),
156                                     getBranchesCount());
157        }
158    
159        /**
160         * <p>Sets location of the center of the star.</p>
161         *
162         * @param x the x location of the center of the star
163         */
164        public void setX(double x) {
165            this.x = x;
166            starShape = generateStar(x, getY(), getInnerRadius(), getOuterRadius(),
167                                     getBranchesCount());
168        }
169    
170        /**
171         * <p>Sets the location of the center of the star.</p>
172         *
173         * @param y the x location of the center of the star
174         */
175        public void setY(double y) {
176            this.y = y;
177            starShape = generateStar(getX(), y, getInnerRadius(), getOuterRadius(),
178                                     getBranchesCount());
179        }
180    
181        /**
182         * <p>Sets the outer radius of the star, that is the distance between its
183         * center and the tips of the branches. The outer radius must always be
184         * greater than the inner radius.</p>
185         *
186         * @param outerRadius the distance between the center of the star and the
187         *   tips of the branches
188         * @throws IllegalArgumentException if the inner radius is &gt;= outer radius
189         */
190        public void setOuterRadius(double outerRadius) {
191            if (innerRadius >= outerRadius) {
192                throw new IllegalArgumentException("The outer radius must be > " +
193                                                   "inner radius.");
194            }
195    
196            this.outerRadius = outerRadius;
197            starShape = generateStar(getX(), getY(), getInnerRadius(), outerRadius,
198                                     getBranchesCount());
199        }
200    
201        /**
202         * <p>Sets the number branches of the star. A star must always have at least
203         * 3 branches.</p>
204         *
205         * @param branchesCount the number of branches
206         * @throws IllegalArgumentException if <code>branchesCount</code> is &lt;=2
207         */
208        public void setBranchesCount(int branchesCount) {
209            if (branchesCount <= 2) {
210                throw new IllegalArgumentException("The number of branches must" +
211                                                   " be >= 3.");
212            }
213    
214            this.branchesCount = branchesCount;
215            starShape = generateStar(getX(), getY(), getInnerRadius(),
216                                     getOuterRadius(), branchesCount);
217        }
218    
219        /**
220         * <p>Returns the location of the center of star.</p>
221         *
222         * @return the x coordinate of the center of the star
223         */
224        public double getX() {
225            return x;
226        }
227    
228        /**
229         * <p>Returns the location of the center of star.</p>
230         *
231         * @return the y coordinate of the center of the star
232         */
233        public double getY() {
234            return y;
235        }
236    
237        /**
238         * <p>Returns the distance between the center of the star and the origin
239         * of the branches.</p>
240         *
241         * @return the inner radius of the star
242         */
243        public double getInnerRadius() {
244            return innerRadius;
245        }
246    
247        /**
248         * <p>Returns the distance between the center of the star and the tips
249         * of the branches.</p>
250         *
251         * @return the outer radius of the star
252         */
253        public double getOuterRadius() {
254            return outerRadius;
255        }
256    
257        /**
258         * <p>Returns the number of branches of the star.</p>
259         *
260         * @return the number of branches, always &gt;= 3
261         */
262        public int getBranchesCount() {
263            return branchesCount;
264        }
265    
266        /**
267         * {@inheritDoc}
268         */
269        public Rectangle getBounds() {
270            return starShape.getBounds();
271        }
272    
273        /**
274         * {@inheritDoc}
275         */
276        public Rectangle2D getBounds2D() {
277            return starShape.getBounds2D();
278        }
279    
280        /**
281         * {@inheritDoc}
282         */
283        public boolean contains(double x, double y) {
284            return starShape.contains(x, y);
285        }
286    
287        /**
288         * {@inheritDoc}
289         */
290        public boolean contains(Point2D p) {
291            return starShape.contains(p);
292        }
293    
294        /**
295         * {@inheritDoc}
296         */
297        public boolean intersects(double x, double y, double w, double h) {
298            return starShape.intersects(x, y, w, h);
299        }
300    
301        /**
302         * {@inheritDoc}
303         */
304        public boolean intersects(Rectangle2D r) {
305            return starShape.intersects(r);
306        }
307    
308        /**
309         * {@inheritDoc}
310         */
311        public boolean contains(double x, double y, double w, double h) {
312            return starShape.contains(x, y, w, h);
313        }
314    
315        /**
316         * {@inheritDoc}
317         */
318        public boolean contains(Rectangle2D r) {
319            return starShape.contains(r);
320        }
321    
322        /**
323         * {@inheritDoc}
324         */
325        public PathIterator getPathIterator(AffineTransform at) {
326            return starShape.getPathIterator(at);
327        }
328    
329        /**
330         * {@inheritDoc}
331         */
332        public PathIterator getPathIterator(AffineTransform at, double flatness) {
333            return starShape.getPathIterator(at, flatness);
334        }
335    }