数学
3D
pov-ray

はじめに

これは POV-Rayによる数学お絵かき入門 Advent Calendar 2017 の6日目の記事です.

POV-Rayでは幾つかの制御文が用意されていますが, その中でも今日は

  • while
  • if
  • macro

について書きます.

公式ドキュメントでは以下が近いです.
http://www.povray.org/documentation/3.7.0/r3_3.html#r3_3_2_6
http://www.povray.org/documentation/3.7.0/r3_3.html#r3_3_2_8

while

POV-Rayで繰り返しの処理を書くには次のようにします.

#while(条件)
    処理
#end

POV-Rayにはfor文が用意されていないため, 球面を複数並べる処理は次のように書きます.

#declare i=0;
#while(i<5)
    sphere{<i,0,0>,0.2 pigment{rgb<i/4,1-i/4,0>}}
    #declare i=i+1;
#end

この例では色の指定にもiを入れ込めて球面の色が変わるようになっています.https://i.imgur.com/j3sTDBZ.png

if

条件分岐を記述するためには次のように書きます.

#if(条件)
    処理1
#else
    処理2
#end

第四回に書いたように, POV-Rayでは真偽値が1と0で表現されます.
ifwhileの条件で判断される際には, 0に等しいか否かによって判断されます.

macro

マクロを書くには次のように書きます.

#macro(第1引数,...,第n引数)
    処理
#end

つまらない例ですが, 鉛直方向を向いた円柱のマクロは次のように書けます.

#macro(r,l)
    cylinder{<0,0,0>,<0,l,0>,r}
#end

第四回でも少し触れましたが, macro内で変数を宣言する際にはローカル変数にはlocal, グローバル変数にはdeclareを使います.

基本的にはmacroは関数と考えて差し支えありませんが, 厳密には異なります.
これに関しては連載後半で改めて記述する予定です.

多面体

立方体

whilemacroを使う
一番簡単な立方体から書きなおします.

対称性を考えれば, 頂点は次のように書けます.

#declare i=-1;
#while(i<2)
    #declare j=-1;
    #while(j<2)
        #declare k=-1;
        #while(k<2)
            sphere{<i,j,k>,0.05 pigment{rgb<1,0,1>}}
            #declare k=k+2;
        #end
        #declare j=j+2;
    #end
    #declare i=i+2;
#end

cylinderを付け加えて辺を描きます.

#declare i=-1;
#while(i<2)
    #declare j=-1;
    #while(j<2)
        #declare k=-1;
        #while(k<2)
            sphere{<i,j,k>,0.05 pigment{rgb<1,0,1>}}
            #declare k=k+2;
        #end
        cylinder{<i,j,1>,<i,j,-1>,0.05 pigment{rgb<1,0,1>}}
        #declare j=j+2;
    #end
    #declare i=i+2;
#end

ここで次のマクロRotateを定義します.

#macro Rotate(w,i)
    #local j=i;
    #local wj=w;
    #while(j)
        #local wj=<wj.z,wj.x,wj.y>;
        #local j=j-1;
    #end
    wj
#end

このマクロによって, ベクトル$(1,1,1)$を回転軸として引数のベクトルが$120^\circ$回転します.

上と合わせれば次のように書けます.

#declare i=-1;
#while(i<2)
    #declare j=-1;
    #while(j<2)
        #declare k=-1;
        #while(k<2)
            sphere{<i,j,k>,0.05 pigment{rgb<1,0,1>}}
            #declare k=k+2;
        #end
        #declare l=0;
        #while (l<3)
            cylinder{Rotate(<i,j,1>,l),Rotate(<i,j,-1>,l),0.05 pigment{rgb<1,0,1>}}
            #declare l=l+1;
        #end
        #declare j=j+2;
    #end
    #declare i=i+2;
#end

正八面体

同様に次のように描けます.

#declare i=-1;
#while(i<2)
    #declare l=0;
    #while(l<3)
        sphere{Rotate(<i,0,0>,l),0.05 pigment{rgb<0,1,1>}}
        #declare j=-1;
        #while (j<2)
            cylinder{Rotate(<i,0,0>,l),Rotate(<0,j,0>,l),0.05 pigment{rgb<0,1,1>}}
            #declare j=j+2;
        #end
        #declare l=l+1;
    #end
    #declare i=i+2;
#end

正四面体

全列挙の方が早そうですが, 折角なので対称性を利用して描いてみましょう.
これまでと違って条件分岐ifも使います.
立方体の頂点を元に正四面体を2つ作ります.

#declare i=-1;
#while(i<2)
    #declare j=-1;
    #while(j<2)
        #declare k=-1;
        #while(k<2)
            sphere{<i,j,k>,0.05 pigment{rgb<1,1,0>}}
            #declare k=k+2;
        #end
        #declare l=0;
        #while (l<3)
            cylinder{Rotate(<i,j,1>,l),Rotate(<i,-j,-1>,l),0.05 pigment{rgb<1,0,1>}}
            #declare l=l+1;
        #end
        #declare j=j+2;
    #end
    #declare i=i+2;
#end

2つの正四面体の内の1つだけ描画するために, spherecylinderifで囲みます.

#declare i=-1;
#while(i<2)
    #declare j=-1;
    #while(j<2)
        #declare k=-1;
        #while(k<2)
            #if(mod(i*j*k,2)=1) sphere{<i,j,k>,0.05 pigment{rgb<1,1,0>}} #end
            #declare k=k+2;
        #end
        #declare l=0;
        #while (l<3)
            #if(mod(i*j,2)=1) cylinder{Rotate(<i,j,1>,l),Rotate(<i,-j,-1>,l),0.05 pigment{rgb<1,1,0>}} #end
            #declare l=l+1;
        #end
        #declare j=j+2;
    #end
    #declare i=i+2;
#end

正十二面体

前回愚直に書こうとして諦めた正十二面体も, 次のように書けます.

#declare phi=(1+sqrt(5))/2;

#declare i=-1;
#while(i<2)
    #declare j=-1;
    #while(j<2)
        #declare k=-1;
        #while(k<2)
            sphere{<i,j,k>,0.05 pigment{rgb<1,0,0>}}
            #declare k=k+2;
        #end
        #declare l=0;
        #while (l<3)
            sphere{Rotate(<i*phi,j/phi,0>,l),0.05 pigment{rgb<1,0,0>}}
            cylinder{Rotate(<i*phi,0,0>,l),Rotate(<i*phi,j/phi,0>,l),0.05 pigment{rgb<1,0,0>}}
            cylinder{Rotate(<i,j,1>,l),Rotate(<i*phi,j/phi,0>,l),0.05 pigment{rgb<1,0,0>}}
            cylinder{Rotate(<i,j,-1>,l),Rotate(<i*phi,j/phi,0>,l),0.05 pigment{rgb<1,0,0>}}
            #declare l=l+1;
        #end
        #declare j=j+2;
    #end
    #declare i=i+2;
#end

正二十面体

最後の例は正二十面体です.
これも各頂点座標が黄金比で書けます.

#declare i=-1;
#while(i<2)
    #declare j=-1;
    #while(j<2)
        #declare l=0;
        #while (l<3)
            sphere{Rotate(<i,j*phi,0>,l),0.05 pigment{rgb<0,0,1>}}
            cylinder{Rotate(<0,j*phi,0>,l),Rotate(<i,j*phi,0>,l),0.05 pigment{rgb<0,0,1>}}
            #declare k=-1;
            #while(k<2)
                cylinder{Rotate(<1,1*phi,0>,l)*<i,j,k>,Rotate(<1,1*phi,0>,l+1)*<i,j,k>,0.05 pigment{rgb<0,0,1>}}
                #declare k=k+2;
            #end
            #declare l=l+1;
        #end
        #declare j=j+2;
    #end
    #declare i=i+2;
#end