Pull to refresh

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

Reading time 4 min
Views 28K


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

Articles