코호 곡선

거북이 그래픽(Turtle Graphic) 은 그래픽스의 여러 분야에서 두루 활용된다. 프랙탈 분야에서도 예외가 아니어서 많은 프랙탈이 거북이 그래픽을 응용하고 있다. 그 대표적인 예가 나중에 다룰 L-SYSTEM 이다. 거북이 그래픽스에 대해 궁금한 사항은 다음 링크된 참고 문서를 참고한다.

이 문서에서는 거북이 그래픽스를 이용한 코호 곡선을 설명한다.

거북이 그래픽

코호 곡선에 대해서 자세히 알아보기 전에 우리가 사용할 거북이 그래픽 클래스에 대해서 알아보자. 거북이 그래픽은 모래사장 위에서 거북이가 지나간 자리에 자국이 남듯이 캔바스 위에서 전진과 회전만을 적용해 그림을 그리는 것이다. 이 거북이 그래픽 클래스에서는 전진(Forward) 와 회전을 지원하고 회전을 왼쪽 회전과 오른쪽 회전으로 나누어 작성했다. 다음은 이 거북이 그래픽의 클래스 전 코드와 간단한 예제를 실행한 모습이다. 물론 지금 작성한 거북이 그래픽 클래스는 OpenGL 상에서 올바르게 작동한다.

// Header File

#ifndef _TURTLE_H_
#define _TURTLE_H_

#include <vector>

#define TORAD 0.01745f //각도를 라디안으로 바꾸기 위한 (3.14152/180) 의 값 

//거북이가 지나간 지점의 좌표를 기억하기 위한 자료
struct Point3D
{
    float x, y, z;
    Point3D() { x=y=z=0.0f; }
    Point3D(const Point3D& pt) { x=pt.x; y=pt.y; z=pt.z; }
    Point3D(float ax, float ay, float az) { x=ax; y=ay; z=az;}
};

using namespace std;

typedef vector<Point3D> plist; //Point3D 를 담는 vector 컨테이너
typedef vector<Point3D>::iterator pi; //반복자

class Turtle
{
private:
    plist PointList; //거북이가 지나간 모든 지점을 저장할 컨테이너
    bool IsUp; //현재 거북이가 들려있는지. 즉, 현재 거북이가 들려 있다면 선은 그려지지 않는다. 
    float angle; //회전 각도
public:
    Turtle(void); //거북이 생성
    void SetStartPoint(float x, float y, float z); //거북이가 출발할 시작 위치 좌표
    void Forward(float length); //앞으로 몇 length 만큼 진행하는가? 진행 거리만큼 선을 그린다. 단 IsUp == false 일 때만.
    void Left(float rotate_angle); //왼쪽으로 rotate_angle 만큼 회전한다. 
    void Right(float rotate_angle); //오른쪽으로 rotate_angle 만큼 회전한다.
    void Up(void); //거북이를 든다.
    void Down(void); //거북이를 내려 놓는다.
    void MarkPoint(void); //거북이가 지나간 모든 지점을 점으로 표시한다.
};

#endif
// Source File

#include <windows.h>
#include <gl\gl.h>
#include <math.h>
#include "turtle.h"

Turtle::Turtle(void)
{
    PointList.clear();
    Point3D startPoint(0.0f, 0.0f, 0.0f);
    PointList.push_back(startPoint);
    IsUp = false;
    angle = 0.0f;
}

void Turtle::SetStartPoint(float x, float y, float z)
{
    angle = 0.0f;
    Point3D startPoint(x, y, z);
    PointList.clear();
    PointList.push_back(startPoint);
}

void Turtle::Forward(float length)
{
    static unsigned int count = 0;
    static float radian = 0.0f;
    static Point3D pt;
    count = PointList.size();
    radian = angle*TORAD;
    pt.x = (length*cos(radian))+PointList[count-1].x;
    pt.y = (length*sin(radian))+PointList[count-1].y;
    pt.z = (PointList[count-1].z);
    if(!IsUp)
    {
        glBegin(GL_LINES);
            glVertex3f(PointList[count-1].x, PointList[count-1].y, PointList[count-1].z);
            glVertex3f(pt.x, pt.y, pt.z);
        glEnd();
    }
    PointList.push_back(pt);
}

void Turtle::Left(float rotate_angle)
{
    angle = (angle + rotate_angle);
    angle = fmod(angle, 360.0f);
}

void Turtle::Right(float rotate_angle)
{
    angle = (angle - rotate_angle);
    angle = fmod(angle, 360.0f);
}

void Turtle::Up(void)
{
    IsUp = true;
}

void Turtle::Down(void)
{
    IsUp = false;
}

void Turtle::MarkPoint(void)
{
    glPointSize(5.0f);
    glColor3f(1.0f, 0.0f, 0.0f);
    glBegin(GL_POINTS);
        for(pi i = PointList.begin(); i != PointList.end(); ++i)
        {
           glVertex3f((*i).x, (*i).y, (*i).z);
        }
    glEnd();
    glColor3f(1.0f, 1.0f, 1.0f);
}

코호 곡선

1904년 코호에 의해 발표된 곡선이다. 외관은 곡선처럼 보이나 곡선내의 어떤 부분도 '구불어진 곡선'은 포함하고 있지 않고 직선의 연결로만 되어 있다. 선의 총 길이는 무한대이다. 발표 당시에는 전혀 주목을 받지 못하다가 70여년이 흘러 만델브로트에 의해 프랙탈이 등장한 후 화려하게 복권되어, 이제는 프랙탈에 관한 어떤 책에서나 빠짐없이 등장한다.

알고리즘

코호 곡선은 완벽한 재귀적 구조로 되어 있다. 기본 도형은 직선이다. 직선에서 시작하여 다음과 같은 알고리즘으로 그려지게 된다.

  1. 직선을 그린다.

  2. 왼쪽으로 60 도 회전하여 직선을 그린다.

  3. 오른쪽으로 120 도 회전하여 직선을 그린다.

  4. 왼쪽으로 60 도 회전하여 직선을 그린다.

재귀호출이 한번 반복될 때마다 차수를 1씩 줄이고(n -> n-1), 차수가 0 이 되면 선을 그리면 된다. 다음은 이 알고리즘을 코드로 표현한 것이다.

void Koch(int n)
{
    if(n==0)
    {
        turtle.Forward(0.3f);
    }
    else
    {
        Koch(n-1);
        turtle.Left(60); Koch(n-1);
        turtle.Right(120); Koch(n-1);
        turtle.Left(60); Koch(n-1);
    }
}

차수 n=1 인 경우를 생각해 보자. 처음에는 n=1 이고 따라서 if...else { ... } 의 { ... } 부분이 실행되어 위에서 설명한 알고리즘대로 방향을 회전시키면서 Koch() 함수가 4차례 재귀호출된다. Koch(n-1) 은 Koch(1-1) 로 실행되고 이때에는 n=0 이므로 if(n==0) turtle.Forward() 함수가 실행되어 직선을 그리고 직선 끝으로 이동한다. else { ... } 의 4 개의 Koch() 함수가 실행되고나면 위에서 위의 그림 중 직선 옆의 가운데 그림처럼 선이 그려진다.

아래는 차수 7 의 코호 곡선을 그린 실행 결과와 전 코드이다.

#include "lib\egl.h"
#include "turtle.h"
using namespace egl;

Turtle turtle;

class RenderWindow : public Window
{
private:
public:
    virtual void RenderGLScene(void);
};

void Koch(int n)
{
    if(n==0)
    {
        turtle.Forward(0.3f);
    }
    else
    {
        Koch(n-1);
        turtle.Left(60); Koch(n-1);
        turtle.Right(120); Koch(n-1);
        turtle.Left(60); Koch(n-1);
    }
}

void RenderWindow::RenderGLScene(void)
{
    Window::RenderGLScene();
    glTranslatef(0.0f, 0.0f, -70.0f);
    turtle.SetStartPoint(-35.0f, 0.0f, 0.0f);
    Koch(7);
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    RenderWindow app;
    if(!app.Create(FALSE, "EDin's OpenGL - Koch Curve"))
        return EXIT_FAILURE;
    return app.Run();
}

Last updated