Namespace Infragistics.Samples.Common Public Class BezierCurveBuilder ''' <summary> ''' 指定の点を通るベジエ曲線を表す PathFigure を返します。 ''' </summary> Public Shared Function GetBezierSegments(points As PointCollection, tension As Double, Optional isClosed As Boolean = False) As PathFigure Dim ret As New PathFigure() ret.Segments.Clear() ret.IsClosed = False If isClosed Then Dim first As Point = points(0) Dim last As Point = points(points.Count - 1) If first.X <> last.X OrElse first.Y <> last.Y Then points.Add(first) End If End If Dim bzPoints = GetBezierPoints(points, tension) ' 最初の点が開始ポイントです。 ret.StartPoint = bzPoints(0) For i As Integer = 1 To bzPoints.Count - 1 Step 3 ' B1 コントロール ポイント ' B2 コントロール ポイント ' P2 開始 / 終了ポイント ret.Segments.Add(New BezierSegment() With { _ .Point1 = bzPoints(i), _ .Point2 = bzPoints(i + 1), _ .Point3 = bzPoints(i + 2) _ }) Next Return ret End Function #Region "Bezier Methods" ' 計算のためのロジックのいくつかは次の記事に基づいています。 ' http://www.codeproject.com/KB/silverlight/MapBezier.aspx ''' <summary> ''' 指定の点を通るベジエ曲線のポイントを返します。 ''' </summary> ''' <param name="points"></param> ''' <param name="tension"></param> ''' <returns></returns> Public Shared Function GetBezierPoints(points As PointCollection, tension As Double) As PointCollection Dim ret As New PointCollection() For i As Integer = 0 To points.Count - 1 ' 最初の点をそのまま付加するため If i = 0 Then ret.Add(points(0)) Continue For End If ' 最初と最後の点を除く各ポイントが B1、B2、次のポイントをもつため ' 最後の点は次のポイントをもちません。 ret.Add(GetBezierControlPoint1(points, i - 1, tension)) ret.Add(GetBezierControlPoint2(points, i - 1, tension)) ret.Add(points(i)) Next Return ret End Function ''' <summary> ''' ベジエ曲線の最初のコントロール ポイントを返します。 ''' </summary> ''' <param name="points">曲線上のポイント</param> ''' <param name="i">コントロール ポイントを計算するためのポイント番号</param> ''' <param name="tension">テンション</param> ''' <returns></returns> ''' <remarks>式: B1i = Pi + Pi' / 3</remarks> Public Shared Function GetBezierControlPoint1(points As PointCollection, i As Integer, tension As Double) As Point Dim drv = GetBezierDerivative(points, i, tension) Return New Point(points(i).X + drv.X / 3, points(i).Y + drv.Y / 3) End Function ''' <summary> ''' ベジエ曲線の 2 番目のコントロール ポイントを返します。 ''' </summary> ''' <param name="points">曲線上のポイント</param> ''' <param name="i">コントロール ポイントを計算するためのポイント番号</param> ''' <param name="tension">テンション</param> ''' <returns></returns> ''' <remarks>式: B2i = P[i + 1] - P'[i + 1] / 3</remarks> Public Shared Function GetBezierControlPoint2(points As PointCollection, i As Integer, tension As Double) As Point Dim drv = GetBezierDerivative(points, i + 1, tension) Return New Point(points(i + 1).X - drv.X / 3, points(i + 1).Y - drv.Y / 3) End Function ''' <summary> ''' ポイント コレクションにポイントのサイズが変わった派生形を返します。 ''' </summary> ''' <param name="points">曲線上のポイント</param> ''' <param name="i">コントロール ポイントを計算するためのポイント番号</param> ''' <param name="tension">テンション</param> ''' <returns></returns> Public Shared Function GetBezierDerivative(points As PointCollection, i As Integer, tension As Double) As Point If points.Count < 2 Then Throw New System.ArgumentOutOfRangeException("points", "PointCollection must contain at least two points.") End If Dim x As Double, y As Double If i = 0 Then ' 最初のポイント x = (points(1).X - points(0).X) / tension y = (points(1).Y - points(0).Y) / tension Return New Point(x, y) End If If i Is points.Count - 1 Then ' 最後のポイント x = (points(i).X - points(i - 1).X) / tension y = (points(i).Y - points(i - 1).Y) / tension Return New Point(x, y) End If x = (points(i + 1).X - points(i - 1).X) / tension y = (points(i + 1).Y - points(i - 1).Y) / tension Return New Point(x, y) End Function #End Region ''' <summary> ''' ベジエ曲線のために Path.Data を表す文字列を返します。 ''' Blend 中の曲線を探すためです。このコードでは使用されません。 ''' </summary> ''' <returns>XAML で使用される Path.Data</returns> Public Shared Function GetBezierPathDataString(points As PointCollection, tension As Double) As String Dim bzPoints = GetBezierPoints(points, tension) Dim sbRet As New System.Text.StringBuilder() ' M 0,0 C 1.66, 1.66 6.66,11.66 10,10 For i As Integer = 0 To bzPoints.Count - 1 If i = 0 Then sbRet.AppendFormat("M {0},{1} ", bzPoints(i).X, bzPoints(i).Y) Continue For End If If i Mod 3 = 1 Then sbRet.Append("C ") End If sbRet.AppendFormat("{0},{1} ", bzPoints(i).X, bzPoints(i).Y) Next Return sbRet.ToString() End Function End Class End Namespace