向标准 TikZ 节点添加更多锚点

向标准 TikZ 节点添加更多锚点

我经常发现自己需要的不仅仅是 TikZ 节点提供的标准锚点,即、north等等。我通常使用坐标计算来实现这一点,如下例所示,但对于这样一个看似简单的任务来说,这有点繁琐。north easteast

您对访问节点侧面的点有更好的想法吗?理想情况下,我想要的是像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〉位于angle0(=东)和 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}

注意控制点扩展方向的使用。结果:

三叶草

相关内容