Return to Maxima でコンピュータ演習

Maxima でパラパラアニメ作成:ffmpeg で mp4 編

斜方投射をパラパラアニメにする

運動方程式

水平方向を $x$, 鉛直上向きを $y$ として,運動方程式は重力加速度の大きさを $g$ として

\begin{eqnarray}
\frac{d^2 x}{dt^2} &=& 0 \\
\frac{d^2 y}{dt^2} &=& -g
\end{eqnarray}

解は

\begin{eqnarray}
x(t) &=& x_0 + v_{0x} t \\
y(t) &=& y_0 + v_{0y} t – \frac{1}{2} g t^2
\end{eqnarray}

初期条件を $x_0 = 0, y_0 = 0, v_{0x} = v_0 \cos\theta, v_{0y} = v_0 \sin\theta$ とすると

\begin{eqnarray}
x(t) &=& v_{0} \cos\theta \cdot t \\
y(t) &=& v_{0} \sin\theta \cdot t – \frac{1}{2} g t^2
\end{eqnarray}

無次元化

この系に特徴的な時間 $\displaystyle \tau \equiv \frac{v_0}{g}$ および長さ $\displaystyle \ell \equiv v_0 \tau = \frac{v_0^2}{g}$ で解を無次元化しておく。

\begin{eqnarray}
\bar{t} &\equiv& \frac{t}{\tau} \\
\bar{x} &\equiv& \frac{x}{\ell} = \cos\theta\cdot \bar{t} \\
\bar{y}&\equiv& \frac{y}{\ell} = \bar{h} + \sin\theta\cdot \bar{t} – \frac{1}{2} \bar{t}^2 \\
\bar{v}_x &\equiv& \frac{d\bar{x}}{d\bar{t}} = \cos\theta \\
\bar{v}_y &\equiv& \frac{d\bar{y}}{d\bar{t}} = \sin\theta – \bar{t}
\end{eqnarray}

以後しばらくは,無次元化された量であることを忘れないことにして,簡単のために $\bar{\ }$ を省略する。

\begin{eqnarray}
{x} &=& \cos\theta\cdot {t} \tag{1}\\
{y} &=&  \sin\theta\cdot {t} – \frac{1}{2} {t}^2 \tag{2}\\
{v}_x &=& \cos\theta \tag{3}\\
{v}_y &=& \sin\theta – {t}  \tag{4}
\end{eqnarray}

In [1]:
x(t, theta):= cos(theta) * t$
y(t, theta):= sin(theta) * t - t**2/2$

vx(t, theta):= cos(theta)$
vy(t, theta):= sin(theta) - t$

滞空時間と到達距離

$t = 0$ で投射して,ふたたび地面 $y = 0$ に落ちるまでの滞空時間 $T$ は $(2)$ 式から

$$ 0 = \sin\theta \cdot t – \frac{1}{2} t^2 $$

より,
$$T = 2 \sin \theta $$

この時間での水平到達距離 $L$ は $(1)$ 式から

\begin{eqnarray}
L &=& \cos\theta \cdot T \\
&=& 2 \sin\theta \cos\theta = \sin 2\theta
\end{eqnarray}

In [2]:
T(theta):= 2 * sin(theta)$

draw2d() でグラフを描く

Maxima でグラフを描く関数としては plot2d(), plot3d()plot 系と,draw2d() , draw3d()draw 系がある。ここでは,draw 系を利用してみる。

In [3]:
/* θ = π/4 の場合に,まずは軌道の全体をグラフにする。*/
draw2d(
  /* 表示範囲 */
  xrange = [-0.1, 1.1], yrange = [-0.1, 0.5],
  /* 縦横比 */
  proportional_axes=xy,
  /* グリッド。y 軸の目盛 */
  grid = true, ytics = 0.2, 
  xaxis = true, yaxis = true,
    
  /* 射法投射の軌道 */
  line_width = 1, 
  color = purple,
  parametric(x(t, %pi/4), y(t, %pi/4), t, 0, T(%pi/4))
  )$

Δt ごとのグラフを N 個作成

In [ ]:
/* パラパラアニメにするため,一定時間間隔ごとのグラフを作成し,*/
/* ファイルに保存する */

/* 分割数 */
N: 40$

for i:0 thru N do(
  /* %03d.png のような filename に */
  tmpname: printf(false, "~4d",1000+i),
  filename: substring(tmpname, 2), 
  
  dt: T(%pi/4)/N, 
  tend: dt * i, 
  
  draw2d(
    nticks=1000,
    /* 表示範囲 */
    xrange = [-0.1, 1.1], yrange = [-0.1, 0.5],
    /* 縦横比 */
    proportional_axes=xy,
    /* グリッド。y 軸の目盛 */
    grid = true, ytics = 0.2, 
    xaxis = true, yaxis = true,
    
    /* 斜方投射の軌道 */
    line_width = 1, 
    color = purple,
    parametric(x(t, %pi/4), y(t, %pi/4), t, 0, tend),
    
    /* 各 i ごとに png ファイルに保存する。*/ 
    file_name = filename, 
    dimensions = 60*[12.0,9.0],
    terminal = 'png 
  )
)$

ffmpeg でアニメにしてみる

In [ ]:
/* 古い out.mp4 は削除してから。*/
system("rm -f out.mp4")$

/* framerate を変えるとパラパラアニメの速さがかわる。*/
/* stream_loop はさらに何回繰り返すかを指定する。*/
system("ffmpeg  -stream_loop 2  -framerate 10    -hide_banner -loglevel error -i %03d.png -vcodec libx264 -pix_fmt yuv420p out.mp4")$

できた動画ファイル out.mp4 は JupyterHub のホームから,当該ファイルをクリックすることで確認できます。

In [ ]:
/* 不用になったファイルは削除しておく。disk full 回避のため。*/
system("rm -f ???.png")$
In [4]:
/* 粒子の位置に●を描く例。*/
/* 分割数 */
N: 4$

dt: T(%pi/4)/N$ 
/* 時刻 ti の位置,速度を描く */
ti: dt * 1$

draw2d(
  /* 表示範囲 */
  xrange = [-0.1, 1.1], yrange = [-0.1, 0.5],
  /* 縦横比 */
  proportional_axes=xy,
  /* グリッド。軸の目盛 */
  grid = true, xtics = 0.25, ytics = 0.25, 
  xaxis = true, yaxis = true,
 
  point_type = 7,
  color = red,
  points([[x(ti, %pi/4), y(ti, %pi/4)]])
  )$

ちなみに,point_type の種類は

none (-1)
dot (0)
plus (1)
multiply (2)
asterisk (3)
square (4) 
filled_square (5)
circle (6)
filled_circle (7)
up_triangle (8)
filled_up_triangle (9)
down_triangle (10) 
filled_down_triangle (11)
diamant (12)
filled_diamant (13)
In [5]:
/* 粒子の位置に速度ベクトルを描く例。*/
/* ベクトルの長さ調整用 */
scaling: 0.2$

/* 分割数 */
N: 4$

dt: T(%pi/4)/N$ 
/* 時刻 ti の位置,速度を描く */
ti: dt * 1$

draw2d(
  /* 表示範囲 */
  xrange = [-0.1, 1.1], yrange = [-0.1, 0.5],
  /* 縦横比 */
  proportional_axes=xy,
  /* グリッド。軸の目盛 */
  grid = true, xtics = 0.25, ytics = 0.25, 
  xaxis = true, yaxis = true,
 
  /* ベクトルの矢の設定 */
  head_length = 0.03,
  head_angle  = 20, 
  
  /* 速度ベクトルの x 成分 */
  color = blue, 
  key = "速度の x 成分",
  vector([x(ti, %pi/4), y(ti, %pi/4)], scaling*[vx(ti, %pi/4), 0]), 
  
  /* 速度ベクトルの y 成分 */
  color = red, 
  key = "速度の y 成分",
  vector([x(ti, %pi/4), y(ti, %pi/4)], scaling*[0, vy(ti, %pi/4)]), 
  
  /* 速度ベクトル */
  color = black, 
  key = "速度ベクトル",
  vector([x(ti, %pi/4), y(ti, %pi/4)], scaling*[vx(ti, %pi/4), vy(ti, %pi/4)]), 
  
  key = "", 
  point_type = 6,
  color = red,
  points([[x(ti, %pi/4), y(ti, %pi/4)]]), 
  
  /* Web ページ用に画像ファイル保存 */
  file_name = maxanim03, 
  terminal = 'svg 
  )$

In [ ]:
/* 粒子の位置に速度ベクトルを描く例。*/
/* ベクトルの長さ調整用 */
scaling: 0.2$

/* 分割数 */
N: 40$

dt: T(%pi/4)/N$ 
/* 時刻 ti の位置,速度を描く */
for i:0 thru N do(
  /* %03d.png のような filename に */
  tmpname: printf(false, "~4d",1000+i),
  filename: substring(tmpname, 2), 

ti: dt * i,

draw2d(font = "Arial", font_size = 18,
  /* 表示範囲 */
  xrange = [-0.2, 1.2], yrange = [-0.2, 0.5],
  /* 縦横比 */
  proportional_axes=xy,
  /* グリッド。軸の目盛 */
  grid = true, xtics = 0.25, ytics = 0.25, 
  xaxis = true, yaxis = true,
 
  /* ベクトルの矢の設定 */
  head_length = 0.03,
  head_angle  = 20, 
  
  /* 速度ベクトルの x 成分 */
  color = blue, 
  key = "速度の x 成分",
  vector([x(ti, %pi/4), y(ti, %pi/4)], scaling*[vx(ti, %pi/4), 0]), 
  
  /* 速度ベクトルの y 成分 */
  color = red, 
  key = "速度の y 成分",
  vector([x(ti, %pi/4), y(ti, %pi/4)], scaling*[0, vy(ti, %pi/4)]), 
  
  /* 速度ベクトル */
  color = black, 
  key = "速度ベクトル",
  vector([x(ti, %pi/4), y(ti, %pi/4)], scaling*[vx(ti, %pi/4), vy(ti, %pi/4)]), 

  /* 斜方投射の軌道 */
  line_width = 1, 
  color = purple,
  key = "", 
  parametric(x(t, %pi/4), y(t, %pi/4), t, 0, ti),

  key = "", 
  point_type = 6,
  color = red,
  point_size = 3, 
  points([[x(ti, %pi/4), y(ti, %pi/4)]]), 
  
  /* 各 i ごとに png ファイルに保存する。*/ 
  file_name = filename, 
  dimensions = 120*[12.0,9.0],
  terminal = 'png 
  )
)$
In [ ]:
/* 古い out.mp4 は削除してから。*/
system("rm -f out1.mp4")$

/* framerate を変えるとパラパラアニメの速さがかわる。*/
/* stream_loop はさらに何回繰り返すかを指定する。*/
system("ffmpeg   -stream_loop 2  -framerate 20         -hide_banner -loglevel error -i %03d.png -vcodec libx264 -pix_fmt yuv420p out1.mp4")$

円運動をパラパラアニメにする

円運動の媒介変数表示

\begin{eqnarray}
x &=& a \cos \frac{2\pi}{T} t = a \cos \omega t \\
y &=& a \sin \frac{2\pi}{T} t = a \sin \omega t
\end{eqnarray}

周期 $T$ を $N$ 等分し,
\begin{eqnarray}
\Delta t &\equiv& \frac{T}{N} \\
t_i &=& \Delta t \times i, \quad (i = 0, 1, \dots, N)
\end{eqnarray}

つまり
$$\omega t_i = \frac{2\pi}{N} \times i, \quad (i = 0, 1, \dots, N)$$
として,等しい時間間隔 $\displaystyle \Delta t$ ごとの位置 $x(\omega t_i), \ y(\omega t_i)$ をグラフにする。

In [7]:
/* 以前の定義を無しに */
kill(x, y)$

x(omegat):= a * cos(omegat);
y(omegat):= a * sin(omegat);
Out[7]:
\[\tag{${\it \%o}_{22}$}x\left({\it omegat}\right):=a\,\cos {\it omegat}\]
Out[7]:
\[\tag{${\it \%o}_{23}$}y\left({\it omegat}\right):=a\,\sin {\it omegat}\]

まずは円軌道と点を1個描く

In [8]:
/* 半径 */
a: 1$

/* 分割数 */
N: 36$

/* t = 0 での位置 */
i: 0$
omegat: 2*%pi/N * i$

draw2d(
    /* 表示範囲 */
    xrange = [-a-0.5, a+0.5], yrange = [-a-0.5, a+0.5],
    /* 縦横比 */
    proportional_axes = xy,
    /* x 軸 y 軸 */
    xaxis = true, yaxis = true, 
    xtics = false, ytics = false, 
    /* 外枠を消す */
    axis_top = false, axis_bottom = false, 
    axis_left = false, axis_right = false, 
    /* 滑らかに曲線を描くように */
    nticks = 200,

    /* 円軌道 */
    color = blue,
    parametric(x(t), y(t), t, 0, 2*%pi),
  
    /* 点の位置 */
    point_type = 7,
    color = red,
    points([[x(omegat), y(omegat)]]), 
  
  /* Web ページ用に画像ファイル保存 */
  file_name = maxanim04, 
  terminal = 'svg 
)$

 

 

 

シーン1: Δt ごとの点の位置を N 個描く

In [ ]:
/* 不用になったファイルは削除しておく。disk full 回避のため。*/
system("rm -f ???.png")$
In [ ]:
/* 半径 */
a: 1$

/* 分割数 */
N: 18$

/* t = 0 から t = T まで Δt ごとの位置 */
for i:0 thru N do(
  /* アニメ作成用のファイルファイル名 */
  tmpname: printf(false, "~4d",1000+i),
  filename: substring(tmpname, 2), 

  omegat: 2*%pi/N * i,

  draw2d(
    /* 表示範囲 */
    xrange = [-a-0.5, a+0.5], yrange = [-a-0.5, a+0.5],
    /* 縦横比 */
    proportional_axes = xy,
    /* x 軸 y 軸 */
    xaxis = true, yaxis = true, 
    xtics = false, ytics = false, 
    /* 外枠を消す */
    axis_top = false, axis_bottom = false, 
    axis_left = false, axis_right = false, 
    /* 滑らかに曲線を描くように */
    nticks = 1000,

    /* 円軌道 */
    color = blue,
    parametric(x(t), y(t), t, 0, 2*%pi),
  
    /* 点の位置 */
    point_type = 7,
    point_size = 3,
    color = red,
    points([[x(omegat), y(omegat)]]), 

    /* 各 i ごとに png ファイルに保存する。*/ 
    file_name = filename, 
    dimensions = 120*[12.0,9.0],
    terminal = 'png 
  )
)$

ここまでをアニメにしてみる

In [ ]:
/* 古い out.mp4 は削除してから。*/
system("rm -f out2.mp4")$

/* framerate を変えるとパラパラアニメの速さがかわる。*/
/* stream_loop はさらに何回繰り返すかを指定する。*/
system("ffmpeg  -stream_loop 0   -framerate 6      -hide_banner -loglevel error -i %03d.png -vcodec libx264 -pix_fmt yuv420p out2.mp4")$

シーン2:過去の点の位置を消さずに複数個の点を描く

In [ ]:
/* 半径 */
a: 1$

/* 分割数 */
N: 18$

/* t = 0 から t = T まで Δt ごとの位置 */
for i:1 thru N do(
  /* アニメ作成用のファイルファイル名 */
  /* シーン1のファイルの次から連番に */
  tmpname: printf(false, "~4d",1000+i+N),
  filename: substring(tmpname, 2), 

  pxy: makelist(points([[x(2*%pi/N * j), y(2*%pi/N * j)]]), j, 0, i),
  
  draw2d(
    /* 表示範囲 */
    xrange = [-a-0.5, a+0.5], yrange = [-a-0.5, a+0.5],
    /* 縦横比 */
    proportional_axes = xy,
    /* x 軸 y 軸 */
    xaxis = true, yaxis = true, 
    xtics = false, ytics = false, 
    /* 外枠を消す */
    axis_top = false, axis_bottom = false, 
    axis_left = false, axis_right = false, 
    /* 滑らかに曲線を描くように */
    nticks = 1000,

    /* 円軌道 */
    color = blue,
    parametric(x(t), y(t), t, 0, 2*%pi),
  
    /* 点の位置 */
    point_type = 7,
    point_size = 3,
    color = red,
    pxy, 

    /* 各 i ごとに png ファイルに保存する。*/ 
    file_name = filename, 
    dimensions = 120*[12.0,9.0],
    terminal = 'png 
  )
)$

ここまでをアニメにしてみる

In [ ]:
/* 古い out.mp4 は削除してから。*/
system("rm -f out3.mp4")$

/* framerate を変えるとパラパラアニメの速さがかわる。*/
/* stream_loop はさらに何回繰り返すかを指定する。*/
system("ffmpeg  -stream_loop 0   -framerate 6      -hide_banner -loglevel error -i %03d.png -vcodec libx264 -pix_fmt yuv420p out3.mp4")$

シーン3:Δt の間に掃いた部分の扇形を描く

In [ ]:
/* 半径 */
a: 1$

/* 分割数 */
N: 18$

/* t = 0 から t = T まで Δt ごとの位置 */
for i:1 thru N do(
  /* アニメ作成用のファイルファイル名 */
  /* シーン2のファイルの次から連番に */
  tmpname: printf(false, "~4d",1000+i+2*N),
  filename: substring(tmpname, 2), 

  pxy: makelist(points([[x(2*%pi/N * j), y(2*%pi/N * j)]]), j, 0, N),
  
  draw2d(
    /* 表示範囲 */
    xrange = [-a-0.5, a+0.5], yrange = [-a-0.5, a+0.5],
    /* 縦横比 */
    proportional_axes = xy,
    /* x 軸 y 軸 */
    xaxis = true, yaxis = true, 
    xtics = false, ytics = false, 
    /* 外枠を消す */
    axis_top = false, axis_bottom = false, 
    axis_left = false, axis_right = false, 
    /* 滑らかに曲線を描くように */
    nticks = 1000,

    /* 円軌道 */
    color = blue,
    parametric(x(t), y(t), t, 0, 2*%pi),
    
    /* 扇形の直線 */
    explicit(tan(2*%pi/N * i)*x, x, 
             min(0, cos(2*%pi/N * i)), 
             max(0, cos(2*%pi/N * i))),
    explicit(tan(2*%pi/N * (i-1))*x, x, 
             min(0, cos(2*%pi/N * (i-1))), 
             max(0, cos(2*%pi/N * (i-1)))),
  
    /* 点の位置 */
    point_type = 7,
    point_size = 3,
    color = red,
    pxy, 

    /* 各 i ごとに png ファイルに保存する。*/ 
    file_name = filename, 
    dimensions = 120*[12.0,9.0],
    terminal = 'png 
  )
)$

ここまでをアニメにしてみる

In [ ]:
/* 古い out.mp4 は削除してから。*/
system("rm -f out4.mp4")$

/* framerate を変えるとパラパラアニメの速さがかわる。*/
/* stream_loop はさらに何回繰り返すかを指定する。*/
system("ffmpeg  -stream_loop 2   -framerate 6      -hide_banner -loglevel error -i %03d.png -vcodec libx264 -pix_fmt yuv420p out4.mp4")$

最終的には,楕円軌道の場合に以下のような動画を作ることを目標に。