Octave で遊んでみた (sin 関数)

# octave で三角関数のグラフを描いて、ImageMagik でアニメーションしてみる

Windows の octave 5.1.0 で遊んでみた。
その昔「三角関数って、こうやって説明したら良いのにこういうの無いよね」って書いてた blog を思い出したので作ってみた。 (その blog でも gif を作ってました)

## octave で画像を描く
```perl:octave&nbsp;が無いので、言語は&nbsp;perl&nbsp;指定
clear		# 変数クリア
cd "Z:\\Temp"	# 画像の保存先

max=4*pi	# グラフの終わり [rad]
div=100		# 分割数
theta_a=linspace(0,pi*2,36);

hax = newplot();
h=gcf();

for i=0:div
	theta_b = max .* i / div # 現在の角度
	hold off	# 一度全部消す

	# 左の円と、右の sin 関数を描く (動かない部分)
	plot(cos(theta_a) - 1, sin(theta_a), "-b", theta_a, sin(theta_a), "-r", "linewidth", .5)

	# この後は全部上書き (以下は動く部分)
	hold on

	now_x=cos(theta_b);
	now_y=sin(theta_b);
	# 左の円周を回る点
	# 中心から円周へ線を引く
	plot([0-1, now_x-1], [0, now_y], "-g", "linewidth", 2)

	# 右の sin の点
	plot(theta_b, now_y, "or", "MarkerSize", 3)

	# 円周を回る点から、右の sin のグラフへ線を引く
	plot([now_x-1, theta_b], [now_y, now_y], "g", "linewidth", 2)

	# 軸の書式設定
	set(gca, 'box', "off", 'Xaxislocation', "origin", 'Yaxislocation', "origin")
	set(gca, 'xtick', [0 pi/2 2*pi/2 3*pi/2 4*pi/2  5*pi/2 6*pi/2 7*pi/2 8*pi/2])
	xticklabels(hax,{'_0','\pi/2','\pi','3\pi/2','2\pi','5\pi/2','3\pi','7\pi/2','4\pi'})
	axis("equal",[-2 pi*4 -1 1])

	# 保存
	saveas (h, sprintf('temp_%03d.png', i));
endfor
```
## ImageMagik で画像を繋げて動画にする
[ImageMagik](http://www.imagemagick.org/script/download.php) から ImageMagick-7.0.8-68-portable-Q16-x86.zip 辺りを拾ってくる。中の convert を取り出してコマンドプロンプトでトリミング & アニメーションへ変換。
※事前に gimp 等でトリミング範囲を確認しておく

```cmd:
> convert -crop 989x183+145+343 +repage -delay 3 -layers Optimize Z:\Temp\temp_???.png Z:\Temp\anime_opt.gif
```
出力はこんな感じ
![anime_opt.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306041/9c9e87ad-0766-d90d-5b13-63ecc7a41dc8.gif)

101枚合成して 115kB に収まるって小さいですね。必要の無い部分をトリミングしたり、うまいこと圧縮してるんですね。
ループは 0～100 でなくて、1～100 にすれば良かったかな。最後 $4\pi$ までやりたかったので、気付きにくい 0 は要らなかったかも。
div=360 にして、convert で -delay 1 にすれば同程度の速度で 2度づつ描きます。(ブラウザはそんなに早く更新できないので今でも無駄ですが)
sin のグラフが一周期で終わってるのはわざとです。$2\pi$ 以降のグラフが思い描けますよね？ということで。

画像を保存するところ、

```perl:
	# 保存
	print "-S989,193" -dpng temp.png
	rename ("temp.png", sprintf('temp_%03d.png', i));
```
みたいにするとグラフ自体が小さくなってしまい悲しい。
本当は eps とかで保存して、適当に拡大縮小をして使うのが良いのかも。そうすると ghostview も入れないといけなくなるけど。

## sin のグラフが伸びていくほうが自然かな
sin を書いていくような動画にしてみた。

```perl:
clear       # 変数クリア
cd 'Z:/Temp'

max=4*pi    # グラフの終わり [rad]
div=360     # 分割数
theta_a=linspace(0,pi*2,36); # 動かない部分用
                             # theta_b は動く部分用
hax=newplot();
h=gcf();

for i=1:div
    theta_b = max .* i / div # 現在の角度
    hold off    # 一度全部消す

    # 左の円を描く (動かない部分)
    plot(cos(theta_a) - 1, sin(theta_a), "-b", "linewidth", .5)

    # ここからは全部上書き (以下は動く部分)
    hold on
    # 右の sin 関数を描く
    fplot (@sin, [0, theta_b], "-r", "linewidth", .5)

    now_x=cos(theta_b);
    now_y=sin(theta_b);
    # 左の円周を回る点
    # 中心から円周へ線を引く
    plot([0-1, now_x-1], [0, now_y], "-g", "linewidth", 2)

    # 右の sin の点
    plot(theta_b, now_y, "or", "MarkerSize", 3)

    # 円周を回る点から、右の sin のグラフへ線を引く
    plot([now_x-1, theta_b], [now_y, now_y], "g", "linewidth", 2)

    # 軸の書式設定
    grid on
    set(gca, 'box', "off", 'Xaxislocation', "origin", 'Yaxislocation', "origin")
    set(gca, 'xtick', [0 pi/2 2*pi/2 3*pi/2 4*pi/2  5*pi/2 6*pi/2 7*pi/2 8*pi/2])
    xticklabels(hax,{'0','\pi/2','\pi','3\pi/2','2\pi','5\pi/2','3\pi','7\pi/2','4\pi'})
    axis("equal",[-2.2 pi*4+0.2 -1.2 1.2])
    xlabel(' \Theta [rad]')
    legend({'unit circle',' sin(\Theta)'})
    # 画面を更新する
    refresh
    print -depsc temp.eps
    rename ("temp.eps", sprintf('temp_%03d.eps', i));
endfor
```
xlabel が X 軸からかけ離れたところにあるって、なかなか謎仕様ですね。離れてるのでアニメーション化の時に切り落とされましたとさ。

```cmd:
> ImageMagick-7.0.8-Q16-HDRI\magick.exe convert -density 200x200 -crop 1307x279+175+439 +repage -delay 3 temp_???.eps anime.gif
> ImageMagick-7.0.8-Q16-HDRI\magick.exe convert -layers Optimize anime.gif anime_opt.gif
> ImageMagick-7.0.8-Q16-HDRI\magick.exe convert -layers OptimizeFrame anime.gif anime_optFrame.gif

>dir *.gif
...
2019/10/15  11:16         5,860,507 anime.gif
2019/10/15  11:18         1,885,793 anime_opt.gif
2019/10/15  11:19         4,900,334 anime_optFrame.gif
```
anime.gif: すべての画像をそのまま繋げたもの。
anime_optFrame.gif: anime.gif から変更ある部分 (長方形の領域) のみを切り取って繋げたもの
anime_opt.gif: anime_optFrame.gif の長方形の領域の中でも変化のないドットを透明にしたもの。

![anime_opt.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306041/4d4d8452-05ea-6afd-315d-a72c0b057a8f.gif)
IrfanView で拡大したりするとゆらゆらするけど、ブラウザで見ると大丈夫ですね。

# スクロールさせてみた
```perl:
clear       # 変数クリア
cd 'I:/Users/All Users/Temp'

max=2*pi    # グラフの終わり [rad]
div=90      # 分割数
theta_a=linspace(0,pi*2,36); # 動かない部分用
                             # theta_b は動く部分用
hax=newplot();
h=gcf();

for i=1:div
    theta_b = max .* i / div   # 現在の角度
    theta_start = theta_b - pi # グラフ開始の角度
    hold off    # 一度全部消す

    # 左の円を描く (動かない部分)
    plot(cos(theta_a) + theta_start - 1, sin(theta_a), "-b", "linewidth", .5)
    hold on
    # Y軸っぽいの
    plot([theta_start theta_start], [-1.2 1.2],'k')
    
    # 以下は動く部分
    # 右の sin 関数を描く
    fplot (@sin, [theta_start, theta_b], "-r", "linewidth", .5)

    now_x=cos(theta_b);
    now_y=sin(theta_b);
    # 左の円周を回る点
    # 中心から円周へ線を引く
    plot([theta_start-1, theta_start+now_x-1], [0, now_y], "-g", "linewidth", 2)

    # 右の sin の点
    plot(theta_b, now_y, "or", "MarkerSize", 3)

    # 円周を回る点から、右の sin のグラフへ線を引く
    plot([theta_start+now_x-1, theta_b], [now_y, now_y], "g", "linewidth", 2)

    # 軸の書式設定
    grid on
    set(gca, 'box', "off", 'Xaxislocation', "origin", 'Yaxislocation', "right")
    if ( theta_start < -pi/2 )
      set(gca, 'xtick', [-pi/2 0 pi/2 2*pi/2 3*pi/2 4*pi/2])
      xticklabels(hax,{'3\pi/2','0','\pi/2','\pi', '3\pi/2'})
    elseif( theta_start < 0 )
      set(gca, 'xtick', [0 pi/2 2*pi/2 3*pi/2 4*pi/2  5*pi/2])
      xticklabels(hax,{'0','\pi/2','\pi','3\pi/2','2\pi','\pi/2'})
    elseif( theta_start < pi/2 )
      set(gca, 'xtick', [pi/2 2*pi/2 3*pi/2 4*pi/2 5*pi/2 3*pi])
      xticklabels(hax,{'\pi/2','\pi','3\pi/2','2\pi','\pi/2','\pi'})
    else
      set(gca, 'xtick', [2*pi/2 3*pi/2 4*pi/2 5*pi/2 3*pi])
      xticklabels(hax,{'\pi','3\pi/2','2\pi','\pi/2','\pi'})
    endif      
    axis("equal",[theta_start-2.2 theta_start+max+0.2 -1.2 1.2])
    xlabel(' \Theta [rad]')
    legend({'unit circle',' sin(\Theta)'})
    # 画面を更新する
    refresh
    print -depsc temp.eps
    rename ("temp.eps", sprintf('temp_%03d.eps', i));
endfor
```
axis で、表示する範囲を移動させ、左の円も同じように移動させてみた

```cmd:
ImageMagick-7.0.8-Q16-HDRI\magick.exe" convert -density 200x200 -crop 1300x344+216+407 +repage -layers Optimize -delay 3 temp_???.eps anime.gif
```
![anime.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306041/ed24bac7-2401-660d-d7b9-a7b52d2e01a3.gif)
$2\pi \rightarrow 0$ みたいに $ 5\pi/2 \rightarrow \pi/2$, $3\pi \rightarrow \pi \dots$ とかやろうかな？とおもったけど、それをするとループできないのでやめときました。