はじめに
前回フラクタルについて書いたので,今回フラクタル図形に関する内容を書きます.
2020/5/6 @scivola 様のアドバイスより,改修
フラクタル図形
フラクタル幾何 wikipedia
フラクタル図形はフラクタル幾何とも言われるみたいですね.
ひとつの図形の中を拡大してみても,その小さな同じ図形が繰り返されているような図形のことです.
フラクタル図形のプログラミング
このようにフラクタル図形は同じ図形が繰り返されている・・・
ということは再帰ですね.
そのためプログラミングにおいてフラクタル図形の描画は,再帰を学ぶ良い題材として扱われるみたいです.
今回は,フラクタル図形の中からコッホ曲線とドラゴン曲線を実装してみようと思います.
ツールの仕様
作成したツールの仕様は以下の通りです.
コマンドラインからの引数を受け取りフラクタル図形をfractal_shape.png
として出力します.
usage = <<"EOS"
fractal_shape.rb : フラクタル図形描画ツール
Usage:
ruby fractal_shape.rb arg1 arg2 arg3
Description:
・コマンドライン引数として3つ入力してください
・fractal_shape.pngとして出力されます
Example:
# 再帰回数3のコッホ曲線を1000×1000サイズで出力
ruby fractal_shape.rb koch 1000 3
# 再帰回数10のドラゴン曲線を500×500サイズで出力
ruby fractal_shape.rb dragon 500 10
Options:
arg1:フラクタル図形の指定(コッホ曲線:'koch', ドラゴン曲線:'dragon')
arg2:出力するpng画像のサイズ
arg3:フラクタルのステップ数
EOS
Rubyによる実装
アルゴリズムの部分に関しては,以下の記事を参考にさせていただき,実装しました.
非常にわかりやすくまとまっており,アルゴリズムの部分は言語関係なく活用できますので是非.
再帰プログラムによるフラクタル図形の描画
入力はこんな感じで受け取ります.
引数がちゃんと3つ無い場合はUsage
を表示します.
- 引数1:コッホ曲線かドラゴン曲線か
- 引数2:大きさ
- 引数3:繰り返し回数
という具合で,クラスに渡します.
if __FILE__ == $0
if ARGV.size != 3
puts usage
exit
end
id = ARGV[0]
width = height = ARGV[1].to_i
step = ARGV[2].to_i
fractal_shape = FractalShape.new(width, height, step)
if id == "koch"
fractal_shape.koch
elsif id == "dragon"
fractal_shape.dragon
end
end
まずはinitialize()
で初期化を行います.
rubyで二次元描画どうするのかなあとかなり苦戦してしまいましたが
cairoというgemを使ってみることにしました.
def initialize(width, height, step)
@width = width
@height = height
@step = step
@surface = Cairo::ImageSurface.new(width, height)
@context = Cairo::Context.new(@surface)
@context.set_source_rgb(1, 0, 0)
@context.set_line_cap :round
end
描画を行う関数です.
context.move_to
からcontext.line_to
にかけて形を作り
context.stroke
で線分を描画します.
ここまではコッホ曲線,ドラゴン曲線ともに共通です.
def draw_line(s, e, context)
context.move_to(s[0], s[1])
context.line_to(e[0], e[1])
context.stroke
end
コッホ曲線
まずはコッホ曲線の実装からです.
最初の部分は,コッホ曲線を描画する際に大きさ,向きをいい感じにするために適当な計算をしてます.
def koch
p = Vector[@width * 0.1, @height * 0.5]
q = Vector[@width * 0.9, @height * 0.5]
koch_algorithm(p, q, @step, @context)
@surface.write_to_png("fractal_shape.png")
end
コッホ曲線のアルゴリズム
コッホ曲線のアルゴリズムです.
先ほどの記事が分かり易すぎるので,私が加筆する必要がありません・・・
def koch_algorithm(a, b, n, context)
c = (a * 2 + b) / 3.0
d = (a + b * 2) / 3.0
xx = (b - a)[0]
yy = -(b - a)[1]
distance = Math.hypot(xx, yy) / Math.sqrt(3)
if xx >= 0
angle1 = Math.atan(yy.to_f / xx) + Math::PI / 6
e = a + Vector[Math.cos(angle1), -Math.sin(angle1)] * distance
else
angle2 = Math.atan(yy.to_f / xx) - Math::PI / 6
e = b + Vector[Math.cos(angle2), -Math.sin(angle2)] * distance
end
if n <= 0
draw_line(a, c, context)
draw_line(c, e, context)
draw_line(e, d, context)
draw_line(d, b, context)
else
koch_algorithm(a, c, n - 1, context)
koch_algorithm(c, e, n - 1, context)
koch_algorithm(e, d, n - 1, context)
koch_algorithm(d, b, n - 1, context)
end
end
###結果
ドラゴン曲線
次は本命ドラゴン曲線の実装です.
こちらも同じくドラゴン曲線を描画する際に大きさをいい感じにするために適当な計算をしてます.
def dragon
p = Vector[@width * 0.3, @height * 0.5]
q = Vector[@width * 0.9, @height * 0.5]
dragon_algorithm(p, q, @step, @context)
@surface.write_to_png("fractal_shape.png")
end
ドラゴン曲線のアルゴリズム
ドラゴン曲線のアルゴリズムです.
def dragon_algorithm(a, b, n, context)
xx = (b - a)[0]
yy = -(b - a)[1]
c = Vector[a[0]+(xx + yy)/2, b[1]+(xx + yy)/2]
if n <= 0
draw_line(a, c, context)
draw_line(b, c, context)
else
dragon_algorithm(a, c, n - 1, context)
dragon_algorithm(b, c, n - 1, context)
end
end
###結果
そしてステップ数が10回を超えてくると・・・
「ヘイウェイ・ドラゴン」を召喚!!
ヘイウェイ・ドラゴン(ハーター・ヘイウェイ・ドラゴンあるいはジュラシック・パーク・ドラゴンとも呼ばれる)は、NASAの物理学者のジョン・ヘイウェイ、ブルース・バンクスおよびウィリアム・ハーターによって初めて研究され、1967年、雑誌『サイエンティフィック・アメリカン(Scientific American)』のマーティン・ガードナーによるコラム「数学ゲーム(Mathematical Games」で紹介された。その性質についてはチャンドラー・デイビスとドナルド・クヌースによって初めて出版化された。マイケル・クライトンの小説『ジュラシック・パーク』の節ごとのタイトルページで使用されている。
出でよ!「ツイン・ドラゴン」!!
二つのヘイウェイ・ドラゴンを背中合わせに配置することにより、ツインドラゴン(デイビス-クヌース・ドラゴンとも呼ばれる)を作ることが出来る。
粉砕!玉砕!大喝采!
なんてこともドラゴン曲線のアルゴリズムを,位置をずらして2回実行すれば簡単にできちゃいます.
まとめ
ということで,ツイン・ドラゴンを召喚できましたフラクタル図形を描画できました!
アルゴリズムさえ変えれば他のフラクタル図形も簡単に描画できますので
是非コピペして使ってみてください.
参考文献
再帰プログラムによるフラクタル図形の描画, 石立 喬[著] ,2005 https://codezine.jp/article/detail/73