2*2行列の計算を可視化したい(matplotlibとtkinter練習)

# この記事について
線形写像とか固有値とかが、何をしているのかよく分からなかったので、
pythonの練習ついでに作ってみました。
matplotlibとnumpy行列演算とtkinterが融合してます。

ただの勉強の副産物です。需要？信憑性？…あっ…ないです…


# 結論
```math
\boldsymbol{r} = A\boldsymbol{v} \\
\\
\begin{pmatrix}
x_1 \\
y_1
\end{pmatrix}
 = 
\begin{pmatrix}
1 & 2 \\
3 & 4
\end{pmatrix}
\begin{pmatrix}
x_0 \\
y_0
\end{pmatrix}
```
こんな感じで作用してる真ん中の2*2の行列をAとしています。
x1とかの"r"eturnベクトルが変換後で、
x0とかの"v"ectorが変換前としたときに、

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647496/6e6ea2ce-826d-4b9f-f128-d12656bd9706.png" width="300x380">

これが、

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647496/500db4c3-34b7-0a6c-d177-a7754b76d94b.png" width="300x380">

こう。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647496/794dcc46-37a1-ae74-ffcd-412720cebe95.png" width="300x380">

ベクトルつけると、こう。


```math
\boldsymbol{r} = (t*A)\boldsymbol{v}
```
のようにt倍したものという式にして、tを下のバーで動かすと、

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647496/bbbdd227-aaaf-370c-5677-9fe2a65d8f16.gif" width="300x380">

こう。


こちらは単位行列の方が分かりやすい…？のでしょうか。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647496/0d60ac22-8b34-251f-741e-fbac21b5ebb0.gif" width="300x380">



とまあこのように、
変換前の(x0,y0)を格子状の点として用意して、
その子らがどんな感じで(x1,y1)に動くか（移動過程ではなく、それぞれの行き先）
を見たいというのが目的です。

移動過程も見たいのですが、どうしたらいいのかわかりませんでした。


# コード
コードはこんなのです。パパっと作っちゃったので、なっげぇです。
```python:すべて.py
import tkinter
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import numpy as np

# ベクトルも書きます？
vec_flag = True

### 関数 ###
# スライダーを移動したとき
def slider_scroll(
        value,
        x_min, x_max, x_n,
        y_min, y_max, y_n,
        A):
    t = float(value) # valueはstr()っぽいから治す
    x_data, y_data = create_point(x_min, x_max, x_n, y_min, y_max, y_n, t, A)

    if vec_flag == True:
        ax.cla()
        ax.quiver(o_x_data, o_y_data, x_data-o_x_data, y_data-o_y_data, color = "orange",
                angles = 'xy', scale_units = 'xy', scale = 1) # 始点x,始点y, 成分x, 成分y
        ax.plot(x_data, y_data, marker='o', linestyle="None") # https://beiznotes.org/matplotlib-plot-without-line/
        ax.grid()

    else:
        line.set_xdata(x_data)
        line.set_ydata(y_data)

    canvas.draw()


# 点を用意する関数(分かりやすい書き方なので最適化はお願いします)
def create_point(
        x_min, x_max, x_n,
        y_min, y_max, y_n,
        t, A):
    # arange用の分割幅を先に計算
    dx_width = (x_max - x_min)/(x_n)
    dy_width = (y_max - y_min)/(y_n)

    x_line = np.arange(x_min, x_max, dx_width) # 一列用意して
    x_data = np.tile(x_line, (y_n, 1)) # 並べる

    y_line = np.arange(y_min, y_max, dy_width) # 一列用意して
    y_line = y_line.reshape(y_n,1) # 行にして
    y_data = np.tile(y_line, (1, x_n)) # 並べる

    result_x = x_data.copy()
    result_y = y_data.copy()

    for x in range(x_n):
        for y in range(y_n):

            vector = np.array(
                    [[x],
                     [y]]
                    ) # ベクトル用意

            result = np.dot(t*A, vector) # 行列の積（pythonでは内積で書く）

            result_x[x,y] = result[0]
            result_y[x,y] = result[1]

    # 次元を1に矯正
    result_x = result_x.reshape(1, x_n*y_n)
    result_y = result_y.reshape(1, x_n*y_n)
    result_x = np.squeeze(result_x)
    result_y = np.squeeze(result_y)
    return result_x, result_y



if __name__ == "__main__":
    ### 変数 ###
    x_min = 0 # x_minから
    x_max = 5 # x_maxまで
    x_n = 5 # x_n個プロット

    y_min = 0 # y_minから
    y_max = 5 # y_maxまで
    y_n = 5 # y_n個プロット

    t = 1 # t倍して変化を見るための係数
    theta = 0 # 回転行列とか用の媒介変数

    A = np.array( # 表現行列
        [
            [1, 0],
            [0, 1]
        ]
    )

    # 単位行列
    E = np.array( # 表現行列
        [
            [1, 0],
            [0, 1]
        ]
    )
    #グラフデータ
    o_x_data, o_y_data = create_point(x_min, x_max, x_n, y_min, y_max, y_n, t, E)


    ### 表示本体 ###
    root = tkinter.Tk()
    root.title("matplotlib 埋め込み")

    #グラフ用オブジェクト生成

    fig = Figure(figsize=(5, 5))   #Figure
    ax = fig.add_subplot(1, 1, 1)           #Axes
    ax.set_xlim([x_min-1, x_max])
    ax.set_ylim([y_min-1, y_max])
    line, = ax.plot(o_x_data, o_y_data, marker='o', linestyle="None") # https://beiznotes.org/matplotlib-plot-without-line/
    ax.grid() # グリッド追加

    #Figureを埋め込み
    canvas = FigureCanvasTkAgg(fig, master=root)  # A tk.DrawingArea.
    canvas.get_tk_widget().pack()
    #ツールバーを表示
    toolbar=NavigationToolbar2Tk(canvas, root)

    ### スケールなどを用意　https://imagingsolution.net/program/python/tkinter/scale_trackbar/ ###
    # tを操作する用 
    scaleH = tkinter.Scale( root,
                    command = lambda value:slider_scroll(value, x_min, x_max, x_n, y_min, y_max, y_n, A),
                    orient=tkinter.HORIZONTAL,   # 配置の向き、水平(HORIZONTAL)、垂直(VERTICAL)
                    length = 300,           # 全体の長さ
                    width = 20,             # 全体の太さ
                    sliderlength = 20,      # スライダー（つまみ）の幅
                    from_ = -10,            # 最小値（開始の値）
                    to = 10,                # 最大値（終了の値）
                    resolution=0.1,         # 変化の分解能(初期値:1)
                    tickinterval=10         # 目盛りの分解能(初期値0で表示なし)
                    )
    scaleH.set(t)
    scaleH.pack()
    #---------------------------------------------------------------

    tkinter.mainloop()
```

# 参考
・tkinterとmatplotlibの合体！について：https://www.python-beginners.com/entry/20210720/1626710254
・グリッド追加　https://www.delftstack.com/ja/howto/matplotlib/how-to-show-pyplot-grid-in-matplotlib/
・スライドボタン追加　https://imagingsolution.net/program/python/tkinter/scale_trackbar/
など、後はわからなかったらその都度つまみ食いしました。ありがとうございました。

# コードの細かいとこ
すみません。書ききる気力が足りませんでした。
以下、処理のメモです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647496/ffe71575-8e2a-da77-d70a-a503230fa833.png)