matsulibの日記

Ingredients as Code

matplotlibによるサイクロイドのアニメーションにて

以下のサイトを参考に、matplotlibでグラフのアニメーション化を試してみた。

matplotlibでランダムウォークをアニメーション - Soleil cou coupé
Simple Animated Plot with Matplotlib | Scientific Python Script Repository
Drawing and Animating Shapes with Matplotlib — nickcharlton.net

具体的な構造についてはよく分からないけど、まあ、ジェネレータがフレーム毎に呼ばれて値を返し、その値を毎回プロットすれば良い。

  1. アニメーションが表示されるパレットfigを用意する。
  2. obj,=ax.plot([ ],[ ])でアニメーションで動く変数objを用意する。
  3. ジェネレータgen()が呼ばれてdataを返す。例えば、x,y。
  4. obj.set_data(x,y)で値をobjにセット。
  5. 値がプロットされ、3に戻る。

これがジェネレータが終了するまで続く感じ。必要ならばfigも途中で再定義可能。
4の中身はfunc(data)内に記述。それらをまとめるanimation.FuncAnimation(fig,func,gen)でアニメーションが出力される。

ソースコード

さて、サイクロイドが何かというのは他の人にまかせて、その媒介変数表示は半径R、回転角θのとき
   x=R(θ-sinθ), y=R(1-cosθ)
なので、これをθに対して動かすコードは次のようになる。

# -*- coding:utf-8 -*-
from matplotlib import pyplot as plt
from matplotlib import animation
import numpy as np

def circle(a, b, r):
    # 点(a,b)を中心とする半径rの円
    T = 100
    x, y = [0]*T, [0]*T
    for i,theta in enumerate(np.linspace(0,2*np.pi,T)):
        x[i] = a + r*np.cos(theta)
        y[i] = b + r*np.sin(theta)
    return x, y

R = 1

def gen():
    for theta in np.linspace(0,4*np.pi,100):
        yield R*(theta-np.sin(theta)), R*(1-np.cos(theta)), R*theta

fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_ylim(-5, 5)
ax.set_xlim(0, 10)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.grid()
time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes)

cycloid, = ax.plot([], [], 'r-', lw=2)
line, = ax.plot([], [], 'y-', lw=2)
circle_line, = ax.plot([], [], 'g', lw=2)
point, = ax.plot([], [], 'bo', ms=4)

xx, yy = [], []
def func(data):
    x, y, Rt = data
    time_text.set_text('theta = %.2f pi' % (Rt/np.pi))
    xx.append(x)
    yy.append(y)
    cx, cy = circle(Rt, R, R)
    
    cycloid.set_data(xx, yy)
    line.set_data((x,Rt), (y,R))
    circle_line.set_data(cx, cy)
    point.set_data(x, y)

ani = animation.FuncAnimation(fig,
        func, gen, blit=False, interval=100, repeat=False)

#ani.save("cycloid.mp4")
plt.show()

gifアニメ化

生成されたcycloid.mp4は

ffmpeg -i cycloid.mp4 -r 10 output%05d.png
convert output*.png cycloid.gif
rm output*.png

でgifアニメにできる。※手元の環境ではconvertが非常に重くて一度フリーズした。
よく分からないけどMP4ファイルではなく最初から連番pngを吐かせることも出来るはず。gif化ももっと手軽に使えるフリーソフトがあると思う。

f:id:matsulib:20131102032917g:plain:w400