Bernsley Fern(バーンズリーのシダ)
今回のフラクタルはシダを描いていく.
シダは見た目からして自己相似を含んでいるのは分かるが、これを実際に一定の操作のみで描くことが可能.
数式は例によってWiki1を参照していく.
計算としては非常に簡単で,以下のようなアフィン変換を座標に施すだけである.
という処理、このfを4つに分割して、離散確率分布を利用して参照していく.
4つのfはシダの茎,連続する小さい葉,左側の大きな葉,右側の大きな葉を表している.
この4つの係数は以下のような感じ.
シダの茎
連続する小さい葉
左側の大きな葉
右側の大きな葉
後はこれを実際にコードで書いてあげるだけ!
今回はTransform関係は以下のように定義.
2x2の行列はVec4で定義して、TranslationはVec2で表現.
mat2x2がなかったので,Vec4で代用した感じ.
3x3にしてもよかったけど、まあこれでもいいかぁという感じな怠惰.
次に4つのアフィン係数を定義.そのまま.
std::array<TransformFern, 4> Transform;
// シダの茎
Transform[0] = TransformFern{ Vec4{0.0,0.0,0.0,0.16}, Vec2{0.0, 0.0} };
// 連続する小さい葉
Transform[1] = TransformFern{ Vec4{0.85,0.04,-0.04,0.85}, Vec2{0.0, 1.6} };
// 左側の大きな葉
Transform[2] = TransformFern{ Vec4{0.2,-0.26,0.23,0.22}, Vec2{0.0, 1.6} };
// 右側の大きな葉
Transform[3] = TransformFern{ Vec4{-0.15,0.28,0.26,0.24}, Vec2{0.0, 0.44} };
確率も定義.重み付き分布関数を作りたいので、std::discete_distribution<size_t>を使って表現してみた.
Array<double> probabilities{ 0.01,0.85,0.07,0.07 };
std::random_device seed_gen;
std::uint32_t seed = seed_gen();
std::mt19937 engine(seed);
std::discrete_distribution<std::size_t> dist(
probabilities.begin(),
probabilities.end()
);
ここまでくれば後は処理.
初期位置は(x,y)=(0,0)とする.
処理回数はloopで指定した回数.
ループ内ではまず最初にindexでどのfのアフィン変換を行うかを決定する.
後は行列計算をしてtranslationを足せば終わり.
// 位置を計算
double tempX =
point.x * Transform[index].m_matrix.x +
point.y * Transform[index].m_matrix.y +
Transform[index].m_translation.x;
double tempY =
point.x * Transform[index].m_matrix.z +
point.y * Transform[index].m_matrix.w +
Transform[index].m_translation.y;
point = { tempX,tempY };
// 追加
pointSet.push_back(point);
結果を見てみよう.
うん、ちゃんとシダっぽいのが形成されている.
他にも種類があるっぽいので、別のも描画して遊んでみると面白いかもしれない.