使用 tikz 和 foreach 循环绘制一列箭头

使用 tikz 和 foreach 循环绘制一列箭头

我正在尝试使用 tikz 绘制一系列随机箭头,每个箭头都从前一个箭头结束的地方开始。我使用极坐标和remember。从逻辑角度来看,我觉得以下代码应该有效。但是,前一个箭头的端点不知何故与后面箭头的起点不匹配……

代码:

\begin{figure}[h]
\centering
\begin{tikzpicture}
\draw[-latex] (-3,0) -- (3,0) node[right]{$\ts{Re}(r)$};
\draw[-latex](0,-3) -- (0,3) node[above] {$\ts{Im}(r)$};

\def\r{0}
\def\t{0}

\foreach \z [remember=\r as \rlast (initially 0),remember=\t as \tlast (initially 0)] in {1,2,3,4,5,6}
  {
    \pgfmathsetmacro{\r}{random(0,360)};
\pgfmathsetmacro{\t}{0.01*random(0,100)};
    \draw[->] (\rlast:\tlast) -- ++ (\r:\t);
  }
\end{tikzpicture}
\end{figure}

在此处输入图片描述

答案1

由于命名坐标是全局存储的:

\documentclass{standalone}
\usepackage{tikz}
\begin{document}

\begin{tikzpicture}
\draw[-latex] (-3,0) -- (3,0) node[right]{$\textrm{Re}(r)$};
\draw[-latex](0,-3) -- (0,3) node[above] {$\textrm{Im}(r)$};

\coordinate (last) at (0,0);

\foreach \z in {1,2,3,4,5,6}
  {
    \pgfmathsetmacro{\r}{random(0,360)};
    \pgfmathsetmacro{\t}{0.01*random(0,100)};
    \draw[->] (last) -- ++ (\r:\t) coordinate (last);
  }
\end{tikzpicture}

\end{document}

答案2

chains图书馆和图书馆都graphs可以帮助您减少手动操作。

这些键将根据前一个坐标(红色)放置下一个坐标,同时将绝对放置坐标(蓝色)。random polar shiftrandom polar placement

代码(链)

\documentclass[tikz]{standalone}
\usetikzlibrary{arrows.meta, chains}
\tikzset{
  random polar shift/.style={    at=(\tikzchainprevious),
                              shift={({random(0,360)}:{random(0,#1)})}},
  random polar placement/.style={at={({random(0,360)}:{random(0,#1)})}}}
\begin{document}
\pgfmathsetseed{20230330}
\begin{tikzpicture}[
  start chain=ps going {random polar shift     = 100},
  start chain=pp going {random polar placement = 300},
  every join/.append style={->, shorten >=2\pgflinewidth}]
\draw[-Latex] (-3,0) -- (3,0) node[right]{$\textrm{Re}(r)$};
\draw[-Latex] (0,-3) -- (0,3) node[above]{$\textrm{Im}(r)$};
\path[x=+.1mm, y=+.1mm] foreach \z in {1,...,6} {
   coordinate[on chain=ps, join=by red]
   coordinate[on chain=pp, join=by blue]};
\end{tikzpicture}
\end{document}

代码(图表)

\documentclass[tikz]{standalone}
\usetikzlibrary{arrows.meta, graphs.standard}
\tikzgraphsset{
  random polar shift/.style={group shift={({random(0,360)}:{random(0,#1)})}},
  random polar shift/.default=100,
  random polar placement/.style={nodes={at={({random(0,360)}:{random(0,#1)})}}},
  random polar placement/.default=100}
\begin{document}
\pgfmathsetseed{30032023}
\begin{tikzpicture}
\draw[-Latex] (-3,0) -- (3,0) node[right]{$\textrm{Re}(r)$};
\draw[-Latex] (0,-3) -- (0,3) node[above]{$\textrm{Im}(r)$};
\path[x=+.1mm, y=+.1mm, shorten >=2\pgflinewidth,
  graphs/every graph/.append style={path, ->, nodes=coordinate}]
  graph[edges=red,  random polar shift        ] {              subgraph I_n [n=6] }
  graph[edges=blue, random polar placement=300] { 0[x=0, y=0], subgraph I_n [n=5] };
\end{tikzpicture}
\end{document}

输出(链/图)

在此处输入图片描述 在此处输入图片描述

答案3

使用++(x,y)(或+(x,y)) 可以定义相对于前一个坐标的坐标(这意味着\draw (1,1) -- ++(1,1);此路径的第二个坐标本质上是({1+1},{1+1})(2,2))。在您的设置中,这不会产生预期的结果,因为您将 存储\r\rlast\t,但\tlast您绘制到({\r+\rlast},{\t+\tlast})。因此,路径的第二个坐标永远不会与下一个路径的第一个坐标相同。删除,您就会没事了(我稍微++放大了值):\t

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

\begin{document}
\begin{tikzpicture}
\draw[-latex] (-3,0) -- (3,0) node[right] {$\textrm{Re}(r)$};
\draw[-latex] (0,-3) -- (0,3) node[above] {$\textrm{Im}(r)$};

\foreach \z [
    remember=\r as \rlast (initially 0), 
    remember=\t as \tlast (initially 0)
] in {1,2,3,4,5,6} {
    \pgfmathsetmacro{\r}{random(0,360)};
    \pgfmathsetmacro{\t}{0.03*random(0,100)};
    \draw[->] (\rlast:\tlast) -- (\r:\t);
}
\end{tikzpicture}
\end{document}

你不需要事先定义\t\r,因为 TiZ 将会动态地定义它们。

在此处输入图片描述


如果您需要每个路径的第二个坐标相对于第一个坐标,则需要以这样的方式进行计算,即\r\t获取计算出的相对值,以便将这些值存储在和中\rlast\tlast如果您直接在坐标上进行计算(例如使用\draw[->] (\rlast:\tlast) -- ({\r+\rlast}:{\t+\tlast});),那么您将再次遇到与原始问题相同的问题。所以,我认为这也应该适合您:

\begin{document}
\begin{tikzpicture}
\draw[-latex] (-3,0) -- (3,0) node[right] {$\textrm{Re}(r)$};
\draw[-latex] (0,-3) -- (0,3) node[above] {$\textrm{Im}(r)$};

\foreach \z [
    remember=\r as \rlast (initially 0), 
    remember=\t as \tlast (initially 0)
] in {1,2,3,4,5,6} {
    \pgfmathsetmacro{\r}{random(0,360)+\rlast};
    \pgfmathsetmacro{\t}{0.01*random(0,100)+\tlast};
    \draw[->] (\rlast:\tlast) -- (\r:\t);
}
\end{tikzpicture}
\end{document}

答案4

为了比较,下面是元帖子

在此处输入图片描述

这已被包裹起来,luamplib因此您需要用 来编译它lualatex

\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\mplibtextextlabel{enable}
\begin{mplibcode}
% a function to turn a <path> into a <picture> of the path draw with arrows
vardef with_arrows expr p = image(
    interim ahangle := 20;
    for i=1 upto length p:
        drawarrow subpath(i-1, i) of p 
            cutafter fullcircle scaled 3 shifted point i of p; % shorten the arrow slightly
    endfor) 
enddef;
beginfig(1);
    % axes
    path xx, yy;
    xx = (left--right) scaled 150;
    yy = xx rotated 90;
    
    % paths for the chains of arrows
    path chain[];  
    chain1 = origin for i=1 upto 12: .. uniformdeviate 144 * dir uniformdeviate 360 endfor;
    chain2 = origin for i=1 upto 12: -- uniformdeviate 144 * dir uniformdeviate 360 endfor;
    % note the different connectors -- for straight, .. for free

    % two pictures for comparison
    picture P[];
    P1 = image(
        drawarrow xx;
        drawarrow yy;
        draw with_arrows chain1 withcolor 1/2 blue;
    );
    P2 = image(
        drawarrow xx;
        drawarrow yy;
        draw with_arrows chain2 withcolor 1/2 red;
    );

    % show the pictures side-by-side
    draw P1; draw P2 shifted 320 right;

endfig;
\end{mplibcode}
\end{document}

笔记

  • 为了得到MP 中的点,您可以r * dir thetatheta度数来写。

  • 要获取半径为 144 pt 的圆中任意位置的随机点,可以这样写uniformdeviate 144 * dir uniformdeviate 360

  • uniformdeviate接受单个无分隔符的数字参数并返回 0 到给定数字之间的一个随机实数。

  • 您可以跟踪当前点并在生成它们时绘制指向下一个点的箭头,但我认为在变量中捕获所有点会更简洁<path>

  • 然后宏with_arrows获取该数据<path>并返回<picture>用箭头(细箭头和小间隙)绘制的路径。

  • 我认为我更喜欢带有弯曲路径的版本。

相关内容