[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