File: Common\General\AxisScale.cs
Project: ndp\fx\src\DataVisualization\System.Windows.Forms.DataVisualization.csproj (System.Windows.Forms.DataVisualization)
//-------------------------------------------------------------
// <copyright company=’Microsoft Corporation’>
//   Copyright © Microsoft Corporation. All Rights Reserved.
// </copyright>
//-------------------------------------------------------------
// @owner=alexgor, deliant
//=================================================================
//  File:		AxisScale.cs
//
//  Namespace:	System.Web.UI.WebControls[Windows.Forms].Charting
//
//	Classes:	AxisScale
//
//  Purpose:	Base class for the Axis class which defines axis 
//				csale related properties and methods.
//
//	Reviewed:	GS Aug 8, 2002
//				AG Aug 8, 2002
//
//===================================================================
 
#region Used namespaces
 
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Data;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Collections.Generic;
#if Microsoft_CONTROL

	using System.Windows.Forms.DataVisualization.Charting.Data;
	using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
	using System.Windows.Forms.DataVisualization.Charting.Utilities;
	using System.Windows.Forms.DataVisualization.Charting.Borders3D;
	using System.Windows.Forms.DataVisualization.Charting;
 
#else
	using System.Web;
	using System.Web.UI;
	using System.Web.UI.DataVisualization.Charting;
	using System.Web.UI.DataVisualization.Charting.Data;
	using System.Web.UI.DataVisualization.Charting.ChartTypes;
	using System.Web.UI.DataVisualization.Charting.Utilities;
#endif
 
#endregion
 
#if Microsoft_CONTROL
	namespace System.Windows.Forms.DataVisualization.Charting
#else
namespace System.Web.UI.DataVisualization.Charting
 
#endif
{
	#region Axis enumerations
 
	/// <summary>
	/// An enumeration of the mode of automatically calculating intervals.
	/// </summary>
	public enum IntervalAutoMode
	{
		/// <summary>
		/// Fixed number of intervals always created on the axis.
		/// </summary>
		FixedCount,
 
		/// <summary>
		/// Number of axis intervals depends on the axis length.
		/// </summary>
		VariableCount
	}
 
 
 
	/// <summary>
	/// An enumeration of axis position.
	/// </summary>
	internal enum AxisPosition
	{
		/// <summary>
		/// Left position
		/// </summary>
		Left,
 
		/// <summary>
		/// Right position
		/// </summary>
		Right,
 
		/// <summary>
		/// Top position
		/// </summary>
		Top,
 
		/// <summary>
		/// Bottom position
		/// </summary>
		Bottom
	}
 
	/// <summary>
	/// An enumeration of axis arrow styles.
	/// </summary>
	public enum AxisArrowStyle
	{
		/// <summary>
		/// No arrow
		/// </summary>
		None, 
		/// <summary>
		/// Triangle type
		/// </summary>
		Triangle, 
		/// <summary>
		/// Sharp triangle type
		/// </summary>
		SharpTriangle, 
		/// <summary>
		/// Lines type
		/// </summary>
		Lines
	}
 
	#endregion
 
	/// <summary>
	/// The Axis class keeps information about minimum, maximum 
	/// and interval values and it is responsible for setting 
	/// these values automatically. It also handles
	/// logarithmic and reversed axis.
	/// </summary>
	public partial class Axis
	{
		#region Axis scale fields
 
		// Represents the distance between the data points and its 
		// chart area margin, Measured as a percentage of default 
		// margin size.
		internal double					margin = 100.0;
		internal double					marginView = 0.0;
		internal bool					offsetTempSet = false;
 
		// Used for column chart margin
		internal double					marginTemp = 0.0;
		private ArrayList				_stripLineOffsets = new ArrayList();
		
 
		// Data members, which store properties values
		private  bool					_isLogarithmic = false;
		internal double					logarithmBase = 10.0;
		internal bool					isReversed = false;
		internal bool					isStartedFromZero = true;
		internal TickMark				minorTickMark = null;
		internal TickMark				majorTickMark = null;
		internal Grid					minorGrid = null;
		internal Grid					majorGrid = null;
		internal bool					enabled = false;		
		internal bool					autoEnabled = true;		
		internal LabelStyle					labelStyle = null;
		private	 DateTimeIntervalType	_internalIntervalType = DateTimeIntervalType.Auto;
		internal double					maximum = Double.NaN;
		internal double					crossing = Double.NaN;
		internal double					minimum = Double.NaN;
 
		// Temporary Minimum and maximum values.
		internal double					tempMaximum = Double.NaN;
		internal double					tempMinimum = Double.NaN;
		internal double					tempCrossing = Double.NaN;
		internal CustomLabelsCollection	tempLabels;
		internal bool					tempAutoMaximum = true;
		internal bool					tempAutoMinimum = true;
		internal double					tempMajorGridInterval = Double.NaN;
		internal double					tempMinorGridInterval = 0.0;
		internal double					tempMajorTickMarkInterval = Double.NaN;
		internal double					tempMinorTickMarkInterval = 0.0;
		internal double					tempLabelInterval = Double.NaN;
		internal DateTimeIntervalType	tempGridIntervalType = DateTimeIntervalType.NotSet;
		internal DateTimeIntervalType	tempTickMarkIntervalType = DateTimeIntervalType.NotSet;
		internal DateTimeIntervalType	tempLabelIntervalType = DateTimeIntervalType.NotSet;
 
		// Paint mode
		internal bool					paintMode = false;
 
		// Axis type (X, Y, X2, Y2)
		internal AxisName				axisType = AxisName.X;
 
		// Automatic maximum value (from data point values).
		private bool					_autoMaximum = true;
 
		// Automatic minimum value (from data point values).
		private bool					_autoMinimum = true;
		
		/// <summary>
		/// Axis position: Left, Right, Top Bottom
		/// </summary>
		private AxisPosition			_axisPosition = AxisPosition.Left;
 
		/// <summary>
		/// Opposite Axis for this Axis. Necessary for Crossing.		
		/// </summary>
		internal Axis					oppositeAxis = null;
 
		// Axis data scaleView
		private	AxisScaleView			_scaleView = null;
 
#if Microsoft_CONTROL

		// Axis scroll bar class
		internal AxisScrollBar			scrollBar = null;
 
#endif // Microsoft_CONTROL
 
		// For scater chart X values could be rounded.
		internal bool roundedXValues = false;
 
		// If Axis is logarithmic value shoud be converted to 
		// linear only once.
		internal bool logarithmicConvertedToLinear = false;
 
		// IsLogarithmic minimum value
		internal double logarithmicMinimum;
 
		// IsLogarithmic maximum value
		internal double logarithmicMaximum;
 
		// Correction of interval because of 
		// 3D Rotation and perspective
		internal double interval3DCorrection = Double.NaN;
 
		// Axis coordinate convertion optimization fields
		internal bool optimizedGetPosition = false;
		internal double paintViewMax = 0.0;
		internal double paintViewMin = 0.0;
		internal double	paintRange = 0.0;
		internal double	valueMultiplier = 0.0;
		internal RectangleF	paintAreaPosition = RectangleF.Empty;
		internal double paintAreaPositionBottom = 0.0;
		internal double paintAreaPositionRight = 0.0;
		internal double paintChartAreaSize = 0.0;	
 
 
 
		// Determines how number of intervals automatically calculated
		private IntervalAutoMode _intervalAutoMode = IntervalAutoMode.FixedCount;
 
		// True if scale segments are used
		internal bool scaleSegmentsUsed = false;
 
 
 
		// Preffered number of intervals on the axis
		internal int  prefferedNumberofIntervals = 5;
 
        private Stack<Double> _intervalsStore = new Stack<Double>();
 
		#endregion
 
		#region Axis scale properties
 
		/// <summary>
		/// Axis position
		/// </summary>
		[
		Bindable(true),
		DefaultValue(AxisPosition.Left),
		NotifyParentPropertyAttribute(true),
		SRDescription("DescriptionAttributeReverse"),
		#if !Microsoft_CONTROL
		PersistenceMode(PersistenceMode.Attribute),
		#endif
		DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
		SerializationVisibilityAttribute(SerializationVisibility.Hidden)
		]
		virtual internal AxisPosition AxisPosition
		{
			get
			{
                return this._axisPosition;
			}
			set
			{
                this._axisPosition = value;
#if SUBAXES
				// Update axis position of the sub axis
				if( !((Axis)this).IsSubAxis )
				{
					foreach(SubAxis subAxis in ((Axis)this).SubAxes)
					{
						subAxis._axisPosition = value;
					}
				}
 
#endif // SUBAXES
                this.Invalidate();
			}
		}
 
 
 
		/// <summary>
        /// Gets or sets a flag which indicates whether the number of intervals
        /// on the axis is fixed or varies with the axis size.
		/// </summary>
		[
		SRCategory("CategoryAttributeInterval"),
		DefaultValue(IntervalAutoMode.FixedCount),
		SRDescription("DescriptionAttributeIntervalAutoMode"),
		]
		public IntervalAutoMode IntervalAutoMode
		{
			get
			{
				return this._intervalAutoMode;
			}
			set
			{
				this._intervalAutoMode = value;
				this.Invalidate();
			}
		}
 
 
 
		/// <summary>
		/// Gets or sets a flag which indicates whether the axis is reversed.
        /// If set to reversed, the values on the axis are in reversed sort order
        /// and the direction of values on the axis is flipped.
		/// </summary>
		[
		SRCategory("CategoryAttributeScale"),
		Bindable(true),
		DefaultValue(false),
		NotifyParentPropertyAttribute(true),
		SRDescription("DescriptionAttributeReverse"),
		#if !Microsoft_CONTROL
		PersistenceMode(PersistenceMode.Attribute)
		#endif
		]
		public bool IsReversed
		{
			get
			{
				return isReversed;
			}
			set
			{
				isReversed = value;
				this.Invalidate();
			}
		}
 
		/// <summary>
        /// Gets or sets a flag which indicates whether the minimum value
        /// of the axis will be automatically set to zero if all data point
        /// values are positive.  If there are negative data point values,
        /// the minimum value of the data points will be used.
		/// </summary>
		[
		SRCategory("CategoryAttributeScale"),
		Bindable(true),
		DefaultValue(true),
		NotifyParentPropertyAttribute(true),
		SRDescription("DescriptionAttributeStartFromZero3"),
		#if !Microsoft_CONTROL
		PersistenceMode(PersistenceMode.Attribute)
		#endif
		]
		public bool IsStartedFromZero
		{
			get
			{
				return isStartedFromZero;
			}
			set
			{
				isStartedFromZero = value;
				this.Invalidate();
			}
		}
 
		/// <summary>
        /// Gets or sets a flag to add a margin to the axis.
		/// If true, a space is added between the first/last data 
		/// point and the border of chart area.
		/// </summary>
		[
		SRCategory("CategoryAttributeScale"),
		Bindable(true),
		DefaultValue(true),
		NotifyParentPropertyAttribute(true),
		SRDescription("DescriptionAttributeMargin"),
		#if !Microsoft_CONTROL
		PersistenceMode(PersistenceMode.Attribute)
		#endif
		]
		public bool IsMarginVisible
		{
			get
			{
				if( margin > 0 )
					return true;
				else
					return false;
			}
			set
			{
				if( value == true )
					margin = 100;
				else
					margin = 0;
 
				this.Invalidate();
			}
		}
 
		/// <summary>
		/// Date and time interval type.
		/// </summary>
		[
		SRCategory("CategoryAttributeScale"),
		Bindable(true),
		DefaultValue(DateTimeIntervalType.Auto),
		NotifyParentPropertyAttribute(true),
		SRDescription("DescriptionAttributeInternalIntervalType"),
		RefreshPropertiesAttribute(RefreshProperties.All),
		#if !Microsoft_CONTROL
		PersistenceMode(PersistenceMode.Attribute)
		#endif
		]
		internal DateTimeIntervalType InternalIntervalType
		{
			get
			{
                return _internalIntervalType;
			}
			set
			{
				// Set intervals for labels, grids and tick marks. ( Auto interval type )
				if( tempMajorGridInterval <= 0.0 ||
					(double.IsNaN(tempMajorGridInterval) && ((Axis)this).Interval <= 0.0) ) 
				{
					majorGrid.intervalType = value;
				}
 
				if( this.tempMajorTickMarkInterval <= 0.0 ||
					(double.IsNaN(tempMajorTickMarkInterval) && ((Axis)this).Interval <= 0.0) ) 
				{
					majorTickMark.intervalType = value;
				}
 
				if( this.tempLabelInterval <= 0.0 ||
					(double.IsNaN(tempLabelInterval) && ((Axis)this).Interval <= 0.0) ) 
				{
					labelStyle.intervalType = value;
				}
 
                _internalIntervalType = value;
 
				this.Invalidate();
			}
		}
 
		/// <summary>
		/// Sets auto interval values to grids, tick marks
		/// and labels
		/// </summary>
		internal double SetInterval
		{
			set
			{
				if( tempMajorGridInterval <= 0.0 ||
					(double.IsNaN(tempMajorGridInterval) && ((Axis)this).Interval <= 0.0) ) 
				{
					majorGrid.interval = value;
				}
 
				if( tempMajorTickMarkInterval <= 0.0 ||
					(double.IsNaN(tempMajorTickMarkInterval) && ((Axis)this).Interval <= 0.0) ) 
				{
					majorTickMark.interval = value;
				}
 
				if( tempLabelInterval <= 0.0 ||
					(double.IsNaN(tempLabelInterval) && ((Axis)this).Interval <= 0.0) ) 
				{
					labelStyle.interval = value;
				}
 
				this.Invalidate();
			}
		}
 
		/// <summary>
		/// Sets auto interval values to grids, tick marks
		/// and labels
		/// </summary>
		internal void SetIntervalAndType(double newInterval, DateTimeIntervalType newIntervalType)
		{
			if( tempMajorGridInterval <= 0.0 ||
				(double.IsNaN(tempMajorGridInterval) && ((Axis)this).Interval <= 0.0) ) 
			{
				majorGrid.interval = newInterval;
				majorGrid.intervalType = newIntervalType;
			}
 
			if( tempMajorTickMarkInterval <= 0.0 ||
				(double.IsNaN(tempMajorTickMarkInterval) && ((Axis)this).Interval <= 0.0) ) 
			{
				majorTickMark.interval = newInterval;
				majorTickMark.intervalType = newIntervalType;
			}
 
			if( tempLabelInterval <= 0.0 ||
				(double.IsNaN(tempLabelInterval) && ((Axis)this).Interval <= 0.0) ) 
			{
				labelStyle.interval = newInterval;
				labelStyle.intervalType = newIntervalType;
			}
 
			this.Invalidate();
		}
 
		
		/// <summary>
		/// Gets or sets the maximum axis value.
		/// </summary>
		[
 
		SRCategory("CategoryAttributeScale"),
		Bindable(true),
		DefaultValue(Double.NaN),
		NotifyParentPropertyAttribute(true),
		SRDescription("DescriptionAttributeMaximum"),
		#if !Microsoft_CONTROL
		PersistenceMode(PersistenceMode.Attribute),
		#endif
        TypeConverter(typeof(AxisMinMaxAutoValueConverter))
		]
		public double Maximum
		{
			get
			{
				// Get maximum
                if (_isLogarithmic && logarithmicConvertedToLinear && !Double.IsNaN(maximum))
					return logarithmicMaximum;
				else
					return maximum;
			}
			set
			{
				// Split a value to maximum and auto maximum
				if( Double.IsNaN(value) )
				{
					_autoMaximum = true;
					maximum = Double.NaN;
				}
				else
				{
					// Set maximum
					maximum = value;
 
					// Set non linearized Maximum for logarithmic scale
					logarithmicMaximum = value;
 
					_autoMaximum = false;
				}
 
				// Reset original property value fields
				((Axis)this).tempMaximum = maximum;
 
				// This line is added because of Save ScaleView State August 29, 2003
				// in Web Forms. This place could cause problems with Reset Auto Values.
				((Axis)this).tempAutoMaximum = _autoMaximum;
 
				this.Invalidate();
			}
		}
 
		/// <summary>
		/// Gets or sets the minimum axis value
		/// </summary>
		[
 
		SRCategory("CategoryAttributeScale"),
		Bindable(true),
		DefaultValue(Double.NaN),
		NotifyParentPropertyAttribute(true),
		SRDescription("DescriptionAttributeMinimum"),
		#if !Microsoft_CONTROL
		PersistenceMode(PersistenceMode.Attribute),
		#endif
        TypeConverter(typeof(AxisMinMaxAutoValueConverter))
		]
		public double Minimum
		{
			get
			{
				// Get minimum
                if (_isLogarithmic && logarithmicConvertedToLinear && !Double.IsNaN(maximum))
					return logarithmicMinimum;
				else
					return minimum;
			}
			set
			{
				// Split a value to minimum and auto minimum
				if( Double.IsNaN(value) )
				{
					_autoMinimum = true;
					minimum = Double.NaN;
				}
				else
				{
					// Set maximum
					minimum = value;
					_autoMinimum = false;
 
					// Set non linearized Minimum for logarithmic scale
					logarithmicMinimum = value;
				}
 
				// Reset original property value fields
				((Axis)this).tempMinimum = minimum;
 
				// This line is added because of Save ScaleView State August 29, 2003
				// in Web Forms. This place could cause problems with Reset Auto Values.
				((Axis)this).tempAutoMinimum = _autoMinimum;
 
				this.Invalidate();
			}
		}
 
		/// <summary>
		/// Gets or sets the point where axis is crossed by another axis.
		/// </summary>
		[
		SRCategory("CategoryAttributeScale"),
		Bindable(true),
		DefaultValue(Double.NaN),
		NotifyParentPropertyAttribute(true),
		SRDescription("DescriptionAttributeCrossing"),
		#if !Microsoft_CONTROL
		PersistenceMode(PersistenceMode.Attribute),
		#endif
        TypeConverter(typeof(AxisCrossingValueConverter))
		]
		virtual public double Crossing
		{
			get
			{
				if( paintMode )
                    if (_isLogarithmic)
						return Math.Pow( this.logarithmBase, GetCrossing() );
					else
						return GetCrossing();
				else
					return crossing;
			}
			set
			{
				crossing = value;
 
				// Reset original property value fields
				((Axis)this).tempCrossing = crossing;
 
				this.Invalidate();
			}
		}
 
	
		/// <summary>
		/// Enables or disables the axis.
		/// </summary>
		[
		SRCategory("CategoryAttributeMisc"),
		Bindable(true),
		DefaultValue(typeof(AxisEnabled), "Auto"),
		NotifyParentPropertyAttribute(true),
		SRDescription("DescriptionAttributeEnabled7"),
		#if !Microsoft_CONTROL
		PersistenceMode(PersistenceMode.Attribute)
		#endif
		]
		public AxisEnabled Enabled
		{
			get
			{
				// Take Enabled from two fields: enabled and auto enabled
				if( autoEnabled )
				{
					return AxisEnabled.Auto;
				}
				else if( enabled )
				{
					return AxisEnabled.True;
				}
				else
				{
					return AxisEnabled.False;
				}	
			}
			set
			{ // Split Enabled to two fields: enabled and auto enabled
				if( value == AxisEnabled.Auto )
				{
					autoEnabled = true;
				}
				else if( value == AxisEnabled.True )
				{
					enabled = true;
					autoEnabled = false;
				}
				else
				{
					enabled = false;
					autoEnabled = false;
				}
 
				this.Invalidate();
			}
		}
 
		/// <summary>
        /// Gets or sets a flag which indicates whether the axis is logarithmic. 
		/// Zeros or negative data values are not allowed on logarithmic charts. 
		/// </summary>
		[
        SRCategory("CategoryAttributeScale"),
		Bindable(true),
		DefaultValue(false),
		NotifyParentPropertyAttribute(true),
		SRDescription("DescriptionAttributeLogarithmic"),
		#if !Microsoft_CONTROL
		PersistenceMode(PersistenceMode.Attribute)
		#endif
		]
		public bool IsLogarithmic
		{
			get
			{
                return _isLogarithmic;
			}
			set
			{
                _isLogarithmic = value;
				this.Invalidate();
			}
		}
 
		/// <summary>
		/// Base of the logarithm used in logarithmic scale. 
		/// By default, this value is 10.
		/// </summary>
		[
        SRCategory("CategoryAttributeScale"),
		Bindable(true),
		DefaultValue(10.0),
		NotifyParentPropertyAttribute(true),
		SRDescription("DescriptionAttributeLogarithmBase"),
		#if !Microsoft_CONTROL
		PersistenceMode(PersistenceMode.Attribute)
		#endif
		]
		public double LogarithmBase
		{
			get
			{
				return logarithmBase;
			}
			set
			{
				if( value < 2.0 )
				{
                    throw (new ArgumentOutOfRangeException("value", SR.ExceptionAxisScaleLogarithmBaseInvalid));
				}
 
				logarithmBase = value;
				
				this.Invalidate();
			}
		}
 
		#endregion
 
		#region Axis Segments and Scale Breaks Properties
 
 
 
		// Field that stores Axis automatic scale breaks style.
		internal AxisScaleBreakStyle axisScaleBreakStyle = null;
 
        /// <summary>
        /// Gets or sets the style of scale breaks.
        /// </summary>
		[
		SRCategory("CategoryAttributeScale"),
		SRDescription("DescriptionAttributeScaleBreakStyle"),
        TypeConverter(typeof(NoNameExpandableObjectConverter)),
		NotifyParentPropertyAttribute(true),
#if Microsoft_CONTROL
		DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 
#else
		PersistenceMode(PersistenceMode.InnerProperty),
#endif
		]
		virtual public AxisScaleBreakStyle ScaleBreakStyle
		{
			get
			{
				return this.axisScaleBreakStyle;
			}
			set
			{
				this.axisScaleBreakStyle = value;
				this.axisScaleBreakStyle.axis = (Axis)this;
				//this.Invalidate();
			}
		}
 
		// Field that stores axis scale segments
		internal AxisScaleSegmentCollection scaleSegments = null;
 
		/// <summary>
		/// Axis scale segment collection.
		/// </summary>
		[
		SRCategory("CategoryAttributeScale"),
		Browsable(false),
		EditorBrowsable(EditorBrowsableState.Never),
		SRDescription("DescriptionAttributeAxisScaleSegmentCollection_AxisScaleSegmentCollection"),
		SerializationVisibilityAttribute(SerializationVisibility.Hidden),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), 
        Editor(Editors.ChartCollectionEditor.Editor, Editors.ChartCollectionEditor.Base)
		]
        internal AxisScaleSegmentCollection ScaleSegments
		{
			get
			{
				return this.scaleSegments;
			}
		}
 
		#endregion // Axis Segments and Scale Breaks Properties
 
		#region Axis data scaleView properies and methods
 
		/// <summary>
		/// Gets or sets the scale view settings of the axis.
		/// </summary>
		[
		SRCategory("CategoryAttributeDataView"),
		Bindable(true),
		SRDescription("DescriptionAttributeView"),
 
#if Microsoft_CONTROL
		DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 
#else
		PersistenceMode(PersistenceMode.InnerProperty),
#endif
        TypeConverter(typeof(NoNameExpandableObjectConverter))
		]
 	    public AxisScaleView ScaleView
		{
			get
			{
				return _scaleView;
			}
			set
			{
				_scaleView = value;
				_scaleView.axis = (Axis)this;
				this.Invalidate();
			}
		}
 
#if Microsoft_CONTROL

		/// <summary>
		/// Gets or sets the scroll bar settings of the axis.
		/// </summary>
		[
		SRCategory("CategoryAttributeDataView"),
		Bindable(true),
		SRDescription("DescriptionAttributeScrollBar"),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 
        TypeConverter(typeof(NoNameExpandableObjectConverter))
		]
        public AxisScrollBar ScrollBar
		{
			get
			{
				return scrollBar;
			}
			set
			{
				scrollBar = value;
				scrollBar.axis = (Axis)this;
				this.Invalidate();
			}
		}
 
#endif // Microsoft_CONTROL
 
		/// <summary>
		/// Gets axis data scaleView minimum position.
		/// </summary>
		/// <returns>Axis data scaleView minimum position.</returns>
		internal double ViewMinimum
		{
            get { return _scaleView.ViewMinimum; }
		}
 
		/// <summary>
		/// Gets axis data scaleView minimum position.
		/// </summary>
		/// <returns>Axis data scaleView minimum position.</returns>
		internal double ViewMaximum
		{
            get { return _scaleView.ViewMaximum; }
		}
 
        /// <summary>
        /// Gets automatic maximum value (from data point values). 
        /// </summary>
        internal bool AutoMaximum
        {
            get { return _autoMaximum; }
        }
 
        /// <summary>
        /// Gets automatic minimum value (from data point values). 
        /// </summary>
        internal bool AutoMinimum
        {
            get { return _autoMinimum; }
        }
 
		#endregion
 
		#region Axis position converters methos
 
		/// <summary>
		/// This function converts axis value to relative position (0-100%). 
		/// If an axis has a logarithmic scale, the value is converted to a linear scale.
		/// </summary>
		/// <param name="axisValue">Value from axis.</param>
		/// <returns>Relative position (0-100%).</returns>
		public double GetPosition( double axisValue )
		{
			// Adjust for the IsLogarithmic axis
            if (_isLogarithmic && axisValue != 0.0) 
			{
				axisValue = Math.Log( axisValue, this.logarithmBase );
			}
 
			// Get linear position
			return GetLinearPosition(axisValue);
		}
			
		/// <summary>
		/// This function converts an axis value to relative position (0-100%). 
		/// If an axis has a logarithmic scale, the value is converted to a linear scale.
		/// </summary>
		/// <param name="axisValue">Axis value.</param>
		/// <returns>Relative position (0-100%).</returns>
		public double ValueToPosition( double axisValue )
		{
			return GetPosition( axisValue );
		}
			
		/// <summary>
		/// This function converts an axis value to a pixel position. 
        /// If an axis has a logarithmic scale, the value is converted to a linear scale.
		/// </summary>
		/// <param name="axisValue">Value from axis.</param>
		/// <returns>Pixel position.</returns>
		public double ValueToPixelPosition( double axisValue )
		{
			// Get relative value
			double val = ValueToPosition(axisValue);
 
			// Convert it to pixels
			if( AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom )
			{
				val *= (this.Common.ChartPicture.Width - 1) / 100F; 
			}
			else 
			{
				val *= (this.Common.ChartPicture.Height - 1) / 100F; 
			}
 
			return val;
		}
 
		/// <summary>
		/// This function converts a relative position to an axis value.
        /// If an axis has a logarithmic scale, the value is converted to a linear scale.
		/// </summary>
		/// <param name="position">Relative position (0-100%).</param>
		/// <returns>Axis value.</returns>
		public double PositionToValue( double position )
		{
			return PositionToValue(position, true);
		}
 
		/// <summary>
		/// This function converts a relative position to an axis value.
        /// If an axis has a logarithmic scale, the value is converted to a linear scale.
		/// </summary>
		/// <param name="position">Relative position (0-100%).</param>
		/// <param name="validateInput">Indicates if input value range should be checked.</param>
		/// <returns>Axis value.</returns>
		internal double PositionToValue( double position, bool validateInput)
		{
			// Check parameters
			if(validateInput &&
				(position < 0 || position > 100) )
			{
                throw (new ArgumentException(SR.ExceptionAxisScalePositionInvalid, "position"));
			}
 
			// Check if plot area position was already calculated
			if(PlotAreaPosition == null)
			{
                throw (new InvalidOperationException(SR.ExceptionAxisScalePositionToValueCallFailed));
			}
 
			// Convert chart picture position to plotting position
			if( AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom )
				position = position - PlotAreaPosition.X;
			else 
				position = PlotAreaPosition.Bottom - position;
			
 
			// The Chart area size
			double ChartArea;
			if( AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom )
				ChartArea = PlotAreaPosition.Width;
			else 
				ChartArea = PlotAreaPosition.Height;
			
		
			// The Real range as double
			double viewMax = ViewMaximum;
			double viewMin = ViewMinimum;
			double	range = viewMax - viewMin;
 
			// Avoid division by zero
			double axisValue = 0;
			if( range != 0 )	
			{
				// Find axis value from position
				axisValue = range / ChartArea * position;
			}
			
			// Corrected axis value for reversed
			if( isReversed )
				axisValue = viewMax - axisValue;
			else
				axisValue = viewMin + axisValue;
					
			return axisValue;
		}
			
		/// <summary>
		/// This function converts a pixel position to an axis value.
		/// If an axis has a logarithmic scale, the value is converted to a linear scale.
		/// </summary>
		/// <param name="position">Pixel position.</param>
		/// <returns>Axis value.</returns>
		public double PixelPositionToValue( double position )
		{
			// Convert it to pixels
			double val = position;
			if( AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom )
			{
				val *= 100F / ((float)(this.Common.ChartPicture.Width - 1));
			}
			else 
			{
				val *= 100F / ((float)(this.Common.ChartPicture.Height - 1));
			}
 
			// Get from relative position
			return PositionToValue(val);
		}
 
		#endregion
 
		#region Axis scale methods
 
		/// <summary>
		/// Sets axis position. Axis position depends 
		/// on crossing and reversed value.
		/// </summary>
		internal void SetAxisPosition()
		{
			// Change position of the axis
			if( GetOppositeAxis().isReversed )
			{
				if( AxisPosition == AxisPosition.Left )
					AxisPosition = AxisPosition.Right;
				else if( AxisPosition == AxisPosition.Right ) 
					AxisPosition = AxisPosition.Left;
				else if( AxisPosition == AxisPosition.Top ) 
					AxisPosition = AxisPosition.Bottom;
				else if( AxisPosition == AxisPosition.Bottom ) 
					AxisPosition = AxisPosition.Top;
			}
		}
 
		/// <summary>
		/// Sets temporary offset value.
		/// </summary>
		internal void SetTempAxisOffset( )
		{
            if (ChartArea.Series.Count == 0)
            {
                return;
            }
			// Conditions when this code changes margin size: Column chart, 
			// margin is turned off, Interval offset is not used for 
			// gridlines, tick marks and labels.
			Series ser = ChartArea.GetFirstSeries();
			if( ( ser.ChartType == SeriesChartType.Column || 
				ser.ChartType == SeriesChartType.StackedColumn || 
				ser.ChartType == SeriesChartType.StackedColumn100 ||
				ser.ChartType == SeriesChartType.Bar ||
 
                ser.ChartType == SeriesChartType.RangeBar || 
				ser.ChartType == SeriesChartType.RangeColumn || 
 
				ser.ChartType == SeriesChartType.StackedBar || 
				ser.ChartType == SeriesChartType.StackedBar100 ) &&
                margin != 100.0 && !offsetTempSet &&
                this._autoMinimum)
			{
                
				// Find offset correction for Column chart margin.
				double offset;
				marginTemp = margin;
 
				// Find point width
				// Check if series provide custom value for point width
				double pointWidthSize;
				string strWidth = ser[CustomPropertyName.PointWidth];
				if(strWidth != null)
				{
					pointWidthSize = CommonElements.ParseDouble(strWidth);
				}
				else
				{
					pointWidthSize = 0.8;
				}
		
				margin = ( pointWidthSize / 2 ) * 100;
				offset = ( margin ) / 100;
			    double contraOffset = ( 100 - margin ) / 100;
                
                if (this._intervalsStore.Count == 0) 
                {
                    this._intervalsStore.Push(this.labelStyle.intervalOffset);
                    this._intervalsStore.Push(this.majorGrid.intervalOffset);
                    this._intervalsStore.Push(this.majorTickMark.intervalOffset);
                    this._intervalsStore.Push(this.minorGrid.intervalOffset);
                    this._intervalsStore.Push(this.minorTickMark.intervalOffset);
                }
 
				this.labelStyle.intervalOffset = Double.IsNaN(this.labelStyle.intervalOffset) ? offset : this.labelStyle.intervalOffset + offset;
				this.majorGrid.intervalOffset = Double.IsNaN(this.majorGrid.intervalOffset) ? offset : this.majorGrid.intervalOffset + offset;
				this.majorTickMark.intervalOffset = Double.IsNaN(this.majorTickMark.intervalOffset) ? offset : this.majorTickMark.intervalOffset + offset;
                this.minorGrid.intervalOffset = Double.IsNaN(this.minorGrid.intervalOffset) ? offset : this.minorGrid.intervalOffset + offset;
                this.minorTickMark.intervalOffset = Double.IsNaN(this.minorTickMark.intervalOffset) ? offset : this.minorTickMark.intervalOffset + offset;
 
				foreach( StripLine strip in ((Axis)(this)).StripLines )
				{
					_stripLineOffsets.Add( strip.IntervalOffset );
					strip.IntervalOffset -= contraOffset;
				}
				offsetTempSet = true;
			}
		}
 
		/// <summary>
		/// Resets temporary offset value.
		/// </summary>
		internal void ResetTempAxisOffset(  )
		{
			if( this.offsetTempSet )
			{
                System.Diagnostics.Debug.Assert(this._intervalsStore.Count == 5, "Fail in interval store count");
 
                this.minorTickMark.intervalOffset = this._intervalsStore.Pop();
                this.minorGrid.intervalOffset = this._intervalsStore.Pop();
                this.majorTickMark.intervalOffset = this._intervalsStore.Pop();
                this.majorGrid.intervalOffset = this._intervalsStore.Pop();
                this.labelStyle.intervalOffset = this._intervalsStore.Pop();
				int index = 0;
				foreach( StripLine strip in ((Axis)(this)).StripLines )
				{
					if( _stripLineOffsets.Count > index )
					{
						strip.IntervalOffset = (double)_stripLineOffsets[index];
					}
					index++;
				}
				_stripLineOffsets.Clear();
				offsetTempSet = false;
				margin = marginTemp;
			}
		}
 
		/// <summary>
		/// This function will create auto maximum and minimum values 
		/// using the interval. This function will make a gap between 
		/// data points and border of the chart area.
		/// </summary>
		/// <param name="inter">Interval</param>
		/// <param name="shouldStartFromZero">True if minimum scale value should start from zero.</param>
		/// <param name="autoMax">Maximum is auto</param>
		/// <param name="autoMin">Minimum is auto</param>
		/// <param name="min">Minimum value</param>
		/// <param name="max">Maximum value</param>
		/// <returns>Interval</returns>
		internal double RoundedValues( 
			double inter, 
			bool shouldStartFromZero,
			bool autoMax, 
			bool autoMin, 
			ref double min, 
			ref double max )
		{
			// For X Axes
			if( axisType == AxisName.X || axisType == AxisName.X2 )
			{
				if( margin == 0.0 && !this.roundedXValues )
				{
					return inter;
				}
			}
			else // For Y Axes
			{
				// Avoid dividing with 0. There is no gap.
				if( margin == 0.0 )
				{
					return inter;	
				}
			}
			
			if( autoMin )
			{ // Set minimum value
				if( min < 0.0 || ( !shouldStartFromZero && !ChartArea.stacked ) )
				{
					min = (double)( ((decimal)Math.Ceiling( min / inter ) - 1m ) * (decimal)inter );
				}
				else
				{
					min = 0.0;
				}
			}
			if( autoMax )
			{// Set maximum value
				if( max <= 0.0 && shouldStartFromZero )
				{
					max = 0.0;
				}
				else
				{
					max = (double)( ((decimal)Math.Floor( max / inter ) + 1m ) *  (decimal)inter );
				}
			}
			return inter;
		}
 
 
		/// <summary>
		/// Recalculates an intelligent interval from real interval.
		/// </summary>
		/// <param name="diff">Real interval.</param>
		/// <returns>Inteligent interval.</returns>
		internal double CalcInterval( double diff )
		{
			// If the interval is zero return error
			if( diff == 0.0 )
			{
                throw (new ArgumentOutOfRangeException("diff", SR.ExceptionAxisScaleIntervalIsZero));
			}
 
			// If the real interval is > 1.0
			double	step = -1;
			double	temp = diff; 
			while( temp > 1.0 )
			{
				step ++;
				temp = temp / 10.0;
				if( step > 1000 )
				{
                    throw (new InvalidOperationException(SR.ExceptionAxisScaleMinimumMaximumInvalid));
				}
			}
 
			
			// If the real interval is < 1.0
			temp = diff; 
			if( temp < 1.0 )
			{
				step = 0;
			}
 
			while( temp < 1.0 )
			{
				step --;
				temp = temp * 10.0;
				if( step < -1000 )
				{
                    throw (new InvalidOperationException(SR.ExceptionAxisScaleMinimumMaximumInvalid));
				}
			}
	
			double power = (this.IsLogarithmic) ? this.logarithmBase : 10.0;
			double tempDiff = diff / Math.Pow( power, step );
 
			if( tempDiff < 3 )
				tempDiff = 2;
			else if( tempDiff < 7 )
				tempDiff = 5;
			else
				tempDiff = 10;
 
			// Make a correction of the real interval
			return  tempDiff * Math.Pow( power, step );
		}
 
		/// <summary>
		/// Recalculates a intelligent interval from real interval 
		/// obtained from maximum and minimum values
		/// </summary>
		/// <param name="min">Minimum</param>
		/// <param name="max">Maximum</param>
		/// <returns>Auto Interval</returns>
		private double CalcInterval( double min, double max )
		{
			// Approximated interval value
			return CalcInterval( ( max - min ) / 5 );
		}
 
		
		/// <summary>
		/// Recalculates a intelligent interval from real interval 
		/// obtained from maximum, minimum and date type if 
		/// the values is date-time value.
		/// </summary>
		/// <param name="min">Minimum value.</param>
		/// <param name="max">Maximum value.</param>
		/// <param name="date">True if date.</param>
		/// <param name="type">Date time interval type.</param>
		/// <param name="valuesType">AxisName of date-time values.</param>
		/// <returns>Auto Interval.</returns>
		internal double CalcInterval( 
			double min, 
			double max, 
			bool date, 
			out DateTimeIntervalType type,
			ChartValueType valuesType)
		{
			// AxisName is date time
			if( date )
			{
				DateTime dateTimeMin = DateTime.FromOADate( min );
				DateTime dateTimeMax = DateTime.FromOADate( max );
				TimeSpan timeSpan = dateTimeMax.Subtract( dateTimeMin );
 
				// Minutes
				double	inter = timeSpan.TotalMinutes;
 
				// For Range less than 60 seconds interval is 5 sec
				if( inter <= 1.0 && valuesType != ChartValueType.Date)
				{
					// Milli Seconds
					double	mlSeconds = timeSpan.TotalMilliseconds;
					if(mlSeconds <= 10)
					{
						type = DateTimeIntervalType.Milliseconds;
						return 1;
					}
					if(mlSeconds <= 50)
					{
						type = DateTimeIntervalType.Milliseconds;
						return 4;
					}
					if(mlSeconds <= 200)
					{
						type = DateTimeIntervalType.Milliseconds;
						return 20;
					}
					if(mlSeconds <= 500)
					{
						type = DateTimeIntervalType.Milliseconds;
						return 50;
					}
					
					// Seconds
					double	seconds = timeSpan.TotalSeconds;
					
					if(seconds <= 7)
					{
						type = DateTimeIntervalType.Seconds;
						return 1;
					}
					else if(seconds <= 15)
					{
						type = DateTimeIntervalType.Seconds;
						return 2;
					}
					else if(seconds <= 30)
					{
						type = DateTimeIntervalType.Seconds;
						return 5;
					}
					else if(seconds <= 60)
					{
						type = DateTimeIntervalType.Seconds;
						return 10;
					}
 
				}// For Range less than 120 seconds interval is 10 sec
				else if( inter <= 2.0 && valuesType != ChartValueType.Date)
				{
					type = DateTimeIntervalType.Seconds;
					return 20;
				}// For Range less than 180 seconds interval is 30 sec
				else if( inter <= 3.0 && valuesType != ChartValueType.Date)
				{
					type = DateTimeIntervalType.Seconds;
					return 30;
				}
					
				// For Range less than 10 minutes interval is 1 min
				else if( inter <= 10 && valuesType != ChartValueType.Date) 
				{
					type = DateTimeIntervalType.Minutes;
					return 1;
				}
				// For Range less than 20 minutes interval is 1 min
				else if( inter <= 20 && valuesType != ChartValueType.Date) 
				{
					type = DateTimeIntervalType.Minutes;
					return 2;
				}// For Range less than 60 minutes interval is 5 min
				else if( inter <= 60 && valuesType != ChartValueType.Date)
				{
					type = DateTimeIntervalType.Minutes;
					return 5;
				}// For Range less than 120 minutes interval is 10 min
				else if( inter <= 120 && valuesType != ChartValueType.Date)
				{
					type = DateTimeIntervalType.Minutes;
					return 10;
				}// For Range less than 180 minutes interval is 30 min
				else if( inter <= 180 && valuesType != ChartValueType.Date)
				{
					type = DateTimeIntervalType.Minutes;
					return 30;
				}
					// For Range less than 12 hours interval is 1 hour
				else if( inter <= 60*12 && valuesType != ChartValueType.Date)
				{
					type = DateTimeIntervalType.Hours;
					return 1;
				}
					// For Range less than 24 hours interval is 4 hour
				else if( inter <= 60*24 && valuesType != ChartValueType.Date)
				{
					type = DateTimeIntervalType.Hours;
					return 4;
				}
					// For Range less than 2 days interval is 6 hour
				else if( inter <= 60*24*2 && valuesType != ChartValueType.Date)
				{
					type = DateTimeIntervalType.Hours;
					return 6;
				}
					// For Range less than 3 days interval is 12 hour
				else if( inter <= 60*24*3 && valuesType != ChartValueType.Date)
				{
					type = DateTimeIntervalType.Hours;
					return 12;
				}
 
				// For Range less than 10 days interval is 1 day
				else if( inter <= 60*24*10 )
				{
					type = DateTimeIntervalType.Days;
					return 1;
				}
					// For Range less than 20 days interval is 2 day
				else if( inter <= 60*24*20 )
				{
					type = DateTimeIntervalType.Days;
					return 2;
				}
					// For Range less than 30 days interval is 3 day
				else if( inter <= 60*24*30 )
				{
					type = DateTimeIntervalType.Days;
					return 3;
				}
					// For Range less than 2 months interval is 1 week
				else if( inter <= 60*24*30.5*2 )
				{
					type = DateTimeIntervalType.Weeks;
					return 1;
				}
					// For Range less than 5 months interval is 2weeks
				else if( inter <= 60*24*30.5*5 )
				{
					type = DateTimeIntervalType.Weeks;
					return 2;
				}
					// For Range less than 12 months interval is 1 month
				else if( inter <= 60*24*30.5*12 )
				{
					type = DateTimeIntervalType.Months;
					return 1;
				}
					// For Range less than 24 months interval is 3 month
				else if( inter <= 60*24*30.5*24 )
				{
					type = DateTimeIntervalType.Months;
					return 3;
				}
				// For Range less than 48 months interval is 6 months 
				else if( inter <= 60*24*30.5*48 )
				{
					type = DateTimeIntervalType.Months;
					return 6;
				}	
				// For Range more than 48 months interval is year 
				else if( inter >= 60*24*30.5*48 )
				{
					type = DateTimeIntervalType.Years;
					return CalcYearInterval( inter / 60 / 24 / 365 );
				}	
			}
 
			 // Else numbers
			type = DateTimeIntervalType.Number;
			return CalcInterval( min, max );
 
		}
				
		/// <summary>
		/// Recalculates a intelligent interval for years
		/// </summary>
		/// <param name="years">Number of years</param>
		/// <returns>Interval in years</returns>
		private double CalcYearInterval( double years )
		{
			// If the interval is zero return error
			if( years <= 1.0 )
			{
                throw (new ArgumentOutOfRangeException("years", SR.ExceptionAxisScaleIntervalIsLessThen1Year));
			}
 
			if( years < 5 )
				return 1;
			else if( years < 10 )
				return 2;
						
			// Make a correction of the interval
			return Math.Floor( years / 5 ); 
		}
 
		/// <summary>
		/// This method returns the number of units 
		/// between min and max.
		/// </summary>
		/// <param name="min">Minimum.</param>
		/// <param name="max">Maximum.</param>
		/// <param name="type">Date type.</param>
		/// <returns>Number of units.</returns>
		private int GetNumOfUnits( double min, double max, DateTimeIntervalType type )
		{
            double current = ChartHelper.GetIntervalSize(min, 1, type);
			return (int)Math.Round((max - min) / current);
		}
 
		/// <summary>
		/// This method checks if value type is date-time.
		/// </summary>
		/// <returns>Date-time type or Auto.</returns>
		internal ChartValueType GetDateTimeType()
		{
			List<string> list = null;
 
			ChartValueType dateType = ChartValueType.Auto;
		
			// Check if Value type is date from first series in the axis
			if( axisType == AxisName.X )
			{ 
				// Check X axes type
				list = ChartArea.GetXAxesSeries( AxisType.Primary, ((Axis)this).SubAxisName );
				if( list.Count == 0 )
				{
					return ChartValueType.Auto;
				}
 
				if( Common.DataManager.Series[list[0]].IsXValueDateTime() )
				{
					dateType = Common.DataManager.Series[list[0]].XValueType;
				}
			}
			else if( axisType == AxisName.X2 )
			{ 
				// Check X2 axes type
				list = ChartArea.GetXAxesSeries( AxisType.Secondary, ((Axis)this).SubAxisName );
				if( list.Count == 0 )
				{
					return ChartValueType.Auto;
				}
 
				if( Common.DataManager.Series[list[0]].IsXValueDateTime() )
				{
					dateType = Common.DataManager.Series[list[0]].XValueType;
				}
			}
			else if( axisType == AxisName.Y )
			{ 
				// Check Y axes type
				list = ChartArea.GetYAxesSeries( AxisType.Primary, ((Axis)this).SubAxisName );
				if( list.Count == 0 )
				{
					return ChartValueType.Auto;
				}
 
				if( Common.DataManager.Series[list[0]].IsYValueDateTime() )
				{
					dateType = Common.DataManager.Series[list[0]].YValueType;
				}
			}
			else if( axisType == AxisName.Y2 )
			{ 
				// Check Y2 axes type
				list = ChartArea.GetYAxesSeries( AxisType.Secondary, ((Axis)this).SubAxisName );
				if( list.Count == 0 )
				{
					return ChartValueType.Auto;
				}
 
				if( Common.DataManager.Series[list[0]].IsYValueDateTime() )
				{
					dateType = Common.DataManager.Series[list[0]].YValueType;
				}
			}
 
			return dateType;
		}
 
		/// <summary>
		/// This method removes "Auto", "min", "max" from crossing
		/// value and creates a double value.
		/// </summary>
		/// <returns>Crossing value</returns>
		private double GetCrossing()
		{
			if( Double.IsNaN(crossing) )
			{
				if( Common.ChartTypeRegistry.GetChartType( (string)ChartArea.ChartTypes[0] ).ZeroCrossing )
				{
					if( ViewMinimum > 0.0 )
					{
						return ViewMinimum;
					}
					else if( ViewMaximum < 0.0 )
					{
						return ViewMaximum;
					}
					else
					{
						return 0.0;
					}
				}
				else
				{
					return ViewMinimum;
				}
			}
			else if( crossing == Double.MaxValue )
			{
				return ViewMaximum;
			}
			else if(  crossing == Double.MinValue )
			{
				return ViewMinimum;
			}
 
			return crossing;
		}
 
		/// <summary>
		/// Set auto minimum number. The minimum number 
		/// which was sent to this function will be used to 
		/// estimate a rounded minimum.
		/// </summary>
		/// <param name="min"> This value is a recommendation for the minimum value. </param>
		internal void SetAutoMinimum(double min)
		{
			// Set the minimum
			if( _autoMinimum )
			{
				minimum = min;
			}
		}
		
		/// <summary>
		/// Set auto maximum number. The maximum number 
		/// which was sent to this function will be used to 
		/// estimate a rounded maximum.
		/// </summary>
		/// <param name="max">This value is a recommendation for the maximum value.</param>
		internal void SetAutoMaximum(double max)
		{
			// Set the maximum
			if( _autoMaximum )
			{
				maximum = max;
			}
		}
 
		/// <summary>
		/// Find opposite axis of this axis.  What is opposite 
		/// axis depend on first series in chart area and primary 
		/// and secondary X and Y axes for the first series.
		/// </summary>
		/// <returns>Opposite axis</returns>
		internal Axis GetOppositeAxis()
		{
			// Oppoiste axis found
            if (oppositeAxis != null) 
			{
                return oppositeAxis;
			}
 
			List<string> list;
 
			switch( axisType )
			{
				// X Axis
				case AxisName.X:
					list = ChartArea.GetXAxesSeries( AxisType.Primary, ((Axis)this).SubAxisName ); 
					// There aren't data series
					if( list.Count == 0 )
                        oppositeAxis = ChartArea.AxisY;
						// Take opposite axis from the first series from chart area
					else if( Common.DataManager.Series[list[0]].YAxisType == AxisType.Primary )
                        oppositeAxis = ChartArea.AxisY.GetSubAxis(Common.DataManager.Series[list[0]].YSubAxisName);
					else
                        oppositeAxis = ChartArea.AxisY2.GetSubAxis(Common.DataManager.Series[list[0]].YSubAxisName);
					break;
					// X2 Axis
				case AxisName.X2:
					list = ChartArea.GetXAxesSeries( AxisType.Secondary, ((Axis)this).SubAxisName ); 
					// There aren't data series
					if( list.Count == 0 )
                        oppositeAxis = ChartArea.AxisY2;
						// Take opposite axis from the first series from chart area
					else if( Common.DataManager.Series[list[0]].YAxisType == AxisType.Primary)
                        oppositeAxis = ChartArea.AxisY.GetSubAxis(Common.DataManager.Series[list[0]].YSubAxisName);
					else
                        oppositeAxis = ChartArea.AxisY2.GetSubAxis(Common.DataManager.Series[list[0]].YSubAxisName);
					break;
					// Y Axis
				case AxisName.Y:
					list = ChartArea.GetYAxesSeries( AxisType.Primary, ((Axis)this).SubAxisName ); 
					// There aren't data series
					if( list.Count == 0 )
                        oppositeAxis = ChartArea.AxisX;
						// Take opposite axis from the first series from chart area
					else if( Common.DataManager.Series[list[0]].XAxisType == AxisType.Primary )
                        oppositeAxis = ChartArea.AxisX.GetSubAxis(Common.DataManager.Series[list[0]].XSubAxisName);
					else
                        oppositeAxis = ChartArea.AxisX2.GetSubAxis(Common.DataManager.Series[list[0]].XSubAxisName);
					break;
					// Y2 Axis
				case AxisName.Y2:
					list = ChartArea.GetYAxesSeries( AxisType.Secondary, ((Axis)this).SubAxisName ); 
					// There aren't data series
					if( list.Count == 0 )
                        oppositeAxis = ChartArea.AxisX2;
						// Take opposite axis from the first series from chart area
					else if( Common.DataManager.Series[list[0]].XAxisType == AxisType.Primary  )
                        oppositeAxis = ChartArea.AxisX.GetSubAxis(Common.DataManager.Series[list[0]].XSubAxisName);
					else
                        oppositeAxis = ChartArea.AxisX2.GetSubAxis(Common.DataManager.Series[list[0]].XSubAxisName);
					break;
			}
            return oppositeAxis;
		}
 
		/// <summary>
		/// This function converts Values from Axes to 
		/// linear relative positions.
		/// </summary>
		/// <param name="axisValue">Value from axis.</param>
		/// <returns>Relative position.</returns>
		internal double GetLinearPosition( double axisValue )
		{
			bool circularArea = (ChartArea == null || !ChartArea.chartAreaIsCurcular) ? 
				false : true;
 
			// Check if some value calculation is optimized
			if(!this.optimizedGetPosition)
			{
				paintViewMax = ViewMaximum;
				paintViewMin = ViewMinimum;
				paintRange = paintViewMax - paintViewMin;
				paintAreaPosition = PlotAreaPosition.ToRectangleF();
 
				// Update position for circular chart area
				if(circularArea)
				{
					paintAreaPosition.Width /= 2.0f;
					paintAreaPosition.Height /= 2.0f;
				}
 
				paintAreaPositionBottom = paintAreaPosition.Y + paintAreaPosition.Height;
				paintAreaPositionRight = paintAreaPosition.X + paintAreaPosition.Width;
 
				// The Chart area size
				if( AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom )
					paintChartAreaSize = paintAreaPosition.Width;
				else 
					paintChartAreaSize = paintAreaPosition.Height;
 
				valueMultiplier = 0.0;
				if( paintRange != 0 )
				{
					valueMultiplier = paintChartAreaSize / paintRange;
				}
			}
 
			// The Chart area pixel size
			double position = valueMultiplier * ( axisValue - paintViewMin);
 
 
 
			// Check if axis scale segments are enabled
			if(this.scaleSegmentsUsed)
			{
				AxisScaleSegment scaleSegment = this.ScaleSegments.FindScaleSegmentForAxisValue(axisValue);
				if(scaleSegment != null)
				{
					double segmentSize = 0.0;
					double segmentPosition = 0.0;
					scaleSegment.GetScalePositionAndSize(paintChartAreaSize, out segmentPosition, out segmentSize);
 
					// Make sure value do not exceed max possible
					if(!this.ScaleSegments.AllowOutOfScaleValues)
					{
						if(axisValue > scaleSegment.ScaleMaximum)
						{
							axisValue = scaleSegment.ScaleMaximum;
						}
						else if(axisValue < scaleSegment.ScaleMinimum)
						{
							axisValue = scaleSegment.ScaleMinimum;
						}
					}
 
					double segmentScaleRange = scaleSegment.ScaleMaximum - scaleSegment.ScaleMinimum;
 
					position = (segmentSize / segmentScaleRange) * (axisValue - scaleSegment.ScaleMinimum);
					position += segmentPosition;
				}
			}
 
 
			// Window position 
			// (Do Not use .Right or .Bottom methods below) - rounding issue!
			if( isReversed )
			{
				if( AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom )
					position = paintAreaPositionRight - position;
				else 
					position = paintAreaPosition.Y + position;
			}
			else
			{
				if( AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom )
					position = paintAreaPosition.X + position;
				else 
					position = paintAreaPositionBottom - position;
			}
 
			return position;
		}
 
 
		#endregion
 
		#region Axis estimate axis methods
 
		/// <summary>
		/// This function recalculates minimum maximum and interval. 
		/// The function uses current values for minimum and maximum to 
		/// find rounding values. If the value from the data source for the 
		/// maximum value is 376.5 this function will return 380. This function 
		/// also set interval type for date
		/// </summary>
		internal void EstimateAxis()
		{
			double axisInterval;
 
			// Check if veiw size specified without scaleView position
			if(!Double.IsNaN(this.ScaleView.Size))
			{
				// If size set only use axis minimum for scaleView position
				if(Double.IsNaN(this.ScaleView.Position))
				{
					this.ScaleView.Position = this.Minimum;
				}
			}
 
			// Zooming Mode
			if( !Double.IsNaN(_scaleView.Position) && !Double.IsNaN(_scaleView.Size) )
			{ 
				double viewMaximum = ViewMaximum;
				double viewMinimum = ViewMinimum;
 
				// IsLogarithmic axes
                if (this._isLogarithmic)
				{
					viewMaximum = Math.Pow( this.logarithmBase, viewMaximum );
					viewMinimum = Math.Pow( this.logarithmBase, viewMinimum );
				}
				else
				{
					// Add rounding and gap for maximum and minimum
					EstimateAxis( ref this.minimum, ref this.maximum, _autoMaximum, _autoMinimum );
				}
 
				// Find Interval for Zoom
				axisInterval = EstimateAxis( ref viewMinimum, ref viewMaximum, true, true );
			}
			else // No Zooming mode
			{ 
				// Estimate axis shoud be always called for non logarithmic axis
				axisInterval = EstimateAxis( ref this.minimum, ref this.maximum, _autoMaximum, _autoMinimum );
			}
 
			// Set intervals for grids, tick marks and labels
			if( axisInterval <= 0.0 )
			{
                throw (new InvalidOperationException(SR.ExceptionAxisScaleAutoIntervalInvalid));
			}
			else
			{
				// This code checks if all series in the chart area have “integer type” 
                // for specified axes, which means int, uint, long and ulong and rounds interval.
#if SUBAXES
				if( ChartArea.SeriesIntegerType( this.axisType, ((Axis)this).SubAxisName ) )
#else // SUBAXES
                if ( ChartArea.SeriesIntegerType( this.axisType, string.Empty ))
#endif // SUBAXES
                {
					axisInterval = Math.Round( axisInterval );
					if( axisInterval == 0.0 )
					{
						axisInterval = 1.0;
					}
 
					// Round Minimum to floor value if type is integer
					minimum = Math.Floor( minimum );
				}
 
				SetInterval = axisInterval;
			}	
		}
				
		/// <summary>
		/// This function recalculates minimum maximum and interval. 
		/// The function uses current values for minimum and maximum to 
		/// find rounding values. If the value from the data source for the 
		/// maximum value is 376.5 this function will return 380. This function 
		/// also set interval type for date
		/// </summary>
		/// <param name="minimumValue">Minimum</param>
		/// <param name="maximumValue">Maximum</param>
		/// <param name="autoMaximum">Maximum value is Auto</param>
		/// <param name="autoMinimum">Minimum value is Auto</param>
		/// <returns>Interval</returns>
		internal double EstimateAxis( ref double minimumValue, ref double maximumValue, bool autoMaximum, bool autoMinimum )
		{
			double axisInterval;
 
			// The axis minimum value is greater than the maximum value.
			if( maximumValue < minimumValue )
			{
				if(!this.Common.ChartPicture.SuppressExceptions)
				{
                    throw (new InvalidOperationException(SR.ExceptionAxisScaleMinimumValueIsGreaterThenMaximumDataPoint));
				}
				else
				{
					// Max axis scale should be always bigger
					double tempValue = maximumValue;
					maximumValue = minimumValue;
					minimumValue = tempValue;
				}
			}
 
			// Take Value type
			ChartValueType	dateType = GetDateTimeType();
						
			// Axis type is logarithmic
            if (_isLogarithmic)
			{
				axisInterval = EstimateLogarithmicAxis( ref minimumValue, ref maximumValue, crossing, autoMaximum, autoMinimum );
			}
			
			// Axis type is date
			else if( dateType !=  ChartValueType.Auto)
			{
				axisInterval = EstimateDateAxis( ref minimumValue, ref maximumValue, autoMaximum, autoMinimum, dateType );
			}
			
			// Axis type is number
			else 
			{
				axisInterval = EstimateNumberAxis( ref minimumValue, ref maximumValue, this.IsStartedFromZero, this.prefferedNumberofIntervals, autoMaximum, autoMinimum );
			}
 
			// Set intervals for grids, tick marks and labels
			if( axisInterval <= 0.0 )
			{
                throw (new InvalidOperationException(SR.ExceptionAxisScaleAutoIntervalInvalid));
			}
			else
			{
				// Set interval for Grid lines Tick Marks and labels
				SetInterval = axisInterval;
			}	
 
			return axisInterval;
 
		}
 
        /// <summary>
        /// This function recalculates minimum maximum and interval for 
        /// logarithmic axis. The function uses current values for minimum and 
        /// maximum to find new rounding values.
        /// </summary>
        /// <param name="minimumValue">Current Minimum value</param>
        /// <param name="maximumValue">Current Maximum value</param>
        /// <param name="crossingValue">Crossing value</param>
        /// <param name="autoMaximum">Maximum value is Auto</param>
        /// <param name="autoMinimum">Minimum value is Auto</param>
        /// <returns>Interval</returns>
		private double EstimateLogarithmicAxis( ref double minimumValue, ref double maximumValue, double crossingValue, bool autoMaximum, bool autoMinimum )
		{
			double axisInterval;
 
			if( !logarithmicConvertedToLinear )
			{
				// Remember values. Do not use POW function because of rounding.
				this.logarithmicMinimum = this.minimum;
				this.logarithmicMaximum = this.maximum;
			}
 
			// For log axis margin always turn on.
			margin = 100;
 
			// Supress zero and negative values with logarithmic axis exceptions
			if(this.Common != null && this.Common.Chart != null && this.Common.Chart.chartPicture.SuppressExceptions)
			{
				if (minimumValue <= 0.0 )
				{
					minimumValue = 1.0;
				}
				if (maximumValue <= 0.0 )
				{
					maximumValue = 1.0;
				}
				if (crossingValue <= 0.0 && crossingValue != Double.MinValue )
				{
					crossingValue = 1.0;
				}
			}
 
			// The logarithmic axes can not show negative values.
			if( minimumValue <= 0.0 || maximumValue <= 0.0 || crossingValue <= 0.0 )
			{
				if (minimumValue <= 0.0 )
                    throw (new ArgumentOutOfRangeException("minimumValue", SR.ExceptionAxisScaleLogarithmicNegativeValues));
				if (maximumValue <= 0.0 )
                    throw (new ArgumentOutOfRangeException("maximumValue", SR.ExceptionAxisScaleLogarithmicNegativeValues));
			}
 
			// Change crossing to linear scale
			crossingValue = Math.Log( crossingValue, this.logarithmBase );
 
			// Change minimum and maximum to linear scale
			minimumValue = Math.Log( minimumValue, this.logarithmBase );
			maximumValue = Math.Log( maximumValue, this.logarithmBase );
 
			logarithmicConvertedToLinear = true;
			
			// Find interval - Make approximately 5 grid lines and labels.
			double diff = ( maximumValue - minimumValue ) / 5;
 
			// Make good interval for logarithmic scale
			axisInterval = Math.Floor( diff );
			if( axisInterval == 0 ) axisInterval = 1;
 
			if( autoMinimum && autoMaximum )
			{
				// The maximum and minimum rounding with interval
				RoundedValues( axisInterval, this.IsStartedFromZero, autoMaximum, autoMinimum, ref minimumValue, ref maximumValue );
			}
			
			// Do not allow min/max values more than a hundred
			if(ChartArea.hundredPercent)
			{
				if(autoMinimum)
				{
					if(minimumValue < 0)	
						minimumValue = 0;
				}
 
				if(autoMaximum)	
				{
					if(maximumValue > 2)	
						maximumValue = 2;
				}
			}
 
			// Set interval for Grid lines Tick Marks and labels
			return axisInterval;
		}
 
		/// <summary>
		/// This function recalculates minimum maximum and interval for 
		/// Date axis. The function uses current values for minimum and 
		/// maximum to find new rounding values.
		/// </summary>
		/// <param name="minimumValue">Current Minimum value</param>
		/// <param name="maximumValue">Current Maximum value</param>
		/// <param name="autoMaximum">Maximum value is Auto</param>
		/// <param name="autoMinimum">Minimum value is Auto</param>
		/// <param name="valuesType">AxisName of date-time values.</param>
		/// <returns>Interval</returns>
		private double EstimateDateAxis( 
			ref double minimumValue, 
			ref double maximumValue, 
			bool autoMaximum, 
			bool autoMinimum,
			ChartValueType valuesType)
		{
			double axisInterval;
 
			double min = minimumValue;
			double max = maximumValue;
 
			// Find interval for this date type
            axisInterval = CalcInterval(min, max, true, out _internalIntervalType, valuesType);
 
 
			// For 3D Charts interval could be changed. After rotation 
			// projection of axis could be very small.
			if( !double.IsNaN( this.interval3DCorrection ) && 
				ChartArea.Area3DStyle.Enable3D && 
				!ChartArea.chartAreaIsCurcular)
			{
				axisInterval = Math.Floor( axisInterval / this.interval3DCorrection );
 
				this.interval3DCorrection = double.NaN;
			}
 
			// Find number of units between minimum and maximum
			int numberOfUnits = GetNumOfUnits( min, max, _internalIntervalType );
 
			// Make a gap between max point and axis for Y axes
			if( axisType == AxisName.Y || axisType == AxisName.Y2 ) 
			{
                if (autoMinimum && minimumValue > ChartHelper.GetIntervalSize(min, axisInterval, _internalIntervalType))
				{
					// Add gap to the minimum value from the series
					// equal half of the interval
                    minimumValue += ChartHelper.GetIntervalSize( 
						min, 
						- (axisInterval / 2.0) * margin / 100,
                        _internalIntervalType,
						null,
						0.0,
						DateTimeIntervalType.Number,
						false,
						false);
 
					// Align minimum sacale value on the interval
					minimumValue = ChartHelper.AlignIntervalStart(
						minimumValue, 
						axisInterval * margin / 100,
                        _internalIntervalType);
				}
 
				// Increase maximum if not zero. Make a space between chart type 
				// and the end of the chart area.
				if( autoMaximum && max > 0 && margin != 0.0 )
				{
					maximumValue = minimumValue + ChartHelper.GetIntervalSize( 
						minimumValue, 
						(double)((Math.Floor(numberOfUnits / axisInterval  / margin * 100)+2) * axisInterval  * margin / 100),
                        _internalIntervalType);
				}
			}
 
            InternalIntervalType = _internalIntervalType;
 
			// Set interval for Grid lines Tick Marks and labels
			return axisInterval;
		}
		
		/// <summary>
		/// This function recalculates minimum maximum and interval for 
		/// number type axis. The function uses current values for minimum and 
		/// maximum to find new rounding values.
		/// </summary>
		/// <param name="minimumValue">Current Minimum value</param>
		/// <param name="maximumValue">Current Maximum value</param>
		/// <param name="shouldStartFromZero">Should start from zero flag.</param>
		/// <param name="preferredNumberOfIntervals">Preferred number of intervals. Can be set to zero for dynamic mode.</param>
		/// <param name="autoMaximum">Maximum value is Auto</param>
		/// <param name="autoMinimum">Minimum value is Auto</param>
		/// <returns>Interval</returns>
		internal double EstimateNumberAxis( 
			ref double minimumValue, 
			ref double maximumValue, 
			bool shouldStartFromZero, 
			int preferredNumberOfIntervals,
			bool autoMaximum, 
			bool autoMinimum )
		{
			double axisInterval;
			double min = minimumValue;
			double max = maximumValue;
			double diff;
 
			if( !roundedXValues && ( axisType == AxisName.X || axisType == AxisName.X2 ) )
			{
				diff = ChartArea.GetPointsInterval( false, 10 );
				if( diff == 0 || ( max - min ) / diff > 20 )
				{
					diff = ( max - min ) / preferredNumberOfIntervals;
				}
					
			}
			else
			{
				diff = ( max - min ) / preferredNumberOfIntervals;
			}
 
            // For 3D Charts interval could be changed. After rotation 
			// projection of axis could be very small.
			if( !double.IsNaN( this.interval3DCorrection ) && 
				ChartArea.Area3DStyle.Enable3D &&
				!ChartArea.chartAreaIsCurcular)
			{
				diff = diff / this.interval3DCorrection;
 
				// Do not change minimum and maximum with 3D correction.
				if( max - min < diff )
				{
					diff = max - min;
				}
 
				this.interval3DCorrection = double.NaN;
 
				if( diff != 0.0 )
				{
					diff = CalcInterval( diff );
				}
			}
 
				
			if( autoMaximum || autoMinimum ) 
			{
				if( diff == 0 )
				{
					// Can not find interval. Minimum and maximum are same
 
					max = min + 1;
					diff = 0.2;
					axisInterval = 0.2;
				}
				else
				{	
					axisInterval = CalcInterval( diff );
				}
			}
			else 
			{
				axisInterval = diff;
			}
 
			// Case when minimum or maximum is set and interval is > maximum. 
			// Reasons overflow exception.
			if( ((Axis)this).interval != 0 && ((Axis)this).interval > axisInterval && minimumValue + ((Axis)this).interval > maximumValue )
			{
				axisInterval = ((Axis)this).interval;
				if( autoMaximum )
				{
					maximumValue = minimumValue + axisInterval;
				}
 
				if( autoMinimum )
				{
					minimumValue = maximumValue - axisInterval;
				}
			}
 
			// The maximum and minimum rounding for Y Axes
			if( axisType == AxisName.Y || axisType == AxisName.Y2 || ( roundedXValues && ( axisType == AxisName.X || axisType == AxisName.X2 )))
			{
				// Start from zero for the 100% chart types
				bool minIsZero = false;
				bool maxIsZero = false;
				if(ChartArea.hundredPercent)
				{
					minIsZero = (minimumValue == 0.0);
					maxIsZero = (maximumValue == 0.0);
				}
 
				// Round min/max values
				RoundedValues( axisInterval, shouldStartFromZero, autoMaximum, autoMinimum, ref minimumValue, ref maximumValue );
 
				// Do not allow min/max values more than a hundred
				if(ChartArea.hundredPercent)
				{
					if(autoMinimum)
					{
						if(minimumValue < -100)	
							minimumValue = -100;
						if(minIsZero)
							minimumValue = 0;
					}
 
					if(autoMaximum)	
					{
						if(maximumValue > 100)	
							maximumValue = 100;
						if(maxIsZero)
							maximumValue = 0;
					}
				}
			}
 
			// Set interval for Grid lines Tick Marks and labels
			return axisInterval;
		}
 
		#endregion
	}
}