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