0.圓相關公式
在笛卡爾座標系上,一個標準的圓是這樣的:
已知圓心座標 (x0,y0),半徑 R,角度 a,則圓邊上點(x',y')的座標為:
C 的三角函式引數為弧度,轉換如下:
角度轉弧度:
弧度轉角度:
但我們知道,Qt 繪圖是螢幕座標系,起點在左上角,以右下角為正方向:
(可以把計算後的 y 取反來得到想要的效果)
1 void MainWindow::paintEvent(QPaintEvent *event)
2 {
3 event->accept();
4
5 QPainter painter(this);
6 painter.setPen(QPen(Qt::red,2));
7 //移動座標中心點到視窗中心,預設左上角為起點,往右下為正方向
8 painter.translate(width()/2,height()/2);
9 //畫一個圓,圓心為起點(上一步移動到的正中),半徑100px
10 const int R=100;
11 painter.drawEllipse(QPoint(0,0),R,R);
12 //計算45度角圓邊上的點,角度需要轉換為弧度
13 const double a=qDegreesToRadians((double)45);
14 const int x=0+R*cos(a);
15 const int y=0+R*sin(a);
16 //因為螢幕座標系y軸正方向和笛卡爾座標系相反,所以y取反就是我們要的結果了
17 painter.drawLine(QPoint(0,0),QPoint(x,-y)); //y取反
18 }
執行結果:
1.橢圓相關公式
在笛卡爾座標系上,一個標準的橢圓是這樣的:
已知圓心座標 (x0,y0),橫軸 A(長半軸),豎軸 B(短半軸),角度 a,則圓邊上點(x',y')的座標為:
橢圓半徑:
(可以發現,與普通圓相比,不考慮橢圓角度的話只是半徑變化了,其他部分是一樣的)
1 void MainWindow::paintEvent(QPaintEvent *event)
2 {
3 event->accept();
4
5 QPainter painter(this);
6 painter.setPen(QPen(Qt::red,2));
7 //移動座標中心點到視窗中心,預設左上角為起點,往右下為正方向
8 painter.translate(width()/2,height()/2);
9 //畫一個圓,圓心為起點(上一步移動到的正中),半徑100px
10 const int A=150; //橫軸
11 const int B=100; //豎軸
12 painter.drawEllipse(QPoint(0,0),A,B);
13 //計算45度角圓邊上的點,角度需要轉換為弧度
14 const double a=qDegreesToRadians((double)45);
15 const int R=A*B/sqrt(pow(A*sin(a),2)+pow(B*cos(a),2)); //計算對應角度的半徑
16 const int x=0+R*cos(a);
17 const int y=0+R*sin(a);
18 //因為螢幕座標系y軸正方向和笛卡爾座標系相反,所以y取反就是我們要的結果了
19 painter.drawLine(QPoint(0,0),QPoint(x,-y)); //y取反
20 }
執行結果:
2.旋轉矩陣
上面的角度都是從右側開始,橢圓也是標準橢圓,如果我們想旋轉一定角度來繪製,就需要用到我們的旋轉矩陣了。
(本節主要來自參考部落格)
已知 A(x,y) ,求旋轉 a 角度後的 B(x’,y’) 座標:
公式推導:
根據矩陣乘法計算規則,可以推出:
操作流程:
- 把圖形的各點平移,令旋轉中心平移至原點;
- 乘以旋轉矩陣;
- 再平移至原來的旋轉中心。
我們將上面橢圓的程式碼改一下,逆時針旋轉 45 度:
1
2 void MainWindow::paintEvent(QPaintEvent *event)
3 {
4 event->accept();
5
6 QPainter painter(this);
7 painter.setPen(QPen(Qt::red,2));
8 //移動座標中心點到視窗中心,預設左上角為起點,往右下為正方向
9 painter.translate(width()/2,height()/2);
10 //畫一個圓,圓心為起點(上一步移動到的正中),半徑100px
11 const int A=150; //橫軸
12 const int B=100; //豎軸
13 painter.rotate(-45); //在螢幕座標系,qpainter是順時針轉的
14 painter.drawEllipse(QPoint(0,0),A,B);
15 painter.rotate(45);
16
17 //計算45度角圓邊上的點,角度需要轉換為弧度
18 const double a=qDegreesToRadians((double)45);
19 const int R=A*B/sqrt(pow(A*sin(a),2)+pow(B*cos(a),2)); //計算對應角度的半徑
20 const int x=0+R*cos(a);
21 const int y=0+R*sin(a);
22 //根據旋轉矩陣計算
23 const double a2=qDegreesToRadians((double)45);
24 const int x2=x*cos(a2)-y*sin(a2);
25 const int y2=x*sin(a2)+y*cos(a2);
26 //因為螢幕座標系y軸正方向和笛卡爾座標系相反,所以y取反就是我們要的結果了
27 painter.drawLine(QPoint(0,0),QPoint(x2,-y2)); //y取反
28 }
執行結果:
(第一次計算的角度座標是計算標準橢圓下的位置,第二次是根據橢圓的旋轉角度做旋轉得到的座標)
(本來就有 45 度,從右側逆時針旋轉,再旋轉 45 度就成了垂直向上了)
(雖然線可以直接 QPainter 旋轉的方式繪製,但是這樣文字也旋轉了)