summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/generic
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2006-07-01 23:18:44 +0000
committerKarl Berry <karl@freefriends.org>2006-07-01 23:18:44 +0000
commitd6c7c85a6b1261a182a60431f6af860b855ec216 (patch)
tree794c53fc0f09ba53c39a272379fbf0004390c6eb /Master/texmf-dist/source/generic
parentd6dd1c3b1739f957dd0acc6f89e480825511c927 (diff)
mfpic 0.9 update
git-svn-id: svn://tug.org/texlive/trunk@1747 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/generic')
-rw-r--r--Master/texmf-dist/source/generic/mfpic/grafbase.dtx7963
-rw-r--r--Master/texmf-dist/source/generic/mfpic/mfpic.dtx7170
-rw-r--r--Master/texmf-dist/source/generic/mfpic/mfpic.ins69
3 files changed, 9251 insertions, 5951 deletions
diff --git a/Master/texmf-dist/source/generic/mfpic/grafbase.dtx b/Master/texmf-dist/source/generic/mfpic/grafbase.dtx
index 0eadd2885a9..fe734cbcc84 100644
--- a/Master/texmf-dist/source/generic/mfpic/grafbase.dtx
+++ b/Master/texmf-dist/source/generic/mfpic/grafbase.dtx
@@ -1,32 +1,24 @@
% \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.
+% Copyright 2002--2006, Daniel H. Luecking
%
% 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
+% LaTeX Project Public License, either version 1.3b 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.3b or later is part of all distributions of LaTeX version
+% 2003/12/01 or later.
%
-% 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.
+% Mfpic has maintenance status "author-maintained". The Current Maintainer
+% is Daniel H. Luecking. There are several Base Interpreters: plain TeX, LaTeX,
+% plain Metafont and plain MetaPost.
%
%<*driver>
+\ProvidesFile{grafbase.dtx}
+ [2006/05/26 v0.9. Metafont/post macros to interface with mfpic.]%
\documentclass[draft]{ltxdoc}
\usepackage{docmfp}
-\def\fileversion{0.7a beta}
-\def\filedate{2004/04/16}
\addtolength{\textwidth}{.5878pt}
@@ -41,17 +33,18 @@
\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\MF{\prog{meta\-font}}
+\def\MP{\prog{meta\-post}}
+\def\PS{\prog{Post\-Script}}
+\def\CMF{\prog{Meta\-font}}
+\def\CMP{\prog{Meta\-post}}
\def\opt#1{{\sffamily\upshape#1}}
-\def\env#1{{\mytt#1}}
-\let\file\env
-\let\mfc\env
-\let\gbc\env
-\renewcommand\{{\char`\{}
-\renewcommand\}{\char`\}}
+\def\mfc#1{{\mytt#1}}
+\let\env\mfc
+\let\file\mfc
+\let\gbc\mfc
+\renewcommand\{{{\mytt\char`\{}}
+\renewcommand\}{{\mytt\char`\}}}
\renewcommand\|{${}\mathrel{|}{}$}
\makeatletter
@@ -67,6 +60,8 @@
\renewcommand\labelitemiii{\textperiodcentered}
\leftmargini\parindent
% Stupid index!
+\def\usage#1{\textrm{#1}}
+\def\index@prologue{\section*{Index}\markboth{Index}{Index}}
\def\IndexParms{%
\parindent \z@ \columnsep 15pt
\parskip 0pt plus 1pt
@@ -86,7 +81,10 @@
\string\verb\quotechar*\verbatimchar\string#1\verbatimchar
#2 \encapchar usage}%
\@esphack}
+\def\close@crossref{\SpecialEscapechar{:}}
\makeatother
+\def\VariableIndex#1{\SpecialMfpIndex{#1}{\variablestring}{}}
+\def\RoutineIndex #1{\SpecialMfpIndex{#1}{}{}}
\def\pdfTeX{\textrm{pdf\kern.04em\TeX}}
\def\pdfLaTeX{\textrm{pdf\kern.06em\LaTeX}}
@@ -99,6 +97,8 @@
Geoffrey Tobin and Dan Luecking. Dan wrote this documentation.}}
\author{Dan Luecking}
\date{\filedate}
+\SpecialEscapechar{:}
+\def\bslash{:}
\DisableCrossrefs
\CodelineIndex
\AlsoImplementation
@@ -110,7 +110,7 @@
%</driver>
%\fi
%
-% \CheckSum{1}
+% \CheckSum{1369}
% \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
@@ -128,6 +128,7 @@
% Right brace \} Tilde \~}
%
% \catcode`\_=12
+% \GetFileInfo{grafbase.dtx}
% \maketitle
%
% \begin{abstract}
@@ -136,88 +137,112 @@
% \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.
+% \MP{}. As they are more alike than different (96\% of the code is
+% identical), we document both here.
%
% This file documents the \grafbase{} source code. The user manual for
-% \mfpic{} is \file{mfpicdoc.tex}.
+% \mfpic{} is distributed as \file{mfpman.pdf} produced from
+% \file{mfpman.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.
+% \DescribeVariable{grafbaseversion} We use \mfc{grafbaseversion} to
+% check if \prog{grafbase} has been previously loaded, later we use it
+% to check a mismatch with the version of \mfpic{} (if used).
+% \gbc{grafbase} was used in previous versions. If either is known, we
+% bail out. The \gbc{grafbase} boolean is really never needed, but it
+% has been around since I took over. It is possible to write files that
+% change behavior when \grafbase{} is loaded, however if they say
+% ``\gbc{if grafbase:}, they can only be called with \gbc{grafbase} a
+% known boolean.
%
-% 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.
+% \VariableIndex{fileversion} \gbc{fileversion} and
+% \VariableIndex{filedate} \gbc{filedate} provide identifying information.
%
% \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.
+% message delivered as being `\gbc{Grafbase}'. \DescribeRoutine{GBwarn}
+% Warnings are delivered by \gbc{GBwarn}. The takes care of both the
+% \DescribeRoutine{GBerrmsg}\gbc{GBerrmsg}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.";
+if (known grafbaseversion) or (known grafbase):
+ message "Grafbase (" & jobname & "): 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;
+
+string fileversion, filedate;
+fileversion := "0.9"; filedate := "2006/05/26";
+
+message " Loading grafbase macros, version " & fileversion & " " &
+ filedate & ".";
+
+def GBmsg expr s = message "Grafbase (" & jobname & "): " & s; enddef;
+def GBwarn expr s = GBmsg "Warning, " & s; enddef;
+def GBerrmsg (expr s) expr t = errhelp t;
+ errmessage "Grafbase (" & jobname & "): " & s; errhelp "";
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeVariable{MFPIC}
+% The \gbc{MFPIC} variable is not yet used at all.
+% 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.
+%
+% \DescribeVariable{mfpicversion}
+% The output file written by the \mfpic{} macros includes a test that
+% \gbc{mfpicversion} and \mfc{grafbaseversion} are the same,
+% but that would fail to catch a new \grafbase{} with an old \mfpic. So
+% we also put a test here with \DescribeRoutine{checkversions}
+% \gbc{checkversions}, though 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.
+% \begin{macrocode}
+boolean MFPIC; MFPIC := false;
def checkversions (expr g)=
- numeric grafbaseversion; grafbaseversion := g;
- if unknown mfpicversion : % no mfpic, or < 0.63
+ numeric grafbaseversion; grafbaseversion := g;
+ if unknown mfpicversion: % no mfpic, or < 0.63
GBmsg "Recent mfpic not detected.";
- elseif g = mfpicversion :
+ elseif g = mfpicversion:
MFPIC := true;
else:
- GBerrmsg ("version mismatch")
- "The installation may be broken: mfpic and grafbase " &
- "versions do not match.";
+ message "";
+ GBwarn "Version mismatch: "
+ & "mfpic and grafbase versions do not match.";
+ message "";
fi
enddef;
-checkversions (70);
+checkversions (90);
% \end{macrocode}
% We try to make sure that the macros of \file{plain.mf} or
-% \file{plain.mp} are available.
+% \file{plain.mp} (where \mfc{base_name} is defined to be \mfc{"plain"})
+% are available.
% \begin{macrocode}
-if unknown base_name : input plain;
-elseif not string base_name : input plain;
-elseif base_name <> "plain" : input plain;
+if unknown base_name : input plain;
+elseif not string base_name: input plain;
+elseif base_name <> "plain": input plain;
fi
% \end{macrocode}
%
+% \DescribeVariable{METAPOST}
% 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
@@ -225,59 +250,55 @@ fi
%
% 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
+% `\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.
+% `\gbc{known color X}' is always true.
%
-% In \MF{} \mfc{color X} is an identifier (presumably unknown) with the
+% 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;
+if known color Geamparalele din Babadag:
+ 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.";
+if METAPOST:
+ GBerrmsg ("wrong compiler.")
+ "This file is for Metafont. For Metapost use grafbase.mp.";
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.";
+if not METAPOST:
+ GBerrmsg ("wrong compiler.")
+ "This file is for Metapost. For Metafont use grafbase.mf.";
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.
+% inputing \file{grafbase}.
+% \DescribeRoutine{GBenddebug}
+% 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
+if not boolean debug: boolean debug; debug := false; fi
def GBdebug =
begingroup
- save >>; def >> = message enddef;
- >> "Grafbase DEBUG: ";
+ save >>; def >> = message enddef;
+ >> "Grafbase DEBUG";
enddef;
def GBenddebug =
>> "End DEBUG";
@@ -285,21 +306,23 @@ def GBenddebug =
enddef;
vardef mftitle expr t =
- t; message t;
+ if string t: t; message t; fi
enddef;
% \end{macrocode}
%
-% \subsection{Setting up the font}\label{font}
+% \subsection{Setting up the font, \MF{} only}\label{font}
%
-% Font-related housekeeping is for \MF{} only. \MF{} only produces
+% Font-related housekeeping is only for \MF{}. \MF{} only produces
% fonts, so we have to define the variables it thinks are needed for
% fonts.
%
+% \DescribeVariable{GBgeneric}
% 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}.
+% \mfc{proof} mode. We used to set \mfc{mode := cx} (and later
+% \mfc{ljfour}) if it was unknown. For a while we just issued an error
+% message. In this version we define a 600dpi mode called \mfc{GBgeneric}
+% as a fallback (neither \gbc{mode} nor \gbc{localfont} known).
%
% 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
@@ -307,15 +330,29 @@ enddef;
% 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;
+if unknown mode:
+ GBerrmsg ("Metafont mode is unknown.")
+ "Set mode to a known mode. Perhaps localfont or ljfour. "
+ & "If you proceed, a generic 600dpi mode will be used.";
+ if known localfont: mode := localfont;
+ else:
+ mode_def GBgeneric =
+ proofing := 0;
+ fontmaking := 1;
+ tracingtitles := 0;
+ if unknown pixels_per_inch:
+ pixels_per_inch := 600;
+ fi
+ blacker := 0;
+ fillin := 0;
+ o_correction := 1;
+ enddef;
+ mode := GBgeneric;
+ fi
fi
mode_setup;
-if debug :
+if debug:
GBdebug;
>> "pixels_per_inch = " & decimal pixels_per_inch;
GBenddebug;
@@ -335,51 +372,74 @@ interim designsize := 128pt#;
% 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 :
+%<MP>pt# := pt; bp# := bp;
+%<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).
+% Don't complain when variables get too large. For \MF{} this \emph{must}
+% be after \mfc{mode_setup}. Also don't complain if a clockwise path is
+% filled (only \MF{} does this).
% \begin{macrocode}
-%<MF>interim turningcheck := 0;
interim warningcheck := 0;
+%<MF>interim turningcheck := 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.
+% \VariableIndex{unitlen}
+% \VariableIndex{xscale}
+% \VariableIndex{yscale}
+% \VariableIndex{xneg}
+% \VariableIndex{xpos}
+% \VariableIndex{yneg}
+% \VariableIndex{ypos}
+% The following are the various variables determining the extent of a
+% picture. These variables would normally be set by a user for each
+% picture, or by \mfpic, but we give them default values anyway. They
+% give a nominal picture size of one inch with a graph unit corresponding
+% to $1/10$ inch.
% \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;
+unitlen := 1 bp#;
+xscale := 7.2;
+yscale := 7.2;
+xneg := 0; xpos := 10;
+yneg := 0; ypos := 10;
% \end{macrocode}
%
+% \DescribeVariable{deg}\VariableIndex{degree}
% 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}
-%
+% the unit of angle.
+% \DescribeVariable{radian}
+% One radian is $180/\pi$ degrees. We also define \gbc{pi} so a user can say
+% \gbc{90} or \gbc{90deg} or \gbc{pi/2*radian}
+% \DescribeVariable{pi} for the same effect.
+% Actually, not quite: because of \MF{}'s precision limits, the latter is
+% about 90.00025 degrees. \MF{}'s precision is 16 binary places, or
+% slightly under 5 decimals. The accuracy of \gbc{pi} and \gbc{radian} is
+% the maximum possible. If we \emph{define} \gbc{radian} to be
+% \gbc{90/(pi/2)} or \gbc{180/pi} the value of \gbc{pi/2*radian} is even
+% less accurate.
+% \begin{macrocode}
+newinternal deg, pi, radian;
+deg := 1; pi := 3.14159;
+radian := 57.29578;
+numeric degree; degree := deg;
+
+% \end{macrocode}
+%
+% \VariableIndex{drawpen}
+% \VariableIndex{penwd}
% \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
@@ -388,7 +448,8 @@ radian := 180/pi;
% 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.)
-
+%
+% \VariableIndex{hatchpen}
% 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
@@ -398,35 +459,41 @@ radian := 180/pi;
% journal publisher (e.g., AMS) recommend no smaller than \mfc{.5bp} for
% author-supplied drawings.
%
+% \VariableIndex{hatchwd}
% 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;
+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;
+ setvariable (pen) (drawpen) pencircle scaled penwd;
+ save currentpen; pen currentpen; pickup drawpen;
enddef;
-numeric hatchwd; hatchwd := 0.5bp;
-pen hatchpen; hatchpen := pencircle scaled hatchwd;
+numeric hatchwd; hatchwd := 0.5bp;
+pen hatchpen; hatchpen := pencircle scaled hatchwd;
% \end{macrocode}
%
+% \DescribeVariable{clipall}
% 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.
-%
+% picture size variables. The other,
+% \DescribeVariable{ClipOn}
+% \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}.
+% \DescribeVariable{ClipPath}
+% 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.
+%
+% \DescribeVariable{truebbox}
% 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{}
@@ -444,24 +511,25 @@ pen hatchpen; hatchpen := pencircle scaled hatchwd;
% 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;
+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
+ if ClipOn and (ClipPath > 0): clipsto (v, ClipPath); fi
enddef;
def noclip (text t) =
- hide( save ClipOn; boolean ClipOn; ClipOn := false; t)
+ hide ( setboolean (ClipOn) false; t)
enddef;
% \end{macrocode}
%
+% \DescribeVariable{showbbox}
% The boolean \gbc{showbbox} is for debugging the \gbc{*bbox} macros.
% \begin{macrocode}
-boolean showbbox; showbbox := false;
+boolean showbbox; showbbox := false;
% \end{macrocode}
%
@@ -473,29 +541,36 @@ boolean showbbox; showbbox := false;
% 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}
+% We define \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 distinguish between nonwhite
+% (black, ${}<1$) and white (${}\ge1$). For filling commands we will allow
+% levels in between, and fill with an approximation using a version of
+% the \gbc{shade} macro.
% \begin{macrocode}
%<*MF>
-let color = numeric; color black, white;
-black := 0; white := 1;
-def _wc_ = killtext enddef;
+let color = numeric; color black, white;
+black := 0; white := 1;
+def withcolor text t = enddef;
%</MF>
-%<MP>def _wc_ = withcolor enddef;
+def _wc_ = withcolor enddef;
% \end{macrocode}
%
+% \VariableIndex{currentcolor}
+% \VariableIndex{drawcolor}
+% \VariableIndex{fillcolor}
+% \VariableIndex{hatchcolor}
+% \VariableIndex{headcolor}
+% \VariableIndex{pointcolor}
+% \VariableIndex{tlabelcolor}
% 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,
+color currentcolor, drawcolor, fillcolor, hatchcolor,
headcolor, pointcolor, tlabelcolor, background;
currentcolor := fillcolor := drawcolor := hatchcolor :=
headcolor := pointcolor := tlabelcolor := black;
@@ -508,11 +583,11 @@ background := white;
% ($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
+ if unknown t: 0
+ elseif not (numeric t): 0
+ elseif t < 0: 0
+ elseif t > 1: 1
+ else: t
fi
enddef;
@@ -536,7 +611,9 @@ enddef;
% 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.
+% three numeric parameters to a color triple in the obvious way. It does
+% \emph{not} truncate the parameters, so if that is necessary, use
+% \gbc{rgb}.
%
% \DescribeRoutine{rgb}
% To simplify \mfpic, we have the nearly redundant \gbc{rgb} which
@@ -544,7 +621,7 @@ enddef;
% 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 gray (expr g) = (snapto g)*white enddef;
vardef makeclr (expr r, g, b) =
%<MF> gray (sqrt((2r*r + 4g*g + b*b)/7))
@@ -572,25 +649,27 @@ enddef;
% This merely rescales numbers in the range 0--255 to the range 0--1.
%
% \DescribeRoutine{named}
+% These last two, like \gbc{rgb}, are nearly redundant, but they convert
+% numerics to gray, and convert other non-color variables and unknown
+% color variables to black.
% \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.
+% The difference between \gbc{named} and \gbc{forceclr} is that the
+% former requires a suffix parameter (that is, a \emph{name}), 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 named (suffix c) =
+ if unknown c: black else: forceclr (c) fi
+enddef;
vardef forceclr (expr c) =
- if unknown c : black
- elseif numeric c : gray (c)
- elseif color c : c
- else : black
+ if numeric c: gray (c)
+ elseif color c: c
+ else: black
fi
enddef;
@@ -610,16 +689,16 @@ yellow := rgb (1, 1, 0);
%
% \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
+% \gbc{ClipPath} is a typical example of an array. Arrays are 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.
+% 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
@@ -630,36 +709,42 @@ yellow := rgb (1, 1, 0);
% 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
+% but unforeseen usage (which I've since forgotten) 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 that \mfpic's \
+% \cs{plr} writes.
+% \begin{macrocode}
+def list (suffix v) (text lst) =
+ v := 0; for _itm = lst: v[incr v] := _itm; endfor
+ if v = 0:
+ GBerrmsg ("no list to process!")
+ "An attempt was made to produce an array from a "
+ & "list of expressions having no valid entries.";
+ fi
enddef;
def map (text proc) (text lst) =
- hide(_map := 0;)
- for _a = lst :
- if _map = 0 : hide(_map := 1;) else: , fi
- proc(_a)
+ hide (_map := 0;)
+ for _a = lst:
+ if _map = 0: hide (_map := 1;) else: , fi
+ proc (_a)
endfor
enddef;
% \end{macrocode}
-% \DescribeRoutine{knownarray}
+% \DescribeRoutine{knownnumericarray}
% 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}.
+% for \gbc{n} from 1 to \gbc{arr}. Since we so far only need it for
+% numeric arrays, we also check if each entry is numeric.
% \begin{macrocode}
-vardef knownarray suffix arr =
- save _kna; boolean _kna;
- _kna := (known arr) and (numeric arr);
+vardef knownnumericarray suffix arr =
+ setboolean (_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];
+ _kna := (known arr[_idx]) and (numeric arr[_idx]);
endfor
fi
_kna
@@ -668,25 +753,24 @@ 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.
+% This makes some code much more readable. It simply steps through an
+% array and copies the values into another array. It is only used for
+% numeric arrays so far, but could be used for any kind.
% \begin{macrocode}
-def copyarray(suffix from, to) =
- to := 0;
- for _idx = 1 upto from:
- to[incr to] := from[_idx];
- endfor
+def copyarray (suffix src, dest) =
+ for _idx = 1 upto src: dest[_idx] := src[_idx]; endfor
+ dest := src;
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}.
+% array \gbc{p[\,]} with \gbc{p} members. It returns nothing. It is currently
+% unused, although it was once used for things like \gbc{maxpair}.
% \begin{macrocode}
def maparr (text proc) (suffix p) =
- for _idx = 1 upto p: proc (p[_idx]); endfor
+ for _idx = 1 upto p: proc (p[_idx]); endfor
enddef;
% \end{macrocode}
@@ -695,62 +779,105 @@ enddef;
% 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}''
+% used for the array name. We used to include \mfc{save} in this macro,
+% but ran into a problem once when the argument 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);
+% this macro decide what to save. Actually, now it expands to the more
+% general command \gbc{gsetarray} with type \gbc{pair}. That command
+% then reads the suffix argument that should follow.
+%
+% Since the above change was made, macros evolved so that \emph{all} uses
+% of \gbc{textpairs} are now preceeded by \gbc{save}. Thus, I have now
+% replaced them all with calls to \gbc{setpairs} (it calls \gbc{setarray}
+% \emph{does} \gbc{save} the variable). In all those cases, the
+% `\gbc{saved}' variable is a temporary local array.
+%
+% \DescribeRoutine{setuniquepairs}
+% This does the same but omit any pair if it is identical to the previous
+% one. It \mfc{save}\,s the variable, since all its uses are internal
+% and require that.
+% \begin{macrocode}
+def textpairs = gsetarray (pair) enddef;
+
+def setuniquepairs (suffix p) (text t) =
+ save p; pair p[];
+ setpairs (_up) (t);
+ if _up > 0:
+ p := 1; p1 := _up1;
+ for _i = 2 upto _up:
+ if _up[_i] <> p[p]: p[incr p] := _up[_i]; fi
+ endfor
+ else:
+ p := 0;
+ fi
enddef;
% \end{macrocode}
+%
+% \subsection{Utilities}\label{utilities}
+%
% \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}.
+% This applies a procedure \gbc{proc} (which maps numeric to numeric) to
+% each part of pair \gbc{p}, and returns the resultant pair. I've decided
+% not to use it (for efficiency), but to leave it defined for backward
+% compatibility.\\
+% \DescribeRoutine{floorpair}\gbc{floorpair} applies \mfc{floor} to both
+% parts of a pair.\\
+% \DescribeRoutine{ceilingpair}\gbc{ceilingpair} does the same with
+% \gbc{ceiling}.\\
+% \DescribeRoutine{hroundpair}\gbc{hroundpair} does the same with
+% \gbc{hround}.
+%
+% All three could use \gbc{chpair} with \gbc{proc} equal to \mfc{floor},
+% \mfc{ceiling} and \mfc{hround}, but I now code them directly.
+%
+% \DescribeRoutine{goodpair}
+% This last one is used (only in \MF{}) to adjust pairs to the pixel grid.
+% It is the only place \gbc{hroundpair} is used. None of these is used in
+% the \MP{} version.
% \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;
+vardef floorpair (expr p) = (floor (xpart p), floor (ypart p)) enddef;
+vardef ceilingpair (expr p) =
+ (ceiling (xpart p), ceiling (ypart p))
+enddef;
+
+%<*MF>
+def hroundpair (expr p) = (hround (xpart p), hround (ypart p)) enddef;
+vardef goodpair (expr p) = hroundpair(p.t_) enddef;
+%</MF>
% \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.
+% \gbc{emin} differs from \prog{plain}'s \mfc{min} in that it allows
+% only two values. It can therefore be coded simply, without the overhead
+% of a \mfc{for}-loop. \DescribeRoutine{emax}\gbc{emax} is analogous. Both
+% are needed so often that it is possible a significant amount of time is
+% saved with these versions.
%
% \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{pairmax}\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}.
+% minimum $y$ coordinates of all pairs in the array \gbc{p[\,]}.
+% \DescribeRoutine{maxpair}
+% \gbc{maxpair} is analogous. Somehow, both of them have disappeared from
+% \grafbase. They were formerly used only in the \gbc{*bbox} macros.
+% That code used a loop to build an array of control points and these
+% routines would \emph{each} loop through that. The current code uses one
+% loop (instead of three) through the control points, updating both the
+% maximum and minimum at each one.
% \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 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 ) )
@@ -759,23 +886,271 @@ 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
+vardef minpair (suffix p) = setpair (_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
+vardef maxpair (suffix p) = setpair (_mp) p1;
+ for _idx = 2 upto p - 1: _mp := pairmax (_mp, p[_idx]); endfor
pairmax (_mp, p[p])
enddef;
% \end{macrocode}
%
+% \DescribeRoutine{xprod}
+% A binary operation between pairs $z\sb1$ and $x\sb2$ that returns the
+% cross product $x\sb1 y\sb2 - x\sb2 y\sb1$. This gives, among other
+% things, twice the area of the triangle with two sides $z\sb1$ and
+% $z\sb2$. It is used only in \gbc{mkconvex}.
+% \begin{macrocode}
+primarydef Z xprod W = (xpart Z * ypart W - xpart W * ypart Z) enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{force_initial}
+% \gbc{force_initial} modifies a path so that it has all the same points
+% and controls as before, except its first point is replaced with \mfc{p}.
+% \DescribeRoutine{force_terminal}\gbc{force_terminal} replaces the last
+% point. This is for cases where, theoretically, paths \gbc{f} and \gbc{g}
+% should meet at an endpoint, but do not due to finite precision. Instead
+% of doing \mfc{f..g}, which adds a random tiny segment, we adjust the
+% endpoints to exactly match the other and do \mfc{f\&g}, producing a join
+% without an additional segment.
+%
+% \DescribeRoutine{force_equal_ends}
+% This forces the last point of the first path and the first point of the
+% second to equal the average of their original values. It is the only one
+% of these four actually used anywhere else in \grafbase.
+% \DescribeRoutine{replace_ends_of_cycle}\gbc{replace_ends_of_cycle}
+% applies something similar to a cycle.
+% \begin{macrocode}
+def force_initial (expr p) (suffix f) =
+ hide( setnumeric (_n) length f;
+ f := p
+ if _n = 0:
+ {0,0}
+ else:
+ ..controls post0 (f) and pre 1 (f).. subpath (1,_n) of f
+ fi;)
+enddef;
+
+def force_terminal (expr p) (suffix f) =
+ hide(setpath (_f) reverse f;
+ force_initial (p) (_f);
+ f := reverse _f;)
+enddef;
+
+def force_equal_ends (suffix f, g) =
+ hide(save _p; pair _p;
+ _p := .5[pnt[length f] (f), pnt0(g)];
+ force_terminal (_p) (f); force_initial (_p) (g);)
+enddef;
+
+def replace_ends_of_cycle (expr p) (suffix f) =
+hide(
+if cycle f:
+ save _n; _n := length f;
+ f := p
+ if _n = 0: &cycle
+ else: .. controls post0 (f) and pre 1 (f) ..
+ if _n = 1: cycle
+ else: subpath (1, _n - 1) of f ..
+ controls post[_n - 1](f) and pre[_n](f) .. cycle
+ fi
+ fi;
+fi)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{intersects}
+% A binary relation, with the precedence level (almost) that of other
+% relations, produces \mfc{true} if \MF{} determines that the paths
+% intersect, false otherwise. It also \DescribeVariable{thetimes}sets the
+% pair variable \gbc{thetimes} and its parts \gbc{_Xtime} and \gbc{_Ytime}.
+% Then \DescribeRoutine{misses}\gbc{misses} is the opposite relation,
+% used when the intersection point is not needed. It only occurs in the
+% (unused) code of \gbc{tightbbox}.
+% \begin{macrocode}
+pair thetimes;
+numeric _Xtime, _Ytime;
+tertiarydef a intersects b =
+ begingroup
+ thetimes := a intersectiontimes b;
+ _Xtime := xpart thetimes;
+ _Ytime := ypart thetimes;
+ (_Xtime > -1)
+ endgroup
+enddef;
+
+tertiarydef a misses b = ((a intersectiontimes b) < origin) enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{makepicture}
+% Takes any expression and does what it can to make a picture from it.
+%
+% \DescribeRoutine{onepointpath}
+% Takes a point and forces it to be a path. If a vardef takes a list of
+% points and it \emph{must} return a path that perhaps \emph{must} be
+% cyclic, it can use this as a fallback. If an \mfpic{} command such as
+% \cs{arc} receives an invalid optional parameter, it won't know what
+% command to write to the output file. It can use
+% \DescribeRoutine{fallbackpath}\gbc{fallbackpath} as long as the first
+% parameter is a point.
+%
+% \DescribeRoutine{even}
+% Of course \gbc{even} means \gbc{not odd}.
+% \begin{macrocode}
+vardef makepicture (expr s) =
+ if picture s: s
+%<MP> elseif string s: s infont defaultfont scaled defaultscale
+ elseif path s: picpath (s)
+ else: nullpicture
+ fi
+enddef;
+
+vardef onepointpath (expr cyclic, q) =
+ q if cyclic: &cycle else: {0,0} fi
+enddef;
+
+vardef fallbackpath (expr cyclic, p) (text t) =
+ onepointpath (cyclic, p)
+enddef;
+
+def even = not odd enddef;
+
+primarydef a divides b =
+ ((b mod a) = 0)
+enddef;
+
+% \end{macrocode}
+%
+% \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}.
+%
+% \DescribeRoutine{beginimage}
+% Instead of making lengthy drawing code a parameter, one might prefer an
+% environment-like syntax, writing \gbc{X := beginimage } at the start
+% and \DescribeRoutine{endimage}\gbc{endimage} at the end.
+%
+% \DescribeRoutine{makeimage}
+% This is for the \mfpic{} command \cs{mfpimage}. It takes a suffix
+% parameter (the name of the picture variable) and a coordinate pair (in
+% graph coordinates). The drawing commands, up to the following
+% \gbc{endimage}, draw on this picture variable with the given pair as the
+% reference point.
+% \begin{macrocode}
+%<*MF>
+vardef image (text t) =
+ newpicture (currentpicture);
+ t;
+ currentpicture
+enddef;
+
+%</MF>
+def beginimage =
+ begingroup
+ newpicture (currentpicture);
+enddef;
+def endimage =
+ ; currentpicture
+ endgroup
+enddef;
+
+def makeimage (suffix name) (expr refpt) =
+ setpair (_image_reference_point) zconv(refpt);
+ setpicture (name) beginimage
+enddef;
+def concludeimage =
+ endimage shifted
+%<MF> -goodpair (_image_reference_point)
+%<MP> -_image_reference_point
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{setvariable}
+% This is are mainly to save space in \mfpic-generated files. In \grafbase{}
+% itself the \mfc{save} is often inconvenient, but it turns out there are
+% many cases where it \emph{is} used; enough so that we have abbreviations
+% \RoutineIndex{setpicture}\gbc{setpicture},
+% \RoutineIndex{setpath}\gbc{setpath}, \RoutineIndex{setpair}\gbc{setpair}
+% and \RoutineIndex{setboolean}\gbc{setboolean}, together with the
+% common uses \RoutineIndex{newpicture}\gbc{newpicture} and
+% \RoutineIndex{convertpath}\gbc{convertpath}.
+% \DescribeRoutine{gsetvariable}\gbc{gsetvariable} is the global
+% version. It has no abbreviations, but it is occasionally needed for
+% \mfpic{}. The only difference is the lack of a \gbc{save}. None of these
+% commands take the value as a parameter. That should follow, and is picked
+% up by the ending \mfc{:=}.
+%
+% \DescribeRoutine{setarray}
+% Then \gbc{setarray} is the array version. It takes the same parameters
+% as \gbc{setvariable}, but what should follow is a list of expressions in
+% parentheses. It calls \gbc{list} to read each item into
+% \gbc{name1}, \gbc{name2}, etc.
+% \DescribeRoutine{setpairs}\gbc{setpairs} is an abbreviation for arrays
+% of pairs. There is also has a global version
+% \DescribeRoutine{gsetarray}\gbc{gsetarray}.
+% \begin{macrocode}
+def setvariable (text kind) (suffix name) =
+ save name; kind name; name :=
+enddef;
+def gsetvariable (text kind) (suffix name) = kind name; name :=
+enddef;
+
+def setnumeric (suffix name) = save name; name := enddef;
+def setboolean = setvariable (boolean) enddef;
+def setpair = setvariable (pair) enddef;
+def setpath = setvariable (path) enddef;
+def setcolor = setvariable (color) enddef;
+def setpicture = setvariable (picture) enddef;
+def settension (suffix tn) expr tens =
+ setnumeric (tn) if tens > 0: tens else: default_tension fi;
+enddef;
+def fixtension (suffix tn) = if tn < .75: tn := .75; fi enddef;
+
+def newpicture (suffix pic) = setpicture (pic) nullpicture; enddef;
+def convertpath (suffix g) expr f = setpath (g) zconv (f); enddef;
+
+def setarray (text kind) (suffix name) =
+ save name; kind name[]; list (name)
+enddef;
+def setpairs = setarray (pair) enddef;
+def gsetarray (text kind) (suffix name) =
+ numeric name; kind name[]; list (name)
+enddef;
+
+% \end{macrocode}
+%
+% The next are slightly different, but seem to belong here.
+
+% \DescribeRoutine{setbbox}
+% In \gbc{setbbox} we save and initialize \emph{two} pair variables and
+% set them to the bounding box of a path that should follow.
+%
+% \DescribeRoutine{setsplit}
+% There are a couple of routines that modify a variable to make sure it is
+% positive and integral. In a couple of places two routine \emph{must} use
+% the same value. Here we isolate the code that does the modification, and
+% then both routines call \gbc{setsplit}.
+% \begin{macrocode}
+def setbbox (suffix ll, ur) =
+ save ll, ur; pair ll, ur; getbbox (ll, ur)
+enddef;
+
+def setsplit (suffix s) expr ss =
+ setnumeric (s) emax (1, ceiling ss);
+enddef;
+
+% \end{macrocode}
%
-% \section{The \grafbase{} coordinate system}\label{coordinate}
+% \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
@@ -788,7 +1163,7 @@ enddef;
% 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
+% It is defined to 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}.
@@ -796,7 +1171,7 @@ enddef;
% 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))
+% 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.
@@ -808,38 +1183,43 @@ enddef;
% 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.
+% lines) are a design decision that either should be independent of scale
+% or should scale in a nonobvious 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).
+% $(0,0)$.
+%
+% \subsection{The main transforms}\label{ztr}
%
+% \DescribeVariable{vtr}
% 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.
+% and \DescribeVariable{ztr}\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
+% 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
+% 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.
+% In \MF{}, \mfc{currenttransform} (via the macro \mfc{.t_}, defined by
+% \mfc{mode_setup}) takes care of the aspect ratio. In \MP{} the final
+% rasterizer should do this.
%
% \gbc{charwd} and \gbc{charht} are sharped coordinates defined by the
-% startup code \gbc{beginmfpic}, and \gbc{w_} and \gbc{h_} are the
+% startup code \gbc{beginmfpic}, while \gbc{w_} and \gbc{h_} are the
% corresponding device (pixel) coordinates
%
% \DescribeRoutine{setztr}
@@ -849,7 +1229,7 @@ enddef;
% \begin{macrocode}
transform ztr, vtr;
def setztr =
- if debug :
+ if debug:
GBdebug;
%<*MF>
>> "charwd = " & decimal charwd & "pt#";
@@ -874,14 +1254,13 @@ def setztr =
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 :
+ vtr := identity xscaled xscale yscaled yscale scaled (unitlen*hppp);
+ ztr := identity shifted (-xneg, -yneg) transformed vtr;
+ if debug:
GBdebug;
- >> "ztr: ";
+ >> "ztr is";
show ztr;
- >> "vtr: ";
+ >> "vtr is";
show vtr;
GBenddebug;
fi
@@ -890,50 +1269,36 @@ 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.
+% device coordinates. The expressions include pairs, paths, and transforms.
+% This is an affine transform. The inverse,
+% \DescribeRoutine{invzconv}\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.
+% device coordinates. This is a linear (ie, vector) transform. Also,
+% \DescribeRoutine{invvconv}\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;
+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}
%
+% \subsection{The \gbc{mfpic} environment}\label{mfpic}
+%
% \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}.
+% because we can achieve special effects by redefining it (see the
+% \gbc{tile} macro).
% \begin{macrocode}
-def active_plane = currentpicture enddef;
-
-%<*MF>
-vardef image(text t) =
- save currentpicture; picture currentpicture;
- currentpicture := nullpicture;
- t;
- currentpicture
-enddef;
+def active_plane = currentpicture enddef;
-%</MF>
% \end{macrocode}
%
% \DescribeRoutine{initpic}
@@ -943,38 +1308,46 @@ enddef;
% 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{underlaylabels} is true, we try to make them part of the
+% background, adding them to the picture variable \gbc{background_labels}.
+% Just before shipout, the picture is placed on top.
+%
% 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{foreground_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}.
+% the default for \gbc{overlaylabels} is \gbc{false}.
+%
+% We initialize \gbc{foreground_labels} and \gbc{background_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;
+boolean overlaylabels, underlaylabels, havebackground;
+overlaylabels := false;
+underlaylabels := false;
+havebackground := false;
%</MP>
def initpic =
setztr;
resizedrawpen (penwd);
- if ClipOn : ClipPath := 1;
+ if ClipOn: ClipPath := 1;
ClipPath1 := rect (origin, (w_, h_));
fi
- if debug :
+ 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;
+ newpicture (foreground_labels);
+ newpicture (background_labels);
+ havebackground := false;
+ save labelbb; pair labelbb.ll, labelbb.ur;
labelbb.ll := labelbb.ur := origin;
%</MP>
enddef;
@@ -982,32 +1355,23 @@ enddef;
% \end{macrocode}
%
% \DescribeRoutine{mfpicenv}
+% We define a \gbc{mfpicenv} environment for compatibility with older
+% \file{graphbase.mf} (mainly for \prog{fig2dev}'s \file{genmf.c}).
% \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}
+% Actually, I have no idea if \prog{fig2dev} even works with the current
+% \mfpic.
+%
% \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 mfpicenv = enddef;
+def endmfpicenv = enddef;
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;
+ xneg := a; xpos := b;
+ yneg := c; ypos := d;
enddef;
-% \begin{macrocode}
% \end{macrocode}
%
% \DescribeRoutine{beginmfpic}
@@ -1029,23 +1393,23 @@ enddef;
% 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_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_;
+ save 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;
+ 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>
@@ -1067,7 +1431,7 @@ enddef;
% \begin{macrocode}
def endmfpic =
scantokens extra_endmfpic;
- if debug :
+ if debug:
GBdebug;
%<MF> >> "TFM charwd = " & decimal charwd & "pt#";
%<MF> >> "TFM charht = " & decimal charht & "pt#";
@@ -1075,16 +1439,15 @@ def endmfpic =
%<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
+ if clipall: clipto (active_plane) rect (origin, (w_, h_)); fi
+ if showbbox: noclip ( safedraw rect (origin, (w_, h_)) ); fi
%<*MF>
+ scantokens extra_endchar;
+ if proofing > 0: makebox (proofrule); fi
+ chardx := w_; % desired width of character in pixels
shipit;
- if displaying > 0 : makebox(screenrule); showit; fi
+ if displaying > 0: makebox (screenrule); showit; fi
endgroup
%</MF>
% \end{macrocode}
@@ -1096,38 +1459,51 @@ def endmfpic =
%<*MP>
save _ll, _ur;
pair _ll, _ur;
- if truebbox :
+ 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_}
+% \gbc{clipall} is set. In that case, we just set the bounding box to the
+% coordinates determined by \gbc{w_} and \gbc{h_}, otherwise we expand
+% them to the \gbc{labelbb} values.
% \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);
+ _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,
+% cases. 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;
+ _ur := pairmax (_ur, _ll + eps*(1, 1));
+ setbounds active_plane to rect (_ll, _ur);
+% \end{macrocode}
+% Finally, if \gbc{overlaylabels} or \gbc{underlaylabels} was true during
+% a \gbc{newgblabel} command, then the label was not added to
+% \mfc{currentpicture} but rather to \gbc{foreground_labels} or
+% \gbc{background_labels}. We add those pictures now, the former on top of
+% \mfc{currentpicture}, the latter underneath. This might extend the bbox
+% calculated above, but that is one of the effects we \emph{want} to
+% achieve. Picture variables can consume a lot of memory, so we clear
+% each one after we have added it. Unfortunately, we will temporarily
+% have two copies of the current picture in memory for background text, so
+% we perform this operation only if \gbc{havebackground} is true.
+% \begin{macrocode}
+ if havebackground:
+ addto background_labels also active_plane;
+ active_plane := background_labels;
+ background_labels := nullpicture;
+ fi
+ addto active_plane also foreground_labels;
+ foreground_labels := nullpicture;
endfig;
%</MP>
enddef;
@@ -1137,123 +1513,140 @@ enddef;
%
% \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.
+% In the \MP{} version, \gbc{label_adjust}, \gbc{label_sep} and
+% \gbc{labelpath_sep} are the equivalent of \mfpic's \cs{tlabeloffset},
+% \cs{tlpointsep} and \cs{tlpathsep}. 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,
+% \gbc{label_adjust} is a vector displacement applied to all labels,
% 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}).
+% Finally, \gbc{labelpath_sep} is the separation of a surrounding path
+% from the text.
% \begin{macrocode}
-pair label_adjust; label_adjust := (0, 0);
-numeric label_sep; label_sep := 0;
-
+pair label_adjust;
+label_adjust := origin;
+numeric label_sep, labelpath_sep ;
+label_sep := 0; labelpath_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
+% works only if \mfc{etex} is followed 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.
+% certainly any parentheses have to be in matching pairs. Not so obvious
+% is that \cs{begingroup} and \cs{endgroup} have to be balanced: \MF{}
+% sees \cs{begingroup} as `\verb$\$' plus \mfc{begingroup}.) 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;
+%<MF>def verbatimtex text t = 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:
+% operator or the \gbc{picpath} macro).
+
+% The macro \gbc{newgblabel} takes 6 parameters. The first three
+% parameters could easily be condensed into two if \mfpic{} support were
+% all that was required, however I thought it best to make it general.
+% The parameters \gbc{hf} and \gbc{vf} are numeric, with \gbc{hf}
+% representing the fraction of the text that lies left of the point where
+% the text is placed and \gbc{vf} represents the fraction of
+% text that lies below that point. However, if the third parameter is
+% \mfc{true}, then \gbc{vf} is relative to the baseline (i.e., the depth
+% is ignored). In \mfpic{} this is only used with \gbc{vf = 0} to get
+% placement on the baseline.
+%
+% These three parameters 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{hf} determines horizontal position: $0=\mathtt{l}$,
+% $.5=\mathtt{c}$, and $1 = \mathtt{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
+% option \texttt{B}, $\mathtt{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}.
+% $0 = \mathtt{b}$, $.5 = \mathtt{c}$ and $1 = \mathtt{t}$.
+% \end{itemize}
+% The remaining parameters have the following meanings:
+% \begin{itemize}
% \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.
+% First the bounding box of the picture is determined using
+% \gbc{pathdims}. (Why \texttt{\textit{path}dims}? Because it was written
+% for the paths that surround text, and was then incorporated into text
+% placement when \gbc{newgblabel} replace \gbc{gblabel}.) Then
+% \gbc{readjustdims} extends that box by \gbc{label_sep}, 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{foreground_labels} and added to \gbc{active_plane} at
+% \gbc{endmfpic}. If \gbc{underlaylabels} is true, it is placed in
+% picture \gbc{background_labels} and \gbc{active_plane} is placed on top
+% of it. Otherwise, it is added directly to \gbc{active_plane} and the
+% \gbc{labelbb} variables are adjusted.
%
% We also use \gbc{ref_shift} in \MF{} since the curves that surround text
% require it.
%
+% \DescribeRoutine{gblabel}
% 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.
+% parameters are only those that \mfpic{} would write.
+%
+% We provide a null definition of newgblabel for \MF{} to allow \mfpic's
+% \file{.mp} files to be somewhat usable with minimal changes. It
+% requires a text parameter, since \MF{} would be unable to evaluate
+% \mfc{btex} expressions.
% \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;
+ save _lab, _ll, _ur; picture _lab; pair _ll, _ur;
+ _lab := makepicture (s);
+ pathdims (origin, _lab) (_ll, _ur);
+ readjustdims (_ll, _ur) (label_sep);
+ _lab := thegblabel (ref_shift (hf, vf, BL, _ll, _ur), r, _lab);
+ save _b; pair _b;
+ for _itm = pts:
+ _b := zconv (_itm);
+ if overlaylabels:
+ addto foreground_labels also _lab shifted _b _wc_ tlabelcolor;
+ elseif underlaylabels:
+ addto background_labels also _lab shifted _b _wc_ tlabelcolor;
+ havebackground := true;
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
+%</MP>
+%<MF>vardef newgblabel (expr hf, vf, BL, r) (text s) (text pts) =
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) =
+%<MP>vardef gblabel (expr a, b, c, d, r) (expr s) (text t) =
+%<MF>vardef gblabel (expr a, b, c, d, r) (text 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] )
+ (vf)[if BL: 0 else: (ypart ll) fi, ypart ur] )
enddef;
% \end{macrocode}
@@ -1269,35 +1662,35 @@ enddef;
% \end{macrocode}
%
-% \subsection{Decorating the text}\label{decorating}
+% \subsection{Decorating the text, \MF{} or \MP{}}\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
+% return the path in graph coordinates. In
+% \DescribeRoutine{textrect}\gbc{textrect}, the path is a rectangle with
+% optionally 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.
+% In \DescribeRoutine{textoval}\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
+% \DescribeRoutine{textellipse}\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
+% space on all sides determined by \gbc{labelpath_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}.
+% one without. These versions always center the surrounding path on the
+% the point \gbc{loc}. The extended versions (below) have the same
+% flexibility of placement as the commands that place the label being
+% surrounded.
%
% The first parameter \gbc{lbl} is either a pair representing the
% height and width of the text (only possibility in \MF) or the actual
@@ -1323,77 +1716,77 @@ 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.
+% \DescribeRoutine{textrectx}\gbc{textrectx} is the extended version of
+% \gbc{textrect} which allows the same adjustments to the rectangle that we
+% can apply to the text it surrounds (via \gbc{newgblabel}). In fact, it
+% calculates the position in exactly the same manner as that macro, and
+% the first 4 parameters encode that position in the same way.
+%
+% The placement of each path is: shifted and rotated by the same amount
+% as the text (by \gbc{ref_shift}) according to the first four parameters,
+% then shifted to the point given in the third parameter \gbc{loc}, and
+% finally shifted by the vector specified in \gbc{label_adjust}.
%
% \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
+% \gbc{pathdims}) 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.)
+% \DescribeVariable{roundends}\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;
+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
+ pathdims (xy, lbl) (ll, ur);
+ readjustdims (ll, ur) (labelpath_sep)
+ _r := if numeric rad: rad
+ elseif not boolean rad: 0
+ elseif rad: emin (xpart(ur-ll), ypart (ur-ll))/sqrt(2)
+ else: 0
fi;
- if _r = 0 :
- f := rect(ll, ur);
+ 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
+ p1 := ur - _r*dir(45);
+ p3 := ll + _r*dir(45);
+ p2 := (xpart p3, ypart p1);
+ p4 := (xpart p1, ypart p3);
+% \end{macrocode}
+% We allow the rounding radius to be negative and make the corners
+% indented in that case. We no longer reverse the path in this case.
+% \begin{macrocode}
+ q := if _r < 0: reverse fi quartercircle scaled 2_r;
+ f :=
+ (q shifted p1)--(q rotated 90 shifted p2)
+ --(q rotated 180 shifted p3)
+ --(q rotated -90 shifted p4)--cycle;
+
fi
- invvconv(thegblabel(ref_shift(a, b, c, ll, ur), rot, f)) shifted loc
+ readjustdims (ll, ur) (label_sep - labelpath_sep);
+ 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}.
+% \DescribeRoutine{textellipsex}The macro \gbc{textellipsex} is a simlar
+% extension for \gbc{textellipse}. It and the related macro
+% \DescribeRoutine{textovalx}\gbc{textovalx} now call a common macro with
+% different values of a boolean parameter.
% \begin{macrocode}
-def textovalx = xellipse (true) enddef;
-def textellipsex = xellipse (false) enddef;
+def textovalx = xellipse (true) enddef;
+def textellipsex = xellipse (false) enddef;
% \end{macrocode}
% \DescribeRoutine{xellipse}
@@ -1401,97 +1794,100 @@ def textellipsex = xellipse (false) enddef;
% 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}.
+% equals \gbc{mult*ww/hh}, otherwise it equals \gbc{mult}.
% \begin{macrocode}
vardef xellipse (expr aspect, a, b, c, r, xy, lbl, mult, loc) =
- if mult = 0 :
+ 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
+ pathdims (xy, lbl) (ll, ur);
+ readjustdims (ll, ur) (labelpath_sep)
+ cc := .5[ll, ur];
(ww, hh) = ur - cc;
- if (ww = 0) or (hh = 0) : % make a line:
+ if (ww = 0) or (hh = 0):
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);
+ save aa, bb;
+% \end{macrocode}
+%
+% The \gbc{aa} and \gbc{bb} are now calculated in a way that decreases the
+% chance of overflow. As a side effect, negative \gbc{mult} no longer
+% reverses the path.
+% \begin{macrocode}
+ aa := ww ++ if aspect: ww else: hh fi *mult;
+ bb := hh ++ if aspect: hh else: ww fi /mult;
+ f := ellipse (cc, aa, bb, 0);
fi
- invvconv(thegblabel (ref_shift (a, b, c, ll, ur), r, f)) shifted loc
+ readjustdims (ll, ur) (label_sep - labelpath_sep);
+ invvconv (thegblabel (ref_shift(a, b, c, ll, ur), r, f)) shifted loc
fi
enddef;
% \end{macrocode}
%
-% \DescribeRoutine{labeldims}
+% \DescribeRoutine{pathdims}
% 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}.
+% string or path) and assigns suitable values to \gbc{ll} and \gbc{ur}.
+%
+% \DescribeRoutine{readjustdims}
+% This is used to add the separations needed to implement the effects of
+% \gbc{label_sep} and \gbc{labelpath_sep}.
% \begin{macrocode}
-
-def labeldims (expr xy, lbl) (suffix ll, ur) =
- if pair lbl :
- ll := xy; ur := lbl;
+def pathdims (expr xy, lbl) (suffix ll, ur) =
+ if pair lbl:
+ ll := xy; ur := lbl;
else:
-%<MF> ll := ur := (0, 0);
+%<MF> ll := ur := origin;
%<*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;
+ setpicture (_lbl) makepicture (lbl);
ll := llcorner _lbl;
ur := urcorner _lbl;
%</MP>
fi
- ll := ll - label_sep*(1, 1);
- ur := ur + label_sep*(1, 1);
+enddef;
+
+def readjustdims (suffix ll, ur) (expr s) =
+ ll := ll - s*(1,1);
+ ur := ur + s*(1,1);
enddef;
% \end{macrocode}
%
%
-% \section{Additional functions}\label{functions}
+% \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
+% So \gbc{reallysmall} is 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
+% 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)}.
+%
+% We set \gbc{secd x = 1/(cosd x)} unless \gbc{cosd x} is less than
+% \gbc{reallysmall}, then we set it equal to \gbc{1/reallysmall}. We do a
+% similar thing with \gbc{cscd}. (When such a substitution happens
+% \DescribeRoutine{TruncateWarn}\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
+% 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.
@@ -1500,29 +1896,28 @@ enddef;
% 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;
+newinternal reallysmall; reallysmall := 3epsilon;
+newinternal nottoosmall; nottoosmall := eps/2 + 2epsilon;
+def signof (expr X) = if X < 0: - fi enddef;
+def TruncateWarn expr s =
+ GBwarn s & " is too large or undefined, so it will be truncated.";
+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}.
+% We define \RoutineIndex{secd}\gbc{secd}, one of the simplest, to include
+% an out of range test (which also prevents division by 0). Then
+% \RoutineIndex{tand}\gbc{tand} can make use of it without any division.
+% We do the same with \RoutineIndex{cscd}\gbc{cscd} and
+% \RoutineIndex{cotd}\gbc{cotd}.
% \begin{macrocode}
vardef secd primary X =
- save temp; temp := cosd(X);
- if abs(temp) < reallysmall :
- TruncateWarn "Secant";
+ setnumeric (temp) cosd(X);
+ if abs(temp) < reallysmall:
+ TruncateWarn "Secant or Tangent";
temp := signof (temp) reallysmall;
fi
1/temp
@@ -1530,9 +1925,9 @@ 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";
+ setnumeric (temp) sind(X);
+ if abs(temp) < reallysmall:
+ TruncateWarn "Cosecant or Cotangent";
temp := signof(temp) reallysmall;
fi
1/temp
@@ -1540,23 +1935,33 @@ 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.
+% These are the inverse functions, which return an angle in degrees:
+% \RoutineIndex{acos}\gbc{acos}, \RoutineIndex{asin}\gbc{asin} and
+% \RoutineIndex{atan}\gbc{atan}.
% \begin{macrocode}
-vardef acos primary X = angle (X, 1 +-+ X) enddef;
-vardef asin primary X = angle (1 +-+ X, X) enddef;
+vardef acos primary X =
+ if abs X > 1:
+ TruncateWarn "Argument of arccosine";
+ angle (signof(X) 1, 0)
+ else:
+ angle (X, 1 +-+ X)
+ fi
+enddef;
+vardef asin primary X =
+ if abs X > 1:
+ TruncateWarn "Argument of arcsine";
+ angle (0, signof(X) 1)
+ else:
+ angle (1 +-+ X, X)
+ fi
+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.
+% Now the trig functions that take angles in radians:
+% \RoutineIndex{sin}\gbc{sin}, \RoutineIndex{cos}\gbc{cos},
+% \RoutineIndex{tan}\gbc{tan}, \RoutineIndex{cot}\gbc{cot},
+% \RoutineIndex{sec}\gbc{sec} and \RoutineIndex{csc}\gbc{csc}.
% \begin{macrocode}
vardef sin primary X = sind (X*radian) enddef;
vardef cos primary X = cosd (X*radian) enddef;
@@ -1566,65 +1971,64 @@ 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.
+%
+% It is useful to have a \DescribeRoutine{degrees}command to convert
+% from radians to degrees and one to \DescribeRoutine{radians}convert
+% from degrees to radians. Thus \gbc{degrees(pi)} produces
+% (approximately) $180$ and \gbc{radians(180)} is approximately $\pi$.
+% \begin{macrocode}
+vardef degrees (expr t) = t*radian enddef;
+vardef radians (expr t) = t/radian enddef;
+% \end{macrocode}
+%
+% And the inverses (\RoutineIndex{invsin}\gbc{invsin},
+% \RoutineIndex{invcos}\gbc{invcos} and \RoutineIndex{invtan}\gbc{invtan})
+% 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}
+%
+% Here we define the standard exponential function
+% \RoutineIndex{exp}\gbc{exp}. (The \MF{} function \mfc{mexp} has the
+% unusual base $e^{1/256}$ to avoid overflow.) The inverse of \gbc{exp} is
+% the natural logarithm (\RoutineIndex{ln}\gbc{ln} or
+% \RoutineIndex{log}\gbc{log}). We also have the general base logarithm
+% \RoutineIndex{logbase}\gbc{logbase} and its two special instances
+% \RoutineIndex{logtwo} \gbc{logtwo} and \RoutineIndex{logten}\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}
% \CMF's pair variables are a decent replacement for complex variables.
% These give some of the more basic functions of standard complex
-% analysis.
+% analysis: \RoutineIndex{Arg}\gbc{Arg}, \RoutineIndex{Log}\gbc{Log},
+% \RoutineIndex{cis}\gbc{cis}, \RoutineIndex{zexp}\gbc{zexp} and
+% \RoutineIndex{sgn}\gbc{sgn}.
% \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;
+vardef Arg primary Z = (angle Z)/radian enddef;
+vardef Log primary Z = (ln (abs Z), Arg Z) enddef;
+vardef cis primary T = dir (T*radian) enddef;
+vardef zexp primary Z = (exp (xpart Z)) * cis (ypart Z) enddef;
+vardef sgn primary Z = if not (Z = origin): unitvector fi Z enddef;
% \end{macrocode}
-% \DescribeRoutine{cosh}
-% \DescribeRoutine{sinh}
-% \DescribeRoutine{tanh}
-% \DescribeRoutine{sech}
-% \DescribeRoutine{csch}
-% \DescribeRoutine{coth}
-% The hyperbolic functions.
+%
+% The hyperbolic functions: \RoutineIndex{cosh}\gbc{cosh}
+% \RoutineIndex{sinh}\gbc{sinh}, \RoutineIndex{tanh}\gbc{tanh},
+% \RoutineIndex{sech}\gbc{sech}, \RoutineIndex{csch}\gbc{csch} and
+% \RoutineIndex{coth}\gbc{coth}.
% \begin{macrocode}
vardef cosh primary X =
- save temp; temp := 2 exp (-abs(X));
- if temp < reallysmall :
+ setnumeric (temp) 2 exp (-abs(X));
+ if temp < reallysmall:
TruncateWarn "Cosh";
temp := reallysmall;
fi
@@ -1632,8 +2036,8 @@ vardef cosh primary X =
enddef;
vardef sinh primary X =
- save temp; temp := 2 exp (-abs(X));
- if temp < reallysmall :
+ setnumeric (temp) 2 exp (-abs(X));
+ if temp < reallysmall:
TruncateWarn "Sinh";
temp := reallysmall;
fi
@@ -1641,60 +2045,55 @@ vardef sinh primary X =
enddef;
vardef sech primary X =
- save temp; temp := exp(-(abs (X)));
+ setnumeric (temp) exp(-(abs (X)));
2temp/(1 + temp*temp)
enddef;
vardef tanh primary X =
- save temp; temp := exp(-2(abs (X)));
+ setnumeric (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 :
+ save temp, tempa; temp := exp(-(abs (X)));
+ tempa := (1 - temp*temp)/2;
+ if tempa < reallysmall:
TruncateWarn "Csch";
- signof (X) 2temp / reallysmall
- else:
- signof (X) 2temp / (1 - temp*temp)
+ tempa := reallysmall;
fi
+ signof (X) temp / tempa
enddef;
vardef coth primary X =
- save temp; temp := tanh(X);
- if abs(temp) < reallysmall :
+ setnumeric (temp) tanh(X);
+ if abs(temp) < reallysmall:
TruncateWarn "Coth";
- temp := signof (temp) reallysmall;
+ temp := signof (X) reallysmall;
fi
1/temp
enddef;
% \end{macrocode}
-% \DescribeRoutine{acosh}
-% \DescribeRoutine{asinh}
-% \DescribeRoutine{atanh}
-% The inverses of some of the hyperbolic functions.
+% The inverses of some of the hyperbolic functions:
+% \RoutineIndex{acosh}\gbc{acosh}, \RoutineIndex{asinh}\gbc{asinh} and
+% \RoutineIndex{atanh}\gbc{atanh}.
% \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.";
+ if y < 1:
+ TruncateWarn "acosh";
0
else:
- ln (y + (y+-+1))
+ ln (y + (y +-+ 1))
fi
enddef;
-vardef asinh primary y = ln (y + (y++1)) 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
+ 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.";
+ TruncateWarn "atanh";
signof (y) infinity
fi
enddef;
@@ -1702,42 +2101,61 @@ enddef;
% \end{macrocode}
%
% \DescribeRoutine{polar}
-% \DescribeRoutine{id}
% \gbc{polar} converts a polar coordinate pair $(r, \theta)$ to the
% corresponding rectangular coordinate pair.
-%
+% \DescribeRoutine{id}
% \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;
+vardef polar primary p = (xpart p) * dir (ypart p) enddef;
+def id (expr x) = x enddef;
% \end{macrocode}
%
+% The definition of powers (\gbc{x**y}) in \prog{plain} \MF{} and \MP{}
+% could be more accurate. In particular \gbc{x=2**10} ought to be an
+% integer (that is, satisfy \mfc{x=floor x}). Here we redefine
+% \prog{plain}'s \mfc{**}, intercepting the case of a positive integer
+% power of an integer.
+% \begin{macrocode}
+primarydef x**y =
+ if y=2: x*x
+ elseif (x = floor x) and (abs y = floor y):
+ 1 for n=1 upto y: *x endfor
+ else: takepower y of x
+ fi
+enddef;
+let ^ = **;
+% \end{macrocode}
%
-% \section{Coordinate systems and transformations}\label{systems}
+% \section{Coordinate Systems and Transformations}\label{systems}
%
+% \DescribeVariable{T_stack}
+% We want to define a localization of the current transform. To do
+% this we define a LIFO stack of transforms \gbc{T_stack[\,]}, and a pair of
+% macros.
% \DescribeRoutine{T_push}
+% \gbc{T_push} puts its argument (a transform) on the stack, and
% \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;
+% We also define two localizing macros
+% \DescribeRoutine{bcoords}\gbc{bcoords} that pushes our \gbc{ztr}
+% on the stack, and \DescribeRoutine{ecoords}\gbc{ecoords} that pops it
+% off. We no longer put \gbc{vtr} on the stack, since we can recalculate
+% it whenever \gbc{ztr} is changed. \gbc{apply_t} always did this, now
+% \gbc{ecoords} does so as well.
+% \begin{macrocode}
+transform T_stack[];
+numeric 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;
+ 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;
+def bcoords = hide ( T_push (ztr) ) enddef;
+def ecoords = hide ( T_pop (ztr); vtr := vectorpart ztr ) enddef;
% \end{macrocode}
%
@@ -1750,276 +2168,363 @@ def ecoords = hide ( T_pop (vtr); T_pop (ztr) ) enddef;
% 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
+% expression of type transform. Thus the argument of \gbc{apply_t} is a
% phrase which, were it to follow a path, would produce a transformed
% path. Knuth calls such a phrase a \emph{transformer}.
% \begin{macrocode}
+vardef vectorpart primary T = T shifted -(origin transformed T) enddef;
+
def apply_t (text Transformer) =
ztr := identity Transformer transformed ztr;
- vtr := ztr shifted - zconv(origin);
+ vtr := vectorpart ztr;
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.
+% And now we define some available transformers.
+% \RoutineIndex{xslant}\gbc{xslant}, \RoutineIndex{yslant}\gbc{yslant},
+% \RoutineIndex{zslant}\gbc{zslant}, \RoutineIndex{xyswap}\gbc{xyswap} and
+% \RoutineIndex{boost}\gbc{boost}. 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 why zslant is a `slant'.
% \begin{macrocode}
-def xslant = slanted enddef; % (x+sy, y).
+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
+ 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).
+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
+ 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;
+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.
+% These are functions that accept 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 or reflect, 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{transformedpath}
+% This is a vardef that reads an undelimited path expression and returns
+% the path transformed by the text argument. All the others run this,
+% allowing it to grab the path expression.
%
% \DescribeRoutine{rotatedpath}
% This returns the path rotated around point \gbc{p} by angle
% \gbc{th} in degrees.
%
+% \DescribeRoutine{reflectedpath}
+% This reflects the path through the line containing points \gbc{p} and
+% \gbc{q}.
+%
% \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}.
+% \gbc{xscaledpath} is similar, but only the horizontal distances from
+% the line $x={}$\gbc{a} are multiplied by \gbc{s}. And with
+% \DescribeRoutine{yscaledpath}\gbc{yscaledpath} the vertical distances
+% from the line $y={}$\gbc{b} 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{xslantedpath}
+% This returns the path xslanted with line $y = {}$\gbc{b}
+% being the pivot rather than the $x$-axis. \DescribeRoutine{slantedpath}
+% The command \gbc{slantedpath} is just an alias for \gbc{xslantedpath},
+% while \DescribeRoutine{yslantedpath}\gbc{yslantedpath} is the vertical
+% version, yslanted with line $x = {}$\gbc{a} being the pivot rather than
+% the $y$-axis.
%
% \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)
+% This returns the path in which all points 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 transformedpath (text Transformer) expr f = f Transformer
enddef;
-vardef scaledpath (expr p, s) expr f =
- f shifted -p scaled s shifted p
+
+def rotatedpath (expr p, th) =
+ transformedpath (
+ transformed vtr
+ rotatedaround (p transformed vtr, th)
+ transformed (inverse vtr)
+ )
enddef;
-vardef xslantedpath (expr b, s) expr f =
- f shifted (0, -b) slanted s shifted (0, b)
+def reflectedpath (expr p, q) =
+ transformedpath (
+ transformed vtr
+ reflectedabout (p transformed vtr, q transformed vtr)
+ transformed (inverse vtr)
+ )
enddef;
-def slantedpath = xslantedpath enddef;
-vardef yslantedpath (expr a, s) expr f =
- f shifted (-a, 0) yslant s shifted (0, a)
+
+def scaledpath (expr p, s) =
+ transformedpath (shifted -p scaled s shifted p)
enddef;
-vardef xscaledpath (expr a, s) expr f =
- f shifted (-a, 0) xscaled s shifted (a, 0)
+def xscaledpath (expr a, s) =
+ transformedpath (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)
+def yscaledpath (expr b, s) =
+ transformedpath (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)
+
+def slantedpath = xslantedpath enddef;
+def xslantedpath (expr b, s) =
+ transformedpath (shifted (0, -b) slanted s shifted (0, b))
enddef;
-vardef xyswappedpath expr f = f xyswap enddef;
-vardef transformedpath (text Transformer) expr f =
- f Transformer
+def yslantedpath (expr a, s) =
+ transformedpath (shifted (-a, 0) yslant s shifted (0, a))
enddef;
+def shiftedpath (expr v) = transformedpath (shifted v) enddef;
+
+def xyswappedpath = transformedpath (xyswap) 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}.
+% arrowhead on its end.
+%
+% \DescribeRoutine{partialpath}
+% The \gbc{partialpath} 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}. Since the \gbc{gettime} routine was written to
+% find the times for an increasing sequence of lengths, it was optimized
+% to save the index of the previous length and begin from there. Thus it
+% is more efficient to find the smaller of \gbc{a} and \gbc{b} first.
+%
+% Since running \gbc{gettime} would be a very inefficient way to get the
+% first or last point of a path we skip that if either fraction is $0$ or
+% $1$ (a common use is to get the first or last half of a path). We also
+% skip finding the second time if \gbc{a = b} (an unlikely choice, but
+% legal).
+%
+% \DescribeRoutine{gsubpath}
+% \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);
+ save flag, flo, fhi, lo, hi, n;
+ boolean flag; flag = true;
+ convertpath (g) f;
+ n := length f;
+
+ flo := snapto emin(a,b);
+ if flo = 0:
+ lo := 0;
+ elseif flo < 1:
+ setuplengtharray (cum, tot, idx) g;
+ flag := false;
+ lo := gettime (cum, idx) (flo*tot);
+ else:
+ lo := n;
+ fi
+
+ fhi := snapto emax (a,b);
+ if flo = fhi:
+ hi := lo;
+ elseif fhi < 1:
+ if flag: setuplengtharray (cum, tot, idx) g; fi
+ hi := gettime (cum, idx) (fhi*tot);
+ else:
+ hi := n;
+ fi
+
+ if a > b: reverse fi subpath (lo, hi) of f
+enddef;
+
+vardef gsubpath (expr a, b) expr f = subpath (a, b) of f enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{setuplengtharray}
+% This does the frequently repeated saving, rescaling and initializing
+% for those commands that need to convert distance along a path to the
+% corresponding time or point. A path variable should follow, but that
+% is picked up by the \gbc{makelengtharry} at the end.
+% \begin{macrocode}
+def setuplengtharray (suffix cum, tot, idx) =
+ save cum, tot, idx; idx := 0; tot := makelengtharray (cum)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{pathtime}
+% \gbc{pathtime} returns the time \mfc{t} such that \mfc{point t of p} is
+% \gbc{frac} of the distance along \gbc{p} from the start, and
+% \DescribeRoutine{pathpoint}\gbc{pathpoint} returns the point itself.
+% Because the \gbc{gettime} routine requires it anyway, we truncate
+% \gbc{frac} to the interval $[0,1]$ and avoid calling that rather
+% lengthy function at $0$ and $1$.
+%
+% The path in pathtime should be in device coordinates, whereas the
+% user-level command \gbc{pathpoint} expects it in graph coordinates.
+% In fact, since \gbc{pathpoint} would most likely be used in the
+% argument of some figure macro in \mfpic{}, it would require a
+% previously stored path, so we make the path a suffix parameter.
+% \begin{macrocode}
+vardef pathtime@# (suffix p) =
+ if @# <= 0: 0
+ elseif @# >= 1: length p
else:
- tb := gettime (cumlen, idx) (b*totlen);
- ta := gettime (cumlen, idx) (a*totlen);
+ setuplengtharray (cum, tot, idx) p;
+ gettime (cum, idx) (@#*tot)
fi
- subpath (ta, tb) of f
enddef;
-vardef gsubpath (expr a, b) expr f = subpath (a, b) of f enddef;
+
+vardef pathpoint (expr frac) (suffix p) =
+ convertpath (_pp) p; pnt[pathtime[frac] (_pp)] (p)
+enddef;
% \end{macrocode}
%
%
-% \section{Picture level operations}\label{picture}
+% \section{Picture-level Operations}\label{picture}
+%
+% \subsection{Bitwise logical operations}\label{logical}
%
% 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}.
+% for \gbc{picsub}, which occurs only in \gbc{shadepic}. They are all
+% rather wasteful of memory.
%
% \DescribeRoutine{mono}
-% Here we define the bitwise logical operations: and, or, xor, and
+% 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.
+% with weight ${}\le 0$ to 0. It is important to note that we can apply
+% \gbc{mono} only to the suffix parameter in such things as \gbc{orto}.
+% The expression parameter needs to be prepared by the routine that calls
+% these. The return result is culled, so it consists only of 0s and 1s.
% \begin{macrocode}
%<*MF>
-def mono (suffix u) = cull u keeping (1, infinity); enddef;
+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}.
+% \DescribeRoutine{andto, 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}. \gbc{andto} is
+% only used in \gbc{interior} and \gbc{interiors}, \gbc{picand} is not
+% used at all.
% \begin{macrocode}
def andto (suffix u) (expr v) =
- mono (u); addto u also v; cull u keeping (2, 2);
+ 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
+ begingroup setpicture (t) u; andto (t, v); t endgroup
enddef;
% \end{macrocode}
-% \DescribeRoutine{orto}
-% \DescribeRoutine{picor}
+% \DescribeRoutine{orto, 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.
+% is \emph{on} in \gbc{u} or \gbc{v} or both. I've written these so that
+% it doesn't matter if the expression parameter is not mono. It
+% \emph{is} required that it have only positive pixels. The command
+% \gbc{orto} is only used three places: in \gbc{coloraddto}, which
+% is itself never used, and in \gbc{patcharcs} and \gbc{patchrays}. This
+% lack of use is because a less memory intensive version, \gbc{_orto}, is
+% defined later, and that is what we use. Usually we build a picture
+% in a variable \gbc{src} and add that onto another variable \gbc{dest}.
+% If one used \gbc{orto (dest, src)}, then \MF{} would evaluate \gbc{src}
+% and pass a \emph{copy} of it as the parameter of \gbc{orto}. This
+% doubles the memory used, so mostly we use \gbc{_orto}, which passes both
+% parameters as suffixes. \gbc{picor} is never used.
% \begin{macrocode}
def orto (suffix u) (expr v) =
- mono (u); addto u also v; cull u keeping (1, 2);
+ mono (u); addto u also v; cull u keeping (1, infinity);
enddef;
primarydef u picor v =
- begingroup save t; picture t;
- t := u; orto (t, v); t
- endgroup
+ begingroup setpicture (t) u; orto (t, v); t endgroup
enddef;
% \end{macrocode}
-% \DescribeRoutine{xorto}
-% \DescribeRoutine{picxor}
+% \DescribeRoutine{xorto, 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);
+ 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
+ begingroup setpicture (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}.
+% and only if it is \emph{on} in \gbc{u} and off in \gbc{v}. It is
+% unclear whether a \gbc{v} with negative weights will ever occur, but
+% if so, subtracting negative pixels ought to be like adding positive
+% ones, so I've changed \mfc{keeping (1,1)} to \gbc{keeping (1,infinity)}.
+% With this understanding, it doesn't matter here whether \gbc{v} is
+% not mono. As with \gbc{orto}, we have a more memory efficient
+% \gbc{_subto} and now use that everywhere. \gbc{subto} is only used in
+% \gbc{coloraddto}, which is not used anymore. The binop version
+% \DescribeRoutine{picsub}\gbc{picsub} is used only in \gbc{shadepic}.
% \begin{macrocode}
def subto (suffix u) (expr v) =
- mono (u); addto u also -v; cull u keeping (1, 1);
+ mono (u); addto u also -v; cull u keeping (1, infinity);
enddef;
primarydef u picsub v =
- begingroup save t; picture t;
- t := u; mono (t); subto (t, v); t
- endgroup
+ begingroup setpicture (t) u; mono (t); subto (t, v); t endgroup
enddef;
%</MF>
% \end{macrocode}
%
-% \subsection{Producing and modifying pictures}
+% \subsection{Producing and modifying pictures}\label{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
@@ -2031,26 +2536,65 @@ enddef;
% dimension, etc., are in device coordinates.
%
% \DescribeRoutine{coloraddto}
-% This has become a useful abbreviation. In \MF{} it adds when the color
+% This was once 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{}
+% basic \mfc{addto} operation. It was defined only so that \MP{} and \MF{}
% can share the same higher level code.
+%
+% When the last parameter \gbc{v} is the name of picture we can save
+% memory if we pass the name rather than the value. Problems with picture
+% memory turned up in the shading macros for \MF{} and the dashing macros
+% for \MP{}. \DescribeRoutine{coloraddon}\gbc{coloraddon} applies this
+% memory-saving trick and has completely replaced \gbc{coloraddto} in
+% \grafbase{} code. Since \gbc{coloraddto} turned out to be used only with
+% \gbc{u} equal to \gbc{active_plane}, we eliminate that parameter from
+% \gbc{coloraddon}.
+%
+% \DescribeRoutine{_orto}
+% This version of \gbc{orto} saves memory by passing \emph{both}
+% parameters by name. This also allows the application of \gbc{mono} to
+% both parameters. In addition to \gbc{coloraddon}, it is used in
+% \gbc{shade} and \gbc{tess}. \DescribeRoutine{_subto}We also have
+% \gbc{_subto}, an analogous version of \gbc{subto}.
% \begin{macrocode}
def coloraddto (expr clr) (suffix u) (expr v) =
%<*MF>
- if clr < white :
+ if clr < white:
orto (u, v);
else:
- subto (u) (v);
+ 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>def orto (suffix u) (expr v) = addto u also v; enddef;
%<MP>
+def coloraddon (expr clr) (suffix v) =
+%<*MF>
+ if clr < white:
+ _orto (active_plane, v);
+ else:
+ _subto (active_plane, v);
+ fi;
+%</MF>
+%<MP> addto active_plane also v _wc_ clr;
+enddef;
+
+def _orto (suffix u, v) =
+%<MF> mono (u); mono (v);
+ addto u also v;
+%<MF>cull u keeping (1, 2);
+enddef;
+
+%<*MF>
+def _subto (suffix u, v) =
+ mono (u); mono (v); addto u also -v; cull u keeping (1, 1);
+enddef;
+
+%</MF>
% \end{macrocode}
%
% \DescribeRoutine{interior}
@@ -2061,15 +2605,16 @@ enddef;
% 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.
+% We ignore color (new behavior with \mfpic{} version 0.7), since the
+% higher level commands now implement the coloring operations.
% \begin{macrocode}
vardef interior expr c =
- save v; picture v; v := nullpicture;
+ newpicture (v);
addto v contour (c.t_);
-%<MF> cull v dropping (0, 0);
+%<MF> cull v dropping (0,0);
v
enddef;
+
% \end{macrocode}
%
% \DescribeRoutine{interiors}
@@ -2081,7 +2626,7 @@ enddef;
% 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;
+ newpicture (_ints);
for _idx = 1 upto cc:
addto _ints also interior cc[_idx]);
endfor
@@ -2108,16 +2653,18 @@ enddef;
% \gbc{DoClip} command.
% \begin{macrocode}
def clipto (suffix vt) expr c =
+ if path c:
%<MF> andto (vt, interior c);
%<MP> clip vt to c;
+ fi
enddef;
def clipsto (suffix vt, cc) =
%<MF> andto (vt, interiors cc);
%<*MP>
begingroup
- save _cl, _cl_; picture _cl, _cl_; _cl_ := nullpicture;
+ save _cl, _cl_; picture _cl, _cl_; _cl_ := nullpicture;
for _idx = 1 upto cc:
- _cl := vt; clip _cl to cc[_idx]; addto _cl_ also _cl;
+ _cl := vt; clip _cl to cc[_idx]; addto _cl_ also _cl;
endfor
vt := _cl_;
endgroup
@@ -2129,20 +2676,15 @@ enddef;
% \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.
+% to the path. This is not used elsewhere in \grafbase{} nor \mfpic.
%
% 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.
+% changed the name to the uppercase version.
% \begin{macrocode}
vardef Clipped (suffix vt) expr c =
- save _Cl; picture _Cl; _Cl := vt; clipto (_Cl) c; _Cl
+ setpicture (_Cl) vt; clipto (_Cl) c; _Cl
enddef;
-%<MP>let clipped_ = clipped;
-def clipped = Clipped enddef;
-%<MF>def clip = Clipped enddef;
+%<MF>def clip = Clipped enddef;
% \end{macrocode}
%
@@ -2151,21 +2693,23 @@ def clipped = Clipped enddef;
% 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.
+% on top of the \gbc{interior} of the curve colored \gbc{fillcolor}. This
+% is not used elsewhere in \file{grafbase.mp} so it may not be really
+% important whether \gbc{fillcolor} and \mfc{background} are the right
+% choices.
% \begin{macrocode}
vardef picneg (suffix vt) expr c =
%<*MF>
- mono (vt);
- (interior c) picsub vt
+ setpicture (_pn) interior c;
+ _subto (_pn, vt);
%</MF>
%<*MP>
- save _pn; picture _pn; _pn := nullpicture;
- addto _pn (interior c) _wc_ fillcolor;
- addto _pn also (Clipped (vt) c) _wc_ background;
- _pn
+ setpicture (_cl) vt; clip _cl to c;
+ newpicture (_pn);
+ addto _pn also (interior c ) _wc_ fillcolor;
+ addto _pn also _cl _wc_ background;
%</MP>
+ _pn
enddef;
% \end{macrocode}
@@ -2194,8 +2738,8 @@ 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 :
+ newpicture (v);
+ if penwd >= minpenwd:
shpath (v, drawpen) (d);
%<MF> mono (v);
fi
@@ -2215,7 +2759,7 @@ enddef;
def picdot (suffix v) (expr w, p) =
addto v also
%<MP> (w shifted p);
-%<MF> (w shifted hroundpair (p.t_));
+%<MF> (w shifted goodpair (p));
enddef;
% \end{macrocode}
@@ -2229,15 +2773,16 @@ enddef;
% \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.
+% needed. Not in \gbc{shaded} (just below) but later in \gbc{shade} (an
+% older command taking paths in graph coordinates), \gbc{polkadot} and
+% some grid-making commands.
% \begin{macrocode}
vardef setdot (expr apath, sc) =
- if cycle apath : interior
- else : picpath
+ if cycle apath: interior
+ else: picpath
fi
-%<MF> (apath scaled emax(ceiling (sc), minpenwd))
-%<MP> (apath scaled emax(sc, minpenwd))
+%<MF> (apath scaled emax (ceiling (sc), minpenwd))
+%<MP> (apath scaled emax (sc, minpenwd))
enddef;
% \end{macrocode}
@@ -2254,12 +2799,18 @@ enddef;
% (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
+% more gray levels) we allow dots to be rectangles $k\times (k+1)$-pixels
+% (assuming the aspect ratio is 1). 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).
%
+% Why can't we have 64 grey levels in a $8\times 8$ square? Clearly we
+% can in principle turn on any number of the 64 pixels. Unfortunately,
+% spread out patterns (which look best) tend to consume memory, while
+% clumpy patterns are hard to make good-looking. Compensating for aspect
+% ratios unequal to 1 is also pretty hard to do automatically.
+%
% 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
@@ -2267,39 +2818,48 @@ enddef;
% $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.
+% The calculations assume a gray level greater than $1/2$, so the final
+% picture will be mostly white (for darker grays, we use the
+% complementary gray level to construct the `reverse video', and then
+% reverse back). Under this assumption, we concentrate all the black
+% pixels into the lower left and upper right quadrant of the picture we
+% are creating, so most of the calculation determines one of these
+% quadrants. The scratch variables \gbc{_hp} and \gbc{_vp} give the number
+% of horizontal and vertical pixels in the lower left quadrant,
+% \gbc{_dotwd} and \gbc{_dotht} do the same for the actual dot. Then
+% \gbc{_shp} is first set equal to one dot; then a copy of itself is added
+% in the upper right quadrant. Finally, the suffix parameter \gbc{dims} is
+% equated to the nominal width and height of the picture, and either
+% \gbc{_shp} or its reverse is returned.
% \begin{macrocode}
%<*MF>
-numeric shadepicsize; shadepicsize := 0.8bp;
+numeric shadepicsize; shadepicsize := 0.8bp;
vardef shadepic (suffix dims) (expr grparam) =
pair dims;
- save _frac; _frac := 2*emin(grparam, 1 - grparam);
+ setnumeric (_frac) 2*emin (grparam, 1 - grparam);
save _hp, _vp, _dotwd, _dotht;
- if aspect_ratio < 1 :
- _vp := emax (2, hround(shadepicsize.o_));
+ 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)
+ _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)
+ _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));
+ newpicture (_shp);
+ addto _shp contour rect (origin, (_dotwd, _dotht));
picdot (_shp, _shp, dims);
- dims := 2dims; mono (_shp);
- if grparam >= .5 : _shp
- else : (interior (rect ((0,0), dims))) picsub _shp
+ dims := 2dims; mono (_shp);
+ if grparam >= .5: _shp
+ else: (interior (rect (origin, dims))) picsub _shp
fi
enddef;
@@ -2308,78 +2868,83 @@ enddef;
%
% \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
+% \gbc{shadepic}. The routine \gbc{fillwith} 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.
+% \gbc{ur}) is filled with copies of a picture (in this case, the result
+% of \gbc{shadepic}), 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.
+% The \gbc{setbbox} command was defined earlier, in
+% section~\ref{utilities}. 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).
+% We return \gbc{picpath} for non-cycles because I once thought to make
+% \gbc{shaded} a replacement for \gbc{setdot} to get gray dots (in the
+% \gbc{polkadot} routine). That turns out not to work, but this sort of
+% thing is also done in most of the rendering commands that require a
+% closed path.
% \begin{macrocode}
vardef shaded (expr clr) expr c =
- if cycle c :
+ if cycle c:
%<*MP>
- save v; picture v;
- v := nullpicture;
+ newpicture (v);
addto v contour c _wc_ clr;
v
%</MP>
%<*MF>
- if (clr <= black) or (clr >= white) :
+ if (clr <= black) or (clr >= white):
interior c
else:
- save vsh, shpic, shdims, ll, ur;
- picture vsh, shpic; pair shdims, ll, ur;
+ save shdims, shpic;
+ picture shpic; pair shdims;
shpic := shadepic (shdims) (clr);
- gbbox (c, ll, ur);
- vsh := filledwith (shpic) (shdims, ll, ur);
- Clipped (vsh) c
+ setbbox (ll, ur) c;
+ newpicture (vsh);
+ fillwith (vsh) (shpic, shdims, ll, ur);
+ clipto (vsh) c; vsh
fi
%</MF>
- else: picpath c % should we? or just make it null?
+ else: picpath c % should we? or just make it null?
fi
enddef;
% \end{macrocode}
%
-% \DescribeRoutine{filledwith}
+% \DescribeRoutine{fillwith}
% 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.
+% It takes a picture expression \gbc{pic}, 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 draws that
+% rectangle filled with copies of \gbc{pic}. Starting with \mfpic{}
+% version 0.8, it adds to a predefined picture passed by name. Thus the
+% calling routine must make sure that picture is initialized (it need
+% not be \mfc{nullpicture}).
%
% 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.
+% faster (surprisingly much!) to do two separate loops: the second one
+% stacking copies of 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;
+% pixels, while \gbc{fwdims} is in actual pixels. This could have been
+% written 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 fillwith (suffix v) (expr pic, dims, ll, ur) =
+ newpicture (b);
%<*MF>
- save fwdims, _ll, _ur; pair fwdims, _ll, _ur;
- fwdims := hroundpair (dims.t_);
+ save fwdims, _ll, _ur; pair fwdims, _ll, _ur;
+ fwdims := goodpair (dims);
_ll := floorpair (ll.t_);
_ur := ur.t_;
for s = xpart _ll step xpart fwdims until xpart _ur:
@@ -2398,7 +2963,6 @@ vardef filledwith (expr pic, dims, ll, ur) =
addto v also b shifted (0, s);
endfor
%</MP>
- v
enddef;
% \end{macrocode}
@@ -2428,80 +2992,84 @@ enddef;
% 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);
+def thatchf (suffix v) (expr CT, sp, a, b) =
+ begingroup
+ setnumeric (_sp) signof (ypart b - ypart 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);
+ endgroup
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}
+% Tesselations are a type of fill in which a rectangular pattern is
+% repeated throughout a region. The repeated rectangle 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
+% The macro \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{width} and
+% \gbc{height} 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.
+% The tile is enclosed in a group to delimit these changes to the basic
+% drawing parameters. \DescribeRoutine{endtile}\gbc{endtile} merely
+% implements the clipping and then closes the group.
%
% 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}.
+% For tesselation (filling with tiles) we need to know various properties
+% of the tile, so a tile is a composite object consisting of a picture,
+% \gbc{fred.pic} in our example (the actual tile) and a pair
+% \gbc{fred.dims} of the dimensions (in device units). We used to
+% save the \gbc{clipit} parameter in \gbc{atile.clipon}, but it was never
+% used. We also used to have separate numerics \gbc{atile.wd} and
+% \gbc{atile.ht} but they only got used together as a pair.
% \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;
+ picture atile.pic; atile.pic := nullpicture;
+ pair atile.dims;
+%<MF> atile.dims := round ((width, height)*unit);
+%<MP> atile.dims := (width, height)*unit;
begingroup
% \end{macrocode}
-% We simply do a subset of what we do in \gbc{beginmfpic}, redefining
+% We 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.
+% contribute to the tile, and adapting \gbc{ztr} to the tile dimensions.
+% Re also redefine \gbc{xneg}, et al., for the benefit of \gbc{levelset}.
% \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 :
+ def active_plane = atile.pic enddef;
+ save ztr, vtr; transform ztr, vtr;
+ ztr := identity scaled unit; vtr := ztr;
+ save xneg, xpos, yneg, ypos;
+ xneg := 0; xpos := width; yneg := 0; ypos := height;
+% \end{macrocode}
+% To implement \gbc{clipit}, we set the current clipping path array
+% \gbc{ClipPath[\,]} to the boundary of the tile. Note that this turns off
+% user-defined clipping paths, which are unlikely to be correct for the
+% local tile coordinates.
+% \begin{macrocode}
+ save ClipOn; boolean ClipOn;
+ if clipit:
ClipOn := true;
- save ClipPath; path ClipPath[];
- ClipPath = 1;
- ClipPath[1] = rect(origin, (atile.wd, atile.ht));
+ setarray (path) (ClipPath) (rect(origin, atile.dims));
else:
ClipOn := false;
fi
@@ -2514,37 +3082,94 @@ enddef;
% \end{macrocode}
%
% \DescribeRoutine{is_tile}
-% To test whether \gbc{atile} is really a tile, just see if all the
+% To test whether \gbc{atile} is really a tile, just see if the needed
% 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)
+ (known atile.pic ) and (picture atile.pic) and
+ (known atile.dims) and (pair atile.dims )
enddef;
% \end{macrocode}
%
-% \section{Bounding boxes of paths}\label{bboxes}
+% \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.
+% the macro \gbc{getbbox} which takes two pair variable and a path
+% expression, and sets the pairs to the lower left corner and upper right
+% corner, respectively, of a rectangle enclosing the path. 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.
+%
+% \DescribeRoutine{getbbox}
+% 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
+% (or more) before doing that. A calling routine is expected to save and
+% declare the suffixes \gbc{ll} and \gbc{ur}. Within \grafbase{} commands,
+% \gbc{getbbox} is always called by \gbc{setbbox}, which does this.
+%
+% \DescribeRoutine{ctrlsbbox}
+% There is a difference between ``\mfc{postcontrol 0 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{getbbox} passes
+% it half a segment at a time. We don't examine the endpoints of
+% the half-segment: one has already been examined by \gbc{getbbox} and
+% the other (a subdivision point of an original segment) lies on the line
+% segment connecting two control points, and so can't increase the bbox.
+%
+% 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. The \gbc{setsplit} command (subsection~\ref{utilities}) ensures
+% that \gbc{_s} is integral and positive, just in case \gbc{bbox_split}
+% somehow isn't.
+%
+% This description applies only to \MF, because \MP{} has built-in
+% facilities for determining the bounding box.
+%
+% \RoutineIndex{pnt}
+% \RoutineIndex{pre}
+% \RoutineIndex{post}
+% I got tired of typing long expressions like ``\gbc{(precontrol length
+% p of p)}'', and now use the following abbreviations.
+% \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;
+
+def getbbox (suffix ll, ur) expr g =
+%<MP> ll := llcorner g; ur := urcorner g;
+%<*MF>
+ setsplit (_s) bbox_split;
+ ur := ll := pnt 0 (g);
+ 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
+%</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 ( pairmin (ll, post0 (p)), pre 1 (p) );
+ ur := pairmax ( pairmax (ur, post0 (p)), pre 1 (p) );
+enddef;
+
+%</MF>
+% \end{macrocode}
%
% 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
@@ -2554,124 +3179,62 @@ enddef;
% 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.
+% \mfc{.5} (accurate enough, assuming pixel units). 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}.
+% cross the vertical line at \gbc{x}.
+% \DescribeRoutine{ylimit}\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;
+def tightbbox (expr g) (suffix ll, ur) =
+begingroup
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 :
+ 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))) );
+endgroup
+ if showbbox:
noclip ( safedraw rect (ll, ur) );
fi
enddef;
+vardef _xlimit (expr x) =
+ ((x, -infinity)--(x, infinity)) misses g
+enddef;
+
+vardef _ylimit (expr y) =
+ ((-infinity, y)--(infinity, y)) misses g
+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
+% all the lower left. 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).
+% This macro is never used elsewhere in \grafbase.
% \begin{macrocode}
vardef tbbox (suffix g) (suffix ll, ur) =
- save _gll, _gur; pair _gll, _gur;
+ 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);
+ 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));
+ if showbbox: noclip ( safedraw rect (ll, ur) ); fi
enddef;
-%</MF>
+%</unused>
% \end{macrocode}
%
-% \section{Device coordinate rendering
-% commands}\label{basicrendering}
+% \section{Device Coordinate Rendering Commands}\label{basicrendering}
%
-% We use the word rendering to refer to commands that accept a path
+% 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.
@@ -2682,75 +3245,81 @@ enddef;
% \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);
+% \gbc{coloraddon}. In \MP{} it is basically the primitives \mfc{addto}
+% and \mfc{withcolor} applied to \gbc{active_plane}, but in \MF{} it adds
+% when the color is less than 1 (gray or black), otherwise it subtracts
+% (white).
+%
+% \RoutineIndex{colorsafedraw}
+% The command \gbc{safedraw} merely calls \gbc{colorsafedraw}, which then
+% calls \gbc{picpath}, which calls \gbc{shpath}. One reason for this
+% roundabout sequence is to support older files (where \gbc{colorsafedraw}
+% was 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;
+def colorsafedraw (expr clr) expr d =
+ begingroup
+ setpicture (v) picpath d;
+ DoClip (v); coloraddon (clr, v);
+ endgroup
enddef;
% \end{macrocode}
%
% \subsection{Filling}\label{basicfilling}
%
-% \DescribeRoutine{NoCycleWarn}
+% \DescribeRoutine{NoCycle}
% 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
+% (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.
+% \DescribeRoutine{safefill}\RoutineIndex{colorsafefill}
+% The basic \gbc{safefill} simply calls the colored version with the
+% default parameter \gbc{fillcolor}. \gbc{colorsafefill} takes a color as
+% its first parameter and a path expression as second. These commands fill
+% the path 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.
%
+% \DescribeRoutine{safeunfill}
% \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.
+% \gbc{coloraddon} which then subtracts the picture. We do this inside
+% \gbc{noclip}, just because it seems a user would expect clipping only
+% when things are \emph{added}. In \MP{} the white is indeed added, but
+% conceptually, material is cleared away.
% \begin{macrocode}
-def NoCycleWarn expr s =
- GBmsg s & " cannot be applied to an open path. "
- & "The path will be drawn instead.";
+def NoCycle (expr s) expr p =
+ GBwarn s & " cannot be applied to an open path."
+ & " The path will be drawn instead.";
+ safedraw p;
enddef;
-def safefill = colorsafefill (fillcolor) enddef;
+%<MF>vardef isgray (expr X) = (X > black) and (X < white) enddef;
+%<MF>
+def safefill = colorsafefill (fillcolor) enddef;
vardef colorsafefill (expr clr) expr c =
- if cycle c :
- save v; picture v; v := interior c;
+ if cycle c:
+ setpicture (v) interior c;
DoClip (v);
%<*MF>
- if (clr > black) and (clr < white) :
- subto (active_plane) (v);
+ if isgray (clr):
+ _subto (active_plane) (v);
v := nullpicture;
v := shaded (clr) c;
fi
%</MF>
- coloraddto (clr) (active_plane, v);
- else: NoCycleWarn "fill"; safedraw c;
+ coloraddon (clr, v);
+ else: NoCycle("fill") c;
fi
enddef;
def safeunfill expr c =
- if cycle c : noclip (colorsafefill (background) c);
- else: NoCycleWarn "unfill"; safedraw c;
+ if cycle c: noclip (colorsafefill (background) c);
+ else: NoCycle("unfill") c;
fi
enddef;
@@ -2764,14 +3333,14 @@ enddef;
% will \gbc{safedraw} non-cyclic paths.
% \begin{macrocode}
def safeclip expr c =
- if cycle c : clipto (active_plane) c;
- else: NoCycleWarn "clip"; safedraw c;
+ if cycle c: clipto (active_plane) c;
+ else: NoCycle("clip") c;
fi
enddef;
% \end{macrocode}
%
-% \section{Rendering: the highest level commands}\label{rendering}
+% \section{Graph Coordinate Rendering}\label{rendering}
%
% \DescribeRoutine{store}
% Now we come to the highest level rendering operations. These are the
@@ -2784,7 +3353,7 @@ enddef;
% 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.
+% and the rendering is `merely' a side-effect.
%
% \DescribeRoutine{stored}
% The macro \gbc{stored} performs \gbc{store}, but passes the same path as
@@ -2795,56 +3364,194 @@ enddef;
% 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")
- "";
+hide (
+ if (not path f) and (not pair f):
+ GBerrmsg ("improper expression type.")
+ "The second argument to `store' must be a path or pair.";
fi
- if not path fs : path fs; fi
+ if not path fs: path fs; fi
fs := f
)
enddef;
-vardef stored (suffix fs) expr f = store (fs) f; 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}.
+% \DescribeRoutine{drawn}\RoutineIndex{colordrawn}
+% The command \gbc{drawn} merely calls \gbc{colordrawn} with the default
+% color \gbc{drawcolor}. Then \gbc{colordrawn} takes a color \gbc{clr}
+% and a path expression \gbc{f} and returns the same path. In between,
+% \gbc{zconv (f)} is subjected to \gbc{colorsafedraw}.
% \begin{macrocode}
-def drawn = colordrawn (drawcolor) enddef;
+def drawn = colordrawn (drawcolor) enddef;
vardef colordrawn (expr clr) expr f =
- colorsafedraw (clr) (zconv (f)); f
+ colorsafedraw (clr) (zconv (f)); f
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{colorwiggle}
+% This is a multi-tasking command that can draw either zigzag or
+% sinewave shapes depending on the boolean first parameter. For \mfc{true}
+% we get smooth wiggles, for \mfc{false} we get jagged ones. In the
+% smooth case, a tension parameter allows an adjustment to the smoothness.
+% The command \DescribeRoutine{zigzag}\RoutineIndex{colorzigzag}
+% \gbc{zigzag} calls it with the value \mfc{false} and an arbitrary
+% value of the tension;
+% \DescribeRoutine{sinewave}\RoutineIndex{colorsinewave}\gbc{sinewave}
+% calls it with \mfc{true}, allowing it to pick up the tension parameter.
+% All expect a quadruple of dimensions to follow
+%
+% The reason for using a loop (at the end) that draws the \gbc{sinewave}
+% path in pieces, is that all the turning can quickly exceed \MF{}'s limit
+% on the autorounding stack. I'd never heard of this stack until I ran
+% this without a loop and received the ``capacity exceeded'' message. This
+% turns out to be a problem mostly when the ratio of \gbc{len} to
+% \gbc{wid} is too small and the `humps' of the sine are more like
+% `bulbs'. However it is always a problem with \gbc{corkscrew} (below).
+% \begin{macrocode}
+def zigzag = colorzigzag (drawcolor) enddef;
+def colorzigzag (expr clr) = colorwiggle (false, clr, 0) enddef;
+def sinewave = colorsinewave (drawcolor) enddef;
+def colorsinewave = colorwiggle (true) enddef;
+
+vardef colorwiggle (expr smth, clr, tens, blen, elen, len, wid) expr f =
+ convertpath (g) f;
+ setuplengtharray (cumlen, totlen, ct) g;
+ save B;
+ if cycle f:
+ B := 0;
+ else:
+ B := abs(blen)/_rescale_factor;
+ totlen := totlen - B - abs(elen)/_rescale_factor;
+ fi
+ setnumeric (n) 2*round (totlen/len*_rescale_factor);
+ if n < 2:
+ colorsafedraw (clr) g;
+ else:
+ save T, U, X, Y, Z, p;
+ pair U, X, Y, Z; path p;
+ T := if cycle f: 0 else: gettime (cumlen, ct) (B) fi;
+ Z := pnt[T] (g);
+ p :=if not cycle f: (subpath (0,T) of g)
+ if smth: {curl 0} ..tension tens.. else: -- fi
+ fi
+ for i = 1 upto n:
+ hide(
+ T := gettime (cumlen, ct) (B+(i/n)*totlen);
+ X := Z; Z := pnt[T] (g);
+ Y := .5[X,Z]; U := sgn (Z-X);
+ )
+ (Y + (U zscaled (0, if even i: - fi wid)))
+ if smth: {U}..tension tens.. else: -- fi
+ endfor
+ if cycle f: cycle
+ else: if smth: {curl 0} fi (subpath (T, length g) of g)
+ fi;
+ newpicture (v);
+%<MP> shpath (v, drawpen) (p);
+%<*MF>
+ if smth:
+ save n, k;
+ n := length p; k = n div 50;
+ for i = 0 step 50 until 50*(k-1):
+ shpath (v, drawpen) (subpath (i,i+50) of p);
+ endfor
+ shpath (v, drawpen) (subpath (50k,n) of p);
+ else: shpath (v, drawpen) (p);
+ fi
+%</MF>
+ DoClip(v); coloraddon (clr, v);
+ fi
+ f
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{corkscrew}\RoutineIndex{colorcorkscrew}
+% This shares a lot of code with \gbc{zigzag} and \gbc{sinewave}, but
+% the middle is considerably different, so it is not really possible to
+% make a multipurpose command that can do all three.
+% \begin{macrocode}
+def corkscrew = colorcorkscrew (drawcolor) enddef;
+vardef colorcorkscrew (expr clr, tens, blen, elen, len, wid) expr f =
+ convertpath (g) f;
+ setuplengtharray (cumlen, totlen, ct) g;
+ save B;
+ if cycle f:
+ B := 0;
+ else:
+ B := abs(blen)/_rescale_factor;
+ totlen := totlen - B - abs(elen)/_rescale_factor;
+ fi
+ setnumeric (n) round (totlen/len*_rescale_factor);
+ if n < 2:
+ colorsafedraw (clr) g;
+ else:
+ save T, U, X, Y, Z, p;
+ pair U, X, Y, Z; path p;
+ T := if cycle f: 0 else: gettime (cumlen, ct) (B) fi;
+ Z := pnt[T] (g);
+ p :=if (not cycle f) and (B > 0): (subpath (0,T) of g)-- fi
+ for i = 1 upto n:
+ hide(
+ T := gettime (cumlen, ct) (B+(i/n)*totlen);
+ X := Z; Z := pnt[T] (g);
+ Y := .5[X,Z]; U := sgn (Z-X);
+ )
+ (X + (U zscaled (0,-wid))){ U}..tension tens..
+ (Y + (U zscaled (0, wid))){-U}..tension tens..
+ endfor
+ if cycle f: cycle
+ else:
+ {U}(Z + (U zscaled (0,-wid)))
+ if elen <> 0: --(subpath(T, length g) of g) fi
+ fi;
+ newpicture (v);
+%<MP> shpath (v, drawpen) (p);
+%<*MF>
+ save n, k;
+ n := length p; k = n div 50;
+ for i = 0 step 50 until 50*(k-1):
+ shpath (v, drawpen) (subpath (i,i+50) of p);
+ endfor
+ shpath (v, drawpen) (subpath (50k,n) of p);
+%</MF>
+ DoClip(v); coloraddon (clr, v);
+ fi
+ 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.
+% \DescribeRoutine{filled}\RoutineIndex{colorfilled}
+% The command \gbc{filled} calls \gbc{colorfilled} with the default color
+% \gbc{fillcolor}. Then \gbc{colorfilled} takes a color \gbc{clr} and a
+% path expression \gbc{c}, returning the same path after subjecting
+% \gbc{zconv (c)} to \gbc{colorsafefill}.
+% \DescribeRoutine{unfilled}\gbc{unfilled} returns the path after running
+% \gbc{safeunfill}.
%
+% \DescribeRoutine{Clip}
+% Finally, \gbc{Clip} is similar, running \gbc{safeclip}. The name
+% \gbc{clip} (lowercase) is taken: it is a \MP{} primitive.
% \begin{macrocode}
-def filled = colorfilled (fillcolor) enddef;
+def filled = colorfilled (fillcolor) enddef;
vardef colorfilled (expr clr) expr c =
- colorsafefill (clr) zconv (c); 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;
+vardef unfilled expr c = safeunfill zconv (c); c enddef;
+vardef Clip expr c = safeclip zconv (c); c enddef;
% \end{macrocode}
%
% \subsection{Shading}\label{shading}
%
+% \DescribeRoutine{shade}
% 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
@@ -2861,70 +3568,70 @@ vardef Clip expr c = safeclip zconv(c); c enddef;
% \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 shape and size of the dots 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.
+% spacing is too small relative to \gbc{shadewd}, we fill the curve.
+% Otherwise the \gbc{fillwith} macro is used to fill with copies of
+% a dot picture.
% \begin{macrocode}
-numeric shadewd; shadewd := 0.5bp;
+numeric shadewd; shadewd := 0.5bp;
path shadedotpath;
-shadedotpath := fullcircle; % unitsquare;
+shadedotpath := fullcircle;
vardef shade (expr sp) expr f =
- save g; path g; g := zconv (f);
+ convertpath (g) 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;
+% \MF{} and \MP{} produce similar levels of gray on both printers
+% available to me.
+% \begin{macrocode}
+ setnumeric (gr) 1 - (.88*abs(shadewd)/sp)**2;
+ if not cycle g: NoCycle("shade") g;
+ elseif gr <= 0: safefill g;
else:
%<*MF>
- save ll, ur; pair ll, ur;
- gbbox (g, ll, ur);
+ setbbox (ll, ur) g;
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}?
+% We call \gbc{fillwith} to draw these copies. We have to produce this
+% on a picture separate from \gbc{active_plane} so we can apply
+% \gbc{DoClip}. Adding one complex picture onto another means there is a
+% time at which the added picture and the result are in memory at the same
+% time. To save a little memory (at the cost of a little speed), we
+% divide the picture into two, each with half the complexity. We add the
+% one, then (I hope) recover the memory by equating it to \mfc{nullpicture}
+% before adding the second.
%
% 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
+% ensured by \gbc{fillwith}, using \mfc{ceiling} to define \gbc{dv} 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_);
+ % setpair (dv) ceiling (sp/(sqrt 2))*(1,1);
+ % test hex spacing:
+ setpair (dv) ( ceiling(.5sp), ceiling(.5sp*sqrt 3) );
+ setpicture (sh) setdot (shadedotpath, abs(shadewd));
+ newpicture (v);
+ fillwith (v) (sh, 2dv, ll, ur);
+ newpicture (w);
+ addto w also v shifted goodpair (dv);
+ DoClip (v); DoClip (w);
+ clipto (v) (g); clipto (w) (g);
+ _orto (active_plane, v);
v := nullpicture;
- v := filledwith (sh, 2(dx, dx), ll, ur);
- DoClip(v);
- orto (active_plane, Clipped (v) g);
+ _orto (active_plane, w);
%</MF>
% \end{macrocode}
% In \MP{} we just fill with gray. The gray level having been calculated
@@ -2955,61 +3662,61 @@ enddef;
%
% 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}.
+% and tiny paths don't rasterize at all well in \PS. 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;
+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) :
+ convertpath (g) f;
+ if not cycle g: NoCycle("polkadot") g;
+ elseif sp <= emax (2*polkadotwd/3, mindotspace):
safefill g;
else:
- save ll, ur; pair ll, ur;
- gbbox (g, ll, ur);
+ setbbox (ll, ur) g;
% \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.
+% dot centers. The vertical shift is slightly larger (relatively speaking)
+% and the horizontal smaller. We apply a further 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;
+ save dx, dy, dshift; pair dshift;
+ dx := sp/2; dy := dx*sqrt 3;
+ dshift := (xpart(ur - ll) mod dx, 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);
+ save p, dims; pair p, dims;
+ p := ll + dshift;
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;
+% are gray dots. And then to `gray' the dots when \gbc{fillcolor} demands
+% it.
+% \begin{macrocode}
+ setpicture (thepolkadot) setdot (polkadotpath, polkadotwd);
+ newpicture (v);
+ fillwith (v) (thepolkadot, dims, p, ur);
+ fillwith (v) (thepolkadot, dims, p + (dx, dy), ur);
+ DoClip (v); clipto (v) g;
%<*MF>
- if (fillcolor > black) and (fillcolor < white): % gray
- subto (active_plane) (v);
+ if isgray (fillcolor):
+ _subto (active_plane) (v);
+ v := nullpicture;
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;
+ shaded (fillcolor) polkadotpath scaled ceiling (polkadotwd);
+ fillwith (v) (thepolkadot, dims, p, ur);
+ fillwith (v) (thepolkadot, dims, p + (dx, dy), ur);
+ DoClip (v); clipto (v) g;
fi
%</MF>
- coloraddto (fillcolor) (active_plane) (v);
+ coloraddon (fillcolor, v);
fi
f
enddef;
@@ -3018,8 +3725,7 @@ enddef;
%
% \subsection{Hatching}\label{hatching}
%
-% \DescribeRoutine{thatch}
-% \DescribeRoutine{colorthatch}
+% \DescribeRoutine{thatch}\RoutineIndex{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
@@ -3031,52 +3737,49 @@ enddef;
% 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;
+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;
+ convertpath (g) f;
+ if not cycle g: NoCycle("hatch") 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);
+ newpicture (v);
+ setbbox (ll, ur) g rotated -theta;
+ thatchf (v, identity rotated theta, sp, ll, ur);
+ DoClip (v); clipto (v) (g);
+ coloraddon (clr, v);
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}.
+% expression (in graph coordinates) as parameters.\\
+% \DescribeRoutine{hhatch}\gbc{hhatch} has angle 0 and so produces
+% horizontal lines;\\
+% \DescribeRoutine{vhatch}\gbc{vhatch} produces vertical lines;\\
+% \DescribeRoutine{lhatch}\gbc{lhatch} produces lines tilted to the
+% left (running from upper left to lower right);\\
+% \DescribeRoutine{rhatch}\gbc{rhatch} produces lines running from lower
+% left to upper right; and\\
+% \DescribeRoutine{xhatch}\gbc{xhatch} produces
+% cross-hatching, and essentially runs \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 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
+def xhatch = colorxhatch (hatchcolor) enddef;
+def colorxhatch (expr clr, sp) =
+ colorthatch (clr) (sp, 45) colorthatch (clr) (sp, -45)
enddef;
% \end{macrocode}
@@ -3084,48 +3787,52 @@ enddef;
% \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}
+% Tesselation of the interior of a closed path means filling with copies
+% of a \emph{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 and
+% \gbc{fred.dims} is a pair giving its dimensions.
+% \begin{macrocode}
+vardef NoTile (suffix atile) expr g =
+ GBwarn str atile & " is not a valid tile for tess()."
+ & " The path will be drawn instead.";
+ safedraw g;
+enddef;
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;
+ convertpath (_g) c;
+ if not cycle _g: NoCycle("tess") _g;
+ elseif not is_tile (atile): NoTile (atile) _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);
+ setbbox (_ll, _ur) _g;
+ newpicture (_ts);
+ fillwith (_ts) (atile.pic, atile.dims, _ll, _ur);
+ DoClip (_ts); clipto (_ts) _g;
+ _orto (active_plane, _ts);
fi
c
enddef;
% \end{macrocode}
%
-% \subsection{Dots and Dashes}\label{dashes}
+% \subsection{Dots and dashes}\label{dashes}
%
-% \MP{} has some builtin commands for drawing a dashed or dotted curve,
+% \MP{} already has 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
+% (before \MP{} even existed). The code is now reasonably fast and the
+% result is actually better quality than \MP{}'s native commands so we use
+% the same code in both versions. It does, however, use pretty much
+% memory in \MP{}.
+%
+% 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). \MP{}'s own 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.
%
@@ -3139,25 +3846,25 @@ enddef;
%
% 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
+% approximate length of a curve. This is needed to make adjustments in the
+% length of dashes and spaces 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
+% And \gbc{_rescale_factor} 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
+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 _rescale_factor: _rescale_factor := 0.1in; fi
% \end{macrocode}
%
@@ -3173,85 +3880,96 @@ if unknown unit_of_length : unit_of_length := 0.1in; fi
% 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.
+% any patterns, not necessarily related to \gbc{pat.rep}. Also `dots' can be
+% symbols like \gbc{Triangle}.
+%
+% When we tried to deal with arrays of dashing patterns, it became
+% rather a pain to deal with three arrays of arrays. So now we allow the
+% suffix \gbc{pat} to be a single array and call \gbc{mkdasharrays} to
+% produce \gbc{pat.start} and \gbc{pat.finish}. It returns \gbc{true} if
+% all three arrays are successfully produced.
+%
+% The variable \VariableIndex{last_dot_size} \gbc{last_dot_size} is
+% intended to allow the clearing path of arrowhead commands to encompass
+% a final dot larger than \gbc{penwd}.
% \begin{macrocode}
+numeric last_dot_size; last_dot_size := 0;
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.";
+ convertpath (_g) f;
+ save _dpat;
+ if not mkdasharrays (pat) (_dpat):
+ GBwarn "Dash pattern " & str pat
+ & " undefined. Path will be drawn instead.";
safedraw _g;
- elseif pat.rep < 2 : % no "spaces"
+ elseif _dpat.rep < 2:
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.
+% After the following loop, \gbc{_dl.s} is the total length of the
+% corresponding \gbc{pat.s} in multiples of \gbc{_rescale_factor}, and
+% \gbc{_dpat.s[i]} has been 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];
+ save _dl;
+ forsuffixes _s = start, rep, finish:
+ _dl._s := 0;
+ for i = 1 upto _dpat._s:
+ _dpat._s[i] := _dpat._s[i]/_rescale_factor;
+ _dl._s := _dl._s + _dpat._s[i];
endfor
endfor
- if _dl.rep = 0 :
- GBmsg "Dash pattern " & str pat & " has length 0. " &
- "Path will be drawn instead.";
+
+ if _dl.rep = 0:
+ GBwarn "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}.
+% Here \gbc{_g} is our path in device units, but \gbc{setuplengtharray}
+% computes lengths in multiples of \gbc{_rescale_factor} 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
+% of \gbc{f}, we consider the polygon connecting the points $f(0), f(1/s),
+% f(2/s),\ldots,f(k)$ (where $s$ is \gbc{segment_split}) and compute the
+% length of \emph{that} path. 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.
+% determine `when' (i.e., at what values of $t$) to place a dot or draw
+% a dash. The command \gbc{setuplengtharray} does this, storing the
+% cumulative lengths in the array \gbc{_cumlen} and the total in
+% \gbc{_totlen}. It also initializes \gbc{_ct} the index into that array.
% \begin{macrocode}
- save _p; path _p;
- _p := _g scaled (1/unit_of_length);
- save _cumlen, _totlen, _n, _sf;
- _totlen := makelengtharray(_cumlen) _p;
+ setuplengtharray (_cumlen, _totlen, _ct) _g;
% \end{macrocode}
-% Now we scale the dashes so that a whole number of patterns make up
+% Now we adjust 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}).
+% \gbc{pat.rep} to use. If the path length is too small compared to 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
+% After this we rescale the dashes and spaces stored in \gbc{_dpat}, and
% the length of the patterns in \gbc{_dl}.
% \begin{macrocode}
- _sf := scale_adjust (_n, _dl)(_totlen);
- if _n < 0 : safedraw _g;
+ save _n, _sf, _no_dots;
+ boolean _no_dots; _no_dots := true;
+ _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;
+ forsuffixes _s = start, rep, finish:
+ for _i = 1 upto _dpat._s:
+ if (_dpat._s[_i] = 0) and _no_dots: _no_dots := false;
+ else: _dpat._s[_i] := _dpat._s[_i]*_sf;
+ fi
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
+% a dot by defining \gbc{plot_pic} (and preferably also storing its
+% diameter in \gbc{plot_pic.size}). 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.
@@ -3259,9 +3977,15 @@ vardef gendashed (suffix pat) expr f =
% 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);
+ if _no_dots:
+ else:
+ if unknown plot_pic:
+ save plot_pic; path plot_pic;
+ plot_pic := dotpath;
+ fi;
+ last_dot_size :=
+ if known plot_pic.size: plot_pic.size else: penwd fi;
+ setpicture (dashingdot) makesymbol (plot_pic, last_dot_size);
fi
% \end{macrocode}
% The macro \gbc{dashit} draws the dashes, computing where they go and
@@ -3277,56 +4001,52 @@ vardef gendashed (suffix pat) expr f =
% 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}.
+% \gbc{_ct} as the current index into that array.
+% \begin{macrocode}
+ save _t, _d, _v;
+ picture _v; _v := nullpicture;
+ _d0 := 0; _t0 := 0;
+ dashit (_dpat.start) (_v);
+
+% \end{macrocode}
+% 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.
% 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.
+% and add it once with \gbc{coloraddon} 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 :
+% Now we use a standard programming trick: accumulate $m < n$ repetitions
+% before adding them, the memory should be $O(m)$ and the time $O(n/m)$.
+% Making $m$ about $\sqrt n$ seems to work well.
+% \begin{macrocode}
+ 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);
+ _t0 := gettime (_cumlen, _ct) (_d0);
+ dashit (_dpat.rep) (_v);
endfor
- % add _m patterns and reset.
- DoClip(_v);
-%<MF> mono (_v)
- coloraddto (drawcolor) (active_plane, _v);
+ DoClip (_v);
+ coloraddon (drawcolor, _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);
+ _t0 := gettime (_cumlen, _ct) (_d0);
+ dashit (_dpat.finish) (_v);
+ DoClip (_v);
+ coloraddon (drawcolor, _v);
fi
fi
fi
@@ -3336,15 +4056,24 @@ 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));
+% This takes an array name and a path expression (which is assumed to be
+% in device coordinates), computes the array of partial lengths (of the
+% polygon approximation), and returns the total length. To avoid numeric
+% overflow we rescale the lengths and so the array elements are in units
+% of \gbc{_rescale_factor}. At one point we used to rescale the path,
+% but that turned out to be unnecessary and made it harder to accomplish
+% several of the things we now do with this. We also save a little
+% memory by making the path a suffix parameter, which avoids the memory
+% used for the `capsule' of an expression parameter. So far all uses apply
+% it to a path variable and we have to remember to keep it that way.
+% \begin{macrocode}
+vardef makelengtharray (suffix clen) suffix p =
+ setsplit (_s) segment_split;
+ numeric clen[];
+ clen := _s * length p; clen0 := 0;
+ for _i = 1 upto clen:
+ clen[_i] := clen[_i-1] + abs (pnt[_i/_s] (p) - pnt[(_i-1)/_s] (p)) /
+ _rescale_factor;
endfor
clen[clen]
enddef;
@@ -3353,7 +4082,7 @@ enddef;
%
% \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
+% \gbc{pl.\{start\|rep\|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
@@ -3363,8 +4092,8 @@ enddef;
% \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)
+ n := if n < 0: -1 else: round(n) fi;
+ lngth/(pl.start + emax (n, 0)*pl.rep + pl.finish)
enddef;
% \end{macrocode}
@@ -3386,21 +4115,22 @@ enddef;
%
% 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}.
+% \gbc{ct+1}. Note: in the \mfc{forever} loop, the exit must come before
+% the increment.
% \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
+ setnumeric (_gtl) emax (arr[ct], emin (arr[arr], lngth));
+ setsplit (_s) segment_split;
+ forever: exitif ( (arr[ct] <= _gtl) and (_gtl <= arr[ct+1]) );
+ next ct;
endfor
- if arr[ct] = arr[ct+1] : ct
- else: ( ct + (_gtl - arr[ct]) / (arr[ct+1] - arr[ct]) )
- fi /_s
+ if arr[ct] = arr[ct+1]: ct
+ else: ( ct + (_gtl - arr[ct]) / (arr[ct+1] - arr[ct]) )
+ fi /_s
enddef;
+def next suffix X = X := X + 1; enddef;
+
% \end{macrocode}
%
% \DescribeRoutine{dashit}
@@ -3414,7 +4144,7 @@ enddef;
% 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
+% the subpath between those 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
@@ -3423,18 +4153,18 @@ enddef;
% \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;
+ if odd _k:
+ if pos[_k] = 0:
+ _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:
+ else:
_d0 := _d1 + pos[_k];
- _t0 := gettime(_cumlen, _ct) (_d0);
+ _t0 := gettime (_cumlen, _ct) (_d0);
fi
endfor
enddef;
@@ -3442,55 +4172,77 @@ 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}.
+% This is a utility to convert a list of lengths to an array. It is almost
+% the same as \gbc{list}, but it does make sure the array functions as
+% a dashing pattern. It needs to consist of either $1$ item (the signal to
+% draw a solid line) or an even number of items. So we add a zero length
+% term if the size is $0$ or odd and bigger than $1$.
+%
+% \DescribeRoutine{mkdasharrays}
+% The dashing code in \gbc{gendashed} is written so the the beginning and
+% ending patterns can be different from the repeating patterns. If so,
+% they must be named \gbc{pat.start}, \gbc{pat.rep} and \gbc{pat.finish}.
+% If one of these three is not a known array but \gbc{pat} is, this macro
+% creates the array in a generic way. When created this way, \gbc{pat.rep}
+% is a copy of \gbc{pat}. So is \gbc{pat.start}, except only a fraction of
+% the first dash is used, while \gbc{pat.finish} is just the first dash of
+% \gbc{pat} 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;
+ list (pat) (t);
+ if (pat = 0) or (odd (pat) and (pat > 1)):
+ pat[incr pat] := 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];
+enddef;
+
+vardef mkdasharrays (suffix src, dest) =
+ save _bad; boolean _bad; _bad := false;
+ forsuffixes _s = start, rep, finish:
+ numeric dest._s, dest._s[];
+ boolean _bad._s;
+ if knownnumericarray src._s:
+ copyarray (src._s) (dest._s);
+ _bad._s := false;
+ else:
+ _bad := _bad._s := true;
+ fi
endfor
- pat.finish := 1;
- pat.finish[1] := pat.rep[1]*dash_finish;
+ % _bad = one of the three arrays not copied.
+ if _bad:
+ if knownnumericarray src: _bad := false;
+ if _bad.rep: % make dest.rep = src
+ copyarray (src) (dest.rep);
+ fi
+ if _bad.start: % shrink first dash to get dest.start
+ copyarray (src) (dest.start);
+ dest.start1 := dash_start*src1;
+ fi
+ if _bad.finish: % use partial first dash for dest.finish
+ dest.finish := 1;
+ dest.finish1 := dash_finish*src1;
+ fi
+ fi
+ fi
+ not _bad
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_}.
+%
+% The \mfpic{} command \cs{dashed} is now implemented by making a
+% dashpattern from the two arguments and calling gendashed. That is the
+% definition of \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);
+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;
+def DASHED = Dashed enddef;
+%<MF> def dashed = Dashed enddef;
% \end{macrocode}
%
@@ -3511,42 +4263,87 @@ def dashed = DASHED enddef;
% 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.
+% 0 length, the signal that dots are to be used. \DescribeRoutine{dotted}
+% \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);
+ save dots; dashpat (dots) (0, dgap);
+ setpicture (plot_pic) makesymbol (spath, sc);
+ plot_pic.size := sc;
gendashed (dots) f
enddef;
-path dotpath; dotpath := fullcircle;
-vardef dotted (expr dsize, dgap) expr f =
- doplot (dotpath, dsize, dgap) f
-enddef;
+path dotpath; dotpath := fullcircle;
+def dotted = doplot (dotpath) 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.
+% These are useful little utilities to draw the points on top of the
+% curve through them. \gbc{plotnodes} 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.
+%
+% \DescribeRoutine{showcontrols}
+% \gbc{showcontrols} was mainly for debugging; it draws a line segment
+% connecting the control points of each node. It optionally draws a symbol
+% at each control point. We use \mfc{0} for a `symbol' to indicate we
+% don't wish to draw a symbol there. The default color for both the
+% symbols and the segment is \gbc{pointcolor}.
% \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);
+ if size > 0:
+ 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);
+ fi
+ f
+enddef;
+
+def showcontrols = colorshowcontrols (pointcolor) enddef;
+vardef colorshowcontrols (expr clr, syma, symb, size) expr f =
+ save shpre, shpost;
+ pair shpre[], shpost[];
+ shpre := 0; shpost := 0;
+ for a = 0 upto (length f) if cycle f: - 1 fi:
+ shpre [incr shpre] := pre [a] (f);
+ shpost[incr shpost] := post[a] (f);
+ colorsafedraw (clr)
+ (zconv (shpre[shpre]--pnt[a](f)--shpost[shpost]));
endfor
- dosymbols (drawcolor, symbol, size) (_pln);
+ if size > 0:
+ if not numeric syma: dosymbols (clr, syma, size) (shpre) ; fi
+ if not numeric symb: dosymbols (clr, symb, size) (shpost); fi
+ fi
+ f
+enddef;
+
+% \end{macrocode}
+% \subsection{Double-line drawing}\label{doubleline}
+%
+% \DescribeRoutine{doubledraw}
+% This invokes \gbc{parapath}, which has issues with smooth but wiggly
+% paths. An easier approach would be to draw a wide line and erase a
+% narrow one in the middle. However, that would not be transparent in
+% \MP{}.
+% \begin{macrocode}
+def doubledraw = colordoubledraw (drawcolor) enddef;
+vardef colordoubledraw (expr clr, sep) expr f =
+ convertpath (g) f;
+ colorsafedraw (clr) (parapath ( sep/2) g);
+ colorsafedraw (clr) (parapath (-sep/2) g);
f
enddef;
% \end{macrocode}
%
+% \section{Points Symbols and Other Pictures}\label{symbols}
+%
% \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
@@ -3555,266 +4352,36 @@ enddef;
% \begin{macrocode}
%<*MP>
vardef centerit (expr pic) =
- pic shifted -(0.5[urcorner pic, llcorner 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.
+% 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.
+% other paths, a drawing of 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>
+ if picture spath :
+%<MF> setpicture (v) spath; mono (v); v
+%<MP> spath
+ elseif path spath: setdot (spath, sc)
+%<MP> elseif string spath:
+%<MP> spath infont defaultfont scaled defaultscale
else:
- GBmsg "Undefined symbol for plotting, using dotpath instead.";
+ GBwarn "Undefined symbol for plotting, "
+ & "dotpath will be used 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.
@@ -3823,11 +4390,12 @@ defaultcolors;
% \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.
+% it anywhere in \grafbase{} anymore.
% \begin{macrocode}
vardef bpoint (expr ptwd, b) =
fullcircle scaled ptwd shifted b
enddef;
+
% \end{macrocode}
%
% \DescribeRoutine{pointd}
@@ -3836,14 +4404,14 @@ enddef;
% 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)}.
+% drawing it) and call \gbc{plotsymbol (Circle)}.
% \begin{macrocode}
def pointd (expr ptwd, filled) (text t) =
- if filled :
+ if filled:
plotsymbol (SolidCircle, ptwd) (t);
- else :
+ else:
begingroup;
- save clearsymbols; boolean clearsymbols; clearsymbols := true;
+ setboolean (clearsymbols) true;
plotsymbol (Circle, ptwd) (t);
endgroup
fi
@@ -3851,8 +4419,7 @@ enddef;
% \end{macrocode}
%
-% \DescribeRoutine{plotsymbol}
-% \DescribeRoutine{colorplotsymbol}
+% \DescribeRoutine{plotsymbol}\RoutineIndex{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
@@ -3866,435 +4433,102 @@ enddef;
% 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.
+% We copy the text list to an array and call \gbc{dosymbols} so that
+% \gbc{plotnodes}, \gbc{plotsymbol} and \gbc{showcontrols} can share the
+% code.
%
% \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}
+% \gbc{dosymbols} uses identical code twice (once to clear, once to draw),
+% so we put that code in \gbc{addsymbols}. \DescribeRoutine{addsymbols}
+% And finally, \gbc{addsymbols} draws copies of a symbol at a given array
+% of points with a given color.
% \begin{macrocode}
-boolean clearsymbols; clearsymbols := false;
+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
+ if path pth:
+ ( pnt0 (pth) = pnt[length pth] (pth) ) and (not cycle pth)
+ and (length pth > 0)
+ else: false
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];
+def clearopenpath expr f =
+ if clearable (f): safeunfill f & cycle; fi
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
+def plotsymbol = colorplotsymbol (pointcolor) enddef;
+def colorplotsymbol (expr clr, spath, sc) (text t) =
+ if sc > 0:
+ begingroup
+ setpairs (_cpls) (t);
+ if _cpls > 0: dosymbols (clr, spath, sc) (_cpls); fi
+ endgroup
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
+def dosymbols (expr clr, spath, sc) (suffix arr) =
+ if clearsymbols and clearable (spath):
+ addsymbols (background, makesymbol (spath&cycle, sc)) (arr);
fi
+ addsymbols (clr, makesymbol (spath, sc)) (arr);
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;
+def addsymbols (expr clr, symb) (suffix arr) =
+ newpicture (_pls);
+ for _idx = 1 upto arr:
+ picdot (_pls, symb, zconv (arr[_idx]));
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
+ DoClip (_pls); coloraddon (clr, _pls);
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.
+% \DescribeRoutine{putimage}
+% This is designed to allow \mfpic{} users to make a picture (created with
+% \cs{mfpimage} or \cs{tile} perhaps), and put a copy at several
+% locations. This allows more complex things than \cs{plotsymbol} and
+% more flexibility than \cs{tess}. As the picture is should be completely
+% prepared in advance, there is no color or size parameter. Moreover,
+% \gbc{pic} might be quite complex, so we don't use \gbc{picdot} which
+% takes the added picture as an expression, but rather repeat its code.
+% The indirection of adding to \gbc{_pti} is normal when we want to
+% respect clipping without clipping what is already drawn. Saving memory
+% at some sacrifice of speed, we clip and reset with each addition. (It is
+% uncertain whether adding multiple clipped pictures includes the clipping
+% path for each addition. I'll have to test the memory use of this code
+% versus adding all the copies to \gbc{_pti} and clipping once.)
% \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;
+def putimage (suffix pic) (text t) =
+ newpicture (_pti);
+ for _itm = t:
+ addto _pti also
+%<MF> (pic shifted goodpair (zconv (_itm)));
+%<MP> (pic shifted zconv (_itm));
+ DoClip (_pti); addto active_plane also _pti;
+ _pti := nullpicture;
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
+%<MF> mono active_plane
enddef;
% \end{macrocode}
%
-% \section{Axes, Axis Tic Marks, and Grids}
+% \section{Axes, Tic Marks, and Grids}\label{axes}
%
% \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}
+% This is used in \gbc{vectorfield} and to draw axes. It returns nothing.
+% This doesn't follow the usual pattern of drawing a path and returning
+% it. This approach 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.
+% \MF{} won't complain of an isolated expression. The new \mfpic{}
+% method of drawing an axis is to apply \cs{arrow} to the path
+% \gbc{axisline}. Thus it can also be \gbc{dashed}, \gbc{dotted},
+% etc.
%
-% The order is important if axis and head are different colors. This
+% The order is significant 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) =
@@ -4304,16 +4538,19 @@ enddef;
% \end{macrocode}
%
% \DescribeRoutine{xaxis}
-% \DescribeRoutine{yaxis}
+% \gbc{xaxis} draws the $x$-axis through the point $(0,0)$ in graph
+% coordinates. The only parameter is the length of the arrowhead in device
+% coordinates.
+% \DescribeRoutine{yaxis}\gbc{yaxis} draws the $y$-axis.
+%
% \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.
+% \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;
+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}
%
@@ -4322,35 +4559,32 @@ def axes (expr hlen) = xaxis (hlen); yaxis (hlen); enddef;
% 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{axisline}
+% The commands \gbc{axisline.x}, etc., return the appropriate straight
+% line at the appropriate location. 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.
+% change in \mfpic{} code, it is no longer used there. Instead, code
+% is written that allows the head to be drawn after the line is.
+% Note it is a vardef, and so returns the line as a path..
% \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;
+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;
+vardef borderrect =
+ rect((xneg+laxis,yneg+baxis),(xpos-raxis,ypos-taxis))
+enddef;
% \end{macrocode}
%
% Tick marks can be on the inside or outside of a border axis,
@@ -4381,7 +4615,7 @@ 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 \
+% \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;
@@ -4395,13 +4629,14 @@ xtick := ytick := centered;
% 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.
+% 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{pdir} is \mfc{right} or \mfc{up}, indicating the positive
+% direction on the axis.
% \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.
@@ -4409,7 +4644,7 @@ xtick := ytick := centered;
% \begin{macrocode}
vardef axismarks (expr inang, tp, loc, pdir) (expr len) (text t) =
save _tp, _U, _P, _tic, _ticang;
- pair _U, _P, _tic[];
+ pair _U, _P; path _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}
@@ -4419,7 +4654,7 @@ vardef axismarks (expr inang, tp, loc, pdir) (expr len) (text t) =
% 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;
+ _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
@@ -4429,121 +4664,174 @@ vardef axismarks (expr inang, tp, loc, pdir) (expr len) (text t) =
% \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)}.
+% Next, we use \gbc{_tp} to calculate the mark. For example, if
+% \gbc{tp = inside}, then \gbc{_tp = 1}. Since \gbc{_U} points toward
+% inside, \gbc{_tic} will go from \mfc{(0,0)} to a point a distance
+% \gbc{len} in the direction of \gbc{_U}.
% \begin{macrocode}
- _tic1 := (_tp - 1) * len * _U; % start of mark
- _tic2 := _tp * len * _U; % end of mark
+ _tic := (-_U--(0,0)) shifted (_tp*_U) scaled len;
% \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.
+% Finally, for each numeric value in the list \gbc{t}, draw the tic
+% shifted to the corresponding point on the axis.
% \begin{macrocode}
for _a = t:
- safedraw ((_tic1--_tic2) shifted zconv (loc + _a*pdir));
+ safedraw (_tic shifted zconv (loc + _a*pdir));
endfor
enddef;
% \end{macrocode}
%
% \DescribeRoutine{xmarks}
-% \DescribeRoutine{ymarks}
-% \DescribeRoutine{lmarks}
-% \DescribeRoutine{bmarks}
-% \DescribeRoutine{rmarks}
-% \DescribeRoutine{tmarks}
+% \RoutineIndex{lmarks}
+% \RoutineIndex{bmarks}
+% \RoutineIndex{rmarks}
+% \RoutineIndex{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}.
+% really make no sense for the $x$- and \DescribeRoutine{ymarks}$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 xmarks = axismarks ( 90, xtick, origin, right) enddef;
+def ymarks = axismarks (-90, ytick, origin, up) enddef;
def lmarks = axismarks (-90, ltick, (xneg + laxis, 0), up) enddef;
-def bmarks = axismarks ( 90, btick, (0, yneg + baxis), right) 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;
+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}
+% \DescribeRoutine{vargrid, vgrid}
+% Mainly for the purpose of visualising coordinates, \gbc{vargrid}
% 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.
+% are are \gbc{(n*xsp, m*ysp)}, \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;
+% coordinates. \DescribeRoutine{grid}\gbc{grid} is for backward
+% compatibility, calling \gbc{vargrid} with a default \gbc{dsize} of
+% \mfc{.5bp}. The old name \gbc{vgrid} incorrectly suggests a
+% connection to \gbc{vgridlines}.
+% \begin{macrocode}
+path griddotpath; griddotpath := fullcircle;
+def grid = vargrid (0.5bp) enddef;
+vardef vargrid (expr dsize, xsp, ysp) =
+ 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)));
+ for n = ceiling ((xneg + laxis)/xsp) upto floor ((xpos - raxis)/xsp):
+ for m = ceiling ((yneg + baxis)/ysp) upto floor ((ypos - taxis)/ysp):
+ picdot (gridpic, gdot, zconv ((n*xsp, m*ysp)));
endfor
endfor
-%<MF> mono (gridpic);
- coloraddto (pointcolor) (active_plane) (gridpic);
+ coloraddon (pointcolor, gridpic);
enddef;
+def vgrid = vargrid 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}.
+% This is more what I think of when I hear `grid', but the name was
+% already taken. \DescribeRoutine{hgridlines}\gbc{hgridlines} draws
+% horizontal lines through the same points where \gbc{grid} would draw
+% dots, and \DescribeRoutine{vgridlines}\gbc{vgridlines} draws only
+% vertical lines through the same points. Finally,
+% \DescribeRoutine{gridlines}\gbc{gridlines} draws both.
% \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));
+ 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));
+ 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);
+ vgridlines (xsp); hgridlines (ysp);
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{vectorfield} This command produces a field of arrows
+% from a pair-valued formula (text parameter \gbc{fcn}) in a region
+% described by a boolean-valued expression (text parameter \gbc{cond}).
+% This routine simply makes functions (\mfc{vardef}\,s) out of the
+% expressions and calls \DescribeRoutine{mkvectorfield}
+% \gbc{mkvectorfield}, which steps through the points described by
+% \gbc{xsp} and \gbc{ysp} and places an arrow (actually, any path) at
+% each. The arrow path is given by the function \gbc{vf}. The arrow is placed
+% at the point only if the function \gbc{isOK} returns true. It also omits
+% points that lie in the axis margins.
+%
+% \DescribeRoutine{plrvectorfield} The polar version differs only in
+% the distribution of the arrows. They are placed at regular intervals
+% of $r$ an $\theta$. The text parameters should be expressions in
+% \gbc{r} and \gbc{t}, but are otherwise the same. In particular,
+% \gbc{fcn} should return \MF{} pairs, not polar coordinate pairs. The
+% function \gbc{polar} can be used to convert if necessary. Its code is
+% very similar, except for the boolean code needed to keep the
+% vectors within the bounds of the graph.
+% \DescribeRoutine{mkplrvectorfield} It calls \gbc{mkplrvectorfield},
+% which is a lot like the non-polar version, except it first calculates
+% the extremes of the polar variables with \gbc{getpolarbounds} and relies
+% on the boolean to keep it out of the axis margins.
+% \begin{macrocode}
+def vectorfield (expr len, xsp, ysp) (text fcn) (text cond) =
+ save _vf, _is_OK;
+ vardef _vf (expr x,y) = ((0,0)--(fcn)) shifted (x,y) enddef;
+ vardef _is_OK (expr x,y) = cond enddef;
+ mkvectorfield (len, xsp, ysp) (_vf, _is_OK);
+enddef;
+
+vardef mkvectorfield (expr len, xsp, ysp) (suffix vf, isOK) =
+ for n = ceiling ((xneg + laxis)/xsp) upto floor ((xpos - raxis)/xsp):
+ for m = ceiling ((yneg + baxis)/ysp) upto floor ((ypos - taxis)/ysp):
+ if isOK (n*xsp,m*ysp): arrowdraw (len) (vf(n*xsp,m*ysp)); fi
+ endfor
+ endfor
+enddef;
+
+def plrvectorfield (expr len, rsp, tsp) (text fcn) (text cond) =
+ save _vf, _is_OK, _A, _B, _C, _D;
+ _A := xneg + laxis; _B := xpos + raxis;
+ _C := yneg + baxis; _D := ypos + taxis;
+ vardef _vf (expr r,t) = ((0,0)--(fcn)) shifted (r*dir t) enddef;
+ vardef _is_OK (expr r,t) =
+ save _X, _Y; _X := r*cosd t; _Y := r*sind t;
+ (cond) and (_A < _X) and (_X < _B) and (_C < _Y) and (_Y < _D)
+ enddef;
+ mkplrvectorfield (len, rsp, tsp) (_vf, _is_OK);
+enddef;
+
+vardef mkplrvectorfield (expr len, rsp, tsp) (suffix vf, isOK) =
+ save rmin, rmax, tmin, tmax;
+ getpolarbounds;
+ if rmin = 0:
+ if isOK (0,tmin): arrowdraw (len) (vf (0,tmin)); fi
+ rmin := rsp;
+ fi
+ for n = ceiling (rmin/rsp) upto floor (rmax/rsp):
+ for m = ceiling (tmin/tsp) upto floor (tmax/tsp):
+ if isOK (n*rsp,m*tsp): arrowdraw (len) (vf (n*rsp,m*tsp)); fi
+ endfor
+ endfor
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;
+% \gbc{patcharcs} draws on a picture \gbc{X} the arcs \gbc{tstart}${}\le
+% \theta \le{}$\gbc{tstop} with radii starting at \gbc{rstart}, stepping
+% by \gbc{rstep} until \gbc{rstop}.
+% \DescribeRoutine{patchrays}\gbc{patchrays} draws the radial lines
+% with $r$ coordinate varying between \gbc{rstart} and \gbc{rstop} at
+% angles from \gbc{tstart} to \gbc{tstop} stepping by \gbc{tstep}.
+% And \DescribeRoutine{plrpatch}\gbc{plrpatch} simply calls them both,
+% and adds the resulting pictures to \gbc{active_plane}.
+% \begin{macrocode}
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)) );
+ for rad = (if rstart = 0: rstep else: rstart fi)
+ step rstep until rstop:
+ orto (X, picpath zconv (arcplr (origin, tstart, tstop, rad)) );
endfor
enddef;
def patchrays (suffix X) (expr tstart, tstop, tstep, rstart, rstop) =
@@ -4552,112 +4840,136 @@ def patchrays (suffix X) (expr tstart, tstop, tstep, rstart, rstop) =
endfor
enddef;
+def plrpatch (expr rstart, rstop, rstep, tstart, tstop, tstep) =
+begingroup
+ newpicture (v);
+ patcharcs (v) (rstart, rstop, rstep, tstart, tstop);
+ coloraddon (drawcolor, v);
+ v := nullpicture;
+ patchrays (v) (tstart, tstop, tstep, rstart, rstop);
+ coloraddon (drawcolor, v);
+endgroup
+enddef;
+
% \end{macrocode}
%
-% \DescribeRoutine{polargrid}
-% \DescribeRoutine{polargridpoints}
+% Polar coordinate grids 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. Since three of the four require
+% calculating the dimensions of a polar coordinate patch that completely
+% covers the graph rectangle, we isolate that code in
+% \gbc{beginpolargrid}, defined later.
+%
+%
% \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
+% \gbc{gridarcs} creates arcs having radii that are integer multiples of
+% \gbc{rstep} and \DescribeRoutine{gridrays}\gbc{gridrays} draws radial
+% lines at angles that are multiples of \gbc{tstep}. The command
+% \DescribeRoutine{polargrid}\gbc{polargrid}simply calls the first two.
+%
+% \DescribeRoutine{polargridpoints}
+% On the other hand, \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} also
% 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.
+% The \gbc{rmin}, etc., returned by \gbc{beginpolargrid} 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 (x + 1)} 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));
+ if rmin = 0:
+ picdot (gridpic, setdot (griddotpath, penwd), zconv (origin));
fi
- rmin := rstep * floor(rmin/rstep + 1);
- rmax := rstep*ceiling(rmax/rstep - 1);
+ 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);
+ tmin := tstep * ceiling (tmin/tstep);
+ tmax := tstep * floor (tmax/tstep);
patchrays (gridpic) (tmin, tmax, tstep, rmin, rmax);
endpolargrid (drawcolor, .5penwd);
enddef;
+def polargrid (expr rstep, tstep) =
+ gridarcs (rstep); gridrays (tstep);
+enddef;
+
+def polargridpoints (expr dsize, rstep, tstep) =
+ beginpolargrid;
+ setpicture (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;
+
% \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}.
+% This calls \gbc{getpolarbounds} to compute the bounds (on $r$ and
+% $\theta$) of the smallest polar coordinate patch that covers the graph
+% rectangle. \DescribeRoutine{getpolarbounds}That command leaves the values
+% in \gbc{rmin}, \gbc{rmax}, \gbc{tmin} and \gbc{tmax}. Then it
+% initializes \gbc{gridpic} whereon the grids are drawn.
% \begin{macrocode}
def beginpolargrid =
- begingroup;
- save p, r, t, rmax, rmin, tmax, tmin;
+begingroup;
+ save rmax, rmin, tmax, tmin;
+ getpolarbounds;
+ newpicture (gridpic);
+enddef;
+
+def getpolarbounds =
+ save p, r, t;
pair p[];
- % Four corners:
- p0 := (xneg, yneg); p1 := (xneg, ypos);
- p2 := (xpos, ypos); p3 := (xpos, yneg);
+ 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 :
+ r0 := abs(p0); rmax := r0;
+ for j = 1 upto 3:
r[j] := abs(p[j]);
- if rmax < r[j] : rmax := r[j]; fi
+ 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.
+% essentially rotate one corner to have angle zero, get the angles to all
+% corners and rotate back. This guarantees that the wedge with
+% \gbc{tmin}${} < \theta < {}$\gbc{tmax} includes the graph.
% \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 :
+ if (xneg < 0) and (xpos > 0) and (yneg < 0) and (ypos > 0):
+ tmin := 0; tmax := 360;
+ elseif (p0 = origin): tmin := 0; tmax := 90;
+ elseif (p1 = origin): tmin := -90; tmax := 0;
+ elseif (p2 = origin): tmin := -180; tmax := -90;
+ elseif (p3 = origin): 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
+ t := t0 + anglefromto (p0, p[j]);
+ 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
@@ -4667,31 +4979,30 @@ def beginpolargrid =
% 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);
+ if (xneg < 0) and (0 < xpos):
+ rmin := emin (abs(yneg), abs(ypos));
+ elseif (yneg < 0) and (0 < ypos):
+ rmin := emin (abs(xneg), abs(xpos));
+ else:
+ 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
+% dot grids. The \gbc{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);
+ clipto (gridpic) rect ( zconv ((xneg, yneg)) - size*(1,1),
+ zconv ((xpos, ypos)) + size*(1,1) );
+ coloraddon (clr, gridpic);
endgroup
enddef;
@@ -4704,13 +5015,21 @@ enddef;
% \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 ( arcplr (origin, tstart, tstop, rstop) );
safedraw zconv ( ((rstart, 0)--(rstop, 0)) rotated tstop );
enddef;
% \end{macrocode}
%
-% \section{Path construction}
+% \section{Path Construction}\label{pathconstruction}
+%
+% This section is devoted to commands that accept a list or array of
+% points and produce a path, usually (but not necessarily) through those
+% points. In addition there are a few commands that find some of the key
+% points, lines and circles associated with a triangle. No \mfpic{}
+% interface is yet available for the triangle commands.
+%
+% \subsection{Piecewise linear paths}\label{linear}
%
% \DescribeRoutine{rect}
% Most of the macros that only define paths are coordinate independent.
@@ -4727,48 +5046,60 @@ enddef;
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;
+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.
+% equations separated by a semicolon, 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.
+%
+% The equations must \emph{not} be equations that are satisfied by all
+% $n$-gons regardless of size and position. For example:\\
+% \indent \gbc{regulapolygon(4)(Ted)(Ted0 := (0,0);Ted1 + Ted3 = (0,0))}\\
+% The second of these says the middle lies halfway between the extremes,
+% and is already a consequence of the code. It goes without saying that
+% equations that cannot be satisfied by any regular polygon are also out.
% \begin{macrocode}
vardef regularpolygon (expr n) (suffix Bob) (text eqns) =
- pair Bob[]; Bob := emax(round (abs (n)), 2);
+ 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;
+ for _uncle = 1 upto Bob - 1:
+ (Bob1 - Bob0) rotated (360/Bob*_uncle) = Bob[_uncle+1] - Bob0;
endfor
mkpoly (true) (Bob)
enddef;
% \end{macrocode}
%
-% The following set of commands accept any path as argument, but it is
+% The following set of commands take a 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
+% These produce the perpendicular from \,\gbc{point n of t}\, to the
+% (extension of) the opposite side (i.e., the altitude).
+% \DescribeRoutine{altitudept}The first one determines where the altitude
+% meets the opposite side, and the \DescribeRoutine{altitude}second just
+% connects the two points. Since \gbc{altitudept} is always \gbc{point 1
+% of altitude}, it is actually redundant. However, the command
+% \gbc{medianpt} (defined below) is used outside of the construction of
+% \gbc{median}, so it seemed possible the \gbc{altitudept} might be useful
+% also.
+%
+% We need a cycle so that points $n+1$ and $n+2$ will wrap around to the
+% start of the path when necessary.
% \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
+ save A, B, C, zz; pair A, B, C, zz;
+ B := pnt[n + 1] (t);
C := pnt[n + 2] (t);
zz = whatever[B,C];
- zz = A + whatever*((C-B) rotated 90);
+ zz = pnt[n](t) + whatever*((C-B) rotated 90);
zz
enddef;
@@ -4779,9 +5110,8 @@ 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.
+% These two produce the midpoint of the side opposite \,\gbc{point n of t}\,
+% and the \DescribeRoutine{median}line connecting those two points.
% \begin{macrocode}
vardef medianpt expr n of t =
0.5[pnt[n + 1] (t), pnt[n + 2] (t)]
@@ -4794,16 +5124,16 @@ 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.
+% The first produces the point on the side opposite \,\gbc{point n of t}\,
+% where the angle bisector at that corner crosses it and the second
+% produces \DescribeRoutine{anglebisector}the line that bisects that angle.
% \begin{macrocode}
vardef anglebisectorpt expr n of t =
- save A, B, C; pair A, B, C;
+ 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;
+ save zz; pair zz;
zz = whatever[B,C];
zz = A + whatever*((B-A) rotated (.5*cornerangle (A,B,C)));
zz
@@ -4819,17 +5149,22 @@ enddef;
% 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
+% arbitrary values 60 and 90 are designed to match the assumptions used
+% in the 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 anglefromto (expr u, v) =
+ if (u = origin) or (v = origin): 0
+ else: angle (v rotated (-angle u))
+ fi
+enddef;
+
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)))
+ if (A = B) and (B = C) : 60
+ elseif (A=B) or (A=C) : 90
+ else: anglefromto (B - A, C - A)
fi
enddef;
@@ -4837,15 +5172,15 @@ enddef;
%
% \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
+% that connects them. The first and third 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
+ if smooth: mksmooth (tens)
+ else: mkpoly
+ fi (cyclic, pts)
enddef;
% \end{macrocode}
@@ -4853,10 +5188,11 @@ enddef;
% \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.
+% It is also used with an array of paths instead of points.
% \begin{macrocode}
vardef mkpoly (expr cyclic) (suffix pts) =
for _i = 1 upto pts-1: pts[_i]-- endfor
- pts[pts] if cyclic : -- cycle fi
+ pts[pts] if cyclic: -- cycle else: {0,0} fi
enddef;
% \end{macrocode}
@@ -4864,22 +5200,55 @@ enddef;
% \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}.
+% \gbc{mkpoly}. \DescribeRoutine{NoPoints} prints a warning and sets the
+% array a single point, the origin.
% \begin{macrocode}
vardef polyline (expr cyclic) (text t) =
- save _pl; textpairs (_pl) (t); mkpoly (cyclic, _pl)
+ setpairs (_pl) (t);
+ if _pl=0: NoPoints ("polyline", _pl); fi
+ mkpoly (cyclic, _pl)
+enddef;
+
+def NoPoints (expr s) (suffix pts) =
+ GBwarn s & " attempted with empty list."; pts[incr pts] := origin;
+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 angle
+% 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 would
+% require keeping track of the current angle and using the \gbc{polar}
+% command.
+% \begin{macrocode}
+vardef turtle (text t) =
+ setnumeric (_tu) 0;
+ pair _tu[]; _tu0 := origin;
+ for _a = t: _tu[incr _tu] := _tu[_tu - 1] + _a; endfor
+ if _tu = 0: NoPoints("turtle", _tu); fi
+ mkpoly (false, _tu)
enddef;
% \end{macrocode}
%
+% \subsection{Smooth paths}\label{smooth}
+%
% 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}.
+% actually call \gbc{mksmooth} directly, most other path building commands
+% with tension parameters call \gbc{mkpath} or \gbc{mkfcn} (which calls
+% \gbc{mkpath}).
%
% \DescribeRoutine{mksmooth}
% This takes a tension value, a boolean, and the name of an array of
@@ -4891,258 +5260,670 @@ enddef;
% 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.
+% be straight.
% \begin{macrocode}
vardef mksmooth (expr tens, cyclic) (suffix pts) =
- pts1
- if pts = 1 :
- if cyclic : &cycle fi
+ if pts = 1: onepointpath (cyclic, pts1)
else:
- if cyclic :
- {pts[2]-pts[pts]}
- fi
+ settension (_tn) tens; fixtension (_tn);
+ pts1 if cyclic: {pts[2]-pts[pts]} fi
+ for _i = 2 upto pts-1:
+ ..tension _tn..pts[_i]{pts[_i+1]-pts[_i-1]}
+ endfor
+ ..tension _tn..pts[pts]
+ if cyclic: {pts[1]-pts[pts-1]}..tension _tn..cycle fi
+ fi
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{mktenser}
+% This is just like \gbc{mksmooth}, except the tension value is preceded
+% by \mfc{atleast}. At this writing only \gbc{mkconvex} uses it (as a
+% fallback when there are three or fewer points to connect).
+% \begin{macrocode}
+vardef mktenser (expr tens, cyclic) (suffix pts) =
+ if pts = 1: onepointpath (cyclic, pts1)
+ else:
+ settension (_tn) tens; fixtension (_tn);
+ pts1 if cyclic: {pts[2]-pts[pts]} fi
for _i = 2 upto pts-1:
- ..tension tens..pts[_i]{pts[_i+1]-pts[_i-1]}
+ ..tension atleast _tn..pts[_i]{pts[_i+1]-pts[_i-1]}
+ endfor
+ ..tension atleast _tn..pts[pts]
+ if cyclic: {pts[1]-pts[pts-1]}..tension atleast _tn..cycle fi
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{mkconvex}
+% This could have been very much like \gbc{mksmooth}, using
+% \mfc{tension atleast} instead of \mfc{tension} (i.e., exactly
+% \gbc{mktenser} above). Unfortunately This destroys smoothness at the
+% beginning and end of any sequence of three or more points that lie on a
+% straight line. Some geometric situations absolutely prevent smoothness,
+% but this certainly isn't one of them. Two consecutive points
+% identical isn't either, but it does. We let the user or calling command
+% arrange for it not to happen (for example, using \gbc{setuniquepairs}
+% instead of \gbc{setpairs}.
+%
+% What we do is weight the direction to be used at each point by how flat
+% the polygon is on the two sides of each point, the flatter side
+% getting the most weight. If a point is collinear with the next two,
+% the flatness is infinite and the curve is forced in that direction. We
+% measure the flatness using the square root of the area of the triangle
+% made by the given point and the the next two points. Those three points
+% lie on a line just when the area is $0$. Using this measure of fltness
+% can be disputed (two triangles can be equally flat in terms of angles
+% but different in terms of areas), but it has the advantage that if the
+% points are subjected to an affine transformation, the weighting is
+% unchanged and the constructed direction vectors transform the same way.
+% Of course, this is also true of other measures. I've tested only
+% the square root of area and it works well for simple cases.
+%
+% The only way to ensure that the whole path transforms the same as the
+% points is to explicitly calculate the controls (in a manner that
+% transforms the same way as the points). I will do this if I can figure
+% out how it \emph{should} be done. It appears to be a convex programing
+% problem.
+%
+% The end segments of a noncycle are problematic. One could argue that
+% this command doesn't apply to noncycles and just truncate the cyclic
+% path. I decided to treat them specially and hope the result is useful.
+% \begin{macrocode}
+vardef mkconvex (expr tens, cyclic) (suffix pts) =
+ save _B, _d, _tmp; pair _d[];
+ settension (_tn) tens; fixtension (_tn);
+ if pts < 4: mktenser (_tn, cyclic) (pts)
+ else:
+ for _j = 2 upto pts - 1:
+ _B[_j] := sqrt(abs((pts[_j]-pts[_j-1])xprod(pts[_j+1]-pts[_j])));
+ endfor
+ if cyclic:
+ _B1 := sqrt(abs((pts1 - pts[pts])xprod(pts2 - pts1)));
+ _B[pts] := sqrt(abs((pts[pts]-pts[pts-1])xprod(pts1 - pts[pts])));
+ else:
+ _B1 := _B2;
+ _B[pts] := _B[pts-1];
+ fi
+ for _j = 2 upto pts - 1:
+ _tmp := _B[_j-1] + _B[_j+1];
+ _d[_j] :=
+ if _tmp = 0: origin % signal to use curl1
+ else:
+ ( _B[_j+1]*(pts[_j] - pts[_j-1]) +
+ _B[_j-1]*(pts[_j+1] - pts[_j]) )/_tmp
+ fi;
endfor
- ..tension tens..pts[pts]
- if cyclic :
- {pts[1]-pts[pts-1]}..tension tens..cycle
+ if cyclic:
+ _tmp := _B[pts] + _B2;
+ _d1 :=
+ if _tmp = 0: origin
+ else:
+ (_B2*(pts1 - pts[pts]) + _B[pts]*(pts2 - pts1))/_tmp
+ fi;
+ _tmp := _B[pts-1] + _B1;
+ _d[pts] :=
+ if _tmp = 0: origin
+ else:
+ ( _B1*(pts[pts] - pts[pts-1]) +
+ _B[pts-1]*(pts1 - pts[pts]) )/_tmp
+ fi;
+ else:
+ _d1 := origin; _d[pts] := origin;
fi
+ pts1
+ for _j = 1 upto pts-1:
+ {if _d[_j] = origin: curl1 else: _d[_j] fi}
+ ..tension atleast _tn..pts[_j+1]
+ endfor
+ {if _d[pts] = origin: curl1 else: _d[pts] fi}
+ if cyclic: ..tension atleast _tn..cycle fi
fi
enddef;
% \end{macrocode}
%
+% The old \cs{curve} command in \mfpic{} permitted no tension parameter
+% and wrote a \grafbase{} \gbc{curve} command.
% \DescribeRoutine{curve}
+% For backward compatibility we keep that name, but simply call the
+% \gbc{tcurve} command with the default value for tension.
% \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.
+% \gbc{tcurve} converts a list of pairs to an array, then calls
+% \gbc{mksmooth} on the array.
+%
+% The next pair call \gbc{mkconvex}, which tries to produce a convex curve
+% when the points form a convex polygon. The first,
+% \DescribeRoutine{ccurve}\gbc{ccurve}, merely calls the second with a
+% default texnsion, while \DescribeRoutine{tccurve}creates an array from
+% the list of pairs and calls \gbc{mkconvex} on it.
% \begin{macrocode}
-def curve = tcurve (default_tension) enddef;
+numeric default_tension; default_tension := 1;
+def curve = tcurve (default_tension) enddef;
vardef tcurve (expr tens, cyclic) (text t) =
- save _tc; textpairs (_tc) (t); mksmooth (tens, cyclic, _tc)
+ setpairs (_tc) (t); mksmooth (tens, cyclic, _tc)
+enddef;
+
+def ccurve = tccurve (default_tension) enddef;
+vardef tccurve (expr tens, cyclic) (text t) =
+ setuniquepairs (_tcc) (t); mkconvex (tens, cyclic, _tcc)
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
+% 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.
+%
+% \DescribeRoutine{mkbezier}
+% \gbc{mkbezier} takes an array argument and produces either an open or
+% cyclic path with a given tension. \DescribeRoutine{bezier}\gbc{bezier}
+% does nothing more than call \gbc{tbezier} with the default tension,
+% \DescribeRoutine{tbezier}which takes a list of points and creates an
+% array for \gbc{mkbezier} to act on.
% \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
+ settension (_tn) tens; fixtension (_tn);
+ pts1
+ for _i = 2 upto pts: ..tension _tn..pts[_i] endfor
+ if cyclic: ..tension _tn..cycle else: {0,0} fi
enddef;
-def bezier = tbezier (default_tension) enddef;
+def bezier = tbezier (default_tension) enddef;
vardef tbezier (expr tens, cyclic) (text t) =
- save _tsb; textpairs (_tsb) (t); mkbezier (tens, cyclic) (_tsb)
+ setpairs (_tbs) (t);
+ if _tbs=0: NoPoints ("bezier", _tbs); fi
+ mkbezier (tens, cyclic) (_tbs)
enddef;
% \end{macrocode}
%
-% \DescribeRoutine{qbezier}
-% \DescribeRoutine{mkqbezier}
-% It also semed we ought to allow \mfpic{} users to easily reproduce the
+% It also seemed 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 next 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.
+% These commands and the various splines below 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.
%
+% \DescribeRoutine{mkqbezier}
% \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.
+% an odd number for a noncyclic path. The \mfc{for}-loop ends on the last
+% odd index. If \gbc{pts} is odd and \gbc{cyclic} is true we need to
+% manufacture another control point for the connection back to the
+% start. We take that to be a repetition of the last point. If \gbc{pts}
+% is even and \gbc{cyclic} is false then the loop ends at \gbc{pts-1}.
+% Again we need to pick a control to connect to the last point, and again
+% we use that last point as the control. In these two cases the last
+% link is a straight line.
+%
+% \DescribeRoutine{qbezier}
+% The \gbc{qbezier} command takes a list of points and creates an array
+% from then before calling \gbc{mkqbezier}.
% \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
+ if pts=1: {0,0}
+ else:
+ for _i = 2 step 2 until pts - 1:
+ ..controls 1/3[pts[_i], pts[_i-1] ] and 1/3[pts[_i], pts[_i+1] ]..
+ pts[_i+1]
+ endfor
+ if cyclic:
+ ..controls 1/3[ pts[pts], pts[pts - 1] ]
+ and 1/3[ pts[pts], pts1 ]..cycle
+ fi
+ fi
enddef;
vardef qbezier (expr cyclic) (text t) =
- save _qbz; textpairs (_qbz) (t); mkqbezier (cyclic) (_qbz)
+ setpairs (_qbz) (t);
+ if _qbz=0: NoPoints ("qbezier", _qbz); fi
+ if _qbz=1: onepointpath (cyclic, _qbz1)
+ else:
+ if (cyclic and odd _qbz) or (not cyclic and even _qbz):
+ _qbz[incr _qbz] := _qbz[_qbz-1];
+ fi
+ mkqbezier (cyclic) (_qbz)
+ fi
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
+% When calling \gbc{curve} or \gbc{tcurve} 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$.
+% that a sequence of points with increasing $x$-coordinate will produce a
+% path with increasing $x$-coordinate. The \gbc{fcnspline} command will do
+% what we want, but we have no control over the path, apart from the
+% equations at the ends.
+%
+% The requirement to guarantee that a path have increasing $x$-coordinates
+% is that the control points of the segment connecting
+% $(x\sb{j}, y\sb{j})$ to the next $(x\sb{j+1}, y\sb{j+1})$ have their
+% $x$-part in the interval $x\sb{j} < x < x\sb{j+1}$.
%
% 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}
+% $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. By default we choose the two controls so the \mfc{xpart}s
+% divide the $x$-interval into three equal parts. This makes the B\'ezier
+% $f(t)$ linear in the $x$-part and so has the added `advantage' that in
+% each segment, $y$ is a cubic function of $x$. It is not a spline, as we
+% compute the controls locally and allow them to be modified by an
+% additional parameter.
%
% 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.
+% of the straight line directions to adjacent points. We now do the same
+% here, though it is not clear if this is best.
%
% Finally, we permit a tension of sorts by dividing the distance to the
-% controls by a parameter \gbc{fcn_tension}.
+% controls by a parameter normally equal to \gbc{default_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.
+% This computes the control point for the points on the path, following
+% the above description. If by chance some $x$ interval is zero, we
+% make the controls equal to the nodes, which gives a straight vertical
+% (the $y$ values differ because we use \gbc{setuniquepairs}. This also
+% abandons smoothness there.
+%
+% The method selecting the controls is new with \mfpic{} version 0.8.
+% Following discussions with Stephan Hennig in \texttt{comp.text.tex} I
+% came to the conclusion that the method used ought to satisfy the
+% following: if the data are xscaled or yscaled, the control vectors ought
+% to scale the same way. The current version does that.
%
% \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.
+% This is the \mfpic{} interface; \gbc{fcncurve} calls \gbc{functioncurve}
+% with the default tension, which \DescribeRoutine{functioncurve}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
+ Y if (xpart(Z-Y) <> 0) and (xpart(Y-X) <> 0):
+ + xpart(Z-Y)/3/xpart(Z-X)*(Z - X)/ftens fi
enddef;
-vardef mkfcnpath (expr ftens) (suffix q) =
+vardef mkfcnpath (expr ftens) (suffix q) =
+ settension (_tn) ftens;
+ if _tn <= 0: _tn := 1; fi
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])..
+ q[_i]..controls fcncontrol (_tn) (q[_i-1], q[_i], q[_i+1])
+ and fcncontrol (_tn) (q[_i+2], q[_i+1], q[_i])..
endfor
- q[q]
+ q[q]{0,0}
enddef;
-def fcncurve = functioncurve (emax(1.2default_tension, eps)) enddef;
+def fcncurve = functioncurve (default_tension) enddef;
+def tfcncurve = functioncurve 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)
+ settension (_ftens) ftens; if _ftens < 1/3: _ftens := 1/3; fi
+ setuniquepairs (_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.
+% \subsection{Splines with explicit controls}\label{splines}
%
-% 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.
+% For these 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. The
+% nodes are just half way between the one control point and the next.
+%
+% \DescribeRoutine{openqbs}
+% For simplicity, the list is converted to an array \gbc{_oq} first. In
+% the closed version \DescribeRoutine{closedqbs}additional array elements
+% are created at the end, repeating two of the beginning elements.
+% Finally,
+% \DescribeRoutine{mkqbs}
+% \gbc{mkqbs} is called. This draws an open spline based on the points in
+% an array \gbc{b}. The additional array elements defined by
+% \gbc{closedqbs} cause the resulting path to end where it began and a
+% simple \mfc{\&cycle} closes it.
+%
+% \DescribeRoutine{qspline} The \mfpic{} commands \cs{qspline} and
+% \cs{closedqspline} now call \gbc{qspline} with appropriate boolean, for
+% consistency with other commands that have the same argument structure.
+% The commands \gbc{openqbs}, and \gbc{closedqbs} are no longer needed,
+% but are kept for backward compatability. The most efficient setup would
+% be to give \gbc{mkqbs} a boolean argument, but that could break old
+% files.
% \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)
+def openqbs = qspline (false) enddef;
+def closedqbs = qspline (true) enddef;
+
+vardef mkqbs (suffix b) =
+ 0.5[ b1, b2]
+ if b<3: {0,0}
+ else:
+ for _i = 2 upto b-1:
+ ..controls 1/6[ b[_i], b[_i-1] ] and 1/6[ b[_i], b[_i+1] ]..
+ 0.5[ b[_i], b[_i+1] ]
+ endfor
+ fi
+enddef;
+
+vardef qspline (expr cyclic) (text t) =
+ setpairs (_qs) (t);
+ if _qs=0: NoPoints ("qspline", _qs); fi
+ if _qs=1: _qs[incr _qs] := _qs1; fi
+ if cyclic:
+ _qs[incr _qs] := _qs1; _qs[incr _qs] := _qs2;
+ fi
+ mkqbs (_qs) if cyclic: & cycle fi
+enddef;
+% \end{macrocode}
+%
+% These cubic B-splines also require a list of `control' points. Each of
+% the points $Q$, combined with the next one $Q'$, determine two more
+% points that divide the segment from $Q$ to $Q'$ into thirds. These new
+% points become the two control points of a \MF{} B\'ezier segment. The
+% nodes of these segments are half way between the second control of one
+% segment and the first control of the next.
+%
+% \DescribeRoutine{mkcbs}The main code is in \gbc{mkcbs}, which results
+% in an open curve. For backward compatibility, the alias
+% \DescribeRoutine{mkopencbs}\gbc{mkopencbs} is supplied.
+%
+% \DescribeRoutine{mkclosedcbs}
+% Earlier versions of \gbc{mkclosedcbs} would partly redefine its suffix
+% parameter (for example, using \gbc{b[incr b]:=b1}. I decided this
+% shouldn't change the array variable (imagine using two such functions on
+% the same array). Now the code has been rearranged so the cubic case is
+% handled just like the quadratic. We retain \gbc{mkclosedcbs} only for
+% backward compatibility.
+%
+% \DescribeRoutine{opencbs}
+% These are the versions taking a list of points instead of an array name.
+% They create a temporary array and call \gbc{mkcbs}, with
+% \DescribeRoutine{closedcbs}\gbc{closedcbs} extending the array, just
+% like the quadratic versions.
+%
+% \DescribeRoutine{cspline} The \mfpic{} commands \cs{cspline} and
+% \cs{closedcspline} now call \gbc{cspline} with appropriate boolean, for
+% consistency with other commands that have the same argument structure.
+% The commands \gbc{mkopencbs}, \gbc{opencbs}, and \gbc{closedcbs} are no
+% longer needed, but are kept for backward compatability. The most
+% efficient setup would be to give \gbc{mkcbs} a boolean argument, but
+% that could break old files.
+% \begin{macrocode}
+vardef mkcbs (suffix b) =
+ (b[1]+4b[2]+b[3])/6
+ if b < 4: {0,0}
+ else:
+ for _i = 3 upto b-1:
+ ..controls 1/3[ b[_i-1], b[_i] ] and 1/3[ b[_i], b[_i-1] ]
+ .. (b[_i-1] + 4b[_i] + b[_i+1])/6
+ endfor
+ fi
+enddef;
+
+def mkopencbs = mkcbs enddef;
+vardef mkclosedcbs (suffix b) =
+ mkcbs (b) & opencbs (b[b-2],b[b-1],b[b], b1, b2, b3) & cycle
+enddef;
+
+def opencbs = cspline (false) enddef;
+def closedcbs = cspline (true) enddef;
+
+vardef cspline (expr cyclic) (text t) =
+ setpairs (_cs) (t);
+ if _cs=0: NoPoints ("cspline", _cs); fi
+ for _idx = _cs upto 2: _cs[incr _cs] := _cs[_idx]; endfor
+ if cyclic:
+ for _idx = 1 upto 3: _cs[incr _cs] := _cs[_idx]; endfor
+ fi
+ mkcbs (_cs) if cyclic: & cycle fi
+enddef;
+% \end{macrocode}
+%
+% \subsection{Splines with computed controls}\label{computedsplines}
+%
+% A cubic spline through a set of points is a curve obtained by joining
+% each point to the next with a cubic parametrized curve, where adjoining
+% cubics must have matching first and second derivative at their common
+% point. In the previous section's \gbc{mkcbs}, the control points must be
+% supplied, it being up to the user to arrange (if necessary) that the
+% spline produced passes through given points. If, instead, these points
+% are given, it is possible to compute the necessary controls.
+% Unfortunately, the controls are not uniquely determined unless the curve
+% is required to be closed. For open curves, there is need for two
+% additional conditions at the end points. A `relaxed spline' is produced
+% if we require that the second derivative is $0$ at those points.
+%
+% For a closed curve, the equality of the first and second derivatives at
+% the common beginning/ending point gives the needed additional equations.
+%
+% Note that this equates \emph{time} derivatives, so this works best when
+% points are relatively evenly spaced and so the speed is relatively
+% uniform. If points are differently spaced then the relatively slower
+% speed between closely spaced points allows sharper turns without large
+% second derivatives. Curves produced tend to have a more natural look,
+% and relaxed splines are most suitable for smoothing data that is
+% obtained by taking observations at evenly space times. Still, the
+% technique is somewhat unstable when points are closely spaced, for
+% example when a small change in the position of one point can produce a
+% large change in its direction when viewed from another point.
+%
+% \DescribeRoutine{init_spline_eqns}
+% In this command we generate the equations common to all cubic
+% splines: the equality of derivatives at all interior points.
+% This command accepts a suffix \gbc{pts}, which is the array of points
+% to be connected. It initializes the variables \gbc{_spl_pre[\,]} and
+% \gbc{_spl_post[\,]} to unknown arrays of pairs. These will hold the
+% control points.
+%
+% \DescribeRoutine{closed_spline_eqns}The next two macros contain the
+% additional equations: for a closed spline these are the same as the
+% interior equation, but at the first and last point in the array. For
+% \DescribeRoutine{relaxed_spline_eqns}relaxed splines they force
+% the second derivative to be 0 at the first and last point.
+%
+% The macro \gbc{mksplinepath} simply assembles the points and controls
+% previously computed into a path.
+%
+% \DescribeRoutine{mkspline}\gbc{mkspline} issues the
+% common equations and then either the closed equations (\gbc{closed =
+% true}) or the relaxed equations (\gbc{closed = false}), before calling
+% \gbc{mksplinepath}.
+%
+% The knowledgeable user can call \gbc{init_spline_eqns}, append any
+% choice of equations for the end segments, and then call
+% \gbc{mksplinepath~(false)} to produce any sort of open spline.
+%
+% \DescribeRoutine{dospline}
+% This version accepts a list of pairs and produces a spline through
+% them. It simply stores the list in an array and calls the appropriate
+% version that operates on an array. This is the command passed by \mfpic{}.
+% \begin{macrocode}
+def init_spline_eqns (suffix pts) =
+ save _spl_pre, _spl_post;
+ pair _spl_pre[], _spl_post[];
+ for j= 2 upto pts - 1:
+ _spl_post[j] + _spl_pre[j] = 2pts[j];
+ _spl_pre[j+1]+2_spl_pre[j] = 2_spl_post[j]+_spl_post[j-1];
+ endfor
+enddef;
+
+def closed_spline_eqns (suffix pts) =
+ _spl_post1 + _spl_pre1 = 2pts1;
+ _spl_post[pts] + _spl_pre[pts] = 2pts[pts];
+ _spl_pre2 + 2_spl_pre1 = 2_spl_post1 + _spl_post[pts];
+ _spl_pre1+2_spl_pre[pts] = 2_spl_post[pts]+_spl_post[pts-1];
+enddef;
+
+def relaxed_spline_eqns (suffix pts) =
+ _spl_pre2 + pts1 = 2_spl_post1;
+ pts[pts] + _spl_post[pts-1] = 2_spl_pre[pts];
+enddef;
+
+vardef mksplinepath (expr closed) (suffix pts) =
+ pts1..controls _spl_post1 and
+ for j = 2 upto pts if not closed: -1 fi:
+ _spl_pre[j]..pts[j]..controls _spl_post[j] and
+ endfor
+ if closed: _spl_pre1..cycle else: _spl_pre[pts]..pts[pts] fi
+enddef;
+
+def mkspline (expr closed) (suffix pts) =
+ init_spline_eqns (pts);
+ if closed: closed_spline_eqns (pts);
+ else: relaxed_spline_eqns (pts);
+ fi
+ mksplinepath (closed) (pts)
+enddef;
+
+vardef dospline (expr closed) (text the_list) =
+ setpairs (_sp) (the_list);
+ if _sp=0: NoPoints ("dospline", _sp); fi
+ if _sp=1: _sp[incr _sp] := _sp1; fi
+ mkspline (closed) (_sp)
enddef;
% \end{macrocode}
+% The above computations produce a $2$-dimensional spline. A $1$-dimensional
+% cubic spline would be a function $f(t)$ with numeric values rather
+% than pair values. Such are often used to interpolate functions. That is,
+% given pairs $(x\sb j,y\sb{j})$, and assuming they lie on the graph of
+% some function (generally unknown), fill in the graph with $y = f(x)$
+% where $f$ is a cubic function of $x$ in each interval $x\sb j < x < x\sb
+% {j+1}$, making sure that the resulting graph is as smooth as possible at
+% the points $x\sb j$.
+%
+% The requirements on our $2$-dimensional path are the following:
+% \begin{enumerate}
+% \item The $j$th link should connect $(x\sb{j},y\sb{j})$ to $(x\sb{j+1},
+% y\sb{j+1})$.
+% \item The $x$-part of that link should increase linearly from $x\sb{j}$ to
+% $x\sb{j+1}$ as $t$ goes from $0$ to $1$.
+% \item The $y$-part should be a cubic $y = f(x)$.
+% \item The $x$-derivatives $df/dx$ and $d^2f/dx^2$ should match at the
+% connecting points.
+% \end{enumerate}
%
+% Two necessary equations for converting between $x$ and $t$ coordinates
+% are:
+% \begin{equation}\label{first}
+% x = x\sb{j} + t \Delta x\sb{j}
+% \end{equation}
+% (where $\Delta x\sb{j} = x\sb{j+1} - x\sb{j}$) and
+% \begin{equation}\label{second}
+% \frac{df}{dt} = \frac{dx}{dt}\frac{df}{dx} =
+% \Delta x\sb{j} \frac{df}{dx}.
+% \end{equation}
+% Thus we want to choose controls so that (\ref{first}) is maintained and
+% so that $x$-derivatives match. It turns out that this requires controls
+% at
+% \begin{equation}
+% \begin{array}{c}
+% (x\sb{j}, y\sb{j}) - (\Delta x\sb{j-1}, s\sb{j} \Delta x\sb{j-1})/3\\
+% (x\sb{j}, y\sb{j}) + (\Delta x\sb{j} , s\sb{j} \Delta x\sb{j} )/3
+% \end{array}
+% \end{equation}
+% where $s\sb{j}$ is the slope (derivative) at $x\sb{j}$. This provides
+% matching first derivatives automatically (equation (\ref{second})) and
+% also (\ref{first}). To get matching second derivatives we need the same
+% conditions as in parametric splines. We use these equations simplified to
+% the form:
+% \begin{displaymath}
+% s\sb{j+1} \Delta x\sb{j} - 2s\sb{j} (\Delta x\sb{j} +
+% \Delta x\sb{j-1}) + s\sb{j-1}\Delta x\sb{j-1}
+% = 3y\sb{j+1} - 3y\sb{j-1}.
+% \end{displaymath}
+% There can be almost any equations at the end points. For a relaxed
+% spline we equate the second derivatives to 0. To get a periodic
+% function, we equate the slope and second derivative at beginning to
+% those at the end. This makes it possible to put a shifted copy of the
+% graph with starting point at the end of the original and have the same
+% smoothness at that connection as at the other points.
+%
+% \DescribeRoutine{init_fcnspl_eqns}
+% This declares the temporary arrays \gbc{_dx[\,]} (the set of $dx\sb j$)
+% and \gbc{_sl[\,]} (the desired slopes) and issues the common equations.
+% The parameter \gbc{pts} is the array of $(x,y)$ values.
+%
+% \DescribeRoutine{periodic_fcnspl_eqns}For the periodic case we
+% use \gbc{periodic_fcnspl_eqns} to generate the additional
+% equations and for the \DescribeRoutine{relaxed_fcnspl_eqns}relaxed case
+% we use \gbc{relaxed_fcnspl_eqns}. As before, one can produce custom
+% splines by issuing the common equations and then ones own equations.
+%
+% \DescribeRoutine{mkfcnsplpath}
+% Then we assemble the path from the computed information by calling the
+% command \gbc{mkfcnsplpath}.
+%
+% \DescribeRoutine{mkfcnspline}
+% These commands emits the appropriate equations then assemble the path.
+% The if the first parameter is true it uses the periodic equations,
+% otherwise the relaxed equations.
+%
+% \DescribeRoutine{fcnspline}
+% Finally, this command is the one written by \mfpic{}. It copies a list
+% of pairs into an array and calls the appropriate command to process
+% them.
+% \begin{macrocode}
+def init_fcnspl_eqns (suffix pts) =
+ save _dx, _sl; numeric _dx[], _sl[];
+ _dx1 := xpart (pts2 - pts1);
+ for j = 2 upto pts - 1:
+ _dx[j] := xpart (pts[j+1] - pts[j]);
+ _sl[j + 1]*_dx[j] + _sl[j-1]*_dx[j-1] + 2_sl[j]*(_dx[j] + _dx[j-1])
+ = 3*ypart(pts[j+1] - pts[j-1]);
+ endfor
+enddef;
+
+def periodic_fcnspl_eqns (suffix pts) =
+ _sl1 = _sl[pts];
+ _sl2*_dx1 + 2_sl1*_dx1 + 2_sl[pts]*_dx[pts-1] + _sl[pts-1]*_dx[pts-1]
+ = 3 * ypart (pts[2] - pts[pts-1]);
+enddef;
+
+def relaxed_fcnspl_eqns (suffix pts) =
+ _sl2*_dx1 + 2_sl1*_dx1 = 3 * ypart(pts2 - pts1);
+ _sl[pts-1]*_dx[pts-1] + 2_sl[pts]*_dx[pts-1]
+ = 3 * ypart(pts[pts] - pts[pts-1]);
+enddef;
+
+vardef mkfcnsplpath (suffix pts) =
+ pts1..controls (pts1 + (1, _sl1)/3*_dx1) and
+ for j = 2 upto pts - 1:
+ (pts[j] - (1, _sl[j])/3*_dx[j-1]) ..pts[j]..
+ controls (pts[j] + (1,_sl[j])/3*_dx[j]) and
+ endfor
+ (pts[pts] - (1,_sl[pts])*_dx[pts-1]/3)..pts[pts]
+enddef;
+
+vardef mkfcnspline (expr periodic) (suffix pts) =
+ init_fcnspl_eqns (pts);
+ if periodic: periodic_fcnspl_eqns (pts);
+ else: relaxed_fcnspl_eqns (pts);
+ fi
+ mkfcnsplpath (pts)
+enddef;
+
+vardef fcnspline (expr periodic) (text the_list) =
+ setpairs (_fs) (the_list);
+ if _fs<2:
+ if _fs=0: NoPoints ("fcnspline", _fs); fi
+ onepointpath (false, _fs1)
+ else:
+ mkfcnspline (periodic) (_fs)
+ fi
+enddef;
+
+% \end{macrocode}
%
-% \section{Arcs, Circles and Ellipses}
+% \subsection{Arcs, circles and ellipses}\label{arcs}
%
% We have multiple commands that generate circular arcs, differing in
% how the arc is specified. All are (in part) based on the following
@@ -5162,9 +5943,13 @@ enddef;
% 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
+% both \gbc{center} and \gbc{begpt} in \MF, then the other values on the
% arc should be no problem.
%
+% Care has been taken that changing the sign of various parameters
+% produces reasonable results. And there should be no more problem for
+% arcs with sweep larger than 360 degrees than with less.
+%
% \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}
@@ -5178,19 +5963,25 @@ enddef;
% 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}
+% Note that \gbc{mkarc} has parameters that may over-determine the arc.
+% It is only called by arc-making commands that have calculated these
+% parameters and, I hope, ensured they are compatible. \gbc{mkarc}'s job
+% is mainly to ensure that the arc begins at \gbc{begpt} and ends at
+% \gbc{endpt} (exactly). A \gbc{sweep} of $0$ is actually incompatible
+% with any case where \gbc{begpt<>endpt} unless \gbc{center} is
+% literally at infinity, but we allow it even though I am pretty sure
+% the other arc commands all filter out that case.
+% \begin{macrocode}
+vardef mkarc (expr center, begpt, endpt, sweep) =
+ if (sweep = 0): begpt--endpt
+ else:
+ setnumeric (n) ceiling (abs(sweep)/45);
+ setpair (d) (begpt - center) rotated (signof (sweep) 90);
+ begpt{d}
+ for j = 1 upto n-1:
+ ..(begpt rotatedabout (center, j/n*sweep)){d rotated (j/n*sweep)}
+ endfor ..endpt{d rotated sweep}
+ fi
enddef;
% \end{macrocode}
@@ -5200,75 +5991,13 @@ enddef;
% 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
+vardef arc (expr center, begpt, sweep) =
+ if (center = begpt) or (sweep = 0): begpt--begpt
else:
- save to; pair to;
- to := from rotatedabout (center, sweep);
- mkarc (center, from, to, sweep)
+ mkarc (center, begpt, begpt rotatedabout (center, sweep), 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;
+def arccps = arc enddef;
% \end{macrocode}
%
@@ -5277,57 +6006,112 @@ enddef;
% 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)}
+% greater than 45 degrees we let \MF{} find the midpoint \gbc{m} of the
+% arc. Otherwise, we get the center \gbc{c} of the circle and call
+% \gbc{mkarc}.
+%
+% The code for finding \gbc{c} and \gbc{m} used to be separate commands,
+% \gbc{arccenter} and \gbc{midarc}. However, this is the only place we
+% used them and the several cases that they had to consider are reduced
+% because the \mfc{if} in this command takes care of some of them.
+%
+% The code for finding \gbc{m} uses the fact that chord and the line
+% from one of its endpoints to the midpoint subtend a circular arc of
+% \gbc{sweep/2} and so the angle between them is half that, \gbc{sweep/4}.
+% The code gets the intersection between the line in that direction and
+% the perpendicular bisector of the chord.
+%
+% We find the center by intersecting two lines. One is the radius from
+% one end of the chord. Then we branch on two cases: if the chord is close
+% to a diameter, use its perpendicular bisector as the other line,
+% otherwise use the radius from the opposite end of the chord. Here
+% \gbc{cd} is a vector in the direction of the chord from \gbc{begpt} to
+% \gbc{endpt}. The angle \gbc{ang} is the amount we have to rotate
+% \gbc{cd} about \gbc{begpt} to make it point toward the center of the
+% circle. This gives the radius mentioned above.
+% \begin{macrocode}
+vardef arcpps (expr begpt, endpt, sweep) =
+ if begpt = endpt: begpt--endpt
+ else:
+ setpair (cd) unitvector (endpt-begpt);
+ if abs(sweep) <= 45:
+ begpt{cd rotated (-sweep/2)}..endpt{cd rotated (sweep/2)}
+ elseif abs(sweep) <= 90:
+ save m; pair m;
+ m = begpt + whatever*( cd rotated (-sweep/4));
+ m = 0.5[begpt, endpt] + whatever*(cd rotated 90);
+ begpt{cd rotated (-sweep/2)}..m{cd}..endpt{cd rotated (sweep/2)}
else:
- from{cd rotated (-sweep/2)}..midarc(from, to, sweep){cd}
- ..to{cd rotated (sweep/2)}
+ setnumeric (ang) 90 - ((sweep/2) mod 180);
+ if abs(ang) = 90:
+ GBwarn "undefined arc. A line segment will be used instead.";
+ begpt--endpt
+ else:
+ save c; pair c;
+ c = begpt + whatever*(cd rotated ang);
+ c = if abs(ang) < 30:
+ (0.5)[begpt, endpt] + whatever*(cd rotated 90)
+ else:
+ endpt + whatever*(-cd rotated -ang)
+ fi;
+ mkarc (c, begpt, endpt, sweep)
+ fi
fi
- else:
- save center; pair center;
- center := arccenter (from, to, sweep);
- mkarc (center, from, to, sweep)
fi
enddef;
% \end{macrocode}
%
+% \DescribeRoutine{arcpp}
+% In this, two points and the radius of the circle are given. Alone, this
+% would determine two circles and therefore 4 arcs. We reduce the
+% possibilities to two by assuming the arc is anticlockwise from the first
+% point to the second if \gbc{rad} is positive, clockwise if negative.
+% Then \gbc{arcpp} produces the one that has absolute value no more than
+% 180 degrees if \gbc{small} is true, otherwise the other one.
+% \DescribeRoutine{arcppr}\gbc{arcppr} is just \gbc{arcpp} with the
+% boolean argument \gbc{small} last (for compatibility with previous
+% \mfpic{} versions).
+%
+% The code computes the angle of the arc and calls \gbc{arcpps}. If the
+% radius is not larger than half the distance between the points, we make
+% the angle $\pm 180$, which produces a half circle.
+% \begin{macrocode}
+vardef arcpp (expr small, begpt, endpt, rad) =
+ save full, ang; full := signof (rad) 360;
+ if 2*abs(rad) > abs(begpt - endpt):
+ ang := if not small: full - fi 2*asin (abs(begpt-endpt)/(2rad));
+ else: ang := signof (rad) 180;
+ fi
+ arcpps (begpt, endpt, ang)
+enddef;
+def arcppr (expr begpt, endpt, rad, small) =
+ arcpp (small, begpt, endpt, rad)
+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
+ 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
+ mkarc (center, center + rad*dir frtheta,
+ center + rad*dir totheta, totheta - frtheta)
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)
+vardef arcalt (expr center, radius, frtheta, totheta) =
+ arcplr (center, frtheta, totheta, radius)
enddef;
% \end{macrocode}
@@ -5347,11 +6131,16 @@ enddef;
% \end{macrocode}
%
% \DescribeRoutine{ellipse}
-% \DescribeRoutine{circle}
-% We get an ellipse by x-scaling and y-scaling a unit circle, rotating it
+% We get an ellipse by xscaling and yscaling 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.
+%
+% If either radius is negative, the sense of the ellipse is reversed and
+% the starting point changes. If both are negative, only the starting
+% point changes.
+% \DescribeRoutine{circle}
+% \gbc{circle} acts like \gbc{ellipse} with both radii the same.
% \begin{macrocode}
vardef ellipse (expr center, radx, rady, angle) =
fullcircle xscaled (2*radx) yscaled (2*rady) rotated angle
@@ -5360,37 +6149,53 @@ enddef;
vardef circle (expr center, rad) =
fullcircle scaled (2*rad) shifted center
enddef;
-
% \end{macrocode}
+% The next four implement different ways of specifying a circle.
% \DescribeRoutine{circlecp}
+% The first produces the circle with a given center passing through a
+% given point.
% \DescribeRoutine{circleppp}
+% The second produces the circle passing through three given points.
% \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
+% 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.
+% \DescribeRoutine{circleppr}
+% The fourth produces the circle with the given radius passing
+% through the two points in such a way that the angle from the first point
+% to the second is between $0$ and $180$ degrees if the switch \gbc{small}
+% is true. If \gbc{small} is false, then the clockwise arc from first to
+% second is between $180$ and $360$. If \gbc{rad} is negative, the
+% circles switch and their orientation is reversed.
+% \DescribeRoutine{circlepp}\gbc{circlepp} is just \gbc{circleppr} with
+% a different order of arguments (for previous \mfpic{} versions).
+%
+% These could be implemented by finding the center and radius and calling
+% \gbc{circle}. However, we call the arc commands so that those points
+% specified in the parameters that lie on the circle will be nodes of the
+% path produced, in the given order.
% \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
+ arcpps (one, two, 2*cornerangle (three, one, two))
+ & arcpps (two, three, 2*cornerangle (one, two, three))
+ & arcpps (three, one, 2*cornerangle (two, three, one))
+ & cycle
enddef;
vardef circlepps (expr one, two, sweep) =
- save ang, full; numeric ang[], full;
+ save ang, full;
full := signof (sweep) 360;
- ang1 := sweep mod (full);
- ang2 := full - ang1;
- arcpps (one, two, ang1) & arcpps (two, one, ang2) & cycle
+ ang := sweep mod full;
+ arcpps (one, two, ang) & arcpps (two, one, full - ang) & cycle
+enddef;
+vardef circlepp (expr small, one, two, rad) =
+ arcpp (small, one, two, rad) & arcpp (not small, two, one, rad) & cycle
+enddef;
+def circleppr (expr one, two, rad, small) =
+ circleppr (one, two, rad, small)
enddef;
-
% \end{macrocode}
%
% \DescribeRoutine{pathcenter}
@@ -5400,11 +6205,11 @@ enddef;
% finds the intersection of the perpendicular bisectors of two chords.
% \begin{macrocode}
vardef pathcenter expr p =
- save a, cntr, n; pair cntr, a[];
+ save a, cntr, n; pair cntr, a[];
n := length p;
a1 = pnt 0 (p);
a3 = pnt [n/2] (p);
- if cycle p :
+ if cycle p:
a2 = pnt [n/4] (p);
a4 = pnt [3n/4] (p);
else:
@@ -5417,46 +6222,59 @@ vardef pathcenter expr p =
enddef;
% \end{macrocode}
+%
+% The next four commands create certain circles associated to
+% triangles. The triangle is specified as a path expression, so they
+% produce results for any path, but make sense only for a cyclic
+% triangular path.
+%
% \DescribeRoutine{circumcircle}
+% This is just the circle through the three corners.
+%
% \DescribeRoutine{incircle}
-% \DescribeRoutine{excircle}
+% This produces the circle inside the triangle that is tangent to all
+% three sides. It makes use of the fact that the two tangent
+% points on the sides adjacent to corner \gbc{A} (for example) are
+% equidistant from \gbc{A}. The three equations then express the fact that
+% the sum of the two distances from the tangent point to the corners on
+% the same side add up to the length of the side.
+% \DescribeRoutine{excircle} In \gbc{excircle}, a corner is given (by
+% number from $0$ to $2$) and the circle is produced that is
+% \emph{outside} the triangle and is tangent to the side opposite the
+% point and tangent to the extensions of the other two sides.
+%
% \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.
+% This circle passes through the following nine points: the midpoint of
+% each side, the point on each side (extended, if necessary) where the
+% altitude from the opposite corner meets it, and the midpoint of the
+% segments connecting each corner to the intersection of the altitudes.
% \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;
+ 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))
+ save a, b, c, D, E, F;
+ D := abs (B-A) = a + b;
+ E := abs (C-B) = b + c;
+ F := abs (A-C) = a + c;
+ circleppp ((a/D)[A,B], (b/E)[B,C], (c/F)[C,A])
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
+ save A, B, C; pair A, B, C;
+ A := pnt[n] (t);
+ B := pnt[n + 1] (t);
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))
+ save a, b, c, D, E, F;
+ D := abs (B-A) = a - b;
+ E := abs (C-B) = b + c;
+ F := abs (C-A) = a - c;
+ circleppp ((a/D)[A,B], (b/E)[B,C], (c/F)[A,C])
enddef;
vardef ninepointcircle expr t =
@@ -5465,30 +6283,28 @@ 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.
+% This is the average of the three corners of the triangle, or of any
+% path. If \gbc{t} is an open path with length $n$ and the nodes are
+% $x\sb0$ through $x\sb n$, the barycenter is
+% $$ \frac{1}{n+1}\sum\sb{j=0}\sp{n} x\sb j. $$
+% If \gbc{t} is a cycle with $x\sb n = x\sb0$, then it is
+% $$ \frac{1}{n}\sum\sb{j=0}\sp{n-1} x\sb j. $$
+%
+% For a triangle the barycenter is the intersection of the medians. I
+% don't recall if this is the center of any important circle.
+%
+% The centers of the various circles associated with triangles can be
+% found with \gbc{pathcenter}. Or by intersecting vasious lines: the
+% \emph{incenter} (center of the inscribed circle)is the intersection of
+% the angle bisectors; the \emph{circumcenter} is the intersection of the
+% prependicular bisectors.
% \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
+ save m; m := length t if not cycle t: + 1 fi;
+ pnt0(t)/m for k = 1 upto m - 1: + pnt[k](t)/m endfor
enddef;
+
% \end{macrocode}
%
% \DescribeRoutine{sector}
@@ -5504,15 +6320,14 @@ 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.
+% \subsection{Plotting of functions}\label{functionplots}
%
-% All of these macros call \gbc{mkfcn}.
+% In these macros, if the boolean argument \gbc{sm} is true then the
+% path returned will be a B\'ezier, otherwise it will be a polyline. 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.
+% These two parameters are simply passed to \gbc{mkpath} by \gbc{mkfcn},
+% and all these macros call \gbc{mkfcn}.
%
% \DescribeRoutine{mkfcn}
% In this command the text parameter \gbc{pf} should be the name of a
@@ -5520,30 +6335,33 @@ enddef;
% 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.
+% \gbc{pf} and the resulting pairs stored in an array. Then either
+% \gbc{mkpoly} or \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)}
+% end at \gbc{pf(bmax)} even if that is not an integer multiple of
+% \gbc{bst}.
+%
+% \DescribeRoutine{tfcn}This is included for backward compatibility.
% \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;
+vardef mkfcn (expr sm, 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)
+ mkpath (sm, tens, false, _p)
enddef;
-% compatibility:
-def tfcn (expr smooth) = mkfcn (smooth, default_tension) enddef;
+
+def tfcn (expr sm) = mkfcn (sm, default_tension) enddef;
% \end{macrocode}
%
@@ -5561,9 +6379,9 @@ def tfcn (expr smooth) = mkfcn (smooth, default_tension) enddef;
% 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;
+def parafcn (expr sm) = tparafcn (sm, default_tension) enddef;
vardef tparafcn (expr sm, tn) (expr bmin, bmax, bst) (text pf) =
- save _fp; vardef _fp (expr t) = pf enddef;
+ save _fp; vardef _fp (expr t) = pf enddef;
mkfcn (sm, tn) (bmin, bmax, bst) (_fp)
enddef;
@@ -5574,34 +6392,46 @@ enddef;
% 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)
+vardef xfcn (expr sm) (expr xmin, xmax, st) (text _fx) =
+ save _fp; vardef _fp (expr _x) = (_x, _fx(_x)) enddef;
+ mkfcn (sm, 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.
+% This is like \gbc{xfcn} but its last argument, instead of a function,
+% is a text argument that can be copied literally 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.
+% the \cs{btwnfcn} macro. One could code it in \mfpic{} macros with two
+% calls to \gbc{function}.
+%
+% \DescribeRoutine{belowfcn}
+% This is essentially \gbc{btwnfcn} with the first function identically
+% 0, but it more efficiently graphs $0$ with one straight line
+% rather than several end-to-end.
% \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)
+def function (expr sm) = tfunction (sm, default_tension) enddef;
+vardef tfunction (expr sm, tens, xmin, xmax, st) (text _fx) =
+ save _fp; vardef _fp (expr x) = (x, _fx) enddef;
+ mkfcn (sm, 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) =
+def btwnfcn (expr sm) = tbtwnfcn (sm, default_tension) enddef;
+vardef tbtwnfcn (expr sm, tn, xlo, xhi, st)(text _fx)(text _gx) =
tfunction (sm, tn) (xlo, xhi, st) (_fx) --
( reverse tfunction (sm, tn) (xlo, xhi, st) (_gx) ) -- cycle
enddef;
+def belowfcn (expr sm) = tbelowfcn (sm, default_tension) enddef;
+vardef tbelowfcn (expr sm, tn, xlo, xhi, st)(text _fx) =
+ (xlo,0)--(xhi,0)--
+ (reverse tfunction (sm, tn, xlo, xhi, st)(_fx))--cycle
+enddef;
+
% \end{macrocode}
%
% \DescribeRoutine{rfcn}
@@ -5610,32 +6440,1304 @@ enddef;
% 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)
+vardef rfcn (expr sm, tmin, tmax, st) (text ft) =
+ save _fq; vardef _fq (expr t) = (ft(t)) * (dir t) enddef;
+ mkfcn (sm, 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$).
+% This is like \gbc{rfcn}, but with a text argument containing code that
+% can be copied literally into a \mfc{vardef} creating a numeric function
+% with a literal \gbc{t} as the parameter (representing $\theta$).
+%
+% \DescribeRoutine{btwnplrfcn} This is the polar version of \gbc{btwnfcn}.
% \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)
+def plrfcn (expr sm) = tplrfcn (sm, default_tension) enddef;
+vardef tplrfcn (expr sm, tens, tmin, tmax, st) (text ft) =
+ save _fq; vardef _fq (expr t) = (ft) * (dir t) enddef;
+ mkfcn (sm, tens) (tmin, tmax, st) (_fq)
+enddef;
+
+def btwnplrfcn (expr sm) = tbtwnplrfcn (sm, default_tension) enddef;
+vardef tbtwnplrfcn (expr sm, tn, tlo, thi, st)(text _ft)(text _gt)=
+ tplrfcn (sm, tn, tlo, thi, st) (_ft) --
+ ( reverse tplrfcn (sm, tn, tlo, thi, st) (_gt) ) -- cycle
enddef;
+def plrregion (expr sm) = tplrregion (sm, default_tension) enddef;
+vardef tplrregion (expr sm, tn, tlo, thi, st) (text _ft) =
+ (0,0)--tplrfcn (sm, tn, tlo, thi, st ) (_ft)--cycle
+enddef;
% \end{macrocode}
%
+% \DescribeRoutine{mklevelset}
+% This command assumes \gbc{inside_levelset} has been defined, which
+% should be a boolean-valued function of two variables. It tries to create
+% a path such that the expression is true inside the path and false
+% outside it. The intended application is to obtain a path surrounding a
+% region like $\lbrace (x,y) \mid F(x,y) > 0 \rbrace$.
%
-% \section{Pie charts and bar charts}
+% The parameters are
+% \begin{itemize}
+% \item \gbc{sm}: Boolean, smooth path (true) or polyline.
+% \item \gbc{tens}: Numeric, the tension (if \gbc{sm} is true).
+% \item \gbc{X} and \gbc{Y}: A starting point where \gbc{_inside_}
+% should return true.
+% \item \gbc{t}: Numeric, a step size.
+% \item \gbc{a}, \gbc{b}, \gbc{c}, and \gbc{d}: Numeric, the limits
+% beyond which the search routine will not go. This is needed to
+% get a starting range for the binary chop method of \mfc{solve}.
+% In practice, the extent of the \mfpic{} figure will be used.
+% \end{itemize}
%
-% \DescribeRoutine{piechart}
-% The \gbc{piechart} command calculates the wedges of a pie from the text
+% We use \mfc{solve} to find the first point $z\sb1$ to the right of the
+% given point that is on the edge of the region. Then we find the first
+% point of intersection between the circle at $z\sb1$ with radius \gbc{t}
+% and the edge of the region. Continue from this new point to the next
+% until (one hopes) we are within a distance \gbc{t} of the first point.
+% The radius \gbc{t} should be in coordinates appropriate for the use:
+% graph coordinates when used in \mfpic{} figures.
+%
+% The tolerance used in the first \mfc{solve} is \gbc{t/50}. In later uses
+% it is an angle parameter and is set to a number of degrees sufficient to
+% give a distance tolerance at least that.
+%
+% If the starting point \gbc{(X,Y)} does not actually satisfy the
+% condition, a one point path is returned.
+% \begin{macrocode}
+numeric tolerancefactor;
+tolerancefactor := .02;
+vardef mklevelset (expr sm, tens, X, Y, t, a, b, c, d) =
+ save _inside_;
+ vardef _inside_ (expr U, V) =
+ inside_levelset (U, V) and (a < U) and (U < b)
+ and (c < V) and (V < d)
+ enddef;
+ if not _inside_ (X, Y):
+ GBwarn "Invalid seed point for levelset.";
+ pairmax((a,c), pairmin((X,Y), (b,d)))&cycle
+ else:
+ save ls, W, A, B, prev, curr, seed;
+ pair ls[], prev, curr, seed;
+ seed := (X,Y);
+ ls := 0; W := 0;
+
+ save _first_, _next_, get_next;
+ vardef _first_ (expr U) = _inside_ (U, Y) enddef;
+ vardef _next_ (expr ang) =
+ _inside_ (X_curr + t * cosd ang, Y_curr + t * sind ang)
+ enddef;
+ def get_next (expr angA, angB) =
+ X_curr := xpart curr; Y_curr := ypart curr;
+ ls[incr ls] := curr + t * dir (solve _next_ (angA, angB));
+ prev := curr; curr := ls[ls];
+ W := W + anglefromto (prev - seed, curr - seed);
+ enddef;
+
+ interim tolerance := t*tolerancefactor;
+ ls[incr ls] := (solve _first_ (X, b), Y);
+ curr := ls[ls];
+ interim tolerance := radian*tolerancefactor;
+ get_next (180, 0);
+ for n = 3 upto max_points:
+ A := angle (curr - prev);
+ get_next (A + 120, A - 120);
+ exitif ((abs(W) > 180) or (ls > 10))
+ and (abs(ls[ls] - ls1) < 1.2t);
+ endfor
+ mkpath (sm, tens, true) (ls)
+ fi
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{levelset}
+% This is the \mfpic{} interface. It checks the \gbc{t} parameter before
+% passing it to \gbc{mklevelset}, making sure it is not zero, it passes
+% appropriate limits, and defines boolean function \gbc{mklevelset}
+% expects with literal \mfc{x} and \mfc{y} as parameters, using the text
+% parameter \gbc{cond}.
+% \begin{macrocode}
+numeric max_points;
+max_points := 2000;
+
+def levelset (expr s) = tlevelset (s, default_tension) enddef;
+vardef tlevelset (expr smth, tens, seed, seg) (text cond) =
+ save inside_levelset, _t;
+ vardef inside_levelset (expr x, y) = cond enddef;
+ _t := if seg <= 0: emax (xpos-xneg, ypos-yneg)/max_points * 20
+ else: seg fi;
+ mklevelset (smth, tens, xpart seed, ypart seed, _t)
+ (xneg, xpos, yneg, ypos)
+enddef;
+
+% \end{macrocode}
+%
+% \section{Modification of Paths}\label{modification}
+%
+% \subsection{Closing a path}\label{closing}
+%
+% In \MF{} one closes a path with 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 commads take an undelimited path expression as the last
+% parameter and return a cycle (closed path). If the path is already
+% closed, it is returned unchanged.
+%
+% All the closure commands have a version with a tension parameter when
+% that makes sense. Those versions create the join 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. If the first and last point are
+% already equal, we just use the \mfc{\&} path join.
+% \begin{macrocode}
+vardef lclosed expr f =
+ f
+ if not cycle f:
+ if pnt0(f) = pnt[infinity](f): & else: -- fi cycle
+ fi
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{sclosed}
+% 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 can
+% be different. It has a variant \DescribeRoutine{sclosedt}\gbc{sclosedt}
+% that takes a tension argument.
+% \begin{macrocode}
+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: pnt0(f)..tension t..pnt1(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. It has a tense variant
+% \DescribeRoutine{bclosedt}\gbc{bclosedt}
+% \begin{macrocode}
+def bclosed = bclosedt (default_tension) enddef;
+vardef bclosedt (expr t) expr f =
+ f
+ if not cycle f:
+ if pnt0(f) = pnt[infinity](f): & else: ..tension t.. fi cycle
+ fi
+enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{uclosed}
+% Same as \gbc{bclosed}. Retained for backward compatibility. There is
+% a tense variant only for \DescribeRoutine{uclosedt}consistency.
+% \begin{macrocode}
+def uclosed = bclosed enddef;
+def uclosedt = bclosedt enddef;
+
+% \end{macrocode}
+% \DescribeRoutine{cbcontrols}
+% 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 a path. It is used twice in \gbc{cbclosed} on a path
+% and on its reverse. The appropriate three points are passed in the array
+% \gbc{t}.
+% 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 cbcontrols (suffix b, t) =
+ b1 := 2[t3, t2];
+ b2 := 2[t2, t1];
+ 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 =
+ save n; n := length f;
+ if cycle f: f
+ elseif n = 0: f&cycle
+ else:
+ save p, q, t; pair p[], q[], t[];
+ t1 := pnt0(f); t2 := post0(f); t3 := pre1(f);
+ cbcontrols (p, t); % defines p1 to p4
+ t1 := pnt[n](f); t2 := pre[n](f); t3 := post[n-1](f);
+ cbcontrols (q, t); % 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 seemed wrong to be able to close with a cubic B-spline but not a
+% quadratic B-spline; therefore I have 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 (\gbc{mkqbs} on an array of $n$
+% points makes $n-2$ links).
+% \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}
+%
+% \DescribeRoutine{arccomplement}
+% Getting the complement of an arc is easy if the arc is specified by
+% three points. So we just select three points on the arc and do that.
+% The \gbc{setpairs} statement makes \gbc{pp1}, \gbc{pp2} and \gbc{pp3}
+% three points on the arc \gbc{p} in order. The arc we want goes from
+% \gbc{pp3} to \gbc{pp1} with angle twice that of the corner angle at
+% \gbc{pp2}.
+% \begin{macrocode}
+vardef arccomplement expr p =
+ if cycle p: onepointpath (false, pnt0(p))
+ else:
+ setnumeric (nn) length p;
+ setpairs (pp) (pnt0(p), pnt[.5nn](p), pnt[nn](p));
+ arcpps (pp3,pp1,2*cornerangle(pp2,pp3,pp1))
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \subsection{Trimming a path}\label{trimming}
+%
+% \DescribeRoutine{cutoffbefore}
+% This is a useful utility operation present in \file{plain.mp} (as
+% \mfc{cutbefore}) 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.
+%
+% \DescribeRoutine{cutoffafter}
+% What it and \gbc{cutoffafter} do is return the second path with the
+% part before\slash after the first path removed.
+% \begin{macrocode}
+%<MF>path cuttings;
+vardef cutoffbefore (expr b) expr f =
+ save t, n; n := length f;
+ if n > 0:
+ for k = 1 upto n:
+ exitif (subpath (0,k) of f) intersects b;
+ endfor
+ if _Xtime < 0:
+ cuttings := pnt0 (f){0,0};
+ f
+ else:
+ cuttings := subpath (0,_Xtime) of f;
+ subpath (_Xtime, n) of f
+ fi
+ else: f
+ fi
+enddef;
+
+vardef cutoffafter (expr b) expr f =
+ setpath (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{Creating arrows}\label{arrows}
+%
+% 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 + 3z\sb2(1-t)t^2 + z\sb3t^3
+% \]
+% has a tangent at $z\sb0$ equal to the first one of $z\sb{j} -z\sb0$
+% that is nonzero.
+%
+% \DescribeRoutine{__dir}
+% \gbc{__dir} gets the direction at point 0 for an arbitrary path.
+% \gbc{postdirection}
+% \DescribeRoutine{postdirection}
+% reduces to this case using \mfc{subpath}. If the postdirection is
+% $(0,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. If that is $(0,0)$, the path is trivial.
+% \DescribeRoutine{predirection}
+% \gbc{predirection} just runs \gbc{postdirection} on the reversed path.
+%
+% \DescribeRoutine{trivial}
+% This returns \mfc{true} if a path has direction vector $(0,0)$ (as
+% determined by \gbc{__dir}).
+% \begin{macrocode}
+vardef predirection@# (expr p) =
+ - postdirection[length p - @#] (reverse p)
+enddef;
+
+vardef postdirection@# (expr p) =
+ save _n; _n := length (p);
+ setpair (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; exitif v <> origin;
+ v := pre [ n ] (p) - w; exitif v <> origin;
+ v := pnt [ n ] (p) - w; exitif v <> origin;
+ endfor
+ sgn v
+enddef;
+
+vardef trivial expr p = (__dir (p) = origin) 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 path 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;
+boolean hfilled;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{headshape}
+% The following utility not only adjusts the above parameters, but
+% creates the arrowhead paths as it does so. 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}. The paths include two harpoon
+% tips, an arrowhead, and the paths that give regions that will be erased
+% when requested.
+% \begin{macrocode}
+def headshape (expr wr, tens, fil) =
+ interim hdwdr := wr;
+ interim hdten := if tens>0: tens else: default_tension fi;
+ if hdten < .75: hdten := .75; fi
+ setboolean (hfilled) fil;
+ mkheadpaths;
+enddef;
+def mkheadpaths =
+ save Arrowhead, Leftharpoon, Rightharpoon;
+ path Arrowhead, Leftharpoon, Rightharpoon,
+ Arrowhead.clear, Leftharpoon.clear, Rightharpoon.clear;
+ Rightharpoon := (0,0){down}..tension hdten..(.5hdwdr,-1);
+ Rightharpoon.clear := Rightharpoon--(.5hdwdr,0)--cycle;
+ Leftharpoon := (reverse Rightharpoon) xscaled -1;
+ Leftharpoon.clear := (reverse Rightharpoon.clear) xscaled -1;
+ Arrowhead := Leftharpoon & Rightharpoon;
+ Arrowhead.clear := Leftharpoon.clear & Rightharpoon.clear & cycle;
+ if hfilled:
+ Arrowhead := Arrowhead--cycle;
+ Rightharpoon := Rightharpoon--(0,-1)--cycle;
+ Leftharpoon := Leftharpoon--(0,-1)--cycle;
+ fi
+enddef;
+headshape (1,1,false);
+
+% \end{macrocode}
+%
+% \DescribeRoutine{ahead}
+% This command draws an arrowhead. Current code for arrows no longer uses
+% it. \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} is as long as the arrowhead and 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 draw it and fill it,
+% otherwise we just draw it. (To only fill it, make the pen width $0$.)
+% \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 I should have followed
+% the pattern of other macros. However, \gbc{head} was never a user-level
+% macro and it didn't seem to mattered at the time.
+% \begin{macrocode}
+%<MF>def head = ahead (headcolor) enddef;
+%<MP>def head = ahead enddef;
+
+vardef ahead (expr clr, front, back, hwr, tens, filled) =
+ settension (_tn) tens; fixtension (_tn);
+ if front <> back:
+ setpair (side) (hwr/2) * ((front-back) rotated 90);
+ setpath (f) (back + side)..tension _tn..
+ {front-back}front{back-front}..tension _tn..(back - side);
+ if clearhead:
+ safeunfill (back - side)--(front-side)--(front+side)--
+ (back+side) & f & cycle;
+ colorsafedraw (background) (back - side)--(front-side)--
+ (front+side)--(back+side) & f & cycle;
+ fi
+ if filled:
+ f := f--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 arrowhead may appear to point in the wrong
+% direction. But I know of no automatic way of making it always look
+% correct. Therefore \grafbase{} and \mfpic{} have provided a means to
+% micro-adjust the head.
+%
+% The various arrow creation commands take a path expression \gbc{f} in
+% graph coordinates, puts a head/tail/decoration on it and returns
+% \gbc{f}. There are also four parameters determining (i)~the color,
+% (ii)~the size, (iii)~a rotation adjustment, and (iv)~a position
+% adjustment.
+%
+% \DescribeRoutine{headpath}\RoutineIndex{colorheadpath}
+% \RoutineIndex{headpathx}\RoutineIndex{colorheadpathx}
+% \gbc{headpath} calls \gbc{Gheadpath}, a more general command that takes
+% a boolean expression and a shape (path) suffix as arguments. It
+% supplies \mfc{false} for the boolean and \gbc{Arrowhead} for the shape.
+% The \gbc{headpathx} version differs only in that the boolean is
+% \mfc{true}. The \gbc{color...} versions call \gbc{colorGheadpath} in the
+% same way, but require that a color parameter follow.
+% \begin{macrocode}
+def headpath = Gheadpath (false) (Arrowhead) enddef;
+def headpathx = Gheadpath (true) (Arrowhead) enddef;
+
+def colorheadpath = colorGheadpath (false) (Arrowhead) enddef;
+def colorheadpathx = colorGheadpath (true) (Arrowhead) enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{Gheadpath}\RoutineIndex{colorGheadpath}
+% \RoutineIndex{Gheadpathx}\RoutineIndex{colorGheadpathx}
+% For general arrowhead shapes we require two paths; one giving the shape
+% of the head and the other the shape that is cleared when the boolean
+% parameter \gbc{trim} is true. We pass this information by name with a
+% suffix parameter \gbc{ah} that names the head shape. Then \gbc{ah.clear}
+% names the cleared region. If \gbc{ah} is a cycle, the head is filled,
+% otherwise it is drawn with the current \gbc{drawpen}.
+% If one wants it drawn and center erased, one could place first the
+% solid (filled) version with color \mfc{background}, then the outline
+% (drawn only) version.
+%
+% We also need to know which point on the path is the tip, and
+% \gbc{ah.tip} provides that. If \gbc{ah.clear} is undefined, the clearing
+% is silently skipped. If \gbc{ah.tip} is unknown, it is taken to be
+% \mfc{(0,0)}. The head shape is assumed to be initially defined
+% pointing \mfc{up} (to match predefined shapes like \gbc{Triangle} and
+% \gbc{Diamond}).
+%
+% The standard symbols for \gbc{plot} have both the clearing path and
+% the tip defined. Thus one can produce \gbc{Diamond}-tipped arrows. The
+% old arrow heads are given by \gbc{Arrowhead}, which is redefined with
+% every call to \gbc{headshape}.
+%
+% If the \gbc{rot} and \gbc{pos} parameters are zero, the head is placed
+% with its tip at the end of the path, pointing in the direction of the
+% path at that point. Otherwise, the head is rotated around the tip by the
+% amount \gbc{rot} and \emph{then} shifted backward along its new
+% direction by the amount \gbc{pos}.
+%
+% We have a version that takes a color parameter. The simpler version
+% simply calls it with \gbc{clr} equal to \gbc{headcolor}.
+% \begin{macrocode}
+def Gheadpath (expr trim) (suffix ah) =
+ colorGheadpath (trim) (ah) (headcolor)
+enddef;
+vardef colorGheadpath
+(expr trim) (suffix ah) (expr clr, sc, rot, pos) expr f =
+ if (sc <> 0) and (known ah) and (path ah):
+ convertpath (_g) f;
+ setpair (_P) predirection[length _g] (_g);
+ if _P <> origin:
+ _P := _P rotated rot;
+ setnumeric (_ang) anglefromto (up, _P);
+ _P := pnt[length _g] (_g) - pos * _P;
+ setpair (_tip) if known ah.tip: ah.tip else: origin fi;
+
+ if trim:
+ if known ah.clear:
+ safeunfill (ah.clear shifted - _tip)
+ scaled sc
+ rotated _ang
+ shifted _P;
+ fi
+ setnumeric (_ys) max(bp, penwd, last_dot_size);
+ safeunfill cut_path
+%<MP> xscaled sc yscaled _ys
+%<MF> xscaled ceiling sc yscaled ceiling _ys
+ rotated _ang shifted _P;
+ fi
+ if cycle ah: colorsafefill else: colorsafedraw fi (clr)
+ (ah shifted -_tip)
+ scaled sc
+ rotated _ang
+ shifted _P;
+ fi
+ fi
+ f
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{tailpath}\RoutineIndex{colortailpath}
+% This places a tail at the start of a path. It is almost like
+% \gbc{Gheadpath} except there is no clearing done and the tip is at the
+% start (point 0) of the path. Also, the position parameter \gbc{pos} is a
+% forward shift.
+%
+% \DescribeRoutine{midpath}\RoutineIndex{colormidpath}
+% This is just like \gbc{tailpath} except it puts the given shape
+% somewhere in the middle of the path. Its position parameter indicates
+% the fraction of the length of the path where the shape is to be placed.
+% This works best in two cases: the shape has a definite direction (like
+% the \gbc{Arrowhead}) and the tip is placed at the given position, or the
+% shape has a center of symmetry and that is placed at the given position.
+% We obtain this in most cases by shifting $(0,0)$ to that position. The
+% standard arrowhead has its tip at this point, and the standard symbols
+% (with the exception of \gbc{Circle}) have their center of symmetry there.
+% \begin{macrocode}
+def tailpath (suffix sh) = colortailpath (sh) (headcolor) enddef;
+vardef colortailpath (suffix sh) (expr clr, sc, rot, pos) expr f =
+ if (sc <> 0) and (known sh) and (path sh):
+ convertpath (_g) f;
+ setpair(_P) postdirection0 (_g);
+ if _P <> origin:
+ _P := _P rotated rot;
+ if cycle sh: colorsafefill else: colorsafedraw fi (clr)
+ (sh if known sh.tip: shifted -sh.tip fi)
+ scaled sc
+ rotated anglefromto (up, _P)
+ shifted (pnt0 (_g) + pos * _P);
+ fi
+ fi
+ f
+enddef;
+
+def midpath (suffix sh) = colormidpath (sh) (headcolor) enddef;
+vardef colormidpath (suffix sh) (expr clr, sc, rot, pos) expr f =
+ if (sc <> 0) and (known sh) and (path sh):
+ convertpath (_g) f;
+ setnumeric (_t) pathtime[pos] (_g);
+ setpair (_P) postdirection[_t] (_g);
+ if _P <> origin:
+ _P := _P rotated rot;
+ if cycle sh: colorsafefill else: colorsafedraw fi (clr)
+ sh scaled sc
+ rotated anglefromto (up, _P)
+ shifted (pnt[_t] (_g));
+ fi
+ fi
+ f
+enddef;
+
+% \end{macrocode}
+%
+% \subsection{Randomizing a path}
+%
+% In order to randomly change a path, we need to randomly change its
+% points and its controls. If we just apply independent random shifts to
+% every point and control point, it could happen that the direction from a
+% point to a control changes dramatically, introducing a wild change even
+% with a small shift (if point and control were very close to begin with).
+% Also, this method would almost guarantee that a smooth path would
+% randomize into one with all corners. Our solution to these problems is
+% in the following paragraph.
+%
+% If $z\sb0$ and $z\sb3$ are the start and end points, with controls
+% $z\sb1$ and $z\sb2$, then we randomize $z\sb0$ and $z\sb3$ using a
+% random shift with size supplied as a parameter. If there was a
+% preceeding segment, its ending angle and the angle of $z\sb1-z\sb0$
+% determine an angle difference which we multiply by a random factor.
+% This determines the direction to the new control point. If there was no
+% preceeding segment we rotate $z\sb1-z\sb0$ a random amount. Finally we
+% randomly scale $|z\sb1-z\sb0|$.
+%
+% The following `\gbc{deviate}s' are analogous to \MF{}'s
+% \mfc{uniformdeviate}. The first, \DescribeRoutine{signeddeviate}
+% \gbc{signeddeviate X}, produces a random number uniformly distributed in
+% $(-X, X)$. The second, \DescribeRoutine{scaledeviate}
+% \gbc{scaledeviate (W, A)}, produces a pair in a particular direction
+% with length distributed in $(2^{-w}, 2^w)$. The third,
+% \DescribeRoutine{polardeviate}\gbc{polardeviate R} produces a pair
+% whose polar coordinates are separately uniformly distributed, the radius
+% over the interval $(0, R)$ the angle over $(0,360)$. The last,
+% \DescribeRoutine{xydeviate}\gbc{xydeviate (X,Y)}, produces a pair
+% uniformly distributed over the rectangle with corners at $(-X,-Y)$ and
+% $(X,Y)$.
+%
+% \DescribeRoutine{randompair}
+% Finally, \gbc{randompair} runs \gbc{polardeviate} if \gbc{X} is
+% numeric and \gbc{pairdeviate} if it is a pair.
+% \begin{macrocode}
+vardef signeddeviate primary X =
+ (uniformdeviate 1)[-X,X]
+enddef;
+vardef scaledeviate (expr W, A) =
+ 2 ** (signeddeviate W) * dir A
+enddef;
+vardef polardeviate primary R =
+ (uniformdeviate abs(R)) * dir uniformdeviate 360
+enddef;
+vardef xydeviate primary Z =
+ (signeddeviate (xpart Z), signeddeviate (ypart Z))
+enddef;
+
+vardef randompair (expr maxshift) =
+ if numeric maxshift: polardeviate (maxshift)
+ elseif pair maxshift: xydeviate (maxshift)
+ else: (0,0)
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{randompath}
+% The \gbc{randompath} macro returns a path formed from \gbc{f} by
+% shifting each of its point with independent instances of
+% \gbc{randompair (maxshift)}. Its control points are also modified, but
+% the algorithm is not as simple.
+%
+% Let $X$ be one of the points of \gbc{f}, with precontrol $X-U$ and
+% postcontrol $X+V$. We create random $X'$, $U'$ and $V'$ as follows.
+%
+% Let $S$ be the pair that results from \gbc{randompair(maxshift)}, let
+% $w$ be the value of \gbc{weirdness}, let $\eta\sb j(w)$ be the value of
+% the $j$th instance of \gbc{signeddeviate ($w$)}.
+% Then $X' = X + S$, $U'$ is $U$ rotated $\beta=30\eta\sb1(w)$ and scaled
+% $\sigma = 2^{\eta\sb2(w)}$. Let $\alpha$ be the angle between the two
+% vectors $U$ and $V$. Then $V'$ is $\sigma V$ rotated to make the angle
+% between $U'$ and $V'$ equal to $\alpha 2^{\eta\sb3(w)}$.
+% In the new path, the point is $X'$ with precontrol $X'-U'$ and
+% postcontrol $X' + V'$. Note that if the path is smooth at $X$, then
+% $\alpha = 0$ and the new angle is also $0$.
+%
+% \DescribeRoutine{randomlines}
+% This is a simpler version that simply shifts the nodes and connects
+% the results with straight lines. It is intended to be applied to
+% polyline paths.
+%
+% \DescribeRoutine{detrivialized}
+% We start with a routine that strips out trivial segments from a path.
+% This makes some loops a lot easier. We wouldn't want to differently
+% shift the two (equal) endpoints of a trivial segment, making it
+% nontrivial.
+% \begin{macrocode}
+vardef detrivialized expr f =
+ save g; path p, g[]; g := 0;
+ for k = 1 upto length f:
+ p := subpath (k-1,k) of f;
+ if not trivial p: g[incr g] := p; fi
+ endfor
+ if g = 0: onepointpath (cycle f, pnt0(f))
+ else: g1 for k = 2 upto g: &g[k] endfor if cycle f: &cycle fi
+ fi
+enddef;
+
+vardef randompath (expr maxshift, weirdness) expr f =
+ save g, n; path g;
+ g := detrivialized f;
+ n := length g;
+ if n = 0:
+ f shifted randompair (maxshift)
+ else:
+ save X, U, V;
+ pair X[], U[], V[];
+ if cycle g: n := n - 1; fi
+ for k = 0 upto n:
+ X[k] := pnt[k](g);
+ U[k] := X[k] - pre[k](g);
+ V[k] := post[k](g) - X[k];
+ endfor
+ save A, B;
+ for k := 0 upto n:
+ X[k] := X[k] shifted randompair (maxshift);
+ A := anglefromto (U[k],V[k]);
+ B := signeddeviate (30weirdness);
+ U[k] := X[k] - (U[k] zscaled scaledeviate (weirdness,B));
+ B := B - A + A * (2 ** signeddeviate weirdness);
+ V[k] := X[k] + (V[k] zscaled scaledeviate (weirdness,B));
+ endfor
+ X0 for k = 1 upto n:
+ .. controls V[k-1] and U[k] .. X[k]
+ endfor
+ if cycle g:
+ .. controls V[n] and U0 .. cycle
+ fi
+ fi
+enddef;
+
+vardef randomlines (expr maxshift) expr f =
+ save g, n; path g;
+ g := detrivialized f;
+ n := length g;
+ if n = 0:
+ f shifted randompair (maxshift)
+ else:
+ if cycle g: n := n - 1; fi
+ (pnt0(g) shifted randompair (maxshift))
+ for k = 1 upto n:
+ -- (pnt[k](g) shifted randompair (maxshift))
+ endfor
+ if cycle g:
+ -- cycle
+ fi
+ fi
+enddef;
+% \end{macrocode}
+%
+% \subsection{Parallelling a path}
+%
+% \DescribeRoutine{parasegment}
+% This creates a path parallel to a given cubic Bezier segment \gbc{f}.
+% It should be called by a command (such as \gbc{parapath}) that makes
+% sure \gbc{f} is nontrivial (meaning the directions are non-zero). It
+% splits the segment into subsegments for accuracy. Its arguments are the
+% distance the original path is shifted, the number of subsegments to
+% split into, and the path.
+% \begin{macrocode}
+vardef parasegment (expr d, segs, f) =
+ if d = 0: f
+ else:
+ save u, v, t; pair u[], v[];
+ for n = 0 upto segs:
+ t := n/segs;
+ u[n] := postdirection [t] (f);
+ v[n] := pnt[t] (f) + (u[n] zscaled (0,d));
+ endfor
+ v0{u0}
+ for n = 1 upto segs: ...v[n]{u[n]} endfor
+ fi
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{parapath}
+% Attempt to parallel one path with another at a distance \gbc{d}. The
+% algorithm is as follows:
+% \begin{enumerate}
+% \item Create an array of paths that parallel each segment of \gbc{f}.
+% \item Redo the array by adding a half circle at each end. The purpose
+% of the half circle is to force consecutive elements of the array
+% to intersect (I hope).
+% \item Do something like \mfc{buildcycle}: where consecutive array
+% elements intersect, strip the the elements to the subpaths between
+% intersection points, and join them directly.
+% \item If \gbc{f} is a cycle, process the joining at the endpoints in
+% the same way.
+% \end{enumerate}
+% We use \gbc{force_equal_ends} to force exact equality of endpoints so
+% we can join segments with \mfc{\&}. The reason for prefering the
+% \mfc{\&} join is to permit better performance of macros (such as this
+% one) that take a path apart into segments. If we used `\mfc{..}' instead
+% then the returned path would have a great many added segments, nearly
+% all of which are nearly trivial. For similar reasons we drop trivial
+% segments of \gbc{f}.
+% \begin{macrocode}
+vardef parapath (expr d) expr f =
+ if d = 0:
+ f
+ else:
+ save a, g, h, p, q, s, t, u, v, w;
+ path g[], h, p[], q[];
+ numeric a, s, t;
+ pair u, v, w, w[];
+ s := emax (3, emin (segment_split, ceiling (max_points/5/length f)));
+ p := 0;
+ for i = 1 upto length f:
+ h := subpath (i-1, i) of f;
+ if not trivial h:
+ q[incr p] := h;
+ p[p] := parasegment (d, s, h);
+ fi
+ endfor
+ if p = 0:
+ f
+ else:
+ a := if d>0: - fi 180;
+ h := p1;
+ for i = 1 upto p-1:
+ u := predirection 1 (q[i]);
+ v := postdirection 0 (q[i+1]);
+ w1 := pnt 1 (q[i]) - (u zscaled (0,d));
+ w2 := pnt 0 (q[i+1]) - (v zscaled (0,d));
+ w3 := pnt [infinity] (h);
+ w4 := pnt 0 (p[i+1]);
+ g0 := arcpps(w3, w1, a);
+ g1 := h & g0;
+ g2 := arcpps(w2, w4, a) & p[i+1];
+ if (p[i] & g0) intersects reverse g2:
+ s := length g2 - _Ytime;
+ t := length h - length p[i] + _Xtime;
+ g1 := subpath (0, t) of g1;
+ g2 := subpath (s, length g2) of g2;
+ force_equal_ends (g1, g2);
+ h := g1 & g2;
+ else:
+ h := h .. p[i+1];
+ fi
+ endfor
+
+ if cycle f:
+ u := predirection 1 (q[p]);
+ v := postdirection 0 (q[1]);
+ w1 := pnt 1 (q[p]) - (u zscaled (0,d));
+ w2 := pnt 0 (q[1]) - (v zscaled (0,d));
+ w3 := pnt [infinity] (h);
+ w4 := pnt 0 (p[1]);
+ g3 := arcpps(w3, w1, a);
+ g0 := arcpps(w2, w4, a);
+ g1 := g0 & h & g3;
+ g2 := g0 & p[1];
+ if (p[p] & g3) intersects reverse g2:
+ s := length g2 - _Ytime;
+ t := length g0 + length h - length p[p] + _Xtime;
+ g1 := subpath (s, t) of g1;
+ force_equal_ends (g1, g1);
+ h := g1 & cycle;
+ else:
+ h := h..cycle;
+ fi
+ fi
+ h
+ fi
+ fi
+enddef;
+
+vardef turnangle@# (expr f) =
+ anglefromto(predirection@# (f), postdirection@#(f))
+enddef;
+
+% \end{macrocode}
+%
+% \section{Miscellaneous}\label{misc}
+%
+% \subsection{Implementation of \mfpic{}'s \cs{plotdata} command}%
+% \label{plotdata}
+%
+% 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{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. Since a dash pattern need only
+% be an array, we check if it is one and, if it is, we copy it to the
+% next \gbc{dashtype[n]}. We actually copy it to a temporary array and
+% make sure there are at least two patterns before we overwrite
+% \gbc{dashtype}.
+%
+% \DescribeRoutine{getdashpat}
+% We have removed this mod-ing operation from \TeX, where it is
+% cumbersome, to \MF, where it is trivial.
+% \begin{macrocode}
+def setdatadashes (text lst) =
+ save __type; __type := 0;
+ forsuffixes _itm = lst:
+ if knownnumericarray _itm :
+ copyarray (_itm) (__type[__type]);
+% __type := __type + 1;
+ next __type;
+ else: GBwarn "Improper dash pattern in setdatadashes.";
+ fi
+ endfor
+ if __type > 1:
+ save dashtype; dashtype := __type;
+ for _j = 0 upto dashtype - 1:
+ copyarray (__type[_j]) (dashtype[_j]);
+ endfor
+ else:
+ SetdataWarn "dashes";
+ fi
+enddef;
+def getdashpat expr n = dashtype[n mod dashtype] enddef;
+
+def SetdataWarn expr s =
+ GBwarn "command setdata"& s &"() failed. Previous values retained.";
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{defaultdashes}
+% These are the default 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 (unless the user inexplicably
+% sets \mfc{linecap} to \mfc{butt}).
+% \begin{macrocode}
+numeric Solid, Simpledash, Simpledot, Dotdash, Dotdashdot, Dotdashdash;
+dashpat (Solid) (0);
+dashpat (Simple_dash) (3bp, 4bp);
+dashpat (Simple_dot) (0, 4bp);
+dashpat (Dot_dash) (0, 4bp, 3bp, 4bp);
+dashpat (Dot_dash_dot) (0, 4bp, 3bp, 4bp, 0, 4bp);
+dashpat (Dot_dash_dash) (0, 4bp, 3bp, 4bp, 3bp, 4bp);
+
+numeric dashtype, dashtype[], dashtype[][];
+def defaultdashes =
+ setdatadashes (Solid, Simple_dash, Simple_dot,
+ Dot_dash, Dot_dash_dot, Dot_dash_dash);
+enddef;
+defaultdashes;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{setdatasymbols}
+% This can be used to define the sequence of point plotting styles for
+% \mfpic's \cs{plotdata} command. It is quite similar to
+% \gbc{setdatadashes} above and \gbc{setdatacolors} below.
+%
+% \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 __type; path __type[];
+ __type := 0;
+ for _itm = lst:
+ if (known _itm) and (path _itm):
+ __type[__type] := _itm;
+ next __type;
+ else:
+ GBwarn "Improper path in setdatasymbols().";
+ fi
+ endfor
+ if __type > 1:
+ save pointtype; pointtype := __type;
+ path pointtype[];
+ for _j = 0 upto pointtype - 1:
+ pointtype[_j] := __type[_j];
+ endfor
+ else:
+ SetdataWarn "symbols";
+ fi
+enddef;
+def getsymbol expr n := pointtype[n mod pointtype] enddef;
+
+% \end{macrocode}
+%
+% Before we can set the default symbols we need to define some. They need
+% to be paths. The ones below named with `\gbc{Solid}' are closed paths.
+% Since the drawing commands that use them 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. The scaling factors are the square root of the
+% ratios of the areas.
+%
+% Associated with each is a another path with the same basename and the
+% suffix \gbc{clear} and a pair with the suffix \gbc{tip}. Moreover,
+% they are (mostly) symmetric about the $y$-axis pointing (where that
+% makes sense) in the direction \mfc{up}. The purpose of all this is so
+% that they can be plugged into code for adding arrowheads/tails of
+% different shapes.\VariableIndex{Triangle}\VariableIndex{Square}
+% \VariableIndex{Circle}\VariableIndex{Diamond}\VariableIndex{Star}
+% \VariableIndex{Plus}\VariableIndex{Cross}\VariableIndex{Asterisk}
+% \VariableIndex{Crossbar}\VariableIndex{Leftbar}\VariableIndex{Rightbar}
+% \VariableIndex{Righthook}\VariableIndex{Lefthook}
+% \VariableIndex{SolidTriangle}\VariableIndex{SolidSquare}
+% \VariableIndex{SolidCircle}\VariableIndex{SolidDiamond}
+% \VariableIndex{SolidStar}
+% \begin{macrocode}
+def DeclareGBSymbols (text S) =
+ forsuffixes _itm = S:
+ path _itm;
+ path _itm.clear;
+ pair _itm.tip;
+ endfor
+enddef;
+DeclareGBSymbols(
+ Triangle, Square, Circle, Diamond, Star, Plus, Cross,
+ Asterisk, Crossbar, Leftbar, Rightbar, Righthook,
+ Lefthook, SolidTriangle, SolidSquare, SolidCircle,
+ SolidDiamond, SolidStar
+);
+
+vardef undo_cycle expr f = subpath (0, length f) of f enddef;
+
+SolidTriangle := (up--(dir 210)--(dir -30)--cycle) scaled .78;
+Triangle := undo_cycle SolidTriangle;
+
+Triangle.clear := SolidTriangle.clear :=
+ ((dir -30)--(cosd 30,1)--(cosd 210,1)--(dir 210)--up--cycle)
+ scaled .78;
+
+SolidSquare := (up--(-1,1)--(-1,-1)--(1,-1)--(1,1)--cycle) scaled .443;
+Square := undo_cycle SolidSquare;
+
+SolidCircle := fullcircle rotated 90;
+Circle := undo_cycle SolidCircle;
+Circle.clear := SolidCircle.clear :=
+ halfcircle--(-.5,.5)--(.5,.5)--cycle;
+
+SolidDiamond := (up--left--down--right--cycle)
+ scaled .522 yscaled 1.44;
+Diamond := undo_cycle SolidDiamond;
+Diamond.clear := SolidDiamond.clear :=
+ (right--(1,1)--(-1,1)--left--up--cycle) scaled .522 yscaled 1.44;
+
+% As arrow heads, these have their "tips" at their center.
+Plus := ((0,0)--up--down--(0,0)--left--right) scaled .65;
+Plus.clear := (right--(1,1)--(-1,1)--(left)--cycle) scaled .65;
+
+Cross := ((0,0)--(dir 45)--(dir -135)--(0,0)--(dir -45)--(dir 135))
+ scaled .65;
+Cross.clear := ((0,0)--(dir -45)--dir(45)--(dir 135)--(dir -135)--cycle)
+ scaled .65;
+
+Asterisk := ((0,0)--up--down--(0,0)--(dir 30)--(dir -150)
+ --(0,0)--(dir -30)--(dir 150)) scaled .6;
+Asterisk.clear := ((0,0)--(dir -30)--(cosd 30,1)--(cosd 150,1)
+ --(dir -150)--cycle) scaled .6;
+
+Crossbar := ((0,0)--left--right) scaled .65;
+Crossbar.clear := rect (right,(-1,.5)) scaled .65;
+
+Leftbar := ((0,0)--left);
+Rightbar := ((0,0)--right);
+Leftbar.clear := rect((0,0),(-1,.5));
+Rightbar.clear := rect((0,0),(1,.5));
+
+Righthook := arcpps((0,0),(1,0),180);
+Lefthook := Righthook xscaled -1;
+Righthook.clear := Righthook--cycle;
+Lefthook.clear := Lefthook--cycle;
+
+% \end{macrocode}
+%
+% We do some computations to find the vertices of an n-pointed
+% star. We assume that \mfc{A1} is \mfc{up} and the line from there
+% to \mfc{A[1 + m]} determines one side of the top point of the star.
+% We must have $2 \le {}$\gbc{m}${}\le{}$\gbc{n}${}-2$. The rest of the
+% vertices are determined by symmetry. \gbc{Star} is made with \gbc{n=5}
+% and \gbc{m=2}. We store the points in an array so we can use them for
+% the \gbc{Star.clear} path.
+% \begin{macrocode}
+vardef mkstar (expr n, m) (suffix A) =
+ save ang; ang := 360/n;
+ A1 := up; A3 := up rotated ang;
+ A2 = (whatever)[A1, A1 rotated ( ang*m)];
+ A2 = (whatever)[A3, A3 rotated (-ang*m)];
+ for i = 4 upto 2n:
+ A[i] := A[i-2] rotated ang;
+ endfor
+ A := 2n;
+ mkpoly (true, A)
+enddef;
+
+save _A; pair _A[];
+SolidStar := mkstar (5, 2, _A) scaled .84;
+Star := undo_cycle SolidStar;
+Star.clear := polyline (true)
+ (_A9, _A10, _A1, _A2, _A3, (xpart _A3, 1), (xpart _A9, 1)) scaled .84;
+SolidStar.clear := Star.clear;
+
+forsuffixes S =
+ Triangle, Square, Circle, Diamond, Star, Plus, Cross,
+ Asterisk, Crossbar, Leftbar, Rightbar, Righthook,
+ Lefthook, SolidTriangle, SolidSquare, SolidCircle,
+ SolidDiamond, SolidStar :
+ S.tip := point 0 of S;
+endfor
+% \end{macrocode}
+%
+% \DescribeRoutine{gcd}
+% I thought I was going to use \gbc{gcd} for the \gbc{mkstar} routine
+% above, but went another way. Still, it might have a future use. Once we
+% have it, \DescribeRoutine{lcm}\gbc{lcm} is a snap. Since \gbc{gcd}
+% always returns a positive result, \gbc{lcm} satisfies the usual rule for
+% signs of products.
+% \begin{macrocode}
+vardef gcd (expr n, m) =
+ save a, b, r;
+ a := emax (abs(m), abs(n));
+ b := emin (abs(m), abs(n));
+ if b > 0:
+ forever:
+ r := a mod b;
+ exitif r < 1;
+ a := b; b := r;
+ endfor
+ b
+ else:
+ a
+ fi
+enddef;
+
+vardef lcm (expr n, m) =
+ n*m/gcd(n, m)
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeVariable{cut_path}
+% Additional clearing path, almost the same as plain.mf's \gbc{cut_} (no
+% \gbc{cut_} in plain.mp) but rotated, and scaled differently. The odd
+% scaling is so that if yscaled by the diameter of a dot, and the dot
+% happens to be digitized to a square shape, then the \gbc{cut_path},
+% centered at the center of the dot and rotated 45 degrees, will encompass
+% the whole square (theoretically).
+% \begin{macrocode}
+path cut_path; cut_path := (.5,0)--(.5,.71)--(-.5,.71)--(-.5,0)--cycle;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{defaultsymbols}
+% The command for restoring the default symbols.
+% \begin{macrocode}
+numeric pointtype; path pointtype[];
+def defaultsymbols =
+ setdatasymbols( Circle, Cross, SolidDiamond, Square, Plus,
+ Triangle, SolidCircle, Star, SolidTriangle);
+enddef;
+defaultsymbols;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{setdatacolors}
+% Finally, for \MP, we do a similar pair of commands for setting
+% the colors for the \cs{plotdata} command, and for
+% \DescribeRoutine{getcolor}getting the next one.
+%
+% \DescribeRoutine{defaultcolors}
+% These 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.
+% \begin{macrocode}
+def setdatacolors (text lst) =
+ save __type; color __type[];
+ __type := 0;
+ for _itm = lst:
+ if (known _itm) and (color _itm):
+ __type[__type] := _itm;
+ next __type;
+ else: GBwarn "Improper color in setdatacolors().";
+ fi
+ endfor
+ if __type > 1:
+ save colortype; colortype := __type;
+ color colortype[];
+ for _j = 0 upto colortype - 1:
+ colortype[_j] := __type[_j];
+ endfor
+ else:
+ SetdataWarn "colors";
+ fi
+enddef;
+def getcolor expr n = colortype[n mod colortype] enddef;
+
+color dBlue, dOrange, dGreen, dMagenta, dCyan, dYellow;
+dBlue := 0.80blue + .2white;
+dOrange := 0.66yellow + .34red;
+dGreen := 0.80green;
+dMagenta := 0.85magenta;
+dCyan := 0.85cyan;
+dYellow := 0.85yellow;
+
+numeric colortype; color colortype[];
+def defaultcolors =
+ setdatacolors(black, red, dBlue, dOrange, dGreen,
+ dMagenta, dCyan, dYellow);
+enddef;
+defaultcolors;
+
+% \end{macrocode}
+%
+%
+% \subsection{Pie Charts and Bar Charts}\label{charts}
+%
+% \DescribeRoutine{computepie}
+% The \gbc{computepie} 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
@@ -5644,31 +7746,92 @@ enddef;
% \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;
+% \DescribeRoutine{piechart}
+% This calls \gbc{computepie} to calculate the angles and store that in
+% the array \gbc{_dat}, then \gbc{mkpiewedges} creates the actual user
+% level arrays. This separation allows us (in a future enhancement) to
+% easily handle named piecharts, so there can be more than one defined at
+% a time.
+% \begin{macrocode}
+def computepie (suffix dat) (expr sign, ang, cent, rad) (text data) =
+begingroup
+ save _tot, _max, _toobig;
+ _max := 0; dat := 0;
+ for _val = data:
+ dat[incr dat] := _val;
+ _max := emax (_max, _val);
+ endfor
+ if dat=0: GBwarn "piechart attempted with empty list.";
+ _toobig := 1;
+ else:
+ _toobig := infinity/dat;
+ fi
+ if _max > _toobig:
+ for _idx = 1 upto dat:
+ dat[_idx] := dat[_idx]/_toobig;
+ endfor
+ fi
+ for _idx = 2 upto dat:
+ dat[_idx] := dat[_idx - 1] + dat[_idx];
endfor
- _tot := pieangle[piewedge];
- pair piecenter; piecenter := cent;
+ _tot := dat[dat];
+ for _idx = dat downto 2:
+ dat[_idx] := ang + sign*dat[_idx-1]/_tot*360;
+ endfor
+ dat1 := ang; dat[dat + 1] := ang + 360sign;
+endgroup
+enddef;
+
+def piechart (expr sign, ang, cent, rad) (text data) =
+ save _dat;
+ computepie (_dat) (sign, ang, cent, rad) (data);
+ mkpiewedges (_dat, cent, rad);
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{mkpiewedges}
+% 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}
+def mkpiewedges (suffix dat) (expr cent, rad) =
+ numeric piewedge, piedirection, pieangle, pieangle[];
+ pair piecenter, piedirection[];
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] ]);
+ piecenter := cent;
+ piedirection := pieangle := piewedge := dat;
+ for _idx = 1 upto dat:
+ pieangle[_idx] := dat[_idx];
+ piewedge[_idx] := sector (piecenter, rad, dat[_idx], dat[_idx+1]);
+ piedirection[_idx] := dir(0.5[ dat[_idx], dat[_idx+1] ]);
+ endfor
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{namedpiechart}
+% This is a future enhancement. It takes a suffix, the name of the chart
+% which will be the base name of the various arrays and key values, and
+% will be a numeric equal to the number of wedges. The first part is
+% identical to that of \gbc{piechart} above. Note that some elements
+% of the chart are not (yet) directly accessible. Those needed by mfpic
+% are: the paths, the directions and the center. The rest would be easy
+% to add.
+% \begin{macrocode}
+def namedpiechart (suffix nm) (expr sign, ang, cent, rad) (text data) =
+ save _dat;
+ computepie (_dat) (sign, ang, cent, rad) (data);
+ setnumeric (nm) _dat;
+ pair nm.center, nm.direction[];
+ path nm.wedge[];
+ nm.center := cent;
+ for _idx = 1 upto _dat:
+ nm.wedge[_idx] := sector (cent, rad, _dat[_idx], _dat[_idx+1]);
+ nm.direction[_idx] := dir(0.5[ _dat[_idx], _dat[_idx+1] ]);
endfor
- piedirection := pieangle := piewedge;
enddef;
% \end{macrocode}
@@ -5677,56 +7840,85 @@ enddef;
% 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.
+% vertical if \gbc{vert} is true, otherwise horizontal.
%
-% \gbc{start} is the location (on the appropriate axis) of the start of
+% \gbc{firstbar} 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
+% 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 yparts),
+% \gbc{barbegin[\,]} holds their leftmost or bottommost coordinates (either
+% 0 or the xparts 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
+% If the data are pair data, this command uses the xpart as the beginning
+% of the bar and the ypart 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.
+% 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 omit it.
% \begin{macrocode}
-def barchart (expr start, sep, r, vert)(text data) =
+def barchart (expr firstbar, 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;
+ 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;
+ for _nn = 1 upto chartbar:
+ barstart[_nn] := firstbar + sep*(_nn-1);
+ barlength[_nn] := barend[_nn];
+ chartbar[_nn] := rect ((barbegin[_nn], 0), ( barend[_nn], barwd))
+ shifted (0, barstart[_nn]) if vert: xyswap fi;
+ endfor
+enddef;
+
+% \end{macrocode}
+%
+% \DescribeRoutine{namedbarchart}
+% This is a future enhancement. It takes a suffix, the name of the chart,
+% which will be the base name of the path array. Note that the various key
+% elements (ends of the bar, etc.) are not (yet) directly accessible as in
+% the above version, but they would be easy to add.
+% \begin{macrocode}
+def namedbarchart (suffix nm) (expr first, sep, r, vert) (text data) =
+ save nm;
+begingroup
+ save _bb, _ee, _ww;
+ path nm.bar[];
+ nm := 0; _ww := r*sep;
+ for _itm = data:
+ _ee := if pair _itm: ypart _itm else: _itm fi;
+ _bb := if pair _itm: xpart _itm else: 0 fi;
+ nm.bar[incr nm] := rect ((_bb, 0), ( _ee, _ww) )
+ shifted (0, first + sep*(nm-1)) if vert: xyswap fi;
endfor
+endgroup
enddef;
% \end{macrocode}
%
%^^A Overlays - taken from MFbook, p 295. (Bruce Leban)
%
-% \section{Overlays}
+% \subsection{Overlays}\label{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.
+% This final code predates me. When I inherited \mfpic{} it contained no
+% use of \gbc{keepit}. For the \MP{} version I just tried to make sure
+% everything was defined in \MP{} or \file{plain.mp} and otherwise left it
+% alone. One might presumably issue \gbc{keepit} periodically, clearing
+% \mfc{currentpicture} after adding it onto \gbc{totalpicture}. This would
+% save some memory because manipulating \mfc{currentpicture} often
+% requires a couple of copies to be around simultaneously. Right now we
+% treat currentpicture as the place to save things and manipulate local
+% picture variables.
% \begin{macrocode}
picture totalpicture;
boolean totalnull, currentnull;
@@ -5736,10 +7928,10 @@ def clearit =
enddef;
def keepit =
-%<MF> mono (currentpicture);
addto totalpicture also currentpicture;
+%<MF> mono (totalpicture);
currentpicture := nullpicture;
- totalnull := currentnull;
+ totalnull := totalnull or currentnull;
currentnull := true;
enddef;
@@ -5749,15 +7941,14 @@ def addto_currentpicture =
enddef;
def mergeit (text do) =
- if totalnull :
+ if totalnull:
do currentpicture
- elseif currentnull :
+ elseif currentnull:
do totalpicture
else:
begingroup
- save _v_; picture _v_;
+ save _v_; picture _v_;
_v_ := currentpicture;
-%<MF> mono (_v_);
addto _v_ also totalpicture;
do _v_
endgroup
@@ -5765,36 +7956,55 @@ def mergeit (text do) =
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}.
+% This implements \mfpic{}'s \cs{stopshipping} and \cs{resumeshipping}
+% commands. It used to be that those commands wrote a redefinition of
+% \gbc{shipit}, now we write a boolean assignment and the \cs{shipit}
+% command tests it.
% \begin{macrocode}
-def shipit_ =
- mergeit (shipout)
-enddef;
-def shipit = shipit_ enddef;
+boolean noship; noship := false;
+def shipit = if noship: else: mergeit (shipout) fi enddef;
%<*MF>
-def showit_ =
- mergeit (show_)
-enddef;
-def show_ suffix v =
- display v inwindow currentwindow
-enddef;
+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.
+% Here we initialize \gbc{gcode} (which current versions of mfpic do not
+% use) for hacked \mfpic{} files that require it. In \MP{} we load the
+% color definitions. And thats all.
% \begin{macrocode}
-numeric gcode; gcode := 0;
+numeric gcode; gcode := 0;
%<MF>% end grafbase.mf
+%<MP>input dvipsnam.mp;
%<MP>% end grafbase.mp
%</MF|MP>
% \end{macrocode}
%
+% \subsection{Dvips names for colors}\label{dvipsnam}
+%
+% In order to make \file{dvipsnam.mp} useful outside grafbase, we repeat
+% the definition of \gbc{cmyk} here.
+% \begin{macrocode}
+%<*dvips>
+if unknown grafbaseversion:
+ 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;
+ vardef cmyk (expr c, m, y, k) =
+ (snapto 1-c-k, snapto 1-m-k, snapto1-y-k)
+ enddef;
+fi
+
+% \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
@@ -5804,9 +8014,9 @@ numeric gcode; gcode := 0;
%^^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:
+% Declare all the dvips color names to be color variables, and define
+% them as in \file{dvipsnam.def}:
% \begin{macrocode}
-%<*dvips>
color Apricot, Aquamarine, Bittersweet, Black, Blue, BlueGreen,
BlueViolet, BrickRed, Brown, BurntOrange, CadetBlue, CarnationPink,
Cerulean, CornflowerBlue, Cyan, Dandelion, DarkOrchid, Emerald,
@@ -5819,79 +8029,74 @@ 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);
+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>
diff --git a/Master/texmf-dist/source/generic/mfpic/mfpic.dtx b/Master/texmf-dist/source/generic/mfpic/mfpic.dtx
index 1789f6e9146..daab8c0d9b6 100644
--- a/Master/texmf-dist/source/generic/mfpic/mfpic.dtx
+++ b/Master/texmf-dist/source/generic/mfpic/mfpic.dtx
@@ -1,43 +1,42 @@
% \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.
+% Copyright 2002--2006, Daniel H. Luecking
%
% 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
+% LaTeX Project Public License, either version 1.3b 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.3b or later is part of all distributions of LaTeX version
+% 2003/12/01 or later.
%
-% 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.
+% Mfpic has maintenance status "author-maintained". The Current Maintainer
+% is Daniel H. Luecking. There are several Base Interpreters: plain TeX, LaTeX,
+% plain Metafont and plain MetaPost.
%
%<*driver>
+\ProvidesFile{mfpic.dtx}
+%</driver>
+%<sty>\ProvidesPackage{mfpic}
+%<*sty>
+ [2006/05/26 v0.9. Macros for drawing with Metafont/MetaPost.]%
+%</sty>
+%<*driver>
\documentclass[draft]{ltxdoc}
-\def\fileversion{0.7a beta}
-\def\filedate{2004/04/16}
-
\addtolength{\textwidth}{.5878pt}
-\def\mytt{\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$}}
+\def\mytt{\upshape\mdseries\ttfamily}
+\renewcommand\marg[1]{{\mytt\{#1\}}}
+\renewcommand\oarg[1]{{\mytt [#1]}}
+\renewcommand\parg[1]{{\mytt (#1)}}
+\renewcommand \arg[1]{{\mytt \##1}}
+\renewcommand\#{\char`\#\relax}
+\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{meta\-font}}
@@ -48,7 +47,8 @@
\def\mfc#1{{\mytt#1}}
\let\env\mfc
\let\file\mfc
-\let\gbc\mfc
+\let\gbc \mfc
+\catcode`\_=12
\renewcommand\{{\char`\{}
\renewcommand\}{\char`\}}
\renewcommand\|{${}\mathrel{|}{}$}
@@ -66,11 +66,15 @@
\renewcommand\labelitemiii{\textperiodcentered}
\leftmargini\parindent
% Stupid index!
+\def\usage#1{\textrm{#1}}
+\def\index@prologue{\section*{Index}\markboth{Index}{Index}%
+ Numbers refer to the page where the corresponding entry is described.
+}
\def\IndexParms{%
\parindent \z@ \columnsep 15pt
\parskip 0pt plus 1pt
\rightskip 5pt plus2em \mathsurround \z@
- \parfillskip=-5pt \small
+ \parfillskip-5pt \small
% less hanging:
\def\@idxitem{\par\hangindent 20pt}%
\def\subitem{\@idxitem\hspace*{15pt}}%
@@ -99,8 +103,7 @@
\end{document}
%</driver>
%\fi
-%
-% \CheckSum{4392}
+% \CheckSum{4902}
% \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
@@ -117,19 +120,19 @@
% Grave accent \` Left brace \{ Vertical bar \|
% Right brace \} Tilde \~}
%
-% \catcode`\_=12
+% \GetFileInfo{mfpic.dtx}
% \maketitle
%
% \begin{abstract}
-% An environment, \env{mfpic}, is defined, within which commands can
-% be used for drawing pictures, especially graphs of mathematical
-% functions. These commands actually write code to an external file
-% which can be processed through \MF{} or \MP{} (depending on package
-% options) to produce the picture. The picture is then included on a
-% second pass through \TeX.
-%
-% This file documents the source code. See \file{mfpicdoc.tex} for the
-% user documentation.
+% An environment, \env{mfpic}, is defined, within which commands can be
+% used for drawing pictures, especially of mathematical objects and
+% functions. These commands actually write code to an external file to be
+% processed through \MF{} or \MP{} (depending on package options) to
+% produce the picture. The picture is then included on a second pass
+% through \TeX.
+%
+% This file documents the source code. See \file{mfpman.pdf} or
+% \file{mfpman.tex} for the user documentation.
% \end{abstract}
%
% \StopEventually{\PrintIndex}
@@ -139,7 +142,9 @@
% \section{Introductory setup}\label{intro}
%
% \Mfpic{} is designed to work with plain \TeX, \LaTeX, \pdfTeX, and
-% \pdfLaTeX.
+% \pdfLaTeX. I consistently use it with eplain and AMS\LaTeX{}
+% without problems. Users have reported succesfully using it with \AmSTeX.
+% It has \emph{not} been successfully used with \ConTeXt.
%
% \subsection{Versions numbers, category codes, etc.}\label{category}
%
@@ -150,23 +155,21 @@
% files where various punctuation were made active (e.g., with
% \file{french.sty}).
%
-% \DescribeMacro{\mfpfileversion}
-% \DescribeMacro{\mfpfiledate}
-% \DescribeMacro{\mfpicversion}
-% We keep track of version numbers here. We use \cs{mfpfileversion}
-% because \cs{fileversion} is used by very many \LaTeX{} packages.
-% This is important, because later we write the version information to the
+% We keep track of version numbers here. We store the version and date
+% in macros and later we write this version information to the
% output file, The \cs{mfpicversion} is 100 times the numeric part of the
% version. The output file can compare this number with the corresponding
% value in \file{grafbase} and detect installations where there is a
% mismatch.
% \begin{macrocode}
%<*tex>
-{\catcode\lq\.12 \catcode\lq\/12%
-\gdef\mfpfileversion{0.7a beta}%
-\gdef\mfpfiledate{2004/04/16}%
-\gdef\mfpicversion{70}}%
-\ifx\MFPicpackagE\UndEfInEd\else\expandafter\endinput\fi%
+\ifx\mfpfileversion\UndEfInEd\else\expandafter\endinput\fi%
+{%
+ \catcode\lq\.12 \catcode\lq\/12%
+ \gdef\mfpfileversion{0.9}%
+ \gdef\mfpfiledate{2006/05/26}%
+ \gdef\mfpicversion{90}%
+}%
% \end{macrocode}
%
% A bug in older versions of some support files is triggered by the macro
@@ -177,82 +180,71 @@
\let\+\tabalign%
% \end{macrocode}
%
-% \DescribeMacro{\MFPrestorecode}
% We now get rid of unusually catcoded punctuation, space and EOL.
-% We save all the current category codes in the macro \cs{MFPicpackagE}
+% We save all the current category codes in the macro \cs{mfp@restoreALL}
% and restore them at the end by executing this command.
-% We try to assume only: that letters, numbers,
-% `\texttt\bsl', `\texttt\{', `\texttt\}', `\texttt\#' and
-% `\texttt\%' have the usual categories. Styles (e.g., \prog{french})
-% that make punctuation active foul up \cs{write}\,s of MF/MP code. This can
-% still occur in user-generated \cs{write}\,s, so maybe some code should be
-% added in \cs{mfpic} or \cs{mfsrc} command too.
-% \begin{macrocode}
-\edef\MFPrestoreequal{\catcode61\space\the\catcode61\relax}%
-\catcode61 12\relax%
-\edef\MFPrestorespace{\catcode32=\the\catcode32\relax}%
-\catcode32=10\relax%
-\edef\MFPrestoreEOL{\catcode13=\the\catcode13\relax}%
-\catcode13=5\relax
-\edef\MFPrestorelq{\catcode96=\the\catcode96\relax}%
-\catcode96=12
-\def\MFPrestorecode#1{\catcode`\string#1=\the\catcode`#1\relax}%
-% \end{macrocode}
-%
-% \DescribeMacro{\MFPicpackagE}
-% This will be invoked at the end of the file to restore all the catcodes.
-% This will fail if some of these characters have really bad codes
-% (comment or escape, for example) but that would likely be the case only
-% if {\tt\bsl} or {\tt\%} have some different codes and then everything
-% above here would fail.
-% \begin{macrocode}
-\edef\MFPicpackagE{%
- \MFPrestorecode{@}\MFPrestorecode{-}%
- \MFPrestorecode{:}\MFPrestorecode{;}%
- \MFPrestorecode{.}\MFPrestorecode{,}%
- \MFPrestorecode{!}\MFPrestorecode{?}%
- \MFPrestorecode{(}\MFPrestorecode{)}%
- \MFPrestorecode{[}\MFPrestorecode{]}%
- \MFPrestorecode{'}\MFPrestorecode{/}%
- \MFPrestorecode{"}\MFPrestorecode{*}%
- \MFPrestorecode{$}\MFPrestorecode{+}%
- \MFPrestorecode{_}\MFPrestorecode{>}%
- \MFPrestorecode{<}\MFPrestorecode{~}%
- \MFPrestorelq\MFPrestoreEOL\MFPrestorespace
- \MFPrestoreequal\catcode 10=\number\catcode10}%
-% \end{macrocode}
-%
-% Set everything to normal except \texttt @=letter, \mfc{_}=other, and
-% \texttt \textasciitilde=other. Do this by putting the settings in a
-% macro and invoking the macro. That makes it possibly to redo the
-% settings inside some other macro, should that ever become necessary.
-% Characters 10 and 13 are \verb|^^J| and \verb|^^M|, control-J and
-% control-M.
-%
-% \begin{macrocode}
-\catcode`\@=11
-\ifx\@makeother\UndEfInEd \def\@makeother#1{\catcode`#1=12\relax}\fi
-\def\mfp@sanitize{%
-\catcode10=12\catcode13=5\catcode`\$=3\catcode`\ =10
-\@makeother\:\@makeother\;\@makeother\.\@makeother\,%
-\@makeother\!\@makeother\?\@makeother\(\@makeother\)%
-\@makeother\[\@makeother\]%
-\@makeother\'\@makeother\/\@makeother\"\@makeother\*%
-\@makeother\_\@makeother\`\@makeother\+\@makeother\=%
-\@makeother\>\@makeother\<\@makeother\~\@makeother\-}%
-\mfp@sanitize
-\edef\restorenewlinechar{\newlinechar=\number\newlinechar\relax}%
-\chardef\mfp@ctrlJ10 \chardef\mfp@ctrlM13
-\newlinechar\mfp@ctrlJ\def\@nl{^^J}%
+% We assume that letters, numbers, `\texttt\bsl', `\texttt\{', `\texttt\}',
+% `\texttt\#' and `\texttt\%' have the usual categories. However, styles
+% (e.g., \prog{french}) that make punctuation active foul up \cs{write}\,s
+% of MF/MP code. This can still occur in user-generated \cs{write}\,s, so
+% maybe some of this code should be added in \cs{mfpic} or \cs{mfsrc}
+% command, too.
+% \begin{macrocode}
+{%
+ \catcode61\space12\relax% 61=equal sign
+ \catcode32=10\relax% 32=space
+ \catcode13=5 % 13=EOL
+ \catcode96=12 % 96=left quote
+ \catcode`\@=11
+ \gdef\MFPsavecodes{%
+ \edef\mfp@restoreNLchar{\newlinechar\number\newlinechar\relax}%
+ \edef\mfp@restoreELchar{\endlinechar\number\endlinechar\relax}%
+ \edef\mfp@restoreSP{\catcode32=\number\catcode32\relax}%
+ \edef\mfp@restoreEOL{\catcode13=\number\catcode13\relax}%
+ \edef\mfp@restoreALL{\mfp@restorecode{=}\mfp@restorecode{@}%
+ \mfp@restorecode{$}\mfp@restorecode{:}\mfp@restorecode{;}%
+ \mfp@restorecode{.}\mfp@restorecode{,}\mfp@restorecode{!}%
+ \mfp@restorecode{?}\mfp@restorecode{(}\mfp@restorecode{)}%
+ \mfp@restorecode{[}\mfp@restorecode{]}\mfp@restorecode{<}%
+ \mfp@restorecode{>}\mfp@restorecode{*}\mfp@restorecode{/}%
+ \mfp@restorecode{+}\mfp@restorecode{-}\mfp@restorecode{"}%
+ \mfp@restorecode{'}\mfp@restorecode{_}\mfp@restorecode{`}%
+ \mfp@restoreEOL\mfp@restoreSP\catcode10=\number\catcode10
+ \mfp@restoreNLchar\mfp@restoreELchar}}%
+ \gdef\mfp@restorecode#1{\catcode`\string#1=\number\catcode`#1\relax}%
+ \ifx\@makeother\UndEfInEd \gdef\@makeother#1{\catcode`#1=12\relax}\fi
+ \gdef\MFPsanitize{\endlinechar13 \newlinechar10
+ \catcode10=12 \catcode13=5 \catcode`\$=3 \catcode`\ =10
+ \@makeother\:\@makeother\;\@makeother\.\@makeother\,%
+ \@makeother\!\@makeother\?\@makeother\(\@makeother\)%
+ \@makeother\[\@makeother\]\@makeother\>\@makeother\<%
+ \@makeother\/\@makeother\*\@makeother\+\@makeother\-%
+ \@makeother\'\@makeother\"\@makeother\_\@makeother\`%
+ \@makeother\=\@makeother\@}%
+}%
+\MFPsavecodes\MFPsanitize
+\catcode`@11
+\chardef\mfp@ctrlJ 10 \chardef\mfp@ctrlM 13
% \end{macrocode}
%
+% \cs{MFPsavecodes} saves a bunch of things that must have (mostly)
+% normal settings \cs{mfp@restoreALL} will be invoked at the end of the
+% file to restore all the values.
+%
+% \cs{MFPsanitize} sets (almost) everything to normal. It can be used to
+% redo these settings inside some other macro, should that ever become
+% necessary. Characters 10 and 13 are \verb|^^J| and \verb|^^M|,
+% control-J and control-M.
+%
% \subsection{Debugging and messages}\label{debug}
%
-% \DescribeMacro{\ifmfpicdebug}
% \DescribeMacro{\mfpicdebug}
% To turn on debugging before option processing, the user must make sure
% \cs{mfpicdebug} is defined. \cs{mfp@DBlog} writes it contents to the
% log file only if debugging is on.
+% \DescribeMacro{\ifmfpicdebug}\cs{ifmfpicdebug} is normally set to true
+% if the user loads \mfpic{} with the \opt{debug} option, or sets it
+% directly with \cs{mfpicdebugtrue} after loading.
% \begin{macrocode}
\newif\ifmfpicdebug
\ifx\mfpicdebug\UndEfInEd \mfpicdebugfalse
@@ -261,98 +253,133 @@
\def\mfp@DBlog#1{%
\ifmfpicdebug{\let\@msgbreak\mfp@errbrk\wlog{Mfpic debug: #1}}\fi}%
% \end{macrocode}
-% Various kinds of messages to the screen and log file. \cs{mfp@msg} is only
-% used in the definition of the others and to write a blank line. We
-% always use the second, which identifies the source of the message, or
-% the third, which adds the word `warning'. The fourth is for errors
-% and provides for help if a user presses `\texttt h' in response to the
-% message. The bit with the spaces is to get a hanging indentation with
-% text properly aligned on screen.
+%
+% We need to write various kinds of messages to the screen and log file.
+% \cs{mfp@msg} is only used in the definition of the others and to write a
+% blank line. We always use the second, which identifies the source of the
+% message, or the third, which adds the word `warning'. The fourth is for
+% errors and provides for help if a user presses `\texttt h' in response
+% to the message. The bit with the spaces is to get a hanging indentation
+% with text properly aligned on screen. The basic \cs{mfp@msg} defines
+% \cs{@nl} to place the \cs{newlinechar}, for multiline messages. We
+% also define \cs{@nl} in \cs{mfp@write} for new lines in writing to a
+% file. We do not define it at all at the outer level.
% \begin{macrocode}
-\def\mfp@msg#1{{\newlinechar\mfp@ctrlJ\immediate\write16{#1}}}%
-\def\@msgbreak{^^J}%
+\def\mfp@msg#1{%
+ {\newlinechar\mfp@ctrlJ\def\@nl{^^J}\immediate\write16{#1}}}%
\def\mfp@spaces{\space\space\space\space\space\space}%
-%
\def\mfp@msgbrk{^^J\mfp@spaces\space}%
\def\mfp@errbrk{\mfp@msgbrk\mfp@spaces}%
\def\mfp@warnbrk{\mfp@errbrk\space\space}%
-%
-\def\Mfpic@msg#1{{\let\@msgbreak=\mfp@msgbrk
- \mfp@msg{Mfpic: #1 }}}%
-\def\Mfpic@warn#1{{\let\@msgbreak=\mfp@warnbrk
- \mfp@msg{^^JMfpic warning: #1}}}%
-\def\mfp@errmsg#1#2{{\newlinechar\mfp@ctrlJ
- \let\@msgbreak\mfp@errbrk
+\def\Mfpic@msg#1{{\let\@msgbreak\mfp@msgbrk\mfp@msg{Mfpic: #1}}}%
+\def\Mfpic@warn#1{%
+ {\let\@msgbreak\mfp@warnbrk\mfp@msg{\@nl Mfpic warning: #1}}}%
+\def\mfp@errmsg#1#2{{\newlinechar\mfp@ctrlJ\let\@msgbreak\mfp@errbrk
\errhelp{#2}\errmessage{Mfpic error: #1}}}%
% \end{macrocode}
%
% In \TeX{} 3.x we can identify the line being processed when a message is
-% issued. We do not support \TeX{} 2.x. The command \cs{@mfplineno} is
+% issued. We do not support \TeX{} 2.x. The command \cs{mfp@lineno} is
% for information written into the output file.
% \begin{macrocode}
-\def\@online{ on line \number\inputlineno}%
-\def\@mfplineno{line \number\inputlineno\space in TeX source}%
+\def\mfp@lineno{line \number\inputlineno\space in the TeX source}%
% \end{macrocode}
%
% \subsection{Detecting the format}\label{format}
%
% To detect its environment, \mfpic{} needs to test whether certain
-% commands are defined. The \cs{@xp} is usually used to `expand away'
-% the rest of a conditional. \cs{@XP} does this from two levels deep in
-% a conditional. (We use \cs{@xp}\cs{@XP}\cs{@xp} later to expand
-% away from three levels.) Since it uses \cs{@firstoftwo} and
-% \cs{@secondoftwo}, we cannot use it to check their existence.
-% \begin{macrocode}
-\ifx\@firstoftwo\UndEfInEd \long\def\@firstoftwo#1#2{#1}\fi
-\ifx\@secondoftwo\UndEfInEd \long\def\@secondoftwo#1#2{#2}\fi
+% commands are defined. We usually use the positive version
+% \cs{mfp@ifdefined}. The \cs{@xp} in it is used to `expand away' the rest
+% of a conditional. \cs{@XP} does this from two levels deep in a
+% conditional. Since our check uses \cs{@firstoftwo} and
+% \cs{@secondoftwo}, we cannot use it to check \emph{their} existence (but
+% we could use other means).
+%
+% \cs{mfp@ifundefined} checks its argument to see if it is defined.
+% If so, it gobbles all code up to \cs{mfp@enddef}. Otherwise that code is
+% executed. This avoids reading code prematurely (category code changes).
+%
+% We also copy \LaTeX's \cs{@ifundefined} since we have at least five
+% places where we need to supply the name without a backslash.
+% \begin{macrocode}
\let\@xp\expandafter
\def\@XP{\@xp\@xp\@xp}%
+\long\def\@firstoftwo#1#2{#1}%
+\long\def\@secondoftwo#1#2{#2}%
\def\mfp@ifdefined#1{%
\ifx#1\UndEfInEd \@xp\@secondoftwo
\else\ifx#1\relax \@XP\@secondoftwo
\else \@XP\@firstoftwo
\fi\fi}%
+\mfp@ifdefined\@empty{}{\def\@empty{}}%
+\mfp@ifdefined\@gobble{}{\def\@gobble#1{}}%
\mfp@ifdefined\@firstofone{}{\long\def\@firstofone#1{#1}}%
+\mfp@ifdefined\on@line{}%
+ {\def\on@line{ on input line \number\inputlineno}}%
+\let\mfp@enddef\@empty
+\long\def\mfp@gobbledef#1\mfp@enddef{}%
+\def\mfp@ifundefined#1{\mfp@ifdefined#1\mfp@gobbledef\@empty}%
+\mfp@ifundefined\@ifundefined
+ \def\@ifundefined#1{%
+ \@xp\ifx\csname #1\endcsname\relax \@xp\@firstoftwo
+ \else \@xp\@secondoftwo
+ \fi}%
+\mfp@enddef
+% \end{macrocode}
+%
+% The following might be useful in patching things or adding to
+% \cs{mfp@additions}.
+% \begin{macrocode}
+\mfp@ifdefined\g@addto@macro{}{%
+ \long\def\g@addto@macro#1#2{%
+ \begingroup
+ \toks@\@xp{#1#2}%
+ \xdef#1{\the\toks@}%
+ \endgroup}}%
+\mfp@ifdefined\g@prependto@macro{}{%
+ \long\def\g@prependto@macro#1#2{%
+ \begingroup
+ \toks@{#2}\toks2\@xp{#1}%
+ \xdef#1{\the\toks@\the\toks2}%
+ \endgroup}}%
% \end{macrocode}
%
-% We test for the format. If all tests fail we assume plain\TeX{} or
-% something compatible. Mainly this is needed to support \LaTeX. I've done
-% a few things to work around \ConTeXt, but its been a moving target and
-% \mfpic{} didn't work in \ConTeXt{} the last time I tried.
+% We test for LaTeX. We assume plain\TeX{}, or something compatible,
+% except in a few places where LaTeX{} does thing differently. \Mfpic{}
+% didn't work in \ConTeXt{} the last few times I tried.
%
% Here we see if \cs{documentstyle} is defined, and then make sure
% we are not in \AmSTeX{}. Probably there is a better command to test,
% and perhaps we should just test whether the functionality required is
% present, but I'm not really sure how best to do either of those.
% \begin{macrocode}
-\newif\if@mfp@latex \@mfp@latexfalse
+\newif\ifmfp@latex \mfp@latexfalse
\mfp@ifdefined\documentstyle
- {{\def\x{AmS-TeX}%
- \ifx \fmtname\x \mfp@DBlog{AmS-TeX detected.}%
- \else
- \global\@mfp@latextrue \mfp@DBlog{LaTeX detected.}%
- \fi}}%
- {\mfp@DBlog{Neither LaTeX2e nor LaTeX209 nor AmS-TeX.}}%
+ {\def\mfp@tempa{AmS-TeX}%
+ \ifx \fmtname\mfp@tempa \mfp@DBlog{AmS-TeX detected.}%
+ \else \mfp@DBlog{LaTeX detected.}\mfp@latextrue
+ \fi}%
+ {\mfp@DBlog{Neither LaTeX nor AmS-TeX detected.}}%
% \end{macrocode}
%
% We are in \LaTeXe{}, as opposed to \LaTeX2.09, if \cs{documentclass}
-% is defined.
+% is defined. The \file{mfpic.sty} now assumes \LaTeXe{}. It might still
+% be possible to use \LaTeX{}2.09 with \cs{input mfpic}, but I don't
+% care enough to test it.
% \begin{macrocode}
-\newif\if@mfp@latexe \@mfp@latexefalse
+\newif\ifmfp@latexe \mfp@latexefalse
\mfp@ifdefined\documentclass
- {\@mfp@latexetrue \mfp@DBlog{LaTeX2e detected.}}%
- {\mfp@DBlog{Not LaTeX2e.}}%
+ {\mfp@latexetrue \mfp@DBlog{LaTeX2e detected.}}%
+ {\mfp@DBlog{Not LaTeX2e.}}%
% \end{macrocode}
%
% We now test \cs{pdfoutput} to see if pdf support is needed.
% \begin{macrocode}
-\newif\if@mfp@pdf \@mfp@pdffalse
+\newif\ifmfp@pdf \mfp@pdffalse
\mfp@ifdefined\pdfoutput
{\mfp@DBlog{PdfTeX detected...}%
- \ifcase\pdfoutput
- \mfp@DBlog{...with dvi output.}%
- \else
- \@mfp@pdftrue \mfp@DBlog{...with PDF output.}%
+ \ifnum\pdfoutput<1 \mfp@DBlog{...with dvi output.}%
+ \else \mfp@pdftrue \mfp@DBlog{...with PDF output.}%
\fi}%
{\mfp@DBlog{Not pdfTeX.}}%
% \end{macrocode}
@@ -367,68 +394,148 @@
% inside \cs{tlabel} and \cs{tcaption}. \cs{@@par} (in \LaTeX) and
% \cs{endgraf} (in plain \TeX) should equal the primitive \cs{par}.
% \begin{macrocode}
-\if@mfp@latex \else \let\@@par\endgraf \fi
+\ifmfp@latex \else \let\@@par\endgraf \fi
\def\mfp@restorepar{\let\par\@@par}%
% \end{macrocode}
%
% Some \mfpic{} commands end with \cs{ignorespaces}. For some others a
% better choice is the \LaTeX{} strategy of conditionally ignoring spaces.
% \begin{macrocode}
-\if@mfp@latex\else
- \def\@namedef#1{\@xp\def\csname #1\endcsname}%
- \def\@nameuse#1{\csname #1\endcsname}%
-\fi
-\mfp@ifdefined\@bsphack{}%
- {\@nameuse{newskip}\@savsk
+\mfp@ifdefined\@namedef{}%
+ {\def\@namedef#1{\@xp\def\csname #1\endcsname}}%
+\mfp@ifdefined\@namelet{}%
+ {\def\@namelet#1{\@xp\let\csname #1\endcsname}}%
+\mfp@ifdefined\@nameuse{}%
+ {\def\@nameuse#1{\csname #1\endcsname}}%
+\mfp@ifundefined\@bsphack
+ \@nameuse{newskip}\@savsk
\@nameuse{newcount}\@savsf
\def\@bsphack{\relax
\ifhmode \@savsk\lastskip \@savsf\spacefactor \fi}%
\def\@esphack{\relax
\ifhmode \spacefactor\@savsf
\ifdim\@savsk>\z@ \ignorespaces
- \fi\fi}}%
+ \fi\fi}%
+\mfp@enddef
% \end{macrocode}
%
+% \subsection{Warning if commands are already defined}
+%
% We sometimes (either by accident or by choice) redefine things defined
% in some formats. For the accidental redefinitions we want to be told,
% and since we cannot assume \LaTeX{} with its \cs{newcommand}, we define
% a simple check and print a warning when it happens.
% \begin{macrocode}
\mfp@DBlog{Defining test for previous definitions of macros.}%
-\mfp@ifdefined\mfp@testdef
- {\Mfpic@warn{%
- Hey! How can \string\mfp@testdef\space already be defined? %
- \@msgbreak I'll redefine it anyway!}}%
- {}%
\def\mfp@testdef#1{%
\ifx#1\UndEfInEd
\else\ifx#1\relax
- \Mfpic@warn{\string #1 was previouly defined to be \string\relax.}%
+ \Mfpic@warn{\string#1 was previouly defined to be \string\relax.}%
\else
- \Mfpic@warn{\string #1 is already defined!}%
+ \Mfpic@warn{\string#1 is already defined.}%
\fi\fi}%
% \end{macrocode}
%
-% \DescribeMacro{\newdef}
-% \DescribeMacro{\newlet}
-% Is the proposed definer of new definitions itself not new? Well,
+% Is the proposed definer of new definitions itself not new? Well,
% we'd like a warning, but go ahead and define it anyway. All
% \cs{newdef} does is check whether the macro being defined is already
% defined, raise a warning (but \emph{not} an error) and define it
% anyway. We do something similar with \cs{let}.
% \begin{macrocode}
\mfp@testdef\newdef
-\mfp@testdef\newlet
\def\newdef#1{\mfp@testdef#1\def#1}%
\newdef\newlet#1{\mfp@testdef#1\let#1}%
% \end{macrocode}
%
+% \subsection{Error messages and warnings}
+%
+% If one tries to switch on \MP{} support after the output file has been
+% set up, we issue this error message.
+% \begin{macrocode}
+\newdef\mfp@useMP@error{%
+ \mfp@errmsg {Command \mfp@b usemetapost too late}%
+ {It is too late to select the metapost option.^^J%
+ It must be selected before the \opengraphsfile command.^^J%
+ If you proceed, the metafont option will be assumed.}}%
+% \end{macrocode}
+%
+% This one is for macros that need an output file to write to, but are
+% called before one has been opened.
+% \begin{macrocode}
+\def\mfp@nooutput@error#1{%
+ \mfp@errmsg {No output file has been opened}%
+ {You have tried to used a command, #1, that requires an output^^J%
+ file to be opened with the \opengraphsfile command, which^^J%
+ was either not issued or was not successful.}}%
+% \end{macrocode}
+%
+% Some options and commands require the \opt{metapost} option be in
+% effect. We define an error message for the case where it is not in
+% effect.
+% \begin{macrocode}
+\newdef\mfp@noMP@error#1{%
+ \mfp@errmsg {Metafont does not support #1, use MetaPost}%
+ {Metafont doesn't support #1. Perhaps you forgot to turn on^^J%
+ MetaPost support by using the metapost option or issuing the^^J%
+ command \usemetapost. For now, I will ignore it.}}%
+% \end{macrocode}
+%
+% This next is like \cs{mfp@useMP@error}, but it is not so specific.
+% It is for other options that also need to be issued before
+% \cs{opengraphsfile}.
+% \begin{macrocode}
+\def\mfp@afteropen@error#1{%
+ \mfp@errmsg{Improper use of command \string#1\on@line}%
+ {You must issue the command #1 prior to \opengraphsfile.
+ It will be ignored now.}}%
+% \end{macrocode}
+%
+% \cs{mfp@end} is defined to be an error message, but that is for
+% debugging purposes. It's actual use is to mark the end when we need to
+% process a list of tokens. The code should simply detect and remove it,
+% and it should never be expanded, so seeing the error message indicates
+% a coding error by me, or incorrect syntax by a user.
+% \begin{macrocode}
+\newdef\mfp@end{%
+ \mfp@errmsg {Unexpected token \string\mfp@end}%
+ {If you get this message but did not misuse the command^^J%
+ \mfp@end, it could mean you have unbalanced braces.}}%
+% \end{macrocode}
+%
+% The next two are error messages for missing or empty data files.
+% \begin{macrocode}
+\newdef\mfp@nofile@error#1{%
+ \mfp@errmsg{No data file: #1}%
+ {The data file you tried to plot was not found.}}%
+\newdef\mfp@nodata@error#1{%
+ \mfp@errmsg{Empty data file: #1}%
+ {The data file contained only empty lines and comments.}}%
+% \end{macrocode}
+%
+% This message is issued when the \cs{tlabeljustify} command (see
+% section~\ref{labelsettings}) is issued with an invalid parameter.
+% \begin{macrocode}
+\newdef\mfp@justify@error{%
+ \mfp@errmsg{Invalid justification parameter for text label}%
+ {The optional argument for an mfpic text label must be^^J%
+ one of B, b, c or t, followed by^^J
+ one of l, r or c, optionally followed by an angle.}}%
+% \end{macrocode}
+%
+% This is the default meaning assigned to \cs{@nountil} at the
+% beginning of \cs{mfploop}. Use of \cs{mfpuntil} cancels the
+% warning, which is otherwise issued by \cs{mfpendloop}.
+% \begin{macrocode}
+\newdef\mfp@untilwarn{%
+ \Mfpic@warn{\mfp@b mfploop without \string\mfpuntil!}}%
+% \end{macrocode}
+%
%
% \section{Configuring \mfpic{} behavior}\label{configuring}
%
% \subsection{Options}\label{options}
%
-% \DescribeMacro{ifmfpmpost}
+% \DescribeMacro{\ifmfpmpost}
% I have decided to make this a user-level macro so that a user can
% write code that behaves one way for \MP{} and another for \MF. However
% the commands to change the setting are private.
@@ -436,7 +543,7 @@
% Our switch to turn \MP{} support on or off needs to be global, so it
% can't be a normal \cs{newif}.
% \begin{macrocode}
-\global\let\ifmfpmpost\iffalse
+\newlet\ifmfpmpost\iffalse
\newdef\mfp@mposttrue {\global\let\ifmfpmpost\iftrue}%
\newdef\mfp@mpostfalse{\global\let\ifmfpmpost\iffalse}%
\newdef\mfp@ifmpost{%
@@ -444,29 +551,29 @@
% \end{macrocode}
%
% \DescribeMacro{\usemetafont}
-% We make a user-level commands for switching to the appropriate drawing
+% We make user-level commands for switching to the appropriate drawing
% program. This is necessary for plain\TeX, but also used internally by
% \mfpic. \CMF{} is the default. This command needs to not only clear the
% boolean \cs{ifmfpmpost}, but also restore the default graphics inclusion
% commands. Actually, it doesn't \emph{need} to unless \cs{usemetapost}
% was previously issued to change the default. This would normally not
% happen unless the user wanted to close the current graph file and open a
-% new one for \MF.
+% new one for \MF. For similar reasons, we clear the \opt{mplabels}
+% option.
%
% \MP{} support requires macros for including EPS graphics. What works
% best depends on the format, so we delay the definition of
-% \cs{usemetapost} until the \LaTeX{} options section, but we define the
-% error message here.
+% \cs{usemetapost} until the \LaTeX{} options section.
+%
+% Since \cs{mfpicllx} and \cs{mfpiclly} are globally defined by graphic
+% inclusion, we reset then to 0, in case \opt{metafont} and \opt{metapost}
+% options are mixed in one file.
% \begin{macrocode}
\newdef\usemetafont{\mfp@mpostfalse
+ \nomplabels
\def\preparemfpicgraphic##1{}%
- \def\setmfpicgraphic##1{\@graphfont\char\mfp@count}%
- \def\getmfpicoffset##1{}}%
-\newdef\MPtoolate@error{%
- \mfp@errmsg {Command \string\usemetapost\space too late.}%
- {It is too late to select the metapost option.^^J%
- It must be selected before the \opengraphsfile command.^^J%
- If you proceed, the metafont option will be assumed.}}%
+ \def\setmfpicgraphic##1{\mfp@graphfont\char\mfp@count}%
+ \def\getmfpicoffset##1{\gdef\mfpicllx{0}\gdef\mfpiclly{0}}}%
% \end{macrocode}
%
% Some options need an open \file{.mf} or \file{.mp} file before they
@@ -474,174 +581,244 @@
% been issued but not \cs{closegraphsfile}. Since those commands define and
% undefine \cs{mfp@filename}, we use that as our test.
% \begin{macrocode}
-\newdef\@ifmfpfileisopen{\mfp@ifdefined\mfp@filename}%
+\newdef\mfp@ifopengraphsfile{\mfp@ifdefined\mfp@filename}%
% \end{macrocode}
%
% \DescribeMacro{\setmfvariable}
-% \DescribeMacro{\setmpvariable}
% A useful abbreviation for a common requirement: declaring and setting
-% a \MF{} variable. I have decided to change it to a user level macro for
-% power users. \cs{setmpvariable} is another name for it. It takes three
-% arguments: the type of variable, the variable itself, and its value.
-% Example: \cs{setmfvariable}\marg{numeric}\marg{shadewd}\marg{.7pt}.
+% a \MF{} variable. I have decided to change it to a user-level macro for
+% power users.
+% \DescribeMacro{\setmpvariable}\cs{setmpvariable} is another name for it.
+% It takes three arguments: the type of variable, the variable itself, and
+% its value. Example:\\
+% \indent \cs{setmfvariable}\marg{numeric}\marg{shadewd}\marg{.7pt}.\\
% Since it \mfc{save}\,s the variable, it must not have a suffix. This is
% not a big problem since mostly it is for internal use. We document
% that fact for power users and advise ordinary users on the naming of
% \MF{} variables.
%
-% \DescribeMacro{\setmfboolean}
-% \DescribeMacro{\setmfnumeric}
-% \DescribeMacro{\setmfcolor}
-% Abbreviations for the most common internal uses. In the case of \MF,
-% color is the same numeric, but in \MP{} it is a separate variable type.
+% We have abbreviations for the most common internal uses. For example
+% \SpecialUsageIndex{\setmfboolean}\cs{setmfboolean} is the equivalent of
+% \cs{setmfvariable} with type \mfc{boolean}. A similar statement is true
+% For \SpecialUsageIndex{\setmfnumeric}\cs{setmfnumeric},
+% \SpecialUsageIndex{\setmfpair}\cs{setmfpair}, and
+% \SpecialUsageIndex{\setmfcolor}\cs{setmfcolor}.
+%
+% \DescribeMacro{\globalsetmfvariable}
+% This is like \cs{setmfvariable}, except it doesn't perform a
+% \mfc{save} and so the variable is globally defined. Also,
+% suffixed variables are permitted (but not numeric suffixes).
% \begin{macrocode}
\newdef\setmfvariable#1#2#3{%
- \@ifmfpfileisopen{\mfcmd{setvariable (#1) (#2) (#3)}}{}}%
+ \mfp@ifopengraphsfile{\mfcmd{setvariable (#1) (#2) #3}}%
+ {\mfp@nooutput@error\setmfvariable}}%
\newlet\setmpvariable\setmfvariable
-\newdef\setmfboolean#1#2{\setmfvariable{boolean}{#1}{#2}}%
-\newdef\setmfnumeric#1#2{\setmfvariable{numeric}{#1}{#2}}%
-\newdef\setmfcolor#1#2{\setmfvariable{color}{#1}{#2}}%
+\newdef\setmfboolean {\setmfvariable{boolean}}%
+\newdef\setmfnumeric {\setmfvariable{numeric}}%
+\newdef\setmfpair {\setmfvariable{pair}}%
+\newdef\setmfcolor {\setmfvariable{color}}%
+\newdef\globalsetmfvariable#1#2#3{%
+ \mfp@ifopengraphsfile{\mfcmd{gsetvariable (#1) (#2) #3}}%
+ {\mfp@nooutput@error\globalsetmfvariable}}%
+\newlet\globalsetvariable\globalsetmfvariable
% \end{macrocode}
%
% The command \cs{setmfvariable} arranges for the value of
% that variable to be localized to each figure if set inside one, but
% it is global otherwise. We have to be careful that \TeX{} and \MF{}
-% stay in sync, as they have different understandings as to what is
+% stay in synchrony, as they have different understandings as to what is
% global and what is not. So we define a switch that will be set at the
% start of an \env{mfpic} environment to test if a command is issued
-% inside one.
+% inside one. The command \cs{mfp@local} tests this switch and makes the
+% following assignment global if it fails. Then we have two shortcuts for
+% the more commonly occuring cases.
%
-% We also define here \texttt{true} and \texttt{false} as macros. These
-% are strings we need to write to the output frequently.
+% We also define here \texttt{true}, \texttt{false} and \texttt{color} as
+% macros. These are strings we need to write to the output frequently.
% \begin{macrocode}
-\let\ifin@mfpicenv\iffalse
+\newlet\ifin@mfpicenv\iffalse
\newdef\mfp@local{\relax\ifin@mfpicenv\else\global\fi}%
-\newdef\mf@T{true}%
-\newdef\mf@F{false}%
\newdef\mfp@let{\mfp@local\let}%
\newdef\mfp@def{\mfp@local\def}%
+\newdef\mfp@T{true}%
+\newdef\mfp@F{false}%
+\newdef\mfp@C{color}%
% \end{macrocode}
%
% \DescribeMacro{\usecenteredcaptions}
-% \DescribeMacro{\nocenteredcaptions}
% This (\opt{centeredcaptions}) is the simplest option. Other options
-% follow a similar pattern: user-level commands are defined for
-% turning on (and sometimes turning off) the option. Selection of
-% centered captions can be turned on or off at will throughout a
-% document.
-% \begin{macrocode}
-\newdef\usecenteredcaptions{\mfp@let\if@mfp@centcapt\iftrue}%
-\newdef\nocenteredcaptions {\mfp@let\if@mfp@centcapt\iffalse}%
+% follow a similar pattern: a user-level command \cs{usecenteredcaptions}
+% is defined for turning it on and another
+% \DescribeMacro{\nocenteredcaptions}\cs{nocenteredcaptions} is defined
+% for turning it off. Selection of centered captions can be turned on or
+% off at will throughout a document.
+% \begin{macrocode}
+\newdef\usecenteredcaptions{\mfp@let\ifmfp@centcapt\iftrue}%
+\newdef\nocenteredcaptions {\mfp@let\ifmfp@centcapt\iffalse}%
\nocenteredcaptions
% \end{macrocode}
%
+% \DescribeMacro{\useraggedcaptions}
+% Here we define the settings for justified and ragged captions.
+% Justified captions are the default. Ragged cptions are normally
+% raggedright, but are ragged on both sides if \opt{centeredcaptions} is
+% in effect. \DescribeMacro{\noraggedcaptions}For justified captions,
+% lines are justified on both sides, except the last is centered under
+% \opt{centeredcaptions}.
+% \begin{macrocode}
+\newdef\useraggedcaptions{\mfp@let\mfp@capsettings\mfp@raggedcap}%
+\newdef\noraggedcaptions{\mfp@let\mfp@capsettings\mfp@justifiedcap}%
+\newdef\mfp@raggedcap{%
+ \ifmfp@latex
+ \ifmfp@centcapt \centering \else \raggedright \fi
+ \else
+ \rightskip 0pt plus 1fil
+ \ifmfp@centcapt
+ \leftskip 0pt plus 1fil
+ \parfillskip 0pt plus 1fil\relax
+ \fi
+ \def\\{\unskip\hbox{}\penalty-\@M\ignorespaces}%
+ \fi}%
+\def\mfp@justifiedcap{%
+ \ifmfp@centcapt
+ \leftskip 0pt plus 0.5fil
+ \rightskip 0pt plus -0.5fil
+ \parfillskip 0pt plus 1fil\relax
+ \fi
+ \def\\{\unskip\hbox{}\hfil\penalty-\@M\ignorespaces}}%
+\noraggedcaptions
+% \end{macrocode}
+%
% This common code is repeated almost verbatim four times, so we define
% the following abbreviation. The first two arguments are the commands
-% to turn the option on and off. The third and fourth are the \TeX{} and
-% \MF{} booleans to switch. In all cases, the default for the option is
-% \emph{off}, so we run \texttt{\#2} at the end.
+% to turn the option on and off. The third and fourth are the \MF{} and
+% \TeX{} booleans to switch. In all cases, the default for the option is
+% \emph{off}, so we issue the second command after it is defined.
+%
+% Since the output file has not been opened while \mfpic{} is loading, the
+% \MF{} boolean is not set, but the \cs{opengraphsfile} code will check
+% the \TeX{} boolean and write the appropriate \MF{} code.
% \begin{macrocode}
\newdef\mfp@makeoption#1#2#3#4{%
- \newdef#1{\mfp@let#3\iftrue\setmfboolean{#4}\mf@T}%
- \newdef#2{\mfp@let#3\iffalse\setmfboolean{#4}\mf@F}
+ \newdef#1{\mfp@let#4\iftrue
+ \mfp@ifopengraphsfile{\setmfboolean{#3}\mfp@T}{}}%
+ \newdef#2{\mfp@let#4\iffalse
+ \mfp@ifopengraphsfile{\setmfboolean{#3}\mfp@F}{}}%
#2}%
% \end{macrocode}
%
% \DescribeMacro{\clipmfpic}
-% \DescribeMacro{\noclipmfpic}
-% The \opt{clip} option shows only what's inside the rectangle
-% given in the \cs{mfpic} command. It can be turned on or off for
-% each figure independently. The default is the old behavior: no
-% clipping.
-%
-% If the output file has not been opened, \cs{setmfboolean} does nothing.
-% But the \cs{opengraphsfile} code checks the boolean and writes the
-% appropriate code.
+% The \opt{clip} option shows only what's inside the rectangle given in
+% the \cs{mfpic} command. It can be turned on or off for each figure
+% independently. \DescribeMacro{\noclipmfpic}The default is the old
+% behavior: no clipping.
% \begin{macrocode}
-\mfp@makeoption\clipmfpic\noclipmfpic\if@mfp@clip{clipall}%
+\mfp@makeoption\clipmfpic\noclipmfpic{clipall}\ifmfp@clip
% \end{macrocode}
%
% \DescribeMacro{\usetruebbox}
-% \DescribeMacro{\notruebbox}
% The \opt{truebbox} option lets \MP{} set the true bounding box (which
% may differ from the numbers defined through \cs{mfpic}).
+% \DescribeMacro{\notruebbox}The default is the old behavior, \cs{notruebbox}.
%
-% We don't need to be too careful with the scope of truebbox. As
-% currently written, \TeX{} never needs to know the setting except to pass
-% the information on to \cs{opengraphsfile} if the option is set before
-% then. However, we treat it like most other settings, turning on or off
-% an appropriate \TeX{} boolean, in case it ever becomes necessary to know
-% the setting. It is local to any \env{mfpic}, global otherwise. The
-% default is to have \opt{truebbox} turned off.
-% \begin{macrocode}
-\mfp@makeoption\usetruebbox\notruebbox\if@mfp@truebbox{truebbox}%
-% \end{macrocode}
-%
-% Some options and commands require the \opt{metapost} option be in
-% effect. We define a generic error message for such cases. If a command
-% or option requires \MP, but the \opt{metapost} option is not set, the
-% option is ignored, or the command aborted.
+% We don't need to be too careful with the scope of these option
+% commands. As currently written, \TeX{} never needs to know the setting
+% except to pass the information on to \cs{opengraphsfile} if the option
+% is set before then. However, we treat it like most other settings,
+% turning on or off an appropriate \TeX{} boolean, in case it ever becomes
+% necessary to know the setting. It is local to any \env{mfpic}, global
+% otherwise. The default is to have \opt{truebbox} turned off.
% \begin{macrocode}
-\newdef\noMP@error#1{%
- \mfp@errmsg {Metafont does not support #1, use MetaPost.}%
- {Metafont doesn't support #1. Perhaps you forgot to turn on^^J%
- MetaPost support by using the metapost option or issuing the^^J%
- command \usemetapost. For now, I will ignore it.}}%
+\mfp@makeoption\usetruebbox\notruebbox{truebbox}\ifmfp@truebbox
% \end{macrocode}
%
% \DescribeMacro{\usemplabels}
-% \DescribeMacro{\nomplabels}
% An option to let \MP{} create labels, \opt{mplabels} mostly just
% switches between two versions of \cs{tlabel} so no \MP{} booleans
-% are set and so syncronization of scope is not an issue. The default
-% is the old behavior: labels are placed by \TeX. The actual setting is
-% delayed until \cs{opengraphsfile} so the order of these commands and
-% \cs{usemetapost} is not significant (before \cs{opengraphsfile}).
+% are set and so synchronization of scope is not an issue.
+% \DescribeMacro{\nomplabels}The default is the old behavior: labels are
+% placed by \TeX. The actual setting is delayed until \cs{opengraphsfile}
+% so the order of these commands and \cs{usemetapost} is not significant
+% (before \cs{opengraphsfile}).
%
% Because of the frequent need to check this setting, we define
% \cs{@ifmplabels} to execute one of two alternatives based on it.
-% \begin{macrocode}
-\newdef\usemplabels{\mfp@let\if@mfp@mplabels\iftrue
- \@ifmfpfileisopen
- {\mfp@ifmpost{}%
- {\noMP@error{mplabels}\global\let\if@mfp@mplabels\iffalse}}{}}%
-\newdef\nomplabels{\mfp@let\if@mfp@mplabels\iffalse}%
+% When writing \TeX{} code to the file we need to make sure \texttt\#
+% is not doubled. We do this by issuing \cs{mp@mksharpother} before
+% reading the argument that will be written.
+% \begin{macrocode}
+\newdef\usemplabels{\mfp@let\ifmfp@mplabels\iftrue
+ \mfp@ifopengraphsfile
+ {\mfp@ifmpost{}%
+ {\mfp@noMP@error{mplabels}\global\let\ifmfp@mplabels\iffalse}}{}}%
+\newdef\nomplabels{\mfp@let\ifmfp@mplabels\iffalse}%
\newdef\@ifmplabels{%
- \if@mfp@mplabels\@xp\@firstoftwo\else\@xp\@secondoftwo\fi}%
+ \ifmfp@mplabels\@xp\@firstoftwo\else\@xp\@secondoftwo\fi}%
\nomplabels
+\newdef\mp@mksharpother{\@ifmplabels{\@makeother\#}{}}%
% \end{macrocode}
%
% \DescribeMacro{\overlaylabels}
-% \DescribeMacro{\nooverlaylabels}
-% These control an option to defer adding labels (in \MP) until the end
-% of the picture. That means they don't get clipped or covered up by any
-% drawing elements. Neither of these commands affect \MF, but they write
-% to the output file anyway if issued after \cs{opengraphsfile}.
+% The option \opt{overlaylabels} instructs \MP{} to defer adding labels
+% until the end of the picture. That means they don't get clipped or
+% covered up by any drawing elements. \DescribeMacro{\nooverlaylabels}The
+% default is the old behavior, \cs{nooverlaylabels}. Neither of these
+% commands affect \MF, but they write to the output file anyway if issued
+% after \cs{opengraphsfile}.
% \begin{macrocode}
\mfp@makeoption%
- \overlaylabels\nooverlaylabels\if@mfp@overlabels{overlaylabels}%
+ \overlaylabels\nooverlaylabels{overlaylabels}\ifmfp@overlabels
% \end{macrocode}
%
% \DescribeMacro{\clearsymbols}
-% \DescribeMacro{\noclearsymbols}
-% These determine whether the symbols drawn by \cs{plotsymbol} and
-% \cs{plotnodes} should have their interiors erased before being drawn.
+% The option \opt{clearsymbols} causes the symbols drawn by
+% \cs{plotsymbol} and \cs{plotnodes} to have their interiors erased before
+% being drawn. \DescribeMacro{\noclearsymbols}The default is the old
+% behavior: \cs{noclearsymbols}.
% \begin{macrocode}
\mfp@makeoption%
- \clearsymbols\noclearsymbols\if@mfp@clearsym{clearsymbols}%
+ \clearsymbols\noclearsymbols{clearsymbols}\ifmfp@clearsym
+% \end{macrocode}
+%
+% \DescribeMacro{\stopshipping}
+% This is to turn off character shipping for duration of innermost
+% enclosing \MF{} group (eg, \env{mfpic} environment). As far as I know
+% it has never been tested with \MP. (It hasn't been tested recently even
+% in \MF.) The \MF{} code prevents the figure from being added to the font,
+% the \TeX{} switch \cs{ifmfp@noship} is checked before \cs{endmfpic}
+% attempts to add the (nonexistent) figure to the document.
+% \DescribeMacro{\resumeshipping}\cs{resumeshipping} restores character
+% shipping.
+%
+% \DescribeMacro{\noship}\cs{noship} is the older name for
+% \cs{stopshipping}. Its name indicated that it was intended for use in
+% one picture. This is not actually an option (that is, there is no
+% corresponding \cs{DeclareOption} in \LaTeX), but it is coded the same.
+% \begin{macrocode}
+\mfp@makeoption\stopshipping\resumeshipping{noship}\ifmfp@noship
+\newlet\noship\stopshipping
+% \end{macrocode}
+%
+% \DescribeMacro{\mfpreadlog}
+% This sets the boolean governing the option to examine the \MF{} or
+% \MP{} log file for certain information.
+% \begin{macrocode}
+\newlet\ifmfp@readlog\iffalse
+\newdef\mfpreadlog{%
+ \mfp@ifopengraphsfile
+ {\mfp@afteropen@error\mfpreadlog}{\global\let\ifmfp@readlog\iftrue}}%
% \end{macrocode}
%
% \subsection{Graphic inclusion}\label{graphic}
%
% \DescribeMacro{\mfpicllx}
-% \DescribeMacro{\mfpiclly}
% The code to include a figure has been divided into three parts. Under
% \opt{metapost} we need to both include the figure and save the
-% bounding box coordinates of the lower left corner. Here we initialize
-% the macros that hold these coordinates.
+% \DescribeMacro{\mfpiclly} bounding box coordinates of the lower left
+% corner. Here we initialize the macros that hold these coordinates.
% \begin{macrocode}
-\def\mfpicllx{0}%
-\def\mfpiclly{0}%
+\newdef\mfpicllx{0}%
+\newdef\mfpiclly{0}%
% \end{macrocode}
%
% \DescribeMacro{\preparemfpicgraphic}
@@ -671,133 +848,169 @@
%
% The definition of these three is delayed to the option processing
% section, except under the \MF{} default: \cs{usemetafont} defines them.
+%
+% We actually define two alternative versions of \cs{@setmfpicgraphic}.
+% The normal one is as described above, and a `nofile' alternative for
+% when the graphic file somehow doesn't exist. Then we just print the
+% filename.
% \begin{macrocode}
-\newdef\@setmfpicgraphic#1{%
+\newdef\normal@setmfpgraphic#1{%
\preparemfpicgraphic{#1}\setmfpicgraphic{#1}\getmfpicoffset{#1}}%
+\newdef\nofile@setmfpgraphic#1{%
+ {\edef\mfp@tempa{#1}%
+ \vbox to 0pt{\vss
+ \rlap{\kern2pt \mfpdraftfont \mfp@sanitized\mfp@tempa}\kern 2pt}}}%
+\mfp@ifdefined\strip@prefix{}{\def\strip@prefix#1>{}}%
+\newdef\mfp@sanitized{\@xp\strip@prefix\meaning}
+\newlet\@setmfpicgraphic\normal@setmfpgraphic
\usemetafont
% \end{macrocode}
%
% \DescribeMacro{\setfilename}
% The user can also change \cs{setfilename} to modify the assumed
-% filenames of the graphics output by \MP. The default is to concatenate
-% the output file's basename with the picture number.
+% filenames of the graphics output by \MP. The default is the output
+% file's basename (\arg1) with the picture number as the extension.
% \begin{macrocode}
\newdef\setfilename#1#2{#1.#2}%
% \end{macrocode}
%
-% \DescribeMacro{\ifmfpicdraft}
+% The \opt{draft}, \opt{final} and \opt{nowrite} options override
+% \mfpic's attempts to automatically detect whether the pictures should
+% be included. They also work under the \opt{metafont} option, but are
+% less important then (even unnecessary).
+%
% \DescribeMacro{\mfpicdraft}
-% \DescribeMacro{\mfpicfinal}
+% The \opt{draft} option (\cs{mfpicdraft} command) causes the pictures
+% created by \mfpic{} to be omitted.
+%
% \DescribeMacro{\mfpicnowrite}
-% The \opt{draft}, \opt{final} and \opt{nowrite} options override
-% \mfpic's attempts to automatically detect whether the eps
-% files should be included. The \opt{final} option can also be used to
-% override a global draft option. Each of these sets the user level
-% if-command \cs{ifmfpicdraft}, which is retained for backward
-% compatibility. These settings are global since they are intended to
-% be set for the whole document. The four commands
-% \cs{@mfp@draft/finaltrue/false} are used to communicate with the
-% \cs{opengraphsfile} command, in case the user-level commands occur before
-% then.
+% The \opt{nowrite} option (\cs{mfpicnowrite} command) turns off creation
+% of the output \file{.mp} file. It is intended to help speed things up
+% after all figures are completed, so it permits the insertion of pictures.
+%
+% \DescribeMacro{\mfpicfinal}
+% The \opt{final} option (\cs{mfpicfinal} command) can be used to
+% override a global draft option. It forces \mfpic{} to attempt to
+% insert the pictures. Under the \opt{metapost} option, missing figures
+% may cause serious slow-downs as a large number of directories may be
+% searched.
+%
+% Each of these sets the user level if-command
+% \SpecialUsageIndex{\ifmfpicdraft}\cs{ifmfpicdraft}, which is retained
+% for backward compatibility. These settings are global since they are
+% intended to be set for the whole document. The four commands
+% \cs{mfp@\{draft\|final\}\{true\|false\}} are used to communicate with
+% the \cs{opengraphsfile} command, in case the user-level commands occur
+% before then.
% \begin{macrocode}
\newif\ifmfpicdraft
-\newif\if@mfp@draft
-\newif\if@mfp@final
-\newif\if@mfp@nowrite
+\newlet\ifmfp@draft\iffalse
+\newlet\ifmfp@final\iffalse
+\newlet\ifmfp@nowrite\iffalse
\newdef\mfpicdraft{%
- \@ifmfpfileisopen{\global\mfpicdrafttrue}%
- {\global\@mfp@drafttrue}}%
+ \mfp@ifopengraphsfile{\global\mfpicdrafttrue}%
+ {\global\let\ifmfp@draft\iftrue}}%
\newdef\mfpicfinal{%
- \@ifmfpfileisopen{\global\mfpicdraftfalse}%
- {\global\@mfp@finaltrue}}%
+ \mfp@ifopengraphsfile{\global\mfpicdraftfalse}%
+ {\global\let\ifmfp@final\iftrue}}%
\newdef\mfpicnowrite{%
- \@ifmfpfileisopen
- {\mfp@errmsg
- {Improper use of command \string\mfpicnowrite\@online.}%
- {You must issue the command \mfpicnowrite prior to
- \opengraphsfile. It will be ignored now.}}%
- {\global\@mfp@nowritetrue}}%
+ \mfp@ifopengraphsfile{\mfp@afteropen@error\mfpicnowrite}%
+ {\global\let\ifmfp@nowrite\iftrue}}%
+%</tex>
% \end{macrocode}
%
-% \subsection{\CMP{} graphics and \LaTeX{} options}\label{latex}
+% \subsection{\LaTeX{} options and \MP{} graphics inclusion}\label{latex}
%
% Code dependent on \LaTeXe. Mostly this is the option processing
-% facilities.
-% \begin{macrocode}
-\if@mfp@latexe
- \ProvidesPackage{mfpic}[2004/04/16 v0.7a beta.]%
- \DeclareOption{draft}{\mfpicdraft}%
- \DeclareOption{final}{\mfpicfinal}%
- \DeclareOption{nowrite}{\mfpicnowrite}%
- \DeclareOption{metapost}{\mfp@mposttrue}%
- \DeclareOption{metafont}{\usemetafont}%
- \DeclareOption{centeredcaptions}{\usecenteredcaptions}%
- \DeclareOption{clip}{\clipmfpic}%
- \DeclareOption{truebbox}{\usetruebbox}%
- \DeclareOption{mplabels}{\usemplabels}%
- \DeclareOption{overlaylabels}{\overlaylabels}%
- \DeclareOption{clearsymbols}{\clearsymbols}%
- \DeclareOption{debug}{\mfpicdebugtrue}%
- \DeclareOption*{%
- \@ifpackageloaded{graphics}%
- {\Mfpic@warn{Unrecognized option \CurrentOption.}}%
- {\Mfpic@msg{Passing option \CurrentOption\space to %
- graphics package.}%
- \PassOptionsToPackage{\CurrentOption}{graphics}}}%
-% \end{macrocode}
-% If \file{mfpic.cfg} exists, it will be input at this point. Then,
-% we \cs{ProcessOptions}, using the *-form because it allows package
-% options to take precedence over global options. Thus \mfpic's
-% \opt{final} option can counteract the \mfpic-specific side effects of a
-% global \opt{draft} option.
+% facilities. The declaration of options is in the file
+% \file{mfpic.sty}, which will input \file{mfpic.tex}.
% \begin{macrocode}
- \InputIfFileExists{mfpic.cfg}{}{}%
- \ProcessOptions*\relax
-% \end{macrocode}
-%
-% \DescribeMacro{\usemetapost}
-% Finally, one of the definitions of \cs{usemetapost}. The other is
-% for use outside \LaTeXe, when the \prog{graphics} package cannot be
-% assumed to be available.
-% \begin{macrocode}
- \def\usemetapost{%
- \@ifmfpfileisopen{\mfp@mpostfalse\MPtoolate@error}%
- {\mfp@mposttrue \RequirePackage{graphics}%
-% \end{macrocode}
-%
-% Unlike all the other eps inclusion macros, \cs{includegraphics} does
+%<*sty>
+\DeclareOption{draft}{\mfpicdraft}%
+\DeclareOption{final}{%
+ \mfpicfinal\PassOptionsToPackage{final}{graphics}}%
+\DeclareOption{nowrite}{\mfpicnowrite}%
+\DeclareOption{metapost}{\mfp@mposttrue}%
+\DeclareOption{metafont}{\usemetafont}%
+\DeclareOption{centeredcaptions}{\usecenteredcaptions}%
+\DeclareOption{justifiedcaptions}{\noraggedcaptions}%
+\DeclareOption{raggedcaptions}{\useraggedcaptions}%
+\DeclareOption{clip}{\clipmfpic}%
+\DeclareOption{truebbox}{\usetruebbox}%
+\DeclareOption{mplabels}{\usemplabels}%
+\DeclareOption{overlaylabels}{\overlaylabels}%
+\DeclareOption{clearsymbols}{\clearsymbols}%
+\DeclareOption{debug}{\mfpicdebugtrue}%
+\DeclareOption{mfpreadlog}{\mfp@readlogtrue}%
+\DeclareOption*{%
+ \@ifpackageloaded{graphics}%
+ {\Mfpic@warn{Unrecognized option \CurrentOption.}}%
+ {\Mfpic@msg{Passing option \CurrentOption\space to %
+ graphics package.}%
+ \PassOptionsToPackage{\CurrentOption}{graphics}}}%
+\newcommand*\mfpdraftfont{\normalfont\ttfamily}%
+% \end{macrocode}
+%
+% Now we define one of three versions of \cs{@usemetapost}. This command
+% will be issued by the user level command \cs{usemetapost}, and consists
+% of the format dependent code. The one here is, of course, for \LaTeX{}
+% and will be in \file{mfpic.sty}.
+%
+% Unlike all the other EPS inclusion macros, \cs{includegraphics} does
% not make the bounding box coordinates available: \cs{Gin@llx} and the
% others are defined, but are local to a group surrounding the internals
% of \cs{includegraphics}. We locally modify one graphics package internal
% macro to globally save the information we need. Here we save the
-% original definition and define our replacement. This definitely needs
-% to come after the above \cs{RequirePackage} command.
-% \begin{macrocode}
- \global\let\mfp@save@parse@bb\Gread@parse@bb
- \gdef\mfp@parse@bb##1 ##2 ##3 ##4 ##5\\{%
- \mfp@save@parse@bb##1 ##2 ##3 ##4 ##5\\%
- \xdef\mfpicllx{\Gin@llx}\xdef\mfpiclly{\Gin@lly}}%
+% original definition and define our replacement. For this, we definitely
+% need to make sure the \prog{graphics} package is loaded.
+% \begin{macrocode}
+\def\@usemetapost{%
+ \mfp@ifdefined\includegraphics{}{\RequirePackage{graphics}}%
+ \let\mfp@save@parse@bb\Gread@parse@bb
+ \newdef\mfp@parse@bb##1 ##2 ##3 ##4 ##5\\{%
+ \mfp@save@parse@bb##1 ##2 ##3 ##4 ##5\\%
+ \xdef\mfpicllx{\Gin@llx}\xdef\mfpiclly{\Gin@lly}}%
+% \end{macrocode}
+% The \prog{graphics} package makes decisions about graphic files based on
+% the files' extensions. Since we don't want to force our users to rename
+% all the graphics output by \MP, we add code to \cs{preparemfpicgraphic}
+% to force \cs{includegraphics} to treat the current extension (some
+% number) as if it were `{\tt eps}' or `{\tt mps}' according to the
+% context. We also use this command to substitute the above BoundingBox
+% parsing code for the \prog{graphics} package version.
+% \begin{macrocode}
+ \ifmfp@pdf \gdef\mfp@Gtype{mps}%
+ \else \gdef\mfp@Gtype{eps}%
+ \fi
+ \gdef\preparemfpicgraphic##1{%
+ \@namedef{Gin@rule@.\number\mfp@count}####1{{\mfp@Gtype}%
+ {.\number\mfp@count}{####1}}%
+ \let\Gread@parse@bb\mfp@parse@bb}%
+ \global\let\setmfpicgraphic\includegraphics
+ \gdef\getmfpicoffset##1{}}%
+%</sty>
% \end{macrocode}
%
-% The \prog{graphics} package (as opposed to the \prog{graphicx} package)
-% is far too dependent on actual extensions. Since we don't want to
-% force our users to rename all the graphics output by \MP, we have to
-% force \cs{includegraphics} to treat the current extension (some number)
-% as if it were `{\tt eps}' or `{\tt mps}' according to the context. We
-% decide which at this point.
+% \DescribeMacro{\usemetapost}
+% The user level command \cs{usemetapost} starts with a few actions common
+% to all formats, then calls \cs{@usemetapost} which is defined
+% differently for different formats.
% \begin{macrocode}
- \if@mfp@pdf \gdef\mfp@Gtype{mps}%
- \else \gdef\mfp@Gtype{eps}%
- \fi
+%<*tex>
+\newdef\usemetapost{%
+ \mfp@ifopengraphsfile{\mfp@mpostfalse\mfp@useMP@error}%
+ {\mfp@mposttrue \@usemetapost}}%
% \end{macrocode}
%
-% The code we add to \cs{setmfpicgraphic} replaces \cs{Gread@parse@bb} with
-% our own version.
+% Under \LaTeX, if \file{mfpic.cfg} exists, it will be input at this
+% point. Then, we \cs{ProcessOptions}, using the star-form because it allows
+% package options to take precedence over global options. Thus \mfpic's
+% \opt{final} option can counteract the \mfpic-specific side effects of a
+% global \opt{draft} option.
% \begin{macrocode}
- \gdef\preparemfpicgraphic##1{%
- \let\Gread@parse@bb\mfp@parse@bb}%
- \gdef\getmfpicoffset##1{}%
- \gdef\setmfpicgraphic##1{\includegraphics{##1}}}}%
+\ifmfp@latexe
+ \InputIfFileExists{mfpic.cfg}{}{}%
+ \ProcessOptions*\relax
% \end{macrocode}
%
% \cs{ProcessOptions} doesn't allow \cs{RequirePackage}\marg{graphics},
@@ -806,49 +1019,46 @@
% close the output file if the user forgot.
% \begin{macrocode}
\mfp@ifmpost{\usemetapost}{}%
- \AtEndDocument{\@closegraphsfile}%
+ \AtEndDocument{\mfp@closegraphsfile}%
% \end{macrocode}
%
% Now those cases outside \LaTeXe{} that we try to support.
% \begin{macrocode}
\else
- \Mfpic@msg{\mfpfiledate\space v\mfpfileversion.}%
+ \mfp@msg{}\Mfpic@msg{\mfpfiledate\space v\mfpfileversion.\@nl}%
% \end{macrocode}
%
-% \DescribeMacro{\usemetapost}
-% The definition of \cs{usemetapost} depends on the setting of
-% \cs{if@mfp@pdf}: \file{epsf.tex} is used for regular \TeX{} and
-% \file{supp-pdf.tex} for \pdfTeX.
-% \begin{macrocode}
- \if@mfp@pdf
- \def\usemetapost{%
- \@ifmfpfileisopen{\mfp@mpostfalse\MPtoolate@error}%
- {\mfp@mposttrue
- \mfp@ifdefined\convertMPtoPDF{}{\input supp-pdf\relax}%
-% \end{macrocode}
-% The command \cs{convertMPtoPDF} takes the filename and and two
-% scaling factors. It parses the files PS contents to PDF code. It leaves
-% the bounding box info in macros \cs{MPllx}, etc.
-% \begin{macrocode}
- \gdef\setmfpicgraphic##1{\convertMPtoPDF{##1}{1}{1}}%
- \gdef\getmfpicoffset##1{%
- \xdef\mfpicllx{\MPllx}\xdef\mfpiclly{\MPlly}}}}%
- \else
+% The definition of \cs{@usemetapost} in plain \TeX{} depends on the
+% setting of \cs{ifmfp@pdf}. However both cases are so similar that we
+% define a helper macro \cs{mfp@useMP} that just needs to have the names
+% of three commands and one file fed to it.
+% \begin{macrocode}
+ \newdef\mfp@useMP#1#2#3#4{%
+ \mfp@ifdefined#1{}{\input #2\relax}%
+ \global\let\setmfpicgraphic\mfp@includeMPS
+ \gdef\getmfpicoffset##1{\xdef\mfpicllx{#3}\xdef\mfpiclly{#4}}}%
+% \end{macrocode}
+%
+% Under \pdfTeX{}, the graphics inclusion command is \cs{convertMPtoPDF},
+% defined in \file{supp-pdf.tex}. This command parses the file's PS
+% contents to PDF code, and leaves the bounding box info in macros
+% \cs{MPllx} and \cs{MPlly}.
+% \begin{macrocode}
+ \ifmfp@pdf
+ \newdef\mfp@includeMPS#1{\convertMPtoPDF{#1}{1}{1}}%
+ \newdef\@usemetapost{%
+ \mfp@useMP\convertMPtoPDF{supp-pdf}\MPllx\MPlly}%
% \end{macrocode}
-% The final case is the generic plain\TeX, \AmSTeX, or \LaTeX2.09. Our
-% code uses the macros from \file{epsf.tex}, which should be present in
-% every installation that includes \prog{dvips}. The command
-% \cs{epsfbox} takes the filename, writes a \cs{special} for
-% \prog{dvips} (and other programs) and saves the bounding box
-% coordinates in \cs{epsfllx}, etc.
-% \begin{macrocode}
- \def\usemetapost{%
- \@ifmfpfileisopen{\mfp@mpostfalse\MPtoolate@error}%
- {\mfp@mposttrue
- \mfp@ifdefined\epsfbox {}{\input epsf\relax}%
- \gdef\setmfpicgraphic##1{\epsfbox{##1}}%
- \gdef\getmfpicoffset##1{%
- \xdef\mfpicllx{\epsfllx}\xdef\mfpiclly{\epsflly}}}}%
+%
+% Under plain\TeX, eplain (still need to test this after recent eplain
+% changes), \AmSTeX, or \LaTeX2.09, the graphics inclusion command is
+% \cs{epsfbox}, defined in \file{epsf.tex}. This command writes a
+% \cs{special} for \prog{dvips} (and other DVI driver programs), and
+% leaves the bounding box info in macros \cs{epsfllx} and \cs{epsflly}.
+% \begin{macrocode}
+ \else
+ \newdef\mfp@includeMPS#1{\epsfbox{#1}}%
+ \newdef\@usemetapost{\mfp@useMP\epsfbox{epsf}\epsfllx\epsflly}%
\fi
\fi
% \end{macrocode}
@@ -856,254 +1066,236 @@
%
% \section{Optional parameter handling}\label{optional}
%
-% We borrow \LaTeX's \cs{@ifnextchar} to have the advantage of
-% skipping spaces and EOLs, but we skip \cs{relax} as well. The reason for
-% this last is that an EOL will be set equal to \cs{relax} in certain
-% contexts and we want option handling to work the same there as
-% elsewhere. This could be a mistake, since \cs{relax} is a common method
-% to halt such things. However, there should be no occasion in an
-% \env{mfpic} environment where one would want a `\texttt[' to follow a
-% command and \emph{not} be an optional argument. In text label commands
-% we restore the meaning of blank lines, and EOLs ought to be normal.
-%
-% The first argument of \cs{mfp@ifnextchar} is the token being checked. It
-% doesn't have to be a character and we sometimes use \cs{mfp@ifnextchar}
-% to check whether a certain command follows. However, it is usually
-% `\texttt['. The next argument is the code to run if that character is
-% present, the last is code to use if the character is not present. The
-% first argument must end with code that handles the character.
-%
-% The reason for the \cs{long} in the code when the next token is
-% \cs{relax} is that it may be \cs{par} which has been \cs{let} equal to
-% \cs{relax} in \env{mfpic} environments.
+% We borrow \LaTeX's \cs{@ifnextchar}. The first argument is the token
+% being checked. It doesn't have to be a character and we sometimes use
+% \cs{@ifnextchar} to check whether a certain command follows.
+% The next argument is the code to run if that character is present, the
+% last is code to use if the character is not present.
% \begin{macrocode}
\mfp@DBlog{Handlers for optional parameters.}%
-\long\def\mfp@ifnextchar#1#2#3{%
- \let\mfptmp@d=#1\def\mfptmp@a{#2}%
- \def\mfptmp@b{#3}\mfp@checknext}%
-\def\mfp@checknext{\futurelet\@let@token\mfp@ifnch}%
-\def\mfp@ifnch{%
- \ifx\@let@token\@sptoken
- \let\mfptmp@c\eatspace@checknext
- \else\ifx\@let@token\relax
- \long\def\mfptmp@c##1{\mfp@checknext}%
- \else\ifx\@let@token\mfptmp@d
- \let\mfptmp@c\mfptmp@a
- \else
- \let\mfptmp@c\mfptmp@b
- \fi\fi\fi\mfptmp@c}%
-\begingroup
- \def\:{\global\let\@sptoken= }\: %
- \def\:{\eatspace@checknext}\@xp\gdef\: {\mfp@checknext}%
-\endgroup
+\mfp@ifundefined\@ifnextchar
+ \long\def\@ifnextchar#1#2#3{%
+ \let\reserved@d#1%
+ \def\reserved@a{#2}%
+ \def\reserved@b{#3}%
+ \futurelet\@let@token\@ifnch}%
+ \def\@ifnch{%
+ \ifx\@let@token\@sptoken
+ \let\reserved@c\@xifnch
+ \else
+ \ifx\@let@token\reserved@d
+ \let\reserved@c\reserved@a
+ \else
+ \let\reserved@c\reserved@b
+ \fi
+ \fi
+ \reserved@c}%
+ {%
+ \def\:{\global\let\@sptoken= }\: % this makes \@sptoken a space token
+ \def\:{\@xifnch}\@xp\gdef\: {\futurelet\@let@token\@ifnch}%
+ }%
+\mfp@enddef
+% \end{macrocode}
+%
+% The following save us some typing whenever we need to check if some
+% token follows, and then remove it if it does. That's what
+% \cs{mfp@iftoken} does, being just a generalized form of \LaTeX{}'s
+% \cs{@ifstar}. All our star-form commands differ only in what parameter
+% is passed to some other command. Thus \cs{mfp@ifstar} passes either
+% \arg{2} or \arg{3} to command \arg{1}.
+%
+% The \cs{mfp@ifend} is for use in processing lists, which all end with
+% the token \cs{mfp@end}. \cs{mfp@ifendorcomma} is useful for processing
+% comma-separated lists. If \cs{mfp@end} follows, it is removed and \arg1
+% is executed. Otherwise, if a comma follows, it is removed and \arg2 is
+% executed. When neither follows, nothing is removed and \arg3 is
+% executed. Normally, this last is the main command being applied to each
+% element of the list.
+% \begin{macrocode}
+\def\mfp@iftoken#1#2{\@ifnextchar#1{\@firstoftwo{#2}}}%
+\def\mfp@ifstar#1#2#3{\mfp@iftoken*{#1{#2}}{#1{#3}}}%
+\def\mfp@ifend{\mfp@iftoken\mfp@end}%
+\def\mfp@ifendorcomma#1#2#3{\mfp@ifend{#1}{\mfp@iftoken,{#2}{#3}}}%
% \end{macrocode}
%
% I've taken to making an empty optional argument the default for a lot
% of commands. I wanted to make it possible to have the same result for
-% empty brackets (i.e., \texttt{[]}) as for omitted brackets.
-% The following helps check for emptyness. It uses a simple \cs{if} so
-% that \texttt{\#1} will be expanded. I use a non-active
-% \texttt{\textasciitilde} to test for emptyness because, even if a tilde
-% should accidentally occur in \texttt{\#1}, it will most likely be active
-% and expand to something else. In fact, in plain \TeX,
-% \cs{mfp@ifempty}\marg{\textasciitilde} will compare
-% \texttt{\textasciitilde} to \cs{penalty} and in \LaTeX{} to
-% \cs{unhbox}.
+% empty brackets (i.e., \texttt{[\,]}) as for omitted brackets.
+% \cs{mfp@ifempty} helps check for emptyness. It uses a simple \cs{if} so
+% that \arg{1} will be expanded. I use a non-active \texttt\textasciitilde
+% (so it won't expand) to test for emptiness because, even if
+% a tilde should accidentally occur in a user's \arg1, it will most
+% likely be the usual active version which expands to something else.
+%
+% The shorthand \cs{mfp@ifexist} executes some code if \arg1 is not
+% empty, otherwise it does nothing. And \cs{mfp@emptysub} expands to
+% \arg1 if it isn't empty, otherwise \arg2.
%
% The \cs{@ifmtarg} macro is borrowed from \file{ifmtarg.sty}.
% It is used when a space should count as empty, but expansion isn't
-% wanted.
+% needed. It would be nice to have one that counts both as empty, but
+% that seems impossible (or impossibly hard).
+% \begin{macrocode}
+{%
+ \catcode`Q3 \catcode`\~12
+ \gdef\mfp@ifempty#1{\if~#1~\@xp\@firstoftwo\else\@xp\@secondoftwo\fi}%
+ \gdef\mfp@ifexist#1{\if~#1~\@xp\@gobble\else\@xp\@firstofone\fi}%
+ \gdef\mfp@emptysub#1#2{\if~#1~#2\else#1\fi}%
+ \long\gdef\@ifmtarg#1{\@xifmtarg#1QQ\@secondoftwo\@firstoftwo\@nil}%
+ \long\gdef\@xifmtarg#1#2Q#3#4#5\@nil{#4}%
+}%
+% \end{macrocode}
+%
+% These are the two most common methods to parse optional arguments. In
+% \cs{mfp@defaultopt}, if an optional argument is present, we use
+% \cs{mfp@ifemptyopt} to see if it is empty and substitute a default
+% (\arg{2}) if it is. Argument \arg{1} is the command to run
+% on the resulting argument after this check. It needn't be a single
+% command token, but can (and often will) be a series of commands, or a
+% command with additional arguments.
+%
+% \cs{mfp@nullopt} is a convenient abbreviation for a common case: the
+% default option is empty. We could define it in terms of
+% \cs{mfp@defaultopt}, but then we'd have the redundant step of checking
+% for an empty optional argument, only to replace it by an empty argument.
+% That might not make a significant difference, but it just feels wrong.
+%
+% Both commands turn the delimited argument, if present, into an ordinary
+% one. This extra step permits us to avoid many bracket pairs in later
+% code.
% \begin{macrocode}
-\newdef\mfp@ifempty#1{%
- \if~#1~\@xp\@firstoftwo\else\@xp\@secondoftwo\fi}%
-\begingroup
-\catcode`Q=3
-\long\gdef\@ifmtarg#1{\@xifmtarg#1QQ\@secondoftwo\@firstoftwo\@nil}%
-\long\gdef\@xifmtarg#1#2Q#3#4#5\@nil{#4}%
-\endgroup
-\newdef\@ifmtsub#1#2{\@ifmtarg{#1}{#2}{#1}}%
-\newdef\mfp@mtsub#1#2{\mfp@ifempty{#1}{#2}{#1}}%
-% \end{macrocode}
-%
-% The following is a historical holdover. Originally all optional arguments
-% were handled through \cs{do@ptparam}. Then there arose cases that did not
-% fit its assumptions and I wrote \cs{alt@ptparam}. Then, it became more
-% efficient to have \cs{mfp@ifnextchar} (defined above) and now
-% this command is defined in terms of it.
-%
-% The first argument of \cs{do@ptparam} is the command to use and the
-% second is a default value of the optional argument. The command must be
-% defined, and it must be a command whose first argument is delimited by
-% `{\tt[}' and `{\tt]}'. Currently this is no longer used: all commands
-% use one of the variants below.
-% \begin{macrocode}
-\newdef\do@ptparam#1#2{\mfp@ifnextchar[{#1}{#1[#2]}}%
-% \end{macrocode}
-%
-% These are variations on the above. In \cs{simple@opt}, if an optional
-% argument is present, we check to see if it is empty and substitute the
-% default if it is. This was so named because I intended that it be used
-% only if the optional argument required no further parsing (because it
-% turned the argument into a nondelimited one). But now we use it even
-% then and the few commands that require further parsing handle it more or
-% less ad hoc.
-%
-% \cs{null@opt} is a convenient abbreviation for a common case: the
-% default option is null.
-% \begin{macrocode}
-\newdef\simple@opt#1#2{%
- \mfp@ifnextchar[{\process@opt{#1}{#2}}{#1{#2}}}%
-\newdef\process@opt#1#2[#3]{%
- \mfp@ifempty{#3}{#1{#2}}{#1{#3}}}%
-\newdef\null@opt#1{\mfp@ifnextchar[{#1}{#1[]}}%
-% \end{macrocode}
-%
-% Quite a few \mfpic{} commands stuff several parameters into a
-% single optional argument. Generally, these commands can be coded to
-% proceed gracefully if the last parameter is missing (substituting a
-% default). However, if more than that are missing, \TeX{} gobbles the
-% closing bracket and produces pretty inscrutable error mesages. This
-% (adding extra information in one optional argument) was, in retrospect,
-% a bad design decision that was made for backward compatibility and
-% (originally) to minimize the changes in existing commands.
+\newdef\mfp@defaultopt#1#2{%
+ \@ifnextchar[{\mfp@ifemptyopt{#1}{#2}}{#1{#2}}}%
+\newdef\mfp@ifemptyopt#1#2[#3]{\mfp@ifempty{#3}{#1{#2}}{#1{#3}}}%
+\newdef\mfp@nullopt#1{\@ifnextchar[{\mfp@getopt{#1}}{#1{}}}%
+\newdef\mfp@getopt#1[#2]{#1{#2}}%
+% \end{macrocode}
+%
+% Why do we bother to check for an empty optional argument? Because
+% quite a few \mfpic{} commands stuff several parameters into a
+% single optional argument. All but the last are processed as undelimited
+% arguments and the last one as delimited by the right bracket. If only
+% the last parameter is missing, TeX{} takes that as an empty argument and
+% we can proceed gracefully (substituting a default). However, if more
+% than that are missing, \TeX{} takes the closing bracket as the
+% \emph{undelimited} argument and produces pretty inscrutable error
+% mesages. This decision to add extra information in one optional argument
+% was, in retrospect, a bad one. It was made for backward compatibility
+% and (originally) to minimize the changes in existing commands.
% Unfortunately we are stuck with it for at least the near future.
%
-% We use the following utility to check for empty optional parameters.
-% \begin{itemize}
-% \item {\tt\#1} is the parameter to test (passed by some other macro)
-% \item {\tt\#2} is what to use if {\tt\#1} is empty.
-% \item {\tt\#3} is the command being passed the optional argument
-% \end{itemize}
-% We use a \cs{mfp@ifempty} so \texttt{\#1} will get expanded (often we
-% pass arguments as macros). After the above revamping of optional
-% argument handling, we ended up needing this code only once.
-% \begin{macrocode}
-\newdef\do@mtparam#1#2#3{\mfp@ifempty{#1}{#3[#2]}{#3[#1]}}%
-% \end{macrocode}
+% Another reason is that multiple optional arguments generally mean that
+% the to specify a later one, all the previous ones need to be specified.
+% But with the above code, the user can always use an empty bracket pair
+% to indicate the default, minimizing the extra typing.
%
%
% \section{Writing to the \file{.mf} or \file{.mp} file}\label{writing}
%
% \subsection{Preserving linebreaks}\label{linebreaks}
%
-% Some macros can take quite long arguments (we call them
-% \cs{@listmac}\,s), especially if they are automatically generated by
-% some program. While the output file need not ever be looked at, there
-% can be a problem with lines too long for the \MF{} executable's line
-% buffer. Plus, for debugging, we might want to examine the file \mfpic{}
-% writes. We therefore try to get at least the list macros to preserve
-% linebreaks on output.
-%
-% \DescribeMacro{\mfpicobeylines}
-% We make use of the \TeX{} property that expandable commands are
-% expanded inside \cs{write} commands, but unexpandable commands and
-% characters are written verbatim (almost). If this were the only
-% consideration, nothing would need to be done. But characters with
-% catagory code 5 (end-of-line) are converted to spaces upon reading, so
-% we have to change the category of the \cs{endlinechar}, or we have
-% change the \cs{endlinechar} to a character in a different category.
-% We do the former, because the line a macro starts on will already have
-% the end-of-line character added, but its category will not yet be
-% established. We need a category that \TeX{} will not \emph{ignore},
-% but that nevertheless has no (or minimal) side effects if encountered in
-% a \TeX{} file. We therefore make it active, but \cs{let} it equal to
-% \cs{relax}. This makes the end-line-character unexpandable.
+% Some macros (the ones we call list macros) can take quite long
+% arguments, especially if they are automatically generated by some
+% program. While the output file need not ever be looked at, there can be
+% a problem with lines too long for the \MF{} executable's line buffer.
+% Plus, for debugging, we might want to examine the file \mfpic{} writes.
+% We therefore try to get at least the list macros to preserve linebreaks
+% on output.
+%
+% We make use of the \TeX{} property that expandable commands are expanded
+% inside \cs{write} commands, but unexpandable commands and characters are
+% written verbatim (almost). If this were the only consideration, nothing
+% would need to be done. But characters with catagory code 5 (end-of-line)
+% are converted to spaces upon reading, so we have to change the category
+% of the \cs{endlinechar}, or we have change the \cs{endlinechar} to a
+% character of a different category. We do the former, because the line a
+% macro starts on will already have the end-of-line character added, but
+% it will not yet have been tokenized, so its category can be changed. We
+% need a category that \TeX{} will not \emph{ignore}, but that
+% nevertheless has no (or minimal) side effects if encountered in a \TeX{}
+% file. We therefore make it active, but \cs{let} it equal to \cs{relax}.
+% This makes the end-line-character unexpandable since \cs{relax} is not
+% expandable.
% \begin{macrocode}
\mfp@DBlog{Direct output to Metafont/Post file.}%
-\begingroup
+{%
\catcode\mfp@ctrlM\active%
\gdef\@activectrlM{^^M}%
- \gdef\mfpicobeylines{\catcode\mfp@ctrlM\active\let^^M\relax}%
-\endgroup
+ \gdef\mfp@obeylines{\catcode\mfp@ctrlM\active\let^^M\relax}%
+}%
% \end{macrocode}
%
% The choice \cs{relax} seems to be actually a slightly better choice
% than \cs{endgraf} (which we used to use, and which equals the
% unexpandable primative \cs{par}) because \cs{relax} is permitted before
% the opening brace in the syntax of \meta{general text} (for token
-% variable assignments, \cs{write}, \cs{message}, and the like) as well as
-% being skipped in mfpic's version of \cs{@ifnextchar}.
+% variable assignments, \cs{write}, \cs{message}, and the like).
%
-% \DescribeMacro{\preservelines}
% The character assigned to \cs{endlinechar} is added to the end of all
% lines read in by \TeX. The character assigned to \cs{newlinechar}
% will produce a newline when it occurs in a \cs{write}. Our
-% \cs{preservelines} ensures these are the same and makes the above
-% changes in category.
+% \cs{mfp@keeplines} ensures these are the same and makes the above
+% changes in category. This will fail in the following case:
+% \cs{endlinechar} is not \verb$^^M$ and the data starts on the same line
+% as the command that issued \cs{mfp@keeplines}. But then the output will
+% lack only that first linebreak.
% \begin{macrocode}
-\newdef\preservelines{\newlinechar\mfp@ctrlM
- \endlinechar\newlinechar \mfpicobeylines}%
+\newdef\mfp@keeplines{\newlinechar\mfp@ctrlM
+ \endlinechar\newlinechar \mfp@obeylines}%
% \end{macrocode}
%
-% \DescribeMacro{\unpreservelines}
-% This is no longer used and may some day be removed. Grouping is
-% enough to isolate effects of \cs{preservelines}. The command
-% \cs{unpreservelines} simply restores the catcode of control-M to the
-% control-M to the usual in formats consistent with plain \TeX. If we
-% really wanted to reverse the effects of \cs{preservelines}, we'd have to
-% save and restore the four items changed: the values of \cs{newlinchar}
-% and \cs{endlinechar}, the category of control-M, and the definition of
-% an active control-M.
-% \begin{macrocode}
-\newdef\unpreservelines{\catcode\mfp@ctrlM=5 }%
-% \end{macrocode}
+% We want our file writes to issue \cs{mfp@keeplines} before the text is
+% read (and category codes assigned). Also we often want to add a few
+% items to the argument (figure macros append a semi-colon, list macros
+% surround with parentheses), so we isolate the common starting code in
+% \cs{mfp@write}, and then invoke the appropriate continuation command.
%
% \DescribeMacro{\mfsrc}
-% \DescribeMacro{\mfcmd}
-% \DescribeMacro{\mflist}
-% We want these to issue \cs{preservelines} before the text is read
-% (and category codes assigned). Also we often want to add a few items to
-% the argument (figure macros append a semi-colon, list macros surround
-% with parentheses), so we isolate the common starting code in
-% \cs{@mfwrite}, and then invoke the appropriate continuation command.
-% The ending code is in \cs{@mfsrc}. The \cs{begingroup} in
-% \cs{@mfwrite} matches the \cs{endgroup} in \cs{@mfsrc}. The group
-% isolates the changes to EOLs. We also equate \cs{@nl} to an active
-% control-M so that it can be used for line breaks in cases where it is
-% inconvenient to make EOLs active (in the replacement text of
+% This one is the most basic, merely writing its contents. For figure
+% macros, we ultimately call
+% \DescribeMacro{\mfcmd}\cs{mfcmd}, which appends a semicolon,
+% terminating any pending \MF{} command. For macros which take a comma
+% separated list of values as their arguments, we call
+% \DescribeMacro{\mflist}\cs{mflist} which surrounds the list in
+% parentheses and appends a semicolon
+%
+% The ending code is in \cs{mfp@src}. The \cs{begingroup} in
+% \cs{mfp@write} matches the \cs{endgroup} in \cs{mfp@src}. The group
+% isolates the changes to EOLs. We also define \cs{@nl} to expand to an
+% active control-M so that it can be used for line breaks in cases where
+% it is inconvenient to make EOLs active (in the replacement text of
% \cs{opengraphsfile} and \cs{@mfpic}, for example).
% \begin{macrocode}
-\newdef\mfsrc {\@mfwrite\@mfsrc\mfsrc}%
-\newdef\mfcmd {\@mfwrite\@mfcmd}%
-\newdef\mflist{\@mfwrite\@mflist}%
-\newdef\@mfwrite{\begingroup
- \preservelines \let\@nl\@activectrlM}%
+\newdef\mfsrc {\mfp@write\mfp@src\mfsrc}%
+\newdef\mfcmd {\mfp@write\mfp@cmd}%
+\newdef\mflist{\mfp@write\mfp@wlist}%
+\newdef\mfp@write{\begingroup\mfp@keeplines \let\@nl\@activectrlM}%
% \end{macrocode}
%
-% The macro \cs{@mfsrc} does the job of writing to files. The output
+% The macro \cs{mfp@src} does the job of writing to files. The output
% stream \cs{mfp@out} is declared later. The other two commands invoke
-% \cs{@mfsrc} on a slightly modified argument. The first argument to
-% \cs{@mfsrc} is the command that called it (for the error message), the
+% \cs{mfp@src} on a slightly modified argument. The first argument to
+% \cs{mfp@src} is the command that called it (for the error message), the
% second is the code to write.
% \begin{macrocode}
-\newdef\@mfsrc#1#2{%
- \@ifmfpfileisopen{\immediate\write\mfp@out{#2}}%
- {\nooutputfileerror{#1}}\endgroup}%
-\newdef\@mfcmd#1{\@mfsrc\mfcmd{#1;}}%
-\newdef\@mflist#1{\@mfsrc\mflist{(#1);}}%
+\newdef\mfp@src#1#2{%
+ \mfp@ifopengraphsfile{\immediate\write\mfp@out{#2}}%
+ {\mfp@nooutput@error{#1}}\endgroup}%
+\newdef\mfp@cmd#1{\mfp@src\mfcmd{#1;}}%
+\newdef\mfp@wlist#1{\mfp@src\mflist{(#1);}}%
% \end{macrocode}
%
% \subsection{Initializing the output file}\label{output}
%
% Category 12 characters percent, sharp and backslash signs for the
-% \file{.mf} file. The command \cs{mf@d} is used to write a line of
+% \file{.mf} file. The command \cs{mfp@d} is used to write a line of
% dashes above and below each figure to make them stand out when debugging
-% the file. This might be removed in the final distribution.
-% \begin{macrocode}
-\newdef\mfp@gobble#1{}%
-\edef\mf@p{\@xp\mfp@gobble\string\%}% percent (%)
-\edef\mf@s{\@xp\mfp@gobble\string\#}% sharp (#)
-\edef\mf@b{\@xp\mfp@gobble\string\\}% backslash (\)
-\newdef\mf@d{--------------}%
-% \end{macrocode}
-%
-% Used to mark ends of things, \cs{mfp@delim} should never be expanded,
-% but we give it a definition for debugging purposes:
+% the file.
% \begin{macrocode}
-\newdef\mfp@delim{%
- \mfp@errmsg {Misplaced \string\mfp@delim.}%
- {If you get this message but did not (mis)use the command^^J%
- \mfp@delim, please report this to the mfpic maintainer.}}%
+\edef\mfp@p{\@xp\@gobble\string\%}% percent (%)
+\edef\mfp@s{\@xp\@gobble\string\#}% sharp (#)
+\edef\mfp@b{\@xp\@gobble\string\\}% backslash (\)
+\newdef\mfp@d{--------------}%
% \end{macrocode}
%
% The current date and time stamp will be written to the \file{.mf} file.
@@ -1112,14 +1304,17 @@
% have been equated to the primitive commands \cs{year} and \cs{month}
% (which are then redefined).
% \begin{macrocode}
-{\mfp@ifdefined\normalmonth
- {\let\month\normalmonth\let\year\normalyear}{}%
- \xdef\mfp@today{\number\year/\ifnum\month<10 0\fi\number\month/%
- \ifnum\day<10 0\fi\number\day}%
- \count2=\time \divide\count2 by 60
- \xdef\mfp@now{\ifnum\count2<10 0\fi\number\count2:}%
- \multiply\count2 by -60 \advance\count2 \time
- \xdef\mfp@now{\mfp@now\ifnum\count2<10 0\fi\number\count2 }}%
+\mfp@ifdefined\two@digits{}%
+ {\def\two@digits#1{\ifnum#1<10 0\fi\number#1}}%
+{%
+ \mfp@ifdefined\normalmonth
+ {\let\month\normalmonth\let\year\normalyear}{}%
+ \count@\time \divide\count@ by 60
+ \xdef\mfp@now{\number\year/\two@digits\month/\two@digits\day
+ \space at \two@digits\count@:}%
+ \multiply\count@ by -60 \advance\count@\time
+ \xdef\mfp@now{\mfp@now\two@digits\count@}%
+}%
% \end{macrocode}
%
% These registers are allocated:
@@ -1128,17 +1323,17 @@
% \item \cs{mfp@graph} Read stream used to test existence of files and
% for reading datafiles.
% \item \cs{mfp@count} Number for next \env{mfpic} environment.
-% \item \cs{mfp@linetype} Current line style in multi-curve data plots.
-% \item \cs{mfp@n} Scratch counter, so far used only in writing data to
-% the \file{.mf} file.
+% \item \cs{mfp@linetype} Number indexing current line style in
+% multi-curve data plots.
% \item \cs{mfp@sequence} Counter incremented with every datapoint written
-% from a file to implement the feature that a list of numeric data can be
-% plotted against its sequence position.
+% from a file to implement the feature that a list of numeric data
+% can be plotted against its sequence position.
% \item \cs{mfp@toks} Temporary token register.
-% \item \cs{every@tlabel} Tokens inserted at every \cs{tlabel}.
% \item \cs{mfp@verbtex} A temporary token list used by \cs{mfpverbtex}.
% \item \cs{mfp@commonverbatimtex} Tokens written at the start of every
-% output file inside a \mfc{verbatimtex} group.
+% output file inside a \mfc{verbatimtex} group.
+% \item \cs{ifmfp@switch} Not exactly a register, but a temporary
+% switch for passing information from one macro to another.
% \end{itemize}
% \begin{macrocode}
\mfp@DBlog{Declarations used with data and graphs files.}%
@@ -1147,13 +1342,11 @@
\newlet\mfp@data\mfp@graph
\newcount\mfp@count
\newcount\mfp@linetype
-\newcount\mfp@n
\newcount\mfp@sequence
\newtoks\mfp@toks
-\newtoks\every@tlabel
\newtoks\mfp@verbtex
\newtoks\mfp@commonverbatimtex
-\every@tlabel{}%
+\newif\ifmfp@switch
% \end{macrocode}
%
% We store most of the \mfc{verbatimtex} block that \mfpic{} relies on in
@@ -1161,25 +1354,152 @@
% relies on a couple of catcode changes and we want to be in a situation
% where we have that under control. Each line will end with an active
% ctrl-M, which is transformed by \cs{mfsrc} into a newline.
+%
+% The \cs{mpxshipout} is to cover all three cases of the output of
+% `\texttt{mpto -tex}'. In public versions of \MP{} prior to 0.9 there was
+% no such command in that output file. In 0.9 and 0.901 that command is
+% defined at the start of the file, prior to any verbatimtex, but it is
+% buggy. This code overwrites it and fixes the bugs. In later versions a
+% good definition is used, but it appears only just before the first
+% \mfc{btex} and so will overwrite my definition. Thus all cases are
+% served.
% \begin{macrocode}
\begingroup
- \let\newtoks\relax
- \@makeother\%\@makeother\#\preservelines
- \global\mfp@commonverbatimtex=
- {\def\MFPtext#1{%
- \vbox{\def\\{\cr}\MFPcfont\everylabel
- \halign{##\hfil\cr#1\crcr}}}%
+ \mfp@keeplines\@makeother\#%
+ \@makeother\%\global\mfp@commonverbatimtex
+ {\gdef\mpxshipout{\afterassignment\middlempxshipout\setbox0=\hbox}%
+ \gdef\middlempxshipout{\aftergroup\finishmpxshipout}%
+ \gdef\finishmpxshipout{\dimen1=\ht0 \dimen2=\dp0
+ \dimen0=\dimen1 \advance\dimen0\dimen2
+ \setbox0=\hbox{\hbox{\box0
+ \ifnum\dimen0>0 \vrule width1sp height\dimen1 depth\dimen2
+ \else \vrule width1sp height1sp depth0sp\relax
+ \fi}}%
+ \ht0=0pt \dp0=0pt \shipout\hbox{\box0}}%
+ \def\MFPtext#1{\vbox{\def\\{\cr}\MFPcfont\everylabel
+ \halign{##\hfil\cr#1\crcr}}}%
}\endgroup
% \end{macrocode}
%
-% \DescribeMacro{\opengraphsfile}
-% This is the main command to set up the output file. It also aims to
-% check whether \MF{} or \MP{} has already been run on the output file,
-% thereby determining whether the graphics can be included. It does this
-% by checking for the existence of one of the products of these programs.
-% In the case of \MF{} it looks for a \file{.tfm} file; in the case of
-% \MP{} it looks for the first output graphic.
+% We store the first several lines that will be written to the output
+% file in a few internal macros, so that changing them will be easier.
%
+% The first two lines of the output file are comments identifying the
+% file, the \mfpic{} version, the tex source file, and the timestamp.
+% These are stored in \cs{mfp@versioninfo}. We also let \grafbase{} know
+% the \mfpic{} version creating the file. This could catch version
+% mismatches. But since older \grafbase{} didn't test this, some
+% mismatches will be missed.
+%
+% After that (\cs{mfp@init@outfile}) we set the \mfc{mode}, the \mfc{mag}
+% (both ignored by \MP), and arrange for \grafbase{} macros to be loaded.
+% \begin{macrocode}
+\newdef\mfp@versioninfo{%
+ \mfp@p\space\mfp@filename.m\mfp@ifmpost{p}{f},
+ generated by Mfpic, v\mfpfileversion\space\mfpfiledate,\@nl
+ \mfp@p\space from TeX source "\jobname" on \mfp@now\@nl
+ numeric mfpicversion; mfpicversion:=\mfpicversion;\@nl}%
+\newdef\mfp@init@outfile{%
+ if unknown mode: mode = localfont; fi\@nl
+ if unknown mag: mag = \number\mag/1000; fi\@nl
+ if unknown grafbase: input grafbase; fi\@nl\@nl}%
+% \end{macrocode}
+% Then a comparison of the version of this file, stored in
+% \cs{mfpicversion} with the version stored in the \grafbase{} variable
+% \mfc{grafbaseversion}. This should catch a mismatch caused by an old
+% \grafbase{} with a current \mfpic.
+% \begin{macrocode}
+\newdef\mfp@versioncheck{%
+ if unknown grafbaseversion: grafbaseversion = 0; fi\@nl
+ if grafbaseversion <> mfpicversion :\@nl
+ GBmsg "Bad mfpic installation: mfpic and grafbase %
+ versions do not match.";\@nl
+ fi\@nl}%
+% \end{macrocode}
+%
+% This opens the output file and prints a reminder to the user to run
+% \MF{} or \MP{}.
+% \begin{macrocode}
+\newdef\mfp@openfile{%
+ \ifmfp@nowrite \else \mfp@msg{}%
+ \Mfpic@msg{Initializing \mfp@filename\mfp@ext.}%
+ \immediate\openout\mfp@out=\mfp@filename\mfp@ext\relax
+ \Mfpic@msg{Don't forget to process \mfp@filename\mfp@ext!}%
+ \Mfpic@msg{Apply \mfp@progname\space to \mfp@filename\mfp@ext%
+ \mfp@ifmpost{}{, then gftopk to the resulting gf file}.}%
+ \Mfpic@msg{Then reprocess this file "\jobname".\@nl}%
+ \fi}%
+% \end{macrocode}
+%
+% A little bit of \LaTeX{} for checking file existence.
+% \begin{macrocode}
+\mfp@ifundefined\IfFileExists
+ \def\IfFileExists#1{\openin\mfp@graph=#1\relax
+ \ifeof\mfp@graph \@xp\@secondoftwo
+ \else \immediate\closein\mfp@graph \@xp\@firstoftwo
+ \fi}%
+ \def\InputIfFileExists#1#2{\IfFileExists{#1}{#2\input #1\relax}}%
+\mfp@enddef
+% \end{macrocode}
+%
+% The following is the code involved in opening the output file and
+% checking for the existence of already created figures.
+% \cs{mfp@checkoutput} checks if the output file of \MP{} or \MF{} exists.
+% The first argument is the extension of that output file (\file{tfm} for
+% \MF{}, the starting value of the figure counter for \MP{}). We use the
+% existence of that output as a signal that there are graphics to be
+% included.
+%
+% Based on the file existence, we set the values of the draft switch, and
+% issue a message. Any other actions needed are passed as \arg2 (it
+% exists) and \arg3 (it doesn't). These are nonempty only under the
+% \opt{metafont} option: we load the font only if the \file{.tfm} exists,
+% otherwise we use a the \texttt{dummy} font.
+%
+% We do not try to load a nonexistent font because that might trigger some
+% \prog{maketfm} script which could be disastrous at this point, when the
+% \file{.mf} file is in an uncertain state.
+% \begin{macrocode}
+\newdef\mfp@checkoutput#1#2#3{%
+ \IfFileExists{\setfilename\mfp@filename{#1}}%
+ {\global\mfpicdraftfalse #2}%
+ {\global\mfpicdrafttrue #3\mfp@msg{}%
+ \Mfpic@msg{No file \setfilename\mfp@filename{#1}.}%
+ }}%
+\newdef\mfp@checkMPoutput{\mfp@checkoutput{\number\mfp@count}{}{}}%
+\newdef\mfp@checkMFoutput{\mfp@checkoutput{tfm}%
+ {\global\font\mfp@graphfont\mfp@filename\relax}%
+ {\global\let\mfp@graphfont\mfp@dummy}}%
+% \end{macrocode}
+%
+% For \opt{metapost} the output file needs to load some color macros, and
+% write some setup for text labels (even if \cs{nomplabels}, because
+% \cs{usemplabels could be turned on later}). \MF{} now can also handle
+% the definitions in \file{dvipsnam.mp} but it seems a waste to load all
+% those definitions just to get more names for grayscales (even given
+% that \MF{} can find that file).
+% \begin{macrocode}
+\newdef\mfp@MPextras{\mfp@toks\@xp{\every@tlabel}%
+ \mfsrc{%
+ if unknown Apricot: input dvipsnam.mp; fi\@nl
+ verbatimtex\@nl
+ \ifmfp@verbtex \the\mfp@verbtex\@nl \fi
+ \the\mfp@commonverbatimtex\@nl
+ \def\noexpand\everylabel{\the\mfp@toks}\@nl
+ etex;\@nl}%
+ \global\mfp@verbtexfalse \global\mfp@verbtex{}%
+% \end{macrocode}
+% Finally, we process the \MP{}-specific options as they are currently
+% known. The \cs{usemplabels} was delayed until now so that the order of
+% it and \cs{usemetapost} before now would not matter.
+% \begin{macrocode}
+ \@ifmplabels\usemplabels{}%
+ \ifmfp@truebbox \usetruebbox \fi
+ \ifmfp@overlabels \overlaylabels \fi}%
+% \end{macrocode}
+%
+% \DescribeMacro{\opengraphsfile}
+% This is the main command to set up the output file.
% The name of the output file is its only parameter, and it appends
% the extension \file{.mf} or \file{.mp} depending on the whether option
% \opt{metafont} or \opt{metapost} was chosen.
@@ -1188,173 +1508,165 @@
\newdef\opengraphsfile#1{\@bsphack
% \end{macrocode}
% A command can determine if it occurs after \cs{opengraphsfile} but
-% before \cs{closegraphsfile} by testing whether \cs{mfp@filename} is defined.
+% before \cs{closegraphsfile} by testing whether \cs{mfp@filename} is
+% defined. We also save the name of the processing program (\MF{} or \MP)
+% and the file extension (\file{.mf} of \file{.mp}) in macros for use in
+% messages.
% \begin{macrocode}
- \@ifmfpfileisopen%
+ \def\mfp@tempa{#1}%
+ \mfp@ifopengraphsfile%
{\Mfpic@warn
- {An attempt has been made to open a graph file, #1,
- \@msgbreak while another, \mfp@filename, is still open.
- \@msgbreak Closing \mfp@filename.}%
- \@closegraphsfile}%
+ {\@nl An attempt has been made to open a graph file,
+ \mfp@sanitized\mfp@tempa\@msgbreak
+ while another, \mfp@filename, is still open. \@msgbreak
+ Closing \mfp@filename.}\mfp@closegraphsfile}%
{}%
- \xdef\mfp@filename{#1}%
-% \end{macrocode}
-% The value of \cs{mfp@count} should be the number of the first figure.
-% We use the existence of that figure as a signal that there are graphics
-% to be included.
-% \begin{macrocode}
- \mfp@ifmpost{%
- \openin\mfp@graph=\setfilename{\mfp@filename}{\number\mfp@count}%
- \relax
- \ifeof\mfp@graph \mfp@msg{}%
- \Mfpic@msg
- {No file \setfilename{\mfp@filename}{\number\mfp@count}.^^J}%
- \global\mfpicdrafttrue
- \else
- \global\mfpicdraftfalse
- \fi
-% \end{macrocode}
-% Type out the \MP-specific portion of a reminder to process the
-% output. Then on to the \MF{} case.
-% \begin{macrocode}
- \if@mfp@nowrite \else
- \immediate\openout\mfp@out=\mfp@filename.mp\relax
- \Mfpic@msg{Don't forget to process \mfp@filename.mp! %
- \@msgbreak (Apply metapost to \mfp@filename.mp.)}%
- \fi
- }{% metafont
-% \end{macrocode}
-% Under the \opt{metafont} option, we check for the \file{.tfm} file.
-% We do not simply try to load the font because that might trigger
-% some \prog{maketfm} script which could be disastrous at this point,
-% when the \file{.mf} file is in an uncertain state. We open the
-% \file{.tfm} for input, and check \cs{ifeof}, as we did in the
-% \MP{} graphic case.
-% \begin{macrocode}
- \openin\mfp@graph=\mfp@filename.tfm\relax
- \ifeof\mfp@graph \mfp@msg{}%
- \Mfpic@msg{No file \mfp@filename.tfm .^^J}%
- \global\let\@graphfont=\mfp@dummy
- \global\mfpicdrafttrue
+ \xdef\mfp@filename{\mfp@sanitized\mfp@tempa}
+ \xdef\mfp@progname{meta\mfp@ifmpost{pos}{fon}t}%
+ \xdef\mfp@ext{.m\mfp@ifmpost{p}{f}}%
% \end{macrocode}
-% If it does exist, we load the font and in either case type out the
-% \MF{} specific portion of our reminder.
-% \begin{macrocode}
- \else
- \global\font\@graphfont=\mfp@filename\relax
- \global\mfpicdraftfalse
- \fi
- \if@mfp@nowrite \else
- \immediate\openout\mfp@out=\mfp@filename.mf\relax
- \Mfpic@msg{Don't forget to process \mfp@filename.mf! %
- \@msgbreak (Apply metafont to \mfp@filename.mf, %
- then gftopk to the resulting gf file.)}%
- \fi
- }%
-% \end{macrocode}
-% The input stream used for testing is closed and the
-% unspecific portion of the reminder is typed out, then
-% the above settings of \cs{ifmfpicdraft} are overridden if the user has
-% explicitly chosen the \opt{draft} or \opt{final} option. The
-% \opt{nowrite} option merely disables the \cs{@mfsrc} command.
-% \begin{macrocode}
- \closein\mfp@graph
- \if@mfp@nowrite\else
- \Mfpic@msg{Then reprocess this file (\jobname).^^J}%
- \fi
- \if@mfp@draft\global\mfpicdrafttrue\fi
- \if@mfp@final\global\mfpicdraftfalse\fi
- \if@mfp@nowrite\gdef\@mfsrc##1##2{}\fi
-% \end{macrocode}
-% The first two lines of the output file are comments identifying the
-% file, the \mfpic{} version, the tex source file, and the date.
-% Then we let \grafbase{} know the \mfpic{} version creating the file.
-% After that we set the \mfc{mode}, the \mfc{mag} (both ignored by \MP),
-% and arrange for \grafbase{} macros to be loaded. The current
-% \grafbase{} checks the \gbc{mfpicversion}: if it is unknown, or not
-% equal to the value of \gbc{grafbaseversion}, a diagnostic message is
-% printed.
-% \begin{macrocode}
- \mfsrc{%
- \mf@p\space\mfp@filename.m\mfp@ifmpost{p}{f}, %
- generated by Mfpic, v\mfpfileversion\space\mfpfiledate,\@nl
- \mf@p\space from TeX source "\jobname" %
- on \mfp@today\space at \mfp@now\@nl
- numeric mfpicversion; mfpicversion:=\mfpicversion;\@nl
- if unknown mode: mode := localfont; fi\@nl
- if unknown mag: mag := \number\mag/1000; fi\@nl
- if unknown grafbase: input grafbase; fi\@nl\@nl
-% \end{macrocode}
-% Then a comparison of the version of this file, stored in
-% \cs{mfpicversion} with the version stored in the \grafbase{} variable
-% \mfc{grafbaseversion}. This should catch mismatches the previous test
-% would miss.
+%
+% These commands test for the existence of the graphics (\file{.tfm} file
+% or first EPS file) and provisionally set \cs{ifmfpicdraft} based on the
+% result. Then the \file{.mf} or \file{.mp} file is opened, and the
+% automatic settings of \cs{ifmfpicdraft}, etc., are overridden if the
+% user has explicitly chosen the \opt{draft} or \opt{final} option. The
+% \opt{nowrite} option merely disables the \cs{mfp@src} command, called
+% by all the commands that write to the file.
% \begin{macrocode}
- if unknown grafbaseversion: grafbaseversion := 0; fi\@nl
- if grafbaseversion <> mfpicversion :\@nl
- GBmsg "Bad mfpic installation: mfpic and grafbase %
- versions do not match.";\@nl
- fi\@nl}%
+ \mfp@ifmpost\mfp@checkMPoutput\mfp@checkMFoutput
+ \mfp@openfile
+ \ifmfp@draft \global\mfpicdrafttrue \fi
+ \ifmfp@final \global\mfpicdraftfalse \fi
+ \ifmfp@nowrite \gdef\mfp@src##1##2{\endgroup}\fi
% \end{macrocode}
-% For \opt{metapost} we need to load some color macros also, and write
-% some setup for text labels (even if \cs{nomplabels}, because
-% \cs{usemplabels could be turned on later}). \MF{} now can also handle
-% the definitions in \file{dvipsnam.mp} but it seems a waste to load all
-% those definitions just to get a few more grayscales (even given \MF{}
-% can find that file).
+%
+% Now we set up the header and preamble of the output file, writing
+% version info, initializations, version checking code and extra code
+% for \MP{}, if required.
+%
+% Finally, we process options as they are currently known. These were
+% delayed until now because they might require a write to the output file.
+% Finally, we read the \prog{meta*o*t} log file, if the option to do that
+% is in effect.
% \begin{macrocode}
- \mfp@ifmpost
- {\mfsrc{%
- if unknown Apricot: input dvipsnam.mp; fi\@nl
- verbatimtex\@nl
- \the\mfp@commonverbatimtex\@nl
- \def\noexpand\everylabel{\the\every@tlabel}\@nl
- \if@mfp@verbtex \the\mfp@verbtex\@nl \fi
- etex;\@nl}%
- \global\@mfp@verbtexfalse\mfp@verbtex{}%
-% \end{macrocode}
-% Finally, we write information to the output file, and/or set switches
-% based on the options as they are currently known. The \cs{usemplabels}
-% was delayed until now so that the order of it and \cs{usemetapost}
-% before now would not matter. The other five are issued now because they
-% might require a write to the output file.
-% \begin{macrocode}
- \@ifmplabels{\usemplabels}{}%
- \if@mfp@truebbox \usetruebbox \fi
- \if@mfp@overlabels \overlaylabels \fi}{}%
- \if@mfp@clip \clipmfpic \fi
- \if@mfp@clearsym \clearsymbols \fi
- \if@shipping\else\stopshipping\fi
+ \mfsrc{\mfp@versioninfo\mfp@init@outfile\mfp@versioncheck}%
+ \mfp@ifmpost\mfp@MPextras{}%
+ \ifmfp@clip \clipmfpic \fi
+ \ifmfp@clearsym \clearsymbols \fi
+ \ifmfp@noship \stopshipping \fi
+ \ifmfp@readlog \mfp@readlogfile \fi
\@esphack}%
% \end{macrocode}
%
% \DescribeMacro{\closegraphsfile}
-% This finishes off the output files and undefines \cs{mfp@filename}.
-% We define an error message free version \cs{@closegraphsfile} for
+% This command finishes off the output file and undefines \cs{mfp@filename}.
+% We define an error message free version \cs{mfp@closegraphsfile} for
% internal use. We unconditionally undefine \cs{mfp@filename} just in
% case.
% \begin{macrocode}
\newdef\closegraphsfile{%
- \@ifmfpfileisopen{}{\nooutputfileerror\closegraphsfile}%
- \@closegraphsfile \global\let\mfp@filename\UndEfInEd}%
-\newdef\@closegraphsfile{%
- \@ifmfpfileisopen
- {\if@mfp@nowrite \else
+ \mfp@ifopengraphsfile{}{\mfp@nooutput@error\closegraphsfile}%
+ \mfp@closegraphsfile}%
+\newdef\mfp@closegraphsfile{%
+ \mfp@ifopengraphsfile
+ {\ifmfp@nowrite \else
\mfsrc{\@nl end.}\immediate\closeout\mfp@out
- \fi}{}}%
-% \end{macrocode}
-%
-% \DescribeMacro{\nooutputfileerror}
-% We define a generic error message for macros that need an output file
-% to write to. \cs{mfsrc} uses it, as does \cs{closegraphsfile}.
-% \cs{mfpic} also uses it even though all its writes take place through
-% \cs{mfsrc}. This avoids potentially hundreds of error messages.
-% \begin{macrocode}
-\def\nooutputfileerror#1{%
- \mfp@errmsg {No output file has been opened.}%
- {You have tried to used a command, #1, that requires an output^^J%
- file to be opened with the \opengraphsfile command, which^^J%
- was either not issued or was not successful.}}%
+ \fi \global\let\mfp@filename\UndEfInEd}{}}%
+% \end{macrocode}
+%
+% \subsection{Information from \MF: reading its log file}
+%
+% \DescribeMacro{\assignmfvalue}\SpecialUsageIndex{\globalassignmfvalue}
+% This macro was suggested by Werner Lemberg, who also supplied working
+% code. I have made modifications, so I am to blame if it has bugs. It
+% writes code to the \file{.mf} file that causes \MF{} to write
+% information to its \file{.log} file. If the option \opt{mfpreadlog} has
+% been set, that log file will be examined for that information. The
+% result, for example, of
+% \begin{verbatim}
+% \setmfvariable{numeric}{s}{10}
+% \assignmfvalue\foo{s+2}
+% \end{verbatim}
+% is that \cs{foo} will be a macro that expands to `\texttt{12}' (during
+% the \TeX{} run following the \MF{} pass). Under \opt{mplabels} a label
+% containing \cs{foo} will only be correct in the \file{.mp} file after
+% the second \MP{} pass, and so a third \TeX{} run may be required to
+% insert the correct figure. To implement localization, the code causes
+% the internal command \cs{mfpic-\meta{N}-foo} to be defined to the value,
+% where \meta{N} is the number of the current figure. The \cs{foo} is then
+% defined to the value of that internal command. Outside any \env{mfpic}
+% environment, the internal command is \cs{mfpic--foo}. If the \file{.log}
+% file doesn't exist or doesn't yet contain the information, \cs{foo} is
+% defined to `???'.
+%
+% While \cs{mfpicvalue} is merely a signal and doesn't need a
+% definition, we give it a useful definition for debugging: its own name.
+% It could be accidentally redefined by a user, but that will be harmless;
+% the debugging is mainly for me.
+%
+% It is possible to put \cs{global} in front of \cs{assignmfvalue} and
+% its macro will be defined globally in the \TeX{} run, but I could find
+% no way to make it global in the \file{.mp} file under \opt{mplabels}.
+% Thus, I've created a \cs{globalassignmfvalue}.
+%
+% \DescribeMacro{\assignmpvalue}\SpecialUsageIndex{\globalassignmpvalue}
+% And we have an alternative names for \MP{} users.
+% \begin{macrocode}
+\edef\mfpicvalue{\string\mfpicvalue}%
+\newdef\mfp@cmdname{%
+ mfpic-\ifin@mfpicenv\number\mfp@count\fi-\@xp\@gobble\string}%
+\newdef\assignmfvalue{\mfp@assign\@empty}%
+\newdef\globalassignmfvalue{\mfp@assign\global}%
+\newdef\mfp@assign#1#2#3{%
+ #1\edef#2{%
+ \@ifundefined{\mfp@cmdname#2}{???}{\@nameuse{\mfp@cmdname#2}}}%
+ \@ifundefined{\mfp@cmdname#2}%
+ {\mfp@msg{}\Mfpic@msg{No value for \string#2 in mfpic figure
+ \number\mfp@count.\@nl}}%
+ {}%
+ \mfsrc{\@ifmplabels{\@nl verbatimtex #1\def\string#2{#2} etex;\@nl}{}%
+ message "\string\mfpicvalue";\@nl
+ message "\mfp@cmdname#2";\@nl
+ show #3;\@nl
+ message "";}}%
+\newlet\assignmpvalue\assignmfvalue
+\newlet\globalassignmpvalue\globalassignmfvalue
+% \end{macrocode}
+%
+% The following code is what really makes \cs{assignmfvalue} work. It is
+% similar to code in the \prog{metatex} package, adapted by Werner Lemberg
+% and then modified by me. It steps through the \mfpic{} output file looking
+% for this signal: a line containing only `\verb$\mfpicvalue$'. It then
+% reads the next line, creates a macro name from it, and assigns to that
+% macro the value it reads on the next line. This is repeated to the end
+% of the file.
+% \begin{macrocode}
+\newdef\strip@gtgt>> #1\mfp@end{#1}%
+\newdef\mfp@readlogfile{%
+ \openin\mfp@data=\mfp@filename.log\relax
+ \ifeof\mfp@data \Mfpic@msg{No file \mfp@filename.log.}%
+ \else
+ {%
+ \mfp@resetwhitespace \endlinechar -1
+ \catcode`\>12
+ \def\@mfpicvalue{\mfpicvalue}%
+ \message{< Mfpic: Reading \mfp@filename.log}%
+ \mfp@readloglines
+ \message{>}%
+ }%
+ \fi \closein\mfp@data}%
+\newdef\mfp@readloglines{%
+ \read\mfp@data to \mfp@tempa
+ \ifeof\mfp@data
+ \else
+ \ifx\mfp@tempa\@mfpicvalue
+ \read\mfp@data to \mfp@tempa
+ \read\mfp@data to \mfp@tempb
+ \@XP\xdef\@nameuse\mfp@tempa{\@xp\strip@gtgt\mfp@tempb\mfp@end}%
+ \fi
+ \@xp\mfp@readloglines
+ \fi}%
% \end{macrocode}
%
%
@@ -1371,6 +1683,7 @@
% \begin{macrocode}
\mfp@DBlog{Using dummy font's dimensions, to spare TeX registers.}%
% \end{macrocode}
+%
% While most fonts have relatively few font dimensions (twenty-two is a
% common number), it turns out that if one loads a font and immediately
% assigns a value to \cs{fontdimen}~$n$, Then \TeX{} acts as if the font
@@ -1385,7 +1698,7 @@
% some unlikely value, which \TeX{} treats as a new font.
% \begin{macrocode}
\font\mfp@dummy=dummy scaled 1042
-\fontdimen100\mfp@dummy=0pt
+\fontdimen100\mfp@dummy0pt
% \end{macrocode}
%
% Some versions of \file{dummy.tfm} have values already assigned to font
@@ -1396,25 +1709,28 @@
% We use an auxiliary count register to keep track of new font dimens.
% \begin{macrocode}
\newcount\mfp@fdc
-\mfp@fdc=22
+\mfp@fdc22
% \end{macrocode}
-% \DescribeMacro{\newfdim}
+%
% We define a macros that mimics the behavior of \cs{newdimen}, for
% allocating font dimension numbers. It increments \cs{mfp@fdc} and
-% defines its argument \texttt{\#1} to expand to an assignment of the
-% current \cs{fontdimen}.
+% defines its argument \arg{1} to expand to a reference to the
+% \cs{fontdimen} with that number. The syntax it produces is valid as
+% the left side of a dimension assignment and also as a dimension
+% that may follow a real factor and \cs{the}. For example, later we say
+% \cs{newfdim}\cs{pointsize} and give it the value \texttt{2pt}. Then
+% \verb$\the\pointsize$ prints this value and
+% \verb$\pointsize = 0.5\pointsize$ is a valid assignment.
% \begin{macrocode}
\newdef\newfdim#1{%
- \global\advance\mfp@fdc 1
+ \global\advance\mfp@fdc1
\xdef#1{\fontdimen\number\mfp@fdc\mfp@dummy}%
- \wlog{\string#1=\string\fontdimen\number\mfp@fdc\mfp@dummy}}%
+ \wlog{\string#1=\fontdimen\number\mfp@fdc\mfp@dummy}}%
% \end{macrocode}
-% The definition (\cs{xdef}) is global since font dimension
-% assignments are always global anyway. \TeX{} will not accept font
-% dimensions in its syntax for the dimension arithmetic commands
-% \cs{advance}, \cs{multiply} and \cs{divide}. In the rare cases where we
-% need to do this we assign the value to a temporary register and
-% calculate with it. It is, however OK to use them with a numeric factor.
+% \TeX{} will not accept font dimensions in its syntax for the dimension
+% arithmetic commands \cs{advance}, \cs{multiply} and \cs{divide}. In the
+% rare cases where we need to do this we assign the value to a temporary
+% register and calculate with it.
%
% We allocate a couple of boxes. At the end of an \mfpic{}
% environment, \cs{@wholegraph} is loaded with the picture (possibly
@@ -1427,38 +1743,31 @@
and a temporary one.}%
\newbox\@wholegraph
\newbox\@textbox
-\newdef\tb@ht{\ht\@textbox}% height,
-\newdef\tb@dp{\dp\@textbox}% depth and
-\newdef\tb@wd{\wd\@textbox}% width of the text label.
+\newdef\tb@ht{\ht\@textbox}%
+\newdef\tb@dp{\dp\@textbox}%
+\newdef\tb@wd{\wd\@textbox}%
% \end{macrocode}
%
% When labels are processed, they are accumulated in the box
% \cs{@alltlabels}. We allocate it, and four \cs{newfdim}\,s for the
-% location of the four sides.
+% location of three of the sides (We don't need the fourth). The box
+% \cs{@backtext} holds `background text', that is, text accumulated
+% between \cs{startbacktext} and \cs{stopbacktext}.
% \begin{macrocode}
\newbox\@alltlabels
+\newbox\@backtext
\newfdim\@tlabelstop
\newfdim\@tlabelsbot
\newfdim\@tlabelsleft
-\newfdim\@tlabelsright
% \end{macrocode}
%
-% It can be argued that dimensions not needed by \TeX{} should be stored
-% in the output file rather than allocating (font) dimensions. These are
-% everything below from \cs{pointsize} through \cs{symbolspace},
-% inclusive. This would make syncronictity between \TeX{} and \MF{} easier
-% to maintain. Those that are needed by \TeX{} are then few enough to be
-% regular \cs{dimen} registers, making them automatically local to each
-% \env{mfpic} environment. Unfortunately, changing to such a scheme could
-% break some past \mfpic{} files. It would also require recoding every
-% command that now writes one of these dimensions to the output.
-%
-% One of these (\cs{@graphright}) is unused, except for debugging.
-% We could also code around \cs{@graphwd} because it is always
-% \cs{wd}\cs{@wholegraph}. ^^A Is this true? Check it out.
+% The following fontdims record the dimensions of the box containing the
+% picture. We need to adjust them when the text labels are added to the
+% picture, because \TeX{} doesn't do it automatically. For example,
+% kerning left outside the picture will not change the picture box's
+% reference point.
% \begin{macrocode}
\mfp@DBlog{Internal dimension parameters for graph dimensions...}%
-\newfdim\@graphwd
\newfdim\@graphright
\newfdim\@graphleft
\newfdim\@graphtop
@@ -1471,51 +1780,98 @@
%
% The following are for label adjustments. The first two are used to
% implement the feature that all labels can be simultaneously shifted an
-% equal amount. The last is for the feature that the label can be a
-% certain distance separated from the point where it is placed.
+% equal amount. The third is for the feature that the label can be a
+% certain distance separated from the point where it is placed. And the
+% last establishes the distance between a label and a path surrounding it.
% \begin{macrocode}
\newfdim\tlabel@hadj
\newfdim\tlabel@vadj
\newfdim\tlabel@sep
-\tlabel@hadj=0pt
-\tlabel@vadj=0pt
-\tlabel@sep=0pt
+\newfdim\tlpath@sep
+\tlabel@hadj0pt
+\tlabel@vadj0pt
+\tlabel@sep0pt
+\tlpath@sep0pt
% \end{macrocode}
%
+% It can be argued that dimensions not needed by \TeX{} should be stored
+% in the output file rather than allocating (font) dimensions. These are
+% everything below from \cs{pointsize} through \cs{symbolspace},
+% inclusive. This would make synchrony between \TeX{} and \MF{} easier
+% to maintain. Those that are needed by \TeX{} are then few enough to be
+% regular \cs{dimen} registers, making them automatically local to each
+% \env{mfpic} environment. Unfortunately, changing to such a scheme could
+% break some past \mfpic{} files. It would also require recoding every
+% command that now writes one of these dimensions to the output.
+%
% The following are default parameters for various graphics commands that
-% take an optional argument for the size of something. We allocate them,
-% and then initialize them.
+% take an optional argument for the size of something.
+% \begin{itemize}
+% \item\cs{mfpicunit}\SpecialUsageIndex{mfpicunit}, the unit of length in
+% a picture. \cs{@mfpicunit} is used internally.
+% \item\cs{pointsize}\SpecialUsageIndex{pointsize}, the diameter of a
+% \cs{point}.
+% \item\cs{shadespace}\SpecialUsageIndex{shadespace}, the spacing of dots
+% in \cs{shade}.
+% \item\cs{polkadotspace}\SpecialUsageIndex{polkadotspace}, the spacing of
+% polkadots.
+% \item\cs{hatchspace}\SpecialUsageIndex{hatchspace}, the spacing of hatch
+% lines.
+% \item\cs{headlen}\SpecialUsageIndex{headlen}, the size of arrowheads
+% (except on axes).
+% \item\cs{axisheadlen}\SpecialUsageIndex{axisheadlen}, the size of the
+% arrowhead for $x$- and $y$-axes.
+% \item\cs{sideheadlen}\SpecialUsageIndex{sideheadlen}, the size of the
+% arrowhead for border axes.
+% \item\cs{hashlen}\SpecialUsageIndex{hashlen}, the length of tic marks on
+% axes.
+% \item\cs{dashlen}\SpecialUsageIndex{dashlen}, the length of dashes in
+% \cs{dashed}.
+% \item\cs{dashspace}\SpecialUsageIndex{dashspace}, the space between such
+% dashes.
+% \item\cs{dotsize}\SpecialUsageIndex{dotsize}, the size of dots for
+% \cs{dotted}.
+% \item\cs{griddotsize}\SpecialUsageIndex{griddotsize}, the size of dots
+% for \cs{grid} and \cs{plrgridpoints}.
+% \item\cs{dotspace}\SpecialUsageIndex{dotspace}, the space between such
+% dots.
+% \item\cs{symbolspace}\SpecialUsageIndex{symbolspace}, the space between
+% symbols used in a \cs{plot} command.
+% \end{itemize}
+% We allocate them, and then initialize them.
% \begin{macrocode}
\mfp@DBlog{User level dimension parameters, with default settings.}%
-\newfdim\mfpicunit % unit of length
-\newfdim\pointsize % diameter of circle
-\newfdim\shadespace % spacing of shade dots
-\newfdim\polkadotspace % spacing of polkadots
-\newfdim\hatchspace % spacing of lines
-\newfdim\headlen % size of arrowhead (\arrow)
-\newfdim\axisheadlen % size of arrowhead (x, y axes)
-\newfdim\sideheadlen % size of arrowhead (border axes)
-\newfdim\hashlen % length of marks on axes
-\newfdim\dashlen % length of dashes (\dashed)
-\newfdim\dashspace % space between dashes
-\newfdim\dotsize % size of dots (\dotted)
-\newfdim\dotspace % space between dots
-\newfdim\symbolspace % space between symbols (\plot)
-%
-\mfpicunit=1pt
-\pointsize=2pt
-\shadespace=1pt
-\polkadotspace=10pt
-\hatchspace=3pt
-\headlen=3pt
-\axisheadlen=5pt
-\sideheadlen=0pt
-\hashlen=4pt
-\dashlen=4pt
-\dashspace=4pt
-\dotsize=0.5pt
-\dotspace=3pt
-\symbolspace=5pt
+\newfdim\mfpicunit
+\newfdim\@mfpicunit % internal version
+\newfdim\pointsize
+\newfdim\shadespace
+\newfdim\polkadotspace
+\newfdim\hatchspace
+\newfdim\headlen
+\newfdim\axisheadlen
+\newfdim\sideheadlen
+\newfdim\hashlen
+\newfdim\dashlen
+\newfdim\dashspace
+\newfdim\dotsize
+\newfdim\griddotsize
+\newfdim\dotspace
+\newfdim\symbolspace
+\mfpicunit1pt
+\pointsize2pt
+\shadespace1pt
+\polkadotspace10pt
+\hatchspace3pt
+\headlen3pt
+\axisheadlen5pt
+\sideheadlen0pt
+\hashlen4pt
+\dashlen4pt
+\dashspace4pt
+\dotsize0.5pt
+\griddotsize0.5pt
+\dotspace3pt
+\symbolspace5pt
% \end{macrocode}
%
% Font dimensions are global, and so not restricted to an \env{mfpic}
@@ -1523,44 +1879,45 @@
% user to restore a value every time one picture needed a different
% value from the rest. To avoid requiring this, the following macro is
% used to save all values of these dimensions at the start of a picture,
-% and to restore the saved values at the end. (Some internal dimensions
-% (\cs{@graph...}, for example) don't need saving since they are reset
-% with each picture.)
+% and to restore the saved values at the end. Some internal dimensions
+% (all the \cs{@graph...}, \cs{@tlabels...} and \cs{tb@...}) don't need
+% saving since they are reset with each picture.
% \begin{macrocode}
\newdef\save@mfpicdimens{%
\edef\restore@mfpicdimens{%
- \mfpicunit =\the\mfpicunit
- \pointsize =\the\pointsize
- \shadespace =\the\shadespace
-\polkadotspace =\the\polkadotspace
- \hatchspace =\the\hatchspace
- \headlen =\the\headlen
- \axisheadlen =\the\axisheadlen
- \sideheadlen =\the\sideheadlen
- \hashlen =\the\hashlen
- \dashlen =\the\dashlen
- \dashspace =\the\dashspace
- \dotsize =\the\dotsize
- \dotspace =\the\dotspace
- \symbolspace =\the\symbolspace
- \tlabel@hadj =\the\tlabel@hadj
- \tlabel@vadj =\the\tlabel@vadj
- \tlabel@sep =\the\tlabel@sep}}%
-% \end{macrocode}
-%
-% \DescribeMacro{\mfpframesep}
-% \DescribeMacro{\mfpframethickness}
-% \DescribeMacro{\mfpicheight}
-% \DescribeMacro{\mfpicwidth}
-% \DescribeMacro{\mfpiccaptionskip}
-% Here are the few dimension registers we do allocate. The first two are
-% scratch registers, the next two govern frames, and the last two are set
-% after each picture to that pictures size. A \cs{vskip} of
-% \cs{mfpiccaptionskip} is placed between figure and caption by
-% \cs{tcaption}.
+ \mfpicunit \the\mfpicunit
+ \pointsize \the\pointsize
+ \shadespace \the\shadespace
+ \polkadotspace \the\polkadotspace
+ \hatchspace \the\hatchspace
+ \headlen \the\headlen
+ \axisheadlen \the\axisheadlen
+ \sideheadlen \the\sideheadlen
+ \hashlen \the\hashlen
+ \dashlen \the\dashlen
+ \dashspace \the\dashspace
+ \dotsize \the\dotsize
+ \griddotsize \the\griddotsize
+ \dotspace \the\dotspace
+ \symbolspace \the\symbolspace
+ \tlabel@hadj \the\tlabel@hadj
+ \tlabel@vadj \the\tlabel@vadj
+ \tlabel@sep \the\tlabel@sep
+ \tlpath@sep \the\tlpath@sep}}%
+% \end{macrocode}
+%
+% Here are the few dimension registers we do allocate:
+% \SpecialUsageIndex{\mfpframesep}\cs{mfpframesep}, and
+% \SpecialUsageIndex{\mfpframethickness}\cs{mfpframethickness}
+% govern frames.
+% \SpecialUsageIndex{\mfpicheight}\cs{mfpicheight} and
+% \SpecialUsageIndex{\mfpicwidth}\cs{mfpicwidth} are set
+% after each picture to that picture's size. A \cs{vskip} of
+% \SpecialUsageIndex{\mfpiccaptionskip}\cs{mfpiccaptionskip} is placed
+% between figure and caption by \cs{tcaption}. We also allocate a
+% frequently used scratch register
% \begin{macrocode}
\newdimen\mfp@scratch
-\newdimen\mfp@scratchB
\newdimen\mfpframesep
\newdimen\mfpframethickness
\newdimen\mfpicheight
@@ -1568,7 +1925,7 @@
\newskip\mfpiccaptionskip
\mfpframethickness0.4pt
\mfpframesep2pt
-\mfpiccaptionskip=\medskipamount
+\mfpiccaptionskip\medskipamount
% \end{macrocode}
%
%
@@ -1576,15 +1933,15 @@
%
% \DescribeMacro{\mfpicnumber}
% We make a user-level macro for setting the number of the next graph.
-% The purpose of this is so that the output file can be used as a font,
-% setting the figure numbers to the ascii codes of printable characters.
-% Then creating a composite figure is as easy as changing fonts:
-% \marg{\cs{pic} ABABAAB}. The motivation came from a discussion on
-% \texttt{comp.text.tex} started by someone who wanted to input a sequence
-% of 0's and 1's and get a corresponding sequence of black and white
-% squares.
+% The original purpose of this is so that the font file could have
+% characters in the positions of printable characters. Then creating a
+% composite figure is as easy as changing fonts: \marg{\cs{pic} ABABAAB}.
+% The motivation came from a discussion on \texttt{comp.text.tex} started
+% by someone who wanted to input a sequence of 0's and 1's and get a
+% sequence of black and white squares. For \MP{} users it is not very
+% important.
% \begin{macrocode}
-\def\mfpicnumber#1{\global\mfp@count=#1\relax}%
+\def\mfpicnumber#1{\global\mfp@count#1\relax}%
% \end{macrocode}
%
% \DescribeMacro{\mfpaddto}
@@ -1594,235 +1951,302 @@
% used only in the \cs{axislabels} command, so it needn't be globally
% defined, but it might be handy in the future.
% \begin{itemize}
-% \item[] \texttt{\#1} is a \emph{macro} that expands to some number.
-% \item[] \texttt{\#2} is a number to add to it.
+% \item[] \arg{1} is a \emph{macro} that expands to some number.
+% \item[] \arg{2} is a number to add to it.
% \end{itemize}
-% The result is stored again in \texttt{\#1}, overwriting the old value.
+% The result is stored again in \arg{1}, overwriting the old value.
%
% \DescribeMacro{\mfpmultiply}
-% For my personal use (or for power users), this is the multiplicative
-% version, with the same syntax and behavior.
-% \begin{macrocode}
-\newdef\mfpaddto#1#2{\mfp@scratch=#1pt\advance\mfp@scratch#2pt
- \edef#1{\mfp@strippt\mfp@scratch}}%
-\newdef\mfpmultiply#1#2{\mfp@scratch=#1pt \mfp@scratch#2\mfp@scratch
- \edef#1{\mfp@strippt\mfp@scratch}}%
-\begingroup
- \@makeother P\@makeother T\lowercase{\gdef\mfp@factor#1PT{#1}}%
-\endgroup
-\newdef\mfp@strippt{\@xp\mfp@factor\the}%
+% This is the multiplicative version, with the same syntax and behavior.
+% It is not used anywhere yet.
+% \begin{macrocode}
+\newdef\mfpaddto#1#2{\mfp@scratch#1pt \advance\mfp@scratch#2pt
+ \edef#1{\strip@pt\mfp@scratch}}%
+\newdef\mfpmultiply#1#2{\mfp@scratch#1pt \mfp@scratch#2\mfp@scratch
+ \edef#1{\strip@pt\mfp@scratch}}%
+\mfp@ifundefined\strip@pt
+ \begingroup
+ \lccode`\/`\p
+ \lccode`\?`\t
+ \lowercase{%
+ \endgroup
+ \def\rem@pt#1.#2/?}{#1\ifnum#2>\z@.#2\fi}%
+ \def\strip@pt{\@xp\rem@pt\the}%
+\mfp@enddef
% \end{macrocode}
%
-% A few user-level utilities.
-%
% \DescribeMacro{\pointdef}
% A point defining macro, suggested by a user.
% \begin{itemize}
-% \item[] \texttt{\#1} is a symbolic name for a point, only letters.
-% \item[] \texttt{\#2} and \texttt{\#3} are the coordinates.
+% \item[] \arg{1} is a symbolic name for a point, only letters.
+% \item[] \arg{2} and \arg{3} are the coordinates.
% \item[] Usage: \cs{pointdef}\marg{A}\parg{2,5} makes \cs{A} expand
% to \texttt{(2,5)}, \cs{Ax} to \texttt{2}, and \cs{Ay} to
% \texttt{5}.
% \end{itemize}
% \begin{macrocode}
\newdef\pointdef#1(#2,#3){%
+ \@ifundefined{#1} {}{\Mfpic@warn{\mfp@b#1 already defined.}}%
+ \@ifundefined{#1x}{}{\Mfpic@warn{\mfp@b#1x already defined.}}%
+ \@ifundefined{#1y}{}{\Mfpic@warn{\mfp@b#1y already defined.}}%
\@namedef{#1}{(#2,#3)}\@namedef{#1x}{#2}\@namedef{#1y}{#3}}%
% \end{macrocode}
%
-% \DescribeMacro{\pairarray}
-% \DescribeMacro{\numericarray}
-% The first enables the simultaneous definition of \MF{} pair variables
-% \mfc{A1}, \mfc{A2}, etc, \mfc{A[n]} by writing,
-% \cs{pairarray}\marg{A}\marg{\meta{list}}, the list being a list of
-% $n$ ordred pairs. Numeric variables can be created similarly with the
-% second command.
+% \DescribeMacro{\setmfarray}
+% This enables the simultaneous definition of \MF{} (or \MP) variables
+% such as \mfc{A1}, \mfc{A2}, etc., by writing
+% \cs{setmfarray}\marg{\meta{type}}\marg{\meta{name}}\marg{\meta{list}},
+% the \meta{name} being any \MF{} variable without a suffix, the list being
+% a comma separated list of items of the appropriate \meta{type}.
+% For example, \cs{setmfarray}\marg{pair} would require a list of
+% pairs.
+%
+% \SpecialUsageIndex{\numericarray}
+% As with \cs{setmfvariable}, we provide a few abbreviations.
+% \cs{numericarray} is for arrays of numeric variables,
+% \SpecialUsageIndex{\pairarray} \cs{pairarray} for arrays of pair
+% variables and \SpecialUsageIndex{\colorarray}\cs{colorarray} is for
+% arrays of color variables. Finally, to set arrays globally, there is
+% \SpecialUsageIndex{\globalsetmfarray}\cs{globalsetmfarray}.
% \begin{macrocode}
-\newdef\pairarray#1{%
- \mfcmd{save #1}\mfsrc{textpairs (#1)}\mfp@writedata}%
-\newdef\numericarray#1{\mfcmd{save #1; numeric #1; numeric #1[]}%
- \mfsrc{list (#1)}\mfp@writedata}%
+\newdef\setmfarray#1#2{\mfsrc{setarray (#1) (#2)}\mfp@writedata}%
+\newlet\setmparray\setmfarray
+\newdef\pairarray{\setmfarray{pair}}%
+\newdef\numericarray{\setmfarray{numeric}}%
+\newdef\colorarray{\setmfarray{color}}%
+\newdef\globalsetmfarray#1#2{\mfsrc{gsetarray (#1) (#2)}\mfp@writedata}%
% \end{macrocode}
%
-% \DescribeMacro{\lightershade}
-% \DescribeMacro{\darkershade}
-% Shade adjustment macros. They just multiply or divide \cs{shadespace}
-% by 1.2.
+% Shade adjustment macros.
+% \DescribeMacro{\lightershade}\cs{lightershade}
+% just multiplies \cs{shadespace} by $1.2$. While
+% \DescribeMacro{\darkershade}\cs{darkershade} divides by $1.2$
+% (actually, multiplies by $1/1.2$)
% \begin{macrocode}
-\newdef\lightershade{\shadespace=1.2\shadespace}%
-\newdef\darkershade{\shadespace=.833333\shadespace}%
+\newdef\lightershade{\shadespace1.2\shadespace}%
+\newdef\darkershade{\shadespace.83333\shadespace}%
% \end{macrocode}
%
% \DescribeMacro{\dashlineset}
-% \DescribeMacro{\dotlineset}
% The following arrange for contrastive dashed lines. \cs{dashlineset}
% just resets the default parameters that the \cs{dashed} macro uses,
+% \DescribeMacro{\dotlineset}
% \cs{dotlineset} sets these parameters so that \cs{dashed} produces
% something almost dotted.
% \begin{macrocode}
-\newdef\dashlineset{\dashlen=4pt \dashspace=4pt}%
-\newdef\dotlineset{\dashlen=1pt \dashspace=2pt}%
+\newdef\dashlineset{\dashlen4pt \dashspace4pt }%
+\newdef\dotlineset{\dashlen1pt \dashspace2pt }%
% \end{macrocode}
%
-% \DescribeMacro{\ifpointfill}
+% \DescribeMacro{\pointfilltrue}
% With the commands \cs{pointfilltrue} or \cs{pointfillfalse} the user
% can specify points drawn with the \cs{point} command to be either
-% filled in or not.
+% filled in or \DescribeMacro{\pointfillfalse}not.
% \begin{macrocode}
\let\ifpointfill\iftrue
\newdef\pointfilltrue{\mfp@let\ifpointfill\iftrue}%
\newdef\pointfillfalse{\mfp@let\ifpointfill\iffalse}%
% \end{macrocode}
%
+% \CMF{} has very flexible looping capabilities. Before version 0.8 there
+% was no \mfpic{} interface because it seemed that \cs{mfsrc} was the
+% simplest way to access this capability. Anything else would have to
+% be either a clone of \cs{mfsrc} or limit the possibilities. However, I
+% now believe it might be a good thing to have a structured way to perform
+% repetitive actions at the \MF{} level.
+%
+% As of version 0.9, \mfpic{} will allow loops outside \env{mfpic}
+% environments. It seems like a reasonable thing to be allowed to do.
+% Fortunately, the macro names are already unique.
+%
+% \DescribeMacro{\mfpfor}
+% This is the simplest: it writes the word \mfc{for} plus the contents of
+% its only mandatory argument, followed by a colon. The loop is ended by
+% \DescribeMacro{\endmfpfor}\cs{endmfpfor} which merely writes the text
+% \mfc{endfor}.
+% \begin{macrocode}
+\newdef\mfpfor#1{\begingroup\mfsrc{for #1:}}%
+\newdef\endmfpfor{\mfsrc{endfor}\endgroup}%
+% \end{macrocode}
+%
+% \DescribeMacro{\mfploop}
+% This starts a loop with \mfc{forever}. The user is expected to provide
+% an escape condition with \DescribeMacro{\mfpuntil}\cs{mfpuntil}, which
+% can be placed anywhere among the commands contained in the
+% \env{mfploop} environment. \DescribeMacro{\endmfploop}\cs{endmfploop}
+% is a clone of \cs{mfpfor} except for the warning. Grouping keeps
+% the warning from being turned off if \cs{mfpuntil} occurs in a nested
+% loop.
+% \begin{macrocode}
+\newdef\mfploop{%
+ \begingroup\mfsrc{forever:}\let\@nountil\mfp@untilwarn}%
+\newdef\mfpuntil#1{\mfcmd{exitif #1}\let\@nountil\@empty}%
+\newdef\endmfploop{\@nountil\endmfpfor}%
+% \end{macrocode}
+%
+% \DescribeMacro{\mfpwhile}
+% This emulates a while-loop, the condition being given in the mandatory
+% argument. It has the same behavior, and writes almost the same code as
+% \cs{mfploop} with an immediate \cs{mfpuntil} using the negative of the
+% condition. The closing, \DescribeMacro{\endmfpwhile}\cs{endmfpwhile},
+% is again the same as \cs{endmfpfor}.
+% \begin{macrocode}
+\newdef\mfpwhile#1{\begingroup\mfcmd{forever: exitif not(#1)}}%
+\newlet\endmfpwhile\endmfpfor
+% \end{macrocode}
+%
+% \DescribeMacro{\plr}
+% A little utility to convert a list of pairs into another list of
+% pairs, where the given list are assumed to be polar coordinates and the
+% results are their Cartesian counterparts. This is guaranteed to work as
+% expected only in macros with a list argument, or for a single pair.
+% \DescribeMacro{\midpoint}
+% And one to calculate the midpoint, given two points separated by a comma.
+% Can be used in graphics macros where a point is required.
+% \begin{macrocode}
+\newdef\plr#1{map (polar) (#1)}%
+\newdef\midpoint#1{(0.5[#1])}%
+% \end{macrocode}
+%
%
% \section{Macros to implement prefix commands}\label{prefix}
%
-% \Mfpic{} prefix macros change the behavior of the figure macros.
-% Normally we want \cs{circle}\marg{...} to draw the solid outline of a
-% circle. So normally \cs{circle}\marg{...} will write a drawing command
-% followed by a path expression giving the circle to draw. But
-% \cs{dashed}\cs{circle}\marg{...} should write a \emph{dashing} command
-% and \emph{omit} the drawing command. Thus, \emph{rendering} macros
-% like \cs{dashed} need to set a switch, and figure macros like
-% \cs{circle} need to test that switch.
-%
-% Similar considerations hold for prefix macros that transform the
-% figure. The logic is that, at the \MF{} level, every figure macro
-% is syntactically a path expression. Every prefix accepts a path
-% expression as input and has a path as its return value, so a
-% combination prefix + path expression is again a path expression.
+% Some \mfpic{} macros, like \cs{circle} create a path. When used without
+% a prefix, they also draw the path. At the \MF{} level, every figure
+% macro is syntactically a path expression. Other macros are prefix macros
+% and operate on a figure macro that follows. Every prefix accepts a path
+% expression as input and has a path as its return value, so a combination
+% prefix + path expression is again a path expression.
+%
% Prefixes may also have side effects, such as adding some ink to the
% current picture. Such prefixes are called \emph{rendering} macros.
+%
% Rendering macros normally return the same path as their argument, but
-% there are macros that modify the path. Some applying a linear
-% transform, some close an open path, some return a subpath. These we call
+% there are macros that modify the path. Some apply a linear transform,
+% some close an open path, some return a subpath. These we call
% \emph{modification} macros.
%
% This scheme is quite simple, but unfortunately too simple. To avoid
-% having to explicitly type a rendering prefix every time, we need
-% the above mentioned ability to pass information to the figure macros.
+% having to explicitly type a rendering prefix every time, a figure
+% macro needs to know if a rendering prefix has occured, so prefix macros
+% need to pass information to the figure macro (and to later prefixes).
+%
% Moreover, \MF{} abhors an `isolated expression', so the first prefix
% has to write something with a different syntax, and pass the info that
% it has already done so on to later prefixes, who need to test that
-% info.
+% info. Thus, every prefix macro needs to know if it is the first or a
+% later one. They test this \cs{if} command and also set it to false. The
+% figure macros, which are last in the chain, close by resetting it true.
% \begin{macrocode}
\mfp@DBlog{Tests to control multiple prefix commands.}%
+\newif\if@mfpstart\@mfpstarttrue
% \end{macrocode}
%
-% Every prefix macro needs to know if it is the first or a later one.
-% They test this \cs{if} command and also set it to false. The
-% figure macros, which are last in the chain, close by resetting it true.
+% If a prefix is a rendering prefix, it writes its rendering code and sets
+% the following switch to false, letting the figure macro know it needn't
+% invoke the default rendering. Figure macros close by resetting it true.
+% Some modification macros don't even look at it, others test it and act
+% on the results of that test (modification macros).
% \begin{macrocode}
-\newif\if@startfig\@startfigtrue
+\newif\if@mfprend\@mfprendtrue
% \end{macrocode}
%
-% If a prefix is a rendering prefix, it writes the rendering code
-% and sets the following switch to false, letting the figure macro know
-% it needn't invoke the default rendering,
-% Figure macros close by resetting it true. Some modification macros don't
-% touch it at all (the \cs{@modmac}\,s) others just test it
-% (\cs{@closmac}\,s).
+% The following macro is always (and only) invoked by the first prefix
+% macro (or the figure macro, if there are no prefixes). It writes the
+% code that prevents an isolated expression. It stores the following path
+% expression in the variable \gbc{curpath}. Thus every combination of
+% prefixes followed by a figure macro is syntactically a \MF{} variable
+% assignment command.
% \begin{macrocode}
-\newif\if@imrend\@imrendtrue
+\newdef\mfp@storepath{\mfsrc{\@nl store (curpath)}}%
% \end{macrocode}
%
-% The following macro is always (and only) invoked by the first prefix
-% macro. It writes the code that prevents an isolated expression. It
-% stores the following path expression in the variable \gbc{curpath}. Thus
-% every combination of prefixes followed by a figure macro is
-% syntactically a \MF{} variable assignment command.
+% Every prefix and figure macro has to invoke \cs{mfp@first}, since each
+% is potentially the first in the sequence. If \cs{@mfpstart} is true, it
+% writes the storing command, sets \cs{@mfpstartfalse}, and says that
+% rendering is needed. (The rendering macros immediately cancel that
+% switch.)
% \begin{macrocode}
-\newdef\store@path{\mfsrc{\@nl store (curpath)}}%
+\newdef\mfp@first{\relax
+ \if@mfpstart \mfp@storepath \@mfprendtrue \@mfpstartfalse\fi}%
% \end{macrocode}
%
-% Every prefix and figure macro has to invoke \cs{@firststage}, since
-% each is potentially the first in the sequence. If
-% \cs{if@startfig} is true, it writes the storing command, sets
-% \cs{@startfigfalse}, and says that rendering is needed. (The
-% rendering macros immediately cancel that switch.)
+% Every rendering macro invokes this next command. After the first-stage
+% code it writes its rendering command (via \cs{mfsrc}) and passes on the
+% info that rendering is no longer required of the figure macro.
% \begin{macrocode}
-\newdef\@firststage{\relax
- \if@startfig \store@path \@imrendtrue \@startfigfalse\fi}%
+\newdef\mfp@rendmac{\mfp@first \@mfprendfalse \mfsrc}%
% \end{macrocode}
%
-% \DescribeMacro{\@rendmac}
-% Every rendering macro invokes this command. After the first-stage code
-% it writes its rendering command (via \cs{mfsrc}) and passes on the info
-% that rendering is no longer required of the figure macro.
+% The \emph{figure} macros proceed as follows: (i)~they invoke the
+% \cs{mfp@first} code, then (ii)~write a rendering macro if necessary
+% (\cs{mfp@autorend}), (iii)~then make \cs{@mfpstarttrue} (for the next
+% figure) and write \MF{} code that defines the path. The \cs{mfp@modmac}
+% macros do only the first two, then write their modification code,
+% counting on the following figure macro to do (iii). The idea is that
+% [modifier + figure] should act the same as if the path it defines were
+% encoded as a single figure command.
% \begin{macrocode}
-\newdef\@rendmac{\@firststage \@imrendfalse \mfsrc}%
+\newdef\mfp@modmac{\mfp@first\mfp@autorend\mfsrc}%
+\newdef\mfp@autorend{\if@mfprend\@xp\mfp@render\fi}%
% \end{macrocode}
%
-% \DescribeMacro{\@modmac}
-% It is possible we should call this something else, as only \cs{arrow}
-% and \cs{reverse} use \cs{@modmac}. Other modifications cause the
-% modified path to be drawn as if (modifier+figure) were a solitary figure
-% macro. This would be pointless for a reversed path or a path with
-% arrowhead added, as presumably the original path would already be drawn.
-% Moreover, it can be argued (for \MP{} at least) that the arrowhead
-% should be drawn on top of the shaft. This is the only significant effect of
-% coding \cs{arrow} and \cs{reverse} as \cs{@modmac}\,s.
+% Only axis and arrow commands (plus \cs{reverse}) use \cs{mfp@addmac}.
+% As the name suggests, something is added onto an existing figure. That
+% is, it is designed to ignore the rendering state and not invoke implicit
+% rendering. If no explicit rendering occurs, the figure macro that
+% follows invokes a rendering. For arrows, this causes the arrowhead to be
+% drawn on top of the drawing of the shaft.
%
-% It simply invokes the common first-stage code and writes its \MF{} code.
+% It would seem \cs{reverse} doesn't fit this name, but it also needs to
+% be transparent to the rendering state so that it will work in
+% combination with arrows.
+%
+% \cs{mfp@addmac} simply invokes the common first-stage code and writes
+% its \MF{} code.
% \begin{macrocode}
-\newdef\@modmac{\@firststage \mfsrc}%
+\newdef\mfp@addmac{\mfp@first \mfsrc}%
% \end{macrocode}
%
-% \DescribeMacro{\@closmac}
-% Other modification macros were added later, and it turned out that
-% they needed the same treatment as the closure macros that already
-% existed. Therefore, most modification macros invoke \cs{@closmac}. The
-% \emph{figure} macros proceed as follows: they invoke the \cs{@firststage}
-% code, then write a rendering macro if necessary, then make
-% \cs{@starfigtrue} (for the next figure), then write \MF{} code that
-% defines the path. These modification macros do only the first two,
-% counting on the figure macro to follow to invoke the last two. The idea
-% is that {closure + figure} should act the same as if the path it defines
-% were encoded as a single figure command.
-%
-% The \cs{@modmac} and \cs{@closmac} macros differ only when there are
-% no preceeding rendering macros. In that case it is as if \cs{draw} (or
-% more generally \cs{@render}) precedes the \cs{@closmac} macro. That
+% The \cs{mfp@addmac} and \cs{mfp@modmac} macros differ when there are no
+% preceeding rendering macros. In that case it is as if \cs{draw} (or
+% more generally \cs{mfp@render}) precedes the \cs{mfp@modmac} macro. That
% drawing happens \emph{in addition to} any rendering macros that follow.
-% This implicitly added \cs{draw} is not present for \cs{@modmac} macros,
-% unless it is added by the following figure macro itself.
+% This implicitly added \cs{draw} is not present for \cs{mfp@addmac}
+% macros; it is added by the following figure macro. The effect of this is
+% the \cs{mfp@addmac} macros perform their additions on top of an already
+% rendered figure.
+%
+% \DescribeMacro{\setrender}
+% As mentioned above, when a figure macro is invoked without a rendering
+% prefix, it will invoke some default. That default is stored in a macro
+% and can be changed for special effects. Initially we define it as
+% \cs{draw} (with empty optional argument) to draw solid paths in the
+% default color, but we provide a command for the user to change it.
% \begin{macrocode}
-\newdef\@closmac{\@firststage \if@imrend\@xp\@render\fi \mfsrc}%
+\newdef\mfp@render{\draw[]}%
+\newdef\setrender#1{\mfp@def\mfp@render{#1}}%
% \end{macrocode}
%
-% \DescribeMacro{\setrender}
-% As mentioned above, when a figure macro is invoked without a
-% rendering prefix, it will invoke some default. That default is stored
-% in the macro \cs{@render}, which can be changed for special effects.
-% Initially we define it as \cs{draw} to draw solid paths, but provide a
-% command for the user to change it.
-% \begin{macrocode}
-\newdef\@render{\draw[]}%
-\newdef\setrender#1{\mfp@def\@render{#1}}%
-% \end{macrocode}
-%
-% \DescribeMacro{\@figmac}
-% A figure macro will invoke the common first-stage code, then check
-% if rendering is needed. If so, it issues the \cs{@render} command.
-% It also resets the startfig switche. Since a figure macro completes
-% an assignment command in \MF, we use \cs{mfcmd}, which places a
-% semicolon at the end of the code it writes. We define the first part
-% (all but the writing) as a separate command because there are a few cases
-% where we need to delay that writing.
-% \begin{macrocode}
-\newdef\@figmacstart{%
- \@firststage \if@imrend\@xp\@render\fi \@startfigtrue}%
-\newdef\@figmac{\@figmacstart\mfcmd}%
-% \end{macrocode}
-%
-% \DescribeMacro{\@listmac}
-% The list macros take as their last argument a list of $(x,y)$ pairs.
-% That list can either be typed directly into
-% the \TeX{} source file (inside braces, separated by commas) or be
-% present in an external datafile. Either way, the data is written
-% as a list of \MF{} pairs within an enclosing pair of
-% parentheses. Since these are all figure macros, \cs{@listmac} starts out
-% just like \cs{@figmac}, but it writes only part of its arguments, the
-% last one being handled by \cs{mfp@writedata}.
+% A figure macro will invoke the common first-stage code and the rendering
+% check. It also resets the \cs{@mfpstart} switch. Because a figure macro
+% completes an assignment command in \MF, it writes its code with
+% \cs{mfcmd}, which places a semicolon at the end. We define the first
+% part (all but the writing) as a separate command because there are a few
+% cases where we need to delay that writing.
+% \begin{macrocode}
+\newdef\mfp@figmacbase{\mfp@first \mfp@autorend \@mfpstarttrue}%
+\newdef\mfp@figmac{\mfp@figmacbase\mfcmd}%
+% \end{macrocode}
+%
+% The list macros take as their last argument a list of $(x,y)$ pairs (or
+% occasionally a list of numbers). That list can either be typed directly
+% into the \TeX{} source file (inside braces, separated by commas) or be
+% present in an external datafile. Either way, the data is written as a
+% list within an enclosing pair of parentheses. Since these are all
+% figure macros, \cs{mfp@listmac} starts out just like \cs{mfp@figmac},
+% but it writes only part of its arguments, the last one (the data list)
+% being handled by \cs{mfp@writedata}.
%
% \cs{mfp@writedata} looks ahead to see if the token \cs{datafile}
% follows, if it does \cs{mfp@writefile} is invoked, which gobbles
@@ -1833,214 +2257,185 @@
% \cs{mfp@writedata} is also used by non-figure macros such as the axis
% marks macros, plus \cs{piechart} and \cs{barchart}.
% \begin{macrocode}
-\newdef\@listmac#1{\@figmacstart\mfsrc{#1}\mfp@writedata}%
-\newdef\mfp@writedata{%
- \mfp@ifnextchar\datafile{\mfp@writefile}{\mflist}}%
-\newdef\mfp@writefile\datafile#1{\@processdatafile{#1}{\mfp@rwdata}}%
+\newdef\mfp@listmac#1{\mfp@figmacbase\mfsrc{#1}\mfp@writedata}%
+\newdef\mfp@writedata{\mfp@iftoken\datafile{\mfp@writefile}{\mflist}}%
+\newdef\mfp@writefile{%
+ \mfp@ifopengraphsfile
+ {\mfp@processfile{\mfp@rwdata}}{\mfp@nooutput@error\datafile}}%
% \end{macrocode}
%
% \DescribeMacro{\mftitle}
-% \DescribeMacro{\tmtitle}
-% These go here because they didn't seem to go anywhere else. The are
-% mainly for debugging the example files. \cs{getmfp@toks} assigns the
-% following group to a token register and then invokes its argument.
-% \cs{mftitle} and \cs{tmtitle} use this to write the code into
-% the string argument of the \grafbase{} command \gbc{mftitle}.
-% \cs{mftitle} does only that, while \cs{tmtitle} also writes the argument
-% to the \file{.log} file and typesets it in the document.
+% This is mainly for debugging the example files. \cs{mfp@title} takes
+% two arguments: the command that called it (for the error message of
+% \cs{mfp@src}), and some \TeX{} code. That code is written into the string
+% argument of the \grafbase{} command \gbc{mftitle} and ends up in the
+% \MF{} \file{.log} file and as a comment in the GF file. \cs{mftitle}
+% merely calls \cs{mfp@title}, while \DescribeMacro{\tmtitle}\cs{tmtitle}
+% also writes the argument to the \TeX{} \file{.log} file and typesets it
+% in the document.
%
% We write the argument as a token list because that is the the easiest
% verbatim-like way to do it.
% \begin{macrocode}
-\newif\if@mfpswitch
-\newdef\mftitle{\@bsphack\@mfpswitchtrue\mfp@gettoks\mf@title}%
-\newdef\tmtitle{\mfp@gettoks\tm@title}%
-\newdef\mfp@gettoks#1{\afterassignment#1\mfp@toks=}%
-\newdef\tm@title{\@mfpswitchfalse\mf@title
- \wlog{\the\mfp@toks}{\the\mfp@toks}}%
-\newdef\mf@title{\mfsrc{}%
- \immediate\write\mfp@out{mftitle "\the\mfp@toks";}%
- \if@mfpswitch\@esphack\fi}%
-% \end{macrocode}
-%
-% \DescribeMacro{\noship}
-% \DescribeMacro{\stopshipping}
-% \DescribeMacro{\resumeshipping}
-% To turn off character shipping for duration of innermost enclosing
-% group (eg, \env{mfpic} environment). As far as I know it has never been
-% tested with \MP. (It hasn't been tested recently even in \MF.) The \MF{}
-% code prevents the figure from being added to the font, the \TeX{} switch
-% \texttt{shipping} is checked before \cs{endmfpic} attempts to add the
-% (nonexistent) figure to the document.
-% \begin{macrocode}
-\newdef\stopshipping{\mfp@let\if@shipping\iffalse
- \@ifmfpfileisopen{\mfcmd{save shipit;def shipit = relax enddef}}{}}%
-\newdef\resumeshipping{\mfp@let\if@shipping\iftrue
- \@ifmfpfileisopen{\mfcmd{save shipit;def shipit = shipit_ enddef}}{}}%
-\newlet\noship\stopshipping
-\resumeshipping
+\newdef\mftitle#1{\@bsphack\mfp@title\mftitle{#1}\@esphack}%
+\newdef\tmtitle#1{\mfp@title\tmtitle{#1}\wlog{\the\mfp@toks}{#1}}%
+\newdef\mfp@title#1#2{\mfp@toks{#2}%
+ \mfp@write\mfp@src#1{mftitle "\the\mfp@toks";}}%
% \end{macrocode}
%
%
% \section{Macros for getting data from files}\label{rwdata}
%
% The following command contains the common code to open a data file for
-% reading. The first parameter is the filename, the second is what to do
+% reading. The second parameter is the filename, the first is what to do
% if it exists and is nonempty. We first try to \cs{openin} the file and
% issue an error message if that fails. Then we skip any initial blank
% lines and comments and complain if that takes us to the end of the
% file. Otherwise we execute the command specified. Afterwards, since
% each \cs{plotdata} command in a given \env{mfpic} environment
% starts with the linetype at which the previous one ended, we allow the
-% value of \cs{mfp@linetype} to survive the group. If \texttt{\#2} is
-% not \cs{plotdata}, this is harmless (I hope).
+% value of \cs{mfp@linetype} to survive this group but not any enclosing
+% group.
% \begin{macrocode}
-\newdef\@processdatafile#1#2{%
- \openin\mfp@data=#1\relax
- \ifeof\mfp@data \nodatafileerror{#1}%
+\newdef\mfp@processfile#1#2{%
+ \openin\mfp@data=#2\relax
+ \ifeof\mfp@data \mfp@nofile@error{#2}%
\else
- \begingroup
+ \mfp@DBlog{Opening file #2 for data.}%
+ {%
\mfp@resetwhitespace \mfp@setcomment
- \skipBlanksandComments
- \ifeof\mfp@data \emptydatafileerror{#1}\else #2\fi
- \global\mfp@n=\mfp@linetype
- \endgroup
- \mfp@linetype=\mfp@n
+ \mfp@skipblanklines
+ \ifeof\mfp@data
+ \mfp@nodata@error{#2}%
+ \else
+ \message{< Mfpic: Reading #2}%
+ #1%
+ \message{>}%
+ \fi
+ \@xp
+ }\@xp\mfp@linetype\number\mfp@linetype\relax
+ \mfp@DBlog{Closing file #2.}%
\fi
\closein\mfp@data}%
% \end{macrocode}
%
-% \DescribeMacro{\nodatafileerror}
-% \DescribeMacro{\emptydatafileerror}
-% These are error messages for missing or empty data files:
-% \begin{macrocode}
-\newdef\nodatafileerror#1{%
- \mfp@errmsg{No data file: #1 .}%
- {The data file you tried to plot can't be found.}}%
-\newdef\emptydatafileerror#1{%
- \mfp@errmsg{Empty data file: #1 .}%
- {The file you tried to plot contains only empty lines and comments.}}%
-% \end{macrocode}
-%
% When reading files it is important that white space in the files
% always have the same meaning for \TeX{}. This utility resets spaces,
% tabs and EOLs to the standard categories.
% \begin{macrocode}
-\newdef\mfp@resetwhitespace{%
- \catcode`\^^M=5 \catcode`\ =10 \catcode`\^^I=10 }%
+\newdef\mfp@resetwhitespace{\mfp@restorepar
+ \endlinechar`\^^M \catcode`\^^M5 \catcode`\ 10 \catcode`\^^I10 }%
% \end{macrocode}
%
-% \DescribeMacro{\skipcomments}
-% This does what it says, skipping comments in a data file. It reads a
+% \cs{mfp@skipcomments}
+% does what it says, skipping comments in a data file. It reads a
% line from a previously opened file (input stream \cs{mfp@data}) and
% compares it to \cs{@empty}. It calls itself when they are equal (a
-% comment line). When this macro finishes, \cs{mfp@temp} contains either
-% \cs{par} (blank line or EOF) or real data.
+% comment line, a blank line would put a \cs{par} token in \cs{mfp@data}).
+% When this macro finishes, \cs{mfp@temp} contains either \cs{par} (blank
+% line or EOF) or real data.
% \begin{macrocode}
-\mfp@ifdefined\@empty{}{\def\@empty{}}%
-\newdef\skipcomments{\read\mfp@data to \mfp@temp
- \ifx\mfp@temp\@empty \@xp\skipcomments\fi}%
+\newdef\mfp@skipcomments{\read\mfp@data to \mfp@temp
+ \ifx\mfp@temp\@empty \@xp\mfp@skipcomments\fi}%
% \end{macrocode}
%
-% \DescribeMacro{\skipBlanksandComments}
-% This command first skips comments. After that \cs{mfp@temp} can contain
-% \cs{par}, indicating either EOF or a blank line, or something else.
-% In the first case (EOF) the macro terminates, in the second case (blank)
-% it calls itself again, in the third case it also terminates, having
-% presumably found real data.
-%
-% When this is called, a file should already be open (by
-% \cs{@processdatafile}) using the handle \cs{mfp@data}.
+% \cs{mfp@skipblanklines}
+% first skips comments. After that \cs{mfp@temp} can contain \cs{par},
+% indicating either EOF or a blank line, or something else. In the first
+% case (EOF) the macro terminates, in the second case (blank) it calls
+% itself again, in the other case (something else) it also terminates,
+% having presumably found real data.
%
-% The macro \cs{@XP} causes two macros after the next token to be
-% expanded, removing the two \cs{fi}\,s (and so avoiding deep nesting).
+% When this is called, a file should already be open using the handle
+% \cs{mfp@data}.
% \begin{macrocode}
-\newdef\skipBlanksandComments{\skipcomments
+\newdef\mfp@skipblanklines{\mfp@skipcomments
\ifeof\mfp@data
- \else\ifx\mfp@temp\mfp@par \@XP\skipBlanksandComments
+ \else\ifx\mfp@temp\mfp@par \@XP\mfp@skipblanklines
\fi\fi}%
% \end{macrocode}
%
% The following two commands, \cs{mfp@rwdata} and \cs{mfp@rwdataloop}
% perform the work of writing the file (read stream \cs{mfp@data} opened
-% by \cs{@processdatafile}).
+% by \cs{mfp@processfile}).
%
-% The counter \cs{mfp@n} is used to keep track of how many lines of the
-% data file have been written to one line of the output. The token
-% \cs{sequence} in a \cs{using} specification will expand to the value of
-% the counter \cs{mfp@sequence}.
+% The figure counter \cs{mfp@count} is reused (this should all be inside
+% a group) to keep track of how many lines of the data file have been
+% written to one line of the output. The token \cs{sequence} in a
+% \cs{using} specification will expand to the value of the counter
+% \cs{mfp@sequence}.
%
-% When this first macro is called, a non-blank line has been read to the
+% When \cs{mfp@rwdata} is called, a non-blank line has been read to the
% macro \cs{mfp@temp}, to which we now add a space. The macro
% \cs{mfp@buffer} is what will be written to the output file as soon as it
% is long enough. We initialize it by defining it to be an open
% parenthesis followed by the result of applying \cs{parse@data} to the
% contents of \cs{mfp@temp}. Then we call \cs{mfp@rwdataloop}, which does
-% the actual reading and writing.
+% the remaining reading and all the writing.
% \begin{macrocode}
- \def\mfp@rwdata{\mfp@n=1 \mfp@sequence=1
- \def\sequence{\number\mfp@sequence}%
- \edef\mfp@temp{\mfp@temp\space}%
- \edef\mfp@buffer{(\@xp\parse@data\mfp@temp\mfp@delim}%
- \mfp@rwdataloop}%
+\newdef\mfp@nextdatum{\@xp\parse@data\mfp@temp\mfp@end}%
+\newdef\mfp@rwdata{\mfp@count1 \mfp@sequence1
+ \def\sequence{\number\mfp@sequence}%
+ \edef\mfp@temp{\mfp@temp\space}%
+ \edef\mfp@buffer{(\mfp@nextdatum}\mfp@rwdataloop}%
% \end{macrocode}
%
% At this point, the buffer has been started with the first datum, so we
% read another line. If that is blank, we write the buffer (and the closing
-% parenthesis and a semicolon) and terminate the loop. Otherwise we go
-% through a loop:
+% parenthesis and a semicolon) and terminate the loop. Otherwise we have
+% read another datum and then we:
% \begin{enumerate}
-% \item Increment the sequence counter,
+% \item increment the sequence counter,
% \item add a comma to the buffer,
-% \item test whether the buffer is `full', and
-% \item add the datum to the buffer. If the buffer is full in step~3, it is
-% written to the output and cleared before going to step~4.
+% \item test whether the buffer is `full', and if so, write the buffer
+% to the output and clear it, then
+% \item add the datum to the buffer.
% \end{enumerate}
+% The buffer is `full' when \cs{mfp@count} (the number of data in the
+% buffer) exceeds \cs{mfp@dataperline}.
+%
% \cs{mfp@rwdataloop} loops by calling itself at the end. Whenever
% \cs{mfp@temp} is empty (comment line read), (1)--(4) are skipped and
% another line read.
% \begin{macrocode}
- \def\mfp@rwdataloop{%
- \read\mfp@data to \mfp@temp
- \ifx\mfp@temp\mfp@par
- \mfcmd{\mfp@buffer)}%
+\newdef\mfp@rwdataloop{%
+ \read\mfp@data to \mfp@temp
+ \ifx\mfp@temp\mfp@par
+ \mfcmd{\mfp@buffer)}%
+ \else
+ \ifx\mfp@temp\@empty
\else
- \ifx\mfp@temp\@empty % comment line, read another
- \else
- \edef\mfp@temp{\mfp@temp\space}%
- \advance\mfp@sequence1
- \edef\mfp@buffer{\mfp@buffer\mfp@join}%
- \ifnum\mfp@n<\mfpdataperline\relax
- \advance\mfp@n1
- \else
- \mfsrc{\mfp@buffer}%
- \mfp@n=1
- \def\mfp@buffer{}%
- \fi
- \edef\mfp@buffer{\mfp@buffer
- \@xp\parse@data\mfp@temp\mfp@delim}%
+ \edef\mfp@temp{\mfp@temp\space}%
+ \advance\mfp@sequence1
+ \edef\mfp@buffer{\mfp@buffer\mfp@join}%
+ \ifnum\mfp@count<\mfpdataperline\relax \advance\mfp@count1
+ \else \mfsrc{\mfp@buffer}\mfp@count1 \def\mfp@buffer{}%
\fi
- \@xp\mfp@rwdataloop
- \fi}%
+ \edef\mfp@buffer{\mfp@buffer\mfp@nextdatum}%
+ \fi
+ \@xp\mfp@rwdataloop
+ \fi}%
% \end{macrocode}
%
%
% \section{Various \CMF{} Settings.}\label{MFsettings}
%
-% \DescribeMacro{\drawpen}
-% \DescribeMacro{\pen}
% \DescribeMacro{\penwd}
% Macros that write changes in default \grafbase{} variables now use the
% construct ``\gbc{save x; <type> x; x := <value>;}'' (via a call to
% \cs{setmfvariable}). This makes changes local if inside a given \env{mfpic}
% environment, global if outside. An exception is \gbc{penwd} which, as an
% internal variable uses `\gbc{interim}' instead (via the \grafbase{}
-% command \gbc{resizedrawpen}). Both \cs{drawpen} and \cs{pen} have been
-% used in the past, while \cs{penwd} is more consistent with other
-% commands, so we keep all three.
+% command \gbc{resizedrawpen}).
+%
+% \SpecialUsageIndex{\drawpen}
+% \cs{drawpen} was the original command used to set the pen diameter
+% (prior to version 0.2.13). At some point it was replaced by
+% \SpecialUsageIndex{\pen}\cs{pen}. However \cs{penwd} seems to be more
+% consistent with other commands (e.g., \cs{shadewd}), and we keep all three.
% \begin{macrocode}
-\newdef\drawpen#1{\mfcmd{\@nl resizedrawpen (#1)}}%
-\newlet\pen=\drawpen \newlet\penwd=\drawpen
+\newdef\pen#1{\mfcmd{\@nl resizedrawpen (#1)}}%
+\newlet\penwd\pen \newlet\drawpen\pen
% \end{macrocode}
%
% \DescribeMacro{\shadewd}
@@ -2059,35 +2454,34 @@
% \end{macrocode}
%
% \DescribeMacro{\hatchwd}
-% The thickness of lines in the hatching fill.
+% The thickness of lines in the hatching fill is set by \cs{hatchwd}.
% \begin{macrocode}
\newdef\hatchwd#1{\setmfnumeric{hatchwd}{#1}%
\setmfvariable{pen}{hatchpen}{pencircle scaled hatchwd}}%
% \end{macrocode}
%
% \DescribeMacro{\dashpattern}
-% A dash pattern consists (internal to \MF) of three arrays. The
+% A dash pattern consists (internal to \MF) of an array of lengths. The
% \grafbase{} command \gbc{dashpat} accepts a name, and a sequence of
-% numbers representing the sizes of dashes and spaces, and builds these
-% arrays.
+% numbers representing the sizes of dashes and spaces, and builds this
+% array. It differs from \cs{numericarray} in that a little additional
+% checking is done (at the \grafbase{} level).
% \begin{macrocode}
-\newdef\dashpattern#1#2{\mfcmd{save #1; dashpat (#1) (#2)}}%
+\newdef\dashpattern#1{\mfsrc{save #1; dashpat (#1)}\mfp@writedata}%
% \end{macrocode}
%
% \DescribeMacro{\settension}
-% This sets the default tension for \cs{curve} commands, as well as for
-% any command that takes an optional argument of the form
-% \oarg{s\meta{tens}}. This includes \cs{function}, \cs{plrfcn},
-% \cs{fcncurve}, \cs{datafile}, \cs{plotdata} and others. It also sets the
-% default tension for \cs{sclosed} and \cs{bclosed}. The value should be
-% at least $3/4$.
+% This sets the default tension for any command (for example, \cs{curve})
+% that takes an optional argument that contains a tension value. This
+% includes also \cs{function}, \cs{plrfcn}, \cs{fcncurve}, \cs{datafile},
+% \cs{plotdata}. It also sets the default tension for \cs{sclosed} and
+% \cs{bclosed}. The value should be at least $3/4$.
% \begin{macrocode}
\newdef\settension#1{\setmfnumeric{default_tension}{#1}}%
% \end{macrocode}
%
%
-% \section{Settings for text label placement in
-% figures}\label{labelsettings}
+% \section{Settings for text label placement in figures}\label{labelsettings}
%
% \cs{tlabel}\,s are `justified' by placing a particular point of the
% text at the location specified. The \cs{tlabel} command accepts an
@@ -2103,109 +2497,131 @@
% is empty. The code assumes that if there are only 2, 1 or no parts, then
% later parts should be left unchanged. The rotation part is empty by
% default (rather than 0), because some commands will emit a warning if
-% a nonempty rotation exists without \opt{mlabels}.
+% a nonempty rotation exists without \opt{mplabels}.
%
% The result of \cs{tlabeljustify} is that \cs{tl@vpos} is set equal to
% $-1$ if the first letter is (default) \texttt{B} (for Baseline), and
% 0--2 if it is \texttt{b} (bottom), \texttt{c} (center) or \texttt{t}
% (top). Similarly, \cs{tl@hpos} is set equal to $-1$ if the second
% letter is (default) \texttt{l} (for left), and 0 or 1 if it is
-% \texttt{c} (center) or \texttt{r} (right).
+% \texttt{c} (center) or \texttt{r} (right). Making the defaults
+% negative means they must be handled by the \cs{else} portion of any
+% \cs{ifcase}.
%
-% The rotation part is just saved in \cs{tlabel@rot}. This is now true
-% even without \opt{mplabels} in case it is turned on later. It is
-% ignored by \cs{tlabel} without \opt{mplabels}, but a warning is issued
-% here anyway.
+% The rotation part is just saved in \cs{tl@rot}. This is true even
+% without \opt{mplabels}, in case it is turned on later. It is ignored by
+% \cs{tlabel} without \opt{mplabels}, but a warning is issued here anyway.
% \begin{macrocode}
\newcount\tl@vpos \newcount\tl@hpos
-\tl@vpos=-1 \tl@hpos=-1
-\newdef\tlabel@rot{}%
-%
-\newdef\tlabeljustify#1{%
- \mfp@ifempty{#1}{}{\mfp@justify#1\mfp@delim}}%
-\def\mfp@justify#1#2\mfp@delim{\mfp@local\mfp@vconv{#1}%
- \mfp@ifempty{#2}{}{\@mfp@justify#2\mfp@delim}}%
-\newdef\@mfp@justify#1#2\mfp@delim{%
- \mfp@local\mfp@hconv{#1}\mfp@local\edef\tlabel@rot{#2}
+\newdef\mfp@tlh@l{-1}%
+\newdef\mfp@tlh@c{0}%
+\newdef\mfp@tlh@r{1}%
+\newdef\mfp@tlv@B{-1}%
+\newdef\mfp@tlv@b{0}%
+\newdef\mfp@tlv@c{1}%
+\newdef\mfp@tlv@t{2}%
+\newdef\tl@rot{}\tl@vpos-1 \tl@hpos-1
+\newdef\tlabeljustify#1{\mfp@ifexist{#1}{\mfp@justify#1\mfp@end}}%
+\newdef\mfp@justify#1#2\mfp@end{\mfp@local\mfp@vconv{#1}%
+ \mfp@ifexist{#2}{\mfp@@justify#2\mfp@end}}%
+\newdef\mfp@@justify#1#2\mfp@end{\mfp@local\mfp@hconv{#1}%
+ \mfp@local\edef\tl@rot{#2}%
\@ifmplabels{}%
- {\mfp@ifempty{#2}{}%
+ {\mfp@ifexist{#2}%
{\Mfpic@warn{Rotation of labels is not supported without the
- mplabels\@msgbreak option. Rotation may be ignored\@online .}}}}%
+ mplabels\@msgbreak option. Rotation setting\on@line\space
+ may be ignored.}}%
+ }}%
\newdef\mfp@vconv#1{%
- \if B#1\tl@vpos=-1 \else
- \if b#1\tl@vpos= 0 \else
- \if c#1\tl@vpos= 1 \else
- \if t#1\tl@vpos= 2 \else
- \tl@vpos=-1 \mfp@justifyerror
- \fi\fi\fi\fi}%
+ \@ifundefined{mfp@tlv@#1}%
+ {\tl@vpos-1 \mfp@justify@error}%
+ {\tl@vpos\@nameuse{mfp@tlv@#1}\relax}}%
\newdef\mfp@hconv#1{%
- \if l#1\tl@hpos=-1 \else
- \if c#1\tl@hpos= 0 \else
- \if r#1\tl@hpos= 1 \else
- \tl@hpos=-1 \mfp@justifyerror
- \fi\fi\fi}%
-\newdef\mfp@justifyerror{%
- \mfp@errmsg{Invalid justification parameter for text label.}%
- {The optional argument for a text label must be^^J%
- one of B, b, c or t^^J
- followed by l or r or c,^^J%
- followed by an angle (optionally).}}%
+ \@ifundefined{mfp@tlh@#1}%
+ {\tl@hpos-1 \mfp@justify@error}%
+ {\tl@hpos\@nameuse{mfp@tlh@#1}\relax}}%
% \end{macrocode}
%
% \DescribeMacro{\tlpathjustify}
% This acts just like \cs{tlabeljustify} from the user's point of view.
% Since it is only for users, and not used internally, it merely defines
-% three macros that \cs{@@@tlpath} passes on to \cs{tlabeljustify} when no
-% justification parameter is present.
+% three macros that are passed to \cs{tlabeljustify} when no justification
+% parameter is present for a \cs{tlabel}-path command.
% \begin{macrocode}
\newdef\tlpathjustify#1{%
- \mfp@ifempty{#1}{}{\tlp@justify#1\mfp@delim}}%
-\newdef\tlp@justify#1#2\mfp@delim{\mfp@def\tlpath@v{#1}%
- \mfp@ifempty{#2}{}{\tlp@@justify#2\mfp@delim}}%
-\newdef\tlp@@justify#1#2\mfp@delim{%
+ \mfp@ifexist{#1}{\tlp@justify#1\mfp@end}}%
+\newdef\tlp@justify#1#2\mfp@end{\mfp@def\tlpath@v{#1}%
+ \mfp@ifexist{#2}{\tlp@@justify#2\mfp@end}}%
+\newdef\tlp@@justify#1#2\mfp@end{%
\mfp@def\tlpath@h{#1}\mfp@def\tlpath@r{#2}}%
\tlpathjustify{cc}%
% \end{macrocode}
%
+% \DescribeMacro{\tlpointsep}
+% This takes an absolute length as its argument and sets parameters that
+% determine by how much a tlabel is separated from the point it is placed
+% at. This is in case something is drawn at that point, such as a dot or
+% a hash mark on an axis.
+%
+% \DescribeMacro{\tlpathsep}
+% This takes an absolute length and sets parameters that determine by how
+% much a path created by one of the \cs{tlabel\meta{path}} macros is
+% separated from the label it surrounds.
+%
% \DescribeMacro{\tlabelsep}
-% \DescribeMacro{\tlabeloffset}
-% \cs{tlabelsep} takes an absolute length as its argument and sets
-% parameters that determine by how much a label is separated from the
-% point it is placed at. This is in case something is drawn at that point,
-% such as a dot or a hash mark on an axis.
+% This simply issues both the above commands. This is for compatibility
+% with \mfpic{} versions 0.7 or earlier, where both these separations were
+% determined by the same parameters.
%
-% \cs{tlabeloffset}, on the other hand, takes a pair of dimensions,
-% representing offsets. This is mainly so that, if labels seem to be off,
-% they can all be shifted with one command.
+% \DescribeMacro{\tlabeloffset}
+% \cs{tlabeloffset}, on the other hand, takes two arguments: dimensions,
+% representing offsets of labels from their point of placement. This is
+% mainly so that, if labels seem to be off, they can all be shifted with
+% one command.
%
% Both these values are written to the \MF{} file for the sake of the
% commands that draw rectangles or ovals around labels.
% \begin{macrocode}
-\newdef\tlabelsep#1{\mfp@local\tlabel@sep=#1\relax
+\newdef\tlpointsep#1{\mfp@local\tlabel@sep#1\relax
\setmfnumeric{label_sep}{\the\tlabel@sep}}%
+\newdef\tlpathsep#1{\mfp@local\tlpath@sep#1\relax
+ \setmfnumeric{labelpath_sep}{\the\tlpath@sep}}%
+\newdef\tlabelsep#1{\tlpointsep{#1}\tlpathsep{#1}}%
\newdef\tlabeloffset#1#2{%
- \mfp@local\tlabel@hadj=#1\relax \mfp@local\tlabel@vadj=#2\relax
- \setmfvariable{pair}{label_adjust}%
+ \mfp@local\tlabel@hadj#1\relax \mfp@local\tlabel@vadj#2\relax
+ \setmfpair{label_adjust}%
{(\the\tlabel@hadj,\the\tlabel@vadj)}}%
% \end{macrocode}
%
% \DescribeMacro{\everytlabel}
% Every line of a multiline label is written inside a group, so
-% adjustments to baselineskip and other things are difficult or impossible.
-% Therefore, \cs{everytlabel} is provided to define tokens to be inserted
-% before the label code, outside these groups. It is not itself a token
-% variable, so that that it can write the tokens to the \MP{} file once,
-% instead of at every label.
-% \begin{macrocode}
-\newdef\everytlabel{\afterassignment\@everytlabel
- \mfp@local\every@tlabel}%
-\newdef\@everytlabel{%
+% adjustments to baselineskip and other things are difficult or
+% impossible. Therefore, \cs{everytlabel} is provided to define code to be
+% inserted before the label code, outside these groups. The messing around
+% with the token register \cs{mfp@toks} is so one can put definitions in
+% the code without having to double \arg{} characters.
+%
+% The command \cs{extra@endtlabel} is called at the end of every
+% \cs{tlabel}. Normally it does nothing, but the \cs{tlabels} command
+% defines it to repeat the start of a loop.
+% \begin{macrocode}
+\newdef\everytlabel{\afterassignment\mfp@everytlabel\mfp@toks}%
+\newdef\mfp@everytlabel{\mfp@local\edef\every@tlabel{\the\mfp@toks}%
\mfp@ifmpost
- {\@ifmfpfileisopen
+ {\mfp@ifopengraphsfile
{\mfcmd{verbatimtex\@nl
- \def\noexpand\everylabel{\the\every@tlabel} etex}}%
+ \def\noexpand\everylabel{\the\mfp@toks} etex}}%
{}}%
{}}%
+\newdef\every@tlabel{}%
+\newdef\extra@endtlabel{}%
+% \end{macrocode}
+%
+% Without \opt{mplabels} it is impossible to use \MP{} variables in
+% a text label placement parameter. I have occasionally found it useful
+% to have a \TeX{} version of the numeric variable \gbc{pi}.
+% \begin{macrocode}
+\newdef\pinumber{3.14159}%
% \end{macrocode}
%
%
@@ -2215,13 +2631,44 @@
% This sets the arrowhead shape. The parameters are: the ratio of width
% to length, the tension (see \file{grafbase.dtx}) and a boolean
% (\gbc{true} or \gbc{false}) to say whether the the head is filled, or
-% only the barbs are drawn. (In retrospect, it would have been more
-% consistent with other commands to have one \TeX{} parameter with commas
-% separating the components.)
+% only the barbs are drawn. It would be more consistent with other
+% commands to have one \TeX{} parameter with commas separating the
+% components, but this is legacy code.
% \begin{macrocode}
\newdef\headshape#1#2#3{\mfcmd{\@nl headshape (#1, #2, #3)}}%
% \end{macrocode}
%
+% There are up to 4 optional arguments to any given \cs{arrow} command,
+% and these can take any one of 6 special indicators ({\mytt b}, {\mytt c},
+% {\mytt f}, {\mytt l}, {\mytt r} or {\mytt s}). Here we define macros
+% to handle these options. Each macro is fed a \emph{value}. For example,
+% the indicator {\mytt b} must be followed by a length, and that length is
+% the argument of \cs{mfp@arr@b}.
+% \begin{macrocode}
+\newdef\mfp@arr@b#1{\edef\mfp@hlocate{#1}}%
+\newdef\mfp@arr@c#1{\edef\mfp@hcolour{#1}}%
+\newdef\mfp@arr@l#1{\edef\mfp@hlength{#1}}%
+\newdef\mfp@arr@r#1{\edef\mfp@hrotate{#1}}%
+\newlet\mfp@arr@f\mfp@arr@b
+\newlet\mfp@arr@s\mfp@arr@l
+% \end{macrocode}
+%
+% The \cs{arc} and \cs{circle} commands take an optional argument that
+% indicates how the arc or circle is defined (and therefore the format of
+% the non-optional argument). These are passed to different \grafbase{}
+% commands whose names start with \gbc{arc} or \gbc{circle}. The commands
+% below store the rest of that name. The switch is set true by the
+% \cs{arc} command and false by \cs{circle}.
+% \begin{macrocode}
+\newdef\mfp@arc@s{pps}%
+\newdef\mfp@arc@t{ppp}%
+\newdef\mfp@arc@p{\ifmfp@switch plr\fi}%
+\newdef\mfp@arc@a{\ifmfp@switch alt\fi}%
+\newdef\mfp@arc@c{cp\ifmfp@switch s\fi}%
+\newdef\mfp@arc@r{pp (\mfp@T)}%
+\newdef\mfp@arc@q{pp (\mfp@F)}%
+% \end{macrocode}
+%
% \DescribeMacro{\mfpdefinecolor}
% \prog{Mfpic} supports color under \MP{} by writing all color
% definitions to the output \file{.mp} file. In order to support a
@@ -2231,29 +2678,30 @@
% a very dark blue. This can be set by
% \cs{mfpdefinecolor}\marg{vdblue}\marg{cmyk}\marg{.3,.3,0,.7}.
%
-% \DescribeMacro{\fillcolor}
-% \DescribeMacro{\drawcolor}
-% \DescribeMacro{\headcolor}
-% \DescribeMacro{\hatchcolor}
-% \DescribeMacro{\pointcolor}
-% \DescribeMacro{\tlabelcolor}
-% \DescribeMacro{\backgroundcolor}
-% To support the syntax
-% \cs{fillcolor}\oarg{\meta{model}}\marg{\meta{parameters}}, we just
-% call \cs{@mfpcolor} with the \grafbase{} color variable name
-% \gbc{fillcolor}.
+% Several commands are provided to set certain special colors. Currently
+% we have the following commands, setting the color of the same name as
+% the command (except \cs{backgroundcolor} which sets a color named
+% \gbc{background}):
+% \cs{drawcolor}\SpecialUsageIndex{\drawcolor},\\
+% \cs{fillcolor}\SpecialUsageIndex{\fillcolor},
+% \cs{hatchcolor}\SpecialUsageIndex{\hatchcolor},
+% \cs{headcolor}\SpecialUsageIndex{\headcolor},
+% \cs{pointcolor}\SpecialUsageIndex{\pointcolor},
+% \cs{tlabelcolor}\SpecialUsageIndex{\tlabelcolor}, and\\
+% \cs{backgroundcolor}\SpecialUsageIndex{\backgroundcolor}.
+%
+% These commands support the \LaTeX-like syntax:\\
+% \indent\cs{fillcolor}\oarg{\meta{model}}\marg{\meta{parameters}}.\\
+% The code for \cs{backgroundcolor} has to be spelled out since the
+% command name has the string `\texttt{color}', but the color does not.
% \begin{macrocode}
\newdef\mfpdefinecolor#1#2#3{\setmfcolor{#1}{#2(#3)}}%
-\newdef\fillcolor{\@mfpcolor{fillcolor}}%
-\newdef\drawcolor{\@mfpcolor{drawcolor}}%
-\newdef\headcolor{\@mfpcolor{headcolor}}%
-\newdef\hatchcolor{\@mfpcolor{hatchcolor}}%
-\newdef\pointcolor{\@mfpcolor{pointcolor}}%
-\newdef\tlabelcolor{\@mfpcolor{tlabelcolor}}%
-\newdef\backgroundcolor{\@mfpcolor{background}}%
-\newdef\@mfpcolor#1{%
- \mfp@ifnextchar[{\mfp@color{#1}}{\setmfcolor{#1}}}%
-\newdef\mfp@color#1[#2]#3{\setmfcolor{#1}{#2(#3)}}%
+\newdef\mfp@defclr#1#2#3{\setmfcolor{#1}{\mfp@ifempty{#2}{#3}{#2(#3)}}}%
+\newdef\mfp@newcolorcmd#1{%
+ \@namedef{#1\mfp@C}{\mfp@nullopt{\mfp@defclr{#1\mfp@C}}}}%
+\mfp@newcolorcmd{draw}\mfp@newcolorcmd{fill}\mfp@newcolorcmd{tlabel}%
+\mfp@newcolorcmd{hatch}\mfp@newcolorcmd{head}\mfp@newcolorcmd{point}%
+\def\backgroundcolor{\mfp@nullopt{\mfp@defclr{background}}}%
% \end{macrocode}
%
% When \cs{plotdata} is used to draw several graphs from one file, each
@@ -2279,22 +2727,22 @@
% Dashed lines is the default.
% \begin{macrocode}
\newdef\dashedlines{\mfp@local\mfp@linetype0
- \mfp@def\mfp@setstyle{\gendashed{getdashpat\number\mfp@linetype}}}%
+ \mfp@def\mfp@setstyle{\gendashed{getdashpat \number\mfp@linetype}}}%
\dashedlines
% \end{macrocode}
%
% \DescribeMacro{\coloredlines}
-% This uses \cs{draw} with an optional color parameter set
-% to \gbc{colortype0} through (by default) \gbc{colortype 7}. These are
-% color variables defined by \grafbase{}. It also issues a warning if
-% \MF{} is being used. As with \cs{dashedlines}, a \MP{} command
-% \gbc{getcolor} selects these colors.
+% This uses \cs{draw} with an optional color parameter set to
+% \gbc{colortype0} through (by default) \gbc{colortype7}. These are color
+% variables defined by \grafbase{}. It also issues a warning if \MF{} is
+% being used. As with \cs{dashedlines}, a \MP{} command \gbc{getcolor}
+% selects these colors.
% \begin{macrocode}
\newdef\coloredlines{%
\mfp@ifmpost
{\mfp@local\mfp@linetype0
- \mfp@def\mfp@setstyle{\draw [getcolor\number\mfp@linetype]}}%
- {\Mfpic@warn{You can't use \string\coloredlines\space in
+ \mfp@def\mfp@setstyle{\draw[getcolor \number\mfp@linetype]}}%
+ {\Mfpic@warn{You can't use \mfp@b coloredlines in
Metafont.\@msgbreak Using \dashedlines instead}%
\dashedlines}}%
% \end{macrocode}
@@ -2305,7 +2753,7 @@
% through 9 different symbols.
% \begin{macrocode}
\newdef\pointedlines{\mfp@local\mfp@linetype0
- \mfp@def\mfp@setstyle{\plot{getsymbol\number\mfp@linetype}}}%
+ \mfp@def\mfp@setstyle{\plot{getsymbol \number\mfp@linetype}}}%
% \end{macrocode}
%
% \DescribeMacro{\datapointsonly}
@@ -2353,39 +2801,39 @@
% \end{macrocode}
%
% \DescribeMacro{\mfplinetype}
-% \DescribeMacro{\mfplinestyle}
-% The user-level commands \cs{mfplinetype} and \cs{mfplinestyle} allow the
-% user to set the starting value of \cs{mfp@linetype}. The default is to
-% start at $0$. The names ``linestyle'' and ``linetype'' come from
-% an analogous system of changing rendering in \prog{gnuplot}.
-% \begin{macrocode}
-\newdef\mfplinetype#1{\mfp@local\mfp@linetype=#1}%
+% The user-level command \cs{mfplinetype} allows the user to set the
+% starting value of \cs{mfp@linetype}. The default is to start at $0$.
+% \DescribeMacro{\mfplinestyle}\cs{mfplinestyle} is an alias for this
+% same command. The names `linestyle' and `linetype' come from
+% an analogous system of changing rendering in \prog{gnuplot}. (The
+% reason for two names is that \prog{gnuplot} documentation was not
+% consistent in its terminology.)
+% \begin{macrocode}
+\newdef\mfplinetype#1{\mfp@local\mfp@linetype#1\relax}%
\newlet\mfplinestyle\mfplinetype
\mfplinetype{0}%
% \end{macrocode}
%
-% \prog{Mfpic} has 6 possible axes, the x- and y-axes pass through the
+% \prog{Mfpic} has 6 possible axes, the $x$- and $y$-axes pass through the
% origin, $(0,0)$, the four border axes are at the edges of the graph
% space, possibly offset from it by some amount. These four commands hold
% the amount of that offset in graph units, the default being 0.
% \begin{macrocode}
-\newdef\mfp@lshift{0}%
-\newdef\mfp@bshift{0}%
-\newdef\mfp@rshift{0}%
-\newdef\mfp@tshift{0}%
+\newdef\mfp@lshift{0}\newdef\mfp@bshift{0}%
+\newdef\mfp@rshift{0}\newdef\mfp@tshift{0}%
% \end{macrocode}
%
% \DescribeMacro{\axismargin}
+% The main user-level command for setting the above offsets is
+% \cs{axismargin}. Its first argument is one of the letters l, b, r, or t
+% to select the axis and the second is the offset in graph units. A
+% positive offset represents an inward shift, which is why we call them
+% \emph{margins}.
% \DescribeMacro{\setaxismargins}
+% The arguments of \cs{setaxismargins} are four offsets for the axes (in
+% the above order, which is anticlockwise, starting with the left).
% \DescribeMacro{\setallaxismargins}
-% These are the user-level command for setting the above offsets. The
-% first argument to \cs{axismargin} is one of the letters l, b, r, or t to
-% select the axis and the second is the offset in graph units. A positive
-% offset represents an inward shift, which is why we call them
-% \emph{margins}. The arguments of \cs{setaxismargins} are four offsets
-% for the axes (in the above order, which is anticlockwise, starting
-% with the left). \cs{setallaxismargins} takes one offset and applies it
-% to all axes.
+% \cs{setallaxismargins} takes one offset and applies it to all axes.
%
% The values are passed on to \MF{} in the variables \gbc{laxis},
% \gbc{baxis}, \gbc{raxis}, and \gbc{taxis}. They are also needed by the
@@ -2394,44 +2842,54 @@
\newdef\axismargin#1#2{\setmfnumeric{#1axis}{#2}%
\mfp@local\@namedef{mfp@#1shift}{#2}}%
\newdef\setaxismargins#1#2#3#4{\axismargin l{#1}\axismargin b{#2}%
- \axismargin r{#3}\axismargin t{#4}}%
+ \axismargin r{#3}\axismargin t{#4}}%
\newdef\setallaxismargins#1{\setaxismargins{#1}{#1}{#1}{#1}}%
% \end{macrocode}
%
% The following converts axis letters to a numeric, making conditionals for
-% axes simpler to code (\cs{ifcase} versus the six nested \cs{if}\,s we
-% see here.) Plus we get to define the error message only once. The
-% first argument is the letter and the second argument is the calling
-% command (for the error message).
-% \begin{macrocode}
-\newcount\mfp@axisnum
-\def\get@axisnum#1#2{%
- \if x#1\relax \mfp@axisnum0
- \else\if y#1\relax \mfp@axisnum1
- \else\if l#1\relax \mfp@axisnum2
- \else\if b#1\relax \mfp@axisnum3
- \else\if r#1\relax \mfp@axisnum4
- \else\if t#1\relax \mfp@axisnum5
- \else
- \mfp@errmsg {Invalid axis letter "#1".}%
- {The command #2 requires an axis to be designated by one of^^J%
- the following letters: x, y, l, b, r, or t. If you proceed, ^^J%
- x will be assumed.}%
- \fi\fi\fi\fi\fi\fi}%
+% axes simpler to code (\cs{ifcase} versus six nested \cs{if}\,s).
+% Plus we get to define the error message only once. The first argument
+% of \cs{mfp@getaxis} is the letter and the third argument is the calling
+% command (for the error message). We allow the letter to be instead a word,
+% using only the first letter and discarding the rest (\arg2).
+% \begin{macrocode}
+\def\mfp@axis@x{0}%
+\def\mfp@axis@y{1}%
+\def\mfp@axis@l{2}%
+\def\mfp@axis@b{3}%
+\def\mfp@axis@r{4}%
+\def\mfp@axis@t{5}%
+\def\mfp@getaxis#1#2\mfp@end#3{%
+ \@ifundefined{mfp@axis@#1}%
+ {\mfp@errmsg {Invalid axis letter "#1"}%
+ {The command #3 requires an axis to be designated by one of^^J%
+ the following letters: x, y, l, b, r, or t. If you proceed, ^^J%
+ x will be assumed.}\def\mfp@axisnum{0}\def\mfp@axisletter{x}}%
+ {\edef\mfp@axisnum{\@nameuse{mfp@axis@#1}}\def\mfp@axisletter{#1}}%
+ \edef\mfp@defheadlen{%
+ \the\ifnum\mfp@axisnum>1 \sideheadlen\else \axisheadlen\fi}}%
% \end{macrocode}
%
-% \DescribeMacro{\setaxismarks}
-% \DescribeMacro{\setxmarks}
-% \DescribeMacro{\setymarks}
-% \DescribeMacro{\setbordermarks}
-% \DescribeMacro{\setallbordermarks}
% The tick marks (or hash marks) on the axes can be in three possible
-% positions: centered on the axes, all on one side, or all on the other
-% side. The default for the x- and y-axes is centered, for the border axes
-% it is inside. For changing this we provide the \cs{setaxismarks} command,
-% whose first argument is the axis letter, and whose second argument is
-% one of the words \texttt{inside}, \texttt{outside}, \texttt{centered},
-% \texttt{ontop}, \texttt{onbottom}, \texttt{onleft}, or \texttt{onright}.
+% positions: centered on the axes, on one side, or on the other side.
+% The default for the $x$- and $y$-axes is centered, for the border axes it
+% is inside.
+% \DescribeMacro{\setaxismarks}For changing this we provide the
+% \cs{setaxismarks} command, whose first argument is the axis letter, and
+% whose second argument is one of the words \texttt{inside},
+% \texttt{outside}, \texttt{centered}, \texttt{ontop}, \texttt{onbottom},
+% \texttt{onleft}, or \texttt{onright}.
+%
+% \DescribeMacro{\setxmarks}
+% \cs{setxmarks} takes one argument and sets the position for the $x$ axis
+% only, while
+% \DescribeMacro{\setymarks}\cs{setymarks} does the same for the $y$ axis.
+% For the border axes we provide
+% \DescribeMacro{\setbordermarks}\cs{setbordermarks}, which takes four
+% arguments: the positions for the left, bottom, right and top axis (in
+% that order). The abbreviation
+% \DescribeMacro{\setallbordermarks}\cs{setallbordermarks} takes one
+% argument and sets the position for all border axis marks to that.
% \begin{macrocode}
\newdef\setaxismarks#1#2{\setmfnumeric{#1tick}{#2}}%
\newdef\setxmarks#1{\setaxismarks x{#1}}%
@@ -2441,8 +2899,6 @@
\newdef\setallbordermarks#1{\setbordermarks{#1}{#1}{#1}{#1}}%
% \end{macrocode}
%
-% \DescribeMacro{\smoothdata}
-% \DescribeMacro{\unsmoothdata}
% The \cs{datafile} and \cs{plotdata} commands originally just created
% polygonal paths, but users requested the ability to draw smooth ones.
% Originally only the commands below (\cs{smoothdata} and
@@ -2451,30 +2907,42 @@
% effect, so now the commands below just set the defaults for the optional
% parameter, storing them in the macros \cs{mfp@smoothness} and
% \cs{mfp@tension}. The latter can be used to adjust the tension of the
-% \MF{} path if smoothness is `s'. Default tension is now empty rather
-% than 1 so we can implement a scheme to change the actual default used.
-% \begin{macrocode}
-\newdef\smoothdata{\null@opt\@smoothdata}%
-\newdef\@smoothdata[#1]{\mfp@def\mfp@smoothness{s}%
- \mfp@def\mfp@tension{#1}}%
-\newdef\unsmoothdata{\mfp@def\mfp@smoothness{p}%
- \mfp@def\mfp@tension{}}%
+% \MF{} path if smoothness is `\texttt{s}'.
+%
+% \DescribeMacro{\smoothdata}
+% This takes an option tension argument. It defines \cs{mfp@smoothness} to
+% be `\texttt{s}' and \cs{mfp@tension} to be the optional argument.
+% Default tension is empty rather than 1 so we can implement a scheme to
+% change the actual default used.
+% \DescribeMacro{\unsmoothdata}\cs{unsmoothdata} defines
+% \cs{mfp@smoothness} to be `\texttt{p}' and \cs{mfp@tension} to be
+% empty. The latter might not be necessary.
+% \begin{macrocode}
+\newdef\mfp@smdata#1{\mfp@def\mfp@smoothness{#1}%
+ \mfp@nullopt{\mfp@def\mfp@tension}}%
+\newdef\smoothdata{\mfp@smdata{s}}%
+\newdef\unsmoothdata{\mfp@smdata{p}\@empty}%
\unsmoothdata% default
% \end{macrocode}
%
% \DescribeMacro{\using}
-% \DescribeMacro{\usingpairdefault}
-% \DescribeMacro{\usingnumericdefault}
-% These commands control how we interpret data in a file.
% The command \cs{using} is named after a keyword in \prog{gnuplot}. It
% controls which datum is plotted against which by \cs{plotdata} and
-% \cs{datafile}. The default is to read the first two space separated
-% words on a line as the x- and y-coordinate of a point. To allow that
-% there might be more data on a line, it is also assumed that the second
-% word is followed by a space and the rest of the line is read as a third
-% parameter that is not used. In case there are only two words,
-% \mfpic{} will always add a space on the end and then the third
-% parameter will be empty.
+% \cs{datafile}.
+%
+% \DescribeMacro{\usingpairdefault}\cs{usingpairdefault} sets the default,
+% which is to read the first two space separated words on a line as the
+% $x$ and $y$ coordinate of a point. To allow that there might be more
+% data on a line, it is also assumed that the second word is followed by a
+% space and the rest of the line is read as a third parameter that is not
+% used. In case there are only two words, \mfpic{} will always add a space
+% on the end and then the third parameter will be empty.
+%
+% \DescribeMacro{\usingnumericdefault}
+% For the occasional command that needs numeric data,
+% \cs{usingnumericdefault} sets \cs{using} so that the first
+% space-separated word on a line is selected and the remainder of the line
+% discarded.
%
% The \cs{parse@data} command reads its argument as delimited
% parameters, and the \cs{using} command tells what those delimiters are.
@@ -2487,23 +2955,22 @@
% parentheses with the first two parameters separated by a comma. This
% example is the default as we see here in \cs{usingpairdefault}. This
% command can be issued by a user to return to that default. The user can
-% also issue \cs{usingnumericdefault} to write the first space separated
-% word as a single numeric. This is for commands like \cs{piechart}.
+% also issue \cs{usingnumericdefault} to write the first word as a single
+% numeric for commands like \cs{piechart}.
%
% \DescribeMacro{\mfpdataperline}
% \cs{mfpdataperline} is a user level macro that controls the maximum
% number of points that will be written to a single line in the output.
% \begin{macrocode}
-\newdef\using#1#2{\mfp@def\parse@data#1\mfp@delim{#2}}%
+\newdef\using#1#2{\mfp@def\parse@data#1\mfp@end{#2}}%
\newdef\usingpairdefault{\using{##1 ##2 ##3}{(##1,##2)}}%
\newdef\usingnumericdefault{\using{##1 ##2}{##1}}%
\usingpairdefault
\newdef\mfpdataperline{5}%
% \end{macrocode}
%
-% The following macros are used internally by \cs{datafile} and \cs{plotdata}.
-% A comment line is read by \TeX{} as empty so we need an empty macro to
-% test against, and a blank line is read as \cs{par} (not the primitive
+% The following macros are used internally by \cs{datafile} and
+% \cs{plotdata}. A blank line is read as \cs{par} (not the primitive
% \cs{par}, but the literal token \cs{par}, so it tests as equal to
% \cs{mfp@par}. Early versions of \cs{plotdata} wrote a literal \MF{} path
% expression to the output file and \cs{mfp@join} created the \mfc{--}
@@ -2511,27 +2978,27 @@
% commands followed by a comma separated list of points, so \cs{mfp@join}
% has morphed into a comma.
% \begin{macrocode}
-\newdef\mfp@par{\par}%
-\newdef\mfp@join{,}%
+\newdef\mfp@par{\par}\newdef\mfp@join{,}%
% \end{macrocode}
%
-% \DescribeMacro{\makepercentother}
-% \DescribeMacro{\makepercentcomment}
% \DescribeMacro{\mfpdatacomment}
% The user can change the comment character for data files with
% something like \cs{mfpdatacomment}\marg{\cs{\#}}. This also changes the
% percent character to category `other'. We can also use the percent sign
-% as part of the numeric data by sandwiching the \cs{using} command
-% between \cs{makepercentother} and \cs{makepercentcomment}. Every file
-% reading command issues \cs{mfp@setcomment} before reading. The count
-% register \cs{@datacomment} hold the ASCII code of the current comment
+% as part of the numeric data by placing
+% \DescribeMacro{\makepercentother}\cs{makepercentother} before the
+% \cs{using} command to turn `\texttt\%' into an ordinary character.
+% \DescribeMacro{\makepercentcomment}\cs{makepercentcomment} returns it to
+% its usual role as a comment. Every file reading command issues
+% \cs{mfp@setcomment} before reading. The count register
+% \cs{mfp@commentchar} holds the ASCII code of the current comment
% character, the default being the percent sign.
% \begin{macrocode}
-\newcount\@datacommentchar \@datacommentchar`\%
+\newcount\mfp@commentchar \mfp@commentchar`\%%
\newdef\makepercentother{\@makeother\%}%
-\newdef\makepercentcomment{\catcode`\%=14 }%
-\newdef\mfpdatacomment#1{\mfp@local\@datacommentchar`#1}%
-\newdef\mfp@setcomment{\makepercentother\catcode\@datacommentchar14 }%
+\newdef\makepercentcomment{\catcode`\%14 }%
+\newdef\mfpdatacomment#1{\mfp@local\mfp@commentchar`#1}%
+\newdef\mfp@setcomment{\makepercentother\catcode\mfp@commentchar14 }%
% \end{macrocode}
%
% \DescribeMacro{\fdef}
@@ -2544,128 +3011,490 @@
% be use in \cs{function}, or a pair expression so it can be used alone in
% the last argument of \cs{parafcn}.
%
-% We apply \cs{@makeother} to \texttt{\#} so it doesn't get doubled. The
-% \texttt{\#} is frequent in vardefs among the \MF{} gurus.
+% We apply \cs{@makeother} to \arg{} so it doesn't get doubled. The
+% \arg{} is frequent in vardefs among the \MF{} gurus.
% \begin{macrocode}
-\newdef\fdef{\begingroup\@makeother\#\preservelines\@fdef}%
-\newdef\@fdef#1#2#3{\mfcmd{\@nl save #1;\@nl
+\newdef\fdef{\begingroup\@makeother\#\mfp@keeplines\mfp@fdef}%
+\newdef\mfp@fdef#1#2#3{\mfcmd{\@nl save #1;\@nl
vardef #1 (expr #2) = \@nl #3\@nl enddef}\endgroup}%
% \end{macrocode}
%
-% The following is executed last inside \cs{@mfpic@graf@macs}. It is a
-% hook for adding new definitions. It is initialized to empty, and add-on
-% packages can add to it.
+% The following is executed right after \cs{mfp@grafmacs}. It is a hook
+% for adding new definitions inside and \env{mfpic} environment. It is
+% initialized to empty, and add-on packages can add to it.
% \begin{macrocode}
\newdef\mfp@additions{}%
% \end{macrocode}
%
+% \section{The user level graphics macros}
%
-% \section{The main graphics macros}\label{main}
-%
-% In order to avoid redefining commands of other packages and in order
-% to avoid having other packages redefine \mfpic{} commands, all mfpic
-% commands that cannot be used outside the \env{mfpic} environment are
-% defined anew with every such environment. This is accomplished by having
-% the \cs{mfpic} command execute \cs{@mfpic@graf@macs}, which expands to
-% all those definitions. Grouping localizes the definitions, so outside
-% \env{mfpic} environments, these commands revert to being undefined, or
-% to there original definitions if they had any. As might be expected it
-% is quite long, over 300 lines of code at last count.
+% This is the start of the definition of \cs{mfp@grafmacs}. It contains
+% the definition of every user command that has no purpose outside an
+% \env{mfpic} environment. It will be expanded inside every \env{mfpic}
+% environment, localizing all these definitions. We do this so we can
+% avoid name clashes without giving them all names beginning with \cs{mfp}.
% \begin{macrocode}
-\newdef\@mfpic@graf@macs{%
+\newdef\mfp@grafmacs{%
% \end{macrocode}
%
-% \subsection{Plotting data from files}\label{datafiles}
+% \subsection{Figure macros}\label{figure}
+%
+% These are the figure macros. These are implemented as \MF{} paths that
+% can be variously rendered, transformed, etc., with the prefix macros of
+% subsection~\ref{prefixcommands}.
%
-% \DescribeMacro{\plotdata}
% Most of the \mfpic{} graphic commands take optional arguments. Most of
-% them follow the scheme below: \cs{@plotdata} takes a mandatory argument
-% enclosed in brackets, and does the work. \cs{plotdata} is simply defined
-% to test for an optional argument and pass it or a default to
-% \cs{@plotdata}. In this case, the default option is signaled by
-% passing empty brackets via \cs{null@opt}.
+% them follow the scheme \cs{mfp@command} takes mandatory arguments
+% enclosed in braces, and does the work. \cs{command} is simply defined to
+% test for an optional argument (with \cs{mfp@defaultopt}) and pass it or a
+% default to \cs{mfp@command}. A typical example is \cs{ellipse} defined
+% to be \verb$\mfp@defaultopt\mfp@ellipse0$. In some cases, the default
+% option is signaled by passing an empty argument via \cs{mfp@nullopt}.
+%
+% Of the commands that have no optional argument, most simply write
+% the appropriate commands to the output file, and require no
+% \cs{mfp@} version.
+%
+% This section contains a description of the behavior of each command and
+% its syntax in use. For those that call an \cs{mfp@} version, the
+% implementation is in the next section.
+%
+% \subsubsection{Common geometric objects}\label{geometric}
+%
+% The first few (\cs{rect} through \cs{sector}) are closed paths and
+% need not be prefixed with a closing macro before filling, etc.
+%
+% \DescribeMacro{\rect}
+% This takes one mandatory argument, the two diagonally opposite
+% corners of the rectangle separated by a comma, and draws the rectangle
+% with horizontal and vertical sides having these as two corners. The
+% direction of the path (which seldom makes a difference) is such that it
+% starts at the first specified point and moves horizontally first.
%
-% \cs{@plotdata} follows the scheme of most of the data file processing
-% commands: it calls \cs{@processdatafile} with the name of the file (the
-% second parameter of \cs{@plotdata}) and another command, \cs{mfp@doplots},
-% which really does the work. The argument of \cs{mfp@doplots} is the
-% original optional parameter of \cs{plotdata}. This optional parameter is
-% the smoothness, which may be \oarg{s\meta{tension}} for a smooth path
-% or \oarg{p} for a polygonal path, or empty to select whatever default
-% was created by \cs{smoothdata}.
+% This is an example of a command that requires no further processing.
+% The \cs{mfp@figmac} command sets the appropriate switches and writes its
+% argument to the output file, followed by a semicolon.
% \begin{macrocode}
- \newdef\plotdata{\null@opt\@plotdata}%
- \newdef\@plotdata[##1]##2{\@processdatafile{##2}{\mfp@doplots{##1}}}%
+ \newdef\rect##1{\mfp@figmac{rect (##1)}}%
% \end{macrocode}
%
-% \cs{mfp@doplots} is a self-looping macro. It issues the rendering
-% prefix (in \cs{mfp@setstyle}), increments the linetype, and writes the
-% path expression defined by the first set of data in the file (via
-% \cs{do@datafile}, which \emph{really} does the work and which stops at a
-% blank line). That part, when prefixed by \cs{@figmacstart}, functions
-% as a \cs{@figmac}. When finished with one figure, \cs{@if@enddata}
-% checks if there is another set of data in the file. If so,
-% \cs{mfp@doplots} calls itself again.
+% \DescribeMacro{\regpolygon}
+% There are four arguments. The first is the number of sides. The second
+% is the name of a variable to represent the vertices. The other
+% two should be equations locating two vertices or the center and one
+% vertex. If the name used is \gbc{Ver}, then the vertices will be
+% \gbc{Ver1}, \gbc{Ver2}, etc., numbering anticlockwise. The center is
+% \gbc{Ver0}. Case is significant. After the command, the points can be
+% referred to in other \mfpic{} commands (except text label commands
+% when \opt{mplabels} is not in effect).
+% \begin{macrocode}
+ \newdef\regpolygon##1##2##3##4{%
+ \mfp@figmac{regularpolygon (##1) (##2) (##3; ##4;)}}%
+% \end{macrocode}
%
-% The argument is the smoothness parameter(s), which it merely passes on.
-% There is no filename argument: since \cs{@processdatafile} has already
-% opened the input stream \cs{mfp@data} and subsequent macros will read
-% from it.
+% \DescribeMacro{\circle}
+% This has one optional argument containing one of the letters
+% \texttt{t}, \texttt{p} (the default), \texttt{c}, \texttt{s},
+% \texttt{r}, or \texttt{q}. Its purpose is to specify what kind of data
+% is in the single mandatory argument. These are a subset of the optional
+% arguments that the \cs{arc} commad (below) takes, and have almost the
+% same meaning:
+% \begin{itemize}
+% \item \oarg{t}, the three point form needs three points (of course).
+% \item \oarg{p}, the polar form needs the center point and the radius.
+% This is the default when the optional argument is absent.
+% \item \oarg{c}, the center/point form needs the center and one point on
+% the circle.
+% \item \oarg{s}, the point-sweep form requires two points and the angle of
+% arc between them. The angle may be positive or negative. A
+% positive angle indicates the number of degrees anticlockwise from
+% the first point to the second. Negative means clockwise.
+% \item \oarg{r}, the point-radius form requires two points and the
+% radius of the circle. Of the two possible circles, it produces the
+% one where the anticlockwise arc from the first point to the second
+% is not more than 180 degrees.
+% \item \oarg{q}, an alternate point-radius with the same parameters.
+% It produces the circle
+% where the anticlockwise arc from the first point to the second
+% is not less than 180 degrees.
+% \end{itemize}
+%
+% Supplying \cs{arc} with the same parameters, or the appropriate superset
+% in the case of \oarg{p} and \oarg{c}, would produce a sub-arc of the
+% circle that \cs{circle} produces. This is the reason for giving
+% \cs{circle} these options: to make diagrams which show a circle and an
+% arc on it.
+%
+% We use \cs{def} instead of \cs{newdef} because we know \LaTeX{} has
+% already defined a \cs{circle} command. We now use the same command,
+% \cs{mfp@arccircle}, for arcs and circles, setting a switch to tell it
+% which to produce.
% \begin{macrocode}
- \def\mfp@doplots##1{\mfp@setstyle
- \advance\mfp@linetype1 \@figmacstart
- \do@datafile{##1}%
- \@if@enddata{}{\mfp@doplots{##1}}}%
+ \def\circle{\mfp@switchfalse\mfp@defaultopt\mfp@arccircle{p}}%
% \end{macrocode}
%
-% When two blank lines occur in succession, \cs{plotdata} is suppose to
-% stop. \cs{@ifenddata} is called when \cs{do@datafile} ends (one blank
-% line was found), it checks for another blank line (or end-of-file)
-% and, if it finds one, selects the first of the two alternatives that
-% follow.
+% \DescribeMacro{\ellipse}
+% \cs{ellipse} has one optional argument, an angle of rotation and one
+% mandatory argument, containing the center, the horizontal and vertical
+% radii (before rotation), separated by commas.
% \begin{macrocode}
- \newdef\@if@enddata{% check for another blank line
- \ifeof\mfp@data \@xp\@firstoftwo
- \else
- \skipcomments
- \ifx\mfp@temp\mfp@par \@XP\@firstoftwo
- \else \@XP\@secondoftwo
- \fi
- \fi}%
+ \newdef\ellipse{\mfp@defaultopt\mfp@ellipse{0}}%
% \end{macrocode}
%
-% \DescribeMacro{\datafile}
-% \cs{datafile} defines a path connecting the points in a datafile. The
-% optional parameter is the smoothness, as in \cs{plotdata}. The other
-% parameter is the filename. \cs{@datafile} does the figure macro stuff,
-% opens the data file, and calls \cs{do@datafile} which does all the work.
+% \DescribeMacro{\sector}
+% This is the closed curve starting at the center of a circle, out
+% along a radius to the circumference of the circle, along an arc of the
+% circle, then back to the center. It has one mandatory argument
+% containing the center, the radius, and the two angles, separated by
+% commas.
% \begin{macrocode}
- \newdef\datafile{\null@opt\@datafile}%
- \newdef\@datafile[##1]##2{\@figmacstart
- \@processdatafile{##2}{\do@datafile{##1}}}%
+ \newdef\sector##1{\mfp@figmac{sector (##1)}}%
% \end{macrocode}
%
-% I lied. Actually all \cs{do@datafile} does is handle any empty
-% optional parameter and call \cs{@do@datafile} to do the work.
+% \DescribeMacro{\arc}
+% We have seven (actually 14) ways to specify a circular arc. \cs{arc}
+% takes an optional argument: a letter to indicate which of the 7
+% specifications. It has one mandatory argument containing the data for
+% that specification, separated by commas. It also has a star-form,
+% which produces the complementary arc. That is, instead of the arc that
+% is specified (as described below) it produces the rest of the circle
+% from the end of the specified arc to its start.
+%
+% The available letters and the corresponding required data are listed
+% below. The default optional argument is \oarg{s}.
+% \begin{itemize}
+% \item \oarg{t}, the three point form needs three ordered pairs.
+% \item \oarg{p}, the polar form needs the center point, two angles and
+% the radius (in that order).
+% \item \oarg{a}, the alternative polar form needs the center, the radius
+% and two angles (in that order).
+% \item \oarg{c}, the center-point-sweep form needs the center, the
+% starting point, and the angle.
+% \item \oarg{s}, the points-sweep form requires the starting point, the
+% ending point and the angle.
+% \item \oarg{r}, the points-radius form requires the starting point, the
+% ending point and the radius of the arc. There are two circles with
+% the given radius that pass through the given points, and hence
+% four possible arcs. One arc on each circle is anticlockwise from
+% the first point to the second. Of those two arcs, only one is
+% less than $180$ degrees. That one is produced.
+% \item \oarg{q} Same as \oarg{r}, except the arc is the larger of two
+% anticlockwise arcs.
+% \end{itemize}
% \begin{macrocode}
- \newdef\do@datafile##1{%
- \do@mtparam{##1}{\mfp@smoothness\mfp@tension}\@do@datafile}%
+ \newdef\arc{\mfp@iftoken*{\arccomplement\mfp@arc}{\mfp@arc}}%
% \end{macrocode}
%
-% And \cs{@do@datafile} is mainly just a wrapper, processing the optional
-% parameter (passed by either \cs{plotdata} or \cs{datafile}) which
-% selects what figure making code should be written. For
-% \oarg{s\meta{tension}} a \gbc{curve} command is used. For \oarg{p}, a
-% \gbc{polyline}. (The `\cs{mf@F}' means the curve is not closed).
+% \subsubsection{List macros}\label{list}
+%
+% The figure macros of this section are all list macros. They produce a
+% path from a list of ordered pairs. The list can be explicitly given as a
+% mandatory argument containing a comma-separated list of pair
+% expressions, or it can be taken from an external datafile. This is true
+% for \cs{polyline} through \cs{turtle}. These macros differ only in how
+% the data determine the path. (Some of the splines don't connect given
+% points, but rather use the points in a different way to determine a
+% curve.) The list of pairs is not an argument of any of these. It should
+% follow, and is ultimately written to output by the ending code of
+% \cs{mfp@listmac}. This is basic to list macros, and will not be repeated
+% for each one.
+%
+% Commands in this section are grouped by the argument structure of the
+% command written to the output file. The first twelve come in pairs
+% that are distinguished by a boolean parameter, which usually determines
+% whether the path produced is closed, followed by the list.
+%
+% \DescribeMacro{\polyline}
+% The most basic, a polyline or polygonal path, \cs{polyline} draws line
+% segments from each point to the next. \DescribeMacro{\lines}\cs{lines}
+% is an alias.
%
-% After that, \cs{mfp@rwdata} is called, which reads and writes the
-% data.
+% \DescribeMacro{\closedpolyline}\cs{closedpolyline} does the
+% same, except it produces a closed path, connecting the last point to the
+% first. \DescribeMacro{polygon}\cs{polygon} is an alias.
+%
+% \DescribeMacro{\computedspline}The \cs{computedspline} command takes a
+% list of points and computes the controls of a cubic spline that connects
+% the points. Adjacent segments have matching first and second derivatives
+% at the common endpoint. It is limited to what is called a \emph{relaxed}
+% spline, which has zero curvature at the beginning and ending points.
+%
+% \DescribeMacro{\closedcomputedspline}
+% This is similar, but computes the unique \emph{closed} cubic spline that
+% connects a list of points.
+%
+% The next four commands also produce types of splines. The list of pairs
+% is not, however, the points passed through but rather are control points,
+% of a sort.
+%
+% \DescribeMacro{\qspline}
+% This produces quadratic splines. The points create the path as follows:
+% for successive points $p\sb i, p\sb {i+1}$ in the list, the path passes
+% through the midpoint of the line segment connecting $p\sb i$ and
+% $p\sb {i+1}$, tangent to that line segment. This causes adjacent
+% B\'ezier segments to have matching derivatives at their common
+% endpoint. Each three successive data points determines one segment of
+% the path.
+%
+% \DescribeMacro{\closedqspline}
+% The closed variant works by creating two additional segments, viewing
+% the triples $p\sb{n-1}, p\sb{n}, p\sb1$ and $p\sb{n}, p\sb1, p\sb2$ as
+% being `successive'.
+%
+% \DescribeMacro{\cspline} This produces cubic splines. The points
+% determine the curve as follows: for successive points $p\sb i, p\sb
+% {i+1}$ in the list, the line segment connecting them is divided into
+% thirds with two points $q\sb i$ and $q\sb i'$. The curve then passes
+% through the midpoint of the segment from $q\sb {i-1}'$ to $q\sb i$ and
+% tangent to that segment. This causes adjacent B\'ezier segments to have
+% matching first and second derivatives at their common endpoint. Each
+% four successive data points determines one segment of the path.
+%
+% \DescribeMacro{\closedcspline} The closed variant works by viewing the
+% first point as a succesor of the last point, creating three additional
+% path segments.
+%
+% \DescribeMacro{\qbeziers}
+% This produces the equivalent of a sequence of \LaTeX{} \cs{qbezier}
+% commands. The mandatory argument will be a list of points alternating
+% between nodes and control points, ending with the last node.
+% \DescribeMacro{\closedqbeziers}The closed version ends with the control
+% point between the last node and the first. Therefore, the \cs{qbeziers}
+% command needs an odd number of points in the list, while
+% \cs{closedqbeziers} needs an even number. If this is not the case, the
+% last point in the list is repeated, causing the final segment to be a
+% straight line (closed case) or trivial.
+%
+% Note that quadratic B\'eziers need not be smooth at the nodes unless
+% the control points line up. Our plural name distinguishes it from the
+% \LaTeX{} command.
+%
+% \DescribeMacro{\fcnspline}
+% The above splines are two dimensional splines, that is, functions of
+% $t$ with pair values $(x,y)$. One dimensional splines are functions of
+% $x$ with numeric values $y$. This command produces the \emph{graph} of
+% such a function computed from a supplied list of points. It is required
+% that the $x$-values of the points are in increasing order, although it
+% will produce some sort of curve as long as adjacet $x$-values are
+% distinct. It produces the graph of a \emph{relaxed} spline, in the sense
+% that the second derivative $d^2y/dx^2$ is zero at the beginning and end.
+%
+% \DescribeMacro{\periodicfcnspline}
+% This is the same, but instead of requiring the second derivative to be
+% zero at both ends, it produces a graph such that the first and second
+% derivatives at the end match the ones at the beginning. If a copy of the
+% graph is placed with its starting point at the original graph's ending
+% point, then there is a smooth join. If the $y$-values of the
+% beginning and ending points match, a periodic graph is obtained. For
+% these last two commands the paths cannot be closed, and the boolean
+% determines the behavior at the endpoints.
+%
+% The command \cs{mfp@listi} that implements these writes the command
+% name and the argument and ends with code to write the list of pairs.
+% \begin{macrocode}
+ \newdef\lines{\mfp@listi{polyline}\mfp@F}%
+ \newdef\polygon{\mfp@listi{polyline}\mfp@T}%
+ \newdef\computedspline{\mfp@listi{dospline}\mfp@F}%
+ \newdef\closedcomputedspline{\mfp@listi{dospline}\mfp@T}%
+ \newdef\qspline{\mfp@listi{qspline}\mfp@F}%
+ \newdef\closedqspline{\mfp@listi{qspline}\mfp@T}%
+ \newdef\cspline{\mfp@listi{cspline}\mfp@F}%
+ \newdef\closedcspline{\mfp@listi{cspline}\mfp@T}%
+ \newdef\qbeziers{\mfp@listi{qbezier}\mfp@F}%
+ \newdef\closedqbeziers{\mfp@listi{qbezier}\mfp@T}%
+ \newdef\fcnspline{\mfp@listi{fcnspline}\mfp@F}%
+ \newdef\periodicfcnspline{\mfp@listi{fcnspline}\mfp@T}%
+ \newlet\closedpolyline\polygon
+ \newlet\polyline\lines
+% \end{macrocode}
+%
+% The next six macros have an optional argument called `tension', a
+% numerical value that controls how tightly the path turns at each point
+% in the list. If the optional argument is absent (or empty), the
+% \grafbase{} command that is written takes only a boolean argument
+% (closed or not closed) followed by the list of points. If the tension
+% argument is present, a different comamnd is written that also has a tension
+% argument. The command name is almost the same, but has a `\gbc{t}'
+% prepended. The default tension is the \cs{grafbase}{} variable
+% \gbc{default_tension}, set with \cs{settension}, initialized to 1.
+% Tension of 0 is ignored (ultimately tension is divided by) and may
+% produce error messages from \MF{}.
+%
+% \DescribeMacro{\curve}
+% \cs{curve} draws a smooth curve connecting a list of points. it arranges
+% for the curve, at point $p\sb n$ to be traveling parallel to the direction
+% from $p\sb {n-1}$ to $p\sb {n+1}$ (except at the first and last point).
+%
+% The closed version \DescribeMacro{\closedcurve}\cs{closedcurve} treats the
+% first point and last point the same as the rest. It has the alias
+% \DescribeMacro{\cyclic}\cs{cyclic}.
+%
+% \DescribeMacro{\convexcurve}
+% This convex version produces a smooth curve that is convex in those
+% places where \cs{polyline} (with the same list of points) would produce
+% a convex shape. \DescribeMacro{\closedconvexcurve}It also comes in a closed
+% version, with the alias \DescribeMacro{\convexcyclic}
+% \cs{convexcyclic}.
+%
+% \DescribeMacro{\mfbezier}
+% This produces the standard \MF{} cubic B\'ezier using the \MF{} path
+% join operator (\mfc{..}) with tension given by an optional argument.
+% \DescribeMacro{\closedmfbezier}The closed version connects the last
+% point to the first in the same way.
+%
+% The command \cs{mfp@curve} examines the optional argument and writes
+% the command name (first argument, possibly modified) with the
+% appropriate arguments (the boolean or tension plus boolean).
+% \begin{macrocode}
+ \newdef\curve{\mfp@curve{curve}\mfp@F}%
+ \newdef\cyclic{\mfp@curve{curve}\mfp@T}%
+ \newdef\convexcurve{\mfp@curve{ccurve}\mfp@F}%
+ \newdef\convexcyclic{\mfp@curve{ccurve}\mfp@T}%
+ \newdef\mfbezier{\mfp@curve{bezier}\mfp@F}%
+ \newdef\closedmfbezier{\mfp@curve{bezier}\mfp@T}%
+ \newlet\closedcurve\cyclic
+ \newlet\closedconvexcurve\convexcyclic
+% \end{macrocode}
+%
+% \DescribeMacro{\fcncurve}
+% A `function curve' is the graph of a function of $x$. It should always
+% travel left to right. This is not guaranteed by the \cs{curve} macro,
+% above, even if the $x$ coordinates are listed in order. \cs{fcncurve} is
+% guaranteed to do this if the list of points has the $x$ coordinates so
+% ordered.
+%
+% While the optional argument is called tension, it is not quite the
+% same as the tension argument to \cs{curve}, but it has a similar effect.
+% The default value is the sane as for \cs{curve}. The above guarantee is
+% voided if the value used is ${}\le1/3$.
% \begin{macrocode}
- \newdef\@do@datafile[##1##2]{%
- \mfsrc{%
- \if s##1 \mfp@ifempty{##2}{curve }{tcurve (##2) }%
- \else polyline
- \fi (\mf@F)}\mfp@rwdata}%
+ \newdef\fcncurve{\mfp@nullopt\mfp@fcncurve}%
+% \end{macrocode}
+%
+% \DescribeMacro{\turtle}
+% Many years ago there was a tool for teaching programming concepts to
+% young children. They could draw a figure on a screen by programming a
+% `turtle' with a sequence of turns and distances. The following is
+% similar. It has a mandatory argument containing a list of ordered pairs.
+% The first is the coordinates of the starting point, and each of the rest
+% is interpreted as a displacement from the current position, along which
+% a line segment is to be drawn. The terminal point of that segment is the
+% new current position
+% \begin{macrocode}
+ \newdef\turtle{\mfp@listmac{turtle}}%
+% \end{macrocode}
+%
+% \subsubsection{Graphing functions}\label{functions}
+%
+% \DescribeMacro{\function}
+% \prog{Mfpic} provides the ability to plot the graph of any function that
+% can be defined in \MF. This is done with the \cs{function} command, a
+% figure macro which takes one optional parameter that selects
+% the smoothness of the \MF{} path produced. It can be \oarg{p}, which
+% draws a polyline path through selected points (much like \cs{polyline}),
+% or \oarg{s\meta{tension}} which selects a smooth path (much like
+% \cs{curve}\oarg{\meta{tension}}). The tension may be omitted and then
+% will be 1 (or the value set by \cs{settension}). The default optional
+% argument is \oarg{s}, a smooth curve with the default tension.
+%
+% There are two mandatory arguments. The second is the function, which
+% must be written as a formula (strictly speaking, a \MF{} numerical
+% expression) containing only one variable, \texttt{x}. The first
+% mandatory argument contains three numbers separated by commas. They are,
+% in order, the starting value of \texttt{x}, the ending value, and the
+% step size.
+%
+% \DescribeMacro{\parafcn}
+% \cs{parafcn} is similar, except its formula must be an ordered pair of
+% formulas (strictly speaking, a \MF{} pair expression) in which
+% \texttt{t} is the only variable.
+%
+% \DescribeMacro{\plrfcn}
+% \cs{plrfcn} is similar, except its argument is a formula (a \MF{}
+% numeric expression) representing the radius, in which \texttt{t} is the
+% only variable, interpreted as the angle coordinate, $\theta$, in degrees.
+%
+% These three commands are so alike that they call the same macro
+% with the name to be written as the first argument and the default
+% optional argument as the second. The latter is so that we can reuse it
+% for two other commands where the default is different.
+% \begin{macrocode}
+ \newdef\function{\mfp@fcn{function}s}%
+ \newdef\parafcn{\mfp@fcn{parafcn}s}%
+ \newdef\plrfcn{\mfp@fcn{plrfcn}s}%
+% \end{macrocode}
+%
+% \DescribeMacro{\btwnfcn}
+% The region between two functions \cs{btwnfcn} is a figure macro. It is
+% mostly intended for shading or other filling operations so it is already
+% closed and, by default, it is not `smooth'. Its parameters are the
+% same as those of \cs{function} (above), with two exceptions: the default
+% optional argument is \oarg{p} and it has an additional mandatory
+% argument, the other function. The resulting \MF{} path travels along the
+% graph of the first function from the starting $x$ to the ending $x$,
+% thence vertically to the graph of the second function, along it (in
+% reverse) to the starting $x$ and finally, vertically to the start of the
+% first function's graph.
+%
+% \DescribeMacro{\btwnplrfcn}
+% We also have the polar function version, where the two function
+% arguments are as in \cs{plrfcn}. The default option is also \oarg{p}.
+% As with \cs{function}, these are implemented by calling a common
+% command with the basename of the \grafbase{} command as argument.
+%
+% \DescribeMacro{\plrregion}
+% This is like \cs{btwnplrfcn} with 0 as the first function, but it
+% is more efficient. It takes only the other function as an argument.
+% Again, the default option is \oarg{p}.
+%
+% \DescribeMacro{\belowfcn}
+% This is like \cs{btwnfcn} with 0 as the first function, but it
+% is more efficient. It takes only the other function as an argument.
+% The default option is \oarg{p}.
+% \begin{macrocode}
+ \newdef\btwnfcn{\mfp@btwn{btwnfcn}}%
+ \newdef\btwnplrfcn{\mfp@btwn{btwnplrfcn}}%
+ \newdef\belowfcn{\mfp@fcn{belowfcn}p}%
+ \newdef\plrregion{\mfp@fcn{plrregion}p}%
+% \end{macrocode}
+%
+% \DescribeMacro{\levelcurve}
+% One way to graph a function of two variables is to draw level curves.
+% The figure macro \cs{levelcurve} takes an optional smoothness
+% argument (\texttt{[s\meta{tension}]} or \texttt{[p]} with \texttt{p}
+% being the default), and two mandatory arguments. The first
+% mandatory argument is a `seed' point which is known to lie inside the
+% level curve, followed by a comma and a step size. The second is an
+% inequality between the function and the level value that is true inside
+% the level curve. Strictly speaking, the last argument can be any \MF{}
+% boolean valued expression with variables \mfc{x} and \mfc{y}. The simple
+% example,\\
+% \indent\verb$\levelcurve[p]{(0,0),0.1}{x*x + y*y < 3}$\\
+% will draw approximately the circle with radius $\sqrt3$ as a sequence of
+% line segments with length $0.1$. That will require about 108 segments.
+% Its parameters are similar enough to those of \cs{function} that we
+% can use the same interface.
+% \begin{macrocode}
+ \def\levelcurve{\mfp@fcn{levelset}p}%
+% \end{macrocode}
+%
+% \subsection{Plotting data from files}\label{datafiles}
+%
+% \DescribeMacro{\plotdata}
+% The optional parameter is the smoothness, which may be
+% \oarg{s\meta{tension}} for a smooth path or \oarg{p} for a polygonal
+% path, or empty to select the default. The default is \oarg{p} when
+% \mfpic{} is loaded, but can be changed with \cs{smoothdata} and restored
+% with \cs{unsmoothdata}.
+% \begin{macrocode}
+ \newdef\plotdata{\mfp@nullopt\mfp@plotdata}%
+% \end{macrocode}
+%
+% \DescribeMacro{\datafile}
+% \cs{datafile} defines a path connecting the points in a datafile. It has
+% an optional parameter for the smoothness, as in \cs{plotdata}.
+% \begin{macrocode}
+ \newdef\datafile{\mfp@nullopt\mfp@datafile}%
% \end{macrocode}
%
% \subsection{The prefix commands}\label{prefixcommands}
@@ -2676,7 +3505,7 @@
% This stores an \mfpic{} path in a \MF{} path variable. The argument is
% the variable name. It should be followed by an \mfpic{} figure macro
% (such as \cs{rect}\marg{...}). The stored path can be reused via the
-% \cs{mfobj} command. This command is written as a \cs{@rendmac} even
+% \cs{mfobj} command. This command is written as a \cs{mfp@rendmac} even
% though it does no rendering. This is so (1)~it will turn off implicit
% rendering (any rendering must be explicitly written) and (2) it can be
% placed anywhere within the prefix area. This is new behavior with
@@ -2686,22 +3515,48 @@
% precede \cs{store}. Note that \cs{store} takes a second argument but
% it merely puts it back. This is so that the old syntax (with the figure
% macro in braces) will continue to work.
+%
+% \DescribeMacro{\norender}
+% Occasionally it is useful to turn off rendering (say we want to add an
+% arrowhead to a subpath of a path that is already rendered). One could
+% put \cs{store} at the start with an unused name, but as every path is
+% already stored in \gbc{curpath}, that seems redundant. So here is a
+% prefix macro with null rendering and no side effects. It is exactly
+% like \cs{mfp@rendmac}, except it does not end with \cs{mfsrc}.
% \begin{macrocode}
- \newdef\store##1##2{\@rendmac{stored (##1)}##2}%
+ \newdef\store##1##2{\mfp@rendmac{stored (##1)}##2}%
+ \newdef\norender{\mfp@first\@mfprendfalse}%
% \end{macrocode}
%
% \DescribeMacro{\mfobj}
-% \DescribeMacro{\mpobj}
% This is the interface to use a \cs{store}-d object.
-% \cs{mfobj}\marg{\meta{name}} is a figure macro, and should act pretty
-% much exactly the same as if the figure macro that had been stored in the
-% variable were typed in its place. Its one argument, the variable name,
-% is read by \cs{@figmac}, which operates as described previously, either
-% implicitly rendering the path, or allowing it to be picked up by
-% preceding prefix macros.
+% \cs{mfobj}\marg{\meta{name}} is a figure macro, and should act exactly
+% the same as if the figure macro that had been stored in the variable
+% were typed in its place. Its one argument is the variable name.
+% \DescribeMacro{\mpobj}\cs{mpobj} is another name for the same command.
+% \begin{macrocode}
+ \newlet\mfobj\mfp@figmac \newlet\mpobj\mfobj
+% \end{macrocode}
+%
+% \DescribeMacro{\mfpimage}
+% The command \cs{mfpimage} starts a group in which drawing commands
+% work just as usual, except they draw in a picture variable, whose name
+% is the only mandatory argument to \cs{mfpimage}.
+% \DescribeMacro{\endmfpimage}The command \cs{endmfpimage} ends that
+% group. There is an optional argument to set the reference point of the
+% created picture, the default being \gbc{(0,0)}. The resulting picture
+% can then be placed using \cs{putmfpimage}.
+% \begin{macrocode}
+ \newdef\mfpimage{\mfp@defaultopt\mfp@image{(0,0)}}%
+ \newdef\endmfpimage{\mfcmd{\@nl concludeimage}}%
+% \end{macrocode}
+%
+% \DescribeMacro{\putmfpimage}
+% This takes the name of a picture variable created with the
+% \cs{mfpimage} environment (or even with the \cs{tile} environment), and
+% places it with its reference point at the points in a list.
% \begin{macrocode}
- \newlet\mfobj\@figmac
- \newlet\mpobj\@figmac
+ \newdef\putmfpimage##1{\mfsrc{\@nl putimage (##1)}\mfp@writedata}%
% \end{macrocode}
%
% \subsubsection{Subpaths}
@@ -2719,32 +3574,32 @@
% \cs{cutoffbefore} to the reversed path and reversing the result.
%
% \DescribeMacro{\trimpath}
-% This trims off the specified amounts from the start and end of the
-% path. The amounts are two absolute dimensions, separated by commas.
-% They may be \TeX{} dimensions. If the first dimension is absent, it is
-% set to \texttt{0pt}, if the second is absent, it is set equal to the
-% first. (So if both are absent, nothing is trimmed.)
+% This trims off the specified absolute amounts from the start and end of
+% the path. The amounts can be specified as two absolute dimensions
+% separated by a comma, or a single dimension. In the second case, that
+% dimension is used at both the start and end.
+%
+% The implementation uses \cs{mfp@modi} which simply reads the argument
+% and writes it (via \cs{mfp@modmac}) as a set of parameters to the
+% appropriate \grafbase{} command. With \cs{trimpath}, however, we have to
+% parse that argument with \cs{mfp@getdimens}.
% \begin{macrocode}
- \newdef\cutoffafter##1{\@closmac{cutoffafter (##1)}}%
- \newdef\cutoffbefore##1{\@closmac{cutoffbefore (##1)}}%
- \newdef\trimpath##1{\@getdimens##1,,\mfp@delim
- \@closmac{trimmedpath (\the\mfp@scratch, \the\mfp@scratchB)}}%
- \newdef\@getdimens##1,##2,##3\mfp@delim{%
- \mfp@scratch \@ifmtsub{##1}{\z@}\relax
- \mfp@scratchB\@ifmtsub{##2}{\mfp@scratch}\relax}%
+ \newdef\cutoffafter {\mfp@modi{cutoffafter}}%
+ \newdef\cutoffbefore{\mfp@modi{cutoffbefore}}%
+ \newdef\trimpath##1{\mfp@getdimens##1,,\mfp@end
+ \mfp@modi{trimmedpath}\mfp@thedimens}%
% \end{macrocode}
%
% \DescribeMacro{\partpath}
-% \DescribeMacro{\subpath}
-% These produce a subpath of the following path. Each takes a mandatory
-% parameter containing two numbers separated by a comma. In
-% \cs{partpath}, these should be between 0 and 1 and produce a fraction
-% of the following path. In \cs{subpath}, these are absolute numbers between
-% 0 and the number of segments in the path, and it produces the equivalent of
-% \MF's \mfc{subpath} primitive.
+% This produces a subpath of the following path. Its mandatory argument
+% contains two numbers separated by a comma. The numbers should be between
+% 0 and 1 and produce a fraction of the following path.
+% \DescribeMacro{\subpath}\cs{subpath} has a similar argument, but the
+% numbers are between $0$ and the number of segments in the path, and it
+% produces the equivalent of \MF's \mfc{subpath} primitive.
% \begin{macrocode}
- \newdef\partpath##1{\@closmac{partialpath (##1)}}%
- \newdef\subpath##1{\@closmac{gsubpath (##1)}}%
+ \newdef\partpath{\mfp@modi{partialpath}}%
+ \newdef\subpath{\mfp@modi{gsubpath}}%
% \end{macrocode}
%
% \subsubsection{Drawing a curve}\label{drawing}
@@ -2753,33 +3608,82 @@
% dashed, colored, etc.
%
% \DescribeMacro{\draw}
-% \cs{draw} is the initial definition of \cs{@render}. It takes one
-% optional argument, a color. In \MF{}, where color are just numerics, if
-% it is ${}\ge 1$ (representing white) the path is subtracted from the
-% picture, otherwise it is drawn in black.
+% \cs{draw} takes one optional argument, a color. In \MF{}, where the
+% color commands return a numeric, if it is ${}\ge 1$ (representing white)
+% the path is subtracted from the picture, otherwise it is drawn in black
+% (the default). The default in \MP{} is \gbc{drawcolor}.
+%
+% We signal the use of the default color by passing a null optional
+% argument. Because several commands handle color the same way, and all
+% are rendering macros, we have a common macro that only needs the name of
+% the \grafbase{} command.
+% \begin{macrocode}
+ \newdef\draw{\mfp@nullopt{\mfp@clroptrend{drawn}}}%
+% \end{macrocode}
%
+% \DescribeMacro{\doubledraw}
+% Behaves something like \cs{draw}, except it draws a double line. It
+% takes two optional arguments; the first is the distance between the
+% centers of the two lines (default \gbc{2penwd}) and the second is a
+% color.
% \begin{macrocode}
- \newdef\draw{\null@opt\@draw}%
- \newdef\@draw[##1]{%
- \@rendmac{\mfp@ifempty{##1}{drawn}{colordrawn (##1)}}}%
+ \newdef\doubledraw{\mfp@defaultopt\mfp@doubledraw{2penwd}}%
% \end{macrocode}
%
% \DescribeMacro{\dashed}
-% \cs{dashed} (as well as \cs{dotted}) takes one optional argument,
-% which should contain the length of the dashes (diameter of dots) and
-% the length of the spaces between, separated by a comma. The defaults
-% can be seen in the code below.
+% \cs{dashed} and \cs{dotted} take one optional argument, which should
+% contain the length of the dashes (diameter of \DescribeMacro{\dotted}
+% dots) and the length of the spaces between, separated by a comma.
+%
+% Several rendering macros take an optional argument which is written as
+% the first parameter of a \grafbase{} command. Thus all are handled with
+% a single macro whose arguments are the name of the command and the
+% default for the optional argument.
% \begin{macrocode}
- \newdef\dashed{\simple@opt\@dashed{\the\dashlen,\the\dashspace}}%
- \newdef\@dashed##1{\@rendmac{DASHED (##1)}}%
- \newdef\dotted{\simple@opt\@dotted{\the\dotsize,\the\dotspace}}%
- \newdef\@dotted##1{\@rendmac{dotted (##1)}}%
+ \newdef\dashed{\mfp@optrendi{DASHED}{\the\dashlen,\the\dashspace}}%
+ \newdef\dotted{\mfp@optrendi{dotted}{\the\dotsize,\the\dotspace}}%
% \end{macrocode}
%
% \cs{gendashed} takes one mandatory argument, the name of a dash pattern
-% previously defined by \cs{dashpattern}
+% previously defined by \cs{dashpattern}. The implementing macro,
+% \cs{mfp@rendi}, is like the above, except it doesn't need to perform the
+% option processing.
+% \begin{macrocode}
+ \newdef\gendashed{\mfp@rendi{gendashed}}%
+% \end{macrocode}
+%
+% \DescribeMacro{\zigzag}
+% This draws a zigzag, crossing from one side of the following path to
+% the other. It has one required argument containing four dimensions,
+% \meta{dim$\sb1$} through \meta{dim$\sb4$}, separated by commas. What is
+% drawn depends on whether the path is closed or open. For an open path,
+% this draws the start of the path for a distance \meta{dim$\sb1$},
+% followed by zigzags, followed by the end of the path for a distance
+% \meta{dim$\sb2$}. For a closed path the first two dimensions are
+% ignored and the zigzags follow the entire path.
+%
+% The last two dimensions define the size and slant of each `zig'. If we
+% think of the zigzags as representing a periodic wave, \meta{dim$\sb3$}
+% is the wavelength and \meta{dim$\sb4$} is the amplitude (distance from
+% the true path to the sharp points).
+%
+% \DescribeMacro{\sinewave}
+% This is similar, but the result is a smooth sinewave shaped rendering.
+%
+% \DescribeMacro{\coil}
+% This creates a coil, or corkscrew-shaped rendering. In this case
+% \meta{dim$\sb3$} is the distance from one loop to the next and
+% \meta{dim$\sb4$} is the maximum distance to each side of the path.
+% An alias, \DescribeMacro{\corkscrew}\cs{corkscrew}, is provided.
+%
+% Both \cs{sinewave} and \cs{corkscrew} take an optional `tension'
+% argument that affects the smoothness of the result. The default is $1$
+% and the higher the value the closer they become to a zigzag.
% \begin{macrocode}
- \newdef\gendashed##1{\@rendmac{gendashed (##1)}}%
+ \newdef\zigzag{\mfp@rendi{zigzag}}%
+ \newdef\sinewave {\mfp@defaultopt{\mfp@wiggle{sinewave}}1}%
+ \newdef\coil{\mfp@defaultopt{\mfp@wiggle{corkscrew}}1}%
+ \newlet\corkscrew\coil
% \end{macrocode}
%
% \cs{plot} is like \cs{dotted}, except the dots are variously shaped
@@ -2788,167 +3692,184 @@
% argument, the name of the symbol. This can in principal be any
% expression of type \mfc{picture} or \mfc{path}. (In \MP, type
% \mfc{string} is also permitted). For the size parameter to work
-% correctly, the symbol should be a path, and it should have 1 as it's
-% natural size. \Mfpic{} provides the predefined paths
+% correctly, the symbol should be a path, and it should have 1
+% as it's natural size. \Mfpic{} provides the predefined paths
% \texttt{Triangle}, \texttt{Square}, \texttt{Circle}, \texttt{Star},
% \texttt{Diamond}, \texttt{Plus}, \texttt{Cross} and \texttt{Asterisk}.
% The first 5 have filled-in versions: \texttt{SolidTriangle}, etc.
+%
+% The utility \cs{mfp@optrendii} is similar \cs{mfp@optrendi} except it
+% takes an additional argument, in this case, the first mandatory argument
+% of \cs{plot}.
% \begin{macrocode}
- \newdef\plot{\simple@opt\@plot{\the\pointsize, \the\symbolspace}}%
- \newdef\@plot##1##2{\@rendmac{doplot (##2, ##1)}}%
+ \newdef\plot{\mfp@optrendii{doplot}{\the\pointsize,\the\symbolspace}}%
% \end{macrocode}
%
+% \DescribeMacro{\plotnodes}
% Sometimes, rather than equally spaced dots or symbols, we would rather
% have a symbol at each `node' of the following path. If the path is
% created by a list macro, then usually each point in the list is a node
-% (but the spline paths are exceptions). For other figures, the nodes
-% can seem pretty random. For example, a \cs{circle} command without optional
-% argument produces a curve with 8 equally spaced nodes, but with optional
-% argument the nodes depend on how it is drawn and the size of any angle
-% parameter.
+% (but some of the spline paths are exceptions). For other figures, the
+% nodes can seem pretty random. For example, a \cs{circle} command without
+% optional argument produces a curve with 8 equally spaced nodes, but with
+% optional argument the nodes depend on how it is drawn and the size of
+% any angle parameter.
%
% \cs{plotnodes} has almost the same syntax as \cs{plot}, but since it
% places the symbols only at the nodes of the path, there is no spacing
% parameter in the optional argument.
% \begin{macrocode}
- \newdef\plotnodes{\simple@opt\@plotnodes{\the\pointsize}}%
- \newdef\@plotnodes##1##2{\@rendmac{plotnodes (##2, ##1)}}%
+ \newdef\plotnodes{\mfp@optrendii{plotnodes}{\the\pointsize}}%
+% \end{macrocode}
+%
+% \DescribeMacro{\showcontrols}
+% Mainly for debugging, this places a symbol at the precontrol and
+% postcontrol of each node, and draws a line segment from each symbol to
+% the node. The names of two symbols are given in a mandatory argument,
+% separated by commas. As a special case, if either symbol is just the
+% number `\mfc{0}', it is not drawn. There is an optional argument for
+% the size of these symbols. If the size is zero, neither symbol is drawn.
+% That is useful if one only wants to see the line segments.
+% \begin{macrocode}
+ \newdef\showcontrols{\mfp@optrendii{showcontrols}{\the\pointsize}}%
% \end{macrocode}
%
% \subsubsection{Closing a curve}\label{closing}
%
+% There are several commands used to close paths (by prefixing them to
+% figure macros). The \grafbase{} commands that implement closing have a
+% common naming pattern. They all contain the string `\texttt{closed},
+% to which is added a distinguishing prefix. If the command takes a
+% tension argument, the letter `\texttt{t}' is appended. The corresponding
+% command without the `\texttt{t}' calls it with a default tension value.
+%
+% These macros all call \cs{mfp@close}, feeding it the proper prefix and
+% the tension value (empty if there is no tension option).
+%
% \DescribeMacro{\sclosed}
-% \DescribeMacro{\bclosed}
+% The first closes smoothly in the same manner that \cs{curve} creates a
+% smooth path. \DescribeMacro{\bclosed}The second uses an ordinary \MF{}
+% B\'ezier. These two have an optional argument: the amount of tension to
+% put in the connecting link. This makes no sense with the rest.
+%
% \DescribeMacro{\lclosed}
+% \cs{lclosed} always draws a straight line from the end of a path to
+% its start.
+%
% \DescribeMacro{\cbclosed}
-% \DescribeMacro{\qbclosed}
+% \cs{cbclosed} computes a closure by calculating cubic B-spline control
+% points from the path data and then generating a connecting spline (see
+% \cs{cspline}).
+% \DescribeMacro{\qbclosed}\cs{qbclosed} is similar, but quadratic
+% B-splines are used.
+%
% \DescribeMacro{\uclosed}
-% These close a path defined by whatever figure macro follows it. The
-% first closes smoothly in the same manner that \cs{curve} creates a
-% smooth path, the second uses an ordinary \MF{} B\'ezier. These two have
-% an optional argument: the amount of tension to put in the connecting
-% link. Higher tension produces a linking segment with sharper turns near
-% the endpoints and a flatter path in between. This makes no sense with
-% \cs{lclosed}, \cs{qbclosed} or \cs{cbclosed}. The first always draws a
-% straight line and the other two requires a link explicitly determined by
-% the beginning and ending segments of the path: a cubic B-spline for
-% \cs{cbclosed} and a quadratic B-spline for \cs{qbclosed}. See
-% \cs{closedcspline} and \cs{closedqspline} described later.
-%
-% Due to a misunderstanding on my part, \cs{uclosed} was defined in a more
-% complicated manner than necessary, and actually produced exactly the
-% same path as \cs{bclosed}. So now they are just made to be equal.
-% \begin{macrocode}
- \newdef\@closed##1##2{%
- \@closmac{##1closed\mfp@ifempty{##2}{}{t (##2)}}}%
- \newdef\bclosed{\null@opt\@bclosed}%
- \newdef\@bclosed[##1]{\@closed b{##1}}%
- \newdef\sclosed{\null@opt\@sclosed}%
- \newdef\@sclosed[##1]{\@closed s{##1}}%
- \newdef\lclosed{\@closed l{}}%
- \newdef\cbclosed{\@closed{cb}{}}%
- \newdef\qbclosed{\@closed{qb}{}}%
+% At one time \cs{uclosed} was defined in a more complicated manner than
+% necessary, but actually produced exactly the same path as \cs{bclosed}.
+% So now they are just made to be equal.
+% \begin{macrocode}
+ \newdef\bclosed{\mfp@nullopt{\mfp@close b}}%
+ \newdef\sclosed{\mfp@nullopt{\mfp@close s}}%
+ \newdef\lclosed{\mfp@close l{}}%
+ \newdef\cbclosed{\mfp@close{cb}{}}%
+ \newdef\qbclosed{\mfp@close{qb}{}}%
\newlet\uclosed\bclosed
% \end{macrocode}
%
+% \DescribeMacro{\makesector}
+% Takes a following arc and draws the sector (even if the center point
+% is not known). It really only works if prefixed to one of the
+% \cs{arc} commands, but it will attempt to create something even for
+% other paths. It calculates the center of the circle containing the arc
+% and connects that to the endpoints with two straight lines.
+% \begin{macrocode}
+ \newdef\makesector{\mfp@modmac{makesector}}%
+% \end{macrocode}
+%
% \subsubsection{Filling a closed curve}\label{filling}
%
% The next few prefix macros fill the interior of the closed path that
-% follows with some sort of pattern. The path must be either already
-% closed (like that produced by \cs{rect} or \cs{circle}) or explicitly
-% closed with a closing prefix. A non-closed path will simply be drawn
-% with a solid line.
+% follows with some sort of pattern. The path must be closed; if not,
+% the path will simply be drawn with a solid line.
%
% \DescribeMacro{\tess}
% \cs{tess} takes one mandatory argument, the name of a previously defined
-% tile, and fills with shifted copies of the tile.
+% tile (see subsection~\ref{composite}), and fills with shifted copies of
+% the tile.
% \begin{macrocode}
- \newdef\tess##1{\@rendmac{tess (##1)}}%
+ \newdef\tess{\mfp@rendi{tess}}%
% \end{macrocode}
%
% \DescribeMacro{\shade}
+% \cs{shade} is implemented in \MF{} by filling a region with a pattern
+% of dots, simulating gray. It take one optional parameter, the separation
+% between dots, defaulting to \cs{shadespace}. The size of the dots is
+% set by issuing the command \cs{shadewd}. In \MP{} a gray fill is
+% used but the syntax is the same. It would be better to just use
+% \cs{gfill} instead (described later).
+%
% \DescribeMacro{\polkadot}
-% \cs{shade} and \cs{polkadot} take one optional parameter, the separation
-% between dots, defaulting to \cs{shadespace} (resp., \cs{polkadotspace}).
-% The size of the dots is set by issuing the command \cs{shadewd} (resp.,
-% \cs{polkadotwd}).
+% This command is actually meant to fill with a pattern of large dots.
+% It also takes the separation between dots (default \cs{polkadotspace})
+% as an optional argument. The command \cs{polkadotwd} sets the size of
+% the dots.
% \begin{macrocode}
- \newdef\shade{\simple@opt\@shade{\the\shadespace}}%
- \newdef\@shade##1{\@rendmac{shade (##1)}}%
- \newdef\polkadot{\simple@opt\@polkadot{\the\polkadotspace}}%
- \newdef\@polkadot##1{\@rendmac{polkadot (##1)}}%
+ \newdef\shade{\mfp@optrendi{shade}{\the\shadespace}}%
+ \newdef\polkadot{\mfp@optrendi{polkadot}{\the\polkadotspace}}%
% \end{macrocode}
%
% \DescribeMacro{\thatch}
-% \DescribeMacro{\lhatch}
-% \DescribeMacro{\rhatch}
-% \DescribeMacro{\xhatch}
% The hatching macros mostly take two optional parameters, the
% separation of the hatching lines (default \cs{hatchspace}) and their
% color. One slight exception is \cs{thatch}, which is called by all the
% others. Its first optional argument is the separation \emph{and} the
% angle (default $0$) of the lines, separated by a comma. The other
-% hatching macros have a fixed angle. \cs{lhatch} has lines at a $-45$
-% degree angle (upper left to lower right), \cs{rhatch} produces the opposite
-% diagonal, and \cs{xhatch} does both sets of lines (cross hatching).
-% \begin{macrocode}
- \newdef\thatch{\simple@opt\@thatch{\the\hatchspace,0}}%
- \newdef\@thatch##1{\null@opt{\@@thatch{##1}}}%
- \newdef\@@thatch##1[##2]{\@rendmac{%
- \mfp@ifempty{##2}{thatch}{colorthatch (##2)} (##1)}}%
- \newdef\lhatch{\simple@opt\@lhatch{\the\hatchspace}}%
- \newdef\@lhatch##1{\@thatch{##1,-45}}%
- \newdef\rhatch{\simple@opt\@rhatch{\the\hatchspace}}%
- \newdef\@rhatch##1{\@thatch{##1,45}}%
- \newdef\xhatch{\simple@opt\@xhatch{\the\hatchspace}}%
- \newdef\@xhatch##1{\null@opt{\@@xhatch{##1}}}%
- \newdef\@@xhatch##1[##2]{\@rendmac{%
- \mfp@ifempty{##2}{xhatch}{colorxhatch (##2)} (##1)}}%
- \newlet\hatch=\xhatch
-% \end{macrocode}
-%
-% Commands that might conceivably interfere with small bits of \TeX{}
-% code in labels have been renamed. Thus, instead of \cs{fill}, which is a
-% \LaTeX{} text mode command, we have \cs{gfill}. We also have \cs{gclear}
-% and \cs{gclip}. The `g' is for `graphic'. Later, commands that deal
-% with text (labels and captions), have `t' prepended to their more
-% natural name, thus \cs{tlabel} and \cs{tcaption}.
+% hatching macros have a fixed angle.
+% \DescribeMacro{\lhatch}\cs{lhatch} has lines at a $-45$
+% degree angle (upper left to lower right),
+% \DescribeMacro{\rhatch}\cs{rhatch} produces the opposite
+% diagonal (angle $45$ degrees), and \DescribeMacro{\xhatch}\cs{xhatch}
+% does both sets of lines (cross hatching).
+% \begin{macrocode}
+ \newdef\thatch{\mfp@defaultopt\mfp@thatch{\the\hatchspace,0}}%
+ \newdef\lhatch{\mfp@defaultopt\mfp@lhatch{\the\hatchspace}}%
+ \newdef\rhatch{\mfp@defaultopt\mfp@rhatch{\the\hatchspace}}%
+ \newdef\xhatch{\mfp@defaultopt\mfp@xhatch{\the\hatchspace}}%
+ \newlet\hatch\xhatch
+% \end{macrocode}
%
% \DescribeMacro{\gfill}
-% \DescribeMacro{\gclear}
-% \DescribeMacro{\gclip}
% \cs{gfill} take one optional argument, the color with which to fill.
% Under \MF{} the color must be a numeric (representing a level of gray)
% or one of the color functions, which are defined in \file{grafbase.mf}
% to return numeric values.
%
+% \DescribeMacro{\gclear}
% \cs{gclear} will actually fill with the color \texttt{background} in \MP.
-% \cs{gclip} might seem to be the inverse of \cs{gclear} (clearing the
-% outside of a region), but in \MP{} the mechanism is completely
-% different. One wishes \MP{} had an `inverse clip' command to use
-% instead of filling with \gbc{background}. The figure macro following
-% these commands must be a closed path.
+% This merely covers what originally was there.
+%
+% \DescribeMacro{\gclip}
+% \cs{gclip} clears the outside of a region, but it is a true clip
+% operation, completely different from \cs{gclear}.
% \begin{macrocode}
- \newdef\gfill{\null@opt\@gfill}%
- \newdef\@gfill[##1]{%
- \@rendmac{\mfp@ifempty{##1}{filled}{colorfilled (##1)}}}%
- \newdef\gclear{\@rendmac{unfilled}}%
- \newdef\gclip{\@rendmac{Clip}}%
+ \newdef\gfill{\mfp@nullopt{\mfp@clroptrend{filled}}}%
+ \newdef\gclear{\mfp@rendmac{unfilled}}%
+ \newdef\gclip{\mfp@rendmac{Clip}}%
% \end{macrocode}
%
-% \subsubsection{Transforming a curve}\label{transforming}
+% \subsubsection{Modifying a curve}\label{transforming}
%
-% The following are transformation prefixes. They are all but one
-% implemented with \cs{@closmac} for reasons covered earlier. Most have a
-% mandatory argument, the parameters of the transformation. Because of the
-% implementation, one can insert rendering macros between a transformation
-% macro and the path being transformed, and they will apply to the
-% untransformed path.
+% The following are transformation prefixes. They are all (except
+% \cs{reverse}, for reasons covered in section~\ref{prefix}) implemented
+% with \cs{mfp@modmac}. Most have a mandatory argument, the parameters of
+% the transformation. Because of the implementation, one can insert
+% rendering macros between a transformation macro and the path being
+% transformed, and they will apply to the untransformed path.
%
-% If the \env{mfpic} environment has different x and y scales, the
+% If the \env{mfpic} environment has different $x$ and $y$ scales, the
% \cs{rotatepath} and \cs{reflectpath} compensates so that the result is
-% congruent to the original. The others do not because this is what users
-% seem to expect.
+% congruent to the original. This is what users seem to expect. The rest
+% do not compensate and users don't seem to mind.
% \begin{itemize}
% \item \cs{reverse} and \cs{xyswappath} have no argument.
% \item \cs{rotatepath} takes \marg{\meta{point},\meta{angle}}.
@@ -2966,68 +3887,159 @@
% \end{itemize}
%
% All of them simply write the \grafbase{} command of nearly the same
-% name.
-% \begin{macrocode}
- \newdef\reverse{\@modmac{reverse}}%
- \newdef\xyswappath{\@closmac{xyswappedpath}}%
- \newdef\rotatepath##1{\@closmac{rotatedpath (##1)}}%
- \newdef\shiftpath##1{\@closmac{shiftedpath (##1)}}%
- \newdef\scalepath##1{\@closmac{scaledpath (##1)}}%
- \newdef\xscalepath##1{\@closmac{xscaledpath (##1)}}%
- \newdef\yscalepath##1{\@closmac{yscaledpath (##1)}}%
- \newdef\slantpath##1{\@closmac{xslantedpath (##1)}}%
+% name, with the same arguments, via
+% \cs{mfp@modi}.\SpecialUsageIndex{\reverse}
+% \SpecialUsageIndex{\xyswappath}\SpecialUsageIndex{\rotatepath}
+% \SpecialUsageIndex{\shiftpath}\SpecialUsageIndex{\scalepath}
+% \SpecialUsageIndex{\xscalepath}\SpecialUsageIndex{\yscalepath}
+% \SpecialUsageIndex{\slantpath}\SpecialUsageIndex{\xslantpath}
+% \SpecialUsageIndex{\yslantpath}\SpecialUsageIndex{\reflectpath}
+% \SpecialUsageIndex{\transformpath}
+%
+% \begin{macrocode}
+ \newdef\reverse{\mfp@addmac{reverse}}%
+ \newdef\xyswappath{\mfp@modmac{xyswappedpath}}%
+ \newdef\rotatepath{\mfp@modi{rotatedpath}}%
+ \newdef\shiftpath{\mfp@modi{shiftedpath}}%
+ \newdef\scalepath{\mfp@modi{scaledpath}}%
+ \newdef\xscalepath{\mfp@modi{xscaledpath}}%
+ \newdef\yscalepath{\mfp@modi{yscaledpath}}%
+ \newdef\slantpath{\mfp@modi{xslantedpath}}%
\newlet\xslantpath\slantpath
- \newdef\yslantpath##1{\@closmac{yslantedpath (##1)}}%
- \newdef\reflectpath##1{\@closmac{reflectedpath (##1)}}%
- \newdef\transformpath##1{\@closmac{transformedpath (##1)}}%
-% \end{macrocode}
-%
-% \subsubsection{Appending an arrowhead}\label{arrowhead}
-%
-% \DescribeMacro{\arrow}
-% The four macros beginning \cs{@h} are assigned default values at the
-% start and changed only if an option calls for it.
-% Each option starts with a letter (The initial letters of
-% `backset', `color', `length', or `rotate'), followed by a value
-% to assign to the corresponding variable (defaulting to \mfc{0pt},
-% \meta{empty}, \cs{the}\cs{headlen} and \gbc{0 deg}). Then \cs{arrow}
-% finishes with the invokation of \cs{@arrow}.
-% \begin{macrocode}
- \def\arrow{\def\@hbackset{0}\def\@hcolour{}%
- \edef\@hlength{\the\headlen}\def\@hrotate{0}%
- \@arrow}%
-% \end{macrocode}
-%
-% \cs{@arrow} calls \cs{@arrowoption} if it finds an optional parameter.
-% After processing the parameter, \cs{@arrowoption} calls \cs{@arrow}
-% again. This loop will end when optional arguments run out. Then
-% \cs{@@arrow} finishes with the actual \MF{} code.
-% \begin{macrocode}
- \newdef\@arrow{\mfp@ifnextchar[{\@arrowoption}{\@@arrow}}%
- \newdef\@@arrow{%
- \@modmac{%
- \mfp@ifempty{\@hcolour}{headpath}{colorheadpath (\@hcolour)}%
- \space(\@hlength, \@hrotate, \@hbackset)}}%
-% \end{macrocode}
-%
-% Since the \cs{arrow} command takes up to 4 optional arguments in any
-% order, we have this utility \cs{@arrowoption} which examines an option's
-% contents and assigns a value to an appropriate macro. Since rather nasty
-% error messages will result if the contents are empty, we test for this
-% and do nothing when there is nothing to process. \cs{@@arrowoption}
-% makes the appropriated definitions, then \cs{@arrow} is iterated to
-% catch another optional parameter.
-% \begin{macrocode}
- \newdef\@arrowoption[##1]{%
- \mfp@ifempty{##1}{}{\@@arrowoption##1\mfp@delim}\@arrow}%
- \newdef\@@arrowoption##1##2\mfp@delim{%
- \if l##1\relax \mfp@ifempty{##2}{}{\def\@hlength{##2}}%
- \else\if r##1\relax \mfp@ifempty{##2}{}{\def\@hrotate{##2}}%
- \else\if b##1\relax \mfp@ifempty{##2}{}{\def\@hbackset{##2}}%
- \else\if c##1\relax \mfp@ifempty{##2}{}{\def\@hcolour{##2}}%
- \else
- \Mfpic@warn{Improper option to \string\arrow\@online .}%
- \fi\fi\fi\fi}%
+ \newdef\yslantpath{\mfp@modi{yslantedpath}}%
+ \newdef\reflectpath{\mfp@modi{reflectedpath}}%
+ \newdef\transformpath{\mfp@modi{transformedpath}}%
+% \end{macrocode}
+%
+% \DescribeMacro{\parallelpath}
+% This is not an affine transformation, but is a transformation of sorts.
+% It tries to define a curve that parallels the following curve at a fixed
+% distance. The mandatory argument is the separation, in graph units. It
+% is sort of experimental and will probably fail if the path that follows
+% is too wild. Since \cs{doubledraw} shares code with \cs{parallelpath},
+% that too will probably fail in similar circumstances.
+% \begin{macrocode}
+ \newdef\parallelpath{\mfp@modi{parapath}}%
+% \end{macrocode}
+%
+% \DescribeMacro{\randomizepath}
+% This, too, is a nonaffine transformation. It tries to define a curve
+% that remains smooth if the original is, but applies random shifts to the
+% nodes and random changes to the direction at each node.
+%
+% The only argument is mandatory and consists of a maximum shift (in graph
+% units) and a `weirdness' (a pure number) separated by a comma.
+%
+% The maximum shift can be a number, in which case it is the maximum
+% possible distance and there is no preferred direction, or it can be an
+% ordered pair, in which case it gives maximum horizontal and vertical
+% shift amounts.
+%
+% The `weirdness' is applied to path directions. If it is 0, there is no
+% change in directions of the path at nodes. If it is a positive number
+% $w$, a random angle of up to $\pm 30 w$ is added to the directions at
+% each node. Also a random scaling between $2^{-w}$ and $2^w$ is applied
+% to the control vectors. Also, if a path has a corner, the angle between
+% the two sides is randomly adjusted to be between $2^{-w}$ and $2^w$
+% times its original size.
+%
+% \DescribeMacro{\randomlines}
+% This is a simpler version whose mandatory argument contains only the
+% maximum shift parameter. It shifts the nodes and connects the results
+% with straight lines.
+% \begin{macrocode}
+ \newdef\randomizepath{\mfp@modi{randompath}}%
+ \newdef\randomlines{\mfp@modi{randomlines}}%
+% \end{macrocode}
+%
+% \DescribeMacro{\arccomplement}
+% This can precede an arc and then returns its complement. It starts at
+% the last point of the following arc and ends at the beginning of that
+% arc. The sense of the resulting arc (clockwise or anticlockwise) is the
+% same as the arc it is applied to. If the following path is not an arc,
+% the result is an arc that connects the end of the path to the beginning,
+% but may bear no obvious relation to that path.
+% \begin{macrocode}
+ \newdef\arccomplement{\mfp@modmac{arccomplement}}%
+% \end{macrocode}
+%
+% \subsubsection{Adding arrowheads (and tails)}\label{arrows}
+%
+% Paths can now have heads and tails and something placed in the
+% middle. Also the head, tail or the `something' can be any symbol. In
+% addition to the \cs{plotsymbol} possibilities, there is \gbc{Arrowhead},
+% which is the arrowhead of previous \mfpic{} versions, as well as
+% \gbc{Leftharpoon} and \gbc{Rightharpoon}, which are the left and right
+% halves.
+%
+% Intended for middle or tail of an arrow are \gbc{Crossbar}, \gbc{Leftbar}
+% and \gbc{Rightbar}, and for the tail there are \gbc{Lefthook} and
+% \gbc{Righthook}. \gbc{Crossbar} cuts right across the shaft
+% (perpendicular unless a rotation option is present) while \gbc{Leftbar}
+% extends to the left (from the point of view of someone facing in the
+% direction of the path. Similarly \gbc{Lefthook} curls to the left of the
+% arrow's shaft.
+%
+% All the new arrow commands take one mandatory argument, the name of the
+% symbol, followed by up to four optional arguments. Three of the optional
+% arguments are the same for all commands: these set the color, the size
+% and the amount of rotation of the symbol. Normally the symbol is
+% adjusted to `point' in the direction of the path (\gbc{Crossbar} is
+% actually perpendicular to it); with the rotation option it can be
+% adjusted.
+%
+% For example, \oarg{c red} makes the symbol red, \oarg{l 5pt} or \oarg{s
+% 5pt} scales the symbol so its nominal size or length is 5 points,
+% \oarg{r 10} rotates the symbol 10 degrees (anticlockwise) from its
+% default position.
+%
+% The other optional argument depends on the command: for heads it is an
+% amount to shift the head backward, for tails the amount to shift it
+% forward, and for things placed in the middle, it is the fraction of the
+% length of the path along which the symbol is placed.
+%
+% For example, \oarg{b 2pt} shifts a head two points backward, \oarg{f
+% 2pt} shifts a tail 2 points forward and \oarg{f .5} puts the symbol
+% right in the middle.
+%
+% The letters are mnemonics, being the initial letters of the words
+% `color', `length' or `size', `rotate', `back', and `forward' or
+% `fraction'. Actually, the letters \texttt{s} and \texttt{l} have
+% identical effects since I couldn't decide which mnemonic was more
+% natural. Oddly, \texttt{b} and \texttt{f} have identical effects also.
+% So if you accidentally use \oarg{b 2pt} for a tail, it will still be
+% shifted \emph{forward}! The reason for this is that these \mfpic{}
+% commands all correspond to \grafbase{} commands that have the same
+% argument structure. Both \texttt{b} and \texttt{f} simply cause the
+% accompanying value to be written into the fourth argument, while the
+% different commands use that argument differently. (You can shift a
+% tail backwards with a negative argument, if you really need that.)
+%
+% \DescribeMacro{\arrowhead}\SpecialUsageIndex{\arrow}
+% The \cs{arrowhead} command has a star-form which allows part of the
+% path beyond the tip of the arrowhead to be erased. The presence of a
+% star ultimately changes the \MF{} command passed on to later macros.
+% It does so by passing to \cs{mfp@arrowhead} the word `\texttt{true}' for
+% the star-form and `\texttt{false}' otherwise.
+%
+% The old \cs{arrow} command first checks for a star, and passes to
+% \cs{mfp@arrow} either an `\texttt{x}' or an empty parameter. This is
+% used to construct the \grafbase{} command name. We use \cs{def}
+% because \prog{eplain} defines one also.
+% \begin{macrocode}
+ \newdef\arrowhead{\mfp@ifstar\mfp@arrowhead\mfp@T\mfp@F}%
+ \def\arrow{\mfp@ifstar\mfp@arrow{x}{}}%
+% \end{macrocode}
+%
+% \DescribeMacro{\arrowtail}
+% Tails are just like heads except that there is no star-form and the
+% \grafbase{} command name (second parameter of \cs{mfp@arr}) is
+% different. And \DescribeMacro{\arrowmid}\cs{arrowmid} is just like
+% \cs{arrowtail} except for the command name and the different default for
+% the position optional argument.
+% \begin{macrocode}
+ \newdef\arrowtail##1{\mfp@arr 0{tailpath (##1)}}%
+ \newdef\arrowmid##1{\mfp@arr{0.5}{midpath (##1)}}%
% \end{macrocode}
%
% This ends the prefix macros.
@@ -3035,24 +4047,30 @@
% \subsection{Transforming the coordinate system}\label{coordinate}
%
% \DescribeMacro{\coords}
-% \DescribeMacro{\endcoords}
% The following apply transforms to the \MF{} coordinate system, and
-% these can be localized with \cs{coords} $\ldots$ \cs{endcoords}. The
-% transforms apply when the curve is \emph{rendered} and have no effect
-% on paths merely stored with \cs{store} and \cs{patharr}. Moreover, if a
-% path is created with \cs{connect}, it is transformed as a whole.
+% these can be localized with the \env{coords} environment. In plain
+% \TeX{} \cs{coords} starts the environment and
+% \DescribeMacro{\endcoords}\cs{endcoords} closes it. The transforms
+% apply when the curve is \emph{rendered} and do not affect what paths are
+% defined. Thus, for example, \cs{store} will store the same path
+% whatever the state of the coordinate system. In terms of \grafbase{}
+% commands, only \gbc{vtr} and \gbc{ztr} are changed, and these are only
+% applied when rendering.
%
% \DescribeMacro{\applyT}
% These are implemented by \cs{applyT} which takes as its argument a \MF{}
% `transformer'.
%
% These commands (except \cs{turn}, kept for backward compatibility) have
-% at most one argument, the parameters of the transformation. \cs{rotate}
-% rotates around the origin, \cs{rotatearound} rotates around a given
-% point, \cs{turn} takes the point as an optional argument.
+% no optional arguments, only mandatory arguments containing the
+% parameters of the transformation. \cs{rotate} rotates around the origin,
+% \cs{rotatearound} rotates around a given point, \cs{turn} takes the point
+% as an optional argument, defaulting to $(0,0)$.
%
% Transformations are applied in reverse of the order they appear in the
-% file (i.e., the most recent first).
+% file (i.e., the most recent first). This applies even if \env{coords}
+% environments are nested, except that at \cs{endcoords} the state at
+% the matching \cs{coords} is restored.
% \begin{macrocode}
\newdef\coords{\mfsrc{\@nl bcoords\@nl}}%
\newdef\endcoords{\mfsrc{\@nl ecoords\@nl}}%
@@ -3060,11 +4078,9 @@
\def\rotate##1{\applyT{rotated ##1}}%
\newdef\rotatearound##1##2{%
\applyT{rotatedaround (##1, ##2)}}%
- \newdef\turn{\simple@opt\@turn{(0,0)}}%
- \newdef\@turn##1##2{\rotatearound{##1}{##2}}%
- \newdef\reflectabout##1##2{%
- \applyT{reflectedabout (##1, ##2)}}% line ##1--##2.
- \let\mirror=\reflectabout
+ \newdef\turn{\mfp@defaultopt\mfp@turn{(0,0)}}%
+ \newdef\reflectabout##1##2{\applyT{reflectedabout (##1, ##2)}}%
+ \let\mirror\reflectabout
\newdef\shift##1{\applyT{shifted ##1}}% pair.
\def\scale##1{\applyT{scaled ##1}}% same scaling
\newdef\xscale##1{\applyT{xscaled ##1}}%
@@ -3079,28 +4095,24 @@
%
% \subsection{Axes and grids}\label{axes}
%
-% \DescribeMacro{\axes}
-% \DescribeMacro{\xaxis}
-% \DescribeMacro{\yaxis}
% An axis would normally not be thought of as a figure macro, because one
% would not want to position them arbitrarily nor transform them. One
% would also not want the hassle of having always to specify the origin,
% etc. However, one would want to occasionally make them dotted or dashed
% or colored.
%
+% \DescribeMacro{\axes}
% The original \cs{axes} was therefore not a figure macro. It always drew
-% both axes with solid lines. Later \cs{xaxis} and \cs{yaxis} were
-% introduced, which mimicked the behavior of \cs{axes}. For backward
-% compatibility, the old behavior is supported with the old commands. The
-% optional argument to \cs{axes}, \cs{xaxis} and \cs{yaxis} is the length
-% of the arrowhead, defaulting to \cs{the}\cs{axisheadlength}.
+% both axes with solid lines. \DescribeMacro{\xaxis}Later \cs{xaxis} and
+% \cs{yaxis} were introduced, which mimicked the behavior of \cs{axes}.
+% For backward \DescribeMacro{\yaxis}compatibility, the old behavior is
+% supported with the old commands. The optional argument to \cs{axes},
+% \cs{xaxis} and \cs{yaxis} is the length of the arrowhead, defaulting to
+% \cs{the}\cs{axisheadlen}.
% \begin{macrocode}
- \newdef\xaxis{\simple@opt\@xaxis{\the\axisheadlen}}%
- \newdef\yaxis{\simple@opt\@yaxis{\the\axisheadlen}}%
- \newdef\axes{\simple@opt\@axes{\the\axisheadlen}}%
- \newdef\@axes##1{\mfcmd{\@nl axes (##1)}}%
- \newdef\@xaxis##1{\mfcmd{\@nl xaxis (##1)}}%
- \newdef\@yaxis##1{\mfcmd{\@nl yaxis (##1)}}%
+ \newdef\xaxis{\mfp@defaultopt{\mfp@simple{xaxis}}{\the\axisheadlen}}%
+ \newdef\yaxis{\mfp@defaultopt{\mfp@simple{yaxis}}{\the\axisheadlen}}%
+ \newdef\axes{\mfp@defaultopt {\mfp@simple{axes}}{\the\axisheadlen}}%
% \end{macrocode}
%
% \DescribeMacro{\axis}
@@ -3108,59 +4120,51 @@
% create axes as figure macros. It also became clear there was so
% much common code that it would be better to implement the syntax
% \cs{axis}\marg{l}, etc., rather than have separate commands \cs{laxis},
-% etc. for all six possible axes. Therefore, only the x- and y-axes have
-% their own commands. The \cs{axis} command has one optional argument
+% etc. for all six possible axes. Therefore, only the $x$- and $y$-axes
+% have their own commands. The \cs{axis} command has one optional argument
% (the arrowhead length) and one mandatory argument, a single letter
% designating the axis to draw.
%
-% The only complication is that the x- and y-axis have one default head
-% length, \cs{axisheadlength} (initially \texttt{5pt}), while the side
-% axes have a different default, \cs{sideheadlength} (initially \texttt{0pt}).
-% Therefore we don't know which default to use until the mandatory argument
-% is read. In \cs{@@axis}, we use \cs{get@axisnum} to check this
-% mandatory argument and convert it to a number \cs{mfp@axisnum}, which is
-% used to select the head length.
+% \DescribeMacro{\axisline}The \cs{axisline} command is provided for users
+% who want more control. It is a figure macro that produces the
+% corresponding line, and the user can use all the flexibility of the
+% \cs{arrowhead} command if necessary.
% \begin{macrocode}
- \newdef\axis{\null@opt\@axis}%
- \newdef\@axis[##1]##2{\get@axisnum{##2}\axis
- \mfp@ifempty{##1}%
- {\edef\mfp@param{%
- \ifnum\mfp@axisnum<2 \the\axisheadlen
- \else \the\sideheadlen
- \fi}\@@axis{\mfp@param}}{\@@axis{##1}}{##2}}%
- \newdef\@@axis##1##2{\arrow[l##1]\@figmac{axisline.##2}}%
+ \newdef\axis{\mfp@nullopt\mfp@axis}%
+ \newdef\axisline##1{\mfp@figmac{axisline.##1}}%
% \end{macrocode}
%
% \DescribeMacro{\doaxes}
% \cs{doaxes} takes one optional argument, the arrowhead length, and one
% mandatory argument, all the axes to draw. These should be just letters
% with nothing separating them except optional spaces. \cs{doaxes} is
-% implemented via the self-looping macro \cs{do@axis}, which applies
-% one of two commands to the letter, depending on whether the optional
-% parameter was present (\cs{@axis}) or not (\cs{@@axis}).
+% implemented via the self-looping macro \cs{mfp@doaxis}.
+%
+% \DescribeMacro{\border}The \cs{border} command is almost equivalent to
+% \cs{doaxes}\marg{lbrt}, except it is a figure macro. With it, the
+% whole picture can be filled with a background color, or outlined in any
+% available rendering.
% \begin{macrocode}
- \newdef\doaxes{\null@opt\@doaxes}%
- \newdef\@doaxes[##1]##2{\def\mfp@axis{\@axis[##1]}%
- \do@axis##2\mfp@delim}%
- \newdef\do@axis##1{%
- \ifx\mfp@delim##1\relax\else\mfp@axis{##1}\@xp\do@axis\fi}%
+ \newdef\doaxes{\mfp@nullopt\mfp@doaxes}%
+ \newdef\border{\mfp@figmac{borderrect}}%
% \end{macrocode}
%
% \DescribeMacro{\axismarks}
-% \DescribeMacro{\xmarks}
-% \DescribeMacro{\ymarks}
-% \DescribeMacro{\lmarks}
-% \DescribeMacro{\bmarks}
-% \DescribeMacro{\rmarks}
-% \DescribeMacro{\tmarks}
% Hash marks (or tick marks) on the axes are provided by the
% \cs{axismarks} command (\cs{marks} was the original choice, but that
-% turned out to conflict with a primative of e\TeX). For brevity, therefore,
-% we also have separate commands \cs{xmarks}, etc. \cs{axismarks} has two
-% mandatory arguments: the letter for the axis, and a comma separated
-% list of numbers giving the positions on the axis. It functions as a list
-% macro (see previous discussion of types of macros) and so the list of
-% numbers can be prescribed by an external file
+% turned out to conflict with one of e\kern-.06em\TeX's primitives).
+% For brevity, therefore, we also have separate commands
+% \DescribeMacro{\xmarks}\cs{xmarks},
+% \cs{ymarks}\SpecialUsageIndex\ymarks,
+% \cs{lmarks}\SpecialUsageIndex\lmarks,
+% \cs{bmarks}\SpecialUsageIndex\bmarks,
+% \cs{rmarks}\SpecialUsageIndex\rmarks, and
+% \cs{tmarks}\SpecialUsageIndex\tmarks.
+%
+% \cs{axismarks} has two mandatory arguments: the letter for the axis, and
+% a comma separated list of numbers giving the positions on the axis. It
+% functions as a list macro (see previous discussion of types of macros)
+% and so the list of numbers can be prescribed by an external file
% \cs{datafile}\marg{\meta{file}}. One must be sure to issue an
% appropriate \cs{using} command to produce a numeric list in the output.
% This is the main reason for the \cs{usingnumericdefault} command.
@@ -3172,10 +4176,9 @@
% first argument (that is, the expectation that optional arguments come
% first is still satisfied). The optional argument is the length of the
% marks. The position of the marks is controlled separately by the
-% \cs{setaxismarks} command, described previously.
+% \cs{setaxismarks} command, described in section~\ref{othersettings}.
% \begin{macrocode}
- \newdef\axismarks##1{\simple@opt{\@marks{##1}}{\the\hashlen}}%
- \newdef\@marks##1##2{\mfsrc{\@nl ##1marks (##2)}\mfp@writedata}%
+ \newdef\axismarks##1{\mfp@defaultopt{\mfp@marks{##1}}{\the\hashlen}}%
\newdef\xmarks{\axismarks x}%
\newdef\ymarks{\axismarks y}%
\newdef\lmarks{\axismarks l}%
@@ -3185,73 +4188,95 @@
% \end{macrocode}
%
% \DescribeMacro{\grid}
-% \DescribeMacro{\gridpoints}
-% \DescribeMacro{\lattice}
-% The first one is, I think, inaccurately named: the word `grid' calls
-% to mind a set of lines. It functions in a similar way: allowing a
-% visualization of the coordinates, but it actually produces dots. The
-% dots are located at the points whose coordinates are integer multiples
-% of two numbers, which are given in the single mandatory argument
-% separated by commas. The optional first argument is the diameter of the
-% dots this defaults to \texttt{0.5bp} (hard coded in \file{grafbase}).
-% Because of the name, I have provided two aliases: \cs{gridpoints}
-% (for consistency with \cs{gridlines}, below) and \cs{lattice}.
+% The command \cs{grid} is, I think, inaccurately named: the word `grid'
+% suggests a set of lines, but this actually produces dots. Its function
+% is to allow a visualization of the coordinates. The dots are located at
+% the points whose coordinates are integer multiples of two numbers, which
+% are given in the single mandatory argument separated by commas. The
+% optional first argument is the diameter of the dots. This defaults to
+% \cs{griddotsize}, initially \texttt{0.5pt}.
+%
+% Because of my concerns about the name, I have provided two aliases:
+% \cs{gridpoints}\SpecialUsageIndex{\gridpoints} (for consistency with
+% \cs{gridlines}, below) and \cs{lattice}\SpecialUsageIndex{\lattice}.
%
% \DescribeMacro{\gridlines}
-% Macro \cs{gridlines} takes the same mandatory argument and draws
-% horizontal and vertical lines intersecting at all the dots that
-% \cs{grid} would draw. Some packages (and \ConTeXt) define a \cs{grid}
-% command so we use \cs{def} here instead of \cs{newdef} or \cs{redef}.
+% Macro \cs{gridlines} has no optional argument and takes the same
+% mandatory argument as \cs{grid}. It draws horizontal and vertical lines
+% intersecting at all the dots that \cs{grid} would draw. Some packages
+% (and \ConTeXt) define a \cs{grid} command so we use \cs{def} here
+% instead of \cs{newdef}.
%
% \DescribeMacro{\hgridlines}
+% This draws only the horizontal lines and its argument is the separation
+% (in graph units) between successive lines.
% \DescribeMacro{\vgridlines}
-% These two draw only the horizontal or vertical lines, and their
-% argument is the separation (in graph units) between successive lines.
+% \cs{vgridlines} is the vertical version.
+%
+% No arguments are shown because \cs{mfp@simple} or \cs{mfp@simpli} will
+% read what it needs.
% \begin{macrocode}
- \def\grid{\null@opt\@grid}%
- \newdef\@grid[##1]##2{%
- \mfcmd{\mfp@ifempty{##1}{grid}{vgrid (##1)} (##2)}}%
- \newlet\gridpoints=\grid \newlet\lattice=\grid
- \newdef\gridlines##1{\mfcmd{\@nl gridlines (##1)}}%
- \newdef\vgridlines##1{\mfcmd{\@nl vgridlines (##1)}}%
- \newdef\hgridlines##1{\mfcmd{\@nl hgridlines (##1)}}%
+ \def\grid{\mfp@defaultopt{\mfp@simplei{vargrid}}{\the\griddotsize}}%
+ \newlet\gridpoints\grid \newlet\lattice\grid
+ \newdef\gridlines{\mfp@simple{gridlines}}%
+ \newdef\vgridlines{\mfp@simple{vgridlines}}%
+ \newdef\hgridlines{\mfp@simple{hgridlines}}%
% \end{macrocode}
%
% \DescribeMacro{\plrgrid}
+% The command \cs{plrgrid} takes one mandatory argument which contains two
+% numbers separated by a comma. The first is a radius (in graph
+% coordinates) and the second an angle. It draws circular arcs at integer
+% multiples of the radius, and radial lines at integer multiples of the
+% angle. It fills the picture rectangle and is clipped to that rectangle.
+%
% \DescribeMacro{\plrgridpoints}
-% \DescribeMacro{\gridarcs}
-% \DescribeMacro{\gridrays}
+% \cs{plrgridpoints} is similar but places only dots at the intersections
+% of these arcs and rays. It takes an optional argument for the size of
+% the dots, default \cs{griddotsize}. \DescribeMacro{\gridarcs}\cs{gridarcs}
+% draws only the arcs. Its only parameter is the distance between them (in
+% graph units). \DescribeMacro{\gridrays}\cs{gridrays} draws only the rays
+% and its parameter is the angle separating the rays.
+%
% \DescribeMacro{\plrpatch}
-% The command \cs{plrgrid} takes one mandatory argument which contains two
-% numbers separated by a comma. The first is a radius and the second an
-% angle. It draws circular arcs at integer multiples of the radius, and
-% radial lines at integer multiples of the angle. It fills the
-% picture rectangle and is clipped to that rectangle. \cs{plrgridpoints}
-% is similar but places only dots at the intersections of these arcs and
-% rays. It takes an optional argument for the size of the dots, normally
-% \cs{dotsize}. \cs{gridarcs} draws only the arcs, \cs{gridrays} only the
-% rays. These last two take only one parameter each: the radius separating
-% the arcs, and the angle separating the rays.
-%
-% \cs{plrpatch} draws: a circular arc at a starting
-% radius, then increments that radius by a step size and draws another
-% arc, etc., and finally draws an arc at the ending radius. It does a similar
-% thing for radial lines and angles. It takes one mandatory argument,
-% which contains 6 numbers separated by commas: starting radius, ending
-% radius, step size, starting angle, ending angle, step size.
-% \begin{macrocode}
- \newdef\plrgrid##1{\mfcmd{\@nl polargrid (##1)}}%
- \newdef\plrgridpoints{\simple@opt\@plrgp{\the\dotsize}}%
- \newdef\@plrgp##1##2{\mfcmd{\@nl polargridpoints (##1, ##2)}}%
- \newdef\gridarcs##1{\mfcmd{\@nl gridarcs (##1)}}%
- \newdef\gridrays##1{\mfcmd{\@nl gridrays (##1)}}%
- \newdef\plrpatch##1{\mfcmd{\@nl polarpatch (##1)}}%
+% \cs{plrpatch} draws: a circular arc at a starting radius, then
+% increments that radius by a step size and draws another arc, etc., and
+% finally draws an arc at the ending radius. It does a similar thing for
+% radial lines and angles. It takes one mandatory argument, which contains
+% $6$ numbers separated by commas: starting radius, ending radius, radius
+% step, starting angle, ending angle, angle step.
+% \begin{macrocode}
+ \newdef\plrgrid{\mfp@simple{polargrid}}%
+ \newdef\plrgridpoints{%
+ \mfp@defaultopt{\mfp@simplei{polargridpoints}}{\the\griddotsize}}%
+ \newdef\gridarcs{\mfp@simple{gridarcs}}%
+ \newdef\gridrays{\mfp@simple{gridrays}}%
+ \newdef\plrpatch{\mfp@simple{polarpatch}}%
+% \end{macrocode}
+%
+% \DescribeMacro{\vectorfield}The \cs{vectorfield} command draws arrows to
+% represent a vector field. It takes one optional argument, the length of
+% the arrowhead (default \cs{headlen}), and three mandatory arguments. The
+% first is like \cs{grid}: it contains two numbers, separated by commas.
+% The arrows are located at the points whose coordinates are integer
+% multiples of two numbers. The second argument is the formula for the
+% vector field. It should consist of a pair expression in which the only
+% unknowns are the variables \texttt{x} and \texttt{y}. The last argument
+% should contain a boolean expression in which the only unknowns are
+% \texttt{x} and \texttt{y}. The macro steps through the relevant set of
+% points and draws the vector only if the boolean is true at that point.
+% The set of points considered includes only those inside the axis margins
+% (set by \cs{axismargin} and related commands). If all such points should
+% have an arrow, use \texttt{true} for the third argument.
+% \begin{macrocode}
+ \newdef\vectorfield{\mfp@defaultopt{\mfp@vectorfield{}}{\the\headlen}}
+ \newdef\plrvectorfield{%
+ \mfp@defaultopt{\mfp@vectorfield{plr}}{\the\headlen}}
% \end{macrocode}
%
% \subsection{Visualizing points}\label{points}
%
% \DescribeMacro{\point}
-% \DescribeMacro{\plotsymbol}
% \cs{point} draws a dot or a small circle. It takes one optional
% argument, the diameter of the dot, and one mandatory argument, the ordered
% pair (or comma separated list of them) at which to draw a dot. After
@@ -3260,6 +4285,7 @@
% grid macros, it is not a figure macro, but merely does its drawing and
% is not subject to any prefix macros.
%
+% \DescribeMacro{\plotsymbol}
% In order to mark points with other than circles, we have the
% \cs{plotsymbol} command, so-called because it uses the same symbols as
% the \cs{plot} command (previously discussed). There is an optional
@@ -3268,432 +4294,32 @@
% is a comma separated list of ordered pairs for the locations.
%
% For both these commands, the default optional argument is
-% \cs{the}\cs{pointsize}.
+% \cs{the}\cs{pointsize}. These are list macros: in place of a list of
+% points in braces, one can use the \cs{datafile} construct.
% \begin{macrocode}
- \newdef\point{\simple@opt\@point{\the\pointsize}}%
- \newdef\@point##1{%
- \mfsrc{\@nl pointd (##1, \ifpointfill\mf@T\else\mf@F\fi)}%
- \mfp@writedata}%
- \newdef\plotsymbol{\simple@opt\@plotsymbol{\the\pointsize}}%
- \newdef\@plotsymbol##1##2{%
- \mfsrc{\@nl plotsymbol (##2, ##1)}\mfp@writedata}%
+ \newdef\point{\mfp@defaultopt\mfp@point{\the\pointsize}}%
+ \newdef\plotsymbol{\mfp@defaultopt\mfp@plotsymbol{\the\pointsize}}%
% \end{macrocode}
%
% \DescribeMacro{\plottext}
-% In \MP{}, \TeX{} labels can be used in place of the symbols in
-% \cs{plotsymbol}. Moreover, multiple copies of text can be placed with
-% the \cs{tlabel} command (under \opt{mplabels}), as it supports a list of
-% points for the location parameter. For \MF{} we supply an alternative,
-% \cs{plottext}, which loops through the list of coordinates and issues
-% \cs{tlabel} commands. The text can be anything that works in \cs{tlabel}.
-%
-% It takes one optional argument, the justification of the text (as in the
-% \cs{tlabel command}, below---see also \cs{tlabeljustify}, above),
-% defaulting to \oarg{cc}. There are two mandatory arguments: the text,
-% and a comma separated list of ordered pairs. For consistency with
-% \cs{plotsymbol} (and because it works better), we now require the same
-% syntax for the points (i.e., no braces around individual points in the
-% list as are allowed in \cs{tlabel} commands).
-%
-% We start by handling the justification, resetting whitespace,
-% and loading the text into a token register. Then we read the list of
-% points with \cs{@@plottext}. Under \opt{mplabels} we process the whole
-% list with a single \cs{tlabel} command. Otherwise we prepare to loop
-% through the list of points. The \cs{begingroup} in \cs{@plottext} is
-% ended by the command called in \cs{@@plottext}.
-% \begin{macrocode}
- \newdef\plottext{\simple@opt\@plottext{cc}}%
- \newdef\@plottext##1{%
- \begingroup
- \mfp@resetwhitespace \tlabeljustify{##1}%
- \@ifmplabels{\@makeother\#}{}%
- \mfp@gettoks\@@plottext}%
- \newdef\@@plottext##1{%
- \@ifmplabels
- {\mfp@setMPpos \def\mfp@points{##1}\@@@tlabel}%
- {\@@@plottext##1,\mfp@delim}}%
-% \end{macrocode}
-% After reading the list of points and appending ending tokens, we
-% loop through them. The ending condition is that the next token is
-% \cs{mfp@delim}. Otherwise we read the coordinates as delimited macros,
-% and call \cs{@tlabel}, feeding it the coordinates and the text to
-% plot there (the \cs{begingroup} in \cs{x@plottext} matches the
-% \cs{endgroup} in \cs{@tlabel}). Then we recall \cs{@@@plottext} for
-% another loop. Since we have used two internal commands of \cs{tlabel}
-% code, we have to remember to check this if ever we rewrite \cs{tlabel}.
-% \begin{macrocode}
- \newdef\@@@plottext{%
- \mfp@ifnextchar\mfp@delim{\@firstoftwo\endgroup}{\x@plottext}}%
- \newdef\x@plottext(##1,##2),{\begingroup
- \@tlabel{##1}{##2}{\the\mfp@toks}\@@@plottext}%
-% \end{macrocode}
-%
-% \subsection{Figure macros}\label{figure}
-%
-% \subsubsection{Common geometric objects}\label{geometric}
-%
-% Finally we get to the real figure macros. These are implemented as
-% \MF{} paths that can be variously rendered, transformed, etc., with the
-% prefix macros.
-%
-% The first few (\cs{rect} through \cs{makesector}) are closed paths and
-% need not be prefixed with a closing macro before filling, etc.
-%
-% \DescribeMacro{\rect}
-% This takes one mandatory argument, the two diagonally opposite
-% corners of the rectangle separated by a comma, and draws the rectangle
-% with horizontal and vertical sides having these as two corners. The
-% direction of the path (which seldom makes a difference) is such that it
-% starts at the first specified point and moves horizontally first.
-% \begin{macrocode}
- \newdef\rect##1{\@figmac{rect (##1)}}%
-% \end{macrocode}
-%
-% \DescribeMacro{\regpolygon}
-% There are four arguments. The first is the number of sides. The second
-% is the name of a variable to represent the vertices. The other
-% two should be equations locating two vertices or the center and one
-% vertex. If the name used is \gbc{Ver}, then the vertices will be
-% \gbc{Ver1}, \gbc{Ver2}, etc., numbering anticlockwise. The center is
-% \gbc{Ver0}. Case is significant. After the command, the points can be
-% referred to in other \mfpic{} commands (except text label commands
-% when \opt{mplabels} is not in effect).
-% \begin{macrocode}
-\newdef\regpolygon##1##2##3##4{%
- \@figmac{regularpolygon(##1)(##2)(##3;##4;)}}%
-% \end{macrocode}
-%
-% \DescribeMacro{\circle}
-% This has one optional argument containing one of the letters
-% \texttt{t}, \texttt{p} (the default), \texttt{c}, or \texttt{s}. Its
-% purpose is to specify what kind of data is in the single mandatory
-% argument. These are a subset of the optional arguments that the \cs{arc}
-% commad (below) takes, and have almost the same meaning:
-% \begin{itemize}
-% \item \oarg{t}, the three point form needs three points (of course).
-% \item \oarg{p}, the polar form needs the center point and the radius.
-% This is the default when the optional argument is absent.
-% \item \oarg{c}, the center/point form needs the center and one point on
-% the circle.
-% \item \oarg{s}, the point-sweep form requires two points and the angle of
-% arc between them. The angle may be positive or negative. A
-% positive angle indicates the number of degrees anticlockwise from
-% the first point to the second. Negative means clockwise.
-% \end{itemize}
-%
-% Supplying \cs{arc} with the same parameters, or the appropriate superset
-% in the case of \oarg{p} and \oarg{c}, would produce a sub-arc of the
-% circle that \cs{circle} produces. This is the reason for giving
-% \cs{circle} these options: to make diagrams which show a circle and an
-% arc on it. (This can also be done with an ellipse by using a
-% transform on a circle and its arc.
-%
-% We use \cs{def} instead of \cs{newdef} or \cs{redef} because we know
-% \LaTeX{} has already defined a \cs{circle} command, but plain \TeX{} has
-% not. Instead of passing the default \texttt{p}, we pass a null
-% argument, causing the code to compare various letters to \cs{relax}
-% (always false) and then execute the default code.
-% \begin{macrocode}
- \def\circle{\null@opt\circl@}%
- \newdef\circl@[##1]##2{%
- \if t##1\relax \@figmac{circleppp (##2)}%
- \else\if c##1\relax \@figmac{circlecp (##2)}%
- \else\if s##1\relax \@figmac{circlepps (##2)}%
- \else \@figmac{circle (##2)}%
- \fi\fi\fi}%
-% \end{macrocode}
-%
-% \DescribeMacro{\ellipse}
-% \cs{ellipse} has one optional argument, an angle of rotation and one
-% mandatory argument, containing the center, the horizontal radius (before
-% rotation) and the vertical radius, separated by commas.
+% We keep \cs{plottext} for backward compatability. It is equivalent
+% to a \cs{tlabel} command with a different default for the justification
+% (\oarg{cc}) and the arguments rearranged. The order for \cs{plottext}
+% is: optional justification, then \TeX{} label, then list of points.
+% Because of the complication involved in making it work, in this case we
+% do not allow a datafile to replace the list of points. All this
+% command now does is perform a simplified version of the start of the
+% \cs{tlabel} command and jump to one of its internals, \cs{mfp@thelabel}.
% \begin{macrocode}
- \newdef\ellipse{\simple@opt\@ellipse{0}}%
- \newdef\@ellipse##1##2{\@figmac{ellipse (##2, ##1)}}%
-% \end{macrocode}
-%
-% \DescribeMacro{\sector}
-% This is the closed curve starting at the center of a circle, out
-% along a radius to the circumference of the circle, along an arc of the
-% circle, then back to the center. It has one mandatory argument
-% containing the center, the radius, and the two angles, separated by
-% commas.
-%
-% \DescribeMacro{\makesector}
-% Takes a following arc and draws the sector (even if the center point
-% is not known). It is really a prefix macro (closing an arc with two
-% straight lines), but the combination \cs{makesector}\cs{arc} acts like a
-% figure.
-% \begin{macrocode}
- \newdef\sector##1{\@figmac{sector (##1)}}%
- \newdef\makesector{\@closmac{makesector}}%
-% \end{macrocode}
-%
-% \DescribeMacro{\arc}
-% A circular arc can be specified in several ways and we support most of
-% them. \cs{arc} takes an optional argument: a letter to indicate which
-% specification. It has one mandatory argument containing the data for
-% that specification, separated by commas.
-%
-% The available letters and the corresponding required data are:
-% \begin{itemize}
-% \item \oarg{t}, the three point form needs three ordered pairs.
-% \item \oarg{p}, the polar form needs the center point, two angles and
-% the radius.
-% \item \oarg{a}, the alternative polar form needs the center, the radius
-% and two angles.
-% \item \oarg{c}, the center-point-sweep form needs the center, the
-% starting point, and the angle.
-% \item \oarg{s}, the point-sweep form requires the starting point, the
-% ending point and the angle.
-% \end{itemize}
-%
-% The default optional argument is \oarg{s}. (I don't know why. It
-% certainly doesn't seem the most natural. I would have picked \oarg{a}.)
-% Like \cs{circle}, an empty argument is passed by default.
-% \begin{macrocode}
- \newdef\arc{\null@opt\@arc}%
- \newdef\@arc[##1]##2{%
- \if t##1\relax \@figmac{arcppp (##2)}%
- \else\if p##1\relax \@figmac{arcplr (##2)}%
- \else\if a##1\relax \@figmac{arcalt (##2)}%
- \else\if c##1\relax \@figmac{arccps (##2)}%
- \else \@figmac{arcpps (##2)}%
- \fi\fi\fi\fi}%
-% \end{macrocode}
-%
-% \DescribeMacro{\plr}
-% \DescribeMacro{\midpoint}
-% A little utility to convert a list of pairs into another list of
-% pairs, where the given list are assumed to be polar coordinates and the
-% results are their Cartesian counterparts. And one to calculate the
-% midpoint, given two points separated by a comma.
-% \begin{macrocode}
- \newdef\plr##1{map (polar) (##1)}%
- \newdef\midpoint##1{(0.5[##1])}%
-% \end{macrocode}
-%
-% \subsubsection{List macros}\label{list}
-%
-% The next several figure macros are all list macros. They draw a path
-% determined by a list of points. The list can be explicitly given as a
-% mandatory argument containing a comma-separated list of ordered pairs,
-% or it can be taken from an external datafile. This is true for
-% \cs{polyline} through \cs{turtle}. These macros differ only in
-% how the points determine the path. (The splines don't actually connect
-% the points given, but rather use the points in a different way to
-% determine a curve.) The list of points is not an argument of any of
-% these. It should follow, and is picked up and written to output by the
-% ending code of \cs{@listmac}. This is basic to list macros, and will not
-% be repeated for each one.
-%
-% \DescribeMacro{\polyline}
-% \DescribeMacro{\polygon}
-% The most basic, a polyline or polygonal path, \cs{polyline}
-% draws line segments from each point to the next. \cs{lines} is an
-% alias. \cs{polygon} does the same, except it closes the path by
-% connecting the last point to the first.
-%
-% In \cs{@polyline}, the argument is \mfc{true} or \mfc{false},
-% indicating whether the path is to be closed.
-% \begin{macrocode}
- \newdef\polyline{\@polyline\mf@F}%
- \newlet\lines=\polyline
- \newdef\polygon{\@polyline\mf@T}%
- \newdef\@polyline##1{\@listmac{polyline (##1)}}%
-% \end{macrocode}
-%
-% \DescribeMacro{\curve}
-% \DescribeMacro{\cyclic}
-% \cs{curve} draws a smooth curve connecting a list of points. it arranges
-% for the curve, at point $p\sb n$ to be traveling parallel to the direction
-% from $p\sb {n-1}$ to $p\sb {n+1}$ (except at the first and last point).
-%
-% \cs{cyclic} is similar, except the curve is closed, and at the first
-% point it also travels parallel to the direction from the previous point
-% (the last one listed) to the next.
-%
-% There is an optional argument, which is the tension. This is a number
-% greater than .75. The larger it is, the closer to a polyline one gets.
-% The default is 1 (set in \file{grafbase}) or more generally, the value
-% set by \cs{settension}.
-% \begin{macrocode}
- \newdef\curve {\null@opt{\@curve\mf@F}}%
- \newdef\cyclic{\null@opt{\@curve\mf@T}}%
- \newdef\@curve##1[##2]{%
- \@listmac{\mfp@ifempty{##2}{curve}{tcurve (##2)} (##1)}}%
-% \end{macrocode}
-%
-% \DescribeMacro{\fcncurve}
-% A `function curve' is the graph of a function of $x$. It should always
-% travel left to right. This is not guaranteed by the \cs{curve} macro,
-% above, even if the x-coordinates are listed in order. \cs{fcncurve} is
-% guaranteed to do this if the list of points has the x-coordinates so
-% ordered.
-%
-% The optional argument is called tension. Internally, it is not quite the
-% same as the tension argument to \cs{curve}, but it also governs how
-% closely the curve stays to the corresponding polyline. If the optional
-% argument is missing, this command uses $1.2$ (more precisely, it uses
-% $1.2$ times the value set with \cs{settension}). The above guarantee is
-% voided if the value used is less than $1$. However, most curves will
-% satisfy the guarantee with some lower values.
-% \begin{macrocode}
- \newdef\fcncurve{\null@opt\@fcncurve}%
- \newdef\@fcncurve[##1]{%
- \@listmac{\mfp@ifempty{##1}{fcncurve}{functioncurve (##1)}}}%
-% \end{macrocode}
-%
-% \DescribeMacro{\qspline}
-% \DescribeMacro{\closedqspline}
-% \DescribeMacro{\cspline}
-% \DescribeMacro{\closedcspline}
-% These are supposed to be types of splines, about which I know only a
-% little. The list or datafile that should follow each command should
-% contain not the points passed through, but instead the control data.
-% For quadratic B-splines the points create the path as follows: for
-% successive points $p\sb i, p\sb {i+1}$ in the list, the path passes through
-% the midpoint of the line segment connecting $p\sb i$ and $p\sb {i+1}$, tangent
-% to that line segment.
-%
-% Cubic splines determine the curve as follows: for successive points
-% $p\sb i, p\sb {i+1}$ in the list, the line segment connecting them is divided
-% into thirds with two points $q\sb i$ and $q\sb i'$. The curve then passes
-% through the midpoint of the segment from $q\sb {i-1}'$ to $q\sb i$ and tangent
-% to that segment.
-%
-% `Control' is obtained as follows: the farther apart the points $p\sb i$
-% and $p\sb {i+1}$ (quadratic) or $q\sb {i-1}'$ and $q\sb i$ (cubic) the
-% less the curvature of the curve at the midpoint.
-%
-% The `\texttt{q}' versions produce quadratic splines, the `\texttt{c}'
-% versions produce cubic splines.
-% \begin{macrocode}
- \newdef\qspline{\@listmac{openqbs}}%
- \newdef\closedqspline{\@listmac{closedqbs}}%
- \newdef\cspline{\@listmac{opencbs}}%
- \newdef\closedcspline{\@listmac{closedcbs}}%
-% \end{macrocode}
-%
-% \DescribeMacro{\mfbezier}
-% \DescribeMacro{\closedmfbezier}
-% These produce the standard \MF{} cubic B\'ezier (the connection is
-% \mfc{..} unless the optional argument is used) with tension given by
-% an optional argument. A mandatory argument (not shown) contains the
-% list of points.
-%
-% \DescribeMacro{qbeziers}
-% \DescribeMacro{closedqbeziers}
-% These produce the equivalent of a sequence of \LaTeX{} \cs{qbezier}
-% commands. The mandatory argument will be a list of points alternating
-% between nodes and control points. Note that quadratic B\'eziers need
-% not be smooth at the nodes unless the control points line up. Our
-% plural name distinguishes it from the \LaTeX{} command.
-% \begin{macrocode}
- \newdef\mfbezier{\null@opt{\@mfbezier\mf@F}}%
- \newdef\closedmfbezier{\null@opt{\@mfbezier\mf@T}}%
- \newdef\@mfbezier##1[##2]{%
- \@listmac{\mfp@ifempty{##2}{bezier}{tbezier (##2)} (##1)}}%
- \newdef\qbeziers{\@listmac{qbezier (\mf@F)}}%
- \newdef\closedqbeziers{\@listmac{qbezier (\mf@T)}}%
-% \end{macrocode}
-%
-% \DescribeMacro{\turtle}
-% Many years ago there was a tool for teaching programming concepts to
-% young children. They could draw a figure on a screen by programming a
-% `turtle' with a sequence of turns and distances. The following is
-% similar. It has a mandatory argument containing a list of ordered pairs.
-% The first is the coordinates of the starting point, and each of the rest
-% is interpreted as a displacement from the current position, along which
-% a line segment is to be drawn. The terminal point of that segment is the
-% new current position
-% \begin{macrocode}
- \newdef\turtle{\@listmac{turtle}}%
-% \end{macrocode}
-%
-% \subsubsection{Graphing functions}\label{functions}
-%
-% \DescribeMacro{\function}
-% \prog{Mfpic} provides the ability to plot the graph of any function that
-% can be defined in \MF. This is done with the \cs{function} command, a
-% figure macro which takes one optional parameter that selects
-% the smoothness of the \MF{} path produced. It can be \oarg{p}, which
-% draws a polyline path through selected points (much like \cs{polyline}),
-% or \oarg{s\meta{tension}} which selects a smooth path (much like
-% \cs{curve}\oarg{\meta{tension}}). The tension may be omitted and then
-% defaults to 1 (set in \file{grafbase}). The default is \oarg{s}.
-%
-% There are two mandatory arguments. The second is the function, which
-% must be written as a formula (strictly speaking, a \MF{} numerical
-% expression) containing only one variable, \texttt{x}. The first
-% mandatory argument contains three numbers separated by commas. They are,
-% in order, the starting value of \texttt{x}, the ending value, and the
-% step size.
-%
-% \DescribeMacro{\parafcn}
-% \cs{parafcn} is similar, except its formula must be an ordered pair of
-% formulas (strictly speaking, a \MF{} pair expression) in which
-% \texttt{t} is the only variable.
-%
-% \DescribeMacro{\plrfcn}
-% \cs{plrfcn} is similar, except its argument is a formula (a \MF{}
-% numeric expression) representing the radius, in which \texttt{t} is the
-% only variable, interpreted as the angle coordinate, $\theta$, in degrees.
-%
-% These three commands are so alike that all they do is set the name to
-% be written to the output and call the common code.
-% \begin{macrocode}
- \newdef\function{\def\mfp@name{function}\mfp@fcn}%
- \newdef\parafcn{\def\mfp@name{parafcn}\mfp@fcn}%
- \newdef\plrfcn{\def\mfp@name{plrfcn}\mfp@fcn}%
- \newdef\mfp@fcn{\simple@opt\@mfp@fcn{s}}%
- \newdef\@mfp@fcn##1{\@@mfp@fcn##1\mfp@delim}%
- \newdef\@@mfp@fcn##1##2\mfp@delim##3##4{%
- \@figmac{\mfp@ifempty{##2}{}{t}\mfp@name\space
- (\if p##1\mf@F\else\mf@T\fi \mfp@ifempty{##2}{}{, ##2})
- (##3) (##4)}}%
-% \end{macrocode}
-%
-% \DescribeMacro{\btwnfcn}
-% The region between two functions \cs{btwnfcn} is a figure macro. It is
-% mostly intended for shading or other filling operations so it is already
-% closed and, by default, it is not `smooth'. Its parameters are the
-% same as those of \cs{function} (above), with two exceptions: the default
-% optional argument is \oarg{p} and it has an additional mandatory
-% argument, the other function. The resulting \MF{} path travels along one
-% the graph of the first function from the starting $x$ to the ending $x$,
-% thence vertically to the graph of the second function, along it (in
-% reverse) to the starting $x$ and finally, vertically to the start of the
-% first function's graph.
-%
-% \DescribeMacro{\plrregion}
-% The figure macro \cs{plrregion} also produces a closed \MF{} path. It
-% has the same parameters as \cs{plrfcn}, and the path produced begins at
-% the origin, travels along a straight line in the direction of the
-% starting angle to the beginning of the graph of the expression, along
-% that graph to the ending angle and then straight back to the origin. Its
-% default option is also \oarg{p}.
-% \begin{macrocode}
- \newdef\btwnfcn{\simple@opt\@btwnfcn{p}}%
- \newdef\@btwnfcn##1{\@@btwnfcn##1\mfp@delim}%
- \newdef\@@btwnfcn##1##2\mfp@delim##3##4##5{\btwn@params{##1}{##2}%
- \@figmac{\t@pref btwnfcn (\s@vals) (##3) (##4) (##5)}}%
- \newdef\btwn@params##1##2{%
- \edef\s@vals{\if s##1\mf@T\else\mf@F\fi}%
- \mfp@ifempty{##2}{\def\t@pref{}}%
- {\def\t@pref{t}\edef\s@vals{\s@vals, ##2}}}%
- \newdef\plrregion{\simple@opt\@plrregion{p}}%
- \newdef\@plrregion##1{\@@plrregion##1\mfp@delim}%
- \newdef\@@plrregion##1##2\mfp@delim##3##4{\btwn@params{##1}{##2}%
- \@figmac{((0,0)--(\t@pref plrfcn (\s@vals) (##3) (##4))%
- --cycle)}}%
+ \newdef\plottext{\begingroup\mfp@resetwhitespace\mp@mksharpother
+ \mfp@defaultopt\mfp@plottext{cc}}%
% \end{macrocode}
%
% \subsection{Some composite objects}\label{composite}
%
% \DescribeMacro{\tile}
% A tile is syntactically a \MF{} picture variable. The commands between
-% \cs{tile} (or \cs{begin}\marg{tile}) and \cs{endtile} (or
+% \cs{tile} and \cs{endtile} (or \cs{begin}\marg{tile} and
% \cs{end}\marg{tile}) should be any sequence of \mfpic{} figure macros
% with possible prefix macros. They will be drawn not on the main picture
% (\gbc{currentpicture}), but on the picture given by the name in the
@@ -3707,52 +4333,51 @@
% tile should be clipped to these dimensions (true) or allowed to extend
% beyond them (false).
% \begin{macrocode}
- \newdef\tile##1{\mfcmd{\@nl tile (##1)}}%
- \newdef\endtile{\mfsrc{endtile;\@nl}}%
+ \newdef\tile{\mfp@simple{tile}}%
+ \newdef\endtile{\mfcmd{\@nl endtile}}%
% \end{macrocode}
%
% \DescribeMacro{\patharr}
-% A path array is a set of \MF{} path variables each having the same name
-% followed by a different numeric suffix. This command turns off rendering
-% and simply assigns these variables in order to the path expressions
-% written by the figure macros between \cs{patharr} and \cs{endpatharr}.
-% It works by redefining \cs{store@path}, issued by all the figure
-% macros. These variables can be accessed in \mfpic{} by using the
-% \cs{mfobj} command.
+% A path array is a set of \MF{} path variables each having the same base
+% name followed by a different numeric suffix. This command turns off
+% rendering and simply assigns these variables in order to the path
+% expressions written by the figure macros between \cs{patharr} and
+% \cs{endpatharr}. It works by redefining \cs{mfp@storepath}, issued by all
+% the figure macros. These variables can be accessed in \mfpic{} by using
+% the \cs{mfobj} command. The name of the path array \emph{without} a
+% numeric suffix is a \MF{} numeric variable whose value is the number of
+% array elements.
%
% \emph{Explicit} rendering commands will work in the sense that the
% affected path will be rendered in addition to being assigned to the
-% variable. The only parameter is the common name of the variables.
+% variable. The only parameter is the common base name of the variables.
% \begin{macrocode}
\newdef\patharr##1{\begingroup
\mfsrc{hide(numeric ##1; path ##1[]; ##1 = 0)}%
- \def\store@path{\mfsrc{\@nl store (##1[incr ##1])}}%
+ \def\mfp@storepath{\mfsrc{\@nl store (##1[incr ##1])}}%
\setrender{}}%
\newdef\endpatharr{\endgroup}%
% \end{macrocode}
%
% \DescribeMacro{\connect}
+% This starts an environment that can contain any sequence of figure
+% macros. The environment functions as a single large \cs{mfp@figmac}, the
+% figure being the path obtained by connecting all the paths
% \DescribeMacro{\endconnect}
-% These define an environment that can contain any sequence of figure
-% macros. It functions as a single large \cs{@figmac}, the figure being
-% the path obtained by connecting all the paths within by straight lines
-% from the end of one to the beginning of the next. It is implemented via
-% a path array, so the individual figures within the environment have
-% implicit rendering turned off. Explicit rendering commands within the
+% (up to the following \cs{endconnect} command) by straight lines from the
+% end of one to the beginning of the next. It is implemented via a path
+% array, so the individual figures within the environment have implicit
+% rendering turned off. Explicit rendering commands within the
% environment will have the expected effect.
%
-% The \cs{if@mfp@latex} stuff at the end allows \cs{connect} to be used as
+% The \cs{mfp@checklatexenv} at the end allows \cs{connect} to be used as
% a \LaTeX{} environment.
% \begin{macrocode}
- \newdef\connect{\@figmac{begingroup; save nexus}%
- \patharr{nexus}}%
+ \newdef\connect{\mfp@figmac{begingroup; save mfpicnexus}%
+ \patharr{mfpicnexus}}%
\newdef\endconnect{\endpatharr
- \mfcmd{\@nl mkpoly (\mf@F, nexus)\@nl endgroup}%
- \if@mfp@latex \def\mfptmp@a{connect}%
- \ifx\mfptmp@a\@currenvir
- \aftergroup\@startfigtrue %\aftergroup\@imrendtrue
- \fi
- \fi}%
+ \mfcmd{\@nl mkpoly (\mfp@F, mfpicnexus)\@nl endgroup}%
+ \mfp@checklatexenv{connect}{\aftergroup\@mfpstarttrue}{}}%
% \end{macrocode}
%
% \subsection{Other graphical features}\label{otherfeatures}
@@ -3761,33 +4386,44 @@
% (or bar graphs, or histograms).
%
% \DescribeMacro{\piechart}
-% The \cs{piechart} command takes one optional argument containing a
-% letter and an angle, and one mandatory argument containing the data. The
-% letter can be `\texttt{c}' for clockwise or `\texttt{a}' for anticlockwise
-% and determines the direction around the pie the different wedges will be
-% drawn. The angle determines the angle of the first edge (in the given
-% direction) of the first wedge. The default is \oarg{c90} which starts
-% the first wedge at 12 o'clock and proceeds clockwise from there. The
-% data in the mandatory argument are numbers separated by commas. The size
-% of a wedge is proportional to the size of the corresponding number.
-% \cs{piechart} is a list macro and can take the data from an external
-% file.
+% Actually, nothing is drawn by the \cs{piechart} command; it only defines
+% a path array and a few other internal variables. Since nothing is
+% drawn by this command, it makes sense to allow it outside the drawing
+% environment, so it has been made available outside \env{mfpic}
+% environments with the command name \cs{mfppiechart}(page
+% \pageref{charts}).
+%
+% The \cs{piechart} command takes one optional argument and two mandatory
+% argument. The first mandatory argument contains the center and radius of
+% the chart separated by a comma; the second is the list of data. The
+% optional argument contains a letter and an optional angle. The letter
+% can be `\texttt{c}' for clockwise or `\texttt{a}' for anticlockwise and
+% determines the direction around the pie the different wedges will be drawn.
+% The angle determines the angle of the first edge (in the given
+% direction) of the first wedge. The default letter is \texttt{c} and the
+% default angle is $90$ degrees, which starts the first wedge at
+% 12~o'clock and proceeds from there either clockwise or anticlockwise..
+%
+% \cs{piechart} acts like a list macro in that the above arguments should
+% be followed by either a list of numbers in braces or a \cs{datafile}
+% command (with numbers in a datafile). If this feature is used, an
+% appropriate \cs{using} command is needed. If the numbers are listed one
+% per line, then \cs{usingnumericdefault} should work.
%
% \DescribeMacro{\piewedge}
-% Actually, nothing is drawn, it only defines a path array and a few
-% other internal variables. Each individual wedge (elements of the just
-% mentioned array) may be drawn using the \cs{piewedge} command, which is
-% a figure macro. That command takes one optional argument that determines
-% how the wedge is drawn, as described below, and one mandatory argument,
-% the number of the wedge. Without the optional argument,
-% \cs{piewedge}\marg{1} draws the first wedge in the position given by the
-% arguments of the \cs{piechart} command.
+% Each individual wedge (elements of the above mentioned path array) may
+% be drawn using the \cs{piewedge} command, which is a figure macro. That
+% command takes one optional argument that determines how the wedge is
+% drawn (described below) and one mandatory argument, the number of the
+% wedge. Without the optional argument, \cs{piewedge}\marg{1} draws the
+% first wedge in the position given by the arguments of the \cs{piechart}
+% command, as described above.
%
% The optional arguments can be:
% \begin{itemize}
-% \item \oarg{x\meta{distance}}, the wedge is exploded, drawn
-% shifted outward from its position by \meta{distance} (in graph
-% units).
+% \item \oarg{x\meta{distance}}, the wedge is `exploded': drawn
+% shifted radially outward from the center of the pie chart by
+% \meta{distance} (in graph units).
% \item \oarg{s(\meta{dx},\meta{dy})}, the wedge is shifted from its
% position by \meta{dx} horizontally and \meta{dy} vertically (in
% graph units).
@@ -3795,80 +4431,513 @@
% point is at coordinates \parg{\meta{x},\meta{y}}.
% \end{itemize}
% \begin{macrocode}
- \newdef\piechart{\simple@opt\@piechart{c}}%
- \newdef\@piechart##1{\@@piechart##1\mfp@delim}%
- \newdef\@@piechart##1##2\mfp@delim##3{%
- \mfsrc{\@nl piechart(\if a##1 \else-\fi1, %
- \mfp@mtsub{##2}{90}, ##3)}\mfp@writedata}%
- \newdef\piewedge{\simple@opt\@piewedge{d}}%
- \newdef\@piewedge##1{\@@piewedge##1\mfp@delim}%
- \newdef\@@piewedge##1##2\mfp@delim##3{%
- \@figmac{(piewedge[##3]%
- \if x##1 shifted (##2*piedirection[##3])%
- \else\if s##1 shifted (##2)%
- \else\if m##1 shifted (##2 - piecenter)%
- \else
- \fi\fi\fi)}}%
+ \newlet\piechart\mfppiechart
+ \newdef\piewedge{\mfp@defaultopt\mfp@piewedge{d}}%
% \end{macrocode}
%
% \DescribeMacro{\barchart}
-% \DescribeMacro{\bargraph}
-% \DescribeMacro{\gantt}
-% \DescribeMacro{\histogram}
-% The \cs{barchart} command takes one optional and two mandatory
-% arguments. The last mandatory argument contains the data, separated by
-% commas and these determine the length of the bars in graph units. This
-% is also a list macro and instead of the list, an external file can hold
-% the data.
+% As with \cs{piechart}, the \cs{barchart} command does not actually draw
+% anything, but defines a path array, so we make available a version for
+% use outside \cs{mfpic} environments: \cs{mfpbarchart} (page
+% \pageref{charts}).
+%
+% The \cs{barchart} command takes one optional and a mandatory
+% arguments. The data follow the mandatory argument. Like \cs{piechart},
+% the data can be a list in braces or an external file. The data determine
+% the length of the bars in graph units. Since version 0.7, the data can
+% be a list of numbers or pairs or both. It is difficult to give a
+% \cs{using} specification that allows both in an external file, but a
+% list in braces is simply written as is and \MF{} has no problems mixing
+% them.
+%
+% Pairs are interpreted as giving the start and end of a bar. Numbers are
+% interpreted as giving only the end, with the start at $0$. If a datafile
+% is used, an appropriate \cs{using} command should be issued beforehand.
+% In that case probably all the data should be pairs or all numeric.
%
% The optional argument determines the position and width of the bars and
% the first mandatory argument determines the orientation. The mandatory
-% argument can be \marg{v} for vertical bars with base on the x-axis, or
-% \marg{h} for horizontal bars with base on the y-axis.
+% argument can be \marg{v} for vertical bars with base on the $x$ axis, or
+% \marg{h} for horizontal bars with base on the $y$ axis.
% The optional argument contains three numbers separated by commas. The
% first is the coordinate of the leading edge of the first bar, the second
% is the distance (in graph coordinates) between the leading edge of one
% bar and that of the next, and the third is the fraction of that distance
% that is occupied by the bar. The default is \oarg{0,1,1}.
%
-% We provide the aliases \cs{bargraph} and \cs{histogram}.
+% We provide the aliases \cs{bargraph}, \cs{histogram} and \cs{gantt}.
%
% \DescribeMacro{\chartbar}
-% \DescribeMacro{\ganttbar}
-% \DescribeMacro{\graphbar}
-% \DescribeMacro{\histobar}
-% The \cs{barchart} command does not actually draw anything, but defines a
-% path array. The \cs{chartbar} command can then be used to draw a bar
-% (one of the members of the just mentioned array). It is a figure macro
-% and take the number of the bar as its mandatory argument. Aliases
-% \cs{graphbar} and \cs{histobar} are provided. We avoid \cs{bar} because
-% it is a math accent command.
-% \begin{macrocode}
- \newdef\barchart{\simple@opt\@barchart{0,1,1}}%
- \newdef\@barchart##1##2{%
- \mfsrc{\@nl barchart (##1, \if h##2\mf@F\else\mf@T\fi)}%
- \mfp@writedata}%
- \newlet\bargraph=\barchart
- \newlet\histogram=\barchart
+% The \cs{chartbar} command can then be used to draw a bar (one of the
+% members of the just mentioned array). It is a figure macro and take the
+% number of the bar as its mandatory argument. Aliases \cs{graphbar},
+% \cs{histobar} and \cs{ganttbar} are provided. We avoid \cs{bar} because
+% it is already a math accent command. This ends \cs{mfp@grafmacs}.
+% \begin{macrocode}
+ \newlet\barchart\mfpbarchart
+ \newlet\bargraph\barchart
+ \newlet\histogram\barchart
\newlet\gantt\barchart
- \newdef\chartbar##1{\@figmac{chartbar[##1]}}%
- \newlet\graphbar=\chartbar
- \newlet\histobar=\chartbar
- \newlet\ganttbar=\chartbar
+ \newdef\chartbar##1{\mfp@figmac{chartbar[##1]}}%
+ \newlet\graphbar\chartbar
+ \newlet\histobar\chartbar
+ \newlet\ganttbar\chartbar
+}% end \mfp@grafmacs
+% \end{macrocode}
+%
+% \DescribeMacro{\mfppiechart}
+% We make \cs{mfppiechart} and \cs{mfpbarchart}\label{charts} global,
+% since all they do is define variables \DescribeMacro{\mfpbarchart}
+% and arrays.
+% \begin{macrocode}
+\newdef\mfppiechart{\mfp@defaultopt\mfp@piechart{c}}%
+\newdef\mfpbarchart{\mfp@defaultopt\mfp@barchart{0,1,1}}%
+\newlet\mfpbargraph\mfpbarchart
+\newlet\mfphistogram\mfpbarchart
+\newlet\mfpgantt\mfpbarchart
+% \end{macrocode}
+%
+% \section{Implementing the graphics macros}
+%
+% To keep \cs{mfp@grafmacs} from becoming unweildy, we define here all the
+% internal commands that are called by the user level macros. We've
+% given most of them names that begin \cs{mfp} to keep from overwriting
+% other package commands
+%
+% \cs{arc} and \cs{circle} are defined identically except that the first
+% sets a switch to true, the second to false. This selects the spelling of
+% the corresponding \grafbase{} command and the value of the first
+% argument of \gbc{fallbackpath}. Both \cs{arc} and \cs{circle} then
+% call \cs{mfp@arccircle} which takes the optional argument as \arg1,
+% and the data required for that option as \arg2. Actually, \cs{arc}
+% first checks for a * and then calls \cs{mfp@arc}, which follows the
+% above description.
+%
+% The \gbc{fallbackpath} is a $0$-length path at whatever point is listed
+% first in \arg2, it is cyclic in the case of a circle (not that it
+% matters for a one-point path).
+% \begin{macrocode}
+\newdef\mfp@arc{\mfp@switchtrue\mfp@defaultopt\mfp@arccircle{s}}%
+\newdef\arc@or@circle{\ifmfp@switch arc\else circle\fi}%
+\newdef\mfp@arccircle#1#2{%
+ \@ifundefined{mfp@arc@#1}%
+ {\Mfpic@warn{Invalid option; ignoring \mfp@b\arc@or@circle\space
+ command.}%
+ \mfp@figmac{fallbackpath(\ifmfp@switch\mfp@F\else\mfp@T\fi) (#2)}}%
+ {\mfp@figmac{\arc@or@circle\@nameuse{mfp@arc@#1} (#2)}}}%
+\newdef\mfp@ellipse#1#2{\mfp@figmac{ellipse (#2, #1)}}%
+% \end{macrocode}
+%
+% \cs{mfp@curve} is called by six graphics macros: \cs{curve},
+% \cs{ccurve}, \cs{mfbezier} and their closed variants . Its two arguments
+% are the name of a \grafbase{} command, and \mfc{true} or \mfc{false}.
+% This works for any \grafbase{} command that creates a smooth path from a
+% list of points, provided the command has two versions: one that begins
+% with \texttt{t} and takes a tension as the first argument and the other
+% without a \texttt{t} that takes the same arguments \emph{except} the
+% tension. The second argument of \cs{mfp@curve} determines whether the
+% curve is to be closed. These are passed along to \cs{mfp@@curve}, which
+% gets the tension as the third argument.
+% \begin{macrocode}
+\newdef\mfp@curve#1#2{\mfp@nullopt{\mfp@@curve{#1}{#2}}}%
+\newdef\mfp@@curve#1#2#3{%
+ \mfp@listmac{\mfp@ifempty{#3}{#1}{t#1 (#3)} (#2)}}%
+% \end{macrocode}
+%
+% The argument of \cs{mfp@fcncurve} is the optional argument of
+% \cs{fcncurve} (the tension).
+% \begin{macrocode}
+\newdef\mfp@fcncurve#1{%
+ \mfp@listmac{\mfp@ifempty{#1}{fcncurve}{functioncurve (#1)}}}%
+% \end{macrocode}
+%
+% \cs{mfp@fcn} is called by six commands: \cs{levelcurve},
+% \cs{plrregion}, \cs{belowfcn}, \cs{function}, \cs{plrfcn} and,
+% \cs{parafcn}. They have a common argument structure for both
+% the \mfpic{} and \grafbase{} command. \cs{mfp@fcn} calls
+% \cs{mfp@dotension} to parse the optional tension argument (the second
+% argument) leaving the results in the macros \cs{mfp@t} (a \texttt{t}
+% for command name if a tension value was present) and \cs{mfp@sval}
+% (\texttt{true} or \texttt{false} and the tension value if it was
+% present). The first argument is the base name of a \grafbase{} command.
+%
+% Ultimately, \cs{mfp@@fcn} is called, and only then the two mandatory
+% arguments of the calling command are read.
+% \begin{macrocode}
+\newdef\mfp@fcn#1#2{%
+ \mfp@defaultopt{\mfp@dotension{\mfp@@fcn{#1}}}{#2}}%
+\newdef\mfp@@fcn#1#2#3{%
+ \mfp@figmac{\mfp@t#1 (\mfp@sval) (#2) (#3)}}%
+% \end{macrocode}
+%
+% \cs{mfp@btwn} is called by \cs{btwnfcn} and \cs{btwnplrfcn}, with the
+% name of a \grafbase{} command as its argument. It does the same tension
+% processing as \cs{mfp@fcn} and calls \cs{mfp@@btwn}, which reads the
+% three mandatory arguments of the calling command.
+% \begin{macrocode}
+\newdef\mfp@btwn#1{%
+ \mfp@defaultopt{\mfp@dotension{\mfp@@btwn{#1}}}p}%
+\newdef\mfp@@btwn#1#2#3#4{%
+ \mfp@figmac{\mfp@t#1 (\mfp@sval) (#2) (#3) (#4)}}%
+% \end{macrocode}
+%
+% The tension parsing is done by \cs{mfp@@dotension}. It causes
+% \cs{mfp@t} to be empty or \texttt{t} depending on the existence of
+% the tension parameter, and causes \cs{mfp@sval} to contain either the
+% smoothness boolean alone or the boolean followed by the tension value,
+% separated by a comma. After this, \cs{mfp@dotension} calls the code it
+% is passed in its first argument.
+% \begin{macrocode}
+\newdef\mfp@dotension#1#2{\mfp@@dotension#2\mfp@end#1}%
+\newdef\mfp@@dotension#1#2\mfp@end{\def\mfp@t{}%
+ \if p#1\relax \let\mfp@sval\mfp@F
+ \else \let\mfp@sval\mfp@T
+ \mfp@ifexist{#2}{\def\mfp@t{t}\edef\mfp@sval{\mfp@sval, #2}}%
+ \fi}%
% \end{macrocode}
%
-% One can add to \cs{mfp@additions} to extend \mfpic. Currently it
-% is empty. This ends \cs{@mfpic@graf@macs}.
+% \cs{mfp@plotdata} is called by \cs{plotdata}. I follows the scheme of
+% most of the data file processing commands: it passes another command (in
+% this case \cs{mfp@doplots}) to \cs{mfp@processfile}, which reads the
+% filename as its second argument. The argument of \cs{mfp@plotdata},
+% which is passed to \cs{mfp@doplots}, is the original optional parameter
+% of \cs{plotdata}.
% \begin{macrocode}
- \newdef\pinumber{3.14159}%
- \mfp@additions
-}%
+\newdef\mfp@plotdata#1{\mfp@processfile{\mfp@doplots{#1}}}%
+% \end{macrocode}
+%
+% \cs{mfp@doplots} is a self-looping macro. It issues the rendering
+% prefix (in \cs{mfp@setstyle}), increments the linetype, and writes the
+% path expression defined by the first set of data in the file (via
+% \cs{mfp@@datafile}, which stops at a blank line). That part, when prefixed
+% by \cs{mfp@figmacbase}, functions as a \cs{mfp@figmac}. When finished with
+% one figure, \cs{mfp@ifenddata} checks if there is another set of data in
+% the file. If so, \cs{mfp@doplots} calls itself again.
+%
+% The argument is the smoothness parameter(s), which it merely passes on.
+% There is no filename argument: \cs{mfp@plotdata} has already opened
+% the input stream \cs{mfp@data} and subsequent macros will read from it.
+% \begin{macrocode}
+\newdef\mfp@doplots#1{\mfp@setstyle
+ \advance\mfp@linetype1 \mfp@figmacbase
+ \mfp@@datafile{#1}\mfp@ifenddata{}{\mfp@doplots{#1}}}%
+% \end{macrocode}
+%
+% When two blank lines occur in succession, \cs{plotdata} is suppose to
+% stop. \cs{mfp@ifenddata} is called when \cs{mfp@@datafile} ends (one blank
+% line was found), it checks for another blank line (or end-of-file)
+% and, if it finds one, selects the first of the two alternatives that
+% follow.
+% \begin{macrocode}
+\newdef\mfp@ifenddata{%
+ \ifeof\mfp@data \@xp\@firstoftwo
+ \else \mfp@skipcomments
+ \ifx\mfp@temp\mfp@par \@XP\@firstoftwo
+ \else \@XP\@secondoftwo
+ \fi
+ \fi}%
+% \end{macrocode}
+%
+% The \cs{datafile} command, which calls \cs{mfp@datafile} is really
+% obsolete, as one can now use \cs{curve} or \cs{polyline} (or any other
+% list-to-path command) followed by the \cs{datafile} construct.
+% However, it requires only two extra commands and then calls a link in
+% the \cs{plotdata} chain of commands, so we keep it.
+%
+% \cs{mfp@datafile} is called by \cs{datafile}, which passes the optional
+% smoothness argument. It does the figure macro stuff, then calls
+% \cs{mfp@processfile} to open the data file and run \cs{mfp@@datafile}.
+% \begin{macrocode}
+\newdef\mfp@datafile#1{%
+ \mfp@figmacbase\mfp@processfile{\mfp@@datafile{#1}}}%
+% \end{macrocode}
+%
+% \cs{mfp@@datafile} checks for an empty optional parameter and then calls
+% \cs{mfp@dodatafile}. We could handle the options earlier in the chain,
+% but we would have to do that for both \cs{plotdata} and \cs{datafile}
+% since both call on it. It is somewhat more efficient to reproduce a bit
+% of the internals of \cs{mfp@defaultopt} just once.
+% \begin{macrocode}
+\newdef\mfp@@datafile#1{%
+ \mfp@ifempty{#1}%
+ {\mfp@dodatafile\mfp@smoothness\mfp@tension}%
+ {\mfp@dodatafile#1}\mfp@end}%
+% \end{macrocode}
+%
+% And \cs{mfp@dodatafile} is mainly just a wrapper, processing the optional
+% parameter (passed by either \cs{plotdata} or \cs{datafile}) which
+% selects what figure making code should be written. For
+% \oarg{s\meta{tension}} a \gbc{curve} command is used. For \oarg{p}, a
+% \gbc{polyline}. (The `\cs{mfp@F}' means the curve is not closed).
+%
+% After that, \cs{mfp@rwdata} is called, which initiates the reading and
+% writing of the the data.
+% \begin{macrocode}
+\newdef\mfp@dodatafile#1#2\mfp@end{%
+ \mfsrc{%
+ \if s#1 \mfp@ifempty{#2}{curve }{tcurve (#2) }%
+ \else polyline
+ \fi (\mfp@F)}\mfp@rwdata}%
+% \end{macrocode}
+%
+% \cs{mfp@image} is called by \cs{mfpimage} with the optional argument in
+% \arg1 and the mandatory argument in \arg2.
+% \begin{macrocode}
+\newdef\mfp@image#1#2{\mfsrc{\@nl makeimage (#2, #1)}}%
+% \end{macrocode}
+%
+% The command \cs{mfp@getdimens} is only called by \cs{trimpath}. Its
+% first argument is the comma separated pair of dimensions from that
+% command. It defines \cs{mfp@thedimens} to expand to exactly that,
+% except: if there is only one dimension and no comma (empty \arg2 and
+% \arg3), the result is a pair of dimensions both equal to it; if there is
+% a comma, but nothing on one side, it returns \texttt{0pt} for the empty
+% side. If a \TeX{} dimension command is used in either position, its
+% value is returned.
+%
+% The only time \arg3 is nonempty is when the original argument of
+% \cs{trimpath} contains a comma (then \arg3 is a comma). In that case we
+% let \cs{mfp@scratch} keep its previous value.
+% \begin{macrocode}
+\newdef\mfp@getdimens#1,#2,#3\mfp@end{%
+ \mfp@scratch \@ifmtarg{#1}{\z@}{#1}\relax
+ \edef\mfp@thedimens{\the\mfp@scratch}%
+ \@ifmtarg{#3}{}{\mfp@scratch \@ifmtarg{#2}{\z@}{#2}\relax}%
+ \edef\mfp@thedimens{\mfp@thedimens,\the\mfp@scratch}}%
+% \end{macrocode}
+%
+% The argument of \cs{mfp@doubledraw} is the optional separation parameter
+% passed by \cs{doubledraw}. Then the optional color is processed.
+% \begin{macrocode}
+\newdef\mfp@doubledraw#1{%
+ \mfp@nullopt{\mfp@clroptrendi{doubledraw}{#1}}}%
+% \end{macrocode}
+%
+% Parameter \arg1 of \cs{mfp@wiggle} is the name, \gbc{sinewave} or
+% \gbc{corkscrew}, \arg2 is the optional argument (tension). The four
+% dimension values should follow as the mandatory argument of the calling
+% command.
+% \begin{macrocode}
+\newdef\mfp@wiggle#1#2{\mfp@rendi{#1 (#2)}}%
+% \end{macrocode}
+%
+% The utility macros, \cs{mfp@rendi}, \cs{mfp@modi} and \cs{mfp@listi}
+% write a command name and one set of parameters. They differ in the setup
+% required. The first writes via \cs{mfp@rendmac}, the second via
+% \cs{mfp@modmac} and the last via \cs{mfp@listmac}. \cs{mfp@rendii}
+% writes two sets of parameters, reversing them. \cs{mfp@optrendi} runs
+% \cs{mfp@rendi}, but feeds it the result of processing a optional
+% argument. \cs{mfp@optrendii} is similar, running \cs{mfp@rendii}.
+% \begin{macrocode}
+\newdef\mfp@rendi#1#2{\mfp@rendmac{#1 (#2)}}%
+\newdef\mfp@rendii#1#2#3{\mfp@rendmac{#1 (#3, #2)}}%
+\newdef\mfp@optrendi#1{\mfp@defaultopt{\mfp@rendi{#1}}}%
+\newdef\mfp@optrendii#1{\mfp@defaultopt{\mfp@rendii{#1}}}%
+\newdef\mfp@modi#1#2{\mfp@modmac{#1 (#2)}}%
+\newdef\mfp@listi#1#2{\mfp@listmac{#1 (#2)}}%
+% \end{macrocode}
+%
+% A great many \grafbase{} rendering commands come in two parts: one
+% command takes a color argument and the other command simply calls the
+% color version with a default color. In all these commands, the name of
+% the first is just the second with `\texttt{color}' prepended. This means
+% that the corresponding \mfpic{} rendering commands can share a common
+% macro, \cs{mfp@clropt}, which is passed the optional color and the name of
+% the second command.
+%
+% To simplify the output file, if a command's color option is empty
+% (signaling the default color) the `\texttt{color}'-free \grafbase{}
+% command name is written, otherwise the command name beginning with
+% `\texttt{color}' is written along with the color parameter.
+% \cs{mfp@clropt} makes the test and writes the command. \arg1 is the
+% color argument and \arg2 is the command's base name. The commands
+% \cs{mfp@clroptrend} and \cs{mfp@clroptrendi} provide useful
+% abbreviations when the command is a rendering command and the
+% \grafbase{} command takes no argument or one argument beside the color.
+%
+% There are a number of commands that are not subject to prefixes. For
+% this we have \cs{mfp@simple}. It reads a command name and its
+% arguments and writes the command, followed by the arguments wrapped in
+% parentheses, followed by a semicolon. We also have \cs{mfp@simplei}
+% which reads an additional argument.
+% \begin{macrocode}
+\newdef\mfp@clropt#1#2{\mfp@ifempty{#1}{#2}{\mfp@C#2 (#1)}}%
+\newdef\mfp@clroptrend#1#2{\mfp@rendmac{\mfp@clropt{#2}{#1}}}%
+\newdef\mfp@clroptrendi#1#2#3{\mfp@rendmac{\mfp@clropt{#3}{#1} (#2)}}%
+\newdef\mfp@simple#1#2{\mfcmd{\@nl #1 (#2)}}%
+\newdef\mfp@simplei#1#2#3{\mfcmd{\@nl #1 (#2, #3)}}%
+% \end{macrocode}
+%
+% All the path closing commands are implemented with \cs{mfp@close}. It
+% writes the closing command determined by the first argument, with a
+% `\texttt{t}' plus the second argument as a parameter, if that is not
+% empty.
+% \begin{macrocode}
+\newdef\mfp@close#1#2{\mfp@modmac{#1closed\mfp@ifexist{#2}{t (#2)}}}%
+% \end{macrocode}
+%
+% These next implement hatching. Except for \cs{mfp@xhatch}, they all call
+% \cs{mfp@thatch}. The argument of these is the optional argument of the
+% calling command. The second optional argument of that command (the
+% color) is processed by \cs{mfp@clroptrendi}.
+% \begin{macrocode}
+\newdef\mfp@thatch#1{\mfp@nullopt{\mfp@clroptrendi{thatch}{#1}}}%
+\newdef\mfp@lhatch#1{\mfp@thatch{#1,-45}}%
+\newdef\mfp@rhatch#1{\mfp@thatch{#1,45}}%
+\newdef\mfp@xhatch#1{\mfp@nullopt{\mfp@clroptrendi{xhatch}{#1}}}%
+% \end{macrocode}
+%
+% The all-purpose \cs{mfp@arr} sets the default values for the
+% \cs{mfp@h...} commands (two of which are passed as parameters). This
+% is accomplished by the \cs{mfp@arr@\meta{x}} commands. The
+% next two commands constitute a loop that ends when no more options
+% follow. Each turn through the loop the initial letter is checked and the
+% value that follows it is stored in one of those macro (possibly
+% replacing some default). Ultimately, \cs{mfp@doarrow} writes the
+% code to the output file.
+% \begin{macrocode}
+\newdef\mfp@arrowhead#1#2{\mfp@arr 0{Gheadpath (#1) (#2)}}%
+\newdef\mfp@arrow#1{\mfp@arr 0{headpath#1}}%
+\newdef\mfp@arr#1#2{\mfp@arr@c{}\mfp@arr@l{\the\headlen}%
+ \mfp@arr@b{#1}\edef\mfp@hcommand{#2}\mfp@arr@r{0}\mfp@@arr}%
+\newdef\mfp@@arr{\@ifnextchar[{\mfp@arropt}{\mfp@doarrow}}%
+\newdef\mfp@arropt[#1]{%
+ \mfp@ifexist{#1}{\mfp@@arropt#1\mfp@end}\mfp@@arr}%
+\newdef\mfp@@arropt#1#2\mfp@end{%
+ \mfp@ifempty{#2}%
+ {\Mfpic@warn{Arrow option #1 has no value\on@line;
+ it will be ignored.}}%
+ {\@ifundefined{mfp@arr@#1}%
+ {\Mfpic@warn{Arrow option #1 invalid \on@line;
+ it will be ignored}}%
+ {\@nameuse{mfp@arr@#1}{#2}}%
+ }}%
+\newdef\mfp@doarrow{%
+ \mfp@addmac{\mfp@clropt{\mfp@hcolour}{\mfp@hcommand} (\mfp@hlength,
+ \mfp@hrotate, \mfp@hlocate)}}%
+% \end{macrocode}
+%
+% Implement the optional argument of \cs{turn} with \cs{mfp@turn}.
+% \begin{macrocode}
+\newdef\mfp@turn#1#2{\rotatearound{#1}{#2}}%
+% \end{macrocode}
+%
+% The first argument of \cs{mfp@axis} is the optional argument of
+% \cs{axis}. Unfortunately, the $x$ and $y$ axis have one default head
+% length, \cs{axisheadlen} (initially \texttt{5pt}), while the side
+% axes have a different default, \cs{sideheadlen} (initially \texttt{0pt}).
+% Therefore we don't know which default to use until the mandatory argument
+% is read. In retrospect, it would have been better to put the optional
+% argument after the mandatory one. So if there is no optional argument,
+% the first argument of \cs{mfp@axis} is empty rather than a default.
+%
+% We use \cs{mfp@getaxis} to check the mandatory argument and return the
+% default head length in \cs{mfp@defheadlen}. It also stores the
+% letter in \cs{mfp@axisletter}, but returns `\texttt{x}' for an invalid
+% one.
+%
+% The letter argument \arg{2} is permitted to be a word, provided it
+% begins with one of the allowed letters. The `\marg{(none)}' is there to
+% provide an argument (and an informative error message) in the unlikely
+% case that this argument is empty.
+%
+% The \cs{mfp@addmac} command is the internal code that \cs{arrow[l \#1]}
+% would produce. This is slightly more efficient than calling \cs{arrow}.
+% \begin{macrocode}
+\newdef\mfp@axis#1#2{\mfp@getaxis#2{(none)}\mfp@end\axis
+ \mfp@addmac{headpath (\mfp@emptysub{#1}{\mfp@defheadlen}, 0, 0)}%
+ \axisline{\mfp@axisletter}}%
+\newdef\mfp@doaxes#1#2{\def\mfp@@axis##1{\mfp@axis{#1}{##1}\mfp@@doaxis}%
+ \mfp@@doaxis#2\mfp@end}%
+\newdef\mfp@@doaxis{\mfp@ifend{}\mfp@@axis}%
+% \end{macrocode}
+%
+% The six commands for putting hashmarks on an axis call \cs{mfp@marks}
+% with the axis letter and the optional length of the marks. Note that
+% the location of the marks is a list of numbers, so it is picked up by
+% \cs{mfp@writedata} just as in \cs{mfp@listmac}. But it is not a figure
+% macro, so we cannot use \cs{mfp@listmac} itself.
+% \begin{macrocode}
+\newdef\mfp@marks#1#2{\mfsrc{\@nl #1marks (#2)}\mfp@writedata}%
+% \end{macrocode}
+%
+% Called by \cs{vectorfield} or \cs{plrvectorfield} after processing the
+% optional argument, the following simply writes the appropriate
+% \grafbase{} code. The first argument is either empty or the string
+% `\texttt{plr}'.
+% \begin{macrocode}
+\newdef\mfp@vectorfield#1#2#3#4#5{%
+ \mfcmd{#1vectorfield (#2, #3) (#4) (#5)}}%
+% \end{macrocode}
+% The first argument of both these is the optional size argument of the
+% calling commands (\cs{point} and \cs{plotsymbol}). For
+% \cs{mfp@plotsymbol} the second argument is the symbol name.
+% \begin{macrocode}
+\newdef\mfp@point#1{%
+ \mfsrc{\@nl pointd (#1, \ifpointfill\mfp@T\else\mfp@F\fi)}%
+ \mfp@writedata}%
+\newdef\mfp@plotsymbol#1#2{%
+ \mfsrc{\@nl plotsymbol (#2, #1)}\mfp@writedata}%
+% \end{macrocode}
+%
+% \cs{mfp@plottext} now simply calls the \cs{tlabel} code, since that can
+% now handle a list of points.
+% \begin{macrocode}
+\newdef\mfp@plottext#1#2#3{\tlabeljustify{#1}%
+ \def\tl@points{#3}\mfp@toks{#2}\mfp@thelabel\endgroup}%
+% \end{macrocode}
+%
+% The first argument of \cs{mfp@piechart} is the optional argument of
+% \cs{piechart}. It comes in two parts: a letter and an optional angle,
+% so \cs{mfp@@piechart} is used to parse it. A similar description
+% applies to \cs{piewedge}.
+% \begin{macrocode}
+\newdef\mfp@piechart#1{\mfp@@piechart#1\mfp@end}%
+\newdef\mfp@@piechart#1#2\mfp@end#3{% #3 = center,radius
+ \mfsrc{\@nl piechart(\if a#1 \else-\fi1, %
+ \mfp@emptysub{#2}{90}, #3)}\mfp@writedata}%
+\newdef\mfp@piewedge#1{\mfp@@piewedge#1\mfp@end}%
+\newdef\mfp@@piewedge#1#2\mfp@end#3{% #3=number
+ \mfp@figmac{(piewedge[#3]%
+ \mfp@ifexist{#2}%
+ {\if #1x shifted (#2*piedirection[#3])%
+ \else\if #1s shifted (#2)%
+ \else\if #1m shifted (#2 - piecenter)%
+ \fi\fi\fi})}}%
+% \end{macrocode}
+%
+% The optional argument of \cs{barchart} becomes \arg{1} and the data is
+% \arg{2}.
+% \begin{macrocode}
+\newdef\mfp@barchart#1#2{%
+ \mfsrc{\@nl barchart (#1, \if h#2\mfp@F\else\mfp@T\fi)}%
+ \mfp@writedata}%
% \end{macrocode}
%
%
% \section{The \env{mfpic} environment}\label{mfpicenv}
%
-% \subsection{Initializations}\label{mfpicinitializations}
+% \subsection{Initializing the environment}\label{mfpicinitializations}
+%
+% For mostly historical reasons, the macros that create labels are
+% redefined anew with each \env{mfpic} environment. These have gradually
+% grown in number and size. We are still doing this, but the internal
+% part of these commands have now been moved outside the environment,
+% and only the user lever commands are localized.
+%
+% \DescribeMacro{\everymfpic}
+% This takes a token list and saves it in \cs{every@mfpic} to be issued
+% at the very end of the \cs{mfpic} command. There is a companion macro
+% \DescribeMacro{\everyendmfpic} that is issued at the very beginning of
+% \cs{endmfpic}. These are always global, otherwise they are defined much
+% like \cs{everytlabel}.
+% \begin{macrocode}
+\newdef\everymfpic{\afterassignment\@everymfpic\mfp@toks}%
+\newdef\everyendmfpic{\afterassignment\@everyendmfpic\mfp@toks}%
+\newdef\@everymfpic{\edef\every@mfpic{\the\mfp@toks}}%
+\newdef\@everyendmfpic{\edef\every@endmfpic{\the\mfp@toks}}%
+\everymfpic{}\everyendmfpic{}%
+% \end{macrocode}
%
% \DescribeMacro{\mfpic}
% This command (or the \LaTeX{} environment \cs{begin}\marg{mfpic}) takes
@@ -3877,8 +4946,8 @@
% equal to the xscale.
%
% There are also 4 mandatory arguments: the first two give the minimum and
-% maximum extent of the x coordinates and the last two do the same for the
-% y coordinates. The coordinates used in the figure commands within an
+% maximum extent of the $x$ coordinates and the last two do the same for the
+% $y$ coordinates. The coordinates used in the figure commands within an
% mfpic environment are multiplied by the corresponding scales, and that
% is the multiple of \cs{mfpicunit} used in positioning, drawing, etc.
%
@@ -3886,77 +4955,66 @@
% mandatory arguments for \cs{@mfpic}, which contains the actual
% definition.
% \begin{macrocode}
-\newdef\mfpic{\simple@opt\@mfpicA{1}}%
-\newdef\@mfpicA#1{\simple@opt{\@mfpic{#1}}{#1}}%
+\newdef\mfpic{\mfp@defaultopt\@mfpicA{1}}%
+\newdef\@mfpicA#1{\mfp@defaultopt{\@mfpic{#1}}{#1}}%
\newdef\@mfpic#1#2#3#4#5#6{%
% \end{macrocode}
%
-% We start by saving \mfpic{} extra fontdimen values. They will be
+% We start by saving \mfpic's extra fontdimen values. They will be
% restored by \cs{endmfpic}. This makes the values local (font dimensions
-% are normally always global). Then we begin a group to localize ordinary
+% are normally always global). Also, we save \cs{mfpicunit} using an
+% internal name to prevent users from changing it midpicture.
+%
+% Then we begin a group to localize ordinary
% \TeX{} definitions and assignments, disable writes to the output if
-% \cs{opengraphsfile} hasn't occurred yet. save the current font in
-% \cs{@tcurr} for restoration inside text labels and captions, set the
-% switch that says we are in an \cs{mfpic} environment, disable \cs{par}
-% so that blank lines have (little or) no effect, and switch to
+% \cs{opengraphsfile} hasn't occurred yet (an error), save the current
+% font in \cs{@tcurr} for restoration inside text labels and captions, set
+% the switch that says we are in an \cs{mfpic} environment, disable
+% \cs{par} so that blank lines have (little or) no effect, and switch to
% \cs{nullfont} so that stray characters (always an error) and spaces (not
-% an error), have no effect. I wonder if \cs{@empty} is a better
-% disabling definition for \cs{par} than \cs{relax}.
+% an error), have no effect. I don't know whether \cs{relax} is a better
+% disabling definition for \cs{par} than \cs{@empty}.
% \begin{macrocode}
\save@mfpicdimens
+ \@mfpicunit\mfpicunit
\begingroup % ended near end of \endmfpic
- \@ifmfpfileisopen{}%
- {\if@mfp@nowrite\else \nooutputfileerror{\mfpic}%
- \def\@mfsrc##1##2{}%
+ \edef\xmin{#3}\edef\xmax{#4}\edef\ymin{#5}\edef\ymax{#6}%
+ \@nameuse{@safe@activestrue}%
+ \mfp@ifopengraphsfile{}%
+ {\ifmfp@nowrite\else \mfp@nooutput@error\mfpic
+ \def\mfp@src##1##2{\endgroup}%
\fi}%
- \mfp@DBlog{Starting mfpic \number\mfp@count \@online.}%
- \font\@tcurr=\fontname\font\relax
+ \mfp@DBlog{Starting mfpic \number\mfp@count \on@line.}%
+ \@xp\let\@xp\@tcurr\the\font
\let\ifin@mfpicenv\iftrue
-% \let\par\relax
\let\par\@empty
\nullfont
% \end{macrocode}
%
% The graphics macros are undefined outside the \env{mfpic} environment.
-% They are defined now by invoking \cs{@mfpic@graf@macs}.
-% \begin{macrocode}
- \@mfpic@graf@macs
-% \end{macrocode}
+% They are defined now by invoking \cs{mfp@grafmacs}.
%
-% Then we define two utility macros used to convert graph coordinates into
-% actual dimensions relative to the lower left corner of the picture. The
-% first argument is a dimension, but it is normally obtained by first
-% multiplying a coordinate value by \cs{mfpicunit}. Since font dimensions
-% may be involved, we assign \texttt{\#\#1} to a scratch dimension
-% register first, since \TeX{} won't \cs{advance} font dimensions. We make
-% the final assignment global for consistency.
+% One can add to \cs{mfp@additions} to extend \mfpic. Currently it
+% is empty.
% \begin{macrocode}
- \newdef\@xconv##1{%
- {\mfp@scratch = ##1\relax
- \advance\mfp@scratch by -#3\mfpicunit
- \mfp@scratch = #1\mfp@scratch
- \global ##1 = \mfp@scratch}}%
- \newdef\@yconv##1{%
- {\mfp@scratch=##1\relax
- \advance\mfp@scratch by -#5\mfpicunit
- \mfp@scratch = #2\mfp@scratch
- \global ##1 = \mfp@scratch}}%
+ \mfp@grafmacs
+ \mfp@additions
% \end{macrocode}
%
+% We also define the coordinate conversion macros \cs{@xconv} and
+% \cs{@yconv} via \cs{mfp@setconv}. These are used to calculate the
+% nominal dimensions of the picture and to position \TeX{} labels.
+%
% We are going to add all text labels to a box \cs{@alltlabels} and put
% that whole box on top of the graph during \cs{endmfpic}.
-% We initialize the box here. The dimensions \cs{@tlabelsbot}, etc.,
-% are also initialized. Each will be changed as labels are added, so a
-% `non-maxdimen' value at \cs{endmfpic} means there are labels to add.
+% We initialize the box here, as well as the dimensions \cs{@tlabelsbot},
+% etc.
% \begin{macrocode}
-\setbox\@alltlabels\vbox{\hbox{}}%
-\@tlabelsbot \maxdimen
-\@tlabelstop -\maxdimen
-\@tlabelsleft \maxdimen
-\@tlabelsright-\maxdimen
+ \mfp@setconv{#1}{#2}{#3}{#5}%
+ \mfp@inittlabels
% \end{macrocode}
%
-% The graph is loaded into a box register at the end of the mfpic
+% The graph is loaded into a box register at the end of the \env{mfpic}
% environment. The size of that box register may have to correspond to the
% arguments of the \cs{mfpic} command. Since \cs{endmfpic} doesn't receive
% these arguments we process them now.
@@ -3966,15 +5024,14 @@
% edges of the graph. These are all font dimensions, so they are global
% assignments.
% \begin{macrocode}
- \@graphright=#4\mfpicunit \@xconv\@graphright
- \@graphtop=#6\mfpicunit \@yconv\@graphtop
- \@graphleft=0pt
- \@graphbot=0pt
- \@graphwd=\@graphright
+ \@xconv{#4}\@graphright
+ \@yconv{#6}\@graphtop
+ \@graphleft 0pt
+ \@graphbot 0pt
% \end{macrocode}
%
-% \subsection{Initialization code for the current
-% picture}\label{current}
+% \subsection{Initializing the \MF{} picture}
+% \label{current}
%
% Each \env{mfpic} environment produces a \gbc{beginmfpic...endmfpic}
% environment in the output file. Here we initialize the variables that
@@ -3983,8 +5040,8 @@
% figure as its only argument. For debugging purposes we write a \MF{}
% comment the contains the line number in the \TeX{} source file where the
% \cs{mfpic} command occurred. We also write a line of dashes
-% (\cs{mf@d}) to make it easier to find the start of the environment. This
-% will be removed in the public release.
+% (\cs{mfp@d}) to make it easier to find the start of the environment. This
+% may be removed in the public release.
%
% When \opt{mplabels} is in effect we need to set the font for the labels
% to the saved \cs{@tcurr} by writing some \mfc{verbatimtex} code. Since
@@ -3994,15 +5051,15 @@
% fact it can be used any number of times. It's purpose is to be at an
% outer level, while \mfc{btex} is localized inside a box
% \begin{macrocode}
- \mfsrc{\@nl \mf@p\mf@d\mf@d\mf@d\@nl
- unitlen := \the\mfpicunit\mf@s;\@nl
+ \mfsrc{\@nl \mfp@p\mfp@d\mfp@d<\number\mfp@count>\@nl
+ unitlen := \the\@mfpicunit\mfp@s;\@nl
xscale := #1; yscale := #2;\@nl
bounds (#3, #4, #5, #6);\@nl
\@nl
- beginmfpic(\number\mfp@count); \mf@p\space\@mfplineno.
+ beginmfpic(\number\mfp@count); \mfp@p\space\mfp@lineno.
\mfp@ifmpost
- {\@nl verbatimtex \@nl
- \begingroup\space\font\noexpand\MFPcfont=\fontname\@tcurr\@nl
+ {\@nl verbatimtex
+ \bgroup\font\noexpand\MFPcfont=\fontname\@tcurr\relax
etex;}{}}%
% \end{macrocode}
%
@@ -4013,17 +5070,18 @@
% \opt{mplabels} where most of the work takes place in \file{grafbase.mp}.
% The most involved is the definition of \cs{tlabel} without
% \opt{mplabels}. All the rest (except \cs{tcaption}) add labels by
-% calling \cs{tlabel}. Some of those could be simpler under \opt{mplabels}
-% but then we'd need two versions of each.
+% calling \cs{tlabel} or one of its internal commands. Some of those could
+% be simpler under \opt{mplabels} but then we'd need two versions of each.
%
% \DescribeMacro{\tlabel}
% This is the common code, processing optional arguments, etc. for
-% \cs{tlabel}. It ultimately calls one of \cs{@tlabel} (no
-% \opt{mplabels}) or \cs{@@tlabel} (\opt{mplabels}). It takes an optional
-% argument describing the justification and rotation, followed by a
-% location in one of two syntaxes (which adds to the complication, but we
-% want to be backward compatible), followed by the text of the label
-% (\TeX{} code).
+% \cs{tlabel}. It, and the first several commands it calls, set switches
+% and macros based on the arguments. The first argument is optional: the
+% justification (the default is that set with the \cs{tlabeljustify}
+% command), the second is a location or locations, and the last is the
+% \TeX{} code that constitutes the label. All these are ultimately stored
+% as macros and then either \cs{mp@tlabel} or \cs{tex@tlabel} is called
+% without arguments.
%
% The location can be in the form \parg{\meta{x},\meta{y}}, but if \MP{}
% is processing labels we would like to be able to use \MP{} expressions,
@@ -4031,60 +5089,240 @@
% token matching for reading the two coordinates. Therefore we allow the
% whole point to be enclosed in braces. Under \opt{mplabels} the contents
% of the braces will be written to the output file without any further
-% parsing. Without \opt{mplabels}, there should be either no braces, or
-% inside the braces is the previously described syntax.
+% parsing. Without \opt{mplabels}, the braces should contain a comma
+% separated list of locations in the previously described syntax.
%
-% We start with \cs{begingroup} to hide changes, then reset whitespace to
-% its normal catcodes. Without \opt{mplabels} we reset \cs{tlabel@rot} to
-% empty. Finally, we check for an optional parameter and run \cs{@tlabel@},
-% feeding it an empty optional argument if there is none.
+% We start things off by calling \cs{mfp@label} to process the optional
+% argument, feeding it an empty one if none is present.
% \begin{macrocode}
- \newdef\tlabel{%
- \begingroup % ended in \@(@@)tlabel
- \mfp@resetwhitespace
- \@ifmplabels{}{\def\tlabel@rot{}}%
- \null@opt\@tlabel@}%
+ \newdef\tlabel{\mfp@nullopt\mfp@label}%
% \end{macrocode}
%
-% We run \cs{tlabeljustify} on the optional parameter. This keeps the
-% default values of \cs{tl@vpos}, \cs{tl@hpos} and \cs{tlabel@rot} if
-% the parameter is empty, otherwise it modifies them inside the current
-% group.
+% We allow some \cs{tlabels} to be placed behind the picture rather than
+% on top. This is easy under \opt{mplabels}: just use \cs{tlabel} before
+% any rendering commands (making sure \opt{overlaylabels} is turned off.)
+% However, \MP{} labels can contain only fonts and one would often want
+% to do this with an included graphic (as a background). After
+% \DescribeMacro{\startbacktext}
+% \cs{startbacktext}, labels are saved in a special place to be included
+% behind the \mfpic{} graphic. This continues until
+% \DescribeMacro{\stopbacktext}\cs{stopbacktext}.
%
-% We then check for a left parenthesis and assume the
-% \parg{\meta{x},\meta{y}} syntax in that case, calling \cs{@@tlabel@}
-% to process it. Under \opt{mplabels} it stuffs the whole point into
-% braces and call the main code \cs{@@tlabel} with that argument,
-% otherwise it pulls out the two coordinates and feeds them as the first
-% two arguments of the main code \cs{@tlabel}.
+% We simply turn off \gbc{overlaylabels} and turn on \gbc{backtextlabels}
+% for \MP{}. We also save the box \cs{@alltlabels} in \cs{@wholegraph}
+% (which will not be needed until \cs{endmfpic}) and then reinitialize it.
+% At \cs{endbacktext} we copy the box \cs{@alltlables} into \cs{@backtext},
+% and then restore what we changed. We let the values of \cs{@tlabelsleft},
+% etc., be updated normally as labels are added to the backtext.
% \begin{macrocode}
- \newdef\@tlabel@[##1]{\tlabeljustify{##1}%
- \mfp@ifnextchar({\@@tlabel@}{\@@tlabel@@}}%
- \newdef\@@tlabel@(##1,##2){%
- \@ifmplabels
- {\@@tlabel{(##1,##2)}}{\@tlabel{##1}{##2}}}%
+ \newdef\startbacktext{\let\mfp@restoreOL\@empty
+ \mfp@ifmpost
+ {\ifmfp@overlabels
+ \let\mfp@restoreOL\overlaylabels\nooverlaylabels
+ \fi \setmfboolean{underlaylabels}{\mfp@T}}{}%
+ \setbox\@wholegraph \box\@alltlabels
+ \setbox\@alltlabels \vbox{\hbox{}}}%
+ \newdef\stopbacktext{%
+ \setbox\@backtext \box\@alltlabels
+ \setbox\@alltlabels\box\@wholegraph
+ \mfp@ifmpost{\setmfboolean{underlaylabels}{\mfp@F}}{}%
+ \mfp@restoreOL}%
% \end{macrocode}
%
-% If no parenthesis, we assume braces surround the point. Under
-% \opt{mplabels} we call the main code (\cs{@@tlabel}) with that argument.
-% Otherwise, we re-call the above \cs{@@tlabel@} on the contents of the
-% braces, assuming (without checking) that the contents are coordinates in
-% parentheses.
+% \subsection{Surrounding the label with a path}\label{surrounding}
+%
+% These next macros place labels, but also define a path surrounding the
+% label. This allows one to clear a space for the label, create a
+% background, draw a box around it, etc., all in one command (with the
+% appropriate sequence of prefixes). They take two optional arguments.
+% The first optional argument depends on the type of path being created.
+% It is described below. The second one is the justification as in the
+% \cs{tlabel} command. There are two mandatory arguments, the location
+% (same syntax as for \cs{tlabel} except it cannot be a list) and the text.
+% They also have star-forms, which create the path but don't place the
+% label.
+%
+% All the curves are sized to just contain what I will call the
+% \emph{label strip}. Imagine a rectangle that just fits around the text.
+% Now extend it on all sides by the value defined with \cs{tlpathsep}.
+% This rectangle is the label strip.
+%
+% \DescribeMacro{\tlabelrect}
+% \cs{tlabelrect} creates the rectangular boundaries of the label strip by
+% default. With the optional argument a rectangle with rounded corners is
+% produced; the optional argument is the radius of the quarter circles used.
+% These quarter circles pass through the corners of the label strip and are
+% joined together with straight lines to create the rest of the rectangle.
+% As a special case, the optional argument can contain the keyword
+% `\texttt{roundends}' to produce a rectangle with the short sides
+% replaced by a semicircle. If the radius is negative, the corners are
+% concave.
+%
+% \DescribeMacro{\tlabeloval}
+% \cs{tlabeloval} produces an ellipse which, by default has the same
+% aspect ratio as the label strip. It's size just includes the label strip,
+% and it passes through the four corners. The optional argument contains a
+% numerical factor $m$ that changes the width and the height so that the
+% width-to-height ratio is multiplied by $m$, but the ellipse still
+% passes through those four corners. The default value of $m$ is 1.
+% Negative $m$ has the same effect as $|m|$. If $m=0$ a rectangle is
+% produced.
+%
+% \DescribeMacro{\tlabelellipse}
+% \cs{tlabelellipse} produces an ellipse, where the optional argument $a$
+% is the ratio of its width to its height. The default for $a$ is 1, which
+% produces a circle. Negative $a$ has the same effect as $|a|$. If $a =
+% 0$, a rectangle is produced.
+%
+% \DescribeMacro{\tlabelcircle}
+% To get a circle one can use \cs{tlabelellipse} without the optional
+% argument, or with argument \oarg{1}. However, it makes for a more
+% readable source if the command reflects the shape. \cs{tlabelcircle} is
+% defined to be essentially \cs{tlabelellipse}\oarg{1}, but it first
+% checks for the \texttt{*} and passes it along in front of the \oarg{1}.
+%
+% We implement the other three by calling \cs{mfp@labelpath} with the name
+% of the curve (\texttt{rect}, \texttt{oval}, or \texttt{ellipse}) and the
+% default value of the optional argument (\texttt{0}, \texttt{1}, and
+% \texttt{1}, respectively) as arguments.
+% \begin{macrocode}
+ \newdef\tlabelrect{\mfp@labelpath{rect}{0}}%
+ \newdef\tlabeloval{\mfp@labelpath{oval}{1}}%
+ \newdef\tlabelellipse{\mfp@labelpath{ellipse}{1}}%
+ \newdef\tlabelcircle{%
+ \mfp@iftoken*{\tlabelellipse*[1]}{\tlabelellipse[1]}}%
+% \end{macrocode}
+%
+% \subsection{Placing multiple text labels}\label{multiple}
+%
+% One of the more onerous things about labels in \env{mfpic} environments
+% used to be the shear amount of typing required. The following commands
+% are intended to reduce the work by having to repeat only the arguments.
+%
+% \DescribeMacro{\tlabels}
+% This command takes only one argument containing any sequence of valid
+% arguments for the \cs{tlabel} command, optionally separated by
+% whitespace (but not blank lines).
+%
+% We begin a group, reset whitespace to normal, and call the command that
+% reads the argument.
% \begin{macrocode}
- \newdef\@@tlabel@@##1{%
- \@ifmplabels {\@@tlabel{##1}}{\@@tlabel@##1}}%
+ \newdef\tlabels{\begingroup
+ \mfp@resetwhitespace\mp@mksharpother \mfp@labels}%
+% \end{macrocode}
+%
+% Labelling axes generates a lot of \cs{tlabel} commands, or a single
+% \cs{tlabels} command with a lot of arguments. Given that such labels
+% have much in common (e.g., for the $x$ axis the $y$ coordinate and the
+% justification are usually the same for all the labels) we can do still
+% better at abbreviating this task.
+%
+% \DescribeMacro{\axislabels}
+% The \cs{axislabels} command takes a mandatory argument (the axis)
+% followed by an optional argument (the justification, as in the
+% \cs{tlabel} command), followed by another mandatory argument, a comma
+% separated list of items of the form \marg{\meta{label}}\meta{coord}. The
+% label is placed at that coordinate on the given axis.
+%
+% Here we call \cs{mfp@axislabel}, feeding it the arguments of the current
+% \cs{@mfpic} command.
+% \begin{macrocode}
+ \newdef\axislabels{\mfp@axislabels{#3}{#4}{#5}{#6}}%
+% \end{macrocode}
+%
+% \subsection{Captions}\label{captions}
+%
+% Captions are not added immediately by the \cs{tcaption} command. That
+% command redefines \cs{mfp@docaption} to add it, and \cs{endmfpic}
+% executes \cs{mfp@docaption}. For now, \cs{mfp@docaption} does nothing.
+%
+% \DescribeMacro{\tcaption}
+% \cs{tcaption} takes an optional argument that holds two numbers,
+% separated by a comma. The first number determines the maximum width of a
+% one line caption. Captions larger than that multiple of the width of the
+% graphic will be reset as a paragraph, whose lines have length the second
+% number times the width of the graph. The defaults are \oarg{1.2,1}.
+% \begin{macrocode}
+ \let\mfp@docaption\@empty
+ \newdef\tcaption{\mfp@defaultopt\mfp@caption{1.2,1.0}}%
+% \end{macrocode}
+%
+% Finally, invoke the \cs{everymfpic} tokens.
+% \begin{macrocode}
+ \every@mfpic}% End of \@mfpic
+% \end{macrocode}
+%
+% \section{Implementing the label and caption macros}
+%
+% \cs{mfp@setconv} is passed four of the arguments of the surrounding
+% \env{mfpic} environment. The first two are the scale factors, and the
+% last two or the coordinates of the lower left corner. It defines two
+% macros used to convert graph coordinates into actual dimensions relative
+% to the lower left corner of the picture. The second argument of
+% \cs{@xconv} and \cs{@yconv} is a dimension register in which the
+% converted result is returned. We make the final assignment global for
+% consistency (font dimension assignments are always global).
+% \begin{macrocode}
+\newdef\mfp@setconv#1#2#3#4{% #1,#2=xscale,yscale. #3,#4=xmin,ymin.
+ \def\@xconv##1##2{% ##1 = coord. ##2 = dimension register or fdim
+ {\mfp@scratch=##1\@mfpicunit \advance\mfp@scratch by -#3\@mfpicunit
+ \mfp@scratch=#1\mfp@scratch
+ \global ##2=\mfp@scratch}}%
+ \def\@yconv##1##2{% ditto
+ {\mfp@scratch=##1\@mfpicunit \advance\mfp@scratch by -#4\@mfpicunit
+ \mfp@scratch=#2\mfp@scratch
+ \global ##2=\mfp@scratch}}}%
+\newdef\mfp@inittlabels{%
+ \setbox\@alltlabels\vbox{\hbox{}}%
+ \setbox\@backtext\vbox{\hbox{}}%
+ \@tlabelsbot \maxdimen
+ \@tlabelstop -\maxdimen
+ \@tlabelsleft \maxdimen}%
+% \end{macrocode}
+%
+% The first argument of \cs{mfp@label} is the justification option. After
+% starting a group (it is ended in \cs{mfp@label@@}, we restore the
+% meaning of whitespace and give \texttt\# a safe catcode for writing to the
+% output, if necessary (\opt{mplabels}). Then we run \cs{tlabeljustify} on
+% the optional argument. An empty argument means any previously set values
+% won't be changed. Then we check for a left parenthesis and assume the
+% \parg{\meta{x},\meta{y}} syntax in that case, calling \cs{mfp@label@}
+% to process it. All it does is pack it in braces and call
+% \cs{mfp@label@@}. If no parentheses we skip to \cs{mfp@label@@}.
+%
+% Then \cs{mfp@label@@} gets a point or list of points as its first argument
+% and the label code as the second. It defines the macro \cs{tl@points} to
+% equal the point (or list) and puts the label into a token register.
+%
+% After all the data are stored away, we call \cs{mfp@thelabel} with no
+% parameters. Then the group (begun by \cs{mfp@label}) is ended, and the
+% \cs{extra@endtlabel} command invoked.
+% \begin{macrocode}
+\newdef\mfp@label#1{\begingroup\mfp@resetwhitespace\mp@mksharpother
+ \tlabeljustify{#1}\@ifnextchar({\mfp@label@}{\mfp@label@@}}%
+\newdef\mfp@label@(#1,#2){\mfp@label@@{(#1,#2)}}%
+\newdef\mfp@label@@#1#2{\def\tl@points{#1}\mfp@toks{#2}\mfp@thelabel
+ \endgroup\extra@endtlabel}%
+% \end{macrocode}
+%
+% At this point all the necessary information has been stored and
+% \cs{mfp@thelabel} merely branches depending on \opt{mplabels}.
+%
+% Clearly we could have put this code at the end of \cs{mfp@label@@} instead
+% of invoking this command. The point of doing things this way is so that
+% other commands too can store away the necessary info while doing other
+% things and simply call \cs{mfp@thelabel} to get the label placed.
+% \begin{macrocode}
+\newdef\mfp@thelabel{\@ifmplabels\mp@tlabel\tex@tlabel}%
% \end{macrocode}
%
-% \DescribeMacro{\extraendtlabel}
% We now come to the main code under \opt{mplabels}, which should just
% arrange to write the correct commands to the output file. All labels are
-% passed as \mfc{btex ... etex} structures. The definition of \cs{MFPtext}
-% will have been written earlier by \cs{opengraphsfile}.
-% \cs{extraendtlabel} is a hook currently only used by the \cs{tlabels}
-% command to force a loop.
+% passed as \mfc{btex ... etex} structures via the command \cs{mfp@btex}
+% below. The definition of \cs{MFPtext} will have been written earlier by
+% \cs{opengraphsfile}.
% \begin{macrocode}
- \newdef\mfp@btex##1{btex \noexpand\MFPtext{##1} etex}%
- \newdef\extraendtlabel{}%
+\newdef\mfp@btex#1{btex \noexpand\MFPtext{#1} etex}%
% \end{macrocode}
%
% The command \gbc{newgblabel}, defined in \file{graphbase.mp} for placing
@@ -4092,7 +5330,7 @@
% support the \texttt{B} vertical placement option, there would be only
% two. All the other options involve placement \emph{relative} to the
% bounding box. But the baseline is absolute: \MP{} always puts it at
-% y-coordinate 0 in the picture returned by \gbc{btex}. The first two
+% $y$ coordinate 0 in the picture returned by \gbc{btex}. The first two
% parameters are numerical and the third is boolean. The
% first represents the fraction of the text that lies left of given
% point. The second is the fraction of the text below the point. If the
@@ -4107,489 +5345,346 @@
%
% The fourth parameter of \gbc{newgblabel} is the angle of rotation. Empty
% rotation is converted to the angle 0, otherwise it is written as is.
-% All four parameters are stored in \cs{mfp@MPpos}.
+% All four parameters are stored in \cs{mfp@MPpos} by the invocation of
+% \cs{mfp@setMPpos}.
% \begin{macrocode}
- \newdef\mfp@setMPpos{%
- \edef\mfp@MPpos{%
- \ifcase\tl@hpos .5\or 1\else 0\fi, % horizontal
- \ifcase\tl@vpos 0\or .5\or 1\else 0\fi, % vertical
- \ifnum \tl@vpos < 0 \mf@T\else\mf@F\fi, % true=Baseline
- \mfp@mtsub{\tlabel@rot}{0}}}%
+\newdef\mfp@setMPpos{%
+ \edef\mfp@MPpos{%
+ \ifcase\tl@hpos .5\or 1\else 0\fi, % horizontal
+ \ifcase\tl@vpos0\or .5\or 1\else 0\fi, % vertical
+ \ifnum \tl@vpos<0 \mfp@T\else\mfp@F\fi, % true=Baseline
+ \mfp@emptysub{\tl@rot}{0}}}%
% \end{macrocode}
%
-% \cs{@@tlabel} is the code called by \cs{tlabel} when \opt{mplabels} is
-% in effect. The argument is a \MP{} pair expression, or comma separated
-% list of them. It should be followed by the \TeX{} code for the label.
-% We will load the label into a token register afterwards. First
-% we restore the meaning of blank lines and then make sure any
-% \texttt{\#}'s in the \TeX{} code are not doubled by changing their
-% category.
-%
-% We load the label in a token register because writing that is (almost)
-% verbatim (blank lines become \cs{par}, \texttt{\#} symbols are
-% doubled if we're not careful, and control names have a space appended).
-% One day maybe we'll try to obtain a true verbatim writing mode.
+% \cs{mp@tlabel} is the code called by \cs{tlabel} when \opt{mplabels} is
+% in effect. At this point all the data needed are stored in macros and
+% we just have to put it together. Thus, it has no parameters and other
+% commands can prepare the data and simply invoke it (or, more usually,
+% invoke \cs{mfp@thelabel} which invokes \cs{tex@tlabel} or \cs{mp@tlabel} as
+% appropriate). The \gbc{newgblabel} command is written with the
+% justification and two more parameters: the label tokens, and the point
+% (or list of points).
% \begin{macrocode}
- \newdef\@@tlabel##1{\def\mfp@points{##1}\mfp@restorepar
- \mfp@setMPpos \@makeother\#\mfp@gettoks\@@@tlabel}%
-% \end{macrocode}
-% Then the \gbc{newgblabel} command is written with the justification
-% parameters, and two more: the label tokens, and the point (or list of
-% points). The group (begun by \cs{tlabel}) is ended, and the
-% \cs{extraendtlabel} command invoked.
-% \begin{macrocode}
- \newdef\@@@tlabel{%
- \mfcmd{\@nl
- newgblabel(\mfp@MPpos)(\mfp@btex{\the\mfp@toks})(\mfp@points)}%
- \endgroup \extraendtlabel}%
+\newdef\mp@tlabel{\mfp@setMPpos
+ \mfcmd{\@nl newgblabel(\mfp@MPpos)(\mfp@btex{\the\mfp@toks})\@nl
+ (\tl@points)}}%
% \end{macrocode}
%
% When \opt{mplabels} is not in effect, we use \TeX{} to position the
-% label. By this point, the optional justification parameter to
-% \cs{tlabel} has been converted to integer values of \cs{tl@vpos} and
-% \cs{tl@hpos}, and the coordinates of the location are passed as the
-% first two arguments of \cs{@tlabel}. The third argument is the \TeX{}
-% code for the label.
-%
-% We put the label in a box (\cs{@textbox}) so we can measure and position
-% it. It is a \cs{vbox} so we can have multiline labels. This is effected
-% by wrapping it in a simple, left-aligned, one column \cs{halign},
-% defining \cs{\bsl} to be \cs{cr}. We make this procedure a separate
-% macro, since we need to do the identical thing in the \cs{tlabelpath}
-% code.
-% \begin{macrocode}
- \newdef\set@textbox##1{%
- \setbox\@textbox=\vbox{\def\\{\cr}\mfp@restorepar
- \@tcurr\the\every@tlabel\halign{####\hfil\cr##1\crcr}}}%
+% label. We put the label in a box (\cs{@textbox}) so we can measure and
+% position it. It is a \cs{vbox} so we can have multiline labels. This is
+% effected by wrapping it in a simple, left-aligned, one column
+% \cs{halign}, defining \cs{\bsl} to be \cs{cr}. We make this procedure a
+% separate macro, since we need to do the identical thing in the
+% \cs{tlabelpath} code.
+% \begin{macrocode}
+\newdef\set@textbox#1{%
+ \setbox\@textbox\vbox{\def\\{\cr}\mfp@restorepar
+ \@tcurr\@nameuse{@safe@activesfalse}\every@tlabel
+ \halign{##\hfil\cr#1\crcr}}}%
+% \end{macrocode}
+%
+% We call \cs{tex@tlabel} to perform the necessary positioning. Again, all
+% the necessary data has been stored so \cs{tex@tlabel} takes no parameters.
+% Before calling the (rather extensive) positioning code, we have to
+% process the (possible) list of points into separate points. Spaces
+% outside parentheses are consumed by \cs{@ifnextchar} and separating
+% commas (even if there are too many) are gobbled by the
+% \cs{mfp@ifendorcomma}. Oddly, this code works even if the separating commas
+% are omitted, but the \opt{mplabels} code does not.
+% \begin{macrocode}
+\newdef\tex@tlabel{\@xp\@textlabel\tl@points\mfp@end}%
+\newdef\@textlabel{\mfp@ifendorcomma{}\@textlabel\@@textlabel}%
+\newdef\@@textlabel(#1,#2){\tex@@tlabel{#1}{#2}\@textlabel}%
+% \end{macrocode}
+%
+% This is the code that sets and positions the label. We compute its total
+% height to simplify later code. The macro \cs{tb@ht} expands to
+% \cs{ht}\cs{@textbox} and \cs{tb@dp} to \cs{dp}\cs{@textbox}.
+% \begin{macrocode}
+\newdef\tex@@tlabel#1#2{%
+ \set@textbox{\the\mfp@toks}%
+ \mfp@scratch\tb@ht \advance\mfp@scratch\tb@dp
+ \tb@totalht\mfp@scratch
+ \@xconv{#1}\mfp@scratch
+% \end{macrocode}
+%
+% The following computes whether the leftmost extent of the picture will
+% be increased by the addition of the label. The macro \cs{tb@wd} expands
+% to \cs{wd}\cs{@textbox}.
+%
+% We have convert the $x$ coordinate of the label's location to a distance
+% from the left edge of the picture. We then subtract from that the amount
+% of the label extending to the left of this location. Then we add or
+% subtract the separation of the label from its location. Finally, we
+% simply add the horizontal adjustment defined by the user with
+% \cs{tlabeloffset}. The value of \cs{@tlabelsleft} is set equal to this
+% if it was greater. We save the calculated value in \cs{tb@moveright},
+% because it is exactly the amount we'll need to \cs{kern} later to
+% position the label.
+% \begin{macrocode}
+ \begingroup
+ \ifcase\tl@hpos \advance\mfp@scratch -0.5\tb@wd % center
+ \or \advance\mfp@scratch -\tb@wd % right
+ \advance\mfp@scratch -\tlabel@sep
+ \else \advance\mfp@scratch \tlabel@sep % left (default)
+ \fi
+ \advance\mfp@scratch \tlabel@hadj
+ \ifdim \mfp@scratch<\@tlabelsleft
+ \@tlabelsleft\mfp@scratch
+ \fi
+ \tb@moveright\mfp@scratch
+ \endgroup
% \end{macrocode}
%
-% We then compute its total height to simplify later code.
-% \begin{macrocode}
- \newdef\@tlabel##1##2##3{%
- \set@textbox{##3}%
- \mfp@scratch\tb@ht \advance\mfp@scratch\tb@dp
- \tb@totalht\mfp@scratch
+% Now we base the calculations on the uppermost extent of picture and
+% label. Otherwise the idea is the same, and we may increase
+% \cs{@tlabelstop}. Again we save the calculated result (in \cs{tb@raise})
+% for later positioning of the label.
+% \begin{macrocode}
+ \@yconv{#2}\mfp@scratch
+ \begingroup
+ \ifcase\tl@vpos \advance\mfp@scratch \tb@totalht % bottom
+ \advance\mfp@scratch \tlabel@sep
+ \or \advance\mfp@scratch 0.5\tb@totalht % center
+ \or \advance\mfp@scratch -\tlabel@sep % top
+ \else \advance\mfp@scratch \tb@ht % baseline (default)
+ \fi
+ \advance\mfp@scratch \tlabel@vadj
+ \ifdim \mfp@scratch>\@tlabelstop
+ \@tlabelstop\mfp@scratch
+ \fi
+ \tb@raise\mfp@scratch
+ \endgroup
% \end{macrocode}
%
-% The following computes whether the rightmost extent of the picture will
-% be increased by the addition of the label. We convert the x-coordinate
-% of the label's location to a distance from the left edge of the picture.
-% We then add to that the amount of the label extending to the right of
-% this location. Then we add or subtract the separation of the label
-% from its location. Finally, we simply add the horizontal adjustment
-% defined by the user with \cs{tlabeloffset}. The value of
-% \cs{@tlabelsright} is set equal to this if it was less. We could omit
-% this group: we never use \cs{@tlabelsright} except in debugging.
-%
-% The macro \cs{tb@wd} expands to \cs{wd}\cs{@textbox}.
+% Finally we compute the lowermost extent, and save it in \cs{@tlabelsbot}.
% \begin{macrocode}
- \mfp@scratch=##1\mfpicunit
- \@xconv\mfp@scratch
- \begingroup
- \ifcase\tl@hpos \advance\mfp@scratch 0.5\tb@wd % center
- \or \advance\mfp@scratch -\tlabel@sep % right
- \else \advance\mfp@scratch \tb@wd % left
- \advance\mfp@scratch \tlabel@sep
- \fi
- \advance\mfp@scratch \tlabel@hadj
- \ifdim \mfp@scratch>\@tlabelsright
- \@tlabelsright=\mfp@scratch
- \fi
- \endgroup
+ \begingroup
+ \ifcase\tl@vpos \advance\mfp@scratch \tlabel@sep % bottom
+ \or \advance\mfp@scratch -0.5\tb@totalht % center
+ \or \advance\mfp@scratch -\tb@totalht % top
+ \advance\mfp@scratch -\tlabel@sep
+ \else \advance\mfp@scratch -\tb@dp % baseline (default)
+ \fi
+ \advance\mfp@scratch \tlabel@vadj
+ \ifdim \mfp@scratch<\@tlabelsbot
+ \@tlabelsbot\mfp@scratch
+ \fi
+ \endgroup
% \end{macrocode}
%
-% The following is the same as the previous calculations, but with respect
-% to the leftmost extension of picture and label. Here, however, we save
-% the calculated value in \cs{tb@moveright}, because it is exactly the
-% amount we'll need to \cs{kern} later to position the label.
+% Now we've computed the new size of \cs{@alltlabels} and we put the
+% new label on top of it with the above calculated kerns.
% \begin{macrocode}
- \begingroup
- \ifcase\tl@hpos \advance\mfp@scratch -0.5\tb@wd % center
- \or \advance\mfp@scratch -\tb@wd % right
- \advance\mfp@scratch -\tlabel@sep
- \else \advance\mfp@scratch \tlabel@sep % left
- \fi
- \advance\mfp@scratch \tlabel@hadj
- \tb@moveright=\mfp@scratch
- \ifdim \mfp@scratch<\@tlabelsleft
- \@tlabelsleft=\mfp@scratch
- \fi
- \endgroup
+ \global \setbox\@alltlabels
+ \vtop{\unvbox\@alltlabels
+ \vbox to 0pt{\kern-\tb@raise
+ \hbox{\kern\tb@moveright\box\@textbox}\vss}}%
+}% End \tex@@tlabel
% \end{macrocode}
%
-% Now we base the calculations on the vertical extent of picture and
-% label. Otherwise the idea is the same, and we may increase
-% \cs{@graphtop}. The macro \cs{tb@ht} means \cs{ht}\cs{@textbox} and
-% \cs{tb@dp} means \cs{dp}\cs{@textbox}. Again we save the calculated
-% result (in \cs{tb@raise}) for later positioning of the label.
-% \begin{macrocode}
- \mfp@scratch=##2\mfpicunit
- \@yconv\mfp@scratch
- \begingroup
- \ifcase\tl@vpos \advance\mfp@scratch \tb@totalht % bottom
- \advance\mfp@scratch \tlabel@sep
- \or \advance\mfp@scratch 0.5\tb@totalht % center
- \or \advance\mfp@scratch -\tlabel@sep % top
- \else \advance\mfp@scratch \tb@ht % baseline
- \fi
- \advance\mfp@scratch \tlabel@vadj
- \tb@raise=\mfp@scratch
- \ifdim \mfp@scratch>\@tlabelstop
- \@tlabelstop=\mfp@scratch
- \fi
- \endgroup
-% \end{macrocode}
+% The argument of \cs{mfp@labels} is the ostensible argument of
+% \cs{tlabels}. Looping is accomplished by defining \cs{extra@endtlabel}
+% to repeat \cs{mfp@dolabel}.
%
-% Finally we compute the bottom-most extent.
+% If \cs{mfp@dolabel} sees the end-of-loop delimiter it ends the group and
+% gobbles the delimiter. Otherwise it calls \cs{tlabel}, which sees the
+% next set of arguments, places the label, and repeats the loop.
% \begin{macrocode}
- \begingroup
- \ifcase\tl@vpos \advance\mfp@scratch \tlabel@sep % bottom
- \or \advance\mfp@scratch -0.5\tb@totalht % center
- \or \advance\mfp@scratch -\tb@totalht % top
- \advance\mfp@scratch -\tlabel@sep
- \else \advance\mfp@scratch -\tb@dp % baseline
- \fi
- \advance\mfp@scratch \tlabel@vadj
- \ifdim \mfp@scratch<\@tlabelsbot
- \@tlabelsbot=\mfp@scratch
- \fi
- \endgroup
+\newdef\mfp@labels#1{\let\extra@endtlabel\mfp@dolabel
+ \mfp@dolabel#1\mfp@end}%
+\newdef\mfp@dolabel{\mfp@ifend\endgroup\tlabel}%
% \end{macrocode}
%
-% Now we've computed the new size of \cs{@alltlabels} and we put the
-% new label on top of it with the above calculated kerns.
+% \cs{mfp@labelpath} stores the name of the path in \cs{mfp@name} and the
+% default argument in \cs{mfp@param}, then check for a \texttt{*} and set
+% a switch based on it before calling \cs{mfp@tlpath}. The
+% \cs{mfp@figmacbase} must be outside the group so the switches it sets
+% will survive for later commands to detect.
% \begin{macrocode}
- \global \setbox\@alltlabels=%
- \vtop{\unvbox\@alltlabels
- \vbox to 0pt{\kern-\tb@raise
- \hbox{\kern\tb@moveright\box\@textbox}\vss}}%
- \endgroup % group begun in \tlabel
- \extraendtlabel}% End \@tlabel
+\newdef\mfp@labelpath#1#2{\mfp@figmacbase
+ \begingroup % ended in \tlabelpath@
+ \def\mfp@name{#1}\def\mfp@param{#2}%
+ \mfp@ifstar\mfp@tlpath\mfp@switchtrue\mfp@switchfalse}%
% \end{macrocode}
%
-% \subsection{Surrounding the label with a path}\label{surrounding}
-%
-% These next macros place labels, but also define a path surrounding the
-% label This allows one to clear a space for the label, create a
-% background, draw a box around it, etc. They take two optional arguments.
-% The first optional argument depends on the type of path being created.
-% It is described below. The second one is the justification as in the
-% \cs{tlabel} command. There are two mandatory arguments, the location
-% (same syntax as for \cs{tlabel}) and the text. They also have
-% star-forms, which create the path but doesn't place the label.
-%
-% All the curves are sized to just contain what I will call the
-% \emph{label strip}. Imagine a rectangle that just fits around the text.
-% Now extend it on all sides by the value defined with \cs{tlabelsep}.
-% This rectangle is the label strip.
-%
-% \DescribeMacro{\tlabelrect}
-% \cs{tlabelrect} creates the rectangular boundaries of the label strip by
-% default. With the optional argument a rectangle with rounded corners is
-% produced; the optional argument is the radius of the quarter circles used.
-% These quarter circles pass through the corners of the label strip and are
-% joined together with straight lines to create the rest of the rectangle.
-% As a special case, the optional argument can contain the keyword
-% `\texttt{roundends}' to produce a rectangle with the short sides
-% replaced by a semicircle.
-%
-% \DescribeMacro{\tlabeloval}
-% \cs{tlabeloval} produces an ellipse which, by default has the same
-% aspect ratio as the label strip. It is sized to just include that label strip,
-% passing through its four corners. The optional argument contains a
-% numerical factor $m$ that, if greater than 1, increases the width and
-% decreases the height to maintain this relationship with the label strip,
-% while multiplying the width to height ratio by $m$. The default value of
-% $m$ is 1.
-%
-% \DescribeMacro{\tlabelellipse}
-% \cs{tlabelellipse} produces an ellipse, where the optional argument $a$
-% is the ratio of its width to its height. The default for $a$ is 1, which
-% produces a circle.
-%
-% \DescribeMacro{\tlabelcircle}
-% To get a circle the user can \cs{tlabelellipse} without the optional
-% argument, or with argument \oarg{1}. However, it makes for a more
-% readable source if the command reflects the shape. \cs{tlabelcircle} is
-% defined to be essentially \cs{tlabelellipse}\oarg{1}, but it first
-% checks for the \texttt{*} and passes it along in front of the \oarg{1}.
-%
-% We implement the other three by calling \cs{tlpath} with the name of the
-% curve (\texttt{rect}, \texttt{oval}, or \texttt{ellipse}) and the
-% default value of the optional argument (\texttt{0}, \texttt{1}, and
-% \texttt{1}, respectively) as arguments.
+% Process the first optional argument, which has value of \cs{mfp@param}
+% as default. The second one is a justification with a default set by the
+% user with \cs{tlpathjustify} or the initial \texttt{[cc]} setting. Once
+% these are processed, we check whether a parentheses follows, calling one
+% of two different handlers for the location parameter(s).
% \begin{macrocode}
- \newdef\tlabelrect{\tlpath{rect}{0}}%
- \newdef\tlabeloval{\tlpath{oval}{1}}%
- \newdef\tlabelellipse{\tlpath{ellipse}{1}}%
- \newdef\tlabelcircle{%
- \mfp@ifnextchar*{\@firstoftwo{\tlabelellipse*[1]}}%
- {\tlabelellipse[1]}}%
-% \end{macrocode}
-%
-% We store the name of the path in \cs{mfp@name} and the default argument
-% in \cs{mfp@param}, then check for a \texttt{*} and set a switch based
-% on it before calling \cs{@tlpath}. The \cs{@figmacstart} must be outside
-% the group so the switches it sets will survive for later commands to
-% detect.
-% \begin{macrocode}
- \newdef\tlpath##1##2{\@figmacstart
- \begingroup % ended in \tlabelpath@
- \def\mfp@name{##1}\def\mfp@param{##2}%
- \mfp@ifnextchar*%
- {\@mfpswitchtrue\@firstoftwo{\@tlpath}}% gobbles the star
- {\@mfpswitchfalse\@tlpath}}%
-% \end{macrocode}
-% Process first optional argument. First one has value of \cs{mfp@param}
-% as default. The second one has a default justification set by the user
-% with \cs{tlpathjustify} or the initial \texttt{[cc]} setting. Once these
-% are processed, we check whether a parentheses follows, calling one of
-% two different handlers for the location parameter(s).
-% \begin{macrocode}
- \newdef\@tlpath{\null@opt\@@tlpath}%
- \newdef\@@tlpath[##1]{%
- \edef\mfp@param{\mfp@mtsub{##1}{\mfp@param}}%
- \mfp@resetwhitespace
- \simple@opt\@@@tlpath{\tlpath@v\tlpath@h\tlpath@r}}%
- \newdef\@@@tlpath##1{\tlabeljustify{##1}%
- \mfp@setMPpos \mfp@ifnextchar({\@tlabelpath}{\@@tlabelpath}}%
+\newdef\mfp@tlpath#1{#1\mfp@defaultopt\mfp@@tlpath\mfp@param}%
+\newdef\mfp@@tlpath#1{\edef\mfp@param{#1}%
+ \mfp@defaultopt\mfp@tlpath@{\tlpath@v\tlpath@h\tlpath@r}}%
+\newdef\mfp@tlpath@#1{\mfp@resetwhitespace\mp@mksharpother
+ \tlabeljustify{#1}\@ifnextchar({\@tlabelpath}{\@@tlabelpath}}%
% \end{macrocode}
%
% These handlers just stuff the point into a macro and call the command
-% that reads the label code. The name \cs{mfp@points} is plural (even
+% that reads the label code. The name \cs{tl@points} is plural (even
% though only one point is supported) because the internal command that
-% places the label expects this macro. We use \cs{@firstofone} so
-% that the braces will not be written to the output, but they will
-% function as braces for \TeX's parameter matching.
-% \begin{macrocode}
- \newdef\@tlabelpath (##1,##2){%
- \def\mfp@points{(\@firstofone{##1},\@firstofone{##2})}%
- \@@@tlabelpath}%
- \newdef\@@tlabelpath##1{\def\mfp@points{##1}\@@@tlabelpath}%
- \newdef\@@@tlabelpath{\mfp@restorepar
- \@ifmplabels{\@makeother\#}{}\mfp@gettoks\tlabelpath@}%
-% \end{macrocode}
-%
-% Under \opt{mplabels} we just call on \MP{} to do everything (including
-% measuring the text). Otherwise we measure the text in \TeX{} and feed
-% the bounding box corners to \MF{} for making the path. Then we optionally
-% place the text based on the switch previously set. All the data needed
-% have already been stored in macros by previous commands in the chain.
-% \begin{macrocode}
- \newdef\tlabelpath@{%
- \@ifmplabels
- {\mfcmd{text\mfp@name x (\mfp@MPpos)\@nl(origin, % dummy parameter
- \mfp@btex{\the\mfp@toks}, \mfp@param, \mfp@points)}}%
- {\measure@textbox{\the\mfp@toks}%
- \mfcmd{text\mfp@name x (\mfp@MPpos)\@nl(\tb@ll, \tb@ur, % text bbox
- \mfp@param, \mfp@points)}}%
- \if@mfpswitch \endgroup % *-form, no label placed
- \else % tlabel commands contain \endgroup
- \@ifmplabels{\@@@tlabel}%
- {\@xp\@@tlabel@\mfp@points{\the\mfp@toks}}%
- \fi}%
-% \end{macrocode}
-%
-% Here we define the command that measures the text. It is almost a clone
-% of a similar code sequence in \cs{@tlabel}, but it produces the \gbc{ll}
-% and \gbc{ur} (corners of the boundingbox) that the \grafbase{} code
-% needs.
+% places the label expects this macro.
% \begin{macrocode}
- \newdef\measure@textbox##1{%
- \set@textbox{##1}%
- \edef\tb@ll{(0, -(\the\tb@dp))}%
- \edef\tb@ur{(\the\tb@wd, \the\tb@ht)}}%
+\newdef\@tlabelpath(#1,#2){\@@tlabelpath{(#1,#2)}}%
+\newdef\@@tlabelpath#1#2{%
+ \def\tl@points{#1}\mfp@toks{#2}\tlabelpath@}%
% \end{macrocode}
%
-% \subsection{Placing multiple text labels}\label{multiple}
-%
-% \DescribeMacro{\tlabels}
-% One of the more onerous things about labels in \env{mfpic} environments
-% was the shear number of them. The following was intended to reduce the
-% amount of typing by having to repeat only the arguments. \cs{tlabels}
-% takes only one argument: any sequence of valid arguments for a \cs{tlabel}
-% command, optionally separated by whitespace (but not blank lines).
+% By this point, all the elements we need are gathered. For the purposes
+% of the label we have \cs{mfp@toks} holding the label code,
+% \cs{tl@points} holding the location, and \cs{tlabeljustify} encoding
+% the adjustments. For the path, we have the type in \cs{mfp@name}, and the
+% one optional parameter parameter in \cs{mfp@param}.
%
-% We begin a group, reset whitespace to normal, and call the command that
-% reads the argument. That command arranges to make the main code
-% self-looping by defining \cs{extraendtlabel} to call the beginning of the
-% loop, then it feeds its argument to \cs{do@tlabels} followed by a
-% delimiter to signal the end of the loop.
+% Under \opt{mplabels} we expect \MP{} to measure the text. Otherwise we
+% measure the text in \TeX{} and feed the bounding box corners to \MF{}
+% for making the path. If \cs{ifmfp@switch} is false we place the text by
+% calling \cs{mfp@thelabel}. All the data needed by \cs{mfp@thelabel} have
+% already been stored in macros by previous commands in the chain. Then we
+% complete the command by ending the group that \cs{mfp@labelpath}
+% started.
% \begin{macrocode}
- \newdef\tlabels{%
- \begingroup % ended in \do@tlabels
- \mfp@resetwhitespace \@tlabels}%
- \newdef\@tlabels##1{\def\extraendtlabel{\do@tlabels}%
- \do@tlabels##1\mfp@delim}%
+\newdef\tlabelpath@{\@ifmplabels{}{\measure@textbox{\the\mfp@toks}}%
+ \mfp@setMPpos
+ \mfcmd{text\mfp@name x (\mfp@MPpos)\@nl
+ (\@ifmplabels{origin, \mfp@btex{\the\mfp@toks}}{\tb@ll, \tb@ur},
+ \mfp@param) (\tl@points)}%
+ \ifmfp@switch\else \mfp@thelabel\fi \endgroup}%
% \end{macrocode}
%
-% If \cs{do@tlabels} sees the end-of-loop delimiter it ends the group and
-% gobbles the delimiter. Otherwise it calls \cs{tlabel}, which sees the
-% next set of arguments, and repeats the loop.
+% Here we define the command that measures the text. It is almost a clone
+% of a similar code sequence in \cs{tex@tlabel}, but it produces the \gbc{ll}
+% and \gbc{ur} (corners of the boundingbox) that the \grafbase{} code
+% needs.
% \begin{macrocode}
- \newdef\do@tlabels{%
- \mfp@ifnextchar\mfp@delim{\@firstoftwo\endgroup}{\tlabel}}%
+\newdef\measure@textbox#1{\set@textbox{#1}%
+ \mfp@scratch-\tb@dp
+ \edef\tb@ll{(0, \the\mfp@scratch)}%
+ \edef\tb@ur{(\the\tb@wd, \the\tb@ht)}}%
% \end{macrocode}
%
-% Labelling axes generates a lot of \cs{tlabel} commands or a single
-% \cs{tlabels} command with a lot of arguments. Given that such labels
-% have much in common (e.g., for the x-axis the y-coordinate and the
-% justification are usually the same for all the labels) we can do still
-% better at abbreviating this task.
+% The first four argumenmts of \cs{mfp@axislabels} are the edges of the
+% surrounding \env{mfpic} environment (left, right, bottom, top). The
+% fifth is the axis letter.
%
-% \DescribeMacro{\axislabels}
-% The \cs{axislabels} command takes a mandatory argument (the axis)
-% followed by an optional argument (the justification, as in the
-% \cs{tlabel} command), followed by another mandatory argument, a comma
-% separated list of items of the form
-% \marg{\meta{label}}\meta{coordinate}. The label is placed at that
-% coordinate on the given axis. This command is complicated by the fact
-% that the default justification should be different for each axis (for
-% example, for the y-axis it is \oarg{cr} but for the x-axis it is
-% \oarg{tc}). For all of them, one of the parts is \texttt{c}, so after
-% resetting whitespace we initialize them both to this and later change
-% only the one that needs it.
+% We start by determining the default justification. which is different
+% for each axis (for example, for the left axis it is \oarg{cr} but for
+% the bottom axis it is \oarg{tc}). For all of them, one of the parts is
+% \texttt{c}, so after resetting whitespace we initialize them both to
+% this and later change only the one that needs it.
%
% Also, one of the coordinates of a label will be given by the argument,
% while the other is the location of the axis. However, which coordinate
-% is which is different for different axes. What we do is feed the
-% argument to \cs{mfp@xcoord} and \cs{mfp@ycoord}. Both are initialized to
+% is which is different for different axes. What we do is feed this
+% position to \cs{mfp@xcoord} and \cs{mfp@ycoord}. Both are initialized to
% the identity macro, but one is changed to expand to just the axis
-% position.
-%
-% The axis position for x- and y-axis is 0. For border axes it is
-% calculated by adding the border axis shift to the border coordinate.
-% \begin{macrocode}
- \newdef\axislabels##1{%
- \begingroup % ended in \do@axislabel
- \mfp@resetwhitespace \tlabeljustify{cc}%
- \let\mfp@xcoord=\@firstofone \let\mfp@ycoord=\@firstofone
- \get@axisnum{##1}\axislabels
- \ifcase\mfp@axisnum \tl@vpos=2 % x-axis -->[tc]
- \def\mfp@ycoord{0}%
- \or \tl@hpos=1 % y-axis-->[cr]
- \def\mfp@xcoord{0}
- \or \tl@hpos=1 % left -->[cr]
- \edef\mfp@xcoord{\mfp@lshift}\mfpaddto\mfp@xcoord{#3}%
- \or \tl@vpos=2 % bottom-->[tc]
- \edef\mfp@ycoord{\mfp@bshift}\mfpaddto\mfp@ycoord{#5}%
- \or \tl@hpos=-1 % right -->[cl]
- \edef\mfp@xcoord{-\mfp@rshift}\mfpaddto\mfp@xcoord{#4}%
- \or \tl@vpos=0 % top -->[bc]
- \edef\mfp@ycoord{-\mfp@tshift}\mfpaddto\mfp@ycoord{#6}%
+% position. The axis position for $x$ and $y$ axis is 0. For border axes
+% it is calculated by adding the border axis shift to the border
+% coordinate.
+% \begin{macrocode}
+\newdef\mfp@axislabels#1#2#3#4#5{%
+ \begingroup % ended in \mfp@(@)doaxislabel
+ \mfp@resetwhitespace
+ \tl@vpos1 \tl@hpos0
+ \let\mfp@xcoord\@firstofone \let\mfp@ycoord\@firstofone
+ \mfp@getaxis#5\relax\mfp@end\axislabels
+ \ifcase\mfp@axisnum\relax
+ \tl@vpos2 \def\mfp@ycoord{0}\or
+ \tl@hpos1 \def\mfp@xcoord{0}\or
+ \tl@hpos1\edef\mfp@xcoord{\mfp@lshift}%
+ \mfpaddto\mfp@xcoord{#1}\or
+ \tl@vpos2\edef\mfp@ycoord{\mfp@bshift}%
+ \mfpaddto\mfp@ycoord{#3}\or
+ \tl@hpos-1 \edef\mfp@xcoord{-\mfp@rshift}%
+ \mfpaddto\mfp@xcoord{#2}\or
+ \tl@vpos0 \edef\mfp@ycoord{-\mfp@tshift}%
+ \mfpaddto\mfp@ycoord{#4}%
\fi
% \end{macrocode}
%
% Now one of \cs{mfp@xcoord} or \cs{mfp@ycoord} expands to the axis
-% position. We change it so that it does this while gobbling a parameter.
-% And then call \cs{@axislabels}, feeding it an empty argument as the
-% default optional argument.
+% position. We change it so that it does this while gobbling the
+% parameter, and then call \cs{mfp@@axislabels}, feeding it an empty
+% argument as the default optional argument. We also give \texttt\# a safe
+% catcode for writing to a \file{.mp} file.
% \begin{macrocode}
\ifx\@firstofone\mfp@xcoord
- \edef\mfp@ycoord####1{\mfp@ycoord}%
+ \edef\mfp@ycoord##1{\mfp@ycoord}%
\else
- \edef\mfp@xcoord####1{\mfp@xcoord}%
- \fi \null@opt\@axislabels}%
-% \end{macrocode}
-%
-% \cs{@axislabels} processes the contents of the optional parameter and
-% calls \cs{do@axislabel} on the list in the argument. An additional item
-% is added that signals the end of the loop.
-% \begin{macrocode}
- \newdef\@axislabels[##1]##2{%
- \tlabeljustify{##1}%
- \do@axislabel~ ##2,\mfp@delim}%
-% \end{macrocode}
-%
-% We add some code that traps the most common typos: a comma between the
-% label and the coordinate, or a missing coordinate, or a doubled comma.
-% It seems impossible to detect which of the first two has happened, and
-% assuming the first case produces really awful results if the second is
-% true. Therefore when a coordinate seems to be missing, we just lose the
-% text (as we have no place to put it) and resume the loop. If the first
-% case is true, the next iteration will see only the number, read one
-% digit or decimal as the text and the rest as the coordinate. The output
-% will be wrong, but at least \TeX{} can proceed.
-%
-% Above we prepended a `\verb*+~ +' so that if the coordinate is missing,
-% the braces around the argument won't be lost; the space will be
-% consumed in the argument processing of \cs{do@@axislabel}.
-%
-% The first argument should be the label, and everything from that to the
-% comma is the location. In the above error cases, the second argument
-% is empty
-% \begin{macrocode}
- \newdef\do@axislabel~##1,{%
- \@ifmtarg{##1}{\x@axislabel}{\do@@axislabel##1\@nil}}%
- \newdef\do@@axislabel##1##2\@nil{%
- \@ifmtarg{##2}%
- {\Mfpic@warn{Possible extra comma, missing coordinate, or
- missing\@msgbreak braces in \string\axislabels\@online.
- Skipping this label.}}%
- {\tlabel(\mfp@xcoord{##2},\mfp@ycoord{##2}){##1}}%
- \x@axislabel}%
- \newdef\x@axislabel{% check for end of list
- \mfp@ifnextchar\mfp@delim{\@firstoftwo\endgroup}{\do@axislabel~ }}%
+ \edef\mfp@xcoord##1{\mfp@xcoord}%
+ \fi \mp@mksharpother\mfp@nullopt\mfp@@axislabels}%
% \end{macrocode}
%
-% \subsection{Captions}\label{captions}
-%
-% Captions are not added immediately by the \cs{tcaption} command. That
-% command redefines \cs{@docaption} to add it, and \cs{endmfpic} executes
-% \cs{@docaption}. For now, \cs{@docaption} does nothing.
+% \cs{mfp@@axislabels} processes the contents of the optional parameter
+% and calls \cs{mfp@doaxislabel} on the list of labels.
% \begin{macrocode}
- \newdef\@docaption{}%
+\newdef\mfp@@axislabels#1#2{%
+ \tlabeljustify{#1}\mfp@doaxislabel#2,\mfp@end}%
% \end{macrocode}
%
-% \DescribeMacro{\tcaption}
-% \cs{tcaption} takes an optional argument that holds two numbers,
-% separated by a comma. The first number determines the maximum width of a
-% one line caption. Captions larger than that multiple of the width of the
-% graphic will be reset as a paragraph, whose lines have length the second
-% number times the width of the graph. The defaults are \oarg{1.2,1}.
+% \cs{mfp@doaxislabel} checks for the end delimiter or a comma (the latter
+% is an error). In the first case it ends all label processing, in the
+% second it repeats itself, otherwise it continues with
+% \cs{mfp@@doaxislabel}.
+%
+% The argument of \cs{mfp@@doaxislabel} should be the label. This command
+% stores the label in \cs{mfp@toks}, then again checks for the delimiter
+% or an comma (both are errors). In the first case it ends the process; in
+% the second it goes back to \cs{mfp@doaxislabel} for another try. In both
+% cases the label is discarded as we don't know where to put it. If
+% neither the delimiter nor a comma occurs, \cs{mfp@mkaxislabel} is run.
+%
+% The argument of \cs{mfp@mkaxislabel} (everything to the next comma) is the
+% location. The command \cs{mfp@mkaxislabel} does nothing if the location
+% parameter is empty, otherwise it stores the location as a pair and calls
+% \cs{mfp@thelabel} to place the label. Finally, it repeats
+% \cs{mfp@doaxislabel} for the next iteration.
+% \begin{macrocode}
+\newdef\mfp@doaxislabel{%
+ \mfp@ifendorcomma\endgroup\mfp@doaxislabel\mfp@@doaxislabel}%
+\newdef\mfp@@doaxislabel#1{\mfp@toks{#1}%
+ \mfp@ifendorcomma\endgroup\mfp@doaxislabel\mfp@mkaxislabel}%
+\newdef\mfp@mkaxislabel#1,{%
+ \@ifmtarg{#1}{}%
+ {\edef\tl@points{(\mfp@xcoord{#1},\mfp@ycoord{#1})}\mfp@thelabel}%
+ \mfp@doaxislabel}%
+% \end{macrocode}
%
-% \cs{@tcaption} has one mandatory argument, the caption's text. In it, the
+% \cs{mfp@caption} has one mandatory argument, the caption's text. In it, the
% \cs{\bsl} command forces line breaks. This works by forcing the width to
% be greater than any reasonable setting of the maximum line width. When
% this happens, the caption is reset in a \cs{vbox} and \cs{\bsl} is
% redefined like a normal line break.
% \begin{macrocode}
- \newdef\tcaption{\simple@opt\tcaption@{1.2, 1.0}}%
- \newdef\tcaption@##1{\@tcaption##1\mfp@delim}%
- \long\def\@tcaption##1,##2\mfp@delim##3{%
- \def\@docaption{%
- \setbox\@textbox=%
- \hbox{\def\\{\hskip\@M\p@}\mfp@restorepar
- \@tcurr##3}%
+\newdef\mfp@caption#1{\begingroup\mfp@resetwhitespace
+ \mfp@@caption#1\mfp@end}%
+\long\def\mfp@@caption#1,#2\mfp@end#3{\endgroup
+ \def\mfp@docaption{%
+ \setbox\@textbox
+ \hbox{\def\\{\hskip\@M\p@\let\\\relax}\mfp@restorepar
+ \@nameuse{@safe@activesfalse}\@tcurr#3}%
% \end{macrocode}
%
% If the above \cs{hbox} is too wide, we re-assign \cs{@textbox} to a
% \cs{vbox} with \cs{hsize} determined by the second part of the optional
-% argument, and with \cs{leftskip} and \cs{rightskip} defined according
-% to the setting of \opt{centeredcaptions}.
-% \begin{macrocode}
- \@graphwd=\wd\@wholegraph
- \ifdim\tb@wd>##1\@graphwd
- \setbox\@textbox=%
- \hbox{%
- \vbox{%
- \if@mfp@latex % keep LaTeX's `\\'
- \else
- \def\\{\unskip\hbox{}\hfil\penalty-\@M\ignorespaces}%
- \fi
- \if@mfp@centcapt
- \leftskip=0pt plus 0.5fil
- \rightskip=0pt plus -0.5fil
- \parfillskip=0pt plus 1fil
- \else
- \leftskip=0pt
- \rightskip=0pt
- \fi
- \hsize=##2\@graphwd \mfp@restorepar
- \noindent\@tcurr ##3%
- }% End vbox.
- }% End hbox.
- \fi
+% argument. \cs{mfp@capsettings} implements the \opt{centeredcaptions}
+% and \opt{raggedcaptions} options.
+% \begin{macrocode}
+ \mfp@scratch\wd\@wholegraph
+ \ifdim\tb@wd>#1\mfp@scratch
+ \setbox\@textbox
+ \hbox{%
+ \vbox{%
+ \hsize#2\mfp@scratch \mfp@restorepar
+ \leftskip0pt
+ \rightskip0pt
+ \mfp@capsettings
+ \@nameuse{@safe@activesfalse}\noindent\@tcurr #3%
+ }% End vbox.
+ }% End hbox.
+ \fi
% \end{macrocode}
%
% Here we determine the wider of the graph or the caption, then put the
@@ -4597,43 +5692,54 @@
% width, stacked one atop the other with \cs{mfpiccaptionskip} (default:
% \cs{medskipamount}) in between.
% \begin{macrocode}
- \mfp@scratch=\wd\@wholegraph
- \ifdim \mfp@scratch<\tb@wd \mfp@scratch=\tb@wd \fi
- \global\setbox\@wholegraph=%
- \vbox{\hbox to \mfp@scratch{\hss\box\@wholegraph\hss}%
- \nointerlineskip \vskip\mfpiccaptionskip
- \hbox to \mfp@scratch{\hss\box\@textbox\hss}}% End vbox.
- }% End \@docaption.
-% \ignorespaces
- }% End \@tcaption.
-% \ignorespaces
-}% End of \@mfpic
+ \ifdim \mfp@scratch<\tb@wd \mfp@scratch\tb@wd \fi
+ \global\setbox\@wholegraph
+ \vbox{\hbox to \mfp@scratch{\hss\box\@wholegraph\hss}%
+ \nointerlineskip \vskip\mfpiccaptionskip
+ \hbox to \mfp@scratch{\hss\box\@textbox\hss}}% End vbox.
+ }% End \mfp@docaption.
+}% End \mfp@@caption.
% \end{macrocode}
%
+% The following is used in \cs{endmfpic}. The first argument of
+% \cs{mfp@adjustdim} is a border dimension of the box containing all the
+% labels, the second is a comparison between that and the corresponding
+% dimension of the graph box, which is the third argument. The last is the
+% macro holding the adjustments needed when \opt{truebbox} is used. The
+% first and last line compensate for the fact that the bbox need not start
+% at $(0,0)$. The second line checks if the label box extends past the
+% graph's edge and adjusts the graph's dimension is it does.
+% \begin{macrocode}
+\def\mfp@adjustdim#1#2#3#4{%
+ \mfp@scratch#1\relax \advance\mfp@scratch-#4 bp
+ \ifdim\mfp@scratch#2#3\relax #3\mfp@scratch \fi
+ #1\mfp@scratch}%
+% \end{macrocode}
%
% \section{Placing the picture: \cs{endmfpic}}\label{endmfpic}
%
+% \DescribeMacro{\endmfpic}
% By the time \cs{endmfpic} is reached, the \MF{} code for the figure has
% all been written to the output file. The labels have all been
% accumulated and arrangements have been made to add the caption.
%
-% \DescribeMacro{\endmfpic}
-% Under \opt{metapost}, write \cs{endgroup} to match beginning written
+% Under \opt{metapost}, write a right brace to match the left written
% by \cs{mfpic} Then write the \cs{grafbase} command \gbc{endmfpic},
% followed by a comment telling which figure number it is, and the line
% number in the \TeX{} source where the \cs{endmfpic} occurred.
% \begin{macrocode}
\mfp@DBlog{Definition of closure of mfpic environment.}%
-\newdef\endmfpic{%
- \mfsrc{\mfp@ifmpost{\@nl verbatimtex \endgroup\space etex;}{}\@nl
- endmfpic;\mf@p (\number\mfp@count) \space\@mfplineno.\@nl
- \mf@p\mf@d\mf@d\mf@d\@nl}%
+\newdef\endmfpic{\every@endmfpic
+ \mfsrc{\mfp@ifmpost{\@nl verbatimtex \egroup\space etex;}{}\@nl
+ endmfpic; \mfp@p\space(\number\mfp@count) \mfp@lineno.\@nl
+ \mfp@p\mfp@d\mfp@d\mfp@d\@nl}%
\ifmfpicdebug\wlog{}\wlog{Mfpic: ENTERED endmfpic.}\wlog{}\fi
% \end{macrocode}
+%
% The code to load and place the box is skipped if figure shipping was
% turned off.
% \begin{macrocode}
- \if@shipping %
+ \ifmfp@noship \else
% \end{macrocode}
%
% \subsection{Loading and measuring the graphic}\label{loading}
@@ -4644,7 +5750,7 @@
% equivalent of issuing \cs{opengraphsfile} at every \cs{mfpic} and
% \cs{closegraphsfile} at every \cs{endmfpic}.)
% \begin{macrocode}
- \global\setbox\@wholegraph=%
+ \global\setbox\@wholegraph
\vbox{%
% \end{macrocode}
%
@@ -4653,66 +5759,61 @@
% \begin{macrocode}
\ifmfpicdraft
\vbox to \@graphtop{\vss
- \hbox to \@graphwd{\kern2pt\tt\#\number\mfp@count\hss}%
- \kern2pt}%
+ \hbox to \@graphright{%
+ \kern2pt \mfpdraftfont\#\number\mfp@count\hss}%
+ \kern2pt}%
\else
% \end{macrocode}
%
% The \opt{truebbox} option works at the \MP{} level. Without it the
% height of the EPS bounding box will actually be the calculated value
-% of \cs{@graphtop} (but increased by up to \texttt{2bp} due to rounding).
-% With it, the box will be its natural height. In either case, the actual
-% height is what we use in the \MP{} case. For \MF{} we use the nominal
-% height calculated in \cs{mfpic}.
+% of \cs{@graphtop}. With it, the box will be its natural height. But
+% in either case, the value will actually be increased by up to
+% \texttt{2bp} because of rounding, so the bounding box height is what we
+% use in the \MP{} case. For \MF{} we use the nominal height calculated in
+% \cs{mfpic}.
% \begin{macrocode}
- \mfp@ifmpost{\vbox}{\vbox to \@graphtop}%
- {\vss
+ \mfp@ifmpost{\vbox}{\vbox to \@graphtop}%
+ {\vss
% \end{macrocode}
%
% \cs{@graphfilename} holds the name of the \MP{} output figure. (It will be
% ignored by the \opt{metafont} option.) We test if the file exists before
% doing the graphic inclusion because of the large number of confusing error
% messages many graphic inclusion packages generate otherwise. When absent
-% we substitute the figure number for \cs{@setmfpicgraphic}. When present,
-% and if in \LaTeXe, we arrange for the current numerical extension to be
-% accepted as an EPS or MPS file by the \cs{includegraphics} command of the
-% graphics package.
-% \begin{macrocode}
- \edef\@graphfilename{\setfilename{\mfp@filename}%
- {\number\mfp@count}}%
- \mfp@ifmpost
- {\openin\mfp@graph=\@graphfilename\relax
- \ifeof\mfp@graph
- \Mfpic@msg{No graph file: \@graphfilename\space.}%
- \def\@setmfpicgraphic##1{\raise2pt\hbox{\kern2pt\tt ##1}}%
- \closein\mfp@graph
- \else\if@mfp@latexe
- \@namedef{Gin@rule@.\number\mfp@count}##1{%
- {\mfp@Gtype}{.\number\mfp@count}{##1}}%
- \fi\fi}{}%
+% we substitute the figure filename for \cs{@setmfpicgraphic}.
+% \begin{macrocode}
+ \edef\@graphfilename{%
+ \setfilename\mfp@filename{\number\mfp@count}}%
+ \mfp@ifmpost
+ {\IfFileExists{\@graphfilename}{}%
+ {\Mfpic@msg{No graph file: \@graphfilename\space.}%
+ \let\@setmfpicgraphic\nofile@setmfpgraphic}%
% \end{macrocode}
%
% We put the graphic in an \cs{hbox} of the required width and close off
% the boxes.
% \begin{macrocode}
- \mfp@ifmpost{\hbox}{\hbox to \@graphwd}%
- {\@xp\@setmfpicgraphic\@xp{\@graphfilename}\hss}% End hbox.
- \kern0pt
+ \hbox}%
+ {\hbox to \@graphright}%
+ {\@xp\@setmfpicgraphic\@xp{\@graphfilename}\hss}%
+ \kern0pt
}% End vbox
\fi
- }%% End vbox (\setbox\@wholegraph)
+ }%% End \setbox\@wholegraph \vbox
% \end{macrocode}
%
% We change the horizontal and vertical dimensions under \opt{metapost} in
% case \opt{truebbox} was in effect, but also because they are almost always
% rounded up to whole numbers of \texttt{bp} and therefore, even without
-% \opt{truebbox} the calculated values needn't be correct.
+% \opt{truebbox}, the calculated values needn't be correct.
% \begin{macrocode}
\mfp@ifmpost
- {\@graphright=\wd\@wholegraph
- \@graphtop=\ht\@wholegraph
- \@graphwd=\@graphright}{}%
+ {\@graphright\wd\@wholegraph
+ \@graphtop\ht\@wholegraph}{}%
\ifmfpicdebug
+ \wlog{Mfpic debug: Dimensions of wholegraph before labels
+ are added, and before truebbox adjustments:}%
\wlog{Mfpic: graphleft = \the\@graphleft}%
\wlog{Mfpic: graphright = \the\@graphright}%
\wlog{Mfpic: graphtop = \the\@graphtop}%
@@ -4726,52 +5827,46 @@
%
% \subsection{Adding labels and caption}\label{addlabel}
%
-% We now add the box \cs{@alltlabels} onto the graph box. The box should
-% still have 0pt height and depth, so we can just stick it under the graph
+% We now add the boxes \cs{@alltlabels} and \cs{@backtext} onto the graph
% box. But first we have to adjust for \cs{mfpicllx} and \cs{mfpiclly}
-% which can only be known after the graphics inclusion. We reset the box
-% \cs{@alltlabels} so its contents are shifted by these values. We only
-% need to do this if labels have have actually been added. The signal that
-% this has happened is that the label dimensions are no longer
-% $\pm$\cs{maxdimen}.
+% which can only be known after the graphics inclusion. We reset those
+% boxes so their contents are shifted by these values. We only need to do
+% this if labels have have actually been added. The signal that this has
+% happened is that the label dimensions are no longer $\pm$\cs{maxdimen}.
+% This doesn't indicate whether it is \cs{@alltlabels} or \cs{@backtext}
+% (or both) that is nonempty, so one of the box resets may be redundant.
% \begin{macrocode}
\ifmfpicdebug
+ \wlog{Mfpic debug: Dimensions of alltlabels, before
+ truebbox adjustments:}%
+ \mfp@scratch\mfpicllx bp\relax
+ \wlog{Mfpic: mfpicllx = \the\mfp@scratch}%
+ \mfp@scratch\mfpiclly bp\relax
+ \wlog{Mfpic: mfpiclly = \the\mfp@scratch}%
\wlog{Mfpic: tlabelsleft = \the\@tlabelsleft}%
- \wlog{Mfpic: tlabelsright = \the\@tlabelsright}%
\wlog{Mfpic: tlabelstop = \the\@tlabelstop}%
\wlog{Mfpic: tlabelsbot = \the\@tlabelsbot}%
\wlog{}%
\fi
\ifdim\@tlabelsleft < \maxdimen
\setbox\@alltlabels\vtop to 0pt{%
- \kern \mfpiclly bp
- \moveleft \mfpicllx bp \box\@alltlabels
- \vss}%
+ \kern \mfpiclly bp \moveleft \mfpicllx bp \box\@alltlabels\vss}%
+ \setbox\@backtext \vtop to 0pt{%
+ \kern \mfpiclly bp \moveleft \mfpicllx bp \box\@backtext \vss}%
% \end{macrocode}
% Then we adjust \cs{@tlabelsleft}, etc. for the boundingbox offset.
-% \begin{macrocode}
- \mfp@scratch\@tlabelsleft
- \advance\mfp@scratch-\mfpicllx bp
- \ifdim\mfp@scratch<\@graphleft
- \@graphleft\mfp@scratch
- \fi
- \mfp@scratch\@tlabelsright
- \advance\mfp@scratch-\mfpicllx bp
- \ifdim\mfp@scratch>\@graphright
- \@graphright\mfp@scratch
- \fi
- \mfp@scratch\@tlabelstop
- \advance\mfp@scratch-\mfpiclly bp
- \ifdim\mfp@scratch>\@graphtop
- \@graphtop\mfp@scratch
- \fi
- \mfp@scratch\@tlabelsbot
- \advance\mfp@scratch-\mfpiclly bp
- \ifdim\mfp@scratch<\@graphbot
- \@graphbot\mfp@scratch
+% The graph dimensions are also adjusted if the tlabel dimensions extend
+% past the edges.
+% \begin{macrocode}
+ \mfp@adjustdim\@tlabelsleft <\@graphleft \mfpicllx
+ \mfp@adjustdim\@tlabelstop >\@graphtop \mfpiclly
+ \mfp@adjustdim\@tlabelsbot <\@graphbot \mfpiclly
+ \ifmfpicdraft\else
+ \setbox\@wholegraph
+ \vtop{\offinterlineskip
+ \box\@backtext\kern-\ht\@wholegraph
+ \unvbox\@wholegraph\box\@alltlabels}%
\fi
- \setbox\@wholegraph=\vtop{%
- \unvbox\@wholegraph\box\@alltlabels}%
\fi
% \end{macrocode}
%
@@ -4787,15 +5882,16 @@
% detected by \TeX.
% \begin{macrocode}
\ifmfpicdebug
+ \wlog{Mfpic debug: Dimensions of alltlabels after
+ truebbox adjustments:}%
\wlog{Mfpic: tlabelsleft = \the\@tlabelsleft}%
- \wlog{Mfpic: tlabelsright = \the\@tlabelsright}%
\wlog{Mfpic: tlabelstop = \the\@tlabelstop}%
\wlog{Mfpic: tlabelsbot = \the\@tlabelsbot}%
\wlog{}%
\fi
- \mfp@scratch=\@graphtop
+ \mfp@scratch\@graphtop
\advance\mfp@scratch by -\@graphbot
- \global \setbox\@wholegraph=%
+ \global \setbox\@wholegraph
\vbox to \mfp@scratch{\vss
\hbox{\kern-\@graphleft\box\@wholegraph}%
\kern-\@graphbot}% End vbox.
@@ -4807,6 +5903,8 @@
% graphht${}={}$graphtop${}-{}$graphbot, and graphdp${}= 0$.
% \begin{macrocode}
\ifmfpicdebug
+ \wlog{Mfpic debug: Dimensions of wholegraph after
+ labels are added:}%
\wlog{Mfpic: graphleft = \the\@graphleft}%
\wlog{Mfpic: graphright = \the\@graphright}%
\wlog{Mfpic: graphtop = \the\@graphtop}%
@@ -4816,10 +5914,9 @@
\wlog{Mfpic: graphdp = \the\dp\@wholegraph}%
\wlog{}%
\fi
- \@docaption
- \global\mfpicheight\ht\@wholegraph
- \global\mfpicwidth\wd\@wholegraph
+ \mfp@docaption
\ifmfpicdebug
+ \wlog{Mfpic debug: Values after tcaption added:}%
\wlog{Mfpic: graphleft = \the\@graphleft}%
\wlog{Mfpic: graphright = \the\@graphright}%
\wlog{Mfpic: graphtop = \the\@graphtop}%
@@ -4829,49 +5926,72 @@
\wlog{Mfpic: graphdp = \the\dp\@wholegraph}%
\wlog{}%
\fi
+ \global\mfpicheight\ht\@wholegraph
+ \global\mfpicwidth\wd\@wholegraph
% \end{macrocode}
%
% \subsection{Placing the graphic}\label{placing}
%
% Add a frame around the box \cs{@wholegraph} in draft mode (so its size
% can be seen) since there is practically nothing in it except the picture
-% number
+% number and perhaps some tlabels.
% \begin{macrocode}
\ifmfpicdraft
- \setbox\@wholegraph=%
- \hbox{\@mfpframed{-\mfpframethickness}{\box\@wholegraph}}%
+ \setbox\@wholegraph
+ \hbox{\@mfpframed{-\mfpframethickness}{\box\@wholegraph}}%
\fi
% \end{macrocode}
-%
+% Now we save the graph globally, for placement after \cs{endgroup}. This
+% is so that tokens placed by \cs{everypar} will be typeset in the current
+% font (instead of \cs{nullfont}). Then we advance the figure count and
+% end the group started by the \cs{mfpic} command.
+% \begin{macrocode}
+ \global\setbox\@wholegraph\box\@wholegraph
+ \global \advance\mfp@count1
+ \fi % \ifmfp@noship
+ \endgroup % Begun near start of \@mfpic
+% \end{macrocode}
% If the \cs{savepic} command occurred prior to this, then \cs{s@vemfpic}
-% is defined and we save this picture in the box named in that
-% \cs{savepic} command and stored in the macro \cs{s@vemfpic}. Otherwise,
-% \cs{s@vemfpic} is undefined, and we place the picture (\cs{@wholegraph})
-% in the document. Afterward, we undefine \cs{s@vemfpic}.
+% is defined and we save this picture in the box named in that command and
+% stored in the macro \cs{s@vemfpic}. Otherwise, \cs{s@vemfpic} is
+% undefined, and we place the picture (\cs{@wholegraph}) in the document.
+% Afterward, we undefine \cs{s@vemfpic}. Note: if \opt{noship} is in
+% force, it remains defined, so the next picture will be stored.
% \begin{macrocode}
+ \ifmfp@noship \else
\mfp@ifdefined\s@vemfpic
- {\global\setbox\s@vemfpic=\box\@wholegraph}%
+ {\global\setbox\s@vemfpic\box\@wholegraph}%
{\leavevmode\box\@wholegraph}%
\global\let\s@vemfpic\UndEfInEd
+ \fi
% \end{macrocode}
-%
-% Now we advance the figure count, end the group started by the \cs{mfpic}
-% command, restore the values of extra font dimensions that were saved,
-% then arrange for spaces to be ignored in case the syntax
-% \cs{begin}\marg{mfpic}$\ldots$\cs{end}\marg{mfpic} was used.
+% Then we restore the values of extra font dimensions that were saved.
+% Just in case, we unset the switch \cs{in@mfpicenv} and then arrange for
+% spaces to be ignored in case this is a \LaTeX{} environment. (I think we
+% shouldn't ignore spaces, but I was overruled. The argument was that
+% \cs{end}\marg{mfpic} should behave like \cs{endmfpic}. I don't buy it,
+% but I will keep things the way they were since version 0.4.)
% \begin{macrocode}
- \global \advance\mfp@count1
- \fi % end of \if@shipping
- \endgroup % Begun near start of \@mfpic
\restore@mfpicdimens
- \if@mfp@latex
- \def\mfptmp@a{mfpic}%
- \ifx\mfptmp@a\@currenvir
- \@ignoretrue% so \endmfpic and \end{mfpic} have the same effect.
- \fi
- \fi
+ \mfp@checklatexenv{mfpic}{\@ignoretrue}{}%
+ \global\let\in@mfpicenv\iffalse
\ignorespaces
-}%
+}% endmfpic
+% \end{macrocode}
+%
+% The following is a little utility to run at \cs{foo} or \cs{endfoo} to
+% see if was initiated as a \LaTeX{} environment or as a plain command.
+% The first argument is the environment name, the second is what to do
+% if that is the current environment, and the third is what to do
+% otherwise. In the two cases we now use it, the third argument is
+% empty.
+% \begin{macrocode}
+\ifmfp@latex
+ \newdef\mfp@checklatexenv#1#2#3{%
+ \def\mfp@tempa{#1}\ifx\mfp@tempa\@currenvir #2\else #3\fi}%
+\else
+ \newdef\mfp@checklatexenv#1#2#3{#3}%
+\fi
% \end{macrocode}
%
%
@@ -4881,26 +6001,26 @@
%
% \DescribeMacro{\newsavepic}
% One saves a picture by first allocating a save box with
-% \cs{newsavepic}\marg{\cs{mypic}}, then saying
-% \cs{savepic}\marg{\cs{mypic}} beforehand. The \cs{endmfpic} code
-% detects that this has been done and saves the picture in \cs{mypic}.
-% It can be placed with \cs{usepic}{\cs{mypic}}.
+% \cs{newsavepic}\cs{mypic}, then saying \cs{savepic}\cs{mypic}
+% beforehand. The \cs{endmfpic} code detects that this has been done and
+% saves the picture in \cs{mypic}. It can be placed with
+% \cs{usepic}\cs{mypic}.
%
% \cs{newsavepic} is just a clone of \LaTeX's \cs{newsavebox}. The one
% argument is a control sequence.
% \begin{macrocode}
-\newdef\newsavepic#1{%
- \if@mfp@latex \newsavebox{#1}%
- \else
+\ifmfp@latex
+ \newlet\newsavepic\newsavebox
+\else
+ \newdef\newsavepic#1{%
\mfp@ifdefined{#1}%
- {\mfp@errmsg
- {Command \string #1 already defined.}%
+ {\mfp@errmsg{Command \string #1 already defined}%
{You have used \newsavepic with an already defined or an^^J%
improper control sequence. Replace #1 with another name.^^J%
If you proceed, \newsavepic will be ignored.}}%
- {\csname newbox\endcsname#1}%
- \fi}%
-\newlet\newpic=\newsavepic % compatibility
+ {\@nameuse{newbox}#1}}%
+\fi
+\newlet\newpic\newsavepic
% \end{macrocode}
%
% \DescribeMacro{\savepic}
@@ -4910,7 +6030,7 @@
% \begin{macrocode}
\newdef\savepic#1{\relax
\mfp@ifdefined{#1}{\gdef\s@vemfpic{#1}}%
- {\mfp@errmsg{Box \string#1 undefined.}%
+ {\mfp@errmsg{Box \string#1 undefined}%
{You tried to save a picture in a box which had not been^^J%
previously allocated. Use \newsavepic to allocate a box.}}}%
% \end{macrocode}
@@ -4926,24 +6046,24 @@
% \subsection{Putting a frame around it}\label{frame}
%
% This is essentially code I wrote years ago to have a framing command
-% that had two properties not enjoyed by come example `\cs{boxit}' commands
+% that had two properties not enjoyed by some example `\cs{boxit}' commands
% for plain\TeX: It should have a baseline the same as the baseline of the
% contents, and it should work as an environment, not by reading an
% argument (so verbatim material could be included).
%
% \DescribeMacro{\mfpframed}
-% \DescribeMacro{\mfpframe}
% \cs{mfpframed} takes an optional argument (the separation) and a
-% mandatory argument, the contents. \cs{mfpframe}$\ldots$\cs{endmfpframe}
-% is an environment and \cs{mfpframe} also takes the separation as an
-% optional argument.
+% mandatory argument, the contents.
+% \DescribeMacro{\mfpframe}
+% \cs{mfpframe}$\ldots$\cs{endmfpframe} is an environment and
+% \cs{mfpframe} also takes the separation as an optional argument.
%
% \DescribeMacro{\framed}
% \cs{framed} is a common command (\ConTeXt{} has one, a few packages also
% define it), therefore we only define it if it is not already defined.
% \begin{macrocode}
-\newdef\mfpframed{\simple@opt\@mfpframed\mfpframesep}%
-\newdef\mfpframe{\simple@opt\@mfpframe\mfpframesep}%
+\newdef\mfpframed{\mfp@defaultopt\@mfpframed\mfpframesep}%
+\newdef\mfpframe{\mfp@defaultopt\@mfpframe\mfpframesep}%
\newdef\@mfpframed#1#2{\@mfpframe{#1}#2\endmfpframe}%
\newdef\@mfpframe#1{%
\leavevmode\hbox\bgroup
@@ -4963,83 +6083,93 @@
\egroup % end vtop
\vrule width\mfpframethickness
\egroup}% end hbox
-\mfp@ifdefined\framed{}{\newlet\framed=\mfpframed}%
+\mfp@ifdefined\framed{}{\let\framed\mfpframed}%
% \end{macrocode}
%
% \subsection{Adding \mfc{verbatimtex} to the \file{.mp}
% file}\label{verbatimtex}
%
-% \DescribeMacro{mfpverbtex}
+% \DescribeMacro{\mfpverbtex}
% This is a user level utility for putting some \mfc{verbatimtex} material
% in the output file for \MP. We allow the user to do this even before
-% \cs{opengraphsfile} by storing it in a token register and setting a flag
-% for that command to test.
+% \cs{opengraphsfile} (but only once) by storing it in a token register
+% and setting a flag for that command to test.
+%
+% We now want \texttt\% signs to be written to the output, but also
+% want to allow the construct
+% \begin{verbatim}
+% \mfpverbtex% Comment
+% {<tex code>}\end{verbatim}
+% to function as users might expect. We therefore perform a
+% \cs{futurelet} (which should set \cs{mfp@next} equal to the opening
+% brace) but we do it solely to get \TeX's scanning mechanism past the
+% comment before we change the category of \texttt\%.
% \begin{macrocode}
-\newif\if@mfp@verbtex
-\def\mfpverbtex{%
+\def\mfpverbtex{\futurelet\mfp@next\@mfpverbtex}%
% \end{macrocode}
%
-% We start a group, make sure lines will be preserved in writing the
-% material, make sure that \texttt{\#} characters will not be doubled by
-% changing its category, then continue after loading the code into the
+% We start a group, change \texttt\% and \texttt\# to `other' so they
+% can be written normally, then continue after loading the code into the
% token register \cs{mfp@verbtex}.
% \begin{macrocode}
- \begingroup \preservelines \@makeother\#%
- \afterassignment\mfp@writetex\global\mfp@verbtex=}%
+\def\@mfpverbtex{%
+ \begingroup
+ \@makeother\#\@makeother\%\mfp@keeplines
+ \afterassignment\mfp@writetex\global\mfp@verbtex}%
% \end{macrocode}
-%
% We end the group and then either set the switch to tell
% \cs{opengraphsfile} that the material is ready, or we write it to the
% output, between the commands \gbc{verbatimtex} and \cs{etex}.
% \begin{macrocode}
+\newif\ifmfp@verbtex
\def\mfp@writetex{%
\endgroup
- \@ifmfpfileisopen
+ \mfp@ifopengraphsfile
{\mfp@ifmpost
- {\mfsrc{verbatimtex}\mfsrc{\the\mfp@verbtex}\mfcmd{etex}}%
- {\noMP@error{verbatimtex}}%
- \global\@mfp@verbtexfalse\mfp@verbtex{}}%
- {\global\@mfp@verbtextrue}}%
+ {\mfsrc{ verbatimtex\@nl \the\mfp@verbtex}\mfcmd{etex}}%
+ {\mfp@noMP@error\mfpverbtex}%
+ \global\mfp@verbtexfalse\mfp@verbtex{}}%
+ {\global\mfp@verbtextrue}}%
% \end{macrocode}
%
%
% \section{Finale}\label{finale}
%
-% Set the default starting graphic number.
+% Set the default starting graphic number. We used to start at 0 and
+% increment it at the start of each picture, but now we start at 1 and
+% increment it at the end of each picture.
% \begin{macrocode}
-\global\mfp@count=1 % 1 because we now advance it in \endmfpic.
+\mfpicnumber{1}%
% \end{macrocode}
%
% Create a system for adding patches in the file \file{mfppatch.tex},
% and for user defaults in a file \file{mfpic.usr}.
% \begin{macrocode}
-\newdef\MFPICinput#1{%
- \immediate\openin\mfp@graph=#1
- \ifeof\mfp@graph
- \immediate\closein\mfp@graph
- \else
- \immediate\closein\mfp@graph
- \input #1
- \fi}%
-\MFPICinput{mfppatch.tex}%
+\InputIfFileExists{mfppatch.tex}{}{}%
+% \end{macrocode}
+%
+% Set the font for missing or draft mode figures (for the file name or
+% figure number). This will already have been defined if \file{mfpic.sty}
+% was used, so here we can probably assume \LaTeX{} wasn't used, and
+% \cs{tt} ought to be available.
+% \begin{macrocode}
+\mfp@ifdefined\mfpdraftfont{}{\newdef\mfpdraftfont{\tt}}%
% \end{macrocode}
%
-% Restore the meaning of \cs{+}, and of the \cs{newlinechar}, and execute
-% the command that restores all the saved category codes.
+% Restore the meaning of \cs{+}, and execute the command that restores all
+% the saved category codes and \cs{newlinechar}.
% \begin{macrocode}
\mfp@DBlog{Punctuation, etc., will revert to old catcodes now.}%
\let\+\mfpsaveplus
-\restorenewlinechar
-\MFPicpackagE%
-\MFPICinput{mfpic.usr}%
+\mfp@restoreALL%
+\InputIfFileExists{mfpic.usr}{}{}%
%</tex>
% \end{macrocode}
%
-% Generate a (far too) simple wrapper for \LaTeX.
+% The \LaTeX{} wrapper inputs \file{mfpic.tex}.
% \begin{macrocode}
-%<*sty>
-\input mfpic.tex\relax
-%</sty>
+%<sty>\input mfpic.tex\relax
% \end{macrocode}
+%
%\clearpage
%\Finale
diff --git a/Master/texmf-dist/source/generic/mfpic/mfpic.ins b/Master/texmf-dist/source/generic/mfpic/mfpic.ins
index c72d045ecac..d8e874b7506 100644
--- a/Master/texmf-dist/source/generic/mfpic/mfpic.ins
+++ b/Master/texmf-dist/source/generic/mfpic/mfpic.ins
@@ -1,56 +1,21 @@
-% File mfpic.ins
-% Mfpic version 0.7a beta 2004/04/16
-%
-% 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.
-%
\input docstrip
\keepsilent
\preamble
-------------------------------------------------------------------
-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.
-
-Documentation, examples, and ancillary files are separate. See
-readme.1st for a complete list.
+Copyright 2002--2006, Daniel H. Luecking
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
+LaTeX Project Public License, either version 1.3b 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.3b or later is part of all distributions of LaTeX version
+2005/12/01 or later.
- 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.
-
-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.
+Mfpic has maintenance status "author-maintained". The Current Maintainer
+is Daniel H. Luecking. There are several Base Interpreters associated
+with mfpic: plain TeX, LaTeX, plain Metafont and plain MetaPost.
\endpreamble
\edef\zyx{%
@@ -60,31 +25,31 @@ endinput.^^J%
\askforoverwritefalse
-\generate{ \file{mfpic.tex}{\from{mfpic.dtx}{tex}}
+\generate{\file{mfpic.tex}{\from{mfpic.dtx}{tex}}
\file{mfpic.sty}{\from{mfpic.dtx}{sty}}
\usepostamble\zyx
\file{grafbase.mf}{\from{grafbase.dtx}{MF}}
\file{grafbase.mp}{\from{grafbase.dtx}{MP}}
\file{dvipsnam.mp}{\from{grafbase.dtx}{dvips}}}
-\def\jts{^^J \space\space\space}
+\def\sJ{^^J \space\space\space}
\immediate\write16{%
-^^J*****************************************************************
+^^J*******************************************************************
^^J
^^J To finish the installation:
^^J
-^^J -- Copy the files mfpic,tex and mfpic.sty to a location where
-\jts TeX and LaTeX will find them. In a TDS compliant system
-\jts the directory TEXMF/tex/generic/mfpic/ is recommended.
+^^J -- Copy the files mfpic.tex, mfpic.sty and mfppatch.tex to a
+\sJ location where TeX and LaTeX will find them. For example, in a
+\sJ TDS compliant system, the directory TEXMF/tex/generic/mfpic/ .
^^J
^^J -- Copy the file grafbase.mf to a location where Metafont
-\jts will find it, for example TEXMF/metafont/mfpic/ .
+\sJ will find it, for example TEXMF/metafont/mfpic/ .
^^J
^^J -- Copy the files grafbase.mp and dvipsnam.mp to a location where
-\jts MetaPost will find them, for example TEXMF/metapost/mfpic/ .
+\sJ MetaPost will find them, for example TEXMF/metapost/mfpic/ .
^^J
^^J -- Refresh the filename database if your TeX system requires it.
^^J
-^^J*****************************************************************}
+^^J*******************************************************************}
\endbatchfile