%%% perfectcut.sty %%%%%%%%%%%%%%%%%%% %%% %%% Author: Guillaume Munch-Maccagnoni %%% http://guillaume.munch.name/ %%% %%% This work may be distributed and/or modified under the conditions of %%% the LaTeX Project Public License, either version 1.3 of this license %%% or (at your option) any later version. Refer to the README file. %%% %%% \ProvidesPackage{perfectcut}[06/23/2017 Perfect Cut v2.3] %%% Option processing \newif\ifcut@mathstyle@ \cut@mathstyle@false \newif\ifcut@realVert@ \cut@realVert@false \newif\ifcut@fixxits@ \cut@fixxits@false \DeclareOption{nomathstyle}{\cut@mathstyle@false}%backwards compat \DeclareOption{mathstyle}{\cut@mathstyle@true} \DeclareOption{realVert}{\cut@realVert@true} \DeclareOption{fixxits}{\cut@fixxits@true} \ProcessOptions* %%% End option processing \RequirePackage{graphicx} \RequirePackage{calc} \newmuskip\cutangleskip \newmuskip\cutbarskip \newmuskip\cutinterbarskip \newmuskip\cutangleouterskip \newmuskip\cutcasebarskip \newif\ifcutdebug %%% Exported commands %%See end of file for a more detailed description of the commands \newcommand{\perfectcut}[2]{\cut@{#1}{#2}}%% displays <#1||#2> \newcommand{\perfectbra}[1]{\cut@bra{#1}}%% displays <#1| \newcommand{\perfectket}[1]{\cut@ket{#1}}%% displays |#2> \let\cutprimitive\perfectcut%backward compat \let\cutbraprimitive\perfectbra%backward compat \let\cutketprimitive\perfectket%backward compat \newcommand{\perfectcase}[2]{\cut@case{#1}{#2}}%% displays [#1|#2] \newcommand{\perfectbrackets}[1]{\cut@brackets{#1}}%% displays [#1] \newcommand{\perfectparens}[1]{\cut@parens{#1}}%% displays (#1) \newcommand{\perfectunary}[4]{\cut@customUnary{#1}{#2}{#3}{#4}}%% displays %% #2#3#4 where #2 and #4 are delimiters. The size of the delimiters is %% computed according to #1 which must be one of IncreaseHeight, %% CurrentHeight, or CurrentHeightPlusOne. \newcommand{\perfectbinary}[6]{\cut@customBinary{#1}{#2}{#3}{#4}{#5}{#6}}%% %% displays #2#3#4#5#6 where #2, #4 and #6 are delimiters. The size of the %% delimiters is computed according to #1 which must be one of IncreaseHeight, %% CurrentHeight, or CurrentHeightPlusOne. %% The following variables can be redefined in your preamble \cutbarskip=5.0mu plus 8.0mu minus 2.0mu \cutangleskip=0.0mu plus 8mu minus 1.0mu \cutangleouterskip=0.0mu plus 8mu minus 0.0mu \cutinterbarskip=1.4mu plus 0mu minus 0mu \cutcasebarskip=3.0mu plus 5.0mu minus 1.5mu \cutdebugfalse%% print the size after each \rangle? %%% Various reimplementations of \left, \right and \middle. %% \nthleft{4}\langle ==> fourth size of \langle; begins at 0 \newcommand{\nthleft}[2]{\cut@nthldelim{#1}{#2}} \newcommand{\nthright}[2]{\cut@nthrdelim{#1}{#2}} \newcommand{\nthmiddle}[2]{\cut@nthmdelim{#1}{#2}} %% \matchleft{\big\langle}| ===> | of the same size as \big\langle obtained %% by resizing the closest glyph \newcommand{\matchleft}[2]{\cut@matchingldelim{#1}{#2}} \newcommand{\matchright}[2]{\cut@matchingrdelim{#1}{#2}} \newcommand{\matchmiddle}[2]{\cut@matchingmdelim{#1}{#2}} %% \lenleft{3mm}\langle ===> \langle of size at least 3mm %% (in math mode it is preferable to use math units such as 10mu,... %% however only regular units are implemented now.) \newcommand{\lenleft}[2]{\cut@lengthldelim{#1}#2} \newcommand{\lenright}[2]{\cut@lengthrdelim{#1}#2} \newcommand{\lenmiddle}[2]{\cut@lengthmdelim{#1}#2} %% \reallenleft{3mm}\langle ===> \langle of size 3mm by resizing the %% closest glyph \newcommand{\reallenleft}[2]{\cut@reallengthldelim{#1}{#2}} \newcommand{\reallenright}[2]{\cut@reallengthrdelim{#1}{#2}} \newcommand{\reallenmiddle}[2]{\cut@reallengthmdelim{#1}{#2}} %%% Preliminary commands %% setting up mathstyle \ifcut@mathstyle@ \RequirePackage{mathstyle} \def\cut@currentcutstyle{\currentmathstyle} \def\cut@setcurrentcutstyle{} \else \RequirePackage{scalerel} \def\cut@setcurrentcutstyle#1{{\ThisStyle{\let\ThisStyle\relax#1}}} \def\cut@currentcutstyle{\SavedStyle} \fi %% sets the behaviour of delimiters to always grow while evaluating #1 \newcommand{\cut@setshortfall}[1]{% \skip0=\delimitershortfall% \global\delimitershortfall=-0.1pt%that's the trick to get perfect growth \count0=\delimiterfactor% \global\delimiterfactor=901\relax% \hbox{$\m@th\cut@currentcutstyle#1$}% \global\delimitershortfall=\skip0% \global\delimiterfactor=\count0% } %% scale #2 to size #1 (length) \newcommand{\cut@resizetoheight}[2]{% \resizebox{!}{#1}{\hbox{$\m@th\cut@currentcutstyle#2$}}% } \newsavebox\cut@boxi \newsavebox\cut@boxj %% scale #2 to the size of #1. Assumes that #1 goes above and below the base line. \newcommand{\cut@resizetoheightof}[2]{% \sbox{\cut@boxi}{$\m@th\cut@currentcutstyle#1$}% \sbox{\cut@boxj}{$\m@th\cut@currentcutstyle#2$}% \raisebox{-\dp\cut@boxi}{% \resizebox{\width}{\ht\cut@boxi+\dp\cut@boxi}{% \raisebox{\dp\cut@boxj}{\usebox{\cut@boxj}}% }% }% } %% gives the delimiter #1 which is immediately bigger than #2 %% notice that \delimitershortfall is not modified so LaTeX can decide to give %% a smaller one. \newcommand{\cut@nextrdelim}[2]{\left.\hspace{-\nulldelimiterspace}\vphantom{#2}\right#1} \newcommand{\cut@nextldelim}[2]{\left#1\vphantom{#2}\hspace{-\nulldelimiterspace}\right.} \newcommand{\cut@nextmdelim}[2]{\left.\hspace{-\nulldelimiterspace}\middle#1\vphantom{#2}\hspace{-\nulldelimiterspace}\right.} %% like the previous one but resized to exactly match argument #1 %% used in order to have vertical bars of the perfect size \newcommand{\cut@matchingldelim}[2]{\mathopen{\cut@resizetoheightof{#1}{\cut@nextldelim{#2}{#1}}}} \newcommand{\cut@matchingrdelim}[2]{\mathclose{\cut@resizetoheightof{#1}{\cut@nextrdelim{#2}{#1}}}} \newcommand{\cut@matchingmdelim}[2]{\mathord{\cut@resizetoheightof{#1}{\cut@nextmdelim{#2}{#1}}}} %% gives the delimiter #2 which is immediately longer than #1 (length) \newcommand{\cut@lengthldelim}[2]{\mathopen{\cut@setcurrentcutstyle{\cut@setshortfall{\cut@nextldelim#2{\rule[-0.101pt]{0pt}{#1}}}}}} \newcommand{\cut@lengthrdelim}[2]{\mathclose{\cut@setcurrentcutstyle{\cut@setshortfall{\cut@nextrdelim#2{\rule[-0.101pt]{0pt}{#1}}}}}} \newcommand{\cut@lengthmdelim}[2]{\mathord{\cut@setcurrentcutstyle{\cut@setshortfall{\cut@nextmdelim#2{\rule[-0.101pt]{0pt}{#1}}}}}} %% like the previous one but resized to exactly match #1 (length) \newcommand{\cut@reallengthldelim}[2]{\mathopen{\cut@setcurrentcutstyle{\cut@resizetoheight{#1}{\cut@nextldelim#2{\rule[-0.101pt]{0pt}{#1}}}}}} \newcommand{\cut@reallengthrdelim}[2]{\mathclose{\cut@setcurrentcutstyle{\cut@resizetoheight{#1}{\cut@nextrdelim#2{\rule[-0.101pt]{0pt}{#1}}}}}} \newcommand{\cut@reallengthmdelim}[2]{\mathord{\cut@setcurrentcutstyle{\cut@resizetoheight{#1}{\cut@nextmdelim#2{\rule[-0.101pt]{0pt}{#1}}}}}} %I don't get anything about this bug which affects the %alignment with respect to the math axis \ifcut@fixxits@ \def\bugfix{} \else \def\bugfix{\cdot} \fi %% iterates #2 over itself #1 number of times \newcommand{\cut@iter}[2]{% \ifcase#1% #2{\bugfix} % 0 = smallest. This dot is here to prevent a % bug regarding vertical positioning. \else% \count0=#1% \advance\count0 -1\relax% \expandafter#2{\expandafter\cut@iter{\the\count0}#2}% \fi% } %% \cut@nthdelim{n}{delim}{f} iterates f{delim} n time over itself after %% resetting delimiter shortfall \newcommand{\cut@nthdelim}[3]{ \def\cut@tempnextdelim{#3{#2}}% \cut@setshortfall{\cut@iter{#1}\cut@tempnextdelim}% } %% \cut@nthxdelim gives the #1-th size of the delimiter #2 \newcommand{\cut@nthldelim}[2]{\mathopen{\cut@setcurrentcutstyle{\cut@nthdelim{#1}{#2}{\cut@nextldelim}}}} \newcommand{\cut@nthrdelim}[2]{\mathclose{\cut@setcurrentcutstyle{\cut@nthdelim{#1}{#2}{\cut@nextrdelim}}}} \newcommand{\cut@nthmdelim}[2]{\mathord{\cut@setcurrentcutstyle{\cut@nthdelim{#1}{#2}{\cut@nextmdelim}}}} %%%% now the main algorithm \newcounter{cut@depth} % lengths with names of the form \cut@height{depth} \newcommand{\cut@localheight}{cut@height\thecut@depth} \newcommand{\cut@newlocalheightcounter}{% \@ifundefined{c@\cut@localheight}{\newcounter{\cut@localheight}}{} } % boxes with names of the form \cut@savebox{num}@{depth} \newcommand{\cut@localsavebox}[1]{cut@savebox#1@\thecut@depth} \newcommand{\cut@newlocalsavebox}[1]{% \@ifundefined{\cut@localsavebox{#1}}{% \expandafter\newsavebox\csname\cut@localsavebox{#1}\endcsname% }{}% } \newcounter{cut@finalheight} \newsavebox\cut@boxleft \newsavebox\cut@boxright %%% Definition of Cut primitives %% Main loop. #1 determines how the height is incremented. #2 and #3 are saved %% in cut@boxleft and cut@boxright. Computed height is stored in cut@finalheight \newcommand{\cut@computeBinary@main}[3]{% \setcounter{cut@finalheight}{0}% {% \addtocounter{cut@depth}{1}% %defining variables \cut@newlocalheightcounter% \cut@newlocalsavebox{0}% \cut@newlocalsavebox{1}% %computing recursively \setcounter{\cut@localheight}{-1}% \expandafter\sbox\csname\cut@localsavebox{0}\endcsname% {$\m@th\cut@currentcutstyle#2$}% \expandafter\sbox\csname\cut@localsavebox{1}\endcsname% {$\m@th\cut@currentcutstyle#3$}% \addtocounter{\cut@localheight}{#1}% %exporting values outside the local scope \setcounter{cut@finalheight}{\value{\cut@localheight}}% \global\sbox\cut@boxleft% {\expandafter\usebox\csname\cut@localsavebox{0}\endcsname}% \global\sbox\cut@boxright% {\expandafter\usebox\csname\cut@localsavebox{1}\endcsname}% \addtocounter{cut@depth}{-1}% }% } %% Displays #1#2#3#4#5. Arguments #2 and #4 can contain other cut primitives. %% Calls to cut primitives inside #2 and #4 will have a smaller height. %% Arguments #1, #3 and #5 can access the current height in two different %% forms via \cut@n and \count0. \newcommand{\cut@computeBinary@IncreaseHeight}[5]{\cut@setcurrentcutstyle{% \cut@computeBinary@main{1}{#2}{#4}% \@ifundefined{c@\cut@localheight}{}{% if #2 and #4 did not contain any cut primitive \ifnum\value{cut@finalheight}>\value{\cut@localheight}% \setcounter{\cut@localheight}{\value{cut@finalheight}}% \fi% }%end @ifundefined \count0=\value{cut@finalheight}% \edef\cut@n{\expandafter\the\count0}% #1% \usebox{\cut@boxleft}% #3% \usebox{\cut@boxright}% #5% }} %% Displays #1#2#3#4#5. Arguments #2 and #4 can contain other cut primitives. %% Does not increase the current height computed by cut primitives inside #2 %% and #4. %% Arguments #1, #3 and #5 can access the current height in two different %% forms via \cut@n and \count0. \newcommand{\cut@computeBinary@CurrentHeight}[5]{\cut@setcurrentcutstyle{% \cut@computeBinary@main{0}{#2}{#4}% \ifnum\value{cut@finalheight}<0% \setcounter{cut@finalheight}{0}% \fi% \@ifundefined{c@\cut@localheight}{}{% if #2 and #4 did not contain any cut primitive \ifnum\value{cut@finalheight}>\value{\cut@localheight}% \setcounter{\cut@localheight}{\value{cut@finalheight}}% \fi% }%end @ifundefined \count0=\value{cut@finalheight}% \edef\cut@n{\expandafter\the\count0}% #1% \usebox{\cut@boxleft}% #3% \usebox{\cut@boxright}% #5% }} %% Displays #1#2#3#4#5. Arguments #2 and #4 can contain other cut primitives. %% Does not increase the current height computed by cut primitives inside #2 %% and #4 but the height to display is increased by 1. %% Arguments #1, #3 and #5 can access the height height in two different %% forms via \cut@n and \count0. \newcommand{\cut@computeBinary@CurrentHeightPlusOne}[5]{\cut@setcurrentcutstyle{% \cut@computeBinary@main{0}{#2}{#4} \@ifundefined{c@\cut@localheight}{}{% if #2 and #4 did not contain any cut primitive \ifnum\value{cut@finalheight}>\value{\cut@localheight}% \setcounter{\cut@localheight}{\value{cut@finalheight}}% \fi% }%end @ifundefined \count0=\value{cut@finalheight}% \advance\count0 1% \edef\cut@n{\expandafter\the\count0}% #1% \usebox{\cut@boxleft}% #3% \usebox{\cut@boxright}% #5% }} %%% Implementation of the particular delimiters %% special vertical bars %% \vert adjusted to #1 \newcommand{\cut@matchvert}[1]{% \setbox0=\hbox{$\matchmiddle{#1}\vert$}% \mkern.6mu% \kern -.5\wd0% \copy0% \kern -.5\wd0% \mkern.6mu% } %% special double vertical bars \newcommand{\cut@doublevert}[1]{% \cut@matchvert{\nthleft{#1}\langle} \mskip\cutinterbarskip% \penalty \the\binoppenalty\relax% \cut@matchvert{\nthleft{#1}\langle} } %% special double vertical bars (alternate) \newcommand{\cut@Vert}[1]{% \setbox0=\hbox{$\matchmiddle{\nthleft{#1}\langle}\Vert$}% \mkern.8mu% \kern -.3\wd0% \copy0% \kern -.3\wd0% \mkern.8mu% %\mkern-3.26mu% %\matchmiddle{\nthleft{#1}\langle}\Vert% %\mkern-3.26mu% } %% setting up realVert \ifcut@realVert@ \let\cut@bars\cut@Vert \else \let\cut@bars\cut@doublevert \fi %% \perfectcut %% <#1||#2>, increases height, inserts skips \newcommand{\cut@}[2]{% \cut@computeBinary@IncreaseHeight% {\mskip\cutangleouterskip% \nthleft{\cut@n}{\langle}% \mskip\cutangleskip}% {#1}% {\mskip\cutbarskip% \cut@bars{\cut@n}% \mskip\cutbarskip}% {#2}% {\mskip\cutangleskip% \nthright{\cut@n}{\rangle}% \mskip\cutangleouterskip}% } %% \perfectbra %% <#1|, increases height, inserts skips \newcommand{\cut@bra}[1]{% \cut@computeBinary@IncreaseHeight% {\mskip\cutangleouterskip% \nthleft{\cut@n}{\langle}% \mskip\cutangleskip}% {#1}% {\mskip\cutbarskip% \cut@matchvert{\nthleft{\cut@n}\langle}% \mskip\cutangleouterskip}% {}{}%only one argument } %% \perfectket %% |#1>, increases height, inserts skips \newcommand{\cut@ket}[1]{% \cut@computeBinary@IncreaseHeight% {\mskip\cutangleouterskip% \cut@matchvert{\nthleft{\cut@n}\langle}% \mskip\cutbarskip}% {#1}% {\mskip\cutangleskip% \nthright{\cut@n}{\rangle}% \mskip\cutangleouterskip}% {}{}%only one argument } %% \perfectcase %% [#1|#2], height is current height plus one, inserts skips \newcommand{\cut@case}[2]{% \cut@computeBinary@CurrentHeightPlusOne% {\nthleft{\cut@n}[% \mskip\cutangleskip}% {#1}% {\mskip\cutcasebarskip% \cut@matchvert{\nthleft{\cut@n}[}% \mskip\cutcasebarskip}% {#2}% {\mskip\cutangleskip% \nthright{\cut@n}]}% } %% \perfectbrackets %% [#1], height is current height plus one, inserts skips only inside \newcommand{\cut@brackets}[1]{% \cut@computeBinary@CurrentHeightPlusOne% {\nthleft{\cut@n}[% \mskip\cutangleskip}% {#1}% {\mskip\cutangleskip% \nthright{\cut@n}]}% {}{}%only one argument } %% \perfectparens %% (#1), height is current height, inserts skips only inside \newcommand{\cut@parens}[1]{% \cut@computeBinary@CurrentHeight% {\nthleft{\cut@n}(% \mskip\cutangleskip}% {#1}% {\mskip\cutangleskip% \nthright{\cut@n})}% {}{}%only one argument } %% \perfectunary %% #2#4#3 where #2 and #3 are delimiters. The size of the delimiters is computed %% according to #1 which must be one of IncreaseHeight, CurrentHeight, %% or CurrentHeightPlusOne. \newcommand{\cut@customUnary}[4]{% \csname cut@computeBinary@#1\endcsname% {\nthleft{\cut@n}#2}% {#4}% {\nthright{\cut@n}#3}% {}{}% }% %% \perfectbinary %% #2#5#3#6#4 where #2, #3 and #4 are delimiters. The size of the delimiters is %% computed according to #1 which must be one of IncreaseHeight, CurrentHeight, %% or CurrentHeightPlusOne. \newcommand{\cut@customBinary}[6]{% \csname cut@computeBinary@#1\endcsname% {\nthleft{\cut@n}#2}% {#5}% {\matchmiddle{\nthleft{\cut@n}#2}#3}%{{\nthmiddle{\cut@n}#4}}% {#6}% {\nthright{\cut@n}#4}% }% %% Example: The following displays a set {#1|#2} with delimiters of the %% appropriate size if there are \perfectcommands inside #1 and #2. %% \def\Set#1#2{\perfectbinary{IncreaseHeight}\{|\}{#1\mathrel{}}{\mathrel{}#2}} %%% for testing purposes \newcommand{\cut@testsize}[2]{ {#1 \[ \mathrm{\f@size\,pt:} \begin{array}{l} \scriptscriptstyle{#2}\\ \scriptstyle{#2}\\ \textstyle{#2} \end{array}\]} } \newcommand{\cut@test}[1]{% \cut@testsize{\Large}{#1}% \cut@testsize{\large}{#1}% \cut@testsize{}{#1}% \cut@testsize{\small}{#1}% \cut@testsize{\footnotesize}{#1}% \cut@testsize{\scriptsize}{#1}% \cut@testsize{\tiny}{#1}% } \newcommand{\cut@testangles}{\cut@test{% \cut@{\cut@{\cut@{\cut@{\cut@{a}{b}}{c}}{d}}{e}}{f}}% }% \newcommand{\cut@testverts}{ \def\line{\rule[-3ex]{0.5em}{3ex}}% \def\v##1{\cut@doublevert{##1}\line} \def\V##1{\cut@Vert{##1}\line} \cut@test{% \line\vert\line\v{0}\v{1}\v{2}\v{3}\v{4}\v{5} \line\Vert\line\V{0}\V{1}\V{2}\V{3}\V{4}\V{5} }}%