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