贝塞尔曲线: 通过起点、终点以及多个控制点绘制得到的曲线,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)