001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * --------------------------- 028 * AbstractXYItemRenderer.java 029 * --------------------------- 030 * (C) Copyright 2002-2007, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Richard Atkinson; 034 * Focus Computer Services Limited; 035 * Tim Bardzil; 036 * Sergei Ivanov; 037 * 038 * $Id: AbstractXYItemRenderer.java,v 1.26.2.15 2007/03/23 13:43:46 mungady Exp $ 039 * 040 * Changes: 041 * -------- 042 * 15-Mar-2002 : Version 1 (DG); 043 * 09-Apr-2002 : Added a getToolTipGenerator() method reflecting the change in 044 * the XYItemRenderer interface (DG); 045 * 05-Aug-2002 : Added a urlGenerator member variable to support HTML image 046 * maps (RA); 047 * 20-Aug-2002 : Added property change events for the tooltip and URL 048 * generators (DG); 049 * 22-Aug-2002 : Moved property change support into AbstractRenderer class (DG); 050 * 23-Sep-2002 : Fixed errors reported by Checkstyle tool (DG); 051 * 18-Nov-2002 : Added methods for drawing grid lines (DG); 052 * 17-Jan-2003 : Moved plot classes into a separate package (DG); 053 * 25-Mar-2003 : Implemented Serializable (DG); 054 * 01-May-2003 : Modified initialise() return type and drawItem() method 055 * signature (DG); 056 * 15-May-2003 : Modified to take into account the plot orientation (DG); 057 * 21-May-2003 : Added labels to markers (DG); 058 * 05-Jun-2003 : Added domain and range grid bands (sponsored by Focus Computer 059 * Services Ltd) (DG); 060 * 27-Jul-2003 : Added getRangeType() to support stacked XY area charts (RA); 061 * 31-Jul-2003 : Deprecated all but the default constructor (DG); 062 * 13-Aug-2003 : Implemented Cloneable (DG); 063 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 064 * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG); 065 * 05-Nov-2003 : Fixed marker rendering bug (833623) (DG); 066 * 11-Feb-2004 : Updated labelling for markers (DG); 067 * 25-Feb-2004 : Added updateCrosshairValues() method. Moved deprecated code 068 * to bottom of source file (DG); 069 * 16-Apr-2004 : Added support for IntervalMarker in drawRangeMarker() method 070 * - thanks to Tim Bardzil (DG); 071 * 05-May-2004 : Fixed bug (948310) where interval markers extend beyond axis 072 * range (DG); 073 * 03-Jun-2004 : Fixed more bugs in drawing interval markers (DG); 074 * 26-Aug-2004 : Added the addEntity() method (DG); 075 * 29-Sep-2004 : Added annotation support (with layers) (DG); 076 * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities --> 077 * TextUtilities (DG); 078 * 06-Oct-2004 : Added findDomainBounds() method and renamed 079 * getRangeExtent() --> findRangeBounds() (DG); 080 * 07-Jan-2005 : Removed deprecated code (DG); 081 * 27-Jan-2005 : Modified getLegendItem() to omit hidden series (DG); 082 * 24-Feb-2005 : Added getLegendItems() method (DG); 083 * 08-Mar-2005 : Fixed positioning of marker labels (DG); 084 * 20-Apr-2005 : Renamed XYLabelGenerator --> XYItemLabelGenerator and 085 * added generators for legend labels, tooltips and URLs (DG); 086 * 01-Jun-2005 : Handle one dimension of the marker label adjustment 087 * automatically (DG); 088 * ------------- JFREECHART 1.0.x --------------------------------------------- 089 * 20-Jul-2006 : Set dataset and series indices in LegendItem (DG); 090 * 24-Oct-2006 : Respect alpha setting in markers (see patch 1567843 by Sergei 091 * Ivanov) (DG); 092 * 24-Oct-2006 : Added code to draw outlines for interval markers (DG); 093 * 24-Nov-2006 : Fixed cloning for legend item generators (DG); 094 * 06-Feb-2007 : Added new updateCrosshairValues() method that takes into 095 * account multiple axis plots (see bug 1086307) (DG); 096 * 20-Feb-2007 : Fixed equals() method implementation (DG); 097 * 01-Mar-2007 : Fixed interval marker drawing (patch 1670686 thanks to 098 * Sergei Ivanov) (DG); 099 * 22-Mar-2007 : Modified the tool tip generator look up (DG); 100 * 23-Mar-2007 : Added drawDomainLine() method (DG); 101 * 102 */ 103 104 package org.jfree.chart.renderer.xy; 105 106 import java.awt.AlphaComposite; 107 import java.awt.Composite; 108 import java.awt.Font; 109 import java.awt.GradientPaint; 110 import java.awt.Graphics2D; 111 import java.awt.Paint; 112 import java.awt.Shape; 113 import java.awt.Stroke; 114 import java.awt.geom.Ellipse2D; 115 import java.awt.geom.Line2D; 116 import java.awt.geom.Point2D; 117 import java.awt.geom.Rectangle2D; 118 import java.io.Serializable; 119 import java.util.Iterator; 120 import java.util.List; 121 122 import org.jfree.chart.LegendItem; 123 import org.jfree.chart.LegendItemCollection; 124 import org.jfree.chart.annotations.XYAnnotation; 125 import org.jfree.chart.axis.ValueAxis; 126 import org.jfree.chart.entity.EntityCollection; 127 import org.jfree.chart.entity.XYItemEntity; 128 import org.jfree.chart.event.RendererChangeEvent; 129 import org.jfree.chart.labels.ItemLabelPosition; 130 import org.jfree.chart.labels.StandardXYSeriesLabelGenerator; 131 import org.jfree.chart.labels.XYItemLabelGenerator; 132 import org.jfree.chart.labels.XYSeriesLabelGenerator; 133 import org.jfree.chart.labels.XYToolTipGenerator; 134 import org.jfree.chart.plot.CrosshairState; 135 import org.jfree.chart.plot.DrawingSupplier; 136 import org.jfree.chart.plot.IntervalMarker; 137 import org.jfree.chart.plot.Marker; 138 import org.jfree.chart.plot.Plot; 139 import org.jfree.chart.plot.PlotOrientation; 140 import org.jfree.chart.plot.PlotRenderingInfo; 141 import org.jfree.chart.plot.ValueMarker; 142 import org.jfree.chart.plot.XYPlot; 143 import org.jfree.chart.renderer.AbstractRenderer; 144 import org.jfree.chart.urls.XYURLGenerator; 145 import org.jfree.data.Range; 146 import org.jfree.data.general.DatasetUtilities; 147 import org.jfree.data.xy.XYDataset; 148 import org.jfree.text.TextUtilities; 149 import org.jfree.ui.GradientPaintTransformer; 150 import org.jfree.ui.Layer; 151 import org.jfree.ui.LengthAdjustmentType; 152 import org.jfree.ui.RectangleAnchor; 153 import org.jfree.ui.RectangleInsets; 154 import org.jfree.util.ObjectList; 155 import org.jfree.util.ObjectUtilities; 156 import org.jfree.util.PublicCloneable; 157 158 /** 159 * A base class that can be used to create new {@link XYItemRenderer} 160 * implementations. 161 */ 162 public abstract class AbstractXYItemRenderer extends AbstractRenderer 163 implements XYItemRenderer, 164 Cloneable, 165 Serializable { 166 167 /** For serialization. */ 168 private static final long serialVersionUID = 8019124836026607990L; 169 170 /** The plot. */ 171 private XYPlot plot; 172 173 /** The item label generator for ALL series. */ 174 private XYItemLabelGenerator itemLabelGenerator; 175 176 /** A list of item label generators (one per series). */ 177 private ObjectList itemLabelGeneratorList; 178 179 /** The base item label generator. */ 180 private XYItemLabelGenerator baseItemLabelGenerator; 181 182 /** The tool tip generator for ALL series. */ 183 private XYToolTipGenerator toolTipGenerator; 184 185 /** A list of tool tip generators (one per series). */ 186 private ObjectList toolTipGeneratorList; 187 188 /** The base tool tip generator. */ 189 private XYToolTipGenerator baseToolTipGenerator; 190 191 /** The URL text generator. */ 192 private XYURLGenerator urlGenerator; 193 194 /** 195 * Annotations to be drawn in the background layer ('underneath' the data 196 * items). 197 */ 198 private List backgroundAnnotations; 199 200 /** 201 * Annotations to be drawn in the foreground layer ('on top' of the data 202 * items). 203 */ 204 private List foregroundAnnotations; 205 206 /** The default radius for the entity 'hotspot' */ 207 private int defaultEntityRadius; 208 209 /** The legend item label generator. */ 210 private XYSeriesLabelGenerator legendItemLabelGenerator; 211 212 /** The legend item tool tip generator. */ 213 private XYSeriesLabelGenerator legendItemToolTipGenerator; 214 215 /** The legend item URL generator. */ 216 private XYSeriesLabelGenerator legendItemURLGenerator; 217 218 /** 219 * Creates a renderer where the tooltip generator and the URL generator are 220 * both <code>null</code>. 221 */ 222 protected AbstractXYItemRenderer() { 223 this.itemLabelGenerator = null; 224 this.itemLabelGeneratorList = new ObjectList(); 225 this.toolTipGenerator = null; 226 this.toolTipGeneratorList = new ObjectList(); 227 this.urlGenerator = null; 228 this.backgroundAnnotations = new java.util.ArrayList(); 229 this.foregroundAnnotations = new java.util.ArrayList(); 230 this.defaultEntityRadius = 3; 231 this.legendItemLabelGenerator = new StandardXYSeriesLabelGenerator( 232 "{0}"); 233 } 234 235 /** 236 * Returns the number of passes through the data that the renderer requires 237 * in order to draw the chart. Most charts will require a single pass, but 238 * some require two passes. 239 * 240 * @return The pass count. 241 */ 242 public int getPassCount() { 243 return 1; 244 } 245 246 /** 247 * Returns the plot that the renderer is assigned to. 248 * 249 * @return The plot (possibly <code>null</code>). 250 */ 251 public XYPlot getPlot() { 252 return this.plot; 253 } 254 255 /** 256 * Sets the plot that the renderer is assigned to. 257 * 258 * @param plot the plot (<code>null</code> permitted). 259 */ 260 public void setPlot(XYPlot plot) { 261 this.plot = plot; 262 } 263 264 /** 265 * Initialises the renderer and returns a state object that should be 266 * passed to all subsequent calls to the drawItem() method. 267 * <P> 268 * This method will be called before the first item is rendered, giving the 269 * renderer an opportunity to initialise any state information it wants to 270 * maintain. The renderer can do nothing if it chooses. 271 * 272 * @param g2 the graphics device. 273 * @param dataArea the area inside the axes. 274 * @param plot the plot. 275 * @param data the data. 276 * @param info an optional info collection object to return data back to 277 * the caller. 278 * 279 * @return The renderer state (never <code>null</code>). 280 */ 281 public XYItemRendererState initialise(Graphics2D g2, 282 Rectangle2D dataArea, 283 XYPlot plot, 284 XYDataset data, 285 PlotRenderingInfo info) { 286 287 XYItemRendererState state = new XYItemRendererState(info); 288 return state; 289 290 } 291 292 // ITEM LABEL GENERATOR 293 294 /** 295 * Returns the label generator for a data item. This implementation simply 296 * passes control to the {@link #getSeriesItemLabelGenerator(int)} method. 297 * If, for some reason, you want a different generator for individual 298 * items, you can override this method. 299 * 300 * @param series the series index (zero based). 301 * @param item the item index (zero based). 302 * 303 * @return The generator (possibly <code>null</code>). 304 */ 305 public XYItemLabelGenerator getItemLabelGenerator(int series, int item) { 306 // return the generator for ALL series, if there is one... 307 if (this.itemLabelGenerator != null) { 308 return this.itemLabelGenerator; 309 } 310 311 // otherwise look up the generator table 312 XYItemLabelGenerator generator 313 = (XYItemLabelGenerator) this.itemLabelGeneratorList.get(series); 314 if (generator == null) { 315 generator = this.baseItemLabelGenerator; 316 } 317 return generator; 318 } 319 320 /** 321 * Returns the item label generator for a series. 322 * 323 * @param series the series index (zero based). 324 * 325 * @return The generator (possibly <code>null</code>). 326 */ 327 public XYItemLabelGenerator getSeriesItemLabelGenerator(int series) { 328 return (XYItemLabelGenerator) this.itemLabelGeneratorList.get(series); 329 } 330 331 /** 332 * Returns the item label generator override. 333 * 334 * @return The generator (possibly <code>null</code>). 335 * 336 * @since 1.0.5 337 * 338 * @see #setItemLabelGenerator(XYItemLabelGenerator) 339 */ 340 public XYItemLabelGenerator getItemLabelGenerator() { 341 return this.itemLabelGenerator; 342 } 343 344 /** 345 * Sets the item label generator for ALL series and sends a 346 * {@link RendererChangeEvent} to all registered listeners. 347 * 348 * @param generator the generator (<code>null</code> permitted). 349 * 350 * @see #getItemLabelGenerator() 351 */ 352 public void setItemLabelGenerator(XYItemLabelGenerator generator) { 353 this.itemLabelGenerator = generator; 354 notifyListeners(new RendererChangeEvent(this)); 355 } 356 357 /** 358 * Sets the item label generator for a series and sends a 359 * {@link RendererChangeEvent} to all registered listeners. 360 * 361 * @param series the series index (zero based). 362 * @param generator the generator (<code>null</code> permitted). 363 */ 364 public void setSeriesItemLabelGenerator(int series, 365 XYItemLabelGenerator generator) { 366 this.itemLabelGeneratorList.set(series, generator); 367 notifyListeners(new RendererChangeEvent(this)); 368 } 369 370 /** 371 * Returns the base item label generator. 372 * 373 * @return The generator (possibly <code>null</code>). 374 */ 375 public XYItemLabelGenerator getBaseItemLabelGenerator() { 376 return this.baseItemLabelGenerator; 377 } 378 379 /** 380 * Sets the base item label generator and sends a 381 * {@link RendererChangeEvent} to all registered listeners. 382 * 383 * @param generator the generator (<code>null</code> permitted). 384 */ 385 public void setBaseItemLabelGenerator(XYItemLabelGenerator generator) { 386 this.baseItemLabelGenerator = generator; 387 notifyListeners(new RendererChangeEvent(this)); 388 } 389 390 // TOOL TIP GENERATOR 391 392 /** 393 * Returns the tool tip generator for a data item. If, for some reason, 394 * you want a different generator for individual items, you can override 395 * this method. 396 * 397 * @param series the series index (zero based). 398 * @param item the item index (zero based). 399 * 400 * @return The generator (possibly <code>null</code>). 401 */ 402 public XYToolTipGenerator getToolTipGenerator(int series, int item) { 403 // return the generator for ALL series, if there is one... 404 if (this.toolTipGenerator != null) { 405 return this.toolTipGenerator; 406 } 407 408 // otherwise look up the generator table 409 XYToolTipGenerator generator 410 = (XYToolTipGenerator) this.toolTipGeneratorList.get(series); 411 if (generator == null) { 412 generator = this.baseToolTipGenerator; 413 } 414 return generator; 415 } 416 417 /** 418 * Returns the override tool tip generator. 419 * 420 * @return The tool tip generator (possible <code>null</code>). 421 * 422 * @since 1.0.5 423 * 424 * @see #setToolTipGenerator(XYToolTipGenerator) 425 */ 426 public XYToolTipGenerator getToolTipGenerator() { 427 return this.toolTipGenerator; 428 } 429 430 /** 431 * Sets the tool tip generator for ALL series and sends a 432 * {@link RendererChangeEvent} to all registered listeners. 433 * 434 * @param generator the generator (<code>null</code> permitted). 435 * 436 * @see #getToolTipGenerator() 437 */ 438 public void setToolTipGenerator(XYToolTipGenerator generator) { 439 this.toolTipGenerator = generator; 440 notifyListeners(new RendererChangeEvent(this)); 441 } 442 443 /** 444 * Returns the tool tip generator for a series. 445 * 446 * @param series the series index (zero based). 447 * 448 * @return The generator (possibly <code>null</code>). 449 */ 450 public XYToolTipGenerator getSeriesToolTipGenerator(int series) { 451 return (XYToolTipGenerator) this.toolTipGeneratorList.get(series); 452 } 453 454 /** 455 * Sets the tool tip generator for a series and sends a 456 * {@link RendererChangeEvent} to all registered listeners. 457 * 458 * @param series the series index (zero based). 459 * @param generator the generator (<code>null</code> permitted). 460 */ 461 public void setSeriesToolTipGenerator(int series, 462 XYToolTipGenerator generator) { 463 this.toolTipGeneratorList.set(series, generator); 464 notifyListeners(new RendererChangeEvent(this)); 465 } 466 467 /** 468 * Returns the base tool tip generator. 469 * 470 * @return The generator (possibly <code>null</code>). 471 * 472 * @see #setBaseToolTipGenerator(XYToolTipGenerator) 473 */ 474 public XYToolTipGenerator getBaseToolTipGenerator() { 475 return this.baseToolTipGenerator; 476 } 477 478 /** 479 * Sets the base tool tip generator and sends a {@link RendererChangeEvent} 480 * to all registered listeners. 481 * 482 * @param generator the generator (<code>null</code> permitted). 483 * 484 * @see #getBaseToolTipGenerator() 485 */ 486 public void setBaseToolTipGenerator(XYToolTipGenerator generator) { 487 this.baseToolTipGenerator = generator; 488 notifyListeners(new RendererChangeEvent(this)); 489 } 490 491 // URL GENERATOR 492 493 /** 494 * Returns the URL generator for HTML image maps. 495 * 496 * @return The URL generator (possibly <code>null</code>). 497 */ 498 public XYURLGenerator getURLGenerator() { 499 return this.urlGenerator; 500 } 501 502 /** 503 * Sets the URL generator for HTML image maps. 504 * 505 * @param urlGenerator the URL generator (<code>null</code> permitted). 506 */ 507 public void setURLGenerator(XYURLGenerator urlGenerator) { 508 this.urlGenerator = urlGenerator; 509 notifyListeners(new RendererChangeEvent(this)); 510 } 511 512 /** 513 * Adds an annotation and sends a {@link RendererChangeEvent} to all 514 * registered listeners. The annotation is added to the foreground 515 * layer. 516 * 517 * @param annotation the annotation (<code>null</code> not permitted). 518 */ 519 public void addAnnotation(XYAnnotation annotation) { 520 // defer argument checking 521 addAnnotation(annotation, Layer.FOREGROUND); 522 } 523 524 /** 525 * Adds an annotation to the specified layer. 526 * 527 * @param annotation the annotation (<code>null</code> not permitted). 528 * @param layer the layer (<code>null</code> not permitted). 529 */ 530 public void addAnnotation(XYAnnotation annotation, Layer layer) { 531 if (annotation == null) { 532 throw new IllegalArgumentException("Null 'annotation' argument."); 533 } 534 if (layer.equals(Layer.FOREGROUND)) { 535 this.foregroundAnnotations.add(annotation); 536 notifyListeners(new RendererChangeEvent(this)); 537 } 538 else if (layer.equals(Layer.BACKGROUND)) { 539 this.backgroundAnnotations.add(annotation); 540 notifyListeners(new RendererChangeEvent(this)); 541 } 542 else { 543 // should never get here 544 throw new RuntimeException("Unknown layer."); 545 } 546 } 547 /** 548 * Removes the specified annotation and sends a {@link RendererChangeEvent} 549 * to all registered listeners. 550 * 551 * @param annotation the annotation to remove (<code>null</code> not 552 * permitted). 553 * 554 * @return A boolean to indicate whether or not the annotation was 555 * successfully removed. 556 */ 557 public boolean removeAnnotation(XYAnnotation annotation) { 558 boolean removed = this.foregroundAnnotations.remove(annotation); 559 removed = removed & this.backgroundAnnotations.remove(annotation); 560 notifyListeners(new RendererChangeEvent(this)); 561 return removed; 562 } 563 564 /** 565 * Removes all annotations and sends a {@link RendererChangeEvent} 566 * to all registered listeners. 567 */ 568 public void removeAnnotations() { 569 this.foregroundAnnotations.clear(); 570 this.backgroundAnnotations.clear(); 571 notifyListeners(new RendererChangeEvent(this)); 572 } 573 574 /** 575 * Returns the radius of the circle used for the default entity area 576 * when no area is specified. 577 * 578 * @return A radius. 579 */ 580 public int getDefaultEntityRadius() { 581 return this.defaultEntityRadius; 582 } 583 584 /** 585 * Sets the radius of the circle used for the default entity area 586 * when no area is specified. 587 * 588 * @param radius the radius. 589 */ 590 public void setDefaultEntityRadius(int radius) { 591 this.defaultEntityRadius = radius; 592 } 593 594 /** 595 * Returns the legend item label generator. 596 * 597 * @return The label generator (never <code>null</code>). 598 * 599 * @see #setLegendItemLabelGenerator(XYSeriesLabelGenerator) 600 */ 601 public XYSeriesLabelGenerator getLegendItemLabelGenerator() { 602 return this.legendItemLabelGenerator; 603 } 604 605 /** 606 * Sets the legend item label generator and sends a 607 * {@link RendererChangeEvent} to all registered listeners. 608 * 609 * @param generator the generator (<code>null</code> not permitted). 610 * 611 * @see #getLegendItemLabelGenerator() 612 */ 613 public void setLegendItemLabelGenerator(XYSeriesLabelGenerator generator) { 614 if (generator == null) { 615 throw new IllegalArgumentException("Null 'generator' argument."); 616 } 617 this.legendItemLabelGenerator = generator; 618 notifyListeners(new RendererChangeEvent(this)); 619 } 620 621 /** 622 * Returns the legend item tool tip generator. 623 * 624 * @return The tool tip generator (possibly <code>null</code>). 625 * 626 * @see #setLegendItemToolTipGenerator(XYSeriesLabelGenerator) 627 */ 628 public XYSeriesLabelGenerator getLegendItemToolTipGenerator() { 629 return this.legendItemToolTipGenerator; 630 } 631 632 /** 633 * Sets the legend item tool tip generator and sends a 634 * {@link RendererChangeEvent} to all registered listeners. 635 * 636 * @param generator the generator (<code>null</code> permitted). 637 * 638 * @see #getLegendItemToolTipGenerator() 639 */ 640 public void setLegendItemToolTipGenerator( 641 XYSeriesLabelGenerator generator) { 642 this.legendItemToolTipGenerator = generator; 643 notifyListeners(new RendererChangeEvent(this)); 644 } 645 646 /** 647 * Returns the legend item URL generator. 648 * 649 * @return The URL generator (possibly <code>null</code>). 650 * 651 * @see #setLegendItemURLGenerator(XYSeriesLabelGenerator) 652 */ 653 public XYSeriesLabelGenerator getLegendItemURLGenerator() { 654 return this.legendItemURLGenerator; 655 } 656 657 /** 658 * Sets the legend item URL generator and sends a 659 * {@link RendererChangeEvent} to all registered listeners. 660 * 661 * @param generator the generator (<code>null</code> permitted). 662 * 663 * @see #getLegendItemURLGenerator() 664 */ 665 public void setLegendItemURLGenerator(XYSeriesLabelGenerator generator) { 666 this.legendItemURLGenerator = generator; 667 notifyListeners(new RendererChangeEvent(this)); 668 } 669 670 /** 671 * Returns the lower and upper bounds (range) of the x-values in the 672 * specified dataset. 673 * 674 * @param dataset the dataset (<code>null</code> permitted). 675 * 676 * @return The range (<code>null</code> if the dataset is <code>null</code> 677 * or empty). 678 */ 679 public Range findDomainBounds(XYDataset dataset) { 680 if (dataset != null) { 681 return DatasetUtilities.findDomainBounds(dataset, false); 682 } 683 else { 684 return null; 685 } 686 } 687 688 /** 689 * Returns the range of values the renderer requires to display all the 690 * items from the specified dataset. 691 * 692 * @param dataset the dataset (<code>null</code> permitted). 693 * 694 * @return The range (<code>null</code> if the dataset is <code>null</code> 695 * or empty). 696 */ 697 public Range findRangeBounds(XYDataset dataset) { 698 if (dataset != null) { 699 return DatasetUtilities.findRangeBounds(dataset, false); 700 } 701 else { 702 return null; 703 } 704 } 705 706 /** 707 * Returns a (possibly empty) collection of legend items for the series 708 * that this renderer is responsible for drawing. 709 * 710 * @return The legend item collection (never <code>null</code>). 711 */ 712 public LegendItemCollection getLegendItems() { 713 if (this.plot == null) { 714 return new LegendItemCollection(); 715 } 716 LegendItemCollection result = new LegendItemCollection(); 717 int index = this.plot.getIndexOf(this); 718 XYDataset dataset = this.plot.getDataset(index); 719 if (dataset != null) { 720 int seriesCount = dataset.getSeriesCount(); 721 for (int i = 0; i < seriesCount; i++) { 722 if (isSeriesVisibleInLegend(i)) { 723 LegendItem item = getLegendItem(index, i); 724 if (item != null) { 725 result.add(item); 726 } 727 } 728 } 729 730 } 731 return result; 732 } 733 734 /** 735 * Returns a default legend item for the specified series. Subclasses 736 * should override this method to generate customised items. 737 * 738 * @param datasetIndex the dataset index (zero-based). 739 * @param series the series index (zero-based). 740 * 741 * @return A legend item for the series. 742 */ 743 public LegendItem getLegendItem(int datasetIndex, int series) { 744 LegendItem result = null; 745 XYPlot xyplot = getPlot(); 746 if (xyplot != null) { 747 XYDataset dataset = xyplot.getDataset(datasetIndex); 748 if (dataset != null) { 749 String label = this.legendItemLabelGenerator.generateLabel( 750 dataset, series); 751 String description = label; 752 String toolTipText = null; 753 if (getLegendItemToolTipGenerator() != null) { 754 toolTipText = getLegendItemToolTipGenerator().generateLabel( 755 dataset, series); 756 } 757 String urlText = null; 758 if (getLegendItemURLGenerator() != null) { 759 urlText = getLegendItemURLGenerator().generateLabel( 760 dataset, series); 761 } 762 Shape shape = getSeriesShape(series); 763 Paint paint = getSeriesPaint(series); 764 Paint outlinePaint = getSeriesOutlinePaint(series); 765 Stroke outlineStroke = getSeriesOutlineStroke(series); 766 result = new LegendItem(label, description, toolTipText, 767 urlText, shape, paint, outlineStroke, outlinePaint); 768 result.setSeriesIndex(series); 769 result.setDatasetIndex(datasetIndex); 770 } 771 } 772 return result; 773 } 774 775 /** 776 * Fills a band between two values on the axis. This can be used to color 777 * bands between the grid lines. 778 * 779 * @param g2 the graphics device. 780 * @param plot the plot. 781 * @param axis the domain axis. 782 * @param dataArea the data area. 783 * @param start the start value. 784 * @param end the end value. 785 */ 786 public void fillDomainGridBand(Graphics2D g2, 787 XYPlot plot, 788 ValueAxis axis, 789 Rectangle2D dataArea, 790 double start, double end) { 791 792 double x1 = axis.valueToJava2D(start, dataArea, 793 plot.getDomainAxisEdge()); 794 double x2 = axis.valueToJava2D(end, dataArea, 795 plot.getDomainAxisEdge()); 796 // TODO: need to change the next line to take account of plot 797 // orientation... 798 Rectangle2D band = new Rectangle2D.Double(x1, dataArea.getMinY(), 799 x2 - x1, dataArea.getMaxY() - dataArea.getMinY()); 800 Paint paint = plot.getDomainTickBandPaint(); 801 802 if (paint != null) { 803 g2.setPaint(paint); 804 g2.fill(band); 805 } 806 807 } 808 809 /** 810 * Fills a band between two values on the range axis. This can be used to 811 * color bands between the grid lines. 812 * 813 * @param g2 the graphics device. 814 * @param plot the plot. 815 * @param axis the range axis. 816 * @param dataArea the data area. 817 * @param start the start value. 818 * @param end the end value. 819 */ 820 public void fillRangeGridBand(Graphics2D g2, 821 XYPlot plot, 822 ValueAxis axis, 823 Rectangle2D dataArea, 824 double start, double end) { 825 826 double y1 = axis.valueToJava2D(start, dataArea, 827 plot.getRangeAxisEdge()); 828 double y2 = axis.valueToJava2D(end, dataArea, plot.getRangeAxisEdge()); 829 // TODO: need to change the next line to take account of the plot 830 // orientation 831 Rectangle2D band = new Rectangle2D.Double(dataArea.getMinX(), y2, 832 dataArea.getWidth(), y1 - y2); 833 Paint paint = plot.getRangeTickBandPaint(); 834 835 if (paint != null) { 836 g2.setPaint(paint); 837 g2.fill(band); 838 } 839 840 } 841 842 /** 843 * Draws a grid line against the range axis. 844 * 845 * @param g2 the graphics device. 846 * @param plot the plot. 847 * @param axis the value axis. 848 * @param dataArea the area for plotting data (not yet adjusted for any 849 * 3D effect). 850 * @param value the value at which the grid line should be drawn. 851 */ 852 public void drawDomainGridLine(Graphics2D g2, 853 XYPlot plot, 854 ValueAxis axis, 855 Rectangle2D dataArea, 856 double value) { 857 858 Range range = axis.getRange(); 859 if (!range.contains(value)) { 860 return; 861 } 862 863 PlotOrientation orientation = plot.getOrientation(); 864 double v = axis.valueToJava2D(value, dataArea, 865 plot.getDomainAxisEdge()); 866 Line2D line = null; 867 if (orientation == PlotOrientation.HORIZONTAL) { 868 line = new Line2D.Double(dataArea.getMinX(), v, 869 dataArea.getMaxX(), v); 870 } 871 else if (orientation == PlotOrientation.VERTICAL) { 872 line = new Line2D.Double(v, dataArea.getMinY(), v, 873 dataArea.getMaxY()); 874 } 875 876 Paint paint = plot.getDomainGridlinePaint(); 877 Stroke stroke = plot.getDomainGridlineStroke(); 878 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT); 879 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE); 880 g2.draw(line); 881 882 } 883 884 /** 885 * Draws a line perpendicular to the domain axis. 886 * 887 * @param g2 the graphics device. 888 * @param plot the plot. 889 * @param axis the value axis. 890 * @param dataArea the area for plotting data (not yet adjusted for any 3D 891 * effect). 892 * @param value the value at which the grid line should be drawn. 893 * @param paint the paint. 894 * @param stroke the stroke. 895 * 896 * @since 1.0.5 897 */ 898 public void drawDomainLine(Graphics2D g2, XYPlot plot, ValueAxis axis, 899 Rectangle2D dataArea, double value, Paint paint, Stroke stroke) { 900 901 Range range = axis.getRange(); 902 if (!range.contains(value)) { 903 return; 904 } 905 906 PlotOrientation orientation = plot.getOrientation(); 907 Line2D line = null; 908 double v = axis.valueToJava2D(value, dataArea, plot.getDomainAxisEdge()); 909 if (orientation == PlotOrientation.HORIZONTAL) { 910 line = new Line2D.Double(dataArea.getMinX(), v, dataArea.getMaxX(), 911 v); 912 } 913 else if (orientation == PlotOrientation.VERTICAL) { 914 line = new Line2D.Double(v, dataArea.getMinY(), v, 915 dataArea.getMaxY()); 916 } 917 918 g2.setPaint(paint); 919 g2.setStroke(stroke); 920 g2.draw(line); 921 922 } 923 924 /** 925 * Draws a line perpendicular to the range axis. 926 * 927 * @param g2 the graphics device. 928 * @param plot the plot. 929 * @param axis the value axis. 930 * @param dataArea the area for plotting data (not yet adjusted for any 3D 931 * effect). 932 * @param value the value at which the grid line should be drawn. 933 * @param paint the paint. 934 * @param stroke the stroke. 935 */ 936 public void drawRangeLine(Graphics2D g2, 937 XYPlot plot, 938 ValueAxis axis, 939 Rectangle2D dataArea, 940 double value, 941 Paint paint, 942 Stroke stroke) { 943 944 Range range = axis.getRange(); 945 if (!range.contains(value)) { 946 return; 947 } 948 949 PlotOrientation orientation = plot.getOrientation(); 950 Line2D line = null; 951 double v = axis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge()); 952 if (orientation == PlotOrientation.HORIZONTAL) { 953 line = new Line2D.Double(v, dataArea.getMinY(), v, 954 dataArea.getMaxY()); 955 } 956 else if (orientation == PlotOrientation.VERTICAL) { 957 line = new Line2D.Double(dataArea.getMinX(), v, 958 dataArea.getMaxX(), v); 959 } 960 961 g2.setPaint(paint); 962 g2.setStroke(stroke); 963 g2.draw(line); 964 965 } 966 967 /** 968 * Draws a vertical line on the chart to represent a 'range marker'. 969 * 970 * @param g2 the graphics device. 971 * @param plot the plot. 972 * @param domainAxis the domain axis. 973 * @param marker the marker line. 974 * @param dataArea the axis data area. 975 */ 976 public void drawDomainMarker(Graphics2D g2, 977 XYPlot plot, 978 ValueAxis domainAxis, 979 Marker marker, 980 Rectangle2D dataArea) { 981 982 if (marker instanceof ValueMarker) { 983 ValueMarker vm = (ValueMarker) marker; 984 double value = vm.getValue(); 985 Range range = domainAxis.getRange(); 986 if (!range.contains(value)) { 987 return; 988 } 989 990 double v = domainAxis.valueToJava2D(value, dataArea, 991 plot.getDomainAxisEdge()); 992 993 PlotOrientation orientation = plot.getOrientation(); 994 Line2D line = null; 995 if (orientation == PlotOrientation.HORIZONTAL) { 996 line = new Line2D.Double(dataArea.getMinX(), v, 997 dataArea.getMaxX(), v); 998 } 999 else if (orientation == PlotOrientation.VERTICAL) { 1000 line = new Line2D.Double(v, dataArea.getMinY(), v, 1001 dataArea.getMaxY()); 1002 } 1003 1004 final Composite originalComposite = g2.getComposite(); 1005 g2.setComposite(AlphaComposite.getInstance( 1006 AlphaComposite.SRC_OVER, marker.getAlpha())); 1007 g2.setPaint(marker.getPaint()); 1008 g2.setStroke(marker.getStroke()); 1009 g2.draw(line); 1010 1011 String label = marker.getLabel(); 1012 RectangleAnchor anchor = marker.getLabelAnchor(); 1013 if (label != null) { 1014 Font labelFont = marker.getLabelFont(); 1015 g2.setFont(labelFont); 1016 g2.setPaint(marker.getLabelPaint()); 1017 Point2D coordinates = calculateDomainMarkerTextAnchorPoint( 1018 g2, orientation, dataArea, line.getBounds2D(), 1019 marker.getLabelOffset(), 1020 LengthAdjustmentType.EXPAND, anchor); 1021 TextUtilities.drawAlignedString(label, g2, 1022 (float) coordinates.getX(), (float) coordinates.getY(), 1023 marker.getLabelTextAnchor()); 1024 } 1025 g2.setComposite(originalComposite); 1026 } 1027 else if (marker instanceof IntervalMarker) { 1028 IntervalMarker im = (IntervalMarker) marker; 1029 double start = im.getStartValue(); 1030 double end = im.getEndValue(); 1031 Range range = domainAxis.getRange(); 1032 if (!(range.intersects(start, end))) { 1033 return; 1034 } 1035 1036 double start2d = domainAxis.valueToJava2D(start, dataArea, 1037 plot.getDomainAxisEdge()); 1038 double end2d = domainAxis.valueToJava2D(end, dataArea, 1039 plot.getDomainAxisEdge()); 1040 double low = Math.min(start2d, end2d); 1041 double high = Math.max(start2d, end2d); 1042 1043 PlotOrientation orientation = plot.getOrientation(); 1044 Rectangle2D rect = null; 1045 if (orientation == PlotOrientation.HORIZONTAL) { 1046 // clip top and bottom bounds to data area 1047 low = Math.max(low, dataArea.getMinY()); 1048 high = Math.min(high, dataArea.getMaxY()); 1049 rect = new Rectangle2D.Double(dataArea.getMinX(), 1050 low, dataArea.getWidth(), 1051 high - low); 1052 } 1053 else if (orientation == PlotOrientation.VERTICAL) { 1054 // clip left and right bounds to data area 1055 low = Math.max(low, dataArea.getMinX()); 1056 high = Math.min(high, dataArea.getMaxX()); 1057 rect = new Rectangle2D.Double(low, 1058 dataArea.getMinY(), high - low, 1059 dataArea.getHeight()); 1060 } 1061 1062 final Composite originalComposite = g2.getComposite(); 1063 g2.setComposite(AlphaComposite.getInstance( 1064 AlphaComposite.SRC_OVER, marker.getAlpha())); 1065 Paint p = marker.getPaint(); 1066 if (p instanceof GradientPaint) { 1067 GradientPaint gp = (GradientPaint) p; 1068 GradientPaintTransformer t = im.getGradientPaintTransformer(); 1069 if (t != null) { 1070 gp = t.transform(gp, rect); 1071 } 1072 g2.setPaint(gp); 1073 } 1074 else { 1075 g2.setPaint(p); 1076 } 1077 g2.fill(rect); 1078 1079 // now draw the outlines, if visible... 1080 if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) { 1081 if (orientation == PlotOrientation.VERTICAL) { 1082 Line2D line = new Line2D.Double(); 1083 double y0 = dataArea.getMinY(); 1084 double y1 = dataArea.getMaxY(); 1085 g2.setPaint(im.getOutlinePaint()); 1086 g2.setStroke(im.getOutlineStroke()); 1087 if (range.contains(start)) { 1088 line.setLine(start2d, y0, start2d, y1); 1089 g2.draw(line); 1090 } 1091 if (range.contains(end)) { 1092 line.setLine(end2d, y0, end2d, y1); 1093 g2.draw(line); 1094 } 1095 } 1096 else { // PlotOrientation.HORIZONTAL 1097 Line2D line = new Line2D.Double(); 1098 double x0 = dataArea.getMinX(); 1099 double x1 = dataArea.getMaxX(); 1100 g2.setPaint(im.getOutlinePaint()); 1101 g2.setStroke(im.getOutlineStroke()); 1102 if (range.contains(start)) { 1103 line.setLine(x0, start2d, x1, start2d); 1104 g2.draw(line); 1105 } 1106 if (range.contains(end)) { 1107 line.setLine(x0, end2d, x1, end2d); 1108 g2.draw(line); 1109 } 1110 } 1111 } 1112 1113 String label = marker.getLabel(); 1114 RectangleAnchor anchor = marker.getLabelAnchor(); 1115 if (label != null) { 1116 Font labelFont = marker.getLabelFont(); 1117 g2.setFont(labelFont); 1118 g2.setPaint(marker.getLabelPaint()); 1119 Point2D coordinates = calculateDomainMarkerTextAnchorPoint( 1120 g2, orientation, dataArea, rect, 1121 marker.getLabelOffset(), marker.getLabelOffsetType(), 1122 anchor); 1123 TextUtilities.drawAlignedString(label, g2, 1124 (float) coordinates.getX(), (float) coordinates.getY(), 1125 marker.getLabelTextAnchor()); 1126 } 1127 g2.setComposite(originalComposite); 1128 1129 } 1130 1131 } 1132 1133 /** 1134 * Calculates the (x, y) coordinates for drawing a marker label. 1135 * 1136 * @param g2 the graphics device. 1137 * @param orientation the plot orientation. 1138 * @param dataArea the data area. 1139 * @param markerArea the rectangle surrounding the marker area. 1140 * @param markerOffset the marker label offset. 1141 * @param labelOffsetType the label offset type. 1142 * @param anchor the label anchor. 1143 * 1144 * @return The coordinates for drawing the marker label. 1145 */ 1146 protected Point2D calculateDomainMarkerTextAnchorPoint(Graphics2D g2, 1147 PlotOrientation orientation, 1148 Rectangle2D dataArea, 1149 Rectangle2D markerArea, 1150 RectangleInsets markerOffset, 1151 LengthAdjustmentType labelOffsetType, 1152 RectangleAnchor anchor) { 1153 1154 Rectangle2D anchorRect = null; 1155 if (orientation == PlotOrientation.HORIZONTAL) { 1156 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1157 LengthAdjustmentType.CONTRACT, labelOffsetType); 1158 } 1159 else if (orientation == PlotOrientation.VERTICAL) { 1160 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1161 labelOffsetType, LengthAdjustmentType.CONTRACT); 1162 } 1163 return RectangleAnchor.coordinates(anchorRect, anchor); 1164 1165 } 1166 1167 /** 1168 * Draws a horizontal line across the chart to represent a 'range marker'. 1169 * 1170 * @param g2 the graphics device. 1171 * @param plot the plot. 1172 * @param rangeAxis the range axis. 1173 * @param marker the marker line. 1174 * @param dataArea the axis data area. 1175 */ 1176 public void drawRangeMarker(Graphics2D g2, 1177 XYPlot plot, 1178 ValueAxis rangeAxis, 1179 Marker marker, 1180 Rectangle2D dataArea) { 1181 1182 if (marker instanceof ValueMarker) { 1183 ValueMarker vm = (ValueMarker) marker; 1184 double value = vm.getValue(); 1185 Range range = rangeAxis.getRange(); 1186 if (!range.contains(value)) { 1187 return; 1188 } 1189 1190 double v = rangeAxis.valueToJava2D(value, dataArea, 1191 plot.getRangeAxisEdge()); 1192 PlotOrientation orientation = plot.getOrientation(); 1193 Line2D line = null; 1194 if (orientation == PlotOrientation.HORIZONTAL) { 1195 line = new Line2D.Double(v, dataArea.getMinY(), v, 1196 dataArea.getMaxY()); 1197 } 1198 else if (orientation == PlotOrientation.VERTICAL) { 1199 line = new Line2D.Double(dataArea.getMinX(), v, 1200 dataArea.getMaxX(), v); 1201 } 1202 1203 final Composite originalComposite = g2.getComposite(); 1204 g2.setComposite(AlphaComposite.getInstance( 1205 AlphaComposite.SRC_OVER, marker.getAlpha())); 1206 g2.setPaint(marker.getPaint()); 1207 g2.setStroke(marker.getStroke()); 1208 g2.draw(line); 1209 1210 String label = marker.getLabel(); 1211 RectangleAnchor anchor = marker.getLabelAnchor(); 1212 if (label != null) { 1213 Font labelFont = marker.getLabelFont(); 1214 g2.setFont(labelFont); 1215 g2.setPaint(marker.getLabelPaint()); 1216 Point2D coordinates = calculateRangeMarkerTextAnchorPoint( 1217 g2, orientation, dataArea, line.getBounds2D(), 1218 marker.getLabelOffset(), 1219 LengthAdjustmentType.EXPAND, anchor); 1220 TextUtilities.drawAlignedString(label, g2, 1221 (float) coordinates.getX(), (float) coordinates.getY(), 1222 marker.getLabelTextAnchor()); 1223 } 1224 g2.setComposite(originalComposite); 1225 } 1226 else if (marker instanceof IntervalMarker) { 1227 IntervalMarker im = (IntervalMarker) marker; 1228 double start = im.getStartValue(); 1229 double end = im.getEndValue(); 1230 Range range = rangeAxis.getRange(); 1231 if (!(range.intersects(start, end))) { 1232 return; 1233 } 1234 1235 double start2d = rangeAxis.valueToJava2D(start, dataArea, 1236 plot.getRangeAxisEdge()); 1237 double end2d = rangeAxis.valueToJava2D(end, dataArea, 1238 plot.getRangeAxisEdge()); 1239 double low = Math.min(start2d, end2d); 1240 double high = Math.max(start2d, end2d); 1241 1242 PlotOrientation orientation = plot.getOrientation(); 1243 Rectangle2D rect = null; 1244 if (orientation == PlotOrientation.HORIZONTAL) { 1245 // clip left and right bounds to data area 1246 low = Math.max(low, dataArea.getMinX()); 1247 high = Math.min(high, dataArea.getMaxX()); 1248 rect = new Rectangle2D.Double(low, 1249 dataArea.getMinY(), high - low, 1250 dataArea.getHeight()); 1251 } 1252 else if (orientation == PlotOrientation.VERTICAL) { 1253 // clip top and bottom bounds to data area 1254 low = Math.max(low, dataArea.getMinY()); 1255 high = Math.min(high, dataArea.getMaxY()); 1256 rect = new Rectangle2D.Double(dataArea.getMinX(), 1257 low, dataArea.getWidth(), 1258 high - low); 1259 } 1260 1261 final Composite originalComposite = g2.getComposite(); 1262 g2.setComposite(AlphaComposite.getInstance( 1263 AlphaComposite.SRC_OVER, marker.getAlpha())); 1264 Paint p = marker.getPaint(); 1265 if (p instanceof GradientPaint) { 1266 GradientPaint gp = (GradientPaint) p; 1267 GradientPaintTransformer t = im.getGradientPaintTransformer(); 1268 if (t != null) { 1269 gp = t.transform(gp, rect); 1270 } 1271 g2.setPaint(gp); 1272 } 1273 else { 1274 g2.setPaint(p); 1275 } 1276 g2.fill(rect); 1277 1278 // now draw the outlines, if visible... 1279 if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) { 1280 if (orientation == PlotOrientation.VERTICAL) { 1281 Line2D line = new Line2D.Double(); 1282 double x0 = dataArea.getMinX(); 1283 double x1 = dataArea.getMaxX(); 1284 g2.setPaint(im.getOutlinePaint()); 1285 g2.setStroke(im.getOutlineStroke()); 1286 if (range.contains(start)) { 1287 line.setLine(x0, start2d, x1, start2d); 1288 g2.draw(line); 1289 } 1290 if (range.contains(end)) { 1291 line.setLine(x0, end2d, x1, end2d); 1292 g2.draw(line); 1293 } 1294 } 1295 else { // PlotOrientation.HORIZONTAL 1296 Line2D line = new Line2D.Double(); 1297 double y0 = dataArea.getMinY(); 1298 double y1 = dataArea.getMaxY(); 1299 g2.setPaint(im.getOutlinePaint()); 1300 g2.setStroke(im.getOutlineStroke()); 1301 if (range.contains(start)) { 1302 line.setLine(start2d, y0, start2d, y1); 1303 g2.draw(line); 1304 } 1305 if (range.contains(end)) { 1306 line.setLine(end2d, y0, end2d, y1); 1307 g2.draw(line); 1308 } 1309 } 1310 } 1311 1312 String label = marker.getLabel(); 1313 RectangleAnchor anchor = marker.getLabelAnchor(); 1314 if (label != null) { 1315 Font labelFont = marker.getLabelFont(); 1316 g2.setFont(labelFont); 1317 g2.setPaint(marker.getLabelPaint()); 1318 Point2D coordinates = calculateRangeMarkerTextAnchorPoint( 1319 g2, orientation, dataArea, rect, 1320 marker.getLabelOffset(), marker.getLabelOffsetType(), 1321 anchor); 1322 TextUtilities.drawAlignedString(label, g2, 1323 (float) coordinates.getX(), (float) coordinates.getY(), 1324 marker.getLabelTextAnchor()); 1325 } 1326 g2.setComposite(originalComposite); 1327 } 1328 } 1329 1330 /** 1331 * Calculates the (x, y) coordinates for drawing a marker label. 1332 * 1333 * @param g2 the graphics device. 1334 * @param orientation the plot orientation. 1335 * @param dataArea the data area. 1336 * @param markerArea the marker area. 1337 * @param markerOffset the marker offset. 1338 * @param anchor the label anchor. 1339 * 1340 * @return The coordinates for drawing the marker label. 1341 */ 1342 private Point2D calculateRangeMarkerTextAnchorPoint(Graphics2D g2, 1343 PlotOrientation orientation, 1344 Rectangle2D dataArea, 1345 Rectangle2D markerArea, 1346 RectangleInsets markerOffset, 1347 LengthAdjustmentType labelOffsetForRange, 1348 RectangleAnchor anchor) { 1349 1350 Rectangle2D anchorRect = null; 1351 if (orientation == PlotOrientation.HORIZONTAL) { 1352 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1353 labelOffsetForRange, LengthAdjustmentType.CONTRACT); 1354 } 1355 else if (orientation == PlotOrientation.VERTICAL) { 1356 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1357 LengthAdjustmentType.CONTRACT, labelOffsetForRange); 1358 } 1359 return RectangleAnchor.coordinates(anchorRect, anchor); 1360 1361 } 1362 1363 /** 1364 * Returns a clone of the renderer. 1365 * 1366 * @return A clone. 1367 * 1368 * @throws CloneNotSupportedException if the renderer does not support 1369 * cloning. 1370 */ 1371 protected Object clone() throws CloneNotSupportedException { 1372 AbstractXYItemRenderer clone = (AbstractXYItemRenderer) super.clone(); 1373 // 'plot' : just retain reference, not a deep copy 1374 1375 if (this.itemLabelGenerator != null 1376 && this.itemLabelGenerator instanceof PublicCloneable) { 1377 PublicCloneable pc = (PublicCloneable) this.itemLabelGenerator; 1378 clone.itemLabelGenerator = (XYItemLabelGenerator) pc.clone(); 1379 } 1380 clone.itemLabelGeneratorList 1381 = (ObjectList) this.itemLabelGeneratorList.clone(); 1382 if (this.baseItemLabelGenerator != null 1383 && this.baseItemLabelGenerator instanceof PublicCloneable) { 1384 PublicCloneable pc = (PublicCloneable) this.baseItemLabelGenerator; 1385 clone.baseItemLabelGenerator = (XYItemLabelGenerator) pc.clone(); 1386 } 1387 1388 if (this.toolTipGenerator != null 1389 && this.toolTipGenerator instanceof PublicCloneable) { 1390 PublicCloneable pc = (PublicCloneable) this.toolTipGenerator; 1391 clone.toolTipGenerator = (XYToolTipGenerator) pc.clone(); 1392 } 1393 clone.toolTipGeneratorList 1394 = (ObjectList) this.toolTipGeneratorList.clone(); 1395 if (this.baseToolTipGenerator != null 1396 && this.baseToolTipGenerator instanceof PublicCloneable) { 1397 PublicCloneable pc = (PublicCloneable) this.baseToolTipGenerator; 1398 clone.baseToolTipGenerator = (XYToolTipGenerator) pc.clone(); 1399 } 1400 1401 if (clone.legendItemLabelGenerator instanceof PublicCloneable) { 1402 clone.legendItemLabelGenerator = (XYSeriesLabelGenerator) 1403 ObjectUtilities.clone(this.legendItemLabelGenerator); 1404 } 1405 if (clone.legendItemToolTipGenerator instanceof PublicCloneable) { 1406 clone.legendItemToolTipGenerator = (XYSeriesLabelGenerator) 1407 ObjectUtilities.clone(this.legendItemToolTipGenerator); 1408 } 1409 if (clone.legendItemURLGenerator instanceof PublicCloneable) { 1410 clone.legendItemURLGenerator = (XYSeriesLabelGenerator) 1411 ObjectUtilities.clone(this.legendItemURLGenerator); 1412 } 1413 1414 clone.foregroundAnnotations = (List) ObjectUtilities.deepClone( 1415 this.foregroundAnnotations); 1416 clone.backgroundAnnotations = (List) ObjectUtilities.deepClone( 1417 this.backgroundAnnotations); 1418 1419 if (clone.legendItemLabelGenerator instanceof PublicCloneable) { 1420 clone.legendItemLabelGenerator = (XYSeriesLabelGenerator) 1421 ObjectUtilities.clone(this.legendItemLabelGenerator); 1422 } 1423 if (clone.legendItemToolTipGenerator instanceof PublicCloneable) { 1424 clone.legendItemToolTipGenerator = (XYSeriesLabelGenerator) 1425 ObjectUtilities.clone(this.legendItemToolTipGenerator); 1426 } 1427 if (clone.legendItemURLGenerator instanceof PublicCloneable) { 1428 clone.legendItemURLGenerator = (XYSeriesLabelGenerator) 1429 ObjectUtilities.clone(this.legendItemURLGenerator); 1430 } 1431 1432 return clone; 1433 } 1434 1435 /** 1436 * Tests this renderer for equality with another object. 1437 * 1438 * @param obj the object (<code>null</code> permitted). 1439 * 1440 * @return <code>true</code> or <code>false</code>. 1441 */ 1442 public boolean equals(Object obj) { 1443 if (obj == this) { 1444 return true; 1445 } 1446 if (!(obj instanceof AbstractXYItemRenderer)) { 1447 return false; 1448 } 1449 AbstractXYItemRenderer that = (AbstractXYItemRenderer) obj; 1450 if (!ObjectUtilities.equal(this.itemLabelGenerator, 1451 that.itemLabelGenerator)) { 1452 return false; 1453 } 1454 if (!this.itemLabelGeneratorList.equals(that.itemLabelGeneratorList)) { 1455 return false; 1456 } 1457 if (!ObjectUtilities.equal(this.baseItemLabelGenerator, 1458 that.baseItemLabelGenerator)) { 1459 return false; 1460 } 1461 if (!ObjectUtilities.equal(this.toolTipGenerator, 1462 that.toolTipGenerator)) { 1463 return false; 1464 } 1465 if (!this.toolTipGeneratorList.equals(that.toolTipGeneratorList)) { 1466 return false; 1467 } 1468 if (!ObjectUtilities.equal(this.baseToolTipGenerator, 1469 that.baseToolTipGenerator)) { 1470 return false; 1471 } 1472 if (!ObjectUtilities.equal(this.urlGenerator, that.urlGenerator)) { 1473 return false; 1474 } 1475 if (!this.foregroundAnnotations.equals(that.foregroundAnnotations)) { 1476 return false; 1477 } 1478 if (!this.backgroundAnnotations.equals(that.backgroundAnnotations)) { 1479 return false; 1480 } 1481 if (this.defaultEntityRadius != that.defaultEntityRadius) { 1482 return false; 1483 } 1484 if (!ObjectUtilities.equal(this.legendItemLabelGenerator, 1485 that.legendItemLabelGenerator)) { 1486 return false; 1487 } 1488 if (!ObjectUtilities.equal(this.legendItemToolTipGenerator, 1489 that.legendItemToolTipGenerator)) { 1490 return false; 1491 } 1492 if (!ObjectUtilities.equal(this.legendItemURLGenerator, 1493 that.legendItemURLGenerator)) { 1494 return false; 1495 } 1496 return super.equals(obj); 1497 } 1498 1499 /** 1500 * Returns the drawing supplier from the plot. 1501 * 1502 * @return The drawing supplier (possibly <code>null</code>). 1503 */ 1504 public DrawingSupplier getDrawingSupplier() { 1505 DrawingSupplier result = null; 1506 XYPlot p = getPlot(); 1507 if (p != null) { 1508 result = p.getDrawingSupplier(); 1509 } 1510 return result; 1511 } 1512 1513 /** 1514 * Considers the current (x, y) coordinate and updates the crosshair point 1515 * if it meets the criteria (usually means the (x, y) coordinate is the 1516 * closest to the anchor point so far). 1517 * 1518 * @param crosshairState the crosshair state (<code>null</code> permitted, 1519 * but the method does nothing in that case). 1520 * @param x the x-value (in data space). 1521 * @param y the y-value (in data space). 1522 * @param transX the x-value translated to Java2D space. 1523 * @param transY the y-value translated to Java2D space. 1524 * @param orientation the plot orientation (<code>null</code> not 1525 * permitted). 1526 * 1527 * @deprecated Use {@link #updateCrosshairValues(CrosshairState, double, 1528 * double, int, int, double, double, PlotOrientation)} -- see bug 1529 * report 1086307. 1530 */ 1531 protected void updateCrosshairValues(CrosshairState crosshairState, 1532 double x, double y, double transX, double transY, 1533 PlotOrientation orientation) { 1534 updateCrosshairValues(crosshairState, x, y, 0, 0, transX, transY, 1535 orientation); 1536 } 1537 1538 /** 1539 * Considers the current (x, y) coordinate and updates the crosshair point 1540 * if it meets the criteria (usually means the (x, y) coordinate is the 1541 * closest to the anchor point so far). 1542 * 1543 * @param crosshairState the crosshair state (<code>null</code> permitted, 1544 * but the method does nothing in that case). 1545 * @param x the x-value (in data space). 1546 * @param y the y-value (in data space). 1547 * @param domainAxisIndex the index of the domain axis for the point. 1548 * @param rangeAxisIndex the index of the range axis for the point. 1549 * @param transX the x-value translated to Java2D space. 1550 * @param transY the y-value translated to Java2D space. 1551 * @param orientation the plot orientation (<code>null</code> not 1552 * permitted). 1553 * 1554 * @since 1.0.4 1555 */ 1556 protected void updateCrosshairValues(CrosshairState crosshairState, 1557 double x, double y, int domainAxisIndex, int rangeAxisIndex, 1558 double transX, double transY, PlotOrientation orientation) { 1559 1560 if (orientation == null) { 1561 throw new IllegalArgumentException("Null 'orientation' argument."); 1562 } 1563 1564 if (crosshairState != null) { 1565 // do we need to update the crosshair values? 1566 if (this.plot.isDomainCrosshairLockedOnData()) { 1567 if (this.plot.isRangeCrosshairLockedOnData()) { 1568 // both axes 1569 crosshairState.updateCrosshairPoint(x, y, domainAxisIndex, 1570 rangeAxisIndex, transX, transY, orientation); 1571 } 1572 else { 1573 // just the domain axis... 1574 crosshairState.updateCrosshairX(x, domainAxisIndex); 1575 } 1576 } 1577 else { 1578 if (this.plot.isRangeCrosshairLockedOnData()) { 1579 // just the range axis... 1580 crosshairState.updateCrosshairY(y, rangeAxisIndex); 1581 } 1582 } 1583 } 1584 1585 } 1586 1587 /** 1588 * Draws an item label. 1589 * 1590 * @param g2 the graphics device. 1591 * @param orientation the orientation. 1592 * @param dataset the dataset. 1593 * @param series the series index (zero-based). 1594 * @param item the item index (zero-based). 1595 * @param x the x coordinate (in Java2D space). 1596 * @param y the y coordinate (in Java2D space). 1597 * @param negative indicates a negative value (which affects the item 1598 * label position). 1599 */ 1600 protected void drawItemLabel(Graphics2D g2, PlotOrientation orientation, 1601 XYDataset dataset, int series, int item, double x, double y, 1602 boolean negative) { 1603 1604 XYItemLabelGenerator generator = getItemLabelGenerator(series, item); 1605 if (generator != null) { 1606 Font labelFont = getItemLabelFont(series, item); 1607 Paint paint = getItemLabelPaint(series, item); 1608 g2.setFont(labelFont); 1609 g2.setPaint(paint); 1610 String label = generator.generateLabel(dataset, series, item); 1611 1612 // get the label position.. 1613 ItemLabelPosition position = null; 1614 if (!negative) { 1615 position = getPositiveItemLabelPosition(series, item); 1616 } 1617 else { 1618 position = getNegativeItemLabelPosition(series, item); 1619 } 1620 1621 // work out the label anchor point... 1622 Point2D anchorPoint = calculateLabelAnchorPoint( 1623 position.getItemLabelAnchor(), x, y, orientation); 1624 TextUtilities.drawRotatedString(label, g2, 1625 (float) anchorPoint.getX(), (float) anchorPoint.getY(), 1626 position.getTextAnchor(), position.getAngle(), 1627 position.getRotationAnchor()); 1628 } 1629 1630 } 1631 1632 /** 1633 * Draws all the annotations for the specified layer. 1634 * 1635 * @param g2 the graphics device. 1636 * @param dataArea the data area. 1637 * @param domainAxis the domain axis. 1638 * @param rangeAxis the range axis. 1639 * @param layer the layer. 1640 * @param info the plot rendering info. 1641 */ 1642 public void drawAnnotations(Graphics2D g2, 1643 Rectangle2D dataArea, 1644 ValueAxis domainAxis, 1645 ValueAxis rangeAxis, 1646 Layer layer, 1647 PlotRenderingInfo info) { 1648 1649 Iterator iterator = null; 1650 if (layer.equals(Layer.FOREGROUND)) { 1651 iterator = this.foregroundAnnotations.iterator(); 1652 } 1653 else if (layer.equals(Layer.BACKGROUND)) { 1654 iterator = this.backgroundAnnotations.iterator(); 1655 } 1656 else { 1657 // should not get here 1658 throw new RuntimeException("Unknown layer."); 1659 } 1660 while (iterator.hasNext()) { 1661 XYAnnotation annotation = (XYAnnotation) iterator.next(); 1662 annotation.draw(g2, this.plot, dataArea, domainAxis, rangeAxis, 1663 0, info); 1664 } 1665 1666 } 1667 1668 /** 1669 * Adds an entity to the collection. 1670 * 1671 * @param entities the entity collection being populated. 1672 * @param area the entity area (if <code>null</code> a default will be 1673 * used). 1674 * @param dataset the dataset. 1675 * @param series the series. 1676 * @param item the item. 1677 * @param entityX the entity's center x-coordinate in user space. 1678 * @param entityY the entity's center y-coordinate in user space. 1679 */ 1680 protected void addEntity(EntityCollection entities, Shape area, 1681 XYDataset dataset, int series, int item, 1682 double entityX, double entityY) { 1683 if (!getItemCreateEntity(series, item)) { 1684 return; 1685 } 1686 if (area == null) { 1687 area = new Ellipse2D.Double(entityX - this.defaultEntityRadius, 1688 entityY - this.defaultEntityRadius, 1689 this.defaultEntityRadius * 2, this.defaultEntityRadius * 2); 1690 } 1691 String tip = null; 1692 XYToolTipGenerator generator = getToolTipGenerator(series, item); 1693 if (generator != null) { 1694 tip = generator.generateToolTip(dataset, series, item); 1695 } 1696 String url = null; 1697 if (getURLGenerator() != null) { 1698 url = getURLGenerator().generateURL(dataset, series, item); 1699 } 1700 XYItemEntity entity = new XYItemEntity(area, dataset, series, item, 1701 tip, url); 1702 entities.add(entity); 1703 } 1704 1705 }