Три способа нарисовать дугу

Узнайте, как использовать SkiaSharp для определения дуг тремя способами

Дуга — это кривая по окружности многоточия, например округленные части этого знака бесконечности:

Знак бесконечности

Несмотря на простоту этого определения, нет способа определить функцию рисования дуги, которая удовлетворяет каждой необходимости, и, следовательно, нет консенсуса среди графических систем лучшего способа рисования дуги. По этой причине SKPath класс не ограничивает себя только одним подходом.

SKPath определяет AddArc метод, пять разных ArcTo методов и два относительных RArcTo метода. Эти методы делятся на три категории, представляющие три очень разных подхода к указанию дуги. Какой из них вы используете, зависит от информации, доступной для определения дуги, и того, как эта дуга вписывается в другую графику, которую вы рисуете.

Угловая дуга

Для подхода угловой дуги к рисованию дуги требуется указать прямоугольник, ограничивающий многоточие. Дуга по окружности этого эллипса обозначается углами от центра эллипса, которые указывают на начало дуги и ее длины. Два разных метода рисуют угловые дуги. AddArc Это метод и ArcTo метод:

public void AddArc (SKRect oval, Single startAngle, Single sweepAngle)

public void ArcTo (SKRect oval, Single startAngle, Single sweepAngle, Boolean forceMoveTo)

Эти методы идентичны методам Android AddArc и [ArcTo]xref:Android.Graphics.Path.ArcTo*.. Метод iOS AddArc аналогичен, но ограничен дугами по окружности круга, а не обобщенным к многоточию.

Оба метода начинаются со SKRect значения, определяющего расположение и размер многоточия:

Овал, начинающий угловую дугу

Дуга является частью окружности этого многоточия.

Аргумент startAngle является по часовой стрелке в градусах относительно горизонтальной линии, нарисованной от центра многоточия справа. Аргумент sweepAngle относится к аргументу startAngle. Ниже приведены startAngle значения sweepAngle 60 градусов и 100 градусов соответственно:

Углы, определяющие угловую дугу

Дуга начинается с угла начала. Его длина регулируется углом сверток. Дуга показана здесь красным цветом:

Выделенная угловая дуга

Кривая, добавленная к пути с AddArc помощью метода, ArcTo просто является частью окружности эллипса:

Угловая дуга сама по себе

sweepAngle Аргументы startAngle могут быть отрицательными: дуга по часовой стрелке для положительных значений sweepAngle и счетчиков по часовой стрелке для отрицательных значений.

AddArcОднако не определяет закрытый контур. При вызове LineTo после AddArc, линия рисуется с конца дуги до точки в методе LineTo , и то же самое имеет значение true ArcTo.

AddArc автоматически запускает новый контур и функционально эквивалентен вызову ArcTo с окончательным аргументом true:

path.ArcTo (oval, startAngle, sweepAngle, true);

Этот последний аргумент вызывается forceMoveTo, и он фактически вызывает MoveTo вызов в начале дуги. Это начинает новый контур. Это не так с последним аргументом false:

path.ArcTo (oval, startAngle, sweepAngle, false);

Эта версия ArcTo рисует линию от текущей позиции к началу дуги. Это означает, что дуга может быть где-то в середине более крупного контура.

Страница "Угол дуги" позволяет использовать два ползунка, чтобы указать угол начала и очистки. XAML-файл создает экземпляры двух Slider элементов и объекта SKCanvasView. Обработчик PaintCanvas в файле AngleArcPage.xaml.cs рисует овал и дугу, используя два SKPaint объекта, определенных как поля:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    SKRect rect = new SKRect(100, 100, info.Width - 100, info.Height - 100);
    float startAngle = (float)startAngleSlider.Value;
    float sweepAngle = (float)sweepAngleSlider.Value;

    canvas.DrawOval(rect, outlinePaint);

    using (SKPath path = new SKPath())
    {
        path.AddArc(rect, startAngle, sweepAngle);
        canvas.DrawPath(path, arcPaint);
    }
}

Как видно, угол начала и угол сверток могут принимать отрицательные значения:

Тройной снимок экрана страницы

Этот подход к созданию дуги алгоритмически прост, и легко наследовать формулы параметрики, описывающие дугу. Зная размер и расположение многоточия, а также угол начала и развертки, начальные и конечные точки дуги можно вычислить с помощью простой тригонометрии:

x = oval.MidX + (oval.Width / 2) * cos(angle)

y = oval.MidY + (oval.Height / 2) * sin(angle)

Значение angle равно или startAnglestartAngle + sweepAngle.

Использование двух углов для определения дуги лучше всего подходит для случаев, когда вы знаете угловую длину дуги, которую вы хотите нарисовать, например, для создания круговой диаграммы. На странице "Взрывная круговая диаграмма " показано это. Класс ExplodedPieChartPage использует внутренний класс для определения некоторых сфабрикованных данных и цветов:

class ChartData
{
    public ChartData(int value, SKColor color)
    {
        Value = value;
        Color = color;
    }

    public int Value { private set; get; }

    public SKColor Color { private set; get; }
}

ChartData[] chartData =
{
    new ChartData(45, SKColors.Red),
    new ChartData(13, SKColors.Green),
    new ChartData(27, SKColors.Blue),
    new ChartData(19, SKColors.Magenta),
    new ChartData(40, SKColors.Cyan),
    new ChartData(22, SKColors.Brown),
    new ChartData(29, SKColors.Gray)
};

Обработчик PaintSurface сначала выполняет циклы по элементам, чтобы вычислить totalValues число. Из этого он может определить размер каждого элемента в виде доли суммы и преобразовать его в угол:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    int totalValues = 0;

    foreach (ChartData item in chartData)
    {
        totalValues += item.Value;
    }

    SKPoint center = new SKPoint(info.Width / 2, info.Height / 2);
    float explodeOffset = 50;
    float radius = Math.Min(info.Width / 2, info.Height / 2) - 2 * explodeOffset;
    SKRect rect = new SKRect(center.X - radius, center.Y - radius,
                             center.X + radius, center.Y + radius);

    float startAngle = 0;

    foreach (ChartData item in chartData)
    {
        float sweepAngle = 360f * item.Value / totalValues;

        using (SKPath path = new SKPath())
        using (SKPaint fillPaint = new SKPaint())
        using (SKPaint outlinePaint = new SKPaint())
        {
            path.MoveTo(center);
            path.ArcTo(rect, startAngle, sweepAngle, false);
            path.Close();

            fillPaint.Style = SKPaintStyle.Fill;
            fillPaint.Color = item.Color;

            outlinePaint.Style = SKPaintStyle.Stroke;
            outlinePaint.StrokeWidth = 5;
            outlinePaint.Color = SKColors.Black;

            // Calculate "explode" transform
            float angle = startAngle + 0.5f * sweepAngle;
            float x = explodeOffset * (float)Math.Cos(Math.PI * angle / 180);
            float y = explodeOffset * (float)Math.Sin(Math.PI * angle / 180);

            canvas.Save();
            canvas.Translate(x, y);

            // Fill and stroke the path
            canvas.DrawPath(path, fillPaint);
            canvas.DrawPath(path, outlinePaint);
            canvas.Restore();
        }

        startAngle += sweepAngle;
    }
}

Для каждого кругового среза создается новый SKPath объект. Путь состоит из линии из центра, а затем ArcTo для рисования дуги и другой линии обратно к центру Close результатов вызова. В этой программе отображаются "взрывные" круговой срезы, перемещая их все из центра на 50 пикселей. Для этой задачи требуется вектор в направлении средней точки угла развертки для каждого среза:

Тройной снимок экрана: страница

Чтобы увидеть, как выглядит без "взрыва", просто закомментируйте Translate звонок:

Тройной снимок экрана страницы

Тангент арка

Второй тип дуги, поддерживаемой SKPathтангенсной дугой, так называемый, потому что дуга является окружностью круга, который тангенс к двум подключенным линиям.

Тангенс дуги добавляется в путь с вызовом ArcTo метода с двумя SKPoint параметрами или ArcTo перегрузкой с отдельными Single параметрами для точек:

public void ArcTo (SKPoint point1, SKPoint point2, Single radius)

public void ArcTo (Single x1, Single y1, Single x2, Single y2, Single radius)

Этот ArcTo метод аналогичен функции PostScript arct (страница 532) и методу iOS AddArcToPoint .

Метод ArcTo состоит из трех точек:

  • Текущая точка контура или точка (0, 0), если MoveTo не была вызвана
  • Первый аргумент точки метода ArcTo , называемый точкой угла
  • Второй аргумент ArcToточки, на который называется целевая точка:

Три точки, начинающие тангенсную дугу

Эти три точки определяют две подключенные линии:

Линии, соединяющие три точки тангенсной дуги

Если три точки являются линейными - то есть, если они лежат на одной прямой линии - ни одна дуга не будет нарисована.

Метод ArcTo также включает radius параметр. Это определяет радиус круга:

Круг тангенсной дуги

Тангентная дуга не обобщена для многоточия.

Если две линии встречаются в любом углу, этот круг можно вставить между этими линиями, чтобы они были тангенсными для обеих строк:

Круг тангентной дуги между двумя линиями

Кривая, добавляемая к контуру, не касается ни одного из точек, указанных в методе ArcTo . Он состоит из прямой линии от текущей точки до первой тангенсной точки, и дуги, которая заканчивается во второй тангенсной точке, показанной здесь в красном цвете:

На схеме показана предыдущая схема с красной линией, показывающей выделенную дугу между двумя линиями.

Вот последняя прямая линия и дуга, которая добавляется в контур:

Выделенная дуга тангенс между двумя линиями

Контур можно продолжить со второй тангенсной точки.

Страница Tangent Arc позволяет экспериментировать с тангенсной дугой. Это первый из нескольких страниц, производных от InteractivePage, который определяет несколько удобных SKPaint объектов и выполняет TouchPoint обработку:

public class InteractivePage : ContentPage
{
    protected SKCanvasView baseCanvasView;
    protected TouchPoint[] touchPoints;

    protected SKPaint strokePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Black,
        StrokeWidth = 3
    };

    protected SKPaint redStrokePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Red,
        StrokeWidth = 15
    };

    protected SKPaint dottedStrokePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Black,
        StrokeWidth = 3,
        PathEffect = SKPathEffect.CreateDash(new float[] { 7, 7 }, 0)
    };

    protected void OnTouchEffectAction(object sender, TouchActionEventArgs args)
    {
        bool touchPointMoved = false;

        foreach (TouchPoint touchPoint in touchPoints)
        {
            float scale = baseCanvasView.CanvasSize.Width / (float)baseCanvasView.Width;
            SKPoint point = new SKPoint(scale * (float)args.Location.X,
                                        scale * (float)args.Location.Y);
            touchPointMoved |= touchPoint.ProcessTouchEvent(args.Id, args.Type, point);
        }

        if (touchPointMoved)
        {
            baseCanvasView.InvalidateSurface();
        }
    }
}

Класс TangentArcPage является производным от InteractivePage. Конструктор в файле TangentArcPage.xaml.cs отвечает за создание экземпляра и инициализацию touchPoints массива и настройку baseCanvasViewInteractivePage) объектаSKCanvasView, созданного в файле TangentArcPage.xaml:

public partial class TangentArcPage : InteractivePage
{
    public TangentArcPage()
    {
        touchPoints = new TouchPoint[3];

        for (int i = 0; i < 3; i++)
        {
            TouchPoint touchPoint = new TouchPoint
            {
                Center = new SKPoint(i == 0 ? 100 : 500,
                                     i != 2 ? 100 : 500)
            };
            touchPoints[i] = touchPoint;
        }

        InitializeComponent();

        baseCanvasView = canvasView;
        radiusSlider.Value = 100;
    }

    void sliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        if (canvasView != null)
        {
            canvasView.InvalidateSurface();
        }
    }
    ...
}

Обработчик PaintSurface использует ArcTo метод для рисования дуги на основе точек касания и а Slider, а также алгоритмически вычисляет круг, на основе угла:

public partial class TangentArcPage : InteractivePage
{
    ...
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        // Draw the two lines that meet at an angle
        using (SKPath path = new SKPath())
        {
            path.MoveTo(touchPoints[0].Center);
            path.LineTo(touchPoints[1].Center);
            path.LineTo(touchPoints[2].Center);
            canvas.DrawPath(path, dottedStrokePaint);
        }

        // Draw the circle that the arc wraps around
        float radius = (float)radiusSlider.Value;

        SKPoint v1 = Normalize(touchPoints[0].Center - touchPoints[1].Center);
        SKPoint v2 = Normalize(touchPoints[2].Center - touchPoints[1].Center);

        double dotProduct = v1.X * v2.X + v1.Y * v2.Y;
        double angleBetween = Math.Acos(dotProduct);
        float hypotenuse = radius / (float)Math.Sin(angleBetween / 2);
        SKPoint vMid = Normalize(new SKPoint((v1.X + v2.X) / 2, (v1.Y + v2.Y) / 2));
        SKPoint center = new SKPoint(touchPoints[1].Center.X + vMid.X * hypotenuse,
                                     touchPoints[1].Center.Y + vMid.Y * hypotenuse);

        canvas.DrawCircle(center.X, center.Y, radius, this.strokePaint);

        // Draw the tangent arc
        using (SKPath path = new SKPath())
        {
            path.MoveTo(touchPoints[0].Center);
            path.ArcTo(touchPoints[1].Center, touchPoints[2].Center, radius);
            canvas.DrawPath(path, redStrokePaint);
        }

        foreach (TouchPoint touchPoint in touchPoints)
        {
            touchPoint.Paint(canvas);
        }
    }

    // Vector methods
    SKPoint Normalize(SKPoint v)
    {
        float magnitude = Magnitude(v);
        return new SKPoint(v.X / magnitude, v.Y / magnitude);
    }

    float Magnitude(SKPoint v)
    {
        return (float)Math.Sqrt(v.X * v.X + v.Y * v.Y);
    }
}

Ниже приведена страница Tangent Arc:

Тройной снимок экрана страницы Tangent Arc

Тангенс дуги идеально подходит для создания округлых углов, таких как округленный прямоугольник. Так как SKPath уже включает AddRoundedRect метод, страница округленного хептагона демонстрирует, как использовать ArcTo для округления углов семистороннего многоугольника. (Код обобщен для любого регулярного многоугольника.)

Обработчик PaintSurfaceRoundedHeptagonPage класса содержит один for цикл для вычисления координат семи вершин гептагона, а второй — для вычисления середины семи сторон от этих вершин. Затем эти средние точки используются для создания пути:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    float cornerRadius = 100;
    int numVertices = 7;
    float radius = 0.45f * Math.Min(info.Width, info.Height);

    SKPoint[] vertices = new SKPoint[numVertices];
    SKPoint[] midPoints = new SKPoint[numVertices];

    double vertexAngle = -0.5f * Math.PI;       // straight up

    // Coordinates of the vertices of the polygon
    for (int vertex = 0; vertex < numVertices; vertex++)
    {
        vertices[vertex] = new SKPoint(radius * (float)Math.Cos(vertexAngle),
                                       radius * (float)Math.Sin(vertexAngle));
        vertexAngle += 2 * Math.PI / numVertices;
    }

    // Coordinates of the midpoints of the sides connecting the vertices
    for (int vertex = 0; vertex < numVertices; vertex++)
    {
        int prevVertex = (vertex + numVertices - 1) % numVertices;
        midPoints[vertex] = new SKPoint((vertices[prevVertex].X + vertices[vertex].X) / 2,
                                        (vertices[prevVertex].Y + vertices[vertex].Y) / 2);
    }

    // Create the path
    using (SKPath path = new SKPath())
    {
        // Begin at the first midpoint
        path.MoveTo(midPoints[0]);

        for (int vertex = 0; vertex < numVertices; vertex++)
        {
            SKPoint nextMidPoint = midPoints[(vertex + 1) % numVertices];

            // Draws a line from the current point, and then the arc
            path.ArcTo(vertices[vertex], nextMidPoint, cornerRadius);

            // Connect the arc with the next midpoint
            path.LineTo(nextMidPoint);
        }
        path.Close();

        // Render the path in the center of the screen
        using (SKPaint paint = new SKPaint())
        {
            paint.Style = SKPaintStyle.Stroke;
            paint.Color = SKColors.Blue;
            paint.StrokeWidth = 10;

            canvas.Translate(info.Width / 2, info.Height / 2);
            canvas.DrawPath(path, paint);
        }
    }
}

Вот работающая программа:

Тройной снимок экрана страницы округленного гептагона

Эллиптическая арка

Эллиптическая дуга добавляется в путь с вызовом ArcTo метода с двумя SKPoint параметрами или ArcTo перегрузкой с отдельными координатами X и Y:

public void ArcTo (SKPoint r, Single xAxisRotate, SKPathArcSize largeArc, SKPathDirection sweep, SKPoint xy)

public void ArcTo (Single rx, Single ry, Single xAxisRotate, SKPathArcSize largeArc, SKPathDirection sweep, Single x, Single y)

Эллиптическая дуга соответствует эллиптической дуге, включенной в масштабируемую векторную графику (SVG) и класс универсальная платформа WindowsArcSegment.

Эти ArcTo методы рисуют дугу между двумя точками, которые являются текущей точкой контура, и последним параметром метода (xyпараметром ArcTo или отдельными x и y параметрами):

Две точки, определяющие эллиптические дуги

Первый параметр ArcTo точки для метода (rили rxry) не является точкой вообще, но вместо этого указывает горизонтальную и вертикальную радию многоточия;

Многоточие, определяющее эллиптические дуги

Параметр xAxisRotate — это число часовых градусов для поворота этого многоточия:

Наклонный многоточие, определяющее эллиптические дуги

Если это наклонное многоточие затем позиционируется так, чтобы он касался двух точек, точки связаны двумя разными дугами:

Первый набор эллиптических дуг

Эти две дуги можно различать двумя способами: верхняя дуга больше нижней дуги, а по мере того как дуга рисуется слева направо, верхняя дуга рисуется в часовом направлении, а нижняя дуга рисуется в направлении по часовой стрелке.

Также можно вписать многоточие между двумя точками другим способом:

Второй набор эллиптических дуг

Теперь есть меньшая дуга на вершине, которая рисуется по часовой стрелке, и большая дуга на нижней части, которая рисуется по часовой стрелке.

Таким образом, эти две точки могут быть соединены дугой, определенной наклонной многоточием в общей сложности четыре способа:

Все четыре эллиптические дуги

Эти четыре дуги отличаются четырьмя сочетаниями аргументов SKPathArcSizeSKPathDirection типа перечисления и ArcTo метода:

  • red: SKPathArcSize.Large и SKPathDirection.Clockwise
  • зеленый: SKPathArcSize.Small и SKPathDirection.Clockwise
  • blue: SKPathArcSize.Small и SKPathDirection.CounterClockwise
  • magenta: SKPathArcSize.Large и SKPathDirection.CounterClockwise

Если многоточие наклона недостаточно большое, чтобы поместиться между двумя точками, то оно равномерно масштабируется, пока не будет достаточно большим. Только две уникальные дуги соединяют две точки в этом случае. Их можно отличить с параметром SKPathDirection .

Хотя этот подход к определению дуги звучит сложно при первой встрече, это единственный подход, который позволяет определить дугу с повернутым многоточием, и это часто самый простой подход, когда необходимо интегрировать дуги с другими частями контура.

Страница Elliptical Arc позволяет интерактивно задать две точки, а также размер и поворот многоточия. Класс EllipticalArcPage является производным от InteractivePage, а PaintSurface обработчик в файле кода EllipticalArcPage.xaml.cs выполняет рисование четырех дуг:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    using (SKPath path = new SKPath())
    {
        int colorIndex = 0;
        SKPoint ellipseSize = new SKPoint((float)xRadiusSlider.Value,
                                          (float)yRadiusSlider.Value);
        float rotation = (float)rotationSlider.Value;

        foreach (SKPathArcSize arcSize in Enum.GetValues(typeof(SKPathArcSize)))
            foreach (SKPathDirection direction in Enum.GetValues(typeof(SKPathDirection)))
            {
                path.MoveTo(touchPoints[0].Center);
                path.ArcTo(ellipseSize, rotation,
                           arcSize, direction,
                           touchPoints[1].Center);

                strokePaint.Color = colors[colorIndex++];
                canvas.DrawPath(path, strokePaint);
                path.Reset();
            }
    }

    foreach (TouchPoint touchPoint in touchPoints)
    {
        touchPoint.Paint(canvas);
    }
}

Здесь выполняется:

Тройной снимок экрана страницы Elliptical Arc

На странице Arc Infinity используется эллиптическая дуга для рисования знака бесконечности . Знак бесконечности основан на двух кругах с радиями 100 единиц, разделенных 100 единицами:

Два круга

Две линии, пересекающие друг друга, касаются обоих кругов:

Два круга с тангенсными линиями

Знак бесконечности — это сочетание частей этих кругов и двух линий. Чтобы использовать эллиптические дуги для рисования знака бесконечности, координаты, в которых две линии тангенты к кругам, должны быть определены.

Создайте правый прямоугольник в одном из кругов:

Два круга с тангенсными линиями и внедренным кругом

Радиус круга составляет 100 единиц, а гипотенуз треугольника составляет 150 единиц, поэтому угол α является арксин (инверсный синус) 100, разделенный на 150 или 41,8 градуса. Длина другой стороны треугольника составляет 150 раз косинус 41,8 градуса, или 112, который также можно вычислить теоремом Пифагора.

Затем координаты точки тангенса можно вычислить с помощью следующих сведений:

x = 112·cos(41.8) = 83

y = 112·sin(41.8) = 75

Четыре тангенсные точки являются все, что необходимо для рисования бесконечного знака, центрированного на точке (0, 0) с кругом радии 100:

Два круга с тангенсными линиями и координатами

Обработчик PaintSurface в ArcInfinityPage классе размещает знак бесконечности, чтобы точка (0, 0) располагалась в центре страницы и масштабирует путь к размеру экрана:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    using (SKPath path = new SKPath())
    {
        path.LineTo(83, 75);
        path.ArcTo(100, 100, 0, SKPathArcSize.Large, SKPathDirection.CounterClockwise, 83, -75);
        path.LineTo(-83, 75);
        path.ArcTo(100, 100, 0, SKPathArcSize.Large, SKPathDirection.Clockwise, -83, -75);
        path.Close();

        // Use path.TightBounds for coordinates without control points
        SKRect pathBounds = path.Bounds;

        canvas.Translate(info.Width / 2, info.Height / 2);
        canvas.Scale(Math.Min(info.Width / pathBounds.Width,
                              info.Height / pathBounds.Height));

        using (SKPaint paint = new SKPaint())
        {
            paint.Style = SKPaintStyle.Stroke;
            paint.Color = SKColors.Blue;
            paint.StrokeWidth = 5;

            canvas.DrawPath(path, paint);
        }
    }
}

Код использует Bounds свойство SKPath для определения измерений бесконечности синуса, чтобы масштабировать его до размера холста:

Тройной снимок экрана страницы Arc Infinity

Результат кажется немного маленьким, что предполагает, что Bounds свойство SKPath сообщает размер больше, чем путь.

Внутри, Skia приблизит дугу с помощью нескольких квадратных кривых Bézier. Эти кривые (как вы увидите в следующем разделе) содержат контрольные точки, которые управляют нарисовкой кривой, но не являются частью отрисованной кривой. Свойство Bounds включает эти контрольные точки.

Чтобы получить более жесткое соответствие, используйте TightBounds свойство, которое исключает контрольные точки. Ниже приведена программа, выполняемая в альбомном режиме, и с помощью TightBounds свойства для получения границ пути:

Тройной снимок экрана страницы Arc Infinity с жесткими границами

Хотя соединения между дуги и прямыми линиями математически гладки, изменение от дуги к прямой линии может показаться немного резко. Более лучший знак бесконечности представлен в следующей статье о трех типах кривых Bézier.