Click here to Skip to main content
15,896,475 members
Articles / Multimedia / GDI+

A flexible charting library for .NET

Rate me:
Please Sign up or sign in to vote.
4.70/5 (1,112 votes)
6 Jun 200730 min read 9.1M   180.2K   2.1K  
Looking for a way to draw 2D line graphs with C#? Here's yet another charting class library with a high degree of configurability, that is also easy to use.
//============================================================================
//ZedGraph Class Library - A Flexible Line Graph/Bar Graph Library in C#
//Copyright (C) 2004  John Champion
//
//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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//=============================================================================

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections ;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.ComponentModel;

namespace ZedGraph
{
	// <summary>
	// <c>ZedGraph</c> is a class library and UserControl (<see cref="ZedGraphControl"/>) that display
	// 2D line graphs of user specified data.  The <c>ZedGraph</c> namespace includes all functionality
	// required to draw, modify, and update the graph.
	// </summary>
	
	/// <summary>
	/// Class <see cref="GraphPane"/> encapsulates the graph pane, which is all display elements
	/// associated with an individual graph.
	/// </summary>
	/// <remarks>This class is the outside "wrapper"
	/// for the ZedGraph classes, and provides the interface to access the attributes
	/// of the graph.  You can have multiple graphs in the same document or form,
	/// just instantiate multiple GraphPane's.
	/// </remarks>
	/// 
	/// <author> John Champion modified by Jerry Vos </author>
	/// <version> $Revision: 3.47 $ $Date: 2005/08/03 02:53:52 $ </version>
	[Serializable]
	public class GraphPane : PaneBase, ICloneable, ISerializable
	{
	#region Private Fields
	
		// Item subclasses ////////////////////////////////////////////////////////////////////
		
		/// <summary>Private field instance of the <see cref="ZedGraph.XAxis"/> class.  Use the
		/// public property <see cref="GraphPane.XAxis"/> to access this class.</summary>
		private XAxis		xAxis;
		/// <summary>Private field instance of the <see cref="ZedGraph.YAxis"/> class.  Use the
		/// public property <see cref="GraphPane.YAxis"/> to access this class.</summary>
		private YAxis		yAxis;
		/// <summary>Private field instance of the <see cref="ZedGraph.Y2Axis"/> class.  Use the
		/// public property <see cref="GraphPane.Y2Axis"/> to access this class.</summary>
		private Y2Axis		y2Axis;
		/// <summary>Private field instance of the <see cref="ZedGraph.CurveList"/> class.  Use the
		/// public property <see cref="GraphPane.CurveList"/> to access this class.</summary>
		private CurveList	curveList;
						
		/// <summary>
		/// private value that contains a <see cref="ZoomStateStack"/>, which stores prior
		/// <see cref="ZoomState"/> objects containing scale range information.  This enables
		/// zooming and panning functionality for the <see cref="ZedGraphControl"/>.
		/// </summary>
		internal ZoomStateStack zoomStack;
		
		// Axis Border Properties //////////////////////////////////////////////////////////////
		
		/// <summary>Private field that determines if the <see cref="AxisRect"/> will be
		/// sized automatically.  Use the public property <see cref="IsAxisRectAuto"/> to access
		/// this value. </summary>
		private bool isAxisRectAuto;
		/// <summary>
		/// Private field that stores the <see cref="ZedGraph.Fill"/> data for this
		/// <see cref="AxisRect"/>.  Use the public property <see cref="AxisFill"/> to
		/// access this value.
		/// </summary>
		private Fill axisFill;
		/// <summary>
		/// Private field that stores the <see cref="ZedGraph.Border"/> data for this
		/// <see cref="AxisRect"/>.  Use the public property <see cref="AxisBorder"/> to
		/// access this value.
		/// </summary>
		private Border axisBorder;
		
		/// <summary>Private field that determines whether or not initial zero values will
		/// be included or excluded when determining the Y or Y2 axis scale range.
		/// Use the public property <see cref="IsIgnoreInitial"/> to access
		/// this value. </summary>
		private bool		isIgnoreInitial;
		/// <summary>Private field that determines whether or not initial
		/// <see cref="PointPair.Missing"/> values will cause the line segments of
		/// a curve to be discontinuous.  If this field is true, then the curves
		/// will be plotted as continuous lines as if the Missing values did not
		/// exist.
		/// Use the public property <see cref="IsIgnoreMissing"/> to access
		/// this value. </summary>
		private bool		isIgnoreMissing;
		/// <summary> private field that determines if the auto-scaled axis ranges will subset the
		/// data points based on any manually set scale range values.  Use the public property
		/// <see cref="IsBoundedRanges"/> to access this value.</summary>
		/// <remarks>The bounds provide a means to subset the data.  For example, if all the axes are set to
		/// autoscale, then the full range of data are used.  But, if the XAxis.Min and XAxis.Max values
		/// are manually set, then the Y data range will reflect the Y values within the bounds of
		/// XAxis.Min and XAxis.Max.</remarks>
		private bool		isBoundedRanges;
		/// <summary>Private field that determines the size of the gap between bar clusters
		/// for bar charts.  This gap is expressed as a fraction of the bar size (1.0 means
		/// leave a 1-barwidth gap between clusters).
		/// Use the public property <see cref="MinClusterGap"/> to access this value. </summary>
		private float		minClusterGap;
		/// <summary>Private field that determines the size of the gap between individual bars
		/// within a bar cluster for bar charts.  This gap is expressed as a fraction of the
		/// bar size (1.0 means leave a 1-barwidth gap between each bar).
		/// Use the public property <see cref="MinBarGap"/> to access this value. </summary>
		private float		minBarGap;
		/// <summary>Private field that determines the base axis from which <see cref="Bar"/>
		/// graphs will be displayed.  The base axis is the axis from which the bars grow with
		/// increasing value. The value is of the enumeration type <see cref="ZedGraph.BarBase"/>.
		/// To access this value, use the public property <see cref="BarBase"/>.
		/// </summary>
		/// <seealso cref="Default.BarBase"/>
		private BarBase		barBase;
		/// <summary>Private field that determines how the <see cref="BarItem"/>
		/// graphs will be displayed. See the <see cref="ZedGraph.BarType"/> enum
		/// for the individual types available.
		/// To access this value, use the public property <see cref="BarType"/>.
		/// </summary>
		/// <seealso cref="Default.BarType"/>
		private BarType barType;
		/// <summary>Private field that determines the width of a bar cluster (for bar charts)
		/// in user scale units.  Normally, this value is 1.0 because bar charts are typically
		/// <see cref="AxisType.Ordinal"/> or <see cref="AxisType.Text"/>, and the bars are
		/// defined at ordinal values (1.0 scale units apart).  For <see cref="AxisType.Linear"/>
		/// or other scale types, you can use this value to scale the bars to an arbitrary
		/// user scale. Use the public property <see cref="ClusterScaleWidth"/> to access this
		/// value. </summary>
		private double		clusterScaleWidth;

		/// <summary>Private field that determines how the <see cref="LineItem"/>
		/// graphs will be displayed. See the <see cref="ZedGraph.LineType"/> enum
		/// for the individual types available.
		/// To access this value, use the public property <see cref="LineType"/>.
		/// </summary>
		/// <seealso cref="Default.LineType"/>
		private LineType lineType;
		
		/// <summary>
		/// The rectangle that contains the area bounded by the axes, in
		/// pixel units
		/// </summary>
		private RectangleF	axisRect;			// The area of the pane defined by the axes
		/// <summary>
		/// The largest square contained in <see cref="GraphPane.axisRect"/>, in
		/// pixel units, used for drawing <see cref="PieItem"/> object.
		/// Use the public property <see cref="PieRect"/> to access this value.
		/// </summary>
		private RectangleF	pieRect;			
		
	#endregion

	#region Defaults
		/// <summary>
		/// A simple struct that defines the
		/// default property values for the <see cref="GraphPane"/> class.
		/// </summary>
		public new struct Default
		{			
			/// <summary>
			/// The default color for the <see cref="Axis"/> border border.
			/// (<see cref="GraphPane.AxisBorder"/> property). 
			/// </summary>
			public static Color AxisBorderColor = Color.Black;
			/// <summary>
			/// The default color for the <see cref="GraphPane.AxisRect"/> background.
			/// (<see cref="GraphPane.AxisFill"/> property). 
			/// </summary>
			public static Color AxisBackColor = Color.White;
			/// <summary>
			/// The default brush for the <see cref="GraphPane.AxisRect"/> background.
			/// (<see cref="ZedGraph.Fill.Brush"/> property of <see cref="GraphPane.AxisFill"/>). 
			/// </summary>
			public static Brush AxisBackBrush = null;
			/// <summary>
			/// The default <see cref="FillType"/> for the <see cref="GraphPane.AxisRect"/> background.
			/// (<see cref="ZedGraph.Fill.Type"/> property of <see cref="GraphPane.AxisFill"/>). 
			/// </summary>
			public static FillType AxisBackType = FillType.Brush;
			/// <summary>
			/// The default pen width for drawing the 
			/// <see cref="GraphPane.AxisRect"/> border border
			/// (<see cref="GraphPane.AxisBorder"/> property).
			/// Units are in points (1/72 inch).
			/// </summary>
			public static float AxisBorderPenWidth = 1F;
			/// <summary>
			/// The default display mode for the <see cref="Axis"/> border border
			/// (<see cref="GraphPane.AxisBorder"/> property). true
			/// to show the border border, false to omit the border
			/// </summary>
			public static bool IsAxisBorderVisible = true;

			/// <summary>
			/// The default settings for the <see cref="Axis"/> scale ignore initial
			/// zero values option (<see cref="GraphPane.IsIgnoreInitial"/> property).
			/// true to have the auto-scale-range code ignore the initial data points
			/// until the first non-zero Y value, false otherwise.
			/// </summary>
			public static bool IsIgnoreInitial = false;
			/// <summary>
			/// The default settings for the <see cref="Axis"/> scale bounded ranges option
			/// (<see cref="GraphPane.IsBoundedRanges"/> property).
			/// true to have the auto-scale-range code subset the data according to any
			/// manually set scale values, false otherwise.
			/// </summary>
			public static bool IsBoundedRanges = true;

			/// <summary>
			/// The default dimension gap between clusters of bars on a
			/// <see cref="Bar"/> graph.
			/// This dimension is expressed in terms of the normal bar width.
			/// </summary>
			/// <seealso cref="Default.MinBarGap"/>
			/// <seealso cref="GraphPane.MinClusterGap"/>
			public static float MinClusterGap = 1.0F;
			/// <summary>
			/// The default dimension gap between each individual bar within a bar cluster
			/// on a <see cref="Bar"/> graph.
			/// This dimension is expressed in terms of the normal bar width.
			/// </summary>
			/// <seealso cref="Default.MinClusterGap"/>
			/// <seealso cref="GraphPane.MinBarGap"/>
			public static float MinBarGap = 0.2F;
			/// <summary>The default value for the <see cref="BarBase"/>, which determines the base
			/// <see cref="Axis"/> from which the <see cref="Bar"/> graphs will be displayed.
			/// </summary>
			/// <seealso cref="GraphPane.BarBase"/>
			public static BarBase BarBase = BarBase.X;
			/// <summary>The default value for the <see cref="GraphPane.BarType"/> property, which
			/// determines if the bars are drawn overlapping eachother in a "stacked" format,
			/// or side-by-side in a "cluster" format.  See the <see cref="ZedGraph.BarType"/>
			/// for more information.
			/// </summary>
			/// <seealso cref="GraphPane.BarType"/>
			public static BarType BarType = BarType.Cluster;
			/// <summary>The default value for the <see cref="GraphPane.LineType"/> property, which
			/// determines if the lines are drawn in normal or "stacked" mode.  See the
			/// <see cref="ZedGraph.LineType"/> for more information.
			/// </summary>
			/// <seealso cref="GraphPane.LineType"/>
			public static LineType LineType = LineType.Normal;
			/// <summary>
			/// The default width of a bar cluster 
			/// on a <see cref="Bar"/> graph.  This value only applies to
			/// <see cref="Bar"/> graphs, and only when the
			/// <see cref="Axis.Type"/> is <see cref="AxisType.Linear"/>,
			/// <see cref="AxisType.Log"/> or <see cref="AxisType.Date"/>.
			/// This dimension is expressed in terms of X scale user units.
			/// </summary>
			/// <seealso cref="Default.MinClusterGap"/>
			/// <seealso cref="GraphPane.MinBarGap"/>
			public static double ClusterScaleWidth = 1.0;
			/// <summary>
			/// The tolerance that is applied to the
			/// <see cref="GraphPane.FindNearestPoint(PointF,out CurveItem,out int)"/> routine.
			/// If a given curve point is within this many pixels of the mousePt, the curve
			/// point is considered to be close enough for selection as a nearest point
			/// candidate.
			/// </summary>
			public static double NearestTol = 7.0;
		}
	#endregion

	#region public Class Instance Properties
		/// <summary>
		/// Gets or sets the list of <see cref="CurveItem"/> items for this <see cref="GraphPane"/>
		/// </summary>
		/// <value>A reference to a <see cref="CurveList"/> collection object</value>
		public CurveList CurveList
		{
			get { return curveList; }
			set { curveList = value; }
		}
		/// <summary>
		/// Accesses the <see cref="XAxis"/> for this graph
		/// </summary>
		/// <value>A reference to a <see cref="XAxis"/> object</value>
		public XAxis XAxis
		{
			get { return xAxis; }
		}
		/// <summary>
		/// Accesses the <see cref="YAxis"/> for this graph
		/// </summary>
		/// <value>A reference to a <see cref="YAxis"/> object</value>
		public YAxis YAxis
		{
			get { return yAxis; }
		}
		/// <summary>
		/// Accesses the <see cref="Y2Axis"/> for this graph
		/// </summary>
		/// <value>A reference to a <see cref="Y2Axis"/> object</value>
		public Y2Axis Y2Axis
		{
			get { return y2Axis; }
		}
	#endregion
	
	#region General Properties
		/// <summary>
		/// Gets or sets a boolean value that affects the data range that is considered
		/// for the automatic scale ranging.
		/// </summary>
		/// <remarks>If true, then initial data points where the Y value
		/// is zero are not included when automatically determining the scale <see cref="Axis.Min"/>,
		/// <see cref="Axis.Max"/>, and <see cref="Axis.Step"/> size.
		/// All data after the first non-zero Y value are included.
		/// </remarks>
		/// <seealso cref="Default.IsIgnoreInitial"/>
		public bool IsIgnoreInitial
		{
			get { return isIgnoreInitial; }
			set { isIgnoreInitial = value; }
		}
		/// <summary> Gets or sets a boolean value that determines if the auto-scaled axis ranges will
		/// subset the data points based on any manually set scale range values.</summary>
		/// <remarks>The bounds provide a means to subset the data.  For example, if all the axes are set to
		/// autoscale, then the full range of data are used.  But, if the XAxis.Min and XAxis.Max values
		/// are manually set, then the Y data range will reflect the Y values within the bounds of
		/// XAxis.Min and XAxis.Max.  Set to true to subset the data, or false to always include
		/// all data points when calculating scale ranges.</remarks>
		public bool IsBoundedRanges
		{
			get { return isBoundedRanges; }
			set { isBoundedRanges = value; }
		}
		/// <summary>Gets or sets a value that determines whether or not initial
		/// <see cref="PointPair.Missing"/> values will cause the line segments of
		/// a curve to be discontinuous.
		/// </summary>
		/// <remarks>If this field is true, then the curves
		/// will be plotted as continuous lines as if the Missing values did not exist.
		/// Use the public property <see cref="IsIgnoreMissing"/> to access
		/// this value. </remarks>
		public bool IsIgnoreMissing
		{
			get { return isIgnoreMissing; }
			set { isIgnoreMissing = value; }
		}

		/// <summary>Determines how the <see cref="LineItem"/>
		/// graphs will be displayed. See the <see cref="ZedGraph.LineType"/> enum
		/// for the individual types available.
		/// </summary>
		/// <seealso cref="Default.LineType"/>
		public LineType LineType
		{
			get { return lineType; }
			set { lineType = value; }
		}
		
	#endregion
		
	#region AxisRect Properties
		/// <summary>
		/// Gets or sets the rectangle that contains the area bounded by the axes
		/// (<see cref="XAxis"/>, <see cref="YAxis"/>, and <see cref="Y2Axis"/>).
		/// If you set this value manually, then the <see cref="IsAxisRectAuto"/>
		/// value will automatically be set to false.
		/// </summary>
		/// <value>The rectangle units are in screen pixels</value>
		public RectangleF AxisRect
		{
			get { return axisRect; }
			set { axisRect = value; this.isAxisRectAuto = false; }
		}
		/// <summary>
		/// IsAxisRectAuto is a boolean value that determines whether or not the 
		/// <see cref="AxisRect"/> will be calculated automatically (almost always true).
		/// </summary>
		/// <remarks>
		/// If you have a need to set the axisRect manually, such as you have multiple graphs
		/// on a page and you want to line up the edges perfectly, you can set this value
		/// to false.  If you set this value to false, you must also manually set
		/// the <see cref="AxisRect"/> property.  Note that the <see cref="PieRect"/> (for Pie
		/// charts) is a function of the <see cref="AxisRect"/>.  Therefore, <see cref="PieRect"/>
		/// will also have to be manually calculated if <see cref="IsAxisRectAuto"/> is false.
		/// You can easily determine the axisRect that ZedGraph would have
		/// calculated by calling the <see cref="CalcAxisRect(Graphics)"/> method, which returns
		/// an axis rect sized for the current data range, scale sizes, etc.
		/// </remarks>
		/// <value>true to have ZedGraph calculate the axisRect, false to do it yourself</value>
		/// <seealso cref="PieItem.CalcPieRect"/>
		public bool IsAxisRectAuto
		{
			get { return isAxisRectAuto; }
			set { isAxisRectAuto = value; }
		}
		/// <summary>
		/// Gets or sets a <see cref="RectangleF"/> that determines the size of the Pie.
		/// </summary>
		/// <remarks>This rectangle is normally square, and slightly smaller than the <see cref="AxisRect"/>.
		/// If you want to set this rectangle manually, you will need to set <see cref="IsAxisRectAuto"/> to
		/// false as well.
		/// </remarks>
		/// <value>The rectangle units are in screen pixels</value>
		public RectangleF PieRect
		{
			get { return pieRect; }
			set { pieRect = value; }
		}

		/// <summary>
		/// Gets or sets the <see cref="ZedGraph.Border"/> class for drawing the border
		/// border around the <see cref="AxisRect"/>
		/// </summary>
		/// <seealso cref="Default.AxisBorderColor"/>
		/// <seealso cref="Default.AxisBorderPenWidth"/>
		public Border AxisBorder
		{
			get { return axisBorder; }
			set { axisBorder = value; }
		}				
		/// <summary>
		/// Gets or sets the <see cref="ZedGraph.Fill"/> data for this
		/// <see cref="AxisRect"/>.
		/// </summary>
		public Fill	AxisFill
		{
			get { return axisFill; }
			set { axisFill = value; }
		}
		#endregion
		
	#region Bar Properties
		/// <summary>
		/// The minimum space between <see cref="Bar"/> clusters, expressed as a
		/// fraction of the bar size.
		/// </summary>
		/// <seealso cref="Default.MinClusterGap"/>
		/// <seealso cref="MinBarGap"/>
		/// <seealso cref="ClusterScaleWidth"/>
		public float MinClusterGap
		{
			get { return minClusterGap; }
			set { minClusterGap = value; }
		}
		/// <summary>
		/// The minimum space between individual <see cref="Bar">Bars</see>
		/// within a cluster, expressed as a
		/// fraction of the bar size.
		/// </summary>
		/// <seealso cref="Default.MinBarGap"/>
		/// <seealso cref="MinClusterGap"/>
		/// <seealso cref="ClusterScaleWidth"/>
		public float MinBarGap
		{
			get { return minBarGap; }
			set { minBarGap = value; }
		}
		/// <summary>Determines the base axis from which <see cref="Bar"/>
		/// graphs will be displayed.  The base axis is the axis from which the bars grow with
		/// increasing value. The value is of the enumeration type <see cref="ZedGraph.BarBase"/>.
		/// </summary>
		/// <seealso cref="Default.BarBase"/>
		public BarBase BarBase
		{
			get { return barBase; }
			set { barBase = value; }
		}
		/// <summary>Determines how the <see cref="BarItem"/>
		/// graphs will be displayed. See the <see cref="ZedGraph.BarType"/> enum
		/// for the individual types available.
		/// </summary>
		/// <seealso cref="Default.BarType"/>
		public BarType BarType
		{
			get { return barType; }
			set { barType = value; }
		}
		/// <summary>
		/// The width of an individual bar cluster on a <see cref="Bar"/> graph.
		/// This value only applies to bar graphs plotted on non-ordinal X axis
		/// types (<see cref="AxisType.Linear"/>, <see cref="AxisType.Log"/>, and
		/// <see cref="AxisType.Date"/>.
		/// </summary>
		/// <seealso cref="Default.ClusterScaleWidth"/>
		/// <seealso cref="MinBarGap"/>
		/// <seealso cref="MinClusterGap"/>
		public double ClusterScaleWidth
		{
			get { return clusterScaleWidth; }
			set { clusterScaleWidth = value; }
		}
	#endregion
	
	#region Constructors

		/// <summary>
		/// Default Constructor.  Sets the <see cref="PaneBase.PaneRect"/> to (0, 0, 500, 375), and
		/// sets the <see cref="PaneBase.Title"/> and <see cref="Axis.Title"/> values to empty
		/// strings.
		/// </summary>
		public GraphPane() : this( new RectangleF( 0, 0, 500, 375 ), "", "", "" )
		{
		}

		/// <summary>
		/// Constructor for the <see cref="GraphPane"/> object.  This routine will
		/// initialize all member variables and classes, setting appropriate default
		/// values as defined in the <see cref="Default"/> class.
		/// </summary>
		/// <param name="paneRect"> A rectangular screen area where the graph is to be displayed.
		/// This area can be any size, and can be resize at any time using the
		/// <see cref="PaneBase.PaneRect"/> property.
		/// </param>
		/// <param name="paneTitle">The <see cref="PaneBase.Title"/> for this <see cref="GraphPane"/></param>
		/// <param name="xTitle">The <see cref="Axis.Title"/> for the <see cref="XAxis"/></param>
		/// <param name="yTitle">The <see cref="Axis.Title"/> for the <see cref="YAxis"/></param>
		public GraphPane( RectangleF paneRect, string paneTitle,
			string xTitle, string yTitle ) : base( paneTitle, paneRect )
		{
			xAxis = new XAxis( xTitle );
			yAxis = new YAxis( yTitle );
			y2Axis = new Y2Axis( string.Empty );
			curveList = new CurveList();
			zoomStack = new ZoomStateStack();
								
			this.isIgnoreInitial = Default.IsIgnoreInitial;
			this.isBoundedRanges = Default.IsBoundedRanges;
			
			this.isAxisRectAuto = true;
			this.axisBorder = new Border( Default.IsAxisBorderVisible, Default.AxisBorderColor, Default.AxisBorderPenWidth );
			this.axisFill = new Fill( Default.AxisBackColor, Default.AxisBackBrush, Default.AxisBackType );

			this.minClusterGap = Default.MinClusterGap;
			this.minBarGap = Default.MinBarGap;
			this.clusterScaleWidth = Default.ClusterScaleWidth;
			this.barBase = Default.BarBase;
			this.barType = Default.BarType;
			
			this.lineType = Default.LineType;
		}

		/// <summary>
		/// The Copy Constructor
		/// </summary>
		/// <param name="rhs">The GraphPane object from which to copy</param>
		public GraphPane( GraphPane rhs ) : base( rhs )
		{
			xAxis = new XAxis( rhs.XAxis );
			yAxis = new YAxis( rhs.YAxis );
			y2Axis = new Y2Axis( rhs.Y2Axis );
			curveList = new CurveList( rhs.CurveList );
			zoomStack = new ZoomStateStack( rhs.zoomStack );
			
			this.isIgnoreInitial = rhs.IsIgnoreInitial;
			this.isBoundedRanges = rhs.isBoundedRanges;
			
			this.isAxisRectAuto = rhs.IsAxisRectAuto;
			this.axisBorder = (Border) rhs.AxisBorder.Clone();
			this.axisFill = (Fill) rhs.AxisFill.Clone();

			this.minClusterGap = rhs.MinClusterGap;
			this.minBarGap = rhs.MinBarGap;
			this.clusterScaleWidth = rhs.ClusterScaleWidth;
			this.barBase = rhs.BarBase;
			this.barType = rhs.BarType;
			
			this.lineType = rhs.LineType;
		} 

		/// <summary>
		/// Deep-copy clone routine
		/// </summary>
		/// <returns>A new, independent copy of the GraphPane</returns>
		public override object Clone()
		{ 
			return new GraphPane( this ); 
		}
	#endregion

	#region Serialization
		/// <summary>
		/// Current schema value that defines the version of the serialized file
		/// </summary>
		public const int schema2 = 1;

		/// <summary>
		/// Constructor for deserializing objects
		/// </summary>
		/// <param name="info">A <see cref="SerializationInfo"/> instance that defines the serialized data
		/// </param>
		/// <param name="context">A <see cref="StreamingContext"/> instance that contains the serialized data
		/// </param>
		protected GraphPane( SerializationInfo info, StreamingContext context ) : base( info, context )
		{
			// The schema value is just a file version parameter.  You can use it to make future versions
			// backwards compatible as new member variables are added to classes
			int sch = info.GetInt32( "schema2" );

			xAxis = (XAxis) info.GetValue( "xAxis", typeof(XAxis) );
			yAxis = (YAxis) info.GetValue( "yAxis", typeof(YAxis) );
			y2Axis = (Y2Axis) info.GetValue( "y2Axis", typeof(Y2Axis) );
			curveList = (CurveList) info.GetValue( "curveList", typeof(CurveList) );

			isAxisRectAuto = info.GetBoolean( "isAxisRectAuto" );
			axisFill = (Fill) info.GetValue( "axisFill", typeof(Fill) );
			axisBorder = (Border) info.GetValue( "axisBorder", typeof(Border) );

			isIgnoreInitial = info.GetBoolean( "isIgnoreInitial" );
			isBoundedRanges = info.GetBoolean( "isBoundedRanges" );
			isIgnoreMissing = info.GetBoolean( "isIgnoreMissing" );

			minClusterGap = info.GetSingle( "minClusterGap" );
			minBarGap = info.GetSingle( "minBarGap" );

			barBase = (BarBase) info.GetValue( "barBase", typeof(BarBase) );
			barType = (BarType) info.GetValue( "barType", typeof(BarType) );

			clusterScaleWidth = info.GetDouble( "clusterScaleWidth" );
			axisRect = (RectangleF) info.GetValue( "axisRect", typeof(RectangleF) );
			lineType = (LineType) info.GetValue( "lineType", typeof(LineType) );
		}
		/// <summary>
		/// Populates a <see cref="SerializationInfo"/> instance with the data needed to serialize the target object
		/// </summary>
		/// <param name="info">A <see cref="SerializationInfo"/> instance that defines the serialized data</param>
		/// <param name="context">A <see cref="StreamingContext"/> instance that contains the serialized data</param>
		[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
		public override void GetObjectData( SerializationInfo info, StreamingContext context )
		{
			base.GetObjectData( info, context );

			info.AddValue( "schema2", schema2 );

			info.AddValue( "xAxis", xAxis );
			info.AddValue( "yAxis", yAxis );
			info.AddValue( "y2Axis", y2Axis );
			info.AddValue( "curveList", curveList );

			info.AddValue( "isAxisRectAuto", isAxisRectAuto );
			info.AddValue( "axisFill", axisFill );
			info.AddValue( "axisBorder", axisBorder );

			info.AddValue( "isIgnoreInitial", isIgnoreInitial );
			info.AddValue( "isBoundedRanges", isBoundedRanges );
			info.AddValue( "isIgnoreMissing", isIgnoreMissing );

			info.AddValue( "minClusterGap", minClusterGap );
			info.AddValue( "minBarGap", minBarGap );

			info.AddValue( "barBase", barBase );
			info.AddValue( "barType", barType );

			info.AddValue( "clusterScaleWidth", clusterScaleWidth );

			info.AddValue( "axisRect", axisRect );

			info.AddValue( "lineType", lineType );
		}
	#endregion

	#region Rendering Methods
		/// <summary>
		/// AxisChange causes the axes scale ranges to be recalculated based on the current data range.
		/// </summary>
		/// <remarks>
		/// There is no obligation to call AxisChange() for manually scaled axes.  AxisChange() is only
		/// intended to handle auto scaling operations.  Call this function anytime you change, add, or
		/// remove curve data to insure that the scale range of the axes are appropriate for the data range.
		/// This method calculates
		/// a scale minimum, maximum, and step size for each axis based on the current curve data.
		/// Only the axis attributes (min, max, step) that are set to auto-range (<see cref="Axis.MinAuto"/>,
		/// <see cref="Axis.MaxAuto"/>, <see cref="Axis.StepAuto"/>) will be modified.  You must call
		/// <see cref="Control.Invalidate()"/> after calling AxisChange to make sure the display gets updated.
		/// </remarks>
		/// <param name="g">
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// </param>
		public void AxisChange( Graphics g )
		{
			double	xMin, xMax, yMin, yMax, y2Min, y2Max;

			// Get the scale range of the data (all curves)
			this.curveList.GetRange( out xMin, out xMax, out yMin,
				out yMax, out y2Min, out y2Max,
				this.isIgnoreInitial, this );

			// Determine the scale factor
			float	scaleFactor = this.CalcScaleFactor();

			// For pie charts, go ahead and turn off the axis displays if it's only pies
			if ( this.CurveList.IsPieOnly )
			{
				//don't want to display axis or border if there's only pies
				this.XAxis.IsVisible = false ;				
				this.YAxis.IsVisible = false ;
				this.Y2Axis.IsVisible = false ;
				this.axisBorder.IsVisible = false ;
				//this.Legend.Position = LegendPos.TopCenter;
			}

			// if the AxisRect is not yet determined, then pick a scale based on a default AxisRect
			// size (using 75% of PaneRect -- code is in Axis.CalcMaxLabels() )
			// With the scale picked, call CalcAxisRect() so calculate a real AxisRect
			// then let the scales re-calculate to make sure that the assumption was ok
			if ( this.isAxisRectAuto )
			{
				//if ( AxisRect.Width == 0 || AxisRect.Height == 0 )
				//{
					// Pick new scales based on the range
					this.xAxis.PickScale( xMin, xMax, this, g, scaleFactor );
					this.yAxis.PickScale( yMin, yMax, this, g, scaleFactor );
					this.y2Axis.PickScale( y2Min, y2Max, this, g, scaleFactor );
	  
				//}

				this.axisRect = CalcAxisRect( g );
				this.pieRect = PieItem.CalcPieRect( g, this, scaleFactor, this.axisRect );
			}
 
			// Pick new scales based on the range
			this.xAxis.PickScale( xMin, xMax, this, g, scaleFactor );
			this.yAxis.PickScale( yMin, yMax, this, g, scaleFactor );
			this.y2Axis.PickScale( y2Min, y2Max, this, g, scaleFactor );
		}
		
		/// <summary>
		/// Draw all elements in the <see cref="GraphPane"/> to the specified graphics device.
		/// </summary>
		/// <remarks>This method
		/// should be part of the Paint() update process.  Calling this routine will redraw all
		/// features of the graph.  No preparation is required other than an instantiated
		/// <see cref="GraphPane"/> object.
		/// </remarks>
		/// <param name="g">
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// </param>
		public override void Draw( Graphics g )
		{			
			// Calculate the axis rect, deducting the area for the scales, titles, legend, etc.
			//int		hStack;
			//float	legendWidth, legendHeight;

			// Draw the pane border & background fill, the title, and the GraphItem objects that lie at
			// ZOrder.G_BehindAll
			base.Draw( g );

			if ( paneRect.Width <= 1 || paneRect.Height <= 1 )
				return;

			// Clip everything to the paneRect
			g.SetClip( this.paneRect );

			// calculate scaleFactor on "normal" pane size (BaseDimension)
			float scaleFactor = this.CalcScaleFactor();


			// if the size of the axisRect is determined automatically, then do so
			// otherwise, calculate the legendrect, scalefactor, hstack, and legendwidth parameters
			// but leave the axisRect alone
			if ( this.isAxisRectAuto )
			{
				this.axisRect = CalcAxisRect( g, scaleFactor );
				this.pieRect = PieItem.CalcPieRect( g, this, scaleFactor, this.axisRect );
			}
			else
				CalcAxisRect( g, scaleFactor  );

			// do a sanity check on the axisRect
			if ( this.axisRect.Width < 1 || this.axisRect.Height < 1 )
				return;
			
			// Draw the graph features only if there is at least one curve with data
			// if ( this.curveList.HasData() &&
			// Go ahead and draw the graph, even without data.  This makes the control
			// version still look like a graph before it is fully set up
			bool showGraf = this.xAxis.Min < this.xAxis.Max &&
				this.yAxis.Min < this.yAxis.Max &&
				this.y2Axis.Min < this.y2Axis.Max;

			// Setup the axes from graphing - This setup must be done before
			// the GraphItem's are drawn so that the Transform functions are
			// ready.  Also, this should be done before CalcAxisRect so that the
			// Axis.Cross - shift parameter can be calculated.
			this.xAxis.SetupScaleData( this );
			this.yAxis.SetupScaleData( this );
			this.y2Axis.SetupScaleData( this );

			// Draw the GraphItems that are behind the Axis objects
			if ( showGraf )
				this.graphItemList.Draw( g, this, scaleFactor, ZOrder.F_BehindAxisFill );

			// Fill the axis background
			this.axisFill.Draw( g, this.axisRect );
			
			if ( showGraf )
			{		
				// Draw the GraphItems that are behind the Axis objects
				this.graphItemList.Draw( g, this, scaleFactor, ZOrder.E_BehindAxis );

				// Draw the Axes
				this.xAxis.Draw( g, this, scaleFactor );
				this.yAxis.Draw( g, this, scaleFactor );
				this.y2Axis.Draw( g, this, scaleFactor );
				
				// Draw the GraphItems that are behind the CurveItems
				this.graphItemList.Draw( g, this, scaleFactor, ZOrder.D_BehindCurves );

				// Clip the points to the actual plot area
				g.SetClip( this.axisRect );
				this.curveList.Draw( g, this, scaleFactor );
				g.SetClip( this.paneRect );

				// Draw the GraphItems that are behind the Axis border
				this.graphItemList.Draw( g, this, scaleFactor, ZOrder.C_BehindAxisBorder );
			}
				
			// Border the axis itself
			this.axisBorder.Draw( g, this.IsPenWidthScaled, scaleFactor, this.axisRect );
			
			if ( showGraf )
			{
				// Draw the GraphItems that are behind the Legend object
				this.graphItemList.Draw( g, this, scaleFactor, ZOrder.B_BehindLegend );

				this.legend.Draw( g, this, scaleFactor );
			
				// Draw the GraphItems that are in front of all other items
				this.graphItemList.Draw( g, this, scaleFactor, ZOrder.A_InFront );
			}

			// Reset the clipping
			g.ResetClip();
		}

		/// <summary>
		/// Calculate the <see cref="AxisRect"/> based on the <see cref="PaneBase.PaneRect"/>.
		/// </summary>
		/// <remarks>The axisRect
		/// is the plot area bounded by the axes, and the paneRect is the total area as
		/// specified by the client application.
		/// </remarks>
		/// <param name="g">
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// </param>
		/// <returns>The calculated axis rect, in pixel coordinates.</returns>
		public RectangleF CalcAxisRect( Graphics g )
		{
			// Calculate the axis rect, deducting the area for the scales, titles, legend, etc.
			//int		hStack;
			//float	legendWidth, legendHeight;
			
			return CalcAxisRect( g, CalcScaleFactor() );
		}

		/// <summary>
		/// Calculate the <see cref="AxisRect"/> based on the <see cref="PaneBase.PaneRect"/>.
		/// </summary>
		/// <remarks>The axisRect
		/// is the plot area bounded by the axes, and the paneRect is the total area as
		/// specified by the client application.
		/// </remarks>
		/// <param name="g">
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// </param>
		/// <param name="scaleFactor">
		/// The scaling factor for the features of the graph based on the <see cref="PaneBase.BaseDimension"/>.  This
		/// scaling factor is calculated by the <see cref="PaneBase.CalcScaleFactor"/> method.  The scale factor
		/// represents a linear multiple to be applied to font sizes, symbol sizes, etc.
		/// </param>
		/// <returns>The calculated axis rect, in pixel coordinates.</returns>
        
		public RectangleF CalcAxisRect( Graphics g, float scaleFactor )
		{				
			// Axis rect starts out at the full pane rect less the margins
            //   and less space for the Pane title
			RectangleF clientRect = this.CalcClientRect( g, scaleFactor );

            float minSpaceX, minSpaceY, minSpaceY2;

			float spaceX = this.xAxis.CalcSpace( g, this, scaleFactor, out minSpaceX );
			float spaceY = this.yAxis.CalcSpace( g, this, scaleFactor, out minSpaceY );
			float spaceY2 = this.y2Axis.CalcSpace( g, this, scaleFactor, out minSpaceY2 );

			// actual axis space for the left side of the Axis rect
			float spaceL = minSpaceY;
			// actual axis space for the right side of the Axis rect
			float spaceR = minSpaceY2;
			// actual axis space for the top side of the Axis rect
			float spaceT = 0;
			// actual axis space for the bottom side of the Axis rect
			float spaceB = minSpaceX;

            SetSpace( this.xAxis, clientRect.Height - spaceX, spaceX, ref spaceB, ref spaceT );
            SetSpace( this.yAxis, clientRect.Width - spaceY - spaceY2, spaceY, ref spaceL, ref spaceR );
            SetSpace( this.y2Axis, clientRect.Width - spaceY - spaceY2, spaceY2, ref spaceR, ref spaceL );

            /*
            float crossFracX = this.xAxis.CalcCrossFraction( this );
            float crossFracY = this.yAxis.CalcCrossFraction( this );
            float crossFracY2 = this.y2Axis.CalcCrossFraction( this );

			float crossPixX = crossFracX * (1 + crossFracX)*(1 + crossFracX * crossFracX) *
				( clientRect.Height - spaceX );
			float crossPixY = crossFracY * (1 + crossFracY)*(1 + crossFracY * crossFracY) *
				( clientRect.Width - spaceY - spaceY2 );
			float crossPixY2 = crossFracY2 * (1 + crossFracY2) * (1 + crossFracY2 * crossFracY2) *
				( clientRect.Width - spaceY - spaceY2 );

            if ( spaceX < crossPixX )
				spaceX = 0;
			else if ( crossPixX > 0 )
				spaceX -= crossPixX;

			if ( spaceY < crossPixY )
				spaceY = 0;
			else if ( crossPixY > 0 )
				spaceY -= crossPixY;

			if ( spaceY2 < crossPixY2 )
				spaceY2 = 0;
			else if ( crossPixY2 > 0 )
				spaceY2 -= crossPixY2;

			if ( this.xAxis.IsScaleLabelsInside )
            {
				if ( spaceX > spaceT ) spaceT = spaceX;
            }
			else
            {
				if ( spaceX > spaceB ) spaceB = spaceX;
            }

			if ( this.yAxis.IsScaleLabelsInside )
            {
				if ( spaceY > spaceR ) spaceR = spaceY;
            }
			else
            {
				if ( spaceY > spaceL ) spaceL = spaceY;
            }

			if ( this.y2Axis.IsScaleLabelsInside )
			{
				if ( spaceY2 > spaceL ) spaceL = spaceY2;
			}
			else
			{
				if ( spaceY2 > spaceR ) spaceR = spaceY2;
			}

            */

			RectangleF tmpRect = clientRect;
			tmpRect.Width -= spaceL + spaceR;
			tmpRect.X += spaceL;
			tmpRect.Height -= spaceT + spaceB;
			tmpRect.Y += spaceT;

			//SizeF axisSize = new SizeF( tmpRect.Size );

            this.legend.CalcRect( g, this, scaleFactor, ref tmpRect );
/*
			tmpRect.Width -= spaceLegend.Width;
			tmpRect.Height -= spaceLegend.Height;
			tmpRect.X += spaceLegend.X;
			tmpRect.Y += spaceLegend.Y;
	
			this.legend.SetLocation( this, tmpRect, scaleFactor );
*/
			return tmpRect;
		}

		private void SetSpace( Axis axis, float clientSize, float spaceReq,
							ref float spaceNorm, ref float spaceAlt )
		{
			float crossFrac = axis.CalcCrossFraction( this );
			float crossPix = crossFrac * ( 1 + crossFrac ) * ( 1 + crossFrac * crossFrac ) * clientSize;

			if ( spaceReq < crossPix )
				spaceReq = 0;
			else if ( crossPix > 0 )
				spaceReq -= crossPix;

			if ( axis.IsScaleLabelsInside )
			{
				if ( spaceReq > spaceAlt )
					spaceAlt = spaceReq;
			}
			else
			{
				if ( spaceReq > spaceNorm )
					spaceNorm = spaceReq;
			}
		}

		/// <summary>
		/// This method will set the <see cref="Axis.MinSpace"/> property for all three axes;
		/// <see cref="XAxis"/>, <see cref="YAxis"/>, and <see cref="Y2Axis"/>.
		/// </summary>
		/// <remarks>The <see cref="Axis.MinSpace"/>
		/// is calculated using the currently required space multiplied by a fraction
		/// (<paramref>bufferFraction</paramref>).
		/// The currently required space is calculated using <see cref="Axis.CalcSpace"/>, and is
		/// based on current data ranges, font sizes, etc.  The "space" is actually the amount of space
		/// required to fit the tic marks, scale labels, and axis title.
		/// The calculation is done by calling the <see cref="Axis.SetMinSpaceBuffer"/> method for
		/// each <see cref="Axis"/>.
		/// </remarks>
		/// <param name="g">A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.</param>
		/// <param name="bufferFraction">The amount of space to allocate for the axis, expressed
		/// as a fraction of the currently required space.  For example, a value of 1.2 would
		/// allow for 20% extra above the currently required space.</param>
		/// <param name="isGrowOnly">If true, then this method will only modify the <see cref="Axis.MinSpace"/>
		/// property if the calculated result is more than the current value.</param>
		public void SetMinSpaceBuffer( Graphics g, float bufferFraction, bool isGrowOnly )
		{
			this.xAxis.SetMinSpaceBuffer( g, this, bufferFraction, isGrowOnly );
			this.yAxis.SetMinSpaceBuffer( g, this, bufferFraction, isGrowOnly  );
			this.y2Axis.SetMinSpaceBuffer( g, this, bufferFraction, isGrowOnly  );
		}
		
		#endregion
	
	#region AddCurve Methods
		/// <summary>
		/// Add a curve (<see cref="CurveItem"/> object) to the plot with
		/// the given data points (double arrays) and properties.
		/// This is simplified way to add curves without knowledge of the
		/// <see cref="CurveList"/> class.  An alternative is to use
		/// the <see cref="ZedGraph.CurveList.Add"/> method.
		/// </summary>
		/// <param name="label">The text label (string) for the curve that will be
		/// used as a <see cref="Legend"/> entry.</param>
		/// <param name="x">An array of double precision X values (the
		/// independent values) that define the curve.</param>
		/// <param name="y">An array of double precision Y values (the
		/// dependent values) that define the curve.</param>
		/// <param name="color">The color to used for the curve line,
		/// symbols, etc.</param>
		/// <returns>A <see cref="CurveItem"/> class for the newly created curve.
		/// This can then be used to access all of the curve properties that
		/// are not defined as arguments to the
		/// <see cref="AddCurve(string,double[],double[],Color)"/> method.</returns>
		public LineItem AddCurve( string label, double[] x, double[] y, Color color )
		{
			LineItem curve = new LineItem( label, x, y, color, SymbolType.Default );
			this.curveList.Add( curve );
			
			return curve;
		}

		/// <summary>
		/// Add a curve (<see cref="CurveItem"/> object) to the plot with
		/// the given data points (<see cref="PointPairList"/>) and properties.
		/// This is simplified way to add curves without knowledge of the
		/// <see cref="CurveList"/> class.  An alternative is to use
		/// the <see cref="ZedGraph.CurveList.Add"/> method.
		/// </summary>
		/// <param name="label">The text label (string) for the curve that will be
		/// used as a <see cref="Legend"/> entry.</param>
		/// <param name="points">A <see cref="PointPairList"/> of double precision value pairs that define
		/// the X and Y values for this curve</param>
		/// <param name="color">The color to used for the curve line,
		/// symbols, etc.</param>
		/// <returns>A <see cref="CurveItem"/> class for the newly created curve.
		/// This can then be used to access all of the curve properties that
		/// are not defined as arguments to the
		/// <see cref="AddCurve(string,PointPairList,Color)"/> method.</returns>
		public LineItem AddCurve( string label, PointPairList points, Color color )
		{
			LineItem curve = new LineItem( label, points, color, SymbolType.Default );
			this.curveList.Add( curve );
			
			return curve;
		}

		/// <summary>
		/// Add a curve (<see cref="CurveItem"/> object) to the plot with
		/// the given data points (double arrays) and properties.
		/// This is simplified way to add curves without knowledge of the
		/// <see cref="CurveList"/> class.  An alternative is to use
		/// the <see cref="ZedGraph.CurveList.Add"/> method.
		/// </summary>
		/// <param name="label">The text label (string) for the curve that will be
		/// used as a <see cref="Legend"/> entry.</param>
		/// <param name="x">An array of double precision X values (the
		/// independent values) that define the curve.</param>
		/// <param name="y">An array of double precision Y values (the
		/// dependent values) that define the curve.</param>
		/// <param name="color">The color to used for the curve line,
		/// symbols, etc.</param>
		/// <param name="symbolType">A symbol type (<see cref="SymbolType"/>)
		/// that will be used for this curve.</param>
		/// <returns>A <see cref="CurveItem"/> class for the newly created curve.
		/// This can then be used to access all of the curve properties that
		/// are not defined as arguments to the
		/// <see cref="AddCurve(string,double[],double[],Color,SymbolType)"/> method.</returns>
		public LineItem AddCurve( string label, double[] x, double[] y,
			Color color, SymbolType symbolType )
		{
			LineItem curve = new LineItem( label, x, y, color, symbolType );
			this.curveList.Add( curve );
			
			return curve;
		}

		/// <summary>
		/// Add a curve (<see cref="CurveItem"/> object) to the plot with
		/// the given data points (<see cref="PointPairList"/>) and properties.
		/// This is simplified way to add curves without knowledge of the
		/// <see cref="CurveList"/> class.  An alternative is to use
		/// the <see cref="ZedGraph.CurveList.Add"/> method.
		/// </summary>
		/// <param name="label">The text label (string) for the curve that will be
		/// used as a <see cref="Legend"/> entry.</param>
		/// <param name="points">A <see cref="PointPairList"/> of double precision value pairs that define
		/// the X and Y values for this curve</param>
		/// <param name="color">The color to used for the curve line,
		/// symbols, etc.</param>
		/// <param name="symbolType">A symbol type (<see cref="SymbolType"/>)
		/// that will be used for this curve.</param>
		/// <returns>A <see cref="CurveItem"/> class for the newly created curve.
		/// This can then be used to access all of the curve properties that
		/// are not defined as arguments to the
		/// <see cref="AddCurve(string,PointPairList,Color,SymbolType)"/> method.</returns>
		public LineItem AddCurve( string label, PointPairList points,
			Color color, SymbolType symbolType )
		{
			LineItem curve = new LineItem( label, points, color, symbolType );
			this.curveList.Add( curve );
			
			return curve;
		}

		/// <summary>
		/// Add a stick graph (<see cref="StickItem"/> object) to the plot with
		/// the given data points (double arrays) and properties.
		/// This is simplified way to add curves without knowledge of the
		/// <see cref="CurveList"/> class.  An alternative is to use
		/// the <see cref="ZedGraph.CurveList.Add"/> method.
		/// </summary>
		/// <param name="label">The text label (string) for the curve that will be
		/// used as a <see cref="Legend"/> entry.</param>
		/// <param name="x">An array of double precision X values (the
		/// independent values) that define the curve.</param>
		/// <param name="y">An array of double precision Y values (the
		/// dependent values) that define the curve.</param>
		/// <param name="color">The color to used for the curve line,
		/// symbols, etc.</param>
		/// <returns>A <see cref="StickItem"/> class for the newly created curve.
		/// This can then be used to access all of the curve properties that
		/// are not defined as arguments to the
		/// <see cref="AddStick(string,double[],double[],Color)"/> method.</returns>
		public StickItem AddStick( string label, double[] x, double[] y, Color color )
		{
			StickItem curve = new StickItem( label, x, y, color );
			this.curveList.Add( curve );
			
			return curve;
		}

		/// <summary>
		/// Add a stick graph (<see cref="StickItem"/> object) to the plot with
		/// the given data points (<see cref="PointPairList"/>) and properties.
		/// This is simplified way to add curves without knowledge of the
		/// <see cref="CurveList"/> class.  An alternative is to use
		/// the <see cref="ZedGraph.CurveList.Add"/> method.
		/// </summary>
		/// <param name="label">The text label (string) for the curve that will be
		/// used as a <see cref="Legend"/> entry.</param>
		/// <param name="points">A <see cref="PointPairList"/> of double precision value pairs that define
		/// the X and Y values for this curve</param>
		/// <param name="color">The color to used for the curve line,
		/// symbols, etc.</param>
		/// <returns>A <see cref="CurveItem"/> class for the newly created curve.
		/// This can then be used to access all of the curve properties that
		/// are not defined as arguments to the
		/// <see cref="AddStick(string,PointPairList,Color)"/> method.</returns>
		public StickItem AddStick( string label, PointPairList points, Color color )
		{
			StickItem curve = new StickItem( label, points, color );
			this.curveList.Add( curve );
			
			return curve;
		}

		/// <summary>
		/// Add an error bar set (<see cref="ErrorBarItem"/> object) to the plot with
		/// the given data points (<see cref="PointPairList"/>) and properties.
		/// This is simplified way to add curves without knowledge of the
		/// <see cref="CurveList"/> class.  An alternative is to use
		/// the <see cref="ZedGraph.CurveList.Add"/> method.
		/// </summary>
		/// <param name="label">The text label (string) for the curve that will be
		/// used as a <see cref="Legend"/> entry.</param>
		/// <param name="x">An array of double precision X values (the
		/// independent values) that define the curve.</param>
		/// <param name="y">An array of double precision Y values (the
		/// dependent values) that define the curve.</param>
		/// <param name="baseValue">An array of double precision values that define the
		/// base value (the bottom) of the bars for this curve.
		/// </param>
		/// <param name="color">The color to used for the curve line,
		/// symbols, etc.</param>
		/// <returns>An <see cref="ErrorBarItem"/> class for the newly created curve.
		/// This can then be used to access all of the curve properties that
		/// are not defined as arguments to the
		/// <see cref="AddErrorBar(string,PointPairList,Color)"/> method.</returns>
		public ErrorBarItem AddErrorBar( string label, double[] x, double[] y,
			double[] baseValue, Color color )
		{
			ErrorBarItem curve = new ErrorBarItem( label, new PointPairList( x, y, baseValue),
				color );
			this.curveList.Add( curve );
			
			return curve;
		}
		/// <summary>
		/// Add an error bar set (<see cref="ErrorBarItem"/> object) to the plot with
		/// the given data points (<see cref="PointPairList"/>) and properties.
		/// This is simplified way to add curves without knowledge of the
		/// <see cref="CurveList"/> class.  An alternative is to use
		/// the <see cref="ZedGraph.CurveList.Add"/> method.
		/// </summary>
		/// <param name="label">The text label (string) for the curve that will be
		/// used as a <see cref="Legend"/> entry.</param>
		/// <param name="points">A <see cref="PointPairList"/> of double precision value pairs that define
		/// the X and Y values for this curve</param>
		/// <param name="color">The color to used for the curve line,
		/// symbols, etc.</param>
		/// <returns>An <see cref="ErrorBarItem"/> class for the newly created curve.
		/// This can then be used to access all of the curve properties that
		/// are not defined as arguments to the
		/// <see cref="AddErrorBar(string,PointPairList,Color)"/> method.</returns>
		public ErrorBarItem AddErrorBar( string label, PointPairList points, Color color )
		{
			ErrorBarItem curve = new ErrorBarItem( label, points, color );
			this.curveList.Add( curve );
			
			return curve;
		}

		/// <summary>
		/// Add a bar type curve (<see cref="CurveItem"/> object) to the plot with
		/// the given data points (<see cref="PointPairList"/>) and properties.
		/// This is simplified way to add curves without knowledge of the
		/// <see cref="CurveList"/> class.  An alternative is to use
		/// the <see cref="ZedGraph.CurveList.Add"/> method.
		/// </summary>
		/// <param name="label">The text label (string) for the curve that will be
		/// used as a <see cref="Legend"/> entry.</param>
		/// <param name="points">A <see cref="PointPairList"/> of double precision value pairs that define
		/// the X and Y values for this curve</param>
		/// <param name="color">The color to used to fill the bars</param>
		/// <returns>A <see cref="CurveItem"/> class for the newly created bar curve.
		/// This can then be used to access all of the curve properties that
		/// are not defined as arguments to the
		/// <see cref="AddBar(string,PointPairList,Color)"/> method.</returns>
		public BarItem AddBar( string label, PointPairList points, Color color )
		{
			BarItem curve = new BarItem( label, points, color );
			this.curveList.Add( curve );
			
			return curve;
		}
		
		/// <summary>
		/// Add a bar type curve (<see cref="CurveItem"/> object) to the plot with
		/// the given data points (double arrays) and properties.
		/// This is simplified way to add curves without knowledge of the
		/// <see cref="CurveList"/> class.  An alternative is to use
		/// the <see cref="ZedGraph.CurveList.Add"/> method.
		/// </summary>
		/// <param name="label">The text label (string) for the curve that will be
		/// used as a <see cref="Legend"/> entry.</param>
		/// <param name="x">An array of double precision X values (the
		/// independent values) that define the curve.</param>
		/// <param name="y">An array of double precision Y values (the
		/// dependent values) that define the curve.</param>
		/// <param name="color">The color to used for the bars</param>
		/// <returns>A <see cref="CurveItem"/> class for the newly created bar curve.
		/// This can then be used to access all of the curve properties that
		/// are not defined as arguments to the
		/// <see cref="AddBar(string,double[],double[],Color)"/> method.</returns>
		public BarItem AddBar( string label, double[] x, double[] y, Color color )
		{
			BarItem curve = new BarItem( label, x, y, color );
			this.curveList.Add( curve );
			
			return curve;
		}

		/// <summary>
		/// Add a "High-Low" bar type curve (<see cref="HiLowBarItem"/> object) to the plot with
		/// the given data points (double arrays) and properties.
		/// This is simplified way to add curves without knowledge of the
		/// <see cref="CurveList"/> class.  An alternative is to use
		/// the <see cref="ZedGraph.CurveList.Add"/> method.
		/// </summary>
		/// <param name="label">The text label (string) for the curve that will be
		/// used as a <see cref="Legend"/> entry.</param>
		/// <param name="x">An array of double precision X values (the
		/// independent values) that define the curve.</param>
		/// <param name="y">An array of double precision Y values (the
		/// dependent values) that define the curve.</param>
		/// <param name="baseVal">An array of double precision values that define the
		/// base value (the bottom) of the bars for this curve.
		/// </param>
		/// <param name="color">The color to used for the bars</param>
		/// <returns>A <see cref="HiLowBarItem"/> class for the newly created bar curve.
		/// This can then be used to access all of the curve properties that
		/// are not defined as arguments to the
		/// <see cref="AddHiLowBar(string,double[],double[],double[],Color)"/> method.</returns>
		public HiLowBarItem AddHiLowBar( string label, double[] x, double[] y,
			double[] baseVal, Color color )
		{
			HiLowBarItem curve = new HiLowBarItem( label, x, y, baseVal, color );
			this.curveList.Add( curve );
			
			return curve;
		}

		/// <summary>
		/// Add a hi-low bar type curve (<see cref="CurveItem"/> object) to the plot with
		/// the given data points (<see cref="PointPairList"/>) and properties.
		/// This is simplified way to add curves without knowledge of the
		/// <see cref="CurveList"/> class.  An alternative is to use
		/// the <see cref="ZedGraph.CurveList.Add"/> method.
		/// </summary>
		/// <param name="label">The text label (string) for the curve that will be
		/// used as a <see cref="Legend"/> entry.</param>
		/// <param name="points">A <see cref="PointPairList"/> of double precision value Trio's that define
		/// the X, Y, and lower dependent values for this curve</param>
		/// <param name="color">The color to used to fill the bars</param>
		/// <returns>A <see cref="HiLowBarItem"/> class for the newly created bar curve.
		/// This can then be used to access all of the curve properties that
		/// are not defined as arguments to the
		/// <see cref="AddHiLowBar(string,PointPairList,Color)"/> method.</returns>
		public HiLowBarItem AddHiLowBar( string label, PointPairList points, Color color )
		{
			HiLowBarItem curve = new HiLowBarItem( label, points, color );
			this.curveList.Add( curve );
			
			return curve;
		}

		/// <summary>
		/// Add a <see cref="PieItem"/> to the display.
		/// </summary>
		/// <param name="value">The value associated with this <see cref="PieItem"/>item.</param>
		/// <param name="color">The display color for this <see cref="PieItem"/>item.</param>
		/// <param name="displacement">The amount this <see cref="PieItem"/>item will be 
		/// displaced from the center of the <see cref="PieItem"/>.</param>
		/// <param name="label">Text label for this <see cref="PieItem"/></param>
		/// <returns>a reference to the <see cref="PieItem"/> constructed</returns>
		public PieItem AddPieSlice ( double value, Color color, double displacement, string label )
		{
			PieItem slice = new PieItem( value, color, displacement, label );
			this.CurveList.Add( slice );
			return slice;
		}

		/// <summary>
		/// Add a <see cref="PieItem"/> to the display, providing a gradient fill for the pie color.
		/// </summary>
		/// <param name="value">The value associated with this <see cref="PieItem"/> instance.</param>
		/// <param name="color1">The starting display color for the gradient <see cref="Fill"/> for this
		/// <see cref="PieItem"/> instance.</param>
		/// <param name="color2">The ending display color for the gradient <see cref="Fill"/> for this
		/// <see cref="PieItem"/> instance.</param>
		/// <param name="fillAngle">The angle for the gradient <see cref="Fill"/>.</param>
		/// <param name="displacement">The amount this <see cref="PieItem"/>  instance will be 
		/// displaced from the center point.</param>
		/// <param name="label">Text label for this <see cref="PieItem"/> instance.</param>
		public PieItem AddPieSlice ( double value, Color color1, Color color2, float fillAngle,
						double displacement, string label )
		{
			PieItem slice = new PieItem( value, color1, color2, fillAngle, displacement, label ) ;
			this.CurveList.Add( slice );
			return slice;
		}

		/// <summary>
		///Creates all the <see cref="PieItem"/>s for a single Pie Chart. 
		/// </summary>
		/// <param name="values">double array containing all <see cref="PieItem.Value"/>s
		/// for a single PieChart.
		/// </param>
		/// <param name="labels"> string array containing all <see cref="CurveItem.Label"/>s
		/// for a single PieChart.
		/// </param>
		/// <returns>an array containing references to all <see cref="PieItem"/>s comprising
		/// the Pie Chart.</returns>
		public PieItem [] AddPieSlices ( double [] values, string [] labels )
		{
			PieItem [] slices = new PieItem[values.Length] ;
			for ( int x = 0 ; x < values.Length ; x++ )
			{
				slices[x]= new PieItem(  values[x],  labels [x] ) ;
				this.CurveList.Add (slices[x]) ;																			  
			}
			return slices ;
		}

	#endregion

	#region General Utility Methods
		/// <summary>
		/// Transform a data point from the specified coordinate type
		/// (<see cref="CoordType"/>) to screen coordinates (pixels).
		/// </summary>
		/// <remarks>This method implicitly assumes that <see cref="AxisRect"/>
		/// has already been calculated via <see cref="AxisChange"/> or
		/// <see cref="Draw"/> methods, or the <see cref="AxisRect"/> is
		/// set manually (see <see cref="IsAxisRectAuto"/>).</remarks>
		/// <param name="ptF">The X,Y pair that defines the point in user
		/// coordinates.</param>
		/// <param name="coord">A <see cref="CoordType"/> type that defines the
		/// coordinate system in which the X,Y pair is defined.</param>
		/// <returns>A point in screen coordinates that corresponds to the
		/// specified user point.</returns>
		public PointF GeneralTransform( PointF ptF, CoordType coord )
		{
			// Setup the scaling data based on the axis rect
			this.xAxis.SetupScaleData( this );
			this.yAxis.SetupScaleData( this );
			this.y2Axis.SetupScaleData( this );

			PointF ptPix = new PointF();

			if ( coord == CoordType.AxisFraction )
			{
				ptPix.X = this.axisRect.Left + ptF.X * this.axisRect.Width;
				ptPix.Y = this.axisRect.Top + ptF.Y * this.axisRect.Height;
			}
			else if ( coord == CoordType.AxisXYScale )
			{
				ptPix.X = this.xAxis.Transform( ptF.X );
				ptPix.Y = this.yAxis.Transform( ptF.Y );
			}
			else if ( coord == CoordType.AxisXY2Scale )
			{
				ptPix.X = this.xAxis.Transform( ptF.X );
				ptPix.Y = this.y2Axis.Transform( ptF.Y );
			}
			else	// PaneFraction
			{
				ptPix.X = this.paneRect.Left + ptF.X * this.paneRect.Width;
				ptPix.Y = this.paneRect.Top + ptF.Y * this.paneRect.Height;
			}

			return ptPix;
		}

		/// <summary>
		/// Return the user scale values that correspond to the specified screen
		/// coordinate position (pixels).
		/// </summary>
		/// <remarks>This method implicitly assumes that <see cref="AxisRect"/>
		/// has already been calculated via <see cref="AxisChange"/> or
		/// <see cref="Draw"/> methods, or the <see cref="AxisRect"/> is
		/// set manually (see <see cref="IsAxisRectAuto"/>).</remarks>
		/// <param name="ptF">The X,Y pair that defines the screen coordinate
		/// point of interest</param>
		/// <param name="x">The resultant value in user coordinates from the
		/// <see cref="XAxis"/></param>
		/// <param name="y">The resultant value in user coordinates from the
		/// <see cref="YAxis"/></param>
		/// <param name="y2">The resultant value in user coordinates from the
		/// <see cref="Y2Axis"/></param>
		public void ReverseTransform( PointF ptF, out double x, out double y,
			out double y2 )
		{
			// Setup the scaling data based on the axis rect
			this.xAxis.SetupScaleData( this );
			this.yAxis.SetupScaleData( this );
			this.y2Axis.SetupScaleData( this );

			x = this.XAxis.ReverseTransform( ptF.X );
			y = this.YAxis.ReverseTransform( ptF.Y );
			y2 = this.Y2Axis.ReverseTransform( ptF.Y );
		}

		/// <summary>
		/// Find the object that lies closest to the specified mouse (screen) point.
		/// </summary>
		/// <remarks>
		/// This method will search through all of the graph objects, such as
		/// <see cref="Axis"/>, <see cref="Legend"/>, <see cref="PaneBase.Title"/>,
		/// <see cref="GraphItem"/>, and <see cref="CurveItem"/>.
		/// If the mouse point is within the bounding box of the items (or in the case
		/// of <see cref="ArrowItem"/> and <see cref="CurveItem"/>, within
		/// <see cref="Default.NearestTol"/> pixels), then the object will be returned.
		/// You must check the type of the object to determine what object was
		/// selected (for example, "if ( object is Legend ) ...").  The
		/// <see paramref="index"/> parameter returns the index number of the item
		/// within the selected object (such as the point number within a
		/// <see cref="CurveItem"/> object.
		/// </remarks>
		/// <param name="mousePt">The screen point, in pixel coordinates.</param>
		/// <param name="g">
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// </param>
		/// <param name="nearestObj">A reference to the nearest object to the
		/// specified screen point.  This can be any of <see cref="Axis"/>,
		/// <see cref="Legend"/>, <see cref="PaneBase.Title"/>,
		/// <see cref="TextItem"/>, <see cref="ArrowItem"/>, or <see cref="CurveItem"/>.
		/// Note: If the pane title is selected, then the <see cref="GraphPane"/> object
		/// will be returned.
		/// </param>
		/// <param name="index">The index number of the item within the selected object
		/// (where applicable).  For example, for a <see cref="CurveItem"/> object,
		/// <see paramref="index"/> will be the index number of the nearest data point,
		/// accessible via <see cref="CurveItem.Points">CurveItem.Points[index]</see>.
		/// index will be -1 if no data points are available.</param>
		/// <returns>true if an object was found, false otherwise.</returns>
		/// <seealso cref="FindNearestObject"/>
		public bool FindNearestObject( PointF mousePt, Graphics g, 
			out object nearestObj, out int index )
		{
			nearestObj = null;
			index = -1;

			// Make sure that the axes & data are being drawn
			if ( 	this.xAxis.Min < this.xAxis.Max &&
					this.yAxis.Min < this.yAxis.Max &&
					this.y2Axis.Min < this.y2Axis.Max )
			{
				float		scaleFactor = CalcScaleFactor();
				//int			hStack;
				//float		legendWidth, legendHeight;
				RectangleF	tmpRect;
				GraphItem	saveGraphItem = null;
				int			saveIndex = -1;
				ZOrder		saveZOrder = ZOrder.G_BehindAll;
	
				// Calculate the axis rect, deducting the area for the scales, titles, legend, etc.
				RectangleF tmpAxisRect = CalcAxisRect( g, scaleFactor );
	
				// See if the point is in a GraphItem
				// If so, just save the object and index so we can see if other overlying objects were
				// intersected as well.
				if ( this.GraphItemList.FindPoint( mousePt, this, g, scaleFactor, out index ) )
				{
					saveGraphItem = this.GraphItemList[index];
					saveIndex = index;
					saveZOrder = saveGraphItem.ZOrder;
				}
				// See if the point is in the legend
				if ( saveZOrder <= ZOrder.B_BehindLegend &&
					this.Legend.FindPoint( mousePt, this, scaleFactor, out index ) )
				{
					nearestObj = this.Legend;
					return true;
				}
				
				// See if the point is in the Pane Title
				if ( saveZOrder <= ZOrder.G_BehindAll && this.isShowTitle )
				{
					SizeF size = this.FontSpec.BoundingBox( g, this.title, scaleFactor );
					tmpRect = new RectangleF( ( this.paneRect.Left + this.paneRect.Right - size.Width ) / 2,
						this.paneRect.Top + this.marginTop * scaleFactor,
						size.Width, size.Height );
					if ( tmpRect.Contains( mousePt ) )
					{
						nearestObj = this;
						return true;
					}
				}

				// See if the point is in the Y Axis
				tmpRect = new RectangleF( this.paneRect.Left, tmpAxisRect.Top,
					tmpAxisRect.Left - this.paneRect.Left, tmpAxisRect.Height );
				if ( saveZOrder <= ZOrder.E_BehindAxis && tmpRect.Contains( mousePt ) )
				{
					nearestObj = this.YAxis;
					return true;
				}
				
				// See if the point is in the Y2 Axis
				tmpRect = new RectangleF( tmpAxisRect.Right, tmpAxisRect.Top,
					this.paneRect.Right - tmpAxisRect.Right, tmpAxisRect.Height );
				if ( saveZOrder <= ZOrder.E_BehindAxis && tmpRect.Contains( mousePt ) )
				{
					nearestObj = this.Y2Axis;
					return true;
				}
				
				// See if the point is in the X Axis
				tmpRect = new RectangleF( tmpAxisRect.Left, tmpAxisRect.Bottom,
					tmpAxisRect.Width, this.paneRect.Bottom - tmpAxisRect.Bottom );
				if ( saveZOrder <= ZOrder.E_BehindAxis && tmpRect.Contains( mousePt ) )
				{
					nearestObj = this.XAxis;
					return true;
				}
				
				CurveItem curve;
				// See if it's a data point
				if ( saveZOrder <= ZOrder.D_BehindCurves && FindNearestPoint( mousePt, out curve, out index ) )
				{
					nearestObj = curve;
					return true;
				}
				
				if ( saveGraphItem != null )
				{
					index = saveIndex;
					nearestObj = saveGraphItem;
					return true;
				}
			}
			
			return false;
		}

		/// <summary>
		/// Find the data point that lies closest to the specified mouse (screen)
		/// point.
		/// </summary>
		/// <remarks>
		/// This method will search only through the points for the specified
		/// curve to determine which point is
		/// nearest the mouse point.  It will only consider points that are within
		/// <see cref="Default.NearestTol"/> pixels of the screen point.
		/// </remarks>
		/// <param name="mousePt">The screen point, in pixel coordinates.</param>
		/// <param name="targetCurve">A <see cref="CurveItem"/> object containing
		/// the data points to be searched.</param>
		/// <param name="nearestCurve">A reference to the <see cref="CurveItem"/>
		/// instance that contains the closest point.  nearestCurve will be null if
		/// no data points are available.</param>
		/// <param name="iNearest">The index number of the closest point.  The
		/// actual data vpoint will then be <see cref="CurveItem.Points">CurveItem.Points[iNearest]</see>
		/// .  iNearest will
		/// be -1 if no data points are available.</param>
		/// <returns>true if a point was found and that point lies within
		/// <see cref="Default.NearestTol"/> pixels
		/// of the screen point, false otherwise.</returns>
		public bool FindNearestPoint( PointF mousePt, CurveItem targetCurve,
			out CurveItem nearestCurve, out int iNearest )
		{
			CurveList targetCurveList = new CurveList();
			targetCurveList.Add( targetCurve );
			return FindNearestPoint( mousePt, targetCurveList,
				out nearestCurve, out iNearest );
		}

		/// <summary>
		/// Find the data point that lies closest to the specified mouse (screen)
		/// point.
		/// </summary>
		/// <remarks>
		/// This method will search through all curves in
		/// <see cref="GraphPane.CurveList"/> to find which point is
		/// nearest.  It will only consider points that are within
		/// <see cref="Default.NearestTol"/> pixels of the screen point.
		/// </remarks>
		/// <param name="mousePt">The screen point, in pixel coordinates.</param>
		/// <param name="nearestCurve">A reference to the <see cref="CurveItem"/>
		/// instance that contains the closest point.  nearestCurve will be null if
		/// no data points are available.</param>
		/// <param name="iNearest">The index number of the closest point.  The
		/// actual data vpoint will then be <see cref="CurveItem.Points">CurveItem.Points[iNearest]</see>
		/// .  iNearest will
		/// be -1 if no data points are available.</param>
		/// <returns>true if a point was found and that point lies within
		/// <see cref="Default.NearestTol"/> pixels
		/// of the screen point, false otherwise.</returns>
		public bool FindNearestPoint( PointF mousePt,
			out CurveItem nearestCurve, out int iNearest )
		{
			return FindNearestPoint( mousePt, this.curveList,
				out nearestCurve, out iNearest );
		}

		/// <summary>
		/// Find the data point that lies closest to the specified mouse (screen)
		/// point.
		/// </summary>
		/// <remarks>
		/// This method will search through the specified list of curves to find which point is
		/// nearest.  It will only consider points that are within
		/// <see cref="Default.NearestTol"/> pixels of the screen point, and it will
		/// only consider <see cref="CurveItem"/>'s that are in 
		/// <paramref name="targetCurveList"/>.
		/// </remarks>
		/// <param name="mousePt">The screen point, in pixel coordinates.</param>
		/// <param name="targetCurveList">A <see cref="CurveList"/> object containing
		/// a subset of <see cref="CurveItem"/>'s to be searched.</param>
		/// <param name="nearestCurve">A reference to the <see cref="CurveItem"/>
		/// instance that contains the closest point.  nearestCurve will be null if
		/// no data points are available.</param>
		/// <param name="iNearest">The index number of the closest point.  The
		/// actual data vpoint will then be <see cref="CurveItem.Points">CurveItem.Points[iNearest]</see>
		/// .  iNearest will
		/// be -1 if no data points are available.</param>
		/// <returns>true if a point was found and that point lies within
		/// <see cref="Default.NearestTol"/> pixels
		/// of the screen point, false otherwise.</returns>
		public bool FindNearestPoint( PointF mousePt, CurveList targetCurveList,
			out CurveItem nearestCurve, out int iNearest )
		{
			CurveItem nearestBar = null;
			int iNearestBar = -1;
			nearestCurve = null;
			iNearest = -1;

			// If the point is outside the axisRect, always return false
			if ( ! axisRect.Contains( mousePt ) )
				return false;

			double	x, y, y2;
			ReverseTransform( mousePt, out x, out y, out y2 );

			if ( xAxis.Min == xAxis.Max || yAxis.Min == yAxis.Max ||
				y2Axis.Min == y2Axis.Max )
				return false;

			ValueHandler valueHandler = new ValueHandler( this, false );

			double	xPixPerUnit = axisRect.Width / ( xAxis.Max - xAxis.Min );
			double	yPixPerUnit = axisRect.Height / ( yAxis.Max - yAxis.Min );
			double	y2PixPerUnit = axisRect.Height / ( y2Axis.Max - y2Axis.Min );

			double	yPixPerUnitAct, yAct, yMinAct, yMaxAct;
			double	minDist = 1e20;
			double	xVal, yVal, dist=99999, distX, distY;
			double	tolSquared = Default.NearestTol * Default.NearestTol;

			int		iBar = 0;

			foreach ( CurveItem curve in targetCurveList )
			{
				//test for pie first...if it's a pie rest of method superfluous
				if ( curve is PieItem && curve.IsVisible )
				{
					if ( ((PieItem)curve).SlicePath.IsVisible (mousePt) )
					{
						nearestBar = curve;
						iNearestBar = 0;
					}

					continue;
				}
				else if ( curve.IsVisible )
				{
					if ( curve.IsY2Axis )
					{
						yAct = y2;
						yMinAct = y2Axis.Min;
						yMaxAct = y2Axis.Max;
						yPixPerUnitAct = y2PixPerUnit;
					}
					else
					{
						yAct = y;
						yMinAct = yAxis.Min;
						yMaxAct = yAxis.Max;
						yPixPerUnitAct = yPixPerUnit;
					}

					PointPairList points = curve.Points;
					float barWidth = curve.GetBarWidth( this );
					double barWidthUserHalf;
					bool isXBaseAxis = ( curve.BaseAxis( this ) == XAxis );
					if ( isXBaseAxis )
						barWidthUserHalf = barWidth / xPixPerUnit / 2.0;
					else
						barWidthUserHalf = barWidth / yPixPerUnit / 2.0;

					if ( points != null )
					{
						for ( int iPt=0; iPt<curve.NPts; iPt++ )
						{
							// xVal is the user scale X value of the current point
							if ( xAxis.IsOrdinal && ! curve.IsOverrideOrdinal )
								xVal = (double) iPt + 1.0;
							else
								xVal = points[iPt].X;

							// yVal is the user scale Y value of the current point
							if ( yAxis.IsOrdinal && ! curve.IsOverrideOrdinal )
								yVal = (double) iPt + 1.0;
							else
								yVal = points[iPt].Y;

							if (	xVal != PointPair.Missing &&
									yVal != PointPair.Missing )
							{

								if ( curve.IsBar || curve is ErrorBarItem ||
									curve is HiLowBarItem )
								{
									double baseVal, lowVal, hiVal;
									valueHandler.GetValues( curve, iPt, out baseVal,
											out lowVal, out hiVal );

									if ( lowVal > hiVal )
									{
										double tmpVal = lowVal;
										lowVal = hiVal;
										hiVal = tmpVal;
									}

									if ( isXBaseAxis )
									{
										
										double centerVal = valueHandler.BarCenterValue( curve, barWidth, iPt, xVal, iBar );
										
										if (	x < centerVal - barWidthUserHalf ||
												x > centerVal + barWidthUserHalf ||
												yAct < lowVal || yAct > hiVal )
											continue;
									}
									else
									{
										double centerVal = valueHandler.BarCenterValue( curve, barWidth, iPt, yVal, iBar );
										
										if (	yAct < centerVal - barWidthUserHalf ||
												yAct > centerVal + barWidthUserHalf ||
												x < lowVal || x > hiVal )
											continue;
									}

									if ( nearestBar == null )
									{
										iNearestBar = iPt;
										nearestBar = curve;
									}
								}
								else if (	xVal >= xAxis.Min && xVal <= xAxis.Max &&
											yVal >= yMinAct && yVal <= yMaxAct )

								{
									distX = (xVal - x) * xPixPerUnit;
									distY = (yVal - yAct) * yPixPerUnitAct;
									dist = distX * distX + distY * distY;

									if ( dist >= minDist )
										continue;
										
									minDist = dist;
									iNearest = iPt;
									nearestCurve = curve;
								}

							}
						}
						
						if ( curve.IsBar )
							iBar++;
					}
				}
			}

			if ( nearestCurve is LineItem )
			{
				float halfSymbol = (float) ( ((LineItem)nearestCurve).Symbol.Size *
					CalcScaleFactor() / 2 );
				minDist -= halfSymbol * halfSymbol;
				if ( minDist < 0 )
					minDist = 0;
			}

			if ( minDist >= tolSquared && nearestBar != null )
			{
				// if no point met the tolerance, but a bar was found, use it
				nearestCurve = nearestBar;
				iNearest = iNearestBar;
				return true;
			}
			else if ( minDist < tolSquared )
			{
				// Did we find a close point, and is it within the tolerance?
				// (minDist is the square of the distance in pixel units)
				return true;
			}
			else  // otherwise, no valid point found
				return false;
		}

		/// <summary>
		/// Determine the width, in screen pixel units, of each bar cluster including
		/// the cluster gaps and bar gaps.
		/// </summary>
		/// <remarks>This method calls the <see cref="Axis.GetClusterWidth"/>
		/// method for the base <see cref="Axis"/> for <see cref="Bar"/> graphs
		/// (the base <see cref="Axis"/> is assigned by the <see cref="GraphPane.BarBase"/>
		/// property).
		/// </remarks>
		/// <seealso cref="ZedGraph.BarBase"/>
		/// <seealso cref="GraphPane.BarBase"/>
		/// <seealso cref="Axis.GetClusterWidth"/>
		/// <seealso cref="GraphPane.BarType"/>
		/// <returns>The width of each bar cluster, in pixel units</returns>
		public float GetClusterWidth()
		{
			return BarBaseAxis().GetClusterWidth( this );
		}
		
		/// <summary>
		/// Determine the <see cref="Axis"/> from which the <see cref="Bar"/> charts are based.
		/// </summary>
		/// <seealso cref="ZedGraph.BarBase"/>
		/// <seealso cref="GraphPane.BarBase"/>
		/// <seealso cref="Axis.GetClusterWidth"/>
		/// <returns>The <see cref="Axis"/> class for the axis from which the bars are based</returns>
		public Axis BarBaseAxis()
		{
			Axis barAxis;
			if ( this.BarBase == BarBase.Y )
				barAxis = this.YAxis;
			else if ( this.BarBase == BarBase.Y2 )
				barAxis = this.Y2Axis;
			else
				barAxis = this.XAxis;
			
			return barAxis;
		}

		#endregion

	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Engineer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions