1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
- //
- // Purpose: ChartAreaAxes is base class of Chart Area class.
- // This class searches for all series, which belongs
- // to this chart area and sets axes minimum and
- // maximum values using data. This class also checks
- // for chart types, which belong to this chart area
- // and prepare axis scale according to them (Stacked
- // chart types have different max and min values).
- // This class recognizes indexed values and prepares
- // axes for them.
- //
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using FastReport.DataVisualization.Charting.ChartTypes;
- namespace FastReport.DataVisualization.Charting
- {
- /// <summary>
- /// ChartAreaAxes class represents axes (X, Y, X2 and Y2) in the chart area.
- /// It contains methods that collect statistical information on the series data and
- /// other axes related methods.
- /// </summary>
- public partial class ChartArea
- {
- #region Fields
- // Axes which belong to this Chart Area
- internal Axis axisY = null;
- internal Axis axisX = null;
- internal Axis axisX2 = null;
- internal Axis axisY2 = null;
-
- // Array of series which belong to this chart area
- private List<string> _series = new List<string>();
- // Array of chart types which belong to this chart area
- internal ArrayList chartTypes = new ArrayList();
- /// <summary>
- /// List of series names that last interval numbers where cashed for
- /// </summary>
- private string _intervalSeriesList = "";
- // Minimum interval between two data points for all
- // series which belong to this chart area.
- internal double intervalData = double.NaN;
- // Minimum interval between two data points for all
- // series which belong to this chart area.
- // IsLogarithmic version of the interval.
- internal double intervalLogData = double.NaN;
- // Series with minimum interval between two data points for all
- // series which belong to this chart area.
- private Series _intervalSeries = null;
- // Indicates that points are located through equal X intervals
- internal bool intervalSameSize = false;
- // Indicates that points alignment checked
- internal bool diffIntervalAlignmentChecked = false;
- // Chart Area contains stacked chart types
- internal bool stacked = false;
- // Chart type with two y values used for scale ( bubble chart type )
- internal bool secondYScale = false;
- // The X and Y axes are switched
- internal bool switchValueAxes = false;
- // True for all chart types, which have axes. False for doughnut and pie chart.
- internal bool requireAxes = true;
- // Indicates that chart area has circular shape (like in radar or polar chart)
- internal bool chartAreaIsCurcular = false;
- // Chart Area contains 100 % stacked chart types
- internal bool hundredPercent = false;
- // Chart Area contains 100 % stacked chart types
- internal bool hundredPercentNegative = false;
- #endregion
- #region Internal properties
- /// <summary>
- /// True if sub axis supported on this chart area
- /// </summary>
- internal bool IsSubAxesSupported
- {
- get
- {
- if(((ChartArea)this).Area3DStyle.Enable3D ||
- ((ChartArea)this).chartAreaIsCurcular)
- {
- return false;
- }
- return true;
- }
- }
- /// <summary>
- /// Data series which belongs to this chart area.
- /// </summary>
- internal List<string> Series
- {
- get
- {
- return _series;
- }
- }
- /// <summary>
- /// Chart types which belongs to this chart area.
- /// </summary>
- internal ArrayList ChartTypes
- {
- get
- {
- return chartTypes;
- }
- }
- #endregion
- #region Methods
- /// <summary>
- /// Gets main or sub axis from the chart area.
- /// </summary>
- /// <param name="axisName">Axis name. NOTE: This parameter only defines X or Y axis.
- /// Second axisType parameter is used to select primary or secondary axis. </param>
- /// <param name="axisType">Axis type.</param>
- /// <param name="subAxisName">Sub-axis name or empty string.</param>
- /// <returns>Main or sub axis of the chart area.</returns>
- internal Axis GetAxis(AxisName axisName, AxisType axisType, string subAxisName)
- {
- // Ignore sub axis in 3D
- if( ((ChartArea)this).Area3DStyle.Enable3D)
- {
- subAxisName = string.Empty;
- }
- if(axisName == AxisName.X || axisName == AxisName.X2)
- {
- if(axisType == AxisType.Primary)
- {
- return ((ChartArea)this).AxisX.GetSubAxis(subAxisName);
- }
- return ((ChartArea)this).AxisX2.GetSubAxis(subAxisName);
- }
- else
- {
- if(axisType == AxisType.Primary)
- {
- return ((ChartArea)this).AxisY.GetSubAxis(subAxisName);
- }
- return ((ChartArea)this).AxisY2.GetSubAxis(subAxisName);
- }
- }
- /// <summary>
- /// Sets default axis values for all different chart type
- /// groups. Chart type groups are sets of chart types.
- /// </summary>
- internal void SetDefaultAxesValues( )
- {
- // The X and Y axes are switched ( Bar chart, stacked bar ... )
- if( switchValueAxes )
- {
- // Set axis positions
- axisY.AxisPosition = AxisPosition.Bottom;
- axisX.AxisPosition = AxisPosition.Left;
- axisX2.AxisPosition = AxisPosition.Right;
- axisY2.AxisPosition = AxisPosition.Top;
- }
- else
- {
- // Set axis positions
- axisY.AxisPosition = AxisPosition.Left;
- axisX.AxisPosition = AxisPosition.Bottom;
- axisX2.AxisPosition = AxisPosition.Top;
- axisY2.AxisPosition = AxisPosition.Right;
- }
- // Reset opposite Axes field. This cashing
- // value is used for optimization.
- foreach( Axis axisItem in ((ChartArea)this).Axes )
- {
- axisItem.oppositeAxis = null;
- #if SUBAXES
- foreach( SubAxis subAxisItem in axisItem.SubAxes )
- {
- subAxisItem.m_oppositeAxis = null;
- }
- #endif // SUBAXES
- }
-
- // ***********************
- // Primary X Axes
- // ***********************
- // Find the number of series which belong to this axis
- if (this.chartAreaIsCurcular)
- {
- // Set axis Maximum/Minimum and Interval for circular chart
- axisX.SetAutoMaximum(360.0);
- axisX.SetAutoMinimum(0.0);
- axisX.SetInterval = Math.Abs(axisX.maximum - axisX.minimum) / 12.0;
- }
- else
- {
- SetDefaultFromIndexesOrData(axisX, AxisType.Primary);
- }
- #if SUBAXES
- // ***********************
- // Primary X Sub-Axes
- // ***********************
- foreach(SubAxis subAxis in axisX.SubAxes)
- {
- SetDefaultFromIndexesOrData(subAxis, AxisType.Primary);
- }
- #endif // SUBAXES
- // ***********************
- // Secondary X Axes
- // ***********************
- SetDefaultFromIndexesOrData(axisX2, AxisType.Secondary);
- #if SUBAXES
- // ***********************
- // Secondary X Sub-Axes
- // ***********************
- foreach(SubAxis subAxis in axisX2.SubAxes)
- {
- SetDefaultFromIndexesOrData(subAxis, AxisType.Secondary);
- }
- #endif // SUBAXES
- // ***********************
- // Primary Y axis
- // ***********************
- if( GetYAxesSeries( AxisType.Primary, string.Empty ).Count != 0 )
- {
- // Find minimum and maximum from Y values.
- SetDefaultFromData( axisY );
- axisY.EstimateAxis();
- }
- #if SUBAXES
- // ***********************
- // Primary Y Sub-Axes
- // ***********************
- foreach(SubAxis subAxis in axisY.SubAxes)
- {
- // Find the number of series which belong to this axis
- if( GetYAxesSeries( AxisType.Primary, subAxis.SubAxisName ).Count != 0 )
- {
- // Find minimum and maximum from Y values.
- SetDefaultFromData( subAxis );
- subAxis.EstimateAxis();
- }
- }
- #endif // SUBAXES
- // ***********************
- // Secondary Y axis
- // ***********************
- if( GetYAxesSeries( AxisType.Secondary, string.Empty ).Count != 0 )
- {
- // Find minimum and maximum from Y values.
- SetDefaultFromData( axisY2 );
- axisY2.EstimateAxis();
- }
- #if SUBAXES
- // ***********************
- // Secondary Y Sub-Axes
- // ***********************
- foreach(SubAxis subAxis in axisY2.SubAxes)
- {
- // Find the number of series which belong to this axis
- if( GetYAxesSeries( AxisType.Secondary, subAxis.SubAxisName ).Count != 0 )
- {
- // Find minimum and maximum from Y values.
- SetDefaultFromData( subAxis );
- subAxis.EstimateAxis();
- }
- }
- #endif // SUBAXES
- // Sets axis position. Axis position depends
- // on crossing and reversed value.
- axisX.SetAxisPosition();
- axisX2.SetAxisPosition();
- axisY.SetAxisPosition();
- axisY2.SetAxisPosition();
- // Enable axes, which are
- // used in data series.
- this.EnableAxes();
-
- // Get scale break segments
- Axis[] axesYArray = new Axis[] { axisY, axisY2 };
- foreach(Axis currentAxis in axesYArray)
- {
- // Get automatic scale break segments
- currentAxis.ScaleBreakStyle.GetAxisSegmentForScaleBreaks(currentAxis.ScaleSegments);
- // Make sure axis scale do not exceed segments scale
- if(currentAxis.ScaleSegments.Count > 0)
- {
- // Save flag that scale segments are used
- currentAxis.scaleSegmentsUsed = true;
- if(currentAxis.minimum < currentAxis.ScaleSegments[0].ScaleMinimum)
- {
- currentAxis.minimum = currentAxis.ScaleSegments[0].ScaleMinimum;
- }
- if(currentAxis.minimum > currentAxis.ScaleSegments[currentAxis.ScaleSegments.Count - 1].ScaleMaximum)
- {
- currentAxis.minimum = currentAxis.ScaleSegments[currentAxis.ScaleSegments.Count - 1].ScaleMaximum;
- }
- }
- }
- bool useScaleSegments = false;
- // Fill Labels
- Axis[] axesArray = new Axis[] { axisX, axisX2, axisY, axisY2 };
- foreach(Axis currentAxis in axesArray)
- {
- useScaleSegments = (currentAxis.ScaleSegments.Count > 0);
- if(!useScaleSegments)
- {
- currentAxis.FillLabels(true);
- }
- else
- {
- bool removeLabels = true;
- int segmentIndex = 0;
- foreach(AxisScaleSegment scaleSegment in currentAxis.ScaleSegments)
- {
- scaleSegment.SetTempAxisScaleAndInterval();
- currentAxis.FillLabels(removeLabels);
- removeLabels = false;
- scaleSegment.RestoreAxisScaleAndInterval();
- // Remove last label for all segments except of the last
- if(segmentIndex < (currentAxis.ScaleSegments.Count - 1) &&
- currentAxis.CustomLabels.Count > 0)
- {
- currentAxis.CustomLabels.RemoveAt(currentAxis.CustomLabels.Count - 1);
- }
- ++segmentIndex;
- }
- }
- }
- foreach (Axis currentAxis in axesArray)
- {
- currentAxis.PostFillLabels();
- }
- }
- /// <summary>
- /// Sets the axis defaults.
- /// If the at least one of the series bound to this axis is Indexed then the defaults are set using the SetDefaultsFromIndexes().
- /// Otherwise the SetDefaultFromData() is used.
- /// </summary>
- /// <param name="axis">Axis to process</param>
- /// <param name="axisType">Axis type</param>
- private void SetDefaultFromIndexesOrData(Axis axis, AxisType axisType)
- {
- //Get array of the series that are linked to this axis
- List<string> axisSeriesNames = GetXAxesSeries(axisType, axis.SubAxisName);
- // VSTS: 196381
- // before this change: If we find one indexed series we will treat all series as indexed.
- // after this change : We will assume that all series are indexed.
- // If we find one non indexed series we will treat all series as non indexed.
- bool indexedSeries = true;
- // DT comments 1:
- // If we have mix of indexed with non-indexed series
- // enforce all indexed series as non-indexed;
- // The result of mixed type of series will be more natural
- // and easy to detect the problem - all datapoints of indexed
- // series will be displayed on zero position.
- //=====================================
- // bool nonIndexedSeries = false;
- //=======================================
- //Loop through the series looking for a indexed one
- foreach(string seriesName in axisSeriesNames)
- {
- // Get series
- Series series = Common.DataManager.Series[seriesName];
- // Check if series is indexed
- if (!ChartHelper.IndexedSeries(series))
- {
- // found one nonindexed series - we will treat all series as non indexed.
- indexedSeries = false;
- break;
- }
- // DT comments 2
- //else
- //{
- // nonIndexedSeries = true;
- //}
- }
- //DT comments 3
- //if (!indexedSeries && nonIndexedSeries)
- //{
- // foreach (string seriesName in axisSeriesNames)
- // {
- // // Get series
- // Series series = Common.DataManager.Series[seriesName];
- // series.xValuesZeros = false;
- // }
- //}
- if (indexedSeries)
- {
- if (axis.IsLogarithmic)
- {
- throw (new InvalidOperationException(SR.ExceptionChartAreaAxisScaleLogarithmicUnsuitable));
- }
- //Set axis defaults from the indexed series
- SetDefaultFromIndexes(axis);
- //We are done...
- return;
- }
- // If haven't found any indexed series -> Set axis defaults from the series data
- SetDefaultFromData(axis);
- axis.EstimateAxis();
- }
- /// <summary>
- /// Enable axes, which are
- /// used in chart area data series.
- /// </summary>
- private void EnableAxes()
- {
- if( _series == null )
- {
- return;
- }
- bool activeX = false;
- bool activeY = false;
- bool activeX2 = false;
- bool activeY2 = false;
- // Data series from this chart area
- foreach( string ser in _series )
- {
- Series dataSeries = Common.DataManager.Series[ ser ];
- // X axes
- if( dataSeries.XAxisType == AxisType.Primary )
- {
- activeX = true;
- #if SUBAXES
- this.Activate( axisX, true, dataSeries.XSubAxisName );
- #else
- this.Activate( axisX, true );
- #endif // SUBAXES
- }
- else
- {
- activeX2 = true;
- #if SUBAXES
- this.Activate( axisX2, true, dataSeries.XSubAxisName );
- #else
- this.Activate( axisX2, true );
- #endif // SUBAXES
- }
- // Y axes
- if( dataSeries.YAxisType == AxisType.Primary )
- {
- activeY = true;
- #if SUBAXES
- this.Activate( axisY, true, dataSeries.YSubAxisName );
- #else
- this.Activate( axisY, true );
- #endif // SUBAXES
- }
- else
- {
- activeY2 = true;
- #if SUBAXES
- this.Activate( axisY2, true, dataSeries.YSubAxisName );
- #else
- this.Activate( axisY2, true );
- #endif // SUBAXES
- }
- }
- #if SUBAXES
- // Enable Axes
- if(!activeX)
- this.Activate( axisX, false, string.Empty );
- if(!activeY)
- this.Activate( axisY, false, string.Empty );
- if(!activeX2)
- this.Activate( axisX2, false, string.Empty );
- if(!activeY2)
- this.Activate( axisY2, false, string.Empty );
- #else // SUBAXES
- // Enable Axes
- if(!activeX)
- this.Activate( axisX, false);
- if(!activeY)
- this.Activate( axisY, false);
- if(!activeX2)
- this.Activate( axisX2, false);
- if(!activeY2)
- this.Activate( axisY2, false);
- #endif // SUBAXES
- }
- #if SUBAXES
- /// <summary>
- /// Enable axis.
- /// </summary>
- /// <param name="axis">Axis.</param>
- /// <param name="active">True if axis is active.</param>
- /// <param name="subAxisName">Sub axis name to activate.</param>
- private void Activate( Axis axis, bool active, string subAxisName )
- {
- // Auto-Enable axis
- if( axis.autoEnabled == true )
- {
- axis.enabled = active;
- }
- // Auto-Enable sub axes
- if(subAxisName.Length > 0)
- {
- SubAxis subAxis = axis.SubAxes.FindByName(subAxisName);
- if(subAxis != null)
- {
- if( subAxis.autoEnabled == true )
- {
- subAxis.enabled = active;
- }
- }
- }
- }
- #else
- /// <summary>
- /// Enable axis.
- /// </summary>
- /// <param name="axis">Axis.</param>
- /// <param name="active">True if axis is active.</param>
- private void Activate( Axis axis, bool active )
- {
- if( axis.autoEnabled == true )
- {
- axis.enabled = active;
- }
- }
- #endif // SUBAXES
- /// <summary>
- /// Check if all data points from series in
- /// this chart area are empty.
- /// </summary>
- /// <returns>True if all points are empty</returns>
- bool AllEmptyPoints()
- {
- // Data series from this chart area
- foreach( string seriesName in this._series )
- {
- Series dataSeries = Common.DataManager.Series[ seriesName ];
- // Data point loop
- foreach( DataPoint point in dataSeries.Points )
- {
- if( !point.IsEmpty )
- {
- return false;
- }
- }
- }
- return true;
- }
- /// <summary>
- /// This method sets default minimum and maximum
- /// values from values in the data manager. This
- /// case is used if X values are not equal to 0 or IsXValueIndexed flag is set.
- /// </summary>
- /// <param name="axis">Axis</param>
- private void SetDefaultFromData( Axis axis )
- {
- #if SUBAXES
- // Process all sub-axes
- if(!axis.IsSubAxis)
- {
- foreach(SubAxis subAxis in axis.SubAxes)
- {
- this.SetDefaultFromData( subAxis );
- }
- }
- #endif // SUBAXES
- // Used for scrolling with logarithmic axes.
- if( !Double.IsNaN(axis.ScaleView.Position) &&
- !Double.IsNaN(axis.ScaleView.Size) &&
- !axis.refreshMinMaxFromData &&
- axis.IsLogarithmic )
- {
- return;
- }
- // Get minimum and maximum from data source
- double autoMaximum;
- double autoMinimum;
- this.GetValuesFromData( axis, out autoMinimum, out autoMaximum );
- // ***************************************************
- // This part of code is used to add a margin to the
- // axis and to set minimum value to zero if
- // IsStartedFromZero property is used. There is special
- // code for logarithmic scale, which will set minimum
- // to one instead of zero.
- // ***************************************************
- // The minimum and maximum values from data manager don’t exist.
- if( axis.enabled &&
- ( (axis.AutoMaximum || double.IsNaN( axis.Maximum )) && (autoMaximum == Double.MaxValue || autoMaximum == Double.MinValue)) ||
- ( (axis.AutoMinimum || double.IsNaN( axis.Minimum )) && (autoMinimum == Double.MaxValue || autoMinimum == Double.MinValue )) )
- {
- if( this.AllEmptyPoints() )
- {
- // Supress exception and use predefined min & max
- autoMaximum = 8.0;
- autoMinimum = 1.0;
- }
- else
- {
- if(!this.Common.ChartPicture.SuppressExceptions)
- {
- throw (new InvalidOperationException(SR.ExceptionAxisMinimumMaximumInvalid));
- }
- }
- }
- // Axis margin used for zooming
- axis.marginView = 0.0;
- if( axis.margin == 100 && (axis.axisType == AxisName.X || axis.axisType == AxisName.X2) )
- {
- axis.marginView = this.GetPointsInterval( false, 10 );
- }
- // If minimum and maximum are same margin always exist.
- if( autoMaximum == autoMinimum &&
- axis.Maximum == axis.Minimum )
- {
- axis.marginView = 1;
- }
- // Do not make axis margine for logarithmic axes
- if( axis.IsLogarithmic )
- {
- axis.marginView = 0.0;
- }
- // Adjust Maximum - Add a gap
- if( axis.AutoMaximum )
- {
- // Add a Gap for X axis
- if( !axis.roundedXValues && ( axis.axisType == AxisName.X || axis.axisType == AxisName.X2 ) )
- {
- axis.SetAutoMaximum( autoMaximum + axis.marginView );
- }
- else
- {
- if( axis.isStartedFromZero && autoMaximum < 0 )
- {
- axis.SetAutoMaximum( 0.0 );
- }
- else
- {
- axis.SetAutoMaximum( autoMaximum );
- }
- }
- }
- // Adjust Minimum - make rounded values and add a gap
- if( axis.AutoMinimum )
- {
- // IsLogarithmic axis
- if( axis.IsLogarithmic )
- {
- if( autoMinimum < 1.0 )
- {
- axis.SetAutoMinimum( autoMinimum );
- }
- else if( axis.isStartedFromZero )
- {
- axis.SetAutoMinimum( 1.0 );
- }
- else
- {
- axis.SetAutoMinimum( autoMinimum );
- }
- }
- else
- {
- if( autoMinimum > 0.0 ) // If Auto calculated Minimum value is positive
- {
- // Adjust Minimum
- if( !axis.roundedXValues && ( axis.axisType == AxisName.X || axis.axisType == AxisName.X2 ) )
- {
- axis.SetAutoMinimum( autoMinimum - axis.marginView );
- }
- // If start From Zero property is true 0 is always on the axis.
- // NOTE: Not applicable if date-time values are drawn. Fixes issue #5644
- else if( axis.isStartedFromZero &&
- !this.SeriesDateTimeType( axis.axisType, axis.SubAxisName ) )
- {
- axis.SetAutoMinimum( 0.0 );
- }
- else
- {
- axis.SetAutoMinimum( autoMinimum );
- }
- }
- else // If Auto calculated Minimum value is non positive
- {
- if( axis.axisType == AxisName.X || axis.axisType == AxisName.X2 )
- {
- axis.SetAutoMinimum( autoMinimum - axis.marginView );
- }
- else
- {
- // If start From Zero property is true 0 is always on the axis.
- axis.SetAutoMinimum( autoMinimum );
- }
- }
- }
- }
- // If maximum or minimum are not auto set value to non logarithmic
- if( axis.IsLogarithmic && axis.logarithmicConvertedToLinear )
- {
- if( !axis.AutoMinimum )
- {
- axis.minimum = axis.logarithmicMinimum;
- }
- if( !axis.AutoMaximum )
- {
- axis.maximum = axis.logarithmicMaximum;
- }
- // Min and max will take real values again if scale is logarithmic.
- axis.logarithmicConvertedToLinear = false;
- }
- // Check if Minimum == Maximum
- if(this.Common.ChartPicture.SuppressExceptions &&
- axis.maximum == axis.minimum)
- {
- axis.minimum = axis.maximum;
- axis.maximum = axis.minimum + 1.0;
- }
- }
- /// <summary>
- /// This method checks if all series in the chart area have “integer type”
- /// for specified axes, which means int, uint, long and ulong.
- /// </summary>
- /// <param name="axisName">Name of the axis</param>
- /// <param name="subAxisName">Sub axis name.</param>
- /// <returns>True if all series are integer</returns>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "subAxisName")]
- internal bool SeriesIntegerType( AxisName axisName, string subAxisName )
- {
- // Series which belong to this chart area
- foreach( string seriesName in this._series )
- {
- Series ser = Common.DataManager.Series[ seriesName ];
- // X axes type
- if( axisName == AxisName.X )
- {
- #if SUBAXES
- if( ser.XAxisType == AxisType.Primary && ser.XSubAxisName == subAxisName)
- #else //SUBAXES
- if ( ser.XAxisType == AxisType.Primary)
- #endif //SUBAXES
- {
- if(ser.XValueType != ChartValueType.Int32 &&
- ser.XValueType != ChartValueType.UInt32 &&
- ser.XValueType != ChartValueType.UInt64 &&
- ser.XValueType != ChartValueType.Int64 )
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- }
- // X axes type
- else if( axisName == AxisName.X2 )
- {
- #if SUBAXES
- if( ser.XAxisType == AxisType.Secondary && ser.XSubAxisName == subAxisName)
- #else //SUBAXES
- if ( ser.XAxisType == AxisType.Secondary)
- #endif //SUBAXES
- {
- if(ser.XValueType != ChartValueType.Int32 &&
- ser.XValueType != ChartValueType.UInt32 &&
- ser.XValueType != ChartValueType.UInt64 &&
- ser.XValueType != ChartValueType.Int64 )
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- }
- // Y axes type
- else if( axisName == AxisName.Y )
- {
- #if SUBAXES
- if( ser.YAxisType == AxisType.Primary && ser.YSubAxisName == subAxisName)
- #else //SUBAXES
- if ( ser.YAxisType == AxisType.Primary)
- #endif //SUBAXES
- {
- if(ser.YValueType != ChartValueType.Int32 &&
- ser.YValueType != ChartValueType.UInt32 &&
- ser.YValueType != ChartValueType.UInt64 &&
- ser.YValueType != ChartValueType.Int64 )
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- }
- else if( axisName == AxisName.Y2 )
- {
- #if SUBAXES
- if( ser.YAxisType == AxisType.Secondary && ser.YSubAxisName == subAxisName)
- #else //SUBAXES
- if ( ser.YAxisType == AxisType.Secondary)
- #endif //SUBAXES
- {
- if(ser.YValueType != ChartValueType.Int32 &&
- ser.YValueType != ChartValueType.UInt32 &&
- ser.YValueType != ChartValueType.UInt64 &&
- ser.YValueType != ChartValueType.Int64 )
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- }
- }
- return false;
- }
- /// <summary>
- /// This method checks if all series in the chart area have “date-time type”
- /// for specified axes.
- /// </summary>
- /// <param name="axisName">Name of the axis</param>
- /// <param name="subAxisName">Sub axis name.</param>
- /// <returns>True if all series are date-time.</returns>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "subAxisName")]
- internal bool SeriesDateTimeType( AxisName axisName, string subAxisName )
- {
- // Series which belong to this chart area
- foreach( string seriesName in this._series )
- {
- Series ser = Common.DataManager.Series[ seriesName ];
- // X axes type
- if( axisName == AxisName.X )
- {
- #if SUBAXES
- if( ser.XAxisType == AxisType.Primary && ser.XSubAxisName == subAxisName)
- #else //SUBAXES
- if ( ser.XAxisType == AxisType.Primary)
- #endif //SUBAXES
- {
- if(ser.XValueType != ChartValueType.Date &&
- ser.XValueType != ChartValueType.DateTime &&
- ser.XValueType != ChartValueType.Time &&
- ser.XValueType != ChartValueType.DateTimeOffset)
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- }
- // X axes type
- else if( axisName == AxisName.X2 )
- {
- #if SUBAXES
- if( ser.XAxisType == AxisType.Secondary && ser.XSubAxisName == subAxisName)
- #else //SUBAXES
- if ( ser.XAxisType == AxisType.Secondary)
- #endif //SUBAXES
- {
- if(ser.XValueType != ChartValueType.Date &&
- ser.XValueType != ChartValueType.DateTime &&
- ser.XValueType != ChartValueType.Time &&
- ser.XValueType != ChartValueType.DateTimeOffset)
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- }
- // Y axes type
- else if( axisName == AxisName.Y )
- {
- #if SUBAXES
- if( ser.YAxisType == AxisType.Primary && ser.YSubAxisName == subAxisName)
- #else //SUBAXES
- if ( ser.YAxisType == AxisType.Primary)
- #endif //SUBAXES
- {
- if(ser.YValueType != ChartValueType.Date &&
- ser.YValueType != ChartValueType.DateTime &&
- ser.YValueType != ChartValueType.Time &&
- ser.YValueType != ChartValueType.DateTimeOffset)
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- }
- else if( axisName == AxisName.Y2 )
- {
- #if SUBAXES
- if( ser.YAxisType == AxisType.Secondary && ser.YSubAxisName == subAxisName)
- #else //SUBAXES
- if ( ser.YAxisType == AxisType.Secondary)
- #endif //SUBAXES
- {
- if(ser.YValueType != ChartValueType.Date &&
- ser.YValueType != ChartValueType.DateTime &&
- ser.YValueType != ChartValueType.Time &&
- ser.YValueType != ChartValueType.DateTimeOffset)
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- }
- }
- return false;
- }
- /// <summary>
- /// This method calculates minimum and maximum from data series.
- /// </summary>
- /// <param name="axis">Axis which is used to find minimum and maximum</param>
- /// <param name="autoMinimum">Minimum value from data.</param>
- /// <param name="autoMaximum">Maximum value from data.</param>
- private void GetValuesFromData( Axis axis, out double autoMinimum, out double autoMaximum )
- {
- // Get number of points in series
- int currentPointsNumber = this.GetNumberOfAllPoints();
- if( !axis.refreshMinMaxFromData &&
- !double.IsNaN(axis.minimumFromData) &&
- !double.IsNaN(axis.maximumFromData) &&
- axis.numberOfPointsInAllSeries == currentPointsNumber )
- {
- autoMinimum = axis.minimumFromData;
- autoMaximum = axis.maximumFromData;
- return;
- }
- // Set Axis type
- AxisType type = AxisType.Primary;
- if( axis.axisType == AxisName.X2 || axis.axisType == AxisName.Y2 )
- {
- type = AxisType.Secondary;
- }
- // Creates a list of series, which have same X axis type.
- string [] xAxesSeries = GetXAxesSeries(type, axis.SubAxisName).ToArray();
- // Creates a list of series, which have same Y axis type.
- string [] yAxesSeries = GetYAxesSeries( type, axis.SubAxisName ).ToArray();
- // Get auto maximum and auto minimum value
- if( axis.axisType == AxisName.X2 || axis.axisType == AxisName.X ) // X axis type is used (X or X2)
- {
- if( stacked ) // Chart area has a stacked chart types
- {
- try
- {
- Common.DataManager.GetMinMaxXValue(out autoMinimum, out autoMaximum, xAxesSeries );
- }
- catch(System.Exception)
- {
- throw (new InvalidOperationException(SR.ExceptionAxisStackedChartsDataPointsNumberMismatch));
- }
- }
- // Chart type with two y values used for scale ( bubble chart type )
- else if( secondYScale )
- {
- autoMaximum = Common.DataManager.GetMaxXWithRadiusValue( (ChartArea)this, xAxesSeries );
- autoMinimum = Common.DataManager.GetMinXWithRadiusValue( (ChartArea)this, xAxesSeries );
- ChartValueType valueTypes = Common.DataManager.Series[xAxesSeries[0]].XValueType;
- if( valueTypes != ChartValueType.Date &&
- valueTypes != ChartValueType.DateTime &&
- valueTypes != ChartValueType.Time &&
- valueTypes != ChartValueType.DateTimeOffset )
- {
- axis.roundedXValues = true;
- }
- }
- else
- {
- Common.DataManager.GetMinMaxXValue(out autoMinimum, out autoMaximum, xAxesSeries );
- }
- }
- else // Y axis type is used (Y or Y2)
- {
-
- // *****************************
- // Stacked Chart AxisName
- // *****************************
- if( stacked ) // Chart area has a stacked chart types
- {
- try
- {
- if(hundredPercent) // It's a hundred percent stacked chart
- {
- autoMaximum = Common.DataManager.GetMaxHundredPercentStackedYValue(hundredPercentNegative, yAxesSeries );
- autoMinimum = Common.DataManager.GetMinHundredPercentStackedYValue(hundredPercentNegative, yAxesSeries );
- }
- else
- {
- // If stacked groupes are used Min/Max range must calculated
- // for each group seperatly.
- double stackMaxBarColumn = double.MinValue;
- double stackMinBarColumn = double.MaxValue;
- double stackMaxArea = double.MinValue;
- double stackMinArea = double.MaxValue;
- // Split series by group names
- ArrayList stackedGroups = this.SplitSeriesInStackedGroups(yAxesSeries);
- foreach(string[] groupSeriesNames in stackedGroups)
- {
- // For stacked bar and column
- double stackMaxBarColumnForGroup = Common.DataManager.GetMaxStackedYValue(0, groupSeriesNames );
- double stackMinBarColumnForGroup = Common.DataManager.GetMinStackedYValue(0, groupSeriesNames );
- // For stacked area
- double stackMaxAreaForGroup = Common.DataManager.GetMaxUnsignedStackedYValue(0, groupSeriesNames );
- double stackMinAreaForGroup = Common.DataManager.GetMinUnsignedStackedYValue(0, groupSeriesNames );
- // Select minimum/maximum
- stackMaxBarColumn = Math.Max(stackMaxBarColumn, stackMaxBarColumnForGroup);
- stackMinBarColumn = Math.Min(stackMinBarColumn, stackMinBarColumnForGroup);
- stackMaxArea = Math.Max(stackMaxArea, stackMaxAreaForGroup);
- stackMinArea = Math.Min(stackMinArea, stackMinAreaForGroup);
- }
-
- autoMaximum = Math.Max(stackMaxBarColumn,stackMaxArea);
- autoMinimum = Math.Min(stackMinBarColumn,stackMinArea);
- }
- // IsLogarithmic axis
- if( axis.IsLogarithmic && autoMinimum < 1.0 )
- autoMinimum = 1.0;
- }
- catch(System.Exception)
- {
- throw (new InvalidOperationException(SR.ExceptionAxisStackedChartsDataPointsNumberMismatch));
- }
- }
- // Chart type with two y values used for scale ( bubble chart type )
- else if( secondYScale )
- {
- autoMaximum = Common.DataManager.GetMaxYWithRadiusValue( (ChartArea)this, yAxesSeries );
- autoMinimum = Common.DataManager.GetMinYWithRadiusValue( (ChartArea)this, yAxesSeries );
- }
- // *****************************
- // Non Stacked Chart Types
- // *****************************
- else
- {
- // Check if any series in the area has ExtraYValuesConnectedToYAxis flag set
- bool extraYValuesConnectedToYAxis = false;
- if(this.Common != null && this.Common.Chart != null)
- {
- foreach(Series series in this.Common.Chart.Series)
- {
- if(series.ChartArea == ((ChartArea)this).Name)
- {
- IChartType charType = Common.ChartTypeRegistry.GetChartType( series.ChartTypeName );
- if(charType != null && charType.ExtraYValuesConnectedToYAxis)
- {
- extraYValuesConnectedToYAxis = true;
- break;
- }
- }
- }
- }
- // The first Chart type can have many Y values (Stock Chart, Range Chart)
- if( extraYValuesConnectedToYAxis )
- {
- Common.DataManager.GetMinMaxYValue(out autoMinimum, out autoMaximum, yAxesSeries );
- }
- else
- { // The first Chart type can have only one Y value
- Common.DataManager.GetMinMaxYValue(0, out autoMinimum, out autoMaximum, yAxesSeries );
- }
- }
- }
- // Store Minimum and maximum from data. There is no
- // reason to calculate this values every time.
- axis.maximumFromData = autoMaximum;
- axis.minimumFromData = autoMinimum;
- axis.refreshMinMaxFromData = false;
- // Make extra test for stored minimum and maximum values
- // from data. If Number of points is different then data
- // source is changed. That means that we should read
- // data again.
- axis.numberOfPointsInAllSeries = currentPointsNumber;
- }
- /// <summary>
- /// Splits a single array of series names into multiple arrays
- /// based on the stacked group name.
- /// </summary>
- /// <param name="seriesNames">Array of series name to split.</param>
- /// <returns>An array list that contains sub-arrays of series names split by group name.</returns>
- private ArrayList SplitSeriesInStackedGroups(string[] seriesNames)
- {
- Hashtable groupsHashTable = new Hashtable();
- foreach(string seriesName in seriesNames)
- {
- // Get series object
- Series series = this.Common.Chart.Series[seriesName];
- // NOTE: Fix for issue #6716
- // Double check that series supports stacked group feature
- string groupName = string.Empty;
- if(StackedColumnChart.IsSeriesStackGroupNameSupported(series))
- {
- // Get stacked group name (empty string by default)
- groupName = StackedColumnChart.GetSeriesStackGroupName(series);
- }
- // Check if this group was alreday added in to the hashtable
- if (groupsHashTable.ContainsKey(groupName))
- {
- ArrayList list = (ArrayList)groupsHashTable[groupName];
- list.Add(seriesName);
- }
- else
- {
- ArrayList list = new ArrayList();
- list.Add(seriesName);
- groupsHashTable.Add(groupName, list);
- }
- }
- // Convert results to a list that contains array of strings
- ArrayList result = new ArrayList();
- foreach(DictionaryEntry entry in groupsHashTable)
- {
- ArrayList list = (ArrayList)entry.Value;
- if(list.Count > 0)
- {
- int index = 0;
- string[] stringArray = new String[list.Count];
- foreach(string str in list)
- {
- stringArray[index++] = str;
- }
- result.Add(stringArray);
- }
- }
- return result;
- }
- /// <summary>
- /// Find number of points for all series
- /// </summary>
- /// <returns>Number of points</returns>
- private int GetNumberOfAllPoints()
- {
- int numOfPoints = 0;
- foreach( Series series in Common.DataManager.Series )
- {
- numOfPoints += series.Points.Count;
- }
- return numOfPoints;
- }
- /// <summary>
- /// This method sets default minimum and maximum values from
- /// indexes. This case is used if all X values in a series
- /// have 0 value or IsXValueIndexed flag is set.
- /// </summary>
- /// <param name="axis">Axis</param>
- private void SetDefaultFromIndexes( Axis axis )
- {
- // Adjust margin for side-by-side charts like column
- axis.SetTempAxisOffset( );
-
- // Set Axis type
- AxisType type = AxisType.Primary;
- if( axis.axisType == AxisName.X2 || axis.axisType == AxisName.Y2 )
- {
- type = AxisType.Secondary;
- }
- // The maximum is equal to the number of data points.
- double autoMaximum = Common.DataManager.GetNumberOfPoints( GetXAxesSeries( type, axis.SubAxisName ).ToArray() );
- double autoMinimum = 0.0;
- // Axis margin used only for zooming
- axis.marginView = 0.0;
- if( axis.margin == 100 )
- axis.marginView = 1.0;
-
- // If minimum and maximum are same margin always exist.
- if( autoMaximum + axis.margin/100 == autoMinimum - axis.margin/100 + 1 )
- {
- // Set Maximum Number.
- axis.SetAutoMaximum( autoMaximum + 1 );
- axis.SetAutoMinimum( autoMinimum );
- }
- else // Nomal case
- {
- // Set Maximum Number.
- axis.SetAutoMaximum( autoMaximum + axis.margin/100 );
- axis.SetAutoMinimum( autoMinimum - axis.margin/100 + 1 );
- }
- // Find the interval. If the nuber of points
- // is less then 10 interval is 1.
- double axisInterval;
-
- if( axis.ViewMaximum - axis.ViewMinimum <= 10 )
- {
- axisInterval = 1.0;
- }
- else
- {
- axisInterval = axis.CalcInterval( ( axis.ViewMaximum - axis.ViewMinimum ) / 5 );
- }
- ChartArea area = (ChartArea)this;
- if( area.Area3DStyle.Enable3D && !double.IsNaN(axis.interval3DCorrection) )
- {
- axisInterval = Math.Ceiling( axisInterval / axis.interval3DCorrection );
- axis.interval3DCorrection = double.NaN;
- // Use interval
- if( axisInterval > 1.0 &&
- axisInterval < 4.0 &&
- axis.ViewMaximum - axis.ViewMinimum <= 4 )
- {
- axisInterval = 1.0;
- }
- }
- axis.SetInterval = axisInterval;
- // If temporary offsets were defined for the margin,
- // adjust offset for minor ticks and grids.
- if(axis.offsetTempSet)
- {
- axis.minorGrid.intervalOffset -= axis.MajorGrid.GetInterval();
- axis.minorTickMark.intervalOffset -= axis.MajorTickMark.GetInterval();
- }
- }
- /// <summary>
- /// Sets the names of all data series which belong to
- /// this chart area to collection and sets a list of all
- /// different chart types.
- /// </summary>
- internal void SetData()
- {
- this.SetData(true, true);
- }
- /// <summary>
- /// Sets the names of all data series which belong to
- /// this chart area to collection and sets a list of all
- /// different chart types.
- /// </summary>
- /// <param name="initializeAxes">If set to <c>true</c> the method will initialize axes default values.</param>
- /// <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>
- internal void SetData( bool initializeAxes, bool checkIndexedAligned)
- {
- // Initialize chart type properties
- stacked = false;
- switchValueAxes = false;
- requireAxes = true;
- hundredPercent = false;
- hundredPercentNegative = false;
- chartAreaIsCurcular = false;
- secondYScale = false;
-
- // AxisName of the chart area already set.
- bool typeSet = false;
- // Remove all elements from the collection
- this._series.Clear();
- // Add series to the collection
- foreach( Series series in Common.DataManager.Series )
- {
- if (series.ChartArea == this.Name && series.IsVisible() && series.Points.Count > 0)
- {
- this._series.Add(series.Name);
- }
- }
- // Remove all elements from the collection
- this.chartTypes.Clear();
- // Add series to the collection
- foreach( Series series in Common.DataManager.Series )
- {
- // A item already exist.
- bool foundItem = false;
- if (series.IsVisible() && series.ChartArea==this.Name)
- {
- foreach( string type in chartTypes )
- {
- // AxisName already exist in the chart area
- if( type == series.ChartTypeName )
- {
- foundItem = true;
- }
- }
- // Add chart type to the collection of
- // Chart area's chart types
- if( !foundItem )
- {
- // Set stacked type
- if( Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).Stacked )
- {
- stacked = true;
- }
- if( !typeSet )
- {
- if( Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).SwitchValueAxes )
- switchValueAxes = true;
- if( !Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).RequireAxes )
- requireAxes = false;
- if( Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).CircularChartArea )
- chartAreaIsCurcular = true;
- if( Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).HundredPercent )
- hundredPercent = true;
- if( Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).HundredPercentSupportNegative )
- hundredPercentNegative = true;
- if( Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).SecondYScale )
- secondYScale = true;
-
- typeSet = true;
- }
- else
- {
- if( Common.ChartTypeRegistry.GetChartType(series.ChartTypeName).SwitchValueAxes != switchValueAxes )
- {
- throw (new InvalidOperationException(SR.ExceptionChartAreaChartTypesCanNotCombine));
- }
- }
-
- // Series is not empty
- if( Common.DataManager.GetNumberOfPoints( series.Name ) != 0 )
- {
- this.chartTypes.Add( series.ChartTypeName );
- }
- }
- }
- }
- // Check that all primary X axis series are aligned if use the IsXValueIndexed flag
- if (checkIndexedAligned)
- {
- for (int axisIndex = 0; axisIndex <= 1; axisIndex++)
- {
- List<string> seriesArray = this.GetXAxesSeries((axisIndex == 0) ? AxisType.Primary : AxisType.Secondary, string.Empty);
- if (seriesArray.Count > 0)
- {
- bool indexed = false;
- string seriesNamesStr = "";
- foreach (string seriesName in seriesArray)
- {
- seriesNamesStr = seriesNamesStr + seriesName.Replace(",", "\\,") + ",";
- if (Common.DataManager.Series[seriesName].IsXValueIndexed)
- {
- indexed = true;
- }
- }
- if (indexed)
- {
- try
- {
- Common.DataManipulator.CheckXValuesAlignment(
- Common.DataManipulator.ConvertToSeriesArray(seriesNamesStr.TrimEnd(','), false));
- }
- catch (Exception e)
- {
- throw (new ArgumentException(SR.ExceptionAxisSeriesNotAligned + e.Message));
- }
- }
- }
- }
- }
- if (initializeAxes)
- {
- // Set default min, max etc.
- SetDefaultAxesValues();
- }
- }
- /// <summary>
- /// Returns names of all series, which belong to this chart area
- /// and have same chart type.
- /// </summary>
- /// <param name="chartType">Chart type</param>
- /// <returns>Collection with series names</returns>
- internal List<string> GetSeriesFromChartType( string chartType )
- {
- // New collection
- List<string> list = new List<string>();
-
- foreach( string seriesName in _series )
- {
- if( String.Compare( chartType, Common.DataManager.Series[seriesName].ChartTypeName, StringComparison.OrdinalIgnoreCase ) == 0 )
- {
- // Add a series name to the collection
- list.Add( seriesName );
- }
- }
- return list;
- }
- /// <summary>
- /// Returns all series which belong to this chart area.
- /// </summary>
- /// <returns>Collection with series</returns>
- internal List<Series> GetSeries( )
- {
- // New collection
- List<Series> list = new List<Series>();
-
- foreach( string seriesName in _series )
- {
- list.Add(Common.DataManager.Series[seriesName]);
- }
- return list;
- }
- /// <summary>
- /// Creates a list of series, which have same X axis type.
- /// </summary>
- /// <param name="type">Axis type</param>
- /// <param name="subAxisName">Sub Axis name</param>
- /// <returns>A list of series</returns>
- internal List<string> GetXAxesSeries( AxisType type, string subAxisName )
- {
- // Create a new collection of series
- List<string> list = new List<string>();
- if (_series.Count == 0)
- {
- return list;
- }
- // Ignore sub axis in 3D
- if( !this.IsSubAxesSupported )
- {
- if(subAxisName.Length > 0)
- {
- return list;
- }
- }
- // Find series which have same axis type
- foreach( string ser in _series )
- {
- #if SUBAXES
- if( Common.DataManager.Series[ser].XAxisType == type &&
- (Common.DataManager.Series[ser].XSubAxisName == subAxisName || !this.IsSubAxesSupported) )
- #else // SUBAXES
- if ( Common.DataManager.Series[ser].XAxisType == type)
- #endif // SUBAXES
- {
- // Add a series to the collection
- list.Add( ser );
- }
- }
- #if SUBAXES
- // If series list is empty for the sub-axis then
- // try using the main axis.
- if ( list.Count == 0 && subAxisName.Length > 0 )
- {
- return GetXAxesSeries( type, string.Empty );
- }
- #endif // SUBAXES
- // If primary series do not exist return secondary series
- // Axis should always be connected with any series.
- if ( list.Count == 0 )
- {
- if (type == AxisType.Secondary)
- {
- return GetXAxesSeries(AxisType.Primary, string.Empty);
- }
- return GetXAxesSeries(AxisType.Secondary, string.Empty);
- }
-
- return list;
- }
- /// <summary>
- /// Creates a list of series, which have same Y axis type.
- /// </summary>
- /// <param name="type">Axis type</param>
- /// <param name="subAxisName">Sub Axis name</param>
- /// <returns>A list of series</returns>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "subAxisName")]
- internal List<string> GetYAxesSeries( AxisType type, string subAxisName )
- {
- // Create a new collection of series
- List<string> list = new List<string>();
- // Find series which have same axis type
- foreach( string ser in _series )
- {
- // Get series Y axis type
- AxisType seriesYAxisType = Common.DataManager.Series[ser].YAxisType;
- #if SUBAXES
- string seriesYSubAxisName = subAxisName;
- #endif // SUBAXES
- // NOTE: Fixes issue #6969
- // Ignore series settings if only Primary Y axis supported by the chart type
- if (Common.DataManager.Series[ser].ChartType == SeriesChartType.Radar ||
- Common.DataManager.Series[ser].ChartType == SeriesChartType.Polar)
- {
- seriesYAxisType = AxisType.Primary;
- #if SUBAXES
- seriesYSubAxisName = string.Empty;
- #endif // SUBAXES
- }
- #if SUBAXES
- if( seriesYAxisType == type &&
- (Common.DataManager.Series[ser].YSubAxisName == seriesYSubAxisName || !this.IsSubAxesSupported) )
- #else // SUBAXES
- if (seriesYAxisType == type)
- #endif // SUBAXES
- {
- // Add a series to the collection
- list.Add( ser );
- }
- }
- #if SUBAXES
- // If series list is empty for the sub-axis then
- // try using the main axis.
- if ( list.Count == 0 && subAxisName.Length > 0 )
- {
- return GetYAxesSeries( type, string.Empty );
- }
- #endif // SUBAXES
- // If primary series do not exist return secondary series
- // Axis should always be connected with any series.
- if ( list.Count == 0 && type == AxisType.Secondary )
- {
- return GetYAxesSeries( AxisType.Primary, string.Empty );
- }
- return list;
- }
- /// <summary>
- /// Get first series from the chart area
- /// </summary>
- /// <returns>Data series</returns>
- internal Series GetFirstSeries()
- {
- if( _series.Count == 0 )
- {
- throw (new InvalidOperationException(SR.ExceptionChartAreaSeriesNotFound));
- }
- return Common.DataManager.Series[_series[0]];
- }
-
- /// <summary>
- /// This method returns minimum interval between
- /// any two data points from series which belong
- /// to this chart area.
- /// </summary>
- /// <param name="isLogarithmic">Indicates logarithmic scale.</param>
- /// <param name="logarithmBase">Logarithm Base</param>
- /// <returns>Minimum Interval</returns>
- internal double GetPointsInterval(bool isLogarithmic, double logarithmBase)
- {
- bool sameInterval;
- return GetPointsInterval( _series, isLogarithmic, logarithmBase, false, out sameInterval );
- }
-
- /// <summary>
- /// This method returns minimum interval between
- /// any two data points from specified series.
- /// </summary>
- /// <param name="seriesList">List of series.</param>
- /// <param name="isLogarithmic">Indicates logarithmic scale.</param>
- /// <param name="logarithmBase">Base for logarithmic base</param>
- /// <param name="checkSameInterval">True if check for the same interval should be performed.</param>
- /// <param name="sameInterval">Return true if interval is the same.</param>
- /// <returns>Minimum Interval</returns>
- internal double GetPointsInterval( List<string> seriesList, bool isLogarithmic, double logarithmBase, bool checkSameInterval, out bool sameInterval )
- {
- Series nullSeries = null;
- return GetPointsInterval(seriesList, isLogarithmic, logarithmBase, checkSameInterval, out sameInterval, out nullSeries);
- }
-
- /// <summary>
- /// This method returns minimum interval between
- /// any two data points from specified series.
- /// </summary>
- /// <param name="seriesList">List of series.</param>
- /// <param name="isLogarithmic">Indicates logarithmic scale.</param>
- /// <param name="logarithmicBase">Logarithm Base</param>
- /// <param name="checkSameInterval">True if check for the same interval should be performed.</param>
- /// <param name="sameInterval">Return true if interval is the same.</param>
- /// <param name="series">Series with the smallest interval between points.</param>
- /// <returns>Minimum Interval</returns>
- internal double GetPointsInterval( List<string> seriesList, bool isLogarithmic, double logarithmicBase, bool checkSameInterval, out bool sameInterval, out Series series )
- {
- long ticksInterval = long.MaxValue;
- int monthsInteval = 0;
- double previousInterval = double.MinValue;
- double oldInterval = Double.MaxValue;
- // Initialize return value
- sameInterval = true;
- series = null;
- // Create comma separate string of series names
- string seriesNames = "";
- if(seriesList != null)
- {
- foreach( string serName in seriesList )
- {
- seriesNames += serName + ",";
- }
- }
- // Do not calculate interval every time;
- if( checkSameInterval == false || diffIntervalAlignmentChecked == true)
- {
- if (!isLogarithmic)
- {
- if( !double.IsNaN(intervalData) && _intervalSeriesList == seriesNames)
- {
- sameInterval = intervalSameSize;
- series = _intervalSeries;
- return intervalData;
- }
- }
- else
- {
- if( !double.IsNaN(intervalLogData) && _intervalSeriesList == seriesNames)
- {
- sameInterval = intervalSameSize;
- series = _intervalSeries;
- return intervalLogData;
- }
- }
- }
- // Data series loop
- int seriesIndex = 0;
- Series currentSmallestSeries = null;
- ArrayList[] seriesXValues = new ArrayList[seriesList.Count];
- foreach( string ser in seriesList )
- {
- Series dataSeries = Common.DataManager.Series[ ser ];
- bool isXValueDateTime = dataSeries.IsXValueDateTime();
- // Copy X values to array and prepare for sorting Sort X values.
- seriesXValues[seriesIndex] = new ArrayList();
- bool sortPoints = false;
- double prevXValue = double.MinValue;
- double curentXValue = 0.0;
- if(dataSeries.Points.Count > 0)
- {
- if (isLogarithmic)
- {
- prevXValue = Math.Log(dataSeries.Points[0].XValue, logarithmicBase);
- }
- else
- {
- prevXValue = dataSeries.Points[0].XValue;
- }
- }
- foreach( DataPoint point in dataSeries.Points )
- {
- if (isLogarithmic)
- {
- curentXValue = Math.Log(point.XValue, logarithmicBase);
- }
- else
- {
- curentXValue = point.XValue;
- }
- if(prevXValue > curentXValue)
- {
- sortPoints = true;
- }
- seriesXValues[seriesIndex].Add(curentXValue);
- prevXValue = curentXValue;
- }
- // Sort X values
- if(sortPoints)
- {
- seriesXValues[seriesIndex].Sort();
- }
- // Data point loop
- for( int point = 1; point < seriesXValues[seriesIndex].Count; point++ )
- {
- // Interval between two sorted data points.
- double interval = Math.Abs( (double)seriesXValues[seriesIndex][ point - 1 ] - (double)seriesXValues[seriesIndex][ point ] );
- // Check if all intervals are same
- if(sameInterval)
- {
- if(isXValueDateTime)
- {
- if(ticksInterval == long.MaxValue)
- {
- // Calculate first interval
- GetDateInterval(
- (double)seriesXValues[seriesIndex][ point - 1 ],
- (double)seriesXValues[seriesIndex][ point ],
- out monthsInteval,
- out ticksInterval);
- }
- else
- {
- // Calculate current interval
- long curentTicksInterval = long.MaxValue;
- int curentMonthsInteval = 0;
- GetDateInterval(
- (double)seriesXValues[seriesIndex][ point - 1 ],
- (double)seriesXValues[seriesIndex][ point ],
- out curentMonthsInteval,
- out curentTicksInterval);
- // Compare current interval with previous
- if(curentMonthsInteval != monthsInteval || curentTicksInterval != ticksInterval)
- {
- sameInterval = false;
- }
- }
- }
- else
- {
- if( previousInterval != interval && previousInterval != double.MinValue )
- {
- sameInterval = false;
- }
- }
- }
- previousInterval = interval;
- // If not minimum interval keep the old one
- if( oldInterval > interval && interval != 0)
- {
- oldInterval = interval;
- currentSmallestSeries = dataSeries;
- }
- }
- ++seriesIndex;
- }
- // If interval is not the same check if points from all series are aligned
- this.diffIntervalAlignmentChecked = false;
- if( checkSameInterval && !sameInterval && seriesXValues.Length > 1)
- {
- bool sameXValue = false;
- this.diffIntervalAlignmentChecked = true;
- // All X values must be same
- int listIndex = 0;
- foreach(ArrayList xList in seriesXValues)
- {
- for(int pointIndex = 0; pointIndex < xList.Count && !sameXValue; pointIndex++)
- {
- double xValue = (double)xList[pointIndex];
- // Loop through all other lists and see if point is there
- for(int index = listIndex + 1; index < seriesXValues.Length && !sameXValue; index++)
- {
- if( (pointIndex < seriesXValues[index].Count && (double)seriesXValues[index][pointIndex] == xValue) ||
- seriesXValues[index].Contains(xValue))
- {
- sameXValue = true;
- break;
- }
- }
- }
- ++listIndex;
- }
- // Use side-by-side if at least one xommon X value between eries found
- if(sameXValue)
- {
- sameInterval = true;
- }
- }
- // Interval not found. Interval is 1.
- if( oldInterval == Double.MaxValue)
- {
- oldInterval = 1;
- }
- intervalSameSize = sameInterval;
- if (!isLogarithmic)
- {
- intervalData = oldInterval;
- _intervalSeries = currentSmallestSeries;
- series = _intervalSeries;
- _intervalSeriesList = seriesNames;
- return intervalData;
- }
- else
- {
- intervalLogData = oldInterval;
- _intervalSeries = currentSmallestSeries;
- series = _intervalSeries;
- _intervalSeriesList = seriesNames;
- return intervalLogData;
- }
- }
- /// <summary>
- /// Calculates the difference between two values in years, months, days, ...
- /// </summary>
- /// <param name="value1">First value.</param>
- /// <param name="value2">Second value.</param>
- /// <param name="monthsInteval">Interval in months.</param>
- /// <param name="ticksInterval">Interval in ticks.</param>
- private void GetDateInterval(double value1, double value2, out int monthsInteval, out long ticksInterval)
- {
- // Convert values to dates
- DateTime date1 = DateTime.FromOADate(value1);
- DateTime date2 = DateTime.FromOADate(value2);
- // Calculate months difference
- monthsInteval = date2.Month - date1.Month;
- monthsInteval += (date2.Year - date1.Year) * 12;
- // Calculate interval in ticks for days, hours, ...
- ticksInterval = 0;
- ticksInterval += (date2.Day - date1.Day) * TimeSpan.TicksPerDay;
- ticksInterval += (date2.Hour - date1.Hour) * TimeSpan.TicksPerHour;
- ticksInterval += (date2.Minute - date1.Minute) * TimeSpan.TicksPerMinute;
- ticksInterval += (date2.Second - date1.Second) * TimeSpan.TicksPerSecond;
- ticksInterval += (date2.Millisecond - date1.Millisecond) * TimeSpan.TicksPerMillisecond;
- }
- #endregion
- }
- }
|