정보과학 계획서-미분방정식 방향장 생성 프로그램 알고리즘의 구현
1. 인터페이스 구현
가. 구현 알고리즘
1. 숫자 입력용 상자 제작
위아래 화살표 터치를 이용하여 미분방정식의 초깃값을 조절할 수 있도록 숫자 입력 상자를 제작하였다. 숫자 입력 상자에는 위쪽 화살표 터치 시 값이 1 증가, 아래쪽 화살표 터치 시 값이 1 감소하는 기능을 넣어 사용자가 간편하게 원하는 초깃값을 설정할 수 있도록 하였다.
2. 프로그램 실행 버튼 제작
프로그램 실행 버튼에 마우스를 접근시키면 버튼의 색이 붉은 색으로 변하며 눌러진 듯한 텍스처로 변경되게 하는 기능을 추가하였다.
나. 구현 모습
2. 기울기의 계산
가. 수식 입력받기
사용자의 키보드로부터 수식을 입력받는 기능을 제작하였다. 미분방정식 수식에 사용될 값인 0부터 9까지의 수, 연산기호 ‘+’‘-’, 미지수 x,y를 제외한 값이 입력되었을 때 문자열에 저장하지 않도록 하여 필요한 값들만 입력될 수 있도록 하였다. SHIFT 키와 BACKSPACE 키 또한 사용자로부터의 키보드 입력으로 인식되어 깨진 글자가 출력되는 현상을 없애기 위하여 위 두 키 또한 입력받지 않도록 하였다. BACKSPACE 키를 눌렀을 때에는 입력받은 문자를 저장하는 문자열에서 한 문자를 제거할 수 있도록 제작하였다.
나. 입력받은 수식 출력하기
사용자로부터 값을 입력받을 뿐만 아니라 입력받은 수식을 화면에 표시도 하여야 사용자가 프로그램을 편의성 높게 사용할 수 있다. 이를 위하여 입력받은 수식이 프로그램 화면에 표시되도록 제작하였다. 일정 문자 수 이상의 문자가 입력되어 지정된 칸을 벗어나게 되면 자동으로 줄바꿈이 일어나게 하는 기능 또한 추가하였다.
다. 기울기 계산하기
본 프로그램의 가장 핵심적인 기능 중 하나이다. 입력받은 수식을 변환하여 이를 이용해 기울기를 계산하는 기능으로, 좌표평면 위 격자점들에 대하여 각 지점에서의 기울기를 계산하여 배열에 저장하였다. 배열의 매 칸의 이전 칸에 포함된 문자의 종류에 따른 경우를 나누어 값을 입력받았다. 그 예시로, 만약 i-1번째 칸의 문자가 ‘+’ 또는 ‘-’라면 i번째 칸에는 숫자, x, y 중 한 문자가 올 것이므로 이들의 값을 temp에 저장하도록 알고리즘을 제작하였다. 연속된 배열의 자리에 숫자가 연속해서 나타난다면 이들은 십의 자리, 백의 자리를 나타내므로 기존의 temp에 저장되어 있던 값에 10배를 한 뒤 새로운 숫자 값을 추가하는 방식으로 숫자 값을 계산하였다. 그렇게 최종적으로 주어진 범위의 모든 격자점들에 대한 기울기룰 계산한 뒤 이를 반환하였다.
라. 구현 모습
3. 방향장 생성
가. 방향장 생성
이후 구해낸 각 격자점에서의 기울기 값을 이용해 방향장을 생성하였다. 프로세싱의 좌표 시스템은 y좌표의 방향이 반대인 점을 유의하며 각 방향장의 화살표의 길이가 15로 고정되도록 기울기를 변형하였다. 이후 모든 격자점에 대해 화살표를 생성하여 방향장을 구현하였다.
나. 구현 모습
4. 그래프 개형 그리기
가. 오일러 방법을 이용한 개형 그리기
오일러의 방법을 이용한 조각적 선형함수로 초깃값이 주어졌을 때 미분방정식의 그래프의 개형을 출력하였다. 초깃값보다 큰 x에 대해서는 0.025싹 값을 증가시키며 매 구간마다 새로운 함숫값과 기울기를 구해 내며 그래프 개형을 그렸으며 초깃값보다 작은 x에 대해서는 0.025씩 값을 감소시키며 매 구간마다 새로운 함숫값과 기울기를 구해내며 그래프 개형을 그렸다.
나. 구현 모습
5. 소스코드
가. 메인 코드
String str1=""; String res=""; int len=0; rectangle r2,r3,r5,r6; another_rectangle r1,r4; boolean bobok=false; float midx; float midy; float valuex; float valuey; float price[][] = new float[20][20]; void setup() //초기상태 { translate(500,500); //좌표변환 size(1000,1000); background(255); r1 = new another_rectangle(-300,80); r2 = new rectangle(-220,80); r3 = new rectangle(-150,80); r4 = new another_rectangle(-300,240); r5 = new rectangle(-220,240); r6 = new rectangle(-150,240); } void draw()//반복 { background(255); translate(500,500); //좌표변환 strokeWeight(5); stroke(0); fill(255); rect(-450,-450,900,900,15); //처음화면 textSize(41); //안내글씨 fill(0); text("Function", -350,-280); text("Result", 100,-280); textSize(60); text("Slope Field Generater", -250,-350); fill(240); //직사각형 strokeWeight(3); r1.design(); r2.design(); r3.design(); r4.design(); r5.design(); r6.design(); fill(200); rect(-370,345,200,100,10); stroke(200); triangle(-375,435,-365,435,-365,445); triangle(-180,338,-170,348,-180,348); stroke(0); rect(-380,335,200,100,10); line(-375,435,-365,445); line(-183,433,-173,443); line(-171,348,-183,339); textSize(80); //큰 글씨 fill(0); text("x:",-410,100); text("y:",-410,250); text(".",-270,270); text(".",-270,110); textSize(35); //작은글씨 text("g(x,y)=",-440,-220); textSize(50); text("Initial Value",-350,-50); textSize(50); //시작글씨 text("Run",-330,400); noFill(); //좌표평면 rect(-330,-250,220,100,5); rect(-80,-250,500,500,5); stroke(0,0,0,85); line(-70,0,410,0); line(410,0,400,-10); line(410,0,400,10); line(170,-240,170,240); line(170,-240,160,-230); line(170,-240,180,-230); stroke(0,0,0,30); for(int x=-55 ; x<420 ; x+=25) line(x,-245,x,245); for(int y=-225 ; y<250 ; y+=25) line(-70,y,410,y); writeText(str1); if((mouseX>120)&&(mouseX<320)&&((mouseY>835)&&(mouseY<935))) //시작버튼 변경 { strokeWeight(3); noStroke(); fill(255); rect(-390,325,240,110); stroke(0); fill(250,0,0); rect(-370,345,200,100,10); rect(-376,339,200,100,10); line(-370,440,-365,445); line(-179,437,-173,443); line(-171,348,-177,345); fill(0); textSize(50); text("Run",-325,405); strokeWeight(3); } if((mouseX>120) && (mouseX<320) && (mouseY>835) && (mouseY<935)) //시작버튼 터치 감지 { if(mousePressed==true ) { res = str1; bobok=true; } } if(bobok==true) { price=savePrice(res); makeSlope(price); drawGraph(res,r1.number,r2.number,r3.number,r4.number,r5.number,r6.number); } } |
나. 거리 함수
int dist(int x, int y) //거리 계산 함수 { return (mouseX - x - 500) * (mouseX - x - 500) + (mouseY - y - 500) * (mouseY - y - 500); } |
다. 문자 입력 함수
void keyPressed() { translate(500,500); fill(0); if(keyCode == SHIFT) return; if(int(key) >= int('A')) { if(int(key) != int('x') && int(key) != 'y') return; else str1 += key; } else if(key==BACKSPACE) { int len = str1.length(); str1 = str1.substring(0, len - 1); return; } else if(int('0') <= int(key) && int(key) <= int('9') && str1.length()<42) str1 += key; else { if(int(key) != int('+') && int(key) != int('-')) return; else str1 += key; } } |
라. 그래프 그리기 함수
float calcul(String str,float x,float y) { float value = 0; float temp=0; if(str.charAt(0)>=48 && str.charAt(0)<=57) { temp=float(str.charAt(0))-float('0'); } else if(str.charAt(0)=='x') { temp=x; } else if(str.charAt(0)=='y') { temp=y; } for(int i=1 ; i<str.length() ; ++i) { if(str.charAt(i-1)>=48 && str.charAt(i-1)<=57) //앞이 숫자일 때 { if(str.charAt(i)>=48 && str.charAt(i)<=57) { temp=10*temp+float(str.charAt(i))-float('0'); } else if(str.charAt(i)=='x') { temp*=x; } else if(str.charAt(i)=='y') { temp*=y; } else if(str.charAt(i)=='+') { value+=temp; temp=0; } else if(str.charAt(i)=='-') { value-=temp; temp=0; } } else if(str.charAt(i-1)=='x' || str.charAt(i-1)=='y') //앞이 x, y일 때 { if(str.charAt(i)=='x') { temp *= x; } else if(str.charAt(i)=='y') { temp *= y; } else if(str.charAt(i)=='+') { value+=temp; temp=0; } else if(str.charAt(i)=='-') { value-=temp; temp=0; } } else if(str.charAt(i-1)=='+') //앞이 +일 때 { if(str.charAt(i)=='x') { temp += x; } else if(str.charAt(i)=='y') { temp += y; } else if(str.charAt(i)>=48 && str.charAt(i)<=57) { temp +=float(str.charAt(i))-float('0'); } } else if(str.charAt(i-1)=='-') //앞이 -일 때 { if(str.charAt(i)=='x') { temp -= x; } else if(str.charAt(i)=='y') { temp -= y; } else if(str.charAt(i)>=48 && str.charAt(i)<=57) { temp -=float(str.charAt(i))-float('0'); } } } value+=temp; return value; } void drawGraph(String str,int a, int b, int c, int d, int e, int f) { float x1,x2; float y1,y2; float temp=0; float n1=(float)a; //자료형변환 float n2=0.1*((float)b); float n3=0.01*((float)c); float n4=(float)d; float n5 = 0.1*((float)e); float n6 = 0.01*((float)f); x1=n1+n2+n3; y1=n4+n5+n6; stroke(255,0,0); for(float i=x1 ; i<=3 ; i+=0.025) { x2=i+0.05; y2=y1-calcul(str,i,y1)*0.025; temp=y2; //170 250, 3->250 x1=(float)170+(float)250/3*i; x2=(float)170+(float)250/3*x2; y1=(float)250/3*y1; y2=(float)250/3*y2; if(y1>=-250 && y1<=250 && y2>=-250 && y2<=250) line(x1,y1,x2,y2); y1=temp; } x1=n1+n2+n3; y1=n4+n5+n6; for(float i=x1 ; i>=-3 ; i-=0.025) { x2=i-0.05; y2=y1+calcul(str,i,y1)*0.025; temp=y2; x1=(float)170+(float)250/3*i; x2=(float)170+(float)250/3*x2; y1=(float)250/3*y1; y2=(float)250/3*y2; if(y1>=-250 && y1<=250 && y2>=-250 && y2<=250) line(x1,y1,x2,y2); y1=temp; } } |
마. 방향장 생성 함수
void makeSlope(float price[][]) { for(int i=0 ; i<19 ; ++i) { for(int j=0 ; j<19 ; ++j) price[i][j]=-price[i][j]; } stroke(0); float len = 15; for(int x=0 ; x<19 ; ++x) { for(int y=0 ; y<19 ; ++y) { midx=-80+25*(x+1); midy=-250+25*(y+1); valuex=len/sqrt(float(1)+price[x][y]*price[x][y]); valuey=len * price[x][y]/sqrt(float(1)+price[x][y]*price[x][y]); line(midx-0.5*valuex,midy-0.5*valuey,midx+0.5*valuex,midy+0.5*valuey); ellipse(midx+0.5*valuex,midy+0.5*valuey,3,3); } } } |
바. 마우스 터치 함수
void mousePressed()//마우스 클릭 시 실행 { r1.touch(); r2.touch(); r3.touch(); r4.touch(); r5.touch(); r6.touch(); } |
사. 숫자 조작 상자 생성 함수
class rectangle //클래스 지정 { int number=0; int x,y; rectangle(int x, int y) //직사각형 중심 좌표 { this.x=x; this.y=y; } void touch() //터치 시 실행 { if(dist(x,y-65)<=150) number=(number+1)%10; if(dist(x,y+65)<=150) number=(number+9)%10; } void design() //직사각형과 화살표 생성 { strokeWeight(3); stroke(100); fill(240); rect(x-30,y-45,60,90,5); fill(200,0,0); stroke(100); triangle(x-15,y-55,x+15,y-55,x,y-75); fill(0,0,200); triangle(x-15,y+55,x+15,y+55,x,y+75); fill(0); textSize(80); stroke(0); textAlign(CENTER,CENTER); text(str(number),x,y-2); textAlign(LEFT,BASELINE); } } class another_rectangle //클래스 지정 { int number=0; int x,y; another_rectangle(int x, int y) //직사각형 중심 좌표 { this.x=x; this.y=y; } void touch() //터치 시 실행 { if(dist(x,y-65)<=150) number=(number+1)%4; if(dist(x,y+65)<=150) number=(number+3)%4; } void design() //직사각형과 화살표 생성 { strokeWeight(3); stroke(100); fill(240); rect(x-30,y-45,60,90,5); fill(200,0,0); stroke(100); triangle(x-15,y-55,x+15,y-55,x,y-75); fill(0,0,200); triangle(x-15,y+55,x+15,y+55,x,y+75); fill(0); textSize(80); stroke(0); textAlign(CENTER,CENTER); text(str(number),x,y-2); textAlign(LEFT,BASELINE); } } |
아. 기울기 계산 함수
float[][] savePrice(String str) { float Price[][] = new float[20][20]; float temp=0; float x,y; float k=(float)1/3; for(int l=-9 ; l<=9 ; ++l) { for(int j=-9 ; j<=9 ; ++j) { x=k*float(l); y=k*float(j); if(str.charAt(0)>=48 && str.charAt(0)<=57) { temp=float(str.charAt(0))-float('0'); } else if(str.charAt(0)=='x') { temp=x; } else if(str.charAt(0)=='y') { temp=y; } for(int i=1 ; i<str.length() ; ++i) { if(str.charAt(i-1)>=48 && str.charAt(i-1)<=57) //앞이 숫자일 때 { if(str.charAt(i)>=48 && str.charAt(i)<=57) { temp=10*temp+float(str.charAt(i))-float('0'); } else if(str.charAt(i)=='x') { temp*=x; } else if(str.charAt(i)=='y') { temp*=y; } else if(str.charAt(i)=='+') { Price[l+9][j+9]+=temp; temp=0; } else if(str.charAt(i)=='-') { Price[l+9][j+9]-=temp; temp=0; } } else if(str.charAt(i-1)=='x' || str.charAt(i-1)=='y') //앞이 x, y일 때 { if(str.charAt(i)=='x') { temp *= x; } else if(str.charAt(i)=='y') { temp *= y; } else if(str.charAt(i)=='+') { Price[l+9][j+9]+=temp; temp=0; } else if(str.charAt(i)=='-') { Price[l+9][j+9]-=temp; temp=0; } } else if(str.charAt(i-1)=='+') //앞이 +일 때 { if(str.charAt(i)=='x') { temp += x; } else if(str.charAt(i)=='y') { temp += y; } else if(str.charAt(i)>=48 && str.charAt(i)<=57) { temp +=float(str.charAt(i))-float('0'); } } else if(str.charAt(i-1)=='-') //앞이 -일 때 { if(str.charAt(i)=='x') { temp -= x; } else if(str.charAt(i)=='y') { temp -= y; } else if(str.charAt(i)>=48 && str.charAt(i)<=57) { temp -=float(str.charAt(i))-float('0'); } } } Price[l+9][j+9]+=temp; } } return Price; } |
자. 텍스트 출력 함수
void writeText(String str) { textSize(30); int len=str.length(); if(str.length()<=14) { text(str, -323,-220); } else if(str.length()<=28) { text(str.substring(0,14),-323,-220); text(str.substring(14,len),-323,-195); } else if(str.length()<=42) { text(str.substring(0,14),-323,-220); text(str.substring(14,28),-323,-195); text(str.substring(28,len),-323,-170); } } void writeText2(String str) { textSize(30); len=str.length(); if(str.length()<=14) { text(str, -323,-220); } else if(str.length()<=28) { text(str.substring(0,14),-323,-220); text(str.substring(14,len),-323,-195); } else if(str.length()<=42) { text(str.substring(0,14),-323,-220); text(str.substring(14,28),-323,-195); text(str.substring(28,len),-323,-170); } } |
6. 실행결과
가. 실행결과
프로그램을 통하여 구해낸 다양한 미분방정식들의 개형이다. 본 프로그램을 통하여 미분방정식의 풀이법을 모를 때 미분방정식의 개형을 간단하게 알 수 있게 되었다는 의의가 있다. 또한 초깃값이 변화할 때 미분방정식의 개형이 어떻게 변화하는지를 관찰하는 좋은 수단으로 사용될 수 있다.