Qt 求圓和橢圓上任意角度點的座標

一杯清酒邀明月發表於2024-04-02

0.圓相關公式

在笛卡爾座標系上,一個標準的圓是這樣的:

已知圓心座標 (x0,y0),半徑 R,角度 a,則圓邊上點(x',y')的座標為:

C 的三角函式引數為弧度,轉換如下:

角度轉弧度: Qt 求圓和橢圓上任意角度點的座標

弧度轉角度:Qt 求圓和橢圓上任意角度點的座標

但我們知道,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 旋轉的方式繪製,但是這樣文字也旋轉了)

相關文章