动画设计实验

Background

matlab给我们提供了plot,ezplot,fplot,polar,bar,bar3,plot3,mesh,surf一系列绘图的办法,python也给我们matplotlib.pyplot库可以画各种各样的静态图形,可我们有的时候想展示曲线的运动情况,像放动画一样展现出来,甚至还想保存为gif或者mp4之类的文件,这就不得不提到我们今天的主题了:动画实验;

Content

matlab绘图基本算法

不像matplotlib中可以调用animation模块,matlab只能调用绘图的api来实现逐帧绘制;

绘制的基本思想是:

  1. 确定动画的画布;
  2. 确定动画的帧数;
  3. 清空当前的图形;
  4. 绘制当前帧的图形;
  5. 暂停一小会以免动画太快;

有了这些思想,在matlab就可以绘图了,不过想要利用python的库,还需要一些基本知识;

FuncAnimation基础介绍

animation 有个核心类: FuncAnimation ;

FuncAnimation(fig, func, frames, init_func, interval, blit)

  • fig: 绘制动图的画布名称

    这个参数通常由先前的figure()函数创建;

  • func: 回调函数

    更新新一帧的方式,这个函数返回当前帧的绘制对象,请注意返回值不只一个

    一般计算出新的坐标后,利用绘制对象的set_data方法去更新它;

  • frames: 动画的帧数

    参数常见值为n:int,相当于range(n),但事实上也可以取值 iterable,generator,None

  • init_func: 初始帧

    func类似,返回初始帧的绘制对象。

  • interval: 帧长

    决定帧更新频率,单位:ms

  • blit: 选择更新所有点,还是仅更新产生变化的点。

通过这些参数可以看到,基本和matlab手动实现所需要的准备工作是差不多的;

保存动图文件

在matlab中,一般将动画保存到.avi文件中,这是由matlab提供的接口决定的,一般方法如下:

  1. obj=VidioWriter('test.avi'):产生VidioWriter对象,同时起一个名字
  2. open(obj):打开obj对象;
  3. writeVidio(obj,frame):将当前帧存到obj中;
  4. close(obj):关闭obj对象

而在python中,你只需要安装一些依赖项即可,如果用 FuncAnimation 生成的动图,注意参数fps–frames per second:

  • mp4:执行 pip install ffmpeg
  • gif : 执行 pip install Wand
1
2
ani.save("test.mp4", fps=20, writer="ffmpeg")
ani.save("test.gif", fps=50, writer="imagemagick")

Example

实现正方形旋转,保存文件;

我们知道,对于列向量$(x_0,y_0)^T$,逆时针旋转$\theta$角,得到新的向量$(x_1,y_1)^T=\begin{pmatrix}cos\theta&-sin\theta\sin\theta&cos\theta \end{pmatrix}(x_0,y_0)^T$,我们分别采用两种语言实现:

matlab:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
P=[0 1 1 0 0;0 0 1 1 0];
obj = VideoWriter('test.avi');
open(obj);
for t = linspace(0, 3*pi/4, 100)
M = [cos(t) -sin(t);sin(t) cos(t)];
PP = M*P;
plot(PP(1,:),PP(2,:),'r');
axis equal;
axis([-2 2 -2 2]);
frame = getframe(gcf);
writeVideo(obj, frame);
pause(0.05);
end
close(obj);

来看一下我们的test.avi(突然发现它在Typora里预览不了…)

python:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import numpy as np 
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

fig = plt.figure(figsize = (6, 6))
ax = fig.add_subplot(1, 1, 1)
p = np.array([[0,1,1,0,0],[0,0,1,1,0]])
square, = ax.plot(p[0], p[1], color = 'red', animated=True)
frames = np.linspace(0, 3*np.pi/4, 100)

def init():
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)
return square,

def update(t):
M = np.array([[np.cos(t), -np.sin(t)],[np.sin(t), np.cos(t)]])
pp = M @ p
ax.set_aspect('equal')
square.set_data(pp[0], pp[1])
return square,

ani = FuncAnimation(fig, update, frames=frames, init_func=init, interval=50, blit=True)
# plt.show()
ani.save("test.gif", fps=25, writer="imagemagick")

test

Remark

又水了一篇…