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

贝塞尔曲线(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.mzph.cn/news/648336.shtml

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

相关文章

SpringCloud-Knife4j文档聚合

在微服务架构下&#xff0c;如果给每个微服务都配置文档&#xff0c;那么每个微服务的接口文档都有自己独立的访问地址&#xff0c;这样要一个个打开每个微服务的文档非常麻烦。一般我们会采用聚合的办法&#xff0c;将所有微服务的接口整合到一个文档中&#xff0c;具体做法有…

DDColor:AI图像着色工具,优秀的黑白图像上色模型,支持双解码器!

前言 在数字图像处理领域&#xff0c;图像上色 一直是一个重要的课题。传统的图像上色方法通常需要人工干预&#xff0c;耗时且效果有限。 然而&#xff0c;随着深度学习技术的发展&#xff0c;自动图像上色模型逐渐成为了研究热点。 其中&#xff0c;DDColor 图像上色模型以…

vue3+elementPlus pc和小程序ai聊天文生图

websocket封装可以看上一篇文章 //pc端 <template><div class"common-layout theme-white"><el-container><el-aside><div class"title-box"><span>AI Chat</span></div><div class"chat-list&…

iOS推送通知

文章目录 一、推送通知的介绍1. 简介2. 通知的分类 二、本地通知1. 本地通知的介绍2. 实现本地通知3. 监听本地通知的点击 三、远程通知1. 什么是远程通知2. 为什么需要远程通知3. 远程通知的原理4. 如何做远程通知5. 远程通知证书配置6. 获取远程推送要用的 DeviceToken7. 测试…

代码随想录算法训练营第30天 | 回溯总结 + 3道Hard题目

今日任务 332.重新安排行程 51. N皇后 37. 解数独 总结 总结 回溯总结&#xff1a;代码随想录 回溯是递归的副产品&#xff0c;只要有递归就会有回溯&#xff0c;所以回溯法也经常和二叉树遍历&#xff0c;深度优先搜索混在一起&#xff0c;因为这两种方式都是用了递归。 …

《WebKit 技术内幕》学习之十三(1):移动WebKit

1 触控和手势事件 1.1 HTML5规范 随着电容屏幕的流行&#xff0c;触控操作变得前所未有的流行起来。时至今日&#xff0c;带有多点触控功能已经成为了移动设备的标准配置&#xff0c;基于触控的手势识别技术也获得巨大的发展&#xff0c;如使用两个手指来缩放应用的大小等。…

DAY11_(简易版)VUEElement综合案例

目录 1 VUE1.1 概述1.1.1 Vue js文件下载 1.2 快速入门1.3 Vue 指令1.3.1 v-bind & v-model 指令1.3.2 v-on 指令1.3.3 条件判断指令1.3.4 v-for 指令 1.4 生命周期1.5 案例1.5.1 需求1.5.2 查询所有功能1.5.3 添加功能 2 Element2.0 element-ui js和css和字体图标下载2.1 …

Vulnhub靶场DC-6

攻击机192.168.223.128 靶机192.168.223.134 主机发现:nmap -sP 192.168.223.0/24 端口扫描 nmap -sV -p- -A 192.168.223.134 开启了22 80端口&#xff0c;80是apache 2.4.25 先进入web界面看一下 用ip进不去&#xff0c;应该被重定向到wordy.com vim /etc/hosts 加上 19…

亚信安慧AntDB构建未来数据库典范

亚信安慧AntDB是一款数据库管理系统&#xff0c;它采用全球影响力大、社区繁荣、开放度高、生态增长迅速的PG内核。这款系统具有卓越的性能和稳定性&#xff0c;在全球范围内备受用户青睐。 与此同时&#xff0c;AntDB的社区也是充满活力的&#xff0c;用户可以在社区中交流经…

Vue中使用TypeScript:全面指南和最佳实践

🚀 欢迎来到我的专栏!专注于Vue3的实战总结和开发实践分享,让你轻松驾驭Vue3的奇妙世界! 🌈✨在这里,我将为你呈现最新的Vue3技术趋势,分享独家实用教程,并为你解析开发中的难题。让我们一起深入Vue3的魅力,助力你成为Vue大师! 👨‍💻💡不再徘徊,快来关注…

WebSocket服务端数据推送及心跳机制(Spring Boot + VUE):

文章目录 一、WebSocket简介&#xff1a;二、WebSocket通信原理及机制&#xff1a;三、WebSocket特点和优点&#xff1a;四、WebSocket心跳机制&#xff1a;五、在后端Spring Boot 和前端VUE中如何建立通信&#xff1a;【1】在Spring Boot 中 pom.xml中添加 websocket依赖【2】…

都学Python了,C++难道真的用不着了吗?

都学Python了&#xff0c;C难道真的用不着了吗&#xff1f; 在开始前我分享下我的经历&#xff0c;刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;两年时间从3k薪资涨到18k的&#xff0c; 我师父给了一些【C 】学习方法和资料&#xff0c;让我不断提升自己…

单片机介绍

本文为博主 日月同辉&#xff0c;与我共生&#xff0c;csdn原创首发。希望看完后能对你有所帮助&#xff0c;不足之处请指正&#xff01;一起交流学习&#xff0c;共同进步&#xff01; > 发布人&#xff1a;日月同辉,与我共生_单片机-CSDN博客 > 欢迎你为独创博主日月同…

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

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

拓展全球市场:静态代理IP成为跨境电商战略的关键工具

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

EIGRP实验

实验大纲 一、基本配置 1.构建网络拓扑结构图 2.路由器基本配置 3.配置PC 4.测试连通性 5.保存配置文件 二、配置EIGRP 1.查看路由表 2.配置EIGRP动态路由 3.查看路由器路由表 4.测试网络连通性 5.查看所有路由器的路由协议 6.保存配置文件 三、配置OSPF 1.配置…

SpringMVC-对静态资源的访问

1.工程中加入静态资源 在webapp下创建static文件夹&#xff0c;此文件夹专门放入静态资源 2.使项目可以处理静态资源的请求 在SpringMVC配置文件中添加以下语句 1.引入命名空间 xmlns:mvc"http://www.springframework.org/schema/mvc" xsi:schemaLocation“http…

Influxdb系列(二)influx Cli工具操作influxdb

一、安装并使用 influx CLI 客户端下载地址&#xff1a;https://docs.influxdata.com/influxdb/v2/tools/influx-cli/ [root192 bin]# tar -xvzf influxdb2-client-2.7.3-linux-amd64.tar.gz [root192 bin]# cp influx /usr/local/bin/二、客户端方式创建用户 相关的官网地址…

HNSW算法

From&#xff1a; HNSW算法(nsmlib/hnswlib)-CSDN博客HNSW算法的基本原理及使用 - 知乎 HNSW是一种广泛使用的ANN图索引结构&#xff0c;包括DiskANN、DF-GAS、SmartSSD等。本文档主要总结HNSW的结构与工作流程&#xff0c;便于后期研究其工作流程在迁移到CSD中存在的I/O问题…

JVM篇----第八篇

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