Несколько месяцев назад вышел графический пакет для 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
Автор: dbarashev