22

When I place a decoration at the end of a to path with "bend left=..." or "bend right=...", the decorations disappear at high bend angles:

\documentclass[border = 2mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}
\tikzset{enddot/.style={
    decoration={markings, mark=at position 1 with {
    \fill (0,0)circle(2pt);
    }}, postaction={decorate}}}
\begin{document}
\begin{tikzpicture}
\foreach \angle in {0,10,...,80}{
\draw[enddot] (0,0)to[bend right=\angle](\angle:2);
}
\end{tikzpicture}
\end{document}

enter image description here

Is this a bug in Tikz?

  • 6
    As a workaround you can use mark=at position 0.99 instead of mark=at position 1. Seems like some round off error. – Peter Grill May 15 '18 at 20:01
  • I'm using that workaround right now. Btw, the largest number that works for every angle seems to be around 0.9997... – Andi Bauer May 16 '18 at 08:02
  • 2
    An example shown in the manual (v3.0.1a, page 587) implies that mark=at position -0.1pt works. If we replace -0.1pt with factor 1, the example will also fail. So this might be an issue known by the author. Maybe you should just avoid using 1 in a complex curve. – wklchris May 16 '18 at 20:58
  • 1
    Cool, didn't know that one can also give absolute positions on a path. And do I understand correctly that the positions are interpreted modulo the length of the path such that -0.1pt will be just right before the path end, independent of its length? – Andi Bauer May 17 '18 at 15:37
  • 1
    @AndiBauer Yeah. You can check that section of the manual for details. The syntax allows either a factor or an abosolute distance. – wklchris May 17 '18 at 17:32
  • 1
    @PeterGrill Can you add an answer ? That is actually the credible source of the problem – percusse May 21 '18 at 16:09
  • 1
    @percusse I suspect some rounding error is affecting the "not drawing if pos >1" check explained in the manual. A possible solution would to allow to extrapolate the decoration after the end... which would be useful sometime. – Rmano May 21 '18 at 16:15
  • @Rmano Current decorations are not defined for path length factor >1. You need to write new decorations with different extrapolation rules, (keep the last angle or keep turning etc.) – percusse May 21 '18 at 17:34
  • 1
    @percusse: Done. – Peter Grill May 21 '18 at 18:53

3 Answers3

18

That's a very interesting observation, but you could achieve the desired output very easily with the arrows.meta library.

\documentclass[border = 2mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\begin{document}
\begin{tikzpicture}
\foreach \angle in {0,10,...,80}{
\draw[-Circle] (0,0)to[bend right=\angle](\angle:2);
}
\end{tikzpicture}
\end{document}

enter image description here

IMHO this suggests that it is not a bug, rather the start and end points are to be dealt with arrows methods. It is not too surprising that these special points can cause some issues when used in decorations. Notice that in these decorations you are always in the tangent coordinate system at a given point of the path. (Of course, when your decoration is a circle, you cannot appreciate it, but if you were to place \draw (0,-2pt) -- (0,2pt); you'd see that this is orthogonal to the path at the given point). And clearly this tangent coordinate system is ill-defined at the ends of the curve. However, arrows are "trained" to deal with this, and thus there is no issue.

ADDENDUM: I do not think it is (only) a round-off error. Consider the MWE

\documentclass[border = 2mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}
\tikzset{enddot/.style={
    decoration={markings, 
    mark=at position 1 with {
    \fill (0,0)circle(2pt);
    },
    mark=at position 0.5 with {
    \fill (0,0)circle(2pt);
    }}, postaction={decorate}}}
\begin{document}
\begin{tikzpicture}
\foreach \angle in {0,10,...,80}{
\draw[enddot] (0,0)to[bend right=\angle](\angle:2);
}
\end{tikzpicture}
\end{document}

It produces like in the OP's case

enter image description here

NOTE: None of the circles in the middle has been drawn.

However, once I remove

    mark=at position 1 with {
    \fill (0,0)circle(2pt);
    },

I get

enter image description here

IMHO this is not explainable as rounding error. Or am I doing something stupid?

ANOTHER ADDENDUM: In order to see why that happens at a more technical level, I added a typeout to \pgf@decorate@@movealongpath in pgfmoduledecorations.tex such that it becomes

\def\pgf@decorate@@movealongpath{%
  \advance\pgfdecoratedinputsegmentcompleteddistance\pgf@decorate@distancetomove%
  \advance\pgfdecoratedinputsegmentremainingdistance-\pgf@decorate@distancetomove%
  \ifdim\pgfdecoratedinputsegmentremainingdistance>0pt\relax%
    \let\pgf@next\pgf@decorate@@@movealongpath%
  \else%
          \typeout{gotcha}
    \pgf@decorate@distancetomove-\pgfdecoratedinputsegmentremainingdistance%
    \pgf@decorate@processnextinputsegmentobject%
    \ifx\pgf@decorate@currentinputsegmentobjects\pgfutil@empty%
      \pgfdecoratedremainingdistance0pt\relax%
      \let\pgf@next\relax%
    \else%
      \let\pgf@next\pgf@decorate@@movealongpath%
    \fi%
  \fi%
  \pgf@next%
}

which leads to the typeouts

0
gotcha
10
gotcha
20
gotcha
30
gotcha
40
50
60
70
80

where the numbers are the respective angles. This means that at the too high angle the remaining distance becomes negative (or nonpositive to be more precise), which it probably should not, and hence the decoration gets suppressed. So I guess it is fair to say that

  • Yes, a rounding error plays a role in the explanation, but it is probably fair to say that they are not the sole explanation. Naively, I would expect a rounding error to just move a decoration, not to swallow it. However, digging into the code I believe to have identified the piece of code that is responsible for the disappearance of the decorations.

  • J Leon V.'s observation are consistent with that. Yet I am not sure that they explain why this happens. But I am also not claiming this answer truly explains why the above if statement has been implemented.

  • On the other hand, I think this answer provides a reliable way of placing the decorations where they should be. (I also see that my statement on the derivative above is not to the point. I keep it for historical reasons. I also think I could defend it by reminding the reader that the arc length is integrated as an integral over the length of the tangent, but this is not what I had in mind when I wrote the statement.)

  • I don't see why the tangent vector should be ill-defined at the ends of a curve. For the point 0 it works fine. Maybe it takes the derivative numerically into the direction of the line which fails at 1. But that still doesn't explain why it does work for some angles. – Andi Bauer May 16 '18 at 07:59
  • I exactly want to use this rotated coordinate system feature to place a node after the line end, see: https://tex.stackexchange.com/q/431650/163063. Can one use the arrows method for this also? – Andi Bauer May 16 '18 at 08:00
  • .. And as for your first comment: I assume that you know how mathematicians define differentiability and that you need a (small) neighborhood around the point. –  May 16 '18 at 14:28
  • @marmot you're right, at the end of the path the differentiation should be the one-side type (https://en.wikipedia.org/wiki/Semi-differentiability ), left- or right- given the case. – Rmano May 21 '18 at 18:40
  • @Rmano Yes, that's how it is done for arrows, I think. Of course, here things get discretized, therefore the OP finds a maximal value of 0.9997. I guess if he were to decrease the scale, this value will shrink. And I think there will be always things that break if one goes to too small or large dimension. (Of course, the fact that the circle just gets swallowed is a bit surprising.) –  May 21 '18 at 18:50
  • @marmot the manual says that the pos parameter must be growing-only, or havoc will rise... if you put the 0.5 mark before the 1.0, all the 0.5 circle are drawn. – Rmano May 21 '18 at 19:56
  • @Rmano Thanks a lot! I didn't know this, and, interestingly enough, it has not caused any trouble in the animation here. What I am more concerned about, though, is that all middle dots disappear, and that all dots do appear if I use pos=0.9 instead of pos=1. This suggests TikZ goes into a "swallow all decorations" mode after it came across one that it does not want to place. –  May 21 '18 at 20:01
  • 1
    Not really THE answer, but very useful if someone wants to nail the bug... And the bounty was expiring.... – Rmano May 27 '18 at 20:37
18

It's interesting, so the error is in the algorithm that disappears the decorations when the line is finished, for example, if I use a defined length to position them, if they exceed the length of the line, the points disappear as the next result.

\foreach \distance in {0.9,1.92,...,2.5} 
...
...mark=at position \distance cm with ... % position in cm.

enter image description here

In my first iteration probe with varying the position by percent of line:

\foreach \distance in {0.90,0.91,...,1.05} 

enter image description here

The first observation is that the foreach algorithm when calculating the step, adds decimals, is not limited to the decimals of the step. If I make the steps in thousandths, I get this.

\foreach \distance in {0.99,0.991,...,1.01}

enter image description here

Here you can see that the error occurs, even with values close to 1, then, probe with values close to 1 increasing the accuracy with more decimals, and I got this.

\foreach \distance in {0.9,0.99,0.999,0.9999,0.99999,0.999999,1,1.0009} 

enter image description here

As you can see the error is caused when approaching in 4 cecimals.The algorithm that locates the origin of the decoration in the line has a limited exatitude, probably due to the calculation length of the variables, while the bend angle, makes the ongitude of this line have a value with more or less decimals, that make the other algorithm that decides where the mark disappears can not decide the limit correctly. Will there be some configuration to increase the calculation bits in the algorithms?

MWE:

% arara: pdflatex: {synctex: yes, action: nonstopmode}
% arara: animate: {density: 200, delay: 200, other: -background white -alpha remove}
% arara: showanimate
\documentclass[tikz,border = 2pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}
\begin{document}
    \foreach \distance in {0.9,0.99,0.999,0.9999,0.99999,0.999999,1,1.000009}
    {
        \begin{tikzpicture}
        % fill circle and plot
        \foreach \angle in {0,10,...,80}{
            \draw[decoration={markings, mark=at position \distance with {
                    \fill[red] (-1pt,-1pt) rectangle (1pt,1pt);
            }}, postaction={decorate}] (0,0)to[bend right=\angle](\angle:2);
        }
        \draw (-0.5,-0.2) rectangle (2.5,2.5);
        \node (a) at (1,2.25) {\tiny Mark position=\distance } ;
        \end{tikzpicture}
    }
\end{document}

For Animation I use ImageMagic portable version, embedded in arara in animate yaml file.

J Leon V.
  • 11,533
  • 16
  • 47
13

Seems like a round off error to me. As a workaround you can use mark=at position 0.99 instead of mark=at position 1. Seems like some round off error.

enter image description here

Code:

\documentclass[border = 2pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}
\tikzset{enddot/.style={
    decoration={markings, mark=at position 0.99 with {
    \fill[red] (0,0) circle (2pt);
    }}, postaction={decorate}}}
\begin{document}
\begin{tikzpicture}
\foreach \angle in {0,10,...,80}{
\draw[enddot] (0,0)to[bend right=\angle](\angle:2);
}
\end{tikzpicture}
\end{document}
Peter Grill
  • 223,288