Tre tipi di curve di Bézier
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:
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
nellaCubicTo
chiamata - secondo punto di controllo:
point2
nellaCubicTo
chiamata - end point:
point3
nellaCubicTo
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:
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 t
controllo (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
, pt2
e pt3
che definiscono una curva di Bézier (mostrata in rosso) che approssima un arco circolare:
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:
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:
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:
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:
È 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:
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:
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.
È 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:
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.