マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

LaTeX 上で色々と実装してみる話(ナベアツ編-6)

前回の続き)

なんだか「マクロツイーター」っぽくなってきましたね!

MetaPost

前回は PostScript を用いてナベアツを実装した。描画言語を利用するというアイデアは有望であるが、しかし残念ながら PostScript ではフォントをアホにすることができなかった。*1そうすると「TeX のフォントが自由に使える描画言語」の使用という考えに自然と行き着くであろう。というわけで、MetaPost を使うことにする。

MetaPost のコードを LaTeX の文書に挿入するためのパッケージを CTAN で探すと、emp というものが見つかった。これを使うと、次のようなことができる。*2(パッケージの使用法はマニュアルを参照されたい。「小さなパッケージ」なので容易に理解できる。)

[example-emp.tex]
\documentclass[dvips]{article}
\usepackage{emp}
\begin{document}
\begin{empfile}

% 設定やマクロ定義(beginfig〜endfig 外のもの)を
% empcmds 環境に記述する.
\begin{empcmds}
  def char_ol (expr x, y, fnt, fsiz, ch) =
    for item within glyph ch of fnt scaled .001fsiz:
      draw pathpart item shifted (x,y);
    endfor
  enddef;
\end{empcmds}

% 各々の図を emp 環境に記述する.
% (0,0)はMetaPostに引き渡される「図の大きさ」だが,
% ここでは利用しないのでゼロにした.
\begin{emp}(0,0)
  pickup pencircle scaled .4pt;
  string fnt; fnt := "cmbx10";
  char_ol( 0.00pt, 0.00pt,fnt,10pt,"T");
  char_ol( 6.56pt,-2.68pt,fnt,10pt,"E");
  char_ol(12.68pt, 0.00pt,fnt,10pt,"X");
\end{emp}%
\end{empfile}
\end{document}

ただ残念なことに、このパッケージでは、「LaTeX のユーザ定義命令に利用する」ことを想定していないので、以下のような制限があって、「ナベアツ問題」の解決には使えない。

  • MetaPost のコード記述について、verbatim に書く方法(環境)しか用意されていない。だから、命令の引数(#1)を MetaPost 側に渡すことができない。
  • 一連の emp や empcmds 環境を empfile 環境に含めることが要求される。*3これだと、MetaPost のコードがプレアンブル((ユーザ定義命令(\NabeAzz)の定義は当然プレアンブルに書きたい!))と本体に分散する場合に対応できない。

あれこれ悩むよりも、作る方が早いので……

このパッケージは次の機能を提供する。

  • emp 環境、empcmds 環境の「命令版」を提供する。((\doempdef もある。))
    • \doemp[<名前>](<幅>,<高さ>){<MetaPostコード>}
    • \doempcmds{<MetaPostコード>}
    ここで引数のコードの部分は verbatim でない(命令引数が参照できる)。
  • \empfixedfile[<ファイルベース名>] を実行すると、文書のそれ以降を \begin{empfile}[<ファイルベース名>]〜\end{empfile} で囲ったのと同等の動作をする。元々の empfile と異なり、\begin{document} を跨ぐことが可能である。
  • pdfTeX に対応させた。

これで無事にナベアツできるようになる。

[nabeazz-metapost.tex]
\documentclass[a4paper]{article}% when using pdfTeX
%\documentclass[a4paper,dvips]{article}%  when using dvips
\usepackage{emp,bxemp}
\empfixedfile
\newcommand*\NabeAzz[1]{%
  \doemp(0,0){nabeazz(#1);}%
}
%!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BEGIN MetaPost code
\begin{empcmds}
prologues := 3;
nazunit = 12;
nazwidth = 144;
string nazfont; nazfont = "cmr10"; nazfsize = 10pt;
string nazahofont; nazahofont = "cmfi10"; nazahofsize = 14.4pt;
nazsep = 5pt;

vardef nazaho primary n =
  save i, n_, s, r; n_ = n;
  if n mod 3 = 0:
    true
  else:
    boolean r; r = false;
    string s; s = decimal n_;
    for i = 1 upto length s:
      r := r or ((substring (i-1,i) of s) = "3");
    endfor
    r
  fi
enddef;

vardef naznumber(expr n) =
  naznumberin(n, nazaho n)
enddef;
naznht = ypart(urcorner("3" infont nazahofont scaled
    (nazahofsize / fontsize nazahofont)));
vardef naznumberin(expr n, a) =
  save n_, fnt, fsiz, pic; n_ = n;
  string fnt;
  if a: fnt = nazahofont; fsiz = nazahofsize;
  else: fnt = nazfont; fsiz = nazfsize;
  fi
  nazsetheight(decimal n_ infont fnt scaled (fsiz / fontsize fnt),
      naznht)
enddef;
vardef nazsetheight(expr pic, ht) =
  save pic_, wd; picture pic_; pic_ := pic;
  wd = xpart(urcorner pic_);
  setbounds pic_ to (0,0)--(wd,0)--(wd,ht)--(0,ht)--cycle;
  pic_
enddef;

path nazpath;
def nazmakepath(expr m) =
  begingroup save m_, u, np, npx, npy, nplen; m_ = m;
    nplen = xpart(urcorner(naznumberin(m_, true))) * 1.5m_;
    npx = npy = 0; u = nazunit;
    npx3 = nazwidth; npx3-npx2 = npx1 = u;
    path np; np = (u, 0);
    forever:
      np := np--(npx2,npy)..(npx3,npy-u)..(npx2,npy-2u)
            --(npx1,npy-2u)..(0,npy-3u)..(npx1,npy-4u);
      exitif arclength np >= nplen;
      npy := npy-4u;
    endfor
    nazpath = np;
  endgroup
enddef;

def nabeazz(expr m) =
  begingroup save m_, n, t, p, d, pic, cur; m_ = m;
    nazmakepath(m_);
    cur = 0;
    for n = 1 upto m:
      picture pic; pic := naznumber(n);
      forever:
        t := arctime cur of nazpath;
        pair p; p := point t of nazpath;
        pair d; d := direction t of nazpath;
        exitif abs d > 0;
        cur := cur + eps;
      endfor
      label(pic rotated angle(d), p);
      cur := cur + xpart(urcorner(pic)) + nazsep;
    endfor
  endgroup
enddef;
\end{empcmds}
%!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! END MetaPost code
%
\begin{document}
\NabeAzz{40}
\end{document}

pdfTeX エンジンを使う場合は次の手順でコンパイルする。

pdflatex nabeazz-metapost
mpost nabeazz-metapost
pdflatex nabeazz-metapost

dvips を使う場合は、クラスオプションにドライバ指定 dvips を追加したうえで、次の手順でコンパイルする。(出力は PostScript 文書。)

latex nabeazz-metapost
mpost nabeazz-metapost
latex nabeazz-metapost
dvips nabeazz-metapost

なお、原理上は dvipdfmx を使用する((もちろんドライバオプション dvipdfmx の指定が必要。))ことも可能なはずなのだが、上掲の文書では何故か異常な出力になってしまった。*4残念!

正常にコンパイルされた場合は以下の出力が得られる。

これまでのナベアツの出力とは若干違ってるようにも見えるが、気にしないことにしよう。

(えっまだ続くの)

*1:何か細工をすればできそうな気もするが。

*2:この文書のコンパイルの方法は、後で説明する nabeazz-metapost.tex の dvips 版のものと同じである。オリジナルの emp パッケージは pdfTeX に非対応である。

*3:この環境で囲った部分が example-emp.mp というファイルの出力と対応する。empfile 環境はオプション引数をもち、そこには出力ファイルベース名が指定できる。つまり、「複数の MetaPost ファイルに分割して出力する」ことが想定されているわけである。

*4:MetaPost の出力形式は EPS の一種である。ところが dvipdfmx は MetaPost の出力については Ghostscript に渡さずに自前で処理を行う。多分このあたりの処理が怪しいんだと思う。