이번에는 Sierpinski Carpet을 그리는 과정을 통해 CindyJS의 제어문과 colorplot을 소개합니다. 반복시행을 위해서 제어문의 이해는 정말 중요합니다.


Sierpinski Carpet은 다음과 같은 형태의 구조를 말합니다. 현대 기술에서는 Self-similar한 특징으로 안테나를 설계할 때 활용됩니다. Fractal Antenna를 참고하세요.




여기서는 CindyJS로 이 그림을 그리도록 하겠습니다. 



1. 기본파일 준비하기


지난 시간에 소개한대로, 아래와 같은 html파일을 준비합니다. 


<!doctype html>
<html>

<head>
  <title>Sierpinski Carpet</title>
  <script type="text/javascript" src="js/Cindy.js"></script>  
</head>

<body>
  <div id='CSCanvas'></div>
  
  <script id='csdraw' type='text/x-cindyscript'>
  </script>
  
  <script>
    CindyJS({
      scripts: 'cs*',
      autoplay: true,
      ports: [{
        id: 'CSCanvas',
        width: 500,
        height: 500,
        transform: [{
          visibleRect: [0, 1, 1, 0]
        }]
      }]
    })
  </script>
</body>

</html>

5번째 줄에는 문서 제목을 달았는데, 꼭 필요한 것은 아닙니다. 24째 줄에는 계산의 편의를 위해 가로, 세로 500픽셀의 상자의 좌표를 $[0,1]\times [0,1]$로 설정했습니다.



2. 배경색 칠하기 - colorplot


그림의 영역을 원하는 색으로 칠합니다. 전체 영역에 색칠을 할 때는 colorplot()을 이용합니다. html/css를 아시는 분은 해당 기능을 사용해도 됩니다. colorplot은 앞으로도 자주 사용하게 될 것이라서 가장 먼저 소개합니다. colorplot은 다양한 방법으로 사용되는데, 여기서는 색을 정하는 법만 소개합니다. 다음과 같이 하면 위 파일에서 그리기로 한 영역의 전체를 한 가지 색으로 칠해줍니다.


colorplot( 색상코드 )

색상 코드로 가능한 것은 대중적으로 많이 사용되는 RGB 색상의 조합을 순서쌍으로 나타낸 [R, G, B]꼴이나 hue값을 hue(숫자)와 같은 꼴로 나타내는 방법이 가능하고, CindyJS에서는 특별히 gray(숫자), red(숫자), green(숫자), blue(숫자)로 각 색의 강도를 정해주는 형태를 사용할 수 있습니다. 그래서, 빨강색으로 전체 배경을 칠하는 방법은 다음과 같이 다양합니다. 여기서 사용되는 수는 모두 0부터 1까지의 숫자입니다. 인터넷에서 자료를 찾아보면 대체로 R, G, B는 0에서 255까지의 숫자로, hue값은 0에서 360의 값을 사용하는데, CindyJS에서는 최대 숫자에 대한 비율로 사용한다고 이해하면 되겠습니다.

colorplot([1,0,0]);
colorplot(hue(0));
colorplot(red(1));
colorplot(red(1)+green(0)+blue(0)+gray(0));

이 코드는 다음과 같이 csdraw파트에 입력합니다.

<!doctype html>
<html>

<head>
  <title>Sierpinski Carpet</title>
  <script type="text/javascript" src="js/Cindy.js"></script>  
</head>

<body>
  <div id='CSCanvas'></div>
  
  <script id='csdraw' type='text/x-cindyscript'>
    colorplot([1,0,0]);
  </script>
  
  <script>
    CindyJS({
      scripts: 'cs*',
      autoplay: true,
      ports: [{
        id: 'CSCanvas',
        width: 500,
        height: 500,
        transform: [{
          visibleRect: [0, 1, 1, 0]
        }]
      }]
    })
  </script>
</body>

</html>

그러면 다음과 같이 빨간 정사각형이 그려집니다.




3. 사각형의 중앙을 다른 색으로 칠하기 - fillpoly


이제, $[1/3,2/3] \times [1/3,2/3]$ 영역을 다른 색으로 칠합니다. 이 때 사용하는 것은 fillpoly라는 함수로, 기본 사용법은 다음과 같습니다.


fillpoly([꼭짓점 좌표들], color->색상코드);


일단은 다음과 같이 중앙에 색을 칠해 봅니다.


<!doctype html>
<html>

<head>
  <title>Sierpinski Carpet</title>
  <script type="text/javascript" src="js/Cindy.js"></script>  
</head>

<body>
  <div id='CSCanvas'></div>
  
  <script id='csdraw' type='text/x-cindyscript'>
    colorplot([1,0,0]);
    fillpoly( [[1/3,1/3], [1/3,2/3], [2/3,2/3], [2/3,1/3]], color->hue(0.1));
  </script>
  
  <script>
    CindyJS({
      scripts: 'cs*',
      autoplay: true,
      ports: [{
        id: 'CSCanvas',
        width: 500,
        height: 500,
        transform: [{
          visibleRect: [0, 1, 1, 0]
        }]
      }]
    })
  </script>
</body>

</html>

그 결과, 다음과 같은 그림이 얻어집니다.




우리는 이제 이 작업을 반복해서 해야 합니다.



4. 핵심 좌표 생성하기


각 단계별로 그려야 할 사각형의 크기는 정해져 있으므로, 사각형의 왼쪽 아래 모퉁이에 해당되는 좌표를 만들고 그 좌표에서 정해진 길이만큼 가로, 세로변의 길이를 가지는 정사각형을 만들기로 합니다. 두 번째 단계를 시범으로 해서 그림을 그려보고 마지막으로 전체를 반복할 수 있도록 코딩을 하겠습니다.


우리가 모아야 할 좌표는 $k/3^2$꼴의 숫자들로 이루어진 것 중 $k$를 3으로 나눈 나머지가 $1$인 수들입니다. 이러한 수들을 가려내기 위해 자연수값을 가질 변수 x를 가져오고,

x=1;

숫자들을 모아둘 공간을 확보합니다,

coord = [];

x가 $3^2$보다 작은 범위에서 3으로 나눈 나머지가 1인 숫자가 무엇인지를 모조리 뒤져서 coord에 모읍니다.

while( x < 3^2,
  if( mod(x, 3) == 1,
    coord = coord ++ [x/3^2];
  );
  x = x + 1;
);

이렇게 모아진 숫자들로 이루어진 순서쌍을 생성합니다.

corners = directproduct(coord, coord);

이러면 준비가 다 끝납니다. 이것을 모으면 다음과 같은 파일이 만들어집니다. 화면변화는 없습니다.

<!doctype html>
<html>

<head>
  <title>Sierpinski Carpet</title>
  <script type="text/javascript" src="js/Cindy.js"></script>  
</head>

<body>
  <div id='CSCanvas'></div>
  
  <script id='csdraw' type='text/x-cindyscript'>
    colorplot([1,0,0]);
    fillpoly( [[1/3,1/3], [1/3,2/3], [2/3,2/3], [2/3,1/3]], color->hue(0.1));
    x = 1;
    coord = [];
    while( x<3^2,
      if( mod(x,3) == 1,
        coord = coord ++ [x/3^2];
      );
      x = x + 1;
    );
    corners = directproduct(coord, coord);    
  </script>
  
  <script>
    CindyJS({
      scripts: 'cs*',
      autoplay: true,
      ports: [{
        id: 'CSCanvas',
        width: 500,
        height: 500,
        transform: [{
          visibleRect: [0, 1, 1, 0]
        }]
      }]
    })
  </script>
</body>

</html>

갑자기 많은 내용이 나왔습니다. 제어문의 형태가 좀 특이하죠. modulo는 보통 사용되는 %가 아니라 mod를 씁니다. 그리고, 배열에 원소를 추가할 때 ++를 사용함에 주의하세요.



5. 좌표 확인


다음 단계로 넘어가기 전에 앞서 얻은 좌표가 제대로 구해졌는지 확인합시다. corners에 저장된 좌표를 모두 찍어보면 됩니다. 이것은 foralldraw를 사용하면 됩니다.

forall(corners, draw(#));

#corners라는 배열의 모든 원소를 의미합니다. 그러면 다음과 같이 다음번에 그릴 사각형의 왼쪽 모퉁이에 해당되는 점이 표시되는 것을 확인할 수 있습니다. 중앙에 보이는 점은 어차피 가려질 것이므로 무시합니다.

<!doctype html>
<html>

<head>
  <title>Sierpinski Carpet</title>
  <script type="text/javascript" src="js/Cindy.js"></script>  
</head>

<body>
  <div id='CSCanvas'></div>
  
  <script id='csdraw' type='text/x-cindyscript'>
    colorplot([1,0,0]);
    fillpoly( [[1/3,1/3], [1/3,2/3], [2/3,2/3], [2/3,1/3]], color->hue(0.1));
    x = 1;
    coord = [];
    while( x<3^2,
      if( mod(x,3) == 1,
        coord = coord ++ [x/3^2];
      );
      x = x + 1;
    );
    corners = directproduct(coord, coord);
    forall(corners, draw(#));
  </script>
  
  <script>
    CindyJS({
      scripts: 'cs*',
      autoplay: true,
      ports: [{
        id: 'CSCanvas',
        width: 500,
        height: 500,
        transform: [{
          visibleRect: [0, 1, 1, 0]
        }]
      }]
    })
  </script>
</body>

</html>



6. 2단계 사각형 그리기


단계가 바뀌면서 변의 길이가 변하고, 그 변의 길이는 각 단계에서 중요한 역할을 하므로 변의 길이를 따로 변수로 정하겠습니다.

edge = 1/3^2;

그리고, 앞 단계에서 그린 점을 제거하고 사각형으로 채우겠습니다.

forall(corners,
  fillpoly([#,#+[0,edge], #+[edge,edge], #+[edge,0]], color->hue(0.1));
);

이 두 가지를 모아서 다음과 같이 적절한 위치에 입력합니다.(16, 25, 26, 27째 줄)


<!doctype html>
<html>

<head>
  <title>Sierpinski Carpet</title>
  <script type="text/javascript" src="js/Cindy.js"></script>  
</head>

<body>
  <div id='CSCanvas'></div>
  
  <script id='csdraw' type='text/x-cindyscript'>
    colorplot([1,0,0]);
    fillpoly( [[1/3,1/3], [1/3,2/3], [2/3,2/3], [2/3,1/3]], color->hue(0.1));
    x = 1;
    edge = 1/3^2;
    coord = [];
    while( x<3^2,
      if( mod(x,3) == 1,
        coord = coord ++ [x/3^2];
      );
      x = x + 1;
    );
    corners = directproduct(coord, coord);
    forall(corners,
      fillpoly([#,#+[0,edge], #+[edge,edge], #+[edge,0]], color->hue(0.1));
    );
  </script>
  
  <script>
    CindyJS({
      scripts: 'cs*',
      autoplay: true,
      ports: [{
        id: 'CSCanvas',
        width: 500,
        height: 500,
        transform: [{
          visibleRect: [0, 1, 1, 0]
        }]
      }]
    })
  </script>
</body>

</html>

그 결과 다음과 같은 그림을 얻습니다.




7. 반복문으로 묶기


2단계를 만들면서 소개한 알고리즘을 묶어서 여러 단계를 한꺼번에 반복할 수 있도록 소스를 수정합니다. 이 용도로 CindyJS에 준비된 것은 repeat입니다. 다음 결과를 잘 관찰하시면 이해가 될겁니다.

<!doctype html>
<html>

<head>
  <title>Sierpinski Carpet</title>
  <script type="text/javascript" src="js/Cindy.js"></script>  
</head>

<body>
  <div id='CSCanvas'></div>
  
  <script id='csdraw' type='text/x-cindyscript'>
    colorplot([1,0,0]);
    repeat(5, i, 
      x = 1;
      coord = [];
      edge = 1/3^i;
      while( x < 3^i,
        if( mod(x, 3) == 1,
          coord = coord ++ [x/3^i]; 
        );
        x=x+1;
      );
      corners = directproduct(coord, coord);
      forall(corners,
        fillpoly([#,#+[0,edge], #+[edge,edge], #+[edge,0]], color->hue(0.1));
      );
    );
  </script>
  
  <script>
    CindyJS({
      scripts: 'cs*',
      autoplay: true,
      ports: [{
        id: 'CSCanvas',
        width: 500,
        height: 500,
        transform: [{
          visibleRect: [0, 1, 1, 0]
        }]
      }]
    })
  </script>
</body>

</html>

생각보다 간단히 다음과 같은 그림을 얻었습니다.



모든 과정을 동영상으로 담아보았습니다. 조금이나마 도움이 되길 바랍니다.

+ Recent posts