001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2011, 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-2011, 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 * 083 */ 084 085 package org.jfree.chart.renderer.xy; 086 087 import java.awt.BasicStroke; 088 import java.awt.GradientPaint; 089 import java.awt.Graphics2D; 090 import java.awt.Paint; 091 import java.awt.Shape; 092 import java.awt.Stroke; 093 import java.awt.geom.Area; 094 import java.awt.geom.GeneralPath; 095 import java.awt.geom.Line2D; 096 import java.awt.geom.Rectangle2D; 097 import java.io.IOException; 098 import java.io.ObjectInputStream; 099 import java.io.ObjectOutputStream; 100 101 import org.jfree.chart.HashUtilities; 102 import org.jfree.chart.LegendItem; 103 import org.jfree.chart.axis.ValueAxis; 104 import org.jfree.chart.entity.EntityCollection; 105 import org.jfree.chart.event.RendererChangeEvent; 106 import org.jfree.chart.labels.XYSeriesLabelGenerator; 107 import org.jfree.chart.labels.XYToolTipGenerator; 108 import org.jfree.chart.plot.CrosshairState; 109 import org.jfree.chart.plot.PlotOrientation; 110 import org.jfree.chart.plot.PlotRenderingInfo; 111 import org.jfree.chart.plot.XYPlot; 112 import org.jfree.chart.urls.XYURLGenerator; 113 import org.jfree.data.xy.XYDataset; 114 import org.jfree.io.SerialUtilities; 115 import org.jfree.ui.GradientPaintTransformer; 116 import org.jfree.ui.StandardGradientPaintTransformer; 117 import org.jfree.util.PublicCloneable; 118 import org.jfree.util.ShapeUtilities; 119 120 /** 121 * Area item renderer for an {@link XYPlot}. This class can draw (a) shapes at 122 * each point, or (b) lines between points, or (c) both shapes and lines, 123 * or (d) filled areas, or (e) filled areas and shapes. The example shown here 124 * is generated by the <code>XYAreaRendererDemo1.java</code> program included 125 * in the JFreeChart demo collection: 126 * <br><br> 127 * <img src="../../../../../images/XYAreaRendererSample.png" 128 * alt="XYAreaRendererSample.png" /> 129 */ 130 public class XYAreaRenderer extends AbstractXYItemRenderer 131 implements XYItemRenderer, PublicCloneable { 132 133 /** For serialization. */ 134 private static final long serialVersionUID = -4481971353973876747L; 135 136 /** 137 * A state object used by this renderer. 138 */ 139 static class XYAreaRendererState extends XYItemRendererState { 140 141 /** Working storage for the area under one series. */ 142 public GeneralPath area; 143 144 /** Working line that can be recycled. */ 145 public Line2D line; 146 147 /** 148 * Creates a new state. 149 * 150 * @param info the plot rendering info. 151 */ 152 public XYAreaRendererState(PlotRenderingInfo info) { 153 super(info); 154 this.area = new GeneralPath(); 155 this.line = new Line2D.Double(); 156 } 157 158 } 159 160 /** Useful constant for specifying the type of rendering (shapes only). */ 161 public static final int SHAPES = 1; 162 163 /** Useful constant for specifying the type of rendering (lines only). */ 164 public static final int LINES = 2; 165 166 /** 167 * Useful constant for specifying the type of rendering (shapes and lines). 168 */ 169 public static final int SHAPES_AND_LINES = 3; 170 171 /** Useful constant for specifying the type of rendering (area only). */ 172 public static final int AREA = 4; 173 174 /** 175 * Useful constant for specifying the type of rendering (area and shapes). 176 */ 177 public static final int AREA_AND_SHAPES = 5; 178 179 /** A flag indicating whether or not shapes are drawn at each XY point. */ 180 private boolean plotShapes; 181 182 /** A flag indicating whether or not lines are drawn between XY points. */ 183 private boolean plotLines; 184 185 /** A flag indicating whether or not Area are drawn at each XY point. */ 186 private boolean plotArea; 187 188 /** A flag that controls whether or not the outline is shown. */ 189 private boolean showOutline; 190 191 /** 192 * The shape used to represent an area in each legend item (this should 193 * never be <code>null</code>). 194 */ 195 private transient Shape legendArea; 196 197 /** 198 * A flag that can be set to specify that the fill paint should be used 199 * to fill the area under the renderer. 200 * 201 * @since 1.0.14 202 */ 203 private boolean useFillPaint; 204 205 /** 206 * A transformer that is applied to the paint used to fill under the 207 * area *if* it is an instance of GradientPaint. 208 * 209 * @since 1.0.14 210 */ 211 private GradientPaintTransformer gradientTransformer; 212 213 /** 214 * Constructs a new renderer. 215 */ 216 public XYAreaRenderer() { 217 this(AREA); 218 } 219 220 /** 221 * Constructs a new renderer. 222 * 223 * @param type the type of the renderer. 224 */ 225 public XYAreaRenderer(int type) { 226 this(type, null, null); 227 } 228 229 /** 230 * Constructs a new renderer. To specify the type of renderer, use one of 231 * the constants: <code>SHAPES</code>, <code>LINES</code>, 232 * <code>SHAPES_AND_LINES</code>, <code>AREA</code> or 233 * <code>AREA_AND_SHAPES</code>. 234 * 235 * @param type the type of renderer. 236 * @param toolTipGenerator the tool tip generator to use 237 * (<code>null</code> permitted). 238 * @param urlGenerator the URL generator (<code>null</code> permitted). 239 */ 240 public XYAreaRenderer(int type, XYToolTipGenerator toolTipGenerator, 241 XYURLGenerator urlGenerator) { 242 243 super(); 244 setBaseToolTipGenerator(toolTipGenerator); 245 setURLGenerator(urlGenerator); 246 247 if (type == SHAPES) { 248 this.plotShapes = true; 249 } 250 if (type == LINES) { 251 this.plotLines = true; 252 } 253 if (type == SHAPES_AND_LINES) { 254 this.plotShapes = true; 255 this.plotLines = true; 256 } 257 if (type == AREA) { 258 this.plotArea = true; 259 } 260 if (type == AREA_AND_SHAPES) { 261 this.plotArea = true; 262 this.plotShapes = true; 263 } 264 this.showOutline = false; 265 GeneralPath area = new GeneralPath(); 266 area.moveTo(0.0f, -4.0f); 267 area.lineTo(3.0f, -2.0f); 268 area.lineTo(4.0f, 4.0f); 269 area.lineTo(-4.0f, 4.0f); 270 area.lineTo(-3.0f, -2.0f); 271 area.closePath(); 272 this.legendArea = area; 273 this.useFillPaint = false; 274 this.gradientTransformer = new StandardGradientPaintTransformer(); 275 } 276 277 /** 278 * Returns true if shapes are being plotted by the renderer. 279 * 280 * @return <code>true</code> if shapes are being plotted by the renderer. 281 */ 282 public boolean getPlotShapes() { 283 return this.plotShapes; 284 } 285 286 /** 287 * Returns true if lines are being plotted by the renderer. 288 * 289 * @return <code>true</code> if lines are being plotted by the renderer. 290 */ 291 public boolean getPlotLines() { 292 return this.plotLines; 293 } 294 295 /** 296 * Returns true if Area is being plotted by the renderer. 297 * 298 * @return <code>true</code> if Area is being plotted by the renderer. 299 */ 300 public boolean getPlotArea() { 301 return this.plotArea; 302 } 303 304 /** 305 * Returns a flag that controls whether or not outlines of the areas are 306 * drawn. 307 * 308 * @return The flag. 309 * 310 * @see #setOutline(boolean) 311 */ 312 public boolean isOutline() { 313 return this.showOutline; 314 } 315 316 /** 317 * Sets a flag that controls whether or not outlines of the areas are drawn 318 * and sends a {@link RendererChangeEvent} to all registered listeners. 319 * 320 * @param show the flag. 321 * 322 * @see #isOutline() 323 */ 324 public void setOutline(boolean show) { 325 this.showOutline = show; 326 fireChangeEvent(); 327 } 328 329 /** 330 * Returns the shape used to represent an area in the legend. 331 * 332 * @return The legend area (never <code>null</code>). 333 */ 334 public Shape getLegendArea() { 335 return this.legendArea; 336 } 337 338 /** 339 * Sets the shape used as an area in each legend item and sends a 340 * {@link RendererChangeEvent} to all registered listeners. 341 * 342 * @param area the area (<code>null</code> not permitted). 343 */ 344 public void setLegendArea(Shape area) { 345 if (area == null) { 346 throw new IllegalArgumentException("Null 'area' argument."); 347 } 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 if (transformer == null) { 399 throw new IllegalArgumentException("Null 'transformer' argument."); 400 } 401 this.gradientTransformer = transformer; 402 fireChangeEvent(); 403 } 404 405 /** 406 * Initialises the renderer and returns a state object that should be 407 * passed to all subsequent calls to the drawItem() method. 408 * 409 * @param g2 the graphics device. 410 * @param dataArea the area inside the axes. 411 * @param plot the plot. 412 * @param data the data. 413 * @param info an optional info collection object to return data back to 414 * the caller. 415 * 416 * @return A state object for use by the renderer. 417 */ 418 public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, 419 XYPlot plot, XYDataset data, PlotRenderingInfo info) { 420 XYAreaRendererState state = new XYAreaRendererState(info); 421 422 // in the rendering process, there is special handling for item 423 // zero, so we can't support processing of visible data items only 424 state.setProcessVisibleItemsOnly(false); 425 return state; 426 } 427 428 /** 429 * Returns a default legend item for the specified series. Subclasses 430 * should override this method to generate customised items. 431 * 432 * @param datasetIndex the dataset index (zero-based). 433 * @param series the series index (zero-based). 434 * 435 * @return A legend item for the series. 436 */ 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 public void drawItem(Graphics2D g2, XYItemRendererState state, 492 Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, 493 ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, 494 int series, int item, CrosshairState crosshairState, int pass) { 495 496 if (!getItemVisible(series, item)) { 497 return; 498 } 499 XYAreaRendererState areaState = (XYAreaRendererState) state; 500 501 // get the data point... 502 double x1 = dataset.getXValue(series, item); 503 double y1 = dataset.getYValue(series, item); 504 if (Double.isNaN(y1)) { 505 y1 = 0.0; 506 } 507 double transX1 = domainAxis.valueToJava2D(x1, dataArea, 508 plot.getDomainAxisEdge()); 509 double transY1 = rangeAxis.valueToJava2D(y1, dataArea, 510 plot.getRangeAxisEdge()); 511 512 // get the previous point and the next point so we can calculate a 513 // "hot spot" for the area (used by the chart entity)... 514 int itemCount = dataset.getItemCount(series); 515 double x0 = dataset.getXValue(series, Math.max(item - 1, 0)); 516 double y0 = dataset.getYValue(series, Math.max(item - 1, 0)); 517 if (Double.isNaN(y0)) { 518 y0 = 0.0; 519 } 520 double transX0 = domainAxis.valueToJava2D(x0, dataArea, 521 plot.getDomainAxisEdge()); 522 double transY0 = rangeAxis.valueToJava2D(y0, dataArea, 523 plot.getRangeAxisEdge()); 524 525 double x2 = dataset.getXValue(series, Math.min(item + 1, 526 itemCount - 1)); 527 double y2 = dataset.getYValue(series, Math.min(item + 1, 528 itemCount - 1)); 529 if (Double.isNaN(y2)) { 530 y2 = 0.0; 531 } 532 double transX2 = domainAxis.valueToJava2D(x2, dataArea, 533 plot.getDomainAxisEdge()); 534 double transY2 = rangeAxis.valueToJava2D(y2, dataArea, 535 plot.getRangeAxisEdge()); 536 537 double transZero = rangeAxis.valueToJava2D(0.0, dataArea, 538 plot.getRangeAxisEdge()); 539 GeneralPath hotspot = new GeneralPath(); 540 if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 541 moveTo(hotspot, transZero, ((transX0 + transX1) / 2.0)); 542 lineTo(hotspot, ((transY0 + transY1) / 2.0), 543 ((transX0 + transX1) / 2.0)); 544 lineTo(hotspot, transY1, transX1); 545 lineTo(hotspot, ((transY1 + transY2) / 2.0), 546 ((transX1 + transX2) / 2.0)); 547 lineTo(hotspot, transZero, ((transX1 + transX2) / 2.0)); 548 } 549 else { // vertical orientation 550 moveTo(hotspot, ((transX0 + transX1) / 2.0), transZero); 551 lineTo(hotspot, ((transX0 + transX1) / 2.0), 552 ((transY0 + transY1) / 2.0)); 553 lineTo(hotspot, transX1, transY1); 554 lineTo(hotspot, ((transX1 + transX2) / 2.0), 555 ((transY1 + transY2) / 2.0)); 556 lineTo(hotspot, ((transX1 + transX2) / 2.0), transZero); 557 } 558 hotspot.closePath(); 559 560 if (item == 0) { // create a new area polygon for the series 561 areaState.area = new GeneralPath(); 562 // the first point is (x, 0) 563 double zero = rangeAxis.valueToJava2D(0.0, dataArea, 564 plot.getRangeAxisEdge()); 565 if (plot.getOrientation() == PlotOrientation.VERTICAL) { 566 moveTo(areaState.area, transX1, zero); 567 } 568 else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 569 moveTo(areaState.area, zero, transX1); 570 } 571 } 572 573 // Add each point to Area (x, y) 574 if (plot.getOrientation() == PlotOrientation.VERTICAL) { 575 lineTo(areaState.area, transX1, transY1); 576 } 577 else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 578 lineTo(areaState.area, transY1, transX1); 579 } 580 581 PlotOrientation orientation = plot.getOrientation(); 582 Paint paint = getItemPaint(series, item); 583 Stroke stroke = getItemStroke(series, item); 584 g2.setPaint(paint); 585 g2.setStroke(stroke); 586 587 Shape shape = null; 588 if (getPlotShapes()) { 589 shape = getItemShape(series, item); 590 if (orientation == PlotOrientation.VERTICAL) { 591 shape = ShapeUtilities.createTranslatedShape(shape, transX1, 592 transY1); 593 } 594 else if (orientation == PlotOrientation.HORIZONTAL) { 595 shape = ShapeUtilities.createTranslatedShape(shape, transY1, 596 transX1); 597 } 598 g2.draw(shape); 599 } 600 601 if (getPlotLines()) { 602 if (item > 0) { 603 if (plot.getOrientation() == PlotOrientation.VERTICAL) { 604 areaState.line.setLine(transX0, transY0, transX1, transY1); 605 } 606 else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 607 areaState.line.setLine(transY0, transX0, transY1, transX1); 608 } 609 g2.draw(areaState.line); 610 } 611 } 612 613 // Check if the item is the last item for the series. 614 // and number of items > 0. We can't draw an area for a single point. 615 if (getPlotArea() && item > 0 && item == (itemCount - 1)) { 616 617 if (orientation == PlotOrientation.VERTICAL) { 618 // Add the last point (x,0) 619 lineTo(areaState.area, transX1, transZero); 620 areaState.area.closePath(); 621 } 622 else if (orientation == PlotOrientation.HORIZONTAL) { 623 // Add the last point (x,0) 624 lineTo(areaState.area, transZero, transX1); 625 areaState.area.closePath(); 626 } 627 628 if (this.useFillPaint) { 629 paint = lookupSeriesFillPaint(series); 630 } 631 if (paint instanceof GradientPaint) { 632 GradientPaint gp = (GradientPaint) paint; 633 GradientPaint adjGP = this.gradientTransformer.transform(gp, 634 dataArea); 635 g2.setPaint(adjGP); 636 } 637 g2.fill(areaState.area); 638 639 // draw an outline around the Area. 640 if (isOutline()) { 641 Shape area = areaState.area; 642 643 // Java2D has some issues drawing dashed lines around "large" 644 // geometrical shapes - for example, see bug 6620013 in the 645 // Java bug database. So, we'll check if the outline is 646 // dashed and, if it is, do our own clipping before drawing 647 // the outline... 648 Stroke outlineStroke = lookupSeriesOutlineStroke(series); 649 if (outlineStroke instanceof BasicStroke) { 650 BasicStroke bs = (BasicStroke) outlineStroke; 651 if (bs.getDashArray() != null) { 652 Area poly = new Area(areaState.area); 653 // we make the clip region slightly larger than the 654 // dataArea so that the clipped edges don't show lines 655 // on the chart 656 Area clip = new Area(new Rectangle2D.Double( 657 dataArea.getX() - 5.0, dataArea.getY() - 5.0, 658 dataArea.getWidth() + 10.0, 659 dataArea.getHeight() + 10.0)); 660 poly.intersect(clip); 661 area = poly; 662 } 663 } // end of workaround 664 665 g2.setStroke(outlineStroke); 666 g2.setPaint(lookupSeriesOutlinePaint(series)); 667 g2.draw(area); 668 } 669 } 670 671 int domainAxisIndex = plot.getDomainAxisIndex(domainAxis); 672 int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis); 673 updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex, 674 rangeAxisIndex, transX1, transY1, orientation); 675 676 // collect entity and tool tip information... 677 EntityCollection entities = state.getEntityCollection(); 678 if (entities != null) { 679 addEntity(entities, hotspot, dataset, series, item, 0.0, 0.0); 680 } 681 682 } 683 684 /** 685 * Returns a clone of the renderer. 686 * 687 * @return A clone. 688 * 689 * @throws CloneNotSupportedException if the renderer cannot be cloned. 690 */ 691 public Object clone() throws CloneNotSupportedException { 692 XYAreaRenderer clone = (XYAreaRenderer) super.clone(); 693 clone.legendArea = ShapeUtilities.clone(this.legendArea); 694 return clone; 695 } 696 697 /** 698 * Tests this renderer for equality with an arbitrary object. 699 * 700 * @param obj the object (<code>null</code> permitted). 701 * 702 * @return A boolean. 703 */ 704 public boolean equals(Object obj) { 705 if (obj == this) { 706 return true; 707 } 708 if (!(obj instanceof XYAreaRenderer)) { 709 return false; 710 } 711 XYAreaRenderer that = (XYAreaRenderer) obj; 712 if (this.plotArea != that.plotArea) { 713 return false; 714 } 715 if (this.plotLines != that.plotLines) { 716 return false; 717 } 718 if (this.plotShapes != that.plotShapes) { 719 return false; 720 } 721 if (this.showOutline != that.showOutline) { 722 return false; 723 } 724 if (this.useFillPaint != that.useFillPaint) { 725 return false; 726 } 727 if (!this.gradientTransformer.equals(that.gradientTransformer)) { 728 return false; 729 } 730 if (!ShapeUtilities.equal(this.legendArea, that.legendArea)) { 731 return false; 732 } 733 return true; 734 } 735 736 /** 737 * Returns a hash code for this instance. 738 * 739 * @return A hash code. 740 */ 741 public int hashCode() { 742 int result = super.hashCode(); 743 result = HashUtilities.hashCode(result, this.plotArea); 744 result = HashUtilities.hashCode(result, this.plotLines); 745 result = HashUtilities.hashCode(result, this.plotShapes); 746 result = HashUtilities.hashCode(result, this.useFillPaint); 747 return result; 748 } 749 750 /** 751 * Provides serialization support. 752 * 753 * @param stream the input stream. 754 * 755 * @throws IOException if there is an I/O error. 756 * @throws ClassNotFoundException if there is a classpath problem. 757 */ 758 private void readObject(ObjectInputStream stream) 759 throws IOException, ClassNotFoundException { 760 stream.defaultReadObject(); 761 this.legendArea = SerialUtilities.readShape(stream); 762 } 763 764 /** 765 * Provides serialization support. 766 * 767 * @param stream the output stream. 768 * 769 * @throws IOException if there is an I/O error. 770 */ 771 private void writeObject(ObjectOutputStream stream) throws IOException { 772 stream.defaultWriteObject(); 773 SerialUtilities.writeShape(this.legendArea, stream); 774 } 775 }