平面上已知起點(diǎn)、終點(diǎn)、圓心、順逆方向畫一段圓弧

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};
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容