9

I have a simple question and hope for a simple answer: why does closing a path with dash pattern not work if the dash length exceeds the length of the path?

\documentclass[tikz,border=3.14mm]{standalone}
\begin{document}
 \begin{tikzpicture}
  \draw[dash pattern=on 5cm off 16cm,blue,ultra thick]
   (0,0) -- (1,0) -- (1,1) -- (0,1) -- cycle;
  \begin{scope}[xshift=2cm]
  \draw[blue,ultra thick]
   (0,0) -- (1,0) -- (1,1) -- (0,1) -- cycle;
  \end{scope}
 \end{tikzpicture} 
\end{document}

enter image description here

If you look closely, you'll notice that the left contour does not close, i.e. the bottom left corner is "screwed up". This is despite the fact that the dash exceeds (5cm) the length of the path (4cm). Why is that and how can one fix this?

Notice that I am not interested in a manual fix of the sort \draw[blue,ultra thick] (0,0) -- (1,0) -- (1,1) -- (0,1) -- (0,-\pgflinewidth/2);. I would, however, also be interested in a solution using decorations. This is because the present question is closely related to this nice question and the discussion below it. If you come up with a solution based on decorations that is different from this post in that it is less hacky, more transparent and does not mix decorations and meta decorations in an intricate way, this would be very interesting, but perhaps fit better as an answer to the partner question. (I am, however, not interested in a pure decorations solution in which one has to "quantize" the steps in such a way that the square gets redrawn.) That is, I would prefer answers without decorations to this question.

  • This is a viewer problem. Acrobat and Evince close the path, Okular, Chrome and Firefox do not. TikZ just copies the dash pattern into the PDF graphics stream, then your PDF viewer has to interpret it. This only happens for butt linecaps, so it might be a viewer bug. – Marcel Krüger Aug 21 '18 at 22:15
  • 1
    Correction for my earlier comment: It happens for all linecaps on the affected readers, it is just invisible when round linecaps and linejoins are used – Marcel Krüger Aug 21 '18 at 22:22
  • @MarcelKrüger If Acrobat does it, then that's pretty much the definition of correct behavior. (At least, that was the approach we took when writing our PDF interpreter.) – Derek Aug 21 '18 at 22:40
  • @MarcelKrüger Hmmh, I am not sure. The right contour is closed on my viewer. So how come that the results are different? But I agree that this is an important observation. (I am using preview, which is then in the list of "bad" viewers.) –  Aug 21 '18 at 22:57
  • @marmot I think the difference is that a dash pattern generally is a concept which makes not that much sense on a closed path, because a dash pattern requires some point where the pattern starts, while a closed path doesn't really start anywhere. So it may be that some viewers translate this into a non-closed path, leading to the observed problem. – Marcel Krüger Aug 21 '18 at 23:20
  • Related https://tex.stackexchange.com/questions/18642/bad-intersection-of-lines-in-tikz?noredirect=1&lq=1 – Cragfelt Aug 21 '18 at 23:22
  • 1
    Anyway I do not use TikZ, so this might not apply there, but in Metapost I would "fix" this with something like subpath(0, length p + eps) of p, so let Metapost loop around and append a minimal start segment of the path after the normal path. Then it is just a normal corner ans everything works. – Marcel Krüger Aug 21 '18 at 23:24
  • @MarcelKrüger But you could draw the path just with \draw[dashed,blue,ultra thick] (0,0) -- (1,0) -- (1,1) -- (0,1) -- cycle;, couldn't you? (Yes, I know that the dash pattern is not glorious then, and that the cheating dashes were created to fix this. What I did not appreciate so far is that even a single dash is problematic.) But your suggestion on the extra path is very clever and promising. –  Aug 21 '18 at 23:27
  • 1
    @Cragfelt Sorry, I disagree. These are not closed paths in the question and the correct answer was to draw a single stretch path. I am doing the same here. –  Aug 21 '18 at 23:28
  • @marmot Is there any reason why you can't just add a conditional to only use dash pattern if the first segment is not longer than the path? – Marcel Krüger Aug 21 '18 at 23:50
  • @MarcelKrüger Of course I could do that. This is more an academic question in which I would like to learn why things behave in the way they behave. In principle, the original problem is solved here, the bad thing about this is that not even the author understands why that works but not an obvious variation thereof. ;-) Your comments and observations are already very helpful, but I really wish to fully understand what's going on. –  Aug 21 '18 at 23:55
  • @MarcelKrüger Actually your proposal to loop around the path sounds very nice. If you are interested in writing an answer, here you can find how to recycle a path. –  Aug 22 '18 at 00:01

2 Answers2

4

A solution which tries to duplicate the closed path to such that the end is just an ordinary point in the middle: (Inspired by Qrrbrbirlbel's use path TikZ key)

\documentclass[tikz,border=3.14mm]{standalone}
\makeatletter
% This assumes that every closed path starts and ends with these tokens.
% That would make sense, but I do not know if it is true
\def\helpdoublepath\pgfsyssoftpath@movetotoken#1#2#3\pgfsyssoftpath@closepathtoken#4#5{%
  \unexpanded{\pgfsyssoftpath@movetotoken{#1}{#2}#3\pgfsyssoftpath@linetotoken{#1}{#2}#3\pgfsyssoftpath@closepathtoken{#4}{#5}}%
}
\def\doublepath#1{%
  \edef#1{\expandafter\helpdoublepath#1}%
}
\tikzset{double path/.code=\tikz@addmode{\pgfsyssoftpath@getcurrentpath\temp\doublepath\temp\pgfsyssoftpath@setcurrentpath\temp}}
\makeatother
\begin{document}
 \begin{tikzpicture}
   \draw[double path,dash pattern=on 5cm off 100cm,blue,ultra thick]
   (0,0) -- (1,0) -- (1,1) -- (0,1) -- cycle;
  \begin{scope}[xshift=2cm]
  \draw[blue,ultra thick]
   (0,0) -- (1,0) -- (1,1) -- (0,1) -- cycle;
  \end{scope}
 \end{tikzpicture} 
\end{document}

enter image description here

3

It's been a while since I wrote a PDF interpreter, but I believe you need to specify the line cap:

  \draw[line cap = rect, dash pattern=on 5cm off 16cm,blue,ultra thick]

enter image description here

Derek
  • 1,748
  • 1
    This only works if the lines meet at an angle of 90°. – Marcel Krüger Aug 21 '18 at 22:20
  • @MarcelKrüger Unfortunately, you are right. – Derek Aug 21 '18 at 22:46
  • Sorry, to me this sounds very much like the \draw[blue,ultra thick] (0,0) -- (1,0) -- (1,1) -- (0,1) -- (0,-\pgflinewidth/2); workaround. Of course, yours is a bit more elegant, but it does not really work for general closed paths. And I do not see why one should have to specify a line cap for a closed path. –  Aug 21 '18 at 22:59
  • 1
    @marmot It's not really a closed path. Dashing happens first, then stroking: the path is split into segments specified by the pattern, then each segment is stroked independently of the others. (I believe this behavior dates back to the PostScript days.) So when the path is dash with a long dash pattern, it turns into an unclosed path again. – Derek Aug 21 '18 at 23:09
  • But the first segment is longer than the path, isn't it? And yes, I see that it does not work, at least not for all viewers. Yet I am hoping someone will be able to inject a \pgfpathclose or something of that sort at the right place to cure the problem in a viewer-independent way. –  Aug 21 '18 at 23:14
  • @marmot You can also "turn the corner": \draw[...] (0,0) -- (1,0) -- (1,1) -- (0,1) -- (0,0) -- (0.1, 0) -- cycle;. But, as with all these approaches, it's not a general solution and will fail for some dash patterns and some paths. – Derek Aug 21 '18 at 23:31
  • Yes, sure, I my fix also works. Of course, if you manage to automatize that in such a way that the to path gets modified (along the lines of Marcel Krüger's comment), then this would be indeed a nice and important solution.... ;-) –  Aug 21 '18 at 23:35