diff options
author | Karl Berry <karl@freefriends.org> | 2006-07-01 23:18:44 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2006-07-01 23:18:44 +0000 |
commit | d6c7c85a6b1261a182a60431f6af860b855ec216 (patch) | |
tree | 794c53fc0f09ba53c39a272379fbf0004390c6eb /Master/texmf-dist/source/generic | |
parent | d6dd1c3b1739f957dd0acc6f89e480825511c927 (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.dtx | 7963 | ||||
-rw-r--r-- | Master/texmf-dist/source/generic/mfpic/mfpic.dtx | 7170 | ||||
-rw-r--r-- | Master/texmf-dist/source/generic/mfpic/mfpic.ins | 69 |
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 |