使用 TikZ 是否可以通过预处理器传递节点内容?

使用 TikZ 是否可以通过预处理器传递节点内容?

我写了一个宏来扫描其内容非法的然后将它们改变为其他内容合适的。在下面的例子中,宏将扫描并将_其替换为\rule[-1pt]{0.75em}{1.0pt}

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}

\makeatletter

\newcommand\aescandash[1]{%%
  \let\ae@scan@dash@result\relax
  \ae@scan@dash@parse#1_\@nil
  \ae@scan@dash@result
}

\def\ae@add@to@result#1#2{%%
  \ifx#1\relax
    \def#1{#2}%%
  \else
    \expandafter\def\expandafter#1\expandafter{#1#2}%%
  \fi}

\def\ae@scan@dash@parse#1_#2\@nil{%%
  \def\ae@reevaluate{}%%
  \expandafter\ifx\expandafter\relax\detokenize\expandafter{#2}\relax
    \ae@add@to@result\ae@scan@dash@result{#1}%%
  \else
    \ae@add@to@result\ae@scan@dash@result{#1\rule[-1pt]{0.75em}{1.0pt}}%%
    \def\ae@reevaluate{\ae@scan@dash@parse#2\@nil}%%
  \fi
  \ae@reevaluate
  }

\makeatother

\begin{document}

\aescandash{this_is_a_dash_filled_sentence}

\end{document}

我希望能够将这样的预处理器应用于 TikZ 图片中节点的内容:

\begin{tikzpicture}[%%
  my node/.style={red,
                  preprocessor=\aescandash},
  ]

  \foreach \myn [count=\myc from 1] in {this,that,another_text}
  {
    \node[my node] at (0,-\myc) {\myn};
  }

\end{tikzpicture}

但没有这样的preprocessor键。理想情况下,我希望预处理器有效地将以下内容传递给节点内容:

\expandafter\aescandash\expandafter{\myn}

换句话说,预处理器将完成以下代码所做的事情:

\begin{tikzpicture}[%%
  my node/.style={red},
  ]

  \foreach \myn [count=\myc from 1] in {this,that,another_text}
  {
    \node[my node] at (0,-\myc) {\expandafter\aescandash\expandafter{\myn}};
  }

\end{tikzpicture}

答案1

为什么不只是这样呢?

\documentclass[tikz,border=5pt]{standalone}
\usetikzlibrary{calc}

\makeatletter

\newcommand\aescandash[1]{%%
  \let\ae@scan@dash@result\relax
  \ae@scan@dash@parse#1_\@nil
  \ae@scan@dash@result
}

\def\ae@add@to@result#1#2{%%
  \ifx#1\relax
  \def#1{#2}%%
  \else
  \expandafter\def\expandafter#1\expandafter{#1#2}%%
  \fi}

\def\ae@scan@dash@parse#1_#2\@nil{%%
  \def\ae@reevaluate{}%%
  \expandafter\ifx\expandafter\relax\detokenize\expandafter{#2}\relax
  \ae@add@to@result\ae@scan@dash@result{#1}%%
  \else
  \ae@add@to@result\ae@scan@dash@result{#1\rule[-1pt]{0.75em}{1.0pt}}%%
  \def\ae@reevaluate{\ae@scan@dash@parse#2\@nil}%%
  \fi
  \ae@reevaluate
}

\makeatother

\begin{document}

  \begin{tikzpicture}
    [
      my node/.code={
        \let\oldmyn\myn
        \gdef\myn{\expandafter\aescandash\expandafter{\oldmyn}}%
      },
    ]

    \foreach \myn [count=\myc from 1] in {this,that,another_text}
    {
      \node[my node] at (0,-\myc) {\myn};
    }

  \end{tikzpicture}

\end{document}

我的节点代码

答案2

可以插入预处理器,但显然这涉及到节点解析器的微小改动。

在下面的代码中,我定义了一个preprocess node content以宏为参数的键。这个宏应该被定义为接受一个参数,即要处理的文本。显然,如果有比简单文本更复杂的东西,那么整个事情可能会失败。

我还展示了使用扩展 latex 替换字符的另一种方法(尽管不一定更好)。我使用星号代替下划线,因此在没有预处理器时不会出现错误。

\documentclass[tikz, border=5]{standalone}

\makeatletter    
\let\tikz@do@fig@original=\tikz@do@fig
\newtoks\tikz@fig@toks%
\def\tikz@do@fig@preprocessed{%
  \tikz@do@fig@original%
  \afterassignment\tikz@do@fig@@preprocessed%
  \tikz@fig@toks=\bgroup}

\def\tikz@do@fig@@preprocessed{%
  \expandafter\tikz@fig@preprocess\expandafter{\the\tikz@fig@toks}%
  \egroup}

\def\tikz@fig@preprocess#1{#1}
\tikzset{%
  preprocess node content/.code={%
    \let\tikz@fig@preprocess=#1\relax%
    \let\tikz@do@fig=\tikz@do@fig@preprocessed%
  }
}
\makeatother

{\catcode`\*=13 \gdef*{\rule[-1pt]{0.75em}{1.0pt}}}
\def\pp#1{{\catcode`\*=13 \scantokens{#1\ignorespaces}}}

\begin{document}
\begin{tikzpicture}
\node [draw] at (0,0) {*foo*bar*};
\node [draw, preprocess node content=\pp] at (0,-1) {*foo*bar*};
\end{tikzpicture}
\end{document}

在此处输入图片描述

答案3

实际上有一个伪装的预处理器。您可以使用node contents从 TikZ 3 开始的键。通过样式嵌套,它变成了预处理器。但缺点是您必须在括号内给出所有规范,因为右括号会完成路径上的节点解析。

编辑:经过 cfr 的修正,选择预处理器或关闭它的选项稍微好一些。scanned node contents无疑是一个臃肿的名字,所以你可以选择一些更吸引人的东西。

\documentclass[]{article}
\usepackage{tikz}
\makeatletter
\newcommand\aescandash[1]{\let\ae@scan@dash@result\relax\ae@scan@dash@parse#1_\@nil\ae@scan@dash@result}
\def\ae@add@to@result#1#2{\ifx#1\relax\def#1{#2}\else\expandafter\def\expandafter#1\expandafter{#1#2}\fi}

\def\ae@scan@dash@parse#1_#2\@nil{\def\ae@reevaluate{}%
\expandafter\ifx\expandafter\relax\detokenize\expandafter{#2}\relax%
    \ae@add@to@result\ae@scan@dash@result{#1}%%
  \else\ae@add@to@result\ae@scan@dash@result{#1\rule[-1pt]{0.75em}{1.0pt}}%
\def\ae@reevaluate{\ae@scan@dash@parse#2\@nil}%%
  \fi%
  \ae@reevaluate%
  }
\makeatother

\tikzset{
  preprocessor/.store in=\mypreproc,
  preprocessor=aescandash,
  %preprocessor=, % Turn it off
  scanned node contents/.style={
    node contents={\csname\mypreproc\endcsname{#1}},
  }
}
\begin{document}
\begin{tikzpicture}
\node[scanned node contents={this_is_a_dash_filled_sentence},fill=red!10,at={(1,0)},anchor=west];
\node[scanned node contents={another_dashed_one},fill=blue!10,at={(0,1)}];
\end{tikzpicture}
\end{document}

在此处输入图片描述

答案4

感谢 @cfr ,这是我想到的解决方案。我没有完全采用他的解决方案,因为我需要\myn在主文档中保留原样以供以后使用。

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}

\makeatletter

\newcommand\aescandash[1]{%%
  \let\ae@scan@dash@result\relax
  \ae@scan@dash@parse#1_\@nil
  \ae@scan@dash@result
}

\def\ae@add@to@result#1#2{%%
  \ifx#1\relax
    \def#1{#2}%%
  \else
    \expandafter\def\expandafter#1\expandafter{#1#2}%%
  \fi}

\def\ae@scan@dash@parse#1_#2\@nil{%%
  \def\ae@reevaluate{}%%
  \expandafter\ifx\expandafter\relax\detokenize\expandafter{#2}\relax
    \ae@add@to@result\ae@scan@dash@result{#1}%%
  \else
    \ae@add@to@result\ae@scan@dash@result{#1\rule[-1pt]{0.75em}{1.0pt}}%%
    \def\ae@reevaluate{\ae@scan@dash@parse#2\@nil}%%
  \fi
  \ae@reevaluate
  }

\makeatother

\begin{document}

\aescandash{this_is_a_dash_filled_sentence}

\begin{tikzpicture}[%%
  my node/.style={red},
  my node content/.code={\def\aenodecontent{\expandafter\aescandash\expandafter{#1}}},
  ]

  \foreach \myn [count=\myc from 1] in {this,that,another_text}
  {
    \node[my node,my node content=\myn] at (0,-\myc) {\aenodecontent};
  }

\end{tikzpicture}

\end{document}

请投票赞成 cfr 的解决方案,而不是赞成这个解决方案(这就是创建这个社区 wiki 的原因)。但我觉得我最终做的事情足够与众不同,值得发布我所采取的方法。

相关内容