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