久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

坐標(biāo)系統(tǒng)與坐標(biāo)變換

 昵稱2005438 2010-07-04

第五章 坐標(biāo)系統(tǒng)與坐標(biāo)變換

by iwaswzq 2005/1/22

經(jīng)常有朋友提問關(guān)于編程過程中遇到的坐標(biāo)變換問題,。我抽了點(diǎn)時(shí)間從msdn摘譯了一些東西,,并加了一些自己的理解,希望能有助于對(duì)程序中坐標(biāo)變換的理解,。鑒于我水平有限,,可能某些概念的理解有些錯(cuò)誤或者解釋不夠準(zhǔn)確,歡迎指正,。不足的地方,,以后有時(shí)間會(huì)繼續(xù)豐富此文。

win32程序使用坐標(biāo)系統(tǒng)之間的變換完成圖形的縮放,、旋轉(zhuǎn),、平移等輸出操作。win32下面總共使用四個(gè)坐標(biāo)空間:世界坐標(biāo)系,、頁面坐標(biāo)系,、設(shè)備坐標(biāo)系和物理坐標(biāo)系(包括客戶區(qū)、桌面或打印紙等),。每個(gè)坐標(biāo)空間都是一個(gè)線性空間,,用兩個(gè)相互垂直的坐標(biāo)軸定位兩維的物體。
我們把改變一個(gè)物體的大小,、方向和形狀的算法稱作“變換”,。一個(gè)圖形物體從一個(gè)坐標(biāo)空間映射到另一個(gè)坐標(biāo)空間的過程就是一個(gè)變換。最終,,物體顯示在一個(gè)物理設(shè)備上,,通常是屏幕或者打印機(jī)。

1,、四個(gè)坐標(biāo)系的定義

坐標(biāo)系  描述
世界坐標(biāo)系 可選,,用于圖形轉(zhuǎn)換的起始坐標(biāo)空間,。最大尺寸是 2^32單位高和 2^32 單位寬。
  支持縮放,、平移,、旋轉(zhuǎn),、變形,、投射等轉(zhuǎn)換操作。

頁面坐標(biāo)系 作為世界坐標(biāo)系之后的第二個(gè)坐標(biāo)系使用,,也可以作為變換的起始坐標(biāo)系,。
  最大尺寸是 2^32單位高和 2^32 單位寬??梢栽O(shè)置映射模式,。

設(shè)備坐標(biāo)系 用于頁面坐標(biāo)系之后。僅僅允許平移操作,。保證設(shè)備坐標(biāo)系的原點(diǎn)位于正確的
  物理設(shè)備空間中合適的位置上,。最大尺寸是2^27單位高和 2^27單位寬。
 
物理坐標(biāo)系 圖形變換后的最終的輸出空間,。通常指程序窗口的客戶區(qū),。也可以是整個(gè)桌面、
  整個(gè)窗口區(qū)域或者打印機(jī),、繪圖儀的某一頁,,取決于程序獲得的DC的句柄。物
  理設(shè)備的尺寸取決于顯卡,、打印機(jī)等的設(shè)置,。


頁面空間和設(shè)備空間一起工作,在這兩個(gè)空間下,,程序可以使用設(shè)備相關(guān)的單位,,例如毫米和英寸。但是在世界坐標(biāo)系和頁面坐標(biāo)系下,,都認(rèn)為是邏輯坐標(biāo)系,,單位是邏輯單位1。

任何程序的繪圖代碼中使用的坐標(biāo)都是從世界坐標(biāo)系開始,,直到物理坐標(biāo)系,,最后得到(看到)輸出結(jié)果。每?jī)蓚€(gè)坐標(biāo)系之間,,系統(tǒng)都采用一種變換方法,,從前面的坐標(biāo)空間復(fù)制(或者映射)一個(gè)矩形區(qū)域到下一個(gè)坐標(biāo)空間。為了便于處理,,如果程序調(diào)用了SetWorldTransform函數(shù),,則這個(gè)映射過程從世界坐標(biāo)系開始,,否則從頁面坐標(biāo)系開始。用圖形表示如下:

+-----------+       +------------+        +------------+       +------------------+
| 繪圖代碼  |       |   (窗口)   |        |   (視口)   |       | (屏幕/打印機(jī)等)  |
| 世界坐標(biāo)系| ---〉 | 頁面坐標(biāo)系 | -----〉| 設(shè)備坐標(biāo)系 | ----〉|   物理坐標(biāo)系     |
|           |       |            |        |            |       |                  |
+-----------+       +------------+        +------------+       +------------------+

舉個(gè)例子說,,如果我們?cè)贒C上面有個(gè)畫線函數(shù):

  MoveTo(hDC, 0, 0);
  LineTo(hDC, 10,10);

則如果我們沒有使用SetWordTransform函數(shù),,可以認(rèn)為我們的畫線操作就是在頁面坐標(biāo)系下面,0,,10都是頁面坐標(biāo)系下的坐標(biāo),。如果我們使用了SetWorldTransform函數(shù),則畫線操作是在世界坐標(biāo)系下面,。頁面坐標(biāo)系中對(duì)應(yīng)的這個(gè)線段是世界坐標(biāo)系下面的線段(0,0) -> (10,10) 通過兩個(gè)坐標(biāo)空間之間的變換矩陣變換得到,,結(jié)果可能還是(0,0)點(diǎn)到(10,10)點(diǎn),也可能是(5,9)點(diǎn)到(-20,14)點(diǎn),,這取決于兩個(gè)坐標(biāo)系之間的變換矩陣,。

同樣道理,對(duì)于上述四個(gè)坐標(biāo)系,,當(dāng)系統(tǒng)從一個(gè)坐標(biāo)系中復(fù)制指定矩形區(qū)域內(nèi)的某個(gè)點(diǎn)到下一個(gè)坐標(biāo)系時(shí),,它使用這兩個(gè)坐標(biāo)系之間的變換算法,根據(jù)點(diǎn)的原坐標(biāo)計(jì)算得到點(diǎn)的像坐標(biāo),。因此,,一個(gè)圖形在不同坐標(biāo)系下,其尺寸,、方向和形狀都可能不同,。注意一點(diǎn),雖然這個(gè)變換是兩個(gè)坐標(biāo)系之間的,,是針對(duì)物體整體而言的變換,,但在系統(tǒng)在操作的時(shí)候,是逐點(diǎn),、逐行操作的,。

雖然很少用到SetWorldTransform函數(shù),但是應(yīng)該掌握最基本的坐標(biāo)系之間的線性變換矩陣,,形式如下:

| eM11 eM12 0 |
| eM21 eM22 0 |   即: x' = x * eM11 + y * eM21 + eDx,
| eDx  eDy  1 |        y' = x * eM12 + y * eM22 + eDy,

函數(shù)采用邏輯單位,,缺省的變換矩陣是單位陣:
|  1   0   0  |
|  0   1   0  |   變換關(guān)系就是 x' = x ;   y' = y ; 相當(dāng)于沒有變換
|  0   0   1  |

四個(gè)eM參數(shù)給出旋轉(zhuǎn)和縮放變換系數(shù),eDx/eDy給出平移變換系數(shù),。

注意SetWorldTransform函數(shù)要求DC的圖形模式是GM_ADVANCED,,可以用SetGraphicsMode設(shè)置,僅Windows NT/ 2000下面支持,。缺省圖形模式是GM_COMPATIBLE,,兼容16位windows,這個(gè)模式下不能使用該函數(shù)。下面簡(jiǎn)單介紹這兩個(gè)坐標(biāo)系之間的變換矩陣,。

2,、世界坐標(biāo)系到頁面坐標(biāo)系的變換

(1)平移。物體上每個(gè)點(diǎn)進(jìn)行水平和垂直的移動(dòng),,eDx 和eDy 參數(shù)分別給出移動(dòng)的尺寸,。具體算法是:

x' = x + Dx
y' = y + Dy
其中 x',y' 是新的坐標(biāo),, x,,y 是源坐標(biāo)。 Dx是水平移動(dòng)距離,,Dy是垂直移動(dòng)距離,。

平移矩陣是:

                      |1   0   0|
|x' y' 1| = |x y 1| * |0   1   0|
                      |Dx  Dy  1|
(2)縮放,。組成物體的每個(gè)水平行或者垂直行進(jìn)行拉伸或者壓縮,。算法公式是:

y' = y * Dy
x' = x * Dx
其中 Dy,Dx是縮放系數(shù),。用矩陣表示為:

|x' y' 1| = |x y 1| * |Dx  0   0|  
                      |0   Dy  0|
                      |0   0   1|

(3)旋轉(zhuǎn),。組成物體的每個(gè)點(diǎn)都相對(duì)于坐標(biāo)原點(diǎn)旋轉(zhuǎn)一個(gè)角度。算法公式是:

x' = (x * cos A) - (y * sin A)
y' = (x * sin A) + (y * cos A)

A表示繞原點(diǎn)逆時(shí)針旋轉(zhuǎn)的角度,,用矩陣表示如下:

|x' y' 1| = |x y 1| * | cos A   sin A   0|
                      |-sin A   cos A   0|
                      |   0       0     1|

(4)變形,。分為水平變形和垂直變形兩種,舉個(gè)例子說,,一個(gè)矩形通過變形成為一個(gè)平行四邊形,。算法公式分別是:

  x' = x + (Sx * y)
  y' = y + (Sy * x)

其中Sx, Sy分別是變形系數(shù)。用矩陣表示為:

|x' y' 1| = |x y 1| * |  1   Sx  0|
                      | Sy    1  0|
                      |  0    0  1|

(5)鏡像映射,。例如水平翻轉(zhuǎn)的公式是:

  x' = –x

用矩陣表示是:|-1    0|
              |0     1|

線性變換可以是上面幾種變換中任意若干種的組合,。最終的變換矩陣是一個(gè)3x3的矩陣??梢哉{(diào)用CombineTransform進(jìn)行兩種變換的組合,,也可以自己按照公式計(jì)算出變換矩陣。

強(qiáng)調(diào)一點(diǎn),,雖然可以通過SetWorldTransform函數(shù)調(diào)用設(shè)置世界坐標(biāo)系到頁面坐標(biāo)系的變換矩陣,,但通常情況下,在我們的程序中,,圖形圖像的變換并不是通過系統(tǒng)來完成的,。而是程序自己完成的,因?yàn)樽约鹤?,可以更加靈活,,容易控制,效率更高,。因此不推薦使用SetWorldTransfor函數(shù),,如果我們要做圖形變換的顯示,,建議自己先用變換算法計(jì)算好,然后直接在頁面坐標(biāo)系下面作圖,。

3,、頁面坐標(biāo)系到設(shè)備坐標(biāo)系之間的變換

這個(gè)變換決定了與特定DC相聯(lián)系的映射模式,影響該DC上的所有圖形輸出,。映射模式本身就是一個(gè)縮放變換,,決定了畫圖操作中一個(gè)單位的尺寸,映射模式也可以用于平移變換,,某些情形下,,映射模式會(huì)改變x,y軸的坐標(biāo)原點(diǎn)。首先來了解幾個(gè)映射模式:

(1)映射模式說明

-------------------------------------------------------------------------
映射模式 描述
-------------------------------------------------------------------------
MM_ANISOTROPIC 每個(gè)頁面空間的單位映射為程序定義的設(shè)備空間的單位,。兩個(gè)坐標(biāo)
  軸的縮放尺寸可以不一致(例如,,一個(gè)世界坐標(biāo)系下面的園在指定
  設(shè)備上可能顯示為橢圓)。坐標(biāo)軸的方向也是程序定義的,。

MM_HIENGLISH 每個(gè)頁面空間的單位映射成設(shè)備空間中的0.001英寸,。x軸向右,y軸向上,。

MM_HIMETRIC 每個(gè)頁面空間的單位映射成設(shè)備空間中的0.01毫米,。x軸向右,y軸向上,。
 
MM_ISOTROPIC  每個(gè)頁面空間的單位映射為程序定義的設(shè)備空間的單位,。兩個(gè)坐標(biāo)軸
  的縮放尺寸一樣。坐標(biāo)軸的方向由程序定義,。

MM_LOENGLISH  每個(gè)頁面空間的單位映射成設(shè)備空間中的0.01英寸,。x軸向右,y軸向上,。

MM_LOMETRIC  每個(gè)頁面空間的單位映射成設(shè)備空間中的0.1毫米,。x軸向右,y軸向上,。

MM_TEXT  每個(gè)頁面空間的單位映射成一個(gè)像素,。就是說無縮放。如果也沒有平移變換,,
  則本映射模式下的頁面空間和物理設(shè)備坐標(biāo)空間等價(jià),。x軸向右,y軸向下,。

MM_TWIPS  每個(gè)頁面空間的單位映射成打印機(jī)點(diǎn)的1/20(1/1440英寸),。x軸向右,y軸向上。
------------------------------------------------------------------------------

要設(shè)置映射模式,,調(diào)用SetMapMode,,要獲得當(dāng)前的映射模式,調(diào)用GetMapMode.

頁面空間到設(shè)備空間的變換涉及到窗口或者視口中的點(diǎn),,從這個(gè)意義上講,,窗口反映了頁面空間中的邏輯坐標(biāo)系統(tǒng),而視口代表設(shè)備空間的設(shè)備坐標(biāo)系統(tǒng),。窗口和視口都包含一個(gè)坐標(biāo)原點(diǎn)和x/y軸,。窗口中使用的參數(shù)是邏輯坐標(biāo),視口中使用的參數(shù)是設(shè)備坐標(biāo)(像素),。系統(tǒng)根據(jù)坐標(biāo)原點(diǎn)生成變換矩陣,。這就意味著,窗口和視口分別負(fù)責(zé)給出從頁面坐標(biāo)空間到設(shè)備坐標(biāo)空間映射變換矩陣的一半?yún)?shù),。

根據(jù)窗口和視口的坐標(biāo)軸尺寸(最大坐標(biāo)值),,可以建立一個(gè)比例或者縮放系數(shù),用于頁面空間到設(shè)備空間的變換,。對(duì)于上面六種預(yù)定一映射模式,,當(dāng)調(diào)用SetMapMode函數(shù)的時(shí)候,坐標(biāo)軸的最大尺寸是由系統(tǒng)設(shè)置的,,無法更改。其他兩種映射模式MM_ISOTROPIC和 MM_ANISOTROPIC下,,需要定義坐標(biāo)軸的最大尺寸,,因此調(diào)用SetMapMode之后,必須調(diào)用SetWindowExtEx和SetViewportExtEx進(jìn)行設(shè)置,。特別是在MM_ISOTROPIC映射模式下,。必須注意先調(diào)用SetWindowExtEx然后調(diào)用SetViewportExtEx。

根據(jù)窗口和視口的坐標(biāo)原點(diǎn),,可以建立頁面空間到設(shè)備空間的線性變換的平移關(guān)系,。通過函數(shù)SetWindowOrgEx和SetViewportOrgEx來設(shè)置原點(diǎn)。坐標(biāo)原點(diǎn)和坐標(biāo)軸的尺寸沒有關(guān)系,,因此任何映射模式下都可以設(shè)置坐標(biāo)原點(diǎn),,改變映射模式也不會(huì)影響當(dāng)前的坐標(biāo)原點(diǎn)。由于這兩個(gè)函數(shù)是相關(guān)的,,所以通常使用一個(gè)即可,,不必兩個(gè)都調(diào)用。記住一點(diǎn),,無論是否調(diào)用這兩個(gè)函數(shù),,設(shè)備坐標(biāo)(0,0)永遠(yuǎn)是左上角。也可以用函數(shù)OffsetWindowOrgEx或者OffsetViewportOrgEx改變坐標(biāo)原點(diǎn)。下面的公式給出了頁面坐標(biāo)空間到設(shè)備坐標(biāo)空間之間的點(diǎn)的映射關(guān)系:

Dx = ((Lx - WOx) * VEx / WEx) + VOx

Dx    設(shè)備空間中的點(diǎn)(或者說單位)
Lx    邏輯單位 x (或者說頁面空間中的單位)
WOx   窗口的 x 原點(diǎn)
VOx   視口中 x 原點(diǎn)
WEx   窗口的 x軸尺寸
VEx   視口的 x軸尺寸

對(duì)于y方向的公式也是一樣的,。

函數(shù)LPtoDP 和 DPtoLP可以用來完成兩個(gè)坐標(biāo)空間點(diǎn)的變換的計(jì)算,。

(2)關(guān)于預(yù)定義映射模式

6種預(yù)定義映射模式中,只有MM_TEXT是設(shè)備相關(guān)的,,其余都是設(shè)備無關(guān)的,。缺省的映射模式是MM_TEXT,即一個(gè)邏輯單位等于一個(gè)像素,。邏輯到設(shè)備的映射僅僅是一個(gè)平移關(guān)系,,和程序設(shè)置的窗口和視口的坐標(biāo)原點(diǎn)有關(guān)。視口和窗口的坐標(biāo)軸尺寸都設(shè)置成1,,從而形成一一映射,。如果程序要顯示準(zhǔn)確的幾何圖形,可以使用MM_LOENGLIST模式,,保證圖形的形狀在任何顯示器和打印機(jī)下面都是一樣的,。如果仍然使用MM_TEXT,則可能在VGA顯示器上面顯示的一個(gè)圓,,到了EGA顯示器下就變成橢圓了,,如果用300dpi的激光打印機(jī)輸出,則結(jié)果是很小的一個(gè)圓,。

(3)自定義映射模式

MM_ISOTROPIC 和 MM_ANISOTROPIC兩個(gè)映射模式用于自定義,。MM_ISOTROPIC可以保證邏輯單位在x和y方向是一致的。MM_ANISOTROPIC下允許設(shè)置成不同,。例如一個(gè) CAD 或者繪圖程序可以用MM_ISOTROPIC模式,,將邏輯單位設(shè)置成工程常用的1/64英寸,代碼如下:

SetMapMode(hDC, MM_ISOTROPIC);
SetWindowExtEx(hDC, 64, 64, NULL);
SetViewportExtEx(hDC, GetDeviceCaps(hDC, LOGPIXELSX),
                      GetDeviceCaps(hDC, LOGPIXELSY), NULL);


4,、設(shè)備坐標(biāo)空間到物理設(shè)備的變換

設(shè)備空間到物理設(shè)備的變換在很多情況下是唯一的,,是由系統(tǒng)控制的,主要的目的是保證設(shè)備坐標(biāo)系的原點(diǎn)準(zhǔn)確映射到物理設(shè)備的合適位置,。比如屏幕上面顯示的某個(gè)程序,,窗口顯示的位置要和窗口矩形在顯存中的位置對(duì)應(yīng)起來,移動(dòng)窗口就是改變這個(gè)控制窗口輸出的矩形在顯存中的起點(diǎn),,反映在顯示器屏幕上面就是程序窗口的移動(dòng),。如果是在打印機(jī)dc上面畫圖,則物理設(shè)備就是紙張了,。這個(gè)變換通常是由系統(tǒng)負(fù)責(zé)控制的,。因此沒有函數(shù)用于設(shè)置這個(gè)變換,也沒有函數(shù)獲取相關(guān)的變換數(shù)據(jù),。

5,、缺省的變換

如果程序創(chuàng)建了一個(gè)DC,,并立刻開始調(diào)用GDI繪圖函數(shù),使用的變換過程就是:

  缺省的頁面空間 -〉設(shè)備空間 -〉客戶區(qū)(物理設(shè)備空間)的變換,。

除非程序調(diào)用SetGraphicsMode 和SetWorldTransform函數(shù),,否則世界坐標(biāo)空間-〉頁面坐標(biāo)空間是單位變換,可以認(rèn)為沒有變換,。

頁面空間-〉設(shè)備空間在MM_TEXT模式下是一一映射,,即給定頁面空間中的一點(diǎn),對(duì)應(yīng)設(shè)備空間中的相同點(diǎn),。前面已經(jīng)指出,,這個(gè)變換不是通過一個(gè)矩陣,而是通過用視口的寬度/窗口的寬度,,視口的高度/窗口的高度兩個(gè)公式來計(jì)算的,。缺省情況下,視口的尺寸是1X1像素,。窗口的尺寸是1X1頁,。

設(shè)備空間-〉物理設(shè)備(客戶區(qū),桌面或者打印機(jī))的變換通常也是一一映射,,即設(shè)備空間中的一點(diǎn)對(duì)應(yīng)客戶區(qū)或者打印機(jī)輸出中的一個(gè)單位,,目的是保證無論程序窗口如何在屏幕上面移動(dòng),最終的輸出始終準(zhǔn)確地反映設(shè)備空間中的圖形,。

注意MM_TEXT模式比較特殊,,它的Y坐標(biāo)軸是向下的,其它映射模式的Y坐標(biāo)軸都是向上的,。

6,、下面用一個(gè)例子考察每個(gè)坐標(biāo)變換函數(shù)的意義。用classwizard生成一個(gè)sdi工程,,視類選擇從CScrollView派生,然后添加如下代碼:

void CSdiscroView::OnDraw(CDC* pDC)
{
 //用綠色填充一個(gè)圓形區(qū)域(中心[200,200],半徑150)
 CRect rect;
 rect.SetRect(50,50,350,350);
 CBrush bru;
 bru.CreateSolidBrush(RGB(0,127,0));
 CBrush *pBrushOld = pDC->SelectObject(&bru);
 pDC->Ellipse(&rect);
 pDC->SelectObject(pBrushOld);
 bru.DeleteObject();
 //輸出坐標(biāo)原點(diǎn)
 pDC->TextOut(0,0,"(0,0)");
 //畫出坐標(biāo)軸
 pDC->MoveTo(0,0);
 pDC->LineTo(500,0); //x軸
 pDC->LineTo(490,5); //箭頭
 pDC->MoveTo(500,0);
 pDC->LineTo(490,-5);

 pDC->MoveTo(0,0);
 pDC->LineTo(0,500); //y軸
 pDC->LineTo(5,490);
 pDC->MoveTo(0,500);
 pDC->LineTo(-5,490);
}

void CSdiscroView::OnInitialUpdate()
{
 CScrollView::OnInitialUpdate();

 CSize sizeTotal;
 //設(shè)置整個(gè)窗口尺寸為1000x1000
 sizeTotal.cx = sizeTotal.cy = 1000;
 SetScrollSizes(MM_TEXT, sizeTotal);
}

實(shí)際上我們所有的畫圖操作都是在世界坐標(biāo)系下面,,由于沒有使用SetWorldTransform,,所以世界坐標(biāo)系和頁面坐標(biāo)系等價(jià),可以認(rèn)為我們的畫圖操作就是在頁面坐標(biāo)系中,。下面分別添加相應(yīng)的函數(shù)調(diào)用,,考察每個(gè)函數(shù)對(duì)輸出的影響。

1,、SetWindowOrg / SetViewportOrg (CDC類成員函數(shù),,對(duì)應(yīng)api函數(shù)SetWindowOrgEx / SetViewportOrgEx

void CSdiscroView::OnDraw(CDC* pDC)
{
 pDC->SetWindowOrg(-100,-50);//或者 pDC->SetViewportOrg(100,50);
 //
  ... //原來的代碼
}

窗口沒有滾動(dòng)之前,視口坐標(biāo)系的原點(diǎn)和窗口坐標(biāo)系的原點(diǎn)重合,,如果滾動(dòng)窗口,,相當(dāng)于改變視口原點(diǎn)的位置,,因此顯示在屏幕上面的部分(視口里面的東西)就發(fā)生變化。現(xiàn)在我們不滾動(dòng)窗口,,而是調(diào)用SetWindowOrg改變窗口原點(diǎn)的坐標(biāo),,看看發(fā)生的變化。

SetWindowOrg(-100,-50)函數(shù)調(diào)用的意思是把窗口(頁面坐標(biāo)系)中的(-100,-50)點(diǎn)設(shè)置成窗口的原點(diǎn),,由于窗口和視口的原點(diǎn)永遠(yuǎn)是重合的,,所以視口的(0,0)點(diǎn)現(xiàn)在就和窗口的(-100,-50)重合,而視口的(0,0)點(diǎn)就是程序客戶區(qū)的左上角,,因此設(shè)置的后果就是:繪圖的輸出向x/y軸的正方向移動(dòng)了,。編譯運(yùn)行以后可以看到,字符串"(0,0)"向右下平移了,,好像向上,、向左滾動(dòng)了窗口一樣。同樣道理,,如果SetWindowOry里面使用的是(100,50),,則效果等同于向下、向右滾動(dòng)了窗口,。
對(duì)應(yīng)圖形如下:
                                                  
                                                    /-----視口的原點(diǎn)
                                        (-100,-50) +-------------+
    /-----視口的原點(diǎn)                               |   (視口)    |
   +-------------+-------+---->頁面坐標(biāo)系x         +    +--------+-----------+----->頁面坐標(biāo)系x
   |(0,0)        |       |                         |    | (0,0)  |           |
   |             |       |                         |    |        |           |
   |   (視口)    |       |                         |    |        |           |
   | (屏幕上的)  |       |                         |    |        |           |
   | (部分    )  |       |   設(shè)置SetWindowOrg后    |    |        |           |
   |             |       |  ---------------------〉+----+--------+           |
   +-------------+       |                              |                    |
   |                     |                              |                    |
   | 整個(gè)窗口比視口大    |                              |    窗口            |
   | 有些部分需要滾動(dòng)    |                              |                    |
   | 才能顯示出來        |                              |                    |
   +---------------------+                              +--------------------+
   |                                                    |
  \/ Y                                                 \/ Y

從效果上SetWindowOrg(-100,-50)和SetViewportOrg(100,50)是等價(jià)的,。但是使用一下就會(huì)發(fā)現(xiàn)SetViewportOrg(100,50)以后如果滾動(dòng)窗口的話,窗口的刷新有些問題,,所以在CScrollView里面用SetWindowOrg比較好,,對(duì)于非滾動(dòng)形式的窗口,使用SetViewportOrg比較直觀一些,。窗口對(duì)應(yīng)的就是頁面坐標(biāo)系,,也就是邏輯坐標(biāo)系,視口對(duì)應(yīng)的是設(shè)備坐標(biāo)系,。

2,、關(guān)于映射模式,上面的例子用的是缺省的映射模式MM_TEXT,,現(xiàn)在改變一下映射模式,,看看有什么變化。去掉設(shè)置原點(diǎn)的代碼,,加上:

void CSdiscroView::OnDraw(CDC* pDC)
{

 pDC->SetMapMode(MM_LOMETRIC);
 //用綠色填充一個(gè)圓形區(qū)域(中心[200,200],半徑150)
... //原來代碼不變
}
運(yùn)行一下看看,,怎么Y坐標(biāo)軸和圓都不見了,原來這個(gè)模式下面,,Y軸是向上的,。把程序里面的Y坐標(biāo)都改成負(fù)值:

void CSdiscroView::OnDraw(CDC* pDC)
{
 pDC->SetMapMode(MM_LOMETRIC);
 //用綠色填充一個(gè)圓形區(qū)域(中心[200,200],半徑150)
 CRect rect;
 rect.SetRect(50,-50,350,-350);
 ...
 //用藍(lán)色輸出坐標(biāo)原點(diǎn)
 pDC->TextOut(0,0,"(0,0)");
 //畫出坐標(biāo)軸
 ...
 pDC->MoveTo(0,0);
 pDC->LineTo(0,-500); //y軸
 pDC->LineTo(5,-490);
 pDC->MoveTo(0,-500);
 pDC->LineTo(-5,-490);
}
運(yùn)行一看,OK都出來了,,但是尺寸比原來小多了,。原來每個(gè)邏輯單位被映射成0.1毫米,。那么圓的直徑是300,應(yīng)該對(duì)應(yīng)30毫米,,用尺子在屏幕上面量一下吧,,幾乎就是30毫米啊:)。不相信,,設(shè)置成MM_HIMETRIC,,天啊,看不到了,,可能太小了,?把圓的半徑加大:

rect.SetRect(50,-50,1350,-1350);

嗯,出來了,,直徑好像是1300*0.01 = 13毫米,。既然這樣MM_HIENGLISH和MM_LOENGLISH以及MM_TWIPS就不測(cè)試了。需要注意一點(diǎn),,SetMapMode函數(shù)調(diào)用后,,僅僅影響后續(xù)的畫圖函數(shù),而它前面的畫圖函數(shù)仍然按照原來的映射模式輸出,。所以同一個(gè)繪圖函數(shù)中,,可以調(diào)用多次SetMapMode改變映射模式,比如繪圖單位在英寸和厘米之間,,繪圖的精度在0.01厘米和0.1厘米之間可以時(shí)刻根據(jù)需要進(jìn)行切換,。

3、SetWindowExt 和 SetViewportExt,。由于它們僅僅在MM_ISOTROPIC 模式下有效,,所以這樣做:

void CSdiscroView::OnDraw(CDC* pDC)
{
 pDC->SetMapMode(MM_ISOTROPIC  );
 CSize sizeOrg = pDC->SetWindowExt(200,100);

 //查看返回值可以發(fā)現(xiàn)SetViewportExt返回的是當(dāng)前屏幕設(shè)置的分辨率1024 x 768,不過y是負(fù)值
        //因?yàn)镸M_ISOTROPIC模式下,,Y軸是向下的,。所以記得所有畫圖代碼中的Y坐標(biāo)用正值!
 sizeOrg =pDC->SetViewportExt(200,100);

 ...//畫圖代碼
}
通過改變兩個(gè)Set函數(shù)中的參數(shù)值,,發(fā)現(xiàn)系統(tǒng)自動(dòng)管理x/y的比率關(guān)系,,使圓形保持正確形狀。而且圖形的大小和參數(shù)有關(guān):
假設(shè)SetWindowExt(xWin,yWin); SetViewportExt(xView,yView);則系統(tǒng)使用xView/xWin , yView/yWin 兩個(gè)比值中較小的一個(gè)作為x/y兩個(gè)方向共同的壓縮比例,。最后圖形的大小僅僅和這個(gè)縮放系數(shù)有關(guān)。如果兩個(gè)系數(shù)都大于1,,則系統(tǒng)使用1:1比例,,并不放大圖形。

4,、DPtoLP 和 LPtoDP ,。這兩個(gè)函數(shù)用于邏輯坐標(biāo)和設(shè)備坐標(biāo)之間的轉(zhuǎn)換,。在MM_TEXT模式下兩個(gè)坐標(biāo)是一樣的。現(xiàn)在設(shè)置成MM_LOMETRIC模式,,看看它們的作用,。

void CSdiscroView::OnDraw(CDC* pDC)
{
 //每個(gè)邏輯單位對(duì)應(yīng)0.1毫米設(shè)備單位
 pDC->SetMapMode(MM_LOMETRIC );

 CPoint p(100,200);
 pDC->DPtoLP(&p,1);
 CString str;
 str.Format(" Use DPtoLP : (100,200) -> (%d,%d)\n",p.x,p.y);
 TRACE(str);

 p.x = 100; p.y = 200;
 pDC->LPtoDP(&p,1);
 str.Format(" Use LPtoDP : (100,200) -> (%d,%d)\n",p.x,p.y);
 TRACE(str);
}

//調(diào)試窗口輸出
Use DPtoLP : (100,200) -> (313,-625)
Use LPtoDP : (100,200) -> (32,-64)

可見設(shè)備坐標(biāo)系中的(100,200)對(duì)應(yīng)的是邏輯坐標(biāo)系(窗口)中的(313,-625)一點(diǎn),邏輯坐標(biāo)系下面的(100,200)對(duì)應(yīng)設(shè)備坐標(biāo)系下面的(32,-64)點(diǎn),。注意這個(gè)變換結(jié)果是設(shè)備相關(guān)的,。對(duì)于不同的dc得到不同的結(jié)果。設(shè)置相同的都用屏幕dc,,用不同計(jì)算機(jī)測(cè)試,,不同的顯示器,不同的顯示模式設(shè)置也會(huì)得到不同的變換結(jié)果,。

這是什么意思呢,?就是說窗口中,邏輯坐標(biāo)(313,-625)在MM_LOMETRIC模式下,,對(duì)應(yīng)設(shè)備坐標(biāo)系中X方向距離原點(diǎn)31.3毫米,,Y方向距離原點(diǎn)62.5毫米的一個(gè)點(diǎn),那個(gè)點(diǎn)在設(shè)備坐標(biāo)系中坐標(biāo)是(100,200),其實(shí)就是MM_TEXT模式下,,邏輯坐標(biāo)系下面的那個(gè)(100,200)點(diǎn),。同樣道理,邏輯坐標(biāo)(100,200)點(diǎn),,映射到設(shè)備坐標(biāo)系中,,是x軸方向距離原點(diǎn)10mm,y軸方向距離原點(diǎn)-20mm的點(diǎn)(注意方向),,那個(gè)點(diǎn)的邏輯坐標(biāo)是(32,-64),。也就是MM_TEXT模式下邏輯坐標(biāo)系中的(32,-64)點(diǎn)。

最后要說明一點(diǎn),,OnEraseBkgnd(CDC* pDC)里面的DC和OnDraw(CDC* pDC)里面的DC有所不同啊,。窗口的滾動(dòng)對(duì)前者沒有影響,也就是說無論窗口如何滾動(dòng),,在OnEraseBkgnd函數(shù)中輸出的東西永遠(yuǎn)在視口固定的位置上,,不受滾動(dòng)影響。所以畫圖的時(shí)候,,不要把背景和前景混淆了,,什么函數(shù)就是干什么工作。

-------------------------------------------------------------
End. iwaswzq 2005/1/22
-------------------------------------------------------------


</PRE>

posted on 2005-06-11 20:37 九月鷹飛 閱讀(4989) 評(píng)論(7)  編輯 收藏


評(píng)論

# re: 坐標(biāo)系統(tǒng)與坐標(biāo)變換 2005-07-13 13:18 ifbb007
好看,,

# re: 坐標(biāo)系統(tǒng)與坐標(biāo)變換 2005-07-23 09:58 lly_blue
在映射模式這方面我遇到了一點(diǎn)麻煩,,還想請(qǐng)教一下。我現(xiàn)在在做打印方面的編程,,如果我在OnPrepareDC()里面設(shè)置成MM_TWIPS的話,,畫圖的時(shí)候總有兩個(gè)虛線框,,并且有時(shí)候位圖不能畫出來,有時(shí)候畫出來也不能再改變位置,;還有,,在這種映射模式下,顯示的位圖跟原位圖顛倒了,,我用的是StretchBlt函數(shù),,就是從內(nèi)存copy到屏幕上,只不過放大了幾倍,,我估計(jì)是跟mm_twips模式下,,y值從上到下是減小的原故,如果是默認(rèn)的mm_text模式就不會(huì)有任何問題了,。我一直為這個(gè)煩惱,,還請(qǐng)幫忙解釋一下,謝謝??!

# re: lly_blue 2005-07-24 21:06 iwaswzq
不清楚你怎么設(shè)置的。如果有時(shí)候位圖不能畫出來,,有時(shí)候能畫出來,,可能就不是映射模式的問題。如果你使用了MM_TWIPS映射模式,,因?yàn)樗膟軸向上,,所以你調(diào)用函數(shù)的時(shí)候,注意高度使用負(fù)值,。


# 說明??! 2005-12-09 11:19 iwaswzq
由于圖形采用的是文本方式,有點(diǎn)亂,。你可以把它復(fù)制下來,,然后貼到記事本里面就看清楚了。

# re: 坐標(biāo)系統(tǒng)與坐標(biāo)變換 2005-12-14 16:41 misslii
我看了你給的http://www./document/viewdoc/?id=1270下了個(gè)代碼,表格能打印出來,但表格中的
數(shù)據(jù)是亂碼?

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多