[pstricks] Cascading divisions (using PSTricks nodes and the arrayjob package)
Denis Girou
Denis.Girou at idris.fr
Wed Apr 4 21:40:22 CEST 2001
Some weeks ago, a student ask on one french TeX mailing list how to build
cascading divisions with LaTeX.
I never use them, as probably everybody here!, but this is a good
programming example and not a very easy problem, if of course we want
a generic solution which compute automatically all the intermediate
divisions and position correctly all the numbers.
I solve this problem using PSTricks nodes and the great `arrayjob' package
from Zhuhan Jiang, allowing also to break the lines for long series of
divisions. The main weakness that I see is that the bounding box can't be
computed and must be defined explicitely by an external "pspicture"
environment if needed, but this is easy to do.
P.S. * Comparing to the version I sent on the GUTenberg mailing list,
I change the code to remove all LaTeX dependencies, so it is now a generic
macro usable with Plain and ConTeXt (even if some examples that I give here
use sometimes other LaTeX macros or environments), but the code is a little
more complicated.
* Yes, the code is rather technical and probably not easy to
understand... Nevertheless, as all examples, it can give ideas to some people
to solve other problems.
D.G.
\documentclass[a4paper]{article}
\usepackage{arrayjob} % For arrays management
\usepackage{calc} % For computations on LaTeX lengths
\usepackage{multido} % For loop structure
\usepackage{pstcol} % PSTricks with the `color' interface
\usepackage{pst-key} % PSTricks interface to `keyval'
\usepackage{pst-node} % PSTricks nodes
\makeatletter
% To remove a spurious blank in `arrayjob' 1.03
\def\one at VEC#1,#2\safty at mark{\def\temp at macro{#2}\temp at toks={#2}%
\ifx\temp at macro\empty \one at VECtrue \else \one at VECfalse \fi
\temp at count=#1\relax}
% To allow the expansion of the content of an array
\newif\ifexpandarrayreadelements
\def\read at array[#1]#2#3{\temp at count=1
\def\one at item##1#1##2\safty at mark{\def\temp at macro{##2}\temp at toks={##2}%
\ifx\temp at macro\empty\else
\def\temp at macro{\one at item##2\safty at mark}\fi
{\temp at toks={##1}\def\temp@@macro{\array{#2}(\the\temp at count)=}%
\expandafter\temp@@macro\expandafter{\the\temp at toks}}%
\expandafter\edef\csname total@#2\endcsname{\the\temp at count}%
\advance\temp at count1\temp at macro}%
% D.G. modification begin - Mar. 13, 2001
% \one at item#3#1\safty at mark}%
\ifexpandarrayreadelements
\expandafter\one at item#3#1\safty at mark%
\else
\one at item#3#1\safty at mark%
\fi}%
% D.G. modification end
% Line broken after these specified numbers of divisors
\define at key{psset}{CascadingDivisionBreaks}{%
\edef\CascadingDivision at Breaks{#1}}
% Inter column space for divisions
\newdimen\CascadingDivision at InterColumnSpace
\define at key{psset}{CascadingDivisionInterColumnSpace}{%
\pssetlength{\CascadingDivision at InterColumnSpace}{#1}}
% Inter row space for divisions
\newdimen\CascadingDivision at InterRowSpace
\define at key{psset}{CascadingDivisionInterRowSpace}{%
\pssetlength{\CascadingDivision at InterRowSpace}{#1}}
% Inter column and row space (set equal) for divisions
\define at key{psset}{CascadingDivisionInterColumnRowSpace}{%
\pssetlength{\CascadingDivision at InterColumnSpace}{#1}%
\pssetlength{\CascadingDivision at InterRowSpace}{#1}}
% Font to use for all the numbers
\define at key{psset}{CascadingDivisionFont}{%
\def\CascadingDivision at Font{#1}}
% Style to print the remainders of the divisions
\define at key{psset}{CascadingDivisionRemainderStyle}{%
\let\CascadingDivision at RemainderStyle#1}
% Default values: current font, no line break, 0.2 units between columns
% and rows, remainders in default text style
\setkeys{psset}{%
CascadingDivisionFont=,
CascadingDivisionBreaks=999,CascadingDivisionInterColumnRowSpace=0.2,
CascadingDivisionRemainderStyle=}
\SpecialCoor % To use PSTricks nodes as coordinates
\newarray\CD at Divisors % Internal array to store the divisors
\newarray\CD at DivisorsByLine % Internal array to store the divisors by line
\def\CascadingDivision{%
\@ifnextchar[{\CascadingDivision at i}{\CascadingDivision at i[]}}
\def\CascadingDivision at i[#1]#2#3{{%
% #1 = number, #2 = list of divisors, separated by the & character,
\setkeys{psset}{#1}% % Store local parameter values
\def\arraystretch{0}% % No automatic spaces between rows in "tabular"
\begingroup
\expandarrayreadelementstrue
\readarray{CD at DivisorsByLine}{\CascadingDivision at Breaks}% Store these numbers
\endgroup
\pst at cntc=\@ne % First index to use in this array
\pst at cntd=\z@ % To count the divisions inside a line
\pnode{CD at Division} % Initialize the starting node
\pnode{CD at DivisionInit} % Idem (useful if we must break the line)
% Erase the preceding possible array values (to delete the array is not enough)
\multido{\iDivisor=\@ne+\@ne}{100}{\CD at Divisors(\iDivisor)={}}
\pst at cnta=#2 % Initial number to divide
\readarray{CD at Divisors}{#3} % Store the divisors in an array
\CascadingDivision at Font % Font to use to print all the numbers
\multido{\iDivisor=\@ne+\@ne}{100}{%
\checkCD at Divisors(\iDivisor)% % Next divisor
\ifemptydata % Do we reach the last divisor?
\multidostop % Yes: it is finished
\else
\advance\pst at cntd\@ne % No: one more division
\rput[lt](CD at Division){%
% Matrix environment to show the 4 values
\psmatrix[mcol=r,colsep=\CascadingDivision at InterColumnSpace,
rowsep=\CascadingDivision at InterRowSpace]
\the\pst at cnta & \Rnode[vref=1.6ex]{CD at NodeA}{} &
[mcol=l]\Rnode[href=1]{CD at NodeB}{\cachedata} \\[-2ex]
& \Rnode{CD at NodeC}{} & \Rnode{CD at NodeD}{} \\[-2ex]
\pst at cntg=\pst at cnta
\global\divide\pst at cnta\cachedata
\pst at cnth=\pst at cnta
\multiply\pst at cnth\cachedata
\advance\pst at cntg-\pst at cnth
\CascadingDivision at RemainderStyle{\the\pst at cntg} & \pnode{CD at NodeE} &
[mcol=l]\rnode[lt]{CD at Division}{%
\Rnode[href=1]{CD at NodeF}{\the\pst at cnta}}
\psline(CD at NodeA)(CD at NodeE) % Vertical line
% Horizontal line (it depend if denominator or numerator is the longest)
\psline(CD at NodeC)(CD at NodeB|CD at NodeD)
\psline(CD at NodeC)(CD at NodeF|CD at NodeD)
% Look if we must break the line
\checkCD at DivisorsByLine(\pst at cntc)%
\pst at cnth=\pst at cntd
\divide\pst at cnth\cachedata
\multiply\pst at cnth\cachedata
\pst at cntg=\pst at cntd
\advance\pst at cntg-\pst at cnth
\ifnum\pst at cntg=\z@ % Yes, we must break the line
\global\pst at cntd=\z@ % As we will start a new line next division
\global\advance\pst at cntc\@ne % Next number of divisors by line
\checkCD at DivisorsByLine(\pst at cntc)%
\ifemptydata
\global\advance\pst at cntc\m at ne% If it doesn't exist, kept last one
\fi
% Insert dots for continuation sign, but not if last divisor!
\pst at cnth=\iDivisor
\advance\pst at cnth\@ne
\checkCD at Divisors(\pst at cnth)% Will be the next divisor
\ifemptydata % No dots if last divisor!
\else
\hbox{~$\ldots$}% Insert dots
\fi
\pnode(CD at DivisionInit|0,-0.5){CD at Division}% Breaks the line
\fi
\endpsmatrix}
\fi}}}
\makeatother
\begin{document}
\CascadingDivision{25}{2&2&2&2&2}
\hspace{3cm}
\CascadingDivision{67587}{163&21&2}
\hspace{4cm}
\CascadingDivision{87}{2}
\hspace{2cm}
\CascadingDivision{987}{8&8&8}
\def\CascadingDivisionRemainderRed#1{\textcolor{red}{#1}}
\psset{CascadingDivisionRemainderStyle=\CascadingDivisionRemainderRed}
\vspace{4cm}
\CascadingDivision[CascadingDivisionFont=\tiny]
{186324}{2&2&2&2&2&2&2&2&2&2&2&2&2&2&2&2&2&2}
\vspace{10cm}
\psframebox[linestyle=none,fillstyle=solid,fillcolor=yellow]{%
\begin{pspicture}(6.7,-3)
\CascadingDivision[CascadingDivisionFont=\LARGE]{65331267}{2591&7&21}
\end{pspicture}}
\clearpage
\CascadingDivision{4261367}{1&1&1}
\hspace{6cm}
\CascadingDivision{163525}{12&5342&2}
\vspace{3cm}
\CascadingDivision[CascadingDivisionFont=\Large,
CascadingDivisionInterColumnRowSpace=5mm]{25}{2&2&2&2&2}
\vspace{8cm}
\scaleboxto(2,0){%
\psframebox[framesep=0,linestyle=none,fillstyle=solid,fillcolor=yellow]{%
\begin{pspicture}(3.5,-3.4)
\CascadingDivision{25}{2&2&2&2&2}
\end{pspicture}}}
\vspace{1cm}
\begin{minipage}{0.47\textwidth}
\CascadingDivision[CascadingDivisionBreaks=2]{25}{2&2&2&2&2}
\end{minipage}%
\begin{minipage}{0.47\textwidth}
\CascadingDivision[CascadingDivisionBreaks={1&2&1}]{25}{2&2&2&2&2}
\end{minipage}
\clearpage
\newcommand{\MyRemainderStyle}[1]{%
\makebox[\widthof{#1}]{%
\psframebox[framesep=0.05,linestyle=none,fillstyle=solid,fillcolor=yellow]{%
\textcolor{red}{#1}}}}
\CascadingDivision[CascadingDivisionInterColumnRowSpace=1mm,
CascadingDivisionRemainderStyle=\MyRemainderStyle]
{25}{2&2&2&2&2}
\vspace{4cm}
\CascadingDivision[CascadingDivisionFont=\scriptsize]
{186324}{2&2&2&2&2&2&2&2&2&2&2&2&2&2&2&2&2&2}
\vspace{3cm}
\CascadingDivision[CascadingDivisionBreaks={8&12}]
{186324}{2&2&2&2&2&2&2&2&2&2&2&2&2&2&2&2&2&2}
\end{document}
More information about the PSTricks
mailing list