001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2007, 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: * BarRenderer3D.java
029: * ------------------
030: * (C) Copyright 2001-2007, by Serge V. Grachov and Contributors.
031: *
032: * Original Author: Serge V. Grachov;
033: * Contributor(s): David Gilbert (for Object Refinery Limited);
034: * Tin Luu;
035: * Milo Simpson;
036: * Richard Atkinson;
037: * Rich Unger;
038: * Christian W. Zuckschwerdt;
039: *
040: * $Id: BarRenderer3D.java,v 1.10.2.8 2007/05/18 09:30:30 mungady Exp $
041: *
042: * Changes
043: * -------
044: * 31-Oct-2001 : First version, contributed by Serge V. Grachov (DG);
045: * 15-Nov-2001 : Modified to allow for null data values (DG);
046: * 13-Dec-2001 : Added tooltips (DG);
047: * 16-Jan-2002 : Added fix for single category or single series datasets,
048: * pointed out by Taoufik Romdhane (DG);
049: * 24-May-2002 : Incorporated tooltips into chart entities (DG);
050: * 11-Jun-2002 : Added check for (permitted) null info object, bug and fix
051: * reported by David Basten. Also updated Javadocs. (DG);
052: * 19-Jun-2002 : Added code to draw labels on bars (TL);
053: * 26-Jun-2002 : Added bar clipping to avoid PRExceptions (DG);
054: * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs
055: * for HTML image maps (RA);
056: * 06-Aug-2002 : Value labels now use number formatter, thanks to Milo
057: * Simpson (DG);
058: * 08-Aug-2002 : Applied fixed in bug id 592218 (DG);
059: * 20-Sep-2002 : Added fix for categoryPaint by Rich Unger, and fixed errors
060: * reported by Checkstyle (DG);
061: * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
062: * CategoryToolTipGenerator interface (DG);
063: * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG);
064: * 06-Nov-2002 : Moved to the com.jrefinery.chart.renderer package (DG);
065: * 28-Jan-2003 : Added an attribute to control the shading of the left and
066: * bottom walls in the plot background (DG);
067: * 25-Mar-2003 : Implemented Serializable (DG);
068: * 10-Apr-2003 : Removed category paint usage (DG);
069: * 13-May-2003 : Renamed VerticalBarRenderer3D --> BarRenderer3D and merged with
070: * HorizontalBarRenderer3D (DG);
071: * 30-Jul-2003 : Modified entity constructor (CZ);
072: * 19-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
073: * 07-Oct-2003 : Added renderer state (DG);
074: * 08-Oct-2003 : Removed clipping (replaced with flag in CategoryPlot to
075: * control order in which the data items are processed) (DG);
076: * 20-Oct-2003 : Fixed bug (outline stroke not being used for bar
077: * outlines) (DG);
078: * 21-Oct-2003 : Bar width moved into CategoryItemRendererState (DG);
079: * 24-Nov-2003 : Fixed bug 846324 (item labels not showing) (DG);
080: * 27-Nov-2003 : Added code to respect maxBarWidth setting (DG);
081: * 02-Feb-2004 : Fixed bug where 'drawBarOutline' flag is not respected (DG);
082: * 10-Feb-2004 : Small change to drawItem() method to make cut-and-paste
083: * overriding easier (DG);
084: * 04-Oct-2004 : Fixed bug with item label positioning when plot alignment is
085: * horizontal (DG);
086: * 05-Nov-2004 : Modified drawItem() signature (DG);
087: * 20-Apr-2005 : Renamed CategoryLabelGenerator
088: * --> CategoryItemLabelGenerator (DG);
089: * 25-Apr-2005 : Override initialise() method to fix bug 1189642 (DG);
090: * 09-Jun-2005 : Use addEntityItem from super class (DG);
091: * ------------- JFREECHART 1.0.x ---------------------------------------------
092: * 07-Dec-2006 : Implemented equals() override (DG);
093: * 17-Jan-2007 : Fixed bug in drawDomainGridline() method (DG);
094: * 03-Apr-2007 : Fixed bugs in drawBackground() method (DG);
095: *
096: */
097:
098: package org.jfree.chart.renderer.category;
099:
100: import java.awt.AlphaComposite;
101: import java.awt.Color;
102: import java.awt.Composite;
103: import java.awt.Font;
104: import java.awt.Graphics2D;
105: import java.awt.Image;
106: import java.awt.Paint;
107: import java.awt.Stroke;
108: import java.awt.geom.GeneralPath;
109: import java.awt.geom.Line2D;
110: import java.awt.geom.Point2D;
111: import java.awt.geom.Rectangle2D;
112: import java.io.IOException;
113: import java.io.ObjectInputStream;
114: import java.io.ObjectOutputStream;
115: import java.io.Serializable;
116:
117: import org.jfree.chart.Effect3D;
118: import org.jfree.chart.axis.CategoryAxis;
119: import org.jfree.chart.axis.ValueAxis;
120: import org.jfree.chart.entity.EntityCollection;
121: import org.jfree.chart.event.RendererChangeEvent;
122: import org.jfree.chart.labels.CategoryItemLabelGenerator;
123: import org.jfree.chart.labels.ItemLabelAnchor;
124: import org.jfree.chart.labels.ItemLabelPosition;
125: import org.jfree.chart.plot.CategoryPlot;
126: import org.jfree.chart.plot.Marker;
127: import org.jfree.chart.plot.Plot;
128: import org.jfree.chart.plot.PlotOrientation;
129: import org.jfree.chart.plot.PlotRenderingInfo;
130: import org.jfree.chart.plot.ValueMarker;
131: import org.jfree.data.Range;
132: import org.jfree.data.category.CategoryDataset;
133: import org.jfree.io.SerialUtilities;
134: import org.jfree.text.TextUtilities;
135: import org.jfree.ui.LengthAdjustmentType;
136: import org.jfree.ui.RectangleAnchor;
137: import org.jfree.ui.RectangleEdge;
138: import org.jfree.ui.TextAnchor;
139: import org.jfree.util.PaintUtilities;
140: import org.jfree.util.PublicCloneable;
141:
142: /**
143: * A renderer for bars with a 3D effect, for use with the
144: * {@link org.jfree.chart.plot.CategoryPlot} class.
145: */
146: public class BarRenderer3D extends BarRenderer implements Effect3D,
147: Cloneable, PublicCloneable, Serializable {
148:
149: /** For serialization. */
150: private static final long serialVersionUID = 7686976503536003636L;
151:
152: /** The default x-offset for the 3D effect. */
153: public static final double DEFAULT_X_OFFSET = 12.0;
154:
155: /** The default y-offset for the 3D effect. */
156: public static final double DEFAULT_Y_OFFSET = 8.0;
157:
158: /** The default wall paint. */
159: public static final Paint DEFAULT_WALL_PAINT = new Color(0xDD,
160: 0xDD, 0xDD);
161:
162: /** The size of x-offset for the 3D effect. */
163: private double xOffset;
164:
165: /** The size of y-offset for the 3D effect. */
166: private double yOffset;
167:
168: /** The paint used to shade the left and lower 3D wall. */
169: private transient Paint wallPaint;
170:
171: /**
172: * Default constructor, creates a renderer with a default '3D effect'.
173: */
174: public BarRenderer3D() {
175: this (DEFAULT_X_OFFSET, DEFAULT_Y_OFFSET);
176: }
177:
178: /**
179: * Constructs a new renderer with the specified '3D effect'.
180: *
181: * @param xOffset the x-offset for the 3D effect.
182: * @param yOffset the y-offset for the 3D effect.
183: */
184: public BarRenderer3D(double xOffset, double yOffset) {
185:
186: super ();
187: this .xOffset = xOffset;
188: this .yOffset = yOffset;
189: this .wallPaint = DEFAULT_WALL_PAINT;
190: // set the default item label positions
191: ItemLabelPosition p1 = new ItemLabelPosition(
192: ItemLabelAnchor.INSIDE12, TextAnchor.TOP_CENTER);
193: setBasePositiveItemLabelPosition(p1);
194: ItemLabelPosition p2 = new ItemLabelPosition(
195: ItemLabelAnchor.INSIDE12, TextAnchor.TOP_CENTER);
196: setBaseNegativeItemLabelPosition(p2);
197:
198: }
199:
200: /**
201: * Returns the x-offset for the 3D effect.
202: *
203: * @return The 3D effect.
204: *
205: * @see #getYOffset()
206: */
207: public double getXOffset() {
208: return this .xOffset;
209: }
210:
211: /**
212: * Returns the y-offset for the 3D effect.
213: *
214: * @return The 3D effect.
215: */
216: public double getYOffset() {
217: return this .yOffset;
218: }
219:
220: /**
221: * Returns the paint used to highlight the left and bottom wall in the plot
222: * background.
223: *
224: * @return The paint.
225: *
226: * @see #setWallPaint(Paint)
227: */
228: public Paint getWallPaint() {
229: return this .wallPaint;
230: }
231:
232: /**
233: * Sets the paint used to hightlight the left and bottom walls in the plot
234: * background, and sends a {@link RendererChangeEvent} to all registered
235: * listeners.
236: *
237: * @param paint the paint (<code>null</code> not permitted).
238: *
239: * @see #getWallPaint()
240: */
241: public void setWallPaint(Paint paint) {
242: if (paint == null) {
243: throw new IllegalArgumentException("Null 'paint' argument.");
244: }
245: this .wallPaint = paint;
246: notifyListeners(new RendererChangeEvent(this ));
247: }
248:
249: /**
250: * Initialises the renderer and returns a state object that will be passed
251: * to subsequent calls to the drawItem method. This method gets called
252: * once at the start of the process of drawing a chart.
253: *
254: * @param g2 the graphics device.
255: * @param dataArea the area in which the data is to be plotted.
256: * @param plot the plot.
257: * @param rendererIndex the renderer index.
258: * @param info collects chart rendering information for return to caller.
259: *
260: * @return The renderer state.
261: */
262: public CategoryItemRendererState initialise(Graphics2D g2,
263: Rectangle2D dataArea, CategoryPlot plot, int rendererIndex,
264: PlotRenderingInfo info) {
265:
266: Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
267: dataArea.getY() + getYOffset(), dataArea.getWidth()
268: - getXOffset(), dataArea.getHeight()
269: - getYOffset());
270: CategoryItemRendererState state = super .initialise(g2,
271: adjusted, plot, rendererIndex, info);
272: return state;
273:
274: }
275:
276: /**
277: * Draws the background for the plot.
278: *
279: * @param g2 the graphics device.
280: * @param plot the plot.
281: * @param dataArea the area inside the axes.
282: */
283: public void drawBackground(Graphics2D g2, CategoryPlot plot,
284: Rectangle2D dataArea) {
285:
286: float x0 = (float) dataArea.getX();
287: float x1 = x0 + (float) Math.abs(this .xOffset);
288: float x3 = (float) dataArea.getMaxX();
289: float x2 = x3 - (float) Math.abs(this .xOffset);
290:
291: float y0 = (float) dataArea.getMaxY();
292: float y1 = y0 - (float) Math.abs(this .yOffset);
293: float y3 = (float) dataArea.getMinY();
294: float y2 = y3 + (float) Math.abs(this .yOffset);
295:
296: GeneralPath clip = new GeneralPath();
297: clip.moveTo(x0, y0);
298: clip.lineTo(x0, y2);
299: clip.lineTo(x1, y3);
300: clip.lineTo(x3, y3);
301: clip.lineTo(x3, y1);
302: clip.lineTo(x2, y0);
303: clip.closePath();
304:
305: Composite originalComposite = g2.getComposite();
306: g2.setComposite(AlphaComposite.getInstance(
307: AlphaComposite.SRC_OVER, plot.getBackgroundAlpha()));
308:
309: // fill background...
310: Paint backgroundPaint = plot.getBackgroundPaint();
311: if (backgroundPaint != null) {
312: g2.setPaint(backgroundPaint);
313: g2.fill(clip);
314: }
315:
316: GeneralPath leftWall = new GeneralPath();
317: leftWall.moveTo(x0, y0);
318: leftWall.lineTo(x0, y2);
319: leftWall.lineTo(x1, y3);
320: leftWall.lineTo(x1, y1);
321: leftWall.closePath();
322: g2.setPaint(getWallPaint());
323: g2.fill(leftWall);
324:
325: GeneralPath bottomWall = new GeneralPath();
326: bottomWall.moveTo(x0, y0);
327: bottomWall.lineTo(x1, y1);
328: bottomWall.lineTo(x3, y1);
329: bottomWall.lineTo(x2, y0);
330: bottomWall.closePath();
331: g2.setPaint(getWallPaint());
332: g2.fill(bottomWall);
333:
334: // highlight the background corners...
335: g2.setPaint(Color.lightGray);
336: Line2D corner = new Line2D.Double(x0, y0, x1, y1);
337: g2.draw(corner);
338: corner.setLine(x1, y1, x1, y3);
339: g2.draw(corner);
340: corner.setLine(x1, y1, x3, y1);
341: g2.draw(corner);
342:
343: // draw background image, if there is one...
344: Image backgroundImage = plot.getBackgroundImage();
345: if (backgroundImage != null) {
346: Rectangle2D adjusted = new Rectangle2D.Double(dataArea
347: .getX()
348: + getXOffset(), dataArea.getY(), dataArea
349: .getWidth()
350: - getXOffset(), dataArea.getHeight() - getYOffset());
351: plot.drawBackgroundImage(g2, adjusted);
352: }
353:
354: g2.setComposite(originalComposite);
355:
356: }
357:
358: /**
359: * Draws the outline for the plot.
360: *
361: * @param g2 the graphics device.
362: * @param plot the plot.
363: * @param dataArea the area inside the axes.
364: */
365: public void drawOutline(Graphics2D g2, CategoryPlot plot,
366: Rectangle2D dataArea) {
367:
368: float x0 = (float) dataArea.getX();
369: float x1 = x0 + (float) Math.abs(this .xOffset);
370: float x3 = (float) dataArea.getMaxX();
371: float x2 = x3 - (float) Math.abs(this .xOffset);
372:
373: float y0 = (float) dataArea.getMaxY();
374: float y1 = y0 - (float) Math.abs(this .yOffset);
375: float y3 = (float) dataArea.getMinY();
376: float y2 = y3 + (float) Math.abs(this .yOffset);
377:
378: GeneralPath clip = new GeneralPath();
379: clip.moveTo(x0, y0);
380: clip.lineTo(x0, y2);
381: clip.lineTo(x1, y3);
382: clip.lineTo(x3, y3);
383: clip.lineTo(x3, y1);
384: clip.lineTo(x2, y0);
385: clip.closePath();
386:
387: // put an outline around the data area...
388: Stroke outlineStroke = plot.getOutlineStroke();
389: Paint outlinePaint = plot.getOutlinePaint();
390: if ((outlineStroke != null) && (outlinePaint != null)) {
391: g2.setStroke(outlineStroke);
392: g2.setPaint(outlinePaint);
393: g2.draw(clip);
394: }
395:
396: }
397:
398: /**
399: * Draws a grid line against the domain axis.
400: *
401: * @param g2 the graphics device.
402: * @param plot the plot.
403: * @param dataArea the area for plotting data (not yet adjusted for any
404: * 3D effect).
405: * @param value the Java2D value at which the grid line should be drawn.
406: *
407: */
408: public void drawDomainGridline(Graphics2D g2, CategoryPlot plot,
409: Rectangle2D dataArea, double value) {
410:
411: Line2D line1 = null;
412: Line2D line2 = null;
413: PlotOrientation orientation = plot.getOrientation();
414: if (orientation == PlotOrientation.HORIZONTAL) {
415: double y0 = value;
416: double y1 = value - getYOffset();
417: double x0 = dataArea.getMinX();
418: double x1 = x0 + getXOffset();
419: double x2 = dataArea.getMaxX();
420: line1 = new Line2D.Double(x0, y0, x1, y1);
421: line2 = new Line2D.Double(x1, y1, x2, y1);
422: } else if (orientation == PlotOrientation.VERTICAL) {
423: double x0 = value;
424: double x1 = value + getXOffset();
425: double y0 = dataArea.getMaxY();
426: double y1 = y0 - getYOffset();
427: double y2 = dataArea.getMinY();
428: line1 = new Line2D.Double(x0, y0, x1, y1);
429: line2 = new Line2D.Double(x1, y1, x1, y2);
430: }
431: Paint paint = plot.getDomainGridlinePaint();
432: Stroke stroke = plot.getDomainGridlineStroke();
433: g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
434: g2.setStroke(stroke != null ? stroke
435: : Plot.DEFAULT_OUTLINE_STROKE);
436: g2.draw(line1);
437: g2.draw(line2);
438:
439: }
440:
441: /**
442: * Draws a grid line against the range axis.
443: *
444: * @param g2 the graphics device.
445: * @param plot the plot.
446: * @param axis the value axis.
447: * @param dataArea the area for plotting data (not yet adjusted for any
448: * 3D effect).
449: * @param value the value at which the grid line should be drawn.
450: *
451: */
452: public void drawRangeGridline(Graphics2D g2, CategoryPlot plot,
453: ValueAxis axis, Rectangle2D dataArea, double value) {
454:
455: Range range = axis.getRange();
456:
457: if (!range.contains(value)) {
458: return;
459: }
460:
461: Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
462: dataArea.getY() + getYOffset(), dataArea.getWidth()
463: - getXOffset(), dataArea.getHeight()
464: - getYOffset());
465:
466: Line2D line1 = null;
467: Line2D line2 = null;
468: PlotOrientation orientation = plot.getOrientation();
469: if (orientation == PlotOrientation.HORIZONTAL) {
470: double x0 = axis.valueToJava2D(value, adjusted, plot
471: .getRangeAxisEdge());
472: double x1 = x0 + getXOffset();
473: double y0 = dataArea.getMaxY();
474: double y1 = y0 - getYOffset();
475: double y2 = dataArea.getMinY();
476: line1 = new Line2D.Double(x0, y0, x1, y1);
477: line2 = new Line2D.Double(x1, y1, x1, y2);
478: } else if (orientation == PlotOrientation.VERTICAL) {
479: double y0 = axis.valueToJava2D(value, adjusted, plot
480: .getRangeAxisEdge());
481: double y1 = y0 - getYOffset();
482: double x0 = dataArea.getMinX();
483: double x1 = x0 + getXOffset();
484: double x2 = dataArea.getMaxX();
485: line1 = new Line2D.Double(x0, y0, x1, y1);
486: line2 = new Line2D.Double(x1, y1, x2, y1);
487: }
488: Paint paint = plot.getRangeGridlinePaint();
489: Stroke stroke = plot.getRangeGridlineStroke();
490: g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
491: g2.setStroke(stroke != null ? stroke
492: : Plot.DEFAULT_OUTLINE_STROKE);
493: g2.draw(line1);
494: g2.draw(line2);
495:
496: }
497:
498: /**
499: * Draws a range marker.
500: *
501: * @param g2 the graphics device.
502: * @param plot the plot.
503: * @param axis the value axis.
504: * @param marker the marker.
505: * @param dataArea the area for plotting data (not including 3D effect).
506: */
507: public void drawRangeMarker(Graphics2D g2, CategoryPlot plot,
508: ValueAxis axis, Marker marker, Rectangle2D dataArea) {
509:
510: if (marker instanceof ValueMarker) {
511: ValueMarker vm = (ValueMarker) marker;
512: double value = vm.getValue();
513: Range range = axis.getRange();
514: if (!range.contains(value)) {
515: return;
516: }
517:
518: Rectangle2D adjusted = new Rectangle2D.Double(dataArea
519: .getX(), dataArea.getY() + getYOffset(), dataArea
520: .getWidth()
521: - getXOffset(), dataArea.getHeight() - getYOffset());
522:
523: GeneralPath path = null;
524: PlotOrientation orientation = plot.getOrientation();
525: if (orientation == PlotOrientation.HORIZONTAL) {
526: float x = (float) axis.valueToJava2D(value, adjusted,
527: plot.getRangeAxisEdge());
528: float y = (float) adjusted.getMaxY();
529: path = new GeneralPath();
530: path.moveTo(x, y);
531: path.lineTo((float) (x + getXOffset()), y
532: - (float) getYOffset());
533: path.lineTo((float) (x + getXOffset()),
534: (float) (adjusted.getMinY() - getYOffset()));
535: path.lineTo(x, (float) adjusted.getMinY());
536: path.closePath();
537: } else if (orientation == PlotOrientation.VERTICAL) {
538: float y = (float) axis.valueToJava2D(value, adjusted,
539: plot.getRangeAxisEdge());
540: float x = (float) dataArea.getX();
541: path = new GeneralPath();
542: path.moveTo(x, y);
543: path.lineTo(x + (float) this .xOffset, y
544: - (float) this .yOffset);
545: path.lineTo(
546: (float) (adjusted.getMaxX() + this .xOffset), y
547: - (float) this .yOffset);
548: path.lineTo((float) (adjusted.getMaxX()), y);
549: path.closePath();
550: }
551: g2.setPaint(marker.getPaint());
552: g2.fill(path);
553: g2.setPaint(marker.getOutlinePaint());
554: g2.draw(path);
555:
556: String label = marker.getLabel();
557: RectangleAnchor anchor = marker.getLabelAnchor();
558: if (label != null) {
559: Font labelFont = marker.getLabelFont();
560: g2.setFont(labelFont);
561: g2.setPaint(marker.getLabelPaint());
562: Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
563: g2, orientation, dataArea, path.getBounds2D(),
564: marker.getLabelOffset(),
565: LengthAdjustmentType.EXPAND, anchor);
566: TextUtilities.drawAlignedString(label, g2,
567: (float) coordinates.getX(), (float) coordinates
568: .getY(), marker.getLabelTextAnchor());
569: }
570:
571: } else {
572: super .drawRangeMarker(g2, plot, axis, marker, dataArea);
573: // TODO: draw the interval marker with a 3D effect
574: }
575: }
576:
577: /**
578: * Draws a 3D bar to represent one data item.
579: *
580: * @param g2 the graphics device.
581: * @param state the renderer state.
582: * @param dataArea the area for plotting the data.
583: * @param plot the plot.
584: * @param domainAxis the domain axis.
585: * @param rangeAxis the range axis.
586: * @param dataset the dataset.
587: * @param row the row index (zero-based).
588: * @param column the column index (zero-based).
589: * @param pass the pass index.
590: */
591: public void drawItem(Graphics2D g2,
592: CategoryItemRendererState state, Rectangle2D dataArea,
593: CategoryPlot plot, CategoryAxis domainAxis,
594: ValueAxis rangeAxis, CategoryDataset dataset, int row,
595: int column, int pass) {
596:
597: // check the value we are plotting...
598: Number dataValue = dataset.getValue(row, column);
599: if (dataValue == null) {
600: return;
601: }
602:
603: double value = dataValue.doubleValue();
604:
605: Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
606: dataArea.getY() + getYOffset(), dataArea.getWidth()
607: - getXOffset(), dataArea.getHeight()
608: - getYOffset());
609:
610: PlotOrientation orientation = plot.getOrientation();
611:
612: double barW0 = calculateBarW0(plot, orientation, adjusted,
613: domainAxis, state, row, column);
614: double[] barL0L1 = calculateBarL0L1(value);
615: if (barL0L1 == null) {
616: return; // the bar is not visible
617: }
618:
619: RectangleEdge edge = plot.getRangeAxisEdge();
620: double transL0 = rangeAxis.valueToJava2D(barL0L1[0], adjusted,
621: edge);
622: double transL1 = rangeAxis.valueToJava2D(barL0L1[1], adjusted,
623: edge);
624: double barL0 = Math.min(transL0, transL1);
625: double barLength = Math.abs(transL1 - transL0);
626:
627: // draw the bar...
628: Rectangle2D bar = null;
629: if (orientation == PlotOrientation.HORIZONTAL) {
630: bar = new Rectangle2D.Double(barL0, barW0, barLength, state
631: .getBarWidth());
632: } else {
633: bar = new Rectangle2D.Double(barW0, barL0, state
634: .getBarWidth(), barLength);
635: }
636: Paint itemPaint = getItemPaint(row, column);
637: g2.setPaint(itemPaint);
638: g2.fill(bar);
639:
640: double x0 = bar.getMinX();
641: double x1 = x0 + getXOffset();
642: double x2 = bar.getMaxX();
643: double x3 = x2 + getXOffset();
644:
645: double y0 = bar.getMinY() - getYOffset();
646: double y1 = bar.getMinY();
647: double y2 = bar.getMaxY() - getYOffset();
648: double y3 = bar.getMaxY();
649:
650: GeneralPath bar3dRight = null;
651: GeneralPath bar3dTop = null;
652: if (barLength > 0.0) {
653: bar3dRight = new GeneralPath();
654: bar3dRight.moveTo((float) x2, (float) y3);
655: bar3dRight.lineTo((float) x2, (float) y1);
656: bar3dRight.lineTo((float) x3, (float) y0);
657: bar3dRight.lineTo((float) x3, (float) y2);
658: bar3dRight.closePath();
659:
660: if (itemPaint instanceof Color) {
661: g2.setPaint(((Color) itemPaint).darker());
662: }
663: g2.fill(bar3dRight);
664: }
665:
666: bar3dTop = new GeneralPath();
667: bar3dTop.moveTo((float) x0, (float) y1);
668: bar3dTop.lineTo((float) x1, (float) y0);
669: bar3dTop.lineTo((float) x3, (float) y0);
670: bar3dTop.lineTo((float) x2, (float) y1);
671: bar3dTop.closePath();
672: g2.fill(bar3dTop);
673:
674: if (isDrawBarOutline()
675: && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
676: g2.setStroke(getItemOutlineStroke(row, column));
677: g2.setPaint(getItemOutlinePaint(row, column));
678: g2.draw(bar);
679: if (bar3dRight != null) {
680: g2.draw(bar3dRight);
681: }
682: if (bar3dTop != null) {
683: g2.draw(bar3dTop);
684: }
685: }
686:
687: CategoryItemLabelGenerator generator = getItemLabelGenerator(
688: row, column);
689: if (generator != null && isItemLabelVisible(row, column)) {
690: drawItemLabel(g2, dataset, row, column, plot, generator,
691: bar, (value < 0.0));
692: }
693:
694: // add an item entity, if this information is being collected
695: EntityCollection entities = state.getEntityCollection();
696: if (entities != null) {
697: GeneralPath barOutline = new GeneralPath();
698: barOutline.moveTo((float) x0, (float) y3);
699: barOutline.lineTo((float) x0, (float) y1);
700: barOutline.lineTo((float) x1, (float) y0);
701: barOutline.lineTo((float) x3, (float) y0);
702: barOutline.lineTo((float) x3, (float) y2);
703: barOutline.lineTo((float) x2, (float) y3);
704: barOutline.closePath();
705: addItemEntity(entities, dataset, row, column, barOutline);
706: }
707:
708: }
709:
710: /**
711: * Tests this renderer for equality with an arbitrary object.
712: *
713: * @param obj the object (<code>null</code> permitted).
714: *
715: * @return A boolean.
716: */
717: public boolean equals(Object obj) {
718: if (obj == this ) {
719: return true;
720: }
721: if (!(obj instanceof BarRenderer3D)) {
722: return false;
723: }
724: BarRenderer3D that = (BarRenderer3D) obj;
725: if (this .xOffset != that.xOffset) {
726: return false;
727: }
728: if (this .yOffset != that.yOffset) {
729: return false;
730: }
731: if (!PaintUtilities.equal(this .wallPaint, that.wallPaint)) {
732: return false;
733: }
734: return super .equals(obj);
735: }
736:
737: /**
738: * Provides serialization support.
739: *
740: * @param stream the output stream.
741: *
742: * @throws IOException if there is an I/O error.
743: */
744: private void writeObject(ObjectOutputStream stream)
745: throws IOException {
746: stream.defaultWriteObject();
747: SerialUtilities.writePaint(this .wallPaint, stream);
748: }
749:
750: /**
751: * Provides serialization support.
752: *
753: * @param stream the input stream.
754: *
755: * @throws IOException if there is an I/O error.
756: * @throws ClassNotFoundException if there is a classpath problem.
757: */
758: private void readObject(ObjectInputStream stream)
759: throws IOException, ClassNotFoundException {
760: stream.defaultReadObject();
761: this.wallPaint = SerialUtilities.readPaint(stream);
762: }
763:
764: }
|