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 * ClusteredXYBarRenderer.java 029 * --------------------------- 030 * (C) Copyright 2003-2006, by Paolo Cova and Contributors. 031 * 032 * Original Author: Paolo Cova; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Christian W. Zuckschwerdt; 035 * Matthias Rose; 036 * 037 * $Id: ClusteredXYBarRenderer.java,v 1.8.2.3 2006/12/11 15:31:33 mungady Exp $ 038 * 039 * Changes 040 * ------- 041 * 24-Jan-2003 : Version 1, contributed by Paolo Cova (DG); 042 * 25-Mar-2003 : Implemented Serializable (DG); 043 * 01-May-2003 : Modified drawItem() method signature (DG); 044 * 30-Jul-2003 : Modified entity constructor (CZ); 045 * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG); 046 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 047 * 07-Oct-2003 : Added renderer state (DG); 048 * 03-Nov-2003 : In draw method added state parameter and y==null value 049 * handling (MR); 050 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG); 051 * 15-Jul-2004 : Switched getX() with getXValue() and getY() with 052 * getYValue() (DG); 053 * 01-Oct-2004 : Fixed bug where 'drawBarOutline' flag is ignored (DG); 054 * 16-May-2005 : Fixed to used outline stroke for bar outlines. Removed some 055 * redundant code with the result that the renderer now respects 056 * the 'base' setting from the super-class. Added an equals() 057 * method (DG); 058 * 19-May-2005 : Added minimal item label implementation - needs improving (DG); 059 * ------------- JFREECHART 1.0.x --------------------------------------------- 060 * 11-Dec-2006 : Added support for GradientPaint (DG); 061 * 062 */ 063 064 package org.jfree.chart.renderer.xy; 065 066 import java.awt.GradientPaint; 067 import java.awt.Graphics2D; 068 import java.awt.Paint; 069 import java.awt.geom.Rectangle2D; 070 import java.io.Serializable; 071 072 import org.jfree.chart.axis.ValueAxis; 073 import org.jfree.chart.entity.EntityCollection; 074 import org.jfree.chart.entity.XYItemEntity; 075 import org.jfree.chart.labels.XYItemLabelGenerator; 076 import org.jfree.chart.labels.XYToolTipGenerator; 077 import org.jfree.chart.plot.CrosshairState; 078 import org.jfree.chart.plot.PlotOrientation; 079 import org.jfree.chart.plot.PlotRenderingInfo; 080 import org.jfree.chart.plot.XYPlot; 081 import org.jfree.data.xy.IntervalXYDataset; 082 import org.jfree.data.xy.XYDataset; 083 import org.jfree.ui.RectangleEdge; 084 import org.jfree.util.PublicCloneable; 085 086 /** 087 * An extension of {@link XYBarRenderer} that displays bars for different 088 * series values at the same x next to each other. The assumption here is 089 * that for each x (time or else) there is a y value for each series. If 090 * this is not the case, there will be spaces between bars for a given x. 091 * <P> 092 * This renderer does not include code to calculate the crosshair point for the 093 * plot. 094 */ 095 public class ClusteredXYBarRenderer extends XYBarRenderer 096 implements Cloneable, PublicCloneable, 097 Serializable { 098 099 /** For serialization. */ 100 private static final long serialVersionUID = 5864462149177133147L; 101 102 /** Determines whether bar center should be interval start. */ 103 private boolean centerBarAtStartValue; 104 105 /** 106 * Default constructor. Bar margin is set to 0.0. 107 */ 108 public ClusteredXYBarRenderer() { 109 this(0.0, false); 110 } 111 112 /** 113 * Constructs a new XY clustered bar renderer. 114 * 115 * @param margin the percentage amount to trim from the width of each bar. 116 * @param centerBarAtStartValue if true, bars will be centered on the start 117 * of the time period. 118 */ 119 public ClusteredXYBarRenderer(double margin, 120 boolean centerBarAtStartValue) { 121 super(margin); 122 this.centerBarAtStartValue = centerBarAtStartValue; 123 } 124 125 /** 126 * Draws the visual representation of a single data item. This method 127 * is mostly copied from the superclass, the change is that in the 128 * calculated space for a singe bar we draw bars for each series next to 129 * each other. The width of each bar is the available width divided by 130 * the number of series. Bars for each series are drawn in order left to 131 * right. 132 * 133 * @param g2 the graphics device. 134 * @param state the renderer state. 135 * @param dataArea the area within which the plot is being drawn. 136 * @param info collects information about the drawing. 137 * @param plot the plot (can be used to obtain standard color 138 * information etc). 139 * @param domainAxis the domain axis. 140 * @param rangeAxis the range axis. 141 * @param dataset the dataset. 142 * @param series the series index. 143 * @param item the item index. 144 * @param crosshairState crosshair information for the plot 145 * (<code>null</code> permitted). 146 * @param pass the pass index. 147 */ 148 public void drawItem(Graphics2D g2, 149 XYItemRendererState state, 150 Rectangle2D dataArea, 151 PlotRenderingInfo info, 152 XYPlot plot, 153 ValueAxis domainAxis, 154 ValueAxis rangeAxis, 155 XYDataset dataset, int series, int item, 156 CrosshairState crosshairState, 157 int pass) { 158 159 IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; 160 161 double value0; 162 double value1; 163 if (getUseYInterval()) { 164 value0 = intervalDataset.getStartYValue(series, item); 165 value1 = intervalDataset.getEndYValue(series, item); 166 } 167 else { 168 value0 = getBase(); 169 value1 = intervalDataset.getYValue(series, item); 170 } 171 if (Double.isNaN(value0) || Double.isNaN(value1)) { 172 return; 173 } 174 175 double translatedValue0 = rangeAxis.valueToJava2D(value0, dataArea, 176 plot.getRangeAxisEdge()); 177 double translatedValue1 = rangeAxis.valueToJava2D(value1, dataArea, 178 plot.getRangeAxisEdge()); 179 180 RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 181 double x1 = intervalDataset.getStartXValue(series, item); 182 double translatedX1 = domainAxis.valueToJava2D(x1, dataArea, 183 xAxisLocation); 184 185 double x2 = intervalDataset.getEndXValue(series, item); 186 double translatedX2 = domainAxis.valueToJava2D(x2, dataArea, 187 xAxisLocation); 188 189 double translatedWidth = Math.max(1, Math.abs(translatedX2 190 - translatedX1)); 191 double translatedHeight = Math.abs(translatedValue0 - translatedValue1); 192 193 if (this.centerBarAtStartValue) { 194 translatedX1 -= translatedWidth / 2; 195 } 196 197 PlotOrientation orientation = plot.getOrientation(); 198 double m = getMargin(); 199 if (m > 0.0) { 200 double cut = translatedWidth * getMargin(); 201 translatedWidth = translatedWidth - cut; 202 if (orientation == PlotOrientation.HORIZONTAL) 203 translatedX1 = translatedX1 - cut / 2; 204 else 205 translatedX1 = translatedX1 + cut / 2; 206 } 207 208 int numSeries = dataset.getSeriesCount(); 209 double seriesBarWidth = translatedWidth / numSeries; 210 211 Rectangle2D bar = null; 212 if (orientation == PlotOrientation.HORIZONTAL) { 213 bar = new Rectangle2D.Double(Math.min(translatedValue0, 214 translatedValue1), translatedX1 - seriesBarWidth 215 * (numSeries - series), translatedHeight, seriesBarWidth); 216 } 217 else if (orientation == PlotOrientation.VERTICAL) { 218 bar = new Rectangle2D.Double(translatedX1 + seriesBarWidth * series, 219 Math.min(translatedValue0, translatedValue1), 220 seriesBarWidth, translatedHeight); 221 } 222 Paint itemPaint = getItemPaint(series, item); 223 if (getGradientPaintTransformer() 224 != null && itemPaint instanceof GradientPaint) { 225 GradientPaint gp = (GradientPaint) itemPaint; 226 itemPaint = getGradientPaintTransformer().transform(gp, bar); 227 } 228 g2.setPaint(itemPaint); 229 230 g2.fill(bar); 231 if (isDrawBarOutline() && Math.abs(translatedX2 - translatedX1) > 3) { 232 g2.setStroke(getItemOutlineStroke(series, item)); 233 g2.setPaint(getItemOutlinePaint(series, item)); 234 g2.draw(bar); 235 } 236 237 if (isItemLabelVisible(series, item)) { 238 XYItemLabelGenerator generator = getItemLabelGenerator(series, 239 item); 240 drawItemLabel(g2, dataset, series, item, plot, generator, bar, 241 value1 < 0.0); 242 } 243 244 // add an entity for the item... 245 if (info != null) { 246 EntityCollection entities = info.getOwner().getEntityCollection(); 247 if (entities != null) { 248 String tip = null; 249 XYToolTipGenerator generator 250 = getToolTipGenerator(series, item); 251 if (generator != null) { 252 tip = generator.generateToolTip(dataset, series, item); 253 } 254 String url = null; 255 if (getURLGenerator() != null) { 256 url = getURLGenerator().generateURL(dataset, series, item); 257 } 258 XYItemEntity entity = new XYItemEntity(bar, dataset, series, 259 item, tip, url); 260 entities.add(entity); 261 } 262 } 263 264 } 265 266 /** 267 * Tests this renderer for equality with an arbitrary object, returning 268 * <code>true</code> if <code>obj</code> is a 269 * <code>ClusteredXYBarRenderer</code> with the same settings as this 270 * renderer, and <code>false</code> otherwise. 271 * 272 * @param obj the object (<code>null</code> permitted). 273 * 274 * @return A boolean. 275 */ 276 public boolean equals(Object obj) { 277 if (obj == this) { 278 return true; 279 } 280 if (!(obj instanceof ClusteredXYBarRenderer)) { 281 return false; 282 } 283 if (!super.equals(obj)) { 284 return false; 285 } 286 ClusteredXYBarRenderer that = (ClusteredXYBarRenderer) obj; 287 if (this.centerBarAtStartValue != that.centerBarAtStartValue) { 288 return false; 289 } 290 return true; 291 } 292 293 /** 294 * Returns a clone of the renderer. 295 * 296 * @return A clone. 297 * 298 * @throws CloneNotSupportedException if the renderer cannot be cloned. 299 */ 300 public Object clone() throws CloneNotSupportedException { 301 return super.clone(); 302 } 303 304 }