001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2014, 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 * [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 * XYStepRenderer.java 029 * ------------------- 030 * (C) Copyright 2002-2014, 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 071package org.jfree.chart.renderer.xy; 072 073import java.awt.Graphics2D; 074import java.awt.Paint; 075import java.awt.Stroke; 076import java.awt.geom.Line2D; 077import java.awt.geom.Rectangle2D; 078import java.io.Serializable; 079 080import org.jfree.chart.HashUtilities; 081import org.jfree.chart.axis.ValueAxis; 082import org.jfree.chart.entity.EntityCollection; 083import org.jfree.chart.event.RendererChangeEvent; 084import org.jfree.chart.labels.XYToolTipGenerator; 085import org.jfree.chart.plot.CrosshairState; 086import org.jfree.chart.plot.PlotOrientation; 087import org.jfree.chart.plot.PlotRenderingInfo; 088import org.jfree.chart.plot.XYPlot; 089import org.jfree.chart.urls.XYURLGenerator; 090import org.jfree.data.xy.XYDataset; 091import org.jfree.ui.RectangleEdge; 092import 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 */ 104public 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 @Override 196 public void drawItem(Graphics2D g2, XYItemRendererState state, 197 Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, 198 ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, 199 int series, int item, CrosshairState crosshairState, int pass) { 200 201 // do nothing if item is not visible 202 if (!getItemVisible(series, item)) { 203 return; 204 } 205 206 PlotOrientation orientation = plot.getOrientation(); 207 208 Paint seriesPaint = getItemPaint(series, item); 209 Stroke seriesStroke = getItemStroke(series, item); 210 g2.setPaint(seriesPaint); 211 g2.setStroke(seriesStroke); 212 213 // get the data point... 214 double x1 = dataset.getXValue(series, item); 215 double y1 = dataset.getYValue(series, item); 216 217 RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 218 RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); 219 double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); 220 double transY1 = (Double.isNaN(y1) ? Double.NaN 221 : rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation)); 222 223 if (pass == 0 && item > 0) { 224 // get the previous data point... 225 double x0 = dataset.getXValue(series, item - 1); 226 double y0 = dataset.getYValue(series, item - 1); 227 double transX0 = domainAxis.valueToJava2D(x0, dataArea, 228 xAxisLocation); 229 double transY0 = (Double.isNaN(y0) ? Double.NaN 230 : rangeAxis.valueToJava2D(y0, dataArea, yAxisLocation)); 231 232 if (orientation == PlotOrientation.HORIZONTAL) { 233 if (transY0 == transY1) { 234 // this represents the situation 235 // for drawing a horizontal bar. 236 drawLine(g2, state.workingLine, transY0, transX0, transY1, 237 transX1); 238 } 239 else { //this handles the need to perform a 'step'. 240 241 // calculate the step point 242 double transXs = transX0 + (getStepPoint() 243 * (transX1 - transX0)); 244 drawLine(g2, state.workingLine, transY0, transX0, transY0, 245 transXs); 246 drawLine(g2, state.workingLine, transY0, transXs, transY1, 247 transXs); 248 drawLine(g2, state.workingLine, transY1, transXs, transY1, 249 transX1); 250 } 251 } 252 else if (orientation == PlotOrientation.VERTICAL) { 253 if (transY0 == transY1) { // this represents the situation 254 // for drawing a horizontal bar. 255 drawLine(g2, state.workingLine, transX0, transY0, transX1, 256 transY1); 257 } 258 else { //this handles the need to perform a 'step'. 259 // calculate the step point 260 double transXs = transX0 + (getStepPoint() 261 * (transX1 - transX0)); 262 drawLine(g2, state.workingLine, transX0, transY0, transXs, 263 transY0); 264 drawLine(g2, state.workingLine, transXs, transY0, transXs, 265 transY1); 266 drawLine(g2, state.workingLine, transXs, transY1, transX1, 267 transY1); 268 } 269 } 270 271 // submit this data item as a candidate for the crosshair point 272 int domainAxisIndex = plot.getDomainAxisIndex(domainAxis); 273 int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis); 274 updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex, 275 rangeAxisIndex, transX1, transY1, orientation); 276 277 // collect entity and tool tip information... 278 EntityCollection entities = state.getEntityCollection(); 279 if (entities != null) { 280 addEntity(entities, null, dataset, series, item, transX1, 281 transY1); 282 } 283 284 } 285 286 if (pass == 1) { 287 // draw the item label if there is one... 288 if (isItemLabelVisible(series, item)) { 289 double xx = transX1; 290 double yy = transY1; 291 if (orientation == PlotOrientation.HORIZONTAL) { 292 xx = transY1; 293 yy = transX1; 294 } 295 drawItemLabel(g2, orientation, dataset, series, item, xx, yy, 296 (y1 < 0.0)); 297 } 298 } 299 } 300 301 /** 302 * A utility method that draws a line but only if none of the coordinates 303 * are NaN values. 304 * 305 * @param g2 the graphics target. 306 * @param line the line object. 307 * @param x0 the x-coordinate for the starting point of the line. 308 * @param y0 the y-coordinate for the starting point of the line. 309 * @param x1 the x-coordinate for the ending point of the line. 310 * @param y1 the y-coordinate for the ending point of the line. 311 */ 312 private void drawLine(Graphics2D g2, Line2D line, double x0, double y0, 313 double x1, double y1) { 314 if (Double.isNaN(x0) || Double.isNaN(x1) || Double.isNaN(y0) 315 || Double.isNaN(y1)) { 316 return; 317 } 318 line.setLine(x0, y0, x1, y1); 319 g2.draw(line); 320 } 321 322 /** 323 * Tests this renderer for equality with an arbitrary object. 324 * 325 * @param obj the object (<code>null</code> permitted). 326 * 327 * @return A boolean. 328 */ 329 @Override 330 public boolean equals(Object obj) { 331 if (obj == this) { 332 return true; 333 } 334 if (!(obj instanceof XYLineAndShapeRenderer)) { 335 return false; 336 } 337 XYStepRenderer that = (XYStepRenderer) obj; 338 if (this.stepPoint != that.stepPoint) { 339 return false; 340 } 341 return super.equals(obj); 342 } 343 344 /** 345 * Returns a hash code for this instance. 346 * 347 * @return A hash code. 348 */ 349 @Override 350 public int hashCode() { 351 return HashUtilities.hashCode(super.hashCode(), this.stepPoint); 352 } 353 354 /** 355 * Returns a clone of the renderer. 356 * 357 * @return A clone. 358 * 359 * @throws CloneNotSupportedException if the renderer cannot be cloned. 360 */ 361 @Override 362 public Object clone() throws CloneNotSupportedException { 363 return super.clone(); 364 } 365 366}