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 * YIntervalRenderer.java 029 * ---------------------- 030 * (C) Copyright 2002-present, by David Gilbert. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): -; 034 * 035 */ 036 037package org.jfree.chart.renderer.xy; 038 039import java.awt.Font; 040import java.awt.Graphics2D; 041import java.awt.Paint; 042import java.awt.Shape; 043import java.awt.Stroke; 044import java.awt.geom.Line2D; 045import java.awt.geom.Point2D; 046import java.awt.geom.Rectangle2D; 047import java.io.Serializable; 048import java.util.Objects; 049 050import org.jfree.chart.axis.ValueAxis; 051import org.jfree.chart.entity.EntityCollection; 052import org.jfree.chart.event.RendererChangeEvent; 053import org.jfree.chart.labels.ItemLabelPosition; 054import org.jfree.chart.labels.XYItemLabelGenerator; 055import org.jfree.chart.plot.CrosshairState; 056import org.jfree.chart.plot.PlotOrientation; 057import org.jfree.chart.plot.PlotRenderingInfo; 058import org.jfree.chart.plot.XYPlot; 059import org.jfree.chart.text.TextUtils; 060import org.jfree.chart.ui.RectangleEdge; 061import org.jfree.chart.util.PublicCloneable; 062import org.jfree.chart.util.ShapeUtils; 063import org.jfree.data.Range; 064import org.jfree.data.xy.IntervalXYDataset; 065import org.jfree.data.xy.XYDataset; 066 067/** 068 * A renderer that draws a line connecting the start and end Y values for an 069 * {@link XYPlot}. The example shown here is generated by the 070 * {@code YIntervalRendererDemo1.java} program included in the JFreeChart 071 * demo collection: 072 * <br><br> 073 * <img src="doc-files/YIntervalRendererSample.png" 074 * alt="YIntervalRendererSample.png"> 075 */ 076public class YIntervalRenderer extends AbstractXYItemRenderer 077 implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { 078 079 /** For serialization. */ 080 private static final long serialVersionUID = -2951586537224143260L; 081 082 /** 083 * An additional item label generator. If this is non-null, the item 084 * label generated will be displayed near the lower y-value at the 085 * position given by getNegativeItemLabelPosition(). 086 */ 087 private XYItemLabelGenerator additionalItemLabelGenerator; 088 089 /** 090 * The default constructor. 091 */ 092 public YIntervalRenderer() { 093 super(); 094 this.additionalItemLabelGenerator = null; 095 } 096 097 /** 098 * Returns the generator for the item labels that appear near the lower 099 * y-value. 100 * 101 * @return The generator (possibly {@code null}). 102 * 103 * @see #setAdditionalItemLabelGenerator(XYItemLabelGenerator) 104 */ 105 public XYItemLabelGenerator getAdditionalItemLabelGenerator() { 106 return this.additionalItemLabelGenerator; 107 } 108 109 /** 110 * Sets the generator for the item labels that appear near the lower 111 * y-value and sends a {@link RendererChangeEvent} to all registered 112 * listeners. If this is set to {@code null}, no item labels will be 113 * drawn. 114 * 115 * @param generator the generator ({@code null} permitted). 116 * 117 * @see #getAdditionalItemLabelGenerator() 118 */ 119 public void setAdditionalItemLabelGenerator( 120 XYItemLabelGenerator generator) { 121 this.additionalItemLabelGenerator = generator; 122 fireChangeEvent(); 123 } 124 125 /** 126 * Returns the range of values the renderer requires to display all the 127 * items from the specified dataset. 128 * 129 * @param dataset the dataset ({@code null} permitted). 130 * 131 * @return The range ({@code null} if the dataset is {@code null} or empty). 132 */ 133 @Override 134 public Range findRangeBounds(XYDataset dataset) { 135 return findRangeBounds(dataset, true); 136 } 137 138 /** 139 * Draws the visual representation of a single data item. 140 * 141 * @param g2 the graphics device. 142 * @param state the renderer state. 143 * @param dataArea the area within which the plot is being drawn. 144 * @param info collects information about the drawing. 145 * @param plot the plot (can be used to obtain standard color 146 * information etc). 147 * @param domainAxis the domain axis. 148 * @param rangeAxis the range axis. 149 * @param dataset the dataset. 150 * @param series the series index (zero-based). 151 * @param item the item index (zero-based). 152 * @param crosshairState crosshair information for the plot 153 * ({@code null} permitted). 154 * @param pass the pass index (ignored here). 155 */ 156 @Override 157 public void drawItem(Graphics2D g2, XYItemRendererState state, 158 Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, 159 ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, 160 int series, int item, CrosshairState crosshairState, int pass) { 161 162 // do nothing if item is not visible 163 if (!getItemVisible(series, item)) { 164 return; 165 } 166 167 // setup for collecting optional entity info... 168 EntityCollection entities = null; 169 if (info != null) { 170 entities = info.getOwner().getEntityCollection(); 171 } 172 173 IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; 174 175 double x = intervalDataset.getXValue(series, item); 176 double yLow = intervalDataset.getStartYValue(series, item); 177 double yHigh = intervalDataset.getEndYValue(series, item); 178 179 RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 180 RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); 181 182 double xx = domainAxis.valueToJava2D(x, dataArea, xAxisLocation); 183 double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, yAxisLocation); 184 double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, yAxisLocation); 185 186 Paint p = getItemPaint(series, item); 187 Stroke s = getItemStroke(series, item); 188 189 Line2D line = null; 190 Shape shape = getItemShape(series, item); 191 Shape top = null; 192 Shape bottom = null; 193 PlotOrientation orientation = plot.getOrientation(); 194 if (orientation == PlotOrientation.HORIZONTAL) { 195 line = new Line2D.Double(yyLow, xx, yyHigh, xx); 196 top = ShapeUtils.createTranslatedShape(shape, yyHigh, xx); 197 bottom = ShapeUtils.createTranslatedShape(shape, yyLow, xx); 198 } 199 else if (orientation == PlotOrientation.VERTICAL) { 200 line = new Line2D.Double(xx, yyLow, xx, yyHigh); 201 top = ShapeUtils.createTranslatedShape(shape, xx, yyHigh); 202 bottom = ShapeUtils.createTranslatedShape(shape, xx, yyLow); 203 } else { 204 throw new IllegalStateException(); 205 } 206 g2.setPaint(p); 207 g2.setStroke(s); 208 g2.draw(line); 209 210 g2.fill(top); 211 g2.fill(bottom); 212 213 // for item labels, we have a special case because there is the 214 // possibility to draw (a) the regular item label near to just the 215 // upper y-value, or (b) the regular item label near the upper y-value 216 // PLUS an additional item label near the lower y-value. 217 if (isItemLabelVisible(series, item)) { 218 drawItemLabel(g2, orientation, dataset, series, item, xx, yyHigh, 219 false); 220 drawAdditionalItemLabel(g2, orientation, dataset, series, item, 221 xx, yyLow); 222 } 223 224 // add an entity for the item... 225 Shape hotspot = ShapeUtils.createLineRegion(line, 4.0f); 226 if (entities != null && hotspot.intersects(dataArea)) { 227 addEntity(entities, hotspot, dataset, series, item, 0.0, 0.0); 228 } 229 230 } 231 232 /** 233 * Draws an item label. 234 * 235 * @param g2 the graphics device. 236 * @param orientation the orientation. 237 * @param dataset the dataset. 238 * @param series the series index (zero-based). 239 * @param item the item index (zero-based). 240 * @param x the x coordinate (in Java2D space). 241 * @param y the y coordinate (in Java2D space). 242 */ 243 private void drawAdditionalItemLabel(Graphics2D g2, 244 PlotOrientation orientation, XYDataset dataset, int series, 245 int item, double x, double y) { 246 247 if (this.additionalItemLabelGenerator == null) { 248 return; 249 } 250 251 Font labelFont = getItemLabelFont(series, item); 252 Paint paint = getItemLabelPaint(series, item); 253 g2.setFont(labelFont); 254 g2.setPaint(paint); 255 String label = this.additionalItemLabelGenerator.generateLabel(dataset, 256 series, item); 257 258 ItemLabelPosition position = getNegativeItemLabelPosition(series, item); 259 Point2D anchorPoint = calculateLabelAnchorPoint( 260 position.getItemLabelAnchor(), x, y, orientation); 261 TextUtils.drawRotatedString(label, g2, 262 (float) anchorPoint.getX(), (float) anchorPoint.getY(), 263 position.getTextAnchor(), position.getAngle(), 264 position.getRotationAnchor()); 265 } 266 267 /** 268 * Tests this renderer for equality with an arbitrary object. 269 * 270 * @param obj the object ({@code null} permitted). 271 * 272 * @return A boolean. 273 */ 274 @Override 275 public boolean equals(Object obj) { 276 if (obj == this) { 277 return true; 278 } 279 if (!(obj instanceof YIntervalRenderer)) { 280 return false; 281 } 282 YIntervalRenderer that = (YIntervalRenderer) obj; 283 if (!Objects.equals(this.additionalItemLabelGenerator, 284 that.additionalItemLabelGenerator)) { 285 return false; 286 } 287 return super.equals(obj); 288 } 289 290 /** 291 * Returns a clone of the renderer. 292 * 293 * @return A clone. 294 * 295 * @throws CloneNotSupportedException if the renderer cannot be cloned. 296 */ 297 @Override 298 public Object clone() throws CloneNotSupportedException { 299 return super.clone(); 300 } 301 302}