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

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

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

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

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

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

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

対称性について

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

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

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

五角形の準備

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

Type 11の五角形

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

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

五角形の分割

辺CDの長さを\(r_1\)(つまり、辺DEの長さも\(r_1\))、辺AEの長さを\(r_2\)、辺ABの長さを\(r_3\)、辺BCの長さを\(r_4\)とおき、\( \angle AEB \)を\(\alpha\)とおきます。

角Dを与えると、Type 11の五角形の条件式と\( A+B+C+D+E=540^{\circ} \)により、\[ A=90^{\circ}, \ \ B=270^{\circ}-D, \ \ C=2D-180^{\circ}, \ \ E=360^{\circ}-2D \]となります。また、△CDEは二等辺三角形なので、\[ \angle DCE = \angle DEC = 90^{\circ}-\frac{D}{2} \]となります。

△BCEに注目します。まず、それぞれの頂点の角度は、上記の式から\[ \angle CBE = 180^{\circ}-D+\alpha, \ \ \angle BCE = \frac{5}{2}D-270^{\circ}, \ \ \angle BEC = 270^{\circ} – \frac{3}{2}D – \alpha \]となります。一方、辺の長さは、△CDEが二等辺三角形なので、\[ CE=2 r_1 \sin \frac{D}{2} \]となり、△ABEが直角三角形なので\[BE=\frac{r_2}{\cos \alpha} \]となり、Type 11の五角形の条件式より\[ BC = r_4 = r_1 – 2 r_2 \]となります。したがって、△BCEに正弦定理を適用すると、\[ \frac{CE}{\sin \angle CBE} = \frac{BE}{\sin \angle BCE} = \frac{BC}{\sin \angle BEC} \]となります。この関係式に上記で算出した角度や長さの式を代入して整理すると、\[ \tan \alpha = \frac{3 \sin 2D – 2 \sin 3D}{2 \cos D – \cos 2D}, \ \ r_2 = \frac{ \cos \frac{5}{2} D }{ 2 \cos \frac{5}{2} D – \cos \frac{3}{2} D + \sin \frac{3}{2} D \tan \alpha } r_1 \]が得られます。

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

基本図形の準備

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

基本図形

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

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

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

長方格子の準備

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

長方格子の位置

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

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

ソースコード

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

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

void setup(){

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

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

  float r1 = pentagon_size;
  float A = radians(90.0);
  float D = radians(pentagon_angle);
  float B = radians(270.0)-D;
  float C = 2.0 * D - radians(180.0);
  float E =radians(360.0)-2.0 * D;
  
  float tanalpha = (3.0 * sin(2.0 * D) - 2.0 * sin(3.0 * D)) / (2.0 * cos(D) - cos(2.0 * D));
  float r2 = r1 * cos(2.5 * D) / (2.0 * cos(2.5 * D) - cos(1.5 * D) + sin(1.5 * D) * tanalpha);
  float r3 = r2 * tanalpha;
  float r4 = r1 - 2.0 * r2;

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

  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[0], 3, pentagon_angle);
  
  base[0] = new PVector( vforbase[1][3].copy().add( vforbase[1][4].copy().sub(vforbase[1][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[1][3].copy().add( v[1][4].copy().sub(v[1][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); // タイルを描画
      }
    }
  }
}