p5.js 연습 003. L-시스템을 이용한 시어핀스키 삼각형 그리기

written by jjycjn   2017. 7. 7. 06:01

L-시스템(L-system)은 1986년 린덴마이어(Lindenmayer)가 생태계의 성장 모형, 특히 나무의 분기 모형을 연구하는 목적으로 고안하였다. L-시스템은 세가지 요소 (S,ω,P)로 이루어지는데, 이 시스템의 각 요소들에 대하여

  1. S는 시스템을 구성하는 문장들의 집합, (이를 다시 세분화 하여 매 단계마다 치환규칙에 의해 변하는 변수들의 집합과, 변하지 않는 상수들의 집합으로 나누기도 한다),
  2. ωS의 문장들로 구성된 시스템의 초기 상태,
  3. P는 시스템의 현 단계에서 다음 단계로 변환시키는 치환 규칙

을 나타낸다. 예를 들어 어떤 L-시스템이 다음와 같은 규칙으로 생성된다고 하자.

S={A,B},ω=A,P={(AABA),(BBA)}

그러면 각 단계별 시스템의 상태는 다음과 같다.

0:A1:ABA2:ABABAABA3:ABABAABABAABAABABAABA4:


이 L-시스템은 특히 프랙탈 구조를 그리는데 효과적인데, S의 각 문장들에 대하여 적당한 그림 규칙만 정의해 주면 (예를 들어 위의 예시에서 A는 '단위길이의 직선을 그리기', B는 '반시계 방향으로 90 회전하기' 등으로 임의의 정의하면 된다), 시스템의 단계가 진행됨에 따라서 점차 복잡한 프랙탈 구조의 그림을 자동으로 얻을 수 있다.


아래 두 그림은 L-시스템을 이용하여 만든 시어핀스키 삼각형(Sierpinski triangle)과 시어핀스키 곡선(Sierpinski curve)이다.


시어핀스키 삼각형(Sierpinski triangle)

시어핀스키 삼각형을 그리는 L-시스템은 다음과 같이 주어진다.

S={F,G,+,},ω=FGG,P={(FFG+F+GF),(GGG)}

이제 FG 모두 '단위길이의 직선을 그리기'로 정의하고, +는 '시계 방향으로 120 회전하기', 는 '반시계 방향으로 120 회전하기'로 각각 정의해 주면, 아래의 시어핀스키 삼각형을 얻는다.



p5.js 코드 보기

var Start = "F-G-G";
var Rules = [];
var Result = Start;
var lineLength = 1000;
var n = 0;

Rules[0] = {
  input: "F", 
  output: "F-G+F+G-F"
}
Rules[1] = {
  input: "G", 
  output: "GG"
}

function Generate() {
  var nextResult = "";
  for (var i = 0; i < Result.length; i++) {
    var check = Result.charAt(i);
    for (var j = 0; j < Rules.length; j++) {
      var found = false;
      if (check == Rules[j].input) {
        nextResult += Rules[j].output;
        var found = true;
        break;
      } 
    }
    if (!found) {
        nextResult += check;
    }
  }
  return nextResult;
}

function Turtle() {
  for (var i = 0; i < Result.length; i++) {
    var check = Result.charAt(i);
    if (check == "F") {
      line(0, 0, 0, -lineLength);
      translate(0, -lineLength);
    } else if (check == "G") {
      line(0, 0, 0, -lineLength);
      translate(0, -lineLength);
    } else if (check == "+") {
      rotate(+TWO_PI/3);
    } else if (check == "-") {
      rotate(-TWO_PI/3);
    }
  }
}

function setup() {
  createCanvas(640, 600);
  background(255);
  textSize(15);
  text("n = " + n, 110, 565);
  var button = createButton("Generate")
  button.mousePressed(Button);
  button.position(30, 550);
}

function draw() {
}

function Button() {
  background(255);
  n += 1;
  if (n > 8) {
    background(255);
    Result = "F-G-G";
    lineLength = 1000;
    n = 0;
    text("n = " + n, 110, 565);
  } else {
    lineLength /= 2;
    text("n = " + n, 110, 565);
    push();
    translate(500, 550);
    Turtle();
    pop();
    Result = Generate();
  }  
}



시어핀스키 곡선(Sierpinski curve)

시어핀스키 곡선을 그리는 L-시스템은 다음과 같이 주어진다.

S={F,G,+,},ω=F,P={(FG+F+G),(G+FGF)}

이제 FG 모두 '단위길이의 직선을 그리기'로 정의하고, +는 '시계 방향으로 60 회전하기', 는 '반시계 방향으로 60 회전하기'로 각각 정의해 주면, 아래의 시어핀스키 곡선을 얻는다.



p5.js 코드 보기

var Start = "F";
var Rules = [];
var Result = Start;
var lineLength = 1000;
var n = 0;

Rules[0] = {
  input: "F", 
  output: "-G+F+G-"
}
Rules[1] = {
  input: "G", 
  output: "+F-G-F+"
}

function Generate() {
  var nextResult = "";
  for (var i = 0; i < Result.length; i++) {
    var check = Result.charAt(i);
    for (var j = 0; j < Rules.length; j++) {
      var found = false;
      if (check == Rules[j].input) {
        nextResult += Rules[j].output;
        var found = true;
        break;
      }
    }
    if (!found) {
      nextResult += check;
    }
  }
  return nextResult;
}

function Turtle() {
  for (var i = 0; i < Result.length; i++) {
    var check = Result.charAt(i);
    if (check == "F") {
      line(0, 0, 0, -lineLength);
      translate(0, -lineLength);
    } else if (check == "G") {
      line(0, 0, 0, -lineLength);
      translate(0, -lineLength);
    } else if (check == "+") {
      rotate(+PI/3);
    } else if (check == "-") {
      rotate(-PI/3);
    }
  }
}

function setup() {
  createCanvas(640, 600);
  background(255);
  textSize(15);
  text("n = " + n, 110, 565);
  var button = createButton("Generate")
    button.mousePressed(Button);
  button.position(30, 550);
}

function draw() {
}

function Button() {
  background(255);
  n += 1;
  if (n > 9) {
    background(255);
    Result = "F";
    lineLength = 1000;
    n = 0;
    text("n = " + n, 110, 565);
  } else {
    lineLength /= 2; 
    text("n = " + n, 110, 565);
    push();
    translate(500, 550);
    Turtle();
    pop();
    Result = Generate();
  }
}


  ::  
  • 공유하기  ::