/// <summary> /// Initialize internal scrolling line size variables for later use. /// This size is used in to scroll chart one line up or down. /// </summary> private void GetCurrentViewSmallScrollSize() { //************************************************************************** //** Check if current scrolling line size was not already calculated //************************************************************************** if (double.IsNaN(_currentSmallScrollSize)) { //************************************************************************** //** Calculate line size depending on the current scaleView size //************************************************************************** if (SizeType == DateTimeIntervalType.Auto || SizeType == DateTimeIntervalType.Number) { // Set line size type _currentSmallScrollSizeType = DateTimeIntervalType.Number; // Devide scaleView by 20 to find the scrolling line size double newSize = Size / 20.0; // Make sure that current line size is even with minimum value if (!double.IsNaN(SmallScrollMinSize) && SmallScrollMinSize != 0.0) { double rounder = (Math.Round(newSize / SmallScrollMinSize)); if (rounder < 0) { rounder = 1; } newSize = rounder * SmallScrollMinSize; } // Set new current line size _currentSmallScrollSize = newSize; } else { // Calculate line size for date/time double viewEndPosition = Position + ChartHelper.GetIntervalSize(Position, Size, SizeType); _currentSmallScrollSize = axis.CalcInterval( Position, viewEndPosition, true, out _currentSmallScrollSizeType, ChartValueType.Auto); } //************************************************************************** //** Make sure calculated scroll line size is not smaller than the minimum //************************************************************************** if (!double.IsNaN(SmallScrollMinSize) && SmallScrollMinSize != 0.0) { double newLineSize = ChartHelper.GetIntervalSize(Position, _currentSmallScrollSize, _currentSmallScrollSizeType); double minLineSize = ChartHelper.GetIntervalSize(Position, SmallScrollMinSize, SmallScrollMinSizeType); if (newLineSize < minLineSize) { _currentSmallScrollSize = SmallScrollMinSize; _currentSmallScrollSizeType = SmallScrollMinSizeType; } } } }
/// <summary> /// Scrolls axis data scaleView from current position. /// </summary> /// <param name="scrollType">Direction and size to scroll.</param> /// <param name="fireChangeEvents">Fire scaleView position events from this method.</param> internal void Scroll(ScrollType scrollType, bool fireChangeEvents) { // Adjust current position depending on the scroll type double newPosition = _position; switch (scrollType) { case (ScrollType.SmallIncrement): newPosition += ((axis.IsReversed) ? -1 : 1) * ChartHelper.GetIntervalSize(_position, GetScrollingLineSize(), GetScrollingLineSizeType()); break; case (ScrollType.SmallDecrement): newPosition -= ((axis.IsReversed) ? -1 : 1) * ChartHelper.GetIntervalSize(_position, GetScrollingLineSize(), GetScrollingLineSizeType()); break; case (ScrollType.LargeIncrement): newPosition += ((axis.IsReversed) ? -1 : 1) * ChartHelper.GetIntervalSize(_position, Size, SizeType); break; case (ScrollType.LargeDecrement): newPosition -= ((axis.IsReversed) ? -1 : 1) * ChartHelper.GetIntervalSize(_position, Size, SizeType); break; case (ScrollType.First): if (!axis.IsReversed) { newPosition = (axis.minimum + axis.marginView); } else { newPosition = (axis.maximum - axis.marginView); } break; case (ScrollType.Last): { double viewSize = ChartHelper.GetIntervalSize(newPosition, Size, SizeType); if (!axis.IsReversed) { newPosition = (axis.maximum - axis.marginView - viewSize); } else { newPosition = (axis.minimum + axis.marginView + viewSize); } break; } } // Scroll to the new position Scroll(newPosition, fireChangeEvents); }
/// <summary> /// Internal helper method for scrolling into specified position. /// </summary> /// <param name="newPosition">New data scaleView position.</param> /// <param name="fireChangeEvents">Fire scaleView position events from this method.</param> internal void Scroll(double newPosition, bool fireChangeEvents) { // Get current scaleView size double viewSize = ChartHelper.GetIntervalSize(newPosition, Size, SizeType); // Validate new scaleView position if (newPosition < (axis.minimum + axis.marginView)) { newPosition = (axis.minimum + axis.marginView); } else if (newPosition > (axis.maximum - axis.marginView - viewSize)) { newPosition = (axis.maximum - axis.marginView - viewSize); } // Fire scaleView position changing events ViewEventArgs arguments = new(axis, newPosition, Size, SizeType); if (fireChangeEvents && GetChartObject() != null) { GetChartObject().OnAxisViewChanging(arguments); newPosition = arguments.NewPosition; } // Check if data scaleView position and size is different from current if (newPosition == Position) { return; } // Change scaleView position Position = newPosition; // Fire scaleView position changed events if (fireChangeEvents && GetChartObject() != null) { GetChartObject().OnAxisViewChanged(arguments); } }
/// <summary> /// Fill labels from data from data manager or /// from axis scale. /// </summary> /// <param name="removeFirstRow">True if first row of auto generated labels must be removed.</param> internal void FillLabels(bool removeFirstRow) { #if SUBAXES // Process all sub-axis foreach (SubAxis subAxis in ((Axis)this).SubAxes) { subAxis.FillLabels(true); } #endif // SUBAXES // Labels are disabled for this axis if (!LabelStyle.Enabled || !enabled) { return; } // For circular chart area fill only Y axis labels if (ChartArea != null && ChartArea.chartAreaIsCurcular && axisType != AxisName.Y) { ICircularChartType type = ChartArea.GetCircularChartType(); if (type == null || !type.XAxisLabelsSupported()) { return; } } // Check if the custom labels exist bool customLabelsFlag = false; foreach (CustomLabel lab in CustomLabels) { if (lab.customLabel && (lab.RowIndex == 0 || ChartArea.chartAreaIsCurcular)) { customLabelsFlag = true; } } // Remove the first row of labels if custom labels not exist if (removeFirstRow) { if (!customLabelsFlag) { for (int index = 0; index < CustomLabels.Count; index++) { if (CustomLabels[index].RowIndex == 0) { CustomLabels.RemoveAt(index); index = -1; } } } else { return; } } // Get data series for this axis. List <string> dataSeries = null; switch (axisType) { case AxisName.X: dataSeries = ChartArea.GetXAxesSeries(AxisType.Primary, SubAxisName); break; case AxisName.Y: dataSeries = ChartArea.GetYAxesSeries(AxisType.Primary, SubAxisName); break; case AxisName.X2: dataSeries = ChartArea.GetXAxesSeries(AxisType.Secondary, SubAxisName); break; case AxisName.Y2: dataSeries = ChartArea.GetYAxesSeries(AxisType.Secondary, SubAxisName); break; } // There aren't data series connected with this axis. if (dataSeries == null || dataSeries.Count == 0) { return; } //Let's convert the ArrayList of the series names into to string[] string[] dataSeriesNames = new string[dataSeries.Count]; for (int i = 0; i < dataSeries.Count; i++) { dataSeriesNames[i] = dataSeries[i]; } // Check if series X values all set to zeros bool seriesXValuesZeros = ChartHelper.SeriesXValuesZeros(Common, dataSeriesNames); // Check if series is indexed (All X values zeros or IsXValueIndexed flag set) bool indexedSeries = true; if (!seriesXValuesZeros) { indexedSeries = ChartHelper.IndexedSeries(Common, dataSeriesNames); } // Show End Labels int endLabels = 0; if (labelStyle.IsEndLabelVisible) { endLabels = 1; } // Get chart type of the first series IChartType chartType = Common.ChartTypeRegistry.GetChartType(ChartArea.GetFirstSeries().ChartTypeName); bool fromSeries = false; if (!chartType.RequireAxes) { return; } else if (axisType == AxisName.Y || axisType == AxisName.Y2) { fromSeries = false; } else { fromSeries = true; } // X values from data points are not 0. if (fromSeries && !ChartHelper.SeriesXValuesZeros(Common, dataSeries.ToArray())) { fromSeries = false; } // X values from data points are not 0. if (fromSeries && (labelStyle.GetIntervalOffset() != 0 || labelStyle.GetInterval() != 0)) { fromSeries = false; } // Get value type ChartValueType valueType; if (axisType == AxisName.X || axisType == AxisName.X2) { // If X value is indexed the type is always String. So we use indexed type instead valueType = Common.DataManager.Series[dataSeries[0]].indexedXValueType; } else { valueType = Common.DataManager.Series[dataSeries[0]].YValueType; } if (labelStyle.GetIntervalType() != DateTimeIntervalType.Auto && labelStyle.GetIntervalType() != DateTimeIntervalType.Number && valueType != ChartValueType.Time && valueType != ChartValueType.Date && valueType != ChartValueType.DateTimeOffset) { valueType = ChartValueType.DateTime; } // *********************************** // Pre calculate some values // *********************************** double viewMaximum = ViewMaximum; double viewMinimum = ViewMinimum; // *********************************** // Labels are filled from data series. // *********************************** if (fromSeries) { int numOfPoints; numOfPoints = Common.DataManager.GetNumberOfPoints(dataSeries.ToArray()); // Show end labels if (endLabels == 1) { // min position CustomLabels.Add(-0.5, 0.5, ValueConverter.FormatValue( Common.Chart, this, null, 0.0, LabelStyle.Format, valueType, ChartElementType.AxisLabels), false); } // Labels from point position for (int point = 0; point < numOfPoints; point++) { CustomLabels.Add(point + 0.5, point + 1.5, ValueConverter.FormatValue( Common.Chart, this, null, point + 1, LabelStyle.Format, valueType, ChartElementType.AxisLabels), false); } // Show end labels if (endLabels == 1) { // max position CustomLabels.Add(numOfPoints + 0.5, numOfPoints + 1.5, ValueConverter.FormatValue( Common.Chart, this, null, numOfPoints + 1, LabelStyle.Format, valueType, ChartElementType.AxisLabels), false); } int pointIndx; foreach (string seriesIndx in dataSeries) { // End labels enabled if (endLabels == 1) { pointIndx = 1; } else { pointIndx = 0; } // Set labels from data points labels foreach (DataPoint dataPoint in Common.DataManager.Series[seriesIndx].Points) { // Find first row of labels while (CustomLabels[pointIndx].RowIndex > 0) { pointIndx++; } // Add X labels if ((axisType == AxisName.X || axisType == AxisName.X2) && dataPoint.AxisLabel.Length > 0) { CustomLabels[pointIndx].Text = dataPoint.AxisLabel; } pointIndx++; } } } // *********************************** // Labels are filled from axis scale. // *********************************** else { if (viewMinimum == viewMaximum) { return; } double labValue; // Value, which will be converted to text and used for, labels. double beginPosition; // Begin position for a label double endPosition; // End position for a label double start; // Start position for all labels // Get first series attached to this axis Series axisSeries = null; if (axisType == AxisName.X || axisType == AxisName.X2) { List <string> seriesArray = ChartArea.GetXAxesSeries((axisType == AxisName.X) ? AxisType.Primary : AxisType.Secondary, SubAxisName); if (seriesArray.Count > 0) { axisSeries = Common.DataManager.Series[seriesArray[0]]; if (axisSeries != null && !axisSeries.IsXValueIndexed) { axisSeries = null; } } } // *********************************** // Check if the AJAX zooming and scrolling mode is enabled. // Labels are filled slightly different in this case. // *********************************** DateTimeIntervalType offsetType = (labelStyle.GetIntervalOffsetType() == DateTimeIntervalType.Auto) ? labelStyle.GetIntervalType() : labelStyle.GetIntervalOffsetType(); // By default start is equal to minimum start = viewMinimum; // Adjust start position depending on the interval type if (!ChartArea.chartAreaIsCurcular || axisType == AxisName.Y || axisType == AxisName.Y2) { start = ChartHelper.AlignIntervalStart(start, labelStyle.GetInterval(), labelStyle.GetIntervalType(), axisSeries); } // Move start if there is start position if (labelStyle.GetIntervalOffset() != 0 && axisSeries == null) { start += ChartHelper.GetIntervalSize(start, labelStyle.GetIntervalOffset(), offsetType, axisSeries, 0, DateTimeIntervalType.Number, true, false); } // *************************************** // Date type // *************************************** if (valueType == ChartValueType.DateTime || valueType == ChartValueType.Date || valueType == ChartValueType.Time || valueType == ChartValueType.DateTimeOffset || axisSeries != null) { double position = start; double dateInterval; // Too many labels if ((viewMaximum - start) / ChartHelper.GetIntervalSize(start, labelStyle.GetInterval(), labelStyle.GetIntervalType(), axisSeries, 0, DateTimeIntervalType.Number, true) > ChartHelper.MaxNumOfGridlines) { return; } int counter = 0; double endLabelMaxPosition = viewMaximum - ChartHelper.GetIntervalSize(viewMaximum, labelStyle.GetInterval(), labelStyle.GetIntervalType(), axisSeries, labelStyle.GetIntervalOffset(), offsetType, true) / 2f; double endLabelMinPosition = viewMinimum + ChartHelper.GetIntervalSize(viewMinimum, labelStyle.GetInterval(), labelStyle.GetIntervalType(), axisSeries, labelStyle.GetIntervalOffset(), offsetType, true) / 2f; while ((decimal)position <= (decimal)viewMaximum) { dateInterval = ChartHelper.GetIntervalSize(position, labelStyle.GetInterval(), labelStyle.GetIntervalType(), axisSeries, labelStyle.GetIntervalOffset(), offsetType, true); labValue = position; // For IsLogarithmic axes if (IsLogarithmic) { labValue = Math.Pow(logarithmBase, labValue); } // Check if we do not exceed max number of elements if (counter++ > ChartHelper.MaxNumOfGridlines) { break; } if (endLabels == 0 && position >= endLabelMaxPosition) { break; } beginPosition = position - dateInterval * 0.5; endPosition = position + dateInterval * 0.5; if (endLabels == 0 && position <= endLabelMinPosition) { position += dateInterval; continue; } if ((decimal)beginPosition > (decimal)viewMaximum) { position += dateInterval; continue; } string pointLabel = GetPointLabel(dataSeries, labValue, !seriesXValuesZeros, indexedSeries); if (pointLabel.Length == 0) { // Do not draw last label for indexed series if (position <= maximum && (position != maximum || !Common.DataManager.Series[dataSeries[0]].IsXValueIndexed)) { CustomLabels.Add(beginPosition, endPosition, ValueConverter.FormatValue( Common.Chart, this, null, labValue, LabelStyle.Format, valueType, ChartElementType.AxisLabels), false); } } else { // Add a label to the collection CustomLabels.Add(beginPosition, endPosition, pointLabel, false); } position += dateInterval; } } else { // *************************************** // Scale value type // *************************************** // Show First label if Start Label position is used if (start != viewMinimum) { endLabels = 1; } // Set labels int labelCounter = 0; for (double position = start - endLabels * labelStyle.GetInterval(); position < viewMaximum - 1.5 * labelStyle.GetInterval() * (1 - endLabels); position = (double)((decimal)position + (decimal)labelStyle.GetInterval())) { // Prevent endless loop that may be caused by very small interval // and double/decimal rounding errors ++labelCounter; if (labelCounter > ChartHelper.MaxNumOfGridlines) { break; } labValue = (double)((decimal)position + (decimal)labelStyle.GetInterval()); // This line is introduce because sometimes 0 value will appear as // very small value close to zero. double inter = Math.Log(labelStyle.GetInterval()); double valu = Math.Log(Math.Abs(labValue)); int digits = (int)Math.Abs(inter) + 5; if (digits > 15) { digits = 15; } if (Math.Abs(inter) < Math.Abs(valu) - 5) { labValue = Math.Round(labValue, digits); } // Too many labels if ((viewMaximum - start) / labelStyle.GetInterval() > ChartHelper.MaxNumOfGridlines) { return; } // For IsLogarithmic axes if (IsLogarithmic) { labValue = Math.Pow(logarithmBase, labValue); } beginPosition = (double)((decimal)position + (decimal)labelStyle.GetInterval() * 0.5m); endPosition = (double)((decimal)position + (decimal)labelStyle.GetInterval() * 1.5m); if ((decimal)beginPosition > (decimal)viewMaximum) { continue; } // Show End label if Start Label position is used // Use decimal type to solve rounding issues if ((decimal)((beginPosition + endPosition) / 2.0) > (decimal)viewMaximum) { continue; } string pointLabel = GetPointLabel(dataSeries, labValue, !seriesXValuesZeros, indexedSeries); if (pointLabel.Length > 15 && labValue < 0.000001) { labValue = 0.0; } if (pointLabel.Length == 0) { // Do not draw last label for indexed series if (!(Common.DataManager.Series[dataSeries[0]].IsXValueIndexed && position > maximum)) { // Add a label to the collection CustomLabels.Add(beginPosition, endPosition, ValueConverter.FormatValue( Common.Chart, this, null, labValue, LabelStyle.Format, valueType, ChartElementType.AxisLabels), false); } } else { // Add a label to the collection CustomLabels.Add(beginPosition, endPosition, pointLabel, false); } } } } }
/// <summary> /// Rounds new position of the cursor or range selection /// </summary> /// <param name="cursorPosition"></param> /// <returns></returns> internal double RoundPosition(double cursorPosition) { double roundedPosition = cursorPosition; if (!double.IsNaN(roundedPosition) && GetAxis() != null && Interval != 0 && !double.IsNaN(Interval)) { // Get first series attached to this axis Series axisSeries = null; if (_axis.axisType == AxisName.X || _axis.axisType == AxisName.X2) { List <string> seriesArray = _axis.ChartArea.GetXAxesSeries((_axis.axisType == AxisName.X) ? AxisType.Primary : AxisType.Secondary, _axis.SubAxisName); if (seriesArray.Count > 0) { string seriesName = seriesArray[0]; axisSeries = _axis.Common.DataManager.Series[seriesName]; if (axisSeries != null && !axisSeries.IsXValueIndexed) { axisSeries = null; } } } // If interval type is not set - use number DateTimeIntervalType intervalType = (IntervalType == DateTimeIntervalType.Auto) ? DateTimeIntervalType.Number : IntervalType; // If interval offset type is not set - use interval type DateTimeIntervalType offsetType = (IntervalOffsetType == DateTimeIntervalType.Auto) ? intervalType : IntervalOffsetType; // Round numbers if (intervalType == DateTimeIntervalType.Number) { double newRoundedPosition = Math.Round(roundedPosition / Interval) * Interval; // Add offset number if (IntervalOffset != 0 && !double.IsNaN(IntervalOffset) && offsetType != DateTimeIntervalType.Auto) { if (IntervalOffset > 0) { newRoundedPosition += ChartHelper.GetIntervalSize(newRoundedPosition, IntervalOffset, offsetType); } else { newRoundedPosition -= ChartHelper.GetIntervalSize(newRoundedPosition, IntervalOffset, offsetType); } } // Find rounded position after/before the current double nextPosition = newRoundedPosition; if (newRoundedPosition <= cursorPosition) { nextPosition += ChartHelper.GetIntervalSize(newRoundedPosition, Interval, intervalType, axisSeries, 0, DateTimeIntervalType.Number, true); } else { nextPosition -= ChartHelper.GetIntervalSize(newRoundedPosition, Interval, intervalType, axisSeries, 0, DateTimeIntervalType.Number, true); } // Choose closest rounded position if (Math.Abs(nextPosition - cursorPosition) > Math.Abs(cursorPosition - newRoundedPosition)) { roundedPosition = newRoundedPosition; } else { roundedPosition = nextPosition; } } // Round date/time else { // Find one rounded position prior and one after current position // Adjust start position depending on the interval and type double prevPosition = ChartHelper.AlignIntervalStart(cursorPosition, Interval, intervalType, axisSeries); // Adjust start position depending on the interval offset and offset type if (IntervalOffset != 0 && axisSeries == null) { if (IntervalOffset > 0) { prevPosition += ChartHelper.GetIntervalSize( prevPosition, IntervalOffset, offsetType, axisSeries, 0, DateTimeIntervalType.Number, true); } else { prevPosition += ChartHelper.GetIntervalSize( prevPosition, -IntervalOffset, offsetType, axisSeries, 0, DateTimeIntervalType.Number, true); } } // Find rounded position after/before the current double nextPosition = prevPosition; if (prevPosition <= cursorPosition) { nextPosition += ChartHelper.GetIntervalSize(prevPosition, Interval, intervalType, axisSeries, 0, DateTimeIntervalType.Number, true); } else { nextPosition -= ChartHelper.GetIntervalSize(prevPosition, Interval, intervalType, axisSeries, 0, DateTimeIntervalType.Number, true); } // Choose closest rounded position if (Math.Abs(nextPosition - cursorPosition) > Math.Abs(cursorPosition - prevPosition)) { roundedPosition = prevPosition; } else { roundedPosition = nextPosition; } } } return(roundedPosition); }
/// <summary> /// Helper method, which validates the axis data scaleView position and size. /// Returns adjusted scaleView position and size. /// </summary> /// <param name="viewPosition">ScaleView position.</param> /// <param name="viewSize">ScaleView size.</param> /// <param name="viewSizeType">ScaleView size units type.</param> private void ValidateViewPositionSize(ref double viewPosition, ref double viewSize, ref DateTimeIntervalType viewSizeType) { //**************************************************************** //** Check if new scaleView position is inside axis scale //** minimum/maximum without margin. //**************************************************************** if (viewPosition < (axis.minimum + axis.marginView)) { if (viewSizeType == DateTimeIntervalType.Auto || viewSizeType == DateTimeIntervalType.Number) { viewSize -= (axis.minimum + axis.marginView) - viewPosition; } viewPosition = (axis.minimum + axis.marginView); } else if (viewPosition > (axis.maximum - axis.marginView)) { if (viewSizeType == DateTimeIntervalType.Auto || viewSizeType == DateTimeIntervalType.Number) { viewSize -= viewPosition - (axis.maximum - axis.marginView); } viewPosition = (axis.maximum - axis.marginView); } //**************************************************************** //** Check if new scaleView size is not smaller than minimum size //** set by the user //**************************************************************** double newViewSize = ChartHelper.GetIntervalSize(viewPosition, viewSize, viewSizeType); double minViewSize = ChartHelper.GetIntervalSize(viewPosition, 1, MinSizeType); if (!double.IsNaN(MinSize)) { minViewSize = ChartHelper.GetIntervalSize(viewPosition, MinSize, MinSizeType); if (newViewSize < minViewSize) { viewSize = (double.IsNaN(MinSize)) ? 1 : MinSize; viewSizeType = MinSizeType; newViewSize = ChartHelper.GetIntervalSize(viewPosition, viewSize, viewSizeType); } } //**************************************************************** //** Check if new scaleView size is smaller than (0.000000001) //**************************************************************** if (newViewSize < 0.000000001) { viewSize = 0.000000001; viewSizeType = DateTimeIntervalType.Number; newViewSize = ChartHelper.GetIntervalSize(viewPosition, viewSize, viewSizeType); } //**************************************************************** //** Check if new scaleView end position (position + size) is inside //** axis scale minimum/maximum without margin. //**************************************************************** while ((viewPosition + newViewSize) > (axis.maximum - axis.marginView)) { double currentSize = viewSize; DateTimeIntervalType currentSizeType = viewSizeType; // Try to reduce the scaleView size if (newViewSize > minViewSize) { // Try to adjust the scaleView size if (viewSize > 1) { --viewSize; } else if (viewSizeType == DateTimeIntervalType.Years) { viewSize = 11; viewSizeType = DateTimeIntervalType.Months; } else if (viewSizeType == DateTimeIntervalType.Months) { viewSize = 4; viewSizeType = DateTimeIntervalType.Weeks; } else if (viewSizeType == DateTimeIntervalType.Weeks) { viewSize = 6; viewSizeType = DateTimeIntervalType.Days; } else if (viewSizeType == DateTimeIntervalType.Days) { viewSize = 23; viewSizeType = DateTimeIntervalType.Hours; } else if (viewSizeType == DateTimeIntervalType.Hours) { viewSize = 59; viewSizeType = DateTimeIntervalType.Minutes; } else if (viewSizeType == DateTimeIntervalType.Minutes) { viewSize = 59; viewSizeType = DateTimeIntervalType.Seconds; } else if (viewSizeType == DateTimeIntervalType.Seconds) { viewSize = 999; viewSizeType = DateTimeIntervalType.Milliseconds; } else { viewPosition = (axis.maximum - axis.marginView) - minViewSize; break; } // Double check that scaleView size is not smaller than min size newViewSize = ChartHelper.GetIntervalSize(viewPosition, viewSize, viewSizeType); if (newViewSize < minViewSize) { // Can't adjust size no more (restore prev. value) viewSize = currentSize; viewSizeType = currentSizeType; // Adjust the start position viewPosition = (axis.maximum - axis.marginView) - minViewSize; break; } } else { // Adjust the start position viewPosition = (axis.maximum - axis.marginView) - newViewSize; break; } } }