QT绘制贝塞尔曲线及控制点

该代码示例展示了如何在Qt环境中创建一个BezierCurve类,该类允许用户通过鼠标交互添加和移动控制点,以及通过选择不同的精度值来绘制不同级别的详细贝塞尔曲线。关键函数包括mousePressEvent、mouseMoveEvent和mouseReleaseEvent,用于处理鼠标事件,以及paintEvent用于渲染控制点和贝塞尔曲线。
摘要由CSDN通过智能技术生成

头文件 

#ifndef BEZIERCURVE_H
#define BEZIERCURVE_H

#include <QWidget>

QT_FORWARD_DECLARE_CLASS(BezierCurvePrivate);

class BezierCurve : public QWidget
{
    Q_OBJECT

public:
    BezierCurve(QWidget *parent = nullptr);
    ~BezierCurve() override;

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

private:
    BezierCurvePrivate *d = nullptr;
};

#endif // BEZIERCURVE_H

 源文件

#include "beziercurve.h"


#include <QtMath>
#include <QComboBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>

/**
 * @brief createNBezierCurve 生成N阶贝塞尔曲线点
 * @param src 源贝塞尔控制点
 * @param dest 目的贝塞尔曲线点
 * @param precision 生成精度
 */
static void createNBezierCurve(const QList<QPointF> &src, QList<QPointF> &dest, qreal precision)
{
    if (src.size() <= 0) return;

    //清空
    QList<QPointF>().swap(dest);

    for (qreal t = 0; t < 1.0000; t += precision) {//根据精度绘制点
        int size = src.size();
        QVector<qreal> coefficient(size, 0);
        coefficient[0] = 1.000;
        qreal u1 = 1.0 - t;

        for (int j = 1; j <= size - 1; j++) {
            qreal saved = 0.0;
            for (int k = 0; k < j; k++){
                qreal temp = coefficient[k];
                coefficient[k] = saved + u1 * temp;
                saved = t * temp;
            }
            coefficient[j] = saved;
        }

        QPointF resultPoint;
        for (int i = 0; i < size; i++) {
            QPointF point = src.at(i);
            resultPoint = resultPoint + point * coefficient[i];
        }
        //给贝塞尔曲线加入关键点
        dest.append(resultPoint);
    }
}

class BezierCurvePrivate
{
public:
    BezierCurvePrivate() = default;

    //完成控制点构成
    bool m_completed = false;
    bool m_mousePressed = false;
    int m_currentControlPointIndex = -1;
    qreal m_precision = 0.1;
    QList<QPointF> m_controlPoints;
    QList<QPointF> m_bezierCurve;
};

BezierCurve::BezierCurve(QWidget *parent)
    : QWidget(parent)
{
    d = new BezierCurvePrivate; //贝塞尔曲线类

    QComboBox *comboBox = new QComboBox(this);
    connect(comboBox, QOverload<int>::of(&QComboBox::activated), this, [=](int index) {
        d->m_precision = comboBox->itemText(index).toDouble();
        createNBezierCurve(d->m_controlPoints, d->m_bezierCurve, d->m_precision);//更新贝塞尔曲线 线上的点
        update();
    });
    comboBox->addItem(QString::number(0.1));
    comboBox->addItem(QString::number(0.01));
    comboBox->addItem(QString::number(0.001));
    QLabel *label = new QLabel("精度:", this);
    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(label);
    layout->addWidget(comboBox);

    QWidget *widget = new QWidget(this);
    widget->resize(150, 50);
    widget->setLayout(layout);

    resize(800, 600);
}

BezierCurve::~BezierCurve()
{
    if (d) delete d;
}

void BezierCurve::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event); //即Q_UNUSED()函数在程序中没有实质性的作用,用来避免编译器警告

    QPainter painter(this);//绘制控制点
    painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
    painter.save();
    painter.setBrush(QBrush(Qt::green));
    QFontMetrics metrics(painter.font());//字体规格信息
    for (auto i = 0; i < d->m_controlPoints.size(); i++) {
        painter.setPen(Qt::green);
        painter.drawEllipse(d->m_controlPoints.at(i), 6.0, 6.0);//控制点大小
        //QString number = QString::number(i);
        //auto rect = metrics.boundingRect(number);
        //painter.drawText(d->m_controlPoints.at(i) + QPointF(-rect.width() / 2, rect.height() / 2 - 1.0), number);
    }
    painter.restore();//恢复上面保存的状态

    if (d->m_controlPoints.size() >= 2) {//如果控制点大于2个,绘制贝塞尔曲线
        QPainterPath curve;
        curve.moveTo(d->m_bezierCurve.at(0));
        for (auto i = 1; i < d->m_bezierCurve.size(); i++) {;
            curve.lineTo(d->m_bezierCurve.at(i));
        }
        auto pen = painter.pen();
        pen.setColor(Qt::blue);
        pen.setWidth(2.0);
        painter.setPen(pen);
        painter.drawPath(curve);
    }
}

void BezierCurve::mousePressEvent(QMouseEvent *event) //绘制控制点功能
{
    //event->buttons()返回产生事件的按钮状态,函数返回当前按下的所有按钮,按钮状态可以是Qt::LeftButton,Qt::RightButton,Qt::MidButton或运算组合
    if (event->buttons() & Qt::LeftButton && event->pos().y() > 50) {
        d->m_mousePressed = true; //鼠标按下
        if (d->m_completed) { //如果已经完成绘制
            d->m_currentControlPointIndex = -1; //当前控制点序号
            auto pos = event->pos();
            for (auto i = 0; i < d->m_controlPoints.size(); i++) {
                auto point = d->m_controlPoints.at(i);
                //判断是否在控制点上
                if (qAbs(qSqrt(qPow(pos.x() - point.x(), 2) + qPow(pos.y() - point.y(), 2))) < 10.0000) {
                    d->m_currentControlPointIndex = i;
                    break;
                }
            }
        } else { //如果没有完成绘制,则添加控制点
            d->m_controlPoints.append(event->pos());
            //控制点增加了,需要更新
            createNBezierCurve(d->m_controlPoints, d->m_bezierCurve, d->m_precision);
            update();//update函数的作用是更新窗口
        }
    } else if (event->buttons() & Qt::RightButton) { //如果按下右键,停止绘制
        d->m_completed = true;
    }
}

void BezierCurve::mouseMoveEvent(QMouseEvent *event)
{
    if (event->buttons() & Qt::LeftButton
            && d->m_mousePressed
            && d->m_currentControlPointIndex != -1
            && event->pos().y() > 50) { //如果鼠标左键按住移动
        if (d->m_currentControlPointIndex < d->m_controlPoints.size()) {
            d->m_controlPoints[d->m_currentControlPointIndex] = event->pos();//更新当前控制点位置
            createNBezierCurve(d->m_controlPoints, d->m_bezierCurve, d->m_precision);
            update();
        }
    }
}

void BezierCurve::mouseReleaseEvent(QMouseEvent *event)
{
    d->m_currentControlPointIndex = -1;
    if (event->buttons() & Qt::LeftButton) { //鼠标左键松开
        d->m_mousePressed = false;
    }
}

void BezierCurve::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Escape) {//如果按esc,清除所有内容
        d->m_completed = false;
        QList<QPointF>().swap(d->m_controlPoints);
        QList<QPointF>().swap(d->m_bezierCurve);
        update();
    }
}

 QT绘制贝塞尔曲线及控制点资源-CSDN文库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aspiretop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值