F02 ペンローズタイリング
ペンローズのカイトとダートを使って平面を埋め尽くす方法はいろいろありますが,そのひとつです。ただし,カイト・ダートを増やしていくのではなく,有限な領域を,カイトとダートを縮小して埋めます。
出発点は1本の線分です。この線分からカイトとダートを作り,それを縮小していきます。
アルゴリズムは,「GeoGebra日本」の「GeoGebra実例」「F02 ペンローズタイリング」のものをそのまま借用します。というより,同ページのものをCinderellaに移植する,というほうが正確でしょう。ただ,同ページの手順をそのまま踏んでいくのですが,GeoGebraでそのままやっていっても,何をしているのか分かりにくいところもあるので,適宜補足していきます。また,見出しの表現が若干異なります。
カイトとダートの頂点のリストを返す関数の作成
線分の両端の点を与えると、収縮に用いるカイトとダートのリストを返す関数 defkk(), defkd(), defdk(),defdd() を作ります。 DeflKK1, DeflKD1, DeflDK1, DeflDD1 に相当します。
GeoGebraでツールを作るというのは,CinderellaではCindyscriptのユーザー定義関数を作ることに相当します。
まず,2点 k1,k2 に対し,k1をk2の周りに回転するのですが,Cindyscriptに用意されている rotate() 関数は座標系の回転なので,新たに関数 rot() を作ります。回転は複素数を利用します。
//点p をp0の周りにthだけ回転した点の座標を返す
rot(p,th,p0):=(
z1=complex(p);
z2=complex(p0);
z=z2+(z1-z2)*(cos(th)+i*sin(th));
gauss(z);
);
これを用いて,ツールKK,KD,DK,DD に相当する関数を作ります。なお,こちらは小文字です。
// 2点p1,p2からkk,kd,dk,dd を作る
defkk(k1,k2):=(
k5=rot(k1,-pi/5,k2);
k6=rot(k1,-2*pi/5,k2);
k7=rot(k6,2*pi/5,k5);
[k5,k7,k5,k1];
);
defkd(k1,k2):=(
k5=rot(k1,-pi/5,k2);
k6=rot(k1,-2*pi/5,k2);
[k5,k6];
);
defdk(d1,d2):=(
d4=rot(d1,-3*pi/5,d2);
d5=rot(d2,-pi/5,d4);
d6=rot(d5,-4*pi/5,d2);
[d2,d6];
);
defdd(d1,d2):=(
d4=rot(d1,-3*pi/5,d2);
d5=rot(d2,-pi/5,d4);
DD=[d2,d5];
);
この中から,defkk() の働きを確かめておきましょう。
forall([[1,1],[3,2]],draw(#,color->[1,0,0],size->5));
forall(defkk([1,1],[3,2]),draw(#));
2点(1,1) と (3,2) に対してdefkk() で作成される点を表示します。元の点は赤,できた点は緑です。
(1,1) が共通です。
他も同様にできます。
defkd() derdk() defdd()
点の番号は,GeoGebra実例のページの末尾にあるカイトとダートの図に対応します。その中から,収縮に用いるベクトルを表すリストを作っています。
これらの点を結ぶとカイトとダートの図の一部ができます。すべての辺を描いても,並べて描いていくと重複するわけですから,これだけでよいというわけですね。
タイルのリストを収縮する関数の作成
カイトの点のリストとダートの点のリストをリストにして与えると、収縮して現れるカイトのリストとダートのリストを返す関数を作成します。ツール Defl に相当します。
defl(kdlist):=(
klist=kdlist_1;
dlist=kdlist_2;
kks=[];
repeat(length(klist)/2,s,step->2,
kks=kks++defkk(klist_s,klist_(s+1));
);
kds=[];
repeat(length(klist)/2,s,step->2,
kds=kds++defkd(klist_s,klist_(s+1));
);
dks=[];
repeat(length(dlist)/2,s,step->2,
dks=dks++defdk(dlist_s,dlist_(s+1));
);
dds=[];
repeat(length(dlist)/2,s,step->2,
dds=dds++defdd(dlist_s,dlist_(s+1));
);
[kks++dks,kds++dds];
);
GeoGebraの Sequence[] は繰り返しですので,Cindyscriptでは repeat() です。2つずつとるので,step->2 にしています。
GeoGebraでの Join[] はリストの結合なので,Cindyscriptでは演算子 ++ になります。
GeoGebraと異なるのは,はじめに空リストを用意してから,新しくできるリストを ++ でつないでいくことです。
さて,やっていることを確認するために,カイトとダートのリストを
kdlist=[[[2,3],[4,4]],[[2,6],[4,7]]];
として,defkk() のときのように色分けして表示してみます。
赤がカイトの線分の両端,緑ができた点,黄色がダートの両端の線分,黒ができた点です。
さっぱりわからないので,カイトとダートの全体を表示するスクリプトを作ってみましょう。ここでは混乱を避けるためにそのスクリプトは示しません。(この下からダウンロードできるファイルには含まれています。)
また,位置もGeoGebra実例のものとは変えています。
この図と,GeoGebra実例のページの最後にあるアルゴリズムの図を見比べれば,やっていることの意味がわかります。
また,この関数に渡す引数は,「カイトの点のリストとダートの点のリストのリスト」であることに注意が必要です。つまり,複数のカイトとダートの点のリストを,まとめて渡すとまとめて処理されて返されるのです。
タイル1枚を与えるとその線分を描く関数の作成
線分の両端を与えると,カイトとダートの一部をそれぞれ描く関数です。一部を描く意味は,前述の通り,全部描かなくても連続して描けば,結果として全体が描かれることになるからです。このことは,GeoGebra実例にある「仕様」です。
GeoGebraでは,「線分のリストを返す」となっていますが,GeoGebra の Segment[] は実際には線分を描くことになるので,Cindyscriptでは「リストを返す」のではなく,実際に線分を描かなくてはなりません。
// カイトとダートの線分を描く
kseg(k1,k2):=(
k5=rot(k1,-pi/5,k2);
k6=rot(k1,-2*pi/5,k2);
k3=rot(k5,-3*pi/5,k6);
draw(k1,k2);
draw(k2,k3);
);
dseg(d1,d2):=(
d4=rot(d1,-3*pi/5,d2);
draw(d1,d2);
draw(d1,d4);
);
どの部分が描かれるのか,全体と比べてみます。それぞれ,左が全体,右がこれらで描かれるものです。
kseg() dseg()
タイルのリストを与えるとそれらを描く関数の作成
カイトのリストとダートのリストをリストで与えると、それらを描画する関数 (penseg()) を作成します。
このリストはそれぞれ複数のリストです。要は,リストから2つずつをとって,kseg() と dseg() で描いていくだけです。
penseg(ksds):=(
ks=ksds_1;
ds=ksds_2;
repeat(length(ks)/2,s,step->2,
kseg(ks_s,ks_(s+1));
);
repeat(length(ds)/2,s,step->2,
dseg(ds_s,ds_(s+1));
);
);
ペンローズタイリングの関数の作成
2点を与えるとペンローズタイリングを描く関数 (penrose()) を作成します。
GeoGebra実例では,Defl[] を5回適用して描いていますが,Cindyscriptでは繰り返し回数を引数で与える形にします。
2点A,Bをとってからこの関数を使います。(実際には点をとらずに座標を与えてもよい)
まず,Aを中心とする半径ABの円周上に等間隔に10個の点をとります。中心角が pi/5 の扇形の点です。これをカイトの初期の点リストとし,ダートの点リストはなしにします。このカイトとダートの点のリストが,GeoGebra実例ではNですが,ここでは sun にします。
sun に deal() をn回適用します。できたリストを penseg()で描画します。
penrose(p1,p2,n):=(
sun=[apply(1..10,rot(p2,pi/5*#,p1)),[]];
n2=defl(sun);
repeat(n-1,s,
n2=defl(n2)
);
penseg(n2);
);
n=1 のとき penrose(A.xy,B,xy,1) n=5 のときpenrose(A.xy,B,xy,5)
なお,kseg() dseg() ではカイトとダートの一部だけを描いていますが,全体を描くようにすることもできます。その場合はこれより細かい図になります。