001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
006     *
007     * Project Info:  http://www.jfree.org/jfreechart/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it
010     * under the terms of the GNU Lesser General Public License as published by
011     * the Free Software Foundation; either version 2.1 of the License, or
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
022     * USA.
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025     * in the United States and other countries.]
026     *
027     * -----------------------------
028     * DefaultPolarItemRenderer.java
029     * -----------------------------
030     * (C) Copyright 2004-2008, by Solution Engineering, Inc. and
031     *     Contributors.
032     *
033     * Original Author:  Daniel Bridenbecker, Solution Engineering, Inc.;
034     * Contributor(s):   David Gilbert (for Object Refinery Limited);
035     *
036     * Changes
037     * -------
038     * 19-Jan-2004 : Version 1, contributed by DB with minor changes by DG (DG);
039     * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
040     *               getYValue() (DG);
041     * 04-Oct-2004 : Renamed BooleanUtils --> BooleanUtilities (DG);
042     * 20-Apr-2005 : Update for change to LegendItem class (DG);
043     * ------------- JFREECHART 1.0.x ---------------------------------------------
044     * 04-Aug-2006 : Implemented equals() and clone() (DG);
045     * 02-Feb-2007 : Removed author tags from all over JFreeChart sources (DG);
046     * 14-Mar-2007 : Fixed clone() method (DG);
047     * 04-May-2007 : Fixed lookup for series paint and stroke (DG);
048     * 18-May-2007 : Set dataset for LegendItem (DG);
049     *
050     */
051    
052    package org.jfree.chart.renderer;
053    
054    import java.awt.AlphaComposite;
055    import java.awt.Composite;
056    import java.awt.Graphics2D;
057    import java.awt.Paint;
058    import java.awt.Point;
059    import java.awt.Polygon;
060    import java.awt.Shape;
061    import java.awt.Stroke;
062    import java.awt.geom.Ellipse2D;
063    import java.awt.geom.Rectangle2D;
064    import java.util.Iterator;
065    import java.util.List;
066    
067    import org.jfree.chart.LegendItem;
068    import org.jfree.chart.axis.NumberTick;
069    import org.jfree.chart.axis.ValueAxis;
070    import org.jfree.chart.plot.DrawingSupplier;
071    import org.jfree.chart.plot.PlotRenderingInfo;
072    import org.jfree.chart.plot.PolarPlot;
073    import org.jfree.data.xy.XYDataset;
074    import org.jfree.text.TextUtilities;
075    import org.jfree.ui.TextAnchor;
076    import org.jfree.util.BooleanList;
077    import org.jfree.util.BooleanUtilities;
078    
079    /**
080     * A renderer that can be used with the {@link PolarPlot} class.
081     */
082    public class DefaultPolarItemRenderer extends AbstractRenderer
083            implements PolarItemRenderer {
084    
085        /** The plot that the renderer is assigned to. */
086        private PolarPlot plot;
087    
088        /** Flags that control whether the renderer fills each series or not. */
089        private BooleanList seriesFilled;
090    
091        /**
092         * Creates a new instance of DefaultPolarItemRenderer
093         */
094        public DefaultPolarItemRenderer() {
095            this.seriesFilled = new BooleanList();
096        }
097    
098        /**
099         * Set the plot associated with this renderer.
100         *
101         * @param plot  the plot.
102         *
103         * @see #getPlot()
104         */
105        public void setPlot(PolarPlot plot) {
106            this.plot = plot;
107        }
108    
109        /**
110         * Return the plot associated with this renderer.
111         *
112         * @return The plot.
113         *
114         * @see #setPlot(PolarPlot)
115         */
116        public PolarPlot getPlot() {
117            return this.plot;
118        }
119    
120        /**
121         * Returns the drawing supplier from the plot.
122         *
123         * @return The drawing supplier.
124         */
125        public DrawingSupplier getDrawingSupplier() {
126            DrawingSupplier result = null;
127            PolarPlot p = getPlot();
128            if (p != null) {
129                result = p.getDrawingSupplier();
130            }
131            return result;
132        }
133    
134        /**
135         * Returns <code>true</code> if the renderer should fill the specified
136         * series, and <code>false</code> otherwise.
137         *
138         * @param series  the series index (zero-based).
139         *
140         * @return A boolean.
141         */
142        public boolean isSeriesFilled(int series) {
143            boolean result = false;
144            Boolean b = this.seriesFilled.getBoolean(series);
145            if (b != null) {
146                result = b.booleanValue();
147            }
148            return result;
149        }
150    
151        /**
152         * Sets a flag that controls whether or not a series is filled.
153         *
154         * @param series  the series index.
155         * @param filled  the flag.
156         */
157        public void setSeriesFilled(int series, boolean filled) {
158            this.seriesFilled.setBoolean(series, BooleanUtilities.valueOf(filled));
159        }
160    
161        /**
162         * Plots the data for a given series.
163         *
164         * @param g2  the drawing surface.
165         * @param dataArea  the data area.
166         * @param info  collects plot rendering info.
167         * @param plot  the plot.
168         * @param dataset  the dataset.
169         * @param seriesIndex  the series index.
170         */
171        public void drawSeries(Graphics2D g2,
172                               Rectangle2D dataArea,
173                               PlotRenderingInfo info,
174                               PolarPlot plot,
175                               XYDataset dataset,
176                               int seriesIndex) {
177    
178            Polygon poly = new Polygon();
179            int numPoints = dataset.getItemCount(seriesIndex);
180            for (int i = 0; i < numPoints; i++) {
181                double theta = dataset.getXValue(seriesIndex, i);
182                double radius = dataset.getYValue(seriesIndex, i);
183                Point p = plot.translateValueThetaRadiusToJava2D(theta, radius,
184                        dataArea);
185                poly.addPoint(p.x, p.y);
186            }
187            g2.setPaint(lookupSeriesPaint(seriesIndex));
188            g2.setStroke(lookupSeriesStroke(seriesIndex));
189            if (isSeriesFilled(seriesIndex)) {
190                Composite savedComposite = g2.getComposite();
191                g2.setComposite(AlphaComposite.getInstance(
192                        AlphaComposite.SRC_OVER, 0.5f));
193                g2.fill(poly);
194                g2.setComposite(savedComposite);
195            }
196            else {
197                g2.draw(poly);
198            }
199        }
200    
201        /**
202         * Draw the angular gridlines - the spokes.
203         *
204         * @param g2  the drawing surface.
205         * @param plot  the plot.
206         * @param ticks  the ticks.
207         * @param dataArea  the data area.
208         */
209        public void drawAngularGridLines(Graphics2D g2,
210                                         PolarPlot plot,
211                                         List ticks,
212                                         Rectangle2D dataArea) {
213    
214            g2.setFont(plot.getAngleLabelFont());
215            g2.setStroke(plot.getAngleGridlineStroke());
216            g2.setPaint(plot.getAngleGridlinePaint());
217    
218            double axisMin = plot.getAxis().getLowerBound();
219            double maxRadius = plot.getMaxRadius();
220    
221            Point center = plot.translateValueThetaRadiusToJava2D(axisMin, axisMin,
222                    dataArea);
223            Iterator iterator = ticks.iterator();
224            while (iterator.hasNext()) {
225                NumberTick tick = (NumberTick) iterator.next();
226                Point p = plot.translateValueThetaRadiusToJava2D(
227                        tick.getNumber().doubleValue(), maxRadius, dataArea);
228                g2.setPaint(plot.getAngleGridlinePaint());
229                g2.drawLine(center.x, center.y, p.x, p.y);
230                if (plot.isAngleLabelsVisible()) {
231                    int x = p.x;
232                    int y = p.y;
233                    g2.setPaint(plot.getAngleLabelPaint());
234                    TextUtilities.drawAlignedString(tick.getText(), g2, x, y,
235                            TextAnchor.CENTER);
236                }
237            }
238         }
239    
240        /**
241         * Draw the radial gridlines - the rings.
242         *
243         * @param g2  the drawing surface.
244         * @param plot  the plot.
245         * @param radialAxis  the radial axis.
246         * @param ticks  the ticks.
247         * @param dataArea  the data area.
248         */
249        public void drawRadialGridLines(Graphics2D g2,
250                                        PolarPlot plot,
251                                        ValueAxis radialAxis,
252                                        List ticks,
253                                        Rectangle2D dataArea) {
254    
255            g2.setFont(radialAxis.getTickLabelFont());
256            g2.setPaint(plot.getRadiusGridlinePaint());
257            g2.setStroke(plot.getRadiusGridlineStroke());
258    
259            double axisMin = radialAxis.getLowerBound();
260            Point center = plot.translateValueThetaRadiusToJava2D(axisMin, axisMin,
261                    dataArea);
262    
263            Iterator iterator = ticks.iterator();
264            while (iterator.hasNext()) {
265                NumberTick tick = (NumberTick) iterator.next();
266                Point p = plot.translateValueThetaRadiusToJava2D(90.0,
267                        tick.getNumber().doubleValue(), dataArea);
268                int r = p.x - center.x;
269                int upperLeftX = center.x - r;
270                int upperLeftY = center.y - r;
271                int d = 2 * r;
272                Ellipse2D ring = new Ellipse2D.Double(upperLeftX, upperLeftY, d, d);
273                g2.setPaint(plot.getRadiusGridlinePaint());
274                g2.draw(ring);
275            }
276        }
277    
278        /**
279         * Return the legend for the given series.
280         *
281         * @param series  the series index.
282         *
283         * @return The legend item.
284         */
285        public LegendItem getLegendItem(int series) {
286            LegendItem result = null;
287            PolarPlot polarPlot = getPlot();
288            if (polarPlot != null) {
289                XYDataset dataset = polarPlot.getDataset();
290                if (dataset != null) {
291                    String label = dataset.getSeriesKey(series).toString();
292                    String description = label;
293                    Shape shape = lookupSeriesShape(series);
294                    Paint paint = lookupSeriesPaint(series);
295                    Paint outlinePaint = lookupSeriesOutlinePaint(series);
296                    Stroke outlineStroke = lookupSeriesOutlineStroke(series);
297                    result = new LegendItem(label, description, null, null,
298                            shape, paint, outlineStroke, outlinePaint);
299                    result.setDataset(dataset);
300                }
301            }
302            return result;
303        }
304    
305        /**
306         * Tests this renderer for equality with an arbitrary object.
307         *
308         * @param obj  the object (<code>null</code> not permitted).
309         *
310         * @return <code>true</code> if this renderer is equal to <code>obj</code>,
311         *     and <code>false</code> otherwise.
312         */
313        public boolean equals(Object obj) {
314            if (obj == null) {
315                return false;
316            }
317            if (!(obj instanceof DefaultPolarItemRenderer)) {
318                return false;
319            }
320            DefaultPolarItemRenderer that = (DefaultPolarItemRenderer) obj;
321            if (!this.seriesFilled.equals(that.seriesFilled)) {
322                return false;
323            }
324            return super.equals(obj);
325        }
326    
327        /**
328         * Returns a clone of the renderer.
329         *
330         * @return A clone.
331         *
332         * @throws CloneNotSupportedException if the renderer cannot be cloned.
333         */
334        public Object clone() throws CloneNotSupportedException {
335            DefaultPolarItemRenderer clone
336                    = (DefaultPolarItemRenderer) super.clone();
337            clone.seriesFilled = (BooleanList) this.seriesFilled.clone();
338            return clone;
339        }
340    
341    }