Context
I want to create several diagrams to illustrate processes. They are not technically complex. However, they consist of several nested elements. First, macro-elements are relatively positioned. These macro-elements are made up of sub-elements, which are themselves made-up of sub-sub-elements. Moreover, I finally draw additional elements relatively to macro-, sub-, and/or sub-sub-elements.
Question
My question is thus what is the best way to design such pictures with nested elements using TikZ
?
My requirements are:
- I want to position sub-elements relatively to their parent-element (and if possible relatively to sub-elements of other macro-elements too);
- At a given level, I want to be able to access elements of this levels and of any sub-levels;
- I would like to be able to use relative coordinate in each element. This means that each element would have its own
(0,0)
coordinate. Its position would be computed relatively to the the achor of the element, and the position of the anchor relatively to the(0,0)
of the global picture; - I don't care about properties inheritance: each [sub]element is unambiguously defined.
Possible solutions
- The solution I currently use is to nest
tikzpicture
s. Even if it works most of the time, I know that is is not a recommended solution (Nesting “tikzpicture” within “tikzpicture” elements - Good or bad practice?). I am thus looking for a more robust way to do it. - I have though of
pic
, but it is said that one cannot accesspic
later on ("Unlikenodes
,pic
s cannot be referenced later on." PGF manual §18.1). scope
environments does not seem to work neither, since I want to "dynamically" position elements (i.e. not with constants, but relatively to the others).- Lastly, if
Matrices
might solve the positioning of macro-elements, however, each cell acts as a node and thus cannot be nested In TikZ, is it possible to nest matrices?).
Example
Note: This example is a concrete case, showing code I'd like to improve. However I am rather interested in general guidelines for structuring such diagrams, than in a specific code-review. Or in other word, what strategy should be used to create similar diagrams.
This picture is made up of major elements (green), which are positioned relatively the one to the other:
Most elements constitutes in a group of sub-elements (blue). The latter are also positioned relatively the one to the other:
These sub-elements are also made-up of sub-sub-elements (orange):
Finally, some additional elements are drawn in top of the rest (red):
MWE
(for the purpose of the MWE, I've removed included images. I know this code is far from minimal, but I've tried to find a balance between a representative example, and a minimal one.)
\documentclass[10pt, margin=10pt]{standalone}
\usepackage{graphicx}
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{positioning}
\usetikzlibrary{arrows.meta}
\usepackage{calc}
\begin{document}
%% Constants definition %%
%Length and coefficient%
\newlength{\eboXbigUnit}
\setlength{\eboXbigUnit}{5em}
\newlength{\eboYbigUnit}
\setlength{\eboYbigUnit}{6\baselineskip}
\newlength{\eboInterlabelDistance}
\setlength{\eboInterlabelDistance}{2.5\baselineskip}
\newlength{\eboIconsUnit}
\setlength{\eboIconsUnit}{\baselineskip}
\newlength{\eboInterArrowLength}
\setlength{\eboInterArrowLength}{1.5\baselineskip}
\newlength{\eboLineWidth}
\setlength{\eboLineWidth}{.33ex}
\newcommand{\coefBoxWidth}{6.2}
\newcommand{\coefBoxHeight}{3}
%Colors%
\definecolor{eboOrange}{RGB}{250,179,52}
\definecolor{eboOrange21}{RGB}{253,215,145}
\definecolor{eboOrange22}{RGB}{249,163,13}
\definecolor{eboGrey20}{RGB}{235,234,233}
\definecolor{eboGrey21}{RGB}{173,171,172}
\definecolor{eboGrey22}{RGB}{118,115,116}
\definecolor{eboBlack}{RGB}{35,31,32}
\begin{tikzpicture}[
remember picture,
inner sep=0pt,
outer sep=0pt,
eboStepLabel/.style={
font={\sffamily\scriptsize},
text=eboOrange,
align=center,
anchor=base,
},
eboMiddleArrow/.style={
->,
draw=eboGrey21,
line width=\eboLineWidth,
line cap=round,
line join = round,
>={Straight Barb[line cap=round, line join=miter, angle=45:4*\the\eboLineWidth]}
},
iconGrey/.style={
text=eboBlack,
},
]
%%% TOP LABELS %%%
% Label 1
\node[
eboStepLabel,
]
(1-caption)
at (0,0)
{1. Lorem ipsum\\
dolor sit amet\strut};
% Label 2
\node[
eboStepLabel,
right= \eboInterlabelDistance of 1-caption.east,
anchor=west,
]
(2-caption)
{2. consectetur adipiscing elit.\\
Mauris eu varius eros\strut};
% Label 3
\node[
eboStepLabel,
right= \eboInterlabelDistance of 2-caption.east,
anchor=west,
]
(3-caption)
{3. Ut convallis\\
accumsan porttitor\strut};
% Label 4
\node[
eboStepLabel,
right= \eboInterlabelDistance of 3-caption.east,
anchor=west,
]
(4-caption)
{4. Donec mollis in\\
erat id sollicitudin\strut};
%%% ILLUSTRATIONS%%%
% Illustration step 0
\node (0-picture)[
below left = \eboYbigUnit and 2.75\eboInterlabelDistance of 1-caption.south,
anchor=center
]
{\includegraphics[width=2\baselineskip]{example-image-a}};
% Define center coordinate for illustration of steps 2 to 4
\coordinate [below = \eboYbigUnit of 2-caption.south](2-center);
\coordinate [below = \eboYbigUnit of 3-caption.south](3-center);
\coordinate [below = \eboYbigUnit of 4-caption.south](4-center);
% Illustration step 2
\node (2-picture) at (2-center)[
anchor=base,
]
{%
\begin{tikzpicture}[
inner sep=0pt,
remember picture,
baseline=(2-methodology.base)
]
%true positive
\node[
fill = eboOrange21,
draw = eboOrange22,
anchor=north east,
rectangle,
minimum height = \coefBoxHeight\baselineskip,
minimum width = \coefBoxWidth\baselineskip,
line width = .5\eboLineWidth,
rounded corners=\eboLineWidth
] (2-truepositive)
{};
\node[
below right= .25\baselineskip and .25\baselineskip of 2-truepositive.north west,
anchor = north west,
align = left,
font = {\sffamily\scriptsize},
text = eboOrange22,
](2-truepositive-label)
{Duis tempus};
\coordinate (2-truepositive-articleL) at ($(2-truepositive.north west)!0.9!(2-truepositive.south west)$);
\coordinate (2-truepositive-articleR) at ($(2-truepositive.north east)!0.9!(2-truepositive.south east)$);
\foreach \pos in {0.125, 0.375, ..., 0.875} {
\node[text=eboOrange]
at ($(2-truepositive-articleL)!\pos!(2-truepositive-articleR)$)
{\includegraphics[height=\baselineskip]{example-image-a}};
};
%methodology
\node[
below = .5\baselineskip of 2-truepositive.south,
anchor=north,
fill = eboGrey20,
draw = eboGrey21,
rectangle,
minimum height = \coefBoxHeight\baselineskip,
minimum width = \coefBoxWidth\baselineskip,
line width = .5\eboLineWidth,
rounded corners=\eboLineWidth,
] (2-methodology)
{};
\node[
below right= .25\baselineskip and .25\baselineskip of 2-methodology.north west,
anchor = north west,
align = left,
font = {\sffamily\scriptsize},
text = eboGrey21,
](2-methodology-label)
{Dignissim};
\node[
below = .1\baselineskip of 2-methodology-label.south west,
anchor = north west,
align = left,
font = {\sffamily\tiny},
text = eboGrey21,
](2-methodology-label)
{Nulla ut augue vehicula,\\
tincidunt};
\coordinate (2-methodology-articleL) at ($(2-methodology.north west)!0.9!(2-methodology.south west)$);
\coordinate (2-methodology-articleR) at ($(2-methodology.north east)!0.9!(2-methodology.south east)$);
\foreach \pos in {0.375, 0.625, 0.875} {
\node[text=eboGrey21]
at ($(2-methodology-articleL)!\pos!(2-methodology-articleR)$)
{\includegraphics[height=\baselineskip]{example-image-a}};
};
% dots
\node[
below = .33\baselineskip of 2-methodology.south,
anchor=north,
text = eboGrey21,
font = {\sffamily},
] (2-dots)
{\dots};
% topology
\node[
below = .33\baselineskip of 2-dots.south,
anchor=north,
fill = eboGrey20,
draw = eboGrey21,
rectangle,
minimum height = \coefBoxHeight\baselineskip,
minimum width = \coefBoxWidth\baselineskip,
line width = .5\eboLineWidth,
rounded corners=\eboLineWidth,
] (2-topology)
{};
\node[
below right= .25\baselineskip and .25\baselineskip of 2-topology.north west,
anchor = north west,
align = left,
font = {\sffamily\scriptsize},
text = eboGrey21,
](2-topology-label)
{Luctus rhoncus};
\node[
below = .1\baselineskip of 2-topology-label.south west,
anchor = north west,
align = left,
font = {\sffamily\tiny},
text = eboGrey21,
](2-topology-label)
{Donec molestie mollis arcu\\
eget consectetur};
\coordinate (2-topology-articleL) at ($(2-topology.north west)!0.9!(2-topology.south west)$);
\coordinate (2-topology-articleR) at ($(2-topology.north east)!0.9!(2-topology.south east)$);
\foreach \pos in {0.625, 0.875} {
\node[text=eboGrey21]
at ($(2-topology-articleL)!\pos!(2-topology-articleR)$)
{\includegraphics[height=\baselineskip]{example-image-a}};
};
\end{tikzpicture}
};
% Define center coordinate for illustration of step 1
\path
(2-picture.west)
-|
(1-caption.south)
coordinate [midway] (1-center);
% Illustration step 1
\node (1-picture) at (1-center)
{%
\newlength{\queryHeight}%
\setlength{\queryHeight}{10.166\baselineskip}%
\addtolength{\queryHeight}{\heightof{\tikz\node[font={\sffamily}]{\dots};}}%
\begin{tikzpicture}[inner sep=0pt]
\node[
fill = eboGrey21,
draw = eboGrey22,
rectangle,
minimum height = \queryHeight,
minimum width = \coefBoxWidth\baselineskip,
line width = .5\eboLineWidth,
rounded corners=\eboLineWidth,
] (1-query)
{};
\node[
below right= .5\baselineskip and .25\baselineskip of 1-query.north west,
anchor = north west,
align = left,
font = {\sffamily\scriptsize},
text = eboGrey20,
](1-query-google)
{\includegraphics[height=\baselineskip]{example-image-b}};
\node[
right= .25\baselineskip of 1-query-google.north east,
anchor = north west,
align = left,
font = {\tiny\sffamily},
text = eboGrey20,
](1-query-google)
{$\cdot$~Duis vitae faucibus orci,\\
venenatis nulla ac\\
$\cdot$~Vivamus sit amet\\
hendrerit dui};
\coordinate (1-UL) at ($(1-query.north west)!0.2!(1-query.south west)$);
\coordinate (1-UR) at ($(1-query.north east)!0.2!(1-query.south east)$);
\coordinate (1-LL) at ($(1-query.north west)!1!(1-query.south west)$);
\coordinate (1-LR) at ($(1-query.north east)!1!(1-query.south east)$);
\foreach \x in {0.125,0.375,0.625,0.875}{
\foreach \y/\yname in {0.166/a, 0.333/b, 0.5/c, 0.666/d , 0.833/e}{
\coordinate (myL\yname) at ($(1-UL)!\y!(1-LL)$);
\coordinate (myR\yname) at ($(1-UR)!\y!(1-LR)$);
\node [text=eboGrey22] at ($(myL\yname)!\x!(myR\yname)$) {\includegraphics[height=\baselineskip]{example-image-a}};
};
};
\end{tikzpicture}
};
% Illustration 3
\node (3-picture) at (3-center)
{%
\begin{tikzpicture}[
remember picture,
inner sep=0pt,
categorizationLabel/.style={
font={\sffamily\scriptsize},
text=eboGrey22,
align=left,
},
categorizationSublabel/.style={
font={\sffamily\tiny},
text=eboGrey21,
align=left,
},
]
\node (3-paper) [text=eboOrange]
at (0,0)
{\includegraphics[height=2\baselineskip]{example-image-a}};
\node [
below left = .333\baselineskip and .15\baselineskip of 3-paper.north east,
anchor=north west,
text=eboGrey22,
]
{\includegraphics[width=\baselineskip]{example-image-c}};
%Type of paper
\node (3-label-1) [
categorizationLabel,
below = \baselineskip of 3-paper.south west,
anchor = base west
]
{Proin sollicitudin};
\node (3-label-icon-1)[
left = .5em of 3-label-1.west,
anchor=east,
text=eboGrey22,
]
{\includegraphics[height=2ex]{example-image-b}};
\node (3-sublabel-1-11) [
categorizationSublabel,
below right = .05ex and .175em of 3-label-1.south west,
anchor= north west,
]
{\strut\includegraphics[height=1ex]{example-image} ipsum ac};
\node (3-sublabel-1-21) [
categorizationSublabel,
below = .0ex of 3-sublabel-1-11.south west,
anchor= north west,
]
{\strut\includegraphics[height=1ex]{example-image} vestibulum};
\node (3-sublabel-1-12) [
categorizationSublabel,
below = .0ex of 3-sublabel-1-21.south west,
anchor= north west,
]
{\strut\includegraphics[height=1ex]{example-image} suscipit};
\node (3-sublabel-1-22) [
categorizationSublabel,
below = .0ex of 3-sublabel-1-12.south west,
anchor= north west,
]
{\strut\includegraphics[height=1ex]{example-image} venenatis};
%Type of product
\node (3-label-2) [
categorizationLabel,
below = 2.85\baselineskip of 3-label-1.west,
anchor=west,
]
{Vulputate fringilla};
\node (3-label-icon-2)[
left = .5em of 3-label-2.west,
anchor=east,
text=eboGrey22,
]
{\includegraphics[height=2ex]{example-image-b}};
\node (3-sublabel-2-11) [
categorizationSublabel,
below right = .05ex and .175em of 3-label-2.south west,
anchor= north west,
]
{\strut\includegraphics[height=1ex]{example-image} condimentum};
\node (3-sublabel-2-21) [
categorizationSublabel,
below = .0ex of 3-sublabel-2-11.south west,
anchor= north west,
]
{\strut\includegraphics[height=1ex]{example-image} scelerisque};
\node (3-sublabel-2-12) [
categorizationSublabel,
below = 0.05ex of 3-sublabel-2-21.south west,
anchor= north west,
]
{\strut\includegraphics[height=1ex]{example-image} lacus};
\node (3-sublabel-2-22) [
categorizationSublabel,
below = .0ex of 3-sublabel-2-12.south west,
anchor= north west,
]
{\strut\includegraphics[height=1ex]{example-image} pharetra};
%Type of open-design
\node (3-label-3) [
categorizationLabel,
below = 2.9\baselineskip of 3-label-2.west,
anchor=west,
]
{Fusce justo est};
\node (3-label-icon-3) [
left = .5em of 3-label-3.west,
anchor=east,
text=eboGrey22,
]
{\includegraphics[height=2ex]{example-image-b}};
\node (3-sublabel-3-11) [
categorizationSublabel,
below right = .05ex and .175em of 3-label-3.south west,
anchor= north west,
]
{\strut\includegraphics[height=1ex]{example-image} consequat};
\node (3-sublabel-3-21) [
categorizationSublabel,
below = .0ex of 3-sublabel-3-11.south west,
anchor= north west,
]
{\strut\includegraphics[height=1ex]{example-image} nec facilisis};
\node (3-sublabel-3-12) [
categorizationSublabel,
below = 0.05ex of 3-sublabel-3-21.south west,
anchor= north west,
]
{\strut\includegraphics[height=1ex]{example-image} sodales};
\end{tikzpicture}
};
% Illustration 4
\node (4-picture) at (4-center) [
anchor=base,
]
{%
\begin{tikzpicture}[remember picture,baseline = (4-anchor)]
\node (4-bars)
at (0,0) [iconGrey]
{\includegraphics[width=2\eboIconsUnit]{example-image-b}};
\node (4-r-logo)
at (4-bars.north east) [iconGrey]
{\includegraphics[width=\eboIconsUnit]{example-image-c}};
\node (4-Typologie)
at (0, -3\eboIconsUnit) [%
font={\sffamily\scriptsize},
text=eboBlack,
align=left%
]
{Aenean:\strut};
\node (4-DIY)[%
font={\sffamily\scriptsize},
text=eboGrey22,
align=left,%
below = 0pt of 4-Typologie.south west,
anchor = north west
]
{$\cdot$ Finibus elit\strut};
\node (4-meta) [%
font={\sffamily\scriptsize},
text=eboGrey22,
align=left,%
below = 0pt of 4-DIY.south west,
anchor = north west
]
{$\cdot$ In orci varius\strut};
\node (4-indus)[%
font={\sffamily\scriptsize},
text=eboGrey22,
align=left,%
below = 0pt of 4-meta.south west,
anchor = north west
]
{$\cdot$ Lobortis nunc\strut};
\coordinate (4-anchor)
at ($(4-bars.south)!0.5!(4-Typologie.north)$);
\end{tikzpicture}%
};
%%% ARROWS %%%
% Arrow 0 -> 1
\path
(0-picture.east)
--
(1-picture.west)
node [midway] (01-arrow) {\tikz \draw [eboMiddleArrow] (0,0) -- (\eboInterArrowLength,0);};
% Arrows 1 -> 2
\path
(1-picture.east)
--
(2-picture.west)
node [midway] (12-arrow) {
\begin{tikzpicture}
\draw [eboMiddleArrow] (0,0) -- (\eboInterArrowLength,0);
\draw [eboMiddleArrow] (0,0) -- (\eboInterArrowLength,6ex);
\draw [eboMiddleArrow] (0,0) -- (\eboInterArrowLength,-6ex);
\end{tikzpicture}
};
% Arrow 2-3
\coordinate (23-middle) at ($(2-caption.east)!.5!(3-caption.west)$);
\node [
below = 2.2\eboIconsUnit of 23-middle
] {\tikz \draw [eboMiddleArrow] (0,0) -- (\eboInterArrowLength,0);};
% Arrows 3 -> 4
\path
(3-picture.east)
--
(4-picture.base west)
node [midway] (34-arrow) {%
\begin{tikzpicture}
\draw [eboMiddleArrow] (0,0) -- (\eboInterArrowLength,.75);
\draw [eboMiddleArrow] (0,0) -- (\eboInterArrowLength,-.75);
\end{tikzpicture}
};
\end{tikzpicture}
\end{document}
fit
node to surround it and use that for positioning the next block. How would that sound? BTW, bonus points for trying to avoid nesting tikzpictures.\pic
as a whole, you can refer to any nodes that you define within it.