有没有办法在 let-Block 中定义一个函数?我有一些代码可以创建一个框架并将两个节点放入其中:
\documentclass[tikz]{standalone}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
\node (frame) [minimum width=4.5cm, minimum height=.5cm, draw=black, rounded corners=.1cm, anchor=east] at (0,0) {};
\draw[-, dashed] let \p1=(frame.north west), \p2=(frame.south east), \n1={.5*(\x2-\x1)} in (\x1+\n1, \y1) -- (\x1+\n1, \y2);
\path let \p1=(frame.north west), \p2=(frame.south east), \n1={.25*(\x2-\x1)}, \n2={.5*(\y1-\y2)} in node [align=center] at (\x1+\n1, \y1-\n2) { Left };
\path let \p1=(frame.north west), \p2=(frame.south east), \n1={.75*(\x2-\x1)}, \n2={.5*(\y1-\y2)} in node [align=center] at (\x1+\n1, \y1-\n2) { Right };
\end{tikzpicture}
\end{document}
创建两个节点(左和右)的代码非常相似,我经常需要这样的代码。我想写的代码不是这两\path let ..
行,而是这样的代码:
\path let \p1=(frame.north west), \p2=(frame.south east), \f{v}={v*(\x2-\x1)}, \n2={.5*(\y1-\y2)} in
node [align=center] at (\x1+\f{.25}, \y1-\n2) { Left }
node [align=center] at (\x1+\f{.75}, \y1-\n2) { Right };
因此,我不再编写两行单独的代码,而是定义一个函数f(v)={v*(\x2-\x1)}
,可以调用两次来定义绘制这两个节点的位置。完整的代码如下所示:
\documentclass[tikz]{standalone}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
\node (frame) [minimum width=4.5cm, minimum height=.5cm, draw=black, rounded corners=.1cm, anchor=east] at (0,0) {};
\draw[-, dashed] let \p1=(frame.north west), \p2=(frame.south east), \n1={.5*(\x2-\x1)} in (\x1+\n1, \y1) -- (\x1+\n1, \y2);
\path let \p1=(frame.north west), \p2=(frame.south east), \f{v}={v*(\x2-\x1)}, \n2={.5*(\y1-\y2)} in
node [align=center] at (\x1+\f{.25}, \y1-\n2) { Left }
node [align=center] at (\x1+\f{.75}, \y1-\n2) { Right };
\end{tikzpicture}
\end{document}
有什么想法可以做到这一点吗?提前感谢大家的帮助!
你好,塞巴斯蒂安
答案1
至少有两种可能的解决方案。请注意,在这两种情况下,如果您想在表达式中使用括号,则必须用括号括住 x 坐标。请参阅tikz 坐标中的算术(括号)由于这个原因。
然而,有人可能会争论,为这么简单的事情定义一个函数是否有点过头了。你应该权衡一下总是考虑的是它是否会使您的代码更可读或更难读。
解决方案 1:您可以使用键/pgf/declare function
来声明 pgfmath 函数,该函数由 pgfmath 引擎识别。这样做的好处是函数的作用域是路径本地的。您不会意外地在其他地方使用它。它看起来像这样:
\path[declare function={ f(\v)=\v * (\x2-\x1); }]
let \p1=(frame.north west), \p2=(frame.south east), \n2={.5*(\y1-\y2)} in
node [align=center] at ({\x1+f(0.25)}, \y1-\n2) { Left }
node [align=center] at ({\x1+f(0.75)}, \y1-\n2) { Right };
请注意,函数定义使用未定义的符号\x1
和\x2
,它们在函数执行时进行求值用过的,而不是当定义。根据某些定义,这可能被称为“非纯函数”。如果你在其他地方调用它,\x1
而这些地方\x2
没有定义,它可能会严重崩溃。
另外,您可以像您要求的那样将其放入 let 内的作用域中。作用域之后,定义消失:
\path let \p1=(frame.north west), \p2=(frame.south east), \n2={.5*(\y1-\y2)} in
{[declare function={ f(\v)=\v * (\x2-\x1); }]
node [align=center] at ({\x1+f(0.25)}, \y1-\n2) { Left }
node [align=center] at ({\x1+f(0.75)}, \y1-\n2) { Right }
}
% node at ({\x1+f(0.25)}, \y1-\n2) { Left } % Fails because f is undefined
;
解决方案 2:您可以使用\def
(或\newcommand*
) 来定义一个宏\f
。此宏将一直保留到组结束。
注意 整个 主体周围的括号\f
。这可以防止 的情况0.5 ^ \f{0.25}
分开,因为如果没有括号,这将被评估为(0.5 ^ 0.25) * (\x2 - \x1)
因为^
的优先级高于*
。
\def\f#1{(#1 * (\x2 - \x1))}
\path let \p1=(frame.north west), \p2=(frame.south east), \n2={.5*(\y1-\y2)} in
node [align=center] at ({\x1+\f{0.25}}, \y1-\n2) { Left }
node [align=center] at ({\x1+\f{0.75}}, \y1-\n2) { Right };
完整示例:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{math}
\begin{document}
Using \verb|\def|:
\begin{tikzpicture}
\node (frame) [minimum width=4.5cm, minimum height=.5cm, draw=black, rounded corners=.1cm, anchor=east] at (0,0) {};
\draw[-, dashed] let \p1=(frame.north west), \p2=(frame.south east), \n1={.5*(\x2-\x1)} in (\x1+\n1, \y1) -- (\x1+\n1, \y2);
\def\f#1{(#1 * (\x2 - \x1))}
\path let \p1=(frame.north west), \p2=(frame.south east), \n2={.5*(\y1-\y2)} in
node [align=center] at ({\x1+\f{0.25}}, \y1-\n2) { Left }
node [align=center] at ({\x1+\f{0.75}}, \y1-\n2) { Right };
\end{tikzpicture}
Using \verb|declare function|:
\begin{tikzpicture}
\node (frame) [minimum width=4.5cm, minimum height=.5cm, draw=black, rounded corners=.1cm, anchor=east] at (0,0) {};
\draw[-, dashed] let \p1=(frame.north west), \p2=(frame.south east), \n1={.5*(\x2-\x1)} in (\x1+\n1, \y1) -- (\x1+\n1, \y2);
\path[declare function={
f(\v)=\v * (\x2-\x1);
}] let \p1=(frame.north west), \p2=(frame.south east), \n2={.5*(\y1-\y2)} in
node [align=center] at ({\x1+f(0.25)}, \y1-\n2) { Left }
%node [align=center] at (\x1+\f{0.25}, \y1-\n2) { Left }
node [align=center] at ({\x1+f(0.75)}, \y1-\n2) { Right };
\end{tikzpicture}
\end{document}
答案2
您始终可以使用\newcommand
以避免在文档中重新输入重复的文本:
\documentclass[tikz]{standalone}
\usetikzlibrary{calc}
\newcommand{\myletcode}[2]{
let \p1=(frame.north west), \p2=(frame.south east), \n1={#1*(\x2-\x1)}, \n2={.5*(\y1-\y2)} in node [align=center] at (\x1+\n1, \y1-\n2) { #2 }
}
\begin{document}
\begin{tikzpicture}
\node (frame) [minimum width=4.5cm, minimum height=.5cm, draw=black, rounded corners=.1cm, anchor=east] at (0,0) {};
\draw[-, dashed] let \p1=(frame.north west), \p2=(frame.south east), \n1={.5*(\x2-\x1)} in (\x1+\n1, \y1) -- (\x1+\n1, \y2);
\path \myletcode{.25}{Left};
\path \myletcode{.75}{Right};
\end{tikzpicture}
\end{document}
尽管如果问题是这样的节点,您可以使用rectangle split
from shapes.multipart
。
在这种情况下,你应该使用text width
而不是minimum width
(见“最小宽度”对水平多部分节点没有影响),并且虚线分隔线是使用path picture
命令绘制的,因为我不知道如何为分割线固定特定的样式。
\documentclass[tikz]{standalone}
\usetikzlibrary{shapes.multipart}
\begin{document}
\begin{tikzpicture}[
mynode/.style={
rectangle split,
rectangle split parts=2,
rectangle split horizontal,
rectangle split draw splits=false,
path picture={%
\draw[dashed] (path picture bounding box.north)--
(path picture bounding box.south);},
align=center,
}]
\node (frame) [mynode, text width=2.25cm, minimum height=.5cm, draw=black, rounded corners=.1cm, anchor=east] {Left\nodepart{two}Right};
\end{tikzpicture}
\end{document}
答案3
使用该fit
包的另一个(简单)解决方案:
\documentclass[tikz]{standalone}
\usetikzlibrary{fit,
positioning}
\begin{document}
\begin{tikzpicture}[
node distance = 0pt,
box/.style = {text width=22.5mm, minimum height=5mm,
align=center, outer sep=0pt},
FIT/.style = {draw, rounded corners=1mm, inner sep=0pt, fit=#1}
]
\node (n1) [box] {left};
\node (n2) [box,right=of n1] {right};
\node (n3) [FIT=(n1) (n2)] {};
\draw[dashed] (n3.north) -- (n3.south);
\end{tikzpicture}
\end{document}