この記事では書籍「世界一美しい錯視アート」に掲載されている「夜の金属蛇の回転」という作品をProcessingを使って再現してみます。
夜の金属蛇の回転
今回作成した錯視アート「夜の金属蛇の回転」です。
書籍では「リングがゆっくり回転して見える。」と解説されています。じっと見ていると、そのように見えます。あと、外側のリングと内側のリングに比べて中間のリングが手前に見えたり、奥に見えたりするのも面白いと思います。
描き方のポイント
爪型図形の描き方
リング上に並んでいる爪型図形の描き方は別記事「不可能図形的蛇の回転2」で解説していますので、そちらをご覧ください。
中間のリングの位置調整
中間のリングは外側のリングと内側のリングに対して反対向きの回転になっています。これは、\(x\)軸または\(y\)軸回りに反転させることで描くことができます。ただ、単に反転させただけではだめで、リング間で白色の爪型図形同士、黒色の爪型図形同士がつながるように、中間のリングをリングの中心回りに回転して調整する必要があります。
プログラムコード
今回作成した錯視アート「夜の金属蛇の回転」のプログラムコードを載せておきます。
void setup() {
size(1000, 1000);
background(0,0,0);
noStroke();
translate(width/2, height/2);
float annulus_R = width/2.0; // 円環の大半径
float ratio = 0.8; // 円環の大半径と小半径の比
int parts_num = 36; // パーツの個数
float theta = radians(360)/parts_num;
// 大きい(外側の)円環を描画
for(int i=0; i<parts_num; i++){
drawNail_grad(annulus_R, ratio, theta * 0.4, color(150,150,0), color(255,255,0));
rotate(theta * 0.4);
drawNail(annulus_R, ratio, theta * 0.1, color(255,255,255));
rotate(theta * 0.1);
drawNail_grad(annulus_R, ratio, theta * 0.4, color(0,0,255), color(0,0,200));
rotate(theta * 0.4);
drawNail(annulus_R, ratio, theta * 0.1, color(0,0,0));
rotate(theta * 0.1);
}
// 中間の円環を描画
scale(-1,1);
rotate(theta/4.0);
annulus_R = ratio * annulus_R; // 円環の大半径の大きさを更新
for(int i=0; i<parts_num; i++){
drawNail_grad(annulus_R, ratio, theta * 0.4, color(150,150,0), color(255,255,0));
rotate(theta * 0.4);
drawNail(annulus_R, ratio, theta * 0.1, color(255,255,255));
rotate(theta * 0.1);
drawNail_grad(annulus_R, ratio, theta * 0.4, color(0,0,255), color(0,0,200));
rotate(theta * 0.4);
drawNail(annulus_R, ratio, theta * 0.1, color(0,0,0));
rotate(theta * 0.1);
}
// 小さい円環を描画
scale(-1,1);
rotate(theta/4.0);
annulus_R = ratio * annulus_R; // 円環の大半径の大きさを更新
for(int i=0; i<parts_num; i++){
drawNail_grad(annulus_R, ratio, theta * 0.4, color(150,150,0), color(255,255,0));
rotate(theta * 0.4);
drawNail(annulus_R, ratio, theta * 0.1, color(255,255,255));
rotate(theta * 0.1);
drawNail_grad(annulus_R, ratio, theta * 0.4, color(0,0,255), color(0,0,200));
rotate(theta * 0.4);
drawNail(annulus_R, ratio, theta * 0.1, color(0,0,0));
rotate(theta * 0.1);
}
save("rotating_metallic_snakes_at_night.jpg");
}
// 爪の形状を描く関数
void drawNail(
float annulus_R, // 円環の大半径
float ratio, // 円環の大半径と小半径の比
float theta, // 爪の幅に対応する角度
color fill_color // 爪の色
){
float annulus_r = annulus_R * ratio; // 円環の小半径
float semicircle_radius = (annulus_R - annulus_r)/2.0; // 円環上に並ぶ半円の半径
float mid_radius = annulus_r + semicircle_radius; // 円環の真ん中の半径
fill(fill_color);
float center_x = mid_radius;
float center_y = 0.0;
float x, y;
beginShape();
for(int j=0; j<=100; j++){
x = center_x + semicircle_radius * cos(radians(180)+j*radians(180)/100.0);
y = center_y + semicircle_radius * sin(radians(180)+j*radians(180)/100.0);
vertex(x, y);
}
for(int j=1; j<10; j++){
x = annulus_R * cos(j * theta / 10.0);
y = annulus_R * sin(j * theta / 10.0);
vertex(x, y);
}
center_x = mid_radius * cos(theta);
center_y = mid_radius * sin(theta);
for(int j=0; j<=100; j++){
x = center_x + semicircle_radius * cos(theta-j*radians(180)/100.0);
y = center_y + semicircle_radius * sin(theta-j*radians(180)/100.0);
vertex(x, y);
}
for(int j=1; j<10; j++){
x = annulus_r * cos(theta - j * theta / 10.0);
y = annulus_r * sin(theta - j * theta / 10.0);
vertex(x, y);
}
endShape(CLOSE);
}
// 爪の形状を描く関数(グラデーションあり)
void drawNail_grad(
float annulus_R, // 円環の大半径
float ratio, // 円環の大半径と小半径の比
float theta, // 爪の幅に対応する角度
color color_out, // 爪の外側の色
color color_in // 爪の内側の色
){
float annulus_r = annulus_R * ratio; // 円環の小半径
float semicircle_radius = (annulus_R - annulus_r)/2.0; // 円環上に並ぶ半円の半径
float mid_radius = annulus_r + semicircle_radius; // 円環の真ん中の半径
float center_x, center_y;
float x, y, r, r_theta;
for(int i=0; i<=50; i++){
color col = lerpColor(color_out, color_in, i/50.0); // 色
fill(col);
beginShape();
center_x = mid_radius;
center_y = 0.0;
for(int j=-(50-i); j<=(50-i); j++){
x = center_x + semicircle_radius * cos(radians(270)+j*radians(180)/100.0);
y = center_y + semicircle_radius * sin(radians(270)+j*radians(180)/100.0);
vertex(x, y);
}
x = center_x + semicircle_radius * cos(radians(270)+(50-i)*radians(180)/100.0);
y = center_y + semicircle_radius * sin(radians(270)+(50-i)*radians(180)/100.0);
r = sqrt(x*x + y*y);
r_theta = atan2(y,x);
for(int j=1; j<10; j++){
x = r * cos(r_theta + j * theta / 10.0);
y = r * sin(r_theta + j * theta / 10.0);
vertex(x, y);
}
center_x = mid_radius * cos(theta);
center_y = mid_radius * sin(theta);
for(int j=-(50-i); j<=(50-i); j++){
x = center_x + semicircle_radius * cos(theta - radians(90) - j*radians(180)/100.0);
y = center_y + semicircle_radius * sin(theta - radians(90) - j*radians(180)/100.0);
vertex(x, y);
}
x = center_x + semicircle_radius * cos(theta - radians(90) - (50-i)*radians(180)/100.0);
y = center_y + semicircle_radius * sin(theta - radians(90) - (50-i)*radians(180)/100.0);
r = sqrt(x*x + y*y);
r_theta = atan2(y,x);
for(int j=1; j<10; j++){
x = r * cos(r_theta - j * theta / 10.0);
y = r * sin(r_theta - j * theta / 10.0);
vertex(x, y);
}
endShape(CLOSE);
}
}