LoginSignup
5
4

More than 5 years have passed since last update.

(続き)Bokeh をつかって行列演算を可視化する(with animation)

Last updated at Posted at 2017-11-14

本日は

先日の Bokeh をつかって行列演算を可視化する に引き続いて Bokeh の練習として回転行列

R(\theta)=\begin{bmatrix}
\cos\theta& -\sin\theta\\
\sin\theta& \cos\theta
\end{bmatrix}

のパラメータ $\theta$ を変えた時に像がどのように変化するのを見ていきましょう.
とはいえ,回転行列というぐらいですからぐるぐる回りますよね.
当たり前かもしれませんが,実際に実装して確かめて見ましょう.

実装例

ベタがきで書いています.
本当はクラス化した方が再利用性の観点から良いのですが,それは読者の演習問題とします.よくあるパターンですね.

anim_rot.py

"""
Reference:
https://github.com/bokeh/bokeh/blob/master/examples/plotting/server/animated.py
"""

import numpy as np
from bokeh.plotting import figure, output_file, show, curdoc
from bokeh.models import ColumnDataSource, Div
from bokeh.layouts import gridplot, column
from bokeh.client import push_session

init_degree = 30


def rot_mat(degree):
    theta = np.deg2rad(degree)
    mat = np.array([[np.cos(theta), -np.sin(theta)],
                    [np.sin(theta), np.cos(theta)]])
    return mat


def main():
    xs = np.linspace(-np.pi, np.pi, 11)
    ys = xs
    Xs, Ys = np.meshgrid(xs, ys)
    Xs, Ys = Xs.flatten(), Ys.flatten()

    mat = rot_mat(init_degree)

    transXs, transYs = mat @ np.array([Xs, Ys])

    output_file("lasso_selector.html")
    TOOLS = "pan,lasso_select,save,reset"
    source = ColumnDataSource(data=dict(Xs=Xs,
                                        Ys=Ys,
                                        transXs=transXs,
                                        transYs=transYs))

    f = figure(tools=TOOLS, title="target",
               x_range=(-np.pi*np.sqrt(2)-1, np.pi*np.sqrt(2)+1),
               y_range=(-np.pi*np.sqrt(2)-1, np.pi*np.sqrt(2)+1))
    f.circle('Xs', 'Ys', source=source)

    transf = figure(x_range=f.x_range,
                    y_range=f.y_range,
                    tools=TOOLS,
                    title="trans")
    transf.circle('transXs', 'transYs', source=source, size=6)

    degree = init_degree
    def update():
        nonlocal degree
        degree += 1
        new_mat = rot_mat(degree)
        transXs, transYs = new_mat @ np.array([Xs, Ys])
        source.data = dict(Xs=Xs,
                           Ys=Ys,
                           transXs=transXs,
                           transYs=transYs)
        transf.title.text = "theta={}".format(degree % 360)

    grid = gridplot([[f, transf]])
    plot = column(Div(text="<h2>Transform before and after</h2>"),
                  grid)
    document = curdoc()
    document.add_root(plot)
    document.add_periodic_callback(update, 60)


main()

add_periodic_callbackupdate 関数を突っ込むことで周期的に update を呼び出して $\theta$ を1度ずつずらしたときの像を描画します.

ここで注意なのは

if __name__ == '__main__':
      main() 

のようにせず, そのまま main() を書いておくことです.そうしておかないと実行しても空のページが出力されてしまいます. Referenceとして挙げている方法で session を用いるのも可能なのですが,なぜか起動に時間がかかるのでそのようにはしていません.

実行例

動かして見ましょう.bokeh パッケージをインストールすると bokeh コマンドが使えます.

$ bokeh serve --show anim_rot.py

そうするとブラウザが立ち上がり下画面のように動くはずです.
GIFでキャプチャーするツールで動画化しています.

animrot.gif

想像通り回転している様子がわかります.そして思っていたよりもカクカクせずに動いてくれますね.

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4