ここでは、書籍「フラクタル: 混沌と秩序のあいだに生まれる美しい図形 アルケミスト双書」のp.8,9に掲載されていたLシステムの例であるレヴィC曲線やドラゴン曲線について、自分でも作ってみました。

Lシステムとは

書籍「フラクタル: 混沌と秩序のあいだに生まれる美しい図形 アルケミスト双書」のp.8でコッホ曲線を例に解説されているものが分かりやすいので、それに沿って説明します。

コッホ曲線を描くときの最初の段階を次のように表現します。

1ステップ前進(A)、左に60°回転(+)、1ステップ前進(A)、右に60°回転(-)、もう一度右に60°回転(-)、1ステップ前進(A)、左に60°回転(+)、1ステップ前進(A)

これを「A+A--A+A:60°」で表します。この中の1ステップ前進(A)を「A+A--A+A:60°」で置き換えることにより、 コッホ曲線を描くときの次の段階を表現することができます。これを無限に繰り返すことで、コッホ曲線が得られます。

このように表現した式をLシステムと呼びます。

レヴィC曲線

レヴィC曲線はLシステムで表すと「+A--A+:45°」となります。Aの部分をさらに「+A--A+:45°」で置き換えていきます。

レヴィC曲線

プログラムコード

レヴィC曲線を描くプログラムコードを載せておきます。

float h_min = 5.0; // 枝の長さの最小値

void setup(){
  size(1000,1000);
  background(255,255,255);
  
  // レヴィC曲線
  PVector start = new PVector(width/4.0, height*2.0/3.0);
  PVector end = new PVector(3.0*width/4.0, height*2.0/3.0);
  drawLevyC(start, end);
}

// レビィC曲線を描くための関数
void drawLevyC(PVector start, PVector end){
  // レビィC曲線を描くための新たな頂点を算出
  PVector sub_vec = end.copy().sub(start.copy());
  PVector levy_point = PVector.add(start.copy(), sub_vec.copy().mult(1.0/sqrt(2.0)).rotate(radians(-45)));

  // レヴィC曲線を再帰的に描画する
  if(sub_vec.mag() > h_min){
    drawLevyC(start, levy_point);
    drawLevyC(levy_point, end);
  } else {
    line(start.x, start.y, levy_point.x, levy_point.y);
    line(levy_point.x, levy_point.y, end.x, end.y);
  }
} 

ドラゴン曲線

ドラゴン曲線はLシステムで表すと「+A--B+:45°」となります。ただし、Aの部分は「+A--B+:45°」で置き換えますが、Bの部分は「-A++B-:45°」で置き換えていきます。AもBも上記の解説で言えば「1ステップ前進」ではありますが、置き換え方が変わるので区別しています。

ドラゴン曲線

プログラムコード

ドラゴン曲線を描くプログラムコードを載せておきます。

float h_min = 10.0; // 枝の長さの最小値

void setup(){
  size(1000,1000);
  background(255,255,255);
  
  // ドラゴン曲線
  PVector start = new PVector(width/4.0, height/2.0);
  PVector end = new PVector(3.0*width/4.0, height/2.0);
  drawDragon(start, end);
}

// ドラゴン曲線を描くための関数
void drawDragon(PVector start, PVector end){
  // startとendで結ばれる線分を3分割する点
  PVector sub_vec = end.copy().sub(start.copy());
  PVector dragon_point = PVector.add(start.copy(), sub_vec.copy().mult(1.0/sqrt(2.0)).rotate(radians(-45)));

  // ドラゴン曲線を描画する
    drawDragon_recur(start, dragon_point, 45);
    drawDragon_recur(dragon_point, end, -45);
} 

// ドラゴン曲線を描く際、再帰的に呼び出す関数
void drawDragon_recur(PVector start, PVector end, float theta){
  // ドラゴン曲線を描くための新たな頂点を算出
  PVector sub_vec = end.copy().sub(start.copy());
  PVector dragon_point = PVector.add(start.copy(), sub_vec.copy().mult(1.0/sqrt(2.0)).rotate(radians(-theta)));

  // ドラゴン曲線を再帰的に描画する
  if(sub_vec.mag() > h_min){
    drawDragon_recur(start, dragon_point, 45);
    drawDragon_recur(dragon_point, end, -45);
  } else {
    line(start.x, start.y, dragon_point.x, dragon_point.y);
    line(dragon_point.x, dragon_point.y, end.x, end.y);
  }
} 

コメントを残す