![向标准 TikZ 节点添加更多锚点](https://linux22.com/image/235929/%E5%90%91%E6%A0%87%E5%87%86%20TikZ%20%E8%8A%82%E7%82%B9%E6%B7%BB%E5%8A%A0%E6%9B%B4%E5%A4%9A%E9%94%9A%E7%82%B9.png)
我经常发现自己需要的不仅仅是 TikZ 节点提供的标准锚点,即、north
等等。我通常使用坐标计算来实现这一点,如下例所示,但对于这样一个看似简单的任务来说,这有点繁琐。north east
east
您对访问节点侧面的点有更好的想法吗?理想情况下,我想要的是像north north east
标准形状那样的锚点。
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning,calc}
\begin{document}
\begin{tikzpicture}
\tikzstyle{every node}=[draw,fill=yellow,minimum width=2cm,thin]
\tikzstyle{every path}=[-latex,ultra thick]
\node (A) {A};
\node (B) [below=2mm of A] {B};
\node (C) [below=2mm of B] {C};
\node (D) [right=of B,fill=green] {D};
% Is there an easier way to get the lines from A and C to off-centre points on the left side of D?
\draw (A.east) -- ($(D.south west)!0.75!(D.north west)$);
\draw (B.east) -- (D.west);
\draw (C.east) -- ($(D.south west)!0.25!(D.north west)$);
\end{tikzpicture}
\end{document}
答案1
我开始创建一些代码,允许您向现有节点形状添加更多锚点:
\def\pgfaddtoshape#1#2{%
\begingroup
\def\pgf@sm@shape@name{#1}%
\let\anchor\pgf@sh@anchor
#2%
\endgroup
}
上述代码是从的定义复制而来的\pgfdeclareshape
,仅包含必需的部分,而不包含形状初始化部分。
使用它,可以添加更多节点锚点。这并不容易,因为它必须使用较低级别的 PGF 宏来完成,并且需要了解和理解形状的原点。例如,对于形状,rectangle
原点位于文本基线的左角,而不是矩形的中间。
以下是一些代码,它们将指南针的更多点添加为锚点,以及一些返回节点宽度和/或高度的“伪锚点”。后者对于库let
的语法很有用calc
,例如let \y1 = (somenode.size) in node { width=\x1, height=\y1 };
\def\useanchor#1#2{\csname pgf@anchor@#1@#2\endcsname}
\def\@shiftback#1#2#3#4#5#6{%
\advance\pgf@x by -#5\relax
\advance\pgf@y by -#6\relax
}
\pgfaddtoshape{rectangle}{%
\anchor{west south west}{%
\pgf@process{\northeast}%
\pgf@ya=.5\pgf@y%
\pgf@process{\southwest}%
\pgf@y=1.5\pgf@y%
\advance\pgf@y by \pgf@ya%
\pgf@y=.5\pgf@y%
}%
\anchor{west north west}{%
\pgf@process{\northeast}%
\pgf@ya=1.5\pgf@y%
\pgf@process{\southwest}%
\pgf@y=.5\pgf@y%
\advance\pgf@y by \pgf@ya%
\pgf@y=.5\pgf@y%
}%
\anchor{east north east}{%
\pgf@process{\southwest}%
\pgf@ya=.5\pgf@y%
\pgf@process{\northeast}%
\pgf@y=1.5\pgf@y%
\advance\pgf@y by \pgf@ya%
\pgf@y=.5\pgf@y%
}%
\anchor{east south east}{%
\pgf@process{\southwest}%
\pgf@ya=1.5\pgf@y%
\pgf@process{\northeast}%
\pgf@y=.5\pgf@y%
\advance\pgf@y by \pgf@ya%
\pgf@y=.5\pgf@y%
}%
\anchor{north north west}{%
\pgf@process{\southwest}%
\pgf@xa=1.5\pgf@x%
\pgf@process{\northeast}%
\pgf@x=.5\pgf@x%
\advance\pgf@x by \pgf@xa%
\pgf@x=.5\pgf@x%
}%
\anchor{north north east}{%
\pgf@process{\southwest}%
\pgf@xa=.5\pgf@x%
\pgf@process{\northeast}%
\pgf@x=1.5\pgf@x%
\advance\pgf@x by \pgf@xa%
\pgf@x=.5\pgf@x%
}%
\anchor{south south west}{%
\pgf@process{\northeast}%
\pgf@xa=.5\pgf@x%
\pgf@process{\southwest}%
\pgf@x=1.5\pgf@x%
\advance\pgf@x by \pgf@xa%
\pgf@x=.5\pgf@x%
}%
\anchor{south south east}{%
\pgf@process{\northeast}%
\pgf@xa=1.5\pgf@x%
\pgf@process{\southwest}%
\pgf@x=.5\pgf@x%
\advance\pgf@x by \pgf@xa%
\pgf@x=.5\pgf@x%
}%
\anchor{width}{%
\useanchor{rectangle}{west}%
\pgf@xc=\pgf@x
\useanchor{rectangle}{east}%
\advance\pgf@x by -\pgf@xc
\pgf@y=\z@
\edef\pgf@temp{\csname pgf@sh@nt@\pgfreferencednodename\endcsname}%
\expandafter\@shiftback\pgf@temp
}
\anchor{height}{%
\useanchor{rectangle}{south}%
\pgf@yc=\pgf@y
\useanchor{rectangle}{north}%
\advance\pgf@y by -\pgf@yc
\pgf@x=\z@
\edef\pgf@temp{\csname pgf@sh@nt@\pgfreferencednodename\endcsname}%
\expandafter\@shiftback\pgf@temp
}
\anchor{size}{%
\useanchor{rectangle}{south west}%
\pgf@xc=\pgf@x
\pgf@yc=\pgf@y
\useanchor{rectangle}{north east}%
\advance\pgf@x by -\pgf@xc
\advance\pgf@y by -\pgf@yc
\edef\pgf@temp{\csname pgf@sh@nt@\pgfreferencednodename\endcsname}%
\expandafter\@shiftback\pgf@temp
}
}
pgf-extrect.sty
我的本地树中拥有这两个代码块,TEXMF
如果需要的话,将其作为包加载。
另一个可能的改进是定义替代锚点名称,即“nnw”而不是“north north west”。这可以使用以下宏来完成:
\newcommand{\anchorlet}[2]{%
\global\expandafter
\let\csname pgf@anchor@\pgf@sm@shape@name @#1\expandafter\endcsname
\csname pgf@anchor@\pgf@sm@shape@name @#2\endcsname
}
\newcommand{\anchoralias}[2]{%
\expandafter
\gdef\csname pgf@anchor@\pgf@sm@shape@name @#1\expandafter\endcsname
\expandafter{\csname pgf@anchor@\pgf@sm@shape@name @#2\endcsname}%
}
\pgfaddtoshape{rectangle}{%
\anchorlet{se}{south east}%
\anchorlet{sw}{south west}%
\anchorlet{ne}{north east}%
\anchorlet{nw}{north west}%
\anchorlet{wsw}{west south west}%
\anchorlet{wnw}{west north west}%
\anchorlet{ene}{east north east}%
\anchorlet{ese}{east south east}%
\anchorlet{nnw}{north north west}%
\anchorlet{nne}{north north east}%
\anchorlet{ssw}{south south west}%
\anchorlet{sse}{south south east}%
}
这里\anchorlet
将替代名称链接到原始名称的当前定义,而\anchoralias
链接到名称。如果锚点尚未定义或可能被重新定义(不太可能),则会产生差异。
编辑于 2017 年 10 月 12 日
针对新版本的 PGF 进行了更新,改变了此处使用的内部形状名称宏。
答案2
标准节点的锚点node.〈angle〉
位于angle
0(=东)和 360 之间,按逆时针测量。这些节点位于节点的边界上,位于与中心成给定角度的线上。例如,
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning}
\begin{document}
\begin{tikzpicture}
\tikzstyle{every node}=[draw,fill=yellow,minimum width=2cm,thin]
\tikzstyle{every path}=[-latex,ultra thick]
\node (A) {A};
\node (B) [below=2mm of A] {B};
\node (C) [below=2mm of B] {C};
\node (D) [right=of B,fill=green] {D};
\draw (A.east) -- (D.175);
\draw (B.east) -- (D.west);
\draw (C.east) -- (D.185);
\end{tikzpicture}
\end{document}
也许我应该提一下,TikZ 手册中有精美的图片,显示了默认情况下所有可用的锚点的位置。“形状库”章节。
答案3
定义新的节点形状并不难,并且可以添加额外的锚点。这是我用于结的节点形状;额外的锚点是标准罗盘方向的“更远”版本,我将其用于传入贝塞尔曲线的控制点。
这是代码。它可能需要大大简化(这是我尝试定义的第一个节点形状)。
% This sets a new round of anchors at a specified multiple of the current ones
\def\pgf@sh@@knotanchor#1#2{%
\anchor{#2 north west}{%
\csname pgf@anchor@knot #1@north west\endcsname%
\pgf@x=#2\pgf@x%
\pgf@y=#2\pgf@y%
}%
\anchor{#2 north east}{%
\csname pgf@anchor@knot #1@north east\endcsname%
\pgf@x=#2\pgf@x%
\pgf@y=#2\pgf@y%
}%
\anchor{#2 south west}{%
\csname pgf@anchor@knot #1@south west\endcsname%
\pgf@x=#2\pgf@x%
\pgf@y=#2\pgf@y%
}%
\anchor{#2 south east}{%
\csname pgf@anchor@knot #1@south east\endcsname%
\pgf@x=#2\pgf@x%
\pgf@y=#2\pgf@y%
}%
\anchor{#2 north}{%
\csname pgf@anchor@knot #1@north\endcsname%
\pgf@x=#2\pgf@x%
\pgf@y=#2\pgf@y%
}%
\anchor{#2 east}{%
\csname pgf@anchor@knot #1@east\endcsname%
\pgf@x=#2\pgf@x%
\pgf@y=#2\pgf@y%
}%
\anchor{#2 west}{%
\csname pgf@anchor@knot #1@west\endcsname%
\pgf@x=#2\pgf@x%
\pgf@y=#2\pgf@y%
}%
\anchor{#2 south}{%
\csname pgf@anchor@knot #1@south\endcsname%
\pgf@x=#2\pgf@x%
\pgf@y=#2\pgf@y%
}%
}
% this defines the new node shape, inheriting most from the circle shape
\pgfdeclareshape{knot crossing}
{
\inheritsavedanchors[from=circle] % this is nearly a circle
\inheritanchorborder[from=circle]
\inheritanchor[from=circle]{north}
\inheritanchor[from=circle]{north west}
\inheritanchor[from=circle]{north east}
\inheritanchor[from=circle]{center}
\inheritanchor[from=circle]{west}
\inheritanchor[from=circle]{east}
\inheritanchor[from=circle]{mid}
\inheritanchor[from=circle]{mid west}
\inheritanchor[from=circle]{mid east}
\inheritanchor[from=circle]{base}
\inheritanchor[from=circle]{base west}
\inheritanchor[from=circle]{base east}
\inheritanchor[from=circle]{south}
\inheritanchor[from=circle]{south west}
\inheritanchor[from=circle]{south east}
\inheritanchorborder[from=circle]
\pgf@sh@@knotanchor{crossing}{2}
\pgf@sh@@knotanchor{crossing}{3}
\pgf@sh@@knotanchor{crossing}{4}
\pgf@sh@@knotanchor{crossing}{8}
\pgf@sh@@knotanchor{crossing}{16}
\pgf@sh@@knotanchor{crossing}{32}
\backgroundpath{
\pgfutil@tempdima=\radius%
\pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%
\pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%
\ifdim\pgf@xb<\pgf@yb%
\advance\pgfutil@tempdima by-\pgf@yb%
\else%
\advance\pgfutil@tempdima by-\pgf@xb%
\fi%
}
}
下面是它的使用示例:
\begin{tikzpicture}[every node/.style={knot crossing,transform shape,inner sep=1.5pt},every path/.style={red,line width=2pt}]
\foreach \brk in {0,1,2} {
\begin{scope}[rotate=\brk * 120]
\node (k\brk) at (0,-1) {};
\end{scope}
}
\draw (0,0) \foreach \brk in {0,1,2} {let \n0=\brk, \n1={int(Mod(\brk+1,3))}, \n2={int(Mod(\brk+2,3))} in (k\n0) .. controls (k\n0.16 south east) and (k\n1.16 south west) .. (k\n1.center) .. controls (k\n1.4 north east) and (k\n2.4 north west) .. (k\n2)} (k2);
\end{tikzpicture}
注意控制点扩展方向的使用。结果: