Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Web.DataVisualization / Common / ChartTypes / StackedAreaChart.cs
bloba20586e673f041534fbdd66f449b98fefa94807e
1 //-------------------------------------------------------------
2 // <copyright company=’Microsoft Corporation’>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 // File: StackedAreaChart.cs
9 //
10 // Namespace: DataVisualization.Charting.ChartTypes
12 // Classes: StackedAreaChart, HundredPercentStackedAreaChart
14 // Purpose: Stacked area and hundred percent stacked area charts.
16 // Reviewed: AG - Aug 6, 2002
17 // AG - Microsoft 7, 2007
19 //===================================================================
21 #region Used namespaces
23 using System;
24 using System.Resources;
25 using System.Reflection;
26 using System.Collections;
27 using System.Drawing;
28 using System.Drawing.Drawing2D;
30 #if Microsoft_CONTROL
31 using System.Windows.Forms.DataVisualization.Charting;
32 using System.Windows.Forms.DataVisualization.Charting.Data;
33 using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
34 using System.Windows.Forms.DataVisualization.Charting.Utilities;
35 using System.Windows.Forms.DataVisualization.Charting.Borders3D;
37 #else
38 using System.Web.UI.DataVisualization.Charting;
40 using System.Web.UI.DataVisualization.Charting.ChartTypes;
41 using System.Web.UI.DataVisualization.Charting.Data;
42 using System.Web.UI.DataVisualization.Charting.Utilities;
43 #endif
45 #endregion
47 #if Microsoft_CONTROL
48 namespace System.Windows.Forms.DataVisualization.Charting.ChartTypes
49 #else
50 namespace System.Web.UI.DataVisualization.Charting.ChartTypes
51 #endif
53 /// <summary>
54 /// HundredPercentStackedAreaChart class extends StackedAreaChart class
55 /// by providing its own algorithm for calculating series data point
56 /// Y values. It makes sure that total Y value of all data points in a
57 /// single cluster from all series adds up to 100%.
58 /// </summary>
59 internal class HundredPercentStackedAreaChart : StackedAreaChart
61 #region Constructor
63 /// <summary>
64 /// Default constructor.
65 /// </summary>
66 public HundredPercentStackedAreaChart()
68 hundredPercentStacked = true;
71 #endregion
73 #region Fields
75 // Array of total points values
76 double[] _totalPerPoint = null;
77 int _seriesCount = -1;
78 #endregion
80 #region IChartType interface implementation
82 /// <summary>
83 /// Chart type name
84 /// </summary>
85 override public string Name { get{ return ChartTypeNames.OneHundredPercentStackedArea;}}
87 /// <summary>
88 /// Indicates that it's a hundredred percent chart.
89 /// Axis scale from 0 to 100 percent should be used.
90 /// </summary>
91 override public bool HundredPercent{ get{return true;} }
93 #endregion
95 #region Painting and Selection methods
97 /// <summary>
98 /// Paint HundredPercentStackedAreaChart Chart
99 /// </summary>
100 /// <param name="graph">The Chart Graphics object</param>
101 /// <param name="common">The Common elements object</param>
102 /// <param name="area">Chart area for this chart</param>
103 /// <param name="seriesToDraw">Chart series to draw.</param>
104 override public void Paint( ChartGraphics graph, CommonElements common, ChartArea area, Series seriesToDraw )
106 this.Common = common;
107 // Reset total per point value
108 _totalPerPoint = null;
109 _seriesCount = -1;
110 // Call base class implementation
111 base.Paint( graph, common, area, seriesToDraw );
114 #endregion
116 #region Y values related methods
117 /// <summary>
118 /// Returns series count of same type for given chart area.
119 /// </summary>
120 /// <param name="common">The common elements</param>
121 /// <param name="area">The chart area to inspect</param>
122 /// <returns>Series count of same type</returns>
123 private int GetSeriesCount(CommonElements common, ChartArea area)
125 if (_seriesCount == -1)
127 // Get number of series
128 int seriesCount = 0;
129 foreach (Series ser in common.DataManager.Series)
131 // Use series of the same type which belong to this area
132 if (String.Compare(ser.ChartTypeName, Name, true, System.Globalization.CultureInfo.CurrentCulture) == 0
133 && ser.ChartArea == area.Name && ser.IsVisible())
135 ++seriesCount;
138 _seriesCount = seriesCount;
140 return _seriesCount;
143 /// <summary>
144 /// Helper function, which returns the Y value of the point
145 /// </summary>
146 /// <param name="common">Chart common elements.</param>
147 /// <param name="area">Chart area the series belongs to.</param>
148 /// <param name="series">Sereis of the point.</param>
149 /// <param name="point">Point object.</param>
150 /// <param name="pointIndex">Index of the point.</param>
151 /// <param name="yValueIndex">Index of the Y value to get.</param>
152 /// <returns>Y value of the point.</returns>
153 override public double GetYValue(CommonElements common, ChartArea area, Series series, DataPoint point, int pointIndex, int yValueIndex)
156 // Calculate the totals of all Y values for all series
157 if(_totalPerPoint == null)
159 // Get number of series
160 int seriesCount = GetSeriesCount(common, area);
161 // Fill array of series with this type, which are drawn on this area
162 Series[] seriesArray = new Series[seriesCount];
163 int seriesIndex = 0;
164 foreach( Series ser in common.DataManager.Series )
166 // Use series of the same type which belong to this area
167 if( String.Compare( ser.ChartTypeName, Name, true, System.Globalization.CultureInfo.CurrentCulture ) == 0
168 && ser.ChartArea == area.Name && ser.IsVisible())
170 seriesArray[seriesIndex++] = ser;
174 // Check if series are aligned
175 common.DataManipulator.CheckXValuesAlignment(seriesArray);
177 // Allocate memory for the array
178 _totalPerPoint = new double[series.Points.Count];
180 // Calculate the total of Y value per point
181 for(int index = 0; index < series.Points.Count; index++)
183 _totalPerPoint[index] = 0;
184 foreach( Series ser in seriesArray )
186 _totalPerPoint[index] += Math.Abs(ser.Points[index].YValues[0]);
191 // NOTE: In stacked area chart we need to do processing even if Y value is not set
192 // if(point.YValues[0] == 0 || point.IsEmpty)
193 // {
194 // return 0;
195 // }
197 // Calculate stacked area Y value for 2D chart
198 if(area.Area3DStyle.Enable3D == false)
200 if (_totalPerPoint[pointIndex] == 0)
202 // Get number of series
203 int seriesCount = GetSeriesCount(common, area);
204 return 100.0 / seriesCount;
206 return (point.YValues[0] / _totalPerPoint[pointIndex]) * 100.0;
209 // Get point Height if pointIndex == -1
210 double yValue = double.NaN;
211 if(yValueIndex == -1)
213 Axis vAxis = area.GetAxis(AxisName.Y, series.YAxisType, series.YSubAxisName);
214 double areaZeroValue = vAxis.Crossing;
215 yValue = GetYValue(common, area, series, point, pointIndex, 0);
216 if(area.Area3DStyle.Enable3D && yValue < 0.0)
218 // No negative values support in 3D stacked area chart
219 yValue = -yValue;
221 if( yValue >= 0 )
223 if(!double.IsNaN(prevPosY))
225 areaZeroValue = prevPosY;
228 else
230 if(!double.IsNaN(prevNegY))
232 areaZeroValue = prevNegY;
236 return yValue - areaZeroValue;
240 // Loop through all series
241 prevPosY = double.NaN;
242 prevNegY = double.NaN;
243 prevPositionX = double.NaN;
244 foreach(Series ser in common.DataManager.Series)
246 // Check series of the current chart type & area
247 if(String.Compare(series.ChartArea, ser.ChartArea, true, System.Globalization.CultureInfo.CurrentCulture) == 0 &&
248 String.Compare(series.ChartTypeName, ser.ChartTypeName, true, System.Globalization.CultureInfo.CurrentCulture) == 0 &&
249 series.IsVisible())
251 yValue = (ser.Points[pointIndex].YValues[0] / _totalPerPoint[pointIndex]) * 100.0;
253 // Fix of bug #677411 - Dev10 3D stacked area throws an exception when casting NaN to decimal
254 if (double.IsNaN(yValue) && _totalPerPoint[pointIndex] == 0)
256 yValue = 100.0 / GetSeriesCount(common, area);
259 if(!double.IsNaN(yValue))
260 if(area.Area3DStyle.Enable3D && yValue < 0.0)
262 // No negative values support in 3D stacked area chart
263 yValue = -yValue;
266 if(yValue >= 0.0 && !double.IsNaN(prevPosY))
268 yValue += prevPosY;
270 if(yValue < 0.0 && !double.IsNaN(prevNegY))
272 yValue += prevNegY;
276 // Exit loop when current series was found
277 if (String.Compare(series.Name, ser.Name, StringComparison.Ordinal) == 0)
279 break;
282 // Remenber privious position
283 if(yValue >= 0.0)
285 prevPosY = yValue;
287 else
289 prevNegY = yValue;
291 prevPositionX = ser.Points[pointIndex].XValue;
292 if(prevPositionX == 0.0 && ChartHelper.IndexedSeries(series))
294 prevPositionX = pointIndex + 1;
299 // Y value can't be more than a 100%
300 if(yValue > 100.0)
302 return 100.0;
305 return yValue;
308 #endregion
311 /// <summary>
312 /// StackedAreaChart class extends AreaChart so that chart series are
313 /// positioned on top of each other.
314 /// </summary>
315 internal class StackedAreaChart : AreaChart
317 #region Fields
319 /// <summary>
320 /// Shape of the previous series
321 /// </summary>
322 protected GraphicsPath areaBottomPath = new GraphicsPath();
324 /// <summary>
325 /// Previous stacked positive Y values.
326 /// </summary>
327 protected double prevPosY = double.NaN;
329 /// <summary>
330 /// Previous stacked negative Y values.
331 /// </summary>
332 protected double prevNegY = double.NaN;
334 /// <summary>
335 /// Previous X value.
336 /// </summary>
337 protected double prevPositionX = double.NaN;
339 /// <summary>
340 /// Indicates if chart is 100% stacked
341 /// </summary>
342 protected bool hundredPercentStacked = false;
344 #endregion
346 #region Constructor
348 /// <summary>
349 /// Public constructor.
350 /// </summary>
351 public StackedAreaChart()
353 multiSeries = true;
354 COPCoordinatesToCheck = COPCoordinates.X | COPCoordinates.Y;
357 #endregion
359 #region Default tension method
361 /// <summary>
362 /// Gets default line tension.
363 /// </summary>
364 /// <returns>Line tension.</returns>
365 override protected float GetDefaultTension()
367 return 0f;
370 #endregion
372 #region IChartType interface implementation
374 /// <summary>
375 /// Chart type name
376 /// </summary>
377 public override string Name { get{ return ChartTypeNames.StackedArea;}}
379 /// <summary>
380 /// True if chart type is stacked
381 /// </summary>
382 public override bool Stacked { get{ return true;}}
384 /// <summary>
385 /// Gets chart type image.
386 /// </summary>
387 /// <param name="registry">Chart types registry object.</param>
388 /// <returns>Chart type image.</returns>
389 override public System.Drawing.Image GetImage(ChartTypeRegistry registry)
391 return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
394 #endregion
396 #region Painting and Selection methods
398 /// <summary>
399 /// Paint Stacked Area Chart.
400 /// </summary>
401 /// <param name="graph">The Chart Graphics object.</param>
402 /// <param name="common">The Common elements object.</param>
403 /// <param name="area">Chart area for this chart.</param>
404 /// <param name="seriesToDraw">Chart series to draw.</param>
405 public override void Paint( ChartGraphics graph, CommonElements common, ChartArea area, Series seriesToDraw )
407 this.Common = common;
408 // Set Clip Region
409 graph.SetClip( area.PlotAreaPosition.ToRectangleF() );
411 // Draw chart
412 ProcessChartType( false, graph, common, area, seriesToDraw );
414 // Reset Clip Region
415 ((ChartGraphics)graph).ResetClip();
418 /// <summary>
419 /// This method calculates position of the area and either draws it or checks selection.
420 /// </summary>
421 /// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
422 /// <param name="graph">The Chart Graphics object.</param>
423 /// <param name="common">The Common elements object.</param>
424 /// <param name="area">Chart area for this chart.</param>
425 /// <param name="seriesToDraw">Chart series to draw.</param>
426 override protected void ProcessChartType(
427 bool selection,
428 ChartGraphics graph,
429 CommonElements common,
430 ChartArea area,
431 Series seriesToDraw)
433 this.Common = common;
434 ArrayList prevPointsArray = null;
435 ArrayList curentPointsArray = null;
437 // Prosess 3D chart type
438 if(area.Area3DStyle.Enable3D)
440 base.ProcessChartType(
441 selection,
442 graph,
443 common,
444 area,
445 seriesToDraw);
446 return;
449 // Zero X values mode.
450 bool indexedSeries = ChartHelper.IndexedSeries(this.Common, area.GetSeriesFromChartType(this.Name).ToArray() );
452 // Indicates that the second point loop for drawing lines or labels is required
453 bool requiresSecondPointLoop = false;
454 bool requiresThirdPointLoop = false;
456 //************************************************************
457 //** Loop through all series
458 //************************************************************
459 int seriesPointsNumber = -1;
460 foreach( Series ser in common.DataManager.Series )
462 // Process non empty series of the area with area chart type
463 if( String.Compare( ser.ChartTypeName, this.Name, StringComparison.OrdinalIgnoreCase ) != 0
464 || ser.ChartArea != area.Name || !ser.IsVisible())
466 continue;
469 // Reset area shape paths
470 if(areaPath != null)
472 areaPath.Dispose();
473 areaPath = null;
475 areaBottomPath.Reset();
477 // Check that all seres has the same number of points
478 if(seriesPointsNumber == -1)
480 seriesPointsNumber = ser.Points.Count;
482 else if(seriesPointsNumber != ser.Points.Count)
484 throw (new ArgumentException(SR.ExceptionStackedAreaChartSeriesDataPointsNumberMismatch));
487 // Set active horizontal/vertical axis
488 HAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
489 VAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
490 hAxisMin = HAxis.ViewMinimum;
491 hAxisMax = HAxis.ViewMaximum;
492 vAxisMin = VAxis.ViewMinimum;
493 vAxisMax = VAxis.ViewMaximum;
496 // Get axis position
497 axisPos.X = (float)VAxis.GetPosition(this.VAxis.Crossing);
498 axisPos.Y = (float)VAxis.GetPosition(this.VAxis.Crossing);
499 axisPos = graph.GetAbsolutePoint(axisPos);
501 // Fill previous series values array
502 if(curentPointsArray == null)
504 curentPointsArray = new ArrayList(ser.Points.Count);
506 else
508 prevPointsArray = curentPointsArray;
509 curentPointsArray = new ArrayList(ser.Points.Count);
512 // Call Back Paint event
513 if( !selection )
515 common.Chart.CallOnPrePaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
518 // The data points loop
519 int index = 0;
520 float prevYValue1 = axisPos.Y;
521 float prevYValue2 = axisPos.Y;
522 PointF firstPoint = PointF.Empty;
523 PointF secondPoint = PointF.Empty;
524 foreach( DataPoint point in ser.Points )
526 // Reset pre-calculated point position
527 point.positionRel = new PointF(float.NaN, float.NaN);
529 // Get point value
530 double yValue = (point.IsEmpty) ? 0.0 : GetYValue(common, area, ser, point, index, 0);
531 double xValue = (indexedSeries) ? (index + 1.0) : point.XValue;
533 // Adjust point position with previous value
534 if(prevPointsArray != null && index < prevPointsArray.Count)
536 yValue += (double)prevPointsArray[index];
538 curentPointsArray.Insert(index, yValue);
540 // Get point position
541 float yPosition = (float)VAxis.GetPosition(yValue);
542 float xPosition = (float)HAxis.GetPosition(xValue);
544 // Remeber pre-calculated point position
545 point.positionRel = new PointF(xPosition, yPosition);
547 yValue = VAxis.GetLogValue(yValue);
548 xValue = HAxis.GetLogValue(xValue);
550 // Calculate 2 points to draw area and line
551 if(firstPoint == PointF.Empty)
553 firstPoint.X = xPosition;
554 firstPoint.Y = yPosition;
555 if(prevPointsArray != null && index < prevPointsArray.Count)
557 prevYValue1 = (float)VAxis.GetPosition((double)prevPointsArray[index]);
558 prevYValue1 = graph.GetAbsolutePoint(new PointF(prevYValue1, prevYValue1)).Y;
560 firstPoint = graph.GetAbsolutePoint(firstPoint);
562 ++index;
563 continue;
565 else
567 secondPoint.X = xPosition;
568 secondPoint.Y = yPosition;
569 if(prevPointsArray != null && index < prevPointsArray.Count)
571 prevYValue2 = (float)VAxis.GetPosition((double)prevPointsArray[index]);
572 prevYValue2 = graph.GetAbsolutePoint(new PointF(prevYValue2, prevYValue2)).Y;
574 secondPoint = graph.GetAbsolutePoint(secondPoint);
577 // Round X coordinates
578 firstPoint.X = (float)Math.Round(firstPoint.X);
579 secondPoint.X = (float)Math.Round(secondPoint.X);
582 // Calculate data point area segment path
583 using (GraphicsPath path = new GraphicsPath())
585 path.AddLine(firstPoint.X, firstPoint.Y, secondPoint.X, secondPoint.Y);
586 path.AddLine(secondPoint.X, secondPoint.Y, secondPoint.X, prevYValue2);
587 path.AddLine(secondPoint.X, prevYValue2, firstPoint.X, prevYValue1);
588 path.AddLine(firstPoint.X, prevYValue1, firstPoint.X, firstPoint.Y);
590 // Painting mode
591 if (common.ProcessModePaint)
593 // Get previous point value
594 double xPrevValue = (indexedSeries) ? (index) : ser.Points[index - 1].XValue;
596 // Check if line is completly out of the data scaleView
597 if ((xValue <= hAxisMin && xPrevValue <= hAxisMin) ||
598 (xValue >= hAxisMax && xPrevValue >= hAxisMax))
600 // Save previous point
601 firstPoint = secondPoint;
602 prevYValue1 = prevYValue2;
604 // Increase data point index
605 ++index;
607 continue;
610 // Create area brush
611 Brush areaBrush = null;
612 if (point.BackHatchStyle != ChartHatchStyle.None)
614 areaBrush = graph.GetHatchBrush(point.BackHatchStyle, point.Color, point.BackSecondaryColor);
616 else if (point.BackGradientStyle != GradientStyle.None)
618 this.gradientFill = true;
619 this.Series = point.series;
621 else if (point.BackImage.Length > 0 && point.BackImageWrapMode != ChartImageWrapMode.Unscaled && point.BackImageWrapMode != ChartImageWrapMode.Scaled)
623 areaBrush = graph.GetTextureBrush(point.BackImage, point.BackImageTransparentColor, point.BackImageWrapMode, point.Color);
625 else if (point.IsEmpty && point.Color == Color.Empty)
627 // Stacked area chart empty points should always use
628 // series color, otherwise chart will have empty 'holes'.
629 areaBrush = new SolidBrush(ser.Color);
631 else
633 areaBrush = new SolidBrush(point.Color);
636 // Check if we need second loop to draw area border
637 if ((point.BorderColor != Color.Empty && point.BorderWidth > 0))
639 requiresSecondPointLoop = true;
642 // Check if we need third loop to draw labels
643 if (point.Label.Length > 0 || point.IsValueShownAsLabel)
645 requiresThirdPointLoop = true;
648 // Draw area
649 if (!this.gradientFill)
651 // Start Svg Selection mode
652 graph.StartHotRegion(point);
654 // Turn off anti aliasing and fill area
655 SmoothingMode oldMode = graph.SmoothingMode;
656 graph.SmoothingMode = SmoothingMode.None;
657 graph.FillPath(areaBrush, path);
658 graph.SmoothingMode = oldMode;
660 // Draw top and bottom lines with antialiasing turned On.
661 // Process only if line is drawn by an angle
662 Pen areaLinePen = new Pen(areaBrush, 1);
663 if (!(firstPoint.X == secondPoint.X || firstPoint.Y == secondPoint.Y))
665 graph.DrawLine(areaLinePen, firstPoint.X, firstPoint.Y, secondPoint.X, secondPoint.Y);
667 if (!(firstPoint.X == secondPoint.X || prevYValue2 == prevYValue1))
669 graph.DrawLine(areaLinePen, secondPoint.X, prevYValue2, firstPoint.X, prevYValue1);
672 // End Svg Selection mode
673 graph.EndHotRegion();
676 if (areaPath == null)
678 areaPath = new GraphicsPath();
680 areaPath.AddLine(firstPoint.X, firstPoint.Y, secondPoint.X, secondPoint.Y);
681 areaBottomPath.AddLine(firstPoint.X, prevYValue1, secondPoint.X, prevYValue2);
683 //Clean up
684 if (areaBrush != null)
685 areaBrush.Dispose();
688 if (common.ProcessModeRegions)
690 //**************************************************************
691 //** Add area for the inside of the area
692 //**************************************************************
694 // Allocate array of floats
695 PointF pointNew = PointF.Empty;
696 float[] coord = new float[path.PointCount * 2];
697 PointF[] pathPoints = path.PathPoints;
698 for (int i = 0; i < path.PointCount; i++)
700 pointNew = graph.GetRelativePoint(pathPoints[i]);
701 coord[2 * i] = pointNew.X;
702 coord[2 * i + 1] = pointNew.Y;
705 common.HotRegionsList.AddHotRegion(
706 path,
707 false,
708 coord,
709 point,
710 ser.Name,
711 index);
713 //**************************************************************
714 //** Add area for the top line (with thickness)
715 //**************************************************************
716 if (point.BorderWidth > 1 && point.BorderDashStyle != ChartDashStyle.NotSet && point.BorderColor != Color.Empty)
718 // Create grapics path object dor the curve
719 using (GraphicsPath linePath = new GraphicsPath())
723 linePath.AddLine(firstPoint.X, firstPoint.Y, secondPoint.X, secondPoint.Y);
725 // Widen the lines to the size of pen plus 2
726 linePath.Widen(new Pen(point.Color, point.BorderWidth + 2));
728 catch (OutOfMemoryException)
730 // GraphicsPath.Widen incorrectly throws OutOfMemoryException
731 // catching here and reacting by not widening
733 catch (ArgumentException)
737 // Allocate array of floats
738 pointNew = PointF.Empty;
739 coord = new float[linePath.PointCount * 2];
740 for (int i = 0; i < linePath.PointCount; i++)
742 pointNew = graph.GetRelativePoint(linePath.PathPoints[i]);
743 coord[2 * i] = pointNew.X;
744 coord[2 * i + 1] = pointNew.Y;
747 common.HotRegionsList.AddHotRegion(
748 linePath,
749 false,
750 coord,
751 point,
752 ser.Name,
753 index);
758 // Save previous point
759 firstPoint = secondPoint;
760 prevYValue1 = prevYValue2;
762 // Increase data point index
763 ++index;
767 // Fill whole series area with gradient
768 if(gradientFill && areaPath != null)
770 // Create gradient path
771 using (GraphicsPath gradientPath = new GraphicsPath())
773 gradientPath.AddPath(areaPath, true);
774 areaBottomPath.Reverse();
775 gradientPath.AddPath(areaBottomPath, true);
777 // Create brush
778 using (Brush areaBrush = graph.GetGradientBrush(gradientPath.GetBounds(), this.Series.Color, this.Series.BackSecondaryColor, this.Series.BackGradientStyle))
780 // Fill area with gradient
781 graph.FillPath(areaBrush, gradientPath);
785 areaPath.Dispose();
786 areaPath = null;
787 gradientFill = false;
789 areaBottomPath.Reset();
791 // Call Paint event
792 if( !selection )
794 common.Chart.CallOnPostPaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
798 //************************************************************
799 //** Loop through all series/points for the second time
800 //** Draw border lines.
801 //************************************************************
802 if(requiresSecondPointLoop)
804 prevPointsArray = null;
805 curentPointsArray = null;
806 foreach( Series ser in common.DataManager.Series )
808 if( String.Compare( ser.ChartTypeName, this.Name, StringComparison.OrdinalIgnoreCase ) != 0
809 || ser.ChartArea != area.Name || !ser.IsVisible())
811 continue;
814 // Set active horizontal/vertical axis
815 HAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
816 VAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
818 // Get axis position
819 axisPos.X = (float)VAxis.GetPosition(this.VAxis.Crossing);
820 axisPos.Y = (float)VAxis.GetPosition(this.VAxis.Crossing);
821 axisPos = graph.GetAbsolutePoint(axisPos);
823 // Fill previous series values array
824 if(curentPointsArray == null)
826 curentPointsArray = new ArrayList(ser.Points.Count);
828 else
830 prevPointsArray = curentPointsArray;
831 curentPointsArray = new ArrayList(ser.Points.Count);
834 // The data points loop
835 int index = 0;
836 float prevYValue1 = axisPos.Y;
837 float prevYValue2 = axisPos.Y;
838 PointF firstPoint = PointF.Empty;
839 PointF secondPoint = PointF.Empty;
840 foreach( DataPoint point in ser.Points )
842 // Get point value
843 double yValue = (point.IsEmpty) ? 0.0 : GetYValue(common, area, ser, point, index, 0);
844 double xValue = (indexedSeries) ? (index + 1.0) : point.XValue;
846 // Adjust point position with previous value
847 if(prevPointsArray != null && index < prevPointsArray.Count)
849 yValue += (double)prevPointsArray[index];
851 curentPointsArray.Insert(index, yValue);
853 // Get point position
854 float yPosition = (float)VAxis.GetPosition(yValue);
855 float xPosition = (float)HAxis.GetPosition(xValue);
858 // Calculate 2 points to draw area and line
859 if(firstPoint == PointF.Empty)
861 firstPoint.X = xPosition;
862 firstPoint.Y = yPosition;
863 if(prevPointsArray != null && index < prevPointsArray.Count)
865 prevYValue1 = (float)VAxis.GetPosition((double)prevPointsArray[index]);
866 prevYValue1 = graph.GetAbsolutePoint(new PointF(prevYValue1, prevYValue1)).Y;
868 firstPoint = graph.GetAbsolutePoint(firstPoint);
869 secondPoint = firstPoint;
870 prevYValue2 = prevYValue1;
872 else
874 secondPoint.X = xPosition;
875 secondPoint.Y = yPosition;
876 if(prevPointsArray != null && index < prevPointsArray.Count)
878 prevYValue2 = (float)VAxis.GetPosition((double)prevPointsArray[index]);
879 prevYValue2 = graph.GetAbsolutePoint(new PointF(prevYValue2, prevYValue2)).Y;
881 secondPoint = graph.GetAbsolutePoint(secondPoint);
884 if(index != 0)
886 // Round X coordinates
887 firstPoint.X = (float)Math.Round(firstPoint.X);
888 secondPoint.X = (float)Math.Round(secondPoint.X);
890 // Draw border
891 graph.DrawLineRel(point.BorderColor, point.BorderWidth, point.BorderDashStyle, graph.GetRelativePoint(firstPoint), graph.GetRelativePoint(secondPoint), point.series.ShadowColor, point.series.ShadowOffset );
894 // Save previous point
895 firstPoint = secondPoint;
896 prevYValue1 = prevYValue2;
898 // Increase data point index
899 ++index;
904 //************************************************************
905 //** Loop through all series/points for the second time
906 //** Draw labels.
907 //************************************************************
908 if(requiresThirdPointLoop)
910 prevPointsArray = null;
911 curentPointsArray = null;
912 foreach( Series ser in common.DataManager.Series )
914 if( String.Compare( ser.ChartTypeName, this.Name, StringComparison.OrdinalIgnoreCase ) != 0
915 || ser.ChartArea != area.Name || !ser.IsVisible())
917 continue;
920 // Set active horizontal/vertical axis
921 HAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
922 VAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
924 // Get axis position
925 axisPos.X = (float)VAxis.GetPosition(this.VAxis.Crossing);
926 axisPos.Y = (float)VAxis.GetPosition(this.VAxis.Crossing);
927 axisPos = graph.GetAbsolutePoint(axisPos);
929 // Fill previous series values array
930 if(curentPointsArray == null)
932 curentPointsArray = new ArrayList(ser.Points.Count);
934 else
936 prevPointsArray = curentPointsArray;
937 curentPointsArray = new ArrayList(ser.Points.Count);
940 // The data points loop
941 int index = 0;
942 float prevYValue1 = axisPos.Y;
943 float prevYValue2 = axisPos.Y;
944 PointF firstPoint = PointF.Empty;
945 PointF secondPoint = PointF.Empty;
946 foreach( DataPoint point in ser.Points )
948 // Get point value
949 double yValue = (point.IsEmpty) ? 0.0 : GetYValue(common, area, ser, point, index, 0);
950 double xValue = (indexedSeries) ? (index + 1.0) : point.XValue;
952 // Adjust point position with previous value
953 if(prevPointsArray != null && index < prevPointsArray.Count)
955 yValue += (double)prevPointsArray[index];
957 curentPointsArray.Insert(index, yValue);
959 // Get point position
960 float yPosition = (float)VAxis.GetPosition(yValue);
961 float xPosition = (float)HAxis.GetPosition(xValue);
964 // Calculate 2 points to draw area and line
965 if(firstPoint == PointF.Empty)
967 firstPoint.X = xPosition;
968 firstPoint.Y = yPosition;
969 if(prevPointsArray != null && index < prevPointsArray.Count)
971 prevYValue1 = (float)VAxis.GetPosition((double)prevPointsArray[index]);
972 prevYValue1 = graph.GetAbsolutePoint(new PointF(prevYValue1, prevYValue1)).Y;
974 firstPoint = graph.GetAbsolutePoint(firstPoint);
975 secondPoint = firstPoint;
976 prevYValue2 = prevYValue1;
978 else
980 secondPoint.X = xPosition;
981 secondPoint.Y = yPosition;
982 if(prevPointsArray != null && index < prevPointsArray.Count)
984 prevYValue2 = (float)VAxis.GetPosition((double)prevPointsArray[index]);
985 prevYValue2 = graph.GetAbsolutePoint(new PointF(prevYValue2, prevYValue2)).Y;
987 secondPoint = graph.GetAbsolutePoint(secondPoint);
990 if(!point.IsEmpty && (ser.IsValueShownAsLabel || point.IsValueShownAsLabel || point.Label.Length > 0))
992 // Label text format
993 using (StringFormat format = new StringFormat())
995 format.Alignment = StringAlignment.Center;
996 format.LineAlignment = StringAlignment.Center;
998 // Get label text
999 string text;
1000 if (point.Label.Length == 0)
1002 double pointLabelValue = GetYValue(common, area, ser, point, index, 0);
1003 // Round Y values for 100% stacked area
1004 if (this.hundredPercentStacked && point.LabelFormat.Length == 0)
1006 pointLabelValue = Math.Round(pointLabelValue, 2);
1008 text = ValueConverter.FormatValue(
1009 ser.Chart,
1010 point,
1011 point.Tag,
1012 pointLabelValue,
1013 point.LabelFormat,
1014 ser.YValueType,
1015 ChartElementType.DataPoint);
1017 else
1019 text = point.ReplaceKeywords(point.Label);
1022 // Disable the clip region
1023 Region oldClipRegion = graph.Clip;
1024 graph.Clip = new Region();
1026 // Draw label
1027 PointF labelPosition = PointF.Empty;
1028 labelPosition.X = secondPoint.X;
1029 labelPosition.Y = secondPoint.Y - (secondPoint.Y - prevYValue2) / 2f;
1030 labelPosition = graph.GetRelativePoint(labelPosition);
1032 // Measure string
1033 SizeF sizeFont = graph.GetRelativeSize(
1034 graph.MeasureString(
1035 text,
1036 point.Font,
1037 new SizeF(1000f, 1000f),
1038 StringFormat.GenericTypographic));
1040 // Get label background position
1041 RectangleF labelBackPosition = RectangleF.Empty;
1042 SizeF sizeLabel = new SizeF(sizeFont.Width, sizeFont.Height);
1043 sizeLabel.Height += sizeFont.Height / 8;
1044 sizeLabel.Width += sizeLabel.Width / text.Length;
1045 labelBackPosition = new RectangleF(
1046 labelPosition.X - sizeLabel.Width / 2,
1047 labelPosition.Y - sizeLabel.Height / 2 - sizeFont.Height / 10,
1048 sizeLabel.Width,
1049 sizeLabel.Height);
1051 // Draw label text
1052 using (Brush brush = new SolidBrush(point.LabelForeColor))
1054 graph.DrawPointLabelStringRel(
1055 common,
1056 text,
1057 point.Font,
1058 brush,
1059 labelPosition,
1060 format,
1061 point.LabelAngle,
1062 labelBackPosition,
1063 point.LabelBackColor,
1064 point.LabelBorderColor,
1065 point.LabelBorderWidth,
1066 point.LabelBorderDashStyle,
1067 ser,
1068 point,
1069 index);
1072 // Restore old clip region
1073 graph.Clip = oldClipRegion;
1078 // Save previous point
1079 firstPoint = secondPoint;
1080 prevYValue1 = prevYValue2;
1082 // Increase data point index
1083 ++index;
1090 #endregion
1092 #region 3D Drawing and selection methods
1094 /// <summary>
1095 /// Draws a 3D surface connecting the two specified points in 2D space.
1096 /// Used to draw Line based charts.
1097 /// </summary>
1098 /// <param name="area">Chart area reference.</param>
1099 /// <param name="graph">Chart graphics.</param>
1100 /// <param name="matrix">Coordinates transformation matrix.</param>
1101 /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
1102 /// <param name="prevDataPointEx">Previous data point object.</param>
1103 /// <param name="positionZ">Z position of the back side of the 3D surface.</param>
1104 /// <param name="depth">Depth of the 3D surface.</param>
1105 /// <param name="points">Array of points.</param>
1106 /// <param name="pointIndex">Index of point to draw.</param>
1107 /// <param name="pointLoopIndex">Index of points loop.</param>
1108 /// <param name="tension">Line tension.</param>
1109 /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
1110 /// <param name="topDarkening">Darkenning scale for top surface. 0 - None.</param>
1111 /// <param name="bottomDarkening">Darkenning scale for bottom surface. 0 - None.</param>
1112 /// <param name="thirdPointPosition">Position where the third point is actually located or float.NaN if same as in "firstPoint".</param>
1113 /// <param name="fourthPointPosition">Position where the fourth point is actually located or float.NaN if same as in "secondPoint".</param>
1114 /// <param name="clippedSegment">Indicates that drawn segment is 3D clipped. Only top/bottom should be drawn.</param>
1115 /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
1116 protected override GraphicsPath Draw3DSurface(
1117 ChartArea area,
1118 ChartGraphics graph,
1119 Matrix3D matrix,
1120 LightStyle lightStyle,
1121 DataPoint3D prevDataPointEx,
1122 float positionZ,
1123 float depth,
1124 ArrayList points,
1125 int pointIndex,
1126 int pointLoopIndex,
1127 float tension,
1128 DrawingOperationTypes operationType,
1129 float topDarkening,
1130 float bottomDarkening,
1131 PointF thirdPointPosition,
1132 PointF fourthPointPosition,
1133 bool clippedSegment)
1135 // Call base method
1137 if(pointLoopIndex != 2)
1139 return base.Draw3DSurface(
1140 area,
1141 graph,
1142 matrix,
1143 lightStyle,
1144 prevDataPointEx,
1145 positionZ,
1146 depth,
1147 points,
1148 pointIndex,
1149 pointLoopIndex,
1150 tension,
1151 operationType,
1152 topDarkening,
1153 bottomDarkening,
1154 thirdPointPosition,
1155 fourthPointPosition,
1156 clippedSegment);
1159 // Draw labels in the third loop
1160 else
1162 DataPoint3D pointEx = ((DataPoint3D)points[pointIndex]);
1164 // Draw label for the first point
1165 if(pointEx.index == 2)
1167 // Get point with prev index
1168 int neighborPointIndex = 0;
1169 DataPoint3D pointPrevEx = ChartGraphics.FindPointByIndex(points, pointEx.index - 1, pointEx, ref neighborPointIndex);
1171 // Draw labels in the third loop
1172 DrawLabels3D(
1173 area,
1174 graph,
1175 area.Common,
1176 pointPrevEx,
1177 positionZ,
1178 depth);
1181 // Draw labels in the third loop
1182 DrawLabels3D(
1183 area,
1184 graph,
1185 area.Common,
1186 pointEx,
1187 positionZ,
1188 depth);
1191 return new GraphicsPath();
1194 /// <summary>
1195 /// Gets visibility of the top surface.
1196 /// </summary>
1197 /// <param name="area">Chart area object.</param>
1198 /// <param name="firstPoint">First data point of the line.</param>
1199 /// <param name="secondPoint">Second data point of the line.</param>
1200 /// <param name="upSideDown">Indicates that Y values of the data points are below axis line.</param>
1201 /// <param name="positionZ">Z coordinate of the back side of the cube.</param>
1202 /// <param name="depth">Cube depth.</param>
1203 /// <param name="matrix">Coordinate transformation matrix.</param>
1204 /// <param name="visibleSurfaces">Surface visibility reference. Initialized with bounary cube visibility.</param>
1205 protected override void GetTopSurfaceVisibility(
1206 ChartArea area,
1207 DataPoint3D firstPoint,
1208 DataPoint3D secondPoint,
1209 bool upSideDown,
1210 float positionZ,
1211 float depth,
1212 Matrix3D matrix,
1213 ref SurfaceNames visibleSurfaces)
1215 // Call base class method first
1216 base.GetTopSurfaceVisibility(area, firstPoint, secondPoint, upSideDown,
1217 positionZ, depth, matrix, ref visibleSurfaces);
1219 // Check if the Top surface is overlapped with data point from other series
1220 if( (visibleSurfaces & SurfaceNames.Top) == SurfaceNames.Top )
1222 // Try to find data point with same index from the series above
1223 bool seriesFound = false;
1224 foreach(Series ser in area.Common.DataManager.Series)
1226 if(String.Compare(ser.ChartTypeName, secondPoint.dataPoint.series.ChartTypeName, true, System.Globalization.CultureInfo.CurrentCulture) == 0)
1228 // If series on top of current was found - check point transparency
1229 if(seriesFound)
1231 DataPointCustomProperties pointProperties = ser.Points[secondPoint.index - 1];
1232 if(ser.Points[secondPoint.index - 1].IsEmpty)
1234 pointProperties = ser.EmptyPointStyle;
1236 if (pointProperties.Color.A == 255)
1238 visibleSurfaces ^= SurfaceNames.Top;
1240 break;
1243 // Check series name
1244 if(String.Compare(ser.Name, secondPoint.dataPoint.series.Name, StringComparison.Ordinal) == 0)
1246 seriesFound = true;
1252 // Check if the Bottom surface is on top of the transparent data point from other series
1253 if( (visibleSurfaces & SurfaceNames.Bottom) != SurfaceNames.Bottom )
1255 // Try to find data point with same index from the series above
1256 DataPointCustomProperties pointProperties = null;
1257 foreach(Series ser in area.Common.DataManager.Series)
1259 if(String.Compare(ser.ChartTypeName, secondPoint.dataPoint.series.ChartTypeName, StringComparison.OrdinalIgnoreCase) == 0)
1261 // Check series name
1262 if (pointProperties != null && String.Compare(ser.Name, secondPoint.dataPoint.series.Name, StringComparison.Ordinal) == 0)
1264 if (pointProperties.Color.A != 255)
1266 visibleSurfaces |= SurfaceNames.Bottom;
1268 break;
1271 // Get properties
1272 pointProperties = ser.Points[secondPoint.index - 1];
1273 if(ser.Points[secondPoint.index - 1].IsEmpty)
1275 pointProperties = ser.EmptyPointStyle;
1283 /// <summary>
1284 /// Gets position ob the bottom points in area chart.
1285 /// </summary>
1286 /// <param name="common">Chart common elements.</param>
1287 /// <param name="area">Chart area the series belongs to.</param>
1288 /// <param name="axisPosition">Axis position.</param>
1289 /// <param name="firstPoint">First top point coordinates.</param>
1290 /// <param name="secondPoint">Second top point coordinates.</param>
1291 /// <param name="thirdPointPosition">Position where the third point is actually located or float.NaN if same as in "firstPoint".</param>
1292 /// <param name="fourthPointPosition">Position where the fourth point is actually located or float.NaN if same as in "secondPoint".</param>
1293 /// <param name="thirdPoint">Returns third bottom point coordinates.</param>
1294 /// <param name="fourthPoint">Returns fourth bottom point coordinates.</param>
1295 protected override void GetBottomPointsPosition(
1296 CommonElements common,
1297 ChartArea area,
1298 float axisPosition,
1299 ref DataPoint3D firstPoint,
1300 ref DataPoint3D secondPoint,
1301 PointF thirdPointPosition,
1302 PointF fourthPointPosition,
1303 out PointF thirdPoint,
1304 out PointF fourthPoint)
1306 // Set active vertical/horizontal axis
1307 Axis vAxis = area.GetAxis(AxisName.Y, firstPoint.dataPoint.series.YAxisType, firstPoint.dataPoint.series.YSubAxisName);
1308 Axis hAxis = area.GetAxis(AxisName.X, firstPoint.dataPoint.series.XAxisType, firstPoint.dataPoint.series.XSubAxisName);
1310 // Find bottom points position
1311 double yValue = GetYValue(area.Common, area, firstPoint.dataPoint.series, firstPoint.dataPoint, firstPoint.index - 1, 0);
1312 double xValue = (float)firstPoint.xPosition;
1313 if(yValue >= 0.0)
1315 if(double.IsNaN(this.prevPosY))
1317 yValue = axisPosition;
1319 else
1321 yValue = vAxis.GetPosition(this.prevPosY);
1322 xValue = hAxis.GetPosition(this.prevPositionX);
1325 else
1327 if(double.IsNaN(this.prevNegY))
1329 yValue = axisPosition;
1331 else
1333 yValue = vAxis.GetPosition(this.prevNegY);
1334 xValue = hAxis.GetPosition(this.prevPositionX);
1337 thirdPoint = new PointF((float)xValue, (float)yValue);
1340 yValue = GetYValue(area.Common, area, secondPoint.dataPoint.series, secondPoint.dataPoint, secondPoint.index - 1, 0);
1341 xValue = (float)secondPoint.xPosition;
1342 if(yValue >= 0.0)
1344 if(double.IsNaN(this.prevPosY))
1346 yValue = axisPosition;
1348 else
1350 yValue = vAxis.GetPosition(this.prevPosY);
1351 xValue = hAxis.GetPosition(this.prevPositionX);
1354 else
1356 if(double.IsNaN(this.prevNegY))
1358 yValue = axisPosition;
1360 else
1362 yValue = vAxis.GetPosition(this.prevNegY);
1363 xValue = hAxis.GetPosition(this.prevPositionX);
1366 fourthPoint = new PointF((float)xValue, (float)yValue);
1368 // Check if position of the third and/or fourth point(s) should be adjusted
1369 if(!float.IsNaN(thirdPointPosition.X))
1371 thirdPoint.X = (float)((firstPoint.xCenterVal == 0.0) ? firstPoint.xPosition : firstPoint.xCenterVal);
1373 // Calculate new Y value as an intersection point of two lines:
1374 // line between current 3d & 4th points and vertical line with X value = thirdPointPositionX.
1375 thirdPoint.Y = (thirdPointPosition.X - fourthPoint.X) /
1376 (thirdPoint.X - fourthPoint.X) *
1377 (thirdPoint.Y - fourthPoint.Y) +
1378 fourthPoint.Y;
1380 // Set new X value
1381 thirdPoint.X = thirdPointPosition.X;
1383 if(!float.IsNaN(thirdPointPosition.Y))
1385 thirdPoint.Y = thirdPointPosition.Y;
1388 if(!float.IsNaN(fourthPointPosition.X))
1390 fourthPoint.X = (float)((secondPoint.xCenterVal == 0.0) ? secondPoint.xPosition : secondPoint.xCenterVal);
1392 // Calculate new Y value as an intersection point of two lines:
1393 // line between current 3d & 4th points and vertical line with X value = thirdPointPositionX.
1394 fourthPoint.Y = (fourthPointPosition.X - fourthPoint.X) /
1395 (thirdPoint.X - fourthPoint.X) *
1396 (thirdPoint.Y - fourthPoint.Y) +
1397 fourthPoint.Y;
1399 // Set new X value
1400 fourthPoint.X = fourthPointPosition.X;
1402 if(!float.IsNaN(fourthPointPosition.Y))
1404 fourthPoint.Y = fourthPointPosition.Y;
1409 /// <summary>
1410 /// Returns how many loops through all data points is required (1 or 2)
1411 /// </summary>
1412 /// <param name="selection">Selection indicator.</param>
1413 /// <param name="pointsArray">Points array list.</param>
1414 /// <returns>Number of loops (1 or 2).</returns>
1415 override protected int GetPointLoopNumber(bool selection, ArrayList pointsArray)
1417 // Always one loop for selection
1418 if(selection)
1420 return 1;
1423 // Second loop will be required for semi-transparent colors
1424 int loopNumber = 1;
1425 foreach(object obj in pointsArray)
1427 // Get point & series
1428 DataPoint3D pointEx = (DataPoint3D) obj;
1430 // Check properties
1431 if(pointEx.dataPoint.Color.A != 255)
1433 loopNumber = 2;
1436 // Check title
1437 // VSTS fix #529011: 3-d stacked area and 100% stacked area charts do not show data labels.
1438 if( pointEx.dataPoint.Label.Length > 0 ||
1439 pointEx.dataPoint.IsValueShownAsLabel ||
1440 pointEx.dataPoint.series.IsValueShownAsLabel)
1442 // S loops through all data points required
1443 loopNumber = 3;
1444 break;
1448 return loopNumber;
1451 /// <summary>
1452 /// This method draws labels in point chart.
1453 /// </summary>
1454 /// <param name="graph">The Chart Graphics object.</param>
1455 /// <param name="common">The Common elements object.</param>
1456 /// <param name="area">Chart area for this chart.</param>
1457 /// <param name="pointEx">Data point 3D.</param>
1458 /// <param name="positionZ">Z position of the back side of the 3D surface.</param>
1459 /// <param name="depth">Depth of the 3D surface.</param>
1460 private void DrawLabels3D(
1461 ChartArea area,
1462 ChartGraphics graph,
1463 CommonElements common,
1464 DataPoint3D pointEx,
1465 float positionZ,
1466 float depth)
1468 // Get some properties for performance
1469 string pointLabel = pointEx.dataPoint.Label;
1470 bool pointShowLabelAsValue = pointEx.dataPoint.IsValueShownAsLabel;
1472 // ****************************
1473 // Draw data point value label
1474 // ****************************
1475 if((!pointEx.dataPoint.IsEmpty && (pointEx.dataPoint.series.IsValueShownAsLabel || pointShowLabelAsValue || pointLabel.Length > 0)) ||
1476 (pointShowLabelAsValue || pointLabel.Length > 0))
1478 // Label text format
1479 using (StringFormat format = new StringFormat())
1481 format.Alignment = StringAlignment.Center;
1482 format.LineAlignment = StringAlignment.Center;
1484 // Get label text
1485 string text;
1486 if (pointLabel.Length == 0)
1488 // Round Y values for 100% stacked area
1489 double pointLabelValue = pointEx.dataPoint.YValues[(labelYValueIndex == -1) ? YValueIndex : labelYValueIndex];
1490 if (this.hundredPercentStacked && pointEx.dataPoint.LabelFormat.Length == 0)
1492 pointLabelValue = Math.Round(pointLabelValue, 2);
1495 text = ValueConverter.FormatValue(
1496 pointEx.dataPoint.series.Chart,
1497 pointEx.dataPoint,
1498 pointEx.dataPoint.Tag,
1499 pointLabelValue,
1500 pointEx.dataPoint.LabelFormat,
1501 pointEx.dataPoint.series.YValueType,
1502 ChartElementType.DataPoint);
1504 else
1506 text = pointEx.dataPoint.ReplaceKeywords(pointLabel);
1509 // Get label position
1510 Point3D[] points = new Point3D[1];
1511 points[0] = new Point3D((float)pointEx.xPosition, (float)(pointEx.yPosition + pointEx.height) / 2f, positionZ + depth);
1512 area.matrix3D.TransformPoints(points);
1514 // Measure string
1515 SizeF sizeFont = graph.GetRelativeSize(
1516 graph.MeasureString(
1517 text,
1518 pointEx.dataPoint.Font,
1519 new SizeF(1000f, 1000f),
1520 StringFormat.GenericTypographic));
1522 // Get label background position
1523 RectangleF labelBackPosition = RectangleF.Empty;
1524 SizeF sizeLabel = new SizeF(sizeFont.Width, sizeFont.Height);
1525 sizeLabel.Height += sizeFont.Height / 8;
1526 sizeLabel.Width += sizeLabel.Width / text.Length;
1527 labelBackPosition = new RectangleF(
1528 points[0].PointF.X - sizeLabel.Width / 2,
1529 points[0].PointF.Y - sizeLabel.Height / 2 - sizeFont.Height / 10,
1530 sizeLabel.Width,
1531 sizeLabel.Height);
1533 // Draw label text
1534 using (Brush brush = new SolidBrush(pointEx.dataPoint.LabelForeColor))
1536 graph.DrawPointLabelStringRel(
1537 common,
1538 text,
1539 pointEx.dataPoint.Font,
1540 brush,
1541 points[0].PointF,
1542 format,
1543 pointEx.dataPoint.LabelAngle,
1544 labelBackPosition,
1545 pointEx.dataPoint.LabelBackColor,
1546 pointEx.dataPoint.LabelBorderColor,
1547 pointEx.dataPoint.LabelBorderWidth,
1548 pointEx.dataPoint.LabelBorderDashStyle,
1549 pointEx.dataPoint.series,
1550 pointEx.dataPoint,
1551 pointEx.index - 1);
1557 #endregion
1559 #region Y values methods
1561 /// <summary>
1562 /// Helper function, which returns the Y value of the point.
1563 /// </summary>
1564 /// <param name="common">Chart common elements.</param>
1565 /// <param name="area">Chart area the series belongs to.</param>
1566 /// <param name="series">Sereis of the point.</param>
1567 /// <param name="point">Point object.</param>
1568 /// <param name="pointIndex">Index of the point.</param>
1569 /// <param name="yValueIndex">Index of the Y value to get. Set to -1 to get the height.</param>
1570 /// <returns>Y value of the point.</returns>
1571 override public double GetYValue(
1572 CommonElements common,
1573 ChartArea area,
1574 Series series,
1575 DataPoint point,
1576 int pointIndex,
1577 int yValueIndex)
1579 double yValue = double.NaN;
1581 // Calculate stacked column Y value for 2D chart
1582 if(area.Area3DStyle.Enable3D == false)
1584 return point.YValues[0];
1587 // Get point Height if pointIndex == -1
1588 if(yValueIndex == -1)
1590 Axis vAxis = area.GetAxis(AxisName.Y, series.YAxisType, series.YSubAxisName);
1591 double areaZeroValue = vAxis.Crossing;
1592 yValue = GetYValue(common, area, series, point, pointIndex, 0);
1593 if(area.Area3DStyle.Enable3D && yValue < 0.0)
1595 // No negative values support in 3D stacked area chart
1596 yValue = -yValue;
1598 if( yValue >= 0 )
1600 if(!double.IsNaN(prevPosY))
1602 areaZeroValue = prevPosY;
1605 else
1607 if(!double.IsNaN(prevNegY))
1609 areaZeroValue = prevNegY;
1613 return yValue - areaZeroValue;
1617 // Loop through all series
1618 prevPosY = double.NaN;
1619 prevNegY = double.NaN;
1620 prevPositionX = double.NaN;
1621 foreach(Series ser in common.DataManager.Series)
1623 // Check series of the current chart type & area
1624 if(String.Compare(series.ChartArea, ser.ChartArea, StringComparison.Ordinal) == 0 &&
1625 String.Compare(series.ChartTypeName, ser.ChartTypeName, StringComparison.OrdinalIgnoreCase) == 0 &&
1626 ser.IsVisible())
1628 yValue = ser.Points[pointIndex].YValues[0];
1629 if(area.Area3DStyle.Enable3D && yValue < 0.0)
1631 // No negative values support in 3D stacked area chart
1632 yValue = -yValue;
1634 if(!double.IsNaN(yValue))
1636 if(yValue >= 0.0 && !double.IsNaN(prevPosY))
1638 yValue += prevPosY;
1640 if(yValue < 0.0 && !double.IsNaN(prevNegY))
1642 yValue += prevNegY;
1646 // Exit loop when current series was found
1647 if (String.Compare(series.Name, ser.Name, StringComparison.Ordinal) == 0)
1649 break;
1652 // Remember privious position
1653 if(yValue >= 0.0)
1655 prevPosY = yValue;
1657 if(yValue < 0.0)
1659 prevNegY = yValue;
1661 prevPositionX = ser.Points[pointIndex].XValue;
1662 if(prevPositionX == 0.0 && ChartHelper.IndexedSeries(series))
1664 prevPositionX = pointIndex + 1;
1669 return yValue;
1672 #endregion
1674 #region IDisposable overrides
1675 /// <summary>
1676 /// Releases unmanaged and - optionally - managed resources
1677 /// </summary>
1678 /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
1679 protected override void Dispose(bool disposing)
1681 if (disposing)
1683 // Dispose managed resources
1684 if (this.areaBottomPath != null)
1686 this.areaBottomPath.Dispose();
1687 this.areaBottomPath = null;
1690 base.Dispose(disposing);
1692 #endregion