반응형
′개모양 캐릭터가 무니무니하게 움직여서 고양이모양으로 변형! 다시 이번에는 하트모양으로 변형!′ 이런 식으로 차례차례 모양을 바꿔주는 신나는 자바 애플릿 ′무니무니군′을 만들어봅시다.자바 애플릿은 홈페이지에 올릴 수 있기 때문에 움직임이 있고 즐거운 페이지를 연출할 수 있습니다.더불어 이 ′무님니군′에게 새로운 형태를 알려주기 위한 에디터 ′무니에디타아′도 만들어 보겠습니다.이 에디터를 사용하면 오리지널 형태를 무턱대고 너에게 가르쳐 줄 수 있게 됩니다.혹시모핑인아기무님니군모핑이라는말들어보셨나요?컴퓨터 그래픽스 관계의 세계에서는 서로 다른 두 개의 모양 A와 B가 주어졌을 때, A모양을 서서히 바꾸면서 B모양을 만들어 버리는 기술을 모핑이라고 부릅니다.터미네이터2에서 터미네이터가 액체 금속의 걸쭉한 형태에서 인간의 형태로 변해가는 장면이 있었던 것을 기억하시는 분들이 많으실 겁니다.이런것들은모핑을이용한예들이고볼수가있겠습니다.이번에는 이러한 모핑을 이용하여 2차원의 형태가 서서히 변화하는 자바 애플릿을 만들어 보도록 하겠습니다.모양이 ′문득문득′ 변화하는 모습에서 ′문득 군′이라고 이름을 지어 보았습니다 :-).무니무니 군 작성 흐름 프로그래밍을 쉽게 하기 위해 무니무니는 2차원의 평평한 캐릭터로 다각형의 윤곽을 갖춘 형태라고 하겠습니다.이렇게 하면 다각형의 꼭지점 위치만 생각하면 되기 때문에 프로그래밍과 실제 계산처리를 쉽게 할 수 있습니다.그런데, 이유 없이 군은 서서히 형태를 바꾸어 가기 때문에 일정 시간마다 표시를 갱신할 필요가 있습니다.이런 처리를 하는 데 필요한 건... 맞아, 스레드에요.이번에 소개하는 애플릿에서는 스레드를 이용해 애니메이션 표시를 실시합니다.우선, 무작정 군의 프로토타입 작성에 도전하겠습니다.이것은 최종적인 애플릿의 기본이 되는 부분만을 프로그래밍해 본 심플한 것입니다.프로토타입이 잘 움직이면, 여러가지 기능을 추가하고, 무턱대고 너를 더 즐거운 것으로 만들어 갑니다.마지막으로 무니무니 군에게 새로운 형태를 알려주기 위한 ′무니에디타아′ 소개를 해보도록 하겠습니다.이 에디터를 사용하는 것으로, 오리지날의 형태를 간단하게 추가할 수 있게 됩니다.무작정 너한테 귀엽고 재미있는 모양 많이 알려줄게요.무작정 군의 프로토타입을 만들자 프로토타입은 모핑이 잘 되는 것을 시험하기 위한 것이기 때문에 알고리즘을 간단하게 하기 위해서 꼭지점 수가 같은 다각형을 2개를 준비하고 그 사이에서만 모핑을 하도록 하겠습니다.구체적으로는 정점이 10개인 별형에서 정점이 10개인 정 10각형으로 서서히 모양이 바뀌어 가는 것을 작성합니다.정점의 수가 같으면 별 모양의 첫 번째 정점은 정10각형의 첫 번째 정점으로, 별 모양의 두 번째 정점은 정10각형의 두 번째 정점으로…라는 식으로 각 정점의 이동 전과 이동 후의 위치를 각각 쉽게 알 수 있기 때문에 프로그래밍이 편합니다.그럼 각각의 정점을 처음의 장소부터 끝의 장소까지 서서히 이동시키려면 어떻게 하면 좋을까요? 수학 이야기가 좀 되어 버립니다만, 처음의 장소가 P0, 끝의 장소가 P1로 나타날 때 P0과 P1을 t:1-t로 내분시키는 점 Q는 「Q=(1-t) P0+t1」라는 식으로 나타냅니다.(고등학교 시절에 공부한 곳이 P1-t0에서 끝맺음) P1으로 끝내는 점 Q는 「Q」에서 서서히 이동시킬 수 있습니다. p0+t1」라고 하는 것으로 합니다.)이것이 무작정 군의 알고리즘 중 가장 중요한 부분이 됩니다.조금 어려운 말이지만 앞에서 설명한 것과 같은 식을 이용하여 처음 값과 끝 값에서 그 사이의 값을 구하는 것을 ′선형보간′이라고 합니다.뒷설명에서도또나오는말이기때문에잠깐만기억해두세요.이 선형보간을 10개의 꼭지점 각각에 대해 실시하면 시작의 형태와 끝의 형태로부터 그 사이의 형태를 구할 수 있습니다.그런데, 이러한 알고리즘을 이용해 만든 무님군의 프로토타입의 코드가 리스트 1입니다.실행용 html 파일은 리스트3의 애플릿 태그를 다음과 같이 바꿉니다.리스트 1: 무님니 너의 프로토타입 (「Simple Muni.java」)
import java . applet . * ;
import java . awt . * ;
import java . lang . Math ;
public class SimpleMuni extends Applet implements Runnable
{
Thread thread = null ;
Image bufferImage; // 더블 버퍼용 이미지
Graphics bufferG ;
int [ ] x0 = new int[10]; // 별꼭지점의 x 좌표 배열
int [ ] y0 = new int[10]; // 별꼭지점의 y 좌표배열
int [ ] x1 = new int[10]; // 원형 정점의 x 좌표 배열
int [ ] y1 = new int[10]; // 원형정점의 y 좌표배열
public void init ( )
{
resize ( 300 , 300 ) ;
bufferImage = createImage ( 300 , 300 ) ;
bufferG = bufferImage . getGraphics ( ) ;
bufferImageClear ( ) ;
// 별모양과 원형의 꼭지점 좌표값 계산
for ( int i = 0 ; i < 10 ; i++ )
{
x0 [ i ] = ( int ) ( 150 + ( 40 + 90 * (( i + 1 ) % 2 ))
* Math . sin ( Math . PI * i * 36 / 180 )) ;
y0 [ i ] = ( int ) ( 150 - ( 40 + 90 * (( i + 1 ) % 2 ))
* Math . cos ( Math . PI * i * 36 / 180 )) ;
x1 [ i ] = ( int ) ( 150 + 90 * Math . sin ( Math . PI * i * 36 / 180 )) ;
y1 [ i ] = ( int ) ( 150 - 90 * Math . cos ( Math . PI * i * 36 / 180 )) ;
}
//스레드 인스턴스 작성 및 시작
thread = new Thread ( this ) ;
thread . start ( ) ;
}
public void bufferImageClear ( )
{
bufferG . setColor ( Color . white ) ;
bufferG . fillRect ( 0 , 0 , 300 , 300 ) ;
}
public void update ( Graphics g ) { paint ( g ) ; }
public void paint ( Graphics g )
{ g . drawImage ( bufferImage , 0 , 0 , this ) ; }
public void run ( )
{
try
{
while ( true )
{
//별모양에서 원형으로 모핑
for ( int i = 0 ; i < = 20 ; i++ )
{
drawMuni (( double ) i / 20 ) ;
Thread . sleep ( 50 ) ;
}
Thread . sleep ( 500 ) ;
//원형에서 별모양으로 모핑
for ( int i = 20 ; i > = 0 ; i -- )
{
drawMuni (( double ) i / 20 ) ;
Thread . sleep ( 50 ) ;
}
Thread . sleep ( 500 ) ;
}
}
catch ( InterruptedException e ) { }
}
public void drawMuni ( double t )
{
bufferImageClear ( ) ;
Polygon poly = new Polygon ( ) ;
//중간 형상의 좌표값 계산
for ( int i = 0 ; i < 10 ; i++ )
poly . addPoint (( int ) ( x0 [ i ] * ( 1 . 0 - t ) + x1 [ i ] * t ) ,
( int ) ( y0 [ i ] * ( 1 . 0 - t ) + y1 [ i ] * t )) ;
// 중간형상 그리기
bufferG . setColor ( Color . blue ) ;
bufferG . fillPolygon ( poly ) ;
repaint ( ) ;
}
}}리스트1의 Simple Muni애플렛을 실행하면 10개의 정점으로 이루어진 별 모양이 나타나고 그 모양이 서서히 정 10각형으로 변형됩니다.정10각형이 된 후에는 다시 별 모양으로 돌아갑니다.실행 결과는 아래의 그림1과 같습니다.실제로 움직이고 있는 것은 이쪽에서 보실 수 있습니다.자, 그럼 이제 리스트 1을 살펴볼까요?프로토타입이기 때문에 정점의 수는 10개로 고정해 버리고, 처음 정점의 x좌표와 y좌표를 x0[], y0[]에, 마지막 정점의 x좌표와 y좌표를 x1[], y1[]에 격납하고 있습니다.이 사이를 서서히 이동하는 정점의 x좌표와 y좌표를 앞에서 말한 선형보간의 식으로 계산함으로써 실제로 그림을 그리는 다각형 Polygon을 작성했습니다.코드는 다음과 같이 되어 있군요.Polygon poly = new Polygon ( ) ;
//중간 형상의 좌표값 계산
for ( int i = 0 ; i < 10 ; i++ )
poly . addPoint (( int ) ( x0 [ i ] * ( 1 . 0 - t ) + x1 [ i ] * t ) ,
(int)(y0[i]*(1.0-t)+y1[i]*t));「Q=(1-t)P0+tP1」의 계산을 각 정점의 x좌표, y좌표 각각에 대해서 하여 새로운 정점의 좌표값을 구하고 있음을 알 수 있을까요?Polygon에 대해서는 칼럼을 참조하십시오.쓰레드의 실제 처리는 run() 함수 안에서 정의되어 있습니다.Thread.sleep()와 draw Muni()의 함수가 번갈아 불리고 있네요.draw Muni() 함수는 0에서 1의 값을 인수로 하고 그 값을 바탕으로 중간 형상을 표시합니다.즉, 일정 시간 정지하면 변형이라고 표시를 실시하는 처리를 반복하는 것으로, 무턱대고 군의 애니메이션을 실시하고 있는 것입니다.또, 애니메이션이 깜박이지 않게, 더블 버퍼를 이용하고 있는 것도 주의합시다.더블버퍼에 대한 자세한 내용은 칼럼을 참조해 주십시오.무럭무럭 군을 확장하자 무럭무럭 군 프로토타입이 움직이게 되었으므로, 이번에는 조금 더 확장해서, 더 즐거운 무럭무럭 군을 만들어 봅시다.여러 모양을 연속 변형시키자 모처럼 형태가 바뀌어 가는데, 모양이 2 개만으로는 지루하네요.더 많은 형태를 취급할 수 있도록 합시다.저번에는 x0[], y0[], x1[], y1[]이라고 하는 배열을 사용했는데, 형태가 증가할 때마다 이러한 배열을 추가하면 큰일입니다.Vector를 사용해 여러 형태를 저장할 수 있도록 합니다.또한 새로운 형태를 추가하기 쉽도록 무심히 너의 형태를 나타내는 클래스 MuniFigure와 무심히 너의 데이터를 저장하는 클래스 MuniData를 작성했습니다.소스코드 안에서는 Vector muniDataVector;로서 무작정 군의 형태의 데이터를 Vector에 저장하고 있습니다.Vector에 대해서는 칼럼을 참조하기 바랍니다.꼭지점 수가 다른 다각형을 다루자 지난번에는 두 형태 모두 꼭지점 수가 10개로 같았습니다.이번에는 꼭지점 수가 다른 다각형끼리 모핑을 해보겠습니다.이를 엄밀하게 수행하면 좀 어려운 수식이 나와버리기 때문에 모핑을 시작하기 전에 정점이 적은 쪽의 정점을 분할해서 정점이 많은 쪽의 정점과 같은 수로 만들어버리는 간단한 알고리즘으로 대응하도록 하겠습니다.그림2는 3각형과 5각형의 모핑을 하는 경우의 예를 나타내고 있습니다.변형을 실시하기 전에 삼각형을 구성하는 3개의 정점 중 2개의 정점을 분할해 합계 5개의 정점으로 해 버립니다.이렇게 하면 지금까지의 알고리즘을 그대로 사용할 수 있습니다.코드안에서는, 시작의 MuniData 와 끝의 MuniData 를 인수로 하는 MuniFigure 의 컨스트럭터로, 새로운 중간 형상을 작성하고 있습니다.Muni Figure의 컨스트럭터
public Muni Figure (Muni Data start Muni, Muni Data end Muni) 눈여겨볼 표정 없는 캐릭터라는 것도 쓸쓸하니까 무심히 너에게 눈여겨봐요.단순한 다각형만의 표현에, 조금 애교가 생겼어요(그림 3).눈의 움직이는 방법은 처음 위치와 끝의 위치를 알고 있기 때문에 다른 정점과 마찬가지로 계산할 수 있습니다.아무래도 눈모양을 모핑하기는 힘들어서 이동하면서 일정시간마다 눈을 깜빡이도록 했습니다.열린 눈은 다음과 같이 fill Oval에 의해 타원을 복수 그렸습니다.취향에 따라 눈의 크기를 바꾸거나 패턴을 늘리거나 하는 것도 좋네요.public void drawOpenEyes ( Graphics g , Point eye 0 , Point eye 1 )
{
g . setColor ( Color . white ) ;
g . fillOval ( eye 0 . x , eye 0 . y , 12 , 18 ) ;
g . fillOval ( eye 1 . x , eye 1 . y , 12 , 18 ) ;
g . setColor ( Color . black ) ;
g . fillOval ( eye 0 . x + 4 , eye 0 . y + 5 , 8 , 13 ) ;
g . fillOval ( eye 1 . x + 4 , eye 1 . y + 5 , 8 , 13 ) ;
g . setColor ( Color . white ) ;
g . fillOval ( eye 0 . x + 9 , eye 0 . y + 7 , 3 , 5 ) ;
g . fillOval ( eye 1 . x + 9 , eye 1 . y + 7 , 3 , 5 ) ;
}}색상도 바꾸자 모든 모양이 같은 색이라고 하기에도 무미건조하기 때문에, 모양에 따라 색상이 바뀌도록 해 봅시다.지금까지는 정점의 좌표를 선형보간에서 구했지만 이와 같은 것을 색의 R, G, B 성분에 대해서도 실시함으로써 간의 색을 구할 수 있습니다.실제 코드에서는 다음과 같은 함수로 두 가지 색 사이의 색을 취득합니다.좌표값을 계산하는 것과 비슷하네요.private Color getColor ( int [ ] c0 , int [ ] c1 , double t )
{
return new Color (( int ) ( c0 [ 0 ] * ( 1 - t ) + c1 [ 0 ] * t ) ,
( int ) ( c0 [ 1 ] * ( 1 - t ) + c1 [ 1 ] * t ) ,
( int ) ( c0 [ 2 ] * ( 1 - t ) + c1 [ 2 ] * t )) ;
} 여기서 c0[0], c0[1], c0[2]는 각각 시작 색상의 RGB 성분을, c1[0], c1[1], c1[2]은 각각 끝 색상의 RGB 성분을 나타내고 있습니다.파일로부터 데이터를 읽어들이기 프로토타입판에서는, 직접 코드안에서 좌표 데이터를 계산하고 있었습니다만, 데이터가 바뀔 때마다 컴파일 하고 있는 것은 힘듭니다.거기서, 외부 파일로부터 데이터를 읽어들여 표시하도록 합시다.파일 액세스에 제한이 있는 애플릿에서도, 같은 서버상의 파일의 읽기는 문제 없게 실시할 수 있습니다.이번에는 무작정 군애플렛이 놓여있는 디렉토리와 같은 위치에 있는 munidata.txt라고 하는 파일에서 데이터를 읽어보도록 하겠습니다.그런데, 파일에서 데이터를 가져오려면 먼저 어떤 형식으로 데이터가 파일에 써져 있는지를 결정해야 합니다.구체적인 예로서 리스트 2와 같이 데이터를 파일에 기술해 두기로 했습니다.이 파일을 불러와서 무턱대고 너의 형태를 표시하도록 하겠습니다.리스트2는 빨간색 3각형과 파란색 4각형 데이터를 기술한 예입니다.먼저 ′color′라고 하는 문자열 뒤에 R, G, B 성분의 값을 이어서 적어주면 됩니다.그런 다음 data라고 하는 문자열 뒤에 좌표값 데이터가 몇 개나 존재하는지를 나타내는 값을 적습니다.이 값은 「정점의 수+2」가 됩니다.「+2」가 되는 것은, 정점의 좌표치에 가세해 양쪽 눈의 좌표치가 더해지기 때문입니다.3각형 데이터의 경우 data 5라고 되어 있습니다.이 다음 행에 첫 번째 정점의 x좌표값, y좌표값, 두 번째 정점의 x좌표값, y좌표값…과 같은 값을 계속합니다.여기서 마지막 두 개는 눈의 좌표값입니다.이제 1가지 무턱대고 너를 나타내는 데 필요한 정보를 모두 기술할 수 있습니다.이제 무작정 군 형태의 수만큼 이러한 데이터를 계속 기술하겠습니다.리스트 2: 무작정 당신의 데이터 파일 예시
color 255 0 0
data 5
169 41 81 209 282 209 163 136 184 137
color 0 0 255
data 6
11659 84 212 254 228 245 77 146 117 194 118 그러면 이 파일을 읽기 위한 메서드를 만들어 보겠습니다.이번에는 위에서 정의한 바와 같이 데이터 파일을 읽기 위한 loadData()라고 하는 함수를 리스트 3과 같이 만들어 보았습니다.이 함수는 파일에서 데이터를 불러와 MuniData의 인스턴스를 만들고 muniDataVector에 저장해 나갑니다.읽기를 성공하면 true를 반환하고 실패하면 false를 반환한다.リスト3:ファイルからデータを読み込む関数
공용 부울 로드데이터()
{
입력 스트림이 = null입니다.
MuniDatamuniData = null;
노력하다
{
= 새 URL(getDocumentBase(), DATA_FILE).openStream();
판독기 판독기
= 새 버퍼링 판독기(새 입력 스트림 판독기(is));
스트림Tokenizer st = 새 스트림토큰라이저(판독기);
int 토큰;
while((수정 = st.next 토큰()) != 스트림토큰라이저.TT_EOF)
{
x,y;
if(수정 == 스트림)토큰라이저.TT_WORD)
{
if(′st.sval.color′)
{
muniData = new MuniData();
st.next 토큰(); intr = (int)st.nval;
st.next 토큰(); int g = (int)st.nval;
st.next 토큰(); intb = (int)st.nval;
muniData.set 색상(r, g, b);
}
그렇지 않으면 if(′st.sval.dampla data′)
{
성.다음 토큰();
int point_num = (int)st.nval;
(inti = 0; i = 점_num; i++)
{
st.next 토큰(); x = (int)st.nval;
st.next 토큰(); y = (int)st.nval;
muniData.addPoint(새 점(x, y));
}
muniDataVector.addElement(muniData);
}
}
}
is.closefilen;
}
catch(예외)
{
거짓 반환;
}
true를 반환합니다;
}アプレットからweb上のファイルを読み込むには、urlのインスタンスに対してopenstream()メソッドを呼び出し、入力ストリームを取得します。具体的には、inputstream is
= 새 URL(getDocumentBase(), ′munidata.txt′).openStream();은(는) munidata입니다.txt」というファイルの入力ストリームを取得できます。さて、それでは入力ストリームとは何でしょう? この説明を詳しく書くと、とても長くなってしまうので、極めて単純に、「読み込んだデータの流れのこと」だと思ってください。このデータの流れに対して、いろいろな処理を行い、そのデータの中に含まれる文字列やtokenizerを使用して入力ストリームから文字列と数値を読み込んでいます。streamtokenizerに対してnexttoken()を呼び出すと、入力ストリームから次のデータを取り出し、取り出したデータがどのような型であるかを示す値が返されます。読み込んだデータの値は、そのデータが数値の場合はnvalに、文字列す。実際に動いているものはこちらでご覧いただけます。「むにえでぃたあ」を作ろう今まで、むにむに君の形を新しく追加する場合は、手作業で数値を入力する必要がありましたが、これではあまりにも大変です。jguiを構築することが比較的簡単にできるので、インタラクティブに形を入力できるエディタを作ってみましょう。むにむに君の形を編集するものなので「むにえでぃたあ」と名づけてみました。さて、ここで注意しなけれウトです。形の作成と修正はマウスのクリックとドラッグで行います。むにむに君の新しい形の作成から保存までは次のようなステップで行います。おおまかな輪郭線の作成
マウスをクリックすると、クリックした点を結ぶ折れ線が表示されます(図5-2)。頂点の数には制限がないので、好きな形を作っていきましょう。
最初にクリックした点を再びクリックすると多角形が閉じて、その内側が塗りつぶされます。それと同時に、2つの目がぴょこっと現れます(図5-3)。
色の決定と輪郭線の微調整
作成した形の頂点を、マウスのドラッグで移動させることができます。目の位置も同様にドラッグで移動できます。
輪郭線を微調整して満足のいく形にしましょう。また、色の調節のスライダーを動かすことで、好みの色に設定できます。
データの保存
形と色が決まったら、「保存」ボタンをクリックしてデータを保存しましょう。むにえでぃたあが存在するディレクトの「munidata.txt」という名前のファイルにデータが追加保存されます。(ファイル名が固定なので、保存先ファイル選択のダイアログ ボックスボックスは表示されません。)
ここで作成した「munidata.txt」を、むにむに君アプレットと同じディレクトリに置けば、その形が見事アプレットに反映されます。記事上部のリンクから「むにえでぃたあ」のソースコードをダウンロードしてください。コンパイル後、コマンドプロ 本稿で紹介した範囲でも、ある程度のものができたと思っていますが、まだまだ工夫次第で独自の楽しいむにむに君が作れそうですね。以下に、さらなる発展のためのヒントを記してみますので、興味のある方はチャレンジしてみてください。「무님무님군」변형의 순서를 랜덤으로 한다.
눈에 움직임을 넣다
무턱대고 자네에게 입을 대다
등록되어 있는 무님에 너의 형태를 일람표시하는 「무니에디터어」후에 정점을 추가하거나 삭제를 할 수 있도록 한다.
컴퓨터 그래픽스 홈페이지(CGI)와 연계하여 홈페이지에서 형태를 등록할 수 있도록 하다
import java . applet . * ;
import java . awt . * ;
import java . lang . Math ;
public class SimpleMuni extends Applet implements Runnable
{
Thread thread = null ;
Image bufferImage; // 더블 버퍼용 이미지
Graphics bufferG ;
int [ ] x0 = new int[10]; // 별꼭지점의 x 좌표 배열
int [ ] y0 = new int[10]; // 별꼭지점의 y 좌표배열
int [ ] x1 = new int[10]; // 원형 정점의 x 좌표 배열
int [ ] y1 = new int[10]; // 원형정점의 y 좌표배열
public void init ( )
{
resize ( 300 , 300 ) ;
bufferImage = createImage ( 300 , 300 ) ;
bufferG = bufferImage . getGraphics ( ) ;
bufferImageClear ( ) ;
// 별모양과 원형의 꼭지점 좌표값 계산
for ( int i = 0 ; i < 10 ; i++ )
{
x0 [ i ] = ( int ) ( 150 + ( 40 + 90 * (( i + 1 ) % 2 ))
* Math . sin ( Math . PI * i * 36 / 180 )) ;
y0 [ i ] = ( int ) ( 150 - ( 40 + 90 * (( i + 1 ) % 2 ))
* Math . cos ( Math . PI * i * 36 / 180 )) ;
x1 [ i ] = ( int ) ( 150 + 90 * Math . sin ( Math . PI * i * 36 / 180 )) ;
y1 [ i ] = ( int ) ( 150 - 90 * Math . cos ( Math . PI * i * 36 / 180 )) ;
}
//스레드 인스턴스 작성 및 시작
thread = new Thread ( this ) ;
thread . start ( ) ;
}
public void bufferImageClear ( )
{
bufferG . setColor ( Color . white ) ;
bufferG . fillRect ( 0 , 0 , 300 , 300 ) ;
}
public void update ( Graphics g ) { paint ( g ) ; }
public void paint ( Graphics g )
{ g . drawImage ( bufferImage , 0 , 0 , this ) ; }
public void run ( )
{
try
{
while ( true )
{
//별모양에서 원형으로 모핑
for ( int i = 0 ; i < = 20 ; i++ )
{
drawMuni (( double ) i / 20 ) ;
Thread . sleep ( 50 ) ;
}
Thread . sleep ( 500 ) ;
//원형에서 별모양으로 모핑
for ( int i = 20 ; i > = 0 ; i -- )
{
drawMuni (( double ) i / 20 ) ;
Thread . sleep ( 50 ) ;
}
Thread . sleep ( 500 ) ;
}
}
catch ( InterruptedException e ) { }
}
public void drawMuni ( double t )
{
bufferImageClear ( ) ;
Polygon poly = new Polygon ( ) ;
//중간 형상의 좌표값 계산
for ( int i = 0 ; i < 10 ; i++ )
poly . addPoint (( int ) ( x0 [ i ] * ( 1 . 0 - t ) + x1 [ i ] * t ) ,
( int ) ( y0 [ i ] * ( 1 . 0 - t ) + y1 [ i ] * t )) ;
// 중간형상 그리기
bufferG . setColor ( Color . blue ) ;
bufferG . fillPolygon ( poly ) ;
repaint ( ) ;
}
}}리스트1의 Simple Muni애플렛을 실행하면 10개의 정점으로 이루어진 별 모양이 나타나고 그 모양이 서서히 정 10각형으로 변형됩니다.정10각형이 된 후에는 다시 별 모양으로 돌아갑니다.실행 결과는 아래의 그림1과 같습니다.실제로 움직이고 있는 것은 이쪽에서 보실 수 있습니다.자, 그럼 이제 리스트 1을 살펴볼까요?프로토타입이기 때문에 정점의 수는 10개로 고정해 버리고, 처음 정점의 x좌표와 y좌표를 x0[], y0[]에, 마지막 정점의 x좌표와 y좌표를 x1[], y1[]에 격납하고 있습니다.이 사이를 서서히 이동하는 정점의 x좌표와 y좌표를 앞에서 말한 선형보간의 식으로 계산함으로써 실제로 그림을 그리는 다각형 Polygon을 작성했습니다.코드는 다음과 같이 되어 있군요.Polygon poly = new Polygon ( ) ;
//중간 형상의 좌표값 계산
for ( int i = 0 ; i < 10 ; i++ )
poly . addPoint (( int ) ( x0 [ i ] * ( 1 . 0 - t ) + x1 [ i ] * t ) ,
(int)(y0[i]*(1.0-t)+y1[i]*t));「Q=(1-t)P0+tP1」의 계산을 각 정점의 x좌표, y좌표 각각에 대해서 하여 새로운 정점의 좌표값을 구하고 있음을 알 수 있을까요?Polygon에 대해서는 칼럼을 참조하십시오.쓰레드의 실제 처리는 run() 함수 안에서 정의되어 있습니다.Thread.sleep()와 draw Muni()의 함수가 번갈아 불리고 있네요.draw Muni() 함수는 0에서 1의 값을 인수로 하고 그 값을 바탕으로 중간 형상을 표시합니다.즉, 일정 시간 정지하면 변형이라고 표시를 실시하는 처리를 반복하는 것으로, 무턱대고 군의 애니메이션을 실시하고 있는 것입니다.또, 애니메이션이 깜박이지 않게, 더블 버퍼를 이용하고 있는 것도 주의합시다.더블버퍼에 대한 자세한 내용은 칼럼을 참조해 주십시오.무럭무럭 군을 확장하자 무럭무럭 군 프로토타입이 움직이게 되었으므로, 이번에는 조금 더 확장해서, 더 즐거운 무럭무럭 군을 만들어 봅시다.여러 모양을 연속 변형시키자 모처럼 형태가 바뀌어 가는데, 모양이 2 개만으로는 지루하네요.더 많은 형태를 취급할 수 있도록 합시다.저번에는 x0[], y0[], x1[], y1[]이라고 하는 배열을 사용했는데, 형태가 증가할 때마다 이러한 배열을 추가하면 큰일입니다.Vector를 사용해 여러 형태를 저장할 수 있도록 합니다.또한 새로운 형태를 추가하기 쉽도록 무심히 너의 형태를 나타내는 클래스 MuniFigure와 무심히 너의 데이터를 저장하는 클래스 MuniData를 작성했습니다.소스코드 안에서는 Vector muniDataVector;로서 무작정 군의 형태의 데이터를 Vector에 저장하고 있습니다.Vector에 대해서는 칼럼을 참조하기 바랍니다.꼭지점 수가 다른 다각형을 다루자 지난번에는 두 형태 모두 꼭지점 수가 10개로 같았습니다.이번에는 꼭지점 수가 다른 다각형끼리 모핑을 해보겠습니다.이를 엄밀하게 수행하면 좀 어려운 수식이 나와버리기 때문에 모핑을 시작하기 전에 정점이 적은 쪽의 정점을 분할해서 정점이 많은 쪽의 정점과 같은 수로 만들어버리는 간단한 알고리즘으로 대응하도록 하겠습니다.그림2는 3각형과 5각형의 모핑을 하는 경우의 예를 나타내고 있습니다.변형을 실시하기 전에 삼각형을 구성하는 3개의 정점 중 2개의 정점을 분할해 합계 5개의 정점으로 해 버립니다.이렇게 하면 지금까지의 알고리즘을 그대로 사용할 수 있습니다.코드안에서는, 시작의 MuniData 와 끝의 MuniData 를 인수로 하는 MuniFigure 의 컨스트럭터로, 새로운 중간 형상을 작성하고 있습니다.Muni Figure의 컨스트럭터
public Muni Figure (Muni Data start Muni, Muni Data end Muni) 눈여겨볼 표정 없는 캐릭터라는 것도 쓸쓸하니까 무심히 너에게 눈여겨봐요.단순한 다각형만의 표현에, 조금 애교가 생겼어요(그림 3).눈의 움직이는 방법은 처음 위치와 끝의 위치를 알고 있기 때문에 다른 정점과 마찬가지로 계산할 수 있습니다.아무래도 눈모양을 모핑하기는 힘들어서 이동하면서 일정시간마다 눈을 깜빡이도록 했습니다.열린 눈은 다음과 같이 fill Oval에 의해 타원을 복수 그렸습니다.취향에 따라 눈의 크기를 바꾸거나 패턴을 늘리거나 하는 것도 좋네요.public void drawOpenEyes ( Graphics g , Point eye 0 , Point eye 1 )
{
g . setColor ( Color . white ) ;
g . fillOval ( eye 0 . x , eye 0 . y , 12 , 18 ) ;
g . fillOval ( eye 1 . x , eye 1 . y , 12 , 18 ) ;
g . setColor ( Color . black ) ;
g . fillOval ( eye 0 . x + 4 , eye 0 . y + 5 , 8 , 13 ) ;
g . fillOval ( eye 1 . x + 4 , eye 1 . y + 5 , 8 , 13 ) ;
g . setColor ( Color . white ) ;
g . fillOval ( eye 0 . x + 9 , eye 0 . y + 7 , 3 , 5 ) ;
g . fillOval ( eye 1 . x + 9 , eye 1 . y + 7 , 3 , 5 ) ;
}}색상도 바꾸자 모든 모양이 같은 색이라고 하기에도 무미건조하기 때문에, 모양에 따라 색상이 바뀌도록 해 봅시다.지금까지는 정점의 좌표를 선형보간에서 구했지만 이와 같은 것을 색의 R, G, B 성분에 대해서도 실시함으로써 간의 색을 구할 수 있습니다.실제 코드에서는 다음과 같은 함수로 두 가지 색 사이의 색을 취득합니다.좌표값을 계산하는 것과 비슷하네요.private Color getColor ( int [ ] c0 , int [ ] c1 , double t )
{
return new Color (( int ) ( c0 [ 0 ] * ( 1 - t ) + c1 [ 0 ] * t ) ,
( int ) ( c0 [ 1 ] * ( 1 - t ) + c1 [ 1 ] * t ) ,
( int ) ( c0 [ 2 ] * ( 1 - t ) + c1 [ 2 ] * t )) ;
} 여기서 c0[0], c0[1], c0[2]는 각각 시작 색상의 RGB 성분을, c1[0], c1[1], c1[2]은 각각 끝 색상의 RGB 성분을 나타내고 있습니다.파일로부터 데이터를 읽어들이기 프로토타입판에서는, 직접 코드안에서 좌표 데이터를 계산하고 있었습니다만, 데이터가 바뀔 때마다 컴파일 하고 있는 것은 힘듭니다.거기서, 외부 파일로부터 데이터를 읽어들여 표시하도록 합시다.파일 액세스에 제한이 있는 애플릿에서도, 같은 서버상의 파일의 읽기는 문제 없게 실시할 수 있습니다.이번에는 무작정 군애플렛이 놓여있는 디렉토리와 같은 위치에 있는 munidata.txt라고 하는 파일에서 데이터를 읽어보도록 하겠습니다.그런데, 파일에서 데이터를 가져오려면 먼저 어떤 형식으로 데이터가 파일에 써져 있는지를 결정해야 합니다.구체적인 예로서 리스트 2와 같이 데이터를 파일에 기술해 두기로 했습니다.이 파일을 불러와서 무턱대고 너의 형태를 표시하도록 하겠습니다.리스트2는 빨간색 3각형과 파란색 4각형 데이터를 기술한 예입니다.먼저 ′color′라고 하는 문자열 뒤에 R, G, B 성분의 값을 이어서 적어주면 됩니다.그런 다음 data라고 하는 문자열 뒤에 좌표값 데이터가 몇 개나 존재하는지를 나타내는 값을 적습니다.이 값은 「정점의 수+2」가 됩니다.「+2」가 되는 것은, 정점의 좌표치에 가세해 양쪽 눈의 좌표치가 더해지기 때문입니다.3각형 데이터의 경우 data 5라고 되어 있습니다.이 다음 행에 첫 번째 정점의 x좌표값, y좌표값, 두 번째 정점의 x좌표값, y좌표값…과 같은 값을 계속합니다.여기서 마지막 두 개는 눈의 좌표값입니다.이제 1가지 무턱대고 너를 나타내는 데 필요한 정보를 모두 기술할 수 있습니다.이제 무작정 군 형태의 수만큼 이러한 데이터를 계속 기술하겠습니다.리스트 2: 무작정 당신의 데이터 파일 예시
color 255 0 0
data 5
169 41 81 209 282 209 163 136 184 137
color 0 0 255
data 6
11659 84 212 254 228 245 77 146 117 194 118 그러면 이 파일을 읽기 위한 메서드를 만들어 보겠습니다.이번에는 위에서 정의한 바와 같이 데이터 파일을 읽기 위한 loadData()라고 하는 함수를 리스트 3과 같이 만들어 보았습니다.이 함수는 파일에서 데이터를 불러와 MuniData의 인스턴스를 만들고 muniDataVector에 저장해 나갑니다.읽기를 성공하면 true를 반환하고 실패하면 false를 반환한다.リスト3:ファイルからデータを読み込む関数
공용 부울 로드데이터()
{
입력 스트림이 = null입니다.
MuniDatamuniData = null;
노력하다
{
= 새 URL(getDocumentBase(), DATA_FILE).openStream();
판독기 판독기
= 새 버퍼링 판독기(새 입력 스트림 판독기(is));
스트림Tokenizer st = 새 스트림토큰라이저(판독기);
int 토큰;
while((수정 = st.next 토큰()) != 스트림토큰라이저.TT_EOF)
{
x,y;
if(수정 == 스트림)토큰라이저.TT_WORD)
{
if(′st.sval.color′)
{
muniData = new MuniData();
st.next 토큰(); intr = (int)st.nval;
st.next 토큰(); int g = (int)st.nval;
st.next 토큰(); intb = (int)st.nval;
muniData.set 색상(r, g, b);
}
그렇지 않으면 if(′st.sval.dampla data′)
{
성.다음 토큰();
int point_num = (int)st.nval;
(inti = 0; i = 점_num; i++)
{
st.next 토큰(); x = (int)st.nval;
st.next 토큰(); y = (int)st.nval;
muniData.addPoint(새 점(x, y));
}
muniDataVector.addElement(muniData);
}
}
}
is.closefilen;
}
catch(예외)
{
거짓 반환;
}
true를 반환합니다;
}アプレットからweb上のファイルを読み込むには、urlのインスタンスに対してopenstream()メソッドを呼び出し、入力ストリームを取得します。具体的には、inputstream is
= 새 URL(getDocumentBase(), ′munidata.txt′).openStream();은(는) munidata입니다.txt」というファイルの入力ストリームを取得できます。さて、それでは入力ストリームとは何でしょう? この説明を詳しく書くと、とても長くなってしまうので、極めて単純に、「読み込んだデータの流れのこと」だと思ってください。このデータの流れに対して、いろいろな処理を行い、そのデータの中に含まれる文字列やtokenizerを使用して入力ストリームから文字列と数値を読み込んでいます。streamtokenizerに対してnexttoken()を呼び出すと、入力ストリームから次のデータを取り出し、取り出したデータがどのような型であるかを示す値が返されます。読み込んだデータの値は、そのデータが数値の場合はnvalに、文字列す。実際に動いているものはこちらでご覧いただけます。「むにえでぃたあ」を作ろう今まで、むにむに君の形を新しく追加する場合は、手作業で数値を入力する必要がありましたが、これではあまりにも大変です。jguiを構築することが比較的簡単にできるので、インタラクティブに形を入力できるエディタを作ってみましょう。むにむに君の形を編集するものなので「むにえでぃたあ」と名づけてみました。さて、ここで注意しなけれウトです。形の作成と修正はマウスのクリックとドラッグで行います。むにむに君の新しい形の作成から保存までは次のようなステップで行います。おおまかな輪郭線の作成
マウスをクリックすると、クリックした点を結ぶ折れ線が表示されます(図5-2)。頂点の数には制限がないので、好きな形を作っていきましょう。
最初にクリックした点を再びクリックすると多角形が閉じて、その内側が塗りつぶされます。それと同時に、2つの目がぴょこっと現れます(図5-3)。
色の決定と輪郭線の微調整
作成した形の頂点を、マウスのドラッグで移動させることができます。目の位置も同様にドラッグで移動できます。
輪郭線を微調整して満足のいく形にしましょう。また、色の調節のスライダーを動かすことで、好みの色に設定できます。
データの保存
形と色が決まったら、「保存」ボタンをクリックしてデータを保存しましょう。むにえでぃたあが存在するディレクトの「munidata.txt」という名前のファイルにデータが追加保存されます。(ファイル名が固定なので、保存先ファイル選択のダイアログ ボックスボックスは表示されません。)
ここで作成した「munidata.txt」を、むにむに君アプレットと同じディレクトリに置けば、その形が見事アプレットに反映されます。記事上部のリンクから「むにえでぃたあ」のソースコードをダウンロードしてください。コンパイル後、コマンドプロ 本稿で紹介した範囲でも、ある程度のものができたと思っていますが、まだまだ工夫次第で独自の楽しいむにむに君が作れそうですね。以下に、さらなる発展のためのヒントを記してみますので、興味のある方はチャレンジしてみてください。「무님무님군」변형의 순서를 랜덤으로 한다.
눈에 움직임을 넣다
무턱대고 자네에게 입을 대다
등록되어 있는 무님에 너의 형태를 일람표시하는 「무니에디터어」후에 정점을 추가하거나 삭제를 할 수 있도록 한다.
컴퓨터 그래픽스 홈페이지(CGI)와 연계하여 홈페이지에서 형태를 등록할 수 있도록 하다
반응형