001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2014, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ------------------- 028 * XYAreaRenderer.java 029 * ------------------- 030 * (C) Copyright 2002-2014, by Hari and Contributors. 031 * 032 * Original Author: Hari (ourhari@hotmail.com); 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Richard Atkinson; 035 * Christian W. Zuckschwerdt; 036 * Martin Krauskopf; 037 * 038 * Changes: 039 * -------- 040 * 03-Apr-2002 : Version 1, contributed by Hari. This class is based on the 041 * StandardXYItemRenderer class (DG); 042 * 09-Apr-2002 : Removed the translated zero from the drawItem method - 043 * overridden the initialise() method to calculate it (DG); 044 * 30-May-2002 : Added tool tip generator to constructor to match super 045 * class (DG); 046 * 25-Jun-2002 : Removed unnecessary local variable (DG); 047 * 05-Aug-2002 : Small modification to drawItem method to support URLs for HTML 048 * image maps (RA); 049 * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG); 050 * 07-Nov-2002 : Renamed AreaXYItemRenderer --> XYAreaRenderer (DG); 051 * 25-Mar-2003 : Implemented Serializable (DG); 052 * 01-May-2003 : Modified drawItem() method signature (DG); 053 * 27-Jul-2003 : Made line and polygon properties protected rather than 054 * private (RA); 055 * 30-Jul-2003 : Modified entity constructor (CZ); 056 * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG); 057 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 058 * 07-Oct-2003 : Added renderer state (DG); 059 * 08-Dec-2003 : Modified hotspot for chart entity (DG); 060 * 10-Feb-2004 : Changed the drawItem() method to make cut-and-paste overriding 061 * easier. Also moved state class into this class (DG); 062 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState. Renamed 063 * XYToolTipGenerator --> XYItemLabelGenerator (DG); 064 * 15-Jul-2004 : Switched getX() with getXValue() and getY() with 065 * getYValue() (DG); 066 * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG); 067 * 19-Jan-2005 : Now accesses primitives only from dataset (DG); 068 * 21-Mar-2005 : Override getLegendItem() and equals() methods (DG); 069 * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG); 070 * ------------- JFREECHART 1.0.x --------------------------------------------- 071 * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG); 072 * 14-Feb-2007 : Fixed bug in clone() (DG); 073 * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG); 074 * 04-May-2007 : Set processVisibleItemsOnly flag to false (DG); 075 * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG); 076 * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG); 077 * 17-Jun-2008 : Apply legend font and paint attributes (DG); 078 * 31-Dec-2008 : Fix for bug 2471906 - dashed outlines performance issue (DG); 079 * 11-Jun-2009 : Added a useFillPaint flag and a GradientPaintTransformer for 080 * the paint under the series (DG); 081 * 06-Oct-2011 : Avoid GeneralPath methods requiring Java 1.5 (MK); 082 * 03-Jul-2013 : Use ParamChecks (DG); 083 * 084 */ 085 086package org.jfree.chart.renderer.xy; 087 088import java.awt.BasicStroke; 089import java.awt.GradientPaint; 090import java.awt.Graphics2D; 091import java.awt.Paint; 092import java.awt.Shape; 093import java.awt.Stroke; 094import java.awt.geom.Area; 095import java.awt.geom.GeneralPath; 096import java.awt.geom.Line2D; 097import java.awt.geom.Rectangle2D; 098import java.io.IOException; 099import java.io.ObjectInputStream; 100import java.io.ObjectOutputStream; 101 102import org.jfree.chart.HashUtilities; 103import org.jfree.chart.LegendItem; 104import org.jfree.chart.axis.ValueAxis; 105import org.jfree.chart.entity.EntityCollection; 106import org.jfree.chart.event.RendererChangeEvent; 107import org.jfree.chart.labels.XYSeriesLabelGenerator; 108import org.jfree.chart.labels.XYToolTipGenerator; 109import org.jfree.chart.plot.CrosshairState; 110import org.jfree.chart.plot.PlotOrientation; 111import org.jfree.chart.plot.PlotRenderingInfo; 112import org.jfree.chart.plot.XYPlot; 113import org.jfree.chart.urls.XYURLGenerator; 114import org.jfree.chart.util.ParamChecks; 115import org.jfree.data.xy.XYDataset; 116import org.jfree.io.SerialUtilities; 117import org.jfree.ui.GradientPaintTransformer; 118import org.jfree.ui.StandardGradientPaintTransformer; 119import org.jfree.util.PublicCloneable; 120import 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 */ 132public 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 */ 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 */ 218 public XYAreaRenderer() { 219 this(AREA); 220 } 221 222 /** 223 * Constructs a new renderer. 224 * 225 * @param type the type of the renderer. 226 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 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 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 */ 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 */ 775 private void writeObject(ObjectOutputStream stream) throws IOException { 776 stream.defaultWriteObject(); 777 SerialUtilities.writeShape(this.legendArea, stream); 778 } 779}