1 //-------------------------------------------------------------
2 // <copyright company=’Microsoft Corporation’>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 // File: StackedAreaChart.cs
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
24 using System
.Resources
;
25 using System
.Reflection
;
26 using System
.Collections
;
28 using System
.Drawing
.Drawing2D
;
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
;
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
;
48 namespace System
.Windows
.Forms
.DataVisualization
.Charting
.ChartTypes
50 namespace System
.Web
.UI
.DataVisualization
.Charting
.ChartTypes
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%.
59 internal class HundredPercentStackedAreaChart
: StackedAreaChart
64 /// Default constructor.
66 public HundredPercentStackedAreaChart()
68 hundredPercentStacked
= true;
75 // Array of total points values
76 double[] _totalPerPoint
= null;
77 int _seriesCount
= -1;
80 #region IChartType interface implementation
85 override public string Name { get{ return ChartTypeNames.OneHundredPercentStackedArea;}}
88 /// Indicates that it's a hundredred percent chart.
89 /// Axis scale from 0 to 100 percent should be used.
91 override public bool HundredPercent{ get{return true;}
}
95 #region Painting and Selection methods
98 /// Paint HundredPercentStackedAreaChart Chart
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;
110 // Call base class implementation
111 base.Paint( graph
, common
, area
, seriesToDraw
);
116 #region Y values related methods
118 /// Returns series count of same type for given chart area.
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
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())
138 _seriesCount
= seriesCount
;
144 /// Helper function, which returns the Y value of the point
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
];
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)
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
223 if(!double.IsNaN(prevPosY
))
225 areaZeroValue
= prevPosY
;
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 &&
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
266 if(yValue
>= 0.0 && !double.IsNaN(prevPosY
))
270 if(yValue
< 0.0 && !double.IsNaN(prevNegY
))
276 // Exit loop when current series was found
277 if (String
.Compare(series
.Name
, ser
.Name
, StringComparison
.Ordinal
) == 0)
282 // Remenber privious position
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%
312 /// StackedAreaChart class extends AreaChart so that chart series are
313 /// positioned on top of each other.
315 internal class StackedAreaChart
: AreaChart
320 /// Shape of the previous series
322 protected GraphicsPath areaBottomPath
= new GraphicsPath();
325 /// Previous stacked positive Y values.
327 protected double prevPosY
= double.NaN
;
330 /// Previous stacked negative Y values.
332 protected double prevNegY
= double.NaN
;
335 /// Previous X value.
337 protected double prevPositionX
= double.NaN
;
340 /// Indicates if chart is 100% stacked
342 protected bool hundredPercentStacked
= false;
349 /// Public constructor.
351 public StackedAreaChart()
354 COPCoordinatesToCheck
= COPCoordinates
.X
| COPCoordinates
.Y
;
359 #region Default tension method
362 /// Gets default line tension.
364 /// <returns>Line tension.</returns>
365 override protected float GetDefaultTension()
372 #region IChartType interface implementation
377 public override string Name { get{ return ChartTypeNames.StackedArea;}}
380 /// True if chart type is stacked
382 public override bool Stacked { get{ return true;}}
385 /// Gets chart type image.
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");
396 #region Painting and Selection methods
399 /// Paint Stacked Area Chart.
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
;
409 graph
.SetClip( area
.PlotAreaPosition
.ToRectangleF() );
412 ProcessChartType( false, graph
, common
, area
, seriesToDraw
);
415 ((ChartGraphics
)graph
).ResetClip();
419 /// This method calculates position of the area and either draws it or checks selection.
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(
429 CommonElements common
,
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(
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())
469 // Reset area shape paths
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
;
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
);
508 prevPointsArray
= curentPointsArray
;
509 curentPointsArray
= new ArrayList(ser
.Points
.Count
);
512 // Call Back Paint event
515 common
.Chart
.CallOnPrePaint(new ChartPaintEventArgs(ser
, graph
, common
, area
.PlotAreaPosition
));
518 // The data points loop
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
);
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
);
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
);
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
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
);
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;
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
);
684 if (areaBrush
!= null)
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(
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(
758 // Save previous point
759 firstPoint
= secondPoint
;
760 prevYValue1
= prevYValue2
;
762 // Increase data point 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);
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
);
787 gradientFill
= false;
789 areaBottomPath
.Reset();
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())
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
);
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
);
830 prevPointsArray
= curentPointsArray
;
831 curentPointsArray
= new ArrayList(ser
.Points
.Count
);
834 // The data points loop
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
)
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
;
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
);
886 // Round X coordinates
887 firstPoint
.X
= (float)Math
.Round(firstPoint
.X
);
888 secondPoint
.X
= (float)Math
.Round(secondPoint
.X
);
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
904 //************************************************************
905 //** Loop through all series/points for the second time
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())
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
);
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
);
936 prevPointsArray
= curentPointsArray
;
937 curentPointsArray
= new ArrayList(ser
.Points
.Count
);
940 // The data points loop
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
)
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
;
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))
993 using (StringFormat format
= new StringFormat())
995 format
.Alignment
= StringAlignment
.Center
;
996 format
.LineAlignment
= StringAlignment
.Center
;
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(
1015 ChartElementType
.DataPoint
);
1019 text
= point
.ReplaceKeywords(point
.Label
);
1022 // Disable the clip region
1023 Region oldClipRegion
= graph
.Clip
;
1024 graph
.Clip
= new Region();
1027 PointF labelPosition
= PointF
.Empty
;
1028 labelPosition
.X
= secondPoint
.X
;
1029 labelPosition
.Y
= secondPoint
.Y
- (secondPoint
.Y
- prevYValue2
) / 2f
;
1030 labelPosition
= graph
.GetRelativePoint(labelPosition
);
1033 SizeF sizeFont
= graph
.GetRelativeSize(
1034 graph
.MeasureString(
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,
1052 using (Brush brush
= new SolidBrush(point
.LabelForeColor
))
1054 graph
.DrawPointLabelStringRel(
1063 point
.LabelBackColor
,
1064 point
.LabelBorderColor
,
1065 point
.LabelBorderWidth
,
1066 point
.LabelBorderDashStyle
,
1072 // Restore old clip region
1073 graph
.Clip
= oldClipRegion
;
1078 // Save previous point
1079 firstPoint
= secondPoint
;
1080 prevYValue1
= prevYValue2
;
1082 // Increase data point index
1092 #region 3D Drawing and selection methods
1095 /// Draws a 3D surface connecting the two specified points in 2D space.
1096 /// Used to draw Line based charts.
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(
1118 ChartGraphics graph
,
1120 LightStyle lightStyle
,
1121 DataPoint3D prevDataPointEx
,
1128 DrawingOperationTypes operationType
,
1130 float bottomDarkening
,
1131 PointF thirdPointPosition
,
1132 PointF fourthPointPosition
,
1133 bool clippedSegment
)
1137 if(pointLoopIndex
!= 2)
1139 return base.Draw3DSurface(
1155 fourthPointPosition
,
1159 // Draw labels in the third loop
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
1181 // Draw labels in the third loop
1191 return new GraphicsPath();
1195 /// Gets visibility of the top surface.
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(
1207 DataPoint3D firstPoint
,
1208 DataPoint3D secondPoint
,
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
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
;
1243 // Check series name
1244 if(String
.Compare(ser
.Name
, secondPoint
.dataPoint
.series
.Name
, StringComparison
.Ordinal
) == 0)
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
;
1272 pointProperties
= ser
.Points
[secondPoint
.index
- 1];
1273 if(ser
.Points
[secondPoint
.index
- 1].IsEmpty
)
1275 pointProperties
= ser
.EmptyPointStyle
;
1284 /// Gets position ob the bottom points in area chart.
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
,
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
;
1315 if(double.IsNaN(this.prevPosY
))
1317 yValue
= axisPosition
;
1321 yValue
= vAxis
.GetPosition(this.prevPosY
);
1322 xValue
= hAxis
.GetPosition(this.prevPositionX
);
1327 if(double.IsNaN(this.prevNegY
))
1329 yValue
= axisPosition
;
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
;
1344 if(double.IsNaN(this.prevPosY
))
1346 yValue
= axisPosition
;
1350 yValue
= vAxis
.GetPosition(this.prevPosY
);
1351 xValue
= hAxis
.GetPosition(this.prevPositionX
);
1356 if(double.IsNaN(this.prevNegY
))
1358 yValue
= axisPosition
;
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
) +
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
) +
1400 fourthPoint
.X
= fourthPointPosition
.X
;
1402 if(!float.IsNaN(fourthPointPosition
.Y
))
1404 fourthPoint
.Y
= fourthPointPosition
.Y
;
1410 /// Returns how many loops through all data points is required (1 or 2)
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
1423 // Second loop will be required for semi-transparent colors
1425 foreach(object obj
in pointsArray
)
1427 // Get point & series
1428 DataPoint3D pointEx
= (DataPoint3D
) obj
;
1431 if(pointEx
.dataPoint
.Color
.A
!= 255)
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
1452 /// This method draws labels in point chart.
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(
1462 ChartGraphics graph
,
1463 CommonElements common
,
1464 DataPoint3D pointEx
,
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
;
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
,
1498 pointEx
.dataPoint
.Tag
,
1500 pointEx
.dataPoint
.LabelFormat
,
1501 pointEx
.dataPoint
.series
.YValueType
,
1502 ChartElementType
.DataPoint
);
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
);
1515 SizeF sizeFont
= graph
.GetRelativeSize(
1516 graph
.MeasureString(
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,
1534 using (Brush brush
= new SolidBrush(pointEx
.dataPoint
.LabelForeColor
))
1536 graph
.DrawPointLabelStringRel(
1539 pointEx
.dataPoint
.Font
,
1543 pointEx
.dataPoint
.LabelAngle
,
1545 pointEx
.dataPoint
.LabelBackColor
,
1546 pointEx
.dataPoint
.LabelBorderColor
,
1547 pointEx
.dataPoint
.LabelBorderWidth
,
1548 pointEx
.dataPoint
.LabelBorderDashStyle
,
1549 pointEx
.dataPoint
.series
,
1559 #region Y values methods
1562 /// Helper function, which returns the Y value of the point.
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
,
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
1600 if(!double.IsNaN(prevPosY
))
1602 areaZeroValue
= prevPosY
;
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 &&
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
1634 if(!double.IsNaN(yValue
))
1636 if(yValue
>= 0.0 && !double.IsNaN(prevPosY
))
1640 if(yValue
< 0.0 && !double.IsNaN(prevNegY
))
1646 // Exit loop when current series was found
1647 if (String
.Compare(series
.Name
, ser
.Name
, StringComparison
.Ordinal
) == 0)
1652 // Remember privious position
1661 prevPositionX
= ser
.Points
[pointIndex
].XValue
;
1662 if(prevPositionX
== 0.0 && ChartHelper
.IndexedSeries(series
))
1664 prevPositionX
= pointIndex
+ 1;
1674 #region IDisposable overrides
1676 /// Releases unmanaged and - optionally - managed resources
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
)
1683 // Dispose managed resources
1684 if (this.areaBottomPath
!= null)
1686 this.areaBottomPath
.Dispose();
1687 this.areaBottomPath
= null;
1690 base.Dispose(disposing
);