貝塞爾曲線: 通過起點、終點以及多個控制點繪製得到的曲線,Photoshop中的鋼筆工具就是貝塞爾曲線。另外,CSS中動畫的計時函數也有三次貝塞爾曲線,例如常用的ease - cubic-bezier(0.25,0.1,0.25,1)
, ease-in-out - cubic-bezier(0.42,0,0.58,1)
。貝塞爾曲線的通用計算公式爲,其中t可以理解爲時刻,Pi表示控制點(包括起終點)。
本文使用Numpy擬合三次貝塞爾曲線cubic-bezier(0.3,0,0,1)
,其圖像如下:
擬合後的結果如下:
可以使用geogebra查看擬合後的曲線在整個座標軸上的結果,本文中擬合後的結果爲:,挺長的emmm
擬合過程:
- 以間隔0.001計算每個t時對應的座標(x,y)
- 使用pyplot繪製曲線
- 使用
np.polyfit(x, y, deg)
擬合曲線 - 繪製擬合後的曲線,根據結果調整
deg
參數(即多項式的最高次)
代碼如下
from matplotlib import pyplot as plt
import numpy as np
p0 = (0, 0)
p1 = (0, 0)
p2 = (1, 1)
p3 = (1, 1)
def calculateP(t: float):
"""
根據p0~p3計算時刻t曲線的座標,0 <= t <= 1,曲線: cubic-bezier(.4,0,0,1)
p0 p1 p2 p3
(0, 0) (0.4, 0) (0, 1) (1, 1)
返回一個(x, y)
"""
tmp = 1 - t
x = p0[0] * pow(tmp, 3) + 3 * p1[0] * t * pow(tmp, 2) + 3 * \
p2[0] * pow(t, 2) * tmp + 1 * p3[0] * pow(t, 3)
y = p0[1] * pow(tmp, 3) + 3 * p1[1] * t * pow(tmp, 2) + 3 * \
p2[1] * pow(t, 2) * tmp + 1 * p3[1] * pow(t, 3)
return (x, y)
def getPoints(dis, calc: callable):
"""
獲取三次貝塞爾曲線的離散點,參數 dis 指定離散點的間距,calc 指定計算函數
返回一個座標列表[(x,y)]
"""
if dis <= 0:
return [(0, 0)]
t = 0
res = []
while t < 1:
res.append(calc(t))
t += dis
return res
def showCurve(points):
""" 繪製 points 中的點 """
# plt.figure(figsize=(5, 5))
plt.subplot()
plt.grid()
plt.plot([p[0] for p in points], [p[1] for p in points])
plt.show()
def work():
global p1
global p2
dis = 0.001
p1 = (0.3, 0)
p2 = (0, 1)
points = getPoints(dis, calculateP)
showCurve(points)
f = np.polyfit([p[0] for p in points], [p[1] for p in points], 16)
print([x for x in f])
f = np.poly1d(f)
npoints = getPoints(dis, lambda x: (x, f(x)))
showCurve(npoints)