Ejemplo n.º 1
0
        /// <summary>
        /// Adjust areas rectangle coordinate to fit the 3D border
        /// </summary>
        /// <param name="graph">Graphics to draw the border on.</param>
        /// <param name="areasRect">Position to adjust.</param>
        public virtual void AdjustAreasPosition(ChartGraphics graph, ref SKRect areasRect)
        {
            SKSize relSizeLeftTop     = new(sizeLeftTop.Width, sizeLeftTop.Height);
            SKSize relSizeRightBottom = new(sizeRightBottom.Width, sizeRightBottom.Height);

            relSizeLeftTop.Width      += defaultRadiusSize * 0.7f;
            relSizeLeftTop.Height     += defaultRadiusSize * 0.85f;
            relSizeRightBottom.Width  += defaultRadiusSize * 0.7f;
            relSizeRightBottom.Height += defaultRadiusSize * 0.7f;
            relSizeLeftTop             = graph.GetRelativeSize(relSizeLeftTop);
            relSizeRightBottom         = graph.GetRelativeSize(relSizeRightBottom);

            if (relSizeLeftTop.Width > 30f)
            {
                relSizeLeftTop.Width = 0;
            }
            if (relSizeLeftTop.Height > 30f)
            {
                relSizeLeftTop.Height = 0;
            }
            if (relSizeRightBottom.Width > 30f)
            {
                relSizeRightBottom.Width = 0;
            }
            if (relSizeRightBottom.Height > 30f)
            {
                relSizeRightBottom.Height = 0;
            }

            areasRect.Left   += relSizeLeftTop.Width;
            areasRect.Right  -= Math.Min(areasRect.Width, relSizeLeftTop.Width + relSizeRightBottom.Width);
            areasRect.Top    += relSizeLeftTop.Height;
            areasRect.Bottom -= Math.Min(areasRect.Height, relSizeLeftTop.Height + relSizeRightBottom.Height);

            if (areasRect.Right > 100f)
            {
                if (areasRect.Width > 100f - areasRect.Right)
                {
                    areasRect.Right -= 100f - areasRect.Right;
                }
                else
                {
                    areasRect.Left -= 100f - areasRect.Right;
                }
            }
            if (areasRect.Bottom > 100f)
            {
                if (areasRect.Height > 100f - areasRect.Bottom)
                {
                    areasRect.Bottom -= 100f - areasRect.Bottom;
                }
                else
                {
                    areasRect.Top -= 100f - areasRect.Bottom;
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Adjust areas rectangle coordinate to fit the 3D border.
        /// </summary>
        /// <param name="graph">Graphics to draw the border on.</param>
        /// <param name="areasRect">Position to adjust.</param>
        public virtual void AdjustAreasPosition(ChartGraphics graph, ref SKRect areasRect)
        {
            SKSize borderSize = new(defaultRadiusSize / 2f, defaultRadiusSize / 2f);

            borderSize = graph.GetRelativeSize(borderSize);

            // Do not do anything if rectangle is too small
            if (borderSize.Width < 30f)
            {
                areasRect.Top   += borderSize.Width;
                areasRect.Right -= Math.Min(areasRect.Width, borderSize.Width * 2.5f);
            }

            if (borderSize.Height < 30f)
            {
                areasRect.Top    += borderSize.Height;
                areasRect.Bottom -= Math.Min(areasRect.Height, borderSize.Height * 2.5f);
            }

            if (areasRect.Left + areasRect.Width > 100f)
            {
                areasRect.Left -= 100f - areasRect.Width;
            }
            if (areasRect.Top + areasRect.Height > 100f)
            {
                areasRect.Top -= 100f - areasRect.Height;
            }
        }
Ejemplo n.º 3
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();
            }
        }
Ejemplo n.º 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();
                }
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Draws 3D button in the scroll bar
        /// </summary>
        /// <param name="graph">Chart graphics.</param>
        /// <param name="buttonRect">Button position.</param>
        /// <param name="pressedState">Indicates that button is pressed.</param>
        /// <param name="buttonType">Button type to draw.</param>
        internal void PaintScrollBar3DButton(
            ChartGraphics graph,
            SKRect buttonRect,
            bool pressedState,
            ScrollBarButtonType buttonType)
        {
            // Page Up/Down buttons do not require drawing
            if (buttonType == ScrollBarButtonType.LargeIncrement || buttonType == ScrollBarButtonType.LargeDecrement)
            {
                return;
            }

            // Get 3 levels of colors for button drawing
            var darkerColor  = ChartGraphics.GetGradientColor(_buttonCurrentColor, SKColors.Black, 0.5);
            var darkestColor = ChartGraphics.GetGradientColor(_buttonCurrentColor, SKColors.Black, 0.8);
            var lighterColor = ChartGraphics.GetGradientColor(_buttonCurrentColor, SKColors.White, 0.5);

            // Fill button rectangle background
            graph.FillRectangleRel(
                buttonRect,
                _buttonCurrentColor,
                ChartHatchStyle.None,
                "",
                ChartImageWrapMode.Tile,
                SKColor.Empty,
                ChartImageAlignmentStyle.Center,
                GradientStyle.None,
                SKColor.Empty,
                darkerColor,
                (pressedState) ? 1 : 0,
                ChartDashStyle.Solid,
                SKColor.Empty,
                0,
                PenAlignment.Outset);

            // Check if 2 or 1 pixel border will be drawn (if size too small)
            bool singlePixelBorder = Size <= 12;

            // Draw 3D effect around the button when not pressed
            if (!pressedState)
            {
                // Get relative size of 1 pixel
                SKSize pixelRelativeSize = new(1, 1);
                pixelRelativeSize = graph.GetRelativeSize(pixelRelativeSize);

                // Draw top/left border with button color
                graph.DrawLineRel(
                    (singlePixelBorder) ? lighterColor : _buttonCurrentColor, 1, ChartDashStyle.Solid,
                    new SKPoint(buttonRect.Left, buttonRect.Bottom),
                    new SKPoint(buttonRect.Left, buttonRect.Top));
                graph.DrawLineRel(
                    (singlePixelBorder) ? lighterColor : _buttonCurrentColor, 1, ChartDashStyle.Solid,
                    new SKPoint(buttonRect.Left, buttonRect.Top),
                    new SKPoint(buttonRect.Right, buttonRect.Top));

                // Draw right/bottom border with the darkest color
                graph.DrawLineRel(
                    (singlePixelBorder) ? darkerColor : darkestColor, 1, ChartDashStyle.Solid,
                    new SKPoint(buttonRect.Right, buttonRect.Bottom),
                    new SKPoint(buttonRect.Right, buttonRect.Top));
                graph.DrawLineRel(
                    (singlePixelBorder) ? darkerColor : darkestColor, 1, ChartDashStyle.Solid,
                    new SKPoint(buttonRect.Left, buttonRect.Bottom),
                    new SKPoint(buttonRect.Right, buttonRect.Bottom));

                if (!singlePixelBorder)
                {
                    // Draw right/bottom border (offset 1) with the dark color
                    graph.DrawLineRel(
                        darkerColor, 1, ChartDashStyle.Solid,
                        new SKPoint(buttonRect.Right - pixelRelativeSize.Width, buttonRect.Bottom - pixelRelativeSize.Height),
                        new SKPoint(buttonRect.Right - pixelRelativeSize.Width, buttonRect.Top + pixelRelativeSize.Height));
                    graph.DrawLineRel(
                        darkerColor, 1, ChartDashStyle.Solid,
                        new SKPoint(buttonRect.Left + pixelRelativeSize.Width, buttonRect.Bottom - pixelRelativeSize.Height),
                        new SKPoint(buttonRect.Right - pixelRelativeSize.Width, buttonRect.Bottom - pixelRelativeSize.Height));

                    // Draw top/left border (offset 1) with lighter color
                    graph.DrawLineRel(
                        lighterColor, 1, ChartDashStyle.Solid,
                        new SKPoint(buttonRect.Left + pixelRelativeSize.Width, buttonRect.Bottom - pixelRelativeSize.Height),
                        new SKPoint(buttonRect.Left + pixelRelativeSize.Width, buttonRect.Top + pixelRelativeSize.Height));
                    graph.DrawLineRel(
                        lighterColor, 1, ChartDashStyle.Solid,
                        new SKPoint(buttonRect.Left + pixelRelativeSize.Width, buttonRect.Left + pixelRelativeSize.Height),
                        new SKPoint(buttonRect.Right - pixelRelativeSize.Width, buttonRect.Left + pixelRelativeSize.Height));
                }
            }

            // Check axis orientation
            bool verticalAxis = (axis.AxisPosition == AxisPosition.Left ||
                                 axis.AxisPosition == AxisPosition.Right);

            // Set graphics transformation for button pressed mode
            float pressedShifting = (singlePixelBorder) ? 0.5f : 1f;

            if (pressedState)
            {
                graph.TranslateTransform(pressedShifting, pressedShifting);
            }

            // Draw button image
            SKRect buttonAbsRect = graph.GetAbsoluteRectangle(buttonRect);
            float  imageOffset   = (singlePixelBorder) ? 2 : 3;

            switch (buttonType)
            {
            case (ScrollBarButtonType.SmallDecrement):
            {
                // Calculate triangal points position
                SKPoint[] points = new SKPoint[3];
                if (verticalAxis)
                {
                    points[0].X = buttonAbsRect.Left + imageOffset;
                    points[0].Y = buttonAbsRect.Top + (imageOffset + 1f);
                    points[1].X = buttonAbsRect.Left + buttonAbsRect.Width / 2f;
                    points[1].Y = buttonAbsRect.Bottom - imageOffset;
                    points[2].X = buttonAbsRect.Right - imageOffset;
                    points[2].Y = buttonAbsRect.Top + (imageOffset + 1f);
                }
                else
                {
                    points[0].X = buttonAbsRect.Left + imageOffset;
                    points[0].Y = buttonAbsRect.Top + buttonAbsRect.Height / 2f;
                    points[1].X = buttonAbsRect.Right - (imageOffset + 1f);
                    points[1].Y = buttonAbsRect.Top + imageOffset;
                    points[2].X = buttonAbsRect.Right - (imageOffset + 1f);
                    points[2].Y = buttonAbsRect.Bottom - imageOffset;
                }

                using var brush = new SKPaint { Style = SKPaintStyle.Fill, Color = _lineCurrentColor };

                graph.FillPolygon(brush, points);

                break;
            }

            case (ScrollBarButtonType.SmallIncrement):
            {
                // Calculate triangal points position
                SKPoint[] points = new SKPoint[3];
                if (verticalAxis)
                {
                    points[0].X = buttonAbsRect.Left + imageOffset;
                    points[0].Y = buttonAbsRect.Bottom - (imageOffset + 1f);
                    points[1].X = buttonAbsRect.Left + buttonAbsRect.Width / 2f;
                    points[1].Y = buttonAbsRect.Top + imageOffset;
                    points[2].X = buttonAbsRect.Right - imageOffset;
                    points[2].Y = buttonAbsRect.Bottom - (imageOffset + 1f);
                }
                else
                {
                    points[0].X = buttonAbsRect.Right - imageOffset;
                    points[0].Y = buttonAbsRect.Top + buttonAbsRect.Height / 2f;
                    points[1].X = buttonAbsRect.Left + (imageOffset + 1f);
                    points[1].Y = buttonAbsRect.Top + imageOffset;
                    points[2].X = buttonAbsRect.Left + (imageOffset + 1f);
                    points[2].Y = buttonAbsRect.Bottom - imageOffset;
                }

                using var brush = new SKPaint { Style = SKPaintStyle.Fill, Color = _lineCurrentColor };
                graph.FillPolygon(brush, points);

                break;
            }

            case (ScrollBarButtonType.ZoomReset):
            {
                // Draw circule with a minus sign

                using var pen = new SKPaint { Style = SKPaintStyle.Fill, Color = _lineCurrentColor };

                graph.DrawEllipse(pen, buttonAbsRect.Left + imageOffset - 0.5f, buttonAbsRect.Top + imageOffset - 0.5f, buttonAbsRect.Width - 2f * imageOffset, buttonAbsRect.Height - 2f * imageOffset);
                graph.DrawLine(pen, buttonAbsRect.Left + imageOffset + 1.5f, buttonAbsRect.Top + buttonAbsRect.Height / 2f - 0.5f, buttonAbsRect.Right - imageOffset - 2.5f, buttonAbsRect.Top + buttonAbsRect.Height / 2f - 0.5f);

                break;
            }
            }

            // Reset graphics transformation for button pressed mode
            if (pressedState)
            {
                graph.TranslateTransform(-pressedShifting, -pressedShifting);
            }
        }