Beispiel #1
0
        /// <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);
                        }
                    }
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Paint FastLine Chart.
        /// </summary>
        /// <param name="graph">The Chart Graphics object.</param>
        /// <param name="common">The Common elements object.</param>
        /// <param name="area">Chart area for this chart.</param>
        /// <param name="seriesToDraw">Chart series to draw.</param>
        virtual public void Paint(
            ChartGraphics graph,
            CommonElements common,
            ChartArea area,
            Series seriesToDraw)
        {
            Common = common;
            Graph  = graph;
            bool clipRegionSet = false;

            if (area.Area3DStyle.Enable3D)
            {
                // Initialize variables
                chartArea3DEnabled = true;
                matrix3D           = area.matrix3D;
            }
            else
            {
                chartArea3DEnabled = false;
            }

            //************************************************************
            //** Loop through all series
            //************************************************************
            foreach (Series series in common.DataManager.Series)
            {
                // Process non empty series of the area with FastLine chart type
                if (string.Compare(series.ChartTypeName, Name, true, System.Globalization.CultureInfo.CurrentCulture) != 0 ||
                    series.ChartArea != area.Name ||
                    !series.IsVisible())
                {
                    continue;
                }

                // Get 3D series depth and Z position
                if (chartArea3DEnabled)
                {
                    area.GetSeriesZPositionAndDepth(series, out float seriesDepth, out seriesZCoordinate);
                    seriesZCoordinate += seriesDepth / 2.0f;
                }

                // Set active horizontal/vertical axis
                Axis   hAxis    = area.GetAxis(AxisName.X, series.XAxisType, (area.Area3DStyle.Enable3D) ? string.Empty : Series.XSubAxisName);
                Axis   vAxis    = area.GetAxis(AxisName.Y, series.YAxisType, (area.Area3DStyle.Enable3D) ? string.Empty : Series.YSubAxisName);
                double hAxisMin = hAxis.ViewMinimum;
                double hAxisMax = hAxis.ViewMaximum;
                double vAxisMin = vAxis.ViewMinimum;
                double vAxisMax = vAxis.ViewMaximum;

                // Get "PermittedPixelError" attribute
                float permittedPixelError = 1.0f;
                if (series.IsCustomPropertySet(CustomPropertyName.PermittedPixelError))
                {
                    string attrValue = series[CustomPropertyName.PermittedPixelError];

                    bool parseSucceed = float.TryParse(attrValue, NumberStyles.Any, CultureInfo.CurrentCulture, out float pixelError);

                    if (parseSucceed)
                    {
                        permittedPixelError = pixelError;
                    }
                    else
                    {
                        throw (new InvalidOperationException(SR.ExceptionCustomAttributeValueInvalid2("PermittedPixelError")));
                    }

                    // "PermittedPixelError" attribute value should be in range from zero to 1
                    if (permittedPixelError < 0f || permittedPixelError > 1f)
                    {
                        throw (new InvalidOperationException(SR.ExceptionCustomAttributeIsNotInRange0to1("PermittedPixelError")));
                    }
                }

                // Get pixel size in axes coordinates
                SKSize pixelSize            = graph.GetRelativeSize(new SKSize(permittedPixelError, permittedPixelError));
                SKSize axesMin              = graph.GetRelativeSize(new SKSize((float)hAxisMin, (float)vAxisMin));
                double axesValuesPixelSizeX = Math.Abs(hAxis.PositionToValue(axesMin.Width + pixelSize.Width, false) - hAxis.PositionToValue(axesMin.Width, false));

                // Create line pen
                SKPaint linePen = new() { Color = series.Color, StrokeWidth = series.BorderWidth };
                linePen.PathEffect = ChartGraphics.GetPenStyle(series.BorderDashStyle, series.BorderWidth);
                linePen.StrokeCap  = SKStrokeCap.Round;

                // Create empty line pen
                SKPaint emptyLinePen = new() { Style = SKPaintStyle.Stroke, Color = series.EmptyPointStyle.Color, StrokeWidth = series.EmptyPointStyle.BorderWidth };
                emptyLinePen.PathEffect = ChartGraphics.GetPenStyle(series.EmptyPointStyle.BorderDashStyle, series.EmptyPointStyle.BorderWidth);
                emptyLinePen.StrokeCap  = SKStrokeCap.Round;

                // Check if series is indexed
                bool indexedSeries = ChartHelper.IndexedSeries(Common, series.Name);

                // Loop through all ponts in the series
                int       index                      = 0;
                double    yValueRangeMin             = double.NaN;
                double    yValueRangeMax             = double.NaN;
                DataPoint pointRangeMin              = null;
                DataPoint pointRangeMax              = null;
                double    xValue                     = 0;
                double    yValue                     = 0;
                double    xValuePrev                 = 0;
                double    yValuePrev                 = 0;
                DataPoint prevDataPoint              = null;
                SKPoint   lastVerticalSegmentPoint   = SKPoint.Empty;
                SKPoint   prevPoint                  = SKPoint.Empty;
                SKPoint   currentPoint               = SKPoint.Empty;
                bool      prevPointInAxesCoordinates = false;
                bool      verticalLineDetected       = false;
                bool      prevPointIsEmpty           = false;
                bool      currentPointIsEmpty        = false;
                bool      firstNonEmptyPoint         = false;
                double    xPixelConverter            = (graph.Common.ChartPicture.Width - 1.0) / 100.0;
                double    yPixelConverter            = (graph.Common.ChartPicture.Height - 1.0) / 100.0;
                foreach (DataPoint point in series.Points)
                {
                    // Get point X and Y values
                    xValue = (indexedSeries) ? index + 1 : point.XValue;
                    xValue = hAxis.GetLogValue(xValue);
                    yValue = vAxis.GetLogValue(point.YValues[0]);
                    currentPointIsEmpty = point.IsEmpty;

                    // NOTE: Fixes issue #7094
                    // If current point is non-empty but the previous one was,
                    // use empty point style properties to draw it.
                    if (prevPointIsEmpty && !currentPointIsEmpty && !firstNonEmptyPoint)
                    {
                        firstNonEmptyPoint  = true;
                        currentPointIsEmpty = true;
                    }
                    else
                    {
                        firstNonEmptyPoint = false;
                    }

                    // Check if line is completly out of the data scaleView
                    if (!verticalLineDetected &&
                        ((xValue < hAxisMin && xValuePrev < hAxisMin) ||
                         (xValue > hAxisMax && xValuePrev > hAxisMax) ||
                         (yValue < vAxisMin && yValuePrev < vAxisMin) ||
                         (yValue > vAxisMax && yValuePrev > vAxisMax)))
                    {
                        xValuePrev = xValue;
                        yValuePrev = yValue;
                        prevPointInAxesCoordinates = true;
                        ++index;
                        continue;
                    }
                    else if (!clipRegionSet && (xValuePrev < hAxisMin || xValuePrev > hAxisMax ||
                                                xValue > hAxisMax || xValue < hAxisMin ||
                                                yValuePrev < vAxisMin || yValuePrev > vAxisMax ||
                                                yValue < vAxisMin || yValue > vAxisMax))
                    {
                        // Set clipping region for line drawing
                        graph.SetClip(area.PlotAreaPosition.ToSKRect());
                        clipRegionSet = true;
                    }

                    // Check if point may be skipped
                    // Check if points X value in acceptable error boundary
                    if (index > 0 &&
                        currentPointIsEmpty == prevPointIsEmpty && Math.Abs(xValue - xValuePrev) < axesValuesPixelSizeX)
                    {
                        if (!verticalLineDetected)
                        {
                            verticalLineDetected = true;
                            if (yValue > yValuePrev)
                            {
                                yValueRangeMax = yValue;
                                yValueRangeMin = yValuePrev;
                                pointRangeMax  = point;
                                pointRangeMin  = prevDataPoint;
                            }
                            else
                            {
                                yValueRangeMax = yValuePrev;
                                yValueRangeMin = yValue;
                                pointRangeMax  = prevDataPoint;
                                pointRangeMin  = point;
                            }
                        }
                        else
                        {
                            if (yValue > yValueRangeMax)
                            {
                                yValueRangeMax = yValue;
                                pointRangeMax  = point;
                            }
                            else if (yValue < yValueRangeMin)
                            {
                                yValueRangeMin = yValue;
                                pointRangeMin  = point;
                            }
                        }

                        // Remember last point
                        prevDataPoint = point;

                        // Remember last vertical range point
                        // Note! Point is in axes coordinate.
                        lastVerticalSegmentPoint.Y = (float)yValue;

                        // Increase counter and proceed to next data point
                        ++index;
                        continue;
                    }

                    // Get point pixel position
                    currentPoint.X = (float)
                                     (hAxis.GetLinearPosition(xValue) * xPixelConverter);
                    currentPoint.Y = (float)
                                     (vAxis.GetLinearPosition(yValue) * yPixelConverter);

                    // Check if previous point must be converted from axes values to pixels
                    if (prevPointInAxesCoordinates)
                    {
                        prevPoint.X = (float)
                                      (hAxis.GetLinearPosition(xValuePrev) * xPixelConverter);
                        prevPoint.Y = (float)
                                      (vAxis.GetLinearPosition(yValuePrev) * yPixelConverter);
                    }

                    // Draw accumulated vertical line (with minimal X values differences)
                    if (verticalLineDetected)
                    {
                        // Convert Y coordinates to pixels
                        yValueRangeMin = (vAxis.GetLinearPosition(yValueRangeMin) * yPixelConverter);
                        yValueRangeMax = (vAxis.GetLinearPosition(yValueRangeMax) * yPixelConverter);

                        // Draw accumulated vertical line
                        DrawLine(
                            series,
                            prevDataPoint,
                            pointRangeMin,
                            pointRangeMax,
                            index,
                            (prevPointIsEmpty) ? emptyLinePen : linePen,
                            prevPoint.X,
                            (float)yValueRangeMin,
                            prevPoint.X,
                            (float)yValueRangeMax);

                        // Reset vertical line detected flag
                        verticalLineDetected = false;

                        // Convert last point of the vertical line segment to pixel coordinates
                        prevPoint.Y = (float)
                                      (vAxis.GetLinearPosition(lastVerticalSegmentPoint.Y) * yPixelConverter);
                    }

                    // Draw line from previous to current point
                    if (index > 0)
                    {
                        DrawLine(
                            series,
                            point,
                            pointRangeMin,
                            pointRangeMax,
                            index,
                            (currentPointIsEmpty) ? emptyLinePen : linePen,
                            prevPoint.X,
                            prevPoint.Y,
                            currentPoint.X,
                            currentPoint.Y);
                    }

                    // Remember last point coordinates
                    xValuePrev    = xValue;
                    yValuePrev    = yValue;
                    prevDataPoint = point;
                    prevPoint     = currentPoint;
                    prevPointInAxesCoordinates = false;
                    prevPointIsEmpty           = currentPointIsEmpty;
                    ++index;
                }

                // Draw last accumulated line segment
                if (verticalLineDetected)
                {
                    // Check if previous point must be converted from axes values to pixels
                    if (prevPointInAxesCoordinates)
                    {
                        prevPoint.X = (float)
                                      (hAxis.GetLinearPosition(xValuePrev) * xPixelConverter);
                        prevPoint.Y = (float)
                                      (vAxis.GetLinearPosition(yValuePrev) * yPixelConverter);
                    }

                    // Convert Y coordinates to pixels
                    yValueRangeMin = (vAxis.GetLinearPosition(yValueRangeMin) * yPixelConverter);
                    yValueRangeMax = (vAxis.GetLinearPosition(yValueRangeMax) * yPixelConverter);

                    // Draw accumulated vertical line
                    DrawLine(
                        series,
                        prevDataPoint,
                        pointRangeMin,
                        pointRangeMax,
                        index - 1,
                        prevPointIsEmpty ? emptyLinePen : linePen,
                        prevPoint.X,
                        (float)yValueRangeMin,
                        prevPoint.X,
                        (float)yValueRangeMax);
                }
            }

            // Reset Clip Region
            if (clipRegionSet)
            {
                graph.ResetClip();
            }
        }
        /// <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);
        }
Beispiel #4
0
        /// <summary>
        /// Paint FastPoint Chart.
        /// </summary>
        /// <param name="graph">The Chart Graphics object.</param>
        /// <param name="common">The Common elements object.</param>
        /// <param name="area">Chart area for this chart.</param>
        /// <param name="seriesToDraw">Chart series to draw.</param>
        virtual public void Paint(
            ChartGraphics graph,
            CommonElements common,
            ChartArea area,
            Series seriesToDraw)
        {
            Common = common;
            Graph  = graph;
            if (area.Area3DStyle.Enable3D)
            {
                // Initialize variables
                chartArea3DEnabled = true;
                matrix3D           = area.matrix3D;
            }
            else
            {
                chartArea3DEnabled = false;
            }

            //************************************************************
            //** Loop through all series
            //************************************************************
            foreach (Series series in common.DataManager.Series)
            {
                // Process non empty series of the area with FastPoint chart type
                if (String.Compare(series.ChartTypeName, Name, true, System.Globalization.CultureInfo.CurrentCulture) != 0 ||
                    series.ChartArea != area.Name ||
                    !series.IsVisible())
                {
                    continue;
                }

                // Get 3D series depth and Z position
                if (chartArea3DEnabled)
                {
                    area.GetSeriesZPositionAndDepth(series, out float seriesDepth, out seriesZCoordinate);
                    seriesZCoordinate += seriesDepth / 2.0f;
                }

                // Set active horizontal/vertical axis
                Axis   hAxis    = area.GetAxis(AxisName.X, series.XAxisType, (area.Area3DStyle.Enable3D) ? string.Empty : Series.XSubAxisName);
                Axis   vAxis    = area.GetAxis(AxisName.Y, series.YAxisType, (area.Area3DStyle.Enable3D) ? string.Empty : Series.YSubAxisName);
                double hAxisMin = hAxis.ViewMinimum;
                double hAxisMax = hAxis.ViewMaximum;
                double vAxisMin = vAxis.ViewMinimum;
                double vAxisMax = vAxis.ViewMaximum;

                // Get "PermittedPixelError" attribute.
                // By default use 1/3 of the marker size.
                float permittedPixelError = series.MarkerSize / 3f;
                if (series.IsCustomPropertySet(CustomPropertyName.PermittedPixelError))
                {
                    string attrValue = series[CustomPropertyName.PermittedPixelError];

                    bool parseSucceed = float.TryParse(attrValue, NumberStyles.Any, CultureInfo.CurrentCulture, out float pixelError);

                    if (parseSucceed)
                    {
                        permittedPixelError = pixelError;
                    }
                    else
                    {
                        throw (new InvalidOperationException(SR.ExceptionCustomAttributeValueInvalid2("PermittedPixelError")));
                    }

                    // "PermittedPixelError" attribute value should be in range from zero to 1
                    if (permittedPixelError < 0f || permittedPixelError > 1f)
                    {
                        throw (new InvalidOperationException(SR.ExceptionCustomAttributeIsNotInRange0to1("PermittedPixelError")));
                    }
                }

                // Get pixel size in axes coordinates
                SKSize pixelSize            = graph.GetRelativeSize(new SKSize(permittedPixelError, permittedPixelError));
                SKSize axesMin              = graph.GetRelativeSize(new SKSize((float)hAxisMin, (float)vAxisMin));
                double axesValuesPixelSizeX = Math.Abs(hAxis.PositionToValue(axesMin.Width + pixelSize.Width, false) - hAxis.PositionToValue(axesMin.Width, false));
                double axesValuesPixelSizeY = Math.Abs(vAxis.PositionToValue(axesMin.Height + pixelSize.Height, false) - vAxis.PositionToValue(axesMin.Height, false));

                // Create point marker brush
                SKPaint markerBrush      = new() { Color = (series.MarkerColor == SKColor.Empty) ? series.Color : series.MarkerColor, Style = SKPaintStyle.Fill };
                SKPaint emptyMarkerBrush = new() { Color = (series.EmptyPointStyle.MarkerColor == SKColor.Empty) ? series.EmptyPointStyle.Color : series.EmptyPointStyle.MarkerColor, Style = SKPaintStyle.Fill };

                // Create point marker border pen
                SKPaint borderPen      = null;
                SKPaint emptyBorderPen = null;
                if (series.MarkerBorderColor != SKColor.Empty && series.MarkerBorderWidth > 0)
                {
                    borderPen = new SKPaint()
                    {
                        Style = SKPaintStyle.Stroke, Color = series.MarkerBorderColor, StrokeWidth = series.MarkerBorderWidth
                    };
                }
                if (series.EmptyPointStyle.MarkerBorderColor != SKColor.Empty && series.EmptyPointStyle.MarkerBorderWidth > 0)
                {
                    emptyBorderPen = new SKPaint()
                    {
                        Style = SKPaintStyle.Stroke, Color = series.EmptyPointStyle.MarkerBorderColor, StrokeWidth = series.EmptyPointStyle.MarkerBorderWidth
                    };
                }

                // Check if series is indexed
                bool indexedSeries = ChartHelper.IndexedSeries(Common, series.Name);

                // Get marker size taking in consideration current DPIs
                int markerSize = series.MarkerSize;
                if (graph != null && graph.Graphics != null)
                {
                    // Marker size is in pixels and we do the mapping for higher DPIs
                    markerSize = Math.Max(markerSize, markerSize);
                }

                // Loop through all ponts in the series
                int         index            = 0;
                double      xValuePrev       = 0.0;
                double      yValuePrev       = 0.0;
                SKPoint     currentPoint     = SKPoint.Empty;
                double      xPixelConverter  = (graph.Common.ChartPicture.Width - 1.0) / 100.0;
                double      yPixelConverter  = (graph.Common.ChartPicture.Height - 1.0) / 100.0;
                MarkerStyle markerStyle      = series.MarkerStyle;
                MarkerStyle emptyMarkerStyle = series.EmptyPointStyle.MarkerStyle;
                foreach (DataPoint point in series.Points)
                {
                    // Get point X and Y values
                    double xValue = (indexedSeries) ? index + 1 : point.XValue;
                    xValue = hAxis.GetLogValue(xValue);
                    double yValue = vAxis.GetLogValue(point.YValues[0]);
                    bool   currentPointIsEmpty = point.IsEmpty;

                    // Check if point is completly out of the data scaleView
                    if (xValue < hAxisMin ||
                        xValue > hAxisMax ||
                        yValue < vAxisMin ||
                        yValue > vAxisMax)
                    {
                        xValuePrev = xValue;
                        yValuePrev = yValue;
                        ++index;
                        continue;
                    }

                    // Check if point may be skipped
                    if (index > 0 && Math.Abs(xValue - xValuePrev) < axesValuesPixelSizeX &&
                        Math.Abs(yValue - yValuePrev) < axesValuesPixelSizeY)
                    {
                        // Increase counter and proceed to the next data point
                        ++index;
                        continue;
                    }

                    // Get point pixel position
                    currentPoint.X = (float)
                                     (hAxis.GetLinearPosition(xValue) * xPixelConverter);
                    currentPoint.Y = (float)
                                     (vAxis.GetLinearPosition(yValue) * yPixelConverter);

                    // Draw point marker
                    MarkerStyle currentMarkerStyle = (currentPointIsEmpty) ? emptyMarkerStyle : markerStyle;
                    if (currentMarkerStyle != MarkerStyle.None)
                    {
                        this.DrawMarker(
                            graph,
                            point,
                            index,
                            currentPoint,
                            currentMarkerStyle,
                            markerSize,
                            (currentPointIsEmpty) ? emptyMarkerBrush : markerBrush,
                            (currentPointIsEmpty) ? emptyBorderPen : borderPen);
                    }

                    // Remember last point coordinates
                    xValuePrev = xValue;
                    yValuePrev = yValue;
                    ++index;
                }

                // Dispose used brushes and pens
                markerBrush.Dispose();
                emptyMarkerBrush.Dispose();
                if (borderPen != null)
                {
                    borderPen.Dispose();
                }
                if (emptyBorderPen != null)
                {
                    emptyBorderPen.Dispose();
                }
            }
        }
Beispiel #5
0
        /// <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;
                }
            }
        }