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 * CategoryStepRenderer.java 029 * ------------------------- 030 * 031 * (C) Copyright 2004-2006, by Brian Cole and Contributors. 032 * 033 * Original Author: Brian Cole; 034 * Contributor(s): David Gilbert (for Object Refinery Limited); 035 * 036 * $Id: CategoryStepRenderer.java,v 1.5.2.2 2006/12/01 13:57:09 mungady Exp $ 037 * 038 * Changes 039 * ------- 040 * 21-Apr-2004 : Version 1, contributed by Brian Cole (DG); 041 * 22-Apr-2004 : Fixed Checkstyle complaints (DG); 042 * 05-Nov-2004 : Modified drawItem() signature (DG); 043 * 08-Mar-2005 : Added equals() method (DG); 044 * ------------- JFREECHART 1.0.x --------------------------------------------- 045 * 30-Nov-2006 : Added checks for series visibility (DG); 046 * 047 */ 048 049 package org.jfree.chart.renderer.category; 050 051 import java.awt.Graphics2D; 052 import java.awt.geom.Line2D; 053 import java.awt.geom.Rectangle2D; 054 import java.io.Serializable; 055 056 import org.jfree.chart.axis.CategoryAxis; 057 import org.jfree.chart.axis.ValueAxis; 058 import org.jfree.chart.event.RendererChangeEvent; 059 import org.jfree.chart.plot.CategoryPlot; 060 import org.jfree.chart.plot.PlotOrientation; 061 import org.jfree.chart.renderer.xy.XYStepRenderer; 062 import org.jfree.data.category.CategoryDataset; 063 import org.jfree.util.PublicCloneable; 064 065 /** 066 * A "step" renderer similar to {@link XYStepRenderer} but 067 * that can be used with the {@link CategoryPlot} class. 068 */ 069 public class CategoryStepRenderer extends AbstractCategoryItemRenderer 070 implements Cloneable, PublicCloneable, 071 Serializable { 072 073 /** For serialization. */ 074 private static final long serialVersionUID = -5121079703118261470L; 075 076 /** The stagger width. */ 077 public static final int STAGGER_WIDTH = 5; // could make this configurable 078 079 /** 080 * A flag that controls whether or not the steps for multiple series are 081 * staggered. 082 */ 083 private boolean stagger = false; 084 085 /** A working line - need to remove this. */ 086 private transient Line2D line = new Line2D.Double(0.0, 0.0, 0.0, 0.0); 087 088 /** 089 * Creates a new renderer (stagger defaults to <code>false</code>). 090 */ 091 public CategoryStepRenderer() { 092 this(false); 093 } 094 095 /** 096 * Creates a new renderer. 097 * 098 * @param stagger should the horizontal part of the step be staggered by 099 * series? 100 */ 101 public CategoryStepRenderer(boolean stagger) { 102 this.stagger = stagger; 103 } 104 105 /** 106 * Returns the flag that controls whether the series steps are staggered. 107 * 108 * @return A boolean. 109 */ 110 public boolean getStagger() { 111 return this.stagger; 112 } 113 114 /** 115 * Sets the flag that controls whether or not the series steps are 116 * staggered and sends a {@link RendererChangeEvent} to all registered 117 * listeners. 118 * 119 * @param shouldStagger a boolean. 120 */ 121 public void setStagger(boolean shouldStagger) { 122 this.stagger = shouldStagger; 123 notifyListeners(new RendererChangeEvent(this)); 124 } 125 126 /** 127 * Draws the line. 128 * 129 * @param g2 the graphics device. 130 * @param orientation the plot orientation. 131 * @param x0 the x-coordinate for the start of the line. 132 * @param y0 the y-coordinate for the start of the line. 133 * @param x1 the x-coordinate for the end of the line. 134 * @param y1 the y-coordinate for the end of the line. 135 */ 136 protected void drawLine(Graphics2D g2, PlotOrientation orientation, 137 double x0, double y0, double x1, double y1) { 138 139 if (orientation == PlotOrientation.VERTICAL) { 140 this.line.setLine(x0, y0, x1, y1); 141 g2.draw(this.line); 142 } 143 else if (orientation == PlotOrientation.HORIZONTAL) { 144 this.line.setLine(y0, x0, y1, x1); // switch x and y 145 g2.draw(this.line); 146 } 147 // else unknown orientation (complain?) 148 } 149 150 /** 151 * Draw a single data item. 152 * 153 * @param g2 the graphics device. 154 * @param state the renderer state. 155 * @param dataArea the area in which the data is drawn. 156 * @param plot the plot. 157 * @param domainAxis the domain axis. 158 * @param rangeAxis the range axis. 159 * @param dataset the dataset. 160 * @param row the row index (zero-based). 161 * @param column the column index (zero-based). 162 * @param pass the pass index. 163 */ 164 public void drawItem(Graphics2D g2, 165 CategoryItemRendererState state, 166 Rectangle2D dataArea, 167 CategoryPlot plot, 168 CategoryAxis domainAxis, 169 ValueAxis rangeAxis, 170 CategoryDataset dataset, 171 int row, 172 int column, 173 int pass) { 174 175 // do nothing if item is not visible 176 if (!getItemVisible(row, column)) { 177 return; 178 } 179 180 Number value = dataset.getValue(row, column); 181 if (value == null) { 182 return; 183 } 184 PlotOrientation orientation = plot.getOrientation(); 185 186 // current data point... 187 double x1s = domainAxis.getCategoryStart( 188 column, getColumnCount(), dataArea, plot.getDomainAxisEdge() 189 ); 190 double x1 = domainAxis.getCategoryMiddle( 191 column, getColumnCount(), dataArea, plot.getDomainAxisEdge() 192 ); 193 double x1e = 2 * x1 - x1s; // or: x1s + 2*(x1-x1s) 194 double y1 = rangeAxis.valueToJava2D( 195 value.doubleValue(), dataArea, plot.getRangeAxisEdge() 196 ); 197 g2.setPaint(getItemPaint(row, column)); 198 g2.setStroke(getItemStroke(row, column)); 199 200 if (column != 0) { 201 Number previousValue = dataset.getValue(row, column - 1); 202 if (previousValue != null) { 203 // previous data point... 204 double previous = previousValue.doubleValue(); 205 double x0s = domainAxis.getCategoryStart( 206 column - 1, getColumnCount(), dataArea, 207 plot.getDomainAxisEdge() 208 ); 209 double x0 = domainAxis.getCategoryMiddle( 210 column - 1, getColumnCount(), dataArea, 211 plot.getDomainAxisEdge() 212 ); 213 double x0e = 2 * x0 - x0s; // or: x0s + 2*(x0-x0s) 214 double y0 = rangeAxis.valueToJava2D( 215 previous, dataArea, plot.getRangeAxisEdge() 216 ); 217 if (getStagger()) { 218 int xStagger = row * STAGGER_WIDTH; 219 if (xStagger > (x1s - x0e)) { 220 xStagger = (int) (x1s - x0e); 221 } 222 x1s = x0e + xStagger; 223 } 224 drawLine(g2, orientation, x0e, y0, x1s, y0); 225 // extend x0's flat bar 226 227 drawLine(g2, orientation, x1s, y0, x1s, y1); // upright bar 228 } 229 } 230 drawLine(g2, orientation, x1s, y1, x1e, y1); // x1's flat bar 231 232 // draw the item labels if there are any... 233 if (isItemLabelVisible(row, column)) { 234 drawItemLabel( 235 g2, orientation, dataset, row, column, x1, y1, 236 (value.doubleValue() < 0.0) 237 ); 238 } 239 /* This is how LineAndShapeRenderer.drawItem() handles tips and URLs, but 240 I omit it due to time pressure. It shouldn't be hard to put back 241 in. 242 243 // collect entity and tool tip information... 244 if (state.getInfo() != null) { 245 EntityCollection entities = 246 state.getInfo().getOwner().getEntityCollection(); 247 if (entities != null && shape != null) { 248 String tip = null; 249 CategoryItemLabelGenerator generator = 250 getItemLabelGenerator(row, column); 251 if (generator != null) { 252 tip = generator.generateToolTip(dataset, row, column); 253 } 254 String url = null; 255 if (getItemURLGenerator(row, column) != null) 256 url = getItemURLGenerator(row, column).generateURL(dataset, row, 257 column); 258 } 259 CategoryItemEntity entity = new CategoryItemEntity( 260 shape, tip, url, dataset, row, 261 dataset.getColumnKey(column), column); 262 entities.addEntity(entity); 263 } 264 } 265 */ 266 267 } 268 269 /** 270 * Tests this renderer for equality with an arbitrary object. 271 * 272 * @param obj the object (<code>null</code> permitted). 273 * 274 * @return A boolean. 275 */ 276 public boolean equals(Object obj) { 277 if (obj == this) { 278 return true; 279 } 280 if (!(obj instanceof CategoryStepRenderer)) { 281 return false; 282 } 283 if (!super.equals(obj)) { 284 return false; 285 } 286 CategoryStepRenderer that = (CategoryStepRenderer) obj; 287 if (this.stagger != that.stagger) { 288 return false; 289 } 290 return true; 291 } 292 293 }