<commit_before>---
layout: post
title: 贝塞尔曲线简介
date: 2016-4-22
categories: blog
tags: [matlab,数学,算法]
description: 贝塞尔曲线
---

# 1、什么是贝塞尔曲线？

贝塞尔（Bézier curve）曲线最初由<NAME>于1959年运用de Casteljau演算法开发。1962年，法国工程师皮埃尔·贝塞尔（Pierre Bézier）将其应用于汽车的主体设计，并广泛发表。

### 意义

随着计算机日益普及，常常需要在电脑绘制图形，如果采用鼠绘的方式无疑很难绘制出满意的图形。

众所周知：

- 线段（一次贝塞尔曲线）：给定起点与终点的坐标，就能确定唯一的一条线段；
- 圆（椭圆）：给定圆心坐标和半径大小，就可以通过方程计算出圆周上每一点的坐标；
- 矩形：给定对角点的坐标，也可以顺利求出四条边上的所有点。

那么，除了抛物线等规则的曲线，普通的曲线该如何在电脑上描述呢？答案便是贝塞尔曲线。

# 2、如何绘制一条贝塞尔曲线？

二次贝塞尔曲线|三次贝塞尔曲线|四次贝塞尔曲线
:--:|:--:|:--:
![bazier2](http://7xrrbc.com1.z0.glb.clouddn.com/bazier1.gif)|![bazier3](http://7xrrbc.com1.z0.glb.clouddn.com/bazier2.gif)|![bazier4](http://7xrrbc.com1.z0.glb.clouddn.com/bazier3.gif)

可以看到，$n$次贝塞尔曲线由三类点来描述：

- 一个 __起点__ $P_0$
- $n-1$个 __控制点__ $P_1,\cdots,P_{n-1}$
- 一个 __终点__ $P_n$

上述点可以按顺序依次组成一个坐标向量 $P=[P_0,P_1,\cdots,P_n]^T$。

可以由四个步骤得到一个精度为$t$的贝塞尔曲线：

1. 将坐标向量$P$（$P^*$）中两两相邻点确定的线段$t$等分；
2. 取第$i$个等分点，构成新的坐标向量$P^*$；
3. 重复步骤1~2，直到$P^*$中仅含一个点，该点便是贝塞尔曲线上的点；
4. 重复步骤1~3，将贝塞尔曲线上的点用光滑的线连接起来。

# 3、如何计算贝塞尔曲线上的点？

以三次贝塞尔曲线为例，其坐标向量为
$$P=[P_0,P_1,P_2,P_3]^T$$

根据矢量运算法则，得到第$i$个等分点的坐标（第一次迭代）为
$$X_i=\frac{i}{t}(P_1-P_0)+P_0$$
$$Y_i=\frac{i}{t}(P_2-P_1)+P_1$$
$$Z_i=\frac{i}{t}(P_3-P_2)+P_2$$

整理即
$$X_i=(1-\frac{i}{t})P_0+\frac{i}{t}P_1$$
$$Y_i=(1-\frac{i}{t})P_1+\frac{i}{t}P_2$$
$$Z_i=(1-\frac{i}{t})P_2+\frac{i}{t}P_3$$

同理，第二次迭代得到
$$K_i=(1-\frac{i}{t})X_i+\frac{i}{t}Y_i$$
$$M_i=(1-\frac{i}{t})Y_i+\frac{i}{t}Z_i$$

整理即
$$K_i=(1-\frac{i}{t})^2P_0+2\frac{i}{t}(1-\frac{i}{t})P_1+(\frac{i}{t})^2P_2$$
$$M_i=(1-\frac{i}{t})^2P_1+2\frac{i}{t}(1-\frac{i}{t})P_2+(\frac{i}{t})^2P_3$$

经过最终迭代后，得到曲线上的第$i$点坐标
$$B_i=(1-\frac{i}{t})^3P_0+3\frac{i}{t}(1-\frac{i}{t})^2P_1+3(1-\frac{i}{t})(\frac{i}{t})^2P_2+(\frac{i}{t})^3P_3$$

记$k=\frac{i}{t}$。显然，$k\in[0,1]$。上式（$n=3$）可以简化为
$$B_i=\sum_{i=0}^nb_i(k)P_i\text{，其中}b_i(k)=\begin{pmatrix}n\\i\end{pmatrix}k^i(1-k)^{n-i}$$

# 4、matlab程序示例

自定义m函数bezi2()可以求取任意精度（默认为100）与任意阶（由坐标向量中的点数确定）的贝塞尔曲线，结果为以复数形式表示的贝塞尔曲线上点的坐标。其源码如下：

```m
>> plot(bezi2([1,1;1,3;9,1;9,3])), grid

function [B] = 
% P = [起点,坐标;...;控制点,坐标;...;终点,坐标]
% t = 曲线精度（默认100）
% B = [(t+1)×2]
    if nargin < 2, t = 100; end
    n = size(P,1);
    k = (0:t)'/t;
    b = zeros(t+1,n);
    for i=1:n,
        b(:,i) = nchoosek(n-1,i-1)*(1-k).^(n-i).*k.^(i-1);
    end
    B = b*P*[1;1j];
end
```

运行结果如图：

![bazier0](http://7xrrbc.com1.z0.glb.clouddn.com/bazier0.png)

增加控制点个数，得到下图：

![bazier](http://7xrrbc.com1.z0.glb.clouddn.com/bazier.png)

从图中可以看到，控制点有点类似于“拉扯”的效果。因此，通过控制控制点的个数与位置，就能轻松在电脑上绘制出理想的曲线。<commit_msg>from sp3<commit_after>---
layout: post
title: 贝塞尔曲线简介
date: 2016-4-22
categories: blog
tags: [matlab,数学,算法]
description: 贝塞尔曲线
---

# 1、什么是贝塞尔曲线？

贝塞尔（Bézier curve）曲线最初由<NAME>于1959年运用de Casteljau演算法开发。1962年，法国工程师皮埃尔·贝塞尔（<NAME>）将其应用于汽车的主体设计，并广泛发表。

### 意义

随着计算机日益普及，常常需要在电脑绘制图形，如果采用鼠绘的方式无疑很难绘制出满意的图形。

众所周知：

- 线段（一次贝塞尔曲线）：给定起点与终点的坐标，就能确定唯一的一条线段；
- 圆（椭圆）：给定圆心坐标和半径大小，就可以通过方程计算出圆周上每一点的坐标；
- 矩形：给定对角点的坐标，也可以顺利求出四条边上的所有点。

那么，除了抛物线等规则的曲线，普通的曲线该如何在电脑上描述呢？答案便是贝塞尔曲线。

# 2、如何绘制一条贝塞尔曲线？

二次贝塞尔曲线|三次贝塞尔曲线|四次贝塞尔曲线
:--:|:--:|:--:
![bazier2](http://7xrrbc.com1.z0.glb.clouddn.com/bazier1.gif)|![bazier3](http://7xrrbc.com1.z0.glb.clouddn.com/bazier2.gif)|![bazier4](http://7xrrbc.com1.z0.glb.clouddn.com/bazier3.gif)

可以看到，$n$次贝塞尔曲线由三类点来描述：

- 一个 __起点__ $P_0$
- $n-1$个 __控制点__ $P_1,\cdots,P_{n-1}$
- 一个 __终点__ $P_n$

上述点可以按顺序依次组成一个坐标向量 $P=[P_0,P_1,\cdots,P_n]^T$。

可以由四个步骤得到一个精度为$t$的贝塞尔曲线：

1. 将坐标向量$P$（$P^*$）中两两相邻点确定的线段$t$等分；
2. 取第$i$个等分点，构成新的坐标向量$P^*$；
3. 重复步骤1~2，直到$P^*$中仅含一个点，该点便是贝塞尔曲线上的点；
4. 重复步骤1~3，将贝塞尔曲线上的点用光滑的线连接起来。

# 3、如何计算贝塞尔曲线上的点？

以三次贝塞尔曲线为例，其坐标向量为

$$P=[P_0,P_1,P_2,P_3]^T$$

根据矢量运算法则，得到第$i$个等分点的坐标（第一次迭代）为

$$X_i=\frac{i}{t}(P_1-P_0)+P_0$$

$$Y_i=\frac{i}{t}(P_2-P_1)+P_1$$

$$Z_i=\frac{i}{t}(P_3-P_2)+P_2$$

整理即

$$X_i=(1-\frac{i}{t})P_0+\frac{i}{t}P_1$$

$$Y_i=(1-\frac{i}{t})P_1+\frac{i}{t}P_2$$

$$Z_i=(1-\frac{i}{t})P_2+\frac{i}{t}P_3$$

同理，第二次迭代得到

$$K_i=(1-\frac{i}{t})X_i+\frac{i}{t}Y_i$$

$$M_i=(1-\frac{i}{t})Y_i+\frac{i}{t}Z_i$$

整理即

$$K_i=(1-\frac{i}{t})^2P_0+2\frac{i}{t}(1-\frac{i}{t})P_1+(\frac{i}{t})^2P_2$$

$$M_i=(1-\frac{i}{t})^2P_1+2\frac{i}{t}(1-\frac{i}{t})P_2+(\frac{i}{t})^2P_3$$

经过最终迭代后，得到曲线上的第$i$点坐标

$$B_i=(1-\frac{i}{t})^3P_0+3\frac{i}{t}(1-\frac{i}{t})^2P_1+3(1-\frac{i}{t})(\frac{i}{t})^2P_2+(\frac{i}{t})^3P_3$$

记$k=\frac{i}{t}$。显然，$k\in[0,1]$。上式（$n=3$）可以简化为

$$B_i=\sum_{i=0}^nb_i(k)P_i\text{，其中}b_i(k)=\begin{pmatrix}n\\i\end{pmatrix}k^i(1-k)^{n-i}$$

# 4、matlab程序示例

自定义m函数bezi2()可以求取任意精度（默认为100）与任意阶（由坐标向量中的点数确定）的贝塞尔曲线，结果为以复数形式表示的贝塞尔曲线上点的坐标。其源码如下：

```m
>> plot(bezi2([1,1;1,3;9,1;9,3])), grid

function [B] = 
% P = [起点,坐标;...;控制点,坐标;...;终点,坐标]
% t = 曲线精度（默认100）
% B = [(t+1)×2]
    if nargin < 2, t = 100; end
    n = size(P,1);
    k = (0:t)'/t;
    b = zeros(t+1,n);
    for i=1:n,
        b(:,i) = nchoosek(n-1,i-1)*(1-k).^(n-i).*k.^(i-1);
    end
    B = b*P*[1;1j];
end
```

运行结果如图：

![bazier0](http://7xrrbc.com1.z0.glb.clouddn.com/bazier0.png)

增加控制点个数，得到下图：

![bazier](http://7xrrbc.com1.z0.glb.clouddn.com/bazier.png)

从图中可以看到，控制点有点类似于“拉扯”的效果。因此，通过控制控制点的个数与位置，就能轻松在电脑上绘制出理想的曲线。
{'fraction_non_alphanumeric': 0.2805769610748864, 'fraction_numerical': 0.05789369689784628, 'mean_word_length': 6.210826210826211, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '485832', 'n_tokens_mistral': 3891, 'n_tokens_neox': 4312, 'n_words': 215}