January 3, 2017

creating a picture with tikz

In the scientific context it is necessary to visualize the content of methodological approaches from time to time, e.g. for writing publications. A popular option for creating graphics is TikZ, a markup language for TeX. One problem is that pictures become complex after a short time in a way that they are very difficult to maintain. Since I am currently creating a graphic with TikZ, I would like to explain my procedure here briefly.

Sketch of the picture

Requirements

The figure above shows a sketch of the figure. The basic requirements are manageable:

  1. The approach consists of three different levels of abstraction, which should be visualized below each other.
  2. Each level of abstraction is subdivided into a left and a right part, whereby the right part requires considerably more space.
  3. Communication has to be indicated with the help of labeled arrows on the right parts of the abstraction levels.

LaTeX code

For the sake of simplicity, I use the beamer class in LaTeX. Thus, the following preamble of the document is given:

\documentclass{beamer}

% Load TikZ package
\usepackage{tikz}

% Load TikZ libraries
\usetikzlibrary{arrows,calc}

% Load fp package
\usepackage{fp}

\begin{document}
	...
\end{document}

First the TikZ package with additional libraries is loaded. The TikZ library arrows defines aditional arrow tips. The TikZ library calc allows calculations for coordinates. In addition the fp package is used, which allows arithmetic operations of fixed point real numbers.

I use help lines to get a better understanding of the proportions of the image:

% Help lines
\newif\ifhelplines
	% show/hide help lines
	\helplinestrue
	% \helplinesfalse

In order to simply switch the lines on and off (here help lines are on), a condition is introduced first. The content area begins with various variable declarations and calculations:

% Variables
\FPeval{\aplusb}           {10} % centimeter
\FPeval{\nodeMarginPercent}{0.025}
\FPeval{\bend}             {0}

% Calculated values
\FPeval{\a}                {(root(2,5)-1)/2*\aplusb}
\FPeval{\b}                {(3-root(2,5))/2*\aplusb}

\FPeval{\nodeMargin}       {\aplusb*\nodeMarginPercent}
\FPeval{\aNodeWidth}       {\a-(2*\nodeMargin)}
\FPeval{\bNodeWidth}       {\b-(2*\nodeMargin)}
\FPeval{\levelHeight}      {\a/3}
\FPeval{\levelNodeHeight}  {\levelHeight-(2*\nodeMargin)}

\FPeval{} of the fp package is used to define or calculate different variables. Since I am using the Golden ratio to subdivide the left and right part of each abstraction level, the main width of the picture is defined as \aplusb. Each part of each level has its own region in the picture, in which TikZ nodes are placed at the center. \nodeMarginPercent defines the distance of the node to the edges of each region depending on \aplusb. \bend is used for bending the arrows (here it is set to 0 since I don’t want the arrows to bend).

\a and \b are used as variable identifiers similar to the ones of the Wikipedia page, \a is calculated as the height of the picture, \b is the shorter part of the Golden ratio. The remaining variable identifiers should be self-explanatory.

After defining a frame and after the start of the TikZ environment the different TikZ classes are defined:

[
>=stealth',
helpLine/.style={gray,dotted},
leftNode/.style={draw,minimum width=\bNodeWidth cm,minimum height=\levelNodeHeight cm},
rightNode/.style={draw,minimum width=\aNodeWidth cm,minimum height=\levelNodeHeight cm}
]

leftNode uses the defined variables \bNodeWidth and \levelNodeHeight to describe the nodes used in the left parts of each level of abstraction, rightNode works similarly. Next things is to define/calculate coordinates to draw the actual picture:

\coordinate (start) at (0,0);

\coordinate (firstLeftUpper) at (start);
\coordinate (firstLeftLower) at ($(firstLeftUpper)+(0,-\levelHeight)$);
\coordinate (firstMiddleUpper) at ($(firstLeftUpper)+(\b,0)$);
\coordinate (firstMiddleLower) at ($(firstMiddleUpper)+(0,-\levelHeight)$);
\coordinate (firstRightUpper) at ($(firstMiddleUpper)+(\a,0)$);
\coordinate (firstRightLower) at ($(firstRightUpper)+(0,-\levelHeight)$);

\coordinate (firstLeftMidpoint) at ($(firstLeftUpper)+(\b/2,-\levelHeight/2)$);
\coordinate (firstRightMidpoint) at ($(firstMiddleUpper)+(\a/2,-\levelHeight/2)$);

\coordinate (firstRightNodeLeftLowerPort) at ($(firstMiddleUpper)+(\nodeMargin,-\nodeMargin)+(\aNodeWidth/3,-\levelNodeHeight)$);
\coordinate (firstRightNodeRightLowerPort) at ($(firstRightUpper)-(\nodeMargin,\nodeMargin)-(\aNodeWidth/3,\levelNodeHeight)$);

I am using the coordinate (0,0) as the start of the picture, all other coordinates are relative to this one and calculated using the TikZ library calc. first*Upper and first*Lower are the coordinates bounding the two parts. They are used to calculate the dimensions of each TikZ node or rather to draw the help lines. first*Midpoint is calculated for using as the center of the TikZ nodes. firstRightNode*LowerPort is used as the ports for connecting the arrows. These coordinates are used for the first level of abstraction, the second and third level of abstraction work similarly.

The next thing is to actually draw something:

% first section
\ifhelplines
	\draw[helpLine] (firstLeftUpper) rectangle (firstMiddleLower);
	\draw[helpLine] (firstMiddleUpper) rectangle (firstRightLower);
\fi
\node[leftNode] (firstLeftNode) at (firstLeftMidpoint) {First Left Node};
\node[rightNode] (firstRightNode) at (firstRightMidpoint) {First Right Node};

If the condition for drawing help lines is set to true, the help lines highlight the left and the right part of the abstraction level. After that, a TikZ node is drawn within each part of the abstraction level.

Last thing is to draw the arrows:

\path[->] (firstRightNodeLeftLowerPort) edge[bend right=\bend] node[left] {\scriptsize From first to second} (secondRightNodeLeftUpperPort)
          (secondRightNodeLeftLowerPort) edge[bend right=\bend] node[left] {\scriptsize From second to third} (thirdRightNodeLeftUpperPort)
          (thirdRightNodeRightUpperPort) edge[bend right=\bend] node[right] {\scriptsize From third to second} (secondRightNodeRightLowerPort)
          (secondRightNodeRightUpperPort) edge[bend right=\bend] node[right] {\scriptsize From second to first} (firstRightNodeRightLowerPort);

If \bend is set to a value greater than zero, the arrows will be bended. The font size is set to \scriptsize since the distance between the different nodes is to small for normal font size.

The resulting picture (with help lines) will look like the following:

The result (with help lines)

Changes to the dimensions are likely to become necessary as soon as the actual content is added. However, the changes should be simple. What do you think of my approach? What would you do differently?

Download

The complete file can be downloaded here.

© 2018 Henrik Peters (Imprint, Privacy policy), Powered by the Hemingway theme for Hugo.