summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/generic/mfpic/grafbase.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/generic/mfpic/grafbase.dtx')
-rw-r--r--Master/texmf-dist/source/generic/mfpic/grafbase.dtx5900
1 files changed, 5900 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/generic/mfpic/grafbase.dtx b/Master/texmf-dist/source/generic/mfpic/grafbase.dtx
new file mode 100644
index 00000000000..0eadd2885a9
--- /dev/null
+++ b/Master/texmf-dist/source/generic/mfpic/grafbase.dtx
@@ -0,0 +1,5900 @@
+% \iffalse
+% Copyright 2002--2003, Daniel H. Luecking
+%
+% Mfpic consists of the 3 files mfpic.dtx, grafbase.dtx and mfpic.ins
+% and the 5 files they generate: mfpic.tex, mfpic.sty, grafbase.mf,
+% grafbase.mp, and dvipsnam.mp.
+%
+% Mfpic may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License, either version 1.2 of this license or (at
+% your option) any later version. The latest version of this license is in
+%
+% http://www.latex-project.org/lppl.txt
+%
+% and version 1.2 or later is part of all distributions of LaTeX version
+% 1999/12/01 or later.
+%
+% Documentation, examples, and ancillary files are separate and not
+% covered by this license. See readme.1st for a complete list. See
+% individual files for any copyright and license restrictions.
+%
+% With respect to the proposed draft LPPL-1.3: mfpic has maintenance
+% status "maintained". The Current Maintainer is Daniel H. Luecking. There
+% are several Base Interpreters: TeX, LaTeX, MetaPost and Metafont.
+%
+%<*driver>
+\documentclass[draft]{ltxdoc}
+\usepackage{docmfp}
+\def\fileversion{0.7a beta}
+\def\filedate{2004/04/16}
+
+\addtolength{\textwidth}{.5878pt}
+
+\def\mytt{\upshape\mdseries\ttfamily}
+\renewcommand\marg[1]{{\mytt \{#1\}}}
+\renewcommand\oarg[1]{{\mytt [#1]}}
+\renewcommand\parg[1]{{\mytt (#1)}}
+\renewcommand{\meta}[1]{{$\langle$\rmfamily\itshape#1$\rangle$}}
+\DeclareRobustCommand\cs[1]{{\mytt\char`\\#1}}
+\def\prog#1{{\mdseries\scshape #1}}
+\def\grafbase{\prog{grafbase}}
+\def\Grafbase{\prog{Grafbase}}
+\def\mfpic{\prog{mfpic}}
+\def\Mfpic{\prog{Mfpic}}
+\def\MF{\prog{metafont}}
+\def\MP{\prog{metapost}}
+\def\CMF{\prog{Metafont}}
+\def\CMP{\prog{Metapost}}
+\def\opt#1{{\sffamily\upshape#1}}
+\def\env#1{{\mytt#1}}
+\let\file\env
+\let\mfc\env
+\let\gbc\env
+\renewcommand\{{\char`\{}
+\renewcommand\}{\char`\}}
+\renewcommand\|{${}\mathrel{|}{}$}
+
+\makeatletter
+\newcommand\bsl{{\mytt\@backslashchar}}
+% Stupid lists!
+\def\@listi{\leftmargin\leftmargini
+ \parsep \z@ \@plus\p@ \@minus\z@
+ \topsep 4\p@ \@plus\p@ \@minus2\p@
+ \itemsep\parsep}
+\let\@listI\@listi \@listi
+\renewcommand\labelitemi{\normalfont\bfseries \textendash}
+\renewcommand\labelitemii{\textasteriskcentered}
+\renewcommand\labelitemiii{\textperiodcentered}
+\leftmargini\parindent
+% Stupid index!
+\def\IndexParms{%
+ \parindent \z@ \columnsep 15pt
+ \parskip 0pt plus 1pt
+ \rightskip 5pt plus2em \mathsurround \z@
+ \parfillskip=-5pt \small
+ % less hanging:
+ \def\@idxitem{\par\hangindent 20pt}%
+ \def\subitem{\@idxitem\hspace*{15pt}}%
+ \def\subsubitem{\@idxitem\hspace*{25pt}}%
+ \def\indexspace{\par\vspace{10pt plus 2pt minus 3pt}}}
+\renewcommand\routinestring{}
+\renewcommand\variablestring{\space(var.)}
+% Why does every command have to be indexed twice?
+\renewcommand\SpecialMfpIndex[3]{\@bsphack
+ \index{%
+ \string#1\actualchar
+ \string\verb\quotechar*\verbatimchar\string#1\verbatimchar
+ #2 \encapchar usage}%
+ \@esphack}
+\makeatother
+
+\def\pdfTeX{\textrm{pdf\kern.04em\TeX}}
+\def\pdfLaTeX{\textrm{pdf\kern.06em\LaTeX}}
+\def\ConTeXt{\textrm{Con\kern-.16em\TeX\kern-0.06em t}}
+\def\PiCTeX{\textrm{P\kern-.13em\lower.3ex\hbox{I}C\TeX}}
+
+\title{The \grafbase{} macros\thanks{This file has version number
+ \fileversion, last revised \filedate. The code described here
+ was developed by several people, notably Thomas Leathrum,
+ Geoffrey Tobin and Dan Luecking. Dan wrote this documentation.}}
+\author{Dan Luecking}
+\date{\filedate}
+\DisableCrossrefs
+\CodelineIndex
+\AlsoImplementation
+
+\begin{document}
+ \DeleteShortVerb{\|}
+ \DocInput{grafbase.dtx}
+\end{document}
+%</driver>
+%\fi
+%
+% \CheckSum{1}
+% \CharacterTable
+% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
+% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
+% Digits \0\1\2\3\4\5\6\7\8\9
+% Exclamation \! Double quote \" Hash (number) \#
+% Dollar \$ Percent \% Ampersand \&
+% Acute accent \' Left paren \( Right paren \)
+% Asterisk \* Plus \+ Comma \,
+% Minus \- Point \. Solidus \/
+% Colon \: Semicolon \; Less than \<
+% Equals \= Greater than \> Question mark \?
+% Commercial at \@ Left bracket \[ Backslash \\
+% Right bracket \] Circumflex \^ Underscore \_
+% Grave accent \` Left brace \{ Vertical bar \|
+% Right brace \} Tilde \~}
+%
+% \catcode`\_=12
+% \maketitle
+%
+% \begin{abstract}
+% Macros are defined for use with \mfpic{}. The latter is a set of \TeX{}
+% macros which allows a \file{.tex} file to write a \file{.mf} or
+% \file{.mp} file that, with the help of these macros and \MF{} (or \MP),
+% can be used to create pictures in the document, especially mathematical
+% pictures. There are two versions of \grafbase, one for \MF{} and one for
+% \MP{}. As they are alike more than unlike, we document both here.
+%
+% This file documents the \grafbase{} source code. The user manual for
+% \mfpic{} is \file{mfpicdoc.tex}.
+% \end{abstract}
+%
+% \StopEventually{\PrintIndex}
+% \tableofcontents
+%
+%
+% \section{Introduction}\label{intro}
+%
+% \subsection{Identification and checks}\label{checks}
+%
+% We start with identifying information. Then we detect if grafbase was
+% loaded already, but we don't do anything about it except write a
+% message.
+%
+% The \gbc{grafbaseversion} is required to match the definition of
+% \cs{mfpicversion} in \file{mfpic.tex}. The output file written by the
+% \mfpic{} macros includes a test that these are the same, but that would
+% fail to catch a new \grafbase{} with an old \mfpic. So we also put a test
+% here, and it alone would fail to catch the use of older versions of
+% \grafbase{} with current versions of \mfpic. Newer versions of \mfpic{}
+% signal their version before inputting \file{grafbase}. Unfortunately
+% (for error checking), \grafbase{} can also be used without \mfpic, so if
+% \gbc{mfpicversion} is unknown, we merely write a message.
+%
+% It might be possible (at some point) to optimize things for \mfpic{} use
+% whenever \gbc{mfpicversion} is defined, but so far we don't do anything
+% except test the version and set this boolean.
+%
+% \DescribeRoutine{GBmsg}
+% \DescribeRoutine{GBerrmsg}
+% These are used fairly consistently and identify the source of the
+% message delivered as being \gbc{"Grafbase"}. The \gbc{GBerrmsg} takes
+% care of both the error message and the \mfc{errhelp} string.
+% \begin{macrocode}
+%<*MF|MP>
+string fileversion, filedate;
+fileversion := "0.7a beta"; filedate := "2004/04/16";
+def GBmsg expr s = message "Grafbase: " & s; enddef;
+def GBerrmsg (expr s) expr t =
+ errhelp t;
+ errmessage "Grafbase: "& s;
+ errhelp "";
+enddef;
+if (known grafbaseversion) or (known grafbase) :
+ GBmsg "You have loaded grafbase more than once! " &
+ "Please make sure that it is loaded only once.";
+ endinput;
+fi
+
+boolean grafbase; grafbase := true;
+boolean MFPIC; MFPIC := false;
+
+def checkversions (expr g)=
+ numeric grafbaseversion; grafbaseversion := g;
+ if unknown mfpicversion : % no mfpic, or < 0.63
+ GBmsg "Recent mfpic not detected.";
+ elseif g = mfpicversion :
+ MFPIC := true;
+ else:
+ GBerrmsg ("version mismatch")
+ "The installation may be broken: mfpic and grafbase " &
+ "versions do not match.";
+ fi
+enddef;
+
+checkversions (70);
+
+% \end{macrocode}
+% We try to make sure that the macros of \file{plain.mf} or
+% \file{plain.mp} are available.
+% \begin{macrocode}
+if unknown base_name : input plain;
+elseif not string base_name : input plain;
+elseif base_name <> "plain" : input plain;
+fi
+
+% \end{macrocode}
+%
+% We try to determine which of \MF{} or \MP{} is using these macros.
+% Perhaps one day we'll merge both versions of \grafbase{} in one file and
+% use the following boolean. For now, we only use it to catch cases where
+% the \grafbase{} file is being used by the wrong compiler.
+%
+% Of course, \MP{} natively knows about colors but \MF{} doesn't, so we
+% use that to set a boolean \gbc{METAPOST}. We don't simply check if
+% \mfc{known blue} is \mfc{true} because `\mfc{blue}' is certainly a legal
+% variable name in \MF. Instead we check \mfc{known color X} for
+% some unlikely \gbc{X}. In \MP, \gbc{color X} is either true or
+% false (\gbc{X} is a color or it isn't) and therefore always known, so
+% \gbc{known color X} is always true.
+%
+% In \MF{} \mfc{color X} is an identifier (presumably unknown) with the
+% base name \mfc{color} and suffix \mfc{X}.
+% \begin{macrocode}
+boolean METAPOST;
+
+if known color Maurits Cornelis Escher : METAPOST := true;
+else: METAPOST := false;
+fi
+
+%<*MF>
+if METAPOST :
+ GBerrmsg ("wrong compiler")
+ "You may have input to Metapost a file designed for Metafont. " &
+ "Instead of the file grafbase.mf, Metapost should be using " &
+ "grafbase.mp. Make sure Metapost can find it.";
+fi
+%</MF>
+%<*MP>
+if not METAPOST :
+ GBerrmsg ("wrong compiler")
+ "You may have input to Metafont a file designed for Metapost. " &
+ "Instead of the file grafbase.mp, Metafont should be using " &
+ "grafbase.mf. Make sure the extension was not changed.";
+fi
+%</MP>
+
+% \end{macrocode}
+%
+% \DescribeRoutine{GBdebug}
+% \DescribeRoutine{GBenddebug}
+% The \gbc{debug} flag is for developers, who should set it before
+% inputing \file{grafbase}. These two routines start and end debug
+% messages.
+%
+% \DescribeRoutine{mftitle}
+% The \gbc{mftitle} macro is useful when debugging.
+% It will put its argument, which should be a string, as a TFM comment,
+% and also print it to the terminal and log file.
+% \begin{macrocode}
+if (unknown debug) or (not boolean debug) :
+ boolean debug; debug := false;
+fi
+
+def GBdebug =
+ begingroup
+ save >>; def >> = message enddef;
+ >> "Grafbase DEBUG: ";
+enddef;
+def GBenddebug =
+ >> "End DEBUG";
+ endgroup
+enddef;
+
+vardef mftitle expr t =
+ t; message t;
+enddef;
+
+% \end{macrocode}
+%
+% \subsection{Setting up the font}\label{font}
+%
+% Font-related housekeeping is for \MF{} only. \MF{} only produces
+% fonts, so we have to define the variables it thinks are needed for
+% fonts.
+%
+% We intercept the \mfc{mode} variable before \mfc{mode_setup} can set
+% \mfc{proof} mode. We used to set \mfc{mode := cx} if it was unknown,
+% then for a while we just issued an error message; in this version we
+% try \mfc{ljfour}.
+%
+% The font identifier and coding scheme are just for information and end
+% up as comments in the \file{.tfm} file (in all capitals). The design
+% size just needs to be rather large for graphics, and \mfc{128pt\#} is
+% anyway the default if we didn't set it ourselves.
+% \begin{macrocode}
+%<*MF>
+if unknown mode :
+ GBerrmsg ("unknown Metafont mode")
+ "Please use \mode:=localfont; or a mode known on your " &
+ "system. If you continue, ljfour mode will be tried.";
+ mode := lfjour;
+fi
+
+mode_setup;
+if debug :
+ GBdebug;
+ >> "pixels_per_inch = " & decimal pixels_per_inch;
+ GBenddebug;
+fi
+font_identifier := "MFpic graphics";
+font_coding_scheme := "Arbitrary";
+interim designsize := 128pt#;
+
+%</MF>
+% \end{macrocode}
+%
+% \Mfpic-generated files make reference to \mfc{aspect_ratio} and
+% \mfc{pt\#}, while \MP{} has no need for them. Rather than make
+% \mfpic{} write different things, and to make the files intended for
+% \MF{} also work with \MP, we define them in the obvious way. We also
+% add a definition of \mfc{hppp} and \gbc{t_} to simplify maintenance of
+% two versions of the \file{grafbase} files. Then we define
+% \gbc{currenttransform} for \MP{} sake.
+% \begin{macrocode}
+%<MP>pt# := 1pt;
+%<MP>def t_ = transformed currenttransform enddef;
+if unknown aspect_ratio: aspect_ratio := 1; fi
+if unknown hppp : hppp := 1 fi;
+if unknown currenttransform :
+ transform currenttransform;
+ currenttransform := identity yscaled aspect_ratio;
+fi
+
+% \end{macrocode}
+%
+% Don't complain when variables get too large. For \MF{} this has to be
+% after \mfc{mode_setup}, which sets \mfc{warningcheck := 1}. Also don't
+% complain if a clockwise path is filled (\MF).
+% \begin{macrocode}
+%<MF>interim turningcheck := 0;
+interim warningcheck := 0;
+% \end{macrocode}
+%
+% \subsection{Initializations}\label{init}
+%
+% Picture size variables would normally be set by a user for each
+% picture, or by \mfpic, but we give them default values anyway.
+% \begin{macrocode}
+numeric unitlen, xscale, yscale, xneg, xpos, yneg, ypos;
+
+%<MF>unitlen := 1 bp#;
+%<MP>unitlen := 1 bp;
+xscale := 7.2; % (xscale * unitlen) = 1/10 inch
+yscale := 7.2; % (yscale * unitlen) = 1/10 inch
+xneg := 0; xpos := 10;
+yneg := 0; ypos := 10;
+
+% \end{macrocode}
+%
+% We support both degrees and radians for angles. In \MF, one degree is
+% the unit of angle. One radian is $180/\pi$ degrees. A user can say
+% \gbc{90} or \gbc{90deg} or \gbc{pi/2*radian} for the same effect.
+% \begin{macrocode}
+newinternal radian, pi, deg;
+deg := 1; pi := 3.14159;
+radian := 180/pi;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{resizedrawpen}
+% Since we need to do this frequently, we define a macro that changes the
+% pen width for subsequent drawing. This enables the file written by
+% \mfpic{} to be less cluttered. At least that was the original reason.
+% Now it gives us the opportunity to localize changes to \mfc{currentpen}
+% and \gbc{drawpen}. (We already had this for different
+% \gbc{beginmfpic}, since that reinitializes drawpen, but now it is local
+% to other groups as well.)
+
+% We could do this for the hatching pen, but it doesn't seem to change as
+% often. The \mfc{pickup} command performs \mfc{yscaled aspect_ratio}, but
+% so does the \gbc{shpath}, the only other place pens are required. In
+% fact, we wouldn't need to \mfc{pickup} the pen at all, except power
+% users may want to rely on \gbc{drawpen} always being the current pen. We
+% make its diameter \mfc{.5pt} for backward compatibility. But many
+% journal publisher (e.g., AMS) recommend no smaller than \mfc{.5bp} for
+% author-supplied drawings.
+%
+% The default \gbc{hatchwd} used to be larger, but it seemed ugly to me.
+% (Backward compatibility? What's that?).
+% \begin{macrocode}
+newinternal penwd; penwd := 0.5pt;
+pen drawpen;
+
+def resizedrawpen (expr s) =
+ interim penwd := s;
+ setvariable (pen) (drawpen) (pencircle scaled penwd);
+ save currentpen; pen currentpen; pickup drawpen;
+enddef;
+
+numeric hatchwd; hatchwd := 0.5bp;
+pen hatchpen; hatchpen := pencircle scaled hatchwd;
+
+% \end{macrocode}
+%
+% We have two booleans related to clipping. One, \gbc{clipall} is meant to
+% be turned on just once (per picture), and it causes the \gbc{endmfpic}
+% code to clip the current picture to the boundaries defined by the
+% picture size variables. The other, \gbc{ClipOn}, is meant to be turned
+% on and off. While on, most drawing macros (all?) will clip their result
+% to the current \emph{clipping path array}. The clipping path array is an
+% array of paths: \gbc{ClipPath[]} together with a numeric \gbc{ClipPath}.
+% The numeric variable contains the number of clipping paths; the paths
+% are \gbc{ClipPath[1]} through \gbc{ClipPath[ClipPath]}. A macro later on
+% is defined to loop through the array, clipping the current picture to
+% the union of their interiors.
+%
+% The \gbc{truebbox} boolean sets the bounding box of the picture to its
+% natural size in \MP. The default behavior of \MP{} is to output a
+% bounding box that is the natural size of the graphic. The \grafbase{}
+% default is to override this default, setting \gbc{truebbox} to
+% \mfc{false}. \CMF's default behavior is to force the user to specify the
+% bounding box, and provides no natural way to obtain any information
+% about the actual extent of the ink. So, for now, this boolean is only
+% for \MP.
+%
+% \DescribeRoutine{DoClip}
+% This is for the frequent conditional code to implement \gbc{ClipOn}.
+% The command \gbc{clipsto} is defined later.
+%
+% \DescribeRoutine{noclip}
+% For debugging we sometimes want to make sure something is drawn
+% without clipping being applied. For this we have \gbc{noclip}.
+% \begin{macrocode}
+boolean clipall; clipall := false;
+boolean ClipOn; ClipOn := false;
+path ClipPath[]; numeric ClipPath; ClipPath = 0;
+boolean truebbox; truebbox := false;
+
+def DoClip (suffix v) =
+ if ClipOn and (ClipPath > 0) : clipsto (v, ClipPath); fi
+enddef;
+
+def noclip (text t) =
+ hide( save ClipOn; boolean ClipOn; ClipOn := false; t)
+enddef;
+
+% \end{macrocode}
+%
+% The boolean \gbc{showbbox} is for debugging the \gbc{*bbox} macros.
+% \begin{macrocode}
+boolean showbbox; showbbox := false;
+
+% \end{macrocode}
+%
+% \subsubsection{Colors}\label{colors}
+%
+% Of course colors are only recognized by \MP. The colors \mfc{black},
+% \mfc{white}, \mfc{red}, \mfc{green} and \mfc{blue} are part of
+% \file{plain.mp}. We define other standard colors to get all eight
+% colors where the coordinates are 0 or 1.
+%
+% \DescribeRoutine{color}
+% We begin trying to merge the format of mfpic output files by defining
+% \MF{} replacements for some of the \MP{} color variables and macros. Our
+% point of view will be: make each color variable a numeric in \MF. Each
+% will lie between $0$ and $1$ representing shades of gray. For
+% \emph{drawing} commands we will only between nonwhite (black, ${}<1$) or
+% white (${}=1$). For filling commands we will allow levels in between,
+% and fill with an approximation using a version of \gbc{shade}
+% \begin{macrocode}
+%<*MF>
+let color = numeric; color black, white;
+black := 0; white := 1;
+def _wc_ = killtext enddef;
+%</MF>
+%<MP>def _wc_ = withcolor enddef;
+
+% \end{macrocode}
+%
+% We also define some color variables whose names reflect their use.
+% Thus, \gbc{fillcolor} is used for filling, etc. The color
+% \gbc{currentcolor} isn't used anywhere yet. The color
+% \mfc{background} is used in \MP{} for unfilling a region.
+% \begin{macrocode}
+color currentcolor, fillcolor, drawcolor, hatchcolor,
+ headcolor, pointcolor, tlabelcolor, background;
+currentcolor := fillcolor := drawcolor := hatchcolor :=
+ headcolor := pointcolor := tlabelcolor := black;
+background := white;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{snapto}
+% This truncates numerics to the $[0,1]$ range, but also returns a value
+% ($0$) for unknown and non-numeric input.
+% \begin{macrocode}
+vardef snapto expr t =
+ if unknown t : 0
+ elseif not (numeric t) : 0
+ elseif t < 0 : 0
+ elseif t > 1 : 1
+ else : t
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% The \mfpic{} handling of \LaTeX-like color models relies on being able
+% to convert those models to \MP's \opt{rgb} system. Because of the use of
+% \gbc{snapto}, the following color functions will return \mfc{black} for
+% unknown parameters. In the \MF{} case, they are all converted to
+% numerics through \gbc{makeclr}.
+%
+% \DescribeRoutine{gray}
+% The simplest is \gbc{gray} which converts a numeric to a multiple of
+% white. In \MF, \gbc{white} is a numeric and equal to $1$ so this is
+% almost redundant except for handling unknowns and out of range values.
+%
+% \DescribeRoutine{makeclr}
+% This is defined to convert a triple of numerics to a color, mainly for
+% \MF. The formula has three desirable properties: it weights the
+% different color coordinates approximately like some color luminescence
+% models do, it assigns different graylevels to the eight colors that have
+% components 0 or 1 only, and it is biased toward lighter grays. Of course
+% it takes \mfc{black} to 0 and \mfc{white} to 1. In \MP, it simply turns
+% three numeric parameters to a color triple in the obvious way.
+%
+% \DescribeRoutine{rgb}
+% To simplify \mfpic, we have the nearly redundant \gbc{rgb} which
+% converts a triple of numeric arguments to \opt{rgb}. Rather than make
+% it formally the identity function under \MP, we define it to handle
+% unknowns, and truncate out of range values.
+% \begin{macrocode}
+vardef gray (expr g) = (snapto g)*white enddef;
+
+vardef makeclr (expr r, g, b) =
+%<MF> gray (sqrt((2r*r + 4g*g + b*b)/7))
+%<MP> (r, g, b)
+enddef;
+
+vardef rgb (expr r, g, b) =
+ makeclr (snapto r, snapto g, snapto b)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{cmyk}
+% This algorithm for converting \opt{cmyk} values to \opt{rgb} values is
+% the one used in the PostScript header file \file{color.pro} (distributed
+% with \prog{dvips}).
+% \begin{macrocode}
+vardef cmyk (expr c, m, y, k) =
+ rgb (1-c-k, 1-m-k, 1-y-k)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{RGB}
+% This merely rescales numbers in the range 0--255 to the range 0--1.
+%
+% \DescribeRoutine{named}
+% \DescribeRoutine{forceclr}
+% These, too, are nearly redundant, but they convert numerics to gray, and
+% convert other non-color variables and unknown color variables to
+% black. The difference between \gbc{named} and \gbc{forceclr} is that the
+% former requires a suffix parameter, while the latter takes an
+% expression. It may be that the latter will never be needed, but for a
+% time it seemed there were cases where we ought to use it to force an
+% expression to be a color.
+% \begin{macrocode}
+vardef RGB (expr R, G, B) =
+ rgb (R/255, G/255, B/255)
+enddef;
+
+vardef named (suffix c) = forceclr (c) enddef;
+vardef forceclr (expr c) =
+ if unknown c : black
+ elseif numeric c : gray (c)
+ elseif color c : c
+ else : black
+ fi
+enddef;
+
+% \end{macrocode}
+% And then the standard colors. Using \gbc{rgb} ensures they are defined
+% in \MF{} as well as \MP.
+% \begin{macrocode}
+color red, green, blue, cyan, magenta, yellow;
+red := rgb (1, 0, 0);
+green := rgb (0, 1, 0);
+blue := rgb (0, 0, 1);
+cyan := rgb (0, 1, 1);
+magenta := rgb (1, 0, 1);
+yellow := rgb (1, 1, 0);
+
+% \end{macrocode}
+%
+% \subsection{Arrays}\label{arrays}
+%
+% \gbc{ClipPath} is a standard example of an array. It is based on the
+% fact that a variable can be of a different type from (and can be
+% almost completely unrelated to) the variables formed by putting numeric
+% suffixes on it.
+%
+% \DescribeRoutine{list}
+% The \gbc{list} macro is essentially due to Frank Michielsen, and assigns
+% a \emph{list} (i.e., a comma separated sequence of expressions) to an
+% array. Note that the items in the list have to be the same type, and the
+% same type as \mfc{v[]}. But \mfc{v} itself must be numeric.
+%
+% \DescribeRoutine{map}
+% The \gbc{map} macro takes two text parameters. The first is any
+% procedure, the second is a list of expressions. The procedure is applied
+% to each expression and the resulting new expressions are separated by
+% commas, that is, a new list is generated (for use in \mfc{for} loops).
+% This is full of possibilities for errors. One reared its head because
+% the original version started with a comma indicating an empty starting
+% expression (normally it would be ignored and that turn through the loop
+% skipped). However, it managed to produce an error in a reasonable
+% but unforeseen usage and so I added the \gbc{_map} variable that
+% skips the comma on the first time through the loop. This routine is
+% currently only used in the code \mfpic's \cs{plr} writes.
+% \begin{macrocode}
+vardef list (suffix v) (text lst) =
+ v := 0; for _itm = lst: v[incr v] := _itm; endfor
+enddef;
+
+def map (text proc) (text lst) =
+ hide(_map := 0;)
+ for _a = lst :
+ if _map = 0 : hide(_map := 1;) else: , fi
+ proc(_a)
+ endfor
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{knownarray}
+% Checks if a suffix is the name of an array. Requires \gbc{arr} to be a
+% known positive integer, and all the variables \gbc{arr[n]} to be known
+% for \gbc{n} from 1 to \gbc{arr}.
+% \begin{macrocode}
+vardef knownarray suffix arr =
+ save _kna; boolean _kna;
+ _kna := (known arr) and (numeric arr);
+ if _kna :
+ _kna := (arr = floor arr) and (arr >= 1);
+ for _idx = 1 upto arr :
+ exitif not _kna;
+ _kna := known arr[_idx];
+ endfor
+ fi
+ _kna
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{copyarray}
+% We only need this once, but it makes the code much more readable. It
+% simply steps through an array and copies the values into another array.
+% \begin{macrocode}
+def copyarray(suffix from, to) =
+ to := 0;
+ for _idx = 1 upto from:
+ to[incr to] := from[_idx];
+ endfor
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{maparr}
+% The \gbc{maparr} macro applies a procedure \gbc{proc} to each member of
+% array \gbc{p[]} with \gbc{p} members. It returns nothing, and currently
+% is unused. Though it could have been used for things like \gbc{maxpair}.
+% \begin{macrocode}
+def maparr (text proc) (suffix p) =
+ for _idx = 1 upto p: proc (p[_idx]); endfor
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{textpairs}
+% This macro takes a suffix (name of an array to be constructed) and a
+% list of pairs, and assigns them to the array. It is normally called from
+% another macro, which does any necessary \mfc{save}-ing of the variable
+% used for the array name. We used to include ``\mfc{save p;}'' in this
+% macro, but ran into a problem once when \mfc{p} had a suffix. You can't
+% apply \mfc{save} to a variable with a suffix. Moreover, ``\mfc{save p}''
+% also renders \mfc{p.x} unknown, so I judged it best to let whoever calls
+% this macro decide what to save.
+% \begin{macrocode}
+def textpairs (suffix p) (text t) =
+ numeric p; pair p[]; list (p) (t);
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{chpair}
+% This applies a procedure \gbc{proc} (which maps numeric to
+% numeric) to each part of pair \gbc{p}, and returns the resultant pair.
+%
+% \DescribeRoutine{floorpair}
+% \DescribeRoutine{ceilingpair}
+% \DescribeRoutine{hroundpair}
+% \gbc{floorpair}, \gbc{ceilingpair} and \gbc{hroundpair} use it with
+% \gbc{proc} equal to \mfc{floor}, \mfc{ceiling} and \mfc{hround}.
+% The last one is not defined in the \MP{} version of \grafbase{}
+% because \mfc{hround} is not defined (only \mfc{round}, which already
+% works on pairs). Actually, none of these is used any longer in
+% \file{grafbase.mp}.
+% \begin{macrocode}
+vardef chpair (text proc) (expr p) =
+ (proc (xpart p), proc (ypart p))
+enddef;
+
+vardef floorpair (expr p) = chpair (floor) (p) enddef;
+vardef ceilingpair (expr p) = chpair (ceiling) (p) enddef;
+%<MF>vardef hroundpair (expr p) = chpair (hround) (p) enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{emin}
+% \DescribeRoutine{emax}
+% These are more efficient versions of \prog{plain}'s \mfc{min} and
+% \mfc{max}: avoiding a \mfc{for} loop when only two values are compared.
+%
+% \DescribeRoutine{pairmin}
+% \DescribeRoutine{pairmax}
+% \gbc{pairmin} operates on two pairs, returning a pair having the
+% smaller of the two xparts and the smaller of the two yparts. Of course
+% \gbc{pairmax} is analogous, producing the maximum.
+%
+% \DescribeRoutine{minpair}
+% \DescribeRoutine{maxpair}
+% The \gbc{minpair} macro returns the pair comprising the minimum $x$ and
+% minimum $y$ coordinates of all pairs in the array \gbc{p[]}, where
+% \gbc{p} itself is a numeric count of the members in \gbc{p[]}.
+% \gbc{maxpair} is analogous. These operate by repeatedly calling
+% \gbc{pairmin} or \gbc{pairmax}.
+% \begin{macrocode}
+vardef emin (expr a, b) = if a < b : a else: b fi enddef;
+vardef emax (expr a, b) = if a > b : a else: b fi enddef;
+
+vardef pairmin (expr z, w) =
+ ( emin (xpart z, xpart w), emin (ypart z, ypart w ) )
+enddef;
+vardef pairmax (expr z, w) =
+ ( emax (xpart z, xpart w), emax (ypart z, ypart w ) )
+enddef;
+
+vardef minpair (suffix p) =
+ save _mp; pair _mp; _mp := p1;
+ for _idx = 2 upto p - 1 :
+ _mp := pairmin(_mp, p[_idx]);
+ endfor
+ pairmin (_mp, p[p])
+enddef;
+vardef maxpair (suffix p) =
+ save _mp; pair _mp; _mp := p1;
+ for _idx = 2 upto p - 1: _mp := pairmax(_mp, p[_idx]); endfor
+ pairmax (_mp, p[p])
+enddef;
+
+% \end{macrocode}
+%
+%
+% \section{The \grafbase{} coordinate system}\label{coordinate}
+%
+% We need to make a distinction between graph units, sharped units, and
+% device units. In \MF, a device unit is 1 pixel. On a LaserJet IV, one
+% inch is 600 pixels. When constructing a character, \MF{} uses the pixel
+% as its unit. Since this differs from one printing device to another,
+% \file{plain.mf} arranges for \emph{sharped} units (the name comes from the
+% convention that they are written using a name that ends in \mfc{\#}). The
+% dimension \mfc{1pt\#} in \MF{} is arbitrarily set to 1, and other
+% units defined by conversion factors (\mfc{in\#=72.27}; neither \MF{}
+% nor \MP{} makes a distinction between distances and numbers: \mfc{2pt}
+% just means \mfc{2} times the value of \mfc{pt}). When one needs to
+% draw something actually \emph{one point long}, then \mfc{1pt} is used.
+% It is defined to be equal \mfc{pt\#*hppp}, where \mfc{hppp} stands for
+% ``horizontal pixels per point'' and its value is usually set by
+% \mfc{mode_setup}. So \mfc{1pt} is $600/72.27$ (pixels) if
+% \mfc{mode} is \mfc{ljfour}.
+%
+% Often, when we want numbers not to become too large, we do calculations,
+% define paths, etc., in sharped units, then draw by scaling to device
+% units. In \grafbase{} we take this one step further: a horizontal graph
+% unit (i.e., the difference between the graph points (0, 0) and (1, 0))
+% represents \gbc{unitlen*xscale} sharped units, and
+% \gbc{unitlen*xscale*hppp} actual pixels. The \grafbase{} macros do much
+% of the calculations in graph units.
+%
+% In \MP, there is no difference between device and sharped units.
+% The \emph{postscript point} or \emph{big point} (1/72 inches) is the
+% unit in \MP: \mfc{bp = 1}.
+%
+% Some things need to be in graph units (for example, positions within a
+% graph defined by the user) or independent of units (standard shapes)
+% that scale appropriately when scales change. Other things (thickness of
+% lines) are a design decision that is either independent of scale, or
+% scales in a non-obvious way. The diameter of the drawing pen is one of
+% the latter things, so the default pen width is in device units. Also for
+% the hatching pen.
+%
+% When drawing a path we want to use device coordinates. When defining
+% paths, we typically want to use graph coordinates. The macros that do
+% the drawing, therefore, need to convert from one to the other. In
+% addition, for inclusion of the picture in a \TeX{} document, we normally
+% want the lower left corner of the graph space to have device coordinates
+% (0, 0).
+%
+% We therefore have two transforms: \gbc{vtr} is the \emph{vector} or
+% linear transform for pair quantities that remain invariant under shifts,
+% and \gbc{ztr} is a \emph{point} or affine transformation for pair
+% quantities that change appropriately under shifts.
+%
+% The quantities \gbc{xneg}, \gbc{xpos}, \gbc{yneg}, and \gbc{ypos} are
+% in \emph{graph} coordinates. Shifting by \gbc{(-xneg, -yneg)} transforms
+% the lower left corner to $(0, 0)$. Multiplication by \gbc{xscale} and
+% \gbc{yscale} converts to multiples of \gbc{unitlen} and multiplication
+% by \gbc{unitlen} gets us sharped coordinates. For \MF{},
+% multiplication by \mfc{hppp} converts to device coordinates, while for
+% \MP{} sharped and device are the same (the printer's PostScript
+% rasterizing engine -- or \prog{GhostScript} -- does the final conversion
+% to actual pixels).
+%
+% \mfc{currenttransform} (via the macro \mfc{.t_}, defined by
+% \mfc{mode_setup}) takes care of the aspect ratio.
+%
+% \gbc{charwd} and \gbc{charht} are sharped coordinates defined by the
+% startup code \gbc{beginmfpic}, and \gbc{w_} and \gbc{h_} are the
+% corresponding device (pixel) coordinates
+%
+% \DescribeRoutine{setztr}
+% This macro does the defining of \gbc{ztr} and \gbc{vtr}. It is called
+% by \gbc{beginmfpic}, at which time all the necessary quantities should be
+% known.
+% \begin{macrocode}
+transform ztr, vtr;
+def setztr =
+ if debug :
+ GBdebug;
+%<*MF>
+ >> "charwd = " & decimal charwd & "pt#";
+ >> "charht = " & decimal charht & "pt#";
+ >> "w_ = " & decimal w_ & " pixels";
+ >> "h_ = " & decimal h_ & " pixels";
+ >> "unitlen = " & decimal unitlen & "pt#";
+ >> "hppp = " & decimal hppp;
+%</MF>
+%<*MP>
+ >> "w_ = " & decimal w_ & "bp";
+ >> "h_ = " & decimal h_ & "bp";
+ >> "unitlen = " & decimal unitlen & "bp";
+%</MP>
+ >> "xneg = " & decimal xneg;
+ >> "xpos = " & decimal xpos;
+ >> "yneg = " & decimal yneg;
+ >> "ypos = " & decimal ypos;
+ >> "xscale = " & decimal xscale;
+ >> "yscale = " & decimal yscale;
+ GBenddebug;
+ fi
+ save ztr, vtr;
+ transform ztr, vtr;
+ vtr := identity xscaled (xscale) yscaled (yscale)
+ scaled (unitlen*hppp);
+ ztr := identity shifted (-(xneg, yneg)) transformed vtr;
+ if debug :
+ GBdebug;
+ >> "ztr: ";
+ show ztr;
+ >> "vtr: ";
+ show vtr;
+ GBenddebug;
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{zconv}
+% \DescribeRoutine{invzconv}
+% The macro \gbc{zconv} converts a variety of expressions from graph to
+% device coords. The expressions include pairs, paths, and transforms.
+% This is an affine transform. The inverse, \gbc{invzconv}, converts a
+% variety of expressions from device to graph coordinates.
+%
+% \DescribeRoutine{vconv}
+% \DescribeRoutine{invvconv}
+% The vector version, \gbc{vconv}, converts a vector \gbc{v} from graph to
+% device coordinates. This is a linear (ie, vector) transform. Finally,
+% \gbc{invvconv} converts a vector from device to graph coordinates.
+% \begin{macrocode}
+vardef zconv (expr a) = a transformed ztr enddef;
+vardef invzconv (expr a) = a transformed (inverse ztr) enddef;
+vardef vconv (expr v) = v transformed vtr enddef;
+vardef invvconv (expr v) = v transformed (inverse vtr) enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{active_plane}
+% \gbc{active_plane} is the active drawing plane. \mfc{currentpicture} is
+% unknown at this stage (because it's set in \gbc{beginmfpic}). We use a
+% \mfc{def}, and not a picture assignment, partly for this reason but also
+% because we can achieve special effects (see \gbc{image} below, and the
+% \gbc{tile} macro) by redefining it.
+%
+% \DescribeRoutine{image}
+% The \mfc{image} macro exists in \file{plain.mp} but not \file{plain.mf}.
+% The purpose is to just use the \file{plain} \MF{} and \grafbase{} macros
+% as you normally would, but wrap the whole thing in parentheses preceded
+% by \gbc{X := image} to get all those things drawn on the picture
+% variable \gbc{X}.
+% \begin{macrocode}
+def active_plane = currentpicture enddef;
+
+%<*MF>
+vardef image(text t) =
+ save currentpicture; picture currentpicture;
+ currentpicture := nullpicture;
+ t;
+ currentpicture
+enddef;
+
+%</MF>
+% \end{macrocode}
+%
+% \DescribeRoutine{initpic}
+% \gbc{initpic} is called by \gbc{beginmfpic} after \gbc{w_} and
+% \gbc{h_} are defined. At this point \gbc{xneg}, \gbc{xscale}, etc.,
+% have known values and \gbc{setztr} can define the transforms that are
+% based on them. Also, the default \gbc{drawpen} is initialized and the
+% boundary of the graph space is assigned to the clipping array.
+%
+% If \gbc{overlaylabels} is \gbc{true}, we try to make labels in \MP{}
+% behave the same as labels in \TeX{} (for \mfpic) by adding the labels
+% on last. We do this by adding them to the picture variable
+% \gbc{current_labels} as they occur, then add that picture onto
+% \gbc{active_plane} just before shipout. For backward compatibility,
+% the default for \gbc{overlaylabels} is \gbc{false}. We initialize
+% \gbc{current_labels} here. The pair variables \gbc{labelbb.ll} and
+% \gbc{labelbb.ur} keep track of the bounding box of added labels in case
+% \gbc{overlaylabels}, \gbc{truebbox}, and \gbc{clipall} are all
+% \gbc{false}.
+% \begin{macrocode}
+%<*MP>
+boolean overlaylabels;
+overlaylabels = false;
+
+%</MP>
+def initpic =
+ setztr;
+ resizedrawpen (penwd);
+ if ClipOn : ClipPath := 1;
+ ClipPath1 := rect (origin, (w_, h_));
+ fi
+ if debug :
+ GBdebug;
+ >> "Drawing nominal bounding box around picture";
+ GBenddebug;
+ noclip ( safedraw rect (origin, (w_, h_)) );
+ fi
+%<*MP>
+ save current_labels; picture current_labels;
+ current_labels := nullpicture;
+ save labelbb; pair labelbb.ll, labelbb.ur;
+ labelbb.ll := labelbb.ur := origin;
+%</MP>
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{mfpicenv}
+% \DescribeRoutine{endmfpicenv}
+% For compatibility with older \file{graphbase.mf} (for
+% \prog{fig2dev}'s \file{genmf.c}). Actually, I have no idea if
+% \prog{fig2dev} even works with the current \mfpic.
+% \begin{macrocode}
+def mfpicenv = enddef;
+def endmfpicenv = enddef;
+% \end{macrocode}
+% \DescribeRoutine{bounds}
+% This used to be for compatibility also, but I decided it was a
+% convenient abbreviation, so \mfpic{} uses it now.
+% \begin{macrocode}
+def bounds (expr a, b, c, d) =
+ xneg := a; xpos := b;
+ yneg := c; ypos := d;
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{setvariable}
+% This is mainly to save space in \mfpic-generated files.
+def setvariable (text kind) (suffix name) (expr value) =
+ save name; kind name; name := value;
+enddef;
+
+% \begin{macrocode}
+% \end{macrocode}
+%
+% \DescribeRoutine{beginmfpic}
+% This is the figure wrapper. \mfpic{} used to begin with figure 1 and
+% progressively increment the number. The current value of \gbc{gcode} was
+% always equal to the current figure number. Now, \mfpic{} explicitly
+% writes the figure number, so we assign \gbc{gcode} to that number in
+% case any old files made use of the current number through the
+% \gbc{gcode} variable.
+%
+% Originally, \gbc{beginmfpic} defined \mfc{w}, \mfc{h} and \mfc{d}, but
+% that caused problems if an \mfpic{} user tried to store a path in a
+% variable named \gbc{h}, etc. So now we use the less obvious names ending
+% in underscore. Apart from this, the code below is a clone of
+% \file{plain.mf}'s \mfc{beginchar} (for \MF). In fact, it used to invoke
+% \mfc{beginchar}. For \MP, we invoke \mfc{beginfig} explicitly. This does
+% the \mfc{clear...} actions and \mfc{charcode} assignment.
+%
+% The `\mfc{extra_...mfpic}' strings provide a compiler-independent way
+% to add to the extra beginning and ending tokens.
+% \begin{macrocode}
+string extra_beginmfpic; extra_beginmfpic := "";
+string extra_endmfpic; extra_endmfpic := "";
+
+def beginmfpic (expr ch) =
+%<MP> beginfig (ch);
+%<MF> begingroup
+ gcode := ch;
+ save w_, h_, d_; numeric w_, h_, d_;
+ charwd := (xpos-xneg)*xscale*unitlen;
+ charht := (ypos-yneg)*yscale*unitlen;
+ chardp := 0;
+%<*MF>
+ charcode := if known ch : byte ch else: 0 fi;
+ w_ := hround(charwd*hppp);
+ h_ := vround(charht*hppp);
+ d_ := vround(chardp*hppp);
+ charic := 0; clearxy; clearit; clearpen;
+ scantokens extra_beginchar;
+%</MF>
+%<*MP>
+ w_ := charwd;
+ h_ := charht;
+ d_ := chardp;
+%</MP>
+ initpic;
+ scantokens extra_beginmfpic;
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{endmfpic}
+% For \MF, we again clone \file{plain.mf}'s \mfc{endchar}, adding support
+% for the \gbc{clipall} (clip to the graph rectangle), and \gbc{ClipOn}
+% (clip to some user specified array of paths), and \gbc{showbbox} (draw
+% the boundary of the graph for debugging purposes).
+% \begin{macrocode}
+def endmfpic =
+ scantokens extra_endmfpic;
+ if debug :
+ GBdebug;
+%<MF> >> "TFM charwd = " & decimal charwd & "pt#";
+%<MF> >> "TFM charht = " & decimal charht & "pt#";
+%<MP> >> "width = " & decimal w_ & "bp";
+%<MP> >> "height = " & decimal h_ & "bp";
+ GBenddebug;
+ fi
+%<*MF>
+ if proofing>0 : makebox(proofrule); fi
+ chardx := w_; % desired width of character in pixels
+%</MF>
+ DoClip (active_plane);
+ if clipall : clipto (active_plane) rect(origin, (w_, h_)); fi
+ if showbbox : noclip ( safedraw rect (origin, (w_, h_)) ); fi
+%<*MF>
+ shipit;
+ if displaying > 0 : makebox(screenrule); showit; fi
+ endgroup
+%</MF>
+% \end{macrocode}
+%
+% \MP's code is more involved due to the possibility to put typeset text
+% in a picture. In addition to the \gbc{clipall}, \gbc{ClipOn} and
+% \gbc{showbbox} support, we have support for labels and \gbc{truebbox}.
+% \begin{macrocode}
+%<*MP>
+ save _ll, _ur;
+ pair _ll, _ur;
+ if truebbox :
+ _ll := llcorner active_plane;
+ _ur := urcorner active_plane;
+% \end{macrocode}
+% We try to let the bbox include labels, even when they extend beyond the
+% nominal picture boundaries. However, they will have been clipped off if
+% \gbc{clipall} is set. In that case, just set the bounding box to the
+% coordinates determined by \gbc{w_} and \gbc{h_}
+% \begin{macrocode}
+ elseif clipall:
+ _ll := origin;
+ _ur := (w_,h_);
+ else: % expand to accomodate labels
+ _ll := pairmin((0, 0 ), labelbb.ll);
+ _ur := pairmax((w_, h_), labelbb.ur);
+ fi
+% \end{macrocode}
+% A bounding box in the output PostScript code can have a side with
+% length 0 (e.g., a picture drawn with \mfpic{} that contains only
+% text placed by \TeX). This can cause division by 0 errors in some
+% contexts. That's why we don't just let \MP{} determine the bounding box,
+% but force the upper and lower coordinates to differ.
+% \begin{macrocode}
+ _ur := pairmax(_ur, _ll + eps*(1, 1));
+ setbounds active_plane to rect(_ll, _ur);
+% \end{macrocode}
+% If \gbc{overlaylabels} was true during a \gbc{newgblabel} command,
+% then \gbc{current_labels} contains that labels. We add them now, on top
+% of the picture. This might also extend the bbox, but that is an effect
+% we want to achieve.
+% \begin{macrocode}
+ addto active_plane also current_labels;
+ endfig;
+%</MP>
+enddef;
+
+% \end{macrocode}
+%
+%
+% \section{Text}\label{text}
+%
+% In the \MP{} version, \gbc{label_adjust} and \gbc{label_sep} are the
+% equivalent of \mfpic's \cs{tlabeloffset} and \cs{tlabelsep}. In the
+% \MF{} version they are still needed (in \gbc{textrect}, etc.) to place
+% the paths that are to surround the text that \TeX{} places.
+%
+% \gbc{label_adjust} is a vector displacement for the label,
+% while \gbc{label_sep} is the distance from the label to
+% the point of placement, when that point is on the edges of the label's
+% bounding box. Both are in device coordinates (e.g., \mfc{3bp}).
+% \begin{macrocode}
+pair label_adjust; label_adjust := (0, 0);
+numeric label_sep; label_sep := 0;
+
+% \end{macrocode}
+%
+% Another aspect of trying to make \mfpic's \file{.mp} and \file{.mf}
+% the same, we here define a version of \mfc{verbatimtex} for \MF. This
+% works only if \mfc{etex} is fillowed by a semicolon, and no semicolons
+% appear in the \TeX{} material. (There may be other forbidden things, and
+% certainly any parentheses have to be in matching pairs.) We would like
+% the output of \mfpic{} under the \opt{metapost} option to be usable in
+% \MF{} with minimal changes.
+% \begin{macrocode}
+%<MF>def verbatimtex = killtext enddef;
+
+% \end{macrocode}
+%
+% \subsection{Placement of text, \MP{} only}\label{placement}
+%
+% \DescribeRoutine{newgblabel}
+% \DescribeRoutine{gblabel}
+% This is how \mfpic{} places labels when \opt{mplabels} is in effect.
+% Since labels will typically be \mfc{btex...etex}, which are picture
+% expressions, it will actually place any picture, \gbc{s}. If you feed it
+% a string or path, it will convert it to a picture (with the \mfc{infont}
+% operator or the \gbc{picpath} macro). The first two parameters could easily
+% be condensed into one if \mfpic{} support were all that was required,
+% however I thought it best to generalize. The first two parameters
+% \gbc{hf} and \gbc{vf} are numeric. The \gbc{hf} represent the fraction of
+% the text that lies left of the point where the text is placed. Normally,
+% \gbc{vf} represents the fraction of text that lies below the point, but
+% if the third parameter is \mfc{true}, this fraction is relative to the
+% baseline (i.e., the depth is ignored). Currently we only use this with
+% \gbc{vf = 0} to get placement on the baseline. (Actually, \mfpic{} only
+% ever uses values of 0, .5 and 1 for \gbc{hf} or \gbc{vf}).
+%
+% The macro \gbc{newgblabel} takes 6 parameters. The first 3, as explained
+% above, effect the justification of the text (location of the point of
+% placement relative to the label). They correspond to the optional
+% parameter of \cs{tlabel} in \mfpic{} as follows:
+% \begin{itemize}
+% \item \gbc{hf} determines horizontal position: 0 = \texttt{l},
+% .5 = \texttt{c}, and 1 = \texttt{r}.
+% \item \gbc{vf} and \gbc{BL} determine vertical position. For placement
+% option \texttt{B}, \gbc{vf} = 0 and \gbc{BL} is \mfc{true}. For the
+% rest, \gbc{BL} is \mfc{false} and \gbc{vf} corresponds as follows:
+% 0 = \texttt{b}, .5 = \texttt{c} and 1 = \texttt{t}.
+% \item \gbc{r} is degrees of rotation about the specified point.
+% \item \gbc{s} is a string or picture expression (typically
+% \mfc{btex ... etex} code)
+% \item \gbc{pts} is a list of pairs in graph coordinates.
+% \end{itemize}
+% First the bounding box of the picture is extended by \gbc{label_sep} in
+% all directions by \gbc{labeldims}, then a new reference point for the
+% picture is calculated using
+% \DescribeRoutine{ref_shift}\gbc{ref_shift}
+% and then \gbc{thegblabel} rotates it around the reference
+% point and adds the \gbc{label_adjust}. Finally, for each \gbc{_itm} in
+% \gbc{pts}, the result is shifted by \gbc{_itm}. If \gbc{overlaylabels}
+% is true, the label is placed on the picture \gbc{current_labels} and
+% added to \gbc{active_plane} at \gbc{endmfpic}. Otherwise, it is added
+% directly to \gbc{active_plane} and and the \gbc{labelbb} are adjusted.
+%
+% We also use \gbc{ref_shift} in \MF{} since the curves that surround text
+% require it.
+%
+% We keep \gbc{gblabel} for backward compatibility with old \mfpic{}
+% files, but it merely calls \gbc{newgblabel}. While the old \gbc{gblabel}
+% had the same flexibility as \gbc{newgblabel}, this one assumes that the
+% parameters are only those that \mfpic{} would write. We provide a null
+% definition for \MF{} to allow \mfpic's \file{.mp} files to be usable with
+% minimal changes.
+% \begin{macrocode}
+%<MF>def newgblabel (expr hf, vf, BL, r) (text s) (text pts) = enddef;
+%<*MP>
+vardef newgblabel (expr hf, vf, BL, r) (expr s) (text pts) =
+ save _lab, _ll, _ur; picture _lab; pair _ll, _ur;
+ _lab :=
+ if picture s : s
+ elseif string s : s infont defaultfont scaled defaultscale
+ elseif path s : picpath (s)
+ else : nullpicture
+ fi;
+ labeldims (origin, _lab) (_ll, _ur);
+ _lab := thegblabel(ref_shift (hf, vf, BL, _ll, _ur), r, _lab);
+ save _b; pair _b;
+ for _itm = pts :
+ _b := zconv(_itm);
+ if overlaylabels :
+ addto current_labels also _lab shifted _b _wc_ tlabelcolor;
+ else:
+ addto active_plane also _lab shifted _b _wc_ tlabelcolor;
+ labelbb.ll := pairmin (_b + llcorner _lab, labelbb.ll);
+ labelbb.ur := pairmax (_b + urcorner _lab, labelbb.ur);
+ fi
+ endfor
+enddef;
+
+% Assumes a+b=1 and either c+d=1 or c=d=0:
+vardef gblabel (expr a, b, c, d, r) (expr s) (text t) =
+ newgblabel (b, d, (c = 0) and (d = 0), r) (s) (t);
+enddef;
+
+%</MP>
+vardef ref_shift (expr hf, vf, BL, ll, ur) =
+ - ( (hf)[xpart ll, xpart ur],
+ (vf)[if BL: 0 else: (ypart ll) fi, ypart ur] )
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{thegblabel}
+% When \gbc{thegblabel} is called by the above, \gbc{p} is a text picture,
+% but it is also called by the \gbc{textrect}, etc., in which case \gbc{p}
+% is a path. This is why it is needed in the \MF{} version.
+% \begin{macrocode}
+vardef thegblabel (expr z, r, p) =
+ ((p shifted z) rotated r) shifted label_adjust
+enddef;
+
+% \end{macrocode}
+%
+% \subsection{Decorating the text}\label{decorating}
+%
+% \DescribeRoutine{textrect}
+% \DescribeRoutine{textoval}
+% \DescribeRoutine{textellipse}
+% The three macros \gbc{textrect}, \gbc{textoval} and \gbc{textellipse}
+% are designed to surround a bit of text with some curve. These macros
+% return the path in graph coordinates. In the first one, the path is a
+% rectangle with rounded corners. The second parameter, \gbc{rad}, is the
+% radius of quarter circles at the corners (in device units). In the other
+% two cases, the path is an ellipse. They differ in the meaning of the
+% second parameter.
+%
+% In \gbc{textoval}, the second parameter \emph{multiplies} the ratio of
+% width to height of the text to produce the ratio for the ellipse. Thus,
+% with \gbc{mult}=1, the ratio will be the same as that of the text. In
+% \gbc{textellipse}, the second parameter \gbc{rat} is the actual value of
+% the ratio of width to height of the ellipse and a value of 1 produces a
+% circle. In either macro, if that parameter is 0, we draw a rectangle.
+%
+% The size of each path is determined so that, when the text is placed and
+% the path drawn, it passes through the four corners of the following
+% rectangle: the rectangle which just encloses the text plus the amount of
+% space on all sides determined by \gbc{label_sep}. Note that this means
+% a rectangle with rounded corners will have larger height and width than
+% one without.
+%
+% The placement of each path is: centered at the point given in the third
+% parameter \gbc{loc}, shifted by the vector specified in \gbc{label_adjust}.
+%
+% The first parameter \gbc{lbl} is either a pair representing the
+% height and width of the text (only possibility in \MF) or the actual
+% text. These macros are being kept for backward compatibity, but now they
+% call the extended versions that allow the path to follow arbitrary
+% text placement. The parameters \gbc{(.5,.5,false,0)} were those
+% assumed in the past version: centered at the point, with no rotation.
+%
+% The extended versions of \gbc{textoval} and \gbc{textellipse} are both
+% now implemented in a single command \gbc{xellipse}, with a boolean to
+% specify whether the aspect ratio of the text is used to calculate the
+% aspect of the ellipse.
+% \begin{macrocode}
+vardef textrect (expr lbl, rad, loc) =
+ textrectx (.5, .5, false, 0) (origin, lbl, rad, loc)
+enddef;
+vardef textoval (expr lbl, mult, loc) =
+ xellipse (true, .5, .5, false, 0) (origin, lbl, mult, loc)
+enddef;
+vardef textellipse (expr lbl, rat, loc) =
+ xellipse (false, .5, .5, false, 0) (origin, lbl, rat, loc)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{textrectx}
+% \DescribeRoutine{textovalx}
+% \DescribeRoutine{textellipsex}
+% These are extended versions of the previous three. They will now be
+% able adjust the position of the path in the same manner as \gbc{newgblabel}
+% does the text. In fact, they calculate the position in exactly the same
+% manner as that macro, and the first 4 parameters encode that in the same
+% way.
+%
+% \gbc{lbl} is either the upper right corner of the text or the label
+% itself. In the first case \gbc{xy} is the lower left corner, in the
+% second case it is a dummy parameter, the bounding box being obtained (in
+% \gbc{labeldims}) by measuring the label. For these extended macros, the
+% parameters \gbc{lbl}, \gbc{mult}, \gbc{rad}, and \gbc{loc} are as in
+% the unextended versions.
+%
+% \gbc{roundends} is a boolean. We really only need it to be a type
+% distinguishable from any numeric value. \Mfpic{} users can specify it
+% rather than an explicit radius, and when the code of \gbc{textrectx}
+% detects this, it uses the maximum radius for the corners (making the
+% short side of the `rectangle' a semicircle). That is, if \gbc{rad} is
+% a boolean (and \mfc{true}) then the radius at the corners is so chosen.
+% (If \gbc{rad} is \mfc{false} the corners are not rounded at all.)
+% \begin{macrocode}
+boolean roundends; roundends := true;
+vardef textrectx (expr a, b, c, rot, xy, lbl, rad, loc) =
+ save ll, ur, _r, f, zz;
+ pair ll, ur, zz; path f;
+ labeldims (xy, lbl) (ll, ur);
+ _r :=
+ if boolean rad :
+ if rad : emin (xpart (ur-ll), ypart (ur-ll))/sqrt(2)
+ else: 0
+ fi
+ elseif numeric rad : rad
+ else: 0
+ fi;
+ if _r = 0 :
+ f := rect(ll, ur);
+ else:
+ save p, q;
+ pair p[]; path q;
+ p1 := ur - _r*dir(45); % center of upper right arc
+ p3 := ll + _r*dir(45); % lower left
+ p2 := (xpart p3, ypart p1); % upper left
+ p4 := (xpart p1, ypart p3); % lower right
+ q := quartercircle scaled 2_r;
+ if _r > 0:
+ f := (q shifted p1) -- (q rotated 90 shifted p2)
+ -- (q rotated 180 shifted p3)
+ -- (q rotated -90 shifted p4)
+ -- cycle;
+ else:
+ f := (q shifted p1) -- (q rotated -90 shifted p4)
+ -- (q rotated 180 shifted p3)
+ -- (q rotated 90 shifted p2)
+ -- cycle;
+ fi
+ fi
+ invvconv(thegblabel(ref_shift(a, b, c, ll, ur), rot, f)) shifted loc
+enddef;
+
+% \end{macrocode}
+%
+% As the coding of \gbc{textoval} and \gbc{textellipse} was refined, it
+% turned out that each refinement in one suggested a similar change in the
+% other. In the end the two differed only in two lines, so now both call
+% another macro \gbc{xellipse}.
+% \begin{macrocode}
+def textovalx = xellipse (true) enddef;
+def textellipsex = xellipse (false) enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{xellipse}
+% In \gbc{xellipse}, \gbc{aa} and \gbc{bb} are the horizontal and
+% vertical radii of the resulting ellipse, while \gbc{ww} and \gbc{hh}
+% are half the width and height size of the text. If the boolean
+% \gbc{aspect} is true, the aspect ratio of the ellipse (i.e., \gbc{aa/bb})
+% equals \gbc{mult*hh/ww}, otherwise it equals \gbc{mult}.
+% \begin{macrocode}
+vardef xellipse (expr aspect, a, b, c, r, xy, lbl, mult, loc) =
+ if mult = 0 :
+ textrectx (a, b, c, r) (xy, lbl, 0, loc)
+ else:
+ save ll, ur, cc, ww, hh, f;
+ pair ll, ur, cc; path f;
+ labeldims (xy, lbl) (ll, ur);
+ cc := .5[ll, ur]; % center
+ (ww, hh) = ur - cc;
+ if (ww = 0) or (hh = 0) : % make a line:
+ f = (ll--ur);
+ else:
+ save aa, bb, mm;
+ mm := if aspect : ww/hh*mult else: mult fi;
+ aa := ww ++ hh*mm;
+ bb := aa/mm;
+ f := ellipse(cc, aa, bb, 0);
+ fi
+ invvconv(thegblabel (ref_shift (a, b, c, ll, ur), r, f)) shifted loc
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{labeldims}
+% This has been changed to make the code of \mfpic{} a bit simpler and
+% to aid in backward compatibility. It takes a couple of pairs (the actual
+% or nominal label bounding box corners) or something visible (picture,
+% string or path) and assigns suitable values to \gbc{ll} and \gbc{ur}
+% then expands the bbox by \gbc{label_sep}.
+% \begin{macrocode}
+
+def labeldims (expr xy, lbl) (suffix ll, ur) =
+ if pair lbl :
+ ll := xy; ur := lbl;
+ else:
+%<MF> ll := ur := (0, 0);
+%<*MP>
+ save _lbl; picture _lbl;
+ _lbl :=
+ if picture lbl : lbl
+ elseif string lbl :
+ lbl infont defaultfont scaled defaultscale
+ elseif path lbl : picpath (lbl)
+ else : nullpicture
+ fi;
+ ll := llcorner _lbl;
+ ur := urcorner _lbl;
+%</MP>
+ fi
+ ll := ll - label_sep*(1, 1);
+ ur := ur + label_sep*(1, 1);
+enddef;
+
+% \end{macrocode}
+%
+%
+% \section{Additional functions}\label{functions}
+%
+% Complex variable functions are provided, which interpret a pair $(x, y)$
+% as the complex number $z = x + iy$. We also provide for the use of
+% radians, add the standard exponential and logarithms, and add the
+% hyperbolic functions and their inverses.
+%
+% The value \gbc{eps/2 + epsilon} is the smallest value with
+% reciprocal less than \mfc{infinity}. I set \gbc{nottoosmall} a speck
+% bigger to ensure that the same is true of \gbc{2*(nottoosmall/2)}.
+%
+% Normally \mfc{infinity = 2**12 - epsilon} is the largest number allowed
+% (as a value involved in actual drawing in \MF). Since we set
+% \mfc{warningcheck=0}, values not assigned to a variable and not
+% written to the \file{.tfm} file (and any value in \MP) can be as high as
+% \mfc{2**15 - epsilon}, which is a speck smaller than \mfc{1/(2epsilon)}.
+% So \gbc{reallysmall} is to be the smallest number whose reciprocal is a
+% usable number. (\mfc{epsilon} is the smallest possible positive number
+% in \MF.)
+%
+% We set \gbc{secd x = 1/(cosd x)} unless \gbc{cosd x} is less than ``really
+% small'', then we set it equal to \gbc{1/reallysmall}. We do a similar
+% thing with \gbc{cscd}.
+%^^A
+% \DescribeRoutine{TruncateWarn}
+% (When such a substitution happens \gbc{TruncateWarn} prints a message
+% that a truncation has taken place.)
+%
+% Why not just determine what number will produce arithmetic overflow and
+% test for that? Because I'm lazy: it would require a different number
+% for each of the functions. Instead, since \MF{} has no ``arithmetic
+% underflow'', I compute something that is guaranteed to work and occurs
+% in the formula for the function as a reciprocal (e.g., $t = e^{-|x|}$
+% for \gbc{cosh x}) and make sure the number is not too small to take its
+% reciprocal.
+%
+% \DescribeRoutine{signof}
+% This expands to a minus sign if its argument is negative, otherwise
+% nothing.
+% \begin{macrocode}
+newinternal nottoosmall; nottoosmall := eps/2 + 2epsilon;
+newinternal reallysmall; reallysmall := 3epsilon;
+def signof (expr X) = if X < 0 : - fi enddef;
+def TruncateWarn expr s = GBmsg s & " too large; truncating"; enddef;
+
+% \end{macrocode}
+% In addition to \mfc{sind} and \mfc{cosd} which take angles in degrees,
+% we define the remaining trig functions \gbc{tand}, \gbc{cotd},
+% \gbc{secd}, and \gbc{cscd}.
+%
+% \DescribeRoutine{secd}
+% \DescribeRoutine{tand}
+% \DescribeRoutine{cscd}
+% \DescribeRoutine{cotd}
+% We define \gbc{secd}, one of the simplest, to include an ``out of range''
+% test (which also prevents division by 0). Then \gbc{tand} can make use
+% of it without any division. We do the same with \gbc{cscd} and
+% \gbc{cotd}.
+% \begin{macrocode}
+vardef secd primary X =
+ save temp; temp := cosd(X);
+ if abs(temp) < reallysmall :
+ TruncateWarn "Secant";
+ temp := signof (temp) reallysmall;
+ fi
+ 1/temp
+enddef;
+vardef tand primary X = sind(X)*secd(X) enddef;
+
+vardef cscd primary X =
+ save temp; temp := sind(X);
+ if abs(temp) < reallysmall :
+ TruncateWarn "Cosecant";
+ temp := signof(temp) reallysmall;
+ fi
+ 1/temp
+enddef;
+vardef cotd primary X = cosd(X)*cscd(X) enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{acos}
+% \DescribeRoutine{asin}
+% \DescribeRoutine{atan}
+% These are the inverse functions, which return an angle in degrees.
+% \begin{macrocode}
+vardef acos primary X = angle (X, 1 +-+ X) enddef;
+vardef asin primary X = angle (1 +-+ X, X) enddef;
+vardef atan primary X = angle (1, X) enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{sin}
+% \DescribeRoutine{cos}
+% \DescribeRoutine{tan}
+% \DescribeRoutine{cot}
+% \DescribeRoutine{sec}
+% \DescribeRoutine{csc}
+% Now the trig functions that take angles in radians.
+% \begin{macrocode}
+vardef sin primary X = sind (X*radian) enddef;
+vardef cos primary X = cosd (X*radian) enddef;
+vardef tan primary X = tand (X*radian) enddef;
+vardef cot primary X = cotd (X*radian) enddef;
+vardef sec primary X = secd (X*radian) enddef;
+vardef csc primary X = cscd (X*radian) enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{invsin}
+% \DescribeRoutine{invcos}
+% \DescribeRoutine{invtan}
+% And the inverses that return angles in radians.
+% \begin{macrocode}
+vardef invcos primary X = (acos X)/radian enddef;
+vardef invsin primary X = (asin X)/radian enddef;
+vardef invtan primary X = (atan X)/radian enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{exp}
+% \DescribeRoutine{ln}
+% \DescribeRoutine{log}
+% \DescribeRoutine{logbase}
+% \DescribeRoutine{logtwo}
+% \DescribeRoutine{logten}
+% Here we define the standard exponential function. (The \MF{} function
+% \mfc{mexp} has the unusual base $e^{1/256}$ to avoid overflow.) The
+% inverse of \gbc{exp} is the natural logarithm (\gbc{ln} or \gbc{log}).
+% We also have the general base logarithm \gbc{logbase} and its two
+% special instances \gbc{logtwo} and \gbc{logten}.
+% \begin{macrocode}
+vardef exp primary X = mexp (256 * X) enddef;
+vardef ln primary X = (mlog X) / 256 enddef;
+def log = ln enddef;
+vardef logbase (expr B) primary X = (mlog X)/(mlog B) enddef;
+def logtwo = logbase( 2) enddef;
+def logten = logbase(10) enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{Arg}
+% \DescribeRoutine{Log}
+% \DescribeRoutine{cis}
+% \DescribeRoutine{zexp}
+% \DescribeRoutine{sgn}
+% \CMF's pair variables are a decent replacement for complex variables.
+% These give some of the more basic functions of standard complex
+% analysis.
+% \begin{macrocode}
+vardef Arg primary Z = (angle Z)/radian enddef;
+vardef Log primary Z = (ln(abs(Z)), Arg (Z)) enddef;
+vardef cis primary T = dir(radian*T) enddef;
+vardef zexp primary Z = (exp (xpart Z))*(cis(ypart Z)) enddef;
+vardef sgn primary Z =
+ if Z = origin : origin else: unitvector Z fi
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{cosh}
+% \DescribeRoutine{sinh}
+% \DescribeRoutine{tanh}
+% \DescribeRoutine{sech}
+% \DescribeRoutine{csch}
+% \DescribeRoutine{coth}
+% The hyperbolic functions.
+% \begin{macrocode}
+vardef cosh primary X =
+ save temp; temp := 2 exp (-abs(X));
+ if temp < reallysmall :
+ TruncateWarn "Cosh";
+ temp := reallysmall;
+ fi
+ 1/temp + temp/4
+enddef;
+
+vardef sinh primary X =
+ save temp; temp := 2 exp (-abs(X));
+ if temp < reallysmall :
+ TruncateWarn "Sinh";
+ temp := reallysmall;
+ fi
+ signof (X) (1/temp - temp/4)
+enddef;
+
+vardef sech primary X =
+ save temp; temp := exp(-(abs (X)));
+ 2temp/(1 + temp*temp)
+enddef;
+
+vardef tanh primary X =
+ save temp; temp := exp(-2(abs (X)));
+ signof (X) (1 - temp)/(1 + temp)
+enddef;
+
+vardef csch primary X =
+ save temp; temp := exp(-(abs (X)));
+ if abs(1 - temp*temp) < reallysmall :
+ TruncateWarn "Csch";
+ signof (X) 2temp / reallysmall
+ else:
+ signof (X) 2temp / (1 - temp*temp)
+ fi
+enddef;
+
+vardef coth primary X =
+ save temp; temp := tanh(X);
+ if abs(temp) < reallysmall :
+ TruncateWarn "Coth";
+ temp := signof (temp) reallysmall;
+ fi
+ 1/temp
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{acosh}
+% \DescribeRoutine{asinh}
+% \DescribeRoutine{atanh}
+% The inverses of some of the hyperbolic functions.
+% \begin{macrocode}
+vardef acosh primary y =
+ if y < 1 :
+ GBerrmsg ("Undefined function: acosh " & decimal y)
+ "If you proceed, a value of 0 will be used. " &
+ "Expect more errors later.";
+ 0
+ else:
+ ln (y + (y+-+1))
+ fi
+enddef;
+
+vardef asinh primary y = ln (y + (y++1)) enddef;
+
+vardef atanh primary y =
+ if abs (y) < 1 :
+ (ln(1+y) - ln(1-y))/2
+ else:
+ GBerrmsg ("Undefined function: atanh " & decimal y)
+ "If you proceed, a value of plus or minus infinity " &
+ "will be used. Expect more errors later.";
+ signof (y) infinity
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{polar}
+% \DescribeRoutine{id}
+% \gbc{polar} converts a polar coordinate pair $(r, \theta)$ to the
+% corresponding rectangular coordinate pair.
+%
+% \gbc{id} returns its argument, which can be any expression of any type.
+% \begin{macrocode}
+vardef polar (expr p) = (xpart p) * dir (ypart p) enddef;
+def id (expr x) = x enddef;
+
+% \end{macrocode}
+%
+%
+% \section{Coordinate systems and transformations}\label{systems}
+%
+% \DescribeRoutine{T_push}
+% \DescribeRoutine{T_pop}
+% \DescribeRoutine{bcoords}
+% \DescribeRoutine{ecoords}
+% We want to define a localization of the ``current transform''. To do
+% this we define a LIFO stack of transforms \gbc{T_stack[]}, a pair of
+% macros \gbc{T_push} puts its argument (a transform) on the stack, and
+% \gbc{T_pop} pops it off into its argument (a transform variable name).
+% We also define two localizing macros \gbc{bcoords} that pushes our
+% \gbc{ztr} and \gbc{vtr} on the stack, and \gbc{ecoords} that pops them
+% off.
+% \begin{macrocode}
+transform T_stack[]; T_stack := 0;
+def T_push (expr T) = T_stack[incr T_stack] := T; enddef;
+def T_pop (suffix $) =
+ if T_stack > 0 :
+ $ := T_stack[T_stack]; T_stack := T_stack - 1;
+ fi
+enddef;
+
+def bcoords = hide ( T_push (ztr); T_push (vtr) ) enddef;
+def ecoords = hide ( T_pop (vtr); T_pop (ztr) ) enddef;
+
+% \end{macrocode}
+%
+% \subsection{Coordinate changes}\label{changes}
+%
+% \DescribeRoutine{apply_t}
+% Here we define a mechanism for changing \gbc{ztr} and \gbc{vtr} by
+% composing them with a new transform. Since a transform can be any affine
+% transform, we get \gbc{ztr} by composing with the transform, but we
+% calculate \gbc{vtr} from \gbc{ztr} by arranging that \mfc{origin
+% transformed vtr} is \mfc{origin}. The syntax is \gbc{apply_t(rotated
+% theta)} or \gbc{apply_t(transformed T)} if \mfc{T} is a variable or
+% expression of type transform. Thus the argument of \gbc{apply_t} is
+% phrase which, were it to follow a path, would produce a transformed
+% path. Knuth calls such a phrase a \emph{transformer}.
+% \begin{macrocode}
+def apply_t (text Transformer) =
+ ztr := identity Transformer transformed ztr;
+ vtr := ztr shifted - zconv(origin);
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{xslant}
+% \DescribeRoutine{yslant}
+% \DescribeRoutine{zslant}
+% \DescribeRoutine{xyswap}
+% \DescribeRoutine{boost}
+% And now we define some available transformers. The only two that
+% need comment are \gbc{zslant} and \gbc{boost}. I know that boost comes
+% from special relativity, but I have no idea what zslant is about.
+% \begin{macrocode}
+def xslant = slanted enddef; % (x+sy, y).
+def yslant primary s = % (x, y+sx).
+ transformed
+ begingroup
+ save _T; transform _T;
+ origin transformed _T = origin;
+ (1, 0) transformed _T = (1, s);
+ (0, 1) transformed _T = (0, 1);
+ _T
+ endgroup
+enddef;
+
+def zslant primary p = % (xu+yv, xv+yu), where p = (u, v).
+ transformed
+ begingroup
+ save _T; transform _T;
+ xpart _T = ypart _T = 0;
+ xxpart _T = yypart _T = xpart p;
+ xypart _T = yxpart _T = ypart p;
+ _T
+ endgroup
+enddef;
+
+def xyswap = zslant (0, 1) enddef;
+def boost primary X = zslant (cosh X, sinh X) enddef;
+
+% \end{macrocode}
+%
+% \subsection{Path transformation}\label{transformation}
+%
+% These are functions that accept a path and return a path in graph
+% coordinates. For the most part they are named and defined to
+% apply a similarly named transform to the path and return the result.
+% There are two exceptions. When we draw things, we expect that rotated
+% and reflected objects appear congruent to the originals. If we define a
+% path in graph coordinates, and the x and y directions are scaled
+% differently, then simply rotating the graph coordinates will distort
+% angles. The same is true of reflection. Therefore, we apply \gbc{vtr}
+% (so we are in drawing coordinates) then rotate, then apply \gbc{inverse
+% vtr}. This may be a mistake, or perhaps we should do it for all of
+% these. For now, I'm sticking with the scheme I inherited. One can
+% always use \gbc{coords} and \gbc{apply_t} if one wants the difference in
+% scales ignored.
+%
+% \DescribeRoutine{rotatedpath}
+% This returns the path rotated around point \gbc{p} by angle
+% \gbc{th} in degrees.
+%
+% \DescribeRoutine{scaledpath}
+% This returns the path scaled so that distances from the point
+% \gbc{p} are multiplied by \gbc{s}.
+%
+% \DescribeRoutine{xslantedpath}
+% This returns the path x-slanted with line $y = {}$\gbc{b}
+% being the pivot rather than the x-axis.
+%
+% \DescribeRoutine{yslantedpath}
+% This returns the path y-slanted with line $x = {}$\gbc{a}
+% being the pivot rather than the y-axis.
+%
+% \DescribeRoutine{xscaledpath}
+% This returns the path scaled so that vertical distances
+% from the line $y={}$\gbc{a} are multiplied by \gbc{s}.
+%
+% \DescribeRoutine{yscaledpath}
+% This returns the path scaled so that vertical distances
+% from the line $x={}$\gbc{b} are multiplied by \gbc{s}.
+%
+% \DescribeRoutine{shiftedpath}
+% This returns the path shifted by the vector (pair) \gbc{v}.
+%
+% \DescribeRoutine{reflectedpath}
+% This returns the path relected about the line through the
+% points \gbc{p} and \gbc{q}.
+%
+% \DescribeRoutine{xyswappedpath}
+% Finally, this returns the path in which all coordinates
+% have had the coordinates exchanged $(a, b) \to (b, a)$. Note that this
+% is not the same as \gbc{reflectedpath ((0, 0), (1, 1))}, as it performs the
+% reflection in graph coordinates, as its name implies. If \gbc{vtr} has
+% not been changed (by \gbc{apply_t}) then \gbc{xyswappedpath} will
+% convert vertical lines to horizontal and vice versa. The
+% \gbc{reflectedpath} version will not when x and y are scaled differently,
+% for then the line \gbc{(0, 0)--(1, 1)} is not at a 45 degree angle in
+% device coordinates where drawing takes place.
+% \begin{macrocode}
+vardef rotatedpath (expr p, th) expr f =
+ f transformed vtr rotatedaround (p transformed vtr, th)
+ transformed (inverse vtr)
+enddef;
+vardef scaledpath (expr p, s) expr f =
+ f shifted -p scaled s shifted p
+enddef;
+vardef xslantedpath (expr b, s) expr f =
+ f shifted (0, -b) slanted s shifted (0, b)
+enddef;
+def slantedpath = xslantedpath enddef;
+vardef yslantedpath (expr a, s) expr f =
+ f shifted (-a, 0) yslant s shifted (0, a)
+enddef;
+vardef xscaledpath (expr a, s) expr f =
+ f shifted (-a, 0) xscaled s shifted (a, 0)
+enddef;
+vardef yscaledpath (expr b, s) expr f =
+ f shifted (0, -b) yscaled s shifted (0, b)
+enddef;
+vardef shiftedpath (expr v) expr f = f shifted v enddef;
+vardef reflectedpath (expr p, q) expr f =
+ f transformed vtr
+ reflectedabout (p transformed vtr, q transformed vtr)
+ transformed (inverse vtr)
+enddef;
+vardef xyswappedpath expr f = f xyswap enddef;
+vardef transformedpath (text Transformer) expr f =
+ f Transformer
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{partialpath}
+% \DescribeRoutine{gsubpath}
+% It seems odd, in retrospect, that we got by with a user interface that
+% didn't include any subpath operations. But recently a user asked for the
+% ability to add an arrowhead to the \emph{middle} of a path, and it
+% seemed best to provide a subpath and use existing commands to add an
+% arrowhead on its end. This macro takes two fractions $\alpha$ and
+% $\beta$ between 0 and 1, and a path \gbc{f}, and returns the subpath
+% from $\alpha * {} $\meta{length of \gbc{f}} to $\beta * {}$\meta{length
+% of \gbc{f}} of \gbc{f}. \gbc{gsubpath} is the same as \MF's subpath
+% primitive, but follows the prefix macro syntax of accepting a path
+% expression (rather than a primary) and wrapping the result in a
+% \mfc{vardef}.
+% \begin{macrocode}
+vardef partialpath (expr a, b) expr f =
+ save p; path p;
+ p := zconv (f) scaled (1/unit_of_length);
+ save cumlen, totlen, idx, ta, tb;
+ totlen := makelengtharray(cumlen) p;
+% \end{macrocode}
+% \gbc{idx} holds the current index into the array \gbc{cumlen[]}. The
+% code of \gbc{gettime} is optimized for sorted lengths. If we always
+% found \gbc{ta} first, we'd have to re-initialize \gbc{idx} in case
+% \gbc{tb < ta}, i.e., search from the beginning again.
+% \begin{macrocode}
+ idx := 0;
+ if a <= b:
+ ta := gettime (cumlen, idx) (a*totlen);
+ tb := gettime (cumlen, idx) (b*totlen);
+ else:
+ tb := gettime (cumlen, idx) (b*totlen);
+ ta := gettime (cumlen, idx) (a*totlen);
+ fi
+ subpath (ta, tb) of f
+enddef;
+vardef gsubpath (expr a, b) expr f = subpath (a, b) of f enddef;
+
+% \end{macrocode}
+%
+%
+% \section{Picture level operations}\label{picture}
+%
+% None of these operations are available in \MP. Mostly these are used by
+% higher level operations. Those higher level operations are available in
+% \MP, but need to be defined differently.
+%
+% \subsection{Bitwise logical operations}\label{logical}
+%
+% We have two types of operations. One type is a binary operator that
+% takes two picture expressions and returns a picture, the other type
+% returns nothing, but merely modifies a given picture variable. These
+% take the name of a picture and a picture expression and modify the named
+% one. The binary operators are not used elsewhere in graphbase except
+% for \gbc{picsub}, which occurs in \gbc{picneg} and \gbc{shadepic}.
+%
+% \DescribeRoutine{mono}
+% Here we define the bitwise logical operations: and, or, xor, and
+% difference. These mostly only work if all pixels have values 0 or 1.
+% Since \MF{} allows other integer values, we define a \gbc{mono} operator
+% that converts all pixels with weight ${}\ge 1$ to 1 and all pixels
+% with weight ${}\le 0$ to 0.
+% \begin{macrocode}
+%<*MF>
+def mono (suffix u) = cull u keeping (1, infinity); enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{andto}
+% \DescribeRoutine{picand}
+% The bitwise and: in the resulting picture, a pixel is \emph{on} if and
+% only if it is \emph{on} in both \gbc{u} and \gbc{v}.
+% \begin{macrocode}
+def andto (suffix u) (expr v) =
+ mono (u); addto u also v; cull u keeping (2, 2);
+enddef;
+primarydef u picand v =
+ begingroup save t; picture t;
+ t := u; andto (t, v); t
+ endgroup
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{orto}
+% \DescribeRoutine{picor}
+% The inclusive or: in the result, a pixel is \emph{on} if and only if it
+% is \emph{on} in \gbc{u} or \gbc{v} or both.
+% \begin{macrocode}
+def orto (suffix u) (expr v) =
+ mono (u); addto u also v; cull u keeping (1, 2);
+enddef;
+primarydef u picor v =
+ begingroup save t; picture t;
+ t := u; orto (t, v); t
+ endgroup
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{xorto}
+% \DescribeRoutine{picxor}
+% The exclusive or, also called the symmetric difference:
+% in the result, a pixel is \emph{on} if and only if it is \emph{on} in
+% \gbc{u} or \gbc{v}, but not both. These are not used elsewhere in
+% \grafbase.
+% \begin{macrocode}
+def xorto (suffix u) (expr v) =
+ mono (u); addto u also v; cull u keeping (1, 1);
+enddef;
+primarydef u picxor v =
+ begingroup save t; picture t;
+ t := u; xorto (t, v); t
+ endgroup
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{subto}
+% \DescribeRoutine{picsub}
+% The nonsymmetric difference: in the result, a pixel is \emph{on} if
+% and only if it is \emph{on} in \gbc{u} and off in \gbc{v}.
+% \begin{macrocode}
+def subto (suffix u) (expr v) =
+ mono (u); addto u also -v; cull u keeping (1, 1);
+enddef;
+primarydef u picsub v =
+ begingroup save t; picture t;
+ t := u; mono (t); subto (t, v); t
+ endgroup
+enddef;
+
+%</MF>
+% \end{macrocode}
+%
+% \subsection{Producing and modifying pictures}
+%
+% Here we define some slightly higher level commands that make use (in \MF)
+% of the previous bitmap operations. In \MP, they mostly need different
+% definitions, but we have merged most of them by providing a \MP{}
+% alternative for the most frequently used bitmap operation in the
+% previous section, \gbc{orto}. These operations either return a picture
+% or modify a picture variable. They do not draw anything unless
+% \gbc{active_plane} is the modified picture. All curves, points,
+% dimension, etc., are in device coordinates.
+%
+% \DescribeRoutine{coloraddto}
+% This has become a useful abbreviation. In \MF{} it adds when the color
+% is not white, subtracts when it is. Grays are handles in \MF{} by
+% appropriate preparation of \gbc{u} and \gbc{v}. See, for example, the
+% code of \gbc{colorsafefill}. In \MP{} it is an abbreviation for the
+% basic \mfc{addto} operation, and is defined only so that \MP{} and \MF{}
+% can share the same higher level code.
+% \begin{macrocode}
+def coloraddto (expr clr) (suffix u) (expr v) =
+%<*MF>
+ if clr < white :
+ orto (u, v);
+ else:
+ subto (u) (v);
+ fi;
+%</MF>
+%<MP> addto u also v _wc_ clr;
+enddef;
+
+%<MP>def orto (suffix u) (expr v) = addto u also v; enddef;
+%<MP>
+% \end{macrocode}
+%
+% \DescribeRoutine{interior}
+% This takes the following expresion, \gbc{c}, which must be a
+% closed path, and returns the picture expression which is that path
+% filled. The cull command (\MF{} only) retains negative pixels
+% (converting them to positive). This way, clockwise contours are filled
+% also. \gbc{interior} is one of the most used commands throughout the
+% rest of \grafbase.
+%
+% We ignore color (new behavior), since the higher level commands now
+% implement the coloring operations.
+% \begin{macrocode}
+vardef interior expr c =
+ save v; picture v; v := nullpicture;
+ addto v contour (c.t_);
+%<MF> cull v dropping (0, 0);
+ v
+enddef;
+% \end{macrocode}
+%
+% \DescribeRoutine{interiors}
+% This is followed by the name of an array of closed paths and
+% returns the picture of the interiors of those closed paths. It builds
+% the returned picture from \mfc{nullpicture} by successively adding
+% the result of \gbc{interior} applied to each path in the array. This is
+% only used once by \grafbase, in \gbc{clipsto}, which might be a better
+% place to put the \mfc{for}-loop and not use this at all.
+% \begin{macrocode}
+vardef interiors suffix cc =
+ save _ints; picture _ints; _ints := nullpicture;
+ for _idx = 1 upto cc:
+ addto _ints also interior cc[_idx]);
+ endfor
+%<MF> mono (_ints);
+ _ints
+enddef;
+
+% \end{macrocode}
+%
+% \subsection{Clipping}\label{basicclipping}
+%
+% \DescribeRoutine{clipto}
+% \gbc{clipto} takes the name of a picture \gbc{vt} and a closed path
+% \gbc{c} and modifies the picture leaving only the part inside the path.
+% In \MP{} we just invoke the \mfc{clip} primitive.
+%
+% \DescribeRoutine{clipsto}
+% This is similar, except it takes an array of paths \gbc{cc} and
+% leaves what is interior to any of the paths. This is one case where
+% \MP{} requires a substantially different point of view. In \MF, we
+% create the interiors and `and' the result to the named picture. In \MP,
+% we have to create the picture which is \gbc{vt} clipped to each separate
+% path, and combine the results. \Grafbase{} only uses this in the
+% \gbc{DoClip} command.
+% \begin{macrocode}
+def clipto (suffix vt) expr c =
+%<MF> andto (vt, interior c);
+%<MP> clip vt to c;
+enddef;
+def clipsto (suffix vt, cc) =
+%<MF> andto (vt, interiors cc);
+%<*MP>
+ begingroup
+ save _cl, _cl_; picture _cl, _cl_; _cl_ := nullpicture;
+ for _idx = 1 upto cc:
+ _cl := vt; clip _cl to cc[_idx]; addto _cl_ also _cl;
+ endfor
+ vt := _cl_;
+ endgroup
+%</MP>
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{Clipped}
+% Here, rather than modify a given picture, \gbc{Clipped} is a vardef
+% returning the picture which is the result of clipping the given picture
+% to the path.
+%
+% Having found out that \mfc{clipped} is a \MP{} primitive, I've
+% changed the name to the uppercase version, but keep the lowercase
+% version for now (backward compatibility). We save the primitive
+% meaning in \gbc{clipped_}. We also define \gbc{clip} in \MF{} for
+% backward compatibility.
+% \begin{macrocode}
+vardef Clipped (suffix vt) expr c =
+ save _Cl; picture _Cl; _Cl := vt; clipto (_Cl) c; _Cl
+enddef;
+%<MP>let clipped_ = clipped;
+def clipped = Clipped enddef;
+%<MF>def clip = Clipped enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{picneg}
+% The reverse video is easy in \MF, where \gbc{picneg} takes a picture
+% name and a closed path, and returns the part of the picture inside the
+% path, but with pixels reversed. In \MP{} we can only approximate this:
+% we clip the given picture and add that (using color \gbc{background})
+% on top of the \gbc{interior} of the curve. This is not used elsewhere
+% in \file{grafbase.mp} so it is not really important if \gbc{fillcolor}
+% or \mfc{black} is the right choice.
+% \begin{macrocode}
+vardef picneg (suffix vt) expr c =
+%<*MF>
+ mono (vt);
+ (interior c) picsub vt
+%</MF>
+%<*MP>
+ save _pn; picture _pn; _pn := nullpicture;
+ addto _pn (interior c) _wc_ fillcolor;
+ addto _pn also (Clipped (vt) c) _wc_ background;
+ _pn
+%</MP>
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{shpath}
+% \gbc{shpath} does most of the work of drawing curves in \grafbase. It is
+% called by \gbc{safedraw} which is used by almost all the commands that
+% somehow draw a curve. It takes the name of a picture, a pen expression
+% and a path expression. It draws the path on the picture with the pen.
+% Since we use this (ultimately) for almost all drawing of paths, we
+% automatically have the aspect ratio taken care of by the \mfc{.t_}
+% macro.
+%
+% \DescribeRoutine{picpath}
+% \gbc{picpath} accepts a path expression and returns a picture, which is
+% either \gbc{nullpicture} (\gbc{penwd} too small) or the path drawn with
+% \gbc{drawpen}. This is mostly how \gbc{shpath} gets used: curve drawing
+% commands produce a picture with \gbc{picpath} and that gets used.
+%
+% \begin{macrocode}
+def shpath (suffix v) (expr q, f) =
+ addto v doublepath (f.t_) withpen (q.t_);
+enddef;
+
+numeric minpenwd;
+%<MF>minpenwd := 1; % 1 pixel
+%<MP>minpenwd := .05bp; % 1 pixel at 1440dpi
+vardef picpath expr d =
+ save v; picture v; v := nullpicture;
+ if penwd >= minpenwd :
+ shpath (v, drawpen) (d);
+%<MF> mono (v);
+ fi
+ v
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{picdot}
+% This places a specified picture expression (\gbc{w}) at a specified
+% location (\gbc{p}) in a specified picture variable (\gbc{v}). It is used
+% a number of places. It's \MF{} version takes care of the aspect ratio
+% via \mfc{.t_}. This is how we draw points and symbols and dots along a
+% curve: make the symbol into a picture \gbc{w} and add that picture with
+% \gbc{picdot}.
+% \begin{macrocode}
+def picdot (suffix v) (expr w, p) =
+ addto v also
+%<MP> (w shifted p);
+%<MF> (w shifted hroundpair (p.t_));
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{setdot}
+% \gbc{setdot} is named for its use rather than what it does. It takes a
+% path and a scale (numeric expression) and returns a picture which is a
+% drawing of the filled interior of the path (if it is a cycle) or the
+% path itself (not a cycle). In \MF, we ensure that the scale is at least
+% one pixel (assumes that the \gbc{apath} has dimension about 1 and
+% \gbc{minpenwd} is 1). This usually assures that something is drawn. In
+% \MP, \gbc{minpenwd} has the same purpose (though it is probably not
+% necessary). This routine is used a number of times where dots are
+% needed. Not in \gbc{shaded} (just below) but later in \gbc{shade}, an
+% older command taking paths in graph coordinates.
+% \begin{macrocode}
+vardef setdot (expr apath, sc) =
+ if cycle apath : interior
+ else : picpath
+ fi
+%<MF> (apath scaled emax(ceiling (sc), minpenwd))
+%<MP> (apath scaled emax(sc, minpenwd))
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{shadepic}
+% We want to shade regions with a very regular pattern of black and white
+% pixels for best appearance. Experiments show that symmetric dots
+% (e.g., circles, squares) work better than non-symmetric (e.g.,
+% rectangular). Circular dots are not significantly better than square at
+% the size needed. I believe that the default result of \gbc{shade} looks
+% reasonably good on my system. (That happens to produce two 3-pixel by
+% 3-pixel square dots in a 8-pixel square on a 360dpi printer.) So we try
+% to produce something similar. That is, the shading picture is 1.6bp
+% (8 pixels at 360dpi) square.
+%
+% As a compromise (symmetric dots look better, but rectangular dots give
+% more gray levels) we allow dots to be $k\times (k+1)$-pixels (assuming
+% the aspect ratio is 1) rectangles. This produces twice the number of
+% gray levels. In my 360dpi example we get 15 gray levels. The two
+% farthest apart (4 by 4 dots versus 3 by 4 dots) differ by 1/8 in
+% fraction of area of coverage (which we equate to grayness).
+%
+% The parameter \gbc{dims} needs to be a pair variable, and it will be
+% assigned the actual dimensions of the picture returned. These routines
+% are complicated by the fact that we may have an aspect ratio unequal to
+% $1$. When \mfc{aspect_ratio = 1} the basic concept is simple: make an
+% $n\times n$ square with two dots, each nearly $k \times k$ and nearly
+% square, where $2k^2/n^2$ is the gray level needed.
+%
+% First \gbc{dims} is equated to half the size needed, the dot is created
+% (\gbc{_shp}) and then the dot is repeated and \gbc{dims} is doubled.
+% Finally, for dark gray (gray levels less than .5) we calculate the shade
+% picture for \gbc{1 - greylevel} and subtract it from a black square.
+% \begin{macrocode}
+%<*MF>
+numeric shadepicsize; shadepicsize := 0.8bp;
+vardef shadepic (suffix dims) (expr grparam) =
+ pair dims;
+ save _frac; _frac := 2*emin(grparam, 1 - grparam);
+ save _hp, _vp, _dotwd, _dotht;
+ if aspect_ratio < 1 :
+ _vp := emax (2, hround(shadepicsize.o_));
+ _hp := hround (_vp._o_);
+ _dotwd := hround (_hp*sqrt _frac);
+ _dotht := if _dotwd = 0 : 0
+ else: hround (_hp*_vp*_frac/_dotwd)
+ fi;
+ else:
+ _hp := emax (2, hround (shadepicsize));
+ _vp := hround (_hp.o_);
+ _dotht := hround (_vp*sqrt _frac);
+ _dotwd := if _dotht = 0 : 0
+ else: hround (_hp*_vp*_frac/_dotht)
+ fi;
+ fi
+ dims := ( _hp, _vp._o_ );
+ save _shp; picture _shp; _shp := nullpicture;
+ addto _shp contour rect ((0,0), (_dotwd, _dotht));
+ picdot (_shp, _shp, dims);
+ dims := 2dims; mono (_shp);
+ if grparam >= .5 : _shp
+ else : (interior (rect ((0,0), dims))) picsub _shp
+ fi
+enddef;
+
+%</MF>
+% \end{macrocode}
+%
+% \DescribeRoutine{shaded}
+% This fills the interior of a contour (device coordinates) with copies of
+% \gbc{shadepic}. The routine \gbc{filledwith} is defined later, but its
+% name reflects its effect: a bounding rectangle (corners at \gbc{ll} and
+% \gbc{ur}) is filled with copies of a picture (\gbc{shpic} in this case),
+% the picture having nominal dimensions \gbc{shdims} in this case.
+%
+% It may seem odd that black and white return the same thing. That is
+% because white is handled in the calling routine by subtracting the
+% result.
+%
+% The \gbc{gbbox} command is defined in the next section. The bounding
+% rectangle it obtains is only approximate in \MF{}, but that is
+% sufficient, since we only use it to produce things that are eventually
+% clipped.
+%
+% I am not sure why we return \gbc{picpath} for non-cycles, but I think
+% I once thought to make \gbc{shaded} a replacement for setdot to get gray
+% dots (in the \gbc{polkadot} routine).
+% \begin{macrocode}
+vardef shaded (expr clr) expr c =
+ if cycle c :
+%<*MP>
+ save v; picture v;
+ v := nullpicture;
+ addto v contour c _wc_ clr;
+ v
+%</MP>
+%<*MF>
+ if (clr <= black) or (clr >= white) :
+ interior c
+ else:
+ save vsh, shpic, shdims, ll, ur;
+ picture vsh, shpic; pair shdims, ll, ur;
+ shpic := shadepic (shdims) (clr);
+ gbbox (c, ll, ur);
+ vsh := filledwith (shpic) (shdims, ll, ur);
+ Clipped (vsh) c
+ fi
+%</MF>
+ else: picpath c % should we? or just make it null?
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{filledwith}
+% This is one of the ways we obtain something other than a solid fill. The
+% routines \gbc{polkadot}, \gbc{tess} and (in \MF) \gbc{shade} and
+% \gbc{shaded} all use it.
+%
+% It takes a picture expression, along with its dimensions (the pair
+% \gbc{dims}) in device coordinates, plus the opposite corners, \gbc{ll}
+% and \gbc{ur}, of a boundingbox rectangle, and returns the picture which
+% is that rectangle filled with copies of the picture.
+%
+% One might do this with two nested loops, but it turns out to be much
+% (much!) faster to do two separate loops: the second one stacking the row
+% built by the first loop.
+%
+% We try to do any rounding that might have been forgotten. This code
+% takes a mode's aspect ratio into account so that (most) calling routines
+% don't have to. (That is, \gbc{dims} should be measured in horizontal
+% pixels, while \gbc{fwdims} is in actual pixels. I would have written
+% this in terms of \gbc{picdot}, which already handles aspect, but it
+% has got to be more efficient to do the aspect ratio calculations
+% once rather than every time through the loop.)
+% \begin{macrocode}
+vardef filledwith (expr pic, dims, ll, ur) =
+ save b, v; picture b, v;
+ b := v := nullpicture;
+%<*MF>
+ save fwdims, _ll, _ur; pair fwdims, _ll, _ur;
+ fwdims := hroundpair (dims.t_);
+ _ll := floorpair (ll.t_);
+ _ur := ur.t_;
+ for s = xpart _ll step xpart fwdims until xpart _ur:
+ addto b also pic shifted (s, 0);
+ endfor
+ for s = ypart _ll step ypart fwdims until ypart _ur:
+ addto v also b shifted (0, s);
+ endfor
+ mono (v);
+%</MF>
+%<*MP>
+ for s = xpart ll step xpart dims until xpart ur:
+ addto b also pic shifted (s, 0);
+ endfor
+ for s = ypart ll step ypart dims until ypart ur:
+ addto v also b shifted (0, s);
+ endfor
+%</MP>
+ v
+enddef;
+
+% \end{macrocode}
+%
+% \subsection{Hatching}\label{basichatching}
+%
+% \DescribeRoutine{thatchf}
+% This is the all-purpose macro called by the other macros that
+% fill a region with hatching. It takes the name of a picture \gbc{v},
+% a transform expression \gbc{CT}, a numeric expresion \gbc{sp} giving the
+% space between hatch lines, and two pairs, \gbc{a} and \gbc{b},
+% that represent the lower left and upper right limits of a rectangle.
+% The expression \gbc{sp} must be nonzero. The calling macros should take
+% care of that.
+%
+% It modifies the picture by adding to it the rectangle full of
+% hatching lines spaced \gbc{sp} apart. The rectangle is initially upright
+% and the lines horizontal, but they are drawn transformed by the
+% transform \gbc{CT}. This is how diagonal hatching is accomplished: the
+% transform is a rotation.
+%
+% We guard against \gbc{ypart a} being greater than \gbc{ypart b} or
+% \gbc{sp} being negative: \gbc{_sp} is \gbc{sp} modified to have the same
+% sign as \gbc{ypart (b - a)}. Thus, repeatedly adding it to \gbc{ypart a}
+% gets one to \gbc{ypart b}. We make the starting value an integer
+% multiple of \gbc{_sp} to make sure adjacent regions don't have jarringly
+% misaligned hatch lines. (I guess that's the reason; this algorithm
+% predates me.)
+% \begin{macrocode}
+vardef thatchf (suffix v) (expr CT, sp, a, b) =
+ save _sp;
+ _sp = signof (ypart(b - a)) abs(sp);
+ for _y = _sp*( ceiling ((ypart a)/_sp) ) step _sp until ypart b:
+ shpath (v, hatchpen)
+ ( ( (xpart a, _y)--(xpart b, _y) ) transformed CT );
+ endfor
+%<MF> mono(v);
+enddef;
+
+% \end{macrocode}
+%
+% \subsection{Tiles}\label{tiles}
+%
+% Tesselations are a generalization type of fill in which a rectangular
+% pattern is repeated throughout a region. The rectangular pattern is
+% called a tile. We provide here an environment in which the drawing commands
+% add to a picture variable other than \mfc{currentpicture}. We do this
+% very simply by redefining \gbc{active_plane}, localizing the
+% redefinition between \gbc{tile} and \gbc{endtile}
+%
+% \DescribeRoutine{tile}
+% \DescribeRoutine{endtile}
+% \gbc{tile} accepts one suffix parameter, the name of the tile, followed
+% by three numeric expressions and a boolean. \gbc{unit} should be a
+% dimension in device units and is the unit of length for all high level
+% drawing commands within the environment. \gbc{height} and \gbc{width}
+% specify the size of the tile in multiples of \gbc{unit}, and
+% \gbc{clipit} is a boolean that determines if the resulting picture is
+% clipped to the rectangle these parameters determine. For example,\\
+% \indent \gbc{tile (fred)(1in, 1, 2, true)} \\
+% starts a tile named \gbc{fred} which will be 1 inch wide and 2 inches
+% tall, and any marks that extend beyond this rectangle are clipped off.
+%
+% In \MF, the picture should be a whole number of pixels in size, so that
+% the tiles fit perfectly together. The fact that shifts must be integer
+% values is only mildly relevant, because the placement code does the
+% rounding.
+%
+% For tesselation (filling with tiles), we need to know various properties
+% of the tile so, in fact, a tile is a composite object consisting of a
+% picture, \gbc{fred.pic} in our example (the actual tile), two numerics
+% \gbc{fred.wd} and \gbc{fred.ht} (the device dimensions) and a boolean
+% \gbc{fred.clipon}.
+% \begin{macrocode}
+def tile (suffix atile) (expr unit, width, height, clipit) =
+ picture atile.pic; atile.pic := nullpicture;
+ numeric atile.wd, atile.ht;
+%<MF> (atile.wd, atile.ht) = round ((width, height)*unit);
+%<MP> (atile.wd, atile.ht) = (width, height)*unit;
+ boolean atile.clipon; atile.clipon := clipit;
+ begingroup
+% \end{macrocode}
+% We simply do a subset of what we do in \gbc{beginmfpic}, redefining
+% \gbc{active_plane} so that all drawing commands that add to it will
+% contribute to the tile.
+% \begin{macrocode}
+% save active_plane;
+ save active_plane;
+ def active_plane = atile.pic enddef;
+ save ztr, vtr; transform ztr, vtr;
+ ztr := identity scaled unit; vtr := ztr;
+% \end{macrocode}
+% And if clipit is true we set the \gbc{ClipPath} to the bounding
+% rectangle so that all commands that respect \gbc{ClipOn} will draw only
+% inside the tile. And in case some don't, \gbc{endtile} clips it all
+% anyway.
+% \begin{macrocode}
+ save ClipOn; boolean ClipOn;
+ if clipit :
+ ClipOn := true;
+ save ClipPath; path ClipPath[];
+ ClipPath = 1;
+ ClipPath[1] = rect(origin, (atile.wd, atile.ht));
+ else:
+ ClipOn := false;
+ fi
+enddef;
+def endtile =
+ DoClip (active_plane);
+ endgroup
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{is_tile}
+% To test whether \gbc{atile} is really a tile, just see if all the
+% components are defined and of the correct type.
+% \begin{macrocode}
+vardef is_tile (suffix atile) =
+ (known atile.pic ) and (picture atile.pic ) and
+ (known atile.wd ) and (numeric atile.wd ) and
+ (known atile.ht ) and (numeric atile.ht ) and
+ (known atile.clipon) and (boolean atile.clipon)
+enddef;
+
+% \end{macrocode}
+%
+% \section{Bounding boxes of paths}\label{bboxes}
+%
+% To fill a region with other than a solid fill, we normally fill a
+% rectangle with copies of a picture (or a path) and then clip to the
+% boundary curve. In order not to place too many copies, we try to find a
+% rectangle that is not too much larger than that region. For this we have
+% the macro \gbc{gbbox} which takes a path expression and two pair
+% variables, and sets the pairs to the lower left corner and upper right
+% corner, respectively, of a rectangle enclosing the path. In \MF, this
+% finds a rather loose box, the smallest rectangle containing all the
+% control points and all the nodes of the path. Or rather it used to do
+% that. Now we break the path into twice as many nodes and use the control
+% points relative to that, which gives a tighter box. Both algorithms make
+% use of the fact that the convex hull of the points and controls for a
+% path segment contains the segment.
+%
+% The bounding box macros are used on paths in device coordinates, but
+% there is no intrinsic reason that has to be so: they will return the
+% bounding box in whatever coordinates the supplied path is in.
+%
+% We also have \gbc{tightbbox} and \gbc{tbbox} in \MF{} but these are no
+% longer used so we'll omit them from \grafbase, but keep them in the
+% documentation for now.
+%
+% \DescribeRoutine{tightbbox}
+% Calculate tight bounding box points \gbc{ll} and \gbc{ur} for path
+% \gbc{g}. The tight bounding box is accurate to the limits of the
+% \mfc{solve} macro, which is the numeric \mfc{tolerance}, which we set to
+% \mfc{.5} (pixel). This is only called by \gbc{tbbox}, which is never
+% used.
+%
+% \DescribeRoutine{xlimit}
+% \DescribeRoutine{ylimit}
+% \gbc{xlimit(x)} returns a value of true if the path \gbc{g} doesn't
+% cross the vertical line at \gbc{x}. \gbc{ylimit(y)} is the same for
+% the horizontal line at \gbc{y}.
+% \begin{macrocode}
+%<*unused>
+vardef tightbbox (expr g) (suffix ll, ur) =
+ % true iff horizontal at y does not intersect g:
+ vardef xlimit (expr x) =
+ (((x, -infinity)--(x, infinity)) intersectiontimes g) < origin
+ enddef;
+ % true iff vertical at x does not intersect g:
+ vardef ylimit (expr y) =
+ (((-infinity, y)--(infinity, y)) intersectiontimes g) < origin
+ enddef;
+ interim tolerance := .5;
+ ll := ( (solve xlimit (-infinity, xpart pnt 0 (g))),
+ (solve ylimit (-infinity, ypart pnt 0 (g))) );
+ ur := ( (solve xlimit ( infinity, xpart pnt 0 (g))),
+ (solve ylimit ( infinity, ypart pnt 0 (g))) );
+ if showbbox :
+ noclip ( safedraw rect (ll, ur) );
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{tbbox}
+% \gbc{tbbox} simply calls \gbc{tightbbox} on each of an array of paths
+% and takes the maximum of all the upper right corners and the minimum of
+% all the lowerleft. Same syntax as \gbc{tightbbox} except that, instead
+% of a path parameter, \gbc{g} must be the name of an array of paths.
+%
+% This macro is never used elsewhere in \grafbase. Changed recently to
+% use the new \gbc{pairmin} and \gbc{pairmax} macros. This avoids two
+% \mfc{for} loops (which seem to be something of a \MF{} bottleneck).
+% \begin{macrocode}
+vardef tbbox (suffix g) (suffix ll, ur) =
+ save _gll, _gur; pair _gll, _gur;
+ tightbbox (g1, ll, ur);
+ for _idx = 2 upto g:
+ tightbbox (g[_idx], _gll, _gur);
+ ll := pairmin(ll, _gll); ur := pairmax(ll, _gur);
+ endfor
+ if showbbox : noclip ( safedraw rect (ll, ur) ); fi
+enddef;
+%</unused>
+% \end{macrocode}
+%
+% \DescribeRoutine{gbbox}
+% One can get a rather loose bounding rectangle by using the fact that
+% each segment of a path (from \mfc{point j of g} to \mfc{point j+1 of g})
+% is contained in the convex set determined by all 4 control points for
+% that segment. So we get a containing rectangle by getting the smallest
+% and largest values of the x- and y-coordinates of all those points. We
+% can get a considerably tighter fit if we cut each segment in half.
+%
+% \DescribeRoutine{ctrlsbbox}
+% There is a difference between `\mfc{postcontrol j of (subpath (j,j+1/2)
+% of p)}' and `\mfc{postcontrol j of p}'. To gain the tighter box we have
+% to look at the former. \gbc{ctrlsbbox} just updates the previously found
+% corners \gbc{ll} and \gbc{ur} of the bounding box based on the controls
+% of the path segment \gbc{p}, and the calling routine \gbc{gbbox} passes
+% it half a segment at a time. We don't actually examine both endpoints of
+% the half-segment, only those that are nodes of the original path. The
+% reason is that the subdivision points are already on the line segment
+% connecting the controls of the subpaths on either side. We've given this
+% potentially unlimited accuracy by allowing the number of subdivisions
+% (\gbc{bbox_split}) to be arbitrary. We choose 2 for the default.
+%
+% This description applies only to \MF, because \MP{} has primitive
+% facilities for determining the bounding box.
+%
+% \DescribeRoutine{pnt}
+% \DescribeRoutine{pre}
+% \DescribeRoutine{post}
+% I got tired of typing long expressions like `\gbc{precontrol length p of p}',
+% and now use the abbreviation `\gbc{pre[length p](p)}'.
+% \begin{macrocode}
+vardef pnt@# (expr p) = point @# of p enddef;
+vardef pre@# (expr p) = precontrol @# of p enddef;
+vardef post@# (expr p) = postcontrol @# of p enddef;
+
+vardef gbbox (expr g) (suffix ll, ur) =
+%<MP> ll := llcorner g; ur := urcorner g;
+%<*MF>
+ save _s; _s := emax(1, ceiling bbox_split);
+ ur := ll := pnt 0 (g);
+ if (length g) > 0 :
+ for _j = 1 upto length g:
+ ll := pairmin(ll, pnt[_j] (g)); ur := pairmax(ur, pnt[_j] (g));
+ endfor
+ for _j = 1 upto _s*(length g):
+ ctrlsbbox (subpath ((_j-1)/_s, _j/_s) of g) (ll, ur);
+ endfor
+ fi
+%</MF>
+ if showbbox : noclip ( safedraw rect (ll, ur) ); fi
+enddef;
+
+%<*MF>
+numeric bbox_split; bbox_split := 2;
+def ctrlsbbox (expr p) (suffix ll, ur) =
+ ll := pairmin (ll, post0 (p)); ll := pairmin (ll, pre 1 (p));
+ ur := pairmax (ur, post0 (p)); ur := pairmax (ur, pre 1 (p));
+enddef;
+
+%</MF>
+% \end{macrocode}
+%
+% \section{Device coordinate rendering
+% commands}\label{basicrendering}
+%
+% We use the word rendering to refer to commands that accept a path
+% expression as one parameter and use it to modify the \gbc{active_plane}.
+% All the commands in this section expect paths, pairs and dimensions in
+% device coordinates.
+%
+% \subsection{Drawing}\label{basicdrawing}
+%
+% \DescribeRoutine{safedraw}
+% \gbc{safedraw} accepts a path expression, and adds the result to
+% \gbc{active_plane}. It is the first drawing command to draw
+% exclusively on \gbc{active_plane}. This is the first of many uses of
+% \gbc{coloraddto}. In \MP{} it is basically the primitive \mfc{addto
+% ... also ... withcolor ...}, but in \MF{} it adds when the color is less
+% than 1 (gray or black), otherwise it subtracts (white).
+%
+% \DescribeRoutine{colorsafedraw}
+% \gbc{safedraw} merely calls colorsafedraw, which calls \gbc{picpath},
+% which calls \gbc{shpath}. One reason for this roundabout path
+% is to support older files (\gbc{colorsafedraw} not defined). Another
+% is that color handling in \MF{} requires a picture with pixels of
+% weight 1 or 0 only (\gbc{picpath}). Moreover, \gbc{shpath} guarantees
+% that the mode's aspect ratio is respected.
+% \begin{macrocode}
+def safedraw = colorsafedraw (drawcolor) enddef;
+vardef colorsafedraw (expr clr) expr d =
+ save v; picture v; v := picpath d;
+ DoClip (v);
+ coloraddto (clr) (active_plane, v);
+enddef;
+
+% \end{macrocode}
+%
+% \subsection{Filling}\label{basicfilling}
+%
+% \DescribeRoutine{NoCycleWarn}
+% This is a common warning for all those commands that require a cycle
+% (closed) path but an open path is supplied. In addition to the warning
+% in those commands, we also call \gbc{safedraw} for debugging purposes.
+% We make no attempt to color the path, but maybe we should.
+%
+% \DescribeRoutine{safefill}
+% \DescribeRoutine{colorsafefill}
+% \DescribeRoutine{safeunfill}
+% These three take one parameter that is a path expression, and
+% \gbc{colorsafefill} takes another that is a color. These commands fill
+% (or unfill) it in the \gbc{active_plane}. In \MF, when the color is
+% strictly between $0$ and 1, a gray fill is simulated with the
+% \gbc{shaded} macro.
+%
+% To simulate the effect of painting over in gray, the \MF{} version
+% clears the region before adding the shaded fill.
+%
+% \gbc{safeunfill} is just \gbc{safefill} with the color \mfc{background}.
+% In \MF{}, when \gbc{background = white = 1}, this is detected by
+% \gbc{coloraddto} which then subtracts the picture.
+% \begin{macrocode}
+def NoCycleWarn expr s =
+ GBmsg s & " cannot be applied to an open path. "
+ & "The path will be drawn instead.";
+enddef;
+
+def safefill = colorsafefill (fillcolor) enddef;
+vardef colorsafefill (expr clr) expr c =
+ if cycle c :
+ save v; picture v; v := interior c;
+ DoClip (v);
+%<*MF>
+ if (clr > black) and (clr < white) :
+ subto (active_plane) (v);
+ v := nullpicture;
+ v := shaded (clr) c;
+ fi
+%</MF>
+ coloraddto (clr) (active_plane, v);
+ else: NoCycleWarn "fill"; safedraw c;
+ fi
+enddef;
+def safeunfill expr c =
+ if cycle c : noclip (colorsafefill (background) c);
+ else: NoCycleWarn "unfill"; safedraw c;
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \subsection{Clipping}\label{clipping}
+%
+% \DescribeRoutine{safeclip}
+% This applies \gbc{clipto} to the active drawing plane. It follows the
+% pattern started with \gbc{safefill} where commands that require a cycle
+% will \gbc{safedraw} non-cyclic paths.
+% \begin{macrocode}
+def safeclip expr c =
+ if cycle c : clipto (active_plane) c;
+ else: NoCycleWarn "clip"; safedraw c;
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \section{Rendering: the highest level commands}\label{rendering}
+%
+% \DescribeRoutine{store}
+% Now we come to the highest level rendering operations. These are the
+% commands written to the output file by \mfpic. They accept a path in
+% \emph{graph} coordinates, convert it to device coordinates, rendering
+% the result, and return the original path. This way one can render a
+% path and pass it on to the preceding command for further processing.
+% This is how \mfpic{} implements multiple prefix macros. However, this
+% cannot be kept up because \MF{} abhors an isolated expression. Therefore
+% we provide a command that accepts a path and doesn't pass it on. In
+% theory, it could do nothing, but in \mfpic{} we store the path in
+% \gbc{curpath}, making every \mfpic{} figure a path assigment command
+% and the rendering is merely a side-effect.
+%
+% \DescribeRoutine{stored}
+% The macro \gbc{stored} performs \gbc{store}, but passes the same path as
+% its return value. This is used by \mfpic{} to implements the \cs{store}
+% command, allowing it to also be a prefix macro
+%
+% I don't know if \gbc{store} needs to employ \mfc{hide()}, but it seems
+% not to hurt.
+% \begin{macrocode}
+def store (suffix fs) expr f =
+hide(
+ if (not path f) and (not pair f) :
+ GBerrmsg ("Second argument to `store' must be a path or pair")
+ "";
+ fi
+ if not path fs : path fs; fi
+ fs := f
+)
+enddef;
+vardef stored (suffix fs) expr f = store (fs) f; f enddef;
+
+% \end{macrocode}
+%
+% \subsection{Drawing}\label{drawing}
+%
+% \DescribeRoutine{drawn}
+% \DescribeRoutine{colordrawn}
+% \gbc{drawn} and \gbc{colordrawn} accept a path
+% expression \gbc{f} and return the same. In between, \gbc{zconv(f)} is
+% subjected to \gbc{colorsafedraw}.
+% \begin{macrocode}
+def drawn = colordrawn (drawcolor) enddef;
+vardef colordrawn (expr clr) expr f =
+ colorsafedraw (clr) (zconv (f)); f
+enddef;
+
+% \end{macrocode}
+%
+% \subsection{Filling, unfilling and clipping}\label{filling}
+%
+% \DescribeRoutine{filled}
+% \DescribeRoutine{colorfilled}
+% \DescribeRoutine{unfilled}
+% \DescribeRoutine{Clip}
+% These subject \gbc{zconv(f)} to \gbc{colorsafefill}, \gbc{safeunfill} or
+% \gbc{safeclip}. The name \gbc{clip} (lowercase) is taken: it is a \MP{}
+% primitive and an old \file{grafbase.mf} command we keep for compatibility.
+%
+% \begin{macrocode}
+def filled = colorfilled (fillcolor) enddef;
+vardef colorfilled (expr clr) expr c =
+ colorsafefill (clr) zconv (c); c
+enddef;
+vardef unfilled expr c = safeunfill zconv (c); c enddef;
+vardef Clip expr c = safeclip zconv(c); c enddef;
+
+% \end{macrocode}
+%
+% \subsection{Shading}\label{shading}
+%
+% Shading is accomplished differently in \MP{} from \MF; however, many of
+% the same parameters are used for compatibility (so that \MP{} can be run
+% on a \file{.mf} created for \grafbase{} by \mfpic). In \MP, shading is
+% just filling with some level of gray. In \MF, we place a pattern of
+% small dots with the size and spacing adjustable. For compatibility,
+% \MP{} accepts these size and spacing parameters, but simply uses them to
+% calculate the darkness of gray.
+%
+% Ideally (i.e., for best appearance) one would shade with single pixels
+% placed in a regular pattern. Unfortunately, this is the most memory
+% intensive for \MF, which stores bitmaps by scanning each row of pixels,
+% and records where changes from black to white occur. We do use simple
+% dots, but make them quite a bit larger than one pixel. By default,
+% \gbc{0.5bp} in diameter, spaced (in \mfpic) a default \gbc{1pt} between
+% centers.
+%
+% \DescribeRoutine{shade}
+% This is the old \gbc{shade} macro, filling a contour with small dots.
+% The shape and size of the dot used can be selected by defining
+% \gbc{shadedotpath} and \gbc{shadewd}.
+%
+% A closed path representing the boundary of one dot of unit size,
+% \gbc{shadedotpath} is initialized to a circle.
+%
+% The parameter \gbc{sp} is the distance between the centers of the dots in
+% device coordinates, and \gbc{f} is the path to be filled in \emph{graph
+% coordinates}.
+%
+% As usual, if the path is not closed, we draw the curve instead. If the
+% spacing is too small relative to \gbc{shadewd}, we fill the curve. Otherwise the \gbc{filledwith} macro is used to fill with copies of
+% a dot picture. For speed, it actually fills with a two-dot picture.
+% \begin{macrocode}
+numeric shadewd; shadewd := 0.5bp;
+path shadedotpath;
+shadedotpath := fullcircle; % unitsquare;
+
+vardef shade (expr sp) expr f =
+ save g; path g; g := zconv (f);
+% \end{macrocode}
+% It seems clear that the gray level (\gbc{gr}) should depend
+% quadratically on \gbc{shadewd/sp}. Also, there is a point where the
+% result is essentially black and a fill would be more efficient.
+% The value .88 is arrived at empirically and is a compromise so that
+% \MF{} and \MP{} produce similar levels of gray on both printers available
+% to me. Theoretically, no white will appear when
+% \gbc{sqrt(2)*shadewd/sp >= 1}
+% \begin{macrocode}
+ save gr; numeric gr;
+ gr := 1 - (.88*abs(shadewd)/sp)**2;
+ if not cycle g : NoCycleWarn "shade"; safedraw g;
+ elseif gr <= 0 : safefill g;
+ else:
+%<*MF>
+ save ll, ur; pair ll, ur;
+ gbbox (g, ll, ur);
+ ll := floorpair (ll);
+% \end{macrocode}
+%
+% What we do is draw a row of dots and stack the rows to fill a rectangle.
+% We call \gbc{filledwith} to draw these copies. To save a little memory
+% we do this twice with half the dots each time. The second set
+% interleaves the first and is staggered from the first by half the
+% horizontal spacing. I wonder which which uses less memory, rendering
+% \gbc{v} unknown with \mfc{picture v}, or null with \mfc{v := nullpicture}?
+%
+% Shifts of pictures need to be by integer number of pixels, but this is
+% ensured by \gbc{filledwith}, using \mfc{ceiling} to define \gbc{dx} is
+% more to ensure it is not rounded down to 0.
+% \begin{macrocode}
+ save sh, v; picture sh, v;
+ save dx; dx := ceiling(sp/(sqrt 2));
+ sh := setdot (shadedotpath, abs(shadewd));
+ v := filledwith (sh, 2(dx, dx), ll, ur);
+ DoClip(v);
+ orto (active_plane, Clipped (v) g);
+ sh := sh shifted hroundpair ((dx, dx).t_);
+ v := nullpicture;
+ v := filledwith (sh, 2(dx, dx), ll, ur);
+ DoClip(v);
+ orto (active_plane, Clipped (v) g);
+%</MF>
+% \end{macrocode}
+% In \MP{} we just fill with gray. The gray level having been calculated
+% at the beginning.
+% \begin{macrocode}
+%<*MP>
+ colorsafefill (gr*white) g;
+%</MP>
+ fi
+ f
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{polkadot}
+% The macro \gbc{polkadot} is intended to fill a region with \emph{large}
+% dots. The diameter, \gbc{polkadotwd}, is initialized to \mfc{5bp}. The
+% code is similar to that of \gbc{shade}, but here we attempt a hexagonal
+% array: each dot surrounded by 6 equally spaced dots. Because of their
+% larger size and presumably larger spacing, we can be a little less
+% efficient and so we aim for improved visual appearance. We do what we
+% can to avoid unsightly slivers of partial dots, and only draw a dot if
+% its center lies in the bounding box.
+%
+% We also permit the circles to overlap, and only replace the code with a
+% fill if the dots overlap so much that no background can show (this assumes
+% that \gbc{polkadotpath} is a circle).
+%
+% If the space \gbc{sp} and \gbc{polkadotwd} are too small, there will
+% be a great many tiny dots. It is quite easy to overflow \MP{} capacity
+% and the dots are really ugly. In \MF, we already have \gbc{shade} to
+% place tiny dots. Therefore, we merely fill if \gbc{sp} is less that a
+% certain minimum, even if that minimum is greater than \gbc{polkadotwd}.
+% \begin{macrocode}
+polkadotwd := 5bp;
+mindotspace := 1bp;
+path polkadotpath; polkadotpath := fullcircle;
+
+vardef polkadot (expr sp) expr f =
+ save g; path g; g := zconv (f);
+ if not cycle g : NoCycleWarn "polkadot"; safedraw g;
+ elseif sp <= emax (2*polkadotwd/3, mindotspace) :
+ safefill g;
+ else:
+ save ll, ur; pair ll, ur;
+ gbbox (g, ll, ur);
+% \end{macrocode}
+% As with \gbc{shade}, we shift alternate rows by half the spacing between
+% dot centers, \gbc{dx}. The vertical shift \gbc{dy} is slightly larger
+% (relatively speaking) and the horizontal smaller. We also apply a
+% horizontal and vertical shift to avoid small pieces of dots. What it does
+% is take only those dots whose centers lie in the bounding box, and center
+% the whole array relative to that box.
+% \begin{macrocode}
+ save dx, dy; dx := sp/2; dy := dx*(sqrt 3);
+ hshift := ((xpart (ur - ll)) mod dx)/2;
+ vshift := ((ypart (ur - ll)) mod dy)/2;
+% \end{macrocode}
+% Here, \gbc{p} is the center of the first dot in the lower left corner.
+% \begin{macrocode}
+ save p, dims; pair p, dims;
+ p := ll + (hshift, vshift);
+ dims := 2(dx, dy);
+% \end{macrocode}
+% The extra \MF{} code is to clear what's under the dots in case they
+% are gray dots. And then to `gray' the dots when fillcolor demands it.
+% \begin{macrocode}
+ save v, thepolkadot; picture v, thepolkadot;
+ thepolkadot := setdot (polkadotpath, polkadotwd);
+ v := filledwith (thepolkadot, dims, p, ur);
+ p := p + (dx, dy);
+ orto (v, filledwith (thepolkadot, dims, p, ur));
+ DoClip (v); clipto (v) g;
+%<*MF>
+ if (fillcolor > black) and (fillcolor < white): % gray
+ subto (active_plane) (v);
+ thepolkadot :=
+ shaded (fillcolor) polkadotpath scaled ceiling(polkadotwd);
+ v := filledwith (thepolkadot, dims, p, ur);
+ p := p - (dx, dy);
+ orto (v, filledwith (thepolkadot, dims, p, ur));
+ DoClip (v); clipto (v) g;
+ fi
+%</MF>
+ coloraddto (fillcolor) (active_plane) (v);
+ fi
+ f
+enddef;
+
+% \end{macrocode}
+%
+% \subsection{Hatching}\label{hatching}
+%
+% \DescribeRoutine{thatch}
+% \DescribeRoutine{colorthatch}
+% Hatch interior of path \gbc{f} (graph coordinates) with lines at angle
+% \gbc{theta}, spaced \gbc{sp} apart (device coordinates). As usual an
+% unclosed path is simply drawn. The thickness of the lines is determined
+% by \gbc{hatchwd}. If \gbc{sp} is not greater than \gbc{abs(hatchwd)}, we
+% simply fill. This will ensure \gbc{thatchf} is called only for positive
+% \gbc{sp}.
+%
+% We find the bounding box of the backward rotated path, so when that box
+% is filled with lines and rotated, it will cover the path. After calling
+% \gbc{thatchf} we add the picture, clipped to the path.
+% \begin{macrocode}
+def thatch = colorthatch (hatchcolor) enddef;
+vardef colorthatch (expr clr) (expr sp, theta) expr f =
+ save g; path g; g := zconv (f);
+ if not cycle g : NoCycleWarn "hatch"; safedraw g;
+ elseif sp <= abs(hatchwd) : colorsafefill (clr) g;
+ else:
+ save v; picture v; v := nullpicture;
+ save CT; transform CT; CT := identity rotated theta;
+ save ll, ur; pair ll, ur;
+ gbbox (g transformed inverse CT, ll, ur);
+ thatchf (v, CT, sp, ll, ur);
+ DoClip(v);
+ coloraddto (clr) (active_plane) (Clipped (v) g);
+ fi
+ f
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{hhatch}
+% \DescribeRoutine{vhatch}
+% \DescribeRoutine{lhatch}
+% \DescribeRoutine{rhatch}
+% \DescribeRoutine{xhatch}
+% We offer some special cases, calling \gbc{thatch} with different angles.
+% These take only the spacing (in device coordinates) and a path
+% expression (in graph coordinates) as parameters. \gbc{hhatch} has angle
+% 0 and so produces horizontal lines; \gbc{vhatch} produces vertical
+% lines; \gbc{lhatch} produces lines tilted to the left (running from
+% upper left to lower right), and \gbc{rhatch} produces lines running from
+% lower left to upper right. \gbc{xhatch} produces cross-hatching, and
+% essentially runss \gbc{lhatch} and \gbc{rhatch}.
+%
+% Color is a parameter only for \gbc{colorxhatch}. The reason for that
+% is to make code written by \mfpic{} simpler. The \mfpic{} commands for
+% the others actual write calls to \gbc{thatch} or \gbc{colorthatch}.
+%
+% \begin{macrocode}
+def hhatch (expr sp) = thatch (sp, 0) enddef;
+def vhatch (expr sp) = thatch (sp, 90) enddef;
+def lhatch (expr sp) = thatch (sp, -45) enddef;
+def rhatch (expr sp) = thatch (sp, 45) enddef;
+
+def xhatch = colorxhatch (hatchcolor) enddef;
+vardef colorxhatch (expr clr, sp) expr f =
+ colorthatch (clr) (sp, 45) colorthatch (clr) (sp, -45) f
+enddef;
+
+% \end{macrocode}
+%
+% \subsection{Tesselations}\label{tess}
+%
+% \DescribeRoutine{tess}
+% Tesselation of interior of closed path is filling with copies of a tile
+% (see subsection~\ref{tiles}). The path is in graph units, the tile is a
+% suffix parameter and is the name of a previously defined tile. In fact,
+% one can create the picture any way one likes (it doesn't have to be with
+% the \gbc{tile} environment). Thus \gbc{tess (fred) f;} will work as long
+% as \gbc{fred.pic} is a picture \gbc{fred.wd} is its width, etc.
+% \begin{macrocode}
+vardef tess (suffix atile) expr c =
+ save _g; path _g; _g := zconv (c);
+ if not is_tile (atile) :
+ GBerrmsg ("Tile parameter " & str atile & " of tess() is invalid")
+ "This tile may be undefined or incorrectly defined. " &
+ "If you proceed, tess() will be abandoned and the curve " &
+ "merely drawn."; safedraw _g;
+ elseif not cycle _g : NoCycleWarn "tess"; safedraw _g;
+ else:
+ save _ll, _ur; pair _ll, _ur;
+ gbbox (_g, _ll, _ur);
+ save _ts; picture _ts;
+ _ts := filledwith (atile.pic, (atile.wd, atile.ht), _ll, _ur);
+ DoClip (_ts);
+ orto (active_plane, Clipped (_ts) _g);
+ fi
+ c
+enddef;
+
+% \end{macrocode}
+%
+% \subsection{Dots and Dashes}\label{dashes}
+%
+% \MP{} has some builtin commands for drawing a dashed or dotted curve,
+% but \MF{} does not. Considerable effort went into making this possible
+% (before \MP{} even existed). The code is reasonably fast and the result
+% is actually better quality than the builtin commands of \MP{} so we use
+% the same code in both versions. The \grafbase{} dashing code is designed
+% to produce a whole number of dashes on any curve to which it is applied,
+% and (usually) to begin and end with half a dash (so that when dashed
+% curves abut the result looks decent). The built-in facilities do neither
+% of these. In addition, the dotting code is flexible enough that copies
+% of any picture (not just a circular dot) can be used to trace a path.
+%
+% The general command is \gbc{gendashed} which takes a suffix parameter
+% (the name of a \emph{dashing pattern}, see below) and a path expression
+% in graph coordinates.
+%
+% A dashing pattern \gbc{pat} consists of three arrays, \gbc{pat.start},
+% which is used to draw the beginning of the path (half a dash in the
+% default \gbc{dashed} command), \gbc{pat.finish}, which is used to draw
+% the other end, and \gbc{pat.rep}, which is the repeating pattern for
+% drawing the rest of the curve. Each of these is an \emph{array} of
+% numerics. These should be lengths, in device units, and represent the
+% lengths of dashes and spaces.
+%
+% We start with some variables and their defaults, some of which are no
+% longer used. \gbc{segment_split} is used in the code for finding the
+% approximate length of a curve. This is needed so that adjustments
+% can be made so that a whole number of repeated patterns are used.
+% \gbc{dashsize} and \gbc{dashgap} are no longer used. Originally they
+% gave the lengths of default dashes and the spaces in between.
+% \gbc{dash_start} and \gbc{dash_finish} are the fractions of a dash
+% length that are used at the start and finish if the command
+% \gbc{dashpat} is used to create the dashing pattern.
+%
+% And \gbc{unit_of_length} is used to adjust numbers downward and avoid
+% arithmetic overflow. For a 1200dpi \MF{} mode, a curve 4 inches long
+% will be over \mfc{infinity} pixels in length, but only 40 deci-inches.
+% Our default for this variable is just that: 1/10 inch.
+% \begin{macrocode}
+if unknown segment_split : segment_split := 8; fi
+if unknown dashsize : dashsize := 3bp; fi
+if unknown dashgap : dashgap := dashsize + 2penwd; fi
+if unknown dash_finish : dash_finish := .5; fi
+if unknown dash_start : dash_start := .5; fi
+if unknown unit_of_length : unit_of_length := 0.1in; fi
+
+% \end{macrocode}
+%
+% \DescribeRoutine{gendashed}
+% The main idea is to have a list of lengths represent the repeating
+% pattern of dashes and dots. These lengths represent a dash length,
+% followed by a gap length, etc., so there are an even number. To start
+% dashing a path, we normally take a fraction (\gbc{dash_start}) of the
+% first dash, then the rest of the pattern. We continue by repeating the
+% pattern as many times as will fit, then we finish off with a fraction
+% (\gbc{dash_finish}) of the first dash. A dash of length 0 is a dot. A
+% gap of length 0 is OK, but useless unless it's between a dot and a dash,
+% and you arrange for the dot's size to be different from \gbc{penwd}.
+%
+% We generalize this so that \gbc{pat.start} and \gbc{pat.finish} can be
+% any patterns, not necessarily related to \gbc{pat.rep}. Also "dots" can be
+% symbols like \gbc{Triangle}. We also supply (later) the \gbc{dashpat}
+% command which takes a list of lengths, equates \gbc{pat.rep} to them,
+% and generates \gbc{pat.start} and \gbc{pat.finish} according to the
+% description above.
+% \begin{macrocode}
+vardef gendashed (suffix pat) expr f =
+ save _g; path _g; _g := zconv(f);
+ if (unknown pat.rep) : % no "pattern"
+ GBmsg "Dash pattern " & str pat & " undefined. " &
+ "Path will be drawn instead.";
+ safedraw _g;
+ elseif pat.rep < 2 : % no "spaces"
+ safedraw _g;
+ else:
+% \end{macrocode}
+% We want to manipulate the values of \gbc{pat} so that a whole number of
+% repetitions are used. So we copy \gbc{pat} to \gbc{_tmppat}.
+%
+% After this loop, \gbc{_dl.s} is the total length of the corresponding
+% \gbc{pat.s} in multiples of \gbc{unit_of_length}, and \gbc{_tmppat.s[i]}
+% is \gbc{pat.s[i]} converted to these units.
+% \begin{macrocode}
+ save _dl, _tmppat;
+ forsuffixes _s = start, rep, finish :
+ _dl._s := 0; _tmppat._s := pat._s;
+ for i = 1 upto pat._s :
+ _tmppat._s[i] := pat._s[i]/unit_of_length;
+ _dl._s := _dl._s + _tmppat._s[i];
+ endfor
+ endfor
+ if _dl.rep = 0 :
+ GBmsg "Dash pattern " & str pat & " has length 0. " &
+ "Path will be drawn instead.";
+ safedraw _g;
+ else:
+% \end{macrocode}
+% Here \gbc{_g} is our path in device units, but we convert that to our
+% unit of length to avoid having paths of length \gbc{infinity}.
+%
+% This is how we process a path mathematically: let $f(t)$, $0 \le t \le
+% k$ be the formula for the path \gbc{f}, $k$ being the number of segments
+% of \gbc{f}, we consider the polygon connecting the points $f(0), f(1/8),
+% f(2/8),\ldots,f(k)$ and compute the length of \emph{that} path (assuming
+% \gbc{segment_split = 8}). Actually, we compute and save the cumulative
+% lengths at each vertex of this polygon, since we use that later to
+% determine ``when'' (i.e., at what values of $t$) to place a dot or draw
+% a dash. The command \gbc{makelengtharray} does this, storing the
+% cumulative lengths in the array \gbc{_cumlen} and returning the total
+% length.
+% \begin{macrocode}
+ save _p; path _p;
+ _p := _g scaled (1/unit_of_length);
+ save _cumlen, _totlen, _n, _sf;
+ _totlen := makelengtharray(_cumlen) _p;
+% \end{macrocode}
+% Now we scale the dashes so that a whole number of patterns make up
+% the lengths of the approximating polygon. \gbc{scale_adjust} returns
+% the scaling factor, equates \gbc{_n} to the total number of
+% \gbc{pat.rep} to use. If the path length is already less than the length
+% of the start and finish patterns, this is equated to $-1$ as a flag to
+% draw the path instead. (recall \gbc{_dl.s} holds the length of part
+% \gbc{s}).
+%
+% After this we rescale the dashes and spaces stored in \gbc{_tmppat}, and
+% the length of the patterns in \gbc{_dl}.
+% \begin{macrocode}
+ _sf := scale_adjust (_n, _dl)(_totlen);
+ if _n < 0 : safedraw _g;
+ else:
+ forsuffixes _s = start, rep, finish :
+ for _i = 1 upto _tmppat._s :
+ _tmppat._s[_i] := _tmppat._s[_i]*_sf;
+ endfor
+ _dl._s := _dl._s*_sf;
+ endfor
+% \end{macrocode}
+% The user has the capability to use something other than a small disk for
+% a dot by defining \gbc{plot_pic}. The utility \gbc{makesymbol} is
+% defined later. It examines \gbc{plot_pic} and makes a picture depending
+% on what type of variable it is. The default \gbc{dotpath} is
+% \mfc{fullcircle}, but user may also change that to get different dots.
+% \gbc{makesymbol} scales by \gbc{penwd} \emph{only if the first
+% parameter is a path}. This is how to increase the dot size (the code in
+% \gbc{plot} uses this.)
+% \begin{macrocode}
+ save dashingdot; picture dashingdot;
+ if known plot_pic : dashingdot := makesymbol(plot_pic, penwd);
+ else: dashingdot := makesymbol(dotpath, penwd);
+ fi
+% \end{macrocode}
+% The macro \gbc{dashit} draws the dashes, computing where they go and
+% drawing the appropriate subpaths of \gbc{_g} or placing a dot at the
+% appropriate point. \gbc{dashit} returns nothing and assumes all the
+% information accumulated so far, so it can only be called by
+% \gbc{gendashed}.
+%
+% \gbc{_t} and \gbc{_d} are temporary variables used by
+% \gbc{dashit}, but we declare them here since we initialize them
+% differently for each call. \gbc{_d0} and \gbc{_d1} hold the
+% position along the curve of the ends of a dash in distance from the
+% start; \gbc{_t0} and \gbc{_t1} are the same, but in terms of time.
+% A macro \gbc{gettime} converts the first to the second. It uses the
+% cumulative length array \gbc{_cumlen} for this, and maintains
+% \gbc{_ct} as the current index into that array. The parameters to
+% \gbc{dashit} are the name of the part of the dashing pattern that is being
+% drawn, and a temporary picture variable. The latter holds the picture
+% until \gbc{DoClip} can process it, then it is added to \gbc{active_plane}.
+% The code of \gbc{dashit} leaves \gbc{_d0} pointing to the current
+% position on the curve, but for safety and to reduce accumulated
+% round-off error, we initialize it to what it should be before each call.
+% \begin{macrocode}
+ save _ct, _t, _d, _v;
+ picture _v; _v := nullpicture;
+ _ct := 0;
+ % Begin with pat.start
+ _d0 := 0; _t0 := 0;
+ dashit (_tmppat.start) (_v);
+% \end{macrocode}
+% The repeating pattern has the tendency to use lots of memory. Previously
+% I added all the dashes to \gbc{_v} and then added it all at once to
+% \gbc{active_plane}. The purpose was to be able to \gbc{DoClip} it once,
+% and add it once with \gbc{coloraddto} to get it drawn in color under \MF.
+% This was simplest, but a memory hog requiring $O(n)$ in memory, where
+% $n$ is the number of repeated patterns. Then we tried clipping and adding
+% within \gbc{dashit}. This was terribly slow, requiring $O(n)$ in time.
+% Now I'm going to try a standard programming trick: accumulate $m < n$
+% repetitions before adding them, the memory should be $O(m)$ and the
+% time $O(n/m)$. As a first try, we make $m$ about $\sqrt n$.
+% \begin{macrocode}
+ % then pat.rep
+ if _n > 0 :
+ save _m; _m := ceiling sqrt(_n);
+ for _j = 0 step _m until _n - 1 :
+ for _i = 0 upto _m - 1 :
+ exitif (_i + _j) > _n - 1;
+ _d0 := _dl.start + (_j + _i)*_dl.rep;
+ _t0 := gettime(_cumlen, _ct) (_d0);
+ dashit (_tmppat.rep) (_v);
+ endfor
+ % add _m patterns and reset.
+ DoClip(_v);
+%<MF> mono (_v)
+ coloraddto (drawcolor) (active_plane, _v);
+ _v := nullpicture;
+ endfor
+ fi
+ % and finally, pat.finish
+ _d0 := _totlen - _dl.finish;
+ _t0 := gettime(_cumlen, _ct) (_d0);
+ dashit (_tmppat.finish) (_v);
+ DoClip(_v);
+%<MF> mono(_v)
+ coloraddto (drawcolor) (active_plane, _v);
+ fi
+ fi
+ fi
+ f
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{makelengtharray}
+% This takes an array name and a path expression (any coordinates),
+% computes the array of partial lengths (of the polygon approximation),
+% and returns the total length.
+% \begin{macrocode}
+vardef makelengtharray (suffix clen) expr p =
+ save _s; _s := emax (1, ceiling segment_split);
+ clen := _s*length p; clen[0] := 0;
+ for _i = 1 upto clen :
+ clen[_i] := clen[_i-1] + abs (pnt[_i/_s] (p) - pnt[(_i-1)/_s] (p));
+ endfor
+ clen[clen]
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{scale_adjust}
+% Here \gbc{n} is a suffix defined by the calling routine,
+% \gbc{pl.\{start\|ref\|finish\}} are the lengths of corresponding parts
+% of a dashing pattern, \gbc{lngth} is the length of some path (determined
+% by the calling routine). It determines how many times \gbc{pl.rep} goes
+% into \gbc{lngth - pl.start - pl.finish}. If this is negative it remains
+% negative, otherwise it is rounded. \gbc{scale_adjust} then determines
+% and returns the scaling factor \gbc{sf} required to make
+% \gbc{sf*(pl.start + n*pl.rep + pl.finish)} equal to \gbc{lngth}.
+% \begin{macrocode}
+vardef scale_adjust (suffix n, pl) (expr lngth) =
+ n := (lngth - pl.start - pl.finish)/pl.rep;
+ n := if n < 0 : -1 else: round(n) fi;
+ lngth/(pl.start + emax(n, 0)*pl.rep + pl.finish)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{gettime}
+% \gbc{arr} is an increasing array of lengths, defined by the calling
+% routine. \gbc{ct} is current index into that array; it will vary with
+% subsequent calls. Calling routine initializes it before the first call,
+% \gbc{gettime} updates it. \gbc{lngth} is a length interpreted as the
+% length along the path associated to the array.
+%
+% Since this array is generated by splitting the segments of the path at
+% times \gbc{i/segment_split} we first determine in which of these splits
+% the given distance is (i.e., find \gbc{ct} so that \gbc{lngth} lies
+% between \gbc{arr[ct-1]} and \gbc{arr[ct]}). To avoid problems with
+% round-off error, bad length parameter, etc., we force \gbc{lngth} to
+% satisfy this for some index between the current value of \gbc{ct} and
+% \gbc{arr} inclusive.
+%
+% Once we know what segment we are in, we determine the time by linear
+% interpolation between the times corresponding to \gbc{ct} and
+% \gbc{ct+1}.
+% \begin{macrocode}
+vardef gettime (suffix arr, ct) (expr lngth) =
+ save _gtl, _s;
+ _s := emax(1, ceiling segment_split);
+ _gtl := emax (arr[ct], emin (arr[arr], lngth));
+ forever:
+ exitif ( (arr[ct] <= _gtl) and (_gtl <= arr[ct+1]) );
+ ct := ct + 1; % need to exit *before* incrementing
+ endfor
+ if arr[ct] = arr[ct+1] : ct
+ else: ( ct + (_gtl - arr[ct]) / (arr[ct+1] - arr[ct]) )
+ fi /_s
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{dashit}
+% No variables are saved or initialized; \gbc{gendashed} defines array
+% \gbc{_cumlen}, path \gbc{_g}, and initializes \gbc{_d0},
+% \gbc{_t0} and \gbc{_ct}.
+%
+% \gbc{pos} is one of the dashpattern arrays, so it consists of numerics
+% interpreted as lengths of dashes (odd index) and spaces (even index). In
+% the first case \gbc{_d0} and \gbc{_t0} will already be pointing to
+% the beginning of the dash and we get to the end of the dash by adding
+% the length of a dash (\gbc{pos[_j]}) to \gbc{_d0} (getting
+% \gbc{_d1}) and calling \gbc{gettime} (getting \gbc{_t1}). We draw
+% the subpath between thos points. Unless \gbc{pos[_j] = 0}, in which case
+% a dot is placed.
+%
+% For even \gbc{j} (a space) we are at \gbc{_d1} and \gbc{_t1} and
+% we increment them to get \gbc{_d0} and \gbc{_t0} for the next
+% iteration.
+% \begin{macrocode}
+def dashit (suffix pos) (suffix pic) =
+ for _k = 1 upto pos:
+ if odd _k : % draw a dash of length pos[_k]
+ if pos[_k] = 0 : % point required
+ _d1 := _d0; _t1 := _t0;
+ picdot (pic, dashingdot, pnt [_t0] (_g));
+ else:
+ _d1 := _d0 + pos[_k];
+ _t1 := gettime (_cumlen, _ct) (_d1);
+ shpath (pic, drawpen) (subpath (_t0, _t1) of _g);
+ fi
+ else: % find the start of the next dash:
+ _d0 := _d1 + pos[_k];
+ _t0 := gettime(_cumlen, _ct) (_d0);
+ fi
+ endfor
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{dashpat}
+% This is a utility to convert a text list of lengths to the three dash
+% pattern arrays required by \gbc{gendashed}. We first simply copy the
+% list to array \gbc{pat.rep}. If the number is odd we add a 0-length
+% item (a nonspacing space). Unless the number is 1, which we use as a
+% signal that a curve should be solid and not dashed at all.
+%
+% \gbc{pat.start} is the same as \gbc{pat.rep} except the first dash is
+% reduced by the factor \gbc{dash_start}. \gbc{pat.finish} is just the
+% first dash of \gbc{pat.rep} reduced by the factor \gbc{dash_finish}.
+% \begin{macrocode}
+def dashpat (suffix pat) (text t) =
+ pat.rep := 0;
+ for _itm = t:
+ pat.rep[incr pat.rep] := _itm;
+ endfor;
+ if odd (pat.rep) and (pat.rep > 1):
+ pat.rep[incr pat.rep] := 0;
+ fi
+ pat.start := 1;
+ pat.start[1] := pat.rep[1]*dash_start;
+ for _idx = 2 upto pat.rep :
+ pat.start[incr pat.start] := pat.rep[_idx];
+ endfor
+ pat.finish := 1;
+ pat.finish[1] := pat.rep[1]*dash_finish;
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{dashed}
+% \DescribeRoutine{DASHED}
+% The old \gbc{dashed} is now implemented by making a dashpattern from the
+% two arguments and calling gendashed. When \MP{} support was added, I
+% thought it best not to overwrite the \MP{} primitive \gbc{dashed} and
+% the command was named \gbc{DASHED}, but then later it seemed better to
+% keep \MF/\MP{} compatibility so \gbc{dashed} was used. Now for backward
+% compatibility we maintain both. In \MP, we save the primitive
+% \mfc{dashed} as \gbc{dashed_}.
+%
+% \gbc{dashed} takes parameters which are the length and the space (device
+% coordinates) and a path (graph coordinates). It returns the path.
+% \begin{macrocode}
+vardef DASHED (expr dlen, dgap) expr f =
+ save dashes; dashpat (dashes) (dlen, dgap);
+ gendashed (dashes) f
+enddef;
+
+%<MP>let dashed_ = dashed;
+def dashed = DASHED enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{doplot}
+% \gbc{doplot} places symbols at positions along a path determined by
+% \gbc{dgap} (space between symbols), they are scaled by \gbc{sc} and the
+% actual symbol is \gbc{spath}. Currently this may be one of three things:
+% \begin{enumerate}
+% \item A path, giving the shape of the dot, which should be defined in
+% units so that the desired size is obtained under scaling by
+% \gbc{sc}. Normally this means one unit across.
+% \item A picture. This is used unscaled, it being presumed that it has
+% been prepared by a user to the correct size.
+% \item (\MP{} only) a string.
+% \end{enumerate}
+% All these are converted to a picture by the \gbc{makesymbol} command and
+% it is assigned to \gbc{plot_pic}, which \gbc{gendashed} has been
+% trained to use when dots are needed.
+%
+% After this \gbc{gendashed} is called with a pattern where the dashes are
+% 0 length, signalling that dots are used. \gbc{dotted} is implemented by
+% calling \gbc{doplot} with \gbc{dotpath} the symbol.
+% \begin{macrocode}
+vardef doplot (expr spath, sc, dgap) expr f =
+ save dots; dashpat (dots) (0, dgap);
+ save plot_pic; picture plot_pic;
+ plot_pic := makesymbol (spath, sc);
+ gendashed (dots) f
+enddef;
+
+path dotpath; dotpath := fullcircle;
+vardef dotted (expr dsize, dgap) expr f =
+ doplot (dotpath, dsize, dgap) f
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{plotnodes}
+% This is a useful little utility to draw the points on top of the
+% curve through them. It differs from \gbc{plotsymbol} (defined later) in
+% that it takes a path parameter (rather than a list of points) and
+% returns that path (so it works with \mfpic{} as a prefix macro). It
+% also uses \gbc{drawcolor}. Otherwise it calls the same code.
+% \begin{macrocode}
+vardef plotnodes (expr symbol, size) expr f =
+ save _pln; pair _pln[];
+ _pln := 0;
+ for _a = 0 upto (length f) if cycle f : - 1 fi :
+ _pln[incr _pln] := pnt[_a] (f);
+ endfor
+ dosymbols (drawcolor, symbol, size) (_pln);
+ f
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{centerit}
+% This accepts a picture and returns the same picture centered. This
+% is close to impossible in \MF, so we only do it in \MP. Actually, we
+% no longer use it, because in the one case where we did
+% (\gbc{makesymbol}), it seemed to restrict the user's choices too much.
+% \begin{macrocode}
+%<*MP>
+vardef centerit (expr pic) =
+ pic shifted -(0.5[urcorner pic, llcorner pic]);
+enddef;
+
+%</MP>
+% \end{macrocode}
+%
+% \DescribeRoutine{makesymbol}
+% This utility: takes \emph{any} expression and scale and returns a picture.
+% If the expression \gbc{spath} is a cycle it returns the interior, for
+% other paths it draws the path, in either case scaled by \gbc{sc}. If
+% already a picture, it returns it. In \MP, if it is a string, it returns
+% a picture containing that string drawn in the \mfc{defaultfont}. In any
+% other case, the default dot is returned.
+% \begin{macrocode}
+vardef makesymbol (expr spath, sc) =
+ if path spath : setdot (spath, sc)
+ elseif picture spath :
+%<MF> save v; picture v; v:= spath; mono (v); v
+%<*MP>
+ spath
+ elseif string spath :
+ spath infont defaultfont scaled defaultscale
+%</MP>
+ else:
+ GBmsg "Undefined symbol for plotting, using dotpath instead.";
+ setdot (dotpath, sc)
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% These are some symbols to be used by \gbc{doplot} and \gbc{plotsymbol}.
+% They are paths. The ones named with ``\gbc{Solid}'' are closed paths.
+% Since these two drawing commands feed the path to \gbc{setdot}, they end
+% up filled if they are cyclic, merely drawn if not.
+%
+% All are intended to have roughly the area (when area makes sense) of a
+% circle with diameter 1.
+% \begin{macrocode}
+path Triangle, Square, Circle, Diamond, Star, Plus, Cross,
+ Asterisk, SolidTriangle, SolidSquare, SolidCircle,
+ SolidDiamond, SolidStar;
+
+Triangle := (for n = 0 upto 2:
+ (up rotated 120n)-- endfor up) scaled .78;
+SolidTriangle := Triangle & cycle;
+
+Square := (for n = 0 upto 3:
+ dir (90n + 45)-- endfor dir 45) scaled .63;
+SolidSquare := Square & cycle;
+
+Circle := halfcircle & halfcircle rotated 180;
+SolidCircle := Circle & cycle;
+
+Diamond := (Square rotated 45) xscaled (1/1.2) yscaled 1.2;
+SolidDiamond := Diamond & cycle;
+
+Plus := (origin for n = 0 upto 3:
+ --(up rotated 90n)--origin endfor) scaled .65;
+Cross := Plus rotated 45;
+Asterisk := (origin for n = 0 upto 5:
+ --(up rotated 60n)--origin endfor) scaled .6;
+
+% \end{macrocode}
+%
+% We do some computations to find the vertices of a standard 5-pointed
+% star (pentagram). The first equation says the indented vertex at the
+% ``left shoulder'' is on the line from the top of the ``head'' to the
+% ``left foot'', and the second says it is on the line from the ``left
+% hand'' to the ``right hand''. That point determined, we get the rest by
+% rotaing 72 degrees.
+% \begin{macrocode}
+pair zz;
+zz = (whatever)[up, up rotated 144];
+zz = (whatever)[up rotated 72, up rotated -72];
+Star := (for n = 0 upto 4:
+ (up rotated 72n)--(zz rotated 72n)-- endfor up) scaled .84;
+SolidStar := Star & cycle;
+save zz;
+
+% \end{macrocode}
+%
+% In \mfpic, the \cs{plotdata} command draws several curves with one
+% command. The curves are drawn with changeable methods of rendering.
+% There are three schemes. The first draws the curves with different dash
+% patterns. Another scheme is to plot the curves with different symbols.
+% Still another is to use different colors (\MP{} only).
+%
+% We implement the changing of patterns (symbols, colors) by defining
+% arrays of such things and changing the index into the array. For
+% example, when the user has selected dashes, the first curve is
+% \gbc{gendashed} with the pattern \gbc{dashtype0}, the next with
+% \gbc{dashtype1}, etc.
+%
+% \DescribeRoutine{defaultdashes}
+% These are the usual dash patterns. Their setting is done by a macro
+% so the user may easily restore them. The spaces are apparently larger
+% than the dashes, but taking the thickness of the pen into account
+% (\mfc{.5bp}) the dashes will appear about \mfc{.5bp} larger than stated
+% and the spaces about \mfc{.5bp} smaller.
+% \begin{macrocode}
+numeric dashtype;
+forsuffixes s = start, rep, finish :
+ numeric dashtype[].s, dashtype[].s[];
+endfor
+def defaultdashes =
+ dashpat (dashtype0) (0); % solid
+ dashpat (dashtype1) (3bp, 4bp); % dashed
+ dashpat (dashtype2) (0, 4bp); % dotted
+ dashpat (dashtype3) (0, 4bp, 3bp, 4bp); % dot-dash
+ dashpat (dashtype4) (0, 4bp, 3bp, 4bp, 0, 4bp);% dot-dash-dot
+ dashpat (dashtype5) (0, 4bp, 3bp, 4bp, 3bp, 4bp);% dot-dash-dash
+ dashtype := 6;
+enddef;
+defaultdashes;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{isdashpat}
+% Checks, for the given variable \gbc{pat}, if the three arrays that
+% make up a dash pattern are know arrays. It does not check if they are
+% numeric arrays, but one hardly thinks they could all three exist
+% accidentally if they hadn't been created by \gbc{dashpat}.
+%
+% \DescribeRoutine{setdatadashes}
+% We have this method for users to select their own dash patterns. The
+% \gbc{setdatadashes} command requires a list of suffixes previously
+% defined by the \gbc{dashpat} command.
+%
+% \DescribeRoutine{getdashpat}
+% And finally, we remove the mod-ing operation from \TeX, where it is
+% cumbersome, to \MF, where it is trivial, with this command.
+% \begin{macrocode}
+vardef isdashpat suffix pat =
+ (knownarray pat.start) and
+ (knownarray pat.finish) and
+ (knownarray pat.rep)
+enddef;
+
+def setdatadashes (text lst) =
+ save dashtype; dashtype := 0;
+ forsuffixes _itm = lst :
+ if isdashpat _itm :
+ forsuffixes _s = start, rep, finish :
+ copyarray (_itm._s, dashtype[dashtype]._s);
+ endfor
+ dashtype := dashtype + 1;
+ else: GBmsg "Improper dash pattern in setdatadashes.";
+ fi
+ endfor
+ if dashtype = 0 :
+ SetdataWarn "dashes";
+ defaultdashes;
+ fi
+enddef;
+def getdashpat expr n = dashtype[n mod dashtype] enddef;
+
+def SetdataWarn expr s =
+ GBmsg "Command setdata"& s &"() failed; using defaults."
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{setdatasymbols}
+% This can be used to define the sequence of point plotting styles for
+% \mfpic's \cs{plotdata} command. We could use it to set the default
+% symbols, but I worry about the difficulty chasing down bugs if
+% \gbc{defaultpoints} calls \gbc{setdatasymbols} which can again call
+% \gbc{defaultpoints}.
+%
+% \DescribeRoutine{getsymbol}
+% This is similar to \gbc{getdashpat}. In fact we could write a
+% single macro to do both, but I think we get a more readable \mfpic{}
+% output file if we have separate commands.
+% \begin{macrocode}
+def setdatasymbols (text lst) =
+ save pointtype; path pointtype[]; pointtype := 0;
+ for _itm = lst :
+ if (known _itm) and (path _itm):
+ pointtype[pointtype] := _itm;
+ pointtype := pointtype + 1;
+ else:
+ GBmsg "Improper path in setdatasymbols().";
+ fi
+ endfor
+ if pointtype = 0:
+ SetdataWarn "symbols";
+ defaultsymbols;
+ fi
+enddef;
+def getsymbol expr n := pointtype[n mod pointtype] enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{defaultsymbols}
+% We store the default definitions in a macro so the user can restore
+% them easily.
+% \begin{macrocode}
+numeric pointtype; path pointtype[];
+def defaultsymbols =
+ pointtype0 := Circle;
+ pointtype1 := Cross;
+ pointtype2 := SolidDiamond;
+ pointtype3 := Square;
+ pointtype4 := Plus;
+ pointtype5 := Triangle;
+ pointtype6 := SolidCircle;
+ pointtype7 := Star;
+ pointtype8 := SolidTriangle;
+ pointtype := 9;
+enddef;
+defaultsymbols;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{setdatacolors}
+% \DescribeRoutine{getcolor}
+% Finally, for \MP, we do a similar pair of commands for setting and
+% getting the colors for the \cs{plotdata} command. The default colors
+% were tested on screen and on an inkjet printer. The adjustments away
+% from pure colors is based on a compromise between those experiments.
+%
+% \DescribeRoutine{defaultcolors}
+% We store the default definitions in a macro so the user can restore
+% them easily.
+% \begin{macrocode}
+%<*MP>
+def setdatacolors (text lst) =
+ save colortype; color colortype[]; colortype := 0;
+ for _itm = lst :
+ if (known _itm) and (color _itm) :
+ colortype[colortype] := _itm;
+ colortype := colortype + 1;
+ else: GBmsg "Improper color in setdatacolors().";
+ fi
+ endfor
+ if colortype = 0 :
+ SetdataWarm "colors";
+ defaultcolors;
+ fi
+enddef;
+def getcolor expr n = colortype[n mod colortype] enddef;
+
+numeric colortype; color colortype[];
+def defaultcolors =
+ colortype0 := black;
+ colortype1 := red;
+ colortype2 := 0.80blue + .2white; % blue
+ colortype3 := 0.66yellow + .34red; % orange
+ colortype4 := 0.80green; % green
+ colortype5 := 0.85magenta; % magenta
+ colortype6 := 0.85cyan; % cyan
+ colortype7 := 0.85yellow; % yellow
+ colortype := 8;
+enddef;
+defaultcolors;
+
+%</MP>
+% \end{macrocode}
+%
+% Points are filled or unfilled circles. They are implemented with
+% \gbc{plotsymbol}, but the code differs in that filled or unfilled
+% circles are determined by a parameter rather than the type of curve.
+% In addition, for unfilled circles, it clears the pixels inside the circle.
+%
+% \DescribeRoutine{bpoint}
+% \gbc{bpoint} is basicly a shorthand for a scaled circle shifted to a
+% point. The scale and the point are in device coordinates. We don't use
+% it anywhere in grafbase anymore.
+% \begin{macrocode}
+vardef bpoint (expr ptwd, b) =
+ fullcircle scaled ptwd shifted b
+enddef;
+% \end{macrocode}
+%
+% \DescribeRoutine{pointd}
+% This draws disks with diameter \gbc{ptwd}, filled or unfilled based on
+% the boolean \gbc{filled}, at the graph coordinate coordinates in the
+% list \gbc{t}. In case \gbc{filled} is true, \gbc{pointd} calls
+% \gbc{plotsymbol (SolidCircle)} otherwise we make \gbc{clearsymbols} true
+% (so that the area where each point is drawn will be cleared before
+% drawing it) and call \gbc{plotsymbol(Circle)}.
+% \begin{macrocode}
+def pointd (expr ptwd, filled) (text t) =
+ if filled :
+ plotsymbol (SolidCircle, ptwd) (t);
+ else :
+ begingroup;
+ save clearsymbols; boolean clearsymbols; clearsymbols := true;
+ plotsymbol (Circle, ptwd) (t);
+ endgroup
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{plotsymbol}
+% \DescribeRoutine{colorplotsymbol}
+% These place a symbol centered at each of the graph
+% coordinate points in the list. The symbol placed is the first parameter,
+% which would normally be a path, but can be a picture or, in \MP, a
+% string. Like the \gbc{doplot} command, it calls \gbc{makesymbol}.
+% If \gbc{spath} is of type path, and is cyclic, it is drawn filled. This
+% is because we call \gbc{makesymbol} on it, and that subjects it to
+% \gbc{setdot}, which has that behavior. For other types of symbols, we
+% simply convert them to pictures with \gbc{makesymbol} and then place
+% them. Unlike \gbc{pointd} above, the interior of the path is not
+% erased by default. However, in the special case where the symbol is an
+% open path, if its first point is equal to its last point, and
+% \gbc{clearsymbols} is true, then the interior of the path obtained by
+% \gbc{\& cycle} is cleared before the path itself is drawn.
+%
+% \DescribeRoutine{dosymbols}
+% \DescribeRoutine{addsymbols}
+% We copy the text list to an array and call \gbc{dosymbols} so that
+% \gbc{plotnodes} can share the code. Also, since \gbc{dosymbols} uses
+% identical code twice (once to clear, once to draw), we put that code
+% in \gbc{addsymbols}
+% \begin{macrocode}
+boolean clearsymbols; clearsymbols := false;
+vardef clearable (expr pth) =
+ false
+ if path pth :
+ if (not cycle pth) and (length pth > 0):
+ if ( pnt0 (pth) = pnt[length pth] (pth) ) :
+ or true
+ fi fi fi
+enddef;
+
+def plotsymbol = colorplotsymbol (pointcolor) enddef;
+vardef colorplotsymbol (expr clr, spath, sc) (text t) =
+ save _cpls;
+ textpairs (_cpls) (t);
+ dosymbols (clr, spath, sc) (_cpls);
+enddef;
+
+vardef dosymbols (expr clr, spath, sc) (suffix arr) =
+ save one_symbol, _pls; picture one_symbol, _pls;
+ if clearsymbols and clearable (spath):
+ addsymbols (background, spath&cycle, sc) (arr);
+ fi
+ addsymbols (clr, spath, sc) (arr);
+enddef;
+
+def addsymbols (expr clr, spath, sc) (suffix arr) =
+ one_symbol := makesymbol (spath, sc);
+ _pls := nullpicture;
+ for _idx = 1 upto arr:
+ picdot (_pls, one_symbol, zconv(arr[_idx]));
+ endfor
+ DoClip (_pls);
+%<MF> mono (_pls);
+ coloraddto (clr) (active_plane) (_pls);
+enddef;
+
+% \end{macrocode}
+%
+%
+% \section{Modification of paths}\label{modification}
+%
+% \subsection{Closing a path}\label{closing}
+%
+% In \MF{} one can close a path by any legal path connection between the
+% last point and the keyword \mfc{cycle}. Connecting the last point to the
+% first point is not enough. \Grafbase{} commands provide a few different
+% ways. All the closure commands have a version with a tension parameter
+% when that makes sense. These version make the connection with the
+% supplied tension. The ones where it doesn't make sense are \gbc{lclosed},
+% \gbc{cbclosed} and \gbc{qbclosed}. The first always uses a straight line
+% and the other two require explicit controls.
+%
+% \DescribeRoutine{lclosed}
+% This closes with a line segment.
+% \begin{macrocode}
+vardef lclosed expr f = f if not cycle f : --cycle fi enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{sclosed}
+% \DescribeRoutine{sclosedt}
+% This closes the path in the manner that \gbc{mksmooth} creates a path.
+% This will change the first and last segment of the original path. In
+% particular, if there are fewer than three segments, the whole path is
+% different.
+% \begin{macrocode}
+numeric default_tension;
+default_tension := 1;
+
+def sclosed = sclosedt (default_tension) enddef;
+vardef sclosedt (expr t) expr f =
+ if cycle f : f
+ else: save n; n := length f;
+ if n = 0 : f&cycle
+ elseif n = 1 : f..tension t..cycle
+ else :
+ (pnt0 (f)) { (pnt1(f)) - (pnt[n] (f)) }..tension t
+ ..(subpath (1, n-1) of f)..tension t
+ ..(pnt[n](f)) { pnt0(f) - pnt[n-1](f) }
+ ..tension t..cycle
+ fi
+ fi
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{bclosed}
+% This closes with the basic default \MF{} Bezi\'er. It is a smooth
+% closure, but it does not have the same direction at the endpoints
+% that \gbc{mksmooth (true)} would have produced.
+% \begin{macrocode}
+def bclosed = bclosedt (default_tension) enddef;
+vardef bclosedt (expr t) expr f =
+ f if not cycle f : ..tension t..cycle fi
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{uclosed}
+% This is now just a renaming of \gbc{bclosed}. It turns out (contrary
+% to my earlier belief) that just adding \mfc{..cycle} does \emph{not}
+% change the original curve. \gbc{sclosed} will do that because it
+% takes the curve apart and redoes its end segments.
+% \begin{macrocode}
+def uclosed = bclosed enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{bsplinecontrols}
+% This utility is for use in \gbc{cbclosed}. It converts Bezier segment
+% key points of a path \gbc{f}, to cubic B-spline control points stored
+% in an array \gbc{b}. The data needed are the first point and first two
+% control points of \gbc{f}. The B-spline points needed are \gbc{b1} and
+% \gbc{b4}. The extra two points \gbc{b2} and \gbc{b3} divide the line
+% from \gbc{b1} to \gbc{b4} into thirds and will be turned into Bezier
+% control points of a new path segment.
+% \begin{macrocode}
+def bsplinecontrols (suffix b) expr f =
+ b := 4;
+ b1 := 2[pre 1(f), post0(f)];
+ b2 := 2[post0(f), pnt 0(f)];
+ b3 := 2[b1, b2];
+ b4 := 2[b2, b3];
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{cbclosed}
+% This closes a path with a cubic B-spline. If the path \gbc{f} had been
+% produced by \gbc{opencbs}, then \gbc{q1} and \gbc{q4} would have been the
+% last two points in the argument list, and \gbc{p4} and \gbc{p1} would
+% have been the first two. We just use them and mimic the effect of
+% \gbc{closedcbs}.
+% \begin{macrocode}
+vardef cbclosed expr f =
+ if cycle f : f
+ elseif (length f)=0 : f&cycle
+ else:
+ save p, q; pair p[], q[];
+ bsplinecontrols (p) f; % defines p1 to p4
+ bsplinecontrols (q) reverse f; % defines q1 to q4
+ f..controls q2 and q3..opencbs (q1,q4,p4,p1)
+ ..controls p3 and p2..cycle
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{qbclosed}
+% It seems wrong to be able to close with a cubic B-spline but not a
+% quadratic B-spline. Therefore I will add such a possibility. We
+% calculate B-spline controls \gbc{p[n]} that will agree with those of
+% \gbc{f}, if \gbc{f} had been created as a quadratic B-spline. Note
+% that \gbc{cbclosed} required three \MF{} links to close the curve;
+% \gbc{qbclosed} only requires two.
+% \begin{macrocode}
+vardef qbclosed expr f =
+ if cycle f : f
+ else: save n; n := length f;
+ if n = 0 : f&cycle
+ else:
+ save p; pair p[]; p := 4;
+ p1 := (3/2)[pnt[n](f), pre[n](f)];
+ p2 := 2[p1, pnt[n](f)];
+ p4 := (3/2)[pnt 0 (f), post0 (f)];
+ p3 := 2[p4, pnt 0 (f)];
+ f & mkqbs (p) & cycle
+ fi
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{makesector}
+% This makes sense only if the path being modified is an arc. It closes
+% the arc by connecting its ends to the center of the circle, as
+% computed by \gbc{pathcenter}.
+% \begin{macrocode}
+vardef makesector expr p =
+ (pathcenter p)--p--cycle
+enddef;
+
+% \end{macrocode}
+% \subsection{Trimming a path}\label{trimming}
+%
+% \DescribeRoutine{cutoffbefore}
+% \DescribeRoutine{cutoffafter}
+% This is a useful utility operation present in \file{plain.mp} but
+% missing from \file{plain.mf}. We write a different version for our
+% purposes; it has the syntax of most of our path modification
+% commands. Plus, the first loop tries to avoid a bug (or perhaps
+% inaccuracy) in \mfc{intersectiontimes} which can return an intersection
+% time in a later segment of \gbc{f} than the first intersection point.
+% If I can learn the actual method used to find intersection times, I'll
+% put in some \mfc{solve} code to get the first \gbc{t} rather than the
+% minimal `shuffled binary' of the pair \gbc{w}.
+% \begin{macrocode}
+%<MF>path cuttings;
+vardef cutoffbefore (expr b) expr f =
+ save w, t, u, n; n:= length f;
+ pair w;
+ for k = 1 upto n :
+ w := (subpath (0,k) of f) intersectiontimes b;
+ exitif w > left;
+ endfor
+ if debug :
+ GBdebug;
+ >> "Intersectiontimes:";
+ show w;
+ GBenddebug;
+ fi
+ t := xpart w;
+ if t < 0:
+ cuttings := pnt0 (f);
+ f
+ else:
+ cuttings := subpath (0,t) of f;
+ subpath (t, n) of f
+ fi
+enddef;
+
+vardef cutoffafter (expr b) expr f =
+ save g; path g;
+ g := cutoffbefore (b) reverse f;
+ cuttings := reverse cuttings;
+ reverse g
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{trimmedpath}
+% This takes two lengths and a path and trims off the ends of the path
+% that lie within the given lengths of the endpoints. The lengths are in
+% device coordinates, the path in graph coordinates.
+% \begin{macrocode}
+vardef trimmedpath (expr btrim, etrim) expr f =
+ save g, h; path g, h;
+ g := invvconv (fullcircle scaled 2btrim) shifted pnt0(f);
+ h := invvconv (fullcircle scaled 2etrim) shifted pnt[length f] (f);
+ cutoffafter (h) cutoffbefore (g) f
+enddef;
+
+% \end{macrocode}
+%
+% \subsection{Appending an arrowhead}
+%
+%
+% \DescribeRoutine{predirection}
+% \DescribeRoutine{postdirection}
+% \DescribeRoutine{__dir}
+% First, some better \mfc{direction} commands. They makes use of the fact
+% (easily proved) that a cubic B\'ezier $z\sb0(1 - t)^3 + 3z\sb1(1-t)^2t +
+% z\sb2(1-t)t^2 + z\sb3t^3$ has a direction at $z\sb0$ equal to the first
+% one of $z\sb{j} -z\sb0$ that is nonzero.
+%
+% \gbc{__dir} gets the direction at point 0 for an arbitrary path.
+% \gbc{postdirection} reduces to this case using \mfc{subpath}. If the
+% postdirection is 0, that means the path is trivial from that point to
+% the end so we are effectively at an endpoint (noncyclic path) and we use
+% the incoming direction. \gbc{predirection} just runs
+% \gbc{postdirection} on the reversed path.
+% \begin{macrocode}
+vardef predirection@# (expr p) =
+ - postdirection[length p - @#] (reverse p)
+enddef;
+
+vardef postdirection@# (expr p) =
+ save _n; _n := length (p);
+ save v; pair v; v := __dir (subpath (@#, @# + _n) of p);
+ if v = origin :
+ v := - __dir (subpath (@#, @# - _n) of p);
+ fi
+ v
+enddef;
+
+vardef __dir (expr p) =
+ save v, w; pair v, w; w := pnt0 (p);
+ v := origin;
+ for n = 1 upto length (p) :
+ v := post[n-1] (p) - w;
+ if v = origin :
+ v := pre[n] (p) - w;
+ if v = origin :
+ v := pnt[n] (p) - w;
+ fi
+ fi
+ exitif v <> origin;
+ endfor
+ v
+enddef;
+
+% \end{macrocode}
+%
+% Arrowheads can be just two straight lines at an angle placed on the end
+% of a curve, or it can be a filled triangle. \grafbase{} permits both,
+% but it also allows the two lines (or the corresponding sides of the
+% triangle) to be gracefully concave and tangent to the path at the
+% endpoint of the path. The parameters controlling the shape of the arrowhead
+% are the two numerics \gbc{hdwdr}, the ratio of the length to width of the
+% arrowhead, and \gbc{hdten}, the tension in the two angled curves. By
+% default, one side of an arrowhead is just the \MF{} path
+% \mfc{a..b\marg{\meta{tangent}}}, where \mfc{a} is the base of the
+% arrowhead (calculated from \gbc{hdwdr}) and \gbc{b} is the end of the
+% path and \meta{tangent} is the direction of the curve at that
+% point. The curve can be straightened by increasing \gbc{hdten}, the
+% head widened by increasing \gbc{hdwdr}
+%
+% The arrowhead is drawn by drawing two of the curves described above. If
+% \gbc{hfilled} is \mfc{true}, the two base points (\gbc{a} above) are
+% connected and the three sided region filled.
+% \begin{macrocode}
+newinternal hdwdr, hdten;
+interim hdwdr := 1; interim hdten := 1;
+boolean hfilled; hfilled := false;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{headshape}
+% The following little utility adjusts the above parameters, call it
+% with two pure numbers \gbc{wr} and \gbc{tens} for the \gbc{hdwdr} and
+% \gbc{hdten}, and a boolean \gbc{fil} for \gbc{hfilled}.
+% \begin{macrocode}
+def headshape (expr wr, tens, fil) =
+ interim hdwdr := wr; interim hdten := tens;
+ save hfilled; boolean hfilled; hfilled := fil;
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{ahead}
+% \DescribeRoutine{colorhead}
+% This command draws an arrowhead. \gbc{front} and \gbc{back} are in
+% device coordinates. They are the point of the arrowhead (\gbc{front})
+% and the point such that \gbc{front - back} points in the direction of
+% the arrow. We use the ratio \gbc{hwr} to compute the other two
+% corners. So \gbc{side} is the vector from \gbc{back} to one of the
+% corners \gbc{p1}, and the other corner is on the other side. \gbc{f}
+% is the path of the arrowhead.
+%
+% If \gbc{filled} is true we close the curve and fill it, otherwise we
+% draw it. \gbc{clr} is the color used to draw or fill it.
+%
+% For backward compatibility we define \gbc{head}. In \MF{} \gbc{head}
+% didn't have a color parameter, while in \MP{} it has always had one,
+% in retrospect, this was not a good idea, and we should have followed
+% the pattern of other macros. However, \gbc{head} was never a user-level
+% macro and I didn't think it mattered.
+% \begin{macrocode}
+%<MF>def head = ahead (headcolor) enddef;
+%<MP>def head = ahead enddef;
+
+vardef ahead (expr clr, front, back, hwr, tens, filled) =
+ if front <> back :
+ save side; pair side;
+ side := (hwr/2) * ((front-back) rotated 90);
+ save f; path f;
+ f := (back + side)..tension tens..
+ {front-back}front{back-front}..tension tens..
+ (back - side)
+ if filled : --cycle; colorsafefill (clr) f fi;
+ colorsafedraw (clr) f;
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% It is a fact of life that, unless the path to which the head is added is
+% a straight line, the above described arrowhead looks ``off''. But I know
+% of no automatic way of making it look good. Therefore \grafbase{} and
+% \mfpic{} have provided a means to micro-adjust the head. (Actually, I
+% think the best looking arrowhead for small heads and paths of modest
+% curvature is obtained by taking a secant for the direction of the head
+% head rather than a tangent.)
+%
+% \DescribeRoutine{headpath}
+% \DescribeRoutine{colorheadpath}
+% This takes a path expression \gbc{f} in graph coordinates, puts an
+% arrowhead on it and returns \gbc{f}. The arrowhead is placed according
+% to the first four parameters. \gbc{hlen} is the length of the head in
+% device coordinates, the width being determined by \gbc{hdwdr}, and
+% and \gbc{hrot} is a rotation adjustment. \gbc{hback} is a distance (in
+% device coordinates) by which it is set back from the point of placement.
+% It is set back in the direction determined after the rotation.
+%
+% If the length of the head is 0, we just skip everything and return
+% \gbc{f}.
+%
+% \gbc{headpath} calls \gbc{colorheadpath} with the color set to
+% \gbc{headcolor}.
+% \begin{macrocode}
+def headpath = colorheadpath (headcolor) enddef;
+vardef colorheadpath (expr clr, hlen, hrot, hback) expr f =
+ if hlen <> 0 :
+ save g; path g; g := zconv (f);
+ save P; pair P[];
+ P2 := pnt[length g] (g);
+ P1 := predirection[length g] (g);
+ if P1 <> (0, 0) :
+ P3 := (unitvector P1) rotated hrot;
+ P4 := P2 - (hback * P3);
+ P5 := P4 - (hlen * P3);
+ ahead (clr, P4, P5, hdwdr, hdten, hfilled);
+ fi
+ fi
+ f
+enddef;
+
+% \end{macrocode}
+%
+% \section{Axes, Axis Tic Marks, and Grids}
+%
+% \DescribeRoutine{arrowdraw}
+% This is used elsewhere only to draw axes. It returns nothing. This
+% doesn't follow the usual pattern of drawing something and returning the
+% same path. This makes the old \cs{axes}, \cs{xaxis} and \cs{yaxis}
+% commands in \mfpic{} impossible to dash or dot. The newer axis drawing
+% commands permit this and so use other code.
+%
+% We simply call \gbc{headpath} with default values, but add \gbc{drawn}
+% to make sure the path is drawn, and precede it with \gbc{store} so
+% \MF{} won't complain of an isolated expression. The new axis commands
+% just call \gbc{headpath}, and the \mfpic{} code makes sure it is drawn,
+% but it can also be \gbc{dashed}, \gbc{dotted}, and \gbc{doplot}\,ed.
+%
+% The order is important if axis and head are different colors. This
+% order puts the head on top of the shaft.
+% \begin{macrocode}
+def arrowdraw (expr hlen) (expr f) =
+ store (curpath) headpath (hlen, 0, 0) drawn f;
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{xaxis}
+% \DescribeRoutine{yaxis}
+% \DescribeRoutine{axes}
+% These draw the obvious things: the corresponding axis or axes through
+% the point (0, 0) in graph coordinates. The only parameter is the length
+% of the arrowhead in device coordinates. \gbc{axes} draws both axes with
+% the same length of head.
+% \begin{macrocode}
+def xaxis (expr hlen) = arrowdraw (hlen) ((xneg, 0)--(xpos, 0)); enddef;
+def yaxis (expr hlen) = arrowdraw (hlen) ((0, yneg)--(0, ypos)); enddef;
+def axes (expr hlen) = xaxis (hlen); yaxis (hlen); enddef;
+
+% \end{macrocode}
+%
+% For axes at the borders of the graph coordinates, we allow for them to
+% be shifted inwards. The amount of the shift is given by \gbc{laxis} for
+% the left side axis, \gbc{baxis} for the bottom axis, etc. They are in
+% graph coordinates.
+%
+% \DescribeRoutine{axisline.x}
+% \DescribeRoutine{axisline.y}
+% \DescribeRoutine{axisline.l}
+% \DescribeRoutine{axisline.b}
+% \DescribeRoutine{axisline.r}
+% \DescribeRoutine{axisline.t}
+% The commands \gbc{axisline.l}, etc., return the straight line along the
+% corresponding edge shifted the appropriate amount. These are vardefs
+% rather than variables so they can be affected by changing shift values.
+%
+% \DescribeRoutine{axis}
+% Finally, the commands \gbc{axis.x}, etc. examine their suffix and
+% apply \gbc{headpath} to the corresponding axis line. With a recent
+% change in \mfpic{} code, it no longer gets used, as we now simply apply
+% \cs{arrow} to the appropriate \gbc{axisline}. The change was made for
+% consistency: so that the head would be drawn on top of the shaft.
+% \begin{macrocode}
+laxis := baxis := raxis := taxis := 0;
+
+vardef axisline.x = (xneg + laxis, 0)--(xpos - raxis, 0) enddef;
+vardef axisline.y = (0, yneg + baxis)--(0, ypos - taxis) enddef;
+vardef axisline.l = axisline.y shifted (xneg + laxis, 0) enddef;
+vardef axisline.b = axisline.x shifted (0, yneg + baxis) enddef;
+vardef axisline.r = axisline.y shifted (xpos - raxis, 0) enddef;
+vardef axisline.t = axisline.x shifted (0, ypos - taxis) enddef;
+vardef axis@# (expr len) =
+ headpath (len, 0, 0) axisline@#
+enddef;
+
+% \end{macrocode}
+%
+% Tick marks can be on the inside or outside of a border axis,
+% above or below any horizontal axes, left or right of any vertical axis
+% or centered on any axis. The following numerics are merely used to
+% convert the names to numeric code that the drawing routine will examine.
+%
+% However, it is no accident that \gbc{onbottom = onright} and that
+% \gbc{centered} is halfway between \gbc{onright} and \gbc{onleft}. The
+% code uses the numeric values to compute a shift, and one can supply an
+% expression like \gbc{.33ontop+.67onbottom]} and then 1/3 of each mark
+% will be above (and 2/3 will be below) the axis.
+%
+% The negative value of \gbc{inside} and \gbc{outside} is a flag that they
+% are to be treated differently. The others have the property that the
+% direction is the direction of the axis rotated a certain way (e.g.,
+% $90$ degrees from \mfc{up} points \mfc{left}, $-90$ points \mfc{right}).
+% But \gbc{inside} is right of the left axis and left of the right axis.
+% \begin{macrocode}
+numeric inside, outside, centered, onleft, onright, ontop, onbottom;
+inside := -2;
+outside := -1;
+onright := 1;
+onleft := 2;
+centered := .5[onright, onleft];
+onbottom := onright;
+ontop := onleft;
+% \end{macrocode}
+%
+% We interact with \mfpic{} by allowing the user to change the value of
+% \gbc{ltick}, for example, with a command like \
+% \cs{setaxismarks l}\marg{outside}. Here we set the defaults.
+% \begin{macrocode}
+ltick := rtick := ttick := btick := inside;
+xtick := ytick := centered;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{axismarks}
+% This utility macro draws the tick marks on an arbitrary axis. The
+% different commands \gbc{xmarks}, etc., call this command with particular
+% values of these parameters.
+% \begin{itemize}
+% \item \gbc{inang} is the direction one must rotate the axis to point
+% inside. This is always $\pm90$ degrees. The x-axis and y-axis are
+% treated just like bottom and left axis in this respect.
+% \item \gbc{tp} is the tick position (e.g., \gbc{inside} or
+% \gbc{ontop}).
+% \item \gbc{loc} is the location of the 0-point of the axis (graph
+% coordinates).
+% \item \gbc{pdir} is the positive direction on the axis (right or up).
+% \item \gbc{len} is the length of a tick mark, supplied as an argument
+% to the individual axis mark commands.
+% \item \gbc{t} is the list of positions, also supplied.
+% \end{itemize}
+% \begin{macrocode}
+vardef axismarks (expr inang, tp, loc, pdir) (expr len) (text t) =
+ save _tp, _U, _P, _tic, _ticang;
+ pair _U, _P, _tic[];
+% \end{macrocode}
+% For \gbc{onleft}, \gbc{onright}, \gbc{ontop} or \gbc{onbottom}, which
+% are positive, don't examine \gbc{inang} but for \gbc{inside/outside}
+% use it to determine what inside means. \gbc{_ticang} will be the angle
+% to rotate \gbc{pdir} to set the direction of the tic mark.
+%
+% Then we shift the numeric value of \gbc{tp} by one, so \gbc{centered}
+% corresponds to $.5$ and the rest to either $0$ or $1$.
+% \begin{macrocode}
+ _ticang := if tp<0 : inang else: 90 fi;
+ _tp := abs(tp) - 1;
+% \end{macrocode}
+% Except, we go through the following shenanigans so that the marks are
+% always perpendicular to the axis, even if a coordinate transform will
+% slant the axis. After this \gbc{_U} should point in direction of inside,
+% onleft or ontop.
+% \begin{macrocode}
+ _U := unitvector (vconv (pdir)) rotated _ticang;
+% \end{macrocode}
+% Next, we use \gbc{_tp} to calculate the ends of the mark. For example,
+% if \gbc{tp = inside}, then \gbc{_tp = 1}. Since \gbc{_U} points toward
+% inside, \gbc{_tic2} will be \gbc{len} toward the inside and \gbc{_tic1 =
+% (0, 0)}.
+% \begin{macrocode}
+ _tic1 := (_tp - 1) * len * _U; % start of mark
+ _tic2 := _tp * len * _U; % end of mark
+% \end{macrocode}
+% Finally, convert each numeric position to a point on the axis, a
+% multiple of \gbc{pdir} from the 0 point of the axis, and than draw the
+% tic.
+% \begin{macrocode}
+ for _a = t:
+ safedraw ((_tic1--_tic2) shifted zconv (loc + _a*pdir));
+ endfor
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{xmarks}
+% \DescribeRoutine{ymarks}
+% \DescribeRoutine{lmarks}
+% \DescribeRoutine{bmarks}
+% \DescribeRoutine{rmarks}
+% \DescribeRoutine{tmarks}
+% And now the specialized command for each axis. Inside and outside
+% really make no sense for the x- and y-axis, but since a bottom axis is
+% usually used for x and a left axis for y, we give \gbc{xmarks} the same
+% first parameter as \gbc{bmarks} and \gbc{ymarks} the same as \gbc{lmarks}.
+% \begin{macrocode}
+def xmarks = axismarks ( 90, xtick, (0, 0), right) enddef;
+def ymarks = axismarks (-90, ytick, (0, 0), up) enddef;
+def lmarks = axismarks (-90, ltick, (xneg + laxis, 0), up) enddef;
+def bmarks = axismarks ( 90, btick, (0, yneg + baxis), right) enddef;
+def rmarks = axismarks ( 90, rtick, (xpos - raxis, 0), up) enddef;
+def tmarks = axismarks (-90, ttick, (0, ypos - taxis), right) enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{vgrid}
+% \DescribeRoutine{grid}
+% This is mainly for the purpose of visualising coordinates. \gbc{vgrid}
+% draws a dot of size \gbc{dsize} at every point whose coordinates
+% are are \gbc{(n*xspace, m*yspace)}, \gbc{n} and \gbc{m} being integers.
+% \gbc{dsize} is in device coordinates, the spacings are in graph
+% coordinates. \gbc{grid} is for backward compatibility, calling vgrid
+% with a default \gbc{dsize} of \mfc{.5bp}.
+%
+% \begin{macrocode}
+path griddotpath; griddotpath := fullcircle;
+def grid = vgrid (0.5bp) enddef;
+vardef vgrid (expr dsize, xspace, yspace) =
+ save gdot, gridpic; picture gdot, gridpic;
+ gdot := setdot (griddotpath, dsize);
+ gridpic := nullpicture;
+ for n = ceiling(xneg/xspace) upto floor(xpos/xspace):
+ for m = ceiling(yneg/yspace) upto floor(ypos/yspace):
+ picdot (gridpic, gdot, zconv((n*xspace, m*yspace)));
+ endfor
+ endfor
+%<MF> mono (gridpic);
+ coloraddto (pointcolor) (active_plane) (gridpic);
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{hgridlines}
+% \DescribeRoutine{vgridlines}
+% \DescribeRoutine{gridlines}
+% This is more what I think of when I hear ``grid'', but the name was already
+% taken. \gbc{gridlines} draws horizontal and vertical lines through the
+% same points where \gbc{grid} would draw a dot. To draw only horizontal
+% or only vertical lines use \gbc{hgridlines} or \gbc{vgridlines}.
+% \begin{macrocode}
+def hgridlines (expr ysp) =
+ for n = ceiling((yneg + baxis)/ysp) upto floor((ypos - taxis)/ysp) :
+ safedraw zconv((xneg + laxis, n*ysp)--(xpos - raxis, n*ysp));
+ endfor
+enddef;
+def vgridlines (expr xsp) =
+ for n = ceiling((xneg + laxis)/xsp) upto floor((xpos - raxis)/xsp) :
+ safedraw zconv((n*xsp, yneg + baxis)--(n*xsp, ypos - taxis));
+ endfor
+enddef;
+def gridlines (expr xsp, ysp) =
+ vgridlines (xsp); hgridlines (ysp);
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{plrpatch}
+% \DescribeRoutine{patcharcs}
+% \DescribeRoutine{patchrays}
+% Polar grids can be drawn two ways. \gbc{patcharcs} draws the arcs
+% \gbc{tstart}${}\le \theta \le{}$\gbc{tstop} with $r = {}$\gbc{rstart},
+% stepping by \gbc{rstep} until \gbc{rstop}. \gbc{patchrays} draws the lines
+% \gbc{rstart}${}\le r \le{}$\gbc{rstop} with $\theta = {}$\gbc{tstart}
+% stepping by \gbc{tstep} until \gbc{tstop}. \gbc{plrpatch} then calls
+%
+% They are utilities that draw on a picture variable \gbc{X}, and then a
+% calling command like \gbc{plrpatch} adds them to \gbc{active_plane}.
+% \gbc{plrpatch} used to be called by \gbc{polarpatch}, but now it is
+% not called at all.
+% \begin{macrocode}
+vardef plrpatch (expr rstart, rstop, rstep, tstart, tstop, tstep) =
+ save v; picture v; v := nullpicture;
+ patcharcs (v) (rstart, rstop, rstep, tstart, tstop);
+ coloraddto (drawcolor) (active_plane, v);
+ v := nullpicture;
+ patchrays (v) (tstart, tstop, tstep, rstart, rstop);
+ coloraddto (drawcolor) (active_plane, v);
+enddef;
+def patcharcs (suffix X) (expr rstart, rstop, rstep, tstart, tstop) =
+ for rad = (if rstart=0: rstep else: rstart fi) step rstep until rstop:
+ orto (X, picpath zconv (arcplr ((0, 0), tstart, tstop, rad)) );
+ endfor
+enddef;
+def patchrays (suffix X) (expr tstart, tstop, tstep, rstart, rstop) =
+ for _ang = tstart step tstep until tstop:
+ orto (X) (picpath zconv ((rstart*dir _ang)--(rstop*dir _ang)));
+ endfor
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{polargrid}
+% \DescribeRoutine{polargridpoints}
+% \DescribeRoutine{gridarcs}
+% \DescribeRoutine{gridrays}
+% These are analogous to \gbc{gridlines} and \gbc{grid}. They first draw a
+% grid large enough to cover the whole graph, then clip it to the. graph
+% boundaries. The arcs have radii that are multiples of \gbc{rstep} and
+% radial lines have angles that are multiples of \gbc{tstep}. The command
+% \gbc{polargridpoints} draws dots at the points where the lines and arcs
+% in \gbc{poloargrid} would intersect. The `step' parameters are in graph
+% coordinates. \gbc{beginpolargrid} computes the bounds for the patch and
+% declares the picture variable \gbc{gridpic}, while \gbc{endpolargrid}
+% clips the resulting picture and adds it to \gbc{active_plane}.
+%
+% The \gbc{rmin}, etc., returned are modified to fit the grid established
+% by the step sizes. A ray could happen to be one of the graph's sides, so
+% we use \mfc{ceiling} and \mfc{floor} which doesn't change integer
+% values. However, the arc with radius \gbc{rmin} or \gbc{rmax} could
+% touch the graph rectangle in at most 4 points, so we use \mfc{floor (1
+% + x)} and \mfc{ceiling(x - 1)} to start and stop before the edge of the
+% graph.
+% \begin{macrocode}
+def polargrid (expr rstep, tstep) =
+ gridarcs (rstep); gridrays (tstep);
+enddef;
+
+def polargridpoints (expr dsize, rstep, tstep) =
+ beginpolargrid;
+ save gdot; picture gdot; gdot := setdot (griddotpath, dsize);
+ if rmin = 0:
+ picdot (gridpic, gdot, zconv(origin));
+ rmin := rstep;
+ fi
+ for n = ceiling (rmin/rstep) upto floor (rmax/rstep) :
+ for m = ceiling (tmin/tstep) upto floor (tmax/tstep) :
+ picdot ( gridpic, gdot, zconv ( polar((n*rstep, m*tstep)) ) );
+ endfor
+ endfor
+ endpolargrid (pointcolor, .5dsize);
+enddef;
+
+def gridarcs (expr rstep) =
+ beginpolargrid;
+ if rmin = 0 : % add "circle" of radius 0
+ picdot (gridpic, setdot(griddotpath, penwd), zconv(origin));
+ fi
+ rmin := rstep * floor(rmin/rstep + 1);
+ rmax := rstep*ceiling(rmax/rstep - 1);
+ patcharcs (gridpic) (rmin, rmax, rstep, tmin, tmax);
+ endpolargrid (drawcolor, .5penwd);
+enddef;
+
+def gridrays (expr tstep) =
+ beginpolargrid;
+ tmin := tstep*ceiling(tmin/tstep);
+ tmax := tstep * floor(tmax/tstep);
+ patchrays (gridpic) (tmin, tmax, tstep, rmin, rmax);
+ endpolargrid (drawcolor, .5penwd);
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{beginpolargrid}
+% This computes the bounds (on $r$ and $\theta$) of the smallest polar
+% coordinate patch that covers the graph rectangle. It leaving the values
+% in \gbc{rmin}, \gbc{rmax}, \gbc{tmin} and \gbc{tmax}. It is only for use
+% in \gbc{polargrid}, \gbc{gridarcs} and \gbc{gridrays}.
+% \begin{macrocode}
+def beginpolargrid =
+ begingroup;
+ save p, r, t, rmax, rmin, tmax, tmin;
+ pair p[];
+ % Four corners:
+ p0 := (xneg, yneg); p1 := (xneg, ypos);
+ p2 := (xpos, ypos); p3 := (xpos, yneg);
+% \end{macrocode}
+% This loop finds the radial coordinate of each corner of the graph and
+% finds the maximum while doing so.
+% \begin{macrocode}
+ r0 := abs(p0); rmax := r0;
+ for j = 1 upto 3 :
+ r[j] := abs(p[j]);
+ if rmax < r[j] : rmax := r[j]; fi
+ endfor
+% \end{macrocode}
+% When the origin is inside the graph rectangle we need the full range
+% of $r$ and $\theta$. When the origin is one of the corners, the angles
+% can just be read off. Otherwise, to find the range of $\theta$ we
+% rotate one corner to have angle zero (so now we are guaranteed all
+% angles are between $-180$ and $180$) and get the largest and smallest of
+% the angles to all the corners.
+% \begin{macrocode}
+ rmin := 0;
+ if (xneg < 0) and (xpos > 0) and (yneg < 0) and (ypos > 0) :
+ tmin := 0; tmax := 360;
+ elseif (p0 = (0,0)) : tmin := 0; tmax := 90;
+ elseif (p1 = (0,0)) : tmin := -90; tmax := 0;
+ elseif (p2 = (0,0)) : tmin := -180; tmax := -90;
+ elseif (p3 = (0,0)) : tmin := 90; tmax := 180;
+ else :
+ tmax := tmin := t0 := angle p0;
+ for j = 1 upto 3:
+ t := t0 + angle (p[j] rotated -t0);
+ if tmax < t : tmax := t; fi
+ if tmin > t : tmin := t; fi
+ endfor
+% \end{macrocode}
+% The minimum value of $r$ can be one of 9 possibilities: if the four
+% sides of the graph are extended infinitely far in both directions, the
+% origin can be in any one of the 9 regions formed. We've already disposed
+% of the inside of the graph. This code considers the remaining regions in
+% the following order: (1)~above or below, (2)~left or right, and (3)~one
+% of the four corner regions.
+% \begin{macrocode}
+ if (xneg < 0) and (xpos > 0) : % (1)
+ rmin := emin(abs(yneg), abs(ypos));
+ elseif (yneg < 0) and (ypos > 0) : % (2)
+ rmin := emin(abs(xneg), abs(xpos));
+ else : % (3)
+ rmin := min(r0, r1, r2, r3);
+ fi
+ fi
+ save gridpic; picture gridpic; gridpic := nullpicture;
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{endpolargrid}
+% The \gbc{clr} is \gbc{drawcolor} for line grids, \gbc{pointcolor} for
+% dot grids. The size is half the width of the grid's lines or half the
+% width of the grid's dots. The purpose is to make sure dots and lines on
+% the graph's edge aren't cut off. For dots I should probably put this
+% decision in the code that draws them on \gbc{gridpic}.
+% \begin{macrocode}
+
+def endpolargrid (expr clr, size)=
+ clipto (gridpic) rect ( zconv((xneg, yneg)) - size*(1,1),
+ zconv((xpos, ypos)) + size*(1,1) );
+ coloraddto (clr) (active_plane) (gridpic);
+ endgroup
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{polarpatch}
+% Finally, this just does \gbc{plrpatch}, but also draws the ending
+% boundaries, in case they are not an integer number of steps from the
+% start.
+% \begin{macrocode}
+vardef polarpatch (expr rstart, rstop, rstep, tstart, tstop, tstep) =
+ plrpatch (rstart, rstop, rstep, tstart, tstop, tstep);
+ safedraw zconv ( arcplr ((0, 0), tstart, tstop, rstop) );
+ safedraw zconv ( ((rstart, 0)--(rstop, 0)) rotated tstop );
+enddef;
+
+% \end{macrocode}
+%
+% \section{Path construction}
+%
+% \DescribeRoutine{rect}
+% Most of the macros that only define paths are coordinate independent.
+% The simplest is \gbc{rect}. It accepts two pair expressions and produces
+% the upright rectangle with those points at opposite corners. It might be
+% noted that if the corners really are lower left and upper right, then
+% the path is anticlockwise, If they are on the other diagonal, the
+% path is clockwise. The path is a cycle (closed).
+%
+% \DescribeRoutine{triangle}
+% Produces a closed path joining three points with straight lines; first
+% named point \gbc{A} is \mfc{point 0 of triangle (A, B, C)}, etc.
+% \begin{macrocode}
+vardef rect (expr ll, ur) =
+ ll--(xpart ur, ypart ll)--ur--(xpart ll, ypart ur)--cycle
+enddef;
+vardef triangle (expr A, B, C) = A--B--C--cycle enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{regularpolygon}
+% The first argument is the number of sides, the second is an array name
+% to hold the list of vertices. The third argument contains two
+% equations, preferably the location of two of the vertices, or the
+% location of the center and one vertex. That plus the equations in the
+% \mfc{for}-loop give \gbc{n+1} equations to determine the \gbc{n}
+% vertices and the center. Note that the vertices are numbered
+% anticlockwise.
+% \begin{macrocode}
+vardef regularpolygon (expr n) (suffix Bob) (text eqns) =
+ pair Bob[]; Bob := emax(round (abs (n)), 2);
+ eqns;
+ for _uncle = 1 upto Bob - 1 :
+ (Bob1 - Bob0) rotated (360*_uncle/Bob) = Bob[_uncle+1] - Bob0;
+ endfor
+ mkpoly (true) (Bob)
+enddef;
+
+% \end{macrocode}
+%
+% The following set of commands accept any path as argument, but it is
+% intended that it be a triangle. Even then, they work correctly only if
+% it is a cycle.
+%
+% \DescribeRoutine{altitudept}
+% \DescribeRoutine{altitude}
+% These first two produce the perpendicular from \gbc{point n of t}
+% to the (extension of) the opposite side (i.e., the altitude). The
+% first one determines where the altitude meets the opposite side, and the
+% second just connects the two points
+% \begin{macrocode}
+vardef altitudept expr n of t =
+ save A, B, C, zz; pair A, B, C, zz;
+ A := pnt[n] (t);
+ B := pnt[n + 1] (t); % wraps around a cyclic path
+ C := pnt[n + 2] (t);
+ zz = whatever[B,C];
+ zz = A + whatever*((C-B) rotated 90);
+ zz
+enddef;
+
+vardef altitude expr n of t =
+ (pnt[n](t))--(altitudept n of t)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{medianpt}
+% \DescribeRoutine{median}
+% These two produce the line from \gbc{point n of t} to the midpoint of
+% the opposite side.
+% \begin{macrocode}
+vardef medianpt expr n of t =
+ 0.5[pnt[n + 1] (t), pnt[n + 2] (t)]
+enddef;
+
+vardef median expr n of t =
+ (pnt[n](t))--(medianpt n of t)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{anglebisectorpt}
+% \DescribeRoutine{anglebisector}
+% These two produce the line from \gbc{point n of t} to the opposite side
+% that bisects the angle there.
+% \begin{macrocode}
+vardef anglebisectorpt expr n of t =
+ save A, B, C; pair A, B, C;
+ A := pnt[n ] (t);
+ B := pnt[n + 1] (t);
+ C := pnt[n + 2] (t);
+ save zz; pair zz;
+ zz = whatever[B,C];
+ zz = A + whatever*((B-A) rotated (.5*cornerangle (A,B,C)));
+ zz
+enddef;
+
+vardef anglebisector expr n of t =
+ (pnt[n](t))--(anglebisectorpt n of t)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{cornerangle}
+% This calculates the angle at the corner of a triangle. Specifically,
+% the angle (between $-180$ and $180$) required to rotate the vector
+% \gbc{B-A} into \gbc{C-A}. For degenerate triangles the seemingly
+% arbitrary values 0, 60 and 90 are designed to match the assumptions used
+% in the \gbc{arc*} commands. But also to guarantee that the three
+% \gbc{cornerangle}\,s add up to $\pm180$. \gbc{cornerangle (A,B,C)} gives
+% the angle at \gbc{A}, positive if \gbc{A--B--C--cycle} is
+% anticlockwise.
+% \begin{macrocode}
+vardef cornerangle (expr A, B, C) =
+ if (A = B) and (B = C) : 60
+ elseif (B = C) : 0
+ elseif (A = B) or (A = C) : 90
+ else: angle ((C - A) rotated (-angle (B - A)))
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{mkpath}
+% This accepts the name of an array of pairs and produces a path
+% that connects them. The first two parameters are booleans. If
+% \gbc{smooth} is \mfc{true} a smooth path is produced, otherwise a
+% polyline. If \gbc{cyclic} is \mfc{true} the path is closed. The work is
+% actually done by \gbc{mksmooth} or \gbc{mkpoly}.
+% \begin{macrocode}
+vardef mkpath (expr smooth, tens, cyclic) (suffix pts) =
+ if smooth : mksmooth (tens, cyclic, pts)
+ else : mkpoly (cyclic, pts)
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{mkpoly}
+% This produces the path of line segments connecting \gbc{pts1},
+% \gbc{pts2}, etc., closing it up if the boolean \gbc{cyclic} is true.
+% \begin{macrocode}
+vardef mkpoly (expr cyclic) (suffix pts) =
+ for _i = 1 upto pts-1: pts[_i]-- endfor
+ pts[pts] if cyclic : -- cycle fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{polyline}
+% This is the \mfpic{} interface. Instead of an array name, it accepts a
+% list of pair expressions, forms an array from them and calls
+% \gbc{mkpoly}.
+% \begin{macrocode}
+vardef polyline (expr cyclic) (text t) =
+ save _pl; textpairs (_pl) (t); mkpoly (cyclic, _pl)
+enddef;
+
+% \end{macrocode}
+%
+% We added an optional parameter for the tension of smooth curves to
+% \mfpic. It used to be implemented this way: functions that implement a
+% tension parameter set \gbc{cur_tension} and called \gbc{mksmooth}, which
+% uses that tension in its formation of a path. Since \gbc{mksmooth} was
+% only ever used in this way, I decided to change its syntax to include a
+% tension parameter. Only the functions \gbc{tcurve} and \gbc{mkpath}
+% actually call \gbc{mksmooth} directly, other path building commands with
+% tension parameters call \gbc{mkfcn}, which calls \gbc{mkpath}.
+%
+% \DescribeRoutine{mksmooth}
+% This takes a tension value, a boolean, and the name of an array of
+% points, draws the curve connecting them and closes it up if the boolean
+% is true. It draws the curve forcing it to have the same direction at a
+% point as the line segment connecting the preceding and following points.
+% This is normally best if the curve direction changes relatively modestly
+% from point to point. For example, if the polyline would be convex, then
+% this smooth version would be pretty close to being convex. If the convex
+% polygon has several consecutive sides that are in the same direction,
+% all but the first and last of these segments in the smooth version would
+% be straight. We should experiment with ``\mfc{tension atleast}'' here
+% to see what difference it makes.
+% \begin{macrocode}
+vardef mksmooth (expr tens, cyclic) (suffix pts) =
+ pts1
+ if pts = 1 :
+ if cyclic : &cycle fi
+ else:
+ if cyclic :
+ {pts[2]-pts[pts]}
+ fi
+ for _i = 2 upto pts-1:
+ ..tension tens..pts[_i]{pts[_i+1]-pts[_i-1]}
+ endfor
+ ..tension tens..pts[pts]
+ if cyclic :
+ {pts[1]-pts[pts-1]}..tension tens..cycle
+ fi
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{curve}
+% \DescribeRoutine{tcurve}
+% The old \cs{curve} command in \mfpic{} permitted no tension parameter
+% and wrote a \grafbase{} \gbc{curve} command. For backward compatibility
+% we keep that name, but simply call the \gbc{tcurve} command with the
+% default value for tension. \gbc{tcurve} converts a list of pairs to an
+% array, then calls \gbc{mksmooth} on the array.
+% \begin{macrocode}
+def curve = tcurve (default_tension) enddef;
+vardef tcurve (expr tens, cyclic) (text t) =
+ save _tc; textpairs (_tc) (t); mksmooth (tens, cyclic, _tc)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{mkbezier}
+% \DescribeRoutine{bezier}
+% \DescribeRoutine{tbezier}
+% It seemed odd that we had no way for an \MF-savvy user to easily get
+% the standard \mfc{p..q..r} kind of path. For such a simple one
+% \cs{mfobj} with the explicit path expression would work, but when one
+% has to add a tension to it, it is nice to have an abbreviation. That's
+% what these are for.
+% \begin{macrocode}
+vardef mkbezier (expr tens, cyclic) (suffix pts) =
+ for _i = 1 upto pts-1 : pts[_i]..tension tens.. endfor
+ pts[pts] if cyclic : ..tension tens..cycle fi
+enddef;
+
+def bezier = tbezier (default_tension) enddef;
+vardef tbezier (expr tens, cyclic) (text t) =
+ save _tsb; textpairs (_tsb) (t); mkbezier (tens, cyclic) (_tsb)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{qbezier}
+% \DescribeRoutine{mkqbezier}
+% It also semed we ought to allow \mfpic{} users to easily reproduce the
+% effect of a sequence of \LaTeX's \cs{qbezier} commands. That's what
+% these are for.
+%
+% These commands and the various splines don't use tension as they have
+% their control points explicitly given, not computed from the tension
+% value by \MF. The \gbc{qbezier} command does not produce a smooth path
+% unless the controls are explicitely chosen for that. The spline commands
+% will almost always produce a smooth path.
+%
+% \gbc{mkqbezier} requires an even number of points for a cyclic path,
+% an oddnumber for a noncyclic path. If \gbc{pts} has the wrong parity,
+% the last point in the list is repeated. This makes the last segment
+% either trivial or a straight line. We increment \gbc{_mqb} instead of
+% \gbc{pts} so a user's suffix doesn't unexpectedly change.
+% \begin{macrocode}
+vardef mkqbezier (expr cyclic) (suffix pts) =
+ save _mqb; _mqb := pts;
+ if (cyclic and odd pts) or not (cyclic or odd pts):
+ pts[incr _mqb] := pts[pts];
+ fi
+ if cyclic : pts[incr _mqb] := pts1; fi
+ pts1
+ for _i = 2 step 2 until _mqb - 1 :
+ ..controls 1/3[pts[_i],pts[_i-1]] and 1/3[pts[_i], pts[_i+1]]
+ ..pts[_i+1]
+ endfor
+ if cyclic : &cycle fi
+enddef;
+
+vardef qbezier (expr cyclic) (text t) =
+ save _qbz; textpairs (_qbz) (t); mkqbezier (cyclic) (_qbz)
+enddef;
+
+% \end{macrocode}
+%
+% For quadratic B-splines, a list of pairs representing the control
+% points must be given. The nodes of the path and the cubic Bezi\'er
+% controls required to produce a quadratic B-spline are computed.
+%
+% \DescribeRoutine{mkqbs}
+% For simplicity, the list is converted to an array \gbc{_oq} first and
+% \gbc{mkqbs} is called. This draws an open spline based on the points in
+% an array \gbc{b}.
+% \begin{macrocode}
+vardef openqbs (text t) =
+ save _oq; textpairs (_oq) (t); mkqbs (_oq)
+enddef;
+
+vardef closedqbs (text t) =
+ save _cq; textpairs (_cq) (t);
+ _cq[incr _cq] := _cq1; _cq[incr _cq] := _cq2;
+ mkqbs (_cq) & cycle
+enddef;
+
+vardef mkqbs (suffix b) =
+ for _i = 1 upto b-2:
+ 0.5[b[_i], b[_i+1]]
+ ..controls 1/6[b[_i+1], b[_i]] and 1/6[b[_i+1], b[_i+2]]..
+ endfor
+ 0.5[b[b-1], b[b]]
+enddef;
+
+% \end{macrocode}
+% As for cubic B-splines, I'll have to trust the previous coder, as I
+% didn't even know what a cubic B-spline was until I deduced it from his
+% code. Earlier versions of \gbc{mkclosedcbs} would define
+% \gbc{b[incr b]:=b1} and \gbc{b[incr b]:=b2}. I decided we shouldn't
+% change the values of variables associated with the given suffix \gbc{b}
+% and so now we use \gbc{mkopencbs} to get most of the way around and then
+% fill in the gap with an explicit call to \gbc{opencbs}).
+% \begin{macrocode}
+vardef mkopencbs (suffix b) =
+ for _i = 1 upto b-3:
+ (b[_i]+4b[_i+1]+b[_i+2])/6
+ ..controls 1/3[b[_i+1], b[_i+2]] and 2/3[b[_i+1], b[_i+2]]..
+ endfor
+ (b[b-2]+4b[b-1]+b[b])/6
+enddef;
+vardef mkclosedcbs (suffix b) =
+ mkopencbs (b) & opencbs (b[b-2],b[b-1],b[b], b1, b2, b3) & cycle
+enddef;
+
+vardef opencbs (text t) =
+ save _oc; textpairs (_oc) (t); mkopencbs (_oc)
+enddef;
+vardef closedcbs (text t) =
+ save _clc; textpairs (_clc) (t); mkclosedcbs (_clc)
+enddef;
+
+% \end{macrocode}
+% When calling \gbc{curve} or \gbc{tcurve} there there can be a problem
+% with the resulting path: even with high tension one is not guaranteed
+% that a sequence of points with increasing x-coordinate will produce a
+% path with increasing x-coordinate. The requirement to guarantee this is
+% that the control points of the segment connecting $(x\sb1, y\sb1)$ to the
+% next $(x\sb2, y\sb2)$ have their \gbc{xpart} in the interval $x\sb1 < x
+% < x\sb2$.
+%
+% Therefore, if we wish to plot a curve connecting points with increasing
+% x-coordinates and believe that the resulting path should be the graph of
+% a function, we pretty much have to select the control points ourselves.
+% A related problem is to keep the path under control. That is, the
+% segment of the curve connecting $(x\sb1, y\sb1)$ to the next $(x\sb2,
+% y\sb2)$ should have \gbc{ypart} within an interval not too much larger
+% than the interval $y\sb1 < y < y\sb2$.
+%
+% We accomplish both these tasks at once by making the vector from
+% $(x\sb n, y\sb n)$ to its \mfc{postcontrol} have length less than
+% $|x\sb{n+1} - x\sb n|$, and the same for the vector from $(x\sb{n+1},
+% y\sb{n+1})$ to its \mfc{precontrol}
+%
+% Another concern is what direction to place the controls. In
+% \gbc{mksmooth} we ask the direction at a given point to be the average
+% of the straight line directions to adjacent points. For graphing
+% functions, we average the slopes instead. An added refinement is that
+% this is a weighted average, with the nearer x-coordinate being weighted
+% more.
+%
+% Finally, we permit a tension of sorts by dividing the distance to the
+% controls by a parameter \gbc{fcn_tension}.
+%
+% \DescribeRoutine{fcncontrol}
+% This computes the control point for the points on the path. The
+% parameters \gbc{X, Y, Z} are three successive points of the path to be
+% constructed. If they are given in order, it gives the postcontrol of
+% \gbc{Y}. If they are in reverse order, the precontrol is obtained.
+% Oddly enough, the addition of the trap for \gbc{dl=0} or \gbc{dr=0}
+% made it possible to trivially extend the array (in
+% \gbc{functioncurve}) and get better looking results than either method
+% used before this.
+%
+% \DescribeRoutine{mkfcnpath}
+% This produces the path, calling \gbc{fcncontrol} to produce the controls.
+%
+% \DescribeRoutine{functioncurve}
+% \DescribeRoutine{fcncurve}
+% This is the interface; \gbc{fcncurve} calls \gbc{functioncurve} with the
+% default tension, which then takes a list of points, converts it to an
+% array, and calls \gbc{mkfcnpath} to build the path.
+% \begin{macrocode}
+vardef fcncontrol (expr ftens, X, Y, Z) =
+ save dl, dr, before, after; pair before, after;
+ before := Y - X; after := Z - Y;
+ dl := xpart (before); dr := xpart (after);
+ if (dr = 0) or (dl = 0):
+ Y + abs(dr)/ftens * sgn before
+ else:
+ Y + abs(dr)/ftens * unitvector (before*dr/dl + after*dl/dr)
+ fi
+enddef;
+
+vardef mkfcnpath (expr ftens) (suffix q) =
+ for _i = 1 upto q - 1:
+ q[_i]..controls fcncontrol (ftens) (q[_i-1], q[_i], q[_i+1])
+ and fcncontrol (ftens) (q[_i+2], q[_i+1], q[_i])..
+ endfor
+ q[q]
+enddef;
+
+def fcncurve = functioncurve (emax(1.2default_tension, eps)) enddef;
+vardef functioncurve (expr ftens) (text t) =
+ save _fc; textpairs (_fc) (t);
+ if _fc > 1 : _fc0 := _fc1; _fc[_fc+1] := _fc[_fc]; fi
+ mkfcnpath (ftens)(_fc)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{turtle}
+% \emph{Turtle graphics} was a teaching tool to get youngsters used to the
+% concept of programming while also teaching geometry. The students fed an
+% Apple II computer a sequence of angles and distances, and a small
+% triangle on the screen (the ``turtle'') would turn the indicated amount
+% and travel the indicated distance, tracing a polyline on the screen.
+%
+% The argument of \gbc{turtle} is a list of pairs. The first is the
+% starting point, the rest are vector displacements (moves). The
+% distance and incremental angles of the original turtle graphics seems
+% to have been abandoned at some point in the development of \grafbase.
+% \begin{macrocode}
+vardef turtle (text t) =
+ save _tu; pair _tu[]; _tu := 0; _tu0 := (0, 0);
+ for _a = t: _tu[incr _tu] := _tu[_tu - 1] + _a; endfor
+ mkpoly (false, _tu)
+enddef;
+
+% \end{macrocode}
+%
+%
+% \section{Arcs, Circles and Ellipses}
+%
+% We have multiple commands that generate circular arcs, differing in
+% how the arc is specified. All are (in part) based on the following
+% \gbc{mkarc}. However, perfectly reasonable arcs can have centers so far
+% away that requiring the center among the parameters can cause numeric
+% overflow.
+%
+% I'd like to use some scheme that avoids this. It is possible, given
+% three reasonably spaced points on an arc with angle less than 90
+% degrees between each, to draw the arc without finding the center.
+% However, I am not sure how to reduce any given format to this
+% information
+%
+% Another problem is that of accuracy. If the angle is small, accuracy is
+% not usually a problem, but if an angle is close to 360, and the
+% endpoints are known, then finding the center (or finding other points on
+% the arc without knowing the center) is unstable.
+%
+% There is really no problem with \gbc{mkarc} itself: if you can express
+% both \gbc{center} and \gbc{from} in \MF, then the other values on the
+% arc should be no problem.
+%
+% \DescribeRoutine{mkarc}
+% This takes the center, starting and ending point (pair expressions) and
+% the angle, and returns the arc defined pretty much the way \file{plain.mf}
+% defines \mfc{quartercircle}.
+%
+% It would be easier to do something like we frequently do with
+% \mfc{fullcircle}: make an arc of unit radius, and then rotate, scale
+% and shift it into place. However, I would like to accomplish at least
+% the following: if an endpoint of the arc is among the parameters, or is
+% straightforwardly implied by them, then the corresponding endpoint of
+% the path created should test equal to that point. Shifting works OK, but
+% scaling and rotating cause roundoff differences.
+%
+% Note that \gbc{mkarc} has parameters that over-determine the arc. It
+% is only called by arc making commands that have calculated these
+% parameters. \gbc{mkarc}'s job is to ensure that the arc begins at
+% \gbc{from} and ends at \gbc{to} (exactly).
+% \begin{macrocode}
+vardef mkarc (expr center, from, to, sweep) =
+ save n, d; pair d;
+ n := ceiling (abs(sweep)/45);
+ d := (from - center) rotated (signof (sweep) 90);
+ from{d}
+ for j = 1 upto n-1 :
+ ..(from rotatedabout (center, j/n*sweep)){d rotated (j/n*sweep)}
+ endfor ..to{d rotated sweep}
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{arc}
+% The most basic: center of circle, starting point of arc, and angle
+% subtended. Another name for \gbc{arc} is \gbc{arccps}, (\gbc{cps} is
+% for ``center, point, sweep'').
+% \begin{macrocode}
+vardef arc (expr center, from, sweep) =
+ if (center = from) or (sweep = 0) :
+ from--from
+ else:
+ save to; pair to;
+ to := from rotatedabout (center, sweep);
+ mkarc (center, from, to, sweep)
+ fi
+enddef;
+def arccps = arc enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{arccenter}
+% For arcs greater than 90 degrees we will convert to the above basic
+% \gbc{mkarc}. Since two of the methods don't provide the center among the
+% parameters, we use the utility \gbc{arccenter} to locate that center,
+% given two points and the angle. This can fail (arithmetic overflow) if
+% the angle is too small relative to the distance between the points.
+% Therefore, we try not to call it for small angles (or angles near
+% multiples of 360).
+%
+% We find the center by solving equations representing two lines which
+% must be perpendicular to the circle. Which two lines we use depends on
+% the sweep. For accurate solutions we want the angle between the two
+% lines to be closer to 90 than to 0. \gbc{ang} is the angle we need to
+% rotate the chord \gbc{(to - from)} to be perpendicular to the circle at
+% \gbc{from}. \gbc{cd} is a vector in the direction of the chord. When
+% \gbc{ang} is close to $\pm90$, we use the lines perpendicular to the circle
+% at \gbc{from} and \gbc{to}. Otherwise we use the lines perpendicular
+% to the circle at \gbc{from} and perpendicular to the chord at its
+% midpoint. The latter is better when \gbc{ang} is between $-30$ and $30$
+% degrees.
+% \begin{macrocode}
+vardef arccenter (expr from, to, sweep) =
+ save ang, c;
+ pair c;
+ ang := 90 - (sweep mod 360)/2; % -90 < ang <= 90
+ if (abs(ang) = 90) or (from = to) :
+ GBmsg "The central point of this arc is undefined. " &
+ "Using midpoint of chord instead.";
+ 0.5[from, to]
+ else:
+ save cd; pair cd; cd := to - from;
+ c = from + whatever*(cd rotated ang);
+ if abs(ang) < 30 :
+ c = (0.5)[from, to] + whatever*(cd rotated 90);
+ else:
+ c = to + whatever*(-cd rotated -ang);
+ fi
+ c
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{midarc}
+% This finds the midpoint of the arc determined by two points and an
+% angle. It work because the angle subtended at \gbc{from} by an arc of
+% length \gbc{sweep/2} is \gbc{sweep/4}. We use it for small angles, as
+% we can then draw the arc without having to find its center.
+% \begin{macrocode}
+vardef midarc (expr from, to, sweep) =
+ save m, cd; pair m, cd;
+ cd := to - from;
+ m = from + whatever*( cd rotated (-sweep/4));
+ m = 0.5[from, to] + whatever*(cd rotated 90);
+ m
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{arcpps}
+% In this form we are given two points and the angle of the arc between
+% them. If the points are equal or the sweep makes the arc undefined, we
+% return a line segment. If the sweep is less than 90 degrees we use the
+% idea from the code of \mfc{quartercircle}, except, when the sweep is
+% greater than 45 degrees we let \MF{} find the midpint \gbc{m} of the
+% arc. Otherwise, we get the center of the circle and call \gbc{mkarc}.
+% \begin{macrocode}
+vardef arcpps (expr from, to, sweep) =
+ if ((sweep mod 360) = 0) or (from = to) :
+ GBmsg "Undefined arc. A line segment will be used instead.";
+ from--to
+ elseif abs(sweep) <= 90 :
+ save cd; pair cd; cd := to - from;
+ if abs(sweep) <= 45 :
+ from{cd rotated (-sweep/2)}..to{cd rotated (sweep/2)}
+ else:
+ from{cd rotated (-sweep/2)}..midarc(from, to, sweep){cd}
+ ..to{cd rotated (sweep/2)}
+ fi
+ else:
+ save center; pair center;
+ center := arccenter (from, to, sweep);
+ mkarc (center, from, to, sweep)
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{arcplr}
+% This one takes the center and polar coordinates of the ends relative to
+% the center. We just call \gbc{mkarc} with the obviously computed
+% endpoints and sweep.
+% \begin{macrocode}
+vardef arcplr (expr center, frtheta, totheta, rad) =
+ if rad = 0 :
+ center -- center
+ else:
+ save from, to; pair from, to;
+ from := center + rad*dir frtheta;
+ to := center + rad*dir totheta;
+ if frtheta = totheta :
+ from--to
+ else:
+ mkarc (center, from, to, totheta - frtheta)
+ fi
+ fi
+enddef;
+% \end{macrocode}
+%
+% \DescribeRoutine{arcalt}
+% This one is the same as above, but with the same argument order as
+% \gbc{sector}.
+% \begin{macrocode}
+vardef arcalt (expr center, radius, anglefrom, angleto) =
+ arcplr (center, anglefrom, angleto, radius)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{arcppp}
+% This last one finds the arc connecting three points in the order given.
+% It works by calling \gbc{arcpps} twice, using first the sweep from
+% \gbc{first} to \gbc{second}, and then the sweep from \gbc{second} to
+% \gbc{third}. Each of these is twice the opposite angle of the triangle
+% formed from these points, and calculated by \gbc{cornerangle}.
+% \begin{macrocode}
+vardef arcppp (expr first, second, third) =
+ arcpps (first, second, 2*cornerangle (third, first, second)) &
+ arcpps (second, third, 2*cornerangle (first, second, third))
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{ellipse}
+% \DescribeRoutine{circle}
+% We get an ellipse by x-scaling and y-scaling a unit circle, rotating it
+% and then shifting it into position. All parameters are coordinate
+% independent expressions, with obvious meaning (\gbc{center} is a pair, the
+% rest numeric). \gbc{circle} is similar, but we only scale and shift.
+% \begin{macrocode}
+vardef ellipse (expr center, radx, rady, angle) =
+ fullcircle xscaled (2*radx) yscaled (2*rady) rotated angle
+ shifted center
+enddef;
+vardef circle (expr center, rad) =
+ fullcircle scaled (2*rad) shifted center
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{circlecp}
+% \DescribeRoutine{circleppp}
+% \DescribeRoutine{circlepps}
+% The next three implement different ways of specifying a circle. The
+% first produces the circle with a given center passing through a given
+% point. The second produces the circle passing through three given
+% points. The third produces the circle passing through two given points
+% in such a way that the arc from the first to the second has a given
+% angle.
+% \begin{macrocode}
+vardef circlecp (expr center, point) =
+ mkarc (center, point, point, 360) & cycle
+enddef;
+vardef circleppp (expr one, two, three) =
+ save ang; numeric ang[];
+ ang0 := cornerangle(three, one, two);
+ ang1 := cornerangle(one, two, three);
+ ang2 := cornerangle(two, three, one);
+ arcpps (one, two, 2ang0) & arcpps (two, three, 2ang1) &
+ arcpps (three, one, 2ang2) & cycle
+enddef;
+vardef circlepps (expr one, two, sweep) =
+ save ang, full; numeric ang[], full;
+ full := signof (sweep) 360;
+ ang1 := sweep mod (full);
+ ang2 := full - ang1;
+ arcpps (one, two, ang1) & arcpps (two, one, ang2) & cycle
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{pathcenter}
+% This finds the center of a circle. For other paths, the point found
+% may be meaningless (but it will also obtain the center of an arc or a
+% rectangle). It takes three supposedly distinct points on the path and
+% finds the intersection of the perpendicular bisectors of two chords.
+% \begin{macrocode}
+vardef pathcenter expr p =
+ save a, cntr, n; pair cntr, a[];
+ n := length p;
+ a1 = pnt 0 (p);
+ a3 = pnt [n/2] (p);
+ if cycle p :
+ a2 = pnt [n/4] (p);
+ a4 = pnt [3n/4] (p);
+ else:
+ a2 := a3;
+ a4 := pnt[n] (p);
+ fi
+ cntr = .5[a1, a3] + whatever*((a3 - a1) rotated 90);
+ cntr = .5[a2, a4] + whatever*((a4 - a2) rotated 90);
+ cntr
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{circumcircle}
+% \DescribeRoutine{incircle}
+% \DescribeRoutine{excircle}
+% \DescribeRoutine{ninepointcircle}
+% These four create the relevant circles from a given triangle. The
+% triangle is specified as a path expression, so they produce results for
+% any path, but make sense only for a cyclic triangular path.
+% \begin{macrocode}
+vardef circumcircle expr t =
+ circleppp (pnt0 (t), pnt1 (t), pnt2 (t))
+enddef;
+
+vardef incircle expr t =
+ save A, B, C; pair A, B, C;
+ A := pnt0 (t);
+ B := pnt1 (t);
+ C := pnt2 (t);
+ % Find the tangent points on the sides. E.g., a is the common
+ % distance from A to the tangent points on the adjacent sides.
+ save a, b, c;
+ a + b = abs (B-A);
+ b + c = abs (C-B);
+ a + c = abs (A-C);
+ circleppp (A + a*unitvector (B-A),
+ B + b*unitvector (C-B),
+ C + c*unitvector (A-C))
+enddef;
+
+vardef excircle expr n of t =
+ save A, B, C; pair A, B, C;
+ A := pnt[n] (t);
+ B := pnt[n + 1] (t); % wraps around
+ C := pnt[n + 2] (t);
+ save a, b, c;
+ a - b = abs (B-A);
+ b + c = abs (C-B);
+ a - c = abs (C-A);
+ circleppp (A + a*unitvector(B-A),
+ B + b*unitvector(C-B),
+ C + c*unitvector(C-A))
+enddef;
+
+vardef ninepointcircle expr t =
+ circleppp (medianpt 0 of t, medianpt 1 of t, medianpt 2 of t)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{circumcenter}
+% \DescribeRoutine{incenter}
+% \DescribeRoutine{excenter}
+% \DescribeRoutine{ninepointcenter}
+% \DescribeRoutine{barycenter}
+% These find various centers associated with a triangle. The last one is
+% made to work for any path.
+% \begin{macrocode}
+vardef circumcenter expr t = pathcenter circumcircle t enddef;
+vardef incenter expr t = pathcenter incircle t enddef;
+vardef excenter expr n of t = pathcenter excircle n of t enddef;
+vardef ninepointcenter expr t = pathcenter ninepointcircle t enddef;
+
+% Make this work for any path.
+% Divide as we go,
+% decrease chance of overflow.
+vardef barycenter expr t =
+ save n, m; n := length t; m := n + 1;
+ save xxx;
+ xxx : = pnt0 (t)/m for k = 1 upto n-1 : + pnt[k] (t)/m endfor;
+ if cycle t: xxx*(1 + 1/n)
+ else: xxx + pnt[n] (t)/m
+ fi
+enddef;
+% \end{macrocode}
+%
+% \DescribeRoutine{sector}
+% \gbc{sector} produces the closed path consisting of a straight line
+% of length \gbc{rad} from \gbc{center} in the direction \gbc{frtheta},
+% thence along an arc of the circle centered at \gbc{center} to angle
+% \gbc{totheta}, and then along the straight line back to \gbc{center}.
+% \begin{macrocode}
+vardef sector (expr center, rad, frtheta, totheta) =
+ center -- arcalt (center, rad, frtheta, totheta) -- cycle
+enddef;
+
+% \end{macrocode}
+%
+%
+% \section{Plotting of functions}
+%
+% In these macros, if the boolean argument \gbc{smooth} is true then the
+% path returned will be a B\'ezier, otherwise it will be a polyline. The
+% parameter is simply passed to \gbc{mkpath}. If a \gbc{tens} parameter
+% exists, then the smooth version will have that value of tension,
+% otherwise the value of \gbc{default_tension} is used.
+%
+% All of these macros call \gbc{mkfcn}.
+%
+% \DescribeRoutine{mkfcn}
+% In this command the text parameter \gbc{pf} should be the name of a
+% function of some sort that can take a numeric value in parentheses and
+% return a pair expression. The parameters \gbc{bmin}, \gbc{bmax} and
+% \gbc{bst} determine a sequence of numeric values starting at \gbc{bmin},
+% stepping by \gbc{bst} and ending with \gbc{bmax}. These are fed to
+% \gbc{pf} and the resulting pairs stored in an array. Then
+% \gbc{mksmooth} is called with the tension \gbc{tens} and the name of the
+% array.
+%
+% For stability, we don't actually step by \gbc{bst}, but round
+% \gbc{(bmax-bmin)/bst} and step that many equal steps. We first adjust
+% the step size upward so the number of steps doesn't exceed
+% \gbc{infinity}. The path is forced to begin at \gbc{pf(bmin)} and
+% end at \gbc{pf(bmax)}
+% \begin{macrocode}
+vardef mkfcn (expr smooth, tens) (expr bmin, bmax, bst) (text pf) =
+ save _p; pair _p[]; _p := 0;
+ save _dx, _n, _r; numeric _dx, _n, _r;
+ if bmax = bmin : _n := 1;
+ else:
+ _r := bmax - bmin;
+ _dx := max (abs(bst), nottoosmall*abs(_r), epsilon);
+ _n := emax (round(abs(_r)/_dx), 1);
+ fi
+ for _i = 0 upto _n: _p[incr _p] := pf(bmin + _i/_n*_r); endfor
+ mkpath (smooth, tens, false, _p)
+enddef;
+% compatibility:
+def tfcn (expr smooth) = mkfcn (smooth, default_tension) enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{parafcn}
+% This is like \gbc{mkfcn}, but the text argument is not a pair
+% valued function, but rather a text parameter containing code that, when
+% copied literally into a vardef, defines a function in which \gbc{t} is
+% the argument, and which returns a pair.
+%
+% Older files are supported with a definition of \gbc{parafcn} that calls
+% \gbc{tparafcn} with \gbc{default_tension}. I should have made this easier
+% by reversing the smoothness and tension arguments, but for backward
+% compatibility I have to leave it thus. Other commands implement \mfpic's
+% tension options: \gbc{function} and \gbc{plrfcn}. They also have forms
+% that accept a tension argument (\gbc{tfunction} and \gbc{tplrfcn}) and
+% call them with the default tension.
+% \begin{macrocode}
+def parafcn (expr smooth) = tparafcn (smooth, default_tension) enddef;
+vardef tparafcn (expr sm, tn) (expr bmin, bmax, bst) (text pf) =
+ save _fp; vardef _fp (expr t) = pf enddef;
+ mkfcn (sm, tn) (bmin, bmax, bst) (_fp)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{xfcn}
+% This first converts its final argument, which should be a numeric
+% valued function \gbc{f}, to a pair valued function \gbc{(x, f(x))}, then
+% calls \gbc{mkfcn} to return the path that should be the graph of $f(x)$.
+% \begin{macrocode}
+vardef xfcn (expr smooth) (expr xmin, xmax, st) (text _fx) =
+ save _fp; vardef _fp (expr _x) = (_x, _fx(_x)) enddef;
+ mkfcn (smooth, default_tension) (xmin, xmax, st) (_fp)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{function}
+% This is to \gbc{xfcn} as \gbc{parafcn} is to \gbc{mkfcn}: it
+% takes a text argument and copies it into a vardef so as to define a pair
+% valued function with a literal \gbc{x} as the argument.
+%
+% \DescribeRoutine{btwnfcn}
+% This is mainly for the sake of simpler \mfpic{} output, implementing
+% the \cs{btwnfcn} macro.
+% \begin{macrocode}
+def function (expr smooth) = tfunction (smooth, default_tension) enddef;
+vardef tfunction (expr smooth, tens) (expr xmin, xmax, st) (text _fx) =
+ save _fp; vardef _fp (expr x) = (x, _fx) enddef;
+ mkfcn (smooth, tens) (xmin, xmax, st) (_fp)
+enddef;
+
+def btwnfcn (expr sm) = tbtwnfcn (sm, default_tension) enddef;
+vardef tbtwnfcn (expr sm, tn)(expr xlo, xhi, st)(text _fx)(text _gx) =
+ tfunction (sm, tn) (xlo, xhi, st) (_fx) --
+ ( reverse tfunction (sm, tn) (xlo, xhi, st) (_gx) ) -- cycle
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{rfcn}
+% This takes the name of a function \gbc{f} which is a numeric
+% valued function of a numeric parameter. It interprets it as a polar
+% curve $(\theta, f(\theta))$, converts that to a curve in rectangular
+% coordinates and calls \gbc{mkfcn} on it.
+% \begin{macrocode}
+vardef rfcn (expr smooth) (expr tmin, tmax, st) (text ft) =
+ save _fq; vardef _fq (expr t) = (ft(t)) * (dir t) enddef;
+ mkfcn (smooth, default_tension) (tmin, tmax, st) (_fq)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{plrfcn}
+% This is to \gbc{rfcn} as \gbc{parafcn} is to \gbc{mkfcn}: the
+% text argument should be code that can be copied literally into a
+% \mfc{vardef} creating a numeric function with a literal \gbc{t} as the
+% parameter (representing $\theta$).
+% \begin{macrocode}
+def plrfcn (expr smooth) = tplrfcn (smooth, default_tension) enddef;
+vardef tplrfcn (expr smooth, tens) (expr tmin, tmax, st) (text ft) =
+ save _fq; vardef _fq (expr t) = (ft) * (dir t) enddef;
+ mkfcn (smooth, tens) (tmin, tmax, st) (_fq)
+enddef;
+
+% \end{macrocode}
+%
+%
+% \section{Pie charts and bar charts}
+%
+% \DescribeRoutine{piechart}
+% The \gbc{piechart} command calculates the wedges of a pie from the text
+% parameter \gbc{data}. It should be a list of positive numerics, and the
+% result will be one wedge for each datum, the area of the wedge being
+% proportional to the corresponding datum. The wedge for each datum has
+% its point at \gbc{cent} and the wedge for the first datum begins at
+% angle \gbc{ang}. Each wedge is clockwise from the preceding one if
+% \gbc{sign = -1}, otherwise anticlockwise. The radius of the pie is
+% \gbc{rad}.
+%
+% After the calculations, the wedges (closed sectors) are stored in the
+% array \gbc{piewedge[]} with the numeric \gbc{piewedge} holding the number
+% of wedges. The center is saved in \gbc{piecenter}, the directions of the
+% wedges (the bisecting rays) are stored in \gbc{piedirection[]}, the
+% starting angles of the wedges in \gbc{pieangle[]}
+% \begin{macrocode}
+vardef piechart (expr sign, ang, cent, rad) (text data) =
+ save _sum, _tot;
+ numeric piewedge; piewedge := 0;
+ numeric pieangle, pieangle[]; pieangle0 := 0;
+ for _val = data :
+ pieangle[incr piewedge] := pieangle[piewedge - 1] + _val;
+ endfor
+ _tot := pieangle[piewedge];
+ pair piecenter; piecenter := cent;
+ path piewedge[];
+ numeric piedirection; pair piedirection[];
+ pieangle[piewedge + 1] = ang + sign*360;
+ for _n = piewedge downto 1 :
+ pieangle[_n] := ang + sign*pieangle[_n - 1]/_tot*360;
+ piewedge[_n] =
+ sector(cent, rad, pieangle[_n], pieangle[_n+1]);
+ piedirection[_n] := dir(0.5[ pieangle[_n], pieangle[_n+1] ]);
+ endfor
+ piedirection := pieangle := piewedge;
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{barchart}
+% I was told that there are better ways (than piecharts) to represent
+% quantitative data. Perhaps bar charts are better. \gbc{barchart}
+% calculates the bars from the text parameter, \gbc{data}. These bars are
+% vertical \gbc{vert} is true, otherwise horizontal.
+%
+% \gbc{start} is the location (on the appropriate axis) of the start of
+% the first bar. \gbc{sep} is the separation between bar centers. \gbc{r}
+% is the ratio of the width of the bars to their separation.
+%
+% After the calculations, the array of paths \gbc{chartbar[]} holds the
+% rectangles, \gbc{barend[]} holds their rightmost or topmost
+% coordinates (which is just the items in \gbc{data} or their y-parts),
+% \gbc{barbegin[]} holds their leftmost or bottommost coordinates (either
+% 0 or the x-parts of the data), \gbc{barstart[]} holds the appropriate
+% coordinate of the leading edge of the bar, and \gbc{barwd = r*sep}.
+%
+% If the data are pair data, this command uses the x-part as the beginning
+% of the bar and the y-part as the end. Thus Gantt diagrams can be
+% created. We keep \gbc{barlength} for backward compatibility (formerly
+% all data had to be numeric and bars went from 0 to \gbc{barlength[]}).
+% \gbc{barlength[]} was made available to help place some label or symbol
+% at the end of a bar and existing code might break if we omitted it.
+% \begin{macrocode}
+def barchart (expr start, sep, r, vert)(text data) =
+ numeric barbegin, barbegin[],
+ barend, barend[],
+ barlength, barlength[],
+ barstart, barstart[],
+ chartbar, barwd;
+ path chartbar[];
+ chartbar := 0; barwd := r*sep;
+ for _itm = data :
+ barend[incr chartbar] := if pair _itm: ypart _itm else: _itm fi;
+ barbegin[chartbar] := if pair _itm: xpart _itm else: 0 fi;
+ endfor
+ barbegin := barend := barlength := barstart := chartbar;
+ for _n = 1 upto chartbar :
+ barstart[_n] := start + sep*(_n-1);
+ barlength[_n] := barend[_n];
+ chartbar[_n] := rect ((barbegin[_n], 0), ( barend[_n], barwd) )
+ shifted (0, barstart[_n]) if vert: xyswap fi;
+ endfor
+enddef;
+
+% \end{macrocode}
+%
+%^^A Overlays - taken from MFbook, p 295. (Bruce Leban)
+%
+% \section{Overlays}
+%
+% This final code predates me. I've never seen it used and don't know what
+% its for. For the \MP{} version I just tried to make sure everything was
+% defined in \MP{} or \file{plain.mp} and otherwise left it alone.
+% \begin{macrocode}
+picture totalpicture;
+boolean totalnull, currentnull;
+def clearit =
+ currentpicture := totalpicture := nullpicture;
+ currentnull := totalnull := true;
+enddef;
+
+def keepit =
+%<MF> mono (currentpicture);
+ addto totalpicture also currentpicture;
+ currentpicture := nullpicture;
+ totalnull := currentnull;
+ currentnull := true;
+enddef;
+
+def addto_currentpicture =
+ currentnull := false;
+ addto currentpicture
+enddef;
+
+def mergeit (text do) =
+ if totalnull :
+ do currentpicture
+ elseif currentnull :
+ do totalpicture
+ else:
+ begingroup
+ save _v_; picture _v_;
+ _v_ := currentpicture;
+%<MF> mono (_v_);
+ addto _v_ also totalpicture;
+ do _v_
+ endgroup
+ fi
+enddef;
+
+% \end{macrocode}
+% This apparently redundant definition (\gbc{shipit} = \gbc{shipit_}) is
+% so that \mfpic{} can turn shipping off and back on by redefining
+% \gbc{shipit} to either \gbc{shipit_} or \mfc{relax}.
+% \begin{macrocode}
+def shipit_ =
+ mergeit (shipout)
+enddef;
+def shipit = shipit_ enddef;
+
+%<*MF>
+def showit_ =
+ mergeit (show_)
+enddef;
+def show_ suffix v =
+ display v inwindow currentwindow
+enddef;
+
+%</MF>
+% \end{macrocode}
+%
+% Here we initialize \gbc{gcode} (which current versions of mfpic do not use)
+% for hacked \mfpic{} files that require it. And that's all.
+% \begin{macrocode}
+numeric gcode; gcode := 0;
+
+%<MF>% end grafbase.mf
+%<MP>% end grafbase.mp
+%</MF|MP>
+% \end{macrocode}
+%
+% The following code was borrowed from the the standard \LaTeX{} graphics
+% package (\file{dvipsname.def} by David Carlisle and Sebastian Rahtz). In
+% fact it was mostly generated automatically by some editor macros that
+% replaced \prog{graphics} package code with the \grafbase{} code.
+%
+%^^A This file may be distributed under the terms of the LaTeX Project Public
+%^^A License, as described in \file{lppl.txt} in the base LaTeX
+%^^A distribution, either version 1.0 or, at your option, any later version.
+%
+% Declare all the dvips color names to be color variables:
+% \begin{macrocode}
+%<*dvips>
+color Apricot, Aquamarine, Bittersweet, Black, Blue, BlueGreen,
+BlueViolet, BrickRed, Brown, BurntOrange, CadetBlue, CarnationPink,
+Cerulean, CornflowerBlue, Cyan, Dandelion, DarkOrchid, Emerald,
+ForestGreen, Fuchsia, Goldenrod, Gray, Green, GreenYellow, JungleGreen,
+Lavender, LimeGreen, Magenta, Mahogany, Maroon, Melon, MidnightBlue,
+Mulberry, NavyBlue, OliveGreen, Orange, OrangeRed, Orchid, Peach,
+Periwinkle, PineGreen, Plum, ProcessBlue, Purple, RawSienna, Red,
+RedOrange, RedViolet, Rhodamine, RoyalBlue, RoyalPurple, RubineRed,
+Salmon, SeaGreen, Sepia, SkyBlue, SpringGreen, Tan, TealBlue, Thistle,
+Turquoise, Violet, VioletRed, White, WildStrawberry, Yellow,
+YellowGreen, YellowOrange;
+
+% \end{macrocode}
+% The function \gbc{cmyk} (which converts a CMYK quadruple to \MP's rgb
+% triple) is defined in \file{grafbase.mp}, which should be input before
+% \file{dvipsnam.mp}:
+% \begin{macrocode}
+Apricot = cmyk( 0, 0.32, 0.52, 0);
+Aquamarine = cmyk(0.82, 0, 0.30, 0);
+Bittersweet = cmyk( 0, 0.75, 1, 0.24);
+Black = cmyk( 0, 0, 0, 1);
+Blue = cmyk( 1, 1, 0, 0);
+BlueGreen = cmyk(0.85, 0, 0.33, 0);
+BlueViolet = cmyk(0.86, 0.91, 0, 0.04);
+BrickRed = cmyk( 0, 0.89, 0.94, 0.28);
+Brown = cmyk( 0, 0.81, 1, 0.60);
+BurntOrange = cmyk( 0, 0.51, 1, 0);
+CadetBlue = cmyk(0.62, 0.57, 0.23, 0);
+CarnationPink = cmyk( 0, 0.63, 0, 0);
+Cerulean = cmyk(0.94, 0.11, 0, 0);
+CornflowerBlue = cmyk(0.65, 0.13, 0, 0);
+Cyan = cmyk( 1, 0, 0, 0);
+Dandelion = cmyk( 0, 0.29, 0.84, 0);
+DarkOrchid = cmyk(0.40, 0.80, 0.20, 0);
+Emerald = cmyk( 1, 0, 0.50, 0);
+ForestGreen = cmyk(0.91, 0, 0.88, 0.12);
+Fuchsia = cmyk(0.47, 0.91, 0, 0.08);
+Goldenrod = cmyk( 0, 0.10, 0.84, 0);
+Gray = cmyk( 0, 0, 0, 0.50);
+Green = cmyk( 1, 0, 1, 0);
+GreenYellow = cmyk(0.15, 0, 0.69, 0);
+JungleGreen = cmyk(0.99, 0, 0.52, 0);
+Lavender = cmyk( 0, 0.48, 0, 0);
+LimeGreen = cmyk(0.50, 0, 1, 0);
+Magenta = cmyk( 0, 1, 0, 0);
+Mahogany = cmyk( 0, 0.85, 0.87, 0.35);
+Maroon = cmyk( 0, 0.87, 0.68, 0.32);
+Melon = cmyk( 0, 0.46, 0.50, 0);
+MidnightBlue = cmyk(0.98, 0.13, 0, 0.43);
+Mulberry = cmyk(0.34, 0.90, 0, 0.02);
+NavyBlue = cmyk(0.94, 0.54, 0, 0);
+OliveGreen = cmyk(0.64, 0, 0.95, 0.40);
+Orange = cmyk( 0, 0.61, 0.87, 0);
+OrangeRed = cmyk( 0, 1, 0.50, 0);
+Orchid = cmyk(0.32, 0.64, 0, 0);
+Peach = cmyk( 0, 0.50, 0.70, 0);
+Periwinkle = cmyk(0.57, 0.55, 0, 0);
+PineGreen = cmyk(0.92, 0, 0.59, 0.25);
+Plum = cmyk(0.50, 1, 0, 0);
+ProcessBlue = cmyk(0.96, 0, 0, 0);
+Purple = cmyk(0.45, 0.86, 0, 0);
+RawSienna = cmyk( 0, 0.72, 1, 0.45);
+Red = cmyk( 0, 1, 1, 0);
+RedOrange = cmyk( 0, 0.77, 0.87, 0);
+RedViolet = cmyk(0.07, 0.90, 0, 0.34);
+Rhodamine = cmyk( 0, 0.82, 0, 0);
+RoyalBlue = cmyk( 1, 0.50, 0, 0);
+RoyalPurple = cmyk(0.75, 0.90, 0, 0);
+RubineRed = cmyk( 0, 1, 0.13, 0);
+Salmon = cmyk( 0, 0.53, 0.38, 0);
+SeaGreen = cmyk(0.69, 0, 0.50, 0);
+Sepia = cmyk( 0, 0.83, 1, 0.70);
+SkyBlue = cmyk(0.62, 0, 0.12, 0);
+SpringGreen = cmyk(0.26, 0, 0.76, 0);
+Tan = cmyk(0.14, 0.42, 0.56, 0);
+TealBlue = cmyk(0.86, 0, 0.34, 0.02);
+Thistle = cmyk(0.12, 0.59, 0, 0);
+Turquoise = cmyk(0.85, 0, 0.20, 0);
+Violet = cmyk(0.79, 0.88, 0, 0);
+VioletRed = cmyk( 0, 0.81, 0, 0);
+White = cmyk( 0, 0, 0, 0);
+WildStrawberry = cmyk( 0, 0.96, 0.39, 0);
+Yellow = cmyk( 0, 0, 1, 0);
+YellowGreen = cmyk(0.44, 0, 0.74, 0);
+YellowOrange = cmyk( 0, 0.42, 1, 0);
+
+% End of file `dvipsnam.mp'.
+%</dvips>
+% \end{macrocode}
+% \clearpage
+%\Finale