1 /* =========================================================== 2 * JFreeChart : a free chart library for the Java(tm) platform 3 * =========================================================== 4 * 5 * (C) Copyright 2000-2013, 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 10 * under the terms of the GNU Lesser General Public License as published by 11 * the Free Software Foundation; either version 2.1 of the License, or 12 * (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17 * License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22 * USA. 23 * 24 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 25 * Other names may be trademarks of their respective owners.] 26 * 27 * ------------------- 28 * XYAreaRenderer.java 29 * ------------------- 30 * (C) Copyright 2002-2013, by Hari and Contributors. 31 * 32 * Original Author: Hari (ourhari@hotmail.com); 33 * Contributor(s): David Gilbert (for Object Refinery Limited); 34 * Richard Atkinson; 35 * Christian W. Zuckschwerdt; 36 * Martin Krauskopf; 37 * 38 * Changes: 39 * -------- 40 * 03-Apr-2002 : Version 1, contributed by Hari. This class is based on the 41 * StandardXYItemRenderer class (DG); 42 * 09-Apr-2002 : Removed the translated zero from the drawItem method - 43 * overridden the initialise() method to calculate it (DG); 44 * 30-May-2002 : Added tool tip generator to constructor to match super 45 * class (DG); 46 * 25-Jun-2002 : Removed unnecessary local variable (DG); 47 * 05-Aug-2002 : Small modification to drawItem method to support URLs for HTML 48 * image maps (RA); 49 * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG); 50 * 07-Nov-2002 : Renamed AreaXYItemRenderer --> XYAreaRenderer (DG); 51 * 25-Mar-2003 : Implemented Serializable (DG); 52 * 01-May-2003 : Modified drawItem() method signature (DG); 53 * 27-Jul-2003 : Made line and polygon properties protected rather than 54 * private (RA); 55 * 30-Jul-2003 : Modified entity constructor (CZ); 56 * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG); 57 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 58 * 07-Oct-2003 : Added renderer state (DG); 59 * 08-Dec-2003 : Modified hotspot for chart entity (DG); 60 * 10-Feb-2004 : Changed the drawItem() method to make cut-and-paste overriding 61 * easier. Also moved state class into this class (DG); 62 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState. Renamed 63 * XYToolTipGenerator --> XYItemLabelGenerator (DG); 64 * 15-Jul-2004 : Switched getX() with getXValue() and getY() with 65 * getYValue() (DG); 66 * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG); 67 * 19-Jan-2005 : Now accesses primitives only from dataset (DG); 68 * 21-Mar-2005 : Override getLegendItem() and equals() methods (DG); 69 * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG); 70 * ------------- JFREECHART 1.0.x --------------------------------------------- 71 * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG); 72 * 14-Feb-2007 : Fixed bug in clone() (DG); 73 * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG); 74 * 04-May-2007 : Set processVisibleItemsOnly flag to false (DG); 75 * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG); 76 * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG); 77 * 17-Jun-2008 : Apply legend font and paint attributes (DG); 78 * 31-Dec-2008 : Fix for bug 2471906 - dashed outlines performance issue (DG); 79 * 11-Jun-2009 : Added a useFillPaint flag and a GradientPaintTransformer for 80 * the paint under the series (DG); 81 * 06-Oct-2011 : Avoid GeneralPath methods requiring Java 1.5 (MK); 82 * 03-Jul-2013 : Use ParamChecks (DG); 83 * 84 */ 85 86 package org.jfree.chart.renderer.xy; 87 88 import java.awt.BasicStroke; 89 import java.awt.GradientPaint; 90 import java.awt.Graphics2D; 91 import java.awt.Paint; 92 import java.awt.Shape; 93 import java.awt.Stroke; 94 import java.awt.geom.Area; 95 import java.awt.geom.GeneralPath; 96 import java.awt.geom.Line2D; 97 import java.awt.geom.Rectangle2D; 98 import java.io.IOException; 99 import java.io.ObjectInputStream; 100 import java.io.ObjectOutputStream; 101 102 import org.jfree.chart.HashUtilities; 103 import org.jfree.chart.LegendItem; 104 import org.jfree.chart.axis.ValueAxis; 105 import org.jfree.chart.entity.EntityCollection; 106 import org.jfree.chart.event.RendererChangeEvent; 107 import org.jfree.chart.labels.XYSeriesLabelGenerator; 108 import org.jfree.chart.labels.XYToolTipGenerator; 109 import org.jfree.chart.plot.CrosshairState; 110 import org.jfree.chart.plot.PlotOrientation; 111 import org.jfree.chart.plot.PlotRenderingInfo; 112 import org.jfree.chart.plot.XYPlot; 113 import org.jfree.chart.urls.XYURLGenerator; 114 import org.jfree.chart.util.ParamChecks; 115 import org.jfree.data.xy.XYDataset; 116 import org.jfree.io.SerialUtilities; 117 import org.jfree.ui.GradientPaintTransformer; 118 import org.jfree.ui.StandardGradientPaintTransformer; 119 import org.jfree.util.PublicCloneable; 120 import org.jfree.util.ShapeUtilities; 121 122 /** 123 * Area item renderer for an {@link XYPlot}. This class can draw (a) shapes at 124 * each point, or (b) lines between points, or (c) both shapes and lines, 125 * or (d) filled areas, or (e) filled areas and shapes. The example shown here 126 * is generated by the <code>XYAreaRendererDemo1.java</code> program included 127 * in the JFreeChart demo collection: 128 * <br><br> 129 * <img src="../../../../../images/XYAreaRendererSample.png" 130 * alt="XYAreaRendererSample.png" /> 131 */ 132 public class XYAreaRenderer extends AbstractXYItemRenderer 133 implements XYItemRenderer, PublicCloneable { 134 135 /** For serialization. */ 136 private static final long serialVersionUID = -4481971353973876747L; 137 138 /** 139 * A state object used by this renderer. 140 */ 141 static class XYAreaRendererState extends XYItemRendererState { 142 143 /** Working storage for the area under one series. */ 144 public GeneralPath area; 145 146 /** Working line that can be recycled. */ 147 public Line2D line; 148 149 /** 150 * Creates a new state. 151 * 152 * @param info the plot rendering info. 153 */ XYAreaRendererState(PlotRenderingInfo info)154 public XYAreaRendererState(PlotRenderingInfo info) { 155 super(info); 156 this.area = new GeneralPath(); 157 this.line = new Line2D.Double(); 158 } 159 160 } 161 162 /** Useful constant for specifying the type of rendering (shapes only). */ 163 public static final int SHAPES = 1; 164 165 /** Useful constant for specifying the type of rendering (lines only). */ 166 public static final int LINES = 2; 167 168 /** 169 * Useful constant for specifying the type of rendering (shapes and lines). 170 */ 171 public static final int SHAPES_AND_LINES = 3; 172 173 /** Useful constant for specifying the type of rendering (area only). */ 174 public static final int AREA = 4; 175 176 /** 177 * Useful constant for specifying the type of rendering (area and shapes). 178 */ 179 public static final int AREA_AND_SHAPES = 5; 180 181 /** A flag indicating whether or not shapes are drawn at each XY point. */ 182 private boolean plotShapes; 183 184 /** A flag indicating whether or not lines are drawn between XY points. */ 185 private boolean plotLines; 186 187 /** A flag indicating whether or not Area are drawn at each XY point. */ 188 private boolean plotArea; 189 190 /** A flag that controls whether or not the outline is shown. */ 191 private boolean showOutline; 192 193 /** 194 * The shape used to represent an area in each legend item (this should 195 * never be <code>null</code>). 196 */ 197 private transient Shape legendArea; 198 199 /** 200 * A flag that can be set to specify that the fill paint should be used 201 * to fill the area under the renderer. 202 * 203 * @since 1.0.14 204 */ 205 private boolean useFillPaint; 206 207 /** 208 * A transformer that is applied to the paint used to fill under the 209 * area *if* it is an instance of GradientPaint. 210 * 211 * @since 1.0.14 212 */ 213 private GradientPaintTransformer gradientTransformer; 214 215 /** 216 * Constructs a new renderer. 217 */ XYAreaRenderer()218 public XYAreaRenderer() { 219 this(AREA); 220 } 221 222 /** 223 * Constructs a new renderer. 224 * 225 * @param type the type of the renderer. 226 */ XYAreaRenderer(int type)227 public XYAreaRenderer(int type) { 228 this(type, null, null); 229 } 230 231 /** 232 * Constructs a new renderer. To specify the type of renderer, use one of 233 * the constants: <code>SHAPES</code>, <code>LINES</code>, 234 * <code>SHAPES_AND_LINES</code>, <code>AREA</code> or 235 * <code>AREA_AND_SHAPES</code>. 236 * 237 * @param type the type of renderer. 238 * @param toolTipGenerator the tool tip generator to use 239 * (<code>null</code> permitted). 240 * @param urlGenerator the URL generator (<code>null</code> permitted). 241 */ XYAreaRenderer(int type, XYToolTipGenerator toolTipGenerator, XYURLGenerator urlGenerator)242 public XYAreaRenderer(int type, XYToolTipGenerator toolTipGenerator, 243 XYURLGenerator urlGenerator) { 244 245 super(); 246 setBaseToolTipGenerator(toolTipGenerator); 247 setURLGenerator(urlGenerator); 248 249 if (type == SHAPES) { 250 this.plotShapes = true; 251 } 252 if (type == LINES) { 253 this.plotLines = true; 254 } 255 if (type == SHAPES_AND_LINES) { 256 this.plotShapes = true; 257 this.plotLines = true; 258 } 259 if (type == AREA) { 260 this.plotArea = true; 261 } 262 if (type == AREA_AND_SHAPES) { 263 this.plotArea = true; 264 this.plotShapes = true; 265 } 266 this.showOutline = false; 267 GeneralPath area = new GeneralPath(); 268 area.moveTo(0.0f, -4.0f); 269 area.lineTo(3.0f, -2.0f); 270 area.lineTo(4.0f, 4.0f); 271 area.lineTo(-4.0f, 4.0f); 272 area.lineTo(-3.0f, -2.0f); 273 area.closePath(); 274 this.legendArea = area; 275 this.useFillPaint = false; 276 this.gradientTransformer = new StandardGradientPaintTransformer(); 277 } 278 279 /** 280 * Returns true if shapes are being plotted by the renderer. 281 * 282 * @return <code>true</code> if shapes are being plotted by the renderer. 283 */ getPlotShapes()284 public boolean getPlotShapes() { 285 return this.plotShapes; 286 } 287 288 /** 289 * Returns true if lines are being plotted by the renderer. 290 * 291 * @return <code>true</code> if lines are being plotted by the renderer. 292 */ getPlotLines()293 public boolean getPlotLines() { 294 return this.plotLines; 295 } 296 297 /** 298 * Returns true if Area is being plotted by the renderer. 299 * 300 * @return <code>true</code> if Area is being plotted by the renderer. 301 */ getPlotArea()302 public boolean getPlotArea() { 303 return this.plotArea; 304 } 305 306 /** 307 * Returns a flag that controls whether or not outlines of the areas are 308 * drawn. 309 * 310 * @return The flag. 311 * 312 * @see #setOutline(boolean) 313 */ isOutline()314 public boolean isOutline() { 315 return this.showOutline; 316 } 317 318 /** 319 * Sets a flag that controls whether or not outlines of the areas are drawn 320 * and sends a {@link RendererChangeEvent} to all registered listeners. 321 * 322 * @param show the flag. 323 * 324 * @see #isOutline() 325 */ setOutline(boolean show)326 public void setOutline(boolean show) { 327 this.showOutline = show; 328 fireChangeEvent(); 329 } 330 331 /** 332 * Returns the shape used to represent an area in the legend. 333 * 334 * @return The legend area (never <code>null</code>). 335 */ getLegendArea()336 public Shape getLegendArea() { 337 return this.legendArea; 338 } 339 340 /** 341 * Sets the shape used as an area in each legend item and sends a 342 * {@link RendererChangeEvent} to all registered listeners. 343 * 344 * @param area the area (<code>null</code> not permitted). 345 */ setLegendArea(Shape area)346 public void setLegendArea(Shape area) { 347 ParamChecks.nullNotPermitted(area, "area"); 348 this.legendArea = area; 349 fireChangeEvent(); 350 } 351 352 /** 353 * Returns the flag that controls whether the series fill paint is used to 354 * fill the area under the line. 355 * 356 * @return A boolean. 357 * 358 * @since 1.0.14 359 */ getUseFillPaint()360 public boolean getUseFillPaint() { 361 return this.useFillPaint; 362 } 363 364 /** 365 * Sets the flag that controls whether or not the series fill paint is 366 * used to fill the area under the line and sends a 367 * {@link RendererChangeEvent} to all listeners. 368 * 369 * @param use the new flag value. 370 * 371 * @since 1.0.14 372 */ setUseFillPaint(boolean use)373 public void setUseFillPaint(boolean use) { 374 this.useFillPaint = use; 375 fireChangeEvent(); 376 } 377 378 /** 379 * Returns the gradient paint transformer. 380 * 381 * @return The gradient paint transformer (never <code>null</code>). 382 * 383 * @since 1.0.14 384 */ getGradientTransformer()385 public GradientPaintTransformer getGradientTransformer() { 386 return this.gradientTransformer; 387 } 388 389 /** 390 * Sets the gradient paint transformer and sends a 391 * {@link RendererChangeEvent} to all registered listeners. 392 * 393 * @param transformer the transformer (<code>null</code> not permitted). 394 * 395 * @since 1.0.14 396 */ setGradientTransformer(GradientPaintTransformer transformer)397 public void setGradientTransformer(GradientPaintTransformer transformer) { 398 ParamChecks.nullNotPermitted(transformer, "transformer"); 399 this.gradientTransformer = transformer; 400 fireChangeEvent(); 401 } 402 403 /** 404 * Initialises the renderer and returns a state object that should be 405 * passed to all subsequent calls to the drawItem() method. 406 * 407 * @param g2 the graphics device. 408 * @param dataArea the area inside the axes. 409 * @param plot the plot. 410 * @param data the data. 411 * @param info an optional info collection object to return data back to 412 * the caller. 413 * 414 * @return A state object for use by the renderer. 415 */ 416 @Override initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset data, PlotRenderingInfo info)417 public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, 418 XYPlot plot, XYDataset data, PlotRenderingInfo info) { 419 XYAreaRendererState state = new XYAreaRendererState(info); 420 421 // in the rendering process, there is special handling for item 422 // zero, so we can't support processing of visible data items only 423 state.setProcessVisibleItemsOnly(false); 424 return state; 425 } 426 427 /** 428 * Returns a default legend item for the specified series. Subclasses 429 * should override this method to generate customised items. 430 * 431 * @param datasetIndex the dataset index (zero-based). 432 * @param series the series index (zero-based). 433 * 434 * @return A legend item for the series. 435 */ 436 @Override getLegendItem(int datasetIndex, int series)437 public LegendItem getLegendItem(int datasetIndex, int series) { 438 LegendItem result = null; 439 XYPlot xyplot = getPlot(); 440 if (xyplot != null) { 441 XYDataset dataset = xyplot.getDataset(datasetIndex); 442 if (dataset != null) { 443 XYSeriesLabelGenerator lg = getLegendItemLabelGenerator(); 444 String label = lg.generateLabel(dataset, series); 445 String description = label; 446 String toolTipText = null; 447 if (getLegendItemToolTipGenerator() != null) { 448 toolTipText = getLegendItemToolTipGenerator().generateLabel( 449 dataset, series); 450 } 451 String urlText = null; 452 if (getLegendItemURLGenerator() != null) { 453 urlText = getLegendItemURLGenerator().generateLabel( 454 dataset, series); 455 } 456 Paint paint = lookupSeriesPaint(series); 457 result = new LegendItem(label, description, toolTipText, 458 urlText, this.legendArea, paint); 459 result.setLabelFont(lookupLegendTextFont(series)); 460 Paint labelPaint = lookupLegendTextPaint(series); 461 if (labelPaint != null) { 462 result.setLabelPaint(labelPaint); 463 } 464 result.setDataset(dataset); 465 result.setDatasetIndex(datasetIndex); 466 result.setSeriesKey(dataset.getSeriesKey(series)); 467 result.setSeriesIndex(series); 468 } 469 } 470 return result; 471 } 472 473 /** 474 * Draws the visual representation of a single data item. 475 * 476 * @param g2 the graphics device. 477 * @param state the renderer state. 478 * @param dataArea the area within which the data is being drawn. 479 * @param info collects information about the drawing. 480 * @param plot the plot (can be used to obtain standard color information 481 * etc). 482 * @param domainAxis the domain axis. 483 * @param rangeAxis the range axis. 484 * @param dataset the dataset. 485 * @param series the series index (zero-based). 486 * @param item the item index (zero-based). 487 * @param crosshairState crosshair information for the plot 488 * (<code>null</code> permitted). 489 * @param pass the pass index. 490 */ 491 @Override drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass)492 public void drawItem(Graphics2D g2, XYItemRendererState state, 493 Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, 494 ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, 495 int series, int item, CrosshairState crosshairState, int pass) { 496 497 if (!getItemVisible(series, item)) { 498 return; 499 } 500 XYAreaRendererState areaState = (XYAreaRendererState) state; 501 502 // get the data point... 503 double x1 = dataset.getXValue(series, item); 504 double y1 = dataset.getYValue(series, item); 505 if (Double.isNaN(y1)) { 506 y1 = 0.0; 507 } 508 double transX1 = domainAxis.valueToJava2D(x1, dataArea, 509 plot.getDomainAxisEdge()); 510 double transY1 = rangeAxis.valueToJava2D(y1, dataArea, 511 plot.getRangeAxisEdge()); 512 513 // get the previous point and the next point so we can calculate a 514 // "hot spot" for the area (used by the chart entity)... 515 int itemCount = dataset.getItemCount(series); 516 double x0 = dataset.getXValue(series, Math.max(item - 1, 0)); 517 double y0 = dataset.getYValue(series, Math.max(item - 1, 0)); 518 if (Double.isNaN(y0)) { 519 y0 = 0.0; 520 } 521 double transX0 = domainAxis.valueToJava2D(x0, dataArea, 522 plot.getDomainAxisEdge()); 523 double transY0 = rangeAxis.valueToJava2D(y0, dataArea, 524 plot.getRangeAxisEdge()); 525 526 double x2 = dataset.getXValue(series, Math.min(item + 1, 527 itemCount - 1)); 528 double y2 = dataset.getYValue(series, Math.min(item + 1, 529 itemCount - 1)); 530 if (Double.isNaN(y2)) { 531 y2 = 0.0; 532 } 533 double transX2 = domainAxis.valueToJava2D(x2, dataArea, 534 plot.getDomainAxisEdge()); 535 double transY2 = rangeAxis.valueToJava2D(y2, dataArea, 536 plot.getRangeAxisEdge()); 537 538 double transZero = rangeAxis.valueToJava2D(0.0, dataArea, 539 plot.getRangeAxisEdge()); 540 GeneralPath hotspot = new GeneralPath(); 541 if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 542 moveTo(hotspot, transZero, ((transX0 + transX1) / 2.0)); 543 lineTo(hotspot, ((transY0 + transY1) / 2.0), 544 ((transX0 + transX1) / 2.0)); 545 lineTo(hotspot, transY1, transX1); 546 lineTo(hotspot, ((transY1 + transY2) / 2.0), 547 ((transX1 + transX2) / 2.0)); 548 lineTo(hotspot, transZero, ((transX1 + transX2) / 2.0)); 549 } 550 else { // vertical orientation 551 moveTo(hotspot, ((transX0 + transX1) / 2.0), transZero); 552 lineTo(hotspot, ((transX0 + transX1) / 2.0), 553 ((transY0 + transY1) / 2.0)); 554 lineTo(hotspot, transX1, transY1); 555 lineTo(hotspot, ((transX1 + transX2) / 2.0), 556 ((transY1 + transY2) / 2.0)); 557 lineTo(hotspot, ((transX1 + transX2) / 2.0), transZero); 558 } 559 hotspot.closePath(); 560 561 if (item == 0) { // create a new area polygon for the series 562 areaState.area = new GeneralPath(); 563 // the first point is (x, 0) 564 double zero = rangeAxis.valueToJava2D(0.0, dataArea, 565 plot.getRangeAxisEdge()); 566 if (plot.getOrientation() == PlotOrientation.VERTICAL) { 567 moveTo(areaState.area, transX1, zero); 568 } 569 else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 570 moveTo(areaState.area, zero, transX1); 571 } 572 } 573 574 // Add each point to Area (x, y) 575 if (plot.getOrientation() == PlotOrientation.VERTICAL) { 576 lineTo(areaState.area, transX1, transY1); 577 } 578 else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 579 lineTo(areaState.area, transY1, transX1); 580 } 581 582 PlotOrientation orientation = plot.getOrientation(); 583 Paint paint = getItemPaint(series, item); 584 Stroke stroke = getItemStroke(series, item); 585 g2.setPaint(paint); 586 g2.setStroke(stroke); 587 588 Shape shape; 589 if (getPlotShapes()) { 590 shape = getItemShape(series, item); 591 if (orientation == PlotOrientation.VERTICAL) { 592 shape = ShapeUtilities.createTranslatedShape(shape, transX1, 593 transY1); 594 } 595 else if (orientation == PlotOrientation.HORIZONTAL) { 596 shape = ShapeUtilities.createTranslatedShape(shape, transY1, 597 transX1); 598 } 599 g2.draw(shape); 600 } 601 602 if (getPlotLines()) { 603 if (item > 0) { 604 if (plot.getOrientation() == PlotOrientation.VERTICAL) { 605 areaState.line.setLine(transX0, transY0, transX1, transY1); 606 } 607 else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 608 areaState.line.setLine(transY0, transX0, transY1, transX1); 609 } 610 g2.draw(areaState.line); 611 } 612 } 613 614 // Check if the item is the last item for the series. 615 // and number of items > 0. We can't draw an area for a single point. 616 if (getPlotArea() && item > 0 && item == (itemCount - 1)) { 617 618 if (orientation == PlotOrientation.VERTICAL) { 619 // Add the last point (x,0) 620 lineTo(areaState.area, transX1, transZero); 621 areaState.area.closePath(); 622 } 623 else if (orientation == PlotOrientation.HORIZONTAL) { 624 // Add the last point (x,0) 625 lineTo(areaState.area, transZero, transX1); 626 areaState.area.closePath(); 627 } 628 629 if (this.useFillPaint) { 630 paint = lookupSeriesFillPaint(series); 631 } 632 if (paint instanceof GradientPaint) { 633 GradientPaint gp = (GradientPaint) paint; 634 GradientPaint adjGP = this.gradientTransformer.transform(gp, 635 dataArea); 636 g2.setPaint(adjGP); 637 } 638 g2.fill(areaState.area); 639 640 // draw an outline around the Area. 641 if (isOutline()) { 642 Shape area = areaState.area; 643 644 // Java2D has some issues drawing dashed lines around "large" 645 // geometrical shapes - for example, see bug 6620013 in the 646 // Java bug database. So, we'll check if the outline is 647 // dashed and, if it is, do our own clipping before drawing 648 // the outline... 649 Stroke outlineStroke = lookupSeriesOutlineStroke(series); 650 if (outlineStroke instanceof BasicStroke) { 651 BasicStroke bs = (BasicStroke) outlineStroke; 652 if (bs.getDashArray() != null) { 653 Area poly = new Area(areaState.area); 654 // we make the clip region slightly larger than the 655 // dataArea so that the clipped edges don't show lines 656 // on the chart 657 Area clip = new Area(new Rectangle2D.Double( 658 dataArea.getX() - 5.0, dataArea.getY() - 5.0, 659 dataArea.getWidth() + 10.0, 660 dataArea.getHeight() + 10.0)); 661 poly.intersect(clip); 662 area = poly; 663 } 664 } // end of workaround 665 666 g2.setStroke(outlineStroke); 667 g2.setPaint(lookupSeriesOutlinePaint(series)); 668 g2.draw(area); 669 } 670 } 671 672 int domainAxisIndex = plot.getDomainAxisIndex(domainAxis); 673 int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis); 674 updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex, 675 rangeAxisIndex, transX1, transY1, orientation); 676 677 // collect entity and tool tip information... 678 EntityCollection entities = state.getEntityCollection(); 679 if (entities != null) { 680 addEntity(entities, hotspot, dataset, series, item, 0.0, 0.0); 681 } 682 683 } 684 685 /** 686 * Returns a clone of the renderer. 687 * 688 * @return A clone. 689 * 690 * @throws CloneNotSupportedException if the renderer cannot be cloned. 691 */ 692 @Override clone()693 public Object clone() throws CloneNotSupportedException { 694 XYAreaRenderer clone = (XYAreaRenderer) super.clone(); 695 clone.legendArea = ShapeUtilities.clone(this.legendArea); 696 return clone; 697 } 698 699 /** 700 * Tests this renderer for equality with an arbitrary object. 701 * 702 * @param obj the object (<code>null</code> permitted). 703 * 704 * @return A boolean. 705 */ 706 @Override equals(Object obj)707 public boolean equals(Object obj) { 708 if (obj == this) { 709 return true; 710 } 711 if (!(obj instanceof XYAreaRenderer)) { 712 return false; 713 } 714 XYAreaRenderer that = (XYAreaRenderer) obj; 715 if (this.plotArea != that.plotArea) { 716 return false; 717 } 718 if (this.plotLines != that.plotLines) { 719 return false; 720 } 721 if (this.plotShapes != that.plotShapes) { 722 return false; 723 } 724 if (this.showOutline != that.showOutline) { 725 return false; 726 } 727 if (this.useFillPaint != that.useFillPaint) { 728 return false; 729 } 730 if (!this.gradientTransformer.equals(that.gradientTransformer)) { 731 return false; 732 } 733 if (!ShapeUtilities.equal(this.legendArea, that.legendArea)) { 734 return false; 735 } 736 return true; 737 } 738 739 /** 740 * Returns a hash code for this instance. 741 * 742 * @return A hash code. 743 */ 744 @Override hashCode()745 public int hashCode() { 746 int result = super.hashCode(); 747 result = HashUtilities.hashCode(result, this.plotArea); 748 result = HashUtilities.hashCode(result, this.plotLines); 749 result = HashUtilities.hashCode(result, this.plotShapes); 750 result = HashUtilities.hashCode(result, this.useFillPaint); 751 return result; 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 */ readObject(ObjectInputStream stream)762 private void readObject(ObjectInputStream stream) 763 throws IOException, ClassNotFoundException { 764 stream.defaultReadObject(); 765 this.legendArea = SerialUtilities.readShape(stream); 766 } 767 768 /** 769 * Provides serialization support. 770 * 771 * @param stream the output stream. 772 * 773 * @throws IOException if there is an I/O error. 774 */ writeObject(ObjectOutputStream stream)775 private void writeObject(ObjectOutputStream stream) throws IOException { 776 stream.defaultWriteObject(); 777 SerialUtilities.writeShape(this.legendArea, stream); 778 } 779 } 780