001/*
002 * $Id: Star2D.java 3863 2010-10-26 02:53:32Z kschaefe $
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
035package org.jdesktop.swingx.geom;
036
037import java.awt.Rectangle;
038import java.awt.Shape;
039import java.awt.geom.AffineTransform;
040import java.awt.geom.GeneralPath;
041import java.awt.geom.PathIterator;
042import java.awt.geom.Point2D;
043import 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
055public 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    @Override
270    public Rectangle getBounds() {
271        return starShape.getBounds();
272    }
273
274    /**
275     * {@inheritDoc}
276     */
277    @Override
278    public Rectangle2D getBounds2D() {
279        return starShape.getBounds2D();
280    }
281
282    /**
283     * {@inheritDoc}
284     */
285    @Override
286    public boolean contains(double x, double y) {
287        return starShape.contains(x, y);
288    }
289
290    /**
291     * {@inheritDoc}
292     */
293    @Override
294    public boolean contains(Point2D p) {
295        return starShape.contains(p);
296    }
297
298    /**
299     * {@inheritDoc}
300     */
301    @Override
302    public boolean intersects(double x, double y, double w, double h) {
303        return starShape.intersects(x, y, w, h);
304    }
305
306    /**
307     * {@inheritDoc}
308     */
309    @Override
310    public boolean intersects(Rectangle2D r) {
311        return starShape.intersects(r);
312    }
313
314    /**
315     * {@inheritDoc}
316     */
317    @Override
318    public boolean contains(double x, double y, double w, double h) {
319        return starShape.contains(x, y, w, h);
320    }
321
322    /**
323     * {@inheritDoc}
324     */
325    @Override
326    public boolean contains(Rectangle2D r) {
327        return starShape.contains(r);
328    }
329
330    /**
331     * {@inheritDoc}
332     */
333    @Override
334    public PathIterator getPathIterator(AffineTransform at) {
335        return starShape.getPathIterator(at);
336    }
337
338    /**
339     * {@inheritDoc}
340     */
341    @Override
342    public PathIterator getPathIterator(AffineTransform at, double flatness) {
343        return starShape.getPathIterator(at, flatness);
344    }
345}