1. ホーム
  2. 壁紙アート
  3. 五角形によるタイリング
  4. 五角形によるタイリング(Type 12)

ここでは、15タイプある五角形によるタイリングのうち、Type 12のタイリングを再現してみました。なお、今回のタイリングを作成するにあたって、杉本晃久さんのサイトで紹介されている「凸五角形タイル張り問題」を参考にしました。

五角形によるタイリング(Type 12)

今回、再現した五角形によるタイリング(Type 12)は以下のような図形になります。

五角形によるタイリング(Type 12)

以下で、この五角形によるタイリング(Type 12)の描き方について解説していきます。

五角形によるタイリング(Type 12)の描き方

対称性について

まず、この五角形によるタイリング(Type 12)の対称性について考えます。

五角形によるタイリング(Type 12)の対称性

上図のように、タイリングのパターンをじっくり見ていくと、上図の赤丸の周りの180°回転に対して図形は回転対称になっていることが分かり、そして縦横の赤色の点線の軸に対して図形はすべり鏡映を持っていることがわかります。また、黒色の点を格子としてみると、近接する4つの黒色の点同士が長方形を形作っていることから、黒色の点は長方格子になっていることが分かります。これらをまとめると、五角形によるタイリング(Type 12)は、壁紙群PGGの対称性を持つことが分かります。さらに言えば、アイソヘドラルタイリングIH6(PGG)に分類されます。

五角形の準備

今回利用する五角形は以下のような図形になります。

Type 12の五角形

五角形の角度のうち、上図の角度Aが90°、角度Cと角度Eの和が180°、角度Bの2倍と角度Cの和が360°になるようにとります。また、辺AEの長さの2倍と辺CDの長さ、および辺BCの長さと辺DEの長さの和が同じになるようにとります。

この五角形を描くために、以下のように五角形を分割すると分かりやすくなります。

五角形の分割

辺AEの長さを\(r_1\)、辺ABの長さを\(r_2\)、辺BCの長さを\(r_3\)、辺CDの長さを\(r_4\)、辺DEの長さを\(r_5\)とおき、\( \angle ABE \)を\(\alpha\)、\( \angle DBE \)を\(\beta\)とおきます。

角Cを与えると、Type 12の五角形の条件式と\( A+B+C+D+E=540^{\circ} \)により、\[ A=90^{\circ}, \ \ B=180^{\circ}-\frac{C}{2}, \ \ D=90^{\circ}+\frac{C}{2}, \ \ E=180^{\circ}-C \]となります。また、辺の長さ\(r_1\)を与えると、\[ r_2 = \frac{r_1}{\tan \alpha}, \ \ r_4 = 2 r_1 \]となります。\(r_5=2 r_1 – r_3\)となるので、あとは、角度\(\alpha\)と長さ\(r_3\)がわかれば、Type 12の五角形を決定することができます。

△BDEに注目すると、正弦定理より\[ \frac{BD}{\sin \angle BED} = \frac{ED}{\sin \angle DBE} = \frac{BE}{\sin \angle BDE} \]となります。それぞれの頂点の角度は、上記の式から\[ \angle BED = 90^{\circ}-C+\alpha, \ \ \angle DBE = \beta, \ \ \angle BDE = 90^{\circ} + C – \alpha – \beta \]となり、辺BEの長さは\[ BE = \frac{r_1}{\sin \alpha} \]となります。

一方、△BCDに注目すると、正弦定理より\[ \frac{BD}{\sin \angle BCD} = \frac{BC}{\sin \angle BDC} = \frac{CD}{\sin \angle CBD} \]となります。それぞれの頂点の角度は、上記の式から\[ \angle BCD = C, \ \ \angle BDC = \alpha + \beta – \frac{C}{2}, \ \ \angle CBD = 180^{\circ} – \frac{C}{2} – \alpha – \beta \]となります。

これらの関係式に上記で算出した角度や長さの式を代入して整理すると、\[ \tan \alpha = \frac{\cos C + \sin \frac{C}{2}}{4 \sin \frac{C}{2} \sin C + \sin C – \cos \frac{C}{2}}, \ \ \tan \beta = \frac{\cos \alpha \cos (\alpha – C)}{\sin \alpha \cos (\alpha – C) + \cos \frac{C}{2}}, \\ r_3 = \frac{ 2 \sin ( \alpha + \beta – \frac{C}{2} ) }{ \sin ( \alpha + \beta + \frac{C}{2} )} r_1 \]が得られます。

以上より、Type 12の五角形は頂点Cの角度と辺AEの長さを指定することで描くことができます。

基本図形の準備

次に、基本図形を準備します。今回は、以下のような組合せを基本図形とします(組合せは他にもあります)。

基本図形

この基本図形を実際に描いていくための手順を示しておきます。

  1. ⓪の図形を描きます。
  2. ⓪の五角形の辺ABに対して線対称に反転することで①の五角形を描きます。

なお、壁紙群PGGの対称性に合わせて基本図形を並べていく際に、今回は⓪の五角形の頂点BとCの中点と⓪の五角形の頂点DとEの中点(上図の黒丸)を長方格子点と合うようにしています。

長方格子の準備

上記で説明したように、上図の黒丸が長方格子点と一致するようにとりますので、それに伴い、長方格子の形状も決まってきます。つまり、長方格子を張る2つのベクトルのうち1つは、⓪の五角形の頂点DとEの中点から⓪の五角形の頂点BとCの中点へのベクトルとなります。もう一つのベクトルは①の五角形を頂点D周りで角度Dだけ反時計回りに回転させた五角形②を準備すると、①の五角形の頂点DとEの中点から②の五角形の頂点BとCの中点へのベクトルとなります。

長方格子の位置

壁紙群PGGの対称性に合わせて並べる

ここまで準備ができたら、あとは壁紙群PGGの対称性に合わせて基本図形を並べていくことで五角形によるタイリングを行うことができます。

ソースコード

今回再現した五角形によるタイリング(Type 12)のプログラムのソースコードを示しておきます。

PVector[][] lattice; // 格子点ベクトル
PShape tile; // タイル
PVector[] base = new PVector[2]; // 格子を張るベクトル
float pentagon_size = 50.0;
float pentagon_angle = 60.0; // Type 12の五角形の標準角度(頂点Cの角度)

void setup(){

  size(1000, 1000, P2D);
  noFill();
 
  makeRectVector(); // 長方格子を張るベクトルの生成
  makeLattice(); // 格子点ベクトルを生成
  makeTilePGG(); // タイルを生成
  drawTiling(); // タイリングを描画
}

// 五角形の頂点を取得する関数
PVector[] getPentagon(){

  float r1 = pentagon_size;
  float A = radians(90.0);
  float C = radians(pentagon_angle);
  float B = radians(180.0)-C/2.0;
  float D = radians(90.0)+C/2.0;
  float E =radians(360.0)-2.0 * D;
  
  float tanalpha = (cos(C) + sin(C / 2.0)) / (4.0 * sin(C / 2.0) * sin(C) + sin(C) - cos(C / 2.0));
  float alpha = atan(tanalpha);
  float tanbeta = cos(alpha) * cos(alpha - C) / (sin(alpha) * cos(alpha - C) + cos(C / 2.0));
  float beta = atan(tanbeta);
  float r2 = r1 / tanalpha;
  float r3 = r1 * 2.0 * sin(alpha + beta - C/2.0) / sin(alpha + beta + C/2.0);
  float r4 = 2.0 * r1;
  float r5 = 2.0 * r1 - r3;

  PVector[] v = new PVector[5];
  v[2] = PVector.fromAngle(-radians(180.0)+B).mult(r3/2.0);
  v[1] = PVector.fromAngle(B).mult(r3/2.0);
  v[0] = v[1].copy().add( PVector.fromAngle(radians(180.0)).mult(r2) );
  v[4] = v[0].copy().add( PVector.fromAngle(-radians(90.0)).mult(r1) );
  v[3] = v[4].copy().add( PVector.fromAngle(-E+radians(90.0)).mult(r5) );

  return v;
}

// 五角形を指定された軸に対して鏡映する関数
PVector[] getReflection(
  PVector[] v, // 鏡映する元の図形の頂点座標
  int index1,  // 鏡映軸の指定するための始点位置のindex
  int index2   // 鏡映軸の指定するための終点位置のindex
){
  PVector[] ref = new PVector[5];
 
  PVector axis = v[index1].copy().sub(v[index2].copy());
  for(int i=0; i<5; i++){
    PVector temp = v[i].copy().sub(v[index1].copy());
    float t = temp.dot(axis)/ axis.dot(axis);
    PVector p = v[index1].copy().add( axis.copy().mult(t) );
    ref[i] = p.copy().mult(2.0).sub(v[i].copy());
  }
  
  return ref;
  
}

// 五角形を指定された点の周りで回転する関数
PVector[] getRotation(
  PVector[] v, // 回転する元の図形の頂点座標
  int index,   // 回転軸の座標位置のindex
  float angle  // 回転角度
){
  PVector[] rot = new PVector[5];
 
  for(int i=0; i<5; i++){
    PVector temp = v[i].copy().sub(v[index].copy());
    PVector p = temp.copy().rotate(radians(angle));
    rot[i] = v[index].copy().add(p.copy());
  }
  
  return rot;
  
}

// 長方格子を張るベクトルを生成する関数
void makeRectVector(){
  PVector[][] vforbase = new PVector[3][5]; // 長方格子を張るベクトルを求めるための3つの五角形の座標
  vforbase[0] = getPentagon();
  vforbase[1] = getReflection(vforbase[0], 0, 1);
  vforbase[2] = getRotation(vforbase[1], 3, -90.0-pentagon_angle/2.0);

  base[0] = new PVector( vforbase[0][3].copy().add( vforbase[0][4].copy().sub(vforbase[0][3].copy()).mult(1.0/2.0) ).mag(), 0.0);
  base[1] = new PVector(0.0, vforbase[2][3].copy().add( vforbase[2][4].copy().sub(vforbase[2][3].copy()).mult(1.0/2.0) ).mag() );
}

// 長方格子を生成する関数
void makeLattice(){
  int col_num = ceil(width / abs(base[0].x)); // 列数
  int row_num = ceil(height / abs(base[1].y)); // 行の数
  lattice = new PVector[col_num + 1][row_num + 1];
  for (int i = 0; i < col_num + 1; i++){
    for (int j = 0; j < row_num + 1; j++){
      PVector v = PVector.mult(base[0], i); 
      v.add(PVector.mult(base[1], j));
      lattice[i][j] = new PVector(v.x, v.y);
    }
  }
}

// 2つの五角形から構成される基本図形を生成する関数
PShape makeFundamentalShape(){
  PVector[][] v = new PVector[2][5]; // 基本図形を構成する2つの五角形の座標
  // 2つの五角形のそれぞれの座標を取得
  v[0] = getPentagon();
  v[1] = getReflection(v[0], 0, 1);

  PVector lat = v[0][3].copy().add( v[0][4].copy().sub(v[0][3].copy()).mult(1.0/2.0) );
  float angle = PVector.angleBetween(lat, new PVector(-1.0, 0.0));
  for(int i=0; i<5; i++){
    v[0][i].rotate(-angle);
    v[1][i].rotate(-angle);
  }
  
  // 五角形を2つ描いて1つのグループにまとめる
  PShape pentagons = createShape(GROUP);
  for(int i=0; i<2; i++){
    PShape pentagon = createShape();
    pentagon.beginShape(); 
    for(int j=0; j<5; j++){
      pentagon.vertex(v[i][j].x, v[i][j].y);
    }
    pentagon.endShape(CLOSE);
    pentagons.addChild(pentagon);
  }
  
  return pentagons;
}

// タイルを生成する関数
void makeTilePGG(){
  tile = createShape(GROUP); // PShapeのグループを作る

  PShape pentagons = makeFundamentalShape(); // 2つの五角形で構成される図形の生成
  tile.addChild(pentagons); // グループに追加
      
  pentagons = makeFundamentalShape(); // 2つの五角形で構成される図形の生成
  pentagons.scale(pow(-1,1),pow(-1,1)); // 図形の反転
  tile.addChild(pentagons); // グループに追加
      
  pentagons = makeFundamentalShape(); // 2つの五角形で構成される図形の生成
  pentagons.scale(pow(-1,1),pow(-1,0)); // 図形の反転
  pentagons.translate(-base[0].x, base[1].y); // 図形の位置を調整
  tile.addChild(pentagons); // グループに追加
      
  pentagons = makeFundamentalShape(); // 2つの五角形で構成される図形の生成
  pentagons.scale(pow(-1,0),pow(-1,1)); // 図形の反転
  pentagons.translate(base[0].x, -base[1].y); // 図形の位置を調整
  tile.addChild(pentagons); // グループに追加

}

// 格子形状に合わせたタイリングを描画する関数
void drawTiling(){
//  background(255);
  for (int i=0; i<lattice.length; i++){
    for (int j=0; j<lattice[0].length; j++){
      if( i%2 == 0 && j%2 == 0 ){
        tile.resetMatrix();
        tile.translate(lattice[i][j].x, lattice[i][j].y); // タイルの位置を指定
        shape(tile); // タイルを描画
      }
    }
  }
}

考察

Type 12の五角形を準備するとき、今回基準となる角度として\(C=60^{\circ}\)と取りました。その結果、△BCDが角度30°、60°、90°をもつ直角三角形になりました。

Type 12の五角形

そこで、ちょっと仮説を立ててみました。

\( \angle CBE \)は角度Cの取り方によらずに90°になる?

これについて、ちょっと実験してみました。以下の図は左が\(C=50^{\circ}\)としたときの五角形、右が\(C=70^{\circ}\)としたときの五角形になります。

∠CBDについての考察

これらの図形を眺めてみると、\( \angle CBE \)は90°になっているようにも見えます。そこで実際に、\( \angle CBE \)を計算してみました。結果、\[ C=50^{\circ} \ \ \to \angle CBE = 87.52^{\circ}, \ \ C=70^{\circ} \ \ \to \angle CBE = 92.97^{\circ} \]と、微妙に90°からずれていました。というわけで、\( \angle CBE \)が90°になるのは、角度Cが60°のときだけのようです。

考察2

角度Cを30°から80°まで動かして、動画にしてみました。

動画にするには、ソースコードにint型の変数itarationを追加して、以下のようにsetup関数をsetup関数とdraw関数に書き換えます。

PVector[][] lattice; // 格子点ベクトル
PShape tile; // タイル
PVector[] base = new PVector[2]; // 格子を張るベクトル
float pentagon_size = 50.0;
float pentagon_angle = 30.0; // Type 12の五角形の標準角度(頂点Cの角度)
int iteration = 0;

void setup(){
  size(1000, 1000, P2D);
  noFill();
}

void draw(){
  background(204);
  iteration++;
  pentagon_angle += 1-2*((iteration%100)/50); 
  makeRectVector(); // 長方格子を張るベクトルの生成
  makeLattice(); // 格子点ベクトルを生成
  makeTilePGG(); // タイルを生成
  drawTiling(); // タイリングを描画
}