第五週 スクリプトによるグラフィックス,モーフィング
計算機序論2, 
www.kameda-lab.org 
2004/11/07
実習にあたって
    今週もC言語の習得のための課題です.
    スクリプトによりグラフィックスを自由に制御できるようにすることと,
    モーフィングと呼ばれるグラフィックスのテクニックを扱います.
スクリプトによる描画の書式
    まずは,スクリプトの概要について考えよう.
    サンプルプログラムでは,以下のような書式を設定している.
    コメント(#),データ入力(L, F, P),描画制御(C, S),
    標準的な描画(T, D),応用的な描画(I, M),繰り返し・終了(E, Z),
    等を用意している.
//  コマンド:
//
//  #: コメント (その行は無視される)
//
//  L: 線分の登録
//     L sx sy sz ex ey ez r g b w
//  F: そこまでをひとまとまりの図形として宣言
//  P: 幾何変換の登録
//     P dx dy dz sx sy sz tx ty tz
//
//  C: 画面をクリア
//  S: スリープ (指定された数 * 1msec)
//
//  T: 幾何変換をセット
//  D: 指定された図形を描画
//
//  I: スムーズに幾何変換パラメータを変更(補間)しながら描画
//     I F P1 P2 times usleep
//     (図形Fを変換P1からP2の間, times刻みで補間しながら描画.
//      途中でusleepづつスリープする)
//  M: モーフィング
//     M F1 P1 F2 P2 times usleep 
//     (図形F1を変換P1したものと図形F2を変換P2したものとのモーフィング)
//
//  E: ここまでを無限に繰り返す
//     (ただしこのプログラムではこれを指定するとキーを受け付けなくなる)
//  Z: これで終了
// 
    データの宣言から描画の制御までを一つのファイルに
    書いておくことにより,順番に実行される.
    プログラム中に書くのに比べ,格段に扱いやすくなる.
    実際には以下のようにファイルに書く.
    まず,"L","F"等で図形データを登録し,
    次に,"P"で幾何変換パラメータを登録する.
    その後,描画やその制御のコマンドとなる.
# F0: Y
L	-0.5 0.5 0.0	-0.25 0.0 0.0	1.0 0.5 0.5	3.0
L	0.0 0.5	0.0	-0.25 0.0 0.0   1.0 0.5	0.5	3.0
L	-0.25 0.0 0.0	-0.25 -0.5 0.0	1.0 0.5	0.5	3.0
F
# F1: N
L	0.0 0.5 0.0	0.0 -0.5 0.0	0.5 0.0 1.0	5.0
L	0.0 0.5 0.0	0.5 -0.5 0.0	0.5 0.0 1.0	5.0
L	0.5 -0.5 0.0	0.5 0.5 0.0	0.5 0.0 1.0	5.0
F
(中略)
# 幾何変換 P0〜P3
P	0.3 0.3 -2.0	0.5 0.5 0.5	0.0 0.0 0.0
P	0.0 0.0 -2.0	0.3 0.3 0.5	0.0 0.0 180.0
(中略)
# ここから描画コマンド
C	
T	1
D	1
S	500
(中略)
# スムーズな幾何変換
#	F	P1	P2	N	S
I	1	1	2	30	10
(中略)
#	モーフィング
#	F1	P1	F2	P2	N	S
M	1	1	0	2	30	10
S	500
M	0	2	1	1	30	10
(中略)
# 終了 (Eにすれば,ループとなる)
Z
    コマンドを自分で新しく定義し,
    もっと多様なことができるように拡張するのも面白いだろう.
    基本的には,execute_command 関数の辺りを理解すれば,
    拡張はできるはずである.
    
モーフィングの簡単なサンプル
    まずは,プログラム例を
    見てみよう.LINE_interpolation, draw_each_morphing_FIGURE,
    draw_morphing_FIGURE がモーフィングのための関数である.
    これらの関数は,ある図形から別の図形へ,
    徐々に変わっていく様子を描画するものである.
    今回のプログラムでは,一つの線分から対応する別の線分への変化を
    なめらかに行う.
     まず,図形として与えられたfigure1とfigure2の間で,
     その中間の図形を表示する関数(draw_morphing_FIGURE)が呼ばれる.
     その関数がパラメータを変更しながら,
     2図形のモーフィング図形を描く関数(draw_eah_morphing_FIGURE)を呼ぶ.
     これが,各々の図形間で
     対応する線分の間の補間をする関数(LINE_interpolation)を呼び,
     その結果を描画する.
     
// 2つの図形の中間段階を描く
void draw_each_morphing_FIGURE  (struct FIGURE *figure1, 
				 struct FIGURE *figure2, 
				 struct PARAMETER *parameter1, 
				 struct PARAMETER *parameter2, 
				 double ratio) {
  struct LINE *line1, *line2;
  struct LINE *transformed_line1, *transformed_line2;
  struct LINE *intermediate_line;
  line1 = figure1->first_line;
  line2 = figure2->first_line;
  
  while (line1 != NULL && line2 != NULL) {
    // line1をparameter1で変換したものと, 
    // line2をparameter2で変換したものの補間をする
    transformed_line1 = LINE_transform (line1, parameter1);
    transformed_line2 = LINE_transform (line2, parameter2);
    intermediate_line = LINE_interpolation
      (transformed_line1, transformed_line2, ratio);
    normalized_draw_LINE (intermediate_line);
    line1 = line1->next;
    line2 = line2->next;
    free_LINE(transformed_line1);
    free_LINE(transformed_line2);
    free_LINE(intermediate_line);
  } 
}
// 図形間を順にモーフィングしていく.中間段階を順に描いていく
void draw_morphing_FIGURE (struct FIGURE *figure1, 
			   struct FIGURE *figure2, 
			   struct PARAMETER *parameter1, 
			   struct PARAMETER *parameter2, 
			   int times, float sleep) {
  int i;
  
  for (i=0; i <= times; i++){
    glClear (GL_COLOR_BUFFER_BIT);
    draw_each_morphing_FIGURE
      (figure1, figure2, parameter1, parameter2, (double)i/times);
    glutSwapBuffers();
    glFlush();
    usleep(sleep * 1000);
  }
}
    モーフィングの要点は以下の関数に集約される.
    2つの線分の始点どうし,終点どうしの間の点を各々求めているだけである.
    この関数では,2つの線分を引数として,その中間的な線分を返す.
    構造体を返す関数であることに注意.
// 2つの線の中間(モーフィング)を求め, 線分の構造体として値を返す
struct LINE *LINE_interpolation(struct LINE *line1, 
				struct LINE *line2, 
				double ratio) {
  struct LINE *newline;
  double r1, r2;
  newline = new_LINE();
  r1 = 1 - ratio;
  r2 = ratio;
  newline->start->x = r1 * line1->start->x + r2 * line2->start->x;
  // (中略: 残りは自分で埋めよ。 end pointの他にもrgbと幅がある)
  return (newline);
}
データファイル例
以下のファイルはスクリプトの例である。
実行時にはfig-script.datというファイル名に変更しないと動かないことに注意。
課題
    
      - まずは上記の説明を参考に,
      プログラムの各部分に自分なりの注釈を付けよう.
- コマンドを解釈する部分を完成させよう.
      パラメータの補間やモーフィングにも,一部,欠けがある.
      それを埋めていこう.
- プログラムを完成させ,自分なりの作品を作ってみよう.
- (発展)ファイル名を自由に指定できるようにしてみよう.
- (発展)自分なりの描画関数を作ってみよう.
Yoshinari Kameda: 2004/09/17-
Yuichi Nakamura: Tue Aug 12 00:41:10 JST 2003