やったこと
Pythonで動く点Pを描画し、gifとして出力しました。
例題
以下のような動く点Pの問題を可視化してみます。
AB=4cm, BC=6cmの長方形ABCDがあり、点PはAを出発して毎秒1cmでA→B→C→Dと進む。 出発から$x$秒後の△APDの面積を$y$ cm2とする。
出典
https://math.005net.com/yoten/doten.php
(ただし、毎秒2cmを毎秒1cmとしています。)
描画
環境
- Anaconda3 + jupyter notebook
- Python 3.6
- matplotlib 3.1.1
- numpy 1.16.5
1.空っぽの図をつくる
まず、図形を描画するための下準備をします。
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot(111)
plt.show()
2.長方形を表示する
縦4cm、横6cmの長方形を作ります。ここで、座標原点$O$をどこにおくかにはいろいろな選び方があります。今回は長方形の重心(縦2cm、横3cmのところ)を原点$O$に選び、点A,B,C,Dの座標を決めます。こうしておくと、あとで長方形の一回り外側にラベルを表示させたいときにコードが簡単になります。
-
図形を描画するために
matplotlib.patches
を用います。また点の座標をベクトルとして扱いたいのでnumpy
も入れます。 -
長方形が枠に収まるように
set_xlim
,set_ylim
でx軸、y軸の範囲を長方形よりやや大きめにしておきます。 -
点A,B,C,Dの座標を
np.array
型で定義します。 -
pat.Polygon
で長方形を作り、ax1
に追加します。
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()
3.辺の長さ・頂点の名前を表示する
辺の長さを描画します
text
は第一引数にx座標、第二引数にy座標、第三引数に名前を指定することで文字を描画できます。
#辺の長さを表示する
ax1.text(-3.5,0.0,"4cm",horizontalalignment='center',verticalalignment='center')
ax1.text(0.0,-2.5,"6cm",horizontalalignment='center',verticalalignment='center')
配置は好みに合わせて微調整してください。
つづいて、点の名前を長方形の一回り外側に表示します。辺の長さと同様に手打ちで表示位置を決めてもよいです。
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()
4.点Pを表示する
いよいよ主役の登場です。
点Pの座標を定義し、ax1.plot()
で点を表示します。点Pの名前の表示のさせ方は点A,B,C,Dと同じです。
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軸を消しておきます。
%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()
で動画の出力ができます。
%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()