001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2022, 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-2022, 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.api.RectangleEdge; 061import org.jfree.chart.api.PublicCloneable; 062import org.jfree.chart.internal.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 * @since 1.0.10 088 */ 089 private XYItemLabelGenerator additionalItemLabelGenerator; 090 091 /** 092 * The default constructor. 093 */ 094 public YIntervalRenderer() { 095 super(); 096 this.additionalItemLabelGenerator = null; 097 } 098 099 /** 100 * Returns the generator for the item labels that appear near the lower 101 * y-value. 102 * 103 * @return The generator (possibly {@code null}). 104 * 105 * @see #setAdditionalItemLabelGenerator(XYItemLabelGenerator) 106 * 107 * @since 1.0.10 108 */ 109 public XYItemLabelGenerator getAdditionalItemLabelGenerator() { 110 return this.additionalItemLabelGenerator; 111 } 112 113 /** 114 * Sets the generator for the item labels that appear near the lower 115 * y-value and sends a {@link RendererChangeEvent} to all registered 116 * listeners. If this is set to {@code null}, no item labels will be 117 * drawn. 118 * 119 * @param generator the generator ({@code null} permitted). 120 * 121 * @see #getAdditionalItemLabelGenerator() 122 * 123 * @since 1.0.10 124 */ 125 public void setAdditionalItemLabelGenerator( 126 XYItemLabelGenerator generator) { 127 this.additionalItemLabelGenerator = generator; 128 fireChangeEvent(); 129 } 130 131 /** 132 * Returns the range of values the renderer requires to display all the 133 * items from the specified dataset. 134 * 135 * @param dataset the dataset ({@code null} permitted). 136 * 137 * @return The range ({@code null} if the dataset is {@code null} or empty). 138 */ 139 @Override 140 public Range findRangeBounds(XYDataset dataset) { 141 return findRangeBounds(dataset, true); 142 } 143 144 /** 145 * Draws the visual representation of a single data item. 146 * 147 * @param g2 the graphics device. 148 * @param state the renderer state. 149 * @param dataArea the area within which the plot is being drawn. 150 * @param info collects information about the drawing. 151 * @param plot the plot (can be used to obtain standard color 152 * information etc). 153 * @param domainAxis the domain axis. 154 * @param rangeAxis the range axis. 155 * @param dataset the dataset. 156 * @param series the series index (zero-based). 157 * @param item the item index (zero-based). 158 * @param crosshairState crosshair information for the plot 159 * ({@code null} permitted). 160 * @param pass the pass index (ignored here). 161 */ 162 @Override 163 public void drawItem(Graphics2D g2, XYItemRendererState state, 164 Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, 165 ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, 166 int series, int item, CrosshairState crosshairState, int pass) { 167 168 // do nothing if item is not visible 169 if (!getItemVisible(series, item)) { 170 return; 171 } 172 173 // setup for collecting optional entity info... 174 EntityCollection entities = null; 175 if (info != null) { 176 entities = info.getOwner().getEntityCollection(); 177 } 178 179 IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; 180 181 double x = intervalDataset.getXValue(series, item); 182 double yLow = intervalDataset.getStartYValue(series, item); 183 double yHigh = intervalDataset.getEndYValue(series, item); 184 185 RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 186 RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); 187 188 double xx = domainAxis.valueToJava2D(x, dataArea, xAxisLocation); 189 double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, yAxisLocation); 190 double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, yAxisLocation); 191 192 Paint p = getItemPaint(series, item); 193 Stroke s = getItemStroke(series, item); 194 195 Line2D line = null; 196 Shape shape = getItemShape(series, item); 197 Shape top = null; 198 Shape bottom = null; 199 PlotOrientation orientation = plot.getOrientation(); 200 if (orientation == PlotOrientation.HORIZONTAL) { 201 line = new Line2D.Double(yyLow, xx, yyHigh, xx); 202 top = ShapeUtils.createTranslatedShape(shape, yyHigh, xx); 203 bottom = ShapeUtils.createTranslatedShape(shape, yyLow, xx); 204 } 205 else if (orientation == PlotOrientation.VERTICAL) { 206 line = new Line2D.Double(xx, yyLow, xx, yyHigh); 207 top = ShapeUtils.createTranslatedShape(shape, xx, yyHigh); 208 bottom = ShapeUtils.createTranslatedShape(shape, xx, yyLow); 209 } else { 210 throw new IllegalStateException(); 211 } 212 g2.setPaint(p); 213 g2.setStroke(s); 214 g2.draw(line); 215 216 g2.fill(top); 217 g2.fill(bottom); 218 219 // for item labels, we have a special case because there is the 220 // possibility to draw (a) the regular item label near to just the 221 // upper y-value, or (b) the regular item label near the upper y-value 222 // PLUS an additional item label near the lower y-value. 223 if (isItemLabelVisible(series, item)) { 224 drawItemLabel(g2, orientation, dataset, series, item, xx, yyHigh, 225 false); 226 drawAdditionalItemLabel(g2, orientation, dataset, series, item, 227 xx, yyLow); 228 } 229 230 // add an entity for the item... 231 Shape hotspot = ShapeUtils.createLineRegion(line, 4.0f); 232 if (entities != null && hotspot.intersects(dataArea)) { 233 addEntity(entities, hotspot, dataset, series, item, 0.0, 0.0); 234 } 235 236 } 237 238 /** 239 * Draws an item label. 240 * 241 * @param g2 the graphics device. 242 * @param orientation the orientation. 243 * @param dataset the dataset. 244 * @param series the series index (zero-based). 245 * @param item the item index (zero-based). 246 * @param x the x coordinate (in Java2D space). 247 * @param y the y coordinate (in Java2D space). 248 */ 249 private void drawAdditionalItemLabel(Graphics2D g2, 250 PlotOrientation orientation, XYDataset dataset, int series, 251 int item, double x, double y) { 252 253 if (this.additionalItemLabelGenerator == null) { 254 return; 255 } 256 257 Font labelFont = getItemLabelFont(series, item); 258 Paint paint = getItemLabelPaint(series, item); 259 g2.setFont(labelFont); 260 g2.setPaint(paint); 261 String label = this.additionalItemLabelGenerator.generateLabel(dataset, 262 series, item); 263 264 ItemLabelPosition position = getNegativeItemLabelPosition(series, item); 265 Point2D anchorPoint = calculateLabelAnchorPoint( 266 position.getItemLabelAnchor(), x, y, orientation); 267 TextUtils.drawRotatedString(label, g2, 268 (float) anchorPoint.getX(), (float) anchorPoint.getY(), 269 position.getTextAnchor(), position.getAngle(), 270 position.getRotationAnchor()); 271 } 272 273 /** 274 * Tests this renderer for equality with an arbitrary object. 275 * 276 * @param obj the object ({@code null} permitted). 277 * 278 * @return A boolean. 279 */ 280 @Override 281 public boolean equals(Object obj) { 282 if (obj == this) { 283 return true; 284 } 285 if (!(obj instanceof YIntervalRenderer)) { 286 return false; 287 } 288 YIntervalRenderer that = (YIntervalRenderer) obj; 289 if (!Objects.equals(this.additionalItemLabelGenerator, that.additionalItemLabelGenerator)) { 290 return false; 291 } 292 return super.equals(obj); 293 } 294 295 /** 296 * Returns a clone of the renderer. 297 * 298 * @return A clone. 299 * 300 * @throws CloneNotSupportedException if the renderer cannot be cloned. 301 */ 302 @Override 303 public Object clone() throws CloneNotSupportedException { 304 return super.clone(); 305 } 306 307}