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

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

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

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

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

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

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

対称性について

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

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

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

五角形の準備

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

Type 13の五角形

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

この五角形を描くために、以下のように五角形に対して補助線を入れてみます。

五角形への補助線

つまり、辺AEの延長線に対して頂点Dからの垂線が交わる点をFとします。

このとき、∠EDFを\(\alpha\)とおくと、\[ \alpha = E-90^{\circ} = B-90^{\circ} \]となります。

あとは、頂点Bの角度と辺BCの長さを指定することで描くことができます。考え方としては、頂点Bを原点とし、直線ABが\(x\)軸(右向きを正)、直線ABと垂直の向きに\(y\)軸(下向きを正)を設定して、それぞれの頂点の座標を決めていくようにしていきます。

辺BCの長さ\(r_1\)と角度Bを指定すると、頂点Cの座標を決めることができます。角度Cは90°で、辺CDの長さは\(r_1\)ですので、頂点Dの座標も決まります。頂点Dから左の向き(\(x\)軸の負の向き)で長さが\(2r_1\)のベクトルを、角度\(\alpha\)だけ反時計回りに傾けたベクトルをとると、そのベクトルの終点が頂点Eになります。最後に、辺AEと辺ABが垂直に交わっていることを考慮すると、頂点Aの座標は、\(x\)座標が頂点Eの\(x\)座標と同じで、\(y\)座標が頂点Bの\(y\)座標と同じになります。以上で、五角形のすべての頂点の座標が決まります。(今回作成したソースコードではそのあと、原点が頂点Aになるようにシフトしています。)

基本図形の準備

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

基本図形

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

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

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

長方格子の準備

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

長方格子の位置

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

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

ソースコード

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

PVector[][] lattice; // 格子点ベクトル
PShape tile; // タイル
PVector[] base = new PVector[2]; // 格子を張るベクトル
float pentagon_size = 50.0;
float pentagon_angle = 110.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 / base[0].x); // 列数
  int row_num = ceil(height / 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] = v.copy();
    }
  }
}

// 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); // グループに追加
            
  float angle = radians(90.0); 
  pentagons = makeFundamentalShape(); // 2つの五角形で構成される図形の生成
  pentagons.rotate(angle);
  pentagons.translate(-base[0].x-base[1].x, -base[0].y-base[1].y); // 基本図形の位置を調整
  tile.addChild(pentagons); // グループに追加
      
  pentagons = makeFundamentalShape(); // 2つの五角形で構成される図形の生成
  pentagons.rotate(-angle);
  pentagons.translate(base[0].x + base[1].x, base[0].y + 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); // タイルを描画
      }
    }
  }
}