File: Common\ChartTypes\StackedColumnChart.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:		StackedColumnChart.cs
//
//  Namespace:	DataVisualization.Charting.ChartTypes
//
//	Classes:	StackedColumnChart, HundredPercentStackedColumnChart
//
//  Purpose:	This class contains all necessary methods and 
//				properties for drawing and selection of the stacked 
//				Column	chart. Every data point in the Stacked Column 
//				chart is represented with one rectangle. If there is 
//				more then one series with this chart type from 
//				same chart area, Column with same X values are 
//				Stacked.
//
//	Reviewed:	AG - Aug 6, 2002
//              AG - Microsoft 7, 2007
//
//===================================================================
 
#region Used namespaces
 
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
 
#if Microsoft_CONTROL
	using System.Windows.Forms.DataVisualization.Charting.Utilities;
#else
	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>
    /// HundredPercentStackedColumnChart class extends StackedColumnChart class
    /// by providing its own algorithm for calculating series data point
    /// Y values. It makes sure that total Y value of all data points in a
    /// single cluster from all series adds up to 100%.
    /// </summary>
	internal class HundredPercentStackedColumnChart : StackedColumnChart
	{
		#region Constructor
 
		/// <summary>
		/// Default constructor.
		/// </summary>
		public HundredPercentStackedColumnChart()
		{
			hundredPercentStacked = true;
		}
 
		#endregion 
 
		#region Fields
 
 
		// Total Y values from all series at specified index orgonized by stacked groups
		// Hashtable will contain arrays of doubles stored by group name key.
		Hashtable		_stackedGroupsTotalPerPoint = null;
 
 
		#endregion
 
		#region IChartType interface implementation
 
		/// <summary>
		/// Chart type name
		/// </summary>
		override public string Name			{ get{ return ChartTypeNames.OneHundredPercentStackedColumn;}}
 
		/// <summary>
		/// Indicates that it's a hundredred percent chart.
		/// Axis scale from 0 to 100 percent should be used.
		/// </summary>
		override public bool HundredPercent{ get{return true;} }
 
		/// <summary>
		/// Indicates that it's a hundredred percent chart.
		/// Axis scale from 0 to 100 percent should be used.
		/// </summary>
		override public bool HundredPercentSupportNegative{ get{return true;} }
 
		#endregion
 
		#region Painting and Selection methods
 
		/// <summary>
        /// Paint HundredPercentStackedColumnChart 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>
		override public void Paint( ChartGraphics graph, CommonElements common, ChartArea area, Series seriesToDraw )
		{
			// Reset pre-calculated totals
 
			this._stackedGroupsTotalPerPoint = null;
			
			base.Paint( graph, common, area, seriesToDraw );
		}
 
		#endregion
 
		#region Y values 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>
		override public double GetYValue(CommonElements common, ChartArea area, Series series, DataPoint point, int pointIndex, int yValueIndex)
		{
			// Array of Y totals for individual series index in the current stacked group
			double[] currentGroupTotalPerPoint = null;
 
 
 
			string currentStackedGroupName = HundredPercentStackedColumnChart.GetSeriesStackGroupName(series);
			if(this._stackedGroupsTotalPerPoint == null)
			{
				// Create new hashtable
				this._stackedGroupsTotalPerPoint = new Hashtable();
 
				// Iterate through all stacked groups
				foreach(string groupName in this.stackGroupNames)
				{
					// Get series that belong to the same group
					Series[] seriesArray = HundredPercentStackedColumnChart.GetSeriesByStackedGroupName(
                        common, groupName, series.ChartTypeName, series.ChartArea);
 
					// Check if series are aligned
					common.DataManipulator.CheckXValuesAlignment(seriesArray);
 
					// Allocate memory for the array of totals
					double[] totals = new double[series.Points.Count];
 
					// Calculate the total of Y value per point 
					for(int index = 0; index < series.Points.Count; index++)
					{
						totals[index] = 0;
						foreach( Series ser in seriesArray )
						{
							totals[index] += Math.Abs(ser.Points[index].YValues[0]);
						}
					}
 
					// Add totals array into the hashtable
					this._stackedGroupsTotalPerPoint.Add(groupName, totals);
				}
			}
 
			// Find array of total Y values based on the current stacked group name
			currentGroupTotalPerPoint = (double[])this._stackedGroupsTotalPerPoint[currentStackedGroupName];
 
 
			// IsEmpty point
			if(!area.Area3DStyle.Enable3D)
			{
				if(point.YValues[0] == 0 || point.IsEmpty)
				{
					return 0;
				}
			}
 
			// Calculate stacked column Y value for 2D chart
			if(area.Area3DStyle.Enable3D == false || yValueIndex == -2)
			{
				if(currentGroupTotalPerPoint[pointIndex] == 0.0)
				{
					return 0.0;
				}
				return (point.YValues[0] / currentGroupTotalPerPoint[pointIndex]) * 100.0;
			}
 
 
			// Get point Height if pointIndex == -1
			double yValue = double.NaN;
			if(yValueIndex == -1)
			{
				Axis	vAxis = area.GetAxis(AxisName.Y, series.YAxisType, series.YSubAxisName);
				double	barZeroValue = vAxis.Crossing;
				yValue = GetYValue(common, area, series, point, pointIndex, 0);
				if( yValue >= 0 )
				{
					if(!double.IsNaN(prevPosY))
					{
						barZeroValue = prevPosY;
					}
				}
				else
				{
					if(!double.IsNaN(prevNegY))
					{
						barZeroValue = prevNegY;
					}
				}
 
				return yValue - barZeroValue;
			}
 
			
			// Loop through all series to find point value
			prevPosY = double.NaN;
			prevNegY = double.NaN;
			foreach(Series ser in common.DataManager.Series)
			{
				// Check series of the current chart type & area
				if(String.Compare(series.ChartArea, ser.ChartArea, StringComparison.Ordinal) == 0 &&
                    String.Compare(series.ChartTypeName, ser.ChartTypeName, StringComparison.OrdinalIgnoreCase) == 0 && 
					ser.IsVisible())
				{
 
					// Series must belong to the same stacked group
					if(currentStackedGroupName != HundredPercentStackedColumnChart.GetSeriesStackGroupName(ser))
					{
						continue;
					}
 
 
					if(double.IsNaN(yValue))
					{
						if(currentGroupTotalPerPoint[pointIndex] == 0.0)
						{
							yValue = 0.0;
						}
						else
						{
							yValue = (ser.Points[pointIndex].YValues[0] / currentGroupTotalPerPoint[pointIndex]) * 100.0;
						}
					}
					else
					{
						if(currentGroupTotalPerPoint[pointIndex] == 0.0)
						{
							yValue = 0.0;
						}
						else
						{
							yValue = (ser.Points[pointIndex].YValues[0] / currentGroupTotalPerPoint[pointIndex]) * 100.0;
						}
						if(yValue >= 0.0 && !double.IsNaN(prevPosY))
						{
							yValue += prevPosY;
						}
						if(yValue < 0.0 && !double.IsNaN(prevNegY))
						{
							yValue += prevNegY;
						}
					}
 
					// Exit loop when current series was found
                    if (String.Compare(series.Name, ser.Name, StringComparison.Ordinal) == 0)
					{
						break;
					}
 
					// Save previous value
					if(yValue >= 0.0)
					{
						prevPosY = yValue;
					}
					else
					{
						prevNegY = yValue;
					}
				}
			}
			
			return (yValue > 100.0) ? 100.0 : yValue;
		}
 
		#endregion
	}
 
	/// <summary>
    /// StackedColumnChart class contains all the code necessary to draw 
    /// and hit test Stacked Column chart. 
    /// </summary>
	internal class StackedColumnChart : IChartType
	{
		#region Fields
 
		/// <summary>
		/// Previous stacked positive Y values.
		/// </summary>
		protected	double	prevPosY = double.NaN;
 
		/// <summary>
		/// Previous stacked negative Y values.
		/// </summary>
		protected	double	prevNegY = double.NaN;
 
		/// <summary>
		/// Indicates if chart is 100% stacked
		/// </summary>
		protected	bool			hundredPercentStacked = false;
 
 
 
		/// <summary>
		/// True if stacke group name is applicable
		/// </summary>
		internal	bool			stackGroupNameUsed = false;
 
		/// <summary>
		/// List of all stack group names
		/// </summary>
		internal	ArrayList		stackGroupNames = null;
 
		/// <summary>
		/// Name of the current stack group.
		/// </summary>
		internal	string			currentStackGroup = string.Empty;
 
 
 
		#endregion
 
		#region IChartType interface implementation
 
		/// <summary>
		/// Chart type name
		/// </summary>
		virtual public string Name			{ get{ return ChartTypeNames.StackedColumn;}}
 
		/// <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");
		}
	
		/// <summary>
		/// True if chart type is stacked
		/// </summary>
		virtual public bool Stacked		{ get{ return true;}}
 
 
		/// <summary>
		/// True if stacked chart type supports groups
		/// </summary>
		virtual public bool SupportStackedGroups	{ get { return true; } }
 
 
		/// <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 true;}}
 
		/// <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>
		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>
		/// 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>
		/// 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>
		/// 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.Rectangle;
		}
 
		/// <summary>
		/// Number of supported Y value(s) per point 
		/// </summary>
		virtual public int YValuesPerPoint{ get { return 1; } }
 
		/// <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 true;} }
 
		#endregion
 
		#region Constructor
 
		/// <summary>
		/// Default constructor.
		/// </summary>
		public StackedColumnChart()
		{
		}
 
		#endregion
 
		#region Painting and Selection methods
 
		/// <summary>
        /// Paint StackedColumnChart 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 )
		{	
 
			// Reset stacked group names flag
			stackGroupNameUsed = true;
 
 
			// Set Clip Region in rounded to a pixel coordinates
			RectangleF areaPosition = ((ChartGraphics)graph).GetAbsoluteRectangle( area.PlotAreaPosition.ToRectangleF());
			float right = (float)Math.Ceiling(areaPosition.Right);
			float bottom = (float)Math.Ceiling(areaPosition.Bottom);
			areaPosition.X = (float)Math.Floor(areaPosition.X);
			areaPosition.Width = right - areaPosition.X;
			areaPosition.Y = (float)Math.Floor(areaPosition.Y);
			areaPosition.Height = bottom - areaPosition.Y;
			((ChartGraphics)graph).SetClipAbs( areaPosition );
 
			// Draw shadow first
			ProcessChartType( false, graph, common, area, true, false, seriesToDraw );
 
			// Draw stacked bars
			ProcessChartType( false, graph, common, area, false, false, seriesToDraw );
 
			// Draw labels
			ProcessChartType( false, graph, common, area, false, true, seriesToDraw );
 
			// Reset Clip Region
			((ChartGraphics)graph).ResetClip();
		}
 
		/// <summary>
		/// This method recalculates size of the Columns. 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 objec.t</param>
		/// <param name="common">The Common elements object.</param>
		/// <param name="area">Chart area for this chart.</param>
		/// <param name="shadow">True if shadow mode is active.</param>
		/// <param name="labels">Labels drawing mode.</param>
		/// <param name="seriesToDraw">Chart series to draw.</param>
		private void ProcessChartType( 
			bool selection, 
			ChartGraphics graph, 
			CommonElements common, 
			ChartArea area, 
			bool shadow, 
			bool labels, 
			Series seriesToDraw )
		{
 
 
			//************************************************************
			//** If stacked series is attached to diferent X and Y axis
			//** they can not be processed. To solve this issue series 
			//** will be orgonized in groups based on the axes.
			//************************************************************
 
			// Loop through all series and check if different axes are used
			bool differentAxesAreUsed = false;
			AxisType xAxisType = AxisType.Primary;
			AxisType yAxisType = AxisType.Primary;
			string xSubAxisName = string.Empty;
			string ySubAxisName = string.Empty;
			for(int seriesIndex = 0; seriesIndex < common.DataManager.Series.Count; seriesIndex++)
			{
				// Process non empty series of the area with stacked column chart type
				Series ser = common.DataManager.Series[seriesIndex];
				if( String.Compare( ser.ChartTypeName, Name, StringComparison.OrdinalIgnoreCase ) != 0 
					|| ser.ChartArea != area.Name || !ser.IsVisible())
				{
					continue;
				}
 
				if(seriesIndex == 0)
				{
					xAxisType = ser.XAxisType;
					yAxisType = ser.YAxisType;
					xSubAxisName = ser.XSubAxisName;
					ySubAxisName = ser.YSubAxisName;
				}
				else if(xAxisType != ser.XAxisType ||
					yAxisType != ser.YAxisType ||
					xSubAxisName != ser.XSubAxisName ||
					ySubAxisName != ser.YSubAxisName)
				{
					differentAxesAreUsed = true;
					break;
				}
			}
 
			// Set stacked groups based on the axes used
			if(differentAxesAreUsed)
			{
				for(int seriesIndex = 0; seriesIndex < common.DataManager.Series.Count; seriesIndex++)
				{
					// Process non empty series of the area with stacked column chart type
					Series ser = common.DataManager.Series[seriesIndex];
					if( String.Compare( ser.ChartTypeName, Name, StringComparison.OrdinalIgnoreCase ) != 0 
						|| ser.ChartArea != area.Name || !ser.IsVisible())
					{
						continue;
					}
 
					// Set new group name
					string stackGroupName = StackedColumnChart.GetSeriesStackGroupName(ser);
					stackGroupName = "_X_" + ser.XAxisType.ToString() + ser.XSubAxisName + "_Y_" + ser.YAxisType.ToString() + ser.YSubAxisName + "__"; 
					ser[CustomPropertyName.StackedGroupName] = stackGroupName;
				}
			}
 
			//************************************************************
			//** Check how many stack groups are available.
			//************************************************************
			
			// Loop through all series and get unique stack group names.
			this.stackGroupNames = new ArrayList();
			foreach( Series ser in common.DataManager.Series )
			{
				// Process non empty series of the area with stacked column chart type
				if( String.Compare( ser.ChartTypeName, Name, StringComparison.OrdinalIgnoreCase ) != 0 
					|| ser.ChartArea != area.Name || !ser.IsVisible())
				{
					continue;
				}
 
				// Get stack group name from the series
				string stackGroupName = StackedColumnChart.GetSeriesStackGroupName(ser);
 
				// Add group name if it do not already exsist
				if(!this.stackGroupNames.Contains(stackGroupName))
				{
					this.stackGroupNames.Add(stackGroupName);
				}
			}
 
 
			//************************************************************
			//** Prosess 3D chart type.
			//************************************************************
			if(area.Area3DStyle.Enable3D)
			{
				if(!shadow)
				{
					ProcessChartType3D( 
						selection, 
						graph, 
						common, 
						area, 
						labels,
						seriesToDraw );
				}
 
				return;
			}
			
			
			// All data series from chart area which have Column chart type
			string[]	seriesList = area.GetSeriesFromChartType(Name).ToArray();
 
			// Get maximum number of data points for all series
			int		maxNumOfPoints = common.DataManager.GetNumberOfPoints(seriesList);
 
			// Zero X values mode.
			bool	indexedSeries = ChartHelper.IndexedSeries(common, seriesList);
 
			//************************************************************
			//** Loop through all data points
			//************************************************************
			for( int pointIndx = 0; pointIndx < maxNumOfPoints; pointIndx++ )
			{
 
				//************************************************************
				//** Loop through all stack groups
				//************************************************************
				for(int groupIndex = 0;  groupIndex < this.stackGroupNames.Count; groupIndex++)
				{
					// Rememmber current stack group name
					this.currentStackGroup = (string)this.stackGroupNames[groupIndex];
 
					int		seriesIndx = 0;		// Data series index
					double	PreviousPosY = 0;	// Previous positive Y value
					double	PreviousNegY = 0;	// Previous negative Y value
 
					//************************************************************
					//** Loop through all series
					//************************************************************
					foreach( Series ser in common.DataManager.Series )
					{
						// Process non empty series of the area with stacked column chart type
						if( String.Compare( ser.ChartTypeName, Name, StringComparison.OrdinalIgnoreCase ) != 0 
							|| ser.ChartArea != area.Name || !ser.IsVisible())
						{
							continue;
						}
 
						// Series point index is out of range
						if( pointIndx >= ser.Points.Count )
						{
							continue;
						}
 
 
						// Check if series belongs to the current group name
						string seriesStackGroupName = StackedColumnChart.GetSeriesStackGroupName(ser);
						if(seriesStackGroupName != this.currentStackGroup)
						{
							continue;
						}
 
 
						// Get data point
						DataPoint point = ser.Points[ pointIndx ];
 
						// Reset pre-calculated point position
						point.positionRel = new PointF(float.NaN, float.NaN);
 
						// Set active horizontal/vertical axis
						Axis	vAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
						Axis	hAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
 
						// Interval between Columns
                        bool sameInterval = false;
						double interval = 1;
						if( !indexedSeries )
						{
                            if (ser.Points.Count == 1 &&
                                (ser.XValueType == ChartValueType.Date || 
                                 ser.XValueType == ChartValueType.DateTime || 
                                 ser.XValueType == ChartValueType.Time ||
                                 ser.XValueType == ChartValueType.DateTimeOffset))
                            {
                                // Check if interval is the same
                                List<string> typeSeries = area.GetSeriesFromChartType(Name);
                                area.GetPointsInterval(typeSeries, hAxis.IsLogarithmic, hAxis.logarithmBase, true, out sameInterval);
 
                                // Special case when there is only one data point and date scale is used.
                                if (!double.IsNaN(hAxis.majorGrid.GetInterval()) && hAxis.majorGrid.GetIntervalType() != DateTimeIntervalType.NotSet)
                                {
                                    interval = ChartHelper.GetIntervalSize(hAxis.minimum, hAxis.majorGrid.GetInterval(), hAxis.majorGrid.GetIntervalType());
                                }
                                else
                                {
                                    interval = ChartHelper.GetIntervalSize(hAxis.minimum, hAxis.Interval, hAxis.IntervalType);
                                }
                            }
                            else
                            {
                                interval = area.GetPointsInterval(hAxis.IsLogarithmic, hAxis.logarithmBase);
                            }
						}
 
						// Calculates the width of Columns.
						double width = ser.GetPointWidth(graph, hAxis, interval, 0.8);
 
 
						// Adjust width by number of stacked groups
						width = width / (double)this.stackGroupNames.Count;
 
 
						// Call Back Paint event
						if( !selection )
						{
                            common.Chart.CallOnPrePaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
						}
										
						// Change Y value if Column is out of plot area
						double	yValue = GetYValue(common, area, ser, point, pointIndx, 0);
						if( seriesIndx != 0 )
						{
							if( yValue >= 0 )
							{
								yValue = yValue + PreviousPosY;
							}
							else
							{
								yValue = yValue + PreviousNegY;
							}
						}
 
                        // Check if scrolling/zooming frames cutting mode is enabled
                        bool ajaxScrollingEnabled = false;
 
						// Save original Y Value
						double	originalYValue = yValue;
 
						// Get IsLogarithmic value
						yValue = vAxis.GetLogValue(yValue);
 
						// Check if value is inside plotting area
                        if ( !( ajaxScrollingEnabled && labels) )
                        {
                            if (yValue > vAxis.ViewMaximum)
                            {
                                yValue = vAxis.ViewMaximum;
                            }
                            if (yValue < vAxis.ViewMinimum)
                            {
                                yValue = vAxis.ViewMinimum;
                            }
                        }
 
						// Recalculates Height position and zero position of Columns
						double	height = vAxis.GetLinearPosition( yValue );
					
						// Set Start position for a column
						double	barZeroValue = 0.0;
						if( seriesIndx == 0 )
						{
                            if (ajaxScrollingEnabled && labels)
                            {
                                // If AJAX scrolling is used always use 0.0 as a starting point
                                barZeroValue = 0.0;
                            }
                            else
                            {
                                // Set Start position for a Column
                                barZeroValue = vAxis.Crossing;
                            }
 
						}
						else if( GetYValue(common, area, ser, point, pointIndx, 0) >= 0 )
						{
							barZeroValue = PreviousPosY;
						}
						else
						{
							barZeroValue = PreviousNegY;
						}
						double zero = vAxis.GetPosition(barZeroValue);
 
						// Set x position
						double	xValue = point.XValue;
						if( indexedSeries )
						{
							// The formula for position is based on a distance 
							//from the grid line or nPoints position.
							xValue = (double)pointIndx + 1;
						}
 
						double xPosition = hAxis.GetPosition( xValue );
 
						// Adjust X position of each stack group
						if(this.stackGroupNames.Count > 1)
						{
							xPosition = xPosition - width * ((double) this.stackGroupNames.Count) / 2.0 + width / 2.0 + groupIndex * width;
						}
 
 
						xValue = hAxis.GetLogValue(xValue);
 
						// Calculate column position
						RectangleF	rectSize = RectangleF.Empty;
						try
						{
							// Set the Column rectangle
							rectSize.X = (float)(xPosition - width/2);
							rectSize.Width = (float)(width);
 
							// The top side of rectangle has always 
							// smaller value than a bottom value
							if( zero < height )
							{
								rectSize.Y = (float)zero;
								rectSize.Height = (float)height - rectSize.Y;
							}
							else
							{
								rectSize.Y = (float)height;
								rectSize.Height = (float)zero - rectSize.Y;
							}
						}
						catch(OverflowException)
						{
                            seriesIndx++;
							continue;
						}
 
						// Remeber pre-calculated point position
						point.positionRel = new PointF((float)xPosition, rectSize.Top);
 
						// if data point is not empty
						if( point.IsEmpty )
						{
                            seriesIndx++;
							continue;
						}
 
						// **************************************************
						// Painting mode
						// **************************************************
						if( common.ProcessModePaint )
						{
							// Check if column is completly out of the data scaleView
                            bool skipPoint = false;
							if(xValue < hAxis.ViewMinimum || 
								xValue > hAxis.ViewMaximum ||
								(yValue < vAxis.ViewMinimum && barZeroValue < vAxis.ViewMinimum) ||
								(yValue > vAxis.ViewMaximum && barZeroValue > vAxis.ViewMaximum) )
							{
                                skipPoint = true;
							}
 
                            if(!skipPoint)
                            {
 
							    // Ser shadow
							    int shadowOffset = 0;
							    if( shadow )
							    {
								    shadowOffset = ser.ShadowOffset;
							    }
 
							    if( !labels )
							    {
								    // Check if column is partialy in the data scaleView
								    bool	clipRegionSet = false;
								    if(rectSize.X < area.PlotAreaPosition.X || 
									    rectSize.Right > area.PlotAreaPosition.Right || 
									    rectSize.Y < area.PlotAreaPosition.Y || 
									    rectSize.Bottom > area.PlotAreaPosition.Bottom)
								    {
									    // Set clipping region for line drawing 
									    graph.SetClip( area.PlotAreaPosition.ToRectangleF() );
									    clipRegionSet = true;
								    }
 
								    // Start Svg Selection mode
								    graph.StartHotRegion( point );
 
        						    if(!shadow || shadowOffset != 0)
								    {
									    // Draw the Column rectangle
									    graph.FillRectangleRel( rectSize, 
										    (!shadow)? point.Color : Color.Transparent, 
										    point.BackHatchStyle, 
										    point.BackImage, 
										    point.BackImageWrapMode, 
										    point.BackImageTransparentColor,
										    point.BackImageAlignment,
										    point.BackGradientStyle, 
										    (!shadow)? point.BackSecondaryColor : Color.Transparent, 
										    point.BorderColor, 
										    point.BorderWidth, 
										    point.BorderDashStyle, 
										    ser.ShadowColor, 
										    shadowOffset,
										    PenAlignment.Inset,
										    (shadow) ? BarDrawingStyle.Default : ChartGraphics.GetBarDrawingStyle(point),
										    true);
								    }
 
								    // End Svg Selection mode
								    graph.EndHotRegion( );
 
								    // Reset Clip Region
								    if(clipRegionSet)
								    {
									    graph.ResetClip();
								    }
							    }
							    else
							    {
    							    // Draw labels
								    DrawLabels( common, graph, area, point, pointIndx, ser, rectSize );
							    }
                            }
						}
 
						// **************************************************
						// Create Hot Regions
						// **************************************************
						if( common.ProcessModeRegions && !shadow && !labels)
						{
							common.HotRegionsList.AddHotRegion( rectSize, point, ser.Name, pointIndx );
						}
										
						// Call Paint event
						if( !selection )
						{
                            common.Chart.CallOnPostPaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
						}
 
					
						// Axis is logarithmic
						if( vAxis.IsLogarithmic )
						{
							yValue = Math.Pow( vAxis.logarithmBase, yValue );
						}
 
						// Data series index
						seriesIndx++;
						if( GetYValue(common, area, ser, point, pointIndx, 0) >= 0 )
						{
							PreviousPosY = originalYValue;
						}
						else
						{
							PreviousNegY = originalYValue;
						}
					}
 
				}
 
			}
 
 
 
 
			//************************************************************
			//** Remove stacked groups created for series attached to different axis
			//************************************************************
 
			if(differentAxesAreUsed)
			{
				for(int seriesIndex = 0; seriesIndex < common.DataManager.Series.Count; seriesIndex++)
				{
					// Process non empty series of the area with stacked column chart type
					Series ser = common.DataManager.Series[seriesIndex];
					if( String.Compare( ser.ChartTypeName, Name, StringComparison.OrdinalIgnoreCase ) != 0 
						|| ser.ChartArea != area.Name || !ser.IsVisible())
					{
						continue;
					}
 
					// Set new group name
					string stackGroupName = StackedColumnChart.GetSeriesStackGroupName(ser);
					int index = stackGroupName.IndexOf("__", StringComparison.Ordinal);
					if(index >= 0)
					{
						stackGroupName = stackGroupName.Substring(index + 2);
					}
					if(stackGroupName.Length > 0)
					{
						ser[CustomPropertyName.StackedGroupName] = stackGroupName;
					}
					else
					{
						ser.DeleteCustomProperty(CustomPropertyName.StackedGroupName);
					}
				}
			}
 
 
 
		}
		
 
 
		/// <summary>
		/// Helper method that gets an array of series that belong to the specified
		/// stacked group.
		/// </summary>
		/// <param name="common">Chart common elements.</param>
		/// <param name="groupName">Group name to get the list of series for.</param>
        /// <param name="chartTypeName">Series chart type name to include in the result list.</param>
        /// <param name="chartAreaName">Series chart are name where series should belong to.</param>
		/// <returns>Array of series that belong to the specified group.</returns>
		static internal Series[] GetSeriesByStackedGroupName(CommonElements common, string groupName, string chartTypeName, string chartAreaName)
		{
			// Get a list of series with specified group name
			ArrayList list = new ArrayList();
			foreach(Series series in common.DataManager.Series)
			{
                if (String.Compare(series.ChartTypeName, chartTypeName, StringComparison.OrdinalIgnoreCase) == 0 &&
                    chartAreaName == series.ChartArea &&
                    series.IsVisible())
                {
                    if (GetSeriesStackGroupName(series) == groupName)
                    {
                        list.Add(series);
                    }
                }
			}
 
			// Convert array list to array of series
			int index = 0;
			Series[] arrayOfSeries = new Series[list.Count];
			foreach(Series series in list)
			{
				arrayOfSeries[index++] = series;
			}
 
			return arrayOfSeries;
		}
 
		/// <summary>
		/// Helper method that gets stacked group name from specified series.
		/// </summary>
		/// <param name="series">Series to get the group name from.</param>
		/// <returns>Series stacked group name.</returns>
		static internal string GetSeriesStackGroupName(Series series)
		{
			// Get stack group name from the series
			string stackGroupName = string.Empty;
			if(series.IsCustomPropertySet(CustomPropertyName.StackedGroupName))
			{
				stackGroupName = series[CustomPropertyName.StackedGroupName];
			}
			return stackGroupName;
		}
 
		/// <summary>
		/// Checks if series supports stacked group names.
		/// </summary>
		/// <param name="series">Series to check.</param>
		/// <returns>True if feature supported.</returns>
		static internal bool IsSeriesStackGroupNameSupported(Series series)
		{
			if( series.ChartType == SeriesChartType.StackedColumn ||
				series.ChartType == SeriesChartType.StackedColumn100 ||
				series.ChartType == SeriesChartType.StackedBar ||
				series.ChartType == SeriesChartType.StackedBar100)
			{
				return true;
			}
			return false;
		}
 
 
 
		/// <summary>
		/// Draw Stacked Column labels.
		/// </summary>
		/// <param name="common">Chart common elements.</param>
		/// <param name="graph">Chart Graphics.</param>
		/// <param name="area">Chart area the series belongs to.</param>
		/// <param name="point">Data point.</param>
		/// <param name="pointIndex">Data point index.</param>
		/// <param name="series">Data series.</param>
		/// <param name="rectangle">Column rectangle.</param>
		public void DrawLabels(
			CommonElements common, 
			ChartGraphics graph, 
			ChartArea area, 
			DataPoint point, 
			int pointIndex, 
			Series series, 
			RectangleF rectangle )
		{
			// Label text format
            using (StringFormat format = new StringFormat())
            {
                format.Alignment = StringAlignment.Center;
                format.LineAlignment = StringAlignment.Center;
 
                // Disable the clip region
                Region oldClipRegion = graph.Clip;
                graph.Clip = new Region();
 
                if (point.IsValueShownAsLabel || point.Label.Length > 0)
                {
                    // Get label text
                    string text;
                    if (point.Label.Length == 0)
                    {
                        // Round Y values for 100% stacked area
                        double pointLabelValue = GetYValue(common, area, series, point, pointIndex, 0);
                        if (this.hundredPercentStacked && point.LabelFormat.Length == 0)
                        {
                            pointLabelValue = Math.Round(pointLabelValue, 2);
                        }
 
                        text = ValueConverter.FormatValue(
                            series.Chart,
                            point,
                            point.Tag,
                            pointLabelValue,
                            point.LabelFormat,
                            series.YValueType,
                            ChartElementType.DataPoint);
                    }
                    else
                    {
                        text = point.ReplaceKeywords(point.Label);
                    }
 
                    // Calculate label position
                    PointF labelPosition = PointF.Empty;
                    labelPosition.X = rectangle.X + rectangle.Width / 2f;
                    labelPosition.Y = rectangle.Y + rectangle.Height / 2f;
 
                    // Get text angle
                    int textAngle = point.LabelAngle;
 
                    // Check if text contains white space only
                    if (text.Trim().Length != 0)
                    {
                        SizeF sizeFont = SizeF.Empty;
 
 
                        // Check if Smart Labels are enabled
                        if (series.SmartLabelStyle.Enabled)
                        {
                            sizeFont = graph.GetRelativeSize(
                                graph.MeasureString(
                                text,
                                point.Font,
                                new SizeF(1000f, 1000f),
                                StringFormat.GenericTypographic));
                            // Force some SmartLabelStyle settings for column chart
                            bool oldMarkerOverlapping = series.SmartLabelStyle.IsMarkerOverlappingAllowed;
                            LabelAlignmentStyles oldMovingDirection = series.SmartLabelStyle.MovingDirection;
                            series.SmartLabelStyle.IsMarkerOverlappingAllowed = true;
 
                            // Change default moving direction
                            if (series.SmartLabelStyle.MovingDirection == (LabelAlignmentStyles.Top | LabelAlignmentStyles.Bottom | LabelAlignmentStyles.Right | LabelAlignmentStyles.Left | LabelAlignmentStyles.TopLeft | LabelAlignmentStyles.TopRight | LabelAlignmentStyles.BottomLeft | LabelAlignmentStyles.BottomRight))
                            {
                                series.SmartLabelStyle.MovingDirection = LabelAlignmentStyles.Bottom | LabelAlignmentStyles.Top;
                            }
 
                            // Adjust label position using SmartLabelStyle algorithm
                            labelPosition = area.smartLabels.AdjustSmartLabelPosition(
                                common,
                                graph,
                                area,
                                series.SmartLabelStyle,
                                labelPosition,
                                sizeFont,
                                format,
                                labelPosition,
                                new SizeF(0f, 0f),
                                LabelAlignmentStyles.Center);
 
                            // Restore forced values
                            series.SmartLabelStyle.IsMarkerOverlappingAllowed = oldMarkerOverlapping;
                            series.SmartLabelStyle.MovingDirection = oldMovingDirection;
 
                            // Smart labels always use 0 degrees text angle
                            textAngle = 0;
                        }
 
 
 
                        // Draw label
                        if (!labelPosition.IsEmpty)
                        {
                            // Fix the .Net issue that text looks shifted to the left.
                            PointF absPosition = graph.GetAbsolutePoint(labelPosition);
                            if (graph.TextRenderingHint != TextRenderingHint.AntiAlias)
                            {
                                absPosition.X = (float)Math.Ceiling(absPosition.X) + 1f;
                                labelPosition = graph.GetRelativePoint(absPosition);
                            }
 
                            // Measure string
                            if (sizeFont.IsEmpty)
                            {
                                sizeFont = graph.GetRelativeSize(
                                    graph.MeasureString(
                                    text,
                                    point.Font,
                                    new SizeF(1000f, 1000f),
                                    StringFormat.GenericTypographic));
                            }
 
                            // Get label background position
                            RectangleF labelBackPosition = RectangleF.Empty;
                            SizeF sizeLabel = new SizeF(sizeFont.Width, sizeFont.Height);
                            sizeLabel.Height += sizeFont.Height / 8;
                            sizeLabel.Width += sizeLabel.Width / text.Length;
                            labelBackPosition = new RectangleF(
                                labelPosition.X - sizeLabel.Width / 2,
                                labelPosition.Y - sizeLabel.Height / 2 - sizeFont.Height / 10,
                                sizeLabel.Width,
                                sizeLabel.Height);
 
                            // Draw label text
                            using (Brush brush = new SolidBrush(point.LabelForeColor))
                            {
 
                                graph.DrawPointLabelStringRel(
                                    common,
                                    text,
                                    point.Font,
                                    brush,
                                    labelPosition,
                                    format,
                                    textAngle,
                                    labelBackPosition,
                                    point.LabelBackColor,
                                    point.LabelBorderColor,
                                    point.LabelBorderWidth,
                                    point.LabelBorderDashStyle,
                                    series,
                                    point,
                                    pointIndex);
                            }
                        }
                    }
                }
 
                // Restore old clip region
                graph.Clip = oldClipRegion;
            }
		}
 
		#endregion
 
		#region Y values 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.  Set to -1 to get the height.</param>
		/// <returns>Y value of the point.</returns>
		virtual public double GetYValue(CommonElements common, ChartArea area, Series series, DataPoint point, int pointIndex, int yValueIndex)
		{
			double	yValue = double.NaN;
 
			// Calculate stacked column Y value for 2D chart
			if(area.Area3DStyle.Enable3D == false || yValueIndex == -2)
			{
				return point.YValues[0];
			}
 
			// Get point Height if pointIndex == -1
			if(yValueIndex == -1)
			{
				Axis	vAxis = area.GetAxis(AxisName.Y, series.YAxisType, series.YSubAxisName);
				double	barZeroValue = vAxis.Crossing;
				yValue = GetYValue(common, area, series, point, pointIndex, 0);
				if( yValue >= 0 )
				{
					if(!double.IsNaN(prevPosY))
					{
						barZeroValue = prevPosY;
					}
				}
				else
				{
					if(!double.IsNaN(prevNegY))
					{
						barZeroValue = prevNegY;
					}
				}
 
				return yValue - barZeroValue;
			}
 
			// Loop through all series
			prevPosY = double.NaN;
			prevNegY = double.NaN;
			foreach(Series ser in common.DataManager.Series)
			{
				// Check series of the current chart type & area
				if(String.Compare(series.ChartArea, ser.ChartArea, StringComparison.Ordinal) == 0 &&
                    String.Compare(series.ChartTypeName, ser.ChartTypeName, StringComparison.OrdinalIgnoreCase) == 0 && 
					ser.IsVisible())
				{
 
					// Check if series belongs to the current group name
					string seriesStackGroupName = StackedColumnChart.GetSeriesStackGroupName(ser);
					if(this.stackGroupNameUsed && 
						seriesStackGroupName != this.currentStackGroup)
					{
						continue;
					}
 
 
 
					if(double.IsNaN(yValue))
					{
						yValue = ser.Points[pointIndex].YValues[0];
					}
					else
					{
						yValue = ser.Points[pointIndex].YValues[0];
						if(yValue >= 0.0 && !double.IsNaN(prevPosY))
						{
							yValue += prevPosY;
						}
						if(yValue < 0.0 && !double.IsNaN(prevNegY))
						{
							yValue += prevNegY;
						}
					}
 
					// Exit loop when current series was found
                    if (String.Compare(series.Name, ser.Name, StringComparison.Ordinal) == 0)
					{
						break;
					}
 
					// Save previous value
					if(yValue >= 0.0)
					{
						prevPosY = yValue;
					}
 
					if(yValue < 0.0)
					{
						prevNegY = yValue;
					}
				}
			}
			
			return yValue;
		}
 
		#endregion
 
		#region 3D Painting and Selection method
 
		/// <summary>
		/// This method recalculates size of the Columns. This method is used 
		/// from Paint or Select method in 3D space.
		/// </summary>
		/// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
		/// <param name="graph">The Chart Graphics objec.t</param>
		/// <param name="common">The Common elements object.</param>
		/// <param name="area">Chart area for this chart.</param>
		/// <param name="labels">Labels drawing mode.</param>
		/// <param name="seriesToDraw">Chart series to draw.</param>
		private void ProcessChartType3D( 
			bool selection, 
			ChartGraphics graph, 
			CommonElements common, 
			ChartArea area, 
			bool labels, 
			Series seriesToDraw )
		{
			// Labels & markers are drawn with the data points in the first iteration
			if(labels && !selection)
			{
				return;
			}
 
			// Get list of series to draw
			List<string> typeSeries = null;
 
 
			// Get all series names that belong the same cluster
			typeSeries = area.GetClusterSeriesNames(seriesToDraw.Name);
 
 
			//************************************************************
			//** Get order of data points drawing
			//************************************************************
			ArrayList	dataPointDrawingOrder = area.GetDataPointDrawingOrder(typeSeries, this, selection, COPCoordinates.X | COPCoordinates.Y, null, 0, false);
 
 
			//************************************************************
			//** Loop through all data poins and draw them
			//************************************************************
			bool	drawLabels = false;
			foreach(object obj in dataPointDrawingOrder)
			{
				// Get point & series
				DataPoint3D	pointEx = (DataPoint3D) obj;
				DataPoint	point = pointEx.dataPoint;
				Series		ser = point.series;
 
 
				// Set current stack group name
				this.currentStackGroup = StackedColumnChart.GetSeriesStackGroupName(ser);
 
 
				// Reset pre-calculated point position
				point.positionRel = new PointF(float.NaN, float.NaN);
 
				// Set active vertical/horizontal axis
				Axis	vAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
				Axis	hAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
 
				// Get point bar drawing style
				BarDrawingStyle	barDrawingStyle = ChartGraphics.GetBarDrawingStyle(point);
		
				// All cut points are darkened except of the first and last series
				float	topDarkening = 0.5f;
				float	bottomDarkening = 0.5f;
 
				// NOTE: Following code was replaced with the code below to fix issue #5391
//				if((string)typeSeries[typeSeries.Count - 1] == ser.Name)
//				{
//					topDarkening = 0f;
//				}
//				if((string)typeSeries[0] == ser.Name)
//				{
//					bottomDarkening = 0f;
//				}
 
				bool	firstVisibleSeries = true;
				bool	lastVisibleSeries = false;
				for(int seriesIndex = 0; seriesIndex < typeSeries.Count; seriesIndex++)
				{
					// Get series object
					Series currentSeries = common.DataManager.Series[seriesIndex];
 
					// Check if it is a first series with non-zero Y value
					if(firstVisibleSeries)
					{
						// Make series has non zero vallue
						if(pointEx.index <= currentSeries.Points.Count &&
							currentSeries.Points[pointEx.index - 1].YValues[0] != 0.0)
						{
							firstVisibleSeries = false;
							if(currentSeries.Name == ser.Name)
							{
								bottomDarkening = 0f;
							}
						}
					}
 
					// Check if it is a last series with non-zero Y value
					if(currentSeries.Name == ser.Name)
					{
						lastVisibleSeries = true;
					}
					else if(pointEx.index <= currentSeries.Points.Count &&
						currentSeries.Points[pointEx.index - 1].YValues[0] != 0.0)
					{
						lastVisibleSeries = false;
					}
				}
 
				// Remove darkenning from the last series in the group
				if(lastVisibleSeries)
				{
					topDarkening = 0f;
				}
 
 
 
				// If stacked groups are used remove darkenning from the
				// first/last series in the group
				if(area.StackGroupNames != null &&
                    area.StackGroupNames.Count > 1 &&
					area.Area3DStyle.IsClustered)
				{
					// Get series group name
					string groupName = StackedColumnChart.GetSeriesStackGroupName(ser);
					
					// Iterate through all series in the group
					bool	firstSeries = true;
					bool	lastSeries = false;
					foreach(string seriesName in typeSeries)
					{
						Series currentSeries = common.DataManager.Series[seriesName];
						if(StackedColumnChart.GetSeriesStackGroupName(currentSeries) == groupName)
						{
							// check if first seris
							if(firstSeries)
							{
								if(pointEx.index < currentSeries.Points.Count &&
									currentSeries.Points[pointEx.index - 1].YValues[0] != 0.0)
								{
									firstSeries = false;
									if(seriesName == ser.Name)
									{
										bottomDarkening = 0f;
									}
 
								}
							}
 
							// check if last series
							if(seriesName == ser.Name)
							{
								lastSeries = true;
							}
							else if(pointEx.index < currentSeries.Points.Count &&
						currentSeries.Points[pointEx.index - 1].YValues[0] != 0.0)
							{
								lastSeries = false;
							}
						}
					}
 
					// Remove darkenning from the last series in the group
					if(lastSeries)
					{
						topDarkening = 0f;
					}
				}
 
 
 
				// Check if value is inside plotting area
				double	yValue = GetYValue(common, area, ser, pointEx.dataPoint, pointEx.index - 1, 0);
				
				// Set Start position for a column
				double	barZeroValue = yValue - GetYValue(common, area, ser, pointEx.dataPoint, pointEx.index - 1, -1);
 
				// Convert values if logarithmic axis is used
				yValue = vAxis.GetLogValue(yValue);
				barZeroValue = vAxis.GetLogValue(barZeroValue);
 
				if( barZeroValue > vAxis.ViewMaximum )
				{
					topDarkening = 0.5f;
					barZeroValue = vAxis.ViewMaximum;
				}
				else if( barZeroValue < vAxis.ViewMinimum )
				{
					bottomDarkening = 0.5f;
					barZeroValue = vAxis.ViewMinimum;
				}
				if( yValue > vAxis.ViewMaximum )
				{
					topDarkening = 0.5f;
					yValue = vAxis.ViewMaximum;
				}
				else if( yValue < vAxis.ViewMinimum )
				{
					bottomDarkening = 0.5f;
					yValue = vAxis.ViewMinimum;
				}
 
				// Recalculates Height position and zero position of Columns
				double	height = vAxis.GetLinearPosition(yValue);
				double	zero = vAxis.GetLinearPosition(barZeroValue);
			
				// Calculate column position
				RectangleF	rectSize = RectangleF.Empty;
				try
				{
					// Set the Column rectangle
					rectSize.X = (float)(pointEx.xPosition - pointEx.width/2);
					rectSize.Width = (float)(pointEx.width);
 
					// The top side of rectangle has always 
					// smaller value than a bottom value
					if( zero < height )
					{
						float temp = bottomDarkening;
						bottomDarkening = topDarkening;
						topDarkening = temp;
 
						rectSize.Y = (float)zero;
						rectSize.Height = (float)height - rectSize.Y;
					}
					else
					{
						rectSize.Y = (float)height;
						rectSize.Height = (float)zero - rectSize.Y;
					}
				}
				catch(OverflowException)
				{
					continue;
				}
 
				// Remeber pre-calculated point position
				point.positionRel = new PointF((float)pointEx.xPosition, rectSize.Top);
 
				// if data point is not empty
				if( point.IsEmpty )
				{
					continue;
				}
 
				// Painting mode
				// Check if column is completly out of the data scaleView
				double	xValue = (pointEx.indexedSeries) ? pointEx.index : point.XValue;
				xValue = hAxis.GetLogValue(xValue);
				if(xValue < hAxis.ViewMinimum || 
					xValue > hAxis.ViewMaximum ||
					(yValue < vAxis.ViewMinimum && barZeroValue < vAxis.ViewMinimum) ||
					(yValue > vAxis.ViewMaximum && barZeroValue > vAxis.ViewMaximum) )
				{
					continue;
				}
 
				// Check if column is partialy in the data scaleView
				bool	clipRegionSet = false;
				if(rectSize.Right <= area.PlotAreaPosition.X || rectSize.X >= area.PlotAreaPosition.Right)
				{
					continue;
				}
 
				if(rectSize.X < area.PlotAreaPosition.X)
				{
					rectSize.Width -= area.PlotAreaPosition.X - rectSize.X;
					rectSize.X = area.PlotAreaPosition.X;
				}
				if(rectSize.Right > area.PlotAreaPosition.Right)
				{
					rectSize.Width -= rectSize.Right - area.PlotAreaPosition.Right;
				}
				if(rectSize.Width < 0)
				{
					rectSize.Width = 0;
				}
				if(rectSize.Height == 0f || rectSize.Width == 0f)
				{
					continue;
				}
 
				// 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 Column rectangle
				using ( GraphicsPath rectPath = graph.Fill3DRectangle( 
					rectSize, 
					pointEx.zPosition,
					pointEx.depth,
					area.matrix3D,
					area.Area3DStyle.LightStyle,
					point.Color, 
					topDarkening,
					bottomDarkening,
					point.BorderColor, 
					point.BorderWidth, 
					point.BorderDashStyle, 
					barDrawingStyle,
					true,
					drawingOperationType))
                {
				    // End Svg Selection mode
				    graph.EndHotRegion( );
 
				    // Reset Clip Region
				    if(clipRegionSet)
				    {
					    graph.ResetClip();
				    }
 
				    if( common.ProcessModeRegions && !labels)
				    {
					    common.HotRegionsList.AddHotRegion( rectPath, false, graph, point, ser.Name, pointEx.index - 1 );
				    }
 
				    // Check if labels should be drawn
					    if( point.IsValueShownAsLabel || point.Label.Length > 0 )
				    {
					    drawLabels = true;
				    }
                }
			}
		
 
		
			//************************************************************
			//** Loop through all data poins and draw labels
			//************************************************************
			if(drawLabels)
			{
				foreach(object obj in dataPointDrawingOrder)
				{
					// Get point & series
					DataPoint3D	pointEx = (DataPoint3D) obj;
					DataPoint	point = pointEx.dataPoint;
					Series		ser = point.series;
 
					// Set active vertical/horizontal axis
					Axis	vAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
					Axis	hAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
 
					// Check if value is inside plotting area
					double	yValue = GetYValue(common, area, ser, pointEx.dataPoint, pointEx.index - 1, 0);
					if( yValue > vAxis.ViewMaximum )
					{
						yValue = vAxis.ViewMaximum;
					}
					if( yValue < vAxis.ViewMinimum )
					{
						yValue = vAxis.ViewMinimum;
					}
					yValue = vAxis.GetLogValue(yValue);
 
					// Recalculates Height position and zero position of Columns
					double	height = pointEx.yPosition;
 
					// Set Start position for a column
					double	barZeroValue = yValue - vAxis.GetLogValue(GetYValue(common, area, ser, pointEx.dataPoint, pointEx.index - 1, -1));
					double	zero = pointEx.height;
		
					// Calculate column position
					RectangleF	rectSize = RectangleF.Empty;
					try
					{
						// Set the Column rectangle
						rectSize.X = (float)(pointEx.xPosition - pointEx.width/2);
						rectSize.Width = (float)(pointEx.width);
 
						// The top side of rectangle has always 
						// smaller value than a bottom value
						if( zero < height )
						{
							rectSize.Y = (float)zero;
							rectSize.Height = (float)height - rectSize.Y;
						}
						else
						{
							rectSize.Y = (float)height;
							rectSize.Height = (float)zero - rectSize.Y;
						}
					}
					catch(OverflowException)
					{
						continue;
					}
 
					// if data point is not empty
					if( point.IsEmpty )
					{
						continue;
					}
 
					// Painting mode
					if( !selection )
					{
						// Check if column is completly out of the data scaleView
						double	xValue = (pointEx.indexedSeries) ? pointEx.index : point.XValue;
						xValue = hAxis.GetLogValue(xValue);
						if(xValue < hAxis.ViewMinimum || 
							xValue > hAxis.ViewMaximum ||
							(yValue < vAxis.ViewMinimum && barZeroValue < vAxis.ViewMinimum) ||
							(yValue > vAxis.ViewMaximum && barZeroValue > vAxis.ViewMaximum) )
						{
							continue;
						}
				
						// Draw labels
						DrawLabels3D( common, graph, area, pointEx, pointEx.index - 1, ser, rectSize );
 
					}
				}
			}
		}
 
		/// <summary>
		/// Draw Stacked Column labels.
		/// </summary>
		/// <param name="common">Chart common elements.</param>
		/// <param name="graph">Chart Graphics.</param>
		/// <param name="area">Chart area the series belongs to.</param>
		/// <param name="pointEx">Data point.</param>
		/// <param name="pointIndex">Data point index.</param>
		/// <param name="series">Data series.</param>
		/// <param name="rectangle">Column rectangle.</param>
		internal void DrawLabels3D(
			CommonElements common, 
			ChartGraphics graph, 
			ChartArea area, 
			DataPoint3D pointEx, 
			int pointIndex, 
			Series series, 
			RectangleF rectangle )
		{
			DataPoint point = pointEx.dataPoint; 
 
			// Label text format
            using (StringFormat format = new StringFormat())
            {
                format.Alignment = StringAlignment.Center;
                format.LineAlignment = StringAlignment.Center;
 
                // Disable the clip region
                Region oldClipRegion = graph.Clip;
                graph.Clip = new Region();
 
                if (point.IsValueShownAsLabel || point.Label.Length > 0)
                {
                    // Get label text
                    string text;
                    if (point.Label.Length == 0)
                    {
                        // Round Y values for 100% stacked area
                        double pointLabelValue = GetYValue(common, area, series, point, pointIndex, -2);
                        if (this.hundredPercentStacked && point.LabelFormat.Length == 0)
                        {
                            pointLabelValue = Math.Round(pointLabelValue, 2);
                        }
 
                        text = ValueConverter.FormatValue(
                            series.Chart,
                            point,
                            point.Tag,
                            pointLabelValue,
                            point.LabelFormat,
                            series.YValueType,
                            ChartElementType.DataPoint);
                    }
                    else
                    {
                        text = point.ReplaceKeywords(point.Label);
                    }
 
                    // Calculate label position
                    PointF labelPosition = PointF.Empty;
                    labelPosition.X = rectangle.X + rectangle.Width / 2f;
                    labelPosition.Y = rectangle.Y + rectangle.Height / 2f;
 
                    // Transform coordinates
                    Point3D[] marker3DPosition = new Point3D[1];
                    marker3DPosition[0] = new Point3D(labelPosition.X, labelPosition.Y, (float)(pointEx.zPosition + pointEx.depth));
                    area.matrix3D.TransformPoints(marker3DPosition);
 
                    labelPosition.X = marker3DPosition[0].X;
                    labelPosition.Y = marker3DPosition[0].Y;
 
                    int textAngle = point.LabelAngle;
 
                    SizeF sizeFont = SizeF.Empty;
 
 
                    // Check if Smart Labels are enabled
                    if (series.SmartLabelStyle.Enabled)
                    {
                        sizeFont = graph.GetRelativeSize(
                            graph.MeasureString(
                            text,
                            point.Font,
                            new SizeF(1000f, 1000f),
                            StringFormat.GenericTypographic));
 
                        // Force some SmartLabelStyle settings for column chart
                        bool oldMarkerOverlapping = series.SmartLabelStyle.IsMarkerOverlappingAllowed;
                        LabelAlignmentStyles oldMovingDirection = series.SmartLabelStyle.MovingDirection;
                        series.SmartLabelStyle.IsMarkerOverlappingAllowed = true;
 
                        // Change default moving direction
                        if (series.SmartLabelStyle.MovingDirection == (LabelAlignmentStyles.Top | LabelAlignmentStyles.Bottom | LabelAlignmentStyles.Right | LabelAlignmentStyles.Left | LabelAlignmentStyles.TopLeft | LabelAlignmentStyles.TopRight | LabelAlignmentStyles.BottomLeft | LabelAlignmentStyles.BottomRight))
                        {
                            series.SmartLabelStyle.MovingDirection = LabelAlignmentStyles.Bottom | LabelAlignmentStyles.Top;
                        }
 
                        // Adjust label position using SmartLabelStyle algorithm
                        labelPosition = area.smartLabels.AdjustSmartLabelPosition(
                            common,
                            graph,
                            area,
                            series.SmartLabelStyle,
                            labelPosition,
                            sizeFont,
                            format,
                            labelPosition,
                            new SizeF(0f, 0f),
                            LabelAlignmentStyles.Center);
 
                        // Restore forced values
                        series.SmartLabelStyle.IsMarkerOverlappingAllowed = oldMarkerOverlapping;
                        series.SmartLabelStyle.MovingDirection = oldMovingDirection;
 
                        // Smart labels always use 0 degrees text angle
                        textAngle = 0;
                    }
 
 
 
                    if (!labelPosition.IsEmpty)
                    {
                        // Measure string
                        if (sizeFont.IsEmpty)
                        {
                            sizeFont = graph.GetRelativeSize(
                                graph.MeasureString(
                                text,
                                point.Font,
                                new SizeF(1000f, 1000f),
                                StringFormat.GenericTypographic));
                        }
 
                        // Get label background position
                        RectangleF labelBackPosition = RectangleF.Empty;
                        SizeF sizeLabel = new SizeF(sizeFont.Width, sizeFont.Height);
                        sizeLabel.Height += sizeFont.Height / 8;
                        sizeLabel.Width += sizeLabel.Width / text.Length;
                        labelBackPosition = new RectangleF(
                            labelPosition.X - sizeLabel.Width / 2,
                            labelPosition.Y - sizeLabel.Height / 2 - sizeFont.Height / 10,
                            sizeLabel.Width,
                            sizeLabel.Height);
 
                        // Draw label text
                        using (Brush brush = new SolidBrush(point.LabelForeColor))
                        {
                            graph.DrawPointLabelStringRel(
                                common,
                                text,
                                point.Font,
                                brush,
                                labelPosition,
                                format,
                                textAngle,
                                labelBackPosition,
                                point.LabelBackColor,
                                point.LabelBorderColor,
                                point.LabelBorderWidth,
                                point.LabelBorderDashStyle,
                                series,
                                point,
                                pointIndex);
                        }
                    }
                }
 
                // Restore old clip region
                graph.Clip = oldClipRegion;
            }
		}
		#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)
		{
		}
 
		#endregion
 
        #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
 
	}
}