我想要创建一个 SPDT 开关符号节点,它看起来像这样:
因此,我想,我可以先从矩形节点派生出自定义形状,其大小将由 设定minimum width/height
;然后我可以在south west
、north west
和处添加小圆形节点,并从到节点east
绘制带箭头的连接线。我希望添加的节点是可寻址的(这样我以后可以将它们用作连接线);我还希望有一个参数来控制小圆形节点的大小。east
north west
我尝试阅读:
...因此我尝试编造一个例子,但是我在这一点上遇到了困难:
和矩形边框X
只是为了调试而添加的;它开始正常(我可以绘制一个圆形路径,也可以在同一位置添加一个圆形节点south west
),但问题是 - 我似乎无法正确计算“小”节点的主节点的高度north west
。
解决这个问题的正确方法是什么?另外,下面的 MWE 可以用于above=of
定位,但right=of
定位时会崩溃 - 有什么方法可以解决这个问题吗?
以下是我目前的 MWE:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning}
\usetikzlibrary{calc}
\usepackage[active,tightpage]{preview}
\PreviewEnvironment{tikzpicture}
% https://tex.stackexchange.com/questions/18400/can-a-shape-be-composed-out-of-subshapes-in-tikz
% https://tex.stackexchange.com/questions/65469/how-to-draw-saturation-symbol-inside-a-node-in-tikz
% https://tex.stackexchange.com/questions/73877/pgfdeclareshape-with-variable-dimensions-using-pgfkeys
\makeatletter
\pgfkeys{/tikz/spdtcircsize/.initial = 0.15cm}
\pgfdeclareshape{spdt}{
%\savedanchor\centerpoint{
% \pgf@x = .5\wd\pgfnodeparttextbox
% \pgf@y = .5\ht\pgfnodeparttextbox
%}
%\anchor{center}{\centerpoint}
\inheritsavedanchors[from={rectangle}]
\inheritbackgroundpath[from={rectangle}]
\inheritanchorborder[from={rectangle}]
\foreach \x in {center,north east,north west,north,south,south east,south west}{
\inheritanchor[from={rectangle}]{\x}
}
\saveddimen\circsize{\pgf@x=\pgfkeysvalueof{/tikz/spdtcircsize}}
% \savedanchor{\center}{%
% \pgfpointorigin}
% \anchor{center}{\center}
%%
% \backgroundpath{
\foregroundpath{
% \centerpoint
% \pgfkeys{/pgf/minimum size = \circsize}
% \pgfset{inner sep=1pt}
%%
\southwest
\pgfpathcircle{\southwest}{\circsize}
% \pgfusepath{draw}
{
\pgfkeys{/pgf/minimum size = \circsize}
\pgftransformshift{\southwest}
\pgfnode{circle}{center}{}{\tikz@fig@name-c1}{\pgfusepath{draw}}
}
%\pgfpathmoveto{\pgfpoint{0}{-\pgf@ya}}
\pgfpointdiff{\northeast}{\southwest}
\pgf@xa=\pgf@x \pgf@ya=\pgf@y
% \pgfpathmoveto{\pgfpointadd{\southwest}{\pgfpoint{0}{-1.0\pgf@ya}}}
\pgfpathcircle{\pgfpointadd{\southwest}{\pgfpoint{0}{-1.0\pgf@ya}}}{\circsize}
% \pgfnode{circle}{center}{}{\tikz@fig@name-c2}{}
}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\node[draw] (n1) at (1,0) {Testing};
% right= of causes ! Package PGF Math Error: Unknown function `west' (in 'west').
\node[spdt,draw,minimum width=20pt,minimum height=20pt] (n2) [above=10pt of n1] {X};
\end{tikzpicture}
\end{document}
答案1
最终解决方案:我将圆圈移到边界内,创建了新的锚点并绘制了开关。所有这些 \pgfscope 都是必需的。
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning}
\usetikzlibrary{calc}
\usetikzlibrary{arrows.meta}
\newlength{\circsize}
\newlength{\spdtright}
\newlength{\spdtup}
\newlength{\spdtleft}
\newlength{\spdtdown}
\newlength{\spdtlen}
\usepackage[active,tightpage]{preview}
\PreviewEnvironment{tikzpicture}
\pgfkeys{/tikz/spdtcircsize/.initial = 0.08cm}
\pgfdeclareshape{spdt}{
\inheritsavedanchors[from={rectangle}]
%\inheritbackgroundpath[from={rectangle}]
\inheritanchorborder[from={rectangle}]
\foreach \x in {center,text,north east,north west,north,south,south east,south west,east,west}{
\inheritanchor[from={rectangle}]{\x}
}
\anchor{out}{
\pgfextractx{\spdtright}{\northeast}%
\pgfextracty{\spdtup}{\northeast}%
\pgfextracty{\spdtdown}{\southwest}%
\pgfpoint{\spdtright}{0.5\spdtup+0.5\spdtdown}
}
\anchor{off}{
\circsize=\pgfkeysvalueof{/tikz/spdtcircsize}%
\pgfextractx{\spdtleft}{\southwest}%
\pgfextracty{\spdtdown}{\southwest}%
\pgfpoint{\spdtleft}{\spdtdown+\circsize}%
}
\anchor{on}{
\circsize=\pgfkeysvalueof{/tikz/spdtcircsize}%
\pgfextracty{\spdtup}{\northeast}%
\pgfextractx{\spdtleft}{\southwest}%
\pgfpoint{\spdtleft}{\spdtup-\circsize}%
}
\foregroundpath{
\circsize=\pgfkeysvalueof{/tikz/spdtcircsize}%
\pgfextractx{\spdtright}{\northeast}%
\pgfextracty{\spdtup}{\northeast}%
\pgfextractx{\spdtleft}{\southwest}%
\pgfextracty{\spdtdown}{\southwest}%
% compute sin and cos for sloped line
\pgfscope
\spdtlen=0.5\spdtup-0.5\spdtdown-\circsize% length
\pgfmathmultiply{\spdtlen}{\spdtlen}%
\let\spdtcos=\pgfmathresult% macro
\spdtlen=\spdtright-\spdtleft-2\circsize%
\pgfmathparse{sqrt(\spdtlen*\spdtlen+\spdtcos)}
\let\spdtsin=\pgfmathresult%
\pgfmathdivide{\spdtlen}{\spdtsin}%
\global\let\spdtcos=\pgfmathresult%
\spdtlen=0.5\spdtup-0.5\spdtdown-\circsize%
\pgfmathdivide{\spdtlen}{\spdtsin}%
\global\let\spdtsin=\pgfmathresult%
\endpgfscope
% draw circles
\pgfscope
\pgfpathcircle{\pgfpoint{\spdtleft+\circsize}{\spdtdown+\circsize}}{\circsize}%
\pgfpathcircle{\pgfpoint{\spdtleft+\circsize}{\spdtup-\circsize}}{\circsize}%
\pgfpathcircle{\pgfpoint{\spdtright-\circsize}{0.5\spdtup+0.5\spdtdown}}{\circsize}%
\pgfusepath{stroke}
\endpgfscope
% draw arrow
\pgfscope
\pgfsetarrowsend{Triangle[open]}%
\pgfpathmoveto{\pgfpoint{\spdtright-\circsize-\spdtcos\circsize}{0.5\spdtup+0.5\spdtdown+\spdtsin\circsize}}%
\pgfpathlineto{\pgfpoint{\spdtleft+\circsize+\spdtcos\circsize}{\spdtup-\circsize-\spdtsin\circsize}}%
\pgfusepath{stroke}%
\endpgfscope
}
}
\begin{document}
\begin{tikzpicture}
\node[draw] (n1) at (1,0) {Testing};
\node[spdt,draw,minimum width=20pt,minimum height=20pt] (n2) [above=10pt of n1] {};
\draw (n2.on) -- +(-0.2,0)
(n2.off) -- +(-0.2,0)
(n2.out) -- +(0.2,0);
\node[spdt,draw,minimum width=20pt,minimum height=20pt] (n3) [below=10pt of n1] {X};
\end{tikzpicture}
\end{document}
答案2
好的,我这里遇到了几个问题。
首先,我同时绘制了圆路径和圆节点,以确保我理解正确,这样我就可以根据需要使用其中任何一种。事实证明,必须小心\pgfdeclareshape 中圆形路径与圆形节点的大小- in\pgfpathcircle
有一个半径参数,但\pgfnode
viaminumum size
有一个直径参数。当然,在这种情况下,我只需要节点,因为我想在外部使用它们。
其次,我尝试计算主节点的高度和宽度(\pgfpointdiff{\northeast}{\southwest}
),因为我不知道如何访问它的锚点;结果发现有一个命令,\pgfpointanchor
-并且\tikz@fig@name
可以与它一起用来引用主节点。
第三,我不太了解 PGF 引擎——我的理解是,如果你调用类似的命令\pgfpointanchor
,那么你就会在内部寄存器中获得结果\pgf@x
,\pgf@y
然后你可以将其分配给临时寄存器等\pgf@xa
。\pgf@ya
因此,我认为这可以获取锚点坐标并绘制圆圈:
{
\pgftransformreset
\pgfpointanchor{\tikz@fig@name}{south west}
\xdef\mycoordinate{\noexpand\pgfpoint{\the\pgf@x}{\the\pgf@y}}
}
\pgfpathcircle{\mycoordinate}{\circsize}
... 但出于某种原因,它没有 -\mycoordinate
结果错了。但是 - 这是奇怪的部分 - 如果我把整个放在\pgfpointanchor{\tikz@fig@name}{south west}
而\pgfpathcircle
不是中\mycoordinate
,那么定位就没问题了?!
因此,考虑到所有这些因素,我得出了下面发布的 MWE,其结果如下:
红色节点(n2)
仅用于测试样式和位置;否则(n3)
其右侧就是如何使用它。
但还有一个问题 - 我从某处注意到自定义形状(通过\pgfdeclareshape
)应该只使用 PGF 基元,而不是 TikZ 绘图命令 - 但我必须使用\draw
,这样我才能轻松指定箭头尖端,并且该线连接圆形节点的边界 - 而不是它们的中心。如果有人知道可以使用 PGF 命令实现相同解决方案的解决方案,请发布答案。
除此之外,看来这个 MWE 为我做了这件事:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning}
\usetikzlibrary{calc}
\usetikzlibrary{arrows}
\usepackage[active,tightpage]{preview}
\PreviewEnvironment{tikzpicture}
\makeatletter
\pgfkeys{/tikz/.cd,
mycircsize/.store in=\circsize,
mycircsize=0.2cm
}
\pgfdeclareshape{spdt}{
\inheritsavedanchors[from={rectangle}]
\inheritbackgroundpath[from={rectangle}]
\inheritanchorborder[from={rectangle}]
\foreach \x in {center,north,north east,north west,south,south east,south west,east,west}{
\inheritanchor[from={rectangle}]{\x}
}
% \backgroundpath{
\foregroundpath{
\pgfkeys{/pgf/minimum size = \circsize}
\pgfset{inner sep=0pt,outer sep=0pt}
% draw connector nodes
\southwest
\pgfpathcircle{\southwest}{0.5*\circsize}% just a test
{
\pgftransformshift{\southwest}
\pgfnode{circle}{center}{}{\tikz@fig@name-c1}{\pgfusepath{draw}}
%\pgfusepath{draw}
}
{
\pgftransformshift{\pgfpointanchor{\tikz@fig@name}{north west}}
\pgfnode{circle}{center}{}{\tikz@fig@name-c2}{\pgfusepath{draw}}
}
{
\pgftransformshift{\pgfpointanchor{\tikz@fig@name}{east}}
\pgfnode{circle}{center}{}{\tikz@fig@name-c3}{\pgfusepath{draw}}
}
% draw switch arrow line
% with PGF primitives, line goes from center of nodes, not borders
%\pgfpathmoveto{\pgfpointanchor{\tikz@fig@name}{east}}
%\pgfpathlineto{\pgfpointanchor{\tikz@fig@name}{north west}}
% so using a TikZ draw command:
\draw[-open triangle 60] (\tikz@fig@name-c3) -- (\tikz@fig@name-c2);
}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\node[draw] (n1) at (1,0) {Testing};
\node[spdt,draw=red,line width=2pt,minimum width=20pt,minimum height=20pt] (n2) [above=10pt of n1] {X};
\node[spdt,minimum width=20pt,minimum height=20pt] (n3) [right=40pt of n2] {};
\draw (n1.west) -- ++(-15pt,0) |- (n2-c1);
\draw (n1.east) -- ++(15pt,0) |- (n2-c3);
\draw (n1.east) -- (n3-c3|-n1.east) -- ++(15pt,0) |- (n3-c3);
\end{tikzpicture}
\end{document}