Диаграммы и графы в LaTeX с использованием PGF/TikZ 3.0

98570f1a0346b879cff0cc52e7dd1b1c.pngНесколько месяцев назад вышел графический пакет для LaTeX PGF/TikZ 3.0, и в нём появилось немало интересных штук. В этой статье мы попробуем их применить для рисования простой блок-схемы. Нарисуем, например, кусочек известной схемы определения языка по письменности. Средства, уже рассмотренные в ранее опубликованной статье, трогать не будем, а поговорим об упрощённой нотации записи графов и управлением позиционированием узлов и ветвлением графа.

Упрощённая нотация графовСтандартными командами рисования узлов и линий в TikZ являются \node и \path, но код с ними получается довольно многословным и за забором из команд \node можно потерять саму диаграмму. В TikZ 3.0 появилась упрощённая нотация для графов, позаимствованная из известного пакета Graphviz и его языка DOT. В DOT-нотации простейший граф можно записать как последовательность текстовых меток и псевдострелочек, вроде a → b → c.

6b9b340d9bd6ad551ae6c3c0fd20be17.png

Начнём с преамбулы:

\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} Произвольный текст в метках aa6c556675326b55dd5c046217c1e8b9.pngЕсли в метке есть, например, дефис, или ещё что-нибудь более-менее сложное (математическая формула?), то нас без дополнительных подсказок не поймут, а вот если его закавычить, то всё будет ок:

\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} 28169d38542f531af7199e6b2492218e.png

Как видите, из узлов-вопросов провелись дуги к соответствующим узлам-ответам. За расположение узлов при ветвлении отвечает параметр 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} 746229cccde91a62be8b89b8a4927d4c.pngНо можно явно исключить листья из списка концов проводимых дуг, добавив опции 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

© Habrahabr.ru