VB NET座標系統簡介

2018年12月02日

首頁 | Function plotting(3D) | Rotation/reflection(3D) | Projection/Transformation(3D) | Contour plotting(3d) | tube plotting(3D) | VB NET座標系統簡介 | Joint line segments

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

 

第二十四章VB Net座標係系統介紹

24.1前言

GDI+ 使用三個座標空間:(1)全局座標(World Coordinate,或稱世界座標)(2)頁面座標(Page Coordinate),和(3)裝置座標(Device Coordinate)。全局座標是用來製作特定繪圖自然模型(實物)的座標,也就是在 .NET Framework 中傳遞到方法的座標。頁面座標則是指繪圖介面 (例如表單或控制項)上使用的座標系統。裝置座標是圖形輸出裝置用座標,也就是User進行繪圖後顯示的實體裝置(例如螢幕或紙張)所使用的座標。User在呼叫Graphics畫線方法DrawLine(Point1,Point2) 時,傳遞至 DrawLine 方法的點在實體世界座標位置 (Point1,Point2)User所指定的全局座標空間。在GDI+要在螢幕上繪製線條之前,系統會先將座標經過轉換成一組序列。一個名為「全局轉換」的轉換會將全局座標轉換為頁面座標,而另一個名為「頁面轉換」的轉換則是將頁面座標轉換為裝置座標。

24.2座標系統

假設您想要使用原點位於表單工作區 (Client Area)中,而非位於左上角的預設(Default)座標系統。 例如,您希望原點位於距離工作區左側100個像素和距離工作區頂端200個像素的位置。下圖將顯示此座標系統。

 

 

 

呼叫 gr.DrawLine(Pens.Blue, 0, 0, 150,80) 前,如將座標原點平移至(100,200) 時,即

執行下列陳述句:

   

Dim gr As Graphics = Me.CreateGraphics

gr.TranslateTransform(100, 200)

gr.DrawLine(Pens.Blue, 0, 0, 150, 80)

 

您會取得下圖中所顯示的線條。

 

 

 

在這三個座標空間中,因為都採用預設的Pixel度量單位,因此線條的端點座標位

置如下:

 

座標系統

直線起點

直線終點

全局座標

(0,0)

(150,80)

頁面座標

(100,200)

(250,280)

裝置座標

(100,200)

(250,280)

 

此處應特別注意是頁面座標空間的原點一律位於工作區的左上角,且度量單位為像素,因此裝置座標和頁面座標是相同的。如果您將度量單位設為像素以外的單位 (

英吋),則裝置座標便與頁面座標不同。將全局座標作頁面座標平移轉換,轉換後的矩陣會存放在 Graphics 類別的 Transform 屬性中。上例中,全局轉換是指在X方向轉換 100 個單位和在 Y 方向轉換 200 個單位。

頁面轉換會將頁面座標對應至裝置座標。Graphics類別提供PageUnit PageScale 屬性,來管理頁面轉換。Graphics 類別也提供兩個唯讀屬性,分別是DpiXDpiY,來檢視顯示裝置每英吋的水平點和垂直點。您可以使用Graphics類別的PageUnit屬性來指定像素以外的度量單位,但不能將PageUnit屬性設定為World,因為這不是實體單位,因此會造

成例外狀況 (Exception)  

下列範例是從(0, 0)(1,1)繪製一條線,其中點(1, 1)是指距離(0, 0)點右邊1英吋和下方1英吋的位置。如果建構畫筆時未指定畫筆寬度,則上述範例將繪製出一條寬為一英

吋的線條。 您可以在 Pen 建構函式的第二個引數中指定畫筆寬度:

 

Dim gr As Graphics = Me.CreateGraphics

gr.Clear(Me.BackColor)

Dim myPen1 As New Pen(Color.Gray, 1 / gr.DpiX)

gr.PageUnit = GraphicsUnit.Inch

gr.DrawLine(New Pen(Color.Red, 1 / gr.DpiX), 0, 0, 1, 1)

gr.Dispose()

 

 

 

 

Dim gr As Graphics = Me.CreateGraphics

gr.Clear(Me.BackColor)

Dim myPen1 As New Pen(Color.Gray, 1 )

gr.PageUnit = GraphicsUnit.Inch

gr.DrawLine(New Pen(Color.Red, 1), 0, 0, 1, 1)

gr.Dispose()

 

 

 

如果假設顯示裝置的水平方向每英吋有96個點,且其垂直方向每英吋有96

點,則上述範例的線條端點會分別在三種座標空間中使用下列座標:

 

 

座標系統

直線起點

直線終點

全局座標

(0,0)

(1,1)

頁面座標

(0,0)

(1,1))

裝置座標

(0,0))

(1*96,1*96)=(96,96)

請注意,由於全局座標空間的原點位於工作區的左上角,因此頁面座標和全局座標是相同的。您可以結合全局和頁面轉換來達到各種效果。例如,假設您想要使用英吋做為度量單位,而且想要您的座標系統的原點位於工作區左邊緣的1.5英吋和工作區頂端的0.75 英吋處。下面的範例將設定Graphics物件的全局轉換和頁面轉換,然後從(0, 0)(1,2)繪製出一條線:

Dim gr As Graphics = Me.CreateGraphics

        gr.Clear(Me.BackColor)

        Dim myPen1 As New Pen(Color.Gray, 1 / gr.DpiY)

        gr.PageUnit = GraphicsUnit.Inch

        gr.DrawLine(myPen1, 1.5F, 0.75F, 1.5F, 4.5F) 'x

        gr.DrawLine(myPen1, 1.5F, 0.75F, 4.0F, 0.75F) 'y

        gr.DrawString("Inch時平移至(1.5,0.75)", myfont, Brushes.Blue, 1.5F, 0.75F)

        gr.TranslateTransform(1.5F, 0.75F)

        Dim myPen2 As New Pen(Color.Red, 1 / gr.DpiY)

        gr.DrawLine(myPen2, 0, 0, 1, 2)

        gr.Dispose()

 

       列圖示將顯示該線條和座標系統。

 

 

如果假設顯示裝置的水平方向每英吋有96個點,且其垂直方向每英吋有96個點,則上述範例的線條結束點會分別在三種座標空間中使用下列座標:

 座標系統

直線起點

直線終點

全局座標

(0,0)

(1,2)

頁面座標

(1.5,0.75)

(2.5,2.75))

裝置座標

(1.5*96,0.75*96)=(144,72))

(2.5*96,2.75*96)=(240,264)

 

24.2座標系統平移及旋轉

VB 6.0 FormPictureBox控制項因有ScaleModeScale等屬性,及Pset()Point()等方法,故利用VB 6.0繪圖及程式碼編寫相當直接簡單容易;而VB NET因不再支持上述屬性及方法,因此User必須另外設法解決。本章,我們將討論有關VB NET如何處理對應於VB 6.0ScaleModeScale等屬性的方法。我們在本書第二十一章21.5.4線性轉換就曾介紹過以矩陣線性轉換改變座標系統的觀念,在GDI+畫圖作業中,全局座標必須轉換成頁面座標,然後頁面座標轉換為設備座標。您可以使用全局轉換來改變座標系統,並可使用區域轉換來旋轉和縮放新座標系統所繪製的物件。假設您想要使用一個座標系統,其

原點距離工作區左邊緣有x個像素和工作區頂端y個像素,如您想使用像素做為度量單

位,其x軸指向右方為正,y軸朝上為正(預設座標系統的y 軸朝下),因此您可以在水平軸進行反射以改變座標系統。Rod Stephens曾利用繪圖物件Graphics座標系統平移及旋轉方式,將Pixel座標系統轉換為使用者座標系統;另外在Visual Basic 2010 Programmers Reference乙書中另介紹以先在使用者座標系統畫圖,然後再平移至Pixel座標系統中的方法。解決Scale屬性的方法,有很多種方式,有興趣的讀者可參考Rod Stephens所編寫與Computer graphics有關著作,或上網搜尋研讀。

前述Rod Stephens有關繪圖物件Graphics座標系統平移及旋轉以改變座標系統的方

法,原程式碼僅適用在表單上繪圖,沒有座標格線,下圖為利用Rod Stephens所編原howto_net_set_scale繪製f(x)=50 + 45 * (Sin(x / 3) ^ 2 + Sin(x / 7)) / 2的成果圖示。

 

 

  經修正改寫後可適用在圖形方塊中畫圖,相關程序碼及繪製f(x)=50 + 45 * (Sin(x /

3) + Sin(x / 7)) / 2執行後成果披露於下供讀者參考:

  

Private m_InverseTransformation As Matrix

  Private canvasX_Left As Single = -100.0 ‘繪圖物件左上角x座標

  Private canvasX_Right As Single = 100.0 ‘繪圖物件右下角x座標

  Private canvasY_Top As Single = 100.0  ‘繪圖物件左上角y座標

' 注意原Pixel座標係朝下為正,此處取正號則改為朝上為正

   Private canvasY_Bottom As Single = -100.0‘繪圖物件右下角y座標

    Private xStep As Single = (canvasX_Right - canvasX_Left) / 20.0 ‘座標格線間距

Private yStep As Single = (canvasY_Top - canvasY_Bottom) / 20.0

    ' 畫圖物件轉換使其與使用者座標吻合.

 

    Private Sub SetScale(ByVal gr As Graphics, ByVal graphicWid As Single, ByVal    graphicHt As Single, ByVal canvasX_left As Single, ByVal canvasX_Right As Single, ByVal canvasY_Top As Single, ByVal canvasY_Bottom As Single)

        gr.ResetTransform()

        ' 設定視埠(viewport)寬、高及繪圖物件尺寸

        Dim bounds As RectangleF = gr.ClipBounds

        gr.ScaleTransform( _

            graphicWid / (canvasX_Right - canvasX_left), _

            graphicHt / (canvasY_Bottom - canvasY_Top))

 

        ' 平移(canvasX_left, canvasY_Top) 至繪圖物件中心.

        gr.TranslateTransform(-canvasX_left, -canvasY_Top)

    End Sub

 

    Public Sub DrawAxials(ByVal Gr As Graphics, ByVal myCanvas As System.Object)

        SetScale(Gr, myCanvas.ClientSize.Width, myCanvas.ClientSize.Height, _

        canvasX_left,canvasX_Right, canvasY_Top, canvasY_Bottom)

        ' 儲存逆反矩陣

m_InverseTransformation = Gr.Transform

        m_InverseTransformation.Invert()

        ' 畫座標軸

      

        For x As Single = canvasX_Left To canvasX_Right Step xStep

            If Math.Abs(x) > 0.00001 Then Gr.DrawLine(New Pen(Color.Gray, 0), x, canvasY_Bottom, x, canvasY_Top)   ‘畫y軸格線

         Next x

‘畫座標y

        Gr.DrawLine(New Pen(Color.Red, 0), canvasX_left, 0, canvasX_Right, 0)

        For y As Single = canvasY_Bottom To canvasY_Top Step yStep

            If Math.Abs(y) > 0.00001 Then Gr.DrawLine(New Pen(Color.Gray, 0), canvasX_Left, y, canvasX_Right, y) ‘畫x軸格線

        Next y

        Gr.DrawLine(New Pen(Color.Red, 0), 0, canvasY_Bottom, 0, canvasY_Top)  畫座y標軸

'注意文字會顛倒

        For x As Single = canvasX_Left To canvasX_Right Step xStep

            If (x Mod 40) = 0 Then Gr.DrawString(x.ToString, New Font("New Times Roman", 6), Brushes.Blue, x, -5)

        Next x

    End Sub

 

 

上述作業方式,將使用者繪圖物件尺寸當做變數,只要改變尺寸變數及正負號,即可符合使用者需求,使用上尚稱簡便。唯美中不足的為其是將繪圖物件上下顛倒,因此如在轉換後繪圖物件書寫文字,所顯示者也是上下顛倒;另一方面,如需要取得繪圖物件的Pixel座標(如使用FloodFill方法或GetPixelSetPixel),則又需要將User座標值換算成Pixel座標值。為解決上述問題,筆者想出一種姑且稱之為『雙座標系統』的變通辦法以

解決上述問題。

24.3雙座標系統

雙座標系統的原理,相當直接及簡單。因為VB NET預設的座標系統,左上角為(0,0),右下角為(Width,Height),如對應之User座標假設為(xLeft, yTop)(xRight, yBottom),因兩者為線性比例關係,如令Pixel座標上任意點為(xp,yp)對應之使用者座標為(xg,yg),則由

 

xp=a+b*xg

yp=a+b*yp

xg=c+d*xp

yg=c+d*yp

 

可分別解出

 

xp = (xRight - xLeft) / Width * xp + xLeft

yp = (yBottom - yTop) / Height * yp + yTop

xp = xg *Width / (xRight - xLeft) xLeft*Width / (xRight - xLeft)

yp = yg * Height / (yBottom - yTop) - yTop * Height / (yBottom - yTop)

 

上述關係為雙座標系統的基本方程式,應用上述公式時,引數應該使用浮點數,尤其是使用者座標值轉換為Pixel座標值,更應採用雙精準浮點數,否則因計算誤差累積關係,致繪製的圖形產生非常明顯的鋸齒狀(Alias)現象。下兩圖為使用者座標值轉換為Pixel座標值時,引數使用整數及雙精準浮點數後所繪製繪製的x=t,y=sin(t)t=-10.0~10.0參數

圖的不同成果圖示。

 

 

下面為雙座標系統座標轉換的程序碼。

 

Private Function XYFromPixel(ByVal canvas As PictureBox, ByVal xLeft As Single, ByVal xRight As Single, ByVal yTop As Single, ByVal yBottom As Single, _

     ByVal xp As Integer, ByVal yp As Integer) As PointF

        XYFromPixel.X = CSng(xRight - xLeft) / CSng(canvas.ClientSize.Width) * xp +

xLeft

        XYFromPixel.Y = CSng(yBottom - yTop) / CSng(canvas.ClientSize.Height) * yp + yTop

End Function

 

 Private Function XYFromUser(ByVal canvas As PictureBox, ByVal xLeft As Double, ByVal xRight As Double, ByVal yTop As Double, ByVal yBottom As Double, _

     ByVal xg As Double, ByVal yg As Double) As PointF

        XYFromUser.X = xg * CSng(canvas.ClientSize.Width) / (xRight - xLeft) - xLeft * CSng(canvas.ClientSize.Width) / (xRight - xLeft)

        XYFromUser.Y = yg * CSng(canvas.ClientSize.Height) / (yBottom - yTop) - yTop * CSng(canvas.ClientSize.Height) / (yBottom - yTop)

End Function

 

下面則為雙座標系統座標轉換,利用使用者座標轉換為Pixel座標繪製座標軸及格線

的程式片段碼內容(完整版本請參考附錄光碟),供讀者參考。

 

Private Sub 畫副格線(ByVal Nsecx As Integer, ByVal Nsecy As Integer)

        Dim i As Integer, Xi As Double, Yi As Double

        Dim XY1 As PointF, XY2 As PointF, radiT As Single

        gp = PicGraph.CreateGraphics

        Select Case gl座標類型 '0為矩形;1為極座標

            Case 0   '矩形座標

                Select Case glx弳度

                    Case False

 

                        For i = 1 To Nsecx * 4

                            Xi = 0 + (i - 1) * gl副格間距x

                            XY1 = XYFromUser(PicGraph, glXmin, glXmax, glYmax, glYmin, Xi, -200)

                            XY2 = XYFromUser(PicGraph, glXmin, glXmax, glYmax, glYmin, Xi, 200)

                            gp.DrawLine(Pens.Gray, XY1, XY2)

                        Next i

                        For i = 1 To Nsecx * 4

                            Xi = 0 - (i - 1) * gl副格間距x

                            XY1 = XYFromUser(PicGraph, glXmin, glXmax, glYmax, glYmin, Xi, -200)

                            XY2 = XYFromUser(PicGraph, glXmin, glXmax, glYmax, glYmin, Xi, 200)

                            gp.DrawLine(Pens.Gray, XY1, XY2)

                        Next i

 

                    Case True

                        For i = 1 To Nsecx * 4

                            Xi = 0 + (i - 1) * gl副格間距x * PI / 2

                            XY1 = XYFromUser(PicGraph, glXmin, glXmax, glYmax, glYmin, Xi, -200)

                            XY2 = XYFromUser(PicGraph, glXmin, glXmax, glYmax, glYmin, Xi, 200)

                            gp.DrawLine(Pens.Gray, XY1, XY2)

                        Next i

                        For i = 1 To Nsecx * 4

                            Xi = 0 - (i - 1) * gl副格間距x * PI / 2

                            XY1 = XYFromUser(PicGraph, glXmin, glXmax, glYmax, glYmin, Xi, -200)

                            XY2 = XYFromUser(PicGraph, glXmin, glXmax, glYmax, glYmin, Xi, 200)

                            gp.DrawLine(Pens.Gray, XY1, XY2)

                        Next i

                End Select

 

                Select Case gly弳度

 

                    .

                    .

                    .

        End Select

        gp.Dispose()

End Sub

 

Private Function XYFromPixel(ByVal canvas As Object, ByVal xLeft As Single, ByVal xRight As Single, ByVal yTop As Single, ByVal yBottom As Single, _

     ByVal xp As Integer, ByVal yp As Integer) As PointF

        XYFromPixel.X = CSng(xRight - xLeft) / CSng(canvas.ClientSize.Width) * xp + xLeft

        XYFromPixel.Y = CSng(yBottom - yTop) / CSng(canvas.ClientSize.Height) * yp + yTop

    End Function

    Private Function XYFromUser(ByVal canvas As Object, ByVal xLeft As Double, ByVal xRight As Double, ByVal yTop As Double, ByVal yBottom As Double, _

     ByVal xg As Double, ByVal yg As Double) As PointF

        XYFromUser.X = xg * CSng(canvas.ClientSize.Width) / (xRight - xLeft) - xLeft * CSng(canvas.ClientSize.Width) / (xRight - xLeft)

        XYFromUser.Y = yg * CSng(canvas.ClientSize.Height) / (yBottom - yTop) - yTop * CSng(canvas.ClientSize.Height) / (yBottom - yTop)

    End Function

 

Private Sub drawPolar(ByRef expression As String, ByRef pStart As Double, ByRef pEnd As Double)

        Dim pts() As PointF

        Dim expression1 As String

        Dim i As Double

        Dim l As Double

        Dim myError, lasterror As Boolean

        Dim x1, y1 As Double, x2, y2 As Double

        On Error GoTo errh

        expression1 = EditedEqu(expression)

        Init0(expression1)

        gp = PicGraph.CreateGraphics

        'Call DrawAxials(gl座標類型, gp, PicGraph)

        Dim tstep As Single = 0.05

        Dim nstep As Integer = (pEnd - pStart) / tstep + 1

        lasterror = False

        Dim npt As Long

        lasterror = False

        For n As Integer = 0 To nstep

            i = pStart + n * tstep

            l = GetVal0(0, 0, i)

            x1 = l * Cos(i)

            y1 = l * Sin(i)

            ReDim Preserve pts(npt)

            myError = False

            pts(npt).X = x1

            pts(npt).Y = y1

            x2 = XYFromUser(PicGraph, -10, 10, 10, -10, x1, y1).X

            y2 = XYFromUser(PicGraph, -10, 10, 10, -10, x1, y1).Y

            pts(npt).X = x2

            pts(npt).Y = y2

            npt = npt + 1

        Next n

        gp.DrawCurve(New Pen(Color.Blue, 0), pts)

        gp.Dispose()

errh:

        myError = True

        Resume Next

    End Sub

下圖為利用雙座標系統所繪製繪製的x=t,y=sin(t)t=-10.0~10.0參數

圖的成果圖示。

 

 

下面為利用上面程式所繪製的x=t,y=sin(t)t=-10~t=10的參數圖,及

r(t)=2.5*(exp(cos(t))-2*cos(4*t)+sin(t/4)^3t=-10~t=10的極座標參數圖。

 

 

 

 

24.5如何繪製實體物件

Page空間中,允許的真正現實世界中的標準有:

1. World:指定全局座標顯示的測量單位(常數=0)

2. Display:指定顯示的測量單位,視窗顯示會使用像素單位,印表機則使用1/100英吋

(常數=1)

3. Pixel:指定裝置像素為顯示的測量單位 PageUnit的預設值(常數=2)

4. Document:指定文件單位(1/300英吋)顯示的測量單位(常數=3)

5. Inch:指定顯示的測量單位為英吋(常數=4)

6. Point:指定印表機的點(1/72英吋)顯示的測量單位(常數=5)

7. Millimeter:指定顯示的測量單位為公釐(常數=6)


下面的演示程式,就是以現實世界座標所繪製者。

Private Sub DrawRectangles(ByVal gp As Graphics)

        gp.PageUnit = GraphicsUnit.Pixel

        Dim myPen As New Pen(Color.Magenta, 3) 'this pen will be 3 pixels wide

 

        gp.DrawRectangle(myPen, 15, 15, 150, 120) 'draw a rectangle in Pixel mode (the default)

        gp.DrawString("in Pixel mode(the default),(15, 15, 150, 120)", New Font("新細明體", 12), New SolidBrush(Color.Red), 15, 15)

        myPen.Dispose()

 

        gp.PageUnit = GraphicsUnit.Inch

        myPen = New Pen(Color.Blue, 0.05F) 'this pen will be 1/20th of an inch wide

 

        gp.DrawRectangle(myPen, 0.1F, 2.5F, 3.5F, 1.0F) ' draw a rectangle 4" by 1"

        gp.DrawString("draw a rectangle 4in by 1in,(0.1F, 2.5F, 3.5F, 1.0F)", New Font("新細明體", 12), New SolidBrush(Color.Red), 0.1F, 2.5F)

        myPen.Dispose()

 

        gp.PageUnit = GraphicsUnit.Millimeter

        myPen = New Pen(Color.Black, 1.2F) 'this pen will be 1 millimeter wide

 

        gp.DrawRectangle(myPen, 4.0F, 95.5F, 40.0F, 35.0F) ' draw a rectangle 80 by 60 mm

        gp.DrawString("draw a rectangle 40 by 65 mm,(4.0F, 95.5F, 40.0F, 35.0F)", New Font("新細明體", 12), New SolidBrush(Color.Red), 4.0F, 95.5F)

 

        myPen.Dispose()

        gp.Dispose()

    End Sub

 

 

 

 

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