Source for org.jfree.chart.renderer.xy.XYStepRenderer

   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: }