4

I'm trying to find the minimum x coordinate in a set of TikZ coordinates.

Consider this code

\documentclass[tikz]{standalone}
\usetikzlibrary{positioning}
\newdimen\x
\newdimen\xmin
\xmin=10000pt
\newdimen\y
\begin{document}
\begin{tikzpicture}[every node/.style={draw}]
\node (1) {182};
\node[below=of 1] (2) {183731468};
\node[below=of 2] (3) {74632};
\path (1.west); \pgfgetlastxy{\x}{\y}
\ifdim\x<\xmin \xmin=\x \fi
\fill[red] (\xmin,\y) circle (1pt);
\path (2.west); \pgfgetlastxy{\x}{\y}
\ifdim\x<\xmin \xmin=\x \fi
\fill[red] (\xmin,\y) circle (1pt);
\path (3.west); \pgfgetlastxy{\x}{\y}
\ifdim\x<\xmin \xmin=\x \fi
\fill[red] (\xmin,\y) circle (1pt);
\end{tikzpicture}
\end{document}

and this code

\documentclass[tikz]{standalone}
\usetikzlibrary{positioning}
\newdimen\x
\newdimen\xmin
\xmin=10000pt
\newdimen\y
\begin{document}
\begin{tikzpicture}[every node/.style={draw}]
\node (1) {182};
\node[below=of 1] (2) {183731468};
\node[below=of 2] (3) {74632};
\foreach \i in {1,2,3} {
    \path (\i.west); \pgfgetlastxy{\x}{\y}
    \ifdim\x<\xmin \xmin=\x \fi
    \fill[red] (\xmin,\y) circle (1pt);
}
\end{tikzpicture}
\end{document}

These are the outputs of the first code (left) and the second code (right) side by side:

enter image description here enter image description here

Look at the dot at the bottom. The first code gives the intended output, but the second doesn't.

Why? And how to modify the \foreach loop in the second code so that it gives the same output as the first?

3

In the \foreach variant, you are in a group when setting the dimension, so the "outside" value won't change. So you need to make the dimension global (or smuggle it out of the group). Here I made it global by just adding \global before \xmin=\x.

\documentclass[tikz]{standalone}
\usetikzlibrary{positioning}
\newdimen\x
\newdimen\xmin
\xmin=10000pt
\newdimen\y
\begin{document}
\begin{tikzpicture}[every node/.style={draw}]
\node (1) {182};
\node[below=of 1] (2) {183731468};
\node[below=of 2] (3) {74632};
\foreach \i in {1,2,3} {
    \path (\i.west); \pgfgetlastxy{\x}{\y}
    \ifdim\x<\xmin \global\xmin=\x \fi
    \fill[red] (\xmin,\y) circle (1pt);
}
\end{tikzpicture}
\end{document}

enter image description here

ADDENDUM: Of course, there are loops which do not introduce groups. The arguably simplest of those is the built-in \loop.

\documentclass[tikz]{standalone}
\usetikzlibrary{positioning}
\newdimen\x
\newdimen\xmin
\xmin=10000pt
\newdimen\y
\newcounter{loopi}
\begin{document}
\begin{tikzpicture}[every node/.style={draw}]
\node (1) {182};
\node[below=of 1] (2) {183731468};
\node[below=of 2] (3) {74632};
\setcounter{loopi}{0}
\loop
\stepcounter{loopi}
    \path (\number\value{loopi}.west); \pgfgetlastxy{\x}{\y}
    \ifdim\x<\xmin \xmin=\x \fi
    \fill[red] (\xmin,\y) circle (1pt);
\ifnum\number\value{loopi}<3\repeat
\end{tikzpicture}
\end{document}
  • @JouleV You only change the local but not the outside value. (This is also the reason why \draw[blue] ... renders only the ... but not every other path blue. Likewise, if you say \tikz[blue]{....} everything in the tikzpicture will be blue, but not outside, and so on.) In TeX all macros are local unless you explicitly make them global. – marmot Apr 14 at 14:56
2

You can use a different loop that doesn't do grouping. Instead of \i you use simply #1.

\documentclass[border=4]{standalone}
\usepackage{xparse}
\usepackage{tikz}

\usetikzlibrary{positioning}

\ExplSyntaxOn
\NewDocumentCommand{\nforeach}{mmm}
 {
  \cs_set:Nn \joulev_nforeach:n { #3 }
  \int_step_function:nnN { #1 } { #2 } \joulev_nforeach:n
 }
\ExplSyntaxOff

\newdimen\x
\newdimen\xmin
\newdimen\y

\begin{document}
\begin{tikzpicture}[every node/.style={draw}]
\xmin=10000pt
\node (1) {182};
\node[below=of 1] (2) {183731468};
\node[below=of 2] (3) {74632};
\nforeach{1}{3}{
  \path (#1.west); \pgfgetlastxy{\x}{\y}
  \ifdim\x<\xmin \xmin=\x \fi
  \fill[red] (\xmin,\y) circle (1pt);
}
\end{tikzpicture}
\end{document}

enter image description here

  • Maybe I will have to start learning expl3 syntax soon. What does those commands inside \ExplSyntax mean? – user156344 Apr 14 at 15:27
  • @JouleV The first line sets a temporary function according to the code in the third argument; the second line does a loop from the starting point (first argument) to the final point (second argument). No fancy syntax like \foreach, but for most cases it's sufficient. You find a more elaborate implementation at tex.stackexchange.com/a/329628/4427 – egreg Apr 14 at 15:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy