Cardinal 曲线

根据定义,给定点集 \(\{ \mathbf {P}_{k-1}, \mathbf {P}_k, \mathbf {P}_{k+1}, \mathbf {P}_{k+2} \}\) , 则 \(\mathbf {P}_k\) 到 \(\mathbf {P}_{k+1}\) 之间的 Cardinal 曲线可以由如下方程生成:

\[\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}
\]

其中,\(s\) 用于控制曲线的松紧,取值范围为 [0, 1]。 0 表示最紧绷 (无平滑转角);1 表示最松弛。

绘制思路

根据公式,则只需将 \(u\) 从 0 到 1 采样并依据公式计算坐标即可。不过,由于不好把握 \(u\) 的采样间隔,这里并不打算采用这种方案。

我的思路是:

  1. 先计算两点之间 x, y 方向的差值
  2. 取差值绝对值较大的那个值,按预设精度进行细分,计算出 \(u\) 的步长
  3. 根据 \(u\) 的步长,计算方程的增量 \(\Delta \mathbf{P}\)
  4. 采用前向差分法,依次计算中间点坐标

    a. 如果中间点到上一线段的距离小于预设精度,抛弃该点

前向差分

设有一个三次样条曲线,其表达式如下:

\[\mathbf {P}(u) = \mathbf{a}u^3 + \mathbf{b}u^2 + \mathbf{c}u + \mathbf {d}
\]

如果将 \(u\) 的取值范围细分为具有固定大小 \(h\) 的子区间,则相邻两点 \(x\) 坐标为:

\[\begin{aligned}
x_k &= x(u_k) \\
x_{k+1} &= x(u_k + h)
\end{aligned}
\]

可计算出前向差分为:

\[\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}
\]

由于 \(\Delta_1 x_k\) 是关于 \(u\) 的多项式,计算复杂,所以也可以计算出 \(\Delta_1 x_k\) 的增量 \(\Delta_2 x_k\),这样即可用加法计算出 \(\Delta_1 x_k\) 的值。不断重复计算增量的过程,直到增量为常数。最终可以得到:

\[\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}
\]

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

\(k\) \(\Delta_3 x_k\) \(\Delta_2 x_k\) \(\Delta_1 x_k\) \(x_k\)
0 \(6\mathbf{a}h^3\) \(6\mathbf{a}h^3 + 2\mathbf{b}h^2\) \(\mathbf {a}h^3 + \mathbf {b}h^2 + \mathbf {c}h\) \(\mathbf {d}\)
1 \(6\mathbf{a}h^3\) \(\Delta_2 x_0 + \Delta_3 x_0\) \(\Delta_1 x_0 + \Delta_2 x_0\) \(x_0+\Delta_1 x_0\)
\(\vdots\)
\(k\) \(6\mathbf{a}h^3\) \(\Delta_2 x_{k-1} + \Delta_3 x_{k-1}\) \(\Delta_1 x_{k-1} + \Delta_2 x_{k-1}\) \(x_{k-1} + \Delta x_{k-1}\)
可以看到,每次迭代只需计算 3 次加法。

成果

最终效果如下图,也可以点击此处查看演示效果及源代码。

看起来效果还是不错的。不过,根据算法描述可以发现,如果两个点相距很近,就会因为中间点不够多而走样。

其它生成方法

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

JS 绘制 Cardinal 样条曲线的更多相关文章

  1. Cardinal样条曲线的Javascript实现(理论篇)

    首先,要对样条曲线进行插值的原因是:希望通过给定的关键帧点生成一条希望的直线或者曲线. 1.直线插值 生成一条直线,给定直线首尾的关键点P0,P1,就能确定这条直线的特性,比如y=kx+b中的斜率k和 ...

  2. 利用d3.js绘制雷达图

    利用d3,js将数据可视化,能够做到数据与代码的分离.方便以后改动数据. 这次利用d3.js绘制了一个五维的雷达图.即将多个对象的五种属性在一张图上对照. 数据写入data.csv.数据类型写入typ ...

  3. 应用wavesurfer.js绘制音频波形图小白极速上手总结

    一.简介 1.1  引   人生中第一份工作公司有语音识别业务,需要做一个web网页来整合语音引擎的标注结果和错误率等参数,并提供人工比对的语音标注功能(功能类似于TranscriberAG等),(博 ...

  4. js 绘制数学函数

    <!-- <!doctype html> --> <html lang="en"> <head> <meta charset= ...

  5. JS 绘制心形线

    JS 绘制心形线 <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> < ...

  6. QT绘制B样条曲线

    ²  贝塞尔曲线 贝塞尔曲线是通过一组多边折线的各顶点来定义.在各顶点中,曲线经过第一点和最后一点,其余各点则定义曲线的导数.阶次和形状.第一条和最后一条则表示曲线起点和终点的切线方向. ²  B样条 ...

  7. Cardinal样条曲线的Javascript实现(代码篇)

    由上一篇文章得到了Cardinal曲线的矩阵表达式,下面就这个矩阵表达式就可以来对曲线进行插值了. 这里选用了JS来实现,完全是因为之前交作业的时候还不知道怎么在Xcode里建完整的C++OpenGL ...

  8. ichart.js绘制虚线 ,平均分虚线

    var Data=new Array(); Data[0] = { labels : ["第一单元","第二单元","第三单元"," ...

  9. Javascript实战开发:教你使用raphael.js绘制中国地图

    最近的数据统计项目中要用到中国地图,也就是在地图上动态的显示某个时间段某个省份地区的统计数据,我们不需要flash,仅仅依靠raphael.js以及SVG图像就可以完成地图的交互操作.在本文中,我给大 ...

  10. d3.js 绘制极坐标图(polar plot)

    0.引言 在极坐标系中,任意位置可由一个夹角和一段相对原点(极点)的距离表示.也就是说,我们可以用 (angle,r) 来表示极坐标系中的点. 1.数据 假设我们有如下数据集[ [10, 0.2], ...

随机推荐

  1. 学习使用docker-compose搭建Redis哨兵集群

    搭建的记录 Redis的容器部署后,redis-server的默认安装目录为 /usr/local/bin. docker volumes 映射的文件与本地的文件共享,修改本地文件后,需要重启dock ...

  2. Mongodb使用save更新

    mongodb 的 insert().save()  ,区别主要是:若存在主键,insert()  不做操作,而save() 则更改原来的内容为新内容. 在使用save时,由于mongodb默认是_i ...

  3. 2. 从0开始学ARM-CPU原理,基于ARM的SOC讲解

    关于ARM的一些基本概念,大家可以参考我之前的文章: <到底什么是Cortex.ARMv8.arm架构.ARM指令集.soc?一文帮你梳理基础概念[科普]> 关于ARM指令用到的IDE开发 ...

  4. CF1234D

    CF1234D 链接: https://codeforces.com/problemset/problem/1234/D 题目大意: 给你一个字符串s,你需要完成如下q次询问 把 s 的第 p 位改为 ...

  5. Win32 创建窗口翻车记录

    今天创建窗口的时候,在捕获消息WM_CREATE的时候翻车了,找了好久都没找到,我换另一台电脑上又没出问题, 现分享下:LRESULT QWnd::WindowProc(HWND hWnd, UINT ...

  6. PPT或Visio比较舒适的RGB配色参数

    1.187 204 235 2.222 156 83 3.117 156 83 4.64 116 52 5.117 121 74 6.69 137 148 7.182 194 154 8.207 19 ...

  7. “vscode #include 错误”的问题解决办法

    确定gcc没问题的情况下 打开这个文件 把linux下的文件直接复制过来 然后加上路径就OK了

  8. 解决gedit报错无法打开的问题

    彻底解决关于gedit的Unable to init server: 无法连接: 拒绝连接_BD_Marathon的博客-CSDN博客_unable to init server: 无法连接: 拒绝连 ...

  9. 修改kubeadm证书过期时间及更新k8s集群证书

    一.为什么要修改 kubeadm 证书时间 Kubernetes 官方提供了 kubeadm 工具安装 kubernetes 集群,使用这个工具安装集群非常便捷,使部署和升级 Kubernetes 变 ...

  10. 偶发的系统卡顿内存飙升导致OOM

    线上有个小程序,客户反馈的现象是偶发性的卡主没响应,前端失去连接,点其他菜单都没响应.通过查看配置的dump目录有很多的GC日志,以及生成的一个堆内存快照. JVM的配置参数大概为: -Xms512M ...