版权声明:本文为博主原创文章,转载请附上博文链接!
需要拷贝代码请直接使用本文最后的例程,文章前面的大部分代码都不是最佳实践,是在编程过程中的摸索(走过的弯路),不过这些示范对笔者今后写算法启发很大。
要完成的功能是根据起点,终点和控制点,绘制n阶贝塞尔曲线
首先看n阶贝塞尔曲线的公式
公式中用了组合数,大数组合数计算也有算法:
下面来说明一下这个组合数计算的优化过程:
500 + 500 = 1000
上面两个式子计算结果是相等的,但是如果编程实现,
第一个式子就必须使用至少一个Uint32 来存放100000;
但是第二个式子只需要Uint16类型就可以完成整个计算。
现代的x86,硬件计算浮点都是先转换为double的,所以用double会比float会更快,当然这里只针对英特尔家族复杂指令集产品。
为什么用float不用double是因为PointF是C#自带的结构体{float X;float Y;},要改为double也是很简单的,全局替换一下数据类型即可
C#数组自带Length属性,但是为了方便移植到C,这里还是使用数组+数组长度作为入参,这样可以很容易的改写为C下面的数组指针+数组长度。
直接实现数学公式是比较简单的,直接贴代码:
public static class Bezier
{
}
使用方法:
// 第一个是起点,最后一个是终点,中间的都是控制点,贝赛尔曲线阶数 = 总点数-1
PointF[] pointList = new PointF[] { new PointF(1.3F, 2.4F), new PointF(2, 3), new PointF(12.3F, 13.2F) };
PointF[] aa = Bezier.draw_bezier_curves(pointList, pointList.Length, 0.001F); // 在起点和终点之间画1/0.001=1000个点
foreach (var item in aa)
{
}
可以很容易的改写成C/C++版,
PointF只是个结构体,{float X;float Y};
C/C++中数组部分不需要new
Math.Pow()对应于C语言math.h头文件里的 pow()
List<PointF>在C++中可以通过vector实现
在C中可以换成malloc分配个数组,大小推荐使用 (1/step) + 1。
目前为止,只是从数学公式上实现了程序,这个程序有什么问题呢?
下面放两张图,分别是通过上面的代码计算出来的 4阶和 某n(n很大)阶的巴赛尔曲线,
可以看到阶数过多的时候计算出错了,原因就在于计算组合数函数那里,当阶数过多的时候,组合数中阶乘的计算结果溢出了。
其实仔细观察会发现阶乘计算结果在bezier_interpolation_func函数中又乘以 了一个小数,
这里就是可以改进的地方,阶乘的计算结果既然只是中间值,那么就可以通过某些算法来除掉这个中间值或者通过转化为累加的方式来解决,
使它能够计算更高的阶数而不会溢出(如果有足够的内存空间..)。
下面来看一个改进版的程序
可以看到将bezier_interpolation_func修改为递归的形式,同时去掉了求组合数的过程。
看一下结果
可以,实现了功能。
然后将递归改为循环,循环的方式有更好的兼容性和效率,特别是某些低端的嵌入式编译器不支持递归方式。
/// 参考: http://blog.csdn.net/Fioman/article/details/2578895
public static PointF bezier_interpolation_func(float t, PointF[] points, int count)
{
}
下面是c语言的一个完整程序,不过是打印曲线点的坐标,不太直观。
#include "math.h"
#include "assert.h"
typedef struct
{
} PointF;
PointF bezier_interpolation_func(float t, PointF* points, int count)
{
}
void draw_bezier_curves(PointF* points, int count, PointF* out_points,int out_count)
{
}
int main(int argc, char **argv)
{
}
参考连接http://blog.csdn.net/Fioman/article/details/2578895
简介引用连接http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/de-casteljau.html