我正在尝试在 Tikz 中创建自定义形状,以便我能够使用节点的选项来确定如何绘制这种形状。
举个例子带对角线填充的矩形节点考虑到这一点,作为第一次尝试,我尝试实现一个myshape
具有单个自定义锚点的简单矩形input
,该锚点应绘制在其左侧(选项input west
)或右侧(选项input east
)。 当然,我知道 Tikz 已经为我提供了实现这一点的方法,但这只是为了在创建更复杂的东西之前进行一个简单的实验。
然而,我的mwe
下面我的代码(也利用了一些取自示例:D 触发器和移位寄存器) 无法根据提供的选项计算锚点。事实上,它的行为就像未定义选项一样,即形状始终根据\else
我的\if...\else...\fi
构造的参数进行编译,这导致input
锚点始终绘制在下图中矩形的右侧。
\documentclass{beamer}
\usepackage{tikz}
\usetikzlibrary{positioning,}
\makeatletter
\newif\ifpgf@input@west
% Use these with PGF
\def\pgfsetinputwest{\pgf@input@westtrue}%
\def\pgfsetinputeast{\pgf@input@westfalse}%
% Use these with TikZ
\tikzoption{input west}[]{\pgfsetinputwest}
\tikzoption{input east}[]{\pgfsetinputeast}
% Custom shape (myshape)
\pgfdeclareshape{myshape}{
% The 'minimum width' and 'minimum height' keys, not the content, determine the size
\savedanchor\northeast{%
\pgfmathsetlength\pgf@x{\pgfshapeminwidth}%
\pgfmathsetlength\pgf@y{\pgfshapeminheight}%
\pgf@x=0.5\pgf@x
\pgf@y=0.5\pgf@y
}
% This is redundant, but makes some things easier:
\savedanchor\southwest{%
\pgfmathsetlength\pgf@x{\pgfshapeminwidth}%
\pgfmathsetlength\pgf@y{\pgfshapeminheight}%
\pgf@x=-0.5\pgf@x
\pgf@y=-0.5\pgf@y
}
% Inherit from rectangle
\inheritanchorborder[from=rectangle]
% Define same anchor a normal rectangle has
\anchor{center}{\pgfpointorigin}
\anchor{north}{\northeast \pgf@x=0pt}
\anchor{east}{\northeast \pgf@y=0pt}
\anchor{south}{\southwest \pgf@x=0pt}
\anchor{west}{\southwest \pgf@y=0pt}
\anchor{north east}{\northeast}
\anchor{north west}{\northeast \pgf@x=-\pgf@x}
\anchor{south west}{\southwest}
\anchor{south east}{\southwest \pgf@x=-\pgf@x}
\anchor{text}{
\pgfpointorigin
\advance\pgf@x by -.5\wd\pgfnodeparttextbox%
\advance\pgf@y by -.5\ht\pgfnodeparttextbox%
\advance\pgf@y by +.5\dp\pgfnodeparttextbox%
}
% Define anchors for input
\ifpgf@input@west
\anchor{input}{
\pgf@process{\northeast}%
\pgf@x=-1\pgf@x%
\pgf@y=0pt%
}
\else
\anchor{input}{
\pgf@process{\northeast}%
\pgf@x=1\pgf@x%
\pgf@y=0pt%
}
\fi
% Draw the rectangle box and the port labels
\backgroundpath{
% Rectangle box
\pgfpathrectanglecorners{\southwest}{\northeast}
}
}
% Key to add font macros to the current font
\tikzset{add font/.code={\expandafter\def\expandafter\tikz@textfont\expandafter{\tikz@textfont#1}}}
% Define default style for this node
\tikzset{every myshape node/.style={draw,minimum width=0.4cm,minimum
height=0.4cm,inner sep=1mm,outer sep=0pt,cap=round,add
font=\sffamily}}
\makeatother
\begin{document}
\begin{frame}{MWE}
\begin{figure}
\centering
\begin{tikzpicture}[auto, node distance=4mm and 4mm]%
\node (start) at (0,0) {};
\node [myshape, draw, input west] (myshape_w) [right=of start] {};
\node [myshape, draw, input east] (myshape_e) [below=of myshape_w] {};
\draw [-latex] (start.center) -- (myshape_w.input);
\draw [-latex] (start.center) |- (myshape_e.input);
\end{tikzpicture}
\end{figure}
\end{frame}
\end{document}
我不太明白为什么我的mwe
会失败,我只知道一旦引入它,它就会开始偏离我的预期\if...\else...\fi
,所以我担心我缺乏一些关于这种低级 Tex 编码如何工作的基本知识。如果能为我指明正确的方向,我将不胜感激。
答案1
有两个问题。锚点在您尚未决定它应该是西还是东时进行评估,并且还需\if
要让形状“知道”。所以基本上您需要\deferredanchor
(参见 pgfmanual v3.1.4 第 1129 页)和\savedmacro
(参见第 1128 页)。我还发现更改一个简单的数字比使用 更直观\if
。
\documentclass{beamer}
\usepackage{tikz}
\usetikzlibrary{positioning}
\makeatletter
% Use these with TikZ
\def\my@side{1}
\tikzset{input west/.code=\def\my@internal@side{-1}}
\tikzset{input east/.code=\def\my@internal@side{1}}
% Custom shape (myshape)
\pgfdeclareshape{myshape}{
% The 'minimum width' and 'minimum height' keys, not the content, determine the size
\savedanchor\northeast{%
\pgfmathsetlength\pgf@x{\pgfshapeminwidth}%
\pgfmathsetlength\pgf@y{\pgfshapeminheight}%
\pgf@x=0.5\pgf@x
\pgf@y=0.5\pgf@y
}
% This is redundant, but makes some things easier:
\savedanchor\southwest{%
\pgfmathsetlength\pgf@x{\pgfshapeminwidth}%
\pgfmathsetlength\pgf@y{\pgfshapeminheight}%
\pgf@x=-0.5\pgf@x
\pgf@y=-0.5\pgf@y
}
% Inherit from rectangle
\inheritanchorborder[from=rectangle]
% Define same anchor a normal rectangle has
\anchor{center}{\pgfpointorigin}
\anchor{north}{\northeast \pgf@x=0pt}
\anchor{east}{\northeast \pgf@y=0pt}
\anchor{south}{\southwest \pgf@x=0pt}
\anchor{west}{\southwest \pgf@y=0pt}
\anchor{north east}{\northeast}
\anchor{north west}{\northeast \pgf@x=-\pgf@x}
\anchor{south west}{\southwest}
\anchor{south east}{\southwest \pgf@x=-\pgf@x}
\anchor{text}{
\pgfpointorigin
\advance\pgf@x by -.5\wd\pgfnodeparttextbox%
\advance\pgf@y by -.5\ht\pgfnodeparttextbox%
\advance\pgf@y by +.5\dp\pgfnodeparttextbox%
}
\savedmacro{\my@side}{\let\my@side\my@internal@side}
% Define anchors for input
\deferredanchor{input}{
\pgf@process{\northeast}%
\pgf@x=\my@side\pgf@x%
\pgf@y=0pt%
}
% Draw the rectangle box and the port labels
\backgroundpath{
% Rectangle box
\pgfpathrectanglecorners{\southwest}{\northeast}
}
}
% Key to add font macros to the current font
\tikzset{add font/.code={\expandafter\def\expandafter\tikz@textfont\expandafter{\tikz@textfont#1}}}
% Define default style for this node
\tikzset{every myshape node/.style={draw,minimum width=0.4cm,minimum
height=0.4cm,inner sep=1mm,outer sep=0pt,cap=round,add
font=\sffamily}}
\makeatother
\begin{document}
\begin{frame}{MWE}
\begin{figure}
\centering
\begin{tikzpicture}[auto, node distance=4mm and 4mm,line cap=rect]%
\coordinate (start) at (0,0);
\node [input west,myshape, draw] (myshape_w) [right=of start] {};
\node [input east,myshape, draw] (myshape_e) [below=of myshape_w] {};
\draw [-latex] (start) -- (myshape_w.input);
\draw [-latex] (start) |- (myshape_e.input);
\end{tikzpicture}
\end{figure}
\end{frame}
\end{document}
我还想提请您注意一下/.is choice
关键点,这使得我们可以让事情变得更加优雅。
\documentclass{beamer}
\usepackage{tikz}
\usetikzlibrary{positioning}
\makeatletter
% Use these with TikZ
\def\my@side{1}
\tikzset{input/.is choice,
input/west/.code=\def\my@internal@side{-1},
input/east/.code=\def\my@internal@side{1},
input=east}
% Custom shape (myshape)
\pgfdeclareshape{myshape}{
% The 'minimum width' and 'minimum height' keys, not the content, determine the size
\savedanchor\northeast{%
\pgfmathsetlength\pgf@x{\pgfshapeminwidth}%
\pgfmathsetlength\pgf@y{\pgfshapeminheight}%
\pgf@x=0.5\pgf@x
\pgf@y=0.5\pgf@y
}
% This is redundant, but makes some things easier:
\savedanchor\southwest{%
\pgfmathsetlength\pgf@x{\pgfshapeminwidth}%
\pgfmathsetlength\pgf@y{\pgfshapeminheight}%
\pgf@x=-0.5\pgf@x
\pgf@y=-0.5\pgf@y
}
% Inherit from rectangle
\inheritanchorborder[from=rectangle]
% Define same anchor a normal rectangle has
\anchor{center}{\pgfpointorigin}
\anchor{north}{\northeast \pgf@x=0pt}
\anchor{east}{\northeast \pgf@y=0pt}
\anchor{south}{\southwest \pgf@x=0pt}
\anchor{west}{\southwest \pgf@y=0pt}
\anchor{north east}{\northeast}
\anchor{north west}{\northeast \pgf@x=-\pgf@x}
\anchor{south west}{\southwest}
\anchor{south east}{\southwest \pgf@x=-\pgf@x}
\anchor{text}{
\pgfpointorigin
\advance\pgf@x by -.5\wd\pgfnodeparttextbox%
\advance\pgf@y by -.5\ht\pgfnodeparttextbox%
\advance\pgf@y by +.5\dp\pgfnodeparttextbox%
}
\savedmacro{\my@side}{\let\my@side\my@internal@side}
% Define anchors for input
\deferredanchor{input}{
\pgf@process{\northeast}%
\pgf@x=\my@side\pgf@x%
\pgf@y=0pt%
}
% Draw the rectangle box and the port labels
\backgroundpath{
% Rectangle box
\pgfpathrectanglecorners{\southwest}{\northeast}
}
}
% Key to add font macros to the current font
\tikzset{add font/.code={\expandafter\def\expandafter\tikz@textfont\expandafter{\tikz@textfont#1}}}
% Define default style for this node
\tikzset{every myshape node/.style={draw,minimum width=0.4cm,minimum
height=0.4cm,inner sep=1mm,outer sep=0pt,cap=round,add
font=\sffamily}}
\makeatother
\begin{document}
\begin{frame}{MWE}
\begin{figure}
\centering
\begin{tikzpicture}[auto, node distance=4mm and 4mm,line cap=rect]%
\coordinate (start) at (0,0);
\node [input=west,myshape, draw] (myshape_w) [right=of start] {};
\node [input=east,myshape, draw] (myshape_e) [below=of myshape_w] {};
\draw [-latex] (start) -- (myshape_w.input);
\draw [-latex] (start) |- (myshape_e.input);
\end{tikzpicture}
\end{figure}
\end{frame}
\end{document}