7/12 Java プログラミング第3回


最近の計算機関連のニュースから


前回の課題


はいぱーワークブックへの補足

今回の課題の補助となるように,いくつかの補足をおこなう.

アニメーション GIF ファイルの作成

いろいろなWWWページを見ていると,画像が変化するページを見かけること がある.このような動く画像は以下のような技術で作成されている.
  1. アニメーション GIF
  2. Javaアプレット
  3. JavaScript
  4. ShockWaveプラグイン
    教育用計算機システムではサポートしていない
  5. 動画プラグイン
    教育用計算機システムではサポートしていない
ここでは1の方法を紹介する.画像形式の1つである GIF 形式には,複数の画 像をパラパラマンガのように順に表示させるという形式が用意されている.こ こでは,マンデルブロー集合を拡大して表示していくアニメーションを作成し てみる.

まず,アニメーション GIF はパラパラマンガなので,コマ数だけ静止画ファ イルを用意する必要がある.前回のマンデルブロー集合を求めるプログラムの パラメータを変更してはコンパイル,実行を繰り返しても良いが,繰り返し部 分は人間がやるよりもコンピュータにやらせた方が良い.

この講義の範囲を超えるが,Java言語からファイルに出力するプログラムを 紹介する.ファイルに出力するには,まず,

      java.io.PrintWriter ps=new java.io.PrintWriter(new java.io.FileWriter("mandel"+k+".ppm"));
のようにして,ファイル名("mandel"+k+".ppm")からFileWriterを作り,それ から PrintWriterを作る.
「作る」というのは「オブジェクトの生成」にあたり,演算子 new で実現さ れるが,この講義の範囲を超えるので詳しい説明は省略する.興味のある人は, あたりを参照すること.

これまで,

 System.out.println( 出力したい文字列 );
としてきたのを,
 ps.println( 出力したい文字列 );
とするとファイルへの出力に変る.
これは,変数 ps に入っているjava.io.PrintWriter型のオブジェクトのメソッド println を呼び出しているという動作だが,ここでは詳しい説明は省略する.

出力を終えたら,ファイルを閉じる必要がある.

ps.close();
を実行する.

プログラム

class Mandel{
  // 実数を引数(パラメタ)に持つメソッド mandel の定義
  static int mandel(double x, double y){
    double zr=0, zi=0,new_zr;
    int i;
    for(i=0;i<50;i=i+1){
      if(zr*zr+zi*zi>4.0) { // |Zi|>2.0 の時は発散と判断
        return i;
      }
       // 複素数の計算
       // (zr+zi*i)*(zr+zi*i)+x+y*i=(zr*zr-zi*zi+x)+(2*zr*zi+y)*i
      new_zr=zr*zr-zi*zi+x;
      zi=2*zr*zi+y;
      zr=new_zr;
    }
    return 50;
  }
    // 入出力関係でエラーが発生する可能性があるので,
    // throws java.io.IOException をつける.
  public static void main(String[] args) throws java.io.IOException{
    int width=256, height=256;
    int k;
    for(k=0;k<10;k++){
        // ファイルに出力する時は,ファイル名("mandel"+k+".ppm")から
        //  FileWriterを作り,それから PrintWriterを作る
      java.io.PrintWriter ps=new java.io.PrintWriter(new java.io.FileWriter("mandel"+k+".ppm"));
        // マンデルブロー集合を計算する範囲のスケール
        // 3e-0.8k なので,kが0の時は3
      double scale=3.0*Math.exp(-(double)k*0.8);
        // ファイルへの出力
      ps.println("P3 "+width+" "+height+" 255");
      int i,j,r;
      double x, y;
      for(i=0;i<height;i=i+1){
	for(j=0;j<width;j=j+1){
	  // -1.7 <= x < 0.7, -1.2 <= y < 1.2の範囲で計算
	  // 実数(double)から整数(int)への変換は「(double)整数式」
	  // のようにおこなう.
	  x= -1.2525 -scale*0.5+scale*((double)j/(double)width);
	  y= -0.3425 -scale*0.5+scale*((double)i/(double)height);
	  // 0 <= r <= 50の値が返る
	  r=mandel(x,y);
	  // r=50の時に青(0,0,255), r=0の時にほぼ白(250,250,250)とする.
	    // ファイルへの出力
	  ps.println((250-r*5)+" "+(250-r*5)+" 250");
	}
      }
        // ファイルストリームを閉じる
      ps.close();
    }
  }
}
これを実行すると,mandel0.ppm, mandel1.ppm, ..., mandel9.ppmというファ イルができる.
convert -delay 40 mandel[0-9].ppm mandel-animation.gif
のように実行すると,0.4秒間隔(-delay のパラメータは 1/100秒単位で指定)で, mandel[0-9].gif を次々に表示していく.以下のような画像が現れる.
mandel-animation.gif
このアニメーションはロードされた直後に一度動くようにしてあるので, Reload ボタンを押すと動き出す.

注意

あまりコマ数を多く作成しすぎると,ディスク容量の制限をオー バしてしまうおそれがあります(/tmp等で作業すれば,この制限はクリアでき ますが,作業を終えた後ファイルを消すように注意しましょう).また,実行 時間もかかるので注意が必要です.

簡単な Computer Graphics(CG)

Java 言語でCG画像を作成するために,Java2D, Java3Dという API (アプリケー ションインタフェース)が用意されているが,ここでは簡単なレイトレーシング プログラムを書いてみる.

レイトレーシング法(ray tracing algorithm)は,計算機内部の3次元世界か ら2次元画像を生成する方法(レンダリング法)の一つで,視線方向に光線を追 跡していく方法( 慶應SFC千代倉研究室によるCG教科書などを参照)である.

レイトレーシング法は本来,鏡面反射や屈折などを含む画像の生成に役に立ち, また複数の物体の描画で隠面消去が自然に定義できるという特徴があるが,こ こでは,その特徴は生かさずに,

プログラムを扱う.
class RayTrace{
  public static void main(String[] args) throws java.io.IOException{
    // 背景色
    double backr=0.2, backg=0.2, backb=0.6;
    // b=(10,10,100)に大きさ30のボールがある.
    // ボールの色(黄色)
    double ballred=1.0, ballgreen=1.0, ballblue=0.0;
    // ボールの座標(10,10,100)と半径(30)
    double bx=10.0, by=10.0, bz=100.0, br=30.0;
    // 入力光の方向は(1,1,1)の方向
    // 平方根を取る操作は Math.sqrt 
    // 単位ベクトル(長さ1)に正規化する
    double lx=Math.sqrt(1.0/3.0), ly=Math.sqrt(1.0/3.0), lz=Math.sqrt(1.0/3.0);
    // 画面の幅, 高さ
    int width=128, height=128;
    System.out.println("P3 "+width+" "+height+" 255");
    int i,j,r;
    double vx, vy, vz, vlen;
    for(i=0;i<height;i=i+1){
      for(j=0;j<width;j=j+1){
	// 視線ベクトル.1.0離れた幅1.0, 高さ 1.0のスクリーンに投影
	vz=1.0;
	vx=(double)(i-height/2)/(double)height;
	vy=(double)(j-width/2)/(double)width;
	// (vx, vy, vz) を長さ1のベクトルにする.
	vlen=Math.sqrt(vx*vx+vy*vy+vz*vz);
	vx=vx/vlen; vz=vz/vlen; vz=vz/vlen;
	// b-v t と v が直交する点を求める.
	// (bx-t*vx)^2+(by-t*vy)^2+(bz-t*vz)^2+(bz-t*vz)^2=br^2
        // はtの2次方程式になる.
	// (vx^2+vy^2+vz^2)*t^2+(-2*bx*vx-2*by*vy-2*bz*vz)*t+
	//   bx^2+by^2+bz^2-br^2=0
	double a=vx*vx+vy*vy+vz*vz, b=-2*vx*bx-2*vy*by-2*vz*bz;
	double c=bx*bx+by*by+bz*bz-br*br;
	// 解の公式より t=(-b±√(b*b-4*a*c))/(2*a)
	double d=b*b-4*a*c;
	if(d<0.0){ // 衝突しない時は背景色を描く.
	  System.out.println((int)(backr*255.0)+" "+(int)(backg*255.0)+" "+(int)(backb*255.0));
	}
	else{ // 始点(0,0,0)がボール内にないという前提で小さい方のtに決める
	  double t= (-b-Math.sqrt(d))/(2*a);
	  // ボールの法線ベクトル(Normal Vector)を求める
	  double nx= vx*t-bx, ny= vy*t-by, nz= vz*t-bz;
	  // 法線ベクトルを単位ベクトルにする.
	  double nlen=Math.sqrt(nx*nx+ny*ny+nz*nz);
	  nx=nx/nlen; ny=ny/nlen; nz=nz/nlen;
	  // 法線ベクトルと視線ベクトルの costhetaを求める
	  // 単位ベクトル同士なのでベクトルの内積で良い.
	  double costheta1= -vx*nx-vy*ny-vz*nz;
	  // 法線ベクトルと入力光の costheta を求める
	  double costheta2= -lx*nx-ly*ny-lz*nz;
	  // 拡散反射成分だけを求める
	  // 2つの数の最小値を取るMath.minの呼び出し
	  double red=Math.min(1.0,ballred*costheta1*costheta2);
	  double green=Math.min(1.0,ballgreen*costheta1*costheta2);
	  double blue=Math.min(1.0,ballblue*costheta1*costheta2);
	  System.out.println((int)(red*255.0)+" "+(int)(green*255.0)+" "+(int)(blue*255.0));
	}
      }
    }
  }
}

今日の課題

~/jousho00/のディレクトリの下に report712.htmlというHTML文書を作成した後で,
/home/ktanaka/bin/report712 1
を実行しなさい.~/jousho00/report712.htmlは,以下のものを含んでいて, 他人から読める必要がある. 通常の課題の3回分程度の配点なので,時間をかけてじっくりと取り組むように.

締切は,8/4(金)の21:00.8月中は採点のため他人から見えるようにしておくこと.


ktanaka at ecc.u-tokyo.ac.jp