Draw stroke inside/outside of path for arbitrary shapes and nodes

This is a followup question of Is there a way to draw TikZ lines on the “inside” or “outside” of a path?

Since the other question is quite old I want to ask, if TikZ in the meantime is able to draw a stroke inside, on or outside of a path, like in this picture?

This was generated with the following code using \pgflinewidth to change the actual path. But this solution doesn’t work for arbitrary shapes and not for nodes, furthermore it is broken when scaling the picture.

\documentclass[border=5mm,tikz]{standalone}

\usetikzlibrary{calc}

\begin{document}
\begin{tikzpicture}[
every node/.style = {below=5mm, text=black, font=\small},
%   scale = 1.5,
]
% path
\draw [red] (0,0) rectangle +(2,2)
+(1,0) node {actual path};
% stroke on path
\draw [line width = 4mm] (3,0) rectangle +(2,2);
\draw [red] (3,0) rectangle +(2,2)
+(1,0) node {stroke on path};
% stroke inside path
\draw [line width = 4mm]
($(6,0)+(\pgflinewidth/2,\pgflinewidth/2)$) rectangle
+($(2,2)-(\pgflinewidth,\pgflinewidth)$);
\draw [red] (6,0) rectangle +(2,2)
+(1,0) node {stroke inside path};
% stroke outsie path
\draw [line width = 4mm]
($(9,0)-(\pgflinewidth/2,\pgflinewidth/2)$) rectangle
+($(2,2)+(\pgflinewidth,\pgflinewidth)$);
\draw [red] (9,0) rectangle +(2,2)
+(1,0) node {stroke outside path};
\end{tikzpicture}
\end{document}

Actually I need it for drawing a row of nodes where some of them are filled and some are drawn, but the total hight should be equal and they should’t overlap. This is how it should not look like:

Instead the stroke of “State B” and “State C” should lie inside of the path. (The postaction is only for demonstration.)

Code:

\begin{tikzpicture}[
every node/.style = {
inner sep=0pt, outer sep=0pt, postaction = {draw, red, thin},
minimum height = 8mm, anchor = south west, font=\small
},
a/.style = {fill = gray},
b/.style = {draw, line width = 2mm},
c/.style = {draw, line width = 2mm, shape = signal },
]
\node at (0,0) [minimum width = 20mm, a] {State A};
\node at (2,0) [minimum width = 20mm, b] {State B};
\node at (4,0) [minimum width = 30mm, a] {State A};
\node at (7,0) [minimum width = 30mm, c] {State C};
\draw [->] (0,-0.5) -- +(10.5,0);
\foreach \x in {0,0.5,...,10}
\draw (\x,-0.6) -- (\x,-0.4);
\end{tikzpicture}
• Enlarging State A is much simpler – percusse Feb 4 '16 at 11:56
• @percusse: Yes maybe for rectangle shapes but in any cases the problem will be that the overlap is still there. If I move the left/right side of State A it will start/end at the wrong position of the x axis. – Tobi Feb 4 '16 at 12:41
• If you are OK with PGF syntax you can do it with tex.stackexchange.com/questions/53184/… – percusse Feb 4 '16 at 13:17
• You really should include all the details in the first revision, because your question now might require a completely different solution, making existing answers incomplete —or wrong— in the process. Not a huge problem, but sometimes solutions can require a lot of work, so... :P – Alenanno Feb 4 '16 at 14:55
• @Alenanno: Sorry. I wanted to keep the example code simple and clean but I also said in the first revision that it us about arbitrary shapes ;-) – Tobi Feb 4 '16 at 20:42

It is somewhat easy to come up with a decoration which draws the outside of a path. In principle, one could reverse the path to get the curve drawn inside but that has some unforeseen consequences.

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{calc,decorations}

\pgfdeclaredecoration{stroke outside path}{initial}{%
\state{initial}[width=\pgfdecoratedinputsegmentlength/100,next state=iterate]
{
\pgfpathmoveto{\pgfqpoint{0pt}{.5\pgflinewidth}}
}%
\state{iterate}[width=\pgfdecoratedinputsegmentlength/100]
{
\pgfpathlineto{\pgfqpoint{.5\pgflinewidth}{.5\pgflinewidth}}
}%
\state{final}{%
}%
}%

\begin{document}
\begin{tikzpicture}[
every node/.style = {below=5mm, text=black, font=\small},
%   scale = 1.5,
]
% path
\draw [red] (0,0) rectangle +(2,2)
+(1,0) node {actual path};
% stroke on path
\draw [line width = 4mm] (3,0) rectangle +(2,2);
\draw [red] (3,0) rectangle +(2,2)
+(1,0) node {stroke on path};
% stroke inside path
\draw [decoration={reverse path, stroke outside path}, decorate, line width = 4mm] (6,0) rectangle +(2,2);
\draw [red] (6,0) rectangle +(2,2)
+(1,0) node {stroke inside path};
% stroke outsie path
\draw [decoration=stroke outside path, decorate, line width = 4mm] (9,0) rectangle +(2,2);
\draw [red] (9,0) rectangle +(2,2)
+(1,0) node {stroke outside path};
\end{tikzpicture}

\begin{tikzpicture}
\draw [decoration=stroke outside path, decorate, line width = 4mm] (0,0) .. controls (1,-1) and (2,2) .. (3,0);
\draw [red] (0,0) .. controls (1,-1) and (2,2) .. (3,0);
\end{tikzpicture}
\end{document}

For this solution you need to load the calc library. Then we will basically draw a line for the b style, appended after the node has been set. Without calc, the line would appear like in your example, but using it, we can add or remove the \pgflinewidth.

I haven't tested it, but it should be scalable.

Code

\documentclass[margin=10pt]{standalone}
\usepackage{pgffor}
\usepackage{subcaption}
\usepackage{tikz}

\usetikzlibrary{calc}

\begin{document}
\begin{tikzpicture}[
every node/.style = {
inner sep=0pt, outer sep=0pt, postaction = {draw, red, thin},
minimum height = 8mm, anchor = south west, font=\small
},
a/.style = {fill = gray},
b/.style = {append after command={\pgfextra{
\draw[line width=1mm] ($(\tikzlastnode.south west)+(.5\pgflinewidth,.5\pgflinewidth)$) rectangle ($(\tikzlastnode.north east)+(-.5\pgflinewidth,-.5\pgflinewidth)$);
}}},
]
\node at (0,0) [minimum width = 20mm, a] {State A};
\node at (2,0) [minimum width = 20mm, b] {State B};
\node at (4,0) [minimum width = 30mm, a] {State A};
\end{tikzpicture}
\end{document}
• Thanks, but this won’t work for arbitrary (node) shapes … – Tobi Feb 4 '16 at 12:27
• @Tobi How many arbitrary shapes will you use? And which ones? – Alenanno Feb 4 '16 at 12:42
• Actually I can’t tell. This is part of a package and the user can define the shapes. The default will use rectangles and triangles in the form of volume bars (no default shapes, I’ll define them for this purpose) – Tobi Feb 4 '16 at 12:45