경로 정보 및 열거형Path Information and Enumeration

샘플 다운로드 샘플 다운로드Download Sample Download the sample

경로 대 한 정보를 가져오고 내용을 열거 합니다.Get information about paths and enumerate the contents

합니다 SKPath 여러 속성 및 메서드는 경로 대 한 정보를 얻을 수 있도록 하는 클래스를 정의 합니다.The SKPath class defines several properties and methods that allow you to obtain information about the path. 합니다 Bounds 하 고 TightBounds 속성 (및 관련된 메서드) 경로의 metrical 크기를 가져옵니다.The Bounds and TightBounds properties (and related methods) obtain the metrical dimensions of a path. 합니다 Contains 메서드를 사용 하면 경로 내에서 특정 지점 인지 확인 합니다.The Contains method lets you determine if a particular point is within a path.

경우에 따라 모든 줄 및 경로 구성 하는 곡선의 총 길이 확인 하는 것이 유용 합니다.It is sometimes useful to determine the total length of all the lines and curves that make up a path. 이 길이 계산 하지 않으므로 간단한 알고리즘 방식으로 작업을 전체 클래스 라는 PathMeasure 를 전용으로 지정 됩니다.Calculating this length is not an algorithmically simple task, so an entire class named PathMeasure is devoted to it.

또한 유용한 경우가 모든 그리기 작업 및 경로 구성 하는 요소를 가져오려고 합니다.It is also sometimes useful to obtain all the drawing operations and points that make up a path. 처음에는이 기능이 불필요 한 것 처럼 보일 수 있습니다. 프로그램에서 경로를 만든 경우 프로그램은 콘텐츠를 이미 알고 있습니다.At first, this facility might seem unnecessary: If your program has created the path, the program already knows the contents. 그러나 지금까지 살펴본 하 여 경로 만들 수도 있습니다 경로 효과 변환 하 경로에 텍스트 문자열합니다.However, you've seen that paths can also be created by path effects and by converting text strings into paths. 모든 그리기 작업 및 이러한 경로 구성 하는 지점을 얻을 수 있습니다.You can also obtain all the drawing operations and points that make up these paths. 한 가지 방법은 다음과 같습니다. 반구 주위의 텍스트 줄 바꿈, 예를 들어 모든 지점에 알고리즘 변환 적용One possibility is to apply an algorithmic transform to all the points, for example, to wrap text around a hemisphere:

경로 길이 가져오기Getting the Path Length

문서의 경로 및 텍스트 사용 하는 방법을 살펴보았습니다 합니다 DrawTextOnPath 해당 기준을 따릅니다 경로의 과정 텍스트 문자열을 그리는 방법입니다.In the article Paths and Text you saw how to use the DrawTextOnPath method to draw a text string whose baseline follows the course of a path. 그러나 경로 정확 하 게 맞도록 텍스트의 크기를 원하는 경우?But what if you want to size the text so that it fits the path precisely? 텍스트를 원 주위 그릴 원의 원주를 계산 하는 간단한 이므로 쉽습니다.Drawing text around a circle is easy because the circumference of a circle is simple to calculate. 하지만 타원의 일부 또는 베 지 어 곡선의 길이 그리 간단 하지 않습니다.But the circumference of an ellipse or the length of a Bézier curve is not so simple.

합니다 SKPathMeasure 클래스는 데 도움이 됩니다.The SKPathMeasure class can help. 합니다 생성자 허용는 SKPath 인수를 및 Length 속성 길이 표시 합니다.The constructor accepts an SKPath argument, and the Length property reveals its length.

이 클래스에 설명 되어는 경로 길이 샘플을 기반으로 하는 합니다 베 지 어 곡선 페이지입니다.This class is demonstrated in the Path Length sample, which is based on the Bezier Curve page. 합니다 PathLengthPage.xaml 에서 파생 되는 파일 InteractivePage 터치 인터페이스를 포함 합니다.The PathLengthPage.xaml file derives from InteractivePage and includes a touch interface:

<local:InteractivePage xmlns="http://xamarin.com/schemas/2014/forms"
                       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                       xmlns:local="clr-namespace:SkiaSharpFormsDemos"
                       xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
                       xmlns:tt="clr-namespace:TouchTracking"
                       x:Class="SkiaSharpFormsDemos.Curves.PathLengthPage"
                       Title="Path Length">
    <Grid BackgroundColor="White">
        <skia:SKCanvasView x:Name="canvasView"
                           PaintSurface="OnCanvasViewPaintSurface" />
        <Grid.Effects>
            <tt:TouchEffect Capture="True"
                            TouchAction="OnTouchEffectAction" />
        </Grid.Effects>
    </Grid>
</local:InteractivePage>

합니다 PathLengthPage.xaml.cs 코드 숨김 파일을 사용 하면 끝점을 정의 하 고 입방 형 3 차원 큐빅 곡선의 제어점을 4 개의 터치 포인트를 이동할 수 있습니다.The PathLengthPage.xaml.cs code-behind file allows you to move four touch points to define the end points and control points of a cubic Bézier curve. 세 개의 필드가 정의 텍스트 문자열을 SKPaint 개체 및 텍스트의 너비를 계산된 합니다.Three fields define a text string, an SKPaint object, and a calculated width of the text:

public partial class PathLengthPage : InteractivePage
{
    const string text = "Compute length of path";

    static SKPaint textPaint = new SKPaint
    {
        Style = SKPaintStyle.Fill,
        Color = SKColors.Black,
        TextSize = 10,
    };

    static readonly float baseTextWidth = textPaint.MeasureText(text);
    ...
}

합니다 baseTextWidth 필드를 기준으로 텍스트의 너비를 TextSize 10의 설정 합니다.The baseTextWidth field is the width of the text based on a TextSize setting of 10.

PaintSurface 처리기 베 지 어 곡선을 그리고 다음 전체 길이 따라 맞게 텍스트 크기:The PaintSurface handler draws the Bézier curve and then sizes the text to fit along its full length:

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

        // Get path length
        SKPathMeasure pathMeasure = new SKPathMeasure(path, false, 1);

        // Find new text size
        textPaint.TextSize = pathMeasure.Length / baseTextWidth * 10;

        // Draw text on path
        canvas.DrawTextOnPath(text, path, 0, 0, textPaint);
    }
    ...
}

합니다 Length 속성을 새로 만든 SKPathMeasure 경로의 길이 가져옵니다.The Length property of the newly created SKPathMeasure object obtains the length of the path. 경로 길이 나눈는 baseTextWidth (인 10의 텍스트 크기에 따라 텍스트의 너비) 값 및 10의 기본 텍스트 크기를 곱한 후 합니다.The path length is divided by the baseTextWidth value (which is the width of the text based on a text size of 10) and then multiplied by the base text size of 10. 결과 해당 경로 상에 텍스트를 표시 하기 위한 새 텍스트 크기:The result is a new text size for displaying the text along that path:

베 지 어 곡선을 더 길거나 더 짧은 가져옵니다를 변경 하는 텍스트 크기를 볼 수 있습니다.As the Bézier curve gets longer or shorter, you can see the text size change.

경로 트래버스합니다.Traversing the Path

SKPathMeasure 이상의 측정값 경로의 길이 수행할 수 있습니다.SKPathMeasure can do more than just measure the length of the path. 0과 경로 길이 사이의 모든 값에 대 한는 SKPathMeasure 개체는 해당 지점에서 경로 및 경로 곡선 탄젠트의 위치를 가져올 수 있습니다.For any value between zero and the path length, an SKPathMeasure object can obtain the position on the path, and the tangent to the path curve at that point. 탄젠트 벡터의 형태로 제공 됩니다는 SKPoint 개체 또는 회전으로 캡슐화를 SKMatrix 개체입니다.The tangent is available as a vector in the form of an SKPoint object, or as a rotation encapsulated in an SKMatrix object. 여기의 가지가 SKPathMeasure 다양 하 고 유연한 방식으로이 정보를 가져오고:Here are the methods of SKPathMeasure that obtain this information in varied and flexible ways:

Boolean GetPosition (Single distance, out SKPoint position)

Boolean GetTangent (Single distance, out SKPoint tangent)

Boolean GetPositionAndTangent (Single distance, out SKPoint position, out SKPoint tangent)

Boolean GetMatrix (Single distance, out SKMatrix matrix, SKPathMeasureMatrixFlags flag)

멤버는 SKPathMeasureMatrixFlags 열거:The members of the SKPathMeasureMatrixFlags enumeration are:

  • GetPosition
  • GetTangent
  • GetPositionAndTangent

합니다 자전거 절반 파이프 페이지 사람 모양 아이콘이 입방 형 3 차원 큐빅 곡선을 따라 앞뒤로를 보이는 자전거에 애니메이션 효과 줍니다.The Unicycle Half-Pipe page animates a stick figure on a unicycle that seems to ride back and forth along a cubic Bézier curve:

절반 파이프와 unicycle을 스트로크 하는 데 사용 되는 UnicycleHalfPipePage 개체는클래스의필드로정의됩니다.SKPaintThe SKPaint object used for stroking both the half-pipe and the unicycle is defined as a field in the UnicycleHalfPipePage class. 또한 정의 SKPath 는 자전거에 대 한 개체:Also defined is the SKPath object for the unicycle:

public class UnicycleHalfPipePage : ContentPage
{
    ...
    SKPaint strokePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        StrokeWidth = 3,
        Color = SKColors.Black
    };

    SKPath unicyclePath = SKPath.ParseSvgPathData(
        "M 0 0" +
        "A 25 25 0 0 0 0 -50" +
        "A 25 25 0 0 0 0 0 Z" +
        "M 0 -25 L 0 -100" +
        "A 15 15 0 0 0 0 -130" +
        "A 15 15 0 0 0 0 -100 Z" +
        "M -25 -85 L 25 -85");
    ...
}

클래스의 표준 재정의 포함 합니다 OnAppearingOnDisappearing 애니메이션에 대 한 메서드.The class contains the standard overrides of the OnAppearing and OnDisappearing methods for animation. PaintSurface 처리기 절반 파이프에 대 한 경로 만들고 다음을 그립니다.The PaintSurface handler creates the path for the half-pipe and then draws it. SKPathMeasure 개체는 다음이 경로에 따라 만들어집니다.An SKPathMeasure object is then created based on this path:

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

        canvas.Clear();

        using (SKPath pipePath = new SKPath())
        {
            pipePath.MoveTo(50, 50);
            pipePath.CubicTo(0, 1.25f * info.Height,
                             info.Width - 0, 1.25f * info.Height,
                             info.Width - 50, 50);

            canvas.DrawPath(pipePath, strokePaint);

            using (SKPathMeasure pathMeasure = new SKPathMeasure(pipePath))
            {
                float length = pathMeasure.Length;

                // Animate t from 0 to 1 every three seconds
                TimeSpan timeSpan = new TimeSpan(DateTime.Now.Ticks);
                float t = (float)(timeSpan.TotalSeconds % 5 / 5);

                // t from 0 to 1 to 0 but slower at beginning and end
                t = (float)((1 - Math.Cos(t * 2 * Math.PI)) / 2);

                SKMatrix matrix;
                pathMeasure.GetMatrix(t * length, out matrix,
                                      SKPathMeasureMatrixFlags.GetPositionAndTangent);

                canvas.SetMatrix(matrix);
                canvas.DrawPath(unicyclePath, strokePaint);
            }
        }
    }
}

합니다 PaintSurface 의 값을 계산 하는 처리기 t 는 이동 0에서 1 5 초 마다.The PaintSurface handler calculates a value of t that goes from 0 to 1 every five seconds. 사용 하 여는 Math.Cos 함수는 값으로 변환할 t 는 범위는 0에서 1을 0으로 다시 1은 오른쪽 맨 위에 있는 자전거에 해당 하는 동안 0에서 왼쪽, 위쪽에서 시작 하는 자전거를 해당 위치 합니다.It then uses the Math.Cos function to convert that to a value of t that ranges from 0 to 1 and back to 0, where 0 corresponds to the unicycle at the beginning on the top left, while 1 corresponds to the unicycle at the top right. 코사인 함수는 파이프의 맨 위에 있는 가장 느린 고 맨 아래에서 가장 빠른 속도 하면 됩니다.The cosine function causes the speed to be slowest at the top of the pipe and fastest at the bottom.

이 값 t 첫 번째 인수에 대 한 경로 길이 곱한 수 해야 GetMatrix합니다.Notice that this value of t must be multiplied by the path length for the first argument to GetMatrix. 행렬에 적용 되는 SKCanvas 자전거 패스 그리기에 대 한 개체입니다.The matrix is then applied to the SKCanvas object for drawing the unicycle path.

경로 열거합니다.Enumerating the Path

클래스를 포함 하는 두 개의 SKPath 경로의 내용을 열거할 수 있습니다.Two embedded classes of SKPath allow you to enumerate the contents of path. 이러한 클래스는 SKPath.Iterator 하 고 SKPath.RawIterator 합니다.These classes are SKPath.Iterator and SKPath.RawIterator. 두 클래스는 매우 유사 하지만 SKPath.Iterator 경로 길이가 0 인, 또는 길이가 0에 가까운 요소를 제거할 수 있습니다.The two classes are very similar, but SKPath.Iterator can eliminate elements in the path with a zero length, or close to a zero length. RawIterator 아래 예제에 사용 됩니다.The RawIterator is used in the example below.

형식의 개체를 가져올 수 있습니다 SKPath.RawIterator 를 호출 하 여 합니다 CreateRawIterator 메서드의 SKPath합니다.You can obtain an object of type SKPath.RawIterator by calling the CreateRawIterator method of SKPath. 반복적으로 호출 하 여 수행 됩니다 경로 통해 열거 합니다 Next 메서드.Enumerating through the path is accomplished by repeatedly calling the Next method. 4 개의 배열을 전달 SKPoint 값:Pass to it an array of four SKPoint values:

SKPoint[] points = new SKPoint[4];
...
SKPathVerb pathVerb = rawIterator.Next(points);

합니다 Next 의 멤버를 반환 하는 메서드를 SKPathVerb 열거형 형식입니다.The Next method returns a member of the SKPathVerb enumeration type. 이러한 값을 경로에 특정 그리기 명령을 나타냅니다.These values indicate the particular drawing command in the path. 배열에 삽입 하는 유효한 점 개수가이 동사에 따라 달라 집니다.The number of valid points inserted in the array depends on this verb:

  • Move 단일 지점Move with a single point
  • Line 두 점과 함께Line with two points
  • Cubic 점이 4 개를 사용 하 여Cubic with four points
  • Quad 세 개의 점을 사용 하 여Quad with three points
  • Conic 세 개의 점을 사용 하 여 (또한 호출을 ConicWeight 가중치에 대 한 메서드)Conic with three points (and also call the ConicWeight method for the weight)
  • Close 하나의 점에서Close with one point
  • Done

Done 동사 경로 열거형 완료 되었음을 나타냅니다.The Done verb indicates that the path enumeration is complete.

있으며 없습니다 Arc 동사입니다.Notice that there are no Arc verbs. 이 모든 원호 경로에 추가 하는 경우에 베 지 어 곡선으로 변환 됩니다 것을 나타냅니다.This indicates that all arcs are converted into Bézier curves when added to the path.

에 있는 정보의 일부를 SKPoint 배열 중복 됩니다.Some of the information in the SKPoint array is redundant. 예를 들어 경우는 Move 뒤에 동사를 Line 동사를 수반 되는 두 요소의 첫 번째는 Line 와 같습니다는 Move 지점.For example, if a Move verb is followed by a Line verb, then the first of the two points that accompany the Line is the same as the Move point. 실제로이 중복 매우 유용합니다.In practice, this redundancy is very helpful. 표시 되는 경우는 Cubic 동사 입방 형 3 차원 큐빅 곡선을 정의 하는 모든 네 가지 요소와 함께 합니다.When you get a Cubic verb, it is accompanied by all four points that define the cubic Bézier curve. 이전 동사 설정한 현재 위치를 유지할 필요가 없습니다.You don't need to retain the current position established by the previous verb.

문제가 있는 동사에 것 인데, Close합니다.The problematic verb, however, is Close. 이 명령은 현재 위치에서에서 이전에 설정 된 윤곽선의 시작 부분에 직선을 그립니다는 Move 명령입니다.This command draws a straight line from the current position to the beginning of the contour established earlier by the Move command. 이상적으로 Close 동사를 하나의 지점 보다는이 두 지점을 제공 해야 합니다.Ideally, the Close verb should provide these two points rather than just one point. 더욱 심각한 함께 제공 되는 지점에는 Close 동사는 항상 (0, 0).What's worse is that the point accompanying the Close verb is always (0, 0). 경로 통해 열거 하는 경우에 유지 해야 아마도 Move 지점과 현재 위치입니다.When you enumerate through a path, you'll probably need to retain the Move point and the current position.

열거, 병합, 및 MalformingEnumerating, Flattening, and Malforming

알고리즘을 적용 하는 것이 바람직 경우가 변환 malform에 대 한 경로에 어떤 방식으로든에서:It is sometimes desirable to apply an algorithmic transform to a path to malform it in some way:

이러한 문자는 대부분 직선 이루어진 아직 곡선에 이러한 직선을 twisted 분명히 있어야 합니다.Most of these letters consist of straight lines, yet these straight lines have apparently been twisted into curves. 어떻게 이것이 가능 한가요?How is this possible?

키가 원래 직선은 일련의 작은 직선으로 분류 됩니다.The key is that the original straight lines are broken into a series of smaller straight lines. 그런 다음 곡선을 형성 하는 다양 한 방법의 더 작은 직선 이러한 개별을 조작할 수 있습니다.These individual smaller straight lines can then be manipulated in different ways to form a curve.

이 프로세스를 사용 하는 데는 SkiaSharpFormsDemos 샘플에는 정적 PathExtensions 클래스를 Interpolate 구분 하는 메서드를 하나의 단위 길이에 다양 한 짧은 줄에는 직선입니다.To help with this process, the SkiaSharpFormsDemos sample contains a static PathExtensions class with an Interpolate method that breaks down a straight line into numerous short lines that are only one unit in length. 또한 클래스에는 일련의 작은 직선 곡선 근사치는 세 가지 유형의 베 지 어 곡선으로 변환 하는 여러 메서드가 들어 있습니다.In addition, the class contains several methods that convert the three types of Bézier curves into a series of tiny straight lines that approximate the curve. (문서의 파라메트릭 수식에 표시 된 베 지 어 곡선의 세 가지 형식.) 이 프로세스를 호출할 평면화 곡선:(The parametric formulas were presented in the article Three Types of Bézier Curves.) This process is called flattening the curve:

static class PathExtensions
{
    ...
    static SKPoint[] Interpolate(SKPoint pt0, SKPoint pt1)
    {
        int count = (int)Math.Max(1, Length(pt0, pt1));
        SKPoint[] points = new SKPoint[count];

        for (int i = 0; i < count; i++)
        {
            float t = (i + 1f) / count;
            float x = (1 - t) * pt0.X + t * pt1.X;
            float y = (1 - t) * pt0.Y + t * pt1.Y;
            points[i] = new SKPoint(x, y);
        }

        return points;
    }

    static SKPoint[] FlattenCubic(SKPoint pt0, SKPoint pt1, SKPoint pt2, SKPoint pt3)
    {
        int count = (int)Math.Max(1, Length(pt0, pt1) + Length(pt1, pt2) + Length(pt2, pt3));
        SKPoint[] points = new SKPoint[count];

        for (int i = 0; i < count; i++)
        {
            float t = (i + 1f) / count;
            float x = (1 - t) * (1 - t) * (1 - t) * pt0.X +
                        3 * t * (1 - t) * (1 - t) * pt1.X +
                        3 * t * t * (1 - t) * pt2.X +
                        t * t * t * pt3.X;
            float y = (1 - t) * (1 - t) * (1 - t) * pt0.Y +
                        3 * t * (1 - t) * (1 - t) * pt1.Y +
                        3 * t * t * (1 - t) * pt2.Y +
                        t * t * t * pt3.Y;
            points[i] = new SKPoint(x, y);
        }

        return points;
    }

    static SKPoint[] FlattenQuadratic(SKPoint pt0, SKPoint pt1, SKPoint pt2)
    {
        int count = (int)Math.Max(1, Length(pt0, pt1) + Length(pt1, pt2));
        SKPoint[] points = new SKPoint[count];

        for (int i = 0; i < count; i++)
        {
            float t = (i + 1f) / count;
            float x = (1 - t) * (1 - t) * pt0.X + 2 * t * (1 - t) * pt1.X + t * t * pt2.X;
            float y = (1 - t) * (1 - t) * pt0.Y + 2 * t * (1 - t) * pt1.Y + t * t * pt2.Y;
            points[i] = new SKPoint(x, y);
        }

        return points;
    }

    static SKPoint[] FlattenConic(SKPoint pt0, SKPoint pt1, SKPoint pt2, float weight)
    {
        int count = (int)Math.Max(1, Length(pt0, pt1) + Length(pt1, pt2));
        SKPoint[] points = new SKPoint[count];

        for (int i = 0; i < count; i++)
        {
            float t = (i + 1f) / count;
            float denominator = (1 - t) * (1 - t) + 2 * weight * t * (1 - t) + t * t;
            float x = (1 - t) * (1 - t) * pt0.X + 2 * weight * t * (1 - t) * pt1.X + t * t * pt2.X;
            float y = (1 - t) * (1 - t) * pt0.Y + 2 * weight * t * (1 - t) * pt1.Y + t * t * pt2.Y;
            x /= denominator;
            y /= denominator;
        }

        return points;
    }

    static double Length(SKPoint pt0, SKPoint pt1)
    {
        return Math.Sqrt(Math.Pow(pt1.X - pt0.X, 2) + Math.Pow(pt1.Y - pt0.Y, 2));
    }
}

이러한 모든 메서드는 확장 메서드에서 참조 CloneWithTransform 도이 클래스에 포함 되어 있으며 아래에 표시 합니다.All these methods are referenced from the extension method CloneWithTransform also included in this class and shown below. 이 메서드는 경로 명령을 열거 하 고 데이터를 기반으로 새 경로 생성 하 여 경로 복제 합니다.This method clones a path by enumerating the path commands and constructing a new path based on the data. 그러나 새 경로 으로만 이루어진 MoveToLineTo 호출 합니다.However, the new path consists only of MoveTo and LineTo calls. 모든 곡선 및 직선 일련의 작은 줄으로 세분화 됩니다.All the curves and straight lines are reduced to a series of tiny lines.

호출할 때 CloneWithTransform, 메서드에 전달할를 Func<SKPoint, SKPoint>를 사용 하는 함수는는 SKPaint 반환 하는 매개 변수는 SKPoint 값.When calling CloneWithTransform, you pass to the method a Func<SKPoint, SKPoint>, which is a function with an SKPaint parameter that returns an SKPoint value. 이 함수는 사용자 지정 알고리즘 변환을 적용 하려면 모든 지점에 대 한 호출 됩니다.This function is called for every point to apply a custom algorithmic transform:

static class PathExtensions
{
    public static SKPath CloneWithTransform(this SKPath pathIn, Func<SKPoint, SKPoint> transform)
    {
        SKPath pathOut = new SKPath();

        using (SKPath.RawIterator iterator = pathIn.CreateRawIterator())
        {
            SKPoint[] points = new SKPoint[4];
            SKPathVerb pathVerb = SKPathVerb.Move;
            SKPoint firstPoint = new SKPoint();
            SKPoint lastPoint = new SKPoint();

            while ((pathVerb = iterator.Next(points)) != SKPathVerb.Done)
            {
                switch (pathVerb)
                {
                    case SKPathVerb.Move:
                        pathOut.MoveTo(transform(points[0]));
                        firstPoint = lastPoint = points[0];
                        break;

                    case SKPathVerb.Line:
                        SKPoint[] linePoints = Interpolate(points[0], points[1]);

                        foreach (SKPoint pt in linePoints)
                        {
                            pathOut.LineTo(transform(pt));
                        }

                        lastPoint = points[1];
                        break;

                    case SKPathVerb.Cubic:
                        SKPoint[] cubicPoints = FlattenCubic(points[0], points[1], points[2], points[3]);

                        foreach (SKPoint pt in cubicPoints)
                        {
                            pathOut.LineTo(transform(pt));
                        }

                        lastPoint = points[3];
                        break;

                    case SKPathVerb.Quad:
                        SKPoint[] quadPoints = FlattenQuadratic(points[0], points[1], points[2]);

                        foreach (SKPoint pt in quadPoints)
                        {
                            pathOut.LineTo(transform(pt));
                        }

                        lastPoint = points[2];
                        break;

                    case SKPathVerb.Conic:
                        SKPoint[] conicPoints = FlattenConic(points[0], points[1], points[2], iterator.ConicWeight());

                        foreach (SKPoint pt in conicPoints)
                        {
                            pathOut.LineTo(transform(pt));
                        }

                        lastPoint = points[2];
                        break;

                    case SKPathVerb.Close:
                        SKPoint[] closePoints = Interpolate(lastPoint, firstPoint);

                        foreach (SKPoint pt in closePoints)
                        {
                            pathOut.LineTo(transform(pt));
                        }

                        firstPoint = lastPoint = new SKPoint(0, 0);
                        pathOut.Close();
                        break;
                }
            }
        }
        return pathOut;
    }
    ...
}

복제 경로 작은 직선에 감소 하기 때문에 변환 함수에 직선 곡선으로 변환 하는 기능이 있습니다.Because the cloned path is reduced to tiny straight lines, the transform function has the capability of converting straight lines to curves.

메서드를 각 윤곽선 이라는 변수에 있는 첫 번째 지점에서 유지 하는 공지 firstPoint 변수에 현재 위치 각 그리기 명령 및 lastPoint합니다.Notice that the method retains the first point of each contour in the variable called firstPoint and the current position after each drawing command in the variable lastPoint. 이러한 변수는 마지막 닫는 생성 하는 데 필요한 경우 줄을 Close 동사 발생 합니다.These variables are necessary to construct the final closing line when a Close verb is encountered.

합니다 GlobularText 샘플이 확장 메서드를 사용 하 여 보이는 텍스트 주위에 배치할 반구 3D 효과 냅니다.The GlobularText sample uses this extension method to seemingly wrap text around a hemisphere in a 3D effect:

합니다 GlobularTextPage 클래스 생성자는이 변환을 수행 합니다.The GlobularTextPage class constructor performs this transform. 만듭니다는 SKPaint 텍스트에 대해 개체를 가져오는 다음을 SKPath 에서 개체를 GetTextPath 메서드.It creates an SKPaint object for the text, and then obtains an SKPath object from the GetTextPath method. 이 전달 된 경로 CloneWithTransform 변환 함수를 함께 확장 메서드:This is the path passed to the CloneWithTransform extension method along with a transform function:

public class GlobularTextPage : ContentPage
{
    SKPath globePath;

    public GlobularTextPage()
    {
        Title = "Globular Text";

        SKCanvasView canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;

        using (SKPaint textPaint = new SKPaint())
        {
            textPaint.Typeface = SKTypeface.FromFamilyName("Times New Roman");
            textPaint.TextSize = 100;

            using (SKPath textPath = textPaint.GetTextPath("HELLO", 0, 0))
            {
                SKRect textPathBounds;
                textPath.GetBounds(out textPathBounds);

                globePath = textPath.CloneWithTransform((SKPoint pt) =>
                {
                    double longitude = (Math.PI / textPathBounds.Width) *
                                            (pt.X - textPathBounds.Left) - Math.PI / 2;
                    double latitude = (Math.PI / textPathBounds.Height) *
                                            (pt.Y - textPathBounds.Top) - Math.PI / 2;

                    longitude *= 0.75;
                    latitude *= 0.75;

                    float x = (float)(Math.Cos(latitude) * Math.Sin(longitude));
                    float y = (float)Math.Sin(latitude);

                    return new SKPoint(x, y);
                });
            }
        }
    }
    ...
}

변환 함수는 먼저 라는 두 개의 값을 계산 longitudelatitude – π/2 맨 위 및 텍스트의 왼쪽에서에서 오른쪽 텍스트의 맨 아래에서 π/2 사이의 합니다.The transform function first calculates two values named longitude and latitude that range from –π/2 at the top and left of the text, to π/2 at the right and bottom of the text. 이러한 값의 범위는 시각적으로 만족 하지 않으므로 0.75를 곱하여 인하 됩니다.The range of these values isn't visually satisfactory, so they are reduced by multiplying by 0.75. (이러한 조정 하지 않고 코드를 사용해 보십시오.(Try the code without those adjustments. 텍스트가 너무 북부 및 중남부 극 지방에서 모호한 측면에서 씬.) 이러한 3 차원 구형 좌표에 2 차원 변환 됩니다 xy 표준 수식 좌표입니다.The text becomes too obscure at the north and south poles, and too thin at the sides.) These three-dimensional spherical coordinates are converted to two-dimensional x and y coordinates by standard formulas.

새 경로 필드로 저장 됩니다.The new path is stored as a field. PaintSurface 처리기 다음 단순히 해야 중심 및 화면에 표시 하는 경로 확장 합니다.The PaintSurface handler then merely needs to center and scale the path to display it on the screen:

public class GlobularTextPage : ContentPage
{
    SKPath globePath;
    ...
    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        using (SKPaint pathPaint = new SKPaint())
        {
            pathPaint.Style = SKPaintStyle.Fill;
            pathPaint.Color = SKColors.Blue;
            pathPaint.StrokeWidth = 3;
            pathPaint.IsAntialias = true;

            canvas.Translate(info.Width / 2, info.Height / 2);
            canvas.Scale(0.45f * Math.Min(info.Width, info.Height));     // radius
            canvas.DrawPath(globePath, pathPaint);
        }
    }
}

이 매우 다양 한 기술입니다.This is a very versatile technique. 경로 효과의 배열에서 설명 하는 경우는 경로 효과 문서 항목을 포함 시켜야 느낌 둘러싸지 매우 않습니다 채울 방법이이 있습니다.If the array of path effects described in the Path Effects article doesn't quite encompass something you felt should be included, this is a way to fill in the gaps.