//============================================================================
//ZedGraph Class Library - A Flexible Line Graph/Bar Graph Library in C#
//Copyright ?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 DrawGraph
{
	// 
	// ZedGraph is a class library and UserControl () that display
	// 2D line graphs of user specified data.  The ZedGraph namespace includes all functionality
	// required to draw, modify, and update the graph.
	// 
	/// 
	/// Class  encapsulates the graph pane, which is all display elements
	/// associated with an individual graph.
	/// 
	/// 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.
	/// 
	/// 
	///  John Champion modified by Jerry Vos 
	///  $Revision: 3.79 $ $Date: 2007/06/02 06:56:03 $ 
	[Serializable]
	public class GraphPane : PaneBase, ICloneable, ISerializable
	{
	#region Events
		/// 
		/// A delegate to provide notification through the 
		/// when  is called.
		/// 
		/// The  for which AxisChange() has
		/// been called.
		/// 
		public delegate void AxisChangeEventHandler( GraphPane pane );
		/// 
		/// Subscribe to this event to be notified when  is called.
		/// 
		public event AxisChangeEventHandler AxisChangeEvent;
	#endregion
	#region Private Fields
		// Item subclasses ////////////////////////////////////////////////////////////////////
		/// Private field instance of the  class.  Use the
		/// public property  to access this class.
		private XAxis _xAxis;
		/// Private field instance of the  class.  Use the
		/// public property  to access this class.
		private X2Axis _x2Axis;
		/// Private field instance of the  class.  Use the
		/// public property  to access this class.
		private YAxisList _yAxisList;
		/// Private field instance of the  class.  Use the
		/// public property  to access this class.
		private Y2AxisList _y2AxisList;
		/// Private field instance of the  class.  Use the
		/// public property  to access this class.
		private CurveList _curveList;
		/// 
		/// private value that contains a , which stores prior
		///  objects containing scale range information.  This enables
		/// zooming and panning functionality for the .
		/// 
		private ZoomStateStack _zoomStack;
		// Chart Properties //////////////////////////////////////////////////////////////
		internal Chart _chart;
		internal BarSettings _barSettings;
		/// 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  to access
		/// this value. 
		private bool _isIgnoreInitial;
		/// Private field that determines whether or not initial
		///  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  to access
		/// this value. 
		private bool _isIgnoreMissing;
		///  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
		///  to access this value.
		/// 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.
		private bool _isBoundedRanges;
		/// 
		/// private field that determines if ZedGraph should modify the scale ranges for the Y and Y2
		/// axes such that the number of steps, and therefore the grid lines, line up.  Use the
		/// public property  to acccess this value.
		/// 
		private bool _isAlignGrids;
		/// Private field that determines how the 
		/// graphs will be displayed. See the  enum
		/// for the individual types available.
		/// To access this value, use the public property .
		/// 
		/// 
		private LineType _lineType;
	#endregion
	#region Defaults
		/// 
		/// A simple struct that defines the
		/// default property values for the  class.
		/// 
		public new struct Default
		{
			/// 
			/// The default settings for the  scale ignore initial
			/// zero values option ( property).
			/// true to have the auto-scale-range code ignore the initial data points
			/// until the first non-zero Y value, false otherwise.
			/// 
			public static bool IsIgnoreInitial = false;
			/// 
			/// The default settings for the  scale bounded ranges option
			/// ( property).
			/// true to have the auto-scale-range code subset the data according to any
			/// manually set scale values, false otherwise.
			/// 
			public static bool IsBoundedRanges = false;
			/// The default value for the  property, which
			/// determines if the lines are drawn in normal or "stacked" mode.  See the
			///  for more information.
			/// 
			/// 
			public static LineType LineType = LineType.Normal;
			/// 
			/// The default width of a bar cluster 
			/// on a  graph.  This value only applies to
			///  graphs, and only when the
			///  is ,
			///  or .
			/// This dimension is expressed in terms of X scale user units.
			/// 
			/// 
			/// 
			public static double ClusterScaleWidth = 1.0;
			/// 
			/// The tolerance that is applied to the
			///  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.
			/// 
			public static double NearestTol = 7.0;
		}
	#endregion
	#region Class Instance Properties
		/// 
		/// Gets or sets the list of  items for this 
		/// 
		/// A reference to a  collection object
		public CurveList CurveList
		{
			get { return _curveList; }
			set { _curveList = value; }
		}
		/// 
		/// Accesses the  for this graph
		/// 
		/// A reference to a  object
		public XAxis XAxis
		{
			get { return _xAxis; }
		}
		/// 
		/// Accesses the  for this graph
		/// 
		/// A reference to a  object
		public X2Axis X2Axis
		{
			get { return _x2Axis; }
		}
		/// 
		/// Accesses the primary  for this graph
		/// 
		/// A reference to a  object
		/// 
		/// 
		public YAxis YAxis
		{
			get { return _yAxisList[0] as YAxis; }
		}
		/// 
		/// Accesses the primary  for this graph
		/// 
		/// A reference to a  object
		/// 
		/// 
		public Y2Axis Y2Axis
		{
			get { return _y2AxisList[0] as Y2Axis; }
		}
		/// 
		/// Gets the collection of Y axes that belong to this .
		/// 
		public YAxisList YAxisList
		{
			get { return _yAxisList; }
		}
		/// 
		/// Gets the collection of Y2 axes that belong to this .
		/// 
		public Y2AxisList Y2AxisList
		{
			get { return _y2AxisList; }
		}
		/// 
		/// Gets the  instance for this .
		/// 
		public Chart Chart
		{
			get { return _chart; }
		}
		/// 
		/// Gets the  instance for this ,
		/// which stores the global properties for bar type charts.
		/// 
		public BarSettings BarSettings
		{
			get { return _barSettings; }
		}
	#endregion
	#region General Properties
		/// 
		/// Gets or sets a boolean value that affects the data range that is considered
		/// for the automatic scale ranging.
		/// 
		/// If true, then initial data points where the Y value
		/// is zero are not included when automatically determining the scale ,
		/// , and  size.
		/// All data after the first non-zero Y value are included.
		/// 
		/// 
		[Bindable( true ), Browsable( true ), Category( "Display" ), NotifyParentProperty( true )]
		[Description("Determines whether the auto-ranged scale will include all data points" +
			" or just the visible data points")]
		public bool IsIgnoreInitial
		{
			get { return _isIgnoreInitial; }
			set { _isIgnoreInitial = value; }
		}
		///  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.
		/// 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.
		public bool IsBoundedRanges
		{
			get { return _isBoundedRanges; }
			set { _isBoundedRanges = value; }
		}
		/// Gets or sets a value that determines whether or not initial
		///  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  to access
		/// this value. 
		public bool IsIgnoreMissing
		{
			get { return _isIgnoreMissing; }
			set { _isIgnoreMissing = value; }
		}
		
		/// 
		/// Gets or sets a value that determines if ZedGraph should modify the scale ranges
		/// for the Y and Y2 axes such that the number of major steps, and therefore the
		/// major grid lines, line up.
		/// 
		/// 
		/// This property affects the way that  selects the scale
		/// ranges for the Y and Y2 axes.  It applies to the scale ranges of all Y and Y2 axes,
		/// but only if the  is set to true.
		/// 
		public bool IsAlignGrids
		{
			get { return _isAlignGrids; }
			set { _isAlignGrids = value; }
		}
		/// Determines how the 
		/// graphs will be displayed. See the  enum
		/// for the individual types available.
		/// 
		/// 
		public LineType LineType
		{
			get { return _lineType; }
			set { _lineType = value; }
		}
		/// 
		/// Gets a value that indicates whether or not the  for
		/// this  is empty.  Note that this value is only used for
		/// the .
		/// 
		public bool IsZoomed
		{
			get { return !_zoomStack.IsEmpty; }
		}
		/// 
		/// Gets a reference to the  for this .
		/// 
		public ZoomStateStack ZoomStack
		{
			get { return _zoomStack; }
		}
	#endregion
	#region Constructors
		/// 
		/// Default Constructor.  Sets the  to (0, 0, 500, 375), and
		/// sets the  and  values to empty
		/// strings.
		/// 
		public GraphPane()
			: this( new RectangleF( 0, 0, 500, 375 ), "", "", "" )
		{
		}
		/// 
		/// Constructor for the  object.  This routine will
		/// initialize all member variables and classes, setting appropriate default
		/// values as defined in the  class.
		/// 
		///  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
		///  property.
		/// 
		/// The  for this 
		/// The  for the 
		/// The  for the 
		public GraphPane( RectangleF rect, string title,
			string xTitle, string yTitle )
			: base( title, rect )
		{
			_xAxis = new XAxis( xTitle );
			_x2Axis = new X2Axis( "" );
			_yAxisList = new YAxisList();
			_y2AxisList = new Y2AxisList();
			_yAxisList.Add( new YAxis( yTitle ) );
			_y2AxisList.Add( new Y2Axis( string.Empty ) );
			_curveList = new CurveList();
			_zoomStack = new ZoomStateStack();
			_isIgnoreInitial = Default.IsIgnoreInitial;
			_isBoundedRanges = Default.IsBoundedRanges;
			_isAlignGrids = false;
			_chart = new Chart();
			_barSettings = new BarSettings( this );
			_lineType = Default.LineType;
		}
		/// 
		/// The Copy Constructor
		/// 
		/// The GraphPane object from which to copy
		public GraphPane( GraphPane rhs )
			: base( rhs )
		{
			// copy values for all the value types
			_isIgnoreInitial = rhs.IsIgnoreInitial;
			_isBoundedRanges = rhs._isBoundedRanges;
			_isAlignGrids = rhs._isAlignGrids;
			_chart = rhs._chart.Clone();
			_barSettings = new BarSettings( rhs._barSettings, this );
			_lineType = rhs.LineType;
			// copy all the reference types with deep copies
			_xAxis = new XAxis( rhs.XAxis );
			_x2Axis = new X2Axis( rhs.X2Axis );
			_yAxisList = new YAxisList( rhs._yAxisList );
			_y2AxisList = new Y2AxisList( rhs._y2AxisList );
			_curveList = new CurveList( rhs.CurveList );
			_zoomStack = new ZoomStateStack( rhs._zoomStack );
		}
		/// 
		/// Implement the  interface in a typesafe manner by just
		/// calling the typed version of 
		/// 
		/// A deep copy of this object
		object ICloneable.Clone()
		{
			return this.Clone();
		}
		/// 
		/// Typesafe, deep-copy clone method.
		/// 
		/// A new, independent copy of this class
		public GraphPane Clone()
		{
			return new GraphPane( this );
		}
	#endregion
	#region Serialization
		/// 
		/// Current schema value that defines the version of the serialized file
		/// 
		//changed to 2 when yAxisList and y2AxisList were added
		//changed to 3 when chart object was added
		//changed to 10 when refactored to version 5
		//changed to 11 when added x2axis
		public const int schema2 = 11;
		/// 
		/// Constructor for deserializing objects
		/// 
		/// A  instance that defines the serialized data
		/// 
		/// A  instance that contains the serialized data
		/// 
		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 ) );
			if ( sch >= 11 )
				_x2Axis = (X2Axis)info.GetValue( "x2Axis", typeof( X2Axis ) );
			else
				_x2Axis = new X2Axis( "" );
			_yAxisList = (YAxisList)info.GetValue( "yAxisList", typeof( YAxisList ) );
			_y2AxisList = (Y2AxisList)info.GetValue( "y2AxisList", typeof( Y2AxisList ) );
			_curveList = (CurveList)info.GetValue( "curveList", typeof( CurveList ) );
			_chart = (Chart) info.GetValue( "chart", typeof( Chart ) );
			_barSettings = (BarSettings)info.GetValue( "barSettings", typeof( BarSettings ) );
			_barSettings._ownerPane = this;
			_isIgnoreInitial = info.GetBoolean( "isIgnoreInitial" );
			_isBoundedRanges = info.GetBoolean( "isBoundedRanges" );
			_isIgnoreMissing = info.GetBoolean( "isIgnoreMissing" );
			_isAlignGrids = info.GetBoolean( "isAlignGrids" );
			_lineType = (LineType)info.GetValue( "lineType", typeof( LineType ) );
			_zoomStack = new ZoomStateStack();
		}
		/// 
		/// Populates a  instance with the data needed to serialize the target object
		/// 
		/// A  instance that defines the serialized data
		/// A  instance that contains the serialized data
		[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( "x2Axis", _x2Axis );
			info.AddValue( "yAxisList", _yAxisList );
			info.AddValue( "y2AxisList", _y2AxisList );
			info.AddValue( "curveList", _curveList );
			info.AddValue( "chart", _chart );
			info.AddValue( "barSettings", _barSettings );
			info.AddValue( "isIgnoreInitial", _isIgnoreInitial );
			info.AddValue( "isBoundedRanges", _isBoundedRanges );
			info.AddValue( "isIgnoreMissing", _isIgnoreMissing );
			info.AddValue( "isAlignGrids", _isAlignGrids );
			info.AddValue( "lineType", _lineType );
		}
	#endregion
	#region Rendering Methods
		/// 
		/// AxisChange causes the axes scale ranges to be recalculated based on the current data range.
		/// 
		/// 
		/// 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
		/// (, , )
		/// will be modified.  You must call  after calling
		/// AxisChange to make sure the display gets updated.
		/// This overload of AxisChange just uses the default Graphics instance for the screen.
		/// If you have a Graphics instance available from your Windows Form, you should use
		/// the  overload instead.
		/// 
		public void AxisChange()
		{
			using ( Graphics g = Graphics.FromHwnd( IntPtr.Zero ) )
				AxisChange( g );
		}
		/// 
		/// AxisChange causes the axes scale ranges to be recalculated based on the current data range.
		/// 
		/// 
		/// 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
		/// (, , )
		/// will be modified.  You must call
		///  after calling AxisChange to make sure the display gets updated.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		public void AxisChange( Graphics g )
		{
			//double	xMin, xMax, yMin, yMax, y2Min, y2Max;
			// Get the scale range of the data (all curves)
			_curveList.GetRange( /* out xMin, out xMax, out yMin,
				out yMax, out y2Min, out y2Max, */
				_isIgnoreInitial, _isBoundedRanges, 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.X2Axis.IsVisible = false;
				this.YAxis.IsVisible = false;
				this.Y2Axis.IsVisible = false;
				_chart.Border.IsVisible = false;
				//this.Legend.Position = LegendPos.TopCenter;
			}
			// Set the ClusterScaleWidth, if needed
			//_barSettings.CalcClusterScaleWidth();
			if ( _barSettings._clusterScaleWidthAuto )
				_barSettings._clusterScaleWidth = 1.0;
			// if the ChartRect is not yet determined, then pick a scale based on a default ChartRect
			// size (using 75% of Rect -- code is in Axis.CalcMaxLabels() )
			// With the scale picked, call CalcChartRect() so calculate a real ChartRect
			// then let the scales re-calculate to make sure that the assumption was ok
			if ( _chart._isRectAuto )
			{
				PickScale( g, scaleFactor );
				_chart._rect = CalcChartRect( g );
				//this.pieRect = PieItem.CalcPieRect( g, this, scaleFactor, this.chartRect );
			}
			// Pick new scales based on the range
			PickScale( g, scaleFactor );
			// Set the ClusterScaleWidth, if needed
			_barSettings.CalcClusterScaleWidth();
			// Trigger the AxisChangeEvent
			if ( this.AxisChangeEvent != null )
				this.AxisChangeEvent( this );
		}
		private void PickScale( Graphics g, float scaleFactor )
		{
			int maxTics = 0;
			_xAxis._scale.PickScale( this, g, scaleFactor );
			_x2Axis._scale.PickScale( this, g, scaleFactor );
			foreach ( Axis axis in _yAxisList )
			{
				axis._scale.PickScale( this, g, scaleFactor );
				if ( axis._scale.MaxAuto )
				{
					int nTics = axis._scale.CalcNumTics();
					maxTics = nTics > maxTics ? nTics : maxTics;
				}
			}
			foreach ( Axis axis in _y2AxisList )
			{
				axis._scale.PickScale( this, g, scaleFactor );
				if ( axis._scale.MaxAuto )
				{
					int nTics = axis._scale.CalcNumTics();
					maxTics = nTics > maxTics ? nTics : maxTics;
				}
			}
			if ( _isAlignGrids )
			{
				foreach ( Axis axis in _yAxisList )
					ForceNumTics( axis, maxTics );
				foreach ( Axis axis in _y2AxisList )
					ForceNumTics( axis, maxTics );
			}
		}
		private void ForceNumTics( Axis axis, int numTics )
		{
			if ( axis._scale.MaxAuto )
			{
				int nTics = axis._scale.CalcNumTics();
				if ( nTics < numTics )
					axis._scale._maxLinearized += axis._scale._majorStep * ( numTics - nTics );
			}
		}
		/// 
		/// Draw all elements in the  to the specified graphics device.
		/// 
		/// 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
		///  object.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		public override void Draw( Graphics g )
		{
			// Calculate the chart 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 GraphObj objects that lie at
			// ZOrder.G_BehindAll
			base.Draw( g );
			if ( _rect.Width <= 1 || _rect.Height <= 1 )
				return;
			// Clip everything to the rect
			g.SetClip( _rect );
			// calculate scaleFactor on "normal" pane size (BaseDimension)
			float scaleFactor = this.CalcScaleFactor();
			// if the size of the ChartRect is determined automatically, then do so
			// otherwise, calculate the legendrect, scalefactor, hstack, and legendwidth parameters
			// but leave the ChartRect alone
			if ( _chart._isRectAuto )
			{
				_chart._rect = CalcChartRect( g, scaleFactor );
				//this.pieRect = PieItem.CalcPieRect( g, this, scaleFactor, this.chartRect );
			}
			else
				CalcChartRect( g, scaleFactor );
			// do a sanity check on the ChartRect
			if ( _chart._rect.Width < 1 || _chart._rect.Height < 1 )
				return;
			// Draw the graph features only if there is at least one curve with data
			// if ( _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 = AxisRangesValid();
			// Setup the axes for graphing - This setup must be done before
			// the GraphObj's are drawn so that the Transform functions are
			// ready.  Also, this should be done before CalcChartRect so that the
			// Axis.Cross - shift parameter can be calculated.
			_xAxis.Scale.SetupScaleData( this, _xAxis );
			_x2Axis.Scale.SetupScaleData( this, _x2Axis );
			foreach ( Axis axis in _yAxisList )
				axis.Scale.SetupScaleData( this, axis );
			foreach ( Axis axis in _y2AxisList )
				axis.Scale.SetupScaleData( this, axis );
			// Draw the GraphItems that are behind the Axis objects
			if ( showGraf )
				_graphObjList.Draw( g, this, scaleFactor, ZOrder.G_BehindChartFill );
			// Fill the axis background
			_chart.Fill.Draw( g, _chart._rect );
			if ( showGraf )
			{
				// Draw the GraphItems that are behind the CurveItems
				_graphObjList.Draw( g, this, scaleFactor, ZOrder.F_BehindGrid );
				DrawGrid( g, scaleFactor );
				// Draw the GraphItems that are behind the CurveItems
				_graphObjList.Draw( g, this, scaleFactor, ZOrder.E_BehindCurves );
				// Clip the points to the actual plot area
				g.SetClip( _chart._rect );
				_curveList.Draw( g, this, scaleFactor );
				g.SetClip( _rect );
			}
			if ( showGraf )
			{
				// Draw the GraphItems that are behind the Axis objects
				_graphObjList.Draw( g, this, scaleFactor, ZOrder.D_BehindAxis );
				// Draw the Axes
				_xAxis.Draw( g, this, scaleFactor, 0.0f );
				_x2Axis.Draw( g, this, scaleFactor, 0.0f );
				float yPos = 0;
				foreach ( Axis axis in _yAxisList )
				{
					axis.Draw( g, this, scaleFactor, yPos );
					yPos += axis._tmpSpace;
				}
				yPos = 0;
				foreach ( Axis axis in _y2AxisList )
				{
					axis.Draw( g, this, scaleFactor, yPos );
					yPos += axis._tmpSpace;
				}
				// Draw the GraphItems that are behind the Axis border
				_graphObjList.Draw( g, this, scaleFactor, ZOrder.C_BehindChartBorder );
			}
			// Border the axis itself
			_chart.Border.Draw( g, this, scaleFactor, _chart._rect );
			if ( showGraf )
			{
				// Draw the GraphItems that are behind the Legend object
				_graphObjList.Draw( g, this, scaleFactor, ZOrder.B_BehindLegend );
				_legend.Draw( g, this, scaleFactor );
				// Draw the GraphItems that are in front of all other items
				_graphObjList.Draw( g, this, scaleFactor, ZOrder.A_InFront );
			}
			// Reset the clipping
			g.ResetClip();
			// Reset scale data
			// this sets the temp values to NaN to cause an exception if these values are
			// being used improperly
			// Don't do this, since the web control needs access
			/*
			_xAxis.Scale.ResetScaleData();
			foreach ( Axis axis in _yAxisList )
				axis.Scale.ResetScaleData();
			foreach ( Axis axis in _y2AxisList )
				axis.Scale.ResetScaleData();
			*/
		}
		internal void DrawGrid( Graphics g, float scaleFactor )
		{
			_xAxis.DrawGrid( g, this, scaleFactor, 0.0f );
			_x2Axis.DrawGrid( g, this, scaleFactor, 0.0f );
			float shiftPos = 0.0f;
			foreach ( YAxis yAxis in _yAxisList )
			{
				yAxis.DrawGrid( g, this, scaleFactor, shiftPos );
				shiftPos += yAxis._tmpSpace;
			}
			shiftPos = 0.0f;
			foreach ( Y2Axis y2Axis in _y2AxisList )
			{
				y2Axis.DrawGrid( g, this, scaleFactor, shiftPos );
				shiftPos += y2Axis._tmpSpace;
			}
		}
		private bool AxisRangesValid()
		{
			bool showGraf = _xAxis._scale._min < _xAxis._scale._max &&
					_x2Axis._scale._min < _x2Axis._scale._max;
			foreach ( Axis axis in _yAxisList )
				if ( axis._scale._min > axis._scale._max )
					showGraf = false;
			foreach ( Axis axis in _y2AxisList )
				if ( axis._scale._min > axis._scale._max )
					showGraf = false;
			return showGraf;
		}
		/// 
		/// Calculate the  based on the .
		/// 
		/// The ChartRect
		/// is the plot area bounded by the axes, and the rect is the total area as
		/// specified by the client application.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// The calculated chart rect, in pixel coordinates.
		public RectangleF CalcChartRect( Graphics g )
		{
			// Calculate the chart rect, deducting the area for the scales, titles, legend, etc.
			//int		hStack;
			//float	legendWidth, legendHeight;
			return CalcChartRect( g, CalcScaleFactor() );
		}
		/// 
		/// Calculate the  based on the .
		/// 
		/// The ChartRect
		/// is the plot area bounded by the axes, and the rect is the total area as
		/// specified by the client application.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// 
		/// The scaling factor for the features of the graph based on the .  This
		/// scaling factor is calculated by the  method.  The scale factor
		/// represents a linear multiple to be applied to font sizes, symbol sizes, etc.
		/// 
		/// The calculated chart rect, in pixel coordinates.
		public RectangleF CalcChartRect( Graphics g, float scaleFactor )
		{
			// chart 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 = 0;
			//float minSpaceY = 0;
			//float minSpaceY2 = 0;
			float totSpaceY = 0;
			//float spaceY2 = 0;
			// actual minimum axis space for the left side of the chart rect
			float minSpaceL = 0;
			// actual minimum axis space for the right side of the chart rect
			float minSpaceR = 0;
			// actual minimum axis space for the bottom side of the chart rect
			float minSpaceB = 0;
			// actual minimum axis space for the top side of the chart rect
			float minSpaceT = 0;
			_xAxis.CalcSpace( g, this, scaleFactor, out minSpaceB );
			_x2Axis.CalcSpace( g, this, scaleFactor, out minSpaceT );
			//minSpaceB = _xAxis.tmpMinSpace;
			foreach ( Axis axis in _yAxisList )
			{
				float fixedSpace;
				float tmp = axis.CalcSpace( g, this, scaleFactor, out fixedSpace );
				//if ( !axis.CrossAuto || axis.Cross < _xAxis.Min )
				if ( axis.IsCrossShifted( this ) )
					totSpaceY += tmp;
				minSpaceL += fixedSpace;
			}
			foreach ( Axis axis in _y2AxisList )
			{
				float fixedSpace;
				float tmp = axis.CalcSpace( g, this, scaleFactor, out fixedSpace );
				//if ( !axis.CrossAuto || axis.Cross < _xAxis.Min )
				if ( axis.IsCrossShifted( this ) )
					totSpaceY += tmp;
				minSpaceR += fixedSpace;
			}
			float spaceB = 0, spaceT = 0, spaceL = 0, spaceR = 0;
			SetSpace( _xAxis, clientRect.Height - _xAxis._tmpSpace, ref spaceB, ref spaceT );
//			minSpaceT = Math.Max( minSpaceT, spaceT );
			SetSpace( _x2Axis, clientRect.Height - _x2Axis._tmpSpace, ref spaceT, ref spaceB );
			_xAxis._tmpSpace = spaceB;
			_x2Axis._tmpSpace = spaceT;
			float totSpaceL = 0;
			float totSpaceR = 0;
			foreach ( Axis axis in _yAxisList )
			{
				SetSpace( axis, clientRect.Width - totSpaceY, ref spaceL, ref spaceR );
				minSpaceR = Math.Max( minSpaceR, spaceR );
				totSpaceL += spaceL;
				axis._tmpSpace = spaceL;
			}
			foreach ( Axis axis in _y2AxisList )
			{
				SetSpace( axis, clientRect.Width - totSpaceY, ref spaceR, ref spaceL );
				minSpaceL = Math.Max( minSpaceL, spaceL );
				totSpaceR += spaceR;
				axis._tmpSpace = spaceR;
			}
			RectangleF tmpRect = clientRect;
			totSpaceL = Math.Max( totSpaceL, minSpaceL );
			totSpaceR = Math.Max( totSpaceR, minSpaceR );
			spaceB = Math.Max( spaceB, minSpaceB );
			spaceT = Math.Max( spaceT, minSpaceT );
			tmpRect.X += totSpaceL;
			tmpRect.Width -= totSpaceL + totSpaceR;
			tmpRect.Height -= spaceT + spaceB;
			tmpRect.Y += spaceT;
			_legend.CalcRect( g, this, scaleFactor, ref tmpRect );
			return tmpRect;
		}
		private void SetSpace( Axis axis, float clientSize, ref float spaceNorm, ref float spaceAlt )
		{
			//spaceNorm = 0;
			//spaceAlt = 0;
			float crossFrac = axis.CalcCrossFraction( this );
			float crossPix = crossFrac * ( 1 + crossFrac ) * ( 1 + crossFrac * crossFrac ) * clientSize;
			if ( !axis.IsPrimary( this ) && axis.IsCrossShifted( this ) )
				axis._tmpSpace = 0;
			if ( axis._tmpSpace < crossPix )
				axis._tmpSpace = 0;
			else if ( crossPix > 0 )
				axis._tmpSpace -= crossPix;
			if ( axis._scale._isLabelsInside && ( axis.IsPrimary( this ) || ( crossFrac != 0.0 && crossFrac != 1.0 ) ) )
				spaceAlt = axis._tmpSpace;
			else
				spaceNorm = axis._tmpSpace;
		}
		/// 
		/// This method will set the  property for all three axes;
		/// , , and .
		/// 
		/// The 
		/// is calculated using the currently required space multiplied by a fraction
		/// (bufferFraction).
		/// The currently required space is calculated using , 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  method for
		/// each .
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 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.
		/// If true, then this method will only modify the 
		/// property if the calculated result is more than the current value.
		public void SetMinSpaceBuffer( Graphics g, float bufferFraction, bool isGrowOnly )
		{
			_xAxis.SetMinSpaceBuffer( g, this, bufferFraction, isGrowOnly );
			_x2Axis.SetMinSpaceBuffer( g, this, bufferFraction, isGrowOnly );
			foreach ( Axis axis in _yAxisList )
				axis.SetMinSpaceBuffer( g, this, bufferFraction, isGrowOnly );
			foreach ( Axis axis in _y2AxisList )
				axis.SetMinSpaceBuffer( g, this, bufferFraction, isGrowOnly );
		}
	#endregion
	#region AddCurve Methods
		/// 
		/// Add a curve ( object) to the plot with
		/// the given data points (double arrays) and properties.
		/// This is simplified way to add curves without knowledge of the
		///  class.  An alternative is to use
		/// the  Add() method.
		/// 
		/// The text label (string) for the curve that will be
		/// used as a  entry.
		/// An array of double precision X values (the
		/// independent values) that define the curve.
		/// An array of double precision Y values (the
		/// dependent values) that define the curve.
		/// The color to used for the curve line,
		/// symbols, etc.
		/// A  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
		///  method.
		public LineItem AddCurve( string label, double[] x, double[] y, Color color )
		{
			LineItem curve = new LineItem( label, x, y, color, SymbolType.Default );
			_curveList.Add( curve );
			return curve;
		}
		/// 
		/// Add a curve ( object) to the plot with
		/// the given data points () and properties.
		/// This is simplified way to add curves without knowledge of the
		///  class.  An alternative is to use
		/// the  Add() method.
		/// 
		/// The text label (string) for the curve that will be
		/// used as a  entry.
		/// A  of double precision value pairs that define
		/// the X and Y values for this curve
		/// The color to used for the curve line,
		/// symbols, etc.
		/// A  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
		///  method.
		public LineItem AddCurve( string label, IPointList points, Color color )
		{
			LineItem curve = new LineItem( label, points, color, SymbolType.Default );
			_curveList.Add( curve );
			return curve;
		}
		/// 
		/// Add a curve ( object) to the plot with
		/// the given data points (double arrays) and properties.
		/// This is simplified way to add curves without knowledge of the
		///  class.  An alternative is to use
		/// the  Add() method.
		/// 
		/// The text label (string) for the curve that will be
		/// used as a  entry.
		/// An array of double precision X values (the
		/// independent values) that define the curve.
		/// An array of double precision Y values (the
		/// dependent values) that define the curve.
		/// The color to used for the curve line,
		/// symbols, etc.
		/// A symbol type ()
		/// that will be used for this curve.
		/// A  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
		///  method.
		public LineItem AddCurve( string label, double[] x, double[] y,
			Color color, SymbolType symbolType )
		{
			LineItem curve = new LineItem( label, x, y, color, symbolType );
			_curveList.Add( curve );
			return curve;
		}
		/// 
		/// Add a curve ( object) to the plot with
		/// the given data points () and properties.
		/// This is simplified way to add curves without knowledge of the
		///  class.  An alternative is to use
		/// the  Add() method.
		/// 
		/// The text label (string) for the curve that will be
		/// used as a  entry.
		/// A  of double precision value pairs that define
		/// the X and Y values for this curve
		/// The color to used for the curve line,
		/// symbols, etc.
		/// A symbol type ()
		/// that will be used for this curve.
		/// A  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
		///  method.
		public LineItem AddCurve( string label, IPointList points,
			Color color, SymbolType symbolType )
		{
			LineItem curve = new LineItem( label, points, color, symbolType );
			_curveList.Add( curve );
			return curve;
		}
		/// 
		/// Add a stick graph ( object) to the plot with
		/// the given data points (double arrays) and properties.
		/// This is simplified way to add curves without knowledge of the
		///  class.  An alternative is to use
		/// the  Add() method.
		/// 
		/// The text label (string) for the curve that will be
		/// used as a  entry.
		/// An array of double precision X values (the
		/// independent values) that define the curve.
		/// An array of double precision Y values (the
		/// dependent values) that define the curve.
		/// The color to used for the curve line,
		/// symbols, etc.
		/// A  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
		///  method.
		public StickItem AddStick( string label, double[] x, double[] y, Color color )
		{
			StickItem curve = new StickItem( label, x, y, color );
			_curveList.Add( curve );
			return curve;
		}
		/// 
		/// Add a stick graph ( object) to the plot with
		/// the given data points () and properties.
		/// This is simplified way to add curves without knowledge of the
		///  class.  An alternative is to use
		/// the  Add() method.
		/// 
		/// The text label (string) for the curve that will be
		/// used as a  entry.
		/// A  of double precision value pairs that define
		/// the X and Y values for this curve
		/// The color to used for the curve line,
		/// symbols, etc.
		/// A  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
		///  method.
		public StickItem AddStick( string label, IPointList points, Color color )
		{
			StickItem curve = new StickItem( label, points, color );
			_curveList.Add( curve );
			return curve;
		}
		/// 
		/// Add a candlestick graph ( object) to the plot with
		/// the given data points () and properties.
		/// 
		/// 
		/// This is simplified way to add curves without knowledge of the
		///  class.  An alternative is to use
		/// the  Add() method.
		/// Note that the 
		/// should contain  objects instead of 
		/// objects in order to contain all the data values required for this curve type.
		/// 
		/// The text label (string) for the curve that will be
		/// used as a  entry.
		/// A  of double precision value pairs that define
		/// the X and Y values for this curve
		/// The color to used for the curve line,
		/// symbols, etc.
		/// A  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
		///  method.
		public OHLCBarItem AddOHLCBar( string label, IPointList points, Color color )
		{
			OHLCBarItem curve = new OHLCBarItem( label, points, color );
			_curveList.Add( curve );
			return curve;
		}
		/// 
		/// Add a japanesecandlestick graph ( object) to the plot with
		/// the given data points () and properties.
		/// 
		/// 
		/// This is simplified way to add curves without knowledge of the
		///  class.  An alternative is to use
		/// the  Add() method.
		/// Note that the 
		/// should contain  objects instead of 
		/// objects in order to contain all the data values required for this curve type.
		/// 
		/// The text label (string) for the curve that will be
		/// used as a  entry.
		/// A  of double precision value pairs that define
		/// the X and Y values for this curve
		/// A  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
		///  method.
		public JapaneseCandleStickItem AddJapaneseCandleStick( string label, IPointList points )
		{
			JapaneseCandleStickItem curve = new JapaneseCandleStickItem( label, points );
			_curveList.Add( curve );
			return curve;
		}
		/// 
		/// Add an error bar set ( object) to the plot with
		/// the given data points () and properties.
		/// This is simplified way to add curves without knowledge of the
		///  class.  An alternative is to use
		/// the  Add() method.
		/// 
		/// The text label (string) for the curve that will be
		/// used as a  entry.
		/// An array of double precision X values (the
		/// independent values) that define the curve.
		/// An array of double precision Y values (the
		/// dependent values) that define the curve.
		/// An array of double precision values that define the
		/// base value (the bottom) of the bars for this curve.
		/// 
		/// The color to used for the curve line,
		/// symbols, etc.
		/// An  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
		///  method.
		public ErrorBarItem AddErrorBar( string label, double[] x, double[] y,
			double[] baseValue, Color color )
		{
			ErrorBarItem curve = new ErrorBarItem( label, new PointPairList( x, y, baseValue ),
				color );
			_curveList.Add( curve );
			return curve;
		}
		/// 
		/// Add an error bar set ( object) to the plot with
		/// the given data points () and properties.
		/// This is simplified way to add curves without knowledge of the
		///  class.  An alternative is to use
		/// the  Add() method.
		/// 
		/// The text label (string) for the curve that will be
		/// used as a  entry.
		/// A  of double precision value pairs that define
		/// the X and Y values for this curve
		/// The color to used for the curve line,
		/// symbols, etc.
		/// An  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
		///  method.
		public ErrorBarItem AddErrorBar( string label, IPointList points, Color color )
		{
			ErrorBarItem curve = new ErrorBarItem( label, points, color );
			_curveList.Add( curve );
			return curve;
		}
		/// 
		/// Add a bar type curve ( object) to the plot with
		/// the given data points () and properties.
		/// This is simplified way to add curves without knowledge of the
		///  class.  An alternative is to use
		/// the  Add() method.
		/// 
		/// The text label (string) for the curve that will be
		/// used as a  entry.
		/// A  of double precision value pairs that define
		/// the X and Y values for this curve
		/// The color to used to fill the bars
		/// A  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
		///  method.
		public BarItem AddBar( string label, IPointList points, Color color )
		{
			BarItem curve = new BarItem( label, points, color );
			_curveList.Add( curve );
			return curve;
		}
		/// 
		/// Add a bar type curve ( object) to the plot with
		/// the given data points (double arrays) and properties.
		/// This is simplified way to add curves without knowledge of the
		///  class.  An alternative is to use
		/// the  Add() method.
		/// 
		/// The text label (string) for the curve that will be
		/// used as a  entry.
		/// An array of double precision X values (the
		/// independent values) that define the curve.
		/// An array of double precision Y values (the
		/// dependent values) that define the curve.
		/// The color to used for the bars
		/// A  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
		///  method.
		public BarItem AddBar( string label, double[] x, double[] y, Color color )
		{
			BarItem curve = new BarItem( label, x, y, color );
			_curveList.Add( curve );
			return curve;
		}
		/// 
		/// Add a "High-Low" bar type curve ( object) to the plot with
		/// the given data points (double arrays) and properties.
		/// This is simplified way to add curves without knowledge of the
		///  class.  An alternative is to use
		/// the  Add() method.
		/// 
		/// The text label (string) for the curve that will be
		/// used as a  entry.
		/// An array of double precision X values (the
		/// independent values) that define the curve.
		/// An array of double precision Y values (the
		/// dependent values) that define the curve.
		/// An array of double precision values that define the
		/// base value (the bottom) of the bars for this curve.
		/// 
		/// The color to used for the bars
		/// A  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
		///  method.
		public HiLowBarItem AddHiLowBar( string label, double[] x, double[] y,
			double[] baseVal, Color color )
		{
			HiLowBarItem curve = new HiLowBarItem( label, x, y, baseVal, color );
			_curveList.Add( curve );
			return curve;
		}
		/// 
		/// Add a hi-low bar type curve ( object) to the plot with
		/// the given data points () and properties.
		/// This is simplified way to add curves without knowledge of the
		///  class.  An alternative is to use
		/// the  Add() method.
		/// 
		/// The text label (string) for the curve that will be
		/// used as a  entry.
		/// A  of double precision value Trio's that define
		/// the X, Y, and lower dependent values for this curve
		/// The color to used to fill the bars
		/// A  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
		///  method.
		public HiLowBarItem AddHiLowBar( string label, IPointList points, Color color )
		{
			HiLowBarItem curve = new HiLowBarItem( label, points, color );
			_curveList.Add( curve );
			return curve;
		}
		/// 
		/// Add a  to the display.
		/// 
		/// The value associated with this item.
		/// The display color for this item.
		/// The amount this item will be 
		/// displaced from the center of the .
		/// Text label for this 
		/// a reference to the  constructed
		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;
		}
		/// 
		/// Add a  to the display, providing a gradient fill for the pie color.
		/// 
		/// The value associated with this  instance.
		/// The starting display color for the gradient  for this
		///  instance.
		/// The ending display color for the gradient  for this
		///  instance.
		/// The angle for the gradient .
		/// The amount this   instance will be 
		/// displaced from the center point.
		/// Text label for this  instance.
		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;
		}
		/// 
		///Creates all the s for a single Pie Chart. 
		/// 
		/// double array containing all s
		/// for a single PieChart.
		/// 
		///  string array containing all s
		/// for a single PieChart.
		/// 
		/// an array containing references to all s comprising
		/// the Pie Chart.
		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
		/// 
		/// Transform a data point from the specified coordinate type
		/// () to screen coordinates (pixels).
		/// 
		/// This method implicitly assumes that 
		/// has already been calculated via  or
		///  methods, or the  is
		/// set manually (see ).
		/// The X,Y pair that defines the point in user
		/// coordinates.
		/// A  type that defines the
		/// coordinate system in which the X,Y pair is defined.
		/// A point in screen coordinates that corresponds to the
		/// specified user point.
		public PointF GeneralTransform( PointF ptF, CoordType coord )
		{
			// Setup the scaling data based on the chart rect
			_xAxis.Scale.SetupScaleData( this, _xAxis );
			foreach ( Axis axis in _yAxisList )
				axis.Scale.SetupScaleData( this, axis );
			foreach ( Axis axis in _y2AxisList )
				axis.Scale.SetupScaleData( this, axis );
			return this.TransformCoord( ptF.X, ptF.Y, coord );
		}
		/// 
		/// Transform a data point from the specified coordinate type
		/// () to screen coordinates (pixels).
		/// 
		/// This method implicitly assumes that 
		/// has already been calculated via  or
		///  methods, or the  is
		/// set manually (see ).
		/// Note that this method is more accurate than the 
		/// overload, since it uses double types.  This would typically only be significant for
		///  coordinates.
		/// 
		/// The x coordinate that defines the location in user space
		/// The y coordinate that defines the location in user space
		/// A  type that defines the
		/// coordinate system in which the X,Y pair is defined.
		/// A point in screen coordinates that corresponds to the
		/// specified user point.
		public PointF GeneralTransform( double x, double y, CoordType coord )
		{
			// Setup the scaling data based on the chart rect
			_xAxis.Scale.SetupScaleData( this, _xAxis );
			foreach ( Axis axis in _yAxisList )
				axis.Scale.SetupScaleData( this, axis );
			foreach ( Axis axis in _y2AxisList )
				axis.Scale.SetupScaleData( this, axis );
			return this.TransformCoord( x, y, coord );
		}
		/// 
		/// Return the user scale values that correspond to the specified screen
		/// coordinate position (pixels).  This overload assumes the default
		///  and .
		/// 
		/// This method implicitly assumes that 
		/// has already been calculated via  or
		///  methods, or the  is
		/// set manually (see ).
		/// The X,Y pair that defines the screen coordinate
		/// point of interest
		/// The resultant value in user coordinates from the
		/// 
		/// The resultant value in user coordinates from the
		/// primary 
		public void ReverseTransform( PointF ptF, out double x, out double y )
		{
			// Setup the scaling data based on the chart rect
			_xAxis.Scale.SetupScaleData( this, _xAxis );
			this.YAxis.Scale.SetupScaleData( this, this.YAxis );
			x = this.XAxis.Scale.ReverseTransform( ptF.X );
			y = this.YAxis.Scale.ReverseTransform( ptF.Y );
		}
		/// 
		/// Return the user scale values that correspond to the specified screen
		/// coordinate position (pixels).
		/// 
		/// This method implicitly assumes that 
		/// has already been calculated via  or
		///  methods, or the  is
		/// set manually (see ).
		/// The X,Y pair that defines the screen coordinate
		/// point of interest
		/// The resultant value in user coordinates from the
		/// 
		/// The resultant value in user coordinates from the
		/// 
		/// The resultant value in user coordinates from the
		/// primary 
		/// The resultant value in user coordinates from the
		/// primary 
		public void ReverseTransform( PointF ptF, out double x, out double x2, out double y,
			out double y2 )
		{
			// Setup the scaling data based on the chart rect
			_xAxis.Scale.SetupScaleData( this, _xAxis );
			_x2Axis.Scale.SetupScaleData( this, _x2Axis );
			this.YAxis.Scale.SetupScaleData( this, this.YAxis );
			this.Y2Axis.Scale.SetupScaleData( this, this.Y2Axis );
			x = this.XAxis.Scale.ReverseTransform( ptF.X );
			x2 = this.X2Axis.Scale.ReverseTransform( ptF.X );
			y = this.YAxis.Scale.ReverseTransform( ptF.Y );
			y2 = this.Y2Axis.Scale.ReverseTransform( ptF.Y );
		}
		/// 
		/// Return the user scale values that correspond to the specified screen
		/// coordinate position (pixels).
		/// 
		/// This method implicitly assumes that 
		/// has already been calculated via  or
		///  methods, or the  is
		/// set manually (see ).
		/// The X,Y pair that defines the screen coordinate
		/// point of interest
		/// true to return data that corresponds to an
		/// , false for an .
		/// true to return data that corresponds to a
		/// , false for a .
		/// The ordinal index of the Y or Y2 axis from which
		/// to return data (see , )
		/// 
		/// The resultant value in user coordinates from the
		/// 
		/// The resultant value in user coordinates from the
		/// primary 
		public void ReverseTransform( PointF ptF, bool isX2Axis, bool isY2Axis, int yAxisIndex,
					out double x, out double y )
		{
			// Setup the scaling data based on the chart rect
			Axis xAxis = _xAxis;
			if ( isX2Axis )
				xAxis = _x2Axis;
			xAxis.Scale.SetupScaleData( this, xAxis );
			x = xAxis.Scale.ReverseTransform( ptF.X );
			Axis yAxis = null;
			if ( isY2Axis && Y2AxisList.Count > yAxisIndex )
				yAxis = Y2AxisList[yAxisIndex];
			else if ( !isY2Axis && YAxisList.Count > yAxisIndex )
				yAxis = YAxisList[yAxisIndex];
			if ( yAxis != null )
			{
				yAxis.Scale.SetupScaleData( this, yAxis );
				y = yAxis.Scale.ReverseTransform( ptF.Y );
			}
			else
				y = PointPair.Missing;
		}
		/// 
		/// Return the user scale values that correspond to the specified screen
		/// coordinate position (pixels) for all y axes.
		/// 
		/// This method implicitly assumes that 
		/// has already been calculated via  or
		///  methods, or the  is
		/// set manually (see ).
		/// The X,Y pair that defines the screen coordinate
		/// point of interest
		/// The resultant value in user coordinates from the
		/// 
		/// The resultant value in user coordinates from the
		/// 
		/// An array of resultant values in user coordinates from the
		/// list of  instances.  This method allocates the
		/// array for you, according to the number of  objects
		/// in the list.
		/// An array of resultant values in user coordinates from the
		/// list of  instances.  This method allocates the
		/// array for you, according to the number of  objects
		/// in the list.
		public void ReverseTransform( PointF ptF, out double x, out double x2, out double[] y,
			out double[] y2 )
		{
			// Setup the scaling data based on the chart rect
			_xAxis.Scale.SetupScaleData( this, _xAxis );
			x = this.XAxis.Scale.ReverseTransform( ptF.X );
			_x2Axis.Scale.SetupScaleData( this, _x2Axis );
			x2 = this.X2Axis.Scale.ReverseTransform( ptF.X );
			y = new double[_yAxisList.Count];
			y2 = new double[_y2AxisList.Count];
			for ( int i = 0; i < _yAxisList.Count; i++ )
			{
				Axis axis = _yAxisList[i];
				axis.Scale.SetupScaleData( this, axis );
				y[i] = axis.Scale.ReverseTransform( ptF.Y );
			}
			for ( int i = 0; i < _y2AxisList.Count; i++ )
			{
				Axis axis = _y2AxisList[i];
				axis.Scale.SetupScaleData( this, axis );
				y2[i] = axis.Scale.ReverseTransform( ptF.Y );
			}
		}
		/// 
		/// Add a secondary  (left side) to the list of axes
		/// in the Graph.
		/// 
		/// 
		/// Note that the primary  is always included by default.
		/// This method turns off the  and 
		///  and 
		/// properties by default.
		/// 
		/// The title for the .
		/// the ordinal position (index) in the .
		public int AddYAxis( string title )
		{
			YAxis axis = new YAxis( title );
			axis.MajorTic.IsOpposite = false;
			axis.MinorTic.IsOpposite = false;
			axis.MajorTic.IsInside = false;
			axis.MinorTic.IsInside = false;
			_yAxisList.Add( axis );
			return _yAxisList.Count - 1;
		}
		/// 
		/// Add a secondary  (right side) to the list of axes
		/// in the Graph.
		/// 
		/// 
		/// Note that the primary  is always included by default.
		/// This method turns off the  and 
		///  and 
		/// properties by default.
		/// 
		/// The title for the .
		/// the ordinal position (index) in the .
		public int AddY2Axis( string title )
		{
			Y2Axis axis = new Y2Axis( title );
			axis.MajorTic.IsOpposite = false;
			axis.MinorTic.IsOpposite = false;
			axis.MajorTic.IsInside = false;
			axis.MinorTic.IsInside = false;
			_y2AxisList.Add( axis );
			return _y2AxisList.Count - 1;
		}
		/// 
		/// Find the object that lies closest to the specified mouse (screen) point.
		/// 
		/// 
		/// This method will search through all of the graph objects, such as
		/// , , ,
		/// , and .
		/// If the mouse point is within the bounding box of the items (or in the case
		/// of  and , within
		///  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
		///  parameter returns the index number of the item
		/// within the selected object (such as the point number within a
		///  object.
		/// 
		/// The screen point, in pixel coordinates.
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// A reference to the nearest object to the
		/// specified screen point.  This can be any of ,
		/// , ,
		/// , , or .
		/// Note: If the pane title is selected, then the  object
		/// will be returned.
		/// 
		/// The index number of the item within the selected object
		/// (where applicable).  For example, for a  object,
		///  will be the index number of the nearest data point,
		/// accessible via CurveItem.Points[index].
		/// index will be -1 if no data points are available.
		/// true if an object was found, false otherwise.
		/// 
		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 ( AxisRangesValid() )
			{
				float scaleFactor = CalcScaleFactor();
				//int			hStack;
				//float		legendWidth, legendHeight;
				RectangleF tmpRect;
				GraphObj saveGraphItem = null;
				int saveIndex = -1;
				ZOrder saveZOrder = ZOrder.H_BehindAll;
				// Calculate the chart rect, deducting the area for the scales, titles, legend, etc.
				RectangleF tmpChartRect = CalcChartRect( g, scaleFactor );
				// See if the point is in a GraphObj
				// If so, just save the object and index so we can see if other overlying objects were
				// intersected as well.
				if ( this.GraphObjList.FindPoint( mousePt, this, g, scaleFactor, out index ) )
				{
					saveGraphItem = this.GraphObjList[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.H_BehindAll && _title._isVisible )
				{
					SizeF size = _title._fontSpec.BoundingBox( g, _title._text, scaleFactor );
					tmpRect = new RectangleF( ( _rect.Left + _rect.Right - size.Width ) / 2,
						_rect.Top + _margin.Top * scaleFactor,
						size.Width, size.Height );
					if ( tmpRect.Contains( mousePt ) )
					{
						nearestObj = this;
						return true;
					}
				}
				float left = tmpChartRect.Left;
				// See if the point is in one of the Y Axes
				for ( int yIndex = 0; yIndex < _yAxisList.Count; yIndex++ )
				{
					Axis yAxis = _yAxisList[yIndex];
					float width = yAxis._tmpSpace;
					if ( width > 0 )
					{
						tmpRect = new RectangleF( left - width, tmpChartRect.Top,
							width, tmpChartRect.Height );
						if ( saveZOrder <= ZOrder.D_BehindAxis && tmpRect.Contains( mousePt ) )
						{
							nearestObj = yAxis;
							index = yIndex;
							return true;
						}
						left -= width;
					}
				}
				left = tmpChartRect.Right;
				// See if the point is in one of the Y2 Axes
				for ( int yIndex = 0; yIndex < _y2AxisList.Count; yIndex++ )
				{
					Axis y2Axis = _y2AxisList[yIndex];
					float width = y2Axis._tmpSpace;
					if ( width > 0 )
					{
						tmpRect = new RectangleF( left, tmpChartRect.Top,
							width, tmpChartRect.Height );
						if ( saveZOrder <= ZOrder.D_BehindAxis && tmpRect.Contains( mousePt ) )
						{
							nearestObj = y2Axis;
							index = yIndex;
							return true;
						}
						left += width;
					}
				}
				// See if the point is in the X Axis
				tmpRect = new RectangleF( tmpChartRect.Left, tmpChartRect.Bottom,
					tmpChartRect.Width, _rect.Bottom - tmpChartRect.Bottom );
				if ( saveZOrder <= ZOrder.D_BehindAxis && tmpRect.Contains( mousePt ) )
				{
					nearestObj = this.XAxis;
					return true;
				}
				CurveItem curve;
				// See if it's a data point
				if ( saveZOrder <= ZOrder.E_BehindCurves && FindNearestPoint( mousePt, out curve, out index ) )
				{
					nearestObj = curve;
					return true;
				}
				if ( saveGraphItem != null )
				{
					index = saveIndex;
					nearestObj = saveGraphItem;
					return true;
				}
			}
			return false;
		}
		/// 
		/// Find the data point that lies closest to the specified mouse (screen)
		/// point for the specified curve.
		/// 
		/// 
		/// 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
		///  pixels of the screen point.
		/// 
		/// The screen point, in pixel coordinates.
		/// A reference to the 
		/// instance that contains the closest point.  nearestCurve will be null if
		/// no data points are available.
		/// A  object containing
		/// the data points to be searched.
		/// The index number of the closest point.  The
		/// actual data vpoint will then be CurveItem.Points[iNearest]
		/// .  iNearest will
		/// be -1 if no data points are available.
		/// true if a point was found and that point lies within
		///  pixels
		/// of the screen point, false otherwise.
		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 );
		}
		/// 
		/// Find the data point that lies closest to the specified mouse (screen)
		/// point.
		/// 
		/// 
		/// This method will search through all curves in
		///  to find which point is
		/// nearest.  It will only consider points that are within
		///  pixels of the screen point.
		/// 
		/// The screen point, in pixel coordinates.
		/// A reference to the 
		/// instance that contains the closest point.  nearestCurve will be null if
		/// no data points are available.
		/// The index number of the closest point.  The
		/// actual data vpoint will then be CurveItem.Points[iNearest]
		/// .  iNearest will
		/// be -1 if no data points are available.
		/// true if a point was found and that point lies within
		///  pixels
		/// of the screen point, false otherwise.
		public bool FindNearestPoint( PointF mousePt,
			out CurveItem nearestCurve, out int iNearest )
		{
			return FindNearestPoint( mousePt, _curveList,
				out nearestCurve, out iNearest );
		}
		/// 
		/// Find the data point that lies closest to the specified mouse (screen)
		/// point.
		/// 
		/// 
		/// This method will search through the specified list of curves to find which point is
		/// nearest.  It will only consider points that are within
		///  pixels of the screen point, and it will
		/// only consider 's that are in 
		/// .
		/// 
		/// The screen point, in pixel coordinates.
		/// A  object containing
		/// a subset of 's to be searched.
		/// A reference to the 
		/// instance that contains the closest point.  nearestCurve will be null if
		/// no data points are available.
		/// The index number of the closest point.  The
		/// actual data vpoint will then be CurveItem.Points[iNearest]
		/// .  iNearest will
		/// be -1 if no data points are available.
		/// true if a point was found and that point lies within
		///  pixels
		/// of the screen point, false otherwise.
		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 ChartRect, always return false
			if ( !_chart._rect.Contains( mousePt ) )
				return false;
			double x, x2;
			double[] y;
			double[] y2;
			//ReverseTransform( mousePt, out x, out y, out y2 );
			ReverseTransform( mousePt, out x, out x2, out y, out y2 );
			if ( !AxisRangesValid() )
				return false;
			ValueHandler valueHandler = new ValueHandler( this, false );
			double xPixPerUnit = _chart._rect.Width / ( _xAxis._scale._max - _xAxis._scale._min );
			//double	yPixPerUnit = chartRect.Height / ( yAxis.Max - yAxis.Min );
			//double	y2PixPerUnit; // = chartRect.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 != null &&
							( (PieItem)curve ).SlicePath.IsVisible( mousePt ) )
					{
						nearestBar = curve;
						iNearestBar = 0;
					}
					continue;
				}
				else if ( curve.IsVisible )
				{
					int yIndex = curve.GetYAxisIndex( this );
					Axis yAxis = curve.GetYAxis( this );
					if ( curve.IsY2Axis )
					{
						yAct = y2[yIndex];
						yMinAct = _y2AxisList[yIndex]._scale._min;
						yMaxAct = _y2AxisList[yIndex]._scale._max;
					}
					else
					{
						yAct = y[yIndex];
						yMinAct = _yAxisList[yIndex]._scale._min;
						yMaxAct = _yAxisList[yIndex]._scale._max;
					}
					yPixPerUnitAct = _chart._rect.Height / ( yMaxAct - yMinAct );
					IPointList points = curve.Points;
					float barWidth = curve.GetBarWidth( this );
					double barWidthUserHalf;
					Axis baseAxis = curve.BaseAxis( this );
					bool isXBaseAxis = ( baseAxis is XAxis || baseAxis is X2Axis );
					if ( isXBaseAxis )
						barWidthUserHalf = barWidth / xPixPerUnit / 2.0;
					else
						barWidthUserHalf = barWidth / yPixPerUnitAct / 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._scale.IsAnyOrdinal && !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._scale.IsAnyOrdinal && !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 || curve is OHLCBarItem ||
									curve is JapaneseCandleStickItem )
								{
									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._scale._min && xVal <= _xAxis._scale._max &&
											yVal >= yMinAct && yVal <= yMaxAct )
								{
									if ( curve is LineItem && _lineType == LineType.Stack )
									{
										double zVal;
										valueHandler.GetValues( curve, iPt, out xVal, out zVal, out yVal );
									}
									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;
		}
		/// 
		/// Search through the  and  for
		/// items that contain active  objects.
		/// 
		/// The mouse location where the click occurred
		/// An appropriate  instance
		/// The current scaling factor for drawing operations.
		/// The clickable object that was found.  Typically a type of
		///  or a type of .
		/// The  instance that is contained within
		/// the  object.
		/// An index value, indicating which point was clicked for
		///  type objects.
		/// returns true if a clickable link was found under the
		/// , or false otherwise.
		/// 
		public bool FindLinkableObject( PointF mousePt, Graphics g, float scaleFactor,
				out object source, out Link link, out int index )
		{
			index = -1;
			// First look for graph objects that lie in front of the data points
			foreach ( GraphObj graphObj in _graphObjList )
			{
				link = graphObj._link;
				bool inFront = graphObj.IsInFrontOfData;
				if ( link.IsActive )
				{
					if ( graphObj.PointInBox( mousePt, this, g, scaleFactor ) )
					{
						source = graphObj;
						return true;
					}
				}
			}
			// Second, look at the curve data points
			foreach ( CurveItem curve in _curveList )
			{
				link = curve._link;
				if ( link.IsActive )
				{
					CurveItem nearestCurve;
					if ( FindNearestPoint( mousePt, curve, out nearestCurve, out index ) )
					{
						source = curve;
						return true;
					}
				}
			}
			// Third, look for graph objects that lie behind the data points
			foreach ( GraphObj graphObj in _graphObjList )
			{
				link = graphObj._link;
				bool inFront = graphObj.IsInFrontOfData;
				if ( link.IsActive )
				{
					if ( graphObj.PointInBox( mousePt, this, g, scaleFactor ) )
					{
						source = graphObj;
						return true;
					}
				}
			}
			source = null;
			link = null;
			index = -1;
			return false;
		}
		// Revision: JCarpenter 10/06
		/// 
		/// Find any objects that exist within the specified (screen) rectangle.
		/// This method will search through all of the graph objects, such as
		/// , , ,
		/// , and .
		/// and see if the objects' bounding boxes are within the specified (screen) rectangle
		/// This method returns true if any are found.
		/// 
		public bool FindContainedObjects( RectangleF rectF, Graphics g,
			 out CurveList containedObjs )
		{
			containedObjs = new CurveList();
			foreach ( CurveItem ci in this.CurveList )
			{
				for ( int i = 0; i < ci.Points.Count; i++ )
				{
					if ( ci.Points[i].X > rectF.Left &&
						 ci.Points[i].X < rectF.Right &&
						 ci.Points[i].Y > rectF.Bottom &&
						 ci.Points[i].Y < rectF.Top )
					{
						containedObjs.Add( ci );
					}
				}
			}
			return ( containedObjs.Count > 0 );
		}
	#endregion
	}
}