在下面的 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
和扩展\edef
inition 之间的区别,但你可以将前者视为已读取但尚未执行定义的类型。
因此,您可以按如下方式定义代码
\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}