首页 > 其他分享 >实现Bezier样条曲线

实现Bezier样条曲线

时间:2024-08-21 22:51:57浏览次数:6  
标签:曲线 样条 int double void Bezier zmBezier include event

1.给出n+1 个控制点pk=(xk,yk,zk),这里k可取值0-n,多项式函数公式如下

获取的单个点的代码 

void zmBezier::getPoint(float u, double p[3])
{
    int n = m_count - 1;
    double x = 0, y = 0, z = 0;
    for(int k = 0; k <= n; k++)
    {
        x += m_ctrlPoints[k][0] * BEZ_k_n(n, k, u);
        y += m_ctrlPoints[k][1] * BEZ_k_n(n, k, u);
        z += m_ctrlPoints[k][2] * BEZ_k_n(n, k, u);
    }

    p[0] = x;
    p[1] = y;
    p[2] = z;
}

 

 2.混合函数是如下的多项式

double zmBezier::BEZ_k_n(int n, int k, double u)
{
    return  C_n_k(n, k) * pow(u, k) * pow(1 - u, n - k);
}

3.二项式系数

 

double zmBezier::C_n_k(int n, int k)
{
    n = m_count - 1;
    return factorial(n) / (factorial(k) * factorial(n - k));
}

 4.Bezier样条完整代码,全部用指针表示点


/**
Bezier曲线
给定n+1个控制点 Pk=(Xk,Yk,Zk),k取值0-n
多项式函数
-----------------------------------
              n
        P(u)= Σ  Pk × BEZ(u)            0≤u≤1
             k=0           k,n
-----------------------------------
混合函数
-----------------------------------
                         k       n-k
        BEZ(u)=C(n,k) × u × (1-u)       0≤u≤1
             k,n

-----------------------------------
二项式系数
-----------------------------------
                     n!
        C(n,k)=——————————————————
                k! × (n-k)!

-----------------------------------

不想使用 点 结构,全部用指针数组表示点集
*/
#ifndef ZMBEZIER_H
#define ZMBEZIER_H


class zmBezier
{
public:
    zmBezier();
    ~zmBezier();
    zmBezier(int n, double (*points)[3]);


    void getPoint(float u, double p[3]);                //获取参数u时的某一点
    void getCurve(int n, double (*curve)[3]);           //获取n个插值点,代表曲线
    void setCtrlPoints(int n, double (*points)[3]);     //设置控制点
    void getCtrlPoints(int &n, double (*points)[3]);    //获取控制点

private:
    inline double factorial(double n);                  //阶乘
    inline double C_n_k(int n, int k);                  //二项式系数,参数n为了形式上更接近二项式
    inline double BEZ_k_n(int n, int k, double u);      //混合函数

private:
    int m_count;                                        //控制点数量
    double (*m_ctrlPoints)[3];                          //控制点坐标
};

#endif // ZMBEZIER_H
#include "zmBezier.h"

#include<cmath>
#include<string>

zmBezier::zmBezier()

{
    m_count = 0;
    m_ctrlPoints = nullptr;
}

zmBezier::zmBezier(int n, double(*points)[3])
{
    m_count = n;

    m_ctrlPoints = new double[n][3];
    memcpy_s(m_ctrlPoints, sizeof (double)*n * 3, points, sizeof (double)*n * 3);
}

zmBezier::~zmBezier()
{
    m_count = 0;
    delete [] m_ctrlPoints;
}

double zmBezier::C_n_k(int n, int k)
{
    n = m_count - 1;
    return factorial(n) / (factorial(k) * factorial(n - k));
}

double zmBezier::factorial(double n)
{
    return tgamma(n + 1);
}

void zmBezier::getPoint(float u, double p[3])
{
    int n = m_count - 1;
    double x = 0, y = 0, z = 0;
    for(int k = 0; k <= n; k++)
    {
        x += m_ctrlPoints[k][0] * BEZ_k_n(n, k, u);
        y += m_ctrlPoints[k][1] * BEZ_k_n(n, k, u);
        z += m_ctrlPoints[k][2] * BEZ_k_n(n, k, u);
    }

    p[0] = x;
    p[1] = y;
    p[2] = z;
}

double zmBezier::BEZ_k_n(int n, int k, double u)
{
    return  C_n_k(n, k) * pow(u, k) * pow(1 - u, n - k);
}

void zmBezier::getCurve(int count, double (*curve)[3])
{

    double point[3] = {0};
    for(int k = 0; k < count; k++) {
        getPoint(1.0 * k / (count - 1), point);
        curve[k][0] = point[0];
        curve[k][1] = point[1];
        curve[k][2] = point[2];
    }
}

void zmBezier::setCtrlPoints(int n, double(*points)[3])
{
    delete [] m_ctrlPoints;

    m_count = n;

    m_ctrlPoints = new double[n][3];
    int size = sizeof (double) * n * 3;
    memcpy_s(m_ctrlPoints, size, points, size);
}

void zmBezier::getCtrlPoints(int &n, double (*points)[3])
{
    n = m_count;

    if(m_count)
    {
        int size = sizeof (double) * n * 3;
        memcpy_s(points, size, m_ctrlPoints, size);
    }
}

5. 继承QWidget,定义可显示的控制点

#ifndef MYCTRLPOINT_H
#define MYCTRLPOINT_H

#include <QWidget>

class myCtrlPoint : public QWidget
{
    Q_OBJECT
public:
    myCtrlPoint(QWidget *parent);

    QPoint getPosition();
    void setPostion(const QPoint &point);
protected:
    void paintEvent(QPaintEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;

private:
    QPoint m_clicked;

};

#endif // MYCTRLPOINT_H

主要是实现鼠标事件:

5.1 鼠标左键单击,点变成绿色

5.2 鼠标左键拖动,点在父窗口中移动 

5.3 鼠标右键,从父类中删除自己


#include"myCanvas.h"
#include"myCtrlPoint.h"

#include<QKeyEvent>
#include<QPainter>
#include<QMouseEvent>

myCtrlPoint::myCtrlPoint(QWidget *parent)
    : QWidget(parent)
{
    setFixedSize(20, 20);
}

void myCtrlPoint::paintEvent(QPaintEvent *event)
{
    QWidget::paintEvent(event);
    QPainter painter(this);
    if(m_clicked != QPoint())  {
        painter.setBrush(Qt::green);
    }
    else {
        painter.setBrush(Qt::lightGray);
    }
    painter.drawRect(rect());
}


void myCtrlPoint::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        m_clicked = event->globalPos();
        update();
    }
    else if(event->button() == Qt::RightButton)
    {
        myCanvas *canvase = (myCanvas *)parent();
        canvase->m_ctrlWidgets.removeOne(this);
        this->deleteLater();
        canvase->update();
    }


}

void myCtrlPoint::mouseMoveEvent(QMouseEvent *event)
{
    if(m_clicked == QPoint())
    {
        QWidget::mouseMoveEvent(event);

    }
    else
    {
        QPoint cur = event->globalPos();
        QPoint dis = cur - m_clicked;
        m_clicked = cur;
        move(mapToParent(QPoint(0, 0)) + dis);
        ((QWidget *)parent())->update();
    }
}

void myCtrlPoint::mouseReleaseEvent(QMouseEvent *event)
{
    m_clicked = QPoint();
    update();
}

QPoint myCtrlPoint::getPosition()
{
    return mapToParent(rect().center());
}

void myCtrlPoint::setPostion(const QPoint &point)
{
    QPoint target = point - rect().topLeft();
    move(target);

}

6. 继承QWidget,实现一块画布

#ifndef MYCANVAS_H
#define MYCANVAS_H

#include <QWidget>

#include"zmBezier.h"


class myCtrlPoint;
class myCanvas : public QWidget
{
    friend class myCtrlPoint;
    Q_OBJECT
public:
    explicit myCanvas(QWidget *parent = nullptr);
    ~myCanvas();

protected:
    void paintEvent(QPaintEvent *event) override;
    void mouseDoubleClickEvent(QMouseEvent *event) override;

private:
    zmBezier m_curve;
    double m_points[1024][3];                   //不想paintEvent中动态分配内存
    QVector<myCtrlPoint *>m_ctrlWidgets;
};

#endif // MYCANVAS_H

6.1 构造时随机生成4个控制点

6.2 绘制事件中绘制控制点之间的连线、绘制Bezier曲线

6.3 鼠标左键双击空白处会添加一个控制点

6.4 因为不想再绘制事件中动态分配内存,所以用了一个比较大的数组

6.5 控制点是画布的友元类,方便控制点删除自己

#include"myCanvas.h"
#include"myCtrlPoint.h"

#include<QTime>
#include<QDebug>
#include<QPainter>
#include<QMouseEvent>
#include<QRandomGenerator>

myCanvas::myCanvas(QWidget *parent)
    : QWidget(parent)
{
    QRandomGenerator random(QTime::currentTime().second());

    for(int i = 0; i < 4; i++)
    {
        myCtrlPoint *ctrl = new myCtrlPoint(this);
        m_ctrlWidgets.append(ctrl);

        ctrl->setPostion(QPoint(random.generateDouble() * 400, random.generateDouble() * 400));
    }

    resize(500, 500);
}

myCanvas::~myCanvas()
{

}

void myCanvas::paintEvent(QPaintEvent *event)
{
    QWidget::paintEvent(event);
    QPainter painter(this);
    painter.drawText(20, 20, "1.左键拖动控制点");
    painter.drawText(20, 40, "2.右键删除控制点");
    painter.drawText(20, 60, "3.左键双击空白处添加控制点");


    int n = m_ctrlWidgets.count();
    if(n)
    {
        painter.setPen(QPen(Qt::blue, 1, Qt::DotLine));

        for(int i = 0; i < n - 1; i++)
        {
            painter.drawLine(m_ctrlWidgets[i]->getPosition(), m_ctrlWidgets[i + 1]->getPosition());
        }

//        double (*ctrls)[3] = new double[n][3];       尽量别动态分配了,下面限制下点数
        if(n > 1024) {
            n = 1024;
        }

        for(int i = 0; i < n; i++)
        {
//            ctrls[i][0] = m_ctrlWidgets[i]->getPosition().x();
//            ctrls[i][1] = m_ctrlWidgets[i]->getPosition().y();
//            ctrls[i][2] = 0;

            m_points[i][0] = m_ctrlWidgets[i]->getPosition().x();
            m_points[i][1] = m_ctrlWidgets[i]->getPosition().y();
            m_points[i][2] = 0;

        }
        m_curve.setCtrlPoints(n, m_points);
//        m_curve.setCtrlPoints(n, ctrls);
//        delete [] ctrls;

        int request = 100;
//        double (*points)[3] = new double[request][3];

//        m_curve.getCurve(request, points);
        m_curve.getCurve(request, m_points);

        painter.setPen(QPen(Qt::green, 1));
        for(int i = 0; i < request - 1; i++) {
            painter.drawLine(m_points[i][0], m_points[i][1],
                             m_points[i + 1][0], m_points[i + 1][1]);
        }

//        delete [] points;
    }
}

void myCanvas::mouseDoubleClickEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        QPoint point = event->pos();
        myCtrlPoint *ctrl = new myCtrlPoint(this);
        m_ctrlWidgets.append(ctrl);

        ctrl->setPostion(point);
        ctrl->show();
        update();
    }

}

 7.直接显示画布


#include<QApplication>

#include"myCanvas.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    myCanvas camvas;
    camvas.show();

    return a.exec();
}

标签:曲线,样条,int,double,void,Bezier,zmBezier,include,event
From: https://blog.csdn.net/weixin_69505365/article/details/141404357

相关文章

  • 密码学之椭圆曲线(ECC)
    1.椭圆曲线加密ECC概述1.1ECC定义与原理椭圆曲线密码学(ECC)是一种基于椭圆曲线数学的公钥密码体系,它利用了椭圆曲线上的点构成的阿贝尔群和相应的离散对数问题来实现加密和数字签名。ECC的安全性依赖于椭圆曲线离散对数问题(ECDLP)的难解性。在ECC中,首先需要选择一个椭圆......
  • 扫描切除-实体轮廓:方程式驱动曲线路径vs螺旋线路径
    最近,在使用solidworks2018的过程中,接触到扫描切除-实体轮廓命令,如图1-2所示。此命令可以使用一个实体来切除另一个实体,用来切除的实体可以按一定的轨迹运动。测试过程中发现,这个命令频繁出错,切除失败,体验实在是太差了。下面对比了在该命令下使用方程式驱动曲线和螺旋线命令构建......
  • 机器学习之ROC曲线
    机器学习之ROC曲线1.TPR与FPR计算2.TPR、FPR与分类阈值的关系3.生成ROC曲线4.AUC计算参考文献本博客主要参考了https://www.evidentlyai.com/classification-metrics/explain-roc-curve。1.TPR与FPR计算真阳率TPR(TruePositiverate),又称召回率recallrate......
  • 货拉拉金融梦碎:信贷业务夭折,增长曲线难寻
    同城货运平台的巨头货拉拉,近年来一直备受争议与质疑。尽管其市场规模和估值持续攀升,但背后隐藏的问题却如同冰山一角,逐渐浮出水面。近日,货拉拉再度因纵容超载等问题被推上风口浪尖,而其盈利能力和多元化发展战略也遭到了严峻的挑战。货拉拉自成立以来,通过连接商户与司机,构建......
  • 为什么自动控制原理中要采用对数频率特性曲线(伯德图)进行绘制?
    什么是伯德图?伯德图是系统频率响应的一种图示方法。也称为开环对数频率特性曲线。可以根据伯德图系统频率的角度分析系统性能,包括稳定性,动态品质,稳态误差。伯德图分为两张图,幅频特性和相频特性。1.幅频特性图横坐标为lgw:实际工程中低频成分较多,采用此坐标形式可以扩展低频......
  • 用layui +echarts 曲线图实现子页面向父页面传值,点击曲线图表上的点后删除该点,并在删
    下面是一个完整的示例,展示了如何使用layui和ECharts实现以下功能:子页面向父页面传值。点击曲线图上的点后删除该点。删除后自动刷新layui表格列表。根据子页面传值和起止时间刷新父页面。文件结构假设你有两个文件:父页面(index.html)子页面(child.html)1.子页面......
  • QCustomPlot绘制股票曲线,去除中间休市时间
    QCPAxis中增加两个函数,设置x轴的值和标签映射关系,要把中午午休的时间去掉; voidsetTickVector(QVector<double>tickVector){mTickVector=tickVector;};voidsetTickLabels(QVector<QString>tickLabel){mTickVectorLabels=tickLabel;}voidNGraph::SetXTimeLab......
  • SVG之Path路径详解(二),全面解析贝塞尔曲线
    前言如果没看过上一篇文章,可以点击链接前往观看,循序渐进,体验更佳在进入正题前,先温习一下svg的坐标系,x轴为水平向右,y轴为垂直向下在前一篇文章中,我们已经了解了d属性的M、L、H、V、A命令,接下来,将继续了解剩下命令d属性详解主要定义了路径的路径数据,由描述路径的一系列命令数......
  • 80W大功率激光炮夜钓灯专用芯片FP7195,0.1%深度无极无频闪调光调色应用,调光曲线顺滑无
    【80W大功率夜钓灯驱动方案】12V降6V驱动芯片7195钓鱼灯工作原理图夜钓灯电路框架钓鱼灯内部到底有什么让它如此炙手可热?今天,让我们一起揭开钓鱼灯的神秘面纱,对市场上的一款钓鱼灯进行拆解,如下:驱动电路板分为三个部分:FP7195恒流驱动模块,单片机控制模块,降压供电模......
  • 【MATLAB源码-第174期】基于matlab的OFDM电力线系统仿真:梳状导频+LS/MMSE/SVD信道估计
    操作环境:MATLAB2022a1、算法描述OFDM电力线通信系统(PLC)是一种通过电力线传输数据的通信技术,利用了OFDM(OrthogonalFrequencyDivisionMultiplexing,正交频分复用)技术的优势来提高数据传输的速率和质量。电力线作为一种传输介质,其特点包括信道条件的不稳定性、高衰减率以及......