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