# Good practice in defining multipart shapes for TikZ

I have created a multipart shape for use in TikZ.

The shape has three levels, with five text entries.

The top level is a rectangle whose dimensions are only based on the text in that rectangle.

The bottom level is a set of three rectangles that should have equal heights (based on the largest height in the set) and equal widths (based on the largest width in the set).

The middle level is a single rectangle that should have the same width as the bottom level (so I need to have the width chosen as the maximum of the middle level or the set of three on the lower level).

In order to make this work, I wanted to have code used in multiple \savedanchors, so I defined a couple of functions (one for the width of the shape, one for the height of the bottom layer) as described in How can I define a calculation of a dimension that I can reuse in a \savedanchor declaration.

So after much trial and error, I developed a multipart shape. I have a WE below (it's probably not an MWE, because I have lots of anchors -- I don't need that many for an MWE.

Here are some questions/comments that I arrived at in my coding odyssey. Can anyone tell me if they are real, or if I'm just suffering from cargo cult programming?

1. I need a \savedanchor for every point that I want to use for drawing the background.
2. I created (but may not need) a \savedanchor for the text in each part of the shape. Do I need those \savedanchors?
3. I am not sure when (or if) I need to use \pgf@process{\mysavedanchor} as opposed to \mysavedanchor when defining anchors. I have examples of both.
4. I feel like this code is more closely related to assembly language programming than to, say C or Python. Does my code exhibit good practices, or is it really bad?

I know the example is long, and if I need to, I can strip out all of the anchors that are not savedanchors. But I would like to get some code review if possible.

CODE

    \documentclass{article}

\usepackage{tikz}

\newbox\pgfnodepartlowerbox
\newbox\pgfnodeparttotalbox
\newbox\pgfnodepartchildbox
\newbox\pgfnodepartlevelbox
\newbox\pgfnodepartsiblingbox

%
% A five-part shape that looks somewhat like a house, with three parts
% on the lowest level, one part on the middle level, and one part on
% the top level.
%
%
% Parts: text, level, child, sibling, total

\makeatletter
\def\my@widthcode{
\pgfmathsetlength{\pgf@xa}{\pgfkeysvalueof{/pgf/inner xsep}}%
% get the widths of each of the three rectangles on the lower level
\pgfutil@tempdima=\wd\pgfnodepartlevelbox%
\pgf@xb=\wd\pgfnodepartchildbox%
\ifdim\pgf@xb>\pgfutil@tempdima%
\pgfutil@tempdima=\pgf@xb%
\fi%
\pgf@xc=\wd\pgfnodepartsiblingbox%
\ifdim\pgf@xc>\pgfutil@tempdima%
\pgfutil@tempdima=\pgf@xc%
\fi%
% account for the 3 boxes on the lower level
\pgfutil@tempdima = 3\pgfutil@tempdima%
\advance \pgfutil@tempdima by 2\pgflinewidth% two lines in the middle of the row
% see if the main level box is wider
\pgf@xb = \wd\pgfnodeparttextbox%
\ifdim\pgf@xb>\pgfutil@tempdima%
\pgfutil@tempdima=\pgf@xb%
\fi%
% See if the total box is wider
\pgf@xb=\wd\pgfnodeparttotalbox%
\ifdim\pgf@xb>\pgfutil@tempdima%
\pgfutil@tempdima=\pgf@xb%
\fi%
}

\def\my@heightcode {
% get the heights of each of the three rectangles on the lower level
% return values: \pgfutil@tempdima is total box height
%                        \pgfutil@tempdimb is depth
\pgfutil@tempdima=\ht\pgfnodepartlevelbox%
\pgfutil@tempdimb=\dp\pgfnodepartlevelbox%
\pgf@yb=\ht\pgfnodepartchildbox%
\pgf@ya = \dp\pgfnodepartchildbox %
\ifdim\pgf@yb>\pgfutil@tempdima%
\pgfutil@tempdima=\pgf@yb%
\fi%
\ifdim\pgf@ya>\pgfutil@tempdimb%
\pgfutil@tempdimb=\pgf@ya%
\fi%
\pgf@yc=\ht\pgfnodepartsiblingbox%
\pgf@ya = \dp\pgfnodepartsiblingbox %
\ifdim\pgf@yc>\pgfutil@tempdima%
\pgfutil@tempdima=\pgf@yc%
\fi%
\ifdim\pgf@ya>\pgfutil@tempdimb%
\pgfutil@tempdimb=\pgf@ya%
\fi%
}

\pgfdeclareshape{variety}
{
%
% Node parts
%
\nodeparts{text,total,level,sibling,child}

%
% Saved Anchors
%
\savedanchor\centerpoint{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\pgf@y = .5\ht\pgfnodeparttextbox%
}%
\savedanchor\totalanchor{%
\pgf@x=-.5\wd\pgfnodeparttotalbox%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=2\pgf@y%
}
\savedanchor\childanchor{%
\pgf@x=-.5\wd\pgfnodepartchildbox%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=-2\pgf@y%
\my@heightcode
\pgf@ya = \pgfutil@tempdima
%\immediate\write16{ childanchor \the\pgf@x \the\pgf@y}
}
\savedanchor\levelanchor{%
\pgfmathsetlength{\pgf@x} {.5\wd\pgfnodeparttextbox}% get to center of text box
\my@widthcode%
\advance\pgf@x by-.33\pgfutil@tempdima % go to 1/6 of the total width
\advance\pgf@x by -.5\wd\pgfnodepartlevelbox% get to left end of level box
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\my@heightcode
}
\savedanchor\siblinganchor{%
\pgfmathsetlength{\pgf@x}{.5\wd\pgfnodeparttextbox}% get to center of text box
\my@widthcode%
\advance\pgf@x by .33\pgfutil@tempdima % go to 5/6 of the total width
\advance\pgf@x by -.5\wd\pgfnodepartsiblingbox% get to left end of sibling box
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\my@heightcode
}

\savedanchor\southwest{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\my@widthcode%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=-3\pgf@y%
\my@heightcode%
} %
\savedanchor\siblingsouthwest{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\my@widthcode%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=-3\pgf@y%
\my@heightcode%
} %
\savedanchor\levelnortheast{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\my@widthcode%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=-\pgf@y%
} %
\savedanchor\textnortheast{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\my@widthcode%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
} %
\savedanchor\textsoutheast{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\my@widthcode%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=-\pgf@y%
} %
\savedanchor\totalsouthwest{%
\pgfmathsetlength{\pgf@x}{\pgfkeysvalueof{/pgf/inner xsep}}%
\pgf@x=-\pgf@x%
\advance\pgf@x by -.5\wd\pgfnodeparttotalbox% get to left end of total box
%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
} %
\savedanchor\totalnortheast{%
\pgfmathsetlength{\pgf@x}{\pgfkeysvalueof{/pgf/inner xsep}}%
\advance\pgf@x by .5\wd\pgfnodeparttotalbox% get to right end of total box
%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=3\pgf@y%
%
} %
\savedanchor\northeast{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\my@widthcode%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=3\pgf@y% above text, above and below total
} %

%
% Anchors
%
% Non-boundary
\anchor{center}{\centerpoint}
\anchor{mid}{\pgf@process{\centerpoint}\pgfmathsetlength\pgf@y{.5ex}}
\anchor{base}{\pgf@process{\centerpoint}\pgf@y=0pt}
% Boundary -- clockwise from southwest
\anchor{south west}{\southwest}
\anchor{text south west}{%
\pgf@process{\levelnortheast}
\pgf@ya = \pgf@y
\pgf@process{\southwest}
\pgf@y = \pgf@ya
}
\anchor{west}{
\pgf@process{\textnortheast}%
\pgf@ya=.5\pgf@y%
\pgf@process{\textsoutheast}%
\pgf@process{\southwest}%
\pgf@y= \pgf@ya%
}
\anchor{text north west}{%
\pgf@process{\textnortheast}
\pgf@ya = \pgf@y
\pgf@process{\southwest}
\pgf@y = \pgf@ya
}
\anchor{north west}{%
\pgf@process{\totalnortheast}
\pgf@ya = \pgf@y
\pgf@process{\southwest}
\pgf@y= \pgf@ya
}
\anchor{total north west}{%
\pgf@process{\totalnortheast}
\pgf@ya = \pgf@y
\pgf@process{\totalsouthwest}
\pgf@y = \pgf@ya
}
\anchor{total south west} {\totalsouthwest}
\anchor{total south east} {%
\pgf@process{\totalsouthwest}
\pgf@ya = \pgf@y
\pgf@process{\totalnortheast}
\pgf@y = \pgf@ya
}
\anchor{north}{
\pgf@process{\totalsouthwest}%
\pgf@xa=.5\pgf@x%
\pgf@process{\totalnortheast}%
\pgf@x=.5\pgf@x%
}
\anchor{total north east} {\totalnortheast}
\anchor{north east}{\northeast}%
\anchor{text north east}{\textnortheast}%
\anchor{east}{%
\pgf@process{\textnortheast}%
\pgf@ya=.5\pgf@y%
\pgf@process{\textsoutheast}%
\pgf@y=.5\pgf@y%
}
\anchor{text south east} {\textsoutheast}
\anchor{south east}{
\pgf@process{\textsoutheast}
\pgf@xa=\pgf@x
\southwest
\pgf@x=\pgf@xa
}
\anchor{sibling south west} {\siblingsouthwest}
\anchor{south}{
\pgf@process{\textsoutheast}%
\pgf@xa=.5\pgf@x%
\pgf@process{\southwest}%
\pgf@x=.5\pgf@x%
}
\anchor{south}{
\pgf@process{\textsoutheast}%
\pgf@xa=.5\pgf@x%
\pgf@process{\southwest}%
\pgf@x=.5\pgf@x%
}
\anchor{level south east}{
\pgf@process{\levelnortheast}
\pgf@xa=\pgf@x
\southwest
\pgf@x=\pgf@xa
}
\anchor{level north east}{\levelnortheast}

\anchor{total west}{%
\pgf@process{\totalanchor}%
\pgf@ya = \pgf@y%
\pgf@process{\totalsouthwest}%
\pgf@y=\pgf@ya}
\anchor{total east}{%
\pgf@process{\totalanchor}%
\pgf@ya = \pgf@y%
\pgf@process{\totalnortheast}%
\pgf@y=\pgf@ya}
\anchor{base west}{\southwest\pgf@y=0pt}
\anchor{base east}{\northeast\pgf@y=0pt}
\anchor{lower west}{%
\pgf@process{\levelanchor}%
\pgf@ya = \pgf@y%
\pgf@process{\southwest}%
\pgf@y=\pgf@ya}
\anchor{lower east}{%
\pgf@process{\levelanchor}%
\pgf@ya = \pgf@y%
\pgf@process{\northeast}%
\pgf@y=\pgf@ya}
\anchor{center}{\centerpoint}
\anchor{total}{\totalanchor}
\anchor{child}{\childanchor}
\anchor{level}{\levelanchor}
\anchor{sibling}{\siblinganchor}
\anchorborder{%
\pgf@xb=\pgf@x% xb/yb is target
\pgf@yb=\pgf@y%
\southwest%
\pgf@xa=\pgf@x% xa/ya is se
\pgf@ya=\pgf@y%
\northeast%
\pgf@xc=.5\pgf@x% x/y is half width/height
\pgf@yc=.5\pgf@y%
\edef\pgf@marshal{%
\noexpand\pgfpointborderrectangle
{\noexpand\pgfqpoint{\the\pgf@xb}{\the\pgf@yb}}
{\noexpand\pgfqpoint{\the\pgf@xc}{\the\pgf@yc}}%
}%
\pgf@process{\pgf@marshal}%
}

%
% Background path
%
\backgroundpath{
\pgfpathrectanglecorners
\pgfmathsetlength{\pgf@yc}{\pgfkeysvalueof{/pgf/outer ysep}}
\pgf@yc = -\pgf@yc
\pgfpathrectanglecorners
\pgfpathrectanglecorners%
{\pgfpointscale{-1}{\pgfpoint{\pgfkeysvalueof{/pgf/outer xsep}}{0 pt}}}}
\pgfpathrectanglecorners%
{\levelnortheast}
\pgfpathrectanglecorners%
{\pgfpoint{0 pt}{\pgfkeysvalueof{/pgf/outer ysep}}}}
{\pgfpointscale{-1}{\pgfpoint{\pgfkeysvalueof{/pgf/outer xsep}}{0 pt}}}}
%  \pgfusepath{stroke,fill}
}
\inheritanchorborder[from=rectangle]
}
\makeatother

\begin{document}

\begin{tikzpicture}
\Huge
\node[name=s,shape=variety, draw=black!10 ,fill=yellow!20, line width=4pt] {Variety\vrule width 1pt height 2cm
\nodepart{total}Total\nodepart{level}Level\nodepart{child}Child\nodepart{sibling}Sibling};
\foreach \anchor/\placement in
{north west/above left, north/above, north east/above right,
west/left, center/above, east/right,
text south west/left, text north west/left, total north west/above left,
total south west/left, total south east/right,
total north east/above right,
total west/left, total east/right,
text north east/right, text south east/right,
sibling south west/below right, level south east/below left,
total/below right, child/below, level/below, sibling/below,
base/below, mid/above, base west/left, base east/right,
south west/below left, south/below, south east/below right, text/left,
lower west/left, lower east/right,
level north east/right%
}
\draw[shift=(s.\anchor)] plot[mark=x] coordinates{(0,0)}
node[\placement] {\scriptsize\texttt{(s.\anchor)}};
\end{tikzpicture}
\end{document}


OUTPUT

• I think overall your code looks good except that you may want to be a bit more careful in avoiding stray spaces.
– user194703
Apr 22 '20 at 18:20
• @Schrödinger's cat, what is the definition of 'stray spaces'? Should I never include spaces in my code? Or only include spaces when I need them to end a macro name? I'm used to adding whitespace for readability when I program, and I have learned that it sometime causes problems in tex programming. So some guidance would be helpful to me. Apr 22 '20 at 18:27
• What I mean is that if one compares your code to what one finds in the libraries, you leave spaces e.g. after by and sometimes miss a % at the end of the line as in \pgf@process{\levelnortheast}. Overall you avoid these spaces to a very large extent.
– user194703
Apr 22 '20 at 18:30