1 //-------------------------------------------------------------
2 // <copyright company=
\92Microsoft Corporation
\92>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 // File: ChartAreaAxes.cs
10 // Namespace: System.Web.UI.WebControls[Windows.Forms].Charting
12 // Classes: ChartAreaAxes
14 // Purpose: ChartAreaAxes is base class of Chart Area class.
15 // This class searches for all series, which belongs
16 // to this chart area and sets axes minimum and
17 // maximum values using data. This class also checks
18 // for chart types, which belong to this chart area
19 // and prepare axis scale according to them (Stacked
20 // chart types have different max and min values).
21 // This class recognizes indexed values and prepares
24 // Reviewed: GS - Jul 31, 2002
25 // AG - August 7, 2002
27 //===================================================================
29 #region Used namespaces
32 using System.Collections;
33 using System.Collections.Generic;
36 using System.Windows.Forms.DataVisualization.Charting;
37 using System.Windows.Forms.DataVisualization.Charting.Data;
38 using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
39 using System.Windows.Forms.DataVisualization.Charting.Utilities;
40 using System.Windows.Forms.DataVisualization.Charting.Borders3D;
42 using System.Web.UI.DataVisualization.Charting.Data;
43 using System.Web.UI.DataVisualization.Charting.ChartTypes;
49 namespace System.Windows.Forms.DataVisualization.Charting
51 namespace System.Web.UI.DataVisualization.Charting
55 /// ChartAreaAxes class represents axes (X, Y, X2 and Y2) in the chart area.
56 /// It contains methods that collect statistical information on the series data and
57 /// other axes related methods.
59 public partial class ChartArea
63 // Axes which belong to this Chart Area
64 internal Axis axisY = null;
65 internal Axis axisX = null;
66 internal Axis axisX2 = null;
67 internal Axis axisY2 = null;
69 // Array of series which belong to this chart area
70 private List<string> _series = new List<string>();
72 // Array of chart types which belong to this chart area
73 internal ArrayList chartTypes = new ArrayList();
76 /// List of series names that last interval numbers where cashed for
78 private string _intervalSeriesList = "";
80 // Minimum interval between two data points for all
81 // series which belong to this chart area.
82 internal double intervalData = double.NaN;
84 // Minimum interval between two data points for all
85 // series which belong to this chart area.
86 // IsLogarithmic version of the interval.
87 internal double intervalLogData = double.NaN;
89 // Series with minimum interval between two data points for all
90 // series which belong to this chart area.
91 private Series _intervalSeries = null;
93 // Indicates that points are located through equal X intervals
94 internal bool intervalSameSize = false;
96 // Indicates that points alignment checked
97 internal bool diffIntervalAlignmentChecked = false;
99 // Chart Area contains stacked chart types
100 internal bool stacked = false;
102 // Chart type with two y values used for scale ( bubble chart type )
103 internal bool secondYScale = false;
105 // The X and Y axes are switched
106 internal bool switchValueAxes = false;
108 // True for all chart types, which have axes. False for doughnut and pie chart.
109 internal bool requireAxes = true;
111 // Indicates that chart area has circular shape (like in radar or polar chart)
112 internal bool chartAreaIsCurcular = false;
114 // Chart Area contains 100 % stacked chart types
115 internal bool hundredPercent = false;
117 // Chart Area contains 100 % stacked chart types
118 internal bool hundredPercentNegative = false;
122 #region Internal properties
125 /// True if sub axis supported on this chart area
127 internal bool IsSubAxesSupported
131 if(((ChartArea)this).Area3DStyle.Enable3D ||
132 ((ChartArea)this).chartAreaIsCurcular)
141 /// Data series which belongs to this chart area.
143 internal List<string> Series
152 /// Chart types which belongs to this chart area.
154 internal ArrayList ChartTypes
167 /// Gets main or sub axis from the chart area.
169 /// <param name="axisName">Axis name. NOTE: This parameter only defines X or Y axis.
170 /// Second axisType parameter is used to select primary or secondary axis. </param>
171 /// <param name="axisType">Axis type.</param>
172 /// <param name="subAxisName">Sub-axis name or empty string.</param>
173 /// <returns>Main or sub axis of the chart area.</returns>
174 internal Axis GetAxis(AxisName axisName, AxisType axisType, string subAxisName)
176 // Ignore sub axis in 3D
177 if( ((ChartArea)this).Area3DStyle.Enable3D)
179 subAxisName = string.Empty;
182 if(axisName == AxisName.X || axisName == AxisName.X2)
184 if(axisType == AxisType.Primary)
186 return ((ChartArea)this).AxisX.GetSubAxis(subAxisName);
188 return ((ChartArea)this).AxisX2.GetSubAxis(subAxisName);
192 if(axisType == AxisType.Primary)
194 return ((ChartArea)this).AxisY.GetSubAxis(subAxisName);
196 return ((ChartArea)this).AxisY2.GetSubAxis(subAxisName);
201 /// Sets default axis values for all different chart type
202 /// groups. Chart type groups are sets of chart types.
204 internal void SetDefaultAxesValues( )
206 // The X and Y axes are switched ( Bar chart, stacked bar ... )
207 if( switchValueAxes )
209 // Set axis positions
210 axisY.AxisPosition = AxisPosition.Bottom;
211 axisX.AxisPosition = AxisPosition.Left;
212 axisX2.AxisPosition = AxisPosition.Right;
213 axisY2.AxisPosition = AxisPosition.Top;
217 // Set axis positions
218 axisY.AxisPosition = AxisPosition.Left;
219 axisX.AxisPosition = AxisPosition.Bottom;
220 axisX2.AxisPosition = AxisPosition.Top;
221 axisY2.AxisPosition = AxisPosition.Right;
224 // Reset opposite Axes field. This cashing
225 // value is used for optimization.
226 foreach( Axis axisItem in ((ChartArea)this).Axes )
228 axisItem.oppositeAxis = null;
230 foreach( SubAxis subAxisItem in axisItem.SubAxes )
232 subAxisItem.m_oppositeAxis = null;
237 // ***********************
239 // ***********************
240 // Find the number of series which belong to this axis
241 if (this.chartAreaIsCurcular)
243 // Set axis Maximum/Minimum and Interval for circular chart
244 axisX.SetAutoMaximum(360.0);
245 axisX.SetAutoMinimum(0.0);
246 axisX.SetInterval = Math.Abs(axisX.maximum - axisX.minimum) / 12.0;
250 SetDefaultFromIndexesOrData(axisX, AxisType.Primary);
254 // ***********************
255 // Primary X Sub-Axes
256 // ***********************
257 foreach(SubAxis subAxis in axisX.SubAxes)
259 SetDefaultFromIndexesOrData(subAxis, AxisType.Primary);
263 // ***********************
265 // ***********************
266 SetDefaultFromIndexesOrData(axisX2, AxisType.Secondary);
269 // ***********************
270 // Secondary X Sub-Axes
271 // ***********************
272 foreach(SubAxis subAxis in axisX2.SubAxes)
274 SetDefaultFromIndexesOrData(subAxis, AxisType.Secondary);
278 // ***********************
280 // ***********************
281 if( GetYAxesSeries( AxisType.Primary, string.Empty ).Count != 0 )
283 // Find minimum and maximum from Y values.
284 SetDefaultFromData( axisY );
285 axisY.EstimateAxis();
289 // ***********************
290 // Primary Y Sub-Axes
291 // ***********************
292 foreach(SubAxis subAxis in axisY.SubAxes)
294 // Find the number of series which belong to this axis
295 if( GetYAxesSeries( AxisType.Primary, subAxis.SubAxisName ).Count != 0 )
297 // Find minimum and maximum from Y values.
298 SetDefaultFromData( subAxis );
299 subAxis.EstimateAxis();
304 // ***********************
306 // ***********************
307 if( GetYAxesSeries( AxisType.Secondary, string.Empty ).Count != 0 )
309 // Find minimum and maximum from Y values.
310 SetDefaultFromData( axisY2 );
311 axisY2.EstimateAxis();
315 // ***********************
316 // Secondary Y Sub-Axes
317 // ***********************
318 foreach(SubAxis subAxis in axisY2.SubAxes)
320 // Find the number of series which belong to this axis
321 if( GetYAxesSeries( AxisType.Secondary, subAxis.SubAxisName ).Count != 0 )
323 // Find minimum and maximum from Y values.
324 SetDefaultFromData( subAxis );
325 subAxis.EstimateAxis();
330 // Sets axis position. Axis position depends
331 // on crossing and reversed value.
332 axisX.SetAxisPosition();
333 axisX2.SetAxisPosition();
334 axisY.SetAxisPosition();
335 axisY2.SetAxisPosition();
337 // Enable axes, which are
338 // used in data series.
344 // Get scale break segments
345 Axis[] axesYArray = new Axis[] { axisY, axisY2 };
346 foreach(Axis currentAxis in axesYArray)
348 // Get automatic scale break segments
349 currentAxis.ScaleBreakStyle.GetAxisSegmentForScaleBreaks(currentAxis.ScaleSegments);
351 // Make sure axis scale do not exceed segments scale
352 if(currentAxis.ScaleSegments.Count > 0)
354 // Save flag that scale segments are used
355 currentAxis.scaleSegmentsUsed = true;
357 if(currentAxis.minimum < currentAxis.ScaleSegments[0].ScaleMinimum)
359 currentAxis.minimum = currentAxis.ScaleSegments[0].ScaleMinimum;
361 if(currentAxis.minimum > currentAxis.ScaleSegments[currentAxis.ScaleSegments.Count - 1].ScaleMaximum)
363 currentAxis.minimum = currentAxis.ScaleSegments[currentAxis.ScaleSegments.Count - 1].ScaleMaximum;
370 bool useScaleSegments = false;
373 Axis[] axesArray = new Axis[] { axisX, axisX2, axisY, axisY2 };
374 foreach(Axis currentAxis in axesArray)
377 useScaleSegments = (currentAxis.ScaleSegments.Count > 0);
379 if(!useScaleSegments)
381 currentAxis.FillLabels(true);
386 bool removeLabels = true;
387 int segmentIndex = 0;
388 foreach(AxisScaleSegment scaleSegment in currentAxis.ScaleSegments)
390 scaleSegment.SetTempAxisScaleAndInterval();
392 currentAxis.FillLabels(removeLabels);
393 removeLabels = false;
395 scaleSegment.RestoreAxisScaleAndInterval();
397 // Remove last label for all segments except of the last
398 if(segmentIndex < (currentAxis.ScaleSegments.Count - 1) &&
399 currentAxis.CustomLabels.Count > 0)
401 currentAxis.CustomLabels.RemoveAt(currentAxis.CustomLabels.Count - 1);
409 foreach (Axis currentAxis in axesArray)
411 currentAxis.PostFillLabels();
416 /// Sets the axis defaults.
417 /// If the at least one of the series bound to this axis is Indexed then the defaults are set using the SetDefaultsFromIndexes().
418 /// Otherwise the SetDefaultFromData() is used.
420 /// <param name="axis">Axis to process</param>
421 /// <param name="axisType">Axis type</param>
422 private void SetDefaultFromIndexesOrData(Axis axis, AxisType axisType)
424 //Get array of the series that are linked to this axis
425 List<string> axisSeriesNames = GetXAxesSeries(axisType, axis.SubAxisName);
427 // before this change: If we find one indexed series we will treat all series as indexed.
428 // after this change : We will assume that all series are indexed.
429 // If we find one non indexed series we will treat all series as non indexed.
430 bool indexedSeries = true;
432 // If we have mix of indexed with non-indexed series
433 // enforce all indexed series as non-indexed;
434 // The result of mixed type of series will be more natural
435 // and easy to detect the problem - all datapoints of indexed
436 // series will be displayed on zero position.
437 //=====================================
438 // bool nonIndexedSeries = false;
439 //=======================================
440 //Loop through the series looking for a indexed one
441 foreach(string seriesName in axisSeriesNames)
444 Series series = Common.DataManager.Series[seriesName];
445 // Check if series is indexed
446 if (!ChartHelper.IndexedSeries(series))
448 // found one nonindexed series - we will treat all series as non indexed.
449 indexedSeries = false;
455 // nonIndexedSeries = true;
460 //if (!indexedSeries && nonIndexedSeries)
462 // foreach (string seriesName in axisSeriesNames)
465 // Series series = Common.DataManager.Series[seriesName];
466 // series.xValuesZeros = false;
472 if (axis.IsLogarithmic)
474 throw (new InvalidOperationException(SR.ExceptionChartAreaAxisScaleLogarithmicUnsuitable));
476 //Set axis defaults from the indexed series
477 SetDefaultFromIndexes(axis);
482 // If haven't found any indexed series -> Set axis defaults from the series data
483 SetDefaultFromData(axis);
488 /// Enable axes, which are
489 /// used in chart area data series.
491 private void EnableAxes()
493 if( _series == null )
498 bool activeX = false;
499 bool activeY = false;
500 bool activeX2 = false;
501 bool activeY2 = false;
503 // Data series from this chart area
504 foreach( string ser in _series )
506 Series dataSeries = Common.DataManager.Series[ ser ];
509 if( dataSeries.XAxisType == AxisType.Primary )
513 this.Activate( axisX, true, dataSeries.XSubAxisName );
515 this.Activate( axisX, true );
523 this.Activate( axisX2, true, dataSeries.XSubAxisName );
525 this.Activate( axisX2, true );
529 if( dataSeries.YAxisType == AxisType.Primary )
533 this.Activate( axisY, true, dataSeries.YSubAxisName );
535 this.Activate( axisY, true );
542 this.Activate( axisY2, true, dataSeries.YSubAxisName );
544 this.Activate( axisY2, true );
552 this.Activate( axisX, false, string.Empty );
554 this.Activate( axisY, false, string.Empty );
556 this.Activate( axisX2, false, string.Empty );
558 this.Activate( axisY2, false, string.Empty );
562 this.Activate( axisX, false);
564 this.Activate( axisY, false);
566 this.Activate( axisX2, false);
568 this.Activate( axisY2, false);
577 /// <param name="axis">Axis.</param>
578 /// <param name="active">True if axis is active.</param>
579 /// <param name="subAxisName">Sub axis name to activate.</param>
580 private void Activate( Axis axis, bool active, string subAxisName )
583 if( axis.autoEnabled == true )
585 axis.enabled = active;
588 // Auto-Enable sub axes
589 if(subAxisName.Length > 0)
591 SubAxis subAxis = axis.SubAxes.FindByName(subAxisName);
594 if( subAxis.autoEnabled == true )
596 subAxis.enabled = active;
605 /// <param name="axis">Axis.</param>
606 /// <param name="active">True if axis is active.</param>
607 private void Activate( Axis axis, bool active )
609 if( axis.autoEnabled == true )
611 axis.enabled = active;
617 /// Check if all data points from series in
618 /// this chart area are empty.
620 /// <returns>True if all points are empty</returns>
621 bool AllEmptyPoints()
623 // Data series from this chart area
624 foreach( string seriesName in this._series )
626 Series dataSeries = Common.DataManager.Series[ seriesName ];
629 foreach( DataPoint point in dataSeries.Points )
641 /// This method sets default minimum and maximum
642 /// values from values in the data manager. This
643 /// case is used if X values are not equal to 0 or IsXValueIndexed flag is set.
645 /// <param name="axis">Axis</param>
646 private void SetDefaultFromData( Axis axis )
649 // Process all sub-axes
652 foreach(SubAxis subAxis in axis.SubAxes)
654 this.SetDefaultFromData( subAxis );
660 // Used for scrolling with logarithmic axes.
661 if( !Double.IsNaN(axis.ScaleView.Position) &&
662 !Double.IsNaN(axis.ScaleView.Size) &&
663 !axis.refreshMinMaxFromData &&
669 // Get minimum and maximum from data source
672 this.GetValuesFromData( axis, out autoMinimum, out autoMaximum );
674 // ***************************************************
675 // This part of code is used to add a margin to the
676 // axis and to set minimum value to zero if
677 // IsStartedFromZero property is used. There is special
678 // code for logarithmic scale, which will set minimum
679 // to one instead of zero.
680 // ***************************************************
681 // The minimum and maximum values from data manager don
\92t exist.
684 ( (axis.AutoMaximum || double.IsNaN( axis.Maximum )) && (autoMaximum == Double.MaxValue || autoMaximum == Double.MinValue)) ||
685 ( (axis.AutoMinimum || double.IsNaN( axis.Minimum )) && (autoMinimum == Double.MaxValue || autoMinimum == Double.MinValue )) )
687 if( this.AllEmptyPoints() )
689 // Supress exception and use predefined min & max
695 if(!this.Common.ChartPicture.SuppressExceptions)
697 throw (new InvalidOperationException(SR.ExceptionAxisMinimumMaximumInvalid));
702 // Axis margin used for zooming
703 axis.marginView = 0.0;
704 if( axis.margin == 100 && (axis.axisType == AxisName.X || axis.axisType == AxisName.X2) )
706 axis.marginView = this.GetPointsInterval( false, 10 );
709 // If minimum and maximum are same margin always exist.
710 if( autoMaximum == autoMinimum &&
711 axis.Maximum == axis.Minimum )
716 // Do not make axis margine for logarithmic axes
717 if( axis.IsLogarithmic )
719 axis.marginView = 0.0;
722 // Adjust Maximum - Add a gap
723 if( axis.AutoMaximum )
725 // Add a Gap for X axis
726 if( !axis.roundedXValues && ( axis.axisType == AxisName.X || axis.axisType == AxisName.X2 ) )
728 axis.SetAutoMaximum( autoMaximum + axis.marginView );
732 if( axis.isStartedFromZero && autoMaximum < 0 )
734 axis.SetAutoMaximum( 0.0 );
738 axis.SetAutoMaximum( autoMaximum );
743 // Adjust Minimum - make rounded values and add a gap
744 if( axis.AutoMinimum )
746 // IsLogarithmic axis
747 if( axis.IsLogarithmic )
749 if( autoMinimum < 1.0 )
751 axis.SetAutoMinimum( autoMinimum );
753 else if( axis.isStartedFromZero )
755 axis.SetAutoMinimum( 1.0 );
759 axis.SetAutoMinimum( autoMinimum );
764 if( autoMinimum > 0.0 ) // If Auto calculated Minimum value is positive
767 if( !axis.roundedXValues && ( axis.axisType == AxisName.X || axis.axisType == AxisName.X2 ) )
769 axis.SetAutoMinimum( autoMinimum - axis.marginView );
771 // If start From Zero property is true 0 is always on the axis.
772 // NOTE: Not applicable if date-time values are drawn. Fixes issue #5644
773 else if( axis.isStartedFromZero &&
774 !this.SeriesDateTimeType( axis.axisType, axis.SubAxisName ) )
776 axis.SetAutoMinimum( 0.0 );
780 axis.SetAutoMinimum( autoMinimum );
783 else // If Auto calculated Minimum value is non positive
785 if( axis.axisType == AxisName.X || axis.axisType == AxisName.X2 )
787 axis.SetAutoMinimum( autoMinimum - axis.marginView );
791 // If start From Zero property is true 0 is always on the axis.
792 axis.SetAutoMinimum( autoMinimum );
798 // If maximum or minimum are not auto set value to non logarithmic
799 if( axis.IsLogarithmic && axis.logarithmicConvertedToLinear )
801 if( !axis.AutoMinimum )
803 axis.minimum = axis.logarithmicMinimum;
806 if( !axis.AutoMaximum )
808 axis.maximum = axis.logarithmicMaximum;
810 // Min and max will take real values again if scale is logarithmic.
811 axis.logarithmicConvertedToLinear = false;
814 // Check if Minimum == Maximum
815 if(this.Common.ChartPicture.SuppressExceptions &&
816 axis.maximum == axis.minimum)
818 axis.minimum = axis.maximum;
819 axis.maximum = axis.minimum + 1.0;
824 /// This method checks if all series in the chart area have
\93integer type
\94
825 /// for specified axes, which means int, uint, long and ulong.
827 /// <param name="axisName">Name of the axis</param>
828 /// <param name="subAxisName">Sub axis name.</param>
829 /// <returns>True if all series are integer</returns>
830 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "subAxisName")]
831 internal bool SeriesIntegerType( AxisName axisName, string subAxisName )
833 // Series which belong to this chart area
834 foreach( string seriesName in this._series )
836 Series ser = Common.DataManager.Series[ seriesName ];
838 if( axisName == AxisName.X )
841 if( ser.XAxisType == AxisType.Primary && ser.XSubAxisName == subAxisName)
843 if ( ser.XAxisType == AxisType.Primary)
846 if(ser.XValueType != ChartValueType.Int32 &&
847 ser.XValueType != ChartValueType.UInt32 &&
848 ser.XValueType != ChartValueType.UInt64 &&
849 ser.XValueType != ChartValueType.Int64 )
860 else if( axisName == AxisName.X2 )
863 if( ser.XAxisType == AxisType.Secondary && ser.XSubAxisName == subAxisName)
865 if ( ser.XAxisType == AxisType.Secondary)
869 if(ser.XValueType != ChartValueType.Int32 &&
870 ser.XValueType != ChartValueType.UInt32 &&
871 ser.XValueType != ChartValueType.UInt64 &&
872 ser.XValueType != ChartValueType.Int64 )
883 else if( axisName == AxisName.Y )
886 if( ser.YAxisType == AxisType.Primary && ser.YSubAxisName == subAxisName)
888 if ( ser.YAxisType == AxisType.Primary)
892 if(ser.YValueType != ChartValueType.Int32 &&
893 ser.YValueType != ChartValueType.UInt32 &&
894 ser.YValueType != ChartValueType.UInt64 &&
895 ser.YValueType != ChartValueType.Int64 )
905 else if( axisName == AxisName.Y2 )
908 if( ser.YAxisType == AxisType.Secondary && ser.YSubAxisName == subAxisName)
910 if ( ser.YAxisType == AxisType.Secondary)
914 if(ser.YValueType != ChartValueType.Int32 &&
915 ser.YValueType != ChartValueType.UInt32 &&
916 ser.YValueType != ChartValueType.UInt64 &&
917 ser.YValueType != ChartValueType.Int64 )
932 /// This method checks if all series in the chart area have
\93date-time type
\94
933 /// for specified axes.
935 /// <param name="axisName">Name of the axis</param>
936 /// <param name="subAxisName">Sub axis name.</param>
937 /// <returns>True if all series are date-time.</returns>
938 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "subAxisName")]
939 internal bool SeriesDateTimeType( AxisName axisName, string subAxisName )
941 // Series which belong to this chart area
942 foreach( string seriesName in this._series )
944 Series ser = Common.DataManager.Series[ seriesName ];
946 if( axisName == AxisName.X )
949 if( ser.XAxisType == AxisType.Primary && ser.XSubAxisName == subAxisName)
951 if ( ser.XAxisType == AxisType.Primary)
954 if(ser.XValueType != ChartValueType.Date &&
955 ser.XValueType != ChartValueType.DateTime &&
956 ser.XValueType != ChartValueType.Time &&
957 ser.XValueType != ChartValueType.DateTimeOffset)
968 else if( axisName == AxisName.X2 )
971 if( ser.XAxisType == AxisType.Secondary && ser.XSubAxisName == subAxisName)
973 if ( ser.XAxisType == AxisType.Secondary)
976 if(ser.XValueType != ChartValueType.Date &&
977 ser.XValueType != ChartValueType.DateTime &&
978 ser.XValueType != ChartValueType.Time &&
979 ser.XValueType != ChartValueType.DateTimeOffset)
990 else if( axisName == AxisName.Y )
993 if( ser.YAxisType == AxisType.Primary && ser.YSubAxisName == subAxisName)
995 if ( ser.YAxisType == AxisType.Primary)
998 if(ser.YValueType != ChartValueType.Date &&
999 ser.YValueType != ChartValueType.DateTime &&
1000 ser.YValueType != ChartValueType.Time &&
1001 ser.YValueType != ChartValueType.DateTimeOffset)
1011 else if( axisName == AxisName.Y2 )
1014 if( ser.YAxisType == AxisType.Secondary && ser.YSubAxisName == subAxisName)
1016 if ( ser.YAxisType == AxisType.Secondary)
1019 if(ser.YValueType != ChartValueType.Date &&
1020 ser.YValueType != ChartValueType.DateTime &&
1021 ser.YValueType != ChartValueType.Time &&
1022 ser.YValueType != ChartValueType.DateTimeOffset)
1037 /// This method calculates minimum and maximum from data series.
1039 /// <param name="axis">Axis which is used to find minimum and maximum</param>
1040 /// <param name="autoMinimum">Minimum value from data.</param>
1041 /// <param name="autoMaximum">Maximum value from data.</param>
1042 private void GetValuesFromData( Axis axis, out double autoMinimum, out double autoMaximum )
1044 // Get number of points in series
1045 int currentPointsNumber = this.GetNumberOfAllPoints();
1047 if( !axis.refreshMinMaxFromData &&
1048 !double.IsNaN(axis.minimumFromData) &&
1049 !double.IsNaN(axis.maximumFromData) &&
1050 axis.numberOfPointsInAllSeries == currentPointsNumber )
1052 autoMinimum = axis.minimumFromData;
1053 autoMaximum = axis.maximumFromData;
1058 AxisType type = AxisType.Primary;
1059 if( axis.axisType == AxisName.X2 || axis.axisType == AxisName.Y2 )
1061 type = AxisType.Secondary;
1064 // Creates a list of series, which have same X axis type.
1065 string [] xAxesSeries = GetXAxesSeries(type, axis.SubAxisName).ToArray();
1067 // Creates a list of series, which have same Y axis type.
1068 string [] yAxesSeries = GetYAxesSeries( type, axis.SubAxisName ).ToArray();
1070 // Get auto maximum and auto minimum value
1071 if( axis.axisType == AxisName.X2 || axis.axisType == AxisName.X ) // X axis type is used (X or X2)
1073 if( stacked ) // Chart area has a stacked chart types
1077 Common.DataManager.GetMinMaxXValue(out autoMinimum, out autoMaximum, xAxesSeries );
1079 catch(System.Exception)
1081 throw (new InvalidOperationException(SR.ExceptionAxisStackedChartsDataPointsNumberMismatch));
1085 // Chart type with two y values used for scale ( bubble chart type )
1086 else if( secondYScale )
1088 autoMaximum = Common.DataManager.GetMaxXWithRadiusValue( (ChartArea)this, xAxesSeries );
1089 autoMinimum = Common.DataManager.GetMinXWithRadiusValue( (ChartArea)this, xAxesSeries );
1090 ChartValueType valueTypes = Common.DataManager.Series[xAxesSeries[0]].XValueType;
1091 if( valueTypes != ChartValueType.Date &&
1092 valueTypes != ChartValueType.DateTime &&
1093 valueTypes != ChartValueType.Time &&
1094 valueTypes != ChartValueType.DateTimeOffset )
1096 axis.roundedXValues = true;
1101 Common.DataManager.GetMinMaxXValue(out autoMinimum, out autoMaximum, xAxesSeries );
1104 else // Y axis type is used (Y or Y2)
1107 // *****************************
1108 // Stacked Chart AxisName
1109 // *****************************
1110 if( stacked ) // Chart area has a stacked chart types
1114 if(hundredPercent) // It's a hundred percent stacked chart
1116 autoMaximum = Common.DataManager.GetMaxHundredPercentStackedYValue(hundredPercentNegative, yAxesSeries );
1117 autoMinimum = Common.DataManager.GetMinHundredPercentStackedYValue(hundredPercentNegative, yAxesSeries );
1121 // If stacked groupes are used Min/Max range must calculated
1122 // for each group seperatly.
1123 double stackMaxBarColumn = double.MinValue;
1124 double stackMinBarColumn = double.MaxValue;
1125 double stackMaxArea = double.MinValue;
1126 double stackMinArea = double.MaxValue;
1128 // Split series by group names
1129 ArrayList stackedGroups = this.SplitSeriesInStackedGroups(yAxesSeries);
1130 foreach(string[] groupSeriesNames in stackedGroups)
1132 // For stacked bar and column
1133 double stackMaxBarColumnForGroup = Common.DataManager.GetMaxStackedYValue(0, groupSeriesNames );
1134 double stackMinBarColumnForGroup = Common.DataManager.GetMinStackedYValue(0, groupSeriesNames );
1137 double stackMaxAreaForGroup = Common.DataManager.GetMaxUnsignedStackedYValue(0, groupSeriesNames );
1138 double stackMinAreaForGroup = Common.DataManager.GetMinUnsignedStackedYValue(0, groupSeriesNames );
1140 // Select minimum/maximum
1141 stackMaxBarColumn = Math.Max(stackMaxBarColumn, stackMaxBarColumnForGroup);
1142 stackMinBarColumn = Math.Min(stackMinBarColumn, stackMinBarColumnForGroup);
1143 stackMaxArea = Math.Max(stackMaxArea, stackMaxAreaForGroup);
1144 stackMinArea = Math.Min(stackMinArea, stackMinAreaForGroup);
1148 autoMaximum = Math.Max(stackMaxBarColumn,stackMaxArea);
1149 autoMinimum = Math.Min(stackMinBarColumn,stackMinArea);
1151 // IsLogarithmic axis
1152 if( axis.IsLogarithmic && autoMinimum < 1.0 )
1155 catch(System.Exception)
1157 throw (new InvalidOperationException(SR.ExceptionAxisStackedChartsDataPointsNumberMismatch));
1160 // Chart type with two y values used for scale ( bubble chart type )
1161 else if( secondYScale )
1163 autoMaximum = Common.DataManager.GetMaxYWithRadiusValue( (ChartArea)this, yAxesSeries );
1164 autoMinimum = Common.DataManager.GetMinYWithRadiusValue( (ChartArea)this, yAxesSeries );
1167 // *****************************
1168 // Non Stacked Chart Types
1169 // *****************************
1172 // Check if any series in the area has ExtraYValuesConnectedToYAxis flag set
1173 bool extraYValuesConnectedToYAxis = false;
1174 if(this.Common != null && this.Common.Chart != null)
1176 foreach(Series series in this.Common.Chart.Series)
1178 if(series.ChartArea == ((ChartArea)this).Name)
1180 IChartType charType = Common.ChartTypeRegistry.GetChartType( series.ChartTypeName );
1181 if(charType != null && charType.ExtraYValuesConnectedToYAxis)
1183 extraYValuesConnectedToYAxis = true;
1190 // The first Chart type can have many Y values (Stock Chart, Range Chart)
1191 if( extraYValuesConnectedToYAxis )
1193 Common.DataManager.GetMinMaxYValue(out autoMinimum, out autoMaximum, yAxesSeries );
1196 { // The first Chart type can have only one Y value
1197 Common.DataManager.GetMinMaxYValue(0, out autoMinimum, out autoMaximum, yAxesSeries );
1202 // Store Minimum and maximum from data. There is no
1203 // reason to calculate this values every time.
1204 axis.maximumFromData = autoMaximum;
1205 axis.minimumFromData = autoMinimum;
1206 axis.refreshMinMaxFromData = false;
1208 // Make extra test for stored minimum and maximum values
1209 // from data. If Number of points is different then data
1210 // source is changed. That means that we should read
1212 axis.numberOfPointsInAllSeries = currentPointsNumber;
1217 /// Splits a single array of series names into multiple arrays
1218 /// based on the stacked group name.
1220 /// <param name="seriesNames">Array of series name to split.</param>
1221 /// <returns>An array list that contains sub-arrays of series names split by group name.</returns>
1222 private ArrayList SplitSeriesInStackedGroups(string[] seriesNames)
1224 Hashtable groupsHashTable = new Hashtable();
1225 foreach(string seriesName in seriesNames)
1227 // Get series object
1228 Series series = this.Common.Chart.Series[seriesName];
1230 // NOTE: Fix for issue #6716
1231 // Double check that series supports stacked group feature
1232 string groupName = string.Empty;
1233 if(StackedColumnChart.IsSeriesStackGroupNameSupported(series))
1235 // Get stacked group name (empty string by default)
1236 groupName = StackedColumnChart.GetSeriesStackGroupName(series);
1239 // Check if this group was alreday added in to the hashtable
1240 if (groupsHashTable.ContainsKey(groupName))
1242 ArrayList list = (ArrayList)groupsHashTable[groupName];
1243 list.Add(seriesName);
1247 ArrayList list = new ArrayList();
1248 list.Add(seriesName);
1249 groupsHashTable.Add(groupName, list);
1253 // Convert results to a list that contains array of strings
1254 ArrayList result = new ArrayList();
1255 foreach(DictionaryEntry entry in groupsHashTable)
1257 ArrayList list = (ArrayList)entry.Value;
1261 string[] stringArray = new String[list.Count];
1262 foreach(string str in list)
1264 stringArray[index++] = str;
1266 result.Add(stringArray);
1276 /// Find number of points for all series
1278 /// <returns>Number of points</returns>
1279 private int GetNumberOfAllPoints()
1281 int numOfPoints = 0;
1282 foreach( Series series in Common.DataManager.Series )
1284 numOfPoints += series.Points.Count;
1291 /// This method sets default minimum and maximum values from
1292 /// indexes. This case is used if all X values in a series
1293 /// have 0 value or IsXValueIndexed flag is set.
1295 /// <param name="axis">Axis</param>
1296 private void SetDefaultFromIndexes( Axis axis )
1298 // Adjust margin for side-by-side charts like column
1299 axis.SetTempAxisOffset( );
1302 AxisType type = AxisType.Primary;
1303 if( axis.axisType == AxisName.X2 || axis.axisType == AxisName.Y2 )
1305 type = AxisType.Secondary;
1308 // The maximum is equal to the number of data points.
1309 double autoMaximum = Common.DataManager.GetNumberOfPoints( GetXAxesSeries( type, axis.SubAxisName ).ToArray() );
1310 double autoMinimum = 0.0;
1312 // Axis margin used only for zooming
1313 axis.marginView = 0.0;
1314 if( axis.margin == 100 )
1315 axis.marginView = 1.0;
1317 // If minimum and maximum are same margin always exist.
1318 if( autoMaximum + axis.margin/100 == autoMinimum - axis.margin/100 + 1 )
1320 // Set Maximum Number.
1321 axis.SetAutoMaximum( autoMaximum + 1 );
1322 axis.SetAutoMinimum( autoMinimum );
1326 // Set Maximum Number.
1327 axis.SetAutoMaximum( autoMaximum + axis.margin/100 );
1328 axis.SetAutoMinimum( autoMinimum - axis.margin/100 + 1 );
1331 // Find the interval. If the nuber of points
1332 // is less then 10 interval is 1.
1333 double axisInterval;
1335 if( axis.ViewMaximum - axis.ViewMinimum <= 10 )
1341 axisInterval = axis.CalcInterval( ( axis.ViewMaximum - axis.ViewMinimum ) / 5 );
1344 ChartArea area = (ChartArea)this;
1345 if( area.Area3DStyle.Enable3D && !double.IsNaN(axis.interval3DCorrection) )
1347 axisInterval = Math.Ceiling( axisInterval / axis.interval3DCorrection );
1349 axis.interval3DCorrection = double.NaN;
1352 if( axisInterval > 1.0 &&
1353 axisInterval < 4.0 &&
1354 axis.ViewMaximum - axis.ViewMinimum <= 4 )
1361 axis.SetInterval = axisInterval;
1363 // If temporary offsets were defined for the margin,
1364 // adjust offset for minor ticks and grids.
1365 if(axis.offsetTempSet)
1367 axis.minorGrid.intervalOffset -= axis.MajorGrid.GetInterval();
1368 axis.minorTickMark.intervalOffset -= axis.MajorTickMark.GetInterval();
1373 /// Sets the names of all data series which belong to
1374 /// this chart area to collection and sets a list of all
1375 /// different chart types.
1377 internal void SetData()
1379 this.SetData(true, true);
1383 /// Sets the names of all data series which belong to
1384 /// this chart area to collection and sets a list of all
1385 /// different chart types.
1387 /// <param name="initializeAxes">If set to <c>true</c> the method will initialize axes default values.</param>
1388 /// <param name="checkIndexedAligned">If set to <c>true</c> the method will check that all primary X axis series are aligned if use the IsXValueIndexed flag.</param>
1389 internal void SetData( bool initializeAxes, bool checkIndexedAligned)
1391 // Initialize chart type properties
1393 switchValueAxes = false;
1395 hundredPercent = false;
1396 hundredPercentNegative = false;
1397 chartAreaIsCurcular = false;
1398 secondYScale = false;
1400 // AxisName of the chart area already set.
1401 bool typeSet = false;
1403 // Remove all elements from the collection
1404 this._series.Clear();
1406 // Add series to the collection
1407 foreach( Series series in Common.DataManager.Series )
1409 if (series.ChartArea == this.Name && series.IsVisible() && series.Points.Count > 0)
1411 this._series.Add(series.Name);
1415 // Remove all elements from the collection
1416 this.chartTypes.Clear();
1418 // Add series to the collection
1419 foreach( Series series in Common.DataManager.Series )
1421 // A item already exist.
1422 bool foundItem = false;
1423 if (series.IsVisible() && series.ChartArea==this.Name)
1425 foreach( string type in chartTypes )
1427 // AxisName already exist in the chart area
1428 if( type == series.ChartTypeName )
1433 // Add chart type to the collection of
1434 // Chart area's chart types
1438 if( Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).Stacked )
1445 if( Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).SwitchValueAxes )
1446 switchValueAxes = true;
1447 if( !Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).RequireAxes )
1448 requireAxes = false;
1449 if( Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).CircularChartArea )
1450 chartAreaIsCurcular = true;
1451 if( Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).HundredPercent )
1452 hundredPercent = true;
1453 if( Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).HundredPercentSupportNegative )
1454 hundredPercentNegative = true;
1455 if( Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).SecondYScale )
1456 secondYScale = true;
1462 if( Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).SwitchValueAxes != switchValueAxes )
1464 throw (new InvalidOperationException(SR.ExceptionChartAreaChartTypesCanNotCombine));
1468 // Series is not empty
1469 if( Common.DataManager.GetNumberOfPoints( series.Name ) != 0 )
1471 this.chartTypes.Add( series.ChartTypeName );
1477 // Check that all primary X axis series are aligned if use the IsXValueIndexed flag
1478 if (checkIndexedAligned)
1480 for (int axisIndex = 0; axisIndex <= 1; axisIndex++)
1482 List<string> seriesArray = this.GetXAxesSeries((axisIndex == 0) ? AxisType.Primary : AxisType.Secondary, string.Empty);
1483 if (seriesArray.Count > 0)
1485 bool indexed = false;
1486 string seriesNamesStr = "";
1487 foreach (string seriesName in seriesArray)
1489 seriesNamesStr = seriesNamesStr + seriesName.Replace(",", "\\,") + ",";
1490 if (Common.DataManager.Series[seriesName].IsXValueIndexed)
1500 Common.DataManipulator.CheckXValuesAlignment(
1501 Common.DataManipulator.ConvertToSeriesArray(seriesNamesStr.TrimEnd(','), false));
1505 throw (new ArgumentException(SR.ExceptionAxisSeriesNotAligned + e.Message));
1513 // Set default min, max etc.
1514 SetDefaultAxesValues();
1519 /// Returns names of all series, which belong to this chart area
1520 /// and have same chart type.
1522 /// <param name="chartType">Chart type</param>
1523 /// <returns>Collection with series names</returns>
1524 internal List<string> GetSeriesFromChartType( string chartType )
1527 List<string> list = new List<string>();
1529 foreach( string seriesName in _series )
1531 if( String.Compare( chartType, Common.DataManager.Series[seriesName].ChartTypeName, StringComparison.OrdinalIgnoreCase ) == 0 )
1533 // Add a series name to the collection
1534 list.Add( seriesName );
1542 /// Returns all series which belong to this chart area.
1544 /// <returns>Collection with series</returns>
1545 internal List<Series> GetSeries( )
1548 List<Series> list = new List<Series>();
1550 foreach( string seriesName in _series )
1552 list.Add(Common.DataManager.Series[seriesName]);
1559 /// Creates a list of series, which have same X axis type.
1561 /// <param name="type">Axis type</param>
1562 /// <param name="subAxisName">Sub Axis name</param>
1563 /// <returns>A list of series</returns>
1564 internal List<string> GetXAxesSeries( AxisType type, string subAxisName )
1566 // Create a new collection of series
1567 List<string> list = new List<string>();
1568 if (_series.Count == 0)
1572 // Ignore sub axis in 3D
1573 if( !this.IsSubAxesSupported )
1575 if(subAxisName.Length > 0)
1581 // Find series which have same axis type
1582 foreach( string ser in _series )
1585 if( Common.DataManager.Series[ser].XAxisType == type &&
1586 (Common.DataManager.Series[ser].XSubAxisName == subAxisName || !this.IsSubAxesSupported) )
1588 if ( Common.DataManager.Series[ser].XAxisType == type)
1591 // Add a series to the collection
1597 // If series list is empty for the sub-axis then
1598 // try using the main axis.
1599 if ( list.Count == 0 && subAxisName.Length > 0 )
1601 return GetXAxesSeries( type, string.Empty );
1605 // If primary series do not exist return secondary series
1606 // Axis should always be connected with any series.
1607 if ( list.Count == 0 )
1609 if (type == AxisType.Secondary)
1611 return GetXAxesSeries(AxisType.Primary, string.Empty);
1613 return GetXAxesSeries(AxisType.Secondary, string.Empty);
1620 /// Creates a list of series, which have same Y axis type.
1622 /// <param name="type">Axis type</param>
1623 /// <param name="subAxisName">Sub Axis name</param>
1624 /// <returns>A list of series</returns>
1625 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "subAxisName")]
1626 internal List<string> GetYAxesSeries( AxisType type, string subAxisName )
1628 // Create a new collection of series
1629 List<string> list = new List<string>();
1631 // Find series which have same axis type
1632 foreach( string ser in _series )
1634 // Get series Y axis type
1635 AxisType seriesYAxisType = Common.DataManager.Series[ser].YAxisType;
1637 string seriesYSubAxisName = subAxisName;
1640 // NOTE: Fixes issue #6969
1641 // Ignore series settings if only Primary Y axis supported by the chart type
1642 if (Common.DataManager.Series[ser].ChartType == SeriesChartType.Radar ||
1643 Common.DataManager.Series[ser].ChartType == SeriesChartType.Polar)
1645 seriesYAxisType = AxisType.Primary;
1647 seriesYSubAxisName = string.Empty;
1653 if( seriesYAxisType == type &&
1654 (Common.DataManager.Series[ser].YSubAxisName == seriesYSubAxisName || !this.IsSubAxesSupported) )
1656 if (seriesYAxisType == type)
1659 // Add a series to the collection
1665 // If series list is empty for the sub-axis then
1666 // try using the main axis.
1667 if ( list.Count == 0 && subAxisName.Length > 0 )
1669 return GetYAxesSeries( type, string.Empty );
1673 // If primary series do not exist return secondary series
1674 // Axis should always be connected with any series.
1675 if ( list.Count == 0 && type == AxisType.Secondary )
1677 return GetYAxesSeries( AxisType.Primary, string.Empty );
1684 /// Get first series from the chart area
1686 /// <returns>Data series</returns>
1687 internal Series GetFirstSeries()
1689 if( _series.Count == 0 )
1691 throw (new InvalidOperationException(SR.ExceptionChartAreaSeriesNotFound));
1694 return Common.DataManager.Series[_series[0]];
1698 /// This method returns minimum interval between
1699 /// any two data points from series which belong
1700 /// to this chart area.
1702 /// <param name="isLogarithmic">Indicates logarithmic scale.</param>
1703 /// <param name="logarithmBase">Logarithm Base</param>
1704 /// <returns>Minimum Interval</returns>
1705 internal double GetPointsInterval(bool isLogarithmic, double logarithmBase)
1708 return GetPointsInterval( _series, isLogarithmic, logarithmBase, false, out sameInterval );
1712 /// This method returns minimum interval between
1713 /// any two data points from specified series.
1715 /// <param name="seriesList">List of series.</param>
1716 /// <param name="isLogarithmic">Indicates logarithmic scale.</param>
1717 /// <param name="logarithmBase">Base for logarithmic base</param>
1718 /// <param name="checkSameInterval">True if check for the same interval should be performed.</param>
1719 /// <param name="sameInterval">Return true if interval is the same.</param>
1720 /// <returns>Minimum Interval</returns>
1721 internal double GetPointsInterval( List<string> seriesList, bool isLogarithmic, double logarithmBase, bool checkSameInterval, out bool sameInterval )
1723 Series nullSeries = null;
1724 return GetPointsInterval(seriesList, isLogarithmic, logarithmBase, checkSameInterval, out sameInterval, out nullSeries);
1728 /// This method returns minimum interval between
1729 /// any two data points from specified series.
1731 /// <param name="seriesList">List of series.</param>
1732 /// <param name="isLogarithmic">Indicates logarithmic scale.</param>
1733 /// <param name="logarithmicBase">Logarithm Base</param>
1734 /// <param name="checkSameInterval">True if check for the same interval should be performed.</param>
1735 /// <param name="sameInterval">Return true if interval is the same.</param>
1736 /// <param name="series">Series with the smallest interval between points.</param>
1737 /// <returns>Minimum Interval</returns>
1738 internal double GetPointsInterval( List<string> seriesList, bool isLogarithmic, double logarithmicBase, bool checkSameInterval, out bool sameInterval, out Series series )
1740 long ticksInterval = long.MaxValue;
1741 int monthsInteval = 0;
1742 double previousInterval = double.MinValue;
1743 double oldInterval = Double.MaxValue;
1745 // Initialize return value
1746 sameInterval = true;
1749 // Create comma separate string of series names
1750 string seriesNames = "";
1751 if(seriesList != null)
1753 foreach( string serName in seriesList )
1755 seriesNames += serName + ",";
1759 // Do not calculate interval every time;
1760 if( checkSameInterval == false || diffIntervalAlignmentChecked == true)
1764 if( !double.IsNaN(intervalData) && _intervalSeriesList == seriesNames)
1766 sameInterval = intervalSameSize;
1767 series = _intervalSeries;
1768 return intervalData;
1773 if( !double.IsNaN(intervalLogData) && _intervalSeriesList == seriesNames)
1775 sameInterval = intervalSameSize;
1776 series = _intervalSeries;
1777 return intervalLogData;
1783 int seriesIndex = 0;
1784 Series currentSmallestSeries = null;
1785 ArrayList[] seriesXValues = new ArrayList[seriesList.Count];
1786 foreach( string ser in seriesList )
1788 Series dataSeries = Common.DataManager.Series[ ser ];
1789 bool isXValueDateTime = dataSeries.IsXValueDateTime();
1791 // Copy X values to array and prepare for sorting Sort X values.
1792 seriesXValues[seriesIndex] = new ArrayList();
1793 bool sortPoints = false;
1794 double prevXValue = double.MinValue;
1795 double curentXValue = 0.0;
1796 if(dataSeries.Points.Count > 0)
1800 prevXValue = Math.Log(dataSeries.Points[0].XValue, logarithmicBase);
1804 prevXValue = dataSeries.Points[0].XValue;
1807 foreach( DataPoint point in dataSeries.Points )
1811 curentXValue = Math.Log(point.XValue, logarithmicBase);
1815 curentXValue = point.XValue;
1818 if(prevXValue > curentXValue)
1823 seriesXValues[seriesIndex].Add(curentXValue);
1824 prevXValue = curentXValue;
1830 seriesXValues[seriesIndex].Sort();
1834 for( int point = 1; point < seriesXValues[seriesIndex].Count; point++ )
1836 // Interval between two sorted data points.
1837 double interval = Math.Abs( (double)seriesXValues[seriesIndex][ point - 1 ] - (double)seriesXValues[seriesIndex][ point ] );
1839 // Check if all intervals are same
1842 if(isXValueDateTime)
1844 if(ticksInterval == long.MaxValue)
1846 // Calculate first interval
1848 (double)seriesXValues[seriesIndex][ point - 1 ],
1849 (double)seriesXValues[seriesIndex][ point ],
1855 // Calculate current interval
1856 long curentTicksInterval = long.MaxValue;
1857 int curentMonthsInteval = 0;
1859 (double)seriesXValues[seriesIndex][ point - 1 ],
1860 (double)seriesXValues[seriesIndex][ point ],
1861 out curentMonthsInteval,
1862 out curentTicksInterval);
1864 // Compare current interval with previous
1865 if(curentMonthsInteval != monthsInteval || curentTicksInterval != ticksInterval)
1867 sameInterval = false;
1874 if( previousInterval != interval && previousInterval != double.MinValue )
1876 sameInterval = false;
1881 previousInterval = interval;
1883 // If not minimum interval keep the old one
1884 if( oldInterval > interval && interval != 0)
1886 oldInterval = interval;
1887 currentSmallestSeries = dataSeries;
1894 // If interval is not the same check if points from all series are aligned
1895 this.diffIntervalAlignmentChecked = false;
1896 if( checkSameInterval && !sameInterval && seriesXValues.Length > 1)
1898 bool sameXValue = false;
1899 this.diffIntervalAlignmentChecked = true;
1901 // All X values must be same
1903 foreach(ArrayList xList in seriesXValues)
1905 for(int pointIndex = 0; pointIndex < xList.Count && !sameXValue; pointIndex++)
1907 double xValue = (double)xList[pointIndex];
1909 // Loop through all other lists and see if point is there
1910 for(int index = listIndex + 1; index < seriesXValues.Length && !sameXValue; index++)
1912 if( (pointIndex < seriesXValues[index].Count && (double)seriesXValues[index][pointIndex] == xValue) ||
1913 seriesXValues[index].Contains(xValue))
1925 // Use side-by-side if at least one xommon X value between eries found
1928 sameInterval = true;
1933 // Interval not found. Interval is 1.
1934 if( oldInterval == Double.MaxValue)
1939 intervalSameSize = sameInterval;
1942 intervalData = oldInterval;
1943 _intervalSeries = currentSmallestSeries;
1944 series = _intervalSeries;
1945 _intervalSeriesList = seriesNames;
1946 return intervalData;
1950 intervalLogData = oldInterval;
1951 _intervalSeries = currentSmallestSeries;
1952 series = _intervalSeries;
1953 _intervalSeriesList = seriesNames;
1954 return intervalLogData;
1959 /// Calculates the difference between two values in years, months, days, ...
1961 /// <param name="value1">First value.</param>
1962 /// <param name="value2">Second value.</param>
1963 /// <param name="monthsInteval">Interval in months.</param>
1964 /// <param name="ticksInterval">Interval in ticks.</param>
1965 private void GetDateInterval(double value1, double value2, out int monthsInteval, out long ticksInterval)
1967 // Convert values to dates
1968 DateTime date1 = DateTime.FromOADate(value1);
1969 DateTime date2 = DateTime.FromOADate(value2);
1971 // Calculate months difference
1972 monthsInteval = date2.Month - date1.Month;
1973 monthsInteval += (date2.Year - date1.Year) * 12;
1975 // Calculate interval in ticks for days, hours, ...
1977 ticksInterval += (date2.Day - date1.Day) * TimeSpan.TicksPerDay;
1978 ticksInterval += (date2.Hour - date1.Hour) * TimeSpan.TicksPerHour;
1979 ticksInterval += (date2.Minute - date1.Minute) * TimeSpan.TicksPerMinute;
1980 ticksInterval += (date2.Second - date1.Second) * TimeSpan.TicksPerSecond;
1981 ticksInterval += (date2.Millisecond - date1.Millisecond) * TimeSpan.TicksPerMillisecond;