Partager via


Trois façons de dessiner un arc

Découvrez comment utiliser SkiaSharp pour définir des arcs de trois façons différentes

Un arc est une courbe sur la circonférence d’un ellipse, comme les parties arrondies de ce signe infini :

Signe infini

Malgré la simplicité de cette définition, il n’existe aucun moyen de définir une fonction de dessin à arc qui répond à tous les besoins, et par conséquent, aucun consensus entre les systèmes graphiques de la meilleure façon de dessiner un arc. Pour cette raison, la SKPath classe ne se limite pas à une seule approche.

SKPath définit une AddArc méthode, cinq méthodes différentes ArcTo et deux méthodes relatives RArcTo . Ces méthodes appartiennent à trois catégories, représentant trois approches très différentes pour spécifier un arc. Celui que vous utilisez dépend des informations disponibles pour définir l’arc, et de la façon dont cet arc s’adapte aux autres graphiques que vous dessinez.

Arc d’angle

L’approche de l’arc d’angle pour dessiner des arcs nécessite que vous spécifiiez un rectangle qui limite un ellipse. L’arc sur la circonférence de cet ellipse est indiqué par des angles du centre de l’ellipse qui indiquent le début de l’arc et sa longueur. Deux méthodes différentes dessinent des arcs d’angle. Il s’agit de la AddArc méthode et de la ArcTo méthode :

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

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

Ces méthodes sont identiques aux méthodes Android AddArc et [ArcTo]xref :Android.Graphics.Path.ArcTo*). La méthode iOS AddArc est similaire, mais elle est limitée aux arcs sur la circonférence d’un cercle plutôt que généralisée à un ellipse.

Les deux méthodes commencent par une SKRect valeur qui définit à la fois l’emplacement et la taille d’un ellipse :

L’ovale qui commence un arc d’angle

L’arc fait partie de la circonférence de cet ellipse.

L’argument startAngle est un angle au niveau des aiguilles d’une montre par rapport à une ligne horizontale dessinée du centre de l’ellipse à droite. L’argument sweepAngle est relatif au startAngle. Voici les startAngle sweepAngle valeurs de 60 degrés et 100 degrés, respectivement :

Angles qui définissent un arc d’angle

L’arc commence à l’angle de début. Sa longueur est régie par l’angle de balayage. L’arc est illustré ici en rouge :

Arc d’angle mis en surbrillance

La courbe ajoutée au chemin d’accès avec la ou ArcTo la AddArc méthode est simplement cette partie de la circonférence de l’ellipse :

L’arc d’angle lui-même

Les startAngle arguments sweepAngle ou les arguments peuvent être négatifs : l’arc est dans le sens des aiguilles d’une montre pour les valeurs positives et sweepAngle dans le sens inverse des aiguilles d’une montre pour les valeurs négatives.

Toutefois, AddArc ne définit pas de contour fermé. Si vous appelez LineTo après AddArc, une ligne est dessinée de la fin de l’arc au point de la LineTo méthode, et la même chose est vraie .ArcTo

AddArc démarre automatiquement un nouveau contour et est fonctionnellement équivalent à un appel avec ArcTo un argument final de true:

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

Ce dernier argument est appelé forceMoveTo, et il provoque efficacement un MoveTo appel au début de l’arc. Cela commence un nouveau contour. Ce n’est pas le cas avec un dernier argument de false:

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

Cette version de ArcTo dessine une ligne de la position actuelle au début de l’arc. Cela signifie que l’arc peut être quelque part au milieu d’un contour plus grand.

La page Angle Arc vous permet d’utiliser deux curseurs pour spécifier les angles de début et de balayage. Le fichier XAML instancie deux Slider éléments et un SKCanvasView. Le PaintCanvas gestionnaire dans le fichier AngleArcPage.xaml.cs dessine à la fois l’ovale et l’arc à l’aide de deux SKPaint objets définis en tant que champs :

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

Comme vous pouvez le voir, l’angle de début et l’angle de balayage peuvent prendre des valeurs négatives :

Capture d’écran triple de la page Angle Arc

Cette approche de la génération d’un arc est algorithmiquement la plus simple, et il est facile de dériver les équations paramétriques qui décrivent l’arc. Connaître la taille et l’emplacement de l’ellipse, ainsi que les angles de début et de balayage, les points de début et de fin de l’arc peuvent être calculés à l’aide de trigonométrie simple :

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

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

La angle valeur est soit startAngle .startAngle + sweepAngle

L’utilisation de deux angles pour définir un arc est optimale pour les cas où vous connaissez la longueur angulaire de l’arc que vous souhaitez dessiner, par exemple pour créer un graphique en secteurs. La page Graphique en secteurs explosé illustre cela. La ExplodedPieChartPage classe utilise une classe interne pour définir des données et des couleurs fabriquées :

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

Le PaintSurface gestionnaire effectue d’abord une boucle dans les éléments pour calculer un totalValues nombre. À partir de là, elle peut déterminer la taille de chaque élément comme fraction du total et la convertir en angle :

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

Un nouvel SKPath objet est créé pour chaque tranche de secteurs. Le chemin se compose d’une ligne du centre, puis d’une ArcTo ligne pour dessiner l’arc, et une autre ligne vers les résultats du centre à partir de l’appel Close . Ce programme affiche des tranches de secteurs « explosées » en les déplaçant toutes hors du centre de 50 pixels. Cette tâche nécessite un vecteur dans la direction du point intermédiaire de l’angle de balayage pour chaque tranche :

Capture d’écran triple de la page Graphique en secteurs explosé

Pour voir ce qu’il semble sans l’explosion, il suffit de commenter l’appel Translate :

Capture d’écran triple de la page Graphique à secteurs explosés sans explosion

Arc tangente

Le deuxième type d’arc pris en charge par SKPath est l’arc tangent, appelé ainsi parce que l’arc est la circonférence d’un cercle qui est tangent à deux lignes connectées.

Un arc tangent est ajouté à un chemin d’accès avec un appel à la ArcTo méthode avec deux SKPoint paramètres, ou la ArcTo surcharge avec des paramètres distincts Single pour les points :

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

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

Cette ArcTo méthode est similaire à la fonction PostScript arct (page 532) et à la méthode iOS AddArcToPoint .

La ArcTo méthode implique trois points :

  • Point actuel du contour ou point (0, 0) s’il MoveTo n’a pas été appelé
  • Premier argument de point à la ArcTo méthode, appelé point d’angle
  • Deuxième argument de point à ArcTo, appelé point de destination :

Trois points qui commencent un arc tangente

Ces trois points définissent deux lignes connectées :

Lignes reliant les trois points d’un arc tangent

Si les trois points sont colignes, c’est-à-dire s’ils reposent sur la même ligne droite , aucun arc ne sera dessiné.

La ArcTo méthode inclut également un radius paramètre. Cela définit le rayon d’un cercle :

Cercle d’un arc tangente

L’arc tangente n’est pas généralisé pour un ellipse.

Si les deux lignes se rencontrent à n’importe quel angle, ce cercle peut être inséré entre ces lignes afin qu’elle soit tangente aux deux lignes :

Cercle d’arc tangente entre les deux lignes

La courbe ajoutée au contour ne touche pas l’un des points spécifiés dans la ArcTo méthode. Il se compose d’une ligne droite du point actuel au premier point tangente et d’un arc qui se termine au deuxième point tangent, illustré ici en rouge :

Diagramme montrant le diagramme précédent annoté avec une ligne rouge qui montre l’arc tangent en surbrillance entre les deux lignes.

Voici la ligne droite finale et l’arc ajoutés au contour :

Arc tangente mis en surbrillance entre les deux lignes

Le contour peut être continué à partir du deuxième point tangent.

La page Arc tangente vous permet d’expérimenter l’arc tangente. Il s’agit du premier de plusieurs pages qui dérivent de InteractivePage, qui définit quelques objets pratiques SKPaint et effectue le TouchPoint traitement :

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

La classe TangentArcPage est dérivée de InteractivePage. Le constructeur du fichier TangentArcPage.xaml.cs est responsable de l’instanciation et de l’initialisation du touchPoints tableau, et de la définition baseCanvasView (inInteractivePage) de l’objet SKCanvasView instancié dans le fichier 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();
        }
    }
    ...
}

Le PaintSurface gestionnaire utilise la méthode pour dessiner l’arc ArcTo en fonction des points tactiles et d’un Slider, mais calcule également le cercle sur lequel l’angle est basé :

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

Voici la page Tangent Arc en cours d’exécution :

Capture d’écran triple de la page Tangent Arc

L’arc tangente est idéal pour créer des angles arrondis, tels qu’un rectangle arrondi. Étant donné qu’elle SKPath inclut déjà une AddRoundedRect méthode, la page Heptagon arrondie montre comment ArcTo arrondir les angles d’un polygone à sept côtés. (Le code est généralisé pour n’importe quel polygone normal.)

Le PaintSurface gestionnaire de la RoundedHeptagonPage classe contient une for boucle pour calculer les coordonnées des sept sommets du heptagon, et une seconde pour calculer les points intermédiaires des sept côtés de ces sommets. Ces points intermédiaires sont ensuite utilisés pour construire le chemin d’accès :

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

Voici le programme en cours d’exécution :

Capture d’écran triple de la page Heptagon arrondie

Arc elliptique

L’arc elliptique est ajouté à un chemin d’accès avec un appel à la ArcTo méthode qui a deux SKPoint paramètres, ou la ArcTo surcharge avec des coordonnées X et Y distinctes :

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)

L’arc elliptique est cohérent avec l’arc elliptique inclus dans Scalable Vector Graphics (SVG) et la classe plateforme Windows universelleArcSegment.

Ces ArcTo méthodes dessinent un arc entre deux points, qui sont le point actuel du contour et le dernier paramètre de la ArcTo méthode (le paramètre ou les xy paramètres distincts x et y les paramètres) :

Les deux points qui ont défini un arc elliptique

Le premier paramètre de point de la ArcTo méthode (rou et ryrx ) n’est pas un point du tout, mais spécifie plutôt le rayon horizontal et vertical d’un ellipse ;

ellipse qui a défini un arc elliptique

Le xAxisRotate paramètre est le nombre de degrés d’horloge à faire pivoter cette ellipse :

L’ellipse inclinée qui a défini un arc elliptique

Si cette ellipse inclinée est alors positionnée de sorte qu’elle touche les deux points, les points sont connectés par deux arcs différents :

Premier ensemble d’arcs elliptiques

Ces deux arcs peuvent être distingués de deux manières : l’arc supérieur est supérieur à l’arc inférieur, et comme l’arc est dessiné de gauche à droite, l’arc supérieur est dessiné dans une direction dans le sens des aiguilles d’une montre tandis que l’arc inférieur est dessiné dans une direction à sens inverse.

Il est également possible d’ajuster l’ellipse entre les deux points d’une autre manière :

Deuxième ensemble d’arcs elliptiques

Maintenant, il y a un arc plus petit sur le dessus qui est dessiné dans le sens des aiguilles d’une montre, et un arc plus grand sur le bas qui est dessiné dans le sens inverse des aiguilles d’une montre.

Ces deux points peuvent donc être connectés par un arc défini par l’ellipse inclinée de quatre façons :

Quatre arcs elliptiques

Ces quatre arcs sont distingués par les quatre combinaisons des arguments de type d’énumération et SKPathDirection de la SKPathArcSize ArcTo méthode :

  • rouge : SKPathArcSize.Large et SKPathDirection.Clockwise
  • vert : SKPathArcSize.Small et SKPathDirection.Clockwise
  • blue : SKPathArcSize.Small et SKPathDirection.CounterClockwise
  • magenta : SKPathArcSize.Large et SKPathDirection.CounterClockwise

Si l’ellipse inclinée n’est pas assez grande pour s’ajuster entre les deux points, elle est uniformément mise à l’échelle jusqu’à ce qu’elle soit assez grande. Seuls deux arcs uniques connectent les deux points dans ce cas. Ceux-ci peuvent être distingués par le SKPathDirection paramètre.

Bien que cette approche de la définition d’un arc sonne complexe lors de la première rencontre, il s’agit de la seule approche qui permet de définir un arc avec un ellipse pivoté, et c’est souvent l’approche la plus simple lorsque vous devez intégrer des arcs à d’autres parties du contour.

La page Arc Elliptical vous permet de définir de manière interactive les deux points, ainsi que la taille et la rotation de l’ellipse. La EllipticalArcPage classe dérive de InteractivePage, et le PaintSurface gestionnaire dans le fichier code-behind EllipticalArcPage.xaml.cs dessine les quatre arcs :

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

Ici, il est en cours d’exécution :

Capture d’écran triple de la page Arc Elliptical

La page Arc Infinity utilise l’arc elliptique pour dessiner un signe infini. Le signe infini est basé sur deux cercles avec rayon de 100 unités séparées par 100 unités :

Deux cercles

Deux lignes se croisant sont tangentes aux deux cercles :

Deux cercles avec des lignes tangentes

Le signe infini est une combinaison de parties de ces cercles et des deux lignes. Pour utiliser l’arc elliptique pour dessiner le signe infini, les coordonnées où les deux lignes sont tangentes aux cercles doivent être déterminées.

Construisez un rectangle droit dans l’un des cercles :

Deux cercles avec des lignes tangentes et un cercle incorporé

Le rayon du cercle est de 100 unités et l’hypotenuse du triangle est de 150 unités, de sorte que l’angle α est l’arcsine (sinus inverse) de 100 divisé par 150 ou 41,8 degrés. La longueur de l’autre côté du triangle est de 150 fois le cosinus de 41,8 degrés, ou 112, qui peut également être calculé par le théorème pythagorean.

Les coordonnées du point tangent peuvent ensuite être calculées à l’aide de ces informations :

x = 112·cos(41.8) = 83

y = 112·sin(41.8) = 75

Les quatre points tangentes sont tout ce qui est nécessaire pour dessiner un signe infini centré sur le point (0, 0) avec rayon du cercle de 100 :

Deux cercles avec des lignes et des coordonnées tangentes

Le PaintSurface gestionnaire de la ArcInfinityPage classe positionne le signe infini afin que le point (0, 0) soit positionné au centre de la page et met à l’échelle le chemin d’accès à la taille de l’écran :

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

Le code utilise la Bounds propriété de SKPath déterminer les dimensions du sinus infini pour l’adapter à la taille du canevas :

Capture d’écran triple de la page Arc Infinity

Le résultat semble un peu petit, ce qui suggère que la Bounds propriété d’est SKPath signaler une taille supérieure au chemin d’accès.

En interne, Skia se rapproche de l’arc à l’aide de plusieurs courbes quadratiques de Bézier. Ces courbes (comme vous le verrez dans la section suivante) contiennent des points de contrôle qui régissent la façon dont la courbe est dessinée, mais qui ne font pas partie de la courbe rendue. La Bounds propriété inclut ces points de contrôle.

Pour obtenir un ajustement plus serré, utilisez la TightBounds propriété, qui exclut les points de contrôle. Voici le programme s’exécutant en mode paysage et en utilisant la TightBounds propriété pour obtenir les limites de chemin d’accès :

Capture d’écran triple de la page Arc Infinity avec des limites étroites

Bien que les connexions entre les arcs et les lignes droites soient mathématiquement lisses, le passage de l’arc à la ligne droite peut sembler un peu abrupt. Un meilleur signe infini est présenté dans l’article suivant sur trois types de courbes de Bézier.