作者:上帝的爱神_413_645 | 来源:互联网 | 2023-10-11 11:46
一、贝塞尔曲线简介摘抄自某度百科:贝塞尔曲线(Béziercurve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲
- 一、 贝塞尔曲线
- 1. 一阶贝塞尔曲线
- 2. 二阶贝塞尔曲线
- 3. 三阶贝塞尔曲线
- 二、代码示例
一、 贝塞尔曲线
简介摘抄自某度百科:
贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。
一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。
贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如PhotoShop等。在Flash4中还没有完整的曲线工具,而在Flash5里面已经提供出贝塞尔曲线工具。
贝塞尔曲线于1962,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由Paul de Casteljau于1959年运用de Casteljau演算法开发,以稳定数值的方法求出贝兹曲线。
简单说,贝塞尔曲线就是通过若干控制点来控制的曲线。通过调整控制点,可以决定曲线的走势(不知道大家有没有被Word中曲线箭头支配的恐惧)。
贝塞尔曲线公式:
\[P_i^k = \left\{
\begin{array}{l}
P_i, k=0 \ (1-t)P_i^{k-1} + tP_{i+1}^{k-1}, k=1,2,...,n, i=0,1,...,n-k-1\\end{array}
\right.
\]
先不考究公式的含义,先看公式的元素,不难发现,本质是点的线性组合。
\(P_i^k\)中的\(P\)指的点的坐标(二维的也好,三维的也罢);\(k\)指的第\(k\)次处理,\(k=0\)指的就是初始情况;\(i\)指的点的索引;\(n\)指的初始点的个数。
大概知道这些参数的含义(可能不太准确),举个例子:
//初始n=3,同时给定三个点P0、P1、P2坐标信息
//k=0,得到P0_0、P1_0、P2_0
//k=1,得到P0_1、P1_1
//k=2,得到P0_2
可以发现,每次迭代后生成的点的个数递减,因此每个\(t\)最终对应一个点,将这个点绘制即可。然后重复操作。
以上只是对公式的通俗解释,实际上公式的含义很明确,就是线性组合。
而贝塞尔曲线要完成的事情也很简单,就是根据给定的点,把曲线绘制出来就行了。怎么绘制?带进公式就行了。
1. 一阶贝塞尔曲线
一阶贝塞尔曲线有两个控制点,所以公式可以简化为:\(P = (1-t)P_0 + tP_1, t \in [0,1]\),显然,这是一个点的线性组合,所以最终得到的是一条直线。
2. 二阶贝塞尔曲线
二阶贝塞尔曲线有三个控制点,所以公式可以简化为:\(P = (1-t)^2P_0 + 2(1-t)tP_1 + t^2P_2, t \in [0,1]\),显然,这是一个关于\(t\)的二次函数,所以是一条抛物线。
3. 三阶贝塞尔曲线
三阶贝塞尔曲线有四个控制点,就不简化了,原理一样。
对于高阶贝塞尔曲线,直接从公式入手,递归即可,每次递归绘制一个点。
二、代码示例
//-----------------------------------------------
//功能描述:绘制贝塞尔曲线
//
//参 数:*ppoint[in], 顶点坐标,[x1, y2],[x2, y2],...
// num[in], ppoint个数(num大于等于3,目前最大支持_BEZIER_POINT_NUM_MAX)
// color[in], 颜色值
// step[in], 步长(0// *pInOut[in/out], 传入前一点计算坐标,传出本次计算的点坐标
//
//返 回 值:void
//
//备注内容:无
//-----------------------------------------------
#define _BEZIER_POINT_NUM_MAX 10
void G2D_DrawBezierCurve( float *ppoint, uint16_t num, uint16_t color, float step, float *pInOut )
{
float new_point[2*_BEZIER_POINT_NUM_MAX];
if (num > _BEZIER_POINT_NUM_MAX) {
USR_LOG_D("bezier curve point must between [3,%d]!", _BEZIER_POINT_NUM_MAX);
return;
}
if (1 == num)
{
TFT_DrawLine(pInOut[0], pInOut[1], ppoint[0], ppoint[1], color);
pInOut[0] = ppoint[0];
pInOut[1] = ppoint[1];
return;
}
else
{
for (int i=0; i<(num-1); i++)
{
new_point[2*i+0] = (1-step)*ppoint[2*i] + step*ppoint[2*(i+1)];
new_point[2*i+1] = (1-step)*ppoint[2*i+1] + step*ppoint[2*(i+1)+1];
}
G2D_DrawBezierCurve(new_point, num-1, color, step, pInOut);
}
}
//贝塞尔曲线控制点1
_internal_ro float _k_bezier_ctl_point_buf1[][2] = {
{20, 220},
{70, 110},
{120, 115},
{170, 200},
{220, 100},
};
//贝塞尔曲线控制点2
_internal_ro float _k_bezier_ctl_point_buf2[][2] = {
{25,220},
{190,170},
{200,140},
{180,80},
{60,90},
{30,120},
{33,165},
{40,185},
{80,230},
{200,230},
};
//贝塞尔曲线控制点
_internal_ro float *_k_bezier_ctl_point[] = {
(float*)_k_bezier_ctl_point_buf1,
(float*)_k_bezier_ctl_point_buf2,
};
_internal_ro uint32_t _k_bezier_ctl_point_num[] = {
GET_ARRAY_NUM(_k_bezier_ctl_point_buf1),
GET_ARRAY_NUM(_k_bezier_ctl_point_buf2),
};
memcpy(dummy_result, _k_bezier_ctl_point[index], VERTEX_DATA_SIZE_2F);
for (int i=0; i<100; i++)
{
G2D_DrawBezierCurve((float*)_k_bezier_ctl_point[index], _k_bezier_ctl_point_num[index], RED, 0.01f*i, dummy_result);
delay_ms(1000/25);
}