贝塞尔曲线与de Casteljau算法

贝塞尔曲线与de Casteljau算法

一、简介

前言

贝塞尔曲线原理、推导及Matlab实现这篇文章中,详细地介绍了贝塞尔曲线的原理、推导过程以及Matlab实现。文章中计算贝塞尔曲线所采用的方法是定义法,该方法简洁易懂,不过其中的二项式系数 ( n i ) = n ! i ! ⋅ ( n − i ) ! \left( \begin{matrix} n \\ i \end{matrix} \right) = \frac{n !}{i ! \cdot (n - i) !} (ni)=i!(ni)!n!看似简单,实则在高阶时会带来非常大的计算量。对于程序设计来说,这样的开销显然是不太能够接受的。所以,本文将会引用一个新的算法——de Casteljau算法

趣闻

de Casteljau算法是雪铁龙公司一位名为Paul de Faget de Casteljau的工程师在1963年的发明,理论上来说Paul de Faget de Casteljau才是贝塞尔曲线的“第一位发明者”。他发现对于汽车的几何外形设计,与其定义一条以长度为参数的曲线,不如定义一系列控制点作为控制曲线的参数,通过控制点的位置变化来影响曲线形态的变化。这一举动对于汽车设计产业而言无疑是创新性的,所以在初期遭到了公司内部的质疑,但最终这位年轻的工程师还是说服公司采纳了他的算法。该算法经采用后,最终取得了成功,雪铁龙公司要求Casteljau对de Casteljau算法严格保密,直到8年之后,这位工程师的成果才为世人所知。

同期,尽管雪铁龙公司对de Casteljau算法进行了严密的保护,但其竞争对手雷诺公司还是了解到Casteljau的工作,并且也开始对该方向进行研究。1966年,雷诺公司的工程师Pierre Bézier和他的团队独立重现了Casteljau的研究成果,并将这条曲线正式命名为Bézier curve,即我们熟知的贝塞尔曲线。与雪铁龙公司的态度相反,雷诺公司允许Bézier发表他的研究成果,所以更为世人所熟知的是贝塞尔曲线而不是de Casteljau算法

二、原理

相比定义法在求取高阶贝塞尔曲线时带来的大量计算数值不稳定性de Casteljau算法又快又稳定,并且更加贴近贝塞尔曲线的特性。

de Casteljau算法可以看作是重复线性插值repeated linear interpolation)的过程。

重复线性插值

重复线性插值是指在数据点之间使用线性插值来估算其他数据点的值,通常是在时间序列或连续数据中使用的一种插值方法。

而线性插值是一种简单的插值方法,它假定在两个已知数据点之间的值变化是线性的。在重复的线性插值中,这个过程会在数据点之间不断重复进行,以填补整个数据序列中的缺失值或生成更密集的数据点。

重复的线性插值的过程可以通过下述的三张过程图来直观地感受到:


重复线性插值1


重复线性插值2


重复线性插值3

拥有上述知识后,开始着手构建贝塞尔曲线。

一次曲线(线性)

一次曲线贝塞尔曲线由两个端点 P 0 P_0 P0 P 1 P_1 P1组成,曲线由线段直接连接这两个点,是一条最简单的线性曲线。

P ( t ) P(t) P(t)描述一条由 P 0 P_0 P0 P 1 P_1 P1的直线,即使用了标准的线性插值:

P ( t ) = ( 1 − t ) P 0 + t P 1 , t ∈ [ 0 , 1 ] (1) P(t) = (1 - t) P_0 + t P_1, t \in [0, 1] \tag1 P(t)=(1t)P0+tP1,t[0,1](1)


>线性贝塞尔曲线演示动画

线性贝塞尔曲线演示动画,t在[0,1]区间

二次曲线

二次贝塞尔曲线由定点 P 0 , P 1 , P 2 P_0, P_1, P_2 P0,P1,P2确定,通过三次线性插值来构建:

P 0 1 = ( 1 − t ) P 0 + t P 1 P 1 1 = ( 1 − t ) P 1 + t P 2 P ( t ) = P 0 2 = ( 1 − t ) P 0 1 + t P 1 1 (2) \begin{matrix} P_0^1 = (1 - t) P_0 + t P_1 \\ P_1^1 = (1 - t) P_1 + t P_2 \\ P(t) = P_0^2 = (1 - t) P_0^1 + t P_1^1 \end{matrix} \tag2 P01=(1t)P0+tP1P11=(1t)P1+tP2P(t)=P02=(1t)P01+tP11(2)

( 2 ) (2) (2)也可以表述为:

  • P 0 , P 1 P_0, P_1 P0,P1之间的连续点 P 0 1 P_0^1 P01,描述一条线性贝塞尔曲线;
  • P 1 , P 2 P_1, P_2 P1,P2之间的连续点 P 1 1 P_1^1 P11,描述一条线性贝塞尔曲线;
  • P 0 1 , P 1 1 P_0^1, P_1^1 P01,P11之间的连续点 P 0 2 P_0^2 P02,描述一条二次贝塞尔曲线。


二次贝塞尔曲线演示动画

二次贝塞尔曲线演示动画,t在[0,1]区间

三次曲线

三次曲线由定点 P 0 , P 1 , P 2 , P 3 P_0, P_1, P_2, P_3 P0,P1,P2,P3确定,通过六次线性插值来构建:

P 0 1 = ( 1 − t ) P 0 + t P 1 P 1 1 = ( 1 − t ) P 1 + t P 2 P 2 1 = ( 1 − t ) P 2 + t P 3 P 0 2 = ( 1 − t ) P 0 1 + t P 1 1 P 1 2 = ( 1 − t ) P 1 1 + t P 2 1 P ( t ) = P 0 3 = ( 1 − t ) P 0 2 + t P 1 2 (3) \begin{matrix} P_0^1 = (1 - t) P_0 + t P_1 \\ P_1^1 = (1 - t) P_1 + t P_2 \\ P_2^1 = (1 - t) P_2 + t P_3 \\ P_0^2 = (1 - t) P_0^1 + t P_1^1 \\ P_1^2 = (1 - t) P_1^1 + t P_2^1 \\ P(t) = P_0^3 = (1 - t) P_0^2 + t P_1^2 \\ \end{matrix} \tag3 P01=(1t)P0+tP1P11=(1t)P1+tP2P21=(1t)P2+tP3P02=(1t)P01+tP11P12=(1t)P11+tP21P(t)=P03=(1t)P02+tP12(3)

( 2 ) (2) (2)也可以表述为:

  • P 0 , P 1 P_0, P_1 P0,P1之间的连续点 P 0 1 P_0^1 P01,描述一条线性贝塞尔曲线;
  • P 1 , P 2 P_1, P_2 P1,P2之间的连续点 P 1 1 P_1^1 P11,描述一条线性贝塞尔曲线;
  • P 2 , P 3 P_2, P_3 P2,P3之间的连续点 P 2 1 P_2^1 P21,描述一条线性贝塞尔曲线;
  • P 0 1 , P 1 1 P_0^1, P_1^1 P01,P11之间的连续点 P 0 2 P_0^2 P02,描述一条二次贝塞尔曲线;
  • P 1 1 , P 2 1 P_1^1, P_2^1 P11,P21之间的连续点 P 1 2 P_1^2 P12,描述一条二次贝塞尔曲线;
  • P 0 2 , P 1 2 P_0^2, P_1^2 P02,P12之间的连续点 P 0 3 P_0^3 P03,描述一条三次贝塞尔曲线。


三次贝塞尔曲线演示动画

三次贝塞尔曲线演示动画,t在[0,1]区间

高次曲线

为构建更高次的曲线,则通过更多次的线性插值来构建,这就是重复线性插值。

如,四次曲线通过十次线性插值:


四次贝塞尔曲线演示动画

四次贝塞尔曲线演示动画,t在[0,1]区间

五次曲线通过十五次线性插值:


五次贝塞尔曲线演示动画

五次贝塞尔曲线演示动画,t在[0,1]区间

推广

对上文总结得出,只要对控制多边形上的各点进行线性插值,就可以得到更少的点和线段。

比如有四个控制点的三次曲线,对四个点进行线性插值,得到三个点和对应的三条线段;对三个点进行线性插值,得到两个点和对应的两条线段;最终对两个点进行线性插值,得到一个可以描述三次曲线的目标点。

该过程可以用下图自下而上的金字塔构建方法形象地给出:


金字塔

金字塔的基底为控制点,顶尖为目标点

故对于任意一点,它都可以由下式表示:

P i j ( t ) = ( 1 − t ) P i j − 1 + t P i + 1 j − 1 , i = 0 , 1 , ⋯   , n ; j = 0 , 1 , ⋯   , i (4) P_i^j(t)= (1 - t) P_i^{j - 1} + t P_{i + 1}^{j - 1}, i = 0, 1, \cdots , n; j = 0, 1, \cdots , i \tag4 Pij(t)=(1t)Pij1+tPi+1j1,i=0,1,,n;j=0,1,,i(4)

特性

观察贝塞尔曲线,从几何意义上来看,当曲线参数 t = 0 t = 0 t=0时,对应曲线的第 0 0 0个(首个)控制点,即 P ( 0 ) = P 0 P(0) = P_0 P(0)=P0;当 t = 1 t = 1 t=1时,对应曲线的第 n n n个(末尾)控制点,即 P ( 1 ) = P n P(1) = P_n P(1)=Pn

这便是贝塞尔曲线的端点插值特性

三、应用

Matlab实现

main.m

%% 初始化
clc
clear

%% 输入
n = input("请输入曲线阶数:\n");
P = pos_input(n);  % 获取控制点坐标

%% 调用贝塞尔曲线计算函数
t = linspace(0, 1, 1000);  % 生成一系列t参数,范围在[0, 1]
curve = BC_deCasteljau(P, t);  % 计算贝塞尔曲线
%% 绘制贝塞尔曲线
plot(curve(:, 1), curve(:, 2), '-b');  % 绘制贝塞尔曲线
hold on;
plot(P(:, 1), P(:, 2), '-ro');  % 绘制控制点
title(sprintf('贝塞尔曲线 (阶数: %d)', n));
xlabel('X');
ylabel('Y');
grid on;
legend('贝塞尔曲线', '控制点');
hold off;

BC_deCasteljau.m(计算贝塞尔曲线)

function B = BC_deCasteljau(control_points, t)
    % 通过de Casteljau算法计算贝塞尔曲线
    n = size(control_points, 1) - 1;  % 通过控制点个数获取阶数
    B = zeros(length(t), size(control_points, 2));  % 初始化曲线矩阵
    for k = 1 : length(t)  % 每个参数t都对应一个曲线坐标
        curve = control_points;  % 复制控制点坐标
        for i = 1 : n  % 通过线性插值从1次到n次,如同金字塔第1层至第n层
            for j = 1 : n - i + 1  % i次曲线对应的点数,如同金字塔第n层长度
                % i次曲线第j点由(i - 1)次曲线的第j点和第j + 1点线性插值得到
                curve(j, :) = (1 - t(k)) * curve(j, :) + t(k) * curve(j + 1, :);
            end
        end
        % 取n次曲线的第1个(也是唯一一个)坐标点作为第k个t参数对应的曲线坐标
        % 如同金字塔塔尖的点
        B(k, :) = curve(1, :);  
    end
end

pos_input.m(选择输入控制点 OR 随机生成)

function P = pos_input(I)
   % 输入贝塞尔曲线控制点 
    P = ones(I, 2);  % 初始化坐标矩阵
    switch input("是否使用随机点?(范围[-100, 100], y = Yes, n = No):\n", 's')
        case 'y'
            P = generate_random_points(I + 1);
        case 'n'
            for i = 0 : I
                P(i + 1, :) = input("请输入输入坐标点P" + num2str(i) + "(以一维矩阵形式输入): \n");
            end
        otherwise
            disp("请选择 y 或者 n噢~\n");
            return
    end
end

generate_random_points.m

function random_points = generate_random_points(n)
    % 生成n个随机点的坐标,范围为[-100, 100]
    random_points = zeros(n, 2);  % 初始化存储点坐标的数组
    % 生成随机坐标
    for i = 1 : n
        % 生成x坐标
        random_points(i, 1) = rand * 200 - 100; % [-100, 100]
        % 生成y坐标
        random_points(i, 2) = rand * 200 - 100; % [-100, 100]
    end
end

曲线绘制


七阶贝塞尔曲线

七阶贝塞尔曲线


九阶贝塞尔曲线

九阶贝塞尔曲线

四、参考

  1. The de Casteljau Algorithm for Evaluating Bezier Curves
  2. Bézier 曲线与 De Casteljau 算法
### 回答1: 贝塞尔曲线Casteljau算法是一种递归算法,用于计算贝塞尔曲线上的点。该算法将曲线分成若干个小段,每个小段都可以通过递归地计算出其上的点。具体来说,该算法将控制点按照一定的比例分成两部分,然后分别对这两部分递归地应用Casteljau算法,直到只剩下一个点为止。最终,这些点组成的序列就是贝塞尔曲线上的点。 ### 回答2: 贝塞尔曲线是计算机图形学中常用的一种曲线,其特点是能够描述复杂的曲线轨迹并且可以通过控制点来调整曲线的形状。 而Casteljau算法是一种递归计算Bezier曲线的方法,它被广泛用于计算机图形学中的曲线绘制问题,也常常被用于解决曲线拟合和曲线插值问题。 该算法的核心思想是将贝塞尔曲线分割成多个子曲线,对于每个子曲线,通过递归继续分割,最后得到所有的曲线段上的点坐标。具体来说,算法的流程如下: 1. 首先,将所有的控制点构成第一级节点,执行递归操作。 2. 在每次递归中,将当前级别的节点复制到下一级节点,并计算中间点,一直递归到最底层。 3. 在最底层的节点上,可以计算该节点对应的曲线上的点坐标。 4. 不断合并曲线段上的点坐标,得到整个贝塞尔曲线Casteljau算法的优点在于递归过程中可以根据需要细分控制多边形的深度和精度,能够灵活地应对不同的问题。同时,算法的时间复杂度为O(n^2),也比其他传统的Bezier曲线计算方法更高效。 熟悉Casteljau算法,能够更好地理解Bezier曲线的绘制原理和计算方法,从而在计算机图形学中应用上更加灵活和高效。 ### 回答3: 贝塞尔曲线是一类常用的二维或三维曲线,在计算机图形学中广泛应用。而Casteljau算法就是一种生成Bezier曲线的方法,其本质是一种递归算法,从而实现了对Bezier曲线的插值。 在这个算法中,我们可以将最终的Bezier曲线看作一个多项式函数。此时,我们将控制点看作是该多项式的系数。在开始Casteljau算法之前,我们需要先将控制点进行排序,从而保证从一个控制点产生一个Bezier曲线,这样我们就可以确定得到的多项式的系数。 接下来,我们从n个控制点开始,进行递归计算。具体地,我们将两个相邻的控制点进行连接,并计算连接线中点。这个中点就是新生成Bezier曲线中的控制点。然后,我们再将相邻的中点进行连接,并继续计算连接线中点,直到计算得到的中点只有一个。此时,这个中点就是Bezier曲线上的一个点,也就是我们要生成的点。 重复进行这个递归过程,直到所有要求的Bezier曲线上的点都生成完成为止。上述过程中,我们需要计算的连接线中点都是通过线性插值来得到的,这就是Casteljau算法的关键所在。 总之,通过Casteljau算法,我们可以根据给定的控制点,生成出千变万化的Bezier曲线,具有极高的灵活性和可塑性,因此被广泛地应用于计算机图形学、计算机辅助设计等领域。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值