    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2005, 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     * AreaRenderer.java
    029     * -----------------
    030     * (C) Copyright 2002-2005, by Jon Iles and Contributors.
    031     *
    032     * Original Author:  Jon Iles;
    033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034     *                   Christian W. Zuckschwerdt;
    035     *
    036     * $Id: AreaRenderer.java,v 1.6.2.4 2005/11/28 12:06:35 mungady Exp $
    037     *
    038     * Changes:
    039     * --------
    040     * 21-May-2002 : Version 1, contributed by John Iles (DG);
    041     * 29-May-2002 : Now extends AbstractCategoryItemRenderer (DG);
    042     * 11-Jun-2002 : Updated Javadoc comments (DG);
    043     * 25-Jun-2002 : Removed unnecessary imports (DG);
    044     * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    045     * 10-Oct-2002 : Added constructors and basic entity support (DG);
    046     * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
    047     *               CategoryToolTipGenerator interface (DG);
    048     * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG);
    049     * 06-Nov-2002 : Renamed drawCategoryItem() --> drawItem() and now using axis
    050     *               for category spacing.  Renamed AreaCategoryItemRenderer
    051     *               --> AreaRenderer (DG);
    052     * 17-Jan-2003 : Moved plot classes into a separate package (DG);
    053     * 25-Mar-2003 : Implemented Serializable (DG);
    054     * 10-Apr-2003 : Changed CategoryDataset to KeyedValues2DDataset in
    055     *               drawItem() method (DG);
    056     * 12-May-2003 : Modified to take into account the plot orientation (DG);
    057     * 30-Jul-2003 : Modified entity constructor (CZ);
    058     * 13-Aug-2003 : Implemented Cloneable (DG);
    059     * 07-Oct-2003 : Added renderer state (DG);
    060     * 05-Nov-2004 : Modified drawItem() signature (DG);
    061     * 20-Apr-2005 : Apply tooltips and URLs to legend items (DG);
    062     * 09-Jun-2005 : Use addItemEntity() method from superclass (DG);
    063     *
    064     */
    065
    066    package org.jfree.chart.renderer.category;
    067
    068    import java.awt.Graphics2D;
    069    import java.awt.Paint;
    070    import java.awt.Shape;
    071    import java.awt.Stroke;
    072    import java.awt.geom.GeneralPath;
    073    import java.awt.geom.Rectangle2D;
    074    import java.io.Serializable;
    075
    076    import org.jfree.chart.LegendItem;
    077    import org.jfree.chart.axis.CategoryAxis;
    078    import org.jfree.chart.axis.ValueAxis;
    079    import org.jfree.chart.entity.EntityCollection;
    080    import org.jfree.chart.event.RendererChangeEvent;
    081    import org.jfree.chart.plot.CategoryPlot;
    082    import org.jfree.chart.plot.PlotOrientation;
    083    import org.jfree.chart.renderer.AreaRendererEndType;
    084    import org.jfree.data.category.CategoryDataset;
    085    import org.jfree.ui.RectangleEdge;
    086    import org.jfree.util.PublicCloneable;
    087
    088    /**
    089     * A category item renderer that draws area charts.  You can use this renderer
    090     * with the {@link org.jfree.chart.plot.CategoryPlot} class.
    091     *
    092     * @author Jon Iles
    093     */
    094    public class AreaRenderer extends AbstractCategoryItemRenderer
    095                              implements Cloneable, PublicCloneable, Serializable {
    096
    097        /** For serialization. */
    098        private static final long serialVersionUID = -4231878281385812757L;
    099
    100        /** A flag that controls how the ends of the areas are drawn. */
    101        private AreaRendererEndType endType;
    102
    103        /**
    104         * Creates a new renderer.
    105         */
    106        public AreaRenderer() {
    107            super();
    108            this.endType = AreaRendererEndType.TAPER;
    109        }
    110
    111        /**
    112         * Returns a token that controls how the renderer draws the end points.
    113         *
    114         * @return The end type (never <code>null</code>).
    115         */
    116        public AreaRendererEndType getEndType() {
    117            return this.endType;
    118        }
    119
    120        /**
    121         * Sets a token that controls how the renderer draws the end points, and
    122         * sends a {@link RendererChangeEvent} to all registered listeners.
    123         *
    124         * @param type  the end type (<code>null</code> not permitted).
    125         */
    126        public void setEndType(AreaRendererEndType type) {
    127            if (type == null) {
    128                throw new IllegalArgumentException("Null 'type' argument.");
    129            }
    130            this.endType = type;
    131            notifyListeners(new RendererChangeEvent(this));
    132        }
    133
    134        /**
    135         * Returns a legend item for a series.
    136         *
    137         * @param datasetIndex  the dataset index (zero-based).
    138         * @param series  the series index (zero-based).
    139         *
    140         * @return The legend item.
    141         */
    142        public LegendItem getLegendItem(int datasetIndex, int series) {
    143
    144            CategoryPlot cp = getPlot();
    145            if (cp == null) {
    146                return null;
    147            }
    148
    149            CategoryDataset dataset;
    150            dataset = cp.getDataset(datasetIndex);
    151            String label = getLegendItemLabelGenerator().generateLabel(
    152                dataset, series
    153            );
    154            String description = label;
    155            String toolTipText = null;
    156            if (getLegendItemToolTipGenerator() != null) {
    157                toolTipText = getLegendItemToolTipGenerator().generateLabel(
    158                    dataset, series
    159                );
    160            }
    161            String urlText = null;
    162            if (getLegendItemURLGenerator() != null) {
    163                urlText = getLegendItemURLGenerator().generateLabel(
    164                    dataset, series
    165                );
    166            }
    167            Shape shape = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0);
    168            Paint paint = getSeriesPaint(series);
    169            Paint outlinePaint = getSeriesOutlinePaint(series);
    170            Stroke outlineStroke = getSeriesOutlineStroke(series);
    171
    172            return new LegendItem(label, description, toolTipText, urlText,
    173                shape, paint, outlineStroke, outlinePaint);
    174
    175        }
    176
    177        /**
    178         * Draw a single data item.
    179         *
    180         * @param g2  the graphics device.
    181         * @param state  the renderer state.
    182         * @param dataArea  the data plot area.
    183         * @param plot  the plot.
    184         * @param domainAxis  the domain axis.
    185         * @param rangeAxis  the range axis.
    186         * @param dataset  the dataset.
    187         * @param row  the row index (zero-based).
    188         * @param column  the column index (zero-based).
    189         * @param pass  the pass index.
    190         */
    191        public void drawItem(Graphics2D g2,
    192                             CategoryItemRendererState state,
    193                             Rectangle2D dataArea,
    194                             CategoryPlot plot,
    195                             CategoryAxis domainAxis,
    196                             ValueAxis rangeAxis,
    197                             CategoryDataset dataset,
    198                             int row,
    199                             int column,
    200                             int pass) {
    201
    202            // plot non-null values only...
    203            Number value = dataset.getValue(row, column);
    204            if (value != null) {
    205                PlotOrientation orientation = plot.getOrientation();
    206                RectangleEdge axisEdge = plot.getDomainAxisEdge();
    207                int count = dataset.getColumnCount();
    208                float x0 = (float) domainAxis.getCategoryStart(
    209                    column, count, dataArea, axisEdge
    210                );
    211                float x1 = (float) domainAxis.getCategoryMiddle(
    212                    column, count, dataArea, axisEdge
    213                );
    214                float x2 = (float) domainAxis.getCategoryEnd(
    215                    column, count, dataArea, axisEdge
    216                );
    217
    218                x0 = Math.round(x0);
    219                x1 = Math.round(x1);
    220                x2 = Math.round(x2);
    221
    222                if (this.endType == AreaRendererEndType.TRUNCATE) {
    223                    if (column == 0) {
    224                        x0 = x1;
    225                    }
    226                    else if (column == getColumnCount() - 1) {
    227                        x2 = x1;
    228                    }
    229                }
    230
    231                double yy1 = value.doubleValue();
    232
    233                double yy0 = 0.0;
    234                if (column > 0) {
    235                    Number n0 = dataset.getValue(row, column - 1);
    236                    if (n0 != null) {
    237                        yy0 = (n0.doubleValue() + yy1) / 2.0;
    238                    }
    239                }
    240
    241                double yy2 = 0.0;
    242                if (column < dataset.getColumnCount() - 1) {
    243                    Number n2 = dataset.getValue(row, column + 1);
    244                    if (n2 != null) {
    245                        yy2 = (n2.doubleValue() + yy1) / 2.0;
    246                    }
    247                }
    248
    249                RectangleEdge edge = plot.getRangeAxisEdge();
    250                float y0 = (float) rangeAxis.valueToJava2D(yy0, dataArea, edge);
    251                float y1 = (float) rangeAxis.valueToJava2D(yy1, dataArea, edge);
    252                float y2 = (float) rangeAxis.valueToJava2D(yy2, dataArea, edge);
    253                float yz = (float) rangeAxis.valueToJava2D(0.0, dataArea, edge);
    254
    255                g2.setPaint(getItemPaint(row, column));
    256                g2.setStroke(getItemStroke(row, column));
    257
    258                GeneralPath area = new GeneralPath();
    259
    260                if (orientation == PlotOrientation.VERTICAL) {
    261                    area.moveTo(x0, yz);
    262                    area.lineTo(x0, y0);
    263                    area.lineTo(x1, y1);
    264                    area.lineTo(x2, y2);
    265                    area.lineTo(x2, yz);
    266                }
    267                else if (orientation == PlotOrientation.HORIZONTAL) {
    268                    area.moveTo(yz, x0);
    269                    area.lineTo(y0, x0);
    270                    area.lineTo(y1, x1);
    271                    area.lineTo(y2, x2);
    272                    area.lineTo(yz, x2);
    273                }
    274                area.closePath();
    275
    276                g2.setPaint(getItemPaint(row, column));
    277                g2.fill(area);
    278
    279                // draw the item labels if there are any...
    280                if (isItemLabelVisible(row, column)) {
    281                    drawItemLabel(
    282                        g2, orientation, dataset, row, column, x1, y1,
    283                        (value.doubleValue() < 0.0)
    284                    );
    285                }
    286
    287                // add an item entity, if this information is being collected
    288                EntityCollection entities = state.getEntityCollection();
    289                if (entities != null) {
    290                    addItemEntity(entities, dataset, row, column, area);
    291                }
    292            }
    293
    294        }
    295
    296        /**
    297         * Returns an independent copy of the renderer.
    298         *
    299         * @return A clone.
    300         *
    301         * @throws CloneNotSupportedException  should not happen.
    302         */
    303        public Object clone() throws CloneNotSupportedException {
    304            return super.clone();
    305        }
    306
    307    }