贝塞尔曲线(Bezier Curve)原理、公式推导及matlab代码实现

本文主要是介绍贝塞尔曲线(Bezier Curve)原理、公式推导及matlab代码实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

参考链接

定义

直观理解

 公式推导

一次贝塞尔曲线(线性公式)

二次贝塞尔曲线(二次方公式)

 三次贝塞尔曲线(三次方公式)

n次贝塞尔曲线(一般参数公式)

代码实现


参考链接

贝塞尔曲线(Bezier Curve)原理及公式推导_bezier曲线-CSDN博客

贝塞尔曲线(Bezier Curve)原理、公式推导及matlab代码实现-CSDN博客

贝塞尔曲线——这个是可以在线控制点来绘制贝塞尔曲线的网站

定义

贝塞尔曲线用于计算机图形绘制形状,CSS 动画和许多其他地方。

贝塞尔曲线(Bezier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线定义:起始点、终止点(也称锚点)、控制点。通过调整控制点,贝塞尔曲线的形状会发生变化。 贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如PhotoShop等。

1962年,法国数学家Pierre Bézier第一个研究了这种矢量绘制曲线的方法,并给出了详细的计算公式,因此按照这样的公式绘制出来的曲线就用他的姓氏来命名,称为贝塞尔曲线。

贝塞尔曲线的一些特性:

  • 使用n个控制点\{P_1,P_2,\cdots ,P_n\}来控制曲线的形状
  • 曲线通过起始点P_1 和终止点P_n,接近但不通过中间点P_2\sim P_{n-1}
  • 曲线的阶次等于控制点的数量减一。 对于两个点我们能得到一条线性曲线(直线),三个点 — 一条二阶曲线,四个点 — 一条三阶曲线。

  • 曲线总是在控制点的凸包内部

由于最后一个属性,在计算机图形学中,可以优化相交测试。如果凸包不相交,则曲线也不相交。因此,首先检查凸包的交叉点可以非常快地给出“无交叉”结果。检查交叉区域或凸包更容易,因为它们是矩形,三角形等(见上图),比曲线简单的多。

直观理解

Step 1. 在二维平面内选三个不同的点并依次用线段连接

Step 2. 在线段ABBC上找到DE两点,使得\frac{AD}{DB}=\frac{BE}{EC}

 Step 3. 连接DE,并在DE上找到F点,使其满足\frac{DE}{FE}=\frac{AD}{DB}=\frac{BE}{EC}(抛物线的三切线定理)

Step 4. 找出符合上述条件的所有点

 上述为一个二阶贝塞尔曲线。同样的有n阶贝塞尔曲线:

曲线图示
一阶

二阶

三阶

四阶

五阶

 公式推导

一次贝塞尔曲线(线性公式)

定义:给定点P_0P_1,线性贝塞尔曲线只是一条两点之间的直线,这条线由下式给出,且其等同于线性插值:

B(t)=P_0+(P_0-P_1)t=(1-t)P_0+tP_1,t\in [0,1]

其中,公式里的P_0P_1同步表示为其xy轴坐标。

假设P_0坐标为(a.b)P_1坐标为(c,d)P_2坐标为(x,y),则有:

 \frac{x-a}{c-x}=\frac{t}{1-t}\Rightarrow x=(1-t)a+tc

同理有:

\frac{y-b}{d-y}=\frac{t}{1-t}\Rightarrow y=(1-t)b+td

于是可将上式简写为:


B(t)=(1-t)P_0+tP_1,t\in [0,1]

二次贝塞尔曲线(二次方公式)

定义:二次贝塞尔曲线的路径由给定点P_0P_1P_2的函数B(t)给出:

B(t)=(1-t)^2P_0+2t(1-t)P_1+t^2P_2,t\in [0,1]

 假设P_0P_1上的点为AP_1P_2上的点为BAB上的点为C(也即C为曲线上的点。则根据一次贝塞尔曲线公式有:

A=(1-t)P_0+tP_1

B=(1-t)P_1+tP_2

C=(1-t)A+tB

将上式中AB带入C中,即可得到二次贝塞尔曲线的公式:


B(t)=(1-t)^2P_0+2t(1-t)P_1+t^2P_2,t\in [0,1]

 三次贝塞尔曲线(三次方公式)

同理可得三次贝塞尔曲线公式:

B(t)=(1-t)^3P_0+3t(1-t)^2P_1+3t^2(1-t)P_2+t^3P_3,t\in [0,1]

n次贝塞尔曲线(一般参数公式)

定义:给定点P_0P_1\cdots ,P_n,则n次贝塞尔曲线由下式给出:

n次贝塞尔曲线的公式可由如下递归表达:

 \mathrm{P}_{0}^{\mathrm{n}}=(1-\mathrm{t}) \mathrm{P}_{0}^{\mathrm{n}-1}+\mathrm{tP}_{1}^{\mathrm{n}-1}, \mathrm{t} \in[0,1]

进一步可以得到贝塞尔曲线的递推计算公式:

\mathrm{P}_{\mathrm{i}}^{\mathrm{k}}\left\{\begin{array}{l} \mathrm{P}_{\mathrm{i}}, \mathrm{k}=0 \\ (1-\mathrm{t}) \mathrm{P}_{\mathrm{i}}^{\mathrm{k}-1}+\mathrm{tP}_{\mathrm{i}+1}^{\mathrm{k}-1}, \mathrm{k}=1,2, \ldots, \mathrm{n} ; \mathrm{i}=0,1, \ldots, \mathrm{n}-\mathrm{k} \end{array}\right.

代码实现

首先来看不同阶数的贝塞尔曲线公式,来找共同点:

N=2:         B(t)=(1-t)P_0+tP_1,t\in [0,1]

N=3:         B(t)=(1-t)^2P_0+2t(1-t)P_1+t^2P_2,t\in [0,1]

N=4:        B(t)=(1-t)^3P_0+3t(1-t)^2P_1+3t^2(1-t)P_2+t^3P_3,t\in [0,1]

可将贝塞尔曲线一般参数公式中的表达式用如下方式表示:
设有常数 a,b 和 c,则该表达式可统一表示为如下形式:

a(1-t)^bt^cP_n

根据上面的分析就可以总结出 a,b,c 对应的取值规则:

b: (N - 1)递减到 0 (b 为 1-t 的幂)
c: 0 递增到 (N - 1) (c 为 t 的幂)
a: 在 N 分别为 1,2,3,4,5 时将其值用如下形式表示: 

N=1:---------1
N=2:--------1 1
N=3:------1 2 1
N=4:-----1 3 3 1
N=5:---1 4 6 4 1
a 值的改变规则为: 杨辉三角

-------------------------------------------------------------------

理论基础有了,开始写代码

a 值用杨辉三角计算,b ,c 值在for 循环里计算,P_n从传入的点坐标读取。

step1:首先使用杨辉三角的方式生成a值
 

    N = len(control_points)ta = np.zeros((N, N))# 初始化杨辉三角左右两边的值为1for i in range(N):ta[i, 0] = 1ta[i, i] = 1# 计算杨辉三角for row in range(2, N):for col in range(1, row):ta[row, col] = ta[row-1, col-1] + ta[row-1, col]

step2:生成贝塞尔曲线上的点

    p = np.zeros((M, 2))for i in range(M):t = i / M  # 确定每一个点的比例for k in range(N):c = k  # 分别确定 a, b, c 三个系数b = N - c - 1  # 分别确定 a, b, c 三个系数a = ta[N-1, k]  # 分别确定 a, b, c 三个系数# 确定点的 x 和 y 坐标p[i, 0] += a * (1 - t)**b * t**c * control_points[k, 0]p[i, 1] += a * (1 - t)**b * t**c * control_points[k, 1]

完整代码


# N表示控制点个数,M表示时间步
import numpy as np
from scipy.special import combdef calculate_bezier_curve(control_points, M=1000):N = len(control_points)ta = np.zeros((N, N))# 初始化杨辉三角左右两边的值为1for i in range(N):ta[i, 0] = 1ta[i, i] = 1# 计算杨辉三角for row in range(2, N):for col in range(1, row):ta[row, col] = ta[row-1, col-1] + ta[row-1, col]p = np.zeros((M, 2))for i in range(M):t = i / M  # 确定每一个点的比例for k in range(N):c = k  # 分别确定 a, b, c 三个系数b = N - c - 1  # 分别确定 a, b, c 三个系数a = ta[N-1, k]  # 分别确定 a, b, c 三个系数# 确定点的 x 和 y 坐标p[i, 0] += a * (1 - t)**b * t**c * control_points[k, 0]p[i, 1] += a * (1 - t)**b * t**c * control_points[k, 1]return p# 示例调用
control_points = np.array([(0, 0), (1, 2), (2, 0)])
result_points = calculate_bezier_curve(control_points)# 打印结果
print(result_points)# 可视化
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 6))
plt.plot(result_points[:, 0], result_points[:, 1], label='Bezier Curve')

下图是一个生成的二阶贝塞尔曲线(有3个控制点)

 另外一种实现方式:

def bezier_curve(points, n_times=1000):"""Generate a Bezier curve from control points.Args:points (list of tuples): control points.n_times (int): number of time steps (resolution of the curve).Returns:list of tuples: points on the bezier curve."""n_points = len(points)t = np.linspace(0, 1, n_times)curve = np.zeros((n_times, 2))for i in range(n_points):binom = comb(n_points - 1, i) # 计算二项式系数,即组合数。表示从 n_points - 1 个元素中选择 i 个元素的方式有多少种。curve += np.outer(binom * (t ** i) * ((1 - t) ** (n_points - 1 - i)), points[i])return curvecontrol_points1 = [(0, 0), (1, 2), (2, 0)]
bezier1 = bezier_curve(control_points1)
print(bezier1)

这篇关于贝塞尔曲线(Bezier Curve)原理、公式推导及matlab代码实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/623549

相关文章

Python如何实现读取csv文件时忽略文件的编码格式

《Python如何实现读取csv文件时忽略文件的编码格式》我们再日常读取csv文件的时候经常会发现csv文件的格式有多种,所以这篇文章为大家介绍了Python如何实现读取csv文件时忽略文件的编码格式... 目录1、背景介绍2、库的安装3、核心代码4、完整代码1、背景介绍我们再日常读取csv文件的时候经常

Golang中map缩容的实现

《Golang中map缩容的实现》本文主要介绍了Go语言中map的扩缩容机制,包括grow和hashGrow方法的处理,具有一定的参考价值,感兴趣的可以了解一下... 目录基本分析带来的隐患为什么不支持缩容基本分析在 Go 底层源码 src/runtime/map.go 中,扩缩容的处理方法是 grow

Go 1.23中Timer无buffer的实现方式详解

《Go1.23中Timer无buffer的实现方式详解》在Go1.23中,Timer的实现通常是通过time包提供的time.Timer类型来实现的,本文主要介绍了Go1.23中Timer无buff... 目录Timer 的基本实现无缓冲区的实现自定义无缓冲 Timer 实现更复杂的 Timer 实现总结在

基于Python实现多语言朗读与单词选择测验

《基于Python实现多语言朗读与单词选择测验》在数字化教育日益普及的今天,开发一款能够支持多语言朗读和单词选择测验的程序,对于语言学习者来说无疑是一个巨大的福音,下面我们就来用Python实现一个这... 目录一、项目概述二、环境准备三、实现朗读功能四、实现单词选择测验五、创建图形用户界面六、运行程序七、

Vue中动态权限到按钮的完整实现方案详解

《Vue中动态权限到按钮的完整实现方案详解》这篇文章主要为大家详细介绍了Vue如何在现有方案的基础上加入对路由的增、删、改、查权限控制,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、数据库设计扩展1.1 修改路由表(routes)1.2 修改角色与路由权限表(role_routes)二、后端接口设计

C#集成DeepSeek模型实现AI私有化的流程步骤(本地部署与API调用教程)

《C#集成DeepSeek模型实现AI私有化的流程步骤(本地部署与API调用教程)》本文主要介绍了C#集成DeepSeek模型实现AI私有化的方法,包括搭建基础环境,如安装Ollama和下载DeepS... 目录前言搭建基础环境1、安装 Ollama2、下载 DeepSeek R1 模型客户端 ChatBo

Spring Cloud Hystrix原理与注意事项小结

《SpringCloudHystrix原理与注意事项小结》本文介绍了Hystrix的基本概念、工作原理以及其在实际开发中的应用方式,通过对Hystrix的深入学习,开发者可以在分布式系统中实现精细... 目录一、Spring Cloud Hystrix概述和设计目标(一)Spring Cloud Hystr

Qt实现发送HTTP请求的示例详解

《Qt实现发送HTTP请求的示例详解》这篇文章主要为大家详细介绍了如何通过Qt实现发送HTTP请求,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、添加network模块2、包含改头文件3、创建网络访问管理器4、创建接口5、创建网络请求对象6、创建一个回复对

C++实现回文串判断的两种高效方法

《C++实现回文串判断的两种高效方法》文章介绍了两种判断回文串的方法:解法一通过创建新字符串来处理,解法二在原字符串上直接筛选判断,两种方法都使用了双指针法,文中通过代码示例讲解的非常详细,需要的朋友... 目录一、问题描述示例二、解法一:将字母数字连接到新的 string思路代码实现代码解释复杂度分析三、

grom设置全局日志实现执行并打印sql语句

《grom设置全局日志实现执行并打印sql语句》本文主要介绍了grom设置全局日志实现执行并打印sql语句,包括设置日志级别、实现自定义Logger接口以及如何使用GORM的默认logger,通过这些... 目录gorm中的自定义日志gorm中日志的其他操作日志级别Debug自定义 Loggergorm中的