N阶贝塞尔曲线画法
Posted Leslie X徐
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了N阶贝塞尔曲线画法相关的知识,希望对你有一定的参考价值。
N阶贝塞尔曲线画法
涉及知识:
- 贝塞尔曲线
- 牛顿二项式
- 杨辉三角
- 组合数
主要逻辑代码:
/**
* @brief createNBezierCurve 生成N阶贝塞尔曲线点
* @param src 源贝塞尔控制点
* @param dest 目的贝塞尔曲线点
* @param precision 生成精度
*/
static void createNBezierCurve(const QVector<QPointF> &src, QVector<QPointF> &dest, qreal precision)
int size = src.size();
//系数数组
QVector<qreal> coff(size,0);
//准备二维数组a[n][m]表示n阶杨辉三角,使用new创建动态二维数组
int** a = new int*[size];
for(int i=0;i<size;++i)
a[i] = new int[size];
//求系数,个数对应控制点数。使用杨辉三角+组合数的方法
for(int i=0;i<size;++i)
//首尾为 1
a[i][0]=1;
a[i][i]=1;
//利用组合数原理 创建杨辉三角 递推 动态规划法
for(int i=1;i<size;++i)
for(int j=1;j<i;++j)
a[i][j] = a[i-1][j-1] + a[i-1][j];
//总循环,时间变化 p=t*p1+(1-t)*p0
for(qreal t1=0; t1<1; t1+=precision )
//求每个系数
qreal t2 = 1 - t1 ;
int n = size - 1;
//利用到牛顿二项式展开 (a2+2ab+b2)
coff[0] = pow(t2,n); coff[n] = pow(t1,n);
for(int i=1;i<size-1;++i)
coff[i] = pow(t2,n-i) * pow(t1,i) * a[n][i];
//求曲线上点
QPointF ret(0,0);
for(int i=0;i<size;++i)
ret += src[i] * coff[i];
//把所得的点放入目标数组,最后用QPainterPath连起来
dest.append(ret);
//释放二维数组
for(int i = 0; i < size; i ++)
delete [] a[i];
delete [] a;
绘制
头文件
#ifndef DRAWBEZIER_H
#define DRAWBEZIER_H
#include <QWidget>
#include <QGraphicsItem>
QT_BEGIN_NAMESPACE
namespace Ui class DrawBezier;
QT_END_NAMESPACE
class DrawBezier : public QWidget
Q_OBJECT
public:
DrawBezier(QWidget *parent = nullptr);
~DrawBezier();
void draw();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
private:
Ui::DrawBezier *ui;
QList<QGraphicsItem *>_ctlPts;
QVector<QPointF> _src;
QVector<QPointF> _dest;
QGraphicsItem *_curvenullptr;
QGraphicsItem *_Linenullptr;
QGraphicsScene* scene;
bool isCreatingfalse;
bool isAlteringfalse;
;
#endif // DRAWBEZIER_H
源文件
#include "DrawBezier.h"
#include "ui_DrawBezier.h"
#include <QDebug>
/**
* @brief createNBezierCurve 生成N阶贝塞尔曲线点
* @param src 源贝塞尔控制点
* @param dest 目的贝塞尔曲线点
* @param precision 生成精度
*/
static void createNBezierCurve(const QVector<QPointF> &src, QVector<QPointF> &dest, qreal precision)
DrawBezier::DrawBezier(QWidget *parent)
: QWidget(parent)
, ui(new Ui::DrawBezier)
ui->setupUi(this);
this->resize(600,600);
//创建场景,必须创建
scene = new QGraphicsScene(300,300,600,600);
ui->graphicsView->setScene(scene);
ui->graphicsView->setDragMode(QGraphicsView::RubberBandDrag);
//=========================================================
connect(ui->graphicsView,&QWGraphicsView::mouseClicked,this,[this](QPoint pos)
if(isCreating==false)return;
QPointF Scenepos = ui->graphicsView->mapToScene(pos);
QGraphicsItem* item = scene->addRect(QRectF(Scenepos,QSize(10,10)),QPen(QColor(Qt::blue)),QBrush(QColor(Qt::blue)));
item->setFlags(QGraphicsItem::ItemIsSelectable|
QGraphicsItem::ItemIsFocusable|
QGraphicsItem::ItemIsMovable);
_ctlPts.append(item);
ui->label->setText(u8"控制点个数:"+QString::number(_ctlPts.size()));
ui->graphicsView->viewport()->update();
draw();
);
connect(ui->graphicsView,&QWGraphicsView::mouseClickedFinished,this,[this]()
isCreating = false;
);
connect(ui->graphicsView,&QWGraphicsView::mouseReleased,this,[this]()
if(isCreating==true)return;
draw();
);
DrawBezier::~DrawBezier()
for(auto i : _ctlPts)delete i;
if(_curve)delete _curve;
if(_Line)delete _Line;_Line=nullptr;
delete scene;
delete ui;
void DrawBezier::draw()
if(_ctlPts.size()==0)return;
_src.clear();
_dest.clear();
//利用生成的控制点生成需要的控制点位置,因为在拖动控制点时位置会刷新
// item在场景中的位置需要使用 scenePos+boundingRect().center()来获取
for(auto i : _ctlPts)
_src.append(i->scenePos() + i->boundingRect().center());
createNBezierCurve(_src,_dest,0.01);
QPainterPath curvepath;
curvepath.moveTo(_dest[0]);
for(int i=1 ;i<_dest.size() ;++i)
curvepath.lineTo(_dest[i]);
if(_curve)delete _curve;_curve=nullptr;
_curve = scene->addPath(curvepath,QPen(QBrush(Qt::green),3,Qt::SolidLine));
QPainterPath linepath;
linepath.moveTo(_src[0]);
for(int i=1 ;i<_src.size() ;++i)
linepath.lineTo(_src[i]);
if(_Line)delete _Line;_Line=nullptr;
_Line = scene->addPath(linepath,QPen(QBrush(Qt::yellow),3,Qt::DotLine));
void DrawBezier::on_pushButton_clicked()
isCreating = true;
void DrawBezier::on_pushButton_2_clicked()
for(auto i : _ctlPts)delete i;
if(_curve)delete _curve;_curve=nullptr;
if(_Line)delete _Line;_Line=nullptr;
_ctlPts.clear();
isCreating = false;
void DrawBezier::on_pushButton_3_clicked()
for(auto item : scene->selectedItems())
_ctlPts.removeAll(item);
delete item;
draw();
以上是关于N阶贝塞尔曲线画法的主要内容,如果未能解决你的问题,请参考以下文章
贝塞尔曲线 WPF MVVM N阶实现 公式详解+源代码下载