    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     * StackedAreaRenderer.java
    029     * ------------------------
    030     * (C) Copyright 2002-2005, by Dan Rivett (d.rivett@ukonline.co.uk) and
    031     *                          Contributors.
    032     *
    033     * Original Author:  Dan Rivett (adapted from AreaCategoryItemRenderer);
    034     * Contributor(s):   Jon Iles;
    035     *                   David Gilbert (for Object Refinery Limited);
    036     *                   Christian W. Zuckschwerdt;
    037     *
    038     * $Id: StackedAreaRenderer.java,v 1.6.2.2 2005/10/25 20:54:16 mungady Exp $
    039     *
    040     * Changes:
    041     * --------
    042     * 20-Sep-2002 : Version 1, contributed by Dan Rivett;
    043     * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
    044     *               CategoryToolTipGenerator interface (DG);
    045     * 01-Nov-2002 : Added tooltips (DG);
    046     * 06-Nov-2002 : Renamed drawCategoryItem() --> drawItem() and now using axis
    047     *               for category spacing. Renamed StackedAreaCategoryItemRenderer
    048     *               --> StackedAreaRenderer (DG);
    049     * 26-Nov-2002 : Switched CategoryDataset --> TableDataset (DG);
    050     * 26-Nov-2002 : Replaced isStacked() method with getRangeType() method (DG);
    051     * 17-Jan-2003 : Moved plot classes to a separate package (DG);
    052     * 25-Mar-2003 : Implemented Serializable (DG);
    053     * 13-May-2003 : Modified to take into account the plot orientation (DG);
    054     * 30-Jul-2003 : Modified entity constructor (CZ);
    055     * 07-Oct-2003 : Added renderer state (DG);
    056     * 29-Apr-2004 : Added getRangeExtent() override (DG);
    057     * 05-Nov-2004 : Modified drawItem() signature (DG);
    058     * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() (DG);
    059     *
    060     */
    061
    062    package org.jfree.chart.renderer.category;
    063
    064    import java.awt.Graphics2D;
    065    import java.awt.Polygon;
    066    import java.awt.Shape;
    067    import java.awt.geom.Rectangle2D;
    068    import java.io.Serializable;
    069
    070    import org.jfree.chart.axis.CategoryAxis;
    071    import org.jfree.chart.axis.ValueAxis;
    072    import org.jfree.chart.entity.EntityCollection;
    073    import org.jfree.chart.plot.CategoryPlot;
    074    import org.jfree.chart.plot.PlotOrientation;
    075    import org.jfree.data.Range;
    076    import org.jfree.data.category.CategoryDataset;
    077    import org.jfree.data.general.DatasetUtilities;
    078    import org.jfree.ui.RectangleEdge;
    079    import org.jfree.util.PublicCloneable;
    080
    081    /**
    082     * A renderer that draws stacked area charts for a
    083     * {@link org.jfree.chart.plot.CategoryPlot}.
    084     *
    085     * @author Dan Rivett
    086     */
    087    public class StackedAreaRenderer extends AreaRenderer
    088                                     implements Cloneable, PublicCloneable,
    089                                                Serializable {
    090
    091        /** For serialization. */
    092        private static final long serialVersionUID = -3595635038460823663L;
    093
    094        /**
    095         * Creates a new renderer.
    096         */
    097        public StackedAreaRenderer() {
    098            super();
    099        }
    100
    101        /**
    102         * Returns the range of values the renderer requires to display all the
    103         * items from the specified dataset.
    104         *
    105         * @param dataset  the dataset (<code>null</code> not permitted).
    106         *
    107         * @return The range (or <code>null</code> if the dataset is empty).
    108         */
    109        public Range findRangeBounds(CategoryDataset dataset) {
    110            return DatasetUtilities.findStackedRangeBounds(dataset);
    111        }
    112
    113        /**
    114         * Draw a single data item.
    115         *
    116         * @param g2  the graphics device.
    117         * @param state  the renderer state.
    118         * @param dataArea  the data plot area.
    119         * @param plot  the plot.
    120         * @param domainAxis  the domain axis.
    121         * @param rangeAxis  the range axis.
    122         * @param dataset  the data.
    123         * @param row  the row index (zero-based).
    124         * @param column  the column index (zero-based).
    125         * @param pass  the pass index.
    126         */
    127        public void drawItem(Graphics2D g2,
    128                             CategoryItemRendererState state,
    129                             Rectangle2D dataArea,
    130                             CategoryPlot plot,
    131                             CategoryAxis domainAxis,
    132                             ValueAxis rangeAxis,
    133                             CategoryDataset dataset,
    134                             int row,
    135                             int column,
    136                             int pass) {
    137
    138            // plot non-null values...
    139            Number value = dataset.getValue(row, column);
    140            if (value == null) {
    141                return;
    142            }
    143
    144            // leave the y values (y1, y0) untranslated as it is going to be be
    145            // stacked up later by previous series values, after this it will be
    146            // translated.
    147            double x1 = domainAxis.getCategoryMiddle(
    148                column, getColumnCount(), dataArea, plot.getDomainAxisEdge()
    149            );
    150            double y1 = 0.0;  // calculate later
    151            double y1Untranslated = value.doubleValue();
    152
    153            g2.setPaint(getItemPaint(row, column));
    154            g2.setStroke(getItemStroke(row, column));
    155
    156            if (column != 0) {
    157
    158                Number previousValue = dataset.getValue(row, column - 1);
    159                if (previousValue != null) {
    160
    161                    double x0 = domainAxis.getCategoryMiddle(
    162                        column - 1, getColumnCount(), dataArea,
    163                        plot.getDomainAxisEdge()
    164                    );
    165                    double y0Untranslated = previousValue.doubleValue();
    166
    167                    // Get the previous height, but this will be different for both
    168                    // y0 and y1 as the previous series values could differ.
    169                    double previousHeightx0Untranslated
    170                        = getPreviousHeight(dataset, row, column - 1);
    171                    double previousHeightx1Untranslated
    172                        = getPreviousHeight(dataset, row, column);
    173
    174                    // Now stack the current y values on top of the previous values.
    175                    y0Untranslated += previousHeightx0Untranslated;
    176                    y1Untranslated += previousHeightx1Untranslated;
    177
    178                    // Now translate the previous heights
    179                    RectangleEdge location = plot.getRangeAxisEdge();
    180                    double previousHeightx0 = rangeAxis.valueToJava2D(
    181                        previousHeightx0Untranslated, dataArea, location
    182                    );
    183                    double previousHeightx1 = rangeAxis.valueToJava2D(
    184                        previousHeightx1Untranslated, dataArea, location
    185                    );
    186
    187                    // Now translate the current y values.
    188                    double y0 = rangeAxis.valueToJava2D(
    189                        y0Untranslated, dataArea, location
    190                    );
    191                    y1 = rangeAxis.valueToJava2D(
    192                        y1Untranslated, dataArea, location
    193                    );
    194
    195                    Polygon p = null;
    196                    PlotOrientation orientation = plot.getOrientation();
    197                    if (orientation == PlotOrientation.HORIZONTAL) {
    198                        p = new Polygon();
    199                        p.addPoint((int) y0, (int) x0);
    200                        p.addPoint((int) y1, (int) x1);
    201                        p.addPoint((int) previousHeightx1, (int) x1);
    202                        p.addPoint((int) previousHeightx0, (int) x0);
    203                    }
    204                    else if (orientation == PlotOrientation.VERTICAL) {
    205                        p = new Polygon();
    206                        p.addPoint((int) x0, (int) y0);
    207                        p.addPoint((int) x1, (int) y1);
    208                        p.addPoint((int) x1, (int) previousHeightx1);
    209                        p.addPoint((int) x0, (int) previousHeightx0);
    210                    }
    211                    g2.setPaint(getItemPaint(row, column));
    212                    g2.setStroke(getItemStroke(row, column));
    213                    g2.fill(p);
    214                }
    215
    216            }
    217
    218            // add an item entity, if this information is being collected
    219            EntityCollection entities = state.getEntityCollection();
    220            if (entities != null) {
    221                Shape shape = new Rectangle2D.Double(x1 - 3.0, y1 - 3.0, 6.0, 6.0);
    222                addItemEntity(entities, dataset, row, column, shape);
    223            }
    224
    225        }
    226
    227        /**
    228         * Calculates the stacked value of the all series up to, but not including
    229         * <code>series</code> for the specified category, <code>category</code>.
    230         * It returns 0.0 if <code>series</code> is the first series, i.e. 0.
    231         *
    232         * @param data  the data.
    233         * @param series  the series.
    234         * @param category  the category.
    235         *
    236         * @return double returns a cumulative value for all series' values up to
    237         *         but excluding <code>series</code> for Object
    238         *         <code>category</code>.
    239         */
    240        protected double getPreviousHeight(CategoryDataset data,
    241                                           int series, int category) {
    242
    243            double result = 0.0;
    244            Number tmp;
    245            for (int i = 0; i < series; i++) {
    246                tmp = data.getValue(i, category);
    247                if (tmp != null) {
    248                    result += tmp.doubleValue();
    249                }
    250            }
    251            return result;
    252
    253        }
    254
    255    }