Java tutorial
/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-2009, by Object Refinery Limited and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Java is a trademark or registered trademark of Sun Microsystems, Inc. * in the United States and other countries.] * * ----------- * XYPlot.java * ----------- * (C) Copyright 2000-2009, by Object Refinery Limited and Contributors. * * Original Author: David Gilbert (for Object Refinery Limited); * Contributor(s): Craig MacFarlane; * Mark Watson (www.markwatson.com); * Jonathan Nash; * Gideon Krause; * Klaus Rheinwald; * Xavier Poinsard; * Richard Atkinson; * Arnaud Lelievre; * Nicolas Brodu; * Eduardo Ramalho; * Sergei Ivanov; * Richard West, Advanced Micro Devices, Inc.; * Ulrich Voigt - patches 1997549 and 2686040; * Peter Kolb - patches 1934255 and 2603321; * Andrew Mickish - patch 1868749; * * Changes (from 21-Jun-2001) * -------------------------- * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG); * 18-Sep-2001 : Updated header and fixed DOS encoding problem (DG); * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG); * 19-Oct-2001 : Removed the code for drawing the visual representation of each * data point into a separate class StandardXYItemRenderer. * This will make it easier to add variations to the way the * charts are drawn. Based on code contributed by Mark * Watson (DG); * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG); * 20-Nov-2001 : Fixed clipping bug that shows up when chart is displayed * inside JScrollPane (DG); * 12-Dec-2001 : Removed unnecessary 'throws' clauses from constructor (DG); * 13-Dec-2001 : Added skeleton code for tooltips. Added new constructor. (DG); * 16-Jan-2002 : Renamed the tooltips class (DG); * 22-Jan-2002 : Added DrawInfo class, incorporating tooltips and crosshairs. * Crosshairs based on code by Jonathan Nash (DG); * 05-Feb-2002 : Added alpha-transparency setting based on code by Sylvain * Vieujot (DG); * 26-Feb-2002 : Updated getMinimumXXX() and getMaximumXXX() methods to handle * special case when chart is null (DG); * 28-Feb-2002 : Renamed Datasets.java --> DatasetUtilities.java (DG); * 28-Mar-2002 : The plot now registers with the renderer as a property change * listener. Also added a new constructor (DG); * 09-Apr-2002 : Removed the transRangeZero from the renderer.drawItem() * method. Moved the tooltip generator into the renderer (DG); * 23-Apr-2002 : Fixed bug in methods for drawing horizontal and vertical * lines (DG); * 13-May-2002 : Small change to the draw() method so that it works for * OverlaidXYPlot also (DG); * 25-Jun-2002 : Removed redundant import (DG); * 20-Aug-2002 : Renamed getItemRenderer() --> getRenderer(), and * setXYItemRenderer() --> setRenderer() (DG); * 28-Aug-2002 : Added mechanism for (optional) plot annotations (DG); * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG); * 18-Nov-2002 : Added grid settings for both domain and range axis (previously * these were set in the axes) (DG); * 09-Jan-2003 : Further additions to the grid settings, plus integrated plot * border bug fix contributed by Gideon Krause (DG); * 22-Jan-2003 : Removed monolithic constructor (DG); * 04-Mar-2003 : Added 'no data' message, see bug report 691634. Added * secondary range markers using code contributed by Klaus * Rheinwald (DG); * 26-Mar-2003 : Implemented Serializable (DG); * 03-Apr-2003 : Added setDomainAxisLocation() method (DG); * 30-Apr-2003 : Moved annotation drawing into a separate method (DG); * 01-May-2003 : Added multi-pass mechanism for renderers (DG); * 02-May-2003 : Changed axis locations from int to AxisLocation (DG); * 15-May-2003 : Added an orientation attribute (DG); * 02-Jun-2003 : Removed range axis compatibility test (DG); * 05-Jun-2003 : Added domain and range grid bands (sponsored by Focus Computer * Services Ltd) (DG); * 26-Jun-2003 : Fixed bug (757303) in getDataRange() method (DG); * 02-Jul-2003 : Added patch from bug report 698646 (secondary axes for * overlaid plots) (DG); * 23-Jul-2003 : Added support for multiple secondary datasets, axes and * renderers (DG); * 27-Jul-2003 : Added support for stacked XY area charts (RA); * 19-Aug-2003 : Implemented Cloneable (DG); * 01-Sep-2003 : Fixed bug where change to secondary datasets didn't generate * change event (797466) (DG) * 08-Sep-2003 : Added internationalization via use of properties * resourceBundle (RFE 690236) (AL); * 08-Sep-2003 : Changed ValueAxis API (DG); * 08-Sep-2003 : Fixes for serialization (NB); * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); * 17-Sep-2003 : Fixed zooming to include secondary domain axes (DG); * 18-Sep-2003 : Added getSecondaryDomainAxisCount() and * getSecondaryRangeAxisCount() methods suggested by Eduardo * Ramalho (RFE 808548) (DG); * 23-Sep-2003 : Split domain and range markers into foreground and * background (DG); * 06-Oct-2003 : Fixed bug in clearDomainMarkers() and clearRangeMarkers() * methods. Fixed bug (815876) in addSecondaryRangeMarker() * method. Added new addSecondaryDomainMarker methods (see bug * id 815869) (DG); * 10-Nov-2003 : Added getSecondaryDomain/RangeAxisMappedToDataset() methods * requested by Eduardo Ramalho (DG); * 24-Nov-2003 : Removed unnecessary notification when updating axis anchor * values (DG); * 21-Jan-2004 : Update for renamed method in ValueAxis (DG); * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG); * 12-Mar-2004 : Fixed bug where primary renderer is always used to determine * range type (DG); * 22-Mar-2004 : Fixed cloning bug (DG); * 23-Mar-2004 : Fixed more cloning bugs (DG); * 07-Apr-2004 : Fixed problem with axis range when the secondary renderer is * stacked, see this post in the forum: * http://www.jfree.org/phpBB2/viewtopic.php?t=8204 (DG); * 07-Apr-2004 : Added get/setDatasetRenderingOrder() methods (DG); * 26-Apr-2004 : Added option to fill quadrant areas in the background of the * plot (DG); * 27-Apr-2004 : Removed major distinction between primary and secondary * datasets, renderers and axes (DG); * 30-Apr-2004 : Modified to make use of the new getRangeExtent() method in the * renderer interface (DG); * 13-May-2004 : Added optional fixedLegendItems attribute (DG); * 19-May-2004 : Added indexOf() method (DG); * 03-Jun-2004 : Fixed zooming bug (DG); * 18-Aug-2004 : Added removedAnnotation() method (by tkram01) (DG); * 05-Oct-2004 : Modified storage type for dataset-to-axis maps (DG); * 06-Oct-2004 : Modified getDataRange() method to use renderer to determine * the x-value range (now matches behaviour for y-values). Added * getDomainAxisIndex() method (DG); * 12-Nov-2004 : Implemented new Zoomable interface (DG); * 25-Nov-2004 : Small update to clone() implementation (DG); * 22-Feb-2005 : Changed axis offsets from Spacer --> RectangleInsets (DG); * 24-Feb-2005 : Added indexOf(XYItemRenderer) method (DG); * 21-Mar-2005 : Register plot as change listener in setRenderer() method (DG); * 21-Apr-2005 : Added get/setSeriesRenderingOrder() methods (ET); * 26-Apr-2005 : Removed LOGGER (DG); * 04-May-2005 : Fixed serialization of domain and range markers (DG); * 05-May-2005 : Removed unused draw() method (DG); * 20-May-2005 : Added setDomainAxes() and setRangeAxes() methods, as per * RFE 1183100 (DG); * 01-Jun-2005 : Upon deserialization, register plot as a listener with its * axes, dataset(s) and renderer(s) - see patch 1209475 (DG); * 01-Jun-2005 : Added clearDomainMarkers(int) method to match * clearRangeMarkers(int) (DG); * 06-Jun-2005 : Fixed equals() method to handle GradientPaint (DG); * 09-Jun-2005 : Added setRenderers(), as per RFE 1183100 (DG); * 06-Jul-2005 : Fixed crosshair bug (id = 1233336) (DG); * ------------- JFREECHART 1.0.x --------------------------------------------- * 26-Jan-2006 : Added getAnnotations() method (DG); * 05-Sep-2006 : Added MarkerChangeEvent support (DG); * 13-Oct-2006 : Fixed initialisation of CrosshairState - see bug report * 1565168 (DG); * 22-Nov-2006 : Fixed equals() and cloning() for quadrant attributes, plus * API doc updates (DG); * 29-Nov-2006 : Added argument checks (DG); * 15-Jan-2007 : Fixed bug in drawRangeMarkers() (DG); * 07-Feb-2007 : Fixed bug 1654215, renderer with no dataset (DG); * 26-Feb-2007 : Added missing setDomainAxisLocation() and * setRangeAxisLocation() methods (DG); * 02-Mar-2007 : Fix for crosshair positioning with horizontal orientation * (see patch 1671648 by Sergei Ivanov) (DG); * 13-Mar-2007 : Added null argument checks for crosshair attributes (DG); * 23-Mar-2007 : Added domain zero base line facility (DG); * 04-May-2007 : Render only visible data items if possible (DG); * 24-May-2007 : Fixed bug in render method for an empty series (DG); * 07-Jun-2007 : Modified drawBackground() to pass orientation to * fillBackground() for handling GradientPaint (DG); * 24-Sep-2007 : Added new zoom methods (DG); * 26-Sep-2007 : Include index value in IllegalArgumentExceptions (DG); * 05-Nov-2007 : Applied patch 1823697, by Richard West, for removal of domain * and range markers (DG); * 12-Nov-2007 : Fixed bug in equals() method for domain and range tick * band paint attributes (DG); * 27-Nov-2007 : Added new setFixedDomain/RangeAxisSpace() methods (DG); * 04-Jan-2008 : Fix for quadrant painting error - see patch 1849564 (DG); * 25-Mar-2008 : Added new methods with optional notification - see patch * 1913751 (DG); * 07-Apr-2008 : Fixed NPE in removeDomainMarker() and * removeRangeMarker() (DG); * 22-May-2008 : Modified calculateAxisSpace() to process range axes first, * then adjust the plot area before calculating the space * for the domain axes (DG); * 09-Jul-2008 : Added renderer state notification when series pass begins * and ends - see patch 1997549 by Ulrich Voigt (DG); * 25-Jul-2008 : Fixed NullPointerException for plots with no axes (DG); * 15-Aug-2008 : Added getRendererCount() method (DG); * 25-Sep-2008 : Added minor tick support, see patch 1934255 by Peter Kolb (DG); * 25-Nov-2008 : Allow datasets to be mapped to multiple axes - based on patch * 1868749 by Andrew Mickish (DG); * 18-Dec-2008 : Use ResourceBundleWrapper - see patch 1607918 by * Jess Thrysoee (DG); * 10-Mar-2009 : Allow some annotations to contribute to axis autoRange (DG); * 18-Mar-2009 : Modified anchored zoom behaviour and fixed bug in * "process visible range" rendering (DG); * 19-Mar-2009 : Added panning support based on patch 2686040 by Ulrich * Voigt (DG); * 19-Mar-2009 : Added entity support - see patch 2603321 by Peter Kolb (DG); * 30-Mar-2009 : Delegate panning to axes (DG); * */ package net.droidsolutions.droidcharts.core.plot; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import net.droidsolutions.droidcharts.awt.Line2D; import net.droidsolutions.droidcharts.awt.Point2D; import net.droidsolutions.droidcharts.awt.Rectangle2D; import net.droidsolutions.droidcharts.awt.Shape; import net.droidsolutions.droidcharts.common.Layer; import net.droidsolutions.droidcharts.common.ObjectList; import net.droidsolutions.droidcharts.common.RectangleEdge; import net.droidsolutions.droidcharts.common.RectangleInsets; import net.droidsolutions.droidcharts.core.LegendItem; import net.droidsolutions.droidcharts.core.LegendItemCollection; import net.droidsolutions.droidcharts.core.anotations.XYAnnotation; import net.droidsolutions.droidcharts.core.anotations.XYAnnotationBoundsInfo; import net.droidsolutions.droidcharts.core.axis.Axis; import net.droidsolutions.droidcharts.core.axis.AxisCollection; import net.droidsolutions.droidcharts.core.axis.AxisLocation; import net.droidsolutions.droidcharts.core.axis.AxisSpace; import net.droidsolutions.droidcharts.core.axis.AxisState; import net.droidsolutions.droidcharts.core.axis.TickType; import net.droidsolutions.droidcharts.core.axis.ValueAxis; import net.droidsolutions.droidcharts.core.axis.ValueTick; import net.droidsolutions.droidcharts.core.data.Range; import net.droidsolutions.droidcharts.core.data.XYDataset; import net.droidsolutions.droidcharts.core.data.general.DatasetUtilities; import net.droidsolutions.droidcharts.core.event.RendererChangeEvent; import net.droidsolutions.droidcharts.core.event.RendererChangeListener; import net.droidsolutions.droidcharts.core.renderer.RendererUtilities; import net.droidsolutions.droidcharts.core.renderer.xy.AbstractXYItemRenderer; import net.droidsolutions.droidcharts.core.renderer.xy.XYItemRenderer; import net.droidsolutions.droidcharts.core.renderer.xy.XYItemRendererState; /** * A general class for plotting data in the form of (x, y) pairs. This plot can * use data from any class that implements the {@link XYDataset} interface. * <P> * <code>XYPlot</code> makes use of an {@link XYItemRenderer} to draw each point * on the plot. By using different renderers, various chart types can be * produced. * <p> * The {@link org.jfree.chart.ChartFactory} class contains static methods for * creating pre-configured charts. */ public class XYPlot extends Plot implements ValueAxisPlot, Pannable, Zoomable, RendererChangeListener, Cloneable { /** For serialization. */ private static final long serialVersionUID = 7044148245716569264L; /** The default grid line stroke. */ public static final Float DEFAULT_GRIDLINE_STROKE = 1f; /** The default grid line paint. */ public static final Paint DEFAULT_GRIDLINE_PAINT = new Paint(Paint.ANTI_ALIAS_FLAG); static { DEFAULT_GRIDLINE_PAINT.setColor(Color.LTGRAY); } /** The default crosshair visibility. */ public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false; /** The default crosshair stroke. */ public static final Float DEFAULT_CROSSHAIR_STROKE = DEFAULT_GRIDLINE_STROKE; /** The default crosshair paint. */ public static final Paint DEFAULT_CROSSHAIR_PAINT = new Paint(Paint.ANTI_ALIAS_FLAG); static { DEFAULT_CROSSHAIR_PAINT.setColor(Color.BLUE); } /** The plot orientation. */ private PlotOrientation orientation; /** The offset between the data area and the axes. */ private RectangleInsets axisOffset; /** The domain axis / axes (used for the x-values). */ private ObjectList domainAxes; /** The domain axis locations. */ private ObjectList domainAxisLocations; /** The range axis (used for the y-values). */ private ObjectList rangeAxes; /** The range axis location. */ private ObjectList rangeAxisLocations; /** Storage for the datasets. */ private ObjectList datasets; /** Storage for the renderers. */ private ObjectList renderers; /** * Storage for the mapping between datasets/renderers and domain axes. The * keys in the map are Integer objects, corresponding to the dataset index. * The values in the map are List objects containing Integer objects * (corresponding to the axis indices). If the map contains no entry for a * dataset, it is assumed to map to the primary domain axis (index = 0). */ private Map datasetToDomainAxesMap; /** * Storage for the mapping between datasets/renderers and range axes. The * keys in the map are Integer objects, corresponding to the dataset index. * The values in the map are List objects containing Integer objects * (corresponding to the axis indices). If the map contains no entry for a * dataset, it is assumed to map to the primary domain axis (index = 0). */ private Map datasetToRangeAxesMap; /** The origin point for the quadrants (if drawn). */ private transient Point2D quadrantOrigin = new Point2D.Double(0.0, 0.0); /** The paint used for each quadrant. */ private transient Paint[] quadrantPaint = new Paint[] { null, null, null, null }; /** A flag that controls whether the domain grid-lines are visible. */ private boolean domainGridlinesVisible; /** The stroke used to draw the domain grid-lines. */ private transient Float domainGridlineStroke; /** The paint used to draw the domain grid-lines. */ private transient Paint domainGridlinePaint; /** A flag that controls whether the range grid-lines are visible. */ private boolean rangeGridlinesVisible; /** The stroke used to draw the range grid-lines. */ private transient Float rangeGridlineStroke; /** The paint used to draw the range grid-lines. */ private transient Paint rangeGridlinePaint; /** * A flag that controls whether the domain minor grid-lines are visible. * * @since 1.0.12 */ private boolean domainMinorGridlinesVisible; /** * The stroke used to draw the domain minor grid-lines. * * @since 1.0.12 */ private transient Float domainMinorGridlineStroke; /** * The paint used to draw the domain minor grid-lines. * * @since 1.0.12 */ private transient Paint domainMinorGridlinePaint; /** * A flag that controls whether the range minor grid-lines are visible. * * @since 1.0.12 */ private boolean rangeMinorGridlinesVisible; /** * The stroke used to draw the range minor grid-lines. * * @since 1.0.12 */ private transient Float rangeMinorGridlineStroke; /** * The paint used to draw the range minor grid-lines. * * @since 1.0.12 */ private transient Paint rangeMinorGridlinePaint; /** * A flag that controls whether or not the zero baseline against the domain * axis is visible. * * @since 1.0.5 */ private boolean domainZeroBaselineVisible; /** * The stroke used for the zero baseline against the domain axis. * * @since 1.0.5 */ private transient Float domainZeroBaselineStroke; /** * The paint used for the zero baseline against the domain axis. * * @since 1.0.5 */ private transient Paint domainZeroBaselinePaint; /** * A flag that controls whether or not the zero baseline against the range * axis is visible. */ private boolean rangeZeroBaselineVisible; /** The stroke used for the zero baseline against the range axis. */ private transient Float rangeZeroBaselineStroke; /** The paint used for the zero baseline against the range axis. */ private transient Paint rangeZeroBaselinePaint; /** A flag that controls whether or not a domain crosshair is drawn.. */ private boolean domainCrosshairVisible; /** The domain crosshair value. */ private double domainCrosshairValue; /** The pen/brush used to draw the crosshair (if any). */ private transient Float domainCrosshairStroke; /** The color used to draw the crosshair (if any). */ private transient Paint domainCrosshairPaint; /** * A flag that controls whether or not the crosshair locks onto actual data * points. */ private boolean domainCrosshairLockedOnData = true; /** A flag that controls whether or not a range crosshair is drawn.. */ private boolean rangeCrosshairVisible; /** The range crosshair value. */ private double rangeCrosshairValue; /** The pen/brush used to draw the crosshair (if any). */ private transient Float rangeCrosshairStroke; /** The color used to draw the crosshair (if any). */ private transient Paint rangeCrosshairPaint; /** * A flag that controls whether or not the crosshair locks onto actual data * points. */ private boolean rangeCrosshairLockedOnData = true; /** A map of lists of foreground markers (optional) for the domain axes. */ private Map foregroundDomainMarkers; /** A map of lists of background markers (optional) for the domain axes. */ private Map backgroundDomainMarkers; /** A map of lists of foreground markers (optional) for the range axes. */ private Map foregroundRangeMarkers; /** A map of lists of background markers (optional) for the range axes. */ private Map backgroundRangeMarkers; /** * A (possibly empty) list of annotations for the plot. The list should be * initialised in the constructor and never allowed to be <code>null</code>. */ private List annotations; /** The paint used for the domain tick bands (if any). */ private transient Paint domainTickBandPaint; /** The paint used for the range tick bands (if any). */ private transient Paint rangeTickBandPaint; /** The fixed domain axis space. */ private AxisSpace fixedDomainAxisSpace; /** The fixed range axis space. */ private AxisSpace fixedRangeAxisSpace; /** * The order of the dataset rendering (REVERSE draws the primary dataset * last so that it appears to be on top). */ private DatasetRenderingOrder datasetRenderingOrder = DatasetRenderingOrder.REVERSE; /** * The order of the series rendering (REVERSE draws the primary series last * so that it appears to be on top). */ private SeriesRenderingOrder seriesRenderingOrder = SeriesRenderingOrder.REVERSE; /** * The weight for this plot (only relevant if this is a subplot in a * combined plot). */ private int weight; /** * An optional collection of legend items that can be returned by the * getLegendItems() method. */ private LegendItemCollection fixedLegendItems; /** * A flag that controls whether or not panning is enabled for the domain * axis/axes. * * @since 1.0.13 */ private boolean domainPannable; /** * A flag that controls whether or not panning is enabled for the range * axis/axes. * * @since 1.0.13 */ private boolean rangePannable; /** * Creates a new <code>XYPlot</code> instance with no dataset, no axes and * no renderer. You should specify these items before using the plot. */ public XYPlot() { this(null, null, null, null); } /** * Creates a new plot with the specified dataset, axes and renderer. Any of * the arguments can be <code>null</code>, but in that case you should take * care to specify the value before using the plot (otherwise a * <code>NullPointerException</code> may be thrown). * * @param dataset * the dataset (<code>null</code> permitted). * @param domainAxis * the domain axis (<code>null</code> permitted). * @param rangeAxis * the range axis (<code>null</code> permitted). * @param renderer * the renderer (<code>null</code> permitted). */ public XYPlot(XYDataset dataset, ValueAxis domainAxis, ValueAxis rangeAxis, XYItemRenderer renderer) { super(); this.orientation = PlotOrientation.VERTICAL; this.weight = 1; // only relevant when this is a subplot this.axisOffset = RectangleInsets.ZERO_INSETS; // allocate storage for datasets, axes and renderers (all optional) this.domainAxes = new ObjectList(); this.domainAxisLocations = new ObjectList(); this.foregroundDomainMarkers = new HashMap(); this.backgroundDomainMarkers = new HashMap(); this.rangeAxes = new ObjectList(); this.rangeAxisLocations = new ObjectList(); this.foregroundRangeMarkers = new HashMap(); this.backgroundRangeMarkers = new HashMap(); this.datasets = new ObjectList(); this.renderers = new ObjectList(); this.datasetToDomainAxesMap = new TreeMap(); this.datasetToRangeAxesMap = new TreeMap(); this.annotations = new java.util.ArrayList(); this.datasets.set(0, dataset); if (dataset != null) { // dataset.addChangeListener(this); } this.renderers.set(0, renderer); if (renderer != null) { renderer.setPlot(this); renderer.addChangeListener(this); } this.domainAxes.set(0, domainAxis); this.mapDatasetToDomainAxis(0, 0); if (domainAxis != null) { domainAxis.setPlot(this); // domainAxis.addChangeListener(this); } this.domainAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT); this.rangeAxes.set(0, rangeAxis); this.mapDatasetToRangeAxis(0, 0); if (rangeAxis != null) { rangeAxis.setPlot(this); // rangeAxis.addChangeListener(this); } this.rangeAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT); configureDomainAxes(); configureRangeAxes(); Paint white = new Paint(Paint.ANTI_ALIAS_FLAG); white.setColor(Color.WHITE); Paint black = new Paint(Paint.ANTI_ALIAS_FLAG); white.setColor(Color.BLACK); this.domainGridlinesVisible = true; this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE; this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT; this.domainMinorGridlinesVisible = false; this.domainMinorGridlineStroke = DEFAULT_GRIDLINE_STROKE; this.domainMinorGridlinePaint = white; this.domainZeroBaselineVisible = false; this.domainZeroBaselinePaint = black; this.domainZeroBaselineStroke = 1f; this.rangeGridlinesVisible = true; this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE; this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT; this.rangeMinorGridlinesVisible = false; this.rangeMinorGridlineStroke = DEFAULT_GRIDLINE_STROKE; this.rangeMinorGridlinePaint = white; this.rangeZeroBaselineVisible = false; this.rangeZeroBaselinePaint = black; this.rangeZeroBaselineStroke = 1f; this.domainCrosshairVisible = false; this.domainCrosshairValue = 0.0; this.domainCrosshairStroke = DEFAULT_CROSSHAIR_STROKE; this.domainCrosshairPaint = DEFAULT_CROSSHAIR_PAINT; this.rangeCrosshairVisible = false; this.rangeCrosshairValue = 0.0; this.rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE; this.rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT; } /** * Returns the plot type as a string. * * @return A short string describing the type of plot. */ public String getPlotType() { return "XY_Plot"; } /** * Returns the orientation of the plot. * * @return The orientation (never <code>null</code>). * * @see #setOrientation(PlotOrientation) */ public PlotOrientation getOrientation() { return this.orientation; } /** * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param orientation * the orientation (<code>null</code> not allowed). * * @see #getOrientation() */ public void setOrientation(PlotOrientation orientation) { if (orientation == null) { throw new IllegalArgumentException("Null 'orientation' argument."); } if (orientation != this.orientation) { this.orientation = orientation; // fireChangeEvent(); } } /** * Returns the axis offset. * * @return The axis offset (never <code>null</code>). * * @see #setAxisOffset(RectangleInsets) */ public RectangleInsets getAxisOffset() { return this.axisOffset; } /** * Sets the axis offsets (gap between the data area and the axes) and sends * a {@link PlotChangeEvent} to all registered listeners. * * @param offset * the offset (<code>null</code> not permitted). * * @see #getAxisOffset() */ public void setAxisOffset(RectangleInsets offset) { if (offset == null) { throw new IllegalArgumentException("Null 'offset' argument."); } this.axisOffset = offset; // fireChangeEvent(); } /** * Returns the domain axis with index 0. If the domain axis for this plot is * <code>null</code>, then the method will return the parent plot's domain * axis (if there is a parent plot). * * @return The domain axis (possibly <code>null</code>). * * @see #getDomainAxis(int) * @see #setDomainAxis(ValueAxis) */ public ValueAxis getDomainAxis() { return getDomainAxis(0); } /** * Returns the domain axis with the specified index, or <code>null</code>. * * @param index * the axis index. * * @return The axis (<code>null</code> possible). * * @see #setDomainAxis(int, ValueAxis) */ public ValueAxis getDomainAxis(int index) { ValueAxis result = null; if (index < this.domainAxes.size()) { result = (ValueAxis) this.domainAxes.get(index); } if (result == null) { Plot parent = getParent(); if (parent instanceof XYPlot) { XYPlot xy = (XYPlot) parent; result = xy.getDomainAxis(index); } } return result; } /** * Sets the domain axis for the plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param axis * the new axis (<code>null</code> permitted). * * @see #getDomainAxis() * @see #setDomainAxis(int, ValueAxis) */ public void setDomainAxis(ValueAxis axis) { setDomainAxis(0, axis); } /** * Sets a domain axis and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param index * the axis index. * @param axis * the axis (<code>null</code> permitted). * * @see #getDomainAxis(int) * @see #setRangeAxis(int, ValueAxis) */ public void setDomainAxis(int index, ValueAxis axis) { setDomainAxis(index, axis, true); } /** * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to * all registered listeners. * * @param index * the axis index. * @param axis * the axis. * @param notify * notify listeners? * * @see #getDomainAxis(int) */ public void setDomainAxis(int index, ValueAxis axis, boolean notify) { ValueAxis existing = getDomainAxis(index); if (existing != null) { // existing.removeChangeListener(this); } if (axis != null) { axis.setPlot(this); } this.domainAxes.set(index, axis); if (axis != null) { axis.configure(); // axis.addChangeListener(this); } if (notify) { // fireChangeEvent(); } } /** * Sets the domain axes for this plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param axes * the axes (<code>null</code> not permitted). * * @see #setRangeAxes(ValueAxis[]) */ public void setDomainAxes(ValueAxis[] axes) { for (int i = 0; i < axes.length; i++) { setDomainAxis(i, axes[i], false); } // fireChangeEvent(); } /** * Returns the location of the primary domain axis. * * @return The location (never <code>null</code>). * * @see #setDomainAxisLocation(AxisLocation) */ public AxisLocation getDomainAxisLocation() { return (AxisLocation) this.domainAxisLocations.get(0); } /** * Sets the location of the primary domain axis and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param location * the location (<code>null</code> not permitted). * * @see #getDomainAxisLocation() */ public void setDomainAxisLocation(AxisLocation location) { // delegate... setDomainAxisLocation(0, location, true); } /** * Sets the location of the domain axis and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param location * the location (<code>null</code> not permitted). * @param notify * notify listeners? * * @see #getDomainAxisLocation() */ public void setDomainAxisLocation(AxisLocation location, boolean notify) { // delegate... setDomainAxisLocation(0, location, notify); } /** * Returns the edge for the primary domain axis (taking into account the * plot's orientation). * * @return The edge. * * @see #getDomainAxisLocation() * @see #getOrientation() */ public RectangleEdge getDomainAxisEdge() { return Plot.resolveDomainAxisLocation(getDomainAxisLocation(), this.orientation); } /** * Returns the number of domain axes. * * @return The axis count. * * @see #getRangeAxisCount() */ public int getDomainAxisCount() { return this.domainAxes.size(); } /** * Clears the domain axes from the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @see #clearRangeAxes() */ public void clearDomainAxes() { for (int i = 0; i < this.domainAxes.size(); i++) { ValueAxis axis = (ValueAxis) this.domainAxes.get(i); if (axis != null) { // axis.removeChangeListener(this); } } this.domainAxes.clear(); // fireChangeEvent(); } /** * Configures the domain axes. */ public void configureDomainAxes() { for (int i = 0; i < this.domainAxes.size(); i++) { ValueAxis axis = (ValueAxis) this.domainAxes.get(i); if (axis != null) { axis.configure(); } } } /** * Returns the location for a domain axis. If this hasn't been set * explicitly, the method returns the location that is opposite to the * primary domain axis location. * * @param index * the axis index. * * @return The location (never <code>null</code>). * * @see #setDomainAxisLocation(int, AxisLocation) */ public AxisLocation getDomainAxisLocation(int index) { AxisLocation result = null; if (index < this.domainAxisLocations.size()) { result = (AxisLocation) this.domainAxisLocations.get(index); } if (result == null) { result = AxisLocation.getOpposite(getDomainAxisLocation()); } return result; } /** * Sets the location for a domain axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param index * the axis index. * @param location * the location (<code>null</code> not permitted for index 0). * * @see #getDomainAxisLocation(int) */ public void setDomainAxisLocation(int index, AxisLocation location) { // delegate... setDomainAxisLocation(index, location, true); } /** * Sets the axis location for a domain axis and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index * the axis index. * @param location * the location (<code>null</code> not permitted for index 0). * @param notify * notify listeners? * * @since 1.0.5 * * @see #getDomainAxisLocation(int) * @see #setRangeAxisLocation(int, AxisLocation, boolean) */ public void setDomainAxisLocation(int index, AxisLocation location, boolean notify) { if (index == 0 && location == null) { throw new IllegalArgumentException("Null 'location' for index 0 not permitted."); } this.domainAxisLocations.set(index, location); if (notify) { // fireChangeEvent(); } } /** * Returns the edge for a domain axis. * * @param index * the axis index. * * @return The edge. * * @see #getRangeAxisEdge(int) */ public RectangleEdge getDomainAxisEdge(int index) { AxisLocation location = getDomainAxisLocation(index); RectangleEdge result = Plot.resolveDomainAxisLocation(location, this.orientation); if (result == null) { result = RectangleEdge.opposite(getDomainAxisEdge()); } return result; } /** * Returns the range axis for the plot. If the range axis for this plot is * <code>null</code>, then the method will return the parent plot's range * axis (if there is a parent plot). * * @return The range axis. * * @see #getRangeAxis(int) * @see #setRangeAxis(ValueAxis) */ public ValueAxis getRangeAxis() { return getRangeAxis(0); } /** * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param axis * the axis (<code>null</code> permitted). * * @see #getRangeAxis() * @see #setRangeAxis(int, ValueAxis) */ public void setRangeAxis(ValueAxis axis) { if (axis != null) { axis.setPlot(this); } // plot is likely registered as a listener with the existing axis... ValueAxis existing = getRangeAxis(); if (existing != null) { // existing.removeChangeListener(this); } this.rangeAxes.set(0, axis); if (axis != null) { axis.configure(); // axis.addChangeListener(this); } // fireChangeEvent(); } /** * Returns the location of the primary range axis. * * @return The location (never <code>null</code>). * * @see #setRangeAxisLocation(AxisLocation) */ public AxisLocation getRangeAxisLocation() { return (AxisLocation) this.rangeAxisLocations.get(0); } /** * Sets the location of the primary range axis and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param location * the location (<code>null</code> not permitted). * * @see #getRangeAxisLocation() */ public void setRangeAxisLocation(AxisLocation location) { // delegate... setRangeAxisLocation(0, location, true); } /** * Sets the location of the primary range axis and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param location * the location (<code>null</code> not permitted). * @param notify * notify listeners? * * @see #getRangeAxisLocation() */ public void setRangeAxisLocation(AxisLocation location, boolean notify) { // delegate... setRangeAxisLocation(0, location, notify); } /** * Returns the edge for the primary range axis. * * @return The range axis edge. * * @see #getRangeAxisLocation() * @see #getOrientation() */ public RectangleEdge getRangeAxisEdge() { return Plot.resolveRangeAxisLocation(getRangeAxisLocation(), this.orientation); } /** * Returns a range axis. * * @param index * the axis index. * * @return The axis (<code>null</code> possible). * * @see #setRangeAxis(int, ValueAxis) */ public ValueAxis getRangeAxis(int index) { ValueAxis result = null; if (index < this.rangeAxes.size()) { result = (ValueAxis) this.rangeAxes.get(index); } if (result == null) { Plot parent = getParent(); if (parent instanceof XYPlot) { XYPlot xy = (XYPlot) parent; result = xy.getRangeAxis(index); } } return result; } /** * Sets a range axis and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param index * the axis index. * @param axis * the axis (<code>null</code> permitted). * * @see #getRangeAxis(int) */ public void setRangeAxis(int index, ValueAxis axis) { setRangeAxis(index, axis, true); } /** * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to * all registered listeners. * * @param index * the axis index. * @param axis * the axis (<code>null</code> permitted). * @param notify * notify listeners? * * @see #getRangeAxis(int) */ public void setRangeAxis(int index, ValueAxis axis, boolean notify) { ValueAxis existing = getRangeAxis(index); if (existing != null) { // existing.removeChangeListener(this); } if (axis != null) { axis.setPlot(this); } this.rangeAxes.set(index, axis); if (axis != null) { axis.configure(); // axis.addChangeListener(this); } if (notify) { // fireChangeEvent(); } } /** * Sets the range axes for this plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param axes * the axes (<code>null</code> not permitted). * * @see #setDomainAxes(ValueAxis[]) */ public void setRangeAxes(ValueAxis[] axes) { for (int i = 0; i < axes.length; i++) { setRangeAxis(i, axes[i], false); } // fireChangeEvent(); } /** * Returns the number of range axes. * * @return The axis count. * * @see #getDomainAxisCount() */ public int getRangeAxisCount() { return this.rangeAxes.size(); } /** * Clears the range axes from the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @see #clearDomainAxes() */ public void clearRangeAxes() { for (int i = 0; i < this.rangeAxes.size(); i++) { ValueAxis axis = (ValueAxis) this.rangeAxes.get(i); if (axis != null) { // axis.removeChangeListener(this); } } this.rangeAxes.clear(); // fireChangeEvent(); } /** * Configures the range axes. * * @see #configureDomainAxes() */ public void configureRangeAxes() { for (int i = 0; i < this.rangeAxes.size(); i++) { ValueAxis axis = (ValueAxis) this.rangeAxes.get(i); if (axis != null) { axis.configure(); } } } /** * Returns the location for a range axis. If this hasn't been set * explicitly, the method returns the location that is opposite to the * primary range axis location. * * @param index * the axis index. * * @return The location (never <code>null</code>). * * @see #setRangeAxisLocation(int, AxisLocation) */ public AxisLocation getRangeAxisLocation(int index) { AxisLocation result = null; if (index < this.rangeAxisLocations.size()) { result = (AxisLocation) this.rangeAxisLocations.get(index); } if (result == null) { result = AxisLocation.getOpposite(getRangeAxisLocation()); } return result; } /** * Sets the location for a range axis and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param index * the axis index. * @param location * the location (<code>null</code> permitted). * * @see #getRangeAxisLocation(int) */ public void setRangeAxisLocation(int index, AxisLocation location) { // delegate... setRangeAxisLocation(index, location, true); } /** * Sets the axis location for a domain axis and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index * the axis index. * @param location * the location (<code>null</code> not permitted for index 0). * @param notify * notify listeners? * * @since 1.0.5 * * @see #getRangeAxisLocation(int) * @see #setDomainAxisLocation(int, AxisLocation, boolean) */ public void setRangeAxisLocation(int index, AxisLocation location, boolean notify) { if (index == 0 && location == null) { throw new IllegalArgumentException("Null 'location' for index 0 not permitted."); } this.rangeAxisLocations.set(index, location); if (notify) { // fireChangeEvent(); } } /** * Returns the edge for a range axis. * * @param index * the axis index. * * @return The edge. * * @see #getRangeAxisLocation(int) * @see #getOrientation() */ public RectangleEdge getRangeAxisEdge(int index) { AxisLocation location = getRangeAxisLocation(index); RectangleEdge result = Plot.resolveRangeAxisLocation(location, this.orientation); if (result == null) { result = RectangleEdge.opposite(getRangeAxisEdge()); } return result; } /** * Returns the primary dataset for the plot. * * @return The primary dataset (possibly <code>null</code>). * * @see #getDataset(int) * @see #setDataset(XYDataset) */ public XYDataset getDataset() { return getDataset(0); } /** * Returns a dataset. * * @param index * the dataset index. * * @return The dataset (possibly <code>null</code>). * * @see #setDataset(int, XYDataset) */ public XYDataset getDataset(int index) { XYDataset result = null; if (this.datasets.size() > index) { result = (XYDataset) this.datasets.get(index); } return result; } /** * Sets the primary dataset for the plot, replacing the existing dataset if * there is one. * * @param dataset * the dataset (<code>null</code> permitted). * * @see #getDataset() * @see #setDataset(int, XYDataset) */ public void setDataset(XYDataset dataset) { setDataset(0, dataset); } /** * Sets a dataset for the plot. * * @param index * the dataset index. * @param dataset * the dataset (<code>null</code> permitted). * * @see #getDataset(int) */ public void setDataset(int index, XYDataset dataset) { XYDataset existing = getDataset(index); if (existing != null) { // existing.removeChangeListener(this); } this.datasets.set(index, dataset); if (dataset != null) { // dataset.addChangeListener(this); } // send a dataset change event to self... // DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); // datasetChanged(event); } /** * Returns the number of datasets. * * @return The number of datasets. */ public int getDatasetCount() { return this.datasets.size(); } /** * Returns the index of the specified dataset, or <code>-1</code> if the * dataset does not belong to the plot. * * @param dataset * the dataset (<code>null</code> not permitted). * * @return The index. */ public int indexOf(XYDataset dataset) { int result = -1; for (int i = 0; i < this.datasets.size(); i++) { if (dataset == this.datasets.get(i)) { result = i; break; } } return result; } /** * Maps a dataset to a particular domain axis. All data will be plotted * against axis zero by default, no mapping is required for this case. * * @param index * the dataset index (zero-based). * @param axisIndex * the axis index. * * @see #mapDatasetToRangeAxis(int, int) */ public void mapDatasetToDomainAxis(int index, int axisIndex) { List axisIndices = new java.util.ArrayList(1); axisIndices.add(new Integer(axisIndex)); mapDatasetToDomainAxes(index, axisIndices); } /** * Maps the specified dataset to the axes in the list. Note that the * conversion of data values into Java2D space is always performed using the * first axis in the list. * * @param index * the dataset index (zero-based). * @param axisIndices * the axis indices (<code>null</code> permitted). * * @since 1.0.12 */ public void mapDatasetToDomainAxes(int index, List axisIndices) { if (index < 0) { throw new IllegalArgumentException("Requires 'index' >= 0."); } checkAxisIndices(axisIndices); Integer key = new Integer(index); this.datasetToDomainAxesMap.put(key, new ArrayList(axisIndices)); // fake a dataset change event to update axes... // datasetChanged(new DatasetChangeEvent(this, getDataset(index))); } /** * Maps a dataset to a particular range axis. All data will be plotted * against axis zero by default, no mapping is required for this case. * * @param index * the dataset index (zero-based). * @param axisIndex * the axis index. * * @see #mapDatasetToDomainAxis(int, int) */ public void mapDatasetToRangeAxis(int index, int axisIndex) { List axisIndices = new java.util.ArrayList(1); axisIndices.add(new Integer(axisIndex)); mapDatasetToRangeAxes(index, axisIndices); } /** * Maps the specified dataset to the axes in the list. Note that the * conversion of data values into Java2D space is always performed using the * first axis in the list. * * @param index * the dataset index (zero-based). * @param axisIndices * the axis indices (<code>null</code> permitted). * * @since 1.0.12 */ public void mapDatasetToRangeAxes(int index, List axisIndices) { if (index < 0) { throw new IllegalArgumentException("Requires 'index' >= 0."); } checkAxisIndices(axisIndices); Integer key = new Integer(index); this.datasetToRangeAxesMap.put(key, new ArrayList(axisIndices)); // fake a dataset change event to update axes... // datasetChanged(new DatasetChangeEvent(this, getDataset(index))); } /** * This method is used to perform argument checking on the list of axis * indices passed to mapDatasetToDomainAxes() and mapDatasetToRangeAxes(). * * @param indices * the list of indices (<code>null</code> permitted). */ private void checkAxisIndices(List indices) { // axisIndices can be: // 1. null; // 2. non-empty, containing only Integer objects that are unique. if (indices == null) { return; // OK } int count = indices.size(); if (count == 0) { throw new IllegalArgumentException("Empty list not permitted."); } HashSet set = new HashSet(); for (int i = 0; i < count; i++) { Object item = indices.get(i); if (!(item instanceof Integer)) { throw new IllegalArgumentException("Indices must be Integer instances."); } if (set.contains(item)) { throw new IllegalArgumentException("Indices must be unique."); } set.add(item); } } /** * Returns the number of renderer slots for this plot. * * @return The number of renderer slots. * * @since 1.0.11 */ public int getRendererCount() { return this.renderers.size(); } /** * Returns the renderer for the primary dataset. * * @return The item renderer (possibly <code>null</code>). * * @see #setRenderer(XYItemRenderer) */ public XYItemRenderer getRenderer() { return getRenderer(0); } /** * Returns the renderer for a dataset, or <code>null</code>. * * @param index * the renderer index. * * @return The renderer (possibly <code>null</code>). * * @see #setRenderer(int, XYItemRenderer) */ public XYItemRenderer getRenderer(int index) { XYItemRenderer result = null; if (this.renderers.size() > index) { result = (XYItemRenderer) this.renderers.get(index); } return result; } /** * Sets the renderer for the primary dataset and sends a * {@link PlotChangeEvent} to all registered listeners. If the renderer is * set to <code>null</code>, no data will be displayed. * * @param renderer * the renderer (<code>null</code> permitted). * * @see #getRenderer() */ public void setRenderer(XYItemRenderer renderer) { setRenderer(0, renderer); } /** * Sets a renderer and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param index * the index. * @param renderer * the renderer. * * @see #getRenderer(int) */ public void setRenderer(int index, XYItemRenderer renderer) { setRenderer(index, renderer, true); } /** * Sets a renderer and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param index * the index. * @param renderer * the renderer. * @param notify * notify listeners? * * @see #getRenderer(int) */ public void setRenderer(int index, XYItemRenderer renderer, boolean notify) { XYItemRenderer existing = getRenderer(index); if (existing != null) { existing.removeChangeListener(this); } this.renderers.set(index, renderer); if (renderer != null) { renderer.setPlot(this); renderer.addChangeListener(this); } configureDomainAxes(); configureRangeAxes(); if (notify) { // fireChangeEvent(); } } /** * Sets the renderers for this plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param renderers * the renderers (<code>null</code> not permitted). */ public void setRenderers(XYItemRenderer[] renderers) { for (int i = 0; i < renderers.length; i++) { setRenderer(i, renderers[i], false); } // fireChangeEvent(); } /** * Returns the dataset rendering order. * * @return The order (never <code>null</code>). * * @see #setDatasetRenderingOrder(DatasetRenderingOrder) */ public DatasetRenderingOrder getDatasetRenderingOrder() { return this.datasetRenderingOrder; } /** * Sets the rendering order and sends a {@link PlotChangeEvent} to all * registered listeners. By default, the plot renders the primary dataset * last (so that the primary dataset overlays the secondary datasets). You * can reverse this if you want to. * * @param order * the rendering order (<code>null</code> not permitted). * * @see #getDatasetRenderingOrder() */ public void setDatasetRenderingOrder(DatasetRenderingOrder order) { if (order == null) { throw new IllegalArgumentException("Null 'order' argument."); } this.datasetRenderingOrder = order; // fireChangeEvent(); } /** * Returns the series rendering order. * * @return the order (never <code>null</code>). * * @see #setSeriesRenderingOrder(SeriesRenderingOrder) */ public SeriesRenderingOrder getSeriesRenderingOrder() { return this.seriesRenderingOrder; } /** * Sets the series order and sends a {@link PlotChangeEvent} to all * registered listeners. By default, the plot renders the primary series * last (so that the primary series appears to be on top). You can reverse * this if you want to. * * @param order * the rendering order (<code>null</code> not permitted). * * @see #getSeriesRenderingOrder() */ public void setSeriesRenderingOrder(SeriesRenderingOrder order) { if (order == null) { throw new IllegalArgumentException("Null 'order' argument."); } this.seriesRenderingOrder = order; // fireChangeEvent(); } /** * Returns the index of the specified renderer, or <code>-1</code> if the * renderer is not assigned to this plot. * * @param renderer * the renderer (<code>null</code> permitted). * * @return The renderer index. */ public int getIndexOf(XYItemRenderer renderer) { return this.renderers.indexOf(renderer); } /** * Returns the renderer for the specified dataset. The code first determines * the index of the dataset, then checks if there is a renderer with the * same index (if not, the method returns renderer(0). * * @param dataset * the dataset (<code>null</code> permitted). * * @return The renderer (possibly <code>null</code>). */ public XYItemRenderer getRendererForDataset(XYDataset dataset) { XYItemRenderer result = null; for (int i = 0; i < this.datasets.size(); i++) { if (this.datasets.get(i) == dataset) { result = (XYItemRenderer) this.renderers.get(i); if (result == null) { result = getRenderer(); } break; } } return result; } /** * Returns the weight for this plot when it is used as a subplot within a * combined plot. * * @return The weight. * * @see #setWeight(int) */ public int getWeight() { return this.weight; } /** * Sets the weight for the plot and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param weight * the weight. * * @see #getWeight() */ public void setWeight(int weight) { this.weight = weight; // fireChangeEvent(); } /** * Returns <code>true</code> if the domain gridlines are visible, and * <code>false<code> otherwise. * * @return <code>true</code> or <code>false</code>. * * @see #setDomainGridlinesVisible(boolean) */ public boolean isDomainGridlinesVisible() { return this.domainGridlinesVisible; } /** * Sets the flag that controls whether or not the domain grid-lines are * visible. * <p> * If the flag value is changed, a {@link PlotChangeEvent} is sent to all * registered listeners. * * @param visible * the new value of the flag. * * @see #isDomainGridlinesVisible() */ public void setDomainGridlinesVisible(boolean visible) { if (this.domainGridlinesVisible != visible) { this.domainGridlinesVisible = visible; // fireChangeEvent(); } } /** * Returns <code>true</code> if the domain minor gridlines are visible, and * <code>false<code> otherwise. * * @return <code>true</code> or <code>false</code>. * * @see #setDomainMinorGridlinesVisible(boolean) * * @since 1.0.12 */ public boolean isDomainMinorGridlinesVisible() { return this.domainMinorGridlinesVisible; } /** * Sets the flag that controls whether or not the domain minor grid-lines * are visible. * <p> * If the flag value is changed, a {@link PlotChangeEvent} is sent to all * registered listeners. * * @param visible * the new value of the flag. * * @see #isDomainMinorGridlinesVisible() * * @since 1.0.12 */ public void setDomainMinorGridlinesVisible(boolean visible) { if (this.domainMinorGridlinesVisible != visible) { this.domainMinorGridlinesVisible = visible; // fireChangeEvent(); } } /** * Returns the stroke for the grid-lines (if any) plotted against the domain * axis. * * @return The stroke (never <code>null</code>). * * @see #setDomainGridlineStroke(Stroke) */ public Float getDomainGridlineStroke() { return this.domainGridlineStroke; } /** * Sets the stroke for the grid lines plotted against the domain axis, and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke * the stroke (<code>null</code> not permitted). * * @throws IllegalArgumentException * if <code>stroke</code> is <code>null</code>. * * @see #getDomainGridlineStroke() */ public void setDomainGridlineStroke(Float stroke) { if (stroke == null) { throw new IllegalArgumentException("Null 'stroke' argument."); } this.domainGridlineStroke = stroke; // fireChangeEvent(); } /** * Returns the stroke for the minor grid-lines (if any) plotted against the * domain axis. * * @return The stroke (never <code>null</code>). * * @see #setDomainMinorGridlineStroke(Stroke) * * @since 1.0.12 */ public Float getDomainMinorGridlineStroke() { return this.domainMinorGridlineStroke; } /** * Sets the stroke for the minor grid lines plotted against the domain axis, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke * the stroke (<code>null</code> not permitted). * * @throws IllegalArgumentException * if <code>stroke</code> is <code>null</code>. * * @see #getDomainMinorGridlineStroke() * * @since 1.0.12 */ public void setDomainMinorGridlineStroke(Float stroke) { if (stroke == null) { throw new IllegalArgumentException("Null 'stroke' argument."); } this.domainMinorGridlineStroke = stroke; // fireChangeEvent(); } /** * Returns the paint for the grid lines (if any) plotted against the domain * axis. * * @return The paint (never <code>null</code>). * * @see #setDomainGridlinePaint(Paint) */ public Paint getDomainGridlinePaint() { return this.domainGridlinePaint; } /** * Sets the paint for the grid lines plotted against the domain axis, and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint * the paint (<code>null</code> not permitted). * * @throws IllegalArgumentException * if <code>paint</code> is <code>null</code>. * * @see #getDomainGridlinePaint() */ public void setDomainGridlinePaint(Paint paint) { if (paint == null) { throw new IllegalArgumentException("Null 'paint' argument."); } this.domainGridlinePaint = paint; // fireChangeEvent(); } /** * Returns the paint for the minor grid lines (if any) plotted against the * domain axis. * * @return The paint (never <code>null</code>). * * @see #setDomainMinorGridlinePaint(Paint) * * @since 1.0.12 */ public Paint getDomainMinorGridlinePaint() { return this.domainMinorGridlinePaint; } /** * Sets the paint for the minor grid lines plotted against the domain axis, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint * the paint (<code>null</code> not permitted). * * @throws IllegalArgumentException * if <code>paint</code> is <code>null</code>. * * @see #getDomainMinorGridlinePaint() * * @since 1.0.12 */ public void setDomainMinorGridlinePaint(Paint paint) { if (paint == null) { throw new IllegalArgumentException("Null 'paint' argument."); } this.domainMinorGridlinePaint = paint; // fireChangeEvent(); } /** * Returns <code>true</code> if the range axis grid is visible, and * <code>false<code> otherwise. * * @return A boolean. * * @see #setRangeGridlinesVisible(boolean) */ public boolean isRangeGridlinesVisible() { return this.rangeGridlinesVisible; } /** * Sets the flag that controls whether or not the range axis grid lines are * visible. * <p> * If the flag value is changed, a {@link PlotChangeEvent} is sent to all * registered listeners. * * @param visible * the new value of the flag. * * @see #isRangeGridlinesVisible() */ public void setRangeGridlinesVisible(boolean visible) { if (this.rangeGridlinesVisible != visible) { this.rangeGridlinesVisible = visible; // fireChangeEvent(); } } /** * Returns the stroke for the grid lines (if any) plotted against the range * axis. * * @return The stroke (never <code>null</code>). * * @see #setRangeGridlineStroke(Stroke) */ public Float getRangeGridlineStroke() { return this.rangeGridlineStroke; } /** * Sets the stroke for the grid lines plotted against the range axis, and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke * the stroke (<code>null</code> not permitted). * * @see #getRangeGridlineStroke() */ public void setRangeGridlineStroke(Float stroke) { if (stroke == null) { throw new IllegalArgumentException("Null 'stroke' argument."); } this.rangeGridlineStroke = stroke; // fireChangeEvent(); } /** * Returns the paint for the grid lines (if any) plotted against the range * axis. * * @return The paint (never <code>null</code>). * * @see #setRangeGridlinePaint(Paint) */ public Paint getRangeGridlinePaint() { return this.rangeGridlinePaint; } /** * Sets the paint for the grid lines plotted against the range axis and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint * the paint (<code>null</code> not permitted). * * @see #getRangeGridlinePaint() */ public void setRangeGridlinePaint(Paint paint) { if (paint == null) { throw new IllegalArgumentException("Null 'paint' argument."); } this.rangeGridlinePaint = paint; // fireChangeEvent(); } /** * Returns <code>true</code> if the range axis minor grid is visible, and * <code>false<code> otherwise. * * @return A boolean. * * @see #setRangeMinorGridlinesVisible(boolean) * * @since 1.0.12 */ public boolean isRangeMinorGridlinesVisible() { return this.rangeMinorGridlinesVisible; } /** * Sets the flag that controls whether or not the range axis minor grid * lines are visible. * <p> * If the flag value is changed, a {@link PlotChangeEvent} is sent to all * registered listeners. * * @param visible * the new value of the flag. * * @see #isRangeMinorGridlinesVisible() * * @since 1.0.12 */ public void setRangeMinorGridlinesVisible(boolean visible) { if (this.rangeMinorGridlinesVisible != visible) { this.rangeMinorGridlinesVisible = visible; // fireChangeEvent(); } } /** * Returns the stroke for the minor grid lines (if any) plotted against the * range axis. * * @return The stroke (never <code>null</code>). * * @see #setRangeMinorGridlineStroke(Stroke) * * @since 1.0.12 */ public Float getRangeMinorGridlineStroke() { return this.rangeMinorGridlineStroke; } /** * Sets the stroke for the minor grid lines plotted against the range axis, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke * the stroke (<code>null</code> not permitted). * * @see #getRangeMinorGridlineStroke() * * @since 1.0.12 */ public void setRangeMinorGridlineStroke(Float stroke) { if (stroke == null) { throw new IllegalArgumentException("Null 'stroke' argument."); } this.rangeMinorGridlineStroke = stroke; // fireChangeEvent(); } /** * Returns the paint for the minor grid lines (if any) plotted against the * range axis. * * @return The paint (never <code>null</code>). * * @see #setRangeMinorGridlinePaint(Paint) * * @since 1.0.12 */ public Paint getRangeMinorGridlinePaint() { return this.rangeMinorGridlinePaint; } /** * Sets the paint for the minor grid lines plotted against the range axis * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint * the paint (<code>null</code> not permitted). * * @see #getRangeMinorGridlinePaint() * * @since 1.0.12 */ public void setRangeMinorGridlinePaint(Paint paint) { if (paint == null) { throw new IllegalArgumentException("Null 'paint' argument."); } this.rangeMinorGridlinePaint = paint; // fireChangeEvent(); } /** * Returns a flag that controls whether or not a zero baseline is displayed * for the domain axis. * * @return A boolean. * * @since 1.0.5 * * @see #setDomainZeroBaselineVisible(boolean) */ public boolean isDomainZeroBaselineVisible() { return this.domainZeroBaselineVisible; } /** * Sets the flag that controls whether or not the zero baseline is displayed * for the domain axis, and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param visible * the flag. * * @since 1.0.5 * * @see #isDomainZeroBaselineVisible() */ public void setDomainZeroBaselineVisible(boolean visible) { this.domainZeroBaselineVisible = visible; // fireChangeEvent(); } /** * Returns the stroke used for the zero baseline against the domain axis. * * @return The stroke (never <code>null</code>). * * @since 1.0.5 * * @see #setDomainZeroBaselineStroke(Stroke) */ public Float getDomainZeroBaselineStroke() { return this.domainZeroBaselineStroke; } /** * Sets the stroke for the zero baseline for the domain axis, and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param stroke * the stroke (<code>null</code> not permitted). * * @since 1.0.5 * * @see #getRangeZeroBaselineStroke() */ public void setDomainZeroBaselineStroke(Float stroke) { if (stroke == null) { throw new IllegalArgumentException("Null 'stroke' argument."); } this.domainZeroBaselineStroke = stroke; // fireChangeEvent(); } /** * Returns the paint for the zero baseline (if any) plotted against the * domain axis. * * @since 1.0.5 * * @return The paint (never <code>null</code>). * * @see #setDomainZeroBaselinePaint(Paint) */ public Paint getDomainZeroBaselinePaint() { return this.domainZeroBaselinePaint; } /** * Sets the paint for the zero baseline plotted against the domain axis and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint * the paint (<code>null</code> not permitted). * * @since 1.0.5 * * @see #getDomainZeroBaselinePaint() */ public void setDomainZeroBaselinePaint(Paint paint) { if (paint == null) { throw new IllegalArgumentException("Null 'paint' argument."); } this.domainZeroBaselinePaint = paint; // fireChangeEvent(); } /** * Returns a flag that controls whether or not a zero baseline is displayed * for the range axis. * * @return A boolean. * * @see #setRangeZeroBaselineVisible(boolean) */ public boolean isRangeZeroBaselineVisible() { return this.rangeZeroBaselineVisible; } /** * Sets the flag that controls whether or not the zero baseline is displayed * for the range axis, and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param visible * the flag. * * @see #isRangeZeroBaselineVisible() */ public void setRangeZeroBaselineVisible(boolean visible) { this.rangeZeroBaselineVisible = visible; // fireChangeEvent(); } /** * Returns the stroke used for the zero baseline against the range axis. * * @return The stroke (never <code>null</code>). * * @see #setRangeZeroBaselineStroke(Stroke) */ public Float getRangeZeroBaselineStroke() { return this.rangeZeroBaselineStroke; } /** * Sets the stroke for the zero baseline for the range axis, and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param stroke * the stroke (<code>null</code> not permitted). * * @see #getRangeZeroBaselineStroke() */ public void setRangeZeroBaselineStroke(Float stroke) { if (stroke == null) { throw new IllegalArgumentException("Null 'stroke' argument."); } this.rangeZeroBaselineStroke = stroke; // fireChangeEvent(); } /** * Returns the paint for the zero baseline (if any) plotted against the * range axis. * * @return The paint (never <code>null</code>). * * @see #setRangeZeroBaselinePaint(Paint) */ public Paint getRangeZeroBaselinePaint() { return this.rangeZeroBaselinePaint; } /** * Sets the paint for the zero baseline plotted against the range axis and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint * the paint (<code>null</code> not permitted). * * @see #getRangeZeroBaselinePaint() */ public void setRangeZeroBaselinePaint(Paint paint) { if (paint == null) { throw new IllegalArgumentException("Null 'paint' argument."); } this.rangeZeroBaselinePaint = paint; // fireChangeEvent(); } /** * Returns the paint used for the domain tick bands. If this is * <code>null</code>, no tick bands will be drawn. * * @return The paint (possibly <code>null</code>). * * @see #setDomainTickBandPaint(Paint) */ public Paint getDomainTickBandPaint() { return this.domainTickBandPaint; } /** * Sets the paint for the domain tick bands. * * @param paint * the paint (<code>null</code> permitted). * * @see #getDomainTickBandPaint() */ public void setDomainTickBandPaint(Paint paint) { this.domainTickBandPaint = paint; // fireChangeEvent(); } /** * Returns the paint used for the range tick bands. If this is * <code>null</code>, no tick bands will be drawn. * * @return The paint (possibly <code>null</code>). * * @see #setRangeTickBandPaint(Paint) */ public Paint getRangeTickBandPaint() { return this.rangeTickBandPaint; } /** * Sets the paint for the range tick bands. * * @param paint * the paint (<code>null</code> permitted). * * @see #getRangeTickBandPaint() */ public void setRangeTickBandPaint(Paint paint) { this.rangeTickBandPaint = paint; // fireChangeEvent(); } /** * Returns the origin for the quadrants that can be displayed on the plot. * This defaults to (0, 0). * * @return The origin point (never <code>null</code>). * * @see #setQuadrantOrigin(Point2D) */ public Point2D getQuadrantOrigin() { return this.quadrantOrigin; } /** * Sets the quadrant origin and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param origin * the origin (<code>null</code> not permitted). * * @see #getQuadrantOrigin() */ public void setQuadrantOrigin(Point2D origin) { if (origin == null) { throw new IllegalArgumentException("Null 'origin' argument."); } this.quadrantOrigin = origin; // fireChangeEvent(); } /** * Returns the paint used for the specified quadrant. * * @param index * the quadrant index (0-3). * * @return The paint (possibly <code>null</code>). * * @see #setQuadrantPaint(int, Paint) */ public Paint getQuadrantPaint(int index) { if (index < 0 || index > 3) { throw new IllegalArgumentException("The index value (" + index + ") should be in the range 0 to 3."); } return this.quadrantPaint[index]; } /** * Sets the paint used for the specified quadrant and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index * the quadrant index (0-3). * @param paint * the paint (<code>null</code> permitted). * * @see #getQuadrantPaint(int) */ public void setQuadrantPaint(int index, Paint paint) { if (index < 0 || index > 3) { throw new IllegalArgumentException("The index value (" + index + ") should be in the range 0 to 3."); } this.quadrantPaint[index] = paint; // fireChangeEvent(); } /** * Adds a marker for the domain axis and sends a {@link PlotChangeEvent} to * all registered listeners. * <P> * Typically a marker will be drawn by the renderer as a line perpendicular * to the range axis, however this is entirely up to the renderer. * * @param marker * the marker (<code>null</code> not permitted). * * @see #addDomainMarker(Marker, Layer) * @see #clearDomainMarkers() */ public void addDomainMarker(Marker marker) { // defer argument checking... addDomainMarker(marker, Layer.FOREGROUND); } /** * Adds a marker for the domain axis in the specified layer and sends a * {@link PlotChangeEvent} to all registered listeners. * <P> * Typically a marker will be drawn by the renderer as a line perpendicular * to the range axis, however this is entirely up to the renderer. * * @param marker * the marker (<code>null</code> not permitted). * @param layer * the layer (foreground or background). * * @see #addDomainMarker(int, Marker, Layer) */ public void addDomainMarker(Marker marker, Layer layer) { addDomainMarker(0, marker, layer); } /** * Clears all the (foreground and background) domain markers and sends a * {@link PlotChangeEvent} to all registered listeners. * * @see #addDomainMarker(int, Marker, Layer) */ public void clearDomainMarkers() { if (this.backgroundDomainMarkers != null) { Set keys = this.backgroundDomainMarkers.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Integer key = (Integer) iterator.next(); clearDomainMarkers(key.intValue()); } this.backgroundDomainMarkers.clear(); } if (this.foregroundDomainMarkers != null) { Set keys = this.foregroundDomainMarkers.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Integer key = (Integer) iterator.next(); clearDomainMarkers(key.intValue()); } this.foregroundDomainMarkers.clear(); } // fireChangeEvent(); } /** * Clears the (foreground and background) domain markers for a particular * renderer. * * @param index * the renderer index. * * @see #clearRangeMarkers(int) */ public void clearDomainMarkers(int index) { Integer key = new Integer(index); if (this.backgroundDomainMarkers != null) { Collection markers = (Collection) this.backgroundDomainMarkers.get(key); if (markers != null) { Iterator iterator = markers.iterator(); while (iterator.hasNext()) { Marker m = (Marker) iterator.next(); // m.removeChangeListener(this); } markers.clear(); } } if (this.foregroundRangeMarkers != null) { Collection markers = (Collection) this.foregroundDomainMarkers.get(key); if (markers != null) { Iterator iterator = markers.iterator(); while (iterator.hasNext()) { Marker m = (Marker) iterator.next(); // m.removeChangeListener(this); } markers.clear(); } } // fireChangeEvent(); } /** * Adds a marker for a specific dataset/renderer and sends a * {@link PlotChangeEvent} to all registered listeners. * <P> * Typically a marker will be drawn by the renderer as a line perpendicular * to the domain axis (that the renderer is mapped to), however this is * entirely up to the renderer. * * @param index * the dataset/renderer index. * @param marker * the marker. * @param layer * the layer (foreground or background). * * @see #clearDomainMarkers(int) * @see #addRangeMarker(int, Marker, Layer) */ public void addDomainMarker(int index, Marker marker, Layer layer) { addDomainMarker(index, marker, layer, true); } /** * Adds a marker for a specific dataset/renderer and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * <P> * Typically a marker will be drawn by the renderer as a line perpendicular * to the domain axis (that the renderer is mapped to), however this is * entirely up to the renderer. * * @param index * the dataset/renderer index. * @param marker * the marker. * @param layer * the layer (foreground or background). * @param notify * notify listeners? * * @since 1.0.10 */ public void addDomainMarker(int index, Marker marker, Layer layer, boolean notify) { if (marker == null) { throw new IllegalArgumentException("Null 'marker' not permitted."); } if (layer == null) { throw new IllegalArgumentException("Null 'layer' not permitted."); } Collection markers; if (layer == Layer.FOREGROUND) { markers = (Collection) this.foregroundDomainMarkers.get(new Integer(index)); if (markers == null) { markers = new java.util.ArrayList(); this.foregroundDomainMarkers.put(new Integer(index), markers); } markers.add(marker); } else if (layer == Layer.BACKGROUND) { markers = (Collection) this.backgroundDomainMarkers.get(new Integer(index)); if (markers == null) { markers = new java.util.ArrayList(); this.backgroundDomainMarkers.put(new Integer(index), markers); } markers.add(marker); } // marker.addChangeListener(this); if (notify) { // fireChangeEvent(); } } /** * Removes a marker for the domain axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param marker * the marker. * * @return A boolean indicating whether or not the marker was actually * removed. * * @since 1.0.7 */ public boolean removeDomainMarker(Marker marker) { return removeDomainMarker(marker, Layer.FOREGROUND); } /** * Removes a marker for the domain axis in the specified layer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param marker * the marker (<code>null</code> not permitted). * @param layer * the layer (foreground or background). * * @return A boolean indicating whether or not the marker was actually * removed. * * @since 1.0.7 */ public boolean removeDomainMarker(Marker marker, Layer layer) { return removeDomainMarker(0, marker, layer); } /** * Removes a marker for a specific dataset/renderer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index * the dataset/renderer index. * @param marker * the marker. * @param layer * the layer (foreground or background). * * @return A boolean indicating whether or not the marker was actually * removed. * * @since 1.0.7 */ public boolean removeDomainMarker(int index, Marker marker, Layer layer) { return removeDomainMarker(index, marker, layer, true); } /** * Removes a marker for a specific dataset/renderer and, if requested, sends * a {@link PlotChangeEvent} to all registered listeners. * * @param index * the dataset/renderer index. * @param marker * the marker. * @param layer * the layer (foreground or background). * @param notify * notify listeners? * * @return A boolean indicating whether or not the marker was actually * removed. * * @since 1.0.10 */ public boolean removeDomainMarker(int index, Marker marker, Layer layer, boolean notify) { ArrayList markers; if (layer == Layer.FOREGROUND) { markers = (ArrayList) this.foregroundDomainMarkers.get(new Integer(index)); } else { markers = (ArrayList) this.backgroundDomainMarkers.get(new Integer(index)); } if (markers == null) { return false; } boolean removed = markers.remove(marker); if (removed && notify) { // fireChangeEvent(); } return removed; } /** * Adds a marker for the range axis and sends a {@link PlotChangeEvent} to * all registered listeners. * <P> * Typically a marker will be drawn by the renderer as a line perpendicular * to the range axis, however this is entirely up to the renderer. * * @param marker * the marker (<code>null</code> not permitted). * * @see #addRangeMarker(Marker, Layer) */ public void addRangeMarker(Marker marker) { addRangeMarker(marker, Layer.FOREGROUND); } /** * Adds a marker for the range axis in the specified layer and sends a * {@link PlotChangeEvent} to all registered listeners. * <P> * Typically a marker will be drawn by the renderer as a line perpendicular * to the range axis, however this is entirely up to the renderer. * * @param marker * the marker (<code>null</code> not permitted). * @param layer * the layer (foreground or background). * * @see #addRangeMarker(int, Marker, Layer) */ public void addRangeMarker(Marker marker, Layer layer) { addRangeMarker(0, marker, layer); } /** * Clears all the range markers and sends a {@link PlotChangeEvent} to all * registered listeners. * * @see #clearRangeMarkers() */ public void clearRangeMarkers() { if (this.backgroundRangeMarkers != null) { Set keys = this.backgroundRangeMarkers.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Integer key = (Integer) iterator.next(); clearRangeMarkers(key.intValue()); } this.backgroundRangeMarkers.clear(); } if (this.foregroundRangeMarkers != null) { Set keys = this.foregroundRangeMarkers.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Integer key = (Integer) iterator.next(); clearRangeMarkers(key.intValue()); } this.foregroundRangeMarkers.clear(); } // fireChangeEvent(); } /** * Adds a marker for a specific dataset/renderer and sends a * {@link PlotChangeEvent} to all registered listeners. * <P> * Typically a marker will be drawn by the renderer as a line perpendicular * to the range axis, however this is entirely up to the renderer. * * @param index * the dataset/renderer index. * @param marker * the marker. * @param layer * the layer (foreground or background). * * @see #clearRangeMarkers(int) * @see #addDomainMarker(int, Marker, Layer) */ public void addRangeMarker(int index, Marker marker, Layer layer) { addRangeMarker(index, marker, layer, true); } /** * Adds a marker for a specific dataset/renderer and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * <P> * Typically a marker will be drawn by the renderer as a line perpendicular * to the range axis, however this is entirely up to the renderer. * * @param index * the dataset/renderer index. * @param marker * the marker. * @param layer * the layer (foreground or background). * @param notify * notify listeners? * * @since 1.0.10 */ public void addRangeMarker(int index, Marker marker, Layer layer, boolean notify) { Collection markers; if (layer == Layer.FOREGROUND) { markers = (Collection) this.foregroundRangeMarkers.get(new Integer(index)); if (markers == null) { markers = new java.util.ArrayList(); this.foregroundRangeMarkers.put(new Integer(index), markers); } markers.add(marker); } else if (layer == Layer.BACKGROUND) { markers = (Collection) this.backgroundRangeMarkers.get(new Integer(index)); if (markers == null) { markers = new java.util.ArrayList(); this.backgroundRangeMarkers.put(new Integer(index), markers); } markers.add(marker); } // marker.addChangeListener(this); if (notify) { // fireChangeEvent(); } } /** * Clears the (foreground and background) range markers for a particular * renderer. * * @param index * the renderer index. */ public void clearRangeMarkers(int index) { Integer key = new Integer(index); if (this.backgroundRangeMarkers != null) { Collection markers = (Collection) this.backgroundRangeMarkers.get(key); if (markers != null) { Iterator iterator = markers.iterator(); while (iterator.hasNext()) { Marker m = (Marker) iterator.next(); // m.removeChangeListener(this); } markers.clear(); } } if (this.foregroundRangeMarkers != null) { Collection markers = (Collection) this.foregroundRangeMarkers.get(key); if (markers != null) { Iterator iterator = markers.iterator(); while (iterator.hasNext()) { Marker m = (Marker) iterator.next(); // m.removeChangeListener(this); } markers.clear(); } } // fireChangeEvent(); } /** * Removes a marker for the range axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param marker * the marker. * * @return A boolean indicating whether or not the marker was actually * removed. * * @since 1.0.7 */ public boolean removeRangeMarker(Marker marker) { return removeRangeMarker(marker, Layer.FOREGROUND); } /** * Removes a marker for the range axis in the specified layer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param marker * the marker (<code>null</code> not permitted). * @param layer * the layer (foreground or background). * * @return A boolean indicating whether or not the marker was actually * removed. * * @since 1.0.7 */ public boolean removeRangeMarker(Marker marker, Layer layer) { return removeRangeMarker(0, marker, layer); } /** * Removes a marker for a specific dataset/renderer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index * the dataset/renderer index. * @param marker * the marker. * @param layer * the layer (foreground or background). * * @return A boolean indicating whether or not the marker was actually * removed. * * @since 1.0.7 */ public boolean removeRangeMarker(int index, Marker marker, Layer layer) { return removeRangeMarker(index, marker, layer, true); } /** * Removes a marker for a specific dataset/renderer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index * the dataset/renderer index. * @param marker * the marker. * @param layer * the layer (foreground or background). * @param notify * notify listeners? * * @return A boolean indicating whether or not the marker was actually * removed. * * @since 1.0.10 */ public boolean removeRangeMarker(int index, Marker marker, Layer layer, boolean notify) { if (marker == null) { throw new IllegalArgumentException("Null 'marker' argument."); } ArrayList markers; if (layer == Layer.FOREGROUND) { markers = (ArrayList) this.foregroundRangeMarkers.get(new Integer(index)); } else { markers = (ArrayList) this.backgroundRangeMarkers.get(new Integer(index)); } if (markers == null) { return false; } boolean removed = markers.remove(marker); if (removed && notify) { // fireChangeEvent(); } return removed; } /** * Adds an annotation to the plot and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param annotation * the annotation (<code>null</code> not permitted). * * @see #getAnnotations() * @see #removeAnnotation(XYAnnotation) */ public void addAnnotation(XYAnnotation annotation) { addAnnotation(annotation, true); } /** * Adds an annotation to the plot and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param annotation * the annotation (<code>null</code> not permitted). * @param notify * notify listeners? * * @since 1.0.10 */ public void addAnnotation(XYAnnotation annotation, boolean notify) { if (annotation == null) { throw new IllegalArgumentException("Null 'annotation' argument."); } this.annotations.add(annotation); if (notify) { // fireChangeEvent(); } } /** * Removes an annotation from the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param annotation * the annotation (<code>null</code> not permitted). * * @return A boolean (indicates whether or not the annotation was removed). * * @see #addAnnotation(XYAnnotation) * @see #getAnnotations() */ public boolean removeAnnotation(XYAnnotation annotation) { return removeAnnotation(annotation, true); } /** * Removes an annotation from the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param annotation * the annotation (<code>null</code> not permitted). * @param notify * notify listeners? * * @return A boolean (indicates whether or not the annotation was removed). * * @since 1.0.10 */ public boolean removeAnnotation(XYAnnotation annotation, boolean notify) { if (annotation == null) { throw new IllegalArgumentException("Null 'annotation' argument."); } boolean removed = this.annotations.remove(annotation); if (removed && notify) { // fireChangeEvent(); } return removed; } /** * Returns the list of annotations. * * @return The list of annotations. * * @since 1.0.1 * * @see #addAnnotation(XYAnnotation) */ public List getAnnotations() { return new ArrayList(this.annotations); } /** * Clears all the annotations and sends a {@link PlotChangeEvent} to all * registered listeners. * * @see #addAnnotation(XYAnnotation) */ public void clearAnnotations() { this.annotations.clear(); // fireChangeEvent(); } /** * Calculates the space required for all the axes in the plot. * * @param g2 * the graphics device. * @param plotArea * the plot area. * * @return The required space. */ protected AxisSpace calculateAxisSpace(Canvas g2, Rectangle2D plotArea) { AxisSpace space = new AxisSpace(); space = calculateRangeAxisSpace(g2, plotArea, space); Rectangle2D revPlotArea = space.shrink(plotArea, null); space = calculateDomainAxisSpace(g2, revPlotArea, space); return space; } /** * Calculates the space required for the domain axis/axes. * * @param g2 * the graphics device. * @param plotArea * the plot area. * @param space * a carrier for the result (<code>null</code> permitted). * * @return The required space. */ protected AxisSpace calculateDomainAxisSpace(Canvas g2, Rectangle2D plotArea, AxisSpace space) { if (space == null) { space = new AxisSpace(); } // reserve some space for the domain axis... if (this.fixedDomainAxisSpace != null) { if (this.orientation == PlotOrientation.HORIZONTAL) { space.ensureAtLeast(this.fixedDomainAxisSpace.getLeft(), RectangleEdge.LEFT); space.ensureAtLeast(this.fixedDomainAxisSpace.getRight(), RectangleEdge.RIGHT); } else if (this.orientation == PlotOrientation.VERTICAL) { space.ensureAtLeast(this.fixedDomainAxisSpace.getTop(), RectangleEdge.TOP); space.ensureAtLeast(this.fixedDomainAxisSpace.getBottom(), RectangleEdge.BOTTOM); } } else { // reserve space for the domain axes... for (int i = 0; i < this.domainAxes.size(); i++) { Axis axis = (Axis) this.domainAxes.get(i); if (axis != null) { RectangleEdge edge = getDomainAxisEdge(i); space = axis.reserveSpace(g2, this, plotArea, edge, space); } } } return space; } /** * Calculates the space required for the range axis/axes. * * @param g2 * the graphics device. * @param plotArea * the plot area. * @param space * a carrier for the result (<code>null</code> permitted). * * @return The required space. */ protected AxisSpace calculateRangeAxisSpace(Canvas g2, Rectangle2D plotArea, AxisSpace space) { if (space == null) { space = new AxisSpace(); } // reserve some space for the range axis... if (this.fixedRangeAxisSpace != null) { if (this.orientation == PlotOrientation.HORIZONTAL) { space.ensureAtLeast(this.fixedRangeAxisSpace.getTop(), RectangleEdge.TOP); space.ensureAtLeast(this.fixedRangeAxisSpace.getBottom(), RectangleEdge.BOTTOM); } else if (this.orientation == PlotOrientation.VERTICAL) { space.ensureAtLeast(this.fixedRangeAxisSpace.getLeft(), RectangleEdge.LEFT); space.ensureAtLeast(this.fixedRangeAxisSpace.getRight(), RectangleEdge.RIGHT); } } else { // reserve space for the range axes... for (int i = 0; i < this.rangeAxes.size(); i++) { Axis axis = (Axis) this.rangeAxes.get(i); if (axis != null) { RectangleEdge edge = getRangeAxisEdge(i); space = axis.reserveSpace(g2, this, plotArea, edge, space); } } } return space; } /** * Draws the plot within the specified area on a graphics device. * * @param g2 * the graphics device. * @param area * the plot area (in Java2D space). * @param anchor * an anchor point in Java2D space (<code>null</code> permitted). * @param parentState * the state from the parent plot, if there is one ( * <code>null</code> permitted). * @param info * collects chart drawing information (<code>null</code> * permitted). */ public void draw(Canvas g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { // if the plot area is too small, just return... boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW); boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW); if (b1 || b2) { return; } // record the plot area... if (info != null) { info.setPlotArea(area); } // adjust the drawing area for the plot insets (if any)... RectangleInsets insets = getInsets(); insets.trim(area); AxisSpace space = calculateAxisSpace(g2, area); Rectangle2D dataArea = space.shrink(area, null); this.axisOffset.trim(dataArea); createAndAddEntity((Rectangle2D) dataArea.clone(), info, null, null); if (info != null) { info.setDataArea(dataArea); } // draw the plot background and axes... drawBackground(g2, dataArea); Map axisStateMap = drawAxes(g2, area, dataArea, info); PlotOrientation orient = getOrientation(); // the anchor point is typically the point where the mouse last // clicked - the crosshairs will be driven off this point... if (anchor != null && !dataArea.contains(anchor)) { anchor = null; } CrosshairState crosshairState = new CrosshairState(); crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY); crosshairState.setAnchor(anchor); crosshairState.setAnchorX(Double.NaN); crosshairState.setAnchorY(Double.NaN); if (anchor != null) { ValueAxis domainAxis = getDomainAxis(); if (domainAxis != null) { double x; if (orient == PlotOrientation.VERTICAL) { x = domainAxis.java2DToValue(anchor.getX(), dataArea, getDomainAxisEdge()); } else { x = domainAxis.java2DToValue(anchor.getY(), dataArea, getDomainAxisEdge()); } crosshairState.setAnchorX(x); } ValueAxis rangeAxis = getRangeAxis(); if (rangeAxis != null) { double y; if (orient == PlotOrientation.VERTICAL) { y = rangeAxis.java2DToValue(anchor.getY(), dataArea, getRangeAxisEdge()); } else { y = rangeAxis.java2DToValue(anchor.getX(), dataArea, getRangeAxisEdge()); } crosshairState.setAnchorY(y); } } crosshairState.setCrosshairX(getDomainCrosshairValue()); crosshairState.setCrosshairY(getRangeCrosshairValue()); g2.save(); g2.clipRect((float) dataArea.getMinX(), (float) dataArea.getMinY(), (float) dataArea.getMaxX(), (float) dataArea.getMaxY()); AxisState domainAxisState = (AxisState) axisStateMap.get(getDomainAxis()); if (domainAxisState == null) { if (parentState != null) { domainAxisState = (AxisState) parentState.getSharedAxisStates().get(getDomainAxis()); } } AxisState rangeAxisState = (AxisState) axisStateMap.get(getRangeAxis()); if (rangeAxisState == null) { if (parentState != null) { rangeAxisState = (AxisState) parentState.getSharedAxisStates().get(getRangeAxis()); } } if (domainAxisState != null) { drawDomainTickBands(g2, dataArea, domainAxisState.getTicks()); } if (rangeAxisState != null) { drawRangeTickBands(g2, dataArea, rangeAxisState.getTicks()); } if (domainAxisState != null) { drawDomainGridlines(g2, dataArea, domainAxisState.getTicks()); drawZeroDomainBaseline(g2, dataArea); } if (rangeAxisState != null) { drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks()); drawZeroRangeBaseline(g2, dataArea); } // draw the markers that are associated with a specific renderer... for (int i = 0; i < this.renderers.size(); i++) { drawDomainMarkers(g2, dataArea, i, Layer.BACKGROUND); } for (int i = 0; i < this.renderers.size(); i++) { drawRangeMarkers(g2, dataArea, i, Layer.BACKGROUND); } // now draw annotations and render data items... boolean foundData = false; DatasetRenderingOrder order = getDatasetRenderingOrder(); if (order == DatasetRenderingOrder.FORWARD) { // draw background annotations int rendererCount = this.renderers.size(); for (int i = 0; i < rendererCount; i++) { XYItemRenderer r = getRenderer(i); if (r != null) { ValueAxis domainAxis = getDomainAxisForDataset(i); ValueAxis rangeAxis = getRangeAxisForDataset(i); r.drawAnnotations(g2, dataArea, domainAxis, rangeAxis, Layer.BACKGROUND, info); } } // render data items... for (int i = 0; i < getDatasetCount(); i++) { foundData = render(g2, dataArea, i, info, crosshairState) || foundData; } // draw foreground annotations for (int i = 0; i < rendererCount; i++) { XYItemRenderer r = getRenderer(i); if (r != null) { ValueAxis domainAxis = getDomainAxisForDataset(i); ValueAxis rangeAxis = getRangeAxisForDataset(i); r.drawAnnotations(g2, dataArea, domainAxis, rangeAxis, Layer.FOREGROUND, info); } } } else if (order == DatasetRenderingOrder.REVERSE) { // draw background annotations int rendererCount = this.renderers.size(); for (int i = rendererCount - 1; i >= 0; i--) { XYItemRenderer r = getRenderer(i); if (i >= getDatasetCount()) { // we need the dataset to make continue; // a link to the axes } if (r != null) { ValueAxis domainAxis = getDomainAxisForDataset(i); ValueAxis rangeAxis = getRangeAxisForDataset(i); r.drawAnnotations(g2, dataArea, domainAxis, rangeAxis, Layer.BACKGROUND, info); } } for (int i = getDatasetCount() - 1; i >= 0; i--) { foundData = render(g2, dataArea, i, info, crosshairState) || foundData; } // draw foreground annotations for (int i = rendererCount - 1; i >= 0; i--) { XYItemRenderer r = getRenderer(i); if (i >= getDatasetCount()) { // we need the dataset to make continue; // a link to the axes } if (r != null) { ValueAxis domainAxis = getDomainAxisForDataset(i); ValueAxis rangeAxis = getRangeAxisForDataset(i); r.drawAnnotations(g2, dataArea, domainAxis, rangeAxis, Layer.FOREGROUND, info); } } } // draw domain crosshair if required... int xAxisIndex = crosshairState.getDomainAxisIndex(); ValueAxis xAxis = getDomainAxis(xAxisIndex); RectangleEdge xAxisEdge = getDomainAxisEdge(xAxisIndex); if (!this.domainCrosshairLockedOnData && anchor != null) { double xx; if (orient == PlotOrientation.VERTICAL) { xx = xAxis.java2DToValue(anchor.getX(), dataArea, xAxisEdge); } else { xx = xAxis.java2DToValue(anchor.getY(), dataArea, xAxisEdge); } crosshairState.setCrosshairX(xx); } setDomainCrosshairValue(crosshairState.getCrosshairX(), false); if (isDomainCrosshairVisible()) { double x = getDomainCrosshairValue(); Paint paint = getDomainCrosshairPaint(); int oldAlpha = paint.getAlpha(); paint.setAlpha(getForegroundAlpha()); Float stroke = getDomainCrosshairStroke(); drawDomainCrosshair(g2, dataArea, orient, x, xAxis, stroke, paint); paint.setAlpha(oldAlpha); } // draw range crosshair if required... int yAxisIndex = crosshairState.getRangeAxisIndex(); ValueAxis yAxis = getRangeAxis(yAxisIndex); RectangleEdge yAxisEdge = getRangeAxisEdge(yAxisIndex); if (!this.rangeCrosshairLockedOnData && anchor != null) { double yy; if (orient == PlotOrientation.VERTICAL) { yy = yAxis.java2DToValue(anchor.getY(), dataArea, yAxisEdge); } else { yy = yAxis.java2DToValue(anchor.getX(), dataArea, yAxisEdge); } crosshairState.setCrosshairY(yy); } setRangeCrosshairValue(crosshairState.getCrosshairY(), false); if (isRangeCrosshairVisible()) { double y = getRangeCrosshairValue(); Paint paint = getRangeCrosshairPaint(); int oldAlpha = paint.getAlpha(); paint.setAlpha(getForegroundAlpha()); Float stroke = getRangeCrosshairStroke(); drawRangeCrosshair(g2, dataArea, orient, y, yAxis, stroke, paint); paint.setAlpha(oldAlpha); } if (!foundData) { drawNoDataMessage(g2, dataArea); } for (int i = 0; i < this.renderers.size(); i++) { drawDomainMarkers(g2, dataArea, i, Layer.FOREGROUND); } for (int i = 0; i < this.renderers.size(); i++) { drawRangeMarkers(g2, dataArea, i, Layer.FOREGROUND); } drawAnnotations(g2, dataArea, info); g2.restore(); drawOutline(g2, dataArea); } /** * Draws the background for the plot. * * @param g2 * the graphics device. * @param area * the area. */ public void drawBackground(Canvas g2, Rectangle2D area) { fillBackground(g2, area, this.orientation); drawQuadrants(g2, area); } /** * Draws the quadrants. * * @param g2 * the graphics device. * @param area * the area. * * @see #setQuadrantOrigin(Point2D) * @see #setQuadrantPaint(int, Paint) */ protected void drawQuadrants(Canvas g2, Rectangle2D area) { // 0 | 1 // --+-- // 2 | 3 boolean somethingToDraw = false; ValueAxis xAxis = getDomainAxis(); if (xAxis == null) { // we can't draw quadrants without a valid x-axis return; } double x = xAxis.getRange().constrain(this.quadrantOrigin.getX()); double xx = xAxis.valueToJava2D(x, area, getDomainAxisEdge()); ValueAxis yAxis = getRangeAxis(); if (yAxis == null) { // we can't draw quadrants without a valid y-axis return; } double y = yAxis.getRange().constrain(this.quadrantOrigin.getY()); double yy = yAxis.valueToJava2D(y, area, getRangeAxisEdge()); double xmin = xAxis.getLowerBound(); double xxmin = xAxis.valueToJava2D(xmin, area, getDomainAxisEdge()); double xmax = xAxis.getUpperBound(); double xxmax = xAxis.valueToJava2D(xmax, area, getDomainAxisEdge()); double ymin = yAxis.getLowerBound(); double yymin = yAxis.valueToJava2D(ymin, area, getRangeAxisEdge()); double ymax = yAxis.getUpperBound(); double yymax = yAxis.valueToJava2D(ymax, area, getRangeAxisEdge()); Rectangle2D[] r = new Rectangle2D[] { null, null, null, null }; if (this.quadrantPaint[0] != null) { if (x > xmin && y < ymax) { if (this.orientation == PlotOrientation.HORIZONTAL) { r[0] = new Rectangle2D.Double(Math.min(yymax, yy), Math.min(xxmin, xx), Math.abs(yy - yymax), Math.abs(xx - xxmin)); } else { // PlotOrientation.VERTICAL r[0] = new Rectangle2D.Double(Math.min(xxmin, xx), Math.min(yymax, yy), Math.abs(xx - xxmin), Math.abs(yy - yymax)); } somethingToDraw = true; } } if (this.quadrantPaint[1] != null) { if (x < xmax && y < ymax) { if (this.orientation == PlotOrientation.HORIZONTAL) { r[1] = new Rectangle2D.Double(Math.min(yymax, yy), Math.min(xxmax, xx), Math.abs(yy - yymax), Math.abs(xx - xxmax)); } else { // PlotOrientation.VERTICAL r[1] = new Rectangle2D.Double(Math.min(xx, xxmax), Math.min(yymax, yy), Math.abs(xx - xxmax), Math.abs(yy - yymax)); } somethingToDraw = true; } } if (this.quadrantPaint[2] != null) { if (x > xmin && y > ymin) { if (this.orientation == PlotOrientation.HORIZONTAL) { r[2] = new Rectangle2D.Double(Math.min(yymin, yy), Math.min(xxmin, xx), Math.abs(yy - yymin), Math.abs(xx - xxmin)); } else { // PlotOrientation.VERTICAL r[2] = new Rectangle2D.Double(Math.min(xxmin, xx), Math.min(yymin, yy), Math.abs(xx - xxmin), Math.abs(yy - yymin)); } somethingToDraw = true; } } if (this.quadrantPaint[3] != null) { if (x < xmax && y > ymin) { if (this.orientation == PlotOrientation.HORIZONTAL) { r[3] = new Rectangle2D.Double(Math.min(yymin, yy), Math.min(xxmax, xx), Math.abs(yy - yymin), Math.abs(xx - xxmax)); } else { // PlotOrientation.VERTICAL r[3] = new Rectangle2D.Double(Math.min(xx, xxmax), Math.min(yymin, yy), Math.abs(xx - xxmax), Math.abs(yy - yymin)); } somethingToDraw = true; } } if (somethingToDraw) { for (int i = 0; i < 4; i++) { if (this.quadrantPaint[i] != null && r[i] != null) { Paint paint = this.quadrantPaint[i]; paint.setStyle(Paint.Style.FILL); int oldAlpha = paint.getAlpha(); paint.setAlpha(getBackgroundAlpha()); g2.drawRect((float) r[i].getMinX(), (float) r[i].getMinY(), (float) r[i].getMaxX(), (float) r[i].getMaxY(), paint); paint.setAlpha(oldAlpha); } } } } /** * Draws the domain tick bands, if any. * * @param g2 * the graphics device. * @param dataArea * the data area. * @param ticks * the ticks. * * @see #setDomainTickBandPaint(Paint) */ public void drawDomainTickBands(Canvas g2, Rectangle2D dataArea, List ticks) { Paint bandPaint = getDomainTickBandPaint(); if (bandPaint != null) { boolean fillBand = false; ValueAxis xAxis = getDomainAxis(); double previous = xAxis.getLowerBound(); Iterator iterator = ticks.iterator(); while (iterator.hasNext()) { ValueTick tick = (ValueTick) iterator.next(); double current = tick.getValue(); if (fillBand) { getRenderer().fillDomainGridBand(g2, this, xAxis, dataArea, previous, current); } previous = current; fillBand = !fillBand; } double end = xAxis.getUpperBound(); if (fillBand) { getRenderer().fillDomainGridBand(g2, this, xAxis, dataArea, previous, end); } } } /** * Draws the range tick bands, if any. * * @param g2 * the graphics device. * @param dataArea * the data area. * @param ticks * the ticks. * * @see #setRangeTickBandPaint(Paint) */ public void drawRangeTickBands(Canvas g2, Rectangle2D dataArea, List ticks) { Paint bandPaint = getRangeTickBandPaint(); if (bandPaint != null) { boolean fillBand = false; ValueAxis axis = getRangeAxis(); double previous = axis.getLowerBound(); Iterator iterator = ticks.iterator(); while (iterator.hasNext()) { ValueTick tick = (ValueTick) iterator.next(); double current = tick.getValue(); if (fillBand) { getRenderer().fillRangeGridBand(g2, this, axis, dataArea, previous, current); } previous = current; fillBand = !fillBand; } double end = axis.getUpperBound(); if (fillBand) { getRenderer().fillRangeGridBand(g2, this, axis, dataArea, previous, end); } } } /** * A utility method for drawing the axes. * * @param g2 * the graphics device (<code>null</code> not permitted). * @param plotArea * the plot area (<code>null</code> not permitted). * @param dataArea * the data area (<code>null</code> not permitted). * @param plotState * collects information about the plot (<code>null</code> * permitted). * * @return A map containing the state for each axis drawn. */ protected Map drawAxes(Canvas g2, Rectangle2D plotArea, Rectangle2D dataArea, PlotRenderingInfo plotState) { AxisCollection axisCollection = new AxisCollection(); // add domain axes to lists... for (int index = 0; index < this.domainAxes.size(); index++) { ValueAxis axis = (ValueAxis) this.domainAxes.get(index); if (axis != null) { axisCollection.add(axis, getDomainAxisEdge(index)); } } // add range axes to lists... for (int index = 0; index < this.rangeAxes.size(); index++) { ValueAxis yAxis = (ValueAxis) this.rangeAxes.get(index); if (yAxis != null) { axisCollection.add(yAxis, getRangeAxisEdge(index)); } } Map axisStateMap = new HashMap(); // draw the top axes double cursor = dataArea.getMinY() - this.axisOffset.calculateTopOutset(dataArea.getHeight()); Iterator iterator = axisCollection.getAxesAtTop().iterator(); while (iterator.hasNext()) { ValueAxis axis = (ValueAxis) iterator.next(); AxisState info = axis.draw(g2, cursor, plotArea, dataArea, RectangleEdge.TOP, plotState); cursor = info.getCursor(); axisStateMap.put(axis, info); } // draw the bottom axes cursor = dataArea.getMaxY() + this.axisOffset.calculateBottomOutset(dataArea.getHeight()); iterator = axisCollection.getAxesAtBottom().iterator(); while (iterator.hasNext()) { ValueAxis axis = (ValueAxis) iterator.next(); AxisState info = axis.draw(g2, cursor, plotArea, dataArea, RectangleEdge.BOTTOM, plotState); cursor = info.getCursor(); axisStateMap.put(axis, info); } // draw the left axes cursor = dataArea.getMinX() - this.axisOffset.calculateLeftOutset(dataArea.getWidth()); iterator = axisCollection.getAxesAtLeft().iterator(); while (iterator.hasNext()) { ValueAxis axis = (ValueAxis) iterator.next(); AxisState info = axis.draw(g2, cursor, plotArea, dataArea, RectangleEdge.LEFT, plotState); cursor = info.getCursor(); axisStateMap.put(axis, info); } // draw the right axes cursor = dataArea.getMaxX() + this.axisOffset.calculateRightOutset(dataArea.getWidth()); iterator = axisCollection.getAxesAtRight().iterator(); while (iterator.hasNext()) { ValueAxis axis = (ValueAxis) iterator.next(); AxisState info = axis.draw(g2, cursor, plotArea, dataArea, RectangleEdge.RIGHT, plotState); cursor = info.getCursor(); axisStateMap.put(axis, info); } return axisStateMap; } /** * Draws a representation of the data within the dataArea region, using the * current renderer. * <P> * The <code>info</code> and <code>crosshairState</code> arguments may be * <code>null</code>. * * @param g2 * the graphics device. * @param dataArea * the region in which the data is to be drawn. * @param index * the dataset index. * @param info * an optional object for collection dimension information. * @param crosshairState * collects crosshair information (<code>null</code> permitted). * * @return A flag that indicates whether any data was actually rendered. */ public boolean render(Canvas g2, Rectangle2D dataArea, int index, PlotRenderingInfo info, CrosshairState crosshairState) { boolean foundData = false; XYDataset dataset = getDataset(index); if (!DatasetUtilities.isEmptyOrNull(dataset)) { foundData = true; ValueAxis xAxis = getDomainAxisForDataset(index); ValueAxis yAxis = getRangeAxisForDataset(index); if (xAxis == null || yAxis == null) { return foundData; // can't render anything without axes } XYItemRenderer renderer = getRenderer(index); if (renderer == null) { renderer = getRenderer(); if (renderer == null) { // no default renderer available return foundData; } } XYItemRendererState state = renderer.initialise(g2, dataArea, this, dataset, info); int passCount = renderer.getPassCount(); SeriesRenderingOrder seriesOrder = getSeriesRenderingOrder(); if (seriesOrder == SeriesRenderingOrder.REVERSE) { // render series in reverse order for (int pass = 0; pass < passCount; pass++) { int seriesCount = dataset.getSeriesCount(); for (int series = seriesCount - 1; series >= 0; series--) { int firstItem = 0; int lastItem = dataset.getItemCount(series) - 1; if (lastItem == -1) { continue; } if (state.getProcessVisibleItemsOnly()) { int[] itemBounds = RendererUtilities.findLiveItems(dataset, series, xAxis.getLowerBound(), xAxis.getUpperBound()); firstItem = Math.max(itemBounds[0] - 1, 0); lastItem = Math.min(itemBounds[1] + 1, lastItem); } state.startSeriesPass(dataset, series, firstItem, lastItem, pass, passCount); for (int item = firstItem; item <= lastItem; item++) { renderer.drawItem(g2, state, dataArea, info, this, xAxis, yAxis, dataset, series, item, crosshairState, pass); } state.endSeriesPass(dataset, series, firstItem, lastItem, pass, passCount); } } } else { // render series in forward order for (int pass = 0; pass < passCount; pass++) { int seriesCount = dataset.getSeriesCount(); for (int series = 0; series < seriesCount; series++) { int firstItem = 0; int lastItem = dataset.getItemCount(series) - 1; if (state.getProcessVisibleItemsOnly()) { int[] itemBounds = RendererUtilities.findLiveItems(dataset, series, xAxis.getLowerBound(), xAxis.getUpperBound()); firstItem = Math.max(itemBounds[0] - 1, 0); lastItem = Math.min(itemBounds[1] + 1, lastItem); } state.startSeriesPass(dataset, series, firstItem, lastItem, pass, passCount); for (int item = firstItem; item <= lastItem; item++) { renderer.drawItem(g2, state, dataArea, info, this, xAxis, yAxis, dataset, series, item, crosshairState, pass); } state.endSeriesPass(dataset, series, firstItem, lastItem, pass, passCount); } } } } return foundData; } /** * Returns the domain axis for a dataset. * * @param index * the dataset index. * * @return The axis. */ public ValueAxis getDomainAxisForDataset(int index) { int upper = Math.max(getDatasetCount(), getRendererCount()); if (index < 0 || index >= upper) { throw new IllegalArgumentException("Index " + index + " out of bounds."); } ValueAxis valueAxis = null; List axisIndices = (List) this.datasetToDomainAxesMap.get(new Integer(index)); if (axisIndices != null) { // the first axis in the list is used for data <--> Java2D Integer axisIndex = (Integer) axisIndices.get(0); valueAxis = getDomainAxis(axisIndex.intValue()); } else { valueAxis = getDomainAxis(0); } return valueAxis; } /** * Returns the range axis for a dataset. * * @param index * the dataset index. * * @return The axis. */ public ValueAxis getRangeAxisForDataset(int index) { int upper = Math.max(getDatasetCount(), getRendererCount()); if (index < 0 || index >= upper) { throw new IllegalArgumentException("Index " + index + " out of bounds."); } ValueAxis valueAxis = null; List axisIndices = (List) this.datasetToRangeAxesMap.get(new Integer(index)); if (axisIndices != null) { // the first axis in the list is used for data <--> Java2D Integer axisIndex = (Integer) axisIndices.get(0); valueAxis = getRangeAxis(axisIndex.intValue()); } else { valueAxis = getRangeAxis(0); } return valueAxis; } /** * Draws the gridlines for the plot, if they are visible. * * @param g2 * the graphics device. * @param dataArea * the data area. * @param ticks * the ticks. * * @see #drawRangeGridlines(Graphics2D, Rectangle2D, List) */ protected void drawDomainGridlines(Canvas g2, Rectangle2D dataArea, List ticks) { // no renderer, no gridlines... if (getRenderer() == null) { return; } // draw the domain grid lines, if any... if (isDomainGridlinesVisible() || isDomainMinorGridlinesVisible()) { Float gridStroke = null; Paint gridPaint = null; Iterator iterator = ticks.iterator(); boolean paintLine = false; while (iterator.hasNext()) { paintLine = false; ValueTick tick = (ValueTick) iterator.next(); if ((tick.getTickType() == TickType.MINOR) && isDomainMinorGridlinesVisible()) { gridStroke = getDomainMinorGridlineStroke(); gridPaint = getDomainMinorGridlinePaint(); paintLine = true; } else if ((tick.getTickType() == TickType.MAJOR) && isDomainGridlinesVisible()) { gridStroke = getDomainGridlineStroke(); gridPaint = getDomainGridlinePaint(); paintLine = true; } XYItemRenderer r = getRenderer(); if ((r instanceof AbstractXYItemRenderer) && paintLine) { ((AbstractXYItemRenderer) r).drawDomainLine(g2, this, getDomainAxis(), dataArea, tick.getValue(), gridPaint, gridStroke); } } } } /** * Draws the gridlines for the plot's primary range axis, if they are * visible. * * @param g2 * the graphics device. * @param area * the data area. * @param ticks * the ticks. * * @see #drawDomainGridlines(Graphics2D, Rectangle2D, List) */ protected void drawRangeGridlines(Canvas g2, Rectangle2D area, List ticks) { // no renderer, no gridlines... if (getRenderer() == null) { return; } // draw the range grid lines, if any... if (isRangeGridlinesVisible() || isRangeMinorGridlinesVisible()) { Float gridStroke = null; Paint gridPaint = null; ValueAxis axis = getRangeAxis(); if (axis != null) { Iterator iterator = ticks.iterator(); boolean paintLine = false; while (iterator.hasNext()) { paintLine = false; ValueTick tick = (ValueTick) iterator.next(); if ((tick.getTickType() == TickType.MINOR) && isRangeMinorGridlinesVisible()) { gridStroke = getRangeMinorGridlineStroke(); gridPaint = getRangeMinorGridlinePaint(); paintLine = true; } else if ((tick.getTickType() == TickType.MAJOR) && isRangeGridlinesVisible()) { gridStroke = getRangeGridlineStroke(); gridPaint = getRangeGridlinePaint(); paintLine = true; } if ((tick.getValue() != 0.0 || !isRangeZeroBaselineVisible()) && paintLine) { getRenderer().drawRangeLine(g2, this, getRangeAxis(), area, tick.getValue(), gridPaint, gridStroke); } } } } } /** * Draws a base line across the chart at value zero on the domain axis. * * @param g2 * the graphics device. * @param area * the data area. * * @see #setDomainZeroBaselineVisible(boolean) * * @since 1.0.5 */ protected void drawZeroDomainBaseline(Canvas g2, Rectangle2D area) { if (isDomainZeroBaselineVisible()) { XYItemRenderer r = getRenderer(); // FIXME: the renderer interface doesn't have the drawDomainLine() // method, so we have to rely on the renderer being a subclass of // AbstractXYItemRenderer (which is lame) if (r instanceof AbstractXYItemRenderer) { AbstractXYItemRenderer renderer = (AbstractXYItemRenderer) r; renderer.drawDomainLine(g2, this, getDomainAxis(), area, 0.0, this.domainZeroBaselinePaint, this.domainZeroBaselineStroke); } } } /** * Draws a base line across the chart at value zero on the range axis. * * @param g2 * the graphics device. * @param area * the data area. * * @see #setRangeZeroBaselineVisible(boolean) */ protected void drawZeroRangeBaseline(Canvas g2, Rectangle2D area) { if (isRangeZeroBaselineVisible()) { getRenderer().drawRangeLine(g2, this, getRangeAxis(), area, 0.0, this.rangeZeroBaselinePaint, this.rangeZeroBaselineStroke); } } /** * Draws the annotations for the plot. * * @param g2 * the graphics device. * @param dataArea * the data area. * @param info * the chart rendering info. */ public void drawAnnotations(Canvas g2, Rectangle2D dataArea, PlotRenderingInfo info) { Iterator iterator = this.annotations.iterator(); while (iterator.hasNext()) { XYAnnotation annotation = (XYAnnotation) iterator.next(); ValueAxis xAxis = getDomainAxis(); ValueAxis yAxis = getRangeAxis(); annotation.draw(g2, this, dataArea, xAxis, yAxis, 0, info); } } /** * Draws the domain markers (if any) for an axis and layer. This method is * typically called from within the draw() method. * * @param g2 * the graphics device. * @param dataArea * the data area. * @param index * the renderer index. * @param layer * the layer (foreground or background). */ protected void drawDomainMarkers(Canvas g2, Rectangle2D dataArea, int index, Layer layer) { XYItemRenderer r = getRenderer(index); if (r == null) { return; } // check that the renderer has a corresponding dataset (it doesn't // matter if the dataset is null) if (index >= getDatasetCount()) { return; } Collection markers = getDomainMarkers(index, layer); ValueAxis axis = getDomainAxisForDataset(index); if (markers != null && axis != null) { Iterator iterator = markers.iterator(); while (iterator.hasNext()) { Marker marker = (Marker) iterator.next(); r.drawDomainMarker(g2, this, axis, marker, dataArea); } } } /** * Draws the range markers (if any) for a renderer and layer. This method is * typically called from within the draw() method. * * @param g2 * the graphics device. * @param dataArea * the data area. * @param index * the renderer index. * @param layer * the layer (foreground or background). */ protected void drawRangeMarkers(Canvas g2, Rectangle2D dataArea, int index, Layer layer) { XYItemRenderer r = getRenderer(index); if (r == null) { return; } // check that the renderer has a corresponding dataset (it doesn't // matter if the dataset is null) if (index >= getDatasetCount()) { return; } Collection markers = getRangeMarkers(index, layer); ValueAxis axis = getRangeAxisForDataset(index); if (markers != null && axis != null) { Iterator iterator = markers.iterator(); while (iterator.hasNext()) { Marker marker = (Marker) iterator.next(); r.drawRangeMarker(g2, this, axis, marker, dataArea); } } } /** * Returns the list of domain markers (read only) for the specified layer. * * @param layer * the layer (foreground or background). * * @return The list of domain markers. * * @see #getRangeMarkers(Layer) */ public Collection getDomainMarkers(Layer layer) { return getDomainMarkers(0, layer); } /** * Returns the list of range markers (read only) for the specified layer. * * @param layer * the layer (foreground or background). * * @return The list of range markers. * * @see #getDomainMarkers(Layer) */ public Collection getRangeMarkers(Layer layer) { return getRangeMarkers(0, layer); } /** * Returns a collection of domain markers for a particular renderer and * layer. * * @param index * the renderer index. * @param layer * the layer. * * @return A collection of markers (possibly <code>null</code>). * * @see #getRangeMarkers(int, Layer) */ public Collection getDomainMarkers(int index, Layer layer) { Collection result = null; Integer key = new Integer(index); if (layer == Layer.FOREGROUND) { result = (Collection) this.foregroundDomainMarkers.get(key); } else if (layer == Layer.BACKGROUND) { result = (Collection) this.backgroundDomainMarkers.get(key); } if (result != null) { result = Collections.unmodifiableCollection(result); } return result; } /** * Returns a collection of range markers for a particular renderer and * layer. * * @param index * the renderer index. * @param layer * the layer. * * @return A collection of markers (possibly <code>null</code>). * * @see #getDomainMarkers(int, Layer) */ public Collection getRangeMarkers(int index, Layer layer) { Collection result = null; Integer key = new Integer(index); if (layer == Layer.FOREGROUND) { result = (Collection) this.foregroundRangeMarkers.get(key); } else if (layer == Layer.BACKGROUND) { result = (Collection) this.backgroundRangeMarkers.get(key); } if (result != null) { result = Collections.unmodifiableCollection(result); } return result; } /** * Utility method for drawing a horizontal line across the data area of the * plot. * * @param g2 * the graphics device. * @param dataArea * the data area. * @param value * the coordinate, where to draw the line. * @param stroke * the stroke to use. * @param paint * the paint to use. */ protected void drawHorizontalLine(Canvas g2, Rectangle2D dataArea, double value, Float stroke, Paint paint) { ValueAxis axis = getRangeAxis(); if (getOrientation() == PlotOrientation.HORIZONTAL) { axis = getDomainAxis(); } if (axis.getRange().contains(value)) { double yy = axis.valueToJava2D(value, dataArea, RectangleEdge.LEFT); Line2D line = new Line2D.Double(dataArea.getMinX(), yy, dataArea.getMaxX(), yy); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(stroke); g2.drawLine((float) line.getX1(), (float) line.getY1(), (float) line.getX2(), (float) line.getY2(), paint); } } /** * Draws a domain crosshair. * * @param g2 * the graphics target. * @param dataArea * the data area. * @param orientation * the plot orientation. * @param value * the crosshair value. * @param axis * the axis against which the value is measured. * @param stroke * the stroke used to draw the crosshair line. * @param paint * the paint used to draw the crosshair line. * * @since 1.0.4 */ protected void drawDomainCrosshair(Canvas g2, Rectangle2D dataArea, PlotOrientation orientation, double value, ValueAxis axis, Float stroke, Paint paint) { if (axis.getRange().contains(value)) { Line2D line = null; if (orientation == PlotOrientation.VERTICAL) { double xx = axis.valueToJava2D(value, dataArea, RectangleEdge.BOTTOM); line = new Line2D.Double(xx, dataArea.getMinY(), xx, dataArea.getMaxY()); } else { double yy = axis.valueToJava2D(value, dataArea, RectangleEdge.LEFT); line = new Line2D.Double(dataArea.getMinX(), yy, dataArea.getMaxX(), yy); } paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(stroke); g2.drawLine((float) line.getX1(), (float) line.getY1(), (float) line.getX2(), (float) line.getY2(), paint); } } /** * Utility method for drawing a vertical line on the data area of the plot. * * @param g2 * the graphics device. * @param dataArea * the data area. * @param value * the coordinate, where to draw the line. * @param stroke * the stroke to use. * @param paint * the paint to use. */ protected void drawVerticalLine(Canvas g2, Rectangle2D dataArea, double value, Float stroke, Paint paint) { ValueAxis axis = getDomainAxis(); if (getOrientation() == PlotOrientation.HORIZONTAL) { axis = getRangeAxis(); } if (axis.getRange().contains(value)) { double xx = axis.valueToJava2D(value, dataArea, RectangleEdge.BOTTOM); Line2D line = new Line2D.Double(xx, dataArea.getMinY(), xx, dataArea.getMaxY()); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(stroke); g2.drawLine((float) line.getX1(), (float) line.getY1(), (float) line.getX2(), (float) line.getY2(), paint); } } /** * Draws a range crosshair. * * @param g2 * the graphics target. * @param dataArea * the data area. * @param orientation * the plot orientation. * @param value * the crosshair value. * @param axis * the axis against which the value is measured. * @param stroke * the stroke used to draw the crosshair line. * @param paint * the paint used to draw the crosshair line. * * @since 1.0.4 */ protected void drawRangeCrosshair(Canvas g2, Rectangle2D dataArea, PlotOrientation orientation, double value, ValueAxis axis, Float stroke, Paint paint) { if (axis.getRange().contains(value)) { Line2D line = null; if (orientation == PlotOrientation.HORIZONTAL) { double xx = axis.valueToJava2D(value, dataArea, RectangleEdge.BOTTOM); line = new Line2D.Double(xx, dataArea.getMinY(), xx, dataArea.getMaxY()); } else { double yy = axis.valueToJava2D(value, dataArea, RectangleEdge.LEFT); line = new Line2D.Double(dataArea.getMinX(), yy, dataArea.getMaxX(), yy); } paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(stroke); g2.drawLine((float) line.getX1(), (float) line.getY1(), (float) line.getX2(), (float) line.getY2(), paint); } } /** * Handles a 'click' on the plot by updating the anchor values. * * @param x * the x-coordinate, where the click occurred, in Java2D space. * @param y * the y-coordinate, where the click occurred, in Java2D space. * @param info * object containing information about the plot dimensions. */ public void handleClick(int x, int y, PlotRenderingInfo info) { Rectangle2D dataArea = info.getDataArea(); if (dataArea.contains(x, y)) { // set the anchor value for the horizontal axis... ValueAxis xaxis = getDomainAxis(); if (xaxis != null) { double hvalue = xaxis.java2DToValue(x, info.getDataArea(), getDomainAxisEdge()); setDomainCrosshairValue(hvalue); } // set the anchor value for the vertical axis... ValueAxis yaxis = getRangeAxis(); if (yaxis != null) { double vvalue = yaxis.java2DToValue(y, info.getDataArea(), getRangeAxisEdge()); setRangeCrosshairValue(vvalue); } } } /** * A utility method that returns a list of datasets that are mapped to a * particular axis. * * @param axisIndex * the axis index (<code>null</code> not permitted). * * @return A list of datasets. */ private List getDatasetsMappedToDomainAxis(Integer axisIndex) { if (axisIndex == null) { throw new IllegalArgumentException("Null 'axisIndex' argument."); } List result = new ArrayList(); for (int i = 0; i < this.datasets.size(); i++) { List mappedAxes = (List) this.datasetToDomainAxesMap.get(new Integer(i)); if (mappedAxes == null) { if (axisIndex.equals(ZERO)) { result.add(this.datasets.get(i)); } } else { if (mappedAxes.contains(axisIndex)) { result.add(this.datasets.get(i)); } } } return result; } /** * A utility method that returns a list of datasets that are mapped to a * particular axis. * * @param axisIndex * the axis index (<code>null</code> not permitted). * * @return A list of datasets. */ private List getDatasetsMappedToRangeAxis(Integer axisIndex) { if (axisIndex == null) { throw new IllegalArgumentException("Null 'axisIndex' argument."); } List result = new ArrayList(); for (int i = 0; i < this.datasets.size(); i++) { List mappedAxes = (List) this.datasetToRangeAxesMap.get(new Integer(i)); if (mappedAxes == null) { if (axisIndex.equals(ZERO)) { result.add(this.datasets.get(i)); } } else { if (mappedAxes.contains(axisIndex)) { result.add(this.datasets.get(i)); } } } return result; } /** * Returns the index of the given domain axis. * * @param axis * the axis. * * @return The axis index. * * @see #getRangeAxisIndex(ValueAxis) */ public int getDomainAxisIndex(ValueAxis axis) { int result = this.domainAxes.indexOf(axis); if (result < 0) { // try the parent plot Plot parent = getParent(); if (parent instanceof XYPlot) { XYPlot p = (XYPlot) parent; result = p.getDomainAxisIndex(axis); } } return result; } /** * Returns the index of the given range axis. * * @param axis * the axis. * * @return The axis index. * * @see #getDomainAxisIndex(ValueAxis) */ public int getRangeAxisIndex(ValueAxis axis) { int result = this.rangeAxes.indexOf(axis); if (result < 0) { // try the parent plot Plot parent = getParent(); if (parent instanceof XYPlot) { XYPlot p = (XYPlot) parent; result = p.getRangeAxisIndex(axis); } } return result; } /** * Returns the range for the specified axis. * * @param axis * the axis. * * @return The range. */ public Range getDataRange(ValueAxis axis) { Range result = null; List mappedDatasets = new ArrayList(); List includedAnnotations = new ArrayList(); boolean isDomainAxis = true; // is it a domain axis? int domainIndex = getDomainAxisIndex(axis); if (domainIndex >= 0) { isDomainAxis = true; mappedDatasets.addAll(getDatasetsMappedToDomainAxis(new Integer(domainIndex))); if (domainIndex == 0) { // grab the plot's annotations Iterator iterator = this.annotations.iterator(); while (iterator.hasNext()) { XYAnnotation annotation = (XYAnnotation) iterator.next(); if (annotation instanceof XYAnnotationBoundsInfo) { includedAnnotations.add(annotation); } } } } // or is it a range axis? int rangeIndex = getRangeAxisIndex(axis); if (rangeIndex >= 0) { isDomainAxis = false; mappedDatasets.addAll(getDatasetsMappedToRangeAxis(new Integer(rangeIndex))); if (rangeIndex == 0) { Iterator iterator = this.annotations.iterator(); while (iterator.hasNext()) { XYAnnotation annotation = (XYAnnotation) iterator.next(); if (annotation instanceof XYAnnotationBoundsInfo) { includedAnnotations.add(annotation); } } } } // iterate through the datasets that map to the axis and get the union // of the ranges. Iterator iterator = mappedDatasets.iterator(); while (iterator.hasNext()) { XYDataset d = (XYDataset) iterator.next(); if (d != null) { XYItemRenderer r = getRendererForDataset(d); if (isDomainAxis) { if (r != null) { result = Range.combine(result, r.findDomainBounds(d)); } else { result = Range.combine(result, DatasetUtilities.findDomainBounds(d)); } } else { if (r != null) { result = Range.combine(result, r.findRangeBounds(d)); } else { result = Range.combine(result, DatasetUtilities.findRangeBounds(d)); } } // FIXME: the XYItemRenderer interface doesn't specify the // getAnnotations() method but it should if (r instanceof AbstractXYItemRenderer) { AbstractXYItemRenderer rr = (AbstractXYItemRenderer) r; Collection c = rr.getAnnotations(); Iterator i = c.iterator(); while (i.hasNext()) { XYAnnotation a = (XYAnnotation) i.next(); if (a instanceof XYAnnotationBoundsInfo) { includedAnnotations.add(a); } } } } } Iterator it = includedAnnotations.iterator(); while (it.hasNext()) { XYAnnotationBoundsInfo xyabi = (XYAnnotationBoundsInfo) it.next(); if (xyabi.getIncludeInDataBounds()) { if (isDomainAxis) { result = Range.combine(result, xyabi.getXRange()); } else { result = Range.combine(result, xyabi.getYRange()); } } } return result; } /* *//** * Receives notification of a change to the plot's dataset. * <P> * The axis ranges are updated if necessary. * * @param event * information about the event (not used here). */ /* * public void datasetChanged(DatasetChangeEvent event) { * configureDomainAxes(); configureRangeAxes(); if (getParent() != null) { * getParent().datasetChanged(event); } else { PlotChangeEvent e = new * PlotChangeEvent(this); e.setType(ChartChangeEventType.DATASET_UPDATED); * notifyListeners(e); } } */ /** * Receives notification of a renderer change event. * * @param event * the event. */ public void rendererChanged(RendererChangeEvent event) { // if the event was caused by a change to series visibility, then // the axis ranges might need updating... if (event.getSeriesVisibilityChanged()) { configureDomainAxes(); configureRangeAxes(); } // fireChangeEvent(); } /** * Returns a flag indicating whether or not the domain crosshair is visible. * * @return The flag. * * @see #setDomainCrosshairVisible(boolean) */ public boolean isDomainCrosshairVisible() { return this.domainCrosshairVisible; } /** * Sets the flag indicating whether or not the domain crosshair is visible * and, if the flag changes, sends a {@link PlotChangeEvent} to all * registered listeners. * * @param flag * the new value of the flag. * * @see #isDomainCrosshairVisible() */ public void setDomainCrosshairVisible(boolean flag) { if (this.domainCrosshairVisible != flag) { this.domainCrosshairVisible = flag; // fireChangeEvent(); } } /** * Returns a flag indicating whether or not the crosshair should "lock-on" * to actual data values. * * @return The flag. * * @see #setDomainCrosshairLockedOnData(boolean) */ public boolean isDomainCrosshairLockedOnData() { return this.domainCrosshairLockedOnData; } /** * Sets the flag indicating whether or not the domain crosshair should * "lock-on" to actual data values. If the flag value changes, this method * sends a {@link PlotChangeEvent} to all registered listeners. * * @param flag * the flag. * * @see #isDomainCrosshairLockedOnData() */ public void setDomainCrosshairLockedOnData(boolean flag) { if (this.domainCrosshairLockedOnData != flag) { this.domainCrosshairLockedOnData = flag; // fireChangeEvent(); } } /** * Returns the domain crosshair value. * * @return The value. * * @see #setDomainCrosshairValue(double) */ public double getDomainCrosshairValue() { return this.domainCrosshairValue; } /** * Sets the domain crosshair value and sends a {@link PlotChangeEvent} to * all registered listeners (provided that the domain crosshair is visible). * * @param value * the value. * * @see #getDomainCrosshairValue() */ public void setDomainCrosshairValue(double value) { setDomainCrosshairValue(value, true); } /** * Sets the domain crosshair value and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners (provided that the * domain crosshair is visible). * * @param value * the new value. * @param notify * notify listeners? * * @see #getDomainCrosshairValue() */ public void setDomainCrosshairValue(double value, boolean notify) { this.domainCrosshairValue = value; if (isDomainCrosshairVisible() && notify) { // fireChangeEvent(); } } /** * Returns the {@link Stroke} used to draw the crosshair (if visible). * * @return The crosshair stroke (never <code>null</code>). * * @see #setDomainCrosshairStroke(Stroke) * @see #isDomainCrosshairVisible() * @see #getDomainCrosshairPaint() */ public Float getDomainCrosshairStroke() { return this.domainCrosshairStroke; } /** * Sets the Stroke used to draw the crosshairs (if visible) and notifies * registered listeners that the axis has been modified. * * @param stroke * the new crosshair stroke (<code>null</code> not permitted). * * @see #getDomainCrosshairStroke() */ public void setDomainCrosshairStroke(Float stroke) { if (stroke == null) { throw new IllegalArgumentException("Null 'stroke' argument."); } this.domainCrosshairStroke = stroke; // fireChangeEvent(); } /** * Returns the domain crosshair paint. * * @return The crosshair paint (never <code>null</code>). * * @see #setDomainCrosshairPaint(Paint) * @see #isDomainCrosshairVisible() * @see #getDomainCrosshairStroke() */ public Paint getDomainCrosshairPaint() { return this.domainCrosshairPaint; } /** * Sets the paint used to draw the crosshairs (if visible) and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param paint * the new crosshair paint (<code>null</code> not permitted). * * @see #getDomainCrosshairPaint() */ public void setDomainCrosshairPaint(Paint paint) { if (paint == null) { throw new IllegalArgumentException("Null 'paint' argument."); } this.domainCrosshairPaint = paint; // fireChangeEvent(); } /** * Returns a flag indicating whether or not the range crosshair is visible. * * @return The flag. * * @see #setRangeCrosshairVisible(boolean) * @see #isDomainCrosshairVisible() */ public boolean isRangeCrosshairVisible() { return this.rangeCrosshairVisible; } /** * Sets the flag indicating whether or not the range crosshair is visible. * If the flag value changes, this method sends a {@link PlotChangeEvent} to * all registered listeners. * * @param flag * the new value of the flag. * * @see #isRangeCrosshairVisible() */ public void setRangeCrosshairVisible(boolean flag) { if (this.rangeCrosshairVisible != flag) { this.rangeCrosshairVisible = flag; // fireChangeEvent(); } } /** * Returns a flag indicating whether or not the crosshair should "lock-on" * to actual data values. * * @return The flag. * * @see #setRangeCrosshairLockedOnData(boolean) */ public boolean isRangeCrosshairLockedOnData() { return this.rangeCrosshairLockedOnData; } /** * Sets the flag indicating whether or not the range crosshair should * "lock-on" to actual data values. If the flag value changes, this method * sends a {@link PlotChangeEvent} to all registered listeners. * * @param flag * the flag. * * @see #isRangeCrosshairLockedOnData() */ public void setRangeCrosshairLockedOnData(boolean flag) { if (this.rangeCrosshairLockedOnData != flag) { this.rangeCrosshairLockedOnData = flag; // fireChangeEvent(); } } /** * Returns the range crosshair value. * * @return The value. * * @see #setRangeCrosshairValue(double) */ public double getRangeCrosshairValue() { return this.rangeCrosshairValue; } /** * Sets the range crosshair value. * <P> * Registered listeners are notified that the plot has been modified, but * only if the crosshair is visible. * * @param value * the new value. * * @see #getRangeCrosshairValue() */ public void setRangeCrosshairValue(double value) { setRangeCrosshairValue(value, true); } /** * Sets the range crosshair value and sends a {@link PlotChangeEvent} to all * registered listeners, but only if the crosshair is visible. * * @param value * the new value. * @param notify * a flag that controls whether or not listeners are notified. * * @see #getRangeCrosshairValue() */ public void setRangeCrosshairValue(double value, boolean notify) { this.rangeCrosshairValue = value; if (isRangeCrosshairVisible() && notify) { // fireChangeEvent(); } } /** * Returns the stroke used to draw the crosshair (if visible). * * @return The crosshair stroke (never <code>null</code>). * * @see #setRangeCrosshairStroke(Stroke) * @see #isRangeCrosshairVisible() * @see #getRangeCrosshairPaint() */ public Float getRangeCrosshairStroke() { return this.rangeCrosshairStroke; } /** * Sets the stroke used to draw the crosshairs (if visible) and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param stroke * the new crosshair stroke (<code>null</code> not permitted). * * @see #getRangeCrosshairStroke() */ public void setRangeCrosshairStroke(Float stroke) { if (stroke == null) { throw new IllegalArgumentException("Null 'stroke' argument."); } this.rangeCrosshairStroke = stroke; // fireChangeEvent(); } /** * Returns the range crosshair paint. * * @return The crosshair paint (never <code>null</code>). * * @see #setRangeCrosshairPaint(Paint) * @see #isRangeCrosshairVisible() * @see #getRangeCrosshairStroke() */ public Paint getRangeCrosshairPaint() { return this.rangeCrosshairPaint; } /** * Sets the paint used to color the crosshairs (if visible) and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param paint * the new crosshair paint (<code>null</code> not permitted). * * @see #getRangeCrosshairPaint() */ public void setRangeCrosshairPaint(Paint paint) { if (paint == null) { throw new IllegalArgumentException("Null 'paint' argument."); } this.rangeCrosshairPaint = paint; // fireChangeEvent(); } /** * Returns the fixed domain axis space. * * @return The fixed domain axis space (possibly <code>null</code>). * * @see #setFixedDomainAxisSpace(AxisSpace) */ public AxisSpace getFixedDomainAxisSpace() { return this.fixedDomainAxisSpace; } /** * Sets the fixed domain axis space and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param space * the space (<code>null</code> permitted). * * @see #getFixedDomainAxisSpace() */ public void setFixedDomainAxisSpace(AxisSpace space) { setFixedDomainAxisSpace(space, true); } /** * Sets the fixed domain axis space and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param space * the space (<code>null</code> permitted). * @param notify * notify listeners? * * @see #getFixedDomainAxisSpace() * * @since 1.0.9 */ public void setFixedDomainAxisSpace(AxisSpace space, boolean notify) { this.fixedDomainAxisSpace = space; if (notify) { // fireChangeEvent(); } } /** * Returns the fixed range axis space. * * @return The fixed range axis space (possibly <code>null</code>). * * @see #setFixedRangeAxisSpace(AxisSpace) */ public AxisSpace getFixedRangeAxisSpace() { return this.fixedRangeAxisSpace; } /** * Sets the fixed range axis space and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param space * the space (<code>null</code> permitted). * * @see #getFixedRangeAxisSpace() */ public void setFixedRangeAxisSpace(AxisSpace space) { setFixedRangeAxisSpace(space, true); } /** * Sets the fixed range axis space and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param space * the space (<code>null</code> permitted). * @param notify * notify listeners? * * @see #getFixedRangeAxisSpace() * * @since 1.0.9 */ public void setFixedRangeAxisSpace(AxisSpace space, boolean notify) { this.fixedRangeAxisSpace = space; if (notify) { // fireChangeEvent(); } } /** * Returns <code>true</code> if panning is enabled for the domain axes, and * <code>false</code> otherwise. * * @return A boolean. * * @since 1.0.13 */ public boolean isDomainPannable() { return this.domainPannable; } /** * Sets the flag that enables or disables panning of the plot along the * domain axes. * * @param pannable * the new flag value. * * @since 1.0.13 */ public void setDomainPannable(boolean pannable) { this.domainPannable = pannable; } /** * Returns <code>true</code> if panning is enabled for the range axes, and * <code>false</code> otherwise. * * @return A boolean. * * @since 1.0.13 */ public boolean isRangePannable() { return this.rangePannable; } /** * Sets the flag that enables or disables panning of the plot along the * range axes. * * @param pannable * the new flag value. * * @since 1.0.13 */ public void setRangePannable(boolean pannable) { this.rangePannable = pannable; } /** * Pans the domain axes by the specified percentage. * * @param percent * the distance to pan (as a percentage of the axis length). * @param info * the plot info * @param source * the source point where the pan action started. * * @since 1.0.13 */ public void panDomainAxes(double percent, PlotRenderingInfo info, Point2D source) { if (!isDomainPannable()) { return; } int domainAxisCount = getDomainAxisCount(); for (int i = 0; i < domainAxisCount; i++) { ValueAxis axis = getDomainAxis(i); if (axis == null) { continue; } if (axis.isInverted()) { percent = -percent; } axis.pan(percent); } } /** * Pans the range axes by the specified percentage. * * @param percent * the distance to pan (as a percentage of the axis length). * @param info * the plot info * @param source * the source point where the pan action started. * * @since 1.0.13 */ public void panRangeAxes(double percent, PlotRenderingInfo info, Point2D source) { if (!isRangePannable()) { return; } int rangeAxisCount = getRangeAxisCount(); for (int i = 0; i < rangeAxisCount; i++) { ValueAxis axis = getRangeAxis(i); if (axis == null) { continue; } if (axis.isInverted()) { percent = -percent; } axis.pan(percent); } } /** * Multiplies the range on the domain axis/axes by the specified factor. * * @param factor * the zoom factor. * @param info * the plot rendering info. * @param source * the source point (in Java2D space). * * @see #zoomRangeAxes(double, PlotRenderingInfo, Point2D) */ public void zoomDomainAxes(double factor, PlotRenderingInfo info, Point2D source) { // delegate to other method zoomDomainAxes(factor, info, source, false); } /** * Multiplies the range on the domain axis/axes by the specified factor. * * @param factor * the zoom factor. * @param info * the plot rendering info. * @param source * the source point (in Java2D space). * @param useAnchor * use source point as zoom anchor? * * @see #zoomRangeAxes(double, PlotRenderingInfo, Point2D, boolean) * * @since 1.0.7 */ public void zoomDomainAxes(double factor, PlotRenderingInfo info, Point2D source, boolean useAnchor) { // perform the zoom on each domain axis for (int i = 0; i < this.domainAxes.size(); i++) { ValueAxis domainAxis = (ValueAxis) this.domainAxes.get(i); if (domainAxis != null) { if (useAnchor) { // get the relevant source coordinate given the plot // orientation double sourceX = source.getX(); if (this.orientation == PlotOrientation.HORIZONTAL) { sourceX = source.getY(); } double anchorX = domainAxis.java2DToValue(sourceX, info.getDataArea(), getDomainAxisEdge()); domainAxis.resizeRange2(factor, anchorX); } else { domainAxis.resizeRange(factor); } } } } /** * Zooms in on the domain axis/axes. The new lower and upper bounds are * specified as percentages of the current axis range, where 0 percent is * the current lower bound and 100 percent is the current upper bound. * * @param lowerPercent * a percentage that determines the new lower bound for the axis * (e.g. 0.20 is twenty percent). * @param upperPercent * a percentage that determines the new upper bound for the axis * (e.g. 0.80 is eighty percent). * @param info * the plot rendering info. * @param source * the source point (ignored). * * @see #zoomRangeAxes(double, double, PlotRenderingInfo, Point2D) */ public void zoomDomainAxes(double lowerPercent, double upperPercent, PlotRenderingInfo info, Point2D source) { for (int i = 0; i < this.domainAxes.size(); i++) { ValueAxis domainAxis = (ValueAxis) this.domainAxes.get(i); if (domainAxis != null) { domainAxis.zoomRange(lowerPercent, upperPercent); } } } /** * Multiplies the range on the range axis/axes by the specified factor. * * @param factor * the zoom factor. * @param info * the plot rendering info. * @param source * the source point. * * @see #zoomDomainAxes(double, PlotRenderingInfo, Point2D, boolean) */ public void zoomRangeAxes(double factor, PlotRenderingInfo info, Point2D source) { // delegate to other method zoomRangeAxes(factor, info, source, false); } /** * Multiplies the range on the range axis/axes by the specified factor. * * @param factor * the zoom factor. * @param info * the plot rendering info. * @param source * the source point. * @param useAnchor * a flag that controls whether or not the source point is used * for the zoom anchor. * * @see #zoomDomainAxes(double, PlotRenderingInfo, Point2D, boolean) * * @since 1.0.7 */ public void zoomRangeAxes(double factor, PlotRenderingInfo info, Point2D source, boolean useAnchor) { // perform the zoom on each range axis for (int i = 0; i < this.rangeAxes.size(); i++) { ValueAxis rangeAxis = (ValueAxis) this.rangeAxes.get(i); if (rangeAxis != null) { if (useAnchor) { // get the relevant source coordinate given the plot // orientation double sourceY = source.getY(); if (this.orientation == PlotOrientation.HORIZONTAL) { sourceY = source.getX(); } double anchorY = rangeAxis.java2DToValue(sourceY, info.getDataArea(), getRangeAxisEdge()); rangeAxis.resizeRange2(factor, anchorY); } else { rangeAxis.resizeRange(factor); } } } } /** * Zooms in on the range axes. * * @param lowerPercent * the lower bound. * @param upperPercent * the upper bound. * @param info * the plot rendering info. * @param source * the source point. * * @see #zoomDomainAxes(double, double, PlotRenderingInfo, Point2D) */ public void zoomRangeAxes(double lowerPercent, double upperPercent, PlotRenderingInfo info, Point2D source) { for (int i = 0; i < this.rangeAxes.size(); i++) { ValueAxis rangeAxis = (ValueAxis) this.rangeAxes.get(i); if (rangeAxis != null) { rangeAxis.zoomRange(lowerPercent, upperPercent); } } } /** * Returns <code>true</code>, indicating that the domain axis/axes for this * plot are zoomable. * * @return A boolean. * * @see #isRangeZoomable() */ public boolean isDomainZoomable() { return true; } /** * Returns <code>true</code>, indicating that the range axis/axes for this * plot are zoomable. * * @return A boolean. * * @see #isDomainZoomable() */ public boolean isRangeZoomable() { return true; } /** * Returns the number of series in the primary dataset for this plot. If the * dataset is <code>null</code>, the method returns 0. * * @return The series count. */ public int getSeriesCount() { int result = 0; XYDataset dataset = getDataset(); if (dataset != null) { result = dataset.getSeriesCount(); } return result; } /** * Returns the fixed legend items, if any. * * @return The legend items (possibly <code>null</code>). * * @see #setFixedLegendItems(LegendItemCollection) */ public LegendItemCollection getFixedLegendItems() { return this.fixedLegendItems; } /** * Sets the fixed legend items for the plot. Leave this set to * <code>null</code> if you prefer the legend items to be created * automatically. * * @param items * the legend items (<code>null</code> permitted). * * @see #getFixedLegendItems() */ public void setFixedLegendItems(LegendItemCollection items) { this.fixedLegendItems = items; // fireChangeEvent(); } /** * Returns the legend items for the plot. Each legend item is generated by * the plot's renderer, since the renderer is responsible for the visual * representation of the data. * * @return The legend items. */ public LegendItemCollection getLegendItems() { if (this.fixedLegendItems != null) { return this.fixedLegendItems; } LegendItemCollection result = new LegendItemCollection(); int count = this.datasets.size(); for (int datasetIndex = 0; datasetIndex < count; datasetIndex++) { XYDataset dataset = getDataset(datasetIndex); if (dataset != null) { XYItemRenderer renderer = getRenderer(datasetIndex); if (renderer == null) { renderer = getRenderer(0); } if (renderer != null) { int seriesCount = dataset.getSeriesCount(); for (int i = 0; i < seriesCount; i++) { if (renderer.isSeriesVisible(i) && renderer.isSeriesVisibleInLegend(i)) { LegendItem item = renderer.getLegendItem(datasetIndex, i); if (item != null) { result.add(item); } } } } } } return result; } }