001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2006, 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 * StackedXYBarRenderer.java 029 * ------------------------- 030 * (C) Copyright 2004-2006, by Andreas Schroeder and Contributors. 031 * 032 * Original Author: Andreas Schroeder; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * 035 * $Id: StackedXYBarRenderer.java,v 1.10.2.3 2006/12/06 22:26:35 mungady Exp $ 036 * 037 * Changes 038 * ------- 039 * 01-Apr-2004 : Version 1 (AS); 040 * 15-Jul-2004 : Switched getX() with getXValue() and getY() with 041 * getYValue() (DG); 042 * 15-Aug-2004 : Added drawBarOutline to control draw/don't-draw bar 043 * outlines (BN); 044 * 10-Sep-2004 : drawBarOutline attribute is now inherited from XYBarRenderer 045 * and double primitives are retrieved from the dataset rather 046 * than Number objects (DG); 047 * 07-Jan-2005 : Updated for method name change in DatasetUtilities (DG); 048 * 25-Jan-2005 : Modified to handle negative values correctly (DG); 049 * ------------- JFREECHART 1.0.x --------------------------------------------- 050 * 06-Dec-2006 : Added support for GradientPaint (DG); 051 * 052 */ 053 054 package org.jfree.chart.renderer.xy; 055 056 import java.awt.GradientPaint; 057 import java.awt.Graphics2D; 058 import java.awt.Paint; 059 import java.awt.geom.Rectangle2D; 060 061 import org.jfree.chart.axis.ValueAxis; 062 import org.jfree.chart.entity.EntityCollection; 063 import org.jfree.chart.entity.XYItemEntity; 064 import org.jfree.chart.labels.XYToolTipGenerator; 065 import org.jfree.chart.plot.CrosshairState; 066 import org.jfree.chart.plot.PlotOrientation; 067 import org.jfree.chart.plot.PlotRenderingInfo; 068 import org.jfree.chart.plot.XYPlot; 069 import org.jfree.data.Range; 070 import org.jfree.data.general.DatasetUtilities; 071 import org.jfree.data.xy.IntervalXYDataset; 072 import org.jfree.data.xy.TableXYDataset; 073 import org.jfree.data.xy.XYDataset; 074 import org.jfree.ui.RectangleEdge; 075 076 /** 077 * A bar renderer that displays the series items stacked. 078 * The dataset used together with this renderer must be a 079 * {@link org.jfree.data.xy.IntervalXYDataset} and a 080 * {@link org.jfree.data.xy.TableXYDataset}. For example, the 081 * dataset class {@link org.jfree.data.xy.CategoryTableXYDataset} 082 * implements both interfaces. 083 */ 084 public class StackedXYBarRenderer extends XYBarRenderer { 085 086 /** For serialization. */ 087 private static final long serialVersionUID = -7049101055533436444L; 088 089 /** 090 * Creates a new renderer. 091 */ 092 public StackedXYBarRenderer() { 093 super(); 094 } 095 096 /** 097 * Creates a new renderer. 098 * 099 * @param margin the percentual amount of the bars that are cut away. 100 */ 101 public StackedXYBarRenderer(double margin) { 102 super(margin); 103 } 104 105 /** 106 * Initialises the renderer and returns a state object that should be 107 * passed to all subsequent calls to the drawItem() method. Here there is 108 * nothing to do. 109 * 110 * @param g2 the graphics device. 111 * @param dataArea the area inside the axes. 112 * @param plot the plot. 113 * @param data the data. 114 * @param info an optional info collection object to return data back to 115 * the caller. 116 * 117 * @return A state object. 118 */ 119 public XYItemRendererState initialise(Graphics2D g2, 120 Rectangle2D dataArea, 121 XYPlot plot, 122 XYDataset data, 123 PlotRenderingInfo info) { 124 return new XYBarRendererState(info); 125 } 126 127 /** 128 * Returns the range of values the renderer requires to display all the 129 * items from the specified dataset. 130 * 131 * @param dataset the dataset (<code>null</code> permitted). 132 * 133 * @return The range (<code>null</code> if the dataset is <code>null</code> 134 * or empty). 135 */ 136 public Range findRangeBounds(XYDataset dataset) { 137 if (dataset != null) { 138 return DatasetUtilities.findStackedRangeBounds( 139 (TableXYDataset) dataset); 140 } 141 else { 142 return null; 143 } 144 } 145 146 /** 147 * Draws the visual representation of a single data item. 148 * 149 * @param g2 the graphics device. 150 * @param state the renderer state. 151 * @param dataArea the area within which the plot is being drawn. 152 * @param info collects information about the drawing. 153 * @param plot the plot (can be used to obtain standard color information 154 * etc). 155 * @param domainAxis the domain axis. 156 * @param rangeAxis the range axis. 157 * @param dataset the dataset. 158 * @param series the series index (zero-based). 159 * @param item the item index (zero-based). 160 * @param crosshairState crosshair information for the plot 161 * (<code>null</code> permitted). 162 * @param pass the pass index. 163 */ 164 public void drawItem(Graphics2D g2, 165 XYItemRendererState state, 166 Rectangle2D dataArea, 167 PlotRenderingInfo info, 168 XYPlot plot, 169 ValueAxis domainAxis, 170 ValueAxis rangeAxis, 171 XYDataset dataset, 172 int series, 173 int item, 174 CrosshairState crosshairState, 175 int pass) { 176 177 if (!(dataset instanceof IntervalXYDataset 178 && dataset instanceof TableXYDataset)) { 179 String message = "dataset (type " + dataset.getClass().getName() 180 + ") has wrong type:"; 181 boolean and = false; 182 if (!IntervalXYDataset.class.isAssignableFrom(dataset.getClass())) { 183 message += " it is no IntervalXYDataset"; 184 and = true; 185 } 186 if (!TableXYDataset.class.isAssignableFrom(dataset.getClass())) { 187 if (and) { 188 message += " and"; 189 } 190 message += " it is no TableXYDataset"; 191 } 192 193 throw new IllegalArgumentException(message); 194 } 195 196 IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; 197 double value = intervalDataset.getYValue(series, item); 198 if (Double.isNaN(value)) { 199 return; 200 } 201 202 double positiveBase = 0.0; 203 double negativeBase = 0.0; 204 205 for (int i = 0; i < series; i++) { 206 double v = dataset.getYValue(i, item); 207 if (!Double.isNaN(v)) { 208 if (v > 0) { 209 positiveBase = positiveBase + v; 210 } 211 else { 212 negativeBase = negativeBase + v; 213 } 214 } 215 } 216 217 double translatedBase; 218 double translatedValue; 219 RectangleEdge edgeR = plot.getRangeAxisEdge(); 220 if (value > 0.0) { 221 translatedBase = rangeAxis.valueToJava2D(positiveBase, dataArea, 222 edgeR); 223 translatedValue = rangeAxis.valueToJava2D(positiveBase + value, 224 dataArea, edgeR); 225 } 226 else { 227 translatedBase = rangeAxis.valueToJava2D(negativeBase, dataArea, 228 edgeR); 229 translatedValue = rangeAxis.valueToJava2D(negativeBase + value, 230 dataArea, edgeR); 231 } 232 233 RectangleEdge edgeD = plot.getDomainAxisEdge(); 234 double startX = intervalDataset.getStartXValue(series, item); 235 if (Double.isNaN(startX)) { 236 return; 237 } 238 double translatedStartX = domainAxis.valueToJava2D(startX, dataArea, 239 edgeD); 240 241 double endX = intervalDataset.getEndXValue(series, item); 242 if (Double.isNaN(endX)) { 243 return; 244 } 245 double translatedEndX = domainAxis.valueToJava2D(endX, dataArea, edgeD); 246 247 double translatedWidth = Math.max(1, Math.abs(translatedEndX 248 - translatedStartX)); 249 double translatedHeight = Math.abs(translatedValue - translatedBase); 250 if (getMargin() > 0.0) { 251 double cut = translatedWidth * getMargin(); 252 translatedWidth = translatedWidth - cut; 253 translatedStartX = translatedStartX + cut / 2; 254 } 255 256 Rectangle2D bar = null; 257 PlotOrientation orientation = plot.getOrientation(); 258 if (orientation == PlotOrientation.HORIZONTAL) { 259 bar = new Rectangle2D.Double(Math.min(translatedBase, 260 translatedValue), translatedEndX, translatedHeight, 261 translatedWidth); 262 } 263 else if (orientation == PlotOrientation.VERTICAL) { 264 bar = new Rectangle2D.Double(translatedStartX, 265 Math.min(translatedBase, translatedValue), 266 translatedWidth, translatedHeight); 267 } 268 269 Paint itemPaint = getItemPaint(series, item); 270 if (getGradientPaintTransformer() 271 != null && itemPaint instanceof GradientPaint) { 272 GradientPaint gp = (GradientPaint) itemPaint; 273 itemPaint = getGradientPaintTransformer().transform(gp, bar); 274 } 275 g2.setPaint(itemPaint); 276 g2.fill(bar); 277 if (isDrawBarOutline() 278 && Math.abs(translatedEndX - translatedStartX) > 3) { 279 g2.setStroke(getItemStroke(series, item)); 280 g2.setPaint(getItemOutlinePaint(series, item)); 281 g2.draw(bar); 282 } 283 284 // add an entity for the item... 285 if (info != null) { 286 EntityCollection entities = info.getOwner().getEntityCollection(); 287 if (entities != null) { 288 String tip = null; 289 XYToolTipGenerator generator 290 = getToolTipGenerator(series, item); 291 if (generator != null) { 292 tip = generator.generateToolTip(dataset, series, item); 293 } 294 String url = null; 295 if (getURLGenerator() != null) { 296 url = getURLGenerator().generateURL(dataset, series, item); 297 } 298 XYItemEntity entity = new XYItemEntity(bar, dataset, series, 299 item, tip, url); 300 entities.add(entity); 301 } 302 } 303 } 304 305 }