QT 利用QPainter繪圖的座標系轉換
Qt繪製圖形時,少不了座標計算,那麼如何更好更快地計算出座標呢?現在來分析一下。
如下圖所示,原來座標系是(0,0)X axis右Y axis下
現在想把它變成,座標在視窗中間,X右,Y上,標準的數學中的座標系。
Painter.setWindow(x,y,width().height());
用setWindow這個函式,表示什麼意思。前兩個引數左上角位置,後兩個引數寬高。
這個是原本的座標系,要把他變成下面這個
怎麼算呢,可以把變換好的座標系在紙上畫出來,左上角座標傳給前兩個引數,後兩個引數寬高可以算出來。
Width= 50-(-50) = 100; Height = -50-50 = -100;
那麼就是painter.setWindow(-50,50,100,-100);
一般painter.setWindow(-width()/2,height()/2,width(),-height());就可以達到目的了
World Corrdinates |
===> |
Window Coordinates |
===> |
Device Coordinates |
(邏輯座標) |
世界變換 |
中間態座標 |
視窗視口變換 |
(物理座標) |
在預設情況下,3個座標系是一致的。
世界變換
世界變換直接相關的函式:
Qpainter::setWorldMatrixEnabled |
啟用,禁用世界變換 |
Qpainter:;setWorldTransform |
設定世界變換 |
Qpainter::worldTransform |
獲取當前 |
Qpainter::resetTransform |
重置Qtransform() |
4個常用的函式:Qpainter::scale Qpainter::shear Qpainter::rotate Qpainter::translate
注:它們通過直接呼叫的Qtransform的相應成員直接修改世界變換
Void Qpainter::scale(qreal sx,qreal sy) { ...... d->state->worldMatrix.scale(sx,sy); ....... }
世界變換的兩個馬甲:Qpainter::setTransform Qpainter::transform
Void Qpainter::setTransform(const Qtransfrom &transform,bool combine)
{
setWorldTransform(transform,combine);
}
廢棄的函式(從Qt4.3開始,Qtransform取代了Qmatrix的位置,下列函式已不建議使用):
Qpainter::setWorldMatrix Qpainter::worldMatrix
視窗視口變換
直接相關:
Qpainter::setViewTransformEnabled |
啟用,禁用視口變換 |
Qpainter::viewTransformEnabled |
|
Qpainter::setViewport |
設定 視口(物理座標) |
Qpainter::setWindow |
設定 視窗(與視口是同一矩形,中間太座標) |
該變換是簡單的線性變換。
複合變換
視窗視口變換和世界變換的複合:Qpainter::combinedTransform
Qtransform Qpainter::combinedTransform() const
{
Q_D(const QPainter);
Return d->state->worldMatrix* d->viewTransform();
}
典型應用:對滑鼠事件的響應中,講座標從物理座標變換Qpainter需要的邏輯座標
仿射變換、透射變換
Qt4.3(包括)之前的Qmatrix只支援仿射變換(Affine transformation)
平移(Translation) 縮放(Scale) 旋轉(Rotation) 剪下(Shear)
Qtransform支援透射變換(perspective transformation).
M11 |
M12 |
M13 |
M21 |
M22 |
M23 |
M31 dx |
M32 dy |
M33 |
變換關係:
X’=m11*x + m21*y +dx
Y’=m22*y + m12*x +dy
if(is not affine){
w’=m13*x + m23*y + m33
x’/=w’
y’/=w’
}
相關(射影幾何學,仿射幾何學,歐式幾何學)
Qt的座標系統常用的幾個函式:
translate()函式,進行平移變換;scale()函式,進行比例變換;rotate()函式,進行旋轉變換;shear()函式,進行扭曲變換。
translate()函式,進行平移變換;scale()函式,進行比例變換;rotate()函式,進行旋轉變換;shear()函式,進行扭曲變換。最後介紹兩個有用的函式save()和restore(),利用它們來儲存和彈出座標系的狀態,從而實現快速利用幾個變換來繪圖。一、座標系簡介:
Qt中每一個視窗都有一個座標系,預設的,視窗左上角為座標原點,然後水平向右依次增大,水平向左依次減小,垂直向下依次增大,垂直向上依次減小。原點即為(0,0)點,然後以畫素為單位增減。
例如:
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush(Qt::red);
painter.drawRect(0,0,100,100);
painter.setBrush(Qt::yellow);
painter.drawRect(-50,-50,100,100);
}
我們先在原點(0,0)繪製了一個長寬都是100畫素的紅色矩形,又在(-50,-50)點繪製了一個同樣大小的黃色矩形。可以看到,我們只能看到黃色矩形的一部分。效果如下圖。二、座標系變換:
座標系變換是利用變換矩陣來進行的,我們可以利用QTransform類來設定變換矩陣,因為一般我們不需要進行更改,所以這裡不在涉及。下面我們只是對座標系的平移,縮放,旋轉,扭曲等應用進行介紹。
1.利用translate()函式進行平移變換
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush(Qt::yellow);
painter.drawRect(0,0,50,50);
painter.translate(100,100); //將點(100,100)設為原點
painter.setBrush(Qt::red);
painter.drawRect(0,0,50,50);
painter.translate(-100,-100);
painter.drawLine(0,0,20,20);
}
效果如下:這裡將(100,100)點作為了原點,所以此時(100,100)就是(0,0)點,以前的(0,0)點就是
(-100,-100)點。要想使原來的(0,0)點重新成為原點,就是將(-100,-100)設為原點。
2.利用scale()函式進行比例變換,實現縮放效果
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush(Qt::yellow);
painter.drawRect(0,0,100,100);
painter.scale(2,2); //放大兩倍
painter.setBrush(Qt::red);
painter.drawRect(50,50,50,50);
}
效果如下:可以看到,painter.scale(2,2),是將橫縱座標都擴大了兩倍,現在的(50,50)點就相當於以前的(100,100)點。
3.利用shear()函式就行扭曲變換
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush(Qt::yellow);
painter.drawRect(0,0,50,50);
painter.shear(0,1); //縱向扭曲變形
painter.setBrush(Qt::red);
painter.drawRect(50,0,50,50);
}
效果如下:這裡,painter.shear(0,1),是對縱向進行扭曲,0表示不扭曲,當將第一個0更改時就會對橫行進行扭曲,關於扭曲變換到底是什麼效果,你觀察一下是很容易發現的。
4.利用rotate()函式進行比例變換,實現縮放效果
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawLine(0,0,100,0);
painter.rotate(30); //以原點為中心,順時針旋轉30度
painter.drawLine(0,0,100,0);
painter.translate(100,100);
painter.rotate(30);
painter.drawLine(0,0,100,0);
}
效果如下:因為預設的rotate()函式是以原點為中心進行順時針旋轉的,所以我們要想使其以其他點為中心進行旋轉,就要先進行原點的變換。這裡的painter.translate(100,100)將(100,100)設定為新的原點,想讓直線以其為中心進行旋轉,可是你已經發現效果並非如此。是什麼原因呢?我們新增一條語句,如下:
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawLine(0,0,100,0);
painter.rotate(30); //以原點為中心,順時針旋轉30度
painter.drawLine(0,0,100,0);
painter.rotate(-30);
painter.translate(100,100);
painter.rotate(30);
painter.drawLine(0,0,100,0);
}
效果如下:這時就是我們想要的效果了。我們加的一句程式碼為painter.rotate(-30),這是因為前面已經將座標旋轉了30度,我們需要將其再旋轉回去,才能是以前正常的座標系統。不光這個函式如此,這裡介紹的這幾個函式均如此,所以很容易出錯。下面我們將利用兩個函式來很好的解決這個問題。
三、座標系狀態的保護
我們可以先利用save()函式來儲存座標系現在的狀態,然後進行變換操作,操作完之後,再用restore()函式將以前的座標系狀態恢復,其實就是一個入棧和出棧的操作。
例如:
void Dialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.save(); //儲存座標系狀態
painter.translate(100,100);
painter.drawLine(0,0,50,50);
painter.restore(); //恢復以前的座標系狀態
painter.drawLine(0,0,50,50);
}
效果如下:利用好這兩個函式,可以實現快速的座標系切換,繪製出不同的圖形。