9

I tried to answer an interesting but underrated old question.

To make my answer work also for sloped paths, I need the coordinates of the visual back end point of an arrow.

From the TikZ manual: enter image description here enter image description here

I would like to change ([xshift=-#1]\tikzinputsegmentlast) in the following code with the coordinates of the visual back end of the arrow.

Is it possible?

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\usetikzlibrary{decorations.pathreplacing}
    
\tikzset{
  mystyle/.style={
    -{Triangle[open, length=#1]},
    decoration={
      show path construction,
      lineto code={
        \coordinate (n0) at (\tikzinputsegmentfirst);
        \coordinate (n3) at ([xshift=-#1]\tikzinputsegmentlast);
        \coordinate (n4) at (\tikzinputsegmentlast);
        \coordinate (n1) at (barycentric cs:n0=2,n3=1);
        \coordinate (n2) at (barycentric cs:n0=1,n3=2);
        \draw [-, color=red] (n0) -- (n1);
        \draw [-, color=green] (n1) -- (n2);
        \draw [-, color=blue] (n2) -- (n3);
        \draw (n3) -- (n4);
        }
      },
    decorate
  },
  mystyle/.default=5pt,
  every node/.style={circle, draw}
}

\begin{document}
\begin{tikzpicture}
\node (A) at (0,0) {A};
\node (B) at (5,0) {B};
\draw[mystyle] (A) -- (B);
\end{tikzpicture}

\begin{tikzpicture}
\node (A) at (0,0) {A};
\node (B) at (5,0) {B};
\draw[mystyle={35pt}] (A) -- (B);
\end{tikzpicture}

\begin{tikzpicture}
\node (A) at (0,0) {A};
\node (B) at (5,1) {B};
\draw[mystyle={20}] (A) -- (B); 
\end{tikzpicture}

\end{document}

As you can see, now the third picture is wrong.

enter image description here

5
+50

The positions of the visual back end is stored in

\csname pgf@ar@visual@\pgf@arrow@id\endcsname

with format

{visual tip end}{visual back end}{}

So tip minus back is the distance you want to subtract from the line.

To learn \pgf@arrow@id, you pass the arrow specification to \pgfsetarrowsend. For instance \pgfsetarrowsend{Triangle[length=1cm]}. The function of the later command is two-fold:

  • If the arrow spec has not been used before, it computes everything needed and caches.
  • If the arrow spec has been used before, it sets \pgf@arrow@id to the old id.

Either way, you know \pgf@arrow@id and now \csname pgf@ar@visual@\pgf@arrow@id\endcsname makes sense. The rest is expandafter-exercise.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\usetikzlibrary{decorations.pathreplacing}
\usetikzlibrary{calc}
\makeatletter
\def\computevisuallength#1#2#3{% visual end, visual tip, dummy
    \pgf@x#1
    \pgf@y#2
    \advance\pgf@x-\pgf@y
    \xdef\visuallength{\the\pgf@x}
}
\tikzset{
  mystyle/.style={
    decoration={
      show path construction,
      lineto code={
        \coordinate (n0) at (\tikzinputsegmentfirst);
        \coordinate (n4) at (\tikzinputsegmentlast);
        \draw[dash pattern=on0off9999,-{Triangle[open, length=#1]}] (n0) -- (n4);
        {
            \pgfsetarrowsend{Triangle[open, length=#1]}% force pgf recall this arrow
            \def\pgf@arrow@hull@point{hull}%%
            \message{^^J }
            \message{^^J arrow id: pgf@arrow@id\pgf@arrow@id}
            \message{^^J hull: \csname pgf@ar@hull@\pgf@arrow@id\endcsname}
            \message{^^J tip back: \csname pgf@ar@ends@\pgf@arrow@id\endcsname}
            \message{^^J vis tip back: \csname pgf@ar@visual@\pgf@arrow@id\endcsname}
            \message{^^J etc etc}
            \message{^^J }
            \expandafter\expandafter\expandafter\computevisuallength
            \csname pgf@ar@visual@\pgf@arrow@id\endcsname
        }
        \coordinate (n3) at ($(n4)!\visuallength!(n0)$);
        \coordinate (n1) at (barycentric cs:n0=2,n3=1);
        \coordinate (n2) at (barycentric cs:n0=1,n3=2);
        \draw [color=red] (n0) -- (n1);
        \draw [color=green] (n1) -- (n2);
        \draw [color=blue] (n2) -- (n3);
        }
      },
    decorate
  },
  mystyle/.default=5pt,
  every node/.style={circle, draw}
}

\begin{document}
\begin{tikzpicture}
\node (A) at (0,0) {A};
\node (B) at (5,0) {B};
\draw[mystyle] (A) -- (B);
\end{tikzpicture}

\begin{tikzpicture}
\node (A) at (0,0) {A};
\node (B) at (5,0) {B};
\draw[mystyle={35pt}] (A) -- (B);
\end{tikzpicture}

\begin{tikzpicture}
\node (A) at (0,0) {A};
\node (B) at (5,1) {B};
\draw[mystyle={20}] (A) -- (B); 
\end{tikzpicture}

% Repeat the second arrow, can pgf recall the parameter?
\begin{tikzpicture}
\node (A) at (0,0) {A};
\node (B) at (5,0) {B};
\draw[mystyle={35pt}] (A) -- (B);
\end{tikzpicture}

\end{document}

line before arrow is divided into three equal segments

2
  • 1
    +1 You're the arrow master. Nov 25 at 10:04
  • 1
    This is perfect, it works also with other arrow types. I can award the bounty in 7 hours.
    – CarLaTeX
    Nov 25 at 11:43
5
\coordinate (n3) at 
          ($(\tikzinputsegmentlast)!#1!(\tikzinputsegmentfirst)$);

works. This defines n3 to be a coordinate along the line from segment last and segment first, and #1 (#1 must be a length) away from last.

Bonus: The code snippet below adds support for shorten >=<length>, as well as the use case when #1 is a unit-less "length" (by making use of a "feature" of pgfmath).

\coordinate (n3) at 
          ($(\tikzinputsegmentlast)!#1+\pgf@shorten@end@additional!(\tikzinputsegmentfirst)$);

Full example

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta, calc}
\usetikzlibrary{decorations.pathreplacing}

\makeatletter
\tikzset{
  mystyle/.style={
    -{Triangle[open, length=#1]},
    decoration={
      show path construction,
      lineto code={
        \coordinate (n0) at (\tikzinputsegmentfirst);
        \coordinate (n3) at 
          ($(\tikzinputsegmentlast)!#1+\pgf@shorten@end@additional!(\tikzinputsegmentfirst)$);
        \coordinate (n4) at (\tikzinputsegmentlast);
        \coordinate (n1) at (barycentric cs:n0=2,n3=1);
        \coordinate (n2) at (barycentric cs:n0=1,n3=2);
        \begin{scope}[shorten >=0pt, arrows={-}]
          \draw[red] (n0) -- (n1);
          \draw[green] (n1) -- (n2);
          \draw[blue] (n2) -- (n3);
        \end{scope}
        % this `\draw` will add the end-of-path arrow
        \draw (n3) -- (n4);
        % control group
        \draw[help lines]
           ($ (\tikzinputsegmentfirst) + (0,.2) $) -- 
           ($ (\tikzinputsegmentlast)  + (0,.2) $);
        }
      },
    decorate
  },
  mystyle/.default=5pt,
  every node/.style={circle, draw}
}
\makeatother

\begin{document}
\begin{tikzpicture}
  \node (A) at (0,0) {A};
  \node (B) at (5,0) {B};
  \draw[mystyle] (A) -- (B);
\end{tikzpicture}

\begin{tikzpicture}
  \node (A) at (0,0) {A};
  \node (B) at (5,0) {B};
  \draw[mystyle={35pt}] (A) -- (B);
\end{tikzpicture}

\begin{tikzpicture}
  \node (A) at (0,0) {A};
  \node (B) at (5,1) {B};
  \draw[mystyle={20}] (A) -- (B);
\end{tikzpicture}

\begin{tikzpicture}
  \node (A) at (0,0) {A};
  \node (B) at (5,1) {B};
  \draw[mystyle={20}, shorten >=10pt] (A) -- (B);
\end{tikzpicture}
\end{document}

enter image description here

Remark: To support curve to, I guess something similar to \pgf@prep@curved(start|end) is required.

7
  • +1 In the old question, the arrow head was given separate from the tricolor style. I made some experiments with applying three actions in the line of decoration={lineto, pre=moveto, pre length=0.66*\pgfmetadecoratedpathlength}, decorate, blue but I can not make it work with an arrow head. Nov 20 at 10:35
  • 1
    @hpekristiansen I think the decoration never takes the path shortening into account, as shown in pgfmanual sec. 3.12 "Adding the Snaked Line and Multi-Line Text". Hence a complete answer to the old question really requires retrieving or re-calculating the shorten length. Nov 20 at 12:01
  • Thank you @muzimuzhiZ. Actually, this solves the original problem but doesn't give exactly the answer to this question (the coordinates of the visual back end). I think you should answer the original question. I'll wait a bit before accepting your answer to see if someone else gives another solution.
    – CarLaTeX
    Nov 20 at 15:58
  • Coordinate n3 is the one you need. If you prefer a standalone solution that works outside the decoration,\pgf@prepare@end@of@path can be a starter. I will try this way, maybe tomorrow. Nov 20 at 16:26
  • @muzimuzhiZ Yes, but coordinate n3 is calculated using the length passed as an argument, it is not general. For example, if I change the arrow shape, the default doesn't work anymore. Thank you if you have time to dedicate to this tomorrow.
    – CarLaTeX
    Nov 20 at 16:38

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.