1. 程式人生 > >Qt座標變換原理之視窗/視口變換原理

Qt座標變換原理之視窗/視口變換原理

Qt座標變換原理之視窗/視口變換原理

若對C++語法不熟悉,建議參閱《C++語法詳解》一書,電子工業出版社出版,該書語法示例短小精悍,對查閱C++知識點相當方便,並對語法原理進行了透徹、深入詳細的講解。

需要用到的QPainter類中的函式如下
1)、QTransform combinedTransform() const; //返回當前視窗/視口和世界變換的變換矩陣組合。
2)、void setViewTransformEnabled(bool enable); //若enable為true,則啟用視口的轉換,否則禁用。
bool viewTransformEnabled() const; //若視口轉換已啟用,則返回true。
3)、QRect viewport() const; //返回視口矩形
QRect window() const; //返回視窗矩形
4)、void setViewport(const QRect &rectangle);
void setViewport(int x, int y, int width, int height); //設定視口矩形,並啟用視口轉換。
5)、void setWindow(const QRect &rectangle);
void setWindow(int x, int y, int width, int height); //設定視窗矩形,並啟用視口轉換。
1、理解“邏輯”
“邏輯”一詞具有“理論上的”意思,說簡單一點,就是“假想的”
2、從圖形繪製的方向理解視窗和視口(原理見圖12-60)
在這裡插入圖片描述


視窗就是一個邏輯上的(假想的)矩形,圖形在這個假想的矩形上繪製,繪製完之後再把圖形對映到視口上,視口通常是與需要顯示的裝置相關聯的(即繪製裝置,比如QWidget部件,顯示屏等),然後圖形才能顯示出來。
3、從圖形顯示方向理解視窗和視口(見圖12-61)
在這裡插入圖片描述
假設需要顯示圖12-61中陰影部分的圖形,則,首先把圖形對映到視窗,此時可對圖形進行旋轉、縮放、平移等操作,然後把超出視窗之外的圖形裁剪掉(這就是為什麼視窗全稱為裁剪視窗的原因),然後把圖形對映到視口顯示出來,若把視口理解為我們的眼睛,則視口和視窗的概念就很容易理解了。視窗的所有內容只有完全對映到了視口的範圍內,才能被完全顯示,一個視窗的內容可以同時對映到多個視口,即可以從不同的視口去觀察視窗中的內容,在視窗到視口的對映過程中,還可對圖形進行縮放、平移等簡單變換。
注意:此處講解的只是一種理論模型,實際實現時是否會裁剪圖形,以及座標變換是在視窗進行還是在視口進行,則視實際使用的繪圖軟體而不同。
4、視窗/視口主要作用
繪製裝置有以畫素為單位的(比如顯示器),也有以點為單位的(比如印表機),使用視窗/視口機制,可使繪圖與繪製裝置相互獨立,我們只需在視窗繪圖,而不必關心底層繪製裝置到底是什麼(即,不需關心底層裝置究竟使用的什麼單位等)。因為圖形在視窗中繪製,因此視窗是來自真實世界的。
5、視窗/視口變換公式的推導
因為計算機中圖形的繪製和顯示通常都是位於矩形區域之內的,因此視窗/視口變換就是矩形到矩形的變換。
視窗/視口變換原理(見圖12-62):在視窗中被對映的點(x,y)與原點所圍成的矩形區域,與被對映後在視口中的點(x’,y’)與原點所圍成的矩形區域,其所對應的長度之比,應與視窗和視口所形成的矩形的長度之比相等,對於矩形區域的寬度,其原理相同。
在這裡插入圖片描述

為便於講解引入表12-20所示變數及概念(意義見圖12-62)
在這裡插入圖片描述
根據變換原理,變換後的點(x’,y’)可由如下公式計算得出(參見圖12-62):

    ①、(x'-Vox)/(x-Wox)=Vex/Wex     (y'-Voy)/(y-Woy)=Vey/Wey     				(24)
②、公式24移項後得到
        x'=(x-Wox)*Vex/Wex+Vox      y'=(y-Woy)*Vey/Wey+Voy     				(25)
        其中Vex/Wex和Vey/Wey是座標對映時x方向和y方向的縮放係數
③、公式25展開後得到
        x'=x*Vex/Wex+Vox-Wox*Vex/Wex    y'=y*Vey/Wey+Voy-Woy*Vey/Wey   	(26)
④、若令
Vex/Wex=S				Vey/Wey=K		(S和K是縮放係數)			(27)
Vox-Wox*Vex/Wex=m		Voy-Woy*Vey/Wey=n,							(28)
則公式26簡化為:
x'=x*S+m    	y'=y*K+n     (這就是數學上的座標轉換公式)    		(29)

6、理解視窗/視口變換公式
視口中的裝置就是指的實際的繪製裝置,裝置座標和裝置點是不會變的,其位置通常被設定為繪製裝置的左上角,方向是x軸從左向右,y軸從上到下增長。在視口中的座標值x’,y’,Vox,Voy都是相對於裝置座標而言的,因此使用公式計算出來的座標值x’,y’是相對於裝置座標而言的,因為視窗通常來自於現實世界,因此視窗中的座標值是相對於世界座標而言的。
對映公式使用的數值是沒有單位的,不存在某個數表示多少毫米,多少畫素這一說法,計算時只需把相應的數值代入公式就能計算出正確的結果。
7、視窗/視口變換可實現的座標變換
注意:以下的視窗和視口範圍使用的數值並不是真實的數值,因為在座標轉換公式中,使用的是縮放係數(即他們的比值),因此使用真實數值並沒有多大意義。
(1)、相等變換
即x’=x; y’=y的情形,此時視口和視窗的原點重合,視口和視窗的縮放係數為1。即

(Wox,Woy)=(Vox,Voy)=(0,0);    (Wex,Wey)=(Vex,Vey)=(1,1);

(2)、平移變換
即x’=x+m; y’=y+n;的情形,此時視口和視窗的縮放係數為1。即

S=K=Vex/Wex=Vey/Wey=1,  或  (Vex,Vey)=(Wex,Wey)=(1,1);   因此
m=Vox-Wox*Vex/Wex=Vox-Wox;    n=Voy-Woy*Vey/Wey=Voy-Woy;
由此可得出平移公式為:
x'=x+Vox-Wox;  y'=y+Voy-Woy;

(3)、縮放變換
即x’=xS; y’=yK;的情形,此時視窗和視口原點座標為(0,0);
(4)、對稱變換:
若圖形從水平方向翻轉,則x方向的縮放係數S為負數,若圖形從垂直方向翻轉,則y方向的縮放係數K為負數,可見縮放係數的正負,決定著座標軸是否反向。
(5)、以上各種變換的合併變換。

8、視窗/視口資料的單位(邏輯單位與裝置單位)
視窗和視口的資料在數學上來講是沒有什麼實際意義的,資料就是一個數值,但在現實世界中繪製圖形時,需要知道圖形在繪圖區域的具體位置,因此需要知道給定的資料具體表示多少個單位的距離,這樣就需要給資料指定一個單位。比如,視口中的資料以“畫素”為單位,視窗中的資料以“毫米”為單位等。
視口資料的單位:來自視口的資料,一般都以實際的物理單位為單位,比如若視口為顯示屏,若顯示屏以畫素為單位,則資料100表示位於顯示屏100個畫素處的實際位置,若顯示屏以毫米為單位,則資料100表示位於顯示屏100毫米處的實際位置。若視口中資料的意義未確定,則使用“裝置單位”表示視口中資料的單位,比如100,表示100裝置單位。
視窗資料的單位:視窗的資料是實際繪製時希望使用的資料,因實際繪製時可能會使用多種不同的單位,因此在未明確視窗中資料的單位時,使用“邏輯單位”表示視窗資料的單位。比如若想以毫米為單位繪製圖形,則1邏輯單位就是1毫米,若想以0.1毫米的精度繪製圖形,則1邏輯單位就是0.1毫米。
由以上可知,若想讓繪製裝置以實際視窗繪製時使用的邏輯單位為單位來顯示圖形,則在裝置單位和邏輯單位之間需要一個單位轉換,而這種轉換可以使用視窗/視口變換來實現(詳見下文)。
資料的來源:由公式可知,x’,y’,Vex,Vey,Vox,Voy是來自視口的資料,因此這些資料是以裝置單位為單位的,x,y,Wex,Wey,Wox,Woy是來自視窗的資料,這些資料是以邏輯單位為單位的。
9、視口/視窗範圍比Vex/Wex(縮放係數S)與單位轉換
對於縮放係數Vey/Wey=K的原理是相同的,僅以S為例講解。
實際繪製圖形時,我們只需指定繪製時的單位,而不需關心物理裝置究竟使用什麼單位,比如想繪製100毫米長的直線,只需在繪製時直接輸入100就能在物理裝置上顯示100毫米這麼長的直線,而不是顯示100畫素或100點大小這麼長的直線,這就是單位轉換的問題,使用視窗/視口變換就能實現單位轉換。
雖然使用視窗/視口變換也可實現一些簡單的座標變換,但通常使用視窗/視口進行單位轉換以實現在視窗中繪製的圖形與現實世界相同,當然單位轉換也可使用QTransform變換矩陣來完成。
視口/視窗範圍比在資料無任何單位時,就僅僅表示縮放係數,即Vex/Wex = S。
視口/視窗範圍比S的意義一般根據視口所表示的實際意義而定,下面以視窗的資料為未確定的邏輯單位,然後再依視口資料的單位不同,而分別對S的單位進行推導
①、若視口的資料使用畫素作為單位,則視口/視窗範圍比 Vex/Wex=S 表示“1個邏輯單位中的畫素個數”,換句話說,就是1邏輯單位有S個畫素,使用數學形式就是:S 畫素/邏輯單位,即縮放係數S的單位為“畫素/邏輯單位”,比如100邏輯單位就表示有“100S”個畫素
②、同理,若視口的資料使用毫米為單位,則縮放係數S的單位為“毫米/邏輯單位”表示1邏輯單位有S毫米。比如100邏輯單位就表示有100
S 毫米
③、若資料各自擁有單位,則在使用座標轉換公式計算時,需要注意各資料的單位應一致,否則可能會導致不一致的結果。比如,已知縮放係數 S = 0.1mm/邏輯單位,即S = 0.1,若Wox = 0畫素,Vox = 100畫素,x = 100邏輯單位,則按公式

x'=x*Vex/Wex+Vox-Wox*Vex/Wex =x*S+Vox-Wox*S=100*0.1+100 =110

在資料無單位的情況下是正確的,但在各資料有單位時,這是錯誤的,因為最後的結果110,即不能表示110毫米,也不能表示110畫素。上式中可把Vox的值轉換為以毫米為單位,代入公式再進行計算,計算的結果為毫米,也可把x、S、Wox轉換為以畫素為單位進行計算,計算的結果為畫素。在資料有單位時,使用座標轉換公式時的單位一定要一致。在計算機中,畫素可以轉換為毫米、英寸等單位,因此容易出現這種錯誤。
④、由以上討論可知,縮放係數S就是表示的一個邏輯單位所對應的物理單位,使用數學公式就是

Vex/Wex = S 物理單位/邏輯單位

因此在視窗中的1個邏輯單位,對映到視口上時,就以S個物理單位進行顯示,比如,若S = 0.1 mm/邏輯單位,則視窗中的1邏輯單位,將在視口上以實際的0.1mm進行顯示。

10、單位轉換示例
例1:假設想以1邏輯單位表示0.1毫米,螢幕的解析度為1024畫素,螢幕實際長度為376mm,若客戶區的長度為500畫素,試確定視窗範圍的尺寸,注意:螢幕是物理裝置,是以畫素為單位來繪製圖形的。
解:主要應注意單位間的轉換,本示例只計算了X方向的情形,Y方向的情形未予計算。本示例的實際應用示例見示例12.24
方法一:把單位轉換為畫素

由題知,1邏輯單位=0.1mm,即 S = 0.1mm /邏輯單位 
螢幕的實際解析度為:X = 1024/376 = 2.7234 畫素/mm,
把S轉換為以畫素為單位:
S = 0.1 mm/邏輯單位 × 2.7234 畫素/mm = 0.27234 畫素/邏輯單位 ,即1邏輯單位有0.27234個畫素
因此 Vex/Wex = S = 0.27234 畫素/邏輯單位 ,又因Vex = 500 畫素,所以 
Wex = 500/0.27234 = 1836邏輯單位

方法二:把單位轉換為毫米

由方法一知,螢幕實際解析度為2.7234 畫素/mm
客戶區的長度為500畫素,即Vex =500畫素 = 500 / 2.7234 mm= 183.6 mm,
由題知,1邏輯單位=0.1mm/邏輯單位,即 S = 0.1 
因此 Vex/Wex = 183.6 / Wex = S = 0.1 ,推得Wex =1836邏輯單位。		█

示例12.24:使用視窗/視口變換實現以毫米為單位在顯示屏上繪製圖形(結果見圖12-63)
void paintEvent(QPaintEvent *e){
    	QPainter pr(this);
    	QBrush bs(QColor(255,255,1));
//繪製一個與視口一樣大小的矩形,以便於觀察
    	pr.drawRect(100,100,500,500);  
    	QPen pn;
    	pn.setColor(QColor(111,1,1));
    	pn.setWidth(10);
    	pn.setCapStyle(Qt::FlatCap);
    	pr.setPen(pn);
    	pr.setViewport(100,100,500,500);	//設定視口
    	pr.setWindow(0,0,1836,1836);	//設定視窗
//注意:在設定視窗/視口之後,使用drawXXX繪製圖形時使用的座標,都需要使用公式26進行計算。
    	pr.drawLine(0,100,200,100);	}

在這裡插入圖片描述
11、使用視窗/視口變換移動繪製裝置的座標
由以上講解可知,通過計算視窗/視口變換可實現繪製圖形時以毫米、釐米等現實世界的單位為單位,下面再講解一個使用視窗/視口變換實現移動繪製裝置座標的方法
繪製裝置的座標預設位於左上角,但可通過視窗/視口變換在邏輯上來移動該座標系到我們想要的位置。具體方法如下:
使S = K = 1,即Vex = Wex; Vey =Wey; 也就是視窗和視口具有相同的寬度和長度,這意味著視窗和視口擁有相同的長度單位。此時,公式26變為
x’=x+Vox-Wox; y’=y+Voy-Woy;
以上公式是一個座標平移公式,該公式不但是一個平移點的公式,也是平移座標系的公式。因此該公式相當於把座標系向x方向平移了Vox - Wox的距離,向y方向平移了Voy - Woy的距離。在之後繪製圖形時可以以該座標系為參考進行繪製。若繪圖軟體不裁剪圖形,則可簡單的把視窗/視口範圍設定為1,即Vex = Wex = Vey = Wey =1。詳見示例12.25

示例12.25:移動繪製裝置的座標(結果見圖12-64)
void paintEvent(QPaintEvent *e){
    	QPainter pr(this);
QBrush bs(QColor(255,255,1));
//以下繪製的交叉直線用於標示平移後的
//座標位置,以方便觀察
    	pr.drawLine(0,100,555,100);    
pr.drawLine(100,0,100,555);
//設定畫筆
    	QPen pn;    pn.setColor(QColor(111,1,1));
   	pn.setWidth(10);pn.setCapStyle(Qt::FlatCap);
    	pr.setPen(pn);
//設定視窗/視口,以下程式碼相當於把裝置座標移至(100,100)處,本示例也可把視窗/視口範圍設定為1。
    		pr.setViewport(50,50,200,200);   pr.setWindow(-50,-50,200,200);
    	pr.drawLine(0,100,100,100);		pr.drawRect(-50,-50,100,100);}

在這裡插入圖片描述

本文作者:黃邦勇帥(原名:黃勇)