Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2007, 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: * DeviationRenderer.java 29: * ---------------------- 30: * (C) Copyright 2007, by Object Refinery Limited and Contributors. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): -; 34: * 35: * $Id: DeviationRenderer.java,v 1.1.2.2 2007/03/15 12:48:56 mungady Exp $ 36: * 37: * Changes 38: * ------- 39: * 21-Feb-2007 : Version 1 (DG); 40: * 41: */ 42: 43: package org.jfree.chart.renderer.xy; 44: 45: import java.awt.AlphaComposite; 46: import java.awt.Composite; 47: import java.awt.Graphics2D; 48: import java.awt.geom.GeneralPath; 49: import java.awt.geom.Rectangle2D; 50: import java.util.List; 51: 52: import org.jfree.chart.axis.ValueAxis; 53: import org.jfree.chart.entity.EntityCollection; 54: import org.jfree.chart.event.RendererChangeEvent; 55: import org.jfree.chart.plot.CrosshairState; 56: import org.jfree.chart.plot.PlotOrientation; 57: import org.jfree.chart.plot.PlotRenderingInfo; 58: import org.jfree.chart.plot.XYPlot; 59: import org.jfree.data.xy.IntervalXYDataset; 60: import org.jfree.data.xy.XYDataset; 61: import org.jfree.ui.RectangleEdge; 62: 63: /** 64: * A specialised subclass of the {@link XYLineAndShapeRenderer} that requires 65: * an {@link IntervalXYDataset} and represents the y-interval by shading an 66: * area behind the y-values on the chart. 67: * 68: * @since 1.0.5 69: */ 70: public class DeviationRenderer extends XYLineAndShapeRenderer { 71: 72: /** 73: * A state object that is passed to each call to <code>drawItem</code>. 74: */ 75: public static class State extends XYLineAndShapeRenderer.State { 76: 77: /** 78: * A list of coordinates for the upper y-values in the current series 79: * (after translation into Java2D space). 80: */ 81: public List upperCoordinates; 82: 83: /** 84: * A list of coordinates for the lower y-values in the current series 85: * (after translation into Java2D space). 86: */ 87: public List lowerCoordinates; 88: 89: /** 90: * Creates a new state instance. 91: * 92: * @param info the plot rendering info. 93: */ 94: public State(PlotRenderingInfo info) { 95: super(info); 96: this.lowerCoordinates = new java.util.ArrayList(); 97: this.upperCoordinates = new java.util.ArrayList(); 98: } 99: 100: } 101: 102: /** The alpha transparency for the interval shading. */ 103: private float alpha; 104: 105: /** 106: * Creates a new renderer that displays lines and shapes for the data 107: * items, as well as the shaded area for the y-interval. 108: */ 109: public DeviationRenderer() { 110: this(true, true); 111: } 112: 113: /** 114: * Creates a new renderer. 115: * 116: * @param lines show lines between data items? 117: * @param shapes show a shape for each data item? 118: */ 119: public DeviationRenderer(boolean lines, boolean shapes) { 120: super(lines, shapes); 121: super.setDrawSeriesLineAsPath(true); 122: this.alpha = 0.5f; 123: } 124: 125: /** 126: * Returns the alpha transparency for the background shading. 127: * 128: * @return The alpha transparency. 129: * 130: * @see #setAlpha(float) 131: */ 132: public float getAlpha() { 133: return this.alpha; 134: } 135: 136: /** 137: * Sets the alpha transparency for the background shading, and sends a 138: * {@link RendererChangeEvent} to all registered listeners. 139: * 140: * @param alpha the alpha (in the range 0.0f to 1.0f). 141: * 142: * @see #getAlpha() 143: */ 144: public void setAlpha(float alpha) { 145: if (alpha < 0.0f || alpha > 1.0f) { 146: throw new IllegalArgumentException( 147: "Requires 'alpha' in the range 0.0 to 1.0."); 148: } 149: this.alpha = alpha; 150: notifyListeners(new RendererChangeEvent(this)); 151: } 152: 153: /** 154: * This method is overridden so that this flag cannot be changed---it is 155: * set to <code>true</code> for this renderer. 156: * 157: * @param flag ignored. 158: */ 159: public void setDrawSeriesLineAsPath(boolean flag) { 160: // ignore 161: } 162: 163: /** 164: * Initialises and returns a state object that can be passed to each 165: * invocation of the {@link #drawItem} method. 166: * 167: * @param g2 the graphics target. 168: * @param dataArea the data area. 169: * @param plot the plot. 170: * @param dataset the dataset. 171: * @param info the plot rendering info. 172: * 173: * @return A newly initialised state object. 174: */ 175: public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, 176: XYPlot plot, XYDataset dataset, PlotRenderingInfo info) { 177: State state = new State(info); 178: state.seriesPath = new GeneralPath(); 179: return state; 180: } 181: 182: /** 183: * Returns the number of passes (through the dataset) used by this 184: * renderer. 185: * 186: * @return <code>3</code>. 187: */ 188: public int getPassCount() { 189: return 3; 190: } 191: 192: /** 193: * Returns <code>true</code> if this is the pass where the shapes are 194: * drawn. 195: * 196: * @param pass the pass index. 197: * 198: * @return A boolean. 199: * 200: * @see #isLinePass(int) 201: */ 202: protected boolean isItemPass(int pass) { 203: return (pass == 2); 204: } 205: 206: /** 207: * Returns <code>true</code> if this is the pass where the lines are 208: * drawn. 209: * 210: * @param pass the pass index. 211: * 212: * @return A boolean. 213: * 214: * @see #isItemPass(int) 215: */ 216: protected boolean isLinePass(int pass) { 217: return (pass == 1); 218: } 219: 220: /** 221: * Draws the visual representation of a single data item. 222: * 223: * @param g2 the graphics device. 224: * @param state the renderer state. 225: * @param dataArea the area within which the data is being drawn. 226: * @param info collects information about the drawing. 227: * @param plot the plot (can be used to obtain standard color 228: * information etc). 229: * @param domainAxis the domain axis. 230: * @param rangeAxis the range axis. 231: * @param dataset the dataset. 232: * @param series the series index (zero-based). 233: * @param item the item index (zero-based). 234: * @param crosshairState crosshair information for the plot 235: * (<code>null</code> permitted). 236: * @param pass the pass index. 237: */ 238: public void drawItem(Graphics2D g2, 239: XYItemRendererState state, 240: Rectangle2D dataArea, 241: PlotRenderingInfo info, 242: XYPlot plot, 243: ValueAxis domainAxis, 244: ValueAxis rangeAxis, 245: XYDataset dataset, 246: int series, 247: int item, 248: CrosshairState crosshairState, 249: int pass) { 250: 251: // do nothing if item is not visible 252: if (!getItemVisible(series, item)) { 253: return; 254: } 255: 256: // first pass draws the shading 257: if (pass == 0) { 258: IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; 259: State drState = (State) state; 260: 261: double x = intervalDataset.getXValue(series, item); 262: double yLow = intervalDataset.getStartYValue(series, item); 263: double yHigh = intervalDataset.getEndYValue(series, item); 264: 265: RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 266: RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); 267: 268: double xx = domainAxis.valueToJava2D(x, dataArea, xAxisLocation); 269: double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, 270: yAxisLocation); 271: double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, 272: yAxisLocation); 273: 274: PlotOrientation orientation = plot.getOrientation(); 275: if (orientation == PlotOrientation.HORIZONTAL) { 276: drState.lowerCoordinates.add(new double[] {yyLow, xx}); 277: drState.upperCoordinates.add(new double[] {yyHigh, xx}); 278: } 279: else if (orientation == PlotOrientation.VERTICAL) { 280: drState.lowerCoordinates.add(new double[] {xx, yyLow}); 281: drState.upperCoordinates.add(new double[] {xx, yyHigh}); 282: } 283: 284: if (item == (dataset.getItemCount(series) - 1)) { 285: // last item in series, draw the lot... 286: // set up the alpha-transparency... 287: Composite originalComposite = g2.getComposite(); 288: g2.setComposite(AlphaComposite.getInstance( 289: AlphaComposite.SRC_OVER, this.alpha)); 290: g2.setPaint(getItemFillPaint(series, item)); 291: GeneralPath area = new GeneralPath(); 292: double[] coords = (double[]) drState.lowerCoordinates.get(0); 293: area.moveTo((float) coords[0], (float) coords[1]); 294: for (int i = 1; i < drState.lowerCoordinates.size(); i++) { 295: coords = (double[]) drState.lowerCoordinates.get(i); 296: area.lineTo((float) coords[0], (float) coords[1]); 297: } 298: int count = drState.upperCoordinates.size(); 299: coords = (double[]) drState.upperCoordinates.get(count - 1); 300: area.lineTo((float) coords[0], (float) coords[1]); 301: for (int i = count - 2; i >= 0; i--) { 302: coords = (double[]) drState.upperCoordinates.get(i); 303: area.lineTo((float) coords[0], (float) coords[1]); 304: } 305: area.closePath(); 306: g2.fill(area); 307: g2.setComposite(originalComposite); 308: 309: drState.lowerCoordinates.clear(); 310: drState.upperCoordinates.clear(); 311: } 312: } 313: if (isLinePass(pass)) { 314: 315: // the following code handles the line for the y-values...it's 316: // all done by code in the super class 317: if (item == 0) { 318: State s = (State) state; 319: s.seriesPath.reset(); 320: s.setLastPointGood(false); 321: } 322: 323: if (getItemLineVisible(series, item)) { 324: drawPrimaryLineAsPath(state, g2, plot, dataset, pass, 325: series, item, domainAxis, rangeAxis, dataArea); 326: } 327: } 328: 329: // second pass adds shapes where the items are .. 330: else if (isItemPass(pass)) { 331: 332: // setup for collecting optional entity info... 333: EntityCollection entities = null; 334: if (info != null) { 335: entities = info.getOwner().getEntityCollection(); 336: } 337: 338: drawSecondaryPass(g2, plot, dataset, pass, series, item, 339: domainAxis, dataArea, rangeAxis, crosshairState, entities); 340: } 341: } 342: 343: /** 344: * Tests this renderer for equality with an arbitrary object. 345: * 346: * @param obj the object (<code>null</code> permitted). 347: * 348: * @return A boolean. 349: */ 350: public boolean equals(Object obj) { 351: if (obj == this) { 352: return true; 353: } 354: if (!(obj instanceof DeviationRenderer)) { 355: return false; 356: } 357: DeviationRenderer that = (DeviationRenderer) obj; 358: if (this.alpha != that.alpha) { 359: return false; 360: } 361: return super.equals(obj); 362: } 363: 364: }