3 つの種類のベジエ曲線
SkiaSharp を使用して 3 次、2 次、および円錐ベジエ曲線をレンダリングする方法について説明します
ベジェ曲線は、自動車会社ルノーのフランス人エンジニア、ピエール・ベジエ(1910-1999)にちなみ、自動車ボディのコンピュータ支援設計に曲線を使用しました。
ベジエ曲線は、インタラクティブなデザインに適していることで知られています。適切に動作します。つまり、曲線が無限になったり扱いにくいりする特異点がなく、一般的に審美的に楽しいです。
コンピューターベースのフォントの文字アウトラインは、通常、ベジエ曲線で定義されます。
ベジエ曲線に関するウィキペディアの記事には、いくつかの有用な背景情報が含まれています。 ベジエ曲線という用語は、実際には同様の曲線のファミリを指します。 SkiaSharp は、 3 次曲線、 2 次曲線、 および円錐曲線と呼ばれる 3 種類のベジエ曲線をサポートします。 円錐は 、有理二次とも呼ばれます。
立方ベジエ曲線
立方は、ほとんどの開発者がベジエ曲線の主題が現れたときに考えるベジエ曲線の種類です。
3 つのSKPoint
パラメーターを持つ メソッド、または個別x
の パラメーターと y
パラメーターを持つオーバーロードをCubicTo
使用して、3 次ベジエ曲線SKPath
をCubicTo
オブジェクトに追加できます。
public void CubicTo (SKPoint point1, SKPoint point2, SKPoint point3)
public void CubicTo (Single x1, Single y1, Single x2, Single y2, Single x3, Single y3)
曲線は、コンターの現在のポイントから始まります。 完全な 3 次ベジエ曲線は、次の 4 つの点で定義されます。
- 始点: コンター内の現在の点、または (0, 0) が呼び出されていない場合
MoveTo
- 最初の制御ポイント:
point1
呼び出し内CubicTo
- 2 番目の制御ポイント:
point2
呼び出し内CubicTo
- end point:
point3
in theCubicTo
call
結果の曲線は始点から始まり、終点で終了します。 一般に、曲線は 2 つのコントロール ポイントを通過しません。代わりに、コントロールポイントは磁石と同様に機能し、曲線をそれらに向かって引っ張ります。
3 次ベジエ曲線の感覚を得る最善の方法は、実験です。 これは、 からInteractivePage
派生したベジエ曲線ページの目的です。 BezierCurvePage.xaml ファイルは、 と TouchEffect
をSKCanvasView
インスタンス化します。 BezierCurvePage.xaml.cs 分離コード ファイルは、コンストラクターに 4 つのTouchPoint
オブジェクトを作成します。 イベント ハンドラーは PaintSurface
、 を SKPath
作成して 4 つの TouchPoint
オブジェクトに基づいてベジエ曲線をレンダリングし、コントロール ポイントから終点まで点線の接線を描画します。
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);
}
}
ここでは、次の操作を実行しています。
数学的には、曲線は 3 次多項式です。 曲線は、最大で 3 点で直線と交差します。 始点では、曲線は常に始点から最初のコントロール ポイントまでの直線に接し、同じ方向になります。 終点では、曲線は常に、2 番目のコントロール ポイントから終点までの直線と同じ方向に正接します。
3 次ベジエ曲線は、常に 4 つの点を結ぶ凸四辺形で囲まれます。 これを 凸包と呼びます。 コントロール ポイントが始点と終点の間の直線上にある場合、ベジエ曲線は直線としてレンダリングされます。 ただし、3 番目のスクリーンショットが示すように、曲線はそれ自体を通過することもできます。
パスコンターには複数の接続された立方ベジエ曲線を含めることができますが、2 つの立方ベジエ曲線間の接続は、次の 3 つの点が共線 (つまり直線上にある) 場合にのみ滑らかになります。
- 1 番目の曲線の 2 番目のコントロール ポイント
- 1 番目の曲線の終点(2 番目の曲線の始点でもあります)
- 2 番目の曲線の最初のコントロール ポイント
SVG パス データに関する次の記事では、スムーズに接続されたベジエ曲線の定義を容易にする機能について説明します。
立方ベジエ曲線をレンダリングする基になるパラメトリック方程式を知ると便利な場合があります。 0 ~ 1 の範囲の t の場合、パラメトリック方程式は次のようになります。
x(t) = (1 – t)²x₀ + 3t(1 – t)²x₁ + 3t²(1 – t)x₂ + t 2x₃
y(t) = (1 – t)²y₀ + 3t(1 – t)²y₁ + 3t²(1 – t)y₂ + t 2y₃
3 の最大指数は、これらが 3 次多項式であることを確認します。 0 に等しい場合 t
、ポイントが (x₀、y₀) であり、開始点が 1 の場合 t
、ポイントは終了点である (x₃、y₃) であることを簡単に確認できます。 開始点の近く (低い値の t
場合) では、最初のコントロール ポイント (x₁、y₁) は強力な効果を持ち、終了点の近く ('t' の高い値) の 2 番目のコントロール ポイント (x₂、y₂) は強力な効果を持ちます。
円弧に対するベジエ曲線近似
ベジェ曲線を使用して円弧をレンダリングすると便利な場合があります。3 次ベジエ曲線は、円弧を 4 分の 1 の円まで非常によく近似できるため、接続された 4 つのベジエ曲線で円全体を定義できます。 この近似は、25 年以上前に公開された 2 つの記事で説明されています。
Tor Dokken,et al, "Curvature-Continuousベジエ曲線による円の良好な近似", Computer Aided Geometric Design 7 (1990), 33-41.
Michael Goldapp,"3 次多項式による円弧の近似" , Computer Aided Geometric Design 8 (1991), 227-238.
次の図は、、、pt2
pt1
、 というラベルが付いた pto
4 つの点を示し、pt3
円弧に近似するベジエ曲線 (赤で表示) を定義しています。
始点と終点から制御点までの線は、円とベジエ曲線への接線であり、 長さは L です。上記の最初の記事は、長さ L が次のように計算されるときに、ベジエ曲線が円弧に最もよく近似していることを示しています。
L = 4 × tan(α / 4) / 3
図は 45 度の角度を示しているため、L は 0.265 になります。 コードでは、その値に円の目的の半径が乗算されます。
ベジェ円弧ページでは、最大 180 度の角度で円弧を近似するベジエ曲線の定義を試すことができます。 BezierCircularArcPage.xaml ファイルは、 と をSlider
インスタンス化SKCanvasView
して角度を選択します。 BezierCircularArgPage.xaml.cs 分離コード ファイルのイベント ハンドラーはPaintSurface
、変換を使用して、ポイント (0, 0) をキャンバスの中心に設定します。 比較のためにその点を中心に円を描画し、ベジエ曲線の 2 つのコントロール ポイントを計算します。
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);
}
始点と終点 (point0
および point3
) は、円の法線パラメトリック方程式に基づいて計算されます。 円の中心は (0, 0) であるため、これらの点は円の中心から円周までの放射状ベクトルとして扱うこともできます。 コントロール ポイントは円に接する線上に存在するため、これらの放射状ベクトルに対して直角になります。 別の座標に対する直角のベクトルは、単に X 座標と Y 座標が入れ替わった元のベクトルであり、そのうちの 1 つが負の値になります。
さまざまな角度で実行されているプログラムを次に示します。
3 番目のスクリーンショットをよく見ると、角度が 180 度の場合、ベジエ曲線が半円から特に逸脱していることがわかりますが、iOS 画面では、角度が 90 度の場合は 4 分の 1 の円にちょうど収まることがわかります。
四半期の円が次のような方向にある場合、2 つのコントロール ポイントの座標を計算するのは非常に簡単です。
円の半径が 100 の場合、 L は 55 であり、覚えやすい数値です。
[ 円の四角化] ページでは、円と四角形の間の図形がアニメーション化されます。 円は、クラスのこの配列定義の最初の列に座標が表示される 4 つのベジエ曲線によって SquaringTheCirclePage
近似されます。
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() }
};
...
}
2 番目の列には、面積が円の面積とほぼ同じ四角形を定義する 4 つのベジエ曲線の座標が含まれています。 (特定の円として 正確な 領域を持つ正方形を描画することは、 円を二乗する古典的な解決不可能な幾何学的問題です。ベジェ曲線を使用して正方形をレンダリングする場合、各曲線の 2 つのコントロール ポイントは同じであり、始点と終点と共線であるため、ベジエ曲線は直線としてレンダリングされます。
配列の 3 番目の列は、アニメーションの補間値用です。 ページは 16 ミリ秒間タイマーを設定し PaintSurface
、ハンドラーはその速度で呼び出されます。
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);
}
}
ポイントは、 の正弦波振動値 t
に基づいて補間されます。 次に、補間された点を使用して、接続された 4 つのベジエ曲線の系列を構築します。 実行されているアニメーションを次に示します。
このようなアニメーションは、円弧と直線の両方としてレンダリングできる十分なアルゴリズム的柔軟性を持つ曲線がないと不可能です。
ベジェ無限大ページでは、ベジエ曲線を利用して円弧を近似することもできます。クラスのハンドラーをPaintSurface
次に示BezierInfinityPage
します。
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);
}
}
}
これらの座標をグラフ 用紙にプロットして、それらがどのように関連しているかを確認することをお勧めします。 無限大記号はポイント (0,0) を中心にして配置され、2 つのループの中心は (–150, 0) と (150, 0) と半径 100 です。 一連の CubicTo
コマンドでは、-95 と –205 の値を受け取るコントロール ポイントの X 座標 (これらの値は -150 プラスマイナス 55)、205 と 95 (150 プラスマイナス 55)、左右の辺に対して 250 と –250 を確認できます。 唯一の例外は、無限大記号が中央でそれ自体を横切る場合です。 その場合、コントロール ポイントには 50 と –50 の組み合わせの座標があり、中心付近の曲線を直線化します。
無限大記号を次に示します。
これは、Arc を描画する 3 つの方法に関する記事の Arc Infinity ページでレンダリングされる無限大記号よりも、中心に向かってやや滑らかです。
二次ベジエ曲線
2 次ベジエ曲線には制御点が 1 つだけあり、曲線は始点、制御点、終点の 3 つの点だけで定義されます。 パラメトリック方程式は、3 次ベジエ曲線とよく似ていますが、最も高い指数は 2 であるため、曲線は 2 次多項式になります。
x(t) = (1 – t)²x₀ + 2t(1 – t)x₁ + t²x₂
y(t) = (1 – t)²y₀ + 2t(1 – t)y₁ + t²y₂
パスに 2 次ベジエ曲線を追加するには、 メソッドまたはオーバーロードを使用QuadTo
し、座標とy
座標をQuadTo
指定しますx
。
public void QuadTo (SKPoint point1, SKPoint point2)
public void QuadTo (Single x1, Single y1, Single x2, Single y2)
メソッドは、コントロール ポイントとして、 の現在位置から にpoint2
point1
曲線を追加します。
ベジェ曲線ページとよく似た 2 次ベジエ曲線 ページを使用して、2 次ベジ エ曲線 を試すことができますが、タッチ ポイントは 3 つだけです。 次に示すのは、PaintSurface
二次Curve.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);
}
}
そして、ここでそれが実行されています:
点線は、始点と終点の曲線に接し、制御点で接します。
2 次ベジエは、一般的な形状の曲線が必要な場合に適していますが、2 つではなく 1 つのコントロール ポイントの利便性を好みます。 2 次ベジエは他のどの曲線よりも効率的にレンダリングされるため、Skia で楕円円弧をレンダリングするために内部的に使用されます。
ただし、2 次ベジエ曲線の形状は楕円ではなく、楕円の円弧を近似するために複数の 2 次ベジエが必要な理由です。二次ベジエは、代わりに放物線のセグメントです。
コニック ベジエ曲線
コニック ベジエ曲線 (有理二次ベジエ曲線とも呼ばれます) は、ベジエ曲線のファミリに比較的最近追加された曲線です。 2 次ベジエ曲線と同様に、有理二次ベジエ曲線には始点、終点、1 つの制御点が含まれます。 しかし、有理二次ベジエ曲線には 重み 値も必要です。 パラメトリック数式には比率が含まれるため、 これは合理的 な 2 次と呼ばれます。
X と Y のパラメトリック方程式は、同じ分母を共有する比率です。 0 から 1 の範囲の t の分母と w の重みの値を次に示します。
d(t) = (1 – t)² + 2wt(1 – t) + t²
理論上、有理二次は 3 つの個別の重み値 (3 つの項ごとに 1 つ) を含むことができますが、これらは中間項の 1 つの重み値のみに簡略化できます。
X 座標と Y 座標のパラメトリック方程式は、2 次ベジエのパラメトリック方程式と似ていますが、中間項にも重み値が含まれており、式は分母で除算されます。
x(t) = ((1 – t)²x₀ + 2wt(1 – t)x₁ + t²x₂)) ÷ d(t)
y(t) = ((1 – t)²y₀ + 2wt(1 – t)y₁ + t²y₂)) ÷ d(t)
有理二次ベジエ曲線は、ハイパーボラス、放物線、楕円、円など、任意の円錐セクションのセグメントを正確に表すことができるため、 円 錐とも呼ばれます。
パスに有理二次ベジエ曲線を追加するには、 メソッドまたはオーバーロードを使用ConicTo
し、座標とy
座標をConicTo
指定x
します。
public void ConicTo (SKPoint point1, SKPoint point2, Single weight)
public void ConicTo (Single x1, Single y1, Single x2, Single y2, Single weight)
最後 weight
のパラメーターに注目してください。
[円錐曲線] ページでは、これらの曲線を試すことができます。 ConicCurvePage
クラスは、InteractivePage
から派生したものです。 ConicCurvePage.xaml ファイルは、 をインスタンス化Slider
して、-2 から 2 の重み値を選択します。 ConicCurvePage.xaml.cs 分離コード ファイルは 3 つのTouchPoint
オブジェクトを作成しPaintSurface
、ハンドラーは単にコントロール ポイントに接線を含む結果曲線をレンダリングします。
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);
}
}
ここでは、次の操作を実行しています。
ご覧のように、コントロール ポイントは、ウェイトが高い場合に、曲線の方に向かって引っ張るように見えます。 重みが 0 の場合、曲線は始点から終点までの直線になります。
理論上、負の重みが許容され、曲線がコントロール ポイントから 曲 がります。 ただし、-1 以下の重みによって、パラメトリック方程式の分母が t の特定の値に対して負になります。 この理由から、メソッドでは ConicTo
負の重みは無視される可能性があります。 Conic Curve プログラムを使用すると、負の重みを設定できますが、実験でわかるように、負の重みは 0 の重みと同じ効果を持ち、直線がレンダリングされます。
コントロール ポイントと重みを派生させるのは非常に簡単です。このメソッドを使用 ConicTo
すると、半円まで円弧を描画できます (ただし、含まれません)。 次の図では、始点と終点の接線がコントロール ポイントで一致しています。
三角法を使用して、円の中心からのコントロール ポイントの距離を決定できます。これは、円の半径を角度αの半分のコサインで割った値です。 始点と終点の間に円弧を描画するには、重みを角度の半分の同じコサインに設定します。 角度が 180 度の場合、接線は一致せず、重みは 0 であることに注意してください。 ただし、角度が 180 度未満の場合、数学は正常に動作します。
[円錐円弧] ページでこれを示します。 ConicCircularArc.xaml ファイルは、角度を選択するための をインスタンス化Slider
します。 ConicCircularArc.xaml.cs 分離コード ファイルのハンドラーはPaintSurface
、コントロール ポイントと重みを計算します。
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);
}
}
ご覧のように、赤で表示されるパスと、参照用に ConicTo
表示される基になる円の間に視覚的な違いはありません。
しかし、角度を180度に設定すると、数学は失敗します。
理論的には(パラメトリック方程式に基づいて)、同じ点を持つが重みの負の値を持つへのConicTo
別の呼び出しで円を完了できるため、負の重みをサポートしていないのConicTo
は残念です。 これにより、ゼロ度と 180 度の間の任意の角度 (含まない) に基づいて、2 つの ConicTo
曲線だけで円全体を作成できます。