    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     * AreaRenderer.java
    029     * -----------------
    030     * (C) Copyright 2002-2008, 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     * Changes:
    037     * --------
    038     * 21-May-2002 : Version 1, contributed by John Iles (DG);
    039     * 29-May-2002 : Now extends AbstractCategoryItemRenderer (DG);
    040     * 11-Jun-2002 : Updated Javadoc comments (DG);
    041     * 25-Jun-2002 : Removed unnecessary imports (DG);
    042     * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    043     * 10-Oct-2002 : Added constructors and basic entity support (DG);
    044     * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
    045     *               CategoryToolTipGenerator interface (DG);
    046     * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG);
    047     * 06-Nov-2002 : Renamed drawCategoryItem() --> drawItem() and now using axis
    048     *               for category spacing.  Renamed AreaCategoryItemRenderer
    049     *               --> AreaRenderer (DG);
    050     * 17-Jan-2003 : Moved plot classes into a separate package (DG);
    051     * 25-Mar-2003 : Implemented Serializable (DG);
    052     * 10-Apr-2003 : Changed CategoryDataset to KeyedValues2DDataset in
    053     *               drawItem() method (DG);
    054     * 12-May-2003 : Modified to take into account the plot orientation (DG);
    055     * 30-Jul-2003 : Modified entity constructor (CZ);
    056     * 13-Aug-2003 : Implemented Cloneable (DG);
    057     * 07-Oct-2003 : Added renderer state (DG);
    058     * 05-Nov-2004 : Modified drawItem() signature (DG);
    059     * 20-Apr-2005 : Apply tooltips and URLs to legend items (DG);
    060     * 09-Jun-2005 : Use addItemEntity() method from superclass (DG);
    061     * ------------- JFREECHART 1.0.x ---------------------------------------------
    062     * 11-Oct-2006 : Fixed bug in equals() method (DG);
    063     * 30-Nov-2006 : Added checks for series visibility (DG);
    064     * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG);
    065     * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG);
    066     * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
    067     * 17-Jun-2008 : Apply legend shape, font and paint attributes (DG);
    068     * 26-Jun-2008 : Added crosshair support (DG);
    069     *
    070     */
    071
    072    package org.jfree.chart.renderer.category;
    073
    074    import java.awt.Graphics2D;
    075    import java.awt.Paint;
    076    import java.awt.Shape;
    077    import java.awt.Stroke;
    078    import java.awt.geom.GeneralPath;
    079    import java.awt.geom.Rectangle2D;
    080    import java.io.Serializable;
    081
    082    import org.jfree.chart.LegendItem;
    083    import org.jfree.chart.axis.CategoryAxis;
    084    import org.jfree.chart.axis.ValueAxis;
    085    import org.jfree.chart.entity.EntityCollection;
    086    import org.jfree.chart.event.RendererChangeEvent;
    087    import org.jfree.chart.plot.CategoryPlot;
    088    import org.jfree.chart.plot.PlotOrientation;
    089    import org.jfree.chart.renderer.AreaRendererEndType;
    090    import org.jfree.data.category.CategoryDataset;
    091    import org.jfree.ui.RectangleEdge;
    092    import org.jfree.util.PublicCloneable;
    093
    094    /**
    095     * A category item renderer that draws area charts.  You can use this renderer
    096     * with the {@link CategoryPlot} class.  The example shown here is generated
    097     * by the <code>AreaChartDemo1.java</code> program included in the JFreeChart
    098     * Demo Collection:
    099     * <br><br>
    100     * <img src="../../../../../images/AreaRendererSample.png"
    101     * alt="AreaRendererSample.png" />
    102     */
    103    public class AreaRenderer extends AbstractCategoryItemRenderer
    104            implements Cloneable, PublicCloneable, Serializable {
    105
    106        /** For serialization. */
    107        private static final long serialVersionUID = -4231878281385812757L;
    108
    109        /** A flag that controls how the ends of the areas are drawn. */
    110        private AreaRendererEndType endType;
    111
    112        /**
    113         * Creates a new renderer.
    114         */
    115        public AreaRenderer() {
    116            super();
    117            this.endType = AreaRendererEndType.TAPER;
    118            setBaseLegendShape(new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0));
    119        }
    120
    121        /**
    122         * Returns a token that controls how the renderer draws the end points.
    123         * The default value is {@link AreaRendererEndType#TAPER}.
    124         *
    125         * @return The end type (never <code>null</code>).
    126         *
    127         * @see #setEndType
    128         */
    129        public AreaRendererEndType getEndType() {
    130            return this.endType;
    131        }
    132
    133        /**
    134         * Sets a token that controls how the renderer draws the end points, and
    135         * sends a {@link RendererChangeEvent} to all registered listeners.
    136         *
    137         * @param type  the end type (<code>null</code> not permitted).
    138         *
    139         * @see #getEndType()
    140         */
    141        public void setEndType(AreaRendererEndType type) {
    142            if (type == null) {
    143                throw new IllegalArgumentException("Null 'type' argument.");
    144            }
    145            this.endType = type;
    146            fireChangeEvent();
    147        }
    148
    149        /**
    150         * Returns a legend item for a series.
    151         *
    152         * @param datasetIndex  the dataset index (zero-based).
    153         * @param series  the series index (zero-based).
    154         *
    155         * @return The legend item.
    156         */
    157        public LegendItem getLegendItem(int datasetIndex, int series) {
    158
    159            // if there is no plot, there is no dataset to access...
    160            CategoryPlot cp = getPlot();
    161            if (cp == null) {
    162                return null;
    163            }
    164
    165            // check that a legend item needs to be displayed...
    166            if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) {
    167                return null;
    168            }
    169
    170            CategoryDataset dataset = cp.getDataset(datasetIndex);
    171            String label = getLegendItemLabelGenerator().generateLabel(dataset,
    172                    series);
    173            String description = label;
    174            String toolTipText = null;
    175            if (getLegendItemToolTipGenerator() != null) {
    176                toolTipText = getLegendItemToolTipGenerator().generateLabel(
    177                        dataset, series);
    178            }
    179            String urlText = null;
    180            if (getLegendItemURLGenerator() != null) {
    181                urlText = getLegendItemURLGenerator().generateLabel(dataset,
    182                        series);
    183            }
    184            Shape shape = lookupLegendShape(series);
    185            Paint paint = lookupSeriesPaint(series);
    186            Paint outlinePaint = lookupSeriesOutlinePaint(series);
    187            Stroke outlineStroke = lookupSeriesOutlineStroke(series);
    188
    189            LegendItem result = new LegendItem(label, description, toolTipText,
    190                    urlText, shape, paint, outlineStroke, outlinePaint);
    191            result.setLabelFont(lookupLegendTextFont(series));
    192            Paint labelPaint = lookupLegendTextPaint(series);
    193            if (labelPaint != null) {
    194                result.setLabelPaint(labelPaint);
    195            }
    196            result.setDataset(dataset);
    197            result.setDatasetIndex(datasetIndex);
    198            result.setSeriesKey(dataset.getRowKey(series));
    199            result.setSeriesIndex(series);
    200            return result;
    201
    202        }
    203
    204        /**
    205         * Draw a single data item.
    206         *
    207         * @param g2  the graphics device.
    208         * @param state  the renderer state.
    209         * @param dataArea  the data plot area.
    210         * @param plot  the plot.
    211         * @param domainAxis  the domain axis.
    212         * @param rangeAxis  the range axis.
    213         * @param dataset  the dataset.
    214         * @param row  the row index (zero-based).
    215         * @param column  the column index (zero-based).
    216         * @param pass  the pass index.
    217         */
    218        public void drawItem(Graphics2D g2,
    219                             CategoryItemRendererState state,
    220                             Rectangle2D dataArea,
    221                             CategoryPlot plot,
    222                             CategoryAxis domainAxis,
    223                             ValueAxis rangeAxis,
    224                             CategoryDataset dataset,
    225                             int row,
    226                             int column,
    227                             int pass) {
    228
    229            // do nothing if item is not visible
    230            if (!getItemVisible(row, column)) {
    231                return;
    232            }
    233
    234            // plot non-null values only...
    235            Number value = dataset.getValue(row, column);
    236            if (value != null) {
    237                PlotOrientation orientation = plot.getOrientation();
    238                RectangleEdge axisEdge = plot.getDomainAxisEdge();
    239                int count = dataset.getColumnCount();
    240                float x0 = (float) domainAxis.getCategoryStart(column, count,
    241                        dataArea, axisEdge);
    242                float x1 = (float) domainAxis.getCategoryMiddle(column, count,
    243                        dataArea, axisEdge);
    244                float x2 = (float) domainAxis.getCategoryEnd(column, count,
    245                        dataArea, axisEdge);
    246
    247                x0 = Math.round(x0);
    248                x1 = Math.round(x1);
    249                x2 = Math.round(x2);
    250
    251                if (this.endType == AreaRendererEndType.TRUNCATE) {
    252                    if (column == 0) {
    253                        x0 = x1;
    254                    }
    255                    else if (column == getColumnCount() - 1) {
    256                        x2 = x1;
    257                    }
    258                }
    259
    260                double yy1 = value.doubleValue();
    261
    262                double yy0 = 0.0;
    263                if (column > 0) {
    264                    Number n0 = dataset.getValue(row, column - 1);
    265                    if (n0 != null) {
    266                        yy0 = (n0.doubleValue() + yy1) / 2.0;
    267                    }
    268                }
    269
    270                double yy2 = 0.0;
    271                if (column < dataset.getColumnCount() - 1) {
    272                    Number n2 = dataset.getValue(row, column + 1);
    273                    if (n2 != null) {
    274                        yy2 = (n2.doubleValue() + yy1) / 2.0;
    275                    }
    276                }
    277
    278                RectangleEdge edge = plot.getRangeAxisEdge();
    279                float y0 = (float) rangeAxis.valueToJava2D(yy0, dataArea, edge);
    280                float y1 = (float) rangeAxis.valueToJava2D(yy1, dataArea, edge);
    281                float y2 = (float) rangeAxis.valueToJava2D(yy2, dataArea, edge);
    282                float yz = (float) rangeAxis.valueToJava2D(0.0, dataArea, edge);
    283
    284                g2.setPaint(getItemPaint(row, column));
    285                g2.setStroke(getItemStroke(row, column));
    286
    287                GeneralPath area = new GeneralPath();
    288
    289                if (orientation == PlotOrientation.VERTICAL) {
    290                    area.moveTo(x0, yz);
    291                    area.lineTo(x0, y0);
    292                    area.lineTo(x1, y1);
    293                    area.lineTo(x2, y2);
    294                    area.lineTo(x2, yz);
    295                }
    296                else if (orientation == PlotOrientation.HORIZONTAL) {
    297                    area.moveTo(yz, x0);
    298                    area.lineTo(y0, x0);
    299                    area.lineTo(y1, x1);
    300                    area.lineTo(y2, x2);
    301                    area.lineTo(yz, x2);
    302                }
    303                area.closePath();
    304
    305                g2.setPaint(getItemPaint(row, column));
    306                g2.fill(area);
    307
    308                // draw the item labels if there are any...
    309                if (isItemLabelVisible(row, column)) {
    310                    drawItemLabel(g2, orientation, dataset, row, column, x1, y1,
    311                            (value.doubleValue() < 0.0));
    312                }
    313
    314                // submit the current data point as a crosshair candidate
    315                int datasetIndex = plot.indexOf(dataset);
    316                updateCrosshairValues(state.getCrosshairState(),
    317                        dataset.getRowKey(row), dataset.getColumnKey(column),
    318                        yy1, datasetIndex, x1, y1, orientation);
    319
    320                // add an item entity, if this information is being collected
    321                EntityCollection entities = state.getEntityCollection();
    322                if (entities != null) {
    323                    addItemEntity(entities, dataset, row, column, area);
    324                }
    325            }
    326
    327        }
    328
    329        /**
    330         * Tests this instance for equality with an arbitrary object.
    331         *
    332         * @param obj  the object to test (<code>null</code> permitted).
    333         *
    334         * @return A boolean.
    335         */
    336        public boolean equals(Object obj) {
    337            if (obj == this) {
    338                return true;
    339            }
    340            if (!(obj instanceof AreaRenderer)) {
    341                return false;
    342            }
    343            AreaRenderer that = (AreaRenderer) obj;
    344            if (!this.endType.equals(that.endType)) {
    345                return false;
    346            }
    347            return super.equals(obj);
    348        }
    349
    350        /**
    351         * Returns an independent copy of the renderer.
    352         *
    353         * @return A clone.
    354         *
    355         * @throws CloneNotSupportedException  should not happen.
    356         */
    357        public Object clone() throws CloneNotSupportedException {
    358            return super.clone();
    359        }
    360
    361    }