1. /* ===========================================================
  2. * JFreeChart : a free chart library for the Java(tm) platform
  3. * ===========================================================
  4. *
  5. * (C) Copyright 2000-2004, by Object Refinery Limited and Contributors.
  6. *
  7. * Project Info: http://www.jfree.org/jfreechart/index.html
  8. *
  9. * This library is free software; you can redistribute it and/or modify it under the terms
  10. * of the GNU Lesser General Public License as published by the Free Software Foundation;
  11. * either version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  14. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15. * See the GNU Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public License along with this
  18. * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  19. * Boston, MA 02111-1307, USA.
  20. *
  21. * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  22. * in the United States and other countries.]
  23. *
  24. * ------------------
  25. * BarRenderer3D.java
  26. * ------------------
  27. * (C) Copyright 2001-2004, by Serge V. Grachov and Contributors.
  28. *
  29. * Original Author: Serge V. Grachov;
  30. * Contributor(s): David Gilbert (for Object Refinery Limited);
  31. * Tin Luu;
  32. * Milo Simpson;
  33. * Richard Atkinson;
  34. * Rich Unger;
  35. * Christian W. Zuckschwerdt;
  36. *
  37. * $Id: BarRenderer3D.java,v 1.6 2004/11/18 16:37:07 mungady Exp $
  38. *
  39. * Changes
  40. * -------
  41. * 31-Oct-2001 : First version, contributed by Serge V. Grachov (DG);
  42. * 15-Nov-2001 : Modified to allow for null data values (DG);
  43. * 13-Dec-2001 : Added tooltips (DG);
  44. * 16-Jan-2002 : Added fix for single category or single series datasets, pointed out by
  45. * Taoufik Romdhane (DG);
  46. * 24-May-2002 : Incorporated tooltips into chart entities (DG);
  47. * 11-Jun-2002 : Added check for (permitted) null info object, bug and fix reported by David
  48. * Basten. Also updated Javadocs. (DG);
  49. * 19-Jun-2002 : Added code to draw labels on bars (TL);
  50. * 26-Jun-2002 : Added bar clipping to avoid PRExceptions (DG);
  51. * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs for HTML image
  52. * maps (RA);
  53. * 06-Aug-2002 : Value labels now use number formatter, thanks to Milo Simpson (DG);
  54. * 08-Aug-2002 : Applied fixed in bug id 592218 (DG);
  55. * 20-Sep-2002 : Added fix for categoryPaint by Rich Unger, and fixed errors reported by
  56. * Checkstyle (DG);
  57. * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and CategoryToolTipGenerator
  58. * interface (DG);
  59. * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG);
  60. * 06-Nov-2002 : Moved to the com.jrefinery.chart.renderer package (DG);
  61. * 28-Jan-2003 : Added an attribute to control the shading of the left and bottom walls in the
  62. * plot background (DG);
  63. * 25-Mar-2003 : Implemented Serializable (DG);
  64. * 10-Apr-2003 : Removed category paint usage (DG);
  65. * 13-May-2003 : Renamed VerticalBarRenderer3D --> BarRenderer3D and merged with
  66. * HorizontalBarRenderer3D (DG);
  67. * 30-Jul-2003 : Modified entity constructor (CZ);
  68. * 19-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
  69. * 07-Oct-2003 : Added renderer state (DG);
  70. * 08-Oct-2003 : Removed clipping (replaced with flag in CategoryPlot to control order in which
  71. * the data items are processed) (DG);
  72. * 20-Oct-2003 : Fixed bug (outline stroke not being used for bar outlines) (DG);
  73. * 21-Oct-2003 : Bar width moved into CategoryItemRendererState (DG);
  74. * 24-Nov-2003 : Fixed bug 846324 (item labels not showing) (DG);
  75. * 27-Nov-2003 : Added code to respect maxBarWidth setting (DG);
  76. * 02-Feb-2004 : Fixed bug where 'drawBarOutline' flag is not respected (DG);
  77. * 10-Feb-2004 : Small change to drawItem() method to make cut-and-paste overriding easier (DG);
  78. * 04-Oct-2004 : Fixed bug with item label positioning when plot alignment is horizontal (DG);
  79. * 05-Nov-2004 : Modified drawItem() signature (DG);
  80. *
  81. */
  82. package org.jfree.chart.renderer.category;
  83. import java.awt.AlphaComposite;
  84. import java.awt.Color;
  85. import java.awt.Composite;
  86. import java.awt.Graphics2D;
  87. import java.awt.Image;
  88. import java.awt.Paint;
  89. import java.awt.Stroke;
  90. import java.awt.geom.GeneralPath;
  91. import java.awt.geom.Line2D;
  92. import java.awt.geom.Rectangle2D;
  93. import java.io.IOException;
  94. import java.io.ObjectInputStream;
  95. import java.io.ObjectOutputStream;
  96. import java.io.Serializable;
  97. import org.jfree.chart.Effect3D;
  98. import org.jfree.chart.axis.CategoryAxis;
  99. import org.jfree.chart.axis.ValueAxis;
  100. import org.jfree.chart.entity.CategoryItemEntity;
  101. import org.jfree.chart.entity.EntityCollection;
  102. import org.jfree.chart.labels.CategoryLabelGenerator;
  103. import org.jfree.chart.labels.CategoryToolTipGenerator;
  104. import org.jfree.chart.labels.ItemLabelAnchor;
  105. import org.jfree.chart.labels.ItemLabelPosition;
  106. import org.jfree.chart.plot.CategoryPlot;
  107. import org.jfree.chart.plot.Marker;
  108. import org.jfree.chart.plot.Plot;
  109. import org.jfree.chart.plot.PlotOrientation;
  110. import org.jfree.chart.plot.ValueMarker;
  111. import org.jfree.data.Range;
  112. import org.jfree.data.category.CategoryDataset;
  113. import org.jfree.io.SerialUtilities;
  114. import org.jfree.ui.RectangleEdge;
  115. import org.jfree.ui.TextAnchor;
  116. import org.jfree.util.PublicCloneable;
  117. /**
  118. * A renderer for bars with a 3D effect, for use with the {@link org.jfree.chart.plot.CategoryPlot}
  119. * class.
  120. *
  121. * @author Serge V. Grachov
  122. */
  123. public class BarRenderer3D extends BarRenderer
  124. implements Effect3D, Cloneable, PublicCloneable, Serializable {
  125. /** The default x-offset for the 3D effect. */
  126. public static final double DEFAULT_X_OFFSET = 12.0;
  127. /** The default y-offset for the 3D effect. */
  128. public static final double DEFAULT_Y_OFFSET = 8.0;
  129. /** The default wall paint. */
  130. public static final Paint DEFAULT_WALL_PAINT = new Color(0xDD, 0xDD, 0xDD);
  131. /** The size of x-offset for the 3D effect. */
  132. private double xOffset;
  133. /** The size of y-offset for the 3D effect. */
  134. private double yOffset;
  135. /** The paint used to shade the left and lower 3D wall. */
  136. private transient Paint wallPaint;
  137. /**
  138. * Default constructor, creates a renderer with a ten pixel '3D effect'.
  139. */
  140. public BarRenderer3D() {
  141. this(DEFAULT_X_OFFSET, DEFAULT_Y_OFFSET);
  142. }
  143. /**
  144. * Constructs a new renderer with the specified '3D effect'.
  145. *
  146. * @param xOffset the x-offset for the 3D effect.
  147. * @param yOffset the y-offset for the 3D effect.
  148. */
  149. public BarRenderer3D(double xOffset, double yOffset) {
  150. super();
  151. this.xOffset = xOffset;
  152. this.yOffset = yOffset;
  153. this.wallPaint = DEFAULT_WALL_PAINT;
  154. // set the default item label positions
  155. ItemLabelPosition p1 = new ItemLabelPosition(
  156. ItemLabelAnchor.INSIDE12, TextAnchor.TOP_CENTER
  157. );
  158. setPositiveItemLabelPosition(p1);
  159. ItemLabelPosition p2 = new ItemLabelPosition(
  160. ItemLabelAnchor.INSIDE12, TextAnchor.TOP_CENTER
  161. );
  162. setNegativeItemLabelPosition(p2);
  163. }
  164. /**
  165. * Returns the x-offset for the 3D effect.
  166. *
  167. * @return The 3D effect.
  168. */
  169. public double getXOffset() {
  170. return this.xOffset;
  171. }
  172. /**
  173. * Returns the y-offset for the 3D effect.
  174. *
  175. * @return The 3D effect.
  176. */
  177. public double getYOffset() {
  178. return this.yOffset;
  179. }
  180. /**
  181. * Returns the paint used to highlight the left and bottom wall in the plot background.
  182. *
  183. * @return The paint.
  184. */
  185. public Paint getWallPaint() {
  186. return this.wallPaint;
  187. }
  188. /**
  189. * Sets the paint used to hightlight the left and bottom walls in the plot background.
  190. *
  191. * @param paint the paint.
  192. */
  193. public void setWallPaint(Paint paint) {
  194. this.wallPaint = paint;
  195. }
  196. /**
  197. * Draws the background for the plot.
  198. *
  199. * @param g2 the graphics device.
  200. * @param plot the plot.
  201. * @param dataArea the area inside the axes.
  202. */
  203. public void drawBackground(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea) {
  204. float x0 = (float) dataArea.getX();
  205. float x1 = x0 + (float) Math.abs(this.xOffset);
  206. float x3 = (float) dataArea.getMaxX();
  207. float x2 = x3 - (float) Math.abs(this.xOffset);
  208. float y0 = (float) dataArea.getMaxY();
  209. float y1 = y0 - (float) Math.abs(this.yOffset);
  210. float y3 = (float) dataArea.getMinY();
  211. float y2 = y3 + (float) Math.abs(this.yOffset);
  212. GeneralPath clip = new GeneralPath();
  213. clip.moveTo(x0, y0);
  214. clip.lineTo(x0, y2);
  215. clip.lineTo(x1, y3);
  216. clip.lineTo(x3, y3);
  217. clip.lineTo(x3, y1);
  218. clip.lineTo(x2, y0);
  219. clip.closePath();
  220. // fill background...
  221. Paint backgroundPaint = plot.getBackgroundPaint();
  222. if (backgroundPaint != null) {
  223. g2.setPaint(backgroundPaint);
  224. g2.fill(clip);
  225. }
  226. GeneralPath leftWall = new GeneralPath();
  227. leftWall.moveTo(x0, y0);
  228. leftWall.lineTo(x0, y2);
  229. leftWall.lineTo(x1, y3);
  230. leftWall.lineTo(x1, y1);
  231. leftWall.closePath();
  232. g2.setPaint(getWallPaint());
  233. g2.fill(leftWall);
  234. GeneralPath bottomWall = new GeneralPath();
  235. bottomWall.moveTo(x0, y0);
  236. bottomWall.lineTo(x1, y1);
  237. bottomWall.lineTo(x3, y1);
  238. bottomWall.lineTo(x2, y0);
  239. bottomWall.closePath();
  240. g2.setPaint(getWallPaint());
  241. g2.fill(bottomWall);
  242. // higlight the background corners...
  243. g2.setPaint(Color.lightGray);
  244. Line2D corner = new Line2D.Double(x0, y0, x1, y1);
  245. g2.draw(corner);
  246. corner.setLine(x1, y1, x1, y3);
  247. g2.draw(corner);
  248. corner.setLine(x1, y1, x3, y1);
  249. g2.draw(corner);
  250. // draw background image, if there is one...
  251. Image backgroundImage = plot.getBackgroundImage();
  252. if (backgroundImage != null) {
  253. Composite originalComposite = g2.getComposite();
  254. g2.setComposite(
  255. AlphaComposite.getInstance(AlphaComposite.SRC, plot.getBackgroundAlpha())
  256. );
  257. g2.drawImage(
  258. backgroundImage,
  259. (int) x1, (int) y3,
  260. (int) (x3 - x1 + 1), (int) (y1 - y3 + 1),
  261. null
  262. );
  263. g2.setComposite(originalComposite);
  264. }
  265. }
  266. /**
  267. * Draws the outline for the plot.
  268. *
  269. * @param g2 the graphics device.
  270. * @param plot the plot.
  271. * @param dataArea the area inside the axes.
  272. */
  273. public void drawOutline(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea) {
  274. float x0 = (float) dataArea.getX();
  275. float x1 = x0 + (float) Math.abs(this.xOffset);
  276. float x3 = (float) dataArea.getMaxX();
  277. float x2 = x3 - (float) Math.abs(this.xOffset);
  278. float y0 = (float) dataArea.getMaxY();
  279. float y1 = y0 - (float) Math.abs(this.yOffset);
  280. float y3 = (float) dataArea.getMinY();
  281. float y2 = y3 + (float) Math.abs(this.yOffset);
  282. GeneralPath clip = new GeneralPath();
  283. clip.moveTo(x0, y0);
  284. clip.lineTo(x0, y2);
  285. clip.lineTo(x1, y3);
  286. clip.lineTo(x3, y3);
  287. clip.lineTo(x3, y1);
  288. clip.lineTo(x2, y0);
  289. clip.closePath();
  290. // put an outline around the data area...
  291. Stroke outlineStroke = plot.getOutlineStroke();
  292. Paint outlinePaint = plot.getOutlinePaint();
  293. if ((outlineStroke != null) && (outlinePaint != null)) {
  294. g2.setStroke(outlineStroke);
  295. g2.setPaint(outlinePaint);
  296. g2.draw(clip);
  297. }
  298. }
  299. /**
  300. * Draws a grid line against the domain axis.
  301. *
  302. * @param g2 the graphics device.
  303. * @param plot the plot.
  304. * @param dataArea the area for plotting data (not yet adjusted for any 3D effect).
  305. * @param value the Java2D value at which the grid line should be drawn.
  306. *
  307. */
  308. public void drawDomainGridline(Graphics2D g2,
  309. CategoryPlot plot,
  310. Rectangle2D dataArea,
  311. double value) {
  312. Line2D line1 = null;
  313. Line2D line2 = null;
  314. PlotOrientation orientation = plot.getOrientation();
  315. if (orientation == PlotOrientation.HORIZONTAL) {
  316. double y0 = value;
  317. double y1 = value - getYOffset();
  318. double x0 = dataArea.getMinX();
  319. double x1 = x0 + getXOffset();
  320. double x2 = dataArea.getMaxY();
  321. line1 = new Line2D.Double(x0, y0, x1, y1);
  322. line2 = new Line2D.Double(x1, y1, x2, y1);
  323. }
  324. else if (orientation == PlotOrientation.VERTICAL) {
  325. double x0 = value;
  326. double x1 = value + getXOffset();
  327. double y0 = dataArea.getMaxY();
  328. double y1 = y0 - getYOffset();
  329. double y2 = dataArea.getMinY();
  330. line1 = new Line2D.Double(x0, y0, x1, y1);
  331. line2 = new Line2D.Double(x1, y1, x1, y2);
  332. }
  333. Paint paint = plot.getDomainGridlinePaint();
  334. Stroke stroke = plot.getDomainGridlineStroke();
  335. g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
  336. g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
  337. g2.draw(line1);
  338. g2.draw(line2);
  339. }
  340. /**
  341. * Draws a grid line against the range axis.
  342. *
  343. * @param g2 the graphics device.
  344. * @param plot the plot.
  345. * @param axis the value axis.
  346. * @param dataArea the area for plotting data (not yet adjusted for any 3D effect).
  347. * @param value the value at which the grid line should be drawn.
  348. *
  349. */
  350. public void drawRangeGridline(Graphics2D g2,
  351. CategoryPlot plot,
  352. ValueAxis axis,
  353. Rectangle2D dataArea,
  354. double value) {
  355. Range range = axis.getRange();
  356. if (!range.contains(value)) {
  357. return;
  358. }
  359. Rectangle2D adjusted = new Rectangle2D.Double(
  360. dataArea.getX(),
  361. dataArea.getY() + getYOffset(),
  362. dataArea.getWidth() - getXOffset(),
  363. dataArea.getHeight() - getYOffset()
  364. );
  365. Line2D line1 = null;
  366. Line2D line2 = null;
  367. PlotOrientation orientation = plot.getOrientation();
  368. if (orientation == PlotOrientation.HORIZONTAL) {
  369. double x0 = axis.valueToJava2D(value, adjusted, plot.getRangeAxisEdge());
  370. double x1 = x0 + getXOffset();
  371. double y0 = dataArea.getMaxY();
  372. double y1 = y0 - getYOffset();
  373. double y2 = dataArea.getMinY();
  374. line1 = new Line2D.Double(x0, y0, x1, y1);
  375. line2 = new Line2D.Double(x1, y1, x1, y2);
  376. }
  377. else if (orientation == PlotOrientation.VERTICAL) {
  378. double y0 = axis.valueToJava2D(value, adjusted, plot.getRangeAxisEdge());
  379. double y1 = y0 - getYOffset();
  380. double x0 = dataArea.getMinX();
  381. double x1 = x0 + getXOffset();
  382. double x2 = dataArea.getMaxX();
  383. line1 = new Line2D.Double(x0, y0, x1, y1);
  384. line2 = new Line2D.Double(x1, y1, x2, y1);
  385. }
  386. Paint paint = plot.getRangeGridlinePaint();
  387. Stroke stroke = plot.getRangeGridlineStroke();
  388. g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
  389. g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
  390. g2.draw(line1);
  391. g2.draw(line2);
  392. }
  393. /**
  394. * Draws a range marker.
  395. *
  396. * @param g2 the graphics device.
  397. * @param plot the plot.
  398. * @param axis the value axis.
  399. * @param marker the marker.
  400. * @param dataArea the area for plotting data (not including 3D effect).
  401. */
  402. public void drawRangeMarker(Graphics2D g2,
  403. CategoryPlot plot,
  404. ValueAxis axis,
  405. Marker marker,
  406. Rectangle2D dataArea) {
  407. if (marker instanceof ValueMarker) {
  408. ValueMarker vm = (ValueMarker) marker;
  409. double value = vm.getValue();
  410. Range range = axis.getRange();
  411. if (!range.contains(value)) {
  412. return;
  413. }
  414. Rectangle2D adjusted = new Rectangle2D.Double(
  415. dataArea.getX(), dataArea.getY() + getYOffset(),
  416. dataArea.getWidth() - getXOffset(), dataArea.getHeight() - getYOffset()
  417. );
  418. GeneralPath path = null;
  419. PlotOrientation orientation = plot.getOrientation();
  420. if (orientation == PlotOrientation.HORIZONTAL) {
  421. float x = (float) axis.valueToJava2D(
  422. value, adjusted, plot.getRangeAxisEdge()
  423. );
  424. float y = (float) adjusted.getMaxY();
  425. path = new GeneralPath();
  426. path.moveTo(x, y);
  427. path.lineTo((float) (x + getXOffset()), y - (float) getYOffset());
  428. path.lineTo(
  429. (float) (x + getXOffset()), (float) (adjusted.getMinY() - getYOffset())
  430. );
  431. path.lineTo(x, (float) adjusted.getMinY());
  432. path.closePath();
  433. }
  434. else if (orientation == PlotOrientation.VERTICAL) {
  435. float y = (float) axis.valueToJava2D(
  436. value, adjusted, plot.getRangeAxisEdge()
  437. );
  438. float x = (float) dataArea.getX();
  439. path = new GeneralPath();
  440. path.moveTo(x, y);
  441. path.lineTo(x + (float) this.xOffset, y - (float) this.yOffset);
  442. path.lineTo((float) (adjusted.getMaxX() + this.xOffset), y - (float) this.yOffset);
  443. path.lineTo((float) (adjusted.getMaxX()), y);
  444. path.closePath();
  445. }
  446. g2.setPaint(marker.getPaint());
  447. g2.fill(path);
  448. g2.setPaint(marker.getOutlinePaint());
  449. g2.draw(path);
  450. }
  451. else {
  452. super.drawRangeMarker(g2, plot, axis, marker, dataArea);
  453. // TODO: draw the interval marker with a 3D effect
  454. }
  455. }
  456. /**
  457. * Draws a 3D bar to represent one data item.
  458. *
  459. * @param g2 the graphics device.
  460. * @param state the renderer state.
  461. * @param dataArea the area for plotting the data.
  462. * @param plot the plot.
  463. * @param domainAxis the domain axis.
  464. * @param rangeAxis the range axis.
  465. * @param dataset the dataset.
  466. * @param row the row index (zero-based).
  467. * @param column the column index (zero-based).
  468. * @param pass the pass index.
  469. */
  470. public void drawItem(Graphics2D g2,
  471. CategoryItemRendererState state,
  472. Rectangle2D dataArea,
  473. CategoryPlot plot,
  474. CategoryAxis domainAxis,
  475. ValueAxis rangeAxis,
  476. CategoryDataset dataset,
  477. int row,
  478. int column,
  479. int pass) {
  480. // check the value we are plotting...
  481. Number dataValue = dataset.getValue(row, column);
  482. if (dataValue == null) {
  483. return;
  484. }
  485. double value = dataValue.doubleValue();
  486. Rectangle2D adjusted = new Rectangle2D.Double(
  487. dataArea.getX(),
  488. dataArea.getY() + getYOffset(),
  489. dataArea.getWidth() - getXOffset(),
  490. dataArea.getHeight() - getYOffset()
  491. );
  492. PlotOrientation orientation = plot.getOrientation();
  493. double barW0 = calculateBarW0(plot, orientation, adjusted, domainAxis, state, row, column);
  494. double[] barL0L1 = calculateBarL0L1(value);
  495. if (barL0L1 == null) {
  496. return; // the bar is not visible
  497. }
  498. RectangleEdge edge = plot.getRangeAxisEdge();
  499. double transL0 = rangeAxis.valueToJava2D(barL0L1[0], adjusted, edge);
  500. double transL1 = rangeAxis.valueToJava2D(barL0L1[1], adjusted, edge);
  501. double barL0 = Math.min(transL0, transL1);
  502. double barLength = Math.abs(transL1 - transL0);
  503. // draw the bar...
  504. Rectangle2D bar = null;
  505. if (orientation == PlotOrientation.HORIZONTAL) {
  506. bar = new Rectangle2D.Double(barL0, barW0, barLength, state.getBarWidth());
  507. }
  508. else {
  509. bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(), barLength);
  510. }
  511. Paint itemPaint = getItemPaint(row, column);
  512. g2.setPaint(itemPaint);
  513. g2.fill(bar);
  514. double x0 = bar.getMinX();
  515. double x1 = x0 + getXOffset();
  516. double x2 = bar.getMaxX();
  517. double x3 = x2 + getXOffset();
  518. double y0 = bar.getMinY() - getYOffset();
  519. double y1 = bar.getMinY();
  520. double y2 = bar.getMaxY() - getYOffset();
  521. double y3 = bar.getMaxY();
  522. GeneralPath bar3dRight = null;
  523. GeneralPath bar3dTop = null;
  524. if (barLength > 0.0) {
  525. bar3dRight = new GeneralPath();
  526. bar3dRight.moveTo((float) x2, (float) y3);
  527. bar3dRight.lineTo((float) x2, (float) y1);
  528. bar3dRight.lineTo((float) x3, (float) y0);
  529. bar3dRight.lineTo((float) x3, (float) y2);
  530. bar3dRight.closePath();
  531. if (itemPaint instanceof Color) {
  532. g2.setPaint(((Color) itemPaint).darker());
  533. }
  534. g2.fill(bar3dRight);
  535. }
  536. bar3dTop = new GeneralPath();
  537. bar3dTop.moveTo((float) x0, (float) y1);
  538. bar3dTop.lineTo((float) x1, (float) y0);
  539. bar3dTop.lineTo((float) x3, (float) y0);
  540. bar3dTop.lineTo((float) x2, (float) y1);
  541. bar3dTop.closePath();
  542. g2.fill(bar3dTop);
  543. if (isDrawBarOutline() && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
  544. g2.setStroke(getItemOutlineStroke(row, column));
  545. g2.setPaint(getItemOutlinePaint(row, column));
  546. g2.draw(bar);
  547. if (bar3dRight != null) {
  548. g2.draw(bar3dRight);
  549. }
  550. if (bar3dTop != null) {
  551. g2.draw(bar3dTop);
  552. }
  553. }
  554. CategoryLabelGenerator generator = getLabelGenerator(row, column);
  555. if (generator != null && isItemLabelVisible(row, column)) {
  556. drawItemLabel(g2, dataset, row, column, plot, generator, bar, (value < 0.0));
  557. }
  558. // collect entity and tool tip information...
  559. if (state.getInfo() != null) {
  560. EntityCollection entities = state.getInfo().getOwner().getEntityCollection();
  561. if (entities != null) {
  562. GeneralPath barOutline = new GeneralPath();
  563. barOutline.moveTo((float) x0, (float) y3);
  564. barOutline.lineTo((float) x0, (float) y1);
  565. barOutline.lineTo((float) x1, (float) y0);
  566. barOutline.lineTo((float) x3, (float) y0);
  567. barOutline.lineTo((float) x3, (float) y2);
  568. barOutline.lineTo((float) x2, (float) y3);
  569. barOutline.closePath();
  570. String tip = null;
  571. CategoryToolTipGenerator tipster = getToolTipGenerator(row, column);
  572. if (tipster != null) {
  573. tip = tipster.generateToolTip(dataset, row, column);
  574. }
  575. String url = null;
  576. if (getItemURLGenerator(row, column) != null) {
  577. url = getItemURLGenerator(row, column).generateURL(dataset, row, column);
  578. }
  579. CategoryItemEntity entity = new CategoryItemEntity(
  580. barOutline, tip, url, dataset, row, dataset.getColumnKey(column), column
  581. );
  582. entities.add(entity);
  583. }
  584. }
  585. }
  586. /**
  587. * Provides serialization support.
  588. *
  589. * @param stream the output stream.
  590. *
  591. * @throws IOException if there is an I/O error.
  592. */
  593. private void writeObject(ObjectOutputStream stream) throws IOException {
  594. stream.defaultWriteObject();
  595. SerialUtilities.writePaint(this.wallPaint, stream);
  596. }
  597. /**
  598. * Provides serialization support.
  599. *
  600. * @param stream the input stream.
  601. *
  602. * @throws IOException if there is an I/O error.
  603. * @throws ClassNotFoundException if there is a classpath problem.
  604. */
  605. private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
  606. stream.defaultReadObject();
  607. this.wallPaint = SerialUtilities.readPaint(stream);
  608. }
  609. }