Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2008, 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: * XYStepRenderer.java 29: * ------------------- 30: * (C) Copyright 2002-2008, by Roger Studner and Contributors. 31: * 32: * Original Author: Roger Studner; 33: * Contributor(s): David Gilbert (for Object Refinery Limited); 34: * Matthias Rose; 35: * Gerald Struck (fix for bug 1569094); 36: * Ulrich Voigt (patch 1874890); 37: * Martin Hoeller (contribution to patch 1874890); 38: * 39: * Changes 40: * ------- 41: * 13-May-2002 : Version 1, contributed by Roger Studner (DG); 42: * 25-Jun-2002 : Updated import statements (DG); 43: * 22-Jul-2002 : Added check for null data items (DG); 44: * 25-Mar-2003 : Implemented Serializable (DG); 45: * 01-May-2003 : Modified drawItem() method signature (DG); 46: * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG); 47: * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 48: * 28-Oct-2003 : Added tooltips, code contributed by Matthias Rose 49: * (RFE 824857) (DG); 50: * 10-Feb-2004 : Removed working line (use line from state object instead) (DG); 51: * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState. Renamed 52: * XYToolTipGenerator --> XYItemLabelGenerator (DG); 53: * 19-Jan-2005 : Now accesses only primitives from dataset (DG); 54: * 15-Mar-2005 : Fix silly bug in drawItem() method (DG); 55: * 19-Sep-2005 : Extend XYLineAndShapeRenderer (fixes legend shapes), added 56: * support for series visibility, and use getDefaultEntityRadius() 57: * for entity hotspot size (DG); 58: * ------------- JFREECHART 1.0.x --------------------------------------------- 59: * 15-Jun-2006 : Added basic support for item labels (DG); 60: * 11-Oct-2006 : Fixed rendering with horizontal orientation (see bug 1569094), 61: * thanks to Gerald Struck (DG); 62: * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG); 63: * 14-Feb-2008 : Applied patch 1874890 by Ulrich Voigt (with contribution from 64: * Martin Hoeller) (DG); 65: * 14-May-2008 : Call addEntity() in drawItem() (DG); 66: * 67: */ 68: 69: package org.jfree.chart.renderer.xy; 70: 71: import java.awt.Graphics2D; 72: import java.awt.Paint; 73: import java.awt.Stroke; 74: import java.awt.geom.Line2D; 75: import java.awt.geom.Rectangle2D; 76: import java.io.Serializable; 77: 78: import org.jfree.chart.HashUtilities; 79: import org.jfree.chart.axis.ValueAxis; 80: import org.jfree.chart.entity.EntityCollection; 81: import org.jfree.chart.event.RendererChangeEvent; 82: import org.jfree.chart.labels.XYToolTipGenerator; 83: import org.jfree.chart.plot.CrosshairState; 84: import org.jfree.chart.plot.PlotOrientation; 85: import org.jfree.chart.plot.PlotRenderingInfo; 86: import org.jfree.chart.plot.XYPlot; 87: import org.jfree.chart.urls.XYURLGenerator; 88: import org.jfree.data.xy.XYDataset; 89: import org.jfree.ui.RectangleEdge; 90: import org.jfree.util.PublicCloneable; 91: 92: /** 93: * Line/Step item renderer for an {@link XYPlot}. This class draws lines 94: * between data points, only allowing horizontal or vertical lines (steps). 95: */ 96: public class XYStepRenderer extends XYLineAndShapeRenderer 97: implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { 98: 99: /** 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: }