5

So a while ago I asked this question on doubled delimiters. I found this piece of code and adapted it into the following:

\documentclass{article}

\usepackage{mathtools}

\DeclarePairedDelimiterX{\dbrackets}[1]{\lbrack}{\rbrack}{
    \nhphantom{$\delimsize\lbrack$} \delimsize\lbrack \mathopen{} #1 \mathclose{} \delimsize\rbrack \nhphantom{$\delimsize\rbrack$}
}
\DeclarePairedDelimiterX{\dbraces}[1]{\lbrace}{\rbrace}{
    \nhphantom{$\delimsize\lbrace$} \delimsize\lbrace \mathopen{} #1 \mathclose{} \delimsize\rbrace \nhphantom{$\delimsize\rbrace$}
}
\DeclarePairedDelimiterX{\dparens}[1]{\lparen}{\rparen}{
    \nhphantom{$\delimsize\lparen$} \delimsize\lparen \mathopen{} #1 \mathclose{} \delimsize\rparen \nhphantom{$\delimsize\rparen$}
}


\newcommand{\nhphantom}[1]{\sbox0{#1}\hspace{-0.751\dimexpr\the\wd0 \relax}}


\begin{document}

$\dbrackets[\big]{\frac12}$
$\dbraces[\big]{\frac12}$
$\dparens[\big]{\frac12}$

%These don't work:

%$\dbrackets*{\frac12}$
%$\dbraces*{\frac12}$
%$\dparens*{\frac12}$

\end{document}

It does exactly what I wanted, but I don't really understand the code and there are many macros I have never seen. So I would really appreciate if someone could explain to me what is going on here:

  • What is the intention of the \nhphantom macro?
  • What does \sbox0 do?
  • What does \dimexpr\the\wd0 do?

I would also like to understand why the starred version of the mathtools commands don't work.

  • Just to start, -0.751\dimexpr\the\wd0\relax can be, much more simply, -0.751\wd0 (a fraction of the width of the current contents of \box0). – egreg Apr 13 '18 at 13:21

2 Answers2

7

TeX has something called a box, which is what it uses to organize things in the document. Knuth's TeX has 256 box registers for you to use. Think of that as 256 actual boxes available for TeX to store things and then use them in the document.

The command \sbox0{#1} puts the contents of #1 in the box register n° 0.

Now that the box register 0 has #1 you can do things with this box, like measure its width with \wd, height with \ht, and depth with \dp, so \wd0 returns the with of the contents of the box 0.

So, breaking apart the \nhphantom macro:

% Store the contents of #1 into box register 0
\sbox0{#1}%
% Go back -0.751 times the \wd of the box register 0
\hspace{-0.751\dimexpr\the\wd0 \relax}

Also, as egreg said in the comments, -0.751\dimexpr\the\wd0 is redundant. The \the will get the text representation of \wd0, then the \dimexpr will read this text back into a number. You can skip this back-and-forth with -0.751\wd0, so you can simplify to:

\newcommand{\nhphantom}[1]{\sbox0{#1}\hspace{-0.751\wd0}}

This register principle is what makes up TeX's memory. TeX has:

  • \count registers to hold counters (integers);
  • \dimen registers to hold lengths;
  • \skip registers to hold glue (one dimen plus another minus one more);
  • \muskip registers to hold muglue (for maths);
  • \box registers to hold boxes; and
  • \toks registers to hold lists of tokens.

As for your question about the starred versions (which I saw only now, sorry)...

The \sbox0{#1} in the \nhphantom macro saves the contents of #1 in an \hbox, which is not in math mode, thus you have to call \nhphantom with $...$ to enforce math mode. But this creates a math mode in text mode in math mode thing, which makes TeX "forget" about the rest of the equation it is in.

When you use a starred version of a delimiter created by \DeclarePairedDelimiterX, you make the outer delimiters be \left<delim> and \right<delim>, and the \delimsize becomes \middle to be used safely between \left and \right. The problem is that the math-in-text-in-math thing makes TeX forget about the surrounding \left and \right and the argument of \nhphantom is $\middle\lbrack$, which is invalid because you cannot use \middle without an enclosing \left...\right.

To workaround this issue I changed the \nhphantom macro a little. It now has two arguments: the delimiter and the content in between. The \nhphantom macro then measures the width of \left<delim>\vphantom{#1}\right., which is valid.

Here is the complete working code:

\documentclass{article}

\usepackage{mathtools}

\DeclarePairedDelimiterX{\dbrackets}[1]{\lbrack}{\rbrack}{
    \nhphantom{\lbrack}{#1} \delimsize\lbrack \mathopen{} #1 \mathclose{} \delimsize\rbrack \nhphantom{\rbrack}{#1}
}
\DeclarePairedDelimiterX{\dbraces}[1]{\lbrace}{\rbrace}{
    \nhphantom{\lbrace}{#1} \delimsize\lbrace \mathopen{} #1 \mathclose{} \delimsize\rbrace \nhphantom{\rbrace}{#1}
}
\DeclarePairedDelimiterX{\dparens}[1]{\lparen}{\rparen}{
    \nhphantom{\lparen}{#1} \delimsize\lparen \mathopen{} #1 \mathclose{} \delimsize\rparen \nhphantom{\rparen}{#1}
}

\newcommand{\nhphantom}[2]{\sbox0{$\left#1\vphantom{#2}\right.$}\hspace{-0.58\wd0}}

\begin{document}

$\dbrackets[\big]{\frac12}$
$\dbraces[\big]{\frac12}$
$\dparens[\big]{\frac12}$

%These do work :)

$\dbrackets*{\frac12}$
$\dbraces*{\frac12}$
$\dparens*{\frac12}$

\end{document}

enter image description here

  • 3
    \the is redundant, because \wd0 is a dimension. But then also \dimexpr and \relax are. – egreg Apr 13 '18 at 13:25
0

In the accepted answer, the 0.58 is an ad-hoc value, fit by hand to look good. But a multiplicative scaling is wrong here; 0.58 does not work for \Bigg, nor does it work for inline math of default size. Worse, it behaves differently for the starred and non-starred versions of the commands, because measuring auto-resizing \sbox does not help if the command was sent a specific size.

That said, Phelype's solution does get us most of the way there. I recommend the following minor alteration to the \nhphantom command:

\newcommand{\nhphantom}[2]{\sbox0{\kern-2\nulldelimiterspace
      $\left.\delimsize#1\vphantom{#2}\right.$}\hspace{-.97\wd0}}

the \kearn-2\nulldelimeterspace eliminates extra (measured!) space of the \left. and the \right., while the \delimsize expands to the appropriate size, (or to \middle), in the context of the \DeclarePairedDelimieterX macro. The number -.97 may as well be replaced by -1, but I find that a (small) amount of multiplicative scaling is still aesthetically pleasing.

Now that the spacing is exact, we must now include a small additive space (below, 1.2pt), which does scale propery, even for very large braces. In the double bracket instance,

\DeclarePairedDelimiterX{\dbrackets}[1]{\lbrack}{\rbrack}
        {\nhphantom{\lbrack}{#1}\hspace{1.2pt}\delimsize\lbrack\mathopen{}#1\mathclose{}\delimsize\rbrack\hspace{1.2pt}\nhphantom{\rbrack}{#1}}

BONUS: here's a simple automation to define variants:

% same code as before
\newcommand{\nhphantom}[2]{\sbox0{\kern-2\nulldelimiterspace$\left.\delimsize#1\vphantom{#2}\right.$}\hspace{-.97\wd0}}

% very minor wrapper around what we had before \newcommand\DeclareDoubleDelim[5]{ \DeclarePairedDelimiterX{#1}[1]{#2}{#5} {\nhphantom{#3}{##1}\hspace{1.2pt}\delimsize#3\mathopen{}##1\mathclose{}\delimsize#4\hspace{1.2pt}\nhphantom{#4}{##1}} }

% examples \DeclareDoubleDelim \dbrackets [[]] \DeclareDoubleDelim \dblbraces {{}} \DeclareDoubleDelim \dblparens (()) \DeclareDoubleDelim \dblangles \langle\langle\rangle\rangle

[ \dbrackets{\frac12} \quad \dblangles{\frac12} \qquad \dbrackets[\Bigg]{\frac12} \quad \dblangles[\Bigg]{\frac12} \qquad \dbrackets{\nicefrac12} \quad \dblangles{\nicefrac12} ]

first and last

oricha
  • 321