qt贝塞尔曲线演示工具

贝塞尔曲线演示工具

该代码实现了一个基于Qt的贝塞尔曲线演示工具,主要功能包括:

1.可视化组件:

  • 绘制网格背景和贝塞尔曲线控制线
  • 显示可拖动的控制点(起点绿色,终点红色,中间点蓝色)
  • 实时渲染贝塞尔曲线路径

2.交互功能:

  • 鼠标拖动调整控制点位置
  • 点选控制点高亮显示
  • 动画演示贝塞尔曲线生成过程

3.技术特点:

  • 使用QWidget实现自定义绘制
  • 支持反锯齿渲染
  • 通过QTimer实现平滑动画效果
  • 采用面向对象设计分离控制点和曲线逻辑
    该工具适用于教学演示和图形算法学习,直观展示贝塞尔曲线的数学原理和可视化效果。
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

#include <QApplication>
#include <QMainWindow>
#include <QWidget>
#include <QPainter>
#include <QPointF>
#include <QVector>
#include <QTimer>
#include <QPushButton>
#include <QLabel>
#include <QMouseEvent>
#include <QGridLayout>
#include <cmath>

class BezierPoint : public QWidget {
public:
    BezierPoint(QWidget *parent = nullptr) : QWidget(parent), selected(false) {
        setFixedSize(24, 24);
    }

    void setSelected(bool sel) {
        selected = sel;
        update();
    }

protected:
    void paintEvent(QPaintEvent *) override {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);

        if (selected) {
            painter.setPen(QPen(Qt::red, 2));
            painter.setBrush(Qt::yellow);
        } else {
            painter.setPen(QPen(QColor(232, 196, 142), 2));
            painter.setBrush(Qt::white);
        }

        painter.drawEllipse(rect().center(), 8, 8);
    }

private:
    bool selected;
};

class BezierWidget : public QWidget {
    Q_OBJECT

public:
    BezierWidget(QWidget *parent = nullptr) : QWidget(parent), t(0.0), animating(false) {
        setMinimumSize(800, 600);
        setMouseTracking(true);

        // Initialize control points
        controlPoints << QPointF(100, 500) << QPointF(250, 200) << QPointF(500, 300) << QPointF(700, 450);

        // Create point widgets
        for (int i = 0; i < controlPoints.size(); ++i) {
            BezierPoint *point = new BezierPoint(this);
            point->move(controlPoints[i].x() - 12, controlPoints[i].y() - 12);
            bezierPoints << point;
        }

        // Set up timer for animation
        timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, [this]() {
            t += 0.005;
            if (t > 1.0) {
                t = 1.0;
                timer->stop();
                animating = false;
                emit animationFinished();
            }
            update();
        });
    }

    void startAnimation() {
        t = 0.0;
        animating = true;
        timer->start(30);
    }

    void stopAnimation() {
        animating = false;
        timer->stop();
    }

    bool isAnimating() const { return animating; }

signals:
    void animationFinished();

protected:
    void paintEvent(QPaintEvent *) override {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);

        // Draw background grid
        drawGrid(painter);

        // Draw control lines
        drawControlLines(painter);

        // Draw Bezier curve
        drawBezierCurve(painter);

        // Draw animation lines and points if animating
        if (animating) {
            drawAnimation(painter);
        }
    }

    void mousePressEvent(QMouseEvent *event) override {
        if (event->button() == Qt::LeftButton) {
            for (int i = 0; i < bezierPoints.size(); ++i) {
                QRect pointRect(bezierPoints[i]->geometry());
                if (pointRect.contains(event->pos())) {
                    selectedPoint = i;
                    bezierPoints[i]->setSelected(true);
                    return;
                }
            }
            selectedPoint = -1;
        }
    }

    void mouseMoveEvent(QMouseEvent *event) override {
        if (selectedPoint >= 0 && selectedPoint < controlPoints.size()) {
            // Move control point
            controlPoints[selectedPoint] = event->pos();
            bezierPoints[selectedPoint]->move(event->x() - 12, event->y() - 12);
            update();
        }
    }

    void mouseReleaseEvent(QMouseEvent *event) override {
        Q_UNUSED(event);
        if (selectedPoint >= 0 && selectedPoint < bezierPoints.size()) {
            bezierPoints[selectedPoint]->setSelected(false);
        }
        selectedPoint = -1;
    }

private:
    void drawGrid(QPainter &painter) {
        painter.save();
        painter.setPen(QPen(QColor(200, 200, 200), 0.5));

        // Draw grid lines
        for (int x = 50; x < width(); x += 50) {
            painter.drawLine(x, 50, x, height() - 50);
        }

        for (int y = 50; y < height(); y += 50) {
            painter.drawLine(50, y, width() - 50, y);
        }

        // Draw border
        painter.setPen(QPen(Qt::black, 1, Qt::DashLine));
        painter.drawRect(50, 50, width() - 100, height() - 100);

        painter.restore();
    }

    void drawControlLines(QPainter &painter) {
        painter.save();
        painter.setPen(QPen(QColor(232, 196, 142), 2));

        // Draw control lines
        for (int i = 0; i < controlPoints.size() - 1; ++i) {
            painter.drawLine(controlPoints[i], controlPoints[i+1]);
        }

        // Draw control points
        for (int i = 0; i < controlPoints.size(); ++i) {
            painter.setPen(QPen(Qt::black, 1));
            painter.setBrush(i == 0 ? Qt::green : (i == controlPoints.size()-1 ? Qt::red : Qt::blue));
            painter.drawEllipse(controlPoints[i], 4, 4);

            // Draw point label
            painter.setPen(Qt::black);
            painter.drawText(controlPoints[i] + QPointF(10, -10), QString::number(i+1));
        }

        painter.restore();
    }

    void drawBezierCurve(QPainter &painter) {
        painter.save();
        painter.setPen(QPen(Qt::red, 2));

        // Draw Bezier curve
        QPainterPath path;
        path.moveTo(controlPoints[0]);

        // Cubic Bezier curve with 4 points
        if (controlPoints.size() == 4) {
            path.cubicTo(controlPoints[1], controlPoints[2], controlPoints[3]);
        }
        // Quadratic Bezier curve with 3 points
        else if (controlPoints.size() == 3) {
            path.quadTo(controlPoints[1], controlPoints[2]);
        }
        // Linear Bezier curve with 2 points
        else if (controlPoints.size() == 2) {
            path.lineTo(controlPoints[1]);
        }

        painter.drawPath(path);
        painter.restore();
    }

    void drawAnimation(QPainter &painter) {
        // Draw intermediate points and lines
        QVector<QPointF> currentLevel = controlPoints;
        QVector<QVector<QPointF>> levels;
        levels.append(currentLevel);

        while (currentLevel.size() > 1) {
            QVector<QPointF> nextLevel;
            QColor lineColor;

            switch (currentLevel.size() % 3) {
                case 0: lineColor = QColor(0, 170, 0); break; // Green
                case 1: lineColor = Qt::blue; break;
                case 2: lineColor = Qt::red; break;
                default: lineColor = Qt::darkGray;
            }

            painter.setPen(QPen(lineColor, 1.5));

            // Draw lines between points in the current level
            for (int i = 0; i < currentLevel.size() - 1; ++i) {
                painter.drawLine(currentLevel[i], currentLevel[i+1]);

                // Calculate next level points
                QPointF p = interpolate(currentLevel[i], currentLevel[i+1], t);
                nextLevel.append(p);

                // Draw intermediate points
                painter.setPen(QPen(Qt::black, 1));
                painter.setBrush(lineColor);
                painter.drawEllipse(p, 3, 3);
                painter.setPen(QPen(lineColor, 1.5));
            }

            currentLevel = nextLevel;
            levels.append(currentLevel);
        }

        // Draw the final point on the curve
        if (!currentLevel.isEmpty()) {
            painter.setPen(QPen(Qt::red, 2));
            painter.setBrush(Qt::red);
            painter.drawEllipse(currentLevel[0], 5, 5);
        }

        // Draw t value
        painter.setPen(Qt::black);
        painter.drawText(20, 30, QString("t: %1").arg(t, 0, 'f', 3));
    }

    QPointF interpolate(const QPointF &p1, const QPointF &p2, qreal t) {
        return p1 + t * (p2 - p1);
    }

private:
    QVector<QPointF> controlPoints;
    QVector<BezierPoint*> bezierPoints;
    int selectedPoint = -1;
    qreal t;
    bool animating;
    QTimer *timer;
};

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        setWindowTitle("贝塞尔曲线可视化工具");

        QWidget *centralWidget = new QWidget(this);
        QGridLayout *layout = new QGridLayout(centralWidget);

        bezierWidget = new BezierWidget(this);
        layout->addWidget(bezierWidget, 0, 0, 1, 2);

        // Create control buttons
        QPushButton *startButton = new QPushButton("开始动画", this);
        connect(startButton, &QPushButton::clicked, this, [=,this]() {
            if (bezierWidget->isAnimating()) {
                bezierWidget->stopAnimation();
                startButton->setText("开始动画");
            } else {
                bezierWidget->startAnimation();
                startButton->setText("停止动画");
            }
        });

        QPushButton *resetButton = new QPushButton("重置曲线", this);
        connect(resetButton, &QPushButton::clicked, this, [=,this]() {
            bezierWidget->stopAnimation();
            startButton->setText("开始动画");
            bezierWidget->update();
        });

        layout->addWidget(startButton, 1, 0);
        layout->addWidget(resetButton, 1, 1);

        // Add information label
        QLabel *infoLabel = new QLabel(
            "使用说明:\n"
            "1. 拖动控制点改变曲线形状\n"
            "2. 点击'开始动画'查看曲线生成过程\n"
            "3. 不同颜色线段展示递归插值过程\n"
            "4. 红色圆点是曲线上的点(t值)"
        );
        layout->addWidget(infoLabel, 2, 0, 1, 2);

        setCentralWidget(centralWidget);
        resize(850, 700);
    }

private:
    BezierWidget *bezierWidget;
};



#endif // WIDGET_H
相关推荐
啊森要自信2 小时前
【QT】常⽤控件详解(四)常用显示类控件类 Label && LCDNumber && ProgressBar && Calendar Widget
开发语言·数据库·c++·qt·qt6.3
sannianji3 小时前
qt 采用movetothread在应用程序gui关闭时如何正确退出。
开发语言·qt
kfepiza5 小时前
JavaScript将String转为base64 笔记250802
开发语言·javascript·笔记
Warren985 小时前
Vue2博客项目笔记(第一天)
java·开发语言·javascript·vue.js·windows·笔记·ecmascript
软件测试-阿涛5 小时前
【自动化测试】Python Selenium 自动化测试元素定位专业教程
开发语言·python·selenium·自动化
幽迷狂6 小时前
AFSIM入门教程03.03:更新所有依赖库版本
c++·qt·仿真·osgearth·osg·军事·afsim
勇闯逆流河6 小时前
【C++】Stack and Queue and Functor
开发语言·c++
来来走走7 小时前
Flutter开发 了解Scaffold
android·开发语言·flutter
xiangweiqiang8 小时前
用phpstudy安装php8.2后报错:意思是找不到php_redis.dll拓展时
开发语言·php