正方形を分割してフラクタルを作る by Processing
前回は長方形を分割してフラクタル図形を作りましたが、今回は正方形をフラクタります。
こういうのを作りますよ。
この2パターンの解説をしていきます。
構造の解説
正方形を分割して4つの正方形にする。さらにそれを四分割。それを繰り返していくそんなフラクタル構造になっています。
左右のどちらも構造は一緒です。
再帰する or 再帰しないの分岐方法の違いによって2つは違った表情を見せています。
実装
分岐させることは後回しにして、まずは再帰関数を見ていきましょう。
int count = 5; //再帰回数
void setup() {
size(600, 600);
noLoop();
noFill();
stroke(0);
}
void draw() {
background(255);
squareRecusion(10, 10, width-20, count);
}
//squareRecusion(x座標, y座標, 一辺の大きさ, 再帰回数)
void squareRecusion(float x, float y, float size, int n) {
square(x, y, size);
n--;
if (n >= 0) {
//Half size
float hs = size/2;
squareRecusion(x, y, hs, n);
squareRecusion(x+hs, y, hs, n);
squareRecusion(x+hs, y+hs, hs, n);
squareRecusion(x, y+hs, hs, n);
}
}
座標、大きさは下図のようになっているので
四分割したそれぞれに引数を渡して再帰させます。
countの回数分だけ再帰を行います。
これを実行するとグリッドができます。
ここから面白い形にしていきますよ。
if文を使って描画する or 描画しないを分岐させていきましょう。
分岐パターン1
ものすごくシンプルに考えるとこんなコードになるでしょう。
void squareRecusion(float x, float y, float size, int n) {
square(x, y, size);
n--;
if (n >= 0) {
float hs = size/2;
if (random(1) > 0.5) {
squareRecusion(x, y, hs, n);
squareRecusion(x+hs, y, hs, n);
squareRecusion(x+hs, y+hs, hs, n);
squareRecusion(x, y+hs, hs, n);
}
}
}
50%の確率で再帰するようにしてみました。
でもこれだとこのように最初の正方形しか描画されないなんてことも出てきます。
そりゃ50%だもの。
見た目のバランスを取るために、nが大きい値(大きい正方形)ほど再起する確率が高くなるようにマッピングしましょう。
void squareRecusion(float x, float y, float size, int n) {
square(x, y, size);
n--;
if (n >= 0) {
float hs = size/2;
//nが大きい(大きい正方形)ほど再起する確率が高くなるようにマッピング
//Probability
float p = map(n, 0, count-1, 0.5, 0);
if (random(1) > p) {
squareRecusion(x, y, hs, n);
squareRecusion(x+hs, y, hs, n);
squareRecusion(x+hs, y+hs, hs, n);
squareRecusion(x, y+hs, hs, n);
}
}
}
count-1は上のn--によってすでにcountから1が引かれてる状態なのでつまり最大値です。
美しいバランスになりましたぁ。あぁぁ…
完成形のコードを貼っておきます。
Sキーで保存、マウスクリックで再描画。
int count = 5;
void setup(){
size(600, 600);
noLoop();
noFill();
stroke(0);
}
void draw(){
background(255);
squareRecusion(10, 10, width-20, count);
}
void squareRecusion(float x, float y, float size, int n) {
square(x, y, size);
n--;
if (n >= 0) {
float hs = size/2;
float p = map(n, 0, count-1, 0.5, 0);
if (random(1) > p) {
squareRecusion(x, y, hs, n);
squareRecusion(x+hs, y, hs, n);
squareRecusion(x+hs, y+hs, hs, n);
squareRecusion(x, y+hs, hs, n);
}
}
}
void mousePressed(){
redraw();
}
void keyPressed(){
if(key == 's')saveFrame("####.png");
}
もうひとつのパターンもいってみましょおおぉぉぉぉぉ!
分岐パターン2
上でやった分岐をそれぞれに適用してみます。
つまりこういうこと。
int count = 5;
void setup(){
size(600, 600);
noLoop();
noFill();
stroke(0);
}
void draw(){
background(255);
squareRecusion(10, 10, width-20, count);
}
void squareRecusion(float x, float y, float size, int n) {
square(x, y, size);
n--;
if (n >= 0) {
float probability = map(n, 0, count-1, 0.5, 0);
float hs = size/2;
if (random(1) > probability) {
squareRecusion(x, y, hs, n);
}
if (random(1) > probability) {
squareRecusion(x+hs, y, hs, n);
}
if (random(1) > probability) {
squareRecusion(x+hs, y+hs, hs, n);
}
if (random(1) > probability) {
squareRecusion(x, y+hs, hs, n);
}
}
}
void mousePressed(){
redraw();
}
void keyPressed(){
if(key == 's')saveFrame("####.png");
}
実行結果ドンッ!
ビジュアル的な違いをみてみると、パターン1は必ず正方形が描画され、パターン2は長方形や欠けた正方形のような形が新たに生成されていますね。
こっちのパターンは重なりが楽しめそうですね。
もっと楽しむためのアイデア
僕が以前書いたこのTipsと組み合わせてみましょう。
int count = 5;
void setup() {
size(600, 600);
noLoop();
}
void draw() {
background(255);
squareRecusion(10, 10, width-20, count);
}
void squareRecusion(float x, float y, float size, int n) {
fill(255);
stroke(0);
square(x, y, size);
randomShape(x, y, size);
n--;
if (n >= 0) {
float hs = size/2;
float p = map(n, 0, count-1, 0.5, 0);
if (random(1) > p) {
squareRecusion(x, y, hs, n);
squareRecusion(x+hs, y, hs, n);
squareRecusion(x+hs, y+hs, hs, n);
squareRecusion(x, y+hs, hs, n);
}
}
}
void mousePressed() {
redraw();
}
void keyPressed() {
if (key == 's')saveFrame("####.png");
}
void randomShape(float x, float y, float s) {
float hs = s/2;
pushMatrix();
translate(x + hs, y + hs);
fill(0);
noStroke();
if (random(1) > 3.0/4.0) {
square(-hs, -hs, s);
} else if (random(1) > 2.0/4.0) {
rotate(HALF_PI * int(random(4)));
triangle(-hs, -hs, hs, hs, -hs, hs);
} else if (random(1) > 1.0/4.0) {
rotate(HALF_PI * int(random(4)));
arc(0, -hs, s, s, 0, PI);
} else {
rotate(HALF_PI * int(random(4)));
arc(-hs, -hs, s*2, s*2, 0, HALF_PI);
}
popMatrix();
}
僕のnoteの中でこのフラクタル構造を使った作品をいくつか貼っておきます。
参考までにどうぞ。
この構造は頭で考えるよりもとりあえず実装してみると意外な結果が出ると思います。
素敵なものができたらぜひシェアしてください。
おわりに
自分がProcessingを始めたての頃こんな解説欲しかったなってことを書きました。
これからもそのスタンスでTipsを書いていこうと思います。
ありがとうございました!
応援してくださる方はぜひはご購入をお願いします!
Happy Coding!!
ここから先は
¥ 100
応援してくださる方!いつでもサポート受け付けてます!