Edited at

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

More than 5 years have passed since last update.

巷ではシダを生やすのが流行っているようですが、

意外にも 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 を使って GUI で表示したかったんですが、何時間か試しても実現できませんでした…。

そのため妥協案として JPEG 画像を生成して Mac の場合はそれをプレビューで表示するようにしました。


結果

shida.jpg


元ネタ


参考