6

I suspect that arithmetic I put inside newcommand is not processed as I would expect.

What is the value of \FMARGIN after declaring it as

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

Is it 12mm or something else?

Because when I use it inside coordinate arithmetic I get unexpected results. Not all margins are same distance away from the bounding box.

Complete code:

\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}

Output:

output

Difference becomes more obvious when \FWIDTH is set to a higher value (like 5mm).

TeXnician
  • 33,589
bp2017
  • 3,756
  • 1
  • 13
  • 33
  • 3
    Try using package calc and then use \newlength\FMARGIN \setlength\FMARGIN{2mm} ... \setlength\FBWIDTH{10mm+\FMARGIN} or whatever. Or use the PGF maths stuff \pgfmathsetlength ... or even \pgfmathsetmacro .... But PGF maths is slow. – cfr Sep 07 '17 at 01:02
  • I would answer, probably, but the question is 'what does it give?' and @ShreevatsaR already answered that. I'm somewhat surprised you don't get an error. – cfr Sep 07 '17 at 01:05
  • 2
    Note that you can always use \show\FMARGIN to find out for yourself. – cfr Sep 07 '17 at 01:07
  • @ShreevatsaR You should answer, since you actually answered the question and I can't fit my non-answer into a comment. – cfr Sep 07 '17 at 01:17
  • 2
    \newcommand{\FMARGIN}{\dimexpr10mm+\FWIDTH\relax} – Steven B. Segletes Sep 07 '17 at 01:35

4 Answers4

9

What was originally used in the question, namely

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

stores as \FMARGIN the tokens in the command. Substitution and evaluation is not made until it is used. However, when TeX is looking for a length, something like 10mm+2mm will not do. Rather, the command \dimexpr10mm+2mm\relax is needed to perform the addition of lengths and provide the result as a single length which can be used by the macro seeking a length input.

\dimexpr stands for "dimensional expression" (the dimension being length) and \relax terminates the evaluation of a dimensional expression.

If one needs the result in textual form, that is, the tokens that are equivalent to a length of 12mm, then one uses \the\dimexpr10mm+2mm\relax, whereas without the \the, the result is stored as an internal length and not a series of alphanumeric tokens.

A good way to see what is happening is comparing this code:

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

to this code

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

The former evaluates to 10mm+2mm while the latter evaluates to 34.14328pt. Here is the 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}

enter image description here

8

When defining macros (such as with \newcommand), TeX does no arithmetic: you can think of it as simple substitution of strings (or to be more precise, tokens). So when you write:

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

no arithmetic happens in 10mm+\FWIDTH. The result is that \FMARGIN gets defined as a macro which expands to 10mm+\FWIDTH (and this has no special arithmetical meaning; it's simply a sequence of five tokens 1, 0, m, m, +, followed by the token \FWIDTH). And when you use it somewhere as \FMARGIN, under typical circumstances it will (ultimately) expand to the string 10mm+2mm, which again is simply a sequence of 8 characters: it's as if you had typed 10mm+2mm at that place.

So when you use:

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

it's as if you had typed

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

and although this happens to compile, it is clearly not what you want. Macros work this way because TeX was initially designed for typesetting, and macros were supposed to be a shortcut for saving typing, not for doing computation.

You can debug this for yourself (see what has \FMARGIN been defined as) by adding \show\FMARGIN) into the file:

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

when TeX reaches that point, it will show you the expansion and wait for you to hit Return before continuing:

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

? 

Reading this requires some training, but what the above tells you is that \FMARGIN is defined as 10mm+\FWIDTH (not as 12mm).

So how do you fix this? In this case, because mm is a dimension that TeX happens to understand (but only when it's looking for a length, not when simply defining macros and such), there are some tricks you can use:

  • you can define \FMARGIN as a length, using \setlength, as in @cfr's answer

  • you can define \FMARGIN to expand to something that will result in 12mm when TeX is looking for a length, as in @Steven's answer

  • you can implement your original intention of somehow defining \FMARGIN to expand to 12mm, though it is slightly tricky (again, see Steven's answer).

ShreevatsaR
  • 45,428
  • 10
  • 117
  • 149
5

This isn't an answer, but too long for a comment. It doesn't answer the question asked, that is. It does address a potential follow-up of how to avoid the problem.

Macros expand to their replacement text. For handling dimensions, it is easiest to use dimensions/lengths. For handling integers, it is easiest to use counts/counters.

\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}

variable margins

cfr
  • 198,882
  • I noticed both of these triangles are still a little (few pixels) off margin at the apex. Which means there is something else to consider. But it's a minor issue in comparison with what I had before. Using \setlength works (although not perfectly). It may be that the bounding box behaving not as expected. – bp2017 Sep 07 '17 at 02:12
  • @bp2017 The maths is not very precise in TikZ. Are you sure it is not just the expected level of imprecision? – cfr Sep 07 '17 at 02:31
3

I found \pgfmathsetlengthmacro to be the easiest solution.

All I did was replace \newcommand in \newcommand{\FWIDTH}{2mm} \newcommand{\FMARGIN}{10mm+\FWIDTH}

with \pgfmathsetlengthmacro to get \pgfmathsetlengthmacro{\FWIDTH}{2mm} \pgfmathsetlengthmacro{\FMARGIN}{10mm+\FWIDTH}

Now coordinate arithmetic calculates \FMARGIN correctly.

bp2017
  • 3,756
  • 1
  • 13
  • 33
  • 2
    You should be aware, if not already, of one additional distinction between a "length" and a "macro". A length, once set, is fixed, whereas a macro is evaluated at time of use. Thus, as a length, \FMARGIN will be locked in at 12mm, even if \FWIDTH later changes. As a \newcommand macro, \FMARGIN will change automatically, in the event that \FWIDTH is changed. See https://tex.stackexchange.com/questions/123443/defining-a-length-that-scales-with-fontsize-changes – Steven B. Segletes Sep 07 '17 at 17:37