1. /* ===========================================================
  2. * JFreeChart : a free chart library for the Java(tm) platform
  3. * ===========================================================
  4. *
  5. * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
  6. *
  7. * Project Info: http://www.jfree.org/jfreechart/index.html
  8. *
  9. * This library is free software; you can redistribute it and/or modify it
  10. * under the terms of the GNU Lesser General Public License as published by
  11. * the Free Software Foundation; either version 2.1 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful, but
  15. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  17. * License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public License
  20. * along with this library; if not, write to the Free Software Foundation,
  21. * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  22. *
  23. * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  24. * in the United States and other countries.]
  25. *
  26. * -------------------------
  27. * CategoryStepRenderer.java
  28. * -------------------------
  29. *
  30. * (C) Copyright 2004, 2005, by Brian Cole and Contributors.
  31. *
  32. * Original Author: Brian Cole;
  33. * Contributor(s): David Gilbert (for Object Refinery Limited);
  34. *
  35. * $Id: CategoryStepRenderer.java,v 1.3 2005/03/08 15:52:08 mungady Exp $
  36. *
  37. * Changes
  38. * -------
  39. * 21-Apr-2004 : Version 1, contributed by Brian Cole (DG);
  40. * 22-Apr-2004 : Fixed Checkstyle complaints (DG);
  41. * 05-Nov-2004 : Modified drawItem() signature (DG);
  42. * 08-Mar-2005 : Added equals() method (DG);
  43. *
  44. */
  45. package org.jfree.chart.renderer.category;
  46. import java.awt.Graphics2D;
  47. import java.awt.geom.Line2D;
  48. import java.awt.geom.Rectangle2D;
  49. import java.io.Serializable;
  50. import org.jfree.chart.axis.CategoryAxis;
  51. import org.jfree.chart.axis.ValueAxis;
  52. import org.jfree.chart.event.RendererChangeEvent;
  53. import org.jfree.chart.plot.CategoryPlot;
  54. import org.jfree.chart.plot.PlotOrientation;
  55. import org.jfree.chart.renderer.xy.XYStepRenderer;
  56. import org.jfree.data.category.CategoryDataset;
  57. import org.jfree.util.PublicCloneable;
  58. /**
  59. * A "step" renderer similar to {@link XYStepRenderer} but
  60. * that can be used with the {@link CategoryPlot} class.
  61. *
  62. * @author Brian Cole
  63. */
  64. public class CategoryStepRenderer extends AbstractCategoryItemRenderer
  65. implements Cloneable, PublicCloneable,
  66. Serializable {
  67. /** The stagger width. */
  68. public static final int STAGGER_WIDTH = 5; // could make this configurable...
  69. /**
  70. * A flag that controls whether or not the steps for multiple series are
  71. * staggered.
  72. */
  73. private boolean stagger = false;
  74. /** A working line - need to remove this. */
  75. private transient Line2D line = new Line2D.Double(0.0, 0.0, 0.0, 0.0);
  76. /**
  77. * Creates a new renderer (stagger defaults to <code>false</code>).
  78. */
  79. public CategoryStepRenderer() {
  80. this(false);
  81. }
  82. /**
  83. * Creates a new renderer.
  84. *
  85. * @param stagger should the horizontal part of the step be staggered by
  86. * series?
  87. */
  88. public CategoryStepRenderer(boolean stagger) {
  89. this.stagger = stagger;
  90. }
  91. /**
  92. * Returns the flag that controls whether the series steps are staggered.
  93. *
  94. * @return A boolean.
  95. */
  96. public boolean getStagger() {
  97. return this.stagger;
  98. }
  99. /**
  100. * Sets the flag that controls whether or not the series steps are
  101. * staggered and sends a {@link RendererChangeEvent} to all registered
  102. * listeners.
  103. *
  104. * @param shouldStagger a boolean.
  105. */
  106. public void setStagger(boolean shouldStagger) {
  107. this.stagger = shouldStagger;
  108. notifyListeners(new RendererChangeEvent(this));
  109. }
  110. /**
  111. * Draws the line.
  112. *
  113. * @param g2 the graphics device.
  114. * @param orientation the plot orientation.
  115. * @param x0 the x-coordinate for the start of the line.
  116. * @param y0 the y-coordinate for the start of the line.
  117. * @param x1 the x-coordinate for the end of the line.
  118. * @param y1 the y-coordinate for the end of the line.
  119. */
  120. protected void drawLine(Graphics2D g2, PlotOrientation orientation,
  121. double x0, double y0, double x1, double y1) {
  122. if (orientation == PlotOrientation.VERTICAL) {
  123. this.line.setLine(x0, y0, x1, y1);
  124. g2.draw(this.line);
  125. }
  126. else if (orientation == PlotOrientation.HORIZONTAL) {
  127. this.line.setLine(y0, x0, y1, x1); // switch x and y
  128. g2.draw(this.line);
  129. }
  130. // else unknown orientation (complain?)
  131. }
  132. /**
  133. * Draw a single data item.
  134. *
  135. * @param g2 the graphics device.
  136. * @param state the renderer state.
  137. * @param dataArea the area in which the data is drawn.
  138. * @param plot the plot.
  139. * @param domainAxis the domain axis.
  140. * @param rangeAxis the range axis.
  141. * @param dataset the dataset.
  142. * @param row the row index (zero-based).
  143. * @param column the column index (zero-based).
  144. * @param pass the pass index.
  145. */
  146. public void drawItem(Graphics2D g2,
  147. CategoryItemRendererState state,
  148. Rectangle2D dataArea,
  149. CategoryPlot plot,
  150. CategoryAxis domainAxis,
  151. ValueAxis rangeAxis,
  152. CategoryDataset dataset,
  153. int row,
  154. int column,
  155. int pass) {
  156. Number value = dataset.getValue(row, column);
  157. if (value == null) {
  158. return;
  159. }
  160. PlotOrientation orientation = plot.getOrientation();
  161. // current data point...
  162. double x1s = domainAxis.getCategoryStart(
  163. column, getColumnCount(), dataArea, plot.getDomainAxisEdge()
  164. );
  165. double x1 = domainAxis.getCategoryMiddle(
  166. column, getColumnCount(), dataArea, plot.getDomainAxisEdge()
  167. );
  168. double x1e = 2 * x1 - x1s; // or: x1s + 2*(x1-x1s)
  169. double y1 = rangeAxis.valueToJava2D(
  170. value.doubleValue(), dataArea, plot.getRangeAxisEdge()
  171. );
  172. g2.setPaint(getItemPaint(row, column));
  173. g2.setStroke(getItemStroke(row, column));
  174. if (column != 0) {
  175. Number previousValue = dataset.getValue(row, column - 1);
  176. if (previousValue != null) {
  177. // previous data point...
  178. double previous = previousValue.doubleValue();
  179. double x0s = domainAxis.getCategoryStart(
  180. column - 1, getColumnCount(), dataArea,
  181. plot.getDomainAxisEdge()
  182. );
  183. double x0 = domainAxis.getCategoryMiddle(
  184. column - 1, getColumnCount(), dataArea,
  185. plot.getDomainAxisEdge()
  186. );
  187. double x0e = 2 * x0 - x0s; // or: x0s + 2*(x0-x0s)
  188. double y0 = rangeAxis.valueToJava2D(
  189. previous, dataArea, plot.getRangeAxisEdge()
  190. );
  191. if (getStagger()) {
  192. int xStagger = row * STAGGER_WIDTH;
  193. if (xStagger > (x1s - x0e)) {
  194. xStagger = (int) (x1s - x0e);
  195. }
  196. x1s = x0e + xStagger;
  197. }
  198. drawLine(g2, orientation, x0e, y0, x1s, y0);
  199. // extend x0's flat bar
  200. drawLine(g2, orientation, x1s, y0, x1s, y1); // upright bar
  201. }
  202. }
  203. drawLine(g2, orientation, x1s, y1, x1e, y1); // x1's flat bar
  204. // draw the item labels if there are any...
  205. if (isItemLabelVisible(row, column)) {
  206. drawItemLabel(
  207. g2, orientation, dataset, row, column, x1, y1,
  208. (value.doubleValue() < 0.0)
  209. );
  210. }
  211. /* This is how LineAndShapeRenderer.drawItem() handles tips and URLs, but
  212. I omit it due to time pressure. It shouldn't be hard to put back
  213. in.
  214. // collect entity and tool tip information...
  215. if (state.getInfo() != null) {
  216. EntityCollection entities =
  217. state.getInfo().getOwner().getEntityCollection();
  218. if (entities != null && shape != null) {
  219. String tip = null;
  220. CategoryItemLabelGenerator generator =
  221. getItemLabelGenerator(row, column);
  222. if (generator != null) {
  223. tip = generator.generateToolTip(dataset, row, column);
  224. }
  225. String url = null;
  226. if (getItemURLGenerator(row, column) != null)
  227. url = getItemURLGenerator(row, column).generateURL(dataset, row,
  228. column);
  229. }
  230. CategoryItemEntity entity = new CategoryItemEntity(
  231. shape, tip, url, dataset, row,
  232. dataset.getColumnKey(column), column);
  233. entities.addEntity(entity);
  234. }
  235. }
  236. */
  237. }
  238. /**
  239. * Tests this renderer for equality with an arbitrary object.
  240. *
  241. * @param obj the object (<code>null</code> permitted).
  242. *
  243. * @return A boolean.
  244. */
  245. public boolean equals(Object obj) {
  246. if (obj == this) {
  247. return true;
  248. }
  249. if (!(obj instanceof CategoryStepRenderer)) {
  250. return false;
  251. }
  252. if (!super.equals(obj)) {
  253. return false;
  254. }
  255. CategoryStepRenderer that = (CategoryStepRenderer) obj;
  256. if (this.stagger != that.stagger) {
  257. return false;
  258. }
  259. return true;
  260. }
  261. }