Pythonで動く点Pを可視化する

## やったこと
Pythonで動く点Pを描画し、gifとして出力しました。

![ugokutenP02.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253018/f635aeae-8d95-be81-bc74-e411c5416d92.gif)

## 例題
以下のような動く点Pの問題を可視化してみます。

>AB=4cm, BC=6cmの長方形ABCDがあり、点PはAを出発して毎秒1cmでA→B→C→Dと進む。 出発から$x$秒後の△APDの面積を$y$ cm<sup>2</sup>とする。

出典
https://math.005net.com/yoten/doten.php
（ただし、毎秒2cmを毎秒1cmとしています。）


## 描画

### 環境
* Anaconda3 + jupyter notebook
* Python 3.6
* matplotlib 3.1.1
* numpy 1.16.5

### 1.空っぽの図をつくる

まず、図形を描画するための下準備をします。

```python
import matplotlib.pyplot as plt

fig = plt.figure()
ax1 = fig.add_subplot(111)

plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253018/95869dcf-b559-3da8-99d2-c13d6946c880.png)

### 2.長方形を表示する

縦4cm、横6cmの長方形を作ります。ここで、座標原点$O$をどこにおくかにはいろいろな選び方があります。今回は長方形の重心（縦2cm、横3cmのところ）を原点$O$に選び、点A,B,C,Dの座標を決めます。こうしておくと、あとで長方形の一回り外側にラベルを表示させたいときにコードが簡単になります。
![1576855216649.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253018/8774c884-2394-d4bf-e9d2-6a55b8b0317f.jpeg)

* 図形を描画するために`matplotlib.patches`を用います。また点の座標をベクトルとして扱いたいので`numpy`も入れます。

* 長方形が枠に収まるように`set_xlim`,`set_ylim`でx軸、y軸の範囲を長方形よりやや大きめにしておきます。
* 点A,B,C,Dの座標を`np.array`型で定義します。
* `pat.Polygon`で長方形を作り、`ax1`に追加します。

```python

import numpy as np #追加
import matplotlib.pyplot as plt
import matplotlib.patches as pat #追加

fig = plt.figure()

ax1 = fig.add_subplot(111)

ax1.set_xlim(-4,4)
ax1.set_ylim(-3,3)

A=np.array([-3,2])
B=np.array([-3,-2])
C=np.array([3,-2])
D=np.array([3,2])

p = pat.Polygon(xy = [A,B,C,D],
                edgecolor='black',
                facecolor='white',
                linewidth=1.6)

ax1.add_patch(p)

plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253018/ed8258ad-bf2b-6dfb-e1d2-09f5263149a2.png)

### 3.辺の長さ・頂点の名前を表示する

辺の長さを描画します
`text`は第一引数にx座標、第二引数にy座標、第三引数に名前を指定することで文字を描画できます。

```python
#辺の長さを表示する
ax1.text(-3.5,0.0,"4cm",horizontalalignment='center',verticalalignment='center')
ax1.text(0.0,-2.5,"6cm",horizontalalignment='center',verticalalignment='center')
```

配置は好みに合わせて微調整してください。

つづいて、点の名前を長方形の一回り外側に表示します。辺の長さと同様に手打ちで表示位置を決めてもよいです。

```python
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as pat

fig = plt.figure()
ax1 = fig.add_subplot(111)

ax1.set_xlim(-4,4)
ax1.set_ylim(-3,3)

A=np.array([-3,2])
B=np.array([-3,-2])
C=np.array([3,-2])
D=np.array([3,2])

pol = pat.Polygon(xy = [A,B,C,D],
                edgecolor='black',
                facecolor='white',
                linewidth=1.6)

ax1.add_patch(pol)

#辺の長さを表示させる
ax1.text(-3.5,0.0,"4cm",horizontalalignment='center',verticalalignment='center')
ax1.text(0.0,-2.5,"6cm",horizontalalignment='center',verticalalignment='center')

#頂点の名前を表示させる
scale=1.1
ax1.text(A[0]*scale,A[1]*scale,"A",fontsize=15,horizontalalignment='center',verticalalignment='center')
ax1.text(B[0]*scale,B[1]*scale,"B",fontsize=15,horizontalalignment='center',verticalalignment='center')
ax1.text(C[0]*scale,C[1]*scale,"C",fontsize=15,horizontalalignment='center',verticalalignment='center')
ax1.text(D[0]*scale,D[1]*scale,"D",fontsize=15,horizontalalignment='center',verticalalignment='center')

plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253018/446e7163-a319-c3c3-5c78-9a74b9642249.png)

### 4.点Pを表示する
いよいよ主役の登場です。
点Pの座標を定義し、`ax1.plot()`で点を表示します。点Pの名前の表示のさせ方は点A,B,C,Dと同じです。

```python
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as pat

fig = plt.figure()

ax1 = fig.add_subplot(111)

ax1.set_xlim(-4,4)
ax1.set_ylim(-3,3)

A=np.array([-3,2])
B=np.array([-3,-2])
C=np.array([3,-2])
D=np.array([3,2])

pol = pat.Polygon(xy = [A,B,C,D],
                edgecolor='black',
                facecolor='white',
                linewidth=1.6)

ax1.add_patch(pol)

ax1.text(-3.5,0.0,"4cm",horizontalalignment='center',verticalalignment='center')
ax1.text(0.0,-2.5,"6cm",horizontalalignment='center',verticalalignment='center')

scale=1.1
ax1.text(A[0]*scale,A[1]*scale,"A",fontsize=15,horizontalalignment='center',verticalalignment='center')
ax1.text(B[0]*scale,B[1]*scale,"B",fontsize=15,horizontalalignment='center',verticalalignment='center')
ax1.text(C[0]*scale,C[1]*scale,"C",fontsize=15,horizontalalignment='center',verticalalignment='center')
ax1.text(D[0]*scale,D[1]*scale,"D",fontsize=15,horizontalalignment='center',verticalalignment='center')

P=np.array([-3.0,1.0])

#動く点Pを表示する
scale_P=1.2
ax1.plot(P[0],P[1],marker='o',color='black')
ax1.text(P[0]*scale_P,P[1]*scale_P,"P",fontsize=15,horizontalalignment='center',verticalalignment='center')

plt.show()
```

### 5.△APDを表示する

長方形ABCDをつくったときと同様に三角形APDをつくり図に追加します、ついでにx軸とy軸を消しておきます。

```python
%matplotlib nbagg
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as pat

fig = plt.figure()

ax1 = fig.add_subplot(111)

ax1.set_xlim(-4,4)
ax1.set_ylim(-3,3)

A=np.array([-3,2])
B=np.array([-3,-2])
C=np.array([3,-2])
D=np.array([3,2])

pol = pat.Polygon(xy = [A,B,C,D],
                edgecolor='black',
                facecolor='white',
                linewidth=1.6)

ax1.add_patch(pol)

ax1.text(-3.5,0.0,"4cm",horizontalalignment='center',verticalalignment='center')
ax1.text(0.0,-2.5,"6cm",horizontalalignment='center',verticalalignment='center')

scale=1.1
ax1.text(A[0]*scale,A[1]*scale,"A",fontsize=15,horizontalalignment='center',verticalalignment='center')
ax1.text(B[0]*scale,B[1]*scale,"B",fontsize=15,horizontalalignment='center',verticalalignment='center')
ax1.text(C[0]*scale,C[1]*scale,"C",fontsize=15,horizontalalignment='center',verticalalignment='center')
ax1.text(D[0]*scale,D[1]*scale,"D",fontsize=15,horizontalalignment='center',verticalalignment='center')

P=np.array([-3.0,1.0])

scale_P=1.2
ax1.plot(P[0],P[1],marker='o',color='black')
ax1.text(P[0]*scale_P,P[1]*scale_P,"P",fontsize=15,horizontalalignment='center',verticalalignment='center')

#△APDを追加
S = pat.Polygon(xy = [A,P,D],
                    edgecolor='black',
                    facecolor='lightgray',
                    linewidth=1.6)

ax1.add_patch(S)

#枠を消す
plt.axis('off')

plt.show()
```

### 6.動かす

動く点Pを動かし、それをgif形式のアニメーションで出力します。
$x$秒後の動く点Pの座標を毎回更新し、コマ送りで描画することによってアニメーションをつくります。

* あらかじめ長方形ABCDの描画など時間が経過しても変わらない操作を`initialize()`にまとめておきます。
* `moveP(x)`は経過時間から動く点Pが線分AB、BC、CDのどこにいるか場合分けを行い、動く点Pの座標を返します。
* 動く点Pの速度(`velocity`)は秒速1cmとし、描画間隔(`timestep`)は0.1秒とします。
* アニメーション作成には`PillowWriter`と`FuncAnimation`を使います。
`animate(i)`に一回の描画更新で行う操作をすべてまとめておき、`FuncAnimation`の引数にとることでアニメーションをつくります。
* `anim.save()`で動画の出力ができます。

```python
%matplotlib nbagg #jupyter notebook上でアニメーション表示させるために必要
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as pat
from matplotlib.animation import PillowWriter,FuncAnimation #動画作成用に追加

fig = plt.figure()
ax1 = fig.add_subplot(111)

A=np.array([-3,2])
B=np.array([-3,-2])
C=np.array([3,-2])
D=np.array([3,2])

scale=1.1
scaleP=1.2

p = pat.Polygon(xy = [A,B,C,D],
                edgecolor='black',
                facecolor='white',
                linewidth=1.6)

def initialize():
    ax1.set_xlim(-4,4)
    ax1.set_ylim(-3,3)
    ax1.add_patch(p)


    ax1.text(A[0]*scale,A[1]*scale,"A",fontsize=15,horizontalalignment='center',verticalalignment='center')
    ax1.text(B[0]*scale,B[1]*scale,"B",fontsize=15,horizontalalignment='center',verticalalignment='center')
    ax1.text(C[0]*scale,C[1]*scale,"C",fontsize=15,horizontalalignment='center',verticalalignment='center')
    ax1.text(D[0]*scale,D[1]*scale,"D",fontsize=15,horizontalalignment='center',verticalalignment='center')

    ax1.text(-3.5,0.0,"4cm",horizontalalignment='center',verticalalignment='center')
    ax1.text(0.0,-2.5,"6cm",horizontalalignment='center',verticalalignment='center')

   
def moveP(x):
    if 0<= x <4:
        return A+np.array([0,-1])*x*velocity
    elif 4<=x <10:
        return B+np.array([1,0])*(x-4)*velocity
    elif 10<= x <14:
        return C+np.array([0,1])*(x-10)*velocity
    else:
        return D
        
velocity=1.0
timestep=0.1

def animate(t):
    plt.cla()
    initialize()
    
    x=timestep*t
    P=moveP(x)
    
    ax1.plot(P[0],P[1],marker='o',color='black')
    ax1.text(P[0]*scaleP,P[1]*scaleP,"P",fontsize=15,horizontalalignment='center',verticalalignment='center')
    
    S = pat.Polygon(xy = [A,P,D],
                    edgecolor='black',
                    facecolor='lightgray',
                    linewidth=1.6)

    ax1.add_patch(S)
    
    plt.axis('off')
    plt.title('x=' + '{:.1f}'.format(x)+'sec')
    
anim = FuncAnimation(fig,animate,frames=140,repeat=True,interval=timestep*1000)
#anim.save("ugokutenP.gif", writer='pillow',fps=10)
plt.show()
```

![ugokutenP02.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253018/f635aeae-8d95-be81-bc74-e411c5416d92.gif)