绘制贝塞尔曲线

时间:2021-07-02 05:48:43

我们在做项目的时候很多时候会绘制曲线,本篇文章介绍一种Qt绘制简单的贝塞尔曲线的方法。

程序的示例效果如下:
绘制贝塞尔曲线
拖拽鼠标的控制点可以移动点的位置,可以随意拖拽出自己想要的曲线。(红色的点为曲线的起始和终止点,蓝色的点为贝塞尔曲线的控制点)

头文件,BezierCurve.h

class BezierCurveWidget : public QWidget
{
    Q_OBJECT

public:
    BezierCurveWidget(QWidget *parent = nullptr);
    ~BezierCurveWidget();

protected:
    virtual void paintEvent(QPaintEvent *event) override;
    virtual void mousePressEvent(QMouseEvent *event) override;
    virtual void mouseReleaseEvent(QMouseEvent *event) override;
    virtual void mouseMoveEvent(QMouseEvent *event) override;

private:
    QPoint m_StartPos;
    QPoint m_EndPos;
    QPoint m_CtrolPos1;
    QPoint m_CtrolPos2;

    bool onClickedStartPos = false;
    bool onClickedEndPos = false;
    bool onClickedCtrol1Pos = false;
    bool onClickedCtrol2Pos = false;

    // 圆的半径
    int m_RInterval = 8;

    // 绘制起始点和控制点
    void drawPoints(QPainter* painter);
    // 根据点获取包围矩形
    QRect getOutSizeRect(QPoint);
    // 绘制连线
    void drawLines(QPainter* painter);
};

源文件,BezierCurve.cpp

BezierCurveWidget::BezierCurveWidget(QWidget *parent)
    :QWidget(parent)
{
    m_StartPos = QPoint(100, 100);
    m_EndPos = QPoint(600, 600);
    m_CtrolPos1 = QPoint(100, 600);
    m_CtrolPos2 = QPoint(600, 100);
}

BezierCurveWidget::~BezierCurveWidget()
{

}

void BezierCurveWidget::paintEvent(QPaintEvent *event)
{
    QPainterPath path;
    path.moveTo(m_StartPos);

    // 添加贝塞尔曲线绘制路径
    path.cubicTo(m_CtrolPos1, m_CtrolPos2, m_EndPos);

    // 设置画笔
    QPainter nPainter(this);
    QPen nPen;
    nPen.setColor(QColor(0, 200, 0));
    nPen.setWidth(4);
    nPainter.setPen(nPen);
    // 设置抗锯齿
    nPainter.setRenderHint(QPainter::Antialiasing, true);

    // 绘制路径
    nPainter.drawPath(path);
    // 绘制起始点和控制点
    drawPoints(&nPainter);
    // 绘制连线
    drawLines(&nPainter);

    return QWidget::paintEvent(event);
}

void BezierCurveWidget::mousePressEvent(QMouseEvent *event)
{
    QPoint nMousePos = event->pos();

    if (getOutSizeRect(m_StartPos).contains(nMousePos))
        onClickedStartPos = true;
    else if (getOutSizeRect(m_EndPos).contains(nMousePos))
        onClickedEndPos = true;
    else if (getOutSizeRect(m_CtrolPos1).contains(nMousePos))
        onClickedCtrol1Pos = true;
    else if (getOutSizeRect(m_CtrolPos2).contains(nMousePos))
        onClickedCtrol2Pos = true;

    return QWidget::mousePressEvent(event);
}

void BezierCurveWidget::mouseReleaseEvent(QMouseEvent *event)
{
    onClickedStartPos = false;
    onClickedEndPos = false;
    onClickedCtrol1Pos = false;
    onClickedCtrol2Pos = false;

    return QWidget::mouseReleaseEvent(event);
}

void BezierCurveWidget::mouseMoveEvent(QMouseEvent *event)
{
    QPoint nMousePos = event->pos();

    if (onClickedStartPos)
        m_StartPos = nMousePos;
    else if (onClickedEndPos)
        m_EndPos = nMousePos;
    else if (onClickedCtrol1Pos)
        m_CtrolPos1 = nMousePos;
    else if (onClickedCtrol2Pos)
        m_CtrolPos2 = nMousePos;
    this->repaint();

    return QWidget::mouseMoveEvent(event);
}

void BezierCurveWidget::drawPoints(QPainter* painter)
{
    painter->save();

    // 红色圆圈绘制起始点
    painter->setPen(QPen(QColor(255, 0, 0)));
    painter->drawEllipse(m_StartPos, m_RInterval, m_RInterval);
    painter->drawEllipse(m_EndPos, m_RInterval, m_RInterval);

    // 蓝色圆圈绘制控制点
    painter->setPen(QPen(QColor(0, 255, 255)));
    painter->drawEllipse(m_CtrolPos1, m_RInterval, m_RInterval);
    painter->drawEllipse(m_CtrolPos2, m_RInterval, m_RInterval);

    painter->restore();
}

void BezierCurveWidget::drawLines(QPainter* painter)
{
    painter->save();

    // 绘制控制点与起始点的连线
    QPen nPen;
    nPen.setStyle(Qt::DotLine);
    nPen.setColor(QColor(200, 100, 100));
    nPen.setWidth(1);
    painter->setPen(nPen);

    painter->drawLine(m_StartPos, m_CtrolPos1);
    painter->drawLine(m_EndPos, m_CtrolPos2);
    painter->drawLine(m_CtrolPos1, m_CtrolPos2);

    painter->restore();
}

QRect BezierCurveWidget::getOutSizeRect(QPoint pos)
{
    int interval = m_RInterval;
    return QRect(pos.x() - interval, pos.y() - interval, 2 * interval, 2 * interval);
}

使用函数void QPainterPath::cubicTo(const QPointF & c1, const QPointF & c2, const QPointF & endPoint)就可以绘制出一条贝塞尔曲线了c1和c2为曲线的控制点,endPoint为曲线的终点,而曲线的起点为当前点。