「Erlang でシダを描画する」(ただし、Ruby(RMagick)と合わせ技)

「Erlang でシダを描画する」をしました。
ですが、Erlang でのグラフィクス処理がよくわからず、プロットは Ruby(RMagick)で行いました。
Ruby 側からコマンドとして Erlang 側を呼び出しています。受け渡しデータ形式は YAML にしました。
実行は Linux で行いました。

![shida.png](https://qiita-image-store.s3.amazonaws.com/0/60624/08c023a1-4880-0bc9-8b6a-d750ed2e064c.png)

(プログラムで描画した 'shida.png')

---
Erlang のプログラムです。
Erlang は全然分かりません。これだけ書くだけでも、四苦八苦しました。
(Erlang っぽい書き方をもっと知りたいです...)

```erlang:shida.erl
-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 がでるが、ここでは気にしない。)

```shell-session
$ erlc shida.erl         
shida.erl:4: Warning: function main/1 is unused
```
---
ここでは、Erlang プログラムが出力する YAML は、Ruby スクリプトが直接読み込んでいますが、以下ようのに、Eralng プログラムを単体で起動して、その YAML を得ることができます。

```shell-session
$ erl -noshell -s shida fern -s init stop >shida.yml       # 'shida.yml' に YAML を出力
```

YAML を得ることは、コンパイルをしなくとも 'escript' を使うことでできます。
escript は Erlang のソースをスクリプトとして実行するコマンドです。
main/1 は escript で実行される時のエントリになる関数です。

```shell-session
$ escript shida.erl >shida.yml
```

出力される YAML は以下のようなものになります。

```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 を使用しています。

```rb:shida.rb
#!/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 です)

```shell-session
$ 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' をインストール)