4

When I was trying to understand this question I discovered a strange behavior when we use relative coordinates just after cycle in a transformed path.

Here is an example (the black path) :

\documentclass[tikz,border=7mm]{standalone}
\begin{document}
  \begin{tikzpicture}
    \draw[green]       (0,0) -- ++(1,0) -- ++(0,1) -- cycle node[red]{.} -- ++(0,1);
    \draw[xshift=15mm] (0,0) -- ++(1,0) -- ++(0,1) -- cycle node[red]{.} -- ++(0,1);
    \draw[green]     (3.5,0) -- ++(1,0) -- ++(0,1) -- cycle node[red]{.} -- ++(0,1);
  \end{tikzpicture}
\end{document}

enter image description here

If we replace cycle by (current subpath start) all three lines look the same.

Question: Is this behavior of cycle normal (documented), or is it a bug ?

Edit: For me cycle and (current subpath start) should not differ on how they move the current point, but only on how the path is "closed". So in some sens tikz should add --(current subpath start) after any cycle (it already adds (current subpath start)-- before cycle in some situations as explained in 14.2.1 Straight Lines)

  • xshift is a persistent coordinate transform. See for example \draw[xshift=15mm] (0,0) -- ++(1,0) -- ++(0,1) -- cycle \pgfextra{\pgfgetlastxy{\mya}{\myb}} node[below,red]{\mya\myb} -- ++(0,1);. If you don't provide a location for the node it picks up the last known (possibly transformed coordinate) – percusse Dec 14 '15 at 11:20
  • @percusse ok, but why cycle doesn't "move" the current point to (current subpath start) ? – Kpym Dec 14 '15 at 11:25
  • It does but the point gets transformed. The transforms are global – percusse Dec 14 '15 at 12:23
  • @percusse I understand what is happening, I think : the transformation is applied for the second time to the coordinate that is already transformed. But this looks like a bug for me. Is it ? In any case, bug or not, this behavior is non logical : the line in --++(0,1) is drawn from (1.5,0) and the coordinate ++(0,1) is calculated from (3,0) ... I have already seen this "double transformation" in other situations. – Kpym Dec 14 '15 at 12:33
  • (current subpath start) is a fixed and named reference. The transform doesn't apply to it because it is already on the canvas. But the node without a specific explicit location picks up whatever known to TikZ and the coordinate gets \pgf@processed. It's not a double application but rather asking for (0,0) point and receiving ([xshift=15mm]0,0). Add \pgfextra{\pgftransformreset} in between the cycle and node and you should see it. – percusse Dec 14 '15 at 12:49
5
+50

The problem is in tikz.code.tex line 2846

\def\tikz@@close cycle{%
  \tikz@flush@moveto%
  \edef\tikz@timer@start{\noexpand\pgfqpoint{\the\tikz@lastx}{\the\tikz@lasty}}
  \tikz@make@last@position{\expandafter\pgfpoint\pgfsyssoftpath@lastmoveto}%
  \tikz@path@close{\expandafter\pgfpoint\pgfsyssoftpath@lastmoveto}%
  \def\pgfstrokehook{}%
  \edef\tikz@timer@end{\noexpand\pgfqpoint{\the\tikz@lastx}{\the\tikz@lasty}}%
  \let\tikz@timer=\tikz@timer@line%
  \let\tikz@tangent\tikz@timer@start%
  \tikz@scan@next@command%
}

Where \tikz@make@last@position{\expandafter\pgfpoint\pgfsyssoftpath@lastmoveto} recalls the starting point of the current subpath. It is stored in absolute coordinate. Hence in your case, the three calls of --cycle involve PGF-points

\pgfpoint{0.0pt}{0.0pt}
\pgfpoint{42.67912pt}{0.0pt}
\pgfpoint{99.5846pt}{0.0pt}

And then when TikZ locates the nodes, these coordinates are used with transformation... That is to say that the transformation, for example xshift, is applied twice! To see this very clearly, one might test

\tikz\draw(0,0)node[blue]{.}[xshift=1cm](0,0)[yshift=1cm]--(0,0)--cycle node[red]{.};

Now the red point is at (2,1) because the old xshift is applied twice and the young yshift is applied once.

To fix this, one needs to replace the coordinate by a "un-transformed" one. And you actually gave a hint: the node (current subpath start). One might modify the definition as follows

\makeatletter
\def\tikz@@close cycle{%
  \tikz@flush@moveto%
  \edef\tikz@timer@start{\noexpand\pgfqpoint{\the\tikz@lastx}{\the\tikz@lasty}}
  %\tikz@make@last@position{\expandafter\pgfpoint\pgfsyssoftpath@lastmoveto}% replaced by the next line
  \tikz@make@last@position{\expandafter\pgfpointanchor{current subpath start}{center}}%
  \tikz@path@close{\expandafter\pgfpoint\pgfsyssoftpath@lastmoveto}%
  \def\pgfstrokehook{}%
  \edef\tikz@timer@end{\noexpand\pgfqpoint{\the\tikz@lastx}{\the\tikz@lasty}}%
  \let\tikz@timer=\tikz@timer@line%
  \let\tikz@tangent\tikz@timer@start%
  \tikz@scan@next@command%
}

Now it should work.


Full code

\documentclass[tikz,border=7mm]{standalone}
\begin{document}

  \tikz\draw(0,0)node[blue]{.}[xshift=1cm](0,0)[yshift=1cm]--(0,0)--cycle node[red]{.};

\makeatletter
\def\tikz@@close cycle{%
  \tikz@flush@moveto%
  \edef\tikz@timer@start{\noexpand\pgfqpoint{\the\tikz@lastx}{\the\tikz@lasty}}
  %\tikz@make@last@position{\expandafter\pgfpoint\pgfsyssoftpath@lastmoveto}% replaced by the next line
  \tikz@make@last@position{\expandafter\pgfpointanchor{current subpath start}{center}}%
  \tikz@path@close{\expandafter\pgfpoint\pgfsyssoftpath@lastmoveto}%
  \def\pgfstrokehook{}%
  \edef\tikz@timer@end{\noexpand\pgfqpoint{\the\tikz@lastx}{\the\tikz@lasty}}%
  \let\tikz@timer=\tikz@timer@line%
  \let\tikz@tangent\tikz@timer@start%
  \tikz@scan@next@command%
}

  \begin{tikzpicture}
    \draw[green]       (0,0) -- ++(1,0) -- ++(0,1) -- cycle node[red]{.} -- ++(0,1);
    \draw[xshift=15mm] (0,0) -- ++(1,0) -- ++(0,1) -- cycle node[red]{.} -- ++(0,1);
    \draw[green]     (3.5,0) -- ++(1,0) -- ++(0,1) -- cycle node[red]{.} -- ++(0,1);
  \end{tikzpicture}

\end{document}
  • This looks very promising ! Thanks. The question is : is this modification backward compatible with all "standard situations". I suppose yes. – Kpym Dec 21 '15 at 22:30
  • 2
    I can't still find where but I think this will break some stuff (in the decorations I suspect). I should check this if I have time. Very nice analysis. – percusse Dec 21 '15 at 23:06

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.