30

Motivation

To put arrows inside of a TikZ path I have defined myself a TikZ style arrow inside. To make it even more portable I want it to take arguments like this

\draw[arrow inside={pos = 0.1, end = |}] (1,-0.5) -- (1,1.5);

such that I can define the position of the arrow using pos and the type of the arrow by end. If no arguments are given it should fall back to the defaults.

enter image description here

What I have tried so far

To achive the above picture, I wrote the following snippet. The problem is that the values are properly stored in the macros I defined, but are not being updated, nor do I now, how to retain the defaults.

\documentclass{article}
\pagestyle{empty}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}
\makeatletter
\tikzset{
    arrow inside/pos/.store in = \arrow@inside@pos,
    arrow inside/end/.store in = \arrow@inside@end,
    arrow inside/pos = 0.5,
    arrow inside/end = >,
    arrow inside/.style = {
        postaction = {
            decorate,
            decoration={
                markings,
                mark=at position \arrow@inside@pos with {\arrow{\arrow@inside@end}}
            }
        }
    },
}
\makeatother
\begin{document}
\[ \int_{
    \tikz[scale=0.3]{
        \path[fill=lightgray] (0,0) rectangle (1,1);
        \draw (1.5,1) -- (0,1) -- (0,0) -- (1.5,0);
        \draw[arrow inside={pos = 0.1, end = |}] (1,-0.5) -- (1,1.5);
    }
} \vec{B} \cdot \vec{n} \, df \]
\end{document}

Possibly related:

2 Answers 2

28
+100

Jake is correct in his now deleted answer. The options you pass to arrow inside are not applied as they are simply grabbed and not used (i.e. “gobbled”) similar to #1 in:

\newcommand*{\myCommand}[1]{Foobar Rhubarb}% no "#1" in its definition

You need to add #1 in your .style definition.

But instead of using the .store in handler, consider using .initial keys which simply store a value. This way, you don’t add another layer of abstraction (PGFkeys already use a specific “namespace”), no need to add your own \arrow@inside@… macros if PGFkeys already uses /tikz/arrow inside/….

To also avoid troubles with /.cd inside a .style, I’d use a specific key set arrow inside that sets the path to /tikz/arrow inside.

References

Code

\documentclass{article}
\pagestyle{empty}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}
\tikzset{
  set arrow inside/.code={\pgfqkeys{/tikz/arrow inside}{#1}},
  set arrow inside={pos/.initial=.5, end/.initial=>},
  arrow inside/.style={
    set arrow inside={#1},
    postaction={
      decorate,
      decoration={
        markings,
        mark=at position \pgfkeysvalueof{/tikz/arrow inside/pos}
             with \arrow{\pgfkeysvalueof{/tikz/arrow inside/end}}
      }
    }
  },
}
\begin{document}
\[ \int_{
    \tikz[scale=0.3]{
        \path[fill=lightgray] (0,0) rectangle (1,1);
        \draw (1.5,1) -- (0,1) -- (0,0) -- (1.5,0);
        \draw[arrow inside={pos = 0.1, end = |}] (1,-0.5) -- (1,1.5);
    }
} \vec{B} \cdot \vec{n} \, df \]
\end{document}

Output

enter image description here

But why stop there?

Sometimes users want to use more than one arrow tip along a (part of a) path which I realized in a comment should be made better.

The following solution doesn’t use a pos key anymore (this can be changed) but applies the options end and opt to a list of arrow-tip marks in a decoration.

(Note that for regular intervals, the library also provides mark=between positions <start> and <end> step <step> with <marking>.)

Code

\documentclass[tikz,border=5pt]{standalone}
\usetikzlibrary{decorations.markings}
\tikzset{
  set arrow inside/.code={\pgfqkeys{/tikz/arrow inside}{#1}},
  set arrow inside={end/.initial=>, opt/.initial=},
  /pgf/decoration/Mark/.style={
    mark/.expanded=at position #1 with
    {\noexpand\arrow[\pgfkeysvalueof{/tikz/arrow inside/opt}]
      {\pgfkeysvalueof{/tikz/arrow inside/end}}}},
  arrow inside/.style 2 args={
    set arrow inside={#1},
    postaction={decorate,decoration={
        markings,Mark/.list={#2}}}},
}
\begin{document}
\tikz[thick]
  \draw[
    arrow inside={
      end=|,
      opt=red!\pgfkeysvalueof{/pgf/decoration/mark info/sequence number}0!blue
    }{0, .05, .15, .3, .5, .7, .85, .95, 1},
  ] (0,0) to[bend left] (4,0);
\end{document}

Output

enter image description here

3
  • This is a great answer! I will reward it with a small bounty as soon as it's possible, because of the extra credit with the sequence of markings inside. (BTW: Where did Jake's answer go?) Sep 3, 2013 at 13:04
  • @HenriMenke: I deleted mine, because it doesn't add anything that Qrrbrbirlbel's doesn't cover.
    – Jake
    Sep 6, 2013 at 19:45
18
+50

I suggest a different approach, not optimal (see What do the pgfkeys key handlers .get and .store in do?), but it works. Indeed, one problem in your code is that, inside arrow style the keys do not inherit the correct path /tikz/arrow inside/key-name. To make it working you can do something like:

\[ \int_{
    \tikz[scale=0.3]{
        \path[fill=lightgray] (0,0) rectangle (1,1);
        \draw (1.5,1) -- (0,1) -- (0,0) -- (1.5,0);
        \draw[/tikz/arrow inside/pos = 0.4,/tikz/arrow inside/end = |,arrow inside] (1,-0.5) -- (1,1.5);
    }
} \vec{B} \cdot \vec{n} \, df \]

which is not so convenient IMHO. The other problem is that if you define a style doing something with keys, those keys have to be set not inside the style, but before as I did with the above code. Notice that:

\[ \int_{
    \tikz[scale=0.3]{
        \path[fill=lightgray] (0,0) rectangle (1,1);
        \draw (1.5,1) -- (0,1) -- (0,0) -- (1.5,0);
        \draw[arrow inside={/tikz/arrow inside/pos = 0.4,/tikz/arrow inside/end = |}] (1,-0.5) -- (1,1.5);
    }
} \vec{B} \cdot \vec{n} \, df \]

does not do anything as the keys still have the values 0.5 and > respectively.

So, my approach will be based on the "triple" handlers initial, get and store in and will let you use:

arrow inside={pos = 0.4,end = |}

inside \draw.

First I would define the keys:

\pgfkeys{/arrow inside/.cd,
  pos/.initial  = 0.5,
  pos/.get      = \arrow@inside@pos,
  pos/.store in = \arrow@inside@pos,
  end/.initial  = >,
  end/.get      = \arrow@inside@end,
  end/.store in = \arrow@inside@end,
}

They are under the path /arrow inside/ so later on we should take care of this.

Second, I would define a style to place the arrow:

place arrow/.style = {
    postaction = {
        decorate,
        decoration={
            markings,
            mark=at position \arrow@inside@pos with {\arrow{\arrow@inside@end}}
        }
    }
},

Rather than the keys, the style belongs to the usual /tikz/ path. To combine the keys and the aforementioned style, I define:

arrow inside/.style={place arrow,/arrow inside/.cd,#1}

as a style which "places" the arrow and, by changing the default /tikz/ path, allows you to use the previously defined keys.

A mwe:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations.markings}
\makeatletter
\pgfkeys{/arrow inside/.cd,
  pos/.initial  = 0.5,
  pos/.get      = \arrow@inside@pos,
  pos/.store in = \arrow@inside@pos,
  end/.initial  = >,
  end/.get      = \arrow@inside@end,
  end/.store in = \arrow@inside@end,
}
\tikzset{arrow inside/.style={place arrow,/arrow inside/.cd,#1},
    place arrow/.style = {
        postaction = {
            decorate,
            decoration={
                markings,
                mark=at position \arrow@inside@pos with {\arrow{\arrow@inside@end}}
            }
        }
    },
}
\makeatother
\begin{document}

\[ \int_{
    \tikz[scale=0.3]{
        \path[fill=lightgray] (0,0) rectangle (1,1);
        \draw (1.5,1) -- (0,1) -- (0,0) -- (1.5,0);
        \draw[arrow inside={pos = 0.4,end = |}] (1,-0.5) -- (1,1.5);
    }
} \vec{B} \cdot \vec{n} \, df \]


\[ \int_{
    \tikz[scale=0.3]{
        \path[fill=lightgray] (0,0) rectangle (1,1);
        \draw (1.5,1) -- (0,1) -- (0,0) -- (1.5,0);
        \draw[arrow inside={pos = 0.7,end = stealth}] (1,-0.5) -- (1,1.5);
    }
} \vec{B} \cdot \vec{n} \, df \]

\end{document}

The result:

enter image description here

Notice that the keys now update correctly their values.

2
  • I think the best explanation for this has been given by percusse in this comment. Indeed, as also my answer there demonstrates, the .get handler is needed for, let's say, the special case in which you first use the keys without passing any values (the initial ones are given) and then you use them with values. In this case, try to say \draw[arrow inside={}] (1,-0.5) -- (1,1.5); in the first example: the compilation will rise errors as both macros won't be defined. Sep 3, 2013 at 7:12
  • Thank you for your great answer. If I could I would accept both, but I will reward yours with a small bounty as soon as it's possible for giving a great insight on .get and .store in. Sep 3, 2013 at 13:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .