调整箭头以吞噬路径

调整箭头以吞噬路径

我正在制作一个网络图,其中有形状箭头贯穿整个路径,而不仅仅是一条末端带有箭头的线。

因为这个bend选项非常容易使用,所以我选择使用箭头而不是装饰品来实现这一点。

下面的代码对我来说是可行的。我可以按原样使用它,但有几件事看起来有点不合时宜。

1) 使用全局长度来存储原始路径长度(通过使用装饰preaction)似乎是一种 hack,尽管有些聪明。有没有更好(更干净、更少 hack)的方法来获取原始路径长度?

2) 箭头的边界框(由箭头定义中的凸包定义)不适合弯曲的箭头。有什么办法可以解决这个问题吗?我尝试使用\pgfusepath{use as bounding box},但绘图代码中没有提供(如手册中所述)。

image of arrow on curve

\documentclass[border=10pt,multi,tikz]{standalone}

\usetikzlibrary{arrows.meta}
\usetikzlibrary{bending}
\usetikzlibrary{decorations}

\newlength{\mypathlength}

\pgfdeclaredecoration{stashlength}{final}{
  \state{final}{
     \setlength{\mypathlength}{\pgfdecoratedpathlength}
     \global\mypathlength=\mypathlength}}


\pgfdeclarearrow{
  name = foo,
  parameters = {%
   \the\pgfarrowlength 
  },
  setup code = {%
    % The different end values:
    \pgfarrowssettipend{.25\pgfarrowlength}
    \pgfarrowssetlineend{-.25\pgfarrowlength}
    \pgfarrowssetvisualbackend{-.5\pgfarrowlength}
    \pgfarrowssetbackend{-.75\pgfarrowlength}
    % The hull
    \pgfarrowshullpoint{.25\pgfarrowlength}{0pt}%
    \pgfarrowsupperhullpoint{-.75\pgfarrowlength}{.5\pgfarrowlength}%
    \pgfarrowsupperhullpoint{-\mypathlength}{-.5\pgfarrowlength}% doesn't create the right bounding box
    % Saves: Only the length:
    \pgfarrowssavethe\pgfarrowlength
  },
  drawing code = {%
    \pgfpathmoveto{\pgfqpoint{.25\pgfarrowlength}{0pt}}
    \pgfpathlineto{\pgfpoint{-\mypathlength-.25\pgfarrowlength}{.5\pgfarrowlength}}
    \pgfpathlineto{\pgfpoint{-\mypathlength}{0pt}}
    \pgfpathlineto{\pgfpoint{-\mypathlength-.25\pgfarrowlength}{-.5\pgfarrowlength}}
    \pgfpathclose
    %\pgfusepath{use as bounding box}  %won't work as described in the manual (page 1019) -- would try to add arrows recursively
    \pgfusepathqstroke
  },
  defaults = {%
   length = 4cm 
  },
}

\begin{document} 
\begin{tikzpicture}
  %comment the next line out to see bounding box problems
  \node at (0,2) {Used to save space for arrow};
  \draw [-{foo[bend]},preaction={decorate,decoration=stashlength}] (0,0) to [bend right] (8,0);

  %\draw[-{foo[length=15pt]},preaction={decorate,decoration=stashlength}] (0,0) -- (8,0);

\end{tikzpicture}
\end{document}

答案1

编辑我意识到设置代码仍然与绘图代码不匹配。我试图猜测你想要什么并同步它们,但关键是设置中为船体指定的点与绘图时实际用于构造尖端的点相匹配。

您可以通过正确设置船体点来改善结果。在当前代码中,它们与您绘制的内容不对应。因此,它们似乎不会影响保存的边界框。

特别是,我相信你需要

\pgfarrowsupperhullpoint{-\mypathlength\advance\pgf@x by-.75\pgfarrowlength}{.5\pgfarrowlength}% it saves the bounding box correctly if it corresponds to the points actually used in the drawing code

因为这看起来就是您真正想要绘制的。这解决了边界框问题,该问题与您的使用无关,并且会影响在直线路径上bend的使用。foo

without fix

请注意,箭头的尾部未被捕获在表示边界框的虚线矩形内。

随着变化,完整的箭头包含在虚线边界框中。

with fix

但是,这并不能完全解决问题,因为它不考虑箭头轨迹上的x和的最大值和最小值,而只考虑它们在指定点的值。当箭头没有弯曲时,这种方法是可行的,因为线是直的,但在使用时,这种方法会失效。这包括您示例中涉及箭头的情况。箭头的尾部现在完全被边界框包围,正如预期的那样。但箭头的腹部却没有。ybend

failure even with fixed code

然而,这种情况也会发生在常见的箭头上:

failure with standard tip

请注意,您实际上不需要新的长度。\pgfarrowlength应该是箭头的长度,\pgfarrowwidth可以用于宽度。

部分改进的完整代码:

\documentclass[border=20pt,tikz]{standalone}
\usetikzlibrary{arrows.meta,bending,decorations}

\pgfdeclaredecoration{stashlength}{final}{
  \state{final}{
    \setlength{\pgfarrowlength}{\pgfdecoratedpathlength}%
    \global\pgfarrowlength=\pgfarrowlength}}

\makeatletter
\pgfdeclarearrow{
  name = foo,
  parameters = {%
   \the\pgfarrowlength,
   \the\pgfarrowwidth,
  },
  setup code = {%
    % The different end values:
    \pgfarrowssettipend{.25\pgfarrowwidth}
    \pgfarrowssetlineend{-.25\pgfarrowwidth}
    \pgfarrowssetvisualbackend{-\pgfarrowlength\advance\pgf@x by-.5\pgfarrowwidth}
    \pgfarrowssetbackend{-\pgfarrowlength\advance\pgf@x by-.75\pgfarrowwidth}
    % The hull
    \pgfarrowshullpoint{-\pgfarrowlength\advance\pgf@x by-.5\pgfarrowwidth}{0pt}%
    \pgfarrowsupperhullpoint{-\pgfarrowlength\advance\pgf@x by-.75\pgfarrowwidth}{.5\pgfarrowwidth}% it saves the bounding box correctly if it corresponds to the points actually used in the drawing code
    % Saves: Only the length:
    \pgfarrowssavethe\pgfarrowwidth
    \pgfarrowssavethe\pgfarrowlength
  },
  drawing code = {%
    \pgfpathmoveto{\pgfqpoint{.25\pgfarrowwidth}{0pt}}
    \pgfpathlineto{\pgfpoint{-\pgfarrowlength-.75\pgfarrowwidth}{.5\pgfarrowwidth}}
    \pgfpathlineto{\pgfpoint{-\pgfarrowlength-.5\pgfarrowwidth}{0pt}}
    \pgfpathlineto{\pgfpoint{-\pgfarrowlength-.75\pgfarrowwidth}{-.5\pgfarrowwidth}}
    \pgfpathclose
    \pgfusepathqstroke
  },
  defaults = {%
   width = 4cm
  },
}
\makeatother
\newcommand*\drawbb{\draw [densely dashed, gray] (current bounding box.south east) rectangle (current bounding box.north west);}
\begin{document}
\begin{tikzpicture}
  %comment the next line out to see bounding box problems
%   \node at (0,2) {Used to save space for arrow};
  \draw [-{foo[bend]},preaction={decorate,decoration=stashlength}] (0,0) to [bend right] (8,0);
  %\draw[-{foo[length=15pt]},preaction={decorate,decoration=stashlength}] (0,0) -- (8,0);
  \drawbb
\end{tikzpicture}
\begin{tikzpicture}
  \draw[-{foo[width=15pt]},preaction={decorate,decoration=stashlength}, postaction={draw=red, -}] (0,0) -- (1,0);
  \drawbb
  \draw [help lines] (-1,-1) grid (6,1);
\end{tikzpicture}
\begin{tikzpicture}
  %comment the next line out to see bounding box problems
%   \node at (0,2) {Used to save space for arrow};
  \draw [-{foo[bend]},preaction={decorate,decoration=stashlength}] (0,0) to [bend right] (20,0);
  %\draw[-{foo[length=15pt]},preaction={decorate,decoration=stashlength}] (0,0) -- (8,0);
  \drawbb
\end{tikzpicture}
\begin{tikzpicture}
  \draw [-{Stealth[bend, length=10cm]}] (0,0) to [bend right] (8,0);
  \drawbb
\end{tikzpicture}
\end{document}

相关内容