Conceptos básicos del trazado en SkiaSharp

Download SampleDescargar el ejemplo

Explorar el objeto SKPath de SkiaSharp para combinar líneas y curvas conectadas

Una de las características más importantes del trazado de gráficos es la capacidad de definir cuándo se deben conectar varias líneas y cuándo no. La diferencia puede ser significativa, como demuestran las partes superiores de estos dos triángulos:

Two triangles showing the difference between connected and disconnected lines

El objeto SKPath encapsula un trazado de gráficos. Un trazado es una colección de uno o varios contornos. Cada contorno es una colección de líneas rectas y curvas conectadas. Los contornos no están conectados entre sí, pero pueden superponerse visualmente. A veces, un solo contorno se puede superponer.

Normalmente, un contorno comienza con una llamada al método siguiente de SKPath:

  • MoveTo para comenzar un nuevo contorno

El argumento de ese método es un único punto, que se puede expresar como un valor SKPoint o como coordenadas X e Y independientes. La llamada MoveTo establece un punto al principio del contorno y un punto actual inicial. Puede llamar a los siguientes métodos para continuar el contorno con una línea o curva desde el punto actual hasta un punto especificado en el método, que se convierte entonces en el nuevo punto actual:

  • LineTo para agregar una línea recta al trazado
  • ArcTo para agregar un arco, que es una línea en la circunferencia de un círculo o elipse
  • CubicTo para agregar un B-spline cúbico
  • QuadTo para agregar un B-spline cuadrático
  • ConicTo para agregar un B-spline cuadrático racional, que puede representar con precisión secciones cónicas (elipses, parábolas e hipérbolas).

Ninguno de estos cinco métodos contiene toda la información necesaria para describir la línea o curva. Cada uno de estos cinco métodos funciona junto con el punto actual establecido por la llamada al método inmediatamente anterior. Por ejemplo, el método LineTo agrega una línea recta al contorno basándose en el punto actual, por lo que el parámetro a LineTo es solo un punto.

La clase SKPath también define métodos que tienen los mismos nombres que estos seis métodos pero con un R al principio:

El R significa relativo. Estos métodos tienen la misma sintaxis que los métodos correspondientes sin el R pero son relativos al punto actual. Son útiles para dibujar partes similares de un trazado en un método al que se llama varias veces.

Un contorno termina con otra llamada a MoveTo o RMoveTo, que inicia un nuevo contorno, o una llamada a Close, que cierra el contorno. El método Close agrega automáticamente una línea recta desde el punto actual hasta el primer punto del contorno, y marca el trazado como cerrado, lo que significa que se representará sin ningún límite de trazo.

La diferencia entre contornos abiertos y cerrados se ilustra en la página Contornos de dos triángulos, que utiliza un objeto SKPath con dos contornos para representar dos triángulos. El primer contorno está abierto y el segundo está cerrado. Esta es la clase TwoTriangleContoursPage:

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

    canvas.Clear();

    // Create the path
    SKPath path = new SKPath();

    // Define the first contour
    path.MoveTo(0.5f * info.Width, 0.1f * info.Height);
    path.LineTo(0.2f * info.Width, 0.4f * info.Height);
    path.LineTo(0.8f * info.Width, 0.4f * info.Height);
    path.LineTo(0.5f * info.Width, 0.1f * info.Height);

    // Define the second contour
    path.MoveTo(0.5f * info.Width, 0.6f * info.Height);
    path.LineTo(0.2f * info.Width, 0.9f * info.Height);
    path.LineTo(0.8f * info.Width, 0.9f * info.Height);
    path.Close();

    // Create two SKPaint objects
    SKPaint strokePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Magenta,
        StrokeWidth = 50
    };

    SKPaint fillPaint = new SKPaint
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Cyan
    };

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

El primer contorno consiste en una llamada a MoveTo utilizando coordenadas X e Y en lugar de un valor SKPoint, seguido de tres llamadas a LineTo para dibujar los tres lados del triángulo. El segundo contorno solo tiene dos llamadas a LineTo pero termina el contorno con una llamada a Close, que cierra el contorno. Esta diferencia es importante:

Triple screenshot of the Two Triangle Contours page

Como puede ver, el primer contorno es obviamente una serie de tres líneas conectadas, pero el final no se conecta con el principio. Las dos líneas se superponen en la parte superior. El segundo contorno está obviamente cerrado, y se ha conseguido con una llamada LineTo menos porque el método Close agrega automáticamente una línea final para cerrar el contorno.

SKCanvas define solo un método DrawPath, al que en esta demostración se llama dos veces para rellenar y trazar la línea. Todos los contornos se rellenan, incluso aquellos que no están cerrados. Para rellenar los trazados sin rellenar, se supone que existe una línea recta entre los puntos inicial y final de los contornos. Si quita el último LineTo del primer contorno, o elimina la llamada Close del segundo contorno, cada contorno tendrá solo dos lados pero se rellenará como si fuera un triángulo.

SKPath define muchos otros métodos y propiedades. Los métodos siguientes agregan contornos completos a la ruta de acceso, que puede cerrarse o no en función del método :

Tenga en cuenta que un objeto SKPath define solo una geometría: una serie de puntos y conexiones. Solo cuando un objeto SKPath se combina con un objeto SKPaint, el trazado se representa con un color, una anchura de trazo, etc. determinados. Además, hay que tener en cuenta que el objeto SKPaint que se pasa al método DrawPath define las características de todo el trazado. Si desea dibujar algo que requiera varios colores, debe usar un trazado independiente para cada color.

Del mismo modo que el aspecto del inicio y el final de una línea se define mediante un límite de trazo, el aspecto de la conexión entre dos líneas se define mediante una unión de trazo. Esto se especifica estableciendo la propiedad StrokeJoin de SKPaint en un miembro de la enumeración SKStrokeJoin:

  • Miter para una unión de punto
  • Round para una unión redondeada
  • Bevel para una unión cortada

En la página Uniones de trazo se muestran estas tres uniones de trazo con código similar a la página Límites de trazos. Este es el controlador de eventos PaintSurface en la clase StrokeJoinsPage:

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

    canvas.Clear();

    SKPaint textPaint = new SKPaint
    {
        Color = SKColors.Black,
        TextSize = 75,
        TextAlign = SKTextAlign.Right
    };

    SKPaint thickLinePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Orange,
        StrokeWidth = 50
    };

    SKPaint thinLinePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        Color = SKColors.Black,
        StrokeWidth = 2
    };

    float xText = info.Width - 100;
    float xLine1 = 100;
    float xLine2 = info.Width - xLine1;
    float y = 2 * textPaint.FontSpacing;
    string[] strStrokeJoins = { "Miter", "Round", "Bevel" };

    foreach (string strStrokeJoin in strStrokeJoins)
    {
        // Display text
        canvas.DrawText(strStrokeJoin, xText, y, textPaint);

        // Get stroke-join value
        SKStrokeJoin strokeJoin;
        Enum.TryParse(strStrokeJoin, out strokeJoin);

        // Create path
        SKPath path = new SKPath();
        path.MoveTo(xLine1, y - 80);
        path.LineTo(xLine1, y + 80);
        path.LineTo(xLine2, y + 80);

        // Display thick line
        thickLinePaint.StrokeJoin = strokeJoin;
        canvas.DrawPath(path, thickLinePaint);

        // Display thin line
        canvas.DrawPath(path, thinLinePaint);
        y += 3 * textPaint.FontSpacing;
    }
}

Esta es la ejecución del programa:

Triple screenshot of the Stroke Joins page

La unión angular consta de un punto afilado donde se conectan las líneas. Cuando dos líneas se unen en un ángulo pequeño, la unión angular puede llegar a ser bastante larga. Para evitar uniones angulares excesivamente largas, su longitud está limitada por el valor de la propiedad StrokeMiter de SKPaint. Una unión angular que supera esta longitud se corta para convertirse en una unión biselada.