C-3 样条曲线

C-3 样条曲线

插值三次样条函数

  • 曲线的曲率 k ( x ) = y ′ ′ ( 1 + y ′ 2 ) 3 2 k(x)=\frac{y''}{(1+y'^2)^{\frac{3}{2}}} k(x)=(1+y′2)23y′′
  • 三次样条函数的定义:设在区间[a,b]上给定的一个分割 Δ : a = x 0 < x 1 . . . < x n − 1 < x n = b \Delta:a=x_0<x_1...<x_{n-1}<x_n=b Δ:a=x0<x1...<xn1<xn=b,若满足以下条件,则称[a,b]上的一个函数 S ( x ) S(x) S(x)是三次样条函数 。
    • 在每个小区间 [ x i − 1 , x i ] ( i = 1 , 2 , 3 , . . . , n ) [x_{i-1},x_i](i=1,2,3,...,n) [xi1,xi](i=1,2,3,...,n) S ( x ) S(x) S(x)是三次多项式
    • 在整个区间 [ a , b ] [a,b] [a,b]上, S ( x ) S(x) S(x)为二阶连续可导的函数
    • S ( X i ) = y i , ( i = 1 , 2 , 3 , . . . , n ) S(X_i)=y_i,(i=1,2,3,...,n) S(Xi)=yi,(i=1,2,3,...,n)则称 S ( X ) S(X) S(X)为三次样条函数

关于样条函数可以这么想,想象一个木板上依次钉着一排钉子,其中钉子的纵坐标各不相同,横坐标依次排开(相当于木板是区间,钉子是分割)。然后将一个柔软的线依次按照钉子的奇偶上下绕过这些钉子。然后一用力拉紧这条线,这条线就会紧紧的绷在这些钉子之间,和每一个钉子也都有接触(相当于一个函数经过了这些点),那么这根线就是样条函数。很容易想象,只是用柔软的线的话,该样条函数是C0连续的,也就是说在连接处只是函数值相同,导数并不相同。三次样条函数就是穿过这些钉子,但是保持着C2连续条件的函数。

  • 样条函数的构造

    • 已经有了离散化的数据,如何去构造一个函数去连接它们,这一过程就是插值
    • 一般用插值的方式去构造样条函数
    • 常用的插值方法一般是线性插值方法,牛顿插值方法和Lagrange插值方法
  • 线性插值方法

    • 就是用直线连接各个点,可以发现,上面举的直线和钉子的例子就是线性插值方法。

      当然线性插值有很多改进的版本,比如计算一下两个点的中点然后进行插值,还有一些其他更复杂的方法,数值计算方法这门课里有相关内容。线性插值是一种非常重要的插值方法,图形学很多地方都用的是线性插值,比如计算三角形内的颜色,双线性插值,三线性插值等等。因为很多情况下并不需要很高的连续性,或者在点足够密集的情况下,单纯将它们用直线连接起来反而是一种高效可行的办法。

  • 牛顿插值方法

    • 其实这就是牛顿迭代法,其收敛速度是很快的,有一个刻画收敛速度的数学描述,我记得牛顿迭代法是 n 2 n^2 n2收敛,二阶收敛。
    • 计算差商: 首先,根据给定的数据点 ( x 0 , y 0 ) , ( x 1 , y 1 ) , . . . , ( x n , y n ) (x_0,y_0),(x_1,y_1),...,(x_n,y_n) (x0,y0),(x1,y1),...,(xn,yn),计算差商。差商是通过递归定义的,具体而言, f [ x i , x i + 1 , . . . , x i + k ] f[x_i,x_{i+1},...,x_{i+k}] f[xi,xi+1,...,xi+k]表示通过点 x i , x i + 1 , . . . , x i + k x_i,x_{i+1},...,x_{i+k} xi,xi+1,...,xi+k 的差商,其中 k k k 是差商的阶数。对于二阶差商,它的计算公式为: f [ x i , x i + 1 ] = f ( x i + 1 ) − f ( x i ) x i + 1 − x i f[x_i,x_{i+1}]=\frac{f(x_{i+1})-f(x_i)}{x_{i+1}-x_i} f[xi,xi+1]=xi+1xif(xi+1)f(xi),对于更高阶的差商,可以通过递归公式来计算。
    • 构建插值多项式: 一旦计算出所有的差商,可以使用这些差商构建插值多项式。牛顿插值多项式通常写成如下形式: P ( X ) = f ( x 0 ) + f [ x 0 , x 1 ] ( x − x 0 ) + f [ x 0 , x 1 , x 2 ] ( x − x 0 ) ( x − x 1 ) + . . . + P(X)=f(x_0)+f[x_0,x_1](x-x_0)+f[x_0,x_1,x_2](x-x_0)(x-x_1)+...+ P(X)=f(x0)+f[x0,x1](xx0)+f[x0,x1,x2](xx0)(xx1)+...+ f [ x 0 , x 1 , . . x n ] ( x − x 0 ) ( x − x 1 ) . . . ( x − x n − 1 ) f[x_0,x_1,..x_n](x-x_0)(x-x_1)...(x-x_{n-1}) f[x0,x1,..xn](xx0)(xx1)...(xxn1)其中, f [ x 0 , x 1 , . . x i ] f[x_0,x_1,..x_i] f[x0,x1,..xi] 是通过数据点 x 0 , x 1 , . . . , x i x_0,x_1,...,x_i x0,x1,...,xi的差商, f ( x 0 ) f(x_0) f(x0)是插值的起点。
  • Lagrange插值方法

    • 拉格朗日插值方法是Hermit插值方法的一种特例,直接采用拉格朗日基函数进行插值即可,给定的点还是 ( x 0 , y 0 ) , ( x 1 , y 1 ) , . . . , ( x n , y n ) (x_0,y_0),(x_1,y_1),...,(x_n,y_n) (x0,y0),(x1,y1),...,(xn,yn)

    • 基函数 L i ( x ) = ∏ j = 0 , j ≠ i n x − x j x i − x j L_i(x)=\prod^{n}_{j=0,j\ne i}\frac{x-x_j}{x_i-x_j} Li(x)=j=0,j=inxixjxxj

    • 插值函数 P ( x ) = ∑ i n y i ⋅ L i ( x ) P(x)=\sum^{n}_{i}y_i\cdot L_i(x) P(x)=inyiLi(x)

    • 举例

      x012
      y539
      L i ( x ) L_i(x) Li(x) ( x − 1 ) ( x − 2 ) ( 0 − 1 ) ( 0 − 2 ) = ( x − 1 ) ( x − 2 ) 2 \frac{(x-1)(x-2)}{(0-1)(0-2)}=\frac{(x-1)(x-2)}{2} (01)(02)(x1)(x2)=2(x1)(x2) ( x − 1 ) ( x − 2 ) ( 1 − 0 ) ( 1 − 2 ) = − ( x − 0 ) ( x − 2 ) 2 \frac{(x-1)(x-2)}{(1-0)(1-2)}=-\frac{(x-0)(x-2)}{2} (10)(12)(x1)(x2)=2(x0)(x2) ( x − 0 ) ( x − 1 ) ( 2 − 0 ) ( 2 − 1 ) = ( x − 0 ) ( x − 1 ) 2 \frac{(x-0)(x-1)}{(2-0)(2-1)}=\frac{(x-0)(x-1)}{2} (20)(21)(x0)(x1)=2(x0)(x1)

P ( X ) = 5 ( x − 1 ) ( x − 2 ) 2 − 3 ( x − 0 ) ( x − 2 ) 2 + 9 ( x − 0 ) ( x − 1 ) 2 = 4 x 2 − 6 x + 5 P(X)=5\frac{(x-1)(x-2)}{2}-3\frac{(x-0)(x-2)}{2}+9\frac{(x-0)(x-1)}{2}=4x^2-6x+5 P(X)=52(x1)(x2)32(x0)(x2)+92(x0)(x1)=4x26x+5

B样条函数

  • 以贝塞尔基函数为插值函数的样条,因此称为B样条曲线

代码

  • 用二次贝塞尔函数进行点的插值,这里没有做点移动后的C2条件的光滑处理
  • 环境: Imgui、Implot、GLFW 、glad
void Node::updataContoral() {

	if (x.size() == 2&& x.size() > controlx.size() + 1) {
		controlx.push_back((x[0] + x[1]) * 1 / 2);
		controly.push_back((y[0] + y[1]) * 3 / 5);
	}
	//插入控制点
	if (x.size() >= 3&& x.size()>controlx.size() + 1) {
		controlx.push_back(2 * x[x.size() - 2] - controlx[controlx.size() - 1]);
		controly.push_back(2 * y[x.size() - 2] - controly[controly.size() - 1]);	
	}
}

void Node::createBesselNode(){

	updataContoral();
	BesselNode_x.clear();
	BesselNode_y.clear();
	//计算每一段的贝塞尔函数
	for (int i = 0; i < x.size() -1; i++) {
		Bessel(x[i], y[i], controlx[i], controly[i], x[i + 1], y[i + 1]);
	}
}

在这里插入图片描述

参考资料

  • 某一本不知出处的《计算几何》 链接:https://pan.baidu.com/s/1eOHipTyQ2A_KmCkOa7687g 提取码:6666

  • Games102的几何建模课程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值