This article only briefly describes the formula derivation of the Bezier curve, and gives a code implementation. Before reading this article, please make sure you have some background knowledge of Bezier curves. Relevant knowledge can be learned through the following courses: MOOC-Computer Graphics-China Agricultural University-Zhao Ming or watch the port version of Station B.
Algorithm principle
Given n + 1 n+1n+1 control pointP i ( i = 0 , 1 , ⋯ , n ) P_i(i=0,1,\cdots,n)Pi(i=0,1,⋯,n ) , structure polynomial function
P ( u ) = ∑ i = 0 n P i ⋅ BEZ i , n ( u ) , 0 ⩽ u ⩽ 1 , (1) \mathcal{P}(u)=\sum_{i= 0}^{n}P_i\cdot BEZ_{i,n}(u), \quad 0\leqslant u\leqslant 1 \thinspace, \tag{1}P(u)=i=0∑nPi⋅B E Zi,n(u),0⩽u⩽1,( 1 )
Approach curve. Among them,BEZ i , n ( u ) BEZ_{i,n}(u)B E Zi,n( u ) callednnn次Bernstein基函数,且有
B E Z i , n ( u ) = C n i u i ( 1 − u ) n − i , ( i = 0 , 1 , ⋯ , n u ∈ [ 0 , 1 ] ) 。 (2) BEZ_{i,n}(u)=C_n^i u^i(1-u)^{n-i}, \quad \left(\begin{array}{l} i=0,1,\cdots,n \\ u\in[0,1] \end{array}\right) \thinspace。 \tag{2} B E Zi,n(u)=Cniui(1−u)n−i,(i=0,1,⋯,nu∈[0,1])。(2)
For formula (1), the function P ( u ) \mathcal{P}(u)P ( u ) converts the real number interval[ 0 , 1 ] [0,1][0,1 ] is mapped to the point set on the real number field, and the points in the point set form the approximate curve sought. BEZ i , n ( u ) BEZ_{i,n}(u)B E Zi,n( u ) can be regarded as the control pointP i P_iPiin uuThe weight under u . BecauseBEZ i , n ( u ) BEZ_{i,n}(u)B E Zi,n(u)具有权性:
∑ i = 0 n B E Z i , n ( u ) ≡ 1 , ∀ u ∈ ( 0 , 1 ) 。 (3) \sum_{i=0}^{n} BEZ_{i,n}(u) \equiv 1, \quad \forall u\in(0,1) \thinspace。\tag{3} i=0∑nB E Zi,n(u)≡1,∀u∈(0,1)。(3)
我们定义:
β i ( r ) = { P i , r = 0 ( 1 − u ) β i ( r − 1 ) + u β i + 1 ( r − 1 ) , r = 1 , 2 , ⋯ , n , (4) \beta_{i}^{(r)} = \begin{cases} P_i, & r = 0 \\ (1-u)\beta_{i}^{(r-1)} + u\beta_{i+1}^{(r-1)}, & r = 1,2,\cdots,n \end{cases} \thinspace, \tag{4} bi(r)={
Pi,(1−u ) bi(r−1)+uβi+1(r−1),r=0r=1,2,⋯,n,( 4 )
i = 0 , 1 , ⋯ , n− ri=0,1,\cdots,nri=0,1,⋯,n−r。
It can be proved (see Wikipedia De Casteljau algorithm for the proof process ):
P ( u ) = ∑ i = 0 n β i ( 0 ) ⋅ BEZ i , n ( u ) = β 0 ( n ) . (5) \mathcal{P}(u) = \sum_{i=0}^{n} \beta_{i}^{(0)}\cdot BEZ_{i,n}(u) = \beta_{0 }^{(n)} \thinspace. \tag{5}P(u)=i=0∑nbi(0)⋅B E Zi,n(u)=b0(n)。(5)
So the algorithm logic is as follows:
- In the interval of real numbers [ 0 , 1 ] [0,1][0,1 ] over a large number of samples. For each sampleuuu , computationP ( u ) \mathcal{P}(u)P(u)。
- According to formula (5), we can calculate β 0 ( n ) \beta_{0}^{(n)}b0(n)To calculate P ( u ) \mathcal{P}(u)P(u)。
- β 0 ( n ) \beta_{0}^{(n)}b0(n)It can be calculated recursively by formula (4).
Code
def draw_curve(p_list):
"""
:param p_list: (list of list of int: [[x0, y0], [x1, y1], [x2, y2], ...]) 曲线的控制点坐标列表
:return: (list of list of int: [[x_0, y_0], [x_1, y_1], [x_2, y_2], ...]) 绘制结果的像素点坐标列表
"""
result = []
n = len(p_list) - 1
freq = 1000
for u in range(freq + 1):
u /= freq
tmp = [[p[0], p[1]] for p in p_list]
for r in range(1, n + 1):
tmp = [
[(1 - u) * tmp[i][0] + u * tmp[i + 1][0],
(1 - u) * tmp[i][1] + u * tmp[i + 1][1]
] for i in range(n - r + 1)
]
result.append([round(tmp[0][0]), round(tmp[0][1])])
return result