贝塞尔曲线开发的艺术

贝塞尔曲线开发的艺术

一句话概括贝塞尔曲线:将任意一条曲线转化为精确的数学公式。

很多绘图工具中的钢笔工具,就是典型的贝塞尔曲线的应用,这里的一个网站可以在线模拟钢笔工具的使用:

http://bezier.method.ac/

20160719162422192

贝塞尔曲线中有一些比较关键的名词,解释如下:

  • 数据点:通常指一条路径的起始点和终止点
  • 控制点:控制点决定了一条路径的弯曲轨迹,根据控制点的个数,贝塞尔曲线被分为一阶贝塞尔曲线(0个控制点)、二阶贝塞尔曲线(1个控制点)、三阶贝塞尔曲线(2个控制点)等等。

要想对贝塞尔曲线有一个比较好的认识,可以参考WIKI上的链接:

https://en.wikipedia.org/wiki/B%C3%A9zier_curve

20160719162452111

贝塞尔曲线模拟

在Android中,一般来说,开发者只考虑二阶贝塞尔曲线和三阶贝塞尔曲线,SDK也只提供了二阶和三阶的API调用。对于再高阶的贝塞尔曲线,通常可以将曲线拆分成多个低阶的贝塞尔曲线,也就是所谓的降阶操作。下面将通过代码来模拟二阶和三阶的贝塞尔曲线是如何绘制和控制的。

贝塞尔曲线的一个比较好的动态演示如下所示:

http://myst729.github.io/bezier-curve/

20160719162741693

二阶模拟

二阶贝塞尔曲线在Android中的API为:quadTo()和rQuadTo(),这两个API在原理上是可以互相转换的——quadTo是基于绝对坐标,而rQuadTo是基于相对坐标,所以后面我都只以其中一个来进行讲解。

先来看下最终的效果:

20160719163120476

从前面的介绍可以知道,二阶贝塞尔曲线有两个数据点和一个控制点,只需要在代码中绘制出这些辅助点和辅助线即可,同时,控制点可以通过onTouchEvent来进行传递。

三阶模拟

三阶贝塞尔曲线在Android中的API为:cubicTo()和rCubicTo(),这两个API在原理上是可以互相转换的——quadTo是基于绝对坐标,而rCubicTo是基于相对坐标,所以后面我都只以其中一个来进行讲解。

有了二阶的基础,再来模拟三阶就非常简单了,无非是增加了一个控制点而已,先看下效果图:

20160719173906245

代码只需要在二阶的基础上添加一些辅助点即可,下面只给出一些关键代码,详细代码请参考Github:

模拟网页

如下所示的网页,模拟了三阶贝塞尔曲线的绘制,可以通过拖动曲线来获取两个控制点的坐标,而起始点分别是(0,0)和(1,1)。

http://cubic-bezier.com/

20160719173939902

通过这个网页,也可以比较方便的获取三阶贝塞尔曲线的控制点坐标。

贝塞尔曲线应用

圆滑绘图

当在屏幕上绘制路径时,例如手写板,最基本的方法是通过Path.lineTo将各个触点连接起来,而这种方式在很多时候会发现,两个点的连接是非常生硬的,因为它毕竟是通过直线来连接的,如果通过二阶贝塞尔曲线来将各个触点连接,就会圆滑的多,不会出现太多的生硬连接。

先来看下代码,非常简单的绘制路径代码:

先来看下通过mPath.lineTo来实现的绘图,效果如下所示:

20160719174009506

图片中的拐点有明显的锯齿效果,即通过直线的连接,再来看下通过贝塞尔曲线来连接的效果,通常情况下,贝塞尔曲线的控制点取两个连续点的中点:

通过二阶贝塞尔曲线的连接效果如图所示:

20160719174039075

可以明显的发现,曲线变得更加圆滑了。

曲线变形

通过控制贝塞尔曲线的控制点,就可以实现对一条路径的修改。所以,利用贝塞尔曲线,可以实现很多的路径动画,例如:

20160719174305469

这里就是简单的改变二阶贝塞尔曲线的控制点来实现曲线的变形。

网上一些比较复杂的变形动画效果,也是基于这种实现方式,其原理都是通过改变控制点的位置,从而达到对图形的变换,例如圆形到心形的变化、圆形到五角星的变换,等等。

波浪效果

波浪的绘制是贝塞尔曲线一个非常简单的应用,而让波浪进行波动,其实并不需要对控制点进行改变,而是可以通过位移来实现,这里我们是借助贝塞尔曲线来实现波浪的绘制效果,效果如图所示:

20160719174547117

波浪动画实际上并不复杂,但三角函数确实对一些开发者比较困难,开发者可以通过下面的这个网站来模拟三角函数图像的绘制:

https://www.desmos.com/calculator

20160719175058994

路径动画

贝塞尔曲线的另一个非常常用的功能,就是作为动画的运动轨迹,让动画目标能够沿曲线平滑的实现移动动画,也就是让物体沿着贝塞尔曲线运动,而不是机械的直线,本例实现效果如下所示:

20160719175157622

其中,用于改变运动点坐标的关键evaluator如下所示:

这里的TypeEvaluator计算用到了计算贝塞尔曲线上点的计算算法,这个会在后面继续讲解。

贝塞尔曲线进阶

求贝塞尔曲线上任意一点的坐标

求贝塞尔曲线上任意一点的坐标,这一过程,就是利用了De Casteljau算法。

http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/de-casteljau.html

20160719175248779

利用这一算法,有开发者开发了一个演示多阶贝塞尔曲线的效果的App,其原理就是通过绘制贝塞尔曲线上的点来进行绘制的,地址如下所示:

https://github.com/venshine/BezierMaker

下面这篇文章就详细的讲解了该算法的应用,我的代码也从这里提取而来:

http://devmag.org.za/2011/04/05/bzier-curves-a-tutorial/

计算

有了公式,只需要代码实现就OK了,我们先写两个公式:

我们来将路径绘制到View中,看是否正确:

这次我们并没有通过API提供的贝塞尔曲线绘制方法来绘制二阶、三阶贝塞尔曲线,而是通过时间t和起始点来计算一条贝塞尔曲线上的所有点,可以发现,通过算法计算出来的点,与通过API所绘制出来的点,是完全吻合的。

贝塞尔曲线拟合计算

贝塞尔曲线有一个非常常用的动画效果——MetaBall算法。相信很多开发者都见过类似的动画,例如QQ的小红点消除,UC浏览器的下拉刷新loading等等。要做好这个动画,实际上最重要的就是通过贝塞尔曲线来拟合两个图形。

效果如图所示:

20160719175318436

矩形拟合

我们来看一下拟合的原理,实际上就是通过贝塞尔曲线来连接两个圆上的四个点,当我们调整下画笔的填充方式,并绘制一些辅助线,我们来看具体是如何进行拟合的,如图所示:

20160719175333312

可以发现,控制点为两圆圆心连线的中点,连接线为图中的这样一个矩形,当圆比较小时,这种通过矩形来拟合的方式几乎是没有问题的,但我们把圆放大,再来看下这种拟合,如图所示:

20160719175348172

当圆的半径扩大之后,就可以非常明显的发现拟合的连接点与圆有一定相交的区域,这样的拟合效果就不好了,我们将画笔模式调整回来,如图所示:

20160719175501611

所以,简单的矩形拟合,在圆半径小的时候,是可以的,但当圆半径变大之后,就需要更加严格的拟合了。

这里我们先来讲解下,如何计算矩形拟合的几个关键点。

从前面那张线图可以看出,标红的两个角是相等的,而这个角可以通过两个圆心的坐标来算出,有了这样一个角度,通过R x cos和 R x sin来计算矩形的一个顶点的坐标,类似的,其它坐标可求,关键代码如下所示:

切线拟合

如前面所说,矩形拟合在半径较小的情况下,是可以实现完美拟合的,而当半径变大后,就会出现贝塞尔曲线与圆相交的情况,导致拟合失败。

那么如何来实现完美的拟合呢?实际上,也就是说贝塞尔曲线与圆的连接点到贝塞尔曲线的控制点的连线,一定是圆的切线,这样的话,无论圆的半径如何变化,贝塞尔曲线一定是与圆拟合的,具体效果如图所示:

20160719175539971

这时候我们把画笔模式调整回来看下填充效果,如图所示:

20160719175609777

这样拟合是非常完美的。那么要如何来计算这些拟合的关键点呢?在前面的线图中,我标记出了两个角,这两个角分别可以求出,相减,就可以获取切点与圆心的夹角了,这样,通过R x cos和R x sin就可以求出切点的坐标了。

其中,小的角可以通过两个圆心的坐标来求出,而大的角,可以通过直角三角形(圆心、切点、控制点)来求出,即控制点到圆心的距离/半径。

关键代码如下所示:

圆的拟合

贝塞尔曲线做动画,很多时候都需要使用到圆的特效,而通过二阶、三阶贝塞尔曲线来拟合圆,也不是一个非常简单的事情,所以,我直接把结论拿出来了,具体的算法地址如下所示:

http://spencermortensen.com/articles/bezier-circle/

http://stackoverflow.com/questions/1734745/how-to-create-circle-with-b%C3%A9zier-curves

20160719175640612

20160719175710856

有了贝塞尔曲线的控制点,再对其实现动画,就非常简单了,与之前的动画没有太大的区别。

源代码

本次的讲解代码已经全部上传到Github :

https://github.com/xuyisheng/BezierArt

欢迎大家提issue。

打赏支持我写出更多好文章,谢谢!

打赏作者

打赏支持我写出更多好文章,谢谢!

2 7 收藏 1 评论

关于作者:eclipse_xu

Android 高级开发工程师;《Android群英传》、《Android群英传:神兵利器》作者、慕课网Android讲师;CSDN博客专家 个人主页 · 我的文章 · 13 ·    

相关文章

可能感兴趣的话题



直接登录
最新评论
跳到底部
返回顶部