贝塞尔曲线(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))

    # 初始化杨辉三角左右两边的值为1
    for i in range(N):
        ta[i, 0] = 1
        ta[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 comb

def calculate_bezier_curve(control_points, M=1000):
    N = len(control_points)
    ta = np.zeros((N, N))

    # 初始化杨辉三角左右两边的值为1
    for i in range(N):
        ta[i, 0] = 1
        ta[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 curve

control_points1 = [(0, 0), (1, 2), (2, 0)]
bezier1 = bezier_curve(control_points1)
print(bezier1)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

群晖NAS搭建WebDav结合内网穿透实现公网访问本地影视资源

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

2024华数杯国际数学建模B题思路+代码+模型+论文

2024华数杯国际数学建模B题思路代码模型论文:1.17上午第一时间更新,详细内容见文末名片 问题B:光伏电 背景 中国的电力构成包括传统的能源发电(如煤炭、石油和天然气)、可再生能源发电 (如水力发电、风能…

OpenGL:关于纹理映射时任意四边形中的插值问题(二)

OpenGL:关于纹理映射时任意四边形中的插值问题-CSDN博客 上次是使用逆双线性插值的方法解决四边形纹理映射时产生的折痕问题。 其实也有点问题,就是双线性插值会使得纹理产生一点扭曲。 不是投影的效果。 想达到纹理投影的效果,可以使用透…

AndroidStudio下载安装教程

下载链接:官方下载 https://developer.android.google.cn/studio?hlzh-cn点击后弹出条款及条件页面,往下滑,勾选同意,然后就点击下载,等待下载完成 下载完成后双击打开,Next 继续Next 选择安装路径&am…

行列转化【附加面试题】

在MySQL中,行列转换是一种常见的操作。它包括行转列和列转行两种情况。 行转列:行转列是将表中的某些行转换成列,以提供更为清晰、易读的数据视图。例如,假设我们有一个包含科目和分数的表,我们可以使用SUM和CASE语句…

C++(13)——string

上篇文章中介绍了中部分函数的用法,本篇文章将继续对其他的函数进行介绍: 1. substr: string substr (size_t pos 0, size_t len npos) const; 函数的两个参数如上述代码所示,此函数的主要作用是根据一个已有的的对象的起始坐标开始&a…

基于python集成学习算法XGBoost农业数据可视化分析预测系统

文章目录 基于python集成学习算法XGBoost农业数据可视化分析预测系统一、项目简介二、开发环境三、项目技术四、功能结构五、功能实现模型构建封装类用于网格调参训练模型系统可视化数据请求接口模型评分 0.5*mse 六、系统实现七、总结 基于python集成学习算法XGBoost农业数据可…

我终于学会的前端技能——代码调试、打断点

在技术的世界里,要用魔法来打败魔法 说来惭愧我做前端已近三年了竟然还没有学会如何调试代码,也就是给自己的代码打上断点一步步看它的运行状态以达到理清代码运行逻辑、排查问题提升开发效率的目的。直到最近我才学会了这一技能,在这之前我…

JSP简单学习

jsp是在html中嵌入java代码 jsp也是在j2ee服务端中的java组件 第一次运行 在第一次运行jsp代码时会经历以下步骤,将jsp转为java代码,将java代码转为class文件。 所以通常会比较慢,编译后就好多了。 四大作用域 requestsessionpageapplica…

CUDA Toolkit 下载,安装,验证

CUDA Toolkit 下载 进cuda官网下载 CUDA Toolkit链接: https://developer.nvidia.com/cuda-downloads 官网默认显示当前的最新版本,这里以安装CUDA Toolkit 12.2 为示例 下载CUDA Toolkit 完成 CUDA Toolkit 安装 开始安装: 点同意&#…

Linux第30步_通过USB OTG将固件烧写到eMMC中

学习目的:在Win11中,使用STM32CubeProgrammer工具,通过USB OTG将固件烧写到eMMC中。 安装软件检查: 1、是否安装了JAVA; 2、是否安装了STM32CubeProgrammer工具; 3、是否安装 了DFU驱动程序; 4、是否安装了“Notepad”软件; …

关于SpringBoot项目整合Log4j2实现自定义日志打印失效原因

主要的原因是因为,SpringBoot的logback包的存在,会导致Spring Boot项目优先实现logback的日志设置,所以导致我们用Log4j2实现自定义日志失效。 先找l哪个包引用了logback包 进入之后查询logback 然后双击包 发现是spring-boot-starter-loggin…

python - 等值线(contour)数值添加白色背景边框

python - 等值线(contour)数值添加白色背景边框 如下图所示,图为NCL官网实力的等值线绘图。可以观察到,图中每条等值线都带有一个白色的矩形边框,使其在黑色的等值线更加清晰明了,更具有可读性。但是,目前我还是用pyt…

基于LDA的评论大数据的分析及主题建模

1.微博的关键词大数据采集; 已完成,待优化 2.LDA 错误1:使用了import pyLDAvis.sklearn,提示没有模块no module named pyldavis.sklearn。 默认安装 pyLDAvis3.4.1,最后降级处理,解决方式: …

云渲染是不是不吃电脑配置?对本地电脑要求高吗?

本地电脑渲染吃力,项目又急着要,没有用过的朋友会搜索: 云渲染对电脑配置要求高不高? 它的答案是:不了你是渲染动画还是效果图,都不吃电脑配置,只要你本地电脑能够做图,能够把项目的…

分析Unity崩溃日志(Android)

Unity版本: 这里Unity版本是Unity 2019.4.30f1 崩溃信息: 错误信息如下: Exception Appversion: versionName 1.0.1 versionCode 1 Process: sg.atla.f.c PID: 22716 UID: 10749 Flags: 0x3088be44 Package: sg.atla.f.c v1 (1.0.1) Fo…

如何判断光模块失效以及光模块应用注意点

1.测试光功率是否在指标要求范围之内,如果出现无光或者光功率小的现象,处理方法: A、检查光功率选择的波长和测量单位 (dbm)。 B、清洁光纤连接器端面,光模块光口。 C、检查光纤连接器端面是否发黑和划伤,光纤连接器是否存在折断&…

HTTP超详细介绍

HTTP讲解 1.HTTP的介绍2.HTTP协议的特点3.HTTP工作原理4.HTTP三点注意事项5.HTTP消息结构6.客户端请求消息7.服务端响应消息8.HTTP请求方法9.HTTP 响应头信息10.HTTP 状态码(HTTP Status Code)10.1.下面是常见的HTTP状态码10.2.HTTP状态码分类10.3.HTTP状…

【USTC】verilog 习题练习 21-25

21 基于端口名称的实例化 题目描述 创建一 verilog 电路,实现对模块 mod_a 基于端口名称的实例化,如下图所示: 其中mod_a模块的代码为: module mod_a (output out1,output out2,input in1,input in2,input in3,in…

如何批量修改图片大小尺寸?分享3个方法

如何批量修改图片大小尺寸?在日常生活中,有时候我们需要将大量的图片上传到社交媒体或应用程序上,而这些平台通常都有图片大小和尺寸的限制。通过批量修改图片大小尺寸,我们可以快速地将多张图片调整为所需的尺寸,节省…