//============================================================================
//PieItem Class
//Copyright ?2005  Bob Kaye
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//=============================================================================
using System;
using System.Drawing;
using System.Collections;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.Runtime.Serialization;
using System.Security.Permissions;
namespace DrawGraph
{
	/// 
	/// A class representing a pie chart object comprised of one or more
	/// s.
	/// 
	///  Bob Kaye 
	///  $Revision: 1.31 $ $Date: 2007/01/25 07:56:09 $ 
	[Serializable]
	public class PieItem : CurveItem, ICloneable, ISerializable
	{
		#region Fields
		/*
		/// 
		/// Private field instance of the  class indicating whether
		/// the instance is displayed in 2D or 3D.(see )
		/// 
		private PieType pieType;
*/
		/// 
		/// Percentage (expressed as #.##) of 	radius  to
		/// which this  is to be displaced from the center.
		///   Displacement is done outward  along the radius
		/// bisecting the chord of this .  Maximum allowable value
		/// is 0.5.
		/// 
		private double _displacement;
		/// 
		/// A  which will customize the label display of this
		/// 
		/// 
		private TextObj _labelDetail;
		/// 
		/// Private	field	that stores the	 data for this
		/// .	 Use the public property  to
		/// access this value.
		/// 
		private Fill _fill;
		/// 
		/// Private	field	that stores the	 class that defines	the
		/// properties of the	border around	this . Use the public
		/// property	 to access this value.
		/// 
		private Border _border;
		/// 
		/// Private field that stores the absolute value of this  instance.
		/// Value will be set to zero if submitted value is less than zero. 
		/// 
		private double _pieValue;
		/// 
		/// An enum that specifies how each  for this  object 
		/// will be displayed.  Use the public property  to access this data.  
		/// Use enum .
		/// 
		private PieLabelType _labelType;
		/// 
		/// The point on the arc of this  representing the intersection of
		/// the arc and the explosion radius.
		/// 
		private PointF _intersectionPoint;
		/// 
		/// The bounding rectangle for this .
		/// 
		private RectangleF _boundingRectangle;
		/// 
		/// The formatted string for this 's label.  Formatting is
		/// done based on  the .
		/// 
		private string _labelStr;
		/// 
		/// The point at which the line between this  and its
		/// label bends to the horizontal.
		/// 
		private PointF _pivotPoint;
		/// 
		/// The point at the end of the line between	this  and 
		/// it's label (i.e. the beginning of the label display)
		/// 
		private PointF _endPoint;
		/// 
		/// Private field to hold the GraphicsPath of this  to be
		/// used for 'hit testing'.
		/// 
		private GraphicsPath _slicePath;
		/// 
		/// Private field which holds the angle (in degrees) at which the display of this 
		/// object will begin.
		/// 
		private float _startAngle;
		/// 
		///Private field which holds the length (in degrees) of the arc representing this  
		///object.
		/// 
		private float _sweepAngle;
		/// 
		///Private field which represents the angle (in degrees) of the radius along which this 
		///object will be displaced, if desired.
		/// 
		private float _midAngle;
		/// 
		///Private field which determines the number of decimal digits displayed to 
		///in a  label containing a value. 
		/// 
		private int _valueDecimalDigits;
		/// 
		///Private field which determines the number of decimal digits displayed 
		///in a  label containing a percent. 
		/// 
		private int _percentDecimalDigits;
		private static ColorSymbolRotator _rotator = new ColorSymbolRotator();
		#endregion
		#region Defaults
		/// 
		/// Specify the default property values for the  class.
		/// 
		public struct Default
		{
			/// 
			///Default  displacement.
			/// 
			public static double Displacement = 0;
			/// 
			/// The default pen width	to be used for drawing the	border around	the PieItem
			/// ( property). Units are points.
			/// 
			public static float BorderWidth = 1.0F;
			/// 
			/// The default fill mode for this PieItem ( property).
			/// 
			public static FillType FillType = FillType.Brush;
			/// 
			/// The default border mode for PieItem ( property).
			/// true to	display frame around PieItem, false otherwise
			/// 
			public static bool IsBorderVisible = true;
			/// 
			/// The default color for drawing	frames around	PieItem
			/// ( property).
			/// 
			public static Color BorderColor = Color.Black;
			/// 
			/// The default color for filling in	the PieItem
			/// (	property).
			/// 
			public static Color FillColor = Color.Red;
			/// 
			/// The default custom brush for filling in the PieItem.
			/// ( property).
			/// 
			public static Brush FillBrush = null;
			/// 
			///Default value for controlling  display.
			/// 
			public static bool isVisible = true;
			/// 
			/// Default value for .
			/// 
			public static PieLabelType LabelType = PieLabelType.Name;
			/// 
			/// The default font size for   entries
			/// ( property).  Units are
			/// in points (1/72 inch).
			/// 
			public static float FontSize = 10;
			/// 
			/// Default value for the number of decimal digits  
			/// to be displayed when   contains a value.
			/// 
			public static int ValueDecimalDigits = 0;
			/// 
			/// Default value for the number of decimal digits  
			/// to be displayed where  contains a percent.
			/// 
			public static int PercentDecimalDigits = 2;
		}
		#endregion Defaults
		#region PieItem Properties
		/// 
		/// Gets or sets the a value which determines the amount, if any, of this   
		/// displacement.
		/// 
		public double Displacement
		{
			get { return ( _displacement ); }
			set { _displacement = value > .5 ? .5 : value; }
		}
		/// 
		/// Gets or sets a path representing this 
		/// 
		public GraphicsPath SlicePath
		{
			get { return _slicePath; }
			//			set { this.slicePath = value; } 
		}
		/// 
		/// Gets or sets the  to be used
		/// for displaying this 's label.
		/// 
		public TextObj LabelDetail
		{
			get { return _labelDetail; }
			set { _labelDetail = value; }
		}
		/// 
		/// Gets or sets the  object so as to be able to modify
		/// its properties.
		/// 
		public Border Border
		{
			get { return ( _border ); }
			set { _border = value; }
		}
		/// 
		/// Gets or sets the  object which is used to fill the
		/// pie slice with color.
		/// 
		public Fill Fill
		{
			get { return _fill; }
			set { _fill = value; }
		}
		/// 
		/// Gets or sets the arc length (in degrees) of this .
		/// 
		private float SweepAngle
		{
			get { return _sweepAngle; }
			set { _sweepAngle = value; }
		}
		/// 
		/// Gets or sets the starting angle (in degrees) of this .
		/// 
		private float StartAngle
		{
			get { return ( _startAngle ); }
			set { _startAngle = value; }
		}
		/// 
		/// Gets or sets the angle (in degrees) of the radius along which 
		/// this  will be displaced.
		/// 
		private float MidAngle
		{
			get { return ( _midAngle ); }
			set { _midAngle = value; }
		}
		/// 
		///  Gets or sets the value of this .  
		///  Minimum value is 0. 
		/// 
		public double Value
		{
			get { return ( _pieValue ); }
			set { _pieValue = value > 0 ? value : 0; }
		}
		/// 
		/// Gets or sets the  to be used in displaying 
		///  labels.
		/// 
		public PieLabelType LabelType
		{
			get { return ( _labelType ); }
			set
			{
				_labelType = value;
				if ( value == PieLabelType.None )
					this.LabelDetail.IsVisible = false;
				else
					this.LabelDetail.IsVisible = true;
			}
		}
		/// 
		/// Gets or sets the number of decimal digits to be displayed in a  
		/// value label.
		/// 
		public int ValueDecimalDigits
		{
			get { return ( _valueDecimalDigits ); }
			set { _valueDecimalDigits = value; }
		}
		/// 
		/// Gets or sets the number of decimal digits to be displayed in a  
		/// percent label.
		/// 
		public int PercentDecimalDigits
		{
			get { return ( _percentDecimalDigits ); }
			set { _percentDecimalDigits = value; }
		}
		/*
		/// 
		/// Getsor sets enum  to be used	for drawing this .
		/// 
		public PieType PieType
		{
			get { return (this.pieType); }
			set { this.pieType = value; }
		}
 */
		/// 
		/// Gets a flag indicating if the Z data range should be included in the axis scaling calculations.
		/// 
		/// The parent  of this .
		/// 
		/// true if the Z data are included, false otherwise
		override internal bool IsZIncluded( GraphPane pane )
		{
			return false;
		}
		/// 
		/// Gets a flag indicating if the X axis is the independent axis for this 
		/// 
		/// The parent  of this .
		/// 
		/// true if the X axis is independent, false otherwise
		override internal bool IsXIndependent( GraphPane pane )
		{
			return true;
		}
		#endregion
		#region Constructors
		/// 
		/// Create a new , providing a gradient fill for the pie color.
		/// 
		/// The value associated with this  instance.
		/// The starting display color for the gradient  for this
		///  instance.
		/// The ending display color for the gradient  for this
		///  instance.
		/// The angle for the gradient .
		/// The amount this   instance will be 
		/// displaced from the center point.
		/// Text label for this  instance.
		public PieItem( double pieValue, Color color1, Color color2, float fillAngle,
						double displacement, string label )
			:
						this( pieValue, color1, displacement, label )
		{
			if ( !color1.IsEmpty && !color2.IsEmpty )
				_fill = new Fill( color1, color2, fillAngle );
		}
		/// 
		/// Create a new .
		/// 
		/// The value associated with this  instance.
		/// The display color for this  instance.
		/// The amount this   instance will be 
		/// displaced from the center point.
		/// Text label for this  instance.
		public PieItem( double pieValue, Color color, double displacement, string label )
			: base( label )
		{
			_pieValue = pieValue;
			_fill = new Fill( color.IsEmpty ? _rotator.NextColor : color );
			_displacement = displacement;
			_border = new Border( Default.BorderColor, Default.BorderWidth );
			_labelDetail = new TextObj();
			_labelDetail.FontSpec.Size = Default.FontSize;
			_labelType = Default.LabelType;
			_valueDecimalDigits = Default.ValueDecimalDigits;
			_percentDecimalDigits = Default.PercentDecimalDigits;
			_slicePath = null;
		}
		/// 
		/// Create a  new .
		/// 
		/// The value associated with this  instance.
		/// Text label for this  instance
		public PieItem( double pieValue, string label )
			:
			this( pieValue, _rotator.NextColor, Default.Displacement, label )
		{
		}
		/// 
		/// The Copy Constructor
		/// 
		/// The  object from which to copy
		public PieItem( PieItem rhs )
			: base( rhs )
		{
			_pieValue = rhs._pieValue;
			_fill = rhs._fill.Clone();
			this.Border = rhs._border.Clone();
			_displacement = rhs._displacement;
			_labelDetail = rhs._labelDetail.Clone();
			_labelType = rhs._labelType;
			_valueDecimalDigits = rhs._valueDecimalDigits;
			_percentDecimalDigits = rhs._percentDecimalDigits;
		}
		/// 
		/// Implement the  interface in a typesafe manner by just
		/// calling the typed version of 
		/// 
		/// A deep copy of this object
		object ICloneable.Clone()
		{
			return this.Clone();
		}
		/// 
		/// Typesafe, deep-copy clone method.
		/// 
		/// A new, independent copy of this class
		public PieItem Clone()
		{
			return new PieItem( this );
		}
		#endregion
		#region Serialization
		/// 
		/// Current schema value that defines the version of the serialized file
		/// 
		public const int schema2 = 10;
		/// 
		/// Constructor for deserializing objects
		/// 
		/// A  instance that defines the serialized data
		/// 
		/// A  instance that contains the serialized data
		/// 
		protected PieItem( SerializationInfo info, StreamingContext context )
			: base( info, context )
		{
			// The schema value is just a file version parameter.  You can use it to make future versions
			// backwards compatible as new member variables are added to classes
			int sch = info.GetInt32( "schema2" );
			_displacement = info.GetDouble( "displacement" );
			_labelDetail = (TextObj)info.GetValue( "labelDetail", typeof( TextObj ) );
			_fill = (Fill)info.GetValue( "fill", typeof( Fill ) );
			_border = (Border)info.GetValue( "border", typeof( Border ) );
			_pieValue = info.GetDouble( "pieValue" );
			_labelType = (PieLabelType)info.GetValue( "labelType", typeof( PieLabelType ) );
			_intersectionPoint = (PointF)info.GetValue( "intersectionPoint", typeof( PointF ) );
			_boundingRectangle = (RectangleF)info.GetValue( "boundingRectangle", typeof( RectangleF ) );
			_pivotPoint = (PointF)info.GetValue( "pivotPoint", typeof( PointF ) );
			_endPoint = (PointF)info.GetValue( "endPoint", typeof( PointF ) );
			_slicePath = (GraphicsPath)info.GetValue( "slicePath", typeof( GraphicsPath ) );
			_startAngle = (float)info.GetDouble( "startAngle" );
			_sweepAngle = (float)info.GetDouble( "sweepAngle" );
			_midAngle = (float)info.GetDouble( "midAngle" );
			_labelStr = info.GetString( "labelStr" );
			_valueDecimalDigits = info.GetInt32( "valueDecimalDigits" );
			_percentDecimalDigits = info.GetInt32( "percentDecimalDigits" );
		}
		/// 
		/// Populates a  instance with the data needed to serialize the target object
		/// 
		/// A  instance that defines the serialized data
		/// A  instance that contains the serialized data
		[SecurityPermissionAttribute( SecurityAction.Demand, SerializationFormatter = true )]
		public override void GetObjectData( SerializationInfo info, StreamingContext context )
		{
			base.GetObjectData( info, context );
			info.AddValue( "schema2", schema2 );
			info.AddValue( "displacement", _displacement );
			info.AddValue( "labelDetail", _labelDetail );
			info.AddValue( "fill", _fill );
			info.AddValue( "border", _border );
			info.AddValue( "pieValue", _pieValue );
			info.AddValue( "labelType", _labelType );
			info.AddValue( "intersectionPoint", _intersectionPoint );
			info.AddValue( "boundingRectangle", _boundingRectangle );
			info.AddValue( "pivotPoint", _pivotPoint );
			info.AddValue( "endPoint", _endPoint );
			info.AddValue( "slicePath", _slicePath );
			info.AddValue( "startAngle", _startAngle );
			info.AddValue( "sweepAngle", _sweepAngle );
			info.AddValue( "midAngle", _midAngle );
			info.AddValue( "labelStr", _labelStr );
			info.AddValue( "valueDecimalDigits", _valueDecimalDigits );
			info.AddValue( "percentDecimalDigits", _percentDecimalDigits );
		}
		#endregion
		#region Methods
		/// 
		/// Do all rendering associated with this  item to the specified
		///  device.  This method is normally only
		/// called by the Draw method of the parent 
		/// collection object.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// 
		/// A reference to the  object that is the parent or
		/// owner of this object.
		/// 
		/// Not used for rendering Piesparam>
		/// 
		/// The scaling factor to be used for rendering objects.  This is calculated and
		/// passed down by the parent  object using the
		///  method, and is used to proportionally adjust
		/// font sizes, etc. according to the actual size of the graph.
		/// 				
		override public void Draw( Graphics g, GraphPane pane, int pos, float scaleFactor )
		{
			if ( pane.Chart._rect.Width <= 0 && pane.Chart._rect.Height <= 0 )
			{
				//pane.PieRect = RectangleF.Empty;
				_slicePath = null;
			}
			else
			{
				//pane.PieRect = CalcPieRect( g, pane, scaleFactor, pane.ChartRect );
				CalcPieRect( g, pane, scaleFactor, pane.Chart._rect );
				_slicePath = new GraphicsPath();
				if ( !_isVisible )
					return;
				RectangleF tRect = _boundingRectangle;
				if ( tRect.Width >= 1 && tRect.Height >= 1 )
				{
					SmoothingMode sMode = g.SmoothingMode;
					g.SmoothingMode = SmoothingMode.AntiAlias;
					Fill tFill = _fill;
					Border tBorder = _border;
					if ( this.IsSelected )
					{
						tFill = Selection.Fill;
						tBorder = Selection.Border;
					}
					using ( Brush brush = tFill.MakeBrush( _boundingRectangle ) )
					{
						g.FillPie( brush, tRect.X, tRect.Y, tRect.Width, tRect.Height, this.StartAngle, this.SweepAngle );
						//add GraphicsPath for hit testing
						_slicePath.AddPie( tRect.X, tRect.Y, tRect.Width, tRect.Height,
							this.StartAngle, this.SweepAngle );
						if ( this.Border.IsVisible )
						{
							using ( Pen borderPen = tBorder.GetPen( pane, scaleFactor ) )
							{
								g.DrawPie( borderPen, tRect.X, tRect.Y, tRect.Width, tRect.Height,
									this.StartAngle, this.SweepAngle );
							}
						}
						if ( _labelType != PieLabelType.None )
							DrawLabel( g, pane, tRect, scaleFactor );
						//brush.Dispose();
					}
					g.SmoothingMode = sMode;
				}
			}
		}
		/// 
		/// Calculate the  that will be used to define the bounding rectangle of
		/// the Pie.
		/// 
		/// This rectangle always lies inside of the , and it is
		/// normally a square so that the pie itself is not oval-shaped.
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// 
		/// A reference to the  object that is the parent or
		/// owner of this object.
		/// 
		/// 
		/// The scaling factor to be used for rendering objects.  This is calculated and
		/// passed down by the parent  object using the
		///  method, and is used to proportionally adjust
		/// font sizes, etc. according to the actual size of the graph.
		/// 				
		/// The  (normally the )
		/// that bounds this pie.
		/// 
		public static RectangleF CalcPieRect( Graphics g, GraphPane pane, float scaleFactor, RectangleF chartRect )
		{
			//want to draw the largest pie possible within ChartRect
			//but want to leave  5% slack around the pie so labels will not overrun clip area
			//largest pie is limited by the smaller of ChartRect.height or ChartRect.width...
			//this rect (nonExplRect)has to be re-positioned so that it's in the center of ChartRect.
			//Where ChartRect is almost a square - low Aspect Ratio -, need to contract pieRect so that there's some
			//room for labels, if they're visible.
			double maxDisplacement = 0;
			RectangleF tempRect;   //= new RectangleF(0,0,0,0);
			RectangleF nonExplRect = chartRect;
			if ( pane.CurveList.IsPieOnly )
			{
				if ( nonExplRect.Width < nonExplRect.Height )
				{
					//create slack rect
					nonExplRect.Inflate( -(float)0.05F * nonExplRect.Height, -(float)0.05F * nonExplRect.Width );
					//get the difference between dimensions
					float delta = ( nonExplRect.Height - nonExplRect.Width ) / 2;
					//make a square	so we end up with circular pie
					nonExplRect.Height = nonExplRect.Width;
					//keep the center point  the same
					nonExplRect.Y += delta;
				}
				else
				{
					nonExplRect.Inflate( -(float)0.05F * nonExplRect.Height, -(float)0.05F * nonExplRect.Width );
					float delta = ( nonExplRect.Width - nonExplRect.Height ) / 2;
					nonExplRect.Width = nonExplRect.Height;
					nonExplRect.X += delta;
				}
				//check aspect ratio
				double aspectRatio = chartRect.Width / chartRect.Height;
				//make an adjustment in rect size,as aspect ratio varies
				if ( aspectRatio < 1.5 )
					nonExplRect.Inflate( -(float)( .1 * ( 1.5 / aspectRatio ) * nonExplRect.Width ),
											-(float)( .1 * ( 1.5 / aspectRatio ) * nonExplRect.Width ) );
				//modify the rect to determine if any of the labels need to be wrapped....
				//first see if there's any exploded slices and if so, what's the max displacement...
				//also, might as well get all the display params we can
				PieItem.CalculatePieChartParams( pane, ref maxDisplacement );
				if ( maxDisplacement != 0 )			 //need new rectangle if any slice exploded	
					CalcNewBaseRect( maxDisplacement, ref nonExplRect );
				foreach ( PieItem slice in pane.CurveList )
				{
					slice._boundingRectangle = nonExplRect;
					//if exploded, need to re-calculate rectangle for slice
					if ( slice.Displacement != 0 )
					{
						tempRect = nonExplRect;
						slice.CalcExplodedRect( ref tempRect );
						slice._boundingRectangle = tempRect;
					}
					//now get all the other slice specific drawing details, including need for wrapping label
					slice.DesignLabel( g, pane, slice._boundingRectangle, scaleFactor );
				}
			}
			return nonExplRect;
		}
		/// 
		/// Recalculate the bounding rectangle when a piee slice is displaced.
		/// 
		/// rectangle to be used for drawing exploded pie
		private void CalcExplodedRect( ref RectangleF explRect )
		{
			//pie exploded out along the slice bisector - modify upper left of bounding rect to account for displacement
			//keep height and width same
			explRect.X += (float)( this.Displacement * explRect.Width / 2 * Math.Cos( _midAngle * Math.PI / 180 ) );
			explRect.Y += (float)( this.Displacement * explRect.Height / 2 * Math.Sin( _midAngle * Math.PI / 180 ) );
		}
		/// 
		/// Calculate the values needed to properly display this .
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// maximum slice displacement
		private static void CalculatePieChartParams( GraphPane pane, ref double maxDisplacement )
		{
			string lblStr = " ";
			//loop thru slices and get total value and maxDisplacement
			double pieTotalValue = 0;
			foreach ( PieItem curve in pane.CurveList )
				if ( curve.IsPie )
				{
					pieTotalValue += curve._pieValue;
					if ( curve.Displacement > maxDisplacement )
						maxDisplacement = curve.Displacement;
				}
			double nextStartAngle = 0;
			//now loop thru and calculate the various angle values
			foreach ( PieItem curve in pane.CurveList )
			{
				lblStr = curve._labelStr;
				curve.StartAngle = (float)nextStartAngle;
				curve.SweepAngle = (float)( 360 * curve.Value / pieTotalValue );
				curve.MidAngle = curve.StartAngle + curve.SweepAngle / 2;
				nextStartAngle = curve._startAngle + curve._sweepAngle;
				PieItem.BuildLabelString( curve );
			}
		}
		/// 
		/// Render the label for this .
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// Bounding rectangle for this .
		/// 
		/// The scaling factor to be used for rendering objects.  This is calculated and
		/// passed down by the parent  object using the
		///  method, and is used to proportionally adjust
		/// font sizes, etc. according to the actual size of the graph.
		/// 				
		public void DrawLabel( Graphics g, GraphPane pane, RectangleF rect, float scaleFactor )
		{
			if ( !_labelDetail.IsVisible )
				return;
			using ( Pen labelPen = this.Border.GetPen( pane, scaleFactor ) )
			{
				//draw line from intersection point to pivot point -
				g.DrawLine( labelPen, _intersectionPoint, _pivotPoint );
				//draw horizontal line to move label away from pie...
				g.DrawLine( labelPen, _pivotPoint, _endPoint );
			}
			//draw the label (TextObj)
			_labelDetail.Draw( g, pane, scaleFactor );
		}
		/// 
		/// This method collects all the data relative to rendering this 's label.
		/// 
		/// 
		///  A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// The rectangle used for rendering this 
		/// 
		/// 
		/// The scaling factor to be used for rendering objects.  This is calculated and
		/// passed down by the parent  object using the
		///  method, and is used to proportionally adjust
		/// font sizes, etc. according to the actual size of the graph.
		/// 
		public void DesignLabel( Graphics g, GraphPane pane, RectangleF rect, float scaleFactor )
		{
			if ( !_labelDetail.IsVisible )
				return;
			_labelDetail.LayoutArea = new SizeF();
			//this.labelDetail.IsWrapped = false;
			//label line will come off the explosion radius and then pivot to the horizontal right or left,
			//dependent on position.. 
			//text will be at the end of horizontal segment...
			CalculateLinePoints( rect, _midAngle );
			//now get size of bounding rect for label
			SizeF size = _labelDetail.FontSpec.BoundingBox( g, _labelStr, scaleFactor );
			//how much room left for the label - most likely midangles for wrapping
			//Right - 315 -> 45 degrees
			//Bottom - 45 -> 135
			//Left - 135 -> 225
			//Top - 225 -> 315
			RectangleF chartRect = pane.Chart._rect;
			float fill = 0;
			if ( _midAngle > 315 || _midAngle <= 45 )
			{
				//correct by wrapping text
				fill = chartRect.X + chartRect.Width - _endPoint.X - 5;
				if ( size.Width > fill )
				{
					//need to wrap, so create label rectangle for overloaded DrawString - two rows, max
					_labelDetail.LayoutArea = new SizeF( fill, size.Height * 3.0F );
				}
			}
			if ( _midAngle > 45 && _midAngle <= 135 )
			{
				//correct by moving radial line toward one or the other end of the range
				fill = chartRect.Y + chartRect.Height - _endPoint.Y - 5;
				//is there enuf room for the label
				if ( size.Height / 2 > fill )
				{
					//no, so got to move explosion radius
					if ( _midAngle > 90 )	//move _label clockwise one-third of way to the end of the arc
						CalculateLinePoints( rect, _midAngle + ( _sweepAngle + _startAngle - _midAngle ) / 3 );
					else						//move _label counter-clockwise one-third of way to the start of the arc
						CalculateLinePoints( rect, _midAngle - ( _midAngle - ( _midAngle - _startAngle ) / 3 ) );
				}
			}
			if ( _midAngle > 135 && _midAngle <= 225 )
			{
				//wrap text 
				fill = _endPoint.X - chartRect.X - 5;
				//need to wrap, so create label rectangle for overloaded DrawString - two rows, max
				if ( size.Width > fill )
				{
					_labelDetail.LayoutArea = new SizeF( fill, size.Height * 3.0F );
				}
			}
			if ( _midAngle > 225 && _midAngle <= 315 )
			{
				//correct by moving radial line toward one or the other end of the range
				fill = _endPoint.Y - 5 - chartRect.Y;
				//is there enuf room for the label
				if ( size.Height / 2 > fill )
				{
					//no, so got to move explosion radius
					if ( _midAngle < 270 )	//move _label counter-clockwise one-third of way to the start of the arc
						CalculateLinePoints( rect, _midAngle - ( _sweepAngle + _startAngle - _midAngle ) / 3 );
					else						//move _label clockwise one-third of way to the end of the arc
						CalculateLinePoints( rect, _midAngle + ( _midAngle - _startAngle ) / 3 );
				}
			}
			//complete the location Detail info
			_labelDetail.Location.AlignV = AlignV.Center;
			_labelDetail.Location.CoordinateFrame = CoordType.PaneFraction;
			_labelDetail.Location.X = ( _endPoint.X - pane.Rect.X ) / pane.Rect.Width;
			_labelDetail.Location.Y = ( _endPoint.Y - pane.Rect.Y ) / pane.Rect.Height;
			_labelDetail.Text = _labelStr;
		}
		/// 
		/// 
		/// 
		/// 
		/// 
		private void CalculateLinePoints( RectangleF rect, double midAngle )
		{
			//get the point where the explosion radius intersects the this arc
			PointF rectCenter = new PointF( ( rect.X + rect.Width / 2 ), ( rect.Y + rect.Height / 2 ) );
			_intersectionPoint = new PointF( (float)( rectCenter.X + ( rect.Width / 2 * Math.Cos( ( midAngle ) * Math.PI / 180 ) ) ),
				(float)( rectCenter.Y + ( rect.Height / 2 * Math.Sin( ( midAngle ) * Math.PI / 180 ) ) ) );
			//draw line from intersection point to pivot point - length to be .05 * pieRect.Width pixels long
			_pivotPoint = new PointF( (float)( _intersectionPoint.X + .05 * rect.Width * Math.Cos( ( midAngle ) * Math.PI / 180 ) ),
				(float)( _intersectionPoint.Y + .05 * rect.Width * Math.Sin( ( midAngle ) * Math.PI / 180 ) ) );
			//add horizontal line to move label away from pie...length to be 5% of rect.Width
			//does line go to left or right....label alignment is to the opposite
			if ( _pivotPoint.X >= rectCenter.X )		//goes to right
			{
				_endPoint = new PointF( (float)( _pivotPoint.X + .05 * rect.Width ), _pivotPoint.Y );
				_labelDetail.Location.AlignH = AlignH.Left;
			}
			else
			{
				_endPoint = new PointF( (float)( _pivotPoint.X - .05 * rect.Width ), _pivotPoint.Y );
				_labelDetail.Location.AlignH = AlignH.Right;
			}
			_midAngle = (float)midAngle;
		}
		/// 
		/// Build the string that will be displayed as the slice label as determined by 
		/// .
		/// 
		/// reference to the 
		private static void BuildLabelString( PieItem curve )
		{
			//set up label string formatting
			NumberFormatInfo labelFormat = (NumberFormatInfo)NumberFormatInfo.CurrentInfo.Clone();
			labelFormat.NumberDecimalDigits = curve._valueDecimalDigits;
			labelFormat.PercentPositivePattern = 1;					//no space between number and % sign
			labelFormat.PercentDecimalDigits = curve._percentDecimalDigits;
			switch ( curve._labelType )
			{
				case PieLabelType.Value:
					curve._labelStr = curve._pieValue.ToString( "F", labelFormat );
					break;
				case PieLabelType.Percent:
					curve._labelStr = ( curve._sweepAngle / 360 ).ToString( "P", labelFormat );
					break;
				case PieLabelType.Name_Value:
					curve._labelStr = curve._label._text + ": " + curve._pieValue.ToString( "F", labelFormat );
					break;
				case PieLabelType.Name_Percent:
					curve._labelStr = curve._label._text + ": " + ( curve._sweepAngle / 360 ).ToString( "P", labelFormat );
					break;
				case PieLabelType.Name_Value_Percent:
					curve._labelStr = curve._label._text + ": " + curve._pieValue.ToString( "F", labelFormat ) +
						" (" + ( curve._sweepAngle / 360 ).ToString( "P", labelFormat ) + ")";
					break;
				case PieLabelType.Name:
					curve._labelStr = curve._label._text;
					break;
				case PieLabelType.None:
				default:
					break;
			}
		}
		/// 
		/// A method which calculates a new size for the bounding rectangle for the non-displaced 
		/// 's in the pie chart.  This method is called after it is found
		/// that at least one slice is displaced.
		/// 
		/// The biggest displacement among the s
		/// making up the pie chart.
		/// The current bounding rectangle
		private static void CalcNewBaseRect( double maxDisplacement, ref RectangleF baseRect )
		{
			//displacement expressed in terms of % of pie radius	...do not want exploded slice to 
			//go beyond nonExplRect, but want to maintain the same center point...therefore, got to 
			//reduce the diameter of the nonexploded pie by the alue of the displacement
			float xDispl = (float)( ( maxDisplacement * baseRect.Width ) );
			float yDispl = (float)( ( maxDisplacement * baseRect.Height ) );
			baseRect.Inflate( -(float)( ( xDispl / 10 ) ), -(float)( ( xDispl / 10 ) ) );
		}
		/// 
		/// Draw a legend key entry for this  at the specified location
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// 
		/// A reference to the  object that is the parent or
		/// owner of this object.
		/// 
		/// The  struct that specifies the
		/// location for the legend key
		/// 
		/// The scaling factor to be used for rendering objects.  This is calculated and
		/// passed down by the parent  object using the
		///  method, and is used to proportionally adjust
		/// font sizes, etc. according to the actual size of the graph.
		/// 
		override public void DrawLegendKey( Graphics g, GraphPane pane, RectangleF rect, float scaleFactor )
		{
			if ( !_isVisible )
				return;
			// Fill the slice
			if ( _fill.IsVisible )
			{
				// just avoid height/width being less than 0.1 so GDI+ doesn't cry
				using ( Brush brush = _fill.MakeBrush( rect ) )
				{
					g.FillRectangle( brush, rect );
					//brush.Dispose();
				}
			}
			// Border the bar
			if ( !_border.Color.IsEmpty )
				_border.Draw( g, pane, scaleFactor, rect );
		}
		/// 
		/// Determine the coords for the rectangle associated with a specified point for 
		/// this 
		/// 
		/// The  to which this curve belongs
		/// The index of the point of interest
		/// A list of coordinates that represents the "rect" for
		/// this point (used in an html AREA tag)
		/// true if it's a valid point, false otherwise
		override public bool GetCoords( GraphPane pane, int i, out string coords )
		{
			coords = string.Empty;
			PointF pt = _boundingRectangle.Location;
			pt.X += _boundingRectangle.Width / 2.0f;
			pt.Y += _boundingRectangle.Height / 2.0f;
			float radius = _boundingRectangle.Width / 2.0f;
			Matrix matrix = new Matrix();
			// Move the coordinate system to local coordinates
			// of this text object (that is, at the specified
			// x,y location)
			matrix.Translate( pt.X, pt.Y );
			matrix.Rotate( this.StartAngle );
			//One mark every 5'ish degrees
			int count = (int)Math.Floor ( SweepAngle / 5 ) + 1;
			PointF[] pts = new PointF[2 + count];
			pts[0] = new PointF( 0, 0 );
			pts[1] = new PointF( radius, 0 );
			double angle = 0.0;
			for ( int j = 2; j < count + 2; j++ )
			{
				angle += SweepAngle / count;
				pts[j] = new PointF(radius * (float)Math.Cos(angle * Math.PI / 180.0),
											radius * (float)Math.Sin( angle * Math.PI / 180.0 ) );
			}
			matrix.TransformPoints( pts );
			coords = String.Format("{0:f0},{1:f0},{2:f0},{3:f0},",
						pts[0].X, pts[0].Y, pts[1].X, pts[1].Y );
			for (int j = 2; j < count + 2; j++)
				coords += String.Format(j > count ? "{0:f0},{1:f0}" : "{0:f0},{1:f0},", pts[j].X, pts[j].Y);
			return true;
		}
		#endregion
	}
}