8

Consider the following MWE:

\documentclass[]{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\tikzset{myarr/.style={
        -{Triangle[width=6pt, length=9pt]},
}}
\begin{document}
\begin{tikzpicture}
    \draw[myarr] (0,0) -- (3,0) node[draw, anchor=west]{A};
\end{tikzpicture}

\bigskip

\begin{tikzpicture}[scale=0.5, transform shape]
    \draw[myarr] (0,0) -- (3,0) node[draw, anchor=west]{A};
\end{tikzpicture}
\end{document}

which produces:

enter image description here

Is it possible to define the myarr style so that the arrows will scale when the transform shape key is given with a scale?

Notice that in the real case the arrow will be drawn deep in the code, so I would like a solution where I just change the definition of myarr, without touching the rest of code. Something like defining a \scaledpt that enable doing

\tikzset{myarr/.style={
        -{Triangle[width=6\scaledpt, length=9\scaledpt]},
}}
7

One option is to use \pgflowlevelsynccm. However, it is to be used with great care.

\documentclass[]{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\tikzset{myarr/.style={
        -{Triangle[width=6pt, length=9pt]},
}}
\begin{document}
\begin{tikzpicture}
    \draw[myarr] (0,0) -- (3,0) node[draw, anchor=west]{A};
\end{tikzpicture}

\bigskip

\begin{tikzpicture}[scale=0.5, transform shape]
    \pgflowlevelsynccm
    \draw[myarr] (0,0) -- (3,0) node[draw, anchor=west]{A};
\end{tikzpicture}
\end{document}

enter image description here

A more complex but less dangerous solution is to read out \pgfgettransformentries and scale the arrow dimensions accordingly. As requested in the comment, it is spelled out here.

\documentclass[]{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\tikzset{myarr/.style={%
/utils/exec=\pgfgettransformentries{\tmpa}{\tmpb}{\tmpc}{\tmpd}{\tmp}{\tmp}%
\pgfmathsetmacro{\mydet}{\tmpa*\tmpd-\tmpb*\tmpc},
        -{Triangle[width=6pt, length=9pt,scale={sqrt(abs(\mydet))}]},
}}
\begin{document}
\begin{tikzpicture}
    \draw[myarr] (0,0) -- (3,0) node[draw, anchor=west]{A};
\end{tikzpicture}

\bigskip

\begin{tikzpicture}[scale=0.5, transform shape]
    \draw[myarr] (0,0) -- (3,0) node[draw, anchor=west]{A};
\end{tikzpicture}
\end{document}

enter image description here

Some comments on the background. The Euclidean transformations in 2 dimensions are given my a transformation matrix (with entries a, b, c and d, say) and a translation, i.e. a 2d vector. As explained on p. 1142 of the pgfmanual v3.1.4, they can be inferred with \pgfgettransformentries. The key scale=<factor> sets a=<factor> and d=<factor>, but in general the 2d volume gets scaled by the determinant a*d-b*c (such that under orthogonal transformations it is invariant) and the scale factor is its square root. (The determinant is just the Jacobian, and has been used e.g. before e.g. in this post and this post. I was urged to mention these earlier posts, which I am happy to do, but where should one stop?) That is, the second proposal does not tilt the arrow, but the first one does that yet is more "dangerous". A third option would be to apply \pgflowlevelsynccm only to the arrow, yet this will be quite a hack.

Let me also mention that the pgf manual is very clear on that line widths do not get transformed with scale=<factor>, regardless of whether or not you add transform shape. And (standard) arrows do scale with the line width. From this it is obvious that transform shape should not transform the size of arrow heads. If you want to have a fully rescaled picture, you'd need to scale the line widths, too. This can be fed into the arrows as well.

  • Yes, that's viable, but... 1) t also scales the arrow and the nodes even if transform shape is not here, which shouldn't (arguably) happen, and 2) it would be nice to restrict it to the arrowheads. – Rmano Aug 29 at 16:25
  • @Rmano I spelled out the second option. As I said, you can read out the transformations with \pgfgettransformentries and infer the scale transformation from that. This transformation is a matrix, but for your purpose the square root of its determinant is the scale factor. These tricks have been used elsewhere to adjust the line widths automatically. – Schrödinger's cat Aug 29 at 22:37
  • 1
    @Rmano You're welcome! I made it a bit more fool proof by taking the absolute value, such that there is no problem in connection with, say, xscale=-1. – Schrödinger's cat Aug 30 at 15:27
  • 1
    @Schrödinger'scat yes, I too had thought about the absolute value thing --- I have used it in github.com/circuitikz/circuitikz/pull/275 (yep, I linked to this answer, for everybody peace of mind). – Rmano Aug 30 at 17:19
  • 2
    I have removed a series of comments here related to the mathematical basis of the calculation. To summarise: the use of a Jacobian determinant approach is seen in various answers on the site but is not unique to any of them: it is a common mathematical approach. – Joseph Wright Aug 30 at 17:44
8

How about transform canvas?

enter image description here

\documentclass[]{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\tikzset{myarr/.style={
        -{Triangle[width=6pt, length=9pt]},
}}
\begin{document}
\begin{tikzpicture}
\draw[myarr] (0,0) -- (3,0) node[draw, anchor=west]{A};
\end{tikzpicture}

\bigskip

\begin{tikzpicture}[transform canvas={scale=0.5}]
\draw[myarr] (0,0) -- (3,0) node[draw, anchor=west]{A};
\end{tikzpicture}
\end{document}
  • @Rmano: You are not going to find the simplest solution, right? ^^ – Black Mild Aug 30 at 6:29
  • 2
    This is in principle a very neat solution if it were not for the fact that transform canvas screws up the bounding box. – Schrödinger's cat Aug 30 at 17:12
7

The arrows have the scale, scale length and scale width option (see page 193 of the TikZ 3.1.4b manual). I have changed your style so that this factor becomes a parameter of your style.

\tikzset{myarr/.style={
        -{Triangle[width=6pt, length=9pt,scale=#1]},
        myarr/.default=1
}}

screenshot

\documentclass[]{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}

\tikzset{myarr/.style={
        -{Triangle[width=6pt, length=9pt,scale=#1]},
        myarr/.default=1
}}
\begin{document}
\begin{tikzpicture}
    \draw[myarr] (0,0) -- (3,0) node[draw, anchor=west]{A};
\end{tikzpicture}

\bigskip

\begin{tikzpicture}[scale=0.5,transform shape]
    \draw[myarr=.5] (0,0) -- (3,0) node[draw, anchor=west]{A};
\end{tikzpicture}
\end{document}
  • Yes, it's a good idea. But in my case, the arrow is deep into the code, so I would really like to restrict the change just to the arrow definition. I'll edit the question accordingly, I feel I was not clear enough. – Rmano Aug 29 at 16:55
  • 1
    And thinking about it, that's strange. Given that the arrows do have the scale option, the tranform shape should percolate down to them, ...or not? – Rmano Aug 29 at 17:00
  • scale only acts on coordinates, not on shapes and arrows are shapes. See page 1054 of TikZ 3.1.4b manual Coordinate Versus Canvas Transformations – AndréC Aug 29 at 17:15
  • Yes --- but as you can see in the example, when you use transform shape scale should also work on the shapes: the node (the square and the "A") is smaller! – Rmano Aug 29 at 18:16
  • 1
    And so, we can conclude that contrary to what I said, arrows are not forms for TikZ. – AndréC Aug 29 at 20:08

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.