top of page
執筆者の写真NUM

Processing ペンローズ・タイルの実装

更新日:5月10日



最近、タイリングの作品制作にハマっていて、様々な図形にチャレンジしています。

タイリングは知れば知るほど奥深い世界だなと感じています。

今回はペンローズ・タイルについて書いていこうと思います。


目次
  1. ペンローズ・タイルとは?

  2. 黄金三角形

  3. 概要

  4. 実装



ペンローズ・タイルとは?



ペンローズ・タイルとは2種類の菱形を使って作り出す平面充填です。

使用する菱形の形は以下です。

  1. 鋭角72°と鈍角108°の菱形

  2. 鋭角36°と鈍角144°の菱形

正多角形の平面充填の場合、あるパターンに従って図形を並べていけば充填可能ですが、

ペンローズ・タイルの場合、周期的なパターンがないのが特徴です。



黄金三角形

先ほど紹介した2種類の菱形には2種類の黄金三角形によって構成されています。

この章では黄金三角形について説明します。



黄金三角形とは黄金比をφとおくと、辺の比が1:φとなっている2等辺三角形のことです。

αの角が鋭角の方を鋭角黄金三角形、鈍角の方を鈍角黄金三角形というようにします。


概要



ペンローズ・タイルは以下の手順で実施していきます。

  1. 黄金三角形の作成

  2. 1で作成した黄金三角形を2種類の黄金三角形に分割


1,2の手順を繰り返す事でペンローズ・タイルが出来上がります。



実装

それではProcessingでペンローズ・タイルを実装していきます。


実装は大きく分けて3つに分類されます。

  1. 初期化:分割対象となる最初の黄金三角形の作成

  2. 分割:黄金三角形を2種類の黄金三角形に分割

  3. 三角形クラス



 1. 初期化:


void initialize(float scalar){

  PVector v_0 = PVector.fromAngle(3 * PI / 2);
  v_0.mult(scalar);
  
  PVector v_1 = PVector.fromAngle(7 * PI / 10);
  v_1.mult(scalar);
  
  PVector v_2 = PVector.fromAngle(3 * PI / 10);
  v_2.mult(scalar);
  
  listT.add(new Tri(v_0, v_1, v_2));
}
 

fromAngle関数で指定した角度の単位ベクトルを取得。

v_0 は270° 、v_1 は126°、v_2 は54°


取得したベクトルを引数scalarの値で乗算(任意の大きさの黄金三角形を作成できる)

取得したベクトルから三角形のインスタンスを生成し、リストに格納。



 2. 分割:


void triangularDivision() {

  //次の細い三角形のリスト
  ArrayList<Tri> nextT = new ArrayList<Tri>(); 
  //次の太い三角形のリスト
  ArrayList<Tri> nextF = new ArrayList<Tri>(); 
  
  translate(width / 2, height / 2);
  
  //細い三角形が格納されているリストの要素数分、描画と分割
  for (Tri t : listT) {  
    t.divThin(nextT, nextF); 
  }
  
  //細い三角形が格納されているリストの要素数分、描画と分割
  for (Tri t : listF) {  
    t.divFat(nextT, nextF);  
  }
  
  //リストの更新
  listT = nextT;  
  listF = nextF;
}

鋭角黄金三角形のインスタンスが格納されているリストlistTと、

鈍角黄金三角形のインスタンスが格納されているリストlistFの要素数分

分割を行います。


分割して新たにできた2種類の黄金三角形は一時的にリストnextT、nextFに格納された後、

グローバル変数のリストlistT、listFに更新されます。


黄金三角形の分割については3.三角形クラスで詳しく説明します。



 3. 三角形クラス


class Tri {  
  
  //三角形の頂点ベクトル配列
  PVector[] v_;
  
  //黄金数
  float PHI = (1 + sqrt(5)) / 2;
  
  //黄金分割比
  float gDiv = 1/PHI;
  float gDiv2 = (PHI-1)/PHI;
  
  //コンストラクタ
  Tri(PVector v0, PVector v1, PVector v2) {  
    v_ = new PVector[]{v0, v1, v2};
  }

  //三角形の描画
  void drawTriangle(int col) { 
    stroke(255);
    fill(col, 0, 0);
    triangle(v_[0].x, v_[0].y, v_[1].x, v_[1].y, v_[2].x, v_[2].y);
  }
    
  //鋭角黄金三角形を分割する頂点の生成
  void divThin(ArrayList<Tri> nextThin, ArrayList<Tri> nextFat) { 
    
    //鋭角黄金三角形の分割
    PVector v_3 = PVector.mult(v_[0], gDiv2);
    PVector v_4 = PVector.mult(v_[2], gDiv);
    PVector v_5 = PVector.add(v_3, v_4);
    
    //新たに黄金三角形を追加
    nextThin.add(new Tri(v_[1], v_[2], v_5));
    nextFat.add(new Tri(v_5, v_[0], v_[1]));
  }
    
  //鈍角黄金三角形を分割する頂点の生成
  void divFat(ArrayList<Tri> nextThin, ArrayList<Tri> nextFat) { 

    //鈍角黄金三角形の分割
    PVector obV_1 = PVector.mult(v_[2], gDiv2);
    PVector obV_2 = PVector.mult(v_[1], gDiv);
    PVector obV = PVector.add(obV_1, obV_2);

    //鋭角黄金三角形の分割
    PVector acV_1 = PVector.mult(v_[0], gDiv);
    PVector acV_2 = PVector.mult(v_[2], gDiv2);
    PVector acV = PVector.add(acV_1, acV_2);
    
    //新たに黄金三角形を追加
    nextThin.add(new Tri(obV, v_[0], acV));
    nextFat.add(new Tri(obV, v_[0], v_[1]));
    nextFat.add(new Tri(acV, v_[2], obV));
  }
}

三角形クラスは以下の3つの関数を持っています。

  1. 三角形の描画

  2. 鋭角黄金三角形を分割し、新たに鋭角、鈍角黄金三角形を生成

  3. 鈍角黄金三角形を分割し、新たに鋭角黄金三角形を1つ、鈍角黄金三角形を2つ生成


描画に関してはProcessingで標準で用意されているtriangle関数を呼び出しているので、

2,3の関数について説明します。



divThin関数:




△abcの辺acを点dで①鈍角黄金三角形と②鋭角黄金三角形に分割します。

△abcは②の三角形と相似という点がポイントです。


まず辺adと辺cdの比がad : cd = 1 : φ-1という比率の証明をします。


各三角形の辺の比は以下となります。

△abcの辺の比はab : ac : bc = φ : φ : 1

△bcdの辺の比はbc : bd : cd = φ : φ : 1

△dabの辺の比はda : db : ab = 1 : 1 : φ


△abcの辺の比を全体とすると、ad : cd = 1 /φ: φ-1/φ となります。


比率が分かったら次は点dの取得方法です。

点dは内分点の位置ベクトルの公式を使用すると取得できるのでリンクを参照してください。証明もあるので、わかりやすいと思います。


1,2の結果を足し合わせることで、点dを求められます。

  1. ベクトルaに(φ-1/φ)を掛ける

  2. ベクトルcに(1/φ)を掛ける

取得した点dを使用して、新たに三角形2つを生成します。



divFat関数:



divFat関数の鈍角三角形分割も分割比は先ほどと変わらないです。

△①②③を全て合わせた鈍角黄金三角形を△①と△②③に分割します。


△abcの辺の比を全体とすると、ae : be = φ-1/φ : 1 /φ となります。

先ほどの比とは逆ですね。


分割してできた△aedは△abdと相似、△bedは最初の鋭角黄金三角形と相似です。


△bedをdivThin関数の時と同様の手順でさらに分割します。

これで鋭角黄金三角形1つ、鈍角黄金三角形2つができました。



Main:


//鋭角黄金三角形リスト
ArrayList<Tri> listT = new ArrayList<Tri>();  
//鈍角黄金三角形リスト
ArrayList<Tri> listF = new ArrayList<Tri>(); 
//分割回数
int divCount = 8;
//拡大率
int size = 400;

void setup() {
  size(850, 850);
  pixelDensity(2);
  frameRate(10);
  background(255);
  //初期化
  initialize(size);
}

void draw() {
  //分割
  triangularDivision();
  //描画
  if (frameCount == divCount) {
    for (Tri t : listT) {  
      t.drawTriangle(255); 
    }
    for (Tri t : listF) {  
      t.drawTriangle(0); 
    }
    stop();
  }
}
  

Mainは以下の処理となります。

  1. 初期化処理で鋭角黄金三角形を作成

  2. draw関数内で分割

  3. frameCountと分割回数が同じになったら、全ての三角形を描画

  4. 処理を停止


分割回数を8回、初期化の引数を400で描画するとこのようにタイリングされます。


一つ一つの三角形に質感を出すようにするとこのようなペンローズ・タイルもできます。

質感の出し方は各辺から三角形の内心に向かって線を連続的に描画していくと出すことができます。


以上がペンローズ・タイルの実装方法の説明でした。

最後まで読んでいただきありがとうございます。


参考文献
閲覧数:253回0件のコメント

最新記事

すべて表示

Comments


bottom of page