qt贝塞尔曲线演示工具
贝塞尔曲线演示工具
该代码实现了一个基于Qt的贝塞尔曲线演示工具,主要功能包括:
1.可视化组件:
- 绘制网格背景和贝塞尔曲线控制线
- 显示可拖动的控制点(起点绿色,终点红色,中间点蓝色)
- 实时渲染贝塞尔曲线路径
2.交互功能:
- 鼠标拖动调整控制点位置
- 点选控制点高亮显示
- 动画演示贝塞尔曲线生成过程
3.技术特点:
- 使用QWidget实现自定义绘制
- 支持反锯齿渲染
- 通过QTimer实现平滑动画效果
- 采用面向对象设计分离控制点和曲线逻辑
该工具适用于教学演示和图形算法学习,直观展示贝塞尔曲线的数学原理和可视化效果。
#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_OBJECTpublic:BezierWidget(QWidget *parent = nullptr) : QWidget(parent), t(0.0), animating(false) {setMinimumSize(800, 600);setMouseTracking(true);// Initialize control pointscontrolPoints << QPointF(100, 500) << QPointF(250, 200) << QPointF(500, 300) << QPointF(700, 450);// Create point widgetsfor (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 animationtimer = 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 griddrawGrid(painter);// Draw control linesdrawControlLines(painter);// Draw Bezier curvedrawBezierCurve(painter);// Draw animation lines and points if animatingif (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 pointcontrolPoints[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 linesfor (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 borderpainter.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 linesfor (int i = 0; i < controlPoints.size() - 1; ++i) {painter.drawLine(controlPoints[i], controlPoints[i+1]);}// Draw control pointsfor (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 labelpainter.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 curveQPainterPath path;path.moveTo(controlPoints[0]);// Cubic Bezier curve with 4 pointsif (controlPoints.size() == 4) {path.cubicTo(controlPoints[1], controlPoints[2], controlPoints[3]);}// Quadratic Bezier curve with 3 pointselse if (controlPoints.size() == 3) {path.quadTo(controlPoints[1], controlPoints[2]);}// Linear Bezier curve with 2 pointselse if (controlPoints.size() == 2) {path.lineTo(controlPoints[1]);}painter.drawPath(path);painter.restore();}void drawAnimation(QPainter &painter) {// Draw intermediate points and linesQVector<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; // Greencase 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 levelfor (int i = 0; i < currentLevel.size() - 1; ++i) {painter.drawLine(currentLevel[i], currentLevel[i+1]);// Calculate next level pointsQPointF p = interpolate(currentLevel[i], currentLevel[i+1], t);nextLevel.append(p);// Draw intermediate pointspainter.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 curveif (!currentLevel.isEmpty()) {painter.setPen(QPen(Qt::red, 2));painter.setBrush(Qt::red);painter.drawEllipse(currentLevel[0], 5, 5);}// Draw t valuepainter.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_OBJECTpublic: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 buttonsQPushButton *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 labelQLabel *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