001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2006, by Object Refinery Limited 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 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * ----------------- 028 * AreaRenderer.java 029 * ----------------- 030 * (C) Copyright 2002-2006, by Jon Iles and Contributors. 031 * 032 * Original Author: Jon Iles; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Christian W. Zuckschwerdt; 035 * 036 * $Id: AreaRenderer.java,v 1.6.2.6 2006/12/01 13:57:09 mungady Exp $ 037 * 038 * Changes: 039 * -------- 040 * 21-May-2002 : Version 1, contributed by John Iles (DG); 041 * 29-May-2002 : Now extends AbstractCategoryItemRenderer (DG); 042 * 11-Jun-2002 : Updated Javadoc comments (DG); 043 * 25-Jun-2002 : Removed unnecessary imports (DG); 044 * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG); 045 * 10-Oct-2002 : Added constructors and basic entity support (DG); 046 * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and 047 * CategoryToolTipGenerator interface (DG); 048 * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG); 049 * 06-Nov-2002 : Renamed drawCategoryItem() --> drawItem() and now using axis 050 * for category spacing. Renamed AreaCategoryItemRenderer 051 * --> AreaRenderer (DG); 052 * 17-Jan-2003 : Moved plot classes into a separate package (DG); 053 * 25-Mar-2003 : Implemented Serializable (DG); 054 * 10-Apr-2003 : Changed CategoryDataset to KeyedValues2DDataset in 055 * drawItem() method (DG); 056 * 12-May-2003 : Modified to take into account the plot orientation (DG); 057 * 30-Jul-2003 : Modified entity constructor (CZ); 058 * 13-Aug-2003 : Implemented Cloneable (DG); 059 * 07-Oct-2003 : Added renderer state (DG); 060 * 05-Nov-2004 : Modified drawItem() signature (DG); 061 * 20-Apr-2005 : Apply tooltips and URLs to legend items (DG); 062 * 09-Jun-2005 : Use addItemEntity() method from superclass (DG); 063 * ------------- JFREECHART 1.0.x --------------------------------------------- 064 * 11-Oct-2006 : Fixed bug in equals() method (DG); 065 * 30-Nov-2006 : Added checks for series visibility (DG); 066 * 067 */ 068 069 package org.jfree.chart.renderer.category; 070 071 import java.awt.Graphics2D; 072 import java.awt.Paint; 073 import java.awt.Shape; 074 import java.awt.Stroke; 075 import java.awt.geom.GeneralPath; 076 import java.awt.geom.Rectangle2D; 077 import java.io.Serializable; 078 079 import org.jfree.chart.LegendItem; 080 import org.jfree.chart.axis.CategoryAxis; 081 import org.jfree.chart.axis.ValueAxis; 082 import org.jfree.chart.entity.EntityCollection; 083 import org.jfree.chart.event.RendererChangeEvent; 084 import org.jfree.chart.plot.CategoryPlot; 085 import org.jfree.chart.plot.PlotOrientation; 086 import org.jfree.chart.renderer.AreaRendererEndType; 087 import org.jfree.data.category.CategoryDataset; 088 import org.jfree.ui.RectangleEdge; 089 import org.jfree.util.PublicCloneable; 090 091 /** 092 * A category item renderer that draws area charts. You can use this renderer 093 * with the {@link org.jfree.chart.plot.CategoryPlot} class. 094 */ 095 public class AreaRenderer extends AbstractCategoryItemRenderer 096 implements Cloneable, PublicCloneable, Serializable { 097 098 /** For serialization. */ 099 private static final long serialVersionUID = -4231878281385812757L; 100 101 /** A flag that controls how the ends of the areas are drawn. */ 102 private AreaRendererEndType endType; 103 104 /** 105 * Creates a new renderer. 106 */ 107 public AreaRenderer() { 108 super(); 109 this.endType = AreaRendererEndType.TAPER; 110 } 111 112 /** 113 * Returns a token that controls how the renderer draws the end points. 114 * The default value is {@link AreaRendererEndType#TAPER}. 115 * 116 * @return The end type (never <code>null</code>). 117 * 118 * @see #setEndType 119 */ 120 public AreaRendererEndType getEndType() { 121 return this.endType; 122 } 123 124 /** 125 * Sets a token that controls how the renderer draws the end points, and 126 * sends a {@link RendererChangeEvent} to all registered listeners. 127 * 128 * @param type the end type (<code>null</code> not permitted). 129 * 130 * @see #getEndType() 131 */ 132 public void setEndType(AreaRendererEndType type) { 133 if (type == null) { 134 throw new IllegalArgumentException("Null 'type' argument."); 135 } 136 this.endType = type; 137 notifyListeners(new RendererChangeEvent(this)); 138 } 139 140 /** 141 * Returns a legend item for a series. 142 * 143 * @param datasetIndex the dataset index (zero-based). 144 * @param series the series index (zero-based). 145 * 146 * @return The legend item. 147 */ 148 public LegendItem getLegendItem(int datasetIndex, int series) { 149 150 // if there is no plot, there is no dataset to access... 151 CategoryPlot cp = getPlot(); 152 if (cp == null) { 153 return null; 154 } 155 156 // check that a legend item needs to be displayed... 157 if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) { 158 return null; 159 } 160 161 CategoryDataset dataset; 162 dataset = cp.getDataset(datasetIndex); 163 String label = getLegendItemLabelGenerator().generateLabel(dataset, 164 series); 165 String description = label; 166 String toolTipText = null; 167 if (getLegendItemToolTipGenerator() != null) { 168 toolTipText = getLegendItemToolTipGenerator().generateLabel( 169 dataset, series); 170 } 171 String urlText = null; 172 if (getLegendItemURLGenerator() != null) { 173 urlText = getLegendItemURLGenerator().generateLabel(dataset, 174 series); 175 } 176 Shape shape = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0); 177 Paint paint = getSeriesPaint(series); 178 Paint outlinePaint = getSeriesOutlinePaint(series); 179 Stroke outlineStroke = getSeriesOutlineStroke(series); 180 181 return new LegendItem(label, description, toolTipText, urlText, 182 shape, paint, outlineStroke, outlinePaint); 183 184 } 185 186 /** 187 * Draw a single data item. 188 * 189 * @param g2 the graphics device. 190 * @param state the renderer state. 191 * @param dataArea the data plot area. 192 * @param plot the plot. 193 * @param domainAxis the domain axis. 194 * @param rangeAxis the range axis. 195 * @param dataset the dataset. 196 * @param row the row index (zero-based). 197 * @param column the column index (zero-based). 198 * @param pass the pass index. 199 */ 200 public void drawItem(Graphics2D g2, 201 CategoryItemRendererState state, 202 Rectangle2D dataArea, 203 CategoryPlot plot, 204 CategoryAxis domainAxis, 205 ValueAxis rangeAxis, 206 CategoryDataset dataset, 207 int row, 208 int column, 209 int pass) { 210 211 // do nothing if item is not visible 212 if (!getItemVisible(row, column)) { 213 return; 214 } 215 216 // plot non-null values only... 217 Number value = dataset.getValue(row, column); 218 if (value != null) { 219 PlotOrientation orientation = plot.getOrientation(); 220 RectangleEdge axisEdge = plot.getDomainAxisEdge(); 221 int count = dataset.getColumnCount(); 222 float x0 = (float) domainAxis.getCategoryStart(column, count, 223 dataArea, axisEdge); 224 float x1 = (float) domainAxis.getCategoryMiddle(column, count, 225 dataArea, axisEdge); 226 float x2 = (float) domainAxis.getCategoryEnd(column, count, 227 dataArea, axisEdge); 228 229 x0 = Math.round(x0); 230 x1 = Math.round(x1); 231 x2 = Math.round(x2); 232 233 if (this.endType == AreaRendererEndType.TRUNCATE) { 234 if (column == 0) { 235 x0 = x1; 236 } 237 else if (column == getColumnCount() - 1) { 238 x2 = x1; 239 } 240 } 241 242 double yy1 = value.doubleValue(); 243 244 double yy0 = 0.0; 245 if (column > 0) { 246 Number n0 = dataset.getValue(row, column - 1); 247 if (n0 != null) { 248 yy0 = (n0.doubleValue() + yy1) / 2.0; 249 } 250 } 251 252 double yy2 = 0.0; 253 if (column < dataset.getColumnCount() - 1) { 254 Number n2 = dataset.getValue(row, column + 1); 255 if (n2 != null) { 256 yy2 = (n2.doubleValue() + yy1) / 2.0; 257 } 258 } 259 260 RectangleEdge edge = plot.getRangeAxisEdge(); 261 float y0 = (float) rangeAxis.valueToJava2D(yy0, dataArea, edge); 262 float y1 = (float) rangeAxis.valueToJava2D(yy1, dataArea, edge); 263 float y2 = (float) rangeAxis.valueToJava2D(yy2, dataArea, edge); 264 float yz = (float) rangeAxis.valueToJava2D(0.0, dataArea, edge); 265 266 g2.setPaint(getItemPaint(row, column)); 267 g2.setStroke(getItemStroke(row, column)); 268 269 GeneralPath area = new GeneralPath(); 270 271 if (orientation == PlotOrientation.VERTICAL) { 272 area.moveTo(x0, yz); 273 area.lineTo(x0, y0); 274 area.lineTo(x1, y1); 275 area.lineTo(x2, y2); 276 area.lineTo(x2, yz); 277 } 278 else if (orientation == PlotOrientation.HORIZONTAL) { 279 area.moveTo(yz, x0); 280 area.lineTo(y0, x0); 281 area.lineTo(y1, x1); 282 area.lineTo(y2, x2); 283 area.lineTo(yz, x2); 284 } 285 area.closePath(); 286 287 g2.setPaint(getItemPaint(row, column)); 288 g2.fill(area); 289 290 // draw the item labels if there are any... 291 if (isItemLabelVisible(row, column)) { 292 drawItemLabel(g2, orientation, dataset, row, column, x1, y1, 293 (value.doubleValue() < 0.0)); 294 } 295 296 // add an item entity, if this information is being collected 297 EntityCollection entities = state.getEntityCollection(); 298 if (entities != null) { 299 addItemEntity(entities, dataset, row, column, area); 300 } 301 } 302 303 } 304 305 /** 306 * Tests this instance for equality with an arbitrary object. 307 * 308 * @param obj the object to test (<code>null</code> permitted). 309 * 310 * @return A boolean. 311 */ 312 public boolean equals(Object obj) { 313 if (obj == this) { 314 return true; 315 } 316 if (!(obj instanceof AreaRenderer)) { 317 return false; 318 } 319 AreaRenderer that = (AreaRenderer) obj; 320 if (!this.endType.equals(that.endType)) { 321 return false; 322 } 323 return super.equals(obj); 324 } 325 326 /** 327 * Returns an independent copy of the renderer. 328 * 329 * @return A clone. 330 * 331 * @throws CloneNotSupportedException should not happen. 332 */ 333 public Object clone() throws CloneNotSupportedException { 334 return super.clone(); 335 } 336 337 }