Skip to content

Instantly share code, notes, and snippets.

@sgimenez
Created July 22, 2011 17:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sgimenez/1099872 to your computer and use it in GitHub Desktop.
Save sgimenez/1099872 to your computer and use it in GitHub Desktop.
Introducing Alternatives in TeX displays
% Introducing Alternatives in TeX displays
\documentclass{article}
\usepackage{xcolor} % for debug printing
\usepackage{graphicx} %
\usepackage{multicol} % for demo
%%%%% pseudo-random generator
%
\catcode`\@=11
\newcount\randomi % the random number seed (while executing)
\global\randomi=1
\countdef\count@ii=2
\def\nextrandom{\begingroup
\count@ii\randomi
\divide\count@ii 127773 % modulus = multiplier * 127773 + 2836
\count@\count@ii
\multiply\count@ii 127773
\global\advance\randomi-\count@ii % random mod 127773
\global\multiply\randomi 16807
\multiply\count@ 2836
\global\advance\randomi-\count@
\ifnum\randomi<\z@ \global\advance\randomi 2147483647\relax\fi
\endgroup}
\catcode`\@=12
%
%%%%%
%%%%% unvbox centering, thanks to egreg!
%
\catcode`\@=11
\newif\ifboxended
\def\centerunvbox#1{\begingroup
\global\setbox\z@=\box#1\setbox\@ne=\box\voidb@x
\global\boxendedfalse
\relax\docentervbox}
\def\docentervbox{%
\setbox\z@=\vbox{\unvbox\z@
\ifcase\lastnodetype
% char node (can't remove)
\or
% hlist node
\setbox\tw@=\lastbox
\def\next{%
\global\setbox\@ne=\vbox{
\hbox to\hsize{\hfil\box\tw@\hfil}\unvbox\@ne}}%
\or
% vlist node
\setbox\tw@=\lastbox
\def\next{%
\global\setbox\@ne=\vbox{
\hbox to\hsize{\hfil\box\tw@\hfil}\unvbox\@ne}}%
\or\or\or\or
% rule, ins, mark, adjust node (can't remove)
\or\or
% ligature, disc node (can't happen)
\or\or
% whatsit, math node (can't remove)
\or
% glue node
\skip@=\lastskip\unskip
\def\next{\global\setbox\@ne=\vbox{\vskip\skip@\unvbox\@ne}}%
\or
% kern node
\dimen@=\lastkern\unkern
\def\next{\global\setbox\@ne=\vbox{\kern\dimen@\unvbox\@ne}}%
\or
% penalty node
\count@=\lastpenalty\unpenalty
\def\next{\global\setbox\@ne=\vbox{\penalty\count@\unvbox\@ne}}%
\or
% unset node (can't happen)
\or
% math mode node (can't remove)
\else
% empty list
\def\next{\global\boxendedtrue}
\fi
\next}
\ifboxended
\def\next{\unvbox\@ne\endgroup}
\else
\let\next\docentervbox
\fi
\next}
\catcode`\@=12
%
%%%%%
%%%%% display engine
%
\newcount\dtmppenalty
\def\dpenalty#1{\global\advance\dtmppenalty#1}
\def\solvebox#1#2#3{\begingroup
\dimendef\hlimit=1 \dimendef\vlimit=2
\countdef\hfactor=3 \countdef\vfactor=4
\hlimit=\hsize \vlimit=0pt
\hfactor=1 \vfactor=1
#2%
\setbox2\vbox{\hbox{(unsolved display)}}% best result
\count2=15000% counter for best result penalty
%
\count1=0% number of iterations
\loop
%
\global\dtmppenalty=0
\setbox0\vbox{#3}%
%
\dimen0=\wd0
\ifnum\hlimit=0\else
\ifnum \dimen0>\hlimit \advance\dtmppenalty 5000\fi\fi
\multiply\dimen0\hfactor \divide\dimen0 100000 \advance\dtmppenalty\dimen0
\dimen0=\ht0 \advance\dimen0\dp0
\ifnum\vlimit=0\else
\ifnum \dimen0>\vlimit \advance\dtmppenalty 5000\fi\fi
\multiply\dimen0\vfactor \divide\dimen0 100000 \advance\dtmppenalty\dimen0
\advance\dtmppenalty 0
\ifnum \dtmppenalty<\count2
%
% debug
%
%% \debugsolve
%
\setbox2\box0
\count2=\dtmppenalty
\fi
\advance\count1 by 1
\ifnum \count1<100 \repeat
\global\setbox#1\box2
\endgroup
}
\def\solve#1#2{\hbox{\solvebox0{\hfactor5\vfactor20 #1}{#2}\box0}}
\def\csolve#1#2{\vcenter{\solvebox0{\hfactor5\vfactor20 #1}{#2}\box0}}
\def\debugsolve{\par\moveright\hsize\hbox{%
\scalebox{0.5}{\vbox{\color{gray}\vskip 3pt\hbox to \hsize{%
\hfill[\the\count1: \the\wd0--{\bfseries\the\dtmppenalty}]\hfill}%
\hrule\hbox{\vrule\vbox{\vskip\abovedisplayskip
\hbox to \hsize{\hfill\copy0\hfill}%
\vskip\belowdisplayskip}}}}}}%
\def\disp#1{\par
\penalty\predisplaypenalty
\vskip\abovedisplayskip
\solvebox0{\hlimit\hsize\hfactor0\vfactor5}{#1}\centerunvbox0
\vskip\belowdisplayskip
\penalty\postdisplaypenalty
}
\def\display#1\end{\disp{#1}}
%
%%%%%
%%%%% display choice
%
\long\def\choice#1#2{\nextrandom\ifnum\randomi<1073741823 #1\else #2\fi}
%
%%%%%
%%%%% display primitives
%
\newdimen\dhskip\dhskip=35pt
\newdimen\dhsskip\dhsskip=10pt
\newdimen\dvskip\dvskip=8pt
\def\decrdvskip{\dvskip=0.5\dvskip}
\def\decrdhskip{\dhskip=0.5\dhskip}
\def\dds{\decrdvskip\decrdhskip} %decrements spacing
\def\vscr#1{\cr\noalign{\vskip #1}}
\def\vscrpen#1{\cr\noalign{\vskip #1}}
\def\hscr#1{\cr\noalign{\hskip #1}}
\def\alignH#1#2{\hbox{\valign{\vfil##\vfil\cr{#1}\hscr\dhsskip{#2}\cr}}}
\def\twoH#1#2{\hbox{\valign{\vfil##\vfil\cr{#1}\hscr\dhskip{#2}\cr}}}
\def\twoV#1#2{\everycr{\noalign{\penalty\interdisplaylinepenalty}}%
\halign{\hfil##\hfil\cr\vbox{#1}\vscr\dvskip\vbox{#2}\cr}}
\def\twoC#1#2{\choice
{\twoH{#1}{#2}}%
{\dpenalty{50}\twoV{#1}{#2}}}
\def\threeC#1#2#3{\choice
{\hbox{\valign{\vfil##\vfil\cr#1\hscr\dhskip{#2}\hscr\dhskip{#3}\cr}}}%
{\choice
{\dpenalty{25}\everycr{\noalign{\penalty\interdisplaylinepenalty}}%
\halign{\hfil##\hfil\cr
\hbox{#1}\vscr\dvskip\hbox{#2}\vscr\dvskip\hbox{#3}\cr}}%
{\dpenalty{100}\choice{\twoC{#1}{\twoC{#2}{#3}}}{\twoC{\twoC{#1}{#2}}{#3}}}%
}%
}
\def\rel#1#2#3#4{\choice
{\hbox{\valign{\vfil##\vfil\cr{#3}\hscr\dhsskip{#1}\hscr\dhsskip{#4}\cr}}}%
{\choice
{\dpenalty{25}\everycr{\noalign{\penalty\interdisplaylinepenalty}}%
\halign{\hfil##\hfil\cr
\vbox{#3}\vscr\dvskip\noalign{\penalty 50}\vbox{#2}\vscr\dvskip
\vbox{#4}\cr}}%
{\dpenalty{500}\choice
{\twoV{#3}{\alignH{#1}{#4}}}%
{\twoV{\alignH{#3}{#1}}{#4}}}%
}%
}
\def\two#1#2{\twoC{{\dds#1}}{{\dds#2}}}
\def\three#1#2#3{\threeC{{\dds#1}}{{\dds#2}}{{\dds#3}}}
%
%%%%%
%text box with specified width:
\def\tb#1#2{\hbox{\fbox{\hbox to #1{\hss\strut#2\hss}}}}
%text box with specified width and height:
\def\vtb#1#2#3{\hbox{\fbox{\vbox to #2{\vss\hbox to #1{\hss\strut#3\hss}\vss}}}}
%simple math box
\def\mb#1{\hbox{\ensuremath{\mathsurround=0pt\displaystyle #1}}}
% sectioning
\def\testsection#1{\vskip 20pt
\noindent\hrulefill\quad{\bfseries\large #1}\quad\hrulefill\par\bigskip}
%
% Reference document
%
\textwidth=300pt
\textheight=550pt
\parindent=0pt
\pagestyle{empty}
\begin{document}
\centerline{\huge Introducing alternatives in displays}
\centerline{\Large (nothing definitive, just a proof of concept)}
\vskip 5pt
\testsection{Basic display}
Given some command \verb-\boxA- that will produce some \TeX\ box, the
command \verb-\disp{\boxA}- provides a simple way to display this box:
\display
\tb{50pt}{a}
\end
A command \verb-\mb- is provided to build math boxes in standard
``displaystyle''. For example, an equation can be easily be displayed
using \verb-\disp{\mb{x=\frac{1}{a+b}}}-. The result is the
following:
\display
\mb{x = \frac{1}{a + b}}
\end
\testsection{Multiple display}
To obtain a display with two boxes, use
\verb-\disp{\two{\boxA}{\boxB}}-:
\display
\two{\tb{50pt}{a}}
{\tb{50pt}{b}}
\end
When the size of the two boxes is beyond the line size, the two
boxes will automatically be displayed vertically:
\display
\two{\tb{200pt}{a}}
{\tb{200pt}{b}}
\end
This is quite handful if you want to change the size, or provide
different formats for your documents. The same applies for three boxes,
the command is \verb-\disp{\three{\boxA}{\boxB}{\boxC}}-:
\display
\three{\tb{50pt}{a}}
{\tb{50pt}{b}}
{\tb{50pt}{c}}
\end
Of course, it is possible that these boxes will be displayed like
this, in the case they do not fit on a single line:
\display
\three{\tb{100pt}{a}}
{\tb{150pt}{b}}
{\tb{100pt}{c}}
\end
It is also possible that they will end up differently. They might fit
better like this, for example:
\display
\three{\tb{100pt}{a}}
{\tb{120pt}{b}}
{\vtb{50pt}{35pt}{c}}
\end
The external code to display the three boxes has not changed. The
layout is chosen among several possibilities according to boxes sizes,
to occupied space and to some penalties for weird layouts. Some
configuration parameters will be provided to customize penalties and
base spacing between boxes.
\testsection{Nested displays}
The following is perfectly valid:
\begin{verbatim}
\disp{\two
{\three{\boxA}{\boxB}{\boxC}}
{\boxX}
}
\end{verbatim}
One will obtain whatever layout is the more appropriate, including
spacing subtleties. The previous declaration may look like:
\display
\two{\three{\tb{25pt}{a}}{\tb{25pt}{b}}{\tb{25pt}{c}}}
{\tb{50pt}{x}}
\end
or:
\display
\two{\three{\tb{50pt}{a}}{\tb{50pt}{b}}{\tb{50pt}{c}}}
{\vtb{100pt}{35pt}{x}}
\end
or:
\display
\two{\three{\tb{60pt}{a}}{\tb{60pt}{b}}{\tb{60pt}{c}}}
{\tb{175pt}{x}}
\end
or maybe the following (among other possibilities):
\display
\two{\three{\tb{125pt}{a}}{\tb{125pt}{b}}{\tb{125pt}{c}}}
{\tb{150pt}{x}}
\end
\testsection{More complex drawings}
Some mechanisms to display relations between boxes is provided, those
are displayed like:
\display
\rel{\mb{\rightarrow}}{\mb{\downarrow}}
{\tb{50pt}{x}}
{\tb{50pt}{y}}
\end
or alternatively like this:
\display
\rel{\mb{\rightarrow}}{\mb{\downarrow}}
{\tb{150pt}{x}}
{\tb{150pt}{y}}
\end
Here is the code:
\begin{verbatim}
\disp{\rel{\mb{\rightarrow}}{\mb{\downarrow}}
{\boxX}{\boxY}}
\end{verbatim}
Of course, combining \verb-\two- and \verb-\rel-, one can include two
relations in the same display:
\display
\two{\rel{\mb{\rightarrow}}{\mb{\downarrow}}{\tb{50pt}{a}}{\tb{50pt}{b}}}
{\rel{\mb{\rightarrow}}{\mb{\downarrow}}{\tb{50pt}{x}}{\tb{50pt}{y}}}
\end
They might also end up like this:
\display
\two{\rel{\mb{\rightarrow}}{\mb{\downarrow}}{\tb{50pt}{a}}{\tb{50pt}{b}}}
{\rel{\mb{\rightarrow}}{\mb{\downarrow}}{\tb{150pt}{x}}{\tb{150pt}{y}}}
\end
or this:
\display
\two{\rel{\mb{\rightarrow}}{\mb{\downarrow}}
{\vtb{40pt}{50pt}{a}}{\vtb{40pt}{50pt}{b}}}
{\rel{\mb{\rightarrow}}{\mb{\downarrow}}
{\tb{100pt}{x}}{\tb{100pt}{y}}}
\end
\begin{multicols}{2}
[\testsection{Breakable displays}]
This example shows that displays can be split across columns or pages:
\display
\rel{\mb{\rightarrow}}{\mb{\downarrow}}
{\vtb{75pt}{65pt}{a}}
{\vtb{75pt}{85pt}{b}}
\end
This is might be convenient if you have big diagrams and you would
prefer them to be inlined.
\end{multicols}
\testsection{Implementation}
The machinery behind this uses two primitives \verb-\choice- and
\verb-\solve-.
Often used inside other definitions, \verb-\choice- allows the packager (and
the user) to define two alternative displays for the same
object. Example:
\begin{verbatim}
\newcommand{bookname}{%
\choice{The Art of Computer Programming}
{\dpenalty{25}TAoCP}
}
\disp{\hbox{\boxA\quad\shortstack[l]{from\\\bookname}}}
\end{verbatim}
\newcommand{\bookname}{%
\choice{The Art of Computer Programming}
{\dpenalty{25}TAoCP}
}
The purpose of \verb-\dpenalty{25}- is to prevent the use of the
abbreviation in places where the full text can be used.
So, this will display:
\display
\hbox{\vtb{75pt}{65pt}{a}\quad\shortstack[l]{from\\\bookname}}
\end
Or, in the case the box is a little bigger:
\display
\hbox{\vtb{175pt}{65pt}{a}\quad\shortstack[l]{from\\\bookname}}
\end
The command \verb-\solve- (which is used in \verb-\disp-) tries a
hundred times to render its argument, \verb-choice-s are made
randomly. The best result is kept (hopefully, it will have low
dimensions and low penalties).
\testsection{Inline solver}
Inline display like
$\csolve{\vlimit=2\baselineskip}
{\rel{\mb{\rightarrow}}{\mb{\downarrow}}{\tb{50pt}{a}}{\tb{50pt}{b}}}$
is also possible, but it has to be improved.
\testsection{Todos}
Find some better command names and portable/usable syntax.
Report errors correctly.
\medskip
Better performance (an more deterministic results) could be achieved
using better probabilistic methods for finding minima, such as
``Simulated annealing''.
\medskip
Taking in account \verb-\pagetotal- to predict page breaks and choose
the layout accordingly would be great.
\medskip
Lots of other funny stuff...
\end{document}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment