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 * ClusteredXYBarRenderer.java 029 * --------------------------- 030 * (C) Copyright 2003-2005, 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.1 2005/10/25 20:56:21 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 * 060 */ 061 062 package org.jfree.chart.renderer.xy; 063 064 import java.awt.Graphics2D; 065 import java.awt.Paint; 066 import java.awt.geom.Rectangle2D; 067 import java.io.Serializable; 068 069 import org.jfree.chart.axis.ValueAxis; 070 import org.jfree.chart.entity.EntityCollection; 071 import org.jfree.chart.entity.XYItemEntity; 072 import org.jfree.chart.labels.XYToolTipGenerator; 073 import org.jfree.chart.plot.CrosshairState; 074 import org.jfree.chart.plot.PlotOrientation; 075 import org.jfree.chart.plot.PlotRenderingInfo; 076 import org.jfree.chart.plot.XYPlot; 077 import org.jfree.data.xy.IntervalXYDataset; 078 import org.jfree.data.xy.XYDataset; 079 import org.jfree.ui.RectangleEdge; 080 import org.jfree.util.PublicCloneable; 081 082 /** 083 * An extension of {@link XYBarRenderer} that displays bars for different 084 * series values at the same x next to each other. The assumption here is 085 * that for each x (time or else) there is a y value for each series. If 086 * this is not the case, there will be spaces between bars for a given x. 087 * <P> 088 * This renderer does not include code to calculate the crosshair point for the 089 * plot. 090 * 091 * @author Paolo Cova 092 */ 093 public class ClusteredXYBarRenderer extends XYBarRenderer 094 implements Cloneable, PublicCloneable, 095 Serializable { 096 097 /** For serialization. */ 098 private static final long serialVersionUID = 5864462149177133147L; 099 100 /** Determines whether bar center should be interval start. */ 101 private boolean centerBarAtStartValue; 102 103 /** 104 * Default constructor. Bar margin is set to 0.0. 105 */ 106 public ClusteredXYBarRenderer() { 107 this(0.0, false); 108 } 109 110 /** 111 * Constructs a new XY clustered bar renderer. 112 * 113 * @param margin the percentage amount to trim from the width of each bar. 114 * @param centerBarAtStartValue if true, bars will be centered on the start 115 * of the time period. 116 */ 117 public ClusteredXYBarRenderer(double margin, 118 boolean centerBarAtStartValue) { 119 super(margin); 120 this.centerBarAtStartValue = centerBarAtStartValue; 121 } 122 123 /** 124 * Draws the visual representation of a single data item. This method 125 * is mostly copied from the superclass, the change is that in the 126 * calculated space for a singe bar we draw bars for each series next to 127 * each other. The width of each bar is the available width divided by 128 * the number of series. Bars for each series are drawn in order left to 129 * right. 130 * 131 * @param g2 the graphics device. 132 * @param state the renderer state. 133 * @param dataArea the area within which the plot is being drawn. 134 * @param info collects information about the drawing. 135 * @param plot the plot (can be used to obtain standard color 136 * information etc). 137 * @param domainAxis the domain axis. 138 * @param rangeAxis the range axis. 139 * @param dataset the dataset. 140 * @param series the series index. 141 * @param item the item index. 142 * @param crosshairState crosshair information for the plot 143 * (<code>null</code> permitted). 144 * @param pass the pass index. 145 */ 146 public void drawItem(Graphics2D g2, 147 XYItemRendererState state, 148 Rectangle2D dataArea, 149 PlotRenderingInfo info, 150 XYPlot plot, 151 ValueAxis domainAxis, 152 ValueAxis rangeAxis, 153 XYDataset dataset, int series, int item, 154 CrosshairState crosshairState, 155 int pass) { 156 157 IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; 158 159 Paint seriesPaint = getItemPaint(series, item); 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( 176 value0, dataArea, plot.getRangeAxisEdge() 177 ); 178 double translatedValue1 = rangeAxis.valueToJava2D( 179 value1, dataArea, plot.getRangeAxisEdge() 180 ); 181 182 RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 183 double x1 = intervalDataset.getStartXValue(series, item); 184 double translatedX1 = domainAxis.valueToJava2D( 185 x1, dataArea, xAxisLocation 186 ); 187 188 double x2 = intervalDataset.getEndXValue(series, item); 189 double translatedX2 = domainAxis.valueToJava2D( 190 x2, dataArea, xAxisLocation 191 ); 192 193 double translatedWidth = Math.max( 194 1, Math.abs(translatedX2 - translatedX1) 195 ); 196 double translatedHeight = Math.abs(translatedValue0 - translatedValue1); 197 198 if (this.centerBarAtStartValue) { 199 translatedX1 -= translatedWidth / 2; 200 } 201 202 if (getMargin() > 0.0) { 203 double cut = translatedWidth * getMargin(); 204 translatedWidth = translatedWidth - cut; 205 translatedX1 = translatedX1 + cut / 2; 206 } 207 208 int numSeries = dataset.getSeriesCount(); 209 double seriesBarWidth = translatedWidth / numSeries; 210 211 Rectangle2D bar = null; 212 PlotOrientation orientation = plot.getOrientation(); 213 if (orientation == PlotOrientation.HORIZONTAL) { 214 bar = new Rectangle2D.Double( 215 Math.min(translatedValue0, translatedValue1), 216 translatedX1 - seriesBarWidth * (numSeries - series), 217 translatedHeight, seriesBarWidth 218 ); 219 } 220 else if (orientation == PlotOrientation.VERTICAL) { 221 222 bar = new Rectangle2D.Double( 223 translatedX1 + seriesBarWidth * series, 224 Math.min(translatedValue0, translatedValue1), 225 seriesBarWidth, translatedHeight 226 ); 227 228 } 229 g2.setPaint(seriesPaint); 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 // TODO: we need something better for the item labels 238 if (isItemLabelVisible(series, item)) { 239 drawItemLabel( 240 g2, orientation, dataset, series, item, bar.getCenterX(), 241 bar.getY(), value1 < 0.0 242 ); 243 } 244 245 // add an entity for the item... 246 if (info != null) { 247 EntityCollection entities = info.getOwner().getEntityCollection(); 248 if (entities != null) { 249 String tip = null; 250 XYToolTipGenerator generator 251 = getToolTipGenerator(series, item); 252 if (generator != null) { 253 tip = generator.generateToolTip(dataset, series, item); 254 } 255 String url = null; 256 if (getURLGenerator() != null) { 257 url = getURLGenerator().generateURL(dataset, series, item); 258 } 259 XYItemEntity entity = new XYItemEntity( 260 bar, dataset, series, item, tip, url 261 ); 262 entities.add(entity); 263 } 264 } 265 266 } 267 268 /** 269 * Tests this renderer for equality with an arbitrary object, returning 270 * <code>true</code> if <code>obj</code> is a 271 * <code>ClusteredXYBarRenderer</code> with the same settings as this 272 * renderer, and <code>false</code> otherwise. 273 * 274 * @param obj the object (<code>null</code> permitted). 275 * 276 * @return A boolean. 277 */ 278 public boolean equals(Object obj) { 279 if (obj == this) { 280 return true; 281 } 282 if (!(obj instanceof ClusteredXYBarRenderer)) { 283 return false; 284 } 285 if (!super.equals(obj)) { 286 return false; 287 } 288 ClusteredXYBarRenderer that = (ClusteredXYBarRenderer) obj; 289 if (this.centerBarAtStartValue != that.centerBarAtStartValue) { 290 return false; 291 } 292 return true; 293 } 294 295 /** 296 * Returns a clone of the renderer. 297 * 298 * @return A clone. 299 * 300 * @throws CloneNotSupportedException if the renderer cannot be cloned. 301 */ 302 public Object clone() throws CloneNotSupportedException { 303 return super.clone(); 304 } 305 306 } 307