MetaPost (luamplib, gmp) で☃

> これは「[TeX ＆ LaTeX Advent Calendar 2018](https://adventar.org/calendars/3382)」の 3 日目の記事です．2 日目は [puripuri2100](https://puripuri2100.hatenablog.com/entry/2018/12/02/091222) さん，4 日目は [golden_lucky](http://golden-lucky.hatenablog.com/entry/2018/12/04/183000) さんです．

# はじめに
[LuaTeX-ja](https://ja.osdn.net/projects/luatex-ja/wiki/FrontPage) に関わっている関係で，私は LaTeX で文書を作る時はほぼすべて LuaLaTeX  を使用しています．

その際に図をどうやって作るか，ということはいつも問題になります．(u)pLaTeX + dvipdfmx を主に使っていたときには [初等数学プリント作成マクロ emath](http://emath.s40.xrea.com/) を主に使用していました（今でも周囲にはそういう人がちらほらいます）．また，今から始めるというのであれば PGF/TikZ（[TeX Wiki 内の記事](https://texwiki.texjp.org/?TikZ)）を使う，というのがメジャーな選択肢となるのでしょう．

さて，LuaTeX には 内部に [MetaPost](https://tug.org/metapost.html) ライブラリが含まれており，さらにそれを TeX から扱うためのパッケージとして [luamplib](https://ctan.org/pkg/luamplib) パッケージというものが制作されています[^1]．これを利用しない手はないではないか……ということで，私は専ら MetaPost を図の作成に用いるようになりました．

# luamplib パッケージの使い方
## 基本的な使い方
基本的には `\usepackage` で読み込み，mplibcode 環境内に MetaPost コードを記述するだけです．点につけるラベルなど，TeX コードを記述したい場合は `btex ... etex` で囲んだり，`textext()` を使ったりします．

```latex:test.tex
\documentclass{article}
\usepackage{luamplib}
\begin{document}
Hello, MetaPost! \vrule
\begin{mplibcode}
  beginfig(1);
  pickup pencircle scaled 0.25mm;
  draw (0,0)..(0,5mm)..(1cm,5mm)..(5mm,0mm)..(-5mm,0)..cycle;
  dotlabel.lrt(btex $P_1$ etex, (0,0));
  endfig;
\end{mplibcode}
\vrule HOGEHOGE
\end{document}
```
![test.png](https://qiita-image-store.s3.amazonaws.com/0/61603/32970130-fef1-385d-7a27-e5a35da28dc8.png)

ラベルに日本語を使いたい場合，LuaTeX-ja パッケージをいつもどおり読み込むだけで問題ありません（LuaTeX-ja 側で何か特別なことをしているのでもありません）．

### LuaJITTeX 使用時の注意
Lua 5.2/5.3 の代わりに LuaJIT を利用した LuaJITTeX で luamplib パッケージを読み込もうとすると，以下のようなエラーが発生します．これは LuaJITTeX において lfs が定義済みなのになぜか読み込み済みライブラリとしてマークされていないことによるようです．

```
module 'lfs' not found:
        no field package.preload['lfs']
        [kpse lua searcher] file not found: 'lfs'
        [kpse C searcher] file not found: 'lfs'
stack traceback:
        [C]: in function 'require'
        ...texlive/2018/texmf-dist/tex/luatex/luamplib/luamplib.lua:48: in main chunk
        [C]: in function 'require'
        [\directlua]:1: in main chunk.
l.22 \directlua{require("luamplib")}

?
```

どうやら luamplib.lua 中の 48 行目を以下のように書き換えれば良いようです：

```lua
local lfs   = lfs or require ('lfs') --  元は local lfs   = require ('lfs')
```

## `\everymplib`, `\everyendmplib`
mplibcode 環境内に毎回 `beginfig ... endfig` や，図中で使いたい各種マクロを繰り返し書くのは面倒です．luamplib パッケージでは `\everymplib`, `\everyendmplib` を使って，mplibcode 環境の開始・終了に自動で挿入される MetaPost コードを指定できます．例えば，毎回自動で `beginfig`, `endfig` を追加させることにし，さらに領域を斜線で塗るマクロ `hatch` を入れると次のようになります．

```latex:test2.tex
\documentclass{article}
\usepackage{luamplib}
\everymplib{beginfig(1);
  def hatch(expr p)=
  begingroup
    save pct, x, y; picture pct[];
    numeric xl, xu, yl, yu;
    pct1 := currentpicture; currentpicture := nullpicture;
    draw p;
    (xl, yl) = llcorner currentpicture; (xu, yu) = urcorner currentpicture;
    currentpicture := nullpicture;
    pickup pencircle scaled 0.125mm;
    for i = xl-xu step 1.5mm until yu-yl:
      draw (xl, i+yl)--(xu, i+yl+(xu-xl));
    endfor;
    clip currentpicture to p;
    pct2 := currentpicture; currentpicture := pct1; draw pct2;
  endgroup;
enddef;
}
\everyendmplib{endfig;}
\begin{document}
\begin{mplibcode}
  path p; p:=(0,0)--(0,5mm)..(1cm,2mm){dir -50}..(5mm,-3cm)--(2cm,0mm)--cycle;
  hatch(p); pickup pencircle scaled 0.25mm; draw p;
\end{mplibcode}
\end{document}
```

## 寸法・色の指定
mplibcode環境で描かれる図は，周囲の文字色の影響は受けません（無指定時はいつも黒）．mplibcode 環境内で寸法や色を指定するには，それぞれ `\mpdim`, `\mpcolor` 命令を使用します．color パッケージや xcolor パッケージを読み込んでいる場合には，`\mpcolor` の引数には `\color` で指定可能な色指定をそのまま記述できます．xcolor パッケージを読み込んでいると「現在の色」を `.` で表現できるので，`\mpcolor{.}` を活用することで周囲の文字色と同じ色にすることができます．

```latex:test3.tex
\documentclass{article}
\usepackage{luamplib}
\usepackage{xcolor}% 自動では読まない
\begin{document}
M\textcolor{red}{xyz%
\begin{mplibcode}
  beginfig(1);
  u:=0.5\mpdim{4ex}+2mm; v:=\mpdim{4em};
  fill (0,0)--(2v,0)--(2v,u)--cycle ; %無指定時は黒
  fill (0,0)--(2v,0)--(2v,.5u)--cycle withcolor \mpcolor{.}; % 周囲の文字色
  fill (0,0)--(1v,0)--(0,1u)--cycle withcolor \mpcolor{red!90!black};
  fill (0,0)--(.8v,0)--(0,.8u)--cycle withcolor \mpcolor{blue!80!black};
  fill (0,0)--(.6v,0)--(0,.6u)--cycle withcolor \mpcolor{green!70!black};
  endfig;
\end{mplibcode}
aa}
\end{document}
```
![test3.png](https://qiita-image-store.s3.amazonaws.com/0/61603/16cf803b-e5be-b2c9-6c5e-317cdba7f802.png)


なお，`\mpcolor` で指定した色に対しては `withcolor blue+0.5\mpcolor{red!90!black}` のように演算を行うことはできません．

# 本題：scmpsnowman パッケージ
さて，luamplib パッケージの使い方を紹介するだけでは記事としておもしろくありません．日本の（一部の）TeX ユーザには☃（ゆきだるま）が流行しているので，それを使ってなにかやることにしました．

LaTeX 上でゆきだるまを描く手法として，aminophen さんの scsnowman パッケージ（[GitHub リポジトリ](https://github.com/aminophen/scsnowman)，[開発者による紹介記事1](http://acetaminophen.hatenablog.com/entry/2015/12/13/080226)，[紹介記事2](http://acetaminophen.hatenablog.com/entry/2016/08/08/080800)）が知られています．このパッケージは TikZ による実装だったので，MetaPost (luamplib) に移植してみました．

* scmpsnowman パッケージ（[GitHub リポジトリ](https://github.com/h-kitagawa/scsnowman/tree/kitagawa_mp/scmpsnowman)）

ネタで作ったものなので，大元の scsnowman パッケージレポジトリの個人フォーク→その中の一ブランチ→さらに下位フォルダ，というひっそりとした箇所においています．

## 動作環境
想定する環境は TeX Live 2018 上の LuaLaTeX ですが，(u)pLaTeX/pdfLaTeX/XeLaTeX でも動くかもしれません（後述）．以下のパッケージが必要です．

* luamplib パッケージ（LuaLaTeX 下のみ），[gmp](https://ctan.org/pkg/gmp) パッケージ（その他）
* xcolor パッケージ
* keyval パッケージ（本家と同様，オプション引数の解釈のため．LuaTeX-ja を使っているならすでに入っている）
* [etoolbox](https://ctan.org/pkg/etoolbox) パッケージ（LuaTeX-ja を使っているならすでに入っている）

## 基本的な使い方
まず，scmpsnowman-normal.def, scmpsnowman.sty, scmpsym-base.sty を TeX が参照できるディレクトリに配置しておきます．

LaTeX ソース内からは，通常通り `\usepackage` で読み込み，本家 scsnowman パッケージと同様にゆきだるまを置きたい場所で `\scsnowman` 命令を実行するだけです．

```latex:sample1.tex
\documentclass{ltjsarticle} % LuaLaTeX 文書
\usepackage{scmpsnowman}
\begin{document}
これはゆきだるま\scsnowman です．
\end{document}
```
![sample1.png](https://qiita-image-store.s3.amazonaws.com/0/61603/192c7567-18be-7c21-e1a7-77d5cdb7b36a.png)

下のサンプルのように，本家と同じ書式で各種オプションを指定できるようにしています．`\scsnowman` 命令以外の動作確認はほとんどしていません．

```latex:sample2.tex
\documentclass{ltjsarticle}
\usepackage[svgnames]{xcolor}
\usepackage{scmpsnowman}
\usepackage{mflogo} % MetaPost ロゴ
\scsnowmandefault{hat=Green,arms=Brown,snow=SkyBlue} % デフォルト指定
\begin{document}
\MP で\scsnowman[muffler=Red,,adjustbaseline]を描けます．
\end{document}
```
![sample2.png](https://qiita-image-store.s3.amazonaws.com/0/61603/603306e7-538d-bb4a-f4bf-1b0d1932f60c.png)

（2018-12-04 追記）上記 sample1.tex, sample2.tex では LuaTeX-ja のクラスファイルを使いましたが，その必要はありません．例えば以下の例のように．

```latex:sample0.tex
\documentclass{minimal}
\usepackage{scmpsnowman}\listfiles
\begin{document}\scsnowman\end{document}
%  *File List*
%  minimal.cls    2001/05/25 Standard LaTeX minimal class
% scmpsnowman.sty    2018-12-02 Snowman variants using MetaPost
% etoolbox.sty    2018/08/19 v2.5f e-TeX tools for LaTeX (JAW)
%   xcolor.sty    2016/05/11 v2.12 LaTeX color extensions (UK)
%    color.cfg    2016/01/02 v1.6 sample color configuration
%   luatex.def    2018/01/08 v1.0l Graphics/color driver for luatex
% scmpsym-base.sty    2018-12-02 Base for emoji variants using MetaPost
% luamplib.sty    2018/09/27 v2.12.5 mplib package for LuaTeX
%   keyval.sty    2014/10/28 v1.15 key=value parser (DPC)
% scmpsnowman-normal.def
% supp-pdf.mkii
%  ***********
```

なお supp-pdf.mkii は luatex.def から読み込まれるようです．（追記ここまで）


## 非 LuaLaTeX 環境（(u)pLaTeX/pdfLaTeX/XeLaTeX）
LuaLaTeX でない環境では，もちろん luamplib パッケージを使うことはできません．その代わりに，同様の「LaTeX ソース中に MetaPost コードを記述する」機能を提供するパッケージの一つに [gmp](https://ctan.org/pkg/gmp) パッケージがあります．scmpsnowman パッケージは，LuaLaTeX でない環境で読み込まれた場合には自動的に gmp パッケージを読み込みます．

gmp パッケージは，mpost 環境の中に書かれた内容を外部ファイルに書き出し，shell-escape が有効なら（パッケージ側にも shellescape オプションを渡す必要があります），その機能を使って MetaPost を実行する，という方式を取っています．shell-escape が無効の場合は `sh sample3+mp.sh` などとしてあとから自分で MetaPost 実行（など）を行うシェルスクリプトを自分で実行する必要があります．

ということで，LuaLaTeX でない環境下で scmpsnowman パッケージを使う場合には以下のようなソースを記述することになります．しかし shell-escape を有効にするのはセキュリティ的によろしくないので，非 LuaLaTeX 環境下では MetaPost をわざわざ使う意味は薄いように思います．**☃☃☃おとなしく本家 scsnowman パッケージを使いましょう☃☃☃**

```latex:sample3.tex
%#! ptex2pdf -l -ot '-shell-escape' sample3.tex
\documentclass[dvipdfmx]{minimal}
\usepackage[shellescape]{gmp}
\usepackage{scmpsnowman}
\begin{document}
This is a snowman:
\scsnowman[adjustbaseline,scale=2,hat,muffler=red,arms=blue]
\end{document}
```

### gmp パッケージ下での色の指定
gmp パッケージでは `\mpdim` 命令で TeX の寸法を指定することができます[^2]が，`\mpcolor` に対応する命令はありません．そこで，scmpsnowman パッケージ中では，luamplib の実装と似た（？）感じで適当に実装しています．

luamplib の `\mpcolor` 命令を使うと，`withcolor \mpcolor{blue}` は最終的には次の形になるようです（しっかり確かめたわけではありませんが）：

```
withcolor 1 withprescript "MPlibOverrideColor=0 0 1 rg 0 0 1 RG"
```

LuaTeX 内蔵の MetaPost ライブラリでなく，本来の MetaPost ソースで同じような指定を考えてみます．

```a.mp
beginfig(1);
draw (0,0)--(1,0) withcolor 1 withprescript "0 0 1 setrgbcolor";
draw (0,0)--(2,0) withcolor 1 withprescript "0 0 1 setrgbcolor";
endfig;
end.
```

上の a.mp を MetaPost に処理させると，次のような PostScript ファイル a.1 が出来上がります．`withprescript` による色指定の後に，`withcolor 1` による `1 setgray` が来てしまい，結果として最初のパスにおける `withprescript` による色指定は無効になってしまいます．

```a.1
%!PS
% （省略）
%%Page: 1 1
0 0 1 setrgbcolor
 1 setgray 0 0.5 dtransform truncate idtransform setlinewidth pop [] 0 setdash
 1 setlinecap 1 setlinejoin 10 setmiterlimit
newpath 0 0 moveto
1 0 lineto stroke
0 0 1 setrgbcolor
newpath 0 0 moveto
2 0 lineto stroke
showpage
%%EOF
```

そのため，gmp パッケージを使う際には，パスの色指定のところで `withoutcolor \mpcolor{blue}` のように記述（`\mpcolor` は `withprescript ...` に最終的に展開）するようにしています[^3]．

[^1]: もとは [ConTeXt](https://wiki.contextgarden.net/Main_Page) 中のコードを plain TeX や LaTeX でも扱えるように変更を加えたものだそうです．
[^2]: 実は，gmp パッケージが `\mpdim` 命令を先に実装し，luamplib パッケージがそれに触発されて `\mpdim` 命令を追加したという順番です．
[^3]: xcolor パッケージの色指定から PostScript 形式の指定への変換では，dvips.def, dvipdfmx.def, xetex.def にある `\c@lor@to@ps` 命令を使って行っています．しかし pdftex.def にはこの命令がないので，PDF における色指定を PostScript 形式のそれに変換する処理を scmpsym-base.sty 中に適当に作りました．