Using font-relative distances in tikzpictures

When I use scale in tikzpictures (without transform shape), the text size in nodes is not changed (which is good and well). Suppose now that I want to put some node in a scaled graph using a distance proportional to the node font; I naively supposed that I could use ex coords, but see below...

I also printed the values of the coordinates, and \pgf@yy which should give the unit vector, but there is something I miss here...

Is there a way to express a distance which is proportional to the (default) font size, independently from the scale?

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\newdimen\mydimA\newdimen\mydimB
\makeatletter
\newcommand{\showat}[1]{%
\pgfextracty\mydimA{\pgfpointanchor{A}{center}}
\pgfextracty\mydimB{\pgfpointanchor{B}{center}}
\node[red, font=\tiny,  align=left] at(#1) {Before \the\mydimA \\ After \the\mydimB \\
Scale y \the\pgf@yy};
}
\makeatother
\begin{document}
\begin{tikzpicture}[baseline]
\draw (0,0)  --(1,0) coordinate(A) -- (2,0);
\path (A) ++(0,1ex) coordinate(B);
\node [anchor=base] at (B) {$R_g$};
\showat{1,-1}
\end{tikzpicture}
\begin{tikzpicture}[baseline, scale=2]
\draw (0,0)  --(0.5,0) coordinate(A) -- (1,0);
\path (A) ++(0,1ex) coordinate(B);
\node [anchor=base] at (B) {$R_g$};
\showat{0.5,-0.5}
\end{tikzpicture}
\begin{tikzpicture}[baseline, scale=0.01]
\draw (0,0)  --(100,0) coordinate(A) -- (200,0);
\path (A) ++(0,1ex) coordinate(B);
\node [anchor=base] at (B) {$R_g$};
\showat{100,-100}
\end{tikzpicture}
\end{document}


• If you want to scale the whole picture you could use \resizebox -- which scales everything (line width, text spacing) – Aubrey Blumsohn Feb 21 '19 at 12:44
• @AubreyBlumsohn, no, it's not a matter of rescaling the whole picture. I want to be able to say that a label should be at 1ex distance from an anchor, no matter the scale of the figure... it's for a much more complex case, really (see github.com/circuitikz/circuitikz/issues/91) . – Rmano Feb 21 '19 at 12:47
• I didn't understand your question. – AndréC Feb 21 '19 at 14:07
• Coordinates are transformed by scaling while node text isn't. I'm not sure why one would expect something else. Internally, TeX does not care if it is cm or ex, it's all sp. – TeXnician Feb 21 '19 at 14:37
• @AndréC Basically the question is how to get all these three tikzpictures to produce the same results while using coordinate transformations based on font dimensions like ex (relative to the node font). – TeXnician Feb 21 '19 at 14:44

Your 1em is being converted at read time into the coordinate system, which is then affected by the picture scale. You can use reset cm to locally reset the coordinate transformation matrix, locally cancelling the picture scale effect.

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}[baseline]
\draw (0,0)  --(1,0) coordinate(A) -- (2,0);
\path (A) ++(0,1em) coordinate(B);
\node [anchor=base] at (B) {$R_g$};
\end{tikzpicture}
\begin{tikzpicture}[baseline, scale=2]
\draw (0,0)  --(0.5,0) coordinate(A) -- (1,0);
\path[reset cm] (A) ++(0,1em) coordinate(B);
\node [anchor=base] at (B) {$R_g$};
\end{tikzpicture}
\begin{tikzpicture}[baseline, scale=0.01]
\draw (0,0)  --(100,0) coordinate(A) -- (200,0);
\path[reset cm] (A) ++(0,1em) coordinate(B);
\node [anchor=base] at (B) {$R_g$};
\end{tikzpicture}
\end{document}

• I used this and it works as supposed... thanks a lot! – Rmano Feb 21 '19 at 16:12

Resetting the scale locally is one option (see Marsden's answer), but there is a better way to go about things.

With scale=factor, all coordinates are scaled by factor, regardless of their dimension (while the unit vectors are unchanged, as you discovered). Instead, redefine the length of the unit vectors according to the desired scaling. For example, with y={(0cm,2cm)}, while (0,0) -- (1,0) will be twice the usual distance, coordinates with dimensions, such as (0,1ex), will be unchanged:

\begin{document}
\begin{tikzpicture}[baseline]
\draw (0,0)  --(1,0) coordinate(A) -- (2,0);
\path (A) ++(0,1ex) coordinate(B);
\node [anchor=base] at (B) {$R_g$};
\showat{1,-1cm}
\end{tikzpicture}
\begin{tikzpicture}[baseline, x={(2cm,0cm)},y={(0cm,2cm)}]
% Identical code to previous picture:
\draw (0,0)  --(1,0) coordinate(A) -- (2,0);
\path[scale=1] (A) ++(0,1ex) coordinate(B);
\node [anchor=base] at (B) {$R_g$};
\showat{1,-1cm}
\end{tikzpicture}
\end{document}


Output:

Note how the distance between A and B is unchanged, while \the\pgf@yy has doubled, as expected.

Why don't you place B as itself a node (with relative placement)?

That would solve all your problems.

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{positioning}
\newdimen\mydimA\newdimen\mydimB
\makeatletter
\newcommand{\showat}[1]{%
\pgfextracty\mydimA{\pgfpointanchor{A}{center}}
\pgfextracty\mydimB{\pgfpointanchor{B}{center}}
\node[red, font=\tiny,  align=left] at(#1) {Before \the\mydimA \\ After \the\mydimB \\
Scale y \the\pgf@yy};
}
\makeatother
\begin{document}
\tikzset{node distance=1ex}
\begin{tikzpicture}[baseline]
\draw (0,0)  --(1,0) coordinate(A) -- (2,0);
\node[above=of A,anchor=base](B){$R_g$};
\showat{1,-1}
\end{tikzpicture}
\begin{tikzpicture}[baseline, scale=2]
\draw (0,0)  --(0.5,0) coordinate(A) -- (1,0);
%\path (A) ++(0,1ex) coordinate(B)node{y};
\node[above=of A,anchor=base] (B) {$R_g$};
\showat{0.5,-0.5}
\end{tikzpicture}
\begin{tikzpicture}[baseline, scale=0.01]
\draw (0,0)  --(100,0) coordinate(A) -- (200,0);
%\path (A) ++(0,1ex) coordinate(B)node{y};
\node[above=of A,anchor=base] (B) {$R_g$};
\showat{100,-100}
\end{tikzpicture}
\end{document}


Eric Marsden's answer explains nicely what's going on. However, I would just read out the relevant entry of the transformation matrix (it is the 22 entry) and "invert" it. This is a trick that I also used (without inversion, of course) to scale the line width with a shape because by default it doesn't scale.

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\newdimen\mydimA\newdimen\mydimB
\makeatletter
\newcommand{\showat}[1]{%
\pgfextracty\mydimA{\pgfpointanchor{A}{center}}
\pgfextracty\mydimB{\pgfpointanchor{B}{center}}
\node[red, font=\tiny,  align=left] at(#1) {Before \the\mydimA \\ After \the\mydimB \\
Scale y \the\pgf@yy};
}
\makeatother
\begin{document}
\begin{tikzpicture}[baseline]
\draw (0,0)  --(1,0) coordinate(A) -- (2,0);
\pgfgettransformentries{\tmp}{\tmp}{\tmp}{\myscale}{\tmp}{\tmp}
\path  (A) ++(0,1ex/\myscale) coordinate(B);
\node [anchor=base] at (B) {$R_g$};
\showat{1,-1}
\end{tikzpicture}
\begin{tikzpicture}[baseline, scale=2]
\draw (0,0)  --(0.5,0) coordinate(A) -- (1,0);
\pgfgettransformentries{\tmp}{\tmp}{\tmp}{\myscale}{\tmp}{\tmp}
\path  (A) ++(0,1ex/\myscale) coordinate(B);
\node [anchor=base] at (B) {$R_g$};
\showat{0.5,-0.5}
\end{tikzpicture}
\begin{tikzpicture}[baseline, scale=0.01]
\draw (0,0)  --(100,0) coordinate(A) -- (200,0);
\pgfgettransformentries{\tmp}{\tmp}{\tmp}{\myscale}{\tmp}{\tmp}
\path  (A) ++(0,1ex/\myscale) coordinate(B);
\node [anchor=base] at (B) {$R_g$};
\showat{100,-100}
\end{tikzpicture}
\end{document}


• Thanks @marmot, +1! I already accepted Eric's answer (and implemented it), but I think both are valid. – Rmano Feb 21 '19 at 16:13
• @Rmano Yes, Eric was the first to point out what the problem is and provided an elegant way to fix it. You should definitely accept his nice answer. – user121799 Feb 21 '19 at 17:20