001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2008, 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 * LayeredBarRenderer.java
029 * -----------------------
030 * (C) Copyright 2003-2008, by Arnaud Lelievre and Contributors.
031 *
032 * Original Author: Arnaud Lelievre (for Garden);
033 * Contributor(s): David Gilbert (for Object Refinery Limited);
034 * Zoheb Borbora;
035 *
036 * Changes
037 * -------
038 * 28-Aug-2003 : Version 1 (AL);
039 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
040 * 07-Oct-2003 : Added renderer state (DG);
041 * 21-Oct-2003 : Bar width moved to renderer state (DG);
042 * 05-Nov-2004 : Modified drawItem() signature (DG);
043 * 20-Apr-2005 : Renamed CategoryLabelGenerator
044 * --> CategoryItemLabelGenerator (DG);
045 * 17-Nov-2005 : Added support for gradient paint (DG);
046 * ------------- JFREECHART 1.0.x ---------------------------------------------
047 * 18-Aug-2006 : Fixed the bar width calculation to respect the maximum bar
048 * width setting (thanks to Zoheb Borbora) (DG);
049 * 02-Feb-2007 : Removed author tags all over JFreeChart sources (DG);
050 *
051 */
052
053 package org.jfree.chart.renderer.category;
054
055 import java.awt.GradientPaint;
056 import java.awt.Graphics2D;
057 import java.awt.Paint;
058 import java.awt.Stroke;
059 import java.awt.geom.Rectangle2D;
060 import java.io.Serializable;
061
062 import org.jfree.chart.axis.CategoryAxis;
063 import org.jfree.chart.axis.ValueAxis;
064 import org.jfree.chart.entity.EntityCollection;
065 import org.jfree.chart.labels.CategoryItemLabelGenerator;
066 import org.jfree.chart.plot.CategoryPlot;
067 import org.jfree.chart.plot.PlotOrientation;
068 import org.jfree.data.category.CategoryDataset;
069 import org.jfree.ui.GradientPaintTransformer;
070 import org.jfree.ui.RectangleEdge;
071 import org.jfree.util.ObjectList;
072
073 /**
074 * A {@link CategoryItemRenderer} that represents data using bars which are
075 * superimposed. The example shown here is generated by the
076 * <code>LayeredBarChartDemo1.java</code> program included in the JFreeChart
077 * Demo Collection:
078 * <br><br>
079 * <img src="../../../../../images/LayeredBarRendererSample.png"
080 * alt="LayeredBarRendererSample.png" />
081 */
082 public class LayeredBarRenderer extends BarRenderer implements Serializable {
083
084 /** For serialization. */
085 private static final long serialVersionUID = -8716572894780469487L;
086
087 /** A list of the width of each series bar. */
088 protected ObjectList seriesBarWidthList;
089
090 /**
091 * Default constructor.
092 */
093 public LayeredBarRenderer() {
094 super();
095 this.seriesBarWidthList = new ObjectList();
096 }
097
098 /**
099 * Returns the bar width for a series, or <code>Double.NaN</code> if no
100 * width has been set.
101 *
102 * @param series the series index (zero based).
103 *
104 * @return The width for the series (1.0=100%, it is the maximum).
105 */
106 public double getSeriesBarWidth(int series) {
107 double result = Double.NaN;
108 Number n = (Number) this.seriesBarWidthList.get(series);
109 if (n != null) {
110 result = n.doubleValue();
111 }
112 return result;
113 }
114
115 /**
116 * Sets the width of the bars of a series.
117 *
118 * @param series the series index (zero based).
119 * @param width the width of the series bar in percentage (1.0=100%, it is
120 * the maximum).
121 */
122 public void setSeriesBarWidth(int series, double width) {
123 this.seriesBarWidthList.set(series, new Double(width));
124 }
125
126 /**
127 * Calculates the bar width and stores it in the renderer state.
128 *
129 * @param plot the plot.
130 * @param dataArea the data area.
131 * @param rendererIndex the renderer index.
132 * @param state the renderer state.
133 */
134 protected void calculateBarWidth(CategoryPlot plot,
135 Rectangle2D dataArea,
136 int rendererIndex,
137 CategoryItemRendererState state) {
138
139 // calculate the bar width - this calculation differs from the
140 // BarRenderer calculation because the bars are layered on top of one
141 // another, so there is effectively only one bar per category for
142 // the purpose of the bar width calculation
143 CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex);
144 CategoryDataset dataset = plot.getDataset(rendererIndex);
145 if (dataset != null) {
146 int columns = dataset.getColumnCount();
147 int rows = dataset.getRowCount();
148 double space = 0.0;
149 PlotOrientation orientation = plot.getOrientation();
150 if (orientation == PlotOrientation.HORIZONTAL) {
151 space = dataArea.getHeight();
152 }
153 else if (orientation == PlotOrientation.VERTICAL) {
154 space = dataArea.getWidth();
155 }
156 double maxWidth = space * getMaximumBarWidth();
157 double categoryMargin = 0.0;
158 if (columns > 1) {
159 categoryMargin = domainAxis.getCategoryMargin();
160 }
161 double used = space * (1 - domainAxis.getLowerMargin()
162 - domainAxis.getUpperMargin() - categoryMargin);
163 if ((rows * columns) > 0) {
164 state.setBarWidth(Math.min(used / (dataset.getColumnCount()),
165 maxWidth));
166 }
167 else {
168 state.setBarWidth(Math.min(used, maxWidth));
169 }
170 }
171 }
172
173 /**
174 * Draws the bar for one item in the dataset.
175 *
176 * @param g2 the graphics device.
177 * @param state the renderer state.
178 * @param dataArea the plot area.
179 * @param plot the plot.
180 * @param domainAxis the domain (category) axis.
181 * @param rangeAxis the range (value) axis.
182 * @param data the data.
183 * @param row the row index (zero-based).
184 * @param column the column index (zero-based).
185 * @param pass the pass index.
186 */
187 public void drawItem(Graphics2D g2,
188 CategoryItemRendererState state,
189 Rectangle2D dataArea,
190 CategoryPlot plot,
191 CategoryAxis domainAxis,
192 ValueAxis rangeAxis,
193 CategoryDataset data,
194 int row,
195 int column,
196 int pass) {
197
198 PlotOrientation orientation = plot.getOrientation();
199 if (orientation == PlotOrientation.HORIZONTAL) {
200 drawHorizontalItem(g2, state, dataArea, plot, domainAxis,
201 rangeAxis, data, row, column);
202 }
203 else if (orientation == PlotOrientation.VERTICAL) {
204 drawVerticalItem(g2, state, dataArea, plot, domainAxis, rangeAxis,
205 data, row, column);
206 }
207
208 }
209
210 /**
211 * Draws the bar for a single (series, category) data item.
212 *
213 * @param g2 the graphics device.
214 * @param state the renderer state.
215 * @param dataArea the data area.
216 * @param plot the plot.
217 * @param domainAxis the domain axis.
218 * @param rangeAxis the range axis.
219 * @param dataset the dataset.
220 * @param row the row index (zero-based).
221 * @param column the column index (zero-based).
222 */
223 protected void drawHorizontalItem(Graphics2D g2,
224 CategoryItemRendererState state,
225 Rectangle2D dataArea,
226 CategoryPlot plot,
227 CategoryAxis domainAxis,
228 ValueAxis rangeAxis,
229 CategoryDataset dataset,
230 int row,
231 int column) {
232
233 // nothing is drawn for null values...
234 Number dataValue = dataset.getValue(row, column);
235 if (dataValue == null) {
236 return;
237 }
238
239 // X
240 double value = dataValue.doubleValue();
241 double base = 0.0;
242 double lclip = getLowerClip();
243 double uclip = getUpperClip();
244 if (uclip <= 0.0) { // cases 1, 2, 3 and 4
245 if (value >= uclip) {
246 return; // bar is not visible
247 }
248 base = uclip;
249 if (value <= lclip) {
250 value = lclip;
251 }
252 }
253 else if (lclip <= 0.0) { // cases 5, 6, 7 and 8
254 if (value >= uclip) {
255 value = uclip;
256 }
257 else {
258 if (value <= lclip) {
259 value = lclip;
260 }
261 }
262 }
263 else { // cases 9, 10, 11 and 12
264 if (value <= lclip) {
265 return; // bar is not visible
266 }
267 base = lclip;
268 if (value >= uclip) {
269 value = uclip;
270 }
271 }
272
273 RectangleEdge edge = plot.getRangeAxisEdge();
274 double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge);
275 double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge);
276 double rectX = Math.min(transX1, transX2);
277 double rectWidth = Math.abs(transX2 - transX1);
278
279 // Y
280 double rectY = domainAxis.getCategoryMiddle(column, getColumnCount(),
281 dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() / 2.0;
282
283 int seriesCount = getRowCount();
284
285 // draw the bar...
286 double shift = 0.0;
287 double rectHeight = 0.0;
288 double widthFactor = 1.0;
289 double seriesBarWidth = getSeriesBarWidth(row);
290 if (!Double.isNaN(seriesBarWidth)) {
291 widthFactor = seriesBarWidth;
292 }
293 rectHeight = widthFactor * state.getBarWidth();
294 rectY = rectY + (1 - widthFactor) * state.getBarWidth() / 2.0;
295 if (seriesCount > 1) {
296 shift = rectHeight * 0.20 / (seriesCount - 1);
297 }
298
299 Rectangle2D bar = new Rectangle2D.Double(rectX,
300 (rectY + ((seriesCount - 1 - row) * shift)), rectWidth,
301 (rectHeight - (seriesCount - 1 - row) * shift * 2));
302
303 Paint itemPaint = getItemPaint(row, column);
304 GradientPaintTransformer t = getGradientPaintTransformer();
305 if (t != null && itemPaint instanceof GradientPaint) {
306 itemPaint = t.transform((GradientPaint) itemPaint, bar);
307 }
308 g2.setPaint(itemPaint);
309 g2.fill(bar);
310
311 // draw the outline...
312 if (isDrawBarOutline()
313 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
314 Stroke stroke = getItemOutlineStroke(row, column);
315 Paint paint = getItemOutlinePaint(row, column);
316 if (stroke != null && paint != null) {
317 g2.setStroke(stroke);
318 g2.setPaint(paint);
319 g2.draw(bar);
320 }
321 }
322
323 CategoryItemLabelGenerator generator
324 = getItemLabelGenerator(row, column);
325 if (generator != null && isItemLabelVisible(row, column)) {
326 drawItemLabel(g2, dataset, row, column, plot, generator, bar,
327 (transX1 > transX2));
328 }
329
330 // collect entity and tool tip information...
331 EntityCollection entities = state.getEntityCollection();
332 if (entities != null) {
333 addItemEntity(entities, dataset, row, column, bar);
334 }
335 }
336
337 /**
338 * Draws the bar for a single (series, category) data item.
339 *
340 * @param g2 the graphics device.
341 * @param state the renderer state.
342 * @param dataArea the data area.
343 * @param plot the plot.
344 * @param domainAxis the domain axis.
345 * @param rangeAxis the range axis.
346 * @param dataset the dataset.
347 * @param row the row index (zero-based).
348 * @param column the column index (zero-based).
349 */
350 protected void drawVerticalItem(Graphics2D g2,
351 CategoryItemRendererState state,
352 Rectangle2D dataArea,
353 CategoryPlot plot,
354 CategoryAxis domainAxis,
355 ValueAxis rangeAxis,
356 CategoryDataset dataset,
357 int row,
358 int column) {
359
360 // nothing is drawn for null values...
361 Number dataValue = dataset.getValue(row, column);
362 if (dataValue == null) {
363 return;
364 }
365
366 // BAR X
367 double rectX = domainAxis.getCategoryMiddle(column, getColumnCount(),
368 dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() / 2.0;
369
370 int seriesCount = getRowCount();
371
372 // BAR Y
373 double value = dataValue.doubleValue();
374 double base = 0.0;
375 double lclip = getLowerClip();
376 double uclip = getUpperClip();
377
378 if (uclip <= 0.0) { // cases 1, 2, 3 and 4
379 if (value >= uclip) {
380 return; // bar is not visible
381 }
382 base = uclip;
383 if (value <= lclip) {
384 value = lclip;
385 }
386 }
387 else if (lclip <= 0.0) { // cases 5, 6, 7 and 8
388 if (value >= uclip) {
389 value = uclip;
390 }
391 else {
392 if (value <= lclip) {
393 value = lclip;
394 }
395 }
396 }
397 else { // cases 9, 10, 11 and 12
398 if (value <= lclip) {
399 return; // bar is not visible
400 }
401 base = getLowerClip();
402 if (value >= uclip) {
403 value = uclip;
404 }
405 }
406
407 RectangleEdge edge = plot.getRangeAxisEdge();
408 double transY1 = rangeAxis.valueToJava2D(base, dataArea, edge);
409 double transY2 = rangeAxis.valueToJava2D(value, dataArea, edge);
410 double rectY = Math.min(transY2, transY1);
411
412 double rectWidth = state.getBarWidth();
413 double rectHeight = Math.abs(transY2 - transY1);
414
415 // draw the bar...
416 double shift = 0.0;
417 rectWidth = 0.0;
418 double widthFactor = 1.0;
419 double seriesBarWidth = getSeriesBarWidth(row);
420 if (!Double.isNaN(seriesBarWidth)) {
421 widthFactor = seriesBarWidth;
422 }
423 rectWidth = widthFactor * state.getBarWidth();
424 rectX = rectX + (1 - widthFactor) * state.getBarWidth() / 2.0;
425 if (seriesCount > 1) {
426 // needs to be improved !!!
427 shift = rectWidth * 0.20 / (seriesCount - 1);
428 }
429
430 Rectangle2D bar = new Rectangle2D.Double(
431 (rectX + ((seriesCount - 1 - row) * shift)), rectY,
432 (rectWidth - (seriesCount - 1 - row) * shift * 2), rectHeight);
433 Paint itemPaint = getItemPaint(row, column);
434 GradientPaintTransformer t = getGradientPaintTransformer();
435 if (t != null && itemPaint instanceof GradientPaint) {
436 itemPaint = t.transform((GradientPaint) itemPaint, bar);
437 }
438 g2.setPaint(itemPaint);
439 g2.fill(bar);
440
441 // draw the outline...
442 if (isDrawBarOutline()
443 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
444 Stroke stroke = getItemOutlineStroke(row, column);
445 Paint paint = getItemOutlinePaint(row, column);
446 if (stroke != null && paint != null) {
447 g2.setStroke(stroke);
448 g2.setPaint(paint);
449 g2.draw(bar);
450 }
451 }
452
453 // draw the item labels if there are any...
454 double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge);
455 double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge);
456
457 CategoryItemLabelGenerator generator
458 = getItemLabelGenerator(row, column);
459 if (generator != null && isItemLabelVisible(row, column)) {
460 drawItemLabel(g2, dataset, row, column, plot, generator, bar,
461 (transX1 > transX2));
462 }
463
464 // collect entity and tool tip information...
465 EntityCollection entities = state.getEntityCollection();
466 if (entities != null) {
467 addItemEntity(entities, dataset, row, column, bar);
468 }
469 }
470
471 }