// Keisanki Joron 2 (Introduction to Computing II)
// Dept. of Engineering Systems, University of Tsukuba
// 2005/10/16, kameda@iit.tsukuba.ac.jp  Y.Kameda
//             [coutesy of Y.Nakamura]


//
//  ３次元グラフィックスのためのサンプルプログラム
//  
//  今回新しく出てくるデータ構造
//
//    double mat4x4[4][4]: 幾何変換のための行列(3次元グラフィックス用)
//
//  今回加わる／書き変える関数
//
//    行列操作関数
//  
//      copy_matrix()
//      compose_matrix()
//
//    幾何変換操作のための関数
//  
//      simple_translation()
//      simple_rotation_x(), simple_rotation_y(), simple_rotation_z()
//      simple_scaling()
//      create_transform_matrix()
// 
//      POINT_transform()
//      LINE_transform()
//      perspective_projection()
//
//      normalize_draw_LINE()
//      draw_FIGURE_transform()
//      draw_all_FIGURE()
//
//    アニメーションを行うための関数
//      PARAMETER_interpolation()
//      animation_test()
//
//

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>

// OpenGL Utility Toolkit 用ヘッダファイル
// OpenGLそのもののヘッダファイルなどはglut.hの中で呼ばれている
#include <GL/glut.h>

// スクリプトファイル名
#define DATA_FILENAME "fig-data.dat"

// デバッグ用のマクロ
#define DEBUG(s)  fprintf(stderr, "debug:" #s "\n"), fflush(stderr)


//-----------------------------------------------------
// データ構造, 定数の定義
//-----------------------------------------------------

//================================================================
// Windowのサイズ

#define WINDOW_WIDTH  400
#define WINDOW_HEIGHT 400

//================================================================
// ファイル中のコマンドの長さ(terminator含む)

#define CMDSTRLEN 128

//================================================================
// 点のための構造体
struct POINT{
  float x;
  float y;
  float z;
  float w;
};

//================================================================
// 線分のための構造体 (この構造体の線形リストで各文字や図形を表す). 
// 次の線分へのポインタを持つ．最初の線分にアクセスできれば, 数珠繋ぎに
// 最後までアクセスできる．LINE->next → LINE, ....
struct LINE {
  struct POINT *start;  // 始点
  struct POINT *end;    // 終点
  float r;              // 赤色の強度
  float g;              // 緑色の強度
  float b;              // 青色の強度
  float width;          // 線の太さ
  struct LINE *next;    // 次の線分
};

//================================================================
// 図形のための構造体 (この構造体のリストで図形のリストを表す)．
// 次の図形へのポインタを持つ．最初の図形にアクセスできれば, 数珠繋ぎに
// 最後までアクセスできる
struct FIGURE {
  struct LINE *first_line;   // (線分のリスト)の最初の線
  struct FIGURE *next;       // 次の線分リスト
};

//================================================================
// 幾何変換のための行列
typedef double  mat4x4[4][4];

//================================================================
// 幾何変換パラメータのための構造体 
// 9つのパラメータと幾何変換行列を保持．
// （次週で線形リストにする）
struct PARAMETER {
  float dx;               // 平行移動(x方向)
  float dy;               // 平行移動(y方向)
  float dz;               // 平行移動(z方向)
  float sx;               // スケール(x軸)
  float sy;               // スケール(y軸)
  float sz;               // スケール(z軸)
  float thetax;           // 回転(x軸)
  float thetay;           // 回転(y軸)
  float thetaz;           // 回転(z軸)
  mat4x4 *matrix;         // 変換行列
  struct PARAMETER *next; // 次のPARAMETER
};


//================================================================
// 関数のプロトタイプ宣言
void free_LINE (struct LINE *line);


//-----------------------------------------------------
// 大域変数の定義（linked-list-normal.cから場所を移動）
//-----------------------------------------------------

//================================================================
// 図形リストはコマンドファイル中の情報を全て保持する
struct FIGURE *Figures        = NULL; // 図形リストの先頭
struct FIGURE *Figure_at_end  = NULL; // 図形リストの終端

//================================================================
// 読み込み中の線分リストは最終的に１つの図形データ内に格納される
struct LINE *Lines_on_reading = NULL; // 線分リストの先頭
struct LINE *Line_at_end      = NULL; // 線分リストの終端

//-C================================================================
//-C 幾何変換パラメータリストはファイル中の幾何変換情報を全て保持する
//-C struct PARAMETER *Parameters_on_reading = NULL; // 幾何リストの先頭
//-C struct PARAMETER *Parameter_at_end      = NULL; // 幾何リストの終端

//================================================================
// 単位行列(何もしない変換行列)
mat4x4 theIdentity = {{1.0, 0.0, 0.0, 0.0}, 
		      {0.0, 1.0, 0.0, 0.0}, 
		      {0.0, 0.0, 1.0, 0.0},
		      {0.0, 0.0, 0.0, 1.0}};

//================================================================
// 視点の定義
struct POINT theViewPoint = {0.0, 0.0,  0.0, 1.0};
// 視平面の定義
struct POINT theViewPlane = {0.0, 0.0, -1.0, 1.0};

//-----------------------------------------------------
// データ構造を扱う関数／メモリ管理
//-----------------------------------------------------

//================================================================
// 新しい点構造体を作り, 前準備をしておく
struct POINT *new_POINT(void){
  struct POINT *newpoint = NULL;
  
  newpoint = (struct POINT *)calloc(1, sizeof(struct POINT));
  if (newpoint != NULL) {
    newpoint->w = 1.0; // 斉次座標系
  }
  return (newpoint);
}

//================================================================
// 新しい線分構造体を作り, 前準備をしておく
struct LINE *new_LINE(void){
  struct LINE *newline = NULL;

  newline = (struct LINE *)calloc(1, sizeof(struct LINE)); 
  if (newline != NULL) {
    // 始点, 終点を格納するための構造体もメモリ上に確保する
    newline->start = new_POINT();
    newline->end   = new_POINT();
    newline->r = 0.0; newline->g = 0.0; newline->b = 0.0;
    newline->width = 1.0;
    newline->next  = NULL;

    if (newline->start == NULL || newline->end == NULL) {
      free_LINE(newline);
      newline = NULL;
    }
  }
  return (newline);
}

//================================================================
// 新しい図形構造体を作り, 前準備をしておく
struct FIGURE *new_FIGURE (void){
  struct FIGURE *newfigure = NULL;
  
  newfigure = (struct FIGURE *)calloc(1, sizeof(struct FIGURE));
  if (newfigure != NULL) {
    newfigure->first_line = NULL;
    newfigure->next       = NULL;
  }
  return (newfigure);
}  

//================================================================
// 新しいパラメータ構造体を作り, 前準備をしておく
struct PARAMETER *new_PARAMETER (void){
  struct PARAMETER *newparameter = NULL;

  newparameter = (struct PARAMETER *)calloc(1, sizeof(struct PARAMETER));
  if (newparameter != NULL) {
    newparameter->matrix = NULL;
    newparameter->next   = NULL;
  }
  return (newparameter);
}

//================================================================
// 線分構造体のメモリを解放
void free_LINE (struct LINE *line) {
  if (line == NULL) return;
  
  free (line->start);
  free (line->end);
  free (line);
}

//================================================================
// 図形データ１つ分のメモリを解放
void free_FIGURE (struct FIGURE *figure) {
  struct LINE *tmp_line_pointer;

  if (figure == NULL) return;
  while (figure->first_line != NULL) {
    tmp_line_pointer = figure->first_line->next;
    free_LINE(figure->first_line);
    figure->first_line = tmp_line_pointer;
  }
  free(figure);
}

//================================================================
// リスト中の全ての図形データのメモリを解放
void free_all_FIGUREs (struct FIGURE *figure) {
  struct FIGURE *tmp_figure_pointer;

  while (figure != NULL) {
    tmp_figure_pointer = figure->next;
    free(figure);
    figure = tmp_figure_pointer;
  }
}

//================================================================
// パラメータ１つ分のメモリを解放
void free_PARAMETER(struct PARAMETER *parameter){
  if (parameter ==NULL) return;

  free(parameter->matrix);
  free(parameter);
}

//================================================================
// リスト中の全てのパラメータのメモリを解放
void free_all_PARAMETER(struct PARAMETER *parameter){
  struct PARAMETER *tmp_parameter_pointer;

  while (parameter != NULL) {
    tmp_parameter_pointer = parameter->next;
    free_PARAMETER(parameter);
    parameter = tmp_parameter_pointer;
  }
}

//================================================================
// 線分データ１つ分をプリントする
// デバッグ用の関数
// (デバッグ用の関数はうまく使うと強力な助っ人となるので, 敢えて入れてある)
// (デバッグ用の関数は、プログラムが完成したらどこにも使わない)
void print_LINE (struct LINE *line) {
  if (line == NULL)
    fprintf(stdout, "struct LINE : NULL pointer. No information.\n");
  else {
    if (line->start == NULL)
      fprintf(stdout, "( NULL )<->");
    else
      fprintf(stdout, "(%.3f %.3f %.3f)<->",
	      line->start->x, line->start->y, line->start->z);
    if (line->end == NULL)
      fprintf(stdout, "( NULL ) @ ");
    else
      fprintf(stdout, 
	      "(%.3f %.3f %.3f) @ ",
	      line->end->x, line->end->y, line->end->z);
    fprintf(stdout, 
	    "(%.2f %.2f %.2f) %.2f\n", 
	    line->r, line->g, line->b, line->width);
  }
}

//================================================================
// 線分データリストをプリントする
// デバッグ用の関数
void print_all_LINEs (struct LINE *line) {
  int n = 0;

  fprintf(stdout, "\n-*-*-*-*-*-*- LINES [start]     -*-*-*-*-*-*-\n");
  while (line != NULL) {
    fprintf(stdout, "=====LINE %d ====\n", n++); 
    print_LINE(line);
    line = line->next;
  } 
  fprintf(stdout,   "-*-*-*-*-*-*- LINES [%3d lines] -*-*-*-*-*-*-\n",n);
}

//================================================================
// 図形１つ分をプリントする
// デバッグ用の関数
void print_FIGURE (struct FIGURE *figure) {
  if (figure == NULL) return;
  fprintf(stdout, "----FIGURE----\n"); 
  print_all_LINEs (figure->first_line);
  fprintf(stdout, "next: %s\n", (figure->next == NULL) ? "no" : "yes");
  fprintf(stdout, "--------------\n"); 
}

//================================================================
// 図形データリストをプリントする
// デバッグ用の関数
void print_all_FIGURE (struct FIGURE *figure) {
  int n = 0;

  fprintf(stdout, "\n-*-*-*-*-*-*- FIGURES [start]       -*-*-*-*-*-*-\n");
  while (figure != NULL){
    fprintf(stdout, "=====FIGURE %d ====\n", n++); 
    print_FIGURE(figure);
    figure = figure->next;
  }
  fprintf(stdout,   "-*-*-*-*-*-*- FIGURES [%3d figures] -*-*-*-*-*-*-\n",n);
}

//================================================================
// パラメータ１つ分をプリントする
// デバッグ用の関数
void print_PARAMETER (struct PARAMETER *parameter) {
  if (parameter == NULL) return;
  fprintf
    (stderr, 
     "PARAMETER: T(%.1f %.1f %.1f) S(%.3f %.3f %.3f) R(%.3f %.3f %.3f)\n",
     parameter->dx, parameter->dy, parameter->dz,
     parameter->sx, parameter->sy, parameter->sz,
     parameter->thetax, parameter->thetay, parameter->thetaz);
}

//================================================================
// 行列をプリントする
// デバッグ用の関数
void print_matrix (mat4x4 matrix) {
  int i, j;

  fprintf(stderr, "matrix[4][4]={");
  for (i = 0; i < 4; i++) {
    fprintf(stderr, "{ ");
    for (j = 0; j < 4; j++) {
      fprintf(stderr, "%lf ", matrix[i][j]);
    }
    fprintf(stderr, "}");
  }
  fprintf(stderr, "}\n");
}


//-----------------------------------------------------
// データファイルからの読み込み
//-----------------------------------------------------

//================================================================
// LINE構造体の生成, 読み込み
// 線分データをコマンドラインから読み込む
void load_LINE (char *command) {
  struct LINE *newline = NULL;
  int number_of_element = 0;
  char tag[CMDSTRLEN];

  newline = new_LINE();
  if (newline == NULL) {
    fprintf(stderr, "Memory allocation failed at load_LINE()\n");
    return;
  }


  // ここで値を代入している！
  number_of_element = 
    sscanf(command, "%128s  %f %f %f  %f %f %f  %f %f %f  %f", 
	   tag,
	   &newline->start->x, &newline->start->y, &newline->start->z,
	   &newline->end  ->x, &newline->end  ->y, &newline->end  ->z,
	   &newline->r, &newline->g, &newline->b, 
	   &newline->width);
  if (number_of_element != 11) {
    fprintf(stdout, "load_LINE: format error (%d elements found)\n", 
	    number_of_element);
    return;
  }


  if (Lines_on_reading == NULL) {
    Lines_on_reading = newline;
  } else {
    Line_at_end->next = newline;
  }
  Line_at_end = newline;
}


//================================================================
// FIGURE構造体の生成, 読み込み
// これまで読み込まれているLINEの線形リストから図形を設定する
void attach_new_FIGURE (void) {
  struct FIGURE *newfigure;

  newfigure = new_FIGURE();
  if (newfigure == NULL) {
    fprintf(stderr, "Memory allocation failed at attach_new_FIGURE()\n");
    return;
  }
  newfigure->first_line = Lines_on_reading;


  if (Figures == NULL) {
    Figures = newfigure;
  } else {
    Figure_at_end->next = newfigure;
  }
  Figure_at_end = newfigure;

  // LINEの読み込み分は全てFIGURE構造体に移されたので、再び初期化 
  Lines_on_reading = NULL;
  Line_at_end      = NULL;
}


//-----------------------------------------------------
// 描画関数
//-----------------------------------------------------

//================================================================
// 線分を引く関数 （幾何変換なし）
void draw_LINE (struct LINE *line) {
  if (line == NULL) return;
  if (line->start == NULL || line ->end == NULL) return;

  glLineWidth (line->width);
  glColor3f (line->r, line->g, line->b);  

  glBegin (GL_LINES);
  glVertex2f (line->start->x, line->start->y);
  glVertex2f (line->end->x, line->end->y);
  glEnd ();
}

//================================================================
// 図形(線分リスト)を描画（幾何変換なし）
void draw_FIGURE (struct FIGURE *figure) {
  struct LINE *line = NULL;

  if (figure == NULL) return;
  line = figure->first_line;
  // 線分を順番にたどりながら描画
  // ここの部分を自分で考えよ
  //  while (条件) {
  //  処理＋描画
  //  }
}

//================================================================
// 図形を全て描く （幾何変換なし）
// デバッグ用の関数
void draw_all_FIGURE (struct FIGURE *figure) {
  while (figure != NULL) {
    draw_FIGURE (figure);
    figure = figure->next;
  }
}

//================================================================
// 図形を一気に描いていく（幾何変換なし）
void draw_FIGURE_continuously (struct FIGURE *figure) {
  while (figure != NULL) {
    glClear (GL_COLOR_BUFFER_BIT);
    draw_FIGURE (figure);
    glutSwapBuffers();
    glFlush();
    figure = figure->next;

    // [msec](単位) × [micro sec -> msec];
    usleep(300 * 1000);
  }
}

//================================================================
// コマンドを読み込んでデータを設定する関数
void read_commandfile (FILE *commandFile) {
  char command_line[CMDSTRLEN], command[CMDSTRLEN];

  // 一行分ファイルから読み込む (最大CMDSTRLEN文字)
  while (fgets(command_line, CMDSTRLEN, commandFile) != NULL) {
	 
    // 最初の文字列を取ってくる
    sscanf(command_line, "%128s", command);
    fprintf(stderr, "Command \"%s\":", command);

    // コマンドの一文字目で, どのコマンドかを識別
    switch (command[0]) {
      // コメント: 読みとばして, 内容をプリントする
    case '#':
      fprintf(stderr, " %s", command_line);
      break;

      // データの読み込み
    case 'L':  // 線分データを読み込む
      // (適当な関数を選んで実行させよ．既に用意されているもので良い．)
      break;
    case 'F':  // 図形データをセットアップする
      // (適当な関数を選んで実行させよ．既に用意されているもので良い．)
      break;
    default:  // 上記以外のコマンドが読み込まれたらエラーを出して終了
      fprintf(stderr, "command error : %s", command_line);
    }
    fprintf(stderr, "\n");
    glFlush();
  }
}


//-----------------------------------------------------
// 行列操作関数（主に幾何変換操作のときに必要）
//-----------------------------------------------------

//================================================================
// 行列のコピー copy <= original
void copy_matrix (mat4x4 copy, mat4x4 original) {
  int i, j;
  for (i = 0; i < 4; i++) {
    for (j = 0; j < 4; j++) {
      copy[i][j] = original[i][j];
    }
  }
}

//================================================================
// 変換行列の合成 (orimat <= mulmat * orimat)
//   orimatの変換行列にmulmatを前からかけて,
//   orimatの行列に入れる
void compose_matrix (mat4x4 orimat, mat4x4 mulmat) {
  mat4x4 work;
  int i, j, k;

  for (i = 0; i < 4; i++) {
    for (j = 0; j < 4; j++) {
      work[i][j] = 0;
    }
  }
  for (i = 0; i < 4; i++) {
    for (j = 0; j < 4; j++) {
      for (k = 0; k < 4; k++) {
        work[i][k] += mulmat[i][j] * orimat[j][k];
      }
    }
  }
  copy_matrix(orimat, work); // orimat <= work; return orimat
}

//-----------------------------------------------------
// 幾何変換操作のための関数
//-----------------------------------------------------

//================================================================
// 幾何変換：平行移動の変換行列を作る関数
void simple_translation (double dx, double dy, double dz, mat4x4 matrix) {
  matrix[0][0] = 1;
  matrix[0][1] = 0;
  matrix[0][2] = 0;
  matrix[0][3] = dx;
  matrix[1][0] = 0;
  matrix[1][1] = 1;
  matrix[1][2] = 0;
  matrix[1][3] = dy;
  matrix[2][0] = 0;
  matrix[2][1] = 0;
  matrix[2][2] = 1;
  matrix[2][3] = dz;
  matrix[3][0] = 0;
  matrix[3][1] = 0;
  matrix[3][2] = 0;
  matrix[3][3] = 1;
}

//================================================================
// 幾何変換：x軸回りの回転の変換行列を作る関数
// (自分で完成させましょう)
void simple_rotation_x (double thetax, mat4x4 matrix) {
  //
  // (自分で作成)
  //
}

// y軸回りの回転の変換行列を作る関数(自分で作る)
void simple_rotation_y (double thetay, mat4x4 matrix) {
  //
  // (自分で作成)
  //
}

//================================================================
// 幾何変換：z軸回りの回転の変換行列を作る関数
// (自分で完成させましょう)
void simple_rotation_z (double thetaz, mat4x4 matrix) {
  //
  // (自分で作成)
  //
}

//================================================================
// 幾何変換：スケール変換の行列を作る関数
// (自分で完成させましょう)
void simple_scaling (double sx, double sy, double sz, mat4x4 matrix) {
  //
  // (自分で作成)
  //
}

//================================================================
// 与えられている幾何変換パラメータから幾何変換のための行列を作る
void create_transform_matrix (struct PARAMETER *parameter){
  mat4x4 matrix0, matrix1;
  mat4x4 *matrix = NULL;

  if (parameter == NULL) return;

  // 幾何変換のための行列用のメモリをここで確保
  matrix = (mat4x4 *)calloc(1, sizeof(mat4x4));
  if (matrix == NULL) return;

  // 変換行列の初期化
  copy_matrix (matrix0, theIdentity);   // mat0 <= Ident

  // 各々の変換を作って, 掛け合わせていく
  // 回転： Z,Y,X軸まわりにこの順で回転
  simple_rotation_z (parameter->thetaz, matrix1);
  compose_matrix (matrix0, matrix1);  // mat0 <= mat1 * mat0 

  simple_rotation_y (parameter->thetay, matrix1);
  compose_matrix (matrix0, matrix1);  // mat0 <= mat1 * mat0 

  simple_rotation_x (parameter->thetax, matrix1);
  compose_matrix (matrix0, matrix1);  // mat0 <= mat1 * mat0 

  // スケール変換
  simple_scaling(parameter->sx, parameter->sy, parameter->sz, matrix1);
  compose_matrix (matrix0, matrix1);  // mat0 <= mat1 * mat0 

  // 並行移動
  simple_translation(parameter->dx, parameter->dy, parameter->dz, matrix1);
  compose_matrix (matrix0, matrix1);  // mat0 <= mat1 * mat0 

  copy_matrix (*matrix, matrix0); // *mat <= mat0
  parameter->matrix = matrix;
}

// 点列を変換行列によって変換する (行列とベクトルの掛け算) 
struct POINT *POINT_transform (mat4x4 mat, struct POINT *point) {
  struct POINT *new_point;

  new_point = new_POINT();

  new_point->x
    = mat[0][0] * point->x 
    + mat[0][1] * point->y 
    + mat[0][2] * point->z
    + mat[0][3] * point->w;
  //
  // (自分で埋めよ)
  //

  return (new_point);
}

//================================================================
// 幾何変換を加えて, 線分の実際の描画点を求める
struct LINE *LINE_transform (struct LINE *line, struct PARAMETER *parameter) {
  struct LINE *newline;

  newline = new_LINE();
  if (newline == NULL) return (newline);

  // 色, 太さの属性値をコピー
  newline->r = line->r;
  newline->g = line->g;
  newline->b = line->b;
  newline->width = line->width;

  // 実際に幾何変換を施す
  newline->start = POINT_transform (*(parameter->matrix), line->start);
  newline->end   = POINT_transform (*(parameter->matrix), line->end  );

  return (newline);
}

//================================================================
// 透視投影
// (自分で完成させましょう)
struct POINT *perspective_projection (struct POINT *point) {
  mat4x4 perspectivematrix;
  struct POINT  *new_point = NULL;

  if (theViewPlane.z == 0 || point->z == 0) {
    return NULL;
    fprintf(stderr, "zero division in perspective projection (%f), (%f, %f, %f)\n",
	    theViewPlane.z, point->x, point->y, point->z);
    return NULL;
  }

  // 行列を用いた透視投影
  copy_matrix(perspectivematrix, theIdentity); 
  //
  // (自分で作成)
  //



  return (new_point);
}

//================================================================
// 線を、透視変換して 正規化して表示
// (自分で完成させましょう)
void normalized_draw_LINE (struct LINE *line) {
  struct POINT *start_point, *end_point;
  glLineWidth (line->width);
  glColor3f (line->r, line->g, line->b);  

  // 始点, 終点を透視変換
  start_point = perspective_projection(line->start);
  end_point   = perspective_projection(line->end  );
  if (start_point == NULL || end_point == NULL) return;
 
  // 得られた2点間に線分を引く
  //  print_line(line);
  //  x = -0.5〜0.5, y = -0.5〜0.5 の範囲を 
  //  x = 0〜WINDOW_WIDTH, y = 0〜WINDOW_HEIGHTに変換する
  glBegin (GL_LINES);
  glVertex2f ((start_point->x + 0.5) * WINDOW_WIDTH, 
	      (start_point->y + 0.5) * WINDOW_HEIGHT);
  //
  // (自分で作成)
  //
  glEnd ();

  // 計算過程で使ったメモリを解放
  free(start_point);
  free(end_point);
}

//================================================================
// 図形(FIGURE)を、幾何変換を施して描画
// lineを順番にたどりながら描画する
void draw_FIGURE_transform(struct FIGURE *figure, struct PARAMETER *parameter){
  struct LINE *line;

  if (figure == NULL) return;

  // 幾何変換行列がセットされていない場合(ポインタの値がNULL)
  if (parameter->matrix == NULL) {
    // 幾何変換行列を計算して埋める
    create_transform_matrix(parameter);
  }
  line = figure->first_line;
  // 線分を順番にたどりながら描画 (week3の課題)
  // ここの部分を自分で考えよ
  //  while (条件) {
  //  処理＋描画
  //  }
}

//================================================================
// 図形を全て描く
// デバッグ用の関数
void draw_all_FIGURE_transform (struct FIGURE *figure, 
				struct PARAMETER *parameter) {
  while (figure != NULL) {
    draw_FIGURE_transform (figure, parameter);
    figure = figure->next;
  }
}

//-----------------------------------------------------
// アニメーションを行うための関数
//-----------------------------------------------------

//================================================================
// パラメータの補間
struct PARAMETER *PARAMETER_interpolation(struct PARAMETER *parameter1, 
					  struct PARAMETER *parameter2, 
					  double ratio) {
  struct PARAMETER *new_parameter;
  double r1;
  
  new_parameter = new_PARAMETER();
  if (new_parameter == NULL) return NULL;

  r1 = 1 - ratio;

  new_parameter->dx
    = parameter1->dx * r1 + parameter2->dx * ratio;
  new_parameter->dy
    = parameter1->dy * r1 + parameter2->dy * ratio;
  new_parameter->dz
    = parameter1->dz * r1 + parameter2->dz * ratio;
  new_parameter->sx
    = parameter1->sx * r1 + parameter2->sx * ratio;
  new_parameter->sy
    = parameter1->sy * r1 + parameter2->sy * ratio;
  new_parameter->sz
    = parameter1->sz * r1 + parameter2->sz * ratio;
  new_parameter->thetax
    = parameter1->thetax * r1 + parameter2->thetax * ratio;
  new_parameter->thetay
    = parameter1->thetay * r1 + parameter2->thetay * ratio;
  new_parameter->thetaz
    = parameter1->thetaz * r1 + parameter2->thetaz * ratio;
  new_parameter->matrix = NULL;
  return (new_parameter);
}

//================================================================
// パラメータをスムーズに変えながら描画する
// パラメータ固定 (week3専用)
void animation_test (struct FIGURE *figure) {
  int n;
  int times = 500;   // 位置を更新する回数(小さいほど速く移動する)
  struct PARAMETER *current_parameter;
  struct PARAMETER parameter1 // week3時のみ固定初期値として利用
    = {-0.5, -0.5, -2.0,  0.3, 0.3, 1.0,  0.0, 0.0,      M_PI, NULL, NULL};
  struct PARAMETER parameter2 // week3時のみ固定終了値として利用
    = { 0.0,  0.0, -1.0,  0.6, 0.6, 1.0,  0.0, 4 * M_PI,  0.0, NULL, NULL};

  // ここから times 回繰り返す (parameter1からparameter2へ)
  for (n = 0; n <= times; n++) {

    // パラメータを (times - n) : n に線形補間する
    current_parameter 
      = PARAMETER_interpolation(&parameter1, &parameter2, (double)n/times);

    glClear (GL_COLOR_BUFFER_BIT);
    // 変換しながら描画
    draw_all_FIGURE_transform(figure, current_parameter);
    glFlush ();
    free_PARAMETER(current_parameter);

    // usleep (100 * 1000);
    glutSwapBuffers();
  }
}

//-----------------------------------------------------
// ここからはウィンドウに種々のイベントが発生したときに
// どう動くかを規定する関数群
//-----------------------------------------------------



//================================================================
// ウィンドウに再描画要求が来たときの対策用関数 
// glutDisplayFunc()にて登録される              
// 背景色で塗り直すだけ

void kj_display (void) {
  // 画面を背景色で塗り直す
  glClear (GL_COLOR_BUFFER_BIT);
  glutSwapBuffers();
  glFlush ();
}



//================================================================
// ウィンドウサイズの変更が生じたときの対策用関数 
// glutReshapeFunc()にて登録される                
// ウィンドウ生成時にも１回呼び出されることに注意 
void kj_reshape (int w, int h) {
  // ウィンドウのどこまでが見えているのか？
  glViewport (0, 0, (GLsizei) w, (GLsizei) h);

  // 次の３行で投影行列を規定
  // とは言っても、今回はウィンドウ内のピクセル座標がそのまま利用される
  // ＝直交投影[Orthogonal Projection]
  glMatrixMode (GL_PROJECTION); // 今から投影行列を設定すると宣言
  glLoadIdentity ();            // 単位行列をセット

  // 直交投影
  gluOrtho2D (0.0, (GLdouble) w, 0.0, (GLdouble) h);

  // ユーザへの情報提示
  fprintf(stderr, "Reshaped window size is (%4d, %4d)\n", w, h);
}


//================================================================
// キーが何か押されたときの対策用関数 
// glutKeyboardFunc()にて登録される   
// 引数 : key ... 入力文字 
// 引数 : x   ... 文字が押されたときのマウスカーソルのＸ位置 
// 引数 : key ... 文字が押されたときのマウスカーソルのＹ位置 
//
//   "q"か"Q"ならば抜ける．
//
void kj_keyfunc (unsigned char key, int x, int y) {
  if (key == 'q' || key == 'Q') 
    exit (0);
}

//================================================================
// マウスボタンがクリックされたときの対策用関数 
// glutMouseFunc()にて登録される                
// マウスのボタンが押された場合・離された場合の両方で呼ばれることに注意 
// 引数 : button ... 左ボタン，中ボタン，右ボタンを区別するための数が入る
//                      左ボタン: GLUT_LEFT_BUTTON    (== 0)
//                      中ボタン: GLUT_MIDDLE_BUTTON  (== 1)
//                      右ボタン: GLUT_RIGHT_BUTTON   (== 2)
//                      (参考 man glutMouseFunc, less /usr/include/GL/glut.h)
// 引数 : status ... ボタンが押されたか，離されたかを区別するための数が入る 
//                      押した:  GLUT_DOWN (==0)
//                      離した:  GLUT_UP   (==1)
// 引数 : mouse_x, mouse_y ... マウスの座標が入る
void kj_mousefunc (int button, int status, int x, int y) {
  FILE *commandFile;

  // ボタンが離された場合のみ描画を始める
  if (status == GLUT_UP) {
    // ファイルのオープン
    if ((commandFile = fopen(DATA_FILENAME, "r")) != NULL) {
      // コマンドの実行(データの読み込み)
      read_commandfile(commandFile);
      // 描画の実行
      // とりあえず，このようなものを用意したが, 自分で自由に考えてみよ
      animation_test(Figures);
      // ファイルのクローズ／大域変数の初期化
      fclose(commandFile);
      free_all_FIGUREs(Figures);
      Figures       = NULL;
      Figure_at_end = NULL;
    }
  }
}

//-----------------------------------------------------
// ここからが本体
//-----------------------------------------------------

//================================================================
int main (int argc, char *argv[]) {

  // glutの初期化
  glutInit (&argc, argv);

  // ウィンドウを開く
  glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);	    // ダブルバッファ
  glutInitWindowSize (WINDOW_WIDTH, WINDOW_HEIGHT); // ウィンドウの大きさ
  glutInitWindowPosition (100, 100);		    // ウィンドウの位置
  glutCreateWindow (argv[0]);			    // ウィンドウの生成

  // 初期設定
  glClearColor (0.0, 0.0, 0.0, 0.0);  // ウィンドウ全体を書き直すときの色 
  glShadeModel (GL_FLAT);             // Flat Shading, 今回は関係ない     

  // イベントの処理 (詳しくはOpenGLの説明を良く読むこと)
  glutDisplayFunc  (kj_display);
  glutReshapeFunc  (kj_reshape);
  glutKeyboardFunc (kj_keyfunc);
  glutMouseFunc    (kj_mousefunc);
  glutMainLoop ();

  return 0;
}


