Диаграммы в Latex библиотека TIKZ

Возможности библиотеки TIKZ для рисования графиков в Latex

Возможности библиотеки TIKZ для рисования графиков в Latex. Обзор учебника из документации.

Библиотеки для работы с диаграммами

\usetikzlibrary {
positioning,% нативные позиции node
shapes.misc, % настройка внешнего вида фигур, углы и т.д.
graphs, % работает с диаграммами и графами
calc, % считает координаты
arrows.meta % рисует наконечники стрел
}

Стилизация узлов диаграммы

Простой прямоугольник (Не-Терминал)

\usetikzlibrary {positioning}
\begin{tikzpicture}[
    nonterminal/.style={
      % The shape:
      rectangle,
      % The size:
      minimum size=6mm,
      % The border:
      very thick,
      draw=red!50!black!50,         % 50% red and 50% black,
                                    % and that mixed with 50% white
      % The filling:
      top color=white,              % a shading that is white at the top...
      bottom color=red!50!black!20, % and something else at the bottom
      % Font
      font=\itshape
    }]
  \node [nonterminal] {unsigned integer};
\end{tikzpicture}

В стиле определил:

  • rectangle
  • minimum size
  • border — красночерного цвета толстый бордюр
  • filling — градиент top и bottom
  • font

Для рисования просто пишу NODE и все готово st1

Стиль терминалов с круглыми углами

\usetikzlibrary {positioning}
\begin{tikzpicture}[node distance=5mm,
                    terminal/.style={
                      % The shape:
                      rectangle,minimum size=6mm,rounded corners=3mm,
                      % The rest
                      very thick,draw=black!50,
                      top color=white,bottom color=black!20,
                      font=\ttfamily}]
  \node (dot)   [terminal]                {.};
  \node (digit) [terminal,right=of dot]   {digit};
  \node (E)     [terminal,right=of digit] {E};
\end{tikzpicture}

Как приятно писать что-то, когда ты понимаешь, что ты это понимаешь)))

В стиле определено:

  • node distance — это значит,что расстояние между node будет, то, которое задано.
  • terminal — название стиля
  • rectangle — форма
  • minimum size
  • rounded corners — радиус закругления углов (закругляет у любой фигуры \node, \fill, \path)
  • very thick — толщина обводки
  • top, bottom — градиент заливки
  • font

Односимвольный терминал станет кругом, а многосимвольный — прямоугольником с закругленными углами.

st2

Использование библиотеки shapes.misc

Только немного изменится настройка в описании стиля

[node distance=5mm,
                    terminal/.style={
                      % The shape:
                      rounded rectangle,
                      minimum size=6mm,
                      % The rest
                      very thick,draw=black!50,
                      top color=white,bottom color=black!20,
                      font=\ttfamily}]

убрали rounded corners а поставили rounded rectangle — собственно и все. Но разметка слегка отъехала. На рисунке можно увидеть небольшую разницу.

Выравнивание текста в терминалах

Просто добавляем в стиль высоту и глубину строки [text height=1.5ex,text depth=.25ex]

Полезная библиотека позиционирования

\usetikzlibrary {positioning,shapes.misc}
\begin{tikzpicture}[node distance=5mm and 5mm]
  \node (ui1)   [nonterminal]                     {unsigned integer};
  \node (dot)   [terminal,right=of ui1]           {.};
  \node (digit) [terminal,right=of dot]           {digit};
  \node (E)     [terminal,right=of digit]         {E};
  \node (plus)  [terminal,above right=of E]       {+};
  \node (minus) [terminal,below right=of E]       {-};
  \node (ui2)   [nonterminal,below right=of plus] {unsigned integer};
\end{tikzpicture}

основные команды:

  • right=of
  • left=of
  • above=of
  • below=of
  • above right=of
  • и т.д.

Перенесем настройки стилей в преамбулу документа

\tikzset {terminal/.style={
                      % The shape:
                      rectangle,minimum size=6mm,rounded corners=3mm,
                      % The rest
                      very thick,draw=black!50,
                      top color=white,bottom color=black!20,
                      font=\ttfamily}}
\tikzset {nonterminal/.style={
      % The shape:
      rectangle,
      % The size:
      minimum size=6mm,
      % The border:
      very thick,
      draw=red!50!black!50,         % 50% red and 50% black,
                                    % and that mixed with 50% white
      % The filling:
      top color=white,              % a shading that is white at the top...
      bottom color=red!50!black!20, % and something else at the bottom
      % Font
      font=\itshape
    }}

Теперь будет действовать глобально на всех.

Рисуем стрелки

\usetikzlibrary {calc,positioning,shapes.misc}
\begin{tikzpicture}[node distance=5mm and 5mm,
    skip loop/.style={to path={-- ++(0,#1) -| (\tikztotarget)}}]
  \node (dot)   [terminal]                        {.};
  \node (digit) [terminal,right=of dot]           {digit};
  \node (E)     [terminal,right=of digit]         {E};

  \path (dot)   edge[->]                (digit)  % simple edges
        (digit) edge[->]                (E)
        ($ (digit.east)!.5!(E.west) $)
                edge[->,skip loop=-5mm] ($ (digit.west)!.5!(dot.east) $);
\end{tikzpicture}

Разберем кривую стрелку

($ (digit.east)!.5!(E.west) $)
                edge[->,skip loop=-5mm] ($ (digit.west)!.5!(dot.east) $);

т.е. от середины между метками (digit.east)!.5!(E.west) до середины между метками (digit.west)!.5!(dot.east) рисуем кривулину типа skip loop

st4

Пока все довольны.

Но разобрать по частям стиль skip loop очень хочется: skip loop/.style={to path={-- ++(0,#1) -| (\tikztotarget)}}

  • первая часть понятна: от текущей точке рисуем линию вертикально на заданный параметр #1, а потом
  • -| — рисует горизонтально, а потом вертикально к цели, это один из родственников -- — рисует прямую; |-— рисует вертикально и горизонтально и .. — рисует кривую
  • возвращаемся к поставленной цели (\tikztotarget) вообще таких макросов три (\tikztostart, \tikztotarget, and \tikztonodes;)

Матрицы

\usetikzlibrary {shapes.misc}
\begin{tikzpicture}
  \matrix[row sep=1mm,column sep=5mm] {
    % First row:
      & & & & \node [terminal] {+}; & \\
    % Second row:
    \node [nonterminal] {unsigned integer}; &
    \node [terminal]    {.};                &
    \node [terminal]    {digit};            &
    \node [terminal]    {E};                &
                                            &
    \node [nonterminal] {unsigned integer}; \\
    % Third row:
      & & & & \node [terminal] {-}; & \\
  };
\end{tikzpicture}

Пока \matrix представляет собой что-то вроде таблицы, со своими дополнениями.

  • row sep — расстояние между строками
  • column sep — расстояние между столбцами

Дальше обычная tabular.

st5

Промежуточная задача с узлами привязки

\usetikzlibrary {shapes.misc}
\begin{tikzpicture}[point/.style={circle,inner sep=0pt,minimum size=2pt,fill=red},
                   skip loop/.style={to path={-- ++(0,#1) -| (\tikztotarget)}}]
  \matrix[row sep=1mm,column sep=2mm] {
    % First row:
    & & & & & & &  & & & & \node (plus) [terminal] {+};\\
    % Second row:
    \node (p1) [point]  {}; &    \node (ui1)   [nonterminal] {unsigned integer}; &
    \node (p2) [point]  {}; &    \node (dot)   [terminal]    {.};                &
    \node (p3) [point]  {}; &    \node (digit) [terminal]    {digit};            &
    \node (p4) [point]  {}; &    \node (p5)    [point]  {};                      &
    \node (p6) [point]  {}; &    \node (e)     [terminal]    {E};                &
    \node (p7) [point]  {}; &                                                    &
    \node (p8) [point]  {}; &    \node (ui2)   [nonterminal] {unsigned integer}; &
    \node (p9) [point]  {}; &    \node (p10)   [point]       {};\\
    % Third row:
    & & & & & & &  & & & & \node (minus)[terminal] {-};\\
  };

  \path (p4) edge [->,skip loop=-5mm] (p3)
        (p2) edge [->,skip loop=5mm]  (p6);
\end{tikzpicture}
  1. Описываем стиль [point/.style={circle,inner sep=0pt,minimum size=2pt,fill=red}]
  2. Расставляем точки в матрице и даем им имена: \node (p1) [point] {};
  3. Соединяем точки edge (p4) edge [->,skip loop=-5mm] (p3)

Частично задача решена. Вторым этапом убираем видимость точек и результат готов.

Команда GRAPH (библиотека graphs)

Это еще одна мощная команда, которая должна со всем этим хозяйством управиться.

\begin{tikzpicture}[skip loop/.style={to path={-- ++(0,#1) -| (\tikztotarget)}},
                    point/.style={circle,inner sep=0pt,minimum size=2pt,fill=red},
                    hv path/.style={to path={-| (\tikztotarget)}},
                    vh path/.style={to path={|- (\tikztotarget)}}]
  \matrix[row sep=1mm,column sep=2mm] {
    % First row:
    & & & & & & &  & & & & \node (plus) [terminal] {+};\\
    % Second row:
    \node (p1) [point]  {}; &    \node (ui1)   [nonterminal] {unsigned integer}; &
    \node (p2) [point]  {}; &    \node (dot)   [terminal]    {.};                &
    \node (p3) [point]  {}; &    \node (digit) [terminal]    {digit};            &
    \node (p4) [point]  {}; &    \node (p5)    [point]  {};                      &
    \node (p6) [point]  {}; &    \node (e)     [terminal]    {E};                &
    \node (p7) [point]  {}; &                                                    &
    \node (p8) [point]  {}; &    \node (ui2)   [nonterminal] {unsigned integer}; &
    \node (p9) [point]  {}; &    \node (p10)   [point]       {};\\
    % Third row:
    & & & & & & &  & & & & \node (minus)[terminal] {-};\\
};

  \graph {
    (p1) -> (ui1) -- (p2) -> (dot) -- (p3) -> (digit) -- (p4)
         -- (p5)  -- (p6) -> (e) -- (p7) -- (p8) -> (ui2) -- (p9) -> (p10);
    (p4) ->[skip loop=-5mm]  (p3);
    (p2) ->[skip loop=5mm]   (p5);
    (p6) ->[skip loop=-11mm] (p9);
    (p7) ->[vh path]         (plus)  -> [hv path] (p8);
    (p7) ->[vh path]         (minus) -> [hv path] (p8);
  };
\end{tikzpicture}

st6

Завершаем оформление и добавляем стрелочки

Библиотека arrows.meta: и вариант работы p7 ->[vh path] { plus, minus } -> [hv path] p8; библиотеки graphs по раздвоению стрелок. Просто перечисляем узлы в фигурных скобках.

Итоговый вариант:

\begin{tikzpicture}[skip loop/.style={to path={-- ++(0,#1) -| (\tikztotarget)}},
                    point/.style={circle,inner sep=0pt,minimum size=2pt,fill=red},
                    >={Stealth[round]},thick,black!50,text=black,
                    every new ->/.style={shorten >=1pt},
                    graphs/every graph/.style={edges=rounded corners},
                    hv path/.style={to path={-| (\tikztotarget)}},
                    vh path/.style={to path={|- (\tikztotarget)}}]
  \matrix[column sep=4mm] {
    % First row:
    & & & & & & &  & & & & \node (plus) [terminal] {+};\\
    % Second row:
    \node (p1) [point]  {}; &    \node (ui1)   [nonterminal] {unsigned integer}; &
    \node (p2) [point]  {}; &    \node (dot)   [terminal]    {.};                &
    \node (p3) [point]  {}; &    \node (digit) [terminal]    {digit};            &
    \node (p4) [point]  {}; &    \node (p5)    [point]  {};                      &
    \node (p6) [point]  {}; &    \node (e)     [terminal]    {E};                &
    \node (p7) [point]  {}; &                                                    &
    \node (p8) [point]  {}; &    \node (ui2)   [nonterminal] {unsigned integer}; &
    \node (p9) [point]  {}; &    \node (p10)   [point]       {};\\
    % Third row:
    & & & & & & &  & & & & \node (minus)[terminal] {-};\\
};

  \graph [use existing nodes] {
    p1 -> ui1 -- p2 -> dot -- p3 -> digit -- p4 -- p5  -- p6 -> e -- p7 -- p8 -> ui2 -- p9 -> p10;
    p4 ->[skip loop=-5mm]  p3;
    p2 ->[skip loop=5mm]   p5;
    p6 ->[skip loop=-11mm] p9;
    p7 ->[vh path] { plus, minus } -> [hv path] p8;

};
\end{tikzpicture}

st8

Более серьезное погужение в GRAPHs

\tikz \graph [grow right=2cm] { unsigned integer -> d -> digit -> E };

выдаст сразу: st9

Добавим стилей:

\tikz \graph [grow right sep] {
  unsigned integer[nonterminal] -> "."[terminal] -> digit[terminal] -> E[terminal]
};

st10

Добавим + и -:

\usetikzlibrary {graphs,shapes.misc}
\tikz \graph [grow right sep] {
  unsigned integer  [nonterminal] ->
  "."               [terminal] ->
  digit             [terminal] ->
  E                 [terminal] ->
  {
    "+"             [terminal],
    ""              [coordinate],
    "-"             [terminal]
  } ->
  ui2/unsigned integer [nonterminal]
};

st11

Окончательный вариант через Graphs

\usetikzlibrary {arrows.meta,graphs,shapes.misc}
\tikz [>={Stealth[round]}, black!50, text=black, thick,
       every new ->/.style = {shorten >=1pt},
       graphs/every graph/.style = {edges=rounded corners},
       skip loop/.style = {to path={-- ++(0,#1) -| (\tikztotarget)}},
       hv path/.style = {to path={-| (\tikztotarget)}},
       vh path/.style = {to path={|- (\tikztotarget)}},
       nonterminal/.style = {
         rectangle, minimum size=6mm, very thick, draw=red!50!black!50, top color=white,
         bottom color=red!50!black!20, font=\itshape, text height=1.5ex,text depth=.25ex},
       terminal/.style = {
         rounded rectangle,  minimum size=6mm, very thick, draw=black!50, top color=white,
         bottom color=black!20, font=\ttfamily, text height=1.5ex, text depth=.25ex},
       shape = coordinate
       ]
  \graph [grow right sep, branch down=7mm, simple] {
    / -> unsigned integer[nonterminal] -- p1 -> "." [terminal] -- p2 -> digit[terminal] --
    p3 -- p4 -- p5 -> E[terminal] -- q1 ->[vh path]
    {[nodes={yshift=7mm}]
      "+"[terminal], q2, "-"[terminal]
    } -> [hv path]
    q3 -- /unsigned integer [nonterminal] -- p6 -> /;

    p1 ->[skip loop=5mm]   p4;
    p3 ->[skip loop=-5mm]  p2;
    p5 ->[skip loop=-11mm] p6;

    q1 -- q2 -- q3;  % make these edges plain
  };
  

st12

Особенности кода:

  • использовании групп, при делении веток, группы заключаем в {}
  • анонимные координаты обозначаются /
  • simpe — свойство graph — которое определяем, что между 2-мя узлами может быть только 1 edge.
  • graphs/every graph/.style = {edges=rounded corners} — закругленные уголки у стрелок
  • >={Stealth[round]}, black!50, text=black, thick, — стиль стрелок