001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2009, 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 * XYStepRenderer.java 029 * ------------------- 030 * (C) Copyright 2002-2009, by Roger Studner and Contributors. 031 * 032 * Original Author: Roger Studner; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Matthias Rose; 035 * Gerald Struck (fix for bug 1569094); 036 * Ulrich Voigt (patch 1874890); 037 * Martin Hoeller (contribution to patch 1874890); 038 * 039 * Changes 040 * ------- 041 * 13-May-2002 : Version 1, contributed by Roger Studner (DG); 042 * 25-Jun-2002 : Updated import statements (DG); 043 * 22-Jul-2002 : Added check for null data items (DG); 044 * 25-Mar-2003 : Implemented Serializable (DG); 045 * 01-May-2003 : Modified drawItem() method signature (DG); 046 * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG); 047 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 048 * 28-Oct-2003 : Added tooltips, code contributed by Matthias Rose 049 * (RFE 824857) (DG); 050 * 10-Feb-2004 : Removed working line (use line from state object instead) (DG); 051 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState. Renamed 052 * XYToolTipGenerator --> XYItemLabelGenerator (DG); 053 * 19-Jan-2005 : Now accesses only primitives from dataset (DG); 054 * 15-Mar-2005 : Fix silly bug in drawItem() method (DG); 055 * 19-Sep-2005 : Extend XYLineAndShapeRenderer (fixes legend shapes), added 056 * support for series visibility, and use getDefaultEntityRadius() 057 * for entity hotspot size (DG); 058 * ------------- JFREECHART 1.0.x --------------------------------------------- 059 * 15-Jun-2006 : Added basic support for item labels (DG); 060 * 11-Oct-2006 : Fixed rendering with horizontal orientation (see bug 1569094), 061 * thanks to Gerald Struck (DG); 062 * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG); 063 * 14-Feb-2008 : Applied patch 1874890 by Ulrich Voigt (with contribution from 064 * Martin Hoeller) (DG); 065 * 14-May-2008 : Call addEntity() in drawItem() (DG); 066 * 24-Sep-2008 : Fixed bug 2113627 by utilising second pass to draw item 067 * labels (DG); 068 * 069 */ 070 071 package org.jfree.chart.renderer.xy; 072 073 import java.awt.Graphics2D; 074 import java.awt.Paint; 075 import java.awt.Stroke; 076 import java.awt.geom.Line2D; 077 import java.awt.geom.Rectangle2D; 078 import java.io.Serializable; 079 080 import org.jfree.chart.HashUtilities; 081 import org.jfree.chart.axis.ValueAxis; 082 import org.jfree.chart.entity.EntityCollection; 083 import org.jfree.chart.event.RendererChangeEvent; 084 import org.jfree.chart.labels.XYToolTipGenerator; 085 import org.jfree.chart.plot.CrosshairState; 086 import org.jfree.chart.plot.PlotOrientation; 087 import org.jfree.chart.plot.PlotRenderingInfo; 088 import org.jfree.chart.plot.XYPlot; 089 import org.jfree.chart.urls.XYURLGenerator; 090 import org.jfree.data.xy.XYDataset; 091 import org.jfree.ui.RectangleEdge; 092 import org.jfree.util.PublicCloneable; 093 094 /** 095 * Line/Step item renderer for an {@link XYPlot}. This class draws lines 096 * between data points, only allowing horizontal or vertical lines (steps). 097 * The example shown here is generated by the 098 * <code>XYStepRendererDemo1.java</code> program included in the JFreeChart 099 * demo collection: 100 * <br><br> 101 * <img src="../../../../../images/XYStepRendererSample.png" 102 * alt="XYStepRendererSample.png" /> 103 */ 104 public class XYStepRenderer extends XYLineAndShapeRenderer 105 implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { 106 107 /** For serialization. */ 108 private static final long serialVersionUID = -8918141928884796108L; 109 110 /** 111 * The factor (from 0.0 to 1.0) that determines the position of the 112 * step. 113 * 114 * @since 1.0.10. 115 */ 116 private double stepPoint = 1.0d; 117 118 /** 119 * Constructs a new renderer with no tooltip or URL generation. 120 */ 121 public XYStepRenderer() { 122 this(null, null); 123 } 124 125 /** 126 * Constructs a new renderer with the specified tool tip and URL 127 * generators. 128 * 129 * @param toolTipGenerator the item label generator (<code>null</code> 130 * permitted). 131 * @param urlGenerator the URL generator (<code>null</code> permitted). 132 */ 133 public XYStepRenderer(XYToolTipGenerator toolTipGenerator, 134 XYURLGenerator urlGenerator) { 135 super(); 136 setBaseToolTipGenerator(toolTipGenerator); 137 setURLGenerator(urlGenerator); 138 setBaseShapesVisible(false); 139 } 140 141 /** 142 * Returns the fraction of the domain position between two points on which 143 * the step is drawn. The default is 1.0d, which means the step is drawn 144 * at the domain position of the second`point. If the stepPoint is 0.5d the 145 * step is drawn at half between the two points. 146 * 147 * @return The fraction of the domain position between two points where the 148 * step is drawn. 149 * 150 * @see #setStepPoint(double) 151 * 152 * @since 1.0.10 153 */ 154 public double getStepPoint() { 155 return this.stepPoint; 156 } 157 158 /** 159 * Sets the step point and sends a {@link RendererChangeEvent} to all 160 * registered listeners. 161 * 162 * @param stepPoint the step point (in the range 0.0 to 1.0) 163 * 164 * @see #getStepPoint() 165 * 166 * @since 1.0.10 167 */ 168 public void setStepPoint(double stepPoint) { 169 if (stepPoint < 0.0d || stepPoint > 1.0d) { 170 throw new IllegalArgumentException( 171 "Requires stepPoint in [0.0;1.0]"); 172 } 173 this.stepPoint = stepPoint; 174 fireChangeEvent(); 175 } 176 177 /** 178 * Draws the visual representation of a single data item. 179 * 180 * @param g2 the graphics device. 181 * @param state the renderer state. 182 * @param dataArea the area within which the data is being drawn. 183 * @param info collects information about the drawing. 184 * @param plot the plot (can be used to obtain standard color 185 * information etc). 186 * @param domainAxis the domain axis. 187 * @param rangeAxis the vertical axis. 188 * @param dataset the dataset. 189 * @param series the series index (zero-based). 190 * @param item the item index (zero-based). 191 * @param crosshairState crosshair information for the plot 192 * (<code>null</code> permitted). 193 * @param pass the pass index. 194 */ 195 public void drawItem(Graphics2D g2, 196 XYItemRendererState state, 197 Rectangle2D dataArea, 198 PlotRenderingInfo info, 199 XYPlot plot, 200 ValueAxis domainAxis, 201 ValueAxis rangeAxis, 202 XYDataset dataset, 203 int series, 204 int item, 205 CrosshairState crosshairState, 206 int pass) { 207 208 // do nothing if item is not visible 209 if (!getItemVisible(series, item)) { 210 return; 211 } 212 213 PlotOrientation orientation = plot.getOrientation(); 214 215 Paint seriesPaint = getItemPaint(series, item); 216 Stroke seriesStroke = getItemStroke(series, item); 217 g2.setPaint(seriesPaint); 218 g2.setStroke(seriesStroke); 219 220 // get the data point... 221 double x1 = dataset.getXValue(series, item); 222 double y1 = dataset.getYValue(series, item); 223 224 RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 225 RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); 226 double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); 227 double transY1 = (Double.isNaN(y1) ? Double.NaN 228 : rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation)); 229 230 if (pass == 0 && item > 0) { 231 // get the previous data point... 232 double x0 = dataset.getXValue(series, item - 1); 233 double y0 = dataset.getYValue(series, item - 1); 234 double transX0 = domainAxis.valueToJava2D(x0, dataArea, 235 xAxisLocation); 236 double transY0 = (Double.isNaN(y0) ? Double.NaN 237 : rangeAxis.valueToJava2D(y0, dataArea, yAxisLocation)); 238 239 if (orientation == PlotOrientation.HORIZONTAL) { 240 if (transY0 == transY1) { 241 // this represents the situation 242 // for drawing a horizontal bar. 243 drawLine(g2, state.workingLine, transY0, transX0, transY1, 244 transX1); 245 } 246 else { //this handles the need to perform a 'step'. 247 248 // calculate the step point 249 double transXs = transX0 + (getStepPoint() 250 * (transX1 - transX0)); 251 drawLine(g2, state.workingLine, transY0, transX0, transY0, 252 transXs); 253 drawLine(g2, state.workingLine, transY0, transXs, transY1, 254 transXs); 255 drawLine(g2, state.workingLine, transY1, transXs, transY1, 256 transX1); 257 } 258 } 259 else if (orientation == PlotOrientation.VERTICAL) { 260 if (transY0 == transY1) { // this represents the situation 261 // for drawing a horizontal bar. 262 drawLine(g2, state.workingLine, transX0, transY0, transX1, 263 transY1); 264 } 265 else { //this handles the need to perform a 'step'. 266 // calculate the step point 267 double transXs = transX0 + (getStepPoint() 268 * (transX1 - transX0)); 269 drawLine(g2, state.workingLine, transX0, transY0, transXs, 270 transY0); 271 drawLine(g2, state.workingLine, transXs, transY0, transXs, 272 transY1); 273 drawLine(g2, state.workingLine, transXs, transY1, transX1, 274 transY1); 275 } 276 } 277 278 // submit this data item as a candidate for the crosshair point 279 int domainAxisIndex = plot.getDomainAxisIndex(domainAxis); 280 int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis); 281 updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex, 282 rangeAxisIndex, transX1, transY1, orientation); 283 284 // collect entity and tool tip information... 285 EntityCollection entities = state.getEntityCollection(); 286 if (entities != null) { 287 addEntity(entities, null, dataset, series, item, transX1, 288 transY1); 289 } 290 291 } 292 293 if (pass == 1) { 294 // draw the item label if there is one... 295 if (isItemLabelVisible(series, item)) { 296 double xx = transX1; 297 double yy = transY1; 298 if (orientation == PlotOrientation.HORIZONTAL) { 299 xx = transY1; 300 yy = transX1; 301 } 302 drawItemLabel(g2, orientation, dataset, series, item, xx, yy, 303 (y1 < 0.0)); 304 } 305 } 306 } 307 308 /** 309 * A utility method that draws a line but only if none of the coordinates 310 * are NaN values. 311 * 312 * @param g2 the graphics target. 313 * @param line the line object. 314 * @param x0 the x-coordinate for the starting point of the line. 315 * @param y0 the y-coordinate for the starting point of the line. 316 * @param x1 the x-coordinate for the ending point of the line. 317 * @param y1 the y-coordinate for the ending point of the line. 318 */ 319 private void drawLine(Graphics2D g2, Line2D line, double x0, double y0, 320 double x1, double y1) { 321 if (Double.isNaN(x0) || Double.isNaN(x1) || Double.isNaN(y0) 322 || Double.isNaN(y1)) { 323 return; 324 } 325 line.setLine(x0, y0, x1, y1); 326 g2.draw(line); 327 } 328 329 /** 330 * Tests this renderer for equality with an arbitrary object. 331 * 332 * @param obj the object (<code>null</code> permitted). 333 * 334 * @return A boolean. 335 */ 336 public boolean equals(Object obj) { 337 if (obj == this) { 338 return true; 339 } 340 if (!(obj instanceof XYLineAndShapeRenderer)) { 341 return false; 342 } 343 XYStepRenderer that = (XYStepRenderer) obj; 344 if (this.stepPoint != that.stepPoint) { 345 return false; 346 } 347 return super.equals(obj); 348 } 349 350 /** 351 * Returns a hash code for this instance. 352 * 353 * @return A hash code. 354 */ 355 public int hashCode() { 356 return HashUtilities.hashCode(super.hashCode(), this.stepPoint); 357 } 358 359 /** 360 * Returns a clone of the renderer. 361 * 362 * @return A clone. 363 * 364 * @throws CloneNotSupportedException if the renderer cannot be cloned. 365 */ 366 public Object clone() throws CloneNotSupportedException { 367 return super.clone(); 368 } 369 370 }