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