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 * StackedXYBarRenderer.java 029 * ------------------------- 030 * (C) Copyright 2004, 2005, 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.1 2005/10/25 20:56:21 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 * 050 */ 051 052 package org.jfree.chart.renderer.xy; 053 054 import java.awt.Graphics2D; 055 import java.awt.geom.Rectangle2D; 056 import java.io.Serializable; 057 058 import org.jfree.chart.axis.ValueAxis; 059 import org.jfree.chart.entity.EntityCollection; 060 import org.jfree.chart.entity.XYItemEntity; 061 import org.jfree.chart.labels.XYToolTipGenerator; 062 import org.jfree.chart.plot.CrosshairState; 063 import org.jfree.chart.plot.PlotOrientation; 064 import org.jfree.chart.plot.PlotRenderingInfo; 065 import org.jfree.chart.plot.XYPlot; 066 import org.jfree.data.Range; 067 import org.jfree.data.general.DatasetUtilities; 068 import org.jfree.data.xy.IntervalXYDataset; 069 import org.jfree.data.xy.TableXYDataset; 070 import org.jfree.data.xy.XYDataset; 071 import org.jfree.ui.RectangleEdge; 072 073 /** 074 * A bar renderer that displays the series items stacked. 075 * The dataset used together with this renderer must be a 076 * {@link org.jfree.data.xy.IntervalXYDataset} and a 077 * {@link org.jfree.data.xy.TableXYDataset}. For example, the 078 * dataset class {@link org.jfree.data.xy.CategoryTableXYDataset} 079 * implements both interfaces. 080 * 081 * @author andreas.schroeder 082 */ 083 public class StackedXYBarRenderer extends XYBarRenderer 084 implements Serializable { 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 } 142 else { 143 return null; 144 } 145 } 146 147 /** 148 * Draws the visual representation of a single data item. 149 * 150 * @param g2 the graphics device. 151 * @param state the renderer state. 152 * @param dataArea the area within which the plot is being drawn. 153 * @param info collects information about the drawing. 154 * @param plot the plot (can be used to obtain standard color information 155 * etc). 156 * @param domainAxis the domain axis. 157 * @param rangeAxis the range axis. 158 * @param dataset the dataset. 159 * @param series the series index (zero-based). 160 * @param item the item index (zero-based). 161 * @param crosshairState crosshair information for the plot 162 * (<code>null</code> permitted). 163 * @param pass the pass index. 164 */ 165 public void drawItem(Graphics2D g2, 166 XYItemRendererState state, 167 Rectangle2D dataArea, 168 PlotRenderingInfo info, 169 XYPlot plot, 170 ValueAxis domainAxis, 171 ValueAxis rangeAxis, 172 XYDataset dataset, 173 int series, 174 int item, 175 CrosshairState crosshairState, 176 int pass) { 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( 222 positiveBase, dataArea, edgeR 223 ); 224 translatedValue = rangeAxis.valueToJava2D( 225 positiveBase + value, dataArea, edgeR 226 ); 227 } 228 else { 229 translatedBase = rangeAxis.valueToJava2D( 230 negativeBase, dataArea, edgeR 231 ); 232 translatedValue = rangeAxis.valueToJava2D( 233 negativeBase + value, dataArea, edgeR 234 ); 235 } 236 237 RectangleEdge edgeD = plot.getDomainAxisEdge(); 238 double startX = intervalDataset.getStartXValue(series, item); 239 if (Double.isNaN(startX)) { 240 return; 241 } 242 double translatedStartX = domainAxis.valueToJava2D( 243 startX, dataArea, edgeD 244 ); 245 246 double endX = intervalDataset.getEndXValue(series, item); 247 if (Double.isNaN(endX)) { 248 return; 249 } 250 double translatedEndX = domainAxis.valueToJava2D(endX, dataArea, edgeD); 251 252 double translatedWidth = Math.max( 253 1, Math.abs(translatedEndX - translatedStartX) 254 ); 255 double translatedHeight = Math.abs(translatedValue - translatedBase); 256 if (getMargin() > 0.0) { 257 double cut = translatedWidth * getMargin(); 258 translatedWidth = translatedWidth - cut; 259 translatedStartX = translatedStartX + cut / 2; 260 } 261 262 Rectangle2D bar = null; 263 PlotOrientation orientation = plot.getOrientation(); 264 if (orientation == PlotOrientation.HORIZONTAL) { 265 bar = new Rectangle2D.Double( 266 Math.min(translatedBase, translatedValue), 267 translatedEndX, 268 translatedHeight, 269 translatedWidth 270 ); 271 } 272 else if (orientation == PlotOrientation.VERTICAL) { 273 bar = new Rectangle2D.Double( 274 translatedStartX, 275 Math.min(translatedBase, translatedValue), 276 translatedWidth, 277 translatedHeight 278 ); 279 } 280 281 g2.setPaint(getItemPaint(series, item)); 282 g2.fill(bar); 283 if (isDrawBarOutline() 284 && Math.abs(translatedEndX - translatedStartX) > 3) { 285 g2.setStroke(getItemStroke(series, item)); 286 g2.setPaint(getItemOutlinePaint(series, item)); 287 g2.draw(bar); 288 } 289 290 // add an entity for the item... 291 if (info != null) { 292 EntityCollection entities = info.getOwner().getEntityCollection(); 293 if (entities != null) { 294 String tip = null; 295 XYToolTipGenerator generator 296 = getToolTipGenerator(series, item); 297 if (generator != null) { 298 tip = generator.generateToolTip(dataset, series, item); 299 } 300 String url = null; 301 if (getURLGenerator() != null) { 302 url = getURLGenerator().generateURL(dataset, series, item); 303 } 304 XYItemEntity entity = new XYItemEntity( 305 bar, dataset, series, item, tip, url 306 ); 307 entities.add(entity); 308 } 309 } 310 } 311 312 }