카테고리 없음

정보과학 계획서-미분방정식 방향장 생성 프로그램 알고리즘의 구현

임대현 2024. 7. 9. 04:14

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. 실행결과

 

. 실행결과

 

 

프로그램을 통하여 구해낸 다양한 미분방정식들의 개형이다. 본 프로그램을 통하여 미분방정식의 풀이법을 모를 때 미분방정식의 개형을 간단하게 알 수 있게 되었다는 의의가 있다. 또한 초깃값이 변화할 때 미분방정식의 개형이 어떻게 변화하는지를 관찰하는 좋은 수단으로 사용될 수 있다.