如何在 tikzpicture 中使用动态数值变量?

如何在 tikzpicture 中使用动态数值变量?

我目前正在创建技术文档,通过 tikzpicture 描述跨 I2C 总线的数据流(类似示例的图形)我最初是手动执行此操作的,结果如下: 在此处输入图片描述

如图所示,我的描述使用围绕每个文本元素绘制的线交叉图案。我最终停止手动执行此操作(即此图不完整,如图所示),希望我可以创建一个宏函数来为具有一致间距的任意文本元素列表执行此操作。

我想到了以下(不完整的)示例:

\documentclass{article}
\usepackage[utf8x]{inputenc}
\usepackage{tikz}
\usepackage{graphicx,pgf}
\usepackage{listofitems}

\begin{document}

%   @brief          Creates a test graphic.
%   @param[in] #1   The x-coordinate of the graphic.
%   @param[in] #2   The y-coordinate of the graphic.
%   @param[in] #3   A comma separated list of strings.
\newcommand{\CreateTestGraphic}[3]
{%
    \begin{tikzpicture}
    
        \readlist*\mylist{#3}                   % Read the comma separated input array.
        
        \newcommand{\CrossHorDistance}{15pt}    % The horizontal distance of the cross.
        \newcommand{\SeparationDistance}{15pt}  % The horizontal distance to separate each text element by.
          
        \edef\NextTextCenterX{#1}               % Keeps track of the text's center x-coordinate.
        \edef\NextTextCenterY{#2}               % Keeps track of the text's center y-coordinate.
        
        \foreachitem \x \in \mylist
        {%
            \settowidth{\strwidth}{\x}      % The width of the string at the current font and size in
                                            % point (pt) units.
            \settoheight{\strheight}{\x}    % The height of the string at the current font and size in
                                            % point (pt) units.
        
            \coordinate (NextPosition) at (\NextTextCenterX, \NextTextCenterY); % The center coordinate position
                                                                                % to place the next text element at.
            
            % For testing only: Place a dot to show where the center coordinate is on screen.
            %\node at (NextPosition) [circle,
            %                         fill,
            %                         inner sep=1.5pt]{};
            
            % Paint the text to the page.
            \node[] (NextNode) at (NextPosition) {\x};
            
            \draw[color=black]  ([yshift=5pt] NextNode.north west) --
                                ([yshift=5pt] NextNode.north east) --
                                ([yshift=-5pt, xshift=\CrossHorDistance] NextNode.south east);
                                 
            \draw[color=black]  ([yshift=-5pt] NextNode.south west) -- 
                                ([yshift=-5pt] NextNode.south east) --
                                ([yshift=5pt, xshift=\CrossHorDistance] NextNode.north east);
            
            % Update the variables that track the x and y coordinates of where to place the next text.
            %\pgfmathparse{\NextTextCenterX + (\strwidth - 20pt)}
            \pgfmathparse{\NextTextCenterX + 5pt} % This doesn't work when using \strwidth...
            \edef\NextTextCenterX{\pgfmathresult}
        }
        
    \end{tikzpicture}
}

\CreateTestGraphic{0pt}{0pt}{apple, blueberry, banana}

\end{document}

(本文编译于Overleaf.com使用 XeLaTeX 编译器。

此代码产生以下(损坏且不完整的)结果: 损坏的功能输出 尽管有图形输出,编辑器中仍会出现错误: 编译器错误/警告

我花了很多时间研究,试图弥补我对 TeX 引擎的了解。以下是我的主要问题:

  • 如何创建一个可以通过任意值更新的数值变量(像普通编程语言一样)?我尝试使用\edef\newcommand等,但似乎没有一个像我预期的那样工作。
    示例:double var = 15pt; var += 73pt
    上下文:节点在 tikz 的中心坐标处绘制。如果我想将每个文本元素等距放置以允许交叉线一致流动,我需要更新下一个节点的中心坐标。

  • 我到底如何才能获得以点(pt)为单位的文本的宽度和高度?
    上下文:如上文所述,由于我需要持续跟踪下一个节点的中心坐标,因此我需要了解传入文本的尺寸。我尝试过和width("\x")\settowidth{\wvar}{\x}但当我尝试执行等时,在数学中使用它们并没有取得太大的成果width("\x") * 0.5pt。我还需要获取文本的最大高度,这样我才能为所有图形在正确的高度绘制线条。

在示例代码中,我未能成功使用\strwidth以下表达式中的变量:\pgfmathparse{\NextTextCenterX + (\strwidth * 0.5pt)}。我在这里不知道如何实际维护可更新变量,以及如何获取文本的数值宽度和高度,然后可以在变量中使用这些数值进行操作。

有人能帮助我回到正轨并回答我上述的问题吗?


接受答案后的更新(2022 年 5 月 18 日):

谢谢@汤姆下面的回答,实现了所需的图形输出!不过我确实做了一点修改。如我问题的初始图片所示,我希望添加一个不会被封闭的箭头,以显示图形中的移动方向。所以,我修改了@汤姆的回答稍微有点道理,最后,示例代码创建了:

\documentclass{article}
\usepackage{tikz}
\usepackage{graphicx,pgf}
\usepackage{listofitems}

\usetikzlibrary{arrows,calc}

%   @brief          Creates a test graphic.
%   @param[in] #1   The x-coordinate and y-coordinate of the graphic.
%   @param[in] #2   A comma separated list of strings.
\newcommand{\CreateTestGraphic}[2]
{%
    \newcommand{\CrossHorDistance}{15pt}    % The horizontal distance for each cross.

    \begin{tikzpicture}
    
        \begin{scope}[shift={(#1)}]
        
            %
            %   Create a blank node and draw the initial starting cross and direction arrow.
            %
            \coordinate (NextNode) at (0,0);
            
            \node[inner sep=0pt,
                  minimum size=1cm,
                  align=center,
                  right=\CrossHorDistance] (NextNode) at (NextNode.east) {};
                  
            \draw [-stealth] ([xshift=-15pt-\CrossHorDistance] NextNode.west) --
                             ([xshift=0pt-\CrossHorDistance] NextNode.west);
            
            \draw[color=black]  ([yshift=5pt] NextNode.north west) --
                                ([yshift=-5pt, xshift=-\CrossHorDistance] NextNode.south west) --
                                ($([yshift=-5pt, xshift=-\CrossHorDistance] NextNode.south west) + (-5pt, 0pt)$);
                                 
            \draw[color=black]  ([yshift=-5pt] NextNode.south west) --
                                ([yshift=5pt, xshift=-\CrossHorDistance] NextNode.north west) --
                                ($([yshift=5pt, xshift=-\CrossHorDistance] NextNode.north west) + (-5pt, 0pt)$);
                                
            % Reset the 'NextNode' in preparation for painting all of the nodes.
            \coordinate (NextNode) at (0,0);
            
            \foreach \content in {#2}
            {%
                \node[text depth=.3\baselineskip,
                      text height=.7\baselineskip,
                      inner sep=0pt,
                      minimum size=1cm,
                      align=center,
                      right=\CrossHorDistance] (NextNode) at (NextNode.east) {\content};
                      
                \draw[color=black]  ([yshift=5pt] NextNode.north west) --
                                    ([yshift=5pt] NextNode.north east) --
                                    ([yshift=-5pt, xshift=\CrossHorDistance] NextNode.south east);
                                    
                \draw[color=black]  ([yshift=-5pt] NextNode.south west) --
                                    ([yshift=-5pt] NextNode.south east) --
                                    ([yshift=5pt, xshift=\CrossHorDistance] NextNode.north east);
            }
            
            \draw[color=black]  ([yshift=-5pt, xshift=\CrossHorDistance] NextNode.south east) --
                                ($([yshift=-5pt, xshift=\CrossHorDistance] NextNode.south east) + (5pt, 0pt)$);
                                 
            \draw[color=black]  ([yshift=5pt, xshift=\CrossHorDistance] NextNode.north east) --
                                ($([yshift=5pt, xshift=\CrossHorDistance] NextNode.north east) + (5pt, 0pt)$);
            
        \end{scope}

    \end{tikzpicture}
}

\begin{document}

\CreateTestGraphic{0pt, 0pt}{apple, blueberry, banana}

\end{document}

(本文编译于Overleaf.com使用 XeLaTeX 编译器。

最终输出: 最终最终最终输出


接受答案后的更新#2(2022 年 5 月 18 日):

我花了一些时间探索如何为每个单元格着色。为了做到这一点,我需要改变在屏幕上绘制事物的方式。对于任何可能感兴趣的人,这里有一个可行的解决方案示例:

\documentclass{article}
\usepackage{tikz}
\usepackage{graphicx,pgf}
\usepackage{listofitems}
\usepackage{ifthen}

\usepackage{pgfplots}
\pgfplotsset{compat=1.11}
\usepgfplotslibrary{fillbetween}
\usetikzlibrary{intersections}

\pgfdeclarelayer{bg}
\pgfsetlayers{bg,main}

\usetikzlibrary{arrows,calc}

%   @brief          Creates a test graphic.
%   @param[in] #1   The x-coordinate and y-coordinate of the graphic.
%   @param[in] #2   A comma separated list of colors.
%   @param[in] #3   A comma separated list of strings.
\newcommand{\CreateTestGraphic}[3]
{%
    \newcommand{\CrossHorDistance}{15pt}    % The horizontal distance for each cross.

    \begin{tikzpicture}
    
        \begin{scope}[shift={(#1)}]
        
            %
            %   Create a blank node and draw the initial starting cross and direction arrow.
            %
            \coordinate (NextNode) at (0,0);
            
            \node[inner sep=0pt,
                  minimum size=1cm,
                  align=center,
                  right=\CrossHorDistance] (NextNode) at (NextNode.east) {};
                  
            \draw [-stealth] ([xshift=-15pt-\CrossHorDistance] NextNode.west) --
                             ([xshift=0pt-\CrossHorDistance] NextNode.west);
            
            \draw[color=black]  ([yshift=0pt, xshift=-\CrossHorDistance/2] NextNode.west) --
                                ([yshift=-5pt, xshift=-\CrossHorDistance] NextNode.south west) --
                                ($([yshift=-5pt, xshift=-\CrossHorDistance] NextNode.south west) + (-5pt, 0pt)$);
                                 
            \draw[color=black]  ([yshift=0pt, xshift=-\CrossHorDistance/2] NextNode.west) --
                                ([yshift=5pt, xshift=-\CrossHorDistance] NextNode.north west) --
                                ($([yshift=5pt, xshift=-\CrossHorDistance] NextNode.north west) + (-5pt, 0pt)$);
                                
            % Reset the 'NextNode' in preparation for painting all of the nodes.
            \coordinate (NextNode) at (0,0);
            
            \readlist*\colors{#2}
            
            \foreach \content [count=\i] in {#3}
            {%
                \ifthenelse{\equal{\colors[\i]}{none}}
                {%
                    \def\NextColor{none}
                }
                {%
                    \def\NextColor{\colors[\i]!100}
                }
            
                \node[text depth=.1\baselineskip,
                      text height=.7\baselineskip,
                      inner sep=0pt,
                      minimum size=1cm,
                      align=center,
                      right=\CrossHorDistance] (NextNode) at (NextNode.east) {\content};
                      
                \begin{pgfonlayer}{bg}             
                    \draw  [color= black,
                            fill = \NextColor,
                            fill opacity=0.5]   ([yshift=5pt] NextNode.north west) --
                                                ([yshift=5pt] NextNode.north east) --
                                                ([yshift=0pt, xshift=\CrossHorDistance/2] NextNode.east) --
                                                ([yshift=-5pt, xshift=0pt] NextNode.south east) --
                                                ([yshift=-5pt] NextNode.south west) --
                                                ([yshift=0pt, xshift=-\CrossHorDistance/2] NextNode.west) --
                                                ([yshift=5pt, xshift=0pt] NextNode.north west);
                \end{pgfonlayer}
            }

            \draw[color=black]  ([yshift=0pt, xshift=\CrossHorDistance/2] NextNode.east) --
                                ([yshift=-5pt, xshift=\CrossHorDistance] NextNode.south east) --
                                ($([yshift=-5pt, xshift=\CrossHorDistance] NextNode.south east) + (5pt, 0pt)$);
                                 
            \draw[color=black]  ([yshift=0pt, xshift=\CrossHorDistance/2] NextNode.east) --
                                ([yshift=5pt, xshift=\CrossHorDistance] NextNode.north east) --
                                ($([yshift=5pt, xshift=\CrossHorDistance] NextNode.north east) + (5pt, 0pt)$);
                                
            \node [] at ([xshift=\CrossHorDistance + 2.5pt] NextNode.east) {\ldots};
            
        \end{scope}

    \end{tikzpicture}
}

\begin{document}

\CreateTestGraphic{0pt, 0pt}{orange, none, red}{apple, blueberry, banana}

\end{document}

(本文编译于Overleaf.com使用 XeLaTeX 编译器。

最终输出: 新的最终输出

答案1

您可以在 tikz 形状库中使用这个预定义形状。您可以\foreach在 tikzpicture 范围内定义一个命令,如下所示。第一个参数是初始位置,第二个参数将是您要输入的内容列表:

\documentclass[border=0.618cm]{standalone}
\usepackage{listofitems}
\usepackage{tikz}
\usetikzlibrary{shapes}
\tikzset{
    mynd/.style={
        signal,draw,
        signal to=west and east,
        minimum size=1cm, 
        align=center,
        signal pointer angle=110,
        right=-\pgflinewidth
    },
}
\newcommand{\CreateGraphic}[2]{
\begin{scope}[shift={(#1)}]
\coordinate (a) (0,0);
\foreach \content in {#2}
\node (a) [mynd,text width=2cm] at (a.east) {\content};
\end{scope}
}

\begin{document}
\begin{tikzpicture}
\CreateGraphic{0,0}{$\longrightarrow$,(S)tart,7-bit Target (\textbf{Addr})ess,Write Bit,some thing,something longer}
\CreateGraphic{0,-1.5}{$\longrightarrow$,(S)tart,7-bit Target (\textbf{Addr})ess,Write Bit}
\end{tikzpicture}
\end{document}

在此处输入图片描述

使用原来的方法:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\newcommand{\CrossHorDistance}{15pt}
\newcommand{\CreateTestGraphic}[3]{%
\begin{scope}[shift={(#1)}]
\coordinate (NextNode) at (0,0);
\foreach \content in {#3}{
\node[inner sep=0pt,minimum size=1cm,text width=#2,align=center] (NextNode) at ($(NextNode.east)+(#2/2+\CrossHorDistance,0)$) {\content};
\draw[color=black]  ([yshift=5pt] NextNode.north west) -- ([yshift=5pt] NextNode.north east) -- ([yshift=-5pt, xshift=\CrossHorDistance] NextNode.south east);
\draw[color=black]  ([yshift=-5pt] NextNode.south west) -- ([yshift=-5pt] NextNode.south east) -- ([yshift=5pt, xshift=\CrossHorDistance] NextNode.north east);
}
\end{scope}
}

\begin{document}
\begin{tikzpicture}
\CreateTestGraphic{0,0}{2cm}{$\longrightarrow$,apple,blueberry longer,banana}% #1(initial position) #2(text width) #3(list of content)
\CreateTestGraphic{0,-2}{2cm}{$\longrightarrow$,apple,blueberry longer,banana,Write Bit}
\end{tikzpicture}
\end{document}

在此处输入图片描述

未定义文本宽度

\documentclass{article}
\usepackage{tikz}
\newcommand{\CrossHorDistance}{15pt}
\newcommand{\CreateTestGraphic}[2]{%
\begin{scope}[shift={(#1)}]
\coordinate (NextNode) at (0,0);
\foreach \content in {#2}{
\node[inner sep=0pt,minimum size=1cm,align=center,right=\CrossHorDistance] (NextNode) at (NextNode.east) {\content};
\draw[color=black]  ([yshift=5pt] NextNode.north west) -- ([yshift=5pt] NextNode.north east) -- ([yshift=-5pt, xshift=\CrossHorDistance] NextNode.south east);
\draw[color=black]  ([yshift=-5pt] NextNode.south west) -- ([yshift=-5pt] NextNode.south east) -- ([yshift=5pt, xshift=\CrossHorDistance] NextNode.north east);
}
\end{scope}
}

\begin{document}
\begin{tikzpicture}
\CreateTestGraphic{0,0}{$\longrightarrow$,apple,blueberry longer,banana}% #1(initial position) #2(text width) #3(list of content)
\CreateTestGraphic{0,-2}{blueberry longer,banana,Write Bit}
\end{tikzpicture}
\end{document}

在此处输入图片描述

相关内容