n阶贝塞尔曲线绘制(C/C#)

100000 / 100 =  1000

500 + 500 = 1000

C#数组自带Length属性，但是为了方便移植到C，这里还是使用数组+数组长度作为入参，这样可以很容易的改写为C下面的数组指针+数组长度。

public static class Bezier
{
/// <summary>
/// 绘制n阶贝塞尔曲线路径
/// </summary>
/// <param name="points">输入点</param>
/// <param name="count">点数(n+1)</param>
/// <param name="step">步长,步长越小，轨迹点越密集</param>
/// <returns></returns>
public static PointF[] draw_bezier_curves(PointF[] points, int count, float step)
{
List<PointF> bezier_curves_points = new List<PointF>();
float t = 0F;
do
{
PointF temp_point = bezier_interpolation_func(t, points, count);    // 计算插值点
t += step;
}
while (t <= 1 && count > 1);    // 一个点的情况直接跳出.
return bezier_curves_points.ToArray();  // 曲线轨迹上的所有坐标点
}
/// <summary>
/// n阶贝塞尔曲线插值计算函数
/// 根据起点，n个控制点，终点 计算贝塞尔曲线插值
/// </summary>
/// <param name="t">当前插值位置0~1 ，0为起点，1为终点</param>
/// <param name="points">起点，n-1个控制点，终点</param>
/// <param name="count">n+1个点</param>
/// <returns></returns>
private static PointF bezier_interpolation_func(float t, PointF[] points, int count)
{
PointF PointF = new PointF();
float[] part = new float[count];
float sum_x = 0, sum_y = 0;
for (int i = 0; i < count; i++)
{
ulong tmp;
int n_order = count - 1;    // 阶数
tmp = calc_combination_number(n_order, i);
sum_x += (float)(tmp * points[i].X * Math.Pow((1 - t), n_order - i) * Math.Pow(t, i));
sum_y += (float)(tmp * points[i].Y * Math.Pow((1 - t), n_order - i) * Math.Pow(t, i));
}
PointF.X = sum_x;
PointF.Y = sum_y;
return PointF;
}
/// <summary>
/// 计算组合数公式
/// </summary>
/// <param name="n"></param>
/// <param name="k"></param>
/// <returns></returns>
private static ulong calc_combination_number(int n, int k)
{
ulong[] result = new ulong[n + 1];
for (int i = 1; i <= n; i++)
{
result[i] = 1;
for (int j = i - 1; j >= 1; j--)
result[j] += result[j - 1];
result[0] = 1;
}
return result[k];
}
}

// 第一个是起点，最后一个是终点，中间的都是控制点，贝赛尔曲线阶数 = 总点数-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#绘制到Panel画板控件上的代码
// panel1.CreateGraphics().DrawEllipse(new Pen(Color.Green), new RectangleF(item, new SizeF(2, 2)));
}

PointF只是个结构体，{float X；float Y}；
C/C++中数组部分不需要new
Math.Pow()对应于C语言math.h头文件里的 pow()
List<PointF>在C++中可以通过vector实现

public static PointF bezier_interpolation_func(float t, PointF[] points, int count)
{
if (points.Length < 1)  // 一个点都没有
throw new ArgumentOutOfRangeException();

if (count == 1)
return points[0];
else
{
PointF[] tmp_points = new PointF[count];
for (int i = 1; i < count; i++)
{
tmp_points[i - 1].X = (float)(points[i - 1].X * t + points[i].X * (1 - t));
tmp_points[i - 1].Y = (float)(points[i - 1].Y * t + points[i].Y * (1 - t));
}
return bezier_interpolation_func(t, tmp_points, count - 1);
}
}

/// 参考: http://blog.csdn.net/Fioman/article/details/2578895
public static PointF bezier_interpolation_func(float t, PointF[] points, int count)
{
if (points.Length < 1)  // 一个点都没有
throw new ArgumentOutOfRangeException();

PointF[] tmp_points = new PointF[count];
for (int i = 1; i < count; ++i)
{
for (int j = 0; j < count - i; ++j)
{
if (i == 1) // 计算+搬运数据,在计算的时候不要污染源数据
{
tmp_points[j].X = (float)(points[j].X * (1 - t) + points[j + 1].X * t);
tmp_points[j].Y = (float)(points[j].Y * (1 - t) + points[j + 1].Y * t);
continue;
}
tmp_points[j].X = (float)(tmp_points[j].X * (1 - t) + tmp_points[j + 1].X * t);
tmp_points[j].Y = (float)(tmp_points[j].Y * (1 - t) + tmp_points[j + 1].Y * t);
}
}
return tmp_points[0];
}

#include "stdio.h"
#include "math.h"
#include "assert.h"

typedef struct
{
float X;
float Y;
} PointF;

PointF bezier_interpolation_func(float t, PointF* points, int count)
{
assert(count>0);

PointF tmp_points[count];
for (int i = 1; i < count; ++i)
{
for (int j = 0; j < count - i; ++j)
{
if (i == 1)
{
tmp_points[j].X = (float)(points[j].X * (1 - t) + points[j + 1].X * t);
tmp_points[j].Y = (float)(points[j].Y * (1 - t) + points[j + 1].Y * t);
continue;
}
tmp_points[j].X = (float)(tmp_points[j].X * (1 - t) + tmp_points[j + 1].X * t);
tmp_points[j].Y = (float)(tmp_points[j].Y * (1 - t) + tmp_points[j + 1].Y * t);
}
}
return tmp_points[0];
}

void draw_bezier_curves(PointF* points, int count, PointF* out_points,int out_count)
{
float step = 1.0 / out_count;
float t =0;
for(int i=0; i<out_count; i++)
{
PointF temp_point = bezier_interpolation_func(t, points, count);    // 计算插值点
t += step;
out_points[i] = temp_point;
}
}

int main(int argc, char **argv)
{
PointF in[3] = {{100,100},{200,200},{300,100}}; // 输入点

int num = 1000;     // 输出点数
PointF out[num];    // 输出点数组

draw_bezier_curves(in,3,out,num);// 二阶贝塞尔曲线

for(int j=0; j<num; j++)    // 输出路径点
{
printf("%d\t X=%f \t Y=%f \r\n",j,out[j].X,out[j].Y);
}
return 0;
}