7/2 プログラミング(3)


前回までの補足


6/16の課題に関して


今回の目標


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


PPMファイルを出力するプログラム(グラデーション) の書き換え

class Grad{
  public static void main(String[] args){
    int width=128, height=128;
    System.out.println("P3");
    System.out.println(width); // 画像の幅
    System.out.println(height); // 画像の高さ
    System.out.println(255);
    int i,j;
    for(i=0;i<height;i=i+1){ // 縦方向の繰り返し(上から下へ)
      for(j=0;j<width;j=j+1){ // 横方向の繰り返し(左から右へ)
        int r,g,b;

        r=255; // Rが255(最も明るい)
        g=255; // Gが255(最も明るい)
        b=j*2; // Bがjの値に応じて0-254 で変化

        r=Math.min(255,Math.max(0,r)); // R の範囲を0から255までに
        g=Math.min(255,Math.max(0,g)); // G の範囲を0から255までに
        b=Math.min(255,Math.max(0,b)); // B の範囲を0から255までに
        System.out.println(r); // R
        System.out.println(g); // G
        System.out.println(b); // B
      }
    }
  }
}
をベースにプログラムを書き換えてみることを試みる.

まずは,

    int width=128, height=128;
の部分を書き換えると作成する画像の縦横のピクセル数を変更することができる. これをたとえば,
    int width=400, height=200;
に変更すると,幅が400, 高さが200に変更となるはずだが,実際に実行して 作成されたファイルを変換してみると,グラデーションが途中で飽和してしまう. これは,widthが400になると,
        b=j*2; // Bがjの値に応じて0-254 
の行で出力するB(青)の階調の範囲が0から798となり,0-255という制限を 超えてしまうからである.これを
        b=j*256/width;
と直すと,幅と高さを変更できる.

青から赤へのグラデーションを縦方向におこなう場合には,

        r=255; // Rが255(最も明るい)
        g=255; // Gが255(最も明るい)
        b=j*2; // Bがjの値に応じて0-254 で変化
        r=i*256/height; // R
        g=0; // G
        b=255-r; // B
のようにする.ここで,
        r=i*256/height; // R
        r=i/height*256; // R
のようにしても,同じになりそうだが,(i/height)は整数の除算で, (0<=i <=height-1)の範囲では常に結果が0になってしまう.

また,青から緑に斜めにグラデーションさせるには,同じ部分を

        r=0; // R
        g=(i+j)*256/(width+height); // G
        b=255-g; // B
のようにする.

マンデルブロー集合を表示するプログラムを書き換え

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;
  }
  public static void main(String[] args){
    int width=256, height=256;
    System.out.println("P3");
    System.out.println(width);
    System.out.println(height);
    System.out.println(255);
    int i,j,count;
    double x, y;
    for(i=0;i<height;i=i+1){
      for(j=0;j<width;j=j+1){
        int r,g,b; 

           // -1.7 <= x < 0.7, -1.2 <= y < 1.2の範囲で計算
           // 整数(int)から実数(double)への変換は「(double)整数式」
           // のようにおこなう.
        x= -1.7+2.4*((double)j/(double)width);
        y= -1.2+2.4*((double)i/(double)height);
           // 0 <= count <= 50の値が返る
        count=mandel(x,y);
           // count=50の時に黒(0,0,0), count=0の時にほぼ白(250,250,250)とする.
        r=250-count*5; //
        g=250-count*5; //
        b=250-count*5; //

        r=Math.min(255,Math.max(0,r)); // R の範囲を0から255までに
        g=Math.min(255,Math.max(0,g)); // G の範囲を0から255までに
        b=Math.min(255,Math.max(0,b)); // B の範囲を0から255までに
        System.out.println(r); // R
        System.out.println(g); // G
        System.out.println(b); // B
      }
    }
  }
}
を書き換えてみる.まず,
    int width=256, height=256;
を書き換えると画像の大きさを変更できるが,widthとheightの比率を1:1以外にする 場合は,
        x= -1.7+2.4*((double)j/(double)width);
        y= -1.2+2.4*((double)i/(double)height);
の行の拡大率(2.4)をxとyとで変更した方が良い.

元のプログラムでは,-1.7 <= x < 0.7, -1.2 <= y < 1.2の範囲で計算していたが, たとえば,-0.12 <= x <=-0.02, -0.7 <= y <= -0.6 の 範囲を指定するには,

        x= -0.12+0.1*((double)j/(double)width);
        y= -0.7+0.1*((double)i/(double)height);
のようにする.

色を変更するには,

        r=250-count*5; //
        g=250-count*5; //
        b=250-count*5; //
の行を変更する.たとえば,白の背景に青で出したい場合は,
        r=count*255/50; //R
        g=count*255/50; //G
        b=255; // B
のようにすれば良い.

円や長方形を描くプログラム

class Draw{
    /**
     *
     * Math.sqrtを呼び出して平方根を計算できる
     */
    static double distance(double x0,double y0, double x1, double y1){
	double dx=x0-x1;
	double dy=y0-y1;
	return Math.sqrt(dx*dx+dy*dy);
    }
    /**
     * circleX 円の中心のX座標 
     * circleY 円の中心のY座標 
     * circleR 円の半径
     */
    static boolean inCircle(double x, double y, double circleX, double circleY, double circleR){
	if(distance(x,y,circleX,circleY) <= circleR) {
	    return true;
	}
	else {
	    return false;
	}
    }
    /**
     * rectangleX 矩形の左上X座標
     * rectangleY 矩形の左上Y座標
     * rectangleW 矩形の幅
     * rectangleH 矩形の高さ
     */
    static boolean inRectangle(double x, double y, 
			    double rectangleX, double rectangleY, 
			    double rectangleW, double rectangleH){
	if(rectangleX <= x &&
	   x <= rectangleX+rectangleW &&
	   rectangleY <= y &&
	   y <= rectangleY+rectangleH){
	    return true;
	}
	else {
	    return false;
	}
    }
    public static void main(String args[]){
	int width=256;
	int height=256;
	int x,y;
	System.out.println("P3");
        System.out.println(width);
        System.out.println(height);
        System.out.println(255);
	for(y=0;y < height;y++){
	    for(x=0;x < width;x++){
		/**
		 * 赤 r, 緑 g, 青 b をすべて0にすると黒
		 */
		int r=0, g=0, b=0;

		if(inRectangle(x,y,0,50,150,100)){
		    // 白みがかった暗い黄色
		    r=200; g=200; b=100;
		}
		if(inCircle(x,y,200,150,50)){
		    // 暗いマゼンダに
		    r=200; g=0; b=200;
		}
		if(inCircle(x,y,150,200,70)){
		    // 白を重ねる
		    r=r+100;
		    g=g+100;
		    b=b+100;
		    // 255までしか色を表現できないので,
		    // 255と小さい方の値を取ることにより,切り捨てる
		    // Math.minを呼び出して小さい方の値を取る
		}
		{
		    // (200,50)を中心にした緑のフレア効果
		    double len=distance(x,y,200,50);
		    g+=200*25/(len+20);
		}

                r=Math.min(255,Math.max(0,r)); // R の範囲を0から255までに
                g=Math.min(255,Math.max(0,g)); // G の範囲を0から255までに
                b=Math.min(255,Math.max(0,b)); // B の範囲を0から255までに
		System.out.println(r);
                System.out.println(g);
                System.out.println(b);
	    }
	}
    }
}
このプログラムを実行した結果は,
となる.

sin カーブを描くプログラム

class Sin{
    public static void main(String args[]){
	int width=256;
	int height=256;
	int x,y;
	System.out.println("P3");
        System.out.println(width);
        System.out.println(height);
        System.out.println(255);
	for(y=0;y < height;y++){
	    for(x=0;x < width;x++){
		int r,g,b;

		    // Math.sinを呼び出してsinを計算する
		double sinY=-Math.sin(x*0.03)*100.0+128.0;
		    // Math.absを呼び出して絶対値をとる
		    // (int)で整数型に変換する
		int dl=(int)Math.abs(y-sinY);
		r=200*25/(dl+20);
		g=200*25/(dl+20);
		b=200*25/(dl+20);

                r=Math.min(255,Math.max(0,r)); // R の範囲を0から255までに
                g=Math.min(255,Math.max(0,g)); // G の範囲を0から255までに
                b=Math.min(255,Math.max(0,b)); // B の範囲を0から255までに
		System.out.println(r);
                System.out.println(g);
                System.out.println(b);
	    }
	}
    }
}
このプログラムを実行した結果は,
となる.