实现Bezier样条曲线

news2024/12/29 9:05:32

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();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2061709.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Trades和Centertrack在windows上配置

直接说结论好了,小虎在windows配了一个星期失败了,结果是双系统在linux下配置成功。 成功环境 Successful systems info: CUDA 11.4 CUDA driver 470.63.01 python 3.6.13 GCC 7.5.0 pytroch 1.9.0 compilation tools, release 11.4, V11.4.48成功记录

关于AR在医疗领域创新应用

AR技术在医疗领域创新应用&#xff0c;旨在展示AR技术如何为医疗行业带来革命性的变化&#xff0c;我们可以从以下几个方面入手&#xff1a; 一、引言 随着科技的飞速发展&#xff0c;增强现实&#xff08;AR&#xff09;技术正逐步渗透到医疗领域的各个环节&#xff0c;为患…

云手机在亚马逊店铺运营中能发挥什么作用

亚马逊作为全球领先的电商平台&#xff0c;汇聚了庞大的用户群体和交易规模&#xff0c;如何有效吸引流量成为亚马逊店铺经营者面临的难题。而云手机作为一种前沿的技术工具&#xff0c;为亚马逊店铺引流带来了全新的解决方案。本文将深入探讨云手机在亚马逊店铺引流中的关键作…

JVM类加载机制—JVM类加载过程

一、概述 代码编译后&#xff0c;就会生成JVM&#xff08;Java虚拟机&#xff09;能够识别的二进制字节流文件&#xff08;*.class&#xff09;。而JVM把Class文件中的类描述数据从文件加载到内存&#xff0c;并对数据进行校验、转换解析、初始化&#xff0c;使这些数据最终成…

数据结构--图(笔记)

文章目录 1. 概念2. 分类无向图有向图循环图连通图 3. 应用4. 操作(CRUD)5. 图常见的数据结构邻接表邻接矩阵关联矩阵关联矩阵与邻接矩阵 6. 内容出处 1. 概念 ① 图&#xff1a;在计算机科学中&#xff0c;图&#xff08;英语&#xff1a;graph&#xff09;是一种抽象数据类型…

36. 有效的数独【 力扣(LeetCode) 】

一、题目描述 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图…

2-72 基于matlab的平稳小波变换进行多聚焦图像融合

基于matlab的平稳小波变换进行多聚焦图像融合&#xff0c;获得一副清晰的图像&#xff0c;带有一副示例图像&#xff0c;实验效果好。SWT级平稳小波变换&#xff0c;是一种多尺度、多方向、时频局部的图像稀疏表示方法&#xff0c;广泛运行图像处理领域&#xff0c;具有平移不变…

msxml*.dll 错误 ‘80072f7d‘ 安全频道支持出错 解决方案

诡异的 msxml6.dll错误 80072f7d安全频道支持出错&#xff0c;用 SSLTools.exe 修复的方法无效&#xff01;&#xff01;&#xff01; ’--------------------------------------------------------------- 有如下简要 ASP 代码&#xff0c;用于获取网页链接返回内容&#xf…

《图解设计模式》笔记(四)分开考虑

九、Bridge模式&#xff1a;将类的功能层次结构与实现层次结构分离 类的两个层次结构和作用 类的功能层次结构&#xff1a;希望增加新功能时 父类有基本功能&#xff0c;在子类中增加新功能 Something父类 …├─SomethingGood子类 想要再增加新功能 Something父类 …├─So…

计算机的错误计算(六十九)

摘要 计算机的错误计算&#xff08;六十三&#xff09;与&#xff08;六十八&#xff09;分别探讨了大数与 附近数 的余切函数值的错误计算。本节讨论第三种类型数值&#xff1a; 附近数 的余切函数的计算精度问题。 例1. 已知 计算 不妨先用 Python的 torch库计算&…

RocketMQKafka重试队列

为实现服务间的解耦和部分逻辑的异步处理&#xff0c;我们的系统采纳了消息驱动的方法。通过消息队列的使用&#xff0c;各个服务能够基于事件进行通信&#xff0c;从而降低了直接的依赖关系&#xff0c;优化了系统的响应性能和可靠性。 为什么需要考虑消费重试&#xff1f; …

人格凭证(PHC):一种鉴别AI防伪保护隐私的真实身份验证技术

人格凭证&#xff08;PHC&#xff09;&#xff1a;一种鉴别AI防伪保护隐私的真实身份验证技术 引言 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;网络空间中的身份验证问题日益凸显。AI不仅能模仿人类行为&#xff0c;还能创建虚假账户、发布误导性信息…

秒懂Linux之缓冲区

目录 一.何为缓冲区 二. 缓冲区在哪 三. 模拟编码 一.何为缓冲区 缓冲区说白了就是一块内存区域&#xff0c;目的是为了提高使用者的效率以及减少C语言接口的使用频率~ 下面我们用一则小故事来类比出缓冲区的功能~ 张三为了给朋友李四庆祝生日快乐准备了份生日礼物~张三难道…

开源原型设计工具Penpot

Penpot是一个现代化、开源的协同设计平台&#xff0c;专为跨职能团队打造&#xff0c;提供了强大的在线设计和原型制作功能。 以下是对Penpot的详细介绍&#xff1a; 一、平台特点 开源与免费&#xff1a;Penpot是一个完全免费且开放源代码的项目&#xff0c;允许社区贡献和定…

Redis补充

Redis事务 Redis事务的概念 Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令&#xff0c;一个事务中所有命令都会被序列化。在事务执行过程&#xff0c;会按照顺序串行化执行队列中的命令&#xff0c;其他客户端提交的命令请求不会插入到事务执行命令序列中。 …

JAVA多线程等待唤醒机制

为什么要处理线程间通信&#xff1a; 当我们需要多个线程来共同完成一件任务&#xff0c;并且我们希望他们有规律的执行&#xff0c;那么多线程之间需要一些通信机制&#xff0c;可以协调它们的工作&#xff0c;以此实现多线程共同操作一份数据。 比如&#xff1a;线程A用来生…

Java | Leetcode Java题解之第357题统计各位数字都不同的数字个数

题目&#xff1a; 题解&#xff1a; class Solution {public int countNumbersWithUniqueDigits(int n) {if (n 0) {return 1;}if (n 1) {return 10;}int res 10, cur 9;for (int i 0; i < n - 1; i) {cur * 9 - i;res cur;}return res;} }

4-1-5 步进电机原理2(电机专项教程)

4-1-5 步进电机原理2&#xff08;电机专项教程&#xff09; 4-1-5 步进电机原理2永磁式步进电机反应式步进电机混合式步进电机混合式步进电机基本原理 4-1-5 步进电机原理2 新的步进电机分类 永磁式步进电机 目前学习的转子都是永磁铁 反应式步进电机 软磁材料易受到周围磁场…

阿里云魏子珺:阿里云Elasticsearch AI 搜索实践

作者&#xff1a;阿里云魏子珺 【AI搜索 TechDay】是 Elastic 和阿里云联合主办的 AI 技术 Meetup 系列&#xff0c;聚焦企业级 AI 搜索应用和开发者动手实践&#xff0c;旨在帮助开发者在大模型浪潮下升级 AI 搜索&#xff0c;助力业务增长。 阿里云 Elasticsearch 的 AI 搜索…

Nginx笔记(高级)

扩容 通过扩容提升整体吞吐量 单机垂直扩容&#xff1a;硬件资源增加 云服务资源增加 整机&#xff1a;IBM、浪潮、DELL、HP等CPU/主板&#xff1a;更新到主流网卡&#xff1a;10G/40G网卡磁盘&#xff1a;SAS(SCSI) HDD&#xff08;机械&#xff09;、HHD&#xff08;混合&…