8

Placing an arrow as decoration on a path which has been shortened leads to a misplacement of the arrow. This is potentially emphasized if the looseness key has been used. In this other question (and also here), the problem has been noticed, but I do not understand if there is a general way to make the shorten key interact with a decoration getting the naively expected result.

In the MWE below, you can see that not shortening the (orange) path leads the (black) arrow to be centered on it, while shortening the (blue) path makes the (red) arrow misplaced.

\documentclass[border =3mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}

\tikzset{
    mid arrow/.style={postaction={decorate,decoration={markings,mark=at position .35 with {\arrow[#1]{stealth}}}}},
    connect/.style={shorten >= #1 mm, shorten <= #1 mm, out=0, in=180, looseness=2},
}

\begin{document}
    \begin{tikzpicture}
        \coordinate (A) at (0,0); \coordinate (B) at (2,1);
        \path[draw=blue] (A) edge[mid arrow=red, connect=1] (B);
        \path[draw=orange] (B) edge[mid arrow=black, connect=0] (A);
    \end{tikzpicture}
\end{document}

enter image description here

4

Well, if the shorten routine provided by TikZ does not do what you want, you may build your own. Here is a proposal inspired by this answer. The idea is to measure the length of the path and then switch the drawing off and on. (UPDATE: Add dash phase=0.0001pt, a trick discovered by the OP, that avoids a very tiny point at the beginning of the path, which I failed to spot when I posted the earlier versions of this answer.)

\documentclass[border =3mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}
\newlength\mylen
\newlength\vislen
\newlength\hidlen
\tikzset{
    mid arrow/.style={postaction={decorate,decoration={markings,mark=at position .35 with {\arrow[#1]{stealth}}}}},
    connect/.style={
    decoration={
    markings,
    mark=at position 0.5 with {
      \node[draw=none,inner sep=0pt,fill=none,text width=0pt,minimum size=0pt]
      {\global\setlength\mylen{\pgfdecoratedpathlength}
      \pgfmathsetmacro{\hiddenlength}{#1 mm}
      \global\setlength\hidlen{\hiddenlength pt}
      \pgfmathsetmacro{\visiblelength}{\pgfdecoratedpathlength-2*\hidlen}
      \global\setlength\vislen{\visiblelength pt}
      };
    },
  },
  preaction={decorate},
  dash pattern=on 0pt off \hidlen on \vislen off \hidlen, 
  dash phase=0.0001pt, 
    out=0, in=180, looseness=2},
}

\begin{document}
    \begin{tikzpicture}
        \coordinate (A) at (0,0); \coordinate (B) at (2,1);
        \path[draw=blue] (A) edge[mid arrow=red, connect=1] (B);
        \path[draw=orange] (B) edge[mid arrow=black, connect=0] (A);
    \end{tikzpicture}
\end{document}

enter image description here

Let me stress that this is not a real alternative of TikZ' shorten (yet). For instance, if you were to add arrow heads by simply saying \draw[->], this won't work, i.e. the arrow heads would just sit at the position of the target, but a bit of the curve would be missing. (However, you could of course easily add arrow heads wherever you want them.)

ADDENDUM: Just for fun, added a more flexible style myconnect which allows one to skip different amounts at the beginning and at the end of the path, and also double-checked that for curves with controls everything works fine.

\documentclass[border =3mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}
\newlength\mylen
\newlength\vislen
\newlength\hidlen
\newlength\firstlen
\newlength\secondlen
\tikzset{
    mid arrow/.style={postaction={decorate,decoration={markings,mark=at position .35 with {\arrow[#1]{stealth}}}}},
    connect/.style={
    decoration={
    markings,
    mark=at position 0.5 with {
      \node[draw=none,inner sep=0pt,fill=none,text width=0pt,minimum size=0pt]
      {\global\setlength\mylen{\pgfdecoratedpathlength}
      \pgfmathsetmacro{\hiddenlength}{#1 mm}
      \global\setlength\hidlen{\hiddenlength pt}
      \pgfmathsetmacro{\visiblelength}{\pgfdecoratedpathlength-2*\hidlen}
      \global\setlength\vislen{\visiblelength pt}
      };
    },
  },
  preaction={decorate},
  dash pattern=on 0pt off \hidlen on \vislen off \hidlen,
  dash phase=0.0001pt,  
    out=0, in=180, looseness=2},
}

\tikzset{myshorten/.style n args={3}{#1,
    decoration={
    markings,
    mark=at position 0.5 with {
      \node[draw=none,inner sep=0pt,fill=none,text width=0pt,minimum size=0pt] 
      {\global\setlength\mylen{\pgfdecoratedpathlength}
      \pgfmathsetmacro{\hiddenlength}{#2}
      \global\setlength\firstlen{\hiddenlength pt}
      \pgfmathsetmacro{\hiddenlength}{#3}
      \global\setlength\secondlen{\hiddenlength pt}
      \pgfmathsetmacro{\visiblelength}{\pgfdecoratedpathlength-\the\firstlen-\the\secondlen}
      \global\setlength\vislen{\visiblelength pt}
      };
    },
  },
  preaction={decorate},
  dash pattern=on 0\mylen off \firstlen on \vislen off \secondlen,
  dash phase=0.0001pt
  }
}


\begin{document}
    \begin{tikzpicture}
        \coordinate (B) at (2,1); \coordinate (C) at (5,5);
        \path[draw=red] (B) edge[mid arrow=blue,myshorten={out=0, in=180, looseness=2}{5mm}{7mm}]
        (C);
    \end{tikzpicture}

\begin{tikzpicture}
     \coordinate (B) at (2,1); \coordinate (C) at (3,5);
     \draw[gray,connect=0] (B) .. controls ++(0:1.75) and ++(180:1.75) .. (C);      
     \draw[mid arrow=red,connect=10] (B) .. controls ++(0:1.75) and ++(180:1.75) .. (C);        

\end{tikzpicture}
\end{document}

enter image description here

The gray curve in the right picture is just to verify that the curve does not get deformed.

  • 1
    Very nice, I like your idea a lot. And I have to admit that I would have rather naively expected this as default behavior of shortened decorated paths. Anyway, the dash pattern, despite the initial on 0pt, draws an almost invisible dot at the starting point. A dash phase=0.01pt would remove it shifting imperceptibly the drawn path, but it sounds to me as a patch. Is there a more elegant way to fix this minor aspect? – Axel Krypton Mar 11 '18 at 10:59
  • @AxelKrypton A very good catch! I didn't spot that! Perhaps your suggestion is the easiest way out? According to what I see, even dash phase=0.0001pt does the job. (Sometimes one has to help TikZ a little bit.) If you agree, I will update my answer accordingly. – user121799 Mar 11 '18 at 13:21
  • Yes, you are right. dash phase=0.0001pt also does the job and I hardly believe it could be a problem for anyone. I cannot think of anything better for the moment, so an update would be welcome. :) – Axel Krypton Mar 11 '18 at 18:44
  • @AxelKrypton Will do. But which version do you want, the first or the more flexible one (or both)? – user121799 Mar 11 '18 at 18:56
  • I think indeed the second, more flexible one is enough and it somehow contains the first. And it can be easily used or adapted to symmetrically shorten the path. But I leave the edit up to you! – Axel Krypton Mar 11 '18 at 19:24
2

Just a little better ... And the problem is the same with controls or arc, see below.

enter image description here

\documentclass[border=3mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}

\tikzset{
    mid arrow/.style args={#1 at #2}{%  #2 not 0 or 1 exactly
        decoration={%
            markings,
            mark = at position #2 with {%
                % what ever you want, as if you were in a scope
                % the coordinate system tangential
                \draw[#1,-stealth] (0,0)--(1pt,0pt);
            }
        },
        postaction={decorate}
    },
    connect/.style={out=0, in=180, looseness=2, shorten >= #1, shorten <= #1},
}

\begin{document}
    \begin{tikzpicture}
        \coordinate (A) at (0,0);
        \coordinate (B) at (2,1);
        \path[draw=blue] (A) edge[mid arrow=red at .35, connect=1mm] (B);
        \path[draw=orange] (B) edge[mid arrow=black at .35, connect=1mm] (A);
    \end{tikzpicture}
\end{document}

To discuss the first remark in the comment (and to add more arrows in the race).

The problem is more complicated than just switching the decoration step (placing the arrow on the path) and the shorten step, because the black arrow follows the orange line but with gray tangential coordinate.

Switching the two steps is not enough: the shorten action introduces a twist in the new path (the two paths are no more on the same drawn line) and, furthermore, a shift in position. Therefore, the tangential coordinates are calculated on the original path and are no more tangential to the new twisted drawn line.

pos offset = 0.025 :

enter image description here

No pos offset :

enter image description here

The twist: starting and ending points are shifted and leave the initial line and the in and out angles stays the same. shorten does not make the line shorter, it rather creates a new one! Here, with a shorten of 10, the red arrow should be on the dark blue line while it stays on the pale one, which is the original without shortening.

enter image description here

The problem is the same with controls or arc

enter image description here

\documentclass[border=3mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}

\tikzset{
    mid arrow/.style={postaction={decorate,decoration={markings,mark=at position .35 with {\arrow[#1]{stealth}}}}},
    connect/.style={shorten >= #1 mm, shorten <= #1 mm,  looseness=2},
}

\begin{document}
    \begin{tikzpicture}
        \coordinate (A) at (0,0); \coordinate (B) at (2,1);
        \draw[mid arrow=red,connect=10] (A) .. controls ++(0:1.75) and ++(180:1.75) .. (B) ;
        \draw[mid arrow=black,orange] (B) .. controls ++(0:1.75) and ++(180:1.75) .. (A) ;
        \draw[blue] (2,0) arc (0:45:2) ;
        \draw[blue,mid arrow=red,connect=10] (2,0) arc (0:45:2) ;
    \end{tikzpicture}
\end{document}
  • I see your idea. However, the tangential coordinate system scope seems to be tangential to the not shortened path, as it can be seen drawing a longer tangent segment as mark instead of an arrow \draw[#1] (0,0)--(5mm,0pt);. As if the decoration were done at first and the shortening of the path (changing its shape) after. Am I completely mistaken and/or is there a way to flip the order of these operations? – Axel Krypton Mar 10 '18 at 17:32
  • By the way, your GIF is very cool. I could spend hours looking at it. I think I will start making "arrow races" on tikz paths. :) – Axel Krypton Mar 10 '18 at 17:35
  • A race with no suspense – Tarass Mar 10 '18 at 17:44
  • @AxelKrypton Last workaround and edit. – Tarass Mar 10 '18 at 20:48
  • I edited your answer (without changing it) to make it more pedagogical for the new reader. Thanks for the added information. With shorten of 10 the problem is manifest. I still do not perfectly understand why to create the new, shortened path and then decorate it (if possible) would still not fix the problem. In any case, it seems there is not a real solution. Does it mean that it is better to avoid to combine shorten and decoration together as much as possible (e.g. calculating first the starting and ending point of the shortened path and then using a normal decorated path)? – Axel Krypton Mar 10 '18 at 22:26

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.