深入浅出贝塞尔曲线

0 阅读3分钟

贝塞尔曲线的小故事

众所周知,贝塞尔曲线就是贝塞尔(Pierre Bézier)发明的曲线。。。。。。其实并不严格。

Pierre Bézier一直从事数学建模,其博士论文也是和参数化曲线有关。他在雷诺工作期间一直想将数值控制应用到车身模型上,并在60年代开发了初代CAD(Unisurf)。贝塞尔曲线的初次应用就是在这款CAD中,它让工程师们能够在计算机上构建复杂曲线。

1962年Bézier公开了贝塞尔曲线的算法,不过贝塞尔曲线历史还可以再往前追溯一些,其算法根源于1912年的伯恩多项式,贝塞尔曲线是其在在数学建模领域的一个应用。

image.png

1959年,在雪铁龙工作的保尔·德·卡斯特里奥(Paul de Castelijau)实现了与贝塞尔曲线相似的算法,不过直到80年代才公开发表,因此错过了冠名的机会。

贝塞尔曲线的推导过程

要理解贝塞尔曲线的公式,可以从一次贝塞尔曲线,也就是直线开始。

如何用公式表示直线绘制?如果有两点P0和P1,用t表示绘制进度,则对任意t,绘制的的直线上的点的位置为: image.png

这个公式本质上就是最简单的线性插值。

由此,如果有三个点,P0,P1,P2,连接P0和P1得到直线L1,连接P1和P2得到直线L2,根据上述公式,对任意t,分别可得这两条直线上的点,连接这两个点,得到直线L3,根据一次贝塞尔公式,同样可得L3上任意t时的点P,将所有P点连接起来,刚好是一个曲线,因此我们得到了P点的公式——二次贝塞尔曲线:

image.png

同理,可以推导出三次贝塞尔曲线:

image.png

贝塞尔曲线的特性

贝塞尔曲线使人类能够以参数化的形式描述一段曲线,并且几乎所有的曲线都可以由多段贝塞尔曲线拟合

贝塞尔曲线是平滑曲线,具体看曲线上的任意一点,刚好是控制点辅助线与曲线的切点,因此可以绘制多段平滑的贝塞尔曲线。

绘制多段光滑贝塞尔曲线

绘制两端光滑的贝塞尔曲线,至少具备的条件有两个:

  1. 两段线有一个连接点G
  2. 两端线在连接点G的切线方向一致。

对任意多个点绘制多段光滑的贝塞尔曲线,难点就是如何计算控制点,实现思路可以参考echarts中的drawSegmentgithub.com/apache/echa…

实现大致思路如下:假设对x1,y1、x2,y2、x3,y3三个点绘制两条三次贝塞尔曲线,能够在连接处光滑,则首先绘制x1,y1到x2,y2处的三次贝塞尔曲线,对首段曲线,令控制点cp0和x1,y1重合(三次贝塞尔退化为二次),只需要求出cp1。求出cp1后,下一段曲线的cp0,与前一段曲线的cp1和x2,y2共线,因此也容易求出。 image.png

贝塞尔曲线的应用

贝塞尔曲线诞生于汽车行业CAD软件中,至今已应用在诸如仿真,3D打印,游戏,可视化等方方面面。

在前端领域,除了动画的缓动函数可以用贝塞尔曲线表示外,SVG和Canvas分别都有对应的直接绘制贝塞尔曲线的方法:

  • SVG path的C命令
<svg width="300" height="200">
  <path
    d="
      M 50 150
      C 100 50, 200 50, 250 150
    "
    fill="none"
    stroke="black"
    stroke-width="2"
  />
</svg>

  • Canvas中的bezierCurveTo方法
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')

ctx.beginPath()
ctx.moveTo(50, 150)
ctx.bezierCurveTo(
  100, 50,   // 控制点1
  200, 50,   // 控制点2
  250, 150   // 终点
)
ctx.stroke()