正方形を以下のように再帰的に描画していくと、対数らせんが現れます。
この性質を、Processingを用いて確認してみましょう。

その前に、対数らせんについて簡単に紹介します。
###対数らせん
####定義
座標平面上で極座標 $(r, \theta )$ を考えます。正の定数 $a( ≠ 1)$ に対して、方程式
r=a^{\theta} … (*)
をみたす点全体からなる図形を対数らせんと呼びます。この図形はうずまき状の曲線となります。対数らせん上の点の偏角を $\theta$ 移動させると、動径 $r$ が $a$ 倍されることが $(*)$ からわかります。逆にこのような関係が成り立つ点全体を集めたものは、対数らせんになります。
####自己相似性
対数らせんを原点を中心に回転した図形は、もとの対数らせんを拡大/縮小した対数らせんと一致します。このような性質を自己相似性といいます。
この自己相似性は、対数らせんを定める方程式から次のようにしてわかります。まず、偏角 $\theta$ , 動径 $r$ に対して方程式 $r=a^\theta$ で定まる対数らせんを考えます。この対数らせん上の点で、偏角を $b$ 増やしてみましょう。指数の性質から動径は $a^\theta a^b$ に変化します。これはもとの動径の $a^b$ 倍になります。つまり、偏角を $b$ だけ増やした点はもとの対数らせんを $a^b$ 倍した対数らせん上の点になっています。
###正方形を再帰的に描画する
再帰的に四角形の描画をするプログラムを紹介します。キーが押されるたびに内接する正方形のずれが変化し、徐々に対数らせんのようなものが見えてきます。(ずれは0.5~0.049999923の範囲で変化)正方形を塗りつぶす色は指定した範囲内で色相のみランダムになるようにしています。こうすることで、色味を統一しながらランダムに色をつけることができます。
ベクトルの操作には、PVectorクラスが使われます。
【ベクトル演算に用いられる主なメソッド】
- 足し算:
add() - 引き算:
sub() - スカラーをかける:
mult() - スカラーで割る:
div()
PVector[] vec; //PVector型の配列vecを宣言
float gap = 0.5; //内接する正方形のずれを表す変数"gap"の初期値を設定
float x = 0.05;
void setup(){
size(600,600);
colorMode(HSB, 360, 100, 100);
background(360, 0, 100);
vec = new PVector[4]; //4つのベクトルを生成
setVec(vec); //ベクトルの指定
}
void draw(){
stroke(220, 0, 100, 30);
fill(random(200,260), 55, 95, 10);
drawSquare(vec); //四角形の描画
vec = getVec(vec); //ベクトルをgapの分だけずらす
}
- ベクトルの指定を行う関数
生成した4つのベクトルの指定を行います。
void setVec(PVector[] v){
v[0] = new PVector(0, 0); //左上の角
v[1] = new PVector(width, 0); //右上の角
v[2] = new PVector(width, height); //右下の角
v[3] = new PVector(0, height); //左下の角
}
- ベクトルをgapの分だけずらす関数
PVector[] getVec(PVector[] vec){
PVector[] nextVec = new PVector[4]; //4つの新たなベクトルを生成
for(int i = 0; i < 4; i++){
PVector dir = PVector.sub(vec[(i + 1) % 4], vec[i]); //2頂点間の方向ベクトル
dir.mult(gap); //内接する正方形のずれを方向ベクトルにかける
nextVec[i] = PVector.add(vec[i], dir); //新たなベクトルの生成(元の頂点の位置ベクトルをずらす)
}
return(nextVec);
}
- 四角形を描画する関数
生成された4つのベクトルのx座標、y座標から4つの頂点を指定することで多角形の描画に用いられるquad()により四角形を描画します。
void drawSquare(PVector[] v){
quad(v[0].x,v[0].y,v[1].x,v[1].y,v[2].x,v[2].y,v[3].x,v[3].y);
}
- キーが押されるたびに呼び出される関数
キーが押されるたびにキャンバスをクリアし、内接する正方形のずれを更新(その度コンソールにずれを表示)、ベクトルも再指定します。
void keyPressed(){
background(360, 0, 100);
gap = (1-x) / 2;
println("gap =", gap);
setVec(vec);
if (x < 0.9){
x += 0.05;
}else{
x = 0;
}
}
出力例 (実際にはキーを押すことでずれの値が更新されます)

参考: 『数学から創るジェネラティブアート』4.3.1 コード4.4~6
自己相似性が確認できると同時に、だんだんと対数らせんが浮かび上がってくるのが見てとれると思います。
ではなぜ、四角形を再帰的に描画していくと対数らせんが現れるのでしょうか。
上の図のような一辺の長さが1の正方形 $ABCD$ と、それに内接する一辺の長さが $l$ の正方形 $A'B'C'D'$ を考えてみましょう。$\angle AD'A' = \alpha$, ずれの値は $g$、つまりAA'=gとし対角線の交点は点 $O$ とします。
このとき $\triangle AOD \sim \triangle A'OD'$ であるため $AO : A'O = 1 : l$ となり、 $A'O = l (AO)$ が成り立ちます。ここで、線分$AO$と線分$A'D'$の交点をEとします。対頂角であるから $\angle AED' =\angle A'EO$ 。また、$\angle DAC=\angle D'A'C'(={\frac{\pi }{4}})$ であり、2組の角がそれぞれ等しいため $\triangle AD'E \sim \triangle A'OE $ であることがわかります。よって $\angle AD'E = \angle AOA' = \alpha$ が成り立ちます。
以上より、点 $O$ を中心に点 $A$ から点 $A'$ へと $\alpha$ 進めると点 $O$ からの距離が $l$ 倍になるということがわかります。したがって、この頂点の軌跡は偏角 $\alpha$ , 動径 $ l^{\frac{\theta}{\alpha}} $ の対数らせんを描きます。
$AO = r_0$ , $A'O = r_1 $ , $AO$ の角度を $\theta_0$ , $A'O$ の角度を $\theta_1$として確認してみましょう。
\begin{align}
r_1 &= l^{\frac{\theta_1}{\alpha}} \\
&= l^{\frac{\theta_0 + \alpha}{\alpha}} \\
&= l^{\frac{\theta_0}{\alpha} + 1 } \\
&= l \times l^{\frac{\theta_0}{\alpha}}\\
&= l \bigl(r_0\bigr) \\
\end{align}
頂点の軌跡が偏角 $\alpha$ , 動径 $ l^{\frac{\theta}{\alpha}} $ の対数らせんと一致することが確認できました。
###応用
再帰的に四角形の描画をするプログラムを少し改変すると違った図形をつくることもできます。
先ほど紹介した、四角形を描画する関数のdrawSquareを以下のように変更してみてください。
void drawSquare(PVector[] v){
rectMode(CORNERS);
rect(v[0].x,v[0].y,v[2].x,v[2].y);
rect(v[1].x,v[1].y,v[3].x,v[3].y);
for (int i=0; i < 4; i++){
noStroke();
fill(0, 0, 100, 60);
ellipse(v[i].x,v[i].y,5,5);
}
キーを押していくとだんだんと図形が変化し、徐々に対数らせんが見えてきます。
先ほどと同様に、自己相似性も見られます。
参考文献
-
巴山 竜来『数学から創るジェネラティブアート』技術評論社、2019年
-
Casey Reas、Ben Fry、船田 巧(翻訳)『Processingをはじめよう 第2版』
オライリージャパン、2016年


