はじめに
一部ではたこやきとかとも言われている者です。
もうそろそろクリスマスというところで、アドベントカレンダーも終わってしまいますね。
さて、今回この記事では、p5.jsでキャンバスに円を敷き詰めるために頑張ってみたいと思います。
解説する系の記事もいいけど、よくわからんけど頑張ってる姿を実況する記事も好きなので。。
クリエイティブコーディング的なものが好き〜〜みたいな記事はここに書いています。
アドベントカレンダーにしたくせにはじめに以外を全く書いてないのは触れてはいけないパンドラの箱です。
完成希望図
ざっくりとイメージはこんな感じです。
ここで言うところの敷き詰めるは「大きさも位置もランダムに配置された円が一部分も重なることなく描画されている」状態を指しています。
今までは難しそうで遠ざけていましたが、一旦やってみます。
Step1:とりま沢山円を書いてみる
まあ、一旦円を沢山書くところからはじめてみました。
おそらく、後々円同士の距離を測って、重なっていたら描かないみたいなことをする気がするので、円を書く回数を「challenge_num」としてみました。他は特に変なことをしていないはずです。
Qiitaにコード載せることに緊張して、変数名を冗長に書いて置いたくらい。笑
//円を書く個数(後で「描こうとする」個数になる)
const challenge_num = 3000;
function setup() {
createCanvas(600, 600);
//challenge_num個の円を描く
for(let i = 0; i < challenge_num; i ++){
//円の位置とサイズ。サイズについてwidth/2なのには特に意味ない。
let circle_position = createVector(random(width), random(height));
let circle_size = random(width/2)
circle(circle_position.x, circle_position.y, circle_size);
}
}
実行結果
Step2:あれ、一旦できた。
意外とできてしまいました。丁寧に時間をかければかけるものですね。
一応、雑ではありますがどういう実装にしたかといいますと、、
円と円の距離が近いとダメなので、今までに描かれている円の座標とサイズをどこかに記憶しておいて、書こうとしている円と近いかどうかを確認しようってやつです。
この時に、記憶しておこうということで、circle_positionとcircle_sizeをcircle_infoという辞書にしています。(多分配列とかベクトルでもできるけど、後から読んで可読性が高いのは辞書かなと思い。)
で、円を書いたときにこのcircle_infoをcircle_info_arrに追加していって、今までに書いた円の情報を貯めていこうって訳です。
2つの円の距離を測るのはdistで測るのですが、条件で各円の大きさを半分にしているのがポイントです。
circleの引数は直径なので、距離を測る時は半径+半径にするために半分にする必要があります。
また、つまづきかけたポイントとしては、circle_info_arr.push(circle_info);のところで、ここは「円を書くときだけ」実行する必要があります。そうしないと、書いていない円とも比較してしまい、描かれる円の個数がめちゃ少なくなってしまいます;;
で、この辺でコードの説明が下手すぎることに気付いたので、あとはコメントアウト君にお任せしています、、、!!
const challenge_num = 1000;
let circle_info_arr = [];
function setup() {
createCanvas(600, 600);
//challenge_num回、円を描こうとする(条件に合わないと円が描かれないのでこれだけ円が描かれるわけじゃない)
for (let i = 0; i < challenge_num; i++) {
let circle_position = createVector(random(width), random(height));
let circle_size = random(width / 2);
//辞書に情報を詰める
let circle_info = { position: circle_position, size: circle_size };
//円を描くかどうかのフラグ
let success = true;
//これまでに描いた円全てを参照して比較していく
for (let j = 0; j < circle_info_arr.length; j++) {
//今描こうとしている円と参照している円の中心間の距離を測る
let d = dist(
circle_position.x,
circle_position.y,
circle_info_arr[j].position.x,
circle_info_arr[j].position.y
);
//中心間の距離が(半径+半径)よりも短ければ、描かないことにして、これまでに書いた円の参照をやめる
if (d < circle_size / 2 + circle_info_arr[j].size / 2) {
success = false;
break;
}
}
//描こうってなってたら描いて、描いた円としてその情報を配列に追加する
if (success) {
circle(circle_position.x, circle_position.y, circle_size);
circle_info_arr.push(circle_info);
}
}
}
実行結果
Step3:調整して工夫してみる
最後に色をランダムにした上で、円の位置自体も円形にしてみました。
サイズとか直書きなのはサボった証拠、、、
あと、othersのところもっといい名前あるはず、、、
const challenge_num = 3000;
let circle_info_arr = [];
const color_palletes = [
{base:"#FFF6C3", others:["#DC0000", "#850000", "#FFDB89"]},
{base:"#FCFFE7", others:["#EB455F", "#BAD7E9", "#2B3467"]},
{base:"#E6E2C3", others:["#1C315E", "#227C70", "#88A47C"]},
{base:"#FCFDF2", others:["#3B3486", "#7743DB", "#FFE9B1"]},
]
function setup() {
createCanvas(600, 600);
const color_pallete = random(color_palletes);
background(color_pallete.base);
for (let i = 0; i < challenge_num; i++) {
let position_angle = random(TAU);
let position_radius = random(width/3);
let circle_position = createVector(width/2 + position_radius*cos(position_angle), height/2 + position_radius*sin(position_angle));
let circle_size = random(10, 50);
let circle_info = { position: circle_position, size: circle_size };
let success = true;
for (let j = 0; j < circle_info_arr.length; j++) {
let d = dist(
circle_position.x,
circle_position.y,
circle_info_arr[j].position.x,
circle_info_arr[j].position.y
);
if (d < circle_size / 2 + circle_info_arr[j].size / 2) {
success = false;
break;
}
}
if (success) {
noStroke();
fill(random(color_pallete.others))
circle(circle_position.x, circle_position.y, circle_size);
circle_info_arr.push(circle_info);
}
}
}
実行結果
おわりに
完成しました、いえいです。
円を敷き詰めれたら嬉しくて仕方ないですね。
まあ、この記事の長さからも分かる通り、そこそこ長いこと考えないといけなかった割に出来たものがこれかい!ってなったかたもいるかもしれません、、、
が、これに対して一定、面白いかもって思えた方もいると思います。
そんな方が1人でもいれば嬉しいです。
クリスマスイブイブ楽しんでいきましょう!!!