Small text near arrow tips

How can I add some tiny text near arrow tips?

Details

I have an automaton like

Now I want each edge to have the usual arrow tip, and close to the tip additionally a tiny text $\tau$. How can I do that?

A nice and efficient solution would be great: Automatic positioning in a consistent manner, such that it becomes immediately visibly obvious that all arrows are of the type \underset{\scriptstyle \tau}{\Longrightarrow}. These criteria are more important than the exact positioning, but here's an example (with \tau^*, what I actually need) nevertheless:

Better yet: with some text smaller than \tiny, to set it apart from the other text:

I tried

(s0) edge[bend left] node[pos=0.3] {$a$} node[pos=0.9,swap] {$\tau^*$} (s1)


but nice positioning is very difficult that way.

Minimal example:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{automata}

\begin{document}
\begin{tikzpicture}
\node[state,inner sep=0pt,minimum size=.7cm] at (0,0) (s0) {$s_0$};
\node[state,inner sep=0pt,minimum size=.7cm] at (-1,-1) (s1) {$s_1$};

\path[->]
(s0)+(-.5cm,.5cm) edge[bend right,double] (s0)
(s0) edge[bend left,double] node[pos=0.3, auto] {$a$} (s1)
(s1) edge[bend left,double] node [auto] {$\epsilon,a$} (s0)
(s0) edge[loop above,double] node {$\epsilon,a$} (s0)
(s1) edge[loop below,double] node {$\epsilon,a$} (s1)
;
\end{tikzpicture}
\end{document}

-
What's your niceness metric? Left of it right of it? Very close to the arrow head? Very close to the arrow tip? – percusse Jun 12 '13 at 16:17
Actually, the exact position is not that relevant for me, rather that it is consistent, and that it becomes immediately visibly obvious that all arrows are of the same type. I've updated my post accordingly. – DaveBall aka user750378 Jun 12 '13 at 16:36
Sorry for the follow-up but can you manually place a tau that you wish to have? then we will get an idea what the context is. – percusse Jun 12 '13 at 16:41
How automatic does it need to get? You could just add node[pos=.95, auto, inner sep=+2pt, font=\scriptsize] {$\tau^*$} (maybe as an short insert path to use it repeatedly easier, or even more automatic?) to the edges? Or, if you want a fixed distance from the end, you can use the decorations.markings library with say mark=at position -5pt with {…}. – Qrrbrbirlbel Jun 12 '13 at 18:34
Ok, ok, here are +50, Qrrbrbirlbel ;) – DaveBall aka user750378 Jun 14 '13 at 14:46

Automatic placement is not that easy to do because text is for TikZ simply a rectangular black box. We don’t know where in the text white space is that can be occupied by a line or an arrow tip and we also don’t know where the actual text is. This means that for the same placement options but for different angles (at that point on the path where the node shall be placed) we get very differently appealing results.

One work around is to rotate the nodes along the path so that they are placed the same relatively to the path itself. This is done in example 1 with the transform shape option (which may have disadvantages combined with other options like scale).

Admittedly, this may or may not be hard to read and understand. Example 2 shows nearly the same picture but without transform shape. The most upper-right node has to be raised so that it doesn’t overlap with the arrow tip.

A rather different approach is used in example 3 where the arrow text* key is used that calculates an angle the local coordinate system is rotated. The anchor of the placed node is then set in such a way that this anchor is nearest to the path, meaning that orthogonal to the path all node contents are farther away than this anchor. The circle shape is used to garuantee this (the corners of the un-transformed rectangular shape may protrude into the path again). This doesn’t help though against a big arrow tip.

Of course, if another path or another node is in the way TikZ and the decoration can’t prevent any overlapping.

This is by far not an idea solution but it could be made easier to adjust in certain instances by using arrow text=<optional additional distance>;<optional additional raise>:<optional alternative text> or something in that way.

Code

\documentclass[tikz,convert]{standalone}
\usetikzlibrary{automata,decorations.markings}
\makeatletter
\tikzset{
arrow text/.style={
decoration={
name=markings,
mark=at position -\pgfkeysvalueof{/tikz/arrow text distance} with {%
\node[every arrow text node/.try] {$#1$};
}
},
postaction=decorate
},
arrow text*/.style={
decoration={
name=markings,
mark=at position -\pgfkeysvalueof{/tikz/arrow text distance} with {%
\pgfinterruptboundingbox
\pgfnode{coordinate}{center}{}{qrr@arrowtext@origin}{}%
\pgftransformshift{\pgfpointxy{0}{1}}%
\pgfnode{coordinate}{center}{}{qrr@arrowtext@down}{}%
\endpgfinterruptboundingbox
\begingroup
\pgftransformreset
\tikz@scan@one@point\pgfutil@firstofone(qrr@arrowtext@origin)\relax
\pgf@xa\pgf@x\pgf@ya\pgf@y
\tikz@scan@one@point\pgfutil@firstofone(qrr@arrowtext@down)\relax
\csname pgfmathatan2\endcsname{+\pgf@xa}{+\pgf@ya}%
\pgfmath@smuggleone\pgfmathresult
\endgroup
\let\qrr@arrowtext@anchor\pgfmathresult
\node[every arrow text node/.try, anchor/.expanded=\qrr@arrowtext@anchor] {$#1$};
}
},
postaction=decorate
},
arrow text*/.default={\tau\mkern-2mu^*},
arrow text/.default={\tau\mkern-2mu^*},
arrow text distance/.initial=3pt,
arrow text raise/.style={/pgf/decoration/raise={#1}},
every arrow text node/.style={font=\scriptsize,inner sep=+0pt,anchor=center},
arrow text raise=5pt
}
\makeatother
\begin{document}
\begin{tikzpicture}[every arrow text node/.append style={transform shape}]
\node[state,inner sep=0pt,minimum size=.7cm] at (0,0) (s0) {$s_0$};
\node[state,inner sep=0pt,minimum size=.7cm] at (-1,-1) (s1) {$s_1$};

\path[->, every edge/.append style={double, arrow text}]
(s0)+(-.5cm,.5cm) edge[bend right, arrow text distance=10pt]                                  (s0)
(s0) edge[bend left, ]                        node[pos=0.3, auto] {$a$}          (s1)
edge[loop above,]                        node                {$\epsilon,a$} (s0)
(s1) edge[bend left, arrow text distance=6pt] node[auto]          {$\epsilon,a$} (s0)
edge[loop below,]                        node                {$\epsilon,a$} (s1)
;
\end{tikzpicture}

\begin{tikzpicture}
\node[state,inner sep=0pt,minimum size=.7cm] at (0,0) (s0) {$s_0$};
\node[state,inner sep=0pt,minimum size=.7cm] at (-1,-1) (s1) {$s_1$};

\path[->, every edge/.append style={double, arrow text}]
(s0)+(-.5cm,.5cm) edge[bend right, arrow text distance=10pt]                                    (s0)
(s0) edge[bend left, ]                          node[pos=0.3, auto] {$a$}          (s1)
edge[loop above, arrow text raise=10pt]    node                {$\epsilon,a$} (s0)
(s1) edge[bend left,  arrow text distance=6pt]  node[auto]          {$\epsilon,a$} (s0)
edge[loop below,]                          node                {$\epsilon,a$} (s1)
;
\end{tikzpicture}

\begin{tikzpicture}[
every arrow text node/.append style={shape=circle},
arrow text raise=1pt
]
\node[state,inner sep=0pt,minimum size=.7cm] at (0,0) (s0) {$s_0$};
\node[state,inner sep=0pt,minimum size=.7cm] at (-1,-1) (s1) {$s_1$};

\path[->, every edge/.append style={double, arrow text*}]
(s0)+(-.5cm,.5cm) edge[bend right, arrow text distance=10pt]                                    (s0)
(s0) edge[bend left, ]                          node[pos=0.3, auto] {$a$}          (s1)
edge[loop above,]                          node                {$\epsilon,a$} (s0)
(s1) edge[bend left, arrow text distance=6pt]   node[auto]          {$\epsilon,a$} (s0)
edge[loop below,]                          node                {$\epsilon,a$} (s1)
;
\end{tikzpicture}
\end{document}


Output

-
AWESOME, thx Qrrbrbirlbel :) – DaveBall aka user750378 Jun 21 '13 at 13:58
I have tried your second approach, and it works great: I only had to manually move 2 out of 12 arrow texts :) – DaveBall aka user750378 Jun 21 '13 at 14:25
I do not fully understand how to use the optional parameters, though: e.g. edge[arrow text=6pt;4pt] does not work. So I'm using edge[arrow text distance=6pt, arrow text raise=4pt]. Is that how I'm supposed to do it? – DaveBall aka user750378 Jun 21 '13 at 14:27
@DaveBallakauser750378 Currently, yes. The improved syntax with ; and : was just an example of how to improve the input. But I don’t know exactly what your use-cases are or how often which parameter needs to be adjusted manually. It would be best to update your question with a few sentences regarding these points. Then I can see how easy it would be to implement these (when I’ll find the time). – Qrrbrbirlbel Jun 21 '13 at 14:58
Oh, ok, now I understand your text. Since I need it very rarely, edge[arrow text distance=6pt, arrow text raise=4pt] is just fine. – DaveBall aka user750378 Jun 21 '13 at 15:00