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
052package org.jfree.chart.renderer;
053
054import java.awt.AlphaComposite;
055import java.awt.Composite;
056import java.awt.Graphics2D;
057import java.awt.Paint;
058import java.awt.Point;
059import java.awt.Polygon;
060import java.awt.Shape;
061import java.awt.Stroke;
062import java.awt.geom.Ellipse2D;
063import java.awt.geom.Rectangle2D;
064import java.util.Iterator;
065import java.util.List;
066
067import org.jfree.chart.LegendItem;
068import org.jfree.chart.axis.NumberTick;
069import org.jfree.chart.axis.ValueAxis;
070import org.jfree.chart.plot.DrawingSupplier;
071import org.jfree.chart.plot.PlotRenderingInfo;
072import org.jfree.chart.plot.PolarPlot;
073import org.jfree.data.xy.XYDataset;
074import org.jfree.text.TextUtilities;
075import org.jfree.ui.TextAnchor;
076import org.jfree.util.BooleanList;
077import org.jfree.util.BooleanUtilities;
078
079/**
080 * A renderer that can be used with the {@link PolarPlot} class.
081 */
082public 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}