001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ------------------ 028 * XYDotRenderer.java 029 * ------------------ 030 * (C) Copyright 2002-present, by David Gilbert. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): Christian W. Zuckschwerdt; 034 * 035 */ 036 037package org.jfree.chart.renderer.xy; 038 039import java.awt.Graphics2D; 040import java.awt.Paint; 041import java.awt.Shape; 042import java.awt.geom.Rectangle2D; 043import java.io.IOException; 044import java.io.ObjectInputStream; 045import java.io.ObjectOutputStream; 046 047import org.jfree.chart.LegendItem; 048import org.jfree.chart.axis.ValueAxis; 049import org.jfree.chart.event.RendererChangeEvent; 050import org.jfree.chart.plot.CrosshairState; 051import org.jfree.chart.plot.PlotOrientation; 052import org.jfree.chart.plot.PlotRenderingInfo; 053import org.jfree.chart.plot.XYPlot; 054import org.jfree.chart.ui.RectangleEdge; 055import org.jfree.chart.util.Args; 056import org.jfree.chart.util.PublicCloneable; 057import org.jfree.chart.util.SerialUtils; 058import org.jfree.chart.util.ShapeUtils; 059import org.jfree.data.xy.XYDataset; 060 061/** 062 * A renderer that draws a small dot at each data point for an {@link XYPlot}. 063 * The example shown here is generated by the 064 * {@code ScatterPlotDemo4.java} program included in the JFreeChart 065 * demo collection: 066 * <br><br> 067 * <img src="doc-files/XYDotRendererSample.png" alt="XYDotRendererSample.png"> 068 */ 069public class XYDotRenderer extends AbstractXYItemRenderer 070 implements XYItemRenderer, PublicCloneable { 071 072 /** For serialization. */ 073 private static final long serialVersionUID = -2764344339073566425L; 074 075 /** The dot width. */ 076 private int dotWidth; 077 078 /** The dot height. */ 079 private int dotHeight; 080 081 /** 082 * The shape that is used to represent an item in the legend. 083 */ 084 private transient Shape legendShape; 085 086 /** 087 * Constructs a new renderer. 088 */ 089 public XYDotRenderer() { 090 super(); 091 this.dotWidth = 1; 092 this.dotHeight = 1; 093 this.legendShape = new Rectangle2D.Double(-3.0, -3.0, 6.0, 6.0); 094 } 095 096 /** 097 * Returns the dot width (the default value is 1). 098 * 099 * @return The dot width. 100 * 101 * @see #setDotWidth(int) 102 */ 103 public int getDotWidth() { 104 return this.dotWidth; 105 } 106 107 /** 108 * Sets the dot width and sends a {@link RendererChangeEvent} to all 109 * registered listeners. 110 * 111 * @param w the new width (must be greater than zero). 112 * 113 * @throws IllegalArgumentException if {@code w} is less than one. 114 * 115 * @see #getDotWidth() 116 */ 117 public void setDotWidth(int w) { 118 if (w < 1) { 119 throw new IllegalArgumentException("Requires w > 0."); 120 } 121 this.dotWidth = w; 122 fireChangeEvent(); 123 } 124 125 /** 126 * Returns the dot height (the default value is 1). 127 * 128 * @return The dot height. 129 * 130 * @see #setDotHeight(int) 131 */ 132 public int getDotHeight() { 133 return this.dotHeight; 134 } 135 136 /** 137 * Sets the dot height and sends a {@link RendererChangeEvent} to all 138 * registered listeners. 139 * 140 * @param h the new height (must be greater than zero). 141 * 142 * @throws IllegalArgumentException if {@code h} is less than one. 143 * 144 * @see #getDotHeight() 145 */ 146 public void setDotHeight(int h) { 147 if (h < 1) { 148 throw new IllegalArgumentException("Requires h > 0."); 149 } 150 this.dotHeight = h; 151 fireChangeEvent(); 152 } 153 154 /** 155 * Returns the shape used to represent an item in the legend. 156 * 157 * @return The legend shape (never {@code null}). 158 * 159 * @see #setLegendShape(Shape) 160 */ 161 public Shape getLegendShape() { 162 return this.legendShape; 163 } 164 165 /** 166 * Sets the shape used as a line in each legend item and sends a 167 * {@link RendererChangeEvent} to all registered listeners. 168 * 169 * @param shape the shape ({@code null} not permitted). 170 * 171 * @see #getLegendShape() 172 */ 173 public void setLegendShape(Shape shape) { 174 Args.nullNotPermitted(shape, "shape"); 175 this.legendShape = shape; 176 fireChangeEvent(); 177 } 178 179 /** 180 * Draws the visual representation of a single data item. 181 * 182 * @param g2 the graphics device. 183 * @param state the renderer state. 184 * @param dataArea the area within which the data is being drawn. 185 * @param info collects information about the drawing. 186 * @param plot the plot (can be used to obtain standard color 187 * information etc). 188 * @param domainAxis the domain (horizontal) axis. 189 * @param rangeAxis the range (vertical) axis. 190 * @param dataset the dataset. 191 * @param series the series index (zero-based). 192 * @param item the item index (zero-based). 193 * @param crosshairState crosshair information for the plot 194 * ({@code null} permitted). 195 * @param pass the pass index. 196 */ 197 @Override 198 public void drawItem(Graphics2D g2, XYItemRendererState state, 199 Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, 200 ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, 201 int series, int item, CrosshairState crosshairState, int pass) { 202 203 // do nothing if item is not visible 204 if (!getItemVisible(series, item)) { 205 return; 206 } 207 208 // get the data point... 209 double x = dataset.getXValue(series, item); 210 double y = dataset.getYValue(series, item); 211 double adjx = (this.dotWidth - 1) / 2.0; 212 double adjy = (this.dotHeight - 1) / 2.0; 213 if (!Double.isNaN(y)) { 214 RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 215 RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); 216 double transX = domainAxis.valueToJava2D(x, dataArea, 217 xAxisLocation) - adjx; 218 double transY = rangeAxis.valueToJava2D(y, dataArea, yAxisLocation) 219 - adjy; 220 221 g2.setPaint(getItemPaint(series, item)); 222 PlotOrientation orientation = plot.getOrientation(); 223 if (orientation == PlotOrientation.HORIZONTAL) { 224 g2.fillRect((int) transY, (int) transX, this.dotHeight, 225 this.dotWidth); 226 } 227 else if (orientation == PlotOrientation.VERTICAL) { 228 g2.fillRect((int) transX, (int) transY, this.dotWidth, 229 this.dotHeight); 230 } 231 232 int datasetIndex = plot.indexOf(dataset); 233 updateCrosshairValues(crosshairState, x, y, datasetIndex, 234 transX, transY, orientation); 235 } 236 237 } 238 239 /** 240 * Returns a legend item for the specified series. 241 * 242 * @param datasetIndex the dataset index (zero-based). 243 * @param series the series index (zero-based). 244 * 245 * @return A legend item for the series (possibly {@code null}). 246 */ 247 @Override 248 public LegendItem getLegendItem(int datasetIndex, int series) { 249 250 // if the renderer isn't assigned to a plot, then we don't have a 251 // dataset... 252 XYPlot plot = getPlot(); 253 if (plot == null) { 254 return null; 255 } 256 257 XYDataset dataset = plot.getDataset(datasetIndex); 258 if (dataset == null) { 259 return null; 260 } 261 262 LegendItem result = null; 263 if (getItemVisible(series, 0)) { 264 String label = getLegendItemLabelGenerator().generateLabel(dataset, 265 series); 266 String description = label; 267 String toolTipText = null; 268 if (getLegendItemToolTipGenerator() != null) { 269 toolTipText = getLegendItemToolTipGenerator().generateLabel( 270 dataset, series); 271 } 272 String urlText = null; 273 if (getLegendItemURLGenerator() != null) { 274 urlText = getLegendItemURLGenerator().generateLabel( 275 dataset, series); 276 } 277 Paint fillPaint = lookupSeriesPaint(series); 278 result = new LegendItem(label, description, toolTipText, urlText, 279 getLegendShape(), fillPaint); 280 result.setLabelFont(lookupLegendTextFont(series)); 281 Paint labelPaint = lookupLegendTextPaint(series); 282 if (labelPaint != null) { 283 result.setLabelPaint(labelPaint); 284 } 285 result.setSeriesKey(dataset.getSeriesKey(series)); 286 result.setSeriesIndex(series); 287 result.setDataset(dataset); 288 result.setDatasetIndex(datasetIndex); 289 } 290 291 return result; 292 293 } 294 295 /** 296 * Tests this renderer for equality with an arbitrary object. This method 297 * returns {@code true} if and only if: 298 * 299 * <ul> 300 * <li>{@code obj} is not {@code null};</li> 301 * <li>{@code obj} is an instance of {@code XYDotRenderer};</li> 302 * <li>both renderers have the same attribute values. 303 * </ul> 304 * 305 * @param obj the object ({@code null} permitted). 306 * 307 * @return A boolean. 308 */ 309 @Override 310 public boolean equals(Object obj) { 311 if (obj == this) { 312 return true; 313 } 314 if (!(obj instanceof XYDotRenderer)) { 315 return false; 316 } 317 XYDotRenderer that = (XYDotRenderer) obj; 318 if (this.dotWidth != that.dotWidth) { 319 return false; 320 } 321 if (this.dotHeight != that.dotHeight) { 322 return false; 323 } 324 if (!ShapeUtils.equal(this.legendShape, that.legendShape)) { 325 return false; 326 } 327 return super.equals(obj); 328 } 329 330 /** 331 * Returns a clone of the renderer. 332 * 333 * @return A clone. 334 * 335 * @throws CloneNotSupportedException if the renderer cannot be cloned. 336 */ 337 @Override 338 public Object clone() throws CloneNotSupportedException { 339 return super.clone(); 340 } 341 342 /** 343 * Provides serialization support. 344 * 345 * @param stream the input stream. 346 * 347 * @throws IOException if there is an I/O error. 348 * @throws ClassNotFoundException if there is a classpath problem. 349 */ 350 private void readObject(ObjectInputStream stream) 351 throws IOException, ClassNotFoundException { 352 stream.defaultReadObject(); 353 this.legendShape = SerialUtils.readShape(stream); 354 } 355 356 /** 357 * Provides serialization support. 358 * 359 * @param stream the output stream. 360 * 361 * @throws IOException if there is an I/O error. 362 */ 363 private void writeObject(ObjectOutputStream stream) throws IOException { 364 stream.defaultWriteObject(); 365 SerialUtils.writeShape(this.legendShape, stream); 366 } 367 368}