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. 一款.NET开发的AI无损放大工具

    前言 今天大姚给大家分享一款由.NET开源(GPL-3.0 license).基于腾讯ARC Lab提供的Real-ESRGAN模型开发的AI无损放大工具:AI-Lossless-Zoomer. Re ...

  2. 【YashanDB知识库】自动选举配置错误引发的一系列问题

    问题现象 问题出现的步骤/操作: 配置自动选举,数据库备库手动发起switch over,命令会报错 主.备库变为只读状态,数据库无法进行读写操作 shutdown immediate 停止数据库,此 ...

  3. 如何保证 Redis 的高并发和高可用?讨论redis的单点,高可用,集群

    如何保证 Redis 的高并发和高可用?讨论redis的单点,高可用,集群. 打开GitHub搜索redis,边可以看到,该项目的介绍是这样的: Redis is an in-memory datab ...

  4. C++:使自定义类支持迭代器

    概述 在 C++ 中,链表迭代器是一种用来遍历链表(如 std::list)元素的工具.链表是一种数据结构,其中每个元素(节点)包含一个数据值和一个指向下一个节点的指针.链表迭代器允许以类似于数组的方 ...

  5. 【QT界面美化】QT界面美化效果截图QSS+QML

    贴几个QT做的界面美化效果截图. 先来一张动图,有一些画面是QT Widgets + QSS实现的:另外一些画面是QT QML实现的. QT界面美化效果图QT QSS QML 补天云QT技术培训专家 ...

  6. vscode单行注释失效解决办法

    第一种情况: bug:在使用快捷键注释的时候,只有style和script标签里的内容注释有问题,无法使用快捷键注释. 解决方法:禁用vscode的插件Jinja. 第二种情况 方法一 1.Ctrl+ ...

  7. 30. 串联所有单词的子串 Golang实现

    题目描述: 给定一个字符串 s 和一个字符串数组 words. words 中所有字符串 长度相同 . s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串. 例如, ...

  8. LNMP 和 LAMP 对比 (仅供参考)

    Nginx 性能稳定.功能丰富.运维简单.处理静态文件速度快且消耗系统资源极少. Apache 是 LAMP 架构最核心的 Web Server,开源.稳定.模块丰富是 Apache 的优势.但 Ap ...

  9. 手搓大模型Task03:手搓一个最小的 Agent 系统

    前言   训练一个大模型是一件高投入低回报的事情,况且训练的事情是由大的巨头公司来做的事情:通常我们是在已有的大模型基础之上做微调或Agent等:大模型的能力是毋庸置疑的,但大模型在一些实时的问题上, ...

  10. 为什么用 AWS CLI?因为我懒得点鼠标!

    在这篇博客中,我们一起深入探索 AWS CLI 的世界,从零开始,逐步构建在云端的家园.将介绍 AWS CLI 的基本功能和使用场景,如何创建 IAM 用户.VPC.子网.安全组.EC2 实例等,甚至 ...