说到贝塞尔曲线,大家可能首先会想到下面这个人:图1.一位无名英国球员但其实我们说的不是这个叫贝克汉姆的英国人,而另外一个人,就是下面这位名叫“皮埃尔·贝齐尔”(PierreBézier)的法国人:图2.皮埃尔·贝齐尔谈人气,或许不如贝克汉姆,但说到对人类的贡献,就是一个杠杆,而著名的“贝塞尔曲线”就是出自于他。1962年贝塞尔发表了贝塞尔曲线的理论研究。在雷诺工作时,他主要使用贝塞尔曲线进行汽车设计。说到这里,可能还有很多人不明白什么是贝塞尔曲线。看看下图,大家就明白了。图3设计中使用的贝塞尔曲线在Photoshop等各种设计软件中,绘制曲线时主要使用贝塞尔曲线,类似于上图中的曲线。设计者可以通过控制中间的控制点来画出自己需要的曲线。过去,设计师用电脑画直线很容易,但要画出平滑的曲线就非常困难了。但是,贝塞尔曲线的诞生,让大家用电脑画出平滑的曲线成为可能。那就是贝塞尔曲线最大的用处。今天我们就来说说最简单的二阶贝塞尔曲线的推导,并使用matplotlib进行演示。(其实最简单的就是一阶,但是因为它只有一条直线,没有实际用处,所以忽略)先来了解一下二阶贝塞尔曲线的原理。如果两条线段AB和BC连在一起,如下图所示:图4二阶贝塞尔曲线示意图1现在在AB上取一点D,在BC上取一点E,使得AD/AB=BE/BC,如下图:图5.二阶贝塞尔曲线的原理2需要在线段DE上有一个点F,使得DF/DE=AD/AB=BE/BC,如如下图所示:图6二阶贝塞尔曲线的原理图3当D在AB上不断运动,E在BC上不断运动时,F点形成的轨迹是一条曲线,这条曲线为二阶贝塞尔曲线。这就是我们今天要导出和演示的曲线。我们直接用Python代码来展示吧。首先导入各种包:importnumpyasnpimportmatplotlib.pyplotaspltfrommatplotlibimportanimation因为我们演示使用matplotlib,所以需要设置matplotlib的后端,也就是显示方式。这行代码最好单独使用,否则容易失败,所以单独列出来:%matplotlib这是很重要的一步,这一步可以让matplotlib在绘图时弹出一个新的窗口,而不是直接在原来的窗口中绘图window,因为原来的窗口不能显示动画。这里使用了ipython的魔法命令,即在命令前加“%”。在这里说明一下,笔者使用的是win7系统,开发工具是最新版的Anaconda,可以直接到Anaconda官网下载。因为我们要用到A、B、C三个点,所以需要设置三个点的坐标。A点坐标为x1和y1,B点坐标为x2和y2,C点坐标为x3和y3,然后设置一个范围内的点数dots_num,这个dots_num后面会解释。所有这些变量的值都可以自由设置,但是有一些要求,因为作者将绘图的坐标系设置为100,即x轴和y轴的范围是0-100,所以以上三个点的坐标不要超过这个范围,dots_num的个数尽量大一些,这样动画更连贯,所以以上参数的设置如下:x1=10y1=80x2=50y2=10x3=90y3=80dots_num=100接下来就是得到dots_num=100Serre曲线轨迹的函数:deftwo_degree_bc(x1=10,y1=80,x2=50,y2=10,x3=90,y3=80,dots_num=100):#beziercurveglobalxt,yt,x_dots12,x_dots23,y_dots12,y_dots23xt=[]#目标点的x坐标yt=[]#目标点的y坐标x_dots12=np.linspace(x1,x2,dots_num)#线段AB的x坐标y_dots12=np.linspace(y1,y2,dots_num)#线段AB的y坐标x_dots23=np.linspace(x2,x3,点数)#线段BC的x坐标y_dots23=np.linspace(y2,y3,dots_num)#线段BC的y坐标foriinrange(dots_num):#得到目标点的轨迹x=x_dots12[i]+(x_dots23[i]-x_dots12[i])*i/(dots_num-1)y=y_dots12[i]+(y_dots23[i]-y_dots12[i])*i/(dots_num-1)xt.append(x)yt.append(y)其中xt和yt是两个列表,用来存储目标点的x和y坐标,x_dots12和y_dots12分别是线段AB的x和y坐标,x_dots23和y_dots23分别是线段BC的x和y坐标,这四个是numpy数组格式,这些数据都设置为全局变量。从上面的代码我们可以看出变量dots_num的作用就是在AB和BC线段上取那么多点,然后用这些点推导出目标点的坐标。点数越多,目标点的坐标就越多。更多,绘制的曲线会更平滑。接下来是动画函数,后面会解释:defrun(i):art1.set_data(x_dots12[i],y_dots12[i])art2.set_data(x_dots23[i],y_dots23[i])art3.set_data([x_dots12[i],x_dots23[i]],[y_dots12[i],y_dots23[i]])art4.set_data(xt[i],yt[i])returnart1,art2,art3,art4最后绘制动画现在,代码如下:two_degree_bc()#Mr.生成目标点的轨迹fig,ax=plt.subplots(figsize=(8,8))ax.set_aspect(1)#使两个坐标轴成正比plt.xlim([0,100])#设置坐标轴范围plt.ylim([0,100])ax.plot([x1,x2],[y1,y2],color='#3e82fc')#画AB线ax.plot([x2,x3],[y2,y3],color='#3e82fc')#绘制BC线ax.plot(xt,yt,color='orange')#绘制目标曲线art1,=ax.plot(x_dots12[0],y_dots12[0],color='green',marker='o')#scatter不能用,因为得到的对象不是list,而是objectart2,=ax.plot(x_dots23[0],y_dots23[0],color='green',marker='o')art3,=ax.plot([x_dots12[0],x_dots23[0]],[y_dots12[0],y_dots23[0]],color='purple')#plot结果是一个列表,只包含一个元素,即一个形状对象art4,=ax.plot(xt[0],yt[0],color='red',marker='o')ani=animation.FuncAnimation(fig,run,frames=range(100),干预al=2,save_count=50)plt.show()这里先运行two_degree_bc()函数得到目标点的轨迹,然后绘制AB、BC线段和目标曲线,这些都是静态图片然后从变量art1开始绘制动画部分。这部分比较复杂。有四个变量:art1、art2、art3、art4,分别对应线段AB、BC、DE和目标曲线的移动轨迹。只有点在这四个轨迹上移动,才能形成动画。生成动画需要用到动画方法FuncAnimation,它包含多个参数,fig是我们绘制的画布,run是我们生成动画时运行的函数,frames是帧,每一帧包含了对应的静态图片这些运动轨迹中的一个点,连接这些点的轨迹,也就是每一帧都是动画轨迹。帧一般是一个序列,即包含多个变量,每个变量赋值给run函数,run函数就是通过这个参数来生成一个静态图片,那么多静态图片的连接就是一个动画,和我们在电影或者电视里看到的动画是一样的。interval是帧之间的时间间隔,200代表0.2秒,可以任意设置。save_count=50是缓存播放的帧。缓存帧数越多,播放越流畅。这个影响不大,可以随意设置。接下来说一下运行动画函数run的功能。可以看到有5行代码在运行。前4行代码是绘图代码,最后一行是返回参数的代码。前4行中的每一行都代表我们之前所说的内容。点的轨迹,art1为线段AB上的点,使用set_data(x_dots12[i],y_dots12[i])方法生成对应的帧,变量i为上述帧中的参数,生成的Frames返回给FuncAnimation继续播放,形成动画。art2、art3和art4背后的原因是一样的。生成的动画效果静态截图如下:图7.二阶贝塞尔曲线静态图最后放一张动图,让我们以深V动图结束这个话题:图8.二阶贝塞尔曲线贝塞尔曲线贝塞尔曲线的动态图二阶贝塞尔曲线的推导相对容易,但三阶甚至更高阶的推导就有点复杂了。笔者目前正在研究三阶贝塞尔曲线,稍后分享给大家。完整代码已经上传:https://gitee.com/leonmovie/two_degree_bc需要的可以自行下载。作者简介:小李子,数据分析爱好者,擅长数据可视化,比较关注机器学习领域。希望与业内朋友一起学习交流------欢迎搜索关注:Crossin的编程课堂,更多精彩在这里。一起学习,走得更远
