원호를 그리는 3가지 방법

SkiaSharp을 사용하여 세 가지 방법으로 호를 정의하는 방법 알아보기

호는 타원의 둘레에 있는 곡선(예: 이 무한대 기호의 둥근 부분)입니다.

무한대 기호

이러한 정의의 단순성에도 불구하고 모든 필요를 충족하는 아크 그리기 함수를 정의할 수 있는 방법은 없으므로 원호를 그리는 가장 좋은 방법인 그래픽 시스템 간에는 합의가 없습니다. 이러한 이유로 클래스는 SKPath 한 가지 접근 방법으로만 제한하지 않습니다.

SKPathAddArc 메서드, 5개의 서로 다른 ArcTo 메서드 및 두 개의 상대 RArcTo 메서드를 정의합니다. 이러한 메서드는 호를 지정하는 세 가지 매우 다른 방법을 나타내는 세 가지 범주로 나타낸다. 사용하는 것은 호를 정의하는 데 사용할 수 있는 정보와 이 호가 그리는 다른 그래픽과 어떻게 일치하는지에 따라 달라집니다.

각도 호

원호 그리기에 대한 각도 호 접근 방식을 사용하려면 타원을 경계로 하는 사각형을 지정해야 합니다. 이 타원의 둘레에 있는 호는 원호의 시작 부분과 길이를 나타내는 타원의 중심에서 각도로 표시됩니다. 서로 다른 두 메서드는 각도 호를 그립니다. 메서드 및 메서드는 ArcTo 다음과 같습니다AddArc.

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

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

이러한 메서드는 Android AddArc 및 [ArcTo]xref:Android.Graphics.Path.ArcTo*) 메서드와 동일합니다. iOS AddArc 메서드는 유사하지만 타원으로 일반화되지 않고 원 둘레의 호로 제한됩니다.

두 메서드는 줄임표의 위치와 크기를 모두 정의하는 값으로 시작 SKRect 합니다.

각도 호를 시작하는 타원

호는 이 줄임표의 둘레에 속합니다.

인수는 startAngle 타원의 중심에서 오른쪽으로 그리는 가로선을 기준으로 시계 방향 각도입니다. 인수는 sweepAngle .을 기준으로 합니다 startAngle. 다음은 startAnglesweepAngle 각각 60도 및 100도의 값입니다.

각도 호를 정의하는 각도

호는 시작 각도에서 시작됩니다. 해당 길이는 스윕 각도에 의해 제어됩니다. 호는 빨간색으로 표시됩니다.

강조 표시된 각도 호

또는 ArcTo 메서드를 사용하여 경로 AddArc 에 추가된 곡선은 줄임표 둘레의 일부일 뿐입니다.

그 자체로 각도 호

또는 sweepAngle 인수는 startAngle 음수일 수 있습니다. 호는 양수 값의 경우 시계 방향이고 음수 값의 sweepAngle 경우 시계 반대 방향입니다.

그러나 AddArc 닫힌 윤곽선은 정의하지 않습니다. 이후에 AddArc호출 LineTo 하면 호 끝에서 메서드의 지점 LineTo 까지 선이 그려지고 동일한 점이 적용ArcTo됩니다.

AddArc는 새 윤곽선이 자동으로 시작되고 다음의 마지막 인수true를 사용하는 호출 ArcTo 과 기능적으로 동일합니다.

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

마지막 인수가 호출 forceMoveTo되고 호 시작 부분에서 호출이 효과적으로 발생 MoveTo 합니다. 그것은 새로운 윤곽을 시작합니다. 마지막 인수 false는 다음과 같은 경우가 아닙니다.

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

이 버전은 ArcTo 현재 위치에서 호의 시작 부분으로 선을 그립니다. 즉, 호는 더 큰 윤곽선 중간에 있을 수 있습니다.

Angle Arc 페이지에서는 두 개의 슬라이더를 사용하여 시작 및 스윕 각도를 지정할 수 있습니다. XAML 파일은 두 요소와 SKCanvasView.Slider PaintCanvas AngleArcPage.xaml.cs 파일의 처리기는 필드로 정의된 두 개체 SKPaint 를 사용하여 타원과 호를 모두 그립니다.

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

보듯이 시작 각도와 스윕 각도는 모두 음수 값을 사용할 수 있습니다.

Angle Arc 페이지의 삼중 스크린샷

호를 생성하는 이 방법은 알고리즘적으로 가장 간단하며 호를 설명하는 매개 변수 수식을 쉽게 파생할 수 있습니다. 타원의 크기와 위치, 시작 및 스윕 각도를 알고 있는 호의 시작점과 끝점은 간단한 삼각을 사용하여 계산할 수 있습니다.

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

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

값 중 angle 하나 startAngle 또는 startAngle + sweepAngle.입니다.

원호를 정의하기 위해 두 각도를 사용하는 것은 원형 차트를 만들기 위해 그릴 호의 각 길이를 알고 있는 경우에 가장 적합합니다. 쪼개진 원형 차트 페이지에서 이를 보여 줍니다. 클래스는 ExplodedPieChartPage 내부 클래스를 사용하여 일부 조작된 데이터 및 색을 정의합니다.

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

PaintSurface 처리기는 먼저 항목을 반복하여 숫자를 계산합니다totalValues. 여기에서 각 항목의 크기를 합계의 분수로 확인하고 이를 각도로 변환할 수 있습니다.

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

각 원형 조각에 대해 새 SKPath 개체가 만들어집니다. 경로는 중심에서 선으로 구성된 다음 ArcTo 호를 그리는 선으로 구성되고, 또 다른 선은 호출 결과에서 Close 중앙으로 돌아갑니다. 이 프로그램은 가운데에서 모두 50픽셀씩 이동하여 "쪼개진" 원형 조각을 표시합니다. 이 작업에는 각 조각에 대한 스윕 각도의 중간점 방향으로 벡터가 필요합니다.

쪼개진 원형 차트 페이지의 삼중 스크린샷

"폭발"없이 어떻게 생겼는지 보려면 호출을 Translate 주석으로 처리하면 됩니다.

폭발 없이 익은 원형 차트 페이지의 삼중 스크린샷

탄젠트 아크

지원되는 SKPath 호의 두 번째 유형은 탄젠트 호이며, 호는 연결된 두 개의 선에 탄젠트되는 원의 둘레이기 때문에 호출됩니다.

탄젠트 호는 두 개의 SKPoint 매개 변수를 사용하여 메서드를 ArcTo 호출하거나 ArcTo 포인트에 대한 별도의 Single 매개 변수가 있는 오버로드를 사용하여 경로에 추가됩니다.

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

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

ArcTo 메서드는 포스트스크립트 arct (페이지 532) 함수 및 iOS AddArcToPoint 메서드와 비슷합니다.

이 메서드에는 ArcTo 다음 세 가지 점이 포함됩니다.

  • 컨투어의 현재 지점이거나, 호출되지 않은 경우 MoveTo 점(0, 0)입니다.
  • 모퉁이점이라고 하는 메서드의 ArcTo첫 번째 점 인수입니다.
  • 대상 지점이라고 하는 ArcTo두 번째 점 인수입니다.

탄젠트 호를 시작하는 3점

이 세 가지 점은 두 개의 연결된 선을 정의합니다.

탄젠트 호 3점을 연결하는 선

세 점이 콜린어인 경우, 즉 동일한 직선에 놓이면 호가 그려지지 않습니다.

메서드에는 ArcTo 매개 변수도 포함됩니다 radius . 이렇게 하면 원의 반지름이 정의됩니다.

탄젠트 호 원

탄젠트 호는 타원에 대해 일반화되지 않습니다.

두 줄이 임의의 각도에서 만나는 경우 두 선에 탄젠트되도록 해당 선 사이에 해당 원을 삽입할 수 있습니다.

두 선 사이의 탄젠트 원

윤곽선에 추가된 곡선은 메서드에 ArcTo 지정된 점 중 하나에 닿지 않습니다. 현재 지점에서 첫 번째 탄젠트 지점까지의 직선과 여기에 빨간색으로 표시된 두 번째 탄젠트 지점에서 끝나는 호로 구성됩니다.

다이어그램은 두 줄 사이에 강조 표시된 탄젠트 호를 보여 주는 빨간색 선으로 주석이 추가된 이전 다이어그램을 보여줍니다.

윤곽선에 추가되는 마지막 직선 및 호는 다음과 같습니다.

두 선 사이에 강조 표시된 탄젠트 호

두 번째 탄젠트 지점에서 윤곽선을 계속할 수 있습니다.

탄젠트 호 페이지에서 탄젠트 호를 실험할 수 있습니다. 몇 가지 편리한 SKPaint 개체를 정의하고 처리를 수행하는 여러 페이지에서 파생InteractivePage되는 첫 번째 페이지입니다.TouchPoint

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

TangentArcPage 클래스는 InteractivePage에서 파생됩니다. TangentArcPage.xaml.cs 파일의 생성자는 배열을 인스턴스화 및 초기화 touchPoints 하고 TangentArcPage.xaml 파일에서 인스턴스화된 개체로 설정 baseCanvasView (inInteractivePage)SKCanvasView합니다.

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

PaintSurface 처리기는 이 메서드를 사용하여 ArcTo 터치 포인트 및 원을 기반으로 호를 Slider그리지만 각도의 기준이 되는 원을 알고리즘적으로 계산합니다.

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

다음은 탄젠트 아크 페이지가 실행되는 것입니다.

탄젠트 호 페이지의 삼중 스크린샷

탄젠트 호는 둥근 사각형과 같은 둥근 모서리를 만드는 데 적합합니다. 이미 메서드AddRoundedRect 포함되어 있으므로 SKPath 둥근 헵타곤 페이지는 7면 다각형의 모서리를 반올림하는 데 사용하는 ArcTo 방법을 보여 줍니다. (코드는 일반 다각형에 대해 일반화됩니다.)

클래스의 처리기에는 PaintSurface heptagon의 RoundedHeptagonPage 7개 꼭짓점 좌표를 계산하는 루프가 하나 for 있고, 두 번째 루프는 이러한 꼭짓점에서 7개의 측면의 중간점을 계산합니다. 그런 다음, 이러한 중간점은 경로를 생성하는 데 사용됩니다.

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

실행 중인 프로그램은 다음과 같습니다.

둥근 헵타곤 페이지의 삼중 스크린샷

타원형 호

타원 호는 두 개의 SKPoint 매개 변수가 있는 메서드를 ArcTo 호출하거나 ArcTo 별도의 X 및 Y 좌표가 있는 오버로드를 사용하여 경로에 추가됩니다.

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)

타원형 호는 SVG(확장 가능한 벡터 그래픽) 및 유니버설 Windows 플랫폼 ArcSegment 클래스에 포함된 타원형 호와 일치합니다.

이러한 ArcTo 메서드는 컨투어의 현재 점인 두 점과 메서드에 대한 마지막 매개 변수(매개 변수 ArcTo 또는 별도의 x 매개 변수 및 y 매개 변수) xy 사이에 호를 그립니다.

타원형 호를 정의한 두 점

메서드(r또는 rxry)에 대한 ArcTo 첫 번째 점 매개 변수는 점이 아니라 타원의 가로 및 세로 반경을 지정합니다.

타원형 호를 정의한 타원입니다.

매개 변수는 xAxisRotate 이 줄임표를 회전하는 시계 방향 도의 수입니다.

타원형 호를 정의한 기울어진 타원

기울어진 줄임표가 두 점과 닿도록 배치되면 점이 서로 다른 두 호로 연결됩니다.

타원형 호의 첫 번째 집합

이러한 두 호는 두 가지 방법으로 구분할 수 있습니다. 위쪽 호는 아래쪽 호보다 크고, 호가 왼쪽에서 오른쪽으로 그려질 때 위쪽 호는 시계 방향으로 그려지고 아래쪽 호는 시계 반대 방향으로 그려집니다.

두 점 사이의 줄임표를 다른 방식으로 맞출 수도 있습니다.

타원형 호의 두 번째 집합

이제 시계 방향으로 그려진 더 작은 호와 시계 반대 방향으로 그려진 아래쪽에 더 큰 호가 있습니다.

따라서 이러한 두 지점은 기울어진 타원에 의해 정의된 호로 총 4가지 방법으로 연결될 수 있습니다.

4개의 타원형 호 모두

이러한 네 개의 호는 메서드에 대한 열거형 형식 인수의 SKPathArcSizeSKPathDirection 네 가지 조합으로 ArcTo 구분됩니다.

  • 빨간색: SKPathArcSize.Large 및 SKPathDirection.Clockwise
  • 녹색: SKPathArcSize.Small 및 SKPathDirection.Clockwise
  • 파란색: SKPathArcSize.Small 및 SKPathDirection.CounterClockwise
  • magenta: SKPathArcSize.Large 및 SKPathDirection.CounterClockwise

기울어진 타원이 두 점 사이에 맞을 만큼 크지 않은 경우 충분히 커질 때까지 균일하게 크기가 조정됩니다. 이 경우 두 개의 고유 호만 두 점을 연결합니다. 매개 변수를 사용하여 SKPathDirection 구분할 수 있습니다.

호를 정의하는 이 방법은 처음 만날 때 복잡한 소리이지만 회전된 타원으로 호를 정의할 수 있는 유일한 방법이며, 호를 윤곽의 다른 부분과 통합해야 할 때 가장 쉬운 방법입니다.

타원형 호 페이지를 사용하면 두 점과 줄임표의 크기 및 회전을 대화형으로 설정할 수 있습니다. 클래스는 EllipticalArcPage 파생InteractivePage되고 PaintSurface EllipticalArcPage.xaml.cs 코드 숨김 파일의 처리기는 네 개의 호를 그립니다.

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

여기서 실행 중입니다.

타원형 호 페이지의 삼중 스크린샷

Arc Infinity 페이지는 타원형 호를 사용하여 무한대 기호를 그립니다. 무한대 기호는 반경이 100 단위로 100 단위로 구분된 두 개의 원을 기반으로 합니다.

두 개의 원

서로 교차하는 두 줄은 두 원에 대한 탄젠트입니다.

탄젠트 선이 있는 두 원

무한대 기호는 이러한 원의 부분과 두 줄의 조합입니다. 타원형 호를 사용하여 무한대 기호를 그리려면 두 선이 원에 탄젠트되는 좌표를 결정해야 합니다.

원 중 하나에서 오른쪽 사각형을 생성합니다.

탄젠트 선과 포함된 원을 가진 두 개의 원

원의 반경은 100 단위이고 삼각형의 저혈압은 150 단위이므로 α 각도는 150 또는 41.8도로 나눈 100의 아크사인 (역 사인)입니다. 삼각형의 반대쪽 길이는 코사인의 150배인 41.8도 또는 112도이며, 이는 피타고레안 정리로도 계산할 수 있다.

탄젠트 점의 좌표는 다음 정보를 사용하여 계산할 수 있습니다.

x = 112·cos(41.8) = 83

y = 112·sin(41.8) = 75

네 개의 탄젠트 포인트는 원 반경이 100인 점(0, 0)을 중심으로 무한대 기호를 그리는 데 필요한 모든 요소입니다.

탄젠트 선과 좌표가 있는 두 원

PaintSurface 클래스의 ArcInfinityPage 처리기는 (0, 0) 지점이 페이지 가운데에 배치되도록 무한대 기호를 배치하고 경로의 크기를 화면 크기로 조정합니다.

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

코드는 인피니티 사인의 차원을 결정하는 속성을 SKPath 사용하여 Bounds 캔버스 크기로 크기를 조정합니다.

Arc Infinity 페이지의 삼중 스크린샷

결과는 약간 작아 보입니다. 이는 속성 SKPath 이 경로보다 큰 크기를 보고하고 있음을 Bounds 시사합니다.

내부적으로 Skia는 여러 이차 베지어 곡선을 사용하여 아크를 근사화합니다. 다음 섹션에서 볼 수 있듯이 이러한 곡선에는 곡선을 그리는 방법을 제어하지만 렌더링된 곡선의 일부가 아닌 제어점이 포함됩니다. 속성에는 Bounds 이러한 제어점이 포함됩니다.

더 단단히 맞도록 하려면 제어점을 제외하는 속성을 사용합니다 TightBounds . 다음은 가로 모드에서 실행되고 속성을 사용하여 TightBounds 경로 범위를 가져오는 프로그램입니다.

좁은 범위가 있는 Arc Infinity 페이지의 세 가지 스크린샷

호와 직선 사이의 연결은 수학적으로 매끄럽지만 호에서 직선으로의 변경은 약간 갑작스러운 것처럼 보일 수 있습니다. 더 나은 무한대 기호는 세 가지 유형의 베지어 곡선에 대한 다음 문서에 표시됩니다.