1 //-------------------------------------------------------------
2 // <copyright company=
\92Microsoft Corporation
\92>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 // File: AxisScaleBreaks.cs
10 // Namespace: System.Web.UI.WebControls[Windows.Forms].Charting
12 // Classes: AxisScaleBreakStyle
14 // Purpose: Automatic scale breaks feature related classes.
18 //===================================================================
20 #region Used namespaces
23 using System.Collections;
24 using System.Collections.Specialized;
25 using System.ComponentModel;
26 using System.ComponentModel.Design;
29 using System.Drawing.Design;
30 using System.Drawing.Drawing2D;
31 using System.Globalization;
35 using System.Windows.Forms.DataVisualization.Charting.Data;
36 using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
37 using System.Windows.Forms.DataVisualization.Charting.Utilities;
38 using System.Windows.Forms.DataVisualization.Charting.Borders3D;
39 using System.Windows.Forms.DataVisualization.Charting;
44 using System.Web.UI.DataVisualization.Charting;
45 using System.Web.UI.DataVisualization.Charting.Data;
46 using System.Web.UI.DataVisualization.Charting.ChartTypes;
47 using System.Web.UI.DataVisualization.Charting.Utilities;
53 namespace System.Windows.Forms.DataVisualization.Charting
55 namespace System.Web.UI.DataVisualization.Charting
62 /// An enumeration of line styles for axis scale breaks.
64 public enum BreakLineStyle
67 /// No scale break line visible.
72 /// Straight scale break.
82 /// Ragged scale break.
88 /// An enumeration which indicates whether an axis segment should start
89 /// from zero when scale break is used.
91 public enum StartFromZero
99 /// Start the axis segment scale from zero.
104 /// Do not start the axis segment scale from zero.
110 #endregion // Enumerations
113 /// <b>AxisScaleBreakStyle</b> class represents the settings that control the scale break.
116 SRDescription("DescriptionAttributeAxisScaleBreakStyle_AxisScaleBreakStyle"),
117 DefaultProperty("Enabled"),
120 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
121 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
123 public class AxisScaleBreakStyle
128 internal Axis axis = null;
130 // True if scale breaks are enabled
131 private bool _enabled = false;
133 // AxisName of the break line
134 private BreakLineStyle _breakLineStyle = BreakLineStyle.Ragged;
136 // Spacing between scale segments created by scale breaks
137 private double _segmentSpacing = 1.5;
140 private Color _breakLineColor = Color.Black;
143 private int _breakLineWidth = 1;
146 private ChartDashStyle _breakLineDashStyle = ChartDashStyle.Solid;
148 // Minimum segment size in axis length percentage
149 private double _minSegmentSize = 10.0;
151 // Number of segments the axis is devided into to perform statistical analysis
152 private int _totalNumberOfSegments = 100;
154 // Minimum "empty" size to be replace by the scale break
155 private int _minimumNumberOfEmptySegments = 25;
157 // Maximum number of breaks
158 private int _maximumNumberOfBreaks = 2;
160 // Indicates if scale segment should start from zero.
161 private StartFromZero _startFromZero = StartFromZero.Auto;
168 /// AxisScaleBreakStyle constructor.
170 public AxisScaleBreakStyle()
175 /// AxisScaleBreakStyle constructor.
177 /// <param name="axis">Chart axis this class belongs to.</param>
178 internal AxisScaleBreakStyle(Axis axis)
183 #endregion // Constructor
188 /// Gets or sets a flag which indicates whether one of the axis segments should start its scale from zero
189 /// when scale break is used.
192 /// When property is set to <b>StartFromZero.Auto</b>, the range of the scale determines
193 /// if zero value should be included in the scale.
196 SRCategory("CategoryAttributeMisc"),
197 DefaultValue(StartFromZero.Auto),
198 SRDescription("DescriptionAttributeAxisScaleBreakStyle_StartFromZero"),
200 public StartFromZero StartFromZero
204 return this._startFromZero;
208 this._startFromZero = value;
214 /// Maximum number of scale breaks that can be used.
217 SRCategory("CategoryAttributeMisc"),
219 SRDescription("DescriptionAttributeAxisScaleBreakStyle_MaxNumberOfBreaks"),
221 public int MaxNumberOfBreaks
225 return this._maximumNumberOfBreaks;
229 if(value < 1 || value > 5)
231 throw (new ArgumentOutOfRangeException("value", SR.ExceptionAxisScaleBreaksNumberInvalid));
233 this._maximumNumberOfBreaks = value;
239 /// Minimum axis scale region size, in percentage of the total axis length,
240 /// that can be collapsed with the scale break.
243 SRCategory("CategoryAttributeMisc"),
245 SRDescription("DescriptionAttributeAxisScaleBreakStyle_CollapsibleSpaceThreshold"),
247 public int CollapsibleSpaceThreshold
251 return this._minimumNumberOfEmptySegments;
255 if(value < 10 || value > 90)
257 throw (new ArgumentOutOfRangeException("value", SR.ExceptionAxisScaleBreaksCollapsibleSpaceInvalid));
259 this._minimumNumberOfEmptySegments = value;
265 /// Gets or sets a flag which determines if axis automatic scale breaks are enabled.
268 SRCategory("CategoryAttributeMisc"),
270 SRDescription("DescriptionAttributeAxisScaleBreakStyle_Enabled"),
271 ParenthesizePropertyNameAttribute(true),
277 return this._enabled;
281 this._enabled = value;
287 /// Gets or sets the style of the scale break line.
290 SRCategory("CategoryAttributeAppearance"),
291 DefaultValue(BreakLineStyle.Ragged),
292 SRDescription("DescriptionAttributeAxisScaleBreakStyle_BreakLineType"),
294 public BreakLineStyle BreakLineStyle
298 return this._breakLineStyle;
302 this._breakLineStyle = value;
308 /// Gets or sets the spacing of the scale break.
311 SRCategory("CategoryAttributeMisc"),
313 SRDescription("DescriptionAttributeAxisScaleBreakStyle_Spacing"),
315 public double Spacing
319 return this._segmentSpacing;
323 if(value < 0.0 || value > 10)
325 throw (new ArgumentOutOfRangeException("value", SR.ExceptionAxisScaleBreaksSpacingInvalid));
327 this._segmentSpacing = value;
333 /// Gets or sets the color of the scale break line.
336 SRCategory("CategoryAttributeAppearance"),
337 DefaultValue(typeof(Color), "Black"),
338 SRDescription("DescriptionAttributeLineColor"),
339 TypeConverter(typeof(ColorConverter)),
340 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base)
342 public Color LineColor
346 return this._breakLineColor;
350 this._breakLineColor = value;
356 /// Gets or sets the width of the scale break line.
359 SRCategory("CategoryAttributeAppearance"),
361 SRDescription("DescriptionAttributeLineWidth"),
367 return this._breakLineWidth;
371 if(value < 1.0 || value > 10)
373 throw (new ArgumentOutOfRangeException("value", SR.ExceptionAxisScaleBreaksLineWidthInvalid));
375 this._breakLineWidth = value;
381 /// Gets or sets the line style of the scale break line.
384 SRCategory("CategoryAttributeAppearance"),
385 DefaultValue(ChartDashStyle.Solid),
386 SRDescription("DescriptionAttributeLineDashStyle"),
388 public ChartDashStyle LineDashStyle
392 return this._breakLineDashStyle;
396 this._breakLineDashStyle = value;
401 #endregion // Properties
403 #region Helper Methods
406 /// Checks if automatic scale breaks are currently enabled.
408 /// <returns>True if scale breaks are currently enabled.</returns>
409 internal bool IsEnabled()
411 // Axis scale breaks must be enabled AND supported by the axis.
413 this.CanUseAxisScaleBreaks())
421 /// Checks if scale breaks can be used on specified axis.
423 /// <returns>True if scale breaks can be used on this axis</returns>
424 internal bool CanUseAxisScaleBreaks()
426 // Check input parameters
427 if(this.axis == null || this.axis.ChartArea == null || this.axis.ChartArea.Common.Chart == null)
432 // No scale breaks in 3D charts
433 if(this.axis.ChartArea.Area3DStyle.Enable3D)
438 // Axis scale break can only be applied to the Y and Y 2 axis
439 if(this.axis.axisType == AxisName.X || this.axis.axisType == AxisName.X2)
444 // No scale breaks for logarithmic axis
445 if(this.axis.IsLogarithmic)
450 // No scale breaks if axis zooming is enabled
451 if(this.axis.ScaleView.IsZoomed)
456 // Check series associated with this axis
457 ArrayList axisSeries = AxisScaleBreakStyle.GetAxisSeries(this.axis);
458 foreach(Series series in axisSeries)
461 // Some special chart type are not supported
462 if(series.ChartType == SeriesChartType.Renko ||
463 series.ChartType == SeriesChartType.PointAndFigure)
469 // Get chart type interface
470 IChartType chartType = this.axis.ChartArea.Common.ChartTypeRegistry.GetChartType(series.ChartTypeName);
471 if(chartType == null)
476 // Circular and stacked chart types can not use scale breaks
477 if(chartType.CircularChartArea ||
479 !chartType.RequireAxes)
489 /// Gets a list of series objects attached to the specified axis.
491 /// <param name="axis">Axis to get the series for.</param>
492 /// <returns>A list of series that are attached to the specified axis.</returns>
493 static internal ArrayList GetAxisSeries(Axis axis)
495 ArrayList seriesList = new ArrayList();
496 if(axis != null && axis.ChartArea != null && axis.ChartArea.Common.Chart != null)
498 // Iterate through series in the chart
499 foreach(Series series in axis.ChartArea.Common.Chart.Series)
501 // Series should be on the same chart area and visible
502 if(series.ChartArea == axis.ChartArea.Name &&
505 // Check primary/secondary axis
506 if( (axis.axisType == AxisName.Y && series.YAxisType == AxisType.Secondary) ||
507 (axis.axisType == AxisName.Y2 && series.YAxisType == AxisType.Primary))
512 // Add series into the list
513 seriesList.Add(series);
521 /// Invalidate chart control.
523 private void Invalidate()
525 if(this.axis != null)
527 this.axis.Invalidate();
531 #endregion // Helper Methods
533 #region Series StatisticFormula Methods
536 /// Get collection of axis segments to present scale breaks.
538 /// <param name="axisSegments">Collection of axis scale segments.</param>
539 internal void GetAxisSegmentForScaleBreaks(AxisScaleSegmentCollection axisSegments)
541 // Clear segment collection
542 axisSegments.Clear();
544 // Check if scale breaks are enabled
547 // Fill collection of segments
548 this.FillAxisSegmentCollection(axisSegments);
550 // Check if more than 1 segments were defined
551 if(axisSegments.Count >= 1)
553 // Get index of segment which scale should start from zero
554 int startFromZeroSegmentIndex = this.GetStartScaleFromZeroSegmentIndex(axisSegments);
556 // Calculate segment interaval and round the scale
558 foreach(AxisScaleSegment axisScaleSegment in axisSegments)
560 // Check if segment scale should start from zero
561 bool startFromZero = (index == startFromZeroSegmentIndex) ? true : false;
563 // Calculate interval and round scale
564 double minimum = axisScaleSegment.ScaleMinimum;
565 double maximum = axisScaleSegment.ScaleMaximum;
566 axisScaleSegment.Interval = this.axis.EstimateNumberAxis(
567 ref minimum, ref maximum, startFromZero, this.axis.prefferedNumberofIntervals, true, true);
568 axisScaleSegment.ScaleMinimum = minimum;
569 axisScaleSegment.ScaleMaximum = maximum;
571 // Make sure new scale break value range do not exceed axis current scale
572 if (axisScaleSegment.ScaleMinimum < this.axis.Minimum)
574 axisScaleSegment.ScaleMinimum = this.axis.Minimum;
576 if (axisScaleSegment.ScaleMaximum > this.axis.Maximum)
578 axisScaleSegment.ScaleMaximum = this.axis.Maximum;
581 // Increase segment index
585 // Defined axis scale segments cannot overlap.
586 // Check for overlapping and join segments or readjust min/max.
587 bool adjustPosition = false;
588 AxisScaleSegment prevSegment = axisSegments[0];
589 for (int segmentIndex = 1; segmentIndex < axisSegments.Count; segmentIndex++)
591 AxisScaleSegment currentSegment = axisSegments[segmentIndex];
592 if (currentSegment.ScaleMinimum <= prevSegment.ScaleMaximum)
594 if (currentSegment.ScaleMaximum > prevSegment.ScaleMaximum)
596 // If segments are partially overlapping make sure the previous
597 // segment scale is extended
598 prevSegment.ScaleMaximum = currentSegment.ScaleMaximum;
601 // Remove the overlapped segment
602 adjustPosition = true;
603 axisSegments.RemoveAt(segmentIndex);
608 prevSegment = currentSegment;
612 // Calculate the position of each segment
615 this.SetAxisSegmentPosition(axisSegments);
622 /// Gets index of segment that should be started from zero.
624 /// <param name="axisSegments">Axis scale segment collection.</param>
625 /// <returns>Index axis segment or -1.</returns>
626 private int GetStartScaleFromZeroSegmentIndex(AxisScaleSegmentCollection axisSegments)
628 if (this.StartFromZero == StartFromZero.Auto ||
629 this.StartFromZero == StartFromZero.Yes)
632 foreach(AxisScaleSegment axisScaleSegment in axisSegments)
634 // Check if zero value is already part of the scale
635 if(axisScaleSegment.ScaleMinimum < 0.0 && axisScaleSegment.ScaleMaximum > 0.0)
640 // As soon as we get first segment with positive minimum value or
641 // we reached last segment adjust scale to start from zero.
642 if(axisScaleSegment.ScaleMinimum > 0.0 ||
643 index == (axisSegments.Count - 1) )
645 // Check if setting minimum scale to zero will make the
646 // data points in the segment hard to read. This may hapen
647 // when the distance from zero to current minimum is
648 // significantly larger than current scale size.
649 if (this.StartFromZero == StartFromZero.Auto &&
650 axisScaleSegment.ScaleMinimum > 2.0 * (axisScaleSegment.ScaleMaximum - axisScaleSegment.ScaleMinimum) )
658 // Increase segment index
666 /// Sets position of all scale segments in the axis.
668 /// <param name="axisSegments">Collection of axis scale segments.</param>
669 private void SetAxisSegmentPosition(AxisScaleSegmentCollection axisSegments)
671 // Calculate total number of points
672 int totalPointNumber = 0;
673 foreach(AxisScaleSegment axisScaleSegment in axisSegments)
675 if(axisScaleSegment.Tag is int)
677 totalPointNumber += (int)axisScaleSegment.Tag;
681 // Calculate segment minimum size
682 double minSize = Math.Min(this._minSegmentSize, Math.Floor(100.0 / axisSegments.Count));
684 // Set segment position
685 double currentPosition = 0.0;
686 for(int index = 0; index < axisSegments.Count; index++)
688 axisSegments[index].Position = (currentPosition > 100.0) ? 100.0 : currentPosition;
689 axisSegments[index].Size = Math.Round(((int)axisSegments[index].Tag) / (totalPointNumber / 100.0),5);
690 if(axisSegments[index].Size < minSize)
692 axisSegments[index].Size = minSize;
695 // Set spacing for all segments except the last one
696 if(index < (axisSegments.Count - 1) )
698 axisSegments[index].Spacing = this._segmentSpacing;
701 // Advance current position
702 currentPosition += axisSegments[index].Size;
705 // Make sure we do not exceed the 100% axis length
706 double totalHeight = 0.0;
709 // Calculate total height
711 double maxSize = double.MinValue;
712 int maxSizeIndex = -1;
713 for(int index = 0; index < axisSegments.Count; index++)
715 totalHeight += axisSegments[index].Size;
716 if(axisSegments[index].Size > maxSize)
718 maxSize = axisSegments[index].Size;
719 maxSizeIndex = index;
723 // If height is too large find largest segment
724 if(totalHeight > 100.0)
726 // Adjust segment size
727 axisSegments[maxSizeIndex].Size -= totalHeight - 100.0;
728 if(axisSegments[maxSizeIndex].Size < minSize)
730 axisSegments[maxSizeIndex].Size = minSize;
733 // Adjust position of the next segment
734 double curentPosition = axisSegments[maxSizeIndex].Position + axisSegments[maxSizeIndex].Size;
735 for(int index = maxSizeIndex + 1; index < axisSegments.Count; index++)
737 axisSegments[index].Position = curentPosition;
738 curentPosition += axisSegments[index].Size;
742 } while(totalHeight > 100.0);
747 /// Fill collection of axis scale segments.
749 /// <param name="axisSegments">Collection of axis segments.</param>
750 private void FillAxisSegmentCollection(AxisScaleSegmentCollection axisSegments)
752 // Clear axis segments collection
753 axisSegments.Clear();
755 // Get statistics for the series attached to the axis
756 double minYValue = 0.0;
757 double maxYValue = 0.0;
758 double segmentSize = 0.0;
759 double[] segmentMaxValue = null;
760 double[] segmentMinValue = null;
761 int[] segmentPointNumber = GetSeriesDataStatistics(
762 this._totalNumberOfSegments,
767 out segmentMinValue);
768 if (segmentPointNumber == null)
773 // Calculate scale maximum and minimum
774 double minimum = minYValue;
775 double maximum = maxYValue;
776 this.axis.EstimateNumberAxis(
779 this.axis.IsStartedFromZero,
780 this.axis.prefferedNumberofIntervals,
784 // Make sure max/min Y values are not the same
785 if (maxYValue == minYValue)
790 // Calculate the percentage of the scale range covered by the data range.
791 double dataRangePercent = (maxYValue - minYValue) / ((maximum - minimum) / 100.0);
793 // Get sequences of empty segments
794 ArrayList emptySequences = new ArrayList();
795 bool doneFlag = false;
800 // Get longest sequence of segments with no points
801 int startSegment = 0;
802 int numberOfSegments = 0;
803 this.GetLargestSequenseOfSegmentsWithNoPoints(
806 out numberOfSegments);
808 // Adjust minimum empty segments number depending on current segments
809 int minEmptySegments = (int)(this._minimumNumberOfEmptySegments * (100.0 / dataRangePercent));
810 if(axisSegments.Count > 0 && numberOfSegments > 0)
812 // Find the segment which contain newly found empty segments sequence
813 foreach(AxisScaleSegment axisScaleSegment in axisSegments)
815 if(startSegment > 0 && (startSegment + numberOfSegments) <= segmentMaxValue.Length - 1)
817 if(segmentMaxValue[startSegment - 1] >= axisScaleSegment.ScaleMinimum &&
818 segmentMinValue[startSegment + numberOfSegments] <= axisScaleSegment.ScaleMaximum)
820 // Get percentage of segment scale that is empty and suggested for collapsing
821 double segmentScaleRange = axisScaleSegment.ScaleMaximum - axisScaleSegment.ScaleMinimum;
822 double emptySpaceRange = segmentMinValue[startSegment + numberOfSegments] - segmentMaxValue[startSegment - 1];
823 double emptySpacePercent = emptySpaceRange / (segmentScaleRange / 100.0);
824 emptySpacePercent = emptySpacePercent / 100 * axisScaleSegment.Size;
826 if(emptySpacePercent > minEmptySegments &&
827 numberOfSegments > this._minSegmentSize)
829 minEmptySegments = numberOfSegments;
836 // Check if found sequence is long enough
837 if(numberOfSegments >= minEmptySegments)
841 // Store start segment and number of segments in the list
842 emptySequences.Add(startSegment);
843 emptySequences.Add(numberOfSegments);
845 // Check if there are any emty segments sequence found
846 axisSegments.Clear();
847 if(emptySequences.Count > 0)
849 double segmentFrom = double.NaN;
850 double segmentTo = double.NaN;
852 // Based on the segments that need to be excluded create axis segments that
853 // will present on the axis scale.
854 int numberOfPoints = 0;
855 for(int index = 0; index < segmentPointNumber.Length; index++)
857 // Check if current segment is excluded
858 bool excludedSegment = this.IsExcludedSegment(emptySequences, index);
860 // If not excluded segment - update from/to range if they were set
861 if(!excludedSegment &&
862 !double.IsNaN(segmentMinValue[index]) &&
863 !double.IsNaN(segmentMaxValue[index]))
865 // Calculate total number of points
866 numberOfPoints += segmentPointNumber[index];
868 // Set From/To of the visible segment
869 if(double.IsNaN(segmentFrom))
871 segmentFrom = segmentMinValue[index];
872 segmentTo = segmentMaxValue[index];
876 segmentTo = segmentMaxValue[index];
880 // If excluded or last segment - add current visible segment range
881 if(!double.IsNaN(segmentFrom) &&
882 (excludedSegment || index == (segmentPointNumber.Length - 1) ))
884 // Make sure To and From do not match
885 if(segmentTo == segmentFrom)
887 segmentFrom -= segmentSize;
888 segmentTo += segmentSize;
891 // Add axis scale segment
892 AxisScaleSegment axisScaleSegment = new AxisScaleSegment();
893 axisScaleSegment.ScaleMaximum = segmentTo;
894 axisScaleSegment.ScaleMinimum = segmentFrom;
895 axisScaleSegment.Tag = numberOfPoints;
896 axisSegments.Add(axisScaleSegment);
898 // Reset segment range
899 segmentFrom = double.NaN;
900 segmentTo = double.NaN;
906 // Calculate the position of each segment
907 this.SetAxisSegmentPosition(axisSegments);
910 // Make sure we do not exceed specified number of breaks
911 if( (axisSegments.Count - 1) >= this._maximumNumberOfBreaks)
920 /// Check if segment was excluded.
922 /// <param name="excludedSegments">Array of segment indexes.</param>
923 /// <param name="segmentIndex">Index of the segment to check.</param>
924 /// <returns>True if segment with specified index is marked as excluded.</returns>
925 private bool IsExcludedSegment(ArrayList excludedSegments, int segmentIndex)
927 for(int index = 0; index < excludedSegments.Count; index += 2)
929 if(segmentIndex >= (int)excludedSegments[index] &&
930 segmentIndex < (int)excludedSegments[index] + (int)excludedSegments[index + 1])
939 /// Collect statistical information about the series.
941 /// <param name="segmentCount">Segment count.</param>
942 /// <param name="minYValue">Minimum Y value.</param>
943 /// <param name="maxYValue">Maximum Y value.</param>
944 /// <param name="segmentSize">Segment size.</param>
945 /// <param name="segmentMaxValue">Array of segment scale maximum values.</param>
946 /// <param name="segmentMinValue">Array of segment scale minimum values.</param>
947 /// <returns></returns>
948 internal int[] GetSeriesDataStatistics(
950 out double minYValue,
951 out double maxYValue,
952 out double segmentSize,
953 out double[] segmentMaxValue,
954 out double[] segmentMinValue)
956 // Get all series associated with the axis
957 ArrayList axisSeries = AxisScaleBreakStyle.GetAxisSeries(this.axis);
959 // Get range of Y values from axis series
962 axis.Common.DataManager.GetMinMaxYValue(axisSeries, out minYValue, out maxYValue);
964 int numberOfPoints = 0;
965 foreach (Series series in axisSeries)
967 numberOfPoints = Math.Max(numberOfPoints, series.Points.Count);
970 if (axisSeries.Count == 0 || numberOfPoints == 0)
973 segmentMaxValue = null;
974 segmentMinValue = null;
978 // Split range of values into predefined number of segments and calculate
979 // how many points will be in each segment.
980 segmentSize = (maxYValue - minYValue) / segmentCount;
981 int[] segmentPointNumber = new int[segmentCount];
982 segmentMaxValue = new double[segmentCount];
983 segmentMinValue = new double[segmentCount];
984 for(int index = 0; index < segmentCount; index++)
986 segmentMaxValue[index] = double.NaN;
987 segmentMinValue[index] = double.NaN;
989 foreach(Series series in axisSeries)
991 // Get number of Y values to process
992 int maxYValueCount = 1;
993 IChartType chartType = this.axis.ChartArea.Common.ChartTypeRegistry.GetChartType(series.ChartTypeName);
994 if(chartType != null)
996 if(chartType.ExtraYValuesConnectedToYAxis && chartType.YValuesPerPoint > 1)
998 maxYValueCount = chartType.YValuesPerPoint;
1002 // Iterate throug all data points
1003 foreach(DataPoint dataPoint in series.Points)
1005 if(!dataPoint.IsEmpty)
1007 // Iterate through all yValues
1008 for(int yValueIndex = 0; yValueIndex < maxYValueCount; yValueIndex++)
1010 // Calculate index of the scale segment
1011 int segmentIndex = (int)Math.Floor((dataPoint.YValues[yValueIndex] - minYValue) / segmentSize);
1012 if(segmentIndex < 0)
1016 if(segmentIndex > segmentCount - 1)
1018 segmentIndex = segmentCount - 1;
1021 // Increase number points in that segment
1022 ++segmentPointNumber[segmentIndex];
1024 // Store Min/Max values for the segment
1025 if(segmentPointNumber[segmentIndex] == 1)
1027 segmentMaxValue[segmentIndex] = dataPoint.YValues[yValueIndex];
1028 segmentMinValue[segmentIndex] = dataPoint.YValues[yValueIndex];
1032 segmentMaxValue[segmentIndex] = Math.Max(segmentMaxValue[segmentIndex], dataPoint.YValues[yValueIndex]);
1033 segmentMinValue[segmentIndex] = Math.Min(segmentMinValue[segmentIndex], dataPoint.YValues[yValueIndex]);
1040 return segmentPointNumber;
1044 /// Gets largest segment with no points.
1046 /// <param name="segmentPointNumber">Array that stores number of points for each segment.</param>
1047 /// <param name="startSegment">Returns largest empty segment sequence starting index.</param>
1048 /// <param name="numberOfSegments">Returns largest empty segment sequence length.</param>
1049 /// <returns>True if long empty segment sequence was found.</returns>
1050 internal bool GetLargestSequenseOfSegmentsWithNoPoints(
1051 int[] segmentPointNumber,
1052 out int startSegment,
1053 out int numberOfSegments)
1055 // Find the longest sequence of empty segments
1057 numberOfSegments = 0;
1058 int currentSegmentStart = -1;
1059 int currentNumberOfSegments = -1;
1060 for(int index = 0; index < segmentPointNumber.Length; index++)
1062 // Check for the segment with no points
1063 if(segmentPointNumber[index] == 0)
1065 if(currentSegmentStart == -1)
1067 currentSegmentStart = index;
1068 currentNumberOfSegments = 1;
1072 ++currentNumberOfSegments;
1076 // Check if longest sequence found
1077 if(currentNumberOfSegments > 0 &&
1078 (segmentPointNumber[index] != 0 || index == segmentPointNumber.Length - 1))
1080 if(currentNumberOfSegments > numberOfSegments)
1082 startSegment = currentSegmentStart;
1083 numberOfSegments = currentNumberOfSegments;
1085 currentSegmentStart = -1;
1086 currentNumberOfSegments = 0;
1090 // Store value of "-1" in found sequence
1091 if(numberOfSegments != 0)
1093 for(int index = startSegment; index < (startSegment + numberOfSegments); index++)
1095 segmentPointNumber[index] = -1;
1104 #endregion // Series StatisticFormula Methods