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: * LayeredBarRenderer.java
029: * -----------------------
030: * (C) Copyright 2003-2007, 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.CategoryItemEntity;
065: import org.jfree.chart.entity.EntityCollection;
066: import org.jfree.chart.labels.CategoryItemLabelGenerator;
067: import org.jfree.chart.labels.CategoryToolTipGenerator;
068: import org.jfree.chart.plot.CategoryPlot;
069: import org.jfree.chart.plot.PlotOrientation;
070: import org.jfree.data.category.CategoryDataset;
071: import org.jfree.ui.GradientPaintTransformer;
072: import org.jfree.ui.RectangleEdge;
073: import org.jfree.util.ObjectList;
074:
075: /**
076: * A {@link CategoryItemRenderer} that represents data using bars which are
077: * superimposed.
078: */
079: public class LayeredBarRenderer extends BarRenderer implements
080: Serializable {
081:
082: /** For serialization. */
083: private static final long serialVersionUID = -8716572894780469487L;
084:
085: /** A list of the width of each series bar. */
086: protected ObjectList seriesBarWidthList;
087:
088: /**
089: * Default constructor.
090: */
091: public LayeredBarRenderer() {
092: super ();
093: this .seriesBarWidthList = new ObjectList();
094: }
095:
096: /**
097: * Returns the bar width for a series, or <code>Double.NaN</code> if no
098: * width has been set.
099: *
100: * @param series the series index (zero based).
101: *
102: * @return The width for the series (1.0=100%, it is the maximum).
103: */
104: public double getSeriesBarWidth(int series) {
105: double result = Double.NaN;
106: Number n = (Number) this .seriesBarWidthList.get(series);
107: if (n != null) {
108: result = n.doubleValue();
109: }
110: return result;
111: }
112:
113: /**
114: * Sets the width of the bars of a series.
115: *
116: * @param series the series index (zero based).
117: * @param width the width of the series bar in percentage (1.0=100%, it is
118: * the maximum).
119: */
120: public void setSeriesBarWidth(int series, double width) {
121: this .seriesBarWidthList.set(series, new Double(width));
122: }
123:
124: /**
125: * Calculates the bar width and stores it in the renderer state.
126: *
127: * @param plot the plot.
128: * @param dataArea the data area.
129: * @param rendererIndex the renderer index.
130: * @param state the renderer state.
131: */
132: protected void calculateBarWidth(CategoryPlot plot,
133: Rectangle2D dataArea, int rendererIndex,
134: CategoryItemRendererState state) {
135:
136: // calculate the bar width - this calculation differs from the
137: // BarRenderer calculation because the bars are layered on top of one
138: // another, so there is effectively only one bar per category for
139: // the purpose of the bar width calculation
140: CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex);
141: CategoryDataset dataset = plot.getDataset(rendererIndex);
142: if (dataset != null) {
143: int columns = dataset.getColumnCount();
144: int rows = dataset.getRowCount();
145: double space = 0.0;
146: PlotOrientation orientation = plot.getOrientation();
147: if (orientation == PlotOrientation.HORIZONTAL) {
148: space = dataArea.getHeight();
149: } else if (orientation == PlotOrientation.VERTICAL) {
150: space = dataArea.getWidth();
151: }
152: double maxWidth = space * getMaximumBarWidth();
153: double categoryMargin = 0.0;
154: if (columns > 1) {
155: categoryMargin = domainAxis.getCategoryMargin();
156: }
157: double used = space
158: * (1 - domainAxis.getLowerMargin()
159: - domainAxis.getUpperMargin() - categoryMargin);
160: if ((rows * columns) > 0) {
161: state.setBarWidth(Math.min(used
162: / (dataset.getColumnCount()), maxWidth));
163: } else {
164: state.setBarWidth(Math.min(used, maxWidth));
165: }
166: }
167: }
168:
169: /**
170: * Draws the bar for one item in the dataset.
171: *
172: * @param g2 the graphics device.
173: * @param state the renderer state.
174: * @param dataArea the plot area.
175: * @param plot the plot.
176: * @param domainAxis the domain (category) axis.
177: * @param rangeAxis the range (value) axis.
178: * @param data the data.
179: * @param row the row index (zero-based).
180: * @param column the column index (zero-based).
181: * @param pass the pass index.
182: */
183: public void drawItem(Graphics2D g2,
184: CategoryItemRendererState state, Rectangle2D dataArea,
185: CategoryPlot plot, CategoryAxis domainAxis,
186: ValueAxis rangeAxis, CategoryDataset data, int row,
187: int column, int pass) {
188:
189: PlotOrientation orientation = plot.getOrientation();
190: if (orientation == PlotOrientation.HORIZONTAL) {
191: drawHorizontalItem(g2, state, dataArea, plot, domainAxis,
192: rangeAxis, data, row, column);
193: } else if (orientation == PlotOrientation.VERTICAL) {
194: drawVerticalItem(g2, state, dataArea, plot, domainAxis,
195: rangeAxis, data, row, column);
196: }
197:
198: }
199:
200: /**
201: * Draws the bar for a single (series, category) data item.
202: *
203: * @param g2 the graphics device.
204: * @param state the renderer state.
205: * @param dataArea the data area.
206: * @param plot the plot.
207: * @param domainAxis the domain axis.
208: * @param rangeAxis the range axis.
209: * @param data the data.
210: * @param row the row index (zero-based).
211: * @param column the column index (zero-based).
212: */
213: protected void drawHorizontalItem(Graphics2D g2,
214: CategoryItemRendererState state, Rectangle2D dataArea,
215: CategoryPlot plot, CategoryAxis domainAxis,
216: ValueAxis rangeAxis, CategoryDataset data, int row,
217: int column) {
218:
219: // nothing is drawn for null values...
220: Number dataValue = data.getValue(row, column);
221: if (dataValue == null) {
222: return;
223: }
224:
225: // X
226: double value = dataValue.doubleValue();
227: double base = 0.0;
228: double lclip = getLowerClip();
229: double uclip = getUpperClip();
230: if (uclip <= 0.0) { // cases 1, 2, 3 and 4
231: if (value >= uclip) {
232: return; // bar is not visible
233: }
234: base = uclip;
235: if (value <= lclip) {
236: value = lclip;
237: }
238: } else if (lclip <= 0.0) { // cases 5, 6, 7 and 8
239: if (value >= uclip) {
240: value = uclip;
241: } else {
242: if (value <= lclip) {
243: value = lclip;
244: }
245: }
246: } else { // cases 9, 10, 11 and 12
247: if (value <= lclip) {
248: return; // bar is not visible
249: }
250: base = lclip;
251: if (value >= uclip) {
252: value = uclip;
253: }
254: }
255:
256: RectangleEdge edge = plot.getRangeAxisEdge();
257: double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge);
258: double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge);
259: double rectX = Math.min(transX1, transX2);
260: double rectWidth = Math.abs(transX2 - transX1);
261:
262: // Y
263: double rectY = domainAxis.getCategoryMiddle(column,
264: getColumnCount(), dataArea, plot.getDomainAxisEdge())
265: - state.getBarWidth() / 2.0;
266:
267: int seriesCount = getRowCount();
268:
269: // draw the bar...
270: double shift = 0.0;
271: double rectHeight = 0.0;
272: double widthFactor = 1.0;
273: double seriesBarWidth = getSeriesBarWidth(row);
274: if (!Double.isNaN(seriesBarWidth)) {
275: widthFactor = seriesBarWidth;
276: }
277: rectHeight = widthFactor * state.getBarWidth();
278: rectY = rectY + (1 - widthFactor) * state.getBarWidth() / 2.0;
279: if (seriesCount > 1) {
280: shift = rectHeight * 0.20 / (seriesCount - 1);
281: }
282:
283: Rectangle2D bar = new Rectangle2D.Double(rectX,
284: (rectY + ((seriesCount - 1 - row) * shift)), rectWidth,
285: (rectHeight - (seriesCount - 1 - row) * shift * 2));
286:
287: Paint itemPaint = getItemPaint(row, column);
288: GradientPaintTransformer t = getGradientPaintTransformer();
289: if (t != null && itemPaint instanceof GradientPaint) {
290: itemPaint = t.transform((GradientPaint) itemPaint, bar);
291: }
292: g2.setPaint(itemPaint);
293: g2.fill(bar);
294:
295: // draw the outline...
296: if (isDrawBarOutline()
297: && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
298: Stroke stroke = getItemOutlineStroke(row, column);
299: Paint paint = getItemOutlinePaint(row, column);
300: if (stroke != null && paint != null) {
301: g2.setStroke(stroke);
302: g2.setPaint(paint);
303: g2.draw(bar);
304: }
305: }
306:
307: CategoryItemLabelGenerator generator = getItemLabelGenerator(
308: row, column);
309: if (generator != null && isItemLabelVisible(row, column)) {
310: drawItemLabel(g2, data, row, column, plot, generator, bar,
311: (transX1 > transX2));
312: }
313:
314: // collect entity and tool tip information...
315: if (state.getInfo() != null) {
316: EntityCollection entities = state.getEntityCollection();
317: if (entities != null) {
318: String tip = null;
319: CategoryToolTipGenerator tipster = getToolTipGenerator(
320: row, column);
321: if (tipster != null) {
322: tip = tipster.generateToolTip(data, row, column);
323: }
324: String url = null;
325: if (getItemURLGenerator(row, column) != null) {
326: url = getItemURLGenerator(row, column).generateURL(
327: data, row, column);
328: }
329: CategoryItemEntity entity = new CategoryItemEntity(bar,
330: tip, url, data, data.getRowKey(row), data
331: .getColumnKey(column));
332: entities.add(entity);
333: }
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 data the data.
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, Rectangle2D dataArea,
352: CategoryPlot plot, CategoryAxis domainAxis,
353: ValueAxis rangeAxis, CategoryDataset data, int row,
354: int column) {
355:
356: // nothing is drawn for null values...
357: Number dataValue = data.getValue(row, column);
358: if (dataValue == null) {
359: return;
360: }
361:
362: // BAR X
363: double rectX = domainAxis.getCategoryMiddle(column,
364: getColumnCount(), dataArea, plot.getDomainAxisEdge())
365: - state.getBarWidth() / 2.0;
366:
367: int seriesCount = getRowCount();
368:
369: // BAR Y
370: double value = dataValue.doubleValue();
371: double base = 0.0;
372: double lclip = getLowerClip();
373: double uclip = getUpperClip();
374:
375: if (uclip <= 0.0) { // cases 1, 2, 3 and 4
376: if (value >= uclip) {
377: return; // bar is not visible
378: }
379: base = uclip;
380: if (value <= lclip) {
381: value = lclip;
382: }
383: } else if (lclip <= 0.0) { // cases 5, 6, 7 and 8
384: if (value >= uclip) {
385: value = uclip;
386: } else {
387: if (value <= lclip) {
388: value = lclip;
389: }
390: }
391: } else { // cases 9, 10, 11 and 12
392: if (value <= lclip) {
393: return; // bar is not visible
394: }
395: base = getLowerClip();
396: if (value >= uclip) {
397: value = uclip;
398: }
399: }
400:
401: RectangleEdge edge = plot.getRangeAxisEdge();
402: double transY1 = rangeAxis.valueToJava2D(base, dataArea, edge);
403: double transY2 = rangeAxis.valueToJava2D(value, dataArea, edge);
404: double rectY = Math.min(transY2, transY1);
405:
406: double rectWidth = state.getBarWidth();
407: double rectHeight = Math.abs(transY2 - transY1);
408:
409: // draw the bar...
410: double shift = 0.0;
411: rectWidth = 0.0;
412: double widthFactor = 1.0;
413: double seriesBarWidth = getSeriesBarWidth(row);
414: if (!Double.isNaN(seriesBarWidth)) {
415: widthFactor = seriesBarWidth;
416: }
417: rectWidth = widthFactor * state.getBarWidth();
418: rectX = rectX + (1 - widthFactor) * state.getBarWidth() / 2.0;
419: if (seriesCount > 1) {
420: // needs to be improved !!!
421: shift = rectWidth * 0.20 / (seriesCount - 1);
422: }
423:
424: Rectangle2D bar = new Rectangle2D.Double(
425: (rectX + ((seriesCount - 1 - row) * shift)), rectY,
426: (rectWidth - (seriesCount - 1 - row) * shift * 2),
427: rectHeight);
428: Paint itemPaint = getItemPaint(row, column);
429: GradientPaintTransformer t = getGradientPaintTransformer();
430: if (t != null && itemPaint instanceof GradientPaint) {
431: itemPaint = t.transform((GradientPaint) itemPaint, bar);
432: }
433: g2.setPaint(itemPaint);
434: g2.fill(bar);
435:
436: // draw the outline...
437: if (isDrawBarOutline()
438: && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
439: Stroke stroke = getItemOutlineStroke(row, column);
440: Paint paint = getItemOutlinePaint(row, column);
441: if (stroke != null && paint != null) {
442: g2.setStroke(stroke);
443: g2.setPaint(paint);
444: g2.draw(bar);
445: }
446: }
447:
448: // draw the item labels if there are any...
449: double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge);
450: double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge);
451:
452: CategoryItemLabelGenerator generator = getItemLabelGenerator(
453: row, column);
454: if (generator != null && isItemLabelVisible(row, column)) {
455: drawItemLabel(g2, data, row, column, plot, generator, bar,
456: (transX1 > transX2));
457: }
458:
459: // collect entity and tool tip information...
460: if (state.getInfo() != null) {
461: EntityCollection entities = state.getEntityCollection();
462: if (entities != null) {
463: String tip = null;
464: CategoryToolTipGenerator tipster = getToolTipGenerator(
465: row, column);
466: if (tipster != null) {
467: tip = tipster.generateToolTip(data, row, column);
468: }
469: String url = null;
470: if (getItemURLGenerator(row, column) != null) {
471: url = getItemURLGenerator(row, column).generateURL(
472: data, row, column);
473: }
474: CategoryItemEntity entity = new CategoryItemEntity(bar,
475: tip, url, data, data.getRowKey(row), data
476: .getColumnKey(column));
477: entities.add(entity);
478: }
479: }
480: }
481:
482: }
|