「Erlang でシダを描画する」をしました。
ですが、Erlang でのグラフィクス処理がよくわからず、プロットは Ruby(RMagick)で行いました。
Ruby 側からコマンドとして Erlang 側を呼び出しています。受け渡しデータ形式は YAML にしました。
実行は Linux で行いました。
(プログラムで描画した 'shida.png')
Erlang のプログラムです。
Erlang は全然分かりません。これだけ書くだけでも、四苦八苦しました。
(Erlang っぽい書き方をもっと知りたいです...)
-module(shida).
-export([fern/0]).
main(_) -> fern(). % Warning がでる.
w1x(X, Y) -> 0.836 * X + 0.044 * Y.
w1y(X, Y) -> -0.044 * X + 0.836 * Y + 0.169.
w2x(X, Y) -> -0.141 * X + 0.302 * Y.
w2y(X, Y) -> 0.302 * X + 0.141 * Y + 0.127.
w3x(X, Y) -> 0.141 * X - 0.302 * Y.
w3y(X, Y) -> 0.302 * X + 0.141 * Y + 0.169.
w4x(_, _) -> 0.0.
w4y(_, Y) -> 0.175337 * Y.
f(K, X, Y) when 0 < K ->
f(K - 1, w1x(X, Y), w1y(X, Y)),
R2 = random:uniform(),
if
R2 < 0.3 -> f(K - 1, w2x(X, Y), w2y(X, Y));
true -> false
end,
R3 = random:uniform(),
if
R3 < 0.3 -> f(K - 1, w3x(X, Y), w3y(X, Y));
true -> false
end,
R4 = random:uniform(),
if
R4 < 0.3 -> f(K - 1, w4x(X, Y), w4y(X, Y));
true -> false
end;
f(_, X, Y) ->
io:format("- [~p,~p]~n", [X,Y]).
fern() ->
io:format("---~n"),
f(20, 0, 0).
Erlang プログラムのコンパイル。コンパイルされると 'shida.beam' が生成される。
(使わない関数 main/1 が定義されているため Warning がでるが、ここでは気にしない。)
$ erlc shida.erl
shida.erl:4: Warning: function main/1 is unused
ここでは、Erlang プログラムが出力する YAML は、Ruby スクリプトが直接読み込んでいますが、以下ようのに、Eralng プログラムを単体で起動して、その YAML を得ることができます。
$ erl -noshell -s shida fern -s init stop >shida.yml # 'shida.yml' に YAML を出力
YAML を得ることは、コンパイルをしなくとも 'escript' を使うことでできます。
escript は Erlang のソースをスクリプトとして実行するコマンドです。
main/1 は escript で実行される時のエントリになる関数です。
$ escript shida.erl >shida.yml
出力される YAML は以下のようなものになります。
---
- [0.23039197933625838,0.9540610558917142]
- [-0.09346123199420277,0.6618293098304603]
- [0.19585012939122903,0.16605052025195166]
- [0.0,0.10204833181962314]
- [-0.13737612939122906,0.3255055202519517]
- [0.0,0.031221486770771134]
- [0.03498238417920148,0.5230806777106548]
- [0.08400968188016768,0.30905090425531007]
- [0.03915546536211761,0.17890820688023468]
- [-0.03915546536211761,0.22090820688023466]
:
X座標とY座標の組(配列)の配列の形です。(n はだいたい 500,000〜600,000くらいになります)
[[X1, Y1], [X2, Y2], [X3, Y3], ..., [Xn, Yn]]
Ruby スクリプトです。ライブラリは RMagick、YAML を使用しています。
#!/usr/bin/env ruby
require 'yaml'
require 'rmagick'
#
# Plotter
#
W, H = 500, 500
img = Magick::Image.new W, H
img.store_pixels 0, 0, W, H, [Magick::Pixel.new(0,0,0)] * (W * H)
ran = -> { rand 0..0xffff }
color = -> { [ran.()/4, ran.(), ran.()/2, 0xffff - ran.()/8] }
#color = -> { [0, 0xffff, 0] } # 単色 緑
axis = -> dir, scale, origin, n { (n * dir * scale + origin).to_i }
tx = axis.curry[+1, W - 10, W / 2]
ty = axis.curry[-1, H - 10, H ]
t = -> xy { [tx.(xy[0]), ty.(xy[1])] }
plot = -> xy { img.store_pixels *t.(xy), 1, 1, [Magick::Pixel.new(*color.())] }
dump = -> filename = 'shida.png' { img.write filename }
#
# 「シダ」データ取得
#
command = '| erl -noshell -s shida fern -s init stop'
data = open(command) {|f| YAML.load f.read }
#
# プロット
#
data.each &plot
dump.() # 'shida.png' に画像が書き出されます
# vi:set ts=2 sw=2 et:
実行は以下のようにします。
Ruby スクリプトが Erlang プログラムを起動して データをプロットした画像(shida.png)を出力します。
僕の環境では、処理時間が約 50 秒くらいかかります。(Celeron PC です)
$ ruby shida.rb # 'shida.png' にプロットした画像が書き出される。
作成・実行確認した環境
- Ubuntu Linux 14.04
- Erlang R16B03 (erts-5.10.4) (※ Ubuntu の 'erlang' パッケージをインストール)
- Ruby 2.1.5
- rmagick (2.13.4) (※ gem の 'rmagick' をインストール)