I need to replicate this chart using LaTeX, I understand that TikZ should come in handy:
Can anyone please show me the best and easiest way to render the chart? Any ideas, tips, nudges in the right direction on how to do that are highly welcome.
chartsdiagramstikz-pgf
I need to replicate this chart using LaTeX, I understand that TikZ should come in handy:
Can anyone please show me the best and easiest way to render the chart? Any ideas, tips, nudges in the right direction on how to do that are highly welcome.
The boxes could be nodes, something like
\node (box1) at (0,2) [rectangle,draw=black,fill=blue!20!white] {Box 1};
Notice the (box1)
, thats the name of that node, you can use it for easily drawing arrows:
\draw[->] (box1.east) -- (box2.west);
For the arrows with kinks, you can use the ++(x,y)
coordinate notation, which means 'from the last position, go x right and y up, then make this the new position':
\draw[->] (box1.north) -- ++(0,1) -- ++(4,0) -- (box2.north);
Hope this helps getting started :)
Edit 1: You really learn TikZ by doing. I was wondering how to draw the double arrows entering the boxes on the south side. You could use the fact that you can specify any angle at which the arrows leave/enter:
\draw[->] (box1.300) -- ++(0,-0.5) -- ++(4,0) -- (box2.240);
\draw[->] (box1.240) -- ++(0,-1.0) -- ++(4,0) -- (box2.300);
However, the distance between entry and exit point is no longer 4. So it would be nice to specify 1cm below box2.240 which you can do with the calc
library:
\coordinate (A) at ($ (box2.240) + (0,-0.5) $);
\coordinate (B) at ($ (box2.300) + (0,-1.0) $);
\draw[->] (box1.300) -- ++(0,-0.5) -- (A) -- (box2.240);
\draw[->] (box1.240) -- ++(0,-1.0) -- (B) -- (box2.300);
This should cover most things needed for your chart. Here a little example and a picture:
\documentclass[parskip]{scrartcl}
\usepackage[margin=15mm]{geometry}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
\node (box1) at (0,0) [rectangle,draw=black,fill=blue!20!white] {Box 1};
\node (box2) at (4,0) [rectangle,draw=black,fill=blue!20!white] {Box 2};
\draw[->] (box1.east) -- (box2.west);
\draw[->] (box1.north) -- ++(0,1) -- ++(4,0) -- (box2.north);
\coordinate (A) at ($ (box2.240) + (0,-0.5) $);
\coordinate (B) at ($ (box2.300) + (0,-1.0) $);
\draw[->] (box1.300) -- ++(0,-0.5) -- (A) -- (box2.240);
\draw[->] (box1.240) -- ++(0,-1.0) -- (B) -- (box2.300);
\end{tikzpicture}
\end{document}
Edit 2: A nice, calc
free version by percusse:
\begin{tikzpicture}
\node (box1) at (0,0) [draw,fill=blue!20!white] {Box 1};
\node (box2) at (4,0) [draw,fill=blue!20!white] {Box 2};
\draw[->] (box1) -- (box2);
\draw[->] (box1.north) -- ++(0,1) -| (box2.north);
\draw[->] (box1.300) -- ++(0,-0.5) -| (box2.240);
\draw[->] (box1.240) -- ++(0,-1.0) -| (box2.300);
\end{tikzpicture}
Edit 3: Regarding the request for tip aligned arrows: I could not (right now) think of anything elegent, so I used absolute coordinates and the left
option, e.g. draw the node left of the coordinates, e.g. ending at the specified coordinates:
\documentclass[ngerman]{scrartcl}
\usepackage{ucs}
\usepackage[utf8x]{inputenc}
\usepackage{tikz}
\usetikzlibrary{shapes,arrows,positioning,calc}
\begin{document}
\tikzstyle{block} = [rectangle, fill=blue!20, minimum height=3em, minimum width=6em] \tikzstyle{arrow} = [single arrow, draw]
\begin{tikzpicture}[auto, node distance=0.5cm and 0.5cm, arr/.style={->,thick}, line/.style={thick}, font=\footnotesize]
\node (stoffVor) [block] {Box 1 text};
\node (haupt) [block, right=of stoffVor, align=center] {Box 2 text};
\node (stoffNach) [block, right=of haupt] {Box 3 text};
\node (pfeil1) at (-2,-1.5) [arrow,left] {Arrow 1};
\node (pfeil2) at (-2,-2.5) [arrow,left] {Arrow 2 text};
\node (pfeil0) at (-2,0) [arrow,left] {Arrow 3 longer text};
\node (neben) at (9.5,-0.5) [arrow,left,label=below:] {Text};
\node (hauptP) at (9.5,0.5) [arrow,left,label=above:] {Text};
\node (pfeil3) at (9.5,1.5) [arrow,left] {Text};
\node (pfeil4) at (9.5,2.5) [arrow,left] {Lorem, Ipsum, Dolor, Sit};
\draw[arr] (pfeil0.east) -- (stoffVor.west);
\draw[arr] (stoffVor.east) -- (haupt.west);
\draw[arr] (haupt.east) -- (stoffNach.west);
\draw[arr] (stoffNach.north) -- ++(0,0.5) node [auto, swap, yshift=6] {Text} -| ($ (stoffVor.east) + (0.25,0) $);
\draw[arr] (pfeil1.east) -| (stoffVor.240);
\draw[arr] (pfeil1.east) -| (haupt.240);
\draw[arr] (pfeil1.east) -| (stoffNach.240);
\draw[arr] (pfeil2.east) -| (stoffVor.300);
\draw[arr] (pfeil2.east) -| (haupt.300);
\draw[arr] (pfeil2.east) -| (stoffNach.300);
\draw[line] (haupt.150) |- (pfeil4.west);
\draw[line] (haupt.30) |- (pfeil3.west);
\draw[line] (stoffNach.350) -- ++ (0.25,0) -- ++ (0,-0.1) |- (neben.west);
\draw[line] (stoffNach.10) -- ++ (0.25,0) -- ++ (0, +0.1) |- (hauptP.west);
\end{tikzpicture}
\end{document}
Here's a way of plotting this using PGFPlots. You can collect the expression for the red curve while you're looping over the individual components using an \xdef
.
Unfortunately, PGFPlots can't use a perspective projection (and even in plain TikZ I think you'll have to jump through a lot of hoops to simulate it).
\documentclass[border=5mm]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.8}
\begin{document}
\begin{tikzpicture}
\begin{axis}[
set layers=standard,
domain=0:10,
samples y=1,
view={40}{20},
hide axis,
unit vector ratio*=1 2 1,
xtick=\empty, ytick=\empty, ztick=\empty,
clip=false
]
\def\sumcurve{0}
\pgfplotsinvokeforeach{0.5,1.5,...,5.5}{
\draw [on layer=background, gray!20] (axis cs:0,#1,0) -- (axis cs:10,#1,0);
\addplot3 [on layer=main, blue!30, smooth, samples=101] (x,#1,{sin(#1*x*(157))/(#1*2)});
\addplot3 [on layer=axis foreground, very thick, blue,ycomb, samples=2] (10.5,#1,{1/(#1*2)});
\xdef\sumcurve{\sumcurve + sin(#1*x*(157))/(#1*2)}
}
\addplot3 [red, samples=200] (x,0,{\sumcurve});
\draw [on layer=axis foreground] (axis cs:0,0,0) -- (axis cs:10,0,0);
\draw (axis cs:10.5,0.25,0) -- (axis cs:10.5,5.5,0);
\end{axis}
\end{tikzpicture}
\end{document}
Best Answer
This is also doable just with the
calc
library and nothing else.At first we should locate the various
livelli
(italian plural oflivello
): nodes are just fine. Let's then define their aspect:Since they have very similar labels (just the layer number changes), I think the best choice here is to locate them by means a
foreach
loop; but... how to locate nodes sequentially? This is a possible way:Indeed, in such a way each time
\i
increments, also the y coordinate of the layer is incremented as well as the "counter" of its name and label. Each node is placed at a given vertical distance thanks to the syntax0+\i*1cm
: change1cm
to increase or reduce the space between the layers.For the other stack it is just needed to change x coordinate and the name (
layer-2-\i
rather thanlayer-1-\i
):Now the large block on the bottom. Well, to have it of the exact size we can use a function able to compute it:
This, applied to a couple of our layers gives the
\distance
:The large block now should be located:
Its position can be computed to be placed exactly in the middle of the lowest level layers: this result should be shifted down to avoid covering layer
livello
1.Once finished with the layers, the arrows. We can define a style for the type:
and now we're ready to add them to the picture.
Without considering for the moment the large module, for the others there's a similar behaviour: the arrow starts from the
north
anchor of the lower layer up to thesouth
anchor of the next layer. So basically we need to be able to reference two counters: this is again a job for theforeach
.The evaluation allows indeed not only to identify the layers via their name, but it is also helpful for the label
interfaccia livello \i/\nexti
, put as a node at the middle (midway
) left (anchor=east,left=0.15cm
) of the arrow. The font size is a bit reduced to make the picture look better: thefont
key is of course used.Same thing for the other column (just change the column name and the position of the arrow labels from left to right):
Let's now take into account the
protocollo livello
connection. It's basically similar to what did before, without having to evaluate anything:The
above=-0.05cm
is used to have the label a bit more near the arrow.The picture is almost done: just a couple of things should be added. The first one is the connections between the two layers 1 and the large module; by computing the intersection via this is very simple:
Since they are just two arrows I won't use a
foreach
for this purpose. Even for thehost
label, I would use simple nodes:Here the font size has been made bigger always via the
font
key.And now our picture is really finished therefore here it is the whole code:
The picture is perfectly scalable: it means that using
its dimensions are correctly set to be half of the previous one. This is because, even if some fixed units have been used (
1cm
for the block vertical distance,above=-0.05cm
for protocol labels andabove=0.15cm
for host labels), their positioning, actually, is always defined in terms of other nodes or in the midway of a path.To introduce the possibility to easy customize the vertical block distance, one might think about introducing a new key (in the preamble):
Then,
\vertdist
should be applied to:In such a way, using:
the block distance will be doubled with respect to the current setting.