# Rectangle dimensions relative to other elements

I'm preparing a block diagram in tikz as depicted in Figure 1. I made this picture by precisely planning and calculating distances and sizes. However, any change made to this picture could possibly necessitate changes in other components. For example, if I add a component D2 below D, I'd have to adjust the size of E and F accordingly.

My question: Do you know a way how to solve this more nicely?

Concretely, I'd like to have a way of creating such diagrams with the following properties:

• The position, height and width of objects can be defined based on edges of other components (e.g., E.north = B.north; E.south = D.south;)
• The gray boxes fit exactly their contents
• The maximum size of a white rectangle is 70% of the gray box. Multiple white rectangles in the same row share the available width equally
• The database symbol has a fixed size
• When changing the position of the gray box, its content will also move accordingly
• Central constants, such as box height, node distance, etc. should be defined once and changing a constant should not result in the need of adjusting other values

For illustration purposes of my current approach, this is the code that displays most of the screenshot:

\documentclass[tikz]{standalone}
\usetikzlibrary{shapes,fit,backgrounds,positioning,arrows,calc}
\begin{document}
\begin{tikzpicture}[font=\footnotesize,scale=0.9,->,>=stealth',shorten >=1pt,auto,semithick,
component/.style={draw,fill=white,inner xsep=1,minimum height=1.5em,node distance=.25cm and .5cm},
full/.style={minimum width=9cm},
full2/.style={minimum width=7.7cm},
half/.style={minimum width=4.25cm},
half2/.style={minimum width=3.6cm},
third/.style={minimum width=2.66cm},
layer_annot/.style={font=\footnotesize\bfseries,node distance=.5cm},
layer/.style={fill=lightgray,minimum width=\textwidth,minimum height=1.5em+.5cm,node distance=.5cm},
layer2/.style={minimum height=3em+.75cm},
layer4/.style={minimum height=6em+1.3cm},
spanv3/.style={minimum height=4.5em+.54cm},
db/.style={cylinder,draw,shape border rotate=90,fill=white,minimum width=.8cm,minimum height=.4cm,node distance=.5cm and .5cm,yshift=.3cm}]

% background
\begin{pgfonlayer}{background}
\node[layer,layer4]            (F) {};
\node[layer,above=of F,layer2] (J) {};
\end{pgfonlayer}

% components
\node[component,full,below right=of F.north west]                    (A)  {A};
\node[component,half2,below=of A.south east,anchor=north east]       (B)  {B};
\node[component,half2,below=of B]                                    (C)  {C};
\node[component,half2,below=of C]                                    (D)  {D};
\node[component,spanv3,half2,left=of B.north west,anchor=north east] (E)  {E};
\node[db,left=of E.west]                                             (db) {};
\node[component,full,below right=of J.north west]                    (G)  {G};
\node[component,third,above right=of J.south west]                   (H)  {H};
\node[component,third,right=of H]                                    (I)  {I};

% layer names
\node[layer_annot,left=of F.east]   (F_label)   {F};
\node[layer_annot,left=of J.east]   (J_label)   {J};

% connections
\path[->] ($(db -| E.west)+(0,.1)$) edge ($(db.east)+(0,.1)$)
($(db.east)-(0,.1)$) edge ($(db -| E.west)-(0,.1)$)
($(B -| E.east)+(0,.1)$) edge ($(B.west)+(0,.1)$)
($(B.west)-(0,.1)$) edge ($(B -| E.east)-(0,.1)$)
% ...
(E.north) edge (E.north |- A.south)
(A.north -| H.south) edge (H.south)
(A.north -| I.south) edge (I.south)
% ...
;

\end{tikzpicture}
\end{document}

• Yes, of course. You are already loading fit and backgrounds, but only use the latter. The big background nodes could be designed to fit the others. Likewise, you could use fit to determine the dimensions of E.
– user121799
Nov 2, 2018 at 21:11
• I see. Thanks for mentioning that. I was not aware that fit can do more than just fit box sizes to its content. -- In a previous version, I used fit to adjust the size of the background according to its content. However, I soon ran into a chicken-egg problem where the position of the elements depend on the gray boxes and the size of the gray boxes depend on the containing elements, but I have to define one element first and cannot refer to non-existing elements. That's why I removed the usage of fit for the gray boxes. Nov 2, 2018 at 21:21
• Another problem that I ran into was the right alignment of the bold text. When I used fit, I could not achieve a right alignment of the text for some reason. Nov 2, 2018 at 21:24
• I cannot see any bold text. Yet I had never problems of that kind. Do you achieve the bold text via font=\bfseries?
– user121799
Nov 2, 2018 at 21:36
• Yes, font=\bfseries. F and J are considered to be bold. Maybe, it's not clear because they are only single letters. Nov 2, 2018 at 22:11

1. draw the background rectangles via fit and
2. how to make the E node as tall the B, C and D nodes by using calc. (Notice that there is a hardcoded 0.9 which can be eliminated by using transform shape, but I do not know the purpose of scale=0.9 in you picture so I went this way).

Here's the code. I didn't have to add any library as you were loading but not using fit and calc.

\documentclass[tikz]{standalone}
\usetikzlibrary{shapes,fit,backgrounds,positioning,arrows,calc}
\begin{document}
\begin{tikzpicture}[font=\footnotesize,scale=0.9,->,>=stealth',shorten >=1pt,auto,semithick,
component/.style={draw,fill=white,inner xsep=1,minimum height=1.5em,node distance=.25cm and .5cm},
full/.style={minimum width=9cm},
full2/.style={minimum width=7.7cm},
half/.style={minimum width=4.25cm},
half2/.style={minimum width=3.6cm},
third/.style={minimum width=2.66cm},
layer_annot/.style={font=\footnotesize\bfseries,node distance=.5cm},
layer/.style={fill=lightgray,minimum width=\textwidth,minimum height=1.5em+.5cm,node distance=.5cm},
layer2/.style={minimum height=3em+.75cm},
layer4/.style={minimum height=6em+1.3cm},
spanv3/.style={minimum height=4.5em+.54cm},
db/.style={cylinder,draw,shape border rotate=90,fill=white,minimum width=.8cm,minimum height=.4cm,node distance=.5cm and .5cm,yshift=.3cm}]

% components
\node[component,full] at (0,0)                    (A)  {A};
\node[component,half2,below=of A.south east,anchor=north east]       (B)  {B};
\node[component,half2,below=of B]                                    (C)  {C};
\node[component,half2,below=of C]                                    (D)  {D};
\path let \p1=($(B.north)-(D.south)$) in \pgfextra{\typeout{\y1}}
node[component,minimum height=0.9*\y1,inner ysep=0pt,half2,left=of B.north west,anchor=north east] (E)  {E};
\node[db,left=of E.west]                                             (db) {};
\node[component,third,above=1.6cm of A.west,anchor=west]               (H)  {H};
\node[component,third,right=of H]                                    (I)  {I};
\node[component,full,above=8mm of H.west,anchor=west]                    (G)  {G};

% background
\begin{pgfonlayer}{background}
\path ([xshift=3cm]D.east) coordinate(auxD) ([xshift=3cm]G.east) coordinate
(auxG);
\node[layer,fit=(A) (D) (auxD),layer4]  (F) {};
\node[layer,fit=(G) (H) (auxG),layer2] (J) {};
\end{pgfonlayer}

% layer names
\node[layer_annot,left=of F.east]   (F_label)   {F};
\node[layer_annot,left=of J.east]   (J_label)   {J};

% connections
\path[->] ($(db -| E.west)+(0,.1)$) edge ($(db.east)+(0,.1)$)
($(db.east)-(0,.1)$) edge ($(db -| E.west)-(0,.1)$)
($(B -| E.east)+(0,.1)$) edge ($(B.west)+(0,.1)$)
($(B.west)-(0,.1)$) edge ($(B -| E.east)-(0,.1)$)
% ...
(E.north) edge (E.north |- A.south)
(A.north -| H.south) edge (H.south)
(A.north -| I.south) edge (I.south)
% ...
;

\end{tikzpicture}
\end{document}


• Ok, thanks. This solves the problem with the box heights and widths. The scale=0.9 is there for historical reasons, it has no special intention. I'll remove it. Nov 2, 2018 at 22:17