Created
July 22, 2011 17:06
-
-
Save sgimenez/1099872 to your computer and use it in GitHub Desktop.
Introducing Alternatives in TeX displays
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
% 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