この記事では書籍「世界一美しい錯視アート」に掲載されている「蛇の回転4」という作品をProcessingを使って再現してみます。
火事場の蛇の回転
今回作成した錯視アート「蛇の回転4」です。
書籍では「ヘビの環が回転して見える。」と解説されています。確かに、回転して見えます。
描き方のポイント
円盤の描き方
この作品は基本的には1つの円盤を描くことができれば、あとはそれを並べていくことで作成することができます。この円盤の描き方については、ほとんど別記事「冷凍蛇の回転」と同じですので、そちらをご覧ください。ただし、「冷凍蛇の回転」ではチェック柄を描いた後、菱形の列を描きましたが、今回の「蛇の回転4」では、半円の列となります。半円はその直径がチェック柄の動径方向の長さと同じになるように描きます。
円盤の中心に青色の円を描く
円盤の中央に小さい青色の円を描きます。
円盤の並べ方
今回の作品では円盤を上から順番に重ねていきながら描きます。
プログラムコード
今回作成した錯視アート「蛇の回転4」のプログラムコードを載せておきます。
void setup() {
size(1000, 1000, P2D);
background(255,255,255);
noStroke();
translate(width/2, height/2);
float line_len = width/6.0; // 円盤の半径
int lines_num = 36; // 扇形の個数
// 3×4に並んだ円盤を描画
for(int i=0; i<5; i++){
if( i%2 == 0 ){
for(int j=0; j<2; j++){
resetMatrix();
translate(width/3.0+j*width/3.0, width/6.0+i*width/6.0);
scale(pow(-1,j),1);
drawDisk(line_len, lines_num);
}
} else {
for(int j=0; j<3; j++){
resetMatrix();
translate(width/6.0+j*width/3.0, width/6.0+i*width/6.0);
scale(pow(-1,j),1);
drawDisk(line_len, lines_num);
}
}
}
}
// 円盤を描く関数
void drawDisk(
float line_len, // 円盤の半径
int lines_num // 扇形の個数
){
float theta_arcs = radians(360.0/lines_num); // 扇形の中心角の大きさ
float segment_len_initial = 2.0 * line_len * sin(theta_arcs/2.0) / (1.0 + sin(theta_arcs/2.0)) ; // 線分の長さの初期値
float ratio = 1.0 - segment_len_initial / line_len; // 線分の縮小比率
// 扇形から円盤のバックグラウンドとなる黒と白のチェッカー柄を描画
rotate(theta_arcs/2.0);
for(int k=0; k<lines_num; k++){
drawChecker(line_len, segment_len_initial, ratio, theta_arcs, k); // 収束点に向かって縮小していく扇型を描画
rotate(theta_arcs);
}
// 黒と白のチェッカー柄の上に菱形の列を描画
rotate(-theta_arcs/2.0);
for(int k=0; k<lines_num; k++){
drawRhombus(line_len, segment_len_initial, ratio, theta_arcs, k); // 収束点に向かって縮小していく菱形を描画
rotate(theta_arcs);
}
// 最後に円盤の中心に青丸を置く
fill(0,0,255);
circle(0.0,0.0,line_len/10.0);
}
// 黒と白の縞々の扇形を描画する関数
void drawChecker(
float line_len, // 円盤の半径
float segment_len_initial, // 線分の長さの初期値
float ratio, // 線分の縮小比率
float theta, // 扇形の中心角の角度
int number // 扇形の番号(奇数のとき、黒と白が入れ替わる)
){
float x; // 収束点からの長さ
float segment_len; // 線分の長さ
x = line_len;
segment_len = segment_len_initial;
int i = 0;
while(i<100){
noStroke();
// 図形の番号やinversionフラグに合わせて色を指定する
if ((i+number) % 2 == 0){
fill(255,255,255);
} else {
fill(0,0,0);
}
// バームクーヘンのような形状を描いていく
beginShape();
for(int j=0; j<=100; j++){
vertex(x * cos(-theta/2.0+j*theta/100.0), x * sin(-theta/2.0+j*theta/100.0));
}
for(int j=0; j<=100; j++){
vertex((x - segment_len) * cos(theta/2.0-j*theta/100.0), (x - segment_len) * sin(theta/2.0-j*theta/100.0));
}
endShape(CLOSE);
x = x - segment_len; // 収束点からの長さを更新
segment_len = segment_len * ratio; // 線分の長さを更新
i++;
}
}
// 半円の列を描いていく関数
void drawRhombus(
float line_len, // 円盤の半径
float segment_len_initial, // 線分の長さの初期値
float ratio, // 線分の縮小比率
float theta, // 扇形の角度
int number // 図形の番号(奇数のとき、菱形の色が入れ替わる)
){
float x; // 収束点からの長さ
float segment_len; // 線分の長さ
x = line_len;
segment_len = segment_len_initial;
float center_x, center_y; // 半円の中心位置
float diameter; // 半円の半径
int i = 0;
while(i<100){
center_x = x - segment_len/2.0;
center_y = 0.0;
diameter = segment_len;
noStroke();
// 半円の色を指定
if ((i+number) % 2 == 0){
fill(200,0,200); // 紫
} else {
fill(255,242,50); // 黄緑っぽい色
}
// 半円を描く
arc(center_x, center_y, diameter, diameter, radians(0), radians(180));
x = x - segment_len; // 収束点からの長さを更新
segment_len = segment_len * ratio; // 線分の長さを更新
i++;
}
}