1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 |
"""演示13: Bézier 曲线 (de Casteljau 递推) 与 B-Spline 对比。
用控制点拟合自由形状曲线, 展示:
- 三次 Bézier: 由 4 个控制点决定, 递推求值;
- 均匀三次 B-Spline: 每 4 个控制点一段, 全局平滑。
"""
import os
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import BSpline
from figure_style import figures_dir, configure
configure()
OUT = figures_dir()
os.makedirs(OUT, exist_ok=True)
# 控制点(自由形状)
P = np.array([[0, 0], [1, 2], [3, 3], [4, 1], [5, 2.5], [6, 0.5]])
def bezier(points, t):
"""de Casteljau 递推: 由控制点求 Bézier 曲线上参数 t 的点。"""
p = np.array(points, dtype=float)
while len(p) > 1:
p = (1 - t) * p[:-1] + t * p[1:]
return p[0]
# 三次 Bézier: 用前 4 个控制点
t = np.linspace(0, 1, 200)
bz = np.array([bezier(P[:4], ti) for ti in t])
# 三次均匀 B-Spline: 所有控制点, 段间 C² 连续
bs_knots = np.arange(len(P) + 4) - 2
bs_spl = BSpline(bs_knots, P, 3)
tbs = np.linspace(0, len(P) - 3, 500)
bs = bs_spl(tbs)
fig, axes = plt.subplots(1, 2, figsize=(11, 4.5))
for ax, name, curve, ctrl in zip(
axes,
("三次 Bézier (4 控制点, de Casteljau)",
"三次均匀 B-Spline (全局 C² 平滑)"),
(bz, bs),
(P[:4], P)):
ax.plot(ctrl[:, 0], ctrl[:, 1], "--o", c="tab:gray", ms=5, label="控制点")
ax.plot(curve[:, 0], curve[:, 1], "tab:red", lw=2.2, label="曲线")
ax.set_title(name); ax.legend(fontsize=8)
ax.grid(alpha=0.3)
fig.suptitle("自由形状的数学笔: Bézier / B-Spline", fontsize=14)
fig.tight_layout(rect=(0, 0, 1, 0.93))
out = os.path.join(OUT, "fig_cg_bezier.png")
fig.savefig(out, dpi=150)
print(f"-> {out}")
|