Flash CS4 + ActionScript 3.0로 재귀를
이용한 프렉탈 나무 그리기 테스트.
우상단 단계수 옆 입력창에 1~11 사이의 숫자를 입력한 뒤 엔터키를 누르면 화면을 지우고
해당 단계의 프렉탈 나무를 새로 그린다.
재귀를 이용, 피보나치 수열 만들어 배열에 저장하기.
재귀를 이용, 프렉탈 나무 그리기.
최근 읽은 유키 히로시의 책 '프로그래머, 수학으로 생각하라' 6장 재귀 관련 내용 중 재귀함수를 이용, 프렉탈
나무를 그리는 의사 코드가 나왔기에 플래시로 구현해 봤다.
처음엔 x,y좌표를 바꾸는 방식으로 시도해 봤다가 실패. 이후 cos, sin이용해 각도 변화 뒤 이동시키는 방법
사용해 일단 성공.
한데 가지 수를 늘릴 수록 오차가 커지는 문제에 봉착. 나무가 좌우 동일 형태가 나와야 하는데 왼쪽으로 기울어진
형태가 되고 11단계 쯤 가면 아예 괴상한 결과가 나온다.
카오스의 원인이라고 하는 컴퓨터의 소수점 이하 계산 생략이 축적되어 오차가 생기는 듯 하지만 그렇다고 치기엔 너무
오차가 크다. 역시 뭔가 코딩 논리 상의 문제일 듯한 가능성도 커보인다.
원주율 3.14를 Math.PI로 변경했지만 그리 오차가 많이 수정되진 않는다.
만들고 난 뒤 보니 전반적으로 코드가 좀 지저분.
이후 단계수를 키보드로 입력받아 해당 단계에 맞는 프렉탈 나무를 새로 그리는 기능 추가.
책에 나온 의사코드는 아래와 같은 것.
function drawTree(n){ //재귀를 이용 나무 그리기.
if(n==0){
//아무것도 안함
}else{
left(); //왼쪽으로 각도 변화
forward(); //앞으로 그리기
drawTree(n-1); //숫자 줄이고 재귀 호출
back(n); //뒤로 돌아가기
right(); //오른쪽으로 각도변화(원래 중앙 각도로 복귀)
right(); //오른쪽으로 각도 변화
forward(); //앞으로 그리기
drawTree(n-1); //숫자 줄이고 재귀 호출
back(n); //뒤로 돌아가기
left(); //왼쪽으로 각도변화(원래 중앙 각도로 복귀)
}
}
15/5/25 월
|
화면에 'pointM'란 이름의 삼각형 무비클립과 'pNumTxt'란 이름의
입력 텍스트 창, 'explainTxt'란 이름의 다이나믹 텍스트 창을 미리 만들어 두다.
var ix=250, iy=300; //시작 x,y좌표
var tx=250, ty=270; //목표 x,y좌표
var fiboArr:Array=new Array(); //피보나치 수 담기용 배열
var fb=0; //피보나치 수 계산용 변수
var pnt:MovieClip=pointM; //삼각형 무비클립
var lr=0.40; //가지 벌어지는 각도(라디안)
var bl = 20; //가지 길이
var processNum=0; //단계수 저장용 변수
function init(){
processNumSet();
lineDraw01(ix,iy,tx,ty);
ix = tx, iy = ty;
pnt.x = tx, pnt.y = ty;
pnt.tx = 0, pnt.ty = 0;
pnt.theta = 4.7124; //각도(라디안) 4.71 라디안 = 270도
drawTree(processNum);
for(var i=0;i<12;i++){ //피보나치 수 만들어 배열에 담기
fiboArr.push(fibonacci(i));
}
trace(fiboArr);
stage.addEventListener(KeyboardEvent.KEY_UP,keyUps);
}
function processNumSet(){ //단계수 결정하기
processNum = Number(pNumTxt.text);
if(processNum > 12){
explainTxt.text="11이하의 숫자를 입력해 주세요. 11단계의 프렉탈 나무입니다.";
processNum = 11;
}else if(processNum < 1){
explainTxt.text="1이상의 숫자를 입력해 주세요. 1단계의 프렉탈 나무입니다.";
processNum = 1;
}else{
explainTxt.text=processNum+"단계의 프렉탈 나무입니다";
}
}
function keyUps(e:KeyboardEvent){ //키보드 눌렀다 뗄 경우
switch(e.keyCode){
case(13):{
graphics.clear();
ix=250, iy=300;
tx=250, ty=270;
init();
break;
}
}
}
function loop(e:Event){ } //기본 루프
function fibonacci(n){ //재귀 이용 피보나치 수 만들어 리턴
if(n==0){
return 0;
}else if(n==1){
return 1;
}else{
return fibonacci(n-1)+fibonacci(n-2);
}
}
function drawTree(n){ //재귀를 이용 나무 그리기.
if(n==0){
//아무것도 안함
}else{
bl -= 2; //가지 길이 약간 줄이기
rotateMove(-lr, bl); //왼쪽으로 30도 각도 변화, 이동
lineDraw01(ix,iy,pnt.tx,pnt.ty,1,Math.random()*0xffffff);
pnt.x = pnt.tx, pnt.y = pnt.ty;
ix = pnt.tx, iy = pnt.ty;
drawTree(n-1); //숫자 줄이고 재귀 호출
rotateMove(Math.PI, bl); //원래 위치로 각도 변화, 이동
pnt.theta += Math.PI+lr;
pnt.x = pnt.tx, pnt.y = pnt.ty;
ix = pnt.tx, iy = pnt.ty;
bl += 2; //가지 길이 복원
bl -= 2; //가지 길이 약간 줄이기
rotateMove(lr, bl); //오른쪽으로 각도 변화, 이동
lineDraw01(ix,iy,pnt.tx,pnt.ty,1,Math.random()*0xffffff);
pnt.x = pnt.tx, pnt.y = pnt.ty;
ix = pnt.tx, iy = pnt.ty;
drawTree(n-1); //숫자 줄이고 재귀 호출
rotateMove(Math.PI, bl); //원래 위치로 각도 변화, 이동
pnt.theta += Math.PI-lr;
pnt.x = pnt.tx, pnt.y = pnt.ty;
ix = pnt.tx, iy = pnt.ty;
bl += 2; //가지 길이 복원
}
}
function rotateMove(omega, length){//각도 변화시키기
pnt.theta += omega;
//위치계산
pnt.tx = pnt.x + length * Math.cos(pnt.theta);
pnt.ty = pnt.y + length * Math.sin(pnt.theta);
pnt.rotation = pnt.theta*(180/Math.PI); //라디안 > 도 변화하여 회전
}
function lineDraw01(mX,mY,tX,tY,style1=1,style2=0){ //선그리기 함수
//(mX,mY)위치로 이동, (tX,tY)까지 style(굵기,색)대로 선그리기
graphics.lineStyle(style1, style2);
graphics.moveTo(mX, mY);
graphics.lineTo(tX, tY);
}
init(); |