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 }