当前位置: 首页 > 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();
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • JAVA Unsafe 类介绍
  • 封装websocket
  • 代码随想录算法训练营day50 | 深度优先搜索理论基础、98. 所有可达路径、广度优先搜索理论基础
  • 教程:使用Python裁剪TIF影像为多个自定义大小的小块(分割栅格)
  • K8S 探针
  • 创建、使用、删除 数据库
  • web的发展历史,互联网和万维网的关系
  • QT自定义信号槽
  • python 压力测试脚本
  • 主要内容和创新点、研究方法和实现途径这两个概念在学术研究和技术开发中各有侧重点
  • Matplotlib基本操作
  • kubernetes的pod基础
  • MySQL 数据库管理
  • 微信小程序——弹出隐私指引教程(含代码)
  • C:每日一练:单身狗(2.0版本)
  • 时间复杂度分析经典问题——最大子序列和
  • AHK 中 = 和 == 等比较运算符的用法
  • css的样式优先级
  • css属性的继承、初识值、计算值、当前值、应用值
  • If…else
  • iOS小技巧之UIImagePickerController实现头像选择
  • Java|序列化异常StreamCorruptedException的解决方法
  • Linux后台研发超实用命令总结
  • Linux快速复制或删除大量小文件
  • Python学习笔记 字符串拼接
  • Sequelize 中文文档 v4 - Getting started - 入门
  • VUE es6技巧写法(持续更新中~~~)
  • Vultr 教程目录
  • 搞机器学习要哪些技能
  • 来,膜拜下android roadmap,强大的执行力
  • 什么软件可以剪辑音乐?
  • 使用 Node.js 的 nodemailer 模块发送邮件(支持 QQ、163 等、支持附件)
  • 思否第一天
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • ​iOS实时查看App运行日志
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • ​TypeScript都不会用,也敢说会前端?
  • $$$$GB2312-80区位编码表$$$$
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • (16)Reactor的测试——响应式Spring的道法术器
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (附源码)ssm高校升本考试管理系统 毕业设计 201631
  • (回溯) LeetCode 40. 组合总和II
  • (十八)SpringBoot之发送QQ邮件
  • (游戏设计草稿) 《外卖员模拟器》 (3D 科幻 角色扮演 开放世界 AI VR)
  • (转)Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包
  • (转)Linux下编译安装log4cxx
  • (转)mysql使用Navicat 导出和导入数据库
  • (最全解法)输入一个整数,输出该数二进制表示中1的个数。
  • ..thread“main“ com.fasterxml.jackson.databind.JsonMappingException: Jackson version is too old 2.3.1
  • .net(C#)中String.Format如何使用
  • .Net7 环境安装配置
  • .NET单元测试使用AutoFixture按需填充的方法总结
  • .Net调用Java编写的WebServices返回值为Null的解决方法(SoapUI工具测试有返回值)