[Tex/LaTex] How to draw the outline of a path in tikz

arrowstikz-pgf

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.

Best Answer

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):

arrow outlines

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}