Tre tipi di curve di Bézier

Download Sample Scaricare l'esempio

Informazioni su come usare SkiaSharp per eseguire il rendering delle curve cubiche, quadratiche e conic Bézier

La curva di Bézier prende il nome da Pierre Bézier (1910 – 1999), un ingegnere francese presso l'azienda automobilistica Renault, che ha utilizzato la curva per la progettazione computerizzata di corpi auto.

Le curve di Bézier sono note per essere ben adatte al design interattivo: si comportano bene , in altre parole, non ci sono singolarità che causano la curva di diventare infinita o difficile , e sono generalmente esteticamente piacevoli:

A sample Bezier curve

I contorni dei caratteri basati su computer sono in genere definiti con le curve di Bézier.

L'articolo di Wikipedia sulla curva di Bézier contiene alcune utili informazioni di sfondo. Il termine curva bézier si riferisce effettivamente a una famiglia di curve simili. SkiaSharp supporta tre tipi di curve di Bézier, denominate cubiche, quadratiche e coniche. Il conico è noto anche come quadratico razionale.

Curva di Bézier cubica

Il cubico è il tipo di curva di Bézier che la maggior parte degli sviluppatori pensa quando il soggetto delle curve di Bézier arriva.

È possibile aggiungere una curva di Bézier cubica a un SKPath oggetto usando il CubicTo metodo con tre SKPoint parametri oppure l'overload CubicTo con parametri e y separatix:

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

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

La curva inizia al punto corrente del contorno. La curva di Bézier cubica completa è definita da quattro punti:

  • punto iniziale: punto corrente nel contorno o (0, 0) se MoveTo non è stato chiamato
  • primo punto di controllo: point1 nella CubicTo chiamata
  • secondo punto di controllo: point2 nella CubicTo chiamata
  • end point: point3 nella CubicTo chiamata

La curva risultante inizia al punto iniziale e termina al punto finale. La curva in genere non passa attraverso i due punti di controllo; I punti di controllo funzionano invece in modo molto simile ai magneti per tirare la curva verso di essi.

Il modo migliore per provare la curva cubica di Bézier è la sperimentazione. Questo è lo scopo della pagina Curva di Bézier, che deriva da InteractivePage. Il file BezierCurvePage.xaml crea un'istanza di SKCanvasView e .TouchEffect Il file code-behind BezierCurvePage.xaml.cs crea quattro TouchPoint oggetti nel relativo costruttore. Il PaintSurface gestore eventi crea un oggetto SKPath per eseguire il rendering di una curva bézier in base ai quattro TouchPoint oggetti e disegna anche linee tangenti tratteggiate dai punti di controllo ai punti finali:

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

Di seguito è in esecuzione:

Triple screenshot of the Bezier Curve page

Matematicamente, la curva è un polinomio cubico. La curva interseca al massimo una linea retta a tre punti. Al punto iniziale, la curva è sempre tangente a e nella stessa direzione di una linea retta dal punto iniziale al primo punto di controllo. Al punto finale, la curva è sempre tangente a e nella stessa direzione di una linea retta dal secondo punto di controllo al punto finale.

La curva cubica di Bézier è sempre delimitata da un quadrilatero convessa che collega i quattro punti. Si tratta di uno scafo convesso. Se i punti di controllo si trovano sulla linea retta tra l'inizio e l'estremità finale, la curva di Bézier esegue il rendering come linea retta. Ma la curva può anche attraversare se stessa, come illustrato nella terza schermata.

Un contorno di percorso può contenere più curve di Bézier cubiche collegate, ma la connessione tra due curve di Bézier cubiche sarà liscia solo se i tre punti seguenti sono colinear (ovvero giacono su una linea retta):

  • secondo punto di controllo della prima curva
  • punto finale della prima curva, che è anche il punto iniziale della seconda curva
  • primo punto di controllo della seconda curva

Nell'articolo successivo su SVG Path Data si scoprirà una funzionalità per facilitare la definizione delle curve bézier connesse uniformi.

A volte è utile conoscere le equazioni parametriche sottostanti che eseguono il rendering di una curva cubica di Bézier. Per t compreso tra 0 e 1, le equazioni parametriche sono le seguenti:

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

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

L'esponente più alto di 3 conferma che si tratta di polinomiali cubici. È facile verificare che quando t è uguale a 0, il punto è (x₀, y₀), che è il punto iniziale e quando t è uguale a 1, il punto è (x₃, y₃), che è il punto finale. Vicino al punto iniziale (per i valori bassi di ), il primo punto di tcontrollo (x₁, y₁) ha un effetto forte e vicino al punto finale (valori alti di 't') il secondo punto di controllo (x",", y"," ) ha un effetto forte.

Approssimazione curva di Bézier ad archi circolari

A volte è utile usare una curva bézier per eseguire il rendering di un arco circolare. Una curva cubica di Bézier può approssimare un arco circolare molto bene fino a un quarto cerchio, quindi quattro curve di Bézier collegate possono definire un intero cerchio. Questa approssimazione è descritta in due articoli pubblicati più di 25 anni fa:

Tor Dokken, et al, "Good Approssimazione dei cerchi di Curvee-Continuous Bézier curve", Computer Aided Geometric Design 7 (1990), 33-41.

Michael Goldapp, "Approssimazione degli archi circolari di polinomiali cubici", Computer Aided Geometric Design 8 (1991), 227-238.

Il diagramma seguente mostra quattro punti etichettati pto, pt1, pt2e pt3 che definiscono una curva di Bézier (mostrata in rosso) che approssima un arco circolare:

Approximation of a circular arc with a Bézier curve

Le linee dall'inizio e dall'estremità finale ai punti di controllo sono tangenti al cerchio e alla curva di Bézier e hanno una lunghezza di L. Il primo articolo citato in precedenza indica che la curva di Bézier approssima meglio un arco circolare quando tale lunghezza L viene calcolata come segue:

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

La figura mostra un angolo di 45 gradi, quindi L è uguale a 0,265. Nel codice, tale valore verrebbe moltiplicato per il raggio desiderato del cerchio.

La pagina Di arco circolare di Bézier consente di sperimentare la definizione di una curva di Bézier per approssimare un arco circolare per gli angoli che vanno fino a 180 gradi. Il file BezierCircularArcPage.xaml crea un'istanza di SKCanvasView e un Slider oggetto per la selezione dell'angolo. Il PaintSurface gestore eventi nel file code-behind BezierCircularArgPage.xaml.cs usa una trasformazione per impostare il punto (0, 0) sul centro dell'area di disegno. Disegna un cerchio centrato su quel punto per il confronto e quindi calcola i due punti di controllo per la curva di 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);
}

I punti iniziale e finale (point0 e point3) vengono calcolati in base alle normali equazioni parametriche per il cerchio. Poiché il cerchio è centrato su (0, 0), questi punti possono anche essere considerati vettori radiali dal centro del cerchio alla circonferenza. I punti di controllo si trovano su linee che sono tangenti al cerchio, quindi si trovano ad angoli giusti per questi vettori radiali. Un vettore ad angolo retto a un altro è semplicemente il vettore originale con le coordinate X e Y scambiate e una di esse ha reso negativo.

Ecco il programma in esecuzione con angolazioni diverse:

Triple screenshot of the Bezier Circular Arc page

Esaminare attentamente il terzo screenshot e si noterà che la curva di Bézier devia in particolare da un semicircolo quando l'angolo è di 180 gradi, ma lo schermo iOS mostra che sembra adattarsi a un cerchio quarto appena fine quando l'angolo è di 90 gradi.

Il calcolo delle coordinate dei due punti di controllo è piuttosto semplice quando il cerchio del quarto è orientato come segue:

Approximation of a quarter circle with a Bézier curve

Se il raggio del cerchio è 100, L è 55 ed è un numero facile da ricordare.

La pagina Squaring the Circle anima una figura tra un cerchio e un quadrato. Il cerchio è approssimativo da quattro curve di Bézier le cui coordinate sono visualizzate nella prima colonna di questa definizione di matrice nella SquaringTheCirclePage classe :

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() }
    };
    ...
}

La seconda colonna contiene le coordinate di quattro curve di Bézier che definiscono un quadrato la cui area è approssimativamente uguale all'area del cerchio. Il disegno di un quadrato con l'area esatta come un determinato cerchio è il classico problema geometrico insolvibile di quadratura del cerchio. Per il rendering di un quadrato con curve di Bézier, i due punti di controllo per ogni curva sono gli stessi e sono colinear con i punti iniziale e finale, quindi viene eseguito il rendering della curva di Bézier come linea retta.

La terza colonna della matrice è per i valori interpolati per un'animazione. La pagina imposta un timer per 16 millisecondi e il PaintSurface gestore viene chiamato a tale velocità:

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

I punti vengono interpolati in base a un valore oscillante in modo sinusoidale di t. I punti interpolati vengono quindi usati per costruire una serie di quattro curve di Bézier collegate. Ecco l'animazione in esecuzione:

Triple screenshot of the Squaring the Circle page

Un'animazione di questo tipo sarebbe impossibile senza curve sufficientemente flessibili da essere sottoposte a rendering sia come archi circolari che linee rette.

La pagina Bezier Infinity sfrutta anche la capacità di una curva di Bézier di approssimare un arco circolare. Ecco il PaintSurface gestore della BezierInfinityPage classe :

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

Potrebbe essere un buon esercizio tracciare queste coordinate sul foglio grafico per vedere come sono correlate. Il segno infinito è centrato intorno al punto (0, 0) e i due cicli hanno centri di (–150, 0) e (150, 0) e raggi di 100. Nella serie di CubicTo comandi è possibile visualizzare le coordinate X dei punti di controllo che assumono valori di –95 e –205 (tali valori sono –150 più e meno 55), 205 e 95 (150 più e meno 55), oltre a 250 e -250 per i lati destro e sinistro. L'unica eccezione è quando il segno infinito si incrocia al centro. In tal caso, i punti di controllo hanno coordinate con una combinazione di 50 e -50 per raddrizzare la curva vicino al centro.

Ecco il segno infinito:

Triple screenshot of the Bézier Infinity page

È leggermente più uniforme verso il centro rispetto al segno infinito visualizzato dalla pagina Arc Infinity dall'articolo Tre modi per disegnare un arco.

Curva quadratica di Bézier

La curva quadratica di Bézier ha un solo punto di controllo e la curva è definita da soli tre punti: il punto iniziale, il punto di controllo e il punto finale. Le equazioni parametriche sono molto simili alla curva cubica di Bézier, ad eccezione del fatto che l'esponente più alto è 2, quindi la curva è un polinomio quadratico:

x(t) = (1 – t)²x₀ + 2t(1 – t)x₁ + t²x°

y(t) = (1 – t)²y₀ + 2t(1 – t)y₁ + t²y Scalabilità

Per aggiungere una curva quadratica di Bézier a un percorso, usare il QuadTo metodo o l'overload QuadTo con coordinate separate x e y :

public void QuadTo (SKPoint point1, SKPoint point2)

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

I metodi aggiungono una curva dalla posizione corrente a point2 con point1 come punto di controllo.

È possibile sperimentare le curve quadratiche di Bézier con la pagina Curva quadratica, molto simile alla pagina Curva di Bézier, ad eccezione dei soli tre punti di tocco. Ecco il PaintSurface gestore nel file code-behind 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);
    }
}

E qui è in esecuzione:

Triple screenshot of the Quadratic Curve page

Le linee tratteggiate sono tangenti alla curva in corrispondenza del punto iniziale e dell'estremità finale e si incontrano nel punto di controllo.

Il quadratico Bézier è buono se è necessaria una curva di una forma generale, ma si preferisce la praticità di un solo punto di controllo anziché due. Il bézier quadratico esegue il rendering più efficiente di qualsiasi altra curva, motivo per cui viene usato internamente in Skia per eseguire il rendering degli archi ellittici.

Tuttavia, la forma di una curva quadratica bézier non è ellittica, motivo per cui sono necessari più Béziers quadratici per approssimare un arco ellittico. Il bézier quadratico è invece un segmento di parabola.

Curva di Bézier conico

La curva conica di Bézier, nota anche come curva quadratica razionale di Bézier, è un'aggiunta relativamente recente alla famiglia di curve di Bézier. Come la curva quadratica di Bézier, la curva quadratica razionale di Bézier implica un punto iniziale, un punto finale e un punto di controllo. Ma anche la curva quadratica razionale di Bézier richiede un valore di peso . Si chiama quadratico razionale perché le formule parametriche implicano rapporti.

Le equazioni parametriche per X e Y sono rapporti che condividono lo stesso denominatore. Ecco l'equazione per il denominatore per t compreso tra 0 e 1 e il valore di peso w:

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

In teoria, un quadratico razionale può includere tre valori di peso separati, uno per ognuno dei tre termini, ma questi possono essere semplificati in un solo valore di peso nel medio termine.

Le equazioni parametriche per le coordinate X e Y sono simili alle equazioni parametriche per bézier quadratico, ad eccezione del fatto che il termine intermedio include anche il valore di peso e l'espressione è divisa per il denominatore:

x(t) = ((1 – t)²x₀ + 2wt(1 – t)x₁ + t²x interoperabilità)) ÷ d(t)

y(t) = ((1 – t)²y₀ + 2wt(1 – t)y₁ + t²y scalabilità)) ÷ d(t)

Le curve quadratiche razionali di Bézier sono dette anche coniche perché possono rappresentare esattamente segmenti di qualsiasi sezione conica, iperbola, parabola, ellissi e cerchi.

Per aggiungere una curva quadratica razionale di Bézier a un percorso, usare il ConicTo metodo o l'overload ConicTo con coordinate separate x e y :

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

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

Si noti il parametro finale weight .

La pagina Curva conica consente di sperimentare queste curve. La classe ConicCurvePage deriva da InteractivePage. Il file ConicCurvePage.xaml crea un'istanza di per Slider selezionare un valore di peso compreso tra -2 e 2. Il file code-behind ConicCurvePage.xaml.cs crea tre TouchPoint oggetti e il PaintSurface gestore esegue semplicemente il rendering della curva risultante con le linee tangenti nei punti di controllo:

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

Di seguito è in esecuzione:

Triple screenshot of the Conic Curve page

Come si può notare, il punto di controllo sembra tirare la curva verso di esso più quando il peso è superiore. Quando il peso è zero, la curva diventa una linea retta dal punto iniziale al punto finale.

In teoria, i pesi negativi sono consentiti e fanno in modo che la curva si allontana dal punto di controllo. Tuttavia, i pesi di –1 o inferiore causano che il denominatore nelle equazioni parametriche diventi negativo per determinati valori di t. Probabilmente per questo motivo, i pesi negativi vengono ignorati nei ConicTo metodi. Il programma Conic Curve consente di impostare pesi negativi, ma come si può vedere sperimentando, i pesi negativi hanno lo stesso effetto di un peso pari a zero e fanno sì che venga eseguito il rendering di una linea retta.

È molto facile derivare il punto di controllo e il peso per utilizzare il ConicTo metodo per disegnare un arco circolare fino a (ma non incluso) un semicircolo. Nel diagramma seguente, le linee tangenti dall'inizio e dall'estremità finale si incontrano nel punto di controllo.

A conic arc rendering of a circular arc

È possibile usare i trigonometria per determinare la distanza del punto di controllo dal centro del cerchio: è il raggio del cerchio diviso per il coseno della metà dell'angolo α. Per disegnare un arco circolare tra i punti iniziale e finale, impostare il peso sullo stesso coseno della metà dell'angolo. Si noti che se l'angolo è di 180 gradi, le linee tangenti non si incontrano mai e il peso è zero. Ma per gli angoli inferiori a 180 gradi, la matematica funziona bene.

La pagina Arco circolare conico illustra questa operazione. Il file ConicCircularArc.xaml crea un'istanza di per Slider la selezione dell'angolo. Il PaintSurface gestore nel file code-behind ConicCircularArc.xaml.cs calcola il punto di controllo e il peso:

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

Come si può notare, non esiste alcuna differenza visiva tra il ConicTo percorso visualizzato in rosso e il cerchio sottostante visualizzato per riferimento:

Triple screenshot of the Conic Circular Arc page

Ma impostare l'angolo su 180 gradi, e la matematica fallisce.

In questo caso è sfortunato che ConicTo non supporta pesi negativi, perché in teoria (in base alle equazioni parametriche), il cerchio può essere completato con un'altra chiamata a ConicTo con gli stessi punti ma un valore negativo del peso. Ciò consentirebbe di creare un cerchio intero con solo due ConicTo curve basate su qualsiasi angolo tra (ma non inclusi) gradi zero e 180 gradi.