贝塞尔曲线
在计算机看来,所有线条都是“函数”,不管它们是直线还是曲线。而贝塞尔曲线是一种在计算机上表现良好的曲线方程,基本上任何画曲线的地方都会用到它。
常见应用场景:计算机辅助设计和计算机辅助制造应用(CAD/CAM),Adobe Illustrator, Photoshop, Inkscape, Gimp等等。还可以应用在一些图形技术中,像矢量图形(SVG),所以说在可视化学习的重要基础知识。
贝塞尔曲线是线性插值的结果
线性插值(简单理解):“选出两点之间的一个点”
n
阶贝塞尔曲线(n+1
个控制点)
1.线性贝塞尔曲线
例如:下边这个图中,在P1
和P2
中插入一个一个点P
,定义一个t(0<t<1)
来定义P点的位置,得到下面的线性插值函数:(可以根据t来求得出线段上那个点的坐标)
P
(
t
)
=
P
0
+
(
P
1
−
P
0
)
=
(
1
−
t
)
P
0
+
t
P
1
,
t
∈
[
0
,
1
]
t
=
P
0
P
1
/
P
0
P
1
P(t)=P_0+(P_1-P_0)=(1-t)P_0+tP_1,t\in[0,1]\\ t=P_0P1/P_0P_1
P(t)=P0+(P1−P0)=(1−t)P0+tP1,t∈[0,1]t=P0P1/P0P1
2.二阶贝塞尔曲线
上面为一个插值一个点的情况,也成为线性贝塞尔曲线。下面我们在插两值,叫二次方贝塞尔曲线。
在
P
0
P
1
上利用上面的线性公式求出点
P
0
′
,然后在
P
1
P
2
上利用线性公式求出点
P
1
′
,
最后在
P
1
′
P
2
′
上利用线性公式求得最终贝塞尔曲线上的点
P
0
′
′
P
0
′
=
(
1
−
t
)
P
0
+
t
P
1
P
1
′
=
(
1
−
t
)
P
1
+
t
P
2
P
(
t
)
=
(
1
−
t
)
P
0
′
+
t
P
1
′
=
(
1
−
t
)
2
P
0
+
2
t
(
1
−
t
)
P
1
+
t
2
P
2
,
t
∈
[
0
,
1
]
在P_0P_1上利用上面的线性公式求出点P'_0,然后在P_1P_2上利用线性公式求出点P_1',\\最后在P_1'P_2'上利用线性公式 求得最终贝塞尔曲线上的点P''_0\\ P'_0=(1-t)P_0+tP_1\\ P'_1=(1-t)P_1+tP_2\\ P(t)=(1-t)P'_0+tP'_1=(1-t)^2 P_0+2t(1-t)P_1+t^2P_2, t\in[0,1]\\
在P0P1上利用上面的线性公式求出点P0′,然后在P1P2上利用线性公式求出点P1′,最后在P1′P2′上利用线性公式求得最终贝塞尔曲线上的点P0′′P0′=(1−t)P0+tP1P1′=(1−t)P1+tP2P(t)=(1−t)P0′+tP1′=(1−t)2P0+2t(1−t)P1+t2P2,t∈[0,1]
把两个插值建立P建立连接,在连接线中在以同样的参数t建立一个插值,这样线性的曲线就叫二次方贝塞尔曲线
3.三阶贝塞尔曲线
下面还有三次线性插值[最常用的](在在插值P的连线上递归的创建同t的线性插值)
三阶的公式推导同理二阶的,最终为
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)=(1−t)3P0+3t(1−t)2P1+3t2(1−t)P2+t3P3,t∈[0,1]
还可以将点移动到任意位置(贝塞尔曲线便跟着发生改变了)
其中的每个新点都是从前面的几个点形成的,直到形成贝塞尔曲线,递归到最后一个点(称为De Casteljau
算法)。
A
=
(
1
−
t
)
P
0
+
t
P
1
B
=
(
1
−
t
)
P
1
+
t
P
2
C
=
(
1
−
t
)
P
2
+
t
P
3
D
=
(
1
−
t
)
A
+
t
B
E
=
(
1
−
t
)
V
+
t
C
P
=
(
1
−
t
)
D
+
t
E
A=(1-t)P_0+tP_1\\ B=(1-t)P_1+tP_2\\ C=(1-t)P_2+tP_3\\ D=(1-t)A+tB\\ E=(1-t)V+tC\\ P=(1-t)D+tE\\
A=(1−t)P0+tP1B=(1−t)P1+tP2C=(1−t)P2+tP3D=(1−t)A+tBE=(1−t)V+tCP=(1−t)D+tE
4.n阶贝塞尔曲线
给定点
P
0
,
P
1
,
P
2
.
.
.
P
n
给定点P_0,P_1,P_2...P_n
给定点P0,P1,P2...Pn
- P i P_i Pi:贝塞尔控制点
- ( 1 − t ) n − i t i (1-t)_{n-i}t^i (1−t)n−iti:伯恩斯坦多项式
n阶级贝塞尔曲线的伯恩斯坦形式
b
n
(
t
)
=
∑
i
=
0
n
b
i
B
i
n
(
t
)
b^n(t)=\sum_{i=0}^nb_iB_i^n(t)
bn(t)=i=0∑nbiBin(t)
使用示例:在三维空间中定义了一条贝塞尔曲线n=3,
P
0
=
(
0
,
2
,
3
)
,
P
1
=
(
2
,
3
,
5
)
,
P
2
=
(
6
,
7
,
9
)
,
B
3
=
(
3
,
4
,
5
)
P_0 =(0,2,3),P_1 =(2,3,5),P_2 =(6,7,9),B_3 =(3,4,5)
P0=(0,2,3),P1=(2,3,5),P2=(6,7,9),B3=(3,4,5)
P
n
(
t
)
=
P
0
(
1
−
3
)
3
+
P
1
(
1
−
t
)
2
+
P
2
3
t
2
(
1
−
t
)
+
b
3
t
3
P^n(t)=P_0(1-3)^3+P_1(1-t)^2+P_23t^2(1-t)+b_3t^3
Pn(t)=P0(1−3)3+P1(1−t)2+P23t2(1−t)+b3t3
5.de Casteljau algorithm
de Casteljau(德卡尔斯特里奥):给定任意多个点将贝塞尔曲线上的点出来算法
De Casteljau 算法给出了一个系数金字塔
6.贝塞尔曲线性质
- 起始点的方向为响应控制点的切线方向
- 反射变换前后的点绘制出来的贝塞尔曲线是相同的(而其它例如投影之类的是不具备这一性质的)
7.分段Bézier曲线
在实际的应用中如果直接使用高阶贝塞尔曲线很任意出现如下问题:
- 曲线难以控制
- 不寻常
所以通常将高阶的贝塞尔曲线分为许多的低阶的贝塞尔曲线
这一做很容易让人产生一种思考,对于被分割的低阶的贝塞尔曲线在合并的时候的各种情况应该这么处理呢?
- 根据上图可以观察出只要两条贝塞尔曲线的衔接处的切线相互组成为一条直线,那么它们的衔接将是平滑的
- 当然也有相关的术语对这些情况定义,如:
- C 0 C^0 C0表示两条曲线已经是处于接上了的状态,不管其是否平滑
- C 1 C^1 C1表示连接处相邻控制点距离的关系为1:1
8.贝塞尔曲线的数学原理
贝塞尔曲线是“参数”方程的一种形式。
补充普通参数方程的一些特性:参数曲线不像一般函数那样一个方程一个未知数,它是多个方程,但只有一个未知数。如果我们改变了t的值,fa(t)和fb(t)的输出都会发生变化。
例如圆的参数方程
与普通参数方式不同的是贝塞尔曲线的t使用了多项式,贝塞尔曲线不是x的多项式,它是t的多项式,t的值被限制在0和1之间(t项次便代表有多少个点构成的贝塞尔曲线)
把t去掉后
可以发现,每次我们增加一个维度,只要简单地将头尾置为1,中间的操作都是“将上面的两个数字相加”
这便是这些参数的原理了
还有一个简单的办法可以弄清参数项怎么工作的:如果我们将 ( 1 − t ) (1-t) (1−t)重命名为 a a a,将 t t t重命名为b,暂时把权重删掉,可以得到这个:
基本上它就是“每个 a a a和 b b b结合项”的和,在每个加号后面逐步的将 a a a换成 b b b。最终可以得出一般方程:
这些都是和上边得图形示例对应得上的,可以到回去比对分析
使用代码实现贝塞尔曲线方程
function Bezier(2,t):
t2 = t * t
mt = 1-t
mt2 = mt * mt
return mt2 + 2*mt*t + t2
function Bezier(3,t):
t2 = t * t
t3 = t2 * t
mt = 1-t
mt2 = mt * mt
mt3 = mt2 * mt
return mt3 + 3*mt2*t + 3*mt*t2 + t3
9.控制贝塞尔的曲率
贝塞尔曲线是插值方程(就像所有曲线一样),这表示它们取一系列的点,生成一些处于这些点之间的值。
这里对于贝塞尔曲线又多了一个重要的概念:权重/对总曲线的贡献,通过控制权重来改变相应的曲率
实际上,我们可以将每个点对方程产生的曲线做出的贡献进行可视化,因此可以看出曲线上哪些点是重要的,它们处于什么位置。
上面每条线对应着相应的个点,根据
t
的变化可以计算出某个点对贝塞尔曲线的贡献/权重S
通过可视化可以大概看出在所有控制点中起点和终点对曲线形状的贡献比其他点更大些
如果我们要改变曲线(改变曲率),就需要改变每个点的权重,来实现有效地改变插值。
可以很直接地做到这个:只要用一个值乘以每个点,来改变它的强度。这个值照惯例称为“权重”,可以将它加入原始的贝塞尔函数:
“权重”是我们想让曲线所拥有的坐标值:对于一条n
阶曲线,w0
是起始坐标,wn
是终点坐标,中间的所有点都是控制点坐标。假设说一条曲线的起点为(110,150)
,终点为(210,30)
,并受点(25,190)
和点(210,250)
的控制,贝塞尔曲线方程就为:
使用代码实现权重基本函数
function Bezier(2,t,w[]):
t2 = t * t
mt = 1-t
mt2 = mt * mt
return w[0]*mt2 + w[1]*2*mt*t + w[2]*t2
function Bezier(3,t,w[]):
t2 = t * t
t3 = t2 * t
mt = 1-t
mt2 = mt * mt
mt3 = mt2 * mt
return w[0]*mt3 + 3*w[1]*mt2*t + 3*w[2]*mt*t2 + w[3]*t3
10.通过SVG绘制贝塞尔曲线
SVG中绘制平滑曲线的命令有三个,其中两个用来绘制贝塞尔曲线,另外一个用来绘制弧形或者说是圆的一部分。
虽然贝塞尔曲线的类型有很多,但是在path元素里,只存在两种贝塞尔曲线:三次贝塞尔曲线C,和二次贝塞尔曲线Q,因为在计算机图形领域用的比较多的就是这两个了。
[1]绘制二次贝塞尔曲线
有两种命令来来二次贝塞尔曲线Q
和T
Q
Q x1 y1, x y (or q dx1 dy1, dx dy) // 控制点 (x1,y1),终点 (x,y)
T
T x y (or t dx dy) // 终点 (x y),控制点通过前面的Q命令计算得出
快捷命令T会通过前一个控制点,推断出一个新的控制点。这意味着,在你的第一个控制点后面,可以只定义终点,就创建出一个相当复杂的曲线。快捷命令T会通过前一个控制点,推断出一个新的控制点。这意味着,在你的第一个控制点后面,可以只定义终点,就创建出一个相当复杂的曲线。
示例
<?xml version="1.0" standalone="no"?>
<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M10 80 Q 52.5 10, 95 80 T 180 80" stroke="black" fill="transparent"/>
</svg>
[2]绘制三次贝塞尔曲线
三次贝塞尔曲线和二次贝塞尔曲线相比就是多一个控制点,原理大致相同4
C
也有两种命令来绘制三次贝塞尔曲线C
和S
C x1 y1, x2 y2, x y (or c dx1 dy1, dx2 dy2, dx dy)
参数说明:
x y
表示的是曲线的终点,另外两个坐标是出终点外的控制点,(x1,y1)是起点的控制点,(x2,y2)是终点的控制点
示例
<?xml version="1.0" standalone="no"?>
<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M10 10 C 20 20, 40 20, 50 10" stroke="black" fill="transparent"/>
<path d="M70 10 C 70 20, 120 20, 120 10" stroke="black" fill="transparent"/>
<path d="M130 10 C 120 20, 180 20, 170 10" stroke="black" fill="transparent"/>
<path d="M10 60 C 20 80, 40 80, 50 60" stroke="black" fill="transparent"/>
<path d="M70 60 C 70 80, 110 80, 110 60" stroke="black" fill="transparent"/>
<path d="M130 60 C 120 80, 180 80, 170 60" stroke="black" fill="transparent"/>
<path d="M10 110 C 20 140, 40 140, 50 110" stroke="black" fill="transparent"/>
<path d="M70 110 C 70 140, 110 140, 110 110" stroke="black" fill="transparent"/>
<path d="M130 110 C 120 140, 180 140, 170 110" stroke="black" fill="transparent"/>
</svg>
S
S x2 y2, x y (or s dx2 dy2, dx dy)
S命令和T命令相似,当你想将若干个贝塞尔曲线连起来,从而创建出一条很长的平滑曲线,这便是一种很好的选择。通常情况下一个点某一侧的控制点是它另一侧的控制点的对称(以保持斜率不变)。这样,你可以使用一个简写的贝塞尔曲线命令S。
示例:
<?xml version="1.0" standalone="no"?>
<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80" stroke="black" fill="transparent"/>
</svg>
S命令可以用来创建与前面一样的贝塞尔曲线,但是,如果S命令跟在一个C或S命令后面,则它的第一个控制点会被假设成前一个命令曲线的第二个控制点的中心对称点。如果S命令单独使用,前面没有C或S命令,那当前点将作为第一个控制点。
11.通过Canvas2D绘制贝塞尔曲线
其绘图原理和SVG绘制同
quadraticCurveTo(cp1x, cp1y, x, y)
绘制二次贝塞尔曲线,cp1x,cp1y
为一个控制点,x,y为
结束点。
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
绘制三次贝塞尔曲线,cp1x,cp1y
为控制点一,cp2x,cp2y
为控制点二,x,y
为结束点。
参考:
https://zh.javascript.info/bezier-curve
https://www.youtube.com/watch?v=aVwxzDHniEw
https://pomax.github.io/bezierinfo/zh-CN/index.html
https://zh.wikipedia.org/wiki/%E8%B2%9D%E8%8C%B2%E6%9B%B2%E7%B7%9A