File: Common\ChartTypes\PointChart.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:		PointChart.cs
//
//  Namespace:	DataVisualization.Charting.ChartTypes
//
//	Classes:	PointChart
//
//  Purpose:	Provides 2D/3D drawing and hit testing functionality 
//              for the Point chart.
//
//	Reviewed:	AG - Aug 6, 2002
//              AG - Microsoft 6, 2007
//
//===================================================================
 
#region Used namespaces
 
using System;
using System.Resources;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Globalization;
 
#if Microsoft_CONTROL
	using System.Windows.Forms.DataVisualization.Charting;
	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;
#else
    using System.Web.UI.DataVisualization.Charting;
	using System.Web.UI.DataVisualization.Charting.ChartTypes;
	using System.Web.UI.DataVisualization.Charting.Data;
	using System.Web.UI.DataVisualization.Charting.Utilities;
#endif
 
#endregion
 
#if Microsoft_CONTROL
	namespace System.Windows.Forms.DataVisualization.Charting.ChartTypes
#else
	namespace System.Web.UI.DataVisualization.Charting.ChartTypes
#endif
{
	/// <summary>
    /// PointChart class provides 2D/3D drawing and hit testing 
    /// functionality for the Point chart.
	/// </summary>
    internal class PointChart : IChartType
	{
		#region Fields
 
		/// <summary>
		/// Indicates that markers will be always drawn
		/// </summary>
		internal bool	alwaysDrawMarkers = true;
 
		/// <summary>
		/// Index of the Y value used to draw chart
		/// </summary>
        internal int YValueIndex { get; set; }
 
		/// <summary>
		/// Index of the Y value used to be shown as point value label
		/// </summary>
        internal int labelYValueIndex = -1;
 
		/// <summary>
		/// Auto label position flag
		/// </summary>
        internal bool autoLabelPosition = true;
 
		/// <summary>
		/// Label position
		/// </summary>
        internal LabelAlignmentStyles labelPosition = LabelAlignmentStyles.Top;
 
		/// <summary>
		/// Vertical axes
		/// </summary>
        internal Axis VAxis { get; set; }
 
		/// <summary>
		/// Horizontal axes
		/// </summary>
        internal Axis HAxis { get; set; }
 
		/// <summary>
		/// Indexed series flag
		/// </summary>
        internal bool indexedSeries = false;
 
		/// <summary>
		/// Common elements object
		/// </summary>
        internal CommonElements Common { get; set; }
 
		/// <summary>
		/// Chart area object
		/// </summary>
        internal ChartArea Area { get; set; }
 
		/// <summary>
		/// Indicates that marker and label are drawn in the middle of 3D depth
		/// </summary>
        internal bool middleMarker = true;
 
		/// <summary>
		/// Stores information about 3D labels. Used to draw 3D labels in layers.
		/// </summary>
		internal ArrayList label3DInfoList = null;
 
		#endregion
 
		#region Constructors
 
		/// <summary>
		/// Class public constructor.
		/// </summary>
		public PointChart()
		{
		}
 
		/// <summary>
		/// Class public constructor.
		/// </summary>
		/// <param name="alwaysDrawMarkers">Indicates if markers should be always painted.</param>
		public PointChart(bool alwaysDrawMarkers)
		{
			this.alwaysDrawMarkers = alwaysDrawMarkers;
		}
 
		#endregion
 
		#region IChartType interface implementation
 
		/// <summary>
		/// Chart type name
		/// </summary>
		virtual public string Name			{ get{ return ChartTypeNames.Point;}}
 
		/// <summary>
		/// True if chart type is stacked
		/// </summary>
		virtual public bool Stacked		{ get{ return false;}}
 
 
		/// <summary>
		/// True if stacked chart type supports groups
		/// </summary>
		virtual public bool SupportStackedGroups	{ get { return false; } }
 
 
		/// <summary>
		/// True if stacked chart type should draw separately positive and 
		/// negative data points ( Bar and column Stacked types ).
		/// </summary>
		public bool StackSign		{ get{ return false;}}
 
		/// <summary>
		/// True if chart type supports axeses
		/// </summary>
		virtual public bool RequireAxes	{ get{ return true;} }
 
		/// <summary>
		/// Chart type with two y values used for scale ( bubble chart type )
		/// </summary>
		virtual public bool SecondYScale{ get{ return false;} }
 
		/// <summary>
		/// True if chart type requires circular chart area.
		/// </summary>
		public bool CircularChartArea	{ get{ return false;} }
 
		/// <summary>
		/// True if chart type supports logarithmic axes
		/// </summary>
		virtual public bool SupportLogarithmicAxes	{ get{ return true;} }
 
		/// <summary>
		/// True if chart type requires to switch the value (Y) axes position
		/// </summary>
		virtual public bool SwitchValueAxes	{ get{ return false;} }
 
		/// <summary>
		/// True if chart series can be placed side-by-side.
		/// </summary>
		virtual public bool SideBySideSeries { get{ return false;} }
 
		/// <summary>
		/// True if each data point of a chart must be represented in the legend
		/// </summary>
		virtual public bool DataPointsInLegend	{ get{ return false;} }
 
		/// <summary>
		/// If the crossing value is auto Crossing value should be 
		/// automatically set to zero for some chart 
		/// types (Bar, column, area etc.)
		/// </summary>
		virtual public bool ZeroCrossing { get{ return false;} }
 
		/// <summary>
		/// True if palette colors should be applied for each data paoint.
		/// Otherwise the color is applied to the series.
		/// </summary>
		virtual public bool ApplyPaletteColorsToPoints	{ get { return false; } }
 
		/// <summary>
		/// Indicates that extra Y values are connected to the scale of the Y axis
		/// </summary>
		virtual public bool ExtraYValuesConnectedToYAxis{ get { return false; } }
		
		/// <summary>
		/// Indicates that it's a hundredred percent chart.
		/// Axis scale from 0 to 100 percent should be used.
		/// </summary>
		virtual public bool HundredPercent{ get{return false;} }
 
		/// <summary>
		/// Indicates that it's a hundredred percent chart.
		/// Axis scale from 0 to 100 percent should be used.
		/// </summary>
		virtual public bool HundredPercentSupportNegative{ get{return false;} }
 
		/// <summary>
		/// How to draw series/points in legend:
		/// Filled rectangle, Line or Marker
		/// </summary>
		/// <param name="series">Legend item series.</param>
		/// <returns>Legend item style.</returns>
		virtual public LegendImageStyle GetLegendImageStyle(Series series)
		{
			return LegendImageStyle.Marker;
		}
 
		/// <summary>
		/// Number of supported Y value(s) per point 
		/// </summary>
		virtual public int YValuesPerPoint	{ get { return 1; } }
 
		/// <summary>
		/// Gets chart type image.
		/// </summary>
		/// <param name="registry">Chart types registry object.</param>
		/// <returns>Chart type image.</returns>
        virtual public System.Drawing.Image GetImage(ChartTypeRegistry registry)
		{
			return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
		}
 
        #endregion
 
		#region Painting and Selection
 
		/// <summary>
		/// Paint Point Chart.
		/// </summary>
		/// <param name="graph">The Chart Graphics object.</param>
		/// <param name="common">The Common elements object.</param>
		/// <param name="area">Chart area for this chart.</param>
		/// <param name="seriesToDraw">Chart series to draw.</param>
		virtual public void Paint( ChartGraphics graph, CommonElements common, ChartArea area, Series seriesToDraw )
		{	
			this.Common = common;
			this.Area = area;
			ProcessChartType( false, graph, common, area, seriesToDraw);
		}
 
		/// <summary>
		/// This method recalculates size of the bars. This method is used 
		/// from Paint or Select method.
		/// </summary>
		/// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
		/// <param name="graph">The Chart Graphics object.</param>
		/// <param name="common">The Common elements object.</param>
		/// <param name="area">Chart area for this chart.</param>
		/// <param name="seriesToDraw">Chart series to draw.</param>
		virtual protected void ProcessChartType( 
			bool selection, 
			ChartGraphics graph, 
			CommonElements common, 
			ChartArea area, 
			Series seriesToDraw )
		{
            
            this.Common = common;
			// Prosess 3D chart type
			if(area.Area3DStyle.Enable3D)
			{
				ProcessChartType3D( selection, graph, common, area, seriesToDraw );
				return;
			}
 
			// Check if series is indexed
			if( ShiftedSerName.Length == 0)
			{
                indexedSeries = ChartHelper.IndexedSeries(this.Common, area.GetSeriesFromChartType(this.Name).ToArray());
			}
			else
			{
				indexedSeries = ChartHelper.IndexedSeries( common.DataManager.Series[ShiftedSerName] );
			}
 
			//************************************************************
			//** Loop through all series
			//************************************************************
			foreach( Series ser in common.DataManager.Series )
			{
				// Labels and markers have to be shifted if there 
				// is more than one series for column chart. This property 
				// will give a name of the series, which is used, for 
				// labels and markers.
				bool	breakSeriesLoop = false;
				if( ShiftedSerName.Length > 0)
				{
					if( ShiftedSerName != ser.Name )
					{
						continue;
					}
					breakSeriesLoop = true;
				}
 
				// Process only point chart series in this chart area
				if( String.Compare( ser.ChartTypeName, this.Name, true, System.Globalization.CultureInfo.CurrentCulture ) != 0 
					|| ser.ChartArea != area.Name || !ser.IsVisible())
				{
					continue;
				}
 
                // Check if only 1 specified series must be processed
                if (seriesToDraw != null && seriesToDraw.Name != ser.Name)
                {
                    continue;
                }
 
				//************************************************************
				//** Set active horizontal/vertical axis
				//************************************************************
				HAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
				VAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
				double horizontalViewMax = HAxis.ViewMaximum;
				double horizontalViewMin = HAxis.ViewMinimum;
				double verticalViewMax = VAxis.ViewMaximum;
				double verticalViewMin = VAxis.ViewMinimum;
 
				//************************************************************
				//** Call Back Paint event
				//************************************************************
				if( !selection )
				{
                    common.Chart.CallOnPrePaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
				}
 
				//************************************************************
				//** Loop through all data points in the series
				//************************************************************
				int	markerIndex = 0;		// Marker index
				int	index = 1;				// Data points loop
				foreach( DataPoint point in ser.Points )
				{
					// Reset pre-calculated point position
					point.positionRel = new PointF(float.NaN, float.NaN);
 
					//************************************************************
					//** Check if point values are in the chart area
					//************************************************************
 
					// Check for min/max X values
					double xValue = (indexedSeries) ? (double)index : point.XValue;
					xValue = HAxis.GetLogValue(xValue);
					if(xValue > horizontalViewMax || xValue < horizontalViewMin)
					{
						index++;
						continue;
					}
 
					// Check for min/max Y values
					double	yValue = GetYValue(common, area, ser, point, index - 1, YValueIndex);
 
					// Axis is logarithmic
					yValue = VAxis.GetLogValue( yValue );
					
					if( yValue > verticalViewMax || yValue < verticalViewMin)
					{
						index++;
						continue;
					}
 
 
					// Check if point should be drawn on the edge of the data scaleView.
					bool	skipMarker = false;
					if(!ShouldDrawMarkerOnViewEdgeX())
					{
						// Check for min/max X values
						if(xValue == horizontalViewMax && ShiftedX >= 0)
						{
							skipMarker = true;
						}
 
						// Check for min/max X values
						if(xValue == horizontalViewMin && ShiftedX <= 0)
						{
							skipMarker = true;
						}
					}
 
					//************************************************************
					//** Get marker position and size
					//************************************************************
					int			pointMarkerSize = point.MarkerSize;
					string		pointMarkerImage = point.MarkerImage;
					MarkerStyle	pointMarkerStyle = point.MarkerStyle;
 
 
					// Get marker position
					PointF markerPosition = PointF.Empty;
					markerPosition.Y = (float)VAxis.GetLinearPosition(yValue);
					if( indexedSeries )
					{
						// The formula for position is based on a distance 
						// from the grid line or nPoints position.
						markerPosition.X = (float)HAxis.GetPosition( (double)index );
					}
					else
					{
						markerPosition.X = (float)HAxis.GetPosition( point.XValue );
					}
 
					// Labels and markers have to be shifted if there 
					// is more than one series for column chart.
					markerPosition.X += (float)ShiftedX;
 
					// Remeber pre-calculated point position
					point.positionRel = new PointF(markerPosition.X, markerPosition.Y);
 
					// Get marker size
					SizeF markerSize = GetMarkerSize(
						graph, 
						common, 
						area, 
						point, 
						pointMarkerSize, 
						pointMarkerImage);
 
					//************************************************************
					//** Skip marker drawing
					//************************************************************
					if( skipMarker )
					{
						index++;
						continue;
					}
 
					//************************************************************
					//** Draw point chart
					//************************************************************
					if(alwaysDrawMarkers || 
						pointMarkerStyle != MarkerStyle.None || 
						pointMarkerImage.Length > 0)
					{
						if( common.ProcessModePaint )
						{
						// Check marker index
							if(markerIndex == 0)
							{
								// Start Svg Selection mode
								graph.StartHotRegion( point );
 
								// Draw the marker
								this.DrawPointMarker(
									graph,
									point.series,
									point,
									markerPosition, 
									(pointMarkerStyle == MarkerStyle.None) ? MarkerStyle.Circle : pointMarkerStyle,
									(int)markerSize.Height,
									(point.MarkerColor == Color.Empty) ? point.Color : point.MarkerColor,
									(point.MarkerBorderColor == Color.Empty) ? point.BorderColor : point.MarkerBorderColor,
									GetMarkerBorderSize(point),
									pointMarkerImage,
									point.MarkerImageTransparentColor,
									(point.series != null) ? point.series.ShadowOffset : 0,
									(point.series != null) ? point.series.ShadowColor : Color.Empty,
									new RectangleF(markerPosition.X, markerPosition.Y, markerSize.Width, markerSize.Height));
 
								// End Svg Selection mode
								graph.EndHotRegion( );
							}
 
							if( common.ProcessModeRegions )
							{
								SetHotRegions(
									common,
									graph,
									point,
									markerSize,
									point.series.Name,
									index - 1,
									pointMarkerStyle,
									markerPosition );
							}
						}
					
						// Increase the markers counter
						++markerIndex;
						if(ser.MarkerStep == markerIndex)
						{
							markerIndex = 0;
						}
					}
 
					// Start Svg Selection mode
					graph.StartHotRegion( point, true );
 
					// Draw labels
					DrawLabels( 
						area, 
						graph, 
						common, 
						markerPosition, 
						(int)markerSize.Height, 
						point, 
						ser, 
						index - 1);
 
					// End Svg Selection mode
					graph.EndHotRegion( );
 
				
					++index;
				}
						
				// Call Paint event
				if( !selection )
				{
                    common.Chart.CallOnPostPaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
				}
			
				// Break series loop.
				if( breakSeriesLoop )
				{
					break;
				}
			}
		}
 
        /// <summary>
        /// Draw series point marker.
        /// </summary>
        /// <param name="graph">Chart Graphics used for drawing.</param>
        /// <param name="series">Series.</param>
        /// <param name="dataPoint">Series data point.</param>
        /// <param name="point">Coordinates of the center.</param>
        /// <param name="markerStyle">Marker style.</param>
        /// <param name="markerSize">Marker size.</param>
        /// <param name="markerColor">Marker color.</param>
        /// <param name="markerBorderColor">Marker border color.</param>
        /// <param name="markerBorderSize">Marker border size.</param>
        /// <param name="markerImage">Marker image name.</param>
        /// <param name="markerImageTransparentColor">Color of the marker image transparent.</param>
        /// <param name="shadowSize">Marker shadow size.</param>
        /// <param name="shadowColor">Marker shadow color.</param>
        /// <param name="imageScaleRect">Rectangle to which marker image should be scaled.</param>
		protected virtual void DrawPointMarker(
			ChartGraphics graph,
			Series series,
			DataPoint dataPoint,
			PointF point, 
			MarkerStyle markerStyle, 
			int markerSize, 
			Color markerColor, 
			Color markerBorderColor, 
			int markerBorderSize, 
			string markerImage, 
			Color markerImageTransparentColor, 
			int shadowSize, 
			Color shadowColor, 
			RectangleF imageScaleRect
			)
		{
			// Draw marker using relative coordinates
			graph.DrawMarkerRel(
				point, 
				markerStyle,
				markerSize,
				markerColor, 
				markerBorderColor, 
				markerBorderSize, 
				markerImage, 
				markerImageTransparentColor, 
				shadowSize, 
				shadowColor, 
				imageScaleRect);
		}
 
		/// <summary>
		/// Inserts Hot Regions used for image maps, tool tips and 
		/// hit test function
		/// </summary>
		/// <param name="common">Common elements object</param>
		/// <param name="graph">Chart Graphics object</param>
		/// <param name="point">Data point used for hot region</param>
		/// <param name="markerSize">Size of the marker</param>
		/// <param name="seriesName">Name of the series</param>
		/// <param name="pointIndex">Data point index</param>
		/// <param name="pointMarkerStyle">Marker Style</param>
		/// <param name="markerPosition">Marker Position</param>
		private void SetHotRegions( CommonElements common, ChartGraphics graph, DataPoint point, SizeF markerSize, string seriesName, int pointIndex, MarkerStyle pointMarkerStyle, PointF markerPosition )
		{
			
			// Get relative marker size
			SizeF relativeMarkerSize = graph.GetRelativeSize(markerSize);
 
			int insertIndex = common.HotRegionsList.FindInsertIndex();
 
			// Insert circle area
			if( pointMarkerStyle == MarkerStyle.Circle )
			{
				common.HotRegionsList.AddHotRegion( insertIndex, graph, markerPosition.X, markerPosition.Y, relativeMarkerSize.Width/2f, point, seriesName, pointIndex ); 
			}
			// All other markers represented as rectangles
			else
			{
				// Insert area
				common.HotRegionsList.AddHotRegion( 
					new RectangleF(markerPosition.X - relativeMarkerSize.Width/2f, markerPosition.Y - relativeMarkerSize.Height/2f, relativeMarkerSize.Width, relativeMarkerSize.Height),
					point,
					seriesName,
					pointIndex );
			}
		
		}
 
		/// <summary>
		/// This method draws labels in point chart.
		/// </summary>
		/// <param name="graph">The Chart Graphics object</param>
		/// <param name="common">The Common elements object</param>
		/// <param name="area">Chart area for this chart</param>
		/// <param name="markerPosition">Marker position</param>
		/// <param name="markerSize">Marker size</param>
		/// <param name="point">Data point</param>
		/// <param name="ser">Data series</param>
		/// <param name="pointIndex">Data point index.</param>
		private void DrawLabels( 
			ChartArea area, 
			ChartGraphics graph, 
			CommonElements common, 
			PointF markerPosition, 
			int markerSize, 
			DataPoint point, 
			Series ser,
			int pointIndex)
		{
			// Get some properties for performance
			string	pointLabel = point.Label;
			bool	pointShowLabelAsValue = point.IsValueShownAsLabel;
 
			// ****************************
			// Draw data point value label
			// ****************************
			if((!point.IsEmpty && (ser.IsValueShownAsLabel || pointShowLabelAsValue || pointLabel.Length > 0)) ||
				(pointShowLabelAsValue || pointLabel.Length > 0))
			{
				// Label text format
                using (StringFormat format = new StringFormat())
                {
                    format.Alignment = StringAlignment.Near;
                    format.LineAlignment = StringAlignment.Center;
 
                    // Get label text
                    string text;
                    if (pointLabel.Length == 0)
                    {
                        text = ValueConverter.FormatValue(
                            ser.Chart,
                            point,
                            point.Tag,
                            point.YValues[(labelYValueIndex == -1) ? YValueIndex : labelYValueIndex],
                            point.LabelFormat,
                            ser.YValueType,
                            ChartElementType.DataPoint);
                    }
                    else
                    {
                        text = point.ReplaceKeywords(pointLabel);
                    }
 
                    // Get point label style attribute
                    SizeF sizeMarker = graph.GetRelativeSize(new SizeF(markerSize, markerSize));
                    SizeF sizeFont = graph.GetRelativeSize(
                        graph.MeasureString(text, point.Font, new SizeF(1000f, 1000f), StringFormat.GenericTypographic));
 
                    SizeF sizeSingleCharacter = graph.GetRelativeSize(
                        graph.MeasureString("W", point.Font, new SizeF(1000f, 1000f), StringFormat.GenericTypographic));
 
                    // Increase label size when background is drawn
                    SizeF sizeLabel = new SizeF(sizeFont.Width, sizeFont.Height);
                    float horizontalSpacing = sizeLabel.Width / text.Length;
                    sizeLabel.Height += sizeSingleCharacter.Height / 2;
                    sizeLabel.Width += horizontalSpacing;
 
                    // Get attribute from point or series
                    string attrib = point[CustomPropertyName.LabelStyle];
                    if (attrib == null || attrib.Length == 0)
                    {
                        attrib = ser[CustomPropertyName.LabelStyle];
                    }
                    this.autoLabelPosition = true;
                    if (attrib != null && attrib.Length > 0)
                    {
                        this.autoLabelPosition = false;
 
                        // Get label position from attribute
                        if (String.Compare(attrib, "Auto", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            this.autoLabelPosition = true;
                        }
                        else if (String.Compare(attrib, "Center", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            this.labelPosition = LabelAlignmentStyles.Center;
                        }
                        else if (String.Compare(attrib, "Bottom", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            this.labelPosition = LabelAlignmentStyles.Bottom;
                        }
                        else if (String.Compare(attrib, "TopLeft", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            this.labelPosition = LabelAlignmentStyles.TopLeft;
                        }
                        else if (String.Compare(attrib, "TopRight", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            this.labelPosition = LabelAlignmentStyles.TopRight;
                        }
                        else if (String.Compare(attrib, "BottomLeft", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            this.labelPosition = LabelAlignmentStyles.BottomLeft;
                        }
                        else if (String.Compare(attrib, "BottomRight", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            this.labelPosition = LabelAlignmentStyles.BottomRight;
                        }
                        else if (String.Compare(attrib, "Left", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            this.labelPosition = LabelAlignmentStyles.Left;
                        }
                        else if (String.Compare(attrib, "Right", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            this.labelPosition = LabelAlignmentStyles.Right;
                        }
                        else if (String.Compare(attrib, "Top", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            this.labelPosition = LabelAlignmentStyles.Top;
                        }
                        else
                        {
                            throw (new ArgumentException(SR.ExceptionCustomAttributeValueInvalid(attrib, "LabelStyle")));
                        }
                    }
 
                    // Try to get automatic label position
                    if (this.autoLabelPosition)
                    {
                        this.labelPosition = GetAutoLabelPosition(ser, pointIndex);
                    }
 
                    // Calculate label position
                    PointF position = new PointF(markerPosition.X, markerPosition.Y);
                    switch (this.labelPosition)
                    {
                        case LabelAlignmentStyles.Center:
                            format.Alignment = StringAlignment.Center;
                            break;
                        case LabelAlignmentStyles.Bottom:
                            format.Alignment = StringAlignment.Center;
                            position.Y += sizeMarker.Height / 1.75F;
                            position.Y += sizeLabel.Height / 2F;
                            break;
                        case LabelAlignmentStyles.Top:
                            format.Alignment = StringAlignment.Center;
                            position.Y -= sizeMarker.Height / 1.75F;
                            position.Y -= sizeLabel.Height / 2F;
                            break;
                        case LabelAlignmentStyles.Left:
                            format.Alignment = StringAlignment.Far;
                            position.X -= sizeMarker.Height / 1.75F + horizontalSpacing / 2f;
                            break;
                        case LabelAlignmentStyles.TopLeft:
                            format.Alignment = StringAlignment.Far;
                            position.X -= sizeMarker.Height / 1.75F + horizontalSpacing / 2f;
                            position.Y -= sizeMarker.Height / 1.75F;
                            position.Y -= sizeLabel.Height / 2F;
                            break;
                        case LabelAlignmentStyles.BottomLeft:
                            format.Alignment = StringAlignment.Far;
                            position.X -= sizeMarker.Height / 1.75F + horizontalSpacing / 2f;
                            position.Y += sizeMarker.Height / 1.75F;
                            position.Y += sizeLabel.Height / 2F;
                            break;
                        case LabelAlignmentStyles.Right:
                            //format.Alignment = StringAlignment.Near;
                            position.X += sizeMarker.Height / 1.75F + horizontalSpacing / 2f;
                            break;
                        case LabelAlignmentStyles.TopRight:
                            //format.Alignment = StringAlignment.Near;
                            position.X += sizeMarker.Height / 1.75F + horizontalSpacing / 2f;
                            position.Y -= sizeMarker.Height / 1.75F;
                            position.Y -= sizeLabel.Height / 2F;
                            break;
                        case LabelAlignmentStyles.BottomRight:
                            //format.Alignment = StringAlignment.Near;
                            position.X += sizeMarker.Height / 1.75F + horizontalSpacing / 2f;
                            position.Y += sizeMarker.Height / 1.75F;
                            position.Y += sizeLabel.Height / 2F;
                            break;
                    }
 
                    // Get text angle
                    int textAngle = point.LabelAngle;
 
                    // Check if text contains white space only
                    if (text.Trim().Length != 0)
                    {
 
 
                        // Check if Smart Labels are enabled
                        if (ser.SmartLabelStyle.Enabled)
                        {
                            // Adjust label position using SmartLabelStyle algorithm
                            position = area.smartLabels.AdjustSmartLabelPosition(
                                common,
                                graph,
                                area,
                                ser.SmartLabelStyle,
                                position,
                                sizeFont,
                                format,
                                markerPosition,
                                sizeMarker,
                                this.labelPosition);
 
                            // Smart labels always use 0 degrees text angle
                            textAngle = 0;
                        }
 
 
 
                        // Adjust alignment of vertical labels
                        // NOTE: Fixes issue #4560
                        if (textAngle == 90 || textAngle == -90)
                        {
                            switch (this.labelPosition)
                            {
                                case LabelAlignmentStyles.Top:
                                    format.Alignment = StringAlignment.Near;
                                    position.Y += sizeLabel.Height / 2F;
                                    break;
                                case LabelAlignmentStyles.Bottom:
                                    format.Alignment = StringAlignment.Far;
                                    position.Y -= sizeLabel.Height / 2F;
                                    break;
                                case LabelAlignmentStyles.Right:
                                    format.Alignment = StringAlignment.Center;
                                    format.LineAlignment = StringAlignment.Near;
                                    break;
                                case LabelAlignmentStyles.Left:
                                    format.Alignment = StringAlignment.Center;
                                    format.LineAlignment = StringAlignment.Center;
                                    break;
                                case LabelAlignmentStyles.TopLeft:
                                    format.Alignment = StringAlignment.Near;
                                    break;
                                case LabelAlignmentStyles.TopRight:
                                    break;
                                case LabelAlignmentStyles.BottomLeft:
                                    break;
                                case LabelAlignmentStyles.BottomRight:
                                    format.Alignment = StringAlignment.Far;
                                    break;
                            }
                        }
 
                        // Draw label
                        if (!position.IsEmpty)
                        {
                            // Get label background position
                            RectangleF labelBackPosition = RectangleF.Empty;
                            sizeLabel.Height -= sizeFont.Height / 2;
                            sizeLabel.Height += sizeFont.Height / 8;
                            labelBackPosition = GetLabelPosition(
                                graph,
                                position,
                                sizeLabel,
                                format,
                                true);
 
                            // Adjust rectangle position due to horizontal spacing
                            switch (this.labelPosition)
                            {
                                case LabelAlignmentStyles.Left:
                                    labelBackPosition.X += horizontalSpacing / 2f;
                                    break;
                                case LabelAlignmentStyles.TopLeft:
                                    labelBackPosition.X += horizontalSpacing / 2f;
                                    break;
                                case LabelAlignmentStyles.BottomLeft:
                                    labelBackPosition.X += horizontalSpacing / 2f;
                                    break;
                                case LabelAlignmentStyles.Right:
                                    labelBackPosition.X -= horizontalSpacing / 2f;
                                    break;
                                case LabelAlignmentStyles.TopRight:
                                    labelBackPosition.X -= horizontalSpacing / 2f;
                                    break;
                                case LabelAlignmentStyles.BottomRight:
                                    labelBackPosition.X -= horizontalSpacing / 2f;
                                    break;
                            }
 
                            // Draw label text
                            using (Brush brush = new SolidBrush(point.LabelForeColor))
                            {
                                graph.DrawPointLabelStringRel(
                                    common,
                                    text,
                                    point.Font,
                                    brush,
                                    position,
                                    format,
                                    textAngle,
                                    labelBackPosition,
                                    point.LabelBackColor,
                                    point.LabelBorderColor,
                                    point.LabelBorderWidth,
                                    point.LabelBorderDashStyle,
                                    ser,
                                    point,
                                    pointIndex);
                            }
                        }
                    }
                }
			}
		}
 
 
		/// <summary>
		/// Gets rectangle position of the label.
		/// </summary>
		/// <param name="graph">Chart graphics object.</param>
		/// <param name="position">Original label position.</param>
		/// <param name="size">Label text size.</param>
		/// <param name="format">Label string format.</param>
		/// <param name="adjustForDrawing">Result position is adjusted for drawing.</param>
		/// <returns>Label rectangle position.</returns>
		internal static RectangleF GetLabelPosition(
			ChartGraphics graph,
			PointF position, 
			SizeF size,
			StringFormat format,
			bool adjustForDrawing)
		{
			// Calculate label position rectangle
			RectangleF	labelPosition = RectangleF.Empty;
			labelPosition.Width = size.Width;
			labelPosition.Height = size.Height;
 
			// Calculate pixel size in relative coordiantes
			SizeF	pixelSize = SizeF.Empty;
			if(graph != null)
			{
				pixelSize = graph.GetRelativeSize(new SizeF(1f, 1f));
			}
 
			if(format.Alignment == StringAlignment.Far)
			{
				labelPosition.X = position.X - size.Width;
				if(adjustForDrawing && !pixelSize.IsEmpty)
				{
					labelPosition.X -= 4f*pixelSize.Width;
					labelPosition.Width += 4f*pixelSize.Width;
				}
			}
			else if(format.Alignment == StringAlignment.Near)
			{
				labelPosition.X = position.X;
				if(adjustForDrawing && !pixelSize.IsEmpty)
				{
					labelPosition.Width += 4f*pixelSize.Width;
				}
			}
			else if(format.Alignment == StringAlignment.Center)
			{
				labelPosition.X = position.X - size.Width/2F;
				if(adjustForDrawing && !pixelSize.IsEmpty)
				{
					labelPosition.X -= 2f*pixelSize.Width;
					labelPosition.Width += 4f*pixelSize.Width;
				}
			}
 
			if(format.LineAlignment == StringAlignment.Far)
			{
				labelPosition.Y = position.Y - size.Height;
			}
			else if(format.LineAlignment == StringAlignment.Near)
			{
				labelPosition.Y = position.Y;
			}
			else if(format.LineAlignment == StringAlignment.Center)
			{
				labelPosition.Y = position.Y - size.Height/2F;
			}
 
			labelPosition.Y -= 1f * pixelSize.Height;
 
			return labelPosition;
		}
 
		#endregion
 
		#region 3D painting and Selection
 
		/// <summary>
		/// This method recalculates size of the point marker. This method is used 
		/// from Paint or Select method.
		/// </summary>
		/// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
		/// <param name="graph">The Chart Graphics object.</param>
		/// <param name="common">The Common elements object.</param>
		/// <param name="area">Chart area for this chart.</param>
		/// <param name="seriesToDraw">Chart series to draw.</param>
		virtual protected void ProcessChartType3D( 
			bool selection, 
			ChartGraphics graph, 
			CommonElements common, 
			ChartArea area, 
			Series seriesToDraw )
		{
			
			// Get list of series to draw
			List<string> typeSeries = null;
			if( (area.Area3DStyle.IsClustered && this.SideBySideSeries) ||
				this.Stacked)
			{
				// Draw all series of the same chart type
				typeSeries = area.GetSeriesFromChartType(Name);
			}
			else
			{
				// Draw just one chart series
                typeSeries = new List<string>();
				typeSeries.Add(seriesToDraw.Name);
			}
 
 
			//************************************************************
			//** Get order of data points drawing
			//************************************************************
			ArrayList	dataPointDrawingOrder = area.GetDataPointDrawingOrder(typeSeries, this, selection, COPCoordinates.X, null, this.YValueIndex, false);
 
			//************************************************************
			//** Loop through all data poins
			//************************************************************
			foreach(object obj in dataPointDrawingOrder)
			{
				// Process single point
				ProcessSinglePoint3D(
					(DataPoint3D) obj,
					graph, 
					common, 
					area
					);
			}
 
			// Finish processing 3D labels
			this.DrawAccumulated3DLabels(graph, common, area);
			
		}
 
 
		/// <summary>
		/// Draws\Hit tests single 3D point.
		/// </summary>
		/// <param name="pointEx">3D point information.</param>
		/// <param name="graph">The Chart Graphics object.</param>
		/// <param name="common">The Common elements object.</param>
		/// <param name="area">Chart area for this chart.</param>
		internal void ProcessSinglePoint3D( 
			DataPoint3D	pointEx,
			ChartGraphics graph, 
			CommonElements common, 
			ChartArea area
			)
		{
			// Get point & series
			DataPoint	point = pointEx.dataPoint;
			Series		ser = point.series;
 
			// Reset pre-calculated point position
			point.positionRel = new PointF(float.NaN, float.NaN);
 
			//************************************************************
			//** Set active horizontal/vertical axis
			//************************************************************
			HAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
			VAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
 
			//************************************************************
			//** Check if point values are in the chart area
			//************************************************************
 
			// Check for min/max Y values
			double	yValue = GetYValue(common, area, ser, pointEx.dataPoint, pointEx.index - 1, YValueIndex);
 
			// Axis is logarithmic
			yValue = VAxis.GetLogValue( yValue );
				
			if( yValue > VAxis.ViewMaximum || yValue < VAxis.ViewMinimum)
			{
				return;
			}
 
			// Check for min/max X values
			double xValue = (pointEx.indexedSeries) ? (double)pointEx.index : point.XValue;
			xValue = HAxis.GetLogValue(xValue);
			if(xValue > HAxis.ViewMaximum || xValue < HAxis.ViewMinimum)
			{
				return;
			}
 
			// Check if point should be drawn on the edge of the data scaleView.
			if(!ShouldDrawMarkerOnViewEdgeX())
			{
				// Check for min/max X values
				if(xValue == HAxis.ViewMaximum && ShiftedX >= 0)
				{
					return;
				}
 
				// Check for min/max X values
				if(xValue == HAxis.ViewMinimum && ShiftedX <= 0)
				{
					return;
				}
			}
 
			//************************************************************
			//** Get marker position and size
			//************************************************************
 
			// Get marker position
			PointF markerPosition = PointF.Empty;
			markerPosition.Y = (float)pointEx.yPosition;
			markerPosition.X = (float)HAxis.GetLinearPosition(xValue);	// No Log transformation required. Done above!
 
			// Labels and markers have to be shifted if there 
			// is more than one series for column chart.
			markerPosition.X += (float)ShiftedX;
 
			// Remeber pre-calculated point position
			point.positionRel = new PointF(markerPosition.X, markerPosition.Y);
 
			// Get point some point properties and save them in variables
			int			pointMarkerSize = point.MarkerSize;
			string		pointMarkerImage = point.MarkerImage;
			MarkerStyle	pointMarkerStyle = point.MarkerStyle;
 
			// Get marker size
			SizeF markerSize = GetMarkerSize(
				graph, 
				common, 
				area, 
				point, 
				pointMarkerSize, 
				pointMarkerImage);
 
			//************************************************************
			//** Transform marker position in 3D space
			//************************************************************
			// Get projection coordinates
			Point3D[]	marker3DPosition = new Point3D[1];
			marker3DPosition[0] = new Point3D(markerPosition.X, markerPosition.Y, (float)(pointEx.zPosition + ((this.middleMarker) ? pointEx.depth/2f : pointEx.depth)));
 
			// Transform coordinates of text size
			area.matrix3D.TransformPoints(marker3DPosition);
			PointF	markerRotatedPosition = marker3DPosition[0].PointF;
 
			//************************************************************
			//** Draw point chart
			//************************************************************
			GraphicsPath rectPath = null;
			
			if(alwaysDrawMarkers || 
				pointMarkerStyle != MarkerStyle.None || 
				pointMarkerImage.Length > 0)
			{
				// Check marker index
				if((pointEx.index % ser.MarkerStep) == 0)
				{
					// Detect if we need to get graphical path of drawn object
					DrawingOperationTypes	drawingOperationType = DrawingOperationTypes.DrawElement;
				
					if( common.ProcessModeRegions )
					{
						drawingOperationType |= DrawingOperationTypes.CalcElementPath;
					}
 
					// Start Svg Selection mode
					graph.StartHotRegion( point );
 
					// Draw the marker
					rectPath = graph.DrawMarker3D(area.matrix3D, 
						area.Area3DStyle.LightStyle,
						pointEx.zPosition + ((this.middleMarker) ? pointEx.depth/2f : pointEx.depth),
						markerPosition, 
						(pointMarkerStyle == MarkerStyle.None) ? MarkerStyle.Circle : pointMarkerStyle,
						(int)markerSize.Height,
						(point.MarkerColor == Color.Empty) ? point.Color : point.MarkerColor,
						(point.MarkerBorderColor == Color.Empty) ? point.BorderColor : point.MarkerBorderColor,
						GetMarkerBorderSize(point),
						pointMarkerImage,
						point.MarkerImageTransparentColor,
						(point.series != null) ? point.series.ShadowOffset : 0,
						(point.series != null) ? point.series.ShadowColor : Color.Empty,
						new RectangleF(markerRotatedPosition.X, markerRotatedPosition.Y, markerSize.Width, markerSize.Height),
						drawingOperationType);
 
					// End Svg Selection mode
					graph.EndHotRegion( );
				}
			}
 
 
			//**********************************************************************
			//** Data point label is not drawn with the data point. Instead the 
            //** information about label is collected and drawn when all points 
			//** with current Z position are drawn.
			//** This is done to achieve correct Z order layering of labels.
			//**********************************************************************
			if(this.label3DInfoList != null && 
				this.label3DInfoList.Count > 0 &&
				((Label3DInfo)this.label3DInfoList[this.label3DInfoList.Count-1]).PointEx.zPosition != pointEx.zPosition)
			{
				// Draw labels with information previously collected
				this.DrawAccumulated3DLabels(graph, common, area);
			}
 
			// Check if labels info list was created
			if(this.label3DInfoList == null)
			{
				this.label3DInfoList = new ArrayList();
			}
 
			// Store information about the label for future drawing
			Label3DInfo label3DInfo = new Label3DInfo();
			label3DInfo.PointEx = pointEx;
			label3DInfo.MarkerPosition = markerRotatedPosition;
			label3DInfo.MarkerSize = markerSize;
			this.label3DInfoList.Add(label3DInfo);
 
			if( common.ProcessModeRegions )
			{
				// Get relative marker size
				SizeF relativeMarkerSize = graph.GetRelativeSize(markerSize);
 
				// Insert area just after the last custom area
				int insertIndex = common.HotRegionsList.FindInsertIndex();
 
				// Insert circle area
				if(pointMarkerStyle == MarkerStyle.Circle)
				{
					float[]	circCoord = new float[3];
					circCoord[0] = markerRotatedPosition.X;
					circCoord[1] = markerRotatedPosition.Y;
					circCoord[2] = relativeMarkerSize.Width/2f;
 
					common.HotRegionsList.AddHotRegion( 
						insertIndex, 
						graph, 
						circCoord[0], 
						circCoord[1], 
						circCoord[2], 
						point, 
						ser.Name, 
						pointEx.index - 1 
						); 
				}
 
				// Insert path for 3D bar
				if(pointMarkerStyle == MarkerStyle.Square)
				{
					common.HotRegionsList.AddHotRegion( 
						rectPath, 
						false, 
						graph, 
						point, 
						ser.Name, 
						pointEx.index - 1 
						);
				}
 
				// All other markers represented as rectangles
				else
				{
					common.HotRegionsList.AddHotRegion( 
						new RectangleF(markerRotatedPosition.X - relativeMarkerSize.Width/2f, markerRotatedPosition.Y - relativeMarkerSize.Height/2f, relativeMarkerSize.Width, relativeMarkerSize.Height),
						point,
						ser.Name,
						pointEx.index - 1
						);
				}
			}
            if (rectPath != null)
            {
                rectPath.Dispose();
            }
		}
 
		/// <summary>
		/// Draws labels which are srored in the collection.
		/// </summary>
		/// <param name="graph">The Chart Graphics object.</param>
		/// <param name="common">The Common elements object.</param>
		/// <param name="area">Chart area for this chart.</param>
		internal void DrawAccumulated3DLabels(
			ChartGraphics graph, 
			CommonElements common, 
			ChartArea area)
		{
			if(this.label3DInfoList != null)
			{
				foreach(Label3DInfo labelInfo in this.label3DInfoList)
				{
					// Draw labels
					DrawLabels( 
						area, 
						graph, 
						common, 
						labelInfo.MarkerPosition, 
						(int)labelInfo.MarkerSize.Height, 
						labelInfo.PointEx.dataPoint, 
						labelInfo.PointEx.dataPoint.series, 
						labelInfo.PointEx.index - 1);
	
				}
 
				// Clear labels info list
				this.label3DInfoList.Clear();
			}
		}
 
		#endregion
 
		#region Marker and Labels related methods
 
		/// <summary>
		/// Indicates that markers are drawnd on the X edge of the data scaleView.
		/// </summary>
		/// <returns>True. Point chart always draws markers on the edge.</returns>
		virtual protected bool ShouldDrawMarkerOnViewEdgeX()
		{
			return true;
		}
 
		/// <summary>
		/// Gets marker border size.
		/// </summary>
		/// <param name="point">Data point.</param>
		/// <returns>Marker border size.</returns>
        virtual protected int GetMarkerBorderSize(DataPointCustomProperties point)
		{
			return point.MarkerBorderWidth;
		}
 
		/// <summary>
		/// Gets label position. For point chart this function always returns 'Top'.
		/// </summary>
		/// <param name="series">Series.</param>
		/// <param name="pointIndex">Data point index in series.</param>
		/// <returns>Return automaticly detected label position.</returns>
		virtual protected LabelAlignmentStyles GetAutoLabelPosition(Series series, int pointIndex)
		{
			return LabelAlignmentStyles.Top;
		}
 
		/// <summary>
		/// Returns marker size.
		/// </summary>
		/// <param name="graph">The Chart Graphics object.</param>
		/// <param name="common">The Common elements object.</param>
		/// <param name="area">Chart area for this chart.</param>
		/// <param name="point">Data point.</param>
		/// <param name="markerSize">Marker size.</param>
		/// <param name="markerImage">Marker image.</param>
		/// <returns>Marker width and height.</returns>
		virtual protected SizeF GetMarkerSize(
			ChartGraphics graph, 
			CommonElements common, 
			ChartArea area, 
			DataPoint point, 
			int markerSize, 
			string markerImage)
		{
			SizeF size = new SizeF(markerSize, markerSize);
            if (graph != null && graph.Graphics != null)
            {
                // Marker size is in pixels and we do the mapping for higher DPIs
                size.Width = markerSize * graph.Graphics.DpiX / 96;
                size.Height = markerSize * graph.Graphics.DpiY / 96;
            }
            
            if (markerImage.Length > 0) // Get image size
			    common.ImageLoader.GetAdjustedImageSize(markerImage, graph.Graphics, ref size);
 
			return size;
		}
 
		#endregion
 
		#region Labels shifting properties
 
		/// <summary>
		/// Labels and markers have to be shifted if there 
		/// is more than one series for column chart.
		/// NOT USED IN POINT CHART.
		/// </summary>
		virtual public double ShiftedX
		{
			get
			{
				return 0;
			}
			set
			{
			}
		}
 
		/// <summary>
		/// Labels and markers have to be shifted if there 
		/// is more than one series for column chart. This property 
		/// will give a name of the series, which is used, for 
		/// labels and markers.
		/// NOT USED IN POINT CHART.
		/// </summary>
		virtual public string ShiftedSerName
		{
			get
			{
				return "";
			}
			set
			{
			}
		}
 
		#endregion
		
		#region Y values related methods
 
		/// <summary>
		/// Helper function, which returns the Y value of the point.
		/// </summary>
		/// <param name="common">Chart common elements.</param>
		/// <param name="area">Chart area the series belongs to.</param>
		/// <param name="series">Sereis of the point.</param>
		/// <param name="point">Point object.</param>
		/// <param name="pointIndex">Index of the point.</param>
		/// <param name="yValueIndex">Index of the Y value to get.</param>
		/// <returns>Y value of the point.</returns>
		virtual public double GetYValue(
			CommonElements common, 
			ChartArea area, 
			Series series, 
			DataPoint point, 
			int pointIndex, 
			int yValueIndex)
		{
 
			// Point chart do not have height
			if(yValueIndex == -1)
			{
				return 0.0;
			}
 
            // Check required Y values number
            if (point.YValues.Length <= yValueIndex)
            {
                throw (new InvalidOperationException(SR.ExceptionChartTypeRequiresYValues(this.Name, this.YValuesPerPoint.ToString(CultureInfo.InvariantCulture))));
            }
            
            // Check empty point
            if (point.IsEmpty || double.IsNaN(point.YValues[yValueIndex]))
			{
				double result = GetEmptyPointValue( point, pointIndex );
 
                // NOTE: Fixes issue #6921
                // If empty point Y value is zero then check if the scale of
                // the Y axis and if it is not containing zero adjust the Y value
                // of the empty point, so it will be visible
                if (result == 0.0)
                {
                    Axis yAxis = area.GetAxis(AxisName.Y, series.YAxisType, series.YSubAxisName);
                    double yViewMax = yAxis.maximum;
                    double yViewMin = yAxis.minimum;
                    if (result < yViewMin)
                    {
                        result = yViewMin;
                    }
                    else if (result > yViewMax)
                    {
                        result = yViewMax;
                    }
                }
 
                return result;
			}
 
			return point.YValues[yValueIndex];
		}
 
		/// <summary>
		/// This method will find previous and next data point, which is not 
		/// empty and recalculate a new value for current empty data point. 
		/// New value depends on custom attribute “EmptyPointValue” and 
		/// it could be zero or average.
		/// </summary>
		/// <param name="point">IsEmpty data point.</param>
		/// <param name="pointIndex">IsEmpty data point index.</param>
		/// <returns>A Value for empty data point.</returns>
		internal double GetEmptyPointValue( DataPoint point, int pointIndex )
		{
			Series	series = point.series;				// Data series
			double	previousPoint = 0;					// Previous data point value (not empty)
			double	nextPoint = 0;						// Next data point value (not empty)
			int		prevIndx = 0;						// Previous data point index
			int		nextIndx = series.Points.Count - 1;	// Next data point index
 
			//************************************************************
			//** Check custom attribute "EmptyPointValue"
			//************************************************************
			string emptyPointValue = "";
			if( series.EmptyPointStyle.IsCustomPropertySet(CustomPropertyName.EmptyPointValue) )
			{
				emptyPointValue = series.EmptyPointStyle[CustomPropertyName.EmptyPointValue];
			}
			else if( series.IsCustomPropertySet(CustomPropertyName.EmptyPointValue) )
			{
				emptyPointValue = series[CustomPropertyName.EmptyPointValue];
			}
 
			// Take attribute value
			if( String.Compare(emptyPointValue, "Zero", StringComparison.OrdinalIgnoreCase) == 0 )
			{
				// IsEmpty points represented with zero values
                return 0.0;
			}
			
			//************************************************************
			//** IsEmpty point value is an average of neighbour points
			//************************************************************
 
			// Find previous non-empty point value
			for( int indx = pointIndex; indx >= 0; indx-- )
			{
				if( !series.Points[indx].IsEmpty )
				{
					previousPoint = series.Points[indx].YValues[YValueIndex];
					prevIndx = indx;
					break;
				}
				previousPoint = Double.NaN;
			}
 
			// Find next non-empty point value
			for( int indx = pointIndex; indx < series.Points.Count; indx++ )
			{
				if( !series.Points[indx].IsEmpty )
				{
					nextPoint = series.Points[indx].YValues[YValueIndex];
					nextIndx = indx;
					break;
				}
				nextPoint = Double.NaN;
			}
 
			// All Previous points are empty
			if( Double.IsNaN( previousPoint ) )
			{
				// All points are empty
				if( Double.IsNaN( nextPoint ) )
				{
					previousPoint = 0;
				}
				else // Next point is equal to previous point
				{
					previousPoint = nextPoint;
				}
			}
 
			// All next points are empty
			if( Double.IsNaN( nextPoint ) )
			{
				// Previous point is equal to next point
				nextPoint = previousPoint;
			}
 
			// If points value are the same use average
			if( series.Points[nextIndx].XValue == series.Points[prevIndx].XValue )
			{
				return ( previousPoint + nextPoint ) / 2;
			}
 
			// Calculate and return average value
			double aCoeff = (previousPoint - nextPoint) / (series.Points[nextIndx].XValue - series.Points[prevIndx].XValue);
            return -aCoeff * (point.XValue - series.Points[prevIndx].XValue) + previousPoint;
		}
 
		#endregion
 
		#region SmartLabelStyle methods
 
		/// <summary>
		/// Adds markers position to the list. Used to check SmartLabelStyle overlapping.
		/// </summary>
		/// <param name="common">Common chart elements.</param>
		/// <param name="area">Chart area.</param>
		/// <param name="series">Series values to be used.</param>
		/// <param name="list">List to add to.</param>
		public void AddSmartLabelMarkerPositions(CommonElements common, ChartArea area, Series series, ArrayList list)		
		{
            this.Common = common;
            // Check if series is indexed
            indexedSeries = ChartHelper.IndexedSeries(this.Common, area.GetSeriesFromChartType(this.Name).ToArray());
 
			//************************************************************
			//** Set active horizontal/vertical axis
			//************************************************************
			Axis hAxis = area.GetAxis(AxisName.X, series.XAxisType, series.XSubAxisName);
			Axis vAxis = area.GetAxis(AxisName.Y, series.YAxisType, series.YSubAxisName);
 
			//************************************************************
			//** Loop through all data points in the series
			//************************************************************
			int	markerIndex = 0;		// Marker index
			int	index = 1;				// Data points loop
			foreach( DataPoint point in series.Points )
			{
				//************************************************************
				//** Check if point values are in the chart area
				//************************************************************
 
				// Check for min/max Y values
				double	yValue = GetYValue(common, area, series, point, index - 1, YValueIndex);
 
				// Axis is logarithmic
				yValue = vAxis.GetLogValue( yValue );
				
				if( yValue > vAxis.ViewMaximum || yValue < vAxis.ViewMinimum)
				{
					index++;
					continue;
				}
 
				// Check for min/max X values
				double xValue = (indexedSeries) ? (double)index : point.XValue;
				xValue = hAxis.GetLogValue(xValue);
				if(xValue > hAxis.ViewMaximum || xValue < hAxis.ViewMinimum)
				{
					index++;
					continue;
				}
 
				// Check if point should be drawn on the edge of the data scaleView.
				if(!ShouldDrawMarkerOnViewEdgeX())
				{
					// Check for min/max X values
					if(xValue == hAxis.ViewMaximum && ShiftedX >= 0)
					{
						index++;
						continue;
					}
 
					// Check for min/max X values
					if(xValue == hAxis.ViewMinimum && ShiftedX <= 0)
					{
						index++;
						continue;
					}
				}
 
				//************************************************************
				//** Get marker position and size
				//************************************************************
 
				// Get marker position
				PointF markerPosition = PointF.Empty;
				markerPosition.Y = (float)vAxis.GetLinearPosition(yValue);
				if( indexedSeries )
				{
					// The formula for position is based on a distance 
					// from the grid line or nPoints position.
					markerPosition.X = (float)hAxis.GetPosition( (double)index );
				}
				else
				{
					markerPosition.X = (float)hAxis.GetPosition( point.XValue );
				}
 
				// Labels and markers have to be shifted if there 
				// is more than one series for column chart.
				markerPosition.X += (float)ShiftedX;
 
				// Get point some point properties and save them in variables
				int			pointMarkerSize = point.MarkerSize;
				string		pointMarkerImage = point.MarkerImage;
				MarkerStyle	pointMarkerStyle = point.MarkerStyle;
 
				// Get marker size
				SizeF markerSize = GetMarkerSize(
					common.graph, 
					common, 
					area, 
					point, 
					pointMarkerSize, 
					pointMarkerImage);
 
				// Transform marker position in 3D space
				if(area.Area3DStyle.Enable3D)
				{
					// Get series depth and Z position
					float seriesDepth, seriesZPosition;
					area.GetSeriesZPositionAndDepth(series, out seriesDepth, out seriesZPosition);
 
					Point3D[]	marker3DPosition = new Point3D[1];
					marker3DPosition[0] = new Point3D(
						markerPosition.X, 
						markerPosition.Y, 
						(float)(seriesZPosition + ((this.middleMarker) ? seriesDepth/2f : seriesDepth)));
 
					// Transform coordinates
					area.matrix3D.TransformPoints(marker3DPosition);
					markerPosition = marker3DPosition[0].PointF;
				}
 
				// Check if marker visible
				if(alwaysDrawMarkers || 
					pointMarkerStyle != MarkerStyle.None || 
					pointMarkerImage.Length > 0)
				{
					// Check marker index
					if(markerIndex == 0)
					{
						markerSize = common.graph.GetRelativeSize(markerSize);
 
						// Add marker position into the list
						RectangleF	markerRect = new RectangleF(
							markerPosition.X - markerSize.Width / 2f,
							markerPosition.Y - markerSize.Height / 2f,
							markerSize.Width,
							markerSize.Height);
						list.Add(markerRect);
					}
					
					// Increase the markers counter
					++markerIndex;
					if(series.MarkerStep == markerIndex)
					{
						markerIndex = 0;
					}
				}
 
				++index;
			}
		}
 
		#endregion
 
		#region 3D Label Info class
 
		/// <summary>
		/// 3D LabelStyle info.
		/// </summary>
		internal class Label3DInfo
		{
			internal DataPoint3D PointEx = null;
			internal PointF MarkerPosition = PointF.Empty;
			internal SizeF MarkerSize = SizeF.Empty;
		}
 
		#endregion // 3D Label Info class
 
        #region IDisposable interface implementation
        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            //Nothing to dispose at the base class. 
        }
 
        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}