JS 绘制 Cardinal 样条曲线

Cardinal 曲线

可以参考一下我另一篇文章 Cardinal 与三阶 Bezier 相互转换

根据定义,给定点集 { P k − 1 , P k , P k + 1 , P k + 2 } \{ \mathbf {P}_{k-1}, \mathbf {P}_k, \mathbf {P}_{k+1}, \mathbf {P}_{k+2} \} {Pk1,Pk,Pk+1,Pk+2} , 则 P k \mathbf {P}_k Pk P k + 1 \mathbf {P}_{k+1} Pk+1 之间的曲线可以由如下方程生成:
P ( u ) = a u 3 + b u 2 + c u + d d = P k c = s ( P k + 1 − P k − 1 ) b = 3 ( P k + 1 − P k ) − s ( P k + 2 − P k ) − 2 c a = ( P k + 1 − P k ) − c − b \begin {aligned} \mathbf{P}(u) & = \mathbf{a}u^3 + \mathbf{b}u^2 + \mathbf{c}u + \mathbf{d}\\ \mathbf{d} & =\mathbf {P}_k\\ \mathbf{c} & =s(\mathbf {P}_{k+1}-\mathbf {P}_{k-1})\\ \mathbf{b}& =3(\mathbf {P}_{k+1}-\mathbf {P}_k)-s(\mathbf {P}_{k+2}-\mathbf {P}_k)-2\mathbf{c}\\ \mathbf{a}& =(\mathbf {P}_{k+1}-\mathbf {P}_k) - \mathbf{c} - \mathbf{b} \end {aligned} P(u)dcba=au3+bu2+cu+d=Pk=s(Pk+1Pk1)=3(Pk+1Pk)s(Pk+2Pk)2c=(Pk+1Pk)cb
其中, s s s 用于控制曲线的松紧,取值范围为 [0, 1]。 0 表示最紧绷 (无平滑转角);1 表示最松弛。

绘制思路

既然方程已经给出了,则只需将 u u u 从 0 到 1 采样并依据公式计算坐标即可。不过,由于不好把握 u u u 的采样间隔,这里并不打算采用这种方案。

我的思路是:

  1. 先计算两点之间 x, y 方向的差值
  2. 取差值绝对值较大的那个值,按预设精度进行细分,计算出 u u u 的步长
  3. 根据 u u u 的步长,计算方程的增量 Δ P \Delta \mathbf{P} ΔP
  4. 采用前向差分法,依次计算中间点坐标
    a. 如果中间点到上一线段的距离小于预设精度,抛弃该点

前向差分

设有一个三次样条曲线,其表达式如下:
P ( u ) = a u 3 + b u 2 + c u + d \mathbf {P}(u) = \mathbf{a}u^3 + \mathbf{b}u^2 + \mathbf{c}u + \mathbf {d} P(u)=au3+bu2+cu+d
如果将 u u u 的取值范围细分为具有固定大小 h h h 的子区间,则相邻两点 x x x 坐标为:
x k = x ( u k ) x k + 1 = x ( u k + h ) \begin{aligned} x_k &= x(u_k) \\ x_{k+1} &= x(u_k + h) \end{aligned} xkxk+1=x(uk)=x(uk+h)
可计算出前向差分为:
Δ 1 x k = x k + 1 − x k = 3 a h u k 2 + ( 3 a h 2 + 2 b h ) u k + ( a h 3 + b h 2 + c h ) \begin{aligned} \Delta_1 x_k &= x_{k+1} - x_k \\ & = 3\mathbf {a} hu^2_k + (3\mathbf{a}h^2 + 2\mathbf{b}h)u_k + (\mathbf{a}h^3 + \mathbf{b}h^2 + \mathbf{c}h) \end{aligned} Δ1xk=xk+1xk=3ahuk2+(3ah2+2bh)uk+(ah3+bh2+ch)

由于 Δ 1 x k \Delta_1 x_k Δ1xk 是关于 u u u 的多项式,计算复杂,所以也可以计算出 Δ 1 x k \Delta_1 x_k Δ1xk 的增量 Δ 2 x k \Delta_2 x_k Δ2xk,这样即可用加法计算出 Δ 1 x k \Delta_1 x_k Δ1xk 的值。不断重复计算增量的过程,直到增量为常数。最终可以得到:
Δ 1 x k = 3 a h u k 2 + ( 3 a h 2 + 2 b h ) u k + ( a h 3 + b h 2 + c h ) Δ 2 x k = 6 a h 2 u k + 6 a h 3 + 2 b h 2 Δ 3 x k = 6 a h 3 \begin{aligned} \Delta_1 x_k &= 3\mathbf {a} hu^2_k + (3\mathbf{a}h^2 + 2\mathbf{b}h)u_k + (\mathbf{a}h^3 + \mathbf{b}h^2 + \mathbf{c}h) \\ \Delta_2 x_k &= 6\mathbf {a} h^2u_k +6\mathbf{a}h^3 + 2\mathbf{b}h^2\\ \Delta_3 x_k &= 6\mathbf{a}h^3 \end{aligned} Δ1xkΔ2xkΔ3xk=3ahuk2+(3ah2+2bh)uk+(ah3+bh2+ch)=6ah2uk+6ah3+2bh2=6ah3

根据以上公式,从 u 0 = 0 u_0=0 u0=0 开始,步长为 h h h x x x 坐标的迭代过程为:

k k k Δ 3 x k \Delta_3 x_k Δ3xk Δ 2 x k \Delta_2 x_k Δ2xk Δ 1 x k \Delta_1 x_k Δ1xk x k x_k xk
0 6 a h 3 6\mathbf{a}h^3 6ah3 6 a h 3 + 2 b h 2 6\mathbf{a}h^3 + 2\mathbf{b}h^2 6ah3+2bh2 a h 3 + b h 2 + c h \mathbf {a}h^3 + \mathbf {b}h^2 + \mathbf {c}h ah3+bh2+ch d \mathbf {d} d
1 6 a h 3 6\mathbf{a}h^3 6ah3 Δ 2 x 0 + Δ 3 x 0 \Delta_2 x_0 + \Delta_3 x_0 Δ2x0+Δ3x0 Δ 1 x 0 + Δ 2 x 0 \Delta_1 x_0 + \Delta_2 x_0 Δ1x0+Δ2x0 x 0 + Δ 1 x 0 x_0+\Delta_1 x_0 x0+Δ1x0
⋮ \vdots
k k k 6 a h 3 6\mathbf{a}h^3 6ah3 Δ 2 x k − 1 + Δ 3 x k − 1 \Delta_2 x_{k-1} + \Delta_3 x_{k-1} Δ2xk1+Δ3xk1 Δ 1 x k − 1 + Δ 2 x k − 1 \Delta_1 x_{k-1} + \Delta_2 x_{k-1} Δ1xk1+Δ2xk1 x k − 1 + Δ x k − 1 x_{k-1} + \Delta x_{k-1} xk1+Δxk1

可以看到,每次迭代只需计算 3 次加法。

成果

最终效果如下图,也可以点击此处查看演示效果及源代码。
Cardinal 样条曲线
看起来效果还是不错的。不过,根据算法描述可以发现,如果两个点相距很近,就会因为中间点不够多而走样。

其它生成方法

当然还其它方法也可以绘制。比如,二分迭代法。每次计算出曲线的中点,将曲线按中点分为两部分,然后迭代这个过程。还可以先把曲线转换为 Bezier 曲线,然后进行绘制。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值