diff options
Diffstat (limited to 'graphics/pgf/base/doc/pgfmanual-en-tutorial-chains.tex')
-rw-r--r-- | graphics/pgf/base/doc/pgfmanual-en-tutorial-chains.tex | 1067 |
1 files changed, 1067 insertions, 0 deletions
diff --git a/graphics/pgf/base/doc/pgfmanual-en-tutorial-chains.tex b/graphics/pgf/base/doc/pgfmanual-en-tutorial-chains.tex new file mode 100644 index 0000000000..04b6f58433 --- /dev/null +++ b/graphics/pgf/base/doc/pgfmanual-en-tutorial-chains.tex @@ -0,0 +1,1067 @@ +% Copyright 2019 by Till Tantau +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% 2. under the GNU Free Documentation License. +% +% See the file doc/generic/pgf/licenses/LICENSE for more details. + + +\section{Tutorial: Diagrams as Simple Graphs} + +In this tutorial we have a look at how graphs and matrices can be used to +typeset a diagram. + +Ilka, who just got tenure for her professorship on Old and Lovable Programming +Languages, has recently dug up a technical report entitled \emph{The +Programming Language Pascal} in the dusty cellar of the library of her +university. Having been created in the good old times using pens and rules, it +looks like this% +\footnote{The shown diagram was not scanned, but rather typeset using +\tikzname. The jittering lines were created using the |random steps| +decoration.}: + +{ + \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 + }, + 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}, + skip loop/.style={to path={-- ++(0,#1) -| (\tikztotarget)}} + } + \tikzset{terminal/.append style={text height=1.5ex,text depth=.25ex}} + \tikzset{nonterminal/.append style={text height=1.5ex,text depth=.25ex}} + \pgfmathsetseed{1} +\medskip +\noindent\begin{tikzpicture}[ + >=latex,thick, + /pgf/every decoration/.style={/tikz/sharp corners}, + fuzzy/.style={decorate,decoration={random steps,segment length=0.5mm,amplitude=0.15pt}}, + minimum size=6mm,line join=round,line cap=round, + terminal/.style={rectangle,draw,fill=white,fuzzy,rounded corners=3mm}, + nonterminal/.style={rectangle,draw,fill=white,fuzzy}, + node distance=4mm] + + \ttfamily + \begin{scope}[start chain, + every node/.style={on chain}, + terminal/.append style={join=by {->,shorten >=-1pt,fuzzy,decoration={post length=4pt}}}, + nonterminal/.append style={join=by {->,shorten >=-1pt,fuzzy,decoration={post length=4pt}}}, + support/.style={coordinate,join=by fuzzy}] + \node [support] (start) {}; + \node [nonterminal] {unsigned integer}; + \node [support] (after ui) {}; + \node [terminal] {.}; + \node [support] (after dot) {}; + \node [terminal] {digit}; + \node [support] (after digit) {}; + \node [support] (skip) {}; + \node [support] (before E) {}; + \node [terminal] {E}; + \node [support] (after E) {}; + \node [support,xshift=5mm] (between) {}; + \node [support,xshift=5mm] (before last) {}; + \node [nonterminal] {unsigned integer}; + \node [support] (after last) {}; + \node [coordinate,join=by ->] (end) {}; + \end{scope} + \node (plus) [terminal,above=of between] {+}; + \node (minus) [terminal,below=of between] {-}; + + \begin{scope}[->,decoration={post length=4pt},rounded corners=2mm,every path/.style=fuzzy] + \draw (after ui) -- +(0,.7) -| (skip); + \draw (after digit) -- +(0,-.7) -| (after dot); + \draw (before E) -- +(0,-1.2) -| (after last); + \draw (after E) |- (plus); + \draw (plus) -| (before last); + \draw (after E) |- (minus); + \draw (minus) -| (before last); + \end{scope} +\end{tikzpicture} +\medskip + +For her next lecture, Ilka decides to redo this diagram, but this time perhaps +a bit cleaner and perhaps also bit ``cooler''. + +\medskip +\noindent\begin{tikzpicture}[point/.style={coordinate},>={Stealth[round]},thick,draw=black!50, + tip/.style={->,shorten >=1pt},every join/.style={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] {-};\\ + }; + + { [start chain] + \chainin (p1); + \chainin (ui1) [join=by tip]; + \chainin (p2) [join]; + \chainin (dot) [join=by tip]; + \chainin (p3) [join]; + \chainin (digit) [join=by tip]; + \chainin (p4) [join]; + { [start branch=digit loop] + \chainin (p3) [join=by {skip loop=-6mm,tip}]; + } + \chainin (p5) [join,join=with p2 by {skip loop=6mm,tip}]; + \chainin (p6) [join]; + \chainin (e) [join=by tip]; + \chainin (p7) [join]; + { [start branch=plus] + \chainin (plus) [join=by {vh path,tip}]; + \chainin (p8) [join=by {hv path,tip}]; + } + { [start branch=minus] + \chainin (minus) [join=by {vh path,tip}]; + \chainin (p8) [join=by {hv path,tip}]; + } + \chainin (p8) [join]; + \chainin (ui2) [join=by tip]; + \chainin (p9) [join,join=with p6 by {skip loop=-11mm,tip}]; + \chainin (p10) [join=by tip]; + } +\end{tikzpicture} +}\medskip + +Having read the previous tutorials, Ilka knows already how to set up the +environment for her diagram, namely using a |tikzpicture| environment. She +wonders which libraries she will need. She decides that she will postpone the +decision and add the necessary libraries as needed as she constructs the +picture. + + +\subsection{Styling the Nodes} + +The bulk of this tutorial will be about arranging the nodes and connecting them +using chains, but let us start with setting up styles for the nodes. + +There are two kinds of nodes in the diagram, namely what theoreticians like to +call \emph{terminals} and \emph{nonterminals}. For the terminals, Ilka decides +to use a black color, which visually shows that ``nothing needs to be done +about them''. The nonterminals, which still need to be ``processed'' further, +get a bit of red mixed in. + +Ilka starts with the simpler nonterminals, as there are no rounded corners +involved. Naturally, she sets up a style: +% +\begin{codeexample}[preamble={\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} +\end{codeexample} +% +Ilka is pretty proud of the use of the |minimum size| option: As the name +suggests, this option ensures that the node is at least 6mm by 6mm, but it will +expand in size as necessary to accommodate longer text. By giving this option +to all nodes, they will all have the same height of 6mm. + +Styling the terminals is a bit more difficult because of the round corners. +Ilka has several options how she can achieve them. One way is to use the +|rounded corners| option. It gets a dimension as parameter and causes all +corners to be replaced by little arcs with the given dimension as radius. By +setting the radius to 3mm, she will get exactly what she needs: circles, when +the shapes are, indeed, exactly 6mm by 6mm and otherwise half circles on the +sides: +% +\begin{codeexample}[preamble={\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} +\end{codeexample} + +Another possibility is to use a shape that is specially made for typesetting +rectangles with arcs on the sides (she has to use the |shapes.misc| library to +use it). This shape gives Ilka much more control over the appearance. For +instance, she could have an arc only on the left side, but she will not need +this. +% +\begin{codeexample}[preamble={\usetikzlibrary{positioning,shapes.misc}}] +\begin{tikzpicture}[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}] + \node (dot) [terminal] {.}; + \node (digit) [terminal,right=of dot] {digit}; + \node (E) [terminal,right=of digit] {E}; +\end{tikzpicture} +\end{codeexample} +% +At this point, she notices a problem. The baseline of the text in the nodes is +not aligned: +% +\begin{codeexample}[setup code,hidden] +\tikzset{ + 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}, +} +\end{codeexample} +% +\begin{codeexample}[preamble={\usetikzlibrary{calc,positioning,shapes.misc}}] +\begin{tikzpicture}[node distance=5mm] + \node (dot) [terminal] {.}; + \node (digit) [terminal,right=of dot] {digit}; + \node (E) [terminal,right=of digit] {E}; + + \draw [help lines] let \p1 = (dot.base), + \p2 = (digit.base), + \p3 = (E.base) + in (-.5,\y1) -- (3.5,\y1) + (-.5,\y2) -- (3.5,\y2) + (-.5,\y3) -- (3.5,\y3); +\end{tikzpicture} +\end{codeexample} +% +\noindent (Ilka has moved the style definition to the preamble by saying +|\tikzset{terminal/.style=...}|, so that she can use it in all pictures.) + +For the |digit| and the |E| the difference in the baselines is almost +imperceptible, but for the dot the problem is quite severe: It looks more like +a multiplication dot than a period. + +Ilka toys with the idea of using the |base right=of...| option rather than +|right=of...| to align the nodes in such a way that the baselines are all on +the same line (the |base right| option places a node right of something so that +the baseline is right of the baseline of the other object). However, this does +not have the desired effect: +% +\begin{codeexample}[preamble={\usetikzlibrary{positioning,shapes.misc}}] +\begin{tikzpicture}[node distance=5mm] + \node (dot) [terminal] {.}; + \node (digit) [terminal,base right=of dot] {digit}; + \node (E) [terminal,base right=of digit] {E}; +\end{tikzpicture} +\end{codeexample} +% +The nodes suddenly ``dance around''! There is no hope of changing the position +of text inside a node using anchors. Instead, Ilka must use a trick: The +problem of mismatching baselines is caused by the fact that |.| and |digit| and +|E| all have different heights and depth. If they all had the same, they would +all be positioned vertically in the same manner. So, all Ilka needs to do is to +use the |text height| and |text depth| options to explicitly specify a height +and depth for the nodes. +% +\begin{codeexample}[preamble={\usetikzlibrary{positioning,shapes.misc}}] +\begin{tikzpicture}[node distance=5mm, + text height=1.5ex,text depth=.25ex] + \node (dot) [terminal] {.}; + \node (digit) [terminal,right=of dot] {digit}; + \node (E) [terminal,right=of digit] {E}; +\end{tikzpicture} +\end{codeexample} + + +\subsection{Aligning the Nodes Using Positioning Options} + +Ilka now has the ``styling'' of the nodes ready. The next problem is to place +them in the right places. There are several ways to do this. The most +straightforward is to simply explicitly place the nodes at certain coordinates +``calculated by hand''. For very simple graphics this is perfectly alright, but +it has several disadvantages: +% +\begin{enumerate} + \item For more difficult graphics, the calculation may become + complicated. + \item Changing the text of the nodes may make it necessary to recalculate + the coordinates. + \item The source code of the graphic is not very clear since the + relationships between the positions of the nodes are not made + explicit. +\end{enumerate} + +For these reasons, Ilka decides to try out different ways of arranging the +nodes on the page. + +The first method is the use of \emph{positioning options}. To use them, you +need to load the |positioning| library. This gives you access to advanced +implementations of options like |above| or |left|, since you can now say +|above=of some node| in order to place a node above of |some node|, with the +borders separated by |node distance|. + +Ilka can use this to draw the place the nodes in a long row: +% +\begin{codeexample}[setup code,hidden] +\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, + }, +} +\tikzset{ + terminal/.append style={text height=1.5ex,text depth=.25ex}, + nonterminal/.append style={text height=1.5ex,text depth=.25ex}, +} +\end{codeexample} +% +\begin{codeexample}[preamble={\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} +\end{codeexample} + +For the plus and minus nodes, Ilka is a bit startled by their placements. +Shouldn't they be more to the right? The reason they are placed in that manner +is the following: The |north east| anchor of the |E| node lies at the ``upper +start of the right arc'', which, a bit unfortunately in this case, happens to +be the top of the node. Likewise, the |south west| anchor of the |+| node is +actually at its bottom and, indeed, the horizontal and vertical distances +between the top of the |E| node and the bottom of the |+| node are both 5mm. + +There are several ways of fixing this problem. The easiest way is to simply add +a little bit of horizontal shift by hand: +% +\begin{codeexample}[preamble={\usetikzlibrary{positioning,shapes.misc}}] +\begin{tikzpicture}[node distance=5mm and 5mm] + \node (E) [terminal] {E}; + \node (plus) [terminal,above right=of E,xshift=5mm] {+}; + \node (minus) [terminal,below right=of E,xshift=5mm] {-}; + \node (ui2) [nonterminal,below right=of plus,xshift=5mm] {unsigned integer}; +\end{tikzpicture} +\end{codeexample} + +A second way is to revert back to the idea of using a normal rectangle for the +terminals, but with rounded corners. Since corner rounding does not affect +anchors, she gets the following result: +% +\begin{codeexample}[preamble={\usetikzlibrary{positioning,shapes.misc}}] +\begin{tikzpicture}[node distance=5mm and 5mm,terminal/.append style={rectangle,rounded corners=3mm}] + \node (E) [terminal] {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} +\end{codeexample} +% +A third way is to use matrices, which we will do later. + +Now that the nodes have been placed, Ilka needs to add connections. Here, some +connections are more difficult than others. Consider for instance the +``repeat'' line around the |digit|. One way of describing this line is to say +``it starts a little to the right of |digit| than goes down and then goes to +the left and finally ends at a point a little to the left of |digit|''. Ilka +can put this into code as follows: +% +\begin{codeexample}[preamble={\usetikzlibrary{calc,positioning,shapes.misc}}] +\begin{tikzpicture}[node distance=5mm and 5mm] + \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); + + \draw [->] + % start right of digit.east, that is, at the point that is the + % linear combination of digit.east and the vector (2mm,0pt). We + % use the ($ ... $) notation for computing linear combinations + ($ (digit.east) + (2mm,0) $) + % Now go down + -- ++(0,-.5) + % And back to the left of digit.west + -| ($ (digit.west) - (2mm,0) $); +\end{tikzpicture} +\end{codeexample} + +Since Ilka needs this ``go up/down then horizontally and then up/down to a +target'' several times, it seems sensible to define a special \emph{to-path} +for this. Whenever the |edge| command is used, it simply adds the current value +of |to path| to the path. So, Ilka can set up a style that contains the correct +path: +% +\begin{codeexample}[preamble={\usetikzlibrary{calc,positioning,shapes.misc}}] +\begin{tikzpicture}[node distance=5mm and 5mm, + skip loop/.style={to path={-- ++(0,-.5) -| (\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) + (2mm,0) $) + edge[->,skip loop] ($ (digit.west) - (2mm,0) $); +\end{tikzpicture} +\end{codeexample} + +Ilka can even go a step further and make her |skip loop| style parameterized. +For this, the skip loop's vertical offset is passed as parameter |#1|. Also, in +the following code Ilka specifies the start and targets differently, namely as +the positions that are ``in the middle between the nodes''. +% +\begin{codeexample}[preamble={\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} +\end{codeexample} + + +\subsection{Aligning the Nodes Using Matrices} + +Ilka is still bothered a bit by the placement of the plus and minus nodes. +Somehow, having to add an explicit |xshift| seems too much like cheating. + +A perhaps better way of positioning the nodes is to use a \emph{matrix}. In +\tikzname\ matrices can be used to align quite arbitrary graphical objects in +rows and columns. The syntax is very similar to the use of arrays and tables in +\TeX\ (indeed, internally \TeX\ tables are used, but a lot of stuff is going on +additionally). + +In Ilka's graphic, there will be three rows: One row containing only the plus +node, one row containing the main nodes and one row containing only the minus +node. +% +\begin{codeexample}[preamble={\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} +\end{codeexample} +% +That was easy! By toying around with the row and columns separations, Ilka can +achieve all sorts of pleasing arrangements of the nodes. + +Ilka now faces the same connecting problem as before. This time, she has an +idea: She adds small nodes (they will be turned into coordinates later on and +be invisible) at all the places where she would like connections to start and +end. +% +\begin{codeexample}[preamble={\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} +\end{codeexample} +% +Now, it's only a small step to add all the missing edges. + + +\subsection{The Diagram as a Graph} + +Matrices allow Ilka to align the nodes nicely, but the connections are not +quite perfect. The problem is that the code does not really reflect the paths +that underlie the diagram. For this, it seems natural enough to Ilka to use the +|graphs| library since, after all, connecting nodes by edges is exactly what +happens in a graph. The |graphs| library can both be used to connect nodes that +have already been created, but it can also be used to create nodes ``on the +fly'' and these processes can also be mixed. + + +\subsubsection{Connecting Already Positioned Nodes} + +Ilka has already a fine method for positioning her nodes (using a |matrix|), so +all that she needs is an easy way of specifying the edges. For this, she uses +the |\graph| command (which is actually just a shorthand for |\path graph|). It +allows her to write down edges between them in a simple way (the macro +|\matrixcontent| contains exactly the matrix content from the previous example; +no need to repeat it here): +% +\begin{codeexample}[setup code,hidden] +\def\matrixcontent{ + % 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] {-};\\ +} +\end{codeexample} +% +\begin{codeexample}[ + preamble={\usetikzlibrary{graphs,shapes.misc}}, + pre={\tikzset{ampersand replacement=\&,point/.style={coordinate}}}, +] +\begin{tikzpicture}[skip loop/.style={to path={-- ++(0,#1) -| (\tikztotarget)}}, + hv path/.style={to path={-| (\tikztotarget)}}, + vh path/.style={to path={|- (\tikztotarget)}}] + \matrix[row sep=1mm,column sep=2mm] { \matrixcontent }; + + \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} +\end{codeexample} + +This is already pretty near to the desired result, just a few ``finishing +touches'' are needed to style the edges more nicely. + +However, Ilka does not have the feeling that the |graph| command is all that +hot in the example. It certainly does cut down on the number of characters she +has to write, but the overall graph structure is not that much clear -- it is +still mainly a list of paths through the graph. It would be nice to specify +that, say, there the path from |(p7)| sort of splits to |(plus)| and |(minus)| +and then merges once more at |(p8)|. Also, all these parentheses are bit hard +to type. + +It turns out that edges from a node to a whole group of nodes are quite easy to +specify, as shown in the next example. Additionally, by using the +|use existing nodes| option, Ilka can also leave out all the parentheses +(again, some options have been moved outside to keep the examples shorter): +% +\begin{codeexample}[ + preamble={\usetikzlibrary{arrows.meta,graphs,shapes.misc}}, + pre={\tikzset{ + ampersand replacement=\&, + point/.style={coordinate}, + skip loop/.style={to path={-- ++(0,##1) -| (\tikztotarget)}}, + hv path/.style={to path={-| (\tikztotarget)}}, + vh path/.style={to path={|- (\tikztotarget)}}, +}}, +] +\begin{tikzpicture}[>={Stealth[round]},thick,black!50,text=black, + every new ->/.style={shorten >=1pt}, + graphs/every graph/.style={edges=rounded corners}] + \matrix[column sep=4mm] { \matrixcontent }; + + \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} +\end{codeexample} + + +\subsubsection{Creating Nodes Using the Graph Command} + +Ilka has heard that the |graph| command is also supposed to make it easy to +create nodes, not only to connect them. This is, indeed, correct: When the +|use existing nodes| option is not used and when a node name is not surrounded +by parentheses, then \tikzname\ will actually create a node whose name and text +is the node name: +% +\begin{codeexample}[preamble={\usetikzlibrary{graphs}}] +\tikz \graph [grow right=2cm] { unsigned integer -> d -> digit -> E }; +\end{codeexample} +% +Not quite perfect, but we are getting somewhere. First, let us change the +positioning algorithm by saying |grow right sep|, which causes new nodes to be +placed to the right of the previous nodes with a certain fixed separation +(|1em| by default). Second, we add some options to make the node ``look nice''. +Third, note the funny |d| node above: Ilka tried writing just |.| there first, +but got some error messages. The reason is that a node cannot be called |.| in +\tikzname, so she had to choose a different name -- which is not good, since +she wants a dot to be shown! The trick is to put the dot in quotation marks, +this allows you to use ``quite arbitrary text'' as a node name: +% +\begin{codeexample}[preamble={\usetikzlibrary{graphs,shapes.misc}}] +\tikz \graph [grow right sep] { + unsigned integer[nonterminal] -> "."[terminal] -> digit[terminal] -> E[terminal] +}; +\end{codeexample} +% +Now comes the fork to the plus and minus signs. Here, Ilka can use the grouping +mechanism of the |graph| command to create a split: +% +\begin{codeexample}[preamble={\usetikzlibrary{graphs,shapes.misc}}] +\tikz \graph [grow right sep] { + unsigned integer [nonterminal] -> + "." [terminal] -> + digit [terminal] -> + E [terminal] -> + { + "+" [terminal], + "" [coordinate], + "-" [terminal] + } -> + ui2/unsigned integer [nonterminal] +}; +\end{codeexample} +% +Let us see, what is happening here. We want two |unsigned integer| nodes, but +if we just were to use this text twice, then \tikzname\ would have noticed that +the same name was used already in the current graph and, being smart (actually +too smart in this case), would have created an edge back to the already-created +node. Thus, a fresh name is needed here. However, Ilka also cannot just write +|unsigned integer2|, because she wants the original text to be shown, after +all! The trick is to use a slash inside the node name: In order to ``render'' +the node, the text following the slash is used instead of the node name, which +is the text before the slash. Alternatively, the |as| option can be used, which +also allows you to specify how a node should be rendered. + +It turns out that Ilka does not need to invent a name like |ui2| for a node +that she will not reference again anyway. In this case, she can just leave out +the name (write nothing before |/|), which always stands for a ``fresh, +anonymous'' node name. + +Next, Ilka needs to add some coordinates in between of some nodes where the +back-loops should got and she needs to shift the nodes a bit: +% +\begin{codeexample}[ + preamble={\usetikzlibrary{arrows.meta,graphs,shapes.misc}}, + pre={\tikzset{ + skip loop/.style={to path={-- ++(0,##1) -| (\tikztotarget)}}, + hv path/.style={to path={-| (\tikztotarget)}}, + vh path/.style={to path={|- (\tikztotarget)}}, +}}, +] +\begin{tikzpicture}[>={Stealth[round]}, thick, black!50, text=black, + every new ->/.style={shorten >=1pt}, + graphs/every graph/.style={edges=rounded corners}] + \graph [grow right sep, branch down=7mm] { + / [coordinate] -> + unsigned integer [nonterminal] -- + p1 [coordinate] -> + "." [terminal] -- + p2 [coordinate] -> + digit [terminal] -- + p3 [coordinate] -- + p4 [coordinate] -- + p5 [coordinate] -> + E [terminal] -- + q1 [coordinate] ->[vh path] + { [nodes={yshift=7mm}] + "+" [terminal], + q2/ [coordinate], + "-" [terminal] + } -> [hv path] + q3 [coordinate] -- + /unsigned integer [nonterminal] -- + p6 [coordinate] -> + / [coordinate]; + + p1 ->[skip loop=5mm] p4; + p3 ->[skip loop=-5mm] p2; + p5 ->[skip loop=-11mm] p6; + }; +\end{tikzpicture} +\end{codeexample} + +All that remains to be done is to somehow get rid of the strange curves between +the |E| and the unsigned integer. They are caused by \tikzname's attempt at +creating an edge that first goes vertical and then horizontal but is actually +just horizontal. Additionally, the edge should not really be pointed; but it +seems difficult to get rid of this since the \emph{other} edges from |q1|, +namely to |plus| and |minus| should be pointed. + +It turns out that there is a nice way of solving this problem: You can specify +that a graph is |simple|. This means that there can be at most one edge between +any two nodes. Now, if you specify an edge twice, the options of the second +specification ``win''. Thus, by adding two more lines that ``correct'' these +edges, we get the final diagram with its complete code: +% +\begin{codeexample}[preamble={\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 + }; +\end{codeexample} + + +%% TODOsp: a commented subsection +% \subsection{Using Chains} +% +% Matrices allow Ilka to align the nodes nicely, but the connections are +% not quite perfect. The problem is that the code does not really +% reflect the paths that underlie the diagram. +% +% +% For this reason, Ilka decides to try out \emph{chains} by including +% the |chain| library. Basically, a chain is just a sequence of +% (usually) connected nodes. The nodes can already have been constructed +% or they can be constructed as the chain is constructed (or these +% processes can be mixed). +% +% \subsubsection{Creating a Simple Chain} +% +% +% Ilka starts with creating a chain from scratch. For this, she starts a +% chain using the |start chain| option in a scope. Then, inside the +% scope, she uses the |on chain| option on nodes to add them to the +% chain. +% \begin{codeexample}[] +% \begin{tikzpicture}[start chain,node distance=5mm] +% \node [on chain,nonterminal] {unsigned integer}; +% \node [on chain,terminal] {.}; +% \node [on chain,terminal] {digit}; +% \node [on chain,terminal] {E}; +% \node [on chain,nonterminal] {unsigned integer}; +% \end{tikzpicture} +% \end{codeexample} +% (Ilka will add the plus and minus nodes later.) +% +% As can be seen, the nodes of a chain are placed in a row. This can be +% changed, for instance by saying |start chain=going below| we get a +% chain where each node is below the previous one. +% +% The next step is to \emph{join} the nodes of the chain. For this, we +% add the |join| option to each node. This joins the node with the +% previous node (for the first node nothing happens). +% \begin{codeexample}[] +% \begin{tikzpicture}[start chain,node distance=5mm] +% \node [on chain,join,nonterminal] {unsigned integer}; +% \node [on chain,join,terminal] {.}; +% \node [on chain,join,terminal] {digit}; +% \node [on chain,join,terminal] {E}; +% \node [on chain,join,nonterminal] {unsigned integer}; +% \end{tikzpicture} +% \end{codeexample} +% In order to get a arrow tip, we redefine the |every join| style. Also, +% we move the |join| and |on chain| options to the |every node| +% style so that we do not have to repeat them so often. +% \begin{codeexample}[] +% \begin{tikzpicture}[start chain,node distance=5mm, every node/.style={on chain,join}, every join/.style={->}] +% \node [nonterminal] {unsigned integer}; +% \node [terminal] {.}; +% \node [terminal] {digit}; +% \node [terminal] {E}; +% \node [nonterminal] {unsigned integer}; +% \end{tikzpicture} +% \end{codeexample} +% +% +% \subsubsection{Branching and Joining a Chain} +% +% It is now time to add the plus and minus signs. They obviously +% \emph{branch off} the main chain. For this reason, we start a branch +% for them using the |start branch| option. +% \begin{codeexample}[] +% \begin{tikzpicture}[start chain,node distance=5mm, every node/.style={on chain,join}, every join/.style={->}] +% \node [nonterminal] {unsigned integer}; +% \node [terminal] {.}; +% \node [terminal] {digit}; +% \node [terminal] {E}; +% \begin{scope}[start branch=plus] +% \node (plus) [terminal,on chain=going above right] {+}; +% \end{scope} +% \begin{scope}[start branch=minus] +% \node (minus) [terminal,on chain=going below right] {-}; +% \end{scope} +% \node [nonterminal,join=with plus,join=with minus] {unsigned integer}; +% \end{tikzpicture} +% \end{codeexample} +% +% Let us see, what is going on here. First, the |start branch| begins a +% branch, starting with the node last created on the current chain, +% which is the |E| node in our case. This is implicitly also the first +% node on this branch. A branch is nothing different from a chain, which +% is why the plus node is put on this branch using the |on chain| +% option. However, this time we specify the placement of the node +% explicitly using |going |\meta{direction}. This causes the plus sign +% to be placed above and right of the |E| node. It is automatically +% joined to its predecessor on the branch by the implicit |join| +% option. +% +% When the first branch ends, only the plus node has been added and the +% current chain is the original chain once more and we are back to the +% |E| node. Now we start a new branch for the minus node. After this +% branch, the current chain ends at |E| node once more. +% +% Finally, the rightmost unsigned integer is added to the (main) chain, +% which is why it is joined correctly with the |E| node. The two +% additional |join| options get a special |with| parameter. This allows +% you to join a node with a node other than the predecessor on the +% chain. The |with| should be followed by the name of a node. +% +% Since Ilka will need scopes more often in the following, she includes +% the |scopes| library. This allows her to replace |\begin{scope}| +% simply by an opening brace and |\end{scope}| by the corresponding +% closing brace. Also, in the following example we reference +% the nodes |plus| and |minus| using +% their automatic name: The $i$th node on a chain is called +% |chain-|\meta{i}. For a branch \meta{branch}, the $i$th node is called +% |chain/|\meta{branch}|-|\meta{i}. The \meta{i} can be replaced by +% |begin| and |end| to reference the first and (currently) last node on +% the chain. +% +% \begin{codeexample}[] +% \begin{tikzpicture}[start chain,node distance=5mm, every on chain/.style={join}, every join/.style={->}] +% \node [on chain,nonterminal] {unsigned integer}; +% \node [on chain,terminal] {.}; +% \node [on chain,terminal] {digit}; +% \node [on chain,terminal] {E}; +% { [start branch=plus] +% \node (plus) [terminal,on chain=going above right] {+}; +% } +% { [start branch=minus] +% \node (minus) [terminal,on chain=going below right] {-}; +% } +% \node [nonterminal,on chain,join=with chain/plus-end,join=with chain/minus-end] {unsigned integer}; +% \end{tikzpicture} +% \end{codeexample} +% +% +% The next step is to add intermediate coordinate nodes in the same +% manner as Ilka did for the matrix. For them, we change the |join| +% style slightly, namely for these nodes we do not want an arrow +% tip. This can be achieved either by (locally) changing the +% |every join| style or, which is what is done in the below example, by +% giving the desired style using |join=by ...|, where |...| is the style +% to be used for the join. +% +% \begin{codeexample}[] +% \begin{tikzpicture}[start chain,node distance=5mm and 2mm, +% every node/.style={on chain}, +% nonterminal/.append style={join=by ->}, +% terminal/.append style={join=by ->}, +% point/.style={join=by -,circle,fill=red,minimum size=2pt,inner sep=0pt}] +% \node [point] {}; \node [nonterminal] {unsigned integer}; +% \node [point] {}; \node [terminal] {.}; +% \node [point] {}; \node [terminal] {digit}; +% \node [point] {}; \node [point] {}; +% \node [point] {}; \node [terminal] {E}; +% \node [point] {}; +% { [node distance=5mm and 1cm] % local change in horizontal distance +% { [start branch=plus] +% \node (plus) [terminal,on chain=going above right] {+}; +% } +% { [start branch=minus] +% \node (minus) [terminal,on chain=going below right] {-}; +% } +% \node [point,below right=of plus,join=with chain/plus-end by ->,join=with chain/minus-end by ->] {}; +% } +% \node [nonterminal] {unsigned integer}; +% \node [point] {}; +% \end{tikzpicture} +% \end{codeexample} +% +% +% \subsubsection{Chaining Together Already Positioned Nodes} +% +% The final step is to add the missing arrows. We can also use branches +% for them (even though we do not have to, but it is good practice and +% they exhibit the structure of the diagram in the code). +% +% Let us start with the repeat loop around the |digit|. This can be +% thought of as a branch that starts at the point after the digit and +% that ends at the point before the digit. However, we have already +% constructed the point before the digit! In such cases, it is possible +% to ``chain in'' an already positioned node, using the |\chainin| +% command. This command must be followed by a coordinate that contains a +% node name and optionally some options. The effect is that the named +% node is made part of the current chain. +% +% \begin{codeexample}[pre={\tikzset{node distance=5mm and 2mm, +% every node/.style={on chain}, +% terminal/.append style={join=by ->}, +% point/.style={join=by -,circle,fill=red,minimum size=2pt,inner sep=0pt}}}] +% \begin{tikzpicture}[start chain] % plus some styles that are not shown +% \node [point] {}; +% \node (before digit) [point] {}; +% \node [terminal] {digit}; +% \node [point] {}; +% { [start branch=digit loop] +% \chainin (before digit) [join=by {->,skip loop=-5mm}]; +% } +% \node [point] {}; +% \end{tikzpicture} +% \end{codeexample} +% +% +% \subsubsection{Combined Use of Matrices and Chains} +% +% Ilka's final idea is to combine matrices and chains in the following +% manner: She will use a matrix to position the nodes. However, to show +% the logical ``flow structure'' inside the diagram, she will create +% chains and branches that show what is going on. +% +% Ilka starts with the matrix we had earlier, only with slightly adapted +% styles. Then she writes down the main chain and its branches: +% +% \begin{codeexample}[preamble={\usetikzlibrary{arrows.meta}}] +% \begin{tikzpicture}[point/.style={coordinate},>={Stealth[round]},thick,draw=black!50, +% tip/.style={->,shorten >=1pt},every join/.style={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] {-};\\ +% }; +% +% { [start chain] +% \chainin (p1); +% \chainin (ui1) [join=by tip]; +% \chainin (p2) [join]; +% \chainin (dot) [join=by tip]; +% \chainin (p3) [join]; +% \chainin (digit) [join=by tip]; +% \chainin (p4) [join]; +% { [start branch=digit loop] +% \chainin (p3) [join=by {skip loop=-6mm,tip}]; +% } +% \chainin (p5) [join,join=with p2 by {skip loop=6mm,tip}]; +% \chainin (p6) [join]; +% \chainin (e) [join=by tip]; +% \chainin (p7) [join]; +% { [start branch=plus] +% \chainin (plus) [join=by {vh path,tip}]; +% \chainin (p8) [join=by {hv path,tip}]; +% } +% { [start branch=minus] +% \chainin (minus) [join=by {vh path,tip}]; +% \chainin (p8) [join=by {hv path,tip}]; +% } +% \chainin (p8) [join]; +% \chainin (ui2) [join=by tip]; +% \chainin (p9) [join,join=with p6 by {skip loop=-11mm,tip}]; +% \chainin (p10) [join=by tip]; +% } +% \end{tikzpicture} +% \end{codeexample} |