贝塞尔曲线

0x01 线性贝塞尔公式:

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

其等同于线性插值。


0x02 二次贝塞尔曲线

二次方贝兹曲线的路径由给定点P0、P1、P2控制,这条线由下式给出:

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

0x0201 推导

在二阶贝塞尔曲线中,已知三点恒定(P0,P1,P2),设定在P0P1中的点为Pa,在P1P2中的点为Pb,Pt在PaPb上的点,这三点都在相同时间t内做匀速运动。

由公式(1)可知
Pa=P0+(P1P0)t=(1t)P0+tP1(2)P_a= P_0 + (P_1 - P_0)t = (1-t)P_0 + tP_1\quad(2)
Pb=P1+(P2P1)t=(1t)P1+tP2(3)P_b= P_1 + (P_2 - P_1)t = (1-t)P_1 + tP_2\quad(3)
Pt=Pa+(PbPa)t=(1t)Pa+tPb(4)P_t= P_a + (P_b - P_a)t = (1-t)P_a + tP_b\quad(4)

将公式 (2) (3) 代入公式 (4) 中,可得:
Pt=(1t)Pa+tPbP_t= (1-t)P_a + tP_b
=(1t)[(1t)P0+tP1]+t[(1t)P1+tP2]= (1-t)[(1-t)P_0+tP_1]+t[ (1-t)P_1 + tP_2]
=(1t)2P0+(1t)tP1+(1t)tP1+t2P2= (1-t)^2P_0+(1-t)tP_1 + (1-t)tP_1 + t^2P_2
=(1t)2P0+2(1t)tP1+t2P2t[0,1](5)= (1-t)^2P_0+2(1-t)tP_1+ t^2P_2\quad t\in[0,1]\quad (5)

动画如图:


0x03 三次贝塞尔曲线


0x0301 推导

Pa=P0+(P1P0)t=(1t)P0+tP1P_a= P_0 + (P_1 - P_0)t = (1-t)P_0 + tP_1
Pb=P1+(P2P1)t=(1t)P1+tP2P_b= P_1 + (P_2 - P_1)t = (1-t)P_1 + tP_2
Pc=P2+(P3P2)t=(1t)P2+tP3P_c= P_2 + (P_3 - P_2)t = (1-t)P_2 + tP_3

Pd=Pa+(PbPa)t=(1t)Pa+tPbP_d= P_a + (P_b - P_a)t = (1-t)P_a + tP_b
Pe=Pb+(PcPb)t=(1t)Pb+tPcP_e= P_b + (P_c - P_b)t = (1-t)P_b + tP_c

// 代入
Pt=Pd+(PePd)tP_t = P_d + (P_e - P_d)t
=(1t)Pd+tPe= (1-t)P_d + tP_e
=(1t)[(1t)Pa+tPb]+t[(1t)Pb+tPc]= (1-t)[(1-t)P_a + tP_b] + t[(1-t)P_b + tP_c]
=(1t)2Pa+2(1t)tPb+t2Pc= (1-t)^2P_a + 2(1-t)tP_b + t^2P_c
=(1t)2[(1t)P0+tP1]+2(1t)t[(1t)P1+tP2]+t2[(1t)P2+tP3]= (1-t)^2[(1-t)P_0 + tP_1] + 2(1-t)t[(1-t)P_1 + tP_2] + t^2[(1-t)P_2 + tP_3]
=(1t)3P0+(1t)2tP1+2(1t)2tP1+2(1t)t2P2+(1t)t2P2+t3P3= (1-t)^3P_0 + (1-t)^2tP_1 + 2(1-t)^2tP_1 + 2(1-t)t^2P_2 + (1-t)t^2P_2 + t^3P_3
=(1t)3P0+3t(1t)2P1+3(1t)t2P2+t3P3t[0,1]= (1-t)^3P_0 + 3t(1-t)^2P_1 + 3(1-t)t^2P_2 + t^3P_3\quad t\in[0,1]

0x04 一般参数形式的贝塞尔方程

B(t)=i=0n(ni)Pi(1t)nitiB(t) = \sum\limits_{i=0}^{n}\binom{n}{i}P_i(1-t)^{n-i}t^i
=(n0)P0(1t)nt0+(n1)P1(1t)n1t1+...+(nn1)Pn1(1t)1tn1+(nn)Pn(1t)0tn= \binom{n}{0}P_0(1-t)^{n}t^0 + \binom{n}{1}P_1(1-t)^{n-1}t^1+...+\binom{n}{n-1}P_{n-1}(1-t)^{1}t^{n-1}+\binom{n}{n}P_{n}(1-t)^{0}t^{n}


0x05 Python程序实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y

def __mul__(self, n):
return Point(self.x * n, self.y * n)

def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)

__radd__ = __add__

__rmul__ = __mul__

def Cnk(n, k):
if n == k or k == 0:
return 1
return Cnk(n-1, k-1) + Cnk(n-1, k)

def bezier(t, *points):
n = len(points)-1
i = 0
Pt = Point(0, 0)

# from i = 0 to i = n
for p in points:
Pt += Cnk(n, i) * ((1-t)**(n-i)) * (t**i) * p
i += 1

return Pt

p0 = Point(0.84, 2.04)
p1 = Point(2.52, 4.49)
p2 = Point(5.66, 0.81)
p3 = Point(7.73, 4.12)

t = 0.19
pt = bezier(t, p0, p1, p2, p3)

print(f'B({t}) = ({pt.x}, {pt.y})')