# Drawing a node surrounding arbitrarily places nodes

I'm trying to create a (flexible) method of creating a path around a set of nodes. Let me demonstrate with the help of a MWE:

\begin{tikzpicture}[
every node/.style={draw,black},
every path/.style={red}
]

\node at (0,0) (a) {A};
\node at (2,0) (b) {B};
\node at (3,0) (c) {C};
\node at (2,-1) (e) {E};
\node at (3,-1) (f) {F};
\node at (0,-1) (d) {D};

\path [draw, rounded corners]
(a.north west)
-- (c.north east)
-- (f.south east)
-- (e.south west)
-- (b.south west)
-- (a.south west)
-- cycle;

\end{tikzpicture}


Here's what it looks like:

There are two problems here:

1. The surrounding node is too closely placed on the anchors. I need something like an inner sep but that doesn't work since this is a path, not a node.
2. The nodes to be surrounded have to be specified explicitly. This becomes very difficult to manage if we have a lot of nodes to surround and they're moved around at a later stage.

So, what I need is a better workflow for doing something like this to address the above two issues.

Update: After the answer by @ClaudioFiandrino, I would like to post what I need. I hacked around with my limited knowledge and arrived at this:

\newcommand{\shiftpoints}{4pt}
\begin{tikzpicture}[
every node/.style={draw,black},
every path/.style={red},
shifttl/.style={shift={(-\shiftpoints,\shiftpoints)}},
shifttr/.style={shift={(\shiftpoints,\shiftpoints)}},
shiftbl/.style={shift={(-\shiftpoints,-\shiftpoints)}},
shiftbr/.style={shift={(\shiftpoints,-\shiftpoints)}},
]

\node at (0,0) (a) {A};
\node at (2,0) (b) {B};
\node at (3,0) (c) {C};
\node at (2,-1) (e) {E};
\node at (3,-1) (f) {F};
\node at (0,-1) (d) {D};

\begin{scope}[transform shape]
\path [draw,rounded corners]
([shifttl] a.north west)
-- ([shifttr] c.north east)
-- ([shiftbr] f.south east)
-- ([shiftbl] e.south west)
-- ([shiftbl] b.south west)
-- ([shiftbl] a.south west)
-- cycle;
\end{scope}

\end{tikzpicture}


Which produces this:

So, any suggestions for improvement of the code. The end result seems ok for my purposes.

• Use the following as the path command inside a scope \path [draw, transform canvas={shift={(2,0)},xscale=1.1,yscale=1.2},transform canvas={shift={(-2.05,0)}},rounded corners] and strange things happen :) Nov 12, 2012 at 17:31
• Thanks. That kind of works if I nudge things around but I can't get it just right. I can probably get this to work but I can't believe that there isn't a simpler "tikzy" solution to this. Nov 13, 2012 at 15:16

One simple solution is given by Jake in padded boundary of convex hull.

The code:

\documentclass[tikz,border=2bp]{standalone}
\usetikzlibrary{calc,trees}
\newcommand{\convexpath}[2]{
[
create hullnodes/.code={
\global\edef\namelist{#1}
\foreach [count=\counter] \nodename in \namelist {
\global\edef\numberofnodes{\counter}
\node at (\nodename) [draw=none,name=hullnode\counter] {};
}
\node at (hullnode\numberofnodes) [name=hullnode0,draw=none] {};
\pgfmathtruncatemacro\lastnumber{\numberofnodes+1}
\node at (hullnode1) [name=hullnode\lastnumber,draw=none] {};
},
create hullnodes
]
($(hullnode1)!#2!-90:(hullnode0)$)
\foreach [
evaluate=\currentnode as \previousnode using \currentnode-1,
evaluate=\currentnode as \nextnode using \currentnode+1
] \currentnode in {1,...,\numberofnodes} {
-- ($(hullnode\currentnode)!#2!-90:(hullnode\previousnode)$)
let \p1 = ($(hullnode\currentnode)!#2!-90:(hullnode\previousnode) - (hullnode\currentnode)$),
\n1 = {atan2(\y1,\x1)},
\p2 = ($(hullnode\currentnode)!#2!90:(hullnode\nextnode) - (hullnode\currentnode)$),
\n2 = {atan2(\y2,\x2)},
\n{delta} = {-Mod(\n1-\n2,360)}
in
{arc [start angle=\n1, delta angle=\n{delta}, radius=#2]}
}
-- cycle
}

\begin{document}

\begin{tikzpicture}[
every node/.style={draw,black},
every path/.style={red},
scale=2,
transform shape
]

\node at (0,0) (a) {A};
\node at (2,0) (b) {B};
\node at (3,0) (c) {C};
\node at (2,-1) (e) {E};
\node at (3,-1) (f) {F};
\node at (0,-1) (d) {D};

\draw \convexpath{a,b,c,f,e}{12pt};

\end{tikzpicture}
\end{document}


The result:

The problem n. 1 can be addressed by changing the second argument of the \convexpath command.

In case you wonder a smooth alternative, you can proceed as stated in Hobby path realization in convex hull approach. It is, again, something automated:

\documentclass[tikz,border=2bp]{standalone}
\usetikzlibrary{calc,trees,hobby}

\newcommand{\hobbyconvexpath}[2]{
[
create hobbyhullnodes/.code={
\global\edef\namelist{#1}
\foreach [count=\counter] \nodename in \namelist {
\global\edef\numberofnodes{\counter}
\node at (\nodename)
[draw=none,name=hobbyhullnode\counter] {};
}
\node at (hobbyhullnode\numberofnodes)
[name=hobbyhullnode0,draw=none] {};
\pgfmathtruncatemacro\lastnumber{\numberofnodes+1}
\node at (hobbyhullnode1)
[name=hobbyhullnode\lastnumber,draw=none] {};
},
create hobbyhullnodes
]
($(hobbyhullnode1)!#2!-90:(hobbyhullnode0)$)
\pgfextra{
\gdef\hullpath{}
\foreach [
evaluate=\currentnode as \previousnode using int(\currentnode-1),
evaluate=\currentnode as \nextnode using int(\currentnode+1)
] \currentnode in {1,...,\numberofnodes} {
\xdef\hullpath{\hullpath
..($(hobbyhullnode\currentnode)!#2!180:(hobbyhullnode\previousnode)$)
..($(hobbyhullnode\nextnode)!0.5!(hobbyhullnode\currentnode)$)}
\ifx\currentnode\numberofnodes
\xdef\hullpath{\hullpath .. cycle}
\else
\xdef\hullpath{\hullpath
..($(hobbyhullnode\nextnode)!#2!-90:(hobbyhullnode\currentnode)$)}
\fi
}
}
\hullpath
}

\begin{document}

\begin{tikzpicture}[
every node/.style={black},
every path/.style={red},
scale=3,
transform shape,
use Hobby shortcut
]

\node at (0,0) (a) {A};
\node at (2,0) (b) {B};
\node at (3,0) (c) {C};
\node at (2,-1) (e) {E};
\node at (3,-1) (f) {F};
\node at (0,-1) (d) {D};

\draw \hobbyconvexpath{a,b,c,f,e}{12.5pt};

\end{tikzpicture}
\end{document}


The result:

• Thanks. That looks good although not exactly what I need. The issue I have with this is that it looks too complicated. I'll see if someone else can suggest a better approach. Nov 12, 2012 at 15:54
• @recluze: I don't understand why you find \convexpath{a,b,c,f,e}{12pt} or \hobbyconvexpath{a,b,c,f,e}{12.5pt} complicated. The underlying code may be complicated but this is true for all the tikz code and shouldn't matter for you: you only need to copy&paste it. Nov 12, 2012 at 17:32
• @recluze: Ulrike is right; once you copied the code that realize the paths in your preamble you're done: you can forget about it because in the document you really need just one command with a list of nodes and a dimension to fix how much the path is far from the node base. Anyway: automated ways usually comes at some cost, never for free. :) Nov 12, 2012 at 17:44
• @ClaudioFiandrino Apologies if I sounded ungrateful. Your solution is definitely good but I want something that more closely resembles the output I've put in the question. The path I need should be as "organic" as the hobbyconvexpath you've described. The convexpath is much better but it has that diagonal line in the bottom left that I don't want. Nov 13, 2012 at 15:10
• @recluze: no problems. :) I see your need, but unfortunately it's not easy for an automated tool to know that in a given point it should deviate. At least, I cannot figure out a simple solution to let \convexpath be aware of so. Nov 13, 2012 at 17:55

a relatively simple solution with concise code:

\documentclass[tikz, margin=3mm]{standalone}
\usetikzlibrary{calc, chains, positioning}

\begin{document}
\newcommand{\shiftpoints}{4pt}
\begin{tikzpicture}[
node distance = 5mm,
start chain = A going right,
every node/.style = {draw, minimum size=7mm, outer sep=1mm,
on chain=A},
]
% nodes
\node   {A};                    % A-1
\node [coordinate]  {};
\node   {B};
\node   {C};                    % A-4
\node[below=of A-1]     {E};    % A-5
\node [coordinate]  {};
\node   {F};
\node   {D};                    % A-8
% line groping nodes
\path[draw=red, semithick, rounded corners]
(A-1.north) -| (A-8.south east)
-| (A-3.south west)
-| (A-1.west) |-(A-1.north);
\end{tikzpicture}
\end{document}