如何将“newcommand”值声明为某个数字(带单位)和另一个命令的总和,以便它可以用于坐标算术?

如何将“newcommand”值声明为某个数字(带单位)和另一个命令的总和,以便它可以用于坐标算术?

我怀疑我输入的算术newcommand没有按照我预期的方式处理。

\FMARGIN声明为后的值是什么

\newcommand{\FWIDTH}{2mm}
\newcommand{\FMARGIN}{10mm+\FWIDTH}

是12mm还是其他的?

因为当我在坐标算法中使用它时,我得到了意想不到的结果。并非所有边距与边界框的距离都相同。

完整代码:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary
    {%
        calc,
        shapes.geometric
    }
\newcommand{\FWIDTH}{2mm}
\newcommand{\FMARGIN}{10mm+\FWIDTH}
\begin{document}
    \begin{tikzpicture}
        \path node
            [%
                isosceles triangle,
                draw=blue,
                minimum width=30mm,
                line width=1mm
            ]   {};
        \path
            [%
                draw=red,
                line width=1mm
            ]
            ($(current bounding box.north west)+(-\FMARGIN,\FMARGIN)$)
            rectangle
            ($(current bounding box.south east)+(\FMARGIN,-\FMARGIN)$);
    \end{tikzpicture}
\end{document}

输出:

输出

\FWIDTH当设置为更高的值(例如 5 毫米)时,差异变得更加明显。

答案1

问题中最初使用的内容是

\newcommand{\FMARGIN}{10mm+\FWIDTH}

存储为\FMARGIN命令中的标记。直到使用它时才会进行替换和评估。但是,当 TeX 正在寻找长度时,类似这样的操作10mm+2mm将不起作用。相反,\dimexpr10mm+2mm\relax需要该命令来执行长度的添加并将结果作为单个长度提供,该长度可供寻求长度输入的宏使用。

\dimexpr代表“维度表达式”(维度为长度)并\relax终止维度表达式的计算。

如果需要文本形式的结果,即相当于长度的标记12mm,则使用\the\dimexpr10mm+2mm\relax,而如果没有\the,则结果将存储为内部长度而不是一系列字母数字标记。

了解正在发生的事情的一个好方法是比较以下代码:

\edef\tmp{10mm+\FWIDTH}
\detokenize\expandafter{\tmp}

到此代码

\edef\tmp{\the\dimexpr10mm+\FWIDTH\relax}
\detokenize\expandafter{\tmp}

前者计算结果为 ,10mm+2mm而后者计算结果为34.14328pt。以下是 MWE。

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary
    {%
        calc,
        shapes.geometric
    }
\newcommand{\FWIDTH}{2mm}
\newcommand{\FMARGIN}{\dimexpr10mm+\FWIDTH\relax}
\begin{document}
    \begin{tikzpicture}
        \path node
            [%
                isosceles triangle,
                draw=blue,
                minimum width=30mm,
                line width=1mm
            ]   {};
        \path
            [%
                draw=red,
                line width=1mm
            ]
            ($(current bounding box.north west)+(-\FMARGIN,\FMARGIN)$)
            rectangle
            ($(current bounding box.south east)+(\FMARGIN,-\FMARGIN)$);
    \end{tikzpicture}
\end{document}

在此处输入图片描述

答案2

当定义宏(例如\newcommand)时,TeX 不进行任何算术运算:你可以将其视为字符串(或更准确地说,标记)的简单替换。因此,当你写:

\newcommand{\FWIDTH}{2mm}
\newcommand{\FMARGIN}{10mm+\FWIDTH}

在 中不发生任何算术运算10mm+\FWIDTH。结果是\FMARGIN被定义为一个宏,它扩展为10mm+\FWIDTH(这没有特殊的算术含义;它只是五个标记10mm、的序列+,后跟标记\FWIDTH)。当你在某处将它用作 时\FMARGIN,在典型情况下,它将(最终)扩展为字符串10mm+2mm,这又只是一个 8 个字符的序列:就像你10mm+2mm在那个地方输入一样。

因此当你使用:

        ($(current bounding box.north west)+(-\FMARGIN,\FMARGIN)$)
        rectangle
        ($(current bounding box.south east)+(\FMARGIN,-\FMARGIN)$);

就像你输入了

        ($(current bounding box.north west)+(-10mm+2mm,10mm+2mm)$)
        rectangle
        ($(current bounding box.south east)+(10mm+2mm,-10mm+2mm)$);

虽然这恰好可以通过编译,但这显然不是你想要的。宏之所以以这种方式工作,是因为 TeX 最初是为排版而设计的,而宏应该是节省打字的捷径,而不是进行计算。

您可以通过在文件中添加 )来自己调试这个问题(查看已\FMARGIN定义的内容) :\show\FMARGIN

\newcommand{\FWIDTH}{2mm}
\newcommand{\FMARGIN}{10mm+\FWIDTH}
\show\FMARGIN

当 TeX 到达该点时,它将向您显示扩展并等待您按下 Return 键后再继续:

> \FMARGIN=\long macro:
->10mm+\FWIDTH .
l.10 \show\FMARGIN

? 

阅读本文需要一些训练,但上面的内容告诉您,\FMARGIN定义为10mm+\FWIDTH(而不是12mm)。

那么如何解决这个问题呢?在这种情况下,因为mm是 TeX 恰好理解的一个维度(但只有当它是看着对于长度,而不是简单地定义宏等),您可以使用一些技巧:

  • 你可以定义\FMARGIN长度,使用\setlength,如@cfr 的回答

  • 你可以定义\FMARGIN扩展为12mm当 TeX 寻找长度时会导致的结果,例如@Steven 的回答

  • 您可以实现您的初衷,即以某种方式定义\FMARGIN扩展为12mm,尽管这有点棘手(再次参见史蒂文的回答)。

答案3

这不是一个答案,但对于评论来说太长了。也就是说,它没有回答提出的问题。它确实解决了如何避免该问题的潜在后续问题。

宏扩展为替换文本。对于处理尺寸,最简单的方法是使用尺寸/长度。对于处理整数,最简单的方法是使用计数/计数器。

\documentclass{standalone}
\usepackage{tikz,calc}
\usetikzlibrary{calc,shapes.geometric}
\newlength\FMARGIN
\newlength\FWIDTH
\tikzset{%
  fwidth/.code={\setlength\FWIDTH{#1}},
  fmargin/.code={\setlength\FMARGIN{10mm+#1}},
  fwidth/.forward to=/tikz/fmargin,
  fwidth=2mm,
}
\begin{document}
\begin{tikzpicture}
  \path node [isosceles triangle, draw=blue, minimum width=30mm, line width=1mm ]   {};
  \path [draw=red, line width=1mm ] ($(current bounding box.north west)+(-\FMARGIN,\FMARGIN)$) rectangle ($(current bounding box.south east)+(\FMARGIN,-\FMARGIN)$);
\end{tikzpicture}
\begin{tikzpicture}[fwidth=20mm]
  \path node [isosceles triangle, draw=blue, minimum width=30mm, line width=1mm ]   {};
  \path [draw=red, line width=1mm ] ($(current bounding box.north west)+(-\FMARGIN,\FMARGIN)$) rectangle ($(current bounding box.south east)+(\FMARGIN,-\FMARGIN)$);
\end{tikzpicture}
\end{document}

可变边距

答案4

我发现\pgfmathsetlengthmacro这是最简单的解决方案。

我所做的就是\newcommand替换 \newcommand{\FWIDTH}{2mm} \newcommand{\FMARGIN}{10mm+\FWIDTH}

\pgfmathsetlengthmacro得到 \pgfmathsetlengthmacro{\FWIDTH}{2mm} \pgfmathsetlengthmacro{\FMARGIN}{10mm+\FWIDTH}

现在坐标算术计算\FMARGIN正确。

相关内容