Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jfreechart/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * --------- 28: * Plot.java 29: * --------- 30: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): Sylvain Vieujot; 34: * Jeremy Bowman; 35: * Andreas Schneider; 36: * Gideon Krause; 37: * Nicolas Brodu; 38: * Michal Krause; 39: * 40: * Changes 41: * ------- 42: * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG); 43: * 18-Sep-2001 : Updated header info and fixed DOS encoding problem (DG); 44: * 19-Oct-2001 : Moved series paint and stroke methods from JFreeChart 45: * class (DG); 46: * 23-Oct-2001 : Created renderer for LinePlot class (DG); 47: * 07-Nov-2001 : Changed type names for ChartChangeEvent (DG); 48: * Tidied up some Javadoc comments (DG); 49: * 13-Nov-2001 : Changes to allow for null axes on plots such as PiePlot (DG); 50: * Added plot/axis compatibility checks (DG); 51: * 12-Dec-2001 : Changed constructors to protected, and removed unnecessary 52: * 'throws' clauses (DG); 53: * 13-Dec-2001 : Added tooltips (DG); 54: * 22-Jan-2002 : Added handleClick() method, as part of implementation for 55: * crosshairs (DG); 56: * Moved tooltips reference into ChartInfo class (DG); 57: * 23-Jan-2002 : Added test for null axes in chartChanged() method, thanks 58: * to Barry Evans for the bug report (number 506979 on 59: * SourceForge) (DG); 60: * Added a zoom() method (DG); 61: * 05-Feb-2002 : Updated setBackgroundPaint(), setOutlineStroke() and 62: * setOutlinePaint() to better handle null values, as suggested 63: * by Sylvain Vieujot (DG); 64: * 06-Feb-2002 : Added background image, plus alpha transparency for background 65: * and foreground (DG); 66: * 06-Mar-2002 : Added AxisConstants interface (DG); 67: * 26-Mar-2002 : Changed zoom method from empty to abstract (DG); 68: * 23-Apr-2002 : Moved dataset from JFreeChart class (DG); 69: * 11-May-2002 : Added ShapeFactory interface for getShape() methods, 70: * contributed by Jeremy Bowman (DG); 71: * 28-May-2002 : Fixed bug in setSeriesPaint(int, Paint) for subplots (AS); 72: * 25-Jun-2002 : Removed redundant imports (DG); 73: * 30-Jul-2002 : Added 'no data' message for charts with null or empty 74: * datasets (DG); 75: * 21-Aug-2002 : Added code to extend series array if necessary (refer to 76: * SourceForge bug id 594547 for details) (DG); 77: * 17-Sep-2002 : Fixed bug in getSeriesOutlineStroke() method, reported by 78: * Andreas Schroeder (DG); 79: * 23-Sep-2002 : Added getLegendItems() abstract method (DG); 80: * 24-Sep-2002 : Removed firstSeriesIndex, subplots now use their own paint 81: * settings, there is a new mechanism for the legend to collect 82: * the legend items (DG); 83: * 27-Sep-2002 : Added dataset group (DG); 84: * 14-Oct-2002 : Moved listener storage into EventListenerList. Changed some 85: * abstract methods to empty implementations (DG); 86: * 28-Oct-2002 : Added a getBackgroundImage() method (DG); 87: * 21-Nov-2002 : Added a plot index for identifying subplots in combined and 88: * overlaid charts (DG); 89: * 22-Nov-2002 : Changed all attributes from 'protected' to 'private'. Added 90: * dataAreaRatio attribute from David M O'Donnell's code (DG); 91: * 09-Jan-2003 : Integrated fix for plot border contributed by Gideon 92: * Krause (DG); 93: * 17-Jan-2003 : Moved to com.jrefinery.chart.plot (DG); 94: * 23-Jan-2003 : Removed one constructor (DG); 95: * 26-Mar-2003 : Implemented Serializable (DG); 96: * 14-Jul-2003 : Moved the dataset and secondaryDataset attributes to the 97: * CategoryPlot and XYPlot classes (DG); 98: * 21-Jul-2003 : Moved DrawingSupplier from CategoryPlot and XYPlot up to this 99: * class (DG); 100: * 20-Aug-2003 : Implemented Cloneable (DG); 101: * 11-Sep-2003 : Listeners and clone (NB); 102: * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG); 103: * 03-Dec-2003 : Modified draw method to accept anchor (DG); 104: * 12-Mar-2004 : Fixed clipping bug in drawNoDataMessage() method (DG); 105: * 07-Apr-2004 : Modified string bounds calculation (DG); 106: * 04-Nov-2004 : Added default shapes for legend items (DG); 107: * 25-Nov-2004 : Some changes to the clone() method implementation (DG); 108: * 23-Feb-2005 : Implemented new LegendItemSource interface (and also 109: * PublicCloneable) (DG); 110: * 21-Apr-2005 : Replaced Insets with RectangleInsets (DG); 111: * 05-May-2005 : Removed unused draw() method (DG); 112: * 06-Jun-2005 : Fixed bugs in equals() method (DG); 113: * 01-Sep-2005 : Moved dataAreaRatio from here to ContourPlot (DG); 114: * ------------- JFREECHART 1.0.x --------------------------------------------- 115: * 30-Jun-2006 : Added background image alpha - see bug report 1514904 (DG); 116: * 05-Sep-2006 : Implemented the MarkerChangeListener interface (DG); 117: * 11-Jan-2007 : Added some argument checks, event notifications, and many 118: * API doc updates (DG); 119: * 03-Apr-2007 : Made drawBackgroundImage() public (DG); 120: * 07-Jun-2007 : Added new fillBackground() method to handle GradientPaint 121: * taking into account orientation (DG); 122: * 123: */ 124: 125: package org.jfree.chart.plot; 126: 127: import java.awt.AlphaComposite; 128: import java.awt.BasicStroke; 129: import java.awt.Color; 130: import java.awt.Composite; 131: import java.awt.Font; 132: import java.awt.GradientPaint; 133: import java.awt.Graphics2D; 134: import java.awt.Image; 135: import java.awt.Paint; 136: import java.awt.Shape; 137: import java.awt.Stroke; 138: import java.awt.geom.Ellipse2D; 139: import java.awt.geom.Point2D; 140: import java.awt.geom.Rectangle2D; 141: import java.io.IOException; 142: import java.io.ObjectInputStream; 143: import java.io.ObjectOutputStream; 144: import java.io.Serializable; 145: 146: import javax.swing.event.EventListenerList; 147: 148: import org.jfree.chart.LegendItemCollection; 149: import org.jfree.chart.LegendItemSource; 150: import org.jfree.chart.axis.AxisLocation; 151: import org.jfree.chart.event.AxisChangeEvent; 152: import org.jfree.chart.event.AxisChangeListener; 153: import org.jfree.chart.event.ChartChangeEventType; 154: import org.jfree.chart.event.MarkerChangeEvent; 155: import org.jfree.chart.event.MarkerChangeListener; 156: import org.jfree.chart.event.PlotChangeEvent; 157: import org.jfree.chart.event.PlotChangeListener; 158: import org.jfree.data.general.DatasetChangeEvent; 159: import org.jfree.data.general.DatasetChangeListener; 160: import org.jfree.data.general.DatasetGroup; 161: import org.jfree.io.SerialUtilities; 162: import org.jfree.text.G2TextMeasurer; 163: import org.jfree.text.TextBlock; 164: import org.jfree.text.TextBlockAnchor; 165: import org.jfree.text.TextUtilities; 166: import org.jfree.ui.Align; 167: import org.jfree.ui.RectangleEdge; 168: import org.jfree.ui.RectangleInsets; 169: import org.jfree.util.ObjectUtilities; 170: import org.jfree.util.PaintUtilities; 171: import org.jfree.util.PublicCloneable; 172: 173: /** 174: * The base class for all plots in JFreeChart. The 175: * {@link org.jfree.chart.JFreeChart} class delegates the drawing of axes and 176: * data to the plot. This base class provides facilities common to most plot 177: * types. 178: */ 179: public abstract class Plot implements AxisChangeListener, 180: DatasetChangeListener, 181: MarkerChangeListener, 182: LegendItemSource, 183: PublicCloneable, 184: Cloneable, 185: Serializable { 186: 187: /** For serialization. */ 188: private static final long serialVersionUID = -8831571430103671324L; 189: 190: /** Useful constant representing zero. */ 191: public static final Number ZERO = new Integer(0); 192: 193: /** The default insets. */ 194: public static final RectangleInsets DEFAULT_INSETS 195: = new RectangleInsets(4.0, 8.0, 4.0, 8.0); 196: 197: /** The default outline stroke. */ 198: public static final Stroke DEFAULT_OUTLINE_STROKE = new BasicStroke(0.5f); 199: 200: /** The default outline color. */ 201: public static final Paint DEFAULT_OUTLINE_PAINT = Color.gray; 202: 203: /** The default foreground alpha transparency. */ 204: public static final float DEFAULT_FOREGROUND_ALPHA = 1.0f; 205: 206: /** The default background alpha transparency. */ 207: public static final float DEFAULT_BACKGROUND_ALPHA = 1.0f; 208: 209: /** The default background color. */ 210: public static final Paint DEFAULT_BACKGROUND_PAINT = Color.white; 211: 212: /** The minimum width at which the plot should be drawn. */ 213: public static final int MINIMUM_WIDTH_TO_DRAW = 10; 214: 215: /** The minimum height at which the plot should be drawn. */ 216: public static final int MINIMUM_HEIGHT_TO_DRAW = 10; 217: 218: /** A default box shape for legend items. */ 219: public static final Shape DEFAULT_LEGEND_ITEM_BOX 220: = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0); 221: 222: /** A default circle shape for legend items. */ 223: public static final Shape DEFAULT_LEGEND_ITEM_CIRCLE 224: = new Ellipse2D.Double(-4.0, -4.0, 8.0, 8.0); 225: 226: /** The parent plot (<code>null</code> if this is the root plot). */ 227: private Plot parent; 228: 229: /** The dataset group (to be used for thread synchronisation). */ 230: private DatasetGroup datasetGroup; 231: 232: /** The message to display if no data is available. */ 233: private String noDataMessage; 234: 235: /** The font used to display the 'no data' message. */ 236: private Font noDataMessageFont; 237: 238: /** The paint used to draw the 'no data' message. */ 239: private transient Paint noDataMessagePaint; 240: 241: /** Amount of blank space around the plot area. */ 242: private RectangleInsets insets; 243: 244: /** 245: * A flag that controls whether or not the plot outline is drawn. 246: * 247: * @since 1.0.6 248: */ 249: private boolean outlineVisible; 250: 251: /** The Stroke used to draw an outline around the plot. */ 252: private transient Stroke outlineStroke; 253: 254: /** The Paint used to draw an outline around the plot. */ 255: private transient Paint outlinePaint; 256: 257: /** An optional color used to fill the plot background. */ 258: private transient Paint backgroundPaint; 259: 260: /** An optional image for the plot background. */ 261: private transient Image backgroundImage; // not currently serialized 262: 263: /** The alignment for the background image. */ 264: private int backgroundImageAlignment = Align.FIT; 265: 266: /** The alpha value used to draw the background image. */ 267: private float backgroundImageAlpha = 0.5f; 268: 269: /** The alpha-transparency for the plot. */ 270: private float foregroundAlpha; 271: 272: /** The alpha transparency for the background paint. */ 273: private float backgroundAlpha; 274: 275: /** The drawing supplier. */ 276: private DrawingSupplier drawingSupplier; 277: 278: /** Storage for registered change listeners. */ 279: private transient EventListenerList listenerList; 280: 281: /** 282: * Creates a new plot. 283: */ 284: protected Plot() { 285: 286: this.parent = null; 287: this.insets = DEFAULT_INSETS; 288: this.backgroundPaint = DEFAULT_BACKGROUND_PAINT; 289: this.backgroundAlpha = DEFAULT_BACKGROUND_ALPHA; 290: this.backgroundImage = null; 291: this.outlineVisible = true; 292: this.outlineStroke = DEFAULT_OUTLINE_STROKE; 293: this.outlinePaint = DEFAULT_OUTLINE_PAINT; 294: this.foregroundAlpha = DEFAULT_FOREGROUND_ALPHA; 295: 296: this.noDataMessage = null; 297: this.noDataMessageFont = new Font("SansSerif", Font.PLAIN, 12); 298: this.noDataMessagePaint = Color.black; 299: 300: this.drawingSupplier = new DefaultDrawingSupplier(); 301: 302: this.listenerList = new EventListenerList(); 303: 304: } 305: 306: /** 307: * Returns the dataset group for the plot (not currently used). 308: * 309: * @return The dataset group. 310: * 311: * @see #setDatasetGroup(DatasetGroup) 312: */ 313: public DatasetGroup getDatasetGroup() { 314: return this.datasetGroup; 315: } 316: 317: /** 318: * Sets the dataset group (not currently used). 319: * 320: * @param group the dataset group (<code>null</code> permitted). 321: * 322: * @see #getDatasetGroup() 323: */ 324: protected void setDatasetGroup(DatasetGroup group) { 325: this.datasetGroup = group; 326: } 327: 328: /** 329: * Returns the string that is displayed when the dataset is empty or 330: * <code>null</code>. 331: * 332: * @return The 'no data' message (<code>null</code> possible). 333: * 334: * @see #setNoDataMessage(String) 335: * @see #getNoDataMessageFont() 336: * @see #getNoDataMessagePaint() 337: */ 338: public String getNoDataMessage() { 339: return this.noDataMessage; 340: } 341: 342: /** 343: * Sets the message that is displayed when the dataset is empty or 344: * <code>null</code>, and sends a {@link PlotChangeEvent} to all registered 345: * listeners. 346: * 347: * @param message the message (<code>null</code> permitted). 348: * 349: * @see #getNoDataMessage() 350: */ 351: public void setNoDataMessage(String message) { 352: this.noDataMessage = message; 353: notifyListeners(new PlotChangeEvent(this)); 354: } 355: 356: /** 357: * Returns the font used to display the 'no data' message. 358: * 359: * @return The font (never <code>null</code>). 360: * 361: * @see #setNoDataMessageFont(Font) 362: * @see #getNoDataMessage() 363: */ 364: public Font getNoDataMessageFont() { 365: return this.noDataMessageFont; 366: } 367: 368: /** 369: * Sets the font used to display the 'no data' message and sends a 370: * {@link PlotChangeEvent} to all registered listeners. 371: * 372: * @param font the font (<code>null</code> not permitted). 373: * 374: * @see #getNoDataMessageFont() 375: */ 376: public void setNoDataMessageFont(Font font) { 377: if (font == null) { 378: throw new IllegalArgumentException("Null 'font' argument."); 379: } 380: this.noDataMessageFont = font; 381: notifyListeners(new PlotChangeEvent(this)); 382: } 383: 384: /** 385: * Returns the paint used to display the 'no data' message. 386: * 387: * @return The paint (never <code>null</code>). 388: * 389: * @see #setNoDataMessagePaint(Paint) 390: * @see #getNoDataMessage() 391: */ 392: public Paint getNoDataMessagePaint() { 393: return this.noDataMessagePaint; 394: } 395: 396: /** 397: * Sets the paint used to display the 'no data' message and sends a 398: * {@link PlotChangeEvent} to all registered listeners. 399: * 400: * @param paint the paint (<code>null</code> not permitted). 401: * 402: * @see #getNoDataMessagePaint() 403: */ 404: public void setNoDataMessagePaint(Paint paint) { 405: if (paint == null) { 406: throw new IllegalArgumentException("Null 'paint' argument."); 407: } 408: this.noDataMessagePaint = paint; 409: notifyListeners(new PlotChangeEvent(this)); 410: } 411: 412: /** 413: * Returns a short string describing the plot type. 414: * <P> 415: * Note: this gets used in the chart property editing user interface, 416: * but there needs to be a better mechanism for identifying the plot type. 417: * 418: * @return A short string describing the plot type (never 419: * <code>null</code>). 420: */ 421: public abstract String getPlotType(); 422: 423: /** 424: * Returns the parent plot (or <code>null</code> if this plot is not part 425: * of a combined plot). 426: * 427: * @return The parent plot. 428: * 429: * @see #setParent(Plot) 430: * @see #getRootPlot() 431: */ 432: public Plot getParent() { 433: return this.parent; 434: } 435: 436: /** 437: * Sets the parent plot. This method is intended for internal use, you 438: * shouldn't need to call it directly. 439: * 440: * @param parent the parent plot (<code>null</code> permitted). 441: * 442: * @see #getParent() 443: */ 444: public void setParent(Plot parent) { 445: this.parent = parent; 446: } 447: 448: /** 449: * Returns the root plot. 450: * 451: * @return The root plot. 452: * 453: * @see #getParent() 454: */ 455: public Plot getRootPlot() { 456: 457: Plot p = getParent(); 458: if (p == null) { 459: return this; 460: } 461: else { 462: return p.getRootPlot(); 463: } 464: 465: } 466: 467: /** 468: * Returns <code>true</code> if this plot is part of a combined plot 469: * structure (that is, {@link #getParent()} returns a non-<code>null</code> 470: * value), and <code>false</code> otherwise. 471: * 472: * @return <code>true</code> if this plot is part of a combined plot 473: * structure. 474: * 475: * @see #getParent() 476: */ 477: public boolean isSubplot() { 478: return (getParent() != null); 479: } 480: 481: /** 482: * Returns the insets for the plot area. 483: * 484: * @return The insets (never <code>null</code>). 485: * 486: * @see #setInsets(RectangleInsets) 487: */ 488: public RectangleInsets getInsets() { 489: return this.insets; 490: } 491: 492: /** 493: * Sets the insets for the plot and sends a {@link PlotChangeEvent} to 494: * all registered listeners. 495: * 496: * @param insets the new insets (<code>null</code> not permitted). 497: * 498: * @see #getInsets() 499: * @see #setInsets(RectangleInsets, boolean) 500: */ 501: public void setInsets(RectangleInsets insets) { 502: setInsets(insets, true); 503: } 504: 505: /** 506: * Sets the insets for the plot and, if requested, and sends a 507: * {@link PlotChangeEvent} to all registered listeners. 508: * 509: * @param insets the new insets (<code>null</code> not permitted). 510: * @param notify a flag that controls whether the registered listeners are 511: * notified. 512: * 513: * @see #getInsets() 514: * @see #setInsets(RectangleInsets) 515: */ 516: public void setInsets(RectangleInsets insets, boolean notify) { 517: if (insets == null) { 518: throw new IllegalArgumentException("Null 'insets' argument."); 519: } 520: if (!this.insets.equals(insets)) { 521: this.insets = insets; 522: if (notify) { 523: notifyListeners(new PlotChangeEvent(this)); 524: } 525: } 526: 527: } 528: 529: /** 530: * Returns the background color of the plot area. 531: * 532: * @return The paint (possibly <code>null</code>). 533: * 534: * @see #setBackgroundPaint(Paint) 535: */ 536: public Paint getBackgroundPaint() { 537: return this.backgroundPaint; 538: } 539: 540: /** 541: * Sets the background color of the plot area and sends a 542: * {@link PlotChangeEvent} to all registered listeners. 543: * 544: * @param paint the paint (<code>null</code> permitted). 545: * 546: * @see #getBackgroundPaint() 547: */ 548: public void setBackgroundPaint(Paint paint) { 549: 550: if (paint == null) { 551: if (this.backgroundPaint != null) { 552: this.backgroundPaint = null; 553: notifyListeners(new PlotChangeEvent(this)); 554: } 555: } 556: else { 557: if (this.backgroundPaint != null) { 558: if (this.backgroundPaint.equals(paint)) { 559: return; // nothing to do 560: } 561: } 562: this.backgroundPaint = paint; 563: notifyListeners(new PlotChangeEvent(this)); 564: } 565: 566: } 567: 568: /** 569: * Returns the alpha transparency of the plot area background. 570: * 571: * @return The alpha transparency. 572: * 573: * @see #setBackgroundAlpha(float) 574: */ 575: public float getBackgroundAlpha() { 576: return this.backgroundAlpha; 577: } 578: 579: /** 580: * Sets the alpha transparency of the plot area background, and notifies 581: * registered listeners that the plot has been modified. 582: * 583: * @param alpha the new alpha value (in the range 0.0f to 1.0f). 584: * 585: * @see #getBackgroundAlpha() 586: */ 587: public void setBackgroundAlpha(float alpha) { 588: if (this.backgroundAlpha != alpha) { 589: this.backgroundAlpha = alpha; 590: notifyListeners(new PlotChangeEvent(this)); 591: } 592: } 593: 594: /** 595: * Returns the drawing supplier for the plot. 596: * 597: * @return The drawing supplier (possibly <code>null</code>). 598: * 599: * @see #setDrawingSupplier(DrawingSupplier) 600: */ 601: public DrawingSupplier getDrawingSupplier() { 602: DrawingSupplier result = null; 603: Plot p = getParent(); 604: if (p != null) { 605: result = p.getDrawingSupplier(); 606: } 607: else { 608: result = this.drawingSupplier; 609: } 610: return result; 611: } 612: 613: /** 614: * Sets the drawing supplier for the plot. The drawing supplier is 615: * responsible for supplying a limitless (possibly repeating) sequence of 616: * <code>Paint</code>, <code>Stroke</code> and <code>Shape</code> objects 617: * that the plot's renderer(s) can use to populate its (their) tables. 618: * 619: * @param supplier the new supplier. 620: * 621: * @see #getDrawingSupplier() 622: */ 623: public void setDrawingSupplier(DrawingSupplier supplier) { 624: this.drawingSupplier = supplier; 625: notifyListeners(new PlotChangeEvent(this)); 626: } 627: 628: /** 629: * Returns the background image that is used to fill the plot's background 630: * area. 631: * 632: * @return The image (possibly <code>null</code>). 633: * 634: * @see #setBackgroundImage(Image) 635: */ 636: public Image getBackgroundImage() { 637: return this.backgroundImage; 638: } 639: 640: /** 641: * Sets the background image for the plot and sends a 642: * {@link PlotChangeEvent} to all registered listeners. 643: * 644: * @param image the image (<code>null</code> permitted). 645: * 646: * @see #getBackgroundImage() 647: */ 648: public void setBackgroundImage(Image image) { 649: this.backgroundImage = image; 650: notifyListeners(new PlotChangeEvent(this)); 651: } 652: 653: /** 654: * Returns the background image alignment. Alignment constants are defined 655: * in the <code>org.jfree.ui.Align</code> class in the JCommon class 656: * library. 657: * 658: * @return The alignment. 659: * 660: * @see #setBackgroundImageAlignment(int) 661: */ 662: public int getBackgroundImageAlignment() { 663: return this.backgroundImageAlignment; 664: } 665: 666: /** 667: * Sets the alignment for the background image and sends a 668: * {@link PlotChangeEvent} to all registered listeners. Alignment options 669: * are defined by the {@link org.jfree.ui.Align} class in the JCommon 670: * class library. 671: * 672: * @param alignment the alignment. 673: * 674: * @see #getBackgroundImageAlignment() 675: */ 676: public void setBackgroundImageAlignment(int alignment) { 677: if (this.backgroundImageAlignment != alignment) { 678: this.backgroundImageAlignment = alignment; 679: notifyListeners(new PlotChangeEvent(this)); 680: } 681: } 682: 683: /** 684: * Returns the alpha transparency used to draw the background image. This 685: * is a value in the range 0.0f to 1.0f, where 0.0f is fully transparent 686: * and 1.0f is fully opaque. 687: * 688: * @return The alpha transparency. 689: * 690: * @see #setBackgroundImageAlpha(float) 691: */ 692: public float getBackgroundImageAlpha() { 693: return this.backgroundImageAlpha; 694: } 695: 696: /** 697: * Sets the alpha transparency used when drawing the background image. 698: * 699: * @param alpha the alpha transparency (in the range 0.0f to 1.0f, where 700: * 0.0f is fully transparent, and 1.0f is fully opaque). 701: * 702: * @throws IllegalArgumentException if <code>alpha</code> is not within 703: * the specified range. 704: * 705: * @see #getBackgroundImageAlpha() 706: */ 707: public void setBackgroundImageAlpha(float alpha) { 708: if (alpha < 0.0f || alpha > 1.0f) 709: throw new IllegalArgumentException( 710: "The 'alpha' value must be in the range 0.0f to 1.0f."); 711: if (this.backgroundImageAlpha != alpha) { 712: this.backgroundImageAlpha = alpha; 713: this.notifyListeners(new PlotChangeEvent(this)); 714: } 715: } 716: 717: /** 718: * Returns the flag that controls whether or not the plot outline is 719: * drawn. The default value is <code>true</code>. Note that for 720: * historical reasons, the plot's outline paint and stroke can take on 721: * <code>null</code> values, in which case the outline will not be drawn 722: * even if this flag is set to <code>true</code>. 723: * 724: * @return The outline visibility flag. 725: * 726: * @since 1.0.6 727: * 728: * @see #setOutlineVisible(boolean) 729: */ 730: public boolean isOutlineVisible() { 731: return this.outlineVisible; 732: } 733: 734: /** 735: * Sets the flag that controls whether or not the plot's outline is 736: * drawn, and sends a {@link PlotChangeEvent} to all registered listeners. 737: * 738: * @param visible the new flag value. 739: * 740: * @since 1.0.6 741: * 742: * @see #isOutlineVisible() 743: */ 744: public void setOutlineVisible(boolean visible) { 745: this.outlineVisible = visible; 746: notifyListeners(new PlotChangeEvent(this)); 747: } 748: 749: /** 750: * Returns the stroke used to outline the plot area. 751: * 752: * @return The stroke (possibly <code>null</code>). 753: * 754: * @see #setOutlineStroke(Stroke) 755: */ 756: public Stroke getOutlineStroke() { 757: return this.outlineStroke; 758: } 759: 760: /** 761: * Sets the stroke used to outline the plot area and sends a 762: * {@link PlotChangeEvent} to all registered listeners. If you set this 763: * attribute to <code>null</code>, no outline will be drawn. 764: * 765: * @param stroke the stroke (<code>null</code> permitted). 766: * 767: * @see #getOutlineStroke() 768: */ 769: public void setOutlineStroke(Stroke stroke) { 770: if (stroke == null) { 771: if (this.outlineStroke != null) { 772: this.outlineStroke = null; 773: notifyListeners(new PlotChangeEvent(this)); 774: } 775: } 776: else { 777: if (this.outlineStroke != null) { 778: if (this.outlineStroke.equals(stroke)) { 779: return; // nothing to do 780: } 781: } 782: this.outlineStroke = stroke; 783: notifyListeners(new PlotChangeEvent(this)); 784: } 785: } 786: 787: /** 788: * Returns the color used to draw the outline of the plot area. 789: * 790: * @return The color (possibly <code>null<code>). 791: * 792: * @see #setOutlinePaint(Paint) 793: */ 794: public Paint getOutlinePaint() { 795: return this.outlinePaint; 796: } 797: 798: /** 799: * Sets the paint used to draw the outline of the plot area and sends a 800: * {@link PlotChangeEvent} to all registered listeners. If you set this 801: * attribute to <code>null</code>, no outline will be drawn. 802: * 803: * @param paint the paint (<code>null</code> permitted). 804: * 805: * @see #getOutlinePaint() 806: */ 807: public void setOutlinePaint(Paint paint) { 808: if (paint == null) { 809: if (this.outlinePaint != null) { 810: this.outlinePaint = null; 811: notifyListeners(new PlotChangeEvent(this)); 812: } 813: } 814: else { 815: if (this.outlinePaint != null) { 816: if (this.outlinePaint.equals(paint)) { 817: return; // nothing to do 818: } 819: } 820: this.outlinePaint = paint; 821: notifyListeners(new PlotChangeEvent(this)); 822: } 823: } 824: 825: /** 826: * Returns the alpha-transparency for the plot foreground. 827: * 828: * @return The alpha-transparency. 829: * 830: * @see #setForegroundAlpha(float) 831: */ 832: public float getForegroundAlpha() { 833: return this.foregroundAlpha; 834: } 835: 836: /** 837: * Sets the alpha-transparency for the plot and sends a 838: * {@link PlotChangeEvent} to all registered listeners. 839: * 840: * @param alpha the new alpha transparency. 841: * 842: * @see #getForegroundAlpha() 843: */ 844: public void setForegroundAlpha(float alpha) { 845: if (this.foregroundAlpha != alpha) { 846: this.foregroundAlpha = alpha; 847: notifyListeners(new PlotChangeEvent(this)); 848: } 849: } 850: 851: /** 852: * Returns the legend items for the plot. By default, this method returns 853: * <code>null</code>. Subclasses should override to return a 854: * {@link LegendItemCollection}. 855: * 856: * @return The legend items for the plot (possibly <code>null</code>). 857: */ 858: public LegendItemCollection getLegendItems() { 859: return null; 860: } 861: 862: /** 863: * Registers an object for notification of changes to the plot. 864: * 865: * @param listener the object to be registered. 866: * 867: * @see #removeChangeListener(PlotChangeListener) 868: */ 869: public void addChangeListener(PlotChangeListener listener) { 870: this.listenerList.add(PlotChangeListener.class, listener); 871: } 872: 873: /** 874: * Unregisters an object for notification of changes to the plot. 875: * 876: * @param listener the object to be unregistered. 877: * 878: * @see #addChangeListener(PlotChangeListener) 879: */ 880: public void removeChangeListener(PlotChangeListener listener) { 881: this.listenerList.remove(PlotChangeListener.class, listener); 882: } 883: 884: /** 885: * Notifies all registered listeners that the plot has been modified. 886: * 887: * @param event information about the change event. 888: */ 889: public void notifyListeners(PlotChangeEvent event) { 890: Object[] listeners = this.listenerList.getListenerList(); 891: for (int i = listeners.length - 2; i >= 0; i -= 2) { 892: if (listeners[i] == PlotChangeListener.class) { 893: ((PlotChangeListener) listeners[i + 1]).plotChanged(event); 894: } 895: } 896: } 897: 898: /** 899: * Draws the plot within the specified area. The anchor is a point on the 900: * chart that is specified externally (for instance, it may be the last 901: * point of the last mouse click performed by the user) - plots can use or 902: * ignore this value as they see fit. 903: * <br><br> 904: * Subclasses need to provide an implementation of this method, obviously. 905: * 906: * @param g2 the graphics device. 907: * @param area the plot area. 908: * @param anchor the anchor point (<code>null</code> permitted). 909: * @param parentState the parent state (if any). 910: * @param info carries back plot rendering info. 911: */ 912: public abstract void draw(Graphics2D g2, 913: Rectangle2D area, 914: Point2D anchor, 915: PlotState parentState, 916: PlotRenderingInfo info); 917: 918: /** 919: * Draws the plot background (the background color and/or image). 920: * <P> 921: * This method will be called during the chart drawing process and is 922: * declared public so that it can be accessed by the renderers used by 923: * certain subclasses. You shouldn't need to call this method directly. 924: * 925: * @param g2 the graphics device. 926: * @param area the area within which the plot should be drawn. 927: */ 928: public void drawBackground(Graphics2D g2, Rectangle2D area) { 929: // some subclasses override this method completely, so don't put 930: // anything here that *must* be done 931: fillBackground(g2, area); 932: drawBackgroundImage(g2, area); 933: } 934: 935: /** 936: * Fills the specified area with the background paint. 937: * 938: * @param g2 the graphics device. 939: * @param area the area. 940: * 941: * @see #getBackgroundPaint() 942: * @see #getBackgroundAlpha() 943: * @see #fillBackground(Graphics2D, Rectangle2D, PlotOrientation) 944: */ 945: protected void fillBackground(Graphics2D g2, Rectangle2D area) { 946: fillBackground(g2, area, PlotOrientation.VERTICAL); 947: } 948: 949: /** 950: * Fills the specified area with the background paint. If the background 951: * paint is an instance of <code>GradientPaint</code>, the gradient will 952: * run in the direction suggested by the plot's orientation. 953: * 954: * @param g2 the graphics target. 955: * @param area the plot area. 956: * @param orientation the plot orientation (<code>null</code> not 957: * permitted). 958: * 959: * @since 1.0.6 960: */ 961: protected void fillBackground(Graphics2D g2, Rectangle2D area, 962: PlotOrientation orientation) { 963: if (orientation == null) { 964: throw new IllegalArgumentException("Null 'orientation' argument."); 965: } 966: if (this.backgroundPaint == null) { 967: return; 968: } 969: Paint p = this.backgroundPaint; 970: if (p instanceof GradientPaint) { 971: GradientPaint gp = (GradientPaint) p; 972: if (orientation == PlotOrientation.VERTICAL) { 973: p = new GradientPaint((float) area.getCenterX(), 974: (float) area.getMaxY(), gp.getColor1(), 975: (float) area.getCenterX(), (float) area.getMinY(), 976: gp.getColor2()); 977: } 978: else if (orientation == PlotOrientation.HORIZONTAL) { 979: p = new GradientPaint((float) area.getMinX(), 980: (float) area.getCenterY(), gp.getColor1(), 981: (float) area.getMaxX(), (float) area.getCenterY(), 982: gp.getColor2()); 983: } 984: } 985: Composite originalComposite = g2.getComposite(); 986: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 987: this.backgroundAlpha)); 988: g2.setPaint(p); 989: g2.fill(area); 990: g2.setComposite(originalComposite); 991: } 992: 993: /** 994: * Draws the background image (if there is one) aligned within the 995: * specified area. 996: * 997: * @param g2 the graphics device. 998: * @param area the area. 999: * 1000: * @see #getBackgroundImage() 1001: * @see #getBackgroundImageAlignment() 1002: * @see #getBackgroundImageAlpha() 1003: */ 1004: public void drawBackgroundImage(Graphics2D g2, Rectangle2D area) { 1005: if (this.backgroundImage != null) { 1006: Composite originalComposite = g2.getComposite(); 1007: g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1008: this.backgroundImageAlpha)); 1009: Rectangle2D dest = new Rectangle2D.Double(0.0, 0.0, 1010: this.backgroundImage.getWidth(null), 1011: this.backgroundImage.getHeight(null)); 1012: Align.align(dest, area, this.backgroundImageAlignment); 1013: g2.drawImage(this.backgroundImage, (int) dest.getX(), 1014: (int) dest.getY(), (int) dest.getWidth() + 1, 1015: (int) dest.getHeight() + 1, null); 1016: g2.setComposite(originalComposite); 1017: } 1018: } 1019: 1020: /** 1021: * Draws the plot outline. This method will be called during the chart 1022: * drawing process and is declared public so that it can be accessed by the 1023: * renderers used by certain subclasses. You shouldn't need to call this 1024: * method directly. 1025: * 1026: * @param g2 the graphics device. 1027: * @param area the area within which the plot should be drawn. 1028: */ 1029: public void drawOutline(Graphics2D g2, Rectangle2D area) { 1030: if (!this.outlineVisible) { 1031: return; 1032: } 1033: if ((this.outlineStroke != null) && (this.outlinePaint != null)) { 1034: g2.setStroke(this.outlineStroke); 1035: g2.setPaint(this.outlinePaint); 1036: g2.draw(area); 1037: } 1038: } 1039: 1040: /** 1041: * Draws a message to state that there is no data to plot. 1042: * 1043: * @param g2 the graphics device. 1044: * @param area the area within which the plot should be drawn. 1045: */ 1046: protected void drawNoDataMessage(Graphics2D g2, Rectangle2D area) { 1047: Shape savedClip = g2.getClip(); 1048: g2.clip(area); 1049: String message = this.noDataMessage; 1050: if (message != null) { 1051: g2.setFont(this.noDataMessageFont); 1052: g2.setPaint(this.noDataMessagePaint); 1053: TextBlock block = TextUtilities.createTextBlock( 1054: this.noDataMessage, this.noDataMessageFont, 1055: this.noDataMessagePaint, 0.9f * (float) area.getWidth(), 1056: new G2TextMeasurer(g2)); 1057: block.draw(g2, (float) area.getCenterX(), 1058: (float) area.getCenterY(), TextBlockAnchor.CENTER); 1059: } 1060: g2.setClip(savedClip); 1061: } 1062: 1063: /** 1064: * Handles a 'click' on the plot. Since the plot does not maintain any 1065: * information about where it has been drawn, the plot rendering info is 1066: * supplied as an argument. 1067: * 1068: * @param x the x coordinate (in Java2D space). 1069: * @param y the y coordinate (in Java2D space). 1070: * @param info an object containing information about the dimensions of 1071: * the plot. 1072: */ 1073: public void handleClick(int x, int y, PlotRenderingInfo info) { 1074: // provides a 'no action' default 1075: } 1076: 1077: /** 1078: * Performs a zoom on the plot. Subclasses should override if zooming is 1079: * appropriate for the type of plot. 1080: * 1081: * @param percent the zoom percentage. 1082: */ 1083: public void zoom(double percent) { 1084: // do nothing by default. 1085: } 1086: 1087: /** 1088: * Receives notification of a change to one of the plot's axes. 1089: * 1090: * @param event information about the event (not used here). 1091: */ 1092: public void axisChanged(AxisChangeEvent event) { 1093: notifyListeners(new PlotChangeEvent(this)); 1094: } 1095: 1096: /** 1097: * Receives notification of a change to the plot's dataset. 1098: * <P> 1099: * The plot reacts by passing on a plot change event to all registered 1100: * listeners. 1101: * 1102: * @param event information about the event (not used here). 1103: */ 1104: public void datasetChanged(DatasetChangeEvent event) { 1105: PlotChangeEvent newEvent = new PlotChangeEvent(this); 1106: newEvent.setType(ChartChangeEventType.DATASET_UPDATED); 1107: notifyListeners(newEvent); 1108: } 1109: 1110: /** 1111: * Receives notification of a change to a marker that is assigned to the 1112: * plot. 1113: * 1114: * @param event the event. 1115: * 1116: * @since 1.0.3 1117: */ 1118: public void markerChanged(MarkerChangeEvent event) { 1119: notifyListeners(new PlotChangeEvent(this)); 1120: } 1121: 1122: /** 1123: * Adjusts the supplied x-value. 1124: * 1125: * @param x the x-value. 1126: * @param w1 width 1. 1127: * @param w2 width 2. 1128: * @param edge the edge (left or right). 1129: * 1130: * @return The adjusted x-value. 1131: */ 1132: protected double getRectX(double x, double w1, double w2, 1133: RectangleEdge edge) { 1134: 1135: double result = x; 1136: if (edge == RectangleEdge.LEFT) { 1137: result = result + w1; 1138: } 1139: else if (edge == RectangleEdge.RIGHT) { 1140: result = result + w2; 1141: } 1142: return result; 1143: 1144: } 1145: 1146: /** 1147: * Adjusts the supplied y-value. 1148: * 1149: * @param y the x-value. 1150: * @param h1 height 1. 1151: * @param h2 height 2. 1152: * @param edge the edge (top or bottom). 1153: * 1154: * @return The adjusted y-value. 1155: */ 1156: protected double getRectY(double y, double h1, double h2, 1157: RectangleEdge edge) { 1158: 1159: double result = y; 1160: if (edge == RectangleEdge.TOP) { 1161: result = result + h1; 1162: } 1163: else if (edge == RectangleEdge.BOTTOM) { 1164: result = result + h2; 1165: } 1166: return result; 1167: 1168: } 1169: 1170: /** 1171: * Tests this plot for equality with another object. 1172: * 1173: * @param obj the object (<code>null</code> permitted). 1174: * 1175: * @return <code>true</code> or <code>false</code>. 1176: */ 1177: public boolean equals(Object obj) { 1178: if (obj == this) { 1179: return true; 1180: } 1181: if (!(obj instanceof Plot)) { 1182: return false; 1183: } 1184: Plot that = (Plot) obj; 1185: if (!ObjectUtilities.equal(this.noDataMessage, that.noDataMessage)) { 1186: return false; 1187: } 1188: if (!ObjectUtilities.equal( 1189: this.noDataMessageFont, that.noDataMessageFont 1190: )) { 1191: return false; 1192: } 1193: if (!PaintUtilities.equal(this.noDataMessagePaint, 1194: that.noDataMessagePaint)) { 1195: return false; 1196: } 1197: if (!ObjectUtilities.equal(this.insets, that.insets)) { 1198: return false; 1199: } 1200: if (this.outlineVisible != that.outlineVisible) { 1201: return false; 1202: } 1203: if (!ObjectUtilities.equal(this.outlineStroke, that.outlineStroke)) { 1204: return false; 1205: } 1206: if (!PaintUtilities.equal(this.outlinePaint, that.outlinePaint)) { 1207: return false; 1208: } 1209: if (!PaintUtilities.equal(this.backgroundPaint, that.backgroundPaint)) { 1210: return false; 1211: } 1212: if (!ObjectUtilities.equal(this.backgroundImage, 1213: that.backgroundImage)) { 1214: return false; 1215: } 1216: if (this.backgroundImageAlignment != that.backgroundImageAlignment) { 1217: return false; 1218: } 1219: if (this.backgroundImageAlpha != that.backgroundImageAlpha) { 1220: return false; 1221: } 1222: if (this.foregroundAlpha != that.foregroundAlpha) { 1223: return false; 1224: } 1225: if (this.backgroundAlpha != that.backgroundAlpha) { 1226: return false; 1227: } 1228: if (!this.drawingSupplier.equals(that.drawingSupplier)) { 1229: return false; 1230: } 1231: return true; 1232: } 1233: 1234: /** 1235: * Creates a clone of the plot. 1236: * 1237: * @return A clone. 1238: * 1239: * @throws CloneNotSupportedException if some component of the plot does not 1240: * support cloning. 1241: */ 1242: public Object clone() throws CloneNotSupportedException { 1243: 1244: Plot clone = (Plot) super.clone(); 1245: // private Plot parent <-- don't clone the parent plot, but take care 1246: // childs in combined plots instead 1247: if (this.datasetGroup != null) { 1248: clone.datasetGroup 1249: = (DatasetGroup) ObjectUtilities.clone(this.datasetGroup); 1250: } 1251: clone.drawingSupplier 1252: = (DrawingSupplier) ObjectUtilities.clone(this.drawingSupplier); 1253: clone.listenerList = new EventListenerList(); 1254: return clone; 1255: 1256: } 1257: 1258: /** 1259: * Provides serialization support. 1260: * 1261: * @param stream the output stream. 1262: * 1263: * @throws IOException if there is an I/O error. 1264: */ 1265: private void writeObject(ObjectOutputStream stream) throws IOException { 1266: stream.defaultWriteObject(); 1267: SerialUtilities.writePaint(this.noDataMessagePaint, stream); 1268: SerialUtilities.writeStroke(this.outlineStroke, stream); 1269: SerialUtilities.writePaint(this.outlinePaint, stream); 1270: // backgroundImage 1271: SerialUtilities.writePaint(this.backgroundPaint, stream); 1272: } 1273: 1274: /** 1275: * Provides serialization support. 1276: * 1277: * @param stream the input stream. 1278: * 1279: * @throws IOException if there is an I/O error. 1280: * @throws ClassNotFoundException if there is a classpath problem. 1281: */ 1282: private void readObject(ObjectInputStream stream) 1283: throws IOException, ClassNotFoundException { 1284: stream.defaultReadObject(); 1285: this.noDataMessagePaint = SerialUtilities.readPaint(stream); 1286: this.outlineStroke = SerialUtilities.readStroke(stream); 1287: this.outlinePaint = SerialUtilities.readPaint(stream); 1288: // backgroundImage 1289: this.backgroundPaint = SerialUtilities.readPaint(stream); 1290: 1291: this.listenerList = new EventListenerList(); 1292: 1293: } 1294: 1295: /** 1296: * Resolves a domain axis location for a given plot orientation. 1297: * 1298: * @param location the location (<code>null</code> not permitted). 1299: * @param orientation the orientation (<code>null</code> not permitted). 1300: * 1301: * @return The edge (never <code>null</code>). 1302: */ 1303: public static RectangleEdge resolveDomainAxisLocation( 1304: AxisLocation location, PlotOrientation orientation) { 1305: 1306: if (location == null) { 1307: throw new IllegalArgumentException("Null 'location' argument."); 1308: } 1309: if (orientation == null) { 1310: throw new IllegalArgumentException("Null 'orientation' argument."); 1311: } 1312: 1313: RectangleEdge result = null; 1314: 1315: if (location == AxisLocation.TOP_OR_RIGHT) { 1316: if (orientation == PlotOrientation.HORIZONTAL) { 1317: result = RectangleEdge.RIGHT; 1318: } 1319: else if (orientation == PlotOrientation.VERTICAL) { 1320: result = RectangleEdge.TOP; 1321: } 1322: } 1323: else if (location == AxisLocation.TOP_OR_LEFT) { 1324: if (orientation == PlotOrientation.HORIZONTAL) { 1325: result = RectangleEdge.LEFT; 1326: } 1327: else if (orientation == PlotOrientation.VERTICAL) { 1328: result = RectangleEdge.TOP; 1329: } 1330: } 1331: else if (location == AxisLocation.BOTTOM_OR_RIGHT) { 1332: if (orientation == PlotOrientation.HORIZONTAL) { 1333: result = RectangleEdge.RIGHT; 1334: } 1335: else if (orientation == PlotOrientation.VERTICAL) { 1336: result = RectangleEdge.BOTTOM; 1337: } 1338: } 1339: else if (location == AxisLocation.BOTTOM_OR_LEFT) { 1340: if (orientation == PlotOrientation.HORIZONTAL) { 1341: result = RectangleEdge.LEFT; 1342: } 1343: else if (orientation == PlotOrientation.VERTICAL) { 1344: result = RectangleEdge.BOTTOM; 1345: } 1346: } 1347: // the above should cover all the options... 1348: if (result == null) { 1349: throw new IllegalStateException("resolveDomainAxisLocation()"); 1350: } 1351: return result; 1352: 1353: } 1354: 1355: /** 1356: * Resolves a range axis location for a given plot orientation. 1357: * 1358: * @param location the location (<code>null</code> not permitted). 1359: * @param orientation the orientation (<code>null</code> not permitted). 1360: * 1361: * @return The edge (never <code>null</code>). 1362: */ 1363: public static RectangleEdge resolveRangeAxisLocation( 1364: AxisLocation location, PlotOrientation orientation) { 1365: 1366: if (location == null) { 1367: throw new IllegalArgumentException("Null 'location' argument."); 1368: } 1369: if (orientation == null) { 1370: throw new IllegalArgumentException("Null 'orientation' argument."); 1371: } 1372: 1373: RectangleEdge result = null; 1374: 1375: if (location == AxisLocation.TOP_OR_RIGHT) { 1376: if (orientation == PlotOrientation.HORIZONTAL) { 1377: result = RectangleEdge.TOP; 1378: } 1379: else if (orientation == PlotOrientation.VERTICAL) { 1380: result = RectangleEdge.RIGHT; 1381: } 1382: } 1383: else if (location == AxisLocation.TOP_OR_LEFT) { 1384: if (orientation == PlotOrientation.HORIZONTAL) { 1385: result = RectangleEdge.TOP; 1386: } 1387: else if (orientation == PlotOrientation.VERTICAL) { 1388: result = RectangleEdge.LEFT; 1389: } 1390: } 1391: else if (location == AxisLocation.BOTTOM_OR_RIGHT) { 1392: if (orientation == PlotOrientation.HORIZONTAL) { 1393: result = RectangleEdge.BOTTOM; 1394: } 1395: else if (orientation == PlotOrientation.VERTICAL) { 1396: result = RectangleEdge.RIGHT; 1397: } 1398: } 1399: else if (location == AxisLocation.BOTTOM_OR_LEFT) { 1400: if (orientation == PlotOrientation.HORIZONTAL) { 1401: result = RectangleEdge.BOTTOM; 1402: } 1403: else if (orientation == PlotOrientation.VERTICAL) { 1404: result = RectangleEdge.LEFT; 1405: } 1406: } 1407: 1408: // the above should cover all the options... 1409: if (result == null) { 1410: throw new IllegalStateException("resolveRangeAxisLocation()"); 1411: } 1412: return result; 1413: 1414: } 1415: 1416: }