TikZでフローチャートを書く
はじめに
Mermaidでフローチャートを書けるのですが、複雑になってくると配置に不満が出ます。
一方、draw.io Diagrams - Windows に無料でダウンロードしてインストールする | Microsoft Storeだと図を書くのに時間がかかります。
TikZでフローチャートを書く | Molina Tech Hubという素晴らしい記事を見つけたので、試してみました。TikZ初心者の私ですが、とりあえず希望通りの図が書けたということでメモです。
UbuntuでLuaLaTeXをセットアップ
LaTeXはいろいろ種類があるそうですが、[改訂第9版]LaTeX美文書作成入門 | Gihyo Digital Publishing … 技術評論社の電子書籍を見てLuaLaTeXを使うことにしました。
Linux/Linux Mint - TeX WikiのTeX Live/Debianの「日本語関連のパッケージのみインストールする場合」の手順でセットアップします。
sudo apt install texlive-lang-japanese texlive-latex-extra texlive-luatex
横道: jlreq文書クラスも入ってます
日本語 LaTeX の新常識 2021 #LaTeX - Qiitaによると日本語の文書クラスはabenori/jlreqを使うと良さそうです。
jlreq文書クラスはtexlive-lang-japaneseパッケージに含まれています。
$ dpkg -L texlive-lang-japanese | grep -F jlreq.cls
/usr/share/texlive/texmf-dist/tex/latex/jlreq/jlreq.cls
描いてみたフローチャート
ソース:RESPONSE-980-CORRELATION.tex
\documentclass[tikz, border=8pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric}
\usetikzlibrary {shapes.misc}
\usetikzlibrary{positioning}
\begin{document}
\begin{tikzpicture}
\tikzset{Terminal/.style={rounded rectangle, draw, text centered, text width=5cm, minimum height=1.5cm}};
\tikzset{Process/.style={rectangle, draw, text centered, text width=5cm, minimum height=1.5cm}};
\tikzset{Decision/.style={diamond, draw, text centered, aspect=6,text width=10cm, minimum height=1.5cm}};
\node[Terminal] (start) at (0,0){Start};
\node[Decision, below=1 of start.south](id_980041){REPORTING\_LEVEL == 0};
\draw[->, thick] (start) -- (id_980041);
\node[Decision, below=1 of id_980041.south](id_980042){REPORTING\_LEVEL >= 5};
\draw[->, thick] (id_980041.south) -- (id_980042) node[pos=0, anchor=north west]{No};
\node[Decision, below=1 of id_980042.south](id_980043){DETECTION\_ANOMALY\_SCORE == 0};
\draw[->, thick] (id_980042.south) -- (id_980043) node[pos=0, anchor=north west]{No};
\node[Decision, below=1 of id_980043.south](id_980044){BLOCKING\_INBOUND\_ANOMALY\_SCORE >= inbound\_anomaly\_score\_threshold};
\draw[->, thick] (id_980043.south) -- (id_980044) node[pos=0, anchor=north west]{No};
\node[Decision, below=1 of id_980044.south](id_980045){BLOCKING\_OUTBOUND\_ANOMALY\_SCORE >= outbound\_anomaly\_score\_threshold};
\draw[->, thick] (id_980044.south) -- (id_980045) node[pos=0, anchor=north west]{No};
\node[Decision, below=1 of id_980045.south](id_980046){REPORTING\_LEVEL < 2};
\draw[->, thick] (id_980045.south) -- (id_980046) node[pos=0, anchor=north west]{No};
\node[Decision, below=1 of id_980046.south](id_980047){DETECTION\_INBOUND\_ANOMALY\_SCORE >= inbound\_anomaly\_score\_threshold};
\draw[->, thick] (id_980046.south) -- (id_980047) node[pos=0, anchor=north west]{No};
\node[Decision, below=1 of id_980047.south](id_980048){DETECTION\_OUTBOUND\_ANOMALY\_SCORE >= outbound\_anomaly\_score\_threshold};
\draw[->, thick] (id_980047.south) -- (id_980048) node[pos=0, anchor=north west]{No};
\node[Decision, below=1 of id_980048.south](id_980049){REPORTING\_LEVEL < 3};
\draw[->, thick] (id_980048.south) -- (id_980049) node[pos=0, anchor=north west]{No};
\node[Decision, below=1 of id_980049.south](id_980050){BLOCKING\_ANOMALY\_SCORE > 0};
\draw[->, thick] (id_980049.south) -- (id_980050) node[pos=0, anchor=north west]{No};
\node[Decision, below=1 of id_980050.south](id_980051){REPORTING\_LEVEL < 4};
\draw[->, thick] (id_980050.south) -- (id_980051) node[pos=0, anchor=north west]{No};
\node[Terminal, below=1 of id_980051.south](log_reporting){LOG-REPORTING};
\draw[->, thick] (id_980051.south) -- (log_reporting) node[pos=0, anchor=north west]{No};
\draw[->, thick] (id_980042) -- ++(-10,0) node[pos=0, anchor=north east]{Yes} |- (log_reporting.west);
\draw[-, thick] (id_980044) -- ++(-10,0) node[pos=0, anchor=north east]{Yes};
\draw[-, thick] (id_980045) -- ++(-10,0) node[pos=0, anchor=north east]{Yes};
\draw[-, thick] (id_980047) -- ++(-10,0) node[pos=0, anchor=north east]{Yes};
\draw[-, thick] (id_980048) -- ++(-10,0) node[pos=0, anchor=north east]{Yes};
\draw[-, thick] (id_980050) -- ++(-10,0) node[pos=0, anchor=north east]{Yes};
\node[Process, below=1 of log_reporting.south](id_980170){msg: Anomaly Scores};
\draw[->, thick] (log_reporting) --(id_980170);
\node[Terminal, below=1 of id_980170.south](end_reporting){END-REPORTING};
\draw[->, thick] (id_980170) --(end_reporting);
\draw[->, thick] (id_980041) -- ++(10,0) node[pos=0, anchor=north west]{Yes} |- (end_reporting.east);
\draw[-, thick] (id_980043) -- ++(10,0) node[pos=0, anchor=north west]{Yes};
\draw[-, thick] (id_980046) -- ++(10,0) node[pos=0, anchor=north west]{Yes};
\draw[-, thick] (id_980049) -- ++(10,0) node[pos=0, anchor=north west]{Yes};
\draw[-, thick] (id_980051) -- ++(10,0) node[pos=0, anchor=north west]{Yes};
\end{tikzpicture}
\end{document}
PDFの生成手順
lualatex texファイルのベース名
横道: エラーが起きたときはXで終了
エラーの場合はエラーメッセージの後に?
というプロンプトが出ます。
?
Enterで説明が出ます。X
Enterで終了です。
出力例:
$ lualatex RESPONSE-980-CORRELATION
…(略)…
See the pgf package documentation for explanation.
Type H <return> for immediate help.
...
l.17 \draw[->, thick] (start) -- (id_980041x)
;
? H
This error message was generated by an \errmessage
command, so I can't give any explicit help.
Pretend that you're Hercule Poirot: Examine all clues,
and deduce the truth by order and method.
? ?
Type <return> to proceed, S to scroll future error messages,
R to run without stopping, Q to run quietly,
I to insert something, E to edit your file,
1 or ... or 9 to ignore the next 1 to 9 tokens of input,
H for help, X to quit.
? X
1402 words of node memory still in use:
22 hlist, 2 vlist, 1 rule, 2 disc, 2 local_par, 4 dir, 25 glue, 4 kern, 2 pe
nalty, 29 glyph, 67 attribute, 50 glue_spec, 67 attribute_list, 3 temp, 2 if_st
ack, 1 write, 38 pdf_literal, 2 pdf_colorstack nodes
avail lists: 2:9,3:1,4:2,5:12,10:1
warning (pdf backend): no pages of output.
Transcript written on RESPONSE-980-CORRELATION.log.
PDFからSVGの生成手順
pdftocairo -svg pdfファイル名
ファイル名の拡張子を.svgにしたファイルが生成されます。
「TikZの使い方」(2025-09-21 21:06 追記)
TikZの公式マニュアルはPGF/TikZ Manual - Complete Online Documentationです。 フッターのOfficial PDF versionでPDFもダウンロードできます。 このPDFは英語で書かれていて1323ページ(2025-09-21時点)と長大です。
TikZ - TeX Wikiからリンクされていた TeXについて | 壱大整域からダウンロードできる TikZの使い方というPDFは日本語で書かれていて111ページ(2025-09-21時点)です。
というわけで、まずはこちらをありがたく読むのが良さそうです。
余談
文書内の図として作るか図単体として作るか
TikZ - TeX Wikiの図画のみの出力では以下のような書き方を紹介していました。
\documentclass{jlreq}
\usepackage{tikz}
\pgfrealjobname{RESPONSE-980-CORRELATION}
…(略)…
\begin{document}
\beginpgfgraphicnamed{RESPONSE-980-CORRELATION-fig1}
\begin{tikzpicture}
…(略)…
\end{tikzpicture}
\endpgfgraphicnamed
\end{document}
RESPONSE-980-CORRELATION-fig1
だけコンパイルするには以下のようにします。
lualatex --jobname=RESPONSE-980-CORRELATION-fig1 RESPONSE-980-CORRELATION.tex
これ自体は期待通り動きました。一方で文書全体でコンパイルすると2ページになり、1ページ目はページ番号のみで、2ページ目はページからはみ出た状態になってしまいました。
そこで、Google Geminiに聞いて、文書クラスを以下のようにして図単体として作っています。
\documentclass[tikz, border=8pt]{standalone}
線を重ねて核と太くなるので重ねない
複数の菱形の判断(Decision)からlog_reportingへの線は当初は以下のように書いていました。
\draw[->, thick] (id_980042)node[below, xshift=-200]{Yes} -- ++(-10,0) |- (log_reporting.west);
\draw[->, thick] (id_980044)node[below, xshift=-250]{Yes} -- ++(-10,0) |- (log_reporting.west);
ですが、重なった箇所が太くなるので、以下のように2つ以降は交わるところまでだけを書くようにしました。
\draw[->, thick] (id_980042)node[below, xshift=-200]{Yes} -- ++(-10,0) |- (log_reporting.west);
\draw[-, thick] (id_980044)node[below, xshift=-250]{Yes} -- ++(-10,0);
判断から横に伸びる線は中心から描く
菱形の判断(Decision)は以下のような定義になっています。
\tikzset{Decision/.style={diamond, draw, text centered, aspect=6,text width=10cm, minimum height=1.5cm}};
text widthに固定の長さを指定していますが、文字数が増えると自動的にサイズが大きくなりました。
そのため線の引き出しを以下のように左端からにしてしまうと、線の先のX軸の値がそろわなくなってしまいます。
\draw[->, thick] (id_980042.west)node[below, xshift=-200]{Yes} -- ++(-10,0) |- (log_reporting.west);
\draw[-, thick] (id_980044.west)node[below, xshift=-250]{Yes} -- ++(-10,0);
そのため、.west
は省略して中心から引くようにしています。
判断からの線のYesラベルの配置はposとanchorを使うのが良い (2025-09-21 21:06更新)
前項のとおり、菱形の判断から横に伸びる線は判断の中心から引いているため、Yesのラベルを配置する際はxshift
に中心からの距離を指定する必要があります。
\draw[->, thick] (id_980042.west)node[below, xshift=-200]{Yes} -- ++(-10,0) |- (log_reporting.west);
\draw[-, thick] (id_980044.west)node[below, xshift=-250]{Yes} -- ++(-10,0);
実は単位がよくわかっていないのですが、試行錯誤して希望の配置になるような数値を指定しています。
判断が文字数に応じてサイズが変わるので、それに合わせてxshiftも調整する必要があります。
ここはもう少し良い方法を調べたいところです。
(↑xshiftの単位を省略したときのデフォルトはptでした。)
上記のTikZの使い方を読んで、線の横に書くYesやNoのラベルの配置はposとanchorを使うのが良いことが分かりました。
\draw[->, thick] (id_980051.south) -- (log_reporting) node[pos=0, anchor=north west]{No};
\draw[->, thick] (id_980042) -- ++(-10,0) node[pos=0, anchor=north east]{Yes} |- (log_reporting.west);
上記の「ソース:RESPONSE-980-CORRELATION.tex」の箇所もこの方式で書き換えたもので更新済みです。