帮助使用 foreach 重现 tikzpicture 图像

帮助使用 foreach 重现 tikzpicture 图像

我仍在开始使用 tikzpicture。以下是我希望通过自动生成的代码获得的结果

wannabe

这就是我要做的事情。我甚至无法超越这一点:

result

这是我的 MWE。

\documentclass[standalone]
\usepackage{standalone}
\usepackage{pgf}
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{positioning}

\newcommand\vectorcell[5]   {\draw[fill=#3] (#5) rectangle +(#1,#2);
                       \node[font=\sffamily\bfseries] at ($(#5)+({0.5*#1},{0.5*#2})$) {#4};
                       \node[font=\sffamily] at ($(#5)+({0.5*#1+5},{0.5*#2})$) {\texttt{threadIdx.x = }#4};
                      }
\newcommand\cell[1]       {\vectorcell{1}{1}{gray!30}{#1}}

\begin{document}

\begin{figure}
\begin{tikzpicture}[scale=0.5]%
\foreach \j in {0,1,2}%souldn't I remove the following semicolon?
    \node[font=\sffamily] at (12,4.1*\j+1.0) {\texttt{blockIdx.x = }\j};
    \foreach \i in {0,...,3}%
        \cell{\i}{0,1.1*\i+0.5};
\end{tikzpicture}
\end{figure}

\end{document}

我的问题是我无法让第二个foreach发挥作用:我猜是上面的分号。如果我删除它,代码就无法编译,而如果我保留它,我只会得到一列线程,所以肯定不行。

我有两个问题:

  • 我怎样才能让 tikz 创建one columnblockIdx.x threadIdx.x perindex
  • 我怎样才能rotate then the阻止Idx.x label

另外,花括号怎么办?我相信有了上面的答案,我可以自己做。

答案1

\foreach可以用作\foreach \i in {0,1,2} { \foo{\i} },其中最后一个括号给出了每次迭代重复的内容的范围\i。使用这个我们可以重写

\foreach \j in {0,1,2}%souldn't I remove the following semicolon?
    \node[font=\sffamily] at (12,4.1*\j+1.0) {\texttt{blockIdx.x = }\j};
    \foreach \i in {0,...,3}%
        \cell{\i}{0,1.1*\i+0.5};
\end{tikzpicture}

作为

\foreach \j in {0,1,2} {
    \node[font=\sffamily] at (12,4.1*\j+1.0) {\texttt{blockIdx.x = }\j};
    \foreach \i in {0,...,3} {
        \cell{\i}{0,1.1*\i+0.5};
    }
}

现在第二个循环针对的每个值运行\j,尽管现在\cell{\i}{0,1.1*\i+0.5};只会在同一个位置打印相同的内容三次,但我们可以告诉它根据\j使用的值来定位单元格\cell{\i}{0,4.1*\j+1.1*\i+0.5};

此时我们有

enter image description here

所以我们可以看到\foreach代码的各个部分都按预期运行(尽管间距需要一点调整)。

使用杰克的回答在 TikZ 中绘制花括号然后我们可以添加括号并将rotate=-90选项传递给节点以接近预期的图表。

enter image description here

(做这部分时我误读了图表,因此下面添加了更正)

最后,虽然“线程”标签仅跟在\i索引后面。为了进行必要的数学运算,我们需要在定义中用进行更改\vectorcell替换,这将给出第一个参数的整数值,因此我们现在可以在那里进行数学运算。最后,为了在向下移动时获得升序数字,最简单的方法(可以重新编写一些代码...)是将其作为选项传递给环境(这需要删除传递给括号的选项)。#4\pgfmathparse{int(#4)}\pgfmathresult\cell[yshift=-1]tikzpicturemirror

因此我们有

enter image description here

产自

\documentclass[tikz]{standalone}
\usetikzlibrary{calc,decorations.pathreplacing,positioning}

\newcommand\vectorcell[5]{\draw[fill=#3] (#5) rectangle +(#1,#2);
                       \node[font=\sffamily\bfseries] at ($(#5)+({0.5*#1},{0.5*#2})$) {\pgfmathparse{int(#4)}\pgfmathresult};
                       \node[font=\sffamily] at ($(#5)+({0.5*#1+5},{0.5*#2})$) {\texttt{threadIdx.x = }\pgfmathparse{int(#4)}\pgfmathresult};
                      }
\newcommand\cell[2]{\vectorcell{1}{1}{gray!30}{#1}{#2}}

\begin{document}
\begin{tikzpicture}[scale=0.5,yscale=-1]
\foreach \j in {0,1,2}{
    \draw [decorate,decoration={brace,amplitude=10pt,raise=4pt},yshift=0pt] (8.5,6*\j+0.5) -- (8.5,6*\j+4.8) node [black,midway,xshift=0.8cm,rotate=-90] {\texttt{blockIdx.x = }\j};
    \foreach \i in {0,...,3}{
        \cell{\i+4*\j}{0,6*\j+1.1*\i+0.5};
    }
}
\end{tikzpicture}
\end{document}

(另外补充一下:我不明白为什么你\newcommand\cell[1]{\vectorcell{1}{1}{gray!30}{#1}}需要\vectorcell五个强制参数,\newcommand\cell[2]{\vectorcell{1}{1}{gray!30}{#1}{#2}}这对我来说更有意义,所以我改成了上面的那个。)


好的,因此标记灰色块应该跟在后面,4*\j+\i而线程编号应该只跟在后面\i。鉴于\cell宏只调用一次,目前最简单的方法可能是“解包”它并将其全部直接放在宏中,foreach如下所示

\documentclass[tikz]{standalone}
\usetikzlibrary{calc,decorations.pathreplacing,positioning}

\begin{document}
\begin{tikzpicture}[scale=0.5,yscale=-1]
\foreach \j in {0,1,2}{
    \draw [decorate,decoration={brace,amplitude=10pt,raise=4pt},yshift=0pt] (8.5,6*\j+0.5) -- (8.5,6*\j+4.8) node [black,midway,xshift=0.8cm,rotate=-90] {\texttt{blockIdx.x = }\j};
    \foreach \i in {0,...,3}{
        \draw[fill=gray!30] (0,6*\j+1.1*\i+0.5) rectangle +(1,1);
        \node[font=\sffamily\bfseries] at ($(0,6*\j+1.1*\i+0.5)+(0.5,0.5)$) {\pgfmathparse{int(4*\j+\i)}\pgfmathresult};
        \node[font=\sffamily] at ($(0,6*\j+1.1*\i+0.5)+(5.5,0.5)$) {\texttt{threadIdx.x = }\pgfmathparse{int(\i)}\pgfmathresult};
    }
}
\end{tikzpicture}
\end{document}

enter image description here

其中\i单独用于标记线程并且4*\j+\i用于块。

甚至可以通过使用以下方法避免计算框内数字的放置位置

\draw[fill=gray!30] (0,6*\j+1.1*\i+0.5) rectangle +(1,1)
    node [midway, font=\sffamily\bfseries] {\pgfmathparse{int(4*\j+\i)}\pgfmathresult};

将节点自动置于刚刚绘制的矩形内的中心。

也许甚至可以给该节点本身贴上标签,以便该threadIdx.x =节点相对于该节点放置,即

\draw[fill=gray!30] (0,6*\j+1.1*\i+0.5) rectangle +(1,1)
    node (boxnumber-\i-\j) [midway, font=\sffamily\bfseries] {\pgfmathparse{int(4*\j+\i)}\pgfmathresult};
\node[font=\sffamily] at ($(boxnumber-\i-\j)+(5,0)$) {\texttt{threadIdx.x = }\pgfmathparse{int(\i)}\pgfmathresult};

这些对于它的工作来说并不是必需的,甚至看起来也不好看。

但是,我现在已经删除了\newcommand你之前所用的 s,也许将绘图步骤包装在宏中才是你想要的。我认为使用两个宏\cell命令来传递一个坐标和一个计数器值是行不通的,两者都需要单独输入(或至少两个线性独立的组合)。因此,作为基础,我们需要

\newcommand{\thread}[3]{
    \draw[fill=gray!30] (#3) rectangle +(1,1)
        node (boxnumber-#1) [midway, font=\sffamily\bfseries] {\pgfmathparse{int(#1)}\pgfmathresult};
    \node[font=\sffamily] at ($(boxnumber-#1)+(5,0)$) {\texttt{threadIdx.x = }\pgfmathparse{int(#2)}\pgfmathresult};
}

如果需要更改框的大小或颜色,可以将其作为附加参数(可能是可选的,甚至是键值)来完成。我建议将框大小添加为单个参数,该参数需要两个用逗号分隔的值 - 即坐标。

\newcommand{\thread}[5][gray!30]{
    \draw[fill=#1] (#5) rectangle +(#2)
        node (boxnumber-#3) [midway, font=\sffamily\bfseries] {\pgfmathparse{int(#3)}\pgfmathresult};
    \node[font=\sffamily] at ($(boxnumber-#3)+(5,0)$) {\texttt{threadIdx.x = }\pgfmathparse{int(#4)}\pgfmathresult};
}

也许将blockIdx标签和内部分组foreach为单个命令可能会很有用,考虑到没有给出用例,我将停止建议具有越来越脆弱实用性的东西。

答案2

我不太确定你要去哪里。到目前为止,你的应用程序看起来不像是 的典型用例tikz,除了灰色框。以下是实现你所展示内容的另一种方法,可以给你一些想法。

enter image description here

\documentclass{article}
\usepackage{tikz}
\newcommand\boxed[1]% enclose #1 in a grey box
   {\tikz[baseline=(X.base)]
      \node[draw,fill=gray!30,inner sep=1pt,minimum width=1.4em,minimum height=2ex]
        (X){\scriptsize\bfseries\sffamily#1};%
   }
\newcommand\threadIdx[2]% #1 = index of block, #2 = offset
   {\pgfmathparse{#1*4+#2}%
    \let\tmp\pgfmathresult
    \boxed{\pgfmathprintnumber[assume math mode=true]{\tmp}}\quad
    \texttt{threadIdx.x = }{\textsf{#2}}%
   }
\newcommand\threadIdxs[1]% #1 = index of block
   {$\left.
    \renewcommand\arraystretch{1.2}
    \begin{tabular}{l}
    \threadIdx{#1}{0}\\
    \threadIdx{#1}{1}\\
    \threadIdx{#1}{2}\\
    \threadIdx{#1}{3}
    \end{tabular}
    \right\}
    \rotatebox[origin=c]{270}{\scriptsize\texttt{blockIdx.x = }{\textsf{#1}}}
    $%
   }
\begin{document}
\foreach \i in {0,1,2} {\threadIdxs{\i}\medskip\par}
\end{document}

答案3

  • 两个foreach循环:外层 for区块标识符团体,内部线程标识元素
  • 编号从 0 到 11(如果需要,可以更多)的块定义为节点
  • 用于放置节点的chains包,因此不需要计算节点的位置
  • 对于大括号,为了使它的外观更加美观,使用了库calligraphy

梅威瑟:

\documentclass[tikz,margin=3mm]{standalone}
\usetikzlibrary{calc, chains, decorations.pathreplacing, calligraphy, positioning}

\begin{document}
    \begin{tikzpicture}[
node distance = 1mm and 11mm,
  start chain = going below,
  lbox/.style = {font=\normalsize\ttfamily, sloped},%labels style
   box/.style = {draw, fill=gray!30, minimum size=7mm, font=\large\sffamily, 
                 label={[lbox,xshift=7mm]right:threadIdx.x = \j},
                 on chain}
                    ]
\foreach \i [count=\ii from 0] in {0,4,8}% <-- outer loop, determine blockIdx
{
    \foreach \j [count=\k from \i] in {0,1,2,3}% <-- inner loop, determine threadIdx
{
\ifnum\j=0 \node (n\j) [box,yshift=-3mm] {\k}% <-- for separating blockIdx
    \else
           \node (n\j) [box] {\k}
\fi;
}
\draw[decorate, decoration={calligraphic brace, amplitude=6pt,
                pre=moveto, pre length=1pt, post=moveto, post length=1pt,
                raise=42mm},
                draw, thick, pen colour={red}]
      (n0.north) -- node[lbox,above=44mm] {blockIdx.x = \ii} (n3.south);
}
\end{tikzpicture}
\end{document}

enter image description here

相关内容