Spline_Bezier曲線測繪

2018年12月02日

首頁

 

 

如何由GDI+繪圖工具取得SPlineBezierEllipse等曲線座標及現場放樣

一、    前言

GDI+(VB.Net)函數中,有繪製雲狀線(Cubic SPline)、比賽爾(Bezier curve)及橢圓(Ellipse)曲線之工具,但其並無直接提供上述曲線之座標,因此如欲於現場放樣,就可能會產生問題,本文將介紹如何利用VB.NetAddPathFlattenPathData等工具以取得上述曲線之座標,在圖上位置佈設一基準線AB,計算ABAX(X為曲線上)之夾角CitaAX距離L,在工地放樣時,我們可以將經緯儀等測量儀器架設於對應於圖上基準點A之工址,固定上、下盤,後視點B後放鬆上盤,依圖上計算角度方向旋轉cita角後固定上盤,使用橫軸制動螺絲 調整望遠鏡方向後固定方向,然後量取距離LX,如此依序逐點佈設曲線。

 

1.SPline曲線

2.SBesier曲線

 

 

二、    繪製曲線及計算曲線座標

一般Spline曲線都會通過已知測點,曲線形狀受張力箝制大小(0.0~1.0)影響,而Bezier曲線只通過起點及終點,其形狀受第1,2點方向及地第(n,n-1)點方向控制。VB.Net DrawCurve(SPline)曲線點數可以不受限制;而DrawBezier曲線點數限制為4點,DrawBeziers曲線點數限制為(3n+1)點,n=1時等同DrawBezier

利用VBVB.Net自定程式實作前述曲線,可以很容易地在網路上搜尋下載,因此本文不擬專文介紹,下面我們要討論的是如何利用VB.Net命名空間所提供的工具來測繪SPlineBezier曲線,假設User想繪製通過點A(-9.1,-7),B(4,5),C(5,2),D(1,5,-8.7)四控制點之雲狀線或Bezier曲線及其曲線關鍵之座標,可以依下列步驟繪製及計算:

 

(a)利用路徑扁平成線段

(1)   將控制點座標(浮點Real資料型態,世界實際座標)轉換成以表單圖框之螢幕像素座標(Pixel整數資料型態)

(2)   利用DrawCurve繪製曲線

(3)   利用AddPath將曲線轉化為路徑(Path)

(4)   將路徑扁平(Flatten)為多重線(資料型態為Real)

(5)   利用PathPoints()擷取多重線座標

(6)   放樣距離、角度計算

 

    如欲求某特定之x(y)所對應之y(x)值所有解答,求x=xg(y=yg)直線與扁平化後路徑多重線之所有實夾點即可。

 

(b)利用讀取影像顏色擷取曲線座標

(1) 將控制點座標(浮點Real資料型態,世界實際座標)轉換成以表單圖框之螢幕像素座標(Pixel整數資料型態)

(2) 利用DrawCurve繪製曲線

(3) 計算曲線外圍最小包含矩形

(4) 在包含矩形內由上至下(或由下至上)逐條掃瞄水平線,搜尋及記錄與圖框背景色不同之像素位置(Pixel整數資料型態)

(5) 將掃瞄出像素位置轉換世界座標點型態

(6)   放樣距離、角度計算

 

如欲求某特定之x(y)所對應之y(x)值所有解答,可設定i=Cint(xg)[j=cint(yg)] ,然後利用水平(垂直)直接掃瞄即可。

 

三、    程式碼編寫

上述兩種測繪曲線之程式碼主要有

 

(a)  世界座標及螢幕座標轉換

  Public Function ptConvertToUser(ByVal canvas As PictureBox, ByVal xLeft As Single, ByVal xRight As Single, ByVal yTop As Single, ByVal yBottom As Single, _

    ByVal xPix As Integer, ByVal yPix As Integer) As PointF

        ptConvertToUser.X = CSng(xRight - xLeft) / CSng(canvas.ClientSize.Width) * xPix + xLeft

        ptConvertToUser.Y = CSng(yBottom - yTop) / CSng(canvas.ClientSize.Height) * yPix + yTop

    End Function

    Public Function ptConvertToPix(ByVal canvas As PictureBox, ByVal xLeft As Double, ByVal xRight As Double, ByVal yTop As Double, ByVal yBottom As Double, _

     ByVal xUser As Single, ByVal yUser As Single) As PointF

        ptConvertToPix.X = xUser * CSng(canvas.ClientSize.Width) / (xRight - xLeft) - xLeft * CSng(canvas.ClientSize.Width) / (xRight - xLeft)

        ptConvertToPix.Y = yUser * CSng(canvas.ClientSize.Height) / (yBottom - yTop) - yTop * CSng(canvas.ClientSize.Height) / (yBottom - yTop)

    End Function

    Public Function ptConvertToUser(ByVal canvas As PictureBox, ByVal xLeft As Single, ByVal xRight As Single, ByVal yTop As Single, ByVal yBottom As Single, _

        ByVal ptPix As Point) As PointF

        ptConvertToUser.X = CSng(xRight - xLeft) / CSng(canvas.ClientSize.Width) * ptPix.X + xLeft

        ptConvertToUser.Y = CSng(yBottom - yTop) / CSng(canvas.ClientSize.Height) * ptPix.Y + yTop

    End Function

    Public Function ptConvertToPix(ByVal canvas As PictureBox, ByVal xLeft As Double, ByVal xRight As Double, ByVal yTop As Double, ByVal yBottom As Double, _

     ByVal PtUser As PointF) As Point

        ptConvertToPix.X = PtUser.X * CSng(canvas.ClientSize.Width) / (xRight - xLeft) - xLeft * CSng(canvas.ClientSize.Width) / (xRight - xLeft)

        ptConvertToPix.Y = PtUser.Y * CSng(canvas.ClientSize.Height) / (yBottom - yTop) - yTop * CSng(canvas.ClientSize.Height) / (yBottom - yTop)

    End Function

上述中

xLeft為包含資料點之x最小值

xRight為包含資料點x最大值

yTop為包含資料點之y最小值

yBottom為包含資料點y最大值

 

(b)繪製曲線扁平擷取座標(Spline為例)

Private Sub DrawSpline_Flatten()

        Dim ptspath() As PointF

        Dim ptsUser() As PointF

        myPen = Pens.Green

        myBmp = New Bitmap(PictureBox1.Size.Width, PictureBox1.Size.Height)

        myBmpGraphics = Graphics.FromImage(myBmp)

        Dim colorBase As Color = myBmp.GetPixel(1, 1)

        Dim ptsApi() As Point 轉換後整數資料點

        PtsData_Bounds(PictureBox1, ptsOrg, ptsApi) 曲線外圍矩形

        For i As Integer = 0 To ptsApi.Length - 1

            ListBox1.Items.Add("org x=" & ptsOrg(i).X & ";y= " & ptsOrg(i).Y & " ;api x= " & ptsApi(i).X & " ;y= " & ptsApi(i).Y)

            myBmpGraphics.DrawEllipse(Pens.Blue, ptsApi(i).X - 4, ptsApi(i).Y - 4, 8, 8)

        Next

        Dim myGraphicsPath = New GraphicsPath

        myPen = Pens.Red

        Dim tension As Single = 0.5  張力鉗制係數(0~1.0)

        myGraphicsPath.AddCurve(ptsApi, 0.5)

        Dim rect As RectangleF = myGraphicsPath.GetBounds

        myBmpGraphics.DrawRectangle(New Pen(Color.Green, 1), CInt(rect.X), CInt(rect.Y), CInt(rect.Width), CInt(rect.Height))

        myBmpGraphics.DrawPath(myPen, myGraphicsPath)

        Dim count As Integer = 0

        myGraphicsPath.Flatten()  曲線路徑扁平成折線

        ptspath = myGraphicsPath.PathPoints

        For i As Integer = 0 To ptspath.Length - 1

            myBmpGraphics.DrawEllipse(Pens.Blue, ptspath(i).X - 2, ptspath(i).Y - 2, 4, 4)

            ListBox1.Items.Add("spline x=" & ptspath(i).X & "y=" & ptspath(i).Y)

        Next

        Dim ptsBaseline(1) As PointF

        ptsBaseline(0) = New PointF(100, 100)

        ptsBaseline(1) = New PointF(200, 100)

 

        For i As Integer = 0 To ptspath.Length - 1

            myBmpGraphics.DrawLine(Pens.Green, ptsBaseline(0), ptspath(i))

            ReDim Preserve ptsUser(i)

            ptsUser(i) = ptConvertToUser(PictureBox1, xLeft, xRight, yTop, yBottom, CInt(ptspath(i).X), CInt(ptspath(i).Y)) 轉換為世界座標

            ListBox1.Items.Add("ptUser on path :x=" & ptsUser(i).X & ";y= " & ptsUser(i).Y)

        Next

        Dim lDist() As Double, angsIncl() As Double, angsPtI() As Double

        Call Stake(PictureBox1, ptsUser, ptsBaseline, lDist, angsIncl, angsPtI) 放樣資料計算

        lDist:測站至曲線測點距離

angsIncl: 測站基基準線與測點夾角(deg.)

angsPtI:座標水平線與測點夾角(deg.),方向同座標角度量測

        myBmpGraphics.DrawLine(New Pen(Color.Magenta, 2), ptsBaseline(0), ptsBaseline(1))

        PictureBox1.Image = myBmp

    End Sub

 

(c)繪製曲線後掃瞄擷取座標(Bezier為例)

 

Private Sub DrawBezier_getPixel()

        Dim ptsUser() As PointF

        myPen = Pens.Green

        myBmp = New Bitmap(PictureBox1.Size.Width, PictureBox1.Size.Height)

        myBmpGraphics = Graphics.FromImage(myBmp)

        Dim colorBase As Color = myBmp.GetPixel(1, 1)

        Dim ptsApi() As Point

 

        PtsData_Bounds(PictureBox1, ptsOrg, ptsApi)

        For i As Integer = 0 To ptsApi.Length - 1

            ListBox1.Items.Add("org x=" & ptsOrg(i).X & ";y= " & ptsOrg(i).Y & " ;api x= " & ptsApi(i).X & " ;y= " & ptsApi(i).Y)

        Next

        Dim myGraphicsPath = New GraphicsPath

        myPen = Pens.Red

        myGraphicsPath.AddBeziers(ptsApi)

        Dim rect As RectangleF = myGraphicsPath.GetBounds

        myBmpGraphics.DrawRectangle(New Pen(Color.Green, 1), CInt(rect.X), CInt(rect.Y), CInt(rect.Width), CInt(rect.Height))

        myBmpGraphics.DrawPath(myPen, myGraphicsPath)

        Dim count As Integer = 0, ptsUserApi() As Point

矩形框掃瞄

        For j As Integer = CInt(rect.Y) + 2 To CInt(rect.Height + rect.Y) - 2

            For i As Integer = CInt(rect.X) + 2 To CInt(rect.Width + rect.X) - 2

                If myBmp.GetPixel(i, j) <> colorBase Then

                    ReDim Preserve ptsUser(count), ptsUserApi(count)

                    ptsUserApi(count) = New Point(i, j)

                    ptsUser(count) = ptConvertToUser(PictureBox1, xLeft, xRight, yTop, yBottom, i, j)

                    ListBox1.Items.Add(" x=" & i & " ;y=" & j & " ;xuser= " & ptsUser(count).X & " ;yuser=" & ptsUser(count).Y)

                    count += 1

                End If

            Next i

        Next j

        Dim ptsBaseline(1) As PointF

        ptsBaseline(0) = New PointF(100, 100)

        ptsBaseline(1) = New PointF(200, 100)

        For i As Integer = 0 To UBound(ptsUserApi) Step 10

            myBmpGraphics.DrawLine(New Pen(Color.Green, 1), ptsBaseline(0), ptsUserApi(i))

        Next

        myBmpGraphics.DrawLine(Pens.Magenta, ptsBaseline(0), ptsBaseline(1))

        Dim lDist() As Double, angsIncl() As Double, angsPtI() As Double

        Call Stake(PictureBox1, ptsUser, ptsBaseline, lDist, angsIncl, angsPtI)

        For i As Integer = 0 To ptsApi.Length - 1

            myBmpGraphics.DrawEllipse(Pens.Blue, ptsApi(i).X - 4, ptsApi(i).Y - 4, 8, 8)

        Next

        PictureBox1.Image = myBmp

 

(d)繪製曲線放樣計算

Private Sub Stake(ByVal canvas As PictureBox, ByVal ptsPathUser() As PointF, ByVal ptsBaseLineAPI() As PointF, ByRef Ldist() As Double, ByRef angsIncl() As Double, ByRef angsPtI() As Double)

 

        Dim ptsBaseUser(0 To 1) As PointF

        For i As Integer = 0 To 1

            ptsBaseUser(i) = ptConvertToUser(canvas, xLeft, xRight, yTop, yBottom, ptsBaseLineAPI(i).X, ptsBaseLineAPI(i).Y)

            ListBox1.Items.Add("pt_ " & i & " ;baseLine= " & ptsBaseUser(i).X & ";" & ptsBaseUser(i).Y)

        Next

 

        Dim delX1 As Double = ptsBaseUser(1).X - ptsBaseUser(0).X

        Dim delY1 As Double = ptsBaseUser(1).Y - ptsBaseUser(0).Y

        Dim L1 As Double = Sqrt(delX1 * delX1 + delY1 * delY1)

        For i As Integer = 0 To ptsPathUser.Length - 1

            Dim delX2 As Double = CDbl(ptsPathUser(i).X - ptsBaseUser(0).X)

            Dim delY2 As Double = CDbl(ptsPathUser(i).Y - ptsBaseUser(0).Y)

            Dim L2 As Double = Sqrt(delX2 * delX2 + delY2 * delY2)

            ReDim Preserve angsIncl(i), Ldist(i), angsPtI(i)

            angsIncl(i) = Acos((delX1 * delX2 + delY1 * delY2) / L1 / L2) * 180 / PI

            angsPtI(i) = Atan2(delY2 - delY1, delX2 - delX1) * 180 / PI

            If angsPtI(i) < 0.0F Then angsPtI(i) = angsPtI(i) + 360.0

            Ldist(i) = L2

            ListBox1.Items.Add(" pt_ " & i & " ;X= " & Round(ptsPathUser(i).X, 5) & " ;Y= " & Round(ptsPathUser(i).Y, 5) & " ;ldist= " & Round(Ldist(i), 5) & " ;ang= " & Round(angsIncl(i), 5) & " ;angPtI= " & Round(angsPtI(i), 5))

        Next

 

    End Sub

四、    結論及建議

3及圖4分別為SPlineBEZIER曲線利用路徑曲線扁平轉換為線段之成果示圖;圖5及圖6分別為SPlineBEZIER曲線利用圖像掃瞄之成果示圖。兩不同方法會因取點位置不同而座標值會有所差異,如兩者所取位置相同,則利用不同方法會有極相近之計算結果。如欲繪製橢圓(),則可利用畫圖物件畫圖,然後比照前述方法測繪及放樣。圖7為以A(-9.1,-7),B(4,5),C(5,2),D(1,5,-8.7)為包含矩形所繪製之橢圓弧以Flatten所取得的曲線點座標。

 

3 SPline曲線(Flatten)

 

4 BEzier曲線(Flatten)

 

5 SPline曲線(GetPIxel)

 

6 Besier曲線(GetPIxel)

 

7 Ellipse曲線(Flatten)

 

Name(您的大名)
E_MAIL(您的電子信箱)
Comment or Suggestion(您想反應的狀況,建議,或諮詢事項)
首頁


 

 

首頁 | 如何使用Excel試算表作程式資料輸入 | 如何繪製等高線 | 解3D隱函數 | 工程仲裁案例說明 | Spline_Bezier曲線測繪 | VB6工程計算機程式設計 | VB NET工程計算機程式設計 | 如何在VB6中使用Vbscript & Dll | 徐昇多邊形 | 物件導向程式簡介 | 如何在VB6使用VB.net圖案筆刷及顏色表 | VB Net Graphics method(B) | Graphic method in vb net(A)

上次修改此網站的日期: 2018年11月25日