tikzset 在 foreach 循环中不起作用

tikzset 在 foreach 循环中不起作用

我尝试使用 foreach 循环来定义新样式。但是,循环内的代码似乎没有效果。以下 MWE 说明了我的问题:

\documentclass[tikz]{standalone}

\tikzset{
    mystyle/.code=
    {
        %\tikzset{very thick}, % This line has an effect (if uncommented)
        \foreach \pos in {0.1, 0.2, 0.3, 0.4} % **EDIT**: Should be a list of names in the end.
        {
            % There will be other statements here that actually use the list members

            \tikzset{very thick}, %This line is an exact copy of the comented line but has no effect.
            \message{======== Foreach: pos=\pos}
        }
    }
}

\begin{document}
\begin{tikzpicture}
    \draw [help lines] (-10,-10) grid (10,10);
    \draw[mystyle] (0,0) --++ (5,0);
\end{tikzpicture}
\end{document}

当我编译此文档时,控制台显示以下消息

... ======== Foreach:pos=0.1 ======== Foreach:pos=0.2 ======== Foreach:pos=0.3 ======== Foreach:pos=0.4 [1] (./mwe.aux)) ...

所以我相信 \tikzset 确实运行了。没有错误消息。

那么,为什么 \tikzset 命令不能使我的线条变粗?我该如何让它工作呢?

编辑:上下文:这(当然)是更大事情的一部分。最后,我想创建如下所示的晶体管棒图:http://www.southampton.ac.uk/~bim/notes/cad/guides/sticks.html (摘要:芯片级晶体管布局仅使用不同颜色的线条来绘制。当“多晶硅”颜色的线与“N 扩散”或“P 扩散”颜色的线相交时,就会出现晶体管栅极。为此,我首先创建了一些线条和节点样式,借用了循环空间和 circuitikz(用于节点样式)。有了它,我可以绘制我的图表,但它仍然是手动的,因为我必须绘制多条线来创建一个晶体管:扩散和多晶硅线。所以,我决定创建一个新的线条样式,这样我就可以自动放置晶体管门。使用从卡西米尔,我能够创建以下内容:

\documentclass[tikz]{standalone}

% Code adapted from: https://tex.stackexchange.com/a/393496/69074
\usetikzlibrary{decorations.markings}

%{{{ Transistor Stick Diagrams

%{{{ Layer Magic

% Source: Adapted from https://tex.stackexchange.com/a/20426/69074
\pgfdeclarelayer{M3L}   % Metal 3
\pgfdeclarelayer{M2L}   % Metal 1
\pgfdeclarelayer{M1L}   % Metal 1
\pgfdeclarelayer{POL}   % Poly-Silicon
\pgfdeclarelayer{DDL}   % Diffusion
\pgfdeclarelayer{CONTL} % Contacts
\pgfsetlayers{DDL,POL,M1L,M2L,M3L,CONTL,main}

\makeatletter
\pgfkeys{%
    /tikz/on layer/.code={
        \pgfonlayer{#1}\begingroup
        \aftergroup\endpgfonlayer
        \aftergroup\endgroup
    },
    /tikz/node on layer/.code={
        \gdef\node@@on@layer{%
            \setbox\tikz@tempbox=\hbox\bgroup\pgfonlayer{#1}\unhbox\tikz@tempbox\endpgfonlayer\egroup}
        \aftergroup\node@on@layer
    },
    /tikz/end node on layer/.code={
        \endpgfonlayer\endgroup\endgroup
    }
}

\def\node@on@layer{\aftergroup\node@@on@layer}

%}}}

%{{{ Declare the Node shapes

% Code stolen (and slightly adapted from Circuitikz:
% Source: /usr/share/texmf-dist/tex/generic/circuitikz/pgfcircshapes.tex
\makeatletter
\newdimen\sticknodewidth 
\sticknodewidth=1cm

%% Left out, boring and too long

\makeatother
%}}}

%{{{ Line Styles

\tikzstyle{M3}     = [draw=green,       line width=0.3, on layer=M3L  ] % Metal 3 Path
\tikzstyle{M2}     = [draw=yellow,      line width=0.3, on layer=M2L  ] % Metal 2 Path
\tikzstyle{M1}     = [draw=turquoise,   line width=0.3, on layer=M1L  ] % Metal 1 Path
\tikzstyle{PO}     = [draw=blue,        line width=0.3, on layer=POL  ] % Poly-Silicon Path (Gate)
\tikzstyle{PD}     = [draw=red!50,      line width=0.3, on layer=DDL  ] % P-Diffusion Path (PMOS)
\tikzstyle{ND}     = [draw=red,         line width=0.3, on layer=DDL  ] % N-Diffusion Path (NMOS)
\tikzstyle{air}    = [draw=black,dashed,line width=0.1, on layer=CONTL] % N-Diffusion Path (NMOS)
%}}}

%{{{ Port Styles

\tikzstyle{M3P}    = [stickport,    color=green,      node on layer=M3L  ] % Metal 3 Terminal
\tikzstyle{M2P}    = [stickport,    color=yellow,     node on layer=M2L  ] % Metal 2 Terminal
\tikzstyle{M1P}    = [stickport,    color=turquoise,  node on layer=M1L  ] % Metal 1 Terminal
\tikzstyle{POP}    = [stickport,    color=blue,       node on layer=POL  ] % Poly-Silicon Terminal
\tikzstyle{PDP}    = [stickport,    color=red!50,     node on layer=DDL  ] % P-Diffusion Terminal (PMOS)
\tikzstyle{NDP}    = [stickport,    color=red,        node on layer=DDL  ] % N-Diffusion Terminal (NMOS)
\tikzstyle{CON}    = [stickcontact, color=black,      node on layer=CONTL] % Via (between touching M3,M2,M1,PO,P-Diff,N-Diff)
\tikzstyle{TAP}    = [sticktap,     color=black,      node on layer=CONTL] % Substrate Tap (between touching M1,Diff,N-Diff)
\tikzstyle{TAPCON} = [sticktapcon,  color=black,      node on layer=CONTL] % Merged Tap and Via
%}}}

%{{{ Automatic Transistors

\tikzset{
    gate/.style 2 args=
    {
        thick,decoration=
        {
            markings, mark=at position {#1} with
            {
                \draw[PO] (0,-0.25)coordinate(m#2ga)--(0,0.25)coordinate(m#2gb);
                \node[inner xsep=0, inner ysep=0.1mm,above left,font=\tiny,rotate=\pgfdecoratedangle-90](x) {$M_{#2}$};
            }
        },
        postaction={decorate}
    },
    stick pmos/.style=
    {
        PD,
        gate/.list={#1}
    },
    pmos/.code=
    {
        \foreach \name in {#1}
        {
            % Calculate \pos somehow, I'm not there yet
            \tikzset{gate={\pos}{\name}}
        }
    }
}
%}}}

%}}}

\begin{document}
\begin{tikzpicture}
    \draw [help lines] (-10,-10) grid (10,10);

    \draw[stick pmos={{0.333}{1},{0.666}{2}}] (0,0)--++(0,2);
    \draw[PO] (m1ga)--++(0.5,0.5);
    \draw[PO] (m2gb)--++(-0.5,-0.5);

    \draw[pmos={a,b,c}] (3,0)--++(0,2); % Gates spaced automatically
    \draw[PO] (maga)--++(0.5,0.5);
    \draw[PO] (magb)--++(-0.5,-0.5);

\end{tikzpicture}
\end{document}

这个东西非常接近我想要的。然而,最后一部分是这个问题的灵感来源:gate/.list 处理程序不允许我自动放置节点(或者我只是没有找到如何做到这一点),所以我想自己做循环。手册第 82.4.6 节说,/.list 处理程序在内部使用 foreach 循环,所以我尝试了一下,遇到了问题。我创建的 MWE 不是最优的,因为它使用数字列表进行循环,而最终版本应该是名称循环。很抱歉。

答案1

正如我在评论中所说,发生这种情况是因为 的内容\foreach是在组内执行的,因此当组结束时, 的值line width(由键修改very thick)将恢复为先前的值。这就是为什么在循环外执行此操作可以正常工作的原因\foreach

第一个代码的解决方案

这是一个通过更改循环命令的简单解决方案。有几个选项,这里我使用expl3\fp_step_variable:nnnNn

首先我复制一个\fp_step_variable:nnnNn叫做的副本\fpStepVariable。它的语法\fp_step_variable:nnnNn是:

\fp_step_variable:nnnNn {<initial value>} {<step>} {<final value>} <tl var> {<code>}

所以我替换了:

\foreach \pos in {0.1, 0.2, 0.3, 0.4}

经过

\fpStepVariable {0.1} {0.1} {0.4} \pos

我还添加了两个\typeouts 来显示效果。运行代码后控制台显示:

Line width before: 0.4pt
======== Foreach: pos=0.1 ======== Foreach: pos=0.2 ======== Foreach: pos=0.3 ======== Foreach: pos=0.4
Line width after: 1.2pt

完整代码:

\documentclass[tikz]{standalone}
\usepackage{expl3}

\ExplSyntaxOn
\cs_set_eq:NN \fpStepVariable \fp_step_variable:nnnNn
\ExplSyntaxOff

\tikzset{
  mystyle/.code=
  {
    \typeout{Line width before: \the\pgflinewidth}
    \fpStepVariable {0.1} {0.1} {0.4} \pos
      {
        \tikzset{very thick}, %This line is an exact copy of the comented line but has no effect.
        \message{======== Foreach: pos=\pos}
      }
    \typeout{Line width after: \the\pgflinewidth}
  }
}

\begin{document}
\begin{tikzpicture}
  \draw [help lines] (-10,-10) grid (10,10);
  \draw[mystyle] (0,0) --++ (5,0);
\end{tikzpicture}
\end{document}

第二段代码的解决方法

我在这里使用的解决方案除了一些细节外与上一个非常相似。

以前我使用 ,\fp_step_variable:nnnNn因为你有一个统一的数字序列。现在你有字母。解决这个问题的一种方法是使用\clist_map_...函数循环遍历逗号分隔的事物列表。为了保持使用语法相似,我使用了\clist_map_variable:nNn,其语法是:

\clist_map_variable:nNn {<comma list>} <tl var> {<code>}

我复制\clist_map_variable:nNn\ClistMapVariable使用了:

\ClistMapVariable{#1}\name

您传递给键的#1列表在哪里。您将面临的一个问题是变量扩展为名称(、或),但它不是名称本身,因此您需要先扩展它。为此我使用了:a,b,cpmos\nameabc

\edef\tempa{\noexpand\tikzset{gate={\pos}{\name}}}
\tempa

之后\edef\tempa将会是类似于\tikzset{gate={0.12345}{a}}我们想要的结果。

最后,我用

\pgfmathparse{rnd}
\edef\pos{\pgfmathresult}

赋予 一定 价值\pos.

完整代码:

\documentclass[tikz]{standalone}
\usepackage{expl3}
\ExplSyntaxOn
\cs_set_eq:NN \ClistMapVariable \clist_map_variable:nNn
\ExplSyntaxOff

% Code adapted from: https://tex.stackexchange.com/a/393496/69074
\usetikzlibrary{decorations.markings}

%{{{ Transistor Stick Diagrams

%{{{ Layer Magic

% Source: Adapted from https://tex.stackexchange.com/a/20426/69074
\pgfdeclarelayer{M3L}   % Metal 3
\pgfdeclarelayer{M2L}   % Metal 1
\pgfdeclarelayer{M1L}   % Metal 1
\pgfdeclarelayer{POL}   % Poly-Silicon
\pgfdeclarelayer{DDL}   % Diffusion
\pgfdeclarelayer{CONTL} % Contacts
\pgfsetlayers{DDL,POL,M1L,M2L,M3L,CONTL,main}

\makeatletter
\pgfkeys{%
    /tikz/on layer/.code={
        \pgfonlayer{#1}\begingroup
        \aftergroup\endpgfonlayer
        \aftergroup\endgroup
    },
    /tikz/node on layer/.code={
        \gdef\node@@on@layer{%
            \setbox\tikz@tempbox=\hbox\bgroup\pgfonlayer{#1}\unhbox\tikz@tempbox\endpgfonlayer\egroup}
        \aftergroup\node@on@layer
    },
    /tikz/end node on layer/.code={
        \endpgfonlayer\endgroup\endgroup
    }
}

\def\node@on@layer{\aftergroup\node@@on@layer}

%}}}

%{{{ Declare the Node shapes

% Code stolen (and slightly adapted from Circuitikz:
% Source: /usr/share/texmf-dist/tex/generic/circuitikz/pgfcircshapes.tex
\makeatletter
\newdimen\sticknodewidth 
\sticknodewidth=1cm

%% Left out, boring and too long

\makeatother
%}}}

%{{{ Line Styles

\tikzset{
  M3/.style  = { draw=green,       line width=0.3, on layer=M3L   },  % Metal 3 Path
  M2/.style  = { draw=yellow,      line width=0.3, on layer=M2L   },  % Metal 2 Path
  M1/.style  = { draw=turquoise,   line width=0.3, on layer=M1L   },  % Metal 1 Path
  PO/.style  = { draw=blue,        line width=0.3, on layer=POL   },  % Poly-Silicon Path (Gate)
  PD/.style  = { draw=red!50,      line width=0.3, on layer=DDL   },  % P-Diffusion Path (PMOS)
  ND/.style  = { draw=red,         line width=0.3, on layer=DDL   },  % N-Diffusion Path (NMOS)
  air/.style = { draw=black,dashed,line width=0.1, on layer=CONTL },  % N-Diffusion Path (NMOS)
}

%}}}

%{{{ Port Styles

\tikzset{
  M3P/.style    = {stickport,    color=green,      node on layer=M3L  }, % Metal 3 Terminal
  M2P/.style    = {stickport,    color=yellow,     node on layer=M2L  }, % Metal 2 Terminal
  M1P/.style    = {stickport,    color=turquoise,  node on layer=M1L  }, % Metal 1 Terminal
  POP/.style    = {stickport,    color=blue,       node on layer=POL  }, % Poly-Silicon Terminal
  PDP/.style    = {stickport,    color=red!50,     node on layer=DDL  }, % P-Diffusion Terminal (PMOS)
  NDP/.style    = {stickport,    color=red,        node on layer=DDL  }, % N-Diffusion Terminal (NMOS)
  CON/.style    = {stickcontact, color=black,      node on layer=CONTL}, % Via (between touching M3,M2,M1,PO,P-Diff,N-Diff)
  TAP/.style    = {sticktap,     color=black,      node on layer=CONTL}, % Substrate Tap (between touching M1,Diff,N-Diff)
  TAPCON/.style = {sticktapcon,  color=black,      node on layer=CONTL}, % Merged Tap and Via
}
%}}}

%{{{ Automatic Transistors

\tikzset{
    gate/.style 2 args=
    {
        thick,decoration=
        {
            markings, mark=at position {#1} with
            {
                \draw[PO] (0,-0.25)coordinate(m#2ga)--(0,0.25)coordinate(m#2gb);
                \node[inner xsep=0, inner ysep=0.1mm,above left,font=\tiny,rotate=\pgfdecoratedangle-90](x) {$M_{#2}$};
            }
        },
        postaction={decorate}
    },
    stick pmos/.style=
    {
        PD,
        gate/.list={#1}
    },
    pmos/.code=
    {
        \ClistMapVariable{#1}\name
        {
          \pgfmathparse{rnd}
          \edef\pos{\pgfmathresult}
          \edef\tempa{\noexpand\tikzset{gate={\pos}{\name}}}
          \tempa
        }
    }
}
%}}}

%}}}

\begin{document}
\begin{tikzpicture}
    \draw [help lines] (-10,-10) grid (10,10);

    \draw[stick pmos={{0.333}{1},{0.666}{2}}] (0,0)--++(0,2);
    \draw[PO] (m1ga)--++(0.5,0.5);
    \draw[PO] (m2gb)--++(-0.5,-0.5);

    \draw[pmos={a,b,c}] (3,0)--++(0,2); % Gates spaced automatically
    \draw[PO] (maga)--++(0.5,0.5);
    \draw[PO] (magb)--++(-0.5,-0.5);

\end{tikzpicture}
\end{document}

评论

  • 这是其中一种方法。还有更多方法。您可以使用 marmot 先生的建议来“偷运”\tikzset组外的内容,或者您​​可以使用其他循环函数,甚至可以使用样式做一些巧妙的事情。

  • \clist_map_variable:nNn习惯于保持与以前类似的语法。您可以使用\clist_map_inline:nn,这样可以避免进行这种扩展恶作剧。我把这个留给你。

  • 该语法\tikzstyle{a}=[b]已弃用。您应该使用\tikzset{s/.style={b}}。我在您的代码中对其进行了更改。

相关内容