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 * ClusteredXYBarRenderer.java
029 * ---------------------------
030 * (C) Copyright 2003-2006, by Paolo Cova and Contributors.
031 *
032 * Original Author: Paolo Cova;
033 * Contributor(s): David Gilbert (for Object Refinery Limited);
034 * Christian W. Zuckschwerdt;
035 * Matthias Rose;
036 *
037 * $Id: ClusteredXYBarRenderer.java,v 1.8.2.3 2006/12/11 15:31:33 mungady Exp $
038 *
039 * Changes
040 * -------
041 * 24-Jan-2003 : Version 1, contributed by Paolo Cova (DG);
042 * 25-Mar-2003 : Implemented Serializable (DG);
043 * 01-May-2003 : Modified drawItem() method signature (DG);
044 * 30-Jul-2003 : Modified entity constructor (CZ);
045 * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
046 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
047 * 07-Oct-2003 : Added renderer state (DG);
048 * 03-Nov-2003 : In draw method added state parameter and y==null value
049 * handling (MR);
050 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
051 * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
052 * getYValue() (DG);
053 * 01-Oct-2004 : Fixed bug where 'drawBarOutline' flag is ignored (DG);
054 * 16-May-2005 : Fixed to used outline stroke for bar outlines. Removed some
055 * redundant code with the result that the renderer now respects
056 * the 'base' setting from the super-class. Added an equals()
057 * method (DG);
058 * 19-May-2005 : Added minimal item label implementation - needs improving (DG);
059 * ------------- JFREECHART 1.0.x ---------------------------------------------
060 * 11-Dec-2006 : Added support for GradientPaint (DG);
061 *
062 */
063
064 package org.jfree.chart.renderer.xy;
065
066 import java.awt.GradientPaint;
067 import java.awt.Graphics2D;
068 import java.awt.Paint;
069 import java.awt.geom.Rectangle2D;
070 import java.io.Serializable;
071
072 import org.jfree.chart.axis.ValueAxis;
073 import org.jfree.chart.entity.EntityCollection;
074 import org.jfree.chart.entity.XYItemEntity;
075 import org.jfree.chart.labels.XYItemLabelGenerator;
076 import org.jfree.chart.labels.XYToolTipGenerator;
077 import org.jfree.chart.plot.CrosshairState;
078 import org.jfree.chart.plot.PlotOrientation;
079 import org.jfree.chart.plot.PlotRenderingInfo;
080 import org.jfree.chart.plot.XYPlot;
081 import org.jfree.data.xy.IntervalXYDataset;
082 import org.jfree.data.xy.XYDataset;
083 import org.jfree.ui.RectangleEdge;
084 import org.jfree.util.PublicCloneable;
085
086 /**
087 * An extension of {@link XYBarRenderer} that displays bars for different
088 * series values at the same x next to each other. The assumption here is
089 * that for each x (time or else) there is a y value for each series. If
090 * this is not the case, there will be spaces between bars for a given x.
091 * <P>
092 * This renderer does not include code to calculate the crosshair point for the
093 * plot.
094 */
095 public class ClusteredXYBarRenderer extends XYBarRenderer
096 implements Cloneable, PublicCloneable,
097 Serializable {
098
099 /** For serialization. */
100 private static final long serialVersionUID = 5864462149177133147L;
101
102 /** Determines whether bar center should be interval start. */
103 private boolean centerBarAtStartValue;
104
105 /**
106 * Default constructor. Bar margin is set to 0.0.
107 */
108 public ClusteredXYBarRenderer() {
109 this(0.0, false);
110 }
111
112 /**
113 * Constructs a new XY clustered bar renderer.
114 *
115 * @param margin the percentage amount to trim from the width of each bar.
116 * @param centerBarAtStartValue if true, bars will be centered on the start
117 * of the time period.
118 */
119 public ClusteredXYBarRenderer(double margin,
120 boolean centerBarAtStartValue) {
121 super(margin);
122 this.centerBarAtStartValue = centerBarAtStartValue;
123 }
124
125 /**
126 * Draws the visual representation of a single data item. This method
127 * is mostly copied from the superclass, the change is that in the
128 * calculated space for a singe bar we draw bars for each series next to
129 * each other. The width of each bar is the available width divided by
130 * the number of series. Bars for each series are drawn in order left to
131 * right.
132 *
133 * @param g2 the graphics device.
134 * @param state the renderer state.
135 * @param dataArea the area within which the plot is being drawn.
136 * @param info collects information about the drawing.
137 * @param plot the plot (can be used to obtain standard color
138 * information etc).
139 * @param domainAxis the domain axis.
140 * @param rangeAxis the range axis.
141 * @param dataset the dataset.
142 * @param series the series index.
143 * @param item the item index.
144 * @param crosshairState crosshair information for the plot
145 * (<code>null</code> permitted).
146 * @param pass the pass index.
147 */
148 public void drawItem(Graphics2D g2,
149 XYItemRendererState state,
150 Rectangle2D dataArea,
151 PlotRenderingInfo info,
152 XYPlot plot,
153 ValueAxis domainAxis,
154 ValueAxis rangeAxis,
155 XYDataset dataset, int series, int item,
156 CrosshairState crosshairState,
157 int pass) {
158
159 IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset;
160
161 double value0;
162 double value1;
163 if (getUseYInterval()) {
164 value0 = intervalDataset.getStartYValue(series, item);
165 value1 = intervalDataset.getEndYValue(series, item);
166 }
167 else {
168 value0 = getBase();
169 value1 = intervalDataset.getYValue(series, item);
170 }
171 if (Double.isNaN(value0) || Double.isNaN(value1)) {
172 return;
173 }
174
175 double translatedValue0 = rangeAxis.valueToJava2D(value0, dataArea,
176 plot.getRangeAxisEdge());
177 double translatedValue1 = rangeAxis.valueToJava2D(value1, dataArea,
178 plot.getRangeAxisEdge());
179
180 RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
181 double x1 = intervalDataset.getStartXValue(series, item);
182 double translatedX1 = domainAxis.valueToJava2D(x1, dataArea,
183 xAxisLocation);
184
185 double x2 = intervalDataset.getEndXValue(series, item);
186 double translatedX2 = domainAxis.valueToJava2D(x2, dataArea,
187 xAxisLocation);
188
189 double translatedWidth = Math.max(1, Math.abs(translatedX2
190 - translatedX1));
191 double translatedHeight = Math.abs(translatedValue0 - translatedValue1);
192
193 if (this.centerBarAtStartValue) {
194 translatedX1 -= translatedWidth / 2;
195 }
196
197 PlotOrientation orientation = plot.getOrientation();
198 double m = getMargin();
199 if (m > 0.0) {
200 double cut = translatedWidth * getMargin();
201 translatedWidth = translatedWidth - cut;
202 if (orientation == PlotOrientation.HORIZONTAL)
203 translatedX1 = translatedX1 - cut / 2;
204 else
205 translatedX1 = translatedX1 + cut / 2;
206 }
207
208 int numSeries = dataset.getSeriesCount();
209 double seriesBarWidth = translatedWidth / numSeries;
210
211 Rectangle2D bar = null;
212 if (orientation == PlotOrientation.HORIZONTAL) {
213 bar = new Rectangle2D.Double(Math.min(translatedValue0,
214 translatedValue1), translatedX1 - seriesBarWidth
215 * (numSeries - series), translatedHeight, seriesBarWidth);
216 }
217 else if (orientation == PlotOrientation.VERTICAL) {
218 bar = new Rectangle2D.Double(translatedX1 + seriesBarWidth * series,
219 Math.min(translatedValue0, translatedValue1),
220 seriesBarWidth, translatedHeight);
221 }
222 Paint itemPaint = getItemPaint(series, item);
223 if (getGradientPaintTransformer()
224 != null && itemPaint instanceof GradientPaint) {
225 GradientPaint gp = (GradientPaint) itemPaint;
226 itemPaint = getGradientPaintTransformer().transform(gp, bar);
227 }
228 g2.setPaint(itemPaint);
229
230 g2.fill(bar);
231 if (isDrawBarOutline() && Math.abs(translatedX2 - translatedX1) > 3) {
232 g2.setStroke(getItemOutlineStroke(series, item));
233 g2.setPaint(getItemOutlinePaint(series, item));
234 g2.draw(bar);
235 }
236
237 if (isItemLabelVisible(series, item)) {
238 XYItemLabelGenerator generator = getItemLabelGenerator(series,
239 item);
240 drawItemLabel(g2, dataset, series, item, plot, generator, bar,
241 value1 < 0.0);
242 }
243
244 // add an entity for the item...
245 if (info != null) {
246 EntityCollection entities = info.getOwner().getEntityCollection();
247 if (entities != null) {
248 String tip = null;
249 XYToolTipGenerator generator
250 = getToolTipGenerator(series, item);
251 if (generator != null) {
252 tip = generator.generateToolTip(dataset, series, item);
253 }
254 String url = null;
255 if (getURLGenerator() != null) {
256 url = getURLGenerator().generateURL(dataset, series, item);
257 }
258 XYItemEntity entity = new XYItemEntity(bar, dataset, series,
259 item, tip, url);
260 entities.add(entity);
261 }
262 }
263
264 }
265
266 /**
267 * Tests this renderer for equality with an arbitrary object, returning
268 * <code>true</code> if <code>obj</code> is a
269 * <code>ClusteredXYBarRenderer</code> with the same settings as this
270 * renderer, and <code>false</code> otherwise.
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 ClusteredXYBarRenderer)) {
281 return false;
282 }
283 if (!super.equals(obj)) {
284 return false;
285 }
286 ClusteredXYBarRenderer that = (ClusteredXYBarRenderer) obj;
287 if (this.centerBarAtStartValue != that.centerBarAtStartValue) {
288 return false;
289 }
290 return true;
291 }
292
293 /**
294 * Returns a clone of the renderer.
295 *
296 * @return A clone.
297 *
298 * @throws CloneNotSupportedException if the renderer cannot be cloned.
299 */
300 public Object clone() throws CloneNotSupportedException {
301 return super.clone();
302 }
303
304 }