Smooth Paths Using Catmull-Rom Splines

https://qroph.github.io/2018/07/30/smooth-paths-using-catmull-rom-splines.html

Smooth Paths Using Catmull-Rom Splines

July 30, 2018 • Mika Rantanen

Let’s assume that we have a set of points and we want to draw a path that goes through them. The simplest and easiest way to do this is to connect the points with straight lines as shown in Figure 1.

A path using straight lines.

However, straight lines would cause sharp corners to the path and in many cases a smoother path would be preferred. Figure 2 shows a smoother path that goes through the same points that are in Figure 1.

A smooth path.

In this post I’m going to focus on Catmull-Rom splines which are commonly used in computer graphics to create smooth curves. For example, the path in Figure 2 uses them.

Catmull-Rom splines

Catmull-Rom splines are piecewise-defined polynomial functions. A single spline segment is defined by four control points p0,…,p3

but the actual curve is drawn only between points p1 and p2

as is illustrated in Figure 3. However, it is easy to chain these segments together.

One segment of Catmull-Rom spline.

If we want to draw a curve that goes through k

points, we need k+2 control points because the curve is not drawn through the first and the last ones. These two additional points can be selected arbitrarily, but they affect the shape of the curve. Now a segment between points pn and pn+1 is calculated using points pn−1, pn, pn+1, and pn+2, where 1≤n≤k−1. When these segments are combined together, they form a continuous curve, which passes through all points between p1 and pk

. Figure 2 shows an example of this kind of curve.

There are some parameters that can be used to control the shape of the spline. Catmull-Rom splines have three common variants: uniform, centripetal, and chordal. Differences have been studied for example here. Additionally, it is possible to use a tension parameter that defines how “tight” the spline is. When tension is 0, the result looks like the curve in Figure 2, and when tension is 1, the result is straight lines as in Figure 1. The following interactive example demonstrates Catmull-Rom splines and these parameters.

You can add points by clicking the canvas and move existing points by dragging them. The control panel allows you to change the tension of the curve and select the spline variant that is drawn.

Calculating spline segments

Let’s start with following equations that are described in a Wikipedia article about centripetal Catmull-Rom splines:

q(t)=t2−tt2−t1B1+t−t1t2−t1B2,

where

B1=t2−tt2−t0A1+t−t0t2−t0A2,B2=t3−tt3−t1A2+t−t1t3−t1A3,A1=t1−tt1−t0p0+t−t0t1−t0p1,A2=t2−tt2−t1p1+t−t1t2−t1p2,A3=t3−tt3−t2p2+t−t2t3−t2p3,

and where

ti=ti−1+∥pi−pi−1∥α,

and t0=0

, i=1,2,3, and α∈[0,1]

.

The actual segment we are interested in is between t1

and t2 i.e. q(t1)=p1 and q(t2)=p2. If α=0, the resulting curve q is a uniform Catmull-Rom spline. When α=0.5, the curve is a centripetal variant and when α=1

, the result is a chordal variant.

Calculating the curve with previous equation can be quite inconvenient. Often it would be better to use precalculated constants a

, b, c, and d, and represent the curve segment between p1 and p2

as

p(t)=at3+bt2+ct+d,

where t∈[0,1]

, p(0)=p1, and p(1)=p2

. Other way to define this is

p(t)=q(t1+t(t2−t1)).

We can get a relationship between the tangents of p

and q by differentiating these with respect to t

as follows:

p′(t)=3at2+2bt+c=(t2−t1)q′(t1+t(t2−t1)).

Now we have

p(0)=q(t1)=p1=d,p(1)=q(t2)=p2=a+b+c+d,p′(0)=(t2−t1)q′(t1)=m1=c,p′(1)=(t2−t1)q′(t2)=m2=3a+2b+c,

where symbols m1

, and m2 are used to represent tangents at the starting point p1 and at the ending point p2 respectively. By solving a, b, c, and d

from these four equations we get

a=2p1−2p2+m1+m2,b=−3p1+3p2−2m1−m2,c=m1,d=p1.

Now we have to determine m1

and m2 and for that we need tangents q′(t1) and q′(t2). Calculating the derivative of q

is quite cumbersome, but luckily we can use Mathematica to do the work for us. We get

q′(t1)=p1−p0t1−t0−p2−p0t2−t0+p2−p1t2−t1,q′(t2)=p2−p1t2−t1−p3−p1t3−t1+p3−p2t3−t2,

and thus

m1=(t2−t1)(p1−p0t1−t0−p2−p0t2−t0+p2−p1t2−t1),m2=(t2−t1)(p2−p1t2−t1−p3−p1t3−t1+p3−p2t3−t2).

If we want to take tension τ

into account, we must also multiply both m1 and m2 with 1−τ

.

It is now easy to precalculate coefficients a

, b, c, and d for each segment and use equation p(t)=at3+bt2+ct+d

to quickly interpolate that segment.

C++ implementation

The code is written in C++. It uses GLM library that provides the basic linear algebra functionality. Overall, I’ll try to keep the code syntax in this blog quite simple so that it is easy to understand and to convert to other languages.

In its simplest form, we can define a struct of the spline segment as follows:

struct Segment
{vec2 a;vec2 b;vec2 c;vec2 d;
};

Note that we are using two-dimensional splines here. If you want three-dimensional splines, just replace all occurances of vec2 with vec3.

As said in the previous chapter, we need parameters α

and τ to calculate the spline. In following code we use variable alpha for α and tension for τ

. A good value for alpha is 0.5 which gives us a centripetal Catmull-Rom spline, and for tension a value 0 is a good choice. These values can range from 0 to 1.

If we now have points p0, p1, p2, and p3 for each segment, we can calculate coefficients for segments with equations from previous chapter:

float t0 = 0.0f;
float t1 = t0 + pow(distance(p0, p1), alpha);
float t2 = t1 + pow(distance(p1, p2), alpha);
float t3 = t2 + pow(distance(p2, p3), alpha);vec2 m1 = (1.0f - tension) * (t2 - t1) *((p1 - p0) / (t1 - t0) - (p2 - p0) / (t2 - t0) + (p2 - p1) / (t2 - t1));
vec2 m2 = (1.0f - tension) * (t2 - t1) *((p2 - p1) / (t2 - t1) - (p3 - p1) / (t3 - t1) + (p3 - p2) / (t3 - t2));Segment segment;
segment.a = 2.0f * (p1 - p2) + m1 + m2;
segment.b = -3.0f * (p1 - p2) - m1 - m1 - m2;
segment.c = m1;
segment.d = p1;

We can get the same result slightly more efficiently by simplifying the equations and using the following code to calculate m1 and m2:

float t01 = pow(distance(p0, p1), alpha);
float t12 = pow(distance(p1, p2), alpha);
float t23 = pow(distance(p2, p3), alpha);vec2 m1 = (1.0f - tension) *(p2 - p1 + t12 * ((p1 - p0) / t01 - (p2 - p0) / (t01 + t12)));
vec2 m2 = (1.0f - tension) *(p2 - p1 + t12 * ((p3 - p2) / t23 - (p3 - p1) / (t12 + t23)));

These coefficients can be precaluclated for all spline segments assuming that the parameters do not change afterwards. It is now simple to retrieve a point in a segment segment by using the precalculated values:

vec2 point = segment.a * t * t * t +segment.b * t * t +segment.c * t +segment.d;

And t is of course a value between 0 and 1. With a value 0, the result is the starting point of the spline segment, and with a value 1, the result is the ending point.

Smooth Paths Using Catmull-Rom Splines相关推荐

  1. matlab 里catmull rom,Unity中的曲线插值CatmullRom

    ps:博客从typecho换成jekyll后,文章复制到简书来因为图片链接原因变得麻烦了.- -! 正文 之前写了个插件,有个需要曲线插值的功能.给定一些点的位置,物体成一条平滑曲线依次通过这些点. ...

  2. Cocos2d-x 2.0 之 Actions “三板斧” 之三

    [Cocos2d-x 相关教程来源于红孩儿的游戏编程之路 CSDN 博客地址: http://blog.csdn.net/honghaier ] 红孩儿Cocos2d-X学习园地QQ2群:442084 ...

  3. Unity中利用LineRenderer绘制寻路路径

    基于A*的寻路路径点生成,这里只做之后的显示以及曲线平滑 这里主要是Catmull-Rom的应用,最终选用的是Centripetal Catmull–Rom spline 起初想要通过贝塞尔曲线去平滑 ...

  4. 双圆弧插值算法(一)

    双圆弧插值算法(一) Biarc Interpolation 在游戏开发中经常出现两点间的插值问题.大多数情况下,只需要一个简单的线性插值.线性插值很好,因为不会真的弄错.只有一条可能的线连接这些点. ...

  5. dotween曲线运动 unity_Unity中DOTween插件的DOTweenPath轨迹移动

    先来看一下DOTweenPath组件的截图 1.Scene View Commands (1)SHIFT+CTRL:add a waypoint        加一个轨迹点 (2)SHIFT+ALT: ...

  6. unity3D---插件obi Rope---实现绳子效果

    插件下载地址:链接:https://pan.baidu.com/s/1tHRqatJN8LNk4a3SK_Vrsg                提取码:2e9h 导入我们的工程,就可以直接开始使用啦 ...

  7. 连续环境下基于enhanced GA算法的多目标多机器人路径算法

    10.1016/j.eswa.2018.08.008 https://doi.org/10.1016/j.eswa.2018.08.008 Abstract 连续环境多机器人路径规划,基于多种方法结合 ...

  8. Temporal Anti-Aliasing(时域抗锯齿TAA)

    首先说一下走样:一般分为时域走样(如旋转车轮)和空域走样(锯齿),但在 TAA 技术是采用时域相关叠加混合技术来解决空域走样的问题. 简单看一下空域抗锯齿 (Spatial Anti-Aliasing ...

  9. Social LSTM: Human Trajectory Prediction in Crowded Spaces 论文翻译

    摘要 行人可沿不同的轨道行走,以避开障碍物及方便其他行人.在这样的场景中行驶的任何自动驾驶车辆都应该能够预见行人未来的位置,并相应地调整其路径以避免碰撞.轨迹预测问题可以看作是一个序列生成任务,我们感 ...

最新文章

  1. redis api-String
  2. HarmonyOS之常用组件WebView的使用
  3. 回调 that.setdata 数据不更新_重大利空落地,或损上亿利润,乐普医疗回调近四成...
  4. 漫画算法:判断2的乘方
  5. gradle的二进制版本_Gradle入门:创建二进制分发
  6. 将url参数字符串转成数组
  7. stm32基本定时器
  8. C语言课后习题(54)
  9. python课设答辩ppt_如何制作优秀的毕业论文答辩PPT
  10. Git——创建版本库【git init】
  11. 计算机科学与技术专业导论mooc答案,中国大学MOOC人工智能导论(2017级)网课答案...
  12. Ubuntu 使用firefox插件下载百度云文件
  13. Python之os库
  14. 如何解决系统更新后Safari Mac浏览器崩溃等的问题!
  15. SPA项目开发(首页导航左侧菜单)
  16. 帮助新员工快速形成战斗力,Filez在入职与培训场景应用
  17. Linux Chromium源码编译
  18. stm32常用数据类型 U8、U16、U32到底代表什么?
  19. seo模拟快排浏览器指纹进行识别过滤
  20. 计算机开始菜单打不开是什么原因,win10开始菜单打不开怎么办啊

热门文章

  1. R语言分位数回归Quantile Regression分析租房价格
  2. 一套免费开源Java充电桩平台
  3. CodeForces - 437A . The Child and Homework 题解
  4. 简易付安装后无法使用
  5. 证明版~(半)正定\负定的充要条件(用各种矩阵表示)
  6. 医学统计学题库带答案
  7. 对数与指数操作:pow,log,sqrt,exp
  8. 智慧路灯杆在智慧科技园的应用方案
  9. ASP.NET Core 3.1系列(15)——EFCore之DB First
  10. 基于web的家电维修系统/家电维修管理系统