File: Common\ChartTypes\StackedBarChart.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:		StackedBarChart.cs
//
//  Namespace:	DataVisualization.Charting.ChartTypes
//
//	Classes:	StackedBarChart, HundredPercentStackedBarChart
//
//  Purpose:	This class contains all necessary methods and 
//				properties for drawing and selection of the stacked 
//				bar	and hundred percent stacked bar charts. 
//				Every data point in the Stacked bar chart is 
//				represented with one rectangle. If there is 
//				more then one series with this chart type from 
//				same chart area, bars 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;
 
#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.Utilities;
	using System.Web.UI.DataVisualization.Charting.ChartTypes;
#endif
 
#endregion
 
#if Microsoft_CONTROL
	namespace System.Windows.Forms.DataVisualization.Charting.ChartTypes
#else
	namespace System.Web.UI.DataVisualization.Charting.ChartTypes
#endif
{
	/// <summary>
    /// HundredPercentStackedBarChart class extends StackedBarChart 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 HundredPercentStackedBarChart : StackedBarChart
	{
		#region Constructor
 
		/// <summary>
		/// Default constructor.
		/// </summary>
		public HundredPercentStackedBarChart()
		{
			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.OneHundredPercentStackedBar;}}
 
		/// <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 HundredPercentStackedBarChart 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;
 
			// Call base class painting
			base.Paint( graph, common, area, seriesToDraw );
		}
		
		#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>
		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];
 
 
			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>
    /// StackedBarChart class contains all the code necessary to draw 
    /// and hit test Stacked Bar chart. 
    /// </summary>
	internal class StackedBarChart : 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.StackedBar;}}
 
		/// <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>
		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>
		public bool RequireAxes	{ get{ return true;} }
 
		/// <summary>
		/// Chart type with two y values used for scale ( bubble chart type )
		/// </summary>
		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>
		public bool SupportLogarithmicAxes	{ get{ return true;} }
 
		/// <summary>
		/// True if chart type requires to switch the value (Y) axes position
		/// </summary>
		public bool SwitchValueAxes	{ get{ return true;} }
 
		/// <summary>
		/// True if chart series can be placed side-by-side.
		/// </summary>
		public bool SideBySideSeries { 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>
		public bool ZeroCrossing { get{ return true;} }
 
		/// <summary>
		/// True if each data point of a chart must be represented in the legend
		/// </summary>
		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>
		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>
		public LegendImageStyle GetLegendImageStyle(Series series)
		{
			return LegendImageStyle.Rectangle;
		}
 
		/// <summary>
		/// Number of supported Y value(s) per point 
		/// </summary>
		public int YValuesPerPoint{ get { return 1; } }
 
		#endregion
 
		#region Painting and selection methods
 
		/// <summary>
		/// Paint Stacked Bar 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
			this.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
			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 stacked 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="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 Stacked Bar 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 bar 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.X, ser.XAxisType, ser.XSubAxisName);
						Axis	hAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
 
						// Interval between bars
						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
                                bool sameInterval = false;
                                List<string> typeSeries = area.GetSeriesFromChartType(Name);
                                area.GetPointsInterval(typeSeries, vAxis.IsLogarithmic, vAxis.logarithmBase, true, out sameInterval);
 
                                // Special case when there is only one data point and date scale is used.
                                if (!double.IsNaN(vAxis.majorGrid.GetInterval()) && vAxis.majorGrid.GetIntervalType() != DateTimeIntervalType.NotSet)
                                {
                                    interval = ChartHelper.GetIntervalSize(vAxis.minimum, vAxis.majorGrid.GetInterval(), vAxis.majorGrid.GetIntervalType());
                                }
                                else
                                {
                                    interval = ChartHelper.GetIntervalSize(vAxis.minimum, vAxis.Interval, vAxis.IntervalType);
                                }
                            }
                            else
                            {
                                interval = area.GetPointsInterval(vAxis.IsLogarithmic, vAxis.logarithmBase);
                            }
						}
 
						// Calculates the width of bars.
						double width = ser.GetPointWidth(graph, vAxis, 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 Bar 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;
						
						// Axis is logarithmic
						if( hAxis.IsLogarithmic )
						{
							yValue = Math.Log( yValue, hAxis.logarithmBase );
						}
 
						// Recalculates Height position and zero position of bars
						double height = hAxis.GetLinearPosition( yValue );
 
						// 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 = vAxis.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 = vAxis.GetLogValue(xValue);
 
 
						// Set Start position for a bar
						double	barZeroValue;
						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 = hAxis.Crossing;
                            }
						}
						else if( GetYValue(common, area, ser, point, pointIndx, 0) >= 0 )
						{
							barZeroValue = PreviousPosY;
						}
						else
						{
							barZeroValue = PreviousNegY;
						}
						double zero = hAxis.GetPosition(barZeroValue);
 
						// Calculate bar position
						RectangleF	rectSize = RectangleF.Empty;
						try
						{
							// Set the bar rectangle
							rectSize.Y = (float)(xPosition - width/2);
							rectSize.Height = (float)(width);
 
							// The left side of rectangle has always 
							// smaller value than a right value
							if( zero < height )
							{
								rectSize.X = (float)zero;
								rectSize.Width = (float)height - rectSize.X;
							}
							else
							{
								rectSize.X = (float)height;
								rectSize.Width = (float)zero - rectSize.X;
							}
						}
						catch(OverflowException)
						{
							continue;
						}
 
						// Remeber pre-calculated point position
						point.positionRel = new PointF(rectSize.Right, (float)xPosition);
 
 
						// if data point is not empty
						if( point.IsEmpty )
						{
							continue;
						}
 
						// Axis is logarithmic
						if( hAxis.IsLogarithmic )
						{
							barZeroValue = Math.Log( barZeroValue, hAxis.logarithmBase );
						}
						
						// Check if column is completly out of the data scaleView
						bool skipPoint = false;
						if(xValue < vAxis.ViewMinimum || 
							xValue > vAxis.ViewMaximum ||
							(yValue < hAxis.ViewMinimum && barZeroValue < hAxis.ViewMinimum) ||
							(yValue > hAxis.ViewMaximum && barZeroValue > hAxis.ViewMaximum) )
						{
							skipPoint = true;
						}
 
						// ***************************************************
						// Painting mode
						// ***************************************************
						if(!skipPoint)
						{
							if( common.ProcessModePaint )
							{
								// Check if column is partialy in the data scaleView
								bool	clipRegionSet = false;
								if(rectSize.Y < area.PlotAreaPosition.Y || 
									rectSize.Bottom > area.PlotAreaPosition.Bottom ||
									rectSize.X < area.PlotAreaPosition.X || 
									rectSize.Right > area.PlotAreaPosition.Right)
								{
									// Set clipping region for line drawing 
									graph.SetClip( area.PlotAreaPosition.ToRectangleF() );
									clipRegionSet = true;
								}
 
								// Set shadow
								int shadowOffset = 0;
								if( shadow )
								{
									shadowOffset = ser.ShadowOffset;
								}
 
								if( !labels )
								{
									// Start Svg Selection mode
									graph.StartHotRegion( point );
 
									// Draw the bar 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),
										false);
 
									// End Svg Selection mode
									graph.EndHotRegion( );
								}
 
									// Draw labels 
								else
								{
									// Calculate label rectangle 
									RectangleF labelRect = new RectangleF(rectSize.Location, rectSize.Size);
                                    if (clipRegionSet && !ajaxScrollingEnabled)
									{
										labelRect.Intersect(area.PlotAreaPosition.ToRectangleF());
									}
 
									// Draw Labels
									DrawLabels( common, graph, area, point, pointIndx, ser, labelRect );
								}
 
								// Reset Clip Region
								if(clipRegionSet)
								{
									graph.ResetClip();
								}
							}
 
							// ***************************************************
							// Hot Regions Mode
							// ***************************************************
							if( common.ProcessModeRegions && !shadow && !labels)
							{
								common.HotRegionsList.AddHotRegion( rectSize, point, ser.Name, pointIndx );
 
								// Process labels and markers regions only if it was not done while painting
								if(labels && !common.ProcessModePaint)
								{
									DrawLabels( common, graph, area, point, pointIndx, ser, rectSize );
								}
							}
											
							// Call Paint event
							if( !selection )
							{
                                common.Chart.CallOnPostPaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
							}
						}
 
						// Axis is logarithmic
						if( hAxis.IsLogarithmic )
						{
							yValue = Math.Pow( hAxis.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>
		/// 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)
                {
                    // Round Y values for 100% stacked bar
                    double pointLabelValue = GetYValue(common, area, series, point, pointIndex, 0);
                    if (this.hundredPercentStacked && point.LabelFormat.Length == 0)
                    {
                        pointLabelValue = Math.Round(pointLabelValue, 2);
                    }
 
                    // Get label text
                    string text;
                    if (point.Label.Length == 0)
                    {
                        text = ValueConverter.FormatValue(
                            series.Chart,
                            point,
                            point.Tag,
                            pointLabelValue,
                            point.LabelFormat,
                            series.YValueType,
                            ChartElementType.DataPoint);
                    }
                    else
                    {
                        text = point.ReplaceKeywords(point.Label);
                    }
 
                    // Calculate 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)
                    {
                        //************************************************************
                        // Measure string
                        //************************************************************
                        SizeF sizeFont = graph.GetRelativeSize(
                            graph.MeasureString(
                            text,
                            point.Font,
                            new SizeF(1000f, 1000f),
                            StringFormat.GenericTypographic));
 
                        //************************************************************
                        // Check labels style custom properties 
                        //************************************************************
                        BarValueLabelDrawingStyle drawingStyle = BarValueLabelDrawingStyle.Center;
                        string valueLabelAttrib = "";
                        if (point.IsCustomPropertySet(CustomPropertyName.BarLabelStyle))
                        {
                            valueLabelAttrib = point[CustomPropertyName.BarLabelStyle];
                        }
                        else if (series.IsCustomPropertySet(CustomPropertyName.BarLabelStyle))
                        {
                            valueLabelAttrib = series[CustomPropertyName.BarLabelStyle];
                        }
 
                        if (valueLabelAttrib != null && valueLabelAttrib.Length > 0)
                        {
                            if (String.Compare(valueLabelAttrib, "Left", StringComparison.OrdinalIgnoreCase) == 0)
                                drawingStyle = BarValueLabelDrawingStyle.Left;
                            else if (String.Compare(valueLabelAttrib, "Right", StringComparison.OrdinalIgnoreCase) == 0)
                                drawingStyle = BarValueLabelDrawingStyle.Right;
                            else if (String.Compare(valueLabelAttrib, "Center", StringComparison.OrdinalIgnoreCase) == 0)
                                drawingStyle = BarValueLabelDrawingStyle.Center;
                            else if (String.Compare(valueLabelAttrib, "Outside", StringComparison.OrdinalIgnoreCase) == 0)
                                drawingStyle = BarValueLabelDrawingStyle.Outside;
                        }
 
                        //************************************************************
                        // Adjust label position based on the label drawing style
                        //************************************************************
                        if (drawingStyle == BarValueLabelDrawingStyle.Left)
                        {
                            labelPosition.X = rectangle.X + sizeFont.Width / 2f;
                        }
                        else if (drawingStyle == BarValueLabelDrawingStyle.Right)
                        {
                            labelPosition.X = rectangle.Right - sizeFont.Width / 2f;
                        }
                        else if (drawingStyle == BarValueLabelDrawingStyle.Outside)
                        {
                            labelPosition.X = rectangle.Right + sizeFont.Width / 2f;
                        }
 
 
                        // Check if Smart Labels are enabled
                        if (series.SmartLabelStyle.Enabled)
                        {
                            // Force some SmartLabelStyle settings for column chart
                            bool oldMarkerOverlapping = series.SmartLabelStyle.IsMarkerOverlappingAllowed;
                            LabelAlignmentStyles oldMovingDirection = series.SmartLabelStyle.MovingDirection;
                            series.SmartLabelStyle.IsMarkerOverlappingAllowed = true;
                            if (series.SmartLabelStyle.MovingDirection == (LabelAlignmentStyles.Top | LabelAlignmentStyles.Bottom | LabelAlignmentStyles.Right | LabelAlignmentStyles.Left | LabelAlignmentStyles.TopLeft | LabelAlignmentStyles.TopRight | LabelAlignmentStyles.BottomLeft | LabelAlignmentStyles.BottomRight))
                            {
                                series.SmartLabelStyle.MovingDirection = LabelAlignmentStyles.Left | LabelAlignmentStyles.Right;
                            }
 
                            // 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)
                        {
                            // 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);
 
 
 
                            // Adjust label background position that can be changed by the 
                            // Smart Labels algorithm
                            // NOTE: Fixes issue #4688
                            labelBackPosition = area.smartLabels.GetLabelPosition(
                                graph,
                                labelPosition,
                                sizeLabel,
                                format,
                                true);
 
 
 
                            // 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 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. 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 methods
 
		/// <summary>
		/// This method recalculates size of the stacked bars in 3D space. 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="drawLabels">True if labels must be drawn.</param>
		/// <param name="seriesToDraw">Chart series to draw.</param>
		private void ProcessChartType3D( 
			bool selection, 
			ChartGraphics graph, 
			CommonElements common, 
			ChartArea area, 
			bool drawLabels, 
			Series seriesToDraw )
		{
 
			// 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, 
				new BarPointsDrawingOrderComparer(area, selection, COPCoordinates.X | COPCoordinates.Y),
				0,
				false);
 
 
			//************************************************************
			//** Loop through all data poins and draw them
			//************************************************************
			if(!drawLabels)
			{
				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 horizontal/vertical axis
					Axis	vAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
					Axis	hAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
 
					// Get point bar drawing style
					BarDrawingStyle	barDrawingStyle = ChartGraphics.GetBarDrawingStyle(point);
		
					// All cut points are darkened except of the first and last series
					float	rightDarkening = 0.5f;
					float	leftDarkening = 0.5f;
 
					// NOTE: Following code was replaced with the code below to fix issue #5391
//					if((string)typeSeries[typeSeries.Count - 1] == ser.Name)
//					{
//						leftDarkening = 0f;
//					}
//					if((string)typeSeries[0] == ser.Name)
//					{
//						rightDarkening = 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)
								{
									rightDarkening = 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)
					{
						leftDarkening = 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)
								{
									// Make series has non zero vallue
									if(pointEx.index < currentSeries.Points.Count &&
										currentSeries.Points[pointEx.index - 1].YValues[0] != 0.0)
									{
										firstSeries = false;
										if(seriesName == ser.Name)
										{
											rightDarkening = 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)
						{
							leftDarkening = 0f;
						}
					}
 
 
 
					// Change Y value if Bar is out of plot area
					double	yValue = GetYValue(common, area, ser, pointEx.dataPoint, pointEx.index - 1, 0);
 
					// Set Start position for a bar
					double	barZeroValue = yValue - GetYValue(common, area, ser, pointEx.dataPoint, pointEx.index - 1, -1);
 
					// Convert values if logarithmic axis is used
					yValue = hAxis.GetLogValue(yValue);
					barZeroValue = hAxis.GetLogValue(barZeroValue);
 
					if( barZeroValue > hAxis.ViewMaximum )
					{
						leftDarkening = 0.5f;
						barZeroValue = hAxis.ViewMaximum;
					}
					else if( barZeroValue < hAxis.ViewMinimum )
					{
						rightDarkening = 0.5f;
						barZeroValue = hAxis.ViewMinimum;
					}
					if( yValue > hAxis.ViewMaximum )
					{
						leftDarkening = 0.5f;
						yValue = hAxis.ViewMaximum;
					}
					else if( yValue < hAxis.ViewMinimum )
					{
						rightDarkening = 0.5f;
						yValue = hAxis.ViewMinimum;
					}
				
					// Recalculates Height position and zero position of bars
					double	height = hAxis.GetLinearPosition(yValue);
					double	zero = hAxis.GetLinearPosition(barZeroValue);
 
					// Set x position
					double	xValue = (pointEx.indexedSeries) ? pointEx.index : point.XValue;
					xValue = vAxis.GetLogValue(xValue);
 
 
					// Calculate bar position
					RectangleF	rectSize = RectangleF.Empty;
					try
					{
						// Set the bar rectangle
						rectSize.Y = (float)(pointEx.xPosition - pointEx.width/2);
						rectSize.Height = (float)(pointEx.width);
 
						// The left side of rectangle has always 
						// smaller value than a right value
						if( zero < height )
						{
							float temp = leftDarkening;
							leftDarkening = rightDarkening;
							rightDarkening = temp;
 
							rectSize.X = (float)zero;
							rectSize.Width = (float)height - rectSize.X;
						}
						else
						{
							rectSize.X = (float)height;
							rectSize.Width = (float)zero - rectSize.X;
						}
					}
					catch(OverflowException)
					{
						continue;
					}
 
					// Remeber pre-calculated point position
					point.positionRel = new PointF(rectSize.Right, (float)pointEx.xPosition);
 
					// if data point is not empty
					if( point.IsEmpty )
					{
						continue;
					}
 
					GraphicsPath rectPath = null;
			
					// Check if column is completly out of the data scaleView
					if(xValue < vAxis.ViewMinimum || 
						xValue > vAxis.ViewMaximum ||
						(yValue < hAxis.ViewMinimum && barZeroValue < hAxis.ViewMinimum) ||
						(yValue > hAxis.ViewMaximum && barZeroValue > hAxis.ViewMaximum) )
					{
						continue;
					}
 
					// Check if column is partialy in the data scaleView
					bool	clipRegionSet = false;
					if(rectSize.Bottom <= area.PlotAreaPosition.Y || rectSize.Y >= area.PlotAreaPosition.Bottom)
					{
						continue;
					}
					if(rectSize.Y < area.PlotAreaPosition.Y)
					{
						rectSize.Height -= area.PlotAreaPosition.Y - rectSize.Y;
						rectSize.Y = area.PlotAreaPosition.Y;
					}
					if(rectSize.Bottom > area.PlotAreaPosition.Bottom)
					{
						rectSize.Height -= rectSize.Bottom - area.PlotAreaPosition.Bottom;
					}
					if(rectSize.Height < 0)
					{
						rectSize.Height = 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 Bar rectangle
					rectPath = graph.Fill3DRectangle( 
						rectSize, 
						pointEx.zPosition,
						pointEx.depth,
						area.matrix3D,
						area.Area3DStyle.LightStyle,
						point.Color, 
						rightDarkening,
						leftDarkening,
						point.BorderColor, 
						point.BorderWidth, 
						point.BorderDashStyle, 
						barDrawingStyle,
						false,
						drawingOperationType);
 
					// End Svg Selection mode
					graph.EndHotRegion( );
 
					// Reset Clip Region
					if(clipRegionSet)
					{
						graph.ResetClip();
					}
 
					if( common.ProcessModeRegions && !drawLabels)
					{
						common.HotRegionsList.AddHotRegion(
							rectPath,
							false,
							graph,
							point,
							ser.Name,
							pointEx.index - 1
							);
					}
                    if (rectPath != null)
                    {
                        rectPath.Dispose();
                    }
				}
			}
 
			//************************************************************
			//** 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 horizontal/vertical axis
					Axis	vAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
					Axis	hAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
		
					// Change Y value if Bar is out of plot area
					double	yValue = GetYValue(common, area, ser, pointEx.dataPoint, pointEx.index - 1, 0);
					
					// Axis is logarithmic
					if( hAxis.IsLogarithmic )
					{
						yValue = Math.Log( yValue, hAxis.logarithmBase );
					}
 
					// Recalculates Height position and zero position of bars
					double height = pointEx.yPosition;;
 
					// Set x position
					double	xValue = (pointEx.indexedSeries) ? pointEx.index : point.XValue;
 
					// Set Start position for a bar
					double	barZeroValue = yValue - GetYValue(common, area, ser, pointEx.dataPoint, pointEx.index - 1, -1);
					double zero = pointEx.height;
 
					// Calculate bar position
					RectangleF	rectSize = RectangleF.Empty;
					try
					{
						// Set the bar rectangle
						rectSize.Y = (float)(pointEx.xPosition - pointEx.width/2);
						rectSize.Height = (float)(pointEx.width);
 
						// The left side of rectangle has always 
						// smaller value than a right value
						if( zero < height )
						{
							rectSize.X = (float)zero;
							rectSize.Width = (float)height - rectSize.X;
						}
						else
						{
							rectSize.X = (float)height;
							rectSize.Width = (float)zero - rectSize.X;
						}
					}
					catch(OverflowException)
					{
						continue;
					}
 
					// if data point is not empty
					if( point.IsEmpty )
					{
						continue;
					}
			
					// Axis is logarithmic
					if( hAxis.IsLogarithmic )
					{
						barZeroValue = Math.Log( barZeroValue, hAxis.logarithmBase );
					}
 
					// Check if column is completly out of the data scaleView
					if(xValue < vAxis.ViewMinimum || 
						xValue > vAxis.ViewMaximum ||
						(yValue < hAxis.ViewMinimum && barZeroValue < hAxis.ViewMinimum) ||
						(yValue > hAxis.ViewMaximum && barZeroValue > hAxis.ViewMaximum) )
					{
						continue;
					}
 
					// Draw 3D labels
					DrawLabels3D( area, graph, common, rectSize, pointEx, ser, barZeroValue, height, pointEx.width, pointEx.index - 1);
				}	
			}
		}
 
		/// <summary>
		/// Draws labels in 3D.
		/// </summary>
		/// <param name="area">Chart area for this chart.</param>
		/// <param name="graph">The Chart Graphics object.</param>
		/// <param name="common">The Common elements object.</param>
		/// <param name="rectSize">Bar rectangle.</param>
		/// <param name="pointEx">Data point.</param>
		/// <param name="ser">Data series.</param>
		/// <param name="barStartPosition">The zero position or the bottom of bars.</param>
		/// <param name="barSize">The Height of bars.</param>
		/// <param name="width">The width of bars.</param>
		/// <param name="pointIndex">Point index.</param>
		private void DrawLabels3D( 
			ChartArea area,
			ChartGraphics graph, 
			CommonElements common, 
			RectangleF rectSize, 
			DataPoint3D pointEx, 
			Series ser, 
			double barStartPosition, 
			double barSize, 
			double width, 
			int pointIndex)
		{
			DataPoint point = pointEx.dataPoint;
 
			//************************************************************
			// Draw data point value label
			//************************************************************
			if(ser.IsValueShownAsLabel || point.IsValueShownAsLabel || point.Label.Length > 0)
			{
				// Label rectangle
				RectangleF rectLabel = RectangleF.Empty;
 
				// Label text format
                using (StringFormat format = new StringFormat())
                {
 
                    //************************************************************
                    // Get label text 
                    //************************************************************
                    string text;
                    if (point.Label.Length == 0)
                    {
                        // Round Y values for 100% stacked bar
                        double pointLabelValue = GetYValue(common, area, ser, point, pointIndex, -2);
                        if (this.hundredPercentStacked && point.LabelFormat.Length == 0)
                        {
                            pointLabelValue = Math.Round(pointLabelValue, 2);
                        }
 
                        text = ValueConverter.FormatValue(
                            ser.Chart,
                            point,
                            point.Tag,
                            pointLabelValue,
                            point.LabelFormat,
                            ser.YValueType,
                            ChartElementType.DataPoint);
                    }
                    else
                    {
                        text = point.ReplaceKeywords(point.Label);
                    }
 
				
                    //************************************************************
                    // Check labels style custom properties 
                    //************************************************************
                    BarValueLabelDrawingStyle drawingStyle = BarValueLabelDrawingStyle.Center;
                    string valueLabelAttrib = "";
                    if (point.IsCustomPropertySet(CustomPropertyName.BarLabelStyle))
                    {
                        valueLabelAttrib = point[CustomPropertyName.BarLabelStyle];
                    }
                    else if (ser.IsCustomPropertySet(CustomPropertyName.BarLabelStyle))
                    {
                        valueLabelAttrib = ser[CustomPropertyName.BarLabelStyle];
                    }
 
                    if (valueLabelAttrib != null && valueLabelAttrib.Length > 0)
                    {
                        if (String.Compare(valueLabelAttrib, "Left", StringComparison.OrdinalIgnoreCase) == 0)
                            drawingStyle = BarValueLabelDrawingStyle.Left;
                        else if (String.Compare(valueLabelAttrib, "Right", StringComparison.OrdinalIgnoreCase) == 0)
                            drawingStyle = BarValueLabelDrawingStyle.Right;
                        else if (String.Compare(valueLabelAttrib, "Center", StringComparison.OrdinalIgnoreCase) == 0)
                            drawingStyle = BarValueLabelDrawingStyle.Center;
                        else if (String.Compare(valueLabelAttrib, "Outside", StringComparison.OrdinalIgnoreCase) == 0)
                            drawingStyle = BarValueLabelDrawingStyle.Outside;
                    }
 
                    //************************************************************
                    // Make sure label fits. Otherwise change it style
                    //************************************************************
                    bool labelFit = false;
                    while (!labelFit)
                    {
                        // Label text format
                        format.Alignment = StringAlignment.Near;
                        format.LineAlignment = StringAlignment.Center;
 
                        // LabelStyle rectangle
                        if (barStartPosition < barSize)
                        {
                            rectLabel.X = rectSize.Right;
                            rectLabel.Width = area.PlotAreaPosition.Right - rectSize.Right;
                        }
                        else
                        {
                            rectLabel.X = area.PlotAreaPosition.X;
                            rectLabel.Width = rectSize.X - area.PlotAreaPosition.X;
                        }
 
                        // Adjust label rectangle
                        rectLabel.Y = rectSize.Y - (float)width / 2F;
                        rectLabel.Height = rectSize.Height + (float)width;
 
                        // Adjust label position depending on the drawing style
                        if (drawingStyle == BarValueLabelDrawingStyle.Left)
                        {
                            rectLabel = rectSize;
                            format.Alignment = StringAlignment.Near;
                        }
                        else if (drawingStyle == BarValueLabelDrawingStyle.Center)
                        {
                            rectLabel = rectSize;
                            format.Alignment = StringAlignment.Center;
                        }
                        else if (drawingStyle == BarValueLabelDrawingStyle.Right)
                        {
                            rectLabel = rectSize;
                            format.Alignment = StringAlignment.Far;
                        }
 
                        // Reversed string alignment
                        if (barStartPosition >= barSize)
                        {
                            if (format.Alignment == StringAlignment.Far)
                                format.Alignment = StringAlignment.Near;
                            else if (format.Alignment == StringAlignment.Near)
                                format.Alignment = StringAlignment.Far;
                        }
 
                        // Stacked bar chart can not change the BarValueLabelDrawingStyle trying to
                        // fit data point labels because it will cause label overlapping.
                        // NOTE: Code below is commented. Fixes issue #4687 - AG
                        labelFit = true;
 
                        //					// Make sure value label fits rectangle. 
                        //					SizeF valueTextSize = graph.MeasureStringRel(text, point.Font);
                        //					if(!labelSwitched && valueTextSize.Width > rectLabel.Width)
                        //					{
                        //						// Switch label style only once
                        //						labelSwitched = true;
                        //
                        //						// If text do not fit - try to switch between Outside/Inside drawing styles
                        //						if(drawingStyle == BarValueLabelDrawingStyle.Outside)
                        //						{
                        //							drawingStyle = BarValueLabelDrawingStyle.Right;
                        //						}
                        //						else
                        //						{
                        //							drawingStyle = BarValueLabelDrawingStyle.Outside;
                        //						}
                        //					}
                        //					else
                        //					{
                        //						labelFit = true;
                        //					}
                    }
 
                    //************************************************************
                    // Find text rotation center point
                    //************************************************************
 
                    // Measure string size
                    SizeF size = graph.MeasureStringRel(text, point.Font, new SizeF(rectLabel.Width, rectLabel.Height), format);
 
                    PointF rotationCenter = PointF.Empty;
                    if (format.Alignment == StringAlignment.Near)
                    { // Near
                        rotationCenter.X = rectLabel.X + size.Width / 2;
                    }
                    else if (format.Alignment == StringAlignment.Far)
                    { // Far
                        rotationCenter.X = rectLabel.Right - size.Width / 2;
                    }
                    else
                    { // Center
                        rotationCenter.X = (rectLabel.Left + rectLabel.Right) / 2;
                    }
 
                    if (format.LineAlignment == StringAlignment.Near)
                    { // Near
                        rotationCenter.Y = rectLabel.Top + size.Height / 2;
                    }
                    else if (format.LineAlignment == StringAlignment.Far)
                    { // Far
                        rotationCenter.Y = rectLabel.Bottom - size.Height / 2;
                    }
                    else
                    { // Center
                        rotationCenter.Y = (rectLabel.Bottom + rectLabel.Top) / 2;
                    }
 
                    // Reset string alignment to center point
                    format.Alignment = StringAlignment.Center;
                    format.LineAlignment = StringAlignment.Center;
 
 
                    //************************************************************
                    // Adjust label rotation angle
                    //************************************************************
                    int angle = point.LabelAngle;
 
                    // Get projection coordinates
                    Point3D[] rotationCenterProjection = new Point3D[] { 
																		 new Point3D(rotationCenter.X, rotationCenter.Y, pointEx.zPosition + pointEx.depth),
																		 new Point3D(rotationCenter.X - 20f, rotationCenter.Y, pointEx.zPosition + pointEx.depth) };
                    // Transform coordinates of text rotation point
                    area.matrix3D.TransformPoints(rotationCenterProjection);
 
                    // Adjust rotation point
                    rotationCenter = rotationCenterProjection[0].PointF;
 
                    // Adjust angle of the horisontal text
                    if (angle == 0 || angle == 180)
                    {
                        // Convert coordinates to absolute
                        rotationCenterProjection[0].PointF = graph.GetAbsolutePoint(rotationCenterProjection[0].PointF);
                        rotationCenterProjection[1].PointF = graph.GetAbsolutePoint(rotationCenterProjection[1].PointF);
 
                        // Calcuate axis angle
                        float angleXAxis = (float)Math.Atan(
                            (rotationCenterProjection[1].Y - rotationCenterProjection[0].Y) /
                            (rotationCenterProjection[1].X - rotationCenterProjection[0].X));
                        angleXAxis = (float)Math.Round(angleXAxis * 180f / (float)Math.PI);
                        angle += (int)angleXAxis;
                    }
 
                    SizeF sizeFont = SizeF.Empty;
 
 
                    // Check if Smart Labels are enabled
                    if (ser.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 = ser.SmartLabelStyle.IsMarkerOverlappingAllowed;
					LabelAlignmentStyles oldMovingDirection = ser.SmartLabelStyle.MovingDirection;
					ser.SmartLabelStyle.IsMarkerOverlappingAllowed = true;
					if(ser.SmartLabelStyle.MovingDirection == (LabelAlignmentStyles.Top | LabelAlignmentStyles.Bottom | LabelAlignmentStyles.Right | LabelAlignmentStyles.Left | LabelAlignmentStyles.TopLeft | LabelAlignmentStyles.TopRight | LabelAlignmentStyles.BottomLeft | LabelAlignmentStyles.BottomRight) )
					{
						ser.SmartLabelStyle.MovingDirection = LabelAlignmentStyles.Left | LabelAlignmentStyles.Right;
					}
 
                        // Adjust label position using SmartLabelStyle algorithm
                        rotationCenter = area.smartLabels.AdjustSmartLabelPosition(
                            common,
                            graph,
                            area,
                            ser.SmartLabelStyle,
                            rotationCenter,
                            sizeFont,
                            format,
                            rotationCenter,
                            new SizeF(0f, 0f),
                            LabelAlignmentStyles.Center);
 
					// Restore forced values
					ser.SmartLabelStyle.IsMarkerOverlappingAllowed = oldMarkerOverlapping;
					ser.SmartLabelStyle.MovingDirection = oldMovingDirection;
 
                        // Smart labels always use 0 degrees text angle
                        angle = 0;
                    }
 
 
 
 
 
                    //************************************************************
                    // Draw label
                    //************************************************************
                    if (!rotationCenter.IsEmpty)
                    {
                        // Measure string
                        if (sizeFont.IsEmpty)
                        {
                            sizeFont = graph.GetRelativeSize(
                                graph.MeasureString(
                                text,
                                point.Font,
                                new SizeF(1000f, 1000f),
                                new StringFormat(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(
                            rotationCenter.X - sizeLabel.Width / 2,
                            rotationCenter.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,
                                rotationCenter,
                                format,
                                angle,
                                labelBackPosition,
                                point.LabelBackColor,
                                point.LabelBorderColor,
                                point.LabelBorderWidth,
                                point.LabelBorderDashStyle,
                                ser,
                                point,
                                pointIndex);
                        }
                    }
                }
			}
		}
 
		#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)
		{
            // NOTE: Stacked Bar chart type do not support SmartLabelStyle feature
		}
 
		#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
    }
}