How can I draw the outline of a path in tikz?

Using the arrow tip library, I can draw thick arrows with triangular tips, like so:

\begin{tikzpicture}
\draw[line width=2ex,
rounded corners=2ex,
triangle 90 cap reversed-triangle 90 cap
] (0,0) -| (1,1);
\end{tikzpicture}


These arrows are filled. I want just the outline of this arrow, but just using the "double" style doesn't do that. Both the start and end tip are filled:

\begin{tikzpicture}
\draw[line width=2ex,
rounded corners=2ex,
double distance between line centers=2ex,
double,
line width=1pt,
triangle 90 cap reversed-triangle 90 cap
] (0,0) -| (1,1);
\end{tikzpicture}


I think I need to create the outline of the first arrow (which is totally filled), and then draw a line along this outline. I just have no clue how I could do that.

I ended up drawing each arrow twice, as first suggested by EEva, but refined in the way that Andrew suggested. I'm using a LaTeX length to store the width of the line "around" the arrow. This width is called \DblTriCapDelta and the tikz style that uses this length is called DblTriCap, as it produces a triangluar capped arrow with double stroke:

\newlength{\DblTriCapDelta}
\setlength{\DblTriCapDelta}{1pt}
\tikzset{
DblTriCap/.style={
postaction={
draw,
color=white,
shorten >=1.4142\DblTriCapDelta,
shorten <=2.4142\DblTriCapDelta,
line width={\pgflinewidth-\DblTriCapDelta}
}
}
}


I have yet to define a key handler (never did that before). The current usage is:

\begin{tikzpicture}
\draw[line width=4mm,
rounded corners=4mm,
triangle 90 cap reversed-triangle 90 cap,
DblTriCap
] (0,0) -| (1,1);
\end{tikzpicture}


I'd like to be able to choose the fill colour, but didn't quite get it to work.

• Very nice topic, thanks for answering yourself, learned a lot today ! – MP0 May 11 '11 at 14:19
• Still not the holy grail: Drawing an arrow "the wrong way" messes up the shortenings. Move the reversed cap to the other end to see the effect: change the arrow tips to triangle 90 cap-triangle 90 cap reversed,. – Christoph May 11 '11 at 14:52

If I understand correctly what you want to do then you will need to define new arrow shapes to get the outlines. The fact that these arrows are filled is hard-wired in to their definitions. Fortunately, that's not too difficult to do. The code for these arrows is in the file pgflibraryarrows.code.tex and is:

\pgfarrowsdeclare{triangle 90 cap}{triangle 90 cap}
{\pgfarrowsleftextend{+-.1\pgflinewidth}\pgfarrowsrightextend{+\pgflinewidth}}
{
\pgfpathmoveto{\pgfqpoint{-.1\pgflinewidth}{0.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{.5\pgflinewidth}{.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{\pgflinewidth}{0pt}}
\pgfpathlineto{\pgfqpoint{.5\pgflinewidth}{-.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{-.1\pgflinewidth}{-0.5\pgflinewidth}}
\pgfusepathqfill
}


There we can see the final command is \pgfusepathqfill which fills the path just constructed. To get it drawn rather than filled, we change that to \pgfusepathqstroke. Of course, to avoid name clashes, we change the name of our arrow as well (and put this in the preamble rather than editing pgflibraryarrows.code.tex.

\pgfarrowsdeclare{triangle 90 cap outline}{triangle 90 cap outline}
{\pgfarrowsleftextend{+-.1\pgflinewidth}\pgfarrowsrightextend{+\pgflinewidth}}
{
\pgfpathmoveto{\pgfqpoint{-.1\pgflinewidth}{0.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{.5\pgflinewidth}{.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{\pgflinewidth}{0pt}}
\pgfpathlineto{\pgfqpoint{.5\pgflinewidth}{-.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{-.1\pgflinewidth}{-0.5\pgflinewidth}}
\pgfusepathqstroke
}


But even this doesn't work as you want since you presumably want only some of the edges drawn and although one edge is left off (since a fill implicitly closes the path) it isn't the right one. Moreover, the line width is inherited from the main path which definitely isn't right as you want it to be the width of the "left over" piece from the doubled stroke. So we have to mess around a bit with the order in which pieces of the arrow are drawn. Moreover, to be fully flexible we should ensure that the line width is set correctly (incidentally, your two declarations of line width are unnecessary; the second trumps the first).

Summing all that up, the following adapts the arrow heads to provide an outline. It's in a \makeatletter ... \makeatother chunk because I use a couple of temporary storage slots that have @s in their names.

Here's what I got (your original on the left and my variant on the right):

Here's the code (the arrows library is only needed for your example):

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows}

\makeatletter

\pgfarrowsdeclare{triangle 90 cap outline}{triangle 90 cap outline}
{\pgfarrowsleftextend{+-.1\pgflinewidth}\pgfarrowsrightextend{+\pgflinewidth}}
{
\pgfmathsetlength{\pgfutil@tempdima}{.5 * \pgflinewidth + .5 * \pgfinnerlinewidth}
\pgfsetlinewidth{\pgfutil@tempdima}
\pgfpathmoveto{\pgfqpoint{-.1\pgflinewidth}{.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{.5\pgflinewidth}{0.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{1\pgflinewidth}{0\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{.5\pgflinewidth}{-.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{-.1\pgflinewidth}{-.5\pgflinewidth}}
\pgfmathsetlength{\pgfutil@tempdimb}{\pgflinewidth -  \pgfinnerlinewidth}
\pgfsetlinewidth{\pgfutil@tempdimb}
\pgfusepathqstroke
}

\pgfarrowsdeclare{triangle 90 cap reversed outline}{triangle 90 cap reversed outline}
{\pgfarrowsleftextend{+-.1\pgflinewidth}\pgfarrowsrightextend{+\pgflinewidth}}
{
\pgfmathsetlength{\pgfutil@tempdima}{.5 * \pgflinewidth + .5 * \pgfinnerlinewidth}
\pgfsetlinewidth{\pgfutil@tempdima}
\pgfpathmoveto{\pgfqpoint{-.1\pgflinewidth}{.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{1\pgflinewidth}{0.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{0.5\pgflinewidth}{0\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{1\pgflinewidth}{-.5\pgflinewidth}}
\pgfpathlineto{\pgfqpoint{-.1\pgflinewidth}{-.5\pgflinewidth}}
\pgfmathsetlength{\pgfutil@tempdimb}{\pgflinewidth -  \pgfinnerlinewidth}
\pgfsetlinewidth{\pgfutil@tempdimb}
\pgfusepathqstroke
}
\makeatother

\begin{document}
\begin{tikzpicture}
\draw[
rounded corners=2ex,
double distance between line centers=2ex,
double,
line width=1pt,
triangle 90 cap reversed-triangle 90 cap
] (0,0) -| (1,1);
\draw[
rounded corners=2ex,
double distance between line centers=2ex,
double,
line width=1pt,
triangle 90 cap reversed outline-triangle 90 cap outline
] (2,0) -| ++(1,1);
\end{tikzpicture}
\end{document}

• That's cool! I'll use it for now. If there's no way of getting the "outline of a drawn line", I'll accept it as an answer to the "arrow" part of the question. – Christoph May 11 '11 at 9:38
• Good answer! I still see a faint line across the arrow near its head, though. What's that about? – Matthew Leingang May 11 '11 at 9:41
• That's a way more complete answer than the other one clearly. I'm working on my level, always glad to learn new things :) – MP0 May 11 '11 at 9:45
• @Christoph: It is possible to get the "outline of a drawn line" but to do that you have to get at the path between when it is defined and when it is used. The arrows are used straight after they are defined so it isn't possible to interrupt this without some sort of hackery, in which case it is simpler just to define a new arrowhead. (One way to draw the outline would be to use my calligraphy method, but that wouldn't work with the arrows for this very reason.) – Andrew Stacey May 11 '11 at 9:49
• @Matthew: That's a feature of the doubled line. If you draw a doubled line using the double feature then sometimes you can see a shadow at the end. What really ought to happen is that the "blanking out" line extends ever so slightly beyond the underlying line to ensure that that doesn't happen (though I can forsee issues with that as well). – Andrew Stacey May 11 '11 at 9:50

I've also looked for line caps that were just empty, but couldn't find anything in the manual.

However, here is a cheap (but working) solution:

\begin{tikzpicture}
\draw[line width=2ex,
rounded corners=2ex,
triangle 90 cap reversed-triangle 90 cap
] (0,0) -| (1,1);
\draw[line width=1.5ex, color=white,
rounded corners=2ex,
triangle 90 cap reversed-triangle 90 cap
] (0.1,0) -| (1,.95);
\end{tikzpicture}


I just drew the first example two times, one black, one white and moved the borders a bit.

Thanks to Andrew Stacey (in the comments), this can be shorter and nicer, using postaction:

\begin{tikzpicture}
\draw[line width=2ex,
rounded corners=2ex,
triangle 90 cap reversed-triangle 90 cap,
postaction={draw,color=white,line width=1.5ex,shorten >=.25ex,shorten <=.5ex}
] (0,0) -| (1,1);
\end{tikzpicture}

• I do not like this solution, as it means a lot of work and needs to be adjusted for every change to an arrow. – Christoph May 11 '11 at 9:30
• Doubling a path does this very thing on a lower level. – Matthew Leingang May 11 '11 at 9:42
• Actually, this is a very nice solution. As Matthew says, this is effectively what the double option does. It will only work for certain shapes - where contracting the shape to the centre leaves the right borderline (star-like shapes) but that's most of them! The only refinement I would make would be to use the shorten > and shorten < keys to shorten the lines rather than changing the coordinates explicitly. With only a little work, one could set up a "key" to set all the parameters correctly. – Andrew Stacey May 11 '11 at 9:53
• I'll try to come up with such a key, combining @Andrew's and @Eeva's answers. That way, it's possible to get a filled, outlined arrow. – Christoph May 11 '11 at 9:58
• Indeed, with a postaction it's possible to collapse this quite a lot while keeping the idea of the solution. If you put postaction={draw,color=white,line width=1.5ex,shorten >=.25ex,shorten <=.5ex} on the first path (and remove the second) then you get pretty much the same result and it's much more flexible (though one has to experiment a little to get the right shortenings). – Andrew Stacey May 11 '11 at 9:59

I too needed a double stroked (or rather, "filled and stroked") thick arrow line; so here is a slight modification of the snippet in answer by @EEva that uses postaction - with controllable colors, width, and outline width; it produces something like this:

I left the transparency, so it can be seen how arrows are attached to ends. Code attempts to tune the shortens, but if you change the arrow type (and even if not), you'll probably have to finetune manually. The code is:

\documentclass[varwidth]{standalone}

\usepackage{tikz}

\pagecolor{yellow!15}
\begin{document}
\begin{tikzpicture}

\tikzset{%
% use: dblarw={color}{totalouterwidth}{outlineinsidewidth}
dblarw/.style n args={3}{%,
-latex,
line width=#2,
draw=black,  % this draw and color could
color=black, % be an additional style arg
opacity=0.9,
% note: just color=#1 makes all black!
% fill=#1 makes insides (in an open! curve) filled too!
% draw=#1,color=#1, seems to work, though
postaction={
draw=#1,
color=#1,
line width=#2-#3,
shorten >=2*#3,
shorten <=#3,
},
dblarw/.default={gray}{5pt}{1pt},
dblarw/.initial={gray}{5pt}{1pt},
}
}

% unfortunately, must set both style args explicitly;
% cannot do dblarw={green} or dblarw={green}{}{}
\draw[dblarw={green}{5pt}{1pt}]
(0,0) -- (4,4);
\draw[dblarw={red}{5pt}{1pt}]
(0,4) ..controls +(east:4) and +(west:4).. (4,0);
\draw[dblarw={blue}{8pt}{3pt}]
(4,3) -- (0,1);

\end{tikzpicture}
\end{document}