[Tex/LaTex] How to position one node relative to another node at a certain angle in TikZ taking the nodes’ size into account

coordinatespositioningtikz-pgf

I asked a question before about how to position a node relative to another node at a certain angle in TikZ, and the answer I got there was that you can use the \path (node1) ++(angle:distance) node [styles] (node2) {}; syntax.

However, the distance here only concerns the distance between the centers of the two nodes, and not the distances between the outer edges of the nodes. To illustrate, look at the example below:

\documentclass{standalone}

\usepackage{tikz}
\usetikzlibrary{positioning}

\newdimen\nodeSize
\nodeSize=4mm
\newdimen\nodeDist
\nodeDist=6mm
\begin{document}
\begin{tikzpicture}[
    node/.style={%
      draw,
      circle,
      inner sep=0,
      outer sep=0,
      minimum size=\nodeSize,
      node distance=\nodeDist,
    },
  ]
  \node [node] (n1) {$n_1$};
  \node [node, below=of n1] (n2) {$n_2$};
  \path (n2) ++(-120:\nodeDist) node [node] (n3) {$n_3$};
  \path (n2) ++(- 60:\nodeDist) node [node] (n4) {$n_4$};

  \draw (n1) -- (n2);
  \draw (n2) -- (n3);
  \draw (n3) -- (n4);
  \draw (n4) -- (n2);
\end{tikzpicture}
\end{document}

enter image description here

An easy fix is of course to let the distance be the sum of \nodeDist and \nodeSize, but I'm wondering if there is some more robust way of doing this such that:

  1. it uses the value of node distance (thereby not having to deal with lengths external to TikZ), and
  2. takes the actual sizes of the nodes into account.

Ideally, it would be nice to be able to enable a similar method as provided by the positioning library – \node [position=60 degrees from node1] (node2) {}; – or something like it.

Best Answer

You can achieve something like this relatively easily by defining a new style

position/.style args={#1:#2 from #3}{
    at=(#3.#1), anchor=#1+180, shift=(#1:#2)
}

Then you can specify the angle and the separation between the nodes using, for example, position=-120:{\nodeDist} from n2.

If you want to use the value set in node distance, you can use

\makeatletter
\tikzset{
    position/.style args={#1 degrees from #2}{
        at=(#2.#1), anchor=#1+180, shift=(#1:\tikz@node@distance)
    }
}
\makeatother

and call the style using position=120 degrees from n2

\documentclass{standalone}

\usepackage{tikz}
\usetikzlibrary{positioning}

\newdimen\nodeSize
\nodeSize=4mm
\newdimen\nodeDist
\nodeDist=6mm

\tikzset{
    position/.style args={#1:#2 from #3}{
        at=(#3.#1), anchor=#1+180, shift=(#1:#2)
    }
}

\begin{document}
\begin{tikzpicture}[
    node/.style={%
      draw,
      circle,
      inner sep=0,
      outer sep=0,
      minimum size=\nodeSize,
      node distance=\nodeDist,
    },
  ]
  \node [node] (n1) {$n_1$};
  \node [node, below=of n1] (n2) {$n_2$};
  \node [node, position=-120:{\nodeDist} from n2] (n3) {$n_3$};
  \node [node, position=-60:{\nodeDist} from n2] (n4) {$n_4$};

  \draw (n1) -- (n2);
  \draw (n2) -- (n3);
  \draw (n3) -- (n4);
  \draw (n4) -- (n2);
\end{tikzpicture}
\end{document}