1. /* ===========================================================
  2. * JFreeChart : a free chart library for the Java(tm) platform
  3. * ===========================================================
  4. *
  5. * (C) Copyright 2000-2004, 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 under the terms
  10. * of the GNU Lesser General Public License as published by the Free Software Foundation;
  11. * either version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  14. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15. * See the GNU Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public License along with this
  18. * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  19. * Boston, MA 02111-1307, USA.
  20. *
  21. * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  22. * in the United States and other countries.]
  23. *
  24. * ---------------------------
  25. * ClusteredXYBarRenderer.java
  26. * ---------------------------
  27. * (C) Copyright 2003, 2004, by Paolo Cova and Contributors.
  28. *
  29. * Original Author: Paolo Cova;
  30. * Contributor(s): David Gilbert (for Object Refinery Limited);
  31. * Christian W. Zuckschwerdt;
  32. * Matthias Rose;
  33. *
  34. * $Id: ClusteredXYBarRenderer.java,v 1.3 2004/10/01 14:59:21 mungady Exp $
  35. *
  36. * Changes
  37. * -------
  38. * 24-Jan-2003 : Version 1, contributed by Paolo Cova (DG);
  39. * 25-Mar-2003 : Implemented Serializable (DG);
  40. * 01-May-2003 : Modified drawItem(...) method signature (DG);
  41. * 30-Jul-2003 : Modified entity constructor (CZ);
  42. * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
  43. * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
  44. * 07-Oct-2003 : Added renderer state (DG);
  45. * 03-Nov-2003 : In draw method added state parameter and y==null value handling (MR);
  46. * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
  47. * 15-Jul-2004 : Switched getX() with getXValue() and getY() with getYValue() (DG);
  48. * 01-Oct-2004 : Fixed bug where 'drawBarOutline' flag is ignored (DG);
  49. *
  50. */
  51. package org.jfree.chart.renderer.xy;
  52. import java.awt.Graphics2D;
  53. import java.awt.Paint;
  54. import java.awt.geom.Rectangle2D;
  55. import java.io.Serializable;
  56. import org.jfree.chart.axis.ValueAxis;
  57. import org.jfree.chart.entity.EntityCollection;
  58. import org.jfree.chart.entity.XYItemEntity;
  59. import org.jfree.chart.labels.XYToolTipGenerator;
  60. import org.jfree.chart.plot.CrosshairState;
  61. import org.jfree.chart.plot.PlotOrientation;
  62. import org.jfree.chart.plot.PlotRenderingInfo;
  63. import org.jfree.chart.plot.XYPlot;
  64. import org.jfree.data.xy.IntervalXYDataset;
  65. import org.jfree.data.xy.XYDataset;
  66. import org.jfree.ui.RectangleEdge;
  67. import org.jfree.util.PublicCloneable;
  68. /**
  69. * An extension of {@link XYBarRenderer} that displays bars for different
  70. * series values at the same x next to each other. The assumption here is
  71. * that for each x (time or else) there is a y value for each series. If
  72. * this is not the case, there will be spaces between bars for a given x.
  73. * <P>
  74. * This renderer does not include code to calculate the crosshair point for the plot.
  75. *
  76. * @author Paolo Cova
  77. */
  78. public class ClusteredXYBarRenderer extends XYBarRenderer implements Cloneable,
  79. PublicCloneable,
  80. Serializable {
  81. /** Percentage margin (to reduce the width of bars). */
  82. private double margin;
  83. /** A data value of zero translated to a Java2D value. */
  84. private double translatedRangeZero;
  85. /** Determines whether bar center should be interval start. */
  86. private boolean centerBarAtStartValue;
  87. /**
  88. * Default constructor. Bar margin is set to 0.0.
  89. */
  90. public ClusteredXYBarRenderer() {
  91. this(0.0, false);
  92. }
  93. /**
  94. * Constructs a new XY clustered bar renderer.
  95. *
  96. * @param margin the percentage amount to trim from the width of each bar.
  97. * @param centerBarAtStartValue If true, bars will be centered on the start of the time period.
  98. */
  99. public ClusteredXYBarRenderer(double margin, boolean centerBarAtStartValue) {
  100. super(margin);
  101. this.margin = margin;
  102. this.centerBarAtStartValue = centerBarAtStartValue;
  103. }
  104. /**
  105. * Initialises the renderer. Here we calculate the Java2D y-coordinate for zero, since all
  106. * the bars have their bases fixed at zero. Copied from superclass to
  107. * initialize local variables.
  108. *
  109. * @param g2 the graphics device.
  110. * @param dataArea the area inside the axes.
  111. * @param plot the plot.
  112. * @param data the data.
  113. * @param info an optional info collection object to return data back to the caller.
  114. *
  115. * @return The number of passes required by the renderer.
  116. */
  117. public XYItemRendererState initialise(Graphics2D g2,
  118. Rectangle2D dataArea,
  119. XYPlot plot,
  120. XYDataset data,
  121. PlotRenderingInfo info) {
  122. XYItemRendererState state = super.initialise(g2, dataArea, plot, data, info);
  123. ValueAxis rangeAxis = plot.getRangeAxis();
  124. this.translatedRangeZero = rangeAxis.valueToJava2D(0.0, dataArea, plot.getRangeAxisEdge());
  125. return state;
  126. }
  127. /**
  128. * Sets the margin.
  129. *
  130. * @param margin the margin.
  131. */
  132. public void setMargin(double margin) {
  133. this.margin = margin;
  134. super.setMargin(margin);
  135. }
  136. /**
  137. * Draws the visual representation of a single data item. This method
  138. * is mostly copied from the superclass, the change is that in the
  139. * calculated space for a singe bar we draw bars for each series next to
  140. * each other. The width of each bar is the available width divided by
  141. * the number of series. Bars for each series are drawn in order left to
  142. * right.
  143. *
  144. * @param g2 the graphics device.
  145. * @param state the renderer state.
  146. * @param dataArea the area within which the plot is being drawn.
  147. * @param info collects information about the drawing.
  148. * @param plot the plot (can be used to obtain standard color information etc).
  149. * @param domainAxis the domain axis.
  150. * @param rangeAxis the range axis.
  151. * @param dataset the dataset.
  152. * @param series the series index.
  153. * @param item the item index.
  154. * @param crosshairState crosshair information for the plot (<code>null</code> permitted).
  155. * @param pass the pass index.
  156. */
  157. public void drawItem(Graphics2D g2,
  158. XYItemRendererState state,
  159. Rectangle2D dataArea,
  160. PlotRenderingInfo info,
  161. XYPlot plot,
  162. ValueAxis domainAxis,
  163. ValueAxis rangeAxis,
  164. XYDataset dataset, int series, int item,
  165. CrosshairState crosshairState,
  166. int pass) {
  167. IntervalXYDataset intervalData = (IntervalXYDataset) dataset;
  168. Paint seriesPaint = getItemPaint(series, item);
  169. Paint seriesOutlinePaint = getItemOutlinePaint(series, item);
  170. Number y = intervalData.getY(series, item);
  171. if (y == null) {
  172. return;
  173. }
  174. RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
  175. double translatedY = rangeAxis.valueToJava2D(y.doubleValue(), dataArea, yAxisLocation);
  176. RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
  177. double x1 = intervalData.getStartXValue(series, item);
  178. double translatedX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
  179. double x2 = intervalData.getEndXValue(series, item);
  180. double translatedX2 = domainAxis.valueToJava2D(x2, dataArea, xAxisLocation);
  181. double translatedWidth = Math.max(1, Math.abs(translatedX2 - translatedX1));
  182. double translatedHeight = Math.abs(translatedY - this.translatedRangeZero);
  183. if (this.centerBarAtStartValue) {
  184. translatedX1 -= translatedWidth / 2;
  185. }
  186. if (this.margin > 0.0) {
  187. double cut = translatedWidth * this.margin;
  188. translatedWidth = translatedWidth - cut;
  189. translatedX1 = translatedX1 + cut / 2;
  190. }
  191. int numSeries = dataset.getSeriesCount();
  192. double seriesBarWidth = translatedWidth / numSeries;
  193. Rectangle2D bar = null;
  194. PlotOrientation orientation = plot.getOrientation();
  195. if (orientation == PlotOrientation.HORIZONTAL) {
  196. bar = new Rectangle2D.Double(
  197. Math.min(this.translatedRangeZero, translatedY),
  198. translatedX1 - seriesBarWidth * (numSeries - series),
  199. translatedHeight, seriesBarWidth
  200. );
  201. }
  202. else if (orientation == PlotOrientation.VERTICAL) {
  203. bar = new Rectangle2D.Double(
  204. translatedX1 + seriesBarWidth * series,
  205. Math.min(this.translatedRangeZero, translatedY),
  206. seriesBarWidth, translatedHeight
  207. );
  208. }
  209. g2.setPaint(seriesPaint);
  210. g2.fill(bar);
  211. if (isDrawBarOutline() && Math.abs(translatedX2 - translatedX1) > 3) {
  212. g2.setStroke(getItemStroke(series, item));
  213. g2.setPaint(seriesOutlinePaint);
  214. g2.draw(bar);
  215. }
  216. // add an entity for the item...
  217. if (info != null) {
  218. EntityCollection entities = info.getOwner().getEntityCollection();
  219. if (entities != null) {
  220. String tip = null;
  221. XYToolTipGenerator generator = getToolTipGenerator(series, item);
  222. if (generator != null) {
  223. tip = generator.generateToolTip(dataset, series, item);
  224. }
  225. String url = null;
  226. if (getURLGenerator() != null) {
  227. url = getURLGenerator().generateURL(dataset, series, item);
  228. }
  229. XYItemEntity entity = new XYItemEntity(bar, dataset, series, item, tip, url);
  230. entities.add(entity);
  231. }
  232. }
  233. }
  234. /**
  235. * Returns a clone of the renderer.
  236. *
  237. * @return A clone.
  238. *
  239. * @throws CloneNotSupportedException if the renderer cannot be cloned.
  240. */
  241. public Object clone() throws CloneNotSupportedException {
  242. return super.clone();
  243. }
  244. }