Motivation
In the answer
Highlight a group of nodes in a tikz tree, Jake suggested combining the convex hull approach from padded boundary of convex hull with the hobby path and I was really intrigued by the possibility.
Preliminary work
At first I tried to modify at least as possible the \convexpath
:
\documentclass[a4paper,11pt]{article}
\usepackage{tikz}
\usetikzlibrary{hobby,backgrounds,calc,trees}
\newcommand{\myconvexpath}[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)$)
\foreach [
evaluate=\currentnode as \previousnode using \currentnode-1,
evaluate=\currentnode as \nextnode using \currentnode+1
] \currentnode in {1,...,\numberofnodes} {
let \p1 = ($(hobbyhullnode\currentnode)!#2!-90:(hobbyhullnode\previousnode) - (hobbyhullnode\currentnode)$),
\n1 = {atan2(\x1,\y1)},
\p2 = ($(hobbyhullnode\currentnode)!#2!90:(hobbyhullnode\nextnode) - (hobbyhullnode\currentnode)$),
\n2 = {atan2(\x2,\y2)},
\n{delta} = {-Mod(\n1-\n2,360)}
in
{arc [start angle=\n1, delta angle=\n{delta}, radius=#2]}
..($(hobbyhullnode\nextnode)!0.5!(hobbyhullnode\currentnode)$)
..($(hobbyhullnode\nextnode)!#2!-90:(hobbyhullnode\currentnode)$)
}
--cycle
}
\begin{document}
\begin{tikzpicture}[use Hobby shortcut]
\node (f) {f}
child { node (g) {g}
child { node (a) {a}
}
child { node (b) {b}
}
}
child { node (h) {h}
child { node (c) {c}
}
};
\begin{pgfonlayer}{background}
\fill[draw,blue, opacity=0.3] \myconvexpath{f,h,c,g}{12pt};
\fill[draw,red, opacity=0.3] \myconvexpath{g,b,a}{12pt};
\end{pgfonlayer}
\end{tikzpicture}
\end{document}
which leads to:
I suspected the combination of arcs
with the hobby path was the cause of cusps, so in another example I tried with:
\documentclass[a4paper,11pt]{article}
\usepackage{tikz}
\usetikzlibrary{hobby,backgrounds,calc,trees}
\newcommand{\myconvexpath}[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)$)
\foreach [
evaluate=\currentnode as \previousnode using \currentnode-1,
evaluate=\currentnode as \nextnode using \currentnode+1
] \currentnode in {1,...,\numberofnodes} {
let \p1 = ($(hobbyhullnode\currentnode)!#2!-90:(hobbyhullnode\previousnode)$),
\n1 = {atan2(\x1,\y1)},
\p2 = ($(hobbyhullnode\currentnode)!#2!90:(hobbyhullnode\nextnode)$),
\n2 = {atan2(\x2,\y2)},
\n{delta} = {-Mod(\n1-\n2,360)},
\n{end}={add(\n1,\n{delta})}
in
{..([in angle=\n1]$(hobbyhullnode\currentnode)!#2!-90:(hobbyhullnode\previousnode)$)..([out angle=\n{end}]$(hobbyhullnode\currentnode)!#2!90:(hobbyhullnode\nextnode)$)}
..($(hobbyhullnode\nextnode)!0.5!(hobbyhullnode\currentnode)$)
..($(hobbyhullnode\nextnode)!#2!-90:(hobbyhullnode\currentnode)$)
}
--cycle
}
\begin{document}
\begin{tikzpicture}[use Hobby shortcut]
\node (f) {f}
child { node (g) {g}
child { node (a) {a}
}
child { node (b) {b}
}
}
child { node (h) {h}
child { node (c) {c}
}
};
\begin{pgfonlayer}{background}
\fill[draw,blue, opacity=0.3] \myconvexpath{f,h,c,g}{12pt};
\fill[draw,red, opacity=0.3] \myconvexpath{g,b,a}{12pt};
\end{pgfonlayer}
\end{tikzpicture}
\end{document}
that gives a not promising result:
Question
Is there a way to automatically recognize the node angle a path will fall when arrives near it? Doing things by hand, one can force a path to follow the desired direction, for example, h.north -> h.east -> h.south
, but how is it possible to do it automatically without the arc
syntax?
Notice that, for some shapes, one could proceed as follows:
\documentclass[a4paper,11pt]{article}
\usepackage{tikz}
\usetikzlibrary{hobby,backgrounds,calc,trees}
\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!-40:(hobbyhullnode0)$)
\foreach [
evaluate=\currentnode as \previousnode using \currentnode-1,
evaluate=\currentnode as \nextnode using \currentnode+1
] \currentnode in {1,...,\numberofnodes} {
let \p1 = ($(hobbyhullnode\currentnode)!#2!-90:(hobbyhullnode\previousnode) $),
\n1 = {atan2(\x1,\y1)},
\p2 = ($(hobbyhullnode\currentnode)!#2!-90:(hobbyhullnode\nextnode)$),
\n2 = {atan2(\x2,\y2)},
\n{delta} = {-Mod(\n1-\n2,360)},
\n{fin}={add(\n1,\n{delta})}
in
{..($(hobbyhullnode\currentnode)!#2!-220:(hobbyhullnode\previousnode)$)..($(hobbyhullnode\currentnode)!#2!40:(hobbyhullnode\nextnode)$)}
%{arc [start angle=\n1, end angle=\n{fin}, radius=#2]}
..($(hobbyhullnode\nextnode)!0.5!(hobbyhullnode\currentnode)$)
..($(hobbyhullnode\nextnode)!#2!-40:(hobbyhullnode\currentnode)$)
}
--cycle
}
\begin{document}
\begin{tikzpicture}[use Hobby shortcut]
\foreach \place/\text in {{(1,0)/a},{(0,-1)/b},{(-1,0)/c},{(0,1)/d}}
\node[name=\text] at \place {\text};
\begin{pgfonlayer}{background}
\fill[draw,green, opacity=0.3] \hobbyconvexpath{a,b,c,d}{10pt};
\end{pgfonlayer}
\end{tikzpicture}
\end{document}
but in general is not a valid approach and it is still to improve, to get at least the same result of Highlight a group of nodes in a tikz tree.
Best Answer
I'm answering because I think there's a quite good solution on the problem. First of all I'd like to thank Andrew Stacey because without the discussion in chat I wouldn't be able to solve this and, more important, he founds the major issue in the code.
What I learnt
The answer to the question:
is actually: TikZ already does it automatically. Notice indeed that:
correctly leads to:
and the user should not have to care about the angle the line arrives near the blue node.
The major issue
Building on that, however, was not sufficient. Andrew recognized that the paths drawn were not correctly realized due to the fact that the hobby path shortcut construct separately each piece; this was the cause of the bizarre behaviour as per the second figure displayed in the question.
The intermediate result
With his help it was possible to get (code here):
and notice how this solved the problems mentioned before.
A final result
Starting from the intermediate result I noticed that actually the non-perfect fit around some nodes was basically due to the
out angle
, that is the the path from hypothetically($(hobbyhullnode1)!10pt!-90:(hobbyhullnode0)$)..($(hobbyhullnode1)!10pt!90:(hobbyhullnode0)$)
. With a bit of care, the right angle should have been set to180
rather than90
.Here is a MWE:
with the following result:
Another example (since I noticed that the shape highlighted is actually the same in all the three previous figures):