我已经创建了一个多部分形状以用于 TikZ。
该形状有三个级别,有五个文本条目。
顶层是一个矩形,其尺寸仅基于该矩形中的文本。
底层是一组三个矩形,它们应该具有相同的高度(基于集合中最大的高度)和相同的宽度(基于集合中最大的宽度)。
中间层是一个单独的矩形,其宽度应与底层相同(因此我需要选择中间层或下层三个层的集合中的最大值作为宽度)。
为了实现这个功能,我想在多个\savedanchor
s 中使用代码,因此我定义了几个函数(一个用于形状的宽度,一个用于底层的高度),如中所述如何定义可在 \savedanchor 声明中重复使用的维度计算。
因此,经过多次反复尝试,我开发出了一个多部分形状。下面有一个 WE(它可能不是 MWE,因为我有很多锚点——对于 MWE,我不需要那么多锚点。
这是我在编程过程中遇到的一些问题/评论。有人能告诉我这些是真的吗,还是我只是患有货物崇拜编程?
- 我需要一个
\savedanchor
用于绘制背景的每个点。 \savedanchor
我为形状的每个部分的文本创建了一个(但可能不需要) 。我需要这些吗\savedanchor
?- 我不确定何时(或是否)需要使用
\pgf@process{\mysavedanchor}
而不是\mysavedanchor
定义锚点。我有两者的示例。 - 我觉得这段代码与汇编语言编程的关系比与 C 或 Python 等语言编程的关系更密切。我的代码是否体现了良好的实践,还是真的很糟糕?
我知道这个例子很长,如果需要的话,我可以删除所有不是 savedanchors 的锚点。但如果可能的话,我希望进行一些代码审查。
代码
\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%
\advance\pgfutil@tempdima by 2\pgf@xa
\pgf@xb=\wd\pgfnodepartchildbox%
\advance\pgf@xb by 2\pgf@xa %
\ifdim\pgf@xb>\pgfutil@tempdima%
\pgfutil@tempdima=\pgf@xb%
\fi%
\pgf@xc=\wd\pgfnodepartsiblingbox%
\advance\pgf@xc by 2\pgf@xa %
\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%
\advance \pgf@xb by 2\pgf@xa%
\ifdim\pgf@xb>\pgfutil@tempdima%
\pgfutil@tempdima=\pgf@xb%
\fi%
% See if the total box is wider
\pgf@xb=\wd\pgfnodeparttotalbox%
\advance \pgf@xb by 2\pgf@xa%
\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%
\advance\pgfutil@tempdima by \pgfutil@tempdimb%
\pgf@yb=\ht\pgfnodepartchildbox%
\pgf@ya = \dp\pgfnodepartchildbox %
\advance\pgf@yb by \pgf@ya %
\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 %
\advance\pgf@yc by \pgf@ya %
\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%
\advance\pgf@y by-.5\dp\pgfnodeparttextbox%
}%
\savedanchor\totalanchor{%
\pgf@x=-.5\wd\pgfnodeparttotalbox%
\advance\pgf@x by.5\wd\pgfnodeparttextbox%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=2\pgf@y%
\advance\pgf@y by\ht\pgfnodeparttextbox%
\advance\pgf@y by\pgflinewidth%
\advance\pgf@y by\dp\pgfnodeparttotalbox%
}
\savedanchor\childanchor{%
\pgf@x=-.5\wd\pgfnodepartchildbox%
\advance\pgf@x by.5\wd\pgfnodeparttextbox%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=-2\pgf@y%
\advance\pgf@y by-\dp\pgfnodeparttextbox%
\advance\pgf@y by-\pgflinewidth%
\my@heightcode
\pgf@ya = \pgfutil@tempdima
\advance\pgf@y by -\pgf@ya%
\advance\pgf@y by \pgfutil@tempdimb%
%\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}}%
\pgf@y=-2\pgf@y% I start with the separations
\advance\pgf@y by-\dp\pgfnodeparttextbox%
\advance\pgf@y by-\pgflinewidth%
\my@heightcode
\advance\pgf@y by -\pgfutil@tempdima%
\advance\pgf@y by \pgfutil@tempdimb%
}
\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}}%
\pgf@y=-2\pgf@y% I start with the separations
\advance\pgf@y by-\dp\pgfnodeparttextbox%
\advance\pgf@y by-\pgflinewidth%
\my@heightcode
\advance\pgf@y by -\pgfutil@tempdima%
\advance\pgf@y by \pgfutil@tempdimb%
}
\savedanchor\southwest{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\my@widthcode%
\advance\pgf@x by-.5\pgfutil@tempdima%
\advance\pgf@x by -.5\pgflinewidth%
\advance\pgf@x by -\pgfkeysvalueof{/pgf/outer xsep}%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=-3\pgf@y%
\advance\pgf@y by-\dp\pgfnodeparttextbox%
\my@heightcode%
\advance\pgf@y by-\pgfutil@tempdima%
\advance\pgf@y by-1.5\pgflinewidth%
\advance\pgf@y by -\pgfkeysvalueof{/pgf/outer ysep}%
} %
\savedanchor\siblingsouthwest{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\my@widthcode%
\advance\pgf@x by .166\pgfutil@tempdima%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=-3\pgf@y%
\advance\pgf@y by-\dp\pgfnodeparttextbox%
\my@heightcode%
\advance\pgf@y by-\pgfutil@tempdima%
\advance\pgf@y by-1.5\pgflinewidth%
\advance\pgf@y by -\pgfkeysvalueof{/pgf/outer ysep}%
} %
\savedanchor\levelnortheast{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\my@widthcode%
\advance\pgf@x by-.167\pgfutil@tempdima%
\advance\pgf@x by -.5\pgflinewidth%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=-\pgf@y%
\advance\pgf@y by-\dp\pgfnodeparttextbox%
\advance\pgf@y by-.5\pgflinewidth%
} %
\savedanchor\textnortheast{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\my@widthcode%
\advance\pgf@x by .5\pgfutil@tempdima%
\advance\pgf@x by \pgfkeysvalueof{/pgf/outer xsep}%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\advance\pgf@y by \ht\pgfnodeparttextbox%
\advance\pgf@y by .5\pgflinewidth%
\advance\pgf@y by \pgfkeysvalueof{/pgf/outer ysep}%
} %
\savedanchor\textsoutheast{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\my@widthcode%
\advance\pgf@x by .5\pgfutil@tempdima%
\advance\pgf@x by \pgfkeysvalueof{/pgf/outer xsep}%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=-\pgf@y%
\advance\pgf@y by -\dp\pgfnodeparttextbox%
\advance\pgf@y by -.5\pgflinewidth%
} %
\savedanchor\totalsouthwest{%
\pgfmathsetlength{\pgf@x}{\pgfkeysvalueof{/pgf/inner xsep}}%
\pgf@x=-\pgf@x%
\advance \pgf@x by .5\wd\pgfnodeparttextbox%
\advance\pgf@x by -.5\wd\pgfnodeparttotalbox% get to left end of total box
\advance\pgf@x by -.5\pgflinewidth%
\advance\pgf@x by -\pgfkeysvalueof{/pgf/outer xsep}%
%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\advance\pgf@y by \ht\pgfnodeparttextbox%
\advance\pgf@y by .5\pgflinewidth%
\advance\pgf@y by \pgfkeysvalueof{/pgf/outer ysep}%
} %
\savedanchor\totalnortheast{%
\pgfmathsetlength{\pgf@x}{\pgfkeysvalueof{/pgf/inner xsep}}%
\advance\pgf@x by .5\wd\pgfnodeparttextbox%
\advance\pgf@x by .5\wd\pgfnodeparttotalbox% get to right end of total box
\advance\pgf@x by .5\pgflinewidth%
\advance\pgf@x by \pgfkeysvalueof{/pgf/outer xsep}%
%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=3\pgf@y%
\advance \pgf@y by \ht\pgfnodeparttextbox%
%
\advance \pgf@y by \dp\pgfnodeparttotalbox%
\advance \pgf@y by \ht\pgfnodeparttotalbox%
\advance\pgf@y by 1.5\pgflinewidth%
\advance\pgf@y by \pgfkeysvalueof{/pgf/outer ysep}%
} %
\savedanchor\northeast{%
\pgf@x=.5\wd\pgfnodeparttextbox%
\my@widthcode%
\advance\pgf@x by .5\pgfutil@tempdima%
\advance\pgf@x by \pgfkeysvalueof{/pgf/outer xsep}%
\pgfmathsetlength{\pgf@y}{\pgfkeysvalueof{/pgf/inner ysep}}%
\pgf@y=3\pgf@y% above text, above and below total
\advance\pgf@y by \ht\pgfnodeparttextbox%
\advance\pgf@y by \ht\pgfnodeparttotalbox%
\advance\pgf@y by \dp\pgfnodeparttotalbox%
\advance\pgf@y by .5\pgflinewidth%
\advance\pgf@y by \pgfkeysvalueof{/pgf/outer ysep}%
} %
%
% 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}%
\advance \pgf@ya by .5\pgf@y
\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%
\advance\pgf@x by \pgf@xa%
}
\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%
\advance\pgf@y by \pgf@ya%
}
\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%
\advance\pgf@x by \pgf@xa%
}
\anchor{south}{
\pgf@process{\textsoutheast}%
\pgf@xa=.5\pgf@x%
\pgf@process{\southwest}%
\pgf@x=.5\pgf@x%
\advance\pgf@x by \pgf@xa%
}
\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%
\advance\pgf@x by-\pgf@xa%
\advance\pgf@y by-\pgf@ya%
\pgf@xc=.5\pgf@x% x/y is half width/height
\pgf@yc=.5\pgf@y%
\advance\pgf@xa by\pgf@xc% xa/ya becomes center
\advance\pgf@ya by\pgf@yc%
\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}%
\advance\pgf@x by\pgf@xa%
\advance\pgf@y by\pgf@ya%
}
%
% Background path
%
\backgroundpath{
\pgfpathrectanglecorners
{\pgfpointadd{\southwest}{\pgfpoint{\pgfkeysvalueof{/pgf/outer xsep}}{\pgfkeysvalueof{/pgf/outer ysep}}}}
{\pgfpointadd{\textnortheast}{\pgfpointscale{-1}{\pgfpoint{\pgfkeysvalueof{/pgf/outer xsep}}{\pgfkeysvalueof{/pgf/outer ysep}}}}}
\pgfmathsetlength{\pgf@yc}{\pgfkeysvalueof{/pgf/outer ysep}}
\pgf@yc = -\pgf@yc
\pgfpathrectanglecorners
{\pgfpointadd{\totalnortheast}{\pgfpointscale{-1}{\pgfpoint{\pgfkeysvalueof{/pgf/outer xsep}}{\pgfkeysvalueof{/pgf/outer ysep}}}}}
{\pgfpointadd{\totalsouthwest}{\pgfpoint{\pgfkeysvalueof{/pgf/outer xsep}}{\pgf@yc}}}
\pgfpathrectanglecorners%
{\pgfpointadd{\southwest}{\pgfpoint{\pgfkeysvalueof{/pgf/outer xsep}}{\pgfkeysvalueof{/pgf/outer ysep}}}}
{\pgfpointadd{\textsoutheast}%
{\pgfpointscale{-1}{\pgfpoint{\pgfkeysvalueof{/pgf/outer xsep}}{0 pt}}}}
\pgfpathrectanglecorners%
{\pgfpointadd{\southwest}{\pgfpoint{\pgfkeysvalueof{/pgf/outer xsep}}{\pgfkeysvalueof{/pgf/outer ysep}}}}
{\levelnortheast}
\pgfpathrectanglecorners%
{\pgfpointadd{\siblingsouthwest}%
{\pgfpoint{0 pt}{\pgfkeysvalueof{/pgf/outer ysep}}}}
{\pgfpointadd{\textsoutheast}%
{\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}