「プログラムでシダを描画する」を Ruby で描画する

巷ではシダを生やすのが流行っているようですが、
意外にも Ruby での投稿がなかったようなのでやってみました。 

```:shida.rb
require "RMagick"
require "open3"

class ShidaGenerator
  include Magick

  N = 20
  DEFAULT_WIDTH  = 500
  DEFAULT_HEIGHT = 500
  FILE_NAME = "shida.jpg"

  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 = -> (x, y) { 0 }
  W4y = -> (x, y) { 0.175337 * y }

  def initialize(width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT)
    @width = width
    @height = height
  end

  def self.show(width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT)
    self.new(width, height).show
  end

  def generate
    image = Magick::Image.new(@width, @height)

    f = -> (k, x, y) do
      if 0 < k
        f.(k - 1, W1x.(x, y), W1y.(x, y))
        f.(k - 1, W2x.(x, y), W2y.(x, y)) if Random.rand < 0.3
        f.(k - 1, W3x.(x, y), W3y.(x, y)) if Random.rand < 0.3
        f.(k - 1, W4x.(x, y), W4y.(x, y)) if Random.rand < 0.3
      else
        xx = (x * 490 + @width * 0.5).to_i
        yy = (@height - y * 490).to_i
        image.store_pixels(xx, yy, 1, 1, [green_pixel])
      end
    end

    -> (k, x, y) { f.(k, x, y) }.(N, 0, 0)

    image
  end

  def show
    self.generate.write(image_path)
    system("open -a Preview #{image_path}") if open_command_exists?
  end

  private

  def green_pixel
    @pixel ||= Magick::Pixel.new(0, 128 * 256, 0)
  end

  def image_path
    current_dir = File.expand_path(File.dirname(__FILE__))
    File.join(current_dir, FILE_NAME)
  end

  def open_command_exists?
    Open3.capture3("which open").first.empty?.!
  end
end

exit unless __FILE__ == $0

ShidaGenerator.show
```

当初は ShidaGenerator#render メソッドを再帰的に呼んで描画処理を行っていたのですが、
インスタンスメソッドを再帰的に呼ぶのが嫌だったので、メソッド内で lambda を再帰的に呼ぶことで実現しました。

それと、本当は [tk](http://docs.ruby-lang.org/ja/2.1.0/library/tk.html) を使って GUI で表示したかったんですが、何時間か試しても実現できませんでした…。
そのため妥協案として JPEG 画像を生成して Mac の場合はそれをプレビューで表示するようにしました。

## 結果
![shida.jpg](https://qiita-image-store.s3.amazonaws.com/0/6697/3b2f60ca-7428-fa88-92e5-59942c637f5b.jpeg)

## 元ネタ
* [プログラムでシダを描画する - 強火で進め](http://d.hatena.ne.jp/nakamura001/20140505/1399316565)
* [「プログラムでシダを描画する」をDartで描画する - Qiita](http://qiita.com/zukkun/items/08b72c9934d3c0c09936)
* [「プログラムでシダを描画する」をGoで描画する - Qiita](http://qiita.com/qt-luigi/items/ec6cd349259fe6cc29eb)
* [Clojure - プログラムでシダを描画する - Qiita](http://qiita.com/woxtu/items/30aebcf15770c438d6fd)
* [「プログラムでシダを描画する」をPythonで描画する - Qiita](http://qiita.com/noc06140728/items/8b8f06cfc312b8492df4)
* [「プログラムでシダを描画する」をJavaScript+Canvasで描画する - Qiita](http://qiita.com/alucky0707/items/ada6514d6068fdff2a8c)
* [「プログラムでシダを描画する」をPHPで描画する - Qiita](http://qiita.com/mpyw/items/dc339f12705f8b97ef67)
* [「プログラムでシダを描画する」をEmacs Lispで描画する](http://qiita.com/akmiyoshi/items/ff4df934a5a89aaafbcb)
* [「プログラムでシダを描画する」をSass(CSS)で描画する](http://qiita.com/yaegaki/items/ec5c89e13d9fe61281ae)

## 参考
* [RMagick: User's Guide and Reference](http://studio.imagemagick.org/RMagick/doc/index.html)
* [ruby で再帰(reduceメソッド作戦・多重lambda作戦)](http://d.hatena.ne.jp/ux00ff/20120715/1342319322)