Несколько месяцев назад вышел графический пакет для LaTeX PGF/TikZ 3.0, и в нём появилось немало интересных штук. В этой статье мы попробуем их применить для рисования простой блок-схемы. Нарисуем, например, кусочек известной схемы определения языка по письменности. Средства, уже рассмотренные в ранее опубликованной статье, трогать не будем, а поговорим об упрощённой нотации записи графов и управлением позиционированием узлов и ветвлением графа.
Упрощённая нотация графов
Стандартными командами рисования узлов и линий в TikZ являются
\node
и \path
, но код с ними получается довольно многословным и за забором из команд \node
можно потерять саму диаграмму. В TikZ 3.0 появилась упрощённая нотация для графов, позаимствованная из известного пакета Graphviz и его языка DOT. В DOT-нотации простейший граф можно записать как последовательность текстовых меток и псевдострелочек, вроде a -> b -> c
. Начнём с преамбулы:
\usepackage{tikz}
\usetikzlibrary{graphs}
И сделаем простенький граф:
\begin{tikzpicture}
\graph {
Диакритика? -> Да! -> Французский
};
\end{tikzpicture}
Команда
\graph
в своём аргументе принимает описание графа в DOT-нотации, и мы полагаем, что получим цепочку из трёх вершин. В действительности же не всё так просто: наши метки сбились в кучамалу (пункт 1 на картинке «Цепочка Вершин»)Позиционировать узлы графа можно вручную, и мы этим займемся в следующей части, но пока попробуем автоматическое позиционирование. Самое простое, что можно сделать, это подсказать TikZ'у в опциях команды
\graph
, куда должен расти граф и куда ветвиться. Давайте растить граф вправо, так чтоб центры узлов располагались на сетке с шагом три сантиметра (пункт 2): \begin{tikzpicture}
\graph[grow right=3cm] {
Диакритика? -> Да! -> Французский
};
\end{tikzpicture}
Можно указать расстояние не между центрами, а между соседними краями узлов (пункт 3):
\begin{tikzpicture}
\graph[grow right sep=2em] {
Диакритика? -> Да! -> Французский
};
\end{tikzpicture}
Граф можно растить в любом направлении. Стандартные направления right, left, up, down ортогональны осям координат, но можно растить и под углом (пункт 4):
\begin{tikzpicture}
\graph[chain shift=(-45:1)] {
Диакритика? -> Да! -> Французский
};
\end{tikzpicture}
Произвольный текст в метках
Если в метке есть, например, дефис, или ещё что-нибудь более-менее сложное (математическая формула?), то нас без дополнительных подсказок не поймут, а вот если его закавычить, то всё будет ок:
\begin{tikzpicture}
\graph[grow right sep=1em] {
Диакритика? -> Много над E:\\ \`{E}, \'{E}, \^{E}, \"{E} -> Французский
};
\end{tikzpicture}
\begin{tikzpicture}
\graph[grow right sep=1em] {
Диакритика? -> "Много над E:\\ \`{E}, \'{E}, \^{E}, \"{E}" -> Французский
};
\end{tikzpicture}
Ветвление графа
Давайте сделаем нашу схему посложнее и добавим ветвление. В DOT-нотации узлы можно объединить в группу при помощи фигурных скобок и к каждому из узлов группы провести дугу из узла-предка:
\begin{tikzpicture}
%% Опция nodes определяет параметры всех узлов графа. align=center центрирует текст в узле
\graph[nodes={align=center}, grow down sep, branch right sep] {
Диакритика? ->
{
"Много над E:\\ \`{E}, \'{E}, \^{E}, \"{E}" -> Французский,
Мало -> "Хоть что-то есть?" ->
{
"\c{C} и \"{E}" -> Точно не французский? ->
{
"Ой$\dots$он" -> Французский,
Вроде нет -> Албанский
},
"Только \"{A} и \"{O} \\ но мноогоо \\ сдвооенных" -> Финский
}
}
};
\end{tikzpicture}
Как видите, из узлов-вопросов провелись дуги к соответствующим узлам-ответам. За расположение узлов при ветвлении отвечает параметр
branch
, в нашем случае right sep
говорит что ветвление должно идти вправо, с одинаковым расстоянием между слоями. Он может принимать и другие значения, аналогично параметру grow
. Кстати, нам понадобилось указать выравнивание текста в узлах, без которого не сработали бы переносы строк в меткахНо у нас, похоже, проблема. Дуга из узла «Ой… он» к выводу «Французский» провелась, но пошла куда-то вверх. Нельзя ли сделать так, чтоб вывод «Французский» был ниже всех предшествующих ему вопросов и ответов? Если наивно поместить вывод «Французский» за всей группой после вопроса «Диакритика?» то дуги проведутся из всех листьев группы:
\begin{tikzpicture}
%% Добавим стиль рисования всех узлов
\graph[nodes={align=center,rectangle,draw=black}, grow down sep, branch right sep] {
Диакритика? ->
{
"Много над E:\\ \`{E}, \'{E}, \^{E}, \"{E}",
Мало -> "Хоть что-то есть?" ->
{
"\c{C} и \"{E}" -> Точно не французский? ->
{
"Ой$\dots$он",
Вроде нет -> Албанский
},
"Только \"{A} и \"{O} \\ но мноогоо \\ сдвооенных" -> Финский
}
} ->
Французский
};
\end{tikzpicture}
Но можно явно исключить листья из списка концов проводимых дуг, добавив опции
not source
и not target
. Их названия несколько противоречивы: так, чтобы указать, что из узла «Албанский» не должна идти дуга в узел «Французский» надо приписать к узлу «Албанский» опцию [not target]
\begin{tikzpicture}
\graph[nodes={align=center,rectangle,draw=black}, grow down sep, branch right sep] {
Диакритика? ->
{
"Много над E:\\ \`{E}, \'{E}, \^{E}, \"{E}",
Мало -> "Хоть что-то есть?" ->
{
"\c{C} и \"{E}" -> Точно не французский? ->
{
"Ой$\dots$он",
Вроде нет -> Албанский[not target]
},
"Только \"{A} и \"{O} \\ но мноогоо \\ сдвооенных" -> Финский[not target]
}
} ->
Французский
};
\end{tikzpicture}
Пожалуй, для первой части хватит, а в следующих частях можно будет рассмотреть другие стратегии позиционирования узлов и варианты использования DOT-нотации.
Ссылки:
[1] Исходный текст диаграмм из статьи и скомпилированный результат
[2] Пакет PGF/TikZ