TikZ:根据样式和格式节点标签定义箭头起始位置

TikZ:根据样式和格式节点标签定义箭头起始位置

我使用下面的代码,正如我曾经展示的那样这里,在图片上添加描述性标签:

\documentclass[margin=1cm]{standalone}
\usepackage{tikz}

\begin{document}
\begin{tikzpicture}
  \node[anchor=south west,inner sep=0] (image) at (0,0) {\includegraphics[width=5cm]{airplane.png}};
  \begin{scope}[x={(image.south east)},y={(image.north west)}]
  % \draw[help lines, very thin, step=0.02] (0,0) grid (1,1);
  % \draw[help lines,thin,xstep=.1,ystep=.1] (0,0) grid (1,1);
  % \foreach \x in {0,1,...,9} { \node [anchor=north] at (\x/10,0) {0.\x}; }
  % \foreach \y in {0,1,...,9} { \node [anchor=east] at (0,\y/10) {0.\y}; }

  \draw[<-] (0.5,0.6) -- (0.7,1.1) node[above] {label above};
  \draw[<-] (0.3,0.5) -- (0.4,-0.1) node[below] {label below};
  \draw[<-] (0.7,0.6) -- (1.1,0.7) node[right] {label right};
  \draw[<-] (0.1,0.7) -- (-0.1,0.8) node[left] {label left};

  \end{scope}
\end{tikzpicture}
\end{document}

结果:

在此处输入图片描述

(我从这里

如你所见,如果箭头结束于图像上方,我希望标签的定位是标签文本位于箭头上方;如果箭头结束于图像左侧,我希望标签文本位于箭头左侧,依此类推。指定结束坐标 x=-0.1(位于图像左侧)以及标签位置有点多余,因此node[left].我想知道是否有可能自动执行此操作,只需指定起始坐标和结束坐标之一,以及方向(左、右、上、下),例如如下所示

\draw[<-] (0.1,0.7) node[left, y=0.8] {label left};

当然,这并不像我在这里写的那样有效,这只是一个例子。这应该将标签放在箭头的左侧,其端点位于 x=-0.1 和 y=0.8。

注意:我在 MWE 中注释掉的部分只是为了显示一些坐标网格来找到箭头的起始位置。

此外,标签还有另一个问题。请看下面的图片

在此处输入图片描述

其中一个标签中有字母 p(比基线稍微靠下一点),而另一个节点没有。结果是标签不在同一高度,这很丑陋。可以strut{}在标签文本中使用 a 来修复此问题,例如

\draw[<-] (0.4,0.6) -- (0.7,1.1) node[above] {\strut{} aaa};
\draw[<-] (0.3,0.6) -- (0.4,1.1) node[above] {\strut{} paaaaa};

结果如下:

在此处输入图片描述

如何自动将支柱添加到标签文本中?或者应该移动 TikZ 标签的基线?

答案1

这不是真正的答案,只是为了展示可以轻松完成的事情。您可以定义一种样式,将图片框架中的点与外部点和标签连接起来,其中计算外部点和标签位置。样式的工作方式如下:

\draw[<-,lazy label={(0.3,0.4) with auto}];

其中您只需指定点和标签。

\documentclass[margin=1cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}[declare function={offset(\x)=sign(\x)*80pt/(abs(\x)+4pt);},
lazy label/.style args={#1 with #2}{
insert path={
let \p1=($#1-(0.5,0.5)$),\p2=#1,\n1={atan2(\y1,\x1)} in #1 
\pgfextra{\pgfmathtruncatemacro{\ntest}{mod(8+\n1/90,4)}\xdef\ntest{\ntest}
%\typeout{#1:#2,\x1,\y1,\x2,\y2,\n1,\ntest}
}
\ifcase\ntest%
#1 -- (1.1,{\y2+offset(\y1)}) node[right] {#2}
\or
#1 -- ({\x2+offset(\x1)},1.1) node[above] {#2}
\or
#1 -- (-0.1,{\y2+offset(\y1)}) node[left] {#2}
\or
#1 -- ({\x2+offset(\x1)},-0.1) node[below] {#2}
\fi
}}]
  \node[anchor=south west,inner sep=0] (image) at (0,0) 
  {\includegraphics[width=5cm]{example-image-duck}};
  \begin{scope}[x={(image.south east)},y={(image.north west)}]
  \draw[<-,lazy label={(0.3,0.4) with auto}];
  \end{scope}
\end{tikzpicture}
\end{document}

在此处输入图片描述

我还想提一下,如果你在循环中使用此样式,则需要注意扩展问题。然而,这些问题并不特定于样式,也出现在其他上下文中。

\documentclass[margin=1cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}[declare function={offset(\x)=sign(\x)*80pt/(abs(\x)+4pt);},
lazy label/.style args={#1 with #2}{
insert path={
let \p1=($#1-(0.5,0.5)$),\p2=#1,\n1={atan2(\y1,\x1)} in #1 
\pgfextra{\pgfmathtruncatemacro{\ntest}{mod(8+\n1/90,4)}\xdef\ntest{\ntest}
%\typeout{#1:#2,\x1,\y1,\x2,\y2,\n1,\ntest}
}
\ifcase\ntest%
#1 -- (1.1,{\y2+offset(\y1)}) node[right] {#2}
\or
#1 -- ({\x2+offset(\x1)},1.1) node[above] {#2}
\or
#1 -- (-0.1,{\y2+offset(\y1)}) node[left] {#2}
\or
#1 -- ({\x2+offset(\x1)},-0.1) node[below] {#2}
\fi
}}]
  \node[anchor=south west,inner sep=0] (image) at (0,0) 
  {\includegraphics[width=5cm]{example-image-duck}};
  \begin{scope}[x={(image.south east)},y={(image.north west)}]

  \foreach \X in {(0.5,0.6),(0.3,0.4),(0.7,0.6),(0.1,0.7)}
  {
  \edef\temp{\noexpand\draw[<-,lazy label={{\X} with auto}];}
  \temp
  } 
  \end{scope}
\end{tikzpicture}
\end{document}

在此处输入图片描述

到目前为止,一切都很好。但是这种样式不会检查新标签是否与已有标签发生冲突,也不会猜测您的想法,也就是说,在此版本中,您的标签below不会按照您建议的方式完成,因为该点到上边界的距离较短。

答案2

我设法找到了一个可接受的解决方案,借助marmot 的回答。我使用了insert pathTikZ 的样式,如下所示:

\documentclass[border=1cm]{standalone}
\usepackage{tikz}

\tikzset{
  lazy label below/.style args = {#1 at #2}{
    red,
    ->,
    insert path = {
      (#2,-0.1) node[below] {#1 \strut{}}
    }
  },
  lazy label above/.style args = {#1 at #2}{
    red,
    ->,
    insert path = {
      (#2,1.1) node[above] {#1 \strut{}}
    }
  },
}
\begin{document}
\begin{tikzpicture}
  \node[anchor=south west,inner sep=0] (image) at (0,0) {\includegraphics[width=5cm]{example-image}};
  \begin{scope}[x={(image.south east)},y={(image.north west)}]
    \draw[lazy label below={label text at 0.3}] to (0.2,0.2);
    \draw[lazy label above={other text at 0.6}] to (0.4,0.8);
  \end{scope}
\end{tikzpicture}
\end{document}

结果:

在此处输入图片描述

它允许我根据需要将标签放置在图像上方或下方,并相应地设置箭头起点的坐标。此外,它还会将添加到\strut{}标签文本中,以确保所有标签的高度相同。我将在我的tikz-imagelabels包中使用它。

相关内容