贝塞尔曲线是一种由控制点定义的参数多项式曲线,由 Pierre Bézier 与 Paul de Casteljau 在 20 世纪 60 年代分别提出。它不要求曲线经过所有控制点,而是通过控制点“拉动”曲线形状,因此适合描述平滑轮廓和路径。

定义

一条 n 次贝塞尔曲线由 n+1 个控制点 $P_0, P_1, \dots, P_n$ 定义,参数 $t$ 在 $[0, 1]$ 上变化:

$$B(t) = \sum_{i=0}^{n} \binom{n}{i} (1-t)^{n-i} t^i P_i$$

其中 $\binom{n}{i}$ 是二项式系数。$t=0$ 时曲线位于 $P_0$,$t=1$ 时位于 $P_n$;中间控制点一般不在曲线上。

低阶形式

  • 一次(线段):$B(t) = (1-t)P_0 + tP_1$
  • 二次:$B(t) = (1-t)^2 P_0 + 2(1-t)t P_1 + t^2 P_2$
  • 三次:$B(t) = (1-t)^3 P_0 + 3(1-t)^2 t P_1 + 3(1-t)t^2 P_2 + t^3 P_3$

de Casteljau 算法

反复对相邻控制点做线性插值,可以得到曲线上任意一点。对于控制点 $P_0 \dots P_n$:

for k = 1 to n:
    for i = 0 to n - k:
        P_i = (1 - t) * P_i + t * P_{i+1}

最终剩下的点就是 $B(t)$。该算法数值稳定,也常用于把曲线细分折线化。

性质

  • 端点性质:曲线起点为 $P_0$,终点为 $P_n$。
  • 切向:起点处切向指向 $P_1 - P_0$,终点处切向指向 $P_n - P_{n-1}$。
  • 凸包性质:整条曲线位于所有控制点的凸包内。
  • 仿射不变性:对控制点做仿射变换,等价于对曲线本身做同一变换。
  • 次数:n 次曲线需要 n+1 个控制点;多段曲线拼接时可利用连续性约束。

在 Canvas 中的使用

Canvas 2D 提供二次与三次贝塞尔曲线绘制 API:

  • quadraticCurveTo(cpx, cpy, x, y)
  • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

示例:

const ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(20, 120);
ctx.bezierCurveTo(80, 20, 200, 220, 280, 120);
ctx.stroke();

也可以先构造 Path2D,再交给 Canvas 绘制。详见 Canvas 绘图。

在 SVG 中的使用

SVG path 用命令描述贝塞尔曲线:

  • Q x1 y1, x y:二次贝塞尔曲线
  • T x y:平滑二次贝塞尔曲线
  • C x1 y1, x2 y2, x y:三次贝塞尔曲线
  • S x2 y2, x y:平滑三次贝塞尔曲线

字体轮廓(TrueType 使用二次曲线,PostScript / OpenType 使用三次曲线)、SVG、Canvas 和矢量绘图工具都建立在贝塞尔曲线之上。相关笔记:矢量图。

应用

  • 字体与图形轮廓。
  • SVG path 与 Canvas path。
  • 动画缓动曲线(easing curve)。
  • 路径规划与轨迹插值。

参考资料


本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Algorithms