/// <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); }