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