![通过两个平行箭头连接 Tikz 节点](https://linux22.com/image/307196/%E9%80%9A%E8%BF%87%E4%B8%A4%E4%B8%AA%E5%B9%B3%E8%A1%8C%E7%AE%AD%E5%A4%B4%E8%BF%9E%E6%8E%A5%20Tikz%20%E8%8A%82%E7%82%B9.png)
我刚刚开始学习使用 Tikz,对其看似无限的选项和可能性有点迷失。
我试图做的是可视化几个 Matlab 函数交互(哪个调用哪个,输入和输出是什么)。由于只有大约六个子程序,我将每个子程序手动放置为一个节点。这是一个例子:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes,positioning}
\begin{document}
\begin{tikzpicture}[auto]
\node [rounded rectangle,draw] (node1) {Node 1};
\coordinate [above=of node1] (input);
\node [rounded rectangle,draw] at (-10:6cm) (node2) {Node 2};
\draw [->,very thick] ([xshift=0.1cm] input.south) --
node {right} ([xshift=0.1cm] node1.north);
\draw [->,very thick] ([xshift=-0.1cm] node1.north) --
node {left} ([xshift=-0.1cm] input.south);
\draw (node1) -- (node2);
\end{tikzpicture}
\end{document}
现在我想将我使用 xshift 创建的这些双进/出箭头(可能做得很别扭)应用于所有节点连接,因此在此示例中也应用于节点 1 和节点 2 之间的连接。理想情况下,这两个箭头之间的空间应该相同,与它们的角度无关,并且它们可以很好地连接到节点的外线。有什么想法可以实现这一点吗?我发现类似的问题只使用了矩形和正交线。
答案1
最小代码
\documentclass[tikz]{standalone}
\usetikzlibrary{arrows.meta}
\begin{document}
\pgfkeys{
/pgf/arrow keys/.cd,
shear/.store in=\pgfarrowshear,
US/.style={
length = +1.05pt 1.925 1,
shear = 1.7pt,
},
UK/.style={
length = +1.05pt 1.925 1,
shear = -1.7pt,
},
}
\makeatletter
\newdimen\pgfarrowshear
\let\oldmacro\pgf@arrow@drawer@shift
\def\pgf@arrow@drawer@shift{\pgftransformyshift\pgfarrowshear\oldmacro}
\begin{tikzpicture}
\path(1,3)node(A){A}(3,1)node(B){B};;
\draw[double,double distance=3pt,{<[US]}-{>[US]},bend left](A)to(B){};
\draw[double,double distance=3pt,{<[UK]}-{>[UK]},bend left](B)to(A){};
\end{tikzpicture}
\end{document}
一步步
不失一般性,假设箭头从左指向右。
铟pgfcorearrows.code.tex
,钛钾z 使用移动箭头尖端\pgf@arrow@drawer@shift
,定义为
\def\pgf@arrow@drawer@shift#1#2#3{% tip end, back end, line end, sep \pgf@xb#2\pgftransformxshift{-\pgf@xb}% \pgf@xc#1% \advance\pgf@xc by\pgfarrowsep% \advance\pgf@xc by-\pgf@xb% }
这一步是必要的,因为有时我们有shorten >=
或sep=
(以防有更多提示)。但是没有y shift
应用,因为逻辑上箭头提示与主路径对齐。
因此,我们有一个肮脏的步骤,它将此宏重写如下
\def\pgf@arrow@drawer@shift#1#2#3{
\pgftransformyshift\pgfarrowshear% add this line
\pgf@xb#2\pgftransformxshift{-\pgf@xb}%
\pgf@xc#1%
\advance\pgf@xc by\pgfarrowsep%
\advance\pgf@xc by-\pgf@xb%
}
接下来我们想让shear=
它像 一样sep=
。所以我修改了sep/.code
\pgfkeys{
/pgf/arrow keys/.cd,
shear/.code={
\pgfarrowsthreeparameters{#1}%
\expandafter\pgfarrowsaddtooptions\expandafter{\expandafter\pgfarrowslinewidthdependent\pgfarrowstheparameters\pgfarrowshear\pgf@x}%
},
shear/.default = +0pt -.5 -.5
}
因此,现在我们可以通过以下方式实现以下目标:[-{>[shear=0pt .5 .5]>[shear]>[shear=0pt .5 .5]>[shear]}]
线宽相关
然而,\pgfarrowslinewidthdependent
永远不会产生正确数量的y shift
。如果你仔细看手册,它会计算
wi = inner line width wo = outer line width wt = total line width = inner + 2*outer w = #3*wo + (1-#3)*wt = weighted line width return #1 + #2*w
也就是说
#1 + #2 * [ (1-#3)*wi + (2-#3)*wo ]
我们想要的是
0 + .5*wi + .5*wo
这导致
#1 = 0
#2 = 0
#3 = ∞
我不知道为什么 Ti钾Z 就这么做吧。无论如何,我们可以自己做。
\def\pgfarrowslinewidthdependentnew#1#2#3{%
\pgf@x#1%
\ifdim\pgfinnerlinewidth>0pt%
\pgf@arrows@inner@line@width@depnew{#2}{#3}%
\else%
\advance\pgf@x by#2\pgflinewidth%
\fi%
}
\def\pgf@arrows@inner@line@width@depnew#1#2{%
% #1 * outer line width + #2 * inner line width = our new one = the following
% (#1/2) * full line width + (#2-#1/2) * inner line width)
% Compute "real" line width
\[email protected]\pgflinewidth%
\pgf@xa#1\pgf@xa%
\advance\pgf@x by\pgf@xa%
\pgf@xa\pgfinnerlinewidth%
\[email protected]\pgf@xa%
\advance\pgf@x by#2\pgf@xa%
\advance\pgf@x by-#1\pgf@xb%
}
为了方便
我们定义
\pgfkeys{
/pgf/arrow keys/.cd,
Bidirectional/.style={
length = +1.05pt 1.925 1,
shear
}
}
所以我们可以
\begin{tikzpicture}
\path(0,4)(4,0);
\node(A)at(1,3){A};
\node(B)at(3,1){B};
\draw[double,double distance=3pt,{<[Bidirectional]}-{>[Bidirectional]},bend left]
(A)to(B){};
\end{tikzpicture}
代码
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\begin{document}
\makeatletter
\pgfkeys{
/pgf/arrow keys/.cd,
Bidirectional/.style={
length = +1.05pt 1.925 1,
shear
},
shear/.code={
\pgfarrowsthreeparameters{#1}%
\expandafter\pgfarrowsaddtooptions\expandafter{\expandafter\pgfarrowslinewidthdependentnew\pgfarrowstheparameters\pgfarrowshear\pgf@x}%
},
shear/.default = +0pt -.5 -.5
}
\newdimen\pgfarrowshear
\pgfarrowshear0pt
\def\pgfarrowslinewidthdependentnew#1#2#3{%
\pgf@x#1%
\ifdim\pgfinnerlinewidth>0pt%
\pgf@arrows@inner@line@width@depnew{#2}{#3}%
\else%
\advance\pgf@x by#2\pgflinewidth%
\fi%
}
\def\pgf@arrows@inner@line@width@depnew#1#2{%
% #1 * outer line width + #2 * inner line width = our new one = the following
% (#1/2) * full line width + (#2-#1/2) * inner line width)
% Compute "real" line width
\[email protected]\pgflinewidth%
\pgf@xa#1\pgf@xa%
\advance\pgf@x by\pgf@xa%
\pgf@xa\pgfinnerlinewidth%
\[email protected]\pgf@xa%
\advance\pgf@x by#2\pgf@xa%
\advance\pgf@x by-#1\pgf@xb%
}
\def\pgf@arrow@drawer@shift#1#2#3{
\pgftransformyshift\pgfarrowshear%
\pgf@xb#2\pgftransformxshift{-\pgf@xb}%
\pgf@xc#1%
\advance\pgf@xc by\pgfarrowsep%
\advance\pgf@xc by-\pgf@xb%
}
\begin{tikzpicture}
\draw[-{>[sep=1pt]>}](1,1)->(3,1);
\end{tikzpicture}
\begin{tikzpicture}[>={Classical TikZ Rightarrow[length = +1.05pt 1.925 1]}]
\draw[double distance=3pt,-{>[shear=0pt .5 .5]>[shear]>[shear=0pt .5 .5]>[shear]}]
(1,1)->(3,1);
\end{tikzpicture}
\begin{tikzpicture}
\path(0,4)(4,0);
\node(A)at(1,3){A};
\node(B)at(3,1){B};
\draw[double,double distance=3pt,{<[Bidirectional]}-{>[Bidirectional]},bend left]
(A)to(B){};
\end{tikzpicture}
\end{document}
答案2
碰巧的是,我自己找到了一个解决方案,使用 calc 和 intersections 库:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes,positioning,calc,intersections}
\begin{document}
\begin{tikzpicture}[auto,>=stealth]
\node [rounded rectangle,draw,name path=node1](node1) {Node 1};
\coordinate [above=of node1] (input);
\node [rounded rectangle,draw,name path=node2] at (-10:6cm) (node2) {Node 2};
\draw [->,very thick] ([xshift=2pt] input.south) --
node {right} ([xshift=2pt] node1.north);
\draw [->,very thick] ([xshift=-2pt] node1.north) --
node {left} ([xshift=-2pt] input.south);
\path [name path=node12] let \p1 = ($(node2)-(node1)$)
in (node1) ($(node1)!2pt!($(node1)+(-\y1,\x1)$)$) to +(\p1);
\path [name path=node21] let \p1 = ($(node2)-(node1)$)
in (node1) ($(node1)!-2pt!($(node1)+(-\y1,\x1)$)$) to +(\p1);
\node [name intersections={of=node1 and node12}] (start) at (intersection-1){};
\draw [->,very thick,name intersections={of=node2 and node12}]
(start.center) -- node[sloped] {in} (intersection-1);
\node [name intersections={of=node2 and node21}] (start) at (intersection-1){};
\draw [->,very thick,name intersections={of=node1 and node21}]
(start.center) -- node[sloped] {out} (intersection-1);
\end{tikzpicture}
\end{document}
首先,我创建两条与节点中心直接连接平行的路径。我通过使用 let 命令计算节点中心之间的差异向量,将起点沿正交方向移动(在这里我可以定义箭头之间的空间量),然后沿着之前计算的向量移动路径。
接下来,我计算该路径与节点 1 的交点并将其坐标保存为节点(我尝试仅使用坐标,但由于未知原因,它似乎只适用于节点......)。为了能够做到这一点,我必须首先为节点和线提供“路径名称”(请参阅交点库的文档)。使用该节点并计算与节点 2 的交点,然后我可以绘制一条线,该线很好地连接了沿平行线/箭头的节点边缘。对于许多节点,将操作包装到 foreach 循环中应该不会太难。
虽然这个方法相当简短,但可能是一种相当“冗长”的方法。任何有关改进的建议都非常感谢!