PPM形式にはテキスト形式とバイナリ形式があるが,こ こではテキスト形式のみ扱う(convert コマンドで作成されるPPMファイルはバ イナリ形式の場合があるが,pnmnorawコマンドで変換できる).実際の例で見 てみる.
P3 4 4 255 0 0 0 0 0 0 0 0 0 255 0 255 0 0 0 0 255 127 0 0 0 0 0 0 0 0 0 0 0 0 0 255 127 0 0 0 255 0 255 0 0 0 0 0 0 0 0 0上の部分をt.ppmというファイルで保存して,
gimp t.ppmでみると,幅4ドット,高さ4ドットの米粒のような画像が現れるがこれを zoom in していくと,
P3の部分がマジックナンバーという部分で,このファイルが他の画像ファイルで はなくPPM形式であることを示す(Windowsの多くのアプリケーションのように, 拡張子が .ppm でああることで判断しているわけではない).PPM形式では空白 (White Space, Spaceコードだけではなく改行,タブも含まれる)はいくついれ ても良いが,P3の前に入れてはいけない.マジックナンバーの後に 空白 があり,4 4 255で,画像の幅,高さ,色の階調数を順に指定する.階調の最大値は,一般には R(red), G(Green), B(Blue)それぞれ 0(暗い)から255(明るい)までの256階調 あれば人間に自然な画像を表せるといわれているので,ここでは 255 を指定 している.詳しくは, ラスタ画像の形式を参照すること.その後で,
0 0 0 0 0 0 0 0 0 255 0 255 0 0 0 0 255 127 0 0 0 0 0 0 0 0 0 0 0 0 0 255 127 0 0 0 255 0 255 0 0 0 0 0 0 0 0 0のように,4x4=16ピクセルの RGB値を順に指定している.ピクセルの指定の順 番は横書きの文章と同じように左上から右下へ横方向に走査していく.1ピク セル内の指定の順番は R G Bの順番になっている.上の例で現れる色は
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){ System.out.println(255); System.out.println(255); System.out.println(j*2); } } } }を ~/jousho04以下にGrad.java という名前で保存して,
javac Grad.javaとコンパイルして,
java Grad > grad.ppmとプログラムを実行し,出力を grad.ppm というファイルに保存すると,この ファイルは PPM形式(Portable PixMap形式)の画像ファイルになっている.
convert grad.ppm grad.pngを実行して,png形式にしたのが
Z0=0 Zn+1=Zn*Zn+Cとして, 複素数列{Zn}が定義できる.ここで,n->∞としたとき, |Zn|が発散しないようなCの集合をマンデルブロー集合という.
マンデルブロー集合は,集合の境界が複雑な形をしていて, 拡大をしても元の 図形と同じような複雑度を保っている.このような自己相似のことを総称して フラクタルと呼ぶが,マンデルブロー集合はフラクタルな図形の中ではもっと も有名である.
マンデルブロー集合は定義に無限回の繰り返しを含んでいるため, 計算機上 で正確に描くのは,困難である.そこで, 一般には
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,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の範囲で計算 // 整数(int)から実数(double)への変換は「(double)整数式」 // のようにおこなう. x= -1.7+2.4*((double)j/(double)width); y= -1.2+2.4*((double)i/(double)height); // 0 <= r <= 50の値が返る r=mandel(x,y); // r=50の時に黒(0,0,0), r=0の時にほぼ白(250,250,250)とする. System.out.println(250-r*5); System.out.println(250-r*5); System.out.println(250-r*5); } } } }このプログラムを Mandel.java というファイルに保存して,
javac Mandel.java java Mandel > mandel.ppmと実行すると,mandel.ppm というファイルが得られる.これを
open mandel.ppmで見ることもできるし,
convert mandel.ppm mandel.pngとして png 形式に変換して,WWWブラウザで見ることもできる. できた mandel.png は以下のようになる.
System.out.println(250-r*5); System.out.println(250-r*5); System.out.println(250-r*5);の行を,たとえば
System.out.println(((50-r)*71)%256); System.out.println(((50-r)*111)%256); System.out.println(((50-r)*97)%256);とすると(「%」は剰余(mod)を表す演算子, 256で割った余りは 0-255になる),
class Draw{ /** * * Math.sqrtを呼び出して平方根を計算できる */ static double distance(int x0,int y0, int x1, int y1){ int dx=x0-x1; int dy=y0-y1; return Math.sqrt(dx*dx+dy*dy); } /** * circleX 円の中心のX座標 * circleY 円の中心のY座標 * circleR 円の半径 */ static boolean inCircle(int x, int y, int circleX, int circleY, int circleR){ if(distance(x,y,circleX,circleY) <= circleR) { return true; } else { return false; } } /** * rectangleX 矩形の左上X座標 * rectangleY 矩形の左上Y座標 * rectangleW 矩形の幅 * rectangleH 矩形の高さ */ static boolean inRectangle(int x, int y, int rectangleX, int rectangleY, int rectangleW, int 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を呼び出して小さい方の値を取る r=Math.min(255,r); g=Math.min(255,g); b=Math.min(255,b); } { // (200,50)を中心にした緑のフレア効果 double len=distance(x,y,200,50); g+=200*25/(len+20); g=Math.min(255,g); } System.out.println(r); System.out.println(g); System.out.println(b); } } } }このプログラムを実行した結果は,
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++){ // Math.sinを呼び出してsinを計算する double sinY=-Math.sin(x*0.03)*100.0+128.0; // Math.absを呼び出して絶対値をとる // (int)で整数型に変換する int dl=(int)Math.abs(y-sinY); int r,g,b; r=200*25/(dl+20); g=200*25/(dl+20); b=200*25/(dl+20); r=Math.min(255,r); g=Math.min(255,g); b=Math.min(255,b); System.out.println(r); System.out.println(g); System.out.println(b); } } } }このプログラムを実行した結果は,
Forbidden You don't have permission to access /~ktanaka/jousho04/report630/index.html on this server.とうメッセージが出てアクセ スできないはずである.11:10以降にも同様のエラーが出る時は,Shiftキーを 押しながら,再読み込み(Reload)を押してみること.
なお課題の締切りは7/21(水)の21:00である.