Три типа кривых Безье

Узнайте, как использовать SkiaSharp для отрисовки кубических, квадратных и конических кривых Bézier

Кривая Bézier названа в честь Пьера Bézier (1910 –1999), французского инженера автомобильной компании Renault, которая использовала кривую для компьютерного проектирования автомобилей.

Кривые Bézier известны как хорошо подходят для интерактивного дизайна: они хорошо ведут себя - иными словами, нет сингулярности, которые вызывают кривую стать бесконечной или неприхотливой - и они, как правило, эстетически приятно:

Пример кривой Bezier

Символьные контуры шрифтов на основе компьютера обычно определяются кривыми Bézier.

Статья Википедии о кривой Bézier содержит некоторые полезные справочные сведения. Термин Bézier кривой фактически относится к семейству аналогичных кривых. SkiaSharp поддерживает три типа кривых Bézier, называемых кубическими, квадратичными и коническими. Коник также известен как рациональная квадратика.

Кубическая кривая Bézier

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

К объекту можно добавить кубическую кривую SKPath Bézier, используя CubicTo метод с тремя SKPoint параметрами, или CubicTo перегрузку с отдельными x параметрами и y параметрами:

public void CubicTo (SKPoint point1, SKPoint point2, SKPoint point3)

public void CubicTo (Single x1, Single y1, Single x2, Single y2, Single x3, Single y3)

Кривая начинается в текущей точке контура. Полная кубическая кривая Bezier определяется четырьмя точками:

  • начальная точка: текущая точка в контуре или (0, 0), если MoveTo не была вызвана
  • первая контрольная точка: point1 в вызове CubicTo
  • вторая контрольная точка: point2 в вызове CubicTo
  • конечная точка: point3 в вызове CubicTo

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

Лучший способ получить ощущение для кубической кривой Bézier является экспериментирование. Это цель страницы "Кривая Bezier", наследуемой от InteractivePage. Файл BezierCurvePage.xaml создает SKCanvasView экземпляр и a TouchEffect. Файл кода BezierCurvePage.xaml.cs создает четыре TouchPoint объекта в конструкторе. Обработчик PaintSurface событий создает SKPath кривую Bézier на основе четырех TouchPoint объектов, а также рисует пунктирные линии из точек управления до конечных точек:

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

    canvas.Clear();

    // Draw path with cubic Bezier curve
    using (SKPath path = new SKPath())
    {
        path.MoveTo(touchPoints[0].Center);
        path.CubicTo(touchPoints[1].Center,
                     touchPoints[2].Center,
                     touchPoints[3].Center);

        canvas.DrawPath(path, strokePaint);
    }

    // Draw tangent lines
    canvas.DrawLine(touchPoints[0].Center.X,
                    touchPoints[0].Center.Y,
                    touchPoints[1].Center.X,
                    touchPoints[1].Center.Y, dottedStrokePaint);

    canvas.DrawLine(touchPoints[2].Center.X,
                    touchPoints[2].Center.Y,
                    touchPoints[3].Center.X,
                    touchPoints[3].Center.Y, dottedStrokePaint);

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

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

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

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

Кубическая кривая Bézier всегда ограничивается конвексным четырехлатеральным соединением четырех точек. Это называется выпуклой оболочкой. Если контрольные точки лежат на прямой линии между начальной и конечной точкой, то кривая Bézier отображается в виде прямой линии. Но кривая также может пересекать себя, как демонстрирует третий снимок экрана.

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

  • вторая контрольная точка первой кривой
  • конечная точка первой кривой, которая также является начальной точкой второй кривой.
  • первая контрольная точка второй кривой

В следующей статье о данных пути SVG вы узнаете, как упростить определение гладких подключенных кривых Bézier.

Иногда полезно знать базовые параметрические уравнения, которые отображают кубическую кривую Bézier. Для диапазонов от 0 до 1 параметрические уравнения приведены следующим образом:

x(t) = (1 – t)₀ + 3t(1 – t)₁ + 3t²(1 – t)x₃

y(t) = (1 – t)₀ + 3t(1 – t)₁ + 3t₁ + 3t²(1 – t)y₃

Самый высокий экспонент из 3 подтверждает, что они являются кубическими полиномиалами. Легко убедиться, что при t значении 0 точка — (x₀, y₀), которая является начальной точкой, а когда t равно 1, точка — (x₃, y₃), которая является конечной точкой. Рядом с начальной точкой (для низких значений t), первая контрольная точка (x₁, y₁) имеет сильный эффект, а рядом с конечной точкой (высокие значения не') второй контрольной точки (xfx, y₁) имеет сильный эффект.

Приблизительное приближение кривой Bezier к циклическим дугам

Иногда удобно использовать кривую Bézier для отрисовки круговой дуги. Кубическая кривая Bézier может приблизить круговую дугу очень хорошо до четверти круга, поэтому четыре подключенные кривые Bézier могут определить целый круг. Это приближение рассматривается в двух статьях, опубликованных более 25 лет назад:

Тор Dokken, и т. д. "Хорошее приближение кругов кривыми непрерывного Bézier", компьютерная помощь геометрического дизайна 7 (1990), 33-41.

Майкл Голдапп, "Приближение циклических дуг с помощью кубических полиномиал", компьютерная помощь геометрического дизайна 8 (1991), 227-238.

На следующей схеме показаны четыре точки с меткой pto, pt1pt2и pt3 определение кривой Bézier (показанная красным цветом), которая приблизит круговую дугу:

Приближение круговой дуги с кривой Bézier

Линии от начальной и конечной точек к контрольным точкам касаются к кругу и к кривой Bézier, и они имеют длину L. Первая статья, приведенная выше, указывает, что кривая Bézier лучше всего приблизит круговую дугу, когда эта длина L вычисляется следующим образом:

L = 4 × tan(α / 4) / 3

На рисунке показан угол 45 градусов, поэтому L равен 0,265. В коде это значение умножается на нужный радиус круга.

Страница Bezier Circular Arc позволяет экспериментировать с определением кривой Bézier, чтобы приблизить круговую дугу для углов, начиная до 180 градусов. Файл BezierCircularArcPage.xaml создает экземпляр SKCanvasView и объект Slider для выбора угла. Обработчик PaintSurface событий в файле кода BezierCircularArgPage.xaml.cs использует преобразование для задания точки (0, 0) в центре холста. Он рисует круг в центре этой точки для сравнения, а затем вычисляет две контрольные точки для кривой Bézier:

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

    canvas.Clear();

    // Translate to center
    canvas.Translate(info.Width / 2, info.Height / 2);

    // Draw the circle
    float radius = Math.Min(info.Width, info.Height) / 3;
    canvas.DrawCircle(0, 0, radius, blackStroke);

    // Get the value of the Slider
    float angle = (float)angleSlider.Value;

    // Calculate length of control point line
    float length = radius * 4 * (float)Math.Tan(Math.PI * angle / 180 / 4) / 3;

    // Calculate sin and cosine for half that angle
    float sin = (float)Math.Sin(Math.PI * angle / 180 / 2);
    float cos = (float)Math.Cos(Math.PI * angle / 180 / 2);

    // Find the end points
    SKPoint point0 = new SKPoint(-radius * sin, radius * cos);
    SKPoint point3 = new SKPoint(radius * sin, radius * cos);

    // Find the control points
    SKPoint point0Normalized = Normalize(point0);
    SKPoint point1 = point0 + new SKPoint(length * point0Normalized.Y,
                                          -length * point0Normalized.X);

    SKPoint point3Normalized = Normalize(point3);
    SKPoint point2 = point3 + new SKPoint(-length * point3Normalized.Y,
                                          length * point3Normalized.X);

    // Draw the points
    canvas.DrawCircle(point0.X, point0.Y, 10, blackFill);
    canvas.DrawCircle(point1.X, point1.Y, 10, blackFill);
    canvas.DrawCircle(point2.X, point2.Y, 10, blackFill);
    canvas.DrawCircle(point3.X, point3.Y, 10, blackFill);

    // Draw the tangent lines
    canvas.DrawLine(point0.X, point0.Y, point1.X, point1.Y, dottedStroke);
    canvas.DrawLine(point3.X, point3.Y, point2.X, point2.Y, dottedStroke);

    // Draw the Bezier curve
    using (SKPath path = new SKPath())
    {
        path.MoveTo(point0);
        path.CubicTo(point1, point2, point3);
        canvas.DrawPath(path, redStroke);
    }
}

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

Начальные и конечные точки (point0 и point3) вычисляются на основе обычных параметрических уравнений для круга. Так как круг находится в центре (0, 0), эти точки также можно рассматривать как радиальные векторы от центра круга до окружности. Контрольные точки находятся на линиях, которые касаются к кругу, поэтому они находятся в правых углах этих радиальных векторов. Вектор с правым углом к другому — это просто исходный вектор с координатами X и Y, которые переключились, и один из них сделал отрицательным.

Вот программа работает с различными углами:

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

Внимательно посмотрите на третий снимок экрана, и вы увидите, что кривая Bézier особенно отклоняется от полуциря, когда угол составляет 180 градусов, но экран iOS показывает, что он, кажется, подходит четверть круга просто хорошо, когда угол составляет 90 градусов.

Вычисление координат двух контрольных точек довольно легко, когда круг квартала ориентирован следующим образом:

Приближение четвертого круга с кривой Bézier

Если радиус круга равен 100, то L составляет 55, и это легкое число, которое нужно помнить.

Квадратная страница "Круг" анимирует фигуру между кругом и квадратом. Круг приблизился к четырем кривым Bézier, координаты которых отображаются в первом столбце этого определения массива SquaringTheCirclePage в классе:

public class SquaringTheCirclePage : ContentPage
{
    SKPoint[,] points =
    {
        { new SKPoint(   0,  100), new SKPoint(     0,    125), new SKPoint() },
        { new SKPoint(  55,  100), new SKPoint( 62.5f,  62.5f), new SKPoint() },
        { new SKPoint( 100,   55), new SKPoint( 62.5f,  62.5f), new SKPoint() },
        { new SKPoint( 100,    0), new SKPoint(   125,      0), new SKPoint() },
        { new SKPoint( 100,  -55), new SKPoint( 62.5f, -62.5f), new SKPoint() },
        { new SKPoint(  55, -100), new SKPoint( 62.5f, -62.5f), new SKPoint() },
        { new SKPoint(   0, -100), new SKPoint(     0,   -125), new SKPoint() },
        { new SKPoint( -55, -100), new SKPoint(-62.5f, -62.5f), new SKPoint() },
        { new SKPoint(-100,  -55), new SKPoint(-62.5f, -62.5f), new SKPoint() },
        { new SKPoint(-100,    0), new SKPoint(  -125,      0), new SKPoint() },
        { new SKPoint(-100,   55), new SKPoint(-62.5f,  62.5f), new SKPoint() },
        { new SKPoint( -55,  100), new SKPoint(-62.5f,  62.5f), new SKPoint() },
        { new SKPoint(   0,  100), new SKPoint(     0,    125), new SKPoint() }
    };
    ...
}

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

Третий столбец массива предназначен для интерполированных значений анимации. Страница задает таймер для 16 миллисекунд, а PaintSurface обработчик вызывается по этой частоте:

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

    canvas.Clear();

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

    // Interpolate
    TimeSpan timeSpan = new TimeSpan(DateTime.Now.Ticks);
    float t = (float)(timeSpan.TotalSeconds % 3 / 3);   // 0 to 1 every 3 seconds
    t = (1 + (float)Math.Sin(2 * Math.PI * t)) / 2;     // 0 to 1 to 0 sinusoidally

    for (int i = 0; i < 13; i++)
    {
        points[i, 2] = new SKPoint(
            (1 - t) * points[i, 0].X + t * points[i, 1].X,
            (1 - t) * points[i, 0].Y + t * points[i, 1].Y);
    }

    // Create the path and draw it
    using (SKPath path = new SKPath())
    {
        path.MoveTo(points[0, 2]);

        for (int i = 1; i < 13; i += 3)
        {
            path.CubicTo(points[i, 2], points[i + 1, 2], points[i + 2, 2]);
        }
        path.Close();

        canvas.DrawPath(path, cyanFill);
        canvas.DrawPath(path, blueStroke);
    }
}

Точки интерполируются на основе синусоидно колебающегося значения t. Затем интерполированные точки используются для создания серии из четырех подключенных кривых Bézier. Ниже приведена анимация:

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

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

Страница Bezier Infinity также использует преимущества способности кривой Bézier приблизиться к круговой дуге. Ниже приведен PaintSurface обработчик классаBezierInfinityPage:

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.MoveTo(0, 0);                                // Center
        path.CubicTo(  50,  -50,   95, -100,  150, -100); // To top of right loop
        path.CubicTo( 205, -100,  250,  -55,  250,    0); // To far right of right loop
        path.CubicTo( 250,   55,  205,  100,  150,  100); // To bottom of right loop
        path.CubicTo(  95,  100,   50,   50,    0,    0); // Back to center  
        path.CubicTo( -50,  -50,  -95, -100, -150, -100); // To top of left loop
        path.CubicTo(-205, -100, -250,  -55, -250,    0); // To far left of left loop
        path.CubicTo(-250,   55, -205,  100, -150,  100); // To bottom of left loop
        path.CubicTo( -95,  100,  -50,   50,    0,    0); // Back to center
        path.Close();

        SKRect pathBounds = path.Bounds;
        canvas.Translate(info.Width / 2, info.Height / 2);
        canvas.Scale(0.9f * 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);
        }
    }
}

Это может быть хорошее упражнение для построения этих координат на бумаге графа, чтобы узнать, как они связаны. Знак бесконечности сосредоточен вокруг точки (0, 0), а два цикла имеют центры (–150, 0) и (150, 0) и радии 100. В серии CubicTo команд можно увидеть координаты X контрольных точек, принимающие значения –95 и –205 (это –150 плюс и минус 55), 205 и 95 (150 плюс и минус 55), а также 250 и –250 для правых и левых сторон. Единственное исключение заключается в том, что знак бесконечности пересекает себя в центре. В этом случае контрольные точки имеют координаты с сочетанием 50 и –50, чтобы выпрямлять кривую вблизи центра.

Вот знак бесконечности:

Тройной снимок экрана страницы Bézier Infinity

Это несколько гладко в центре, чем знак бесконечности, отрисованный страницей Arc Бесконечности из трех способов рисования статьи Arc.

Квадратная кривая Bézier

Квадратная кривая Bézier имеет только одну контрольную точку, и кривая определяется всего тремя точками: начальной точкой, точкой управления и конечной точкой. Уравнения параметрики очень похожи на кубическую кривую Bézier, за исключением того, что самый высокий экспонент равен 2, поэтому кривая является квадратной многономиальной:

x(t) = (1 – t)₀ + 2t(1 – t)x₁ + t ctrl

y(t) = (1 – t)₀ + 2t(1 – t)y₁ + t geometryy

Чтобы добавить квадратную кривую Bézier в путь, используйте QuadTo метод или QuadTo перегрузку с отдельными x координатами y :

public void QuadTo (SKPoint point1, SKPoint point2)

public void QuadTo (Single x1, Single y1, Single x2, Single y2)

Методы добавляют кривую из текущей позиции point2 в point1 качестве контрольной точки.

Вы можете поэкспериментировать с квадратными кривыми Bézier со страницей квадратной кривой, которая очень похожа на страницу Кривой Bezier, за исключением трех точек касания. Ниже приведен PaintSurface обработчик в файле кода QuadraticCurve.xaml.cs:

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

    canvas.Clear();

    // Draw path with quadratic Bezier
    using (SKPath path = new SKPath())
    {
        path.MoveTo(touchPoints[0].Center);
        path.QuadTo(touchPoints[1].Center,
                    touchPoints[2].Center);

        canvas.DrawPath(path, strokePaint);
    }

    // Draw tangent lines
    canvas.DrawLine(touchPoints[0].Center.X,
                    touchPoints[0].Center.Y,
                    touchPoints[1].Center.X,
                    touchPoints[1].Center.Y, dottedStrokePaint);

    canvas.DrawLine(touchPoints[1].Center.X,
                    touchPoints[1].Center.Y,
                    touchPoints[2].Center.X,
                    touchPoints[2].Center.Y, dottedStrokePaint);

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

И здесь он работает:

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

Пунктирные линии касаются кривой в начальной точке и конечной точке и встречаются в контрольной точке.

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

Тем не менее, форма квадратной кривой Bézier не эллиптична, поэтому для приближения эллиптической дуги требуется несколько квадратичных Béziers. Квадратная Bézier вместо сегмента параболы.

Коническая кривая Bézier

Коническая кривая Bézier , также известная как рациональная квадратная кривая Bézier - это относительно недавнее дополнение к семейству кривых Bézier. Как и квадратная кривая Bézier, рациональная квадратная кривая Bézier включает начальную точку, конечную точку и одну контрольную точку. Но рациональная квадратная кривая Bézier также требует значения веса . Это называется рациональной квадратикой, так как формулы параметрики включают коэффициенты.

Уравнения параметрики для X и Y — это коэффициенты, которые используют один и тот же знаменатель. Ниже приведено уравнение для знаменателя от 0 до 1 и весового значения w:

d(t) = (1 – t)2wt(1 – t) + t

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

Уравнения параметрики для координат X и Y похожи на уравнения параметрики для квадратичного Bézier, за исключением того, что средний термин также включает значение веса, и выражение делится знаменателем:

x(t) = ((1 – t)₀ + 2wt(1 – t)x₁ + t ctrlx ÷ d(t)

y(t) = ((1 – t)₀ + 2wt(1 – t)y₁ + t Ctrl)) ÷ d(t)

Рациональная квадратная кривая Bézier также называются конусами , потому что они могут точно представлять сегменты любого конического раздела — гиперболы, параболы, многоточие и круги.

Чтобы добавить рациональную квадратную кривую Bézier в путь, используйте ConicTo метод или ConicTo перегрузку с отдельными x координатами y :

public void ConicTo (SKPoint point1, SKPoint point2, Single weight)

public void ConicTo (Single x1, Single y1, Single x2, Single y2, Single weight)

Обратите внимание на окончательный weight параметр.

Страница "Коническая кривая" позволяет экспериментировать с этими кривыми. Класс ConicCurvePage является производным от InteractivePage. Файл ConicCurvePage.xaml создает Slider экземпляр для выбора значения веса от –2 до 2. Файл кода ConicCurvePage.xaml.cs создает три TouchPoint объекта, а PaintSurface обработчик просто отрисовывает результирующий кривый с тангенсными линиями в контрольные точки:

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

    canvas.Clear();

    // Draw path with conic curve
    using (SKPath path = new SKPath())
    {
        path.MoveTo(touchPoints[0].Center);
        path.ConicTo(touchPoints[1].Center,
                     touchPoints[2].Center,
                     (float)weightSlider.Value);

        canvas.DrawPath(path, strokePaint);
    }

    // Draw tangent lines
    canvas.DrawLine(touchPoints[0].Center.X,
                    touchPoints[0].Center.Y,
                    touchPoints[1].Center.X,
                    touchPoints[1].Center.Y, dottedStrokePaint);

    canvas.DrawLine(touchPoints[1].Center.X,
                    touchPoints[1].Center.Y,
                    touchPoints[2].Center.X,
                    touchPoints[2].Center.Y, dottedStrokePaint);

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

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

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

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

В теории, отрицательные весы допускаются и приводят к тому, что кривая сгибается от контрольной точки. Однако весовые значения –1 или ниже приводят к тому, что знаменатель в уравнениях параметрики становится отрицательным для определенных значений t. Вероятно, по этой причине отрицательные весы игнорируются в методах ConicTo . Программа конической кривой позволяет задать отрицательные весы, но, как можно увидеть, экспериментируя, отрицательные весы имеют тот же эффект, что и вес нуля, и вызвать отрисовку прямой линии.

Очень легко наследовать контрольную точку и вес, чтобы использовать ConicTo метод для рисования круговой дуги до (но не включая) полуцирки. На следующей схеме линии с начальной и конечной точек соответствуют контрольной точке.

Отрисовка конической дуги круговой дуги

Тригонометрию можно использовать для определения расстояния точки управления от центра круга: это радиус круга, разделенный косином на половину угла α. Чтобы нарисовать круговую дугу между начальными и конечными точками, задайте для этого же косинуса половины угла. Обратите внимание, что если угол равен 180 градусов, то тангенсные линии никогда не соответствуют и вес равен нулю. Но для углов менее 180 градусов математика работает хорошо.

На странице Conic Circular Arc показано это. Файл ConicCircularArc.xaml создает Slider экземпляр для выбора угла. Обработчик PaintSurface в файле кода ConicCircularArc.xaml.cs вычисляет контрольную точку и вес:

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

    canvas.Clear();

    // Translate to center
    canvas.Translate(info.Width / 2, info.Height / 2);

    // Draw the circle
    float radius = Math.Min(info.Width, info.Height) / 4;
    canvas.DrawCircle(0, 0, radius, blackStroke);

    // Get the value of the Slider
    float angle = (float)angleSlider.Value;

    // Calculate sin and cosine for half that angle
    float sin = (float)Math.Sin(Math.PI * angle / 180 / 2);
    float cos = (float)Math.Cos(Math.PI * angle / 180 / 2);

    // Find the points and weight
    SKPoint point0 = new SKPoint(-radius * sin, radius * cos);
    SKPoint point1 = new SKPoint(0, radius / cos);
    SKPoint point2 = new SKPoint(radius * sin, radius * cos);
    float weight = cos;

    // Draw the points
    canvas.DrawCircle(point0.X, point0.Y, 10, blackFill);
    canvas.DrawCircle(point1.X, point1.Y, 10, blackFill);
    canvas.DrawCircle(point2.X, point2.Y, 10, blackFill);

    // Draw the tangent lines
    canvas.DrawLine(point0.X, point0.Y, point1.X, point1.Y, dottedStroke);
    canvas.DrawLine(point2.X, point2.Y, point1.X, point1.Y, dottedStroke);

    // Draw the conic
    using (SKPath path = new SKPath())
    {
        path.MoveTo(point0);
        path.ConicTo(point1, point2, weight);
        canvas.DrawPath(path, redStroke);
    }
}

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

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

Но задайте угол в 180 градусов, и математика завершается ошибкой.

К сожалению, в этом случае ConicTo это не поддерживает отрицательные весы, так как в теории (на основе уравнений параметрики), круг может быть завершен с другим вызовом ConicTo с теми же точками, но отрицательным значением веса. Это позволит создать целый круг с двумя ConicTo кривыми на основе любого угла между (но не включая) ноль градусов и 180 градусов.