Cython(15) 延々と再描画して動画に


2022年 11月 04日

延々と少しだけ違う画像を生成して見せるには、次の2つを用意しないといけない。

  1. 定数Cを描画毎に少しずつ変更する。
  2. 描画が済んだら、また描画関数が呼ばれるようにする。

これを行えば、きっと動画に見えるはずだ。やってみよう。

定数Cを延々と変更する

定数Cは complex(0.322,0.047) に固定されていたが、これを微妙に動かすと、フラクタル図形も微妙に変形するはずで、そのための変位量Δを計算するようにした。

def delta():
	global cnt
	r = 0.012
	th = 0.002 * cnt;
	cnt += 1
	return r * (math.sin(th)+math. sin(th*math.sqrt(2))*1j)

グローバル変数のcntは最初に0に初期化しておくと、これを呼ぶ度に1ずつ増加する。
三角関数sinを用いるのだが、角度を th = 0.002 * cnt で求めている。
変位デルタが戻り値なのだが、実数部、虚数部ともに三角関数sinに変位thを利用している。ただし、実数部と虚数部の変位角が永遠に重ならないように、虚数部のsinの角度は2の平方根を掛けている。これで、Δは延々と重なることのないリサージュ図形となる。
最後にrを掛けているが、これは僅かな変位にするための適当な値0.012にしている。

延々と再描画

次が再描画の関数であるが、変更部分は僅かである。

前と変わったのは、C_valueが初期値にΔを加えることで少しずつ変えた。

最後に、root.after( 5, draw_new_image ) を加えた。
これは、第1引数の値(5)のミリ秒後に、第2引数の関数を実行せよということ。
これにより、この関数は、canvas.update()  の後、5ミリ秒待ってから再度呼び出されることで、永久ループとなっている。
実際には、メインループがイベントなどを監視し、そこに登録しているだけである。

def draw_new_image():
	global canvas, im, root, C_org

	C_value = C_org + delta()
	im = calc_julia.calc_julia(1000,C_value)	## Julia集合の計算

	im = np.uint8(plt.get_cmap()(im)*255)   	## defaultのカラーマップを適用し、uint8 に変換
	im = Image.fromarray(im)                	## <class 'PIL.Image.Image'>
	im = ImageTk.PhotoImage(image=im)       	## Tkinterの標準のフォトイメージ
	canvas.create_image( 500, 500, image=im )   ## Canvasにイメージを貼り付ける。(500,500)が中心
	canvas.update()                         	## update()されると表示される

	root.after( 5, draw_new_image )        	## 10ミリ秒後に再実行

メインであるが、変更は僅かであり、説明は不要であろう。

if __name__ == '__main__':
	gui_init()                              	## GUIの初期化
	cnt = 0
	C_org = complex(0.322,0.047)            	## 初期定数C
	draw_new_image()                        	## 初期表示   
	root.mainloop()                         	## メインループ(イベント待ち)

以上で分けてソースを示したが、動画を動かすPythonのメインプログラムが以下である。念のため。

## julia集合の画像をtkinterのcanvasに描く, 動的に変化する。

import calc_julia
import tkinter
from PIL import Image, ImageTk
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import numpy as np
import math

def delta():
	global cnt
	r = 0.012
	th = 0.002 * cnt;
	cnt += 1
	return r * ( math.sin(th) + math.sin(th*math.sqrt(2))*1j )
    
def draw_new_image():
	global canvas, im, root, C_org

	C_value = C_org + delta()
	im = calc_julia.calc_julia(1000,C_value)	## Julia集合の計算

	im = np.uint8(plt.get_cmap()(im)*255)   	## defaultのカラーマップを適用し、uint8 に変換
	im = Image.fromarray(im)                	## <class 'PIL.Image.Image'>
	im = ImageTk.PhotoImage(image=im)       	## Tkinterの標準のフォトイメージ
	canvas.create_image( 500, 500, image=im )   ## Canvasにイメージを貼り付ける。(500,500)が中心
	canvas.update()                         	## update()されると表示される

	root.after( 10, draw_new_image )        	## 10ミリ秒後に再実行

def gui_init():
	global canvas, root
   	 
	root = tkinter.Tk()
	root.wm_title("Julia集合をtkinterのcanvasに動的に表示")

	canvas_frame = tkinter.Frame(root)                          	## frameを作る
	canvas_frame.pack()                                         	## packする
	canvas = tkinter.Canvas(canvas_frame,width=1001,height=1001)	## frameの中にcanvasを作る
	canvas.pack(expand = tkinter.YES, fill = tkinter.BOTH)      	## packして配置が決定

if __name__ == '__main__':
	gui_init()                              	## GUIの初期化
	cnt = 0
	C_org = complex(0.322,0.047)            	## 初期定数C
	draw_new_image()                        	## 初期表示   
	root.mainloop()                         	## メインループ(イベント待ち)

ということで、Pythonが48行、Cythonが44行という100行未満で動くフラクタル動画ができた。

Julia集合の動画(33MB、約6秒)

一部のブラウザでは動かないかも。その場合は、ブラウザを変更してみてください。