001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, 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-2008, 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 * 067 */ 068 069 package org.jfree.chart.renderer.xy; 070 071 import java.awt.Graphics2D; 072 import java.awt.Paint; 073 import java.awt.Stroke; 074 import java.awt.geom.Line2D; 075 import java.awt.geom.Rectangle2D; 076 import java.io.Serializable; 077 078 import org.jfree.chart.HashUtilities; 079 import org.jfree.chart.axis.ValueAxis; 080 import org.jfree.chart.entity.EntityCollection; 081 import org.jfree.chart.event.RendererChangeEvent; 082 import org.jfree.chart.labels.XYToolTipGenerator; 083 import org.jfree.chart.plot.CrosshairState; 084 import org.jfree.chart.plot.PlotOrientation; 085 import org.jfree.chart.plot.PlotRenderingInfo; 086 import org.jfree.chart.plot.XYPlot; 087 import org.jfree.chart.urls.XYURLGenerator; 088 import org.jfree.data.xy.XYDataset; 089 import org.jfree.ui.RectangleEdge; 090 import org.jfree.util.PublicCloneable; 091 092 /** 093 * Line/Step item renderer for an {@link XYPlot}. This class draws lines 094 * between data points, only allowing horizontal or vertical lines (steps). 095 */ 096 public class XYStepRenderer extends XYLineAndShapeRenderer 097 implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { 098 099 /** For serialization. */ 100 private static final long serialVersionUID = -8918141928884796108L; 101 102 /** 103 * The factor (from 0.0 to 1.0) that determines the position of the 104 * step. 105 * 106 * @since 1.0.10. 107 */ 108 private double stepPoint = 1.0d; 109 110 /** 111 * Constructs a new renderer with no tooltip or URL generation. 112 */ 113 public XYStepRenderer() { 114 this(null, null); 115 } 116 117 /** 118 * Constructs a new renderer with the specified tool tip and URL 119 * generators. 120 * 121 * @param toolTipGenerator the item label generator (<code>null</code> 122 * permitted). 123 * @param urlGenerator the URL generator (<code>null</code> permitted). 124 */ 125 public XYStepRenderer(XYToolTipGenerator toolTipGenerator, 126 XYURLGenerator urlGenerator) { 127 super(); 128 setBaseToolTipGenerator(toolTipGenerator); 129 setURLGenerator(urlGenerator); 130 setBaseShapesVisible(false); 131 } 132 133 /** 134 * Returns the fraction of the domain position between two points on which 135 * the step is drawn. The default is 1.0d, which means the step is drawn 136 * at the domain position of the second`point. If the stepPoint is 0.5d the 137 * step is drawn at half between the two points. 138 * 139 * @return The fraction of the domain position between two points where the 140 * step is drawn. 141 * 142 * @see #setStepPoint(double) 143 * 144 * @since 1.0.10 145 */ 146 public double getStepPoint() { 147 return this.stepPoint; 148 } 149 150 /** 151 * Sets the step point and sends a {@link RendererChangeEvent} to all 152 * registered listeners. 153 * 154 * @param stepPoint the step point (in the range 0.0 to 1.0) 155 * 156 * @see #getStepPoint() 157 * 158 * @since 1.0.10 159 */ 160 public void setStepPoint(double stepPoint) { 161 if (stepPoint < 0.0d || stepPoint > 1.0d) { 162 throw new IllegalArgumentException( 163 "Requires stepPoint in [0.0;1.0]"); 164 } 165 this.stepPoint = stepPoint; 166 fireChangeEvent(); 167 } 168 169 /** 170 * Draws the visual representation of a single data item. 171 * 172 * @param g2 the graphics device. 173 * @param state the renderer state. 174 * @param dataArea the area within which the data is being drawn. 175 * @param info collects information about the drawing. 176 * @param plot the plot (can be used to obtain standard color 177 * information etc). 178 * @param domainAxis the domain axis. 179 * @param rangeAxis the vertical axis. 180 * @param dataset the dataset. 181 * @param series the series index (zero-based). 182 * @param item the item index (zero-based). 183 * @param crosshairState crosshair information for the plot 184 * (<code>null</code> permitted). 185 * @param pass the pass index (ignored here). 186 */ 187 public void drawItem(Graphics2D g2, 188 XYItemRendererState state, 189 Rectangle2D dataArea, 190 PlotRenderingInfo info, 191 XYPlot plot, 192 ValueAxis domainAxis, 193 ValueAxis rangeAxis, 194 XYDataset dataset, 195 int series, 196 int item, 197 CrosshairState crosshairState, 198 int pass) { 199 200 // do nothing if item is not visible 201 if (!getItemVisible(series, item)) { 202 return; 203 } 204 205 PlotOrientation orientation = plot.getOrientation(); 206 207 Paint seriesPaint = getItemPaint(series, item); 208 Stroke seriesStroke = getItemStroke(series, item); 209 g2.setPaint(seriesPaint); 210 g2.setStroke(seriesStroke); 211 212 // get the data point... 213 double x1 = dataset.getXValue(series, item); 214 double y1 = dataset.getYValue(series, item); 215 216 RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 217 RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); 218 double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); 219 double transY1 = (Double.isNaN(y1) ? Double.NaN 220 : rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation)); 221 222 if (item > 0) { 223 // get the previous data point... 224 double x0 = dataset.getXValue(series, item - 1); 225 double y0 = dataset.getYValue(series, item - 1); 226 double transX0 = domainAxis.valueToJava2D(x0, dataArea, 227 xAxisLocation); 228 double transY0 = (Double.isNaN(y0) ? Double.NaN 229 : rangeAxis.valueToJava2D(y0, dataArea, yAxisLocation)); 230 231 if (orientation == PlotOrientation.HORIZONTAL) { 232 if (transY0 == transY1) { 233 // this represents the situation 234 // for drawing a horizontal bar. 235 drawLine(g2, state.workingLine, transY0, transX0, transY1, 236 transX1); 237 } 238 else { //this handles the need to perform a 'step'. 239 240 // calculate the step point 241 double transXs = transX0 + (getStepPoint() 242 * (transX1 - transX0)); 243 drawLine(g2, state.workingLine, transY0, transX0, transY0, 244 transXs); 245 drawLine(g2, state.workingLine, transY0, transXs, transY1, 246 transXs); 247 drawLine(g2, state.workingLine, transY1, transXs, transY1, 248 transX1); 249 } 250 } 251 else if (orientation == PlotOrientation.VERTICAL) { 252 if (transY0 == transY1) { // this represents the situation 253 // for drawing a horizontal bar. 254 drawLine(g2, state.workingLine, transX0, transY0, transX1, 255 transY1); 256 } 257 else { //this handles the need to perform a 'step'. 258 // calculate the step point 259 double transXs = transX0 + (getStepPoint() 260 * (transX1 - transX0)); 261 drawLine(g2, state.workingLine, transX0, transY0, transXs, 262 transY0); 263 drawLine(g2, state.workingLine, transXs, transY0, transXs, 264 transY1); 265 drawLine(g2, state.workingLine, transXs, transY1, transX1, 266 transY1); 267 } 268 } 269 270 } 271 272 // draw the item label if there is one... 273 if (isItemLabelVisible(series, item)) { 274 double xx = transX1; 275 double yy = transY1; 276 if (orientation == PlotOrientation.HORIZONTAL) { 277 xx = transY1; 278 yy = transX1; 279 } 280 drawItemLabel(g2, orientation, dataset, series, item, xx, yy, 281 (y1 < 0.0)); 282 } 283 284 // submit this data item as a candidate for the crosshair point 285 int domainAxisIndex = plot.getDomainAxisIndex(domainAxis); 286 int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis); 287 updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex, 288 rangeAxisIndex, transX1, transY1, orientation); 289 290 // collect entity and tool tip information... 291 EntityCollection entities = state.getEntityCollection(); 292 if (entities != null) { 293 addEntity(entities, null, dataset, series, item, transX1, transY1); 294 } 295 296 } 297 298 /** 299 * A utility method that draws a line but only if none of the coordinates 300 * are NaN values. 301 * 302 * @param g2 the graphics target. 303 * @param line the line object. 304 * @param x0 the x-coordinate for the starting point of the line. 305 * @param y0 the y-coordinate for the starting point of the line. 306 * @param x1 the x-coordinate for the ending point of the line. 307 * @param y1 the y-coordinate for the ending point of the line. 308 */ 309 private void drawLine(Graphics2D g2, Line2D line, double x0, double y0, 310 double x1, double y1) { 311 if (Double.isNaN(x0) || Double.isNaN(x1) || Double.isNaN(y0) 312 || Double.isNaN(y1)) { 313 return; 314 } 315 line.setLine(x0, y0, x1, y1); 316 g2.draw(line); 317 } 318 319 /** 320 * Tests this renderer for equality with an arbitrary object. 321 * 322 * @param obj the object (<code>null</code> permitted). 323 * 324 * @return A boolean. 325 */ 326 public boolean equals(Object obj) { 327 if (obj == this) { 328 return true; 329 } 330 if (!(obj instanceof XYLineAndShapeRenderer)) { 331 return false; 332 } 333 XYStepRenderer that = (XYStepRenderer) obj; 334 if (this.stepPoint != that.stepPoint) { 335 return false; 336 } 337 return super.equals(obj); 338 } 339 340 /** 341 * Returns a hash code for this instance. 342 * 343 * @return A hash code. 344 */ 345 public int hashCode() { 346 return HashUtilities.hashCode(super.hashCode(), this.stepPoint); 347 } 348 349 /** 350 * Returns a clone of the renderer. 351 * 352 * @return A clone. 353 * 354 * @throws CloneNotSupportedException if the renderer cannot be cloned. 355 */ 356 public Object clone() throws CloneNotSupportedException { 357 return super.clone(); 358 } 359 360 }