1.使用QT5繪畫
- 再Q(mào)t5中畫一個(gè)弧一般使用接口
painter.drawArc(rectangle, startAngle*16, spanAngle*16);-
rectangle為圓弧所對(duì)應(yīng)圓的外接矩陣 -
startAngle為圓弧的起始角 -
spanAngle為圓弧的夾角 - 16個(gè)像素約為1°
2.起始角和夾角的核心算法
- 構(gòu)建兩個(gè)復(fù)數(shù),一個(gè)是起點(diǎn)的復(fù)數(shù),一個(gè)是終點(diǎn)的復(fù)數(shù),兩者相除即得到旋轉(zhuǎn)子的復(fù)數(shù)
- 乘以一個(gè)模為1的復(fù)數(shù)時(shí),不會(huì)導(dǎo)致縮放,只會(huì)產(chǎn)生旋轉(zhuǎn),這樣的復(fù)數(shù)就稱為旋轉(zhuǎn)子(rotor)
- 逆時(shí)針的旋轉(zhuǎn)子為
cos(θ) + sin(θ)i,順時(shí)針的旋轉(zhuǎn)子的共軌復(fù)數(shù)cos(θ) - sin(θ)i - 默認(rèn)逆時(shí)針角度為正,順時(shí)針為負(fù)
- 若span角為0,說明弧是一個(gè)整圓,spanAngle需修正為360°
/// 起點(diǎn)向量復(fù)數(shù)、終點(diǎn)向量復(fù)數(shù)、旋轉(zhuǎn)子
ComplexNum c1(startVector.x(), startVector.y());
ComplexNum c2(endVector.x(), endVector.y());
ComplexNum rotor = c2 / c1;
/// 根據(jù)順逆方向計(jì)算夾角
if (isAcw) {
spanAngle = qAtan2(rotor.B(), rotor.A());
if (spanAngle < 0) {
spanAngle += 2*PI;
}
} else {
spanAngle = -qAtan2(-rotor.B(), rotor.A());
if (spanAngle > 0) {
spanAngle -= 2*PI;
}
}
/// 如果是整圓
if(fabs(spanAngle) < 0.0001) spanAngle = 2 * PI;
/// 計(jì)算起始角
c1.setComplexNumValue(1, 0);
c2.setComplexNumValue(startVector.x(),startVector.y());
rotor = c2 / c1;
startAngle = qAtan2(rotor.B(), rotor.A());

image.png

image.png
3.核心代碼
小程序地址:https://gitee.com/liuwentao1234/drawArc
#include <math.h>
#define PI 3.1415926
typedef struct ArcInfo {
double startAngle; /// 起始角 單位°
double spanAngle; /// 夾角 單位°
QRectF rectangle; /// 外接矩陣
}ArcInfo;
static inline void ToAngle(double& num)
{
num = num / PI * 180;
}
ArcInfo DrawArc(QPointF start, QPointF end, QPointF center, bool isAcw)
{
//計(jì)算畫弧所需的參數(shù)有3個(gè):起始角度、夾角、外切矩形
double startAngle = 0, spanAngle = 0;
QRectF rectangle;
//定義起始向量和終止向量
QPointF startVector = start - center;
QPointF endVector = end - center;
//構(gòu)建兩個(gè)復(fù)數(shù),一個(gè)是起點(diǎn)的復(fù)數(shù),一個(gè)是終點(diǎn)的復(fù)數(shù),兩者相除即得到旋轉(zhuǎn)子的復(fù)數(shù)
//乘以一個(gè)模為1的復(fù)數(shù)時(shí),不會(huì)導(dǎo)致縮放,只會(huì)產(chǎn)生旋轉(zhuǎn),這樣的復(fù)數(shù)就稱為旋轉(zhuǎn)子(rotor)
//逆時(shí)針:*旋轉(zhuǎn)子(cos(θ)+sin(θ)i) 順時(shí)針:*旋轉(zhuǎn)子的共軌復(fù)數(shù)(cos(θ)-sin(θ)i)
//默認(rèn)逆時(shí)針角度為正,順時(shí)針為負(fù)
//若span角為0,說明弧是一個(gè)整圓,spanAngle需修正為360°
ComplexNum c1(startVector.x(), startVector.y());
ComplexNum c2(endVector.x(), endVector.y());
ComplexNum rotor = c2 / c1;
if (isAcw) {
spanAngle = qAtan2(rotor.B(), rotor.A());
if (spanAngle < 0) {
spanAngle += 2*PI;
}
} else {
spanAngle = -qAtan2(-rotor.B(), rotor.A());
if (spanAngle > 0) {
spanAngle -= 2*PI;
}
}
if(fabs(spanAngle) < 0.0001) spanAngle = 2 * PI;
//計(jì)算起始角
c1.setComplexNumValue(1, 0);
c2.setComplexNumValue(startVector.x(),startVector.y());
rotor = c2 / c1;
startAngle = qAtan2(rotor.B(), rotor.A());
//弧度轉(zhuǎn)角度
ToAngle(spanAngle);
ToAngle(startAngle);
ui->span->setText(QString::number(spanAngle));
double r = startVector.manhattanLength();
QPointF upperLeftPointOfRect = QPointF(center.x()-r, -(center.y()+r));
rectangle.setRect(upperLeftPointOfRect.x(), upperLeftPointOfRect.y(), 2 * r, 2 * r);
return {startAngle, spanAngle, rectangle};
}