001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert 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 * DeviationStepRenderer.java 029 * -------------------------- 030 * (C) Copyright 2007-present, by David Gilbert and Contributors. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): -; 034 * 035 */ 036 037package org.jfree.chart.renderer.xy; 038 039import org.jfree.chart.axis.ValueAxis; 040import org.jfree.chart.entity.EntityCollection; 041import org.jfree.chart.plot.CrosshairState; 042import org.jfree.chart.plot.PlotOrientation; 043import org.jfree.chart.plot.PlotRenderingInfo; 044import org.jfree.chart.plot.XYPlot; 045import org.jfree.chart.ui.RectangleEdge; 046import org.jfree.data.xy.IntervalXYDataset; 047import org.jfree.data.xy.XYDataset; 048 049import java.awt.*; 050import java.awt.geom.GeneralPath; 051import java.awt.geom.Rectangle2D; 052 053/** 054 * A specialised subclass of the {@link DeviationRenderer} that requires 055 * an {@link IntervalXYDataset} and represents the y-interval by shading an 056 * area behind the y-values on the chart, drawing only horizontal or 057 * vertical lines (steps); 058 * 059 * @since 1.5.1 060 */ 061public class DeviationStepRenderer extends DeviationRenderer { 062 063 /** 064 * Creates a new renderer that displays lines and shapes for the data 065 * items, as well as the shaded area for the y-interval. 066 */ 067 public DeviationStepRenderer() { 068 super(); 069 } 070 071 /** 072 * Creates a new renderer. 073 * 074 * @param lines show lines between data items? 075 * @param shapes show a shape for each data item? 076 */ 077 public DeviationStepRenderer(boolean lines, boolean shapes) { 078 super(lines, shapes); 079 } 080 081 /** 082 * Draws the visual representation of a single data item. 083 * 084 * @param g2 the graphics device. 085 * @param state the renderer state. 086 * @param dataArea the area within which the data is being drawn. 087 * @param info collects information about the drawing. 088 * @param plot the plot (can be used to obtain standard color 089 * information etc). 090 * @param domainAxis the domain axis. 091 * @param rangeAxis the range axis. 092 * @param dataset the dataset. 093 * @param series the series index (zero-based). 094 * @param item the item index (zero-based). 095 * @param crosshairState crosshair information for the plot 096 * ({@code null} permitted). 097 * @param pass the pass index. 098 */ 099 @Override 100 public void drawItem(Graphics2D g2, XYItemRendererState state, 101 Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, 102 ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, 103 int series, int item, CrosshairState crosshairState, int pass) { 104 105 // do nothing if item is not visible 106 if (!getItemVisible(series, item)) { 107 return; 108 } 109 110 // first pass draws the shading 111 if (pass == 0) { 112 IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; 113 State drState = (State) state; 114 115 double x = intervalDataset.getXValue(series, item); 116 double yLow = intervalDataset.getStartYValue(series, item); 117 double yHigh = intervalDataset.getEndYValue(series, item); 118 119 RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 120 RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); 121 122 double xx = domainAxis.valueToJava2D(x, dataArea, xAxisLocation); 123 double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, 124 yAxisLocation); 125 double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, 126 yAxisLocation); 127 128 129 PlotOrientation orientation = plot.getOrientation(); 130 if (item > 0 && !Double.isNaN(xx)) { 131 double yLowPrev = intervalDataset.getStartYValue(series, item-1); 132 double yHighPrev = intervalDataset.getEndYValue(series, item-1); 133 double yyLowPrev = rangeAxis.valueToJava2D(yLowPrev, dataArea, 134 yAxisLocation); 135 double yyHighPrev = rangeAxis.valueToJava2D(yHighPrev, dataArea, 136 yAxisLocation); 137 138 if(!Double.isNaN(yyLow) && !Double.isNaN(yyHigh)) { 139 if (orientation == PlotOrientation.HORIZONTAL) { 140 drState.lowerCoordinates.add(new double[]{yyLowPrev, xx}); 141 drState.upperCoordinates.add(new double[]{yyHighPrev, xx}); 142 } else if (orientation == PlotOrientation.VERTICAL) { 143 drState.lowerCoordinates.add(new double[]{xx, yyLowPrev}); 144 drState.upperCoordinates.add(new double[]{xx, yyHighPrev}); 145 } 146 } 147 } 148 149 boolean intervalGood = !Double.isNaN(xx) && !Double.isNaN(yLow) && !Double.isNaN(yHigh); 150 if (intervalGood) { 151 if (orientation == PlotOrientation.HORIZONTAL) { 152 drState.lowerCoordinates.add(new double[]{yyLow, xx}); 153 drState.upperCoordinates.add(new double[]{yyHigh, xx}); 154 } else if (orientation == PlotOrientation.VERTICAL) { 155 drState.lowerCoordinates.add(new double[]{xx, yyLow}); 156 drState.upperCoordinates.add(new double[]{xx, yyHigh}); 157 } 158 } 159 160 if (item == (dataset.getItemCount(series) - 1) || 161 (!intervalGood && drState.lowerCoordinates.size() > 1)) { 162 // draw items so far, either we reached the end of the series or the next interval is invalid 163 // last item in series, draw the lot... 164 // set up the alpha-transparency... 165 Composite originalComposite = g2.getComposite(); 166 g2.setComposite(AlphaComposite.getInstance( 167 AlphaComposite.SRC_OVER, this.alpha)); 168 g2.setPaint(getItemFillPaint(series, item)); 169 GeneralPath area = new GeneralPath(GeneralPath.WIND_NON_ZERO, 170 drState.lowerCoordinates.size() 171 + drState.upperCoordinates.size()); 172 double[] coords = (double[]) drState.lowerCoordinates.get(0); 173 area.moveTo((float) coords[0], (float) coords[1]); 174 for (int i = 1; i < drState.lowerCoordinates.size(); i++) { 175 coords = (double[]) drState.lowerCoordinates.get(i); 176 area.lineTo((float) coords[0], (float) coords[1]); 177 } 178 int count = drState.upperCoordinates.size(); 179 coords = (double[]) drState.upperCoordinates.get(count - 1); 180 area.lineTo((float) coords[0], (float) coords[1]); 181 for (int i = count - 2; i >= 0; i--) { 182 coords = (double[]) drState.upperCoordinates.get(i); 183 area.lineTo((float) coords[0], (float) coords[1]); 184 } 185 area.closePath(); 186 g2.fill(area); 187 g2.setComposite(originalComposite); 188 189 drState.lowerCoordinates.clear(); 190 drState.upperCoordinates.clear(); 191 } 192 } 193 if (isLinePass(pass)) { 194 195 // the following code handles the line for the y-values...it's 196 // all done by code in the super class 197 if (item == 0) { 198 State s = (State) state; 199 s.seriesPath.reset(); 200 s.setLastPointGood(false); 201 } 202 203 if (getItemLineVisible(series, item)) { 204 drawPrimaryLineAsPath(state, g2, plot, dataset, pass, 205 series, item, domainAxis, rangeAxis, dataArea); 206 } 207 } 208 209 // second pass adds shapes where the items are .. 210 else if (isItemPass(pass)) { 211 212 // setup for collecting optional entity info... 213 EntityCollection entities = null; 214 if (info != null) { 215 entities = info.getOwner().getEntityCollection(); 216 } 217 218 drawSecondaryPass(g2, plot, dataset, pass, series, item, 219 domainAxis, dataArea, rangeAxis, crosshairState, entities); 220 } 221 } 222 223 /** 224 * Draws the item (first pass). This method draws the lines 225 * connecting the items. Instead of drawing separate lines, 226 * a {@code GeneralPath} is constructed and drawn at the end of 227 * the series painting. 228 * 229 * @param g2 the graphics device. 230 * @param state the renderer state. 231 * @param plot the plot (can be used to obtain standard color information 232 * etc). 233 * @param dataset the dataset. 234 * @param pass the pass. 235 * @param series the series index (zero-based). 236 * @param item the item index (zero-based). 237 * @param domainAxis the domain axis. 238 * @param rangeAxis the range axis. 239 * @param dataArea the area within which the data is being drawn. 240 */ 241 @Override 242 protected void drawPrimaryLineAsPath(XYItemRendererState state, 243 Graphics2D g2, XYPlot plot, XYDataset dataset, int pass, 244 int series, int item, ValueAxis domainAxis, ValueAxis rangeAxis, 245 Rectangle2D dataArea) { 246 247 RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 248 RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); 249 250 // get the data point... 251 double x1 = dataset.getXValue(series, item); 252 double y1 = dataset.getYValue(series, item); 253 double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); 254 double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation); 255 256 XYLineAndShapeRenderer.State s = (XYLineAndShapeRenderer.State) state; 257 // update path to reflect latest point 258 if (!Double.isNaN(transX1) && !Double.isNaN(transY1)) { 259 float x = (float) transX1; 260 float y = (float) transY1; 261 PlotOrientation orientation = plot.getOrientation(); 262 if (orientation == PlotOrientation.HORIZONTAL) { 263 x = (float) transY1; 264 y = (float) transX1; 265 } 266 if (s.isLastPointGood()) { 267 if (item > 0) { 268 if (orientation == PlotOrientation.HORIZONTAL) { 269 s.seriesPath.lineTo(s.seriesPath.getCurrentPoint().getX(), y); 270 } else { 271 s.seriesPath.lineTo(x, s.seriesPath.getCurrentPoint().getY()); 272 } 273 } 274 s.seriesPath.lineTo(x, y); 275 } 276 else { 277 s.seriesPath.moveTo(x, y); 278 } 279 s.setLastPointGood(true); 280 } else { 281 s.setLastPointGood(false); 282 } 283 // if this is the last item, draw the path ... 284 if (item == s.getLastItemIndex()) { 285 // draw path 286 drawFirstPassShape(g2, pass, series, item, s.seriesPath); 287 } 288 } 289 290 291 /** 292 * Tests this renderer for equality with an arbitrary object. 293 * 294 * @param obj the object ({@code null} permitted). 295 * 296 * @return A boolean. 297 */ 298 @Override 299 public boolean equals(Object obj) { 300 if (obj == this) { 301 return true; 302 } 303 if (!(obj instanceof DeviationStepRenderer)) { 304 return false; 305 } 306 DeviationStepRenderer that = (DeviationStepRenderer) obj; 307 if (this.alpha != that.alpha) { 308 return false; 309 } 310 return super.equals(obj); 311 } 312 313}