001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-present, by David Gilbert 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
025 * Other names may be trademarks of their respective owners.]
026 *
027 * -----------------
028 * AreaRenderer.java
029 * -----------------
030 * (C) Copyright 2002-present, by Jon Iles and Contributors.
031 *
032 * Original Author:  Jon Iles;
033 * Contributor(s):   David Gilbert;
034 *                   Christian W. Zuckschwerdt;
035 *
036 */
037
038package org.jfree.chart.renderer.category;
039
040import java.awt.Graphics2D;
041import java.awt.Paint;
042import java.awt.Shape;
043import java.awt.Stroke;
044import java.awt.geom.GeneralPath;
045import java.awt.geom.Rectangle2D;
046import java.io.Serializable;
047
048import org.jfree.chart.LegendItem;
049import org.jfree.chart.axis.CategoryAxis;
050import org.jfree.chart.axis.ValueAxis;
051import org.jfree.chart.entity.EntityCollection;
052import org.jfree.chart.event.RendererChangeEvent;
053import org.jfree.chart.plot.CategoryPlot;
054import org.jfree.chart.plot.PlotOrientation;
055import org.jfree.chart.renderer.AreaRendererEndType;
056import org.jfree.chart.ui.RectangleEdge;
057import org.jfree.chart.util.Args;
058import org.jfree.chart.util.PublicCloneable;
059import org.jfree.data.category.CategoryDataset;
060
061/**
062 * A category item renderer that draws area charts.  You can use this renderer
063 * with the {@link CategoryPlot} class.  The example shown here is generated
064 * by the {@code AreaChartDemo1.java} program included in the JFreeChart
065 * Demo Collection:
066 * <br><br>
067 * <img src="doc-files/AreaRendererSample.png" alt="AreaRendererSample.png">
068 */
069public class AreaRenderer extends AbstractCategoryItemRenderer
070        implements Cloneable, PublicCloneable, Serializable {
071
072    /** For serialization. */
073    private static final long serialVersionUID = -4231878281385812757L;
074
075    /** A flag that controls how the ends of the areas are drawn. */
076    private AreaRendererEndType endType;
077
078    /**
079     * Creates a new renderer.
080     */
081    public AreaRenderer() {
082        super();
083        this.endType = AreaRendererEndType.TAPER;
084        setDefaultLegendShape(new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0));
085    }
086
087    /**
088     * Returns a token that controls how the renderer draws the end points.
089     * The default value is {@link AreaRendererEndType#TAPER}.
090     *
091     * @return The end type (never {@code null}).
092     *
093     * @see #setEndType
094     */
095    public AreaRendererEndType getEndType() {
096        return this.endType;
097    }
098
099    /**
100     * Sets a token that controls how the renderer draws the end points, and
101     * sends a {@link RendererChangeEvent} to all registered listeners.
102     *
103     * @param type  the end type ({@code null} not permitted).
104     *
105     * @see #getEndType()
106     */
107    public void setEndType(AreaRendererEndType type) {
108        Args.nullNotPermitted(type, "type");
109        this.endType = type;
110        fireChangeEvent();
111    }
112
113    /**
114     * Returns a legend item for a series.
115     *
116     * @param datasetIndex  the dataset index (zero-based).
117     * @param series  the series index (zero-based).
118     *
119     * @return The legend item.
120     */
121    @Override
122    public LegendItem getLegendItem(int datasetIndex, int series) {
123
124        // if there is no plot, there is no dataset to access...
125        CategoryPlot cp = getPlot();
126        if (cp == null) {
127            return null;
128        }
129
130        // check that a legend item needs to be displayed...
131        if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) {
132            return null;
133        }
134
135        CategoryDataset dataset = cp.getDataset(datasetIndex);
136        String label = getLegendItemLabelGenerator().generateLabel(dataset,
137                series);
138        String description = label;
139        String toolTipText = null;
140        if (getLegendItemToolTipGenerator() != null) {
141            toolTipText = getLegendItemToolTipGenerator().generateLabel(
142                    dataset, series);
143        }
144        String urlText = null;
145        if (getLegendItemURLGenerator() != null) {
146            urlText = getLegendItemURLGenerator().generateLabel(dataset,
147                    series);
148        }
149        Shape shape = lookupLegendShape(series);
150        Paint paint = lookupSeriesPaint(series);
151        Paint outlinePaint = lookupSeriesOutlinePaint(series);
152        Stroke outlineStroke = lookupSeriesOutlineStroke(series);
153
154        LegendItem result = new LegendItem(label, description, toolTipText,
155                urlText, shape, paint, outlineStroke, outlinePaint);
156        result.setLabelFont(lookupLegendTextFont(series));
157        Paint labelPaint = lookupLegendTextPaint(series);
158        if (labelPaint != null) {
159            result.setLabelPaint(labelPaint);
160        }
161        result.setDataset(dataset);
162        result.setDatasetIndex(datasetIndex);
163        result.setSeriesKey(dataset.getRowKey(series));
164        result.setSeriesIndex(series);
165        return result;
166
167    }
168
169    /**
170     * Draw a single data item.
171     *
172     * @param g2  the graphics device.
173     * @param state  the renderer state.
174     * @param dataArea  the data plot area.
175     * @param plot  the plot.
176     * @param domainAxis  the domain axis.
177     * @param rangeAxis  the range axis.
178     * @param dataset  the dataset.
179     * @param row  the row index (zero-based).
180     * @param column  the column index (zero-based).
181     * @param pass  the pass index.
182     */
183    @Override
184    public void drawItem(Graphics2D g2, CategoryItemRendererState state,
185            Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis,
186            ValueAxis rangeAxis, CategoryDataset dataset, int row, int column,
187            int pass) {
188
189        // do nothing if item is not visible or null
190        if (!getItemVisible(row, column)) {
191            return;
192        }
193        Number value = dataset.getValue(row, column);
194        if (value == null) {
195            return;
196        }
197        PlotOrientation orientation = plot.getOrientation();
198        RectangleEdge axisEdge = plot.getDomainAxisEdge();
199        int count = dataset.getColumnCount();
200        float x0 = (float) domainAxis.getCategoryStart(column, count, dataArea,
201                axisEdge);
202        float x1 = (float) domainAxis.getCategoryMiddle(column, count,
203                dataArea, axisEdge);
204        float x2 = (float) domainAxis.getCategoryEnd(column, count, dataArea,
205                axisEdge);
206
207        x0 = Math.round(x0);
208        x1 = Math.round(x1);
209        x2 = Math.round(x2);
210
211        if (this.endType == AreaRendererEndType.TRUNCATE) {
212            if (column == 0) {
213                x0 = x1;
214            }
215            else if (column == getColumnCount() - 1) {
216                x2 = x1;
217            }
218        }
219
220        double yy1 = value.doubleValue();
221
222        double yy0 = 0.0;
223        if (this.endType == AreaRendererEndType.LEVEL) {
224            yy0 = yy1;
225        }
226        if (column > 0) {
227            Number n0 = dataset.getValue(row, column - 1);
228            if (n0 != null) {
229                yy0 = (n0.doubleValue() + yy1) / 2.0;
230            }
231        }
232
233        double yy2 = 0.0;
234        if (column < dataset.getColumnCount() - 1) {
235            Number n2 = dataset.getValue(row, column + 1);
236            if (n2 != null) {
237                yy2 = (n2.doubleValue() + yy1) / 2.0;
238            }
239        }
240        else if (this.endType == AreaRendererEndType.LEVEL) {
241            yy2 = yy1;
242        }
243
244        RectangleEdge edge = plot.getRangeAxisEdge();
245        float y0 = (float) rangeAxis.valueToJava2D(yy0, dataArea, edge);
246        float y1 = (float) rangeAxis.valueToJava2D(yy1, dataArea, edge);
247        float y2 = (float) rangeAxis.valueToJava2D(yy2, dataArea, edge);
248        float yz = (float) rangeAxis.valueToJava2D(0.0, dataArea, edge);
249        double labelXX = x1;
250        double labelYY = y1;
251        g2.setPaint(getItemPaint(row, column));
252        g2.setStroke(getItemStroke(row, column));
253
254        GeneralPath area = new GeneralPath();
255
256        if (orientation == PlotOrientation.VERTICAL) {
257            area.moveTo(x0, yz);
258            area.lineTo(x0, y0);
259            area.lineTo(x1, y1);
260            area.lineTo(x2, y2);
261            area.lineTo(x2, yz);
262        }
263        else if (orientation == PlotOrientation.HORIZONTAL) {
264            area.moveTo(yz, x0);
265            area.lineTo(y0, x0);
266            area.lineTo(y1, x1);
267            area.lineTo(y2, x2);
268            area.lineTo(yz, x2);
269            double temp = labelXX;
270            labelXX = labelYY;
271            labelYY = temp;
272        }
273        area.closePath();
274
275        g2.setPaint(getItemPaint(row, column));
276        g2.fill(area);
277
278        // draw the item labels if there are any...
279        if (isItemLabelVisible(row, column)) {
280            drawItemLabel(g2, orientation, dataset, row, column, labelXX,
281                    labelYY, (value.doubleValue() < 0.0));
282        }
283
284        // submit the current data point as a crosshair candidate
285        int datasetIndex = plot.indexOf(dataset);
286        updateCrosshairValues(state.getCrosshairState(), 
287                dataset.getRowKey(row), dataset.getColumnKey(column), yy1,
288                datasetIndex, x1, y1, orientation);
289
290        // add an item entity, if this information is being collected
291        EntityCollection entities = state.getEntityCollection();
292        if (entities != null) {
293            addItemEntity(entities, dataset, row, column, area);
294        }
295
296    }
297
298    /**
299     * Tests this instance for equality with an arbitrary object.
300     *
301     * @param obj  the object to test ({@code null} permitted).
302     *
303     * @return A boolean.
304     */
305    @Override
306    public boolean equals(Object obj) {
307        if (obj == this) {
308            return true;
309        }
310        if (!(obj instanceof AreaRenderer)) {
311            return false;
312        }
313        AreaRenderer that = (AreaRenderer) obj;
314        if (!this.endType.equals(that.endType)) {
315            return false;
316        }
317        return super.equals(obj);
318    }
319
320    /**
321     * Returns an independent copy of the renderer.
322     *
323     * @return A clone.
324     *
325     * @throws CloneNotSupportedException  should not happen.
326     */
327    @Override
328    public Object clone() throws CloneNotSupportedException {
329        return super.clone();
330    }
331
332}