LoginSignup
2
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2014-12-19

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

shida.png

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


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 がでるが、ここでは気にしない。)

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

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 です)

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