多控制点的贝塞尔曲线生成法

多控制点的贝塞尔曲线生成法

一、贝塞尔曲线基本理论

贝赛尔曲线的本质:是通过数学计算公式去绘制平滑的曲线。
贝塞尔曲线涉及的点:起始点、终止点、控制点
通过变化调整控制点,贝塞尔曲线的形状会发生变化。根据方程的最高阶数,又分为线性贝塞尔曲线、二阶贝塞尔曲线、三阶贝塞尔曲线和高阶贝塞尔曲线。

1.1、线性贝塞尔曲线(一阶贝塞尔曲线)

本质上就是起始点与终止点的线性插值:

P(t)=t*p0+(1-t)p1 t=[0,1]

从p0到p1之间插值n个点,将n个点连接起来,以直代曲。n个点生成方式为,线性插值,t从0到1分成n等份,每份的系数为i*t

vector<vec>bezier;
for(int i=0;i<=100;i++)
{
	float t=0.01*i;
	vec p=t*p0+(1-t)*p1;
	bezier.push_back(p);
}

Alt

1.2、二阶贝塞尔曲线

本质是先经过一阶贝塞尔计算,对其结果再进行一阶贝塞尔计算,推导出来的表示公式为:

P(t)=(1-t)^2 p0+2t(1-t)p1+t^2p2 t=[0,1]

vector<vec>bezier;
for(int i=0;i<=100;i++)
{
	float t=0.01*i;
	vec p=(1-t)*(1-t)*p0+2*t*(1-t)*p1+t*t*p2 
	bezier.push_back(p);
}

Alt

1.3、三阶贝塞尔曲线

本质是在二次贝塞尔的结果在进行一阶贝塞尔计算,推导的表示公式为:

Alt

	bezier.clear();
	float l = len(v3 - v0);
	float s = l / cn;
	for (float t = 0; t < 1-1e-3; t += s)
	{
		float a = -t*t*t + 3 * t*t - 3 * t + 1;
		float b = 3 * t*t*t - 6 * t*t + 3 * t;
		float c = -3 * t*t*t + 3 * t*t;
		float d = t*t*t;
		vec v = a*v0 + b*v1 + c*v2 + d*v3;
		bezier.push_back(v);
	}

Alt

1.4、高阶贝塞尔曲线

本质也一样,都是在上一阶的结果再推导,得到表达公式后,转换为代码即可,此处略

二、多控制点的贝塞尔曲线

这里主要分析的是多控制点的三阶贝塞尔曲线连续光滑的问题,其中一阶、二阶的思路,方法类似,自行推导
问题1:三阶贝塞尔曲线为每四个点可生成连续曲线,当控制点不为四的倍数时,要怎么拼接?
问题2:在每个拼接点处,如何保证光滑连续,无突变?
解答问题之前,先提供其中的一种算法:
Alt

1、如上图所示,型值点(曲线经过给定的点称为型值点,定型的作用)为A,D,G

2、假设在A、D、G三个点位置处,给定一个切向量(根据个人爱好给定),如BI、CE、FH,那么在A、D、G三点处左右两侧的曲率就能一致

3、回顾三阶贝塞尔曲线,有这两个特点:

  • 起点和终点必须为p0和p3,此时t分别为0和1;
  • 曲线在起点和终点处必须与其相切

4、为此,如有控制点,如上图,A、B、C、D。得到的三阶贝塞尔曲线,必然经过A和D,并且曲率分别为BI和CE

综上:即是给定一组连续的点,要求拟合曲线必须经过这些点,则在每两个连续点之间插入两个控制点,拟合成分段三阶贝塞尔曲线,遍历所有给定点,组合得到的曲线一定为连续光滑的三阶贝塞尔曲线。

分析问题一:当输入点数为任意n,两两之间生成连续曲线,则不会再存在四的倍数问题
分析问题二:由上述可知在各个点连接处必然连续

三、代码实现

以下给定每个型值点处的切向曲率的方式

Alt
如图E、F为AB、BC的中点,G为EF的中点,将EF平移,G点与B点重合,则EF为B点处的切线,E撇、F撇为新增控制点。则E撇、F撇的位置,可以随意,本文取k=0.3,即0.3倍单位长度0.3*normalize(EF)
以上切向量定义为任意,控制点位置为任意,根据自己个人喜好给定即可。

std::vector<vec> BezierCurve::GetSecondOrderBezierCurve()
{
	float k = 0.3;
	int vn = cnt_ps.size();
	vector<vector<vec>>type_plists;
	if (vn < 3)return vector<vec>();
	for (int i = 0; i < vn; i++)
	{
		vec&p0 = cnt_ps[(i + vn - 1) % vn];
		vec&p1 = cnt_ps[i];
		vec&p2 = cnt_ps[(i + 1) % vn];
		vec&p3 = cnt_ps[(i + 2) % vn];

		vec v0 = 0.5f*(p1 - p0) + p0;
		vec v1 = 0.5f*(p2 - p1) + p1;
		vec v2 = 0.5f*(p3 - p2) + p2;

		vec v01 = v1 - v0;
		vec v12 = v2 - v1;
		normalize(v01);
		normalize(v12);
		vec c0 = p0 + k*v01;
		vec c1 = p1 - k*v12;
		vector<vec>bezier;
		MakeBezierCurve(p1, c0, c1, p2,10,bezier);
		type_plists.push_back(bezier);
	}
	vector<vec>plist;
	for (auto&ls:type_plists)
	{
		for (auto&item : ls)plist.push_back(item);
	}
	return plist;

Alt

发布了83 篇原创文章 · 获赞 24 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/LittleLittleFish_xyg/article/details/104036248