001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2005, 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, 2005, 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.1 2005/10/25 20:54:16 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 * 045 */ 046 047 package org.jfree.chart.renderer.category; 048 049 import java.awt.Graphics2D; 050 import java.awt.geom.Line2D; 051 import java.awt.geom.Rectangle2D; 052 import java.io.Serializable; 053 054 import org.jfree.chart.axis.CategoryAxis; 055 import org.jfree.chart.axis.ValueAxis; 056 import org.jfree.chart.event.RendererChangeEvent; 057 import org.jfree.chart.plot.CategoryPlot; 058 import org.jfree.chart.plot.PlotOrientation; 059 import org.jfree.chart.renderer.xy.XYStepRenderer; 060 import org.jfree.data.category.CategoryDataset; 061 import org.jfree.util.PublicCloneable; 062 063 /** 064 * A "step" renderer similar to {@link XYStepRenderer} but 065 * that can be used with the {@link CategoryPlot} class. 066 * 067 * @author Brian Cole 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 Number value = dataset.getValue(row, column); 176 if (value == null) { 177 return; 178 } 179 PlotOrientation orientation = plot.getOrientation(); 180 181 // current data point... 182 double x1s = domainAxis.getCategoryStart( 183 column, getColumnCount(), dataArea, plot.getDomainAxisEdge() 184 ); 185 double x1 = domainAxis.getCategoryMiddle( 186 column, getColumnCount(), dataArea, plot.getDomainAxisEdge() 187 ); 188 double x1e = 2 * x1 - x1s; // or: x1s + 2*(x1-x1s) 189 double y1 = rangeAxis.valueToJava2D( 190 value.doubleValue(), dataArea, plot.getRangeAxisEdge() 191 ); 192 g2.setPaint(getItemPaint(row, column)); 193 g2.setStroke(getItemStroke(row, column)); 194 195 if (column != 0) { 196 Number previousValue = dataset.getValue(row, column - 1); 197 if (previousValue != null) { 198 // previous data point... 199 double previous = previousValue.doubleValue(); 200 double x0s = domainAxis.getCategoryStart( 201 column - 1, getColumnCount(), dataArea, 202 plot.getDomainAxisEdge() 203 ); 204 double x0 = domainAxis.getCategoryMiddle( 205 column - 1, getColumnCount(), dataArea, 206 plot.getDomainAxisEdge() 207 ); 208 double x0e = 2 * x0 - x0s; // or: x0s + 2*(x0-x0s) 209 double y0 = rangeAxis.valueToJava2D( 210 previous, dataArea, plot.getRangeAxisEdge() 211 ); 212 if (getStagger()) { 213 int xStagger = row * STAGGER_WIDTH; 214 if (xStagger > (x1s - x0e)) { 215 xStagger = (int) (x1s - x0e); 216 } 217 x1s = x0e + xStagger; 218 } 219 drawLine(g2, orientation, x0e, y0, x1s, y0); 220 // extend x0's flat bar 221 222 drawLine(g2, orientation, x1s, y0, x1s, y1); // upright bar 223 } 224 } 225 drawLine(g2, orientation, x1s, y1, x1e, y1); // x1's flat bar 226 227 // draw the item labels if there are any... 228 if (isItemLabelVisible(row, column)) { 229 drawItemLabel( 230 g2, orientation, dataset, row, column, x1, y1, 231 (value.doubleValue() < 0.0) 232 ); 233 } 234 /* This is how LineAndShapeRenderer.drawItem() handles tips and URLs, but 235 I omit it due to time pressure. It shouldn't be hard to put back 236 in. 237 238 // collect entity and tool tip information... 239 if (state.getInfo() != null) { 240 EntityCollection entities = 241 state.getInfo().getOwner().getEntityCollection(); 242 if (entities != null && shape != null) { 243 String tip = null; 244 CategoryItemLabelGenerator generator = 245 getItemLabelGenerator(row, column); 246 if (generator != null) { 247 tip = generator.generateToolTip(dataset, row, column); 248 } 249 String url = null; 250 if (getItemURLGenerator(row, column) != null) 251 url = getItemURLGenerator(row, column).generateURL(dataset, row, 252 column); 253 } 254 CategoryItemEntity entity = new CategoryItemEntity( 255 shape, tip, url, dataset, row, 256 dataset.getColumnKey(column), column); 257 entities.addEntity(entity); 258 } 259 } 260 */ 261 262 } 263 264 /** 265 * Tests this renderer for equality with an arbitrary object. 266 * 267 * @param obj the object (<code>null</code> permitted). 268 * 269 * @return A boolean. 270 */ 271 public boolean equals(Object obj) { 272 if (obj == this) { 273 return true; 274 } 275 if (!(obj instanceof CategoryStepRenderer)) { 276 return false; 277 } 278 if (!super.equals(obj)) { 279 return false; 280 } 281 CategoryStepRenderer that = (CategoryStepRenderer) obj; 282 if (this.stagger != that.stagger) { 283 return false; 284 } 285 return true; 286 } 287 288 }