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.6 2007/01/17 14:16:11 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 *
095 */
096
097 package org.jfree.chart.renderer.category;
098
099 import java.awt.AlphaComposite;
100 import java.awt.Color;
101 import java.awt.Composite;
102 import java.awt.Font;
103 import java.awt.Graphics2D;
104 import java.awt.Image;
105 import java.awt.Paint;
106 import java.awt.Stroke;
107 import java.awt.geom.GeneralPath;
108 import java.awt.geom.Line2D;
109 import java.awt.geom.Point2D;
110 import java.awt.geom.Rectangle2D;
111 import java.io.IOException;
112 import java.io.ObjectInputStream;
113 import java.io.ObjectOutputStream;
114 import java.io.Serializable;
115
116 import org.jfree.chart.Effect3D;
117 import org.jfree.chart.axis.CategoryAxis;
118 import org.jfree.chart.axis.ValueAxis;
119 import org.jfree.chart.entity.EntityCollection;
120 import org.jfree.chart.event.RendererChangeEvent;
121 import org.jfree.chart.labels.CategoryItemLabelGenerator;
122 import org.jfree.chart.labels.ItemLabelAnchor;
123 import org.jfree.chart.labels.ItemLabelPosition;
124 import org.jfree.chart.plot.CategoryPlot;
125 import org.jfree.chart.plot.Marker;
126 import org.jfree.chart.plot.Plot;
127 import org.jfree.chart.plot.PlotOrientation;
128 import org.jfree.chart.plot.PlotRenderingInfo;
129 import org.jfree.chart.plot.ValueMarker;
130 import org.jfree.data.Range;
131 import org.jfree.data.category.CategoryDataset;
132 import org.jfree.io.SerialUtilities;
133 import org.jfree.text.TextUtilities;
134 import org.jfree.ui.LengthAdjustmentType;
135 import org.jfree.ui.RectangleAnchor;
136 import org.jfree.ui.RectangleEdge;
137 import org.jfree.ui.TextAnchor;
138 import org.jfree.util.PaintUtilities;
139 import org.jfree.util.PublicCloneable;
140
141 /**
142 * A renderer for bars with a 3D effect, for use with the
143 * {@link org.jfree.chart.plot.CategoryPlot} class.
144 */
145 public class BarRenderer3D extends BarRenderer
146 implements Effect3D, Cloneable, PublicCloneable,
147 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, 0xDD, 0xDD);
160
161 /** The size of x-offset for the 3D effect. */
162 private double xOffset;
163
164 /** The size of y-offset for the 3D effect. */
165 private double yOffset;
166
167 /** The paint used to shade the left and lower 3D wall. */
168 private transient Paint wallPaint;
169
170 /**
171 * Default constructor, creates a renderer with a default '3D effect'.
172 */
173 public BarRenderer3D() {
174 this(DEFAULT_X_OFFSET, DEFAULT_Y_OFFSET);
175 }
176
177 /**
178 * Constructs a new renderer with the specified '3D effect'.
179 *
180 * @param xOffset the x-offset for the 3D effect.
181 * @param yOffset the y-offset for the 3D effect.
182 */
183 public BarRenderer3D(double xOffset, double yOffset) {
184
185 super();
186 this.xOffset = xOffset;
187 this.yOffset = yOffset;
188 this.wallPaint = DEFAULT_WALL_PAINT;
189 // set the default item label positions
190 ItemLabelPosition p1 = new ItemLabelPosition(ItemLabelAnchor.INSIDE12,
191 TextAnchor.TOP_CENTER);
192 setPositiveItemLabelPosition(p1);
193 ItemLabelPosition p2 = new ItemLabelPosition(ItemLabelAnchor.INSIDE12,
194 TextAnchor.TOP_CENTER);
195 setNegativeItemLabelPosition(p2);
196
197 }
198
199 /**
200 * Returns the x-offset for the 3D effect.
201 *
202 * @return The 3D effect.
203 *
204 * @see #getYOffset()
205 */
206 public double getXOffset() {
207 return this.xOffset;
208 }
209
210 /**
211 * Returns the y-offset for the 3D effect.
212 *
213 * @return The 3D effect.
214 */
215 public double getYOffset() {
216 return this.yOffset;
217 }
218
219 /**
220 * Returns the paint used to highlight the left and bottom wall in the plot
221 * background.
222 *
223 * @return The paint.
224 *
225 * @see #setWallPaint(Paint)
226 */
227 public Paint getWallPaint() {
228 return this.wallPaint;
229 }
230
231 /**
232 * Sets the paint used to hightlight the left and bottom walls in the plot
233 * background, and sends a {@link RendererChangeEvent} to all registered
234 * listeners.
235 *
236 * @param paint the paint (<code>null</code> not permitted).
237 *
238 * @see #getWallPaint()
239 */
240 public void setWallPaint(Paint paint) {
241 if (paint == null) {
242 throw new IllegalArgumentException("Null 'paint' argument.");
243 }
244 this.wallPaint = paint;
245 notifyListeners(new RendererChangeEvent(this));
246 }
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,
264 CategoryPlot plot,
265 int rendererIndex,
266 PlotRenderingInfo info) {
267
268 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
269 dataArea.getY() + getYOffset(), dataArea.getWidth()
270 - getXOffset(), dataArea.getHeight() - getYOffset());
271 CategoryItemRendererState state = super.initialise(g2, adjusted, plot,
272 rendererIndex, info);
273 return state;
274
275 }
276
277 /**
278 * Draws the background for the plot.
279 *
280 * @param g2 the graphics device.
281 * @param plot the plot.
282 * @param dataArea the area inside the axes.
283 */
284 public void drawBackground(Graphics2D g2, CategoryPlot plot,
285 Rectangle2D dataArea) {
286
287 float x0 = (float) dataArea.getX();
288 float x1 = x0 + (float) Math.abs(this.xOffset);
289 float x3 = (float) dataArea.getMaxX();
290 float x2 = x3 - (float) Math.abs(this.xOffset);
291
292 float y0 = (float) dataArea.getMaxY();
293 float y1 = y0 - (float) Math.abs(this.yOffset);
294 float y3 = (float) dataArea.getMinY();
295 float y2 = y3 + (float) Math.abs(this.yOffset);
296
297 GeneralPath clip = new GeneralPath();
298 clip.moveTo(x0, y0);
299 clip.lineTo(x0, y2);
300 clip.lineTo(x1, y3);
301 clip.lineTo(x3, y3);
302 clip.lineTo(x3, y1);
303 clip.lineTo(x2, y0);
304 clip.closePath();
305
306 // fill background...
307 Paint backgroundPaint = plot.getBackgroundPaint();
308 if (backgroundPaint != null) {
309 g2.setPaint(backgroundPaint);
310 g2.fill(clip);
311 }
312
313 GeneralPath leftWall = new GeneralPath();
314 leftWall.moveTo(x0, y0);
315 leftWall.lineTo(x0, y2);
316 leftWall.lineTo(x1, y3);
317 leftWall.lineTo(x1, y1);
318 leftWall.closePath();
319 g2.setPaint(getWallPaint());
320 g2.fill(leftWall);
321
322 GeneralPath bottomWall = new GeneralPath();
323 bottomWall.moveTo(x0, y0);
324 bottomWall.lineTo(x1, y1);
325 bottomWall.lineTo(x3, y1);
326 bottomWall.lineTo(x2, y0);
327 bottomWall.closePath();
328 g2.setPaint(getWallPaint());
329 g2.fill(bottomWall);
330
331 // higlight the background corners...
332 g2.setPaint(Color.lightGray);
333 Line2D corner = new Line2D.Double(x0, y0, x1, y1);
334 g2.draw(corner);
335 corner.setLine(x1, y1, x1, y3);
336 g2.draw(corner);
337 corner.setLine(x1, y1, x3, y1);
338 g2.draw(corner);
339
340 // draw background image, if there is one...
341 Image backgroundImage = plot.getBackgroundImage();
342 if (backgroundImage != null) {
343 Composite originalComposite = g2.getComposite();
344 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC,
345 plot.getBackgroundAlpha()));
346 g2.drawImage(backgroundImage, (int) x1, (int) y3,
347 (int) (x3 - x1 + 1), (int) (y1 - y3 + 1), null);
348 g2.setComposite(originalComposite);
349 }
350
351 }
352
353 /**
354 * Draws the outline for the plot.
355 *
356 * @param g2 the graphics device.
357 * @param plot the plot.
358 * @param dataArea the area inside the axes.
359 */
360 public void drawOutline(Graphics2D g2, CategoryPlot plot,
361 Rectangle2D dataArea) {
362
363 float x0 = (float) dataArea.getX();
364 float x1 = x0 + (float) Math.abs(this.xOffset);
365 float x3 = (float) dataArea.getMaxX();
366 float x2 = x3 - (float) Math.abs(this.xOffset);
367
368 float y0 = (float) dataArea.getMaxY();
369 float y1 = y0 - (float) Math.abs(this.yOffset);
370 float y3 = (float) dataArea.getMinY();
371 float y2 = y3 + (float) Math.abs(this.yOffset);
372
373 GeneralPath clip = new GeneralPath();
374 clip.moveTo(x0, y0);
375 clip.lineTo(x0, y2);
376 clip.lineTo(x1, y3);
377 clip.lineTo(x3, y3);
378 clip.lineTo(x3, y1);
379 clip.lineTo(x2, y0);
380 clip.closePath();
381
382 // put an outline around the data area...
383 Stroke outlineStroke = plot.getOutlineStroke();
384 Paint outlinePaint = plot.getOutlinePaint();
385 if ((outlineStroke != null) && (outlinePaint != null)) {
386 g2.setStroke(outlineStroke);
387 g2.setPaint(outlinePaint);
388 g2.draw(clip);
389 }
390
391 }
392
393 /**
394 * Draws a grid line against the domain axis.
395 *
396 * @param g2 the graphics device.
397 * @param plot the plot.
398 * @param dataArea the area for plotting data (not yet adjusted for any
399 * 3D effect).
400 * @param value the Java2D value at which the grid line should be drawn.
401 *
402 */
403 public void drawDomainGridline(Graphics2D g2,
404 CategoryPlot plot,
405 Rectangle2D dataArea,
406 double value) {
407
408 Line2D line1 = null;
409 Line2D line2 = null;
410 PlotOrientation orientation = plot.getOrientation();
411 if (orientation == PlotOrientation.HORIZONTAL) {
412 double y0 = value;
413 double y1 = value - getYOffset();
414 double x0 = dataArea.getMinX();
415 double x1 = x0 + getXOffset();
416 double x2 = dataArea.getMaxX();
417 line1 = new Line2D.Double(x0, y0, x1, y1);
418 line2 = new Line2D.Double(x1, y1, x2, y1);
419 }
420 else if (orientation == PlotOrientation.VERTICAL) {
421 double x0 = value;
422 double x1 = value + getXOffset();
423 double y0 = dataArea.getMaxY();
424 double y1 = y0 - getYOffset();
425 double y2 = dataArea.getMinY();
426 line1 = new Line2D.Double(x0, y0, x1, y1);
427 line2 = new Line2D.Double(x1, y1, x1, y2);
428 }
429 Paint paint = plot.getDomainGridlinePaint();
430 Stroke stroke = plot.getDomainGridlineStroke();
431 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
432 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
433 g2.draw(line1);
434 g2.draw(line2);
435
436 }
437
438 /**
439 * Draws a grid line against the range axis.
440 *
441 * @param g2 the graphics device.
442 * @param plot the plot.
443 * @param axis the value axis.
444 * @param dataArea the area for plotting data (not yet adjusted for any
445 * 3D effect).
446 * @param value the value at which the grid line should be drawn.
447 *
448 */
449 public void drawRangeGridline(Graphics2D g2,
450 CategoryPlot plot,
451 ValueAxis axis,
452 Rectangle2D dataArea,
453 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() - getYOffset());
464
465 Line2D line1 = null;
466 Line2D line2 = null;
467 PlotOrientation orientation = plot.getOrientation();
468 if (orientation == PlotOrientation.HORIZONTAL) {
469 double x0 = axis.valueToJava2D(value, adjusted,
470 plot.getRangeAxisEdge());
471 double x1 = x0 + getXOffset();
472 double y0 = dataArea.getMaxY();
473 double y1 = y0 - getYOffset();
474 double y2 = dataArea.getMinY();
475 line1 = new Line2D.Double(x0, y0, x1, y1);
476 line2 = new Line2D.Double(x1, y1, x1, y2);
477 }
478 else if (orientation == PlotOrientation.VERTICAL) {
479 double y0 = axis.valueToJava2D(value, adjusted,
480 plot.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 : Plot.DEFAULT_OUTLINE_STROKE);
492 g2.draw(line1);
493 g2.draw(line2);
494
495 }
496
497 /**
498 * Draws a range marker.
499 *
500 * @param g2 the graphics device.
501 * @param plot the plot.
502 * @param axis the value axis.
503 * @param marker the marker.
504 * @param dataArea the area for plotting data (not including 3D effect).
505 */
506 public void drawRangeMarker(Graphics2D g2,
507 CategoryPlot plot,
508 ValueAxis axis,
509 Marker marker,
510 Rectangle2D dataArea) {
511
512 if (marker instanceof ValueMarker) {
513 ValueMarker vm = (ValueMarker) marker;
514 double value = vm.getValue();
515 Range range = axis.getRange();
516 if (!range.contains(value)) {
517 return;
518 }
519
520 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
521 dataArea.getY() + getYOffset(), dataArea.getWidth()
522 - getXOffset(), dataArea.getHeight() - getYOffset());
523
524 GeneralPath path = null;
525 PlotOrientation orientation = plot.getOrientation();
526 if (orientation == PlotOrientation.HORIZONTAL) {
527 float x = (float) axis.valueToJava2D(value, adjusted,
528 plot.getRangeAxisEdge());
529 float y = (float) adjusted.getMaxY();
530 path = new GeneralPath();
531 path.moveTo(x, y);
532 path.lineTo((float) (x + getXOffset()),
533 y - (float) getYOffset());
534 path.lineTo((float) (x + getXOffset()),
535 (float) (adjusted.getMinY() - getYOffset()));
536 path.lineTo(x, (float) adjusted.getMinY());
537 path.closePath();
538 }
539 else if (orientation == PlotOrientation.VERTICAL) {
540 float y = (float) axis.valueToJava2D(value, adjusted,
541 plot.getRangeAxisEdge());
542 float x = (float) dataArea.getX();
543 path = new GeneralPath();
544 path.moveTo(x, y);
545 path.lineTo(x + (float) this.xOffset, y - (float) this.yOffset);
546 path.lineTo((float) (adjusted.getMaxX() + this.xOffset),
547 y - (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(), LengthAdjustmentType.EXPAND,
565 anchor);
566 TextUtilities.drawAlignedString(label, g2,
567 (float) coordinates.getX(), (float) coordinates.getY(),
568 marker.getLabelTextAnchor());
569 }
570
571 }
572 else {
573 super.drawRangeMarker(g2, plot, axis, marker, dataArea);
574 // TODO: draw the interval marker with a 3D effect
575 }
576 }
577
578 /**
579 * Draws a 3D bar to represent one data item.
580 *
581 * @param g2 the graphics device.
582 * @param state the renderer state.
583 * @param dataArea the area for plotting the data.
584 * @param plot the plot.
585 * @param domainAxis the domain axis.
586 * @param rangeAxis the range axis.
587 * @param dataset the dataset.
588 * @param row the row index (zero-based).
589 * @param column the column index (zero-based).
590 * @param pass the pass index.
591 */
592 public void drawItem(Graphics2D g2,
593 CategoryItemRendererState state,
594 Rectangle2D dataArea,
595 CategoryPlot plot,
596 CategoryAxis domainAxis,
597 ValueAxis rangeAxis,
598 CategoryDataset dataset,
599 int row,
600 int column,
601 int pass) {
602
603 // check the value we are plotting...
604 Number dataValue = dataset.getValue(row, column);
605 if (dataValue == null) {
606 return;
607 }
608
609 double value = dataValue.doubleValue();
610
611 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
612 dataArea.getY() + getYOffset(),
613 dataArea.getWidth() - getXOffset(),
614 dataArea.getHeight() - getYOffset());
615
616 PlotOrientation orientation = plot.getOrientation();
617
618 double barW0 = calculateBarW0(plot, orientation, adjusted, domainAxis,
619 state, row, column);
620 double[] barL0L1 = calculateBarL0L1(value);
621 if (barL0L1 == null) {
622 return; // the bar is not visible
623 }
624
625 RectangleEdge edge = plot.getRangeAxisEdge();
626 double transL0 = rangeAxis.valueToJava2D(barL0L1[0], adjusted, edge);
627 double transL1 = rangeAxis.valueToJava2D(barL0L1[1], adjusted, edge);
628 double barL0 = Math.min(transL0, transL1);
629 double barLength = Math.abs(transL1 - transL0);
630
631 // draw the bar...
632 Rectangle2D bar = null;
633 if (orientation == PlotOrientation.HORIZONTAL) {
634 bar = new Rectangle2D.Double(barL0, barW0, barLength,
635 state.getBarWidth());
636 }
637 else {
638 bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(),
639 barLength);
640 }
641 Paint itemPaint = getItemPaint(row, column);
642 g2.setPaint(itemPaint);
643 g2.fill(bar);
644
645 double x0 = bar.getMinX();
646 double x1 = x0 + getXOffset();
647 double x2 = bar.getMaxX();
648 double x3 = x2 + getXOffset();
649
650 double y0 = bar.getMinY() - getYOffset();
651 double y1 = bar.getMinY();
652 double y2 = bar.getMaxY() - getYOffset();
653 double y3 = bar.getMaxY();
654
655 GeneralPath bar3dRight = null;
656 GeneralPath bar3dTop = null;
657 if (barLength > 0.0) {
658 bar3dRight = new GeneralPath();
659 bar3dRight.moveTo((float) x2, (float) y3);
660 bar3dRight.lineTo((float) x2, (float) y1);
661 bar3dRight.lineTo((float) x3, (float) y0);
662 bar3dRight.lineTo((float) x3, (float) y2);
663 bar3dRight.closePath();
664
665 if (itemPaint instanceof Color) {
666 g2.setPaint(((Color) itemPaint).darker());
667 }
668 g2.fill(bar3dRight);
669 }
670
671 bar3dTop = new GeneralPath();
672 bar3dTop.moveTo((float) x0, (float) y1);
673 bar3dTop.lineTo((float) x1, (float) y0);
674 bar3dTop.lineTo((float) x3, (float) y0);
675 bar3dTop.lineTo((float) x2, (float) y1);
676 bar3dTop.closePath();
677 g2.fill(bar3dTop);
678
679 if (isDrawBarOutline()
680 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
681 g2.setStroke(getItemOutlineStroke(row, column));
682 g2.setPaint(getItemOutlinePaint(row, column));
683 g2.draw(bar);
684 if (bar3dRight != null) {
685 g2.draw(bar3dRight);
686 }
687 if (bar3dTop != null) {
688 g2.draw(bar3dTop);
689 }
690 }
691
692 CategoryItemLabelGenerator generator
693 = getItemLabelGenerator(row, column);
694 if (generator != null && isItemLabelVisible(row, column)) {
695 drawItemLabel(g2, dataset, row, column, plot, generator, bar,
696 (value < 0.0));
697 }
698
699 // add an item entity, if this information is being collected
700 EntityCollection entities = state.getEntityCollection();
701 if (entities != null) {
702 GeneralPath barOutline = new GeneralPath();
703 barOutline.moveTo((float) x0, (float) y3);
704 barOutline.lineTo((float) x0, (float) y1);
705 barOutline.lineTo((float) x1, (float) y0);
706 barOutline.lineTo((float) x3, (float) y0);
707 barOutline.lineTo((float) x3, (float) y2);
708 barOutline.lineTo((float) x2, (float) y3);
709 barOutline.closePath();
710 addItemEntity(entities, dataset, row, column, barOutline);
711 }
712
713 }
714
715 /**
716 * Tests this renderer for equality with an arbitrary object.
717 *
718 * @param obj the object (<code>null</code> permitted).
719 *
720 * @return A boolean.
721 */
722 public boolean equals(Object obj) {
723 if (obj == this) {
724 return true;
725 }
726 if (!(obj instanceof BarRenderer3D)) {
727 return false;
728 }
729 BarRenderer3D that = (BarRenderer3D) obj;
730 if (this.xOffset != that.xOffset) {
731 return false;
732 }
733 if (this.yOffset != that.yOffset) {
734 return false;
735 }
736 if (!PaintUtilities.equal(this.wallPaint, that.wallPaint)) {
737 return false;
738 }
739 return super.equals(obj);
740 }
741
742 /**
743 * Provides serialization support.
744 *
745 * @param stream the output stream.
746 *
747 * @throws IOException if there is an I/O error.
748 */
749 private void writeObject(ObjectOutputStream stream) throws IOException {
750 stream.defaultWriteObject();
751 SerialUtilities.writePaint(this.wallPaint, stream);
752 }
753
754 /**
755 * Provides serialization support.
756 *
757 * @param stream the input stream.
758 *
759 * @throws IOException if there is an I/O error.
760 * @throws ClassNotFoundException if there is a classpath problem.
761 */
762 private void readObject(ObjectInputStream stream)
763 throws IOException, ClassNotFoundException {
764 stream.defaultReadObject();
765 this.wallPaint = SerialUtilities.readPaint(stream);
766 }
767
768 }