2012/12/01 〜 2012/12/25TeX & LaTeX Advent Calendar
こちらは平常(ry
* * *
(前回の続き)
TeX で「ガウス素数の模様」してみた
このプログラムで gptexture()(\gptexture)の引数に 39 を指定した場合、絶対値 39 以下のガウス整数が「模様の描画」の対象になるので、全体の模様は (−39,−39) から (+39,+39) の範囲に収まることになる。元の Lua のプログラムではこれを 79 行 79 桁のテキストで表現しているが、TeX では次のような図として出力することにする。
\begin{picture}(79,79)(-39,-39) %...... % 1-4i がガウス素数であるなら次の命令を実行 \put(1,-4){\rule{\unitlength}{\unitlength}} %...... \end{picture}
ここで図の描画で用いる \unitlength の値を \gptunitlength という寸法値マクロ(\renewcomand で設定する)で設定できるようにした。
gptexture1.lua の set()、reset()、cur() にあたるマクロは次のように成る。
\def\tcgp@set#1#2{\@namedef{tcgp@a/\number#1/\number#2}{1}}
\def\tcgp@reset#1#2{\@namedef{tcgp@a/\number#1/\number#2}{0}}
\def\tcgp@cur#1#2{\@nameuse{tcgp@a/\number#1/\number#2}}\tcgp@set と \tcgp@reset は通常通りの書き換えになっている((ここでは名前参照のマクロに代入される値が定数(0 や 1)なので「nameedef」は不要で \@namedef で済む。))が、\tcgp@cur は少し異様である。cur() は値を返す関数であるから、本則に従うと「手続きへの変換」が必要になるところであろう。ここでは \tcgp@cur を(Lua や C 言語の)「関数」ではなく、単純に TeX の本来の意味での「マクロ」である(つまり単純に文字列の置き換えをするもの)と捉えてほしい。つまり、\tcgp@cur は \@nameuse{...} に展開されるマクロであるから、TeX プログラム中の「値」を表すコードとして \@nameuse{...} があるなら、その代わりに \tcgp@cur を書いてもよいはずである。((「マクロ」にならない「関数」との違いについて少し補足する。例えば、\def\ansA{42} というマクロがあると、コード中の「42」という記述を \ansA に置き換えることができる(かも知れない)。これに対して、\def\ansB{\count@=42 \the\count@} のようなマクロは確かに実行すると \ansA と同じく「42」という出力を行うが、これはコード中の「42」という記述の代わりにはならない。ただ実際にはこの推論についても「値を表すコード」(= 必要に応じてマクロが展開される文脈)という留保条件がつく。))
同様な考えで、絶対値(math.abs())を表すコード(前回に紹介した)を「マクロ」\tcgp@abs にすることができる。
\def\tcgp@abs#1{\ifnum#1<0 -\fi #1}すなわち、\tcgp@abs\tcgp@x は「\ifnum\tcgp@x<0 -\fi \tcgp@x」に展開される。
最後に not の扱いの話をする。if 文であれば、not を処理するのは容易で、単に then と else の部分を入れ替えればよい。
if not (a == 0) then b = a end ↓ \ifnum \tcgp@a=0 \else \tcgp@b=\tcgp@a \fi
これに対して、while 文(\@whilenum)の場合はそのような方法が通用せず、従って、「元の式の否定と同値になる数値比較の式」を何とかして捻り出す必要がある。ここで TeX の if 文が三項演算子((Lua のイディオムでは判り難いのでここでは敢えて C 言語の書き方「(cond ? yes : no)」を採った。))として使えることを思い出すと、「数値比較の結果の真偽値を一度 0/1 の整数に変換してから 0 と比較する」ことで所望の式が得られる。
while not (a == 0) do ... end
↓
while ((a == 0) ? 1 : 0) == 0 do ... end
↓
\@whilenum{\ifnum\tcgp@a=0 1\else0 \fi =0 }\do{ ... }この定型のコードもマクロ \tcgp@not として定義しよう。((実はこの定義だと #1 の直後の空白文字トークンの扱いが微妙で、例えば \tcgp@{0=\tcgp@a} のように用いると「前の数値」によって吸収されず残ってしまう。ところが \ifnum...\fi の部分自体が比較式の左辺の数値の部分に相当しているので、「1」という数字を読む前にある空白トークンは結局無視されることになる。))
\def\tcgp@not#1{\ifnum#1 1 \else 0 \fi =0 }
% これで \@whilenum{\tcgp@not{\tcgp@a=0}}\do{ ... }これで gptexture1.lua を TeX プログラムに書き直す準備が整った。
完成したパッケージのコードは以下の通り。パッケージ名は tcgptexture、名前空間は tcgp。公開命令は \gptunitlength と \gptexture の 2 つである。
% tcgptexture.sty
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{tcgptexture}
%% 変数定義
%(整数値)
\newcount\tcgp@t
\newcount\tcgp@n
\newcount\tcgp@rn
\newcount\tcgp@x
\newcount\tcgp@y
\newcount\tcgp@px
\newcount\tcgp@py
\newcount\tcgp@qx
\newcount\tcgp@qy
%(配列型)
%\tcgp@ct/*
%%(公開) \gptunitlength{<寸法>}
% 1つの点(四角)の大きさ.
\newcommand*{\gptunitlength}{2.5pt}
%%(公開) \gptexture{<>}
% 絶対値がn以下の範囲での模様を出力する.
\newcommand*{\gptexture}[1]{%
\begingroup
\setlength{\unitlength}{\gptunitlength}%
\tcgp@n=#1
\ifnum\tcgp@n<1 \tcgp@n=1 \fi
\tcgp@gpt@setrn
\tcgp@t=\tcgp@n \multiply\tcgp@t2 \advance\tcgp@t1
\begin{picture}(\tcgp@t,\tcgp@t)(-\tcgp@n,-\tcgp@n)%
\tcgp@gpt@init
\tcgp@gpt@sieve
\tcgp@gpt@show
\end{picture}
\endgroup
}
%% 補助的なマクロ
\def\tcgp@set#1#2{\@namedef{tcgp@a/\number#1/\number#2}{1}}
\def\tcgp@reset#1#2{\@namedef{tcgp@a/\number#1/\number#2}{0}}
\def\tcgp@cur#1#2{\@nameuse{tcgp@a/\number#1/\number#2}}
\def\tcgp@abs#1{\ifnum#1<0 -\fi #1}
\def\tcgp@not#1{\ifnum#1 1 \else 0 \fi =0 }
%% \tcgp@pixel
\def\tcgp@pixel#1#2{%
\put(#1,#2){\rule{\unitlength}{\unitlength}}%
}
%% \tcgp@gpt@setrn
\def\tcgp@gpt@setrn{%
\tcgp@rn=0 \tcgp@t=0
\@whilenum\tcgp@not{\tcgp@t>\tcgp@n}\do{%
\advance\tcgp@rn1 \tcgp@t=\tcgp@rn \multiply\tcgp@t\tcgp@rn
}%
\advance\tcgp@rn-1
}
%% \tcgp@gpt@init
\def\tcgp@gpt@init{%
\tcgp@x=-1
\@whilenum \tcgp@x<\tcgp@n \do{%
\advance\tcgp@x1 \tcgp@y=-1
\@whilenum \tcgp@y<\tcgp@n \do{%
\advance\tcgp@y1 \tcgp@set{\tcgp@x}{\tcgp@y}%
}%
}%
\tcgp@reset{0}{0}\tcgp@reset{1}{0}\tcgp@reset{0}{1}%
}
%% \tcgp@gpt@sieve
\def\tcgp@gpt@sieve{%
\tcgp@x=0
\@whilenum \tcgp@x<\tcgp@rn \do{%
\advance\tcgp@x1 \tcgp@y=-1
\@whilenum \tcgp@y<\tcgp@x \do{%
\advance\tcgp@y1
\ifnum \tcgp@cur{\tcgp@x}{\tcgp@y}>0
\tcgp@px=\tcgp@x \tcgp@py=\tcgp@y
\@whilenum\tcgp@not{\tcgp@px>\tcgp@n}\do{%
\tcgp@qx=\tcgp@px \tcgp@qy=\tcgp@py \tcgp@t=1
\@whilenum \tcgp@t>0 \do{%
\tcgp@reset{\tcgp@qx}{\tcgp@qy}\tcgp@reset{\tcgp@qy}{\tcgp@qx}%
\advance\tcgp@qx-\tcgp@y \advance\tcgp@qy\tcgp@x
\ifnum \tcgp@qx<0 \tcgp@t=0 \fi
\ifnum \tcgp@qy>\tcgp@n \tcgp@t=0 \fi
}%
\tcgp@qx=\tcgp@px \tcgp@qy=\tcgp@py \tcgp@t=1
\@whilenum \tcgp@t>0 \do{%
\tcgp@reset{\tcgp@qx}{\tcgp@qy}\tcgp@reset{\tcgp@qy}{\tcgp@qx}%
\advance\tcgp@qx\tcgp@y \advance\tcgp@qy-\tcgp@x
\ifnum \tcgp@qx>\tcgp@n \tcgp@t=0 \fi
\ifnum \tcgp@qy<0 \tcgp@t=0 \fi
}%
\advance\tcgp@px\tcgp@x \advance\tcgp@py\tcgp@y
}%
\tcgp@set{\tcgp@x}{\tcgp@y}\tcgp@set{\tcgp@y}{\tcgp@x}%
\fi
}%
}%
}
%% \tcgp@gpt@show
\def\tcgp@gpt@show{%
\tcgp@x=-\tcgp@n \advance\tcgp@x-1
\@whilenum \tcgp@x<\tcgp@n \do{%
\advance\tcgp@x1 \tcgp@y=-\tcgp@n \advance\tcgp@y-1
\@whilenum \tcgp@y<\tcgp@n \do{%
\advance\tcgp@y1
\tcgp@px=\tcgp@abs\tcgp@x \tcgp@py=\tcgp@abs\tcgp@y
\ifnum \tcgp@cur{\tcgp@px}{\tcgp@py}>0
\tcgp@qx=\tcgp@x \multiply\tcgp@qx\tcgp@x
\tcgp@qy=\tcgp@y \multiply\tcgp@qy\tcgp@y \advance\tcgp@qx\tcgp@qy
\tcgp@qy=\tcgp@n \multiply\tcgp@qy\tcgp@n
\ifnum\tcgp@not{\tcgp@qx>\tcgp@qy}%
\tcgp@pixel{\tcgp@x}{\tcgp@y}%
\fi
\fi
}%
}%
}
\endinputテスト用文書は以下の通り。
\documentclass[a4paper]{article} \usepackage[scale=0.9]{geometry} \usepackage{tcgptexture} \begin{document} \begin{center} \gptexture{99} \end{center} \end{document}

ここまでの内容をマスターしたら、もう「TeX でマンデルブロ集合」も怖くない!?