Ruby練習二日目(コッホ曲線)
今日は、コッホ曲線を描くプログラムを作ってみようと思います。理由は特にありません、思いつきです。はじめは曲がる点を求めるだけのプログラムを考えていましたが、どうせなら目に見えるようにしようと思い、描いたコッホ曲線を画像に出力する処理まで作成しました。画像の描画には cairo を使用しました。と。
require 'cairo' # # コッホ曲線の描画クラス # class KochCurve COS = Math.cos(Math::PI / 3) SIN = Math.sin(Math::PI / 3) #=== コンストラクタ # #_width_ :: 画像幅 #_height_ :: 画像高 # def initialize(width, height) @width = width @height = height surface = Cairo::ImageSurface.new(@width, @height) @context = Cairo::Context.new(surface) init_context end #=== 描画領域の初期化 # def init_context # 背景の設定 @context.set_source_rgb(1, 1, 1) # 色:白 @context.rectangle(0, 0, @width, @height) # 領域全てをパスで囲む @context.fill # パスの内側を塗りつぶす # 線の設定 @context.set_source_rgb(0, 0, 0) # 黒 @context.set_line_width(1.0) # 太さ end #=== 一辺のコッホ曲線を描画 # #_x1_ :: 始点x座標 #_y1_ :: 始点y座標 #_x2_ :: 終点x座標 #_y2_ :: 終点y座標 #_dim_ :: 分割回数 # def koch_curve(x1, y1, x2, y2, dim) @context.stroke { @context.move_to(x1,y1) _koch_curve(x1, y1, x2, y2, dim) } end #=== コッホ曲線のロジック # #_x1_ :: 始点x座標 #_y1_ :: 始点y座標 #_x2_ :: 終点x座標 #_y2_ :: 終点y座標 #_dim_ :: 分割回数 # def _koch_curve(x1, y1, x2, y2, dim) if dim == 1 @context.line_to(x2, y2) else x3 = (2 * x1 + x2) / 3 y3 = (2 * y1 + y2) / 3 x5 = (x1 + 2 * x2) / 3 y5 = (y1 + 2 * y2) / 3 x4 = x3 + (x5 - x3) * COS + (y5 - y3) * SIN y4 = y3 - (x5 - x3) * SIN + (y5 - y3) * COS dim -= 1 _koch_curve(x1, y1, x3, y3, dim) _koch_curve(x3, y3, x4, y4, dim) _koch_curve(x4, y4, x5, y5, dim) _koch_curve(x5, y5, x2, y2, dim) end end #===ファイルへの出力 # #_fname_ :: ファイル名 # def save(fname) @context.target.write_to_png(fname) # 保存したら初期化する init_context end end
使い方はこんな感じ
koch = KochCurve.new(180, 180) # 幅180px,高さ180pxの画像 koch.koch_curve(0, 90, 180, 90, 3) # 画像の中心(横向き)に線を描く koch.save("koch.png") # koch.png というファイルに出力
座標の計算をすれば、コッホ雪片も。下の例だと、右回りに点をトレースします。この場合、内側に伸びていく雪片が描けます。外側に伸びていく雪片にする場合は、左回りにすればOKです。
koch = KochCurve.new(180, 180) # コッホ曲線を3つ合わせたコッホ雪片から、5つ合わせた画像を作成 for i in 3..5 x1 = 40 # 描画開始座標x 適当に調整。 y1 = 30 # 描画開始座標y len = 90 # 線分(?)の長さ rad = (Math::PI * (i - 2)) / i # 正n角形の1つの内角 i.times { |j| x2 = x1 + len * Math.cos(rad - (j * (Math::PI - rad))) y2 = y1 + len * Math.sin(rad - (j * (Math::PI - rad))) koch.koch_curve(x1, y1, x2, y2, 4) x1 = x2 y1 = y2 } koch.save("koch" + i.to_s + ".png") end
でき上がった画像。これに機能を加えるなら、真ん中に描画する機能かな(トリミングする方が早そうだけど)。
感想
- はじめて標準添付以外のライブラリを使用した。ライブラリを使わずに、これと同じプログラムを書けといわれたら、作る気をなくしていただろうな。それもこれも皆様のおかげです。
- Ruby の練習というよりも、sin, cons の勉強って感じ。日頃使わないので、思い出すというよりも再学習。でも、面白かった。