【图形学】贝塞尔曲线理论与实践

news2025/1/11 12:55:12

贝塞尔曲线(Bezier Curve)在计算机图形领域应用非常广泛,比如我们 CSS 动画、 Canvas 以及 Photoshop 等都可以看到贝塞尔曲线的身影。

贝塞尔曲线类型

贝塞尔曲线根据_控制点_的数量分为:

  • 一阶贝塞尔曲线(2 个控制点)
  • 二阶贝塞尔曲线(3 个控制点)
  • 三阶贝塞尔曲线(4 个控制点)
  • n阶贝塞尔曲线(n+1个控制点)

如何绘制出贝塞尔曲线

下图为一个三阶的贝塞尔曲线,包括四个控制点,分别为 P 0 , P 1 , P 2 , P 3 P_0,P_1,P_2,P_3 P0,P1,P2,P3

在这里插入图片描述

那我们通过控制点是怎么绘制出贝塞尔曲线的呢?

通过上图的三阶贝塞尔曲线举例,基本的步骤如下:

  1. 四个控制点通过先后顺序进行连接,形成了三条线段,也就是上图中的 P 0 P 1 , P 1 P 2 , P 2 P 3 P_0P_1,P_1P_2,P_2P_3 P0P1,P1P2,P2P3,然后通过一个参数 t , 其中 t ∈ [ 0 , 1 ] t,其中 t\in[0,1] t,其中t[0,1]该参数的值等于线段上某一个点距离起点的长度除以线段长度。就比如 P 0 P 1 P_0P_1 P0P1线段上有一个点 P 0 ′ ,此时 t 的值就是 P_0^{'},此时t的值就是 P0,此时t的值就是 P 0 P ′ P 0 P 1 \frac{P_0P_{'}}{P_0P_1} P0P1P0P,其中 P 0 ′ P_0^{'} P0位置如下图所示。

在这里插入图片描述

  1. 接下来对每一条线段做同样的操作,得到三个控制点 P 0 ′ , P 1 ′ , P 2 ′ P_0^{'},P_1^{'},P_2^{'} P0,P1,P2,如下图所示。

在这里插入图片描述

  1. 然后对这三个控制点重复第1步操作,得出两个控制点 P 0 ′ ′ , P 1 ′ ′ P_0^{''},P_1^{''} P0′′,P1′′,如下图所示。

在这里插入图片描述

  1. 最后再使用同样的方法可以得到,最终的一个点 P 0 ′ ′ ′ P_0^{'''} P0′′′,如下图所示,此时这个点就是贝塞尔曲线上的一个点。

在这里插入图片描述

通过控制t的值,由 0 增加至 1,就绘制出了一条由起点P_0至终点P_1的贝塞尔曲线。

通过下面这个动画直观感受一下绘制的过程:

在这里插入图片描述

求贝塞尔曲线上的点坐标

贝塞尔曲线的定义

给定n+1个控制点,则n次贝塞尔曲线的定义为

p ( t ) = ∑ i = 0 n P i B i , n ( t ) , t ∈ [ 0 , 1 ] p(t)=\sum_{i=0}^{n} {P_iB_{i,n}(t)},t\in[0,1] p(t)=i=0nPiBi,n(t),t[0,1]式子中 B i , n ( t ) B_{i,n}(t) Bi,n(t)是贝塞尔基数,表达式为 B i , n ( t ) = C n i t i ( 1 − t ) n − i , i = 0 , 1 , 2... , n B_{i,n}(t)=C_n^it^i(1-t)^{n-i},i=0,1,2...,n Bi,n(t)=Cniti(1t)ni,i=0,1,2...,n

1、一阶贝塞尔曲线

在这里插入图片描述

对于一阶贝塞尔曲线,我们可以通过几何知识,很容易根据t的值得出线段上那个点的坐标:

p ( t ) = P 0 + ( P 1 − P 0 ) t p(t) = P_0 + (P_1 - P_0)t p(t)=P0+(P1P0)t

然后可以得出:

p ( t ) = ( 1 − t ) P 0 + t P 1 , t ∈ [ 0 , 1 ] p(t) = (1 - t)P_0 + tP_1,t\in[0,1] p(t)=(1t)P0+tP1,t[0,1]

也可以直接根据定义得出 p ( t ) = ∑ i = 0 1 P i B i , 1 ( t ) = ( 1 − t ) P 0 + t P 1 p(t)=\sum_{i=0}^1{P_iB_{i,1}(t)}=(1-t)P_0+tP_1 p(t)=i=01PiBi,1(t)=(1t)P0+tP1

2、二阶贝塞尔曲线

在这里插入图片描述

对于二阶贝塞尔曲线,其实你可以理解为:在 P 0 P 1 P_0P_1 P0P1上利用一阶公式求出点 P 0 ′ P_0^{'} P0,然后在

P_1P_2上利用一阶公式求出点 P 1 ′ P_1^{'} P1,最后在 P 0 ′ P 1 ′ P_0^{'}P_1^{'} P0P1 上再利用一阶公式就可以求出最终贝塞尔曲线上的点 P 0 ′ ′ P_0{''} P0′′。具体推导过程如下:

P 0 ′ = ( 1 − t ) P 0 + t P 1 P_0^{'} = (1 - t)P_0 + tP_1 P0=(1t)P0+tP1

P 1 ′ = ( 1 − t ) P 1 + t P 2 P_1^{'} = (1 - t)P_1 + tP_2 P1=(1t)P1+tP2

P 0 ′ ′ = ( 1 − t ) P 0 ′ + t P 1 ′ P^{''}_0 = (1 - t)P_0^{'} + tP_1^{'} P0′′=(1t)P0+tP1

整理得到 P 0 ′ ′ = ( 1 − t ) 2 P 0 + 2 t ( 1 − t ) P 1 + t 2 P 2 P_{0}^{''}=(1-t)^2P_0+2t(1-t)P_1+t^2P_2 P0′′=(1t)2P0+2t(1t)P1+t2P2

3、三阶贝塞尔曲线

!在这里插入图片描述

与二阶贝塞尔曲线类似,可以通过相同的几何方法得出以下坐标公式:

p ( t ) = ( 1 − t ) 3 P 0 + 3 t ( 1 − t ) 2 P 1 + 3 t 2 ( 1 − t ) P 2 + t 3 P 3 , t ∈ [ 0 , 1 ] p(t) = (1 - t)^3P_0 + 3t(1 - t)^2P_1 + 3t^2(1 - t)P_2 + t^3P_3 , t\in[0, 1] p(t)=(1t)3P0+3t(1t)2P1+3t2(1t)P2+t3P3,t[0,1]

应用

三次贝塞尔曲线绘制圆

在这里插入图片描述

如图所示,使用三次贝塞尔曲线可以绘制出1/4个圆,那么通过二维变换就可以绘制出一个圆。 假设点 P 0 0 ( 0 , 1 ) 点 P 1 0 ( m , 1 ) 点 P 2 0 ( 1 , m ) 点 P 3 0 ( 1 , 0 ) 假设点P_0^0(0,1)点P_1^0(m,1)点P_2^0(1,m)点P_3^0(1,0) 假设点P00(0,1)P10(m,1)P20(1,m)P30(1,0)
三次贝塞尔曲线的表达式为 p ( t ) = ( 1 − t ) 3 P 0 + 3 t ( 1 − t ) 2 P 1 + 3 t 2 ( 1 − t ) P 2 + t 3 P 3 p(t) = (1 - t)^3P_0 + 3t(1 - t)^2P_1 + 3t^2(1 - t)P_2 + t^3P_3 p(t)=(1t)3P0+3t(1t)2P1+3t2(1t)P2+t3P3
对于圆弧的中点,t=0.5有 ( 2 2 , 2 2 ) = 1 8 P 0 0 + 3 8 P 1 0 + 3 8 P 2 2 + 1 8 P 3 0 (\frac{\sqrt{2}}{2},\frac{\sqrt{2}}{2})=\frac{1}{8}P_0^0+\frac{3}{8}P_1^0+\frac{3}{8}P_2^2+\frac{1}{8}P_3^0 (22 ,22 )=81P00+83P10+83P22+81P30
带入点的坐标可以得到 m = 4 ( 2 − 1 ) 3 ≈ 0.5523 m=\frac{4(\sqrt{2}-1)}{3}\approx0.5523 m=34(2 1)0.5523

/*曲线的轨迹可以用直线去拟合也可以用一个个像素点*/
void CBezierCurve3::DrawCurve(CDC* pDC)
{
	CPen NewPen, *OldPen;
	NewPen.CreatePen(PS_SOLID, 3, RGB(255, 0,0));
	OldPen=pDC->SelectObject(&NewPen);
	CPoint2 p00 = m_p[0], p10 = m_p[1], p20 = m_p[2], p30 = m_p[3];//控制点
	CPoint2 p01, p11, p21, p02, p12, p03;//插值点
	double step = 0.001;
	/*pDC->MoveTo(int(m_p[0].m_x + 0.5), int(m_p[0].m_y + 0.5));*/
	for (double t = 0.00; t < 1; t += step) {
		p01 = (1 - t) * p00 + t * p10;
		p11 = (1 - t) * p10 + t * p20;
		p21 = (1 - t) * p20 + t * p30;
		p02 = (1 - t) * p01 + t * p11;
		p12 = (1 - t) * p11 + t * p21;
		p03 = (1 - t) * p02 + t * p12;
		/*pDC->LineTo(int(p03.m_x + 0.5), int(p03.m_y + 0.5));*/
		auto c = LinearInterP(t,  CRGB(1, 0, 0), CRGB(1.0, 0, 1.0));
		pDC->SetPixelV(p03.m_x, p03.m_y, COLOR(c));
	}
	/*pDC->LineTo(int(m_p[3].m_x + 0.5), int(m_p[3].m_y + 0.5));*/
	pDC->SelectObject(OldPen);
	NewPen.DeleteObject();
}
#define M 4.0/3*(sqrt(2)-1)
C贝塞尔曲线View::C贝塞尔曲线View() noexcept
{
	CPoint2 p[4] = { {0,1},{M,1} ,{1,M},{1,0} };
	for (int i = 0; i < 4; i++) {
		curve[i].ReadPoint(p);
		transform[i].SetMatrix(curve[i].m_p, 4);
		
	}
	transform[0].Scale(100, 100);
	transform[1].Scale(-100,100);
	transform[2].Scale(-100,-100);
	transform[3].Scale(100,-100);
}

在这里插入图片描述

多条贝塞尔曲线拼接

假设有两段三贝塞尔曲线p(t)和q(t)。曲线的控制点分别是
P 0 , P 1 , P 2 , P 3 , Q 0 , Q 1 , Q 2 , Q 3 P_0,P_1,P_2,P_3,Q_0,Q_1,Q_2,Q_3 P0,P1,P2,P3,Q0,Q1,Q2,Q3如果这两段曲线要拼接,那么就要求 P 2 , Q 0 ( P 3 ) , Q 1 P_2,Q_0(P_3),Q_1 P2,Q0(P3),Q1三点共线。
在这里插入图片描述

  • 二次贝塞尔曲线绘制爱心
  void CBezierCurve2::DrawCurve(CDC* pDC)
{
	CPen NewPen, * OldPen;
	NewPen.CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
	OldPen = pDC->SelectObject(&NewPen);
	CPoint2 p00 = m_p[0], p10 = m_p[1], p20 = m_p[2];//控制点
	CPoint2 p01, p11,  p02;//插值点
	double step = 0.00001;
	/*pDC->MoveTo(int(m_p[0].m_x + 0.5), int(m_p[0].m_y + 0.5));*/
	for (double t = 0.00; t < 1; t += step) {
		p01 = (1 - t) * p00 + t * p10;
		p11 = (1 - t) * p10 + t * p20;
		
		p02 = (1 - t) * p01 + t * p11;
		
		/*pDC->LineTo(int(p03.m_x + 0.5), int(p03.m_y + 0.5));*/
		auto c = LinearInterP(t, CRGB(1, 0, 0), CRGB(1.0, 0, 1.0));
		pDC->SetPixelV(p02.m_x, p02.m_y, COLOR(c));
	}
	/*pDC->LineTo(int(m_p[3].m_x + 0.5), int(m_p[3].m_y + 0.5));*/
	pDC->SelectObject(OldPen);
	NewPen.DeleteObject();
}
C贝塞尔曲线View::C贝塞尔曲线View() noexcept
{
	CPoint2 P2[3] = { {0,0},{1.4,1.8},{2,0} };
	CPoint2 P3[3] = { {2,0},{2.3,-3 * 2.3 + 6},{0,-2.6} };\\P2[1],P2[2](P3[0]),P3[1]三点共线
	curve2[0].ReadPoint(P2);
	transform[0].SetMatrix(curve2[0].m_p, 3);
	transform[0].Scale(100, 100);
	curve2[1].ReadPoint(P2);
	transform[1].SetMatrix(curve2[1].m_p, 3);
	transform[1].Scale(-100, 100);
	curve2[2].ReadPoint(P3);
	transform[2].SetMatrix(curve2[2].m_p, 3);
	transform[2].Scale(100, 100);
	curve2[3].ReadPoint(P3);
	transform[3].SetMatrix(curve2[3].m_p, 3);
	transform[3].Scale(-100, 100);

}

在这里插入图片描述

需要项目源代码请评论区留言或私信

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

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

相关文章

首助编辑高手:掌控PDF,工作流程更顺畅!

在繁忙的工作中&#xff0c;我们时常需要处理各种PDF文档。这些文档可能来自客户、同事或自己创建。然而&#xff0c;直接编辑PDF往往不是一件容易的事&#xff0c;需要专业的工具来辅助。今天&#xff0c;我要为大家介绍一款强大的PDF编辑工具——首助编辑高手&#xff0c;它将…

夏季使用打包机需要注意些什么

夏季由于非常炎热的特点&#xff0c;很多设备的使用都需要非常关注它的使用安全&#xff0c;打包机也不例外&#xff0c;尤其是24小时生产的企业&#xff0c;对于设备的使用注意事项更应该多加注意&#xff0c;那么夏季使用打包机到底该注意些什么呢&#xff1f;艾讯认为至少应…

01 Redis的特性+下载安装启动

1.1 NoSQL NoSQL&#xff08;“non-relational”&#xff0c; “Not Only SQL”&#xff09;&#xff0c;泛指非关系型的数据库。 键值存储数据库 &#xff1a; 就像 Map 一样的 key-value 对。如Redis文档数据库 &#xff1a; NoSQL 与关系型数据的结合&#xff0c;最像关系…

eNSP 实验 两台AR配置同网段

实验1&#xff1a;eNSP 两台AR配置同网段 目的&#xff1a;创建两台AR&#xff0c;配置IP互相ping通 拓扑结构&#xff1a; 首先创建一个AR3260 然后创建一个AR2220 然后同轴电缆连接一下 先配置AR2220。 1、切管理员&#xff1a;system-view 进入千兆位以太网 0/0/0 interf…

如何抓住短剧“狂飙”风口?腾讯微搭发布一站式短剧平台解决方案

“制作成本50万&#xff0c;充值流水一个亿。” “7天写完剧本&#xff0c;一周拍完一部剧。” 短剧热度不断提升&#xff0c;情节快节奏、单集时长短、竖屏呈现等特点更迎合现代社会快节奏下的碎片化观看需求。2023年以来&#xff0c;小程序短剧高速发展&#xff0c;数据显示…

electron-builder vue 打包后element-ui字体图标不显示问题

当使用electron打包完成的时候&#xff0c;启动项目发现使用的element-ui字体图标没显示都变成了小方块&#xff0c;并出现报错&#xff0c;请看下图&#xff1a; 解放方法&#xff1a; 在vue.config.js中设置 customFileProtocol字段&#xff1a;pluginOptions: {electronBui…

Vulnhub靶场MATRIX-BREAKOUT: 2 MORPHEUS

攻击机192.168.223.128 目标机192.168.223.140 主机发现nmap -sP 192.168.223.0/24 端口扫描nmap -p- 192.168.223.140 开启了 22&#xff0c;80&#xff0c;81三个端口 看一下web界面 是inguardians 写给jaybeale的信&#xff0c;说计算机被密码锁住了&#xff0c;至少…

【创建vue项目的两种方式】

Vue环境搭建 NodeJs安装包安装淘宝镜像 环境搭建webpack安装全局安装vue/cli查看模板创建项目1.webpack2. vue-cli NodeJs安装包 下载链接&#xff1a;官网链接 下载下来后&#xff0c;直接傻瓜式的安装即可。 通过在cmd控制台输入以下命令查看是否安装成功 node -v因为适配某…

云计算项目六:升级网站运行平台|部署缓存服务|数据迁移|部署集群

升级网站运行平台&#xff5c;部署缓存服务&#xff5c;数据迁移&#xff5c;部署集群 案例1&#xff1a;升级网站运行平台步骤一&#xff1a;清除当前配置步骤二&#xff1a;部署LNMP步骤三&#xff1a;测试配置 案例2&#xff1a;部署内存存储服务步骤一&#xff1a;部署redi…

Linux基础指令【下篇】

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 1.时间指令----date1…

翻译: 使用 GPT-4 将您的 Streamlit 应用程序提升到一个新的水平一

帮助您更快地设计、调试和优化 Streamlit 应用的专业技巧 设计和扩展 Streamlit 应用程序可能是一项艰巨的任务&#xff01;作为开发人员&#xff0c;我们经常面临一些挑战&#xff0c;例如设计良好的 UI、快速调试我们的应用程序以及快速制作它们。 如果有一个工具可以加快速…

【GitHub项目推荐--不错的Flutter项目】【转载】

01 可定制的图表库 FL Chart是一个高度可定制的 Flutter 图表库&#xff0c;支持折线图、条形图、饼图、散点图和雷达图 。 项目地址&#xff1a;https://github.com/imaNNeoFighT/fl_chart LineChart BarChart PieChart Sample1 Sample2 Sample3 …

linux 查看系统日记

一 关于 journalctl 简介&#xff1a; 在window想查看日记通常是通过事件查看器&#xff0c;在linux则可以通过journalctl -xe&#xff0c;journalctl是 Systemd 日志管理工具的一部分&#xff0c;用于检索和显示系统日志。 二 使用journalctl 查看日记&#xff1a; journalct…

关于axios给后端发送数据的问题

这里需要用的插件&#xff1a;qs.js&#xff0c;是前端给后端发送的数组&#xff0c;需要序列化所以要用到这个插件&#xff0c;这里就提取连接在这里&#xff0c;需要的自提&#xff0c;需要导如进来&#xff0c;别忘记了 链接&#xff1a;https://pan.baidu.com/s/1qyD8v9wfd…

JMeter:性能测试和压力测试工具详解

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号&#xff1a;互联网杂货铺&#xff0c;回复1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;薪资嘎嘎涨 JMeter简介 JMeter时Apache下基于java的一款性能测试…

antdesignvue中使用VNode写法

1、使用场景 如图&#xff1a;消息提示框中&#xff0c;将数据中的数据单独一行显示 2、代码 let errorList res.result; //后端返回的数据例&#xff1a; ["1. 数据格式不正确","2. 数据已存在"]if(errorList&&errorList.length!0){this.$notif…

时限挑战 —— 深度解析Pytest插件 pytest-timeout!

在软件开发中&#xff0c;测试用例的执行时间通常是一个关键考虑因素。Pytest插件 pytest-timeout 提供了一个强大的插件&#xff0c;允许你设置测试用例的超时时间。本文将深入介绍 pytest-timeout 插件的基本用法和实际案例&#xff0c;助你精确掌控测试用例的执行时限。 什么…

备忘录模式-C#实现

该实例基于WPF实现&#xff0c;直接上代码&#xff0c;下面为三层架构的代码。 目录 一 Model 二 View 三 ViewModel 一 Model using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace 设计模式练…

JVM篇----第八篇

系列文章目录 文章目录 系列文章目录前言一、标记清除算法( Mark-Sweep)二、复制算法(copying)三、标记整理算法(Mark-Compact)前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分…

Docker应用-自定义网络连接

Docker网络 文章目录 Docker网络网络的基础配置自定义网络网络连通 网络的基础配置 大量的互联网应用服务包括多个服务组件&#xff0c;这往往需要多个容器之间通过网络通信进行互相配合。 目前Docker提供了映射容器端口到宿主机主机和容器互联机制来为容器提供网络服务&…