From b947be239b40a6eaec6083a561bf347ff84385e9 Mon Sep 17 00:00:00 2001
From: Karl Berry <karl@freefriends.org>
Date: Fri, 13 Jan 2006 00:01:57 +0000
Subject: pb-diagram

git-svn-id: svn://tug.org/texlive/trunk@1176 c570f23f-e606-0410-a88d-b1316a301751
---
 .../texmf-dist/tex/latex/pb-diagram/pb-diagram.sty | 963 +++++++++++++++++++++
 1 file changed, 963 insertions(+)
 create mode 100644 Master/texmf-dist/tex/latex/pb-diagram/pb-diagram.sty

(limited to 'Master/texmf-dist/tex/latex/pb-diagram/pb-diagram.sty')

diff --git a/Master/texmf-dist/tex/latex/pb-diagram/pb-diagram.sty b/Master/texmf-dist/tex/latex/pb-diagram/pb-diagram.sty
new file mode 100644
index 00000000000..eacab0d5778
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/pb-diagram/pb-diagram.sty
@@ -0,0 +1,963 @@
+% pb-diagram.sty: LaTeX commutative diagram macros
+% Author: Paul Burchard <burchard@pobox.com>
+% Version Number: 5.0
+% Version Date: 20 Oct 1998
+%
+% Copyright (c) 1990, 1992, 1995, 1998 by Paul Burchard
+%
+%	This program is free software; you can redistribute it and/or modify
+%	it under the terms of the GNU General Public License as published by
+%	the Free Software Foundation; either version 2 of the License, or
+%	(at your option) any later version.
+%
+%	This program is distributed in the hope that it will be useful,
+%	but WITHOUT ANY WARRANTY; without even the implied warranty of
+%	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%	GNU General Public License for more details.
+%
+%	You should have received a copy of the GNU General Public License
+%	along with this program; if not, write to the Free Software
+%	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%
+% Please see documentation file "pb-manual.tex" for an
+% explanation of how to use the "diagram" environment.
+%
+% For those not inclined to read manuals, here is a short example:
+%
+%                      \[
+%                      \begin{diagram}
+%      a       b*        \node{A} \arrow{e,t}{a}
+%   A ---> B* ---> C              \arrow{s,l}{c} \arrow{ese}
+%   | \____ _____/ |       \node{B^*} \arrow{e,t}{b^*}
+% c |   ___X____   | d       \node{C} \arrow{s,r}{d} \arrow{wsw}\\
+%   v `/_      _\' v     \node{D} \arrow[2]{e,b}{e}
+%   D -----------> E       \node[2]{E}
+%          e           \end{diagram}
+%                      \]
+%
+% I would like to acknowledge inspiration from a macro package
+% of Marc-Paul van der Hulst, and the thorough topological
+% torture-testing of this package by Bill Richter.
+%
+%
+% ---USER-ADJUSTABLE PARAMETERS---
+%
+\newskip\dgARROWLENGTH      \dgARROWLENGTH=2.5em\relax
+\newskip\dgTEXTARROWLENGTH  \dgTEXTARROWLENGTH=1.1em\relax
+\newskip\dgHORIZPAD         \dgHORIZPAD=1em\relax
+\newskip\dgVERTPAD          \dgVERTPAD=2ex\relax
+\newskip\dgLABELOFFSET      \dgLABELOFFSET=.7ex\relax
+\newcount\dgARROWPARTS      \dgARROWPARTS=4\relax
+\newcommand{\dgeverynode}{\displaystyle}
+\newcommand{\dgeverylabel}{\scriptstyle}
+\newif\ifdgVERBOSE          \dgVERBOSEtrue
+%
+% for plain LaTeX mode
+\newskip\dgDOTSPACING       \dgDOTSPACING=0.35em
+\newskip\dgDOTSIZE          \dgDOTSIZE=1.5\fontdimen8\tenln
+%
+% obsolete---for compatibility with version 1.0
+\newcount\dgMAXSQUARE       \dgMAXSQUARE=4\relax
+\newcount\dgMINDBLSQ        \dgMINDBLSQ=10\relax
+\newskip\dgCOLUMNWIDTH      \dgCOLUMNWIDTH=2em\relax
+%
+%
+% -----------------------------------------------------------------
+% -------------------------CUSTOMIZABLE CODE-----------------------
+% -----------------------------------------------------------------
+%
+% ### Do not change this file!!!  Create your own style file to ###
+% ### override or add to these definitions.                     ###
+%
+%
+% ---ARROW STYLE OPTIONS---
+%.
+% The \dgo@XXX commands set arrow geometry and style parameters
+% such as \dg@VECTOR and \dg@LBLPOS.  The \dgo@ command should
+% set default values for all option parameters.
+%
+% See sample customizations below for more options.
+%
+\chardef\f@ur=4
+\@namedef{dgo@}{\let\dg@VECTOR=\vector
+   \dg@LBLPOS=\dgARROWPARTS \divide\dg@LBLPOS\tw@}%
+\@namedef{dgo@-}{\let\dg@VECTOR=\line}%
+\@namedef{dgo@!}{\let\dg@VECTOR=\dg@novector}%
+\@namedef{dgo@1}{\dg@LBLPOS=\@ne\relax}%
+\@namedef{dgo@2}{\dg@LBLPOS=\tw@\relax}%
+\@namedef{dgo@3}{\dg@LBLPOS=\thr@@\relax}%
+\@namedef{dgo@4}{\dg@LBLPOS=\f@ur\relax}%
+\@namedef{dgo@5}{\dg@LBLPOS=5\relax}%
+\@namedef{dgo@6}{\dg@LBLPOS=6\relax}%
+\@namedef{dgo@7}{\dg@LBLPOS=7\relax}%
+\@namedef{dgo@8}{\dg@LBLPOS=8\relax}%
+\@namedef{dgo@9}{\dg@LBLPOS=9\relax}%
+%
+%
+% ---ARROW TYPES (directions)---
+%.
+% These macros specify the geometry of a simple arrow in
+% grid coordinates, in the style of LaTeX's \vector command.
+% They set the parameters:
+%    \dg@DX, \dg@DY, \dg@SIZE,
+%
+\def\dgt@e{\dg@DX=\@ne \dg@DY=\z@ \dg@SIZE=\@ne}%
+\def\dgt@w{\dg@DX=\m@ne \dg@DY=\z@ \dg@SIZE=\@ne}%
+\def\dgt@n{\dg@DX=\z@ \dg@DY=\@ne \dg@SIZE=\@ne}%
+\def\dgt@s{\dg@DX=\z@ \dg@DY=\m@ne \dg@SIZE=\@ne}%
+\def\dgt@ne{\dg@DX=\@ne \dg@DY=\@ne \dg@SIZE=\@ne}%
+\def\dgt@se{\dg@DX=\@ne \dg@DY=\m@ne \dg@SIZE=\@ne}%
+\def\dgt@nw{\dg@DX=\m@ne \dg@DY=\@ne \dg@SIZE=\@ne}%
+\def\dgt@sw{\dg@DX=\m@ne \dg@DY=\m@ne \dg@SIZE=\@ne}%
+\def\dgt@nne{\dg@DX=\@ne \dg@DY=\tw@ \dg@SIZE=\@ne}%
+\def\dgt@nnw{\dg@DX=\m@ne \dg@DY=\tw@ \dg@SIZE=\@ne}%
+\def\dgt@sse{\dg@DX=\@ne \dg@DY=-\tw@ \dg@SIZE=\@ne}%
+\def\dgt@ssw{\dg@DX=\m@ne \dg@DY=-\tw@ \dg@SIZE=\@ne}%
+\def\dgt@ene{\dg@DX=\tw@ \dg@DY=\@ne \dg@SIZE=\tw@}%
+\def\dgt@ese{\dg@DX=\tw@ \dg@DY=\m@ne \dg@SIZE=\tw@}%
+\def\dgt@wnw{\dg@DX=-\tw@ \dg@DY=\@ne \dg@SIZE=\tw@}%
+\def\dgt@wsw{\dg@DX=-\tw@ \dg@DY=\m@ne \dg@SIZE=\tw@}%
+%
+%
+% ---GRID GEOMETRY---
+%
+% Computes the grid geom params \dg@XGRID, \dg@YGRID, and
+% \unitlength, with the result that unit grid rects will
+% be 1000*XGRID by 1000*YGRID \unitlengths in size.
+% Don't set XGRID, YGRID too big or TeX will get arithmetic
+% overflow.  Also, the possible aspect ratios XGRID:YGRID
+% are always restricted by the slopes of the arrow font.
+%
+% As inputs, use the pre-supplied values of XGRID and YGRID,
+% which are the calculated required minimum dims for
+% unit grid rects (in sp's).
+%
+\def\dggeometry{%
+   \dg@ZTEMP=\dg@XGRID \multiply\dg@ZTEMP\tw@
+   \ifnum\dg@YGRID=\z@ \dg@ZTEMP=\tw@
+   \else \divide\dg@ZTEMP\dg@YGRID \fi
+   % font limit cutoffs
+   \ifnum\dg@ZTEMP>\f@ur \dg@ZTEMP=\f@ur \fi
+   \ifnum\dg@ZTEMP<\@ne \dg@ZTEMP=\@ne \fi
+   \unitlength=1sp\relax
+   \ifnum\dg@ZTEMP<\tw@
+      % round aspect ratio up toward 2:2, widen rect
+      \advance\dg@ZTEMP\@ne
+      \multiply\unitlength\dg@YGRID
+      \dg@XGRID=\dg@ZTEMP \dg@YGRID=\tw@
+      \dg@rmcommondiv\tw@\dg@XGRID\dg@YGRID
+	  \divide\unitlength\dg@YGRID
+   \else
+      % round aspect ratio down toward 2:2, tallen rect
+      \multiply\unitlength\dg@XGRID
+      \dg@XGRID=\dg@ZTEMP \dg@YGRID=\tw@
+      \dg@rmcommondiv\tw@\dg@XGRID\dg@YGRID
+	  \divide\unitlength\dg@XGRID
+   \fi
+   \divide\unitlength\@m\relax}
+%
+% Bugwards-compatibility mode for versions 3.6 and earlier.
+% This mode is enabled with \let\dggeometry=\dgoldgeometry
+%
+\def\dgoldgeometry{%
+   \dg@ZTEMP=\dg@XGRID \multiply\dg@ZTEMP\tw@
+   \ifnum\dg@YGRID=\z@ \dg@ZTEMP=\tw@
+   \else \divide\dg@ZTEMP\dg@YGRID \fi
+   % font limit cutoffs
+   \ifnum\dg@ZTEMP>\f@ur \dg@ZTEMP=\f@ur \fi
+   \ifnum\dg@ZTEMP<\@ne \dg@ZTEMP=\@ne \fi
+   % The following calculation is garbled!
+   \unitlength=2sp\relax
+   \ifnum\dg@ZTEMP<\tw@
+      % round aspect ratio up toward 2:2, widen rect
+      \advance\dg@ZTEMP\@ne
+      \multiply\unitlength\dg@YGRID
+   \else
+      % round aspect ratio down toward 2:2, tallen rect
+      \multiply\unitlength\dg@XGRID \divide\unitlength\dg@ZTEMP
+   \fi
+   \dg@XGRID=\dg@ZTEMP \dg@YGRID=\tw@
+   \dg@rmcommondiv\tw@\dg@XGRID\dg@YGRID
+   \divide\unitlength\dg@YGRID \divide\unitlength\@m\relax}
+%
+%
+%
+% ---SAMPLE CUSTOMIZATION: TWO-HEADED ARROWS FOR PLAIN LaTeX---
+%
+% Two-headed arrow command that works like \vector.
+% Uses temp vars XTEMP, YTEMP.
+% Usage: \dg@twoheadedvector(DX,DY){SIZE}
+%
+\@namedef{dgo@<>}{\let\dg@VECTOR=\dg@twoheadedvector}%
+
+\def\dg@twoheadedvector(#1,#2)#3{%
+   \begingroup
+   \dg@XTEMP=#1\relax\multiply\dg@XTEMP\m@ne\relax
+   \dg@YTEMP=#2\relax\multiply\dg@YTEMP\m@ne\relax
+   \begin{picture}(0,0)%
+      \thinlines
+      \put(0,0){\vector(#1,#2){#3}}%
+      \put(0,0){\vector(\dg@XTEMP,\dg@YTEMP){0}}%
+   \end{picture}%
+   \endgroup}%
+%
+%
+%
+% ---SAMPLE CUSTOMIZATION: DOTTED ARROWS FOR PLAIN LaTeX---
+%
+% Dotted arrow command that works like \vector.
+% Uses temp vars XTEMP, YTEMP, ZTEMP, XEND, WEND.
+% Usage: \dg@dotvector(DX,DY){SIZE}
+%
+\@namedef{dgo@..}{\let\dg@VECTOR=\dg@dotvector}%
+
+\def\dg@dotvector(#1,#2)#3{%
+   \begingroup
+   \dg@XTEMP=#1\relax \dg@YTEMP=#2\relax
+   \let\dg@NDOTS=\dg@XEND \let\dg@DOTDIAM=\dg@WEND
+   % Find number of dots: make x-spacing be DOTSPACING for arrows
+   % of |slope| <= 1, and make y-spacing be DOTSPACING otherwise.
+   % Thus, true spacing is never more than 30% off from DOTSPACING.
+   \dg@NDOTS=\unitlength \multiply\dg@NDOTS #3\relax
+   \dg@ZTEMP=\dg@YTEMP \dg@changesign\dg@YTEMP\dg@ZTEMP
+   \ifnum\dg@XTEMP>\z@
+      \ifnum\dg@YTEMP>\dg@XTEMP
+         \multiply\dg@NDOTS\dg@YTEMP \divide\dg@NDOTS\dg@XTEMP \fi
+   \else\ifnum\dg@XTEMP<\z@
+      \ifnum\dg@YTEMP>-\dg@XTEMP
+         \multiply\dg@NDOTS\dg@YTEMP \divide\dg@NDOTS-\dg@XTEMP \fi
+   \fi\fi
+   \dg@YTEMP=\dg@ZTEMP
+   \divide\dg@NDOTS\dgDOTSPACING
+   \ifnum\dg@NDOTS>\z@\else \dg@NDOTS=\@ne \fi
+   % Compute increment vector between dots; round to \unitlength's.
+   % Use NDOTS not DOTSPACING, since DOTSPACING not exactly obeyed.
+   \dg@ZTEMP=\unitlength \multiply\dg@ZTEMP #3\relax
+   \divide\dg@ZTEMP\dg@NDOTS
+   \ifnum\dg@XTEMP=\z@
+      \dg@changesign\dg@ZTEMP\dg@YTEMP \dg@YTEMP=\dg@ZTEMP
+   \else
+      \dg@changesign\dg@ZTEMP\dg@XTEMP
+      \multiply\dg@YTEMP\dg@ZTEMP \divide\dg@YTEMP\dg@XTEMP
+      \dg@XTEMP=\dg@ZTEMP
+   \fi
+   \divide\dg@XTEMP\unitlength \divide\dg@YTEMP\unitlength
+   % Draw dotted line with \multiput
+   % and arrowhead as zero-length \vector
+   \begin{picture}(0,0)%
+      \dg@DOTDIAM=\dgDOTSIZE \divide\dg@DOTDIAM\unitlength
+      \multiput(0,0)(\dg@XTEMP,\dg@YTEMP){\dg@NDOTS}{%
+         \circle*{\dg@DOTDIAM}}%
+      \multiply\dg@XTEMP\dg@NDOTS \multiply\dg@YTEMP\dg@NDOTS
+      \put(\dg@XTEMP,\dg@YTEMP){\vector(#1,#2){0}}%
+   \end{picture}%
+   \endgroup}%
+%
+%
+% ----------------------------------------------------------------
+% ----------------------LaTeX ENHANCEMENTS------------------------
+% ----------------------------------------------------------------
+%
+%
+% Allow user-defined commands and environments to have an
+% optional first arg.
+%
+% For environments, also allow ``*-form'' which ignores spaces
+% after the \end{...} command.  (P.S. LaTeX2e has now usurped the
+% ``*-form'' for other purposes in its \newenvironment macro.)
+%
+% Usage:
+%    \newoptcommand{\COMMAND}{DEFAULT}[NARGS]{TEXT}
+%    \renewoptcommand{\COMMAND}{DEFAULT}[NARGS]{TEXT}
+%    \newoptenvironment{NAME}{DEFAULT}[NARGS]{B_TEXT}{E_TEXT}
+%    \newoptenvironment*{NAME}{DEFAULT}[NARGS]{B_TEXT}{E_TEXT}
+%    \renewoptenvironment{NAME}{DEFAULT}[NARGS]{B_TEXT}{E_TEXT}
+%    \renewoptenvironment*{NAME}{DEFAULT}[NARGS]{B_TEXT}{E_TEXT}
+%
+% For example, after defining
+%    \newoptcommand{\glug}{defval}[2]{%
+%        First is ``#1'', 2nd is ``#2''.}
+% the user can do
+%    \glug{hahaha}\\
+%    \glug[oof]{bzzzz}
+% to get
+%    First is ``defval'', 2nd is ``hahaha''.
+%    First is ``oof'', 2nd is ``bzzzz''.
+%
+\def\newoptcommand#1#2{%
+   \@ifnextchar [{\@optargdef#1#2}{\@optargdef#1#2[1]}}
+
+\def\renewoptcommand#1#2{%
+   \edef\@tempa{\expandafter\@cdr\string#1\@nil}%
+   \@ifundefined{\@tempa}{%
+      \@latexerr{\string#1\space undefined}\@ehc}{}%
+   \@ifnextchar [{\@reoptargdef#1#2}{\@reoptargdef#1#2[1]}}
+
+\long\def\@optargdef#1#2[#3]#4{%
+   \@ifdefinable #1{\@reoptargdef#1#2[#3]{#4}}}
+
+\long\def\@reoptargdef#1#2[#3]#4{%
+   \@tempcnta#3\relax \@tempcntb \@ne
+   \let#1\relax \let\@tempa\relax
+   \edef\@tempb{\long\def\csname\string#1\endcsname
+      [\@tempa\the\@tempcntb]}%
+   \advance\@tempcntb \@ne \advance\@tempcnta \m@ne
+   \@whilenum\@tempcnta>0\do{%
+      \edef\@tempb{\@tempb\@tempa\the\@tempcntb}%
+      \advance\@tempcntb \@ne \advance\@tempcnta \m@ne}%
+   \let\@tempa=##\@tempb{#4}\let\@tempa\relax
+   \def#1{\@ifnextchar [{\csname\string#1\endcsname}{%
+      \csname\string#1\endcsname[#2]}}}
+
+\def\newoptenvironment{%
+   \@ifnextchar *{\@@newoptenv{\global\@ignoretrue}}{%
+      \@@newoptenv{}*}}
+
+\def\@@newoptenv#1*#2#3{%
+   \@ifnextchar [{\@newoptenv{#1}{#2}{#3}}{%
+      \@newoptenv{#1}{#2}{#3}[0]}}
+
+\long\def\@newoptenv#1#2#3[#4]#5#6{%
+   \expandafter\newoptcommand\csname#2\endcsname{#3}[#4]{#5}%
+   \expandafter\long\expandafter\def\csname end#2\endcsname{#6#1}}
+
+\def\renewoptenvironment{%
+   \@ifnextchar *{\@@renewoptenv{\global\@ignoretrue}}{%
+      \@@renewoptenv{}*}}
+
+\def\@@renewoptenv#1*#2#3{%
+   \@ifnextchar [{\@renewoptenv{#1}{#2}{#3}}{%
+      \@renewoptenv{#1}{#2}{#3}[0]}}
+
+\long\def\@renewoptenv#1#2#3[#4]#5#6{%
+   \expandafter\renewoptcommand\csname#2\endcsname{#3}[#4]{#5}%
+   \expandafter\long\expandafter\def\csname end#2\endcsname{#6#1}}
+
+%
+%
+% ----------------------------------------------------------------
+% -------------------------CORE CODE------------------------------
+% ----------------------------------------------------------------
+%
+%
+% ---INTERNAL VARIABLES---
+%
+\newcount\dg@HORIZ      \newcount\dg@VERT
+\newcount\dg@XLPAD      \newcount\dg@YBPAD
+\newcount\dg@XRPAD      \newcount\dg@YTPAD
+\newcount\dg@X          \newcount\dg@Y
+\newcount\dg@XGRID      \newcount\dg@YGRID
+\newcount\dg@SIZE
+\newcount\dg@USERSIZE
+\newcount\dg@DX         \newcount\dg@DY
+\newcount\dg@XLBL       \newcount\dg@YLBL
+\newcount\dg@XOFFSET    \newcount\dg@YOFFSET
+\newcount\dg@LBLOFF
+\newcount\dg@XLBLOFF    \newcount\dg@YLBLOFF
+\newcount\dg@LBLPOS
+\newcount\dg@XTEMP      \newcount\dg@YTEMP
+\newcount\dg@ZTEMP
+\newcount\dg@XNODE      \newcount\dg@YNODE
+\newcount\dg@XEND       \newcount\dg@YEND
+\newcount\dg@WEND       \newcount\dg@HEND
+\newcount\dg@COUNT
+\newbox\dg@NODEBOX
+\newbox\dg@LABONEBOX    \newbox\dg@LABTWOBOX
+%
+%
+% ---THE DIAGRAM ENVIRONMENT---
+%
+% Ignores spaces following the \end{diagram},
+% just like most LaTeX built-in environments do (thus the `*').
+% If optional arg supplied, reverts to version 1.0 behavior.
+%
+\newoptenvironment*{diagram}{}[1]{%
+   \ifdgVERBOSE
+      \global\advance\dg@COUNT\@ne
+      \message{[diagram \the\dg@COUNT}%
+   \fi
+   \let\node=\dg@node \let\\=\dg@cr \let\arrow=\dg@arrow
+   %
+   % COMPATIBILITY MODE FOR VERSION 1.0.
+   %
+   % If opt arg given, use it as "BIGNODE" in old-style grid
+   % geometry calculation.
+   \def\dg@BIGNODE{#1}%
+   \ifx\dg@BIGNODE\@empty\else
+      \setbox\dg@NODEBOX=\hbox{$\dgeverynode{#1}$}%
+      \dg@XTEMP=\wd\dg@NODEBOX
+      \dg@YTEMP=\ht\dg@NODEBOX \advance\dg@YTEMP\dp\dg@NODEBOX
+      \ifnum\dg@YTEMP=\z@ \dg@ZTEMP=\z@
+      \else \dg@ZTEMP=\dg@XTEMP \divide\dg@ZTEMP\dg@YTEMP\fi
+      \ifnum\dg@ZTEMP>\dgMAXSQUARE
+         \ifnum\dg@ZTEMP<\dgMINDBLSQ
+		 	\dg@XGRID=\thr@@ \dg@YGRID=\tw@
+         \else \dg@XGRID=\tw@ \dg@YGRID=\@ne \fi
+      \else \dg@XGRID=\@ne \dg@YGRID=\@ne \fi
+      \advance\dg@XTEMP\dgHORIZPAD \advance\dg@YTEMP\dgVERTPAD
+      \dg@XLPAD=\dg@XTEMP \divide\dg@XLPAD\tw@
+      \dg@XRPAD=\dg@XTEMP \divide\dg@XRPAD\tw@
+      \dg@YBPAD=\dg@YTEMP \divide\dg@YBPAD\tw@
+      \dg@YTPAD=\dg@YTEMP \divide\dg@YTPAD\tw@
+      \advance\dg@XTEMP\dgARROWLENGTH
+      \divide\dg@XTEMP\dg@XGRID \divide\dg@XTEMP\@m
+      \unitlength=1sp\relax \multiply\unitlength\dg@XTEMP
+   \fi
+   %
+   % PASS (1): SAVE UP NODES AND ARROWS.
+   %
+   \ifdgVERBOSE \message{.}\fi
+   \dg@X=-\@ne \dg@Y=\z@ \dg@HORIZ=\z@\relax
+   \let\dg@SLIST=\@empty
+   \let\dg@NLIST=\@empty \let\dg@ALIST=\@empty
+   \let\dg@PASS=\dg@savepass
+}{%\enddiagram
+   % Compute size of diagram.
+   \dg@VERT=-\dg@Y\relax
+   %
+   % PASS (2): CALCULATE GRID GEOMETRY.
+   % Since arrows may terminate at nodes later in the diagram,
+   % this requires node data to be saved up in an earlier pass.
+   % Don't need geom pass in compatibility mode.
+   %
+   \ifdgVERBOSE \message{.}\fi
+   \ifx\dg@BIGNODE\@empty
+      % Accumulate data.
+      \dg@XGRID=\z@ \dg@YGRID=\z@
+      \dg@XLPAD=\z@ \dg@XRPAD=\z@ \dg@YBPAD=\z@ \dg@YTPAD=\z@
+      \let\dg@PASS=\dg@geompass
+      \dg@NLIST \dg@ALIST
+      % Calculate \unitlength and normalized XGRID,YGRID.
+      % catch any bogus results along the way
+      \ifnum\dg@XGRID>\z@\else \dg@XGRID=\dgHORIZPAD \fi
+      \ifnum\dg@YGRID>\z@\else \dg@YGRID=\dgVERTPAD \fi
+      \dggeometry
+      \ifdim\unitlength>\z@\else
+         \unitlength=\dgARROWLENGTH \divide\unitlength\@m \fi
+      \ifnum\dg@XGRID>\z@\else \dg@XGRID=\@ne \fi
+      \ifnum\dg@YGRID>\z@\else \dg@YGRID=\@ne \fi
+   \fi
+   % Now convert drawing dimensions to \unitlength's.
+   \dg@LBLOFF=\dgLABELOFFSET \divide\dg@LBLOFF\unitlength
+   \multiply\dg@HORIZ\@m \multiply\dg@HORIZ\dg@XGRID
+   \multiply\dg@VERT\@m \multiply\dg@VERT\dg@YGRID
+   \divide\dg@XLPAD\unitlength \divide\dg@XRPAD\unitlength
+   \divide\dg@YBPAD\unitlength \divide\dg@YTPAD\unitlength
+   %
+   % PASS (3): DRAW SAVED NODES AND ARROWS USING GRID GEOMETRY.
+   %
+   \ifdgVERBOSE \message{.}\fi
+   \let\dg@PASS=\dg@drawpass
+   \hspace*{\dg@XLPAD\unitlength}%
+   \vcenter{%
+      \hsize=0pt\relax\parindent=0pt\relax
+      \parskip=0pt\relax\baselineskip=0pt\relax
+      \vspace*{\dg@YTPAD\unitlength}%
+      \begin{picture}(0,0)(0,0)\dg@NLIST\dg@ALIST\end{picture}%
+      % Tell TeX how high diagram is.
+      \raisebox{\z@}[\z@][\dg@VERT\unitlength]{}%
+      \vspace*{\dg@YBPAD\unitlength}%
+   }
+   % Tell TeX how wide diagram is.
+   \hspace*{\dg@HORIZ\unitlength}%
+   \hspace*{\dg@XRPAD\unitlength}%
+   \ifdgVERBOSE \message{]}\fi
+}%
+
+\def\dg@savepass{s}
+\def\dg@geompass{g}
+\def\dg@drawpass{d}
+
+%
+%
+% ---MAKING NODES---
+%
+% In draw pass, just place centered FORMULA at current coords.
+%
+% In geometry pass, find how far nodes at boundary of diagram
+% stick out over the edge; update XLPAD, XRPAD, YBPAD, YTPAD.
+%
+% In save pass, measure FORMULA, saving incremented position and
+% padded width and height (in scaled points) on \dg@SLIST like so:
+%    ...,\dg@XNODE=<X coord>\relax\dg@YNODE=<Y coord>\relax
+%    \dg@XTEMP=<node width>\relax\dg@YTEMP=<node height>\relax,...
+% and saving node itself (for later drawing) on \dg@NLIST like so:
+%    ...\dg@X=<X coord>\relax\dg@Y=<Y coord>\relax
+%    \dg@node{FORMULA}...
+% Also update HORIZ (horizontal size of diagram). 
+%
+% Usage: \dg@node[NCOLS]{FORMULA}
+\newoptcommand{\dg@node}{\@ne}[2]{%
+   \ifx\dg@PASS\dg@savepass
+      %
+      % Update coordinates and XMAX.
+      \dg@XTEMP=#1\relax
+      \ifnum\dg@XTEMP<\@ne \dg@XTEMP=\@ne\fi
+      \advance\dg@X\dg@XTEMP
+      \ifnum\dg@HORIZ<\dg@X \dg@HORIZ=\dg@X \fi
+      %
+      % Measure padded node.
+      \setbox\dg@NODEBOX=\hbox{$\dgeverynode{#2}$}%
+      \dg@XTEMP=\wd\dg@NODEBOX \advance\dg@XTEMP\dgHORIZPAD
+      \dg@YTEMP=\ht\dg@NODEBOX \advance\dg@YTEMP\dp\dg@NODEBOX
+      \advance\dg@YTEMP\dgVERTPAD
+      %
+      % Save up info in NLIST and SLIST.
+      \toks\z@=\expandafter{\dg@SLIST}%
+      \edef\dg@SLIST{\the\toks\z@
+         ,\noexpand\dg@XNODE=\number\dg@X\noexpand\relax
+         \noexpand\dg@YNODE=\number\dg@Y\noexpand\relax
+         \noexpand\dg@XTEMP=\number\dg@XTEMP\noexpand\relax
+         \noexpand\dg@YTEMP=\number\dg@YTEMP\noexpand\relax}%
+      \toks\z@=\expandafter{\dg@NLIST}%
+      \toks\tw@={\dg@node{#2}}%
+      \edef\dg@NLIST{\the\toks\z@
+         \noexpand\dg@X=\number\dg@X\noexpand\relax
+         \noexpand\dg@Y=\number\dg@Y\noexpand\relax
+         \the\toks\tw@}%
+   \else\ifx\dg@PASS\dg@geompass
+      %
+      % If on boundary, compare padding against half of
+      % padded node size saved in SLIST.
+      \ifnum\dg@X=\z@
+         \dg@getnodesize
+            {\dg@SLIST}{\dg@X}{\dg@Y}{\dg@WEND}{\dg@HEND}%
+         \divide\dg@WEND\tw@
+         \ifnum\dg@XLPAD<\dg@WEND \dg@XLPAD=\dg@WEND \fi\fi
+      \ifnum\dg@X=\dg@HORIZ
+         \dg@getnodesize
+            {\dg@SLIST}{\dg@X}{\dg@Y}{\dg@WEND}{\dg@HEND}%
+         \divide\dg@WEND\tw@
+         \ifnum\dg@XRPAD<\dg@WEND \dg@XRPAD=\dg@WEND \fi\fi
+      \ifnum\dg@Y=\z@
+         \dg@getnodesize
+            {\dg@SLIST}{\dg@X}{\dg@Y}{\dg@WEND}{\dg@HEND}%
+         \divide\dg@HEND\tw@
+         \ifnum\dg@YTPAD<\dg@HEND \dg@YTPAD=\dg@HEND \fi\fi
+      \ifnum\dg@Y=-\dg@VERT\relax
+         \dg@getnodesize
+            {\dg@SLIST}{\dg@X}{\dg@Y}{\dg@WEND}{\dg@HEND}%
+         \divide\dg@HEND\tw@
+         \ifnum\dg@YBPAD<\dg@HEND \dg@YBPAD=\dg@HEND \fi\fi
+   \else\ifx\dg@PASS\dg@drawpass
+      %
+      % Get scaled picture coords from grid coords.
+      \dg@XNODE=\dg@X \multiply\dg@XNODE\@m
+      \multiply\dg@XNODE\dg@XGRID
+      \dg@YNODE=\dg@Y \multiply\dg@YNODE\@m
+      \multiply\dg@YNODE\dg@YGRID
+      %
+      % Place centered formula.
+      \setbox\dg@NODEBOX=\hbox{$\dgeverynode{#2}$}%
+      \put(\dg@XNODE,\dg@YNODE){\dg@makebox{\box\dg@NODEBOX}}%
+   \fi\fi\fi}%
+
+% Increment coordinates to beginning of NROWS-th next
+%    grid row (only in save pass).
+% Usage: \dg@cr[NROWS]
+\newoptcommand{\dg@cr}{\@ne}[1]{%
+   \ifx\dg@PASS\dg@savepass
+      \dg@YTEMP=#1\relax
+      \ifnum\dg@YTEMP<\@ne \dg@YTEMP=\@ne \fi
+      \advance\dg@Y -\dg@YTEMP\relax
+      \dg@X=-\@ne\relax\fi}%
+%
+%
+% ---ARROW COMMAND FRONT ENDS---
+%
+% Usage: \dg@arrow[USERSIZE]{ARROW_SPECS}LABELS...
+\newoptcommand{\dg@arrow}{\@ne}[2]{%
+   % Keep all arrow geometry parameters local.
+   % The \endgroup is in each \dg@process command.
+   \begingroup
+   % Get optional arrow USERSIZE.
+   \dg@USERSIZE=#1\relax
+   \ifnum\dg@USERSIZE<\@ne \dg@USERSIZE=\@ne \fi
+   % Parse arrow specification.
+   \dg@parse{#2}%
+   % In draw pass, calculate all the arrow geometry parameters
+   %    and draw the arrow.
+   % In geometry pass, calculate minimum size of grid rectangle
+   %    which gives all arrows ARROWLENGTH of room.
+   % In save mode, just save up the arrow in ALIST.
+   \ifx\dg@PASS\dg@savepass
+      % Arrow saved with orig LBLTYPE, so don't re-position args.
+      \ifx\dg@label\dgl@b \let\dg@label=\dgl@t \fi
+      \ifx\dg@label\dgl@r \let\dg@label=\dgl@l \fi
+      \let\dg@process=\dg@save
+   \else\ifx\dg@PASS\dg@geompass
+      \let\dg@process=\dg@ignore
+      \dg@geomcalc
+   \else\ifx\dg@PASS\dg@drawpass
+      \let\dg@process=\dg@draw
+      \dg@drawcalc
+   \fi\fi\fi
+   \dg@label{\dg@process{#1}{#2}}}%
+
+% Also make \arrow command available for use outside of diagrams.
+\newoptcommand{\arrow}{\@ne}[2]{%
+   % Parse arrow specification just to check how many labels.
+   \dg@parse{#2}%
+   % Text arrow uses original LBLTYPE, so don't re-position args.
+   \ifx\dg@label\dgl@b \let\dg@label=\dgl@t \fi
+   \ifx\dg@label\dgl@r \let\dg@label=\dgl@l \fi
+   \dg@label{\dg@textarrow{#1}{#2}}}%
+
+\def\dg@textarrow#1#2#3#4{%
+   \mathrel{{%
+      \dgHORIZPAD=0pt\relax\dgVERTPAD=0pt\relax
+      \dgARROWLENGTH=\dgTEXTARROWLENGTH\relax
+	  \dgVERBOSEfalse
+      \begin{diagram}
+         \node{}\arrow[#1]{#2}{#3}{#4}\node{}
+      \end{diagram}}}}
+
+% Parse arrow specification: comma-separated list of features.
+% Usage: \dg@parse{TYPE,ONEORTWOLABEL,OPTION,...}
+\def\dg@parse#1{%
+   % Set default labeling and options.
+   \let\dg@label=\dgl@ \dgo@
+   % Scan list, reading TYPE, LBLTYPE, and OPTIONs in turn.
+   \let\dg@type=\@empty \let\dg@lbltype=\@empty
+   \@for\dg@list:=#1\do{%
+      \ifx\dg@type\@empty \let\dg@type=\dg@list
+      \else\ifx\dg@lbltype\@empty \let\dg@lbltype=\dg@list
+         % In case LBLTYPE not given, try it as an OPTION.
+         % Note: LBLTYPE and OPTION names must not conflict.
+         \@ifundefined{dgo@\dg@list}{}{\@nameuse{dgo@\dg@list}}%
+      \else
+         % Process OPTIONs in turn; ignore bad OPTIONs.
+         \@ifundefined{dgo@\dg@list}{}{\@nameuse{dgo@\dg@list}}%
+      \fi\fi}%
+   % TYPE must be specified (default to "e" on error).
+   % Get raw geometry parameters from TYPE.
+   \@ifundefined{dgt@\dg@type}{\dgt@e}{\@nameuse{dgt@\dg@type}}%
+   % If LBLTYPE valid, get arg handler.
+   \@ifundefined{dgl@\dg@lbltype}{}{%
+      \dg@letname\dg@label{dgl@\dg@lbltype}}}
+
+%
+%
+% ---ARROW PROCESSORS (draw, save, ignore)---
+%
+% Draw labelled arrow, using computed arrow geometry parameters.
+% The \endgroup matches \dg@arrow's \begingroup.
+% Usage: \dg@draw{IGNORE}{IGNORE}{LABEL1}{LABEL2}
+\def\dg@draw#1#2#3#4{%
+   % Typeset labels in advance of all the positioning yoga,
+   % since TeX only allows boxes to nest 40 deep by default.
+   \setbox\dg@LABONEBOX=\hbox{$\dgeverylabel{#3}$}%
+   \setbox\dg@LABTWOBOX=\hbox{$\dgeverylabel{#4}$}%
+   \put(\dg@X,\dg@Y){\dg@makebox{%
+      \begin{picture}(0,0)%
+         \thinlines
+         \put(\dg@XOFFSET,\dg@YOFFSET){%
+            \dg@VECTOR(\dg@DX,\dg@DY){\dg@SIZE}}%
+         \put(\dg@XLBL,\dg@YLBL){\dg@makebox{%
+            \begin{picture}(0,0)%
+               \put(\dg@XLBLOFF,\dg@YLBLOFF){%
+                  \dg@makebox[\dg@LBLONE]{\box\dg@LABONEBOX}}%
+               \put(-\dg@XLBLOFF,-\dg@YLBLOFF){%
+                  \dg@makebox[\dg@LBLTWO]{\box\dg@LABTWOBOX}}%
+            \end{picture}}}%
+      \end{picture}}}%
+   \endgroup}%
+
+% Instead of drawing arrow now, save it on \dg@ALIST, like so:
+%    ...\dg@X=<X coord>\relax\dg@Y=<Y coord>\relax
+%       \dg@arrow[<size>]{<arrow specs>}<labels>...
+% If either LABEL1 or LABEL2 is empty, it will simply be ignored.
+% Usage: \dg@save{SIZE}{SPEC}{LABEL1}{LABEL2}
+\def\dg@save#1#2#3#4{%
+   \endgroup % to match \dg@arrow's \begingroup
+   \toks\z@=\expandafter{\dg@ALIST}%
+   \toks\tw@={\dg@arrow[#1]{#2}{#3}{#4}}%
+   \edef\dg@ALIST{\the\toks\z@%
+      \noexpand\dg@X=\number\dg@X\noexpand\relax
+      \noexpand\dg@Y=\number\dg@Y\noexpand\relax
+      \the\toks\tw@}}%
+
+% No-op arrow processor.
+% Usage: \dg@ignore{SIZE}{SPEC}{LABEL1}{LABEL2}
+\def\dg@ignore#1#2#3#4{\endgroup}
+
+%
+%
+% ---ARROW GEOMETRY CALCULATIONS---
+%
+% Calc minimum size of grid rectangle (in sp's) so that all
+% arrows will have horizontal extent at least  ARROWMINX :=
+% ( |DX|/(|DX|+|DY|) )*ARROWLENGTH  and the vertical extent
+% at least  ARROWMINY := ( |DY|/(|DX|+|DY|) )*ARROWLENGTH.
+% We estimate the horizontal and vertical extents from node
+% corner to node corner (which indeed gives lower bounds).
+% Thus for example XGRID must be at least
+%    ( (WSOURCE+WTARGET)/2 + ARROWMINX ) / (SIZE*USERSIZE)
+% where the width of source (target) node is WSOURCE (WTARGET).
+% There is a similar formula for YGRID.
+%
+% Updates (globally so we see it later!!!):
+%    XGRID, YGRID
+% using these arrow parameters:
+%    X, Y, USERSIZE, SIZE, DX, DY
+% using also the stored node dims in SLIST.
+% Uses temp vars XOFFSET, YOFFSET, XTEMP, YTEMP, ZTEMP,
+% XEND, YEND, WEND, HEND.  No args.
+%
+\def\dg@geomcalc{%
+   % Find other end of arrow (in grid coords).
+   \dg@XEND=\dg@SIZE \multiply\dg@XEND\dg@USERSIZE
+   \ifnum\dg@DX=\z@
+      \dg@YEND=\dg@XEND \dg@XEND=\z@
+      \dg@changesign\dg@YEND\dg@DY
+   \else
+      \dg@changesign\dg@XEND\dg@DX \dg@YEND=\dg@XEND
+      \multiply\dg@YEND\dg@DY \divide\dg@YEND\dg@DX
+   \fi
+   \advance\dg@XEND\dg@X \advance\dg@YEND\dg@Y
+   % Get size of node there.
+   \dg@getnodesize
+      {\dg@SLIST}{\dg@XEND}{\dg@YEND}{\dg@WEND}{\dg@HEND}%
+   \dg@XOFFSET=\dg@WEND \dg@YOFFSET=\dg@HEND
+   % Now average in size of current node.
+   \dg@getnodesize
+      {\dg@SLIST}{\dg@X}{\dg@Y}{\dg@WEND}{\dg@HEND}%
+   \advance\dg@XOFFSET\dg@WEND \divide\dg@XOFFSET\tw@
+   \advance\dg@YOFFSET\dg@HEND \divide\dg@YOFFSET\tw@
+   % Now add in required arrow length.
+   \dg@XTEMP=\dgARROWLENGTH \dg@YTEMP=\dgARROWLENGTH
+   \ifnum\dg@DX>\z@
+      \dg@ZTEMP=\dg@DX \multiply\dg@XTEMP\dg@DX
+   \else \dg@ZTEMP=-\dg@DX \multiply\dg@XTEMP -\dg@DX \fi
+   \ifnum\dg@DY>\z@
+      \advance\dg@ZTEMP\dg@DY \multiply\dg@YTEMP\dg@DY
+   \else \advance\dg@ZTEMP -\dg@DY \multiply\dg@YTEMP -\dg@DY\fi
+   \ifnum\dg@ZTEMP=\z@\else
+      \divide\dg@XTEMP\dg@ZTEMP \divide\dg@YTEMP\dg@ZTEMP
+      \advance\dg@XOFFSET\dg@XTEMP \advance\dg@YOFFSET\dg@YTEMP
+   \fi
+   % Divide by number of grid units to get unit size.
+   \divide\dg@XOFFSET\dg@SIZE \divide\dg@XOFFSET\dg@USERSIZE
+   \divide\dg@YOFFSET\dg@SIZE \divide\dg@YOFFSET\dg@USERSIZE
+   \ifnum\dg@DX=\z@ \dg@XOFFSET=\z@ \fi
+   \ifnum\dg@DY=\z@ \dg@YOFFSET=\z@ \fi
+   \ifnum\dg@XGRID<\dg@XOFFSET \global\dg@XGRID=\dg@XOFFSET\fi
+   \ifnum\dg@YGRID<\dg@YOFFSET \global\dg@YGRID=\dg@YOFFSET\fi
+   \relax}
+
+% Calculate all the arrow geometry parameters for drawing:
+%    X, Y, DX, DY, SIZE, XOFFSET, YOFFSET, VECTOR,
+%    XLBL, YLBL, XLBLOFF, YLBLOFF, LBLONE, LBLTWO,
+% from the (preliminary) values of these basic arrow parameters:
+%    X, Y, USERSIZE, SIZE, DX, DY, VECTOR, LBLPOS
+% using also the grid geom and diagram parameters:
+%    XGRID, YGRID, LBLOFF, SLIST, ARROWPARTS.
+% No args.  Uses temp vars XTEMP, YTEMP, ZTEMP, XEND, WEND.
+\def\dg@drawcalc{%
+   % Get size of node at other end of arrow (in picture coords).
+   \dg@XEND=\dg@SIZE \multiply\dg@XEND\dg@USERSIZE
+   \ifnum\dg@DX=\z@
+      \dg@YEND=\dg@XEND \dg@XEND=\z@
+      \dg@changesign\dg@YEND\dg@DY
+   \else
+      \dg@changesign\dg@XEND\dg@DX \dg@YEND=\dg@XEND
+      \multiply\dg@YEND\dg@DY \divide\dg@YEND\dg@DX
+   \fi
+   \advance\dg@XEND\dg@X \advance\dg@YEND\dg@Y
+   \dg@getnodesize
+      {\dg@SLIST}{\dg@XEND}{\dg@YEND}{\dg@WEND}{\dg@HEND}%
+   \divide\dg@WEND\unitlength \divide\dg@HEND\unitlength
+   % Now adjust arrow dir to grid aspect ratio (XGRID:YGRID).
+   \multiply\dg@DX\dg@XGRID \multiply\dg@DY\dg@YGRID
+   \dg@rmcommondiv\tw@\dg@DX\dg@DY
+   \dg@rmcommondiv\tw@\dg@DX\dg@DY %[sic]
+   \dg@rmcommondiv\thr@@\dg@DX\dg@DY
+   % Scale arrow to requested USERSIZE.
+   % Prepare to convert size from grid to picture coords.
+   \multiply\dg@SIZE\dg@USERSIZE \multiply\dg@SIZE\@m
+   \ifnum\dg@DX=\z@
+      %
+      % Vertical arrow.
+      %
+      % SIZE measures vertical extent in \unitlengths.  Arrow
+      % shortened by (HSOURCE+HTARGET)/2, offset by HSOURCE/2.
+      % Labels at (HSOURCE/2 + SIZE*LBLPOS/ARROWPARTS).
+      \multiply\dg@SIZE\dg@YGRID
+      \divide\dg@HEND\tw@ \advance\dg@SIZE -\dg@HEND
+      \dg@getnodesize
+         {\dg@SLIST}{\dg@X}{\dg@Y}{\dg@WEND}{\dg@YOFFSET}%
+      \divide\dg@YOFFSET\unitlength \divide\dg@YOFFSET\tw@
+      \advance\dg@SIZE -\dg@YOFFSET
+      \dg@XOFFSET=\z@
+      \def\dg@LBLONE{r}\def\dg@LBLTWO{l}%
+      \dg@XLBL=\z@ \dg@YLBL=\dg@SIZE
+      \multiply\dg@YLBL\dg@LBLPOS
+      \divide\dg@YLBL\dgARROWPARTS\relax
+      \advance\dg@YLBL\dg@YOFFSET
+      \dg@changesign\dg@YLBL\dg@DY
+      \dg@changesign\dg@YOFFSET\dg@DY
+   \else
+      % Nonvertical arrow.
+      % SIZE measures horizontal extent in \unitlengths.
+      \multiply\dg@SIZE\dg@XGRID
+      \ifnum\dg@DY=\z@
+         %
+         % Horizontal arrow.
+         %
+         % Arrow shortened by (WSOURCE+WTARGET)/2,
+         % and offset by WSOURCE/2.  Labels at
+         % (WSOURCE/2 + SIZE*LBLPOS/ARROWPARTS).
+         \divide\dg@WEND\tw@ \advance\dg@SIZE -\dg@WEND
+         \dg@getnodesize
+            {\dg@SLIST}{\dg@X}{\dg@Y}{\dg@XOFFSET}{\dg@HEND}%
+         \divide\dg@XOFFSET\unitlength \divide\dg@XOFFSET\tw@
+         \advance\dg@SIZE -\dg@XOFFSET
+         \dg@YOFFSET=\z@
+         \def\dg@LBLONE{b}\def\dg@LBLTWO{t}%
+         \dg@YLBL=\z@ \dg@XLBL=\dg@SIZE
+         \multiply\dg@XLBL\dg@LBLPOS
+         \divide\dg@XLBL\dgARROWPARTS\relax
+         \advance\dg@XLBL\dg@XOFFSET
+         \dg@changesign\dg@XLBL\dg@DX
+         \dg@changesign\dg@XOFFSET\dg@DX
+      \else
+         %
+         % Diagonal arrow.
+         %
+         % Arrow offset in its own direction, with horiz comp
+         % XOFFSET := MIN( WSOURCE/2, |DX/DY|*HSOURCE/2 ), and
+         % shortened in its own direction, with horiz comp
+         % XOFFSET + MIN( WTARGET/2, |DX/DY|*HTARGET/2 ).
+         % Labels at ( XOFFSET+-SIZE*LBLPOS/ARROWPARTS ,
+         %             YOFFSET+-SIZE*LBLPOS*DY/(ARROWPARTS*DX) )
+         \divide\dg@WEND\tw@ \divide\dg@HEND\tw@
+         \multiply\dg@HEND\dg@DX \divide\dg@HEND\dg@DY
+         \ifnum\dg@HEND<\z@ \multiply\dg@HEND\m@ne \fi
+         \ifnum\dg@WEND<\dg@HEND \advance\dg@SIZE -\dg@WEND
+         \else \advance\dg@SIZE -\dg@HEND \fi
+         \dg@getnodesize
+            {\dg@SLIST}{\dg@X}{\dg@Y}{\dg@WEND}{\dg@HEND}%
+         \divide\dg@WEND\unitlength \divide\dg@WEND\tw@
+         \divide\dg@HEND\unitlength \divide\dg@HEND\tw@
+         \multiply\dg@HEND\dg@DX \divide\dg@HEND\dg@DY
+         \ifnum\dg@HEND<\z@ \multiply\dg@HEND\m@ne \fi
+         \ifnum\dg@WEND<\dg@HEND \dg@XOFFSET=\dg@WEND
+         \else \dg@XOFFSET=\dg@HEND \fi
+         \advance\dg@SIZE -\dg@XOFFSET
+         \dg@changesign\dg@XOFFSET\dg@DX
+         \dg@YOFFSET=\dg@XOFFSET
+         \multiply\dg@YOFFSET\dg@DY \divide\dg@YOFFSET\dg@DX
+         \def\dg@LBLONE{br}\def\dg@LBLTWO{tl}%
+         \ifnum\dg@DX<\z@ \ifnum\dg@DY>\z@
+            \def\dg@LBLONE{bl}\def\dg@LBLTWO{tr}\fi\fi
+         \ifnum\dg@DX>\z@ \ifnum\dg@DY<\z@
+            \def\dg@LBLONE{bl}\def\dg@LBLTWO{tr}\fi\fi
+         \dg@XLBL=\dg@SIZE
+         \multiply\dg@XLBL\dg@LBLPOS
+         \divide\dg@XLBL\dgARROWPARTS\relax
+         \dg@changesign\dg@XLBL\dg@DX
+         \dg@YLBL=\dg@XLBL
+         \multiply\dg@YLBL\dg@DY \divide\dg@YLBL\dg@DX
+         \advance\dg@XLBL\dg@XOFFSET
+         \advance\dg@YLBL\dg@YOFFSET
+      \fi
+   \fi
+   % (XLBL,YLBL) is the displacement from the source NODE
+   % to the point along the arrow where the labels will be
+   % attached.  The offset of first label from this attachment
+   % point is the vector 
+   %    (XLBLOFF,YLBLOFF) = LBLOFF*sgn(DX)*(-DY,DX)/max(|DX|,|DY|)
+   % if DX is nonzero, and otherwise
+   %    (XLBLOFF,YLBLOFF) = LBLOFF*(-1,0);
+   % The 2nd label is offset by the vector (-XLBLOFF,-YLBLOFF).
+   \dg@XLBLOFF=-\dg@DY \dg@changesign\dg@XLBLOFF\dg@DX
+   \dg@YLBLOFF=\dg@DX \dg@changesign\dg@YLBLOFF\dg@DX
+   \ifnum\dg@DX=\z@ \dg@XLBLOFF=\m@ne \fi
+   \dg@XTEMP=\dg@DX \dg@changesign\dg@XTEMP\dg@DX
+   \dg@YTEMP=\dg@DY \dg@changesign\dg@YTEMP\dg@DY
+   \ifnum\dg@YTEMP>\dg@XTEMP \dg@XTEMP=\dg@YTEMP \fi
+   \ifnum\dg@XTEMP=\z@ \dg@XTEMP=\@ne \fi
+   \multiply\dg@XLBLOFF\dg@LBLOFF \divide\dg@XLBLOFF\dg@XTEMP
+   \multiply\dg@YLBLOFF\dg@LBLOFF \divide\dg@YLBLOFF\dg@XTEMP
+   %
+   % Change node location from grid coords to picture coords.
+   \multiply\dg@X\@m \multiply\dg@X\dg@XGRID
+   \multiply\dg@Y\@m \multiply\dg@Y\dg@YGRID
+   \relax}%
+
+%
+%
+% ---UTILITY ROUTINES---
+%
+% Remove common divisor DIV from two number registers
+% \NUM1 and \NUM2.
+% Usage: \dg@rmcommondiv{DIV}{\NUM1}{\NUM2}
+% NOTE: uses XTEMP and YTEMP.
+\def\dg@rmcommondiv#1#2#3{%
+   \dg@XTEMP=#2\relax
+   \divide\dg@XTEMP #1\relax \multiply\dg@XTEMP #1\relax
+   \dg@YTEMP=#3\relax
+   \divide\dg@YTEMP #1\relax \multiply\dg@YTEMP #1\relax
+   \ifnum\dg@XTEMP=#2\relax \ifnum\dg@YTEMP=#3\relax
+      \divide#2#1\relax \divide#3#1\relax \fi\fi}%
+
+% Multiply number register \NUM1 by sign of \NUM2.
+% Usage: \dg@changesign\NUM1\NUM2
+\def\dg@changesign#1#2{%
+   \ifnum #2<\z@ \multiply#1\m@ne
+   \else\ifnum #2=\z@ #1=\z@ \fi\fi}%
+
+% Determine from SLIST the padded width and height (in scaled
+%    points) of the node at (XCOORD,YCOORD).  Place the answer
+%    in number registers \WIDTH and \HEIGHT, resp (if no such
+%    node, set \WIDTH=\HEIGHT=0).  SLIST must be comma-separated
+%    list of TeX code segments, each of which puts the node
+%    coords in (XNODE,YNODE) and the node dims in(XTEMP,YTEMP).
+% Uses temp vars XTEMP, YTEMP.
+% Usage: \dg@getnodesize{SLIST}{XCOORD}{YCOORD}{\WIDTH}{\HEIGHT}
+\def\dg@getnodesize#1#2#3#4#5{%
+   #4=\z@\relax #5=\z@\relax
+   % loop through saved nodes
+   \expandafter\@for\expandafter\dg@trynode
+   \expandafter:\expandafter=#1\do{%
+      \dg@XNODE=\m@ne % impossible (in case \dg@trynode is empty)
+      \dg@trynode
+      \ifnum #2=\dg@XNODE \ifnum #3=\dg@YNODE
+         #4=\dg@XTEMP\relax #5=\dg@YTEMP\relax\fi\fi}}%
+
+% Like \makebox(0,0)[POS]{TEXT}, with POS expanded beforehand
+% and bottom alignment at true bottom rather than baseline.
+%
+% Usage: \dg@makebox[POS]{TEXT}
+\long\def\dg@sinkbaseline#1{%
+   \leavevmode\hbox{\vbox{%
+      \lineskiplimit=16383pt\relax\lineskip=0pt\relax
+      \baselineskip=-1000pt\relax
+      \parindent=0pt\relax\parskip=0pt\relax
+      \hbox{#1}\rule{0pt}{0pt}}}}%
+\newoptcommand{\dg@makebox}{}[2]{\dg@sinkbaseline{%
+   \expandafter\makebox\expandafter(\expandafter
+      0\expandafter,\expandafter0\expandafter)\expandafter
+      [#1]{#2}}}%
+
+% Produces nothing.
+% Usage:\dg@novector(X,Y){LaTeX_LENGTH}
+\def\dg@novector(#1,#2)#3{}%
+
+% \let command \CMD equal funny-name command \NAME.
+% Usage: \dg@letname{\CMD}{NAME}.
+\def\dg@letname#1#2{%
+   \relax\expandafter
+   \let\expandafter #1\csname #2\endcsname\relax}%
+
+% Arg handlers, one for each LBLTYPE.
+% Grab labels from following input and feed them to \PROCESS.
+% Usage: \dgl@LBLTYPE{\PROCESS}...
+\def\dgl@#1{#1{}{}}%
+\def\dgl@t#1#2{#1{#2}{}}%
+\def\dgl@b#1#2{#1{}{#2}}%
+\def\dgl@tb#1#2#3{#1{#2}{#3}}%
+\def\dgl@l#1#2{#1{#2}{}}%
+\def\dgl@r#1#2{#1{}{#2}}%
+\def\dgl@lr#1#2#3{#1{#2}{#3}}%
+
-- 
cgit v1.2.3

