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
相关推荐
奇某人1 小时前
【语法】【C+V】本身常用图表类型用法快查【CSDN不支持,VSCODE可用】
开发语言·vscode·markdown·mermaid
做一位快乐的码农1 小时前
php程序设计之基于PHP的手工艺品销售网站/基于php在线销售系统/基于php在线购物商城系统
开发语言·php
@珍惜一生@2 小时前
Qt开源库
开发语言·qt·开源
Slaughter信仰2 小时前
深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)第四章知识点问答补充及重新排版
java·开发语言·jvm
心灵宝贝2 小时前
Mac用户安装JDK 22完整流程(Intel版dmg文件安装指南附安装包下载)
java·开发语言·macos
secondyoung3 小时前
一文丝滑使用Markdown:从写作、绘图到转换为Word与PPT
开发语言·vscode·编辑器·powerpoint·markdown·visual studio·mermaid
雨枪幻。4 小时前
spring boot开发:一些基础知识
开发语言·前端·javascript
爱炸薯条的小朋友4 小时前
C#由Dictionary不正确释放造成的内存泄漏问题与GC代系
开发语言·opencv·c#
m0_480502646 小时前
Rust 登堂 之 函数式编程(三)
开发语言·后端·rust
eqwaak08 小时前
科技信息差(8.26)
大数据·开发语言·人工智能·编辑器