为 TikZ 定义多部分形状的良好做法

为 TikZ 定义多部分形状的良好做法

我已经创建了一个多部分形状以用于 TikZ。

该形状有三个级别,有五个文本条目。

顶层是一个矩形,其尺寸仅基于该矩形中的文本。

底层是一组三个矩形,它们应该具有相同的高度(基于集合中最大的高度)和相同的宽度(基于集合中最大的宽度)。

中间层是一个单独的矩形,其宽度应与底层相同(因此我需要选择中间层或下层三个层的集合中的最大值作为宽度)。

为了实现这个功能,我想在多个\savedanchors 中使用代码,因此我定义了几个函数(一个用于形状的宽度,一个用于底层的高度),如中所述如何定义可在 \savedanchor 声明中重复使用的维度计算

因此,经过多次反复尝试,我开发出了一个多部分形状。下面有一个 WE(它可能不是 MWE,因为我有很多锚点——对于 MWE,我不需要那么多锚点。

这是我在编程过程中遇到的一些问题/评论。有人能告诉我这些是真的吗,还是我只是患有货物崇拜编程?

  1. 我需要一个\savedanchor用于绘制背景的每个点。
  2. \savedanchor我为形状的每个部分的文本创建了一个(但可能不需要) 。我需要这些吗\savedanchor
  3. 我不确定何时(或是否)需要使用\pgf@process{\mysavedanchor}而不是\mysavedanchor定义锚点。我有两者的示例。
  4. 我觉得这段代码与汇编语言编程的关系比与 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}

输出 形狀演示

相关内容