001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, 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 * XYErrorRenderer.java 029 * -------------------- 030 * (C) Copyright 2006, 2007, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * $Id: XYErrorRenderer.java,v 1.1.2.4 2007/03/23 14:01:12 mungady Exp $ 036 * 037 * Changes 038 * ------- 039 * 25-Oct-2006 : Version 1 (DG); 040 * 23-Mar-2007 : Check item visibility before drawing error bars - see bug 041 * 1686178 (DG); 042 * 043 */ 044 045 package org.jfree.chart.renderer.xy; 046 047 import java.awt.BasicStroke; 048 import java.awt.Graphics2D; 049 import java.awt.Paint; 050 import java.awt.geom.Line2D; 051 import java.awt.geom.Rectangle2D; 052 import java.io.IOException; 053 import java.io.ObjectInputStream; 054 import java.io.ObjectOutputStream; 055 056 import org.jfree.chart.axis.ValueAxis; 057 import org.jfree.chart.event.RendererChangeEvent; 058 import org.jfree.chart.plot.CrosshairState; 059 import org.jfree.chart.plot.PlotOrientation; 060 import org.jfree.chart.plot.PlotRenderingInfo; 061 import org.jfree.chart.plot.XYPlot; 062 import org.jfree.data.Range; 063 import org.jfree.data.general.DatasetUtilities; 064 import org.jfree.data.xy.IntervalXYDataset; 065 import org.jfree.data.xy.XYDataset; 066 import org.jfree.io.SerialUtilities; 067 import org.jfree.ui.RectangleEdge; 068 import org.jfree.util.PaintUtilities; 069 070 /** 071 * A line and shape renderer that can also display x and/or y-error values. 072 * This renderer expects an {@link IntervalXYDataset}, otherwise it reverts 073 * to the behaviour of the super class. 074 * 075 * @since 1.0.3 076 */ 077 public class XYErrorRenderer extends XYLineAndShapeRenderer { 078 079 /** A flag that controls whether or not the x-error bars are drawn. */ 080 private boolean drawXError; 081 082 /** A flag that controls whether or not the y-error bars are drawn. */ 083 private boolean drawYError; 084 085 /** The length of the cap at the end of the error bars. */ 086 private double capLength; 087 088 /** 089 * The paint used to draw the error bars (if <code>null</code> we use the 090 * series paint). 091 */ 092 private transient Paint errorPaint; 093 094 /** 095 * Creates a new <code>XYErrorRenderer</code> instance. 096 */ 097 public XYErrorRenderer() { 098 super(false, true); 099 this.drawXError = true; 100 this.drawYError = true; 101 this.errorPaint = null; 102 this.capLength = 4.0; 103 } 104 105 /** 106 * Returns the flag that controls whether or not the renderer draws error 107 * bars for the x-values. 108 * 109 * @return A boolean. 110 * 111 * @see #setDrawXError(boolean) 112 */ 113 public boolean getDrawXError() { 114 return this.drawXError; 115 } 116 117 /** 118 * Sets the flag that controls whether or not the renderer draws error 119 * bars for the x-values and, if the flag changes, sends a 120 * {@link RendererChangeEvent} to all registered listeners. 121 * 122 * @param draw the flag value. 123 * 124 * @see #getDrawXError() 125 */ 126 public void setDrawXError(boolean draw) { 127 if (this.drawXError != draw) { 128 this.drawXError = draw; 129 this.notifyListeners(new RendererChangeEvent(this)); 130 } 131 } 132 133 /** 134 * Returns the flag that controls whether or not the renderer draws error 135 * bars for the y-values. 136 * 137 * @return A boolean. 138 * 139 * @see #setDrawYError(boolean) 140 */ 141 public boolean getDrawYError() { 142 return this.drawYError; 143 } 144 145 /** 146 * Sets the flag that controls whether or not the renderer draws error 147 * bars for the y-values and, if the flag changes, sends a 148 * {@link RendererChangeEvent} to all registered listeners. 149 * 150 * @param draw the flag value. 151 * 152 * @see #getDrawYError() 153 */ 154 public void setDrawYError(boolean draw) { 155 if (this.drawYError != draw) { 156 this.drawYError = draw; 157 notifyListeners(new RendererChangeEvent(this)); 158 } 159 } 160 161 /** 162 * Returns the length (in Java2D units) of the cap at the end of the error 163 * bars. 164 * 165 * @return The cap length. 166 * 167 * @see #setCapLength(double) 168 */ 169 public double getCapLength() { 170 return this.capLength; 171 } 172 173 /** 174 * Sets the length of the cap at the end of the error bars, and sends a 175 * {@link RendererChangeEvent} to all registered listeners. 176 * 177 * @param length the length (in Java2D units). 178 * 179 * @see #getCapLength() 180 */ 181 public void setCapLength(double length) { 182 this.capLength = length; 183 notifyListeners(new RendererChangeEvent(this)); 184 } 185 186 /** 187 * Returns the paint used to draw the error bars. If this is 188 * <code>null</code> (the default), the item paint is used instead. 189 * 190 * @return The paint (possibly <code>null</code>). 191 * 192 * @see #setErrorPaint(Paint) 193 */ 194 public Paint getErrorPaint() { 195 return this.errorPaint; 196 } 197 198 /** 199 * Sets the paint used to draw the error bars. 200 * 201 * @param paint the paint (<code>null</code> permitted). 202 * 203 * @see #getErrorPaint() 204 */ 205 public void setErrorPaint(Paint paint) { 206 this.errorPaint = paint; 207 notifyListeners(new RendererChangeEvent(this)); 208 } 209 210 /** 211 * Returns the range required by this renderer to display all the domain 212 * values in the specified dataset. 213 * 214 * @param dataset the dataset (<code>null</code> permitted). 215 * 216 * @return The range, or <code>null</code> if the dataset is 217 * <code>null</code>. 218 */ 219 public Range findDomainBounds(XYDataset dataset) { 220 if (dataset != null) { 221 return DatasetUtilities.findDomainBounds(dataset, true); 222 } 223 else { 224 return null; 225 } 226 } 227 228 /** 229 * Returns the range required by this renderer to display all the range 230 * values in the specified dataset. 231 * 232 * @param dataset the dataset (<code>null</code> permitted). 233 * 234 * @return The range, or <code>null</code> if the dataset is 235 * <code>null</code>. 236 */ 237 public Range findRangeBounds(XYDataset dataset) { 238 if (dataset != null) { 239 return DatasetUtilities.findRangeBounds(dataset, true); 240 } 241 else { 242 return null; 243 } 244 } 245 246 /** 247 * Draws the visual representation for one data item. 248 * 249 * @param g2 the graphics output target. 250 * @param state the renderer state. 251 * @param dataArea the data area. 252 * @param info the plot rendering info. 253 * @param plot the plot. 254 * @param domainAxis the domain axis. 255 * @param rangeAxis the range axis. 256 * @param dataset the dataset. 257 * @param series the series index. 258 * @param item the item index. 259 * @param crosshairState the crosshair state. 260 * @param pass the pass index. 261 */ 262 public void drawItem(Graphics2D g2, XYItemRendererState state, 263 Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, 264 ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, 265 int series, int item, CrosshairState crosshairState, int pass) { 266 267 if (pass == 0 && dataset instanceof IntervalXYDataset 268 && getItemVisible(series, item)) { 269 IntervalXYDataset ixyd = (IntervalXYDataset) dataset; 270 PlotOrientation orientation = plot.getOrientation(); 271 if (this.drawXError) { 272 // draw the error bar for the x-interval 273 double x0 = ixyd.getStartXValue(series, item); 274 double x1 = ixyd.getEndXValue(series, item); 275 double y = ixyd.getYValue(series, item); 276 RectangleEdge edge = plot.getDomainAxisEdge(); 277 double xx0 = domainAxis.valueToJava2D(x0, dataArea, edge); 278 double xx1 = domainAxis.valueToJava2D(x1, dataArea, edge); 279 double yy = rangeAxis.valueToJava2D(y, dataArea, 280 plot.getRangeAxisEdge()); 281 Line2D line; 282 Line2D cap1 = null; 283 Line2D cap2 = null; 284 double adj = this.capLength / 2.0; 285 if (orientation == PlotOrientation.VERTICAL) { 286 line = new Line2D.Double(xx0, yy, xx1, yy); 287 cap1 = new Line2D.Double(xx0, yy - adj, xx0, yy + adj); 288 cap2 = new Line2D.Double(xx1, yy - adj, xx1, yy + adj); 289 } 290 else { // PlotOrientation.HORIZONTAL 291 line = new Line2D.Double(yy, xx0, yy, xx1); 292 cap1 = new Line2D.Double(yy - adj, xx0, yy + adj, xx0); 293 cap2 = new Line2D.Double(yy - adj, xx1, yy + adj, xx1); 294 } 295 g2.setStroke(new BasicStroke(1.0f)); 296 if (this.errorPaint != null) { 297 g2.setPaint(this.errorPaint); 298 } 299 else { 300 g2.setPaint(getItemPaint(series, item)); 301 } 302 g2.draw(line); 303 g2.draw(cap1); 304 g2.draw(cap2); 305 } 306 if (this.drawYError) { 307 // draw the error bar for the y-interval 308 double y0 = ixyd.getStartYValue(series, item); 309 double y1 = ixyd.getEndYValue(series, item); 310 double x = ixyd.getXValue(series, item); 311 RectangleEdge edge = plot.getRangeAxisEdge(); 312 double yy0 = rangeAxis.valueToJava2D(y0, dataArea, edge); 313 double yy1 = rangeAxis.valueToJava2D(y1, dataArea, edge); 314 double xx = domainAxis.valueToJava2D(x, dataArea, 315 plot.getDomainAxisEdge()); 316 Line2D line; 317 Line2D cap1 = null; 318 Line2D cap2 = null; 319 double adj = this.capLength / 2.0; 320 if (orientation == PlotOrientation.VERTICAL) { 321 line = new Line2D.Double(xx, yy0, xx, yy1); 322 cap1 = new Line2D.Double(xx - adj, yy0, xx + adj, yy0); 323 cap2 = new Line2D.Double(xx - adj, yy1, xx + adj, yy1); 324 } 325 else { // PlotOrientation.HORIZONTAL 326 line = new Line2D.Double(yy0, xx, yy1, xx); 327 cap1 = new Line2D.Double(yy0, xx - adj, yy0, xx + adj); 328 cap2 = new Line2D.Double(yy1, xx - adj, yy1, xx + adj); 329 } 330 g2.setStroke(new BasicStroke(1.0f)); 331 if (this.errorPaint != null) { 332 g2.setPaint(this.errorPaint); 333 } 334 else { 335 g2.setPaint(getItemPaint(series, item)); 336 } 337 g2.draw(line); 338 g2.draw(cap1); 339 g2.draw(cap2); 340 } 341 } 342 super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, 343 dataset, series, item, crosshairState, pass); 344 } 345 346 /** 347 * Tests this instance for equality with an arbitrary object. 348 * 349 * @param obj the object (<code>null</code> permitted). 350 * 351 * @return A boolean. 352 */ 353 public boolean equals(Object obj) { 354 if (obj == this) { 355 return true; 356 } 357 if (!(obj instanceof XYErrorRenderer)) { 358 return false; 359 } 360 XYErrorRenderer that = (XYErrorRenderer) obj; 361 if (this.drawXError != that.drawXError) { 362 return false; 363 } 364 if (this.drawYError != that.drawYError) { 365 return false; 366 } 367 if (this.capLength != that.capLength) { 368 return false; 369 } 370 if (!PaintUtilities.equal(this.errorPaint, that.errorPaint)) { 371 return false; 372 } 373 return super.equals(obj); 374 } 375 376 /** 377 * Provides serialization support. 378 * 379 * @param stream the input stream. 380 * 381 * @throws IOException if there is an I/O error. 382 * @throws ClassNotFoundException if there is a classpath problem. 383 */ 384 private void readObject(ObjectInputStream stream) 385 throws IOException, ClassNotFoundException { 386 stream.defaultReadObject(); 387 this.errorPaint = SerialUtilities.readPaint(stream); 388 } 389 390 /** 391 * Provides serialization support. 392 * 393 * @param stream the output stream. 394 * 395 * @throws IOException if there is an I/O error. 396 */ 397 private void writeObject(ObjectOutputStream stream) throws IOException { 398 stream.defaultWriteObject(); 399 SerialUtilities.writePaint(this.errorPaint, stream); 400 } 401 402 }