TikZで図を作る


FlatBuffersの説明で使った図の作り方です。

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
  \draw[very thick, fill=green!10] (0, 8) rectangle (5, 10);
  \draw[very thick, fill=red!10] (0, 5) rectangle (5, 8);
  \draw[very thick, fill=blue!10] (0, 2) rectangle (5, 5);
  \draw[very thick, fill=yellow!10] (0, 0) rectangle (5, 2);

  \node[below left, green!50!black] at (0, 10) {Header};
  \node[below left, red!50!black] at (0, 8) {Body};
  \node[below left, blue!50!black] at (0, 5) {VTable};
  \node[below left, yellow!50!black] at (0, 2) {String};

  \foreach \height in {1,...,9} \draw[dashed] (0, \height) -- +(5, 0);

  \node at (2.5, 9.5) {Offset to Body};
  \node at (2.5, 8.5) {File Identifier (UTF-8)};

  \draw[->, very thick, dotted] (4.5, 9.5) -- +(1, 0) -- +(1, -1.9) -- +(0, -1.9);
  \draw[->, very thick, dotted] (4.5, 7.4) -- +(1, 0) -- +(1, -2.9) -- +(0, -2.9);

  \node at (2.5, 7.5) {Offset to VTable};
  \node at (2.5, 6.5) {Value (i32)};
  \node at (2.5, 5.5) {Offset to String};

  \node at (2.5, 4.5) {Length of Body, VTable};
  \node at (2.5, 3.5) {Offset of 1th member};
  \node at (2.5, 2.5) {Offset of 2nd member};

  \draw[->, very thick, dashed, blue] (4.5, 2.5) -- +(1.2, 0) -- +(1.2, +2.9) -- +(0, +2.9);
  \draw[->, very thick, dashed, blue] (4.5, 3.5) -- +(1.4, 0) -- +(1.4, +3) -- +(0, +3);

  \draw[->, very thick, dotted, red] (4.5, 5.6) -- +(1.6, 0) -- +(1.6, -4) -- +(0, -4);

  \node at (2.5, 1.5) {``Actual String''};

  \draw[->, very thick] (6.5, 10) -- +(0, -10) node[right] {Memory};
\end{tikzpicture}
\end{document}

TikZの使い方

TikZで単独の図を生成するときは standalone.clsがおすすめです。これはTeXLiveの中でもtexlive-latex-extra等のパッケージに入っています (ディストリビューションによって分け方や命名方法が違うのでapt-file等で検索してください)。これで生成したPDFには余白が含まれず、そのままpdf2svg等でSVGに変換できて便利です。

\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
...
\end{tikzpicture}
\end{document}

図はtikzpicture環境内で作ります。

  \draw[very thick, fill=green!10] (0, 8) rectangle (5, 10);

長方形を描画します。[]内は\draw命令に対するオプションで、very thickは線をとても太く、fill=green!10は色の指定です。TikZではgreen!10!whiteのように二つの色を割合を指定して混ぜる事が出来ます。green!10のように省略するともう一つはwhite扱いになります。
rectangleは長方形の左下 (0, 8) と右上 (5, 10) の座標をもらって描画します。

  \node[below left, green!50!black] at (0, 10) {Header};

(0, 10) の位置にノード(頂点)を置いて文字を表示します。below leftは頂点に対してどの位置に文字を置くかを指定していて、これで左下に出ます。色の指定は\drawの時と同じです。{}の中は文字が入りますが、これはLaTeXの処理系なので、完全なLaTeXが使えます。これがTikZの最大の利点ですね。

  \foreach \height in {1,...,9} \draw[dashed] (0, \height) -- +(5, 0);

この行で図中の横の破線を引いています(長方形に重なってる部分は見えない)。TikZにはforeach命令があって、これで繰り返し処理を行うことができます。{1,...,9}で連番を生成し\heightに毎回値が設定させるので、それを後ろの\drawで使っています。dashedオプションで破線に出来ます。また+(5, 0)のように最初の座標に対して相対位置でパスを指定する事が出来ます。

  \draw[->, very thick, dashed, blue] (4.5, 2.5) -- +(1.2, 0) -- +(1.2, +2.9) -- +(0, +2.9);

->のオプションを渡すと矢印が引けます。<-だと逆向きになります。相対位置は開始点に対してなので注意が必要です。値が中途半端になっているのは図を見ると分かりますが、矢印が重なるのを避ける為です。この辺は手動で勘でやります。

ビルド方法

.latexmkrc
#!/usr/bin/env perl
#
$latex = 'uplatex %O -synctex=1 --file-line-error -interaction=nonstopmode %S';
$pdflatex = 'lualatex %O -synctex=1 --file-line-error -interaction=nonstopmode %S';
$biber = 'biber %O --bblencoding=utf8 -u -U --output_safechars %B';
$bibtex = 'upbibtex %O %B';
$makeindex = 'upmendex %O -o %D %S';
$dvipdf = 'dvipdfmx %O -o %D %S';
$dvips = 'dvips %O -z -f %S | convbkmk -u > %D';
$ps2pdf = 'ps2pdf %O %S %D';
$pdf_mode = 3;
if ($^O eq 'darwin') {
  $pvc_view_file_via_temporary = 0;
  $pdf_previewer = 'open -ga /Applications/Skim.app';
} else {
  $pdf_previewer = 'xdg-open';
}

私はLuaTeX派なので上のTikZのソースもlualatexでコンパイルします。上のように$HOME/.latexmkrcを指定しておけば

$ latexmk -pdf -f

でそのディレクトリにある*.texファイルを全部処理してくれます。CIでビルドする場合は比較的軽量で日本語も処理できるPaperist/docker-alpine-texlive-jaがおすすめです。

最後に

TikZは簡単に綺麗な図が書けて、foreachのようにプログラマブルであり、テキストなので管理が便利です!是非使っていきましょう(/・ω・)/