首頁 | 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)
裝置座標
此處應特別注意是頁面座標空間的原點一律位於工作區的左上角,且度量單位為像素,因此裝置座標和頁面座標是相同的。如果您將度量單位設為像素以外的單位 (如
英吋),則裝置座標便與頁面座標不同。將全局座標作頁面座標平移轉換,轉換後的矩陣會存放在 Graphics 類別的 Transform 屬性中。上例中,全局轉換是指在X方向轉換 100 個單位和在 Y 方向轉換 200 個單位。
頁面轉換會將頁面座標對應至裝置座標。Graphics類別提供PageUnit 和PageScale 屬性,來管理頁面轉換。Graphics 類別也提供兩個唯讀屬性,分別是DpiX和DpiY,來檢視顯示裝置每英吋的水平點和垂直點。您可以使用Graphics類別的PageUnit屬性來指定像素以外的度量單位,但不能將PageUnit屬性設定為World,因為這不是實體單位,因此會造
成例外狀況 (Exception)。
下列範例是從(0, 0)到(1,1)繪製一條線,其中點(1, 1)是指距離(0, 0)點右邊1英吋和下方1英吋的位置。如果建構畫筆時未指定畫筆寬度,則上述範例將繪製出一條寬為一英
吋的線條。 您可以在 Pen 建構函式的第二個引數中指定畫筆寬度:
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 myPen1 As New Pen(Color.Gray, 1 )
gr.DrawLine(New Pen(Color.Red, 1), 0, 0, 1, 1)
如果假設顯示裝置的水平方向每英吋有96個點,且其垂直方向每英吋有96個
點,則上述範例的線條端點會分別在三種座標空間中使用下列座標:
(1,1)
(1,1))
(0,0))
(1*96,1*96)=(96,96)
請注意,由於全局座標空間的原點位於工作區的左上角,因此頁面座標和全局座標是相同的。您可以結合全局和頁面轉換來達到各種效果。例如,假設您想要使用英吋做為度量單位,而且想要您的座標系統的原點位於工作區左邊緣的1.5英吋和工作區頂端的0.75 英吋處。下面的範例將設定Graphics物件的全局轉換和頁面轉換,然後從(0, 0)到(1,2)繪製出一條線:
Dim myPen1 As New Pen(Color.Gray, 1 / gr.DpiY)
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)
下列圖示將顯示該線條和座標系統。
如果假設顯示裝置的水平方向每英吋有96個點,且其垂直方向每英吋有96個點,則上述範例的線條結束點會分別在三種座標空間中使用下列座標:
(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 Form及PictureBox控制項因有ScaleMode及Scale等屬性,及Pset()、Point()等方法,故利用VB 6.0繪圖及程式碼編寫相當直接簡單容易;而VB NET因不再支持上述屬性及方法,因此User必須另外設法解決。本章,我們將討論有關VB NET如何處理對應於VB 6.0的ScaleMode及Scale等屬性的方法。我們在本書第二十一章21.5.4線性轉換就曾介紹過以矩陣線性轉換改變座標系統的觀念,在GDI+畫圖作業中,全局座標必須轉換成頁面座標,然後頁面座標轉換為設備座標。您可以使用全局轉換來改變座標系統,並可使用區域轉換來旋轉和縮放新座標系統所繪製的物件。假設您想要使用一個座標系統,其
原點距離工作區左邊緣有x個像素和工作區頂端y個像素,如您想使用像素做為度量單
位,其x軸指向右方為正,y軸朝上為正(預設座標系統的y 軸朝下),因此您可以在水平軸進行反射以改變座標系統。Rod Stephens曾利用繪圖物件Graphics座標系統平移及旋轉方式,將Pixel座標系統轉換為使用者座標系統;另外在Visual Basic 2010 Programmer’s 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標軸
'注意文字會顛倒
If (x Mod 40) = 0 Then Gr.DrawString(x.ToString, New Font("New Times Roman", 6), Brushes.Blue, x, -5)
上述作業方式,將使用者繪圖物件尺寸當做變數,只要改變尺寸變數及正負號,即可符合使用者需求,使用上尚稱簡便。唯美中不足的為其是將繪圖物件上下顛倒,因此如在轉換後繪圖物件書寫文字,所顯示者也是上下顛倒;另一方面,如需要取得繪圖物件的Pixel座標(如使用FloodFill方法或GetPixel或SetPixel時),則又需要將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)
下面則為雙座標系統座標轉換,利用使用者座標轉換為Pixel座標繪製座標軸及格線
的程式片段碼內容(完整版本請參考附錄光碟),供讀者參考。
Private Sub 畫副格線(ByVal Nsec副x As Integer, ByVal Nsec副y 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 Nsec副x * 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
Xi = 0 - (i - 1) * gl副格間距x
Case True
Xi = 0 + (i - 1) * gl副格間距x * PI / 2
Xi = 0 - (i - 1) * gl副格間距x * PI / 2
End Select
Select Case gly弳度
.
gp.Dispose()
Private Function XYFromPixel(ByVal canvas As Object, ByVal xLeft As Single, ByVal xRight As Single, ByVal yTop As Single, ByVal yBottom As Single, _
XYFromPixel.X = CSng(xRight - xLeft) / CSng(canvas.ClientSize.Width) * xp + xLeft
Private Function XYFromUser(ByVal canvas As Object, ByVal xLeft As Double, ByVal xRight As Double, ByVal yTop As Double, ByVal yBottom As Double, _
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)
'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
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)
errh:
myError = True
Resume Next
下圖為利用雙座標系統所繪製繪製的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)^3,t=-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)
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)
Name(您的大名) E_MAIL(您的電子信箱) Comment or Suggestion(您想反應的狀況,建議,或諮詢事項)