当前位置: 首页 > news >正文

实现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
多项式函数
-----------------------------------nP(u)= Σ  Pk × BEZ(u)            0≤u≤1k=0           k,n
-----------------------------------
混合函数
-----------------------------------k       n-kBEZ(u)=C(n,k) × u × (1-u)       0≤u≤1k,n-----------------------------------
二项式系数
-----------------------------------n!C(n,k)=——————————————————k! × (n-k)!-----------------------------------不想使用 点 结构,全部用指针数组表示点集
*/
#ifndef ZMBEZIER_H
#define ZMBEZIER_Hclass 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();
}

相关文章:

实现Bezier样条曲线

1.给出n1 个控制点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_ctrlPoin…...

MySQL中的EXPLAIN的详解

一、介绍 官网介绍&#xff1a; https://dev.mysql.com/doc/refman/5.7/en/explain-output.htmlhttps://dev.mysql.com/doc/refman/8.0/en/explain-output.htmlexplain&#xff08;执行计划&#xff09;&#xff0c;使用explain关键字可以模拟优化器执行sql查询语句&#xff…...

LearnOpenGL——SSAO学习笔记

LearnOpenGL——SSAO学习笔记 SSAO一、基本概念二、样本缓冲三、法向半球四、随机核心转动五、SSAO着色器六、环境遮蔽模糊七、应用SSAO遮蔽因子 SSAO 一、基本概念 环境光照是我们加入场景总体光照中的一个固定光照常量&#xff0c;它被用来模拟光的散射(Scattering)。散射应…...

[C语言]-基础知识点梳理-文件管理

前言 各位师傅们好&#xff0c;我是qmx_07&#xff0c;今天给大家讲解文件管理的相关知识&#xff0c;也就是常见的 读取&#xff0c;删除一类的操作 文件 为什么要使用文件&#xff1f; 程序的数据是存储在电脑的内存中&#xff0c;如果程序退出&#xff0c;内存回收&…...

pcdn闲置带宽被动收入必看教程。第五讲:光猫更换和基础设置

PCDN闲置带宽被动收入必看教程 —— 第五讲&#xff1a;光猫更换和基础设置 为了从闲置带宽中获得被动收入&#xff0c;高效的网络设备至关重要。运营商提供的光猫通常能满足日常家用需求&#xff0c;但对于PCDN应用来说&#xff0c;它们可能不足以提供所需的高性能和稳定性。…...

工业数据采集网关简介-天拓四方

随着工业4.0和物联网&#xff08;IoT&#xff09;技术的深入发展&#xff0c;工业数据采集网关作为连接现场设备与上层管理系统的关键节点&#xff0c;其在智能工厂中的作用愈发凸显。本文将深入探讨工业数据采集网关的功能、特点、应用场景及其实操性&#xff0c;以期为读者提…...

Java 调整字符串,验证码生成

package text7;public class ZiFanz {public static void main(String[] args) {//1.定义两个字符串String strA "abcde";String strB "deabc";//2.abcde->bcdea->cdeab->deabc旋转字符串//旋转并比较boolean result cheak(strA, strB);System…...

【专题】全球商用服务机器人市场研究(2023)报告合集PDF分享(附原数据表)

原文链接&#xff1a;https://tecdat.cn/?p37366 近年来&#xff0c;随着人工智能、物联网和自动化技术的不断进步&#xff0c;商用服务机器人行业迅速崛起&#xff0c;展现出广阔的发展前景。从最初的实验室研发到如今的规模化应用&#xff0c;商用服务机器人已逐渐成为各行…...

SQL UA注入 (injection 第十八关)

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

初阶数据结构之计数排序

非比较排序 计数排序 计数排序⼜称为鸽巢原理&#xff0c;是对哈希直接定址法的变形应⽤。 操作步骤&#xff1a; 1&#xff09;统计相同元素出现次数 2&#xff09;根据统计的结果将序列回收到原来的序列中 #include "CountSort.h" void Count(int* arr, int n)…...

【开端】记一次诡异的接口排查过程

一、绪论 最近碰到这么一个情况&#xff0c;接口请求超时。前提是两台服务器间的网络是畅通的&#xff0c;端口也是通&#xff0c;应用代码也是通。意思是在应用上&#xff0c;接口没有任何报错&#xff0c;能正常返回数据。客户端到服务端接口也能通&#xff0c;但是接收不到服…...

jenkins最佳实践(二):Pipeline流水线部署springCloud微服务项目

各位小伙伴们大家好呀&#xff0c;我是小金&#xff0c;本篇文章我们将介绍如何使用Pipeline流水线部署我们自己的微服务项目&#xff0c;之前没怎么搞过部署相关的&#xff0c;以至于构建流水线的过程中中也遇到了很多自己以前没有考虑过的问题&#xff0c;特写此篇&#xff0…...

第2章 C语言基础知识

第2章 C语言基础知识 1.printf()函数 在控制台输出数据&#xff0c;需要使用输出函数&#xff0c;C语言常用的输出函数为printf()。 printf()函数为格式化输出函数&#xff0c;其功能是按照用户指定的格式将数据输出到屏幕上。 printf(“格式控制字符串”,[输出列表]); 格式控…...

鹭鹰优化算法SBOA优化RBF神经网络的扩散速度实现多数入多输出数据预测,可以更改数据集(MATLAB代码)

一、鹭鹰优化算法介绍 鹭鹰优化算法&#xff08;Secretary Bird Optimization Algorithm, SBOA&#xff09;是一种新型的元启发式算法&#xff0c;它于2024年4月由Youfa Fu等人提出&#xff0c;并发表在SCI人工智能二区顶刊《Artificial Intelligence Review》上。该算法的灵感…...

MySQL基础练习题48-连续出现的数字

目录 题目 准备数据 分析数据 题目 找出所有至少连续出现三次的数字。 准备数据 ## 创建库 create database db; use db;## 创建表 Create table If Not Exists Logs (id int, num int)## 向表中插入数据 Truncate table Logs insert into Logs (id, num) values (1, 1) i…...

webrtc学习笔记2

音视频采集和播放 打开摄像头并将画面显示到页面 1. 初始化button、video控件 2. 绑定“打开摄像头”响应事件onOpenCamera 3. 如果要打开摄像头则点击 “打开摄像头”按钮&#xff0c;以触发onOpenCamera事件的调用 4. 当触发onOpenCamera调用时 a. 设置约束条件&#xff0c…...

Simple RPC - 06 从零开始设计一个服务端(上)_注册中心的实现

文章目录 Pre核心内容服务端结构概述注册中心的实现1. 注册中心的架构2. 面向接口编程的设计3. 注册中心的接口设计4. SPI机制的应用 小结 Pre Simple RPC - 01 框架原理及总体架构初探 Simple RPC - 02 通用高性能序列化和反序列化设计与实现 Simple RPC - 03 借助Netty实现…...

【深度学习】基于Transformers的大模型推理框架

本文旨在介绍基于transformers的decoder-only语言模型的推理框架。与开源推理框架不同的是&#xff1a; 本框架没有利用额外的开源推理仓库&#xff0c;仅基于huggingface&#xff0c;transformers&#xff0c;pytorch等原生工具进行推理&#xff0c;适合新手学习大模型推理流…...

电脑监控怎样看回放视频?一键解锁电脑监控回放,守护安全不留死角!高效员工电脑监控,回放视频随时查!

你是否曾好奇那些键盘敲击背后的秘密&#xff1f;电脑监控不仅是守护企业安全的隐形盾牌&#xff0c;更是揭秘高效与合规的魔法镜&#xff01;一键解锁安企神监控回放&#xff0c;就像打开时间宝盒&#xff0c;让过去的工作瞬间跃然眼前。无论是精彩瞬间还是潜在风险&#xff0…...

【一起学Rust | 框架篇 | Tauri2.0框架】tauri中rust和前端的相互调用(rust调用前端)

文章目录 前言1. rust中调用前端2. 如何向前端发送事件3. 前端监听事件4. 执行js代码 前言 近期Tauri 2.0 rc版本发布&#xff0c;2.0版本迎来第一个稳定版本&#xff0c;同时官方文档也进行了更新。Tauri是一个使用Rust构建的框架&#xff0c;可以让你使用前端技术来构建桌面…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

LabVIEW双光子成像系统技术

双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制&#xff0c;展现出显著的技术优势&#xff1a; 深层组织穿透能力&#xff1a;适用于活体组织深度成像 高分辨率观测性能&#xff1a;满足微观结构的精细研究需求 低光毒性特点&#xff1a;减少对样本的损伤…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

xmind转换为markdown

文章目录 解锁思维导图新姿势&#xff1a;将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件&#xff08;ZIP处理&#xff09;2.解析JSON数据结构3&#xff1a;递归转换树形结构4&#xff1a;Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...