![401e6abb696aa96dafa34850ad24fb5c.png](https://i-blog.csdnimg.cn/blog_migrate/61ec46de17ba11416019fc55ec21f926.png)
先抛个问题,
如果在工作时间摸鱼(咳咳)
为啥画个曲线还有公式,还要写代码?
在各位使用各种编辑软件的时候,其实到处都有贝塞尔曲线的影子。PhotoShop、AE、Flash,除了钢笔工具,还有调色曲线、运动曲线等等,有些是画在画布上的曲线,有些则影响了曝光度、亮度、运动速度等等,这类曲线最大的特点是可以通过拖动几个控制点去调整整个曲线的轨迹
贝塞尔曲线的应用 |
![cb4a830bda628549d8c68a41ba40ece0.png](https://i-blog.csdnimg.cn/blog_migrate/347432261581b112104f314a4b97e74a.png)
| ![596bbd5dcf6223694a9dcf2d58fd2601.png](https://i-blog.csdnimg.cn/blog_migrate/26b8c047f2f4aba849e62839ca1ed1fb.png)
| ![d1ab45963f410fd4f324a71813ebf951.png](https://i-blog.csdnimg.cn/blog_migrate/7216ad1962a361b6e475f319aee69d07.png)
| ![6c86994063618a2a2b8b7e49f574b6d9.png](https://i-blog.csdnimg.cn/blog_migrate/eab28a933c21c86477cd404fd2b5f69c.png)
|
PS的钢笔工具 | PS的调整颜色曲线 | An的动画曲线 | AE的运动曲线 |
可以说贝塞尔曲线无处不在,但又不是随便画画就能出来的,它是由一个通用公式加上一堆参数组成,直接看理论知识的话门槛又偏高,不利于下手写代码,于是就有了这篇文章
科普版
查过百度百科的可以直接往下看自制版
啥是贝塞尔曲线,它不是你随手画的曲线,也不是圆规描出来的,它是通过套用一个固定的公式经过差值计算画出来的,这个公式大致长这样
![65c33d71fd90c46650885d37c743cee7.png](https://i-blog.csdnimg.cn/blog_migrate/3478ab9a61f378eb8035ed0822915abc.png)
等号左边是通用公式,右边是公式的展开
公式中的Pn便是贝塞尔曲线中的n个控制点,除了第一个和最后一个,其他控制点都不会直接与曲线相交
是不是有点意思?但上手好像又一脸懵逼?那就继续往下看吧
自制版
先看效果
贝塞尔曲线其实是由差值算出的点的集合,差值的原理和过程上面都说了,下面说下更通俗的理解
要实现贝塞尔曲线,就要知道如何差值,从公式去理解显然还是有一定gap的,哪怕有图解,有时候也会有种好像明白但不知道如何入手的感觉
在查阅不少资料和自己动手实践后,大致总结出一个通用步骤(其实也就是求解Pn的过程)
- 设定好起点、终点和控制点集,按顺序排列,以起点为第一个点,终点始终在最后 [start, p1, p2, ..., end]
-
- 这里的点不限于二维,一维、三维均可
- 一维贝塞尔通常用于描述运动的快慢过程,输入的是正常的时间序列,输出的则是扭曲后的时间序列
- 二维、三维贝塞尔通常用于图形、轨迹
- 设定曲线精度(曲线由多少个点组成,精度不够的话会有明显的折线)
-
- 贝塞尔曲线由于是差值算出来的,计算出来的点并不是一个长度均匀的点集,其主要受控制点的分布影响
- 进行差值
实现方法
举个栗子:
这里先列一下假设条件,
曲线有2个控制点(p1、p2),算上起点(start)和终点(end)一共4个点,我们需要得到100个点集来绘制这个曲线
计算的分解动作:
- 输入控制点集[start, p1, p2, end],需要得到的点的个数100
- 根据点的个数计算出每次差值的步长,step=1/99(为啥是99,因为第一次是0,而差值迭代是100次,所以最后一次step是99/99)
- 进行一次差值,假设已经进行到了第n次
- 第n次差值后的步长是step=n/99
- 根据公式,此次差值后的点应该是Pn = start x step³+ 3 x p1 x step ² x (1-step) + 3 x p2 x step x (1-step) ² + end x (1-step)³
- 将Pn加入曲线点集
- 返回所有差值后的点集
这里可以看到,Pn的公式还是有规律可循的,大致规律是
- 多项式中单项的顺序就是控制点集的顺序(废话)
- 多项式中第一个和最后一个的系数是1(废话+1)
- 多项式中,step相关部分的规律(这里先将控制点长度标记为length)
- step「n」次方 x (1-step)「length-1-n」次方,这里length为4,n为点的顺序,从0开始。start的时候(1-step)是0次方故不用计算,end同理,step也是0次方
- 所有单项中的系数遵循杨辉三角(别问为什么,问就看科普公式)
- 杨辉三角的算法由3个阶乘得到
- (length-1)!作为分子
- (length-1-n)! x n!作为分母
- 相除得到杨辉三角系数
因此,2个控制点的贝塞尔曲线的系数固定是1、3、3、1,单个控制点固定是1、2、1,多个控制点则根据数量算一遍杨辉三角就行
至此,贝塞尔曲线再也没啥秘密了,整个差值计算也很单纯,最后贴一个自己写的渣代码,祝大家中秋国庆节日快乐~
/*贝塞尔曲线算法输入:controls:控制点count:需要生成的点的数量输出:生成的点集 */function curve(controls, count) {var c_count = controls.length; var res = []; var n = 1; var yang = c_count-1; for(var i = 1; i <= yang; i++){
n *= i; }for(i = 0; i < count + 1; i++){var p = {x:0, y:0}var t = i / count; var s = 1 - t; for(var j = 0; j < c_count; j++){var m = 1; for(var k = 1; k <= j; k++){
m *= k; }var mn = 1; for(k = 1; k <= yang - j; k++){
mn *= k; }var p1 = Math.pow(s, c_count-j-1); var p2 = Math.pow(t, j); p.x += n / (m * mn) * controls[j].x * p1 * p2; p.y += n / (m * mn) * controls[j].y * p1 * p2; }
res.push(p); }return res;}