如何定义可在 \savedanchor 声明中重复使用的维度计算?

如何定义可在 \savedanchor 声明中重复使用的维度计算?

在下面的 MWE 中,我声明了一个新形状myshape。在几个实例中,我计划使用从不同的 pgf 键(\my@width)计算出来的维度。其中一个实例包括已保存的锚点的声明\base。目前,我必须复制计算这个维度的代码,因为我不能简单地在声明\my@width内部使用\savedanchor(它似乎不可用或默认为 0/空)。所以我想知道是否有办法实现这一点,或者我只是本末倒置,即我是否应该完全放弃维度(我假设我不能使用声明中的已保存锚点\saveddimen)并且只依赖已保存的锚点定义?

\documentclass[a4paper]{article}
\usepackage{tikz}

\makeatletter
\pgfdeclareshape{myshape}
{
  %
  % Saved dimensions
  %
  \saveddimen{\my@width}{%
    \pgfmathsetlength{\pgf@xa}{\pgfkeysvalueof{/pgf/minimum width}}
    \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/inner xsep}}
    \pgfmathsetlength{\pgf@xc}{\pgfkeysvalueof{/pgf/outer xsep}}
    \pgf@x=.5\pgf@xa
    \advance\pgf@x by .5\pgf@xb
    \advance\pgf@x by .5\pgf@xc
  }
  \saveddimen{\my@height}{%
    \pgf@x=\pgfkeysvalueof{/pgf/minimum height}%
  }

  %
  % Saved anchors
  %
  \savedanchor\base{%
    % Duplicated code, can't use \my@width here
    \pgfmathsetlength{\pgf@xa}{\pgfkeysvalueof{/pgf/minimum width}}
    \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/inner xsep}}
    \pgfmathsetlength{\pgf@xc}{\pgfkeysvalueof{/pgf/outer xsep}}
    \pgf@x=.5\pgf@xa
    \advance\pgf@x by .5\pgf@xb
    \advance\pgf@x by .5\pgf@xc
    \pgf@y=0pt
  }

  %
  % Anchors
  %
  \anchor{center}{%
    \base
    \pgfutil@tempdima=\my@height
    \advance\pgf@y by .5\pgfutil@tempdima
  }
  \anchor{south}{\base}

  %
  % Background path
  %
  \backgroundpath{%
    \pgfutil@tempdima=\my@width
    \pgfutil@tempdimb=\my@height
    \pgf@xa=.5\pgfutil@tempdima
    \pgf@ya=0pt
    \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
    \advance\pgf@ya by \pgfutil@tempdimb
    \pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@ya}}
    \advance\pgf@xa by -.5\pgfutil@tempdima
    \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
    \advance\pgf@xa by \pgfutil@tempdima
    \pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@ya}}
  }
}
\tikzset{
  every myshape node/.style={
    draw=black, thick, minimum width=1cm, minimum height=2.5cm,
  }
}
\makeatother

\begin{document}
\begin{tikzpicture}
  \node[myshape] at (0,0) {};
  \node[myshape, inner sep=10pt] at (3,0) {};
\end{tikzpicture}
\end{document}

答案1

这个答案基于聊天中的长时间讨论。

这个答案避免了保存维度并使用已保存的锚点。

锚点\northeast存储高度(包括outer ysep)和宽度的一半(包括 的一半outer xsep)。这是\my@width\my@height在一个锚点中。

锚点\innerwest存储外部分隔符(默认为,.5\pgflinewidth这就是锚点位于线外侧的原因)。这在正确对齐 TikZ 中两个矩形节点角之间绘制的线条?

一个锚点基本上就是两个\saveddimen。请注意,我在计算所有其他锚点时都使用了这两个锚点。

节点放置arc仅适用于 TikZ 的当前 CVS 版本。

代码

\documentclass[tikz,border=2pt]{standalone}
\tikzset{
  hdc/.style={draw,shape=half double crochet},
  crochet/.style={/crochet/.cd,#1},
}
\newcommand*{\crochetset}{\pgfqkeys{/crochet}}% use just like \tikzset{...}
\crochetset{
    height/.initial=2.5cm,
    width/.initial=1cm,
    %%% is there more?
}

\makeatletter
\pgfdeclareshape{half double crochet}{% hdc
  \savedanchor\northeast{%
    \pgfmathsetlength\pgf@x{\pgfkeysvalueof{/crochet/width}+2*\pgfkeysvalueof{/pgf/outer xsep}}%
    \pgf@x=.5\pgf@x
    \pgfmathsetlength\pgf@y{\pgfkeysvalueof{/crochet/height}+2*\pgfkeysvalueof{/pgf/outer ysep}}%
  }
  \savedanchor\innerwest{%
    \pgfmathsetlength\pgf@x{-\pgfkeysvalueof{/pgf/outer xsep}}%
    \pgfmathsetlength\pgf@y{\pgfkeysvalueof{/crochet/height}}%
  }
  \anchor{base}{\pgfpointorigin}%
  \anchor{south}{\pgfpointorigin}%
  \anchor{north}{\northeast\pgf@x\z@}%
  \anchor{west}{\innerwest\pgf@xa\pgf@x\northeast\[email protected]\pgf@y\pgf@x\pgf@xa}%
  \anchor{east}{\innerwest\pgf@xa-\pgf@x\northeast\[email protected]\pgf@y\pgf@x\pgf@xa}%
  \anchor{north east}{\northeast}%
  \anchor{north west}{\northeast\pgf@x-\pgf@x}%
  \anchor{south west}{\innerwest\pgf@y\z@}%
  \anchor{south east}{\innerwest\pgf@y\z@\pgf@x-\pgf@x}%
  \anchor{center}{\northeast\pgf@x\z@\[email protected]\pgf@y}%
  \anchor{bar east}{\innerwest\pgf@xa\pgf@x\northeast\advance\pgf@y\pgf@xa}%
  \anchor{bar west}{\innerwest\pgf@xa\pgf@x\northeast\advance\pgf@y\pgf@xa\pgf@x-\pgf@x}%
  \anchor{inner west}{\innerwest}%
  \anchor{inner east}{\innerwest\pgf@x-\pgf@x}%
  \anchor{bar south east}{\northeast\pgf@xa\pgf@x\innerwest\pgf@x\pgf@xa}
  \anchor{bar south west}{\northeast\pgf@xa-\pgf@x\innerwest\pgf@x\pgf@xa}

  \backgroundpath{% the path will only be needed the first time the shape is used,
                  % so we can use outer ysep without the need to save it.
    \northeast
    \pgf@xa=\pgf@x
    \pgfmathsetlength\pgf@ya{\pgf@y-\pgfkeysvalueof{/pgf/outer ysep}}%
    \pgfmathsetlength\pgf@xa{\pgf@x-\pgfkeysvalueof{/pgf/outer xsep}}%
    \pgfsetrectcap
    \pgfpathmoveto{\pgfqpoint{-\pgf@xa}{\pgf@ya}}%
    \pgfpathlineto{\pgfqpoint{ \pgf@xa}{\pgf@ya}}%
    \advance\pgf@ya-\pgflinewidth
    \pgfpathmoveto{\pgfqpoint{\z@}{\pgf@ya}}%
    \pgfmathsetlength\pgf@ya{\pgfkeysvalueof{/pgf/outer ysep}}%
    \pgfpathlineto{\pgfqpoint{\z@}{\pgf@ya}}%
  }
}
\makeatother

\begin{document}
\begin{tikzpicture}[line width=10pt,opacity=.5]
  \node[hdc] (a) at (0,0) {};
  \crochetset{width=2cm}
  \node[hdc,red] (b') at (5,0) {};
  \node[hdc, outer sep=10pt] (b) at (5,0) {};
  \foreach \anchor/\pos in {
    north east/right,
    north west/left,
    south west/left,
    south east/right,
    north/above,
    west/left,
    south/below,
    east/right,
    bar east/right,
    bar west/left,
    bar south east/right,
    bar south west/left,
    inner west/below left,
    inner east/below right,
%    base/below,%
    center/below%
  }{
    \fill[opacity=.7] (a.\anchor) circle (1pt) node[\pos,inner sep=-2pt,font=\tiny] {\anchor};
    \fill[opacity=.7] (b.\anchor) circle (1pt) node[\pos,inner sep=-2pt,font=\tiny] {\anchor};
  }
\end{tikzpicture}
\begin{tikzpicture}
\draw[gray] (0,0) arc[start angle=0, end angle=180, radius=1cm] \foreach \pos in {0,0.25,0.5,0.75,1} {node [sloped,hdc,black,pos=\pos,crochet={height=1cm,width=.25cm}] {}};
\end{tikzpicture}
\begin{tikzpicture}
\draw[gray] (0,0) arc[start angle=0, end angle=180, radius=1cm] \foreach \pos in {0,0.25,0.5,0.75,1} {node [sloped,hdc,black,anchor=south,pos=\pos,crochet={height=1cm,width=.25cm}] {}};
\end{tikzpicture}
\end{document}

输出

在此处输入图片描述

在此处输入图片描述

在此处输入图片描述

答案2

这是一个很长的评论,因为我会试图阻止你这样做:),但更长的解释可以在 Andrew Stacey 的精彩回答中找到在 \pgfdeclareshape 命令中使用已保存的维度

我真的不知道该如何或从哪里开始解释 :) 让我先给出一个提示。这些声明首先被读取但未执行。当所有内容都读取后,TikZ 便开始通过以非常精心设计的顺序执行操作来慢慢构建所需的部分。

这就是宏家族的本质\saved<>。粗略地说,他们收获代码在形状创建过程中,需要时执行。它们还会收集所需的长度表达式,这些表达式取决于某些键的值,这些值不是在读取代码时收集的,而是在创建节点时收集的。

一个具体的例子是常规的anchor,让我们转到手册(这真的很棒):

\anchor{<name>}{<code>}

此命令声明一个名为 的锚点<name>。与已保存的锚点不同, 不会在<code>每次声明节点时执行。相反,<code>仅在明确请求锚点时执行;无论是在创建节点期间锚定节点,还是作为稍后引用的形状中的位置。

现在,我们可以看到一些定义被备份并存储起来以供以后执行,例如,为了节省资源。同样,如果在执行时一切都可用,这是不可能的。

这不是我的答案,但我的观点是,这样做非常非常不切实际。相反,你可以将这种方法更进一步。我没有专业知识来完全解释 inition\def和扩展\edefinition 之间的区别,但你可以将前者视为已读取但尚未执行定义的类型。

因此,您可以按如下方式定义代码

\def\my@widthcode{% This is the recycled code that will be used more than once.
    \pgfmathsetlength{\pgf@xa}{\pgfkeysvalueof{/pgf/minimum width}}
    \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/inner xsep}}
    \pgfmathsetlength{\pgf@xc}{\pgfkeysvalueof{/pgf/outer xsep}}
    \pgf@x=.5\pgf@xa
    \advance\pgf@x by .5\pgf@xb
    \advance\pgf@x by .5\pgf@xc
}

这将被读取,并可在形状声明的任何位置使用。现在我们可以使用它

  \saveddimen{\my@width}{%
     \my@widthcode
  }

并且

  \savedanchor\base{%
    % Duplicated code, can't use \my@width here
    \my@widthcode
    \pgf@y=0pt
  }

所以整体代码如下,

\documentclass[a4paper]{article}
\usepackage{tikz}

\makeatletter
\def\my@widthcode{
    \pgf@xa=\pgfkeysvalueof{/pgf/minimum width}
    \pgf@xb=\pgfkeysvalueof{/pgf/inner xsep}
    \pgf@xc=\pgfkeysvalueof{/pgf/outer xsep}
    \pgf@x=.5\pgf@xa
    \advance\pgf@x by .5\pgf@xb
    \advance\pgf@x by .5\pgf@xc
}
\pgfdeclareshape{myshape}
{
  %
  % Saved dimensions
  %
  \saveddimen{\my@width}{%
     \my@widthcode
  }
  \saveddimen\my@height{%
    \pgf@x=\pgfkeysvalueof{/pgf/minimum height}%
  }
  %
  % Saved anchors
  %
  \savedanchor\base{%
    \my@widthcode
    \pgf@y=0pt
  }

  %
  % Anchors
  %
  \anchor{center}{%
    \base
    \pgfutil@tempdima=\my@height
    \advance\pgf@y by .5\pgfutil@tempdima
  }
  \anchor{south}{\base}

  %
  % Background path
  %
  \backgroundpath{%
    \pgfutil@tempdima=\my@width
    \pgfutil@tempdimb=\my@height
    \pgf@xa=.5\pgfutil@tempdima
    \pgf@ya=0pt
    \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
    \advance\pgf@ya by \pgfutil@tempdimb
    \pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@ya}}
    \advance\pgf@xa by -.5\pgfutil@tempdima
    \pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
    \advance\pgf@xa by \pgfutil@tempdima
    \pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@ya}}
  }
}
\tikzset{
  every myshape node/.style={
    draw=black, thick, minimum width=1cm, minimum height=2.5cm,
  }
}
\makeatother

\begin{document}
\begin{tikzpicture}
  \node[myshape] at (0,0) {};
  \node[myshape, inner sep=10pt] at (3,0) {};
\end{tikzpicture}
\end{document}

在此处输入图片描述

相关内容