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 * AreaRenderer.java 029 * ----------------- 030 * (C) Copyright 2002-present, by Jon Iles and Contributors. 031 * 032 * Original Author: Jon Iles; 033 * Contributor(s): David Gilbert; 034 * Christian W. Zuckschwerdt; 035 * 036 */ 037 038package org.jfree.chart.renderer.category; 039 040import java.awt.Graphics2D; 041import java.awt.Paint; 042import java.awt.Shape; 043import java.awt.Stroke; 044import java.awt.geom.GeneralPath; 045import java.awt.geom.Rectangle2D; 046import java.io.Serializable; 047 048import org.jfree.chart.LegendItem; 049import org.jfree.chart.axis.CategoryAxis; 050import org.jfree.chart.axis.ValueAxis; 051import org.jfree.chart.entity.EntityCollection; 052import org.jfree.chart.event.RendererChangeEvent; 053import org.jfree.chart.plot.CategoryPlot; 054import org.jfree.chart.plot.PlotOrientation; 055import org.jfree.chart.renderer.AreaRendererEndType; 056import org.jfree.chart.ui.RectangleEdge; 057import org.jfree.chart.util.Args; 058import org.jfree.chart.util.PublicCloneable; 059import org.jfree.data.category.CategoryDataset; 060 061/** 062 * A category item renderer that draws area charts. You can use this renderer 063 * with the {@link CategoryPlot} class. The example shown here is generated 064 * by the {@code AreaChartDemo1.java} program included in the JFreeChart 065 * Demo Collection: 066 * <br><br> 067 * <img src="doc-files/AreaRendererSample.png" alt="AreaRendererSample.png"> 068 */ 069public class AreaRenderer extends AbstractCategoryItemRenderer 070 implements Cloneable, PublicCloneable, Serializable { 071 072 /** For serialization. */ 073 private static final long serialVersionUID = -4231878281385812757L; 074 075 /** A flag that controls how the ends of the areas are drawn. */ 076 private AreaRendererEndType endType; 077 078 /** 079 * Creates a new renderer. 080 */ 081 public AreaRenderer() { 082 super(); 083 this.endType = AreaRendererEndType.TAPER; 084 setDefaultLegendShape(new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0)); 085 } 086 087 /** 088 * Returns a token that controls how the renderer draws the end points. 089 * The default value is {@link AreaRendererEndType#TAPER}. 090 * 091 * @return The end type (never {@code null}). 092 * 093 * @see #setEndType 094 */ 095 public AreaRendererEndType getEndType() { 096 return this.endType; 097 } 098 099 /** 100 * Sets a token that controls how the renderer draws the end points, and 101 * sends a {@link RendererChangeEvent} to all registered listeners. 102 * 103 * @param type the end type ({@code null} not permitted). 104 * 105 * @see #getEndType() 106 */ 107 public void setEndType(AreaRendererEndType type) { 108 Args.nullNotPermitted(type, "type"); 109 this.endType = type; 110 fireChangeEvent(); 111 } 112 113 /** 114 * Returns a legend item for a series. 115 * 116 * @param datasetIndex the dataset index (zero-based). 117 * @param series the series index (zero-based). 118 * 119 * @return The legend item. 120 */ 121 @Override 122 public LegendItem getLegendItem(int datasetIndex, int series) { 123 124 // if there is no plot, there is no dataset to access... 125 CategoryPlot cp = getPlot(); 126 if (cp == null) { 127 return null; 128 } 129 130 // check that a legend item needs to be displayed... 131 if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) { 132 return null; 133 } 134 135 CategoryDataset dataset = cp.getDataset(datasetIndex); 136 String label = getLegendItemLabelGenerator().generateLabel(dataset, 137 series); 138 String description = label; 139 String toolTipText = null; 140 if (getLegendItemToolTipGenerator() != null) { 141 toolTipText = getLegendItemToolTipGenerator().generateLabel( 142 dataset, series); 143 } 144 String urlText = null; 145 if (getLegendItemURLGenerator() != null) { 146 urlText = getLegendItemURLGenerator().generateLabel(dataset, 147 series); 148 } 149 Shape shape = lookupLegendShape(series); 150 Paint paint = lookupSeriesPaint(series); 151 Paint outlinePaint = lookupSeriesOutlinePaint(series); 152 Stroke outlineStroke = lookupSeriesOutlineStroke(series); 153 154 LegendItem result = new LegendItem(label, description, toolTipText, 155 urlText, shape, paint, outlineStroke, outlinePaint); 156 result.setLabelFont(lookupLegendTextFont(series)); 157 Paint labelPaint = lookupLegendTextPaint(series); 158 if (labelPaint != null) { 159 result.setLabelPaint(labelPaint); 160 } 161 result.setDataset(dataset); 162 result.setDatasetIndex(datasetIndex); 163 result.setSeriesKey(dataset.getRowKey(series)); 164 result.setSeriesIndex(series); 165 return result; 166 167 } 168 169 /** 170 * Draw a single data item. 171 * 172 * @param g2 the graphics device. 173 * @param state the renderer state. 174 * @param dataArea the data plot area. 175 * @param plot the plot. 176 * @param domainAxis the domain axis. 177 * @param rangeAxis the range axis. 178 * @param dataset the dataset. 179 * @param row the row index (zero-based). 180 * @param column the column index (zero-based). 181 * @param pass the pass index. 182 */ 183 @Override 184 public void drawItem(Graphics2D g2, CategoryItemRendererState state, 185 Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, 186 ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, 187 int pass) { 188 189 // do nothing if item is not visible or null 190 if (!getItemVisible(row, column)) { 191 return; 192 } 193 Number value = dataset.getValue(row, column); 194 if (value == null) { 195 return; 196 } 197 PlotOrientation orientation = plot.getOrientation(); 198 RectangleEdge axisEdge = plot.getDomainAxisEdge(); 199 int count = dataset.getColumnCount(); 200 float x0 = (float) domainAxis.getCategoryStart(column, count, dataArea, 201 axisEdge); 202 float x1 = (float) domainAxis.getCategoryMiddle(column, count, 203 dataArea, axisEdge); 204 float x2 = (float) domainAxis.getCategoryEnd(column, count, dataArea, 205 axisEdge); 206 207 x0 = Math.round(x0); 208 x1 = Math.round(x1); 209 x2 = Math.round(x2); 210 211 if (this.endType == AreaRendererEndType.TRUNCATE) { 212 if (column == 0) { 213 x0 = x1; 214 } 215 else if (column == getColumnCount() - 1) { 216 x2 = x1; 217 } 218 } 219 220 double yy1 = value.doubleValue(); 221 222 double yy0 = 0.0; 223 if (this.endType == AreaRendererEndType.LEVEL) { 224 yy0 = yy1; 225 } 226 if (column > 0) { 227 Number n0 = dataset.getValue(row, column - 1); 228 if (n0 != null) { 229 yy0 = (n0.doubleValue() + yy1) / 2.0; 230 } 231 } 232 233 double yy2 = 0.0; 234 if (column < dataset.getColumnCount() - 1) { 235 Number n2 = dataset.getValue(row, column + 1); 236 if (n2 != null) { 237 yy2 = (n2.doubleValue() + yy1) / 2.0; 238 } 239 } 240 else if (this.endType == AreaRendererEndType.LEVEL) { 241 yy2 = yy1; 242 } 243 244 RectangleEdge edge = plot.getRangeAxisEdge(); 245 float y0 = (float) rangeAxis.valueToJava2D(yy0, dataArea, edge); 246 float y1 = (float) rangeAxis.valueToJava2D(yy1, dataArea, edge); 247 float y2 = (float) rangeAxis.valueToJava2D(yy2, dataArea, edge); 248 float yz = (float) rangeAxis.valueToJava2D(0.0, dataArea, edge); 249 double labelXX = x1; 250 double labelYY = y1; 251 g2.setPaint(getItemPaint(row, column)); 252 g2.setStroke(getItemStroke(row, column)); 253 254 GeneralPath area = new GeneralPath(); 255 256 if (orientation == PlotOrientation.VERTICAL) { 257 area.moveTo(x0, yz); 258 area.lineTo(x0, y0); 259 area.lineTo(x1, y1); 260 area.lineTo(x2, y2); 261 area.lineTo(x2, yz); 262 } 263 else if (orientation == PlotOrientation.HORIZONTAL) { 264 area.moveTo(yz, x0); 265 area.lineTo(y0, x0); 266 area.lineTo(y1, x1); 267 area.lineTo(y2, x2); 268 area.lineTo(yz, x2); 269 double temp = labelXX; 270 labelXX = labelYY; 271 labelYY = temp; 272 } 273 area.closePath(); 274 275 g2.setPaint(getItemPaint(row, column)); 276 g2.fill(area); 277 278 // draw the item labels if there are any... 279 if (isItemLabelVisible(row, column)) { 280 drawItemLabel(g2, orientation, dataset, row, column, labelXX, 281 labelYY, (value.doubleValue() < 0.0)); 282 } 283 284 // submit the current data point as a crosshair candidate 285 int datasetIndex = plot.indexOf(dataset); 286 updateCrosshairValues(state.getCrosshairState(), 287 dataset.getRowKey(row), dataset.getColumnKey(column), yy1, 288 datasetIndex, x1, y1, orientation); 289 290 // add an item entity, if this information is being collected 291 EntityCollection entities = state.getEntityCollection(); 292 if (entities != null) { 293 addItemEntity(entities, dataset, row, column, area); 294 } 295 296 } 297 298 /** 299 * Tests this instance for equality with an arbitrary object. 300 * 301 * @param obj the object to test ({@code null} permitted). 302 * 303 * @return A boolean. 304 */ 305 @Override 306 public boolean equals(Object obj) { 307 if (obj == this) { 308 return true; 309 } 310 if (!(obj instanceof AreaRenderer)) { 311 return false; 312 } 313 AreaRenderer that = (AreaRenderer) obj; 314 if (!this.endType.equals(that.endType)) { 315 return false; 316 } 317 return super.equals(obj); 318 } 319 320 /** 321 * Returns an independent copy of the renderer. 322 * 323 * @return A clone. 324 * 325 * @throws CloneNotSupportedException should not happen. 326 */ 327 @Override 328 public Object clone() throws CloneNotSupportedException { 329 return super.clone(); 330 } 331 332}