1 //-------------------------------------------------------------
2 // <copyright company=�Microsoft Corporation�>
3 //   Copyright � Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 //  File:		PieChart.cs
9 //
10 //  Namespace:	DataVisualization.Charting.ChartTypes
11 //
12 //	Classes:	PieChart
13 //
14 //  Purpose:	Provides 2D/3D drawing and hit testing functionality
15 //              for the Pie chart. A pie chart shows how proportions
16 //              of data, shown as pie-shaped pieces, contribute to
17 //              the data as a whole.
18 //
19 //              PieChart class is used as a base class for the
20 //              DoughnutChart class.
21 //
22 //	Reviewed:	GS - Aug 8, 2002
23 //				AG - Aug 8, 2002
24 //				AG - Microsoft 6, 2007
25 //
26 //===================================================================
27 
28 #region Used namespaces
29 
30 using System;
31 using System.Collections;
32 using System.Collections.Generic;
33 using System.Drawing;
34 using System.Drawing.Drawing2D;
35 using System.ComponentModel.Design;
36 using System.Globalization;
37 
38 #if Microsoft_CONTROL
39 	using System.Windows.Forms.DataVisualization.Charting.Utilities;
40 #else
41 	using System.Web.UI.DataVisualization.Charting.Utilities;
42 #endif
43 
44 #endregion
45 
46 #if Microsoft_CONTROL
47 	namespace System.Windows.Forms.DataVisualization.Charting.ChartTypes
48 #else
49 	namespace System.Web.UI.DataVisualization.Charting.ChartTypes
50 #endif
51 {
52 	#region Enumerations
53 
54 	/// <summary>
55 	/// Pie Labels style
56 	/// </summary>
57 	internal enum PieLabelStyle
58 	{
59 		/// <summary>
60 		/// Labels are inside pie slice
61 		/// </summary>
62 		Inside,
63 
64 		/// <summary>
65 		/// Labels are outside pie slice
66 		/// </summary>
67 		Outside,
68 
69 		/// <summary>
70 		/// Labels are disabled
71 		/// </summary>
72 		Disabled,
73 
74 	};
75 
76 	#endregion
77 
78 	/// <summary>
79     /// PieChart class provides 2D/3D drawing and hit testing functionality
80     /// for the Pie chart.
81 	/// </summary>
82 	internal class PieChart : IChartType
83 	{
84 		#region Enumerations
85 
86 		/// <summary>
87 		/// Labels Mode for preparing data
88 		/// </summary>
89 		enum LabelsMode
90 		{
91 			/// <summary>
92 			/// There are no labels
93 			/// </summary>
94 			Off,
95 
96 			/// <summary>
97 			/// Drawing labels mode
98 			/// </summary>
99 			Draw,
100 
101 			/// <summary>
102 			/// Labels Estimation mode
103 			/// </summary>
104 			EstimateSize,
105 
106 			/// <summary>
107 			/// Labels Overlap Mode
108 			/// </summary>
109 			LabelsOverlap
110 		};
111 
112 		#endregion
113 
114 		#region Fields
115 
116 		// True if labels fit inside plot area.
117 		private bool _labelsFit = true;
118 
119 		// Field that is used to resize pie
120 		// because of labels.
121 		private float _sizeCorrection = 0.95F;
122 
123 		// True if any pie slice is exploded
124 		private bool _sliceExploded = false;
125 
126 		// True if labels overlap for 2D Pie and outside labels
127 		private bool _labelsOverlap = false;
128 
129 		// Left Lable column used for 3D chart and outside labels
130 		internal LabelColumn labelColumnLeft;
131 
132 		// Right Lable column used for 3D chart and outside labels
133 		internal LabelColumn labelColumnRight;
134 
135 		// Array of label rectangles used to prevent labels overlapping
136 		// for 2D pie chart outside labels.
137 		private	ArrayList _labelsRectangles = new ArrayList();
138 
139 		#endregion
140 
141 		#region IChartType interface implementation
142 
143 		/// <summary>
144 		/// Chart type name
145 		/// </summary>
146 		virtual public string Name			{ get{ return ChartTypeNames.Pie;}}
147 
148 		/// <summary>
149 		/// Gets chart type image.
150 		/// </summary>
151 		/// <param name="registry">Chart types registry object.</param>
152 		/// <returns>Chart type image.</returns>
GetImage(ChartTypeRegistry registry)153 		virtual public System.Drawing.Image GetImage(ChartTypeRegistry registry)
154 		{
155 			return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
156 		}
157 
158 		/// <summary>
159 		/// True if chart type is stacked
160 		/// </summary>
161 		virtual public bool Stacked		{ get{ return false;}}
162 
163 
164 		/// <summary>
165 		/// True if stacked chart type supports groups
166 		/// </summary>
167 		virtual public bool SupportStackedGroups	{ get { return false; } }
168 
169 
170 		/// <summary>
171 		/// True if stacked chart type should draw separately positive and
172 		/// negative data points ( Bar and column Stacked types ).
173 		/// </summary>
174 		public bool StackSign		{ get{ return false;}}
175 
176 		/// <summary>
177 		/// True if chart type supports axeses
178 		/// </summary>
179 		virtual public bool RequireAxes	{ get{ return false;} }
180 
181 		/// <summary>
182 		/// Chart type with two y values used for scale ( bubble chart type )
183 		/// </summary>
184 		public bool SecondYScale{ get{ return false;} }
185 
186 		/// <summary>
187 		/// True if chart type requires circular chart area.
188 		/// </summary>
189 		public bool CircularChartArea	{ get{ return false;} }
190 
191 		/// <summary>
192 		/// True if chart type supports logarithmic axes
193 		/// </summary>
194 		virtual public bool SupportLogarithmicAxes	{ get{ return false;} }
195 
196 		/// <summary>
197 		/// True if chart type requires to switch the value (Y) axes position
198 		/// </summary>
199 		virtual public bool SwitchValueAxes	{ get{ return false;} }
200 
201 		/// <summary>
202 		/// True if chart series can be placed side-by-side.
203 		/// </summary>
204 		virtual public bool SideBySideSeries { get{ return false;} }
205 
206 		/// <summary>
207 		/// If the crossing value is auto Crossing value should be
208 		/// automatically set to zero for some chart
209 		/// types (Bar, column, area etc.)
210 		/// </summary>
211 		virtual public bool ZeroCrossing { get{ return false;} }
212 
213 		/// <summary>
214 		/// True if each data point of a chart must be represented in the legend
215 		/// </summary>
216 		virtual public bool DataPointsInLegend	{ get{ return true;} }
217 
218 		/// <summary>
219 		/// Indicates that extra Y values are connected to the scale of the Y axis
220 		/// </summary>
221 		virtual public bool ExtraYValuesConnectedToYAxis{ get { return false; } }
222 
223 		/// <summary>
224 		/// Indicates that it's a hundredred percent chart.
225 		/// Axis scale from 0 to 100 percent should be used.
226 		/// </summary>
227 		virtual public bool HundredPercent{ get{return false;} }
228 
229 		/// <summary>
230 		/// Indicates that it's a hundredred percent chart.
231 		/// Axis scale from 0 to 100 percent should be used.
232 		/// </summary>
233 		virtual public bool HundredPercentSupportNegative{ get{return false;} }
234 
235 		/// <summary>
236 		/// True if palette colors should be applied for each data paoint.
237 		/// Otherwise the color is applied to the series.
238 		/// </summary>
239 		virtual public bool ApplyPaletteColorsToPoints	{ get { return true; } }
240 
241 		/// <summary>
242 		/// How to draw series/points in legend:
243 		/// Filled rectangle, Line or Marker
244 		/// </summary>
245 		/// <param name="series">Legend item series.</param>
246 		/// <returns>Legend item style.</returns>
GetLegendImageStyle(Series series)247 		virtual public LegendImageStyle GetLegendImageStyle(Series series)
248 		{
249 			return LegendImageStyle.Rectangle;
250 		}
251 
252 		/// <summary>
253 		/// Number of supported Y value(s) per point
254 		/// </summary>
255 		virtual public int YValuesPerPoint{ get { return 1; } }
256 
257 		/// <summary>
258 		/// Chart is Doughnut or Pie type
259 		/// </summary>
260 		virtual public bool Doughnut{ get { return false; } }
261 
262 		#endregion
263 
264 		#region Methods
265 
266 		/// <summary>
267 		/// Default constructor
268 		/// </summary>
PieChart()269 		public PieChart()
270 		{
271 		}
272 
273 
274 
275 		/// <summary>
276 		/// Calculates Collected pie slice if required.
277 		/// </summary>
278 		/// <param name="series">Series to be prepared.</param>
PrepareData(Series series)279 		internal static void PrepareData(Series series)
280 		{
281 			// Check series chart type
282 			if( String.Compare(series.ChartTypeName, ChartTypeNames.Pie, StringComparison.OrdinalIgnoreCase ) != 0 &&
283 				String.Compare(series.ChartTypeName, ChartTypeNames.Doughnut, StringComparison.OrdinalIgnoreCase ) != 0
284                 )
285 			{
286 				return;
287 			}
288 
289 			// Check if collected threshold value is set
290 			double threshold = 0.0;
291             if (series.IsCustomPropertySet(CustomPropertyName.CollectedThreshold))
292             {
293                 double t;
294                 bool parseSucceed = double.TryParse(series[CustomPropertyName.CollectedThreshold], NumberStyles.Any, CultureInfo.InvariantCulture, out t);
295                 if (parseSucceed)
296                 {
297                     threshold = t;
298                 }
299                 else
300                 {
301                     throw (new InvalidOperationException(SR.ExceptionDoughnutCollectedThresholdInvalidFormat));
302                 }
303 
304                 if (threshold < 0.0)
305                 {
306                     throw (new InvalidOperationException(SR.ExceptionDoughnutThresholdInvalid));
307                 }
308             }
309 
310 			// Check if threshold is set
311 			if(threshold > 0.0)
312 			{
313                 // Get reference to the chart control
314 				Chart	chart = series.Chart;
315 				if(chart == null)
316 				{
317                     throw (new InvalidOperationException(SR.ExceptionDoughnutNullReference));
318 				}
319 
320 				// Create a temp series which will hold original series data points
321 				Series seriesOriginalData = new Series("PIE_ORIGINAL_DATA_" + series.Name, series.YValuesPerPoint);
322 				seriesOriginalData.Enabled = false;
323 				seriesOriginalData.IsVisibleInLegend = false;
324 				chart.Series.Add(seriesOriginalData);
325 				foreach(DataPoint dp in series.Points)
326 				{
327 					seriesOriginalData.Points.Add(dp.Clone());
328 				}
329 
330 				// Copy temporary design data attribute
331 				if(series.IsCustomPropertySet("TempDesignData"))
332 				{
333 					seriesOriginalData["TempDesignData"] = "true";
334 				}
335 
336 				// Calculate total value of all data points. IsEmpty points are
337 				// ignored. Absolute value is used.
338 				double total = 0.0;
339 				foreach(DataPoint dp in series.Points)
340 				{
341 					if(!dp.IsEmpty)
342 					{
343 						total += Math.Abs(dp.YValues[0]);
344 					}
345 				}
346 
347 				// Check if threshold value is set in percents
348 				bool percent = true;
349 				if(series.IsCustomPropertySet(CustomPropertyName.CollectedThresholdUsePercent))
350 				{
351 					if(string.Compare(series[CustomPropertyName.CollectedThresholdUsePercent], "True", StringComparison.OrdinalIgnoreCase) == 0)
352 					{
353 						percent = true;
354 					}
355                     else if (string.Compare(series[CustomPropertyName.CollectedThresholdUsePercent], "False", StringComparison.OrdinalIgnoreCase) == 0)
356 					{
357 						percent = false;
358 					}
359 					else
360 					{
361                         throw (new InvalidOperationException(SR.ExceptionDoughnutCollectedThresholdUsePercentInvalid));
362 					}
363 				}
364 
365 				// Convert from percent valur to data point value
366 				if(percent)
367 				{
368 					if(threshold > 100.0)
369 					{
370                         throw (new InvalidOperationException(SR.ExceptionDoughnutCollectedThresholdInvalidRange));
371 					}
372 
373 					threshold = total * threshold / 100.0;
374 				}
375 
376 				// Count how many points will be collected and remove collected points
377 				DataPoint collectedPoint = null;
378 				double collectedTotal = 0.0;
379 				int collectedCount = 0;
380 				int firstCollectedPointIndex = 0;
381 				int originalDataPointIndex = 0;
382 				for(int dataPointIndex = 0; dataPointIndex < series.Points.Count; dataPointIndex++)
383 				{
384 					DataPoint dataPoint = series.Points[dataPointIndex];
385 					if(!dataPoint.IsEmpty && Math.Abs(dataPoint.YValues[0]) <= threshold)
386 					{
387 						// Keep statistics
388 						++collectedCount;
389 						collectedTotal += Math.Abs(dataPoint.YValues[0]);
390 
391 						// Make a template for the collected point using the first removed point
392 						if(collectedPoint == null)
393 						{
394 							firstCollectedPointIndex = dataPointIndex;
395 							collectedPoint = dataPoint.Clone();
396 						}
397 
398 						// Remove first collected point only when second collected point found
399 						if(collectedCount == 2)
400 						{
401 							series.Points.RemoveAt(firstCollectedPointIndex);
402 							--dataPointIndex;
403 						}
404 
405 						// Remove collected point
406 						if(collectedCount > 1)
407 						{
408 							series.Points.RemoveAt(dataPointIndex);
409 							--dataPointIndex;
410 						}
411 					}
412 
413 					// Set point index that will be used for tooltips
414 					dataPoint["OriginalPointIndex"] = originalDataPointIndex.ToString(CultureInfo.InvariantCulture);
415 					++originalDataPointIndex;
416 				}
417 
418 				// Add collected data point into the series
419 				if(collectedCount > 1 && collectedPoint != null)
420 				{
421 					collectedPoint["_COLLECTED_DATA_POINT"] = "TRUE";
422 					collectedPoint.YValues[0] = collectedTotal;
423 					series.Points.Add(collectedPoint);
424 
425 					// Set collected point color
426 					if(series.IsCustomPropertySet(CustomPropertyName.CollectedColor))
427 					{
428 						ColorConverter colorConverter = new ColorConverter();
429 						try
430 						{
431 							collectedPoint.Color = (Color)colorConverter.ConvertFromString(null, CultureInfo.InvariantCulture, series[CustomPropertyName.CollectedColor]);
432 						}
433 						catch
434 						{
435                             throw (new InvalidOperationException(SR.ExceptionDoughnutCollectedColorInvalidFormat));
436 						}
437 					}
438 
439 					// Set collected point exploded attribute
440 					if(series.IsCustomPropertySet(CustomPropertyName.CollectedSliceExploded))
441 					{
442 						collectedPoint[CustomPropertyName.Exploded] = series[CustomPropertyName.CollectedSliceExploded];
443 					}
444 
445 					// Set collected point tooltip
446 					if(series.IsCustomPropertySet(CustomPropertyName.CollectedToolTip))
447 					{
448 						collectedPoint.ToolTip = series[CustomPropertyName.CollectedToolTip];
449 					}
450 
451 					// Set collected point legend text
452 					if(series.IsCustomPropertySet(CustomPropertyName.CollectedLegendText))
453 					{
454 						collectedPoint.LegendText = series[CustomPropertyName.CollectedLegendText];
455 					}
456 					else
457 					{
458                         collectedPoint.LegendText = SR.DescriptionCustomAttributeCollectedLegendDefaultText;
459 					}
460 
461 					// Set collected point label
462 					if(series.IsCustomPropertySet(CustomPropertyName.CollectedLabel))
463 					{
464 						collectedPoint.Label = series[CustomPropertyName.CollectedLabel];
465 					}
466                 }
467 			}
468 		}
469 
470 		/// <summary>
471 		/// Remove any changes done while preparing Pie/Doughnut charts
472 		/// to draw the collected slice.
473 		/// </summary>
474 		/// <param name="series">Series to be un-prepared.</param>
475 		/// <returns>True if series was removed from collection.</returns>
UnPrepareData(Series series)476 		internal static bool UnPrepareData(Series series)
477 		{
478 			if(series.Name.StartsWith("PIE_ORIGINAL_DATA_", StringComparison.Ordinal))
479 			{
480 				// Get reference to the chart control
481 				Chart	chart = series.Chart;
482 				if(chart == null)
483 				{
484                     throw (new InvalidOperationException(SR.ExceptionDoughnutNullReference));
485 				}
486 
487 				// Get original Renko series
488 				Series	pieSeries = chart.Series[series.Name.Substring(18)];
489 
490 				// Copy data back to original Pie series
491 				pieSeries.Points.Clear();
492 				if(!series.IsCustomPropertySet("TempDesignData"))
493 				{
494 					foreach(DataPoint dp in series.Points)
495 					{
496 						pieSeries.Points.Add(dp);
497 					}
498 				}
499 
500 				// Remove series from the collection
501 				chart.Series.Remove(series);
502 				return true;
503 			}
504 			return false;
505 		}
506 
507 
508 
509 		/// <summary>
510 		/// Paint Pie Chart
511 		/// </summary>
512 		/// <param name="graph">The Chart Graphics object</param>
513 		/// <param name="common">The Common elements object</param>
514 		/// <param name="area">Chart area for this chart</param>
515 		/// <param name="seriesToDraw">Chart series to draw.</param>
Paint( ChartGraphics graph, CommonElements common, ChartArea area, Series seriesToDraw )516 		public void Paint( ChartGraphics graph, CommonElements common, ChartArea area, Series seriesToDraw )
517 		{
518 			// Pie chart cannot be combined with other chart types
519 			foreach( Series series in common.DataManager.Series )
520 			{
521 				// Check if series is visible and belong to the current chart area
522 				if( series.IsVisible() &&
523 					series.ChartArea == area.Name )
524 				{
525 					// Check if series chart type matches
526 					if( String.Compare( series.ChartTypeName, this.Name, true, System.Globalization.CultureInfo.CurrentCulture ) != 0 )
527 					{
528 						if(!common.ChartPicture.SuppressExceptions)
529 						{
530 							// Pie/Doughnut chart can not be combined with other chart type
531                             throw (new InvalidOperationException(SR.ExceptionChartCanNotCombine( this.Name )));
532 						}
533 					}
534 				}
535 			}
536 
537 			// 3D Pie Chart
538 			if( area.Area3DStyle.Enable3D )
539 			{
540 
541 				float pieWidth = 10 * area.Area3DStyle.PointDepth / 100;
542 
543                 // Set Clip Region
544 				graph.SetClip(area.Position.ToRectangleF());
545 
546 				// Make reversed X angle because of default angle.
547 				area.Area3DStyle.Inclination *= -1;
548 				int oldYAngle = area.Area3DStyle.Rotation;
549 				area.Area3DStyle.Rotation = area.GetRealYAngle( );
550 
551 				// Draw Pie
552 				ProcessChartType3D( false, graph, common, area, pieWidth );
553 
554 				// Make reversed X angle because of default angle.
555 				area.Area3DStyle.Inclination *= -1;
556 				area.Area3DStyle.Rotation = oldYAngle;
557 
558 				// Reset Clip Region
559 				graph.ResetClip();
560 
561 			}
562 			else
563 			{
564 				// Reset overlapped labels flag
565 				this._labelsOverlap = false;
566 
567 				//Set Clip Region
568 				((ChartGraphics)graph).SetClip( area.Position.ToRectangleF() );
569 
570 				// Resize pie because of labels
571 				SizeCorrection( graph, common, area );
572 
573 				// Draw Pie labels
574 				ProcessChartType( false, graph, common, area, false, LabelsMode.LabelsOverlap );
575 
576 				// If overlapping labels are detected they will be drawn in "columns" on each
577 				// side of the pie. Adjust plotting area to fit the labels
578 				if(this._labelsOverlap)
579 				{
580 					// Resize pie because of labels
581 					SizeCorrection( graph, common, area );
582 
583 					// Reset overlapped labels flag
584 					this._labelsOverlap = false;
585 
586 					// Draw Pie labels
587 					ProcessChartType( false, graph, common, area, false, LabelsMode.LabelsOverlap );
588 				}
589 
590 				// Draw Shadow
591 				ProcessChartType( false, graph, common, area, true, LabelsMode.Off );
592 
593 				// Draw Pie
594 				ProcessChartType( false, graph, common, area, false, LabelsMode.Off );
595 
596 				// Draw Pie labels
597 				ProcessChartType( false, graph, common, area, false, LabelsMode.Draw );
598 
599 				//Reset Clip Region
600 				((ChartGraphics)graph).ResetClip();
601 			}
602 		}
603 
604 		/// <summary>
605 		/// Take Relative Minimum Pie Size attribute
606 		/// </summary>
607 		/// <param name="area">Chart Area</param>
608 		/// <returns>Custom attribute value.</returns>
MinimumRelativePieSize( ChartArea area )609 		private double MinimumRelativePieSize( ChartArea area )
610 		{
611 			// Default value
612 			double minimumSize = 0.3;
613 
614 			// All data series from chart area which have Pie chart type
615 			List<string>	typeSeries = area.GetSeriesFromChartType(Name);
616 
617 			// Data series collection
618 			SeriesCollection	dataSeries = area.Common.DataManager.Series;
619 
620 			// Take Relative Minimum Pie Size attribute
621 			if(dataSeries[typeSeries[0]].IsCustomPropertySet(CustomPropertyName.MinimumRelativePieSize))
622 			{
623 				minimumSize = CommonElements.ParseFloat(dataSeries[typeSeries[0]][CustomPropertyName.MinimumRelativePieSize]) / 100.0;
624 
625 				// Validation
626 				if( minimumSize < 0.1 || minimumSize > 0.7 )
627                     throw (new ArgumentException(SR.ExceptionPieMinimumRelativePieSizeInvalid));
628 
629 			}
630 
631 			return minimumSize;
632 		}
633 
634 		/// <summary>
635 		/// Method that is used to resize pie
636 		/// because of labels.
637 		/// </summary>
SizeCorrection( ChartGraphics graph, CommonElements common, ChartArea area )638 		private void SizeCorrection( ChartGraphics graph, CommonElements common, ChartArea area )
639 		{
640 			float correction = (this._labelsOverlap) ? this._sizeCorrection : 0.95F;
641 			_sliceExploded = false;
642 
643 			// Estimate Labels
644 			if( area.InnerPlotPosition.Auto )
645 			{
646 				for( ; correction >= (float)MinimumRelativePieSize( area ); correction -= 0.05F )
647 				{
648 					// Decrease Pie size
649 					this._sizeCorrection = correction;
650 
651 					// Check if labels fit.
652 					ProcessChartType( false, graph, common, area, false, LabelsMode.EstimateSize );
653 					if( _labelsFit )
654 					{
655 						break;
656 					}
657 				}
658 
659 				// Size correction for exploded pie can not be larger then 0.8
660 				if( _sliceExploded && _sizeCorrection > 0.8F )
661 				{
662 					_sizeCorrection = 0.8F;
663 				}
664 			}
665 			else
666 			{
667 				_sizeCorrection = 0.95F;
668 			}
669 		}
670 
671 		/// <summary>
672 		/// This method recalculates position of pie slices
673 		/// or checks if pie slice is selected.
674 		/// </summary>
675 		/// <param name="selection">If True selection mode is active, otherwise paint mode is active</param>
676 		/// <param name="graph">The Chart Graphics object</param>
677 		/// <param name="common">The Common elements object</param>
678 		/// <param name="area">Chart area for this chart</param>
679 		/// <param name="shadow">Draw pie shadow</param>
680 		/// <param name="labels">Pie labels</param>
ProcessChartType( bool selection, ChartGraphics graph, CommonElements common, ChartArea area, bool shadow, LabelsMode labels )681 		private void ProcessChartType( bool selection, ChartGraphics graph, CommonElements common, ChartArea area, bool shadow, LabelsMode labels )
682 		{
683 			float startAngle = 0;			// Angle in degrees measured clockwise from the x-axis to the first side of the pie section.
684 			string	explodedAttrib = "";	// Exploded attribute
685 			bool exploded;					// Exploded pie slice
686 			float midAngle;					// Angle between Start Angle and End Angle
687 
688 			// Data series collection
689 			SeriesCollection	dataSeries = common.DataManager.Series;
690 
691 			// Clear Labels overlap collection
692 			if( labels == LabelsMode.LabelsOverlap )
693 			{
694 				_labelsRectangles.Clear();
695 			}
696 
697 			// All data series from chart area which have Pie chart type
698 			List<string>	typeSeries = area.GetSeriesFromChartType(Name);
699 			if(typeSeries.Count == 0)
700 			{
701 				return;
702 			}
703 
704 			// Get first pie starting angle
705 			if(typeSeries.Count > 0)
706 			{
707                 if (dataSeries[typeSeries[0]].IsCustomPropertySet(CustomPropertyName.PieStartAngle))
708                 {
709                     float angle;
710                     bool parseSucceed = float.TryParse(dataSeries[typeSeries[0]][CustomPropertyName.PieStartAngle], NumberStyles.Any, CultureInfo.InvariantCulture, out angle);
711                     if (parseSucceed)
712                     {
713                         startAngle = angle;
714                     }
715 
716                     if (!parseSucceed || startAngle > 360f || startAngle < 0f)
717                     {
718                         throw (new InvalidOperationException(SR.ExceptionCustomAttributeAngleOutOfRange("PieStartAngle")));
719                     }
720                 }
721 			}
722 
723 			// Call Back Paint event
724 			if( !selection )
725 			{
726                 common.Chart.CallOnPrePaint(new ChartPaintEventArgs(dataSeries[typeSeries[0]], graph, common, area.PlotAreaPosition));
727 			}
728 
729 			// The data points loop. Find Sum of data points.
730 			double	sum = 0.0;
731 			foreach( DataPoint point in dataSeries[typeSeries[0]].Points )
732 			{
733 				if( !point.IsEmpty )
734 				{
735 					sum += Math.Abs( point.YValues[0] );
736 				}
737 			}
738 
739 			// No points or all points have zero values
740 			if(sum == 0.0)
741 			{
742 				return;
743 			}
744 
745 			// Take radius attribute
746 			float	doughnutRadius = 60f;
747 			if(dataSeries[typeSeries[0]].IsCustomPropertySet(CustomPropertyName.DoughnutRadius))
748 			{
749 				doughnutRadius = CommonElements.ParseFloat(dataSeries[typeSeries[0]][CustomPropertyName.DoughnutRadius]);
750 
751 				// Validation
752 				if( doughnutRadius < 0f || doughnutRadius > 99f )
753                     throw (new ArgumentException(SR.ExceptionPieRadiusInvalid));
754 
755 			}
756 
757 			// This method is introduced to check colors of palette. For
758 			// pie chart the first pie slice and the second pie slice can
759 			// not have same color because they are connected.
760 			CheckPaleteColors( dataSeries[typeSeries[0]].Points );
761 
762 			//************************************************************
763 			//** Data point loop
764 			//************************************************************
765 			int	pointIndx = 0;
766 			int nonEmptyPointIndex = 0;
767 			foreach( DataPoint point in dataSeries[typeSeries[0]].Points )
768 			{
769 				// Do not process empty points
770 				if( point.IsEmpty )
771 				{
772 					pointIndx++;
773 					continue;
774 				}
775 
776 				// Rectangle size
777 				RectangleF	rectangle;
778 				if( area.InnerPlotPosition.Auto )
779 				{
780 					rectangle = new RectangleF( area.Position.ToRectangleF().X, area.Position.ToRectangleF().Y, area.Position.ToRectangleF().Width, area.Position.ToRectangleF().Height );
781 				}
782 				else
783 				{
784 					rectangle = new RectangleF( area.PlotAreaPosition.ToRectangleF().X, area.PlotAreaPosition.ToRectangleF().Y, area.PlotAreaPosition.ToRectangleF().Width, area.PlotAreaPosition.ToRectangleF().Height );
785 				}
786 				if(rectangle.Width < 0f || rectangle.Height < 0f)
787 				{
788 					return;
789 				}
790 
791 				// Find smallest edge
792 				SizeF absoluteSize = graph.GetAbsoluteSize( new SizeF( rectangle.Width, rectangle.Height ) );
793 				float absRadius = ( absoluteSize.Width < absoluteSize.Height ) ? absoluteSize.Width : absoluteSize.Height;
794 
795 				// Size of the square, which will be used for drawing pie.
796 				SizeF relativeSize = graph.GetRelativeSize( new SizeF( absRadius, absRadius ) );
797 
798 				// Center of the pie
799 				PointF middlePoint = new PointF( rectangle.X + rectangle.Width / 2, rectangle.Y + rectangle.Height / 2 );
800 
801 				// Rectangle which will always create circle, never ellipse.
802 				rectangle = new RectangleF( middlePoint.X - relativeSize.Width / 2, middlePoint.Y - relativeSize.Height / 2, relativeSize.Width, relativeSize.Height );
803 
804 				// Size correction because of exploded or labels
805 				if( _sizeCorrection != 1 )
806 				{
807 					rectangle.X += rectangle.Width * ( 1 - _sizeCorrection ) / 2;
808 					rectangle.Y += rectangle.Height * ( 1 - _sizeCorrection ) / 2;
809 					rectangle.Width = rectangle.Width * _sizeCorrection;
810 					rectangle.Height = rectangle.Height * _sizeCorrection;
811 
812 					// Adjust inner plot position
813 					if(area.InnerPlotPosition.Auto)
814 					{
815 						RectangleF rect = rectangle;
816 						rect.X = (rect.X - area.Position.X) / area.Position.Width * 100f;
817 						rect.Y = (rect.Y - area.Position.Y) / area.Position.Height * 100f;
818 						rect.Width = rect.Width / area.Position.Width * 100f;
819 						rect.Height = rect.Height / area.Position.Height * 100f;
820 						area.InnerPlotPosition.SetPositionNoAuto(rect.X, rect.Y, rect.Width, rect.Height);
821 					}
822 				}
823 
824 				float	sweepAngle = (float)( Math.Abs(point.YValues[0]) / sum * 360);
825 
826 				// Check Exploded attribute for data point
827 				exploded = false;
828 				if(point.IsCustomPropertySet(CustomPropertyName.Exploded))
829 				{
830 					explodedAttrib = point[CustomPropertyName.Exploded];
831 					if( String.Compare(explodedAttrib,"true", StringComparison.OrdinalIgnoreCase) == 0 )
832 						exploded = true;
833 					else
834 						exploded = false;
835 				}
836 
837 				Color pieLineColor = Color.Empty;
838 				ColorConverter colorConverter = new ColorConverter();
839 
840 				// Check if special color properties are set
841 				if(point.IsCustomPropertySet(CustomPropertyName.PieLineColor) || dataSeries[typeSeries[0]].IsCustomPropertySet(CustomPropertyName.PieLineColor))
842 				{
843                     bool failed = false;
844                     try
845                     {
846                         pieLineColor = (Color)colorConverter.ConvertFromString(
847                             (point.IsCustomPropertySet(CustomPropertyName.PieLineColor)) ? point[CustomPropertyName.PieLineColor] : dataSeries[typeSeries[0]][CustomPropertyName.PieLineColor]);
848                         failed = false;
849                     }
850                     catch (ArgumentException)
851                     {
852                         failed = true;
853                     }
854                     catch (NotSupportedException)
855                     {
856                         failed = true;
857                     }
858 
859                     if (failed)
860                     {
861                         pieLineColor = (Color)colorConverter.ConvertFromInvariantString(
862     (point.IsCustomPropertySet(CustomPropertyName.PieLineColor)) ? point[CustomPropertyName.PieLineColor] : dataSeries[typeSeries[0]][CustomPropertyName.PieLineColor]);
863                     }
864 				}
865 
866 				// Find Direction to move exploded pie slice
867 				if( exploded )
868 				{
869 					_sliceExploded = true;
870 					midAngle = ( 2 * startAngle + sweepAngle ) / 2;
871 					double xComponent = Math.Cos( midAngle * Math.PI / 180 ) * rectangle.Width / 10;
872 					double yComponent = Math.Sin( midAngle * Math.PI / 180 ) * rectangle.Height / 10;
873 
874 					rectangle.Offset( (float)xComponent, (float)yComponent );
875 				}
876 
877 				// Hot regions of the data points. Labels hot regions are processed aftre drawing.
878 				if( common.ProcessModeRegions && labels == LabelsMode.Draw )
879 				{
880 					Map( common, point, startAngle, sweepAngle, rectangle, Doughnut, doughnutRadius, graph, pointIndx );
881 				}
882 
883 				// Painting mode
884 				if( common.ProcessModePaint )
885 				{
886 					// Draw Shadow
887 					if( shadow )
888 					{
889 						double offset = graph.GetRelativeSize( new SizeF( point.series.ShadowOffset, point.series.ShadowOffset ) ).Width;
890 
891 						// Offset is zero. Do not draw shadow pie slice.
892 						if( offset == 0.0 )
893 						{
894 							break;
895 						}
896 
897 						// Shadow Rectangle
898 						RectangleF shadowRect = new RectangleF( rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height );
899 						shadowRect.Offset( (float)offset, (float)offset );
900 
901 						// Change shadow color
902 						Color shcolor = new Color();
903 						Color shGradientColor = new Color();
904 						Color shBorderColor = new Color();
905 
906 						// Solid color
907 						if( point.Color.A != 255 )
908 							shcolor = Color.FromArgb( point.Color.A/2, point.series.ShadowColor );
909 						else
910 							shcolor = point.series.ShadowColor;
911 
912 						// Gradient Color
913 						if( !point.BackSecondaryColor.IsEmpty )
914 						{
915 							if( point.BackSecondaryColor.A != 255 )
916 								shGradientColor = Color.FromArgb( point.BackSecondaryColor.A/2, point.series.ShadowColor );
917 							else
918 								shGradientColor = point.series.ShadowColor;
919 						}
920 						else
921 							shGradientColor = Color.Empty;
922 
923 						// Border color
924 						if( !point.BorderColor.IsEmpty )
925 						{
926 							if( point.BorderColor.A != 255 )
927 								shBorderColor = Color.FromArgb( point.BorderColor.A/2, point.series.ShadowColor );
928 							else
929 								shBorderColor = point.series.ShadowColor;
930 						}
931 						else
932 							shBorderColor = Color.Empty;
933 
934 						// Draw shadow of pie slice
935 						graph.DrawPieRel(
936 							shadowRect,
937 							startAngle,
938 							sweepAngle,
939 							shcolor,
940 							ChartHatchStyle.None,
941 							"",
942 							point.BackImageWrapMode,
943 							point.BackImageTransparentColor,
944 							point.BackGradientStyle,
945 							shGradientColor,
946 							shBorderColor,
947 							point.BorderWidth,
948 							point.BorderDashStyle,
949 							true,
950 							Doughnut,
951 							doughnutRadius,
952 							PieDrawingStyle.Default);
953 					}
954 					else
955 					{
956 						if( labels == LabelsMode.Off )
957 						{
958 							// Start Svg Selection mode
959 							graph.StartHotRegion( point );
960 
961 							// Draw pie slice
962 							graph.DrawPieRel(
963 								rectangle,
964 								startAngle,
965 								sweepAngle,
966 								point.Color,
967 								point.BackHatchStyle,
968 								point.BackImage,
969 								point.BackImageWrapMode,
970 								point.BackImageTransparentColor,
971 								point.BackGradientStyle,
972 								point.BackSecondaryColor,
973 								point.BorderColor,
974 								point.BorderWidth,
975 								point.BorderDashStyle,
976 								false,
977 								Doughnut,
978 								doughnutRadius,
979 								ChartGraphics.GetPieDrawingStyle(point) );
980 
981 							// End Svg Selection mode
982 							graph.EndHotRegion( );
983 						}
984 					}
985 
986 					// Estimate labels
987 					if( labels == LabelsMode.EstimateSize )
988 					{
989 						EstimateLabels( graph, middlePoint, rectangle.Size, startAngle, sweepAngle, point, exploded, area );
990 						if( _labelsFit == false )
991 						{
992 							return;
993 						}
994 					}
995 
996 					// Labels overlap test
997 					if( labels == LabelsMode.LabelsOverlap )
998 					{
999 						DrawLabels( graph, middlePoint, rectangle.Size, startAngle, sweepAngle, point, doughnutRadius, exploded, area, true, nonEmptyPointIndex, pieLineColor );
1000 					}
1001 
1002 					// Draw labels and markers
1003 					if( labels == LabelsMode.Draw )
1004 					{
1005 						DrawLabels( graph, middlePoint, rectangle.Size, startAngle, sweepAngle, point, doughnutRadius, exploded, area, false, nonEmptyPointIndex, pieLineColor );
1006 					}
1007 
1008 				}
1009 
1010 				if( common.ProcessModeRegions && labels == LabelsMode.Draw )
1011 				{
1012 					// Add labels hot regions if it was not done during the painting
1013 					if( !common.ProcessModePaint )
1014 					{
1015 						DrawLabels( graph, middlePoint, rectangle.Size, startAngle, sweepAngle, point, doughnutRadius, exploded, area, false, nonEmptyPointIndex, pieLineColor );
1016 					}
1017 				}
1018 
1019 
1020 				//**************************************************
1021 				//** Remember point relative position
1022 				//**************************************************
1023 				point.positionRel = new PointF(float.NaN, float.NaN);
1024 
1025 
1026 				// If exploded the shift is bigger
1027 				float expShift = 1;
1028 				if( exploded )
1029 					expShift = 1.2F;
1030 
1031 				midAngle = startAngle + sweepAngle / 2;
1032 
1033 				// Find first line position
1034 				point.positionRel.X = (float)Math.Cos( (midAngle) * Math.PI / 180 ) * rectangle.Width * expShift / 2 + middlePoint.X;
1035 				point.positionRel.Y = (float)Math.Sin( (midAngle) * Math.PI / 180 ) * rectangle.Height * expShift / 2 + middlePoint.Y;
1036 
1037 				// Increase point index and sweep angle
1038 				pointIndx++;
1039 				nonEmptyPointIndex++;
1040 				startAngle += sweepAngle;
1041 				if(startAngle >= 360)
1042 				{
1043 					startAngle -= 360;
1044 				}
1045 			}
1046 
1047 			if( labels == LabelsMode.LabelsOverlap && this._labelsOverlap )
1048 			{
1049 				this._labelsOverlap = PrepareLabels( area.Position.ToRectangleF() );
1050 			}
1051 
1052 			// Call Paint event
1053 			if( !selection )
1054 			{
1055                 common.Chart.CallOnPostPaint(new ChartPaintEventArgs(dataSeries[typeSeries[0]], graph, common, area.PlotAreaPosition));
1056 			}
1057 		}
1058 
1059 		/// <summary>
1060 		/// Draw Pie labels or test for overlaping.
1061 		/// </summary>
1062 		/// <param name="graph">Chart Graphics object</param>
1063 		/// <param name="middlePoint">Center of the pie chart</param>
1064 		/// <param name="relativeSize">Size of the square, which will be used for drawing pie.</param>
1065 		/// <param name="startAngle">Starting angle of a pie slice</param>
1066 		/// <param name="sweepAngle">Sweep angle of a pie slice</param>
1067 		/// <param name="point">Data point</param>
1068 		/// <param name="doughnutRadius">Radius for Doughnut Chart in %</param>
1069 		/// <param name="exploded">The pie slice is exploded</param>
1070 		/// <param name="area">Chart area</param>
1071 		/// <param name="overlapTest">True if test mode is on</param>
1072 		/// <param name="pointIndex">Data Point Index</param>
1073 		/// <param name="pieLineColor">Color of line labels</param>
DrawLabels( ChartGraphics graph, PointF middlePoint, SizeF relativeSize, float startAngle, float sweepAngle, DataPoint point, float doughnutRadius, bool exploded, ChartArea area, bool overlapTest, int pointIndex, Color pieLineColor )1074 		public void DrawLabels( ChartGraphics graph, PointF middlePoint, SizeF relativeSize, float startAngle, float sweepAngle, DataPoint point, float doughnutRadius, bool exploded, ChartArea area, bool overlapTest, int pointIndex, Color pieLineColor )
1075 		{
1076 			bool added = false;	// Indicates that label position was added
1077 			float x; // Label Position
1078 			float y; // Label Position
1079 			Series series; // Data Series
1080 			float labelsHorizontalLineSize = 1; // Horizontal line size for outside labels
1081 			float labelsRadialLineSize = 1; // Radial line size for outside labels
1082 			string text;
1083 
1084 			// Disable the clip region
1085 			Region oldClipRegion = graph.Clip;
1086 			graph.Clip = new Region();
1087 
1088 			// Get label text
1089 			text = this.GetLabelText( point );
1090 			if(text.Length == 0)
1091 			{
1092 				return;
1093 			}
1094 
1095 			float shift;
1096 
1097 			series = point.series;
1098 
1099 			PieLabelStyle style = PieLabelStyle.Inside;
1100 
1101 			// Get label style attribute from series
1102 			if(series.IsCustomPropertySet(CustomPropertyName.LabelStyle))
1103 			{
1104 				string labelStyleAttrib = series[CustomPropertyName.LabelStyle];
1105 
1106 				// Labels Disabled
1107 				if( String.Compare(labelStyleAttrib,"disabled",StringComparison.OrdinalIgnoreCase) == 0 )
1108 					style = PieLabelStyle.Disabled;
1109                 else if (String.Compare(labelStyleAttrib, "outside", StringComparison.OrdinalIgnoreCase) == 0)
1110 					style = PieLabelStyle.Outside;
1111 				else
1112 					style = PieLabelStyle.Inside;
1113 			}
1114 			else if(series.IsCustomPropertySet(CustomPropertyName.PieLabelStyle))
1115 			{
1116 				string labelStyleAttrib = series[CustomPropertyName.PieLabelStyle];
1117 
1118 				// Labels Disabled
1119                 if (String.Compare(labelStyleAttrib, "disabled", StringComparison.OrdinalIgnoreCase) == 0)
1120 					style = PieLabelStyle.Disabled;
1121                 else if (String.Compare(labelStyleAttrib, "outside", StringComparison.OrdinalIgnoreCase) == 0)
1122 					style = PieLabelStyle.Outside;
1123 				else
1124 					style = PieLabelStyle.Inside;
1125 			}
1126 
1127 			// Get label style attribute from point
1128 			if(point.IsCustomPropertySet(CustomPropertyName.LabelStyle))
1129 			{
1130 				string labelStyleAttrib = point[CustomPropertyName.LabelStyle];
1131 
1132 				// Labels Disabled
1133                 if (String.Compare(labelStyleAttrib, "disabled", StringComparison.OrdinalIgnoreCase) == 0)
1134 					style = PieLabelStyle.Disabled;
1135                 else if (String.Compare(labelStyleAttrib, "outside", StringComparison.OrdinalIgnoreCase) == 0)
1136 					style = PieLabelStyle.Outside;
1137 				else
1138 					style = PieLabelStyle.Inside;
1139 			}
1140 			// Get label style attribute from point
1141 			else if(point.IsCustomPropertySet(CustomPropertyName.PieLabelStyle))
1142 			{
1143 				string labelStyleAttrib = point[CustomPropertyName.PieLabelStyle];
1144 
1145 				// Labels Disabled
1146                 if (String.Compare(labelStyleAttrib, "disabled", StringComparison.OrdinalIgnoreCase) == 0)
1147 					style = PieLabelStyle.Disabled;
1148                 else if (String.Compare(labelStyleAttrib, "outside", StringComparison.OrdinalIgnoreCase) == 0)
1149 					style = PieLabelStyle.Outside;
1150 				else
1151 					style = PieLabelStyle.Inside;
1152 			}
1153 
1154 
1155 			// Take labels radial line size attribute from series
1156 			if(series.IsCustomPropertySet(CustomPropertyName.LabelsRadialLineSize))
1157 			{
1158 				string labelsRadialLineSizeAttrib = series[CustomPropertyName.LabelsRadialLineSize];
1159 				labelsRadialLineSize = CommonElements.ParseFloat( labelsRadialLineSizeAttrib);
1160 
1161 				// Validation
1162 				if( labelsRadialLineSize < 0 || labelsRadialLineSize > 100 )
1163                     throw new InvalidOperationException(SR.ExceptionPieRadialLineSizeInvalid);
1164 			}
1165 
1166 			// Take labels radial line size attribute from point
1167 			if(point.IsCustomPropertySet(CustomPropertyName.LabelsRadialLineSize))
1168 			{
1169 				string labelsRadialLineSizeAttrib = point[CustomPropertyName.LabelsRadialLineSize];
1170 				labelsRadialLineSize = CommonElements.ParseFloat( labelsRadialLineSizeAttrib);
1171 
1172 				// Validation
1173 				if( labelsRadialLineSize < 0 || labelsRadialLineSize > 100 )
1174                     throw new InvalidOperationException(SR.ExceptionPieRadialLineSizeInvalid);
1175 			}
1176 
1177 			// Take labels horizontal line size attribute from series
1178 			if(series.IsCustomPropertySet(CustomPropertyName.LabelsHorizontalLineSize))
1179 			{
1180 				string labelsHorizontalLineSizeAttrib = series[CustomPropertyName.LabelsHorizontalLineSize];
1181 				labelsHorizontalLineSize = CommonElements.ParseFloat( labelsHorizontalLineSizeAttrib);
1182 
1183 				// Validation
1184 				if( labelsHorizontalLineSize < 0 || labelsHorizontalLineSize > 100 )
1185                     throw new InvalidOperationException(SR.ExceptionPieHorizontalLineSizeInvalid);
1186 			}
1187 
1188 			// Take labels horizontal line size attribute from point
1189 			if(point.IsCustomPropertySet(CustomPropertyName.LabelsHorizontalLineSize))
1190 			{
1191 				string labelsHorizontalLineSizeAttrib = point[CustomPropertyName.LabelsHorizontalLineSize];
1192 				labelsHorizontalLineSize = CommonElements.ParseFloat( labelsHorizontalLineSizeAttrib);
1193 
1194 				// Validation
1195 				if( labelsHorizontalLineSize < 0 || labelsHorizontalLineSize > 100 )
1196                     throw new InvalidOperationException(SR.ExceptionPieHorizontalLineSizeInvalid);
1197 			}
1198 
1199 			float expShift = 1;
1200 
1201 			// ********************************************
1202 			// Labels are set inside pie
1203 			// ********************************************
1204 			if( style == PieLabelStyle.Inside && !overlapTest )
1205 			{
1206 				float width;
1207 				float height;
1208 
1209 				// If exploded the shift is bigger
1210 				if( exploded )
1211 				{
1212 					expShift = 1.4F;
1213 				}
1214 
1215 				// Get offset of the inside labels position
1216 				// NOTE: This custom attribute is NOT released!
1217 				float positionRatio = 4.0f;
1218 				if(point.IsCustomPropertySet("InsideLabelOffset"))
1219 				{
1220                     bool parseSucceed = float.TryParse(point["InsideLabelOffset"], NumberStyles.Any, CultureInfo.InvariantCulture, out positionRatio);
1221 					if(!parseSucceed || positionRatio < 0f || positionRatio > 100f)
1222 					{
1223 						throw(new InvalidOperationException(SR.ExceptionCustomAttributeIsNotInRange0to100("InsideLabelOffset")));
1224 					}
1225 					positionRatio = 4f / (1f + positionRatio / 100f);
1226 				}
1227 
1228 
1229 				// Shift the string for Doughnut type
1230 				if( Doughnut )
1231 				{
1232 					width = relativeSize.Width * expShift / positionRatio * ( 1 + ( 100 - doughnutRadius ) / 100F );
1233 					height = relativeSize.Height * expShift / positionRatio * ( 1 + ( 100 - doughnutRadius ) / 100F );
1234 				}
1235 				else
1236 				{
1237 					width = relativeSize.Width * expShift / positionRatio;
1238 					height = relativeSize.Height * expShift / positionRatio;
1239 				}
1240 
1241 				// Find string position
1242 				x = (float)Math.Cos( (startAngle + sweepAngle / 2) * Math.PI / 180 ) * width + middlePoint.X;
1243 				y = (float)Math.Sin( (startAngle + sweepAngle / 2) * Math.PI / 180 ) * height + middlePoint.Y;
1244 
1245 				// Center the string horizontally and vertically.
1246                 using (StringFormat format = new StringFormat())
1247                 {
1248                     format.Alignment = StringAlignment.Center;
1249                     format.LineAlignment = StringAlignment.Center;
1250 
1251                     SizeF sizeFont = graph.GetRelativeSize(
1252                         graph.MeasureString(
1253                         text.Replace("\\n", "\n"),
1254                         point.Font,
1255                         new SizeF(1000f, 1000f),
1256                         StringFormat.GenericTypographic));
1257 
1258                     // Get label background position
1259                     RectangleF labelBackPosition = RectangleF.Empty;
1260                     SizeF sizeLabel = new SizeF(sizeFont.Width, sizeFont.Height);
1261                     sizeLabel.Height += sizeLabel.Height / 8;
1262                     sizeLabel.Width += sizeLabel.Width / text.Length;
1263                     labelBackPosition = PointChart.GetLabelPosition(
1264                         graph,
1265                         new PointF(x, y),
1266                         sizeLabel,
1267                         format,
1268                         true);
1269 
1270                     // Draw the label inside the pie
1271                     using (Brush brush = new SolidBrush(point.LabelForeColor))
1272                     {
1273                         graph.DrawPointLabelStringRel(
1274                             area.Common,
1275                             text,
1276                             point.Font,
1277                             brush,
1278                             new PointF(x, y),
1279                             format,
1280                             point.LabelAngle,
1281                             labelBackPosition,
1282                             point.LabelBackColor,
1283                             point.LabelBorderColor,
1284                             point.LabelBorderWidth,
1285                             point.LabelBorderDashStyle,
1286                             series,
1287                             point,
1288                             pointIndex);
1289                     }
1290                 }
1291 			}
1292 
1293 			// ********************************************
1294 			// Labels are set outside pie
1295 			// ********************************************
1296 			else if( style == PieLabelStyle.Outside )
1297 			{
1298 
1299 				// Coefficient which represent shift from pie border
1300 				shift = 0.5F + labelsRadialLineSize * 0.1F;
1301 
1302 				// If exploded the shift is bigger
1303 				if( exploded )
1304 					expShift = 1.2F;
1305 
1306 				float midAngle = startAngle + sweepAngle / 2;
1307 
1308 				// Find first line position
1309 				float x1 = (float)Math.Cos( (midAngle) * Math.PI / 180 ) * relativeSize.Width * expShift / 2 + middlePoint.X;
1310 				float y1 = (float)Math.Sin( (midAngle) * Math.PI / 180 ) * relativeSize.Height * expShift / 2 + middlePoint.Y;
1311 
1312 				float x2 = (float)Math.Cos( (midAngle) * Math.PI / 180 ) * relativeSize.Width * shift * expShift + middlePoint.X;
1313 				float y2 = (float)Math.Sin( (midAngle) * Math.PI / 180 ) * relativeSize.Height * shift * expShift + middlePoint.Y;
1314 
1315 				if( pieLineColor == Color.Empty )
1316 				{
1317 					pieLineColor = point.BorderColor;
1318 				}
1319 
1320 				// Draw first line
1321 				if( !overlapTest )
1322 				{
1323 					graph.DrawLineRel( pieLineColor, point.BorderWidth, ChartDashStyle.Solid, new PointF( x1, y1 ), new PointF( x2, y2 ) );
1324 				}
1325 
1326 				// Set string alingment
1327                 using (StringFormat format = new StringFormat())
1328                 {
1329                     format.Alignment = StringAlignment.Center;
1330                     format.LineAlignment = StringAlignment.Center;
1331 
1332                     // Find second line position
1333                     float y3 = (float)Math.Sin((midAngle) * Math.PI / 180) * relativeSize.Height * shift * expShift + middlePoint.Y;
1334                     float x3;
1335                     float x3Overlap;
1336 
1337                     RectangleF labelRect = RectangleF.Empty;
1338                     RectangleF labelRectOver = RectangleF.Empty;
1339 
1340                     if (midAngle > 90 && midAngle < 270)
1341                     {
1342                         format.Alignment = StringAlignment.Far;
1343                         x3Overlap = -relativeSize.Width * shift * expShift + middlePoint.X - relativeSize.Width / 10 * labelsHorizontalLineSize;
1344                         x3 = (float)Math.Cos((midAngle) * Math.PI / 180) * relativeSize.Width * shift * expShift + middlePoint.X - relativeSize.Width / 10 * labelsHorizontalLineSize;
1345 
1346                         if (overlapTest)
1347                         {
1348                             x3Overlap = x3;
1349                         }
1350 
1351                         // This method returns calculated rectangle from point position
1352                         // for outside label. Rectangle mustn�t be out of chart area.
1353                         labelRect = GetLabelRect(new PointF(x3, y3), area, text, format, graph, point, true);
1354                         labelRectOver = GetLabelRect(new PointF(x3Overlap, y3), area, text, format, graph, point, true);
1355                     }
1356                     else
1357                     {
1358                         format.Alignment = StringAlignment.Near;
1359 
1360                         x3Overlap = relativeSize.Width * shift * expShift + middlePoint.X + relativeSize.Width / 10 * labelsHorizontalLineSize;
1361                         x3 = (float)Math.Cos((midAngle) * Math.PI / 180) * relativeSize.Width * shift * expShift + middlePoint.X + relativeSize.Width / 10 * labelsHorizontalLineSize;
1362 
1363                         if (overlapTest)
1364                         {
1365                             x3Overlap = x3;
1366                         }
1367 
1368                         // This method returns calculated rectangle from point position
1369                         // for outside label. Rectangle mustn�t be out of chart area.
1370                         labelRect = GetLabelRect(new PointF(x3, y3), area, text, format, graph, point, false);
1371                         labelRectOver = GetLabelRect(new PointF(x3Overlap, y3), area, text, format, graph, point, false);
1372                     }
1373 
1374                     // Draw second line
1375                     if (!overlapTest)
1376                     {
1377                         if (this._labelsOverlap)
1378                         {
1379                             float calculatedY3 = (((RectangleF)this._labelsRectangles[pointIndex]).Top + ((RectangleF)this._labelsRectangles[pointIndex]).Bottom) / 2f;
1380                             graph.DrawLineRel(pieLineColor, point.BorderWidth, ChartDashStyle.Solid, new PointF(x2, y2), new PointF(x3Overlap, calculatedY3));
1381                         }
1382                         else
1383                         {
1384                             graph.DrawLineRel(pieLineColor, point.BorderWidth, ChartDashStyle.Solid, new PointF(x2, y2), new PointF(x3, y3));
1385                         }
1386                     }
1387 
1388                     // Draw the string
1389                     if (!overlapTest)
1390                     {
1391                         RectangleF rect = new RectangleF(labelRect.Location, labelRect.Size);
1392                         if (this._labelsOverlap)
1393                         {
1394                             // Draw label from collection if original labels overlap.
1395                             rect = (RectangleF)this._labelsRectangles[pointIndex];
1396                             rect.X = labelRectOver.X;
1397                             rect.Width = labelRectOver.Width;
1398                         }
1399 
1400                         // Get label background position
1401                         SizeF valueTextSize = graph.MeasureStringRel(text.Replace("\\n", "\n"), point.Font);
1402                         valueTextSize.Height += valueTextSize.Height / 8;
1403                         float spacing = valueTextSize.Width / text.Length / 2;
1404                         valueTextSize.Width += spacing;
1405                         RectangleF labelBackPosition = new RectangleF(
1406                             rect.X,
1407                             rect.Y + rect.Height / 2f - valueTextSize.Height / 2f,
1408                             valueTextSize.Width,
1409                             valueTextSize.Height);
1410 
1411                         // Adjust position based on alignment
1412                         if (format.Alignment == StringAlignment.Near)
1413                         {
1414                             labelBackPosition.X -= spacing / 2f;
1415                         }
1416                         else if (format.Alignment == StringAlignment.Center)
1417                         {
1418                             labelBackPosition.X = rect.X + (rect.Width - valueTextSize.Width) / 2f;
1419                         }
1420                         else if (format.Alignment == StringAlignment.Far)
1421                         {
1422                             labelBackPosition.X = rect.Right - valueTextSize.Width - spacing / 2f;
1423                         }
1424 
1425                         // Draw label text outside
1426                         using (Brush brush = new SolidBrush(point.LabelForeColor))
1427                         {
1428                             graph.DrawPointLabelStringRel(
1429                                 area.Common,
1430                                 text,
1431                                 point.Font,
1432                                 brush,
1433                                 rect,
1434                                 format,
1435                                 point.LabelAngle,
1436                                 labelBackPosition,
1437                                 point.LabelBackColor,
1438                                 point.LabelBorderColor,
1439                                 point.LabelBorderWidth,
1440                                 point.LabelBorderDashStyle,
1441                                 series,
1442                                 point,
1443                                 pointIndex);
1444                         }
1445                     }
1446                     else
1447                     {
1448                         // Insert labels in label collection. This
1449                         // code is executed only if labels overlap.
1450                         this.InsertOverlapLabel(labelRectOver);
1451                         added = true;
1452                     }
1453                 }
1454 			}
1455 			// Restore old clip region
1456 			graph.Clip = oldClipRegion;
1457 
1458 
1459 			// Add empty overlap empty position
1460 			if(!added)
1461 			{
1462 				InsertOverlapLabel( RectangleF.Empty );
1463 			}
1464 
1465 			return;
1466 
1467 		}
1468 
1469 
1470 		/// <summary>
1471 		/// This method returns calculated rectangle from point position
1472 		/// for outside label. Rectangle mustn�t be out of chart area.
1473 		/// </summary>
1474 		/// <param name="labelPosition">The first position for label</param>
1475 		/// <param name="area">Chart area used for chart area position</param>
1476 		/// <param name="text">Label text</param>
1477 		/// <param name="format">Text format</param>
1478 		/// <param name="graph">Chart Graphics object</param>
1479 		/// <param name="point">Data point</param>
1480 		/// <param name="leftOrientation">Orientation for label. It could be left or right.</param>
1481 		/// <returns>Calculated rectangle for label</returns>
GetLabelRect( PointF labelPosition, ChartArea area, string text, StringFormat format, ChartGraphics graph, DataPoint point, bool leftOrientation )1482 		private RectangleF GetLabelRect( PointF labelPosition, ChartArea area, string text, StringFormat format, ChartGraphics graph, DataPoint point, bool leftOrientation )
1483 		{
1484 			RectangleF labelRect = RectangleF.Empty;
1485 			if( leftOrientation )
1486 			{
1487 				labelRect.X = area.Position.X;
1488 				labelRect.Y = area.Position.Y;
1489 				labelRect.Width = labelPosition.X - area.Position.X;
1490 				labelRect.Height = area.Position.Height;
1491 			}
1492 			else
1493 			{
1494 				labelRect.X = labelPosition.X;
1495 				labelRect.Y = area.Position.Y;
1496 				labelRect.Width = area.Position.Right - labelPosition.X;
1497 				labelRect.Height = area.Position.Height;
1498 			}
1499 
1500 			// Find bounding rectangle of the text
1501 			SizeF size = graph.MeasureStringRel( text.Replace("\\n", "\n"), point.Font, labelRect.Size, format );
1502 			labelRect.Y = labelPosition.Y - size.Height / 2 * 1.8f;
1503 			labelRect.Height = size.Height * 1.8f;
1504 
1505 			return labelRect;
1506 		}
1507 
1508 
1509 
1510 		/// <summary>
1511 		/// This method returns Pie Label Style enumeration
1512 		/// from Data Point Custom attribute.
1513 		/// </summary>
1514 		/// <param name="point">Data Point</param>
1515 		/// <returns>Pie label style enumeration</returns>
GetLabelStyle( DataPoint point )1516 		private PieLabelStyle GetLabelStyle( DataPoint point )
1517 		{
1518 			Series series = point.series;
1519 
1520 			PieLabelStyle style = PieLabelStyle.Inside;
1521 
1522 			// Get label style attribute from series
1523 			if(series.IsCustomPropertySet(CustomPropertyName.LabelStyle))
1524 			{
1525 				string labelStyleAttrib = series[CustomPropertyName.LabelStyle];
1526 
1527 				// Labels Disabled
1528                 if (String.Compare(labelStyleAttrib, "disabled", StringComparison.OrdinalIgnoreCase) == 0)
1529 					style = PieLabelStyle.Disabled;
1530                 else if (String.Compare(labelStyleAttrib, "outside", StringComparison.OrdinalIgnoreCase) == 0)
1531 					style = PieLabelStyle.Outside;
1532 				else
1533 					style = PieLabelStyle.Inside;
1534 			}
1535 			else if(series.IsCustomPropertySet(CustomPropertyName.PieLabelStyle))
1536 			{
1537 				string labelStyleAttrib = series[CustomPropertyName.PieLabelStyle];
1538 
1539 				// Labels Disabled
1540                 if (String.Compare(labelStyleAttrib, "disabled", StringComparison.OrdinalIgnoreCase) == 0)
1541 					style = PieLabelStyle.Disabled;
1542                 else if (String.Compare(labelStyleAttrib, "outside", StringComparison.OrdinalIgnoreCase) == 0)
1543 					style = PieLabelStyle.Outside;
1544 				else
1545 					style = PieLabelStyle.Inside;
1546 			}
1547 
1548 			// Get label style attribute from point
1549 			if(point.IsCustomPropertySet(CustomPropertyName.LabelStyle))
1550 			{
1551 				string labelStyleAttrib = point[CustomPropertyName.LabelStyle];
1552 
1553 				// Labels Disabled
1554                 if (String.Compare(labelStyleAttrib, "disabled", StringComparison.OrdinalIgnoreCase) == 0)
1555 					style = PieLabelStyle.Disabled;
1556                 else if (String.Compare(labelStyleAttrib, "outside", StringComparison.OrdinalIgnoreCase) == 0)
1557 					style = PieLabelStyle.Outside;
1558 				else
1559 					style = PieLabelStyle.Inside;
1560 			}
1561 			else if(point.IsCustomPropertySet(CustomPropertyName.PieLabelStyle))
1562 			{
1563 				string labelStyleAttrib = point[CustomPropertyName.PieLabelStyle];
1564 
1565 				// Labels Disabled
1566                 if (String.Compare(labelStyleAttrib, "disabled", StringComparison.OrdinalIgnoreCase) == 0)
1567 					style = PieLabelStyle.Disabled;
1568                 else if (String.Compare(labelStyleAttrib, "outside", StringComparison.OrdinalIgnoreCase) == 0)
1569 					style = PieLabelStyle.Outside;
1570 				else
1571 					style = PieLabelStyle.Inside;
1572 			}
1573 
1574 			return style;
1575 		}
1576 
1577 		/// <summary>
1578 		/// Estimate Labels.
1579 		/// </summary>
1580 		/// <param name="graph">Chart Graphics object</param>
1581 		/// <param name="middlePoint">Center of the pie chart</param>
1582 		/// <param name="relativeSize">Size of the square, which will be used for drawing pie.</param>
1583 		/// <param name="startAngle">Starting angle of a pie slice</param>
1584 		/// <param name="sweepAngle">Sweep angle of a pie slice</param>
1585 		/// <param name="point">Data point</param>
1586 		/// <param name="exploded">The pie slice is exploded</param>
1587 		/// <param name="area">Chart area</param>
EstimateLabels( ChartGraphics graph, PointF middlePoint, SizeF relativeSize, float startAngle, float sweepAngle, DataPoint point, bool exploded, ChartArea area )1588 		public bool EstimateLabels( ChartGraphics graph, PointF middlePoint, SizeF relativeSize, float startAngle, float sweepAngle, DataPoint point, bool exploded, ChartArea area )
1589 		{
1590 			float labelsHorizontalLineSize = 1; // Horizontal line size for outside labels
1591 			float labelsRadialLineSize = 1; // Radial line size for outside labels
1592 			float shift;
1593 
1594 			string pointLabel = this.GetPointLabel(point);
1595 
1596 			Series	series = point.series;
1597 
1598 			PieLabelStyle style = PieLabelStyle.Inside;
1599 
1600 			// Get label style attribute from series
1601 			if(series.IsCustomPropertySet(CustomPropertyName.LabelStyle))
1602 			{
1603 				string labelStyleAttrib = series[CustomPropertyName.LabelStyle];
1604 
1605 				// Labels Disabled
1606 				if( String.Compare(labelStyleAttrib,"disabled", StringComparison.OrdinalIgnoreCase) == 0 )
1607 					style = PieLabelStyle.Disabled;
1608                 else if (String.Compare(labelStyleAttrib, "outside", StringComparison.OrdinalIgnoreCase) == 0)
1609 					style = PieLabelStyle.Outside;
1610 				else
1611 					style = PieLabelStyle.Inside;
1612 			}
1613 			else if(series.IsCustomPropertySet(CustomPropertyName.PieLabelStyle))
1614 			{
1615 				string labelStyleAttrib = series[CustomPropertyName.PieLabelStyle];
1616 
1617 				// Labels Disabled
1618                 if (String.Compare(labelStyleAttrib, "disabled", StringComparison.OrdinalIgnoreCase) == 0)
1619 					style = PieLabelStyle.Disabled;
1620                 else if (String.Compare(labelStyleAttrib, "outside", StringComparison.OrdinalIgnoreCase) == 0)
1621 					style = PieLabelStyle.Outside;
1622 				else
1623 					style = PieLabelStyle.Inside;
1624 			}
1625 
1626 			// Get label style attribute from point
1627 			if(point.IsCustomPropertySet(CustomPropertyName.LabelStyle))
1628 			{
1629 				string labelStyleAttrib = point[CustomPropertyName.LabelStyle];
1630 
1631 				// Labels Disabled
1632                 if (String.Compare(labelStyleAttrib, "disabled", StringComparison.OrdinalIgnoreCase) == 0)
1633 					style = PieLabelStyle.Disabled;
1634                 else if (String.Compare(labelStyleAttrib, "outside", StringComparison.OrdinalIgnoreCase) == 0)
1635 					style = PieLabelStyle.Outside;
1636 				else
1637 					style = PieLabelStyle.Inside;
1638 			}
1639 			else if(point.IsCustomPropertySet(CustomPropertyName.PieLabelStyle))
1640 			{
1641 				string labelStyleAttrib = point[CustomPropertyName.PieLabelStyle];
1642 
1643 				// Labels Disabled
1644                 if (String.Compare(labelStyleAttrib, "disabled", StringComparison.OrdinalIgnoreCase) == 0)
1645 					style = PieLabelStyle.Disabled;
1646                 else if (String.Compare(labelStyleAttrib, "outside", StringComparison.OrdinalIgnoreCase) == 0)
1647 					style = PieLabelStyle.Outside;
1648 				else
1649 					style = PieLabelStyle.Inside;
1650 			}
1651 
1652 			// Take labels radial line size attribute from series
1653 			if(series.IsCustomPropertySet(CustomPropertyName.LabelsRadialLineSize))
1654 			{
1655 				string labelsRadialLineSizeAttrib = series[CustomPropertyName.LabelsRadialLineSize];
1656 				labelsRadialLineSize = CommonElements.ParseFloat( labelsRadialLineSizeAttrib );
1657 
1658 				// Validation
1659 				if( labelsRadialLineSize < 0 || labelsRadialLineSize > 100 )
1660                     throw new InvalidOperationException(SR.ExceptionPieRadialLineSizeInvalid);
1661 			}
1662 
1663 			// Take labels radial line size attribute from point
1664 			if(point.IsCustomPropertySet(CustomPropertyName.LabelsRadialLineSize))
1665 			{
1666 				string labelsRadialLineSizeAttrib = point[CustomPropertyName.LabelsRadialLineSize];
1667 				labelsRadialLineSize = CommonElements.ParseFloat( labelsRadialLineSizeAttrib );
1668 
1669 				// Validation
1670 				if( labelsRadialLineSize < 0 || labelsRadialLineSize > 100 )
1671                     throw new InvalidOperationException(SR.ExceptionPieRadialLineSizeInvalid);
1672 			}
1673 
1674 			// Take labels horizontal line size attribute from series
1675 			if(series.IsCustomPropertySet(CustomPropertyName.LabelsHorizontalLineSize))
1676 			{
1677 				string labelsHorizontalLineSizeAttrib = series[CustomPropertyName.LabelsHorizontalLineSize];
1678 				labelsHorizontalLineSize = CommonElements.ParseFloat( labelsHorizontalLineSizeAttrib );
1679 
1680 				// Validation
1681 				if( labelsHorizontalLineSize < 0 || labelsHorizontalLineSize > 100 )
1682                     throw new InvalidOperationException(SR.ExceptionPieHorizontalLineSizeInvalid);
1683 			}
1684 
1685 			// Take labels horizontal line size attribute from point
1686 			if(point.IsCustomPropertySet(CustomPropertyName.LabelsHorizontalLineSize))
1687 			{
1688 				string labelsHorizontalLineSizeAttrib = point[CustomPropertyName.LabelsHorizontalLineSize];
1689 				labelsHorizontalLineSize = CommonElements.ParseFloat( labelsHorizontalLineSizeAttrib );
1690 
1691 				// Validation
1692 				if( labelsHorizontalLineSize < 0 || labelsHorizontalLineSize > 100 )
1693                     throw new InvalidOperationException(SR.ExceptionPieHorizontalLineSizeInvalid);
1694 			}
1695 
1696 			float expShift = 1;
1697 
1698 
1699 			// ********************************************
1700 			// Labels are set outside pie
1701 			// ********************************************
1702 			if( style == PieLabelStyle.Outside )
1703 			{
1704 				// Coefficient which represent shift from pie border
1705 				shift = 0.5F + labelsRadialLineSize * 0.1F;
1706 
1707 				// If exploded the shift is bigger
1708 				if( exploded )
1709 					expShift = 1.2F;
1710 
1711 				float midAngle = startAngle + sweepAngle / 2;
1712 
1713 
1714 				// Find second line position
1715 				float y3 = (float)Math.Sin( (midAngle) * Math.PI / 180 ) * relativeSize.Height * shift * expShift + middlePoint.Y;
1716 				float x3;
1717 
1718 				if( midAngle > 90 && midAngle < 270 )
1719 				{
1720 					x3 = (float)Math.Cos( (midAngle) * Math.PI / 180 ) * relativeSize.Width * shift * expShift + middlePoint.X - relativeSize.Width / 10 * labelsHorizontalLineSize;
1721 				}
1722 				else
1723 				{
1724 					x3 = (float)Math.Cos( (midAngle) * Math.PI / 180 ) * relativeSize.Width * shift * expShift + middlePoint.X + relativeSize.Width / 10 * labelsHorizontalLineSize;
1725 				}
1726 
1727 				// Get label text
1728 				string text;
1729 				if( pointLabel.Length == 0 && point.IsValueShownAsLabel )
1730 				{
1731 					text = ValueConverter.FormatValue(
1732 						series.Chart,
1733 						point,
1734                         point.Tag,
1735 						point.YValues[0],
1736 						point.LabelFormat,
1737 						point.series.YValueType,
1738 						ChartElementType.DataPoint);
1739 				}
1740 				else
1741 				{
1742 					text = pointLabel;
1743 				}
1744 
1745 				SizeF size = graph.MeasureStringRel( text.Replace("\\n", "\n"), point.Font);
1746 
1747 				_labelsFit = true;
1748 
1749 				if(this._labelsOverlap)
1750 				{
1751 					if( midAngle > 90 && midAngle < 270 )
1752 					{
1753 						float xOverlap = -relativeSize.Width * shift * expShift + middlePoint.X - relativeSize.Width / 10 * labelsHorizontalLineSize;
1754 						if( (xOverlap - size.Width) < area.Position.X )
1755 						{
1756 							_labelsFit = false;
1757 						}
1758 					}
1759 					else
1760 					{
1761 						float xOverlap = relativeSize.Width * shift * expShift + middlePoint.X + relativeSize.Width / 10 * labelsHorizontalLineSize;
1762 						if( (xOverlap + size.Width) > area.Position.Right )
1763 						{
1764 							_labelsFit = false;
1765 						}
1766 					}
1767 				}
1768 				else
1769 				{
1770 					if( midAngle > 90 && midAngle < 270 )
1771 					{
1772 						if( x3 - size.Width < area.PlotAreaPosition.ToRectangleF().Left )
1773 							_labelsFit = false;
1774 					}
1775 					else
1776 					{
1777 						if( x3 + size.Width > area.PlotAreaPosition.ToRectangleF().Right )
1778 							_labelsFit = false;
1779 					}
1780 
1781 					if( midAngle > 180 && midAngle < 360 )
1782 					{
1783 						if( y3 - size.Height/2 < area.PlotAreaPosition.ToRectangleF().Top )
1784 							_labelsFit = false;
1785 					}
1786 					else
1787 					{
1788 						if( y3 + size.Height/2 > area.PlotAreaPosition.ToRectangleF().Bottom )
1789 							_labelsFit = false;
1790 					}
1791 				}
1792 
1793 			}
1794 			return true;
1795 		}
1796 
1797 		/// <summary>
1798 		/// This method adds map area information.
1799 		/// </summary>
1800 		/// <param name="common">The Common elements object</param>
1801 		/// <param name="point">Data Point</param>
1802 		/// <param name="startAngle">Start Angle</param>
1803 		/// <param name="sweepAngle">Sweep Angle</param>
1804 		/// <param name="rectangle">Rectangle of the pie</param>
1805 		/// <param name="doughnut">True if doughnut</param>
1806 		/// <param name="doughnutRadius">Doughnut radius in %</param>
1807 		/// <param name="graph">Chart graphics object</param>
1808 		/// <param name="pointIndex">Data point index</param>
Map( CommonElements common, DataPoint point, float startAngle, float sweepAngle, RectangleF rectangle, bool doughnut, float doughnutRadius, ChartGraphics graph, int pointIndex )1809 		private void Map( CommonElements common, DataPoint point, float startAngle, float sweepAngle, RectangleF rectangle, bool doughnut, float doughnutRadius, ChartGraphics graph, int pointIndex )
1810 		{
1811 			// Create a graphics path
1812             using (GraphicsPath path = new GraphicsPath())
1813             {
1814 
1815                 // Create the interior doughnut rectangle
1816                 RectangleF doughnutRect = RectangleF.Empty;
1817 
1818                 doughnutRect.X = rectangle.X + rectangle.Width * (1 - (100 - doughnutRadius) / 100) / 2;
1819                 doughnutRect.Y = rectangle.Y + rectangle.Height * (1 - (100 - doughnutRadius) / 100) / 2;
1820                 doughnutRect.Width = rectangle.Width * (100 - doughnutRadius) / 100;
1821                 doughnutRect.Height = rectangle.Height * (100 - doughnutRadius) / 100;
1822 
1823                 // Get absolute coordinates of the pie rectangle
1824                 rectangle = graph.GetAbsoluteRectangle(rectangle);
1825 
1826                 // Add the pie to the graphics path
1827                 path.AddPie(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, startAngle, sweepAngle);
1828                 // VSTS #250394 (Dev10:591140) Fix - Control should not return �useless� map areas
1829                 if (sweepAngle <= 0)
1830                 {
1831                     return;
1832                 }
1833                 // If the chart type is doughnut
1834                 if (doughnut)
1835                 {
1836 
1837                     // Get absolute coordinates of the interior doughnut rectangle
1838                     doughnutRect = graph.GetAbsoluteRectangle(doughnutRect);
1839 
1840                     // Add the interior doughnut region to the graphics path
1841                     path.AddPie(doughnutRect.X, doughnutRect.Y, doughnutRect.Width, doughnutRect.Width, startAngle, sweepAngle);
1842                 }
1843 
1844                 // Make a polygon from curves
1845                 path.Flatten(new Matrix(), 1f);
1846 
1847                 // Create an area of points and convert them to
1848                 // relative coordinates.
1849                 PointF[] pointNew = new PointF[path.PointCount];
1850                 for (int i = 0; i < path.PointCount; i++)
1851                 {
1852                     pointNew[i] = graph.GetRelativePoint(path.PathPoints[i]);
1853                 }
1854 
1855                 // Allocate array of floats
1856                 float[] coord = new float[path.PointCount * 2];
1857 
1858                 // Transfer path points
1859                 for (int index = 0; index < path.PointCount; index++)
1860                 {
1861                     coord[2 * index] = pointNew[index].X;
1862                     coord[2 * index + 1] = pointNew[index].Y;
1863                 }
1864 
1865 
1866 
1867                 // Check if processing collected data point
1868                 if (point.IsCustomPropertySet("_COLLECTED_DATA_POINT"))
1869                 {
1870                     // Add point to the map area
1871                     common.HotRegionsList.AddHotRegion(
1872                         graph,
1873                         path,
1874                         false,
1875                         point.ReplaceKeywords(point.ToolTip),
1876 #if Microsoft_CONTROL
1877 					string.Empty,
1878 					string.Empty,
1879 					string.Empty,
1880 #else // Microsoft_CONTROL
1881  point.ReplaceKeywords(point.Url),
1882                         point.ReplaceKeywords(point.MapAreaAttributes),
1883                         point.ReplaceKeywords(point.PostBackValue),
1884 #endif // Microsoft_CONTROL
1885  point,
1886                         ChartElementType.DataPoint);
1887 
1888                     return;
1889                 }
1890 
1891 
1892 
1893                 // Add points to the map area
1894                 common.HotRegionsList.AddHotRegion(
1895                     path,
1896                     false,
1897                     coord,
1898                     point,
1899                     point.series.Name,
1900                     pointIndex
1901                     );
1902             }
1903 		}
1904 
1905 		/// <summary>
1906 		/// This method is introduced to check colors of palette. For
1907 		/// pie chart the first pie slice and the second pie slice can
1908 		/// not have same color because they are connected.
1909 		/// </summary>
1910 		/// <param name="points">Data points used for pie chart</param>
CheckPaleteColors( DataPointCollection points )1911 		private void CheckPaleteColors( DataPointCollection points )
1912 		{
1913 			DataPoint firstPoint, lastPoint;
1914 
1915 			firstPoint = points[0];
1916 			lastPoint = points[ points.Count - 1 ];
1917 
1918 			// Change color for last point if same as the first and if it is from pallete.
1919 			if( firstPoint.tempColorIsSet && lastPoint.tempColorIsSet && firstPoint.Color == lastPoint.Color )
1920 			{
1921 				lastPoint.Color = points[ points.Count / 2 ].Color;
1922 				lastPoint.tempColorIsSet = true;
1923 			}
1924 		}
1925 
1926 		#endregion
1927 
1928 		#region 2DLabels
1929 
1930 		/// <summary>
1931 		/// This method finds vertical position for left and
1932 		/// right labels on that way that labels do not
1933 		/// overlap each other.
1934 		/// </summary>
1935 		/// <param name="area">Chart area position</param>
1936 		/// <returns>True if it is possible to find position that labels do not overlap each other.</returns>
PrepareLabels( RectangleF area )1937 		private bool PrepareLabels( RectangleF area )
1938 		{
1939 			// Initialization of local variables
1940 			float splitPoint = area.X + area.Width / 2f;
1941 			int numberOfLeft = 0;
1942 			int numberOfRight = 0;
1943 
1944 			// Find the number of left and right labels.
1945 			foreach( RectangleF rect in this._labelsRectangles )
1946 			{
1947 				if( rect.X < splitPoint )
1948 				{
1949 					numberOfLeft++;
1950 				}
1951 				else
1952 				{
1953 					numberOfRight++;
1954 				}
1955 			}
1956 
1957 			// **********************************************
1958 			// Find the best position for LEFT labels
1959 			// **********************************************
1960 			bool leftResult = true;
1961 			if(numberOfLeft > 0)
1962 			{
1963 				double [] startPoints = new double[numberOfLeft];
1964 				double [] endPoints = new double[numberOfLeft];
1965 				int [] positionIndex = new Int32[numberOfLeft];
1966 
1967 				// Fill double arrays with Top and Bottom coordinates
1968 				// from the label rectangle.
1969 				int splitIndex = 0;
1970 				for( int index = 0; index < _labelsRectangles.Count; index++ )
1971 				{
1972 					RectangleF rect = (RectangleF)_labelsRectangles[index];
1973 					if( rect.X < splitPoint )
1974 					{
1975 						startPoints[ splitIndex ] = rect.Top;
1976 						endPoints[ splitIndex ] = rect.Bottom;
1977 						positionIndex[ splitIndex ] = index;
1978 						splitIndex++;
1979 					}
1980 				}
1981 
1982 				// Sort label positions
1983 				this.SortIntervals( startPoints, endPoints, positionIndex );
1984 
1985 				// Find no overlapping positions if possible.
1986 				if( this.ArrangeOverlappingIntervals( startPoints, endPoints, area.Top, area.Bottom ) )
1987 				{
1988 					// Fill label rectangle top and bottom coordinates
1989 					// from double arrays.
1990 					splitIndex = 0;
1991 					for( int index = 0; index < _labelsRectangles.Count; index++ )
1992 					{
1993 						RectangleF rect = (RectangleF)_labelsRectangles[index];
1994 						if( rect.X < splitPoint )
1995 						{
1996 							rect.Y = (float)startPoints[ splitIndex ];
1997 							rect.Height = (float)(endPoints[ splitIndex ] - rect.Top);
1998 							_labelsRectangles[positionIndex[ splitIndex ]] = rect;
1999 							splitIndex++;
2000 
2001 						}
2002 					}
2003 				}
2004 				else
2005 				{
2006 					leftResult = false;
2007 				}
2008 			}
2009 
2010 			// **********************************************
2011 			// Find the best position for Right labels
2012 			// **********************************************
2013 			bool rigthResult = true;
2014 			if(numberOfRight > 0)
2015 			{
2016 				double [] startPoints = new double[numberOfRight];
2017 				double [] endPoints = new double[numberOfRight];
2018 				int [] positionIndex = new Int32[numberOfRight];
2019 
2020 				// Fill double arrays with Top and Bottom coordinates
2021 				// from the label rectangle.
2022 				int splitIndex = 0;
2023 				for( int index = 0; index < _labelsRectangles.Count; index++ )
2024 				{
2025 					RectangleF rect = (RectangleF)_labelsRectangles[index];
2026 					if( rect.X >= splitPoint )
2027 					{
2028 						startPoints[ splitIndex ] = rect.Top;
2029 						endPoints[ splitIndex ] = rect.Bottom;
2030 						positionIndex[ splitIndex ] = index;
2031 						splitIndex++;
2032 					}
2033 				}
2034 
2035 				// Sort label positions
2036 				this.SortIntervals( startPoints, endPoints, positionIndex );
2037 
2038 				// Find no overlapping positions if possible.
2039 				if( this.ArrangeOverlappingIntervals( startPoints, endPoints, area.Top, area.Bottom ) )
2040 				{
2041 					// Fill label rectangle top and bottom coordinates
2042 					// from double arrays.
2043 					splitIndex = 0;
2044 					for( int index = 0; index < _labelsRectangles.Count; index++ )
2045 					{
2046 						RectangleF rect = (RectangleF)_labelsRectangles[index];
2047 						if( rect.X >= splitPoint )
2048 						{
2049 							rect.Y = (float)startPoints[ splitIndex ];
2050 							rect.Height = (float)(endPoints[ splitIndex ] - rect.Top);
2051 							_labelsRectangles[positionIndex[ splitIndex ]] = rect;
2052 							splitIndex++;
2053 						}
2054 					}
2055 				}
2056 				else
2057 				{
2058 					rigthResult = false;
2059 				}
2060 			}
2061 
2062 			return ( (!leftResult || !rigthResult) ? true : false );
2063 		}
2064 
2065 		/// <summary>
2066 		/// This algorithm sorts labels vertical intervals.
2067 		/// </summary>
2068 		/// <param name="startOfIntervals">Double array of label interval start points</param>
2069 		/// <param name="endOfIntervals">Double array of label interval end points</param>
2070 		/// <param name="positinIndex">Integer array of label interval indexes</param>
SortIntervals( double [] startOfIntervals, double [] endOfIntervals, int [] positinIndex )2071 		private void SortIntervals( double [] startOfIntervals, double [] endOfIntervals, int [] positinIndex )
2072 		{
2073 			double firstCenter;
2074 			double secondCenter;
2075 			double midDouble;
2076 			int midInt;
2077 
2078 			// Sorting loops
2079 			for( int firstIndex = 0; firstIndex < startOfIntervals.Length; firstIndex++ )
2080 			{
2081 				for( int secondIndex = firstIndex; secondIndex < startOfIntervals.Length; secondIndex++ )
2082 				{
2083 					firstCenter = ( startOfIntervals[ firstIndex ] + endOfIntervals[ firstIndex ] ) / 2.0;
2084 					secondCenter = ( startOfIntervals[ secondIndex ] + endOfIntervals[ secondIndex ] ) / 2.0;
2085 
2086 					if( firstCenter > secondCenter )
2087 					{
2088 						// Sort start points
2089 						midDouble = startOfIntervals[ firstIndex ];
2090 						startOfIntervals[ firstIndex ] = startOfIntervals[ secondIndex ];
2091 						startOfIntervals[ secondIndex ] = midDouble;
2092 
2093 						// Sort end points
2094 						midDouble = endOfIntervals[ firstIndex ];
2095 						endOfIntervals[ firstIndex ] = endOfIntervals[ secondIndex ];
2096 						endOfIntervals[ secondIndex ] = midDouble;
2097 
2098 						// Sort indexes
2099 						midInt = positinIndex[ firstIndex ];
2100 						positinIndex[ firstIndex ] = positinIndex[ secondIndex ];
2101 						positinIndex[ secondIndex ] = midInt;
2102 					}
2103 				}
2104 			}
2105 		}
2106 
2107 		/// <summary>
2108 		/// This method inserts label rectangles
2109 		/// into the collection.
2110 		/// </summary>
2111 		/// <param name="labelRect">Label Rectangle</param>
InsertOverlapLabel( RectangleF labelRect )2112 		private void InsertOverlapLabel( RectangleF labelRect )
2113 		{
2114 			// Check if any pair of labels overlap
2115 			if(!labelRect.IsEmpty)
2116 			{
2117 				foreach( RectangleF rect in _labelsRectangles )
2118 				{
2119 					if( labelRect.IntersectsWith( rect ) )
2120 					{
2121 						this._labelsOverlap = true;
2122 					}
2123 				}
2124 			}
2125 
2126 			// Add rectangle to the collection
2127 			_labelsRectangles.Add( labelRect );
2128 		}
2129 
2130 		/// <summary>
2131 		/// This method will find the best position for labels.
2132 		/// It is based on finding non overlap intervals for
2133 		/// left or right side of the pie. This is
2134 		/// recursive algorithm.
2135 		/// </summary>
2136 		/// <param name="startOfIntervals">The start positions of intervals.</param>
2137 		/// <param name="endOfIntervals">The end positions of intervals.</param>
2138 		/// <param name="startArea">Start position of chart area vertical range.</param>
2139 		/// <param name="endArea">End position of chart area vertical range.</param>
2140 		/// <returns>False if non overlapping positions for intervals can not be found.</returns>
ArrangeOverlappingIntervals( double [] startOfIntervals, double [] endOfIntervals, double startArea, double endArea )2141 		private bool ArrangeOverlappingIntervals( double [] startOfIntervals, double [] endOfIntervals, double startArea, double endArea )
2142 		{
2143 
2144 			// Invalidation
2145 			if( startOfIntervals.Length != endOfIntervals.Length )
2146 			{
2147                 throw new InvalidOperationException(SR.ExceptionPieIntervalsInvalid);
2148 			}
2149 
2150 			ShiftOverlappingIntervals( startOfIntervals, endOfIntervals );
2151 
2152 			// Find amount of empty space between intervals.
2153 			double emptySpace = 0;
2154 			for( int intervalIndex = 0; intervalIndex < startOfIntervals.Length - 1; intervalIndex++ )
2155 			{
2156 				// Check overlapping
2157 				if( startOfIntervals[ intervalIndex + 1 ] < endOfIntervals[ intervalIndex ] )
2158 				{
2159                     //throw new InvalidOperationException( SR.ExceptionPieIntervalsOverlapping );
2160 				}
2161 
2162 				emptySpace += startOfIntervals[ intervalIndex + 1 ] -  endOfIntervals[ intervalIndex ];
2163 			}
2164 
2165 			//Find how much intervals are out of area. Out of area could be positive value only.
2166 			double outOfArea = ( endOfIntervals[ endOfIntervals.Length - 1 ] - endArea ) + ( startArea - startOfIntervals[ 0 ] );
2167 			if( outOfArea <= 0 )
2168 			{
2169 				// This algorithm shifts all intervals for the same
2170 				// amount. It is trying to put all intervals inside
2171 				// chart area range.
2172 				ShiftIntervals( startOfIntervals, endOfIntervals, startArea, endArea );
2173 				return true;
2174 			}
2175 
2176 			// There is no enough space for all intervals.
2177 			if( outOfArea > emptySpace )
2178 			{
2179 				return false;
2180 			}
2181 
2182 			// This method reduces empty space between intervals.
2183 			ReduceEmptySpace( startOfIntervals, endOfIntervals, ( emptySpace - outOfArea ) / emptySpace );
2184 
2185 			// This algorithm shifts all intervals for the same
2186 			// amount. It is trying to put all intervals inside
2187 			// chart area range.
2188 			ShiftIntervals( startOfIntervals, endOfIntervals, startArea, endArea );
2189 
2190 			return true;
2191 		}
2192 
2193 		/// <summary>
2194 		/// This method reduces empty space between intervals.
2195 		/// </summary>
2196 		/// <param name="startOfIntervals">The start positions of intervals.</param>
2197 		/// <param name="endOfIntervals">The end positions of intervals.</param>
2198 		/// <param name="reduction">Relative value which presents size reduction.</param>
ReduceEmptySpace( double [] startOfIntervals, double [] endOfIntervals, double reduction )2199 		private void ReduceEmptySpace( double [] startOfIntervals, double [] endOfIntervals, double reduction )
2200 		{
2201 			for( int intervalIndex = 0; intervalIndex < startOfIntervals.Length - 1; intervalIndex++ )
2202 			{
2203 				// Check overlapping
2204 				if( startOfIntervals[ intervalIndex + 1 ] < endOfIntervals[ intervalIndex ] )
2205 				{
2206                     //throw new InvalidOperationException( SR.ExceptionPieIntervalsOverlapping );
2207 				}
2208 
2209 				// Reduce space
2210 				double shift = ( startOfIntervals[ intervalIndex + 1 ] -  endOfIntervals[ intervalIndex ] ) - ( startOfIntervals[ intervalIndex + 1 ] -  endOfIntervals[ intervalIndex ] ) * reduction;
2211 				for( int reductionIndex = intervalIndex + 1; reductionIndex < startOfIntervals.Length; reductionIndex++ )
2212 				{
2213 					startOfIntervals[ reductionIndex ] -= shift;
2214 					endOfIntervals[ reductionIndex ] -= shift;
2215 				}
2216 			}
2217 		}
2218 
2219 		/// <summary>
2220 		/// This algorithm shifts all intervals for the same
2221 		/// amount. It is trying to put all intervals inside
2222 		/// chart area range.
2223 		/// </summary>
2224 		/// <param name="startOfIntervals">The start positions of intervals.</param>
2225 		/// <param name="endOfIntervals">The end positions of intervals.</param>
2226 		/// <param name="startArea">Start position of chart area vertical range.</param>
2227 		/// <param name="endArea">End position of chart area vertical range.</param>
ShiftIntervals( double [] startOfIntervals, double [] endOfIntervals, double startArea, double endArea )2228 		private void ShiftIntervals( double [] startOfIntervals, double [] endOfIntervals, double startArea, double endArea )
2229 		{
2230 
2231 			double shift = 0;
2232 
2233 			if( startOfIntervals[ 0 ] < startArea )
2234 			{
2235 				shift = startArea - startOfIntervals[ 0 ];
2236 			}
2237 			else if( endOfIntervals[ endOfIntervals.Length - 1 ] > endArea )
2238 			{
2239 				shift = endArea - endOfIntervals[ endOfIntervals.Length - 1 ];
2240 			}
2241 
2242 			for( int index = 0; index < startOfIntervals.Length; index++ )
2243 			{
2244 				startOfIntervals[ index ] += shift;
2245 				endOfIntervals[ index ] += shift;
2246 			}
2247 		}
2248 
2249 		/// <summary>
2250 		/// This is used to find non overlapping position for intervals.
2251 		/// </summary>
2252 		/// <param name="startOfIntervals">The start positions of intervals.</param>
2253 		/// <param name="endOfIntervals">The end positions of intervals.</param>
2254 		/// <returns>Returns true if any label overlaps before method is used.</returns>
ShiftOverlappingIntervals( double [] startOfIntervals, double [] endOfIntervals )2255 		private void ShiftOverlappingIntervals( double [] startOfIntervals, double [] endOfIntervals )
2256 		{
2257 			// Invalidation
2258 			if( startOfIntervals.Length != endOfIntervals.Length )
2259 			{
2260                 throw new InvalidOperationException(SR.ExceptionPieIntervalsInvalid);
2261 			}
2262 
2263 			// Find first overlaping intervals
2264 			for( int index = 0; index < startOfIntervals.Length - 1; index++ )
2265 			{
2266 				// Intervals overlap
2267 				if( endOfIntervals[ index ] > startOfIntervals[ index + 1 ] )
2268 				{
2269 					double overlapRange = endOfIntervals[ index ] - startOfIntervals[ index + 1 ];
2270 					SpreadInterval( startOfIntervals, endOfIntervals, index, Math.Floor( overlapRange / 2.0 ) );
2271 				}
2272 			}
2273 		}
2274 
2275 		/// <summary>
2276 		/// This method spread all intervals down or up from
2277 		/// splitIndex. Intervals are spread only if there is no
2278 		/// empty space which will compensate shifting of intervals.
2279 		/// </summary>
2280 		/// <param name="startOfIntervals">The start positions of intervals.</param>
2281 		/// <param name="endOfIntervals">The end positions of intervals.</param>
2282 		/// <param name="splitIndex">Position of the interval which ovelap.</param>
2283 		/// <param name="overlapShift">The half of the overlapping range.</param>
SpreadInterval( double [] startOfIntervals, double [] endOfIntervals, int splitIndex, double overlapShift )2284 		private void SpreadInterval( double [] startOfIntervals, double [] endOfIntervals, int splitIndex, double overlapShift )
2285 		{
2286 			// Move first overlapping intervals.
2287 			endOfIntervals[ splitIndex ] -= overlapShift;
2288 			startOfIntervals[ splitIndex ] -= overlapShift;
2289 
2290 			endOfIntervals[ splitIndex + 1 ] += overlapShift;
2291 			startOfIntervals[ splitIndex + 1 ] += overlapShift;
2292 
2293 			// Move up other intervals if there is no enough empty space
2294 			// to compensate overlapping intervals.
2295 			if( splitIndex > 0 )
2296 			{
2297 				for( int index = splitIndex - 1; index >= 0; index-- )
2298 				{
2299 					if( endOfIntervals[ index ] > startOfIntervals[ index + 1 ] - overlapShift )
2300 					{
2301 						endOfIntervals[ index ] -= overlapShift;
2302 						startOfIntervals[ index ] -= overlapShift;
2303 					}
2304 					else
2305 					{
2306 						break;
2307 					}
2308 				}
2309 			}
2310 
2311 			// Move down other intervals if there is no enough empty space
2312 			// to compensate overlapping intervals.
2313 			if( splitIndex + 2 < startOfIntervals.Length - 1 )
2314 			{
2315 				for( int index = splitIndex + 2; index < startOfIntervals.Length; index++ )
2316 				{
2317 					if( startOfIntervals[ index ] > endOfIntervals[ index - 1 ] + overlapShift )
2318 					{
2319 						endOfIntervals[ index ] += overlapShift;
2320 						startOfIntervals[ index ] += overlapShift;
2321 					}
2322 					else
2323 					{
2324 						break;
2325 					}
2326 				}
2327 			}
2328 		}
2329 
2330 		#endregion
2331 
2332 		#region Y values related methods
2333 
2334 		/// <summary>
2335 		/// Helper function, which returns the Y value of the point.
2336 		/// </summary>
2337 		/// <param name="common">Chart common elements.</param>
2338 		/// <param name="area">Chart area the series belongs to.</param>
2339 		/// <param name="series">Sereis of the point.</param>
2340 		/// <param name="point">Point object.</param>
2341 		/// <param name="pointIndex">Index of the point.</param>
2342 		/// <param name="yValueIndex">Index of the Y value to get.</param>
2343 		/// <returns>Y value of the point.</returns>
GetYValue( CommonElements common, ChartArea area, Series series, DataPoint point, int pointIndex, int yValueIndex)2344 		virtual public double GetYValue(
2345 			CommonElements common,
2346 			ChartArea area,
2347 			Series series,
2348 			DataPoint point,
2349 			int pointIndex,
2350 			int yValueIndex)
2351 		{
2352 			return point.YValues[yValueIndex];
2353 		}
2354 
2355 		#endregion
2356 
2357 		#region 3D painting and selection methods
2358 
2359 		/// <summary>
2360 		/// This method recalculates position of pie slices
2361 		/// or checks if pie slice is selected.
2362 		/// </summary>
2363 		/// <param name="selection">If True selection mode is active, otherwise paint mode is active</param>
2364 		/// <param name="graph">The Chart Graphics object</param>
2365 		/// <param name="common">The Common elements object</param>
2366 		/// <param name="area">Chart area for this chart</param>
2367 		/// <param name="pieWidth">Pie width.</param>
ProcessChartType3D( bool selection, ChartGraphics graph, CommonElements common, ChartArea area, float pieWidth )2368 		private void ProcessChartType3D(
2369 			bool selection,
2370 			ChartGraphics graph,
2371 			CommonElements common,
2372 			ChartArea area,
2373 			float pieWidth )
2374 		{
2375 			string	explodedAttrib = "";					// Exploded attribute
2376 			bool exploded;									// Exploded pie slice
2377 			float midAngle;									// Angle between Start Angle and End Angle
2378 
2379 
2380 			// Data series collection
2381 			SeriesCollection	dataSeries = common.DataManager.Series;
2382 
2383 			// All data series from chart area which have Pie chart type
2384 			List<string>	typeSeries = area.GetSeriesFromChartType(Name);
2385 
2386 			if( typeSeries.Count == 0 )
2387 			{
2388 				return;
2389 			}
2390 
2391 			// Get first pie starting angle
2392             if (dataSeries[typeSeries[0]].IsCustomPropertySet(CustomPropertyName.PieStartAngle))
2393             {
2394                 int angle;
2395                 bool parseSucceed = int.TryParse(dataSeries[typeSeries[0]][CustomPropertyName.PieStartAngle], NumberStyles.Any, CultureInfo.InvariantCulture, out angle);
2396 
2397                 if (parseSucceed)
2398                 {
2399                     if (angle > 180 && angle <= 360)
2400                     {
2401                         angle = -(360 - angle);
2402                     }
2403                     area.Area3DStyle.Rotation = angle;
2404                 }
2405 
2406 
2407                 if (!parseSucceed || area.Area3DStyle.Rotation > 180 || area.Area3DStyle.Rotation < -180)
2408                 {
2409                     throw (new InvalidOperationException(SR.ExceptionCustomAttributeAngleOutOfRange("PieStartAngle")));
2410                 }
2411             }
2412 
2413 			// Call Back Paint event
2414 			if( !selection )
2415 			{
2416                 common.Chart.CallOnPrePaint(new ChartPaintEventArgs(dataSeries[typeSeries[0]], graph, common, area.PlotAreaPosition));
2417 			}
2418 
2419 			// The data points loop. Find Sum of data points.
2420 			double	sum = 0;
2421 			foreach( DataPoint point in dataSeries[typeSeries[0]].Points )
2422 			{
2423 				if( !point.IsEmpty )
2424 				{
2425 					sum += Math.Abs(point.YValues[0]);
2426 				}
2427 			}
2428 
2429 			// Is exploded if only one is exploded
2430 			bool isExploded = false;
2431 			foreach( DataPoint point in dataSeries[typeSeries[0]].Points )
2432 			{
2433 				if(point.IsCustomPropertySet(CustomPropertyName.Exploded))
2434 				{
2435 					explodedAttrib = point[CustomPropertyName.Exploded];
2436 					if( String.Compare(explodedAttrib,"true",StringComparison.OrdinalIgnoreCase) == 0 )
2437 					{
2438 						isExploded = true;
2439 					}
2440 				}
2441 			}
2442 
2443 			// Take radius attribute
2444 			float	doughnutRadius = 60f;
2445 			if(dataSeries[typeSeries[0]].IsCustomPropertySet(CustomPropertyName.DoughnutRadius))
2446 			{
2447 				doughnutRadius = CommonElements.ParseFloat(dataSeries[typeSeries[0]][CustomPropertyName.DoughnutRadius] );
2448 
2449 				// Validation
2450 				if( doughnutRadius < 0f || doughnutRadius > 99f )
2451                     throw (new ArgumentException(SR.ExceptionPieRadiusInvalid));
2452 
2453 			}
2454 
2455 			// Take 3D Label Line Size attribute
2456 			float	labelLineSize = 100f;
2457 			if(dataSeries[typeSeries[0]].IsCustomPropertySet(CustomPropertyName._3DLabelLineSize))
2458 			{
2459 				labelLineSize = CommonElements.ParseFloat(dataSeries[typeSeries[0]][CustomPropertyName._3DLabelLineSize] );
2460 
2461 				// Validation
2462 				if( labelLineSize < 30f || labelLineSize > 200f )
2463                     throw (new ArgumentException(SR.ExceptionPie3DLabelLineSizeInvalid));
2464 
2465 			}
2466 			labelLineSize = labelLineSize * 0.1F / 100F;
2467 
2468 			//************************************************************
2469 			//** Data point loop
2470 			//************************************************************
2471 			float [] startAngleList;
2472 			float [] sweepAngleList;
2473 			int [] pointIndexList;
2474 
2475 			// This method is introduced to check colors of palette. For
2476 			// pie chart the first pie slice and the second pie slice can
2477 			// not have same color because they are connected.
2478 			CheckPaleteColors( dataSeries[typeSeries[0]].Points );
2479 
2480 			bool sameBackFront;
2481 			DataPoint [] points = PointOrder( dataSeries[typeSeries[0]], area, out startAngleList, out sweepAngleList, out pointIndexList, out sameBackFront );
2482 
2483 			// There are no points or all points are empty.
2484 			if( points == null )
2485 			{
2486 				return;
2487 			}
2488 
2489 			RectangleF plotingRectangle = new RectangleF( area.Position.ToRectangleF().X + 1, area.Position.ToRectangleF().Y + 1, area.Position.ToRectangleF().Width-2, area.Position.ToRectangleF().Height-2 );
2490 
2491 			// Check if any data point has outside label
2492 			bool outside = false;
2493 			foreach( DataPoint point in points )
2494 			{
2495 				if( GetLabelStyle( point ) == PieLabelStyle.Outside )
2496 				{
2497 					outside = true;
2498 				}
2499 			}
2500 
2501 			// If outside labels resize Pie size
2502 			if( outside )
2503 			{
2504 				InitPieSize( graph, area, ref plotingRectangle, ref pieWidth, points, startAngleList, sweepAngleList, dataSeries[typeSeries[0]], labelLineSize );
2505 			}
2506 
2507 			// Initialize Matrix 3D
2508 			area.matrix3D.Initialize(
2509 				plotingRectangle,
2510 				pieWidth,
2511 				area.Area3DStyle.Inclination,
2512 				0F,
2513 				0,
2514 				false);
2515 
2516 			//***********************************************************
2517 			//** Initialize Lighting
2518 			//***********************************************************
2519 			area.matrix3D.InitLight(
2520 				area.Area3DStyle.LightStyle
2521 				);
2522 
2523 			// Turns are introduce because of special case � Big pie slice, which
2524 			// is bigger, then 180 degree and it is back and
2525 			// front point in same time. If special case exists drawing has to be split
2526 			// into 4 parts: 1. Drawing back pie slices, 2. Drawing the first part of
2527 			// big slice and other points, 3. Drawing second part of big slice and
2528 			// 4. Drawing top of the pie slices.
2529 			for( int turn = 0; turn < 5; turn++ )
2530 			{
2531 				int	pointIndx = 0;
2532 				foreach( DataPoint point in points )
2533 				{
2534                     // Reset point anchor location
2535                     point.positionRel = PointF.Empty;
2536 
2537 					// Do not process empty points
2538 					if( point.IsEmpty )
2539 					{
2540 						pointIndx++;
2541 						continue;
2542 					}
2543 
2544 					float	sweepAngle = sweepAngleList[pointIndx];
2545 					float	startAngle = startAngleList[pointIndx];
2546 
2547 					// Rectangle size
2548 					RectangleF	rectangle;
2549 					if( area.InnerPlotPosition.Auto )
2550 						rectangle = new RectangleF( plotingRectangle.X, plotingRectangle.Y, plotingRectangle.Width, plotingRectangle.Height );
2551 					else
2552 						rectangle = new RectangleF( area.PlotAreaPosition.ToRectangleF().X, area.PlotAreaPosition.ToRectangleF().Y, area.PlotAreaPosition.ToRectangleF().Width, area.PlotAreaPosition.ToRectangleF().Height );
2553 
2554 					// Find smallest edge
2555 					SizeF absoluteSize = graph.GetAbsoluteSize( new SizeF( rectangle.Width, rectangle.Height ) );
2556 					float absRadius = ( absoluteSize.Width < absoluteSize.Height ) ? absoluteSize.Width : absoluteSize.Height;
2557 
2558 					// Size of the square, which will be used for drawing pie.
2559 					SizeF relativeSize = graph.GetRelativeSize( new SizeF( absRadius, absRadius ) );
2560 
2561 					// Center of the pie
2562 					PointF middlePoint = new PointF( rectangle.X + rectangle.Width / 2, rectangle.Y + rectangle.Height / 2 );
2563 
2564 					// Rectangle which will always create circle, never ellipse.
2565 					rectangle = new RectangleF( middlePoint.X - relativeSize.Width / 2, middlePoint.Y - relativeSize.Height / 2, relativeSize.Width, relativeSize.Height );
2566 
2567 					// Check Exploded attribute for data point
2568 					exploded = false;
2569 					if(point.IsCustomPropertySet(CustomPropertyName.Exploded))
2570 					{
2571 						explodedAttrib = point[CustomPropertyName.Exploded];
2572 						if( String.Compare(explodedAttrib,"true",StringComparison.OrdinalIgnoreCase) == 0 )
2573 							exploded = true;
2574 						else
2575 							exploded = false;
2576 					}
2577 
2578 					// Size correction because of exploded or labels
2579 					float sizeCorrection = 1.0F;
2580 					if( isExploded )
2581 					{
2582 						sizeCorrection = 0.82F;
2583 
2584 						rectangle.X += rectangle.Width * ( 1 - sizeCorrection ) / 2;
2585 						rectangle.Y += rectangle.Height * ( 1 - sizeCorrection ) / 2;
2586 						rectangle.Width = rectangle.Width * sizeCorrection;
2587 						rectangle.Height = rectangle.Height * sizeCorrection;
2588 					}
2589 
2590 
2591 					// Find Direction to move exploded pie slice
2592 					if( exploded )
2593 					{
2594 						_sliceExploded = true;
2595 						midAngle = ( 2 * startAngle + sweepAngle ) / 2;
2596 						double xComponent = Math.Cos( midAngle * Math.PI / 180 ) * rectangle.Width / 10;
2597 						double yComponent = Math.Sin( midAngle * Math.PI / 180 ) * rectangle.Height / 10;
2598 
2599 						rectangle.Offset( (float)xComponent, (float)yComponent );
2600 					}
2601 
2602 					// Adjust inner plot position
2603 					if(area.InnerPlotPosition.Auto)
2604 					{
2605 						RectangleF rect = rectangle;
2606 						rect.X = (rect.X - area.Position.X) / area.Position.Width * 100f;
2607 						rect.Y = (rect.Y - area.Position.Y) / area.Position.Height * 100f;
2608 						rect.Width = rect.Width / area.Position.Width * 100f;
2609 						rect.Height = rect.Height / area.Position.Height * 100f;
2610 						area.InnerPlotPosition.SetPositionNoAuto(rect.X, rect.Y, rect.Width, rect.Height);
2611 					}
2612 
2613 					// Start Svg Selection mode
2614 					graph.StartHotRegion( point );
2615 
2616 					// Drawing or selection of pie clice
2617 					Draw3DPie( turn, graph, point, area, rectangle, startAngle, sweepAngle, doughnutRadius, pieWidth, sameBackFront, exploded, pointIndexList[pointIndx] );
2618 
2619 					// End Svg Selection mode
2620 					graph.EndHotRegion( );
2621 
2622 					if( turn == 1 )
2623 					{
2624 						// Outside labels
2625 						if( GetLabelStyle( point ) == PieLabelStyle.Outside )
2626 						{
2627 							FillPieLabelOutside( graph, area, rectangle, pieWidth, point, startAngle, sweepAngle, pointIndx, doughnutRadius, exploded );
2628 						}
2629 					}
2630 					if( turn == 2 )
2631 					{
2632 
2633 						// Outside labels
2634 						if( GetLabelStyle( point ) == PieLabelStyle.Outside && pointIndx == 0 )
2635 						{
2636 							labelColumnLeft.Sort();
2637 							labelColumnLeft.AdjustPositions();
2638 							labelColumnRight.Sort();
2639 							labelColumnRight.AdjustPositions();
2640 						}
2641 
2642 					}
2643 
2644 					// Increae point index
2645 					pointIndx++;
2646 				}
2647 			}
2648 
2649 			// Call Paint event
2650 			if( !selection )
2651 			{
2652                 common.Chart.CallOnPostPaint(new ChartPaintEventArgs(dataSeries[typeSeries[0]], graph, common, area.PlotAreaPosition));
2653 			}
2654 		}
2655 
2656 		/// <summary>
2657 		/// This method draws a part of a pie slice. Which part is drown
2658 		/// depend on turn. There is special case if there is a big pie
2659 		/// slice (>180) when one pie slice has to be split on parts
2660 		/// and between that other small pie slices has to be drawn.
2661 		/// </summary>
2662 		/// <param name="turn">Turn for drawing.</param>
2663 		/// <param name="graph">Chart Graphics</param>
2664 		/// <param name="point">Data Point to draw</param>
2665 		/// <param name="area">Chart area</param>
2666 		/// <param name="rectangle">Rectangle used for drawing pie clice.</param>
2667 		/// <param name="startAngle">Start angle for pie slice</param>
2668 		/// <param name="sweepAngle">End angle for pie slice</param>
2669 		/// <param name="doughnutRadius">Inner Radius if chart is doughnut</param>
2670 		/// <param name="pieWidth">Width of the pie</param>
2671 		/// <param name="sameBackFront">Pie slice is >180 and same pie slice is back and front slice</param>
2672 		/// <param name="exploded">Pie slice is exploded</param>
2673 		/// <param name="pointIndex">Point Index</param>
Draw3DPie( int turn, ChartGraphics graph, DataPoint point, ChartArea area, RectangleF rectangle, float startAngle, float sweepAngle, float doughnutRadius, float pieWidth, bool sameBackFront, bool exploded, int pointIndex )2674 		private void Draw3DPie(
2675 			int turn,
2676 			ChartGraphics graph,
2677 			DataPoint point,
2678 			ChartArea area,
2679 			RectangleF rectangle,
2680 			float startAngle,
2681 			float sweepAngle,
2682 			float doughnutRadius,
2683 			float pieWidth,
2684 			bool sameBackFront,
2685 			bool exploded,
2686 			int pointIndex
2687 			)
2688 		{
2689 			SolidBrush brush = new SolidBrush(point.Color);
2690 
2691 			// For lightStyle style Non, Border color always exist.
2692 			Color penColor = Color.Empty;
2693 			Color penCurveColor = Color.Empty;
2694 
2695 			if( point.BorderColor == Color.Empty && area.Area3DStyle.LightStyle == LightStyle.None )
2696 			{
2697 				penColor = ChartGraphics.GetGradientColor( point.Color, Color.Black, 0.5 );
2698 			}
2699 			else if( point.BorderColor == Color.Empty )
2700 			{
2701 				penColor = point.Color;
2702 			}
2703 			else
2704 			{
2705 				penColor = point.BorderColor;
2706 			}
2707 
2708 			if( point.BorderColor != Color.Empty || area.Area3DStyle.LightStyle == LightStyle.None )
2709 			{
2710 				penCurveColor = penColor;
2711 			}
2712 
2713 			Pen pen = new Pen(penColor, point.BorderWidth);
2714 			pen.DashStyle = graph.GetPenStyle( point.BorderDashStyle );
2715 
2716 			// Pen for back side slice.
2717 			Pen backSlicePen;
2718 			if( point.BorderColor == Color.Empty )
2719 			{
2720 				backSlicePen = new Pen(point.Color);
2721 			}
2722 			else
2723 			{
2724 				backSlicePen = pen;
2725 			}
2726 
2727 			Pen penCurve = new Pen(penCurveColor, point.BorderWidth);
2728 			penCurve.DashStyle = graph.GetPenStyle( point.BorderDashStyle );
2729 
2730 			// Set Border Width;
2731 			PointF [] points = GetPiePoints( graph, area, pieWidth, rectangle, startAngle, sweepAngle, true, doughnutRadius, exploded );
2732 
2733 			if( points == null )
2734 				return;
2735 
2736             // Remember data point anchor location
2737             point.positionRel.X = points[(int)PiePoints.TopLabelLine].X;
2738             point.positionRel.Y = points[(int)PiePoints.TopLabelLine].Y;
2739             point.positionRel = graph.GetRelativePoint(point.positionRel);
2740 
2741 
2742 			float midAngle = startAngle + sweepAngle / 2F;
2743 			float endAngle = startAngle + sweepAngle;
2744 
2745 			if( turn == 0 )
2746 			{
2747 				// Draw back pie slice (do not fill).
2748 				// Used for transparency.
2749 				if( !this.Doughnut )
2750 				{
2751 					graph.FillPieSlice(
2752 						area,
2753 						point,
2754 						brush,
2755 						backSlicePen,
2756 						points[(int)PiePoints.BottomRectTopLeftPoint],
2757 						points[(int)PiePoints.BottomStart],
2758 						points[(int)PiePoints.BottomRectBottomRightPoint],
2759 						points[(int)PiePoints.BottomEnd],
2760 						points[(int)PiePoints.BottomCenter],
2761 						startAngle,
2762 						sweepAngle,
2763 						false,
2764 						pointIndex
2765 						);
2766 				}
2767 				else
2768 				{
2769 					graph.FillDoughnutSlice(
2770 						area,
2771 						point,
2772 						brush,
2773 						backSlicePen,
2774 						points[(int)PiePoints.BottomRectTopLeftPoint],
2775 						points[(int)PiePoints.BottomStart],
2776 						points[(int)PiePoints.BottomRectBottomRightPoint],
2777 						points[(int)PiePoints.BottomEnd],
2778 						points[(int)PiePoints.DoughnutBottomEnd],
2779 						points[(int)PiePoints.DoughnutBottomStart],
2780 						startAngle,
2781 						sweepAngle,
2782 						false,
2783 						doughnutRadius,
2784 						pointIndex
2785 						);
2786 				}
2787 
2788 			}
2789 			else if( turn == 1 )
2790 			{
2791 				// Case when there is big pie slice ( > 180 ) and big slice is
2792 				// back and front point in same time.
2793 				if( sameBackFront )
2794 				{
2795 
2796 
2797 					// Draw the first part of the curve of the big slice and
2798 					// all curves from other slices. Big pie slice could be on the
2799 					// right or the left side.
2800 					if( midAngle > -90 && midAngle < 90 || midAngle > 270 && midAngle < 450 )
2801 					{
2802 						// Draw Inner Arc for Doughnut
2803 						if( Doughnut )
2804 						{
2805 							DrawDoughnutCurves( graph, area, point, startAngle, sweepAngle, points, brush, penCurve, false, true, pointIndex );
2806 						}
2807 
2808 						DrawPieCurves( graph, area, point, startAngle, sweepAngle, points, brush, penCurve, true, true, pointIndex );
2809 					}
2810 					else
2811 					{
2812 						// Draw Inner Arc for Doughnut
2813 						if( Doughnut )
2814 						{
2815 							DrawDoughnutCurves( graph, area, point, startAngle, sweepAngle, points, brush, penCurve, true, true, pointIndex );
2816 						}
2817 
2818 						DrawPieCurves( graph, area, point, startAngle, sweepAngle, points, brush, penCurve, false, true, pointIndex );
2819 					}
2820 
2821 					// Draw sides of pie slices
2822 					graph.FillPieSides( area, area.Area3DStyle.Inclination, startAngle, sweepAngle, points, brush, pen, Doughnut );
2823 
2824 
2825 				}
2826 				else
2827 				{
2828 					// Draw Inner Arc for Doughnut
2829 					if( Doughnut )
2830 					{
2831 						DrawDoughnutCurves( graph, area, point, startAngle, sweepAngle, points, brush, penCurve, false, false, pointIndex );
2832 					}
2833 
2834 					// This is regular case. There is no big pie slice
2835 					// which is back nad front point in same time.
2836 					graph.FillPieSides( area, area.Area3DStyle.Inclination, startAngle, sweepAngle, points, brush, pen, Doughnut );
2837 
2838 					DrawPieCurves( graph, area, point, startAngle, sweepAngle, points, brush, penCurve, false, false, pointIndex );
2839 				}
2840 
2841 			}
2842 			else if( turn == 2 )
2843 			{
2844 				// This second turned is used only for big pie slice (>180). If big pie
2845 				// slice exist it has to be split if it is necessary. If the big pie slice
2846 				// cover other pie slice from both sides, the big pie slice have to curves.
2847 				// The first curve from big pie slice is drawn first, after that all other
2848 				// pie slices and at the end second curve from big pie slice.
2849 				if( sameBackFront && sweepAngle > 180 )
2850 				{
2851 					// Condition when two draw Doughnut arcs after sides for big pie slice ( > 180 ).
2852 					bool BackFrontDoughnut = ( startAngle > -180 && startAngle < 0 || startAngle > 180 && startAngle < 360 ) &&	( endAngle > -180 && endAngle < 0 || endAngle > 180 && endAngle < 360 );
2853 
2854 					if( area.Area3DStyle.Inclination > 0 )
2855 						BackFrontDoughnut = !BackFrontDoughnut;
2856 
2857 					if( midAngle > -90 && midAngle < 90 || midAngle > 270 && midAngle < 450 )
2858 					{
2859 						// Draw Inner Arc for Doughnut
2860 						if( Doughnut )
2861 						{
2862 							// Draw second part of doughnut curve only for very big slices > 300 ( Visibility issue for Big point depth ).
2863 							if( BackFrontDoughnut && sweepAngle > 300 )
2864 							{
2865 								DrawDoughnutCurves( graph, area, point, startAngle, sweepAngle, points, brush, penCurve, true, true, pointIndex );
2866 							}
2867 						}
2868 
2869 						DrawPieCurves( graph, area, point, startAngle, sweepAngle, points, brush, penCurve, false, true, pointIndex );
2870 					}
2871 					else
2872 					{
2873 						// Draw Inner Arc for Doughnut
2874 						if( Doughnut )
2875 						{
2876 							// Draw second part of doughnut curve only for very big slices > 300( Visibility issue for Big point depth ).
2877 							if( BackFrontDoughnut && sweepAngle > 300 )
2878 							{
2879 								DrawDoughnutCurves( graph, area, point, startAngle, sweepAngle, points, brush, penCurve, false, true, pointIndex );
2880 							}
2881 						}
2882 						DrawPieCurves( graph, area, point, startAngle, sweepAngle, points, brush, penCurve, true, true, pointIndex );
2883 					}
2884 				}
2885 			}
2886 			else if( turn == 3 )
2887 			{
2888 				if( !this.Doughnut )
2889 				{
2890 					// Fill pie slice
2891 					graph.FillPieSlice(
2892 						area,
2893 						point,
2894 						brush,
2895 						pen,
2896 						points[(int)PiePoints.TopRectTopLeftPoint],
2897 						points[(int)PiePoints.TopStart],
2898 						points[(int)PiePoints.TopRectBottomRightPoint],
2899 						points[(int)PiePoints.TopEnd],
2900 						points[(int)PiePoints.TopCenter],
2901 						startAngle,
2902 						sweepAngle,
2903 						true,
2904 						pointIndex
2905 						);
2906 
2907 					// Draw Border
2908 					graph.FillPieSlice(
2909 						area,
2910 						point,
2911 						brush,
2912 						pen,
2913 						points[(int)PiePoints.TopRectTopLeftPoint],
2914 						points[(int)PiePoints.TopStart],
2915 						points[(int)PiePoints.TopRectBottomRightPoint],
2916 						points[(int)PiePoints.TopEnd],
2917 						points[(int)PiePoints.TopCenter],
2918 						startAngle,
2919 						sweepAngle,
2920 						false,
2921 						pointIndex
2922 						);
2923 				}
2924 				else
2925 				{
2926 					// Fill
2927 					graph.FillDoughnutSlice(
2928 						area,
2929 						point,
2930 						brush,
2931 						pen,
2932 						points[(int)PiePoints.TopRectTopLeftPoint],
2933 						points[(int)PiePoints.TopStart],
2934 						points[(int)PiePoints.TopRectBottomRightPoint],
2935 						points[(int)PiePoints.TopEnd],
2936 						points[(int)PiePoints.DoughnutTopEnd],
2937 						points[(int)PiePoints.DoughnutTopStart],
2938 						startAngle,
2939 						sweepAngle,
2940 						true,
2941 						doughnutRadius,
2942 						pointIndex
2943 						);
2944 
2945 					// Draw Border
2946 					graph.FillDoughnutSlice(
2947 						area,
2948 						point,
2949 						brush,
2950 						pen,
2951 						points[(int)PiePoints.TopRectTopLeftPoint],
2952 						points[(int)PiePoints.TopStart],
2953 						points[(int)PiePoints.TopRectBottomRightPoint],
2954 						points[(int)PiePoints.TopEnd],
2955 						points[(int)PiePoints.DoughnutTopEnd],
2956 						points[(int)PiePoints.DoughnutTopStart],
2957 						startAngle,
2958 						sweepAngle,
2959 						false,
2960 						doughnutRadius,
2961 						pointIndex
2962 						);
2963 				}
2964 
2965 				// Draw 3D Outside labels
2966 				if( GetLabelStyle( point ) == PieLabelStyle.Outside )
2967 				{
2968 					// Check if special color properties are set
2969 					Color pieLineColor = pen.Color;
2970 					if(point.IsCustomPropertySet(CustomPropertyName.PieLineColor) || (point.series != null && point.series.IsCustomPropertySet(CustomPropertyName.PieLineColor)) )
2971 					{
2972 						ColorConverter colorConverter = new ColorConverter();
2973                         bool failed = false;
2974 
2975 						try
2976 						{
2977 							if(point.IsCustomPropertySet(CustomPropertyName.PieLineColor))
2978 							{
2979 								pieLineColor = (Color)colorConverter.ConvertFromString(point[CustomPropertyName.PieLineColor]);
2980 							}
2981 							else if(point.series != null && point.series.IsCustomPropertySet(CustomPropertyName.PieLineColor))
2982 							{
2983 								pieLineColor = (Color)colorConverter.ConvertFromString(point.series[CustomPropertyName.PieLineColor]);
2984 							}
2985 						}
2986                         catch (ArgumentException)
2987                         {
2988                             failed = true;
2989                         }
2990                         catch (NotSupportedException)
2991                         {
2992                             failed = true;
2993                         }
2994 
2995                         if(failed)
2996 						{
2997 							if(point.IsCustomPropertySet(CustomPropertyName.PieLineColor))
2998 							{
2999 								pieLineColor = (Color)colorConverter.ConvertFromInvariantString(point[CustomPropertyName.PieLineColor]);
3000 							}
3001 							else if(point.series != null && point.series.IsCustomPropertySet(CustomPropertyName.PieLineColor))
3002 							{
3003 								pieLineColor = (Color)colorConverter.ConvertFromInvariantString(point.series[CustomPropertyName.PieLineColor]);
3004 							}
3005 						}
3006 					}
3007 
3008 					// Draw labels
3009                     using (Pen labelPen = new Pen(pieLineColor, pen.Width))
3010                     {
3011                         Draw3DOutsideLabels(graph, area, labelPen, points, point, midAngle, pointIndex);
3012                     }
3013 				}
3014 
3015 			}
3016 			else
3017 			{
3018 
3019 				// Draw 3D Inside labels
3020 				if( GetLabelStyle( point ) == PieLabelStyle.Inside )
3021 				{
3022 					Draw3DInsideLabels( graph, points, point, pointIndex );
3023 				}
3024 			}
3025 
3026             //Clean up resources
3027             if (brush!=null)
3028                 brush.Dispose();
3029             if (pen != null)
3030                 pen.Dispose();
3031             if (penCurve != null)
3032                 penCurve.Dispose();
3033 		}
3034 
3035 		/// <summary>
3036 		/// This method transforms in 3D space important points for
3037 		/// doughnut or pie slice.
3038 		/// </summary>
3039 		/// <param name="graph">Chart Graphics</param>
3040 		/// <param name="area">Chart Area</param>
3041 		/// <param name="pieWidth">The width of a pie.</param>
3042 		/// <param name="rectangle">Rectangle used for drawing pie clice.</param>
3043 		/// <param name="startAngle">Start angle for pie slice.</param>
3044 		/// <param name="sweepAngle">End angle for pie slice.</param>
3045 		/// <param name="relativeCoordinates">true if relative coordinates has to be returned.</param>
3046 		/// <param name="doughnutRadius">Doughnut Radius</param>
3047 		/// <param name="exploded">Exploded pie slice</param>
3048 		/// <returns>Returns 3D Transformed pie or doughnut points.</returns>
GetPiePoints( ChartGraphics graph, ChartArea area, float pieWidth, RectangleF rectangle, float startAngle, float sweepAngle, bool relativeCoordinates, float doughnutRadius, bool exploded )3049 		private PointF [] GetPiePoints(
3050 			ChartGraphics graph,
3051 			ChartArea area,
3052 			float pieWidth,
3053 			RectangleF rectangle,
3054 			float startAngle,
3055 			float sweepAngle,
3056 			bool relativeCoordinates,
3057 			float doughnutRadius,
3058 			bool exploded
3059 			)
3060 		{
3061 			doughnutRadius = 1 - doughnutRadius / 100F;
3062 
3063 			Point3D [] points;
3064 			PointF [] result;
3065 
3066 			// Doughnut chart has 12 more points
3067 			if( Doughnut )
3068 			{
3069 				points = new Point3D[29];
3070 				result = new PointF[29];
3071 			}
3072 			else
3073 			{
3074 				points = new Point3D[17];
3075 				result = new PointF[17];
3076 			}
3077 
3078 			// Angle 180 Top point on the arc
3079 			points[(int)PiePoints.Top180] = new Point3D(
3080 				rectangle.X + (float)Math.Cos( 180 * Math.PI / 180 ) * rectangle.Width / 2F + rectangle.Width / 2F,
3081 				rectangle.Y + (float)Math.Sin( 180 * Math.PI / 180 ) * rectangle.Height / 2F + rectangle.Height / 2F,
3082 				pieWidth );
3083 
3084 			// Angle 180 Bottom point on the arc
3085 			points[(int)PiePoints.Bottom180] = new Point3D(
3086 				rectangle.X + (float)Math.Cos( 180 * Math.PI / 180 ) * rectangle.Width / 2F + rectangle.Width / 2F,
3087 				rectangle.Y + (float)Math.Sin( 180 * Math.PI / 180 ) * rectangle.Height / 2F + rectangle.Height / 2F,
3088 				0 );
3089 
3090 			// Angle 0 Top point on the arc
3091 			points[(int)PiePoints.Top0] = new Point3D(
3092 				rectangle.X + (float)Math.Cos( 0 * Math.PI / 180 ) * rectangle.Width / 2F + rectangle.Width / 2F,
3093 				rectangle.Y + (float)Math.Sin( 0 * Math.PI / 180 ) * rectangle.Height / 2F + rectangle.Height / 2F,
3094 				pieWidth );
3095 
3096 			// Angle 0 Bottom point on the arc
3097 			points[(int)PiePoints.Bottom0] = new Point3D(
3098 				rectangle.X + (float)Math.Cos( 0 * Math.PI / 180 ) * rectangle.Width / 2F + rectangle.Width / 2F,
3099 				rectangle.Y + (float)Math.Sin( 0 * Math.PI / 180 ) * rectangle.Height / 2F + rectangle.Height / 2F,
3100 				0 );
3101 
3102 			// Top Start Angle point on the arc
3103 			points[(int)PiePoints.TopStart] = new Point3D(
3104 			rectangle.X + (float)Math.Cos( startAngle * Math.PI / 180 ) * rectangle.Width / 2F + rectangle.Width / 2F,
3105 			rectangle.Y + (float)Math.Sin( startAngle * Math.PI / 180 ) * rectangle.Height / 2F + rectangle.Height / 2F,
3106 			pieWidth );
3107 
3108 			// Top End Angle point on the arc
3109 			points[(int)PiePoints.TopEnd] = new Point3D(
3110 			rectangle.X + (float)Math.Cos( ( startAngle + sweepAngle ) * Math.PI / 180 ) * rectangle.Width / 2F + rectangle.Width / 2F,
3111 			rectangle.Y + (float)Math.Sin( ( startAngle + sweepAngle ) * Math.PI / 180 ) * rectangle.Height / 2F + rectangle.Height / 2F,
3112 			pieWidth );
3113 
3114 			// Bottom Start Angle point on the arc
3115 			points[(int)PiePoints.BottomStart] = new Point3D(
3116 			rectangle.X + (float)Math.Cos( startAngle * Math.PI / 180 ) * rectangle.Width / 2F + rectangle.Width / 2F,
3117 			rectangle.Y + (float)Math.Sin( startAngle * Math.PI / 180 ) * rectangle.Height / 2F + rectangle.Height / 2F,
3118 			0 );
3119 
3120 			// Bottom End Angle point on the arc
3121 			points[(int)PiePoints.BottomEnd] = new Point3D(
3122 			rectangle.X + (float)Math.Cos( ( startAngle + sweepAngle ) * Math.PI / 180 ) * rectangle.Width / 2F + rectangle.Width / 2F,
3123 			rectangle.Y + (float)Math.Sin( ( startAngle + sweepAngle ) * Math.PI / 180 ) * rectangle.Height / 2F + rectangle.Height / 2F,
3124 			0 );
3125 
3126 			// Center Top
3127 			points[(int)PiePoints.TopCenter] = new Point3D(
3128 			rectangle.X + rectangle.Width / 2F,
3129 			rectangle.Y + rectangle.Height / 2F,
3130 			pieWidth );
3131 
3132 			// Center Bottom
3133 			points[(int)PiePoints.BottomCenter] = new Point3D(
3134 			rectangle.X + rectangle.Width / 2F,
3135 			rectangle.Y + rectangle.Height / 2F,
3136 			0 );
3137 
3138 			// Top Label Line
3139 			points[(int)PiePoints.TopLabelLine] = new Point3D(
3140 				rectangle.X + (float)Math.Cos( ( startAngle + sweepAngle / 2 ) * Math.PI / 180 ) * rectangle.Width / 2F + rectangle.Width / 2F,
3141 				rectangle.Y + (float)Math.Sin( ( startAngle + sweepAngle / 2 ) * Math.PI / 180 ) * rectangle.Height / 2F + rectangle.Height / 2F,
3142 				pieWidth );
3143 
3144 			// If Pie slice is exploded Label line out size is changed
3145 			float sizeOut;
3146 			if( exploded )
3147 			{
3148 				sizeOut = 1.1F;
3149 			}
3150 			else
3151 			{
3152 				sizeOut = 1.3F;
3153 			}
3154 
3155 			// Top Label Line Out
3156 			points[(int)PiePoints.TopLabelLineout] = new Point3D(
3157 				rectangle.X + (float)Math.Cos( ( startAngle + sweepAngle / 2 ) * Math.PI / 180 ) * rectangle.Width * sizeOut / 2F + rectangle.Width / 2F,
3158 				rectangle.Y + (float)Math.Sin( ( startAngle + sweepAngle / 2 ) * Math.PI / 180 ) * rectangle.Height * sizeOut / 2F + rectangle.Height / 2F,
3159 				pieWidth );
3160 
3161 			// Top Label Center
3162 			if( this.Doughnut )
3163 			{
3164 				points[(int)PiePoints.TopLabelCenter] = new Point3D(
3165 					rectangle.X + (float)Math.Cos( ( startAngle + sweepAngle / 2 ) * Math.PI / 180 ) * rectangle.Width * ( 1 + doughnutRadius ) / 4F + rectangle.Width / 2F,
3166 					rectangle.Y + (float)Math.Sin( ( startAngle + sweepAngle / 2 ) * Math.PI / 180 ) * rectangle.Height * ( 1 + doughnutRadius ) / 4F + rectangle.Height / 2F,
3167 					pieWidth );
3168 			}
3169 			else
3170 			{
3171 				points[(int)PiePoints.TopLabelCenter] = new Point3D(
3172 					rectangle.X + (float)Math.Cos( ( startAngle + sweepAngle / 2 ) * Math.PI / 180 ) * rectangle.Width * 0.5F / 2F + rectangle.Width / 2F,
3173 					rectangle.Y + (float)Math.Sin( ( startAngle + sweepAngle / 2 ) * Math.PI / 180 ) * rectangle.Height * 0.5F / 2F + rectangle.Height / 2F,
3174 					pieWidth );
3175 			}
3176 
3177 
3178 			// Top Rectangle Top Left Point
3179 			points[(int)PiePoints.TopRectTopLeftPoint] = new Point3D(rectangle.X,rectangle.Y,pieWidth);
3180 
3181 			// Top Rectangle Right Bottom Point
3182 			points[(int)PiePoints.TopRectBottomRightPoint] = new Point3D(rectangle.Right,rectangle.Bottom,pieWidth);
3183 
3184 			// Bottom Rectangle Top Left Point
3185 			points[(int)PiePoints.BottomRectTopLeftPoint] = new Point3D(rectangle.X,rectangle.Y,0);
3186 
3187 			// Bottom Rectangle Right Bottom Point
3188 			points[(int)PiePoints.BottomRectBottomRightPoint] = new Point3D(rectangle.Right,rectangle.Bottom,0);
3189 
3190 			if( Doughnut )
3191 			{
3192 				// Angle 180 Top point on the Doughnut arc
3193 				points[(int)PiePoints.DoughnutTop180] = new Point3D(
3194 					rectangle.X + (float)Math.Cos( 180 * Math.PI / 180 ) * rectangle.Width * doughnutRadius / 2F + rectangle.Width / 2F,
3195 					rectangle.Y + (float)Math.Sin( 180 * Math.PI / 180 ) * rectangle.Height * doughnutRadius / 2F + rectangle.Height / 2F,
3196 					pieWidth );
3197 
3198 				// Angle 180 Bottom point on the Doughnut arc
3199 				points[(int)PiePoints.DoughnutBottom180] = new Point3D(
3200 					rectangle.X + (float)Math.Cos( 180 * Math.PI / 180 ) * rectangle.Width * doughnutRadius / 2F + rectangle.Width / 2F,
3201 					rectangle.Y + (float)Math.Sin( 180 * Math.PI / 180 ) * rectangle.Height * doughnutRadius / 2F + rectangle.Height / 2F,
3202 					0 );
3203 
3204 				// Angle 0 Top point on the Doughnut arc
3205 				points[(int)PiePoints.DoughnutTop0] = new Point3D(
3206 					rectangle.X + (float)Math.Cos( 0 * Math.PI / 180 ) * rectangle.Width * doughnutRadius / 2F + rectangle.Width / 2F,
3207 					rectangle.Y + (float)Math.Sin( 0 * Math.PI / 180 ) * rectangle.Height * doughnutRadius / 2F + rectangle.Height / 2F,
3208 					pieWidth );
3209 
3210 				// Angle 0 Bottom point on the Doughnut arc
3211 				points[(int)PiePoints.DoughnutBottom0] = new Point3D(
3212 					rectangle.X + (float)Math.Cos( 0 * Math.PI / 180 ) * rectangle.Width * doughnutRadius / 2F + rectangle.Width / 2F,
3213 					rectangle.Y + (float)Math.Sin( 0 * Math.PI / 180 ) * rectangle.Height * doughnutRadius / 2F + rectangle.Height / 2F,
3214 					0 );
3215 
3216 				// Top Start Angle point on the Doughnut arc
3217 				points[(int)PiePoints.DoughnutTopStart] = new Point3D(
3218 					rectangle.X + (float)Math.Cos( startAngle * Math.PI / 180 ) * rectangle.Width * doughnutRadius / 2F + rectangle.Width / 2F,
3219 					rectangle.Y + (float)Math.Sin( startAngle * Math.PI / 180 ) * rectangle.Height * doughnutRadius / 2F + rectangle.Height / 2F,
3220 					pieWidth );
3221 
3222 				// Top End Angle point on the Doughnut arc
3223 				points[(int)PiePoints.DoughnutTopEnd] = new Point3D(
3224 					rectangle.X + (float)Math.Cos( ( startAngle + sweepAngle ) * Math.PI / 180 ) * rectangle.Width * doughnutRadius / 2F + rectangle.Width / 2F,
3225 					rectangle.Y + (float)Math.Sin( ( startAngle + sweepAngle ) * Math.PI / 180 ) * rectangle.Height * doughnutRadius / 2F + rectangle.Height / 2F,
3226 					pieWidth );
3227 
3228 				// Bottom Start Angle point on the Doughnut arc
3229 				points[(int)PiePoints.DoughnutBottomStart] = new Point3D(
3230 					rectangle.X + (float)Math.Cos( startAngle * Math.PI / 180 ) * rectangle.Width * doughnutRadius / 2F + rectangle.Width / 2F,
3231 					rectangle.Y + (float)Math.Sin( startAngle * Math.PI / 180 ) * rectangle.Height * doughnutRadius / 2F + rectangle.Height / 2F,
3232 					0 );
3233 
3234 				// Bottom End Angle point on the Doughnut arc
3235 				points[(int)PiePoints.DoughnutBottomEnd] = new Point3D(
3236 					rectangle.X + (float)Math.Cos( ( startAngle + sweepAngle ) * Math.PI / 180 ) * rectangle.Width * doughnutRadius / 2F + rectangle.Width / 2F,
3237 					rectangle.Y + (float)Math.Sin( ( startAngle + sweepAngle ) * Math.PI / 180 ) * rectangle.Height * doughnutRadius / 2F + rectangle.Height / 2F,
3238 					0 );
3239 
3240 				rectangle.Inflate( -rectangle.Width * (1 - doughnutRadius) / 2F, -rectangle.Height * (1 - doughnutRadius) / 2F);
3241 
3242 				// Doughnut Top Rectangle Top Left Point
3243 				points[(int)PiePoints.DoughnutTopRectTopLeftPoint] = new Point3D(rectangle.X,rectangle.Y,pieWidth);
3244 
3245 				// Doughnut Top Rectangle Right Bottom Point
3246 				points[(int)PiePoints.DoughnutTopRectBottomRightPoint] = new Point3D(rectangle.Right,rectangle.Bottom,pieWidth);
3247 
3248 				// Doughnut Bottom Rectangle Top Left Point
3249 				points[(int)PiePoints.DoughnutBottomRectTopLeftPoint] = new Point3D(rectangle.X,rectangle.Y,0);
3250 
3251 				// Doughnut Bottom Rectangle Right Bottom Point
3252 				points[(int)PiePoints.DoughnutBottomRectBottomRightPoint] = new Point3D(rectangle.Right,rectangle.Bottom,0);
3253 
3254 
3255 			}
3256 
3257 			// Make 3D transformations
3258 			area.matrix3D.TransformPoints(points);
3259 
3260 			int pointIndx = 0;
3261 			foreach( Point3D point in points )
3262 			{
3263 				result[pointIndx] = point.PointF;
3264 
3265 				// Convert Relative coordinates to absolute.
3266 				if( relativeCoordinates )
3267 				{
3268 					result[pointIndx] = graph.GetAbsolutePoint(result[pointIndx]);
3269 				}
3270 				pointIndx++;
3271 			}
3272 
3273 			return result;
3274 
3275 		}
3276 
3277 
3278 
3279 
3280 		#endregion
3281 
3282 		#region 3D Drawing surfaces
3283 
3284 		/// <summary>
3285 		/// This method is used for drawing curve around pie slices. This is
3286 		/// the most complex part of 3D Pie slice. There is special case if
3287 		/// pie slice is bigger then 180 degree.
3288 		/// </summary>
3289 		/// <param name="graph">Chart Grahics.</param>
3290 		/// <param name="area">Chart Area.</param>
3291 		/// <param name="dataPoint">Data Point used for pie slice.</param>
3292 		/// <param name="startAngle">Start angle of a pie slice.</param>
3293 		/// <param name="sweepAngle">Sweep angle of a pie slice.</param>
3294 		/// <param name="points">Important 3d points of a pie slice.</param>
3295 		/// <param name="brushWithoutLight">Brush without lithing efects.</param>
3296 		/// <param name="pen">Pen used for border.</param>
3297 		/// <param name="rightPosition">Position of the curve of big pie slice. Big pie slice coud have to visible curves - left and right</param>
3298 		/// <param name="sameBackFront">This is big pie slice which is in same time back and front slice.</param>
3299 		/// <param name="pointIndex">Data Point Index</param>
DrawPieCurves( ChartGraphics graph, ChartArea area, DataPoint dataPoint, float startAngle, float sweepAngle, PointF [] points, SolidBrush brushWithoutLight, Pen pen, bool rightPosition, bool sameBackFront, int pointIndex )3300 		private void DrawPieCurves(
3301 			ChartGraphics graph,
3302 			ChartArea area,
3303 			DataPoint dataPoint,
3304 			float startAngle,
3305 			float sweepAngle,
3306 			PointF [] points,
3307 			SolidBrush brushWithoutLight,
3308 			Pen pen,
3309 			bool rightPosition,
3310 			bool sameBackFront,
3311 			int pointIndex
3312 			)
3313 		{
3314 			// Create a graphics path
3315             using (GraphicsPath path = new GraphicsPath())
3316             {
3317                 Brush brush;
3318 
3319                 if (area.Area3DStyle.LightStyle == LightStyle.None)
3320                 {
3321                     brush = brushWithoutLight;
3322                 }
3323                 else
3324                 {
3325                     brush = graph.GetGradientBrush(graph.GetAbsoluteRectangle(area.Position.ToRectangleF()), Color.FromArgb(brushWithoutLight.Color.A, 0, 0, 0), brushWithoutLight.Color, GradientStyle.VerticalCenter);
3326                 }
3327 
3328                 float endAngle = startAngle + sweepAngle;
3329 
3330                 // Very big pie slice ( > 180 degree )
3331                 if (sweepAngle > 180)
3332                 {
3333                     if (DrawPieCurvesBigSlice(graph, area, dataPoint, startAngle, sweepAngle, points, brush, pen, rightPosition, sameBackFront, pointIndex))
3334                         return;
3335                 }
3336 
3337                 // Pie slice pass throw 180 degree. Curve has to be spited.
3338                 if (startAngle < 180 && endAngle > 180)
3339                 {
3340                     if (area.Area3DStyle.Inclination < 0)
3341                     {
3342                         graph.FillPieCurve(
3343                             area,
3344                             dataPoint,
3345                             brush,
3346                             pen,
3347                             points[(int)PiePoints.TopRectTopLeftPoint],
3348                             points[(int)PiePoints.TopRectBottomRightPoint],
3349                             points[(int)PiePoints.BottomRectTopLeftPoint],
3350                             points[(int)PiePoints.BottomRectBottomRightPoint],
3351                             points[(int)PiePoints.TopStart],
3352                             points[(int)PiePoints.Top180],
3353                             points[(int)PiePoints.BottomStart],
3354                             points[(int)PiePoints.Bottom180],
3355                             startAngle,
3356                             180 - startAngle,
3357                             pointIndex
3358                             );
3359 
3360                     }
3361                     else
3362                     {
3363                         graph.FillPieCurve(
3364                             area,
3365                             dataPoint,
3366                             brush,
3367                             pen,
3368                             points[(int)PiePoints.TopRectTopLeftPoint],
3369                             points[(int)PiePoints.TopRectBottomRightPoint],
3370                             points[(int)PiePoints.BottomRectTopLeftPoint],
3371                             points[(int)PiePoints.BottomRectBottomRightPoint],
3372                             points[(int)PiePoints.Top180],
3373                             points[(int)PiePoints.TopEnd],
3374                             points[(int)PiePoints.Bottom180],
3375                             points[(int)PiePoints.BottomEnd],
3376                             180,
3377                             startAngle + sweepAngle - 180,
3378                             pointIndex
3379                             );
3380 
3381                     }
3382                 }
3383 
3384                 // Pie slice pass throw 0 degree. Curve has to be spited.
3385                 else if (startAngle < 0 && endAngle > 0)
3386                 {
3387                     if (area.Area3DStyle.Inclination > 0)
3388                     {
3389                         graph.FillPieCurve(
3390                             area,
3391                             dataPoint,
3392                             brush,
3393                             pen,
3394                             points[(int)PiePoints.TopRectTopLeftPoint],
3395                             points[(int)PiePoints.TopRectBottomRightPoint],
3396                             points[(int)PiePoints.BottomRectTopLeftPoint],
3397                             points[(int)PiePoints.BottomRectBottomRightPoint],
3398                             points[(int)PiePoints.TopStart],
3399                             points[(int)PiePoints.Top0],
3400                             points[(int)PiePoints.BottomStart],
3401                             points[(int)PiePoints.Bottom0],
3402                             startAngle,
3403                             -startAngle,
3404                             pointIndex
3405                             );
3406 
3407                     }
3408                     else
3409                     {
3410                         graph.FillPieCurve(
3411                             area,
3412                             dataPoint,
3413                             brush,
3414                             pen,
3415                             points[(int)PiePoints.TopRectTopLeftPoint],
3416                             points[(int)PiePoints.TopRectBottomRightPoint],
3417                             points[(int)PiePoints.BottomRectTopLeftPoint],
3418                             points[(int)PiePoints.BottomRectBottomRightPoint],
3419                             points[(int)PiePoints.Top0],
3420                             points[(int)PiePoints.TopEnd],
3421                             points[(int)PiePoints.Bottom0],
3422                             points[(int)PiePoints.BottomEnd],
3423                             0,
3424                             sweepAngle + startAngle,
3425                             pointIndex
3426                             );
3427 
3428                     }
3429                 }
3430                 // Pie slice pass throw 360 degree. Curve has to be spited.
3431                 else if (startAngle < 360 && endAngle > 360)
3432                 {
3433                     if (area.Area3DStyle.Inclination > 0)
3434                     {
3435                         graph.FillPieCurve(
3436                             area,
3437                             dataPoint,
3438                             brush,
3439                             pen,
3440                             points[(int)PiePoints.TopRectTopLeftPoint],
3441                             points[(int)PiePoints.TopRectBottomRightPoint],
3442                             points[(int)PiePoints.BottomRectTopLeftPoint],
3443                             points[(int)PiePoints.BottomRectBottomRightPoint],
3444                             points[(int)PiePoints.TopStart],
3445                             points[(int)PiePoints.Top0],
3446                             points[(int)PiePoints.BottomStart],
3447                             points[(int)PiePoints.Bottom0],
3448                             startAngle,
3449                             360 - startAngle,
3450                             pointIndex
3451                             );
3452 
3453                     }
3454                     else
3455                     {
3456                         graph.FillPieCurve(
3457                             area,
3458                             dataPoint,
3459                             brush,
3460                             pen,
3461                             points[(int)PiePoints.TopRectTopLeftPoint],
3462                             points[(int)PiePoints.TopRectBottomRightPoint],
3463                             points[(int)PiePoints.BottomRectTopLeftPoint],
3464                             points[(int)PiePoints.BottomRectBottomRightPoint],
3465                             points[(int)PiePoints.Top0],
3466                             points[(int)PiePoints.TopEnd],
3467                             points[(int)PiePoints.Bottom0],
3468                             points[(int)PiePoints.BottomEnd],
3469                             0,
3470                             endAngle - 360,
3471                             pointIndex
3472                             );
3473                     }
3474                 }
3475                 else
3476                 {
3477                     // ***************************************************
3478                     // REGULAR CASE: The curve is not split.
3479                     // ***************************************************
3480                     if (startAngle < 180 && startAngle >= 0 && area.Area3DStyle.Inclination < 0
3481                         || startAngle < 540 && startAngle >= 360 && area.Area3DStyle.Inclination < 0
3482                         || startAngle >= 180 && startAngle < 360 && area.Area3DStyle.Inclination > 0
3483                         || startAngle >= -180 && startAngle < 0 && area.Area3DStyle.Inclination > 0
3484                         )
3485                     {
3486                         graph.FillPieCurve(
3487                             area,
3488                             dataPoint,
3489                             brush,
3490                             pen,
3491                             points[(int)PiePoints.TopRectTopLeftPoint],
3492                             points[(int)PiePoints.TopRectBottomRightPoint],
3493                             points[(int)PiePoints.BottomRectTopLeftPoint],
3494                             points[(int)PiePoints.BottomRectBottomRightPoint],
3495                             points[(int)PiePoints.TopStart],
3496                             points[(int)PiePoints.TopEnd],
3497                             points[(int)PiePoints.BottomStart],
3498                             points[(int)PiePoints.BottomEnd],
3499                             startAngle,
3500                             sweepAngle,
3501                             pointIndex
3502                             );
3503                     }
3504                 }
3505             }
3506 		}
3507 
3508 		/// <summary>
3509 		/// This method is used for special case when big pie slice has to be drawn.
3510 		/// </summary>
3511 		/// <param name="graph">Chart Grahics.</param>
3512 		/// <param name="area">Chart Area.</param>
3513 		/// <param name="dataPoint">Data Point used for pie slice.</param>
3514 		/// <param name="startAngle">Start angle of a pie slice.</param>
3515 		/// <param name="sweepAngle">Sweep angle of a pie slice.</param>
3516 		/// <param name="points">Important 3d points of a pie slice.</param>
3517 		/// <param name="brush">Brush without lithing efects.</param>
3518 		/// <param name="pen">Pen used for border.</param>
3519 		/// <param name="rightPosition">Position of the curve of big pie slice. Big pie slice coud have to visible curves - left and right</param>
3520 		/// <param name="sameBackFront">This is big pie slice which is in same time back and front slice.</param>
3521 		/// <param name="pointIndex">Data Point Index</param>
3522 		/// <returns>True if slice is special case and it is drawn as a special case.</returns>
DrawPieCurvesBigSlice( ChartGraphics graph, ChartArea area, DataPoint dataPoint, float startAngle, float sweepAngle, PointF [] points, Brush brush, Pen pen, bool rightPosition, bool sameBackFront, int pointIndex )3523 		private bool DrawPieCurvesBigSlice
3524 		(
3525 			ChartGraphics graph,
3526 			ChartArea area,
3527 			DataPoint dataPoint,
3528 			float startAngle,
3529 			float sweepAngle,
3530 			PointF [] points,
3531 			Brush brush,
3532 			Pen pen,
3533 			bool rightPosition,
3534 			bool sameBackFront,
3535 			int pointIndex
3536 		)
3537 		{
3538 			float endAngle = startAngle + sweepAngle;
3539 
3540 			// Two different cases connected with X angle.
3541 			// *****************************************************
3542 			// X angle is positive
3543 			// *****************************************************
3544 			if( area.Area3DStyle.Inclination > 0 )
3545 			{
3546 				// Show curve from 0 to 180.
3547 				if( startAngle < 180 && endAngle > 360 )
3548 				{
3549 					graph.FillPieCurve(
3550 						area,
3551 						dataPoint,
3552 						brush,
3553 						pen,
3554 						points[(int)PiePoints.TopRectTopLeftPoint],
3555 						points[(int)PiePoints.TopRectBottomRightPoint],
3556 						points[(int)PiePoints.BottomRectTopLeftPoint],
3557 						points[(int)PiePoints.BottomRectBottomRightPoint],
3558 						points[(int)PiePoints.Top0],
3559 						points[(int)PiePoints.Top180],
3560 						points[(int)PiePoints.Bottom0],
3561 						points[(int)PiePoints.Bottom180],
3562 						0,
3563 						-180,
3564 						pointIndex
3565 						);
3566 				}
3567 				else if( startAngle < 0 && endAngle > 180 )
3568 				{
3569 					// There is big data point which is back and
3570 					// front point in same time.
3571 					if( sameBackFront )
3572 					{
3573 						// The big pie slice has to be split. This part makes
3574 						// decision which part of this big slice will be
3575 						// drawn first.
3576 						if( rightPosition )
3577 						{
3578 							graph.FillPieCurve(
3579 								area,
3580 								dataPoint,
3581 								brush,
3582 								pen,
3583 								points[(int)PiePoints.TopRectTopLeftPoint],
3584 								points[(int)PiePoints.TopRectBottomRightPoint],
3585 								points[(int)PiePoints.BottomRectTopLeftPoint],
3586 								points[(int)PiePoints.BottomRectBottomRightPoint],
3587 								points[(int)PiePoints.Top180],
3588 								points[(int)PiePoints.TopEnd],
3589 								points[(int)PiePoints.Bottom180],
3590 								points[(int)PiePoints.BottomEnd],
3591 								180,
3592 								endAngle - 180,
3593 								pointIndex
3594 								);
3595 						}
3596 						else
3597 						{
3598 							graph.FillPieCurve(
3599 								area,
3600 								dataPoint,
3601 								brush,
3602 								pen,
3603 								points[(int)PiePoints.TopRectTopLeftPoint],
3604 								points[(int)PiePoints.TopRectBottomRightPoint],
3605 								points[(int)PiePoints.BottomRectTopLeftPoint],
3606 								points[(int)PiePoints.BottomRectBottomRightPoint],
3607 								points[(int)PiePoints.TopStart],
3608 								points[(int)PiePoints.Top0],
3609 								points[(int)PiePoints.BottomStart],
3610 								points[(int)PiePoints.Bottom0],
3611 								startAngle,
3612 								-startAngle,
3613 								pointIndex
3614 								);
3615 						}
3616 					}
3617 					else
3618 					{
3619 						// There is big pie slice (>180), but that pie slice
3620 						// is not back and front point in same time.
3621 						graph.FillPieCurve(
3622 							area,
3623 							dataPoint,
3624 							brush,
3625 							pen,
3626 							points[(int)PiePoints.TopRectTopLeftPoint],
3627 							points[(int)PiePoints.TopRectBottomRightPoint],
3628 							points[(int)PiePoints.BottomRectTopLeftPoint],
3629 							points[(int)PiePoints.BottomRectBottomRightPoint],
3630 							points[(int)PiePoints.TopStart],
3631 							points[(int)PiePoints.Top0],
3632 							points[(int)PiePoints.BottomStart],
3633 							points[(int)PiePoints.Bottom0],
3634 							startAngle,
3635 							-startAngle,
3636 							pointIndex
3637 							);
3638 
3639 						graph.FillPieCurve(
3640 							area,
3641 							dataPoint,
3642 							brush,
3643 							pen,
3644 							points[(int)PiePoints.TopRectTopLeftPoint],
3645 							points[(int)PiePoints.TopRectBottomRightPoint],
3646 							points[(int)PiePoints.BottomRectTopLeftPoint],
3647 							points[(int)PiePoints.BottomRectBottomRightPoint],
3648 							points[(int)PiePoints.Top180],
3649 							points[(int)PiePoints.TopEnd],
3650 							points[(int)PiePoints.Bottom180],
3651 							points[(int)PiePoints.BottomEnd],
3652 							180,
3653 							endAngle - 180,
3654 							pointIndex
3655 							);
3656 					}
3657 
3658 				}
3659 				else
3660 				{
3661 					// Big pie slice behaves as normal pie slice. Continue
3662 					// Non special case alghoritham
3663 					return false;
3664 				}
3665 			}
3666 			// *********************************************
3667 			// X angle negative
3668 			// *********************************************
3669 			else
3670 			{
3671 				// Show curve from 0 to 180.
3672 				if( startAngle < 0 && endAngle > 180 )
3673 				{
3674 					graph.FillPieCurve(
3675 						area,
3676 						dataPoint,
3677 						brush,
3678 						pen,
3679 						points[(int)PiePoints.TopRectTopLeftPoint],
3680 						points[(int)PiePoints.TopRectBottomRightPoint],
3681 						points[(int)PiePoints.BottomRectTopLeftPoint],
3682 						points[(int)PiePoints.BottomRectBottomRightPoint],
3683 						points[(int)PiePoints.Top0],
3684 						points[(int)PiePoints.Top180],
3685 						points[(int)PiePoints.Bottom0],
3686 						points[(int)PiePoints.Bottom180],
3687 						0,
3688 						180,
3689 						pointIndex
3690 						);
3691 				}
3692 				else if( startAngle < 180 && endAngle > 360 )
3693 				{
3694 					// There is big data point which is back and
3695 					// front point in same time.
3696 					if( sameBackFront )
3697 					{
3698 						// The big pie slice has to be split. This part makes
3699 						// decision which part of this big slice will be
3700 						// drawn first.
3701 						if( rightPosition )
3702 						{
3703 							graph.FillPieCurve(
3704 								area,
3705 								dataPoint,
3706 								brush,
3707 								pen,
3708 								points[(int)PiePoints.TopRectTopLeftPoint],
3709 								points[(int)PiePoints.TopRectBottomRightPoint],
3710 								points[(int)PiePoints.BottomRectTopLeftPoint],
3711 								points[(int)PiePoints.BottomRectBottomRightPoint],
3712 								points[(int)PiePoints.TopStart],
3713 								points[(int)PiePoints.Top180],
3714 								points[(int)PiePoints.BottomStart],
3715 								points[(int)PiePoints.Bottom180],
3716 								startAngle,
3717 								180 - startAngle,
3718 								pointIndex
3719 								);
3720 						}
3721 						else
3722 						{
3723 							graph.FillPieCurve(
3724 								area,
3725 								dataPoint,
3726 								brush,
3727 								pen,
3728 								points[(int)PiePoints.TopRectTopLeftPoint],
3729 								points[(int)PiePoints.TopRectBottomRightPoint],
3730 								points[(int)PiePoints.BottomRectTopLeftPoint],
3731 								points[(int)PiePoints.BottomRectBottomRightPoint],
3732 								points[(int)PiePoints.Top0],
3733 								points[(int)PiePoints.TopEnd],
3734 								points[(int)PiePoints.Bottom0],
3735 								points[(int)PiePoints.BottomEnd],
3736 								0,
3737 								endAngle - 360,
3738 								pointIndex
3739 								);
3740 						}
3741 					}
3742 					else
3743 					{
3744 						// There is big pie slice (>180), but that pie slice
3745 						// is not back and front point in same time.
3746 						graph.FillPieCurve(
3747 							area,
3748 							dataPoint,
3749 							brush,
3750 							pen,
3751 							points[(int)PiePoints.TopRectTopLeftPoint],
3752 							points[(int)PiePoints.TopRectBottomRightPoint],
3753 							points[(int)PiePoints.BottomRectTopLeftPoint],
3754 							points[(int)PiePoints.BottomRectBottomRightPoint],
3755 							points[(int)PiePoints.Top0],
3756 							points[(int)PiePoints.TopEnd],
3757 							points[(int)PiePoints.Bottom0],
3758 							points[(int)PiePoints.BottomEnd],
3759 							0,
3760 							endAngle - 360,
3761 							pointIndex
3762 							);
3763 
3764 						graph.FillPieCurve(
3765 							area,
3766 							dataPoint,
3767 							brush,
3768 							pen,
3769 							points[(int)PiePoints.TopRectTopLeftPoint],
3770 							points[(int)PiePoints.TopRectBottomRightPoint],
3771 							points[(int)PiePoints.BottomRectTopLeftPoint],
3772 							points[(int)PiePoints.BottomRectBottomRightPoint],
3773 							points[(int)PiePoints.TopStart],
3774 							points[(int)PiePoints.Top180],
3775 							points[(int)PiePoints.BottomStart],
3776 							points[(int)PiePoints.Bottom180],
3777 							startAngle,
3778 							180 - startAngle,
3779 							pointIndex
3780 							);
3781 					}
3782 
3783 				}
3784 				else
3785 				{
3786 					// Big pie slice behaves as normal pie slice. Continue
3787 					// Non special case alghoritham
3788 					return false;
3789 				}
3790 			}
3791 
3792 			return true;
3793 		}
3794 
3795 		/// <summary>
3796 		/// This method is used for drawing curve around doughnut slices - inner curve.
3797 		/// This is the most complex part of 3D Doughnut slice. There is special case if
3798 		/// pie slice is bigger then 180 degree.
3799 		/// </summary>
3800 		/// <param name="graph">Chart Grahics.</param>
3801 		/// <param name="area">Chart Area.</param>
3802 		/// <param name="dataPoint">Data Point used for pie slice.</param>
3803 		/// <param name="startAngle">Start angle of a pie slice.</param>
3804 		/// <param name="sweepAngle">Sweep angle of a pie slice.</param>
3805 		/// <param name="points">Important 3d points of a pie slice.</param>
3806 		/// <param name="brushWithoutLight">Brush without lithing efects.</param>
3807 		/// <param name="pen">Pen used for border.</param>
3808 		/// <param name="rightPosition">Position of the curve of big pie slice. Big pie slice coud have to visible curves - left and right</param>
3809 		/// <param name="sameBackFront">This is big pie slice which is in same time back and front slice.</param>
3810 		/// <param name="pointIndex">Data Point Index</param>
DrawDoughnutCurves( ChartGraphics graph, ChartArea area, DataPoint dataPoint, float startAngle, float sweepAngle, PointF [] points, SolidBrush brushWithoutLight, Pen pen, bool rightPosition, bool sameBackFront, int pointIndex )3811 		private void DrawDoughnutCurves(
3812 			ChartGraphics graph,
3813 			ChartArea area,
3814 			DataPoint dataPoint,
3815 			float startAngle,
3816 			float sweepAngle,
3817 			PointF [] points,
3818 			SolidBrush brushWithoutLight,
3819 			Pen pen,
3820 			bool rightPosition,
3821 			bool sameBackFront,
3822 			int pointIndex
3823 			)
3824 		{
3825 			// Create a graphics path
3826             using (GraphicsPath path = new GraphicsPath())
3827             {
3828 
3829                 Brush brush;
3830 
3831                 if (area.Area3DStyle.LightStyle == LightStyle.None)
3832                 {
3833                     brush = brushWithoutLight;
3834                 }
3835                 else
3836                 {
3837                     brush = graph.GetGradientBrush(graph.GetAbsoluteRectangle(area.Position.ToRectangleF()), Color.FromArgb(brushWithoutLight.Color.A, 0, 0, 0), brushWithoutLight.Color, GradientStyle.VerticalCenter);
3838                 }
3839 
3840                 float endAngle = startAngle + sweepAngle;
3841 
3842                 // Very big pie slice ( > 180 degree )
3843                 if (sweepAngle > 180)
3844                 {
3845                     if (DrawDoughnutCurvesBigSlice(graph, area, dataPoint, startAngle, sweepAngle, points, brush, pen, rightPosition, sameBackFront, pointIndex))
3846                         return;
3847                 }
3848 
3849                 // Pie slice pass throw 180 degree. Curve has to be spited.
3850                 if (startAngle < 180 && endAngle > 180)
3851                 {
3852                     if (area.Area3DStyle.Inclination > 0)
3853                     {
3854                         graph.FillPieCurve(
3855                             area,
3856                             dataPoint,
3857                             brush,
3858                             pen,
3859                             points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
3860                             points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
3861                             points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
3862                             points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
3863                             points[(int)PiePoints.DoughnutTopStart],
3864                             points[(int)PiePoints.DoughnutTop180],
3865                             points[(int)PiePoints.DoughnutBottomStart],
3866                             points[(int)PiePoints.DoughnutBottom180],
3867                             startAngle,
3868                             180 - startAngle,
3869                             pointIndex
3870                             );
3871 
3872                     }
3873                     else
3874                     {
3875                         graph.FillPieCurve(
3876                             area,
3877                             dataPoint,
3878                             brush,
3879                             pen,
3880                             points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
3881                             points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
3882                             points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
3883                             points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
3884                             points[(int)PiePoints.DoughnutTop180],
3885                             points[(int)PiePoints.DoughnutTopEnd],
3886                             points[(int)PiePoints.DoughnutBottom180],
3887                             points[(int)PiePoints.DoughnutBottomEnd],
3888                             180,
3889                             startAngle + sweepAngle - 180,
3890                             pointIndex
3891                             );
3892 
3893                     }
3894                 }
3895 
3896                     // Pie slice pass throw 0 degree. Curve has to be spited.
3897                 else if (startAngle < 0 && endAngle > 0)
3898                 {
3899                     if (area.Area3DStyle.Inclination < 0)
3900                     {
3901                         graph.FillPieCurve(
3902                             area,
3903                             dataPoint,
3904                             brush,
3905                             pen,
3906                             points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
3907                             points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
3908                             points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
3909                             points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
3910                             points[(int)PiePoints.DoughnutTopStart],
3911                             points[(int)PiePoints.DoughnutTop0],
3912                             points[(int)PiePoints.DoughnutBottomStart],
3913                             points[(int)PiePoints.DoughnutBottom0],
3914                             startAngle,
3915                             -startAngle,
3916                             pointIndex
3917                             );
3918 
3919                     }
3920                     else
3921                     {
3922                         graph.FillPieCurve(
3923                             area,
3924                             dataPoint,
3925                             brush,
3926                             pen,
3927                             points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
3928                             points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
3929                             points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
3930                             points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
3931                             points[(int)PiePoints.DoughnutTop0],
3932                             points[(int)PiePoints.DoughnutTopEnd],
3933                             points[(int)PiePoints.DoughnutBottom0],
3934                             points[(int)PiePoints.DoughnutBottomEnd],
3935                             0,
3936                             sweepAngle + startAngle,
3937                             pointIndex
3938                             );
3939 
3940                     }
3941                 }
3942                 // Pie slice pass throw 360 degree. Curve has to be spited.
3943                 else if (startAngle < 360 && endAngle > 360)
3944                 {
3945                     if (area.Area3DStyle.Inclination < 0)
3946                     {
3947                         graph.FillPieCurve(
3948                             area,
3949                             dataPoint,
3950                             brush,
3951                             pen,
3952                             points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
3953                             points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
3954                             points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
3955                             points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
3956                             points[(int)PiePoints.DoughnutTopStart],
3957                             points[(int)PiePoints.DoughnutTop0],
3958                             points[(int)PiePoints.DoughnutBottomStart],
3959                             points[(int)PiePoints.DoughnutBottom0],
3960                             startAngle,
3961                             360 - startAngle,
3962                             pointIndex
3963                             );
3964 
3965                     }
3966                     else
3967                     {
3968                         graph.FillPieCurve(
3969                             area,
3970                             dataPoint,
3971                             brush,
3972                             pen,
3973                             points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
3974                             points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
3975                             points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
3976                             points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
3977                             points[(int)PiePoints.DoughnutTop0],
3978                             points[(int)PiePoints.DoughnutTopEnd],
3979                             points[(int)PiePoints.DoughnutBottom0],
3980                             points[(int)PiePoints.DoughnutBottomEnd],
3981                             0,
3982                             endAngle - 360,
3983                             pointIndex
3984                             );
3985                     }
3986                 }
3987                 else
3988                 {
3989                     // ***************************************************
3990                     // REGULAR CASE: The curve is not split.
3991                     // ***************************************************
3992                     if (startAngle < 180 && startAngle >= 0 && area.Area3DStyle.Inclination > 0
3993                         || startAngle < 540 && startAngle >= 360 && area.Area3DStyle.Inclination > 0
3994                         || startAngle >= 180 && startAngle < 360 && area.Area3DStyle.Inclination < 0
3995                         || startAngle >= -180 && startAngle < 0 && area.Area3DStyle.Inclination < 0
3996                         )
3997                     {
3998                         graph.FillPieCurve(
3999                             area,
4000                             dataPoint,
4001                             brush,
4002                             pen,
4003                             points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
4004                             points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
4005                             points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
4006                             points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
4007                             points[(int)PiePoints.DoughnutTopStart],
4008                             points[(int)PiePoints.DoughnutTopEnd],
4009                             points[(int)PiePoints.DoughnutBottomStart],
4010                             points[(int)PiePoints.DoughnutBottomEnd],
4011                             startAngle,
4012                             sweepAngle,
4013                             pointIndex
4014                             );
4015                     }
4016                 }
4017 
4018             }
4019 
4020 		}
4021 
4022 
4023 		/// <summary>
4024 		/// This method is used for special case when big doughnut slice has to be drawn.
4025 		/// </summary>
4026 		/// <param name="graph">Chart Grahics.</param>
4027 		/// <param name="area">Chart Area.</param>
4028 		/// <param name="dataPoint">Data Point used for pie slice.</param>
4029 		/// <param name="startAngle">Start angle of a pie slice.</param>
4030 		/// <param name="sweepAngle">Sweep angle of a pie slice.</param>
4031 		/// <param name="points">Important 3d points of a pie slice.</param>
4032 		/// <param name="brush">Brush without lithing efects.</param>
4033 		/// <param name="pen">Pen used for border.</param>
4034 		/// <param name="rightPosition">Position of the curve of big pie slice. Big pie slice coud have to visible curves - left and right</param>
4035 		/// <param name="sameBackFront">This is big pie slice which is in same time back and front slice.</param>
4036 		/// <param name="pointIndex">Data Point Index</param>
4037 		/// <returns>True if slice is special case and it is drawn as a special case.</returns>
DrawDoughnutCurvesBigSlice( ChartGraphics graph, ChartArea area, DataPoint dataPoint, float startAngle, float sweepAngle, PointF [] points, Brush brush, Pen pen, bool rightPosition, bool sameBackFront, int pointIndex )4038 		private bool DrawDoughnutCurvesBigSlice
4039 			(
4040 			ChartGraphics graph,
4041 			ChartArea area,
4042 			DataPoint dataPoint,
4043 			float startAngle,
4044 			float sweepAngle,
4045 			PointF [] points,
4046 			Brush brush,
4047 			Pen pen,
4048 			bool rightPosition,
4049 			bool sameBackFront,
4050 			int pointIndex
4051 			)
4052 		{
4053 			float endAngle = startAngle + sweepAngle;
4054 
4055 			// Two different cases connected with X angle.
4056 			// *****************************************************
4057 			// X angle is positive
4058 			// *****************************************************
4059 			if( area.Area3DStyle.Inclination < 0 )
4060 			{
4061 				// Show curve from 0 to 180.
4062 				if( startAngle < 180 && endAngle > 360 )
4063 				{
4064 					graph.FillPieCurve(
4065 						area,
4066 						dataPoint,
4067 						brush,
4068 						pen,
4069 						points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
4070 						points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
4071 						points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
4072 						points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
4073 						points[(int)PiePoints.DoughnutTop0],
4074 						points[(int)PiePoints.DoughnutTop180],
4075 						points[(int)PiePoints.DoughnutBottom0],
4076 						points[(int)PiePoints.DoughnutBottom180],
4077 						0,
4078 						-180,
4079 						pointIndex
4080 						);
4081 				}
4082 				else if( startAngle < 0 && endAngle > 180 )
4083 				{
4084 					// There is big data point which is back and
4085 					// front point in same time.
4086 					if( sameBackFront )
4087 					{
4088 						// The big pie slice has to be split. This part makes
4089 						// decision which part of this big slice will be
4090 						// drawn first.
4091 						if( rightPosition )
4092 						{
4093 							graph.FillPieCurve(
4094 								area,
4095 								dataPoint,
4096 								brush,
4097 								pen,
4098 								points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
4099 								points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
4100 								points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
4101 								points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
4102 								points[(int)PiePoints.DoughnutTop180],
4103 								points[(int)PiePoints.DoughnutTopEnd],
4104 								points[(int)PiePoints.DoughnutBottom180],
4105 								points[(int)PiePoints.DoughnutBottomEnd],
4106 								180,
4107 								endAngle - 180,
4108 								pointIndex
4109 								);
4110 						}
4111 						else
4112 						{
4113 							graph.FillPieCurve(
4114 								area,
4115 								dataPoint,
4116 								brush,
4117 								pen,
4118 								points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
4119 								points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
4120 								points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
4121 								points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
4122 								points[(int)PiePoints.DoughnutTopStart],
4123 								points[(int)PiePoints.DoughnutTop0],
4124 								points[(int)PiePoints.DoughnutBottomStart],
4125 								points[(int)PiePoints.DoughnutBottom0],
4126 								startAngle,
4127 								-startAngle,
4128 								pointIndex
4129 								);
4130 						}
4131 					}
4132 					else
4133 					{
4134 						// There is big pie slice (>180), but that pie slice
4135 						// is not back and front point in same time.
4136 						graph.FillPieCurve(
4137 							area,
4138 							dataPoint,
4139 							brush,
4140 							pen,
4141 							points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
4142 							points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
4143 							points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
4144 							points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
4145 							points[(int)PiePoints.DoughnutTopStart],
4146 							points[(int)PiePoints.DoughnutTop0],
4147 							points[(int)PiePoints.DoughnutBottomStart],
4148 							points[(int)PiePoints.DoughnutBottom0],
4149 							startAngle,
4150 							-startAngle,
4151 							pointIndex
4152 							);
4153 
4154 						graph.FillPieCurve(
4155 							area,
4156 							dataPoint,
4157 							brush,
4158 							pen,
4159 							points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
4160 							points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
4161 							points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
4162 							points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
4163 							points[(int)PiePoints.DoughnutTop180],
4164 							points[(int)PiePoints.DoughnutTopEnd],
4165 							points[(int)PiePoints.DoughnutBottom180],
4166 							points[(int)PiePoints.DoughnutBottomEnd],
4167 							180,
4168 							endAngle - 180,
4169 							pointIndex
4170 							);
4171 					}
4172 
4173 				}
4174 				else
4175 				{
4176 					// Big pie slice behaves as normal pie slice. Continue
4177 					// Non special case alghoritham
4178 					return false;
4179 				}
4180 			}
4181 				// *********************************************
4182 				// X angle negative
4183 				// *********************************************
4184 			else
4185 			{
4186 				// Show curve from 0 to 180.
4187 				if( startAngle < 0 && endAngle > 180 )
4188 				{
4189 					graph.FillPieCurve(
4190 						area,
4191 						dataPoint,
4192 						brush,
4193 						pen,
4194 						points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
4195 						points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
4196 						points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
4197 						points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
4198 						points[(int)PiePoints.DoughnutTop0],
4199 						points[(int)PiePoints.DoughnutTop180],
4200 						points[(int)PiePoints.DoughnutBottom0],
4201 						points[(int)PiePoints.DoughnutBottom180],
4202 						0,
4203 						180,
4204 						pointIndex
4205 						);
4206 				}
4207 				else if( startAngle < 180 && endAngle > 360 )
4208 				{
4209 					// There is big data point which is back and
4210 					// front point in same time.
4211 					if( sameBackFront )
4212 					{
4213 						// The big pie slice has to be split. This part makes
4214 						// decision which part of this big slice will be
4215 						// drawn first.
4216 						if( rightPosition )
4217 						{
4218 							graph.FillPieCurve(
4219 								area,
4220 								dataPoint,
4221 								brush,
4222 								pen,
4223 								points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
4224 								points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
4225 								points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
4226 								points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
4227 								points[(int)PiePoints.DoughnutTopStart],
4228 								points[(int)PiePoints.DoughnutTop180],
4229 								points[(int)PiePoints.DoughnutBottomStart],
4230 								points[(int)PiePoints.DoughnutBottom180],
4231 								startAngle,
4232 								180 - startAngle,
4233 								pointIndex
4234 								);
4235 						}
4236 						else
4237 						{
4238 							graph.FillPieCurve(
4239 								area,
4240 								dataPoint,
4241 								brush,
4242 								pen,
4243 								points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
4244 								points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
4245 								points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
4246 								points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
4247 								points[(int)PiePoints.DoughnutTop0],
4248 								points[(int)PiePoints.DoughnutTopEnd],
4249 								points[(int)PiePoints.DoughnutBottom0],
4250 								points[(int)PiePoints.DoughnutBottomEnd],
4251 								0,
4252 								endAngle - 360,
4253 								pointIndex
4254 								);
4255 						}
4256 					}
4257 					else
4258 					{
4259 						// There is big pie slice (>180), but that pie slice
4260 						// is not back and front point in same time.
4261 						graph.FillPieCurve(
4262 							area,
4263 							dataPoint,
4264 							brush,
4265 							pen,
4266 							points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
4267 							points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
4268 							points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
4269 							points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
4270 							points[(int)PiePoints.DoughnutTop0],
4271 							points[(int)PiePoints.DoughnutTopEnd],
4272 							points[(int)PiePoints.DoughnutBottom0],
4273 							points[(int)PiePoints.DoughnutBottomEnd],
4274 							0,
4275 							endAngle - 360,
4276 							pointIndex
4277 							);
4278 
4279 						graph.FillPieCurve(
4280 							area,
4281 							dataPoint,
4282 							brush,
4283 							pen,
4284 							points[(int)PiePoints.DoughnutTopRectTopLeftPoint],
4285 							points[(int)PiePoints.DoughnutTopRectBottomRightPoint],
4286 							points[(int)PiePoints.DoughnutBottomRectTopLeftPoint],
4287 							points[(int)PiePoints.DoughnutBottomRectBottomRightPoint],
4288 							points[(int)PiePoints.DoughnutTopStart],
4289 							points[(int)PiePoints.DoughnutTop180],
4290 							points[(int)PiePoints.DoughnutBottomStart],
4291 							points[(int)PiePoints.DoughnutBottom180],
4292 							startAngle,
4293 							180 - startAngle,
4294 							pointIndex
4295 							);
4296 					}
4297 
4298 				}
4299 				else
4300 				{
4301 					// Big pie slice behaves as normal pie slice. Continue
4302 					// Non special case alghoritham
4303 					return false;
4304 				}
4305 			}
4306 
4307 			return true;
4308 		}
4309 
4310 
4311 
4312 		#endregion
4313 
4314 		#region 3D Order of points Methods
4315 
4316 		/// <summary>
4317 		/// This method sort data points on specific way. Because
4318 		/// of order of drawing in 3D space, the back data point
4319 		/// (point which pass throw 270 degree has to be drawn first.
4320 		/// After that side data points have to be drawn. At the end
4321 		/// front data point (data point which pass throw 0 degree)
4322 		/// has to be drawn. There is special case if there is big
4323 		/// data point, which is back and front point in same time.
4324 		/// </summary>
4325 		/// <param name="series">Data series</param>
4326 		/// <param name="area">Chart area</param>
4327 		/// <param name="newStartAngleList">Unsorted List of Start angles.</param>
4328 		/// <param name="newSweepAngleList">Unsorted List of Sweep angles.</param>
4329 		/// <param name="newPointIndexList">Data Point index list</param>
4330 		/// <param name="sameBackFrontPoint">Beck and Fron Points are same - There is a big pie slice.</param>
4331 		/// <returns>Sorted data point list.</returns>
PointOrder( Series series, ChartArea area, out float [] newStartAngleList, out float [] newSweepAngleList, out int [] newPointIndexList, out bool sameBackFrontPoint )4332 		private DataPoint [] PointOrder( Series series, ChartArea area, out float [] newStartAngleList, out float [] newSweepAngleList, out int [] newPointIndexList, out bool sameBackFrontPoint )
4333 		{
4334 
4335 			double startAngle;
4336 			double sweepAngle;
4337 			double endAngle;
4338 			int backPoint = -1;
4339 			int frontPoint = -1;
4340 			sameBackFrontPoint = false;
4341 
4342 			// The data points loop. Find Sum of data points.
4343 			double	sum = 0;
4344 			int numOfEmpty = 0;
4345 			foreach( DataPoint point in series.Points )
4346 			{
4347 				if( point.IsEmpty )
4348 					numOfEmpty++;
4349 
4350 				if( !point.IsEmpty )
4351 				{
4352 					sum += Math.Abs(point.YValues[0]);
4353 				}
4354 			}
4355 
4356 			// Find number of data points
4357 			int numOfPoints = series.Points.Count - numOfEmpty;
4358 
4359 			DataPoint [] points = new DataPoint[ numOfPoints ];
4360 			float [] startAngleList = new float[ numOfPoints ];
4361 			float [] sweepAngleList = new float[ numOfPoints ];
4362 			int [] pointIndexList = new int[ numOfPoints ];
4363 			newStartAngleList = new float[ numOfPoints ];
4364 			newSweepAngleList = new float[ numOfPoints ];
4365 			newPointIndexList = new int[ numOfPoints ];
4366 
4367 			// If sum is less then 0 do not draw pie chart
4368 			if( sum <= 0 )
4369 			{
4370 				return null;
4371 			}
4372 			// *****************************************************
4373 			// Find Back and Front Points. Back point is a point
4374 			// which pass throw 270 degree. Front point pass
4375 			// throw 90 degree.
4376 			// There are two points in the data point list which will be
4377 			// placed at the end and at the beginning on the sorted list: Back
4378 			// point (beginning) and Front point (the end). Back point could
4379 			// be only after Front point at the unsorted list.
4380 			// *****************************************************
4381 			int	pointIndx = 0;
4382 			startAngle = area.Area3DStyle.Rotation;
4383 			foreach( DataPoint point in series.Points )
4384 			{
4385 				// Do not process empty points
4386 				if( point.IsEmpty )
4387 				{
4388 					continue;
4389 				}
4390 
4391 				// Find angles
4392 				sweepAngle = (float)( Math.Abs(point.YValues[0]) * 360 / sum );
4393 				endAngle = startAngle + sweepAngle;
4394 
4395 				startAngleList[ pointIndx ] = (float)startAngle;
4396 				sweepAngleList[ pointIndx ] = (float)sweepAngle;
4397 				pointIndexList[ pointIndx ] = pointIndx;
4398 
4399 				// ***************************************************************
4400 				// Find Back point.
4401 				// Because angle could be between -180 and 540 ( Y axis
4402 				// rotation from -180 to 180 ), Back point could be at -90 and 270
4403 				// ***************************************************************
4404 				if( startAngle <= -90 && endAngle > -90 || startAngle <= 270 && endAngle > 270 && points[0] == null )
4405 				{
4406                     /*if( points[0] != null )
4407                         throw new InvalidOperationException(SR.ExceptionPiePointOrderInvalid);
4408                         */
4409                     backPoint = pointIndx;
4410 					points[0] = point;
4411 					newStartAngleList[0] = startAngleList[pointIndx];
4412 					newSweepAngleList[0] = sweepAngleList[pointIndx];
4413 					newPointIndexList[0] = pointIndexList[pointIndx];
4414 				}
4415 
4416 				// ***************************************************************
4417 				// Find Front point.
4418 				// Because angle could be between -180 and 540 ( Y axis
4419 				// rotation from -180 to 180 ), Front point could be at 90 and 450
4420 				// Case frontPoint == -1 is set because of rounding error.
4421 				// ***************************************************************
4422 				if( startAngle <= 90 && endAngle > 90 || startAngle <= 450 && endAngle > 450 && frontPoint == -1 && ( points[points.Length-1] == null || points.Length == 1 ) )
4423 				{
4424                     /*
4425                    if( points[points.Length-1] != null && points.Length != 1)
4426                         throw new InvalidOperationException(SR.ExceptionPiePointOrderInvalid);
4427                     */
4428                     frontPoint = pointIndx;
4429 					points[points.Length-1] = point;
4430 					newStartAngleList[points.Length-1] = startAngleList[pointIndx];
4431 					newSweepAngleList[points.Length-1] = sweepAngleList[pointIndx];
4432 					newPointIndexList[points.Length-1] = pointIndexList[pointIndx];
4433 				}
4434 
4435 				pointIndx++;
4436 				startAngle += sweepAngle;
4437 			}
4438 
4439 			if( frontPoint == -1 || backPoint == -1 )
4440 			{
4441                 throw new InvalidOperationException(SR.ExceptionPieUnassignedFrontBackPoints);
4442 			}
4443 
4444 			// If front point and back point are same do not
4445 			// put same point in two fields.
4446 			if( frontPoint == backPoint && points.Length != 1 )
4447 			{
4448 				points[points.Length-1] = null;
4449 				newStartAngleList[points.Length-1] = 0;
4450 				newSweepAngleList[points.Length-1] = 0;
4451 				newPointIndexList[points.Length-1] = 0;
4452 				sameBackFrontPoint = true;
4453 			}
4454 
4455 			// ********************************************
4456 			// Special case. Front Point and Back points
4457 			// are same.
4458 			// ********************************************
4459 			if( frontPoint == backPoint )
4460 			{
4461 				// Find middle angle of a data point
4462 				float midAngle = startAngleList[backPoint] + sweepAngleList[backPoint] / 2F;
4463 
4464 				int	listIndx;
4465 				bool rightSidePoints = false;
4466 
4467 				// If big pie slice is on the right and all other
4468 				// pie slices are on the left.
4469 				if( midAngle > -90 && midAngle < 90 || midAngle > 270 && midAngle < 450 )
4470 				{
4471 					rightSidePoints = true;
4472 				}
4473 
4474 				listIndx = numOfPoints - frontPoint;
4475 				pointIndx = 0;
4476 				foreach( DataPoint point in series.Points )
4477 				{
4478 					// Do not process empty points
4479 					if( point.IsEmpty )
4480 					{
4481 						continue;
4482 					}
4483 
4484 					// If Front and back points continue with loop
4485 					if( pointIndx == frontPoint )
4486 					{
4487 						pointIndx++;
4488 						continue;
4489 					}
4490 
4491 					if( pointIndx < frontPoint )
4492 					{
4493 						if( points[listIndx] != null )
4494                             throw new InvalidOperationException(SR.ExceptionPiePointOrderInvalid);
4495 						points[listIndx] = point;
4496 						newStartAngleList[listIndx] = startAngleList[pointIndx];
4497 						newSweepAngleList[listIndx] = sweepAngleList[pointIndx];
4498 						newPointIndexList[listIndx] = pointIndexList[pointIndx];
4499 
4500 						listIndx++;
4501 					}
4502 					pointIndx++;
4503 				}
4504 
4505 				pointIndx = 0;
4506 				listIndx = 1;
4507 				foreach( DataPoint point in series.Points )
4508 				{
4509 					// Do not process empty points
4510 					if( point.IsEmpty )
4511 					{
4512 						continue;
4513 					}
4514 
4515 					// If Front and back points continue with loop
4516 					if( pointIndx == frontPoint )
4517 					{
4518 						pointIndx++;
4519 						continue;
4520 					}
4521 
4522 					if( pointIndx > frontPoint )
4523 					{
4524 						if( points[listIndx] != null )
4525                             throw new InvalidOperationException(SR.ExceptionPiePointOrderInvalid);
4526 						points[listIndx] = point;
4527 						newStartAngleList[listIndx] = startAngleList[pointIndx];
4528 						newSweepAngleList[listIndx] = sweepAngleList[pointIndx];
4529 						newPointIndexList[listIndx] = pointIndexList[pointIndx];
4530 						listIndx++;
4531 					}
4532 					pointIndx++;
4533 				}
4534 				if( rightSidePoints )
4535 				{
4536 					SwitchPoints( numOfPoints, ref points, ref newStartAngleList, ref newSweepAngleList, ref newPointIndexList, backPoint == frontPoint );
4537 				}
4538 			}
4539 			else if( frontPoint < backPoint )
4540 			{
4541 
4542 				// ************************************************
4543 				// Fill From Back Point to the end of unsorted list
4544 				// ************************************************
4545 				pointIndx = 0;
4546 				int	listIndx = 1;
4547 				foreach( DataPoint point in series.Points )
4548 				{
4549 					// Do not process empty points
4550 					if( point.IsEmpty )
4551 					{
4552 						continue;
4553 					}
4554 
4555 					// If Front and back points continue with loop
4556 					if( pointIndx == frontPoint || pointIndx == backPoint )
4557 					{
4558 						pointIndx++;
4559 						continue;
4560 					}
4561 
4562 						// If curent point is after front point.
4563 					else if( pointIndx > backPoint )
4564 					{
4565 						if( points[listIndx] != null )
4566                             throw new InvalidOperationException(SR.ExceptionPiePointOrderInvalid);
4567 						points[listIndx] = point;
4568 						newStartAngleList[listIndx] = startAngleList[pointIndx];
4569 						newSweepAngleList[listIndx] = sweepAngleList[pointIndx];
4570 						newPointIndexList[listIndx] = pointIndexList[pointIndx];
4571 						listIndx++;
4572 					}
4573 
4574 					pointIndx++;
4575 				}
4576 
4577 				// ******************************************************
4578 				// Fill from the begining of unsorted list to Front Point
4579 				// ******************************************************
4580 				pointIndx = 0;
4581 				foreach( DataPoint point in series.Points )
4582 				{
4583 					// Do not process empty points
4584 					if( point.IsEmpty )
4585 					{
4586 						continue;
4587 					}
4588 
4589 					// If Front and back points continue with loop
4590 					if( pointIndx == frontPoint || pointIndx == backPoint )
4591 					{
4592 						pointIndx++;
4593 						continue;
4594 					}
4595 
4596 						// If curent point is before front point.
4597 					else if( pointIndx < frontPoint )
4598 					{
4599 						if( points[listIndx] != null )
4600                             throw new InvalidOperationException(SR.ExceptionPiePointOrderInvalid);
4601 						points[listIndx] = point;
4602 						newStartAngleList[listIndx] = startAngleList[pointIndx];
4603 						newSweepAngleList[listIndx] = sweepAngleList[pointIndx];
4604 						newPointIndexList[listIndx] = pointIndexList[pointIndx];
4605 						listIndx++;
4606 					}
4607 
4608 					pointIndx++;
4609 				}
4610 
4611 
4612 				// *********************************************************
4613 				// This code run only if special case is not active.
4614 				// Special case: FrontPoint and back point are same. This is
4615 				// happening because pie slice is bigger then 180 degree.
4616 				// *********************************************************
4617 
4618 
4619 				// **********************************
4620 				// Fill from Front Point to Back Point
4621 				// **********************************
4622 				listIndx = points.Length - 2;
4623 				pointIndx = 0;
4624 				foreach( DataPoint point in series.Points )
4625 				{
4626 					// Do not process empty points
4627 					if( point.IsEmpty )
4628 					{
4629 						continue;
4630 					}
4631 
4632 					// If Front and back points continue with loop
4633 					if( pointIndx == frontPoint || pointIndx == backPoint )
4634 					{
4635 						pointIndx++;
4636 						continue;
4637 					}
4638 
4639 						// If curent point is between front point and back point.
4640 					else if( pointIndx > frontPoint && pointIndx < backPoint )
4641 					{
4642                         if (points[listIndx] != null) throw new InvalidOperationException(SR.ExceptionPiePointOrderInvalid);
4643 						points[listIndx] = point;
4644 						newStartAngleList[listIndx] = startAngleList[pointIndx];
4645 						newSweepAngleList[listIndx] = sweepAngleList[pointIndx];
4646 						newPointIndexList[listIndx] = pointIndexList[pointIndx];
4647 						listIndx--;
4648 					}
4649 
4650 					pointIndx++;
4651 				}
4652 			}
4653 			else
4654 			{
4655 				// **********************************
4656 				// Fill from Back Point to Front Point
4657 				// **********************************
4658 				int listIndx = 1;
4659 				pointIndx = 0;
4660 				foreach( DataPoint point in series.Points )
4661 				{
4662 					// Do not process empty points
4663 					if( point.IsEmpty )
4664 					{
4665 						continue;
4666 					}
4667 
4668 					// If Front and back points continue with loop
4669 					if( pointIndx == frontPoint || pointIndx == backPoint )
4670 					{
4671 						pointIndx++;
4672 						continue;
4673 					}
4674 
4675 						// If curent point is between front back and front points.
4676 					else if( pointIndx > backPoint && pointIndx < frontPoint )
4677 					{
4678                         if (points[listIndx] != null) throw new InvalidOperationException(SR.ExceptionPiePointOrderInvalid);
4679 						points[listIndx] = point;
4680 						newStartAngleList[listIndx] = startAngleList[pointIndx];
4681 						newSweepAngleList[listIndx] = sweepAngleList[pointIndx];
4682 						newPointIndexList[listIndx] = pointIndexList[pointIndx];
4683 						listIndx++;
4684 					}
4685 
4686 					pointIndx++;
4687 				}
4688 
4689 				// ************************************************
4690 				// Fill From Front Point to the end of unsorted list
4691 				// ************************************************
4692 				listIndx = points.Length - 2;
4693 				pointIndx = 0;
4694 				foreach( DataPoint point in series.Points )
4695 				{
4696 					// Do not process empty points
4697 					if( point.IsEmpty )
4698 					{
4699 						continue;
4700 					}
4701 
4702 					// If Front and back points continue with loop
4703 					if( pointIndx == frontPoint || pointIndx == backPoint )
4704 					{
4705 						pointIndx++;
4706 						continue;
4707 					}
4708 						// If curent point is after front point.
4709 					else if( pointIndx > frontPoint )
4710 					{
4711 						if( points[listIndx] != null )
4712                             throw new InvalidOperationException(SR.ExceptionPiePointOrderInvalid);
4713 						points[listIndx] = point;
4714 						newStartAngleList[listIndx] = startAngleList[pointIndx];
4715 						newSweepAngleList[listIndx] = sweepAngleList[pointIndx];
4716 						newPointIndexList[listIndx] = pointIndexList[pointIndx];
4717 						listIndx--;
4718 					}
4719 
4720 					pointIndx++;
4721 				}
4722 
4723 				// ******************************************************
4724 				// Fill from the begining of unsorted list to Back Point
4725 				// ******************************************************
4726 				pointIndx = 0;
4727 				foreach( DataPoint point in series.Points )
4728 				{
4729 					// Do not process empty points
4730 					if( point.IsEmpty )
4731 					{
4732 						continue;
4733 					}
4734 
4735 					// If Front and back points continue with loop
4736 					if( pointIndx == frontPoint || pointIndx == backPoint )
4737 					{
4738 						pointIndx++;
4739 						continue;
4740 					}
4741 
4742 						// If curent point is before front point.
4743 					else if( pointIndx < backPoint )
4744 					{
4745 						if( points[listIndx] != null )
4746                             throw new InvalidOperationException(SR.ExceptionPiePointOrderInvalid);
4747 						points[listIndx] = point;
4748 						newStartAngleList[listIndx] = startAngleList[pointIndx];
4749 						newSweepAngleList[listIndx] = sweepAngleList[pointIndx];
4750 						newPointIndexList[listIndx] = pointIndexList[pointIndx];
4751 						listIndx--;
4752 					}
4753 
4754 					pointIndx++;
4755 				}
4756 
4757 
4758 				// *********************************************************
4759 				// This code run only if special case is not active.
4760 				// Special case: FrontPoint and back point are same. This is
4761 				// happening because pie slice is bigger then 180 degree.
4762 				// *********************************************************
4763 
4764 
4765 
4766 			}
4767 
4768 
4769 			// *******************************************************
4770 			// If X angle is positive direction of drawing data points
4771 			// should be opposite. This part of code switch order of
4772 			// data points.
4773 			// *******************************************************
4774 			if( area.Area3DStyle.Inclination > 0 )
4775 			{
4776 				SwitchPoints( numOfPoints, ref points, ref newStartAngleList, ref newSweepAngleList, ref newPointIndexList, backPoint == frontPoint );
4777 			}
4778 
4779 			return points;
4780 		}
4781 
4782 		/// <summary>
4783 		/// This method switches order of data points in the array of points.
4784 		/// </summary>
4785 		/// <param name="numOfPoints">Number of data points</param>
4786 		/// <param name="points">Array of Data points</param>
4787 		/// <param name="newStartAngleList">List of start angles which has to be switched together with data points</param>
4788 		/// <param name="newSweepAngleList">List of sweep angles which has to be switched together with data points</param>
4789 		/// <param name="newPointIndexList">Indexes (position) of data points in the series</param>
4790 		/// <param name="sameBackFront">There is big pie slice which has same back and front pie slice</param>
SwitchPoints( int numOfPoints, ref DataPoint [] points, ref float [] newStartAngleList, ref float [] newSweepAngleList, ref int [] newPointIndexList, bool sameBackFront )4791 		private void SwitchPoints( int numOfPoints, ref DataPoint [] points, ref float [] newStartAngleList, ref float [] newSweepAngleList, ref int [] newPointIndexList, bool sameBackFront )
4792 		{
4793 			float [] tempStartAngles = new float[ numOfPoints ];
4794 			float [] tempSweepAngles = new float[ numOfPoints ];
4795 			int [] tempPointIndexList = new int[ numOfPoints ];
4796 			DataPoint [] tempPoints = new DataPoint[ numOfPoints ];
4797 			int start = 0;;
4798 
4799 			// The big pie slice (special case) is always on the beginning.
4800 			if( sameBackFront )
4801 			{
4802 				start = 1;
4803 
4804 				// Switch order.
4805 				tempPoints[0] = points[0];
4806 				tempStartAngles[0] = newStartAngleList[0];
4807 				tempSweepAngles[0] = newSweepAngleList[0];
4808 				tempPointIndexList[0] = newPointIndexList[0];
4809 			}
4810 
4811 			for( int index = start; index < numOfPoints; index++ )
4812 			{
4813 				if( points[ index ] == null )
4814 				{
4815                     throw new InvalidOperationException(SR.ExceptionPieOrderOperationInvalid);
4816 				}
4817 
4818 				// Switch order.
4819 				tempPoints[ numOfPoints - index - 1 + start] = points[ index ];
4820 				tempStartAngles[ numOfPoints - index - 1 + start] = newStartAngleList[ index ];
4821 				tempSweepAngles[ numOfPoints - index - 1 + start] = newSweepAngleList[ index ];
4822 				tempPointIndexList[ numOfPoints - index - 1 + start] = newPointIndexList[ index ];
4823 
4824 			}
4825 
4826 			points = tempPoints;
4827 			newStartAngleList = tempStartAngles;
4828 			newSweepAngleList = tempSweepAngles;
4829 			newPointIndexList = tempPointIndexList;
4830 
4831 		}
4832 
4833 		#endregion
4834 
4835 		#region 3D Label column class
4836 
4837 		/// <summary>
4838 		/// LabelColumn class is used for labels manipulation - outside label style
4839 		/// </summary>
4840 		internal class LabelColumn
4841 		{
4842 			// Fields of Label Column class
4843 			private RectangleF _chartAreaPosition;
4844 			private RectangleF _innerPlotPosition;
4845 			internal float columnHeight;
4846 			internal int numOfItems = 0;
4847 			private int _numOfInsertedLabels = 0;
4848 			private DataPoint [] _points;
4849 			private float [] _yPositions;
4850 			private bool _rightPosition = true;
4851 			private float _labelLineSize;
4852 
4853 			/// <summary>
4854 			/// Constructor
4855 			/// </summary>
4856 			/// <param name="position">Chart Area position.</param>
LabelColumn( RectangleF position )4857 			public LabelColumn( RectangleF position )
4858 			{
4859 				_chartAreaPosition = position;
4860 			}
4861 
4862 			/// <summary>
4863 			/// Return index of label position in the column.
4864 			/// </summary>
4865 			/// <param name="y">y coordinate</param>
4866 			/// <returns>Index of column</returns>
GetLabelIndex( float y )4867 			internal int GetLabelIndex( float y )
4868 			{
4869 				// y coordinate is out of chart area.
4870 				if( y < _chartAreaPosition.Y )
4871 				{
4872 					y = _chartAreaPosition.Y;
4873 				}
4874 				else if( y > _chartAreaPosition.Bottom )
4875 				{
4876 					y = _chartAreaPosition.Bottom - columnHeight;
4877 				}
4878 
4879 				return (int) (( y - _chartAreaPosition.Y ) / columnHeight ) ;
4880 			}
4881 
4882 			/// <summary>
4883 			/// This method sorts labels by y Position
4884 			/// </summary>
Sort()4885 			internal void Sort()
4886 			{
4887 				for( int indexA = 0; indexA < _points.Length; indexA++ )
4888 				{
4889 					for( int indexB = 0; indexB < indexA; indexB++ )
4890 					{
4891 						if( _yPositions[indexA] < _yPositions[indexB] && _points[indexA] != null && _points[indexB] != null )
4892 						{
4893 							float tempYPos;
4894 							DataPoint tempPoint;
4895 							tempYPos = _yPositions[indexA];
4896 							tempPoint = _points[indexA];
4897 							_yPositions[indexA] = _yPositions[indexB];
4898 							_points[indexA] = _points[indexB];
4899 							_yPositions[indexB] = tempYPos;
4900 							_points[indexB] = tempPoint;
4901 						}
4902 					}
4903 				}
4904 			}
4905 
4906 			/// <summary>
4907 			/// Returns label position y coordinate from index position
4908 			/// </summary>
4909 			/// <param name="index">Index position of the row</param>
4910 			/// <returns>Y coordinate row position</returns>
GetLabelPosition( int index )4911 			internal float GetLabelPosition( int index )
4912 			{
4913 				if( index < 0 || index > numOfItems - 1 )
4914                     throw new InvalidOperationException(SR.Exception3DPieLabelsIndexInvalid);
4915 
4916 				return (float) _chartAreaPosition.Y + columnHeight * index + columnHeight / 2;
4917 			}
4918 
4919 			/// <summary>
4920 			/// This method finds X and Y position for outside
4921 			/// labels. There is discrete number of cells and
4922 			/// Y position depends on cell position. X position
4923 			/// is connected with angle between invisible
4924 			/// line (which connects center of a pie and label)
4925 			/// and any horizontal line.
4926 			/// </summary>
4927 			/// <param name="dataPoint">Data Point</param>
4928 			/// <returns>Position of a label</returns>
GetLabelPosition( DataPoint dataPoint )4929 			internal PointF GetLabelPosition( DataPoint dataPoint )
4930 			{
4931 				PointF position = PointF.Empty;
4932 				int pointIndex = 0;
4933 
4934 				// Find Y position of Data Point
4935 				// Loop is necessary to find index of data point in the array list.
4936 				foreach( DataPoint point in _points )
4937 				{
4938 					if( point == dataPoint )
4939 					{
4940 						position.Y = GetLabelPosition( pointIndex );
4941 						break;
4942 					}
4943 					pointIndex++;
4944 				}
4945 
4946 				// Find initial X position for labels ( All labels are aligne ).
4947 				if( _rightPosition )
4948 				{
4949 					position.X = _innerPlotPosition.Right + _chartAreaPosition.Width * this._labelLineSize;
4950 				}
4951 				else
4952 				{
4953 					position.X = _innerPlotPosition.Left - _chartAreaPosition.Width * this._labelLineSize;
4954 				}
4955 
4956 				// Find angle between invisible line (which connects center of a pie and label)
4957 				// and any horizontal line.
4958 				float angle;
4959 				angle = (float)Math.Atan( ( position.Y - _innerPlotPosition.Top - _innerPlotPosition.Height / 2) / ( position.X - _innerPlotPosition.Left - _innerPlotPosition.Width / 2 ));
4960 
4961 				// Make Angle correction for X Position
4962 				float correct;
4963 				if( Math.Cos( angle ) == 0 )
4964 				{
4965 					correct = 0;
4966 				}
4967 				else
4968 				{
4969 					correct = (float)(_innerPlotPosition.Width * 0.4 - _innerPlotPosition.Width * 0.4 / Math.Cos( angle ));
4970 				}
4971 
4972 				// Set Corrected X Position
4973 				if( _rightPosition )
4974 				{
4975 					position.X += correct;
4976 				}
4977 				else
4978 				{
4979 					position.X -= correct;
4980 				}
4981 
4982 				return position;
4983 			}
4984 
4985 			/// <summary>
4986 			/// This method inserts outside labels in Column label list. Column label
4987 			/// list has defined number of cells. This method has to put labels on
4988 			/// the best position in the list. If two labels according to their
4989 			/// positions belong to same cell of the list, this method should
4990 			/// assign to them different positions.
4991 			/// </summary>
4992 			/// <param name="point">Data Point which label has to be inserted</param>
4993 			/// <param name="yCoordinate">Y coordinate which is the best position for this label</param>
4994 			/// <param name="pointIndx">Point index of this data point in the series</param>
InsertLabel( DataPoint point, float yCoordinate, int pointIndx )4995 			internal void InsertLabel( DataPoint point, float yCoordinate, int pointIndx )
4996 			{
4997 
4998 				// Find index of label list by Y value
4999 				int indexYValue = GetLabelIndex( yCoordinate );
5000 
5001 				// This position is already used.
5002 				if( _points[indexYValue] != null )
5003 				{
5004 					// All even elements go up and other
5005 					// Down (If there are many labels which use this position).
5006 					if( pointIndx % 2 == 0 )
5007 					{
5008 						// Check if there is space Down
5009 						if( CheckFreeSpace( indexYValue, false ) )
5010 						{
5011 							// Move labels Down
5012 							MoveLabels( indexYValue, false );
5013 						}
5014 						else
5015 						{
5016 							// Move labels Up
5017 							MoveLabels( indexYValue, true );
5018 						}
5019 					}
5020 					else
5021 					{
5022 						// Check if there is space Up
5023 						if( CheckFreeSpace( indexYValue, true ) )
5024 						{
5025 							// Move labels Up
5026 							MoveLabels( indexYValue, true );
5027 						}
5028 						else
5029 						{
5030 							// Move labels Down
5031 							MoveLabels( indexYValue, false );
5032 						}
5033 					}
5034 				}
5035 
5036 				// Set label position
5037 				_points[indexYValue] = point;
5038 				_yPositions[indexYValue] = yCoordinate;
5039 				_numOfInsertedLabels++;
5040 			}
5041 
5042 			/// <summary>
5043 			/// This method is used for inserting labels. When label is inserted
5044 			/// and that position was previously used, labels have to be
5045 			/// moved on proper way.
5046 			/// </summary>
5047 			/// <param name="position">Position which has to be free</param>
5048 			/// <param name="upDirection">Direction for moving labels</param>
MoveLabels( int position, bool upDirection )5049 			private void MoveLabels( int position, bool upDirection )
5050 			{
5051 				if( upDirection )
5052 				{
5053 					DataPoint point = _points[position];
5054 					float yValue = _yPositions[position];
5055 					_points[position] = null;
5056 					_yPositions[position] = 0;
5057 
5058 					for( int index = position; index > 0; index-- )
5059 					{
5060 						// IsEmpty position found. Stop moving cells UP
5061 						if( _points[index-1] == null )
5062 						{
5063 							_points[index-1] = point;
5064 							_yPositions[index-1] = yValue;
5065 							break;
5066 						}
5067 						else
5068 						{
5069 							DataPoint tempPoint;
5070 							float tempYValue;
5071 
5072 							tempPoint = _points[index-1];
5073 							tempYValue = _yPositions[index-1];
5074 							_points[index-1] = point;
5075 							_yPositions[index-1] = yValue;
5076 							point = tempPoint;
5077 							yValue = tempYValue;
5078 						}
5079 					}
5080 				}
5081 				else
5082 				{
5083 					DataPoint point = _points[position];
5084 					float yValue = _yPositions[position];
5085 					_points[position] = null;
5086 					_yPositions[position] = 0;
5087 
5088 					for( int index = position; index < numOfItems-1; index++ )
5089 					{
5090 						// IsEmpty position found. Stop moving cells UP
5091 						if( _points[index+1] == null )
5092 						{
5093 							_points[index+1] = point;
5094 							_yPositions[index+1] = yValue;
5095 							break;
5096 						}
5097 						else
5098 						{
5099 							DataPoint tempPoint;
5100 							float tempYValue;
5101 
5102 							tempPoint = _points[index+1];
5103 							tempYValue = _yPositions[index+1];
5104 							_points[index+1] = point;
5105 							_yPositions[index+1] = yValue;
5106 							point = tempPoint;
5107 							yValue = tempYValue;
5108 						}
5109 					}
5110 				}
5111 			}
5112 
5113 			/// <summary>
5114 			/// This method is used to center labels in
5115 			/// the middle of chart area (vertically).
5116 			/// </summary>
AdjustPositions()5117 			internal void AdjustPositions()
5118 			{
5119 				int numEmptyUp = 0;
5120 				int numEmptyDown = 0;
5121 
5122 				// Adjust position only if there are many labels
5123 				if( _numOfInsertedLabels < _points.Length / 2 )
5124 					return;
5125 
5126 				// Find the number of empty label positions on the top.
5127 				for( int point = 0; point < _points.Length && _points[point] == null; point++ )
5128 				{
5129 					numEmptyUp++;
5130 				}
5131 
5132 				// Find the number of empty label positions on the bottom.
5133 				for( int point = _points.Length - 1; point >= 0 && _points[point] == null; point-- )
5134 				{
5135 					numEmptyDown++;
5136 				}
5137 
5138 				// Find where are more empty spaces � on the top or on the bottom.
5139 				bool moreEmptyUp = numEmptyUp > numEmptyDown ? true : false;
5140 
5141 				// Find average number of empty spaces for top and bottom.
5142 				int numMove = ( numEmptyUp + numEmptyDown ) / 2;
5143 
5144 				// If difference between empty spaces on the top and
5145 				// the bottom is not bigger then 2 do not adjust labels.
5146 				if( Math.Abs( numEmptyUp - numEmptyDown ) < 2 )
5147 					return;
5148 
5149 				if( moreEmptyUp )
5150 				{
5151 					// Move labels UP
5152 					int indexPoint = 0;
5153 					for( int point = numMove; point < _points.Length; point++ )
5154 					{
5155 						if(numEmptyUp+indexPoint > _points.Length - 1)
5156 							break;
5157 
5158 						_points[point] = _points[numEmptyUp+indexPoint];
5159 						_points[numEmptyUp+indexPoint] = null;
5160 						indexPoint++;
5161 					}
5162 				}
5163 				else
5164 				{
5165 					// Move labels DOWN
5166 					int indexPoint = _points.Length - 1;
5167 					for( int point = _points.Length - 1 - numMove; point >= 0; point-- )
5168 					{
5169 						if(indexPoint - numEmptyDown < 0)
5170 							break;
5171 
5172 						_points[point] = _points[indexPoint - numEmptyDown];
5173 						_points[indexPoint - numEmptyDown] = null;
5174 						indexPoint--;
5175 					}
5176 				}
5177 			}
5178 
5179 
5180 			/// <summary>
5181 			/// Check if there is empty cell Labels column in
5182 			/// specified direction from specified position
5183 			/// </summary>
5184 			/// <param name="position">Start Position for testing</param>
5185 			/// <param name="upDirection">True if direction is upward, false if downward</param>
5186 			/// <returns>True if there is empty cell</returns>
CheckFreeSpace( int position, bool upDirection )5187 			private bool CheckFreeSpace( int position, bool upDirection )
5188 			{
5189 				if( upDirection )
5190 				{
5191 					// Position is on the beginning. There is no empty space.
5192 					if( position == 0 )
5193 					{
5194 						return false;
5195 					}
5196 
5197 					for( int index = position - 1; index >= 0; index-- )
5198 					{
5199 						// There is empty space
5200 						if( _points[index] == null )
5201 						{
5202 							return true;
5203 						}
5204 					}
5205 				}
5206 				else
5207 				{
5208 					// Position is on the end. There is no empty space.
5209 					if( position == numOfItems - 1 )
5210 					{
5211 						return false;
5212 					}
5213 
5214 					for( int index = position + 1; index < numOfItems; index++ )
5215 					{
5216 						// There is empty space
5217 						if( _points[index] == null )
5218 						{
5219 							return true;
5220 						}
5221 					}
5222 				}
5223 
5224 				// There is no empty space
5225 				return false;
5226 			}
5227 
5228 
5229 			/// <summary>
5230 			/// This method initialize label column.
5231 			/// </summary>
5232 			/// <param name="rectangle">Rectangle used for labels</param>
5233 			/// <param name="rightPosition">True if labels are on the right side of chart area.</param>
5234 			/// <param name="maxNumOfRows">Maximum nuber of rows.</param>
5235 			/// <param name="labelLineSize">Value for label line size from custom attribute.</param>
Initialize( RectangleF rectangle, bool rightPosition, int maxNumOfRows, float labelLineSize )5236 			internal void Initialize( RectangleF rectangle, bool rightPosition, int maxNumOfRows, float labelLineSize )
5237 			{
5238 
5239 				// Minimum number of rows.
5240 				numOfItems = Math.Max( numOfItems, maxNumOfRows );
5241 
5242 				// Find height of rows
5243 				columnHeight = _chartAreaPosition.Height / numOfItems;
5244 
5245 				// Set inner plot position
5246 				_innerPlotPosition = rectangle;
5247 
5248 				// Init data column
5249 				_points = new DataPoint[numOfItems];
5250 
5251 				// Init y position column
5252 				_yPositions = new float[numOfItems];
5253 
5254 				// Label column position
5255 				this._rightPosition = rightPosition;
5256 
5257 				// 3D Label line size
5258 				this._labelLineSize = labelLineSize;
5259 
5260 			}
5261 
5262 		}
5263 
5264 		#endregion // 3D Label column class
5265 
5266 		#region 3D Labels
5267 
5268 		/// <summary>
5269 		/// This method calculates initial pie size if outside 3D labels is active.
5270 		/// </summary>
5271 		/// <param name="graph">Chart Graphics object.</param>
5272 		/// <param name="area">Chart Area.</param>
5273 		/// <param name="pieRectangle">Rectangle which is used for drawing pie.</param>
5274 		/// <param name="pieWidth">Width of pie slice.</param>
5275 		/// <param name="dataPoints">List of data points.</param>
5276 		/// <param name="startAngleList">List of start angles.</param>
5277 		/// <param name="sweepAngleList">List of sweep angles.</param>
5278 		/// <param name="series">Data series used for drawing pie chart.</param>
5279 		/// <param name="labelLineSize">Custom Attribute for label line size.</param>
InitPieSize( ChartGraphics graph, ChartArea area, ref RectangleF pieRectangle, ref float pieWidth, DataPoint [] dataPoints, float [] startAngleList, float [] sweepAngleList, Series series, float labelLineSize )5280 		private void InitPieSize(
5281 			ChartGraphics graph,
5282 			ChartArea area,
5283 			ref RectangleF pieRectangle,
5284 			ref float pieWidth,
5285 			DataPoint [] dataPoints,
5286 			float [] startAngleList,
5287 			float [] sweepAngleList,
5288 			Series series,
5289 			float labelLineSize
5290 			)
5291 		{
5292 			labelColumnLeft = new LabelColumn(area.Position.ToRectangleF());
5293 			labelColumnRight = new LabelColumn(area.Position.ToRectangleF());
5294 			float maxSize = float.MinValue;
5295 			float maxSizeVertical = float.MinValue;
5296 
5297 			int	pointIndx = 0;
5298 			// Loop which finds max label size and number of label rows.
5299 			foreach( DataPoint point in dataPoints )
5300 			{
5301 				// Do not process empty points
5302 				if( point.IsEmpty )
5303 				{
5304 					continue;
5305 				}
5306 
5307 				float   midAngle = startAngleList[pointIndx] + sweepAngleList[pointIndx] / 2F;
5308 
5309 				if( midAngle >= -90 && midAngle < 90 || midAngle >= 270 && midAngle < 450 )
5310 				{
5311 					labelColumnRight.numOfItems++;
5312 				}
5313 				else
5314 				{
5315 					labelColumnLeft.numOfItems++;
5316 				}
5317 
5318 				// Find size of the maximum label string.
5319 				SizeF size = graph.MeasureStringRel( GetLabelText( point ).Replace("\\n", "\n"), point.Font );
5320 
5321 				maxSize = Math.Max( size.Width, maxSize );
5322 				maxSizeVertical = Math.Max( size.Height, maxSizeVertical );
5323 
5324 				pointIndx++;
5325 			}
5326 
5327 			float oldWidth = pieRectangle.Width;
5328 			float oldHeight = pieRectangle.Height;
5329 
5330 			// Find size of inner plot are
5331 			pieRectangle.Width = pieRectangle.Width - 2F * maxSize - 2 * pieRectangle.Width * labelLineSize;
5332 
5333 			pieRectangle.Height = pieRectangle.Height - pieRectangle.Height * 0.3F;
5334 
5335 			// Size of pie chart can not be less then MinimumRelativePieSize of chart area.
5336 			if( pieRectangle.Width < oldWidth * (float)this.MinimumRelativePieSize( area ) )
5337 			{
5338 				pieRectangle.Width = oldWidth * (float)this.MinimumRelativePieSize( area );
5339 			}
5340 
5341 			// Size of pie chart can not be less then MinimumRelativePieSize of chart area.
5342 			if( pieRectangle.Height < oldHeight * (float)this.MinimumRelativePieSize( area ) )
5343 			{
5344 				pieRectangle.Height = oldHeight * (float)this.MinimumRelativePieSize( area );
5345 			}
5346 
5347 			// Size has to be reduce always because of label lines.
5348 			if( oldWidth * 0.8F < pieRectangle.Width )
5349 			{
5350 				pieRectangle.Width *= 0.8F;
5351 			}
5352 
5353 			pieRectangle.X = pieRectangle.X + ( oldWidth - pieRectangle.Width ) / 2F;
5354 			pieWidth = pieRectangle.Width / oldWidth * pieWidth;
5355 
5356 			pieRectangle.Y = pieRectangle.Y + ( oldHeight - pieRectangle.Height ) / 2F;
5357 
5358 			// Find maximum number of rows. Number of rows will be changed
5359 			// but this is only recommendation, which depends on font size
5360 			// and Height of chart area.
5361 			SizeF fontSize = new SizeF(1.4F * series.Font.Size,1.4F * series.Font.Size);
5362 			fontSize = graph.GetRelativeSize( fontSize );
5363 			int maxNumOfRows = (int)( pieRectangle.Height / maxSizeVertical/*fontSize.Height*/ );
5364 
5365 			// Initialize label column
5366 			labelColumnRight.Initialize( pieRectangle, true, maxNumOfRows, labelLineSize );
5367 			labelColumnLeft.Initialize( pieRectangle, false, maxNumOfRows, labelLineSize );
5368 
5369 		}
5370 
5371 		/// <summary>
5372 		/// This method inserts outside 3D labels into array of Label column class.
5373 		/// </summary>
5374 		/// <param name="graph">Chart Graphics object.</param>
5375 		/// <param name="area">Chart Area.</param>
5376 		/// <param name="pieRectangle">Rectangle used for drawing pie slices.</param>
5377 		/// <param name="pieWidth">Width of a pie slice.</param>
5378 		/// <param name="point">Data Point.</param>
5379 		/// <param name="startAngle">Start angle of a pie slice.</param>
5380 		/// <param name="sweepAngle">Sweep angle of a pie slice.</param>
5381 		/// <param name="pointIndx">Data point index.</param>
5382 		/// <param name="doughnutRadius">Inner Radius of the doughnut.</param>
5383 		/// <param name="exploded">true if pie slice is exploded.</param>
FillPieLabelOutside( ChartGraphics graph, ChartArea area, RectangleF pieRectangle, float pieWidth, DataPoint point, float startAngle, float sweepAngle, int pointIndx, float doughnutRadius, bool exploded )5384 		private void FillPieLabelOutside(
5385 			ChartGraphics graph,
5386 			ChartArea area,
5387 			RectangleF pieRectangle,
5388 			float pieWidth,
5389 			DataPoint point,
5390 			float startAngle,
5391 			float sweepAngle,
5392 			int pointIndx,
5393 			float doughnutRadius,
5394 			bool exploded
5395 			)
5396 		{
5397 			float midAngle = startAngle + sweepAngle / 2F;
5398 
5399 			PointF [] piePoints = GetPiePoints( graph, area, pieWidth, pieRectangle, startAngle, sweepAngle, false, doughnutRadius, exploded );
5400 
5401 			float y = piePoints[(int)PiePoints.TopLabelLineout].Y;
5402 			if( midAngle >= -90 && midAngle < 90 || midAngle >= 270 && midAngle < 450 )
5403 			{
5404 				labelColumnRight.InsertLabel( point, y, pointIndx );
5405 			}
5406 			else
5407 			{
5408 				labelColumnLeft.InsertLabel( point, y, pointIndx );
5409 			}
5410 		}
5411 
5412 		/// <summary>
5413 		/// This method draws outside labels with lines, which
5414 		/// connect labels with pie slices.
5415 		/// </summary>
5416 		/// <param name="graph">Chart Graphics object</param>
5417 		/// <param name="area">Chart Area</param>
5418 		/// <param name="pen">Pen object</param>
5419 		/// <param name="points">Important pie points</param>
5420 		/// <param name="point">Data point</param>
5421 		/// <param name="midAngle">Middle Angle for pie slice</param>
5422 		/// <param name="pointIndex">Point Index.</param>
Draw3DOutsideLabels( ChartGraphics graph, ChartArea area, Pen pen, PointF [] points, DataPoint point, float midAngle, int pointIndex)5423 		private void Draw3DOutsideLabels(
5424 			ChartGraphics graph,
5425 			ChartArea area,
5426 			Pen pen,
5427 			PointF [] points,
5428 			DataPoint point,
5429 			float midAngle,
5430 			int pointIndex)
5431 		{
5432 			// Take label text
5433 			string text = GetLabelText( point );
5434 			if(text.Length == 0)
5435 			{
5436 				return;
5437 			}
5438 
5439 			graph.DrawLine( pen, points[(int)PiePoints.TopLabelLine], points[(int)PiePoints.TopLabelLineout] );
5440 			LabelColumn columnLabel;
5441 
5442             using (StringFormat format = new StringFormat())
5443             {
5444                 format.LineAlignment = StringAlignment.Center;
5445 
5446                 RectangleF chartAreaPosition = graph.GetAbsoluteRectangle(area.Position.ToRectangleF());
5447                 RectangleF labelPosition = RectangleF.Empty;
5448 
5449                 PointF labelPoint;
5450 
5451                 if (midAngle >= -90 && midAngle < 90 || midAngle >= 270 && midAngle < 450)
5452                 {
5453                     columnLabel = labelColumnRight;
5454                     format.Alignment = StringAlignment.Near;
5455 
5456                     float labelVertSize = graph.GetAbsoluteSize(new SizeF(0f, this.labelColumnRight.columnHeight)).Height;
5457                     labelPoint = graph.GetAbsolutePoint(columnLabel.GetLabelPosition(point));
5458 
5459                     // Label has to be right from TopLabelLineOut
5460                     if (points[(int)PiePoints.TopLabelLineout].X > labelPoint.X)
5461                     {
5462                         labelPoint.X = points[(int)PiePoints.TopLabelLineout].X + 10;
5463                     }
5464 
5465                     labelPosition.X = labelPoint.X;
5466                     labelPosition.Width = chartAreaPosition.Right - labelPosition.X;
5467                     labelPosition.Y = labelPoint.Y - labelVertSize / 2;
5468                     labelPosition.Height = labelVertSize;
5469 
5470                 }
5471                 else
5472                 {
5473                     columnLabel = labelColumnLeft;
5474                     format.Alignment = StringAlignment.Far;
5475 
5476                     float labelVertSize = graph.GetAbsoluteSize(new SizeF(0f, this.labelColumnLeft.columnHeight)).Height;
5477                     labelPoint = graph.GetAbsolutePoint(columnLabel.GetLabelPosition(point));
5478 
5479                     // Label has to be left from TopLabelLineOut
5480                     if (points[(int)PiePoints.TopLabelLineout].X < labelPoint.X)
5481                     {
5482                         labelPoint.X = points[(int)PiePoints.TopLabelLineout].X - 10;
5483                     }
5484 
5485                     labelPosition.X = chartAreaPosition.X;
5486                     labelPosition.Width = labelPoint.X - labelPosition.X;
5487                     labelPosition.Y = labelPoint.Y - labelVertSize / 2;
5488                     labelPosition.Height = labelVertSize;
5489                 }
5490                 format.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit;
5491                 format.Trimming = StringTrimming.EllipsisWord;
5492 
5493                 graph.DrawLine(pen, points[(int)PiePoints.TopLabelLineout], labelPoint);
5494 
5495                 // Get label relative position
5496                 labelPosition = graph.GetRelativeRectangle(labelPosition);
5497 
5498                 // Get label background position
5499                 SizeF valueTextSize = graph.MeasureStringRel(text.Replace("\\n", "\n"), point.Font);
5500                 valueTextSize.Height += valueTextSize.Height / 8;
5501                 float spacing = valueTextSize.Width / text.Length / 2;
5502                 valueTextSize.Width += spacing;
5503                 RectangleF labelBackPosition = new RectangleF(
5504                     labelPosition.X,
5505                     labelPosition.Y + labelPosition.Height / 2f - valueTextSize.Height / 2f,
5506                     valueTextSize.Width,
5507                     valueTextSize.Height);
5508 
5509                 // Adjust position based on alignment
5510                 if (format.Alignment == StringAlignment.Near)
5511                 {
5512                     labelBackPosition.X -= spacing / 2f;
5513                 }
5514                 else if (format.Alignment == StringAlignment.Center)
5515                 {
5516                     labelBackPosition.X = labelPosition.X + (labelPosition.Width - valueTextSize.Width) / 2f;
5517                 }
5518                 else if (format.Alignment == StringAlignment.Far)
5519                 {
5520                     labelBackPosition.X = labelPosition.Right - valueTextSize.Width - spacing / 2f;
5521                 }
5522 
5523                 // Draw label text
5524                 using (Brush brush = new SolidBrush(point.LabelForeColor))
5525                 {
5526                     graph.DrawPointLabelStringRel(
5527                         graph.Common,
5528                         text,
5529                         point.Font,
5530                         brush,
5531                         labelPosition,
5532                         format,
5533                         0,
5534                         labelBackPosition,
5535                         point.LabelBackColor,
5536                         point.LabelBorderColor,
5537                         point.LabelBorderWidth,
5538                         point.LabelBorderDashStyle,
5539                         point.series,
5540                         point,
5541                         pointIndex);
5542                 }
5543             }
5544 		}
5545 
5546 		/// <summary>
5547 		/// This method draws inside labels.
5548 		/// </summary>
5549 		/// <param name="graph">Chart Graphics object</param>
5550 		/// <param name="points">Important pie points</param>
5551 		/// <param name="point">Data point</param>
5552 		/// <param name="pointIndex">Data point index</param>
Draw3DInsideLabels( ChartGraphics graph, PointF [] points, DataPoint point, int pointIndex )5553 		private void Draw3DInsideLabels( ChartGraphics graph, PointF [] points, DataPoint point, int pointIndex )
5554 		{
5555 			// Set String Alignment
5556 			StringFormat format = new StringFormat();
5557 			format.LineAlignment = StringAlignment.Center;
5558 			format.Alignment = StringAlignment.Center;
5559 
5560 			// Take label text
5561 			string text = GetLabelText( point );
5562 
5563 			// Get label relative position
5564 			PointF labelPosition = graph.GetRelativePoint(points[(int)PiePoints.TopLabelCenter]);
5565 
5566 			// Measure string
5567 			SizeF sizeFont = graph.GetRelativeSize(
5568 				graph.MeasureString(
5569 				text.Replace("\\n", "\n"),
5570 				point.Font,
5571 				new SizeF(1000f, 1000f),
5572 				new StringFormat(StringFormat.GenericTypographic)));
5573 
5574 			// Get label background position
5575 			RectangleF labelBackPosition = RectangleF.Empty;
5576 			SizeF sizeLabel = new SizeF(sizeFont.Width, sizeFont.Height);
5577 			sizeLabel.Height += sizeFont.Height / 8;
5578 			sizeLabel.Width += sizeLabel.Width / text.Length;
5579 			labelBackPosition = new RectangleF(
5580 				labelPosition.X - sizeLabel.Width/2,
5581 				labelPosition.Y - sizeLabel.Height/2  - sizeFont.Height / 10,
5582 				sizeLabel.Width,
5583 				sizeLabel.Height);
5584 
5585 			// Draw label text
5586             using (Brush brush = new SolidBrush(point.LabelForeColor))
5587             {
5588                 graph.DrawPointLabelStringRel(
5589                     graph.Common,
5590                     text,
5591                     point.Font,
5592                     brush,
5593                     labelPosition,
5594                     format,
5595                     0,
5596                     labelBackPosition,
5597                     point.LabelBackColor,
5598                     point.LabelBorderColor,
5599                     point.LabelBorderWidth,
5600                     point.LabelBorderDashStyle,
5601                     point.series,
5602                     point,
5603                     pointIndex);
5604             }
5605 		}
5606 
5607         /// <summary>
5608         /// Gets the point label.
5609         /// </summary>
5610         /// <param name="point">The point.</param>
5611         /// <returns></returns>
GetPointLabel(DataPoint point)5612         private String GetPointLabel(DataPoint point)
5613         {
5614             String pointLabel = String.Empty;
5615 
5616             // If There is no Label take axis Label
5617 			if( point.Label.Length == 0 )
5618             {
5619                 pointLabel = point.AxisLabel;
5620                 // remove axis label if is set the CustomPropertyName.PieAutoAxisLabels and is set to false
5621                 if (point.series != null &&
5622                     point.series.IsCustomPropertySet(CustomPropertyName.PieAutoAxisLabels) &&
5623                     String.Equals(point.series.GetCustomProperty(CustomPropertyName.PieAutoAxisLabels), "false", StringComparison.OrdinalIgnoreCase))
5624                 {
5625                     pointLabel = String.Empty;
5626                 }
5627             }
5628 			else
5629 				pointLabel = point.Label;
5630 
5631             return point.ReplaceKeywords(pointLabel);
5632         }
5633 
5634 		/// <summary>
5635 		/// Take formated text from label or axis label
5636 		/// </summary>
5637 		/// <param name="point">Data point which is used.</param>
5638 		/// <returns>Formated text</returns>
GetLabelText( DataPoint point )5639 		private string GetLabelText( DataPoint point )
5640 		{
5641             string pointLabel = this.GetPointLabel(point);
5642 			// Get label text
5643 			string text;
5644 			if( point.Label.Length == 0 && point.IsValueShownAsLabel )
5645 			{
5646 				text = ValueConverter.FormatValue(
5647 					point.series.Chart,
5648 					point,
5649                     point.Tag,
5650 					point.YValues[0],
5651 					point.LabelFormat,
5652 					point.series.YValueType,
5653 					ChartElementType.DataPoint);
5654 			}
5655 			else
5656 			{
5657 				text = pointLabel;
5658 			}
5659 
5660 			// Retuen formated label or axis label text
5661 			return text;
5662 		}
5663 
5664 
5665 		#endregion
5666 
5667 		#region SmartLabelStyle methods
5668 
5669 		/// <summary>
5670 		/// Adds markers position to the list. Used to check SmartLabelStyle overlapping.
5671 		/// </summary>
5672 		/// <param name="common">Common chart elements.</param>
5673 		/// <param name="area">Chart area.</param>
5674 		/// <param name="series">Series values to be used.</param>
5675 		/// <param name="list">List to add to.</param>
AddSmartLabelMarkerPositions(CommonElements common, ChartArea area, Series series, ArrayList list)5676 		public void AddSmartLabelMarkerPositions(CommonElements common, ChartArea area, Series series, ArrayList list)
5677 		{
5678 		}
5679 
5680 		#endregion
5681 
5682         #region IDisposable interface implementation
5683         /// <summary>
5684         /// Releases unmanaged and - optionally - managed resources
5685         /// </summary>
5686         /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
Dispose(bool disposing)5687         protected virtual void Dispose(bool disposing)
5688         {
5689             //Nothing to dispose at the base class.
5690         }
5691 
5692         /// <summary>
5693         /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
5694         /// </summary>
Dispose()5695         public void Dispose()
5696         {
5697             Dispose(true);
5698             GC.SuppressFinalize(this);
5699         }
5700         #endregion
5701     }
5702 }
5703 
5704