Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jfreechart/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * ------------------ 28: * XYDotRenderer.java 29: * ------------------ 30: * (C) Copyright 2002-2007, by Object Refinery Limited. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): Christian W. Zuckschwerdt; 34: * 35: * Changes (from 29-Oct-2002) 36: * -------------------------- 37: * 29-Oct-2002 : Added standard header (DG); 38: * 25-Mar-2003 : Implemented Serializable (DG); 39: * 01-May-2003 : Modified drawItem() method signature (DG); 40: * 30-Jul-2003 : Modified entity constructor (CZ); 41: * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG); 42: * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 43: * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG); 44: * 19-Jan-2005 : Now uses only primitives from dataset (DG); 45: * ------------- JFREECHART 1.0.x --------------------------------------------- 46: * 10-Jul-2006 : Added dotWidth and dotHeight attributes (DG); 47: * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG); 48: * 09-Nov-2007 : Added legend shape attribute, plus override for 49: * getLegendItem() (DG); 50: * 51: */ 52: 53: package org.jfree.chart.renderer.xy; 54: 55: import java.awt.Graphics2D; 56: import java.awt.Paint; 57: import java.awt.Shape; 58: import java.awt.geom.Rectangle2D; 59: import java.io.IOException; 60: import java.io.ObjectInputStream; 61: import java.io.ObjectOutputStream; 62: import java.io.Serializable; 63: 64: import org.jfree.chart.LegendItem; 65: import org.jfree.chart.axis.ValueAxis; 66: import org.jfree.chart.event.RendererChangeEvent; 67: import org.jfree.chart.plot.CrosshairState; 68: import org.jfree.chart.plot.PlotOrientation; 69: import org.jfree.chart.plot.PlotRenderingInfo; 70: import org.jfree.chart.plot.XYPlot; 71: import org.jfree.data.xy.XYDataset; 72: import org.jfree.io.SerialUtilities; 73: import org.jfree.ui.RectangleEdge; 74: import org.jfree.util.PublicCloneable; 75: import org.jfree.util.ShapeUtilities; 76: 77: /** 78: * A renderer that draws a small dot at each data point for an {@link XYPlot}. 79: */ 80: public class XYDotRenderer extends AbstractXYItemRenderer 81: implements XYItemRenderer, 82: Cloneable, 83: PublicCloneable, 84: Serializable { 85: 86: /** For serialization. */ 87: private static final long serialVersionUID = -2764344339073566425L; 88: 89: /** The dot width. */ 90: private int dotWidth; 91: 92: /** The dot height. */ 93: private int dotHeight; 94: 95: /** 96: * The shape that is used to represent an item in the legend. 97: * 98: * @since 1.0.7 99: */ 100: private transient Shape legendShape; 101: 102: /** 103: * Constructs a new renderer. 104: */ 105: public XYDotRenderer() { 106: super(); 107: this.dotWidth = 1; 108: this.dotHeight = 1; 109: this.legendShape = new Rectangle2D.Double(-3.0, -3.0, 6.0, 6.0); 110: } 111: 112: /** 113: * Returns the dot width (the default value is 1). 114: * 115: * @return The dot width. 116: * 117: * @since 1.0.2 118: * @see #setDotWidth(int) 119: */ 120: public int getDotWidth() { 121: return this.dotWidth; 122: } 123: 124: /** 125: * Sets the dot width and sends a {@link RendererChangeEvent} to all 126: * registered listeners. 127: * 128: * @param w the new width (must be greater than zero). 129: * 130: * @throws IllegalArgumentException if <code>w</code> is less than one. 131: * 132: * @since 1.0.2 133: * @see #getDotWidth() 134: */ 135: public void setDotWidth(int w) { 136: if (w < 1) { 137: throw new IllegalArgumentException("Requires w > 0."); 138: } 139: this.dotWidth = w; 140: notifyListeners(new RendererChangeEvent(this)); 141: } 142: 143: /** 144: * Returns the dot height (the default value is 1). 145: * 146: * @return The dot height. 147: * 148: * @since 1.0.2 149: * @see #setDotHeight(int) 150: */ 151: public int getDotHeight() { 152: return this.dotHeight; 153: } 154: 155: /** 156: * Sets the dot height and sends a {@link RendererChangeEvent} to all 157: * registered listeners. 158: * 159: * @param h the new height (must be greater than zero). 160: * 161: * @throws IllegalArgumentException if <code>h</code> is less than one. 162: * 163: * @since 1.0.2 164: * @see #getDotHeight() 165: */ 166: public void setDotHeight(int h) { 167: if (h < 1) { 168: throw new IllegalArgumentException("Requires h > 0."); 169: } 170: this.dotHeight = h; 171: notifyListeners(new RendererChangeEvent(this)); 172: } 173: 174: /** 175: * Returns the shape used to represent an item in the legend. 176: * 177: * @return The legend shape (never <code>null</code>). 178: * 179: * @see #setLegendShape(Shape) 180: * 181: * @since 1.0.7 182: */ 183: public Shape getLegendShape() { 184: return this.legendShape; 185: } 186: 187: /** 188: * Sets the shape used as a line in each legend item and sends a 189: * {@link RendererChangeEvent} to all registered listeners. 190: * 191: * @param shape the shape (<code>null</code> not permitted). 192: * 193: * @see #getLegendShape() 194: * 195: * @since 1.0.7 196: */ 197: public void setLegendShape(Shape shape) { 198: if (shape == null) { 199: throw new IllegalArgumentException("Null 'shape' argument."); 200: } 201: this.legendShape = shape; 202: notifyListeners(new RendererChangeEvent(this)); 203: } 204: 205: /** 206: * Draws the visual representation of a single data item. 207: * 208: * @param g2 the graphics device. 209: * @param state the renderer state. 210: * @param dataArea the area within which the data is being drawn. 211: * @param info collects information about the drawing. 212: * @param plot the plot (can be used to obtain standard color 213: * information etc). 214: * @param domainAxis the domain (horizontal) axis. 215: * @param rangeAxis the range (vertical) axis. 216: * @param dataset the dataset. 217: * @param series the series index (zero-based). 218: * @param item the item index (zero-based). 219: * @param crosshairState crosshair information for the plot 220: * (<code>null</code> permitted). 221: * @param pass the pass index. 222: */ 223: public void drawItem(Graphics2D g2, 224: XYItemRendererState state, 225: Rectangle2D dataArea, 226: PlotRenderingInfo info, 227: XYPlot plot, 228: ValueAxis domainAxis, 229: ValueAxis rangeAxis, 230: XYDataset dataset, 231: int series, 232: int item, 233: CrosshairState crosshairState, 234: int pass) { 235: 236: // get the data point... 237: double x = dataset.getXValue(series, item); 238: double y = dataset.getYValue(series, item); 239: double adjx = (this.dotWidth - 1) / 2.0; 240: double adjy = (this.dotHeight - 1) / 2.0; 241: if (!Double.isNaN(y)) { 242: RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 243: RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); 244: double transX = domainAxis.valueToJava2D(x, dataArea, 245: xAxisLocation) - adjx; 246: double transY = rangeAxis.valueToJava2D(y, dataArea, yAxisLocation) 247: - adjy; 248: 249: g2.setPaint(getItemPaint(series, item)); 250: PlotOrientation orientation = plot.getOrientation(); 251: if (orientation == PlotOrientation.HORIZONTAL) { 252: g2.fillRect((int) transY, (int) transX, this.dotHeight, 253: this.dotWidth); 254: } 255: else if (orientation == PlotOrientation.VERTICAL) { 256: g2.fillRect((int) transX, (int) transY, this.dotWidth, 257: this.dotHeight); 258: } 259: 260: int domainAxisIndex = plot.getDomainAxisIndex(domainAxis); 261: int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis); 262: updateCrosshairValues(crosshairState, x, y, domainAxisIndex, 263: rangeAxisIndex, transX, transY, orientation); 264: } 265: 266: } 267: 268: /** 269: * Returns a legend item for the specified series. 270: * 271: * @param datasetIndex the dataset index (zero-based). 272: * @param series the series index (zero-based). 273: * 274: * @return A legend item for the series (possibly <code>null</code>). 275: */ 276: public LegendItem getLegendItem(int datasetIndex, int series) { 277: 278: // if the renderer isn't assigned to a plot, then we don't have a 279: // dataset... 280: XYPlot plot = getPlot(); 281: if (plot == null) { 282: return null; 283: } 284: 285: XYDataset dataset = plot.getDataset(datasetIndex); 286: if (dataset == null) { 287: return null; 288: } 289: 290: LegendItem result = null; 291: if (getItemVisible(series, 0)) { 292: String label = getLegendItemLabelGenerator().generateLabel(dataset, 293: series); 294: String description = label; 295: String toolTipText = null; 296: if (getLegendItemToolTipGenerator() != null) { 297: toolTipText = getLegendItemToolTipGenerator().generateLabel( 298: dataset, series); 299: } 300: String urlText = null; 301: if (getLegendItemURLGenerator() != null) { 302: urlText = getLegendItemURLGenerator().generateLabel( 303: dataset, series); 304: } 305: Paint fillPaint = lookupSeriesPaint(series); 306: result = new LegendItem(label, description, toolTipText, urlText, 307: getLegendShape(), fillPaint); 308: result.setSeriesKey(dataset.getSeriesKey(series)); 309: result.setSeriesIndex(series); 310: result.setDataset(dataset); 311: result.setDatasetIndex(datasetIndex); 312: } 313: 314: return result; 315: 316: } 317: 318: /** 319: * Tests this renderer for equality with an arbitrary object. This method 320: * returns <code>true</code> if and only if: 321: * 322: * <ul> 323: * <li><code>obj</code> is not <code>null</code>;</li> 324: * <li><code>obj</code> is an instance of <code>XYDotRenderer</code>;</li> 325: * <li>both renderers have the same attribute values. 326: * </ul> 327: * 328: * @param obj the object (<code>null</code> permitted). 329: * 330: * @return A boolean. 331: */ 332: public boolean equals(Object obj) { 333: if (obj == this) { 334: return true; 335: } 336: if (!(obj instanceof XYDotRenderer)) { 337: return false; 338: } 339: XYDotRenderer that = (XYDotRenderer) obj; 340: if (this.dotWidth != that.dotWidth) { 341: return false; 342: } 343: if (this.dotHeight != that.dotHeight) { 344: return false; 345: } 346: if (!ShapeUtilities.equal(this.legendShape, that.legendShape)) { 347: return false; 348: } 349: return super.equals(obj); 350: } 351: 352: /** 353: * Returns a clone of the renderer. 354: * 355: * @return A clone. 356: * 357: * @throws CloneNotSupportedException if the renderer cannot be cloned. 358: */ 359: public Object clone() throws CloneNotSupportedException { 360: return super.clone(); 361: } 362: 363: /** 364: * Provides serialization support. 365: * 366: * @param stream the input stream. 367: * 368: * @throws IOException if there is an I/O error. 369: * @throws ClassNotFoundException if there is a classpath problem. 370: */ 371: private void readObject(ObjectInputStream stream) 372: throws IOException, ClassNotFoundException { 373: stream.defaultReadObject(); 374: this.legendShape = SerialUtilities.readShape(stream); 375: } 376: 377: /** 378: * Provides serialization support. 379: * 380: * @param stream the output stream. 381: * 382: * @throws IOException if there is an I/O error. 383: */ 384: private void writeObject(ObjectOutputStream stream) throws IOException { 385: stream.defaultWriteObject(); 386: SerialUtilities.writeShape(this.legendShape, stream); 387: } 388: 389: }