2

I am trying to draw a diagram below using Tikz or mathcha. As for Tikz I do not know how to position a node relative to other two or more ndoes. As for mathcha, I do not know how to connect a line with a text or math box. enter image description here

3
  • You CAN know after studying the excellent tutorials in part I of the pgfmanual.
    – MS-SPO
    Jun 7 at 6:49
  • Positioning a node relative to other two nodes can be tricky but it depends on the specific case. If all the nodes are as uniform as your example, you might just position them using hard-coded coordinates. Maybe a matrix (or a tikzcd) would be the right tool here. The graphdrawing library with a layered layout could be of use here, too. Jun 7 at 7:35
  • There is probably not a "best" way to solve this. As you can see from the answers below, there are a lot of different approaches to this. Jun 7 at 13:24

2 Answers 2

13

Let's look into this step by step in a very simple way. First we may want to come up with a custom style for our nodes, since this makes it easier to style all nodes the same way. We will first position the nodes and only later connect them.

\documentclass[border=10pt]{standalone}
% we need to load Ti*k*Z obviously
\usepackage{tikz}

% we define a custom style `square` which we can apply to all the nodes
\tikzset{
    square/.style={
        % draw a border
        draw,
        % make the node a square
        minimum width=2em,
        minimum height=2em,
    }
}

\begin{document}
\begin{tikzpicture} 

% inside the `tikzpicture` environment, we place a node using our `square` style 
% we name this node `A` in order to be able to refer to it later
\node[square] at (0,0) (A) {1};

\end{tikzpicture}
\end{document}

enter image description here

TikZ comes with a lot of different libraries which are described in detail in its very voluminous but also very helpful manual. We can make use of the positioning library for relative positioning of nodes. First, let us place a node 1 cm to the right of the first one:

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning}

\tikzset{
    square/.style={
        draw,
        minimum width=2em,
        minimum height=2em,
    }
}

\begin{document}
\begin{tikzpicture} 

\node[square] at (0,0) (A) {1};

\node[square, right=1cm of A] (B) {2};

\end{tikzpicture}
\end{document}

enter image description here

Now, having these two we can use the calc library to place another node below the coordinate that sits halfway between these nodes. The calc library provides a powerful syntax that might look a bit awkward first: $(A)!0.5!(B)$ means "the coordinate that is halfway between (A) and (B)".

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning, calc}

\tikzset{
    square/.style={
        draw,
        minimum width=2em,
        minimum height=2em,
    }
}

\begin{document}
\begin{tikzpicture} 

\node[square] at (0,0) (A) {1};
\node[square, right=1cm of A] (B) {2};

\node[square, below=1cm of $(A)!0.5!(B)$] (C) {3};

\end{tikzpicture}
\end{document}

enter image description here

Having learned this, we can now place the other nodes in a similar way. With the right order of defining the nodes, this can be done without additional complicated calculations:

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning, calc}

\tikzset{
    square/.style={
        draw,
        minimum width=2em,
        minimum height=2em,
    }
}

\begin{document}
\begin{tikzpicture} 

\node[square] at (0,0) (A) {1};
\node[square, right=1cm of A] (B) {2};
\node[square, below=1cm of $(A)!0.5!(B)$] (C) {3};

\node[square, right=1.5cm of $(B)!0.5!(C)$] (D) {4};

\node[square, right=1cm of D] (G) {7};

\node[square, below=0.75cm of G] (H) {8};

\node[square, below=0.75cm of H] (I) {9};

\node[square, left=1.5cm of I] (F) {6};

\node[square, left=1.5cm of F] (E) {5};

\node[square, right=1cm of H] (J) {10};

\end{tikzpicture}
\end{document}

enter image description here

We can now add the edges between the nodes. Let us start with simple horizontal and vertical lines between the nodes where we need them. We can also use relative coordinates such as ++(1,0) to extend a line from one node to a certain direction:

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning, calc}

\tikzset{
    square/.style={
        draw,
        minimum width=2em,
        minimum height=2em,
    }
}

\begin{document}
\begin{tikzpicture} 

\node[square] at (0,0) (A) {1};
\node[square, right=1cm of A] (B) {2};
\node[square, below=1cm of $(A)!0.5!(B)$] (C) {3};
\node[square, right=1.5cm of $(B)!0.5!(C)$] (D) {4};
\node[square, right=1cm of D] (G) {7};
\node[square, below=0.75cm of G] (H) {8};
\node[square, below=0.75cm of H] (I) {9};
\node[square, left=1.5cm of I] (F) {6};
\node[square, left=1.5cm of F] (E) {5};
\node[square, right=1cm of H] (J) {10};

\draw (A) -- (B);

\draw (E) -- (F);

\draw (H) -- (J);

\draw (J) -- ++(1,0);

\end{tikzpicture}
\end{document}

enter image description here

Next, you should familiarize yourself with anchors. Depending on the shape of a node (the default being rectangle), every node has different anchors which mostly sit on its border. Almost every shape has an anchor at the top, at the bottom, at its left and at its right side which are called north, south, east and west respectively. Additionally the anchor center sits in the center of the node. You can point to such an anchor using the name of a node, add a dot and then the name of the anchor, for example A.south:

\documentclass[border=10pt]{standalone}
\usepackage{tikz}

\tikzset{
    square/.style={
        draw,
        minimum width=2em,
        minimum height=2em,
    }
}

\begin{document}
\begin{tikzpicture} 

\node[square] at (0,0) (A) {1};

\fill[red] (A.south) circle[radius=1pt];

\end{tikzpicture}
\end{document}

enter image description here

Knowing this and the fact than we can draw orthogonal edges using -| and |- instead of -- (which we use to draw simple straight lines), we can come up with the following:

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning, calc}

\tikzset{
    square/.style={
        draw,
        minimum width=2em,
        minimum height=2em,
    }
}

\begin{document}
\begin{tikzpicture} 

\node[square] at (0,0) (A) {1};
\node[square, right=1cm of A] (B) {2};
\node[square, below=1cm of $(A)!0.5!(B)$] (C) {3};
\node[square, right=1.5cm of $(B)!0.5!(C)$] (D) {4};
\node[square, right=1cm of D] (G) {7};
\node[square, below=0.75cm of G] (H) {8};
\node[square, below=0.75cm of H] (I) {9};
\node[square, left=1.5cm of I] (F) {6};
\node[square, left=1.5cm of F] (E) {5};
\node[square, right=1cm of H] (J) {10};

\draw (A) -- (B);
\draw (E) -- (F);
\draw (H) -- (J);
\draw (J) -- ++(1,0);

\draw (A.west) -- ++(-0.25,0) |- (C);

\draw (B.east) -- ++(0.25,0) |- (C);

\draw (D.east) -- ++(0.25,0) |- (F);

\draw (G.west) -- ++(-0.25,0) |- (I);

\draw (G.east) -- ++(0.25,0) |- (I);

\end{tikzpicture}
\end{document}

enter image description here

Using orthogonal connections with the |- or -| syntax, it is relatively simple to add coordinates in the middle of the horizontal or vertical part of this edge as well as on the coordinate where the horizontal and vertical part meet (note that in the following example, the coordinates are only placed on the second part of the edge that starts 0.5 cm above A and first goes horizontal and then vertical to B):

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning}

\tikzset{
    square/.style={
        draw,
        minimum width=2em,
        minimum height=2em,
    }
}

\begin{document}
\begin{tikzpicture} 

\node[square] at (0,0) (A) {1};
\node[square, right=1cm of A] (B) {2};

\draw (A.north) -- ++(0,0.5) -| (B)
    coordinate[at start] (C)
    coordinate[near start] (D)
    coordinate[midway] (E)
    coordinate[near end] (F)
    coordinate[at end] (G);

\fill[red] (C) circle[radius=1pt];

\fill[magenta] (D) circle[radius=1pt];

\fill[blue] (E) circle[radius=1pt];

\fill[cyan] (F) circle[radius=1pt];

\fill[green] (G) circle[radius=1pt];

\end{tikzpicture}
\end{document}

enter image description here

We can make use of this and place coordinates on edges using the syntax in the following example which finally helps us to add the last missing edges:

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning, calc}

\tikzset{
    square/.style={
        draw,
        minimum width=2em,
        minimum height=2em,
    }
}

\begin{document}
\begin{tikzpicture} 

\node[square] at (0,0) (A) {1};
\node[square, right=1cm of A] (B) {2};
\node[square, below=1cm of $(A)!0.5!(B)$] (C) {3};
\node[square, right=1.5cm of $(B)!0.5!(C)$] (D) {4};
\node[square, right=1cm of D] (G) {7};
\node[square, below=0.75cm of G] (H) {8};
\node[square, below=0.75cm of H] (I) {9};
\node[square, left=1.5cm of I] (F) {6};
\node[square, left=1.5cm of F] (E) {5};
\node[square, right=1cm of H] (J) {10};

\draw (A) -- (B);
\draw (E) -- (F);
\draw (H) -- (J);
\draw (J) -- ++(1,0);

\draw (A.west) -- ++(-0.25,0) |- (C) coordinate[near start] (AC);
\draw (B.east) -- ++(0.25,0) |- (C) coordinate[near start] (BC);
\draw (D.east) -- ++(0.25,0) |- (F) coordinate[near start] (DF);
\draw (G.west) -- ++(-0.25,0) |- (I);
\draw (G.east) -- ++(0.25,0) |- (I);

\draw (AC) -- ++(-0.25,0) |- (E) coordinate[near start] (ACE);

\draw (BC) -- (D);

\draw (DF) -- (H);

\draw (ACE) -- ++(-1,0);

\end{tikzpicture}
\end{document}

enter image description here

And we are done.

9
  • An academic explanation of the thought process! what about using the option on grid for node placement?
    – anis
    Jun 7 at 14:03
  • @anis Well, there are surely other ways to do this and maybe even simpler ones, but to add too many things into this step-by-step explanation would have been to much, I guess. Jun 7 at 15:12
  • This is what needs to be in the tikz documentation Jun 7 at 15:45
  • @ScottSeidman There are actually some quite nice tutorials in the manual already. Jun 7 at 15:48
  • 1
    @JasperHabicht This is one of the best illustrations I have seen. Thank you!
    – Feng
    Jun 12 at 1:19
7

I'd choose a matrix where the nodes 1, 2 and 3 are in the same column as 5 as well as in the same row as 4 and 7.

The nodes 1, 2 and 3 are placed via a special circular placement mechanism that's used by the chains library. This more or less positions the nodes automatically to each other. A few helpful coordinates (|[c]|) are placed instead of nodes inside the matrix.

Many of these connections are done via the -|- path operation. For the connections to node 3 auxialliary coordinates named x and y are used for having the vertical part at the right place as in the connections running parallel to node 1 and 2.

I'm using rounded corners to show how the connections are built.

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{calc, cd, chains, ext.paths.ortho}
\tikzset{
  -|- through point/.style={
    /tikz/horizontal vertical horizontal,
    /tikz/execute at begin to=
      \tikzset{insert path={let \p0=($(#1)-(\tikztostart)$) in},
               ortho/distance/.expanded=abs(\x0), ortho/from center}},
  matrix node/.default=name,
  matrix node/.style={%
    #1=\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn}}
\makeatletter
\tikzcdset{
  to   suffix/.code=\edef\tikzcd@ar@target{\tikzcd@ar@target#1},
  from suffix/.code=\edef\tikzcd@ar@start {\tikzcd@ar@start#1}}
\makeatother
\tikzcdset{shortcuts/.style={
  /tikz/ortho/install shortcuts,
  /tikz/c/.style={shape=coordinate, yshift=axis_height},
  /tikz/commutative diagrams/c/.style={
    /tikz/every to/.append style={edge node={coordinate (##1)}}}}}
\tikzset{
  counterclockwise placement/.style 2 args={% #1 = phase, #2 = n; snaps to y = ±1
    /utils/exec=\pgfmathsetmacro\ang{#1+(\tikzchaincount-1)*360/(#2)},
    at={(\ang:1|-0,{\ang<180?1:-1})}}}
\begin{document}
\begin{tikzcd}[
  shortcuts, rounded corners,
  cells={nodes={draw, minimum size=+8mm, align=center, text width=width("$00$")}},
  row sep={1cm, between origins}, arrows=-]
  & |[c]|            \ar[r, to suffix=-1, -|-, c=x]
                     \ar[r, to suffix=-3, -|- through point=x]
    & \path[y=+.5cm, start chain=placed {counterclockwise placement={30}{3}}]
        nodeforeach \t in {2, 1, 3} [on chain, matrix node=name prefix](-\t){\t};
                     \ar [from suffix=-1, to suffix=-2]
                     \rar[from suffix=-2, -|-, c=y]
                     \rar[from suffix=-3, -|- through point=y]
      & 4            \drar[-|-]
        & & 7        \drar[-|-]
\\
|[c]|                \urar[-|-] \drar[-|-]
  & & & & |[c]|      \rar \urar[-|-] \drar[-|-]
          & 8        \rar
            & 10     \rar
              & |[c]|
\\
  & |[c]|            \rar
    & 5              \rar
      & 6            \urar[-|-]
        & & 9        \urar[-|-]
\end{tikzcd}
\end{document}

Output

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .