//============================================================================
//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.Runtime.Serialization;
using System.Security.Permissions;
namespace DrawGraph
{
	/// 
	/// The Axis class is an abstract base class that encompasses all properties
	/// and methods required to define a graph Axis.
	/// 
	/// This class is inherited by the
	/// , , and  classes
	/// to define specific characteristics for those types.
	/// 
	/// 
	///  John Champion modified by Jerry Vos 
	///  $Revision: 3.73 $ $Date: 2007/05/19 06:06:05 $ 
	[Serializable]
	abstract public class Axis : ISerializable, ICloneable
	{
	#region Class Fields
		/// 
		/// private field that stores the  class, which implements all the
		/// calculations and methods associated with the numeric scale for this
		/// .  See the public property  to access this class.
		/// 
		internal Scale _scale;
		/// 
		/// Private field that stores the  class, which handles all
		/// the minor tic information.  See the public property  to access this class.
		/// 
		internal MinorTic _minorTic;
		/// 
		/// Private field that stores the  class, which handles all
		/// the major tic information.  See the public property  to access this class.
		/// 
		internal MajorTic _majorTic;
		/// 
		/// Private field that stores the  class, which handles all
		/// the major grid information.  See the public property  to access this class.
		/// 
		internal MajorGrid	_majorGrid;
		/// 
		/// Private field that stores the  class, which handles all
		/// the minor grid information.  See the public property  to access this class.
		/// 
		internal MinorGrid _minorGrid;
		///  Private fields for the  scale rendering properties.
		/// Use the public properties  and 
		/// for access to these values.
		/// 
		internal double _cross;
		///  Private field for the  automatic cross position mode.
		/// Use the public property  for access to this value.
		/// 
		internal bool _crossAuto;
		///  Private fields for the  attributes.
		/// Use the public properties , 
		/// for access to these values.
		/// 
		protected bool _isVisible,
							_isAxisSegmentVisible;
		///  Private field for the  title string.
		/// Use the public property  for access to this value.
		/// 
		protected AxisLabel _title;
		/// 
		/// A tag object for use by the user.  This can be used to store additional
		/// information associated with the .  ZedGraph does
		/// not use this value for any purpose.
		/// 
		/// 
		/// Note that, if you are going to Serialize ZedGraph data, then any type
		/// that you store in  must be a serializable type (or
		/// it will cause an exception).
		/// 
		public object Tag;
		///  Private field for the  drawing dimensions.
		/// Use the public property 
		/// for access to these values. 
		private float	_axisGap;
		/// 
		/// Private field for the  minimum allowable space allocation.
		/// Use the public property  to access this value.
		/// 
		/// 
		private float _minSpace;
		///  Private fields for the  colors.
		/// Use the public property  for access to this values.
		/// 
		private Color _color;
		/// 
		/// Temporary values for axis space calculations (see ).
		/// 
		internal float _tmpSpace;
	#endregion
	#region Events
		/// 
		/// A delegate that allows full custom formatting of the Axis labels
		/// 
		/// The  for which the label is to be
		/// formatted
		/// The  of interest.
		/// The value to be formatted
		/// The zero-based index of the label to be formatted
		/// 
		/// A string value representing the label, or null if the ZedGraph should go ahead
		/// and generate the label according to the current settings
		/// 
		public delegate string ScaleFormatHandler( GraphPane pane, Axis axis, double val, int index );
		/// 
		/// Subscribe to this event to handle custom formatting of the scale labels.
		/// 
		public event ScaleFormatHandler ScaleFormatEvent;
		// Revision: JCarpenter 10/06
		/// 
		/// Allow customization of title based on user preferences.
		/// 
		/// The  of interest.
		/// 
		/// A string value representing the label, or null if the ZedGraph should go ahead
		/// and generate the label according to the current settings.  To make the title
		/// blank, return "".
		/// 
		public delegate string ScaleTitleEventHandler( Axis axis );
		//Revision: JCarpenter 10/06
		/// 
		/// Allow customization of the title when the scale is very large
		/// Subscribe to this event to handle custom formatting of the scale axis label.
		/// 
		public event ScaleTitleEventHandler ScaleTitleEvent;
	#endregion
	#region Defaults
		/// 
		/// A simple struct that defines the
		/// default property values for the  class.
		/// 
		public struct Default
		{
			/// 
			/// The default size for the gap between multiple axes
			/// ( property). Units are in points (1/72 inch).
			/// 
			public static float AxisGap = 5;
			/// 
			/// The default setting for the gap between the scale labels and the axis title.
			/// 
			public static float TitleGap = 0.0f;
			/// 
			/// The default font family for the   text
			/// font specification 
			/// ( property).
			/// 
			public static string TitleFontFamily = "Arial";
			/// 
			/// The default font size for the   text
			/// font specification 
			/// ( property).  Units are
			/// in points (1/72 inch).
			/// 
			public static float TitleFontSize = 14;
			/// 
			/// The default font color for the   text
			/// font specification 
			/// ( property).
			/// 
			public static Color TitleFontColor = Color.Black;
			/// 
			/// The default font bold mode for the   text
			/// font specification 
			/// ( property). true
			/// for a bold typeface, false otherwise.
			/// 
			public static bool TitleFontBold = true;
			/// 
			/// The default font italic mode for the   text
			/// font specification 
			/// ( property). true
			/// for an italic typeface, false otherwise.
			/// 
			public static bool TitleFontItalic = false;
			/// 
			/// The default font underline mode for the   text
			/// font specification 
			/// ( property). true
			/// for an underlined typeface, false otherwise.
			/// 
			public static bool TitleFontUnderline = false;
			/// 
			/// The default color for filling in the  text background
			/// (see  property).
			/// 
			public static Color TitleFillColor = Color.White;
			/// 
			/// The default custom brush for filling in the  text background
			/// (see  property).
			/// 
			public static Brush TitleFillBrush = null;
			/// 
			/// The default fill mode for filling in the  text background
			/// (see  property).
			/// 
			public static FillType TitleFillType = FillType.None;
			/// 
			/// The default color for the  itself
			/// ( property).  This color only affects the
			/// the axis border.
			/// 
			public static Color BorderColor = Color.Black;
			/// 
			/// The default value for , which determines
			/// whether or not the scale segment itself is visible
			/// 
			public static bool IsAxisSegmentVisible = true;
			/// 
			/// The default setting for the  scale axis type
			/// ( property).  This value is set as per
			/// the  enumeration
			/// 
			public static AxisType Type = AxisType.Linear;
			/// 
			/// The default color for the axis segment.
			/// 
			public static Color Color = Color.Black;
			/// 
			/// The default setting for the axis space allocation.  This term, expressed in
			/// points (1/72 inch) and scaled according to  for the
			/// , determines the minimum amount of space an axis must
			/// have between the  and the
			/// .  This minimum space
			/// applies whether  is true or false.
			/// 
			public static float MinSpace = 0f;
		}
	#endregion
	#region Constructors
		/// 
		/// Default constructor for  that sets all axis properties
		/// to default values as defined in the  class.
		/// 
		public Axis()
		{
			_scale = new LinearScale( this );
			_cross = 0.0;
			_crossAuto = true;
			_majorTic = new MajorTic();
			_minorTic = new MinorTic();
			_majorGrid = new MajorGrid();
			_minorGrid = new MinorGrid();
			_axisGap = Default.AxisGap;
			_minSpace = Default.MinSpace;
			_isVisible = true;
			_isAxisSegmentVisible = Default.IsAxisSegmentVisible;
			_title = new AxisLabel( "", Default.TitleFontFamily, Default.TitleFontSize,
					Default.TitleFontColor, Default.TitleFontBold,
					Default.TitleFontUnderline, Default.TitleFontItalic );
			_title.FontSpec.Fill = new Fill( Default.TitleFillColor, Default.TitleFillBrush,
					Default.TitleFillType );
			_title.FontSpec.Border.IsVisible = false;
			_color = Default.Color;
		}
		/// 
		/// Constructor for  that sets all axis properties
		/// to default values as defined in the  class,
		/// except for the .
		/// 
		/// A string containing the axis title
		public Axis( string title )
			: this()
		{
			_title._text = title;
		}
		/// 
		/// The Copy Constructor.
		/// 
		/// The Axis object from which to copy
		public Axis( Axis rhs )
		{
			_scale = rhs._scale.Clone( this );
			_cross = rhs._cross;
			_crossAuto = rhs._crossAuto;
			_majorTic = rhs.MajorTic.Clone();
			_minorTic = rhs.MinorTic.Clone();
			_majorGrid = rhs._majorGrid.Clone();
			_minorGrid = rhs._minorGrid.Clone();
			_isVisible = rhs.IsVisible;
			_isAxisSegmentVisible = rhs._isAxisSegmentVisible;
			_title = (AxisLabel) rhs.Title.Clone();
			_axisGap = rhs._axisGap;
			_minSpace = rhs.MinSpace;
			_color = rhs.Color;
		}
		/// 
		/// Implement the  interface in a typesafe manner by just
		/// calling the typed version of Clone.
		/// 
		/// 
		/// Note that this method must be called with an explicit cast to ICloneable, and
		/// that it is inherently virtual.  For example:
		/// 
		/// ParentClass foo = new ChildClass();
		/// ChildClass bar = (ChildClass) ((ICloneable)foo).Clone();
		/// 
		/// Assume that ChildClass is inherited from ParentClass.  Even though foo is declared with
		/// ParentClass, it is actually an instance of ChildClass.  Calling the ICloneable implementation
		/// of Clone() on foo actually calls ChildClass.Clone() as if it were a virtual function.
		/// 
		/// A deep copy of this object
		object ICloneable.Clone()
		{
			throw new NotImplementedException( "Can't clone an abstract base type -- child types must implement ICloneable" );
			//return new PaneBase( this );
		}
	#endregion
	#region Serialization
		/// 
		/// Current schema value that defines the version of the serialized file
		/// 
		public const int schema = 10;
		/// 
		/// Constructor for deserializing objects
		/// 
		/// A  instance that defines the serialized data
		/// 
		/// A  instance that contains the serialized data
		/// 
		protected Axis( SerializationInfo info, StreamingContext 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( "schema" );
			_cross = info.GetDouble( "cross" );
			_crossAuto = info.GetBoolean( "crossAuto" );
			_majorTic = (MajorTic)info.GetValue( "MajorTic", typeof( MajorTic ) );
			_minorTic = (MinorTic)info.GetValue( "MinorTic", typeof( MinorTic ) );
			_majorGrid = (MajorGrid)info.GetValue( "majorGrid", typeof( MajorGrid ) );
			_minorGrid = (MinorGrid)info.GetValue( "minorGrid", typeof( MinorGrid ) );
			_isVisible = info.GetBoolean( "isVisible" );
			_title = (AxisLabel) info.GetValue( "title", typeof( AxisLabel ) );
			_minSpace = info.GetSingle( "minSpace" );
			_color = (Color)info.GetValue( "color", typeof( Color ) );
			_isAxisSegmentVisible = info.GetBoolean( "isAxisSegmentVisible" );
			_axisGap = info.GetSingle( "axisGap" );
			_scale = (Scale)info.GetValue( "scale", typeof( Scale ) );
			_scale._ownerAxis = this;
		}
		/// 
		/// 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 virtual void GetObjectData( SerializationInfo info, StreamingContext context )
		{
			info.AddValue( "schema", schema );
			info.AddValue( "cross", _cross );
			info.AddValue( "crossAuto", _crossAuto );
			info.AddValue( "MajorTic", MajorTic );
			info.AddValue( "MinorTic", MinorTic );
			info.AddValue( "majorGrid", _majorGrid );
			info.AddValue( "minorGrid", _minorGrid );
			info.AddValue( "isVisible", _isVisible );
			info.AddValue( "title", _title );
			info.AddValue( "minSpace", _minSpace );
			info.AddValue( "color", _color );
			info.AddValue( "isAxisSegmentVisible", _isAxisSegmentVisible );
			info.AddValue( "axisGap", _axisGap );
			info.AddValue( "scale", _scale );
		}
	#endregion
	#region Scale Properties
		/// 
		/// Gets the  instance associated with this .
		/// 
		public Scale Scale
		{
			get { return _scale; }
		}
		/// 
		/// Gets or sets the scale value at which this axis should cross the "other" axis.
		/// 
		/// This property allows the axis to be shifted away from its default location.
		/// For example, for a graph with an X range from -100 to +100, the Y Axis can be located
		/// at the X=0 value rather than the left edge of the ChartRect.  This value can be set
		/// automatically based on the state of .  If
		/// this value is set manually, then  will
		/// also be set to false.  The "other" axis is the axis the handles the second dimension
		/// for the graph.  For the XAxis, the "other" axis is the YAxis.  For the YAxis or
		/// Y2Axis, the "other" axis is the XAxis.
		/// 
		///  The value is defined in user scale units 
		/// 
		/// 
		/// 
		/// 
		public double Cross
		{
			get { return _cross; }
			set { _cross = value; _crossAuto = false; }
		}
		/// 
		/// Gets or sets a value that determines whether or not the  value
		/// is set automatically.
		/// 
		/// Set to true to have ZedGraph put the axis in the default location, or false
		/// to specify the axis location manually with a  value.
		/// 
		/// 
		/// 
		/// 
		public bool CrossAuto
		{
			get { return _crossAuto; }
			set { _crossAuto = value; }
		}
		/// 
		/// Gets or sets the minimum axis space allocation.
		/// 
		/// 
		/// This term, expressed in
		/// points (1/72 inch) and scaled according to 
		/// for the , determines the minimum amount of space
		/// an axis must have between the Chart.Rect and the
		/// GraphPane.Rect.  This minimum space
		/// applies whether  is true or false.
		/// 
		public float MinSpace
		{
			get { return _minSpace; }
			set { _minSpace = value; }
		}
	#endregion
	#region Tic Properties
		/// 
		/// The color to use for drawing this .
		/// 
		/// 
		/// This affects only the axis segment (see ),
		/// since the ,
		/// , , ,
		/// , and 
		/// all have their own color specification.
		/// 
		///  The color is defined using the
		///  class
		/// .
		/// 
		public Color Color
		{
			get { return _color; }
			set { _color = value; }
		}
		/// 
		/// Gets a reference to the  class instance
		/// for this .  This class stores all the major tic settings.
		/// 
		public MajorTic MajorTic
		{
			get { return _majorTic; }
		}
		/// 
		/// Gets a reference to the  class instance
		/// for this .  This class stores all the minor tic settings.
		/// 
		public MinorTic MinorTic
		{
			get { return _minorTic; }
		}
		#endregion
	#region Grid Properties
		/// 
		/// Gets a reference to the  class that contains the properties
		/// of the major grid.
		/// 
		public MajorGrid MajorGrid
		{
			get { return _majorGrid; }
		}
		/// 
		/// Gets a reference to the  class that contains the properties
		/// of the minor grid.
		/// 
		public MinorGrid MinorGrid
		{
			get { return _minorGrid; }
		}
	#endregion
	#region Type Properties
		/// 
		/// This property determines whether or not the  is shown.
		/// 
		/// 
		/// Note that even if
		/// the axis is not visible, it can still be actively used to draw curves on a
		/// graph, it will just be invisible to the user
		/// 
		/// true to show the axis, false to disable all drawing of this axis
		/// .
		/// .
		/// .
		/// .
		public bool IsVisible
		{
			get { return _isVisible; }
			set { _isVisible = value; }
		}
		/// 
		/// Gets or sets a property that determines whether or not the axis segment (the line that
		/// represents the axis itself) is drawn.
		/// 
		/// 
		/// Under normal circumstances, this value won't affect the appearance of the display because
		/// the Axis segment is overlain by the Axis border (see ).
		/// However, when the border is not visible, or when  is set to
		/// false, this value will make a difference.
		/// 
		public bool IsAxisSegmentVisible
		{
			get { return _isAxisSegmentVisible; }
			set { _isAxisSegmentVisible = value; }
		}
		/// 
		/// Gets or sets the  for this .
		/// 
		/// 
		/// The type can be either ,
		/// , ,
		/// or .
		/// 
		/// 
		/// 
		/// 
		/// 
		/// 
		public AxisType Type
		{
			get { return _scale.Type; }
			set { _scale = Scale.MakeNewScale( _scale, value ); }
		}
	#endregion
	#region Label Properties
		/// 
		/// Gets or sets the  class that contains the title of this
		/// .
		/// 
		/// The title normally shows the basis and dimensions of
		/// the scale range, such as "Time (Years)".  The title is only shown if the
		///  property is set to true.  If the Title text is empty,
		/// then no title is shown, and no space is "reserved" for the title on the graph.
		/// 
		/// the title is a string value
		/// 
		public AxisLabel Title
		{
			get { return _title; }
			set { _title = value; }
		}
		/// 
		/// The size of the gap between multiple axes (see  and
		/// ).
		/// 
		/// 
		/// This size will be scaled
		/// according to the  for the
		/// 
		/// 
		/// The axis gap is measured in points (1/72 inch)
		/// .
		public float AxisGap
		{
			get { return _axisGap; }
			set { _axisGap = value; }
		}
	#endregion
	#region Rendering Methods
		/// 
		/// Restore the scale ranging to automatic mode, and recalculate the
		///  scale ranges
		/// 
		/// 
		/// A reference to the  object that is the parent or
		/// owner of this object.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// 
		/// 
		/// 
		/// 
		/// 
		public void ResetAutoScale( GraphPane pane, Graphics g )
		{
			_scale._minAuto = true;
			_scale._maxAuto = true;
			_scale._majorStepAuto = true;
			_scale._minorStepAuto = true;
			_crossAuto = true;
			_scale._magAuto = true;
			//this.numDecAuto = true;
			_scale._formatAuto = true;
			pane.AxisChange( g );
		}
		/// 
		/// Do all rendering associated with this  to the specified
		///  device.
		/// 
		/// 
		/// This method is normally only
		/// called by the Draw method of the parent  object.
		/// 
		/// 
		/// 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  object that is the parent or
		/// owner of this object.
		/// 
		/// 
		/// The scaling factor to be used for rendering objects.  This is calculated and
		/// passed down by the parent  object using the
		///  method, and is used to proportionally adjust
		/// font sizes, etc. according to the actual size of the graph.
		/// 
		/// 
		/// The number of pixels to shift to account for non-primary axis position (e.g.,
		/// the second, third, fourth, etc.  or .
		/// 
		public void Draw( Graphics g, GraphPane pane, float scaleFactor, float shiftPos )
		{
			Matrix saveMatrix = g.Transform;
			_scale.SetupScaleData( pane, this );
			if ( _isVisible )
			{
				SetTransformMatrix( g, pane, scaleFactor );
				shiftPos = CalcTotalShift( pane, scaleFactor, shiftPos );
				_scale.Draw( g, pane, scaleFactor, shiftPos );
				//DrawTitle( g, pane, scaleFactor );
				g.Transform = saveMatrix;
			}
		}
		internal void DrawGrid( Graphics g, GraphPane pane, float scaleFactor, float shiftPos )
		{
			if ( _isVisible )
			{
				Matrix saveMatrix = g.Transform;
				SetTransformMatrix( g, pane, scaleFactor );
				double baseVal = _scale.CalcBaseTic();
				float topPix, rightPix;
				_scale.GetTopRightPix( pane, out topPix, out rightPix );
				shiftPos = CalcTotalShift( pane, scaleFactor, shiftPos );
				_scale.DrawGrid( g, pane, baseVal, topPix, scaleFactor );
				DrawMinorTics( g, pane, baseVal, shiftPos, scaleFactor, topPix );
				g.Transform = saveMatrix;
			}
		}
		/// 
		/// This method will set the  property for this 
		/// 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.
		/// 
		/// 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  object that is the parent or
		/// owner of this object.
		/// 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, GraphPane pane, float bufferFraction,
										bool isGrowOnly )
		{
			// save the original value of minSpace
			float oldSpace = this.MinSpace;
			// set minspace to zero, since we don't want it to affect the CalcSpace() result
			this.MinSpace = 0;
			// Calculate the space required for the current graph assuming scalefactor = 1.0
			// and apply the bufferFraction
			float fixedSpace;
			float space = this.CalcSpace( g, pane, 1.0F, out fixedSpace ) * bufferFraction;
			// isGrowOnly indicates the minSpace can grow but not shrink
			if ( isGrowOnly )
				space = Math.Max( oldSpace, space );
			// Set the minSpace
			this.MinSpace = space;
		}
		/// 
		/// Setup the Transform Matrix to handle drawing of this 
		/// 
		/// 
		/// 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  object that is the parent or
		/// owner of this object.
		/// 
		/// 
		/// The scaling factor to be used for rendering objects.  This is calculated and
		/// passed down by the parent  object using the
		///  method, and is used to proportionally adjust
		/// font sizes, etc. according to the actual size of the graph.
		/// 
		abstract public void SetTransformMatrix( Graphics g, GraphPane pane, float scaleFactor );
		/// 
		/// Calculate the "shift" size, in pixels, in order to shift the axis from its default
		/// location to the value specified by .
		/// 
		/// 
		/// A reference to the  object that is the parent or
		/// owner of this object.
		/// 
		/// The shift amount measured in pixels
		abstract internal float CalcCrossShift( GraphPane pane );
		/// 
		/// Gets the "Cross" axis that corresponds to this axis.
		/// 
		/// 
		/// The cross axis is the axis which determines the of this Axis when the
		/// Axis.Cross property is used.  The
		/// cross axis for any  or 
		/// is always the primary , and
		/// the cross axis for any  or  is
		/// always the primary .
		/// 
		/// 
		/// A reference to the  object that is the parent or
		/// owner of this object.
		/// 
		abstract public Axis GetCrossAxis( GraphPane pane );
//		abstract internal float GetMinPix( GraphPane pane );
		//abstract internal float CalcCrossFraction( GraphPane pane );
		/// 
		/// Returns the linearized actual cross position for this axis, reflecting the settings of
		/// , , and .
		/// 
		/// 
		/// If the value of  lies outside the axis range, it is
		/// limited to the axis range.
		/// 
		internal double EffectiveCrossValue( GraphPane pane )
		{
			Axis crossAxis = GetCrossAxis( pane );
			// Use Linearize here instead of _minLinTemp because this method is called
			// as part of CalcRect() before scale is fully setup
			double min = crossAxis._scale.Linearize( _scale._min );
			double max = crossAxis._scale.Linearize( _scale._max );
			if ( _crossAuto )
			{
				if ( crossAxis._scale.IsReverse == ( this is Y2Axis || this is X2Axis ) )
					return max;
				else
					return min;
			}
			else if ( _cross < min )
				return min;
			else if ( _cross > max )
				return max;
			else
				return _scale.Linearize( _cross );
		}
		/// 
		/// Returns true if the axis is shifted at all due to the setting of
		/// .  This function will always return false if
		///  is true.
		/// 
		internal bool IsCrossShifted( GraphPane pane )
		{
			if ( _crossAuto )
				return false;
			else
			{
				Axis crossAxis = GetCrossAxis( pane );
				if ( ( ( this is XAxis || this is YAxis ) && !crossAxis._scale.IsReverse ) ||
					( ( this is X2Axis || this is Y2Axis ) && crossAxis._scale.IsReverse ) )
				{
					if ( _cross <= crossAxis._scale._min )
						return false;
				}
				else
				{
					if ( _cross >= crossAxis._scale._max )
						return false;
				}
			}
			return true;
		}
		/// 
		/// Calculates the proportional fraction of the total cross axis width at which
		/// this axis is located.
		/// 
		/// 
		/// 
		internal float CalcCrossFraction( GraphPane pane )
		{
			// if this axis is not shifted due to the Cross value
			if ( !this.IsCrossShifted( pane ) )
			{
				// if it's the primary axis and the scale labels are on the inside, then we
				// don't need to save any room for the axis labels (they will be inside the chart rect)
				if ( IsPrimary( pane ) && _scale._isLabelsInside )
					return 1.0f;
				// otherwise, it's a secondary (outboard) axis and we always save room for the axis and labels.
				else
					return 0.0f;
			}
			double effCross = EffectiveCrossValue( pane );
			Axis crossAxis = GetCrossAxis( pane );
			// Use Linearize here instead of _minLinTemp because this method is called
			// as part of CalcRect() before scale is fully setup
			//			double max = crossAxis._scale._maxLinTemp;
			//			double min = crossAxis._scale._minLinTemp;
			double max = crossAxis._scale.Linearize( _scale._min );
			double min = crossAxis._scale.Linearize( _scale._max );
			float frac;
			if ( ( ( this is XAxis || this is YAxis ) && _scale._isLabelsInside == crossAxis._scale.IsReverse ) ||
				 ( ( this is X2Axis || this is Y2Axis ) && _scale._isLabelsInside != crossAxis._scale.IsReverse ) )
				frac = (float)( ( effCross - min ) / ( max - min ) );
			else
				frac = (float)( ( max - effCross ) / ( max - min ) );
			if ( frac < 0.0f )
				frac = 0.0f;
			if ( frac > 1.0f )
				frac = 1.0f;
			return frac;
		}
		private float CalcTotalShift( GraphPane pane, float scaleFactor, float shiftPos )
		{
			if ( !IsPrimary( pane ) )
			{
				// if ( CalcCrossFraction( pane ) != 0.0 )
				if ( IsCrossShifted( pane ) )
				{
					shiftPos = 0;
				}
				else
				{
					// Scaled size (pixels) of a tic
					float ticSize = _majorTic.ScaledTic( scaleFactor );
					// if the scalelabels are on the inside, shift everything so the axis is drawn,
					// for example, to the left side of the available space for a YAxis type
					if ( _scale._isLabelsInside )
					{
						shiftPos += _tmpSpace;
						// shift the axis to leave room for the outside tics
						if ( _majorTic.IsOutside || _majorTic._isCrossOutside ||
										_minorTic.IsOutside || _minorTic._isCrossOutside )
							shiftPos -= ticSize;
					}
					else
					{
						// if it's not the primary axis, add a tic space for the spacing between axes
						shiftPos += _axisGap * scaleFactor;
						// if it has inside tics, leave another tic space
						if ( _majorTic.IsInside || _majorTic._isCrossInside ||
								_minorTic.IsInside || _minorTic._isCrossInside )
							shiftPos += ticSize;
					}
				}
			}
			// shift is the position of the actual axis line itself
			// everything else is based on that position.
			float crossShift = CalcCrossShift( pane );
			shiftPos += crossShift;
			return shiftPos;
		}
		/// 
		/// Calculate the space required (pixels) for this  object.
		/// 
		/// 
		/// This is the total space (vertical space for the X axis, horizontal space for
		/// the Y axes) required to contain the axis.  If  is zero, then
		/// this space will be the space required between the  and
		/// the .  This method sets the internal values of
		///  for use by the 
		/// method.
		/// 
		/// 
		/// 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  object that is the parent or
		/// owner of this object.
		/// 
		/// 
		/// The scaling factor to be used for rendering objects.  This is calculated and
		/// passed down by the parent  object using the
		///  method, and is used to proportionally adjust
		/// font sizes, etc. according to the actual size of the graph.
		/// 
		/// The amount of space (pixels) at the edge of the ChartRect
		/// that is always required for this axis, even if the axis is shifted by the
		///  value.
		/// Returns the space, in pixels, required for this axis (between the
		/// rect and ChartRect)
		public float CalcSpace( Graphics g, GraphPane pane, float scaleFactor, out float fixedSpace )
		{
			//fixedSpace = 0;
			//Typical character height for the scale font
			float charHeight = _scale._fontSpec.GetHeight( scaleFactor );
			// Scaled size (pixels) of a tic
			float ticSize = _majorTic.ScaledTic( scaleFactor );
			// Scaled size (pixels) of the axis gap
			float axisGap = _axisGap * scaleFactor;
			float scaledLabelGap = _scale._labelGap * charHeight;
			float scaledTitleGap = _title.GetScaledGap( scaleFactor );
			// The minimum amount of space to reserve for the NORMAL position of the axis.  This would
			// be the left side of the chart rect for the Y axis, the right side for the Y2 axis, etc.
			// This amount of space is based on the need to reserve space for tics, etc., even if the
			// Axis.Cross property causes the axis to be in a different location.
			fixedSpace = 0;
			// The actual space needed for this axis (ignoring the setting of Axis.Cross)
			_tmpSpace = 0;
			// Account for the Axis
			if ( _isVisible )
			{
				bool hasTic = this.MajorTic.IsOutside || this.MajorTic._isCrossOutside ||
									this.MinorTic.IsOutside || this.MinorTic._isCrossOutside;
				// account for the tic space.  Leave the tic space for any type of outside tic (Outside Tic Space)
				if ( hasTic )
					_tmpSpace += ticSize;
				// if this is not the primary axis
				if ( !IsPrimary( pane ) )
				{
					// always leave an extra tic space for the space between the multi-axes (Axis Gap)
					_tmpSpace += axisGap;
					// if it has inside tics, leave another tic space (Inside Tic Space)
					if ( this.MajorTic._isInside || this.MajorTic._isCrossInside ||
							this.MinorTic._isInside || this.MinorTic._isCrossInside )
						_tmpSpace += ticSize;
				}
				// tic takes up 1x tic
				// space between tic and scale label is 0.5 tic
				// scale label is GetScaleMaxSpace()
				// space between scale label and axis label is 0.5 tic
				// account for the tic labels + 'LabelGap' tic gap between the tic and the label
				_tmpSpace += _scale.GetScaleMaxSpace( g, pane, scaleFactor, true ).Height +
						scaledLabelGap;
				string str = MakeTitle();
				// Only add space for the title if there is one
				// Axis Title gets actual height
				if ( str.Length > 0 && _title._isVisible )
				{
					//tmpSpace += this.TitleFontSpec.BoundingBox( g, str, scaleFactor ).Height;
					fixedSpace = this.Title.FontSpec.BoundingBox( g, str, scaleFactor ).Height +
							scaledTitleGap;
					_tmpSpace += fixedSpace;
					fixedSpace += scaledTitleGap;
				}
				if ( hasTic )
					fixedSpace += ticSize;
			}
			// for the Y axes, make sure that enough space is left to fit the first
			// and last X axis scale label
			if ( this.IsPrimary( pane ) && ( (
					( this is YAxis && (
						( !pane.XAxis._scale._isSkipFirstLabel && !pane.XAxis._scale._isReverse ) ||
						( !pane.XAxis._scale._isSkipLastLabel && pane.XAxis._scale._isReverse ) ) ) ||
					( this is Y2Axis && (
						( !pane.XAxis._scale._isSkipFirstLabel && pane.XAxis._scale._isReverse ) ||
						( !pane.XAxis._scale._isSkipLastLabel && !pane.XAxis._scale._isReverse ) ) ) ) &&
					pane.XAxis.IsVisible && pane.XAxis._scale._isVisible ) )
			{
				// half the width of the widest item, plus a gap of 1/2 the charheight
				float tmp = pane.XAxis._scale.GetScaleMaxSpace( g, pane, scaleFactor, true ).Width / 2.0F;
				//+ charHeight / 2.0F;
				//if ( tmp > tmpSpace )
				//	tmpSpace = tmp;
				fixedSpace = Math.Max( tmp, fixedSpace );
			}
			// Verify that the minSpace property was satisfied
			_tmpSpace = Math.Max( _tmpSpace, _minSpace * (float)scaleFactor );
			fixedSpace = Math.Max( fixedSpace, _minSpace * (float)scaleFactor );
			return _tmpSpace;
		}
		/// 
		/// Determines if this  object is a "primary" one.
		/// 
		/// 
		/// The primary axes are the  (always), the first
		///  in the  
		/// ( = 0),  and the first
		///  in the  
		/// ( = 0).  Note that
		///  and 
		/// always reference the primary axes.
		/// 
		/// 
		/// A reference to the  object that is the parent or
		/// owner of this object.
		/// 
		/// true for a primary  (for the ,
		/// this is always true), false otherwise
		abstract internal bool IsPrimary( GraphPane pane );
		internal void FixZeroLine( Graphics g, GraphPane pane, float scaleFactor,
				float left, float right )
		{
			// restore the zero line if needed (since the fill tends to cover it up)
			if ( _isVisible && _majorGrid._isZeroLine &&
					_scale._min < 0.0 && _scale._max > 0.0 )
			{
				float zeroPix = _scale.Transform( 0.0 );
				using ( Pen zeroPen = new Pen( _color,
						pane.ScaledPenWidth( _majorGrid._penWidth, scaleFactor ) ) )
				{
					g.DrawLine( zeroPen, left, zeroPix, right, zeroPix );
					//zeroPen.Dispose();
				}
			}
		}
		/// 
		/// Draw the minor tic marks as required for this .
		/// 
		/// 
		/// 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  object that is the parent or
		/// owner of this object.
		/// 
		/// 
		/// The scale value for the first major tic position.  This is the reference point
		/// for all other tic marks.
		/// 
		/// The number of pixels to shift this axis, based on the
		/// value of .  A positive value is into the ChartRect relative to
		/// the default axis position.
		/// 
		/// The scaling factor to be used for rendering objects.  This is calculated and
		/// passed down by the parent  object using the
		///  method, and is used to proportionally adjust
		/// font sizes, etc. according to the actual size of the graph.
		/// 
		/// 
		/// The pixel location of the far side of the ChartRect from this axis.
		/// This value is the ChartRect.Height for the XAxis, or the ChartRect.Width
		/// for the YAxis and Y2Axis.
		/// 
		public void DrawMinorTics( Graphics g, GraphPane pane, double baseVal, float shift,
								float scaleFactor, float topPix )
		{
			if ( ( this.MinorTic.IsOutside || this.MinorTic.IsOpposite || this.MinorTic.IsInside ||
					this.MinorTic._isCrossOutside || this.MinorTic._isCrossInside || _minorGrid._isVisible )
					&& _isVisible )
			{
				double	tMajor = _scale._majorStep * _scale.MajorUnitMultiplier,
							tMinor = _scale._minorStep * _scale.MinorUnitMultiplier;
				if ( _scale.IsLog || tMinor < tMajor )
				{
					float minorScaledTic = this.MinorTic.ScaledTic( scaleFactor );
					// Minor tics start at the minimum value and step all the way thru
					// the full scale.  This means that if the minor step size is not
					// an even division of the major step size, the minor tics won't
					// line up with all of the scale labels and major tics.
					double	first = _scale._minLinTemp,
								last = _scale._maxLinTemp;
					double dVal = first;
					float pixVal;
					int iTic = _scale.CalcMinorStart( baseVal );
					int MajorTic = 0;
					double majorVal = _scale.CalcMajorTicValue( baseVal, MajorTic );
					using ( Pen	pen = new Pen( _minorTic._color,
										pane.ScaledPenWidth( MinorTic._penWidth, scaleFactor ) ) )
					using ( Pen minorGridPen = _minorGrid.GetPen( pane, scaleFactor ) )
					{
						// Draw the minor tic marks
						while ( dVal < last && iTic < 5000 )
						{
							// Calculate the scale value for the current tic
							dVal = _scale.CalcMinorTicValue( baseVal, iTic );
							// Maintain a value for the current major tic
							if ( dVal > majorVal )
								majorVal = _scale.CalcMajorTicValue( baseVal, ++MajorTic );
							// Make sure that the current value does not match up with a major tic
							if ( ( ( Math.Abs( dVal ) < 1e-20 && Math.Abs( dVal - majorVal ) > 1e-20 ) ||
								( Math.Abs( dVal ) > 1e-20 && Math.Abs( ( dVal - majorVal ) / dVal ) > 1e-10 ) ) &&
								( dVal >= first && dVal <= last ) )
							{
								pixVal = _scale.LocalTransform( dVal );
								_minorGrid.Draw( g, minorGridPen, pixVal, topPix );
								_minorTic.Draw( g, pane, pen, pixVal, topPix, shift, minorScaledTic );
							}
							iTic++;
						}
					}
				}
			}
		}
		/// 
		/// Draw the title for this .
		/// 
		/// On entry, it is assumed that the
		/// graphics transform has been configured so that the origin is at the left side
		/// of this axis, and the axis is aligned along the X coordinate direction.
		/// 
		/// 
		/// 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  object that is the parent or
		/// owner of this object.
		/// 
		/// The number of pixels to shift this axis, based on the
		/// value of .  A positive value is into the ChartRect relative to
		/// the default axis position.
		/// 
		/// The scaling factor to be used for rendering objects.  This is calculated and
		/// passed down by the parent  object using the
		///  method, and is used to proportionally adjust
		/// font sizes, etc. according to the actual size of the graph.
		/// 
		public void DrawTitle( Graphics g, GraphPane pane, float shiftPos, float scaleFactor )
		{
			string str = MakeTitle();
			// If the Axis is visible, draw the title
			if ( _isVisible && _title._isVisible && str.Length > 0 )
			{
				bool hasTic = ( _scale._isLabelsInside ?
						( this.MajorTic.IsInside || this.MajorTic._isCrossInside ||
								this.MinorTic.IsInside || this.MinorTic._isCrossInside ) :
						( this.MajorTic.IsOutside || this.MajorTic._isCrossOutside || this.MinorTic.IsOutside || this.MinorTic._isCrossOutside ) );
				// Calculate the title position in screen coordinates
				float x = ( _scale._maxPix - _scale._minPix ) / 2;
				float scaledTic = MajorTic.ScaledTic( scaleFactor );
				float scaledLabelGap = _scale._fontSpec.GetHeight( scaleFactor ) * _scale._labelGap;
				float scaledTitleGap = _title.GetScaledGap( scaleFactor );
				// The space for the scale labels is only reserved if the axis is not shifted due to the
				// cross value.  Note that this could be a problem if the axis is only shifted slightly,
				// since the scale value labels may overlap the axis title.  However, it's not possible to
				// calculate that actual shift amount at this point, because the ChartRect rect has not yet been
				// calculated, and the cross value is determined using a transform of scale values (which
				// rely on ChartRect).
				float gap = scaledTic * ( hasTic ? 1.0f : 0.0f ) +
							this.Title.FontSpec.BoundingBox( g, str, scaleFactor ).Height / 2.0F;
				float y = ( _scale._isVisible ? _scale.GetScaleMaxSpace( g, pane, scaleFactor, true ).Height
							+ scaledLabelGap : 0 );
				if ( _scale._isLabelsInside )
					y = shiftPos - y - gap;
				else
					y = shiftPos + y + gap;
				if ( !_crossAuto && !_title._isTitleAtCross )
					y = Math.Max( y, gap );
				AlignV alignV = AlignV.Center;
				// Add in the TitleGap space
				y += scaledTitleGap;
				// Draw the title
				this.Title.FontSpec.Draw( g, pane, str, x, y,
							AlignH.Center, alignV, scaleFactor );
			}
		}
		private string MakeTitle()
		{
			// Revision: JCarpenter 10/06
			// Allow customization of the modified title when the scale is very large
			// The event handler can edit the full label.  If the handler returns
			// null, then the title will be the default.
			if ( ScaleTitleEvent != null )
			{
				string label = ScaleTitleEvent( this );
				if ( label != null )
					return label;
			}
			// If the Mag is non-zero and IsOmitMag == false, and IsLog == false,
			// then add the mag indicator to the title.
			if ( _scale._mag != 0 && !_title._isOmitMag && !_scale.IsLog )
				return _title._text + String.Format( " (10^{0})", _scale._mag );
			else
				return _title._text;
		}
		/// 
		/// Make a value label for the axis at the specified ordinal position.
		/// 
		/// 
		/// This method properly accounts for ,
		/// ,
		/// and other axis format settings.  It also implements the ScaleFormatEvent such that
		/// custom labels can be created.
		/// 
		/// 
		/// A reference to the  object that is the parent or
		/// owner of this object.
		/// 
		/// 
		/// The zero-based, ordinal index of the label to be generated.  For example, a value of 2 would
		/// cause the third value label on the axis to be generated.
		/// 
		/// 
		/// The numeric value associated with the label.  This value is ignored for log
		/// ()
		/// and text () type axes.
		/// 
		/// The resulting value label as a 
		internal string MakeLabelEventWorks( GraphPane pane, int index, double dVal )
		{
			// if there is a valid ScaleFormatEvent, then try to use it to create the label
			// the label will be non-null if it's to be used
			if ( this.ScaleFormatEvent != null )
			{
				string label;
				label = this.ScaleFormatEvent( pane, this, dVal, index );
				if ( label != null )
					return label;
			}
			// second try.  If there's no custom ScaleFormatEvent, then just call
			// _scale.MakeLabel according to the type of scale
			if ( this.Scale != null )
				return _scale.MakeLabel( pane, index, dVal );
			else
				return "?";
		}
	#endregion
	}
}