LoginSignup
0
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-10-13

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

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

octave で画像を描く

octave が無いので、言語は perl 指定
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 から ImageMagick-7.0.8-68-portable-Q16-x86.zip 辺りを拾ってくる。中の convert を取り出してコマンドプロンプトでトリミング & アニメーションへ変換。
※事前に gimp 等でトリミング範囲を確認しておく

> convert -crop 989x183+145+343 +repage -delay 3 -layers Optimize Z:\Temp\temp_???.png Z:\Temp\anime_opt.gif

出力はこんな感じ
anime_opt.gif

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

画像を保存するところ、

    # 保存
    print "-S989,193" -dpng temp.png
    rename ("temp.png", sprintf('temp_%03d.png', i));

みたいにするとグラフ自体が小さくなってしまい悲しい。
本当は eps とかで保存して、適当に拡大縮小をして使うのが良いのかも。そうすると ghostview も入れないといけなくなるけど。

sin のグラフが伸びていくほうが自然かな

sin を書いていくような動画にしてみた。

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 軸からかけ離れたところにあるって、なかなか謎仕様ですね。離れてるのでアニメーション化の時に切り落とされましたとさ。

> 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
IrfanView で拡大したりするとゆらゆらするけど、ブラウザで見ると大丈夫ですね。

スクロールさせてみた

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 で、表示する範囲を移動させ、左の円も同じように移動させてみた

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
$2\pi \rightarrow 0$ みたいに $ 5\pi/2 \rightarrow \pi/2$, $3\pi \rightarrow \pi \dots$ とかやろうかな?とおもったけど、それをするとループできないのでやめときました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0