OpenGL 学习系列文章
说到贝塞尔曲线,大家肯定都不陌生,网上有很多关于介绍和理解贝塞尔曲线的优秀文章和动态图。
以下两个是比较经典的动图了。
二阶贝塞尔曲线:
三阶贝塞尔曲线:
由于在工作中经常要和贝塞尔曲线打交道,所以简单说一下自己的理解:
现在假设我们要在坐标系中绘制一条直线,直线的方程很简单,就是 y=x ,很容易得到下图:
现在我们限制一下 x 的取值范围为 0~1 的闭区间,那么可以得出 y 的取值范围也是 0~1。
而在 0~1 的区间范围内,x 能取的数有多少个呢?答案当然是无数个了。
同理,y 的取值个数也是有无数个。每一个 x 都有唯一的 y 与之对应,一个 (x,y) 在坐标系上就是一个点。
所以最终得到的 0~1 区间的线段,实际上是由无数的点组成的。
那么这条线段有多长呢?长度是由 x 的取值范围来决定的,若 x 的取值为 0~2,那么线段就长了一倍。
另外,如果 x 的取值范围不是无数个,而是以 0.05 的间距从 0 到 1 之间递增,那么得到的就是一串点了。
由于 点 是一个理想状态下的描述,在数学上点是没有宽高、没有面积的。
但是,如果你在草稿纸上绘制一个点,不管你用到是铅笔、毛笔、水笔还是画笔,一个点总是要占面积的。
毛笔画一个点的面积可能需要铅笔画几十个点了。
在实际生活中,如果要以 0.05 的间距在第一幅坐标系图中画出 x 在 0~1 区间的一串点,最终结果就和直接画一条线段没啥差别了。
这就是现实和理想的差别了。理想一串点,现实一条线。
我们把这个逻辑放到手机屏幕上。
手机屏幕上的最小显示单位就是像素了,一个 1920 * 1080 的屏幕指的就是各方向上像素点的数量。
假如绘制一条和屏幕一样宽的线段,一个点最小就算一个像素,最多也就 1080 个点了。
点占的像素越多,那么实际绘制时需要的点的数量越少,这也算是潜在的优化项了。
说完直线,再回到贝塞尔曲线上。
曲线和直线都有一个共同点,它们都有各自特定的方程,只不过我们用的直线例子比较简单,既 y = x ,一眼看出计算结果。
直线方程 y = x,在数学上可以这么描述:y 是关于 x 的函数,既 y = F(x) ,其中 x 的取值决定了该直线的长度。
根据上面的理解,这个长度的直线实际又是由在 x 的取值范围内对应的无数个点组成的。
反观贝塞尔曲线方程以及对应的图形如下:
二阶贝塞尔曲线:
其中,P0 和 P2 是起始点,P1 是控制点。
三阶贝塞尔曲线
其中,P0 和 P3 是起始点,P1 和 P2 是控制点。
不难理解,假设我们要绘制一条曲线,肯定要有起始和结束点来指定曲线的范围曲线。
而控制点就是指定该曲线的弧度,或者说指定该曲线的弯曲走向,不同的控制点得出的曲线绘制结果是不一样的。
另外,可以观察到,无论是几阶贝塞尔曲线,都会有参数 t 以及 t 的取值范围限定。
t 在 0~1 范围的闭区间内,那么 t 的取值个数实际上就有无数个了,这时的 t 就可以理解成上面介绍直线中讲到的 x 。
这样一来,就可以把起始点、控制点当初固定参数,那么贝塞尔曲线计算公式就成了 B = F(t) ,B 是关于 t 的函数,而 t 的取值范围为 0~1 的闭区间。
也就是说贝塞尔曲线,选定了起始点和控制点,照样可以看成是 t 在 0~1 闭区间内对应的无数个点所组成的。
有了上面的阐述,在工(ban)程(zhuan)的角度上,就不难理解贝塞尔曲线到底怎么使用了。
Android 绘制贝塞尔曲线
Android 自带贝塞尔曲线绘制 API ,通过 Path 类的 quadTo 和 cubicTo 方法就可以完成绘制。
// 构建 path 路径,也就是选取
path.reset();
path.moveTo(p0x, p0y);
// 绘制二阶贝塞尔曲线
path.quadTo(p1x, p1y, p2x, p2y);
path.moveTo(p0x, p0y);
path.close();
// 最后的绘制操作
canvas.drawPath(path, paint);
这里的绘制实际上就是把贝塞尔曲线计算的方程式交给了 Android 系统内部去完成了,参数传递上只传递了起始点和控制点。