QT实现单个控制点在曲线上的贝塞尔曲线

发布于:2025-03-04 ⋅ 阅读:(17) ⋅ 点赞:(0)

最终效果:
在这里插入图片描述
一共三个文件
main.cpp

#include <QApplication>
#include "SplineBoard.h"
int main(int argc,char** argv) {
    QApplication a(argc, argv);
    SplineBoard b;
    b.setWindowTitle("标准的贝塞尔曲线");
    b.show();

    SplineBoard b2(0.0001);
    b2.show();
    b2.setWindowTitle("控制点在曲线上的贝塞尔曲线");

    b.move(0,0);
    b2.move(800,0);
    return a.exec();
}

SplineBoard.h

#ifndef SPLINEBOARD_H
#define SPLINEBOARD_H
#include <QPainter>
#include <QWidget>
class SplineBoard:public QWidget
{
    Q_OBJECT
public:
    SplineBoard(double terminalRatio = 0.25);

protected:
    void mouseMoveEvent(QMouseEvent* e);
    void mousePressEvent(QMouseEvent* e);
    void mouseReleaseEvent(QMouseEvent* e);
    void paintEvent(QPaintEvent* e);
    int mPressedPos;
    QPointF mStart;
    QPointF mEnd;
    QPointF mCont;
    const double termRatio;
};

#endif // SPLINEBOARD_H

SplineBoard.cpp

#include "SplineBoard.h"
#include <QDebug>
#include <QMouseEvent>
#include <QPainterPath>

static int radius = 8;
static const int PosEmpty = 0;
static const int PosStart = 1;
static const int PosCont = 2;
static const int PosEnd = 4;

static void buildQuadBezier(QPainterPath& pp, const QPointF& p0,const QPointF& p1,
                     const QPointF& p2, qreal term){
    pp.moveTo(p0);
    const qreal a = (term-0.5)*-4;
    const qreal d = (1-2*term)*-4;
    for (qreal t = 0; t < 1.01; t += 0.01) {
        qreal A = a*t*t+(-1-a)*t+1;
        qreal B = d*t*t - d*t;
        qreal C = a*t*t + (1-a)*t;
        qreal x = A * p0.x() + B * p1.x() + C * p2.x();
        qreal y = A * p0.y() + B * p1.y() + C * p2.y();
        pp.lineTo(x, y);
    }
}
SplineBoard::SplineBoard(double terminalRatio):mPressedPos(PosEmpty),termRatio(terminalRatio)
{
    resize(800,600);
    setWindowTitle("spline board");

    mStart = QPointF(50,300);
    mEnd = QPointF(750,300);
    mCont = QPointF(400,300);
}

void SplineBoard::mouseMoveEvent(QMouseEvent* e){
    auto cur = e->pos();
    if(mPressedPos == PosStart){
        mStart = cur;
    }
    else if(mPressedPos == PosCont){
        mCont = cur;
    }
    else if(PosEnd == mPressedPos){
        mEnd = cur;
    }
    else{
        return;
    }
    update();
}
void SplineBoard::mousePressEvent(QMouseEvent* e){
    auto pos = e->pos();
    auto startRect = QRectF(mStart,QSize(radius*2,radius*2));
    startRect.translate(-radius,-radius);
    auto contRect = QRectF(mCont,QSize(radius*2, radius*2));
    contRect.translate(-radius,-radius);
    auto endRect = QRectF(mEnd , QSize(radius*2, radius*2));
    endRect.translate(-radius,-radius);

    if(startRect.contains(pos)){
        mPressedPos = PosStart;
    }
    else if(contRect.contains(pos)) {
        mPressedPos = PosCont;
    }
    else if(endRect.contains(pos)){
        mPressedPos = PosEnd;
    }
    else{
        mPressedPos = PosEmpty;
    }

}
void SplineBoard::mouseReleaseEvent(QMouseEvent* e){
    mPressedPos = PosEmpty;
}

void SplineBoard::paintEvent(QPaintEvent* e){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);

    QPen pen(Qt::blue);
    pen.setWidth(2);
    painter.setPen(pen);

    QPainterPath path;

    buildQuadBezier(path,mStart,mCont,mEnd,termRatio);
    painter.drawPath(path);

    painter.setPen(Qt::NoPen);
    painter.setBrush(Qt::magenta);
    painter.drawEllipse(mStart,radius,radius);  // Draw start point
    painter.drawEllipse(mCont, radius,radius);  // Draw control point
    painter.drawEllipse(mEnd, radius,radius);  // Draw end point

    QWidget::paintEvent(e);

}