フラクタルの木って検索するといろいろ出てくるあの有名なやつです。
再帰をしていったら、なんか木っぽくなるっていうあれです
あれを描いていきます。
では、いってみましょ!
フラクタルツリー
2023年7月号の日経ソフトウェアの基礎固めアルゴリズムにも載ってました。
フラクタル図形の木
枝を伸ばして、その先端から時計回りと反時計回りに回転させた短い枝を伸ばして・・・と繰り返していくと描かれる図形です。
Juliaでフラクタルツリーの実装
結構有名な?再帰アルゴリズムの例題みたいなものなので、検索すると結構たくさん出てきます。
枝の先端(x1,y1)から次の枝の先端(x2,y2)を求めて、更新していきます。
(x1,y1)から(x2,y2)を求めるには三角関数を使えば簡単に求められますね。x座標ならコサインをy座標ならサインをといった感じで。
今回は、描画にLuxorを使います。
木を描く関数
木を描く関数(tree())を作っていきます。
function tree(n,x1,y1,leng,angle)
rad = deg2rad(angle)
if n > 1
x2 = x1+cos(rad)*leng
y2 = y1-sin(rad)*leng
line(Point(x1,y1),Point(x2,y2))
tree(n-1,x2,y2,leng*0.6,angle+30)
tree(n-1,x2,y2,leng*0.6,angle-30)
end
end
sin,cos関数はラジアンを渡すので、最初に角度をラジアンに変換します。
新しい枝の先端の座標(x2,y2)をそれぞれ求めて、Luxorのline関数に渡して線を引きます。
そして、左と右に枝分かれするように再帰していきます。
描画処理
描画処理の部分をmydraw()として、まとめておきましょう
VSCodeやJupyterやPlutoとかで実行するなら、これで、描画を確認することができます。
function mydraw()
Drawing()
origin()
background("black")
sethue("white")
tree(12,0,300,200,90)
strokepath()
finish()
preview()
end
pngファイルに書き出すなら
@png begin
sethue("black")
tree(12,0,300,200,90)
strokepath()
end
こんな感じで描画してくれます。
これで、簡単な実装ができました。
設定数値を変えると、なんかいろんなのができるんですよ
例えば、枝の角度を80度にして、枝が短くなっていく割合を0.7ぐらいにすると
こんな感じになったりね。
けど、この数値をいじるために、いちいち、関数内の数値をいじるのは、あまり素敵ではないよね。
マジックナンバーばかりの関数はやばいよね(笑)
ということで、Settingっていうstructを使って、もう少しプログラムっぽくしてみましょう
structを使って設定値を操る
ここでは、こんな構造体を使います。
Base.@kwdef struct Setting
n::Int=12
x1::Float64=0
y1::Float64=300
length::Float64=200
angle::Float64=90
rbr::Float64=0.6
lbr::Float64=0.6
right_angle::Float64=30
left_angle::Float64=30
end
上から、
- 繰り返しの数
- 初期のx座標
- 初期のy座標
- 初期の枝の長さ
- 初期の角度
- 右枝の長さの変化率
- 左枝の長さの変化率
- 右枝の角度の変化量
- 左枝の角度の変化量
です。
構造体に初期値を最初から代入したいので
“Base.@kwdef”を使います。これで、構造体を作ると同時に初期値を代入できるラシイ。
あとは、各関数内で構造体を実体化させて使えばいいだけね。
では、それを踏まえて、ソースを書き換えるとこうなる
まずは、木を描く関数
function tree(n,x1,y1,leng,angle)
rad = deg2rad(angle)
s=Setting()
if n > 1
x2 = x1+cos(rad)*leng
y2 = y1-sin(rad)*leng
line(Point(x1,y1),Point(x2,y2))
tree(n-1,x2,y2,leng*s.lbr,angle+s.left_angle)
tree(n-1,x2,y2,leng*s.rbr,angle-s.right_angle)
end
end
次に、描画する関数
function mydraw()
s=Setting()
Drawing()
origin()
background("black")
sethue("white")
tree(s.n,s.x1,s.y1,s.length,s.angle)
strokepath()
finish()
preview()
end
pngを書き出す処理
@png begin
s=Setting()
sethue("black")
tree(s.n,s.x1,s.y1,s.length,s.angle)
strokepath()
end
こんな感じだね
これで、自由自在に変えることができるね。
構造体をさらに一歩進んで!
構造体を使って初期値設定を一括で見やすく、関数の中にマジックナンバーが無いようにできました。
次は、構造体を都度書き換えて、更新して、行こうと思います。
どういうことかっていうと
自分で角度とか長さとか値を設定してやるのもいいけど、適当な数をランダムで入れたら時にめっちゃ素敵なのができるかもしれない!ってのがあるよね?
これやっちゃうと、フラクタルなのかどうか・・・とか思い始めるけど、それは、今はどこかに置いておこう
さぁ!いくぞ!
構造体をちょっといじる
枝の長さの変化率と角度の変化量をランダムに値をとって代入していくっていうことをやっていきます
そしたらまず、構造体に2つの項目を足します。
rate_range=0.5:0.01:0.7
angle_range=20:90
一つ目が変化率の変化範囲
二つ目が角度の変化範囲
juliaの場合ね(最小値:ステップ:最大値)って書けば、範囲を設定できるのね。
書き足したらですね。次に構造体の最初の部分
Base.@kwdef struct Setting
この部分を次のように書き換える
Base.@kwdef mutable struct Setting
大切なのはこの[mutable]
これを入れておかないと、実体化した後に内容を書き換えようとしたら、エラーが出ちゃうぞ
これで構造体の準備はOKだ
関数をいじる
木を描く関数を少しいじります。
s.rbr=rand(s.rate_range)
s.lbr=rand(s.rate_range)
s.right_angle=rand(s.angle_range)
s.left_angle=rand(s.angle_range)
この4行を再帰呼び出しの前に入れます。呼び出した後に書いたら意味ないからね(笑)
この4行がそれぞれの値を書き換えてくれるわけだね
これで完成。
実行してみると
こんな感じで、いろいろ思いもしなかったものができる。
各設定値のレンジを変えれば、ある程度範囲を絞って作ることもできるかもねー
まとめ
今回は、フラクタルの木をやっていきました。
あと構造体の使い方とかね。
フラクタル図形ってほかにもなんかいろいろあるわけです。
過去にもマンデルブロ集合とかについて書いたこともあったけど、それもフラクタルだね。
気になる人はそっちも見てみて。
ではまた
コメント