第 8 章 関数とマクロ 84
8.4 マクロ
填される。Pは小数点以下の桁数。
8.4 マクロ
8.4.1 マクロ宣言
マクロは、「マクロ宣言」(macro declaration)と呼ばれる宣言を書くことによって定義するこ とができます。
マクロ宣言は、
#macro マクロ名 ( 仮引数 ,
...
)動作の記述
#end
と書きます。「マクロ名」のところには、マクロに与えたい識別子を書いて、「動作の記述」のと ころには、マクロの動作となる記述を書きます。「仮引数」のところには、引数に与える識別子 を書きます。仮引数は、マクロが実行されているあいだだけ、マクロが受け取った引数に、名前 として与えられます。たとえば、
#macro Ball(Radius) sphere { 0, Radius }
#end
というマクロ宣言を書くことによって、受け取った引数を半径とする球を原点に作る、Ballと いうマクロを定義することができます。このマクロを、
Ball(7)
というマクロ呼び出しで呼び出すと、Radiusという仮引数が7という数値に名前として与えら れますので、半径が7の球が原点に作られることになります。
8.4.2 仮引数と引数との対応
仮引数と引数とは、マクロ宣言の丸括弧の中で仮引数が並んでいる順序と、マクロ呼び出しの 丸括弧の中で式が並んでいる順序とが一致するように対応づけられます。たとえば、
#macro Hoge(A, B, C) 動作の記述
#end
というマクロを、
Hoge(5, 7, 3)
というマクロ呼び出しで呼び出したとすると、Aは5を、Bは7を、Cは3を受け取ることにな ります。
次のシーンは、3個のトーラスを作ります。
シーンの例 macrotorus.pov
#macro Torus(Radius1, Radius2, Rotate, Translate, Color) object {
torus { Radius1, Radius2 } pigment { color rgb Color } rotate Rotate
translate Translate }
#end camera {
location <0, 12, -40>
look_at 0 angle 70 }
light_source { <50, 50, -50> color rgb 1.6 } Torus(10, 1, 0, <-12, 0, 0>, <1, 1, 0>) Torus(10, 1, 0, <12, 0, 0>, <0, 1, 1>) Torus(6, 0.7, 90*x, 0, <0, 1, 0>)
このシーンの中で定義されているTorusは、トーラスを作るマクロです。このマクロは、次の ような引数を受け取ります。
(1) トーラス全体の半径。
(2) 円管の断面の半径。
(3) 回転角(3次元ベクトル)。
(4) 中心の位置の座標(3次元ベクトル)。
(5) 色(3次元ベクトル)。
8.4.3 マクロ宣言の中での識別子の宣言
マクロ宣言の中には、識別子の宣言を書くことも可能です。ただし、マクロ宣言の中では、識 別子の宣言は、通常、
#local 識別子 = 記述
と書きます。つまり、#declareと書くのではなくて、#localと書くわけです。
#localを使って宣言された識別子は、#declareの場合とは違って、それが名前として何かに
与えられるのは、マクロが実行されているあいだだけです。したがって、その識別子は、マクロ 宣言の外にはいかなる影響も与えません。もしもマクロ宣言の外で同じ識別子が使われていたと しても、それらの識別子は互いにまったく無関係だとみなされます。ですから、マクロ宣言を書 くときには、識別子の宣言に#localを使っている限り、識別子の重複を気にする必要はありま せん。
マクロの仮引数も、それが引数に名前として与えられるのは、マクロが実行されているあいだ だけですので、#localを使って宣言された識別子と同じように、マクロ宣言の外にある識別子 との重複は、気にしなくても大丈夫です。
次のシーンは、3列の球の列を作ります。
シーンの例 macrosequence.pov
#macro Sequence(N, Radius, Rotate, Translate, Color)
#local X = 0;
#while (X < N * Radius * 2) union {
object {
sphere { <X, 0, 0>, Radius } }
pigment { color rgb Color } rotate Rotate
translate Translate }
#local X = X + Radius * 2;
#end
#end camera {
location <6, 8, -30>
look_at <6, 8, 0>
angle 70 }
light_source { <-50, 50, -50> color rgb 1.6 } Sequence(10, 1, 0, 0, <0.5, 1, 0>)
Sequence(20, 0.5, 0, <0, 16, 0>, <0, 0.5, 1>) Sequence(5, 2, 90*z, <-6, 0, 0>, <1, 0.5, 0>)
8.4. マクロ 91 このシーンの中で定義されているSequenceは、球を並べた列を作るマクロです。このマクロ は、次のような引数を受け取ります。
(1) 球の個数。
(2) 球の半径。
(3) 回転角(3次元ベクトル)。
(4) 先頭の球の座標(3次元ベクトル)。
(5) 色(3次元ベクトル)。
8.4.4 ユーザー定義関数
第8.1.5項で説明したように、動作が式によって記述されたマクロを定義すれば、そのマクロ
はユーザー定義関数になります。
次のシーンは、ランダムな角度、ランダムな位置、ランダムな色で、200個のランダムな英字 の大文字を作っています。
シーンの例 alphabet.pov
#macro RandAlpha(R)
chr(int(rand(R)*25)+65)
#end
#macro RandColor(R)
<rand(R), rand(R), rand(R)>
#end
#macro RandRotate(R)
<rand(R)*360, rand(R)*360, rand(R)*360>
#end
#macro RandTranslate(R, D)
<rand(R)*D, rand(R)*D, rand(R)*D>
#end camera {
location <50, 50, -40>
look_at <50, 50, 0>
angle 70 }
light_source { <20, 20, -100> color rgb 1.6 }
#declare RA = seed(127);
#declare RC = seed(255);
#declare RR = seed(511);
#declare RT = seed(1023);
#declare N = 0;
#while (N < 200) object {
text { ttf "timrom.ttf", RandAlpha(RA), 0.2, 0 } pigment { color rgb RandColor(RC) }
scale 20
rotate RandRotate(RR)
translate RandTranslate(RT, 100) }
#declare N = N + 1;
#end
このシーンの中では、次のような関数を定義しています。
RandAlpha(R) 擬似乱数列Rを使って生成したランダムな英字の大文字を返す。
RandColor(R) 擬似乱数列Rを使って生成したランダムな色をあらわす3次元ベクト
ルを返す。
RandRotate(R) 擬似乱数列Rを使って生成したランダムな回転角をあらわす3次元ベ
クトルを返す。
RandTranslate(R, D) 擬似乱数列Rを使って生成した、一辺がDの立方体の中にあるラン ダムな座標を返す。