一、贝塞尔曲线(Bézier Curves)

0、前言

本博客是我在学习的时候翻译前人博客的版本,在翻译过程中出现了很多问题,大家多多反馈。
感谢“密歇根理工大学--计算机科学系--Dr. C.-K. Shene 教授”做出的杰出贡献。

1、介绍

我们的第一个问题是:为什么我们需要新的参数曲线形式?一个直接的答案是,在前一单元(内容太多,建议点击自己去看)讨论的那些参数曲线不是很几何。更准确地说,给定这样一个参数形式,如果不进行进一步分析,很难知道它所代表的底层几何。方程的系数没有任何几何意义,如果修改一个或多个系数,几乎不可能预测形状的变化。因此,设计遵循一定轮廓的曲线是非常困难的。

在实践中,设计师或用户通常不关心底层的数学(当然还有方程式)。他们或多或少专注于完成工作。为此,一个支持用户设计的系统 曲线必须是

1、直观:
        我们期望每个步骤和每个算法都有直观的几何解释。
2、灵活:
        该系统应该为用户提供更多的控制来设计和编辑曲线的形状。创建和编辑曲线的方式应该是简单的、直观的和几何的,而不是通过操纵方程。
3、统一的方法:
        不同类型曲线(如直线、圆锥曲线、三次曲线)的表示、创建和编辑方式必须相同。也就是说,它不需要不同的技术来操作不同的曲线(即,二次曲线和三次曲线)。
4、不变:
        所表示的曲线在平移、旋转、仿射等几何变换下不会改变其几何形状。
5、效率和数值稳定性:
        曲线设计系统的用户可能不关心底层几何结构的美观;但是,他/她希望系统能够快速准确地提供他/她想要的曲线。此外,大量的计算不会“扭曲”曲线的形状(即数值稳定性)。

本单元的重点是一些曲线设计技术,可以满足上述标准。我们将在这里讨论Bézier曲线,并在接下来的两个单元中讨论b样条曲线和NURBS曲线。这些技术的统一主题包括以下优点:

1、用户为系统布置一组控制点,以得出或多或少遵循控制点集趋势的曲线。
2、用户可以改变一些控制点的位置和一些其他特征来修改曲线的形状。不需要方程,因为曲线的方程通常不存储。
3、如果需要,用户可以在不改变曲线形状的情况下添加控制点和其他重要信息。通过这种方式,4、用户可以更自由地编辑曲线,因为添加控制点和其他信息增加了曲线的自由度。
5、用户甚至可以将曲线分成两段进行“微”编辑,然后再将它们合并成一段。
6、在不知道曲线方程的情况下,有非常几何的、直观的和数值上稳定的算法来找到曲线上的点。
7、一旦你知道了曲线,表面对应的东西就几步之遥了。更准确地说,从曲线到曲面的过渡不会造成太大困难,因为你从曲线中学到的东西直接适用于曲面。

我们将从本单元最基本的开始: Bézier曲线。大约在50年代末和60年代初,雪铁龙的保罗·德·卡斯特约和雷诺的皮埃尔·e·巴姆齐耶同时发现了巴姆齐耶曲线。Basis Bspline,或简称b样条,是由N.罗巴切夫斯基知道并研究的,他对数学的主要贡献可能是在18世纪后期所谓的双曲(非欧几里得)几何。然而,我们将采用C. de Boor, M. Cox和L. Mansfield在70年代末开发的现代版本。注意,Bézier曲线是b样条的特殊情况。

b样条曲线和 Bézier曲线都是多项式参数曲线。正如前一单元所讨论的,多项式参数形式不能表示一些简单的曲线,如圆。因此,Bézier曲线和b样条只能表示多项式参数形式所能表示的。通过引入齐次坐标使之有理,将Bézier齐次曲线和b样条推广为有理Bézier齐次曲线和非均匀有理b样条,简称NURBS。显然,有理 Bézier曲线比 Bézier曲线更强大,因为有理Bézier曲线现在可以表示圆和椭圆。同样,NURBS比b样条更强大。这四种曲线表示之间的关系如下所示。

2、Bézier曲线的构造

给定n+1个空间中的P0、P1、P2…Pn控制点,由这些控制点定义的Bézier曲线是

其中系数定义如下:
(
译者建议:可以先从两个点自己慢慢推导,即点在直线上运动,学习参考入口

两个点形式===》简单得到:C(u)=(1-u)P0+uP1,u∈[0,1]

三个点形式==》l=(1-u)P0+uP1,m=(1-u)P1+uP2,C(u)=(1-u)l+um,带入得到:

C(u)=(1-u)^{2}P0+u(1-u)P1+u^{2}P2,u∈[0,1]

四个以上类推得到如下)

因此,在Bézier 曲线上对应于u的点是所有控制点的“加权”平均值,其中权重是系数B_{n,i}(u)。线段P0P1, P1P2,…, Pn-1Pn,称为边(leg),按这个顺序连接形成一条控制折线。许多作者更喜欢把这种控制折线称为控制多边形。函数B_{n,i}(u), 0 <= i <= n,称为Bézier基函数或Bernstein多项式。

注意u的定义域是[0,1]。因此,所有基函数都是非负的。在上面的例子中,由于u和i都可以是0,1 - u和n - i也可以是0,我们采用惯例00等于1。下图显示了由11个控制点定义的Bézier 曲线,其中蓝点是曲线上的一个点,对应于u=0.4。如图所示,曲线或多或少遵循折线。Bézier 曲线的以下特性很重要:

Bézier 曲线的以下特性很重要:

  1. 由n+1个控制点定义的Bézier 曲线的度为n:
    在每个基函数中,u的指数为i + (n - i) = n,因此曲线的度数为n。
  2. C(u)经过P0和Pn:
    如上图所示。曲线通过第一个控制点和最后一个控制点。请用一些简单的代数操作自己验证一下。
  3. Non-negativity:
    所有基函数都是
    非负的。我们之前提到过。
  4. 分割统一:
    在固定u处的基函数和是1。不难证明基函数是表达式1 = (u + (1 - u))^n的二项式展开式中的系数。因此,它们的和是1。此外,由于它们是非负的,我们得出结论,任何基函数的值都在0和1的范围内。

    在上面的左图中,我们有一个由五个控制点定义的Bézier 曲线。它的五个基函数是u中的函数,如右图所示。图中显示u=0.5和所有五个基函数。最右边的竖条显示了将1划分为5个区间的方法,因此被称为统一分割。请注意,分区的颜色与用于绘制其相应基函数的颜色相同。

    因为所有的基函数都在0到1的范围内,并且和为1,所以在加权平均的计算中可以把它们看作是权重。更准确地说,我们可以说“为了计算C(u),我们取控制点Pi的权重B_{n,i}(u)并将它们相加。

  5. 凸包特性:
    这意味着由给定的n + 1个控制点定义的Bézier 曲线完全位于给定控制点的凸包中。一组点的凸包是包含所有点的最小凸集。下图中,11个控制点的凸壳以灰色表示。注意,并非所有控制点都在凸包的边界上。例如,控制点3、4、5、6、8和9位于内部。除了前两个端点外,曲线完全位于凸包中。

    这个属性很重要,因为我们可以保证生成的曲线将在一个可理解和可计算的区域内,而不会超出该区域。

  6. 变异递减特性:
    如果曲线在一个平面上,这意味着没有一条直线与Bézier
    曲线相交的次数比它与曲线的控制折线相交的次数多。

    看看上面的图。黄线与曲线相交3次,与折线相交7次;品红线与曲线相交5次,与折线相交7次;青色线与曲线及其折线相交两次。你可以画其他直线来验证这个性质。

    如果曲线是空间曲线,用“平面”代替“线”就足够了。然而,可能会出现特殊情况;提出一个考虑到这些特殊情况的适当计数方案并不是一件困难的任务。

    那么,这个性质是什么意思呢?它告诉我们曲线的复杂性(即转弯和扭转)并不比控制折线更复杂。换句话说,控制折线比Bézier 曲线更频繁地扭转和转动,因为任意一条线碰到控制折线比碰到曲线更频繁。看一下上图,控制折线比它定义的曲线更复杂

  7. 仿射不变性:
    如果将仿射变换应用于Bézier
    曲线,则可以从其控制点的仿射图像构建结果。这是一个很好的性质。当我们想要对Bézier 曲线进行几何或仿射变换时,这个性质表明我们可以对控制点进行变换,这很容易,一旦得到了变换后的控制点变换后的Bézier 曲线就是由这些新点定义的曲线。

如果u的定义域不是[0,1]怎么办?
有时
Bézier 曲线的定义域是[a,b]而不是[0,1]。因此,需要改变变量(归一化)。我们要做的就是把[a,b]中的u转换成[0,1]中的新u,然后在基函数中使用这个u。将u转换为[0,1]的方法如下:

将u代入基函数B_{n,i}(u)得到:

这些新的基函数在[a,b]定义域上定义了一条Bézier 曲线。我们将在后面的章节中使用这个事实。

简短的总结(重要)
综上所述,要定义n次的
Bézier 曲线,我们需要在空间中选择n + 1个控制点,使它们大致表示所需曲线的形状。然后,如果不符合我们的期望,我们可以移动控制点。当一个或多个控制点被移动时,Bézier 曲线的形状也随之改变。但是,曲线始终处于控制点定义的凸包内(凸包特性),生成的曲线形状比控制折线(变化递减特性)要简单。

3、移动控制点

如果一个控制点移动到一个新的位置,曲线的形状是如何改变的?
假设控制点Pk移动到新的位置Pk + v,其中向量v给出了移动的方向和长度。如下图所示:

设原Bézier 曲线为:

因为新的Bézier 曲线是由P0, P1. ..., Pk+v, ..., Pn,定义的,它的方程D(u)是

在上面的例子中,因为只有第k项使用了不同的控制点Pk + v,重组后我们知道新的曲线是原始曲线和额外的项B_{n,k}(u) v的和。这意味着:

将原曲线上的u对应点沿v方向平移|B_{n,k}(u) v|,得到新曲线上u的对应点。

更准确地说,给定同一个u,在原始曲线上有点 C(u),而在新曲线上有 D(u)并且D(u)= C(u)+ B_{n,k}(u) v。由于 v 给出了运动方向,因此 D(u) 是沿同一方向移动 C(u) 的结果。当然,这个平移的长度是向量 B_{n,k}(u) v的长度。因此,当B_{n,k}(u)达到最大值时,从C(u)到D(u)的变化最大。
更准确地说,给定一个u,我们在原曲线上有点C(u)在新曲线上有点D(u),且 D(u) = C(u) + B_{n,k}(u) v。因为v给出了运动方向,所以D(u)是C(u)在相同方向上运动的结果。这个平移的长度,当然是向量Bn的长度k(u)v。因此,当B_{n,k}(u)达到最大值时,C(u)到D(u)的变化最大。

下图说明了这种效果。黑色和红色曲线都是由9个控制点定义的8度Bézier 曲线。黑色的是原始曲线。如果它的控制点3移动到蓝色向量所指示的新位置,则黑色曲线变为红色曲线。在这两条曲线上各有一个点对应于u=0.5。很明显,C(0.5)向D(0.5)的方向移动。C(0.5)和D(0.5)之间的距离是向量B8的长度,3(0.5)v = 8!/(3!(8-3)!)×0.53(1-0.5)8-3v = 0.22v。因此,该距离约为原控制点3与新控制点3之间距离的22%,如下图所示。

从上面的讨论我们可以得到一个更重要的结论。因为B_{n,k}(u)在开放区间(0,1)内不为零,所以B_{n,k}(u) v在(0,1)内不是零向量。这意味着除了两个端点C(0)和C(1)外,原始曲线上的所有点都被移动到新的位置。因此,我们有

改变控制点的位置会导致Bézier 曲线的形状发生全局变化。
(译者注:沿v方向平移点,则曲线会沿v方向变化

4、在Bézier 曲线上找到一个点:De Casteljau的算法

在构建了Bézier 曲线之后,下一个重要的任务是如何找到特定u在曲线上的点C(u)。一种简单的方法是,将u代入每个基函数中,计算每个基函数与其对应的控制点的乘积,最后将它们相加。虽然这很好,但它在数值上不稳定(即,在评估伯恩斯坦多项式的过程中可能会引入数值误差)。

在下文中,我们将只写下控制点的编号。即P0控制点为00,P1控制点为01,…, 0i代表Pi,…p = 0n。这些数字中的0表示第一次迭代或第0次迭代。之后,它将被1、2、3等所取代。

de Casteljau算法的基本概念是在线段AB中选择一个点C, C将线段AB按u:1-u的比例(即a与C之间的距离与a与B之间的距离之比为u)进行分割,让我们找到确定点C的方法。

从A到B的向量是B - A,因为u是0和1之间的比值,所以C点位于u(B - A),考虑到A的位置,C点为A + u(B - A) = (1 - u)A + uB。因此,给定一个u, (1 -u) A + uB是A和B之间的点C,它将AB分成u:1-u的比例。

de Casteljau算法的思想如下。假设我们想求出C(u) u在[0,1]中。从第一条折线开始,00-01-02-03…-0n,用上面的公式找到从0i到0(i+1)的线段(即连接线)上的一个点1i,该点将线段0i和0(i+1)按u:1-u的比例分割。这样,我们将得到n个点10,11,12 ....1 (n - 1)。他们定义了一条有n - 1条线段的新折线。

在上图中,u = 0.4。10在00和01的线段上,11在01和02的线段上,…, 14在04和05的线段上。所有这些新的点都是蓝色的。

新的点被编号为1i。对这条新的折线应用这个程序,我们将得到n - 1点20,21,…, 2(n-2)和n-2条线段。从这条折线开始,我们可以构造n - 2个点30,31,…, 3(n-3)和n-3条线段。重复这个过程n次会得到一个点n。De Casteljau证明了这是曲线上对应于u的点C(u)

让我们继续看上面的图。设20为线段10和线段11上的点,该点将线段10和线段11按1:1的比例分割。同样,在11和12的线段上选择21,在12和13的线段上选择22,在13和14的线段上选择23。这给出了由20,21,22和23定义的第三条折线。第三条折线有4个点和3条线段。继续这样做,我们将得到一条新的折线,它有三个点30,31和32。从这第四条折线,我们有两点40和41中的第五个点。再做一次,我们得到50点,曲线上的点C(0.4)

这是de Casteljau算法的几何解释,是曲线设计中最优雅的结果之一。


实际计算
基于以上对de Casteljau算法的几何解释,我们给出一种计算方法,如下图所示。

首先,将所有给定的控制点排列成一列,这是图中最左边的一列。对于每一对相邻的控制点,分别画一个东南方向的箭头和一个东北方向的箭头,并在两个相邻箭头的交点处写下一个新的点。例如,如果相邻的两个点是ij和i(j+1),则新的点是(i+1)j。东南(地区)(东北)绑定箭头表示乘以1 - u (代表u)到尾巴的那一点,ij(代表i(j+1)),新的点是和。

因此,从初始列0开始,我们计算列1;从第一列得到第二列,以此类推。最终,经过n次应用,我们会得到一个点n,这就是曲线上的点。下面的算法总结了我们所讨论的内容。它接受一个包含n+1个点的数组P和一个在0和1范围内的u,并返回Bézier 曲线C(u)上的一个点。

  • Input: array P[0:n] of n+1 points and real number u in [0,1]
    Output: point on curve, C(u)
    Working: point array Q[0:n]

    for i := 0 to n do
    • Q[i] := P[i]; // save input

       
    for k := 1 to n do
    • for i := 0 to n - k do
      • Q[i] := (1 - u)Q[i] + u Q[i + 1];
    return Q[0];

递归关系
上面的计算可以递归地表示。首先, 令P0,j 为 Pj ,此时 j = 0, 1, ..., n,即P0,j是第0列的第j个元素。第i列上第j项的计算如下:

更准确地说,Pi,j是(1-u)Pi-1,j(左上角)和Pi-1,j+1(左下角)的和。最终结果(即曲线上的点)是Pn,0。基于这个想法,人们可能会立即想到以下递归过程:

  • function deCasteljau(i,j)
    begin
    • if i = 0 then
      • return P0,j

         
      else
      • return (1-u)* deCasteljau(i-1,j) + u* deCasteljau(i-1,j+1)
    end

看起来简单而简短;然而,它的效率非常低。原因如下。我们首先调用deCasteljau(n,0)来计算Pn,0。else部分将这个调用拆分成另外两个调用,分别是用于计算pn -1,0的deCasteljau(n-1,0)和用于计算pn -1,1的deCasteljau(n-1,1)。

考虑对deCasteljau(n-1,0)的调用。它又分为两个调用,deCasteljau(n-2,0)用于计算pn -2,0, deCasteljau(n-2,1)用于计算pn -2,1。对deCasteljau(n-1,1)的调用分为两个调用,deCasteljau(n-2,1)用于计算pn -2,1, deCasteljau(n-2,2)用于计算pn -2,2。因此,deCasteljau(n-2,1)被调用了两次。如果我们继续扩展这些函数调用,我们会发现几乎所有用于计算Pi,j的函数调用都是重复的,不是一次,而是多次。这有多糟糕?实际上,上面的计算方案与下面计算第n个斐波那契数的方法相同:

  • function Fibonacci(n)
    begin
    • if n = 0 or n = 1 then
      • return 1

         
      else
      • return Fibonacci (n-1) + Fibonacci (n-2)
    end

这个程序需要指数级的函数调用次数(一次练习)来计算Fibonacci(n)。因此,上述de Casteljau算法的递归版本不适合直接实现,尽管它看起来简单而优雅!

一个有趣的观察
de Casteljau算法中的三角计算方案提供了一个有趣的观察结果。看一下下面的计算,在Bézier曲线的7度由8个控制点00,01,…, 07。
让我们考虑在同一列上的一组连续的点作为Bézier曲线的控制点。然后,给定u在[0,1]内,我们如何计算Bézier曲线上的对应点?如果将de Casteljau的算法应用于这些控制点,曲线上的点就是由所选点构成的等边基底的相对顶点!

例如,如果选择的点是02、03、04和05,那么对应于u的这4个控制点定义的曲线上的点是32。看蓝色的三角形。如果选择的点是11、12和13,曲线上的点是31。看这个黄色的三角形。如果选择的点是30、31、32、33和34,曲线上的点是70。

出于同样的原因,70是Bézier曲线上由控制点60和61定义的点。它也是由50、51、52定义的曲线上的点,以及由40、41、42、43定义的曲线上的点。一般来说,如果我们像上面那样选择一个点并绘制一条等边,那么等边的底边由用于计算选定点的控制点组成。

4.1、为什么de Casteljau的算法是正确的?

De Casteljau的算法看起来与计算每个B_{n,k}(u)的简单方法大不相同。一个很自然的问题是:

de Casteljau的算法能正确计算C(u)吗?

答案是肯定的,我们将证明这种说法是正确的。

让我们看一下Bézier曲线的计算,该曲线由7个控制点00、01、02、03、04、05和06定义,如下所示。曲线上的点是60。因为60是从7个控制点00计算出来的,即00,01…, 06,每个输入控制点0i贡献了60的计算。怎么贡献?

让我们研究一下02的贡献。因为每个点ij都乘以1-u(resp, u)用来计算其东南方向(resp.,东北)邻居,02用于计算11和12。因为11(resp, 12)有一个分量02,而11 (resp, 12)则用于计算20和21(resp., 21和22),所以分量02的贡献传播到20、21和22。因此分量02的贡献只能达到上图所示的菱形区域的中间点。

如果我们遵循从0i开始的东北方向的箭头,每一步都会导致第一个索引增加(resp.,减少)一位。参见下图。经过i步,我们将到达i0点。类似地,如果我们遵循从0i开始的东南方向的箭头,每一步都会导致第一个索引增加1;然而,第二个索引从未改变。因此,在n-i步之后,我们将到达点(n-i)i。现在,我们有了菱形的三个角。第四个当然是n0。如下图所示。用上图验证这一说法。

思考我们如何确定0i对n0的计算的贡献?这可以通过从0i“行走”到n0来获得。以上述6度的Bézier曲线为例。如果我们沿着方向路径02、12、21、31、41、51和60,那么我们得到以下结果:

  • 12 has the contribution of (1-u)02.
  • When 12 is used to compute 2112 contributes u12 to 21. Since 12 contains (1-u)0221 will include u(1-u)02.
  • When 21 is used to compute 31, 1-u is multiplied to 21, and, hence, this brings u(1-u)(1-u)02 = u(1-u)202 to 31.
  • Similarly, the computation of 41 and 51 will bring u(1-u)402 to 51.
  • Finally, 51 is multiplied by u to compute 60. This brings the contribution of 02along this pathu2(1-u)4 to 60.

考虑到这个思想,证明de Casteljau算法的正确性非常简单。控制点0i用于计算从0i到n0的所有可能路径的n0。有多少条路径?

译者建议参考好文:javascript - 有关排列组合的一道算法题 - 个人文章 - SegmentFault 思否
用下面的方法思考这个问题。每条路径都有n个箭头。在这n个箭头中,i个箭头是东北方向的。因此,将这些i箭头插入n个位置的方法的数量等于从0i到n0的不同路径的数量。这就是组合系数C(n,i)=(n!)/(i!(n-i)!)因为每条路径的贡献总是ui(1-u)n-i,而且我们有(n!)/(i!(n-i)!)条路径,所以0i对n0的总贡献如下:

因此,控制点Pi对C(u)计算的贡献就是Bernstein多项式B_{n,i}(u)。将所有控制点的贡献加在一起,我们就得到了由控制点P0, P1,…定义的Bézier曲线。Pn。总之,de Casteljau的算法能够正确地计算出曲线上对应于给定u的点。

5、Bézier曲线的导数

为了计算Bézier曲线上一点的切线和法向量,我们必须计算该点的一阶导数和二阶导数。幸运的是,计算Bézier曲线上一点的导数很容易。

回想一下,Bézier曲线由n + 1个控制点P0, P1,…, Pn有如下公式:

其中B_{n,i}(u)定义如下:

由于控制点是常数且与变量u无关,因此计算曲线C'(u)的导数可简化为计算B_{n,i}(u)'s的导数。通过一些简单的代数运算,对于B'n,i(u),我们得到如下结果:

然后,计算曲线C(u)的导数得到:

令 Q0 = n(P1 - P0), Q1 = n(P2 - P1), Q2 = n(P3 - P2), ..., Qn-1 = n(Pn - Pn-1). 由上式可得:

因此,C(u)的导数是由n个控制点n(P1 - P0), n(P2 - P1), n(P3 - P2),…, n(Pn - Pn-1)。定义的n - 1次的Bézier曲线。这种导数曲线通常称为原始Bézier曲线的速拍图(hodograph )。注意,Pi+1 - Pi是从Pi到Pi+1的方向向量,而n (Pi+1 - Pi)是方向向量的n倍长。一旦确定了控制点,就可以立即得到其导数曲线的控制点。下面的左图显示了一个7度的Bézier曲线,右图显示了它的导数是一个6度Bézier曲线。

Bézier曲线切线到他们的第一条线段和最后一条线段:
让u = 0和u = 1得到C'(0) = n (P1 - P0)和C'(1) = n (Pn - Pn-1)第一个意味着在u = 0处的切向量是P1 - P0乘以n的方向。因此,在指示方向的第一条线段是与Bézier曲线的切线。第二个表示在u = 1处的切向量是Pn- Pn-1乘以n的方向。因此,在指示方向的最后一条线段是Bézier曲线的切线。下图很好地展示了这一特性。

C1-Continuity连接两条Bézier曲线
一个Bézier曲线相切到它的第一个和最后一个腿为我们提供了一个技术连接两个或更多Bézier曲线一起设计一个所需的形状。设第一条曲线C(u)由m + 1个控制点P0, P1, P2,…,点。设第二条曲线D(u)由n + 1个控制点Q0, Q1, Q2,…Qn。如果我们想将这两条Bézier曲线连接在一起,则Pm必须等于Q0。这保证了
C^{0}连续连接。回想一下,第一条曲线与它的最后一条腿相切,第二条曲线与它的第一条腿相切。因此,为了实现平滑过渡,Pm-1, Pm = Q0,和Q1必须在同一条线上,以便从Pm-1到Pm的方向和从Q0到Q1的方向相同。如下所示:

虽然以这种方式连接两个Bézier曲线看起来很平滑,但它仍然是一个C^{0} continuity连接,还不是C^{1} continuity。但是,它是G^{1},因为它们有相同的切向量方向。为了保证C^{1} continuity,我们需要保证第一条曲线C'(1)在u = 1处的切向量,和第二条曲线D'(0)在u = 0处的切向量,是相等的。也就是说,下列代码必须保持不变:

这一关系表明,为了在连接点处达到C1连续性,第一条曲线的最后一段长度(即|pm - pm-1|)与第二条曲线第一段长度(即|q1 - q0|)的比值必须为n/m。由于次数m和n是固定的,我们可以调整pm-1或q1在同一行上的位置,从而满足上述关系。

下面的左图有两条Bézier曲线。浅色折线定义了一条4度的Bézier曲线,而深色折线定义了一条5度的Bézier曲线。由于第一条曲线的最后一段和第二条曲线的第一段不在同一条线上,因此两条曲线不能平滑地连接。右图显示了两条Bézier曲线,它们与连接点上的直线相切。然而,它们不是C^{1}连续的。左边的曲线是4次,右边的曲线是7次。但是,左边曲线的最后一段与第二段曲线的第一段的比值似乎接近1,而不是7/4=1.75。为了达到C^{1}连续性,我们应该增加(resp.减少)最后一段(resp. 第一段左边(resp.,右边)线段的长度。然而,它们是G^{1}连续的。

这个切线性质还有一个有趣的应用。如果我们让第一个控制点和最后一个控制点相同(即P0 = Pn), P1, P0和Pn-1共线,则生成的Bézier曲线将是闭合曲线,并且在连接点G^{1}连续,如下图所示。要在P0处实现C1连续,必须有P1 - P0 = Pn- Pn-1(即第一和最后两条腿长度相同,P1, P0 = Pn, Pn-1共线)。

请注意,虽然上面的曲线看起来像一个椭圆,但这并不是因为这条曲线是6次的,Bézier曲线是多项式,不能表示圆和椭圆

导数与de Casteljau算法的关系
让我们重写Bézier曲线的导数如下:

因此,一条Bézier曲线的导数是两条n-1次Bézier曲线的差。为简单起见,设这两条曲线为C1(u)和C2(u):

根据这些定义,我们知道第一条曲线C1(u)是由控制点P1, P2,…, Pn,即第二条曲线C2(u)由控制点P0, P1,…, Pn-1,其导数为:

因此,理论上,要计算C(u)在特定u处的导数,我们可以使用de Casteljau的算法来计算C1(u)和C2(u)。然后,计算它们的差,然后乘以n,得到C'(u)。因此,利用de Casteljau算法可以同时计算曲线C(u)上的点及其切向量C'(u)。

现在我们应用de Casteljau算法来计算C1(u)和C2(u)。参见下图。最左边的列包含所有给定的控制点。点n0是曲线上的点C(u),连接(n-1)0和(n-1)1的线段是de Casteljau网的最后一条控制折线。事实上,n0位于(n-1)0和(n-1)1的线段中,并将其除以u:1-u。

由于曲线C1(u)由控制点01,02,…0n,根据上面的图我们知道(n-1)1是曲线C1(u)上的点。类似地,由于C2(u)由控制点00,01,…, 0(n-1),根据上面的图我们知道(n-1)0是曲线C2(u)上的点。因此,向量C1(u)-C2(u)就是从点(n-1)0到点(n-1)1的向量,而C(u)的导数是n乘以C1(u)-C2(u)

这是什么意思?由于de Casteljau网从(n-1)0到(n-1)1的线段与C(u)的切向量具有相同的方向,因此它与C(u)的曲线相切!总之,我们有

The last polyline, actually a line segment, of a de Casteljau net is tangent to the Bézier curve at C(u).

翻译过来即"de Casteljau网的最后一条折线,实际上是一条线段,与Bézier曲线在C(u)处相切。"

下图展示了一个例子。图中的点对应于u = 0.5。很明显,de Casteljau网的最后一条线段与C(0.5)相切。

高阶导数
计算Bézier曲线的高阶导数是一件简单的事情。回想一下,C(u)的导数如下所示:

将导数公式应用于上面的Bézier曲线,得到以下结果,它给出了原始Bézier曲线的二阶导数:

在得到C'(u)和C " (u)后,可以很容易地计算出C(u)处的移动三角线和曲率。高阶导数可以通过递归应用导数公式得到。

为了简洁地表示高阶导数,我们将使用有限差分。设Di0为控制点Pi,0 <= i <= n.,然后定义第一级差值Di1为上一级差值:

注意,第一个水平差只有n个点,比给定的控制点数量少1。更准确地说,第一层差异如下所示。


第二级差定义为第一级点的差

第二级差有n-1个点。重复这个过程,我们可以定义第k层差如下。第k级差是n-k+1点

有了这些符号,就可以简洁地表示高阶导数。首先,让我们用Di1重写C'(u)如下:

对C'(u)求导得到C''(u)在Di2中的公式:

对C''(u)做同样的操作,我们得到了C'''(u)在Di3中的公式:

继续这个过程,我们可以计算第k阶导数C[k](u)的Dik's:

因此,要计算C(u)在特定u处的k阶导数,我们首先计算Dik,并应用de Casteljau的算法来计算由Dik定义的Bézier曲线上与u对应的点。更准确地说,我们可以将所有的Di0排列在第0列上,将东南方向和东北方向的箭头分别指定为-1和1,以计算Di1的第1列。重复上述操作,直到到达第k列,如下所示:

最后,将de Casteljau算法应用于列k上的点u,并将结果乘以n(n-1)(n-2)…(n-k+1),得到向量C[k](u)!

6、分割Bézier曲线

分割曲线的意义是将给定的Bézier曲线在C(u)为某些u切割成两个曲线段,每一个仍然是Bézier曲线。因为得到的Bézier曲线必须有自己的新控制点,所以原始的控制点集被丢弃了。此外,由于原始n次Bézier曲线被分割为两段,每段都是原始n次Bézier曲线的一个子集,因此得到的Bézier曲线必须是n次曲线。

我们的问题是:

Given a set of n + 1 control points P0, P1, P2, ..., Pn and a parameter value u in the range of 0 and 1, we want to find two sets of n+1 control points Q0, Q1, Q2, ..., Qn and R0, R1, R2, ..., Rn such that the Bézier curve defined by Qi's (resp.Ri's) is the piece of the original Bézier curve on [0,u] (resp., [u,1]).
即“ 给定一组n + 1个控制点P0, P1, P2,…, Pn和参数值u在0和1的范围内,我们想找到两组n+1的控制点Q0, Q1, Q2,…, Qn和R0, R1, R2,…, Rn使得Bézier曲线由Qi's(resp.,  Ri's)为原始Bézier曲线在[0,u]上的段(分别为:[u, 1])

虽然正确性证明有点乏味,但算法实际上非常简单。事实上,de Casteljau计算曲线上点C(u)的算法已经提供了所有必要的信息。下图左图显示了应用de Casteljau算法计算C(u)的所有中间步骤,右图显示了曲线在C(u)处的细分及其相应的控制折线。

回想一下de Casteljau算法中的三角计算方案。对于给定的u,计算C(u)需要n次迭代。在计算过程中,可以收集每列的第一个点和最后一个点,最后收集第一个点的集合。, last)点给出了对应于定义在[0,u]上的原始曲线片段的细分。[u, 1])。因此,在下面的三角形方案中,箭头方向的上边缘和箭头反方向的下边缘分别提供第一个和第二个曲线段的控制点。

回想一下de Casteljau算法中的三角计算方案。对于给定的u,计算C(u)需要n次迭代。在计算过程中,可以收集每列的第一个点和最后一个点,最后收集第一个点的集合 (resp. , last) 点给出了对应于定义在[0,u] (resp. , [u, 1])上的原始曲线片段的细分。因此,在下面的三角形方案中,箭头方向的上边缘和箭头反方向的下边缘分别提供第一个和第二个曲线段的控制点。

请注意,由于由50和51定义的线段与曲线在点60处相切,因此左曲线的最后一段(即点50到点60)与左曲线相切,而右曲线的第一段(即点60到点51)与右曲线相切。

为什么我们需要曲线细分?
曲线细分有着广泛的应用。例如,它可以用于计算两条Bézier曲线的相交,绘制Bézier曲线,使曲线设计更容易。假设我们设计了一条曲线,但没有达到我们的预期。在这种情况下,我们可能希望在适当的点上将曲线细分为两部分,一个满意的部分和一个不满意的部分。然后,我们可以忘记满意的,专注于不满意的。
我们可以根据需要进行多次细分。但是,我们应该记住,如果我们想保持细分曲线段的平滑连接,我们应该保持连接点和它的两个相邻邻居共线。

7、Bézier曲线的升度

“升度的作用--随着次数不断增加到无穷大,控制折线会接近Bézier曲线,并将其作为极限位置”(译者注:

许多应用程序涉及两个或更多Bézier曲线要求所有涉及的曲线具有相同的度。此外,虽然高阶Bézier曲线需要较长的处理时间,但它们在设计形状时具有较高的灵活性。因此,在不改变曲线形状的情况下增加Bézier曲线的度是非常有用的。注意,“不改变曲线的形状”是关键;否则,仅仅增加Bézier曲线的次数没有任何实际意义。在不改变曲线形状的情况下增加Bézier曲线的度称为升度。下面只讨论一个算法。

假设我们有一条Bézier n次曲线,由n + 1个控制点P0, P1, P2,…, Pn,并且我们希望在不改变其形状的情况下,将这条曲线的次数增加到n + 1。由于n + 1 Bézier曲线由n + 2个控制点定义,我们需要找到这样一组新的控制点。显然,P0和Pn一定在新的集合中因为新的曲线也经过它们。因此,我们只需要n个新的控制点。设新的控制点集合为Q0, Q1, Q2,…Qn + 1。如上所述,Q0 = P0, Qn+1 = Pn。其他控制点的计算方法如下:

如果你对上面的通用公式不满意,下面是Q1到Qn每个控制点的公式。

原始折线的每条分支都包含一个新的控制点。更准确地说,腿Pi-1Pi包含新的控制点气。回忆一下讨论de Casteljau的算法,AB上的点C除以AB的比例为u:1-u,可以写成C = (1 -u) a + uB。从新的控制点的公式中,我们看到Qi将线段Pi-1Pi以1 - i/(n+1):i/(n+1)的比例划分。然而,与de Casteljau的算法不同的是,该比值不是一个常数,而是随着i的值而变化。

一旦得到新的控制点集合,就可以丢弃原来的控制点集合。由于原始控制折线的每条分支都包含一个新的控制点,所以用新的控制点替换旧的集合的过程可以看作是砍掉原始控制点的拐角。下图展示了这种切角效果。图中为4次Bézier曲线,红色矩形表示控制点,蓝色虚线段表示控制折线。将度数增加到5后,新的控制折线以实线段的形式显示。很明显,各个角落都减少。左边的表格给出了原始控制折线每条边的比例。

i1 - i/(n+1)
10.8
20.6
30.4
40.2
     

注意,只要系统允许,可以重复使用提升度。还要注意,随着度数的增加,控制点的数量也会增加。此外,新的控制折线向曲线移动。在下图中,我们从一个具有7个控制点的6度Bézier曲线开始。然后,将其次数增加到7、8、10、15和29。从图中可以看出,随着度数的增加,曲线的形状不会发生变化,控制折线也越来越接近曲线。最终,随着次数不断增加到无穷大,控制折线会接近曲线,并将其作为极限位置。

 

为什么升阶算法是正确的?

我们将建立升阶算法的正确性。同样,它需要简单但繁琐的计算。假设给定的Bézier n次曲线C(u)由控制点P0, P1,…, Pn,提升次数Bézier n+1次曲线D(u)由控制点Q0 = P0, Q1, Q2,…, Qn, Qn+1 = Pn。我们将找到n个未知的控制点Qi。

我们的策略很简单。因为C(u)和D(u)是同一条曲线,它们在任意u处的导数应该相等。因此,通过比较第一、第二、第三、……n阶导数在0处,我们就能确定Qi是什么。为了简化计算,我们将使用数学公式。更准确地说,我们通过比较0处的一阶导数来求Q1。然后,我们假设Q1, Q2,…,计算Qk-1,并使用这些已知结果计算Qk。将这个过程迭代n次,我们将计算出所有新的控制点。

基本条件,C(u)的一阶导数是:

因此,u = 0处的导数为n(P1 - P0)。类似地,D(u)在u = 0时的导数为(n+1)(Q1-Q0) = (n+1)(Q1-P0)。因为这两个导数是相等的

求解Q1,得到第一个结果:

推导过程:
对于归纳步骤,我们假设计算Q0 = P0, Q1,…, Qk-1是正确的,并表明Qk也是正确的。这是通过比较原始曲线C(u)和升阶曲线D(u)的k阶导数来实现的。

就像在基线情况下,我们实际上不需要完整的曲线。设C(u)和D(u)的k阶导数分别为C[k](u)和D[k](u)。u = 0处的第k个导数向量是C[k](0)和D[k](0)。因为C(u)和D(u)是同一条曲线,它们在u = 0处的k阶导数应该是相同的。更准确地说,我们应该有C[k](0) = D[k](0)。根据这个关系式和已知的Q0 = P0, Q1,…, Qk-1,我们将计算Qk。

在高阶导数的讨论中,我们知道曲线C(u)的k阶导数函数为

这里 Dki 

组合系数C(k,j)为

Dki的计算是本单元最后的练习。如果你想了解详情,请点击这里。因此,C[k](0)的计算如下:

类似地,我们可以计算D[k](0)。注意曲线D(u)的阶为n+1。这就是乘数是(n+1)n(n-1)…(n-k+2)的原因。

设C[k](0)等于D[k](0),并消去一些参数,得到如下结果:

事实上,我们可以做得更多。由于我们知道(1)Qk是未知的,Q0 = P0,我们可以将这两项从D[k](0)的和中取出。现在,求和从1到k-1。为了使C[k](0)中的求和相容,我们还将Pk和P0项从求和中取出。结果如下:

重写这些结果如下:

根据归纳假设,当j在0和k-1范围内时,Qj已知,计算公式为:

将Qj代入求和项中,得到:

将这个结果代回到(n+1)Qk的表达式中,并将求和分成两部分:

从第一个求和中取出Pk-1后,第一个求和为:

然后,我们从第二个求和中取出P0项:

然而,在这个表达式中,索引j的取值范围是2到k-1。为了使它与第一个求和兼容,我们可以使用一个新的下标h = j - 1。在替换之后,第二个求和如下所示:

注意,这两个求和中的对应项具有不同的符号(即第一个求和中的(-1)k-j+1=(-1)×(-1)k-j,第二个求和中的(-1)k-h)。因此,将这两个求和相加,将抵消求和中的所有项,得到如下结果:

把它代回到(n+1)Qk的表达式中

将表达式除以n+1,就得到了我们想要的结果:

因此,k的升阶计算也成立,升阶算法是正确的。

8、问题(译者注:这里是笔者要求大家思考的10个问题)

  1. 给定xy平面上的3个控制点(-1,0)、(0,1)和(2,0),执行如下操作:
    • 写出它的Bézier曲线方程。
    • 把这个方程展开成等价的常规形式。
    • 因为有三个控制点,所以有三个Bézier系数。写下他们的方程式并画出图表。.
    • 用你的计算器找到足够多的点,使用常规的参数形式,并画出曲线。
    • 在曲线上找到u = 0、0.25、0.5、0.75和1的点,用传统形式表示。
    • 使用de Casteljau的算法找到曲线上u = 0、0.25、0.5、0.75和1对应的点。
    • 细分Bézier曲线在u = 0.4,并列出结果曲线段的控制点。
    • 将这条曲线的度数增加到3,并列出新的控制点集。然后,将度数增加到4,并列出新的控制点集合。
  2. 根据变差递减特性,如果有一条线或一个平面经过一个控制点,或者包含一个控制折线的线段,该怎么办?建议正确计算交叉点,并用例子验证你的说法。
  3. Bézier由三个控制点P0、P1和P2定义的2次曲线是圆锥曲线的一部分。这是什么类型的圆锥曲线?它是抛物线、双曲线还是椭圆的一部分?你可以假设给定的控制点在xy坐标平面上。
  4. 设Bézier曲线C(u) (r。, n次的D(u))由控制点P0, P1,…, Pn(代表;, o0, q1,…Qn)。如果曲线是相同的(即对于[0,1]中的每个u, C(u) = D(u)),那么对应的控制点也是相同的(即对于所有0 <= i <= n, Pi = Qi)。
    Hint:提示:首先证明如果(1-u)A+uB对于[0,1]范围内的每个u都是零向量,那么A和B都是零向量。然后,利用de Casteljau的算法来证明Pi - Qi对于所有的0 <= i <= n都是零向量。
  5. 设Bézier n次曲线C(u)由控制点P0, P1,…Pn。
    1. Prove the following:

    2. 证明曲线C(u)可以重写为下面的矩阵形式:

      其中项mij定义如下:

    因此,Bézier曲线可以用u0 = 1, u1, u2, ....的传统多项式形式重写,联合()。这就是所谓的单项形式(monomial form ),而基函数是u0 = 1 u1 u2 ....,联合。然而,使用这种单项形式在计算上是不稳定的。
  6. 表明Bn,i(u)的最大值出现在u = i/n处,最大值为

  7. 用微积分知识验证下面的结果:
    • the derivative of Bn,i(u):

    • the derivative of Bézier curve p(u):

  8. 讨论了两条Bézier曲线c1 -连续的连接问题,假设曲线的定义域为[0,1]。假设第一条曲线的定义域为[0,s],第二条曲线的定义域为[s,1]。重新计算。你的结论是什么?是否需要修改?
  9. Prove the following:

    其中Dik是第k个差值点,C(k,j)是二项式系数,定义如下:

    使用这个公式,我们可以使用原始的控制点来表示更高的导数,而不是使用有限的差分点。

  10. 将一条Bézier p次曲线在s处细分后,得到两条Bézier曲线,一条在区间[0,s]上,另一条在区间[s,1]上。表明这两条曲线在连接点处C1连续。
    提示:假设在[0,s]上曲线的最后两个控制点是Pp-1和Pp,在[s,1]上曲线的前两个控制点是Q0和Q1。那么,我们有Pp-1, Pp = Q0, Q1在同一直线上,由于细分,Pp-1到Pp = Q0的距离与Pp = Q0到Q1的距离之比等于s。现在,改变两条曲线的变量,使它们的定义域为[0,1]。一个简单的计算就能得出想要的结论。

9、References

以下是关于Bézier曲线的简短列表。Bézier和de Casteljau的论文包含了他们工作的一些非常有趣的评论。罗杰斯的书中包含了一些关于Bézier的历史观点;不幸的是,这本书根本没有提到de Casteljau。Farin和Hansford的书涵盖了计算机辅助几何设计(Computer-Aided Geometric Design, CAGD)的最基本的主题,而Farin的书是一本非常受欢迎的CAGD教科书。Hoschek和Lasser的教科书也讨论了Bézier曲线;然而,它更先进。最后,Piegl和Tiller的书包含了Bézier和其他曲线和曲面的许多算法和代码。

  • Pierre E. Bézier, The First Years of CAD/CAM and the UNISURF CAD System, in Fundamental Developments of Computer-Aided Geometric Modeling, edited by Les Piegl, Academic Press, 1993, pp. 13-26.
  • Paul de Casteljau, Polar Forms for Curve and Surface Modeling as Used at Citroen, in Fundamental Developments of Computer-Aided Geometric Modeling, edited by Les Piegl, Academic Press, 1993, pp. 1-12.
  • Gerald Farin, Curves and Surfaces for CAGD: A Practical Guide, fifth edition, Academic Press, 2002.
  • Gerald Farin and Dianne Hansford, The Essentials of CAGD, A K Peters, 2000.
  • Josef Hoschek and Dieter Lasser, Fundamentals of Computer Aided Geometric Design, translated from the German 1989 edition by Larry L. Schumaker, A K Peters, 1993.
  • Les Piegl and Wayne Tiller, The NURBS Book, second edition, Springer-Verlag, 1997.
  • David F. Rogers, An Introduction to NURBS with Historical Perspective, Academic Press, 2001.
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值