实现Bezier样条曲线

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.mfbz.cn/a/871551.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《机器学习》—— 使用过采样方法实现逻辑回归分类问题

文章目录 一、什么是过采样方法&#xff1f;二、使用过采样方法实现逻辑回归分类问题三、过采样的优缺点 本篇内容是 基于Python的scikit-learn库中sklearn.linear_model 类中的 LogisticRegression&#xff08;&#xff09;逻辑回归方法实现的&#xff0c;其内容中只是在处理…

创建、使用、删除 数据库

一、创建数据库 1.1 使用DDL语句创建数据库 CREATE DATABASE 数据库名 CHARACTER SET 字符编码 COLLATE 排序规则; 如果不指定数据库编码&#xff0c;默认是utf8&#xff1b; 如果不指定排序规则&#xff0c;默认是utf8_general_ci&#xff0c;即不区分大小写&#xff0c;区分…

我“开发“了一款大模型应用,AI门槛这么低了吗?

现在国产大模型多如牛毛。虽然可选的大模型产品很多&#xff0c;但普遍存在同质化、高分低能、实用性差、专业性不足的问题&#xff0c;哪怕是诸如ChatGPT、Gemini这样全球顶尖的大模型也会存在这种情况。 还有一点比较重要的是&#xff0c;由于大模型需要算力、算法、数据的基…

vue+ckEditor5 复制粘贴wold文字+图片并保存格式

第一步在vue2项目下安装 npm install --save ckeditor/ckeditor5-build-decoupled-document 第二 项目下新建一个plugins的文件夹将这个包ckeditor5-build-classic放入 &#xff08;包在页面最上方 有个下载按钮 可以下载&#xff09; 刚开始时 ckeditor5-build-classic文件…

「字符串」前缀函数|KMP匹配:规范化next数组 / LeetCode 28(C++)

目录 概述 思路 核心概念&#xff1a;前缀函数 1.前缀函数 2.next数组 1.考研版本 2.竞赛版本 算法过程 构建next数组 匹配过程 复杂度 Code 概述 为什么大家总觉得KMP难&#xff1f;难的根本就不是这个算法本身。 在互联网上你可以见到八十种KMP算法的next数组…

SQL 布尔盲注 (injection 第六关)

简介 SQL注入&#xff08;SQL Injection&#xff09;是一种常见的网络攻击方式&#xff0c;通过向SQL查询中插入恶意的SQL代码&#xff0c;攻击者可以操控数据库&#xff0c;SQL注入是一种代码注入攻击&#xff0c;其中攻击者将恶意的SQL代码插入到应用程序的输入字段中&am…

26.删除有序数组中的重复项---力扣

题目链接&#xff1a; . - 力扣&#xff08;LeetCode&#xff09;. - 备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/remove-duplicates-from-sorted-array/descript…

嵌入式学习——(Linux高级编程——线程)

线程 一、pthread 线程概述 pthread&#xff08;POSIX threads&#xff09;是一种用于在程序中实现多线程的编程接口。它与进程一样&#xff0c;可以用于实现并发执行任务&#xff0c;但与进程相比有一些不同的特点。 二、优点 1. 比多进程节省资源&#xff1a;进程在创建时…

PDPS软件 那智机器人 (丰田版)离线程序导出处理

在PDPS仿真软件中导出的那智机器人离线程序&#xff0c;一般是无法直接给TFD控制装置-那智机器人&#xff08;丰田式样版&#xff09;导入及识别使用。因此要对导出的程序进行转换编译处理&#xff0c;才能给TFD那智机器人&#xff08;丰田式样版&#xff09;导入离线程序。以下…

HarmonyOS 开发

环境 下载IDE 代码 import { hilog } from kit.PerformanceAnalysisKit; import testNapi from libentry.so; import { router } from kit.ArkUI; import { common, Want } from kit.AbilityKit;Entry Component struct Index {State message: string Hello HarmonyOS!;p…

类与对象(中(2))

开头 大家好啊&#xff0c;上一期内容我们介绍了类与对象中六大默认成员函数中的两种--->构造函数与析构函数&#xff0c;相信大家多少都形成了自己的独到见解。那么今天&#xff0c;我将继续就拷贝构造函数与运算符重载函数来展开讲解&#xff0c;话不多说&#xff0c;我们…

Python版《超级玛丽+源码》-Python制作超级玛丽游戏

小时候最喜欢玩的小游戏就是超级玛丽了&#xff0c;有刺激有又技巧&#xff0c;通关真的很难&#xff0c;救下小公主还被抓走了&#xff0c;唉&#xff0c;心累&#xff0c;最后还是硬着头皮继续闯&#xff0c;终于要通关了&#xff0c;之后再玩还是没有那么容易&#xff0c;哈…

十五年以来 — 战略性云平台服务的演进路径之全面呈现(含亚马逊、微软和谷歌)

Gartner每年都发布对全球IaaS平台进行评估的魔力象限报告。2023年底&#xff0c;Gartner将此项评估的名称改为“战略性云平台服务”&#xff08;Strategic cloud platform services&#xff09;&#xff0c;尽管其核心仍为IaaS&#xff0c;但是&#xff0c;毫无疑问&#xff0c…

手机云电脑游戏测评:ToDesk、易腾云、达龙云、青椒云四款对比分析

文章目录 &#x1f4d1; 引言一、背景概述测试目标 二、测试方案与评测标准2.1 测试设备2.2 评测标准 三、云电脑移动端实测3.1 ToDesk云电脑3.1.1 安装步骤与用户界面3.1.2 性能测试3.1.3 多场景适用性与兼容性3.1.4 性价比 3.2 易腾云电脑3.2.1 安装流程与用户界面3.2.2 帧率…

WebRTC为何成为视频开发领域的首选技术? EasyCVR视频转码助力无缝视频通信

随着互联网的飞速发展&#xff0c;视频通信已成为日常生活和工作中不可或缺的一部分。从在线教育、视频会议到远程医疗、在线直播&#xff0c;视频开发的需求日益增长。在这些应用场景中&#xff0c;选择何种技术来构建视频系统至关重要。 目前&#xff0c;在很多视频业务的开…

Golang | Leetcode Golang题解之第352题将数据流变为多个不相交区间

题目&#xff1a; 题解&#xff1a; type SummaryRanges struct {*redblacktree.Tree }func Constructor() SummaryRanges {return SummaryRanges{redblacktree.NewWithIntComparator()} }func (ranges *SummaryRanges) AddNum(val int) {// 找到 l0 最大的且满足 l0 < val…

Browserless 网页抓取:Playwright 中的 NodeJS

什么是 Playwright&#xff1f; Playwright 是一个用于 Web 测试和自动化的开源框架。基于 Node.js&#xff0c;由 Microsoft 开发&#xff0c;它通过单一 API 支持 Chromium、Firefox 和 WebKit。它可以在 Windows、Linux 和 macOS 上运行&#xff0c;并且兼容 TypeScript、J…

设计模式六大原则(一)--单一职责原则

摘要 单一职责原则是设计模式六大原则之一&#xff0c;强调一个类应该仅有一个引起它变化的原因&#xff0c;即每个类应仅负责一项职责。本文通过详细探讨单一职责原则的定义、实现方式、优缺点及其适用场景&#xff0c;揭示了其在软件设计中的核心地位。通过类的拆分、接口设…

TCP协议段中的六个标志位

目录 ACK SYN RST FIN PSH URG TCP报文格式中的六个标志位由6个比特构成&#xff0c;在通信双方基于TCP协议互相发送报文数据时可以通过报头中标志位来区别对方发送的报文数据的请示。 ACK 确认号是否有效。 接收端对所收到的报文进行检查&#xff0c;若未发现错误&…